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
 | 
						services []Service
 | 
				
			||||||
	ohm      outbound.Manager
 | 
						ohm      outbound.Manager
 | 
				
			||||||
	tag      string
 | 
						tag      string
 | 
				
			||||||
 | 
						listen   string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewCommander creates a new Commander based on the given config.
 | 
					// NewCommander creates a new Commander based on the given config.
 | 
				
			||||||
func NewCommander(ctx context.Context, config *Config) (*Commander, error) {
 | 
					func NewCommander(ctx context.Context, config *Config) (*Commander, error) {
 | 
				
			||||||
	c := &Commander{
 | 
						c := &Commander{
 | 
				
			||||||
		tag: config.Tag,
 | 
							tag:    config.Tag,
 | 
				
			||||||
 | 
							listen: config.Listen,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	common.Must(core.RequireFeatures(ctx, func(om outbound.Manager) {
 | 
						common.Must(core.RequireFeatures(ctx, func(om outbound.Manager) {
 | 
				
			||||||
| 
						 | 
					@ -66,16 +68,29 @@ func (c *Commander) Start() error {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	c.Unlock()
 | 
						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{
 | 
						listener := &OutboundListener{
 | 
				
			||||||
		buffer: make(chan net.Conn, 4),
 | 
							buffer: make(chan net.Conn, 4),
 | 
				
			||||||
		done:   done.New(),
 | 
							done:   done.New(),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	go func() {
 | 
						go listen(listener)
 | 
				
			||||||
		if err := c.server.Serve(listener); err != nil {
 | 
					 | 
				
			||||||
			newError("failed to start grpc server").Base(err).AtError().WriteToLog()
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := c.ohm.RemoveHandler(context.Background(), c.tag); err != nil {
 | 
						if err := c.ohm.RemoveHandler(context.Background(), c.tag); err != nil {
 | 
				
			||||||
		newError("failed to remove existing handler").WriteToLog()
 | 
							newError("failed to remove existing handler").WriteToLog()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
 | 
					// Code generated by protoc-gen-go. DO NOT EDIT.
 | 
				
			||||||
// versions:
 | 
					// versions:
 | 
				
			||||||
// 	protoc-gen-go v1.33.0
 | 
					// 	protoc-gen-go v1.28.1
 | 
				
			||||||
// 	protoc        v4.23.1
 | 
					// 	protoc        v4.25.3
 | 
				
			||||||
// source: app/commander/config.proto
 | 
					// source: app/commander/config.proto
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package commander
 | 
					package commander
 | 
				
			||||||
| 
						 | 
					@ -29,6 +29,8 @@ type Config struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Tag of the outbound handler that handles grpc connections.
 | 
						// Tag of the outbound handler that handles grpc connections.
 | 
				
			||||||
	Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
 | 
						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
 | 
						// Services that supported by this server. All services must implement Service
 | 
				
			||||||
	// interface.
 | 
						// interface.
 | 
				
			||||||
	Service []*serial.TypedMessage `protobuf:"bytes,2,rep,name=service,proto3" json:"service,omitempty"`
 | 
						Service []*serial.TypedMessage `protobuf:"bytes,2,rep,name=service,proto3" json:"service,omitempty"`
 | 
				
			||||||
| 
						 | 
					@ -73,6 +75,13 @@ func (x *Config) GetTag() string {
 | 
				
			||||||
	return ""
 | 
						return ""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Config) GetListen() string {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.Listen
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (x *Config) GetService() []*serial.TypedMessage {
 | 
					func (x *Config) GetService() []*serial.TypedMessage {
 | 
				
			||||||
	if x != nil {
 | 
						if x != nil {
 | 
				
			||||||
		return x.Service
 | 
							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,
 | 
						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,
 | 
						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,
 | 
						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,
 | 
						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,
 | 
						0x16, 0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
 | 
				
			||||||
	0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73,
 | 
						0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x12, 0x3a, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69,
 | 
				
			||||||
	0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61,
 | 
						0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
 | 
				
			||||||
	0x67, 0x65, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x52,
 | 
						0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79,
 | 
				
			||||||
	0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42,
 | 
						0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76,
 | 
				
			||||||
	0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
 | 
						0x69, 0x63, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f,
 | 
				
			||||||
	0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x27, 0x67, 0x69, 0x74,
 | 
						0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78,
 | 
				
			||||||
	0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61,
 | 
						0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65,
 | 
				
			||||||
	0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
 | 
						0x72, 0x50, 0x01, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
 | 
				
			||||||
	0x6e, 0x64, 0x65, 0x72, 0xaa, 0x02, 0x12, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e,
 | 
						0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61,
 | 
				
			||||||
	0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 | 
						0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0xaa, 0x02, 0x12, 0x58,
 | 
				
			||||||
	0x33,
 | 
						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 (
 | 
					var (
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,10 @@ import "common/serial/typed_message.proto";
 | 
				
			||||||
message Config {
 | 
					message Config {
 | 
				
			||||||
  // Tag of the outbound handler that handles grpc connections.
 | 
					  // Tag of the outbound handler that handles grpc connections.
 | 
				
			||||||
  string tag = 1;
 | 
					  string tag = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Network address of commander grpc service.
 | 
				
			||||||
 | 
					  string listen = 3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Services that supported by this server. All services must implement Service
 | 
					  // Services that supported by this server. All services must implement Service
 | 
				
			||||||
  // interface.
 | 
					  // interface.
 | 
				
			||||||
  repeated xray.common.serial.TypedMessage service = 2;
 | 
					  repeated xray.common.serial.TypedMessage service = 2;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@ import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type APIConfig struct {
 | 
					type APIConfig struct {
 | 
				
			||||||
	Tag      string   `json:"tag"`
 | 
						Tag      string   `json:"tag"`
 | 
				
			||||||
 | 
						Listen   string   `json:"listen"`
 | 
				
			||||||
	Services []string `json:"services"`
 | 
						Services []string `json:"services"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,6 +43,7 @@ func (c *APIConfig) Build() (*commander.Config, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return &commander.Config{
 | 
						return &commander.Config{
 | 
				
			||||||
		Tag:     c.Tag,
 | 
							Tag:     c.Tag,
 | 
				
			||||||
 | 
							Listen:  c.Listen,
 | 
				
			||||||
		Service: services,
 | 
							Service: services,
 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,6 +33,80 @@ import (
 | 
				
			||||||
	"google.golang.org/grpc/credentials/insecure"
 | 
						"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) {
 | 
					func TestCommanderRemoveHandler(t *testing.T) {
 | 
				
			||||||
	tcpServer := tcp.Server{
 | 
						tcpServer := tcp.Server{
 | 
				
			||||||
		MsgProcessor: xor,
 | 
							MsgProcessor: xor,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue