Compare commits

..

29 Commits

Author SHA1 Message Date
8293b7fdae Merge branch 'Beta' into Meta
# Conflicts:
#	.github/workflows/docker.yaml
#	.github/workflows/prerelease.yml
2023-02-19 01:25:34 +08:00
0ba415866e Merge branch 'Alpha' into Beta 2023-02-19 01:25:01 +08:00
53b41ca166 Chore: Add action for deleting old workflow 2023-01-30 18:17:22 +08:00
8a75f78e63 chore: adjust Dockerfile 2023-01-12 02:24:12 +08:00
d9692c6366 Merge branch 'Beta' into Meta 2023-01-12 02:15:14 +08:00
f4b0062dfc Merge branch 'Alpha' into Beta 2023-01-12 02:14:49 +08:00
b9ffc82e53 Merge branch 'Beta' into Meta 2023-01-12 01:33:56 +08:00
78aaea6a45 Merge branch 'Alpha' into Beta 2023-01-12 01:33:16 +08:00
3645fbf161 Merge pull request #327 from Rasphino/Meta
Update flake.nix hash
2023-01-08 00:29:29 +08:00
a1d0f22132 fix: update flake.nix hash 2023-01-07 23:38:32 +08:00
fa73b0f4bf Merge remote-tracking branch 'origin/Beta' into Meta 2023-01-01 19:41:36 +08:00
3b76a8b839 Merge remote-tracking branch 'origin/Alpha' into Beta 2023-01-01 19:40:36 +08:00
667f42dcdc Merge pull request #282 from tdjnodj/Meta
Update README.md
2022-12-03 17:23:51 +08:00
dfbe09860f Update README.md 2022-12-03 17:17:08 +08:00
9e20f9c26a chore: update dependencies 2022-11-28 20:33:10 +08:00
f968d0cb82 chore: update github action 2022-11-26 20:16:12 +08:00
2ad84f4379 Merge branch 'Beta' into Meta 2022-11-02 18:08:22 +08:00
c7aa16426f Merge branch 'Alpha' into Beta 2022-11-02 18:07:29 +08:00
5987f8e3b5 Merge branch 'Beta' into Meta 2022-08-29 13:08:29 +08:00
3a8eb72de2 Merge branch 'Alpha' into Beta 2022-08-29 13:08:22 +08:00
33abbdfd24 Merge pull request #174 from MetaCubeX/Alpha
Alpha
2022-08-29 11:24:07 +08:00
0703d6cbff Merge pull request #173 from MetaCubeX/Alpha
Alpha
2022-08-29 11:22:14 +08:00
10d2d14938 Merge branch 'Beta' into Meta
# Conflicts:
#	rules/provider/classical_strategy.go
2022-07-02 10:41:41 +08:00
691cf1d8d6 Merge pull request #94 from bash99/Meta
Update README.md
2022-06-15 19:15:51 +08:00
d1decb8e58 Update README.md
add permissions for systemctl services
clash-dashboard change to updated one
2022-06-15 14:00:05 +08:00
7d04904109 fix: leak dns when domain in hosts list 2022-06-11 18:51:26 +08:00
a5acd3aa97 refactor: clear linkname,reduce cycle dependencies,transport init geosite function 2022-06-11 18:51:22 +08:00
eea9a12560 fix: 规则匹配默认策略组返回错误 2022-06-09 14:18:35 +08:00
0a4570b55c fix: group filter touch provider 2022-06-09 14:18:29 +08:00
48 changed files with 862 additions and 1601 deletions

16
.github/workflows/Delete.yml vendored Normal file
View File

@ -0,0 +1,16 @@
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

View File

@ -34,7 +34,7 @@ func NewInner(conn net.Conn, dst string, host string) *context.ConnContext {
metadata := &C.Metadata{}
metadata.NetWork = C.TCP
metadata.Type = C.INNER
metadata.DNSMode = C.DNSNormal
metadata.DNSMode = C.DNSMapping
metadata.Host = host
metadata.Process = C.ClashName
if h, port, err := net.SplitHostPort(dst); err == nil {

View File

@ -140,15 +140,10 @@ func (b *Base) DialOptions(opts ...dialer.Option) []dialer.Option {
default:
}
if b.tfo {
opts = append(opts, dialer.WithTFO(true))
}
return opts
}
type BasicOption struct {
TFO bool `proxy:"tfo,omitempty" group:"tfo,omitempty"`
Interface string `proxy:"interface-name,omitempty" group:"interface-name,omitempty"`
RoutingMark int `proxy:"routing-mark,omitempty" group:"routing-mark,omitempty"`
IPVersion string `proxy:"ip-version,omitempty" group:"ip-version,omitempty"`

View File

@ -170,7 +170,6 @@ func NewHttp(option HttpOption) (*Http, error) {
name: option.Name,
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
tp: C.Http,
tfo: option.TFO,
iface: option.Interface,
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),

View File

@ -53,9 +53,6 @@ func (rw *nopConn) Read(b []byte) (int, error) {
}
func (rw *nopConn) Write(b []byte) (int, error) {
if len(b) == 0 {
return 0, nil
}
return 0, io.EOF
}

View File

@ -2,6 +2,7 @@ package outbound
import (
"context"
"crypto/tls"
"errors"
"fmt"
"net"
@ -9,9 +10,10 @@ import (
"github.com/Dreamacro/clash/common/structure"
"github.com/Dreamacro/clash/component/dialer"
tlsC "github.com/Dreamacro/clash/component/tls"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/shadowtls"
obfs "github.com/Dreamacro/clash/transport/simple-obfs"
shadowtls "github.com/Dreamacro/clash/transport/sing-shadowtls"
"github.com/Dreamacro/clash/transport/socks5"
v2rayObfs "github.com/Dreamacro/clash/transport/v2ray-plugin"
@ -31,7 +33,8 @@ type ShadowSocks struct {
obfsMode string
obfsOption *simpleObfsOption
v2rayOption *v2rayObfs.Option
shadowTLSOption *shadowtls.ShadowTLSOption
shadowTLSOption *shadowTLSOption
tlsConfig *tls.Config
}
type ShadowSocksOption struct {
@ -64,31 +67,14 @@ type v2rayObfsOption struct {
}
type shadowTLSOption struct {
Password string `obfs:"password"`
Host string `obfs:"host"`
Fingerprint string `obfs:"fingerprint,omitempty"`
ClientFingerprint string `obfs:"client-fingerprint,omitempty"`
SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"`
Version int `obfs:"version,omitempty"`
Password string `obfs:"password"`
Host string `obfs:"host"`
Fingerprint string `obfs:"fingerprint,omitempty"`
SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"`
}
// StreamConn implements C.ProxyAdapter
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 {
case "tls":
c = obfs.NewTLSObfs(c, ss.obfsOption.Host)
@ -101,11 +87,13 @@ func (ss *ShadowSocks) streamConn(c net.Conn, metadata *C.Metadata) (net.Conn, e
if err != nil {
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 {
return ss.method.DialEarlyConn(c, M.ParseSocksaddr(uot.UOTMagicAddress+":443")), nil
return ss.method.DialConn(c, M.ParseSocksaddr(uot.UOTMagicAddress+":443"))
}
return ss.method.DialEarlyConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil
return ss.method.DialConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
}
// DialContext implements C.ProxyAdapter
@ -125,15 +113,7 @@ func (ss *ShadowSocks) DialContextWithDialer(ctx context.Context, dialer C.Diale
safeConnClose(c, err)
}(c)
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)
c, err = ss.StreamConn(c, metadata)
return NewConn(c, ss), err
}
@ -191,7 +171,8 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
var v2rayOption *v2rayObfs.Option
var obfsOption *simpleObfsOption
var shadowTLSOpt *shadowtls.ShadowTLSOption
var shadowTLSOpt *shadowTLSOption
var tlsConfig *tls.Config
obfsMode := ""
decoder := structure.NewDecoder(structure.Option{TagName: "obfs", WeaklyTypedInput: true})
@ -229,20 +210,24 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
}
} else if option.Plugin == shadowtls.Mode {
obfsMode = shadowtls.Mode
opt := &shadowTLSOption{
Version: 2,
}
if err := decoder.Decode(option.PluginOpts, opt); err != nil {
shadowTLSOpt = &shadowTLSOption{}
if err := decoder.Decode(option.PluginOpts, shadowTLSOpt); err != nil {
return nil, fmt.Errorf("ss %s initialize shadow-tls-plugin error: %w", addr, err)
}
shadowTLSOpt = &shadowtls.ShadowTLSOption{
Password: opt.Password,
Host: opt.Host,
Fingerprint: opt.Fingerprint,
ClientFingerprint: opt.ClientFingerprint,
SkipCertVerify: opt.SkipCertVerify,
Version: opt.Version,
tlsConfig = &tls.Config{
NextProtos: shadowtls.DefaultALPN,
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: shadowTLSOpt.SkipCertVerify,
ServerName: shadowTLSOpt.Host,
}
if len(shadowTLSOpt.Fingerprint) == 0 {
tlsConfig = tlsC.GetGlobalTLSConfig(tlsConfig)
} else {
if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, shadowTLSOpt.Fingerprint); err != nil {
return nil, err
}
}
}
@ -252,7 +237,6 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
addr: addr,
tp: C.Shadowsocks,
udp: option.UDP,
tfo: option.TFO,
iface: option.Interface,
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),
@ -264,6 +248,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
v2rayOption: v2rayOption,
obfsOption: obfsOption,
shadowTLSOption: shadowTLSOpt,
tlsConfig: tlsConfig,
}, nil
}

View File

@ -163,7 +163,6 @@ func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) {
addr: addr,
tp: C.ShadowsocksR,
udp: option.UDP,
tfo: option.TFO,
iface: option.Interface,
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),

View File

@ -167,7 +167,6 @@ func NewSnell(option SnellOption) (*Snell, error) {
addr: addr,
tp: C.Snell,
udp: option.UDP,
tfo: option.TFO,
iface: option.Interface,
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),

View File

@ -182,7 +182,6 @@ func NewSocks5(option Socks5Option) (*Socks5, error) {
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
tp: C.Socks5,
udp: option.UDP,
tfo: option.TFO,
iface: option.Interface,
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),

View File

@ -250,7 +250,6 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
addr: addr,
tp: C.Trojan,
udp: option.UDP,
tfo: option.TFO,
iface: option.Interface,
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),

View File

@ -51,7 +51,6 @@ type TuicOption struct {
ReceiveWindowConn int `proxy:"recv-window-conn,omitempty"`
ReceiveWindow int `proxy:"recv-window,omitempty"`
DisableMTUDiscovery bool `proxy:"disable-mtu-discovery,omitempty"`
SNI string `proxy:"sni,omitempty"`
}
// DialContext implements C.ProxyAdapter
@ -107,14 +106,12 @@ func (t *Tuic) dialWithDialer(ctx context.Context, dialer C.Dialer) (pc net.Pack
func NewTuic(option TuicOption) (*Tuic, error) {
addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
serverName := option.Server
tlsConfig := &tls.Config{
ServerName: serverName,
InsecureSkipVerify: option.SkipCertVerify,
MinVersion: tls.VersionTLS13,
}
if option.SNI != "" {
tlsConfig.ServerName = option.SNI
}
var bs []byte
var err error
@ -216,7 +213,6 @@ func NewTuic(option TuicOption) (*Tuic, error) {
udp: true,
tfo: option.FastOpen,
iface: option.Interface,
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),
},
}

View File

@ -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) {
host, _, _ := net.SplitHostPort(v.addr)
if v.isLegacyXTLSEnabled() && !isH2 {
if v.isXTLSEnabled() && !isH2 {
xtlsOpts := vless.XTLSConfig{
Host: host,
SkipCertVerify: v.option.SkipCertVerify,
@ -206,8 +206,8 @@ func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error)
return conn, nil
}
func (v *Vless) isLegacyXTLSEnabled() bool {
return v.client.Addons != nil && v.client.Addons.Flow != vless.XRV
func (v *Vless) isXTLSEnabled() bool {
return v.client.Addons != nil
}
// DialContext implements C.ProxyAdapter
@ -479,7 +479,7 @@ func NewVless(option VlessOption) (*Vless, error) {
if option.Network != "ws" && len(option.Flow) >= 16 {
option.Flow = option.Flow[:16]
switch option.Flow {
case vless.XRO, vless.XRD, vless.XRS, vless.XRV:
case vless.XRO, vless.XRD, vless.XRS:
addons = &vless.Addons{
Flow: option.Flow,
}
@ -510,7 +510,6 @@ func NewVless(option VlessOption) (*Vless, error) {
tp: C.Vless,
udp: option.UDP,
xudp: option.XUDP,
tfo: option.TFO,
iface: option.Interface,
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),

View File

@ -213,12 +213,12 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
}
if metadata.NetWork == C.UDP {
if v.option.XUDP {
return v.client.DialEarlyXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil
return v.client.DialXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
} else {
return v.client.DialEarlyPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil
return v.client.DialPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
}
} else {
return v.client.DialEarlyConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil
return v.client.DialConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
}
}
@ -289,9 +289,9 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
}(c)
if v.option.XUDP {
c = v.client.DialEarlyXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
c, err = v.client.DialXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
} else {
c = v.client.DialEarlyPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
c, err = v.client.DialPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
}
if err != nil {
@ -387,7 +387,6 @@ func NewVmess(option VmessOption) (*Vmess, error) {
tp: C.Vmess,
udp: option.UDP,
xudp: option.XUDP,
tfo: option.TFO,
iface: option.Interface,
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),

View File

@ -17,7 +17,7 @@ import (
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/resolver"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/listener/sing"
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{
Verbosef: func(format string, args ...interface{}) {
log.SingLogger.Debug(fmt.Sprintf(strings.ToLower(format), args...))
sing.Logger.Debug(fmt.Sprintf(strings.ToLower(format), args...))
},
Errorf: func(format string, args ...interface{}) {
log.SingLogger.Error(fmt.Sprintf(strings.ToLower(format), args...))
sing.Logger.Error(fmt.Sprintf(strings.ToLower(format), args...))
},
}, option.Workers)
if debug.Enabled {
log.SingLogger.Trace("created wireguard ipc conf: \n", ipcConf)
sing.Logger.Trace("created wireguard ipc conf: \n", ipcConf)
}
err = outbound.device.IpcSet(ipcConf)
if err != nil {

View File

@ -7,7 +7,6 @@ import (
"time"
"github.com/Dreamacro/clash/adapter/outbound"
"github.com/Dreamacro/clash/common/callback"
"github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/constant/provider"
@ -31,21 +30,11 @@ func (f *Fallback) DialContext(ctx context.Context, metadata *C.Metadata, opts .
c, err := proxy.DialContext(ctx, metadata, f.Base.DialOptions(opts...)...)
if err == nil {
c.AppendToChains(f)
f.onDialSuccess()
} else {
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
}

View File

@ -10,7 +10,6 @@ import (
"github.com/Dreamacro/clash/adapter/outbound"
"github.com/Dreamacro/clash/common/cache"
"github.com/Dreamacro/clash/common/callback"
"github.com/Dreamacro/clash/common/murmur3"
"github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant"
@ -84,24 +83,17 @@ func jumpHash(key uint64, buckets int32) int32 {
// DialContext implements C.ProxyAdapter
func (lb *LoadBalance) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (c C.Conn, err error) {
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...)...)
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
}

View File

@ -6,7 +6,6 @@ import (
"time"
"github.com/Dreamacro/clash/adapter/outbound"
"github.com/Dreamacro/clash/common/callback"
"github.com/Dreamacro/clash/common/singledo"
"github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant"
@ -39,20 +38,10 @@ func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts ..
c, err = proxy.DialContext(ctx, metadata, u.Base.DialOptions(opts...)...)
if err == nil {
c.AppendToChains(u)
u.onDialSuccess()
} else {
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
}

View File

@ -5,15 +5,9 @@ import (
"github.com/sagernet/sing/common/buf"
)
const BufferSize = buf.BufferSize
type Buffer = buf.Buffer
var New = buf.New
var StackNew = buf.StackNew
var StackNewSize = buf.StackNewSize
var With = buf.With
var KeepAlive = common.KeepAlive
//go:norace

View File

@ -1,25 +0,0 @@
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
}

View File

@ -12,14 +12,13 @@ var _ ExtendedConn = (*BufferedConn)(nil)
type BufferedConn struct {
r *bufio.Reader
ExtendedConn
peeked bool
}
func NewBufferedConn(c net.Conn) *BufferedConn {
if bc, ok := c.(*BufferedConn); ok {
return bc
}
return &BufferedConn{bufio.NewReader(c), NewExtendedConn(c), false}
return &BufferedConn{bufio.NewReader(c), NewExtendedConn(c)}
}
// Reader returns the internal bufio.Reader.
@ -27,24 +26,11 @@ func (c *BufferedConn) Reader() *bufio.Reader {
return c.r
}
func (c *BufferedConn) ResetPeeked() {
c.peeked = false
}
func (c *BufferedConn) Peeked() bool {
return c.peeked
}
// Peek returns the next n bytes without advancing the reader.
func (c *BufferedConn) Peek(n int) ([]byte, error) {
c.peeked = true
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) {
return c.r.Read(p)
}

View File

@ -37,25 +37,14 @@ func bindControl(ifaceIdx int) controlFn {
var innerErr error
err = c.Control(func(fd uintptr) {
handle := syscall.Handle(fd)
bind6err := bind6(handle, ifaceIdx)
bind4err := bind4(handle, ifaceIdx)
switch network {
case "ip6", "tcp6":
innerErr = bind6err
case "ip4", "tcp4", "udp4":
innerErr = bind4err
case "udp6":
// golang will set network to udp6 when listenUDP on wildcard ip (eg: ":0", "")
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
}
case "tcp6", "udp6":
innerErr = bind6(handle, ifaceIdx)
_ = bind4(handle, ifaceIdx)
default:
innerErr = bind4(handle, ifaceIdx)
// try bind ipv6, if failed, ignore. it's a workaround for windows disable interface ipv6
_ = bind6(handle, ifaceIdx)
}
})

View File

@ -8,19 +8,20 @@ import (
"net/netip"
"strings"
"sync"
"time"
"github.com/Dreamacro/clash/component/resolver"
"go.uber.org/atomic"
)
var (
dialMux sync.Mutex
actualSingleStackDialContext = serialSingleStackDialContext
actualDualStackDialContext = serialDualStackDialContext
tcpConcurrent = false
ErrorInvalidedNetworkStack = errors.New("invalided network stack")
ErrorConnTimeout = errors.New("connect timeout")
fallbackTimeout = 300 * time.Millisecond
dialMux sync.Mutex
actualSingleDialContext = singleDialContext
actualDualStackDialContext = dualStackDialContext
tcpConcurrent = false
DisableIPv6 = false
ErrorInvalidedNetworkStack = errors.New("invalided network stack")
ErrorDisableIPv6 = errors.New("IPv6 is disabled, dialer cancel")
)
func applyOptions(options ...Option) *option {
@ -55,7 +56,7 @@ func DialContext(ctx context.Context, network, address string, options ...Option
switch network {
case "tcp4", "tcp6", "udp4", "udp6":
return actualSingleStackDialContext(ctx, network, address, opt)
return actualSingleDialContext(ctx, network, address, opt)
case "tcp", "udp":
return actualDualStackDialContext(ctx, network, address, opt)
default:
@ -64,7 +65,18 @@ func DialContext(ctx context.Context, network, address string, options ...Option
}
func ListenPacket(ctx context.Context, network, address string, options ...Option) (net.PacketConn, error) {
cfg := applyOptions(options...)
cfg := &option{
interfaceName: DefaultInterface.Load(),
routingMark: int(DefaultRoutingMark.Load()),
}
for _, o := range DefaultOptions {
o(cfg)
}
for _, o := range options {
o(cfg)
}
lc := &net.ListenConfig{}
if cfg.interfaceName != "" {
@ -88,11 +100,11 @@ func SetDial(concurrent bool) {
dialMux.Lock()
tcpConcurrent = concurrent
if concurrent {
actualSingleStackDialContext = concurrentSingleStackDialContext
actualSingleDialContext = concurrentSingleDialContext
actualDualStackDialContext = concurrentDualStackDialContext
} else {
actualSingleStackDialContext = serialSingleStackDialContext
actualDualStackDialContext = serialDualStackDialContext
actualSingleDialContext = singleDialContext
actualDualStackDialContext = dualStackDialContext
}
dialMux.Unlock()
@ -113,64 +125,312 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po
bindMarkToDialer(opt.routingMark, dialer, network, destination)
}
address := net.JoinHostPort(destination.String(), port)
if opt.tfo {
return dialTFO(ctx, *dialer, network, address)
if DisableIPv6 && destination.Is6() {
return nil, ErrorDisableIPv6
}
return dialer.DialContext(ctx, network, address)
return dialer.DialContext(ctx, network, net.JoinHostPort(destination.String(), port))
}
func serialSingleStackDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) {
ips, port, err := parseAddr(ctx, network, address, opt.resolver)
if err != nil {
return nil, err
}
return serialDialContext(ctx, network, ips, port, opt)
}
func serialDualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) {
ips, port, err := parseAddr(ctx, network, address, opt.resolver)
if err != nil {
return nil, err
}
return dualStackDialContext(
ctx,
func(ctx context.Context) (net.Conn, error) { return serialDialContext(ctx, network, ips, port, opt) },
func(ctx context.Context) (net.Conn, error) { return serialDialContext(ctx, network, ips, port, opt) },
opt.prefer == 4)
}
func concurrentSingleStackDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) {
ips, port, err := parseAddr(ctx, network, address, opt.resolver)
func dualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) {
host, port, err := net.SplitHostPort(address)
if err != nil {
return nil, err
}
if conn, err := parallelDialContext(ctx, network, ips, port, opt); err != nil {
return nil, err
returned := make(chan struct{})
defer close(returned)
type dialResult struct {
net.Conn
error
resolved bool
ipv6 bool
done bool
}
results := make(chan dialResult)
var primary, fallback dialResult
startRacer := func(ctx context.Context, network, host string, r resolver.Resolver, ipv6 bool) {
result := dialResult{ipv6: ipv6, done: true}
defer func() {
select {
case results <- result:
case <-returned:
if result.Conn != nil {
_ = result.Conn.Close()
}
}
}()
var ip netip.Addr
if ipv6 {
if r == nil {
ip, result.error = resolver.ResolveIPv6ProxyServerHost(ctx, host)
} else {
ip, result.error = resolver.ResolveIPv6WithResolver(ctx, host, r)
}
} else {
if r == nil {
ip, result.error = resolver.ResolveIPv4ProxyServerHost(ctx, host)
} else {
ip, result.error = resolver.ResolveIPv4WithResolver(ctx, host, r)
}
}
if result.error != nil {
return
}
result.resolved = true
result.Conn, result.error = dialContext(ctx, network, ip, port, opt)
}
go startRacer(ctx, network+"4", host, opt.resolver, false)
go startRacer(ctx, network+"6", host, opt.resolver, true)
count := 2
for i := 0; i < count; i++ {
select {
case res := <-results:
if res.error == nil {
return res.Conn, nil
}
if !res.ipv6 {
primary = res
} else {
fallback = res
}
if primary.done && fallback.done {
if primary.resolved {
return nil, primary.error
} else if fallback.resolved {
return nil, fallback.error
} else {
return nil, primary.error
}
}
case <-ctx.Done():
err = ctx.Err()
break
}
}
if err == nil {
err = fmt.Errorf("dual stack dial failed")
} else {
return conn, nil
err = fmt.Errorf("dual stack dial failed:%w", err)
}
return nil, err
}
func concurrentDualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) {
ips, port, err := parseAddr(ctx, network, address, opt.resolver)
host, port, err := net.SplitHostPort(address)
if err != nil {
return nil, err
}
if opt.prefer != 4 && opt.prefer != 6 {
return parallelDialContext(ctx, network, ips, port, opt)
var ips []netip.Addr
if opt.resolver != nil {
ips, err = resolver.LookupIPWithResolver(ctx, host, opt.resolver)
} else {
ips, err = resolver.LookupIPProxyServerHost(ctx, host)
}
ipv4s, ipv6s := sortationAddr(ips)
return dualStackDialContext(
ctx,
func(ctx context.Context) (net.Conn, error) {
return parallelDialContext(ctx, network, ipv4s, port, opt)
},
func(ctx context.Context) (net.Conn, error) {
return parallelDialContext(ctx, network, ipv6s, port, opt)
},
opt.prefer == 4)
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) {
returned := make(chan struct{})
defer close(returned)
type dialResult struct {
ip netip.Addr
net.Conn
error
isPrimary bool
done bool
}
preferCount := atomic.NewInt32(0)
results := make(chan dialResult)
tcpRacer := func(ctx context.Context, ip netip.Addr) {
result := dialResult{ip: ip, done: true}
defer func() {
select {
case results <- result:
case <-returned:
if result.Conn != nil {
_ = result.Conn.Close()
}
}
}()
if strings.Contains(network, "tcp") {
network = "tcp"
} else {
network = "udp"
}
if ip.Is6() {
network += "6"
if opt.prefer != 4 {
result.isPrimary = true
}
}
if ip.Is4() {
network += "4"
if opt.prefer != 6 {
result.isPrimary = true
}
}
if result.isPrimary {
preferCount.Add(1)
}
result.Conn, result.error = dialContext(ctx, network, ip, port, opt)
}
for _, ip := range ips {
go tcpRacer(ctx, ip)
}
connCount := len(ips)
var fallback dialResult
var primaryError error
var finalError error
for i := 0; i < connCount; i++ {
select {
case res := <-results:
if res.error == nil {
if res.isPrimary {
return res.Conn, nil
} else {
if !fallback.done || fallback.error != nil {
fallback = res
}
}
} else {
if res.isPrimary {
primaryError = res.error
preferCount.Add(-1)
if preferCount.Load() == 0 && fallback.done && fallback.error == nil {
return fallback.Conn, nil
}
}
}
case <-ctx.Done():
if fallback.done && fallback.error == nil {
return fallback.Conn, nil
}
finalError = ctx.Err()
break
}
}
if fallback.done && fallback.error == nil {
return fallback.Conn, nil
}
if primaryError != nil {
return nil, primaryError
}
if fallback.error != nil {
return nil, fallback.error
}
if finalError == nil {
finalError = fmt.Errorf("all ips %v tcp shake hands failed", ips)
} else {
finalError = fmt.Errorf("concurrent dial failed:%w", 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) {
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)
if err != nil {
return nil, err
}
var ips []netip.Addr
if opt.resolver == nil {
ips, err = resolver.LookupIPv4ProxyServerHost(ctx, host)
} else {
ips, err = resolver.LookupIPv4WithResolver(ctx, host, opt.resolver)
}
if err != nil {
return nil, err
}
return concurrentDialContext(ctx, network, ips, port, opt)
}
func concurrentIPv6DialContext(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.LookupIPv6ProxyServerHost(ctx, host)
} else {
ips, err = resolver.LookupIPv6WithResolver(ctx, host, opt.resolver)
}
if err != nil {
return nil, err
}
return concurrentDialContext(ctx, network, ips, port, opt)
}
type Dialer struct {
@ -189,162 +449,3 @@ func NewDialer(options ...Option) Dialer {
opt := applyOptions(options...)
return Dialer{Opt: *opt}
}
func dualStackDialContext(
ctx context.Context,
ipv4DialFn func(ctx context.Context) (net.Conn, error),
ipv6DialFn func(ctx context.Context) (net.Conn, error),
preferIPv4 bool) (net.Conn, error) {
fallbackTicker := time.NewTicker(fallbackTimeout)
defer fallbackTicker.Stop()
results := make(chan dialResult)
returned := make(chan struct{})
defer close(returned)
racer := func(dial func(ctx context.Context) (net.Conn, error), isPrimary bool) {
result := dialResult{isPrimary: isPrimary}
defer func() {
select {
case results <- result:
case <-returned:
if result.Conn != nil {
_ = result.Conn.Close()
}
}
}()
result.Conn, result.error = dial(ctx)
}
go racer(ipv4DialFn, preferIPv4)
go racer(ipv6DialFn, !preferIPv4)
var fallback dialResult
var err error
for {
select {
case <-ctx.Done():
if fallback.error == nil && fallback.Conn != nil {
return fallback.Conn, nil
}
return nil, fmt.Errorf("dual stack connect failed: %w", err)
case <-fallbackTicker.C:
if fallback.error == nil && fallback.Conn != nil {
return fallback.Conn, nil
}
case res := <-results:
if res.error == nil {
if res.isPrimary {
return res.Conn, nil
}
fallback = res
}
err = res.error
}
}
}
func parallelDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) {
results := make(chan dialResult)
returned := make(chan struct{})
defer close(returned)
tcpRacer := func(ctx context.Context, ip netip.Addr, port string) {
result := dialResult{isPrimary: true}
defer func() {
select {
case results <- result:
case <-returned:
if result.Conn != nil {
_ = result.Conn.Close()
}
}
}()
result.ip = ip
result.Conn, result.error = dialContext(ctx, network, ip, port, opt)
}
for _, ip := range ips {
go tcpRacer(ctx, ip, port)
}
var err error
for {
select {
case <-ctx.Done():
if err != nil {
return nil, err
}
if ctx.Err() == context.DeadlineExceeded {
return nil, ErrorConnTimeout
}
return nil, ctx.Err()
case res := <-results:
if res.error == nil {
return res.Conn, nil
}
err = res.error
}
}
}
func serialDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) {
var (
conn net.Conn
err error
errs []error
)
for _, ip := range ips {
if conn, err = dialContext(ctx, network, ip, port, opt); err == nil {
return conn, nil
} else {
errs = append(errs, err)
}
}
return nil, errors.Join(errs...)
}
type dialResult struct {
ip netip.Addr
net.Conn
error
isPrimary bool
}
func parseAddr(ctx context.Context, network, address string, preferResolver resolver.Resolver) ([]netip.Addr, string, error) {
host, port, err := net.SplitHostPort(address)
if err != nil {
return nil, "-1", err
}
var ips []netip.Addr
switch network {
case "tcp4", "udp4":
if preferResolver == nil {
ips, err = resolver.LookupIPv4ProxyServerHost(ctx, host)
} else {
ips, err = resolver.LookupIPv4WithResolver(ctx, host, preferResolver)
}
case "tcp6", "udp6":
if preferResolver == nil {
ips, err = resolver.LookupIPv6ProxyServerHost(ctx, host)
} else {
ips, err = resolver.LookupIPv6WithResolver(ctx, host, preferResolver)
}
default:
if preferResolver == nil {
ips, err = resolver.LookupIP(ctx, host)
} else {
ips, err = resolver.LookupIPWithResolver(ctx, host, preferResolver)
}
}
if err != nil {
return nil, "-1", fmt.Errorf("dns resolve failed: %w", err)
}
return ips, port, nil
}
func sortationAddr(ips []netip.Addr) (ipv4s, ipv6s []netip.Addr) {
for _, v := range ips {
if v.Is4() || v.Is4In6() {
ipv4s = append(ipv4s, v)
} else {
ipv6s = append(ipv6s, v)
}
}
return
}

View File

@ -18,7 +18,6 @@ type option struct {
routingMark int
network int
prefer int
tfo bool
resolver resolver.Resolver
}
@ -70,12 +69,6 @@ func WithOnlySingleStack(isIPv4 bool) Option {
}
}
func WithTFO(tfo bool) Option {
return func(opt *option) {
opt.tfo = tfo
}
}
func WithOption(o option) Option {
return func(opt *option) {
*opt = o

View File

@ -1,119 +0,0 @@
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
}

View File

@ -36,7 +36,12 @@ type SnifferDispatcher struct {
parsePureIp bool
}
func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) {
func (sd *SnifferDispatcher) TCPSniff(conn net.Conn, 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) {
port, err := strconv.ParseUint(metadata.DstPort, 10, 16)
if err != nil {
@ -69,7 +74,7 @@ func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata
}
sd.rwMux.RUnlock()
if host, err := sd.sniffDomain(conn, metadata); err != nil {
if host, err := sd.sniffDomain(bufConn, metadata); err != nil {
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)
return

View File

@ -11,30 +11,31 @@ import (
"strings"
"sync"
CN "github.com/Dreamacro/clash/common/net"
xtls "github.com/xtls/go"
)
var trustCert,_ = x509.SystemCertPool()
var tlsCertificates = make([]tls.Certificate, 0)
var mutex sync.RWMutex
var errNotMacth error = errors.New("certificate fingerprints do not match")
func AddCertificate(certificate string) error {
func AddCertificate(privateKey, certificate string) error {
mutex.Lock()
defer mutex.Unlock()
if certificate == "" {
return fmt.Errorf("certificate is empty")
}
if ok := trustCert.AppendCertsFromPEM([]byte(certificate)); !ok {
return fmt.Errorf("add certificate failed")
if cert, err := CN.ParseCert(certificate, privateKey); err != nil {
return err
} else {
tlsCertificates = append(tlsCertificates, cert)
}
return nil
}
func ResetCertificate(){
mutex.Lock()
defer mutex.Unlock()
trustCert,_=x509.SystemCertPool()
func GetCertificates() []tls.Certificate {
mutex.RLock()
defer mutex.RUnlock()
return tlsCertificates
}
func verifyFingerprint(fingerprint *[32]byte) func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
@ -86,10 +87,10 @@ func GetSpecifiedFingerprintTLSConfig(tlsConfig *tls.Config, fingerprint string)
func GetGlobalTLSConfig(tlsConfig *tls.Config) *tls.Config {
if tlsConfig == nil {
return &tls.Config{
RootCAs: trustCert,
Certificates: tlsCertificates,
}
}
tlsConfig.RootCAs = trustCert
tlsConfig.Certificates = append(tlsConfig.Certificates, tlsCertificates...)
return tlsConfig
}
@ -106,12 +107,29 @@ func GetSpecifiedFingerprintXTLSConfig(tlsConfig *xtls.Config, fingerprint strin
}
func GetGlobalXTLSConfig(tlsConfig *xtls.Config) *xtls.Config {
xtlsCerts := make([]xtls.Certificate, len(tlsCertificates))
for _, cert := range tlsCertificates {
tlsSsaList := make([]xtls.SignatureScheme, len(cert.SupportedSignatureAlgorithms))
for _, ssa := range cert.SupportedSignatureAlgorithms {
tlsSsa := xtls.SignatureScheme(ssa)
tlsSsaList = append(tlsSsaList, tlsSsa)
}
xtlsCert := xtls.Certificate{
Certificate: cert.Certificate,
PrivateKey: cert.PrivateKey,
OCSPStaple: cert.OCSPStaple,
SignedCertificateTimestamps: cert.SignedCertificateTimestamps,
Leaf: cert.Leaf,
SupportedSignatureAlgorithms: tlsSsaList,
}
xtlsCerts = append(xtlsCerts, xtlsCert)
}
if tlsConfig == nil {
return &xtls.Config{
RootCAs: trustCert,
Certificates: xtlsCerts,
}
}
tlsConfig.RootCAs = trustCert
tlsConfig.Certificates = xtlsCerts
return tlsConfig
}

View File

@ -7,7 +7,7 @@ import (
"github.com/Dreamacro/clash/log"
"github.com/mroth/weightedrand/v2"
utls "github.com/sagernet/utls"
utls "github.com/refraction-networking/utls"
)
type UConn struct {

View File

@ -120,9 +120,13 @@ type Profile struct {
}
type TLS struct {
Certificate string `yaml:"certificate"`
PrivateKey string `yaml:"private-key"`
CustomTrustCert []string `yaml:"custom-certifactes"`
RawCert `yaml:",inline"`
CustomTrustCert []RawCert `yaml:"custom-certifactes"`
}
type RawCert struct {
Certificate string `yaml:"certificate"`
PrivateKey string `yaml:"private-key"`
}
// IPTables config

View File

@ -3,8 +3,6 @@ package constant
import (
"net"
N "github.com/Dreamacro/clash/common/net"
"github.com/gofrs/uuid"
)
@ -15,7 +13,7 @@ type PlainContext interface {
type ConnContext interface {
PlainContext
Metadata() *Metadata
Conn() *N.BufferedConn
Conn() net.Conn
}
type PacketConnContext interface {

View File

@ -12,7 +12,7 @@ import (
type ConnContext struct {
id uuid.UUID
metadata *C.Metadata
conn *N.BufferedConn
conn net.Conn
}
func NewConnContext(conn net.Conn, metadata *C.Metadata) *ConnContext {
@ -36,6 +36,6 @@ func (c *ConnContext) Metadata() *C.Metadata {
}
// Conn implement C.ConnContext Conn
func (c *ConnContext) Conn() *N.BufferedConn {
func (c *ConnContext) Conn() net.Conn {
return c.conn
}

View File

@ -7,17 +7,22 @@ mixed-port: 10801 # HTTP(S) 和 SOCKS 代理混合端口
# tproxy-port: 7893
allow-lan: true # 允许局域网连接
bind-address: "*" # 绑定 IP 地址,仅作用于 allow-lan 为 true'*'表示所有地址
bind-address: "*" # 绑定IP地址仅作用于 allow-lan 为 true'*'表示所有地址
# find-process-mode has 3 values:always, strict, off
# find-process-mode has 3 values: always, strict, off
# - always, 开启,强制匹配所有进程
# - strict, 默认,由 clash 判断是否开启
# - strict, 默认由clash判断是否开启
# - off, 不匹配进程,推荐在路由器上使用此模式
find-process-mode: strict
# global-client-fingerprint:全局TLS指纹,优先低于proxy内的 client-fingerprint
# accepts "chrome","firefox","safari","ios","random","none" options.
# Utls is currently support TLS transport in TCP/grpc/WS/HTTP for VLESS/Vmess and trojan.
global-client-fingerprint: chrome
mode: rule
#自定义 geodata url
#自定义 geox-url
geox-url:
geoip: "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geoip.dat"
geosite: "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat"
@ -27,30 +32,16 @@ log-level: debug # 日志等级 silent/error/warning/info/debug
ipv6: true # 开启 IPv6 总开关,关闭阻断所有 IPv6 链接和屏蔽 DNS 请求 AAAA 记录
tls:
certificate: string # 证书 PEM 格式,或者 证书的路径
private-key: string # 证书对应的私钥 PEM 格式,或者私钥路径
custom-certifactes:
- |
-----BEGIN CERTIFICATE-----
format/pem...
-----END CERTIFICATE-----
external-controller: 0.0.0.0:9093 # RESTful API 监听地址
external-controller-tls: 0.0.0.0:9443 # RESTful API HTTPS 监听地址,需要配置 tls 部分配置文件
# secret: "123456" # `Authorization:Bearer ${secret}`
# secret: "123456" # `Authorization: Bearer ${secret}`
# tcp-concurrent: true # TCP 并发连接所有 IP, 将使用最快握手的 TCP
external-ui: /path/to/ui/folder # 配置 WEB UI 目录,使用 http://{{external-controller}}/ui 访问
# tcp-concurrent: true # TCP并发连接所有IP, 将使用最快握手的TCP
external-ui: /path/to/ui/folder # 配置WEB UI目录使用http://{{external-controller}}/ui 访问
# interface-name: en0 # 设置出口网卡
# 全局 TLS 指纹,优先低于 proxy 内的 client-fingerprint
# 可选: "chrome","firefox","safari","ios","random","none" options.
# Utls is currently support TLS transport in TCP/grpc/WS/HTTP for VLESS/Vmess and trojan.
global-client-fingerprint: chrome
# routing-mark:6666 # 配置 fwmark 仅用于 Linux
# routing-mark: 6666 # 配置 fwmark 仅用于Linux
experimental:
# 类似于 /etc/hosts, 仅支持配置单个 IP
@ -59,13 +50,6 @@ hosts:
# '.dev': 127.0.0.1
# 'alpha.clash.dev': '::1'
profile:
# 存储 select 选择记录
store-selected: false
# 持久化 fake-ip
store-fake-ip: true
# Tun 配置
tun:
enable: false
@ -91,10 +75,10 @@ tun:
#- 1000
# exclude_uid_range: # 排除路由的的用户范围
# - 1000-99999
# Android 用户和应用规则仅在 Android 下被支持
# 并且需要 auto_route
# include_android_user: # 限制被路由的 Android 用户
# - 0
# - 10
@ -121,13 +105,15 @@ sniffer:
# 是否使用嗅探结果作为实际访问,默认 true
# 全局配置,优先级低于 sniffer.sniff 实际配置
override-destination: false
sniff: # TLS 默认如果不配置 ports 默认嗅探 443
sniff:
# TLS 默认如果不配置 ports 默认嗅探 443
TLS:
# ports: [443, 8443]
# 默认嗅探 80
HTTP: # 需要嗅探的端口
HTTP:
# 需要嗅探的端口
ports: [80, 8080-8880]
# 可覆盖 sniffer.override-destination
override-destination: true
@ -142,7 +128,7 @@ sniffer:
- tls
- http
# 强制对此域名进行嗅探
# 仅对白名单中的端口进行嗅探,默认为 44380
# 已废弃,若 sniffer.sniff 配置则此项无效
port-whitelist:
@ -150,8 +136,27 @@ sniffer:
- "443"
# - 8000-9999
# shadowsocks,vmess 入口配置传入流量将和socks,mixed等入口一样按照mode所指定的方式进行匹配处理
# ss-config: ss://2022-blake3-aes-256-gcm:vlmpIPSyHH6f4S8WVPdRIHIlzmB+GIRfoH3aNJ/t9Gg=@:23456
# vmess-config: vmess://1:9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68@:12345
tunnels: # one line config
# tuic服务器入口传入流量将和socks,mixed等入口一样按照mode所指定的方式进行匹配处理
#tuic-server:
# enable: true
# listen: 127.0.0.1:10443
# token:
# - TOKEN
# certificate: ./server.crt
# private-key: ./server.key
# congestion-controller: bbr
# max-idle-time: 15000
# authentication-timeout: 1000
# alpn:
# - h3
# max-udp-relay-packet-size: 1500
tunnels:
# one line config
- tcp/udp,127.0.0.1:6553,114.114.114.114:53,proxy
- tcp,127.0.0.1:6666,rds.mysql.com:3306,vpn
# full yaml config
@ -160,6 +165,12 @@ tunnels: # one line config
target: target.com
proxy: proxy
profile:
# 存储select选择记录
store-selected: false
# 持久化fake-ip
store-fake-ip: true
# DNS配置
dns:
@ -167,7 +178,7 @@ dns:
prefer-h3: true # 开启 DoH 支持 HTTP/3将并发尝试
listen: 0.0.0.0:53 # 开启 DNS 服务器监听
# ipv6: false # false 将返回 AAAA 的空结果
# 用于解析 nameserverfallback 以及其他DNS服务器配置的DNS 服务域名
# 只能使用纯 IP 地址,可使用加密 DNS
default-nameserver:
@ -176,16 +187,16 @@ dns:
- tls://1.12.12.12:853
- tls://223.5.5.5:853
enhanced-mode: fake-ip # or redir-host
fake-ip-range: 198.18.0.1/16 # fake-ip 池设置
# use-hosts: true # 查询 hosts
# 配置不使用fake-ip的域名
# fake-ip-filter:
# - '*.lan'
# - localhost.ptlogin2.qq.com
# DNS主要域名配置
# 支持 UDPTCPDoTDoHDoQ
# 这部分为主要 DNS 配置,影响所有直连,确保使用对大陆解析精准的 DNS
@ -199,20 +210,20 @@ dns:
- dhcp://en0 # dns from dhcp
- quic://dns.adguard.com:784 # DNS over QUIC
# - '8.8.8.8#en0' # 兼容指定DNS出口网卡
# 当配置 fallback 时,会查询 nameserver 中返回的 IP 是否为 CN非必要配置
# 当不是 CN则使用 fallback 中的 DNS 查询结果
# 确保配置 fallback 时能够正常查询
# fallback:
# - tcp://1.1.1.1
# - 'tcp://1.1.1.1#ProxyGroupName' # 指定 DNS 过代理查询ProxyGroupName 为策略组名或节点名,过代理配置优先于配置出口网卡,当找不到策略组或节点名则设置为出口网卡
# 专用于节点域名解析的 DNS 服务器,非必要配置项
# 配置服务器若查询失败将使用 nameserver非并发查询
# proxy-server-nameserver:
# - https://dns.google/dns-query
# - tls://one.one.one.one
# 配置 fallback 使用条件
# fallback-filter:
# geoip: true # 配置是否使用 geoip
@ -227,53 +238,14 @@ dns:
# - '+.google.com'
# - '+.facebook.com'
# - '+.youtube.com'
# 配置查询域名使用的 DNS 服务器
nameserver-policy: # 'www.baidu.com': '114.114.114.114'
# '+.internal.crop.com': '10.0.0.1'
"geosite:cn":
- https://doh.pub/dns-query
- https://dns.alidns.com/dns-query
"www.baidu.com": [https://doh.pub/dns-query, https://dns.alidns.com/dns-query]
proxies: # socks5
- name: "socks"
type: socks5
server: server
port: 443
# username: username
# password: password
# tls: true
# fingerprint: xxxx
# skip-cert-verify: true
# udp: true
# ip-version: ipv6
# http
- name: "http"
type: http
server: server
port: 443
# username: username
# password: password
# tls: true # https
# skip-cert-verify: true
# sni: custom.com
# fingerprint: xxxx # 同 experimental.fingerprints 使用 sha256 指纹,配置协议独立的指纹,将忽略 experimental.fingerprints
# ip-version: dual
# Snell
# Beware that there's currently no UDP support yet
- name: "snell"
type: snell
server: server
port: 44046
psk: yourpsk
# version: 2
# obfs-opts:
# mode: http # or tls
# host: bing.com
# 配置查询域名使用的 DNS 服务器
nameserver-policy:
# 'www.baidu.com': '114.114.114.114'
# '+.internal.crop.com': '10.0.0.1'
"geosite:cn": "https://doh.pub/dns-query"
"www.baidu.com": [https://doh.pub/dns-query,https://dns.alidns.com/dns-query]
proxies:
# Shadowsocks
# cipher支持:
# aes-128-gcm aes-192-gcm aes-256-gcm
@ -296,7 +268,6 @@ proxies: # socks5
# UDP 则为双栈解析,获取结果中的第一个 IPv4
# ipv6-prefer 同 ipv4-prefer
# 现有协议都支持此参数TCP 效果仅在开启 tcp-concurrent 生效
- name: "ss2"
type: ss
server: server
@ -307,7 +278,7 @@ proxies: # socks5
plugin-opts:
mode: tls # or http
# host: bing.com
- name: "ss3"
type: ss
server: server
@ -317,17 +288,17 @@ proxies: # socks5
plugin: v2ray-plugin
plugin-opts:
mode: websocket # no QUIC now
# tls: true # wss
# 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取
# 配置指纹将实现 SSL Pining 效果
# fingerprint: xxxx
# skip-cert-verify: true
# host: bing.com
# path: "/"
# mux: true
# headers:
# custom: value
# tls: true # wss
# 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取
# 配置指纹将实现 SSL Pining 效果
# fingerprint: xxxx
# skip-cert-verify: true
# host: bing.com
# path: "/"
# mux: true
# headers:
# custom: value
- name: "ss4"
type: ss
server: server
@ -338,8 +309,7 @@ proxies: # socks5
plugin-opts:
host: "cloud.tencent.com"
password: "shadow_tls_password"
version: 2 # support 1/2/3
# vmess
# cipher支持 auto/aes-128-gcm/chacha20-poly1305/none
- name: "vmess"
@ -362,7 +332,7 @@ proxies: # socks5
# Host: v2ray.com
# max-early-data: 2048
# early-data-header-name: Sec-WebSocket-Protocol
- name: "vmess-h2"
type: vmess
server: server
@ -378,7 +348,7 @@ proxies: # socks5
- http.example.com
- http-alt.example.com
path: /
- name: "vmess-http"
type: vmess
server: server
@ -389,15 +359,15 @@ proxies: # socks5
# udp: true
# network: http
# http-opts:
# method: "GET"
# path:
# - '/'
# - '/video'
# headers:
# Connection:
# - keep-alive
# # method: "GET"
# # path:
# # - '/'
# # - '/video'
# # headers:
# # Connection:
# # - keep-alive
# ip-version: ipv4 # 设置使用 IP 类型偏好可选ipv4ipv6dual默认值dual
- name: vmess-grpc
server: server
port: 443
@ -413,7 +383,100 @@ proxies: # socks5
grpc-opts:
grpc-service-name: "example"
# ip-version: ipv4
# socks5
- name: "socks"
type: socks5
server: server
port: 443
# username: username
# password: password
# tls: true
# fingerprint: xxxx
# skip-cert-verify: true
# udp: true
# ip-version: ipv6
# http
- name: "http"
type: http
server: server
port: 443
# username: username
# password: password
# tls: true # https
# skip-cert-verify: true
# sni: custom.com
# fingerprint: xxxx # 同 experimental.fingerprints 使用 sha256 指纹,配置协议独立的指纹,将忽略 experimental.fingerprints
# ip-version: dual
# Snell
# Beware that there's currently no UDP support yet
- name: "snell"
type: snell
server: server
port: 44046
psk: yourpsk
# version: 2
# obfs-opts:
# mode: http # or tls
# host: bing.com
# Trojan
- name: "trojan"
type: trojan
server: server
port: 443
password: yourpsk
# client-fingerprint: random # Available: "chrome","firefox","safari","random","none"
# fingerprint: xxxx
# udp: true
# sni: example.com # aka server name
# alpn:
# - h2
# - http/1.1
# skip-cert-verify: true
- name: trojan-grpc
server: server
port: 443
type: trojan
password: "example"
network: grpc
sni: example.com
# skip-cert-verify: true
# fingerprint: xxxx
udp: true
grpc-opts:
grpc-service-name: "example"
- name: trojan-ws
server: server
port: 443
type: trojan
password: "example"
network: ws
sni: example.com
# skip-cert-verify: true
# fingerprint: xxxx
udp: true
# ws-opts:
# path: /path
# headers:
# Host: example.com
- name: "trojan-xtls"
type: trojan
server: server
port: 443
password: yourpsk
flow: "xtls-rprx-direct" # xtls-rprx-origin xtls-rprx-direct
flow-show: true
# udp: true
# sni: example.com # aka server name
# skip-cert-verify: true
# fingerprint: xxxx
# vless
- name: "vless-tcp"
type: vless
@ -426,21 +489,7 @@ proxies: # socks5
# skip-cert-verify: true
# fingerprint: xxxx
# client-fingerprint: random # Available: "chrome","firefox","safari","random","none"
- name: "vless-vision"
type: vless
server: server
port: 443
uuid: uuid
network: tcp
tls: true
udp: true
xudp: true
flow: xtls-rprx-vision # xtls-rprx-origin # enable XTLS
client-fingerprint: chrome
# fingerprint: xxxx
# skip-cert-verify: true
- name: "vless-ws"
type: vless
server: server
@ -457,61 +506,6 @@ proxies: # socks5
path: "/"
headers:
Host: example.com
# Trojan
- name: "trojan"
type: trojan
server: server
port: 443
password: yourpsk
# client-fingerprint: random # Available: "chrome","firefox","safari","random","none"
# fingerprint: xxxx
# udp: true
# sni: example.com # aka server name
# alpn:
# - h2
# - http/1.1
# skip-cert-verify: true
- name: trojan-grpc
server: server
port: 443
type: trojan
password: "example"
network: grpc
sni: example.com
# skip-cert-verify: true
# fingerprint: xxxx
udp: true
grpc-opts:
grpc-service-name: "example"
- name: trojan-ws
server: server
port: 443
type: trojan
password: "example"
network: ws
sni: example.com
# skip-cert-verify: true
# fingerprint: xxxx
udp: true
# ws-opts:
# path: /path
# headers:
# Host: example.com
- name: "trojan-xtls"
type: trojan
server: server
port: 443
password: yourpsk
flow: "xtls-rprx-direct" # xtls-rprx-origin xtls-rprx-direct
flow-show: true
# udp: true
# sni: example.com # aka server name
# skip-cert-verify: true
# fingerprint: xxxx
#hysteria
- name: "hysteria"
@ -538,8 +532,7 @@ proxies: # socks5
# disable_mtu_discovery: false
# fingerprint: xxxx
# fast-open: true # 支持 TCP 快速打开,默认为 false
# wireguard
- name: "wg"
type: wireguard
server: 162.159.192.1
@ -549,9 +542,7 @@ proxies: # socks5
private-key: eCtXsJZ27+4PbhDkHnB923tkUn2Gj59wZw5wFA75MnU=
public-key: Cr8hWlKvtDt7nrvf+f0brNQQzabAqrjfBvas9pmowjo=
udp: true
reserved: "U4An"
# tuic
# reserved: 'U4An'
- name: tuic
server: www.example.com
port: 10443
@ -560,16 +551,16 @@ proxies: # socks5
# ip: 127.0.0.1 # for overwriting the DNS lookup result of the server address set in option 'server'
# heartbeat-interval: 10000
# alpn: [h3]
disable-sni: true
# disable-sni: true
reduce-rtt: true
request-timeout: 8000
# request-timeout: 8000
udp-relay-mode: native # Available: "native", "quic". Default: "native"
# congestion-controller: bbr # Available: "cubic", "new_reno", "bbr". Default: "cubic"
# max-udp-relay-packet-size: 1500
# fast-open: true
# skip-cert-verify: true
# max-open-streams: 20 # default 100, too many open streams may hurt performance
# ShadowsocksR
# The supported ciphers (encryption methods): all stream ciphers in ss
# The supported obfses:
@ -590,7 +581,8 @@ proxies: # socks5
# protocol-param: "#"
# udp: true
proxy-groups: # 代理链,若落地协议支持 UDP over TCP 则可支持 UDP
proxy-groups:
# 代理链,若落地协议支持 UDP over TCP 则可支持 UDP
# Traffic: clash <-> http <-> vmess <-> ss1 <-> ss2 <-> Internet
- name: "relay"
type: relay
@ -599,7 +591,7 @@ proxy-groups: # 代理链,若落地协议支持 UDP over TCP 则可支持 UDP
- vmess
- ss1
- ss2
# url-test 将按照 url 测试结果使用延迟最低节点
- name: "auto"
type: url-test
@ -611,7 +603,7 @@ proxy-groups: # 代理链,若落地协议支持 UDP over TCP 则可支持 UDP
# lazy: true
url: "https://cp.cloudflare.com/generate_204"
interval: 300
# fallback 将按照 url 测试结果按照节点顺序选择
- name: "fallback-auto"
type: fallback
@ -621,7 +613,7 @@ proxy-groups: # 代理链,若落地协议支持 UDP over TCP 则可支持 UDP
- vmess1
url: "https://cp.cloudflare.com/generate_204"
interval: 300
# load-balance 将按照算法随机选择节点
- name: "load-balance"
type: load-balance
@ -631,8 +623,8 @@ proxy-groups: # 代理链,若落地协议支持 UDP over TCP 则可支持 UDP
- vmess1
url: "https://cp.cloudflare.com/generate_204"
interval: 300
# strategy: consistent-hashing # 可选 round-robin 和 sticky-sessions
# strategy: consistent-hashing # 可选 round-robin 和 sticky-sessions
# select 用户自行选择节点
- name: Proxy
type: select
@ -642,7 +634,7 @@ proxy-groups: # 代理链,若落地协议支持 UDP over TCP 则可支持 UDP
- ss2
- vmess1
- auto
# 配置指定 interface-name 和 fwmark 的 DIRECT
- name: en1
type: select
@ -650,7 +642,7 @@ proxy-groups: # 代理链,若落地协议支持 UDP over TCP 则可支持 UDP
routing-mark: 6667
proxies:
- DIRECT
- name: UseProvider
type: select
filter: "HK|TW" # 正则表达式,过滤 provider1 中节点名包含 HK 或 TW
@ -697,8 +689,7 @@ rules:
- DOMAIN-KEYWORD,google,ss1
- IP-CIDR,1.1.1.1/32,ss1
- IP-CIDR6,2409::/64,DIRECT
# 当满足条件是 TCP 或 UDP 流量时,使用名为 sub-rule-name1 规则集
- SUB-RULE,(OR,((NETWORK,TCP),(NETWORK,UDP))),sub-rule-name1
- SUB-RULE,(OR,((NETWORK,TCP),(NETWORK,UDP))),sub-rule-name1 # 当满足条件是 TCP 或 UDP 流量时,使用名为 sub-rule-name1 规则集
- SUB-RULE,(AND,((NETWORK,UDP))),sub-rule-name2
# 定义多个子规则集,规则将以分叉匹配,使用 SUB-RULE 使用
# google.com(not match)--> baidu.com(match)
@ -725,6 +716,15 @@ sub-rules:
- IP-CIDR,8.8.8.8/32,ss1
- DOMAIN,dns.alidns.com,REJECT
tls:
certificate: string # 证书 PEM 格式,或者 证书的路径
private-key: string # 证书对应的私钥 PEM 格式,或者私钥路径
# 自定义证书验证,将加入 Clash 证书验证中,绝大多数 TLS 相关支持DNS
# 可用于自定义证书的验证
custom-certificates:
- certificate: string # 证书 PEM 格式,或者 证书的路径
private-key: string # 证书对应的私钥 PEM 格式,或者私钥路径
# 流量入站
listeners:
- name: socks5-in-1
@ -734,14 +734,14 @@ listeners:
# rule: sub-rule-name1 # 默认使用 rules如果未找到 sub-rule 则直接使用 rules
# proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理
# udp: false # 默认 true
- name: http-in-1
type: http
port: 10809
listen: 0.0.0.0
# rule: sub-rule-name1 # 默认使用 rules如果未找到 sub-rule 则直接使用 rules
# proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时这里的proxy名称必须合法否则会出错)
- name: mixed-in-1
type: mixed # HTTP(S) 和 SOCKS 代理混合
port: 10810
@ -749,14 +749,14 @@ listeners:
# rule: sub-rule-name1 # 默认使用 rules如果未找到 sub-rule 则直接使用 rules
# proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时这里的proxy名称必须合法否则会出错)
# udp: false # 默认 true
- name: reidr-in-1
type: redir
port: 10811
listen: 0.0.0.0
# rule: sub-rule-name1 # 默认使用 rules如果未找到 sub-rule 则直接使用 rules
# proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时这里的proxy名称必须合法否则会出错)
- name: tproxy-in-1
type: tproxy
port: 10812
@ -764,7 +764,7 @@ listeners:
# rule: sub-rule-name1 # 默认使用 rules如果未找到 sub-rule 则直接使用 rules
# proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时这里的proxy名称必须合法否则会出错)
# udp: false # 默认 true
- name: shadowsocks-in-1
type: shadowsocks
port: 10813
@ -773,7 +773,7 @@ listeners:
# proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时这里的proxy名称必须合法否则会出错)
password: vlmpIPSyHH6f4S8WVPdRIHIlzmB+GIRfoH3aNJ/t9Gg=
cipher: 2022-blake3-aes-256-gcm
- name: vmess-in-1
type: vmess
port: 10814
@ -784,7 +784,7 @@ listeners:
- username: 1
uuid: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68
alterId: 1
- name: tuic-in-1
type: tuic
port: 10815
@ -801,7 +801,7 @@ listeners:
# alpn:
# - h3
# max-udp-relay-packet-size: 1500
- name: tunnel-in-1
type: tunnel
port: 10816
@ -810,7 +810,7 @@ listeners:
# proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时这里的proxy名称必须合法否则会出错)
network: [tcp, udp]
target: target.com
- name: tun-in-1
type: tun
# rule: sub-rule-name1 # 默认使用 rules如果未找到 sub-rule 则直接使用 rules
@ -826,25 +826,25 @@ listeners:
inet6-address: # 必须手动设置ipv6地址段
- "fdfe:dcba:9877::1/126"
# strict_route: true # 将所有连接路由到tun来防止泄漏但你的设备将无法其他设备被访问
# inet4_route_address: # 启用 auto_route 时使用自定义路由而不是默认路由
# - 0.0.0.0/1
# - 128.0.0.0/1
# inet6_route_address: # 启用 auto_route 时使用自定义路由而不是默认路由
# - "::/1"
# - "8000::/1"
# inet4_route_address: # 启用 auto_route 时使用自定义路由而不是默认路由
# - 0.0.0.0/1
# - 128.0.0.0/1
# inet6_route_address: # 启用 auto_route 时使用自定义路由而不是默认路由
# - "::/1"
# - "8000::/1"
# endpoint_independent_nat: false # 启用独立于端点的 NAT
# include_uid: # UID 规则仅在 Linux 下被支持,并且需要 auto_route
# - 0
# include_uid_range: # 限制被路由的的用户范围
# - 1000-99999
# exclude_uid: # 排除路由的的用户
# - 1000
#- 1000
# exclude_uid_range: # 排除路由的的用户范围
# - 1000-99999
# Android 用户和应用规则仅在 Android 下被支持
# 并且需要 auto_route
# include_android_user: # 限制被路由的 Android 用户
# - 0
# - 10
@ -852,23 +852,3 @@ listeners:
# - com.android.chrome
# exclude_package: # 排除被路由的 Android 应用包名
# - com.android.captiveportallogin
# 入口配置与 Listener 等价,传入流量将和 socks,mixed 等入口一样按照 mode 所指定的方式进行匹配处理
# shadowsocks,vmess 入口配置传入流量将和socks,mixed等入口一样按照mode所指定的方式进行匹配处理
# ss-config: ss://2022-blake3-aes-256-gcm:vlmpIPSyHH6f4S8WVPdRIHIlzmB+GIRfoH3aNJ/t9Gg=@:23456
# vmess-config: vmess://1:9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68@:12345
# tuic服务器入口传入流量将和socks,mixed等入口一样按照mode所指定的方式进行匹配处理
# tuic-server:
# enable: true
# listen: 127.0.0.1:10443
# token:
# - TOKEN
# certificate: ./server.crt
# private-key: ./server.key
# congestion-controller: bbr
# max-idle-time: 15000
# authentication-timeout: 1000
# alpn:
# - h3
# max-udp-relay-packet-size: 1500

34
go.mod
View File

@ -10,7 +10,7 @@ require (
github.com/go-chi/chi/v5 v5.0.8
github.com/go-chi/cors v1.2.1
github.com/go-chi/render v1.0.2
github.com/gofrs/uuid v4.4.0+incompatible
github.com/gofrs/uuid v4.3.1+incompatible
github.com/google/gopacket v1.1.19
github.com/gorilla/websocket v1.5.0
github.com/hashicorp/golang-lru v0.5.4
@ -20,17 +20,16 @@ require (
github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7
github.com/metacubex/quic-go v0.32.0
github.com/metacubex/sing-shadowsocks v0.1.1-0.20230202072246-e2bef5f088c7
github.com/metacubex/sing-tun v0.1.1-0.20230222113101-fbfa2dab826d
github.com/metacubex/sing-tun v0.1.1-0.20230213124625-28d27a0c236b
github.com/metacubex/sing-wireguard v0.0.0-20230213124601-d04406a109b4
github.com/miekg/dns v1.1.50
github.com/mroth/weightedrand/v2 v2.0.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/sing v0.1.8-0.20230221060643-3401d210384b
github.com/sagernet/sing-shadowtls v0.0.0-20230221130515-dac782ca098e
github.com/sagernet/sing-vmess v0.1.2
github.com/sagernet/sing v0.1.7-0.20230207063819-27d2950cdbe9
github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb
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/samber/lo v1.37.0
github.com/sirupsen/logrus v1.9.0
@ -39,19 +38,19 @@ require (
go.etcd.io/bbolt v1.3.6
go.uber.org/atomic v1.10.0
go.uber.org/automaxprocs v1.5.1
golang.org/x/crypto v0.6.0
golang.org/x/crypto v0.5.0
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db
golang.org/x/net v0.6.0
golang.org/x/net v0.5.0
golang.org/x/sync v0.1.0
golang.org/x/sys v0.5.0
google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d
golang.org/x/sys v0.4.0
google.golang.org/protobuf v1.28.1
gopkg.in/yaml.v3 v3.0.1
lukechampine.com/blake3 v1.1.7
)
require (
github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
@ -60,10 +59,10 @@ require (
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/klauspost/compress v1.15.15 // indirect
github.com/klauspost/compress v1.15.12 // indirect
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
github.com/mdlayher/socket v0.4.0 // indirect
github.com/metacubex/gvisor v0.0.0-20230222112937-bdbcd206ec65 // indirect
github.com/metacubex/gvisor v0.0.0-20230213124051-7a16c835d80e // indirect
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
@ -71,13 +70,14 @@ require (
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-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/u-root/uio v0.0.0-20221213070652-c3537552635f // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
golang.org/x/mod v0.7.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
golang.org/x/tools v0.5.0 // indirect
golang.org/x/mod v0.6.0 // indirect
golang.org/x/text v0.6.0 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
golang.org/x/tools v0.2.0 // indirect
)
replace go.uber.org/atomic v1.10.0 => github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370

68
go.sum
View File

@ -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/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
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/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-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/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI=
github.com/gofrs/uuid v4.3.1+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/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
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-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM=
github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
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/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/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw=
github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc=
github.com/metacubex/gvisor v0.0.0-20230222112937-bdbcd206ec65 h1:WUINdCB/UvSX9I+wN+y5xVEisPrXA73rxkoHK5DMyZs=
github.com/metacubex/gvisor v0.0.0-20230222112937-bdbcd206ec65/go.mod h1:e3lCxh3TozKMWAsYTC7nBVnepAxPL/sNyScUFvmEoec=
github.com/metacubex/gvisor v0.0.0-20230213124051-7a16c835d80e h1:j4j2dlV2d//FAsQlRUriH6nvv36AEAhECbNy7narf1M=
github.com/metacubex/gvisor v0.0.0-20230213124051-7a16c835d80e/go.mod h1:abc7OdNmWlhcNHz84ECEosd5ND5pnWQmD8W55p/4cuc=
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/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-tun v0.1.1-0.20230222113101-fbfa2dab826d h1:oMzkrEoBdwn2/Vyu0n6/LAmvjxqsyFs+f2kqeg7kI8U=
github.com/metacubex/sing-tun v0.1.1-0.20230222113101-fbfa2dab826d/go.mod h1:WmbtxVPpJulKoQGwfnBMk4KSWzZ68sE/myTrQeN/5GE=
github.com/metacubex/sing-tun v0.1.1-0.20230213124625-28d27a0c236b h1:ZF/oNrSCaxIFoZmFQCiUx67t9aENZjyuqw2n4zw3L2o=
github.com/metacubex/sing-tun v0.1.1-0.20230213124625-28d27a0c236b/go.mod h1:TjuaYuR/g1MaY3um89xTfRNt61FJ2IcI/m5zD8QBxw4=
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/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-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/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/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/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/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.1.8-0.20230221060643-3401d210384b h1:Ji2AfGlc4j9AitobOx4k3BCj7eS5nSxL1cgaL81zvlo=
github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
github.com/sagernet/sing-shadowtls v0.0.0-20230221130515-dac782ca098e h1:S1fd0kB9aEU68dd269AQy783sUlFu/2fSh/4YYVJ/Oc=
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/sing v0.1.7-0.20230207063819-27d2950cdbe9 h1:qnXh4RjHsNjdZXkfbqwVqAzYUfc160gfkS5gepmsA+A=
github.com/sagernet/sing v0.1.7-0.20230207063819-27d2950cdbe9/go.mod h1:JLSXsPTGRJFo/3X7EcAOCUgJH2/gAoxSJgBsnCZRp/w=
github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb h1:oyd3w17fXNmWVYFUe17YVHJW5CLW9X2mxJFDP/IWrAM=
github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb/go.mod h1:9KkmnQzTL4Gvv8U2TRAH2BOITCGsGPpHtUPP5sxn5sY=
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/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/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI=
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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
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/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.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
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-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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
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.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-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.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
golang.org/x/text v0.6.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-20191024005414-555d28b269f0/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-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-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.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.5.0 h1:+bSpV5HIeWkuvgaMfI3UmKRThoTA5ODJTUd8T17NO+4=
golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
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-20191204190536-9bdfabe68543/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.28.2-0.20230118093459-a9481185b34d h1:qp0AnQCvRCMlu9jBjtdbTaaEmThIgZOrbVyDEOcmKhQ=
google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/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 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -81,13 +81,13 @@ func ApplyConfig(cfg *config.Config, force bool) {
updateRules(cfg.Rules, cfg.SubRules, cfg.RuleProviders)
updateSniffer(cfg.Sniffer)
updateHosts(cfg.Hosts)
updateGeneral(cfg.General)
initInnerTcp()
updateDNS(cfg.DNS, cfg.General.IPv6)
loadProxyProvider(cfg.Providers)
updateProfile(cfg)
loadRuleProvider(cfg.RuleProviders)
updateListeners(cfg.General, cfg.Listeners, force)
updateGeneral(cfg.General, force)
updateListeners(cfg.Listeners)
updateIPTables(cfg)
updateTun(cfg.General)
updateExperimental(cfg)
@ -134,34 +134,12 @@ func GetGeneral() *config.General {
return general
}
func updateListeners(general *config.General, listeners map[string]C.InboundListener, force bool) {
func updateListeners(listeners map[string]C.InboundListener) {
tcpIn := tunnel.TCPIn()
udpIn := tunnel.UDPIn()
natTable := tunnel.NatTable()
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) {
@ -169,11 +147,9 @@ func updateExperimental(c *config.Config) {
}
func preUpdateExperimental(c *config.Config) {
CTLS.ResetCertificate()
CTLS.AddCertificate(c.TLS.PrivateKey, c.TLS.Certificate)
for _, c := range c.TLS.CustomTrustCert {
if err := CTLS.AddCertificate(c); err != nil {
log.Warnln("%s\nadd error: %s", c, err.Error())
}
CTLS.AddCertificate(c.PrivateKey, c.Certificate)
}
}
@ -328,28 +304,25 @@ func updateTunnels(tunnels []LC.Tunnel) {
listener.PatchTunnel(tunnels, tunnel.TCPIn(), tunnel.UDPIn())
}
func updateGeneral(general *config.General) {
func updateGeneral(general *config.General, force bool) {
tunnel.SetMode(general.Mode)
tunnel.SetFindProcessMode(general.FindProcessMode)
resolver.DisableIPv6 =!general.IPv6
dialer.DisableIPv6 = !general.IPv6
if !dialer.DisableIPv6 {
log.Infoln("Use IPv6")
}
resolver.DisableIPv6 = dialer.DisableIPv6
if general.TCPConcurrent {
dialer.SetDial(general.TCPConcurrent)
log.Infoln("Use tcp concurrent")
}
inbound.SetTfo(general.InboundTfo)
adapter.UnifiedDelay.Store(general.UnifiedDelay)
// 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.DefaultInterface.Store(general.Interface)
if dialer.DefaultInterface.Load() != "" {
log.Infoln("Use interface name: %s", general.Interface)
}
dialer.DefaultRoutingMark.Store(int32(general.RoutingMark))
@ -358,8 +331,35 @@ func updateGeneral(general *config.General) {
}
iface.FlushCache()
if !force {
return
}
geodataLoader := general.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) {

View File

@ -4,7 +4,6 @@ import (
"github.com/Dreamacro/clash/config"
"github.com/Dreamacro/clash/hub/executor"
"github.com/Dreamacro/clash/hub/route"
"github.com/Dreamacro/clash/log"
)
type Option func(*config.Config)
@ -44,7 +43,7 @@ func Parse(options ...Option) error {
if cfg.General.ExternalController != "" {
go route.Start(cfg.General.ExternalController, cfg.General.ExternalControllerTLS,
cfg.General.Secret, cfg.TLS.Certificate, cfg.TLS.PrivateKey,cfg.General.LogLevel==log.DEBUG)
cfg.General.Secret, cfg.TLS.Certificate, cfg.TLS.PrivateKey)
}
executor.ApplyConfig(cfg, true)

View File

@ -5,7 +5,6 @@ import (
"crypto/tls"
"encoding/json"
"net/http"
"runtime/debug"
"strings"
"time"
@ -14,8 +13,8 @@ import (
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/tunnel/statistic"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors"
"github.com/go-chi/render"
"github.com/gorilla/websocket"
@ -44,7 +43,7 @@ func SetUIPath(path string) {
}
func Start(addr string, tlsAddr string, secret string,
certificat, privateKey string, isDebug bool) {
certificat, privateKey string) {
if serverAddr != "" {
return
}
@ -60,17 +59,6 @@ func Start(addr string, tlsAddr string, secret string,
MaxAge: 300,
})
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.Use(authentication)
r.Get("/", hello)

41
listener/sing/log.go Normal file
View File

@ -0,0 +1,41 @@
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{}

View File

@ -67,26 +67,6 @@ func CalculateInterfaceName(name string) (tunName string) {
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) {
if len(additions) == 0 {
additions = []inbound.Addition{
@ -95,7 +75,7 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte
}
}
tunName := options.Device
if tunName == "" || !checkTunName(tunName) {
if tunName == "" {
tunName = CalculateInterfaceName(InterfaceName)
options.Device = tunName
}
@ -237,7 +217,7 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte
EndpointIndependentNat: options.EndpointIndependentNat,
UDPTimeout: udpTimeout,
Handler: handler,
Logger: log.SingLogger,
Logger: sing.Logger,
})
if err != nil {
return

View File

@ -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) {
l, err := inbound.Listen("tcp", addr)
l, err := net.Listen("tcp", addr)
if err != nil {
return nil, err
}

View File

@ -1,68 +0,0 @@
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{}

View File

@ -1,91 +0,0 @@
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)
}
}

View File

@ -1,33 +1,24 @@
package vless
import (
"bytes"
"crypto/subtle"
gotls "crypto/tls"
"encoding/binary"
"errors"
"fmt"
"io"
"net"
"reflect"
"sync"
"unsafe"
"time"
"github.com/Dreamacro/clash/common/buf"
N "github.com/Dreamacro/clash/common/net"
tlsC "github.com/Dreamacro/clash/component/tls"
"github.com/Dreamacro/clash/log"
"github.com/gofrs/uuid"
utls "github.com/sagernet/utls"
xtls "github.com/xtls/go"
"google.golang.org/protobuf/proto"
)
type Conn struct {
N.ExtendedWriter
N.ExtendedReader
net.Conn
N.ExtendedConn
dst *DstAddr
id *uuid.UUID
addons *Addons
@ -36,143 +27,30 @@ type Conn struct {
handshake chan struct{}
handshakeMutex sync.Mutex
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) {
if vc.received {
if vc.readProcess {
buffer := buf.With(b)
err := vc.ReadBuffer(buffer)
return buffer.Len(), err
}
return vc.ExtendedReader.Read(b)
return vc.ExtendedConn.Read(b)
}
if err := vc.recvResponse(); err != nil {
return 0, err
}
vc.received = true
return vc.Read(b)
return vc.ExtendedConn.Read(b)
}
func (vc *Conn) ReadBuffer(buffer *buf.Buffer) error {
if vc.received {
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 := io.CopyN(io.Discard, vc.ExtendedReader, int64(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)
return vc.ExtendedConn.ReadBuffer(buffer)
}
if err := vc.recvResponse(); err != nil {
return err
}
vc.received = true
return vc.ReadBuffer(buffer)
return vc.ExtendedConn.ReadBuffer(buffer)
}
func (vc *Conn) Write(p []byte) (int, error) {
@ -189,19 +67,7 @@ func (vc *Conn) Write(p []byte) (int, error) {
return 0, vc.err
}
}
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)
return vc.ExtendedConn.Write(p)
}
func (vc *Conn) WriteBuffer(buffer *buf.Buffer) error {
@ -215,64 +81,7 @@ func (vc *Conn) WriteBuffer(buffer *buf.Buffer) error {
return vc.err
}
}
if vc.writeFilterApplicationData {
buffer2 := ReshapeBuffer(buffer)
defer buffer2.Release()
vc.FilterTLS(buffer.Bytes())
command := commandPaddingContinue
if !vc.isTLS {
command = commandPaddingEnd
// disable XTLS
vc.readProcess = false
vc.writeFilterApplicationData = false
vc.packetsToFilter = 0
} else 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 || !vc.isTLS {
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)
return vc.ExtendedConn.WriteBuffer(buffer)
}
func (vc *Conn) sendRequest(p []byte) bool {
@ -288,6 +97,9 @@ func (vc *Conn) sendRequest(p []byte) bool {
}
defer close(vc.handshake)
requestLen := 1 // protocol version
requestLen += 16 // UUID
requestLen += 1 // addons length
var addonsBytes []byte
if vc.addons != nil {
addonsBytes, vc.err = proto.Marshal(vc.addons)
@ -295,32 +107,19 @@ func (vc *Conn) sendRequest(p []byte) bool {
return true
}
}
isVision := vc.IsXTLSVisionEnabled()
var buffer *buf.Buffer
if isVision {
_buffer := buf.StackNew()
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()
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(
buffer.WriteByte(Version), // protocol version
@ -345,51 +144,15 @@ func (vc *Conn) sendRequest(p []byte) bool {
)
}
if isVision && !vc.dst.UDP && !vc.dst.Mux {
if len(p) == 0 {
WriteWithPadding(buffer, nil, commandPaddingContinue, vc.id)
} else {
vc.FilterTLS(p)
if vc.isTLS {
WriteWithPadding(buffer, p, commandPaddingContinue, vc.id)
} else {
buf.Must(buf.Error(buffer.Write(p)))
buf.Must(buf.Error(buffer.Write(p)))
// disable XTLS
vc.readProcess = false
vc.writeFilterApplicationData = false
vc.packetsToFilter = 0
}
}
} else {
buf.Must(buf.Error(buffer.Write(p)))
}
_, 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
}
_, vc.err = vc.ExtendedConn.Write(buffer.Bytes())
return true
}
func (vc *Conn) recvResponse() error {
var buf [1]byte
_, vc.err = io.ReadFull(vc.ExtendedReader, buf[:])
_, vc.err = io.ReadFull(vc.ExtendedConn, buf[:])
if vc.err != nil {
return vc.err
}
@ -398,46 +161,30 @@ func (vc *Conn) recvResponse() error {
return errors.New("unexpected response version")
}
_, vc.err = io.ReadFull(vc.ExtendedReader, buf[:])
_, vc.err = io.ReadFull(vc.ExtendedConn, buf[:])
if vc.err != nil {
return vc.err
}
length := int64(buf[0])
if length != 0 { // addon data length > 0
io.CopyN(io.Discard, vc.ExtendedReader, length) // just discard
io.CopyN(io.Discard, vc.ExtendedConn, length) // just discard
}
return nil
}
func (vc *Conn) FrontHeadroom() int {
if vc.IsXTLSVisionEnabled() {
return paddingHeaderLen
}
return 0
}
func (vc *Conn) Upstream() any {
if vc.tlsConn == nil {
return vc.Conn
}
return vc.tlsConn
}
func (vc *Conn) IsXTLSVisionEnabled() bool {
return vc.addons != nil && vc.addons.Flow == XRV
return vc.ExtendedConn
}
// newConn return a Conn instance
func newConn(conn net.Conn, client *Client, dst *DstAddr) (*Conn, error) {
c := &Conn{
ExtendedReader: N.NewExtendedReader(conn),
ExtendedWriter: N.NewExtendedWriter(conn),
Conn: conn,
id: client.uuid,
dst: dst,
handshake: make(chan struct{}),
ExtendedConn: N.NewExtendedConn(conn),
id: client.uuid,
dst: dst,
handshake: make(chan struct{}),
}
if !dst.UDP && client.Addons != nil {
@ -458,42 +205,15 @@ func newConn(conn net.Conn, client *Client, dst *DstAddr) (*Conn, error) {
} else {
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 %s", reflect.TypeOf(conn).Name())
}
}
}
go func() {
select {
case <-c.handshake:
case <-time.After(200 * time.Millisecond):
c.sendRequest(nil)
}
}()
return c, nil
}

View File

@ -1,79 +0,0 @@
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
}

View File

@ -1,69 +0,0 @@
package vless
import (
"bytes"
"encoding/binary"
"math/rand"
"github.com/Dreamacro/clash/common/buf"
"github.com/Dreamacro/clash/log"
"github.com/gofrs/uuid"
)
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() <= buf.BufferSize-paddingHeaderLen {
return nil
}
cutAt := bytes.LastIndex(buffer.Bytes(), tlsApplicationDataStart)
if cutAt == -1 {
cutAt = buf.BufferSize / 2
}
buffer2 := buf.New()
buffer2.Write(buffer.From(cutAt))
buffer.Truncate(cutAt)
return buffer2
}

View File

@ -12,7 +12,6 @@ const (
XRO = "xtls-rprx-origin"
XRD = "xtls-rprx-direct"
XRS = "xtls-rprx-splice"
XRV = "xtls-rprx-vision"
Version byte = 0 // protocol version. preview version is 0
)

View File

@ -2,7 +2,6 @@ package vless
import (
"context"
"errors"
"net"
tlsC "github.com/Dreamacro/clash/component/tls"
@ -10,10 +9,6 @@ import (
xtls "github.com/xtls/go"
)
var (
ErrNotTLS13 = errors.New("XTLS Vision based on TLS 1.3 outer connection")
)
type XTLSConfig struct {
Host string
SkipCertVerify bool

View File

@ -81,7 +81,7 @@ func (tt *tcpTracker) Upstream() any {
return tt.Conn
}
func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.Rule, uploadTotal int64, downloadTotal int64) *tcpTracker {
func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.Rule) *tcpTracker {
uuid, _ := uuid.NewV4()
if conn != nil {
if tcpAddr, ok := conn.RemoteAddr().(*net.TCPAddr); ok {
@ -100,8 +100,8 @@ func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.R
Metadata: metadata,
Chain: conn.Chains(),
Rule: "",
UploadTotal: atomic.NewInt64(uploadTotal),
DownloadTotal: atomic.NewInt64(downloadTotal),
UploadTotal: atomic.NewInt64(0),
DownloadTotal: atomic.NewInt64(0),
},
extendedReader: N.NewExtendedReader(conn),
extendedWriter: N.NewExtendedWriter(conn),
@ -147,7 +147,7 @@ func (ut *udpTracker) Close() error {
return ut.PacketConn.Close()
}
func NewUDPTracker(conn C.PacketConn, manager *Manager, metadata *C.Metadata, rule C.Rule, uploadTotal int64, downloadTotal int64) *udpTracker {
func NewUDPTracker(conn C.PacketConn, manager *Manager, metadata *C.Metadata, rule C.Rule) *udpTracker {
uuid, _ := uuid.NewV4()
metadata.RemoteDst = conn.RemoteDestination()
@ -160,8 +160,8 @@ func NewUDPTracker(conn C.PacketConn, manager *Manager, metadata *C.Metadata, ru
Metadata: metadata,
Chain: conn.Chains(),
Rule: "",
UploadTotal: atomic.NewInt64(uploadTotal),
DownloadTotal: atomic.NewInt64(downloadTotal),
UploadTotal: atomic.NewInt64(0),
DownloadTotal: atomic.NewInt64(0),
},
}

View File

@ -322,7 +322,7 @@ func handleUDPConn(packet C.PacketAdapter) {
}
pCtx.InjectPacketConn(rawPc)
pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule, 0, 0)
pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule)
switch true {
case metadata.SpecialProxy != "":
@ -366,21 +366,8 @@ func handleTCPConn(connCtx C.ConnContext) {
return
}
conn := connCtx.Conn()
conn.ResetPeeked()
if sniffer.Dispatcher.Enable() && sniffingEnable {
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{})
}()
sniffer.Dispatcher.TCPSniff(connCtx.Conn(), metadata)
}
proxy, rule, err := resolveMetadata(connCtx, metadata)
@ -400,27 +387,10 @@ func handleTCPConn(connCtx C.ConnContext) {
}
}
var peekBytes []byte
var peekLen int
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTCPTimeout)
defer cancel()
remoteConn, err := retry(ctx, func(ctx context.Context) (C.Conn, error) {
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
return proxy.DialContext(ctx, dialMetadata)
}, func(err error) {
if rule == nil {
log.Warnln(
@ -438,7 +408,7 @@ func handleTCPConn(connCtx C.ConnContext) {
return
}
remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule, 0, int64(peekLen))
remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule)
defer func(remoteConn C.Conn) {
_ = remoteConn.Close()
}(remoteConn)