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:
parent
0069513780
commit
be6142aa43
6 changed files with 87 additions and 109 deletions
|
@ -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{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue