Bladeren bron

Add configuration item api->listen. (#3317)

* add api.listen configuration item

* add unit tests

* typo

---------

Co-authored-by: nobody <[email protected]>
nobody 2 jaren geleden
bovenliggende
commit
447a49d16a
5 gewijzigde bestanden met toevoegingen van 126 en 21 verwijderingen
  1. 21 6
      app/commander/commander.go
  2. 25 15
      app/commander/config.pb.go
  3. 4 0
      app/commander/config.proto
  4. 2 0
      infra/conf/api.go
  5. 74 0
      testing/scenarios/command_test.go

+ 21 - 6
app/commander/commander.go

@@ -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()

+ 25 - 15
app/commander/config.pb.go

@@ -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 (

+ 4 - 0
app/commander/config.proto

@@ -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;

+ 2 - 0
infra/conf/api.go

@@ -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
 }

+ 74 - 0
testing/scenarios/command_test.go

@@ -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,