From 0467b2a68d708b5e0b3be4270ba3ddd91e10ef3d Mon Sep 17 00:00:00 2001 From: gVisor bot Date: Wed, 11 Jan 2023 22:01:15 +0800 Subject: [PATCH] feat: VLESS support packet encodings (#334) * adjust: Do not use XTLS on H2 connections * feat: VLESS support XUDP fullcone NAT * fix: VLESS with PacketAddr does not work * fix: VLESS XUDP crash --- adapter/outbound/trojan.go | 25 ++++++------- adapter/outbound/vless.go | 77 +++++++++++++++++++++++++++----------- transport/gun/gun_xtls.go | 60 ----------------------------- transport/vless/conn.go | 21 ++++++----- transport/vless/vless.go | 7 +++- transport/vless/xtls.go | 6 +-- 6 files changed, 87 insertions(+), 109 deletions(-) delete mode 100644 transport/gun/gun_xtls.go diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index 698db598..e7928b50 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -4,12 +4,12 @@ import ( "context" "crypto/tls" "fmt" - tlsC "github.com/Dreamacro/clash/component/tls" "net" "net/http" "strconv" "github.com/Dreamacro/clash/component/dialer" + tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/gun" "github.com/Dreamacro/clash/transport/trojan" @@ -219,13 +219,16 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { Fingerprint: option.Fingerprint, } - if option.Network != "ws" && len(option.Flow) >= 16 { - option.Flow = option.Flow[:16] - switch option.Flow { - case vless.XRO, vless.XRD, vless.XRS: - tOption.Flow = option.Flow - default: - return nil, fmt.Errorf("unsupported xtls flow type: %s", option.Flow) + switch option.Network { + case "", "tcp": + if len(option.Flow) >= 16 { + option.Flow = option.Flow[:16] + switch option.Flow { + case vless.XRO, vless.XRD, vless.XRS: + tOption.Flow = option.Flow + default: + return nil, fmt.Errorf("unsupported xtls flow type: %s", option.Flow) + } } } @@ -273,11 +276,7 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { } } - if t.option.Flow != "" { - t.transport = gun.NewHTTP2XTLSClient(dialFn, tlsConfig) - } else { - t.transport = gun.NewHTTP2Client(dialFn, tlsConfig) - } + t.transport = gun.NewHTTP2Client(dialFn, tlsConfig) t.gunTLSConfig = tlsConfig t.gunConfig = &gun.Config{ diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index 6004eb6b..e9e382c4 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -12,6 +12,10 @@ import ( "strconv" "sync" + vmessSing "github.com/sagernet/sing-vmess" + "github.com/sagernet/sing-vmess/packetaddr" + M "github.com/sagernet/sing/common/metadata" + "github.com/Dreamacro/clash/common/convert" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/resolver" @@ -49,6 +53,9 @@ type VlessOption struct { FlowShow bool `proxy:"flow-show,omitempty"` TLS bool `proxy:"tls,omitempty"` UDP bool `proxy:"udp,omitempty"` + PacketAddr bool `proxy:"packet-addr,omitempty"` + XUDP bool `proxy:"xudp,omitempty"` + PacketEncoding string `proxy:"packet-encoding,omitempty"` Network string `proxy:"network,omitempty"` HTTPOpts HTTPOptions `proxy:"http-opts,omitempty"` HTTP2Opts HTTP2Options `proxy:"h2-opts,omitempty"` @@ -137,11 +144,7 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { c, err = vmess.StreamH2Conn(c, h2Opts) case "grpc": - if v.isXTLSEnabled() { - c, err = gun.StreamGunWithXTLSConn(c, v.gunTLSConfig, v.gunConfig) - } else { - c, err = gun.StreamGunWithConn(c, v.gunTLSConfig, v.gunConfig) - } + c, err = gun.StreamGunWithConn(c, v.gunTLSConfig, v.gunConfig) default: // default tcp network // handle TLS And XTLS @@ -152,21 +155,17 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { return nil, err } - return v.client.StreamConn(c, parseVlessAddr(metadata)) + return v.client.StreamConn(c, parseVlessAddr(metadata, v.option.XUDP)) } func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error) { host, _, _ := net.SplitHostPort(v.addr) - if v.isXTLSEnabled() { + if v.isXTLSEnabled() && !isH2 { xtlsOpts := vless.XTLSConfig{ Host: host, SkipCertVerify: v.option.SkipCertVerify, - FingerPrint: v.option.Fingerprint, - } - - if isH2 { - xtlsOpts.NextProtos = []string{"h2"} + Fingerprint: v.option.Fingerprint, } if v.option.ServerName != "" { @@ -212,7 +211,7 @@ func (v *Vless) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d safeConnClose(c, err) }(c) - c, err = v.client.StreamConn(c, parseVlessAddr(metadata)) + c, err = v.client.StreamConn(c, parseVlessAddr(metadata, v.option.XUDP)) if err != nil { return nil, err } @@ -259,7 +258,15 @@ func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o safeConnClose(c, err) }(c) - c, err = v.client.StreamConn(c, parseVlessAddr(metadata)) + if v.option.PacketAddr { + packetAddrMetadata := *metadata // make a copy + packetAddrMetadata.Host = packetaddr.SeqPacketMagicAddress + packetAddrMetadata.DstPort = "443" + + c, err = v.client.StreamConn(c, parseVlessAddr(&packetAddrMetadata, false)) + } else { + c, err = v.client.StreamConn(c, parseVlessAddr(metadata, v.option.XUDP)) + } if err != nil { return nil, fmt.Errorf("new vless client error: %v", err) @@ -289,7 +296,15 @@ func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met safeConnClose(c, err) }(c) - c, err = v.StreamConn(c, metadata) + if v.option.PacketAddr { + packetAddrMetadata := *metadata // make a copy + packetAddrMetadata.Host = packetaddr.SeqPacketMagicAddress + packetAddrMetadata.DstPort = "443" + + c, err = v.StreamConn(c, &packetAddrMetadata) + } else { + c, err = v.StreamConn(c, metadata) + } if err != nil { return nil, fmt.Errorf("new vless client error: %v", err) @@ -305,6 +320,17 @@ func (v *Vless) SupportWithDialer() bool { // ListenPacketOnStreamConn implements C.ProxyAdapter func (v *Vless) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { + if v.option.XUDP { + return newPacketConn(&threadSafePacketConn{ + PacketConn: vmessSing.NewXUDPConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), + }, v), nil + } else if v.option.PacketAddr { + return newPacketConn(&threadSafePacketConn{ + PacketConn: packetaddr.NewConn(&vlessPacketConn{ + Conn: c, rAddr: metadata.UDPAddr(), + }, M.ParseSocksaddr(metadata.RemoteAddress())), + }, v), nil + } return newPacketConn(&vlessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil } @@ -313,7 +339,7 @@ func (v *Vless) SupportUOT() bool { return true } -func parseVlessAddr(metadata *C.Metadata) *vless.DstAddr { +func parseVlessAddr(metadata *C.Metadata, xudp bool) *vless.DstAddr { var addrType byte var addr []byte switch metadata.AddrType() { @@ -337,7 +363,8 @@ func parseVlessAddr(metadata *C.Metadata) *vless.DstAddr { UDP: metadata.NetWork == C.UDP, AddrType: addrType, Addr: addr, - Port: uint(port), + Port: uint16(port), + Mux: metadata.NetWork == C.UDP && xudp, } } @@ -466,6 +493,16 @@ func NewVless(option VlessOption) (*Vless, error) { option: &option, } + switch option.PacketEncoding { + case "packetaddr", "packet": + option.PacketAddr = true + case "xudp": + option.XUDP = true + } + if option.XUDP { + option.PacketAddr = false + } + switch option.Network { case "h2": if len(option.HTTP2Opts.Host) == 0 { @@ -498,11 +535,7 @@ func NewVless(option VlessOption) (*Vless, error) { v.gunTLSConfig = tlsConfig v.gunConfig = gunConfig - if v.isXTLSEnabled() { - v.transport = gun.NewHTTP2XTLSClient(dialFn, tlsConfig) - } else { - v.transport = gun.NewHTTP2Client(dialFn, tlsConfig) - } + v.transport = gun.NewHTTP2Client(dialFn, tlsConfig) } return v, nil diff --git a/transport/gun/gun_xtls.go b/transport/gun/gun_xtls.go deleted file mode 100644 index 0d110727..00000000 --- a/transport/gun/gun_xtls.go +++ /dev/null @@ -1,60 +0,0 @@ -// Modified from: https://github.com/Qv2ray/gun-lite -// License: MIT - -package gun - -import ( - "crypto/tls" - "fmt" - "net" - - xtls "github.com/xtls/go" - "golang.org/x/net/http2" -) - -func NewHTTP2XTLSClient(dialFn DialFn, tlsConfig *tls.Config) *TransportWrap { - wrap := TransportWrap{} - dialFunc := func(network, addr string, cfg *tls.Config) (net.Conn, error) { - pconn, err := dialFn(network, addr) - if err != nil { - return nil, err - } - - wrap.remoteAddr = pconn.RemoteAddr() - xtlsConfig := &xtls.Config{ - InsecureSkipVerify: cfg.InsecureSkipVerify, - ServerName: cfg.ServerName, - } - - cn := xtls.Client(pconn, xtlsConfig) - if err := cn.Handshake(); err != nil { - pconn.Close() - return nil, err - } - state := cn.ConnectionState() - if p := state.NegotiatedProtocol; p != http2.NextProtoTLS { - cn.Close() - return nil, fmt.Errorf("http2: unexpected ALPN protocol %s, want %s", p, http2.NextProtoTLS) - } - return cn, nil - } - - wrap.Transport = &http2.Transport{ - DialTLS: dialFunc, - TLSClientConfig: tlsConfig, - AllowHTTP: false, - DisableCompression: true, - PingTimeout: 0, - } - - return &wrap -} - -func StreamGunWithXTLSConn(conn net.Conn, tlsConfig *tls.Config, cfg *Config) (net.Conn, error) { - dialFn := func(network, addr string) (net.Conn, error) { - return conn, nil - } - - transport := NewHTTP2XTLSClient(dialFn, tlsConfig) - return StreamGunWithTransport(transport, cfg) -} diff --git a/transport/vless/conn.go b/transport/vless/conn.go index e6e6e34c..5ee69611 100644 --- a/transport/vless/conn.go +++ b/transport/vless/conn.go @@ -51,17 +51,20 @@ func (vc *Conn) sendRequest() error { buf.WriteByte(0) // addon data length. 0 means no addon data } - // command - if vc.dst.UDP { - buf.WriteByte(CommandUDP) + if vc.dst.Mux { + buf.WriteByte(CommandMux) } else { - buf.WriteByte(CommandTCP) - } + if vc.dst.UDP { + buf.WriteByte(CommandUDP) + } else { + buf.WriteByte(CommandTCP) + } - // Port AddrType Addr - binary.Write(buf, binary.BigEndian, uint16(vc.dst.Port)) - buf.WriteByte(vc.dst.AddrType) - buf.Write(vc.dst.Addr) + // Port AddrType Addr + binary.Write(buf, binary.BigEndian, vc.dst.Port) + buf.WriteByte(vc.dst.AddrType) + buf.Write(vc.dst.Addr) + } _, err := vc.Conn.Write(buf.Bytes()) return err diff --git a/transport/vless/vless.go b/transport/vless/vless.go index 5afe2c3e..4b101703 100644 --- a/transport/vless/vless.go +++ b/transport/vless/vless.go @@ -1,9 +1,10 @@ package vless import ( - "github.com/Dreamacro/clash/common/utils" "net" + "github.com/Dreamacro/clash/common/utils" + "github.com/gofrs/uuid" ) @@ -19,6 +20,7 @@ const ( const ( CommandTCP byte = 1 CommandUDP byte = 2 + CommandMux byte = 3 ) // Addr types @@ -33,7 +35,8 @@ type DstAddr struct { UDP bool AddrType byte Addr []byte - Port uint + Port uint16 + Mux bool // currently used for XUDP only } // Client is vless connection generator diff --git a/transport/vless/xtls.go b/transport/vless/xtls.go index 8ef4b44e..ab8248af 100644 --- a/transport/vless/xtls.go +++ b/transport/vless/xtls.go @@ -12,7 +12,7 @@ import ( type XTLSConfig struct { Host string SkipCertVerify bool - FingerPrint string + Fingerprint string NextProtos []string } @@ -22,11 +22,11 @@ func StreamXTLSConn(conn net.Conn, cfg *XTLSConfig) (net.Conn, error) { InsecureSkipVerify: cfg.SkipCertVerify, NextProtos: cfg.NextProtos, } - if len(cfg.FingerPrint) == 0 { + if len(cfg.Fingerprint) == 0 { xtlsConfig = tlsC.GetGlobalFingerprintXTLCConfig(xtlsConfig) } else { var err error - if xtlsConfig, err = tlsC.GetSpecifiedFingerprintXTLSConfig(xtlsConfig, cfg.FingerPrint); err != nil { + if xtlsConfig, err = tlsC.GetSpecifiedFingerprintXTLSConfig(xtlsConfig, cfg.Fingerprint); err != nil { return nil, err } }