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