Browse Source

Add multiplexer for vmess

世界 3 years ago
parent
commit
64dbac8138
12 changed files with 171 additions and 50 deletions
  1. 1 1
      common/mux/protocol.go
  2. 6 1
      docs/configuration/outbound/vmess.md
  3. 2 2
      go.mod
  4. 4 4
      go.sum
  5. 4 4
      option/shadowsocks.go
  6. 1 0
      option/vmess.go
  7. 1 1
      outbound/builder.go
  8. 1 1
      outbound/shadowsocks.go
  9. 61 25
      outbound/vmess.go
  10. 2 2
      test/go.mod
  11. 4 4
      test/go.sum
  12. 84 5
      test/mux_test.go

+ 1 - 1
common/mux/protocol.go

@@ -109,7 +109,7 @@ func ReadRequest(reader io.Reader) (*Request, error) {
 	if err != nil {
 		return nil, err
 	}
-	if protocol > byte(ProtocolSMux) {
+	if protocol > byte(ProtocolYAMux) {
 		return nil, E.New("unsupported protocol: ", protocol)
 	}
 	return &Request{Protocol: Protocol(protocol)}, nil

+ 6 - 1
docs/configuration/outbound/vmess.md

@@ -16,7 +16,8 @@
       "authenticated_length": true,
       "network": "tcp",
       "tls": {},
-      
+      "multiplex": {},
+
       "detour": "upstream-out",
       "bind_interface": "en0",
       "routing_mark": 1234,
@@ -92,6 +93,10 @@ Both is enabled by default.
 
 TLS configuration, see [TLS outbound structure](/configuration/shared/tls/#outbound-structure).
 
+#### multiplex
+
+Multiplex configuration, see [Multiplex structure](/configuration/shared/multiplex).
+
 ### Dial Fields
 
 #### detour

+ 2 - 2
go.mod

@@ -13,11 +13,11 @@ require (
 	github.com/hashicorp/yamux v0.1.1
 	github.com/logrusorgru/aurora v2.0.3+incompatible
 	github.com/oschwald/maxminddb-golang v1.9.0
-	github.com/sagernet/sing v0.0.0-20220803094243-d9ca259bec6a
+	github.com/sagernet/sing v0.0.0-20220804023557-9c64b40e7050
 	github.com/sagernet/sing-dns v0.0.0-20220803121532-9e1ffb850d91
 	github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1
 	github.com/sagernet/sing-tun v0.0.0-20220803112223-a8fd6450d4ed
-	github.com/sagernet/sing-vmess v0.0.0-20220802053753-a38d3b22e6b9
+	github.com/sagernet/sing-vmess v0.0.0-20220804023624-e829b41c84c2
 	github.com/spf13/cobra v1.5.0
 	github.com/stretchr/testify v1.8.0
 	github.com/xtaci/smux v1.5.16

+ 4 - 4
go.sum

@@ -149,16 +149,16 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/sagernet/netlink v0.0.0-20220803045538-bdac49abf805 h1:hE+vtsjBCCPmxkRz9jZA+CicHgVkDT6H+Av5ZzskVxs=
 github.com/sagernet/netlink v0.0.0-20220803045538-bdac49abf805/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
-github.com/sagernet/sing v0.0.0-20220803094243-d9ca259bec6a h1:CHxuamnZEAxXoep7ycGSuMOiKzsRYuYa0ucnOCdUT9U=
-github.com/sagernet/sing v0.0.0-20220803094243-d9ca259bec6a/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
+github.com/sagernet/sing v0.0.0-20220804023557-9c64b40e7050 h1:Q6PV3tlP7htm9jgebFryXT4HFvEHvb5QHEcuWrf4E2o=
+github.com/sagernet/sing v0.0.0-20220804023557-9c64b40e7050/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
 github.com/sagernet/sing-dns v0.0.0-20220803121532-9e1ffb850d91 h1:jxt2PYixIkK2i7nUGW3f+PzJagEZcbNyQddBWGuqNnw=
 github.com/sagernet/sing-dns v0.0.0-20220803121532-9e1ffb850d91/go.mod h1:T77zZdE2Cm6VqnFumrpwsq+kxYsbq+vWDhmjtdSl/oM=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1 h1:RYvOc69eSNMN0dwVugrDts41Nn7Ar/C/n/fvytvFcp4=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1/go.mod h1:NqZjiXszgVCMQ4gVDa2V+drhS8NMfGqUqDF86EacEFc=
 github.com/sagernet/sing-tun v0.0.0-20220803112223-a8fd6450d4ed h1:28qeqeuHLZEkzdcZjYwcCn8y4ckyKimaP+L4P25dqUo=
 github.com/sagernet/sing-tun v0.0.0-20220803112223-a8fd6450d4ed/go.mod h1:jNlPidQzZYkpmpQJ+sDN2YGrPsL4QImoqBpuauId9po=
-github.com/sagernet/sing-vmess v0.0.0-20220802053753-a38d3b22e6b9 h1:x+r8P5MKyQWGN3tcI5dmOM1Aei1mlWua2ciMBGz0/oM=
-github.com/sagernet/sing-vmess v0.0.0-20220802053753-a38d3b22e6b9/go.mod h1:ETvczg3TQzGa8zg0lYXj8cYTJr4+OFUmtQer9/c/cLU=
+github.com/sagernet/sing-vmess v0.0.0-20220804023624-e829b41c84c2 h1:C8sc2MYiNx0O7uQ0nieJWq5qYeIHj20XHFWPlcgoQeY=
+github.com/sagernet/sing-vmess v0.0.0-20220804023624-e829b41c84c2/go.mod h1:bNXBqSWYaG3ePl6u0xQY5zneE+ZKa3683ZpuE8S1M1w=
 github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
 github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=

+ 4 - 4
option/shadowsocks.go

@@ -24,8 +24,8 @@ type ShadowsocksDestination struct {
 type ShadowsocksOutboundOptions struct {
 	OutboundDialerOptions
 	ServerOptions
-	Method    string            `json:"method"`
-	Password  string            `json:"password"`
-	Network   NetworkList       `json:"network,omitempty"`
-	Multiplex *MultiplexOptions `json:"multiplex,omitempty"`
+	Method           string            `json:"method"`
+	Password         string            `json:"password"`
+	Network          NetworkList       `json:"network,omitempty"`
+	MultiplexOptions *MultiplexOptions `json:"multiplex,omitempty"`
 }

+ 1 - 0
option/vmess.go

@@ -22,4 +22,5 @@ type VMessOutboundOptions struct {
 	AuthenticatedLength bool                `json:"authenticated_length,omitempty"`
 	Network             NetworkList         `json:"network,omitempty"`
 	TLSOptions          *OutboundTLSOptions `json:"tls,omitempty"`
+	MultiplexOptions    *MultiplexOptions   `json:"multiplex,omitempty"`
 }

+ 1 - 1
outbound/builder.go

@@ -28,7 +28,7 @@ func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, o
 	case C.TypeShadowsocks:
 		return NewShadowsocks(ctx, router, logger, options.Tag, options.ShadowsocksOptions)
 	case C.TypeVMess:
-		return NewVMess(router, logger, options.Tag, options.VMessOptions)
+		return NewVMess(ctx, router, logger, options.Tag, options.VMessOptions)
 	case C.TypeSelector:
 		return NewSelector(router, logger, options.Tag, options.SelectorOptions)
 	default:

+ 1 - 1
outbound/shadowsocks.go

@@ -46,7 +46,7 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
 		method:     method,
 		serverAddr: options.ServerOptions.Build(),
 	}
-	outbound.multiplexDialer, err = mux.NewClientWithOptions(ctx, (*shadowsocksDialer)(outbound), common.PtrValueOrDefault(options.Multiplex))
+	outbound.multiplexDialer, err = mux.NewClientWithOptions(ctx, (*shadowsocksDialer)(outbound), common.PtrValueOrDefault(options.MultiplexOptions))
 	if err != nil {
 		return nil, err
 	}

+ 61 - 25
outbound/vmess.go

@@ -6,6 +6,7 @@ import (
 
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/common/dialer"
+	"github.com/sagernet/sing-box/common/mux"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/log"
 	"github.com/sagernet/sing-box/option"
@@ -20,12 +21,13 @@ var _ adapter.Outbound = (*VMess)(nil)
 
 type VMess struct {
 	myOutboundAdapter
-	dialer     N.Dialer
-	client     *vmess.Client
-	serverAddr M.Socksaddr
+	dialer          N.Dialer
+	client          *vmess.Client
+	serverAddr      M.Socksaddr
+	multiplexDialer N.Dialer
 }
 
-func NewVMess(router adapter.Router, logger log.ContextLogger, tag string, options option.VMessOutboundOptions) (*VMess, error) {
+func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VMessOutboundOptions) (*VMess, error) {
 	var clientOptions []vmess.ClientOption
 	if options.GlobalPadding {
 		clientOptions = append(clientOptions, vmess.ClientWithGlobalPadding())
@@ -37,38 +39,80 @@ func NewVMess(router adapter.Router, logger log.ContextLogger, tag string, optio
 	if err != nil {
 		return nil, err
 	}
-	detour, err := dialer.NewTLS(dialer.NewOutbound(router, options.OutboundDialerOptions), options.Server, common.PtrValueOrDefault(options.TLSOptions))
-	if err != nil {
-		return nil, err
-	}
-	return &VMess{
-		myOutboundAdapter{
+	inbound := &VMess{
+		myOutboundAdapter: myOutboundAdapter{
 			protocol: C.TypeVMess,
 			network:  options.Network.Build(),
 			router:   router,
 			logger:   logger,
 			tag:      tag,
 		},
-		detour,
-		client,
-		options.ServerOptions.Build(),
-	}, nil
+		client:     client,
+		serverAddr: options.ServerOptions.Build(),
+	}
+	inbound.dialer, err = dialer.NewTLS(dialer.NewOutbound(router, options.OutboundDialerOptions), options.Server, common.PtrValueOrDefault(options.TLSOptions))
+	if err != nil {
+		return nil, err
+	}
+	inbound.multiplexDialer, err = mux.NewClientWithOptions(ctx, (*vmessDialer)(inbound), common.PtrValueOrDefault(options.MultiplexOptions))
+	if err != nil {
+		return nil, err
+	}
+	return inbound, nil
 }
 
 func (h *VMess) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
+	if h.multiplexDialer == nil {
+		switch N.NetworkName(network) {
+		case N.NetworkTCP:
+			h.logger.InfoContext(ctx, "outbound connection to ", destination)
+		case N.NetworkUDP:
+			h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
+		}
+		return (*vmessDialer)(h).DialContext(ctx, network, destination)
+	} else {
+		switch N.NetworkName(network) {
+		case N.NetworkTCP:
+			h.logger.InfoContext(ctx, "outbound multiplex connection to ", destination)
+		case N.NetworkUDP:
+			h.logger.InfoContext(ctx, "outbound multiplex packet connection to ", destination)
+		}
+		return h.multiplexDialer.DialContext(ctx, network, destination)
+	}
+}
+
+func (h *VMess) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
+	if h.multiplexDialer == nil {
+		h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
+		return (*vmessDialer)(h).ListenPacket(ctx, destination)
+	} else {
+		h.logger.InfoContext(ctx, "outbound multiplex packet connection to ", destination)
+		return h.multiplexDialer.ListenPacket(ctx, destination)
+	}
+}
+
+func (h *VMess) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
+	return NewEarlyConnection(ctx, h, conn, metadata)
+}
+
+func (h *VMess) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
+	return NewPacketConnection(ctx, h, conn, metadata)
+}
+
+type vmessDialer VMess
+
+func (h *vmessDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
 	ctx, metadata := adapter.AppendContext(ctx)
 	metadata.Outbound = h.tag
 	metadata.Destination = destination
 	switch N.NetworkName(network) {
 	case N.NetworkTCP:
-		h.logger.InfoContext(ctx, "outbound connection to ", destination)
 		outConn, err := h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
 		if err != nil {
 			return nil, err
 		}
 		return h.client.DialEarlyConn(outConn, destination), nil
 	case N.NetworkUDP:
-		h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
 		outConn, err := h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
 		if err != nil {
 			return nil, err
@@ -79,18 +123,10 @@ func (h *VMess) DialContext(ctx context.Context, network string, destination M.S
 	}
 }
 
-func (h *VMess) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
+func (h *vmessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
 	conn, err := h.DialContext(ctx, N.NetworkUDP, destination)
 	if err != nil {
 		return nil, err
 	}
 	return conn.(vmess.PacketConn), nil
 }
-
-func (h *VMess) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
-	return NewEarlyConnection(ctx, h, conn, metadata)
-}
-
-func (h *VMess) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
-	return NewPacketConnection(ctx, h, conn, metadata)
-}

+ 2 - 2
test/go.mod

@@ -10,7 +10,7 @@ require (
 	github.com/docker/docker v20.10.17+incompatible
 	github.com/docker/go-connections v0.4.0
 	github.com/gofrs/uuid v4.2.0+incompatible
-	github.com/sagernet/sing v0.0.0-20220803094243-d9ca259bec6a
+	github.com/sagernet/sing v0.0.0-20220804023557-9c64b40e7050
 	github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1
 	github.com/spyzhov/ajson v0.7.1
 	github.com/stretchr/testify v1.8.0
@@ -55,7 +55,7 @@ require (
 	github.com/sagernet/netlink v0.0.0-20220803045538-bdac49abf805 // indirect
 	github.com/sagernet/sing-dns v0.0.0-20220803121532-9e1ffb850d91 // indirect
 	github.com/sagernet/sing-tun v0.0.0-20220803112223-a8fd6450d4ed // indirect
-	github.com/sagernet/sing-vmess v0.0.0-20220802053753-a38d3b22e6b9 // indirect
+	github.com/sagernet/sing-vmess v0.0.0-20220804023624-e829b41c84c2 // indirect
 	github.com/sirupsen/logrus v1.8.1 // indirect
 	github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
 	github.com/xtaci/smux v1.5.16 // indirect

+ 4 - 4
test/go.sum

@@ -174,16 +174,16 @@ github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/sagernet/netlink v0.0.0-20220803045538-bdac49abf805 h1:hE+vtsjBCCPmxkRz9jZA+CicHgVkDT6H+Av5ZzskVxs=
 github.com/sagernet/netlink v0.0.0-20220803045538-bdac49abf805/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
-github.com/sagernet/sing v0.0.0-20220803094243-d9ca259bec6a h1:CHxuamnZEAxXoep7ycGSuMOiKzsRYuYa0ucnOCdUT9U=
-github.com/sagernet/sing v0.0.0-20220803094243-d9ca259bec6a/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
+github.com/sagernet/sing v0.0.0-20220804023557-9c64b40e7050 h1:Q6PV3tlP7htm9jgebFryXT4HFvEHvb5QHEcuWrf4E2o=
+github.com/sagernet/sing v0.0.0-20220804023557-9c64b40e7050/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
 github.com/sagernet/sing-dns v0.0.0-20220803121532-9e1ffb850d91 h1:jxt2PYixIkK2i7nUGW3f+PzJagEZcbNyQddBWGuqNnw=
 github.com/sagernet/sing-dns v0.0.0-20220803121532-9e1ffb850d91/go.mod h1:T77zZdE2Cm6VqnFumrpwsq+kxYsbq+vWDhmjtdSl/oM=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1 h1:RYvOc69eSNMN0dwVugrDts41Nn7Ar/C/n/fvytvFcp4=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1/go.mod h1:NqZjiXszgVCMQ4gVDa2V+drhS8NMfGqUqDF86EacEFc=
 github.com/sagernet/sing-tun v0.0.0-20220803112223-a8fd6450d4ed h1:28qeqeuHLZEkzdcZjYwcCn8y4ckyKimaP+L4P25dqUo=
 github.com/sagernet/sing-tun v0.0.0-20220803112223-a8fd6450d4ed/go.mod h1:jNlPidQzZYkpmpQJ+sDN2YGrPsL4QImoqBpuauId9po=
-github.com/sagernet/sing-vmess v0.0.0-20220802053753-a38d3b22e6b9 h1:x+r8P5MKyQWGN3tcI5dmOM1Aei1mlWua2ciMBGz0/oM=
-github.com/sagernet/sing-vmess v0.0.0-20220802053753-a38d3b22e6b9/go.mod h1:ETvczg3TQzGa8zg0lYXj8cYTJr4+OFUmtQer9/c/cLU=
+github.com/sagernet/sing-vmess v0.0.0-20220804023624-e829b41c84c2 h1:C8sc2MYiNx0O7uQ0nieJWq5qYeIHj20XHFWPlcgoQeY=
+github.com/sagernet/sing-vmess v0.0.0-20220804023624-e829b41c84c2/go.mod h1:bNXBqSWYaG3ePl6u0xQY5zneE+ZKa3683ZpuE8S1M1w=
 github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
 github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=

+ 84 - 5
test/mux_test.go

@@ -8,19 +8,31 @@ import (
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
+
+	"github.com/gofrs/uuid"
 )
 
+var muxProtocols = []mux.Protocol{
+	mux.ProtocolYAMux,
+	mux.ProtocolSMux,
+}
+
 func TestShadowsocksMux(t *testing.T) {
-	for _, protocol := range []mux.Protocol{
-		mux.ProtocolYAMux,
-		mux.ProtocolSMux,
-	} {
+	for _, protocol := range muxProtocols {
 		t.Run(protocol.String(), func(t *testing.T) {
 			testShadowsocksMux(t, protocol.String())
 		})
 	}
 }
 
+func TestVMessMux(t *testing.T) {
+	for _, protocol := range muxProtocols {
+		t.Run(protocol.String(), func(t *testing.T) {
+			testVMessMux(t, protocol.String())
+		})
+	}
+}
+
 func testShadowsocksMux(t *testing.T, protocol string) {
 	method := shadowaead_2022.List[0]
 	password := mkBase64(t, 16)
@@ -65,7 +77,7 @@ func testShadowsocksMux(t *testing.T, protocol string) {
 					},
 					Method:   method,
 					Password: password,
-					Multiplex: &option.MultiplexOptions{
+					MultiplexOptions: &option.MultiplexOptions{
 						Enabled:  true,
 						Protocol: protocol,
 					},
@@ -85,3 +97,70 @@ func testShadowsocksMux(t *testing.T, protocol string) {
 	})
 	testSuit(t, clientPort, testPort)
 }
+
+func testVMessMux(t *testing.T, protocol string) {
+	user, _ := uuid.NewV4()
+	startInstance(t, option.Options{
+		Log: &option.LogOptions{
+			Level: "trace",
+		},
+		Inbounds: []option.Inbound{
+			{
+				Type: C.TypeMixed,
+				Tag:  "mixed-in",
+				MixedOptions: option.HTTPMixedInboundOptions{
+					ListenOptions: option.ListenOptions{
+						Listen:     option.ListenAddress(netip.IPv4Unspecified()),
+						ListenPort: clientPort,
+					},
+				},
+			},
+			{
+				Type: C.TypeVMess,
+				VMessOptions: option.VMessInboundOptions{
+					ListenOptions: option.ListenOptions{
+						Listen:     option.ListenAddress(netip.IPv4Unspecified()),
+						ListenPort: serverPort,
+					},
+					Users: []option.VMessUser{
+						{
+							UUID: user.String(),
+						},
+					},
+				},
+			},
+		},
+		Outbounds: []option.Outbound{
+			{
+				Type: C.TypeDirect,
+			},
+			{
+				Type: C.TypeVMess,
+				Tag:  "vmess-out",
+				VMessOptions: option.VMessOutboundOptions{
+					ServerOptions: option.ServerOptions{
+						Server:     "127.0.0.1",
+						ServerPort: serverPort,
+					},
+					Security: "auto",
+					UUID:     user.String(),
+					MultiplexOptions: &option.MultiplexOptions{
+						Enabled:  true,
+						Protocol: protocol,
+					},
+				},
+			},
+		},
+		Route: &option.RouteOptions{
+			Rules: []option.Rule{
+				{
+					DefaultOptions: option.DefaultRule{
+						Inbound:  []string{"mixed-in"},
+						Outbound: "vmess-out",
+					},
+				},
+			},
+		},
+	})
+	testSuit(t, clientPort, testPort)
+}