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