From f89ecd97d60fff44285a2de8e3742c0b560fb9c3 Mon Sep 17 00:00:00 2001 From: H1JK Date: Mon, 14 Aug 2023 15:11:33 +0800 Subject: [PATCH 01/66] feat: Converter unofficial TUIC share link support --- common/convert/converter.go | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/common/convert/converter.go b/common/convert/converter.go index b67918db..1f1b086d 100644 --- a/common/convert/converter.go +++ b/common/convert/converter.go @@ -67,6 +67,47 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { proxies = append(proxies, hysteria) + case "tuic": + // A temporary unofficial TUIC share link standard + // Modified from https://github.com/daeuniverse/dae/discussions/182 + // Changes: + // 1. Support TUICv4, just replace uuid:password with token + // 2. Remove `allow_insecure` field + urlTUIC, err := url.Parse(line) + if err != nil { + continue + } + query := urlTUIC.Query() + + tuic := make(map[string]any, 20) + tuic["name"] = uniqueName(names, urlTUIC.Fragment) + tuic["type"] = scheme + tuic["server"] = urlTUIC.Hostname() + tuic["port"] = urlTUIC.Port() + tuic["udp"] = true + password, v5 := urlTUIC.User.Password() + if v5 { + tuic["uuid"] = urlTUIC.User.Username() + tuic["password"] = password + } else { + tuic["token"] = urlTUIC.User.Username() + } + if cc := query.Get("congestion_control"); cc != "" { + tuic["congestion-controller"] = cc + } + if alpn := query.Get("alpn"); alpn != "" { + tuic["alpn"] = strings.Split(alpn, ",") + } + if sni := query.Get("sni"); sni != "" { + tuic["sni"] = sni + } + if query.Get("disable_sni") == "1" { + tuic["disable-sni"] = true + } + if udpRelayMode := query.Get("udp_relay_mode"); udpRelayMode != "" { + tuic["udp-relay-mode"] = udpRelayMode + } + case "trojan": urlTrojan, err := url.Parse(line) if err != nil { From ed09df4e133ec355c8c01bdfc68f453fce2ea9f7 Mon Sep 17 00:00:00 2001 From: H1JK Date: Mon, 14 Aug 2023 15:48:13 +0800 Subject: [PATCH 02/66] fix: TLS ALPN support --- adapter/outbound/vless.go | 2 ++ adapter/outbound/vmess.go | 3 +++ common/buf/sing.go | 7 ------- common/convert/converter.go | 14 +++++++++++--- common/convert/v.go | 5 +++-- transport/gun/utils.go | 26 +++++++++++++++++++++----- 6 files changed, 40 insertions(+), 17 deletions(-) diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index 83ce4e57..2456c2c3 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -57,6 +57,7 @@ type VlessOption struct { UUID string `proxy:"uuid"` Flow string `proxy:"flow,omitempty"` TLS bool `proxy:"tls,omitempty"` + ALPN []string `proxy:"alpn,omitempty"` UDP bool `proxy:"udp,omitempty"` PacketAddr bool `proxy:"packet-addr,omitempty"` XUDP bool `proxy:"xudp,omitempty"` @@ -211,6 +212,7 @@ func (v *Vless) streamTLSConn(ctx context.Context, conn net.Conn, isH2 bool) (ne FingerPrint: v.option.Fingerprint, ClientFingerprint: v.option.ClientFingerprint, Reality: v.realityConfig, + NextProtos: v.option.ALPN, } if isH2 { diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 8a94c082..7495d8a3 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -52,6 +52,7 @@ type VmessOption struct { UDP bool `proxy:"udp,omitempty"` Network string `proxy:"network,omitempty"` TLS bool `proxy:"tls,omitempty"` + ALPN []string `proxy:"alpn,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"` ServerName string `proxy:"servername,omitempty"` @@ -149,6 +150,7 @@ func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M SkipCertVerify: v.option.SkipCertVerify, ClientFingerprint: v.option.ClientFingerprint, Reality: v.realityConfig, + NextProtos: v.option.ALPN, } if v.option.ServerName != "" { @@ -205,6 +207,7 @@ func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M SkipCertVerify: v.option.SkipCertVerify, ClientFingerprint: v.option.ClientFingerprint, Reality: v.realityConfig, + NextProtos: v.option.ALPN, } if v.option.ServerName != "" { diff --git a/common/buf/sing.go b/common/buf/sing.go index 4585bf74..d204ba11 100644 --- a/common/buf/sing.go +++ b/common/buf/sing.go @@ -14,13 +14,6 @@ var NewSize = buf.NewSize var With = buf.With var As = buf.As -var KeepAlive = common.KeepAlive - -//go:norace -func Dup[T any](obj T) T { - return common.Dup(obj) -} - var ( Must = common.Must Error = common.Error diff --git a/common/convert/converter.go b/common/convert/converter.go index 1f1b086d..ddc5c944 100644 --- a/common/convert/converter.go +++ b/common/convert/converter.go @@ -50,7 +50,9 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { hysteria["port"] = urlHysteria.Port() hysteria["sni"] = query.Get("peer") hysteria["obfs"] = query.Get("obfs") - hysteria["alpn"] = []string{query.Get("alpn")} + if alpn := query.Get("alpn"); alpn != "" { + hysteria["alpn"] = strings.Split(alpn, ",") + } hysteria["auth_str"] = query.Get("auth") hysteria["protocol"] = query.Get("protocol") up := query.Get("up") @@ -127,10 +129,12 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { trojan["udp"] = true trojan["skip-cert-verify"], _ = strconv.ParseBool(query.Get("allowInsecure")) - sni := query.Get("sni") - if sni != "" { + if sni := query.Get("sni"); sni != "" { trojan["sni"] = sni } + if alpn := query.Get("alpn"); alpn != "" { + trojan["alpn"] = strings.Split(alpn, ",") + } network := strings.ToLower(query.Get("type")) if network != "" { @@ -258,6 +262,9 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { if strings.HasSuffix(tls, "tls") { vmess["tls"] = true } + if alpn, ok := values["alpn"].(string); ok { + vmess["alpn"] = strings.Split(alpn, ",") + } } switch network { @@ -373,6 +380,7 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { } } proxies = append(proxies, ss) + case "ssr": dcBuf, err := encRaw.DecodeString(body) if err != nil { diff --git a/common/convert/v.go b/common/convert/v.go index 23949aab..2d8cf732 100644 --- a/common/convert/v.go +++ b/common/convert/v.go @@ -24,8 +24,6 @@ func handleVShareLink(names map[string]int, url *url.URL, scheme string, proxy m proxy["port"] = url.Port() proxy["uuid"] = url.User.Username() proxy["udp"] = true - proxy["skip-cert-verify"] = false - proxy["tls"] = false tls := strings.ToLower(query.Get("security")) if strings.HasSuffix(tls, "tls") || tls == "reality" { proxy["tls"] = true @@ -34,6 +32,9 @@ func handleVShareLink(names map[string]int, url *url.URL, scheme string, proxy m } else { proxy["client-fingerprint"] = fingerprint } + if alpn := query.Get("alpn"); alpn != "" { + proxy["alpn"] = strings.Split(alpn, ",") + } } if sni := query.Get("sni"); sni != "" { proxy["servername"] = sni diff --git a/transport/gun/utils.go b/transport/gun/utils.go index e5f6e019..e4a66315 100644 --- a/transport/gun/utils.go +++ b/transport/gun/utils.go @@ -1,10 +1,26 @@ package gun func UVarintLen(x uint64) int { - i := 0 - for x >= 0x80 { - x >>= 7 - i++ + switch { + case x < 1<<(7*1): + return 1 + case x < 1<<(7*2): + return 2 + case x < 1<<(7*3): + return 3 + case x < 1<<(7*4): + return 4 + case x < 1<<(7*5): + return 5 + case x < 1<<(7*6): + return 6 + case x < 1<<(7*7): + return 7 + case x < 1<<(7*8): + return 8 + case x < 1<<(7*9): + return 9 + default: + return 10 } - return i + 1 } From 03b02525897e5f06990a8677a86c2546ae3045f2 Mon Sep 17 00:00:00 2001 From: 3andne <52860475+3andne@users.noreply.github.com> Date: Tue, 15 Aug 2023 20:41:58 -0700 Subject: [PATCH 03/66] feat: bump restls to v0.1.6 (utls v1.4.3) (#692) * feat: bump restls to v0.1.5 (utls v1.4.3) * fix: rm dependency go-quic --- adapter/outbound/shadowsocks.go | 3 +-- go.mod | 5 +++-- go.sum | 10 ++++++---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index c1481622..0f46cf79 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -19,7 +19,7 @@ import ( v2rayObfs "github.com/Dreamacro/clash/transport/v2ray-plugin" restlsC "github.com/3andne/restls-client-go" - "github.com/metacubex/sing-shadowsocks2" + shadowsocks "github.com/metacubex/sing-shadowsocks2" M "github.com/sagernet/sing/common/metadata" "github.com/sagernet/sing/common/uot" ) @@ -294,7 +294,6 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { } restlsConfig, err = restlsC.NewRestlsConfig(restlsOpt.Host, restlsOpt.Password, restlsOpt.VersionHint, restlsOpt.RestlsScript, option.ClientFingerprint) - restlsConfig.SessionTicketsDisabled = true if err != nil { return nil, fmt.Errorf("ss %s initialize restls-plugin error: %w", addr, err) } diff --git a/go.mod b/go.mod index 3167d9ea..9f8da5a4 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/Dreamacro/clash go 1.20 require ( - github.com/3andne/restls-client-go v0.1.4 + github.com/3andne/restls-client-go v0.1.6 github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da github.com/cilium/ebpf v0.11.0 github.com/coreos/go-iptables v0.7.0 @@ -64,6 +64,7 @@ require ( github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/gaukas/godicttls v0.0.4 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/golang/mock v1.6.0 // indirect @@ -72,7 +73,7 @@ require ( github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/josharian/native v1.1.0 // indirect - github.com/klauspost/compress v1.15.15 // indirect + github.com/klauspost/compress v1.16.7 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mdlayher/socket v0.4.1 // indirect github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475 // indirect diff --git a/go.sum b/go.sum index a90f4dba..63d633e1 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/3andne/restls-client-go v0.1.4 h1:kLNC2aSRHPlEVYmTj6EOqJoorCpobEe2toMRSfBF7FU= -github.com/3andne/restls-client-go v0.1.4/go.mod h1:04CGbRk1BwBiEDles8b5mlKgTqIwE5MqF7JDloJV47I= +github.com/3andne/restls-client-go v0.1.6 h1:tRx/YilqW7iHpgmEL4E1D8dAsuB0tFF3uvncS+B6I08= +github.com/3andne/restls-client-go v0.1.6/go.mod h1:iEdTZNt9kzPIxjIGSMScUFSBrUH6bFRNg0BWlP4orEY= github.com/RyuaNerin/go-krypto v1.0.2 h1:9KiZrrBs+tDrQ66dNy4nrX6SzntKtSKdm0wKHhdB4WM= github.com/RyuaNerin/go-krypto v1.0.2/go.mod h1:17LzMeJCgzGTkPH3TmfzRnEJ/yA7ErhTPp9sxIqONtA= github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 h1:cDVUiFo+npB0ZASqnw4q90ylaVAbnYyx0JYqK4YcGok= @@ -35,6 +35,8 @@ github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtB github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk= +github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI= github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= @@ -74,8 +76,8 @@ github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtL github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -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.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= From 574efb4526bd518c12236840238c39041fec286d Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 16 Aug 2023 21:30:12 +0800 Subject: [PATCH 04/66] chore: Update dependencies --- adapter/outbound/wireguard.go | 2 +- go.mod | 14 +++++++------- go.sum | 25 ++++++++++++------------- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index e6738596..c1050ac6 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -302,7 +302,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { if err != nil { return nil, E.Cause(err, "create WireGuard device") } - outbound.device = device.NewDevice(outbound.tunDevice, outbound.bind, &device.Logger{ + outbound.device = device.NewDevice(context.Background(), outbound.tunDevice, outbound.bind, &device.Logger{ Verbosef: func(format string, args ...interface{}) { log.SingLogger.Debug(fmt.Sprintf("[WG](%s) %s", option.Name, fmt.Sprintf(format, args...))) }, diff --git a/go.mod b/go.mod index 9f8da5a4..15babf72 100644 --- a/go.mod +++ b/go.mod @@ -26,16 +26,16 @@ require ( github.com/metacubex/sing-vmess v0.1.8-0.20230801054944-603005461ff8 github.com/metacubex/sing-wireguard v0.0.0-20230611155257-1498ae315a28 github.com/miekg/dns v1.1.55 - github.com/mroth/weightedrand/v2 v2.0.2 + github.com/mroth/weightedrand/v2 v2.1.0 github.com/openacid/low v0.1.21 github.com/oschwald/maxminddb-golang v1.12.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.2.9 - github.com/sagernet/sing-mux v0.1.2 + github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a + github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c github.com/sagernet/sing-shadowtls v0.1.4 - github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 + github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 - github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 + github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f github.com/samber/lo v1.38.1 github.com/shirou/gopsutil/v3 v3.23.7 github.com/sirupsen/logrus v1.9.3 @@ -44,7 +44,7 @@ require ( go.etcd.io/bbolt v1.3.7 go.uber.org/automaxprocs v1.5.3 golang.org/x/crypto v0.12.0 - golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b + golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb golang.org/x/net v0.14.0 golang.org/x/sync v0.3.0 golang.org/x/sys v0.11.0 @@ -103,4 +103,4 @@ require ( golang.org/x/tools v0.9.1 // indirect ) -replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20230714010500-e24664dc75a7 +replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20230816132220-60e73ba2f50f diff --git a/go.sum b/go.sum index 63d633e1..a1c16de2 100644 --- a/go.sum +++ b/go.sum @@ -96,8 +96,8 @@ github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475 h1:qSEOvPPaMrWggF github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475/go.mod h1:wehEpqiogdeyncfhckJP5gD2LtBgJW0wnDC24mJ+8Jg= github.com/metacubex/quic-go v0.37.4-0.20230809092428-5acf8eb2de86 h1:qGExcB3lYk51LPEJh5HTdQplbZmuTn+tkcuhuas1LC8= github.com/metacubex/quic-go v0.37.4-0.20230809092428-5acf8eb2de86/go.mod h1:HhHoyskMk4kzfLPKcm7EF7pGXF89KRVwjbGrEaN6lIU= -github.com/metacubex/sing v0.0.0-20230714010500-e24664dc75a7 h1:XY3Y6nPL45XuN/k3rDXJ1TJknLo8rTo1SVuDOmOEf4E= -github.com/metacubex/sing v0.0.0-20230714010500-e24664dc75a7/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= +github.com/metacubex/sing v0.0.0-20230816132220-60e73ba2f50f h1:rtZaKvzyfIDCciywrYP2mWN3fVL409xfU/VIVddnTbg= +github.com/metacubex/sing v0.0.0-20230816132220-60e73ba2f50f/go.mod h1:9uOZwWkhT2Z2WldolLxX34s+1svAX4i4vvz5hy8u1MA= github.com/metacubex/sing-shadowsocks v0.2.4 h1:Gc99Z17JVif1PKKq1pjqhSmc2kvHUgk+AqxOstCzhQ0= github.com/metacubex/sing-shadowsocks v0.2.4/go.mod h1:w9qoEZSh9aKeXSLXHe0DGbG2UE9/2VlLGwukzQZ7byI= github.com/metacubex/sing-shadowsocks2 v0.1.3 h1:nZvH+4jQXZ92NeNdR9fXaUGTPNJPt6u0nkcuh/NEt5Y= @@ -110,8 +110,8 @@ github.com/metacubex/sing-wireguard v0.0.0-20230611155257-1498ae315a28 h1:mXFpxf github.com/metacubex/sing-wireguard v0.0.0-20230611155257-1498ae315a28/go.mod h1:KrDPq/dE793jGIJw9kcIvjA/proAfU0IeU7WlMXW7rs= github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= -github.com/mroth/weightedrand/v2 v2.0.2 h1:A8wJRUBcfguGl6oOQHI8fy5P4ViGRT9hdQdlG/7RiXo= -github.com/mroth/weightedrand/v2 v2.0.2/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU= +github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU= +github.com/mroth/weightedrand/v2 v2.1.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU= github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 h1:1102pQc2SEPp5+xrS26wEaeb26sZy6k9/ZXlZN+eXE4= github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7/go.mod h1:UqoUn6cHESlliMhOnKLWr+CBH+e3bazUPvFj1XZwAjs= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= @@ -141,18 +141,18 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c 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-mux v0.1.2 h1:av2/m6e+Gh+ECTuJZqYCjJz55BNkot0VyRMkREqyF/g= -github.com/sagernet/sing-mux v0.1.2/go.mod h1:r2V8AlOzXaRCHXK7fILCUGzuI2iILweTaG8C5xlpHxo= +github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c h1:35/FowAvt3Z62mck0TXzVc4jS5R5CWq62qcV2P1cp0I= +github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c/go.mod h1:TKxqIvfQQgd36jp2tzsPavGjYTVZilV+atip1cssjIY= github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k= github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0= -github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE= -github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9/go.mod h1:FUyTEc5ye5NjKnDTDMuiLF2M6T4BE6y6KZuax//UCEg= +github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 h1:Px+hN4Vzgx+iCGVnWH5A8eR7JhNnIV3rGQmBxA7cw6Q= +github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6/go.mod h1:zovq6vTvEM6ECiqE3Eeb9rpIylPpamPcmrJ9tv0Bt0M= github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4= github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM= -github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 h1:g6QtRWQ2dKX7EQP++1JLNtw4C2TNxd4/ov8YUpOPOSo= -github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77/go.mod h1:pJDdXzZIwJ+2vmnT0TKzmf8meeum+e2mTDSehw79eE0= +github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f h1:Kvo8w8Y9lzFGB/7z09MJ3TR99TFtfI/IuY87Ygcycho= +github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f/go.mod h1:mySs0abhpc/gLlvhoq7HP1RzOaRmIXVeZGCh++zoApk= github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg= @@ -207,8 +207,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b h1:r+vk0EmXNmekl0S0BascoeeoHk/L7wmaW2QF90K+kYI= -golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb h1:mIKbk8weKhSeLH2GmUTrvx8CjkyJmnU1wFmg59CUjFA= +golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= 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= @@ -239,7 +239,6 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= From db68d55a0eaf24664ec8853918bf3d2ddf6623f5 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 17 Aug 2023 22:33:07 +0800 Subject: [PATCH 05/66] fix: sing-vmess panic --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 15babf72..6fe21a39 100644 --- a/go.mod +++ b/go.mod @@ -103,4 +103,4 @@ require ( golang.org/x/tools v0.9.1 // indirect ) -replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20230816132220-60e73ba2f50f +replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20230817143035-28d23f152579 diff --git a/go.sum b/go.sum index a1c16de2..07e0bacd 100644 --- a/go.sum +++ b/go.sum @@ -96,8 +96,8 @@ github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475 h1:qSEOvPPaMrWggF github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475/go.mod h1:wehEpqiogdeyncfhckJP5gD2LtBgJW0wnDC24mJ+8Jg= github.com/metacubex/quic-go v0.37.4-0.20230809092428-5acf8eb2de86 h1:qGExcB3lYk51LPEJh5HTdQplbZmuTn+tkcuhuas1LC8= github.com/metacubex/quic-go v0.37.4-0.20230809092428-5acf8eb2de86/go.mod h1:HhHoyskMk4kzfLPKcm7EF7pGXF89KRVwjbGrEaN6lIU= -github.com/metacubex/sing v0.0.0-20230816132220-60e73ba2f50f h1:rtZaKvzyfIDCciywrYP2mWN3fVL409xfU/VIVddnTbg= -github.com/metacubex/sing v0.0.0-20230816132220-60e73ba2f50f/go.mod h1:9uOZwWkhT2Z2WldolLxX34s+1svAX4i4vvz5hy8u1MA= +github.com/metacubex/sing v0.0.0-20230817143035-28d23f152579 h1:dE1dBB6CTzNdSMFTE5OCHvzHLewiqiA1nhD+7egtvAc= +github.com/metacubex/sing v0.0.0-20230817143035-28d23f152579/go.mod h1:9uOZwWkhT2Z2WldolLxX34s+1svAX4i4vvz5hy8u1MA= github.com/metacubex/sing-shadowsocks v0.2.4 h1:Gc99Z17JVif1PKKq1pjqhSmc2kvHUgk+AqxOstCzhQ0= github.com/metacubex/sing-shadowsocks v0.2.4/go.mod h1:w9qoEZSh9aKeXSLXHe0DGbG2UE9/2VlLGwukzQZ7byI= github.com/metacubex/sing-shadowsocks2 v0.1.3 h1:nZvH+4jQXZ92NeNdR9fXaUGTPNJPt6u0nkcuh/NEt5Y= From b8a60261efb2ebe9f5a1df95f52e9669aa20e28c Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Fri, 18 Aug 2023 22:17:07 +0800 Subject: [PATCH 06/66] chore: restore unselected clear selected node in outboundgoup/URLtest when getGroupDelay triggered --- hub/route/groups.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hub/route/groups.go b/hub/route/groups.go index e5b61fb5..c82207f0 100644 --- a/hub/route/groups.go +++ b/hub/route/groups.go @@ -9,6 +9,7 @@ import ( "time" "github.com/Dreamacro/clash/adapter" + "github.com/Dreamacro/clash/adapter/outboundgroup" "github.com/Dreamacro/clash/common/utils" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/tunnel" @@ -57,6 +58,11 @@ func getGroupDelay(w http.ResponseWriter, r *http.Request) { return } + if proxy.(*adapter.Proxy).Type() == C.URLTest { + URLTestGroup := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.URLTest) + URLTestGroup.ForceSet("") + } + query := r.URL.Query() url := query.Get("url") timeout, err := strconv.ParseInt(query.Get("timeout"), 10, 32) @@ -77,7 +83,6 @@ func getGroupDelay(w http.ResponseWriter, r *http.Request) { defer cancel() dm, err := group.URLTest(ctx, url, expectedStatus) - if err != nil { render.Status(r, http.StatusGatewayTimeout) render.JSON(w, r, newError(err.Error())) From 1181fd456077b68aaa4ffbacef6626f807638bc5 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 21 Aug 2023 12:37:39 +0800 Subject: [PATCH 07/66] feat: add `udp-over-stream` for tuic only work with meta tuic server or sing-box 1.4.0-beta.6 --- adapter/outbound/tuic.go | 41 ++++++++++++++++++++++++++++++++++++++++ docs/config.yaml | 5 +++++ listener/sing/sing.go | 29 +++++++++++++++++++++++++++- listener/tuic/server.go | 22 ++++++++++++++++++++- 4 files changed, 95 insertions(+), 2 deletions(-) diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index c10a853a..4d826912 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -6,6 +6,7 @@ import ( "crypto/tls" "encoding/hex" "encoding/pem" + "errors" "fmt" "math" "net" @@ -15,12 +16,15 @@ import ( "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/proxydialer" + "github.com/Dreamacro/clash/component/resolver" tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/tuic" "github.com/gofrs/uuid/v5" "github.com/metacubex/quic-go" + M "github.com/sagernet/sing/common/metadata" + "github.com/sagernet/sing/common/uot" ) type Tuic struct { @@ -59,6 +63,9 @@ type TuicOption struct { DisableMTUDiscovery bool `proxy:"disable-mtu-discovery,omitempty"` MaxDatagramFrameSize int `proxy:"max-datagram-frame-size,omitempty"` SNI string `proxy:"sni,omitempty"` + + UDPOverStream bool `proxy:"udp-over-stream,omitempty"` + UDPOverStreamVersion int `proxy:"udp-over-stream-version,omitempty"` } // DialContext implements C.ProxyAdapter @@ -82,6 +89,32 @@ func (t *Tuic) ListenPacketContext(ctx context.Context, metadata *C.Metadata, op // ListenPacketWithDialer implements C.ProxyAdapter func (t *Tuic) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) { + if t.option.UDPOverStream { + uotDestination := uot.RequestDestination(uint8(t.option.UDPOverStreamVersion)) + uotMetadata := *metadata + uotMetadata.Host = uotDestination.Fqdn + uotMetadata.DstPort = uotDestination.Port + c, err := t.DialContextWithDialer(ctx, dialer, &uotMetadata) + if err != nil { + return nil, err + } + + // tuic uos use stream-oriented udp with a special address, so we need a net.UDPAddr + if !metadata.Resolved() { + ip, err := resolver.ResolveIP(ctx, metadata.Host) + if err != nil { + return nil, errors.New("can't resolve ip") + } + metadata.DstIP = ip + } + + destination := M.SocksaddrFromNet(metadata.UDPAddr()) + if t.option.UDPOverStreamVersion == uot.LegacyVersion { + return newPacketConn(uot.NewConn(c, uot.Request{Destination: destination}), t), nil + } else { + return newPacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination}), t), nil + } + } pc, err := t.client.ListenPacketWithDialer(ctx, metadata, dialer, t.dialWithDialer) if err != nil { return nil, err @@ -239,6 +272,14 @@ func NewTuic(option TuicOption) (*Tuic, error) { tlsConfig.InsecureSkipVerify = true // tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config } + switch option.UDPOverStreamVersion { + case uot.Version, uot.LegacyVersion: + case 0: + option.UDPOverStreamVersion = uot.LegacyVersion + default: + return nil, fmt.Errorf("tuic %s unknown udp over stream protocol version: %d", addr, option.UDPOverStreamVersion) + } + t := &Tuic{ Base: &Base{ name: option.Name, diff --git a/docs/config.yaml b/docs/config.yaml index 6ae3910e..62cd0c58 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -681,6 +681,11 @@ proxies: # socks5 # skip-cert-verify: true # max-open-streams: 20 # default 100, too many open streams may hurt performance # sni: example.com + # + # meta和sing-box私有扩展,将ss-uot用于udp中继,开启此选项后udp-relay-mode将失效 + # 警告,与原版tuic不兼容!!! + # udp-over-stream: false + # udp-over-stream-version: 1 # ShadowsocksR # The supported ciphers (encryption methods): all stream ciphers in ss diff --git a/listener/sing/sing.go b/listener/sing/sing.go index f59fd613..d5731bbf 100644 --- a/listener/sing/sing.go +++ b/listener/sing/sing.go @@ -72,7 +72,27 @@ func UpstreamMetadata(metadata M.Metadata) M.Metadata { } } -func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { +func ConvertMetadata(metadata *C.Metadata) M.Metadata { + return M.Metadata{ + Protocol: metadata.Type.String(), + Source: M.SocksaddrFrom(metadata.SrcIP, metadata.SrcPort), + Destination: M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort), + } +} + +func (h *ListenerHandler) IsSpecialFqdn(fqdn string) bool { + switch fqdn { + case mux.Destination.Fqdn: + case vmess.MuxDestination.Fqdn: + case uot.MagicAddress: + case uot.LegacyMagicAddress: + default: + return false + } + return true +} + +func (h *ListenerHandler) ParseSpecialFqdn(ctx context.Context, conn net.Conn, metadata M.Metadata) error { switch metadata.Destination.Fqdn { case mux.Destination.Fqdn: return mux.HandleConnection(ctx, h, log.SingLogger, conn, UpstreamMetadata(metadata)) @@ -89,6 +109,13 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta metadata.Destination = M.Socksaddr{Addr: netip.IPv4Unspecified()} return h.NewPacketConnection(ctx, uot.NewConn(conn, uot.Request{}), metadata) } + return errors.New("not special fqdn") +} + +func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { + if h.IsSpecialFqdn(metadata.Destination.Fqdn) { + return h.ParseSpecialFqdn(ctx, conn, metadata) + } target := socks5.ParseAddr(metadata.Destination.String()) wg := &sync.WaitGroup{} defer wg.Wait() // this goroutine must exit after conn.Close() diff --git a/listener/tuic/server.go b/listener/tuic/server.go index 76996b27..125c53e1 100644 --- a/listener/tuic/server.go +++ b/listener/tuic/server.go @@ -1,6 +1,7 @@ package tuic import ( + "context" "crypto/tls" "net" "strings" @@ -11,6 +12,7 @@ import ( "github.com/Dreamacro/clash/common/sockopt" C "github.com/Dreamacro/clash/constant" LC "github.com/Dreamacro/clash/listener/config" + "github.com/Dreamacro/clash/listener/sing" "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/transport/socks5" "github.com/Dreamacro/clash/transport/tuic" @@ -36,6 +38,12 @@ func New(config LC.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.Packet inbound.WithSpecialRules(""), } } + h := &sing.ListenerHandler{ + TcpIn: tcpIn, + UdpIn: udpIn, + Type: C.TUIC, + Additions: additions, + } cert, err := CN.ParseCert(config.Certificate, config.PrivateKey) if err != nil { return nil, err @@ -86,7 +94,19 @@ func New(config LC.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.Packet newAdditions = slices.Clone(additions) newAdditions = append(newAdditions, _additions...) } - tcpIn <- inbound.NewSocket(addr, conn, C.TUIC, newAdditions...) + connCtx := inbound.NewSocket(addr, conn, C.TUIC, newAdditions...) + metadata := sing.ConvertMetadata(connCtx.Metadata()) + if h.IsSpecialFqdn(metadata.Destination.Fqdn) { + go func() { // ParseSpecialFqdn will block, so open a new goroutine + _ = h.ParseSpecialFqdn( + sing.WithAdditions(context.Background(), newAdditions...), + conn, + metadata, + ) + }() + return nil + } + tcpIn <- connCtx return nil } handleUdpFn := func(addr socks5.Addr, packet C.UDPPacket, _additions ...inbound.Addition) error { From 3a9fc39cd9caded275d64a0b0d3421e35bc4eb17 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 21 Aug 2023 16:18:56 +0800 Subject: [PATCH 08/66] chore: update quic-go to 0.38.0 --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 6fe21a39..d51b9b22 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.7.2 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 - github.com/metacubex/quic-go v0.37.4-0.20230809092428-5acf8eb2de86 + github.com/metacubex/quic-go v0.38.1-0.20230821081539-517fdb17fb28 github.com/metacubex/sing-shadowsocks v0.2.4 github.com/metacubex/sing-shadowsocks2 v0.1.3 github.com/metacubex/sing-tun v0.1.11 @@ -83,7 +83,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/qtls-go1-20 v0.3.1 // indirect + github.com/quic-go/qtls-go1-20 v0.3.2 // indirect github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect diff --git a/go.sum b/go.sum index 07e0bacd..0256657b 100644 --- a/go.sum +++ b/go.sum @@ -94,8 +94,8 @@ github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvO github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88= github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475 h1:qSEOvPPaMrWggFyFhFYGyMR8i1HKyhXjdi1QYUAa2ww= github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475/go.mod h1:wehEpqiogdeyncfhckJP5gD2LtBgJW0wnDC24mJ+8Jg= -github.com/metacubex/quic-go v0.37.4-0.20230809092428-5acf8eb2de86 h1:qGExcB3lYk51LPEJh5HTdQplbZmuTn+tkcuhuas1LC8= -github.com/metacubex/quic-go v0.37.4-0.20230809092428-5acf8eb2de86/go.mod h1:HhHoyskMk4kzfLPKcm7EF7pGXF89KRVwjbGrEaN6lIU= +github.com/metacubex/quic-go v0.38.1-0.20230821081539-517fdb17fb28 h1:ggSo4B1LDH9ZIROoUibxlrUpi7YCMri7HMXn4aNQkiM= +github.com/metacubex/quic-go v0.38.1-0.20230821081539-517fdb17fb28/go.mod h1:SthFvvoqgrEUgIxQXRnqdUAAYQECBavkhl7iA0geVd8= github.com/metacubex/sing v0.0.0-20230817143035-28d23f152579 h1:dE1dBB6CTzNdSMFTE5OCHvzHLewiqiA1nhD+7egtvAc= github.com/metacubex/sing v0.0.0-20230817143035-28d23f152579/go.mod h1:9uOZwWkhT2Z2WldolLxX34s+1svAX4i4vvz5hy8u1MA= github.com/metacubex/sing-shadowsocks v0.2.4 h1:Gc99Z17JVif1PKKq1pjqhSmc2kvHUgk+AqxOstCzhQ0= @@ -134,8 +134,8 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:Om github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/qtls-go1-20 v0.3.1 h1:O4BLOM3hwfVF3AcktIylQXyl7Yi2iBNVy5QsV+ySxbg= -github.com/quic-go/qtls-go1-20 v0.3.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/quic-go/qtls-go1-20 v0.3.2 h1:rRgN3WfnKbyik4dBV8A6girlJVxGand/d+jVKbQq5GI= +github.com/quic-go/qtls-go1-20 v0.3.2/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 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= From 0a7b7894bd450bf9c262fb1ea891efd1a71f5979 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 24 Aug 2023 23:33:03 +0800 Subject: [PATCH 09/66] feat: proxies support `direct` type --- adapter/outbound/direct.go | 20 ++++++++++++++++++++ adapter/parser.go | 7 +++++++ 2 files changed, 27 insertions(+) diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index 94b59cd0..af183b52 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -12,6 +12,11 @@ type Direct struct { *Base } +type DirectOption struct { + BasicOption + Name string `proxy:"name"` +} + // DialContext implements C.ProxyAdapter func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { opts = append(opts, dialer.WithResolver(resolver.DefaultResolver)) @@ -40,6 +45,21 @@ func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, return newPacketConn(pc, d), nil } +func NewDirectWithOption(option DirectOption) *Direct { + return &Direct{ + Base: &Base{ + name: option.Name, + tp: C.Direct, + udp: true, + tfo: option.TFO, + mpTcp: option.MPTCP, + iface: option.Interface, + rmark: option.RoutingMark, + prefer: C.NewDNSPrefer(option.IPVersion), + }, + } +} + func NewDirect() *Direct { return &Direct{ Base: &Base{ diff --git a/adapter/parser.go b/adapter/parser.go index a561a1ed..78e287f9 100644 --- a/adapter/parser.go +++ b/adapter/parser.go @@ -106,6 +106,13 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { break } proxy, err = outbound.NewTuic(*tuicOption) + case "direct": + directOption := &outbound.DirectOption{} + err = decoder.Decode(mapping, directOption) + if err != nil { + break + } + proxy = outbound.NewDirectWithOption(*directOption) default: return nil, fmt.Errorf("unsupport proxy type: %s", proxyType) } From 630a17cf90f111d06dcada8fe9f7615a5488758b Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sat, 26 Aug 2023 21:19:53 +0800 Subject: [PATCH 10/66] chore: cleanup codes --- common/cache/lrucache.go | 11 ++++------- common/picker/picker_test.go | 10 +++------- common/queue/queue.go | 11 ++++------- component/resource/fetcher.go | 25 +++++++++++-------------- 4 files changed, 22 insertions(+), 35 deletions(-) diff --git a/common/cache/lrucache.go b/common/cache/lrucache.go index 1373b0be..2f9d3e79 100644 --- a/common/cache/lrucache.go +++ b/common/cache/lrucache.go @@ -7,6 +7,8 @@ import ( "time" "github.com/Dreamacro/clash/common/generics/list" + + "github.com/samber/lo" ) // Option is part of Functional Options Pattern @@ -87,7 +89,7 @@ func (c *LruCache[K, V]) Get(key K) (V, bool) { el := c.get(key) if el == nil { - return getZero[V](), false + return lo.Empty[V](), false } value := el.value @@ -119,7 +121,7 @@ func (c *LruCache[K, V]) GetWithExpire(key K) (V, time.Time, bool) { el := c.get(key) if el == nil { - return getZero[V](), time.Time{}, false + return lo.Empty[V](), time.Time{}, false } return el.value, time.Unix(el.expires, 0), true @@ -259,8 +261,3 @@ type entry[K comparable, V any] struct { value V expires int64 } - -func getZero[T any]() T { - var result T - return result -} diff --git a/common/picker/picker_test.go b/common/picker/picker_test.go index 17b823cb..4c1c9ebe 100644 --- a/common/picker/picker_test.go +++ b/common/picker/picker_test.go @@ -5,6 +5,7 @@ import ( "testing" "time" + "github.com/samber/lo" "github.com/stretchr/testify/assert" ) @@ -15,7 +16,7 @@ func sleepAndSend[T any](ctx context.Context, delay int, input T) func() (T, err case <-timer.C: return input, nil case <-ctx.Done(): - return getZero[T](), ctx.Err() + return lo.Empty[T](), ctx.Err() } } } @@ -35,11 +36,6 @@ func TestPicker_Timeout(t *testing.T) { picker.Go(sleepAndSend(ctx, 20, 1)) number := picker.Wait() - assert.Equal(t, number, getZero[int]()) + assert.Equal(t, number, lo.Empty[int]()) assert.NotNil(t, picker.Error()) } - -func getZero[T any]() T { - var result T - return result -} diff --git a/common/queue/queue.go b/common/queue/queue.go index 4755cb35..cb58e2f5 100644 --- a/common/queue/queue.go +++ b/common/queue/queue.go @@ -2,6 +2,8 @@ package queue import ( "sync" + + "github.com/samber/lo" ) // Queue is a simple concurrent safe queue @@ -24,7 +26,7 @@ func (q *Queue[T]) Put(items ...T) { // Pop returns the head of items. func (q *Queue[T]) Pop() T { if len(q.items) == 0 { - return GetZero[T]() + return lo.Empty[T]() } q.lock.Lock() @@ -37,7 +39,7 @@ func (q *Queue[T]) Pop() T { // Last returns the last of item. func (q *Queue[T]) Last() T { if len(q.items) == 0 { - return GetZero[T]() + return lo.Empty[T]() } q.lock.RLock() @@ -69,8 +71,3 @@ func New[T any](hint int64) *Queue[T] { items: make([]T, 0, hint), } } - -func GetZero[T any]() T { - var result T - return result -} diff --git a/component/resource/fetcher.go b/component/resource/fetcher.go index 4b905c7f..c92687b1 100644 --- a/component/resource/fetcher.go +++ b/component/resource/fetcher.go @@ -9,6 +9,8 @@ import ( types "github.com/Dreamacro/clash/constant/provider" "github.com/Dreamacro/clash/log" + + "github.com/samber/lo" ) var ( @@ -65,7 +67,7 @@ func (f *Fetcher[V]) Initial() (V, error) { } if err != nil { - return getZero[V](), err + return lo.Empty[V](), err } var contents V @@ -85,18 +87,18 @@ func (f *Fetcher[V]) Initial() (V, error) { if err != nil { if !isLocal { - return getZero[V](), err + return lo.Empty[V](), err } // parse local file error, fallback to remote buf, err = f.vehicle.Read() if err != nil { - return getZero[V](), err + return lo.Empty[V](), err } contents, err = f.parser(buf) if err != nil { - return getZero[V](), err + return lo.Empty[V](), err } isLocal = false @@ -104,7 +106,7 @@ func (f *Fetcher[V]) Initial() (V, error) { if f.vehicle.Type() != types.File && !isLocal { if err := safeWrite(f.vehicle.Path(), buf); err != nil { - return getZero[V](), err + return lo.Empty[V](), err } } @@ -121,7 +123,7 @@ func (f *Fetcher[V]) Initial() (V, error) { func (f *Fetcher[V]) Update() (V, bool, error) { buf, err := f.vehicle.Read() if err != nil { - return getZero[V](), false, err + return lo.Empty[V](), false, err } now := time.Now() @@ -129,17 +131,17 @@ func (f *Fetcher[V]) Update() (V, bool, error) { if bytes.Equal(f.hash[:], hash[:]) { f.UpdatedAt = &now _ = os.Chtimes(f.vehicle.Path(), now, now) - return getZero[V](), true, nil + return lo.Empty[V](), true, nil } contents, err := f.parser(buf) if err != nil { - return getZero[V](), false, err + return lo.Empty[V](), false, err } if f.vehicle.Type() != types.File { if err := safeWrite(f.vehicle.Path(), buf); err != nil { - return getZero[V](), false, err + return lo.Empty[V](), false, err } } @@ -210,8 +212,3 @@ func NewFetcher[V any](name string, interval time.Duration, vehicle types.Vehicl interval: interval, } } - -func getZero[V any]() V { - var result V - return result -} From 9536372cfbfd9439ff4870462626f21b302257ed Mon Sep 17 00:00:00 2001 From: Alpha <61853980+AlphaGHX@users.noreply.github.com> Date: Wed, 30 Aug 2023 15:49:28 +0800 Subject: [PATCH 11/66] fix: call shutdown before restart (#709) --- hub/route/restart.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hub/route/restart.go b/hub/route/restart.go index 6c3f27f3..cae21844 100644 --- a/hub/route/restart.go +++ b/hub/route/restart.go @@ -8,7 +8,7 @@ import ( "runtime" "syscall" - "github.com/Dreamacro/clash/listener" + "github.com/Dreamacro/clash/hub/executor" "github.com/Dreamacro/clash/log" "github.com/go-chi/chi/v5" @@ -44,7 +44,7 @@ func restart(w http.ResponseWriter, r *http.Request) { func runRestart(execPath string) { var err error - listener.Cleanup(false) + executor.Shutdown() if runtime.GOOS == "windows" { cmd := exec.Command(execPath, os.Args[1:]...) log.Infoln("restarting: %q %q", execPath, os.Args[1:]) From d09937520089061d002fded006a35494bd7a41f4 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Wed, 30 Aug 2023 15:52:41 +0800 Subject: [PATCH 12/66] chore: rename func name --- hub/route/restart.go | 4 ++-- hub/route/upgrade.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hub/route/restart.go b/hub/route/restart.go index cae21844..a907021f 100644 --- a/hub/route/restart.go +++ b/hub/route/restart.go @@ -39,10 +39,10 @@ func restart(w http.ResponseWriter, r *http.Request) { // The background context is used because the underlying functions wrap it // with timeout and shut down the server, which handles current request. It // also should be done in a separate goroutine for the same reason. - go runRestart(execPath) + go restartExecutable(execPath) } -func runRestart(execPath string) { +func restartExecutable(execPath string) { var err error executor.Shutdown() if runtime.GOOS == "windows" { diff --git a/hub/route/upgrade.go b/hub/route/upgrade.go index a58f224c..5e75bc8b 100644 --- a/hub/route/upgrade.go +++ b/hub/route/upgrade.go @@ -41,5 +41,5 @@ func upgrade(w http.ResponseWriter, r *http.Request) { f.Flush() } - go runRestart(execPath) + go restartExecutable(execPath) } From 86cf1dd54bace6dae826444698e612184444a22f Mon Sep 17 00:00:00 2001 From: Mitt <10653144+MittWillson@users.noreply.github.com> Date: Tue, 29 Aug 2023 12:23:16 +0000 Subject: [PATCH 13/66] fix: dualStack confusing error on ipv4 failed connect --- component/dialer/dialer.go | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 89c7564a..2bc5b66c 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -162,8 +162,11 @@ func concurrentDualStackDialContext(ctx context.Context, network string, ips []n func dualStackDialContext(ctx context.Context, dialFn dialFunc, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) { ipv4s, ipv6s := resolver.SortationAddr(ips) - preferIPVersion := opt.prefer + if len(ipv4s) == 0 && len(ipv6s) == 0 { + return nil, ErrorNoIpAddress + } + preferIPVersion := opt.prefer fallbackTicker := time.NewTicker(fallbackTimeout) defer fallbackTicker.Stop() results := make(chan dialResult) @@ -182,11 +185,21 @@ func dualStackDialContext(ctx context.Context, dialFn dialFunc, network string, }() result.Conn, result.error = dialFn(ctx, network, ips, port, opt) } - go racer(ipv4s, preferIPVersion != 6) - go racer(ipv6s, preferIPVersion != 4) + + var wait int + if len(ipv4s) != 0 { + wait++ + go racer(ipv4s, preferIPVersion != 6) + } + + if len(ipv6s) != 0 { + wait++ + go racer(ipv6s, preferIPVersion != 4) + } + var fallback dialResult var errs []error - for i := 0; i < 2; { + for i := 0; i < wait; { select { case <-fallbackTicker.C: if fallback.error == nil && fallback.Conn != nil { From 414d8f21626b464b9e223c39ca2090203af3e31c Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Wed, 30 Aug 2023 17:07:49 +0800 Subject: [PATCH 14/66] chore: use WaitGroup in dualStackDialContext --- component/dialer/dialer.go | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 2bc5b66c..0cfa1b6c 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -169,10 +169,15 @@ func dualStackDialContext(ctx context.Context, dialFn dialFunc, network string, preferIPVersion := opt.prefer fallbackTicker := time.NewTicker(fallbackTimeout) defer fallbackTicker.Stop() + results := make(chan dialResult) returned := make(chan struct{}) defer close(returned) + + var wg sync.WaitGroup + racer := func(ips []netip.Addr, isPrimary bool) { + defer wg.Done() result := dialResult{isPrimary: isPrimary} defer func() { select { @@ -186,27 +191,35 @@ func dualStackDialContext(ctx context.Context, dialFn dialFunc, network string, result.Conn, result.error = dialFn(ctx, network, ips, port, opt) } - var wait int if len(ipv4s) != 0 { - wait++ + wg.Add(1) go racer(ipv4s, preferIPVersion != 6) } if len(ipv6s) != 0 { - wait++ + wg.Add(1) go racer(ipv6s, preferIPVersion != 4) } + go func() { + wg.Wait() + close(results) + }() + var fallback dialResult var errs []error - for i := 0; i < wait; { + +loop: + for { select { case <-fallbackTicker.C: if fallback.error == nil && fallback.Conn != nil { return fallback.Conn, nil } - case res := <-results: - i++ + case res, ok := <-results: + if !ok { + break loop + } if res.error == nil { if res.isPrimary { return res.Conn, nil @@ -221,6 +234,7 @@ func dualStackDialContext(ctx context.Context, dialFn dialFunc, network string, } } } + if fallback.error == nil && fallback.Conn != nil { return fallback.Conn, nil } From 54fee7bd3a57ddd589a96f80b893b8524a9adf4f Mon Sep 17 00:00:00 2001 From: YanceyChiew <35898533+YanceyChiew@users.noreply.github.com> Date: Tue, 15 Aug 2023 09:10:41 +0800 Subject: [PATCH 15/66] Improve: nicer tun info for RESTful api Let the restful api still get TunConf even when tun is off. Otherwise the api will return the default values, instead of the values that actually take effect after enable. * Due to this problem, yacd changes the displayed value back to gvisor immediately after the user selects tun stack. --- hub/executor/executor.go | 2 +- listener/listener.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hub/executor/executor.go b/hub/executor/executor.go index f4cda47a..6d542f60 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -129,7 +129,7 @@ func GetGeneral() *config.General { RedirPort: ports.RedirPort, TProxyPort: ports.TProxyPort, MixedPort: ports.MixedPort, - Tun: listener.GetTunConf(), + Tun: listener.LastTunConf, TuicServer: listener.GetTuicConf(), ShadowSocksConfig: ports.ShadowSocksConfig, VmessConfig: ports.VmessConfig, diff --git a/listener/listener.go b/listener/listener.go index 8f0088db..7b513fa7 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -516,6 +516,7 @@ func ReCreateTun(tunConf LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.Pack defer func() { if err != nil { log.Errorln("Start TUN listening error: %s", err.Error()) + tunConf.Enable = false Cleanup(false) } }() From 9ceaf20584830db6f0548f2e2e08bcbf988d5c58 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Thu, 31 Aug 2023 19:56:20 +0800 Subject: [PATCH 16/66] fix: concurrent map writes #707 --- adapter/adapter.go | 102 ++++++++++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 44 deletions(-) diff --git a/adapter/adapter.go b/adapter/adapter.go index 6cc79c3a..c7351061 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -10,6 +10,7 @@ import ( "net/netip" "net/url" "strconv" + "sync" "time" "github.com/Dreamacro/clash/common/atomic" @@ -36,7 +37,7 @@ type Proxy struct { history *queue.Queue[C.DelayHistory] alive *atomic.Bool url string - extra map[string]*extraProxyState + extra sync.Map } // Alive implements C.Proxy @@ -46,10 +47,8 @@ func (p *Proxy) Alive() bool { // AliveForTestUrl implements C.Proxy func (p *Proxy) AliveForTestUrl(url string) bool { - if p.extra != nil { - if state, ok := p.extra[url]; ok { - return state.alive.Load() - } + if state, ok := p.extra.Load(url); ok { + return state.(*extraProxyState).alive.Load() } return p.alive.Load() @@ -88,16 +87,16 @@ func (p *Proxy) DelayHistory() []C.DelayHistory { for _, item := range queueM { histories = append(histories, item) } + return histories } // DelayHistoryForTestUrl implements C.Proxy func (p *Proxy) DelayHistoryForTestUrl(url string) []C.DelayHistory { var queueM []C.DelayHistory - if p.extra != nil { - if state, ok := p.extra[url]; ok { - queueM = state.history.Copy() - } + + if state, ok := p.extra.Load(url); ok { + queueM = state.(*extraProxyState).history.Copy() } if queueM == nil { @@ -112,19 +111,25 @@ func (p *Proxy) DelayHistoryForTestUrl(url string) []C.DelayHistory { } func (p *Proxy) ExtraDelayHistory() map[string][]C.DelayHistory { - extra := map[string][]C.DelayHistory{} - if p.extra != nil && len(p.extra) != 0 { - for testUrl, option := range p.extra { - histories := []C.DelayHistory{} - queueM := option.history.Copy() - for _, item := range queueM { - histories = append(histories, item) - } + extraHistory := map[string][]C.DelayHistory{} - extra[testUrl] = histories + p.extra.Range(func(k, v interface{}) bool { + + testUrl := k.(string) + state := v.(*extraProxyState) + + histories := []C.DelayHistory{} + queueM := state.history.Copy() + + for _, item := range queueM { + histories = append(histories, item) } - } - return extra + + extraHistory[testUrl] = histories + + return true + }) + return extraHistory } // LastDelay return last history record. if proxy is not alive, return the max value of uint16. @@ -149,11 +154,9 @@ func (p *Proxy) LastDelayForTestUrl(url string) (delay uint16) { alive := p.alive.Load() history := p.history.Last() - if p.extra != nil { - if state, ok := p.extra[url]; ok { - alive = state.alive.Load() - history = state.history.Last() - } + if state, ok := p.extra.Load(url); ok { + alive = state.(*extraProxyState).alive.Load() + history = state.(*extraProxyState).history.Last() } if !alive { @@ -214,23 +217,19 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In record.Delay = t } - if p.extra == nil { - p.extra = map[string]*extraProxyState{} - } - - state, ok := p.extra[url] + state, ok := p.extra.Load(url) if !ok { state = &extraProxyState{ history: queue.New[C.DelayHistory](defaultHistoriesNum), alive: atomic.NewBool(true), } - p.extra[url] = state + p.extra.Store(url, state) } - state.alive.Store(alive) - state.history.Put(record) - if state.history.Len() > defaultHistoriesNum { - state.history.Pop() + state.(*extraProxyState).alive.Store(alive) + state.(*extraProxyState).history.Put(record) + if state.(*extraProxyState).history.Len() > defaultHistoriesNum { + state.(*extraProxyState).history.Pop() } default: log.Debugln("health check result will be discarded, url: %s alive: %t, delay: %d", url, alive, t) @@ -307,7 +306,12 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In } func NewProxy(adapter C.ProxyAdapter) *Proxy { - return &Proxy{adapter, queue.New[C.DelayHistory](defaultHistoriesNum), atomic.NewBool(true), "", map[string]*extraProxyState{}} + return &Proxy{ + ProxyAdapter: adapter, + history: queue.New[C.DelayHistory](defaultHistoriesNum), + alive: atomic.NewBool(true), + url: "", + extra: sync.Map{}} } func urlToMetadata(rawURL string) (addr C.Metadata, err error) { @@ -350,14 +354,24 @@ func (p *Proxy) determineFinalStoreType(store C.DelayHistoryStoreType, url strin return C.OriginalHistory } - if p.extra == nil { - store = C.ExtraHistory - } else { - if _, ok := p.extra[url]; ok { - store = C.ExtraHistory - } else if len(p.extra) < 2*C.DefaultMaxHealthCheckUrlNum { - store = C.ExtraHistory - } + length := 0 + p.extra.Range(func(_, _ interface{}) bool { + length++ + return length < 2*C.DefaultMaxHealthCheckUrlNum + }) + + if length == 0 { + return C.ExtraHistory } + + _, ok := p.extra.Load(url) + if ok { + return C.ExtraHistory + } + + if length < 2*C.DefaultMaxHealthCheckUrlNum { + return C.ExtraHistory + } + return store } From cbdf33c42c1795c85a52b886cee163438a3d8f1e Mon Sep 17 00:00:00 2001 From: "riolu.rs" Date: Fri, 1 Sep 2023 03:11:35 +0800 Subject: [PATCH 17/66] feat: ntp service --- adapter/outbound/vmess.go | 2 + component/tls/reality.go | 3 +- config/config.go | 43 +++++++++++++++ go.mod | 1 + go.sum | 29 ++++++++++ hub/executor/executor.go | 12 +++++ listener/sing_shadowsocks/server.go | 4 +- listener/sing_vmess/server.go | 3 +- ntp/service.go | 84 +++++++++++++++++++++++++++++ 9 files changed, 177 insertions(+), 4 deletions(-) create mode 100644 ntp/service.go diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 7495d8a3..0fa0b1d9 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -18,6 +18,7 @@ import ( "github.com/Dreamacro/clash/component/resolver" tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/ntp" "github.com/Dreamacro/clash/transport/gun" clashVMess "github.com/Dreamacro/clash/transport/vmess" @@ -416,6 +417,7 @@ func NewVmess(option VmessOption) (*Vmess, error) { if option.AuthenticatedLength { options = append(options, vmess.ClientWithAuthenticatedLength()) } + options = append(options, vmess.ClientWithTimeFunc(ntp.Now)) client, err := vmess.NewClient(option.UUID, security, option.AlterID, options...) if err != nil { return nil, err diff --git a/component/tls/reality.go b/component/tls/reality.go index 265c584e..c995af0a 100644 --- a/component/tls/reality.go +++ b/component/tls/reality.go @@ -22,6 +22,7 @@ import ( "github.com/Dreamacro/clash/common/utils" "github.com/Dreamacro/clash/log" + "github.com/Dreamacro/clash/ntp" utls "github.com/sagernet/utls" "github.com/zhangyunhao116/fastrand" @@ -70,7 +71,7 @@ func GetRealityConn(ctx context.Context, conn net.Conn, ClientFingerprint string rawSessionID[i] = 0 } - binary.BigEndian.PutUint64(hello.SessionId, uint64(time.Now().Unix())) + binary.BigEndian.PutUint64(hello.SessionId, uint64(ntp.Now().Unix())) copy(hello.SessionId[8:], realityConfig.ShortID[:]) hello.SessionId[0] = 1 diff --git a/config/config.go b/config/config.go index cb30999b..3db51776 100644 --- a/config/config.go +++ b/config/config.go @@ -87,6 +87,14 @@ type Controller struct { Secret string `json:"-"` } +// NTP config +type NTP struct { + Enable bool `yaml:"enable"` + Server string `yaml:"server"` + Port int `yaml:"port"` + Interval int `yaml:"interval"` +} + // DNS config type DNS struct { Enable bool `yaml:"enable"` @@ -151,6 +159,7 @@ type Experimental struct { type Config struct { General *General IPTables *IPTables + NTP *NTP DNS *DNS Experimental *Experimental Hosts *trie.DomainTrie[resolver.HostValue] @@ -167,6 +176,13 @@ type Config struct { TLS *TLS } +type RawNTP struct { + Enable bool `yaml:"enable"` + Server string `yaml:"server"` + ServerPort int `yaml:"server-port"` + Interval int `yaml:"interval"` +} + type RawDNS struct { Enable bool `yaml:"enable"` PreferH3 bool `yaml:"prefer-h3"` @@ -269,6 +285,7 @@ type RawConfig struct { ProxyProvider map[string]map[string]any `yaml:"proxy-providers"` RuleProvider map[string]map[string]any `yaml:"rule-providers"` Hosts map[string]any `yaml:"hosts"` + NTP RawNTP `yaml:"ntp"` DNS RawDNS `yaml:"dns"` Tun RawTun `yaml:"tun"` TuicServer RawTuicServer `yaml:"tuic-server"` @@ -493,6 +510,9 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } config.Hosts = hosts + ntpCfg := paresNTP(rawCfg) + config.NTP = ntpCfg + dnsCfg, err := parseDNS(rawCfg, hosts, rules, ruleProviders) if err != nil { return nil, err @@ -1132,6 +1152,29 @@ func parseFallbackGeoSite(countries []string, rules []C.Rule) ([]*router.DomainM return sites, nil } +func paresNTP(rawCfg *RawConfig) *NTP { + var server = "time.apple.com" + var port = 123 + var interval = 30 + cfg := rawCfg.NTP + if len(cfg.Server) != 0 { + server = cfg.Server + } + if cfg.ServerPort != 0 { + port = cfg.ServerPort + } + if cfg.Interval != 0 { + interval = cfg.Interval + } + ntpCfg := &NTP{ + Enable: cfg.Enable, + Server: server, + Port: port, + Interval: interval, + } + return ntpCfg +} + func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider) (*DNS, error) { cfg := rawCfg.DNS if cfg.Enable && len(cfg.NameServer) == 0 { diff --git a/go.mod b/go.mod index d51b9b22..00fd802b 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.20 require ( github.com/3andne/restls-client-go v0.1.6 github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da + github.com/beevik/ntp v1.3.0 github.com/cilium/ebpf v0.11.0 github.com/coreos/go-iptables v0.7.0 github.com/dlclark/regexp2 v1.10.0 diff --git a/go.sum b/go.sum index 0256657b..84b13831 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,8 @@ 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/beevik/ntp v1.3.0 h1:/w5VhpW5BGKS37vFm1p9oVk/t4HnnkKZAZIubHM6F7Q= +github.com/beevik/ntp v1.3.0/go.mod h1:vD6h1um4kzXpqmLTuu0cCLcC+NfvC0IC+ltmEDA8E78= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= 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= @@ -193,6 +195,7 @@ github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1 github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zhangyunhao116/fastrand v0.3.0 h1:7bwe124xcckPulX6fxtr2lFdO2KQqaefdtbk+mqO/Ig= @@ -205,6 +208,8 @@ go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= 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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb h1:mIKbk8weKhSeLH2GmUTrvx8CjkyJmnU1wFmg59CUjFA= @@ -212,15 +217,24 @@ golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb/go.mod h1:FXUEEKJgO7OQYeo8N0 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -234,17 +248,30 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= 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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= @@ -253,6 +280,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm 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.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 6d542f60..d5ca4afb 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -2,11 +2,15 @@ package executor import ( "fmt" + "github.com/Dreamacro/clash/ntp" + "net" "net/netip" "os" "runtime" + "strconv" "strings" "sync" + "time" "github.com/Dreamacro/clash/adapter" "github.com/Dreamacro/clash/adapter/inbound" @@ -92,6 +96,7 @@ func ApplyConfig(cfg *config.Config, force bool) { updateSniffer(cfg.Sniffer) updateHosts(cfg.Hosts) updateGeneral(cfg.General) + updateNTP(cfg.NTP) updateDNS(cfg.DNS, cfg.RuleProviders, cfg.General.IPv6) updateListeners(cfg.General, cfg.Listeners, force) updateIPTables(cfg) @@ -178,6 +183,13 @@ func updateListeners(general *config.General, listeners map[string]C.InboundList func updateExperimental(c *config.Config) { } +func updateNTP(c *config.NTP) { + if c.Enable { + ntp.ReCreateNTPService(net.JoinHostPort(c.Server, strconv.Itoa(c.Port)), + time.Duration(c.Interval)) + } +} + func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, generalIPv6 bool) { if !c.Enable { resolver.DefaultResolver = nil diff --git a/listener/sing_shadowsocks/server.go b/listener/sing_shadowsocks/server.go index 1fa9a3ba..13ddde0d 100644 --- a/listener/sing_shadowsocks/server.go +++ b/listener/sing_shadowsocks/server.go @@ -5,7 +5,6 @@ import ( "fmt" "net" "strings" - "time" "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/common/sockopt" @@ -14,6 +13,7 @@ import ( embedSS "github.com/Dreamacro/clash/listener/shadowsocks" "github.com/Dreamacro/clash/listener/sing" "github.com/Dreamacro/clash/log" + "github.com/Dreamacro/clash/ntp" shadowsocks "github.com/metacubex/sing-shadowsocks" "github.com/metacubex/sing-shadowsocks/shadowaead" @@ -64,7 +64,7 @@ func New(config LC.ShadowsocksServer, tcpIn chan<- C.ConnContext, udpIn chan<- C case common.Contains(shadowaead.List, config.Cipher): sl.service, err = shadowaead.NewService(config.Cipher, nil, config.Password, udpTimeout, h) case common.Contains(shadowaead_2022.List, config.Cipher): - sl.service, err = shadowaead_2022.NewServiceWithPassword(config.Cipher, config.Password, udpTimeout, h, time.Now) + sl.service, err = shadowaead_2022.NewServiceWithPassword(config.Cipher, config.Password, udpTimeout, h, ntp.Now) default: err = fmt.Errorf("shadowsocks: unsupported method: %s", config.Cipher) return embedSS.New(config, tcpIn, udpIn) diff --git a/listener/sing_vmess/server.go b/listener/sing_vmess/server.go index f6a279c1..374a378b 100644 --- a/listener/sing_vmess/server.go +++ b/listener/sing_vmess/server.go @@ -10,6 +10,7 @@ import ( C "github.com/Dreamacro/clash/constant" LC "github.com/Dreamacro/clash/listener/config" "github.com/Dreamacro/clash/listener/sing" + "github.com/Dreamacro/clash/ntp" vmess "github.com/metacubex/sing-vmess" "github.com/sagernet/sing/common" @@ -42,7 +43,7 @@ func New(config LC.VmessServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.Packe Additions: additions, } - service := vmess.NewService[string](h, vmess.ServiceWithDisableHeaderProtection()) + service := vmess.NewService[string](h, vmess.ServiceWithDisableHeaderProtection(), vmess.ServiceWithTimeFunc(ntp.Now)) err = service.UpdateUsers( common.Map(config.Users, func(it LC.VmessUser) string { return it.Username diff --git a/ntp/service.go b/ntp/service.go new file mode 100644 index 00000000..e8864eb6 --- /dev/null +++ b/ntp/service.go @@ -0,0 +1,84 @@ +package ntp + +import ( + "context" + "github.com/Dreamacro/clash/log" + "github.com/beevik/ntp" + "sync" + "time" +) + +var offset time.Duration +var service *Service + +type Service struct { + addr string + interval time.Duration + ticker *time.Ticker + ctx context.Context + cancel context.CancelFunc + mu *sync.Mutex + running bool +} + +func ReCreateNTPService(addr string, interval time.Duration) { + if service != nil { + service.Stop() + } + ctx, cancel := context.WithCancel(context.Background()) + service = &Service{addr: addr, interval: interval, ctx: ctx, cancel: cancel, mu: &sync.Mutex{}} + service.Start() +} + +func (srv *Service) Start() { + srv.mu.Lock() + defer srv.mu.Unlock() + log.Infoln("NTP service start") + srv.ticker = time.NewTicker(srv.interval * time.Minute) + service.running = true + go func() { + for { + err := srv.updateTime(srv.addr) + if err != nil { + log.Warnln("updateTime failed:", err) + } + select { + case <-srv.ticker.C: + case <-srv.ctx.Done(): + return + } + } + }() +} + +func (srv *Service) Stop() { + srv.mu.Lock() + defer srv.mu.Unlock() + srv.ticker.Stop() + srv.cancel() + service.running = false +} + +func (srv *Service) updateTime(addr string) error { + response, err := ntp.Query(addr) + if err != nil { + return err + } + localTime := time.Now() + ntpTime := response.Time + offset = localTime.Sub(ntpTime) + if offset > time.Duration(0) { + log.Warnln("System clock is ahead of NTP time by", offset) + } else if offset < time.Duration(0) { + log.Warnln("System clock is behind NTP time by", -offset) + } + return nil +} + +func Now() time.Time { + now := time.Now() + if service.running && offset.Abs() > 0 { + now = now.Add(offset) + } + return now +} From a366e9a4b5397b7da7040b26dc15b3e0f4457600 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 2 Sep 2023 12:37:43 +0800 Subject: [PATCH 18/66] fix: ntp service panic --- common/cmd/cmd_test.go | 2 +- ntp/service.go | 29 ++++++++++++++++++++--------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/common/cmd/cmd_test.go b/common/cmd/cmd_test.go index 4bba6def..b124a22d 100644 --- a/common/cmd/cmd_test.go +++ b/common/cmd/cmd_test.go @@ -21,7 +21,7 @@ func TestSplitArgs(t *testing.T) { func TestExecCmd(t *testing.T) { if runtime.GOOS == "windows" { - _, err := ExecCmd("dir") + _, err := ExecCmd("cmd -c 'dir'") assert.Nil(t, err) return } diff --git a/ntp/service.go b/ntp/service.go index e8864eb6..af06a571 100644 --- a/ntp/service.go +++ b/ntp/service.go @@ -17,7 +17,7 @@ type Service struct { ticker *time.Ticker ctx context.Context cancel context.CancelFunc - mu *sync.Mutex + mu sync.Mutex running bool } @@ -26,7 +26,7 @@ func ReCreateNTPService(addr string, interval time.Duration) { service.Stop() } ctx, cancel := context.WithCancel(context.Background()) - service = &Service{addr: addr, interval: interval, ctx: ctx, cancel: cancel, mu: &sync.Mutex{}} + service = &Service{addr: addr, interval: interval, ctx: ctx, cancel: cancel} service.Start() } @@ -40,7 +40,7 @@ func (srv *Service) Start() { for { err := srv.updateTime(srv.addr) if err != nil { - log.Warnln("updateTime failed:", err) + log.Warnln("updateTime failed: %s", err) } select { case <-srv.ticker.C: @@ -54,9 +54,20 @@ func (srv *Service) Start() { func (srv *Service) Stop() { srv.mu.Lock() defer srv.mu.Unlock() - srv.ticker.Stop() - srv.cancel() - service.running = false + if service.running { + srv.ticker.Stop() + srv.cancel() + service.running = false + } +} + +func (srv *Service) Running() bool { + if srv == nil { + return false + } + srv.mu.Lock() + defer srv.mu.Unlock() + return srv.running } func (srv *Service) updateTime(addr string) error { @@ -68,16 +79,16 @@ func (srv *Service) updateTime(addr string) error { ntpTime := response.Time offset = localTime.Sub(ntpTime) if offset > time.Duration(0) { - log.Warnln("System clock is ahead of NTP time by", offset) + log.Warnln("System clock is ahead of NTP time by %s", offset) } else if offset < time.Duration(0) { - log.Warnln("System clock is behind NTP time by", -offset) + log.Warnln("System clock is behind NTP time by %s", -offset) } return nil } func Now() time.Time { now := time.Now() - if service.running && offset.Abs() > 0 { + if service.Running() && offset.Abs() > 0 { now = now.Add(offset) } return now From 9feb4d666809eb64dfdb17376bdd918146c373fc Mon Sep 17 00:00:00 2001 From: YanceyChiew <35898533+YanceyChiew@users.noreply.github.com> Date: Sat, 2 Sep 2023 01:14:26 +0800 Subject: [PATCH 19/66] fix: RESTful api missing TunConf.device In commit 54fee7b, due to failure to take into account that not all required parameters of `sing_tun.server.New` have default values provided by `LC.Tun`, the name of the tun device cannot be obtained when `TunConf.device` is not explicitly configured. This commit fixed the issue. --- hub/executor/executor.go | 2 +- listener/listener.go | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/hub/executor/executor.go b/hub/executor/executor.go index d5ca4afb..8cb8358f 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -134,7 +134,7 @@ func GetGeneral() *config.General { RedirPort: ports.RedirPort, TProxyPort: ports.TProxyPort, MixedPort: ports.MixedPort, - Tun: listener.LastTunConf, + Tun: listener.GetTunConf(), TuicServer: listener.GetTuicConf(), ShadowSocksConfig: ports.ShadowSocksConfig, VmessConfig: ports.VmessConfig, diff --git a/listener/listener.go b/listener/listener.go index 7b513fa7..a340d3a2 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -84,9 +84,7 @@ type Ports struct { func GetTunConf() LC.Tun { if tunLister == nil { - return LC.Tun{ - Enable: false, - } + return LastTunConf } return tunLister.Config() } From 427a377c2a19aca095f1b39da332d5dc882f5033 Mon Sep 17 00:00:00 2001 From: YanceyChiew <35898533+YanceyChiew@users.noreply.github.com> Date: Sat, 2 Sep 2023 03:27:48 +0800 Subject: [PATCH 20/66] refactor: Decouple .Cleanup from ReCreateTun The listener.Cleanup method will be called during executor.Shutdown and route.restart, so it should serve all kinds of listeners rather than a single tun device. Currently listener.ReCreateTun will call it to handle some internal affairs, This should be decoupled. In this way, the cleanup tasks for data outside the process life cycle that other listeners will add here in the future will not be accidentally triggered by configuring tun. --- listener/listener.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/listener/listener.go b/listener/listener.go index a340d3a2..9b4840e1 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -515,7 +515,6 @@ func ReCreateTun(tunConf LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.Pack if err != nil { log.Errorln("Start TUN listening error: %s", err.Error()) tunConf.Enable = false - Cleanup(false) } }() @@ -526,7 +525,7 @@ func ReCreateTun(tunConf LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.Pack return } - Cleanup(true) + closeTunListener() if !tunConf.Enable { return @@ -896,10 +895,13 @@ func hasTunConfigChange(tunConf *LC.Tun) bool { return false } -func Cleanup(wait bool) { +func closeTunListener() { if tunLister != nil { tunLister.Close() tunLister = nil } - LastTunConf = LC.Tun{} +} + +func Cleanup(wait bool) { + closeTunListener() } From d79c13064eb7bc464da31f72c5c9bcb88ba75c4b Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sat, 2 Sep 2023 14:10:26 +0800 Subject: [PATCH 21/66] chore: cleanup codes --- hub/executor/executor.go | 2 +- listener/listener.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 8cb8358f..b7111c14 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -491,7 +491,7 @@ func updateIPTables(cfg *config.Config) { } func Shutdown() { - listener.Cleanup(false) + listener.Cleanup() tproxy.CleanupTProxyIPTables() resolver.StoreFakePoolState() diff --git a/listener/listener.go b/listener/listener.go index 9b4840e1..b1d59d49 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -902,6 +902,6 @@ func closeTunListener() { } } -func Cleanup(wait bool) { +func Cleanup() { closeTunListener() } From 73fa79bf3f508159311839dc7e295070180c80da Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sat, 2 Sep 2023 16:25:55 +0800 Subject: [PATCH 22/66] feat: configurable TCPKeepAlive interval --- adapter/outbound/direct.go | 4 +++- adapter/outbound/http.go | 4 +++- adapter/outbound/shadowsocks.go | 2 +- adapter/outbound/shadowsocksr.go | 2 +- adapter/outbound/snell.go | 7 ++++--- adapter/outbound/socks5.go | 5 +++-- adapter/outbound/trojan.go | 7 ++++--- adapter/outbound/util.go | 8 -------- adapter/outbound/vless.go | 6 +++--- adapter/outbound/vmess.go | 6 +++--- adapter/outboundgroup/util.go | 12 ------------ common/net/tcpip.go | 10 ++++++++++ config/config.go | 9 +++++++++ docs/config.yaml | 2 ++ listener/autoredir/tcp.go | 3 ++- listener/mixed/mixed.go | 4 ++-- listener/redir/tcp.go | 3 ++- listener/shadowsocks/tcp.go | 2 +- listener/sing_shadowsocks/server.go | 3 ++- listener/sing_vmess/server.go | 3 ++- listener/socks/tcp.go | 2 +- listener/tproxy/tproxy.go | 3 ++- listener/tunnel/tcp.go | 3 ++- 23 files changed, 62 insertions(+), 48 deletions(-) diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index af183b52..720f96f9 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -3,6 +3,8 @@ package outbound import ( "context" "errors" + + N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/resolver" C "github.com/Dreamacro/clash/constant" @@ -24,7 +26,7 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ... if err != nil { return nil, err } - tcpKeepAlive(c) + N.TCPKeepAlive(c) return NewConn(c, d), nil } diff --git a/adapter/outbound/http.go b/adapter/outbound/http.go index 0b652ca9..acc75d37 100644 --- a/adapter/outbound/http.go +++ b/adapter/outbound/http.go @@ -7,11 +7,13 @@ import ( "encoding/base64" "errors" "fmt" + "io" "net" "net/http" "strconv" + N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/proxydialer" tlsC "github.com/Dreamacro/clash/component/tls" @@ -74,7 +76,7 @@ func (h *Http) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metad if err != nil { return nil, fmt.Errorf("%s connect error: %w", h.addr, err) } - tcpKeepAlive(c) + N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 0f46cf79..f744ec53 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -146,7 +146,7 @@ func (ss *ShadowSocks) DialContextWithDialer(ctx context.Context, dialer C.Diale if err != nil { return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) } - tcpKeepAlive(c) + N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index cd6854af..0f03f86d 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -80,7 +80,7 @@ func (ssr *ShadowSocksR) DialContextWithDialer(ctx context.Context, dialer C.Dia if err != nil { return nil, fmt.Errorf("%s connect error: %w", ssr.addr, err) } - tcpKeepAlive(c) + N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) diff --git a/adapter/outbound/snell.go b/adapter/outbound/snell.go index d0b9e748..16405fcf 100644 --- a/adapter/outbound/snell.go +++ b/adapter/outbound/snell.go @@ -6,6 +6,7 @@ import ( "net" "strconv" + N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/structure" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/proxydialer" @@ -93,7 +94,7 @@ func (s *Snell) DialContextWithDialer(ctx context.Context, dialer C.Dialer, meta if err != nil { return nil, fmt.Errorf("%s connect error: %w", s.addr, err) } - tcpKeepAlive(c) + N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) @@ -121,7 +122,7 @@ func (s *Snell) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met if err != nil { return nil, err } - tcpKeepAlive(c) + N.TCPKeepAlive(c) c = streamConn(c, streamOption{s.psk, s.version, s.addr, s.obfsOption}) err = snell.WriteUDPHeader(c, s.version) @@ -207,7 +208,7 @@ func NewSnell(option SnellOption) (*Snell, error) { return nil, err } - tcpKeepAlive(c) + N.TCPKeepAlive(c) return streamConn(c, streamOption{psk, option.Version, addr, obfsOption}), nil }) } diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index f451cd1a..2e9bccd6 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -9,6 +9,7 @@ import ( "net" "strconv" + N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/proxydialer" tlsC "github.com/Dreamacro/clash/component/tls" @@ -80,7 +81,7 @@ func (ss *Socks5) DialContextWithDialer(ctx context.Context, dialer C.Dialer, me if err != nil { return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) } - tcpKeepAlive(c) + N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) @@ -126,7 +127,7 @@ func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, safeConnClose(c, err) }(c) - tcpKeepAlive(c) + N.TCPKeepAlive(c) var user *socks5.User if ss.user != "" { user = &socks5.User{ diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index ec420bf3..6339b476 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -8,6 +8,7 @@ import ( "net/http" "strconv" + N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/proxydialer" tlsC "github.com/Dreamacro/clash/component/tls" @@ -131,7 +132,7 @@ func (t *Trojan) DialContextWithDialer(ctx context.Context, dialer C.Dialer, met if err != nil { return nil, fmt.Errorf("%s connect error: %w", t.addr, err) } - tcpKeepAlive(c) + N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) @@ -184,7 +185,7 @@ func (t *Trojan) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, me defer func(c net.Conn) { safeConnClose(c, err) }(c) - tcpKeepAlive(c) + N.TCPKeepAlive(c) c, err = t.plainStream(ctx, c) if err != nil { return nil, fmt.Errorf("%s connect error: %w", t.addr, err) @@ -268,7 +269,7 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { if err != nil { return nil, fmt.Errorf("%s connect error: %s", t.addr, err.Error()) } - tcpKeepAlive(c) + N.TCPKeepAlive(c) return c, nil } diff --git a/adapter/outbound/util.go b/adapter/outbound/util.go index 7f3ec4c3..36607e4f 100644 --- a/adapter/outbound/util.go +++ b/adapter/outbound/util.go @@ -7,7 +7,6 @@ import ( "net" "net/netip" "sync" - "time" "github.com/Dreamacro/clash/component/resolver" C "github.com/Dreamacro/clash/constant" @@ -19,13 +18,6 @@ var ( once sync.Once ) -func tcpKeepAlive(c net.Conn) { - if tcp, ok := c.(*net.TCPConn); ok { - _ = tcp.SetKeepAlive(true) - _ = tcp.SetKeepAlivePeriod(30 * time.Second) - } -} - func getClientSessionCache() tls.ClientSessionCache { once.Do(func() { globalClientSessionCache = tls.NewLRUClientSessionCache(128) diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index 2456c2c3..81408e5f 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -263,7 +263,7 @@ func (v *Vless) DialContextWithDialer(ctx context.Context, dialer C.Dialer, meta if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } - tcpKeepAlive(c) + N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) }(c) @@ -328,7 +328,7 @@ func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } - tcpKeepAlive(c) + N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) }(c) @@ -578,7 +578,7 @@ func NewVless(option VlessOption) (*Vless, error) { if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } - tcpKeepAlive(c) + N.TCPKeepAlive(c) return c, nil } diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 0fa0b1d9..3e7694d1 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -308,7 +308,7 @@ func (v *Vmess) DialContextWithDialer(ctx context.Context, dialer C.Dialer, meta if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } - tcpKeepAlive(c) + N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) }(c) @@ -369,7 +369,7 @@ func (v *Vmess) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } - tcpKeepAlive(c) + N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) }(c) @@ -469,7 +469,7 @@ func NewVmess(option VmessOption) (*Vmess, error) { if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } - tcpKeepAlive(c) + N.TCPKeepAlive(c) return c, nil } diff --git a/adapter/outboundgroup/util.go b/adapter/outboundgroup/util.go index 85373a1f..84216377 100644 --- a/adapter/outboundgroup/util.go +++ b/adapter/outboundgroup/util.go @@ -1,17 +1,5 @@ package outboundgroup -import ( - "net" - "time" -) - -func tcpKeepAlive(c net.Conn) { - if tcp, ok := c.(*net.TCPConn); ok { - _ = tcp.SetKeepAlive(true) - _ = tcp.SetKeepAlivePeriod(30 * time.Second) - } -} - type SelectAble interface { Set(string) error ForceSet(name string) diff --git a/common/net/tcpip.go b/common/net/tcpip.go index a84e7e4c..171191e2 100644 --- a/common/net/tcpip.go +++ b/common/net/tcpip.go @@ -4,8 +4,11 @@ import ( "fmt" "net" "strings" + "time" ) +var KeepAliveInterval time.Duration + func SplitNetworkType(s string) (string, string, error) { var ( shecme string @@ -44,3 +47,10 @@ func SplitHostPort(s string) (host, port string, hasPort bool, err error) { host, port, err = net.SplitHostPort(temp) return } + +func TCPKeepAlive(c net.Conn) { + if tcp, ok := c.(*net.TCPConn); ok { + _ = tcp.SetKeepAlive(true) + _ = tcp.SetKeepAlivePeriod(KeepAliveInterval * time.Second) + } +} diff --git a/config/config.go b/config/config.go index 3db51776..62fef1bd 100644 --- a/config/config.go +++ b/config/config.go @@ -16,6 +16,7 @@ import ( "github.com/Dreamacro/clash/adapter/outbound" "github.com/Dreamacro/clash/adapter/outboundgroup" "github.com/Dreamacro/clash/adapter/provider" + N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/utils" "github.com/Dreamacro/clash/component/auth" "github.com/Dreamacro/clash/component/dialer" @@ -59,6 +60,7 @@ type General struct { Sniffing bool `json:"sniffing"` EBpf EBpf `json:"-"` GlobalClientFingerprint string `json:"global-client-fingerprint"` + KeepAliveInterval int `json:"keep-alive-interval"` } // Inbound config @@ -280,6 +282,7 @@ type RawConfig struct { TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"` FindProcessMode P.FindProcessMode `yaml:"find-process-mode" json:"find-process-mode"` GlobalClientFingerprint string `yaml:"global-client-fingerprint"` + KeepAliveInterval int `yaml:"keep-alive-interval"` Sniffer RawSniffer `yaml:"sniffer"` ProxyProvider map[string]map[string]any `yaml:"proxy-providers"` @@ -559,6 +562,11 @@ func parseGeneral(cfg *RawConfig) (*General, error) { C.GeoSiteUrl = cfg.GeoXUrl.GeoSite C.MmdbUrl = cfg.GeoXUrl.Mmdb C.GeodataMode = cfg.GeodataMode + if cfg.KeepAliveInterval == 0 { + cfg.KeepAliveInterval = 30 + } + N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second + log.Infoln("Keep Alive Interval set %+v", N.KeepAliveInterval) // checkout externalUI exist if externalUI != "" { externalUI = C.Path.Resolve(externalUI) @@ -600,6 +608,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) { FindProcessMode: cfg.FindProcessMode, EBpf: cfg.EBpf, GlobalClientFingerprint: cfg.GlobalClientFingerprint, + KeepAliveInterval: cfg.KeepAliveInterval, }, nil } diff --git a/docs/config.yaml b/docs/config.yaml index 62cd0c58..ca33911e 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -50,6 +50,8 @@ external-ui: /path/to/ui/folder # 配置 WEB UI 目录,使用 http://{{externa # Utls is currently support TLS transport in TCP/grpc/WS/HTTP for VLESS/Vmess and trojan. global-client-fingerprint: chrome +keep-alive-interval: 30 + # routing-mark:6666 # 配置 fwmark 仅用于 Linux experimental: diff --git a/listener/autoredir/tcp.go b/listener/autoredir/tcp.go index 854d31d6..c390d89a 100644 --- a/listener/autoredir/tcp.go +++ b/listener/autoredir/tcp.go @@ -5,6 +5,7 @@ import ( "net/netip" "github.com/Dreamacro/clash/adapter/inbound" + N "github.com/Dreamacro/clash/common/net" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/transport/socks5" @@ -55,7 +56,7 @@ func (l *Listener) handleRedir(conn net.Conn, in chan<- C.ConnContext) { return } - _ = conn.(*net.TCPConn).SetKeepAlive(true) + N.TCPKeepAlive(conn) in <- inbound.NewSocket(target, conn, C.REDIR, l.additions...) } diff --git a/listener/mixed/mixed.go b/listener/mixed/mixed.go index e8385873..7241927d 100644 --- a/listener/mixed/mixed.go +++ b/listener/mixed/mixed.go @@ -1,9 +1,9 @@ package mixed import ( - "github.com/Dreamacro/clash/adapter/inbound" "net" + "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/common/cache" N "github.com/Dreamacro/clash/common/net" C "github.com/Dreamacro/clash/constant" @@ -70,7 +70,7 @@ func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (* } func handleConn(conn net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[string, bool], additions ...inbound.Addition) { - conn.(*net.TCPConn).SetKeepAlive(true) + N.TCPKeepAlive(conn) bufConn := N.NewBufferedConn(conn) head, err := bufConn.Peek(1) diff --git a/listener/redir/tcp.go b/listener/redir/tcp.go index ad4a91bc..9a843af8 100644 --- a/listener/redir/tcp.go +++ b/listener/redir/tcp.go @@ -4,6 +4,7 @@ import ( "net" "github.com/Dreamacro/clash/adapter/inbound" + N "github.com/Dreamacro/clash/common/net" C "github.com/Dreamacro/clash/constant" ) @@ -66,6 +67,6 @@ func handleRedir(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Ad conn.Close() return } - conn.(*net.TCPConn).SetKeepAlive(true) + N.TCPKeepAlive(conn) in <- inbound.NewSocket(target, conn, C.REDIR, additions...) } diff --git a/listener/shadowsocks/tcp.go b/listener/shadowsocks/tcp.go index febf87c3..2d0958a0 100644 --- a/listener/shadowsocks/tcp.go +++ b/listener/shadowsocks/tcp.go @@ -59,7 +59,7 @@ func New(config LC.ShadowsocksServer, tcpIn chan<- C.ConnContext, udpIn chan<- C } continue } - _ = c.(*net.TCPConn).SetKeepAlive(true) + N.TCPKeepAlive(c) go sl.HandleConn(c, tcpIn) } }() diff --git a/listener/sing_shadowsocks/server.go b/listener/sing_shadowsocks/server.go index 13ddde0d..d0e137a7 100644 --- a/listener/sing_shadowsocks/server.go +++ b/listener/sing_shadowsocks/server.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/Dreamacro/clash/adapter/inbound" + N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/sockopt" C "github.com/Dreamacro/clash/constant" LC "github.com/Dreamacro/clash/listener/config" @@ -145,7 +146,7 @@ func New(config LC.ShadowsocksServer, tcpIn chan<- C.ConnContext, udpIn chan<- C } continue } - _ = c.(*net.TCPConn).SetKeepAlive(true) + N.TCPKeepAlive(c) go sl.HandleConn(c, tcpIn) } diff --git a/listener/sing_vmess/server.go b/listener/sing_vmess/server.go index 374a378b..06f3e051 100644 --- a/listener/sing_vmess/server.go +++ b/listener/sing_vmess/server.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/Dreamacro/clash/adapter/inbound" + N "github.com/Dreamacro/clash/common/net" C "github.com/Dreamacro/clash/constant" LC "github.com/Dreamacro/clash/listener/config" "github.com/Dreamacro/clash/listener/sing" @@ -84,7 +85,7 @@ func New(config LC.VmessServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.Packe } continue } - _ = c.(*net.TCPConn).SetKeepAlive(true) + N.TCPKeepAlive(c) go sl.HandleConn(c, tcpIn) } diff --git a/listener/socks/tcp.go b/listener/socks/tcp.go index cbaac987..2fd252a3 100644 --- a/listener/socks/tcp.go +++ b/listener/socks/tcp.go @@ -67,7 +67,7 @@ func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (* } func handleSocks(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) { - conn.(*net.TCPConn).SetKeepAlive(true) + N.TCPKeepAlive(conn) bufConn := N.NewBufferedConn(conn) head, err := bufConn.Peek(1) if err != nil { diff --git a/listener/tproxy/tproxy.go b/listener/tproxy/tproxy.go index 198481f7..8c868609 100644 --- a/listener/tproxy/tproxy.go +++ b/listener/tproxy/tproxy.go @@ -4,6 +4,7 @@ import ( "net" "github.com/Dreamacro/clash/adapter/inbound" + N "github.com/Dreamacro/clash/common/net" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/socks5" ) @@ -32,7 +33,7 @@ func (l *Listener) Close() error { func (l *Listener) handleTProxy(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) { target := socks5.ParseAddrToSocksAddr(conn.LocalAddr()) - conn.(*net.TCPConn).SetKeepAlive(true) + N.TCPKeepAlive(conn) in <- inbound.NewSocket(target, conn, C.TPROXY, additions...) } diff --git a/listener/tunnel/tcp.go b/listener/tunnel/tcp.go index c1d896ad..d660d2b8 100644 --- a/listener/tunnel/tcp.go +++ b/listener/tunnel/tcp.go @@ -5,6 +5,7 @@ import ( "net" "github.com/Dreamacro/clash/adapter/inbound" + N "github.com/Dreamacro/clash/common/net" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/socks5" ) @@ -34,7 +35,7 @@ func (l *Listener) Close() error { } func (l *Listener) handleTCP(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) { - conn.(*net.TCPConn).SetKeepAlive(true) + N.TCPKeepAlive(conn) ctx := inbound.NewSocket(l.target, conn, C.TUNNEL, additions...) ctx.Metadata().SpecialProxy = l.proxy in <- ctx From 1cad615b25e277b0153192f0b6c8788597c57b8c Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 2 Sep 2023 16:54:35 +0800 Subject: [PATCH 23/66] chore: using xsync.MapOf replace sync.Map --- adapter/adapter.go | 31 ++++++++++---------- component/auth/auth.go | 10 +++---- component/nat/table.go | 58 ++++++++++++++++++++++++------------- constant/adapters.go | 14 +++++---- go.mod | 1 + go.sum | 2 ++ listener/tproxy/packet.go | 11 ++++--- transport/tuic/v4/client.go | 13 +++++---- transport/tuic/v4/server.go | 10 +++---- transport/tuic/v5/client.go | 11 +++---- transport/tuic/v5/server.go | 10 +++---- tunnel/connection.go | 9 ++---- tunnel/statistic/manager.go | 11 +++---- tunnel/tunnel.go | 5 ++-- 14 files changed, 110 insertions(+), 86 deletions(-) diff --git a/adapter/adapter.go b/adapter/adapter.go index c7351061..44f09ba1 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -10,7 +10,6 @@ import ( "net/netip" "net/url" "strconv" - "sync" "time" "github.com/Dreamacro/clash/common/atomic" @@ -19,6 +18,8 @@ import ( "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" + + "github.com/puzpuzpuz/xsync/v2" ) var UnifiedDelay = atomic.NewBool(false) @@ -37,7 +38,7 @@ type Proxy struct { history *queue.Queue[C.DelayHistory] alive *atomic.Bool url string - extra sync.Map + extra *xsync.MapOf[string, *extraProxyState] } // Alive implements C.Proxy @@ -48,7 +49,7 @@ func (p *Proxy) Alive() bool { // AliveForTestUrl implements C.Proxy func (p *Proxy) AliveForTestUrl(url string) bool { if state, ok := p.extra.Load(url); ok { - return state.(*extraProxyState).alive.Load() + return state.alive.Load() } return p.alive.Load() @@ -96,7 +97,7 @@ func (p *Proxy) DelayHistoryForTestUrl(url string) []C.DelayHistory { var queueM []C.DelayHistory if state, ok := p.extra.Load(url); ok { - queueM = state.(*extraProxyState).history.Copy() + queueM = state.history.Copy() } if queueM == nil { @@ -113,10 +114,10 @@ func (p *Proxy) DelayHistoryForTestUrl(url string) []C.DelayHistory { func (p *Proxy) ExtraDelayHistory() map[string][]C.DelayHistory { extraHistory := map[string][]C.DelayHistory{} - p.extra.Range(func(k, v interface{}) bool { + p.extra.Range(func(k string, v *extraProxyState) bool { - testUrl := k.(string) - state := v.(*extraProxyState) + testUrl := k + state := v histories := []C.DelayHistory{} queueM := state.history.Copy() @@ -155,8 +156,8 @@ func (p *Proxy) LastDelayForTestUrl(url string) (delay uint16) { history := p.history.Last() if state, ok := p.extra.Load(url); ok { - alive = state.(*extraProxyState).alive.Load() - history = state.(*extraProxyState).history.Last() + alive = state.alive.Load() + history = state.history.Last() } if !alive { @@ -226,10 +227,10 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In p.extra.Store(url, state) } - state.(*extraProxyState).alive.Store(alive) - state.(*extraProxyState).history.Put(record) - if state.(*extraProxyState).history.Len() > defaultHistoriesNum { - state.(*extraProxyState).history.Pop() + state.alive.Store(alive) + state.history.Put(record) + if state.history.Len() > defaultHistoriesNum { + state.history.Pop() } default: log.Debugln("health check result will be discarded, url: %s alive: %t, delay: %d", url, alive, t) @@ -311,7 +312,7 @@ func NewProxy(adapter C.ProxyAdapter) *Proxy { history: queue.New[C.DelayHistory](defaultHistoriesNum), alive: atomic.NewBool(true), url: "", - extra: sync.Map{}} + extra: xsync.NewMapOf[*extraProxyState]()} } func urlToMetadata(rawURL string) (addr C.Metadata, err error) { @@ -355,7 +356,7 @@ func (p *Proxy) determineFinalStoreType(store C.DelayHistoryStoreType, url strin } length := 0 - p.extra.Range(func(_, _ interface{}) bool { + p.extra.Range(func(_ string, _ *extraProxyState) bool { length++ return length < 2*C.DefaultMaxHealthCheckUrlNum }) diff --git a/component/auth/auth.go b/component/auth/auth.go index 9d30b927..9b351606 100644 --- a/component/auth/auth.go +++ b/component/auth/auth.go @@ -1,7 +1,7 @@ package auth import ( - "sync" + "github.com/puzpuzpuz/xsync/v2" ) type Authenticator interface { @@ -15,7 +15,7 @@ type AuthUser struct { } type inMemoryAuthenticator struct { - storage *sync.Map + storage *xsync.MapOf[string, string] usernames []string } @@ -31,13 +31,13 @@ func NewAuthenticator(users []AuthUser) Authenticator { return nil } - au := &inMemoryAuthenticator{storage: &sync.Map{}} + au := &inMemoryAuthenticator{storage: xsync.NewMapOf[string]()} for _, user := range users { au.storage.Store(user.User, user.Pass) } usernames := make([]string, 0, len(users)) - au.storage.Range(func(key, value any) bool { - usernames = append(usernames, key.(string)) + au.storage.Range(func(key string, value string) bool { + usernames = append(usernames, key) return true }) au.usernames = usernames diff --git a/component/nat/table.go b/component/nat/table.go index adc6eace..df258dc2 100644 --- a/component/nat/table.go +++ b/component/nat/table.go @@ -5,23 +5,28 @@ import ( "sync" C "github.com/Dreamacro/clash/constant" + + "github.com/puzpuzpuz/xsync/v2" ) type Table struct { - mapping sync.Map + mapping *xsync.MapOf[string, *Entry] + lockMap *xsync.MapOf[string, *sync.Cond] } type Entry struct { PacketConn C.PacketConn WriteBackProxy C.WriteBackProxy - LocalUDPConnMap sync.Map + LocalUDPConnMap *xsync.MapOf[string, *net.UDPConn] + LocalLockMap *xsync.MapOf[string, *sync.Cond] } func (t *Table) Set(key string, e C.PacketConn, w C.WriteBackProxy) { t.mapping.Store(key, &Entry{ PacketConn: e, WriteBackProxy: w, - LocalUDPConnMap: sync.Map{}, + LocalUDPConnMap: xsync.NewMapOf[*net.UDPConn](), + LocalLockMap: xsync.NewMapOf[*sync.Cond](), }) } @@ -34,15 +39,19 @@ func (t *Table) Get(key string) (C.PacketConn, C.WriteBackProxy) { } func (t *Table) GetOrCreateLock(key string) (*sync.Cond, bool) { - item, loaded := t.mapping.LoadOrStore(key, sync.NewCond(&sync.Mutex{})) - return item.(*sync.Cond), loaded + item, loaded := t.lockMap.LoadOrCompute(key, makeLock) + return item, loaded } func (t *Table) Delete(key string) { t.mapping.Delete(key) } -func (t *Table) GetLocalConn(lAddr, rAddr string) *net.UDPConn { +func (t *Table) DeleteLock(lockKey string) { + t.lockMap.Delete(lockKey) +} + +func (t *Table) GetForLocalConn(lAddr, rAddr string) *net.UDPConn { entry, exist := t.getEntry(lAddr) if !exist { return nil @@ -51,10 +60,10 @@ func (t *Table) GetLocalConn(lAddr, rAddr string) *net.UDPConn { if !exist { return nil } - return item.(*net.UDPConn) + return item } -func (t *Table) AddLocalConn(lAddr, rAddr string, conn *net.UDPConn) bool { +func (t *Table) AddForLocalConn(lAddr, rAddr string, conn *net.UDPConn) bool { entry, exist := t.getEntry(lAddr) if !exist { return false @@ -63,7 +72,7 @@ func (t *Table) AddLocalConn(lAddr, rAddr string, conn *net.UDPConn) bool { return true } -func (t *Table) RangeLocalConn(lAddr string, f func(key, value any) bool) { +func (t *Table) RangeForLocalConn(lAddr string, f func(key string, value *net.UDPConn) bool) { entry, exist := t.getEntry(lAddr) if !exist { return @@ -76,11 +85,11 @@ func (t *Table) GetOrCreateLockForLocalConn(lAddr, key string) (*sync.Cond, bool if !loaded { return nil, false } - item, loaded := entry.LocalUDPConnMap.LoadOrStore(key, sync.NewCond(&sync.Mutex{})) - return item.(*sync.Cond), loaded + item, loaded := entry.LocalLockMap.LoadOrCompute(key, makeLock) + return item, loaded } -func (t *Table) DeleteLocalConnMap(lAddr, key string) { +func (t *Table) DeleteForLocalConn(lAddr, key string) { entry, loaded := t.getEntry(lAddr) if !loaded { return @@ -88,17 +97,26 @@ func (t *Table) DeleteLocalConnMap(lAddr, key string) { entry.LocalUDPConnMap.Delete(key) } -func (t *Table) getEntry(key string) (*Entry, bool) { - item, ok := t.mapping.Load(key) - // This should not happen usually since this function called after PacketConn created - if !ok { - return nil, false +func (t *Table) DeleteLockForLocalConn(lAddr, key string) { + entry, loaded := t.getEntry(lAddr) + if !loaded { + return } - entry, ok := item.(*Entry) - return entry, ok + entry.LocalLockMap.Delete(key) +} + +func (t *Table) getEntry(key string) (*Entry, bool) { + return t.mapping.Load(key) +} + +func makeLock() *sync.Cond { + return sync.NewCond(&sync.Mutex{}) } // New return *Cache func New() *Table { - return &Table{} + return &Table{ + mapping: xsync.NewMapOf[*Entry](), + lockMap: xsync.NewMapOf[*sync.Cond](), + } } diff --git a/constant/adapters.go b/constant/adapters.go index a3796ef7..5639dd47 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -267,13 +267,17 @@ type NatTable interface { Delete(key string) - GetLocalConn(lAddr, rAddr string) *net.UDPConn + DeleteLock(key string) - AddLocalConn(lAddr, rAddr string, conn *net.UDPConn) bool + GetForLocalConn(lAddr, rAddr string) *net.UDPConn - RangeLocalConn(lAddr string, f func(key, value any) bool) + AddForLocalConn(lAddr, rAddr string, conn *net.UDPConn) bool - GetOrCreateLockForLocalConn(lAddr, key string) (*sync.Cond, bool) + RangeForLocalConn(lAddr string, f func(key string, value *net.UDPConn) bool) - DeleteLocalConnMap(lAddr, key string) + GetOrCreateLockForLocalConn(lAddr string, key string) (*sync.Cond, bool) + + DeleteForLocalConn(lAddr, key string) + + DeleteLockForLocalConn(lAddr, key string) } diff --git a/go.mod b/go.mod index 00fd802b..a57d9a2b 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,7 @@ require ( github.com/mroth/weightedrand/v2 v2.1.0 github.com/openacid/low v0.1.21 github.com/oschwald/maxminddb-golang v1.12.0 + github.com/puzpuzpuz/xsync/v2 v2.5.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c diff --git a/go.sum b/go.sum index 84b13831..e718b9e4 100644 --- a/go.sum +++ b/go.sum @@ -134,6 +134,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/puzpuzpuz/xsync/v2 v2.5.0 h1:2k4qrO/orvmEXZ3hmtHqIy9XaQtPTwzMZk1+iErpE8c= +github.com/puzpuzpuz/xsync/v2 v2.5.0/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/quic-go/qtls-go1-20 v0.3.2 h1:rRgN3WfnKbyik4dBV8A6girlJVxGand/d+jVKbQq5GI= diff --git a/listener/tproxy/packet.go b/listener/tproxy/packet.go index 2966fd2e..b73339a1 100644 --- a/listener/tproxy/packet.go +++ b/listener/tproxy/packet.go @@ -55,16 +55,15 @@ func (c *packet) InAddr() net.Addr { func createOrGetLocalConn(rAddr, lAddr net.Addr, in chan<- C.PacketAdapter, natTable C.NatTable) (*net.UDPConn, error) { remote := rAddr.String() local := lAddr.String() - localConn := natTable.GetLocalConn(local, remote) + localConn := natTable.GetForLocalConn(local, remote) // localConn not exist if localConn == nil { - lockKey := remote + "-lock" - cond, loaded := natTable.GetOrCreateLockForLocalConn(local, lockKey) + cond, loaded := natTable.GetOrCreateLockForLocalConn(local, remote) if loaded { cond.L.Lock() cond.Wait() // we should get localConn here - localConn = natTable.GetLocalConn(local, remote) + localConn = natTable.GetForLocalConn(local, remote) if localConn == nil { return nil, fmt.Errorf("localConn is nil, nat entry not exist") } @@ -74,7 +73,7 @@ func createOrGetLocalConn(rAddr, lAddr net.Addr, in chan<- C.PacketAdapter, natT return nil, fmt.Errorf("cond is nil, nat entry not exist") } defer func() { - natTable.DeleteLocalConnMap(local, lockKey) + natTable.DeleteLockForLocalConn(local, remote) cond.Broadcast() }() conn, err := listenLocalConn(rAddr, lAddr, in, natTable) @@ -82,7 +81,7 @@ func createOrGetLocalConn(rAddr, lAddr net.Addr, in chan<- C.PacketAdapter, natT log.Errorln("listenLocalConn failed with error: %s, packet loss (rAddr[%T]=%s lAddr[%T]=%s)", err.Error(), rAddr, remote, lAddr, local) return nil, err } - natTable.AddLocalConn(local, remote, conn) + natTable.AddForLocalConn(local, remote, conn) localConn = conn } } diff --git a/transport/tuic/v4/client.go b/transport/tuic/v4/client.go index fd3bf54a..ce33b72b 100644 --- a/transport/tuic/v4/client.go +++ b/transport/tuic/v4/client.go @@ -22,6 +22,7 @@ import ( "github.com/Dreamacro/clash/transport/tuic/common" "github.com/metacubex/quic-go" + "github.com/puzpuzpuz/xsync/v2" "github.com/zhangyunhao116/fastrand" ) @@ -49,7 +50,7 @@ type clientImpl struct { openStreams atomic.Int64 closed atomic.Bool - udpInputMap sync.Map + udpInputMap *xsync.MapOf[uint32, net.Conn] // only ready for PoolClient dialerRef C.Dialer @@ -263,11 +264,10 @@ func (t *clientImpl) forceClose(quicConn quic.Connection, err error) { if quicConn != nil { _ = quicConn.CloseWithError(ProtocolError, errStr) } - udpInputMap := &t.udpInputMap - udpInputMap.Range(func(key, value any) bool { - if conn, ok := value.(net.Conn); ok { - _ = conn.Close() - } + udpInputMap := t.udpInputMap + udpInputMap.Range(func(key uint32, value net.Conn) bool { + conn := value + _ = conn.Close() udpInputMap.Delete(key) return true }) @@ -469,6 +469,7 @@ func NewClient(clientOption *ClientOption, udp bool, dialerRef C.Dialer) *Client ClientOption: clientOption, udp: udp, dialerRef: dialerRef, + udpInputMap: xsync.NewIntegerMapOf[uint32, net.Conn](), } c := &Client{ci} runtime.SetFinalizer(c, closeClient) diff --git a/transport/tuic/v4/server.go b/transport/tuic/v4/server.go index b0012d96..56133fea 100644 --- a/transport/tuic/v4/server.go +++ b/transport/tuic/v4/server.go @@ -17,6 +17,7 @@ import ( "github.com/gofrs/uuid/v5" "github.com/metacubex/quic-go" + "github.com/puzpuzpuz/xsync/v2" ) type ServerOption struct { @@ -33,6 +34,7 @@ func NewServerHandler(option *ServerOption, quicConn quic.EarlyConnection, uuid quicConn: quicConn, uuid: uuid, authCh: make(chan struct{}), + udpInputMap: xsync.NewIntegerMapOf[uint32, *atomic.Bool](), } } @@ -45,7 +47,7 @@ type serverHandler struct { authOk atomic.Bool authOnce sync.Once - udpInputMap sync.Map + udpInputMap *xsync.MapOf[uint32, *atomic.Bool] } func (s *serverHandler) AuthOk() bool { @@ -78,8 +80,7 @@ func (s *serverHandler) parsePacket(packet *Packet, udpRelayMode common.UdpRelay assocId = packet.ASSOC_ID - v, _ := s.udpInputMap.LoadOrStore(assocId, &atomic.Bool{}) - writeClosed := v.(*atomic.Bool) + writeClosed, _ := s.udpInputMap.LoadOrCompute(assocId, func() *atomic.Bool { return &atomic.Bool{} }) if writeClosed.Load() { return nil } @@ -173,8 +174,7 @@ func (s *serverHandler) HandleUniStream(reader *bufio.Reader) (err error) { if err != nil { return } - if v, loaded := s.udpInputMap.LoadAndDelete(disassociate.ASSOC_ID); loaded { - writeClosed := v.(*atomic.Bool) + if writeClosed, loaded := s.udpInputMap.LoadAndDelete(disassociate.ASSOC_ID); loaded { writeClosed.Store(true) } case HeartbeatType: diff --git a/transport/tuic/v5/client.go b/transport/tuic/v5/client.go index 74dfd581..c4ac25d4 100644 --- a/transport/tuic/v5/client.go +++ b/transport/tuic/v5/client.go @@ -20,6 +20,7 @@ import ( "github.com/Dreamacro/clash/transport/tuic/common" "github.com/metacubex/quic-go" + "github.com/puzpuzpuz/xsync/v2" "github.com/zhangyunhao116/fastrand" ) @@ -46,7 +47,7 @@ type clientImpl struct { openStreams atomic.Int64 closed atomic.Bool - udpInputMap sync.Map + udpInputMap xsync.MapOf[uint16, net.Conn] // only ready for PoolClient dialerRef C.Dialer @@ -270,10 +271,9 @@ func (t *clientImpl) forceClose(quicConn quic.Connection, err error) { _ = quicConn.CloseWithError(ProtocolError, errStr) } udpInputMap := &t.udpInputMap - udpInputMap.Range(func(key, value any) bool { - if conn, ok := value.(net.Conn); ok { - _ = conn.Close() - } + udpInputMap.Range(func(key uint16, value net.Conn) bool { + conn := value + _ = conn.Close() udpInputMap.Delete(key) return true }) @@ -406,6 +406,7 @@ func NewClient(clientOption *ClientOption, udp bool, dialerRef C.Dialer) *Client ClientOption: clientOption, udp: udp, dialerRef: dialerRef, + udpInputMap: *xsync.NewIntegerMapOf[uint16, net.Conn](), } c := &Client{ci} runtime.SetFinalizer(c, closeClient) diff --git a/transport/tuic/v5/server.go b/transport/tuic/v5/server.go index 30259583..10003a9d 100644 --- a/transport/tuic/v5/server.go +++ b/transport/tuic/v5/server.go @@ -16,6 +16,7 @@ import ( "github.com/gofrs/uuid/v5" "github.com/metacubex/quic-go" + "github.com/puzpuzpuz/xsync/v2" ) type ServerOption struct { @@ -32,6 +33,7 @@ func NewServerHandler(option *ServerOption, quicConn quic.EarlyConnection, uuid quicConn: quicConn, uuid: uuid, authCh: make(chan struct{}), + udpInputMap: xsync.NewIntegerMapOf[uint16, *serverUDPInput](), } } @@ -45,7 +47,7 @@ type serverHandler struct { authUUID atomic.TypedValue[string] authOnce sync.Once - udpInputMap sync.Map + udpInputMap *xsync.MapOf[uint16, *serverUDPInput] } func (s *serverHandler) AuthOk() bool { @@ -94,8 +96,7 @@ func (s *serverHandler) parsePacket(packet *Packet, udpRelayMode common.UdpRelay assocId = packet.ASSOC_ID - v, _ := s.udpInputMap.LoadOrStore(assocId, &serverUDPInput{}) - input := v.(*serverUDPInput) + input, _ := s.udpInputMap.LoadOrCompute(assocId, func() *serverUDPInput { return &serverUDPInput{} }) if input.writeClosed.Load() { return nil } @@ -186,8 +187,7 @@ func (s *serverHandler) HandleUniStream(reader *bufio.Reader) (err error) { if err != nil { return } - if v, loaded := s.udpInputMap.LoadAndDelete(disassociate.ASSOC_ID); loaded { - input := v.(*serverUDPInput) + if input, loaded := s.udpInputMap.LoadAndDelete(disassociate.ASSOC_ID); loaded { input.writeClosed.Store(true) } } diff --git a/tunnel/connection.go b/tunnel/connection.go index 2e76b86b..9fc4f405 100644 --- a/tunnel/connection.go +++ b/tunnel/connection.go @@ -73,12 +73,9 @@ func handleUDPToLocal(writeBack C.WriteBack, pc N.EnhancePacketConn, key string, } func closeAllLocalCoon(lAddr string) { - natTable.RangeLocalConn(lAddr, func(key, value any) bool { - conn, ok := value.(*net.UDPConn) - if !ok || conn == nil { - log.Debugln("Value %#v unknown value when closing TProxy local conn...", conn) - return true - } + natTable.RangeForLocalConn(lAddr, func(key string, value *net.UDPConn) bool { + conn := value + conn.Close() log.Debugln("Closing TProxy local conn... lAddr=%s rAddr=%s", lAddr, key) return true diff --git a/tunnel/statistic/manager.go b/tunnel/statistic/manager.go index 2358d0bd..19ce58d9 100644 --- a/tunnel/statistic/manager.go +++ b/tunnel/statistic/manager.go @@ -2,11 +2,11 @@ package statistic import ( "os" - "sync" "time" "github.com/Dreamacro/clash/common/atomic" + "github.com/puzpuzpuz/xsync/v2" "github.com/shirou/gopsutil/v3/process" ) @@ -14,6 +14,7 @@ var DefaultManager *Manager func init() { DefaultManager = &Manager{ + connections: xsync.NewMapOf[Tracker](), uploadTemp: atomic.NewInt64(0), downloadTemp: atomic.NewInt64(0), uploadBlip: atomic.NewInt64(0), @@ -27,7 +28,7 @@ func init() { } type Manager struct { - connections sync.Map + connections *xsync.MapOf[string, Tracker] uploadTemp *atomic.Int64 downloadTemp *atomic.Int64 uploadBlip *atomic.Int64 @@ -48,14 +49,14 @@ func (m *Manager) Leave(c Tracker) { func (m *Manager) Get(id string) (c Tracker) { if value, ok := m.connections.Load(id); ok { - c = value.(Tracker) + c = value } return } func (m *Manager) Range(f func(c Tracker) bool) { - m.connections.Range(func(key, value any) bool { - return f(value.(Tracker)) + m.connections.Range(func(key string, value Tracker) bool { + return f(value) }) } diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index d4c15a87..b7557e10 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -318,8 +318,7 @@ func handleUDPConn(packet C.PacketAdapter) { return } - lockKey := key + "-lock" - cond, loaded := natTable.GetOrCreateLock(lockKey) + cond, loaded := natTable.GetOrCreateLock(key) go func() { defer packet.Drop() @@ -333,7 +332,7 @@ func handleUDPConn(packet C.PacketAdapter) { } defer func() { - natTable.Delete(lockKey) + natTable.DeleteLock(key) cond.Broadcast() }() From d6b80acfbc1570c07e99d91f035119d443cf9d84 Mon Sep 17 00:00:00 2001 From: H1JK Date: Sat, 2 Sep 2023 20:17:43 +0800 Subject: [PATCH 24/66] chore: Use xsync provided map size calculation --- adapter/adapter.go | 12 +------- test/go.mod | 23 ++++++++------- test/go.sum | 73 +++++++++++++++++++++++++++++++++------------- 3 files changed, 67 insertions(+), 41 deletions(-) diff --git a/adapter/adapter.go b/adapter/adapter.go index 44f09ba1..13f7f06f 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -355,13 +355,7 @@ func (p *Proxy) determineFinalStoreType(store C.DelayHistoryStoreType, url strin return C.OriginalHistory } - length := 0 - p.extra.Range(func(_ string, _ *extraProxyState) bool { - length++ - return length < 2*C.DefaultMaxHealthCheckUrlNum - }) - - if length == 0 { + if p.extra.Size() < 2*C.DefaultMaxHealthCheckUrlNum { return C.ExtraHistory } @@ -370,9 +364,5 @@ func (p *Proxy) determineFinalStoreType(store C.DelayHistoryStoreType, url strin return C.ExtraHistory } - if length < 2*C.DefaultMaxHealthCheckUrlNum { - return C.ExtraHistory - } - return store } diff --git a/test/go.mod b/test/go.mod index 80f958a5..2d7ef893 100644 --- a/test/go.mod +++ b/test/go.mod @@ -14,12 +14,13 @@ require ( replace github.com/Dreamacro/clash => ../ require ( - github.com/3andne/restls-client-go v0.1.4 // indirect + github.com/3andne/restls-client-go v0.1.6 // indirect github.com/Microsoft/go-winio v0.6.0 // indirect github.com/RyuaNerin/go-krypto v1.0.2 // indirect github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect github.com/andybalholm/brotli v1.0.5 // indirect + github.com/beevik/ntp v1.3.0 // indirect github.com/cilium/ebpf v0.11.0 // indirect github.com/coreos/go-iptables v0.7.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -31,6 +32,7 @@ require ( github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/gaukas/godicttls v0.0.4 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gofrs/uuid/v5 v5.0.0 // indirect @@ -44,7 +46,7 @@ require ( github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c // indirect github.com/josharian/native v1.1.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect - github.com/klauspost/compress v1.15.15 // indirect + github.com/klauspost/compress v1.16.7 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect @@ -52,7 +54,7 @@ require ( github.com/mdlayher/socket v0.4.1 // indirect github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 // indirect github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475 // indirect - github.com/metacubex/quic-go v0.37.4-0.20230806014204-ef9b221eec12 // indirect + github.com/metacubex/quic-go v0.38.1-0.20230821081539-517fdb17fb28 // indirect github.com/metacubex/sing-shadowsocks v0.2.4 // indirect github.com/metacubex/sing-shadowsocks2 v0.1.3 // indirect github.com/metacubex/sing-tun v0.1.11 // indirect @@ -60,7 +62,7 @@ require ( github.com/metacubex/sing-wireguard v0.0.0-20230611155257-1498ae315a28 // indirect github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect - github.com/mroth/weightedrand/v2 v2.0.2 // indirect + github.com/mroth/weightedrand/v2 v2.1.0 // indirect github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/openacid/low v0.1.21 // indirect @@ -71,17 +73,18 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/puzpuzpuz/xsync/v2 v2.5.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/qtls-go1-20 v0.3.1 // indirect + github.com/quic-go/qtls-go1-20 v0.3.2 // indirect github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect - github.com/sagernet/sing v0.2.9 // indirect - github.com/sagernet/sing-mux v0.1.2 // indirect + github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a // indirect + github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c // indirect github.com/sagernet/sing-shadowtls v0.1.4 // indirect github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect - github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 // indirect + github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 // indirect github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 // indirect - github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 // indirect + github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f // indirect github.com/samber/lo v1.38.1 // indirect github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect github.com/shirou/gopsutil/v3 v3.23.7 // indirect @@ -99,7 +102,7 @@ require ( gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect go.etcd.io/bbolt v1.3.7 // indirect golang.org/x/crypto v0.12.0 // indirect - golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b // indirect + golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb // indirect golang.org/x/mod v0.11.0 // indirect golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.11.0 // indirect diff --git a/test/go.sum b/test/go.sum index fbf635c6..03971c26 100644 --- a/test/go.sum +++ b/test/go.sum @@ -1,5 +1,5 @@ -github.com/3andne/restls-client-go v0.1.4 h1:kLNC2aSRHPlEVYmTj6EOqJoorCpobEe2toMRSfBF7FU= -github.com/3andne/restls-client-go v0.1.4/go.mod h1:04CGbRk1BwBiEDles8b5mlKgTqIwE5MqF7JDloJV47I= +github.com/3andne/restls-client-go v0.1.6 h1:tRx/YilqW7iHpgmEL4E1D8dAsuB0tFF3uvncS+B6I08= +github.com/3andne/restls-client-go v0.1.6/go.mod h1:iEdTZNt9kzPIxjIGSMScUFSBrUH6bFRNg0BWlP4orEY= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= @@ -11,6 +11,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/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/beevik/ntp v1.3.0 h1:/w5VhpW5BGKS37vFm1p9oVk/t4HnnkKZAZIubHM6F7Q= +github.com/beevik/ntp v1.3.0/go.mod h1:vD6h1um4kzXpqmLTuu0cCLcC+NfvC0IC+ltmEDA8E78= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= 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= @@ -44,6 +46,8 @@ github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtB github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk= +github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= @@ -81,8 +85,8 @@ github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2E github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -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.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -99,8 +103,8 @@ github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvO github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88= github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475 h1:qSEOvPPaMrWggFyFhFYGyMR8i1HKyhXjdi1QYUAa2ww= github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475/go.mod h1:wehEpqiogdeyncfhckJP5gD2LtBgJW0wnDC24mJ+8Jg= -github.com/metacubex/quic-go v0.37.4-0.20230806014204-ef9b221eec12 h1:18tcXxLgwjUjs38QM1E1a+AAh4j+Mo/mKcJTmqHrN9c= -github.com/metacubex/quic-go v0.37.4-0.20230806014204-ef9b221eec12/go.mod h1:HhHoyskMk4kzfLPKcm7EF7pGXF89KRVwjbGrEaN6lIU= +github.com/metacubex/quic-go v0.38.1-0.20230821081539-517fdb17fb28 h1:ggSo4B1LDH9ZIROoUibxlrUpi7YCMri7HMXn4aNQkiM= +github.com/metacubex/quic-go v0.38.1-0.20230821081539-517fdb17fb28/go.mod h1:SthFvvoqgrEUgIxQXRnqdUAAYQECBavkhl7iA0geVd8= github.com/metacubex/sing-shadowsocks v0.2.4 h1:Gc99Z17JVif1PKKq1pjqhSmc2kvHUgk+AqxOstCzhQ0= github.com/metacubex/sing-shadowsocks v0.2.4/go.mod h1:w9qoEZSh9aKeXSLXHe0DGbG2UE9/2VlLGwukzQZ7byI= github.com/metacubex/sing-shadowsocks2 v0.1.3 h1:nZvH+4jQXZ92NeNdR9fXaUGTPNJPt6u0nkcuh/NEt5Y= @@ -117,8 +121,8 @@ github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mroth/weightedrand/v2 v2.0.2 h1:A8wJRUBcfguGl6oOQHI8fy5P4ViGRT9hdQdlG/7RiXo= -github.com/mroth/weightedrand/v2 v2.0.2/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU= +github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU= +github.com/mroth/weightedrand/v2 v2.1.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU= github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 h1:1102pQc2SEPp5+xrS26wEaeb26sZy6k9/ZXlZN+eXE4= github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7/go.mod h1:UqoUn6cHESlliMhOnKLWr+CBH+e3bazUPvFj1XZwAjs= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= @@ -143,10 +147,12 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/puzpuzpuz/xsync/v2 v2.5.0 h1:2k4qrO/orvmEXZ3hmtHqIy9XaQtPTwzMZk1+iErpE8c= +github.com/puzpuzpuz/xsync/v2 v2.5.0/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/qtls-go1-20 v0.3.1 h1:O4BLOM3hwfVF3AcktIylQXyl7Yi2iBNVy5QsV+ySxbg= -github.com/quic-go/qtls-go1-20 v0.3.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/quic-go/qtls-go1-20 v0.3.2 h1:rRgN3WfnKbyik4dBV8A6girlJVxGand/d+jVKbQq5GI= +github.com/quic-go/qtls-go1-20 v0.3.2/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 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= @@ -154,20 +160,20 @@ github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6E 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/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= -github.com/sagernet/sing v0.2.9 h1:3wsTz+JG5Wzy65eZnh6AuCrD2QqcRF6Iq6f7ttmJsAo= -github.com/sagernet/sing v0.2.9/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= -github.com/sagernet/sing-mux v0.1.2 h1:av2/m6e+Gh+ECTuJZqYCjJz55BNkot0VyRMkREqyF/g= -github.com/sagernet/sing-mux v0.1.2/go.mod h1:r2V8AlOzXaRCHXK7fILCUGzuI2iILweTaG8C5xlpHxo= +github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a h1:b89t6Mjgk4rJ5lrNMnCzy1/J116XkhgdB3YNd9FHyF4= +github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a/go.mod h1:9uOZwWkhT2Z2WldolLxX34s+1svAX4i4vvz5hy8u1MA= +github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c h1:35/FowAvt3Z62mck0TXzVc4jS5R5CWq62qcV2P1cp0I= +github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c/go.mod h1:TKxqIvfQQgd36jp2tzsPavGjYTVZilV+atip1cssjIY= github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k= github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0= -github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE= -github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9/go.mod h1:FUyTEc5ye5NjKnDTDMuiLF2M6T4BE6y6KZuax//UCEg= +github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 h1:Px+hN4Vzgx+iCGVnWH5A8eR7JhNnIV3rGQmBxA7cw6Q= +github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6/go.mod h1:zovq6vTvEM6ECiqE3Eeb9rpIylPpamPcmrJ9tv0Bt0M= github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4= github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM= -github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 h1:g6QtRWQ2dKX7EQP++1JLNtw4C2TNxd4/ov8YUpOPOSo= -github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77/go.mod h1:pJDdXzZIwJ+2vmnT0TKzmf8meeum+e2mTDSehw79eE0= +github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f h1:Kvo8w8Y9lzFGB/7z09MJ3TR99TFtfI/IuY87Ygcycho= +github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f/go.mod h1:mySs0abhpc/gLlvhoq7HP1RzOaRmIXVeZGCh++zoApk= github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg= @@ -210,6 +216,7 @@ github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zhangyunhao116/fastrand v0.3.0 h1:7bwe124xcckPulX6fxtr2lFdO2KQqaefdtbk+mqO/Ig= @@ -221,28 +228,39 @@ go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= 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.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b h1:r+vk0EmXNmekl0S0BascoeeoHk/L7wmaW2QF90K+kYI= -golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb h1:mIKbk8weKhSeLH2GmUTrvx8CjkyJmnU1wFmg59CUjFA= +golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= 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.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -257,18 +275,31 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/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.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= 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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= @@ -279,6 +310,8 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From d6cf2a837ff101b0d3c634a8e76b022ff5505353 Mon Sep 17 00:00:00 2001 From: "riolu.rs" Date: Sun, 3 Sep 2023 17:48:52 +0800 Subject: [PATCH 25/66] chore: ntp service dep with sing, optional synchronize system time --- config/config.go | 46 +++++++++++----------- go.mod | 1 - go.sum | 29 -------------- hub/executor/executor.go | 2 +- ntp/service.go | 84 +++++++++++++++++++++++----------------- ntp/time_stub.go | 12 ++++++ ntp/time_unix.go | 14 +++++++ ntp/time_windows.go | 32 +++++++++++++++ 8 files changed, 130 insertions(+), 90 deletions(-) create mode 100644 ntp/time_stub.go create mode 100644 ntp/time_unix.go create mode 100644 ntp/time_windows.go diff --git a/config/config.go b/config/config.go index 62fef1bd..cb030530 100644 --- a/config/config.go +++ b/config/config.go @@ -91,10 +91,11 @@ type Controller struct { // NTP config type NTP struct { - Enable bool `yaml:"enable"` - Server string `yaml:"server"` - Port int `yaml:"port"` - Interval int `yaml:"interval"` + Enable bool `yaml:"enable"` + WriteToSystem bool `yaml:"write-to-system"` + Server string `yaml:"server"` + Port int `yaml:"port"` + Interval int `yaml:"interval"` } // DNS config @@ -179,10 +180,11 @@ type Config struct { } type RawNTP struct { - Enable bool `yaml:"enable"` - Server string `yaml:"server"` - ServerPort int `yaml:"server-port"` - Interval int `yaml:"interval"` + Enable bool `yaml:"enable"` + WriteToSystem bool `yaml:"write-to-system"` + Server string `yaml:"server"` + ServerPort int `yaml:"server-port"` + Interval int `yaml:"interval"` } type RawDNS struct { @@ -399,6 +401,13 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { InboundInterface: "lo", Bypass: []string{}, }, + NTP: RawNTP{ + Enable: false, + WriteToSystem: false, + Server: "time.apple.com", + ServerPort: 123, + Interval: 30, + }, DNS: RawDNS{ Enable: false, IPv6: false, @@ -1162,24 +1171,13 @@ func parseFallbackGeoSite(countries []string, rules []C.Rule) ([]*router.DomainM } func paresNTP(rawCfg *RawConfig) *NTP { - var server = "time.apple.com" - var port = 123 - var interval = 30 cfg := rawCfg.NTP - if len(cfg.Server) != 0 { - server = cfg.Server - } - if cfg.ServerPort != 0 { - port = cfg.ServerPort - } - if cfg.Interval != 0 { - interval = cfg.Interval - } ntpCfg := &NTP{ - Enable: cfg.Enable, - Server: server, - Port: port, - Interval: interval, + Enable: cfg.Enable, + Server: cfg.Server, + Port: cfg.ServerPort, + Interval: cfg.Interval, + WriteToSystem: cfg.WriteToSystem, } return ntpCfg } diff --git a/go.mod b/go.mod index a57d9a2b..aa37eff2 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.20 require ( github.com/3andne/restls-client-go v0.1.6 github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da - github.com/beevik/ntp v1.3.0 github.com/cilium/ebpf v0.11.0 github.com/coreos/go-iptables v0.7.0 github.com/dlclark/regexp2 v1.10.0 diff --git a/go.sum b/go.sum index e718b9e4..530b615d 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,6 @@ 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/beevik/ntp v1.3.0 h1:/w5VhpW5BGKS37vFm1p9oVk/t4HnnkKZAZIubHM6F7Q= -github.com/beevik/ntp v1.3.0/go.mod h1:vD6h1um4kzXpqmLTuu0cCLcC+NfvC0IC+ltmEDA8E78= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= 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= @@ -197,7 +195,6 @@ github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1 github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zhangyunhao116/fastrand v0.3.0 h1:7bwe124xcckPulX6fxtr2lFdO2KQqaefdtbk+mqO/Ig= @@ -210,8 +207,6 @@ go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= 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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb h1:mIKbk8weKhSeLH2GmUTrvx8CjkyJmnU1wFmg59CUjFA= @@ -219,24 +214,15 @@ golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb/go.mod h1:FXUEEKJgO7OQYeo8N0 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -250,30 +236,17 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= 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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= @@ -282,8 +255,6 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm 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.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/hub/executor/executor.go b/hub/executor/executor.go index b7111c14..f4364b7b 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -186,7 +186,7 @@ func updateExperimental(c *config.Config) { func updateNTP(c *config.NTP) { if c.Enable { ntp.ReCreateNTPService(net.JoinHostPort(c.Server, strconv.Itoa(c.Port)), - time.Duration(c.Interval)) + time.Duration(c.Interval), c.WriteToSystem) } } diff --git a/ntp/service.go b/ntp/service.go index af06a571..726ac636 100644 --- a/ntp/service.go +++ b/ntp/service.go @@ -3,7 +3,9 @@ package ntp import ( "context" "github.com/Dreamacro/clash/log" - "github.com/beevik/ntp" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/common/ntp" "sync" "time" ) @@ -12,43 +14,37 @@ var offset time.Duration var service *Service type Service struct { - addr string - interval time.Duration - ticker *time.Ticker - ctx context.Context - cancel context.CancelFunc - mu sync.Mutex - running bool + server M.Socksaddr + ticker *time.Ticker + ctx context.Context + cancel context.CancelFunc + mu sync.Mutex + syncSystemTime bool + running bool } -func ReCreateNTPService(addr string, interval time.Duration) { +func ReCreateNTPService(server string, interval time.Duration, syncSystemTime bool) { if service != nil { service.Stop() } ctx, cancel := context.WithCancel(context.Background()) - service = &Service{addr: addr, interval: interval, ctx: ctx, cancel: cancel} + service = &Service{ + ctx: ctx, + cancel: cancel, + server: M.ParseSocksaddr(server), + ticker: time.NewTicker(interval * time.Minute), + syncSystemTime: syncSystemTime, + } service.Start() } func (srv *Service) Start() { srv.mu.Lock() defer srv.mu.Unlock() - log.Infoln("NTP service start") - srv.ticker = time.NewTicker(srv.interval * time.Minute) + log.Infoln("NTP service start, sync system time is %t", srv.syncSystemTime) service.running = true - go func() { - for { - err := srv.updateTime(srv.addr) - if err != nil { - log.Warnln("updateTime failed: %s", err) - } - select { - case <-srv.ticker.C: - case <-srv.ctx.Done(): - return - } - } - }() + srv.update() + go srv.loopUpdate() } func (srv *Service) Stop() { @@ -70,20 +66,38 @@ func (srv *Service) Running() bool { return srv.running } -func (srv *Service) updateTime(addr string) error { - response, err := ntp.Query(addr) - if err != nil { - return err +func (srv *Service) update() { + response, err := ntp.Exchange(context.Background(), N.SystemDialer, srv.server) + if err != nil || response == nil { + log.Errorln("initialize time: %s", err) } - localTime := time.Now() - ntpTime := response.Time - offset = localTime.Sub(ntpTime) + offset = response.ClockOffset if offset > time.Duration(0) { - log.Warnln("System clock is ahead of NTP time by %s", offset) + log.Infoln("System clock is ahead of NTP time by %s", offset) } else if offset < time.Duration(0) { - log.Warnln("System clock is behind NTP time by %s", -offset) + log.Infoln("System clock is behind NTP time by %s", -offset) + } + if srv.syncSystemTime { + timeNow := response.Time + err = setSystemTime(timeNow) + if err == nil { + log.Infoln("sync system time success: %s", timeNow.Local().Format(ntp.TimeLayout)) + } else { + log.Errorln("write time to system: %s", err) + srv.syncSystemTime = false + } + } +} + +func (srv *Service) loopUpdate() { + for { + select { + case <-srv.ctx.Done(): + return + case <-srv.ticker.C: + } + srv.update() } - return nil } func Now() time.Time { diff --git a/ntp/time_stub.go b/ntp/time_stub.go new file mode 100644 index 00000000..12050983 --- /dev/null +++ b/ntp/time_stub.go @@ -0,0 +1,12 @@ +//go:build !(windows || linux || darwin) + +package ntp + +import ( + "os" + "time" +) + +func setSystemTime(nowTime time.Time) error { + return os.ErrInvalid +} diff --git a/ntp/time_unix.go b/ntp/time_unix.go new file mode 100644 index 00000000..9e819473 --- /dev/null +++ b/ntp/time_unix.go @@ -0,0 +1,14 @@ +//go:build linux || darwin + +package ntp + +import ( + "time" + + "golang.org/x/sys/unix" +) + +func setSystemTime(nowTime time.Time) error { + timeVal := unix.NsecToTimeval(nowTime.UnixNano()) + return unix.Settimeofday(&timeVal) +} diff --git a/ntp/time_windows.go b/ntp/time_windows.go new file mode 100644 index 00000000..8ef29b1b --- /dev/null +++ b/ntp/time_windows.go @@ -0,0 +1,32 @@ +package ntp + +import ( + "time" + "unsafe" + + "golang.org/x/sys/windows" +) + +func setSystemTime(nowTime time.Time) error { + var systemTime windows.Systemtime + systemTime.Year = uint16(nowTime.Year()) + systemTime.Month = uint16(nowTime.Month()) + systemTime.Day = uint16(nowTime.Day()) + systemTime.Hour = uint16(nowTime.Hour()) + systemTime.Minute = uint16(nowTime.Minute()) + systemTime.Second = uint16(nowTime.Second()) + systemTime.Milliseconds = uint16(nowTime.UnixMilli() - nowTime.Unix()*1000) + + dllKernel32 := windows.NewLazySystemDLL("kernel32.dll") + proc := dllKernel32.NewProc("SetSystemTime") + + _, _, err := proc.Call( + uintptr(unsafe.Pointer(&systemTime)), + ) + + if err != nil && err.Error() != "The operation completed successfully." { + return err + } + + return nil +} From 1d4af2d92bcae7ec394830e58435ded6454882ef Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sun, 3 Sep 2023 20:42:54 +0800 Subject: [PATCH 26/66] chore: TCPKeepAlive interval set to 15s by default --- common/net/tcpip.go | 2 +- config/config.go | 8 ++++---- docs/config.yaml | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/common/net/tcpip.go b/common/net/tcpip.go index 171191e2..7bc31edd 100644 --- a/common/net/tcpip.go +++ b/common/net/tcpip.go @@ -7,7 +7,7 @@ import ( "time" ) -var KeepAliveInterval time.Duration +var KeepAliveInterval = 15 * time.Second func SplitNetworkType(s string) (string, string, error) { var ( diff --git a/config/config.go b/config/config.go index cb030530..4421adc2 100644 --- a/config/config.go +++ b/config/config.go @@ -571,11 +571,11 @@ func parseGeneral(cfg *RawConfig) (*General, error) { C.GeoSiteUrl = cfg.GeoXUrl.GeoSite C.MmdbUrl = cfg.GeoXUrl.Mmdb C.GeodataMode = cfg.GeodataMode - if cfg.KeepAliveInterval == 0 { - cfg.KeepAliveInterval = 30 + if cfg.KeepAliveInterval != 0 { + N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second } - N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second - log.Infoln("Keep Alive Interval set %+v", N.KeepAliveInterval) + + log.Debugln("TCP Keep Alive Interval set %+v", N.KeepAliveInterval) // checkout externalUI exist if externalUI != "" { externalUI = C.Path.Resolve(externalUI) diff --git a/docs/config.yaml b/docs/config.yaml index ca33911e..cd540311 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -50,7 +50,8 @@ external-ui: /path/to/ui/folder # 配置 WEB UI 目录,使用 http://{{externa # Utls is currently support TLS transport in TCP/grpc/WS/HTTP for VLESS/Vmess and trojan. global-client-fingerprint: chrome -keep-alive-interval: 30 +# TCP keep alive interval +keep-alive-interval: 15 # routing-mark:6666 # 配置 fwmark 仅用于 Linux experimental: From a1eab125ee29cc4d858f454af9200a2f2455240e Mon Sep 17 00:00:00 2001 From: "riolu.rs" Date: Mon, 4 Sep 2023 18:19:22 +0800 Subject: [PATCH 27/66] fix: ntp service panic --- ntp/service.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/ntp/service.go b/ntp/service.go index 726ac636..3234180d 100644 --- a/ntp/service.go +++ b/ntp/service.go @@ -67,9 +67,19 @@ func (srv *Service) Running() bool { } func (srv *Service) update() { - response, err := ntp.Exchange(context.Background(), N.SystemDialer, srv.server) - if err != nil || response == nil { - log.Errorln("initialize time: %s", err) + var response *ntp.Response + var err error + for i := 0; i < 3; i++ { + response, err = ntp.Exchange(context.Background(), N.SystemDialer, srv.server) + if err != nil { + if i == 2 { + log.Errorln("Initialize NTP time failed: %s", err) + return + } + time.Sleep(time.Second * 2) // wait for 2 seconds before the next try + continue + } + break } offset = response.ClockOffset if offset > time.Duration(0) { @@ -81,9 +91,9 @@ func (srv *Service) update() { timeNow := response.Time err = setSystemTime(timeNow) if err == nil { - log.Infoln("sync system time success: %s", timeNow.Local().Format(ntp.TimeLayout)) + log.Infoln("Sync system time success: %s", timeNow.Local().Format(ntp.TimeLayout)) } else { - log.Errorln("write time to system: %s", err) + log.Errorln("Write time to system: %s", err) srv.syncSystemTime = false } } From 7286391883eedab755dca5e4e6cc6bda476c2793 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Thu, 7 Sep 2023 18:44:58 +0800 Subject: [PATCH 28/66] feat: support users to customize download ua --- component/http/http.go | 6 ++---- config/config.go | 5 +++++ constant/http.go | 5 +++++ 3 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 constant/http.go diff --git a/component/http/http.go b/component/http/http.go index bcede09f..c5172fcb 100644 --- a/component/http/http.go +++ b/component/http/http.go @@ -10,14 +10,12 @@ import ( "time" "github.com/Dreamacro/clash/component/tls" + C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/listener/inner" ) -const ( - UA = "clash.meta" -) - func HttpRequest(ctx context.Context, url, method string, header map[string][]string, body io.Reader) (*http.Response, error) { + UA := C.UA method = strings.ToUpper(method) urlRes, err := URL.Parse(url) if err != nil { diff --git a/config/config.go b/config/config.go index 4421adc2..56c616d0 100644 --- a/config/config.go +++ b/config/config.go @@ -60,6 +60,7 @@ type General struct { Sniffing bool `json:"sniffing"` EBpf EBpf `json:"-"` GlobalClientFingerprint string `json:"global-client-fingerprint"` + GlobalUA string `json:"global-ua"` KeepAliveInterval int `json:"keep-alive-interval"` } @@ -284,6 +285,7 @@ type RawConfig struct { TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"` FindProcessMode P.FindProcessMode `yaml:"find-process-mode" json:"find-process-mode"` GlobalClientFingerprint string `yaml:"global-client-fingerprint"` + GlobalUA string `yaml:"global-ua"` KeepAliveInterval int `yaml:"keep-alive-interval"` Sniffer RawSniffer `yaml:"sniffer"` @@ -370,6 +372,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { ProxyGroup: []map[string]any{}, TCPConcurrent: false, FindProcessMode: P.FindProcessStrict, + GlobalUA: "clash.meta", Tun: RawTun{ Enable: false, Device: "", @@ -571,6 +574,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) { C.GeoSiteUrl = cfg.GeoXUrl.GeoSite C.MmdbUrl = cfg.GeoXUrl.Mmdb C.GeodataMode = cfg.GeodataMode + C.UA = cfg.GlobalUA if cfg.KeepAliveInterval != 0 { N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second } @@ -617,6 +621,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) { FindProcessMode: cfg.FindProcessMode, EBpf: cfg.EBpf, GlobalClientFingerprint: cfg.GlobalClientFingerprint, + GlobalUA: cfg.GlobalUA, KeepAliveInterval: cfg.KeepAliveInterval, }, nil } diff --git a/constant/http.go b/constant/http.go new file mode 100644 index 00000000..8e321f6b --- /dev/null +++ b/constant/http.go @@ -0,0 +1,5 @@ +package constant + +var ( + UA string +) From 90acce7fa15884eabd8ee8f0783cdc26b9d1c6ab Mon Sep 17 00:00:00 2001 From: H1JK Date: Fri, 8 Sep 2023 22:58:59 +0800 Subject: [PATCH 29/66] feat: Add disable quic-go GSO to experimental --- config/config.go | 3 ++- docs/config.yaml | 7 +++++++ hub/executor/executor.go | 3 +++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 56c616d0..db021618 100644 --- a/config/config.go +++ b/config/config.go @@ -156,7 +156,8 @@ type Sniffer struct { // Experimental config type Experimental struct { - Fingerprints []string `yaml:"fingerprints"` + Fingerprints []string `yaml:"fingerprints"` + QUICGoDisableGSO bool `yaml:"quic-go-disable-gso"` } // Config is clash config manager diff --git a/docs/config.yaml b/docs/config.yaml index cd540311..ada57905 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -55,6 +55,13 @@ keep-alive-interval: 15 # routing-mark:6666 # 配置 fwmark 仅用于 Linux experimental: + # Disable quic-go GSO support. This may result in reduced performance on Linux. + # This is not recommended for most users. + # Only users encountering issues with quic-go's internal implementation should enable this, + # and they should disable it as soon as the issue is resolved. + # This field will be removed when quic-go fixes all their issues in GSO. + # This equivalent to the environment variable QUIC_GO_DISABLE_GSO=1. + #quic-go-disable-gso: true # 类似于 /etc/hosts, 仅支持配置单个 IP hosts: diff --git a/hub/executor/executor.go b/hub/executor/executor.go index f4364b7b..b840ba48 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -181,6 +181,9 @@ func updateListeners(general *config.General, listeners map[string]C.InboundList } func updateExperimental(c *config.Config) { + if c.Experimental.QUICGoDisableGSO { + _ = os.Setenv("QUIC_GO_DISABLE_GSO", "1") + } } func updateNTP(c *config.NTP) { From f241e1f81a53ffed8283c2fd1ab360ca40083318 Mon Sep 17 00:00:00 2001 From: H1JK Date: Sat, 9 Sep 2023 09:53:14 +0800 Subject: [PATCH 30/66] chore: Update dependencies --- go.mod | 26 ++++++++--------- go.sum | 53 ++++++++++++++++++----------------- test/go.mod | 27 +++++++++--------- test/go.sum | 81 +++++++++++++++++------------------------------------ 4 files changed, 79 insertions(+), 108 deletions(-) diff --git a/go.mod b/go.mod index aa37eff2..f75f58fd 100644 --- a/go.mod +++ b/go.mod @@ -13,13 +13,13 @@ require ( github.com/go-chi/render v1.0.3 github.com/gofrs/uuid/v5 v5.0.0 github.com/gorilla/websocket v1.5.0 - github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c + github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a github.com/jpillora/backoff v1.0.0 github.com/klauspost/cpuid/v2 v2.2.5 github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.7.2 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 - github.com/metacubex/quic-go v0.38.1-0.20230821081539-517fdb17fb28 + github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf github.com/metacubex/sing-shadowsocks v0.2.4 github.com/metacubex/sing-shadowsocks2 v0.1.3 github.com/metacubex/sing-tun v0.1.11 @@ -38,17 +38,17 @@ require ( github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f github.com/samber/lo v1.38.1 - github.com/shirou/gopsutil/v3 v3.23.7 + github.com/shirou/gopsutil/v3 v3.23.8 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.8.4 github.com/zhangyunhao116/fastrand v0.3.0 go.etcd.io/bbolt v1.3.7 go.uber.org/automaxprocs v1.5.3 - golang.org/x/crypto v0.12.0 - golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb - golang.org/x/net v0.14.0 + golang.org/x/crypto v0.13.0 + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 + golang.org/x/net v0.15.0 golang.org/x/sync v0.3.0 - golang.org/x/sys v0.11.0 + golang.org/x/sys v0.12.0 google.golang.org/protobuf v1.31.0 gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.2.1 @@ -84,7 +84,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/qtls-go1-20 v0.3.2 // indirect + github.com/quic-go/qtls-go1-20 v0.3.3 // indirect github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect @@ -92,16 +92,16 @@ require ( github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e // indirect - github.com/tklauser/go-sysconf v0.3.11 // indirect - github.com/tklauser/numcpus v0.6.0 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect - golang.org/x/mod v0.11.0 // indirect - golang.org/x/text v0.12.0 // indirect + golang.org/x/mod v0.12.0 // indirect + golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.9.1 // indirect + golang.org/x/tools v0.13.0 // indirect ) replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20230817143035-28d23f152579 diff --git a/go.sum b/go.sum index 530b615d..59d83450 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c h1:P/3mFnHCv1A/ej4m8pF5EB6FUt9qEL2Q9lfrcUNwCYs= -github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4= +github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a h1:S33o3djA1nPRd+d/bf7jbbXytXuK/EoXow7+aa76grQ= +github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a/go.mod h1:zmdm3sTSDP3vOOX3CEWRkkRHtKr1DxBx+J1OQFoDQQs= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= @@ -94,8 +94,8 @@ github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvO github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88= github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475 h1:qSEOvPPaMrWggFyFhFYGyMR8i1HKyhXjdi1QYUAa2ww= github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475/go.mod h1:wehEpqiogdeyncfhckJP5gD2LtBgJW0wnDC24mJ+8Jg= -github.com/metacubex/quic-go v0.38.1-0.20230821081539-517fdb17fb28 h1:ggSo4B1LDH9ZIROoUibxlrUpi7YCMri7HMXn4aNQkiM= -github.com/metacubex/quic-go v0.38.1-0.20230821081539-517fdb17fb28/go.mod h1:SthFvvoqgrEUgIxQXRnqdUAAYQECBavkhl7iA0geVd8= +github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf h1:hflzPbb2M+3uUOZEVO72MKd2R62xEermoVaNhJOzBR8= +github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf/go.mod h1:7RCcKJJk1DMeNQQNnYKS+7FqftqPfG031oP8jrYRMw8= github.com/metacubex/sing v0.0.0-20230817143035-28d23f152579 h1:dE1dBB6CTzNdSMFTE5OCHvzHLewiqiA1nhD+7egtvAc= github.com/metacubex/sing v0.0.0-20230817143035-28d23f152579/go.mod h1:9uOZwWkhT2Z2WldolLxX34s+1svAX4i4vvz5hy8u1MA= github.com/metacubex/sing-shadowsocks v0.2.4 h1:Gc99Z17JVif1PKKq1pjqhSmc2kvHUgk+AqxOstCzhQ0= @@ -136,8 +136,8 @@ github.com/puzpuzpuz/xsync/v2 v2.5.0 h1:2k4qrO/orvmEXZ3hmtHqIy9XaQtPTwzMZk1+iErp github.com/puzpuzpuz/xsync/v2 v2.5.0/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/qtls-go1-20 v0.3.2 h1:rRgN3WfnKbyik4dBV8A6girlJVxGand/d+jVKbQq5GI= -github.com/quic-go/qtls-go1-20 v0.3.2/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/quic-go/qtls-go1-20 v0.3.3 h1:17/glZSLI9P9fDAeyCHBFSWSqJcwx1byhLwP5eUIDCM= +github.com/quic-go/qtls-go1-20 v0.3.3/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 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= @@ -159,8 +159,8 @@ github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg= github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s= -github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4= -github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4= +github.com/shirou/gopsutil/v3 v3.23.8 h1:xnATPiybo6GgdRoC4YoGnxXZFRc3dqQTGi73oLvvBrE= +github.com/shirou/gopsutil/v3 v3.23.8/go.mod h1:7hmCaBn+2ZwaZOr6jmPBZDfawwMGuo1id3C6aM8EDqQ= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= @@ -183,10 +183,10 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= -github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= -github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= -github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= @@ -207,20 +207,20 @@ go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= 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.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb h1:mIKbk8weKhSeLH2GmUTrvx8CjkyJmnU1wFmg59CUjFA= -golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= 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.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= -golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= 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.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= @@ -239,24 +239,25 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.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.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/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-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.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 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= diff --git a/test/go.mod b/test/go.mod index 2d7ef893..5582dd04 100644 --- a/test/go.mod +++ b/test/go.mod @@ -8,7 +8,7 @@ require ( github.com/docker/go-connections v0.4.0 github.com/miekg/dns v1.1.55 github.com/stretchr/testify v1.8.4 - golang.org/x/net v0.14.0 + golang.org/x/net v0.15.0 ) replace github.com/Dreamacro/clash => ../ @@ -20,7 +20,6 @@ require ( github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect github.com/andybalholm/brotli v1.0.5 // indirect - github.com/beevik/ntp v1.3.0 // indirect github.com/cilium/ebpf v0.11.0 // indirect github.com/coreos/go-iptables v0.7.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -43,7 +42,7 @@ require ( github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect - github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c // indirect + github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a // indirect github.com/josharian/native v1.1.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/klauspost/compress v1.16.7 // indirect @@ -54,7 +53,7 @@ require ( github.com/mdlayher/socket v0.4.1 // indirect github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 // indirect github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475 // indirect - github.com/metacubex/quic-go v0.38.1-0.20230821081539-517fdb17fb28 // indirect + github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf // indirect github.com/metacubex/sing-shadowsocks v0.2.4 // indirect github.com/metacubex/sing-shadowsocks2 v0.1.3 // indirect github.com/metacubex/sing-tun v0.1.11 // indirect @@ -75,7 +74,7 @@ require ( github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/puzpuzpuz/xsync/v2 v2.5.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/qtls-go1-20 v0.3.2 // indirect + github.com/quic-go/qtls-go1-20 v0.3.3 // indirect github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a // indirect @@ -87,28 +86,28 @@ require ( github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f // indirect github.com/samber/lo v1.38.1 // indirect github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect - github.com/shirou/gopsutil/v3 v3.23.7 // indirect + github.com/shirou/gopsutil/v3 v3.23.8 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/tklauser/go-sysconf v0.3.11 // indirect - github.com/tklauser/numcpus v0.6.0 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect github.com/zhangyunhao116/fastrand v0.3.0 // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect go.etcd.io/bbolt v1.3.7 // indirect - golang.org/x/crypto v0.12.0 // indirect - golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb // indirect - golang.org/x/mod v0.11.0 // indirect + golang.org/x/crypto v0.13.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/mod v0.12.0 // indirect golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.11.0 // indirect - golang.org/x/text v0.12.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.9.1 // indirect + golang.org/x/tools v0.13.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.2.1 // indirect diff --git a/test/go.sum b/test/go.sum index 03971c26..609d2fcb 100644 --- a/test/go.sum +++ b/test/go.sum @@ -11,8 +11,6 @@ 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/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/beevik/ntp v1.3.0 h1:/w5VhpW5BGKS37vFm1p9oVk/t4HnnkKZAZIubHM6F7Q= -github.com/beevik/ntp v1.3.0/go.mod h1:vD6h1um4kzXpqmLTuu0cCLcC+NfvC0IC+ltmEDA8E78= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= 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= @@ -76,8 +74,8 @@ github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c h1:P/3mFnHCv1A/ej4m8pF5EB6FUt9qEL2Q9lfrcUNwCYs= -github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4= +github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a h1:S33o3djA1nPRd+d/bf7jbbXytXuK/EoXow7+aa76grQ= +github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a/go.mod h1:zmdm3sTSDP3vOOX3CEWRkkRHtKr1DxBx+J1OQFoDQQs= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= @@ -103,8 +101,8 @@ github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvO github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88= github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475 h1:qSEOvPPaMrWggFyFhFYGyMR8i1HKyhXjdi1QYUAa2ww= github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475/go.mod h1:wehEpqiogdeyncfhckJP5gD2LtBgJW0wnDC24mJ+8Jg= -github.com/metacubex/quic-go v0.38.1-0.20230821081539-517fdb17fb28 h1:ggSo4B1LDH9ZIROoUibxlrUpi7YCMri7HMXn4aNQkiM= -github.com/metacubex/quic-go v0.38.1-0.20230821081539-517fdb17fb28/go.mod h1:SthFvvoqgrEUgIxQXRnqdUAAYQECBavkhl7iA0geVd8= +github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf h1:hflzPbb2M+3uUOZEVO72MKd2R62xEermoVaNhJOzBR8= +github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf/go.mod h1:7RCcKJJk1DMeNQQNnYKS+7FqftqPfG031oP8jrYRMw8= github.com/metacubex/sing-shadowsocks v0.2.4 h1:Gc99Z17JVif1PKKq1pjqhSmc2kvHUgk+AqxOstCzhQ0= github.com/metacubex/sing-shadowsocks v0.2.4/go.mod h1:w9qoEZSh9aKeXSLXHe0DGbG2UE9/2VlLGwukzQZ7byI= github.com/metacubex/sing-shadowsocks2 v0.1.3 h1:nZvH+4jQXZ92NeNdR9fXaUGTPNJPt6u0nkcuh/NEt5Y= @@ -151,8 +149,8 @@ github.com/puzpuzpuz/xsync/v2 v2.5.0 h1:2k4qrO/orvmEXZ3hmtHqIy9XaQtPTwzMZk1+iErp github.com/puzpuzpuz/xsync/v2 v2.5.0/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/qtls-go1-20 v0.3.2 h1:rRgN3WfnKbyik4dBV8A6girlJVxGand/d+jVKbQq5GI= -github.com/quic-go/qtls-go1-20 v0.3.2/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/quic-go/qtls-go1-20 v0.3.3 h1:17/glZSLI9P9fDAeyCHBFSWSqJcwx1byhLwP5eUIDCM= +github.com/quic-go/qtls-go1-20 v0.3.3/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 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= @@ -178,8 +176,8 @@ github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg= github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s= -github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4= -github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4= +github.com/shirou/gopsutil/v3 v3.23.8 h1:xnATPiybo6GgdRoC4YoGnxXZFRc3dqQTGi73oLvvBrE= +github.com/shirou/gopsutil/v3 v3.23.8/go.mod h1:7hmCaBn+2ZwaZOr6jmPBZDfawwMGuo1id3C6aM8EDqQ= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= @@ -202,10 +200,10 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= -github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= -github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= -github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= @@ -216,7 +214,6 @@ github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zhangyunhao116/fastrand v0.3.0 h1:7bwe124xcckPulX6fxtr2lFdO2KQqaefdtbk+mqO/Ig= @@ -228,39 +225,28 @@ go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= 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.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb h1:mIKbk8weKhSeLH2GmUTrvx8CjkyJmnU1wFmg59CUjFA= -golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= 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.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= -golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -275,33 +261,20 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/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.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= 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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -310,10 +283,8 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 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= From af99b52527535aa578434c61b25d945020a7e467 Mon Sep 17 00:00:00 2001 From: kunish Date: Sat, 9 Sep 2023 13:06:49 +0800 Subject: [PATCH 31/66] docs(README): update dashboard section --- README.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 9339a935..51cecc2d 100644 --- a/README.md +++ b/README.md @@ -24,13 +24,22 @@ - VMess, Shadowsocks, Trojan, Snell protocol support for remote connections - Built-in DNS server that aims to minimize DNS pollution attack impact, supports DoH/DoT upstream and fake IP. - Rules based off domains, GEOIP, IPCIDR or Process to forward packets to different nodes -- Remote groups allow users to implement powerful rules. Supports automatic fallback, load balancing or auto select node based off latency -- Remote providers, allowing users to get node lists remotely instead of hardcoding in config +- Remote groups allow users to implement powerful rules. Supports automatic fallback, load balancing or auto select node + based off latency +- Remote providers, allowing users to get node lists remotely instead of hard-coding in config - Netfilter TCP redirecting. Deploy Clash on your Internet gateway with `iptables`. - Comprehensive HTTP RESTful API controller +## Dashboard + +We made an official web dashboard providing first class support for this project, check it out +at [metacubexd](https://github.com/MetaCubeX/metacubexd) + ## Wiki -Configuration examples can be found at [/docs/config.yaml](https://github.com/MetaCubeX/Clash.Meta/blob/Alpha/docs/config.yaml), while documentation can be found [Clash.Meta Wiki](https://clash-meta.wiki). + +Configuration examples can be found +at [/docs/config.yaml](https://github.com/MetaCubeX/Clash.Meta/blob/Alpha/docs/config.yaml), while documentation can be +found [Clash.Meta Wiki](https://clash-meta.wiki). ## Build @@ -43,7 +52,7 @@ git clone https://github.com/MetaCubeX/Clash.Meta.git cd Clash.Meta && go mod download ``` -If you can't visit github,you should set proxy first: +If you can't visit GitHub, you should set proxy first: ```shell go env -w GOPROXY=https://goproxy.io,direct @@ -324,36 +333,27 @@ ExecStart=/usr/local/bin/Clash-Meta -d /etc/Clash-Meta WantedBy=multi-user.target ``` -Launch clashd on system startup with: +Launch clash-meta daemon on system startup with: ```shell $ systemctl enable Clash-Meta ``` -Launch clashd immediately with: +Launch clash-meta daemon immediately with: ```shell $ systemctl start Clash-Meta ``` -### Display Process name - -Clash add field `Process` to `Metadata` and prepare to get process name for Restful API `GET /connections`. - -To display process name in GUI please use [Razord-meta](https://github.com/MetaCubeX/Razord-meta). - -### Dashboard - -We also made a custom fork of yacd provide better support for this project, check it out at [Yacd-meta](https://github.com/MetaCubeX/Yacd-meta) - ## Development -If you want to build an application that uses clash as a library, check out the +If you want to build an application that uses clash as a library, check out the [GitHub Wiki](https://github.com/Dreamacro/clash/wiki/use-clash-as-a-library) ## Debugging -Check [wiki](https://github.com/MetaCubeX/Clash.Meta/wiki/How-to-use-debug-api) to get an instruction on using debug API. +Check [wiki](https://github.com/MetaCubeX/Clash.Meta/wiki/How-to-use-debug-api) to get an instruction on using debug +API. ## Credits From c3d72f6883d991a9dbcbdb9e54dcd838864a07f1 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+larvan2@users.noreply.github.com> Date: Fri, 15 Sep 2023 23:25:56 +0800 Subject: [PATCH 32/66] feat: download/upgrade XD to external-ui --- config/{updateGeo.go => update_geo.go} | 22 ----- config/update_xd.go | 123 +++++++++++++++++++++++++ config/utils.go | 22 +++++ constant/path.go | 1 + hub/route/server.go | 1 + hub/route/upgrade.go | 17 ++++ hub/updater/updater.go | 2 +- 7 files changed, 165 insertions(+), 23 deletions(-) rename config/{updateGeo.go => update_geo.go} (75%) create mode 100644 config/update_xd.go diff --git a/config/updateGeo.go b/config/update_geo.go similarity index 75% rename from config/updateGeo.go rename to config/update_geo.go index b75d3184..07f211e4 100644 --- a/config/updateGeo.go +++ b/config/update_geo.go @@ -1,17 +1,11 @@ package config import ( - "context" "fmt" - "io" - "net/http" - "os" "runtime" - "time" "github.com/Dreamacro/clash/component/geodata" _ "github.com/Dreamacro/clash/component/geodata/standard" - clashHttp "github.com/Dreamacro/clash/component/http" C "github.com/Dreamacro/clash/constant" "github.com/oschwald/maxminddb-golang" @@ -72,19 +66,3 @@ func UpdateGeoDatabases() error { return nil } - -func downloadForBytes(url string) ([]byte, error) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) - defer cancel() - resp, err := clashHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - return io.ReadAll(resp.Body) -} - -func saveFile(bytes []byte, path string) error { - return os.WriteFile(path, bytes, 0o644) -} diff --git a/config/update_xd.go b/config/update_xd.go new file mode 100644 index 00000000..696768b4 --- /dev/null +++ b/config/update_xd.go @@ -0,0 +1,123 @@ +package config + +import ( + "archive/zip" + "fmt" + "io" + "os" + "path" + "path/filepath" + "strings" + "sync" + + C "github.com/Dreamacro/clash/constant" +) + +const xdURL = "https://codeload.github.com/MetaCubeX/metacubexd/zip/refs/heads/gh-pages" + +var xdMutex sync.Mutex + +func UpdateXD() error { + xdMutex.Lock() + defer xdMutex.Unlock() + + err := cleanup(C.UIPath) + if err != nil { + return fmt.Errorf("cleanup exist file error: %w", err) + } + + data, err := downloadForBytes(xdURL) + if err != nil { + return fmt.Errorf("can't download XD file: %w", err) + } + + saved := path.Join(C.UIPath, "xd.zip") + if saveFile(data, saved) != nil { + return fmt.Errorf("can't save XD zip file: %w", err) + } + defer os.Remove(saved) + + err = unzip(saved, C.UIPath) + if err != nil { + return fmt.Errorf("can't extract XD zip file: %w", err) + } + + err = os.Rename(path.Join(C.UIPath, "metacubexd-gh-pages"), path.Join(C.UIPath, "xd")) + if err != nil { + return fmt.Errorf("can't rename folder: %w", err) + } + return nil +} + +func unzip(src, dest string) error { + r, err := zip.OpenReader(src) + if err != nil { + return err + } + defer r.Close() + + for _, f := range r.File { + fpath := filepath.Join(dest, f.Name) + + if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) { + return fmt.Errorf("invalid file path: %s", fpath) + } + + if f.FileInfo().IsDir() { + os.MkdirAll(fpath, os.ModePerm) + continue + } + + if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil { + return err + } + + outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + if err != nil { + return err + } + + rc, err := f.Open() + if err != nil { + return err + } + + _, err = io.Copy(outFile, rc) + + outFile.Close() + rc.Close() + + if err != nil { + return err + } + } + return nil +} + +func cleanup(root string) error { + return filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if path == root { + // skip root itself + return nil + } + if info.IsDir() { + if err := os.RemoveAll(path); err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + } else { + if err := os.Remove(path); err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + } + return nil + }) +} diff --git a/config/utils.go b/config/utils.go index 799082c4..1fa54634 100644 --- a/config/utils.go +++ b/config/utils.go @@ -1,15 +1,37 @@ package config import ( + "context" "fmt" + "io" "net" + "net/http" "net/netip" + "os" "strings" + "time" "github.com/Dreamacro/clash/adapter/outboundgroup" "github.com/Dreamacro/clash/common/structure" + clashHttp "github.com/Dreamacro/clash/component/http" ) +func downloadForBytes(url string) ([]byte, error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) + defer cancel() + resp, err := clashHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + return io.ReadAll(resp.Body) +} + +func saveFile(bytes []byte, path string) error { + return os.WriteFile(path, bytes, 0o644) +} + func trimArr(arr []string) (r []string) { for _, e := range arr { r = append(r, strings.Trim(e, " ")) diff --git a/constant/path.go b/constant/path.go index d7477e0e..e595d920 100644 --- a/constant/path.go +++ b/constant/path.go @@ -15,6 +15,7 @@ const Name = "clash" var ( GeositeName = "GeoSite.dat" GeoipName = "GeoIP.dat" + UIPath = "" ) // Path is used to get the configuration path diff --git a/hub/route/server.go b/hub/route/server.go index d2fecd05..3df6196a 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -49,6 +49,7 @@ type Memory struct { func SetUIPath(path string) { uiPath = C.Path.Resolve(path) + C.UIPath = uiPath } func Start(addr string, tlsAddr string, secret string, diff --git a/hub/route/upgrade.go b/hub/route/upgrade.go index 5e75bc8b..ef5dffaf 100644 --- a/hub/route/upgrade.go +++ b/hub/route/upgrade.go @@ -5,6 +5,7 @@ import ( "net/http" "os" + "github.com/Dreamacro/clash/config" "github.com/Dreamacro/clash/hub/updater" "github.com/Dreamacro/clash/log" @@ -15,6 +16,7 @@ import ( func upgradeRouter() http.Handler { r := chi.NewRouter() r.Post("/", upgrade) + r.Post("/xd", updateXD) return r } @@ -43,3 +45,18 @@ func upgrade(w http.ResponseWriter, r *http.Request) { go restartExecutable(execPath) } + +func updateXD(w http.ResponseWriter, r *http.Request) { + err := config.UpdateXD() + if err != nil { + log.Warnln("%s", err) + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, newError(fmt.Sprintf("%s", err))) + return + } + + render.JSON(w, r, render.M{"status": "ok"}) + if f, ok := w.(http.Flusher); ok { + f.Flush() + } +} diff --git a/hub/updater/updater.go b/hub/updater/updater.go index 90b14c27..1a930c03 100644 --- a/hub/updater/updater.go +++ b/hub/updater/updater.go @@ -32,7 +32,7 @@ var ( workDir string // mu protects all fields below. - mu sync.RWMutex + mu sync.Mutex currentExeName string // 当前可执行文件 updateDir string // 更新目录 From fa49fd7ba2efbae8cb9b00bf5cf786ea940037d2 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+larvan2@users.noreply.github.com> Date: Fri, 15 Sep 2023 23:15:05 +0800 Subject: [PATCH 33/66] chore: use cmp in go 1.21 Co-authored-by: H1JK --- transport/hysteria/pmtud_fix/avail.go | 3 +-- transport/hysteria/pmtud_fix/unavail.go | 3 +-- transport/tuic/congestion/minmax.go | 16 ---------------- transport/tuic/congestion/minmax_go120.go | 19 +++++++++++++++++++ transport/tuic/congestion/minmax_go121.go | 13 +++++++++++++ 5 files changed, 34 insertions(+), 20 deletions(-) create mode 100644 transport/tuic/congestion/minmax_go120.go create mode 100644 transport/tuic/congestion/minmax_go121.go diff --git a/transport/hysteria/pmtud_fix/avail.go b/transport/hysteria/pmtud_fix/avail.go index 2f2bce83..af248f5c 100644 --- a/transport/hysteria/pmtud_fix/avail.go +++ b/transport/hysteria/pmtud_fix/avail.go @@ -1,5 +1,4 @@ -//go:build linux || windows -// +build linux windows +//go:build linux || windows || darwin package pmtud_fix diff --git a/transport/hysteria/pmtud_fix/unavail.go b/transport/hysteria/pmtud_fix/unavail.go index 0eeb83df..35b849d2 100644 --- a/transport/hysteria/pmtud_fix/unavail.go +++ b/transport/hysteria/pmtud_fix/unavail.go @@ -1,5 +1,4 @@ -//go:build !linux && !windows -// +build !linux,!windows +//go:build !linux && !windows && !darwin package pmtud_fix diff --git a/transport/tuic/congestion/minmax.go b/transport/tuic/congestion/minmax.go index ed75072e..0a8f4ad4 100644 --- a/transport/tuic/congestion/minmax.go +++ b/transport/tuic/congestion/minmax.go @@ -3,27 +3,11 @@ package congestion import ( "math" "time" - - "golang.org/x/exp/constraints" ) // InfDuration is a duration of infinite length const InfDuration = time.Duration(math.MaxInt64) -func Max[T constraints.Ordered](a, b T) T { - if a < b { - return b - } - return a -} - -func Min[T constraints.Ordered](a, b T) T { - if a < b { - return a - } - return b -} - // MinNonZeroDuration return the minimum duration that's not zero. func MinNonZeroDuration(a, b time.Duration) time.Duration { if a == 0 { diff --git a/transport/tuic/congestion/minmax_go120.go b/transport/tuic/congestion/minmax_go120.go new file mode 100644 index 00000000..1266edbc --- /dev/null +++ b/transport/tuic/congestion/minmax_go120.go @@ -0,0 +1,19 @@ +//go:build !go1.21 + +package congestion + +import "golang.org/x/exp/constraints" + +func Max[T constraints.Ordered](a, b T) T { + if a < b { + return b + } + return a +} + +func Min[T constraints.Ordered](a, b T) T { + if a < b { + return a + } + return b +} diff --git a/transport/tuic/congestion/minmax_go121.go b/transport/tuic/congestion/minmax_go121.go new file mode 100644 index 00000000..65b06726 --- /dev/null +++ b/transport/tuic/congestion/minmax_go121.go @@ -0,0 +1,13 @@ +//go:build go1.21 + +package congestion + +import "cmp" + +func Max[T cmp.Ordered](a, b T) T { + return max(a, b) +} + +func Min[T cmp.Ordered](a, b T) T { + return min(a, b) +} From 2d3b9364bf0e6867c34d99d98b464cd9c733e340 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sat, 16 Sep 2023 12:29:49 +0800 Subject: [PATCH 34/66] fix: caceh dns result --- dns/resolver.go | 1 + 1 file changed, 1 insertion(+) diff --git a/dns/resolver.go b/dns/resolver.go index 8f41a44e..89e36214 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -200,6 +200,7 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M isIPReq := isIPRequest(q) if isIPReq { + cache=true return r.ipExchange(ctx, m) } From 33d41338ef00ff20e444e03f3d9ee7b60fc787a4 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sun, 17 Sep 2023 17:05:13 +0800 Subject: [PATCH 35/66] migration: go 1.21 --- adapter/adapter.go | 12 ++++++------ common/observable/observable_test.go | 6 +++--- go.mod | 2 +- hub/updater/limitedreader.go | 13 +------------ transport/tuic/congestion/cubic.go | 2 +- transport/tuic/congestion/cubic_sender.go | 4 ++-- .../tuic/congestion/hybrid_slow_start.go | 4 ++-- transport/tuic/congestion/minmax.go | 2 +- transport/tuic/congestion/minmax_go120.go | 19 ------------------- transport/tuic/congestion/minmax_go121.go | 13 ------------- transport/tuic/congestion/pacer.go | 6 +++--- 11 files changed, 20 insertions(+), 63 deletions(-) delete mode 100644 transport/tuic/congestion/minmax_go120.go delete mode 100644 transport/tuic/congestion/minmax_go121.go diff --git a/adapter/adapter.go b/adapter/adapter.go index 13f7f06f..abef3d89 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -136,21 +136,21 @@ func (p *Proxy) ExtraDelayHistory() map[string][]C.DelayHistory { // LastDelay return last history record. if proxy is not alive, return the max value of uint16. // implements C.Proxy func (p *Proxy) LastDelay() (delay uint16) { - var max uint16 = 0xffff + var maxDelay uint16 = 0xffff if !p.alive.Load() { - return max + return maxDelay } history := p.history.Last() if history.Delay == 0 { - return max + return maxDelay } return history.Delay } // LastDelayForTestUrl implements C.Proxy func (p *Proxy) LastDelayForTestUrl(url string) (delay uint16) { - var max uint16 = 0xffff + var maxDelay uint16 = 0xffff alive := p.alive.Load() history := p.history.Last() @@ -161,11 +161,11 @@ func (p *Proxy) LastDelayForTestUrl(url string) (delay uint16) { } if !alive { - return max + return maxDelay } if history.Delay == 0 { - return max + return maxDelay } return history.Delay } diff --git a/common/observable/observable_test.go b/common/observable/observable_test.go index 5a02273d..3f0b0e7f 100644 --- a/common/observable/observable_test.go +++ b/common/observable/observable_test.go @@ -85,16 +85,16 @@ func TestObservable_UnSubscribeWithNotExistSubscription(t *testing.T) { func TestObservable_SubscribeGoroutineLeak(t *testing.T) { iter := iterator[int]([]int{1, 2, 3, 4, 5}) src := NewObservable[int](iter) - max := 100 + total := 100 var list []Subscription[int] - for i := 0; i < max; i++ { + for i := 0; i < total; i++ { ch, _ := src.Subscribe() list = append(list, ch) } var wg sync.WaitGroup - wg.Add(max) + wg.Add(total) waitCh := func(ch <-chan int) { for range ch { } diff --git a/go.mod b/go.mod index f75f58fd..ca98937b 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/Dreamacro/clash -go 1.20 +go 1.21 require ( github.com/3andne/restls-client-go v0.1.6 diff --git a/hub/updater/limitedreader.go b/hub/updater/limitedreader.go index c31db601..52d7e969 100644 --- a/hub/updater/limitedreader.go +++ b/hub/updater/limitedreader.go @@ -3,8 +3,6 @@ package updater import ( "fmt" "io" - - "golang.org/x/exp/constraints" ) // LimitReachedError records the limit and the operation that caused it. @@ -35,7 +33,7 @@ func (lr *limitedReader) Read(p []byte) (n int, err error) { } } - p = p[:Min(lr.n, int64(len(p)))] + p = p[:min(lr.n, int64(len(p)))] n, err = lr.r.Read(p) lr.n -= int64(n) @@ -56,12 +54,3 @@ func LimitReader(r io.Reader, n int64) (limited io.Reader, err error) { n: n, }, nil } - -// Min returns the smaller of x or y. -func Min[T constraints.Integer | ~string](x, y T) (res T) { - if x < y { - return x - } - - return y -} diff --git a/transport/tuic/congestion/cubic.go b/transport/tuic/congestion/cubic.go index dd491a32..7dbe2b06 100644 --- a/transport/tuic/congestion/cubic.go +++ b/transport/tuic/congestion/cubic.go @@ -186,7 +186,7 @@ func (c *Cubic) CongestionWindowAfterAck( targetCongestionWindow = c.originPointCongestionWindow - deltaCongestionWindow } // Limit the CWND increase to half the acked bytes. - targetCongestionWindow = Min(targetCongestionWindow, currentCongestionWindow+c.ackedBytesCount/2) + targetCongestionWindow = min(targetCongestionWindow, currentCongestionWindow+c.ackedBytesCount/2) // Increase the window by approximately Alpha * 1 MSS of bytes every // time we ack an estimated tcp window of bytes. For small diff --git a/transport/tuic/congestion/cubic_sender.go b/transport/tuic/congestion/cubic_sender.go index ca20b420..a467b8c9 100644 --- a/transport/tuic/congestion/cubic_sender.go +++ b/transport/tuic/congestion/cubic_sender.go @@ -177,7 +177,7 @@ func (c *cubicSender) OnPacketAcked( priorInFlight congestion.ByteCount, eventTime time.Time, ) { - c.largestAckedPacketNumber = Max(ackedPacketNumber, c.largestAckedPacketNumber) + c.largestAckedPacketNumber = max(ackedPacketNumber, c.largestAckedPacketNumber) if c.InRecovery() { return } @@ -245,7 +245,7 @@ func (c *cubicSender) maybeIncreaseCwnd( c.numAckedPackets = 0 } } else { - c.congestionWindow = Min(c.maxCongestionWindow(), c.cubic.CongestionWindowAfterAck(ackedBytes, c.congestionWindow, c.rttStats.MinRTT(), eventTime)) + c.congestionWindow = min(c.maxCongestionWindow(), c.cubic.CongestionWindowAfterAck(ackedBytes, c.congestionWindow, c.rttStats.MinRTT(), eventTime)) } } diff --git a/transport/tuic/congestion/hybrid_slow_start.go b/transport/tuic/congestion/hybrid_slow_start.go index 7586774e..e0b42e53 100644 --- a/transport/tuic/congestion/hybrid_slow_start.go +++ b/transport/tuic/congestion/hybrid_slow_start.go @@ -74,8 +74,8 @@ func (s *HybridSlowStart) ShouldExitSlowStart(latestRTT time.Duration, minRTT ti // Divide minRTT by 8 to get a rtt increase threshold for exiting. minRTTincreaseThresholdUs := int64(minRTT / time.Microsecond >> hybridStartDelayFactorExp) // Ensure the rtt threshold is never less than 2ms or more than 16ms. - minRTTincreaseThresholdUs = Min(minRTTincreaseThresholdUs, hybridStartDelayMaxThresholdUs) - minRTTincreaseThreshold := time.Duration(Max(minRTTincreaseThresholdUs, hybridStartDelayMinThresholdUs)) * time.Microsecond + minRTTincreaseThresholdUs = min(minRTTincreaseThresholdUs, hybridStartDelayMaxThresholdUs) + minRTTincreaseThreshold := time.Duration(max(minRTTincreaseThresholdUs, hybridStartDelayMinThresholdUs)) * time.Microsecond if s.currentMinRTT > (minRTT + minRTTincreaseThreshold) { s.hystartFound = true diff --git a/transport/tuic/congestion/minmax.go b/transport/tuic/congestion/minmax.go index 0a8f4ad4..50667766 100644 --- a/transport/tuic/congestion/minmax.go +++ b/transport/tuic/congestion/minmax.go @@ -16,7 +16,7 @@ func MinNonZeroDuration(a, b time.Duration) time.Duration { if b == 0 { return a } - return Min(a, b) + return min(a, b) } // AbsDuration returns the absolute value of a time duration diff --git a/transport/tuic/congestion/minmax_go120.go b/transport/tuic/congestion/minmax_go120.go deleted file mode 100644 index 1266edbc..00000000 --- a/transport/tuic/congestion/minmax_go120.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build !go1.21 - -package congestion - -import "golang.org/x/exp/constraints" - -func Max[T constraints.Ordered](a, b T) T { - if a < b { - return b - } - return a -} - -func Min[T constraints.Ordered](a, b T) T { - if a < b { - return a - } - return b -} diff --git a/transport/tuic/congestion/minmax_go121.go b/transport/tuic/congestion/minmax_go121.go deleted file mode 100644 index 65b06726..00000000 --- a/transport/tuic/congestion/minmax_go121.go +++ /dev/null @@ -1,13 +0,0 @@ -//go:build go1.21 - -package congestion - -import "cmp" - -func Max[T cmp.Ordered](a, b T) T { - return max(a, b) -} - -func Min[T cmp.Ordered](a, b T) T { - return min(a, b) -} diff --git a/transport/tuic/congestion/pacer.go b/transport/tuic/congestion/pacer.go index f60ef5fe..9e85107d 100644 --- a/transport/tuic/congestion/pacer.go +++ b/transport/tuic/congestion/pacer.go @@ -52,11 +52,11 @@ func (p *pacer) Budget(now time.Time) congestion.ByteCount { return p.maxBurstSize() } budget := p.budgetAtLastSent + (congestion.ByteCount(p.getAdjustedBandwidth())*congestion.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9 - return Min(p.maxBurstSize(), budget) + return min(p.maxBurstSize(), budget) } func (p *pacer) maxBurstSize() congestion.ByteCount { - return Max( + return max( congestion.ByteCount(uint64((MinPacingDelay+TimerGranularity).Nanoseconds())*p.getAdjustedBandwidth())/1e9, maxBurstSizePackets*p.maxDatagramSize, ) @@ -68,7 +68,7 @@ func (p *pacer) TimeUntilSend() time.Time { if p.budgetAtLastSent >= p.maxDatagramSize { return time.Time{} } - return p.lastSentTime.Add(Max( + return p.lastSentTime.Add(max( MinPacingDelay, time.Duration(math.Ceil(float64(p.maxDatagramSize-p.budgetAtLastSent)*1e9/float64(p.getAdjustedBandwidth())))*time.Nanosecond, )) From 6a5a94f48f87bf30d2a31877596566b8b21c2001 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sun, 17 Sep 2023 17:18:35 +0800 Subject: [PATCH 36/66] chore: DNS cache policy follow upstream --- dns/util.go | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/dns/util.go b/dns/util.go index 77f677cb..c9d48a49 100644 --- a/dns/util.go +++ b/dns/util.go @@ -30,9 +30,13 @@ const ( ) func minimalTTL(records []D.RR) uint32 { - return lo.MinBy(records, func(r1 D.RR, r2 D.RR) bool { + minObj := lo.MinBy(records, func(r1 D.RR, r2 D.RR) bool { return r1.Header().Ttl < r2.Header().Ttl - }).Header().Ttl + }) + if minObj != nil { + return minObj.Header().Ttl + } + return 0 } func updateTTL(records []D.RR, ttl uint32) { @@ -46,27 +50,25 @@ func updateTTL(records []D.RR, ttl uint32) { } func putMsgToCache(c *cache.LruCache[string, *D.Msg], key string, msg *D.Msg) { - // skip dns cache for acme challenge - if len(msg.Question) != 0 { - if q := msg.Question[0]; q.Qtype == D.TypeTXT && strings.HasPrefix(q.Name, "_acme-challenge") { - log.Debugln("[DNS] dns cache ignored because of acme challenge for: %s", q.Name) + putMsgToCacheWithExpire(c, key, msg, 0) +} + +func putMsgToCacheWithExpire(c *cache.LruCache[string, *D.Msg], key string, msg *D.Msg, sec uint32) { + if sec == 0 { + if sec = minimalTTL(msg.Answer); sec == 0 { + if sec = minimalTTL(msg.Ns); sec == 0 { + sec = minimalTTL(msg.Extra) + } + } + if sec == 0 { return } - } - var ttl uint32 - switch { - case len(msg.Answer) != 0: - ttl = minimalTTL(msg.Answer) - case len(msg.Ns) != 0: - ttl = minimalTTL(msg.Ns) - case len(msg.Extra) != 0: - ttl = minimalTTL(msg.Extra) - default: - log.Debugln("[DNS] response msg empty: %#v", msg) - return + + sec = max(sec, 120) // at least 2 minutes to cache + } - c.SetWithExpire(key, msg.Copy(), time.Now().Add(time.Second*time.Duration(ttl))) + c.SetWithExpire(key, msg.Copy(), time.Now().Add(time.Duration(sec)*time.Second)) } func setMsgTTL(msg *D.Msg, ttl uint32) { From 7c21768e9956745523b10265f250c54512c5c065 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Mon, 18 Sep 2023 19:21:30 +0800 Subject: [PATCH 37/66] feat: update external-ui --- config/config.go | 35 ++++++++++-- config/{update_xd.go => update_ui.go} | 79 +++++++++++++-------------- constant/path.go | 1 - docs/config.yaml | 6 +- hub/route/server.go | 1 - hub/route/upgrade.go | 6 +- 6 files changed, 75 insertions(+), 53 deletions(-) rename config/{update_xd.go => update_ui.go} (54%) diff --git a/config/config.go b/config/config.go index db021618..a2e64026 100644 --- a/config/config.go +++ b/config/config.go @@ -8,6 +8,8 @@ import ( "net/netip" "net/url" "os" + "path" + "path/filepath" "regexp" "strings" "time" @@ -277,6 +279,8 @@ type RawConfig struct { ExternalController string `yaml:"external-controller"` ExternalControllerTLS string `yaml:"external-controller-tls"` ExternalUI string `yaml:"external-ui"` + ExternalUIURL string `yaml:"external-ui-url" json:"external-ui-url"` + ExternalUIName string `yaml:"external-ui-name" json:"external-ui-name"` Secret string `yaml:"secret"` Interface string `yaml:"interface-name"` RoutingMark int `yaml:"routing-mark"` @@ -569,7 +573,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } func parseGeneral(cfg *RawConfig) (*General, error) { - externalUI := cfg.ExternalUI geodata.SetLoader(cfg.GeodataLoader) C.GeoIpUrl = cfg.GeoXUrl.GeoIp C.GeoSiteUrl = cfg.GeoXUrl.GeoSite @@ -580,14 +583,34 @@ func parseGeneral(cfg *RawConfig) (*General, error) { N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second } - log.Debugln("TCP Keep Alive Interval set %+v", N.KeepAliveInterval) + if cfg.ExternalUIURL != "" { + ExternalUIURL = cfg.ExternalUIURL + } + ExternalUIPath = cfg.ExternalUI // checkout externalUI exist - if externalUI != "" { - externalUI = C.Path.Resolve(externalUI) - if _, err := os.Stat(externalUI); os.IsNotExist(err) { - return nil, fmt.Errorf("external-ui: %s not exist", externalUI) + if ExternalUIPath != "" { + ExternalUIPath = C.Path.Resolve(ExternalUIPath) + if _, err := os.Stat(ExternalUIPath); os.IsNotExist(err) { + defaultUIpath := path.Join(C.Path.HomeDir(), "ui") + log.Warnln("external-ui: %s does not exist, creating folder in %s", ExternalUIPath, defaultUIpath) + if err := os.MkdirAll(defaultUIpath, os.ModePerm); err != nil { + return nil, err + } + ExternalUIPath = defaultUIpath + cfg.ExternalUI = defaultUIpath } } + // checkout UIpath/name exist + if cfg.ExternalUIName != "" { + ExternalUIName = cfg.ExternalUIName + ExternalUIFolder = filepath.Clean(path.Join(ExternalUIPath, cfg.ExternalUIName)) + if _, err := os.Stat(ExternalUIPath); os.IsNotExist(err) { + if err := os.MkdirAll(ExternalUIPath, os.ModePerm); err != nil { + return nil, err + } + } + } + cfg.Tun.RedirectToTun = cfg.EBpf.RedirectToTun return &General{ Inbound: Inbound{ diff --git a/config/update_xd.go b/config/update_ui.go similarity index 54% rename from config/update_xd.go rename to config/update_ui.go index 696768b4..81b5bb03 100644 --- a/config/update_xd.go +++ b/config/update_ui.go @@ -9,112 +9,109 @@ import ( "path/filepath" "strings" "sync" - - C "github.com/Dreamacro/clash/constant" ) -const xdURL = "https://codeload.github.com/MetaCubeX/metacubexd/zip/refs/heads/gh-pages" +var ( + ExternalUIURL string + ExternalUIPath string + ExternalUIFolder string + ExternalUIName string +) var xdMutex sync.Mutex -func UpdateXD() error { +func UpdateUI() error { xdMutex.Lock() defer xdMutex.Unlock() - err := cleanup(C.UIPath) - if err != nil { - return fmt.Errorf("cleanup exist file error: %w", err) + if ExternalUIPath == "" || ExternalUIFolder == "" || ExternalUIName == "" { + return fmt.Errorf("ExternalUI configure incomplete") } - data, err := downloadForBytes(xdURL) + err := cleanup(ExternalUIFolder) if err != nil { - return fmt.Errorf("can't download XD file: %w", err) + if !os.IsNotExist(err) { + return fmt.Errorf("cleanup exist file error: %w", err) + } } - saved := path.Join(C.UIPath, "xd.zip") + data, err := downloadForBytes(ExternalUIURL) + if err != nil { + return fmt.Errorf("can't download file: %w", err) + } + + saved := path.Join(ExternalUIPath, "download.zip") if saveFile(data, saved) != nil { - return fmt.Errorf("can't save XD zip file: %w", err) + return fmt.Errorf("can't save zip file: %w", err) } defer os.Remove(saved) - err = unzip(saved, C.UIPath) + unzipFolder, err := unzip(saved, ExternalUIPath) if err != nil { - return fmt.Errorf("can't extract XD zip file: %w", err) + return fmt.Errorf("can't extract zip file: %w", err) } - err = os.Rename(path.Join(C.UIPath, "metacubexd-gh-pages"), path.Join(C.UIPath, "xd")) + err = os.Rename(unzipFolder, ExternalUIFolder) if err != nil { return fmt.Errorf("can't rename folder: %w", err) } return nil } -func unzip(src, dest string) error { +func unzip(src, dest string) (string, error) { r, err := zip.OpenReader(src) if err != nil { - return err + return "", err } defer r.Close() - + var extractedFolder string for _, f := range r.File { fpath := filepath.Join(dest, f.Name) - if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) { - return fmt.Errorf("invalid file path: %s", fpath) + return "", fmt.Errorf("invalid file path: %s", fpath) } - if f.FileInfo().IsDir() { os.MkdirAll(fpath, os.ModePerm) continue } - if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil { - return err + return "", err } - outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) if err != nil { - return err + return "", err } - rc, err := f.Open() if err != nil { - return err + return "", err } - _, err = io.Copy(outFile, rc) - outFile.Close() rc.Close() - if err != nil { - return err + return "", err + } + if extractedFolder == "" { + extractedFolder = filepath.Dir(fpath) } } - return nil + return extractedFolder, nil } func cleanup(root string) error { + if _, err := os.Stat(root); os.IsNotExist(err) { + return nil + } return filepath.Walk(root, func(path string, info os.FileInfo, err error) error { if err != nil { return err } - if path == root { - // skip root itself - return nil - } if info.IsDir() { if err := os.RemoveAll(path); err != nil { - if os.IsNotExist(err) { - return nil - } return err } } else { if err := os.Remove(path); err != nil { - if os.IsNotExist(err) { - return nil - } return err } } diff --git a/constant/path.go b/constant/path.go index e595d920..d7477e0e 100644 --- a/constant/path.go +++ b/constant/path.go @@ -15,7 +15,6 @@ const Name = "clash" var ( GeositeName = "GeoSite.dat" GeoipName = "GeoIP.dat" - UIPath = "" ) // Path is used to get the configuration path diff --git a/docs/config.yaml b/docs/config.yaml index ada57905..274eaedd 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -41,7 +41,11 @@ external-controller-tls: 0.0.0.0:9443 # RESTful API HTTPS 监听地址,需要 # secret: "123456" # `Authorization:Bearer ${secret}` # tcp-concurrent: true # TCP 并发连接所有 IP, 将使用最快握手的 TCP -external-ui: /path/to/ui/folder # 配置 WEB UI 目录,使用 http://{{external-controller}}/ui 访问 + +# 配置 WEB UI 目录,使用 http://{{external-controller}}/ui 访问 +external-ui: /path/to/ui/folder/ +external-ui-name: xd +external-ui-url: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip" # interface-name: en0 # 设置出口网卡 diff --git a/hub/route/server.go b/hub/route/server.go index 3df6196a..d2fecd05 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -49,7 +49,6 @@ type Memory struct { func SetUIPath(path string) { uiPath = C.Path.Resolve(path) - C.UIPath = uiPath } func Start(addr string, tlsAddr string, secret string, diff --git a/hub/route/upgrade.go b/hub/route/upgrade.go index ef5dffaf..be226616 100644 --- a/hub/route/upgrade.go +++ b/hub/route/upgrade.go @@ -16,7 +16,7 @@ import ( func upgradeRouter() http.Handler { r := chi.NewRouter() r.Post("/", upgrade) - r.Post("/xd", updateXD) + r.Post("/ui", updateUI) return r } @@ -46,8 +46,8 @@ func upgrade(w http.ResponseWriter, r *http.Request) { go restartExecutable(execPath) } -func updateXD(w http.ResponseWriter, r *http.Request) { - err := config.UpdateXD() +func updateUI(w http.ResponseWriter, r *http.Request) { + err := config.UpdateUI() if err != nil { log.Warnln("%s", err) render.Status(r, http.StatusInternalServerError) From fd96efd45697a5278870b0ea4f9893e0ea7b5f7b Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Mon, 18 Sep 2023 19:36:11 +0800 Subject: [PATCH 38/66] chore: ignore PR when Pre-releasing --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d7cdd6c9..4a65a829 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -209,7 +209,7 @@ jobs: Upload-Prerelease: permissions: write-all - if: ${{ github.ref_type=='branch' }} + if: ${{ github.ref_type=='branch' && github.event_name != 'pull_request' }} needs: [Build] runs-on: ubuntu-latest steps: From 20fafdca65642273eccbfe5df257e780c308812c Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Mon, 18 Sep 2023 19:42:08 +0800 Subject: [PATCH 39/66] chore: cleanup code --- config/config.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/config/config.go b/config/config.go index a2e64026..66815274 100644 --- a/config/config.go +++ b/config/config.go @@ -63,7 +63,6 @@ type General struct { EBpf EBpf `json:"-"` GlobalClientFingerprint string `json:"global-client-fingerprint"` GlobalUA string `json:"global-ua"` - KeepAliveInterval int `json:"keep-alive-interval"` } // Inbound config @@ -646,7 +645,6 @@ func parseGeneral(cfg *RawConfig) (*General, error) { EBpf: cfg.EBpf, GlobalClientFingerprint: cfg.GlobalClientFingerprint, GlobalUA: cfg.GlobalUA, - KeepAliveInterval: cfg.KeepAliveInterval, }, nil } From 8b518161a308c1295aa9a83b185e782b6b7e9211 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Wed, 20 Sep 2023 14:23:58 +0800 Subject: [PATCH 40/66] chore: update external-ui --- config/config.go | 2 ++ config/update_ui.go | 22 ++++++++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/config/config.go b/config/config.go index 66815274..d0e69e49 100644 --- a/config/config.go +++ b/config/config.go @@ -608,6 +608,8 @@ func parseGeneral(cfg *RawConfig) (*General, error) { return nil, err } } + } else { + ExternalUIFolder = ExternalUIPath } cfg.Tun.RedirectToTun = cfg.EBpf.RedirectToTun diff --git a/config/update_ui.go b/config/update_ui.go index 81b5bb03..5bc2596e 100644 --- a/config/update_ui.go +++ b/config/update_ui.go @@ -9,6 +9,8 @@ import ( "path/filepath" "strings" "sync" + + C "github.com/Dreamacro/clash/constant" ) var ( @@ -24,29 +26,29 @@ func UpdateUI() error { xdMutex.Lock() defer xdMutex.Unlock() - if ExternalUIPath == "" || ExternalUIFolder == "" || ExternalUIName == "" { + if ExternalUIPath == "" || ExternalUIFolder == "" { return fmt.Errorf("ExternalUI configure incomplete") } - err := cleanup(ExternalUIFolder) - if err != nil { - if !os.IsNotExist(err) { - return fmt.Errorf("cleanup exist file error: %w", err) - } - } - data, err := downloadForBytes(ExternalUIURL) if err != nil { return fmt.Errorf("can't download file: %w", err) } - saved := path.Join(ExternalUIPath, "download.zip") + saved := path.Join(C.Path.HomeDir(), "download.zip") if saveFile(data, saved) != nil { return fmt.Errorf("can't save zip file: %w", err) } defer os.Remove(saved) - unzipFolder, err := unzip(saved, ExternalUIPath) + err = cleanup(ExternalUIFolder) + if err != nil { + if !os.IsNotExist(err) { + return fmt.Errorf("cleanup exist file error: %w", err) + } + } + + unzipFolder, err := unzip(saved, C.Path.HomeDir()) if err != nil { return fmt.Errorf("can't extract zip file: %w", err) } From f909b3c0dcde070aaece47d4b83a1178535412f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B1=90=E6=AE=87?= <95160953+xishang0128@users.noreply.github.com> Date: Wed, 20 Sep 2023 15:26:36 +0800 Subject: [PATCH 41/66] chore: Update android-ndk --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4a65a829..defd294b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -147,7 +147,7 @@ jobs: if: ${{ matrix.job.type=='WithCGO' && matrix.job.target=='android' }} id: setup-ndk with: - ndk-version: r25b + ndk-version: r26 add-to-path: false local-cache: true From 0d7a57fa9d3dd098a5d865e3674fd3d38171b1b3 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Thu, 21 Sep 2023 03:40:46 +0800 Subject: [PATCH 42/66] Chore: update github issue template --- .github/ISSUE_TEMPLATE/bug_report.yml | 82 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.yml | 36 ++++++++++ 2 files changed, 118 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..f8a0f4ae --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,82 @@ +name: Bug report +description: Create a report to help us improve +title: "[Bug] " +body: + - type: checkboxes + id: ensure + attributes: + label: Verify steps + description: " +在提交之前,请确认 +Please verify that you've followed these steps +" + options: + - label: " +确保你使用的是**本仓库**最新的的 clash 或 clash Alpha 版本 +Ensure you are using the latest version of Clash or Clash Premium from **this repository**. +" + required: true + - label: " +如果你可以自己 debug 并解决的话,提交 PR 吧 +Is this something you can **debug and fix**? Send a pull request! Bug fixes and documentation fixes are welcome. +" + required: false + - label: " +我已经在 [Issue Tracker](……/) 中找过我要提出的问题 +I have searched on the [issue tracker](……/) for a related issue. +" + required: true + - label: " +我已经使用 Alpha 分支版本测试过,问题依旧存在 +I have tested using the dev branch, and the issue still exists. +" + required: true + - label: " +我已经仔细看过 [Documentation](https://wiki.metacubex.one/) 并无法自行解决问题 +I have read the [documentation](https://wiki.metacubex.one/) and was unable to solve the issue. +" + required: true + - label: " +这是 Clash 核心的问题,并非我所使用的 Clash 衍生版本(如 OpenClash、KoolClash 等)的特定问题 +This is an issue of the Clash core *per se*, not to the derivatives of Clash, like OpenClash or KoolClash. +" + required: true + - type: input + attributes: + label: Clash version + description: "use `clash -v`" + validations: + required: true + - type: dropdown + id: os + attributes: + label: What OS are you seeing the problem on? + multiple: true + options: + - macOS + - Windows + - Linux + - OpenBSD/FreeBSD + - type: textarea + attributes: + render: yaml + label: "Clash config" + description: " +在下方附上 Clash core 配置文件,请确保配置文件中没有敏感信息(比如:服务器地址,密码,端口等) +Paste the Clash core configuration file below, please make sure that there is no sensitive information in the configuration file (e.g., server address/url, password, port) +" + validations: + required: true + - type: textarea + attributes: + render: shell + label: Clash log + description: " +在下方附上 Clash Core 的日志,log level 使用 DEBUG +Paste the Clash core log below with the log level set to `DEBUG`. +" + - type: textarea + attributes: + label: Description + validations: + required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..c8f70b19 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,36 @@ +name: Feature request +description: Suggest an idea for this project +title: "[Feature] " +body: + - type: checkboxes + id: ensure + attributes: + label: Verify steps + description: " +在提交之前,请确认 +Please verify that you've followed these steps +" + options: + - label: " +我已经在 [Issue Tracker](……/) 中找过我要提出的请求 +I have searched on the [issue tracker](……/) for a related feature request. +" + required: true + - label: " +我已经仔细看过 [Documentation](https://wiki.metacubex.one/) 并无法找到这个功能 +I have read the [documentation](https://wiki.metacubex.one/) and was unable to solve the issue. +" + required: true + - type: textarea + attributes: + label: Description + description: 请详细、清晰地表达你要提出的论述,例如这个问题如何影响到你?你想实现什么功能?目前 Clash Core 的行为是什麽? + validations: + required: true + - type: textarea + attributes: + label: Possible Solution + description: " +此项非必须,但是如果你有想法的话欢迎提出。 +Not obligatory, but suggest a fix/reason for the bug, or ideas how to implement the addition or change +" \ No newline at end of file From 62266010aca1510d7e10a5cbbbfbbb682b7605fb Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 21 Sep 2023 08:25:26 +0800 Subject: [PATCH 43/66] Revert "migration: go 1.21" This reverts commit 33d41338ef00ff20e444e03f3d9ee7b60fc787a4. --- adapter/adapter.go | 12 ++++++------ common/observable/observable_test.go | 6 +++--- go.mod | 2 +- hub/updater/limitedreader.go | 13 ++++++++++++- transport/tuic/congestion/cubic.go | 2 +- transport/tuic/congestion/cubic_sender.go | 4 ++-- .../tuic/congestion/hybrid_slow_start.go | 4 ++-- transport/tuic/congestion/minmax.go | 2 +- transport/tuic/congestion/minmax_go120.go | 19 +++++++++++++++++++ transport/tuic/congestion/minmax_go121.go | 13 +++++++++++++ transport/tuic/congestion/pacer.go | 6 +++--- 11 files changed, 63 insertions(+), 20 deletions(-) create mode 100644 transport/tuic/congestion/minmax_go120.go create mode 100644 transport/tuic/congestion/minmax_go121.go diff --git a/adapter/adapter.go b/adapter/adapter.go index abef3d89..13f7f06f 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -136,21 +136,21 @@ func (p *Proxy) ExtraDelayHistory() map[string][]C.DelayHistory { // LastDelay return last history record. if proxy is not alive, return the max value of uint16. // implements C.Proxy func (p *Proxy) LastDelay() (delay uint16) { - var maxDelay uint16 = 0xffff + var max uint16 = 0xffff if !p.alive.Load() { - return maxDelay + return max } history := p.history.Last() if history.Delay == 0 { - return maxDelay + return max } return history.Delay } // LastDelayForTestUrl implements C.Proxy func (p *Proxy) LastDelayForTestUrl(url string) (delay uint16) { - var maxDelay uint16 = 0xffff + var max uint16 = 0xffff alive := p.alive.Load() history := p.history.Last() @@ -161,11 +161,11 @@ func (p *Proxy) LastDelayForTestUrl(url string) (delay uint16) { } if !alive { - return maxDelay + return max } if history.Delay == 0 { - return maxDelay + return max } return history.Delay } diff --git a/common/observable/observable_test.go b/common/observable/observable_test.go index 3f0b0e7f..5a02273d 100644 --- a/common/observable/observable_test.go +++ b/common/observable/observable_test.go @@ -85,16 +85,16 @@ func TestObservable_UnSubscribeWithNotExistSubscription(t *testing.T) { func TestObservable_SubscribeGoroutineLeak(t *testing.T) { iter := iterator[int]([]int{1, 2, 3, 4, 5}) src := NewObservable[int](iter) - total := 100 + max := 100 var list []Subscription[int] - for i := 0; i < total; i++ { + for i := 0; i < max; i++ { ch, _ := src.Subscribe() list = append(list, ch) } var wg sync.WaitGroup - wg.Add(total) + wg.Add(max) waitCh := func(ch <-chan int) { for range ch { } diff --git a/go.mod b/go.mod index ca98937b..f75f58fd 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/Dreamacro/clash -go 1.21 +go 1.20 require ( github.com/3andne/restls-client-go v0.1.6 diff --git a/hub/updater/limitedreader.go b/hub/updater/limitedreader.go index 52d7e969..c31db601 100644 --- a/hub/updater/limitedreader.go +++ b/hub/updater/limitedreader.go @@ -3,6 +3,8 @@ package updater import ( "fmt" "io" + + "golang.org/x/exp/constraints" ) // LimitReachedError records the limit and the operation that caused it. @@ -33,7 +35,7 @@ func (lr *limitedReader) Read(p []byte) (n int, err error) { } } - p = p[:min(lr.n, int64(len(p)))] + p = p[:Min(lr.n, int64(len(p)))] n, err = lr.r.Read(p) lr.n -= int64(n) @@ -54,3 +56,12 @@ func LimitReader(r io.Reader, n int64) (limited io.Reader, err error) { n: n, }, nil } + +// Min returns the smaller of x or y. +func Min[T constraints.Integer | ~string](x, y T) (res T) { + if x < y { + return x + } + + return y +} diff --git a/transport/tuic/congestion/cubic.go b/transport/tuic/congestion/cubic.go index 7dbe2b06..dd491a32 100644 --- a/transport/tuic/congestion/cubic.go +++ b/transport/tuic/congestion/cubic.go @@ -186,7 +186,7 @@ func (c *Cubic) CongestionWindowAfterAck( targetCongestionWindow = c.originPointCongestionWindow - deltaCongestionWindow } // Limit the CWND increase to half the acked bytes. - targetCongestionWindow = min(targetCongestionWindow, currentCongestionWindow+c.ackedBytesCount/2) + targetCongestionWindow = Min(targetCongestionWindow, currentCongestionWindow+c.ackedBytesCount/2) // Increase the window by approximately Alpha * 1 MSS of bytes every // time we ack an estimated tcp window of bytes. For small diff --git a/transport/tuic/congestion/cubic_sender.go b/transport/tuic/congestion/cubic_sender.go index a467b8c9..ca20b420 100644 --- a/transport/tuic/congestion/cubic_sender.go +++ b/transport/tuic/congestion/cubic_sender.go @@ -177,7 +177,7 @@ func (c *cubicSender) OnPacketAcked( priorInFlight congestion.ByteCount, eventTime time.Time, ) { - c.largestAckedPacketNumber = max(ackedPacketNumber, c.largestAckedPacketNumber) + c.largestAckedPacketNumber = Max(ackedPacketNumber, c.largestAckedPacketNumber) if c.InRecovery() { return } @@ -245,7 +245,7 @@ func (c *cubicSender) maybeIncreaseCwnd( c.numAckedPackets = 0 } } else { - c.congestionWindow = min(c.maxCongestionWindow(), c.cubic.CongestionWindowAfterAck(ackedBytes, c.congestionWindow, c.rttStats.MinRTT(), eventTime)) + c.congestionWindow = Min(c.maxCongestionWindow(), c.cubic.CongestionWindowAfterAck(ackedBytes, c.congestionWindow, c.rttStats.MinRTT(), eventTime)) } } diff --git a/transport/tuic/congestion/hybrid_slow_start.go b/transport/tuic/congestion/hybrid_slow_start.go index e0b42e53..7586774e 100644 --- a/transport/tuic/congestion/hybrid_slow_start.go +++ b/transport/tuic/congestion/hybrid_slow_start.go @@ -74,8 +74,8 @@ func (s *HybridSlowStart) ShouldExitSlowStart(latestRTT time.Duration, minRTT ti // Divide minRTT by 8 to get a rtt increase threshold for exiting. minRTTincreaseThresholdUs := int64(minRTT / time.Microsecond >> hybridStartDelayFactorExp) // Ensure the rtt threshold is never less than 2ms or more than 16ms. - minRTTincreaseThresholdUs = min(minRTTincreaseThresholdUs, hybridStartDelayMaxThresholdUs) - minRTTincreaseThreshold := time.Duration(max(minRTTincreaseThresholdUs, hybridStartDelayMinThresholdUs)) * time.Microsecond + minRTTincreaseThresholdUs = Min(minRTTincreaseThresholdUs, hybridStartDelayMaxThresholdUs) + minRTTincreaseThreshold := time.Duration(Max(minRTTincreaseThresholdUs, hybridStartDelayMinThresholdUs)) * time.Microsecond if s.currentMinRTT > (minRTT + minRTTincreaseThreshold) { s.hystartFound = true diff --git a/transport/tuic/congestion/minmax.go b/transport/tuic/congestion/minmax.go index 50667766..0a8f4ad4 100644 --- a/transport/tuic/congestion/minmax.go +++ b/transport/tuic/congestion/minmax.go @@ -16,7 +16,7 @@ func MinNonZeroDuration(a, b time.Duration) time.Duration { if b == 0 { return a } - return min(a, b) + return Min(a, b) } // AbsDuration returns the absolute value of a time duration diff --git a/transport/tuic/congestion/minmax_go120.go b/transport/tuic/congestion/minmax_go120.go new file mode 100644 index 00000000..1266edbc --- /dev/null +++ b/transport/tuic/congestion/minmax_go120.go @@ -0,0 +1,19 @@ +//go:build !go1.21 + +package congestion + +import "golang.org/x/exp/constraints" + +func Max[T constraints.Ordered](a, b T) T { + if a < b { + return b + } + return a +} + +func Min[T constraints.Ordered](a, b T) T { + if a < b { + return a + } + return b +} diff --git a/transport/tuic/congestion/minmax_go121.go b/transport/tuic/congestion/minmax_go121.go new file mode 100644 index 00000000..65b06726 --- /dev/null +++ b/transport/tuic/congestion/minmax_go121.go @@ -0,0 +1,13 @@ +//go:build go1.21 + +package congestion + +import "cmp" + +func Max[T cmp.Ordered](a, b T) T { + return max(a, b) +} + +func Min[T cmp.Ordered](a, b T) T { + return min(a, b) +} diff --git a/transport/tuic/congestion/pacer.go b/transport/tuic/congestion/pacer.go index 9e85107d..f60ef5fe 100644 --- a/transport/tuic/congestion/pacer.go +++ b/transport/tuic/congestion/pacer.go @@ -52,11 +52,11 @@ func (p *pacer) Budget(now time.Time) congestion.ByteCount { return p.maxBurstSize() } budget := p.budgetAtLastSent + (congestion.ByteCount(p.getAdjustedBandwidth())*congestion.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9 - return min(p.maxBurstSize(), budget) + return Min(p.maxBurstSize(), budget) } func (p *pacer) maxBurstSize() congestion.ByteCount { - return max( + return Max( congestion.ByteCount(uint64((MinPacingDelay+TimerGranularity).Nanoseconds())*p.getAdjustedBandwidth())/1e9, maxBurstSizePackets*p.maxDatagramSize, ) @@ -68,7 +68,7 @@ func (p *pacer) TimeUntilSend() time.Time { if p.budgetAtLastSent >= p.maxDatagramSize { return time.Time{} } - return p.lastSentTime.Add(max( + return p.lastSentTime.Add(Max( MinPacingDelay, time.Duration(math.Ceil(float64(p.maxDatagramSize-p.budgetAtLastSent)*1e9/float64(p.getAdjustedBandwidth())))*time.Nanosecond, )) From 42b85de83eb6b681c456d6c6b07eb1267ae4c695 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 21 Sep 2023 08:28:05 +0800 Subject: [PATCH 44/66] chore: Restore go1.20 support --- dns/util.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dns/util.go b/dns/util.go index c9d48a49..08daadbd 100644 --- a/dns/util.go +++ b/dns/util.go @@ -64,7 +64,9 @@ func putMsgToCacheWithExpire(c *cache.LruCache[string, *D.Msg], key string, msg return } - sec = max(sec, 120) // at least 2 minutes to cache + if sec > 120 { + sec = 120 // at least 2 minutes to cache + } } From 24fd5777676fc76e0965db5bbdfee3bf1b825d96 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 21 Sep 2023 08:57:38 +0800 Subject: [PATCH 45/66] chore: Update dependencies --- go.mod | 18 +++++++++--------- go.sum | 33 +++++++++++++++++---------------- listener/sing_tun/server.go | 7 +++---- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/go.mod b/go.mod index f75f58fd..698fcc8a 100644 --- a/go.mod +++ b/go.mod @@ -20,19 +20,19 @@ require ( github.com/mdlayher/netlink v1.7.2 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf - github.com/metacubex/sing-shadowsocks v0.2.4 - github.com/metacubex/sing-shadowsocks2 v0.1.3 - github.com/metacubex/sing-tun v0.1.11 - github.com/metacubex/sing-vmess v0.1.8-0.20230801054944-603005461ff8 + github.com/metacubex/sing-shadowsocks v0.2.5 + github.com/metacubex/sing-shadowsocks2 v0.1.4 + github.com/metacubex/sing-tun v0.1.12 + github.com/metacubex/sing-vmess v0.1.9-0.20230921005247-a0488d7dac74 github.com/metacubex/sing-wireguard v0.0.0-20230611155257-1498ae315a28 - github.com/miekg/dns v1.1.55 + github.com/miekg/dns v1.1.56 github.com/mroth/weightedrand/v2 v2.1.0 github.com/openacid/low v0.1.21 github.com/oschwald/maxminddb-golang v1.12.0 github.com/puzpuzpuz/xsync/v2 v2.5.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a - github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c + github.com/sagernet/sing v0.2.11 + github.com/sagernet/sing-mux v0.1.3 github.com/sagernet/sing-shadowtls v0.1.4 github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 @@ -66,7 +66,7 @@ require ( github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gaukas/godicttls v0.0.4 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/golang/mock v1.6.0 // indirect github.com/google/btree v1.1.2 // indirect @@ -104,4 +104,4 @@ require ( golang.org/x/tools v0.13.0 // indirect ) -replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20230817143035-28d23f152579 +replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20230921005553-6eacdd2c7a24 diff --git a/go.sum b/go.sum index 59d83450..74cff3cf 100644 --- a/go.sum +++ b/go.sum @@ -44,8 +44,9 @@ github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vz github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M= @@ -96,20 +97,20 @@ github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475 h1:qSEOvPPaMrWggF github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475/go.mod h1:wehEpqiogdeyncfhckJP5gD2LtBgJW0wnDC24mJ+8Jg= github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf h1:hflzPbb2M+3uUOZEVO72MKd2R62xEermoVaNhJOzBR8= github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf/go.mod h1:7RCcKJJk1DMeNQQNnYKS+7FqftqPfG031oP8jrYRMw8= -github.com/metacubex/sing v0.0.0-20230817143035-28d23f152579 h1:dE1dBB6CTzNdSMFTE5OCHvzHLewiqiA1nhD+7egtvAc= -github.com/metacubex/sing v0.0.0-20230817143035-28d23f152579/go.mod h1:9uOZwWkhT2Z2WldolLxX34s+1svAX4i4vvz5hy8u1MA= -github.com/metacubex/sing-shadowsocks v0.2.4 h1:Gc99Z17JVif1PKKq1pjqhSmc2kvHUgk+AqxOstCzhQ0= -github.com/metacubex/sing-shadowsocks v0.2.4/go.mod h1:w9qoEZSh9aKeXSLXHe0DGbG2UE9/2VlLGwukzQZ7byI= -github.com/metacubex/sing-shadowsocks2 v0.1.3 h1:nZvH+4jQXZ92NeNdR9fXaUGTPNJPt6u0nkcuh/NEt5Y= -github.com/metacubex/sing-shadowsocks2 v0.1.3/go.mod h1:5Mt93RlmRlIcDmvtapkhQJ8YTRGLFhHciLYopJjs7j8= -github.com/metacubex/sing-tun v0.1.11 h1:B8meDewklvKkeUfjqR2ViuYLam0/m4IgkTi3qcJIOuc= -github.com/metacubex/sing-tun v0.1.11/go.mod h1:vbki176Y5sxXC1DWXucrPh3q5j8cKai1D87y8m8rjQc= -github.com/metacubex/sing-vmess v0.1.8-0.20230801054944-603005461ff8 h1:AqqZCr9gOeKdO6oIzFh4b2puOUFcw8MdpmGHWRehyX8= -github.com/metacubex/sing-vmess v0.1.8-0.20230801054944-603005461ff8/go.mod h1:tyJg7b4s8NrSztl/Y1ajA7X0sJLlIsEJWkgRVocjmgY= +github.com/metacubex/sing v0.0.0-20230921005553-6eacdd2c7a24 h1:652uMd78eKMU7/sVkW8qqAdZkJaiDoUflfCs5LHvb0Q= +github.com/metacubex/sing v0.0.0-20230921005553-6eacdd2c7a24/go.mod h1:GQ673iPfUnkbK/dIPkfd1Xh1MjOGo36gkl/mkiHY7Jg= +github.com/metacubex/sing-shadowsocks v0.2.5 h1:O2RRSHlKGEpAVG/OHJQxyHqDy8uvvdCW/oW2TDBOIhc= +github.com/metacubex/sing-shadowsocks v0.2.5/go.mod h1:Xz2uW9BEYGEoA8B4XEpoxt7ERHClFCwsMAvWaruoyMo= +github.com/metacubex/sing-shadowsocks2 v0.1.4 h1:OOCf8lgsVcpTOJUeaFAMzyKVebaQOBnKirDdUdBoKIE= +github.com/metacubex/sing-shadowsocks2 v0.1.4/go.mod h1:Qz028sLfdY3qxGRm9FDI+IM2Ae3ty2wR7HIzD/56h/k= +github.com/metacubex/sing-tun v0.1.12 h1:Jgmz0k3ddRiJ8zfS4X7j6B/iSy6GnOdDEU0nhqiZcK4= +github.com/metacubex/sing-tun v0.1.12/go.mod h1:X2P/H1HqXwqGcguGXWDVDhSS1GmDxVi13OmbtDedZ2M= +github.com/metacubex/sing-vmess v0.1.9-0.20230921005247-a0488d7dac74 h1:FtupiyFkaVjFvRa7B/uDtRWg5BNsoyPC9MTev3sDasY= +github.com/metacubex/sing-vmess v0.1.9-0.20230921005247-a0488d7dac74/go.mod h1:8EWBZpc+qNvf5gmvjAtMHK1/DpcWqzfcBL842K00BsM= github.com/metacubex/sing-wireguard v0.0.0-20230611155257-1498ae315a28 h1:mXFpxfR/1nADh+GoT8maWEvc6LO6uatPsARD8WzUDMA= github.com/metacubex/sing-wireguard v0.0.0-20230611155257-1498ae315a28/go.mod h1:KrDPq/dE793jGIJw9kcIvjA/proAfU0IeU7WlMXW7rs= -github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= -github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= +github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU= github.com/mroth/weightedrand/v2 v2.1.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU= github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 h1:1102pQc2SEPp5+xrS26wEaeb26sZy6k9/ZXlZN+eXE4= @@ -143,8 +144,8 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c 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-mux v0.1.3-0.20230811111955-dc1639b5204c h1:35/FowAvt3Z62mck0TXzVc4jS5R5CWq62qcV2P1cp0I= -github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c/go.mod h1:TKxqIvfQQgd36jp2tzsPavGjYTVZilV+atip1cssjIY= +github.com/sagernet/sing-mux v0.1.3 h1:fAf7PZa2A55mCeh0KKM02f1k2Y4vEmxuZZ/51ahkkLA= +github.com/sagernet/sing-mux v0.1.3/go.mod h1:wGeIeiiFLx4HUM5LAg65wrNZ/X1muOimqK0PEhNbPi0= github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k= github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= @@ -239,9 +240,9 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 084c701b..66fe8cd5 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -172,7 +172,7 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte } }() - networkUpdateMonitor, err := tun.NewNetworkUpdateMonitor(handler) + networkUpdateMonitor, err := tun.NewNetworkUpdateMonitor(log.SingLogger) if err != nil { err = E.Cause(err, "create NetworkUpdateMonitor") return @@ -184,15 +184,14 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte return } - defaultInterfaceMonitor, err := tun.NewDefaultInterfaceMonitor(networkUpdateMonitor, tun.DefaultInterfaceMonitorOptions{OverrideAndroidVPN: true}) + defaultInterfaceMonitor, err := tun.NewDefaultInterfaceMonitor(networkUpdateMonitor, log.SingLogger, tun.DefaultInterfaceMonitorOptions{OverrideAndroidVPN: true}) if err != nil { err = E.Cause(err, "create DefaultInterfaceMonitor") return } l.defaultInterfaceMonitor = defaultInterfaceMonitor - defaultInterfaceMonitor.RegisterCallback(func(event int) error { + defaultInterfaceMonitor.RegisterCallback(func(event int) { l.FlushDefaultInterface() - return nil }) err = defaultInterfaceMonitor.Start() if err != nil { From 9b8e2d93435dcd13b9266c6213557c6230fc1c61 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 21 Sep 2023 10:28:28 +0800 Subject: [PATCH 46/66] feat: support Hysteria2 --- adapter/outbound/hysteria.go | 36 ------ adapter/outbound/hysteria2.go | 218 ++++++++++++++++++++++++++++++++++ adapter/outbound/util.go | 38 ++++++ adapter/parser.go | 7 ++ constant/adapters.go | 3 + go.mod | 1 + go.sum | 2 + 7 files changed, 269 insertions(+), 36 deletions(-) create mode 100644 adapter/outbound/hysteria2.go diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index 7da4975d..ff6e5deb 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -268,42 +268,6 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { }, nil } -func stringToBps(s string) uint64 { - if s == "" { - return 0 - } - - // when have not unit, use Mbps - if v, err := strconv.Atoi(s); err == nil { - return stringToBps(fmt.Sprintf("%d Mbps", v)) - } - - m := rateStringRegexp.FindStringSubmatch(s) - if m == nil { - return 0 - } - var n uint64 - switch m[2] { - case "K": - n = 1 << 10 - case "M": - n = 1 << 20 - case "G": - n = 1 << 30 - case "T": - n = 1 << 40 - default: - n = 1 - } - v, _ := strconv.ParseUint(m[1], 10, 64) - n = v * n - if m[3] == "b" { - // Bits, need to convert to bytes - n = n >> 3 - } - return n -} - type hyPacketConn struct { core.UDPConn } diff --git a/adapter/outbound/hysteria2.go b/adapter/outbound/hysteria2.go new file mode 100644 index 00000000..61c8b7c7 --- /dev/null +++ b/adapter/outbound/hysteria2.go @@ -0,0 +1,218 @@ +package outbound + +import ( + "context" + "crypto/sha256" + "crypto/tls" + "encoding/hex" + "encoding/pem" + "errors" + "fmt" + "net" + "os" + "runtime" + "strconv" + + CN "github.com/Dreamacro/clash/common/net" + "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/proxydialer" + tlsC "github.com/Dreamacro/clash/component/tls" + C "github.com/Dreamacro/clash/constant" + tuicCommon "github.com/Dreamacro/clash/transport/tuic/common" + + "github.com/metacubex/sing-quic/hysteria2" + + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" +) + +func init() { + hysteria2.SetCongestionController = tuicCommon.SetCongestionController +} + +type Hysteria2 struct { + *Base + + option *Hysteria2Option + client *hysteria2.Client + dialer *hy2SingDialer +} + +type Hysteria2Option struct { + BasicOption + Name string `proxy:"name"` + Server string `proxy:"server"` + Port int `proxy:"port"` + Up string `proxy:"up,omitempty"` + Down string `proxy:"down,omitempty"` + Password string `proxy:"password,omitempty"` + Obfs string `proxy:"obfs,omitempty"` + ObfsPassword string `proxy:"obfs-password,omitempty"` + SNI string `proxy:"sni,omitempty"` + SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + Fingerprint string `proxy:"fingerprint,omitempty"` + ALPN []string `proxy:"alpn,omitempty"` + CustomCA string `proxy:"ca,omitempty"` + CustomCAString string `proxy:"ca-str,omitempty"` +} + +type hy2SingDialer struct { + dialer dialer.Dialer + proxyName string +} + +var _ N.Dialer = (*hy2SingDialer)(nil) + +func (d *hy2SingDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { + var cDialer C.Dialer = d.dialer + if len(d.proxyName) > 0 { + pd, err := proxydialer.NewByName(d.proxyName, d.dialer) + if err != nil { + return nil, err + } + cDialer = pd + } + return cDialer.DialContext(ctx, network, destination.String()) +} + +func (d *hy2SingDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { + var cDialer C.Dialer = d.dialer + if len(d.proxyName) > 0 { + pd, err := proxydialer.NewByName(d.proxyName, d.dialer) + if err != nil { + return nil, err + } + cDialer = pd + } + return cDialer.ListenPacket(ctx, "udp", "", destination.AddrPort()) +} + +func (h *Hysteria2) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { + options := h.Base.DialOptions(opts...) + h.dialer.dialer = dialer.NewDialer(options...) + c, err := h.client.DialConn(ctx, M.ParseSocksaddr(metadata.RemoteAddress())) + if err != nil { + return nil, err + } + return NewConn(CN.NewRefConn(c, h), h), nil +} + +func (h *Hysteria2) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { + options := h.Base.DialOptions(opts...) + h.dialer.dialer = dialer.NewDialer(options...) + pc, err := h.client.ListenPacket(ctx) + if err != nil { + return nil, err + } + if pc == nil { + return nil, errors.New("packetConn is nil") + } + return newPacketConn(CN.NewRefPacketConn(CN.NewThreadSafePacketConn(pc), h), h), nil +} + +func closeHysteria2(h *Hysteria2) { + if h.client != nil { + _ = h.client.CloseWithError(errors.New("proxy removed")) + } +} + +func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { + addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) + var salamanderPassword string + if len(option.Obfs) > 0 { + if option.ObfsPassword == "" { + return nil, errors.New("missing obfs password") + } + switch option.Obfs { + case hysteria2.ObfsTypeSalamander: + salamanderPassword = option.ObfsPassword + default: + return nil, fmt.Errorf("unknown obfs type: %s", option.Obfs) + } + } + + serverName := option.Server + if option.SNI != "" { + serverName = option.SNI + } + + tlsConfig := &tls.Config{ + ServerName: serverName, + InsecureSkipVerify: option.SkipCertVerify, + MinVersion: tls.VersionTLS13, + } + + var bs []byte + var err error + if len(option.CustomCA) > 0 { + bs, err = os.ReadFile(option.CustomCA) + if err != nil { + return nil, fmt.Errorf("hysteria %s load ca error: %w", option.Name, err) + } + } else if option.CustomCAString != "" { + bs = []byte(option.CustomCAString) + } + + if len(bs) > 0 { + block, _ := pem.Decode(bs) + if block == nil { + return nil, fmt.Errorf("CA cert is not PEM") + } + + fpBytes := sha256.Sum256(block.Bytes) + if len(option.Fingerprint) == 0 { + option.Fingerprint = hex.EncodeToString(fpBytes[:]) + } + } + + if len(option.Fingerprint) != 0 { + var err error + tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint) + if err != nil { + return nil, err + } + } else { + tlsConfig = tlsC.GetGlobalTLSConfig(tlsConfig) + } + + if len(option.ALPN) > 0 { + tlsConfig.NextProtos = option.ALPN + } + + singDialer := &hy2SingDialer{dialer: dialer.NewDialer(), proxyName: option.DialerProxy} + + clientOptions := hysteria2.ClientOptions{ + Context: context.TODO(), + Dialer: singDialer, + ServerAddress: M.ParseSocksaddrHostPort(option.Server, uint16(option.Port)), + SendBPS: stringToBps(option.Up), + ReceiveBPS: stringToBps(option.Down), + SalamanderPassword: salamanderPassword, + Password: option.Password, + TLSConfig: tlsConfig, + UDPDisabled: false, + } + + client, err := hysteria2.NewClient(clientOptions) + if err != nil { + return nil, err + } + + outbound := &Hysteria2{ + Base: &Base{ + name: option.Name, + addr: addr, + tp: C.Hysteria2, + udp: true, + iface: option.Interface, + rmark: option.RoutingMark, + prefer: C.NewDNSPrefer(option.IPVersion), + }, + option: &option, + client: client, + dialer: singDialer, + } + runtime.SetFinalizer(outbound, closeHysteria2) + + return outbound, nil +} diff --git a/adapter/outbound/util.go b/adapter/outbound/util.go index 36607e4f..3faa3c43 100644 --- a/adapter/outbound/util.go +++ b/adapter/outbound/util.go @@ -4,8 +4,10 @@ import ( "bytes" "context" "crypto/tls" + "fmt" "net" "net/netip" + "strconv" "sync" "github.com/Dreamacro/clash/component/resolver" @@ -120,3 +122,39 @@ func safeConnClose(c net.Conn, err error) { _ = c.Close() } } + +func stringToBps(s string) uint64 { + if s == "" { + return 0 + } + + // when have not unit, use Mbps + if v, err := strconv.Atoi(s); err == nil { + return stringToBps(fmt.Sprintf("%d Mbps", v)) + } + + m := rateStringRegexp.FindStringSubmatch(s) + if m == nil { + return 0 + } + var n uint64 + switch m[2] { + case "K": + n = 1 << 10 + case "M": + n = 1 << 20 + case "G": + n = 1 << 30 + case "T": + n = 1 << 40 + default: + n = 1 + } + v, _ := strconv.ParseUint(m[1], 10, 64) + n = v * n + if m[3] == "b" { + // Bits, need to convert to bytes + n = n >> 3 + } + return n +} diff --git a/adapter/parser.go b/adapter/parser.go index 78e287f9..eeb0fd59 100644 --- a/adapter/parser.go +++ b/adapter/parser.go @@ -92,6 +92,13 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { break } proxy, err = outbound.NewHysteria(*hyOption) + case "hysteria2": + hyOption := &outbound.Hysteria2Option{} + err = decoder.Decode(mapping, hyOption) + if err != nil { + break + } + proxy, err = outbound.NewHysteria2(*hyOption) case "wireguard": wgOption := &outbound.WireGuardOption{} err = decoder.Decode(mapping, wgOption) diff --git a/constant/adapters.go b/constant/adapters.go index 5639dd47..33b9a44f 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -36,6 +36,7 @@ const ( Vless Trojan Hysteria + Hysteria2 WireGuard Tuic ) @@ -200,6 +201,8 @@ func (at AdapterType) String() string { return "Trojan" case Hysteria: return "Hysteria" + case Hysteria2: + return "Hysteria2" case WireGuard: return "WireGuard" case Tuic: diff --git a/go.mod b/go.mod index 698fcc8a..d16ccb01 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/mdlayher/netlink v1.7.2 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf + github.com/metacubex/sing-quic v0.0.0-20230921015854-1ed89eed54d5 github.com/metacubex/sing-shadowsocks v0.2.5 github.com/metacubex/sing-shadowsocks2 v0.1.4 github.com/metacubex/sing-tun v0.1.12 diff --git a/go.sum b/go.sum index 74cff3cf..45cb3cbb 100644 --- a/go.sum +++ b/go.sum @@ -99,6 +99,8 @@ github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf h1:hflzPbb2M+ github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf/go.mod h1:7RCcKJJk1DMeNQQNnYKS+7FqftqPfG031oP8jrYRMw8= github.com/metacubex/sing v0.0.0-20230921005553-6eacdd2c7a24 h1:652uMd78eKMU7/sVkW8qqAdZkJaiDoUflfCs5LHvb0Q= github.com/metacubex/sing v0.0.0-20230921005553-6eacdd2c7a24/go.mod h1:GQ673iPfUnkbK/dIPkfd1Xh1MjOGo36gkl/mkiHY7Jg= +github.com/metacubex/sing-quic v0.0.0-20230921015854-1ed89eed54d5 h1:XhWilr6vJoXy4n/sP2datek28FTF3s3rWHhezdFFD8s= +github.com/metacubex/sing-quic v0.0.0-20230921015854-1ed89eed54d5/go.mod h1:oGpQmqe5tj3sPdPWCNLbBoUSwqd+Z6SqVO7TlMNVnH4= github.com/metacubex/sing-shadowsocks v0.2.5 h1:O2RRSHlKGEpAVG/OHJQxyHqDy8uvvdCW/oW2TDBOIhc= github.com/metacubex/sing-shadowsocks v0.2.5/go.mod h1:Xz2uW9BEYGEoA8B4XEpoxt7ERHClFCwsMAvWaruoyMo= github.com/metacubex/sing-shadowsocks2 v0.1.4 h1:OOCf8lgsVcpTOJUeaFAMzyKVebaQOBnKirDdUdBoKIE= From 6c3b973748a844eb0de0c300b320bc7874840104 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 21 Sep 2023 10:43:45 +0800 Subject: [PATCH 47/66] doc: add Hysteria2 doc --- adapter/outbound/hysteria.go | 3 --- adapter/outbound/util.go | 3 +++ docs/config.yaml | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index ff6e5deb..e30565fb 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -11,7 +11,6 @@ import ( "net" "net/netip" "os" - "regexp" "strconv" "time" @@ -43,8 +42,6 @@ const ( DefaultHopInterval = 10 ) -var rateStringRegexp = regexp.MustCompile(`^(\d+)\s*([KMGT]?)([Bb])ps$`) - type Hysteria struct { *Base diff --git a/adapter/outbound/util.go b/adapter/outbound/util.go index 3faa3c43..4b59183e 100644 --- a/adapter/outbound/util.go +++ b/adapter/outbound/util.go @@ -7,6 +7,7 @@ import ( "fmt" "net" "net/netip" + "regexp" "strconv" "sync" @@ -123,6 +124,8 @@ func safeConnClose(c net.Conn, err error) { } } +var rateStringRegexp = regexp.MustCompile(`^(\d+)\s*([KMGT]?)([Bb])ps$`) + func stringToBps(s string) uint64 { if s == "" { return 0 diff --git a/docs/config.yaml b/docs/config.yaml index 274eaedd..c2bdc263 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -642,6 +642,25 @@ proxies: # socks5 # fingerprint: xxxx # fast-open: true # 支持 TCP 快速打开,默认为 false + #hysteria2 + - name: "hysteria2" + type: hysteria2 + server: server.com + port: 443 + # up和down均不写或为0则使用BBR流控 + # up: "30 Mbps" # 若不写单位,默认为 Mbps + # down: "200 Mbps" # 若不写单位,默认为 Mbps + password: yourpassword + # obfs: salamander # 默认为空,如果填写则开启obfs,目前仅支持salamander + # obfs-password: yourpassword + # sni: server.com + # skip-cert-verify: false + # fingerprint: xxxx + # alpn: + # - h3 + # ca: "./my.ca" + # ca-str: "xyz" + # wireguard - name: "wg" type: wireguard From 233eeb0b38e1e549c5b1ef466b689cd25e5fe925 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 21 Sep 2023 14:52:26 +0800 Subject: [PATCH 48/66] feat: inbound support Hysteria2 --- adapter/outbound/hysteria.go | 4 +- adapter/outbound/hysteria2.go | 4 +- adapter/outbound/util.go | 4 +- constant/metadata.go | 5 + listener/config/hysteria2.go | 24 ++++ listener/inbound/hysteria2.go | 93 +++++++++++++++ listener/parse.go | 7 ++ listener/sing_hysteria2/server.go | 180 ++++++++++++++++++++++++++++++ 8 files changed, 315 insertions(+), 6 deletions(-) create mode 100644 listener/config/hysteria2.go create mode 100644 listener/inbound/hysteria2.go create mode 100644 listener/sing_hysteria2/server.go diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index e30565fb..7cd4ea76 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -117,12 +117,12 @@ type HysteriaOption struct { func (c *HysteriaOption) Speed() (uint64, uint64, error) { var up, down uint64 - up = stringToBps(c.Up) + up = StringToBps(c.Up) if up == 0 { return 0, 0, fmt.Errorf("invaild upload speed: %s", c.Up) } - down = stringToBps(c.Down) + down = StringToBps(c.Down) if down == 0 { return 0, 0, fmt.Errorf("invaild download speed: %s", c.Down) } diff --git a/adapter/outbound/hysteria2.go b/adapter/outbound/hysteria2.go index 61c8b7c7..8963da66 100644 --- a/adapter/outbound/hysteria2.go +++ b/adapter/outbound/hysteria2.go @@ -185,8 +185,8 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { Context: context.TODO(), Dialer: singDialer, ServerAddress: M.ParseSocksaddrHostPort(option.Server, uint16(option.Port)), - SendBPS: stringToBps(option.Up), - ReceiveBPS: stringToBps(option.Down), + SendBPS: StringToBps(option.Up), + ReceiveBPS: StringToBps(option.Down), SalamanderPassword: salamanderPassword, Password: option.Password, TLSConfig: tlsConfig, diff --git a/adapter/outbound/util.go b/adapter/outbound/util.go index 4b59183e..b048cd8b 100644 --- a/adapter/outbound/util.go +++ b/adapter/outbound/util.go @@ -126,14 +126,14 @@ func safeConnClose(c net.Conn, err error) { var rateStringRegexp = regexp.MustCompile(`^(\d+)\s*([KMGT]?)([Bb])ps$`) -func stringToBps(s string) uint64 { +func StringToBps(s string) uint64 { if s == "" { return 0 } // when have not unit, use Mbps if v, err := strconv.Atoi(s); err == nil { - return stringToBps(fmt.Sprintf("%d Mbps", v)) + return StringToBps(fmt.Sprintf("%d Mbps", v)) } m := rateStringRegexp.FindStringSubmatch(s) diff --git a/constant/metadata.go b/constant/metadata.go index dbd31fd8..70478911 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -30,6 +30,7 @@ const ( TUNNEL TUN TUIC + HYSTERIA2 INNER ) @@ -78,6 +79,8 @@ func (t Type) String() string { return "Tun" case TUIC: return "Tuic" + case HYSTERIA2: + return "Hysteria2" case INNER: return "Inner" default: @@ -110,6 +113,8 @@ func ParseType(t string) (*Type, error) { res = TUN case "TUIC": res = TUIC + case "HYSTERIA2": + res = HYSTERIA2 case "INNER": res = INNER default: diff --git a/listener/config/hysteria2.go b/listener/config/hysteria2.go new file mode 100644 index 00000000..e8e9c09c --- /dev/null +++ b/listener/config/hysteria2.go @@ -0,0 +1,24 @@ +package config + +import "encoding/json" + +type Hysteria2Server struct { + Enable bool `yaml:"enable" json:"enable"` + Listen string `yaml:"listen" json:"listen"` + Users map[string]string `yaml:"users" json:"users,omitempty"` + Obfs string `yaml:"obfs" json:"obfs,omitempty"` + ObfsPassword string `yaml:"obfs-password" json:"obfs-password,omitempty"` + Certificate string `yaml:"certificate" json:"certificate"` + PrivateKey string `yaml:"private-key" json:"private-key"` + MaxIdleTime int `yaml:"max-idle-time" json:"max-idle-time,omitempty"` + ALPN []string `yaml:"alpn" json:"alpn,omitempty"` + Up string `yaml:"up" json:"up,omitempty"` + Down string `yaml:"down" json:"down,omitempty"` + IgnoreClientBandwidth bool `yaml:"ignore-client-bandwidth" json:"ignore-client-bandwidth,omitempty"` + Masquerade string `yaml:"masquerade" json:"masquerade,omitempty"` +} + +func (h Hysteria2Server) String() string { + b, _ := json.Marshal(h) + return string(b) +} diff --git a/listener/inbound/hysteria2.go b/listener/inbound/hysteria2.go new file mode 100644 index 00000000..00feadb1 --- /dev/null +++ b/listener/inbound/hysteria2.go @@ -0,0 +1,93 @@ +package inbound + +import ( + C "github.com/Dreamacro/clash/constant" + LC "github.com/Dreamacro/clash/listener/config" + "github.com/Dreamacro/clash/listener/sing_hysteria2" + "github.com/Dreamacro/clash/log" +) + +type Hysteria2Option struct { + BaseOption + Users map[string]string `inbound:"users,omitempty"` + Obfs string `inbound:"obfs,omitempty"` + ObfsPassword string `inbound:"obfs-password,omitempty"` + Certificate string `inbound:"certificate"` + PrivateKey string `inbound:"private-key"` + MaxIdleTime int `inbound:"max-idle-time,omitempty"` + ALPN []string `inbound:"alpn,omitempty"` + Up string `inbound:"up,omitempty"` + Down string `inbound:"down,omitempty"` + IgnoreClientBandwidth bool `inbound:"ignore-client-bandwidth,omitempty"` + Masquerade string `inbound:"masquerade,omitempty"` +} + +func (o Hysteria2Option) Equal(config C.InboundConfig) bool { + return optionToString(o) == optionToString(config) +} + +type Hysteria2 struct { + *Base + config *Hysteria2Option + l *sing_hysteria2.Listener + ts LC.Hysteria2Server +} + +func NewHysteria2(options *Hysteria2Option) (*Hysteria2, error) { + base, err := NewBase(&options.BaseOption) + if err != nil { + return nil, err + } + return &Hysteria2{ + Base: base, + config: options, + ts: LC.Hysteria2Server{ + Enable: true, + Listen: base.RawAddress(), + Users: options.Users, + Obfs: options.Obfs, + ObfsPassword: options.ObfsPassword, + Certificate: options.Certificate, + PrivateKey: options.PrivateKey, + MaxIdleTime: options.MaxIdleTime, + ALPN: options.ALPN, + Up: options.Up, + Down: options.Down, + IgnoreClientBandwidth: options.IgnoreClientBandwidth, + Masquerade: options.Masquerade, + }, + }, nil +} + +// Config implements constant.InboundListener +func (t *Hysteria2) Config() C.InboundConfig { + return t.config +} + +// Address implements constant.InboundListener +func (t *Hysteria2) Address() string { + if t.l != nil { + for _, addr := range t.l.AddrList() { + return addr.String() + } + } + return "" +} + +// Listen implements constant.InboundListener +func (t *Hysteria2) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, natTable C.NatTable) error { + var err error + t.l, err = sing_hysteria2.New(t.ts, tcpIn, udpIn, t.Additions()...) + if err != nil { + return err + } + log.Infoln("Hysteria2[%s] proxy listening at: %s", t.Name(), t.Address()) + return nil +} + +// Close implements constant.InboundListener +func (t *Hysteria2) Close() error { + return t.l.Close() +} + +var _ C.InboundListener = (*Hysteria2)(nil) diff --git a/listener/parse.go b/listener/parse.go index c8e1ddf7..b0fac86a 100644 --- a/listener/parse.go +++ b/listener/parse.go @@ -86,6 +86,13 @@ func ParseListener(mapping map[string]any) (C.InboundListener, error) { return nil, err } listener, err = IN.NewVmess(vmessOption) + case "hysteria2": + hysteria2Option := &IN.Hysteria2Option{} + err = decoder.Decode(mapping, hysteria2Option) + if err != nil { + return nil, err + } + listener, err = IN.NewHysteria2(hysteria2Option) case "tuic": tuicOption := &IN.TuicOption{ MaxIdleTime: 15000, diff --git a/listener/sing_hysteria2/server.go b/listener/sing_hysteria2/server.go new file mode 100644 index 00000000..1d6ab29a --- /dev/null +++ b/listener/sing_hysteria2/server.go @@ -0,0 +1,180 @@ +package sing_hysteria2 + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + "net" + "net/http" + "net/http/httputil" + "net/url" + "strings" + + "github.com/Dreamacro/clash/adapter/inbound" + "github.com/Dreamacro/clash/adapter/outbound" + CN "github.com/Dreamacro/clash/common/net" + "github.com/Dreamacro/clash/common/sockopt" + C "github.com/Dreamacro/clash/constant" + LC "github.com/Dreamacro/clash/listener/config" + "github.com/Dreamacro/clash/listener/sing" + "github.com/Dreamacro/clash/log" + + "github.com/metacubex/sing-quic/hysteria2" + + E "github.com/sagernet/sing/common/exceptions" +) + +type Listener struct { + closed bool + config LC.Hysteria2Server + udpListeners []net.PacketConn + services []*hysteria2.Service[string] +} + +func New(config LC.Hysteria2Server, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, additions ...inbound.Addition) (*Listener, error) { + var sl *Listener + var err error + if len(additions) == 0 { + additions = []inbound.Addition{ + inbound.WithInName("DEFAULT-HYSTERIA2"), + inbound.WithSpecialRules(""), + } + } + + h := &sing.ListenerHandler{ + TcpIn: tcpIn, + UdpIn: udpIn, + Type: C.HYSTERIA2, + Additions: additions, + } + + sl = &Listener{false, config, nil, nil} + + cert, err := CN.ParseCert(config.Certificate, config.PrivateKey) + if err != nil { + return nil, err + } + tlsConfig := &tls.Config{ + MinVersion: tls.VersionTLS13, + Certificates: []tls.Certificate{cert}, + } + if len(config.ALPN) > 0 { + tlsConfig.NextProtos = config.ALPN + } else { + tlsConfig.NextProtos = []string{"h3"} + } + + var salamanderPassword string + if len(config.Obfs) > 0 { + if config.ObfsPassword == "" { + return nil, errors.New("missing obfs password") + } + switch config.Obfs { + case hysteria2.ObfsTypeSalamander: + salamanderPassword = config.ObfsPassword + default: + return nil, fmt.Errorf("unknown obfs type: %s", config.Obfs) + } + } + var masqueradeHandler http.Handler + if config.Masquerade != "" { + masqueradeURL, err := url.Parse(config.Masquerade) + if err != nil { + return nil, E.Cause(err, "parse masquerade URL") + } + switch masqueradeURL.Scheme { + case "file": + masqueradeHandler = http.FileServer(http.Dir(masqueradeURL.Path)) + case "http", "https": + masqueradeHandler = &httputil.ReverseProxy{ + Rewrite: func(r *httputil.ProxyRequest) { + r.SetURL(masqueradeURL) + r.Out.Host = r.In.Host + }, + ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) { + w.WriteHeader(http.StatusBadGateway) + }, + } + default: + return nil, E.New("unknown masquerade URL scheme: ", masqueradeURL.Scheme) + } + } + + service, err := hysteria2.NewService[string](hysteria2.ServiceOptions{ + Context: context.Background(), + Logger: log.SingLogger, + SendBPS: outbound.StringToBps(config.Up), + ReceiveBPS: outbound.StringToBps(config.Down), + SalamanderPassword: salamanderPassword, + TLSConfig: tlsConfig, + IgnoreClientBandwidth: config.IgnoreClientBandwidth, + Handler: h, + MasqueradeHandler: masqueradeHandler, + }) + if err != nil { + return nil, err + } + + userNameList := make([]string, 0, len(config.Users)) + userPasswordList := make([]string, 0, len(config.Users)) + for name, password := range config.Users { + userNameList = append(userNameList, name) + userPasswordList = append(userPasswordList, password) + } + service.UpdateUsers(userNameList, userPasswordList) + + for _, addr := range strings.Split(config.Listen, ",") { + addr := addr + _service := *service + service := &_service // make a copy + + ul, err := net.ListenPacket("udp", addr) + if err != nil { + return nil, err + } + + err = sockopt.UDPReuseaddr(ul.(*net.UDPConn)) + if err != nil { + log.Warnln("Failed to Reuse UDP Address: %s", err) + } + + sl.udpListeners = append(sl.udpListeners, ul) + sl.services = append(sl.services, service) + + go func() { + _ = service.Start(ul) + }() + } + + return sl, nil +} + +func (l *Listener) Close() error { + l.closed = true + var retErr error + for _, service := range l.services { + err := service.Close() + if err != nil { + retErr = err + } + } + for _, lis := range l.udpListeners { + err := lis.Close() + if err != nil { + retErr = err + } + } + return retErr +} + +func (l *Listener) Config() string { + return l.config.String() +} + +func (l *Listener) AddrList() (addrList []net.Addr) { + for _, lis := range l.udpListeners { + addrList = append(addrList, lis.LocalAddr()) + } + return +} From ee3213c28f69678e6eec5ba5cc739ce42241713d Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 21 Sep 2023 15:02:12 +0800 Subject: [PATCH 49/66] fix: tuicv5 panic in ReadFrom --- transport/tuic/v5/packet.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/transport/tuic/v5/packet.go b/transport/tuic/v5/packet.go index cd3ed12b..efbe0bb9 100644 --- a/transport/tuic/v5/packet.go +++ b/transport/tuic/v5/packet.go @@ -96,10 +96,10 @@ func (q *quicStreamPacketConn) SetWriteDeadline(t time.Time) error { } func (q *quicStreamPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - if q.inputConn != nil { + if inputConn := q.inputConn; inputConn != nil { // copy inputConn avoid be nil in for loop for { var packet Packet - packet, err = ReadPacket(q.inputConn) + packet, err = ReadPacket(inputConn) if err != nil { return } @@ -116,10 +116,10 @@ func (q *quicStreamPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err err } func (q *quicStreamPacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) { - if q.inputConn != nil { + if inputConn := q.inputConn; inputConn != nil { // copy inputConn avoid be nil in for loop for { var packet Packet - packet, err = ReadPacket(q.inputConn) + packet, err = ReadPacket(inputConn) if err != nil { return } From da24810da2d810ad65492b2f4dd890062e92dee4 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 21 Sep 2023 16:41:31 +0800 Subject: [PATCH 50/66] chore: support set cwnd for hy2 too --- adapter/outbound/hysteria2.go | 2 ++ go.mod | 2 +- go.sum | 4 ++-- listener/config/hysteria2.go | 1 + listener/inbound/hysteria2.go | 2 ++ listener/sing_hysteria2/server.go | 1 + transport/tuic/common/congestion.go | 6 ++++-- 7 files changed, 13 insertions(+), 5 deletions(-) diff --git a/adapter/outbound/hysteria2.go b/adapter/outbound/hysteria2.go index 8963da66..46e052e6 100644 --- a/adapter/outbound/hysteria2.go +++ b/adapter/outbound/hysteria2.go @@ -54,6 +54,7 @@ type Hysteria2Option struct { ALPN []string `proxy:"alpn,omitempty"` CustomCA string `proxy:"ca,omitempty"` CustomCAString string `proxy:"ca-str,omitempty"` + CWND int `proxy:"cwnd,omitempty"` } type hy2SingDialer struct { @@ -191,6 +192,7 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { Password: option.Password, TLSConfig: tlsConfig, UDPDisabled: false, + CWND: option.CWND, } client, err := hysteria2.NewClient(clientOptions) diff --git a/go.mod b/go.mod index d16ccb01..487fe4c5 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/mdlayher/netlink v1.7.2 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf - github.com/metacubex/sing-quic v0.0.0-20230921015854-1ed89eed54d5 + github.com/metacubex/sing-quic v0.0.0-20230921083613-f4d3299bca83 github.com/metacubex/sing-shadowsocks v0.2.5 github.com/metacubex/sing-shadowsocks2 v0.1.4 github.com/metacubex/sing-tun v0.1.12 diff --git a/go.sum b/go.sum index 45cb3cbb..5386ec0d 100644 --- a/go.sum +++ b/go.sum @@ -99,8 +99,8 @@ github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf h1:hflzPbb2M+ github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf/go.mod h1:7RCcKJJk1DMeNQQNnYKS+7FqftqPfG031oP8jrYRMw8= github.com/metacubex/sing v0.0.0-20230921005553-6eacdd2c7a24 h1:652uMd78eKMU7/sVkW8qqAdZkJaiDoUflfCs5LHvb0Q= github.com/metacubex/sing v0.0.0-20230921005553-6eacdd2c7a24/go.mod h1:GQ673iPfUnkbK/dIPkfd1Xh1MjOGo36gkl/mkiHY7Jg= -github.com/metacubex/sing-quic v0.0.0-20230921015854-1ed89eed54d5 h1:XhWilr6vJoXy4n/sP2datek28FTF3s3rWHhezdFFD8s= -github.com/metacubex/sing-quic v0.0.0-20230921015854-1ed89eed54d5/go.mod h1:oGpQmqe5tj3sPdPWCNLbBoUSwqd+Z6SqVO7TlMNVnH4= +github.com/metacubex/sing-quic v0.0.0-20230921083613-f4d3299bca83 h1:xk2Uob4xSGCYRuo+UsDParhi1cam6G+11wrwGd6EvhM= +github.com/metacubex/sing-quic v0.0.0-20230921083613-f4d3299bca83/go.mod h1:oGpQmqe5tj3sPdPWCNLbBoUSwqd+Z6SqVO7TlMNVnH4= github.com/metacubex/sing-shadowsocks v0.2.5 h1:O2RRSHlKGEpAVG/OHJQxyHqDy8uvvdCW/oW2TDBOIhc= github.com/metacubex/sing-shadowsocks v0.2.5/go.mod h1:Xz2uW9BEYGEoA8B4XEpoxt7ERHClFCwsMAvWaruoyMo= github.com/metacubex/sing-shadowsocks2 v0.1.4 h1:OOCf8lgsVcpTOJUeaFAMzyKVebaQOBnKirDdUdBoKIE= diff --git a/listener/config/hysteria2.go b/listener/config/hysteria2.go index e8e9c09c..5520babc 100644 --- a/listener/config/hysteria2.go +++ b/listener/config/hysteria2.go @@ -16,6 +16,7 @@ type Hysteria2Server struct { Down string `yaml:"down" json:"down,omitempty"` IgnoreClientBandwidth bool `yaml:"ignore-client-bandwidth" json:"ignore-client-bandwidth,omitempty"` Masquerade string `yaml:"masquerade" json:"masquerade,omitempty"` + CWND int `yaml:"cwnd" json:"cwnd,omitempty"` } func (h Hysteria2Server) String() string { diff --git a/listener/inbound/hysteria2.go b/listener/inbound/hysteria2.go index 00feadb1..430d0e68 100644 --- a/listener/inbound/hysteria2.go +++ b/listener/inbound/hysteria2.go @@ -20,6 +20,7 @@ type Hysteria2Option struct { Down string `inbound:"down,omitempty"` IgnoreClientBandwidth bool `inbound:"ignore-client-bandwidth,omitempty"` Masquerade string `inbound:"masquerade,omitempty"` + CWND int `inbound:"cwnd,omitempty"` } func (o Hysteria2Option) Equal(config C.InboundConfig) bool { @@ -55,6 +56,7 @@ func NewHysteria2(options *Hysteria2Option) (*Hysteria2, error) { Down: options.Down, IgnoreClientBandwidth: options.IgnoreClientBandwidth, Masquerade: options.Masquerade, + CWND: options.CWND, }, }, nil } diff --git a/listener/sing_hysteria2/server.go b/listener/sing_hysteria2/server.go index 1d6ab29a..4e0a7c07 100644 --- a/listener/sing_hysteria2/server.go +++ b/listener/sing_hysteria2/server.go @@ -111,6 +111,7 @@ func New(config LC.Hysteria2Server, tcpIn chan<- C.ConnContext, udpIn chan<- C.P IgnoreClientBandwidth: config.IgnoreClientBandwidth, Handler: h, MasqueradeHandler: masqueradeHandler, + CWND: config.CWND, }) if err != nil { return nil, err diff --git a/transport/tuic/common/congestion.go b/transport/tuic/common/congestion.go index 8b8018b5..36ee01a1 100644 --- a/transport/tuic/common/congestion.go +++ b/transport/tuic/common/congestion.go @@ -13,7 +13,9 @@ const ( ) func SetCongestionController(quicConn quic.Connection, cc string, cwnd int) { - CWND := c.ByteCount(cwnd) + if cwnd == 0 { + cwnd = 32 + } switch cc { case "cubic": quicConn.SetCongestionControl( @@ -38,7 +40,7 @@ func SetCongestionController(quicConn quic.Connection, cc string, cwnd int) { congestion.NewBBRSender( congestion.DefaultClock{}, congestion.GetInitialPacketSize(quicConn.RemoteAddr()), - CWND*congestion.InitialMaxDatagramSize, + c.ByteCount(cwnd)*congestion.InitialMaxDatagramSize, congestion.DefaultBBRMaxCongestionWindow*congestion.InitialMaxDatagramSize, ), ) From f6bf9c08577060bb199c2f746c7d91dd3c0ca7b9 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Thu, 21 Sep 2023 17:25:01 +0800 Subject: [PATCH 51/66] feat: converter support hysteria2 --- common/convert/converter.go | 32 +++++++++++++++++++++++++++++ common/convert/converter_test.go | 35 ++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 common/convert/converter_test.go diff --git a/common/convert/converter.go b/common/convert/converter.go index ddc5c944..5a618f42 100644 --- a/common/convert/converter.go +++ b/common/convert/converter.go @@ -68,7 +68,39 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { hysteria["skip-cert-verify"], _ = strconv.ParseBool(query.Get("insecure")) proxies = append(proxies, hysteria) + case "hysteria2": + urlHysteria2, err := url.Parse(line) + if err != nil { + continue + } + query := urlHysteria2.Query() + name := uniqueName(names, urlHysteria2.Fragment) + hysteria2 := make(map[string]any, 20) + + hysteria2["name"] = name + hysteria2["type"] = scheme + hysteria2["server"] = urlHysteria2.Hostname() + if port := urlHysteria2.Port(); port != "" { + hysteria2["port"] = port + } else { + hysteria2["port"] = "443" + } + hysteria2["obfs"] = query.Get("obfs") + hysteria2["obfs-password"] = query.Get("obfs-password") + hysteria2["sni"] = query.Get("sni") + hysteria2["skip-cert-verify"], _ = strconv.ParseBool(query.Get("insecure")) + if alpn := query.Get("alpn"); alpn != "" { + hysteria2["alpn"] = strings.Split(alpn, ",") + } + if auth := urlHysteria2.User.String(); auth != "" { + hysteria2["password"] = auth + } + hysteria2["fingerprint"] = query.Get("pinSHA256") + hysteria2["down"] = query.Get("down") + hysteria2["up"] = query.Get("up") + + proxies = append(proxies, hysteria2) case "tuic": // A temporary unofficial TUIC share link standard // Modified from https://github.com/daeuniverse/dae/discussions/182 diff --git a/common/convert/converter_test.go b/common/convert/converter_test.go new file mode 100644 index 00000000..83b41c4c --- /dev/null +++ b/common/convert/converter_test.go @@ -0,0 +1,35 @@ +package convert + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// https://v2.hysteria.network/zh/docs/developers/URI-Scheme/ +func TestConvertsV2Ray_normal(t *testing.T) { + hy2test := "hysteria2://letmein@example.com:8443/?insecure=1&obfs=salamander&obfs-password=gawrgura&pinSHA256=deadbeef&sni=real.example.com&up=114&down=514&alpn=h3,h4#hy2test" + + expected := []map[string]interface{}{ + { + "name": "hy2test", + "type": "hysteria2", + "server": "example.com", + "port": "8443", + "sni": "real.example.com", + "obfs": "salamander", + "obfs-password": "gawrgura", + "alpn": []string{"h3", "h4"}, + "password": "letmein", + "up": "114", + "down": "514", + "skip-cert-verify": true, + "fingerprint": "deadbeef", + }, + } + + proxies, err := ConvertsV2Ray([]byte(hy2test)) + + assert.Nil(t, err) + assert.Equal(t, expected, proxies) +} From 7f49c91267bb08a78296780cd810ad3091f5eaf1 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 21 Sep 2023 23:36:25 +0800 Subject: [PATCH 52/66] fix: hy2 udp not working --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 487fe4c5..0c7b2e31 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/mdlayher/netlink v1.7.2 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf - github.com/metacubex/sing-quic v0.0.0-20230921083613-f4d3299bca83 + github.com/metacubex/sing-quic v0.0.0-20230921153541-9d96dbf04c89 github.com/metacubex/sing-shadowsocks v0.2.5 github.com/metacubex/sing-shadowsocks2 v0.1.4 github.com/metacubex/sing-tun v0.1.12 diff --git a/go.sum b/go.sum index 5386ec0d..d3b3b626 100644 --- a/go.sum +++ b/go.sum @@ -99,8 +99,8 @@ github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf h1:hflzPbb2M+ github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf/go.mod h1:7RCcKJJk1DMeNQQNnYKS+7FqftqPfG031oP8jrYRMw8= github.com/metacubex/sing v0.0.0-20230921005553-6eacdd2c7a24 h1:652uMd78eKMU7/sVkW8qqAdZkJaiDoUflfCs5LHvb0Q= github.com/metacubex/sing v0.0.0-20230921005553-6eacdd2c7a24/go.mod h1:GQ673iPfUnkbK/dIPkfd1Xh1MjOGo36gkl/mkiHY7Jg= -github.com/metacubex/sing-quic v0.0.0-20230921083613-f4d3299bca83 h1:xk2Uob4xSGCYRuo+UsDParhi1cam6G+11wrwGd6EvhM= -github.com/metacubex/sing-quic v0.0.0-20230921083613-f4d3299bca83/go.mod h1:oGpQmqe5tj3sPdPWCNLbBoUSwqd+Z6SqVO7TlMNVnH4= +github.com/metacubex/sing-quic v0.0.0-20230921153541-9d96dbf04c89 h1:8tgapDMa9MNr4FNXBFgkC9Te0SjRVgByJ727GiQpiHE= +github.com/metacubex/sing-quic v0.0.0-20230921153541-9d96dbf04c89/go.mod h1:oGpQmqe5tj3sPdPWCNLbBoUSwqd+Z6SqVO7TlMNVnH4= github.com/metacubex/sing-shadowsocks v0.2.5 h1:O2RRSHlKGEpAVG/OHJQxyHqDy8uvvdCW/oW2TDBOIhc= github.com/metacubex/sing-shadowsocks v0.2.5/go.mod h1:Xz2uW9BEYGEoA8B4XEpoxt7ERHClFCwsMAvWaruoyMo= github.com/metacubex/sing-shadowsocks2 v0.1.4 h1:OOCf8lgsVcpTOJUeaFAMzyKVebaQOBnKirDdUdBoKIE= From 4fe7a463c51626d195b9a7408a5e7bb8522e24ba Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 21 Sep 2023 23:49:45 +0800 Subject: [PATCH 53/66] chore: limit tuicv5's maxUdpRelayPacketSize up to 1200-PacketOverHead --- adapter/outbound/tuic.go | 6 +++++- transport/tuic/server.go | 4 ++++ transport/tuic/tuic.go | 1 + transport/tuic/v5/frag.go | 5 +++++ 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index 4d826912..b1032e8c 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -323,6 +323,10 @@ func NewTuic(option TuicOption) (*Tuic, error) { t.client = tuic.NewPoolClientV4(clientOption) } else { + maxUdpRelayPacketSize := option.MaxUdpRelayPacketSize + if maxUdpRelayPacketSize > tuic.MaxFragSizeV5 { + maxUdpRelayPacketSize = tuic.MaxFragSizeV5 + } clientOption := &tuic.ClientOptionV5{ TlsConfig: tlsConfig, QuicConfig: quicConfig, @@ -331,7 +335,7 @@ func NewTuic(option TuicOption) (*Tuic, error) { UdpRelayMode: udpRelayMode, CongestionController: option.CongestionController, ReduceRtt: option.ReduceRtt, - MaxUdpRelayPacketSize: option.MaxUdpRelayPacketSize, + MaxUdpRelayPacketSize: maxUdpRelayPacketSize, MaxOpenStreams: clientMaxOpenStreams, CWND: option.CWND, } diff --git a/transport/tuic/server.go b/transport/tuic/server.go index a6f91b88..cabc04e0 100644 --- a/transport/tuic/server.go +++ b/transport/tuic/server.go @@ -223,6 +223,10 @@ func NewServer(option *ServerOption, pc net.PacketConn) (*Server, error) { } } if len(option.Users) > 0 { + maxUdpRelayPacketSize := option.MaxUdpRelayPacketSize + if maxUdpRelayPacketSize > MaxFragSizeV5 { + maxUdpRelayPacketSize = MaxFragSizeV5 + } server.optionV5 = &v5.ServerOption{ HandleTcpFn: option.HandleTcpFn, HandleUdpFn: option.HandleUdpFn, diff --git a/transport/tuic/tuic.go b/transport/tuic/tuic.go index 8832ef91..387a152c 100644 --- a/transport/tuic/tuic.go +++ b/transport/tuic/tuic.go @@ -30,6 +30,7 @@ const DefaultConnectionReceiveWindow = common.DefaultConnectionReceiveWindow var GenTKN = v4.GenTKN var PacketOverHeadV4 = v4.PacketOverHead var PacketOverHeadV5 = v5.PacketOverHead +var MaxFragSizeV5 = v5.MaxFragSize type UdpRelayMode = common.UdpRelayMode diff --git a/transport/tuic/v5/frag.go b/transport/tuic/v5/frag.go index ae9dbf10..8df9f785 100644 --- a/transport/tuic/v5/frag.go +++ b/transport/tuic/v5/frag.go @@ -9,6 +9,11 @@ import ( "github.com/metacubex/quic-go" ) +// MaxFragSize is a safe udp relay packet size +// because tuicv5 support udp fragment so we unneeded to do a magic modify for quic-go to increase MaxDatagramFrameSize +// it may not work fine in some platform +var MaxFragSize = 1200 - PacketOverHead + func fragWriteNative(quicConn quic.Connection, packet Packet, buf *bytes.Buffer, fragSize int) (err error) { fullPayload := packet.DATA off := 0 From 90a5aa609a34e22a97e123842cee7a61670d187b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 22 Sep 2023 00:11:57 +0800 Subject: [PATCH 54/66] fix: uot read failed --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 0c7b2e31..6c67a38a 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/mdlayher/netlink v1.7.2 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf - github.com/metacubex/sing-quic v0.0.0-20230921153541-9d96dbf04c89 + github.com/metacubex/sing-quic v0.0.0-20230921160948-82175eb07a81 github.com/metacubex/sing-shadowsocks v0.2.5 github.com/metacubex/sing-shadowsocks2 v0.1.4 github.com/metacubex/sing-tun v0.1.12 @@ -105,4 +105,4 @@ require ( golang.org/x/tools v0.13.0 // indirect ) -replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20230921005553-6eacdd2c7a24 +replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20230921160249-edb949c9c140 diff --git a/go.sum b/go.sum index d3b3b626..0105d57d 100644 --- a/go.sum +++ b/go.sum @@ -97,10 +97,10 @@ github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475 h1:qSEOvPPaMrWggF github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475/go.mod h1:wehEpqiogdeyncfhckJP5gD2LtBgJW0wnDC24mJ+8Jg= github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf h1:hflzPbb2M+3uUOZEVO72MKd2R62xEermoVaNhJOzBR8= github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf/go.mod h1:7RCcKJJk1DMeNQQNnYKS+7FqftqPfG031oP8jrYRMw8= -github.com/metacubex/sing v0.0.0-20230921005553-6eacdd2c7a24 h1:652uMd78eKMU7/sVkW8qqAdZkJaiDoUflfCs5LHvb0Q= -github.com/metacubex/sing v0.0.0-20230921005553-6eacdd2c7a24/go.mod h1:GQ673iPfUnkbK/dIPkfd1Xh1MjOGo36gkl/mkiHY7Jg= -github.com/metacubex/sing-quic v0.0.0-20230921153541-9d96dbf04c89 h1:8tgapDMa9MNr4FNXBFgkC9Te0SjRVgByJ727GiQpiHE= -github.com/metacubex/sing-quic v0.0.0-20230921153541-9d96dbf04c89/go.mod h1:oGpQmqe5tj3sPdPWCNLbBoUSwqd+Z6SqVO7TlMNVnH4= +github.com/metacubex/sing v0.0.0-20230921160249-edb949c9c140 h1:qiTekhMDwY2vXARJx1D9EIEdtllbL7+ZBzHX9DQpWs4= +github.com/metacubex/sing v0.0.0-20230921160249-edb949c9c140/go.mod h1:GQ673iPfUnkbK/dIPkfd1Xh1MjOGo36gkl/mkiHY7Jg= +github.com/metacubex/sing-quic v0.0.0-20230921160948-82175eb07a81 h1:6g+ohVa8FQLXz/ATmped/4kWuK0HKvhy1hwzQXyF0EI= +github.com/metacubex/sing-quic v0.0.0-20230921160948-82175eb07a81/go.mod h1:oGpQmqe5tj3sPdPWCNLbBoUSwqd+Z6SqVO7TlMNVnH4= github.com/metacubex/sing-shadowsocks v0.2.5 h1:O2RRSHlKGEpAVG/OHJQxyHqDy8uvvdCW/oW2TDBOIhc= github.com/metacubex/sing-shadowsocks v0.2.5/go.mod h1:Xz2uW9BEYGEoA8B4XEpoxt7ERHClFCwsMAvWaruoyMo= github.com/metacubex/sing-shadowsocks2 v0.1.4 h1:OOCf8lgsVcpTOJUeaFAMzyKVebaQOBnKirDdUdBoKIE= From d48f9c2a6cc96a27df45ce74754604c5ac71320c Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 22 Sep 2023 14:45:34 +0800 Subject: [PATCH 55/66] chore: rebuild ca parsing --- adapter/outbound/http.go | 22 +++----- adapter/outbound/hysteria.go | 39 ++------------ adapter/outbound/hysteria2.go | 39 ++------------ adapter/outbound/socks5.go | 13 ++--- adapter/outbound/trojan.go | 12 ++--- adapter/outbound/tuic.go | 39 ++------------ adapter/outbound/vless.go | 13 ++--- adapter/outbound/vmess.go | 12 ++--- component/{tls => ca}/config.go | 73 +++++++++++++++++---------- component/http/http.go | 5 +- dns/client.go | 4 +- dns/doh.go | 4 +- dns/doq.go | 4 +- hub/executor/executor.go | 6 +-- transport/sing-shadowtls/shadowtls.go | 10 ++-- transport/trojan/trojan.go | 14 +++-- transport/v2ray-plugin/websocket.go | 13 ++--- transport/vmess/tls.go | 14 +++-- 18 files changed, 120 insertions(+), 216 deletions(-) rename component/{tls => ca}/config.go (63%) diff --git a/adapter/outbound/http.go b/adapter/outbound/http.go index acc75d37..19074bb3 100644 --- a/adapter/outbound/http.go +++ b/adapter/outbound/http.go @@ -14,9 +14,9 @@ import ( "strconv" N "github.com/Dreamacro/clash/common/net" + "github.com/Dreamacro/clash/component/ca" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/proxydialer" - tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" ) @@ -157,19 +157,13 @@ func NewHttp(option HttpOption) (*Http, error) { if option.SNI != "" { sni = option.SNI } - if len(option.Fingerprint) == 0 { - tlsConfig = tlsC.GetGlobalTLSConfig(&tls.Config{ - InsecureSkipVerify: option.SkipCertVerify, - ServerName: sni, - }) - } else { - var err error - if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(&tls.Config{ - InsecureSkipVerify: option.SkipCertVerify, - ServerName: sni, - }, option.Fingerprint); err != nil { - return nil, err - } + var err error + tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(&tls.Config{ + InsecureSkipVerify: option.SkipCertVerify, + ServerName: sni, + }, option.Fingerprint) + if err != nil { + return nil, err } } diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index 7cd4ea76..8a9d6258 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -2,15 +2,11 @@ package outbound import ( "context" - "crypto/sha256" "crypto/tls" "encoding/base64" - "encoding/hex" - "encoding/pem" "fmt" "net" "net/netip" - "os" "strconv" "time" @@ -18,9 +14,9 @@ import ( "github.com/metacubex/quic-go/congestion" M "github.com/sagernet/sing/common/metadata" + "github.com/Dreamacro/clash/component/ca" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/proxydialer" - tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" hyCongestion "github.com/Dreamacro/clash/transport/hysteria/congestion" @@ -150,37 +146,10 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { MinVersion: tls.VersionTLS13, } - var bs []byte var err error - if len(option.CustomCA) > 0 { - bs, err = os.ReadFile(option.CustomCA) - if err != nil { - return nil, fmt.Errorf("hysteria %s load ca error: %w", addr, err) - } - } else if option.CustomCAString != "" { - bs = []byte(option.CustomCAString) - } - - if len(bs) > 0 { - block, _ := pem.Decode(bs) - if block == nil { - return nil, fmt.Errorf("CA cert is not PEM") - } - - fpBytes := sha256.Sum256(block.Bytes) - if len(option.Fingerprint) == 0 { - option.Fingerprint = hex.EncodeToString(fpBytes[:]) - } - } - - if len(option.Fingerprint) != 0 { - var err error - tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint) - if err != nil { - return nil, err - } - } else { - tlsConfig = tlsC.GetGlobalTLSConfig(tlsConfig) + tlsConfig, err = ca.GetTLSConfig(tlsConfig, option.Fingerprint, option.CustomCA, option.CustomCAString) + if err != nil { + return nil, err } if len(option.ALPN) > 0 { diff --git a/adapter/outbound/hysteria2.go b/adapter/outbound/hysteria2.go index 46e052e6..e7ad91df 100644 --- a/adapter/outbound/hysteria2.go +++ b/adapter/outbound/hysteria2.go @@ -2,21 +2,17 @@ package outbound import ( "context" - "crypto/sha256" "crypto/tls" - "encoding/hex" - "encoding/pem" "errors" "fmt" "net" - "os" "runtime" "strconv" CN "github.com/Dreamacro/clash/common/net" + "github.com/Dreamacro/clash/component/ca" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/proxydialer" - tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" tuicCommon "github.com/Dreamacro/clash/transport/tuic/common" @@ -143,37 +139,10 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { MinVersion: tls.VersionTLS13, } - var bs []byte var err error - if len(option.CustomCA) > 0 { - bs, err = os.ReadFile(option.CustomCA) - if err != nil { - return nil, fmt.Errorf("hysteria %s load ca error: %w", option.Name, err) - } - } else if option.CustomCAString != "" { - bs = []byte(option.CustomCAString) - } - - if len(bs) > 0 { - block, _ := pem.Decode(bs) - if block == nil { - return nil, fmt.Errorf("CA cert is not PEM") - } - - fpBytes := sha256.Sum256(block.Bytes) - if len(option.Fingerprint) == 0 { - option.Fingerprint = hex.EncodeToString(fpBytes[:]) - } - } - - if len(option.Fingerprint) != 0 { - var err error - tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint) - if err != nil { - return nil, err - } - } else { - tlsConfig = tlsC.GetGlobalTLSConfig(tlsConfig) + tlsConfig, err = ca.GetTLSConfig(tlsConfig, option.Fingerprint, option.CustomCA, option.CustomCAString) + if err != nil { + return nil, err } if len(option.ALPN) > 0 { diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index 2e9bccd6..d857172e 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -10,9 +10,9 @@ import ( "strconv" N "github.com/Dreamacro/clash/common/net" + "github.com/Dreamacro/clash/component/ca" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/proxydialer" - tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/socks5" ) @@ -180,13 +180,10 @@ func NewSocks5(option Socks5Option) (*Socks5, error) { ServerName: option.Server, } - if len(option.Fingerprint) == 0 { - tlsConfig = tlsC.GetGlobalTLSConfig(tlsConfig) - } else { - var err error - if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint); err != nil { - return nil, err - } + var err error + tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint) + if err != nil { + return nil, err } } diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index 6339b476..337f2a38 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -9,6 +9,7 @@ import ( "strconv" N "github.com/Dreamacro/clash/common/net" + "github.com/Dreamacro/clash/component/ca" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/proxydialer" tlsC "github.com/Dreamacro/clash/component/tls" @@ -280,13 +281,10 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { ServerName: tOption.ServerName, } - if len(option.Fingerprint) == 0 { - tlsConfig = tlsC.GetGlobalTLSConfig(tlsConfig) - } else { - var err error - if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint); err != nil { - return nil, err - } + var err error + tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint) + if err != nil { + return nil, err } t.transport = gun.NewHTTP2Client(dialFn, tlsConfig, tOption.ClientFingerprint, t.realityConfig) diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index b1032e8c..93e49dc7 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -2,22 +2,18 @@ package outbound import ( "context" - "crypto/sha256" "crypto/tls" - "encoding/hex" - "encoding/pem" "errors" "fmt" "math" "net" - "os" "strconv" "time" + "github.com/Dreamacro/clash/component/ca" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/proxydialer" "github.com/Dreamacro/clash/component/resolver" - tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/tuic" @@ -162,37 +158,10 @@ func NewTuic(option TuicOption) (*Tuic, error) { tlsConfig.ServerName = option.SNI } - var bs []byte var err error - if len(option.CustomCA) > 0 { - bs, err = os.ReadFile(option.CustomCA) - if err != nil { - return nil, fmt.Errorf("tuic %s load ca error: %w", addr, err) - } - } else if option.CustomCAString != "" { - bs = []byte(option.CustomCAString) - } - - if len(bs) > 0 { - block, _ := pem.Decode(bs) - if block == nil { - return nil, fmt.Errorf("CA cert is not PEM") - } - - fpBytes := sha256.Sum256(block.Bytes) - if len(option.Fingerprint) == 0 { - option.Fingerprint = hex.EncodeToString(fpBytes[:]) - } - } - - if len(option.Fingerprint) != 0 { - var err error - tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint) - if err != nil { - return nil, err - } - } else { - tlsConfig = tlsC.GetGlobalTLSConfig(tlsConfig) + tlsConfig, err = ca.GetTLSConfig(tlsConfig, option.Fingerprint, option.CustomCA, option.CustomCAString) + if err != nil { + return nil, err } if option.ALPN != nil { // structure's Decode will ensure value not nil when input has value even it was set an empty array diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index 81408e5f..037f3367 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -15,6 +15,7 @@ import ( "github.com/Dreamacro/clash/common/convert" N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/utils" + "github.com/Dreamacro/clash/component/ca" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/proxydialer" "github.com/Dreamacro/clash/component/resolver" @@ -110,13 +111,9 @@ func (v *Vless) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M NextProtos: []string{"http/1.1"}, } - if len(v.option.Fingerprint) == 0 { - wsOpts.TLSConfig = tlsC.GetGlobalTLSConfig(tlsConfig) - } else { - wsOpts.TLSConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, v.option.Fingerprint) - if err != nil { - return nil, err - } + wsOpts.TLSConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, v.option.Fingerprint) + if err != nil { + return nil, err } if v.option.ServerName != "" { @@ -592,7 +589,7 @@ func NewVless(option VlessOption) (*Vless, error) { } var tlsConfig *tls.Config if option.TLS { - tlsConfig = tlsC.GetGlobalTLSConfig(&tls.Config{ + tlsConfig = ca.GetGlobalTLSConfig(&tls.Config{ InsecureSkipVerify: v.option.SkipCertVerify, ServerName: v.option.ServerName, }) diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 3e7694d1..db654580 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -13,6 +13,7 @@ import ( N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/utils" + "github.com/Dreamacro/clash/component/ca" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/proxydialer" "github.com/Dreamacro/clash/component/resolver" @@ -127,12 +128,9 @@ func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M NextProtos: []string{"http/1.1"}, } - if len(v.option.Fingerprint) == 0 { - wsOpts.TLSConfig = tlsC.GetGlobalTLSConfig(tlsConfig) - } else { - if wsOpts.TLSConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, v.option.Fingerprint); err != nil { - return nil, err - } + wsOpts.TLSConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, v.option.Fingerprint) + if err != nil { + return nil, err } if v.option.ServerName != "" { @@ -483,7 +481,7 @@ func NewVmess(option VmessOption) (*Vmess, error) { } var tlsConfig *tls.Config if option.TLS { - tlsConfig = tlsC.GetGlobalTLSConfig(&tls.Config{ + tlsConfig = ca.GetGlobalTLSConfig(&tls.Config{ InsecureSkipVerify: v.option.SkipCertVerify, ServerName: v.option.ServerName, }) diff --git a/component/tls/config.go b/component/ca/config.go similarity index 63% rename from component/tls/config.go rename to component/ca/config.go index d7382f7c..03fb007c 100644 --- a/component/tls/config.go +++ b/component/ca/config.go @@ -1,4 +1,4 @@ -package tls +package ca import ( "bytes" @@ -8,12 +8,13 @@ import ( "encoding/hex" "errors" "fmt" + "os" "strings" "sync" ) var trustCerts []*x509.Certificate -var certPool *x509.CertPool +var globalCertPool *x509.CertPool var mutex sync.RWMutex var errNotMatch = errors.New("certificate fingerprints do not match") @@ -33,12 +34,12 @@ func AddCertificate(certificate string) error { func initializeCertPool() { var err error - certPool, err = x509.SystemCertPool() + globalCertPool, err = x509.SystemCertPool() if err != nil { - certPool = x509.NewCertPool() + globalCertPool = x509.NewCertPool() } for _, cert := range trustCerts { - certPool.AddCert(cert) + globalCertPool.AddCert(cert) } } @@ -53,15 +54,15 @@ func getCertPool() *x509.CertPool { if len(trustCerts) == 0 { return nil } - if certPool == nil { + if globalCertPool == nil { mutex.Lock() defer mutex.Unlock() - if certPool != nil { - return certPool + if globalCertPool != nil { + return globalCertPool } initializeCertPool() } - return certPool + return globalCertPool } func verifyFingerprint(fingerprint *[32]byte) func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { @@ -94,29 +95,49 @@ func convertFingerprint(fingerprint string) (*[32]byte, error) { return (*[32]byte)(fpByte), nil } -func GetDefaultTLSConfig() *tls.Config { - return GetGlobalTLSConfig(nil) +// GetTLSConfig specified fingerprint, customCA and customCAString +func GetTLSConfig(tlsConfig *tls.Config, fingerprint string, customCA string, customCAString string) (*tls.Config, error) { + if tlsConfig == nil { + tlsConfig = &tls.Config{} + } + var certificate []byte + var err error + if len(customCA) > 0 { + certificate, err = os.ReadFile(customCA) + if err != nil { + return nil, fmt.Errorf("load ca error: %w", err) + } + } else if customCAString != "" { + certificate = []byte(customCAString) + } + if len(certificate) > 0 { + certPool := x509.NewCertPool() + if !certPool.AppendCertsFromPEM(certificate) { + return nil, fmt.Errorf("failed to parse certificate:\n\n %s", certificate) + } + tlsConfig.RootCAs = certPool + } else { + tlsConfig.RootCAs = getCertPool() + } + if len(fingerprint) > 0 { + var fingerprintBytes *[32]byte + fingerprintBytes, err = convertFingerprint(fingerprint) + if err != nil { + return nil, err + } + tlsConfig = GetGlobalTLSConfig(tlsConfig) + tlsConfig.VerifyPeerCertificate = verifyFingerprint(fingerprintBytes) + tlsConfig.InsecureSkipVerify = true + } + return tlsConfig, nil } // GetSpecifiedFingerprintTLSConfig specified fingerprint func GetSpecifiedFingerprintTLSConfig(tlsConfig *tls.Config, fingerprint string) (*tls.Config, error) { - if fingerprintBytes, err := convertFingerprint(fingerprint); err != nil { - return nil, err - } else { - tlsConfig = GetGlobalTLSConfig(tlsConfig) - tlsConfig.VerifyPeerCertificate = verifyFingerprint(fingerprintBytes) - tlsConfig.InsecureSkipVerify = true - return tlsConfig, nil - } + return GetTLSConfig(tlsConfig, fingerprint, "", "") } func GetGlobalTLSConfig(tlsConfig *tls.Config) *tls.Config { - certPool := getCertPool() - if tlsConfig == nil { - return &tls.Config{ - RootCAs: certPool, - } - } - tlsConfig.RootCAs = certPool + tlsConfig, _ = GetTLSConfig(tlsConfig, "", "", "") return tlsConfig } diff --git a/component/http/http.go b/component/http/http.go index c5172fcb..8e682e94 100644 --- a/component/http/http.go +++ b/component/http/http.go @@ -2,6 +2,7 @@ package http import ( "context" + "crypto/tls" "io" "net" "net/http" @@ -9,7 +10,7 @@ import ( "strings" "time" - "github.com/Dreamacro/clash/component/tls" + "github.com/Dreamacro/clash/component/ca" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/listener/inner" ) @@ -58,7 +59,7 @@ func HttpRequest(ctx context.Context, url, method string, header map[string][]st return d.DialContext(ctx, network, address) } }, - TLSClientConfig: tls.GetDefaultTLSConfig(), + TLSClientConfig: ca.GetGlobalTLSConfig(&tls.Config{}), } client := http.Client{Transport: transport} diff --git a/dns/client.go b/dns/client.go index ba83412b..56f55668 100644 --- a/dns/client.go +++ b/dns/client.go @@ -9,9 +9,9 @@ import ( "strings" "github.com/Dreamacro/clash/common/atomic" + "github.com/Dreamacro/clash/component/ca" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/resolver" - tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" D "github.com/miekg/dns" @@ -99,7 +99,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) ch := make(chan result, 1) go func() { if strings.HasSuffix(c.Client.Net, "tls") { - conn = tls.Client(conn, tlsC.GetGlobalTLSConfig(c.Client.TLSConfig)) + conn = tls.Client(conn, ca.GetGlobalTLSConfig(c.Client.TLSConfig)) } msg, _, err := c.Client.ExchangeWithConn(m, &D.Conn{ diff --git a/dns/doh.go b/dns/doh.go index 49e502fd..0d84fc4f 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -15,7 +15,7 @@ import ( "sync" "time" - tlsC "github.com/Dreamacro/clash/component/tls" + "github.com/Dreamacro/clash/component/ca" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" "github.com/metacubex/quic-go" @@ -382,7 +382,7 @@ func (doh *dnsOverHTTPS) createClient(ctx context.Context) (*http.Client, error) // HTTP3 is enabled in the upstream options). If this attempt is successful, // it returns an HTTP3 transport, otherwise it returns the H1/H2 transport. func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripper, err error) { - tlsConfig := tlsC.GetGlobalTLSConfig( + tlsConfig := ca.GetGlobalTLSConfig( &tls.Config{ InsecureSkipVerify: false, MinVersion: tls.VersionTLS12, diff --git a/dns/doq.go b/dns/doq.go index f0016d79..afa8259a 100644 --- a/dns/doq.go +++ b/dns/doq.go @@ -12,7 +12,7 @@ import ( "sync" "time" - tlsC "github.com/Dreamacro/clash/component/tls" + "github.com/Dreamacro/clash/component/ca" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" "github.com/metacubex/quic-go" @@ -330,7 +330,7 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio return nil, err } - tlsConfig := tlsC.GetGlobalTLSConfig( + tlsConfig := ca.GetGlobalTLSConfig( &tls.Config{ ServerName: host, InsecureSkipVerify: false, diff --git a/hub/executor/executor.go b/hub/executor/executor.go index b840ba48..88cdfd6c 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -16,6 +16,7 @@ import ( "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/adapter/outboundgroup" "github.com/Dreamacro/clash/component/auth" + "github.com/Dreamacro/clash/component/ca" "github.com/Dreamacro/clash/component/dialer" G "github.com/Dreamacro/clash/component/geodata" "github.com/Dreamacro/clash/component/iface" @@ -23,7 +24,6 @@ import ( "github.com/Dreamacro/clash/component/profile/cachefile" "github.com/Dreamacro/clash/component/resolver" SNI "github.com/Dreamacro/clash/component/sniffer" - CTLS "github.com/Dreamacro/clash/component/tls" "github.com/Dreamacro/clash/component/trie" "github.com/Dreamacro/clash/config" C "github.com/Dreamacro/clash/constant" @@ -83,9 +83,9 @@ func ApplyConfig(cfg *config.Config, force bool) { tunnel.OnSuspend() - CTLS.ResetCertificate() + ca.ResetCertificate() for _, c := range cfg.TLS.CustomTrustCert { - if err := CTLS.AddCertificate(c); err != nil { + if err := ca.AddCertificate(c); err != nil { log.Warnln("%s\nadd error: %s", c, err.Error()) } } diff --git a/transport/sing-shadowtls/shadowtls.go b/transport/sing-shadowtls/shadowtls.go index 0e1e95c0..6d731ae6 100644 --- a/transport/sing-shadowtls/shadowtls.go +++ b/transport/sing-shadowtls/shadowtls.go @@ -5,6 +5,7 @@ import ( "crypto/tls" "net" + "github.com/Dreamacro/clash/component/ca" tlsC "github.com/Dreamacro/clash/component/tls" "github.com/Dreamacro/clash/log" @@ -39,12 +40,9 @@ func NewShadowTLS(ctx context.Context, conn net.Conn, option *ShadowTLSOption) ( } 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 - } + tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint) + if err != nil { + return nil, err } tlsHandshake := shadowtls.DefaultTLSHandshakeFunc(option.Password, tlsConfig) diff --git a/transport/trojan/trojan.go b/transport/trojan/trojan.go index 710905ad..6dfcfe11 100644 --- a/transport/trojan/trojan.go +++ b/transport/trojan/trojan.go @@ -14,6 +14,7 @@ import ( N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/pool" + "github.com/Dreamacro/clash/component/ca" tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/socks5" @@ -77,13 +78,10 @@ func (t *Trojan) StreamConn(ctx context.Context, conn net.Conn) (net.Conn, error ServerName: t.option.ServerName, } - if len(t.option.Fingerprint) == 0 { - tlsConfig = tlsC.GetGlobalTLSConfig(tlsConfig) - } else { - var err error - if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, t.option.Fingerprint); err != nil { - return nil, err - } + var err error + tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, t.option.Fingerprint) + if err != nil { + return nil, err } if len(t.option.ClientFingerprint) != 0 { @@ -112,7 +110,7 @@ func (t *Trojan) StreamConn(ctx context.Context, conn net.Conn) (net.Conn, error ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) defer cancel() - err := tlsConn.HandshakeContext(ctx) + err = tlsConn.HandshakeContext(ctx) return tlsConn, err } diff --git a/transport/v2ray-plugin/websocket.go b/transport/v2ray-plugin/websocket.go index 25483670..066a3e2a 100644 --- a/transport/v2ray-plugin/websocket.go +++ b/transport/v2ray-plugin/websocket.go @@ -6,7 +6,7 @@ import ( "net" "net/http" - tlsC "github.com/Dreamacro/clash/component/tls" + "github.com/Dreamacro/clash/component/ca" "github.com/Dreamacro/clash/transport/vmess" ) @@ -43,13 +43,10 @@ func NewV2rayObfs(ctx context.Context, conn net.Conn, option *Option) (net.Conn, InsecureSkipVerify: option.SkipCertVerify, NextProtos: []string{"http/1.1"}, } - if len(option.Fingerprint) == 0 { - config.TLSConfig = tlsC.GetGlobalTLSConfig(tlsConfig) - } else { - var err error - if config.TLSConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint); err != nil { - return nil, err - } + var err error + config.TLSConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint) + if err != nil { + return nil, err } if host := config.Headers.Get("Host"); host != "" { diff --git a/transport/vmess/tls.go b/transport/vmess/tls.go index 54813029..8bcb6513 100644 --- a/transport/vmess/tls.go +++ b/transport/vmess/tls.go @@ -6,6 +6,7 @@ import ( "errors" "net" + "github.com/Dreamacro/clash/component/ca" tlsC "github.com/Dreamacro/clash/component/tls" ) @@ -25,13 +26,10 @@ func StreamTLSConn(ctx context.Context, conn net.Conn, cfg *TLSConfig) (net.Conn NextProtos: cfg.NextProtos, } - if len(cfg.FingerPrint) == 0 { - tlsConfig = tlsC.GetGlobalTLSConfig(tlsConfig) - } else { - var err error - if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, cfg.FingerPrint); err != nil { - return nil, err - } + var err error + tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, cfg.FingerPrint) + if err != nil { + return nil, err } if len(cfg.ClientFingerprint) != 0 { @@ -51,7 +49,7 @@ func StreamTLSConn(ctx context.Context, conn net.Conn, cfg *TLSConfig) (net.Conn tlsConn := tls.Client(conn, tlsConfig) - err := tlsConn.HandshakeContext(ctx) + err = tlsConn.HandshakeContext(ctx) return tlsConn, err } From bf619d858615ebce70e58f931a12bde29ec8ea5e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 22 Sep 2023 23:33:24 +0800 Subject: [PATCH 56/66] fix: socks5 udp not working on loopback --- adapter/outbound/direct.go | 3 ++- adapter/outbound/socks5.go | 2 +- dns/util.go | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index 720f96f9..75e999a6 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -3,6 +3,7 @@ package outbound import ( "context" "errors" + "net/netip" N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/dialer" @@ -40,7 +41,7 @@ func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, } metadata.DstIP = ip } - pc, err := dialer.ListenPacket(ctx, dialer.ParseNetwork("udp", metadata.DstIP), "", d.Base.DialOptions(opts...)...) + pc, err := dialer.NewDialer(d.Base.DialOptions(opts...)...).ListenPacket(ctx, "udp", "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort)) if err != nil { return nil, err } diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index d857172e..864500c5 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -156,7 +156,7 @@ func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, bindUDPAddr.IP = serverAddr.IP } - pc, err := dialer.ListenPacket(ctx, dialer.ParseNetwork("udp", bindUDPAddr.AddrPort().Addr()), "", ss.Base.DialOptions(opts...)...) + pc, err := cDialer.ListenPacket(ctx, "udp", "", bindUDPAddr.AddrPort()) if err != nil { return } diff --git a/dns/util.go b/dns/util.go index 08daadbd..29de4e2a 100644 --- a/dns/util.go +++ b/dns/util.go @@ -290,7 +290,7 @@ func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName st DstPort: uint16(uintPort), } if proxyAdapter == nil { - return dialer.ListenPacket(ctx, dialer.ParseNetwork(network, dstIP), "", opts...) + return dialer.NewDialer(opts...).ListenPacket(ctx, dialer.ParseNetwork(network, dstIP), "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort)) } if !proxyAdapter.SupportUDP() { From 0207a7ac96caac7f43798c99a5ef13ef86d9a8b8 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 23 Sep 2023 14:01:18 +0800 Subject: [PATCH 57/66] chore: resolver read system hosts file --- component/resolver/host.go | 42 +++++++++++++++++++----------- component/resolver/host_windows.go | 19 ++++++++++++++ 2 files changed, 46 insertions(+), 15 deletions(-) create mode 100644 component/resolver/host_windows.go diff --git a/component/resolver/host.go b/component/resolver/host.go index 3b7e9a37..d6eb5873 100644 --- a/component/resolver/host.go +++ b/component/resolver/host.go @@ -4,6 +4,7 @@ import ( "errors" "net/netip" "strings" + _ "unsafe" "github.com/Dreamacro/clash/common/utils" "github.com/Dreamacro/clash/component/trie" @@ -20,28 +21,39 @@ func NewHosts(hosts *trie.DomainTrie[HostValue]) Hosts { } } +// lookupStaticHost looks up the addresses and the canonical name for the given host from /etc/hosts. +// +//go:linkname lookupStaticHost net.lookupStaticHost +func lookupStaticHost(host string) ([]string, string) + // Return the search result and whether to match the parameter `isDomain` func (h *Hosts) Search(domain string, isDomain bool) (*HostValue, bool) { - value := h.DomainTrie.Search(domain) - if value == nil { - return nil, false - } - hostValue := value.Data() - for { - if isDomain && hostValue.IsDomain { - return &hostValue, true - } else { - if node := h.DomainTrie.Search(hostValue.Domain); node != nil { - hostValue = node.Data() + if value := h.DomainTrie.Search(domain); value != nil { + hostValue := value.Data() + for { + if isDomain && hostValue.IsDomain { + return &hostValue, true } else { - break + if node := h.DomainTrie.Search(hostValue.Domain); node != nil { + hostValue = node.Data() + } else { + break + } } } + if isDomain == hostValue.IsDomain { + return &hostValue, true + } + + return &hostValue, false } - if isDomain == hostValue.IsDomain { - return &hostValue, true + if !isDomain { + addr, _ := lookupStaticHost(domain) + if hostValue, err := NewHostValue(addr); err == nil { + return &hostValue, true + } } - return &hostValue, false + return nil, false } type HostValue struct { diff --git a/component/resolver/host_windows.go b/component/resolver/host_windows.go new file mode 100644 index 00000000..669f9547 --- /dev/null +++ b/component/resolver/host_windows.go @@ -0,0 +1,19 @@ +//go:build !go1.22 + +// a simple standard lib fix from: https://github.com/golang/go/commit/33d4a5105cf2b2d549922e909e9239a48b8cefcc + +package resolver + +import ( + "golang.org/x/sys/windows" + _ "unsafe" +) + +//go:linkname testHookHostsPath net.testHookHostsPath +var testHookHostsPath string + +func init() { + if dir, err := windows.GetSystemDirectory(); err == nil { + testHookHostsPath = dir + "/Drivers/etc/hosts" + } +} From 34f62a091926e3dd3a2cdcff8eae4c8a0021a011 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sat, 23 Sep 2023 17:54:20 +0800 Subject: [PATCH 58/66] feat: add provider proxies api --- hub/route/provider.go | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/hub/route/provider.go b/hub/route/provider.go index 1ba0d32c..c050a9f1 100644 --- a/hub/route/provider.go +++ b/hub/route/provider.go @@ -4,22 +4,35 @@ import ( "context" "net/http" + C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/constant/provider" "github.com/Dreamacro/clash/tunnel" "github.com/go-chi/chi/v5" "github.com/go-chi/render" + "github.com/samber/lo" ) func proxyProviderRouter() http.Handler { r := chi.NewRouter() r.Get("/", getProviders) - r.Route("/{name}", func(r chi.Router) { + r.Route("/{providerName}", func(r chi.Router) { r.Use(parseProviderName, findProviderByName) r.Get("/", getProvider) r.Put("/", updateProvider) r.Get("/healthcheck", healthCheckProvider) + r.Mount("/", proxyProviderProxyRouter()) + }) + return r +} + +func proxyProviderProxyRouter() http.Handler { + r := chi.NewRouter() + r.Route("/{name}", func(r chi.Router) { + r.Use(parseProxyName, findProviderProxyByName) + r.Get("/", getProxy) + r.Get("/healthcheck", getProxyDelay) }) return r } @@ -54,7 +67,7 @@ func healthCheckProvider(w http.ResponseWriter, r *http.Request) { func parseProviderName(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - name := getEscapeParam(r, "name") + name := getEscapeParam(r, "providerName") ctx := context.WithValue(r.Context(), CtxKeyProviderName, name) next.ServeHTTP(w, r.WithContext(ctx)) }) @@ -76,6 +89,27 @@ func findProviderByName(next http.Handler) http.Handler { }) } +func findProviderProxyByName(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var ( + name = r.Context().Value(CtxKeyProxyName).(string) + pd = r.Context().Value(CtxKeyProvider).(provider.ProxyProvider) + ) + proxy, exist := lo.Find(pd.Proxies(), func(proxy C.Proxy) bool { + return proxy.Name() == name + }) + + if !exist { + render.Status(r, http.StatusNotFound) + render.JSON(w, r, ErrNotFound) + return + } + + ctx := context.WithValue(r.Context(), CtxKeyProxy, proxy) + next.ServeHTTP(w, r.WithContext(ctx)) + }) +} + func ruleProviderRouter() http.Handler { r := chi.NewRouter() r.Get("/", getRuleProviders) From 8f515ecc051cae7bb31b7df9409114db639e3f17 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sat, 23 Sep 2023 17:59:59 +0800 Subject: [PATCH 59/66] chore: updateUI API return 501 when config incomplete --- config/config.go | 13 +++---------- config/update_ui.go | 29 ++++++++++++++++++++++++++--- hub/executor/executor.go | 4 +++- hub/route/upgrade.go | 17 ++++++++++++----- 4 files changed, 44 insertions(+), 19 deletions(-) diff --git a/config/config.go b/config/config.go index d0e69e49..fd1a4116 100644 --- a/config/config.go +++ b/config/config.go @@ -9,7 +9,6 @@ import ( "net/url" "os" "path" - "path/filepath" "regexp" "strings" "time" @@ -582,9 +581,6 @@ func parseGeneral(cfg *RawConfig) (*General, error) { N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second } - if cfg.ExternalUIURL != "" { - ExternalUIURL = cfg.ExternalUIURL - } ExternalUIPath = cfg.ExternalUI // checkout externalUI exist if ExternalUIPath != "" { @@ -602,15 +598,12 @@ func parseGeneral(cfg *RawConfig) (*General, error) { // checkout UIpath/name exist if cfg.ExternalUIName != "" { ExternalUIName = cfg.ExternalUIName - ExternalUIFolder = filepath.Clean(path.Join(ExternalUIPath, cfg.ExternalUIName)) - if _, err := os.Stat(ExternalUIPath); os.IsNotExist(err) { - if err := os.MkdirAll(ExternalUIPath, os.ModePerm); err != nil { - return nil, err - } - } } else { ExternalUIFolder = ExternalUIPath } + if cfg.ExternalUIURL != "" { + ExternalUIURL = cfg.ExternalUIURL + } cfg.Tun.RedirectToTun = cfg.EBpf.RedirectToTun return &General{ diff --git a/config/update_ui.go b/config/update_ui.go index 5bc2596e..3526836e 100644 --- a/config/update_ui.go +++ b/config/update_ui.go @@ -2,6 +2,7 @@ package config import ( "archive/zip" + "errors" "fmt" "io" "os" @@ -19,15 +20,18 @@ var ( ExternalUIFolder string ExternalUIName string ) - +var ( + ErrImcompleteConf = errors.New("ExternalUI configure incomplete") +) var xdMutex sync.Mutex func UpdateUI() error { xdMutex.Lock() defer xdMutex.Unlock() - if ExternalUIPath == "" || ExternalUIFolder == "" { - return fmt.Errorf("ExternalUI configure incomplete") + err := prepare() + if err != nil { + return err } data, err := downloadForBytes(ExternalUIURL) @@ -60,6 +64,25 @@ func UpdateUI() error { return nil } +func prepare() error { + if ExternalUIPath == "" || ExternalUIURL == "" { + return ErrImcompleteConf + } + + if ExternalUIName != "" { + ExternalUIFolder = filepath.Clean(path.Join(ExternalUIPath, ExternalUIName)) + if _, err := os.Stat(ExternalUIPath); os.IsNotExist(err) { + if err := os.MkdirAll(ExternalUIPath, os.ModePerm); err != nil { + return err + } + } + } else { + ExternalUIFolder = ExternalUIPath + } + + return nil +} + func unzip(src, dest string) (string, error) { r, err := zip.OpenReader(src) if err != nil { diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 88cdfd6c..d1636754 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -2,7 +2,6 @@ package executor import ( "fmt" - "github.com/Dreamacro/clash/ntp" "net" "net/netip" "os" @@ -12,6 +11,8 @@ import ( "sync" "time" + "github.com/Dreamacro/clash/ntp" + "github.com/Dreamacro/clash/adapter" "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/adapter/outboundgroup" @@ -142,6 +143,7 @@ func GetGeneral() *config.General { AllowLan: listener.AllowLan(), BindAddress: listener.BindAddress(), }, + Controller: config.Controller{}, Mode: tunnel.Mode(), LogLevel: log.Level(), IPv6: !resolver.DisableIPv6, diff --git a/hub/route/upgrade.go b/hub/route/upgrade.go index be226616..28acb23a 100644 --- a/hub/route/upgrade.go +++ b/hub/route/upgrade.go @@ -1,6 +1,7 @@ package route import ( + "errors" "fmt" "net/http" "os" @@ -15,12 +16,12 @@ import ( func upgradeRouter() http.Handler { r := chi.NewRouter() - r.Post("/", upgrade) + r.Post("/", upgradeCore) r.Post("/ui", updateUI) return r } -func upgrade(w http.ResponseWriter, r *http.Request) { +func upgradeCore(w http.ResponseWriter, r *http.Request) { // modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L108 log.Infoln("start update") execPath, err := os.Executable() @@ -49,9 +50,15 @@ func upgrade(w http.ResponseWriter, r *http.Request) { func updateUI(w http.ResponseWriter, r *http.Request) { err := config.UpdateUI() if err != nil { - log.Warnln("%s", err) - render.Status(r, http.StatusInternalServerError) - render.JSON(w, r, newError(fmt.Sprintf("%s", err))) + if errors.Is(err, config.ErrImcompleteConf) { + log.Warnln("%s", err) + render.Status(r, http.StatusNotImplemented) + render.JSON(w, r, newError(fmt.Sprintf("%s", err))) + } else { + log.Warnln("%s", err) + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, newError(fmt.Sprintf("%s", err))) + } return } From 7c59916c2220ce49be36b480797b6ffacd92e679 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sun, 24 Sep 2023 00:19:10 +0800 Subject: [PATCH 60/66] chore: update provider proxies api --- adapter/adapter.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/adapter/adapter.go b/adapter/adapter.go index 13f7f06f..e9ce59bb 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -217,6 +217,10 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In if alive { record.Delay = t } + p.history.Put(record) + if p.history.Len() > defaultHistoriesNum { + p.history.Pop() + } state, ok := p.extra.Load(url) if !ok { From 0d300a35404c9403c56ed47f4c8c7c7b41c28087 Mon Sep 17 00:00:00 2001 From: PuerNya Date: Sun, 24 Sep 2023 15:39:14 +0800 Subject: [PATCH 61/66] chore: handle provider proxies in proxies api --- hub/route/proxies.go | 4 ++-- tunnel/tunnel.go | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/hub/route/proxies.go b/hub/route/proxies.go index 36c9d1b1..c1e30b21 100644 --- a/hub/route/proxies.go +++ b/hub/route/proxies.go @@ -46,7 +46,7 @@ func parseProxyName(next http.Handler) http.Handler { func findProxyByName(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { name := r.Context().Value(CtxKeyProxyName).(string) - proxies := tunnel.Proxies() + proxies := tunnel.ProxiesWithProviders() proxy, exist := proxies[name] if !exist { render.Status(r, http.StatusNotFound) @@ -60,7 +60,7 @@ func findProxyByName(next http.Handler) http.Handler { } func getProxies(w http.ResponseWriter, r *http.Request) { - proxies := tunnel.Proxies() + proxies := tunnel.ProxiesWithProviders() render.JSON(w, r, render.M{ "proxies": proxies, }) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index b7557e10..fd601d30 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -127,6 +127,20 @@ func Proxies() map[string]C.Proxy { return proxies } +func ProxiesWithProviders() map[string]C.Proxy { + allProxies := make(map[string]C.Proxy) + for name, proxy := range proxies { + allProxies[name] = proxy + } + for _, p := range providers { + for _, proxy := range p.Proxies() { + name := proxy.Name() + allProxies[name] = proxy + } + } + return allProxies +} + // Providers return all compatible providers func Providers() map[string]provider.ProxyProvider { return providers From e6366f7442d97785da3d6a31b12cf2e9a085386b Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sun, 24 Sep 2023 19:00:51 +0800 Subject: [PATCH 62/66] chore: fix typo --- config/config.go | 1 + config/update_ui.go | 4 ++-- hub/route/upgrade.go | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/config/config.go b/config/config.go index fd1a4116..a1ddde81 100644 --- a/config/config.go +++ b/config/config.go @@ -461,6 +461,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { GeoIp: "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.dat", GeoSite: "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geosite.dat", }, + ExternalUIURL: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip", } if err := yaml.Unmarshal(buf, rawCfg); err != nil { diff --git a/config/update_ui.go b/config/update_ui.go index 3526836e..27e0f382 100644 --- a/config/update_ui.go +++ b/config/update_ui.go @@ -21,7 +21,7 @@ var ( ExternalUIName string ) var ( - ErrImcompleteConf = errors.New("ExternalUI configure incomplete") + ErrIncompleteConf = errors.New("ExternalUI configure incomplete") ) var xdMutex sync.Mutex @@ -66,7 +66,7 @@ func UpdateUI() error { func prepare() error { if ExternalUIPath == "" || ExternalUIURL == "" { - return ErrImcompleteConf + return ErrIncompleteConf } if ExternalUIName != "" { diff --git a/hub/route/upgrade.go b/hub/route/upgrade.go index 28acb23a..7b486ee3 100644 --- a/hub/route/upgrade.go +++ b/hub/route/upgrade.go @@ -50,7 +50,7 @@ func upgradeCore(w http.ResponseWriter, r *http.Request) { func updateUI(w http.ResponseWriter, r *http.Request) { err := config.UpdateUI() if err != nil { - if errors.Is(err, config.ErrImcompleteConf) { + if errors.Is(err, config.ErrIncompleteConf) { log.Warnln("%s", err) render.Status(r, http.StatusNotImplemented) render.JSON(w, r, newError(fmt.Sprintf("%s", err))) From 67d7e53f7ae2d265a72bd03509723e63ccf19ef1 Mon Sep 17 00:00:00 2001 From: Kiva Date: Sun, 24 Sep 2023 19:27:55 +0800 Subject: [PATCH 63/66] feat: recovering `preHandleMetadata` failure from sniffing (#769) --- component/sniffer/dispatcher.go | 22 +++++++++++++--------- tunnel/tunnel.go | 17 +++++++++++++++-- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/component/sniffer/dispatcher.go b/component/sniffer/dispatcher.go index f813eec2..a1c8a93f 100644 --- a/component/sniffer/dispatcher.go +++ b/component/sniffer/dispatcher.go @@ -35,7 +35,8 @@ type SnifferDispatcher struct { parsePureIp bool } -func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) { +// TCPSniff returns true if the connection is sniffed to have a domain +func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) bool { if (metadata.Host == "" && sd.parsePureIp) || sd.forceDomain.Has(metadata.Host) || (metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping) { inWhitelist := false overrideDest := false @@ -50,7 +51,7 @@ func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata } if !inWhitelist { - return + return false } sd.rwMux.RLock() @@ -58,18 +59,18 @@ func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata if count, ok := sd.skipList.Get(dst); ok && count > 5 { log.Debugln("[Sniffer] Skip sniffing[%s] due to multiple failures", dst) defer sd.rwMux.RUnlock() - return + return false } sd.rwMux.RUnlock() if host, err := sd.sniffDomain(conn, metadata); err != nil { sd.cacheSniffFailed(metadata) log.Debugln("[Sniffer] All sniffing sniff failed with from [%s:%d] to [%s:%d]", metadata.SrcIP, metadata.SrcPort, metadata.String(), metadata.DstPort) - return + return false } else { if sd.skipSNI.Has(host) { log.Debugln("[Sniffer] Skip sni[%s]", host) - return + return false } sd.rwMux.RLock() @@ -77,20 +78,23 @@ func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata sd.rwMux.RUnlock() sd.replaceDomain(metadata, host, overrideDest) + return true } } + return false } func (sd *SnifferDispatcher) replaceDomain(metadata *C.Metadata, host string, overrideDest bool) { + // show log early, since the following code may mutate `metadata.Host` + log.Debugln("[Sniffer] Sniff TCP [%s]-->[%s] success, replace domain [%s]-->[%s]", + metadata.SourceDetail(), + metadata.RemoteAddress(), + metadata.Host, host) metadata.SniffHost = host if overrideDest { metadata.Host = host } metadata.DNSMode = C.DNSNormal - log.Debugln("[Sniffer] Sniff TCP [%s]-->[%s] success, replace domain [%s]-->[%s]", - metadata.SourceDetail(), - metadata.RemoteAddress(), - metadata.Host, host) } func (sd *SnifferDispatcher) Enable() bool { diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index fd601d30..ff64915a 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -419,15 +419,28 @@ func handleTCPConn(connCtx C.ConnContext) { return } + preHandleFailed := false if err := preHandleMetadata(metadata); err != nil { log.Debugln("[Metadata PreHandle] error: %s", err) - return + preHandleFailed = true } conn := connCtx.Conn() conn.ResetPeeked() // reset before sniffer if sniffer.Dispatcher.Enable() && sniffingEnable { - sniffer.Dispatcher.TCPSniff(conn, metadata) + // Try to sniff a domain when `preHandleMetadata` failed, this is usually + // caused by a "Fake DNS record missing" error when enhanced-mode is fake-ip. + if sniffer.Dispatcher.TCPSniff(conn, metadata) { + // we now have a domain name + preHandleFailed = false + } + } + + // If both trials have failed, we can do nothing but give up + if preHandleFailed { + log.Debugln("[Metadata PreHandle] failed to sniff a domain for connection %s --> %s, give up", + metadata.SourceDetail(), metadata.RemoteAddress()) + return } peekMutex := sync.Mutex{} From c0ba7987089813bcf870e59a5b76fd5fe94e149f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 25 Sep 2023 09:10:43 +0800 Subject: [PATCH 64/66] chore: share N.dialer code --- adapter/outbound/hysteria2.go | 40 ++--------------- adapter/outbound/singmux.go | 28 ++---------- adapter/outbound/wireguard.go | 40 ++--------------- component/proxydialer/sing.go | 82 +++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 96 deletions(-) create mode 100644 component/proxydialer/sing.go diff --git a/adapter/outbound/hysteria2.go b/adapter/outbound/hysteria2.go index e7ad91df..57c15a12 100644 --- a/adapter/outbound/hysteria2.go +++ b/adapter/outbound/hysteria2.go @@ -19,7 +19,6 @@ import ( "github.com/metacubex/sing-quic/hysteria2" M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" ) func init() { @@ -31,7 +30,7 @@ type Hysteria2 struct { option *Hysteria2Option client *hysteria2.Client - dialer *hy2SingDialer + dialer proxydialer.SingDialer } type Hysteria2Option struct { @@ -53,40 +52,9 @@ type Hysteria2Option struct { CWND int `proxy:"cwnd,omitempty"` } -type hy2SingDialer struct { - dialer dialer.Dialer - proxyName string -} - -var _ N.Dialer = (*hy2SingDialer)(nil) - -func (d *hy2SingDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { - var cDialer C.Dialer = d.dialer - if len(d.proxyName) > 0 { - pd, err := proxydialer.NewByName(d.proxyName, d.dialer) - if err != nil { - return nil, err - } - cDialer = pd - } - return cDialer.DialContext(ctx, network, destination.String()) -} - -func (d *hy2SingDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { - var cDialer C.Dialer = d.dialer - if len(d.proxyName) > 0 { - pd, err := proxydialer.NewByName(d.proxyName, d.dialer) - if err != nil { - return nil, err - } - cDialer = pd - } - return cDialer.ListenPacket(ctx, "udp", "", destination.AddrPort()) -} - func (h *Hysteria2) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { options := h.Base.DialOptions(opts...) - h.dialer.dialer = dialer.NewDialer(options...) + h.dialer.SetDialer(dialer.NewDialer(options...)) c, err := h.client.DialConn(ctx, M.ParseSocksaddr(metadata.RemoteAddress())) if err != nil { return nil, err @@ -96,7 +64,7 @@ func (h *Hysteria2) DialContext(ctx context.Context, metadata *C.Metadata, opts func (h *Hysteria2) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { options := h.Base.DialOptions(opts...) - h.dialer.dialer = dialer.NewDialer(options...) + h.dialer.SetDialer(dialer.NewDialer(options...)) pc, err := h.client.ListenPacket(ctx) if err != nil { return nil, err @@ -149,7 +117,7 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { tlsConfig.NextProtos = option.ALPN } - singDialer := &hy2SingDialer{dialer: dialer.NewDialer(), proxyName: option.DialerProxy} + singDialer := proxydialer.NewByNameSingDialer(option.DialerProxy, dialer.NewDialer()) clientOptions := hysteria2.ClientOptions{ Context: context.TODO(), diff --git a/adapter/outbound/singmux.go b/adapter/outbound/singmux.go index 9a977318..c9f50ce9 100644 --- a/adapter/outbound/singmux.go +++ b/adapter/outbound/singmux.go @@ -3,7 +3,6 @@ package outbound import ( "context" "errors" - "net" "runtime" CN "github.com/Dreamacro/clash/common/net" @@ -15,14 +14,13 @@ import ( mux "github.com/sagernet/sing-mux" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" ) type SingMux struct { C.ProxyAdapter base ProxyBase client *mux.Client - dialer *muxSingDialer + dialer proxydialer.SingDialer onlyTcp bool } @@ -41,27 +39,9 @@ type ProxyBase interface { DialOptions(opts ...dialer.Option) []dialer.Option } -type muxSingDialer struct { - dialer dialer.Dialer - proxy C.ProxyAdapter - statistic bool -} - -var _ N.Dialer = (*muxSingDialer)(nil) - -func (d *muxSingDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { - var cDialer C.Dialer = proxydialer.New(d.proxy, d.dialer, d.statistic) - return cDialer.DialContext(ctx, network, destination.String()) -} - -func (d *muxSingDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { - var cDialer C.Dialer = proxydialer.New(d.proxy, d.dialer, d.statistic) - return cDialer.ListenPacket(ctx, "udp", "", destination.AddrPort()) -} - func (s *SingMux) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { options := s.base.DialOptions(opts...) - s.dialer.dialer = dialer.NewDialer(options...) + s.dialer.SetDialer(dialer.NewDialer(options...)) c, err := s.client.DialContext(ctx, "tcp", M.ParseSocksaddr(metadata.RemoteAddress())) if err != nil { return nil, err @@ -74,7 +54,7 @@ func (s *SingMux) ListenPacketContext(ctx context.Context, metadata *C.Metadata, return s.ProxyAdapter.ListenPacketContext(ctx, metadata, opts...) } options := s.base.DialOptions(opts...) - s.dialer.dialer = dialer.NewDialer(options...) + s.dialer.SetDialer(dialer.NewDialer(options...)) // sing-mux use stream-oriented udp with a special address, so we need a net.UDPAddr if !metadata.Resolved() { @@ -114,7 +94,7 @@ func closeSingMux(s *SingMux) { } func NewSingMux(option SingMuxOption, proxy C.ProxyAdapter, base ProxyBase) (C.ProxyAdapter, error) { - singDialer := &muxSingDialer{dialer: dialer.NewDialer(), proxy: proxy, statistic: option.Statistic} + singDialer := proxydialer.NewSingDialer(proxy, dialer.NewDialer(), option.Statistic) client, err := mux.NewClient(mux.Options{ Dialer: singDialer, Protocol: option.Protocol, diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index c1050ac6..6a11a234 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -27,7 +27,6 @@ import ( "github.com/sagernet/sing/common/debug" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" "github.com/sagernet/wireguard-go/device" ) @@ -36,7 +35,7 @@ type WireGuard struct { bind *wireguard.ClientBind device *device.Device tunDevice wireguard.Device - dialer *wgSingDialer + dialer proxydialer.SingDialer startOnce sync.Once startErr error resolver *dns.Resolver @@ -70,37 +69,6 @@ type WireGuardPeerOption struct { AllowedIPs []string `proxy:"allowed-ips,omitempty"` } -type wgSingDialer struct { - dialer dialer.Dialer - proxyName string -} - -var _ N.Dialer = (*wgSingDialer)(nil) - -func (d *wgSingDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { - var cDialer C.Dialer = d.dialer - if len(d.proxyName) > 0 { - pd, err := proxydialer.NewByName(d.proxyName, d.dialer) - if err != nil { - return nil, err - } - cDialer = pd - } - return cDialer.DialContext(ctx, network, destination.String()) -} - -func (d *wgSingDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { - var cDialer C.Dialer = d.dialer - if len(d.proxyName) > 0 { - pd, err := proxydialer.NewByName(d.proxyName, d.dialer) - if err != nil { - return nil, err - } - cDialer = pd - } - return cDialer.ListenPacket(ctx, "udp", "", destination.AddrPort()) -} - type wgSingErrorHandler struct { name string } @@ -168,7 +136,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), }, - dialer: &wgSingDialer{dialer: dialer.NewDialer(), proxyName: option.DialerProxy}, + dialer: proxydialer.NewByNameSingDialer(option.DialerProxy, dialer.NewDialer()), } runtime.SetFinalizer(outbound, closeWireGuard) @@ -355,7 +323,7 @@ func closeWireGuard(w *WireGuard) { func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { options := w.Base.DialOptions(opts...) - w.dialer.dialer = dialer.NewDialer(options...) + w.dialer.SetDialer(dialer.NewDialer(options...)) var conn net.Conn w.startOnce.Do(func() { w.startErr = w.tunDevice.Start() @@ -387,7 +355,7 @@ func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { options := w.Base.DialOptions(opts...) - w.dialer.dialer = dialer.NewDialer(options...) + w.dialer.SetDialer(dialer.NewDialer(options...)) var pc net.PacketConn w.startOnce.Do(func() { w.startErr = w.tunDevice.Start() diff --git a/component/proxydialer/sing.go b/component/proxydialer/sing.go new file mode 100644 index 00000000..9b116527 --- /dev/null +++ b/component/proxydialer/sing.go @@ -0,0 +1,82 @@ +package proxydialer + +import ( + "context" + "net" + + C "github.com/Dreamacro/clash/constant" + + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" +) + +type SingDialer interface { + N.Dialer + SetDialer(dialer C.Dialer) +} + +type singDialer proxyDialer + +var _ N.Dialer = (*singDialer)(nil) + +func (d *singDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { + return (*proxyDialer)(d).DialContext(ctx, network, destination.String()) +} + +func (d *singDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { + return (*proxyDialer)(d).ListenPacket(ctx, "udp", "", destination.AddrPort()) +} + +func (d *singDialer) SetDialer(dialer C.Dialer) { + (*proxyDialer)(d).dialer = dialer +} + +func NewSingDialer(proxy C.ProxyAdapter, dialer C.Dialer, statistic bool) SingDialer { + return (*singDialer)(&proxyDialer{ + proxy: proxy, + dialer: dialer, + statistic: statistic, + }) +} + +type byNameSingDialer struct { + dialer C.Dialer + proxyName string +} + +var _ N.Dialer = (*byNameSingDialer)(nil) + +func (d *byNameSingDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { + var cDialer C.Dialer = d.dialer + if len(d.proxyName) > 0 { + pd, err := NewByName(d.proxyName, d.dialer) + if err != nil { + return nil, err + } + cDialer = pd + } + return cDialer.DialContext(ctx, network, destination.String()) +} + +func (d *byNameSingDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { + var cDialer C.Dialer = d.dialer + if len(d.proxyName) > 0 { + pd, err := NewByName(d.proxyName, d.dialer) + if err != nil { + return nil, err + } + cDialer = pd + } + return cDialer.ListenPacket(ctx, "udp", "", destination.AddrPort()) +} + +func (d *byNameSingDialer) SetDialer(dialer C.Dialer) { + d.dialer = dialer +} + +func NewByNameSingDialer(proxyName string, dialer C.Dialer) SingDialer { + return &byNameSingDialer{ + dialer: dialer, + proxyName: proxyName, + } +} From 0dfe696300d70eef66dcebf718bb2571d0dc05bd Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 25 Sep 2023 09:11:20 +0800 Subject: [PATCH 65/66] chore: ntp service support `dialer-proxy` --- config/config.go | 7 +++++-- hub/executor/executor.go | 8 ++++++-- ntp/service.go | 21 +++++++++++++-------- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/config/config.go b/config/config.go index a1ddde81..e240f9b9 100644 --- a/config/config.go +++ b/config/config.go @@ -93,10 +93,11 @@ type Controller struct { // NTP config type NTP struct { Enable bool `yaml:"enable"` - WriteToSystem bool `yaml:"write-to-system"` Server string `yaml:"server"` Port int `yaml:"port"` Interval int `yaml:"interval"` + DialerProxy string `yaml:"dialer-proxy"` + WriteToSystem bool `yaml:"write-to-system"` } // DNS config @@ -183,10 +184,11 @@ type Config struct { type RawNTP struct { Enable bool `yaml:"enable"` - WriteToSystem bool `yaml:"write-to-system"` Server string `yaml:"server"` ServerPort int `yaml:"server-port"` Interval int `yaml:"interval"` + DialerProxy string `yaml:"dialer-proxy"` + WriteToSystem bool `yaml:"write-to-system"` } type RawDNS struct { @@ -1200,6 +1202,7 @@ func paresNTP(rawCfg *RawConfig) *NTP { Server: cfg.Server, Port: cfg.ServerPort, Interval: cfg.Interval, + DialerProxy: cfg.DialerProxy, WriteToSystem: cfg.WriteToSystem, } return ntpCfg diff --git a/hub/executor/executor.go b/hub/executor/executor.go index d1636754..1831584f 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -190,8 +190,12 @@ func updateExperimental(c *config.Config) { func updateNTP(c *config.NTP) { if c.Enable { - ntp.ReCreateNTPService(net.JoinHostPort(c.Server, strconv.Itoa(c.Port)), - time.Duration(c.Interval), c.WriteToSystem) + ntp.ReCreateNTPService( + net.JoinHostPort(c.Server, strconv.Itoa(c.Port)), + time.Duration(c.Interval), + c.DialerProxy, + c.WriteToSystem, + ) } } diff --git a/ntp/service.go b/ntp/service.go index 3234180d..c5506197 100644 --- a/ntp/service.go +++ b/ntp/service.go @@ -2,12 +2,15 @@ package ntp import ( "context" - "github.com/Dreamacro/clash/log" - M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" - "github.com/sagernet/sing/common/ntp" "sync" "time" + + "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/proxydialer" + "github.com/Dreamacro/clash/log" + + M "github.com/sagernet/sing/common/metadata" + "github.com/sagernet/sing/common/ntp" ) var offset time.Duration @@ -15,6 +18,7 @@ var service *Service type Service struct { server M.Socksaddr + dialer proxydialer.SingDialer ticker *time.Ticker ctx context.Context cancel context.CancelFunc @@ -23,16 +27,17 @@ type Service struct { running bool } -func ReCreateNTPService(server string, interval time.Duration, syncSystemTime bool) { +func ReCreateNTPService(server string, interval time.Duration, dialerProxy string, syncSystemTime bool) { if service != nil { service.Stop() } ctx, cancel := context.WithCancel(context.Background()) service = &Service{ + server: M.ParseSocksaddr(server), + dialer: proxydialer.NewByNameSingDialer(dialerProxy, dialer.NewDialer()), + ticker: time.NewTicker(interval * time.Minute), ctx: ctx, cancel: cancel, - server: M.ParseSocksaddr(server), - ticker: time.NewTicker(interval * time.Minute), syncSystemTime: syncSystemTime, } service.Start() @@ -70,7 +75,7 @@ func (srv *Service) update() { var response *ntp.Response var err error for i := 0; i < 3; i++ { - response, err = ntp.Exchange(context.Background(), N.SystemDialer, srv.server) + response, err = ntp.Exchange(context.Background(), srv.dialer, srv.server) if err != nil { if i == 2 { log.Errorln("Initialize NTP time failed: %s", err) From fdd327d58da49b0bf21b0908bc7e3d475eae63a3 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Mon, 25 Sep 2023 13:28:11 +0800 Subject: [PATCH 66/66] fix: fail to set KeepAliveIntervall #715 --- common/net/tcpip.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/net/tcpip.go b/common/net/tcpip.go index 7bc31edd..0499e54c 100644 --- a/common/net/tcpip.go +++ b/common/net/tcpip.go @@ -51,6 +51,6 @@ func SplitHostPort(s string) (host, port string, hasPort bool, err error) { func TCPKeepAlive(c net.Conn) { if tcp, ok := c.(*net.TCPConn); ok { _ = tcp.SetKeepAlive(true) - _ = tcp.SetKeepAlivePeriod(KeepAliveInterval * time.Second) + _ = tcp.SetKeepAlivePeriod(KeepAliveInterval) } }