From 5911d62fbaa7907779e2657ff26d1cb6dec2c1b4 Mon Sep 17 00:00:00 2001 From: gVisor bot Date: Tue, 29 Mar 2022 23:50:41 +0800 Subject: [PATCH] Chore: adjust VLESS --- README.md | 14 ++- adapter/outbound/vless.go | 141 +++++++++++++++++------ listener/tun/device/tun/tun_wireguard.go | 1 - test/config/vless-ws.json | 35 ++++++ test/config/vless-xtls.json | 4 +- test/vless_test.go | 40 ++++++- transport/vless/vless.go | 9 -- 7 files changed, 193 insertions(+), 51 deletions(-) create mode 100644 test/config/vless-ws.json diff --git a/README.md b/README.md index d7af8a41..e9821aaa 100644 --- a/README.md +++ b/README.md @@ -132,14 +132,24 @@ Support outbound transport protocol `VLESS`. The XTLS only support TCP transport by the XRAY-CORE. ```yaml proxies: - - name: "vless-tcp" + - name: "vless-tls" type: vless server: server port: 443 uuid: uuid network: tcp servername: example.com - # flow: xtls-rprx-direct # xtls-rprx-origin # enable XTLS + udp: true + # skip-cert-verify: true + - name: "vless-xtls" + type: vless + server: server + port: 443 + uuid: uuid + network: tcp + servername: example.com + flow: xtls-rprx-direct # or xtls-rprx-origin + # flow-show: true # print the XTLS direct log # udp: true # skip-cert-verify: true ``` diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index 876613b2..908c4de8 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -6,9 +6,11 @@ import ( "encoding/binary" "errors" "fmt" + "io" "net" "net/http" "strconv" + "sync" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/resolver" @@ -20,6 +22,11 @@ import ( "golang.org/x/net/http2" ) +const ( + // max packet length + maxLength = 1024 << 3 +) + type Vless struct { *Base client *vless.Client @@ -39,7 +46,6 @@ type VlessOption struct { UUID string `proxy:"uuid"` Flow string `proxy:"flow,omitempty"` FlowShow bool `proxy:"flow-show,omitempty"` - TLS bool `proxy:"tls,omitempty"` UDP bool `proxy:"udp,omitempty"` Network string `proxy:"network,omitempty"` HTTPOpts HTTPOptions `proxy:"http-opts,omitempty"` @@ -80,19 +86,19 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { wsOpts.Headers = header } - if v.option.TLS { - wsOpts.TLS = true - wsOpts.TLSConfig = &tls.Config{ - ServerName: host, - InsecureSkipVerify: v.option.SkipCertVerify, - NextProtos: []string{"http/1.1"}, - } - if v.option.ServerName != "" { - wsOpts.TLSConfig.ServerName = v.option.ServerName - } else if host := wsOpts.Headers.Get("Host"); host != "" { - wsOpts.TLSConfig.ServerName = host - } + wsOpts.TLS = true + wsOpts.TLSConfig = &tls.Config{ + MinVersion: tls.VersionTLS12, + ServerName: host, + InsecureSkipVerify: v.option.SkipCertVerify, + NextProtos: []string{"http/1.1"}, } + if v.option.ServerName != "" { + wsOpts.TLSConfig.ServerName = v.option.ServerName + } else if host := wsOpts.Headers.Get("Host"); host != "" { + wsOpts.TLSConfig.ServerName = host + } + c, err = vmess.StreamWebsocketConn(c, wsOpts) case "http": // readability first, so just copy default TLS logic @@ -160,7 +166,7 @@ func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error) return vless.StreamXTLSConn(conn, &xtlsOpts) - } else if v.option.TLS { + } else { tlsOpts := vmess.TLSConfig{ Host: host, SkipCertVerify: v.option.SkipCertVerify, @@ -176,8 +182,6 @@ func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error) return vmess.StreamTLSConn(conn, &tlsOpts) } - - return conn, nil } func (v *Vless) isXTLSEnabled() bool { @@ -282,30 +286,97 @@ func parseVlessAddr(metadata *C.Metadata) *vless.DstAddr { type vlessPacketConn struct { net.Conn - rAddr net.Addr - cache [2]byte + rAddr net.Addr + cache [2]byte + remain int + mux sync.Mutex } -func (uc *vlessPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) { - binary.BigEndian.PutUint16(uc.cache[:], uint16(len(b))) - _, _ = uc.Conn.Write(uc.cache[:]) - return uc.Conn.Write(b) -} - -func (uc *vlessPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { - n, err := uc.Conn.Read(uc.cache[:]) - if err != nil { - return n, uc.rAddr, err +func (vc *vlessPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) { + total := len(b) + if total == 0 { + return 0, nil } - n, err = uc.Conn.Read(b) - return n, uc.rAddr, err + + if total < maxLength { + return vc.writePacket(b) + } + + offset := 0 + for { + cursor := offset + maxLength + if cursor > total { + cursor = total + } + + n, err := vc.writePacket(b[offset:cursor]) + if err != nil { + return offset + n, err + } + + offset = cursor + if offset == total { + break + } + } + + return total, nil +} + +func (vc *vlessPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { + vc.mux.Lock() + defer vc.mux.Unlock() + + if vc.remain != 0 { + length := len(b) + if length > vc.remain { + length = vc.remain + } + + n, err := vc.Conn.Read(b[:length]) + if err != nil { + return 0, vc.rAddr, err + } + + vc.remain -= n + + return n, vc.rAddr, nil + } + + if _, err := vc.Conn.Read(b[:2]); err != nil { + return 0, vc.rAddr, err + } + + total := int(binary.BigEndian.Uint16(b[:2])) + if total == 0 { + return 0, vc.rAddr, nil + } + + length := len(b) + if length > total { + length = total + } + + if _, err := io.ReadFull(vc.Conn, b[:length]); err != nil { + return 0, vc.rAddr, errors.New("read packet error") + } + + vc.remain = total - length + + return length, vc.rAddr, nil +} + +func (vc *vlessPacketConn) writePacket(payload []byte) (int, error) { + binary.BigEndian.PutUint16(vc.cache[:], uint16(len(payload))) + + if _, err := vc.Conn.Write(vc.cache[:]); err != nil { + return 0, err + } + + return vc.Conn.Write(payload) } func NewVless(option VlessOption) (*Vless, error) { - if !option.TLS { - return nil, fmt.Errorf("TLS must be true with vless") - } - var addons *vless.Addons if option.Network != "ws" && len(option.Flow) >= 16 { option.Flow = option.Flow[:16] @@ -315,7 +386,7 @@ func NewVless(option VlessOption) (*Vless, error) { Flow: option.Flow, } default: - return nil, fmt.Errorf("unsupported vless flow type: %s", option.Flow) + return nil, fmt.Errorf("unsupported xtls flow type: %s", option.Flow) } } diff --git a/listener/tun/device/tun/tun_wireguard.go b/listener/tun/device/tun/tun_wireguard.go index 6e0ad15f..35008425 100644 --- a/listener/tun/device/tun/tun_wireguard.go +++ b/listener/tun/device/tun/tun_wireguard.go @@ -95,7 +95,6 @@ func (t *TUN) Write(packet []byte) (int, error) { return t.nt.Write(packet, t.offset) } - _ = t.cache[:t.offset] packet = append(t.cache[:t.offset], packet...) return t.nt.Write(packet, t.offset) diff --git a/test/config/vless-ws.json b/test/config/vless-ws.json new file mode 100644 index 00000000..9f3a5db8 --- /dev/null +++ b/test/config/vless-ws.json @@ -0,0 +1,35 @@ +{ + "inbounds": [ + { + "port": 10002, + "listen": "0.0.0.0", + "protocol": "vless", + "settings": { + "clients": [ + { + "id": "b831381d-6324-4d53-ad4f-8cda48b30811", + "level": 0, + "email": "ws@example.com" + } + ] + }, + "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/config/vless-xtls.json b/test/config/vless-xtls.json index b4381d61..1d352c3f 100644 --- a/test/config/vless-xtls.json +++ b/test/config/vless-xtls.json @@ -8,9 +8,9 @@ "clients": [ { "id": "b831381d-6324-4d53-ad4f-8cda48b30811", + "email": "xtls@example.com", "flow": "xtls-rprx-direct", - "level": 0, - "email": "love@example.com" + "level": 0 } ], "decryption": "none" diff --git a/test/vless_test.go b/test/vless_test.go index b5fbec86..3f516925 100644 --- a/test/vless_test.go +++ b/test/vless_test.go @@ -37,7 +37,6 @@ func TestClash_VlessTLS(t *testing.T) { Server: localIP.String(), Port: 10002, UUID: "b831381d-6324-4d53-ad4f-8cda48b30811", - TLS: true, SkipCertVerify: true, ServerName: "example.org", UDP: true, @@ -75,7 +74,6 @@ func TestClash_VlessXTLS(t *testing.T) { Server: localIP.String(), Port: 10002, UUID: "b831381d-6324-4d53-ad4f-8cda48b30811", - TLS: true, SkipCertVerify: true, ServerName: "example.org", UDP: true, @@ -89,3 +87,41 @@ func TestClash_VlessXTLS(t *testing.T) { time.Sleep(waitTime) testSuit(t, proxy) } + +func TestClash_VlessWS(t *testing.T) { + cfg := &container.Config{ + Image: ImageVmess, + ExposedPorts: defaultExposedPorts, + } + hostCfg := &container.HostConfig{ + PortBindings: defaultPortBindings, + Binds: []string{ + fmt.Sprintf("%s:/etc/v2ray/config.json", C.Path.Resolve("vless-ws.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, "vless-ws") + if err != nil { + assert.FailNow(t, err.Error()) + } + defer cleanContainer(id) + + proxy, err := outbound.NewVless(outbound.VlessOption{ + Name: "vless", + Server: localIP.String(), + Port: 10002, + UUID: "b831381d-6324-4d53-ad4f-8cda48b30811", + SkipCertVerify: true, + ServerName: "example.org", + Network: "ws", + UDP: true, + }) + if err != nil { + assert.FailNow(t, err.Error()) + } + + time.Sleep(waitTime) + testSuit(t, proxy) +} diff --git a/transport/vless/vless.go b/transport/vless/vless.go index 458f54de..bb0ce881 100644 --- a/transport/vless/vless.go +++ b/transport/vless/vless.go @@ -35,15 +35,6 @@ type DstAddr struct { Port uint } -// Config of vless -type Config struct { - UUID string - AlterID uint16 - Security string - Port string - HostName string -} - // Client is vless connection generator type Client struct { uuid *uuid.UUID