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
This commit is contained in:
Hellojack 2023-01-11 22:01:15 +08:00 committed by GitHub
parent 0069513780
commit be6142aa43
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 87 additions and 109 deletions

View file

@ -4,12 +4,12 @@ import (
"context" "context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
tlsC "github.com/Dreamacro/clash/component/tls"
"net" "net"
"net/http" "net/http"
"strconv" "strconv"
"github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/dialer"
tlsC "github.com/Dreamacro/clash/component/tls"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/gun" "github.com/Dreamacro/clash/transport/gun"
"github.com/Dreamacro/clash/transport/trojan" "github.com/Dreamacro/clash/transport/trojan"
@ -219,13 +219,16 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
Fingerprint: option.Fingerprint, Fingerprint: option.Fingerprint,
} }
if option.Network != "ws" && len(option.Flow) >= 16 { switch option.Network {
option.Flow = option.Flow[:16] case "", "tcp":
switch option.Flow { if len(option.Flow) >= 16 {
case vless.XRO, vless.XRD, vless.XRS: option.Flow = option.Flow[:16]
tOption.Flow = option.Flow switch option.Flow {
default: case vless.XRO, vless.XRD, vless.XRS:
return nil, fmt.Errorf("unsupported xtls flow type: %s", option.Flow) 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.NewHTTP2Client(dialFn, tlsConfig)
t.transport = gun.NewHTTP2XTLSClient(dialFn, tlsConfig)
} else {
t.transport = gun.NewHTTP2Client(dialFn, tlsConfig)
}
t.gunTLSConfig = tlsConfig t.gunTLSConfig = tlsConfig
t.gunConfig = &gun.Config{ t.gunConfig = &gun.Config{

View file

@ -12,6 +12,10 @@ import (
"strconv" "strconv"
"sync" "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/common/convert"
"github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/resolver" "github.com/Dreamacro/clash/component/resolver"
@ -49,6 +53,9 @@ type VlessOption struct {
FlowShow bool `proxy:"flow-show,omitempty"` FlowShow bool `proxy:"flow-show,omitempty"`
TLS bool `proxy:"tls,omitempty"` TLS bool `proxy:"tls,omitempty"`
UDP bool `proxy:"udp,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"` Network string `proxy:"network,omitempty"`
HTTPOpts HTTPOptions `proxy:"http-opts,omitempty"` HTTPOpts HTTPOptions `proxy:"http-opts,omitempty"`
HTTP2Opts HTTP2Options `proxy:"h2-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) c, err = vmess.StreamH2Conn(c, h2Opts)
case "grpc": case "grpc":
if v.isXTLSEnabled() { c, err = gun.StreamGunWithConn(c, v.gunTLSConfig, v.gunConfig)
c, err = gun.StreamGunWithXTLSConn(c, v.gunTLSConfig, v.gunConfig)
} else {
c, err = gun.StreamGunWithConn(c, v.gunTLSConfig, v.gunConfig)
}
default: default:
// default tcp network // default tcp network
// handle TLS And XTLS // 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 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) { func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error) {
host, _, _ := net.SplitHostPort(v.addr) host, _, _ := net.SplitHostPort(v.addr)
if v.isXTLSEnabled() { if v.isXTLSEnabled() && !isH2 {
xtlsOpts := vless.XTLSConfig{ xtlsOpts := vless.XTLSConfig{
Host: host, Host: host,
SkipCertVerify: v.option.SkipCertVerify, SkipCertVerify: v.option.SkipCertVerify,
FingerPrint: v.option.Fingerprint, Fingerprint: v.option.Fingerprint,
}
if isH2 {
xtlsOpts.NextProtos = []string{"h2"}
} }
if v.option.ServerName != "" { if v.option.ServerName != "" {
@ -212,7 +211,7 @@ func (v *Vless) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d
safeConnClose(c, err) safeConnClose(c, err)
}(c) }(c)
c, err = v.client.StreamConn(c, parseVlessAddr(metadata)) c, err = v.client.StreamConn(c, parseVlessAddr(metadata, v.option.XUDP))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -259,7 +258,15 @@ func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
safeConnClose(c, err) safeConnClose(c, err)
}(c) }(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 { if err != nil {
return nil, fmt.Errorf("new vless client error: %v", err) 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) safeConnClose(c, err)
}(c) }(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 { if err != nil {
return nil, fmt.Errorf("new vless client error: %v", err) return nil, fmt.Errorf("new vless client error: %v", err)
@ -305,6 +320,17 @@ func (v *Vless) SupportWithDialer() bool {
// ListenPacketOnStreamConn implements C.ProxyAdapter // ListenPacketOnStreamConn implements C.ProxyAdapter
func (v *Vless) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { 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 return newPacketConn(&vlessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil
} }
@ -313,7 +339,7 @@ func (v *Vless) SupportUOT() bool {
return true return true
} }
func parseVlessAddr(metadata *C.Metadata) *vless.DstAddr { func parseVlessAddr(metadata *C.Metadata, xudp bool) *vless.DstAddr {
var addrType byte var addrType byte
var addr []byte var addr []byte
switch metadata.AddrType() { switch metadata.AddrType() {
@ -337,7 +363,8 @@ func parseVlessAddr(metadata *C.Metadata) *vless.DstAddr {
UDP: metadata.NetWork == C.UDP, UDP: metadata.NetWork == C.UDP,
AddrType: addrType, AddrType: addrType,
Addr: addr, 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, 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 { switch option.Network {
case "h2": case "h2":
if len(option.HTTP2Opts.Host) == 0 { if len(option.HTTP2Opts.Host) == 0 {
@ -498,11 +535,7 @@ func NewVless(option VlessOption) (*Vless, error) {
v.gunTLSConfig = tlsConfig v.gunTLSConfig = tlsConfig
v.gunConfig = gunConfig v.gunConfig = gunConfig
if v.isXTLSEnabled() { v.transport = gun.NewHTTP2Client(dialFn, tlsConfig)
v.transport = gun.NewHTTP2XTLSClient(dialFn, tlsConfig)
} else {
v.transport = gun.NewHTTP2Client(dialFn, tlsConfig)
}
} }
return v, nil return v, nil

View file

@ -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)
}

View file

@ -51,17 +51,20 @@ func (vc *Conn) sendRequest() error {
buf.WriteByte(0) // addon data length. 0 means no addon data buf.WriteByte(0) // addon data length. 0 means no addon data
} }
// command if vc.dst.Mux {
if vc.dst.UDP { buf.WriteByte(CommandMux)
buf.WriteByte(CommandUDP)
} else { } else {
buf.WriteByte(CommandTCP) if vc.dst.UDP {
} buf.WriteByte(CommandUDP)
} else {
buf.WriteByte(CommandTCP)
}
// Port AddrType Addr // Port AddrType Addr
binary.Write(buf, binary.BigEndian, uint16(vc.dst.Port)) binary.Write(buf, binary.BigEndian, vc.dst.Port)
buf.WriteByte(vc.dst.AddrType) buf.WriteByte(vc.dst.AddrType)
buf.Write(vc.dst.Addr) buf.Write(vc.dst.Addr)
}
_, err := vc.Conn.Write(buf.Bytes()) _, err := vc.Conn.Write(buf.Bytes())
return err return err

View file

@ -1,9 +1,10 @@
package vless package vless
import ( import (
"github.com/Dreamacro/clash/common/utils"
"net" "net"
"github.com/Dreamacro/clash/common/utils"
"github.com/gofrs/uuid" "github.com/gofrs/uuid"
) )
@ -19,6 +20,7 @@ const (
const ( const (
CommandTCP byte = 1 CommandTCP byte = 1
CommandUDP byte = 2 CommandUDP byte = 2
CommandMux byte = 3
) )
// Addr types // Addr types
@ -33,7 +35,8 @@ type DstAddr struct {
UDP bool UDP bool
AddrType byte AddrType byte
Addr []byte Addr []byte
Port uint Port uint16
Mux bool // currently used for XUDP only
} }
// Client is vless connection generator // Client is vless connection generator

View file

@ -12,7 +12,7 @@ import (
type XTLSConfig struct { type XTLSConfig struct {
Host string Host string
SkipCertVerify bool SkipCertVerify bool
FingerPrint string Fingerprint string
NextProtos []string NextProtos []string
} }
@ -22,11 +22,11 @@ func StreamXTLSConn(conn net.Conn, cfg *XTLSConfig) (net.Conn, error) {
InsecureSkipVerify: cfg.SkipCertVerify, InsecureSkipVerify: cfg.SkipCertVerify,
NextProtos: cfg.NextProtos, NextProtos: cfg.NextProtos,
} }
if len(cfg.FingerPrint) == 0 { if len(cfg.Fingerprint) == 0 {
xtlsConfig = tlsC.GetGlobalFingerprintXTLCConfig(xtlsConfig) xtlsConfig = tlsC.GetGlobalFingerprintXTLCConfig(xtlsConfig)
} else { } else {
var err error 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 return nil, err
} }
} }