github.com/xtls/xray-core@v1.8.12-0.20240518155711-3168d27b0bdb/testing/scenarios/command_test.go (about)

     1  package scenarios
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/google/go-cmp/cmp"
    12  	"github.com/google/go-cmp/cmp/cmpopts"
    13  	"github.com/xtls/xray-core/app/commander"
    14  	"github.com/xtls/xray-core/app/policy"
    15  	"github.com/xtls/xray-core/app/proxyman"
    16  	"github.com/xtls/xray-core/app/proxyman/command"
    17  	"github.com/xtls/xray-core/app/router"
    18  	"github.com/xtls/xray-core/app/stats"
    19  	statscmd "github.com/xtls/xray-core/app/stats/command"
    20  	"github.com/xtls/xray-core/common"
    21  	"github.com/xtls/xray-core/common/net"
    22  	"github.com/xtls/xray-core/common/protocol"
    23  	"github.com/xtls/xray-core/common/serial"
    24  	"github.com/xtls/xray-core/common/uuid"
    25  	core "github.com/xtls/xray-core/core"
    26  	"github.com/xtls/xray-core/proxy/dokodemo"
    27  	"github.com/xtls/xray-core/proxy/freedom"
    28  	"github.com/xtls/xray-core/proxy/vmess"
    29  	"github.com/xtls/xray-core/proxy/vmess/inbound"
    30  	"github.com/xtls/xray-core/proxy/vmess/outbound"
    31  	"github.com/xtls/xray-core/testing/servers/tcp"
    32  	"google.golang.org/grpc"
    33  	"google.golang.org/grpc/credentials/insecure"
    34  )
    35  
    36  func TestCommanderListenConfigurationItem(t *testing.T) {
    37  	tcpServer := tcp.Server{
    38  		MsgProcessor: xor,
    39  	}
    40  	dest, err := tcpServer.Start()
    41  	common.Must(err)
    42  	defer tcpServer.Close()
    43  
    44  	clientPort := tcp.PickPort()
    45  	cmdPort := tcp.PickPort()
    46  	clientConfig := &core.Config{
    47  		App: []*serial.TypedMessage{
    48  			serial.ToTypedMessage(&commander.Config{
    49  				Tag:    "api",
    50  				Listen: fmt.Sprintf("127.0.0.1:%d", cmdPort),
    51  				Service: []*serial.TypedMessage{
    52  					serial.ToTypedMessage(&command.Config{}),
    53  				},
    54  			}),
    55  		},
    56  		Inbound: []*core.InboundHandlerConfig{
    57  			{
    58  				Tag: "d",
    59  				ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
    60  					PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}},
    61  					Listen:   net.NewIPOrDomain(net.LocalHostIP),
    62  				}),
    63  				ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
    64  					Address:  net.NewIPOrDomain(dest.Address),
    65  					Port:     uint32(dest.Port),
    66  					Networks: []net.Network{net.Network_TCP},
    67  				}),
    68  			},
    69  		},
    70  		Outbound: []*core.OutboundHandlerConfig{
    71  			{
    72  				Tag:           "default-outbound",
    73  				ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
    74  			},
    75  		},
    76  	}
    77  
    78  	servers, err := InitializeServerConfigs(clientConfig)
    79  	common.Must(err)
    80  	defer CloseAllServers(servers)
    81  
    82  	if err := testTCPConn(clientPort, 1024, time.Second*5)(); err != nil {
    83  		t.Fatal(err)
    84  	}
    85  
    86  	cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock())
    87  	common.Must(err)
    88  	defer cmdConn.Close()
    89  
    90  	hsClient := command.NewHandlerServiceClient(cmdConn)
    91  	resp, err := hsClient.RemoveInbound(context.Background(), &command.RemoveInboundRequest{
    92  		Tag: "d",
    93  	})
    94  	common.Must(err)
    95  	if resp == nil {
    96  		t.Error("unexpected nil response")
    97  	}
    98  
    99  	{
   100  		_, err := net.DialTCP("tcp", nil, &net.TCPAddr{
   101  			IP:   []byte{127, 0, 0, 1},
   102  			Port: int(clientPort),
   103  		})
   104  		if err == nil {
   105  			t.Error("unexpected nil error")
   106  		}
   107  	}
   108  }
   109  
   110  func TestCommanderRemoveHandler(t *testing.T) {
   111  	tcpServer := tcp.Server{
   112  		MsgProcessor: xor,
   113  	}
   114  	dest, err := tcpServer.Start()
   115  	common.Must(err)
   116  	defer tcpServer.Close()
   117  
   118  	clientPort := tcp.PickPort()
   119  	cmdPort := tcp.PickPort()
   120  	clientConfig := &core.Config{
   121  		App: []*serial.TypedMessage{
   122  			serial.ToTypedMessage(&commander.Config{
   123  				Tag: "api",
   124  				Service: []*serial.TypedMessage{
   125  					serial.ToTypedMessage(&command.Config{}),
   126  				},
   127  			}),
   128  			serial.ToTypedMessage(&router.Config{
   129  				Rule: []*router.RoutingRule{
   130  					{
   131  						InboundTag: []string{"api"},
   132  						TargetTag: &router.RoutingRule_Tag{
   133  							Tag: "api",
   134  						},
   135  					},
   136  				},
   137  			}),
   138  		},
   139  		Inbound: []*core.InboundHandlerConfig{
   140  			{
   141  				Tag: "d",
   142  				ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
   143  					PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}},
   144  					Listen:   net.NewIPOrDomain(net.LocalHostIP),
   145  				}),
   146  				ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
   147  					Address:  net.NewIPOrDomain(dest.Address),
   148  					Port:     uint32(dest.Port),
   149  					Networks: []net.Network{net.Network_TCP},
   150  				}),
   151  			},
   152  			{
   153  				Tag: "api",
   154  				ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
   155  					PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(cmdPort)}},
   156  					Listen:   net.NewIPOrDomain(net.LocalHostIP),
   157  				}),
   158  				ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
   159  					Address:  net.NewIPOrDomain(dest.Address),
   160  					Port:     uint32(dest.Port),
   161  					Networks: []net.Network{net.Network_TCP},
   162  				}),
   163  			},
   164  		},
   165  		Outbound: []*core.OutboundHandlerConfig{
   166  			{
   167  				Tag:           "default-outbound",
   168  				ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
   169  			},
   170  		},
   171  	}
   172  
   173  	servers, err := InitializeServerConfigs(clientConfig)
   174  	common.Must(err)
   175  	defer CloseAllServers(servers)
   176  
   177  	if err := testTCPConn(clientPort, 1024, time.Second*5)(); err != nil {
   178  		t.Fatal(err)
   179  	}
   180  
   181  	cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock())
   182  	common.Must(err)
   183  	defer cmdConn.Close()
   184  
   185  	hsClient := command.NewHandlerServiceClient(cmdConn)
   186  	resp, err := hsClient.RemoveInbound(context.Background(), &command.RemoveInboundRequest{
   187  		Tag: "d",
   188  	})
   189  	common.Must(err)
   190  	if resp == nil {
   191  		t.Error("unexpected nil response")
   192  	}
   193  
   194  	{
   195  		_, err := net.DialTCP("tcp", nil, &net.TCPAddr{
   196  			IP:   []byte{127, 0, 0, 1},
   197  			Port: int(clientPort),
   198  		})
   199  		if err == nil {
   200  			t.Error("unexpected nil error")
   201  		}
   202  	}
   203  }
   204  
   205  func TestCommanderAddRemoveUser(t *testing.T) {
   206  	tcpServer := tcp.Server{
   207  		MsgProcessor: xor,
   208  	}
   209  	dest, err := tcpServer.Start()
   210  	common.Must(err)
   211  	defer tcpServer.Close()
   212  
   213  	u1 := protocol.NewID(uuid.New())
   214  	u2 := protocol.NewID(uuid.New())
   215  
   216  	cmdPort := tcp.PickPort()
   217  	serverPort := tcp.PickPort()
   218  	serverConfig := &core.Config{
   219  		App: []*serial.TypedMessage{
   220  			serial.ToTypedMessage(&commander.Config{
   221  				Tag: "api",
   222  				Service: []*serial.TypedMessage{
   223  					serial.ToTypedMessage(&command.Config{}),
   224  				},
   225  			}),
   226  			serial.ToTypedMessage(&router.Config{
   227  				Rule: []*router.RoutingRule{
   228  					{
   229  						InboundTag: []string{"api"},
   230  						TargetTag: &router.RoutingRule_Tag{
   231  							Tag: "api",
   232  						},
   233  					},
   234  				},
   235  			}),
   236  			serial.ToTypedMessage(&policy.Config{
   237  				Level: map[uint32]*policy.Policy{
   238  					0: {
   239  						Timeout: &policy.Policy_Timeout{
   240  							UplinkOnly:   &policy.Second{Value: 0},
   241  							DownlinkOnly: &policy.Second{Value: 0},
   242  						},
   243  					},
   244  				},
   245  			}),
   246  		},
   247  		Inbound: []*core.InboundHandlerConfig{
   248  			{
   249  				Tag: "v",
   250  				ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
   251  					PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}},
   252  					Listen:   net.NewIPOrDomain(net.LocalHostIP),
   253  				}),
   254  				ProxySettings: serial.ToTypedMessage(&inbound.Config{
   255  					User: []*protocol.User{
   256  						{
   257  							Account: serial.ToTypedMessage(&vmess.Account{
   258  								Id: u1.String(),
   259  							}),
   260  						},
   261  					},
   262  				}),
   263  			},
   264  			{
   265  				Tag: "api",
   266  				ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
   267  					PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(cmdPort)}},
   268  					Listen:   net.NewIPOrDomain(net.LocalHostIP),
   269  				}),
   270  				ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
   271  					Address:  net.NewIPOrDomain(dest.Address),
   272  					Port:     uint32(dest.Port),
   273  					Networks: []net.Network{net.Network_TCP},
   274  				}),
   275  			},
   276  		},
   277  		Outbound: []*core.OutboundHandlerConfig{
   278  			{
   279  				ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
   280  			},
   281  		},
   282  	}
   283  
   284  	clientPort := tcp.PickPort()
   285  	clientConfig := &core.Config{
   286  		App: []*serial.TypedMessage{
   287  			serial.ToTypedMessage(&policy.Config{
   288  				Level: map[uint32]*policy.Policy{
   289  					0: {
   290  						Timeout: &policy.Policy_Timeout{
   291  							UplinkOnly:   &policy.Second{Value: 0},
   292  							DownlinkOnly: &policy.Second{Value: 0},
   293  						},
   294  					},
   295  				},
   296  			}),
   297  		},
   298  		Inbound: []*core.InboundHandlerConfig{
   299  			{
   300  				Tag: "d",
   301  				ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
   302  					PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}},
   303  					Listen:   net.NewIPOrDomain(net.LocalHostIP),
   304  				}),
   305  				ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
   306  					Address: net.NewIPOrDomain(dest.Address),
   307  					Port:    uint32(dest.Port),
   308  					NetworkList: &net.NetworkList{
   309  						Network: []net.Network{net.Network_TCP},
   310  					},
   311  				}),
   312  			},
   313  		},
   314  		Outbound: []*core.OutboundHandlerConfig{
   315  			{
   316  				ProxySettings: serial.ToTypedMessage(&outbound.Config{
   317  					Receiver: []*protocol.ServerEndpoint{
   318  						{
   319  							Address: net.NewIPOrDomain(net.LocalHostIP),
   320  							Port:    uint32(serverPort),
   321  							User: []*protocol.User{
   322  								{
   323  									Account: serial.ToTypedMessage(&vmess.Account{
   324  										Id: u2.String(),
   325  										SecuritySettings: &protocol.SecurityConfig{
   326  											Type: protocol.SecurityType_AES128_GCM,
   327  										},
   328  									}),
   329  								},
   330  							},
   331  						},
   332  					},
   333  				}),
   334  			},
   335  		},
   336  	}
   337  
   338  	servers, err := InitializeServerConfigs(serverConfig, clientConfig)
   339  	common.Must(err)
   340  	defer CloseAllServers(servers)
   341  
   342  	if err := testTCPConn(clientPort, 1024, time.Second*5)(); err != io.EOF &&
   343  		/*We might wish to drain the connection*/
   344  		(err != nil && !strings.HasSuffix(err.Error(), "i/o timeout")) {
   345  		t.Fatal("expected error: ", err)
   346  	}
   347  
   348  	cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock())
   349  	common.Must(err)
   350  	defer cmdConn.Close()
   351  
   352  	hsClient := command.NewHandlerServiceClient(cmdConn)
   353  	resp, err := hsClient.AlterInbound(context.Background(), &command.AlterInboundRequest{
   354  		Tag: "v",
   355  		Operation: serial.ToTypedMessage(
   356  			&command.AddUserOperation{
   357  				User: &protocol.User{
   358  					Email: "test@example.com",
   359  					Account: serial.ToTypedMessage(&vmess.Account{
   360  						Id: u2.String(),
   361  					}),
   362  				},
   363  			}),
   364  	})
   365  	common.Must(err)
   366  	if resp == nil {
   367  		t.Fatal("nil response")
   368  	}
   369  
   370  	if err := testTCPConn(clientPort, 1024, time.Second*5)(); err != nil {
   371  		t.Fatal(err)
   372  	}
   373  
   374  	resp, err = hsClient.AlterInbound(context.Background(), &command.AlterInboundRequest{
   375  		Tag:       "v",
   376  		Operation: serial.ToTypedMessage(&command.RemoveUserOperation{Email: "test@example.com"}),
   377  	})
   378  	common.Must(err)
   379  	if resp == nil {
   380  		t.Fatal("nil response")
   381  	}
   382  }
   383  
   384  func TestCommanderStats(t *testing.T) {
   385  	tcpServer := tcp.Server{
   386  		MsgProcessor: xor,
   387  	}
   388  	dest, err := tcpServer.Start()
   389  	common.Must(err)
   390  	defer tcpServer.Close()
   391  
   392  	userID := protocol.NewID(uuid.New())
   393  	serverPort := tcp.PickPort()
   394  	cmdPort := tcp.PickPort()
   395  
   396  	serverConfig := &core.Config{
   397  		App: []*serial.TypedMessage{
   398  			serial.ToTypedMessage(&stats.Config{}),
   399  			serial.ToTypedMessage(&commander.Config{
   400  				Tag: "api",
   401  				Service: []*serial.TypedMessage{
   402  					serial.ToTypedMessage(&statscmd.Config{}),
   403  				},
   404  			}),
   405  			serial.ToTypedMessage(&router.Config{
   406  				Rule: []*router.RoutingRule{
   407  					{
   408  						InboundTag: []string{"api"},
   409  						TargetTag: &router.RoutingRule_Tag{
   410  							Tag: "api",
   411  						},
   412  					},
   413  				},
   414  			}),
   415  			serial.ToTypedMessage(&policy.Config{
   416  				Level: map[uint32]*policy.Policy{
   417  					0: {
   418  						Timeout: &policy.Policy_Timeout{
   419  							UplinkOnly:   &policy.Second{Value: 0},
   420  							DownlinkOnly: &policy.Second{Value: 0},
   421  						},
   422  					},
   423  					1: {
   424  						Stats: &policy.Policy_Stats{
   425  							UserUplink:   true,
   426  							UserDownlink: true,
   427  						},
   428  					},
   429  				},
   430  				System: &policy.SystemPolicy{
   431  					Stats: &policy.SystemPolicy_Stats{
   432  						InboundUplink: true,
   433  					},
   434  				},
   435  			}),
   436  		},
   437  		Inbound: []*core.InboundHandlerConfig{
   438  			{
   439  				Tag: "vmess",
   440  				ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
   441  					PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}},
   442  					Listen:   net.NewIPOrDomain(net.LocalHostIP),
   443  				}),
   444  				ProxySettings: serial.ToTypedMessage(&inbound.Config{
   445  					User: []*protocol.User{
   446  						{
   447  							Level: 1,
   448  							Email: "test",
   449  							Account: serial.ToTypedMessage(&vmess.Account{
   450  								Id: userID.String(),
   451  							}),
   452  						},
   453  					},
   454  				}),
   455  			},
   456  			{
   457  				Tag: "api",
   458  				ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
   459  					PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(cmdPort)}},
   460  					Listen:   net.NewIPOrDomain(net.LocalHostIP),
   461  				}),
   462  				ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
   463  					Address: net.NewIPOrDomain(dest.Address),
   464  					Port:    uint32(dest.Port),
   465  					NetworkList: &net.NetworkList{
   466  						Network: []net.Network{net.Network_TCP},
   467  					},
   468  				}),
   469  			},
   470  		},
   471  		Outbound: []*core.OutboundHandlerConfig{
   472  			{
   473  				ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
   474  			},
   475  		},
   476  	}
   477  
   478  	clientPort := tcp.PickPort()
   479  	clientConfig := &core.Config{
   480  		Inbound: []*core.InboundHandlerConfig{
   481  			{
   482  				ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
   483  					PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}},
   484  					Listen:   net.NewIPOrDomain(net.LocalHostIP),
   485  				}),
   486  				ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
   487  					Address: net.NewIPOrDomain(dest.Address),
   488  					Port:    uint32(dest.Port),
   489  					NetworkList: &net.NetworkList{
   490  						Network: []net.Network{net.Network_TCP},
   491  					},
   492  				}),
   493  			},
   494  		},
   495  		Outbound: []*core.OutboundHandlerConfig{
   496  			{
   497  				ProxySettings: serial.ToTypedMessage(&outbound.Config{
   498  					Receiver: []*protocol.ServerEndpoint{
   499  						{
   500  							Address: net.NewIPOrDomain(net.LocalHostIP),
   501  							Port:    uint32(serverPort),
   502  							User: []*protocol.User{
   503  								{
   504  									Account: serial.ToTypedMessage(&vmess.Account{
   505  										Id: userID.String(),
   506  										SecuritySettings: &protocol.SecurityConfig{
   507  											Type: protocol.SecurityType_AES128_GCM,
   508  										},
   509  									}),
   510  								},
   511  							},
   512  						},
   513  					},
   514  				}),
   515  			},
   516  		},
   517  	}
   518  
   519  	servers, err := InitializeServerConfigs(serverConfig, clientConfig)
   520  	if err != nil {
   521  		t.Fatal("Failed to create all servers", err)
   522  	}
   523  	defer CloseAllServers(servers)
   524  
   525  	if err := testTCPConn(clientPort, 10240*1024, time.Second*20)(); err != nil {
   526  		t.Fatal(err)
   527  	}
   528  
   529  	cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock())
   530  	common.Must(err)
   531  	defer cmdConn.Close()
   532  
   533  	const name = "user>>>test>>>traffic>>>uplink"
   534  	sClient := statscmd.NewStatsServiceClient(cmdConn)
   535  
   536  	sresp, err := sClient.GetStats(context.Background(), &statscmd.GetStatsRequest{
   537  		Name:   name,
   538  		Reset_: true,
   539  	})
   540  	common.Must(err)
   541  	if r := cmp.Diff(sresp.Stat, &statscmd.Stat{
   542  		Name:  name,
   543  		Value: 10240 * 1024,
   544  	}, cmpopts.IgnoreUnexported(statscmd.Stat{})); r != "" {
   545  		t.Error(r)
   546  	}
   547  
   548  	sresp, err = sClient.GetStats(context.Background(), &statscmd.GetStatsRequest{
   549  		Name: name,
   550  	})
   551  	common.Must(err)
   552  	if r := cmp.Diff(sresp.Stat, &statscmd.Stat{
   553  		Name:  name,
   554  		Value: 0,
   555  	}, cmpopts.IgnoreUnexported(statscmd.Stat{})); r != "" {
   556  		t.Error(r)
   557  	}
   558  
   559  	sresp, err = sClient.GetStats(context.Background(), &statscmd.GetStatsRequest{
   560  		Name:   "inbound>>>vmess>>>traffic>>>uplink",
   561  		Reset_: true,
   562  	})
   563  	common.Must(err)
   564  	if sresp.Stat.Value <= 10240*1024 {
   565  		t.Error("value < 10240*1024: ", sresp.Stat.Value)
   566  	}
   567  }