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 }