mirror of https://github.com/XTLS/Xray-core
				
				
				
			Add configuration item api->listen. (#3317)
* add api.listen configuration item * add unit tests * typo --------- Co-authored-by: nobody <nobody@nowhere.mars>pull/3325/head
							parent
							
								
									51504c624c
								
							
						
					
					
						commit
						447a49d16a
					
				| 
						 | 
				
			
			@ -21,12 +21,14 @@ type Commander struct {
 | 
			
		|||
	services []Service
 | 
			
		||||
	ohm      outbound.Manager
 | 
			
		||||
	tag      string
 | 
			
		||||
	listen   string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewCommander creates a new Commander based on the given config.
 | 
			
		||||
func NewCommander(ctx context.Context, config *Config) (*Commander, error) {
 | 
			
		||||
	c := &Commander{
 | 
			
		||||
		tag: config.Tag,
 | 
			
		||||
		tag:    config.Tag,
 | 
			
		||||
		listen: config.Listen,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	common.Must(core.RequireFeatures(ctx, func(om outbound.Manager) {
 | 
			
		||||
| 
						 | 
				
			
			@ -66,16 +68,29 @@ func (c *Commander) Start() error {
 | 
			
		|||
	}
 | 
			
		||||
	c.Unlock()
 | 
			
		||||
 | 
			
		||||
	var listen = func(listener net.Listener) {
 | 
			
		||||
		if err := c.server.Serve(listener); err != nil {
 | 
			
		||||
			newError("failed to start grpc server").Base(err).AtError().WriteToLog()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.listen) > 0 {
 | 
			
		||||
		if l, err := net.Listen("tcp", c.listen); err != nil {
 | 
			
		||||
			newError("API server failed to listen on ", c.listen).Base(err).AtError().WriteToLog()
 | 
			
		||||
			return err
 | 
			
		||||
		} else {
 | 
			
		||||
			newError("API server listening on ", l.Addr()).AtInfo().WriteToLog()
 | 
			
		||||
			go listen(l)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	listener := &OutboundListener{
 | 
			
		||||
		buffer: make(chan net.Conn, 4),
 | 
			
		||||
		done:   done.New(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		if err := c.server.Serve(listener); err != nil {
 | 
			
		||||
			newError("failed to start grpc server").Base(err).AtError().WriteToLog()
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	go listen(listener)
 | 
			
		||||
 | 
			
		||||
	if err := c.ohm.RemoveHandler(context.Background(), c.tag); err != nil {
 | 
			
		||||
		newError("failed to remove existing handler").WriteToLog()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
// Code generated by protoc-gen-go. DO NOT EDIT.
 | 
			
		||||
// versions:
 | 
			
		||||
// 	protoc-gen-go v1.33.0
 | 
			
		||||
// 	protoc        v4.23.1
 | 
			
		||||
// 	protoc-gen-go v1.28.1
 | 
			
		||||
// 	protoc        v4.25.3
 | 
			
		||||
// source: app/commander/config.proto
 | 
			
		||||
 | 
			
		||||
package commander
 | 
			
		||||
| 
						 | 
				
			
			@ -29,6 +29,8 @@ type Config struct {
 | 
			
		|||
 | 
			
		||||
	// Tag of the outbound handler that handles grpc connections.
 | 
			
		||||
	Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
 | 
			
		||||
	// Network address of commander grpc service.
 | 
			
		||||
	Listen string `protobuf:"bytes,3,opt,name=listen,proto3" json:"listen,omitempty"`
 | 
			
		||||
	// Services that supported by this server. All services must implement Service
 | 
			
		||||
	// interface.
 | 
			
		||||
	Service []*serial.TypedMessage `protobuf:"bytes,2,rep,name=service,proto3" json:"service,omitempty"`
 | 
			
		||||
| 
						 | 
				
			
			@ -73,6 +75,13 @@ func (x *Config) GetTag() string {
 | 
			
		|||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *Config) GetListen() string {
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		return x.Listen
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *Config) GetService() []*serial.TypedMessage {
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		return x.Service
 | 
			
		||||
| 
						 | 
				
			
			@ -127,20 +136,21 @@ var file_app_commander_config_proto_rawDesc = []byte{
 | 
			
		|||
	0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72,
 | 
			
		||||
	0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f,
 | 
			
		||||
	0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72,
 | 
			
		||||
	0x6f, 0x74, 0x6f, 0x22, 0x56, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a,
 | 
			
		||||
	0x6f, 0x74, 0x6f, 0x22, 0x6e, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a,
 | 
			
		||||
	0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12,
 | 
			
		||||
	0x3a, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
 | 
			
		||||
	0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73,
 | 
			
		||||
	0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61,
 | 
			
		||||
	0x67, 0x65, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x52,
 | 
			
		||||
	0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42,
 | 
			
		||||
	0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
 | 
			
		||||
	0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x27, 0x67, 0x69, 0x74,
 | 
			
		||||
	0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61,
 | 
			
		||||
	0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
 | 
			
		||||
	0x6e, 0x64, 0x65, 0x72, 0xaa, 0x02, 0x12, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e,
 | 
			
		||||
	0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 | 
			
		||||
	0x33,
 | 
			
		||||
	0x16, 0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
 | 
			
		||||
	0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x12, 0x3a, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69,
 | 
			
		||||
	0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
 | 
			
		||||
	0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79,
 | 
			
		||||
	0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76,
 | 
			
		||||
	0x69, 0x63, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f,
 | 
			
		||||
	0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78,
 | 
			
		||||
	0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65,
 | 
			
		||||
	0x72, 0x50, 0x01, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
 | 
			
		||||
	0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61,
 | 
			
		||||
	0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0xaa, 0x02, 0x12, 0x58,
 | 
			
		||||
	0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65,
 | 
			
		||||
	0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,6 +12,10 @@ import "common/serial/typed_message.proto";
 | 
			
		|||
message Config {
 | 
			
		||||
  // Tag of the outbound handler that handles grpc connections.
 | 
			
		||||
  string tag = 1;
 | 
			
		||||
 | 
			
		||||
  // Network address of commander grpc service.
 | 
			
		||||
  string listen = 3;
 | 
			
		||||
 | 
			
		||||
  // Services that supported by this server. All services must implement Service
 | 
			
		||||
  // interface.
 | 
			
		||||
  repeated xray.common.serial.TypedMessage service = 2;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,6 +14,7 @@ import (
 | 
			
		|||
 | 
			
		||||
type APIConfig struct {
 | 
			
		||||
	Tag      string   `json:"tag"`
 | 
			
		||||
	Listen   string   `json:"listen"`
 | 
			
		||||
	Services []string `json:"services"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -42,6 +43,7 @@ func (c *APIConfig) Build() (*commander.Config, error) {
 | 
			
		|||
 | 
			
		||||
	return &commander.Config{
 | 
			
		||||
		Tag:     c.Tag,
 | 
			
		||||
		Listen:  c.Listen,
 | 
			
		||||
		Service: services,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,6 +33,80 @@ import (
 | 
			
		|||
	"google.golang.org/grpc/credentials/insecure"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestCommanderListenConfigurationItem(t *testing.T) {
 | 
			
		||||
	tcpServer := tcp.Server{
 | 
			
		||||
		MsgProcessor: xor,
 | 
			
		||||
	}
 | 
			
		||||
	dest, err := tcpServer.Start()
 | 
			
		||||
	common.Must(err)
 | 
			
		||||
	defer tcpServer.Close()
 | 
			
		||||
 | 
			
		||||
	clientPort := tcp.PickPort()
 | 
			
		||||
	cmdPort := tcp.PickPort()
 | 
			
		||||
	clientConfig := &core.Config{
 | 
			
		||||
		App: []*serial.TypedMessage{
 | 
			
		||||
			serial.ToTypedMessage(&commander.Config{
 | 
			
		||||
				Tag:    "api",
 | 
			
		||||
				Listen: fmt.Sprintf("127.0.0.1:%d", cmdPort),
 | 
			
		||||
				Service: []*serial.TypedMessage{
 | 
			
		||||
					serial.ToTypedMessage(&command.Config{}),
 | 
			
		||||
				},
 | 
			
		||||
			}),
 | 
			
		||||
		},
 | 
			
		||||
		Inbound: []*core.InboundHandlerConfig{
 | 
			
		||||
			{
 | 
			
		||||
				Tag: "d",
 | 
			
		||||
				ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
 | 
			
		||||
					PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}},
 | 
			
		||||
					Listen:   net.NewIPOrDomain(net.LocalHostIP),
 | 
			
		||||
				}),
 | 
			
		||||
				ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
 | 
			
		||||
					Address:  net.NewIPOrDomain(dest.Address),
 | 
			
		||||
					Port:     uint32(dest.Port),
 | 
			
		||||
					Networks: []net.Network{net.Network_TCP},
 | 
			
		||||
				}),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		Outbound: []*core.OutboundHandlerConfig{
 | 
			
		||||
			{
 | 
			
		||||
				Tag:           "default-outbound",
 | 
			
		||||
				ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	servers, err := InitializeServerConfigs(clientConfig)
 | 
			
		||||
	common.Must(err)
 | 
			
		||||
	defer CloseAllServers(servers)
 | 
			
		||||
 | 
			
		||||
	if err := testTCPConn(clientPort, 1024, time.Second*5)(); err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock())
 | 
			
		||||
	common.Must(err)
 | 
			
		||||
	defer cmdConn.Close()
 | 
			
		||||
 | 
			
		||||
	hsClient := command.NewHandlerServiceClient(cmdConn)
 | 
			
		||||
	resp, err := hsClient.RemoveInbound(context.Background(), &command.RemoveInboundRequest{
 | 
			
		||||
		Tag: "d",
 | 
			
		||||
	})
 | 
			
		||||
	common.Must(err)
 | 
			
		||||
	if resp == nil {
 | 
			
		||||
		t.Error("unexpected nil response")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		_, err := net.DialTCP("tcp", nil, &net.TCPAddr{
 | 
			
		||||
			IP:   []byte{127, 0, 0, 1},
 | 
			
		||||
			Port: int(clientPort),
 | 
			
		||||
		})
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			t.Error("unexpected nil error")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCommanderRemoveHandler(t *testing.T) {
 | 
			
		||||
	tcpServer := tcp.Server{
 | 
			
		||||
		MsgProcessor: xor,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue