diff --git a/test/config/vmess-ws-tls-zero.json b/test/config/vmess-ws-tls-zero.json new file mode 100644 index 0000000..4ac6f14 --- /dev/null +++ b/test/config/vmess-ws-tls-zero.json @@ -0,0 +1,35 @@ +{ + "inbounds": [ + { + "port": 10002, + "listen": "0.0.0.0", + "protocol": "vmess", + "settings": { + "clients": [ + { + "id": "b831381d-6324-4d53-ad4f-8cda48b30811", + "alterId": 0, + "security": "zero" + } + ] + }, + "streamSettings": { + "network": "ws", + "security": "tls", + "tlsSettings": { + "certificates": [ + { + "certificateFile": "/etc/ssl/v2ray/fullchain.pem", + "keyFile": "/etc/ssl/v2ray/privkey.pem" + } + ] + } + } + } + ], + "outbounds": [ + { + "protocol": "freedom" + } + ] +} \ No newline at end of file diff --git a/test/vmess_test.go b/test/vmess_test.go index 3e8d275..a7f40f4 100644 --- a/test/vmess_test.go +++ b/test/vmess_test.go @@ -18,6 +18,8 @@ func TestClash_Vmess(t *testing.T) { cfg := &container.Config{ Image: ImageVmess, ExposedPorts: defaultExposedPorts, + Entrypoint: []string{"/usr/bin/v2ray"}, + Cmd: []string{"run", "-c", "/etc/v2ray/config.json"}, } hostCfg := &container.HostConfig{ PortBindings: defaultPortBindings, @@ -49,6 +51,8 @@ func TestClash_VmessTLS(t *testing.T) { cfg := &container.Config{ Image: ImageVmess, ExposedPorts: defaultExposedPorts, + Entrypoint: []string{"/usr/bin/v2ray"}, + Cmd: []string{"run", "-c", "/etc/v2ray/config.json"}, } hostCfg := &container.HostConfig{ PortBindings: defaultPortBindings, @@ -86,6 +90,8 @@ func TestClash_VmessHTTP2(t *testing.T) { cfg := &container.Config{ Image: ImageVmess, ExposedPorts: defaultExposedPorts, + Entrypoint: []string{"/usr/bin/v2ray"}, + Cmd: []string{"run", "-c", "/etc/v2ray/config.json"}, } hostCfg := &container.HostConfig{ PortBindings: defaultPortBindings, @@ -128,6 +134,8 @@ func TestClash_VmessHTTP(t *testing.T) { cfg := &container.Config{ Image: ImageVmess, ExposedPorts: defaultExposedPorts, + Entrypoint: []string{"/usr/bin/v2ray"}, + Cmd: []string{"run", "-c", "/etc/v2ray/config.json"}, } hostCfg := &container.HostConfig{ PortBindings: defaultPortBindings, @@ -178,6 +186,8 @@ func TestClash_VmessWebsocket(t *testing.T) { cfg := &container.Config{ Image: ImageVmess, ExposedPorts: defaultExposedPorts, + Entrypoint: []string{"/usr/bin/v2ray"}, + Cmd: []string{"run", "-c", "/etc/v2ray/config.json"}, } hostCfg := &container.HostConfig{ PortBindings: defaultPortBindings, @@ -211,6 +221,8 @@ func TestClash_VmessWebsocketTLS(t *testing.T) { cfg := &container.Config{ Image: ImageVmess, ExposedPorts: defaultExposedPorts, + Entrypoint: []string{"/usr/bin/v2ray"}, + Cmd: []string{"run", "-c", "/etc/v2ray/config.json"}, } hostCfg := &container.HostConfig{ PortBindings: defaultPortBindings, @@ -221,7 +233,7 @@ func TestClash_VmessWebsocketTLS(t *testing.T) { }, } - id, err := startContainer(cfg, hostCfg, "vmess-ws") + id, err := startContainer(cfg, hostCfg, "vmess-ws-tls") require.NoError(t, err) t.Cleanup(func() { cleanContainer(id) @@ -244,10 +256,51 @@ func TestClash_VmessWebsocketTLS(t *testing.T) { testSuit(t, proxy) } +func TestClash_VmessWebsocketTLSZero(t *testing.T) { + cfg := &container.Config{ + Image: ImageVmess, + ExposedPorts: defaultExposedPorts, + Entrypoint: []string{"/usr/bin/v2ray"}, + Cmd: []string{"run", "-c", "/etc/v2ray/config.json"}, + } + hostCfg := &container.HostConfig{ + PortBindings: defaultPortBindings, + Binds: []string{ + fmt.Sprintf("%s:/etc/v2ray/config.json", C.Path.Resolve("vmess-ws-tls-zero.json")), + fmt.Sprintf("%s:/etc/ssl/v2ray/fullchain.pem", C.Path.Resolve("example.org.pem")), + fmt.Sprintf("%s:/etc/ssl/v2ray/privkey.pem", C.Path.Resolve("example.org-key.pem")), + }, + } + + id, err := startContainer(cfg, hostCfg, "vmess-ws-tls-zero") + require.NoError(t, err) + t.Cleanup(func() { + cleanContainer(id) + }) + + proxy, err := outbound.NewVmess(outbound.VmessOption{ + Name: "vmess", + Server: localIP.String(), + Port: 10002, + UUID: "b831381d-6324-4d53-ad4f-8cda48b30811", + Cipher: "zero", + Network: "ws", + TLS: true, + SkipCertVerify: true, + UDP: true, + }) + require.NoError(t, err) + + time.Sleep(waitTime) + testSuit(t, proxy) +} + func TestClash_VmessGrpc(t *testing.T) { cfg := &container.Config{ Image: ImageVmess, ExposedPorts: defaultExposedPorts, + Entrypoint: []string{"/usr/bin/v2ray"}, + Cmd: []string{"run", "-c", "/etc/v2ray/config.json"}, } hostCfg := &container.HostConfig{ PortBindings: defaultPortBindings, @@ -289,6 +342,8 @@ func TestClash_VmessWebsocket0RTT(t *testing.T) { cfg := &container.Config{ Image: ImageVmess, ExposedPorts: defaultExposedPorts, + Entrypoint: []string{"/usr/bin/v2ray"}, + Cmd: []string{"run", "-c", "/etc/v2ray/config.json"}, } hostCfg := &container.HostConfig{ PortBindings: defaultPortBindings, @@ -366,6 +421,8 @@ func Benchmark_Vmess(b *testing.B) { cfg := &container.Config{ Image: ImageVmess, ExposedPorts: defaultExposedPorts, + Entrypoint: []string{"/usr/bin/v2ray"}, + Cmd: []string{"run", "-c", "/etc/v2ray/config.json"}, } hostCfg := &container.HostConfig{ PortBindings: defaultPortBindings, diff --git a/transport/vmess/conn.go b/transport/vmess/conn.go index cc3155e..ccc9654 100644 --- a/transport/vmess/conn.go +++ b/transport/vmess/conn.go @@ -35,6 +35,7 @@ type Conn struct { respBodyKey []byte respV byte security byte + option byte isAead bool received bool @@ -74,7 +75,7 @@ func (vc *Conn) sendRequest() error { buf.Write(vc.reqBodyIV[:]) buf.Write(vc.reqBodyKey[:]) buf.WriteByte(vc.respV) - buf.WriteByte(OptionChunkStream) + buf.WriteByte(vc.option) p := rand.Intn(16) // P Sec Reserve Cmd @@ -206,6 +207,7 @@ func newConn(conn net.Conn, id *ID, dst *DstAddr, security Security, isAead bool copy(reqBodyIV[:], randBytes[:16]) copy(reqBodyKey[:], randBytes[16:32]) respV := randBytes[32] + option := OptionChunkStream var ( respBodyKey []byte @@ -227,6 +229,16 @@ func newConn(conn net.Conn, id *ID, dst *DstAddr, security Security, isAead bool var writer io.Writer var reader io.Reader switch security { + case SecurityZero: + security = SecurityNone + if !dst.UDP { + reader = conn + writer = conn + option = 0 + } else { + reader = newChunkReader(conn) + writer = newChunkWriter(conn) + } case SecurityNone: reader = newChunkReader(conn) writer = newChunkWriter(conn) @@ -267,6 +279,7 @@ func newConn(conn net.Conn, id *ID, dst *DstAddr, security Security, isAead bool reader: reader, writer: writer, security: security, + option: option, isAead: isAead, } if err := c.sendRequest(); err != nil { diff --git a/transport/vmess/vmess.go b/transport/vmess/vmess.go index cb0731d..3caab9f 100644 --- a/transport/vmess/vmess.go +++ b/transport/vmess/vmess.go @@ -26,15 +26,9 @@ const ( SecurityAES128GCM Security = 3 SecurityCHACHA20POLY1305 Security = 4 SecurityNone Security = 5 + SecurityZero Security = 6 ) -// CipherMapping return -var CipherMapping = map[string]byte{ - "none": SecurityNone, - "aes-128-gcm": SecurityAES128GCM, - "chacha20-poly1305": SecurityCHACHA20POLY1305, -} - // Command types const ( CommandTCP byte = 1 @@ -95,6 +89,8 @@ func NewClient(config Config) (*Client, error) { security = SecurityCHACHA20POLY1305 case "none": security = SecurityNone + case "zero": + security = SecurityZero case "auto": security = SecurityCHACHA20POLY1305 if runtime.GOARCH == "amd64" || runtime.GOARCH == "s390x" || runtime.GOARCH == "arm64" {