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 }