Compare commits
24 Commits
v1.14.2
...
dev-vision
Author | SHA1 | Date | |
---|---|---|---|
66f108bf24 | |||
c5444a03ac | |||
9aacfe11e7 | |||
11e0bbebf4 | |||
fc58f80cc8 | |||
abced62f4d | |||
4f27911659 | |||
5bfad04b41 | |||
880664c6ab | |||
8f0c61ed14 | |||
7d524668e0 | |||
75680c5866 | |||
a1d008e6f0 | |||
d5d62a4ffd | |||
b72bd5bb37 | |||
7fecd20a1d | |||
f586f22ce3 | |||
21848d6bf1 | |||
28c57c4144 | |||
4a6ebff473 | |||
5c8d955f61 | |||
baaf509637 | |||
db3e1b9ed5 | |||
1a1e3345f4 |
16
.github/workflows/Delete.yml
vendored
16
.github/workflows/Delete.yml
vendored
@ -1,16 +0,0 @@
|
|||||||
name: Delete old workflow runs
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 0 1 * *'
|
|
||||||
# Run monthly, at 00:00 on the 1st day of month.
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
del_runs:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Delete workflow runs
|
|
||||||
uses: GitRML/delete-workflow-runs@main
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.AUTH_PAT }}
|
|
||||||
repository: ${{ github.repository }}
|
|
||||||
retain_days: 30
|
|
@ -34,7 +34,7 @@ func NewInner(conn net.Conn, dst string, host string) *context.ConnContext {
|
|||||||
metadata := &C.Metadata{}
|
metadata := &C.Metadata{}
|
||||||
metadata.NetWork = C.TCP
|
metadata.NetWork = C.TCP
|
||||||
metadata.Type = C.INNER
|
metadata.Type = C.INNER
|
||||||
metadata.DNSMode = C.DNSMapping
|
metadata.DNSMode = C.DNSNormal
|
||||||
metadata.Host = host
|
metadata.Host = host
|
||||||
metadata.Process = C.ClashName
|
metadata.Process = C.ClashName
|
||||||
if h, port, err := net.SplitHostPort(dst); err == nil {
|
if h, port, err := net.SplitHostPort(dst); err == nil {
|
||||||
|
@ -140,10 +140,15 @@ func (b *Base) DialOptions(opts ...dialer.Option) []dialer.Option {
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.tfo {
|
||||||
|
opts = append(opts, dialer.WithTFO(true))
|
||||||
|
}
|
||||||
|
|
||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
type BasicOption struct {
|
type BasicOption struct {
|
||||||
|
TFO bool `proxy:"tfo,omitempty" group:"tfo,omitempty"`
|
||||||
Interface string `proxy:"interface-name,omitempty" group:"interface-name,omitempty"`
|
Interface string `proxy:"interface-name,omitempty" group:"interface-name,omitempty"`
|
||||||
RoutingMark int `proxy:"routing-mark,omitempty" group:"routing-mark,omitempty"`
|
RoutingMark int `proxy:"routing-mark,omitempty" group:"routing-mark,omitempty"`
|
||||||
IPVersion string `proxy:"ip-version,omitempty" group:"ip-version,omitempty"`
|
IPVersion string `proxy:"ip-version,omitempty" group:"ip-version,omitempty"`
|
||||||
|
@ -170,6 +170,7 @@ func NewHttp(option HttpOption) (*Http, error) {
|
|||||||
name: option.Name,
|
name: option.Name,
|
||||||
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
||||||
tp: C.Http,
|
tp: C.Http,
|
||||||
|
tfo: option.TFO,
|
||||||
iface: option.Interface,
|
iface: option.Interface,
|
||||||
rmark: option.RoutingMark,
|
rmark: option.RoutingMark,
|
||||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||||
|
@ -53,6 +53,9 @@ func (rw *nopConn) Read(b []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (rw *nopConn) Write(b []byte) (int, error) {
|
func (rw *nopConn) Write(b []byte) (int, error) {
|
||||||
|
if len(b) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ package outbound
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
@ -10,10 +9,9 @@ import (
|
|||||||
|
|
||||||
"github.com/Dreamacro/clash/common/structure"
|
"github.com/Dreamacro/clash/common/structure"
|
||||||
"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/shadowtls"
|
|
||||||
obfs "github.com/Dreamacro/clash/transport/simple-obfs"
|
obfs "github.com/Dreamacro/clash/transport/simple-obfs"
|
||||||
|
shadowtls "github.com/Dreamacro/clash/transport/sing-shadowtls"
|
||||||
"github.com/Dreamacro/clash/transport/socks5"
|
"github.com/Dreamacro/clash/transport/socks5"
|
||||||
v2rayObfs "github.com/Dreamacro/clash/transport/v2ray-plugin"
|
v2rayObfs "github.com/Dreamacro/clash/transport/v2ray-plugin"
|
||||||
|
|
||||||
@ -33,8 +31,7 @@ type ShadowSocks struct {
|
|||||||
obfsMode string
|
obfsMode string
|
||||||
obfsOption *simpleObfsOption
|
obfsOption *simpleObfsOption
|
||||||
v2rayOption *v2rayObfs.Option
|
v2rayOption *v2rayObfs.Option
|
||||||
shadowTLSOption *shadowTLSOption
|
shadowTLSOption *shadowtls.ShadowTLSOption
|
||||||
tlsConfig *tls.Config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ShadowSocksOption struct {
|
type ShadowSocksOption struct {
|
||||||
@ -67,14 +64,31 @@ type v2rayObfsOption struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type shadowTLSOption struct {
|
type shadowTLSOption struct {
|
||||||
Password string `obfs:"password"`
|
Password string `obfs:"password"`
|
||||||
Host string `obfs:"host"`
|
Host string `obfs:"host"`
|
||||||
Fingerprint string `obfs:"fingerprint,omitempty"`
|
Fingerprint string `obfs:"fingerprint,omitempty"`
|
||||||
SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"`
|
ClientFingerprint string `obfs:"client-fingerprint,omitempty"`
|
||||||
|
SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"`
|
||||||
|
Version int `obfs:"version,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// StreamConn implements C.ProxyAdapter
|
// StreamConn implements C.ProxyAdapter
|
||||||
func (ss *ShadowSocks) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
func (ss *ShadowSocks) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
||||||
|
switch ss.obfsMode {
|
||||||
|
case shadowtls.Mode:
|
||||||
|
// fix tls handshake not timeout
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
|
||||||
|
defer cancel()
|
||||||
|
var err error
|
||||||
|
c, err = shadowtls.NewShadowTLS(ctx, c, ss.shadowTLSOption)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ss.streamConn(c, metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *ShadowSocks) streamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
||||||
switch ss.obfsMode {
|
switch ss.obfsMode {
|
||||||
case "tls":
|
case "tls":
|
||||||
c = obfs.NewTLSObfs(c, ss.obfsOption.Host)
|
c = obfs.NewTLSObfs(c, ss.obfsOption.Host)
|
||||||
@ -87,13 +101,11 @@ func (ss *ShadowSocks) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
|
return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
|
||||||
}
|
}
|
||||||
case shadowtls.Mode:
|
|
||||||
c = shadowtls.NewShadowTLS(c, ss.shadowTLSOption.Password, ss.tlsConfig)
|
|
||||||
}
|
}
|
||||||
if metadata.NetWork == C.UDP && ss.option.UDPOverTCP {
|
if metadata.NetWork == C.UDP && ss.option.UDPOverTCP {
|
||||||
return ss.method.DialConn(c, M.ParseSocksaddr(uot.UOTMagicAddress+":443"))
|
return ss.method.DialEarlyConn(c, M.ParseSocksaddr(uot.UOTMagicAddress+":443")), nil
|
||||||
}
|
}
|
||||||
return ss.method.DialConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
|
return ss.method.DialEarlyConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialContext implements C.ProxyAdapter
|
// DialContext implements C.ProxyAdapter
|
||||||
@ -113,7 +125,15 @@ func (ss *ShadowSocks) DialContextWithDialer(ctx context.Context, dialer C.Diale
|
|||||||
safeConnClose(c, err)
|
safeConnClose(c, err)
|
||||||
}(c)
|
}(c)
|
||||||
|
|
||||||
c, err = ss.StreamConn(c, metadata)
|
switch ss.obfsMode {
|
||||||
|
case shadowtls.Mode:
|
||||||
|
c, err = shadowtls.NewShadowTLS(ctx, c, ss.shadowTLSOption)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err = ss.streamConn(c, metadata)
|
||||||
return NewConn(c, ss), err
|
return NewConn(c, ss), err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,8 +191,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
|
|||||||
|
|
||||||
var v2rayOption *v2rayObfs.Option
|
var v2rayOption *v2rayObfs.Option
|
||||||
var obfsOption *simpleObfsOption
|
var obfsOption *simpleObfsOption
|
||||||
var shadowTLSOpt *shadowTLSOption
|
var shadowTLSOpt *shadowtls.ShadowTLSOption
|
||||||
var tlsConfig *tls.Config
|
|
||||||
obfsMode := ""
|
obfsMode := ""
|
||||||
|
|
||||||
decoder := structure.NewDecoder(structure.Option{TagName: "obfs", WeaklyTypedInput: true})
|
decoder := structure.NewDecoder(structure.Option{TagName: "obfs", WeaklyTypedInput: true})
|
||||||
@ -210,24 +229,20 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
|
|||||||
}
|
}
|
||||||
} else if option.Plugin == shadowtls.Mode {
|
} else if option.Plugin == shadowtls.Mode {
|
||||||
obfsMode = shadowtls.Mode
|
obfsMode = shadowtls.Mode
|
||||||
shadowTLSOpt = &shadowTLSOption{}
|
opt := &shadowTLSOption{
|
||||||
if err := decoder.Decode(option.PluginOpts, shadowTLSOpt); err != nil {
|
Version: 2,
|
||||||
|
}
|
||||||
|
if err := decoder.Decode(option.PluginOpts, opt); err != nil {
|
||||||
return nil, fmt.Errorf("ss %s initialize shadow-tls-plugin error: %w", addr, err)
|
return nil, fmt.Errorf("ss %s initialize shadow-tls-plugin error: %w", addr, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsConfig = &tls.Config{
|
shadowTLSOpt = &shadowtls.ShadowTLSOption{
|
||||||
NextProtos: shadowtls.DefaultALPN,
|
Password: opt.Password,
|
||||||
MinVersion: tls.VersionTLS12,
|
Host: opt.Host,
|
||||||
InsecureSkipVerify: shadowTLSOpt.SkipCertVerify,
|
Fingerprint: opt.Fingerprint,
|
||||||
ServerName: shadowTLSOpt.Host,
|
ClientFingerprint: opt.ClientFingerprint,
|
||||||
}
|
SkipCertVerify: opt.SkipCertVerify,
|
||||||
|
Version: opt.Version,
|
||||||
if len(shadowTLSOpt.Fingerprint) == 0 {
|
|
||||||
tlsConfig = tlsC.GetGlobalTLSConfig(tlsConfig)
|
|
||||||
} else {
|
|
||||||
if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, shadowTLSOpt.Fingerprint); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,6 +252,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
|
|||||||
addr: addr,
|
addr: addr,
|
||||||
tp: C.Shadowsocks,
|
tp: C.Shadowsocks,
|
||||||
udp: option.UDP,
|
udp: option.UDP,
|
||||||
|
tfo: option.TFO,
|
||||||
iface: option.Interface,
|
iface: option.Interface,
|
||||||
rmark: option.RoutingMark,
|
rmark: option.RoutingMark,
|
||||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||||
@ -248,7 +264,6 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
|
|||||||
v2rayOption: v2rayOption,
|
v2rayOption: v2rayOption,
|
||||||
obfsOption: obfsOption,
|
obfsOption: obfsOption,
|
||||||
shadowTLSOption: shadowTLSOpt,
|
shadowTLSOption: shadowTLSOpt,
|
||||||
tlsConfig: tlsConfig,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,6 +163,7 @@ func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) {
|
|||||||
addr: addr,
|
addr: addr,
|
||||||
tp: C.ShadowsocksR,
|
tp: C.ShadowsocksR,
|
||||||
udp: option.UDP,
|
udp: option.UDP,
|
||||||
|
tfo: option.TFO,
|
||||||
iface: option.Interface,
|
iface: option.Interface,
|
||||||
rmark: option.RoutingMark,
|
rmark: option.RoutingMark,
|
||||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||||
|
@ -167,6 +167,7 @@ func NewSnell(option SnellOption) (*Snell, error) {
|
|||||||
addr: addr,
|
addr: addr,
|
||||||
tp: C.Snell,
|
tp: C.Snell,
|
||||||
udp: option.UDP,
|
udp: option.UDP,
|
||||||
|
tfo: option.TFO,
|
||||||
iface: option.Interface,
|
iface: option.Interface,
|
||||||
rmark: option.RoutingMark,
|
rmark: option.RoutingMark,
|
||||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||||
|
@ -182,6 +182,7 @@ func NewSocks5(option Socks5Option) (*Socks5, error) {
|
|||||||
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
||||||
tp: C.Socks5,
|
tp: C.Socks5,
|
||||||
udp: option.UDP,
|
udp: option.UDP,
|
||||||
|
tfo: option.TFO,
|
||||||
iface: option.Interface,
|
iface: option.Interface,
|
||||||
rmark: option.RoutingMark,
|
rmark: option.RoutingMark,
|
||||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||||
|
@ -250,6 +250,7 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
|
|||||||
addr: addr,
|
addr: addr,
|
||||||
tp: C.Trojan,
|
tp: C.Trojan,
|
||||||
udp: option.UDP,
|
udp: option.UDP,
|
||||||
|
tfo: option.TFO,
|
||||||
iface: option.Interface,
|
iface: option.Interface,
|
||||||
rmark: option.RoutingMark,
|
rmark: option.RoutingMark,
|
||||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||||
|
@ -51,6 +51,7 @@ type TuicOption struct {
|
|||||||
ReceiveWindowConn int `proxy:"recv-window-conn,omitempty"`
|
ReceiveWindowConn int `proxy:"recv-window-conn,omitempty"`
|
||||||
ReceiveWindow int `proxy:"recv-window,omitempty"`
|
ReceiveWindow int `proxy:"recv-window,omitempty"`
|
||||||
DisableMTUDiscovery bool `proxy:"disable-mtu-discovery,omitempty"`
|
DisableMTUDiscovery bool `proxy:"disable-mtu-discovery,omitempty"`
|
||||||
|
SNI string `proxy:"sni,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialContext implements C.ProxyAdapter
|
// DialContext implements C.ProxyAdapter
|
||||||
@ -106,12 +107,14 @@ func (t *Tuic) dialWithDialer(ctx context.Context, dialer C.Dialer) (pc net.Pack
|
|||||||
func NewTuic(option TuicOption) (*Tuic, error) {
|
func NewTuic(option TuicOption) (*Tuic, error) {
|
||||||
addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
|
addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
|
||||||
serverName := option.Server
|
serverName := option.Server
|
||||||
|
|
||||||
tlsConfig := &tls.Config{
|
tlsConfig := &tls.Config{
|
||||||
ServerName: serverName,
|
ServerName: serverName,
|
||||||
InsecureSkipVerify: option.SkipCertVerify,
|
InsecureSkipVerify: option.SkipCertVerify,
|
||||||
MinVersion: tls.VersionTLS13,
|
MinVersion: tls.VersionTLS13,
|
||||||
}
|
}
|
||||||
|
if option.SNI != "" {
|
||||||
|
tlsConfig.ServerName = option.SNI
|
||||||
|
}
|
||||||
|
|
||||||
var bs []byte
|
var bs []byte
|
||||||
var err error
|
var err error
|
||||||
@ -213,6 +216,7 @@ func NewTuic(option TuicOption) (*Tuic, error) {
|
|||||||
udp: true,
|
udp: true,
|
||||||
tfo: option.FastOpen,
|
tfo: option.FastOpen,
|
||||||
iface: option.Interface,
|
iface: option.Interface,
|
||||||
|
rmark: option.RoutingMark,
|
||||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -171,7 +171,7 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
|||||||
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() && !isH2 {
|
if v.isLegacyXTLSEnabled() && !isH2 {
|
||||||
xtlsOpts := vless.XTLSConfig{
|
xtlsOpts := vless.XTLSConfig{
|
||||||
Host: host,
|
Host: host,
|
||||||
SkipCertVerify: v.option.SkipCertVerify,
|
SkipCertVerify: v.option.SkipCertVerify,
|
||||||
@ -206,8 +206,8 @@ func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error)
|
|||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Vless) isXTLSEnabled() bool {
|
func (v *Vless) isLegacyXTLSEnabled() bool {
|
||||||
return v.client.Addons != nil
|
return v.client.Addons != nil && v.client.Addons.Flow != vless.XRV
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialContext implements C.ProxyAdapter
|
// DialContext implements C.ProxyAdapter
|
||||||
@ -479,7 +479,7 @@ func NewVless(option VlessOption) (*Vless, error) {
|
|||||||
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]
|
||||||
switch option.Flow {
|
switch option.Flow {
|
||||||
case vless.XRO, vless.XRD, vless.XRS:
|
case vless.XRO, vless.XRD, vless.XRS, vless.XRV:
|
||||||
addons = &vless.Addons{
|
addons = &vless.Addons{
|
||||||
Flow: option.Flow,
|
Flow: option.Flow,
|
||||||
}
|
}
|
||||||
@ -510,6 +510,7 @@ func NewVless(option VlessOption) (*Vless, error) {
|
|||||||
tp: C.Vless,
|
tp: C.Vless,
|
||||||
udp: option.UDP,
|
udp: option.UDP,
|
||||||
xudp: option.XUDP,
|
xudp: option.XUDP,
|
||||||
|
tfo: option.TFO,
|
||||||
iface: option.Interface,
|
iface: option.Interface,
|
||||||
rmark: option.RoutingMark,
|
rmark: option.RoutingMark,
|
||||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||||
|
@ -213,12 +213,12 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
|||||||
}
|
}
|
||||||
if metadata.NetWork == C.UDP {
|
if metadata.NetWork == C.UDP {
|
||||||
if v.option.XUDP {
|
if v.option.XUDP {
|
||||||
return v.client.DialXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
|
return v.client.DialEarlyXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil
|
||||||
} else {
|
} else {
|
||||||
return v.client.DialPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
|
return v.client.DialEarlyPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return v.client.DialConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
|
return v.client.DialEarlyConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,9 +289,9 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
|
|||||||
}(c)
|
}(c)
|
||||||
|
|
||||||
if v.option.XUDP {
|
if v.option.XUDP {
|
||||||
c, err = v.client.DialXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
|
c = v.client.DialEarlyXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
|
||||||
} else {
|
} else {
|
||||||
c, err = v.client.DialPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
|
c = v.client.DialEarlyPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -387,6 +387,7 @@ func NewVmess(option VmessOption) (*Vmess, error) {
|
|||||||
tp: C.Vmess,
|
tp: C.Vmess,
|
||||||
udp: option.UDP,
|
udp: option.UDP,
|
||||||
xudp: option.XUDP,
|
xudp: option.XUDP,
|
||||||
|
tfo: option.TFO,
|
||||||
iface: option.Interface,
|
iface: option.Interface,
|
||||||
rmark: option.RoutingMark,
|
rmark: option.RoutingMark,
|
||||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||||
|
@ -17,7 +17,7 @@ import (
|
|||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
"github.com/Dreamacro/clash/component/resolver"
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/listener/sing"
|
"github.com/Dreamacro/clash/log"
|
||||||
|
|
||||||
wireguard "github.com/metacubex/sing-wireguard"
|
wireguard "github.com/metacubex/sing-wireguard"
|
||||||
|
|
||||||
@ -174,14 +174,14 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
|
|||||||
}
|
}
|
||||||
outbound.device = device.NewDevice(outbound.tunDevice, outbound.bind, &device.Logger{
|
outbound.device = device.NewDevice(outbound.tunDevice, outbound.bind, &device.Logger{
|
||||||
Verbosef: func(format string, args ...interface{}) {
|
Verbosef: func(format string, args ...interface{}) {
|
||||||
sing.Logger.Debug(fmt.Sprintf(strings.ToLower(format), args...))
|
log.SingLogger.Debug(fmt.Sprintf(strings.ToLower(format), args...))
|
||||||
},
|
},
|
||||||
Errorf: func(format string, args ...interface{}) {
|
Errorf: func(format string, args ...interface{}) {
|
||||||
sing.Logger.Error(fmt.Sprintf(strings.ToLower(format), args...))
|
log.SingLogger.Error(fmt.Sprintf(strings.ToLower(format), args...))
|
||||||
},
|
},
|
||||||
}, option.Workers)
|
}, option.Workers)
|
||||||
if debug.Enabled {
|
if debug.Enabled {
|
||||||
sing.Logger.Trace("created wireguard ipc conf: \n", ipcConf)
|
log.SingLogger.Trace("created wireguard ipc conf: \n", ipcConf)
|
||||||
}
|
}
|
||||||
err = outbound.device.IpcSet(ipcConf)
|
err = outbound.device.IpcSet(ipcConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter/outbound"
|
"github.com/Dreamacro/clash/adapter/outbound"
|
||||||
|
"github.com/Dreamacro/clash/common/callback"
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/constant/provider"
|
"github.com/Dreamacro/clash/constant/provider"
|
||||||
@ -30,11 +31,21 @@ func (f *Fallback) DialContext(ctx context.Context, metadata *C.Metadata, opts .
|
|||||||
c, err := proxy.DialContext(ctx, metadata, f.Base.DialOptions(opts...)...)
|
c, err := proxy.DialContext(ctx, metadata, f.Base.DialOptions(opts...)...)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
c.AppendToChains(f)
|
c.AppendToChains(f)
|
||||||
f.onDialSuccess()
|
|
||||||
} else {
|
} else {
|
||||||
f.onDialFailed(proxy.Type(), err)
|
f.onDialFailed(proxy.Type(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c = &callback.FirstWriteCallBackConn{
|
||||||
|
Conn: c,
|
||||||
|
Callback: func(err error) {
|
||||||
|
if err == nil {
|
||||||
|
f.onDialSuccess()
|
||||||
|
} else {
|
||||||
|
f.onDialFailed(proxy.Type(), err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
return c, err
|
return c, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter/outbound"
|
"github.com/Dreamacro/clash/adapter/outbound"
|
||||||
"github.com/Dreamacro/clash/common/cache"
|
"github.com/Dreamacro/clash/common/cache"
|
||||||
|
"github.com/Dreamacro/clash/common/callback"
|
||||||
"github.com/Dreamacro/clash/common/murmur3"
|
"github.com/Dreamacro/clash/common/murmur3"
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
@ -83,17 +84,24 @@ func jumpHash(key uint64, buckets int32) int32 {
|
|||||||
// DialContext implements C.ProxyAdapter
|
// DialContext implements C.ProxyAdapter
|
||||||
func (lb *LoadBalance) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (c C.Conn, err error) {
|
func (lb *LoadBalance) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (c C.Conn, err error) {
|
||||||
proxy := lb.Unwrap(metadata, true)
|
proxy := lb.Unwrap(metadata, true)
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err == nil {
|
|
||||||
c.AppendToChains(lb)
|
|
||||||
lb.onDialSuccess()
|
|
||||||
} else {
|
|
||||||
lb.onDialFailed(proxy.Type(), err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
c, err = proxy.DialContext(ctx, metadata, lb.Base.DialOptions(opts...)...)
|
c, err = proxy.DialContext(ctx, metadata, lb.Base.DialOptions(opts...)...)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
c.AppendToChains(lb)
|
||||||
|
} else {
|
||||||
|
lb.onDialFailed(proxy.Type(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c = &callback.FirstWriteCallBackConn{
|
||||||
|
Conn: c,
|
||||||
|
Callback: func(err error) {
|
||||||
|
if err == nil {
|
||||||
|
lb.onDialSuccess()
|
||||||
|
} else {
|
||||||
|
lb.onDialFailed(proxy.Type(), err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter/outbound"
|
"github.com/Dreamacro/clash/adapter/outbound"
|
||||||
|
"github.com/Dreamacro/clash/common/callback"
|
||||||
"github.com/Dreamacro/clash/common/singledo"
|
"github.com/Dreamacro/clash/common/singledo"
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
@ -38,10 +39,20 @@ func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts ..
|
|||||||
c, err = proxy.DialContext(ctx, metadata, u.Base.DialOptions(opts...)...)
|
c, err = proxy.DialContext(ctx, metadata, u.Base.DialOptions(opts...)...)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
c.AppendToChains(u)
|
c.AppendToChains(u)
|
||||||
u.onDialSuccess()
|
|
||||||
} else {
|
} else {
|
||||||
u.onDialFailed(proxy.Type(), err)
|
u.onDialFailed(proxy.Type(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c = &callback.FirstWriteCallBackConn{
|
||||||
|
Conn: c,
|
||||||
|
Callback: func(err error) {
|
||||||
|
if err == nil {
|
||||||
|
u.onDialSuccess()
|
||||||
|
} else {
|
||||||
|
u.onDialFailed(proxy.Type(), err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
return c, err
|
return c, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
type Buffer = buf.Buffer
|
type Buffer = buf.Buffer
|
||||||
|
|
||||||
|
var StackNew = buf.StackNew
|
||||||
var StackNewSize = buf.StackNewSize
|
var StackNewSize = buf.StackNewSize
|
||||||
var KeepAlive = common.KeepAlive
|
var KeepAlive = common.KeepAlive
|
||||||
|
|
||||||
|
25
common/callback/callback.go
Normal file
25
common/callback/callback.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package callback
|
||||||
|
|
||||||
|
import (
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FirstWriteCallBackConn struct {
|
||||||
|
C.Conn
|
||||||
|
Callback func(error)
|
||||||
|
written bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FirstWriteCallBackConn) Write(b []byte) (n int, err error) {
|
||||||
|
defer func() {
|
||||||
|
if !c.written {
|
||||||
|
c.written = true
|
||||||
|
c.Callback(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return c.Conn.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FirstWriteCallBackConn) Upstream() any {
|
||||||
|
return c.Conn
|
||||||
|
}
|
@ -12,13 +12,14 @@ var _ ExtendedConn = (*BufferedConn)(nil)
|
|||||||
type BufferedConn struct {
|
type BufferedConn struct {
|
||||||
r *bufio.Reader
|
r *bufio.Reader
|
||||||
ExtendedConn
|
ExtendedConn
|
||||||
|
peeked bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBufferedConn(c net.Conn) *BufferedConn {
|
func NewBufferedConn(c net.Conn) *BufferedConn {
|
||||||
if bc, ok := c.(*BufferedConn); ok {
|
if bc, ok := c.(*BufferedConn); ok {
|
||||||
return bc
|
return bc
|
||||||
}
|
}
|
||||||
return &BufferedConn{bufio.NewReader(c), NewExtendedConn(c)}
|
return &BufferedConn{bufio.NewReader(c), NewExtendedConn(c), false}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reader returns the internal bufio.Reader.
|
// Reader returns the internal bufio.Reader.
|
||||||
@ -26,11 +27,20 @@ func (c *BufferedConn) Reader() *bufio.Reader {
|
|||||||
return c.r
|
return c.r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *BufferedConn) Peeked() bool {
|
||||||
|
return c.peeked
|
||||||
|
}
|
||||||
|
|
||||||
// Peek returns the next n bytes without advancing the reader.
|
// Peek returns the next n bytes without advancing the reader.
|
||||||
func (c *BufferedConn) Peek(n int) ([]byte, error) {
|
func (c *BufferedConn) Peek(n int) ([]byte, error) {
|
||||||
|
c.peeked = true
|
||||||
return c.r.Peek(n)
|
return c.r.Peek(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *BufferedConn) Discard(n int) (discarded int, err error) {
|
||||||
|
return c.r.Discard(n)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *BufferedConn) Read(p []byte) (int, error) {
|
func (c *BufferedConn) Read(p []byte) (int, error) {
|
||||||
return c.r.Read(p)
|
return c.r.Read(p)
|
||||||
}
|
}
|
||||||
|
@ -37,14 +37,25 @@ func bindControl(ifaceIdx int) controlFn {
|
|||||||
var innerErr error
|
var innerErr error
|
||||||
err = c.Control(func(fd uintptr) {
|
err = c.Control(func(fd uintptr) {
|
||||||
handle := syscall.Handle(fd)
|
handle := syscall.Handle(fd)
|
||||||
|
bind6err := bind6(handle, ifaceIdx)
|
||||||
|
bind4err := bind4(handle, ifaceIdx)
|
||||||
switch network {
|
switch network {
|
||||||
case "tcp6", "udp6":
|
case "ip6", "tcp6":
|
||||||
innerErr = bind6(handle, ifaceIdx)
|
innerErr = bind6err
|
||||||
_ = bind4(handle, ifaceIdx)
|
case "ip4", "tcp4", "udp4":
|
||||||
default:
|
innerErr = bind4err
|
||||||
innerErr = bind4(handle, ifaceIdx)
|
case "udp6":
|
||||||
// try bind ipv6, if failed, ignore. it's a workaround for windows disable interface ipv6
|
// golang will set network to udp6 when listenUDP on wildcard ip (eg: ":0", "")
|
||||||
_ = bind6(handle, ifaceIdx)
|
if (!addrPort.Addr().IsValid() || addrPort.Addr().IsUnspecified()) && bind6err != nil {
|
||||||
|
// try bind ipv6, if failed, ignore. it's a workaround for windows disable interface ipv6
|
||||||
|
if bind4err != nil {
|
||||||
|
innerErr = bind6err
|
||||||
|
} else {
|
||||||
|
innerErr = bind4err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
innerErr = bind6err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -65,18 +65,7 @@ func DialContext(ctx context.Context, network, address string, options ...Option
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ListenPacket(ctx context.Context, network, address string, options ...Option) (net.PacketConn, error) {
|
func ListenPacket(ctx context.Context, network, address string, options ...Option) (net.PacketConn, error) {
|
||||||
cfg := &option{
|
cfg := applyOptions(options...)
|
||||||
interfaceName: DefaultInterface.Load(),
|
|
||||||
routingMark: int(DefaultRoutingMark.Load()),
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, o := range DefaultOptions {
|
|
||||||
o(cfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, o := range options {
|
|
||||||
o(cfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
lc := &net.ListenConfig{}
|
lc := &net.ListenConfig{}
|
||||||
if cfg.interfaceName != "" {
|
if cfg.interfaceName != "" {
|
||||||
@ -129,7 +118,40 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po
|
|||||||
return nil, ErrorDisableIPv6
|
return nil, ErrorDisableIPv6
|
||||||
}
|
}
|
||||||
|
|
||||||
return dialer.DialContext(ctx, network, net.JoinHostPort(destination.String(), port))
|
address := net.JoinHostPort(destination.String(), port)
|
||||||
|
if opt.tfo {
|
||||||
|
return dialTFO(ctx, *dialer, network, address)
|
||||||
|
}
|
||||||
|
return dialer.DialContext(ctx, network, address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func singleDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) {
|
||||||
|
host, port, err := net.SplitHostPort(address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ip netip.Addr
|
||||||
|
switch network {
|
||||||
|
case "tcp4", "udp4":
|
||||||
|
if opt.resolver == nil {
|
||||||
|
ip, err = resolver.ResolveIPv4ProxyServerHost(ctx, host)
|
||||||
|
} else {
|
||||||
|
ip, err = resolver.ResolveIPv4WithResolver(ctx, host, opt.resolver)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if opt.resolver == nil {
|
||||||
|
ip, err = resolver.ResolveIPv6ProxyServerHost(ctx, host)
|
||||||
|
} else {
|
||||||
|
ip, err = resolver.ResolveIPv6WithResolver(ctx, host, opt.resolver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("dns resolve failed:%w", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dialContext(ctx, network, ip, port, opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func dualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) {
|
func dualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) {
|
||||||
@ -178,6 +200,7 @@ func dualStackDialContext(ctx context.Context, network, address string, opt *opt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if result.error != nil {
|
if result.error != nil {
|
||||||
|
result.error = fmt.Errorf("dns resolve failed:%w", result.error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
result.resolved = true
|
result.resolved = true
|
||||||
@ -225,26 +248,6 @@ func dualStackDialContext(ctx context.Context, network, address string, opt *opt
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func concurrentDualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) {
|
|
||||||
host, port, err := net.SplitHostPort(address)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var ips []netip.Addr
|
|
||||||
if opt.resolver != nil {
|
|
||||||
ips, err = resolver.LookupIPWithResolver(ctx, host, opt.resolver)
|
|
||||||
} else {
|
|
||||||
ips, err = resolver.LookupIPProxyServerHost(ctx, host)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return concurrentDialContext(ctx, network, ips, port, opt)
|
|
||||||
}
|
|
||||||
|
|
||||||
func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) {
|
func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) {
|
||||||
returned := make(chan struct{})
|
returned := make(chan struct{})
|
||||||
defer close(returned)
|
defer close(returned)
|
||||||
@ -356,77 +359,51 @@ func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr
|
|||||||
return nil, finalError
|
return nil, finalError
|
||||||
}
|
}
|
||||||
|
|
||||||
func singleDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) {
|
|
||||||
host, port, err := net.SplitHostPort(address)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var ip netip.Addr
|
|
||||||
switch network {
|
|
||||||
case "tcp4", "udp4":
|
|
||||||
if opt.resolver == nil {
|
|
||||||
ip, err = resolver.ResolveIPv4ProxyServerHost(ctx, host)
|
|
||||||
} else {
|
|
||||||
ip, err = resolver.ResolveIPv4WithResolver(ctx, host, opt.resolver)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
if opt.resolver == nil {
|
|
||||||
ip, err = resolver.ResolveIPv6ProxyServerHost(ctx, host)
|
|
||||||
} else {
|
|
||||||
ip, err = resolver.ResolveIPv6WithResolver(ctx, host, opt.resolver)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return dialContext(ctx, network, ip, port, opt)
|
|
||||||
}
|
|
||||||
|
|
||||||
func concurrentSingleDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) {
|
func concurrentSingleDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) {
|
||||||
switch network {
|
|
||||||
case "tcp4", "udp4":
|
|
||||||
return concurrentIPv4DialContext(ctx, network, address, opt)
|
|
||||||
default:
|
|
||||||
return concurrentIPv6DialContext(ctx, network, address, opt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func concurrentIPv4DialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) {
|
|
||||||
host, port, err := net.SplitHostPort(address)
|
host, port, err := net.SplitHostPort(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var ips []netip.Addr
|
var ips []netip.Addr
|
||||||
if opt.resolver == nil {
|
switch network {
|
||||||
ips, err = resolver.LookupIPv4ProxyServerHost(ctx, host)
|
case "tcp4", "udp4":
|
||||||
} else {
|
if opt.resolver == nil {
|
||||||
ips, err = resolver.LookupIPv4WithResolver(ctx, host, opt.resolver)
|
ips, err = resolver.LookupIPv4ProxyServerHost(ctx, host)
|
||||||
|
} else {
|
||||||
|
ips, err = resolver.LookupIPv4WithResolver(ctx, host, opt.resolver)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if opt.resolver == nil {
|
||||||
|
ips, err = resolver.LookupIPv6ProxyServerHost(ctx, host)
|
||||||
|
} else {
|
||||||
|
ips, err = resolver.LookupIPv6WithResolver(ctx, host, opt.resolver)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
err = fmt.Errorf("dns resolve failed:%w", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return concurrentDialContext(ctx, network, ips, port, opt)
|
return concurrentDialContext(ctx, network, ips, port, opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func concurrentIPv6DialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) {
|
func concurrentDualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) {
|
||||||
host, port, err := net.SplitHostPort(address)
|
host, port, err := net.SplitHostPort(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var ips []netip.Addr
|
var ips []netip.Addr
|
||||||
if opt.resolver == nil {
|
if opt.resolver != nil {
|
||||||
ips, err = resolver.LookupIPv6ProxyServerHost(ctx, host)
|
ips, err = resolver.LookupIPWithResolver(ctx, host, opt.resolver)
|
||||||
} else {
|
} else {
|
||||||
ips, err = resolver.LookupIPv6WithResolver(ctx, host, opt.resolver)
|
ips, err = resolver.LookupIPProxyServerHost(ctx, host)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
err = fmt.Errorf("dns resolve failed:%w", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ type option struct {
|
|||||||
routingMark int
|
routingMark int
|
||||||
network int
|
network int
|
||||||
prefer int
|
prefer int
|
||||||
|
tfo bool
|
||||||
resolver resolver.Resolver
|
resolver resolver.Resolver
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,6 +70,12 @@ func WithOnlySingleStack(isIPv4 bool) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithTFO(tfo bool) Option {
|
||||||
|
return func(opt *option) {
|
||||||
|
opt.tfo = tfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func WithOption(o option) Option {
|
func WithOption(o option) Option {
|
||||||
return func(opt *option) {
|
return func(opt *option) {
|
||||||
*opt = o
|
*opt = o
|
||||||
|
119
component/dialer/tfo.go
Normal file
119
component/dialer/tfo.go
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/sagernet/tfo-go"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tfoConn struct {
|
||||||
|
net.Conn
|
||||||
|
closed bool
|
||||||
|
dialed chan bool
|
||||||
|
cancel context.CancelFunc
|
||||||
|
ctx context.Context
|
||||||
|
dialFn func(ctx context.Context, earlyData []byte) (net.Conn, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *tfoConn) Dial(earlyData []byte) (err error) {
|
||||||
|
c.Conn, err = c.dialFn(c.ctx, earlyData)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.dialed <- true
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *tfoConn) Read(b []byte) (n int, err error) {
|
||||||
|
if c.closed {
|
||||||
|
return 0, io.ErrClosedPipe
|
||||||
|
}
|
||||||
|
if c.Conn == nil {
|
||||||
|
select {
|
||||||
|
case <-c.ctx.Done():
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
case <-c.dialed:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.Conn.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *tfoConn) Write(b []byte) (n int, err error) {
|
||||||
|
if c.closed {
|
||||||
|
return 0, io.ErrClosedPipe
|
||||||
|
}
|
||||||
|
if c.Conn == nil {
|
||||||
|
if err := c.Dial(b); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Conn.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *tfoConn) Close() error {
|
||||||
|
c.closed = true
|
||||||
|
c.cancel()
|
||||||
|
if c.Conn == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.Conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *tfoConn) LocalAddr() net.Addr {
|
||||||
|
if c.Conn == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.Conn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *tfoConn) RemoteAddr() net.Addr {
|
||||||
|
if c.Conn == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.Conn.RemoteAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *tfoConn) SetDeadline(t time.Time) error {
|
||||||
|
if err := c.SetReadDeadline(t); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *tfoConn) SetReadDeadline(t time.Time) error {
|
||||||
|
if c.Conn == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.Conn.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *tfoConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
if c.Conn == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.Conn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *tfoConn) Upstream() any {
|
||||||
|
if c.Conn == nil { // ensure return a nil interface not an interface with nil value
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func dialTFO(ctx context.Context, netDialer net.Dialer, network, address string) (net.Conn, error) {
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
dialer := tfo.Dialer{Dialer: netDialer, DisableTFO: false}
|
||||||
|
return &tfoConn{
|
||||||
|
dialed: make(chan bool, 1),
|
||||||
|
cancel: cancel,
|
||||||
|
ctx: ctx,
|
||||||
|
dialFn: func(ctx context.Context, earlyData []byte) (net.Conn, error) {
|
||||||
|
return dialer.DialContext(ctx, network, address, earlyData)
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
@ -36,12 +36,7 @@ type SnifferDispatcher struct {
|
|||||||
parsePureIp bool
|
parsePureIp bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sd *SnifferDispatcher) TCPSniff(conn net.Conn, metadata *C.Metadata) {
|
func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) {
|
||||||
bufConn, ok := conn.(*N.BufferedConn)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (metadata.Host == "" && sd.parsePureIp) || sd.forceDomain.Search(metadata.Host) != nil || (metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping) {
|
if (metadata.Host == "" && sd.parsePureIp) || sd.forceDomain.Search(metadata.Host) != nil || (metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping) {
|
||||||
port, err := strconv.ParseUint(metadata.DstPort, 10, 16)
|
port, err := strconv.ParseUint(metadata.DstPort, 10, 16)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -74,7 +69,7 @@ func (sd *SnifferDispatcher) TCPSniff(conn net.Conn, metadata *C.Metadata) {
|
|||||||
}
|
}
|
||||||
sd.rwMux.RUnlock()
|
sd.rwMux.RUnlock()
|
||||||
|
|
||||||
if host, err := sd.sniffDomain(bufConn, metadata); err != nil {
|
if host, err := sd.sniffDomain(conn, metadata); err != nil {
|
||||||
sd.cacheSniffFailed(metadata)
|
sd.cacheSniffFailed(metadata)
|
||||||
log.Debugln("[Sniffer] All sniffing sniff failed with from [%s:%s] to [%s:%s]", metadata.SrcIP, metadata.SrcPort, metadata.String(), metadata.DstPort)
|
log.Debugln("[Sniffer] All sniffing sniff failed with from [%s:%s] to [%s:%s]", metadata.SrcIP, metadata.SrcPort, metadata.String(), metadata.DstPort)
|
||||||
return
|
return
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
|
|
||||||
"github.com/mroth/weightedrand/v2"
|
"github.com/mroth/weightedrand/v2"
|
||||||
utls "github.com/refraction-networking/utls"
|
utls "github.com/sagernet/utls"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UConn struct {
|
type UConn struct {
|
||||||
|
@ -447,7 +447,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
|
|||||||
}
|
}
|
||||||
config.General = general
|
config.General = general
|
||||||
|
|
||||||
dialer.DefaultInterface.Store(config.General.Interface)
|
|
||||||
proxies, providers, err := parseProxies(rawCfg)
|
proxies, providers, err := parseProxies(rawCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -3,6 +3,8 @@ package constant
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
N "github.com/Dreamacro/clash/common/net"
|
||||||
|
|
||||||
"github.com/gofrs/uuid"
|
"github.com/gofrs/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,7 +15,7 @@ type PlainContext interface {
|
|||||||
type ConnContext interface {
|
type ConnContext interface {
|
||||||
PlainContext
|
PlainContext
|
||||||
Metadata() *Metadata
|
Metadata() *Metadata
|
||||||
Conn() net.Conn
|
Conn() *N.BufferedConn
|
||||||
}
|
}
|
||||||
|
|
||||||
type PacketConnContext interface {
|
type PacketConnContext interface {
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
type ConnContext struct {
|
type ConnContext struct {
|
||||||
id uuid.UUID
|
id uuid.UUID
|
||||||
metadata *C.Metadata
|
metadata *C.Metadata
|
||||||
conn net.Conn
|
conn *N.BufferedConn
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConnContext(conn net.Conn, metadata *C.Metadata) *ConnContext {
|
func NewConnContext(conn net.Conn, metadata *C.Metadata) *ConnContext {
|
||||||
@ -36,6 +36,6 @@ func (c *ConnContext) Metadata() *C.Metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Conn implement C.ConnContext Conn
|
// Conn implement C.ConnContext Conn
|
||||||
func (c *ConnContext) Conn() net.Conn {
|
func (c *ConnContext) Conn() *N.BufferedConn {
|
||||||
return c.conn
|
return c.conn
|
||||||
}
|
}
|
||||||
|
34
go.mod
34
go.mod
@ -10,7 +10,7 @@ require (
|
|||||||
github.com/go-chi/chi/v5 v5.0.8
|
github.com/go-chi/chi/v5 v5.0.8
|
||||||
github.com/go-chi/cors v1.2.1
|
github.com/go-chi/cors v1.2.1
|
||||||
github.com/go-chi/render v1.0.2
|
github.com/go-chi/render v1.0.2
|
||||||
github.com/gofrs/uuid v4.3.1+incompatible
|
github.com/gofrs/uuid v4.4.0+incompatible
|
||||||
github.com/google/gopacket v1.1.19
|
github.com/google/gopacket v1.1.19
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
github.com/hashicorp/golang-lru v0.5.4
|
github.com/hashicorp/golang-lru v0.5.4
|
||||||
@ -20,16 +20,17 @@ require (
|
|||||||
github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7
|
github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7
|
||||||
github.com/metacubex/quic-go v0.32.0
|
github.com/metacubex/quic-go v0.32.0
|
||||||
github.com/metacubex/sing-shadowsocks v0.1.1-0.20230202072246-e2bef5f088c7
|
github.com/metacubex/sing-shadowsocks v0.1.1-0.20230202072246-e2bef5f088c7
|
||||||
github.com/metacubex/sing-tun v0.1.1-0.20230213124625-28d27a0c236b
|
github.com/metacubex/sing-tun v0.1.1-0.20230222113101-fbfa2dab826d
|
||||||
github.com/metacubex/sing-wireguard v0.0.0-20230213124601-d04406a109b4
|
github.com/metacubex/sing-wireguard v0.0.0-20230213124601-d04406a109b4
|
||||||
github.com/miekg/dns v1.1.50
|
github.com/miekg/dns v1.1.50
|
||||||
github.com/mroth/weightedrand/v2 v2.0.0
|
github.com/mroth/weightedrand/v2 v2.0.0
|
||||||
github.com/oschwald/geoip2-golang v1.8.0
|
github.com/oschwald/geoip2-golang v1.8.0
|
||||||
github.com/refraction-networking/utls v1.2.0
|
|
||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
|
||||||
github.com/sagernet/sing v0.1.7-0.20230207063819-27d2950cdbe9
|
github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b
|
||||||
github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb
|
github.com/sagernet/sing-shadowtls v0.0.0-20230221130515-dac782ca098e
|
||||||
|
github.com/sagernet/sing-vmess v0.1.2
|
||||||
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d
|
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d
|
||||||
|
github.com/sagernet/utls v0.0.0-20230220130002-c08891932056
|
||||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c
|
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c
|
||||||
github.com/samber/lo v1.37.0
|
github.com/samber/lo v1.37.0
|
||||||
github.com/sirupsen/logrus v1.9.0
|
github.com/sirupsen/logrus v1.9.0
|
||||||
@ -38,19 +39,19 @@ require (
|
|||||||
go.etcd.io/bbolt v1.3.6
|
go.etcd.io/bbolt v1.3.6
|
||||||
go.uber.org/atomic v1.10.0
|
go.uber.org/atomic v1.10.0
|
||||||
go.uber.org/automaxprocs v1.5.1
|
go.uber.org/automaxprocs v1.5.1
|
||||||
golang.org/x/crypto v0.5.0
|
golang.org/x/crypto v0.6.0
|
||||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db
|
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db
|
||||||
golang.org/x/net v0.5.0
|
golang.org/x/net v0.6.0
|
||||||
golang.org/x/sync v0.1.0
|
golang.org/x/sync v0.1.0
|
||||||
golang.org/x/sys v0.4.0
|
golang.org/x/sys v0.5.0
|
||||||
google.golang.org/protobuf v1.28.1
|
google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
lukechampine.com/blake3 v1.1.7
|
lukechampine.com/blake3 v1.1.7
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/ajg/form v1.5.1 // indirect
|
github.com/ajg/form v1.5.1 // indirect
|
||||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||||
@ -59,10 +60,10 @@ require (
|
|||||||
github.com/google/go-cmp v0.5.9 // indirect
|
github.com/google/go-cmp v0.5.9 // indirect
|
||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||||
github.com/josharian/native v1.1.0 // indirect
|
github.com/josharian/native v1.1.0 // indirect
|
||||||
github.com/klauspost/compress v1.15.12 // indirect
|
github.com/klauspost/compress v1.15.15 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
|
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
|
||||||
github.com/mdlayher/socket v0.4.0 // indirect
|
github.com/mdlayher/socket v0.4.0 // indirect
|
||||||
github.com/metacubex/gvisor v0.0.0-20230213124051-7a16c835d80e // indirect
|
github.com/metacubex/gvisor v0.0.0-20230222112937-bdbcd206ec65 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
|
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
|
||||||
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
|
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
@ -70,14 +71,13 @@ require (
|
|||||||
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
||||||
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
|
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
|
||||||
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
|
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
|
||||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e // indirect
|
|
||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
||||||
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f // indirect
|
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f // indirect
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||||
golang.org/x/mod v0.6.0 // indirect
|
golang.org/x/mod v0.7.0 // indirect
|
||||||
golang.org/x/text v0.6.0 // indirect
|
golang.org/x/text v0.7.0 // indirect
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
|
||||||
golang.org/x/tools v0.2.0 // indirect
|
golang.org/x/tools v0.5.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace go.uber.org/atomic v1.10.0 => github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370
|
replace go.uber.org/atomic v1.10.0 => github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370
|
||||||
|
68
go.sum
68
go.sum
@ -2,8 +2,8 @@ github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmH
|
|||||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
||||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
@ -28,8 +28,8 @@ github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
|
|||||||
github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI=
|
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
||||||
github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
@ -67,8 +67,8 @@ github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGu
|
|||||||
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
|
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
|
||||||
github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=
|
github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM=
|
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
|
||||||
github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
|
github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||||
@ -87,14 +87,14 @@ github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZ
|
|||||||
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
|
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
|
||||||
github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw=
|
github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw=
|
||||||
github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc=
|
github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc=
|
||||||
github.com/metacubex/gvisor v0.0.0-20230213124051-7a16c835d80e h1:j4j2dlV2d//FAsQlRUriH6nvv36AEAhECbNy7narf1M=
|
github.com/metacubex/gvisor v0.0.0-20230222112937-bdbcd206ec65 h1:WUINdCB/UvSX9I+wN+y5xVEisPrXA73rxkoHK5DMyZs=
|
||||||
github.com/metacubex/gvisor v0.0.0-20230213124051-7a16c835d80e/go.mod h1:abc7OdNmWlhcNHz84ECEosd5ND5pnWQmD8W55p/4cuc=
|
github.com/metacubex/gvisor v0.0.0-20230222112937-bdbcd206ec65/go.mod h1:e3lCxh3TozKMWAsYTC7nBVnepAxPL/sNyScUFvmEoec=
|
||||||
github.com/metacubex/quic-go v0.32.0 h1:dSD8LB4MSeBuD4otd8y1DUZcRdDcEB0Ax5esPOqn2Hw=
|
github.com/metacubex/quic-go v0.32.0 h1:dSD8LB4MSeBuD4otd8y1DUZcRdDcEB0Ax5esPOqn2Hw=
|
||||||
github.com/metacubex/quic-go v0.32.0/go.mod h1:yParIzDYUd/t/pzFlDtZKhnvSqbUu0bPChlKEGmJStA=
|
github.com/metacubex/quic-go v0.32.0/go.mod h1:yParIzDYUd/t/pzFlDtZKhnvSqbUu0bPChlKEGmJStA=
|
||||||
github.com/metacubex/sing-shadowsocks v0.1.1-0.20230202072246-e2bef5f088c7 h1:MNCGIpXhxXn9ck5bxfm/cW9Nr2FGQ5cakcGK0yKZcak=
|
github.com/metacubex/sing-shadowsocks v0.1.1-0.20230202072246-e2bef5f088c7 h1:MNCGIpXhxXn9ck5bxfm/cW9Nr2FGQ5cakcGK0yKZcak=
|
||||||
github.com/metacubex/sing-shadowsocks v0.1.1-0.20230202072246-e2bef5f088c7/go.mod h1:8pBSYDKVxTtqUtGZyEh4ZpFJXwP6wBVVKrs6oQiOwmQ=
|
github.com/metacubex/sing-shadowsocks v0.1.1-0.20230202072246-e2bef5f088c7/go.mod h1:8pBSYDKVxTtqUtGZyEh4ZpFJXwP6wBVVKrs6oQiOwmQ=
|
||||||
github.com/metacubex/sing-tun v0.1.1-0.20230213124625-28d27a0c236b h1:ZF/oNrSCaxIFoZmFQCiUx67t9aENZjyuqw2n4zw3L2o=
|
github.com/metacubex/sing-tun v0.1.1-0.20230222113101-fbfa2dab826d h1:oMzkrEoBdwn2/Vyu0n6/LAmvjxqsyFs+f2kqeg7kI8U=
|
||||||
github.com/metacubex/sing-tun v0.1.1-0.20230213124625-28d27a0c236b/go.mod h1:TjuaYuR/g1MaY3um89xTfRNt61FJ2IcI/m5zD8QBxw4=
|
github.com/metacubex/sing-tun v0.1.1-0.20230222113101-fbfa2dab826d/go.mod h1:WmbtxVPpJulKoQGwfnBMk4KSWzZ68sE/myTrQeN/5GE=
|
||||||
github.com/metacubex/sing-wireguard v0.0.0-20230213124601-d04406a109b4 h1:d96mCF/LYyC9kULd2xwcXfP0Jd8klrOngmRxuUIZg/8=
|
github.com/metacubex/sing-wireguard v0.0.0-20230213124601-d04406a109b4 h1:d96mCF/LYyC9kULd2xwcXfP0Jd8klrOngmRxuUIZg/8=
|
||||||
github.com/metacubex/sing-wireguard v0.0.0-20230213124601-d04406a109b4/go.mod h1:p2VpJuxRefgVMxc8cmatMGSFNvYbjMYMsXJOe7qFstw=
|
github.com/metacubex/sing-wireguard v0.0.0-20230213124601-d04406a109b4/go.mod h1:p2VpJuxRefgVMxc8cmatMGSFNvYbjMYMsXJOe7qFstw=
|
||||||
github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370 h1:UkViS4DCESAUEYgbIEQdD02hyMacFt6Dny+1MOJtNIo=
|
github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370 h1:UkViS4DCESAUEYgbIEQdD02hyMacFt6Dny+1MOJtNIo=
|
||||||
@ -121,22 +121,22 @@ github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4
|
|||||||
github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||||
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
||||||
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||||
github.com/refraction-networking/utls v1.2.0 h1:U5f8wkij2NVinfLuJdFP3gCMwIHs+EzvhxmYdXgiapo=
|
|
||||||
github.com/refraction-networking/utls v1.2.0/go.mod h1:NPq+cVqzH7D1BeOkmOcb5O/8iVewAsiVt2x1/eO0hgQ=
|
|
||||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e h1:5CFRo8FJbCuf5s/eTBdZpmMbn8Fe2eSMLNAYfKanA34=
|
|
||||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e/go.mod h1:qbt0dWObotCfcjAJJ9AxtFPNSDUfZF+6dCpgKEOBn/g=
|
|
||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||||
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||||
github.com/sagernet/sing v0.1.7-0.20230207063819-27d2950cdbe9 h1:qnXh4RjHsNjdZXkfbqwVqAzYUfc160gfkS5gepmsA+A=
|
github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b h1:Ji2AfGlc4j9AitobOx4k3BCj7eS5nSxL1cgaL81zvlo=
|
||||||
github.com/sagernet/sing v0.1.7-0.20230207063819-27d2950cdbe9/go.mod h1:JLSXsPTGRJFo/3X7EcAOCUgJH2/gAoxSJgBsnCZRp/w=
|
github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||||
github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb h1:oyd3w17fXNmWVYFUe17YVHJW5CLW9X2mxJFDP/IWrAM=
|
github.com/sagernet/sing-shadowtls v0.0.0-20230221130515-dac782ca098e h1:S1fd0kB9aEU68dd269AQy783sUlFu/2fSh/4YYVJ/Oc=
|
||||||
github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb/go.mod h1:9KkmnQzTL4Gvv8U2TRAH2BOITCGsGPpHtUPP5sxn5sY=
|
github.com/sagernet/sing-shadowtls v0.0.0-20230221130515-dac782ca098e/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc=
|
||||||
|
github.com/sagernet/sing-vmess v0.1.2 h1:RbOZNAId2LrCai8epMoQXlf0XTrou0bfcw08hNBg6lM=
|
||||||
|
github.com/sagernet/sing-vmess v0.1.2/go.mod h1:9NSj8mZTx1JIY/HF9LaYRppUsVkysIN5tEFpNZujXxY=
|
||||||
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d h1:trP/l6ZPWvQ/5Gv99Z7/t/v8iYy06akDMejxW1sznUk=
|
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d h1:trP/l6ZPWvQ/5Gv99Z7/t/v8iYy06akDMejxW1sznUk=
|
||||||
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d/go.mod h1:jk6Ii8Y3En+j2KQDLgdgQGwb3M6y7EL567jFnGYhN9g=
|
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d/go.mod h1:jk6Ii8Y3En+j2KQDLgdgQGwb3M6y7EL567jFnGYhN9g=
|
||||||
|
github.com/sagernet/utls v0.0.0-20230220130002-c08891932056 h1:gDXi/0uYe8dA48UyUI1LM2la5QYN0IvsDvR2H2+kFnA=
|
||||||
|
github.com/sagernet/utls v0.0.0-20230220130002-c08891932056/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM=
|
||||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
|
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
|
||||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI=
|
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI=
|
||||||
github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw=
|
github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw=
|
||||||
@ -169,15 +169,15 @@ go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66
|
|||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
|
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
|
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
|
||||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
|
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
|
||||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
@ -190,8 +190,8 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY
|
|||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
|
golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
|
||||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||||
@ -220,31 +220,31 @@ golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
|
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
|
golang.org/x/tools v0.5.0 h1:+bSpV5HIeWkuvgaMfI3UmKRThoTA5ODJTUd8T17NO+4=
|
||||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d h1:qp0AnQCvRCMlu9jBjtdbTaaEmThIgZOrbVyDEOcmKhQ=
|
||||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
@ -81,13 +81,13 @@ func ApplyConfig(cfg *config.Config, force bool) {
|
|||||||
updateRules(cfg.Rules, cfg.SubRules, cfg.RuleProviders)
|
updateRules(cfg.Rules, cfg.SubRules, cfg.RuleProviders)
|
||||||
updateSniffer(cfg.Sniffer)
|
updateSniffer(cfg.Sniffer)
|
||||||
updateHosts(cfg.Hosts)
|
updateHosts(cfg.Hosts)
|
||||||
|
updateGeneral(cfg.General)
|
||||||
initInnerTcp()
|
initInnerTcp()
|
||||||
updateDNS(cfg.DNS, cfg.General.IPv6)
|
updateDNS(cfg.DNS, cfg.General.IPv6)
|
||||||
loadProxyProvider(cfg.Providers)
|
loadProxyProvider(cfg.Providers)
|
||||||
updateProfile(cfg)
|
updateProfile(cfg)
|
||||||
loadRuleProvider(cfg.RuleProviders)
|
loadRuleProvider(cfg.RuleProviders)
|
||||||
updateGeneral(cfg.General, force)
|
updateListeners(cfg.General, cfg.Listeners, force)
|
||||||
updateListeners(cfg.Listeners)
|
|
||||||
updateIPTables(cfg)
|
updateIPTables(cfg)
|
||||||
updateTun(cfg.General)
|
updateTun(cfg.General)
|
||||||
updateExperimental(cfg)
|
updateExperimental(cfg)
|
||||||
@ -134,12 +134,34 @@ func GetGeneral() *config.General {
|
|||||||
return general
|
return general
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateListeners(listeners map[string]C.InboundListener) {
|
func updateListeners(general *config.General, listeners map[string]C.InboundListener, force bool) {
|
||||||
tcpIn := tunnel.TCPIn()
|
tcpIn := tunnel.TCPIn()
|
||||||
udpIn := tunnel.UDPIn()
|
udpIn := tunnel.UDPIn()
|
||||||
natTable := tunnel.NatTable()
|
natTable := tunnel.NatTable()
|
||||||
|
|
||||||
listener.PatchInboundListeners(listeners, tcpIn, udpIn, natTable, true)
|
listener.PatchInboundListeners(listeners, tcpIn, udpIn, natTable, true)
|
||||||
|
if !force {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if general.Interface == "" && (!general.Tun.Enable || !general.Tun.AutoDetectInterface) {
|
||||||
|
dialer.DefaultInterface.Store(general.Interface)
|
||||||
|
}
|
||||||
|
|
||||||
|
allowLan := general.AllowLan
|
||||||
|
listener.SetAllowLan(allowLan)
|
||||||
|
|
||||||
|
bindAddress := general.BindAddress
|
||||||
|
listener.SetBindAddress(bindAddress)
|
||||||
|
listener.ReCreateHTTP(general.Port, tcpIn)
|
||||||
|
listener.ReCreateSocks(general.SocksPort, tcpIn, udpIn)
|
||||||
|
listener.ReCreateRedir(general.RedirPort, tcpIn, udpIn, natTable)
|
||||||
|
listener.ReCreateAutoRedir(general.EBpf.AutoRedir, tcpIn, udpIn)
|
||||||
|
listener.ReCreateTProxy(general.TProxyPort, tcpIn, udpIn, natTable)
|
||||||
|
listener.ReCreateMixed(general.MixedPort, tcpIn, udpIn)
|
||||||
|
listener.ReCreateShadowSocks(general.ShadowSocksConfig, tcpIn, udpIn)
|
||||||
|
listener.ReCreateVmess(general.VmessConfig, tcpIn, udpIn)
|
||||||
|
listener.ReCreateTuic(LC.TuicServer(general.TuicServer), tcpIn, udpIn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateExperimental(c *config.Config) {
|
func updateExperimental(c *config.Config) {
|
||||||
@ -304,7 +326,7 @@ func updateTunnels(tunnels []LC.Tunnel) {
|
|||||||
listener.PatchTunnel(tunnels, tunnel.TCPIn(), tunnel.UDPIn())
|
listener.PatchTunnel(tunnels, tunnel.TCPIn(), tunnel.UDPIn())
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateGeneral(general *config.General, force bool) {
|
func updateGeneral(general *config.General) {
|
||||||
tunnel.SetMode(general.Mode)
|
tunnel.SetMode(general.Mode)
|
||||||
tunnel.SetFindProcessMode(general.FindProcessMode)
|
tunnel.SetFindProcessMode(general.FindProcessMode)
|
||||||
dialer.DisableIPv6 = !general.IPv6
|
dialer.DisableIPv6 = !general.IPv6
|
||||||
@ -318,11 +340,18 @@ func updateGeneral(general *config.General, force bool) {
|
|||||||
log.Infoln("Use tcp concurrent")
|
log.Infoln("Use tcp concurrent")
|
||||||
}
|
}
|
||||||
|
|
||||||
adapter.UnifiedDelay.Store(general.UnifiedDelay)
|
inbound.SetTfo(general.InboundTfo)
|
||||||
dialer.DefaultInterface.Store(general.Interface)
|
|
||||||
|
|
||||||
if dialer.DefaultInterface.Load() != "" {
|
adapter.UnifiedDelay.Store(general.UnifiedDelay)
|
||||||
log.Infoln("Use interface name: %s", general.Interface)
|
// Avoid reload configuration clean the value, causing traffic loops
|
||||||
|
if listener.GetTunConf().Enable && listener.GetTunConf().AutoDetectInterface {
|
||||||
|
// changed only when the name is specified
|
||||||
|
// if name is empty, setting delay until after tun loaded
|
||||||
|
if general.Interface != "" && (!general.Tun.Enable || !general.Tun.AutoDetectInterface) {
|
||||||
|
dialer.DefaultInterface.Store(general.Interface)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dialer.DefaultInterface.Store(general.Interface)
|
||||||
}
|
}
|
||||||
|
|
||||||
dialer.DefaultRoutingMark.Store(int32(general.RoutingMark))
|
dialer.DefaultRoutingMark.Store(int32(general.RoutingMark))
|
||||||
@ -331,35 +360,8 @@ func updateGeneral(general *config.General, force bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
iface.FlushCache()
|
iface.FlushCache()
|
||||||
|
|
||||||
if !force {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
geodataLoader := general.GeodataLoader
|
geodataLoader := general.GeodataLoader
|
||||||
G.SetLoader(geodataLoader)
|
G.SetLoader(geodataLoader)
|
||||||
|
|
||||||
allowLan := general.AllowLan
|
|
||||||
listener.SetAllowLan(allowLan)
|
|
||||||
|
|
||||||
bindAddress := general.BindAddress
|
|
||||||
listener.SetBindAddress(bindAddress)
|
|
||||||
|
|
||||||
inbound.SetTfo(general.InboundTfo)
|
|
||||||
|
|
||||||
tcpIn := tunnel.TCPIn()
|
|
||||||
udpIn := tunnel.UDPIn()
|
|
||||||
natTable := tunnel.NatTable()
|
|
||||||
|
|
||||||
listener.ReCreateHTTP(general.Port, tcpIn)
|
|
||||||
listener.ReCreateSocks(general.SocksPort, tcpIn, udpIn)
|
|
||||||
listener.ReCreateRedir(general.RedirPort, tcpIn, udpIn, natTable)
|
|
||||||
listener.ReCreateAutoRedir(general.EBpf.AutoRedir, tcpIn, udpIn)
|
|
||||||
listener.ReCreateTProxy(general.TProxyPort, tcpIn, udpIn, natTable)
|
|
||||||
listener.ReCreateMixed(general.MixedPort, tcpIn, udpIn)
|
|
||||||
listener.ReCreateShadowSocks(general.ShadowSocksConfig, tcpIn, udpIn)
|
|
||||||
listener.ReCreateVmess(general.VmessConfig, tcpIn, udpIn)
|
|
||||||
listener.ReCreateTuic(LC.TuicServer(general.TuicServer), tcpIn, udpIn)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUsers(users []auth.AuthUser) {
|
func updateUsers(users []auth.AuthUser) {
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/Dreamacro/clash/config"
|
"github.com/Dreamacro/clash/config"
|
||||||
"github.com/Dreamacro/clash/hub/executor"
|
"github.com/Dreamacro/clash/hub/executor"
|
||||||
"github.com/Dreamacro/clash/hub/route"
|
"github.com/Dreamacro/clash/hub/route"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Option func(*config.Config)
|
type Option func(*config.Config)
|
||||||
@ -43,7 +44,7 @@ func Parse(options ...Option) error {
|
|||||||
|
|
||||||
if cfg.General.ExternalController != "" {
|
if cfg.General.ExternalController != "" {
|
||||||
go route.Start(cfg.General.ExternalController, cfg.General.ExternalControllerTLS,
|
go route.Start(cfg.General.ExternalController, cfg.General.ExternalControllerTLS,
|
||||||
cfg.General.Secret, cfg.TLS.Certificate, cfg.TLS.PrivateKey)
|
cfg.General.Secret, cfg.TLS.Certificate, cfg.TLS.PrivateKey,cfg.General.LogLevel==log.DEBUG)
|
||||||
}
|
}
|
||||||
|
|
||||||
executor.ApplyConfig(cfg, true)
|
executor.ApplyConfig(cfg, true)
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -13,8 +14,8 @@ import (
|
|||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
"github.com/Dreamacro/clash/tunnel/statistic"
|
"github.com/Dreamacro/clash/tunnel/statistic"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
"github.com/go-chi/cors"
|
"github.com/go-chi/cors"
|
||||||
"github.com/go-chi/render"
|
"github.com/go-chi/render"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
@ -43,7 +44,7 @@ func SetUIPath(path string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Start(addr string, tlsAddr string, secret string,
|
func Start(addr string, tlsAddr string, secret string,
|
||||||
certificat, privateKey string) {
|
certificat, privateKey string, isDebug bool) {
|
||||||
if serverAddr != "" {
|
if serverAddr != "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -59,6 +60,17 @@ func Start(addr string, tlsAddr string, secret string,
|
|||||||
MaxAge: 300,
|
MaxAge: 300,
|
||||||
})
|
})
|
||||||
r.Use(corsM.Handler)
|
r.Use(corsM.Handler)
|
||||||
|
if isDebug {
|
||||||
|
r.Mount("/debug", func() http.Handler {
|
||||||
|
r := chi.NewRouter()
|
||||||
|
r.Put("/gc", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
debug.FreeOSMemory()
|
||||||
|
})
|
||||||
|
handler := middleware.Profiler
|
||||||
|
r.Mount("/", handler())
|
||||||
|
return r
|
||||||
|
}())
|
||||||
|
}
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
r.Use(authentication)
|
r.Use(authentication)
|
||||||
r.Get("/", hello)
|
r.Get("/", hello)
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
package sing
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/log"
|
|
||||||
|
|
||||||
L "github.com/sagernet/sing/common/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
type logger struct{}
|
|
||||||
|
|
||||||
func (l logger) Trace(args ...any) {
|
|
||||||
log.Debugln(fmt.Sprint(args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l logger) Debug(args ...any) {
|
|
||||||
log.Debugln(fmt.Sprint(args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l logger) Info(args ...any) {
|
|
||||||
log.Infoln(fmt.Sprint(args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l logger) Warn(args ...any) {
|
|
||||||
log.Warnln(fmt.Sprint(args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l logger) Error(args ...any) {
|
|
||||||
log.Errorln(fmt.Sprint(args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l logger) Fatal(args ...any) {
|
|
||||||
log.Fatalln(fmt.Sprint(args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l logger) Panic(args ...any) {
|
|
||||||
log.Fatalln(fmt.Sprint(args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
var Logger L.Logger = logger{}
|
|
@ -67,6 +67,26 @@ func CalculateInterfaceName(name string) (tunName string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkTunName(tunName string) (ok bool) {
|
||||||
|
defer func() {
|
||||||
|
if !ok {
|
||||||
|
log.Warnln("[TUN] Unsupported tunName(%s) in %s, force regenerate by ourselves.", tunName, runtime.GOOS)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
if len(tunName) <= 4 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if tunName[:4] != "utun" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if _, parseErr := strconv.ParseInt(tunName[4:], 10, 16); parseErr != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, additions ...inbound.Addition) (l *Listener, err error) {
|
func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, additions ...inbound.Addition) (l *Listener, err error) {
|
||||||
if len(additions) == 0 {
|
if len(additions) == 0 {
|
||||||
additions = []inbound.Addition{
|
additions = []inbound.Addition{
|
||||||
@ -75,7 +95,7 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
tunName := options.Device
|
tunName := options.Device
|
||||||
if tunName == "" {
|
if tunName == "" || !checkTunName(tunName) {
|
||||||
tunName = CalculateInterfaceName(InterfaceName)
|
tunName = CalculateInterfaceName(InterfaceName)
|
||||||
options.Device = tunName
|
options.Device = tunName
|
||||||
}
|
}
|
||||||
@ -217,7 +237,7 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte
|
|||||||
EndpointIndependentNat: options.EndpointIndependentNat,
|
EndpointIndependentNat: options.EndpointIndependentNat,
|
||||||
UDPTimeout: udpTimeout,
|
UDPTimeout: udpTimeout,
|
||||||
Handler: handler,
|
Handler: handler,
|
||||||
Logger: sing.Logger,
|
Logger: log.SingLogger,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -41,7 +41,7 @@ func (l *Listener) handleTCP(conn net.Conn, in chan<- C.ConnContext, additions .
|
|||||||
}
|
}
|
||||||
|
|
||||||
func New(addr, target, proxy string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) {
|
func New(addr, target, proxy string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) {
|
||||||
l, err := net.Listen("tcp", addr)
|
l, err := inbound.Listen("tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
68
log/sing.go
Normal file
68
log/sing.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
L "github.com/sagernet/sing/common/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type singLogger struct{}
|
||||||
|
|
||||||
|
func (l singLogger) TraceContext(ctx context.Context, args ...any) {
|
||||||
|
Debugln(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l singLogger) DebugContext(ctx context.Context, args ...any) {
|
||||||
|
Debugln(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l singLogger) InfoContext(ctx context.Context, args ...any) {
|
||||||
|
Infoln(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l singLogger) WarnContext(ctx context.Context, args ...any) {
|
||||||
|
Warnln(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l singLogger) ErrorContext(ctx context.Context, args ...any) {
|
||||||
|
Errorln(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l singLogger) FatalContext(ctx context.Context, args ...any) {
|
||||||
|
Fatalln(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l singLogger) PanicContext(ctx context.Context, args ...any) {
|
||||||
|
Fatalln(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l singLogger) Trace(args ...any) {
|
||||||
|
Debugln(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l singLogger) Debug(args ...any) {
|
||||||
|
Debugln(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l singLogger) Info(args ...any) {
|
||||||
|
Infoln(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l singLogger) Warn(args ...any) {
|
||||||
|
Warnln(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l singLogger) Error(args ...any) {
|
||||||
|
Errorln(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l singLogger) Fatal(args ...any) {
|
||||||
|
Fatalln(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l singLogger) Panic(args ...any) {
|
||||||
|
Fatalln(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
var SingLogger L.ContextLogger = singLogger{}
|
91
transport/sing-shadowtls/shadowtls.go
Normal file
91
transport/sing-shadowtls/shadowtls.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package sing_shadowtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
tlsC "github.com/Dreamacro/clash/component/tls"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-shadowtls"
|
||||||
|
sing_common "github.com/sagernet/sing/common"
|
||||||
|
utls "github.com/sagernet/utls"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Mode string = "shadow-tls"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultALPN = []string{"h2", "http/1.1"}
|
||||||
|
)
|
||||||
|
|
||||||
|
type ShadowTLSOption struct {
|
||||||
|
Password string
|
||||||
|
Host string
|
||||||
|
Fingerprint string
|
||||||
|
ClientFingerprint string
|
||||||
|
SkipCertVerify bool
|
||||||
|
Version int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewShadowTLS(ctx context.Context, conn net.Conn, option *ShadowTLSOption) (net.Conn, error) {
|
||||||
|
tlsConfig := &tls.Config{
|
||||||
|
NextProtos: DefaultALPN,
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
|
InsecureSkipVerify: option.SkipCertVerify,
|
||||||
|
ServerName: option.Host,
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if len(option.Fingerprint) == 0 {
|
||||||
|
tlsConfig = tlsC.GetGlobalTLSConfig(tlsConfig)
|
||||||
|
} else {
|
||||||
|
if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsHandshake := shadowtls.DefaultTLSHandshakeFunc(option.Password, tlsConfig)
|
||||||
|
if len(option.ClientFingerprint) != 0 {
|
||||||
|
if fingerprint, exists := tlsC.GetFingerprint(option.ClientFingerprint); exists {
|
||||||
|
tlsHandshake = uTLSHandshakeFunc(tlsConfig, *fingerprint.ClientHelloID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client, err := shadowtls.NewClient(shadowtls.ClientConfig{
|
||||||
|
Version: option.Version,
|
||||||
|
Password: option.Password,
|
||||||
|
TLSHandshake: tlsHandshake,
|
||||||
|
Logger: log.SingLogger,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return client.DialContextConn(ctx, conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func uTLSHandshakeFunc(config *tls.Config, clientHelloID utls.ClientHelloID) shadowtls.TLSHandshakeFunc {
|
||||||
|
return func(ctx context.Context, conn net.Conn, sessionIDGenerator shadowtls.TLSSessionIDGeneratorFunc) error {
|
||||||
|
tlsConfig := &utls.Config{
|
||||||
|
Rand: config.Rand,
|
||||||
|
Time: config.Time,
|
||||||
|
VerifyPeerCertificate: config.VerifyPeerCertificate,
|
||||||
|
RootCAs: config.RootCAs,
|
||||||
|
NextProtos: config.NextProtos,
|
||||||
|
ServerName: config.ServerName,
|
||||||
|
InsecureSkipVerify: config.InsecureSkipVerify,
|
||||||
|
CipherSuites: config.CipherSuites,
|
||||||
|
MinVersion: config.MinVersion,
|
||||||
|
MaxVersion: config.MaxVersion,
|
||||||
|
CurvePreferences: sing_common.Map(config.CurvePreferences, func(it tls.CurveID) utls.CurveID {
|
||||||
|
return utls.CurveID(it)
|
||||||
|
}),
|
||||||
|
SessionTicketsDisabled: config.SessionTicketsDisabled,
|
||||||
|
Renegotiation: utls.RenegotiationSupport(config.Renegotiation),
|
||||||
|
SessionIDGenerator: sessionIDGenerator,
|
||||||
|
}
|
||||||
|
tlsConn := utls.UClient(conn, tlsConfig, clientHelloID)
|
||||||
|
return tlsConn.HandshakeContext(ctx)
|
||||||
|
}
|
||||||
|
}
|
@ -1,24 +1,36 @@
|
|||||||
package vless
|
package vless
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/subtle"
|
||||||
|
gotls "crypto/tls"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/buf"
|
"github.com/Dreamacro/clash/common/buf"
|
||||||
N "github.com/Dreamacro/clash/common/net"
|
N "github.com/Dreamacro/clash/common/net"
|
||||||
|
|
||||||
|
tlsC "github.com/Dreamacro/clash/component/tls"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
"github.com/gofrs/uuid"
|
"github.com/gofrs/uuid"
|
||||||
|
buf2 "github.com/sagernet/sing/common/buf"
|
||||||
|
"github.com/sagernet/sing/common/network"
|
||||||
|
"github.com/sagernet/sing/common/rw"
|
||||||
|
utls "github.com/sagernet/utls"
|
||||||
xtls "github.com/xtls/go"
|
xtls "github.com/xtls/go"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
N.ExtendedConn
|
network.ExtendedWriter
|
||||||
|
network.ExtendedReader
|
||||||
|
net.Conn
|
||||||
dst *DstAddr
|
dst *DstAddr
|
||||||
id *uuid.UUID
|
id *uuid.UUID
|
||||||
addons *Addons
|
addons *Addons
|
||||||
@ -27,30 +39,143 @@ type Conn struct {
|
|||||||
handshake chan struct{}
|
handshake chan struct{}
|
||||||
handshakeMutex sync.Mutex
|
handshakeMutex sync.Mutex
|
||||||
err error
|
err error
|
||||||
|
|
||||||
|
tlsConn net.Conn
|
||||||
|
input *bytes.Reader
|
||||||
|
rawInput *bytes.Buffer
|
||||||
|
|
||||||
|
packetsToFilter int
|
||||||
|
isTLS bool
|
||||||
|
isTLS12orAbove bool
|
||||||
|
enableXTLS bool
|
||||||
|
cipher uint16
|
||||||
|
remainingServerHello uint16
|
||||||
|
readRemainingContent int
|
||||||
|
readRemainingPadding int
|
||||||
|
readProcess bool
|
||||||
|
readFilterUUID bool
|
||||||
|
readLastCommand byte
|
||||||
|
writeFilterApplicationData bool
|
||||||
|
writeDirect bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *Conn) Read(b []byte) (int, error) {
|
func (vc *Conn) Read(b []byte) (int, error) {
|
||||||
if vc.received {
|
if vc.received {
|
||||||
return vc.ExtendedConn.Read(b)
|
if vc.readProcess {
|
||||||
|
buffer := buf2.With(b)
|
||||||
|
err := vc.ReadBuffer(buffer)
|
||||||
|
return buffer.Len(), err
|
||||||
|
}
|
||||||
|
return vc.ExtendedReader.Read(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := vc.recvResponse(); err != nil {
|
if err := vc.recvResponse(); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
vc.received = true
|
vc.received = true
|
||||||
return vc.ExtendedConn.Read(b)
|
return vc.Read(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *Conn) ReadBuffer(buffer *buf.Buffer) error {
|
func (vc *Conn) ReadBuffer(buffer *buf.Buffer) error {
|
||||||
if vc.received {
|
if vc.received {
|
||||||
return vc.ExtendedConn.ReadBuffer(buffer)
|
toRead := buffer.FreeBytes()
|
||||||
|
if vc.readRemainingContent > 0 {
|
||||||
|
if vc.readRemainingContent < buffer.FreeLen() {
|
||||||
|
toRead = toRead[:vc.readRemainingContent]
|
||||||
|
}
|
||||||
|
n, err := vc.ExtendedReader.Read(toRead)
|
||||||
|
buffer.Truncate(n)
|
||||||
|
vc.readRemainingContent -= n
|
||||||
|
vc.FilterTLS(toRead)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vc.readRemainingPadding > 0 {
|
||||||
|
err := rw.SkipN(vc.ExtendedReader, vc.readRemainingPadding)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
vc.readRemainingPadding = 0
|
||||||
|
}
|
||||||
|
if vc.readProcess {
|
||||||
|
switch vc.readLastCommand {
|
||||||
|
case commandPaddingContinue:
|
||||||
|
//if vc.isTLS || vc.packetsToFilter > 0 {
|
||||||
|
headerUUIDLen := 0
|
||||||
|
if vc.readFilterUUID {
|
||||||
|
headerUUIDLen = uuid.Size
|
||||||
|
}
|
||||||
|
var header []byte
|
||||||
|
if need := headerUUIDLen + paddingHeaderLen; buffer.FreeLen() < need {
|
||||||
|
header = make([]byte, need)
|
||||||
|
} else {
|
||||||
|
header = buffer.FreeBytes()[:need]
|
||||||
|
}
|
||||||
|
_, err := io.ReadFull(vc.ExtendedReader, header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pos := 0
|
||||||
|
if vc.readFilterUUID {
|
||||||
|
vc.readFilterUUID = false
|
||||||
|
pos = uuid.Size
|
||||||
|
if subtle.ConstantTimeCompare(vc.id.Bytes(), header[:uuid.Size]) != 1 {
|
||||||
|
err = fmt.Errorf("XTLS Vision server responded unknown UUID: %s",
|
||||||
|
uuid.FromBytesOrNil(header[:uuid.Size]).String())
|
||||||
|
log.Errorln(err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vc.readLastCommand = header[pos]
|
||||||
|
vc.readRemainingContent = int(binary.BigEndian.Uint16(header[pos+1:]))
|
||||||
|
vc.readRemainingPadding = int(binary.BigEndian.Uint16(header[pos+3:]))
|
||||||
|
log.Debugln("XTLS Vision read padding: command=%d, payloadLen=%d, paddingLen=%d",
|
||||||
|
vc.readLastCommand, vc.readRemainingContent, vc.readRemainingPadding)
|
||||||
|
return vc.ReadBuffer(buffer)
|
||||||
|
//}
|
||||||
|
case commandPaddingEnd:
|
||||||
|
vc.readProcess = false
|
||||||
|
return vc.ReadBuffer(buffer)
|
||||||
|
case commandPaddingDirect:
|
||||||
|
if vc.input != nil {
|
||||||
|
_, err := buffer.ReadFrom(vc.input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vc.input.Len() == 0 {
|
||||||
|
vc.input = nil
|
||||||
|
}
|
||||||
|
if buffer.IsFull() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if vc.rawInput != nil {
|
||||||
|
_, err := buffer.ReadFrom(vc.rawInput)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vc.rawInput.Len() == 0 {
|
||||||
|
vc.rawInput = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if vc.input == nil && vc.rawInput == nil {
|
||||||
|
vc.readProcess = false
|
||||||
|
vc.ExtendedReader = N.NewExtendedReader(vc.Conn)
|
||||||
|
log.Debugln("XTLS Vision direct read start")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
err := fmt.Errorf("XTLS Vision read unknown command: %d", vc.readLastCommand)
|
||||||
|
log.Debugln(err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vc.ExtendedReader.ReadBuffer(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := vc.recvResponse(); err != nil {
|
if err := vc.recvResponse(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
vc.received = true
|
vc.received = true
|
||||||
return vc.ExtendedConn.ReadBuffer(buffer)
|
return vc.ReadBuffer(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *Conn) Write(p []byte) (int, error) {
|
func (vc *Conn) Write(p []byte) (int, error) {
|
||||||
@ -67,7 +192,19 @@ func (vc *Conn) Write(p []byte) (int, error) {
|
|||||||
return 0, vc.err
|
return 0, vc.err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return vc.ExtendedConn.Write(p)
|
if vc.writeFilterApplicationData {
|
||||||
|
_buffer := buf.StackNew()
|
||||||
|
defer buf.KeepAlive(_buffer)
|
||||||
|
buffer := buf.Dup(_buffer)
|
||||||
|
defer buffer.Release()
|
||||||
|
buffer.Write(p)
|
||||||
|
err := vc.WriteBuffer(buffer)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
return vc.ExtendedWriter.Write(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *Conn) WriteBuffer(buffer *buf.Buffer) error {
|
func (vc *Conn) WriteBuffer(buffer *buf.Buffer) error {
|
||||||
@ -81,7 +218,57 @@ func (vc *Conn) WriteBuffer(buffer *buf.Buffer) error {
|
|||||||
return vc.err
|
return vc.err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return vc.ExtendedConn.WriteBuffer(buffer)
|
if vc.writeFilterApplicationData && vc.isTLS {
|
||||||
|
buffer2 := ReshapeBuffer(buffer)
|
||||||
|
defer buffer2.Release()
|
||||||
|
vc.FilterTLS(buffer.Bytes())
|
||||||
|
command := commandPaddingContinue
|
||||||
|
if buffer.Len() > 6 && bytes.Equal(buffer.To(3), tlsApplicationDataStart) || vc.packetsToFilter <= 0 {
|
||||||
|
command = commandPaddingEnd
|
||||||
|
if vc.enableXTLS {
|
||||||
|
command = commandPaddingDirect
|
||||||
|
vc.writeDirect = true
|
||||||
|
}
|
||||||
|
vc.writeFilterApplicationData = false
|
||||||
|
}
|
||||||
|
ApplyPadding(buffer, command, nil)
|
||||||
|
err := vc.ExtendedWriter.WriteBuffer(buffer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vc.writeDirect {
|
||||||
|
vc.ExtendedWriter = N.NewExtendedWriter(vc.Conn)
|
||||||
|
log.Debugln("XTLS Vision direct write start")
|
||||||
|
//time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
if buffer2 != nil {
|
||||||
|
if vc.writeDirect {
|
||||||
|
return vc.ExtendedWriter.WriteBuffer(buffer2)
|
||||||
|
}
|
||||||
|
vc.FilterTLS(buffer2.Bytes())
|
||||||
|
command = commandPaddingContinue
|
||||||
|
if buffer2.Len() > 6 && bytes.Equal(buffer2.To(3), tlsApplicationDataStart) || vc.packetsToFilter <= 0 {
|
||||||
|
command = commandPaddingEnd
|
||||||
|
if vc.enableXTLS {
|
||||||
|
command = commandPaddingDirect
|
||||||
|
vc.writeDirect = true
|
||||||
|
}
|
||||||
|
vc.writeFilterApplicationData = false
|
||||||
|
}
|
||||||
|
ApplyPadding(buffer2, command, nil)
|
||||||
|
err = vc.ExtendedWriter.WriteBuffer(buffer2)
|
||||||
|
if vc.writeDirect {
|
||||||
|
vc.ExtendedWriter = N.NewExtendedWriter(vc.Conn)
|
||||||
|
log.Debugln("XTLS Vision direct write start")
|
||||||
|
//time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
/*if vc.writeDirect {
|
||||||
|
log.Debugln("XTLS Vision Direct write, payloadLen=%d", buffer.Len())
|
||||||
|
}*/
|
||||||
|
return vc.ExtendedWriter.WriteBuffer(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *Conn) sendRequest(p []byte) bool {
|
func (vc *Conn) sendRequest(p []byte) bool {
|
||||||
@ -97,9 +284,6 @@ func (vc *Conn) sendRequest(p []byte) bool {
|
|||||||
}
|
}
|
||||||
defer close(vc.handshake)
|
defer close(vc.handshake)
|
||||||
|
|
||||||
requestLen := 1 // protocol version
|
|
||||||
requestLen += 16 // UUID
|
|
||||||
requestLen += 1 // addons length
|
|
||||||
var addonsBytes []byte
|
var addonsBytes []byte
|
||||||
if vc.addons != nil {
|
if vc.addons != nil {
|
||||||
addonsBytes, vc.err = proto.Marshal(vc.addons)
|
addonsBytes, vc.err = proto.Marshal(vc.addons)
|
||||||
@ -107,19 +291,32 @@ func (vc *Conn) sendRequest(p []byte) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
requestLen += len(addonsBytes)
|
isVision := vc.IsXTLSVisionEnabled()
|
||||||
requestLen += 1 // command
|
|
||||||
if !vc.dst.Mux {
|
|
||||||
requestLen += 2 // port
|
|
||||||
requestLen += 1 // addr type
|
|
||||||
requestLen += len(vc.dst.Addr)
|
|
||||||
}
|
|
||||||
requestLen += len(p)
|
|
||||||
|
|
||||||
_buffer := buf.StackNewSize(requestLen)
|
var buffer *buf.Buffer
|
||||||
defer buf.KeepAlive(_buffer)
|
if isVision {
|
||||||
buffer := buf.Dup(_buffer)
|
_buffer := buf.StackNew()
|
||||||
defer buffer.Release()
|
defer buf.KeepAlive(_buffer)
|
||||||
|
buffer = buf.Dup(_buffer)
|
||||||
|
defer buffer.Release()
|
||||||
|
} else {
|
||||||
|
requestLen := 1 // protocol version
|
||||||
|
requestLen += 16 // UUID
|
||||||
|
requestLen += 1 // addons length
|
||||||
|
requestLen += len(addonsBytes)
|
||||||
|
requestLen += 1 // command
|
||||||
|
if !vc.dst.Mux {
|
||||||
|
requestLen += 2 // port
|
||||||
|
requestLen += 1 // addr type
|
||||||
|
requestLen += len(vc.dst.Addr)
|
||||||
|
}
|
||||||
|
requestLen += len(p)
|
||||||
|
|
||||||
|
_buffer := buf.StackNewSize(requestLen)
|
||||||
|
defer buf.KeepAlive(_buffer)
|
||||||
|
buffer = buf.Dup(_buffer)
|
||||||
|
defer buffer.Release()
|
||||||
|
}
|
||||||
|
|
||||||
buf.Must(
|
buf.Must(
|
||||||
buffer.WriteByte(Version), // protocol version
|
buffer.WriteByte(Version), // protocol version
|
||||||
@ -144,15 +341,51 @@ func (vc *Conn) sendRequest(p []byte) bool {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.Must(buf.Error(buffer.Write(p)))
|
if isVision && !vc.dst.UDP && !vc.dst.Mux {
|
||||||
|
if len(p) == 0 {
|
||||||
|
vc.packetsToFilter = 0
|
||||||
|
vc.writeFilterApplicationData = false
|
||||||
|
WriteWithPadding(buffer, nil, commandPaddingEnd, vc.id)
|
||||||
|
} else {
|
||||||
|
vc.FilterTLS(p)
|
||||||
|
if vc.isTLS {
|
||||||
|
WriteWithPadding(buffer, p, commandPaddingContinue, vc.id)
|
||||||
|
} else {
|
||||||
|
buf.Must(buf.Error(buffer.Write(p)))
|
||||||
|
vc.readProcess = false
|
||||||
|
vc.writeFilterApplicationData = false
|
||||||
|
vc.packetsToFilter = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buf.Must(buf.Error(buffer.Write(p)))
|
||||||
|
}
|
||||||
|
|
||||||
_, vc.err = vc.ExtendedConn.Write(buffer.Bytes())
|
_, vc.err = vc.ExtendedWriter.Write(buffer.Bytes())
|
||||||
|
if vc.err != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if isVision {
|
||||||
|
switch underlying := vc.tlsConn.(type) {
|
||||||
|
case *gotls.Conn:
|
||||||
|
if underlying.ConnectionState().Version != gotls.VersionTLS13 {
|
||||||
|
vc.err = ErrNotTLS13
|
||||||
|
}
|
||||||
|
case *utls.UConn:
|
||||||
|
if underlying.ConnectionState().Version != utls.VersionTLS13 {
|
||||||
|
vc.err = ErrNotTLS13
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
vc.err = fmt.Errorf(`failed to use %s, maybe "security" is not "tls" or "utls"`, vc.addons.Flow)
|
||||||
|
}
|
||||||
|
vc.tlsConn = nil
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *Conn) recvResponse() error {
|
func (vc *Conn) recvResponse() error {
|
||||||
var buf [1]byte
|
var buf [1]byte
|
||||||
_, vc.err = io.ReadFull(vc.ExtendedConn, buf[:])
|
_, vc.err = io.ReadFull(vc.ExtendedReader, buf[:])
|
||||||
if vc.err != nil {
|
if vc.err != nil {
|
||||||
return vc.err
|
return vc.err
|
||||||
}
|
}
|
||||||
@ -161,30 +394,46 @@ func (vc *Conn) recvResponse() error {
|
|||||||
return errors.New("unexpected response version")
|
return errors.New("unexpected response version")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, vc.err = io.ReadFull(vc.ExtendedConn, buf[:])
|
_, vc.err = io.ReadFull(vc.ExtendedReader, buf[:])
|
||||||
if vc.err != nil {
|
if vc.err != nil {
|
||||||
return vc.err
|
return vc.err
|
||||||
}
|
}
|
||||||
|
|
||||||
length := int64(buf[0])
|
length := int64(buf[0])
|
||||||
if length != 0 { // addon data length > 0
|
if length != 0 { // addon data length > 0
|
||||||
io.CopyN(io.Discard, vc.ExtendedConn, length) // just discard
|
io.CopyN(io.Discard, vc.ExtendedReader, length) // just discard
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (vc *Conn) FrontHeadroom() int {
|
||||||
|
if vc.IsXTLSVisionEnabled() {
|
||||||
|
return paddingHeaderLen
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
func (vc *Conn) Upstream() any {
|
func (vc *Conn) Upstream() any {
|
||||||
return vc.ExtendedConn
|
if vc.tlsConn == nil {
|
||||||
|
return vc.Conn
|
||||||
|
}
|
||||||
|
return vc.tlsConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vc *Conn) IsXTLSVisionEnabled() bool {
|
||||||
|
return vc.addons != nil && vc.addons.Flow == XRV
|
||||||
}
|
}
|
||||||
|
|
||||||
// newConn return a Conn instance
|
// newConn return a Conn instance
|
||||||
func newConn(conn net.Conn, client *Client, dst *DstAddr) (*Conn, error) {
|
func newConn(conn net.Conn, client *Client, dst *DstAddr) (*Conn, error) {
|
||||||
c := &Conn{
|
c := &Conn{
|
||||||
ExtendedConn: N.NewExtendedConn(conn),
|
ExtendedReader: N.NewExtendedReader(conn),
|
||||||
id: client.uuid,
|
ExtendedWriter: N.NewExtendedWriter(conn),
|
||||||
dst: dst,
|
Conn: conn,
|
||||||
handshake: make(chan struct{}),
|
id: client.uuid,
|
||||||
|
dst: dst,
|
||||||
|
handshake: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
if !dst.UDP && client.Addons != nil {
|
if !dst.UDP && client.Addons != nil {
|
||||||
@ -205,15 +454,42 @@ func newConn(conn net.Conn, client *Client, dst *DstAddr) (*Conn, error) {
|
|||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("failed to use %s, maybe \"security\" is not \"xtls\"", client.Addons.Flow)
|
return nil, fmt.Errorf("failed to use %s, maybe \"security\" is not \"xtls\"", client.Addons.Flow)
|
||||||
}
|
}
|
||||||
|
case XRV:
|
||||||
|
c.packetsToFilter = 6
|
||||||
|
c.readProcess = true
|
||||||
|
c.readFilterUUID = true
|
||||||
|
c.writeFilterApplicationData = true
|
||||||
|
c.addons = client.Addons
|
||||||
|
var t reflect.Type
|
||||||
|
var p uintptr
|
||||||
|
switch underlying := conn.(type) {
|
||||||
|
case *gotls.Conn:
|
||||||
|
c.Conn = underlying.NetConn()
|
||||||
|
c.tlsConn = underlying
|
||||||
|
t = reflect.TypeOf(underlying).Elem()
|
||||||
|
p = uintptr(unsafe.Pointer(underlying))
|
||||||
|
case *utls.UConn:
|
||||||
|
c.Conn = underlying.NetConn()
|
||||||
|
c.tlsConn = underlying
|
||||||
|
t = reflect.TypeOf(underlying.Conn).Elem()
|
||||||
|
p = uintptr(unsafe.Pointer(underlying.Conn))
|
||||||
|
case *tlsC.UConn:
|
||||||
|
c.Conn = underlying.NetConn()
|
||||||
|
c.tlsConn = underlying.UConn
|
||||||
|
t = reflect.TypeOf(underlying.Conn).Elem()
|
||||||
|
p = uintptr(unsafe.Pointer(underlying.Conn))
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf(`failed to use %s, maybe "security" is not "tls" or "utls"`, client.Addons.Flow)
|
||||||
|
}
|
||||||
|
i, _ := t.FieldByName("input")
|
||||||
|
r, _ := t.FieldByName("rawInput")
|
||||||
|
c.input = (*bytes.Reader)(unsafe.Pointer(p + i.Offset))
|
||||||
|
c.rawInput = (*bytes.Buffer)(unsafe.Pointer(p + r.Offset))
|
||||||
|
if _, ok := c.Conn.(*net.TCPConn); !ok {
|
||||||
|
log.Debugln("XTLS underlying conn is not *net.TCPConn, got", reflect.TypeOf(conn).Name())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case <-c.handshake:
|
|
||||||
case <-time.After(200 * time.Millisecond):
|
|
||||||
c.sendRequest(nil)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
79
transport/vless/filter.go
Normal file
79
transport/vless/filter.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package vless
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
tls13SupportedVersions = []byte{0x00, 0x2b, 0x00, 0x02, 0x03, 0x04}
|
||||||
|
tlsClientHandshakeStart = []byte{0x16, 0x03}
|
||||||
|
tlsServerHandshakeStart = []byte{0x16, 0x03, 0x03}
|
||||||
|
tlsApplicationDataStart = []byte{0x17, 0x03, 0x03}
|
||||||
|
|
||||||
|
tls13CipherSuiteMap = map[uint16]string{
|
||||||
|
0x1301: "TLS_AES_128_GCM_SHA256",
|
||||||
|
0x1302: "TLS_AES_256_GCM_SHA384",
|
||||||
|
0x1303: "TLS_CHACHA20_POLY1305_SHA256",
|
||||||
|
0x1304: "TLS_AES_128_CCM_SHA256",
|
||||||
|
0x1305: "TLS_AES_128_CCM_8_SHA256",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tlsHandshakeTypeClientHello byte = 0x01
|
||||||
|
tlsHandshakeTypeServerHello byte = 0x02
|
||||||
|
)
|
||||||
|
|
||||||
|
func (vc *Conn) FilterTLS(p []byte) (index int) {
|
||||||
|
if vc.packetsToFilter <= 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
lenP := len(p)
|
||||||
|
vc.packetsToFilter -= 1
|
||||||
|
if index = bytes.Index(p, tlsServerHandshakeStart); index != -1 {
|
||||||
|
if lenP >= index+5 && p[index+5] == tlsHandshakeTypeServerHello {
|
||||||
|
vc.remainingServerHello = binary.BigEndian.Uint16(p[index+3:]) + 5
|
||||||
|
vc.isTLS = true
|
||||||
|
vc.isTLS12orAbove = true
|
||||||
|
if lenP-index >= 79 && vc.remainingServerHello >= 79 {
|
||||||
|
sessionIDLen := int(p[index+43])
|
||||||
|
vc.cipher = binary.BigEndian.Uint16(p[index+43+sessionIDLen+1:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if index = bytes.Index(p, tlsClientHandshakeStart); index != -1 {
|
||||||
|
if lenP >= index+5 && p[index+5] == tlsHandshakeTypeClientHello {
|
||||||
|
vc.isTLS = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if vc.remainingServerHello > 0 {
|
||||||
|
end := vc.remainingServerHello
|
||||||
|
vc.remainingServerHello -= end
|
||||||
|
if end > uint16(lenP) {
|
||||||
|
end = uint16(lenP)
|
||||||
|
}
|
||||||
|
if bytes.Contains(p[index:end], tls13SupportedVersions) {
|
||||||
|
// TLS 1.3 Client Hello
|
||||||
|
cs, ok := tls13CipherSuiteMap[vc.cipher]
|
||||||
|
if ok && cs != "TLS_AES_128_CCM_8_SHA256" {
|
||||||
|
vc.enableXTLS = true
|
||||||
|
}
|
||||||
|
log.Debugln("XTLS Vision found TLS 1.3, packetLength=", lenP, ", CipherSuite=", cs)
|
||||||
|
vc.packetsToFilter = 0
|
||||||
|
return
|
||||||
|
} else if vc.remainingServerHello < 0 {
|
||||||
|
log.Debugln("XTLS Vision found TLS 1.2, packetLength=", lenP)
|
||||||
|
vc.packetsToFilter = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Debugln("XTLS Vision found inconclusive server hello, packetLength=", lenP,
|
||||||
|
", remainingServerHelloBytes=", vc.remainingServerHello)
|
||||||
|
}
|
||||||
|
if vc.packetsToFilter <= 0 {
|
||||||
|
log.Debugln("XTLS Vision stop filtering")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
70
transport/vless/vision.go
Normal file
70
transport/vless/vision.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package vless
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/buf"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
"github.com/gofrs/uuid"
|
||||||
|
buf2 "github.com/sagernet/sing/common/buf"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
paddingHeaderLen = 1 + 2 + 2 // =5
|
||||||
|
|
||||||
|
commandPaddingContinue byte = 0x00
|
||||||
|
commandPaddingEnd byte = 0x01
|
||||||
|
commandPaddingDirect byte = 0x02
|
||||||
|
)
|
||||||
|
|
||||||
|
func WriteWithPadding(buffer *buf.Buffer, p []byte, command byte, userUUID *uuid.UUID) {
|
||||||
|
contentLen := int32(len(p))
|
||||||
|
var paddingLen int32
|
||||||
|
if contentLen < 900 {
|
||||||
|
paddingLen = rand.Int31n(500) + 900 - contentLen
|
||||||
|
}
|
||||||
|
|
||||||
|
if userUUID != nil { // unnecessary, but keep the same with Xray
|
||||||
|
buffer.Write(userUUID.Bytes())
|
||||||
|
}
|
||||||
|
buffer.WriteByte(command)
|
||||||
|
binary.BigEndian.PutUint16(buffer.Extend(2), uint16(contentLen))
|
||||||
|
binary.BigEndian.PutUint16(buffer.Extend(2), uint16(paddingLen))
|
||||||
|
buffer.Write(p)
|
||||||
|
buffer.Extend(int(paddingLen))
|
||||||
|
log.Debugln("XTLS Vision write padding1: command=%v, payloadLen=%v, paddingLen=%v", command, contentLen, paddingLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApplyPadding(buffer *buf.Buffer, command byte, userUUID *uuid.UUID) {
|
||||||
|
contentLen := int32(buffer.Len())
|
||||||
|
var paddingLen int32
|
||||||
|
if contentLen < 900 {
|
||||||
|
paddingLen = rand.Int31n(500) + 900 - contentLen
|
||||||
|
}
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(paddingLen))
|
||||||
|
binary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(contentLen))
|
||||||
|
buffer.ExtendHeader(1)[0] = command
|
||||||
|
if userUUID != nil { // unnecessary, but keep the same with Xray
|
||||||
|
copy(buffer.ExtendHeader(uuid.Size), userUUID.Bytes())
|
||||||
|
}
|
||||||
|
buffer.Extend(int(paddingLen))
|
||||||
|
log.Debugln("XTLS Vision write padding2: command=%d, payloadLen=%d, paddingLen=%d", command, contentLen, paddingLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReshapeBuffer(buffer *buf.Buffer) *buf.Buffer {
|
||||||
|
if buffer.Len() <= buf2.BufferSize-paddingHeaderLen {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cutAt := bytes.LastIndex(buffer.Bytes(), tlsApplicationDataStart)
|
||||||
|
if cutAt == -1 {
|
||||||
|
cutAt = buf2.BufferSize / 2
|
||||||
|
}
|
||||||
|
buffer2 := buf2.New()
|
||||||
|
buffer2.Write(buffer.From(cutAt))
|
||||||
|
buffer.Truncate(cutAt)
|
||||||
|
return buffer2
|
||||||
|
}
|
@ -12,6 +12,7 @@ const (
|
|||||||
XRO = "xtls-rprx-origin"
|
XRO = "xtls-rprx-origin"
|
||||||
XRD = "xtls-rprx-direct"
|
XRD = "xtls-rprx-direct"
|
||||||
XRS = "xtls-rprx-splice"
|
XRS = "xtls-rprx-splice"
|
||||||
|
XRV = "xtls-rprx-vision"
|
||||||
|
|
||||||
Version byte = 0 // protocol version. preview version is 0
|
Version byte = 0 // protocol version. preview version is 0
|
||||||
)
|
)
|
||||||
|
@ -2,6 +2,7 @@ package vless
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
tlsC "github.com/Dreamacro/clash/component/tls"
|
tlsC "github.com/Dreamacro/clash/component/tls"
|
||||||
@ -9,6 +10,10 @@ import (
|
|||||||
xtls "github.com/xtls/go"
|
xtls "github.com/xtls/go"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNotTLS13 = errors.New("XTLS Vision based on TLS 1.3 outer connection")
|
||||||
|
)
|
||||||
|
|
||||||
type XTLSConfig struct {
|
type XTLSConfig struct {
|
||||||
Host string
|
Host string
|
||||||
SkipCertVerify bool
|
SkipCertVerify bool
|
||||||
|
@ -28,7 +28,7 @@ type trackerInfo struct {
|
|||||||
RulePayload string `json:"rulePayload"`
|
RulePayload string `json:"rulePayload"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type tcpTracker struct {
|
type TCPTracker struct {
|
||||||
C.Conn `json:"-"`
|
C.Conn `json:"-"`
|
||||||
*trackerInfo
|
*trackerInfo
|
||||||
manager *Manager
|
manager *Manager
|
||||||
@ -36,11 +36,16 @@ type tcpTracker struct {
|
|||||||
extendedWriter N.ExtendedWriter
|
extendedWriter N.ExtendedWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tt *tcpTracker) ID() string {
|
func (tt *TCPTracker) ID() string {
|
||||||
return tt.UUID.String()
|
return tt.UUID.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tt *tcpTracker) Read(b []byte) (int, error) {
|
func (tt *TCPTracker) AddDownload(n int64) {
|
||||||
|
tt.manager.PushDownloaded(n)
|
||||||
|
tt.DownloadTotal.Add(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tt *TCPTracker) Read(b []byte) (int, error) {
|
||||||
n, err := tt.Conn.Read(b)
|
n, err := tt.Conn.Read(b)
|
||||||
download := int64(n)
|
download := int64(n)
|
||||||
tt.manager.PushDownloaded(download)
|
tt.manager.PushDownloaded(download)
|
||||||
@ -48,7 +53,7 @@ func (tt *tcpTracker) Read(b []byte) (int, error) {
|
|||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tt *tcpTracker) ReadBuffer(buffer *buf.Buffer) (err error) {
|
func (tt *TCPTracker) ReadBuffer(buffer *buf.Buffer) (err error) {
|
||||||
err = tt.extendedReader.ReadBuffer(buffer)
|
err = tt.extendedReader.ReadBuffer(buffer)
|
||||||
download := int64(buffer.Len())
|
download := int64(buffer.Len())
|
||||||
tt.manager.PushDownloaded(download)
|
tt.manager.PushDownloaded(download)
|
||||||
@ -56,7 +61,12 @@ func (tt *tcpTracker) ReadBuffer(buffer *buf.Buffer) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tt *tcpTracker) Write(b []byte) (int, error) {
|
func (tt *TCPTracker) AddUpload(n int64) {
|
||||||
|
tt.manager.PushUploaded(n)
|
||||||
|
tt.UploadTotal.Add(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tt *TCPTracker) Write(b []byte) (int, error) {
|
||||||
n, err := tt.Conn.Write(b)
|
n, err := tt.Conn.Write(b)
|
||||||
upload := int64(n)
|
upload := int64(n)
|
||||||
tt.manager.PushUploaded(upload)
|
tt.manager.PushUploaded(upload)
|
||||||
@ -64,7 +74,7 @@ func (tt *tcpTracker) Write(b []byte) (int, error) {
|
|||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tt *tcpTracker) WriteBuffer(buffer *buf.Buffer) (err error) {
|
func (tt *TCPTracker) WriteBuffer(buffer *buf.Buffer) (err error) {
|
||||||
upload := int64(buffer.Len())
|
upload := int64(buffer.Len())
|
||||||
err = tt.extendedWriter.WriteBuffer(buffer)
|
err = tt.extendedWriter.WriteBuffer(buffer)
|
||||||
tt.manager.PushUploaded(upload)
|
tt.manager.PushUploaded(upload)
|
||||||
@ -72,16 +82,16 @@ func (tt *tcpTracker) WriteBuffer(buffer *buf.Buffer) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tt *tcpTracker) Close() error {
|
func (tt *TCPTracker) Close() error {
|
||||||
tt.manager.Leave(tt)
|
tt.manager.Leave(tt)
|
||||||
return tt.Conn.Close()
|
return tt.Conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tt *tcpTracker) Upstream() any {
|
func (tt *TCPTracker) Upstream() any {
|
||||||
return tt.Conn
|
return tt.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.Rule) *tcpTracker {
|
func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.Rule) *TCPTracker {
|
||||||
uuid, _ := uuid.NewV4()
|
uuid, _ := uuid.NewV4()
|
||||||
if conn != nil {
|
if conn != nil {
|
||||||
if tcpAddr, ok := conn.RemoteAddr().(*net.TCPAddr); ok {
|
if tcpAddr, ok := conn.RemoteAddr().(*net.TCPAddr); ok {
|
||||||
@ -91,7 +101,7 @@ func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.R
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t := &tcpTracker{
|
t := &TCPTracker{
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
manager: manager,
|
manager: manager,
|
||||||
trackerInfo: &trackerInfo{
|
trackerInfo: &trackerInfo{
|
||||||
|
@ -366,8 +366,20 @@ func handleTCPConn(connCtx C.ConnContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conn := connCtx.Conn()
|
||||||
if sniffer.Dispatcher.Enable() && sniffingEnable {
|
if sniffer.Dispatcher.Enable() && sniffingEnable {
|
||||||
sniffer.Dispatcher.TCPSniff(connCtx.Conn(), metadata)
|
sniffer.Dispatcher.TCPSniff(conn, metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
peekMutex := sync.Mutex{}
|
||||||
|
if !conn.Peeked() {
|
||||||
|
peekMutex.Lock()
|
||||||
|
go func() {
|
||||||
|
defer peekMutex.Unlock()
|
||||||
|
_ = conn.SetReadDeadline(time.Now().Add(200 * time.Millisecond))
|
||||||
|
_, _ = conn.Peek(1)
|
||||||
|
_ = conn.SetReadDeadline(time.Time{})
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy, rule, err := resolveMetadata(connCtx, metadata)
|
proxy, rule, err := resolveMetadata(connCtx, metadata)
|
||||||
@ -387,10 +399,26 @@ func handleTCPConn(connCtx C.ConnContext) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var peekBytes []byte
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTCPTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTCPTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
remoteConn, err := retry(ctx, func(ctx context.Context) (C.Conn, error) {
|
remoteConn, err := retry(ctx, func(ctx context.Context) (C.Conn, error) {
|
||||||
return proxy.DialContext(ctx, dialMetadata)
|
remoteConn, err := proxy.DialContext(ctx, dialMetadata)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
peekMutex.Lock()
|
||||||
|
defer peekMutex.Unlock()
|
||||||
|
peekBytes, _ = conn.Peek(conn.Buffered())
|
||||||
|
_, err = remoteConn.Write(peekBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if peekLen := len(peekBytes); peekLen > 0 {
|
||||||
|
_, _ = conn.Discard(peekLen)
|
||||||
|
}
|
||||||
|
return remoteConn, err
|
||||||
}, func(err error) {
|
}, func(err error) {
|
||||||
if rule == nil {
|
if rule == nil {
|
||||||
log.Warnln(
|
log.Warnln(
|
||||||
|
Reference in New Issue
Block a user