From d9181ad84a1a2789ae761f02da782a72a64cf28b Mon Sep 17 00:00:00 2001 From: "Xiaokang Wang (Shelikhoo)" Date: Mon, 16 Sep 2024 11:19:42 +0100 Subject: [PATCH] Add Testing for meek, grpc, httpUpgrade Transport (#3160) * Add instance management based testing * Add testing for meek transport * Add testing for grpc, httpupgrade transport --- testing/scenarios/cert/self-signed_cert.pem | 11 +++ testing/scenarios/cert/self-signed_key.pem | 5 + testing/scenarios/common.go | 21 +++++ testing/scenarios/common_instanceMgr.go | 27 ++++++ testing/scenarios/common_instanceMgr_test.go | 7 ++ testing/scenarios/config/grpc_client.json | 45 +++++++++ testing/scenarios/config/grpc_server.json | 43 +++++++++ .../config/grpc_servicename_client.json | 46 ++++++++++ .../config/grpc_servicename_server.json | 44 +++++++++ .../scenarios/config/httpupgrade_client.json | 46 ++++++++++ .../scenarios/config/httpupgrade_server.json | 44 +++++++++ testing/scenarios/config/meek_client.json | 46 ++++++++++ testing/scenarios/config/meek_server.json | 44 +++++++++ testing/scenarios/grpc_test.go | 91 +++++++++++++++++++ testing/scenarios/httpupgrade_test.go | 52 +++++++++++ testing/scenarios/meek_test.go | 52 +++++++++++ 16 files changed, 624 insertions(+) create mode 100644 testing/scenarios/cert/self-signed_cert.pem create mode 100644 testing/scenarios/cert/self-signed_key.pem create mode 100644 testing/scenarios/common_instanceMgr.go create mode 100644 testing/scenarios/common_instanceMgr_test.go create mode 100644 testing/scenarios/config/grpc_client.json create mode 100644 testing/scenarios/config/grpc_server.json create mode 100644 testing/scenarios/config/grpc_servicename_client.json create mode 100644 testing/scenarios/config/grpc_servicename_server.json create mode 100644 testing/scenarios/config/httpupgrade_client.json create mode 100644 testing/scenarios/config/httpupgrade_server.json create mode 100644 testing/scenarios/config/meek_client.json create mode 100644 testing/scenarios/config/meek_server.json create mode 100644 testing/scenarios/grpc_test.go create mode 100644 testing/scenarios/httpupgrade_test.go create mode 100644 testing/scenarios/meek_test.go diff --git a/testing/scenarios/cert/self-signed_cert.pem b/testing/scenarios/cert/self-signed_cert.pem new file mode 100644 index 00000000000..6eb3dc155ad --- /dev/null +++ b/testing/scenarios/cert/self-signed_cert.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBhDCCASmgAwIBAgIQOGW77bhKIQBVcKBkruQEjDAKBggqhkjOPQQDAjAoMRIw +EAYDVQQKEwlWMlJheSBJbmMxEjAQBgNVBAMTCVYyUmF5IEluYzAeFw0yNDA5MTUx +NjIyMjRaFw0yNDEyMTQxNzIyMjRaMCgxEjAQBgNVBAoTCVYyUmF5IEluYzESMBAG +A1UEAxMJVjJSYXkgSW5jMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmKTCe3pJ +6qYR8JSt4LHurI9ukGQISTBBLrFw8fDsWTeJielnHZdqgKL9swcC+IF/ikjzbT5W +iP0EGUBGk6wbPKM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUF +BwMBMAwGA1UdEwEB/wQCMAAwCgYIKoZIzj0EAwIDSQAwRgIhAKIZM1xlaHwSHsZP +V6aN+AbWnoRgwVVJuY4I5q17FY0cAiEAiLr9NkHd9glFz0BzALygvo7gnNpgF69l +DdiZ4828MXY= +-----END CERTIFICATE----- diff --git a/testing/scenarios/cert/self-signed_key.pem b/testing/scenarios/cert/self-signed_key.pem new file mode 100644 index 00000000000..4b77ce22278 --- /dev/null +++ b/testing/scenarios/cert/self-signed_key.pem @@ -0,0 +1,5 @@ +-----BEGIN RSA PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgmkOATgwafiG4sDs/ +J1xVZfimvgREFJy0mtZHeGM2O6ehRANCAASYpMJ7eknqphHwlK3gse6sj26QZAhJ +MEEusXDx8OxZN4mJ6Wcdl2qAov2zBwL4gX+KSPNtPlaI/QQZQEaTrBs8 +-----END RSA PRIVATE KEY----- diff --git a/testing/scenarios/common.go b/testing/scenarios/common.go index 8bafee0e011..f23291aac37 100644 --- a/testing/scenarios/common.go +++ b/testing/scenarios/common.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/rand" "fmt" + "golang.org/x/net/proxy" "io" "os" "os/exec" @@ -168,6 +169,26 @@ func withDefaultApps(config *core.Config) *core.Config { return config } +func testTCPConnViaSocks(socksPort, testPort net.Port, payloadSize int, timeout time.Duration) func() error { + return func() error { + socksDialer, err := proxy.SOCKS5("tcp", "127.0.0.1:"+socksPort.String(), nil, nil) + if err != nil { + return err + } + destAddr := &net.TCPAddr{ + IP: []byte{127, 0, 0, 1}, + Port: int(testPort), + } + conn, err := socksDialer.Dial("tcp", destAddr.String()) + if err != nil { + return err + } + defer conn.Close() + + return testTCPConn2(conn, payloadSize, timeout)() + } +} + func testTCPConn(port net.Port, payloadSize int, timeout time.Duration) func() error { return func() error { conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{ diff --git a/testing/scenarios/common_instanceMgr.go b/testing/scenarios/common_instanceMgr.go new file mode 100644 index 00000000000..59fbf5db2d7 --- /dev/null +++ b/testing/scenarios/common_instanceMgr.go @@ -0,0 +1,27 @@ +package scenarios + +import ( + core "github.com/v2fly/v2ray-core/v5" + "github.com/v2fly/v2ray-core/v5/app/instman" + "github.com/v2fly/v2ray-core/v5/common" + "github.com/v2fly/v2ray-core/v5/common/serial" + "github.com/v2fly/v2ray-core/v5/features/extension" +) + +func NewInstanceManagerInstanceConfig() *core.Config { + config := &core.Config{} + config.App = append(config.App, serial.ToTypedMessage(&instman.Config{})) + return config +} + +func NewInstanceManagerCoreInstance() (*core.Instance, extension.InstanceManagement) { + coreConfig := NewInstanceManagerInstanceConfig() + instance, err := core.New(coreConfig) + if err != nil { + panic(err) + } + common.Must(instance.Start()) + instanceMgr := instance.GetFeature(extension.InstanceManagementType()) + InstanceMgrIfce := instanceMgr.(extension.InstanceManagement) + return instance, InstanceMgrIfce +} diff --git a/testing/scenarios/common_instanceMgr_test.go b/testing/scenarios/common_instanceMgr_test.go new file mode 100644 index 00000000000..49e85d5e129 --- /dev/null +++ b/testing/scenarios/common_instanceMgr_test.go @@ -0,0 +1,7 @@ +package scenarios + +import "testing" + +func TestInstanceMgrInit(t *testing.T) { + NewInstanceManagerCoreInstance() +} diff --git a/testing/scenarios/config/grpc_client.json b/testing/scenarios/config/grpc_client.json new file mode 100644 index 00000000000..b815e36855c --- /dev/null +++ b/testing/scenarios/config/grpc_client.json @@ -0,0 +1,45 @@ +{ + "log": { + "error": { + "level": "Debug", + "type": "Console" + }, + "access": { + "type": "None" + } + }, + "outbounds": [ + { + "protocol": "vmess", + "settings": { + "address": "127.0.0.1", + "port": 17783, + "uuid": "bcc71618-e552-42c2-a2a3-d4c17a9df764" + }, + "streamSettings": { + "transport": "grpc", + "transportSettings": { + }, + "security": "tls", + "securitySettings": { + "pinnedPeerCertificateChainSha256": [ + "kqHyvea27Pn+JiSqA72lhu9IKAKeGR+3yCyA8JR1mug=" + ], + "allowInsecureIfPinnedPeerCertificate": true + } + } + } + ], + "inbounds": [ + { + "protocol": "socks", + "settings": { + "udpEnabled": false, + "address": "127.0.0.1", + "packetEncoding": "Packet" + }, + "port": 17784, + "listen": "127.0.0.1" + } + ] +} diff --git a/testing/scenarios/config/grpc_server.json b/testing/scenarios/config/grpc_server.json new file mode 100644 index 00000000000..dc307010d8b --- /dev/null +++ b/testing/scenarios/config/grpc_server.json @@ -0,0 +1,43 @@ +{ + "log": { + "error": { + "level": "Debug", + "type": "Console" + }, + "access": { + "type": "None" + } + }, + "outbounds": [ + { + "protocol": "freedom" + } + ], + "inbounds": [ + { + "listen": "127.0.0.1", + "port": 17783, + "protocol": "vmess", + "settings": { + "users": [ + "bcc71618-e552-42c2-a2a3-d4c17a9df764" + ] + }, + "streamSettings": { + "transport": "grpc", + "transportSettings": { + }, + "security": "tls", + "securitySettings": { + "certificate": [ + { + "usage": "ENCIPHERMENT", + "certificateFile": "cert/self-signed_cert.pem", + "keyFile": "cert/self-signed_key.pem" + } + ] + } + } + } + ] +} diff --git a/testing/scenarios/config/grpc_servicename_client.json b/testing/scenarios/config/grpc_servicename_client.json new file mode 100644 index 00000000000..c624bd43662 --- /dev/null +++ b/testing/scenarios/config/grpc_servicename_client.json @@ -0,0 +1,46 @@ +{ + "log": { + "error": { + "level": "Debug", + "type": "Console" + }, + "access": { + "type": "None" + } + }, + "outbounds": [ + { + "protocol": "vmess", + "settings": { + "address": "127.0.0.1", + "port": 17793, + "uuid": "bcc71618-e552-42c2-a2a3-d4c17a9df764" + }, + "streamSettings": { + "transport": "grpc", + "transportSettings": { + "serviceName": "0eae44595474" + }, + "security": "tls", + "securitySettings": { + "pinnedPeerCertificateChainSha256": [ + "kqHyvea27Pn+JiSqA72lhu9IKAKeGR+3yCyA8JR1mug=" + ], + "allowInsecureIfPinnedPeerCertificate": true + } + } + } + ], + "inbounds": [ + { + "protocol": "socks", + "settings": { + "udpEnabled": false, + "address": "127.0.0.1", + "packetEncoding": "Packet" + }, + "port": 17794, + "listen": "127.0.0.1" + } + ] +} diff --git a/testing/scenarios/config/grpc_servicename_server.json b/testing/scenarios/config/grpc_servicename_server.json new file mode 100644 index 00000000000..22c8ca312b8 --- /dev/null +++ b/testing/scenarios/config/grpc_servicename_server.json @@ -0,0 +1,44 @@ +{ + "log": { + "error": { + "level": "Debug", + "type": "Console" + }, + "access": { + "type": "None" + } + }, + "outbounds": [ + { + "protocol": "freedom" + } + ], + "inbounds": [ + { + "listen": "127.0.0.1", + "port": 17793, + "protocol": "vmess", + "settings": { + "users": [ + "bcc71618-e552-42c2-a2a3-d4c17a9df764" + ] + }, + "streamSettings": { + "transport": "grpc", + "transportSettings": { + "serviceName": "0eae44595474" + }, + "security": "tls", + "securitySettings": { + "certificate": [ + { + "usage": "ENCIPHERMENT", + "certificateFile": "cert/self-signed_cert.pem", + "keyFile": "cert/self-signed_key.pem" + } + ] + } + } + } + ] +} diff --git a/testing/scenarios/config/httpupgrade_client.json b/testing/scenarios/config/httpupgrade_client.json new file mode 100644 index 00000000000..47f56899ede --- /dev/null +++ b/testing/scenarios/config/httpupgrade_client.json @@ -0,0 +1,46 @@ +{ + "log": { + "error": { + "level": "Debug", + "type": "Console" + }, + "access": { + "type": "None" + } + }, + "outbounds": [ + { + "protocol": "vmess", + "settings": { + "address": "127.0.0.1", + "port": 17793, + "uuid": "bcc71618-e552-42c2-a2a3-d4c17a9df764" + }, + "streamSettings": { + "transport": "httpupgrade", + "transportSettings": { + "path": "b66efc0c7752" + }, + "security": "tls", + "securitySettings": { + "pinnedPeerCertificateChainSha256": [ + "kqHyvea27Pn+JiSqA72lhu9IKAKeGR+3yCyA8JR1mug=" + ], + "allowInsecureIfPinnedPeerCertificate": true + } + } + } + ], + "inbounds": [ + { + "protocol": "socks", + "settings": { + "udpEnabled": false, + "address": "127.0.0.1", + "packetEncoding": "Packet" + }, + "port": 17794, + "listen": "127.0.0.1" + } + ] +} diff --git a/testing/scenarios/config/httpupgrade_server.json b/testing/scenarios/config/httpupgrade_server.json new file mode 100644 index 00000000000..7bf734ea3dd --- /dev/null +++ b/testing/scenarios/config/httpupgrade_server.json @@ -0,0 +1,44 @@ +{ + "log": { + "error": { + "level": "Debug", + "type": "Console" + }, + "access": { + "type": "None" + } + }, + "outbounds": [ + { + "protocol": "freedom" + } + ], + "inbounds": [ + { + "listen": "127.0.0.1", + "port": 17793, + "protocol": "vmess", + "settings": { + "users": [ + "bcc71618-e552-42c2-a2a3-d4c17a9df764" + ] + }, + "streamSettings": { + "transport": "httpupgrade", + "transportSettings": { + "path": "b66efc0c7752" + }, + "security": "tls", + "securitySettings": { + "certificate": [ + { + "usage": "ENCIPHERMENT", + "certificateFile": "cert/self-signed_cert.pem", + "keyFile": "cert/self-signed_key.pem" + } + ] + } + } + } + ] +} diff --git a/testing/scenarios/config/meek_client.json b/testing/scenarios/config/meek_client.json new file mode 100644 index 00000000000..98d23f594d0 --- /dev/null +++ b/testing/scenarios/config/meek_client.json @@ -0,0 +1,46 @@ +{ + "log": { + "error": { + "level": "Debug", + "type": "Console" + }, + "access": { + "type": "None" + } + }, + "outbounds": [ + { + "protocol": "vmess", + "settings": { + "address": "127.0.0.1", + "port": 17773, + "uuid": "bcc71618-e552-42c2-a2a3-d4c17a9df764" + }, + "streamSettings": { + "transport": "meek", + "transportSettings": { + "url": "https://127.0.0.1:17773/mrss48bvxrkfq1qzeqte5o61mmvc9gx6hq51" + }, + "security": "tls", + "securitySettings": { + "pinnedPeerCertificateChainSha256": [ + "kqHyvea27Pn+JiSqA72lhu9IKAKeGR+3yCyA8JR1mug=" + ], + "allowInsecureIfPinnedPeerCertificate": true + } + } + } + ], + "inbounds": [ + { + "protocol": "socks", + "settings": { + "udpEnabled": false, + "address": "127.0.0.1", + "packetEncoding": "Packet" + }, + "port": 17774, + "listen": "127.0.0.1" + } + ] +} diff --git a/testing/scenarios/config/meek_server.json b/testing/scenarios/config/meek_server.json new file mode 100644 index 00000000000..776b23afa6f --- /dev/null +++ b/testing/scenarios/config/meek_server.json @@ -0,0 +1,44 @@ +{ + "log": { + "error": { + "level": "Debug", + "type": "Console" + }, + "access": { + "type": "None" + } + }, + "outbounds": [ + { + "protocol": "freedom" + } + ], + "inbounds": [ + { + "listen": "127.0.0.1", + "port": 17773, + "protocol": "vmess", + "settings": { + "users": [ + "bcc71618-e552-42c2-a2a3-d4c17a9df764" + ] + }, + "streamSettings": { + "transport": "meek", + "transportSettings": { + "url": "http://127.0.0.1:12777" + }, + "security": "tls", + "securitySettings": { + "certificate": [ + { + "usage": "ENCIPHERMENT", + "certificateFile": "cert/self-signed_cert.pem", + "keyFile": "cert/self-signed_key.pem" + } + ] + } + } + } + ] +} diff --git a/testing/scenarios/grpc_test.go b/testing/scenarios/grpc_test.go new file mode 100644 index 00000000000..19df73d424b --- /dev/null +++ b/testing/scenarios/grpc_test.go @@ -0,0 +1,91 @@ +package scenarios + +import ( + "context" + "os" + "testing" + "time" + + "github.com/v2fly/v2ray-core/v5/common" + "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" + + _ "github.com/v2fly/v2ray-core/v5/main/distro/all" +) + +func TestGRPCDefault(t *testing.T) { + tcpServer := tcp.Server{ + MsgProcessor: xor, + } + dest, err := tcpServer.Start() + common.Must(err) + defer tcpServer.Close() + + coreInst, InstMgrIfce := NewInstanceManagerCoreInstance() + defer coreInst.Close() + + common.Must(InstMgrIfce.AddInstance( + context.TODO(), + "grpc_client", + common.Must2(os.ReadFile("config/grpc_client.json")).([]byte), + "jsonv5")) + + common.Must(InstMgrIfce.AddInstance( + context.TODO(), + "grpc_server", + common.Must2(os.ReadFile("config/grpc_server.json")).([]byte), + "jsonv5")) + + common.Must(InstMgrIfce.StartInstance(context.TODO(), "grpc_server")) + common.Must(InstMgrIfce.StartInstance(context.TODO(), "grpc_client")) + + defer func() { + common.Must(InstMgrIfce.StopInstance(context.TODO(), "grpc_server")) + common.Must(InstMgrIfce.StopInstance(context.TODO(), "grpc_client")) + common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "grpc_server")) + common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "grpc_client")) + coreInst.Close() + }() + + if err := testTCPConnViaSocks(17784, dest.Port, 1024, time.Second*2)(); err != nil { + t.Error(err) + } +} + +func TestGRPCWithServiceName(t *testing.T) { + tcpServer := tcp.Server{ + MsgProcessor: xor, + } + dest, err := tcpServer.Start() + common.Must(err) + defer tcpServer.Close() + + coreInst, InstMgrIfce := NewInstanceManagerCoreInstance() + defer coreInst.Close() + + common.Must(InstMgrIfce.AddInstance( + context.TODO(), + "grpc_client", + common.Must2(os.ReadFile("config/grpc_servicename_client.json")).([]byte), + "jsonv5")) + + common.Must(InstMgrIfce.AddInstance( + context.TODO(), + "grpc_server", + common.Must2(os.ReadFile("config/grpc_servicename_server.json")).([]byte), + "jsonv5")) + + common.Must(InstMgrIfce.StartInstance(context.TODO(), "grpc_server")) + common.Must(InstMgrIfce.StartInstance(context.TODO(), "grpc_client")) + + defer func() { + common.Must(InstMgrIfce.StopInstance(context.TODO(), "grpc_server")) + common.Must(InstMgrIfce.StopInstance(context.TODO(), "grpc_client")) + common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "grpc_server")) + common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "grpc_client")) + coreInst.Close() + }() + + if err := testTCPConnViaSocks(17794, dest.Port, 1024, time.Second*2)(); err != nil { + t.Error(err) + } +} diff --git a/testing/scenarios/httpupgrade_test.go b/testing/scenarios/httpupgrade_test.go new file mode 100644 index 00000000000..6a7366c3b81 --- /dev/null +++ b/testing/scenarios/httpupgrade_test.go @@ -0,0 +1,52 @@ +package scenarios + +import ( + "context" + "os" + "testing" + "time" + + "github.com/v2fly/v2ray-core/v5/common" + "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" + + _ "github.com/v2fly/v2ray-core/v5/main/distro/all" +) + +func TestHTTPUpgrade(t *testing.T) { + tcpServer := tcp.Server{ + MsgProcessor: xor, + } + dest, err := tcpServer.Start() + common.Must(err) + defer tcpServer.Close() + + coreInst, InstMgrIfce := NewInstanceManagerCoreInstance() + defer coreInst.Close() + + common.Must(InstMgrIfce.AddInstance( + context.TODO(), + "httpupgrade_client", + common.Must2(os.ReadFile("config/httpupgrade_client.json")).([]byte), + "jsonv5")) + + common.Must(InstMgrIfce.AddInstance( + context.TODO(), + "httpupgrade_server", + common.Must2(os.ReadFile("config/httpupgrade_server.json")).([]byte), + "jsonv5")) + + common.Must(InstMgrIfce.StartInstance(context.TODO(), "httpupgrade_server")) + common.Must(InstMgrIfce.StartInstance(context.TODO(), "httpupgrade_client")) + + defer func() { + common.Must(InstMgrIfce.StopInstance(context.TODO(), "httpupgrade_server")) + common.Must(InstMgrIfce.StopInstance(context.TODO(), "httpupgrade_client")) + common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "httpupgrade_server")) + common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "httpupgrade_client")) + coreInst.Close() + }() + + if err := testTCPConnViaSocks(17794, dest.Port, 1024, time.Second*2)(); err != nil { + t.Error(err) + } +} diff --git a/testing/scenarios/meek_test.go b/testing/scenarios/meek_test.go new file mode 100644 index 00000000000..1d6a6b43384 --- /dev/null +++ b/testing/scenarios/meek_test.go @@ -0,0 +1,52 @@ +package scenarios + +import ( + "context" + "os" + "testing" + "time" + + "github.com/v2fly/v2ray-core/v5/common" + "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" + + _ "github.com/v2fly/v2ray-core/v5/main/distro/all" +) + +func TestMeek(t *testing.T) { + tcpServer := tcp.Server{ + MsgProcessor: xor, + } + dest, err := tcpServer.Start() + common.Must(err) + defer tcpServer.Close() + + coreInst, InstMgrIfce := NewInstanceManagerCoreInstance() + defer coreInst.Close() + + common.Must(InstMgrIfce.AddInstance( + context.TODO(), + "meek_client", + common.Must2(os.ReadFile("config/meek_client.json")).([]byte), + "jsonv5")) + + common.Must(InstMgrIfce.AddInstance( + context.TODO(), + "meek_server", + common.Must2(os.ReadFile("config/meek_server.json")).([]byte), + "jsonv5")) + + common.Must(InstMgrIfce.StartInstance(context.TODO(), "meek_server")) + common.Must(InstMgrIfce.StartInstance(context.TODO(), "meek_client")) + + defer func() { + common.Must(InstMgrIfce.StopInstance(context.TODO(), "meek_server")) + common.Must(InstMgrIfce.StopInstance(context.TODO(), "meek_client")) + common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "meek_server")) + common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "meek_client")) + coreInst.Close() + }() + + if err := testTCPConnViaSocks(17774, dest.Port, 1024, time.Second*2)(); err != nil { + t.Error(err) + } +}