Merge remote-tracking branch 'Pro-Plus/with-tun' into Alpha
# Conflicts: # README.md # adapter/outbound/trojan.go # adapter/outbound/vless.go # transport/trojan/trojan.go
This commit is contained in:
commit
81c5a65f23
33 changed files with 569 additions and 327 deletions
|
@ -14,6 +14,7 @@ type Direct struct {
|
||||||
|
|
||||||
// DialContext implements C.ProxyAdapter
|
// DialContext implements C.ProxyAdapter
|
||||||
func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
||||||
|
opts = append(opts, dialer.WithDirect())
|
||||||
c, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress(), d.Base.DialOptions(opts...)...)
|
c, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress(), d.Base.DialOptions(opts...)...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -24,6 +25,7 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...
|
||||||
|
|
||||||
// ListenPacketContext implements C.ProxyAdapter
|
// ListenPacketContext implements C.ProxyAdapter
|
||||||
func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
||||||
|
opts = append(opts, dialer.WithDirect())
|
||||||
pc, err := dialer.ListenPacket(ctx, "udp", "", d.Base.DialOptions(opts...)...)
|
pc, err := dialer.ListenPacket(ctx, "udp", "", d.Base.DialOptions(opts...)...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
xtls "github.com/xtls/go"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -13,6 +12,7 @@ import (
|
||||||
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"
|
||||||
|
"github.com/Dreamacro/clash/transport/vless"
|
||||||
|
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
)
|
)
|
||||||
|
@ -34,7 +34,6 @@ type TrojanOption struct {
|
||||||
Server string `proxy:"server"`
|
Server string `proxy:"server"`
|
||||||
Port int `proxy:"port"`
|
Port int `proxy:"port"`
|
||||||
Password string `proxy:"password"`
|
Password string `proxy:"password"`
|
||||||
Flow string `proxy:"flow,omitempty"`
|
|
||||||
ALPN []string `proxy:"alpn,omitempty"`
|
ALPN []string `proxy:"alpn,omitempty"`
|
||||||
SNI string `proxy:"sni,omitempty"`
|
SNI string `proxy:"sni,omitempty"`
|
||||||
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
|
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
|
||||||
|
@ -42,6 +41,8 @@ type TrojanOption struct {
|
||||||
Network string `proxy:"network,omitempty"`
|
Network string `proxy:"network,omitempty"`
|
||||||
GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"`
|
GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"`
|
||||||
WSOpts WSOptions `proxy:"ws-opts,omitempty"`
|
WSOpts WSOptions `proxy:"ws-opts,omitempty"`
|
||||||
|
Flow string `proxy:"flow,omitempty"`
|
||||||
|
FlowShow bool `proxy:"flow-show,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Trojan) plainStream(c net.Conn) (net.Conn, error) {
|
func (t *Trojan) plainStream(c net.Conn) (net.Conn, error) {
|
||||||
|
@ -83,19 +84,13 @@ func (t *Trojan) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
|
return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
|
||||||
}
|
}
|
||||||
var tc trojan.Command
|
|
||||||
if xtlsConn, ok := c.(*xtls.Conn); ok {
|
c, err = t.instance.PresetXTLSConn(c)
|
||||||
xtlsConn.RPRX = true
|
if err != nil {
|
||||||
if t.instance.GetFlow() == trojan.XRD {
|
return nil, err
|
||||||
xtlsConn.DirectMode = true
|
|
||||||
tc = trojan.CommandXRD
|
|
||||||
} else {
|
|
||||||
tc = trojan.CommandXRO
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
tc = trojan.CommandTCP
|
err = t.instance.WriteHeader(c, trojan.CommandTCP, serializesSocksAddr(metadata))
|
||||||
}
|
|
||||||
err = t.instance.WriteHeader(c, tc, serializesSocksAddr(metadata))
|
|
||||||
return c, err
|
return c, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,6 +103,12 @@ func (t *Trojan) DialContext(ctx context.Context, metadata *C.Metadata, opts ...
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c, err = t.instance.PresetXTLSConn(c)
|
||||||
|
if err != nil {
|
||||||
|
c.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if err = t.instance.WriteHeader(c, trojan.CommandTCP, serializesSocksAddr(metadata)); err != nil {
|
if err = t.instance.WriteHeader(c, trojan.CommandTCP, serializesSocksAddr(metadata)); err != nil {
|
||||||
c.Close()
|
c.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -170,12 +171,20 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
|
||||||
|
|
||||||
tOption := &trojan.Option{
|
tOption := &trojan.Option{
|
||||||
Password: option.Password,
|
Password: option.Password,
|
||||||
Flow: option.Flow,
|
|
||||||
ALPN: option.ALPN,
|
ALPN: option.ALPN,
|
||||||
ServerName: option.Server,
|
ServerName: option.Server,
|
||||||
SkipCertVerify: option.SkipCertVerify,
|
SkipCertVerify: option.SkipCertVerify,
|
||||||
ClientSessionCache: getClientSessionCache(),
|
FlowShow: option.FlowShow,
|
||||||
ClientXSessionCache: getClientXSessionCache(),
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if option.SNI != "" {
|
if option.SNI != "" {
|
||||||
|
@ -212,7 +221,12 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
|
||||||
ServerName: tOption.ServerName,
|
ServerName: tOption.ServerName,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.gunTLSConfig = tlsConfig
|
||||||
t.gunConfig = &gun.Config{
|
t.gunConfig = &gun.Config{
|
||||||
ServiceName: option.GrpcOpts.GrpcServiceName,
|
ServiceName: option.GrpcOpts.GrpcServiceName,
|
||||||
|
|
|
@ -67,7 +67,7 @@ func resolveUDPAddr(network, address string) (*net.UDPAddr, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ip, err := resolver.ResolveIP(host)
|
ip, err := resolver.ResolveProxyServerHost(host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -23,7 +24,7 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// max packet length
|
// max packet length
|
||||||
maxLength = 8192
|
maxLength = 1024 << 3
|
||||||
)
|
)
|
||||||
|
|
||||||
type Vless struct {
|
type Vless struct {
|
||||||
|
@ -45,13 +46,14 @@ type VlessOption struct {
|
||||||
UUID string `proxy:"uuid"`
|
UUID string `proxy:"uuid"`
|
||||||
Flow string `proxy:"flow,omitempty"`
|
Flow string `proxy:"flow,omitempty"`
|
||||||
FlowShow bool `proxy:"flow-show,omitempty"`
|
FlowShow bool `proxy:"flow-show,omitempty"`
|
||||||
TLS bool `proxy:"tls,omitempty"`
|
|
||||||
UDP bool `proxy:"udp,omitempty"`
|
UDP bool `proxy:"udp,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"`
|
||||||
GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"`
|
GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"`
|
||||||
WSOpts WSOptions `proxy:"ws-opts,omitempty"`
|
WSOpts WSOptions `proxy:"ws-opts,omitempty"`
|
||||||
|
WSPath string `proxy:"ws-path,omitempty"`
|
||||||
|
WSHeaders map[string]string `proxy:"ws-headers,omitempty"`
|
||||||
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
|
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
|
||||||
ServerName string `proxy:"servername,omitempty"`
|
ServerName string `proxy:"servername,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -62,7 +64,6 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
||||||
case "ws":
|
case "ws":
|
||||||
|
|
||||||
host, port, _ := net.SplitHostPort(v.addr)
|
host, port, _ := net.SplitHostPort(v.addr)
|
||||||
|
|
||||||
wsOpts := &vmess.WebsocketConfig{
|
wsOpts := &vmess.WebsocketConfig{
|
||||||
Host: host,
|
Host: host,
|
||||||
Port: port,
|
Port: port,
|
||||||
|
@ -79,9 +80,9 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
||||||
wsOpts.Headers = header
|
wsOpts.Headers = header
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.option.TLS {
|
|
||||||
wsOpts.TLS = true
|
wsOpts.TLS = true
|
||||||
wsOpts.TLSConfig = &tls.Config{
|
wsOpts.TLSConfig = &tls.Config{
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
ServerName: host,
|
ServerName: host,
|
||||||
InsecureSkipVerify: v.option.SkipCertVerify,
|
InsecureSkipVerify: v.option.SkipCertVerify,
|
||||||
NextProtos: []string{"http/1.1"},
|
NextProtos: []string{"http/1.1"},
|
||||||
|
@ -91,7 +92,6 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
||||||
} else if host := wsOpts.Headers.Get("Host"); host != "" {
|
} else if host := wsOpts.Headers.Get("Host"); host != "" {
|
||||||
wsOpts.TLSConfig.ServerName = host
|
wsOpts.TLSConfig.ServerName = host
|
||||||
}
|
}
|
||||||
}
|
|
||||||
c, err = vmess.StreamWebsocketConn(c, wsOpts)
|
c, err = vmess.StreamWebsocketConn(c, wsOpts)
|
||||||
case "http":
|
case "http":
|
||||||
// readability first, so just copy default TLS logic
|
// readability first, so just copy default TLS logic
|
||||||
|
@ -128,6 +128,7 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
||||||
c, err = gun.StreamGunWithConn(c, v.gunTLSConfig, v.gunConfig)
|
c, err = gun.StreamGunWithConn(c, v.gunTLSConfig, v.gunConfig)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
// default tcp network
|
||||||
// handle TLS And XTLS
|
// handle TLS And XTLS
|
||||||
c, err = v.streamTLSOrXTLSConn(c, false)
|
c, err = v.streamTLSOrXTLSConn(c, false)
|
||||||
}
|
}
|
||||||
|
@ -158,7 +159,7 @@ func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error)
|
||||||
|
|
||||||
return vless.StreamXTLSConn(conn, &xtlsOpts)
|
return vless.StreamXTLSConn(conn, &xtlsOpts)
|
||||||
|
|
||||||
} else if v.option.TLS {
|
} else {
|
||||||
tlsOpts := vmess.TLSConfig{
|
tlsOpts := vmess.TLSConfig{
|
||||||
Host: host,
|
Host: host,
|
||||||
SkipCertVerify: v.option.SkipCertVerify,
|
SkipCertVerify: v.option.SkipCertVerify,
|
||||||
|
@ -174,8 +175,6 @@ func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error)
|
||||||
|
|
||||||
return vmess.StreamTLSConn(conn, &tlsOpts)
|
return vmess.StreamTLSConn(conn, &tlsOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
return conn, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Vless) isXTLSEnabled() bool {
|
func (v *Vless) isXTLSEnabled() bool {
|
||||||
|
@ -213,7 +212,7 @@ func (v *Vless) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d
|
||||||
|
|
||||||
// ListenPacketContext implements C.ProxyAdapter
|
// ListenPacketContext implements C.ProxyAdapter
|
||||||
func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) {
|
func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) {
|
||||||
// vmess use stream-oriented udp with a special address, so we needs a net.UDPAddr
|
// vless use stream-oriented udp with a special address, so we needs a net.UDPAddr
|
||||||
if !metadata.Resolved() {
|
if !metadata.Resolved() {
|
||||||
ip, err := resolver.ResolveIP(metadata.Host)
|
ip, err := resolver.ResolveIP(metadata.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -269,7 +268,7 @@ func parseVlessAddr(metadata *C.Metadata) *vless.DstAddr {
|
||||||
copy(addr[1:], []byte(metadata.Host))
|
copy(addr[1:], []byte(metadata.Host))
|
||||||
}
|
}
|
||||||
|
|
||||||
port, _ := strconv.Atoi(metadata.DstPort)
|
port, _ := strconv.ParseUint(metadata.DstPort, 10, 16)
|
||||||
return &vless.DstAddr{
|
return &vless.DstAddr{
|
||||||
UDP: metadata.NetWork == C.UDP,
|
UDP: metadata.NetWork == C.UDP,
|
||||||
AddrType: addrType,
|
AddrType: addrType,
|
||||||
|
@ -283,43 +282,46 @@ type vlessPacketConn struct {
|
||||||
rAddr net.Addr
|
rAddr net.Addr
|
||||||
remain int
|
remain int
|
||||||
mux sync.Mutex
|
mux sync.Mutex
|
||||||
cache []byte
|
cache [2]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *vlessPacketConn) writePacket(b []byte, addr net.Addr) (int, error) {
|
func (c *vlessPacketConn) writePacket(payload []byte) (int, error) {
|
||||||
length := len(b)
|
binary.BigEndian.PutUint16(c.cache[:], uint16(len(payload)))
|
||||||
defer func() {
|
|
||||||
c.cache = c.cache[:0]
|
|
||||||
}()
|
|
||||||
c.cache = append(c.cache, byte(length>>8), byte(length))
|
|
||||||
c.cache = append(c.cache, b...)
|
|
||||||
n, err := c.Conn.Write(c.cache)
|
|
||||||
if n > 2 {
|
|
||||||
return n - 2, err
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if _, err := c.Conn.Write(c.cache[:]); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return c.Conn.Write(payload)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *vlessPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
func (c *vlessPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||||
if len(b) <= maxLength {
|
total := len(b)
|
||||||
return c.writePacket(b, addr)
|
if total == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if total <= maxLength {
|
||||||
|
return c.writePacket(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
offset := 0
|
offset := 0
|
||||||
total := len(b)
|
|
||||||
for offset < total {
|
for offset < total {
|
||||||
cursor := offset + maxLength
|
cursor := offset + maxLength
|
||||||
if cursor > total {
|
if cursor > total {
|
||||||
cursor = total
|
cursor = total
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err := c.writePacket(b[offset:cursor], addr)
|
n, err := c.writePacket(b[offset:cursor])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return offset + n, err
|
return offset + n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = cursor
|
offset = cursor
|
||||||
|
if offset == total {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return total, nil
|
return total, nil
|
||||||
|
@ -329,40 +331,45 @@ func (c *vlessPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||||
c.mux.Lock()
|
c.mux.Lock()
|
||||||
defer c.mux.Unlock()
|
defer c.mux.Unlock()
|
||||||
|
|
||||||
length := len(b)
|
|
||||||
if c.remain > 0 {
|
if c.remain > 0 {
|
||||||
|
length := len(b)
|
||||||
if c.remain < length {
|
if c.remain < length {
|
||||||
length = c.remain
|
length = c.remain
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err := c.Conn.Read(b[:length])
|
n, err := c.Conn.Read(b[:length])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, c.rAddr, err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.remain -= n
|
c.remain -= n
|
||||||
return n, c.rAddr, nil
|
return n, c.rAddr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var packetLength uint16
|
if _, err := c.Conn.Read(b[:2]); err != nil {
|
||||||
if err := binary.Read(c.Conn, binary.BigEndian, &packetLength); err != nil {
|
return 0, c.rAddr, err
|
||||||
return 0, nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
remain := int(packetLength)
|
total := int(binary.BigEndian.Uint16(b[:2]))
|
||||||
n, err := c.Conn.Read(b[:length])
|
if total == 0 {
|
||||||
remain -= n
|
return 0, c.rAddr, nil
|
||||||
if remain > 0 {
|
|
||||||
c.remain = remain
|
|
||||||
}
|
}
|
||||||
return n, c.rAddr, err
|
|
||||||
|
length := len(b)
|
||||||
|
if length > total {
|
||||||
|
length = total
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := io.ReadFull(c.Conn, b[:length]); err != nil {
|
||||||
|
return 0, c.rAddr, errors.New("read packet error")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.remain = total - length
|
||||||
|
|
||||||
|
return length, c.rAddr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewVless(option VlessOption) (*Vless, error) {
|
func NewVless(option VlessOption) (*Vless, error) {
|
||||||
if !option.TLS && option.Network == "grpc" {
|
|
||||||
return nil, fmt.Errorf("TLS must be true with vless-grpc")
|
|
||||||
}
|
|
||||||
|
|
||||||
var addons *vless.Addons
|
var addons *vless.Addons
|
||||||
if option.Network != "ws" && len(option.Flow) >= 16 {
|
if option.Network != "ws" && len(option.Flow) >= 16 {
|
||||||
option.Flow = option.Flow[:16]
|
option.Flow = option.Flow[:16]
|
||||||
|
@ -372,7 +379,7 @@ func NewVless(option VlessOption) (*Vless, error) {
|
||||||
Flow: option.Flow,
|
Flow: option.Flow,
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported vless flow type: %s", option.Flow)
|
return nil, fmt.Errorf("unsupported xtls flow type: %s", option.Flow)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,13 @@ type HealthCheck struct {
|
||||||
func (hc *HealthCheck) process() {
|
func (hc *HealthCheck) process() {
|
||||||
ticker := time.NewTicker(time.Duration(hc.interval) * time.Second)
|
ticker := time.NewTicker(time.Duration(hc.interval) * time.Second)
|
||||||
|
|
||||||
go hc.check()
|
go func() {
|
||||||
|
t := time.NewTicker(30 * time.Second)
|
||||||
|
<-t.C
|
||||||
|
t.Stop()
|
||||||
|
hc.check()
|
||||||
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
|
|
|
@ -32,14 +32,14 @@ func DialContext(ctx context.Context, network, address string, options ...Option
|
||||||
var ip net.IP
|
var ip net.IP
|
||||||
switch network {
|
switch network {
|
||||||
case "tcp4", "udp4":
|
case "tcp4", "udp4":
|
||||||
if opt.interfaceName != "" {
|
if !opt.direct {
|
||||||
ip, err = resolver.ResolveIPv4WithMain(host)
|
ip, err = resolver.ResolveIPv4ProxyServerHost(host)
|
||||||
} else {
|
} else {
|
||||||
ip, err = resolver.ResolveIPv4(host)
|
ip, err = resolver.ResolveIPv4(host)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if opt.interfaceName != "" {
|
if !opt.direct {
|
||||||
ip, err = resolver.ResolveIPv6WithMain(host)
|
ip, err = resolver.ResolveIPv6ProxyServerHost(host)
|
||||||
} else {
|
} else {
|
||||||
ip, err = resolver.ResolveIPv6(host)
|
ip, err = resolver.ResolveIPv6(host)
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,7 @@ func dualStackDialContext(ctx context.Context, network, address string, opt *opt
|
||||||
results := make(chan dialResult)
|
results := make(chan dialResult)
|
||||||
var primary, fallback dialResult
|
var primary, fallback dialResult
|
||||||
|
|
||||||
startRacer := func(ctx context.Context, network, host string, ipv6 bool) {
|
startRacer := func(ctx context.Context, network, host string, direct bool, ipv6 bool) {
|
||||||
result := dialResult{ipv6: ipv6, done: true}
|
result := dialResult{ipv6: ipv6, done: true}
|
||||||
defer func() {
|
defer func() {
|
||||||
select {
|
select {
|
||||||
|
@ -135,14 +135,14 @@ func dualStackDialContext(ctx context.Context, network, address string, opt *opt
|
||||||
|
|
||||||
var ip net.IP
|
var ip net.IP
|
||||||
if ipv6 {
|
if ipv6 {
|
||||||
if opt.interfaceName != "" {
|
if !direct {
|
||||||
ip, result.error = resolver.ResolveIPv6WithMain(host)
|
ip, result.error = resolver.ResolveIPv6ProxyServerHost(host)
|
||||||
} else {
|
} else {
|
||||||
ip, result.error = resolver.ResolveIPv6(host)
|
ip, result.error = resolver.ResolveIPv6(host)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if opt.interfaceName != "" {
|
if !direct {
|
||||||
ip, result.error = resolver.ResolveIPv4WithMain(host)
|
ip, result.error = resolver.ResolveIPv4ProxyServerHost(host)
|
||||||
} else {
|
} else {
|
||||||
ip, result.error = resolver.ResolveIPv4(host)
|
ip, result.error = resolver.ResolveIPv4(host)
|
||||||
}
|
}
|
||||||
|
@ -155,8 +155,8 @@ func dualStackDialContext(ctx context.Context, network, address string, opt *opt
|
||||||
result.Conn, result.error = dialContext(ctx, network, ip, port, opt)
|
result.Conn, result.error = dialContext(ctx, network, ip, port, opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
go startRacer(ctx, network+"4", host, false)
|
go startRacer(ctx, network+"4", host, opt.direct, false)
|
||||||
go startRacer(ctx, network+"6", host, true)
|
go startRacer(ctx, network+"6", host, opt.direct, true)
|
||||||
|
|
||||||
for res := range results {
|
for res := range results {
|
||||||
if res.error == nil {
|
if res.error == nil {
|
||||||
|
|
|
@ -12,6 +12,7 @@ type option struct {
|
||||||
interfaceName string
|
interfaceName string
|
||||||
addrReuse bool
|
addrReuse bool
|
||||||
routingMark int
|
routingMark int
|
||||||
|
direct bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Option func(opt *option)
|
type Option func(opt *option)
|
||||||
|
@ -33,3 +34,9 @@ func WithRoutingMark(mark int) Option {
|
||||||
opt.routingMark = mark
|
opt.routingMark = mark
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithDirect() Option {
|
||||||
|
return func(opt *option) {
|
||||||
|
opt.direct = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,8 +15,8 @@ var (
|
||||||
// DefaultResolver aim to resolve ip
|
// DefaultResolver aim to resolve ip
|
||||||
DefaultResolver Resolver
|
DefaultResolver Resolver
|
||||||
|
|
||||||
// MainResolver resolve ip with main domain server
|
// ProxyServerHostResolver resolve ip to proxies server host
|
||||||
MainResolver Resolver
|
ProxyServerHostResolver Resolver
|
||||||
|
|
||||||
// DisableIPv6 means don't resolve ipv6 host
|
// DisableIPv6 means don't resolve ipv6 host
|
||||||
// default value is true
|
// default value is true
|
||||||
|
@ -46,10 +46,6 @@ func ResolveIPv4(host string) (net.IP, error) {
|
||||||
return ResolveIPv4WithResolver(host, DefaultResolver)
|
return ResolveIPv4WithResolver(host, DefaultResolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ResolveIPv4WithMain(host string) (net.IP, error) {
|
|
||||||
return ResolveIPv4WithResolver(host, MainResolver)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ResolveIPv4WithResolver(host string, r Resolver) (net.IP, error) {
|
func ResolveIPv4WithResolver(host string, r Resolver) (net.IP, error) {
|
||||||
if node := DefaultHosts.Search(host); node != nil {
|
if node := DefaultHosts.Search(host); node != nil {
|
||||||
if ip := node.Data.(net.IP).To4(); ip != nil {
|
if ip := node.Data.(net.IP).To4(); ip != nil {
|
||||||
|
@ -69,6 +65,7 @@ func ResolveIPv4WithResolver(host string, r Resolver) (net.IP, error) {
|
||||||
return r.ResolveIPv4(host)
|
return r.ResolveIPv4(host)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if DefaultResolver == nil {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), DefaultDNSTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), DefaultDNSTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
ipAddrs, err := net.DefaultResolver.LookupIP(ctx, "ip4", host)
|
ipAddrs, err := net.DefaultResolver.LookupIP(ctx, "ip4", host)
|
||||||
|
@ -81,15 +78,14 @@ func ResolveIPv4WithResolver(host string, r Resolver) (net.IP, error) {
|
||||||
return ipAddrs[rand.Intn(len(ipAddrs))], nil
|
return ipAddrs[rand.Intn(len(ipAddrs))], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil, ErrIPNotFound
|
||||||
|
}
|
||||||
|
|
||||||
// ResolveIPv6 with a host, return ipv6
|
// ResolveIPv6 with a host, return ipv6
|
||||||
func ResolveIPv6(host string) (net.IP, error) {
|
func ResolveIPv6(host string) (net.IP, error) {
|
||||||
return ResolveIPv6WithResolver(host, DefaultResolver)
|
return ResolveIPv6WithResolver(host, DefaultResolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ResolveIPv6WithMain(host string) (net.IP, error) {
|
|
||||||
return ResolveIPv6WithResolver(host, MainResolver)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ResolveIPv6WithResolver(host string, r Resolver) (net.IP, error) {
|
func ResolveIPv6WithResolver(host string, r Resolver) (net.IP, error) {
|
||||||
if DisableIPv6 {
|
if DisableIPv6 {
|
||||||
return nil, ErrIPv6Disabled
|
return nil, ErrIPv6Disabled
|
||||||
|
@ -113,6 +109,7 @@ func ResolveIPv6WithResolver(host string, r Resolver) (net.IP, error) {
|
||||||
return r.ResolveIPv6(host)
|
return r.ResolveIPv6(host)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if DefaultResolver == nil {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), DefaultDNSTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), DefaultDNSTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
ipAddrs, err := net.DefaultResolver.LookupIP(ctx, "ip6", host)
|
ipAddrs, err := net.DefaultResolver.LookupIP(ctx, "ip6", host)
|
||||||
|
@ -125,6 +122,9 @@ func ResolveIPv6WithResolver(host string, r Resolver) (net.IP, error) {
|
||||||
return ipAddrs[rand.Intn(len(ipAddrs))], nil
|
return ipAddrs[rand.Intn(len(ipAddrs))], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil, ErrIPNotFound
|
||||||
|
}
|
||||||
|
|
||||||
// ResolveIPWithResolver same as ResolveIP, but with a resolver
|
// ResolveIPWithResolver same as ResolveIP, but with a resolver
|
||||||
func ResolveIPWithResolver(host string, r Resolver) (net.IP, error) {
|
func ResolveIPWithResolver(host string, r Resolver) (net.IP, error) {
|
||||||
if node := DefaultHosts.Search(host); node != nil {
|
if node := DefaultHosts.Search(host); node != nil {
|
||||||
|
@ -145,6 +145,7 @@ func ResolveIPWithResolver(host string, r Resolver) (net.IP, error) {
|
||||||
return ip, nil
|
return ip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if DefaultResolver == nil {
|
||||||
ipAddr, err := net.ResolveIPAddr("ip", host)
|
ipAddr, err := net.ResolveIPAddr("ip", host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -153,12 +154,34 @@ func ResolveIPWithResolver(host string, r Resolver) (net.IP, error) {
|
||||||
return ipAddr.IP, nil
|
return ipAddr.IP, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil, ErrIPNotFound
|
||||||
|
}
|
||||||
|
|
||||||
// ResolveIP with a host, return ip
|
// ResolveIP with a host, return ip
|
||||||
func ResolveIP(host string) (net.IP, error) {
|
func ResolveIP(host string) (net.IP, error) {
|
||||||
return ResolveIPWithResolver(host, DefaultResolver)
|
return ResolveIPWithResolver(host, DefaultResolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIPWithMainResolver with a host, use main resolver, return ip
|
// ResolveIPv4ProxyServerHost proxies server host only
|
||||||
func ResolveIPWithMainResolver(host string) (net.IP, error) {
|
func ResolveIPv4ProxyServerHost(host string) (net.IP, error) {
|
||||||
return ResolveIPWithResolver(host, MainResolver)
|
if ProxyServerHostResolver != nil {
|
||||||
|
return ResolveIPv4WithResolver(host, ProxyServerHostResolver)
|
||||||
|
}
|
||||||
|
return ResolveIPv4(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolveIPv6ProxyServerHost proxies server host only
|
||||||
|
func ResolveIPv6ProxyServerHost(host string) (net.IP, error) {
|
||||||
|
if ProxyServerHostResolver != nil {
|
||||||
|
return ResolveIPv6WithResolver(host, ProxyServerHostResolver)
|
||||||
|
}
|
||||||
|
return ResolveIPv6(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolveProxyServerHost proxies server host only
|
||||||
|
func ResolveProxyServerHost(host string) (net.IP, error) {
|
||||||
|
if ProxyServerHostResolver != nil {
|
||||||
|
return ResolveIPWithResolver(host, ProxyServerHostResolver)
|
||||||
|
}
|
||||||
|
return ResolveIP(host)
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,7 @@ type DNS struct {
|
||||||
FakeIPRange *fakeip.Pool
|
FakeIPRange *fakeip.Pool
|
||||||
Hosts *trie.DomainTrie
|
Hosts *trie.DomainTrie
|
||||||
NameServerPolicy map[string]dns.NameServer
|
NameServerPolicy map[string]dns.NameServer
|
||||||
|
ProxyServerNameserver []dns.NameServer
|
||||||
}
|
}
|
||||||
|
|
||||||
// FallbackFilter config
|
// FallbackFilter config
|
||||||
|
@ -158,6 +159,7 @@ type RawDNS struct {
|
||||||
FakeIPFilter []string `yaml:"fake-ip-filter"`
|
FakeIPFilter []string `yaml:"fake-ip-filter"`
|
||||||
DefaultNameserver []string `yaml:"default-nameserver"`
|
DefaultNameserver []string `yaml:"default-nameserver"`
|
||||||
NameServerPolicy map[string]string `yaml:"nameserver-policy"`
|
NameServerPolicy map[string]string `yaml:"nameserver-policy"`
|
||||||
|
ProxyServerNameserver []string `yaml:"proxy-server-nameserver"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RawFallbackFilter struct {
|
type RawFallbackFilter struct {
|
||||||
|
@ -805,6 +807,10 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie, rules []C.Rule) (*DNS,
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dnsCfg.ProxyServerNameserver, err = parseNameServer(cfg.ProxyServerNameserver); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if len(cfg.DefaultNameserver) == 0 {
|
if len(cfg.DefaultNameserver) == 0 {
|
||||||
return nil, errors.New("default nameserver should have at least one nameserver")
|
return nil, errors.New("default nameserver should have at least one nameserver")
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ type Resolver struct {
|
||||||
group singleflight.Group
|
group singleflight.Group
|
||||||
lruCache *cache.LruCache
|
lruCache *cache.LruCache
|
||||||
policy *trie.DomainTrie
|
policy *trie.DomainTrie
|
||||||
|
proxyServer []dnsClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIP request with TypeA and TypeAAAA, priority return TypeA
|
// ResolveIP request with TypeA and TypeAAAA, priority return TypeA
|
||||||
|
@ -300,6 +301,11 @@ func (r *Resolver) asyncExchange(ctx context.Context, client []dnsClient, msg *D
|
||||||
return ch
|
return ch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasProxyServer has proxy server dns client
|
||||||
|
func (r *Resolver) HasProxyServer() bool {
|
||||||
|
return len(r.main) > 0
|
||||||
|
}
|
||||||
|
|
||||||
type NameServer struct {
|
type NameServer struct {
|
||||||
Net string
|
Net string
|
||||||
Addr string
|
Addr string
|
||||||
|
@ -318,6 +324,7 @@ type FallbackFilter struct {
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Main, Fallback []NameServer
|
Main, Fallback []NameServer
|
||||||
Default []NameServer
|
Default []NameServer
|
||||||
|
ProxyServer []NameServer
|
||||||
IPv6 bool
|
IPv6 bool
|
||||||
EnhancedMode C.DNSMode
|
EnhancedMode C.DNSMode
|
||||||
FallbackFilter FallbackFilter
|
FallbackFilter FallbackFilter
|
||||||
|
@ -343,6 +350,10 @@ func NewResolver(config Config) *Resolver {
|
||||||
r.fallback = transform(config.Fallback, defaultResolver)
|
r.fallback = transform(config.Fallback, defaultResolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(config.ProxyServer) != 0 {
|
||||||
|
r.proxyServer = transform(config.ProxyServer, defaultResolver)
|
||||||
|
}
|
||||||
|
|
||||||
if len(config.Policy) != 0 {
|
if len(config.Policy) != 0 {
|
||||||
r.policy = trie.New()
|
r.policy = trie.New()
|
||||||
for domain, nameserver := range config.Policy {
|
for domain, nameserver := range config.Policy {
|
||||||
|
@ -376,10 +387,10 @@ func NewResolver(config Config) *Resolver {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMainResolver(old *Resolver) *Resolver {
|
func NewProxyServerHostResolver(old *Resolver) *Resolver {
|
||||||
r := &Resolver{
|
r := &Resolver{
|
||||||
ipv6: old.ipv6,
|
ipv6: old.ipv6,
|
||||||
main: old.main,
|
main: old.proxyServer,
|
||||||
lruCache: old.lruCache,
|
lruCache: old.lruCache,
|
||||||
hosts: old.hosts,
|
hosts: old.hosts,
|
||||||
policy: old.policy,
|
policy: old.policy,
|
||||||
|
|
|
@ -133,10 +133,11 @@ func updateDNS(c *config.DNS, t *config.Tun) {
|
||||||
},
|
},
|
||||||
Default: c.DefaultNameserver,
|
Default: c.DefaultNameserver,
|
||||||
Policy: c.NameServerPolicy,
|
Policy: c.NameServerPolicy,
|
||||||
|
ProxyServer: c.ProxyServerNameserver,
|
||||||
}
|
}
|
||||||
|
|
||||||
r := dns.NewResolver(cfg)
|
r := dns.NewResolver(cfg)
|
||||||
mr := dns.NewMainResolver(r)
|
pr := dns.NewProxyServerHostResolver(r)
|
||||||
m := dns.NewEnhancer(cfg)
|
m := dns.NewEnhancer(cfg)
|
||||||
|
|
||||||
// reuse cache of old host mapper
|
// reuse cache of old host mapper
|
||||||
|
@ -145,9 +146,12 @@ func updateDNS(c *config.DNS, t *config.Tun) {
|
||||||
}
|
}
|
||||||
|
|
||||||
resolver.DefaultResolver = r
|
resolver.DefaultResolver = r
|
||||||
resolver.MainResolver = mr
|
|
||||||
resolver.DefaultHostMapper = m
|
resolver.DefaultHostMapper = m
|
||||||
|
|
||||||
|
if pr.HasProxyServer() {
|
||||||
|
resolver.ProxyServerHostResolver = pr
|
||||||
|
}
|
||||||
|
|
||||||
if t.Enable {
|
if t.Enable {
|
||||||
resolver.DefaultLocalServer = dns.NewLocalServer(r, m)
|
resolver.DefaultLocalServer = dns.NewLocalServer(r, m)
|
||||||
}
|
}
|
||||||
|
@ -157,9 +161,9 @@ func updateDNS(c *config.DNS, t *config.Tun) {
|
||||||
} else {
|
} else {
|
||||||
if !t.Enable {
|
if !t.Enable {
|
||||||
resolver.DefaultResolver = nil
|
resolver.DefaultResolver = nil
|
||||||
resolver.MainResolver = nil
|
|
||||||
resolver.DefaultHostMapper = nil
|
resolver.DefaultHostMapper = nil
|
||||||
resolver.DefaultLocalServer = nil
|
resolver.DefaultLocalServer = nil
|
||||||
|
resolver.ProxyServerHostResolver = nil
|
||||||
}
|
}
|
||||||
dns.ReCreateServer("", nil, nil)
|
dns.ReCreateServer("", nil, nil)
|
||||||
}
|
}
|
||||||
|
@ -365,7 +369,9 @@ func updateIPTables(cfg *config.Config) {
|
||||||
log.Infoln("[IPTABLES] Setting iptables completed")
|
log.Infoln("[IPTABLES] Setting iptables completed")
|
||||||
}
|
}
|
||||||
|
|
||||||
func Cleanup() {
|
func Shutdown() {
|
||||||
P.Cleanup()
|
P.Cleanup()
|
||||||
tproxy.CleanupTProxyIPTables()
|
tproxy.CleanupTProxyIPTables()
|
||||||
|
|
||||||
|
log.Warnln("Clash shutting down")
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,3 @@ func Parse(options ...Option) error {
|
||||||
executor.ApplyConfig(cfg, true)
|
executor.ApplyConfig(cfg, true)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Cleanup() {
|
|
||||||
executor.Cleanup()
|
|
||||||
}
|
|
||||||
|
|
|
@ -29,4 +29,7 @@ type Device interface {
|
||||||
|
|
||||||
// UseIOBased work for other ip stack
|
// UseIOBased work for other ip stack
|
||||||
UseIOBased() error
|
UseIOBased() error
|
||||||
|
|
||||||
|
// Wait waits for the device to close.
|
||||||
|
Wait()
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,7 @@ func (e *Endpoint) dispatchLoop(cancel context.CancelFunc) {
|
||||||
case header.IPv6Version:
|
case header.IPv6Version:
|
||||||
e.InjectInbound(header.IPv6ProtocolNumber, pkt)
|
e.InjectInbound(header.IPv6ProtocolNumber, pkt)
|
||||||
}
|
}
|
||||||
pkt.DecRef() /* release */
|
pkt.DecRef()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
|
||||||
"github.com/Dreamacro/clash/listener/tun/device"
|
"github.com/Dreamacro/clash/listener/tun/device"
|
||||||
"github.com/Dreamacro/clash/listener/tun/device/iobased"
|
"github.com/Dreamacro/clash/listener/tun/device/iobased"
|
||||||
|
|
||||||
|
@ -22,6 +21,8 @@ type TUN struct {
|
||||||
mtu uint32
|
mtu uint32
|
||||||
name string
|
name string
|
||||||
offset int
|
offset int
|
||||||
|
|
||||||
|
cache []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func Open(name string, mtu uint32) (_ device.Device, err error) {
|
func Open(name string, mtu uint32) (_ device.Device, err error) {
|
||||||
|
@ -70,6 +71,10 @@ func Open(name string, mtu uint32) (_ device.Device, err error) {
|
||||||
}
|
}
|
||||||
t.mtu = uint32(tunMTU)
|
t.mtu = uint32(tunMTU)
|
||||||
|
|
||||||
|
if t.offset > 0 {
|
||||||
|
t.cache = make([]byte, 65535)
|
||||||
|
}
|
||||||
|
|
||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,19 +83,9 @@ func (t *TUN) Read(packet []byte) (int, error) {
|
||||||
return t.nt.Read(packet, t.offset)
|
return t.nt.Read(packet, t.offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
buff := pool.Get(t.offset + cap(packet))
|
n, err := t.nt.Read(t.cache, t.offset)
|
||||||
defer func() {
|
|
||||||
_ = pool.Put(buff)
|
|
||||||
}()
|
|
||||||
|
|
||||||
n, err := t.nt.Read(buff, t.offset)
|
copy(packet, t.cache[t.offset:t.offset+n])
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = buff[:t.offset]
|
|
||||||
|
|
||||||
copy(packet, buff[t.offset:t.offset+n])
|
|
||||||
|
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
@ -100,12 +95,15 @@ func (t *TUN) Write(packet []byte) (int, error) {
|
||||||
return t.nt.Write(packet, t.offset)
|
return t.nt.Write(packet, t.offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
packet = append(make([]byte, t.offset), packet...)
|
packet = append(t.cache[:t.offset], packet...)
|
||||||
|
|
||||||
return t.nt.Write(packet, t.offset)
|
return t.nt.Write(packet, t.offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TUN) Close() error {
|
func (t *TUN) Close() error {
|
||||||
|
if t.Endpoint != nil {
|
||||||
|
t.Endpoint.Close()
|
||||||
|
}
|
||||||
return t.nt.Close()
|
return t.nt.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,12 @@ package adapter
|
||||||
// Handler is a TCP/UDP connection handler that implements
|
// Handler is a TCP/UDP connection handler that implements
|
||||||
// HandleTCPConn and HandleUDPConn methods.
|
// HandleTCPConn and HandleUDPConn methods.
|
||||||
type Handler interface {
|
type Handler interface {
|
||||||
HandleTCPConn(TCPConn)
|
HandleTCP(TCPConn)
|
||||||
HandleUDPConn(UDPConn)
|
HandleUDP(UDPConn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TCPHandleFunc handles incoming TCP connection.
|
||||||
|
type TCPHandleFunc func(TCPConn)
|
||||||
|
|
||||||
|
// UDPHandleFunc handles incoming UDP connection.
|
||||||
|
type UDPHandleFunc func(UDPConn)
|
||||||
|
|
|
@ -24,7 +24,7 @@ type GVHandler struct {
|
||||||
UDPIn chan<- *inbound.PacketAdapter
|
UDPIn chan<- *inbound.PacketAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gh *GVHandler) HandleTCPConn(tunConn adapter.TCPConn) {
|
func (gh *GVHandler) HandleTCP(tunConn adapter.TCPConn) {
|
||||||
id := tunConn.ID()
|
id := tunConn.ID()
|
||||||
|
|
||||||
rAddr := &net.UDPAddr{
|
rAddr := &net.UDPAddr{
|
||||||
|
@ -77,7 +77,7 @@ func (gh *GVHandler) HandleTCPConn(tunConn adapter.TCPConn) {
|
||||||
gh.TCPIn <- inbound.NewSocket(socks5.ParseAddrToSocksAddr(rAddr), tunConn, C.TUN)
|
gh.TCPIn <- inbound.NewSocket(socks5.ParseAddrToSocksAddr(rAddr), tunConn, C.TUN)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gh *GVHandler) HandleUDPConn(tunConn adapter.UDPConn) {
|
func (gh *GVHandler) HandleUDP(tunConn adapter.UDPConn) {
|
||||||
id := tunConn.ID()
|
id := tunConn.ID()
|
||||||
|
|
||||||
rAddr := &net.UDPAddr{
|
rAddr := &net.UDPAddr{
|
||||||
|
|
|
@ -3,14 +3,13 @@ package gvisor
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option"
|
||||||
|
|
||||||
"gvisor.dev/gvisor/pkg/tcpip"
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// defaultNICID is the ID of default NIC used by DefaultStack.
|
|
||||||
defaultNICID tcpip.NICID = 0x01
|
|
||||||
|
|
||||||
// nicPromiscuousModeEnabled is the value used by stack to enable
|
// nicPromiscuousModeEnabled is the value used by stack to enable
|
||||||
// or disable NIC's promiscuous mode.
|
// or disable NIC's promiscuous mode.
|
||||||
nicPromiscuousModeEnabled = true
|
nicPromiscuousModeEnabled = true
|
||||||
|
@ -21,9 +20,9 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// withCreatingNIC creates NIC for stack.
|
// withCreatingNIC creates NIC for stack.
|
||||||
func withCreatingNIC(ep stack.LinkEndpoint) Option {
|
func withCreatingNIC(nicID tcpip.NICID, ep stack.LinkEndpoint) option.Option {
|
||||||
return func(s *gvStack) error {
|
return func(s *stack.Stack) error {
|
||||||
if err := s.CreateNICWithOptions(s.nicID, ep,
|
if err := s.CreateNICWithOptions(nicID, ep,
|
||||||
stack.NICOptions{
|
stack.NICOptions{
|
||||||
Disabled: false,
|
Disabled: false,
|
||||||
// If no queueing discipline was specified
|
// If no queueing discipline was specified
|
||||||
|
@ -37,21 +36,21 @@ func withCreatingNIC(ep stack.LinkEndpoint) Option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// withPromiscuousMode sets promiscuous mode in the given NIC.
|
// withPromiscuousMode sets promiscuous mode in the given NICs.
|
||||||
func withPromiscuousMode(v bool) Option {
|
func withPromiscuousMode(nicID tcpip.NICID, v bool) option.Option {
|
||||||
return func(s *gvStack) error {
|
return func(s *stack.Stack) error {
|
||||||
if err := s.SetPromiscuousMode(s.nicID, v); err != nil {
|
if err := s.SetPromiscuousMode(nicID, v); err != nil {
|
||||||
return fmt.Errorf("set promiscuous mode: %s", err)
|
return fmt.Errorf("set promiscuous mode: %s", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// withSpoofing sets address spoofing in the given NIC, allowing
|
// withSpoofing sets address spoofing in the given NICs, allowing
|
||||||
// endpoints to bind to any address in the NIC.
|
// endpoints to bind to any address in the NIC.
|
||||||
func withSpoofing(v bool) Option {
|
func withSpoofing(nicID tcpip.NICID, v bool) option.Option {
|
||||||
return func(s *gvStack) error {
|
return func(s *stack.Stack) error {
|
||||||
if err := s.SetSpoofing(s.nicID, v); err != nil {
|
if err := s.SetSpoofing(nicID, v); err != nil {
|
||||||
return fmt.Errorf("set spoofing: %s", err)
|
return fmt.Errorf("set spoofing: %s", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package gvisor
|
package option
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -7,6 +7,7 @@ import (
|
||||||
"gvisor.dev/gvisor/pkg/tcpip"
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -56,11 +57,11 @@ const (
|
||||||
tcpDefaultBufferSize = 212 << 10 // 212 KiB
|
tcpDefaultBufferSize = 212 << 10 // 212 KiB
|
||||||
)
|
)
|
||||||
|
|
||||||
type Option func(*gvStack) error
|
type Option func(*stack.Stack) error
|
||||||
|
|
||||||
// WithDefault sets all default values for stack.
|
// WithDefault sets all default values for stack.
|
||||||
func WithDefault() Option {
|
func WithDefault() Option {
|
||||||
return func(s *gvStack) error {
|
return func(s *stack.Stack) error {
|
||||||
opts := []Option{
|
opts := []Option{
|
||||||
WithDefaultTTL(defaultTimeToLive),
|
WithDefaultTTL(defaultTimeToLive),
|
||||||
WithForwarding(ipForwardingEnabled),
|
WithForwarding(ipForwardingEnabled),
|
||||||
|
@ -110,7 +111,7 @@ func WithDefault() Option {
|
||||||
|
|
||||||
// WithDefaultTTL sets the default TTL used by stack.
|
// WithDefaultTTL sets the default TTL used by stack.
|
||||||
func WithDefaultTTL(ttl uint8) Option {
|
func WithDefaultTTL(ttl uint8) Option {
|
||||||
return func(s *gvStack) error {
|
return func(s *stack.Stack) error {
|
||||||
opt := tcpip.DefaultTTLOption(ttl)
|
opt := tcpip.DefaultTTLOption(ttl)
|
||||||
if err := s.SetNetworkProtocolOption(ipv4.ProtocolNumber, &opt); err != nil {
|
if err := s.SetNetworkProtocolOption(ipv4.ProtocolNumber, &opt); err != nil {
|
||||||
return fmt.Errorf("set ipv4 default TTL: %s", err)
|
return fmt.Errorf("set ipv4 default TTL: %s", err)
|
||||||
|
@ -124,7 +125,7 @@ func WithDefaultTTL(ttl uint8) Option {
|
||||||
|
|
||||||
// WithForwarding sets packet forwarding between NICs for IPv4 & IPv6.
|
// WithForwarding sets packet forwarding between NICs for IPv4 & IPv6.
|
||||||
func WithForwarding(v bool) Option {
|
func WithForwarding(v bool) Option {
|
||||||
return func(s *gvStack) error {
|
return func(s *stack.Stack) error {
|
||||||
if err := s.SetForwardingDefaultAndAllNICs(ipv4.ProtocolNumber, v); err != nil {
|
if err := s.SetForwardingDefaultAndAllNICs(ipv4.ProtocolNumber, v); err != nil {
|
||||||
return fmt.Errorf("set ipv4 forwarding: %s", err)
|
return fmt.Errorf("set ipv4 forwarding: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -138,7 +139,7 @@ func WithForwarding(v bool) Option {
|
||||||
// WithICMPBurst sets the number of ICMP messages that can be sent
|
// WithICMPBurst sets the number of ICMP messages that can be sent
|
||||||
// in a single burst.
|
// in a single burst.
|
||||||
func WithICMPBurst(burst int) Option {
|
func WithICMPBurst(burst int) Option {
|
||||||
return func(s *gvStack) error {
|
return func(s *stack.Stack) error {
|
||||||
s.SetICMPBurst(burst)
|
s.SetICMPBurst(burst)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -147,7 +148,7 @@ func WithICMPBurst(burst int) Option {
|
||||||
// WithICMPLimit sets the maximum number of ICMP messages permitted
|
// WithICMPLimit sets the maximum number of ICMP messages permitted
|
||||||
// by rate limiter.
|
// by rate limiter.
|
||||||
func WithICMPLimit(limit rate.Limit) Option {
|
func WithICMPLimit(limit rate.Limit) Option {
|
||||||
return func(s *gvStack) error {
|
return func(s *stack.Stack) error {
|
||||||
s.SetICMPLimit(limit)
|
s.SetICMPLimit(limit)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -155,7 +156,7 @@ func WithICMPLimit(limit rate.Limit) Option {
|
||||||
|
|
||||||
// WithTCPBufferSizeRange sets the receive and send buffer size range for TCP.
|
// WithTCPBufferSizeRange sets the receive and send buffer size range for TCP.
|
||||||
func WithTCPBufferSizeRange(a, b, c int) Option {
|
func WithTCPBufferSizeRange(a, b, c int) Option {
|
||||||
return func(s *gvStack) error {
|
return func(s *stack.Stack) error {
|
||||||
rcvOpt := tcpip.TCPReceiveBufferSizeRangeOption{Min: a, Default: b, Max: c}
|
rcvOpt := tcpip.TCPReceiveBufferSizeRangeOption{Min: a, Default: b, Max: c}
|
||||||
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &rcvOpt); err != nil {
|
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &rcvOpt); err != nil {
|
||||||
return fmt.Errorf("set TCP receive buffer size range: %s", err)
|
return fmt.Errorf("set TCP receive buffer size range: %s", err)
|
||||||
|
@ -170,7 +171,7 @@ func WithTCPBufferSizeRange(a, b, c int) Option {
|
||||||
|
|
||||||
// WithTCPCongestionControl sets the current congestion control algorithm.
|
// WithTCPCongestionControl sets the current congestion control algorithm.
|
||||||
func WithTCPCongestionControl(cc string) Option {
|
func WithTCPCongestionControl(cc string) Option {
|
||||||
return func(s *gvStack) error {
|
return func(s *stack.Stack) error {
|
||||||
opt := tcpip.CongestionControlOption(cc)
|
opt := tcpip.CongestionControlOption(cc)
|
||||||
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
|
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
|
||||||
return fmt.Errorf("set TCP congestion control algorithm: %s", err)
|
return fmt.Errorf("set TCP congestion control algorithm: %s", err)
|
||||||
|
@ -181,7 +182,7 @@ func WithTCPCongestionControl(cc string) Option {
|
||||||
|
|
||||||
// WithTCPDelay enables or disables Nagle's algorithm in TCP.
|
// WithTCPDelay enables or disables Nagle's algorithm in TCP.
|
||||||
func WithTCPDelay(v bool) Option {
|
func WithTCPDelay(v bool) Option {
|
||||||
return func(s *gvStack) error {
|
return func(s *stack.Stack) error {
|
||||||
opt := tcpip.TCPDelayEnabled(v)
|
opt := tcpip.TCPDelayEnabled(v)
|
||||||
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
|
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
|
||||||
return fmt.Errorf("set TCP delay: %s", err)
|
return fmt.Errorf("set TCP delay: %s", err)
|
||||||
|
@ -192,7 +193,7 @@ func WithTCPDelay(v bool) Option {
|
||||||
|
|
||||||
// WithTCPModerateReceiveBuffer sets receive buffer moderation for TCP.
|
// WithTCPModerateReceiveBuffer sets receive buffer moderation for TCP.
|
||||||
func WithTCPModerateReceiveBuffer(v bool) Option {
|
func WithTCPModerateReceiveBuffer(v bool) Option {
|
||||||
return func(s *gvStack) error {
|
return func(s *stack.Stack) error {
|
||||||
opt := tcpip.TCPModerateReceiveBufferOption(v)
|
opt := tcpip.TCPModerateReceiveBufferOption(v)
|
||||||
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
|
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
|
||||||
return fmt.Errorf("set TCP moderate receive buffer: %s", err)
|
return fmt.Errorf("set TCP moderate receive buffer: %s", err)
|
||||||
|
@ -203,7 +204,7 @@ func WithTCPModerateReceiveBuffer(v bool) Option {
|
||||||
|
|
||||||
// WithTCPSACKEnabled sets the SACK option for TCP.
|
// WithTCPSACKEnabled sets the SACK option for TCP.
|
||||||
func WithTCPSACKEnabled(v bool) Option {
|
func WithTCPSACKEnabled(v bool) Option {
|
||||||
return func(s *gvStack) error {
|
return func(s *stack.Stack) error {
|
||||||
opt := tcpip.TCPSACKEnabled(v)
|
opt := tcpip.TCPSACKEnabled(v)
|
||||||
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
|
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
|
||||||
return fmt.Errorf("set TCP SACK: %s", err)
|
return fmt.Errorf("set TCP SACK: %s", err)
|
||||||
|
@ -214,7 +215,7 @@ func WithTCPSACKEnabled(v bool) Option {
|
||||||
|
|
||||||
// WithTCPRecovery sets the recovery option for TCP.
|
// WithTCPRecovery sets the recovery option for TCP.
|
||||||
func WithTCPRecovery(v tcpip.TCPRecovery) Option {
|
func WithTCPRecovery(v tcpip.TCPRecovery) Option {
|
||||||
return func(s *gvStack) error {
|
return func(s *stack.Stack) error {
|
||||||
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &v); err != nil {
|
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &v); err != nil {
|
||||||
return fmt.Errorf("set TCP Recovery: %s", err)
|
return fmt.Errorf("set TCP Recovery: %s", err)
|
||||||
}
|
}
|
|
@ -1,22 +1,23 @@
|
||||||
package gvisor
|
package gvisor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option"
|
||||||
|
|
||||||
"gvisor.dev/gvisor/pkg/tcpip"
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
)
|
)
|
||||||
|
|
||||||
func withICMPHandler() Option {
|
func withRouteTable(nicID tcpip.NICID) option.Option {
|
||||||
return func(s *gvStack) error {
|
return func(s *stack.Stack) error {
|
||||||
// Add default route table for IPv4 and IPv6.
|
|
||||||
// This will handle all incoming ICMP packets.
|
|
||||||
s.SetRouteTable([]tcpip.Route{
|
s.SetRouteTable([]tcpip.Route{
|
||||||
{
|
{
|
||||||
Destination: header.IPv4EmptySubnet,
|
Destination: header.IPv4EmptySubnet,
|
||||||
NIC: s.nicID,
|
NIC: nicID,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Destination: header.IPv6EmptySubnet,
|
Destination: header.IPv6EmptySubnet,
|
||||||
NIC: s.nicID,
|
NIC: nicID,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"github.com/Dreamacro/clash/listener/tun/device"
|
"github.com/Dreamacro/clash/listener/tun/device"
|
||||||
"github.com/Dreamacro/clash/listener/tun/ipstack"
|
"github.com/Dreamacro/clash/listener/tun/ipstack"
|
||||||
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/adapter"
|
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/adapter"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option"
|
||||||
|
|
||||||
"gvisor.dev/gvisor/pkg/tcpip"
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||||
|
@ -18,23 +19,23 @@ import (
|
||||||
type gvStack struct {
|
type gvStack struct {
|
||||||
*stack.Stack
|
*stack.Stack
|
||||||
device device.Device
|
device device.Device
|
||||||
|
|
||||||
handler adapter.Handler
|
|
||||||
nicID tcpip.NICID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *gvStack) Close() error {
|
func (s *gvStack) Close() error {
|
||||||
|
var err error
|
||||||
|
if s.device != nil {
|
||||||
|
err = s.device.Close()
|
||||||
|
s.device.Wait()
|
||||||
|
}
|
||||||
if s.Stack != nil {
|
if s.Stack != nil {
|
||||||
s.Stack.Close()
|
s.Stack.Close()
|
||||||
|
s.Stack.Wait()
|
||||||
}
|
}
|
||||||
if s.device != nil {
|
return err
|
||||||
_ = s.device.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New allocates a new *gvStack with given options.
|
// New allocates a new *gvStack with given options.
|
||||||
func New(device device.Device, handler adapter.Handler, opts ...Option) (ipstack.Stack, error) {
|
func New(device device.Device, handler adapter.Handler, opts ...option.Option) (ipstack.Stack, error) {
|
||||||
s := &gvStack{
|
s := &gvStack{
|
||||||
Stack: stack.New(stack.Options{
|
Stack: stack.New(stack.Options{
|
||||||
NetworkProtocols: []stack.NetworkProtocolFactory{
|
NetworkProtocols: []stack.NetworkProtocolFactory{
|
||||||
|
@ -50,18 +51,14 @@ func New(device device.Device, handler adapter.Handler, opts ...Option) (ipstack
|
||||||
}),
|
}),
|
||||||
|
|
||||||
device: device,
|
device: device,
|
||||||
handler: handler,
|
|
||||||
nicID: defaultNICID,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
opts = append(opts,
|
// Generate unique NIC id.
|
||||||
// Important: We must initiate transport protocol handlers
|
nicID := tcpip.NICID(s.Stack.UniqueID())
|
||||||
// before creating NIC, otherwise NIC would dispatch packets
|
|
||||||
// to stack and cause race condition.
|
|
||||||
withICMPHandler(), withTCPHandler(), withUDPHandler(),
|
|
||||||
|
|
||||||
// Create stack NIC and then bind link endpoint.
|
opts = append(opts,
|
||||||
withCreatingNIC(device.(stack.LinkEndpoint)),
|
// Create stack NIC and then bind link endpoint to it.
|
||||||
|
withCreatingNIC(nicID, device),
|
||||||
|
|
||||||
// In the past we did s.AddAddressRange to assign 0.0.0.0/0
|
// In the past we did s.AddAddressRange to assign 0.0.0.0/0
|
||||||
// onto the interface. We need that to be able to terminate
|
// onto the interface. We need that to be able to terminate
|
||||||
|
@ -70,27 +67,34 @@ func New(device device.Device, handler adapter.Handler, opts ...Option) (ipstack
|
||||||
// Promiscuous mode. https://github.com/google/gvisor/issues/3876
|
// Promiscuous mode. https://github.com/google/gvisor/issues/3876
|
||||||
//
|
//
|
||||||
// Ref: https://github.com/cloudflare/slirpnetstack/blob/master/stack.go
|
// Ref: https://github.com/cloudflare/slirpnetstack/blob/master/stack.go
|
||||||
withPromiscuousMode(nicPromiscuousModeEnabled),
|
withPromiscuousMode(nicID, nicPromiscuousModeEnabled),
|
||||||
|
|
||||||
// Enable spoofing if a stack may send packets from unowned addresses.
|
// Enable spoofing if a stack may send packets from unowned
|
||||||
// This change required changes to some netgophers since previously,
|
// addresses. This change required changes to some netgophers
|
||||||
// promiscuous mode was enough to let the netstack respond to all
|
// since previously, promiscuous mode was enough to let the
|
||||||
// incoming packets regardless of the packet's destination address. Now
|
// netstack respond to all incoming packets regardless of the
|
||||||
// that a stack.Route is not held for each incoming packet, finding a route
|
// packet's destination address. Now that a stack.Route is not
|
||||||
// may fail with local addresses we don't own but accepted packets for
|
// held for each incoming packet, finding a route may fail with
|
||||||
// while in promiscuous mode. Since we also want to be able to send from
|
// local addresses we don't own but accepted packets for while
|
||||||
// any address (in response the received promiscuous mode packets), we need
|
// in promiscuous mode. Since we also want to be able to send
|
||||||
// to enable spoofing.
|
// from any address (in response the received promiscuous mode
|
||||||
|
// packets), we need to enable spoofing.
|
||||||
//
|
//
|
||||||
// Ref: https://github.com/google/gvisor/commit/8c0701462a84ff77e602f1626aec49479c308127
|
// Ref: https://github.com/google/gvisor/commit/8c0701462a84ff77e602f1626aec49479c308127
|
||||||
withSpoofing(nicSpoofingEnabled),
|
withSpoofing(nicID, nicSpoofingEnabled),
|
||||||
|
|
||||||
|
// Add default route table for IPv4 and IPv6. This will handle
|
||||||
|
// all incoming ICMP packets.
|
||||||
|
withRouteTable(nicID),
|
||||||
|
|
||||||
|
// Initiate transport protocol (TCP/UDP) with given handler.
|
||||||
|
withTCPHandler(handler.HandleTCP), withUDPHandler(handler.HandleUDP),
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
if err := opt(s); err != nil {
|
if err := opt(s.Stack); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/adapter"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option"
|
||||||
|
|
||||||
"gvisor.dev/gvisor/pkg/tcpip"
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
|
@ -37,9 +40,9 @@ const (
|
||||||
tcpKeepaliveInterval = 30 * time.Second
|
tcpKeepaliveInterval = 30 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
func withTCPHandler() Option {
|
func withTCPHandler(handle adapter.TCPHandleFunc) option.Option {
|
||||||
return func(s *gvStack) error {
|
return func(s *stack.Stack) error {
|
||||||
tcpForwarder := tcp.NewForwarder(s.Stack, defaultWndSize, maxConnAttempts, func(r *tcp.ForwarderRequest) {
|
tcpForwarder := tcp.NewForwarder(s, defaultWndSize, maxConnAttempts, func(r *tcp.ForwarderRequest) {
|
||||||
var wq waiter.Queue
|
var wq waiter.Queue
|
||||||
ep, err := r.CreateEndpoint(&wq)
|
ep, err := r.CreateEndpoint(&wq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -55,7 +58,7 @@ func withTCPHandler() Option {
|
||||||
TCPConn: gonet.NewTCPConn(&wq, ep),
|
TCPConn: gonet.NewTCPConn(&wq, ep),
|
||||||
id: r.ID(),
|
id: r.ID(),
|
||||||
}
|
}
|
||||||
s.handler.HandleTCPConn(conn)
|
handle(conn)
|
||||||
})
|
})
|
||||||
s.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket)
|
s.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/adapter"
|
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/adapter"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option"
|
||||||
|
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
|
@ -12,9 +13,9 @@ import (
|
||||||
"gvisor.dev/gvisor/pkg/waiter"
|
"gvisor.dev/gvisor/pkg/waiter"
|
||||||
)
|
)
|
||||||
|
|
||||||
func withUDPHandler() Option {
|
func withUDPHandler(handle adapter.UDPHandleFunc) option.Option {
|
||||||
return func(s *gvStack) error {
|
return func(s *stack.Stack) error {
|
||||||
udpForwarder := udp.NewForwarder(s.Stack, func(r *udp.ForwarderRequest) {
|
udpForwarder := udp.NewForwarder(s, func(r *udp.ForwarderRequest) {
|
||||||
var wq waiter.Queue
|
var wq waiter.Queue
|
||||||
ep, err := r.CreateEndpoint(&wq)
|
ep, err := r.CreateEndpoint(&wq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -23,10 +24,10 @@ func withUDPHandler() Option {
|
||||||
}
|
}
|
||||||
|
|
||||||
conn := &udpConn{
|
conn := &udpConn{
|
||||||
UDPConn: gonet.NewUDPConn(s.Stack, &wq, ep),
|
UDPConn: gonet.NewUDPConn(s, &wq, ep),
|
||||||
id: r.ID(),
|
id: r.ID(),
|
||||||
}
|
}
|
||||||
s.handler.HandleUDPConn(conn)
|
handle(conn)
|
||||||
})
|
})
|
||||||
s.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
|
s.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -36,8 +36,6 @@ func (s sysStack) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var ipv4LoopBack = netip.MustParsePrefix("127.0.0.0/8")
|
|
||||||
|
|
||||||
func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.Stack, error) {
|
func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.Stack, error) {
|
||||||
var (
|
var (
|
||||||
gateway = tunAddress.Masked().Addr().Next()
|
gateway = tunAddress.Masked().Addr().Next()
|
||||||
|
@ -71,12 +69,6 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
|
||||||
rAddrIp, _ := netip.AddrFromSlice(rAddr.IP)
|
rAddrIp, _ := netip.AddrFromSlice(rAddr.IP)
|
||||||
rAddrPort := netip.AddrPortFrom(rAddrIp, uint16(rAddr.Port))
|
rAddrPort := netip.AddrPortFrom(rAddrIp, uint16(rAddr.Port))
|
||||||
|
|
||||||
if ipv4LoopBack.Contains(rAddrIp) {
|
|
||||||
conn.Close()
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if D.ShouldHijackDns(dnsAddr, rAddrPort) {
|
if D.ShouldHijackDns(dnsAddr, rAddrPort) {
|
||||||
go func() {
|
go func() {
|
||||||
log.Debugln("[TUN] hijack dns tcp: %s", rAddrPort.String())
|
log.Debugln("[TUN] hijack dns tcp: %s", rAddrPort.String())
|
||||||
|
@ -149,12 +141,6 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
|
||||||
rAddrIp, _ := netip.AddrFromSlice(rAddr.IP)
|
rAddrIp, _ := netip.AddrFromSlice(rAddr.IP)
|
||||||
rAddrPort := netip.AddrPortFrom(rAddrIp, uint16(rAddr.Port))
|
rAddrPort := netip.AddrPortFrom(rAddrIp, uint16(rAddr.Port))
|
||||||
|
|
||||||
if ipv4LoopBack.Contains(rAddrIp) {
|
|
||||||
pool.Put(buf)
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if D.ShouldHijackDns(dnsAddr, rAddrPort) {
|
if D.ShouldHijackDns(dnsAddr, rAddrPort) {
|
||||||
go func() {
|
go func() {
|
||||||
defer pool.Put(buf)
|
defer pool.Put(buf)
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/Dreamacro/clash/listener/tun/ipstack"
|
"github.com/Dreamacro/clash/listener/tun/ipstack"
|
||||||
"github.com/Dreamacro/clash/listener/tun/ipstack/commons"
|
"github.com/Dreamacro/clash/listener/tun/ipstack/commons"
|
||||||
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor"
|
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option"
|
||||||
"github.com/Dreamacro/clash/listener/tun/ipstack/system"
|
"github.com/Dreamacro/clash/listener/tun/ipstack/system"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
@ -72,7 +73,7 @@ func New(tunConf *config.Tun, dnsConf *config.DNS, tcpIn chan<- C.ConnContext, u
|
||||||
DNSAdds: tunConf.DNSHijack,
|
DNSAdds: tunConf.DNSHijack,
|
||||||
TCPIn: tcpIn, UDPIn: udpIn,
|
TCPIn: tcpIn, UDPIn: udpIn,
|
||||||
},
|
},
|
||||||
gvisor.WithDefault(),
|
option.WithDefault(),
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
8
main.go
8
main.go
|
@ -106,13 +106,9 @@ func main() {
|
||||||
log.Fatalln("Parse config error: %s", err.Error())
|
log.Fatalln("Parse config error: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer executor.Shutdown()
|
||||||
|
|
||||||
sigCh := make(chan os.Signal, 1)
|
sigCh := make(chan os.Signal, 1)
|
||||||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||||
<-sigCh
|
<-sigCh
|
||||||
|
|
||||||
// cleanup
|
|
||||||
log.Warnln("Clash cleanup")
|
|
||||||
hub.Cleanup()
|
|
||||||
|
|
||||||
log.Warnln("Clash shutting down")
|
|
||||||
}
|
}
|
||||||
|
|
39
test/config/trojan-xtls.json
Normal file
39
test/config/trojan-xtls.json
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
"inbounds": [
|
||||||
|
{
|
||||||
|
"port": 10002,
|
||||||
|
"listen": "0.0.0.0",
|
||||||
|
"protocol": "trojan",
|
||||||
|
"settings": {
|
||||||
|
"clients": [
|
||||||
|
{
|
||||||
|
"password": "example",
|
||||||
|
"email": "xtls@example.com",
|
||||||
|
"flow": "xtls-rprx-direct",
|
||||||
|
"level": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"streamSettings": {
|
||||||
|
"network": "tcp",
|
||||||
|
"security": "xtls",
|
||||||
|
"xtlsSettings": {
|
||||||
|
"certificates": [
|
||||||
|
{
|
||||||
|
"certificateFile": "/etc/ssl/v2ray/fullchain.pem",
|
||||||
|
"keyFile": "/etc/ssl/v2ray/privkey.pem"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outbounds": [
|
||||||
|
{
|
||||||
|
"protocol": "freedom"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"log": {
|
||||||
|
"loglevel": "debug"
|
||||||
|
}
|
||||||
|
}
|
35
test/config/vless-ws.json
Normal file
35
test/config/vless-ws.json
Normal file
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -8,9 +8,9 @@
|
||||||
"clients": [
|
"clients": [
|
||||||
{
|
{
|
||||||
"id": "b831381d-6324-4d53-ad4f-8cda48b30811",
|
"id": "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||||
|
"email": "xtls@example.com",
|
||||||
"flow": "xtls-rprx-direct",
|
"flow": "xtls-rprx-direct",
|
||||||
"level": 0,
|
"level": 0
|
||||||
"email": "love@example.com"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"decryption": "none"
|
"decryption": "none"
|
||||||
|
|
|
@ -131,6 +131,46 @@ func TestClash_TrojanWebsocket(t *testing.T) {
|
||||||
testSuit(t, proxy)
|
testSuit(t, proxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestClash_TrojanXTLS(t *testing.T) {
|
||||||
|
cfg := &container.Config{
|
||||||
|
Image: ImageXray,
|
||||||
|
ExposedPorts: defaultExposedPorts,
|
||||||
|
}
|
||||||
|
hostCfg := &container.HostConfig{
|
||||||
|
PortBindings: defaultPortBindings,
|
||||||
|
Binds: []string{
|
||||||
|
fmt.Sprintf("%s:/etc/xray/config.json", C.Path.Resolve("trojan-xtls.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, "trojan-xtls")
|
||||||
|
if err != nil {
|
||||||
|
assert.FailNow(t, err.Error())
|
||||||
|
}
|
||||||
|
defer cleanContainer(id)
|
||||||
|
|
||||||
|
proxy, err := outbound.NewTrojan(outbound.TrojanOption{
|
||||||
|
Name: "trojan",
|
||||||
|
Server: localIP.String(),
|
||||||
|
Port: 10002,
|
||||||
|
Password: "example",
|
||||||
|
SNI: "example.org",
|
||||||
|
SkipCertVerify: true,
|
||||||
|
UDP: true,
|
||||||
|
Network: "tcp",
|
||||||
|
Flow: "xtls-rprx-direct",
|
||||||
|
FlowShow: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
assert.FailNow(t, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(waitTime)
|
||||||
|
testSuit(t, proxy)
|
||||||
|
}
|
||||||
|
|
||||||
func Benchmark_Trojan(b *testing.B) {
|
func Benchmark_Trojan(b *testing.B) {
|
||||||
cfg := &container.Config{
|
cfg := &container.Config{
|
||||||
Image: ImageTrojan,
|
Image: ImageTrojan,
|
||||||
|
|
|
@ -37,10 +37,9 @@ func TestClash_VlessTLS(t *testing.T) {
|
||||||
Server: localIP.String(),
|
Server: localIP.String(),
|
||||||
Port: 10002,
|
Port: 10002,
|
||||||
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||||
TLS: true,
|
|
||||||
SkipCertVerify: true,
|
SkipCertVerify: true,
|
||||||
ServerName: "example.org",
|
ServerName: "example.org",
|
||||||
UDP: false,
|
UDP: true,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
assert.FailNow(t, err.Error())
|
assert.FailNow(t, err.Error())
|
||||||
|
@ -75,12 +74,49 @@ func TestClash_VlessXTLS(t *testing.T) {
|
||||||
Server: localIP.String(),
|
Server: localIP.String(),
|
||||||
Port: 10002,
|
Port: 10002,
|
||||||
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||||
TLS: true,
|
|
||||||
Flow: "xtls-rprx-direct",
|
|
||||||
//FlowShow: true,
|
|
||||||
SkipCertVerify: true,
|
SkipCertVerify: true,
|
||||||
ServerName: "example.org",
|
ServerName: "example.org",
|
||||||
UDP: false,
|
UDP: true,
|
||||||
|
Flow: "xtls-rprx-direct",
|
||||||
|
FlowShow: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
assert.FailNow(t, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
if err != nil {
|
||||||
assert.FailNow(t, err.Error())
|
assert.FailNow(t, err.Error())
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
xtls "github.com/xtls/go"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -16,15 +16,15 @@ import (
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/transport/socks5"
|
"github.com/Dreamacro/clash/transport/socks5"
|
||||||
|
"github.com/Dreamacro/clash/transport/vless"
|
||||||
"github.com/Dreamacro/clash/transport/vmess"
|
"github.com/Dreamacro/clash/transport/vmess"
|
||||||
|
|
||||||
|
xtls "github.com/xtls/go"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// max packet length
|
// max packet length
|
||||||
maxLength = 8192
|
maxLength = 8192
|
||||||
|
|
||||||
XRD = "xtls-rprx-direct"
|
|
||||||
XRO = "xtls-rprx-origin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -36,21 +36,22 @@ var (
|
||||||
|
|
||||||
type Command = byte
|
type Command = byte
|
||||||
|
|
||||||
var (
|
const (
|
||||||
CommandTCP byte = 1
|
CommandTCP byte = 1
|
||||||
CommandUDP byte = 3
|
CommandUDP byte = 3
|
||||||
CommandXRD byte = 0xf0
|
|
||||||
CommandXRO byte = 0xf1
|
// for XTLS
|
||||||
|
commandXRD byte = 0xf0 // XTLS direct mode
|
||||||
|
commandXRO byte = 0xf1 // XTLS origin mode
|
||||||
)
|
)
|
||||||
|
|
||||||
type Option struct {
|
type Option struct {
|
||||||
Password string
|
Password string
|
||||||
Flow string
|
|
||||||
ALPN []string
|
ALPN []string
|
||||||
ServerName string
|
ServerName string
|
||||||
SkipCertVerify bool
|
SkipCertVerify bool
|
||||||
ClientSessionCache tls.ClientSessionCache
|
Flow string
|
||||||
ClientXSessionCache xtls.ClientSessionCache
|
FlowShow bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebsocketOption struct {
|
type WebsocketOption struct {
|
||||||
|
@ -65,28 +66,22 @@ type Trojan struct {
|
||||||
hexPassword []byte
|
hexPassword []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Trojan) GetFlow() string {
|
|
||||||
return t.option.Flow
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Trojan) StreamConn(conn net.Conn) (net.Conn, error) {
|
func (t *Trojan) StreamConn(conn net.Conn) (net.Conn, error) {
|
||||||
alpn := defaultALPN
|
alpn := defaultALPN
|
||||||
if len(t.option.ALPN) != 0 {
|
if len(t.option.ALPN) != 0 {
|
||||||
alpn = t.option.ALPN
|
alpn = t.option.ALPN
|
||||||
}
|
}
|
||||||
switch t.option.Flow {
|
switch t.option.Flow {
|
||||||
case XRD, XRO:
|
case vless.XRO, vless.XRD, vless.XRS:
|
||||||
xtlsConfig := &xtls.Config{
|
xtlsConfig := &xtls.Config{
|
||||||
NextProtos: alpn,
|
NextProtos: alpn,
|
||||||
MinVersion: xtls.VersionTLS12,
|
MinVersion: xtls.VersionTLS12,
|
||||||
InsecureSkipVerify: t.option.SkipCertVerify,
|
InsecureSkipVerify: t.option.SkipCertVerify,
|
||||||
ServerName: t.option.ServerName,
|
ServerName: t.option.ServerName,
|
||||||
ClientSessionCache: t.option.ClientXSessionCache,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
xtlsConn := xtls.Client(conn, xtlsConfig)
|
xtlsConn := xtls.Client(conn, xtlsConfig)
|
||||||
if err := xtlsConn.Handshake(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
if err := xtlsConn.HandshakeContext(ctx); err != nil {
|
if err := xtlsConn.HandshakeContext(ctx); err != nil {
|
||||||
|
@ -99,7 +94,6 @@ func (t *Trojan) StreamConn(conn net.Conn) (net.Conn, error) {
|
||||||
MinVersion: tls.VersionTLS12,
|
MinVersion: tls.VersionTLS12,
|
||||||
InsecureSkipVerify: t.option.SkipCertVerify,
|
InsecureSkipVerify: t.option.SkipCertVerify,
|
||||||
ServerName: t.option.ServerName,
|
ServerName: t.option.ServerName,
|
||||||
ClientSessionCache: t.option.ClientSessionCache,
|
|
||||||
}
|
}
|
||||||
tlsConn := tls.Client(conn, tlsConfig)
|
tlsConn := tls.Client(conn, tlsConfig)
|
||||||
if err := tlsConn.Handshake(); err != nil {
|
if err := tlsConn.Handshake(); err != nil {
|
||||||
|
@ -139,7 +133,37 @@ func (t *Trojan) StreamWebsocketConn(conn net.Conn, wsOptions *WebsocketOption)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Trojan) PresetXTLSConn(conn net.Conn) (net.Conn, error) {
|
||||||
|
switch t.option.Flow {
|
||||||
|
case vless.XRO, vless.XRD, vless.XRS:
|
||||||
|
if xtlsConn, ok := conn.(*xtls.Conn); ok {
|
||||||
|
xtlsConn.RPRX = true
|
||||||
|
xtlsConn.SHOW = t.option.FlowShow
|
||||||
|
xtlsConn.MARK = "XTLS"
|
||||||
|
if t.option.Flow == vless.XRS {
|
||||||
|
t.option.Flow = vless.XRD
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.option.Flow == vless.XRD {
|
||||||
|
xtlsConn.DirectMode = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("failed to use %s, maybe \"security\" is not \"xtls\"", t.option.Flow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Trojan) WriteHeader(w io.Writer, command Command, socks5Addr []byte) error {
|
func (t *Trojan) WriteHeader(w io.Writer, command Command, socks5Addr []byte) error {
|
||||||
|
if command == CommandTCP {
|
||||||
|
if t.option.Flow == vless.XRD {
|
||||||
|
command = commandXRD
|
||||||
|
} else if t.option.Flow == vless.XRO {
|
||||||
|
command = commandXRO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buf := pool.GetBuffer()
|
buf := pool.GetBuffer()
|
||||||
defer pool.PutBuffer(buf)
|
defer pool.PutBuffer(buf)
|
||||||
|
|
||||||
|
|
|
@ -35,15 +35,6 @@ type DstAddr struct {
|
||||||
Port uint
|
Port uint
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config of vless
|
|
||||||
type Config struct {
|
|
||||||
UUID string
|
|
||||||
AlterID uint16
|
|
||||||
Security string
|
|
||||||
Port string
|
|
||||||
HostName string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Client is vless connection generator
|
// Client is vless connection generator
|
||||||
type Client struct {
|
type Client struct {
|
||||||
uuid *uuid.UUID
|
uuid *uuid.UUID
|
||||||
|
|
Loading…
Reference in a new issue