From dceb8ee535dafbc56740c7ab702e067c9ea8ecce Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 13 Mar 2024 14:49:46 +0800 Subject: [PATCH 001/311] fix: resolve atomic.Value usages with interface types --- common/atomic/value.go | 19 +++++++++++++++---- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/common/atomic/value.go b/common/atomic/value.go index cbc6c5b8..36623b3e 100644 --- a/common/atomic/value.go +++ b/common/atomic/value.go @@ -15,20 +15,31 @@ type TypedValue[T any] struct { value atomic.Value } +// tValue is a struct with determined type to resolve atomic.Value usages with interface types +// https://github.com/golang/go/issues/22550 +// +// The intention to have an atomic value store for errors. However, running this code panics: +// panic: sync/atomic: store of inconsistently typed value into Value +// This is because atomic.Value requires that the underlying concrete type be the same (which is a reasonable expectation for its implementation). +// When going through the atomic.Value.Store method call, the fact that both these are of the error interface is lost. +type tValue[T any] struct { + value T +} + func (t *TypedValue[T]) Load() T { value := t.value.Load() if value == nil { return DefaultValue[T]() } - return value.(T) + return value.(tValue[T]).value } func (t *TypedValue[T]) Store(value T) { - t.value.Store(value) + t.value.Store(tValue[T]{value}) } func (t *TypedValue[T]) Swap(new T) T { - old := t.value.Swap(new) + old := t.value.Swap(tValue[T]{new}) if old == nil { return DefaultValue[T]() } @@ -36,7 +47,7 @@ func (t *TypedValue[T]) Swap(new T) T { } func (t *TypedValue[T]) CompareAndSwap(old, new T) bool { - return t.value.CompareAndSwap(old, new) + return t.value.CompareAndSwap(tValue[T]{old}, tValue[T]{new}) } func (t *TypedValue[T]) MarshalJSON() ([]byte, error) { diff --git a/go.mod b/go.mod index 2d1ac543..9becc8c8 100644 --- a/go.mod +++ b/go.mod @@ -111,4 +111,4 @@ require ( golang.org/x/tools v0.18.0 // indirect ) -replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240313005020-c77f32e55220 +replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240313064558-c197257f6542 diff --git a/go.sum b/go.sum index 62e8fb69..7277a32e 100644 --- a/go.sum +++ b/go.sum @@ -106,8 +106,8 @@ github.com/metacubex/gvisor v0.0.0-20240214095142-666a73bcf165 h1:QIQI4gEm+gTwVN github.com/metacubex/gvisor v0.0.0-20240214095142-666a73bcf165/go.mod h1:SKY70wiF1UTSoyuDZyKPMsUC6MsMxh8Y3ZNkIa6J3fU= github.com/metacubex/quic-go v0.41.1-0.20240307164142-46c6f7cdf2d1 h1:63zKmEWU4MB5MjUSCmeDhm3OzilF7ypXWPq0gAA2GE8= github.com/metacubex/quic-go v0.41.1-0.20240307164142-46c6f7cdf2d1/go.mod h1:F/t8VnA47xoia8ABlNA4InkZjssvFJ5p6E6jKdbkgAs= -github.com/metacubex/sing v0.0.0-20240313005020-c77f32e55220 h1:lZLFR28Jf3gjoHh560uC8AKnWcQ2+VQoQY/rSj6kSrc= -github.com/metacubex/sing v0.0.0-20240313005020-c77f32e55220/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= +github.com/metacubex/sing v0.0.0-20240313064558-c197257f6542 h1:e9nBnrJBv3HzZVeSzJN0G2SADjebd2ZLF1F5dmsjUTc= +github.com/metacubex/sing v0.0.0-20240313064558-c197257f6542/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01 h1:5INHs85Gp1JZsdF7fQp1pXUjfJOX2dhwZjuUQWJVSt8= github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01/go.mod h1:WyY0zYxv+o+18R/Ece+QFontlgXoobKbNqbtYn2zjz8= github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ= From b3db113b1b656ed6de36f643e44f10945f142da6 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 13 Mar 2024 15:32:26 +0800 Subject: [PATCH 002/311] chore: allow disabled system hosts by environment variable `DISABLE_SYSTEM_HOSTS` --- component/resolver/host.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/component/resolver/host.go b/component/resolver/host.go index 69c29a3c..4a429629 100644 --- a/component/resolver/host.go +++ b/component/resolver/host.go @@ -3,6 +3,8 @@ package resolver import ( "errors" "net/netip" + "os" + "strconv" "strings" _ "unsafe" @@ -11,6 +13,8 @@ import ( "github.com/zhangyunhao116/fastrand" ) +var DisableSystemHosts, _ = strconv.ParseBool(os.Getenv("DISABLE_SYSTEM_HOSTS")) + type Hosts struct { *trie.DomainTrie[HostValue] } @@ -47,7 +51,7 @@ func (h *Hosts) Search(domain string, isDomain bool) (*HostValue, bool) { return &hostValue, false } - if !isDomain { + if !isDomain && !DisableSystemHosts { addr, _ := lookupStaticHost(domain) if hostValue, err := NewHostValue(addr); err == nil { return &hostValue, true From c80dd5d738077a53989ddd7e155f685d0e23f1cb Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 19 Mar 2024 14:44:36 +0800 Subject: [PATCH 003/311] chore: retry DNS over TCP when receive a truncated UDP response https://github.com/MetaCubeX/mihomo/issues/1117 --- dns/client.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/dns/client.go b/dns/client.go index 95f0f29b..fc76c124 100644 --- a/dns/client.go +++ b/dns/client.go @@ -12,6 +12,7 @@ import ( "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/log" D "github.com/miekg/dns" "github.com/zhangyunhao116/fastrand" @@ -97,12 +98,22 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) conn = tls.Client(conn, ca.GetGlobalTLSConfig(c.Client.TLSConfig)) } - msg, _, err := c.Client.ExchangeWithConn(m, &D.Conn{ + dConn := &D.Conn{ Conn: conn, UDPSize: c.Client.UDPSize, TsigSecret: c.Client.TsigSecret, TsigProvider: c.Client.TsigProvider, - }) + } + + msg, _, err := c.Client.ExchangeWithConn(m, dConn) + + // Resolvers MUST resend queries over TCP if they receive a truncated UDP response (with TC=1 set)! + if msg != nil && msg.Truncated && c.Client.Net == "" { + tcpClient := *c.Client // copy a client + tcpClient.Net = "tcp" + log.Debugln("[DNS] Truncated reply from %s:%s for %s over UDP, retrying over TCP", c.host, c.port, m.Question[0].String()) + msg, _, err = tcpClient.ExchangeWithConn(m, dConn) + } ch <- result{msg, err} }() From 80408855acf92409e378fc526bbc766269c92eb6 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 19 Mar 2024 15:18:00 +0800 Subject: [PATCH 004/311] chore: update quic-go to 0.42.0 --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 9becc8c8..b6f5ff54 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.41.1-0.20240307164142-46c6f7cdf2d1 + github.com/metacubex/quic-go v0.42.1-0.20240319071510-a251e5c66a5c github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 @@ -104,7 +104,7 @@ require ( github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect - go.uber.org/mock v0.3.0 // indirect + go.uber.org/mock v0.4.0 // indirect golang.org/x/mod v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect diff --git a/go.sum b/go.sum index 7277a32e..7c1e2425 100644 --- a/go.sum +++ b/go.sum @@ -104,8 +104,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-20240214095142-666a73bcf165 h1:QIQI4gEm+gTwVNdiAyF4EIz5cHm7kSlfDGFpYlAa5dg= github.com/metacubex/gvisor v0.0.0-20240214095142-666a73bcf165/go.mod h1:SKY70wiF1UTSoyuDZyKPMsUC6MsMxh8Y3ZNkIa6J3fU= -github.com/metacubex/quic-go v0.41.1-0.20240307164142-46c6f7cdf2d1 h1:63zKmEWU4MB5MjUSCmeDhm3OzilF7ypXWPq0gAA2GE8= -github.com/metacubex/quic-go v0.41.1-0.20240307164142-46c6f7cdf2d1/go.mod h1:F/t8VnA47xoia8ABlNA4InkZjssvFJ5p6E6jKdbkgAs= +github.com/metacubex/quic-go v0.42.1-0.20240319071510-a251e5c66a5c h1:AhaPKvVqF3N/jXFmlW51Cf1+KddslKAsZqcdgGhZjr0= +github.com/metacubex/quic-go v0.42.1-0.20240319071510-a251e5c66a5c/go.mod h1:iGx3Y1zynls/FjFgykLSqDcM81U0IKePRTXEz5g3iiQ= github.com/metacubex/sing v0.0.0-20240313064558-c197257f6542 h1:e9nBnrJBv3HzZVeSzJN0G2SADjebd2ZLF1F5dmsjUTc= github.com/metacubex/sing v0.0.0-20240313064558-c197257f6542/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01 h1:5INHs85Gp1JZsdF7fQp1pXUjfJOX2dhwZjuUQWJVSt8= @@ -218,8 +218,8 @@ gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiV gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ= go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= -go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= -go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= From 143fe84b8e78d5e78fc150731d634c6438b269e5 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 20 Mar 2024 09:30:00 +0800 Subject: [PATCH 005/311] chore: update gvisor --- go.mod | 7 +++---- go.sum | 14 ++++++-------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index b6f5ff54..c86ce9ce 100644 --- a/go.mod +++ b/go.mod @@ -23,9 +23,9 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 - github.com/metacubex/sing-tun v0.2.1-0.20240214100323-23e40bfb9067 + github.com/metacubex/sing-tun v0.2.1-0.20240320004934-5d2b35447bfd github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f - github.com/metacubex/sing-wireguard v0.0.0-20231209125515-0594297f7232 + github.com/metacubex/sing-wireguard v0.0.0-20240320005523-c46db1e2d1be github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 github.com/miekg/dns v1.1.58 github.com/mroth/weightedrand/v2 v2.1.0 @@ -84,7 +84,7 @@ require ( github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mdlayher/socket v0.4.1 // indirect - github.com/metacubex/gvisor v0.0.0-20240214095142-666a73bcf165 // indirect + github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec // indirect github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/pierrec/lz4/v4 v4.1.14 // indirect @@ -93,7 +93,6 @@ require ( github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect - github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // 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 diff --git a/go.sum b/go.sum index 7c1e2425..acb0b28a 100644 --- a/go.sum +++ b/go.sum @@ -102,8 +102,8 @@ github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI= github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88= -github.com/metacubex/gvisor v0.0.0-20240214095142-666a73bcf165 h1:QIQI4gEm+gTwVNdiAyF4EIz5cHm7kSlfDGFpYlAa5dg= -github.com/metacubex/gvisor v0.0.0-20240214095142-666a73bcf165/go.mod h1:SKY70wiF1UTSoyuDZyKPMsUC6MsMxh8Y3ZNkIa6J3fU= +github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc= +github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= github.com/metacubex/quic-go v0.42.1-0.20240319071510-a251e5c66a5c h1:AhaPKvVqF3N/jXFmlW51Cf1+KddslKAsZqcdgGhZjr0= github.com/metacubex/quic-go v0.42.1-0.20240319071510-a251e5c66a5c/go.mod h1:iGx3Y1zynls/FjFgykLSqDcM81U0IKePRTXEz5g3iiQ= github.com/metacubex/sing v0.0.0-20240313064558-c197257f6542 h1:e9nBnrJBv3HzZVeSzJN0G2SADjebd2ZLF1F5dmsjUTc= @@ -114,12 +114,12 @@ github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwV github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A= github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8= -github.com/metacubex/sing-tun v0.2.1-0.20240214100323-23e40bfb9067 h1:sB9Hiq/Fgq94WD1mAFDUTDaQAJ6y3WZ5nZMEavcK0/o= -github.com/metacubex/sing-tun v0.2.1-0.20240214100323-23e40bfb9067/go.mod h1:HWyO52kAVvuSUN2nms4ZlRfiAwgXO9wGQBJFjymqvOQ= +github.com/metacubex/sing-tun v0.2.1-0.20240320004934-5d2b35447bfd h1:NgLb6Lvr8ZxX0inWswVYjal2SUzsJJ54dFQNOluUJuE= +github.com/metacubex/sing-tun v0.2.1-0.20240320004934-5d2b35447bfd/go.mod h1:GfLZG/QgGpW9+BPjltzONrL5vVms86TWqmZ23J68ISc= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbTMBip/C+R79bvbfr42xH1gZl3uFb0RELdZiQ= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= -github.com/metacubex/sing-wireguard v0.0.0-20231209125515-0594297f7232 h1:loWjR+k9dxqBSgruGyT5hE8UCRMmCEjxqZbryfY9no4= -github.com/metacubex/sing-wireguard v0.0.0-20231209125515-0594297f7232/go.mod h1:NGCrBZ+fUmp81yaA1kVskcNWBnwl5z4UHxz47A01zm8= +github.com/metacubex/sing-wireguard v0.0.0-20240320005523-c46db1e2d1be h1:xZHAKZPMdVIwk8zCbmL6vi87xC4VRKWhNLRoO3TdRwA= +github.com/metacubex/sing-wireguard v0.0.0-20240320005523-c46db1e2d1be/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 h1:as/aO/fM8nv4W4pOr9EETP6kV/Oaujk3fUNyQSJK61c= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= @@ -169,8 +169,6 @@ github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e h1:iGH0RMv2F github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e/go.mod h1:YbL4TKHRR6APYQv3U2RGfwLDpPYSyWz6oUlpISBEzBE= github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= github.com/samber/lo v1.39.0/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.24.2 h1:kcR0erMbLg5/3LcInpw0X/rrPSqq4CDPyI6A6ZRC18Y= github.com/shirou/gopsutil/v3 v3.24.2/go.mod h1:tSg/594BcA+8UdQU2XcW803GWYgdtauFFPgJCJKZlVk= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= From 7fd5902e6bce535deedd693137b7fe4c90edd380 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 20 Mar 2024 11:30:39 +0800 Subject: [PATCH 006/311] chore: wireguard outbound only can set `ip` and `ipv6` outside `peers` https://github.com/MetaCubeX/mihomo/issues/522 --- adapter/outbound/wireguard.go | 23 +++++++---------------- docs/config.yaml | 4 +--- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 7c021c87..79803842 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -47,6 +47,8 @@ type WireGuardOption struct { BasicOption WireGuardPeerOption Name string `proxy:"name"` + Ip string `proxy:"ip,omitempty"` + Ipv6 string `proxy:"ipv6,omitempty"` PrivateKey string `proxy:"private-key"` Workers int `proxy:"workers,omitempty"` MTU int `proxy:"mtu,omitempty"` @@ -62,8 +64,6 @@ type WireGuardOption struct { type WireGuardPeerOption struct { Server string `proxy:"server"` Port int `proxy:"port"` - Ip string `proxy:"ip,omitempty"` - Ipv6 string `proxy:"ipv6,omitempty"` PublicKey string `proxy:"public-key,omitempty"` PreSharedKey string `proxy:"pre-shared-key,omitempty"` Reserved []uint8 `proxy:"reserved,omitempty"` @@ -98,7 +98,7 @@ func (option WireGuardPeerOption) Addr() M.Socksaddr { return M.ParseSocksaddrHostPort(option.Server, uint16(option.Port)) } -func (option WireGuardPeerOption) Prefixes() ([]netip.Prefix, error) { +func (option WireGuardOption) Prefixes() ([]netip.Prefix, error) { localPrefixes := make([]netip.Prefix, 0, 2) if len(option.Ip) > 0 { if !strings.Contains(option.Ip, "/") { @@ -160,7 +160,10 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { } outbound.bind = wireguard.NewClientBind(context.Background(), wgSingErrorHandler{outbound.Name()}, outbound.dialer, isConnect, connectAddr, reserved) - var localPrefixes []netip.Prefix + localPrefixes, err := option.Prefixes() + if err != nil { + return nil, err + } var privateKey string { @@ -172,7 +175,6 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { } ipcConf := "private_key=" + privateKey if peersLen := len(option.Peers); peersLen > 0 { - localPrefixes = make([]netip.Prefix, 0, peersLen*2) for i, peer := range option.Peers { var peerPublicKey, preSharedKey string { @@ -208,11 +210,6 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { copy(reserved[:], option.Reserved) outbound.bind.SetReservedForEndpoint(destination, reserved) } - prefixes, err := peer.Prefixes() - if err != nil { - return nil, err - } - localPrefixes = append(localPrefixes, prefixes...) } } else { var peerPublicKey, preSharedKey string @@ -235,11 +232,6 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { if preSharedKey != "" { ipcConf += "\npreshared_key=" + preSharedKey } - var err error - localPrefixes, err = option.Prefixes() - if err != nil { - return nil, err - } var has4, has6 bool for _, address := range localPrefixes { if address.Addr().Is4() { @@ -266,7 +258,6 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { if len(localPrefixes) == 0 { return nil, E.New("missing local address") } - var err error outbound.tunDevice, err = wireguard.NewStackDevice(localPrefixes, uint32(mtu)) if err != nil { return nil, E.Cause(err, "create WireGuard device") diff --git a/docs/config.yaml b/docs/config.yaml index d912eb65..4d0e995e 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -708,12 +708,10 @@ proxies: # socks5 # dialer-proxy: "ss1" # remote-dns-resolve: true # 强制dns远程解析,默认值为false # dns: [ 1.1.1.1, 8.8.8.8 ] # 仅在remote-dns-resolve为true时生效 - # 如果peers不为空,该段落中的allowed-ips不可为空;前面段落的server,port,ip,ipv6,public-key,pre-shared-key均会被忽略,但private-key会被保留且只能在顶层指定 + # 如果peers不为空,该段落中的allowed-ips不可为空;前面段落的server,port,public-key,pre-shared-key均会被忽略,但private-key会被保留且只能在顶层指定 # peers: # - server: 162.159.192.1 # port: 2480 - # ip: 172.16.0.2 - # ipv6: fd01:5ca1:ab1e:80fa:ab85:6eea:213f:f4a5 # public-key: Cr8hWlKvtDt7nrvf+f0brNQQzabAqrjfBvas9pmowjo= # # pre-shared-key: 31aIhAPwktDGpH4JDhA8GNvjFXEf/a6+UaQRyOAiyfM= # allowed-ips: ['0.0.0.0/0'] From e1a5b93cce7088e0c5806823e70425916bedd593 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 20 Mar 2024 12:34:21 +0800 Subject: [PATCH 007/311] chore: rebuild wireguard server address resolve --- adapter/outbound/wireguard.go | 187 ++++++++++++++++++++++------------ go.mod | 2 +- go.sum | 4 +- 3 files changed, 124 insertions(+), 69 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 79803842..3a21c938 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -13,6 +13,7 @@ import ( "strings" "sync" + "github.com/metacubex/mihomo/common/atomic" CN "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/proxydialer" @@ -37,8 +38,7 @@ type WireGuard struct { device *device.Device tunDevice wireguard.Device dialer proxydialer.SingDialer - startOnce sync.Once - startErr error + init func(ctx context.Context) error resolver *dns.Resolver refP *refProxyAdapter } @@ -141,6 +141,14 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { } runtime.SetFinalizer(outbound, closeWireGuard) + resolv := func(ctx context.Context, address M.Socksaddr) (netip.AddrPort, error) { + if address.Addr.IsValid() { + return address.AddrPort(), nil + } + udpAddr, err := resolveUDPAddrWithPrefer(ctx, "udp", address.String(), outbound.prefer) + return udpAddr.AddrPort(), err + } + var reserved [3]uint8 if len(option.Reserved) > 0 { if len(option.Reserved) != 3 { @@ -158,7 +166,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { connectAddr = option.Addr() } } - outbound.bind = wireguard.NewClientBind(context.Background(), wgSingErrorHandler{outbound.Name()}, outbound.dialer, isConnect, connectAddr, reserved) + outbound.bind = wireguard.NewClientBind(context.Background(), wgSingErrorHandler{outbound.Name()}, outbound.dialer, isConnect, connectAddr.AddrPort(), reserved) localPrefixes, err := option.Prefixes() if err != nil { @@ -173,84 +181,145 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { } privateKey = hex.EncodeToString(bytes) } - ipcConf := "private_key=" + privateKey - if peersLen := len(option.Peers); peersLen > 0 { + + if len(option.Peers) > 0 { for i, peer := range option.Peers { - var peerPublicKey, preSharedKey string - { - bytes, err := base64.StdEncoding.DecodeString(peer.PublicKey) - if err != nil { - return nil, E.Cause(err, "decode public key for peer ", i) - } - peerPublicKey = hex.EncodeToString(bytes) + bytes, err := base64.StdEncoding.DecodeString(peer.PublicKey) + if err != nil { + return nil, E.Cause(err, "decode public key for peer ", i) } + peer.PublicKey = hex.EncodeToString(bytes) + if peer.PreSharedKey != "" { bytes, err := base64.StdEncoding.DecodeString(peer.PreSharedKey) if err != nil { return nil, E.Cause(err, "decode pre shared key for peer ", i) } - preSharedKey = hex.EncodeToString(bytes) - } - destination := peer.Addr() - ipcConf += "\npublic_key=" + peerPublicKey - ipcConf += "\nendpoint=" + destination.String() - if preSharedKey != "" { - ipcConf += "\npreshared_key=" + preSharedKey + peer.PreSharedKey = hex.EncodeToString(bytes) } + if len(peer.AllowedIPs) == 0 { return nil, E.New("missing allowed_ips for peer ", i) } - for _, allowedIP := range peer.AllowedIPs { - ipcConf += "\nallowed_ip=" + allowedIP - } + if len(peer.Reserved) > 0 { if len(peer.Reserved) != 3 { return nil, E.New("invalid reserved value for peer ", i, ", required 3 bytes, got ", len(peer.Reserved)) } - copy(reserved[:], option.Reserved) - outbound.bind.SetReservedForEndpoint(destination, reserved) } } } else { - var peerPublicKey, preSharedKey string { bytes, err := base64.StdEncoding.DecodeString(option.PublicKey) if err != nil { return nil, E.Cause(err, "decode peer public key") } - peerPublicKey = hex.EncodeToString(bytes) + option.PublicKey = hex.EncodeToString(bytes) } if option.PreSharedKey != "" { bytes, err := base64.StdEncoding.DecodeString(option.PreSharedKey) if err != nil { return nil, E.Cause(err, "decode pre shared key") } - preSharedKey = hex.EncodeToString(bytes) - } - ipcConf += "\npublic_key=" + peerPublicKey - ipcConf += "\nendpoint=" + connectAddr.String() - if preSharedKey != "" { - ipcConf += "\npreshared_key=" + preSharedKey - } - var has4, has6 bool - for _, address := range localPrefixes { - if address.Addr().Is4() { - has4 = true - } else { - has6 = true - } - } - if has4 { - ipcConf += "\nallowed_ip=0.0.0.0/0" - } - if has6 { - ipcConf += "\nallowed_ip=::/0" + option.PreSharedKey = hex.EncodeToString(bytes) } } - if option.PersistentKeepalive != 0 { - ipcConf += fmt.Sprintf("\npersistent_keepalive_interval=%d", option.PersistentKeepalive) + var ( + initOk atomic.Bool + initMutex sync.Mutex + initErr error + ) + + outbound.init = func(ctx context.Context) error { + if initOk.Load() { + return nil + } + initMutex.Lock() + defer initMutex.Unlock() + // double check like sync.Once + if initOk.Load() { + return nil + } + if initErr != nil { + return initErr + } + + outbound.bind.ResetReservedForEndpoint() + ipcConf := "private_key=" + privateKey + if len(option.Peers) > 0 { + for i, peer := range option.Peers { + destination, err := resolv(ctx, peer.Addr()) + if err != nil { + // !!! do not set initErr here !!! + // let us can retry domain resolve in next time + return E.Cause(err, "resolve endpoint domain for peer ", i) + } + ipcConf += "\npublic_key=" + peer.PublicKey + ipcConf += "\nendpoint=" + destination.String() + if peer.PreSharedKey != "" { + ipcConf += "\npreshared_key=" + peer.PreSharedKey + } + for _, allowedIP := range peer.AllowedIPs { + ipcConf += "\nallowed_ip=" + allowedIP + } + if len(peer.Reserved) > 0 { + copy(reserved[:], option.Reserved) + outbound.bind.SetReservedForEndpoint(destination, reserved) + } + } + } else { + ipcConf += "\npublic_key=" + option.PublicKey + destination, err := resolv(ctx, connectAddr) + if err != nil { + // !!! do not set initErr here !!! + // let us can retry domain resolve in next time + return E.Cause(err, "resolve endpoint domain") + } + outbound.bind.SetConnectAddr(destination) + ipcConf += "\nendpoint=" + destination.String() + if option.PreSharedKey != "" { + ipcConf += "\npreshared_key=" + option.PreSharedKey + } + var has4, has6 bool + for _, address := range localPrefixes { + if address.Addr().Is4() { + has4 = true + } else { + has6 = true + } + } + if has4 { + ipcConf += "\nallowed_ip=0.0.0.0/0" + } + if has6 { + ipcConf += "\nallowed_ip=::/0" + } + } + + if option.PersistentKeepalive != 0 { + ipcConf += fmt.Sprintf("\npersistent_keepalive_interval=%d", option.PersistentKeepalive) + } + + if debug.Enabled { + log.SingLogger.Trace(fmt.Sprintf("[WG](%s) created wireguard ipc conf: \n %s", option.Name, ipcConf)) + } + err = outbound.device.IpcSet(ipcConf) + if err != nil { + initErr = E.Cause(err, "setup wireguard") + return initErr + } + + err = outbound.tunDevice.Start() + if err != nil { + initErr = err + return initErr + } + + initOk.Store(true) + return nil } + mtu := option.MTU if mtu == 0 { mtu = 1408 @@ -270,14 +339,6 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { log.SingLogger.Error(fmt.Sprintf("[WG](%s) %s", option.Name, fmt.Sprintf(format, args...))) }, }, option.Workers) - if debug.Enabled { - log.SingLogger.Trace(fmt.Sprintf("[WG](%s) created wireguard ipc conf: \n %s", option.Name, ipcConf)) - } - err = outbound.device.IpcSet(ipcConf) - if err != nil { - return nil, E.Cause(err, "setup wireguard") - } - //err = outbound.tunDevice.Start() var has6 bool for _, address := range localPrefixes { @@ -317,11 +378,8 @@ func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts options := w.Base.DialOptions(opts...) w.dialer.SetDialer(dialer.NewDialer(options...)) var conn net.Conn - w.startOnce.Do(func() { - w.startErr = w.tunDevice.Start() - }) - if w.startErr != nil { - return nil, w.startErr + if err = w.init(ctx); err != nil { + return nil, err } if !metadata.Resolved() || w.resolver != nil { r := resolver.DefaultResolver @@ -349,11 +407,8 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat options := w.Base.DialOptions(opts...) w.dialer.SetDialer(dialer.NewDialer(options...)) var pc net.PacketConn - w.startOnce.Do(func() { - w.startErr = w.tunDevice.Start() - }) - if w.startErr != nil { - return nil, w.startErr + if err = w.init(ctx); err != nil { + return nil, err } if err != nil { return nil, err diff --git a/go.mod b/go.mod index c86ce9ce..7ff43a9f 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/metacubex/sing-shadowsocks2 v0.2.0 github.com/metacubex/sing-tun v0.2.1-0.20240320004934-5d2b35447bfd github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f - github.com/metacubex/sing-wireguard v0.0.0-20240320005523-c46db1e2d1be + github.com/metacubex/sing-wireguard v0.0.0-20240320043244-d6a8de454284 github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 github.com/miekg/dns v1.1.58 github.com/mroth/weightedrand/v2 v2.1.0 diff --git a/go.sum b/go.sum index acb0b28a..8f557996 100644 --- a/go.sum +++ b/go.sum @@ -118,8 +118,8 @@ github.com/metacubex/sing-tun v0.2.1-0.20240320004934-5d2b35447bfd h1:NgLb6Lvr8Z github.com/metacubex/sing-tun v0.2.1-0.20240320004934-5d2b35447bfd/go.mod h1:GfLZG/QgGpW9+BPjltzONrL5vVms86TWqmZ23J68ISc= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbTMBip/C+R79bvbfr42xH1gZl3uFb0RELdZiQ= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= -github.com/metacubex/sing-wireguard v0.0.0-20240320005523-c46db1e2d1be h1:xZHAKZPMdVIwk8zCbmL6vi87xC4VRKWhNLRoO3TdRwA= -github.com/metacubex/sing-wireguard v0.0.0-20240320005523-c46db1e2d1be/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= +github.com/metacubex/sing-wireguard v0.0.0-20240320043244-d6a8de454284 h1:aort+t6Hb+umsOFODT/P5fzTWr/4Bypp70jXUHhryR8= +github.com/metacubex/sing-wireguard v0.0.0-20240320043244-d6a8de454284/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 h1:as/aO/fM8nv4W4pOr9EETP6kV/Oaujk3fUNyQSJK61c= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= From 2e94531c72eca564ddbec268bf7760cfbca44428 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 20 Mar 2024 18:07:13 +0800 Subject: [PATCH 008/311] Revert "fix hysteria faketcp lookback in TUN mode (#601)" This reverts commit fdaa6a22a4d14ccb08a5173128e1c47585678627. --- component/dialer/bind.go | 3 ++- component/iface/iface.go | 10 ---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/component/dialer/bind.go b/component/dialer/bind.go index 72df8c72..9b6471a3 100644 --- a/component/dialer/bind.go +++ b/component/dialer/bind.go @@ -14,6 +14,7 @@ func LookupLocalAddrFromIfaceName(ifaceName string, network string, destination if err != nil { return nil, err } + destination = destination.Unmap() var addr netip.Prefix switch network { @@ -23,7 +24,7 @@ func LookupLocalAddrFromIfaceName(ifaceName string, network string, destination addr, err = ifaceObj.PickIPv6Addr(destination) default: if destination.IsValid() { - if destination.Is4() || destination.Is4In6() { + if destination.Is4() { addr, err = ifaceObj.PickIPv4Addr(destination) } else { addr, err = ifaceObj.PickIPv6Addr(destination) diff --git a/component/iface/iface.go b/component/iface/iface.go index bf186165..dd932b46 100644 --- a/component/iface/iface.go +++ b/component/iface/iface.go @@ -4,7 +4,6 @@ import ( "errors" "net" "net/netip" - "strings" "time" "github.com/metacubex/mihomo/common/singledo" @@ -38,21 +37,12 @@ func ResolveInterface(name string) (*Interface, error) { if err != nil { continue } - // if not available device like Meta, dummy0, docker0, etc. - if (iface.Flags&net.FlagMulticast == 0) || (iface.Flags&net.FlagPointToPoint != 0) || (iface.Flags&net.FlagRunning == 0) { - continue - } ipNets := make([]netip.Prefix, 0, len(addrs)) for _, addr := range addrs { ipNet := addr.(*net.IPNet) ip, _ := netip.AddrFromSlice(ipNet.IP) - //unavailable IPv6 Address - if ip.Is6() && strings.HasPrefix(ip.String(), "fe80") { - continue - } - ones, bits := ipNet.Mask.Size() if bits == 32 { ip = ip.Unmap() From 284b01ca389d2228800c7411a471c50ccbbc1294 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 21 Mar 2024 12:23:45 +0800 Subject: [PATCH 009/311] fix: wireguard client bind --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7ff43a9f..c70aca97 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/metacubex/sing-shadowsocks2 v0.2.0 github.com/metacubex/sing-tun v0.2.1-0.20240320004934-5d2b35447bfd github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f - github.com/metacubex/sing-wireguard v0.0.0-20240320043244-d6a8de454284 + github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 github.com/miekg/dns v1.1.58 github.com/mroth/weightedrand/v2 v2.1.0 diff --git a/go.sum b/go.sum index 8f557996..7422a062 100644 --- a/go.sum +++ b/go.sum @@ -118,8 +118,8 @@ github.com/metacubex/sing-tun v0.2.1-0.20240320004934-5d2b35447bfd h1:NgLb6Lvr8Z github.com/metacubex/sing-tun v0.2.1-0.20240320004934-5d2b35447bfd/go.mod h1:GfLZG/QgGpW9+BPjltzONrL5vVms86TWqmZ23J68ISc= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbTMBip/C+R79bvbfr42xH1gZl3uFb0RELdZiQ= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= -github.com/metacubex/sing-wireguard v0.0.0-20240320043244-d6a8de454284 h1:aort+t6Hb+umsOFODT/P5fzTWr/4Bypp70jXUHhryR8= -github.com/metacubex/sing-wireguard v0.0.0-20240320043244-d6a8de454284/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= +github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 h1:AGyIB55UfQm/0ZH0HtQO9u3l//yjtHUpjeRjjPGfGRI= +github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 h1:as/aO/fM8nv4W4pOr9EETP6kV/Oaujk3fUNyQSJK61c= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= From 9c08e936f971e4261e854d7bf039e28ab3eb9593 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 22 Mar 2024 00:33:38 +0800 Subject: [PATCH 010/311] fix: unmap 4in6 ip in wireguard --- adapter/outbound/wireguard.go | 7 ++++++- dns/resolver.go | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 3a21c938..fe1f69fa 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -146,7 +146,12 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { return address.AddrPort(), nil } udpAddr, err := resolveUDPAddrWithPrefer(ctx, "udp", address.String(), outbound.prefer) - return udpAddr.AddrPort(), err + if err != nil { + return netip.AddrPort{}, err + } + // net.ResolveUDPAddr maybe return 4in6 address, so unmap at here + addrPort := udpAddr.AddrPort() + return netip.AddrPortFrom(addrPort.Addr().Unmap(), addrPort.Port()), nil } var reserved [3]uint8 diff --git a/dns/resolver.go b/dns/resolver.go index 8ea68ed7..08de69ad 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -324,7 +324,7 @@ func (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (msg *D.Msg, err er func (r *Resolver) lookupIP(ctx context.Context, host string, dnsType uint16) (ips []netip.Addr, err error) { ip, err := netip.ParseAddr(host) if err == nil { - isIPv4 := ip.Is4() + isIPv4 := ip.Is4() || ip.Is4In6() if dnsType == D.TypeAAAA && !isIPv4 { return []netip.Addr{ip}, nil } else if dnsType == D.TypeA && isIPv4 { From d56a439a741ec8762409598bc927390cdf67c5fa Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 23 Mar 2024 22:30:19 +0800 Subject: [PATCH 011/311] fix: dns truncate not work --- dns/client.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dns/client.go b/dns/client.go index fc76c124..3b4efed1 100644 --- a/dns/client.go +++ b/dns/client.go @@ -78,7 +78,9 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) options = append(options, dialer.WithInterface(c.iface)) } - conn, err := getDialHandler(c.r, c.proxyAdapter, c.proxyName, options...)(ctx, network, net.JoinHostPort(ip.String(), c.port)) + dialHandler := getDialHandler(c.r, c.proxyAdapter, c.proxyName, options...) + addr := net.JoinHostPort(ip.String(), c.port) + conn, err := dialHandler(ctx, network, addr) if err != nil { return nil, err } @@ -111,7 +113,16 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) if msg != nil && msg.Truncated && c.Client.Net == "" { tcpClient := *c.Client // copy a client tcpClient.Net = "tcp" + network = "tcp" log.Debugln("[DNS] Truncated reply from %s:%s for %s over UDP, retrying over TCP", c.host, c.port, m.Question[0].String()) + dConn.Conn, err = dialHandler(ctx, network, addr) + if err != nil { + ch <- result{msg, err} + return + } + defer func() { + _ = conn.Close() + }() msg, _, err = tcpClient.ExchangeWithConn(m, dConn) } From d2ae94f20b70a23f82fc74d529bbe4ef3fd43e1d Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 24 Mar 2024 21:24:50 +0800 Subject: [PATCH 012/311] fix: iface panic https://github.com/MetaCubeX/mihomo/issues/1130 --- component/iface/iface.go | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/component/iface/iface.go b/component/iface/iface.go index dd932b46..2fd36861 100644 --- a/component/iface/iface.go +++ b/component/iface/iface.go @@ -40,16 +40,25 @@ func ResolveInterface(name string) (*Interface, error) { ipNets := make([]netip.Prefix, 0, len(addrs)) for _, addr := range addrs { - ipNet := addr.(*net.IPNet) - ip, _ := netip.AddrFromSlice(ipNet.IP) - - ones, bits := ipNet.Mask.Size() - if bits == 32 { + var pf netip.Prefix + switch addr.(type) { + case *net.IPNet: + ipNet := addr.(*net.IPNet) + ip, _ := netip.AddrFromSlice(ipNet.IP) + ones, bits := ipNet.Mask.Size() + if bits == 32 { + ip = ip.Unmap() + } + pf = netip.PrefixFrom(ip, ones) + case *net.IPAddr: + ipNet := addr.(*net.IPAddr) + ip, _ := netip.AddrFromSlice(ipNet.IP) ip = ip.Unmap() + pf = netip.PrefixFrom(ip, ip.BitLen()) + } + if pf.IsValid() { + ipNets = append(ipNets, pf) } - - pf := netip.PrefixFrom(ip, ones) - ipNets = append(ipNets, pf) } r[iface.Name] = &Interface{ From 5af7f4e847ede794c4db4cbaa0964cba0eb8e5dc Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 24 Mar 2024 21:31:52 +0800 Subject: [PATCH 013/311] chore: allow config `table-index` for tun https://github.com/MetaCubeX/mihomo/issues/1128 --- config/config.go | 2 ++ hub/route/configs.go | 4 ++++ listener/config/tun.go | 1 + listener/inbound/tun.go | 2 ++ listener/listener.go | 3 ++- listener/sing_tun/server.go | 6 +++++- 6 files changed, 16 insertions(+), 2 deletions(-) diff --git a/config/config.go b/config/config.go index ca866491..ca179ed0 100644 --- a/config/config.go +++ b/config/config.go @@ -265,6 +265,7 @@ type RawTun struct { EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"` UDPTimeout int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"` FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"` + TableIndex int `yaml:"table-index" json:"table-index"` } type RawTuicServer struct { @@ -1448,6 +1449,7 @@ func parseTun(rawTun RawTun, general *General) error { EndpointIndependentNat: rawTun.EndpointIndependentNat, UDPTimeout: rawTun.UDPTimeout, FileDescriptor: rawTun.FileDescriptor, + TableIndex: rawTun.TableIndex, } return nil diff --git a/hub/route/configs.go b/hub/route/configs.go index ec0b464c..653e4351 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -91,6 +91,7 @@ type tunSchema struct { EndpointIndependentNat *bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"` UDPTimeout *int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"` FileDescriptor *int `yaml:"file-descriptor" json:"file-descriptor"` + TableIndex *int `yaml:"table-index" json:"table-index"` } type tuicServerSchema struct { @@ -209,6 +210,9 @@ func pointerOrDefaultTun(p *tunSchema, def LC.Tun) LC.Tun { if p.FileDescriptor != nil { def.FileDescriptor = *p.FileDescriptor } + if p.TableIndex != nil { + def.TableIndex = *p.TableIndex + } } return def } diff --git a/listener/config/tun.go b/listener/config/tun.go index 1772c6f5..7467e4a6 100644 --- a/listener/config/tun.go +++ b/listener/config/tun.go @@ -49,4 +49,5 @@ type Tun struct { EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"` UDPTimeout int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"` FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"` + TableIndex int `yaml:"table-index" json:"table-index"` } diff --git a/listener/inbound/tun.go b/listener/inbound/tun.go index a1fdebfa..51747c46 100644 --- a/listener/inbound/tun.go +++ b/listener/inbound/tun.go @@ -40,6 +40,7 @@ type TunOption struct { EndpointIndependentNat bool `inbound:"endpoint_independent_nat,omitempty"` UDPTimeout int64 `inbound:"udp_timeout,omitempty"` FileDescriptor int `inbound:"file-descriptor,omitempty"` + TableIndex int `inbound:"table-index,omitempty"` } func (o TunOption) Equal(config C.InboundConfig) bool { @@ -118,6 +119,7 @@ func NewTun(options *TunOption) (*Tun, error) { EndpointIndependentNat: options.EndpointIndependentNat, UDPTimeout: options.UDPTimeout, FileDescriptor: options.FileDescriptor, + TableIndex: options.TableIndex, }, }, nil } diff --git a/listener/listener.go b/listener/listener.go index ac602971..e3506188 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -823,7 +823,8 @@ func hasTunConfigChange(tunConf *LC.Tun) bool { LastTunConf.StrictRoute != tunConf.StrictRoute || LastTunConf.EndpointIndependentNat != tunConf.EndpointIndependentNat || LastTunConf.UDPTimeout != tunConf.UDPTimeout || - LastTunConf.FileDescriptor != tunConf.FileDescriptor { + LastTunConf.FileDescriptor != tunConf.FileDescriptor || + LastTunConf.TableIndex != tunConf.TableIndex { return true } diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index cc26d37d..96ec1573 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -112,6 +112,10 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis } else { udpTimeout = int64(sing.UDPTimeout.Seconds()) } + tableIndex := options.TableIndex + if tableIndex == 0 { + tableIndex = 2022 + } includeUID := uidToRange(options.IncludeUID) if len(options.IncludeUIDRange) > 0 { var err error @@ -225,7 +229,7 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis ExcludePackage: options.ExcludePackage, FileDescriptor: options.FileDescriptor, InterfaceMonitor: defaultInterfaceMonitor, - TableIndex: 2022, + TableIndex: tableIndex, } err = l.buildAndroidRules(&tunOptions) From 288899a47318902802800230d5e35ea7416b52ea Mon Sep 17 00:00:00 2001 From: bobo liu <7552030+fakeboboliu@users.noreply.github.com> Date: Sun, 24 Mar 2024 21:41:05 +0800 Subject: [PATCH 014/311] chore: stylish d2ae94f2 (#1132) --- component/iface/iface.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/component/iface/iface.go b/component/iface/iface.go index 2fd36861..1d0219df 100644 --- a/component/iface/iface.go +++ b/component/iface/iface.go @@ -41,9 +41,8 @@ func ResolveInterface(name string) (*Interface, error) { ipNets := make([]netip.Prefix, 0, len(addrs)) for _, addr := range addrs { var pf netip.Prefix - switch addr.(type) { + switch ipNet := addr.(type) { case *net.IPNet: - ipNet := addr.(*net.IPNet) ip, _ := netip.AddrFromSlice(ipNet.IP) ones, bits := ipNet.Mask.Size() if bits == 32 { @@ -51,7 +50,6 @@ func ResolveInterface(name string) (*Interface, error) { } pf = netip.PrefixFrom(ip, ones) case *net.IPAddr: - ipNet := addr.(*net.IPAddr) ip, _ := netip.AddrFromSlice(ipNet.IP) ip = ip.Unmap() pf = netip.PrefixFrom(ip, ip.BitLen()) From 0b4662e4b7f52f631112d16a69b81e1a49c8fe42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=90=BD=E5=BF=83?= <33619903+Luoxin@users.noreply.github.com> Date: Tue, 26 Mar 2024 14:19:33 +0800 Subject: [PATCH 015/311] fixed: invalid argument to Intn (#1133) --- transport/vmess/http.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/transport/vmess/http.go b/transport/vmess/http.go index 6da9759e..b023fee4 100644 --- a/transport/vmess/http.go +++ b/transport/vmess/http.go @@ -3,6 +3,7 @@ package vmess import ( "bufio" "bytes" + "errors" "fmt" "net" "net/http" @@ -54,6 +55,10 @@ func (hc *httpConn) Write(b []byte) (int, error) { return hc.Conn.Write(b) } + if len(hc.cfg.Path) == 0 { + return -1, errors.New("path is empty") + } + path := hc.cfg.Path[fastrand.Intn(len(hc.cfg.Path))] host := hc.cfg.Host if header := hc.cfg.Headers["Host"]; len(header) != 0 { From 82517e6ba8059339287911af899ffdffca6a4044 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Wed, 27 Mar 2024 19:34:11 +0800 Subject: [PATCH 016/311] chore: include short commit ID in release note --- .github/genReleaseNote.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/genReleaseNote.sh b/.github/genReleaseNote.sh index 0425061d..ab617fd0 100755 --- a/.github/genReleaseNote.sh +++ b/.github/genReleaseNote.sh @@ -18,15 +18,15 @@ if [ -z "$version_range" ]; then fi echo "## What's Changed" > release.md -git log --pretty=format:"* %s by @%an" --grep="^feat" -i $version_range | sort -f | uniq >> release.md +git log --pretty=format:"* %h %s by @%an" --grep="^feat" -i $version_range | sort -f | uniq >> release.md echo "" >> release.md echo "## BUG & Fix" >> release.md -git log --pretty=format:"* %s by @%an" --grep="^fix" -i $version_range | sort -f | uniq >> release.md +git log --pretty=format:"* %h %s by @%an" --grep="^fix" -i $version_range | sort -f | uniq >> release.md echo "" >> release.md echo "## Maintenance" >> release.md -git log --pretty=format:"* %s by @%an" --grep="^chore\|^docs\|^refactor" -i $version_range | sort -f | uniq >> release.md +git log --pretty=format:"* %h %s by @%an" --grep="^chore\|^docs\|^refactor" -i $version_range | sort -f | uniq >> release.md echo "" >> release.md -echo "**Full Changelog**: https://github.com/MetaCubeX/Clash.Meta/compare/$version_range" >> release.md +echo "**Full Changelog**: https://github.com/MetaCubeX/mihomo/compare/$version_range" >> release.md From 06b5121d9eaebad3a8f1ff52cb7fd9d58a321823 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 28 Mar 2024 19:26:41 +0800 Subject: [PATCH 017/311] chore: embed ca-certificates.crt --- .github/workflows/build.yml | 6 ++++++ component/ca/ca-certificates.crt | 0 component/ca/config.go | 18 ++++++++++++++++-- 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 component/ca/ca-certificates.crt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bad84cd1..f9bbbba9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -141,6 +141,12 @@ jobs: run: | go test ./... + - name: Update UA + run: | + sudo apt-get install ca-certificates + sudo update-ca-certificates + cp -f /etc/ssl/certs/ca-certificates.crt component/ca/ca-certificates.crt + - name: Build core env: GOOS: ${{matrix.jobs.goos}} diff --git a/component/ca/ca-certificates.crt b/component/ca/ca-certificates.crt new file mode 100644 index 00000000..e69de29b diff --git a/component/ca/config.go b/component/ca/config.go index 03fb007c..53cb98ab 100644 --- a/component/ca/config.go +++ b/component/ca/config.go @@ -5,10 +5,12 @@ import ( "crypto/sha256" "crypto/tls" "crypto/x509" + _ "embed" "encoding/hex" "errors" "fmt" "os" + "strconv" "strings" "sync" ) @@ -18,6 +20,11 @@ var globalCertPool *x509.CertPool var mutex sync.RWMutex var errNotMatch = errors.New("certificate fingerprints do not match") +//go:embed ca-certificates.crt +var _CaCertificates []byte +var DisableEmbedCa, _ = strconv.ParseBool(os.Getenv("DISABLE_EMBED_CA")) +var DisableSystemCa, _ = strconv.ParseBool(os.Getenv("DISABLE_SYSTEM_CA")) + func AddCertificate(certificate string) error { mutex.Lock() defer mutex.Unlock() @@ -34,13 +41,20 @@ func AddCertificate(certificate string) error { func initializeCertPool() { var err error - globalCertPool, err = x509.SystemCertPool() - if err != nil { + if DisableSystemCa { globalCertPool = x509.NewCertPool() + } else { + globalCertPool, err = x509.SystemCertPool() + if err != nil { + globalCertPool = x509.NewCertPool() + } } for _, cert := range trustCerts { globalCertPool.AddCert(cert) } + if !DisableEmbedCa { + globalCertPool.AppendCertsFromPEM(_CaCertificates) + } } func ResetCertificate() { From 367a287153388aab16a8e2abbc9c8c4946b91eaf Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 28 Mar 2024 21:49:44 +0800 Subject: [PATCH 018/311] chore: don't lookup process when Type==INNER --- tunnel/tunnel.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index f8fdcf11..d5a226e9 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -596,7 +596,7 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { defer configMux.RUnlock() var ( resolved bool - attemptProcessLookup = true + attemptProcessLookup = metadata.Type != C.INNER ) if node, ok := resolver.DefaultHosts.Search(metadata.Host, false); ok { From eae1f05e88365706141bf6c64663e2a16beea4ff Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 28 Mar 2024 21:57:48 +0800 Subject: [PATCH 019/311] fix: wireguard multi peers public key parse --- adapter/outbound/wireguard.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index fe1f69fa..1044c8ec 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -188,7 +188,8 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { } if len(option.Peers) > 0 { - for i, peer := range option.Peers { + for i := range option.Peers { + peer := &option.Peers[i] // we need modify option here bytes, err := base64.StdEncoding.DecodeString(peer.PublicKey) if err != nil { return nil, E.Cause(err, "decode public key for peer ", i) From 89d7b8138ac809b9f1385d824b0b657ab79603e8 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 28 Mar 2024 23:19:42 +0800 Subject: [PATCH 020/311] chore: turned off ECN by default --- config/config.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/config/config.go b/config/config.go index ca179ed0..b2c8ec16 100644 --- a/config/config.go +++ b/config/config.go @@ -482,6 +482,11 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { "www.msftconnecttest.com", }, }, + Experimental: Experimental{ + // https://github.com/quic-go/quic-go/issues/4178 + // Quic-go currently cannot automatically fall back on platforms that do not support ecn, so this feature is turned off by default. + QUICGoDisableECN: true, + }, Sniffer: RawSniffer{ Enable: false, Sniffing: []string{}, From 11f0983e5c3e4d3405c2cb1a9f604299e5b1c5fa Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 28 Mar 2024 23:33:56 +0800 Subject: [PATCH 021/311] fix: resolve by ProxyServerHostResolver should not retry with DefaultResolver when error occurs --- component/resolver/resolver.go | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index 8cbc62fa..f9b56e47 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -213,11 +213,7 @@ func ResolveIP(ctx context.Context, host string) (netip.Addr, error) { // ResolveIPv4ProxyServerHost proxies server host only func ResolveIPv4ProxyServerHost(ctx context.Context, host string) (netip.Addr, error) { if ProxyServerHostResolver != nil { - if ip, err := ResolveIPv4WithResolver(ctx, host, ProxyServerHostResolver); err != nil { - return ResolveIPv4(ctx, host) - } else { - return ip, nil - } + return ResolveIPv4WithResolver(ctx, host, ProxyServerHostResolver) } return ResolveIPv4(ctx, host) } @@ -225,11 +221,7 @@ func ResolveIPv4ProxyServerHost(ctx context.Context, host string) (netip.Addr, e // ResolveIPv6ProxyServerHost proxies server host only func ResolveIPv6ProxyServerHost(ctx context.Context, host string) (netip.Addr, error) { if ProxyServerHostResolver != nil { - if ip, err := ResolveIPv6WithResolver(ctx, host, ProxyServerHostResolver); err != nil { - return ResolveIPv6(ctx, host) - } else { - return ip, nil - } + return ResolveIPv6WithResolver(ctx, host, ProxyServerHostResolver) } return ResolveIPv6(ctx, host) } @@ -237,11 +229,7 @@ func ResolveIPv6ProxyServerHost(ctx context.Context, host string) (netip.Addr, e // ResolveProxyServerHost proxies server host only func ResolveProxyServerHost(ctx context.Context, host string) (netip.Addr, error) { if ProxyServerHostResolver != nil { - if ip, err := ResolveIPWithResolver(ctx, host, ProxyServerHostResolver); err != nil { - return ResolveIP(ctx, host) - } else { - return ip, err - } + return ResolveIPWithResolver(ctx, host, ProxyServerHostResolver) } return ResolveIP(ctx, host) } From 914bc8a3e9d6e8fcc83a0589b45af18bb0fc714f Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Fri, 29 Mar 2024 13:43:11 +0800 Subject: [PATCH 022/311] fix: Fix `DOMAIN-REGEX` parsing --- config/config.go | 2 +- rules/provider/classical_strategy.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/config/config.go b/config/config.go index b2c8ec16..c7931573 100644 --- a/config/config.go +++ b/config/config.go @@ -921,7 +921,7 @@ func parseRules(rulesConfig []string, proxies map[string]C.Proxy, subRules map[s l := len(rule) - if ruleName == "NOT" || ruleName == "OR" || ruleName == "AND" || ruleName == "SUB-RULE" { + if ruleName == "NOT" || ruleName == "OR" || ruleName == "AND" || ruleName == "SUB-RULE" || ruleName == "DOMAIN-REGEX" { target = rule[l-1] payload = strings.Join(rule[1:l-1], ",") } else { diff --git a/rules/provider/classical_strategy.go b/rules/provider/classical_strategy.go index f8042164..6a2dccd5 100644 --- a/rules/provider/classical_strategy.go +++ b/rules/provider/classical_strategy.go @@ -2,9 +2,10 @@ package provider import ( "fmt" + "strings" + C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" - "strings" ) type classicalStrategy struct { @@ -76,7 +77,7 @@ func ruleParse(ruleRaw string) (string, string, []string) { } else if len(item) == 2 { return item[0], item[1], nil } else if len(item) > 2 { - if item[0] == "NOT" || item[0] == "OR" || item[0] == "AND" || item[0] == "SUB-RULE" { + if item[0] == "NOT" || item[0] == "OR" || item[0] == "AND" || item[0] == "SUB-RULE" || item[0] == "DOMAIN-REGEX" { return item[0], strings.Join(item[1:len(item)], ","), nil } else { return item[0], item[1], item[2:] From 4542fc09916828b0aaafff3e4178b56e35d67a53 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 29 Mar 2024 14:32:43 +0800 Subject: [PATCH 023/311] fix: tun lookback when don't have an activated network --- listener/sing_tun/server.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 96ec1573..384ff016 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -173,6 +173,7 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis closed: false, options: options, handler: handler, + tunName: tunName, } defer func() { if err != nil { @@ -279,7 +280,6 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis func (l *Listener) FlushDefaultInterface() { if l.options.AutoDetectInterface { - targetInterface := dialer.DefaultInterface.Load() for _, destination := range []netip.Addr{netip.IPv4Unspecified(), netip.IPv6Unspecified(), netip.MustParseAddr("1.1.1.1")} { autoDetectInterfaceName := l.defaultInterfaceMonitor.DefaultInterfaceName(destination) if autoDetectInterfaceName == l.tunName { @@ -287,17 +287,16 @@ func (l *Listener) FlushDefaultInterface() { } else if autoDetectInterfaceName == "" || autoDetectInterfaceName == "" { log.Warnln("[TUN] Auto detect interface by %s get empty name.", destination.String()) } else { - targetInterface = autoDetectInterfaceName - if old := dialer.DefaultInterface.Load(); old != targetInterface { - log.Warnln("[TUN] default interface changed by monitor, %s => %s", old, targetInterface) - - dialer.DefaultInterface.Store(targetInterface) - + if old := dialer.DefaultInterface.Swap(autoDetectInterfaceName); old != autoDetectInterfaceName { + log.Warnln("[TUN] default interface changed by monitor, %s => %s", old, autoDetectInterfaceName) iface.FlushCache() } return } } + if dialer.DefaultInterface.CompareAndSwap("", "") { + log.Warnln("[TUN] Auto detect interface failed, set '' to DefaultInterface to avoid lookback") + } } } From 72d094822423af389fb181d2b4570aeb9a336fec Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 29 Mar 2024 14:43:42 +0800 Subject: [PATCH 024/311] fix: atomic.TypedValue panic --- common/atomic/value.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/atomic/value.go b/common/atomic/value.go index 36623b3e..82d40076 100644 --- a/common/atomic/value.go +++ b/common/atomic/value.go @@ -43,7 +43,7 @@ func (t *TypedValue[T]) Swap(new T) T { if old == nil { return DefaultValue[T]() } - return old.(T) + return old.(tValue[T]).value } func (t *TypedValue[T]) CompareAndSwap(old, new T) bool { From 3e0bd65135dbd8e1287adc9f04ed1e9472939dd1 Mon Sep 17 00:00:00 2001 From: H1JK Date: Sat, 30 Mar 2024 18:19:40 +0800 Subject: [PATCH 025/311] feat: Converter support Xray HTTPUpgrade fast open path --- common/convert/converter.go | 27 +++++++++++++++++++++++++-- common/convert/v.go | 10 ++++++++-- docs/config.yaml | 4 ++++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/common/convert/converter.go b/common/convert/converter.go index 809aa94f..222dd9fa 100644 --- a/common/convert/converter.go +++ b/common/convert/converter.go @@ -330,7 +330,7 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { vmess["h2-opts"] = h2Opts - case "ws": + case "ws", "httpupgrade": headers := make(map[string]any) wsOpts := make(map[string]any) wsOpts["path"] = []string{"/"} @@ -338,7 +338,30 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { headers["Host"] = host.(string) } if path, ok := values["path"]; ok && path != "" { - wsOpts["path"] = path.(string) + path := path.(string) + pathURL, err := url.Parse(path) + if err == nil { + query := pathURL.Query() + if earlyData := query.Get("ed"); earlyData != "" { + med, err := strconv.Atoi(earlyData) + if err == nil { + switch network { + case "ws": + wsOpts["max-early-data"] = med + wsOpts["early-data-header-name"] = "Sec-WebSocket-Protocol" + case "httpupgrade": + wsOpts["v2ray-http-upgrade-fast-open"] = true + } + query.Del("ed") + pathURL.RawQuery = query.Encode() + path = pathURL.String() + } + } + if earlyDataHeader := query.Get("eh"); earlyDataHeader != "" { + wsOpts["early-data-header-name"] = earlyDataHeader + } + } + wsOpts["path"] = path } wsOpts["headers"] = headers vmess["ws-opts"] = wsOpts diff --git a/common/convert/v.go b/common/convert/v.go index 2d8cf732..4102ab75 100644 --- a/common/convert/v.go +++ b/common/convert/v.go @@ -100,7 +100,7 @@ func handleVShareLink(names map[string]int, url *url.URL, scheme string, proxy m h2Opts["headers"] = headers proxy["h2-opts"] = h2Opts - case "ws": + case "ws", "httpupgrade": headers := make(map[string]any) wsOpts := make(map[string]any) headers["User-Agent"] = RandUserAgent() @@ -113,7 +113,13 @@ func handleVShareLink(names map[string]int, url *url.URL, scheme string, proxy m if err != nil { return fmt.Errorf("bad WebSocket max early data size: %v", err) } - wsOpts["max-early-data"] = med + switch network { + case "ws": + wsOpts["max-early-data"] = med + wsOpts["early-data-header-name"] = "Sec-WebSocket-Protocol" + case "httpupgrade": + wsOpts["v2ray-http-upgrade-fast-open"] = true + } } if earlyDataHeader := query.Get("eh"); earlyDataHeader != "" { wsOpts["early-data-header-name"] = earlyDataHeader diff --git a/docs/config.yaml b/docs/config.yaml index 4d0e995e..6aa06862 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -383,6 +383,7 @@ proxies: # socks5 # headers: # custom: value # v2ray-http-upgrade: false + # v2ray-http-upgrade-fast-open: false - name: "ss4-shadow-tls" type: ss @@ -461,6 +462,7 @@ proxies: # socks5 # max-early-data: 2048 # early-data-header-name: Sec-WebSocket-Protocol # v2ray-http-upgrade: false + # v2ray-http-upgrade-fast-open: false - name: "vmess-h2" type: vmess @@ -589,6 +591,7 @@ proxies: # socks5 headers: Host: example.com # v2ray-http-upgrade: false + # v2ray-http-upgrade-fast-open: false # Trojan - name: "trojan" @@ -633,6 +636,7 @@ proxies: # socks5 # headers: # Host: example.com # v2ray-http-upgrade: false + # v2ray-http-upgrade-fast-open: false - name: "trojan-xtls" type: trojan From 56ed9019a60d794cd528a85aa2e6e9ff0b3e179e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 30 Mar 2024 23:04:03 +0800 Subject: [PATCH 026/311] ci: add armv5/6 back --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f9bbbba9..d95226b0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,6 +34,8 @@ jobs: - { goos: linux, goarch: amd64, goamd64: v1, output: amd64-compatible, test: test } - { goos: linux, goarch: amd64, goamd64: v3, output: amd64 } - { goos: linux, goarch: arm64, output: arm64 } + - { goos: linux, goarch: arm, goarm: '5', output: armv5 } + - { goos: linux, goarch: arm, goarm: '6', output: armv6 } - { goos: linux, goarch: arm, goarm: '7', output: armv7 } - { goos: linux, goarch: mips, mips: hardfloat, output: mips-hardfloat } - { goos: linux, goarch: mips, mips: softfloat, output: mips-softfloat } From f3743fc7f91410be8711fb6a1dcaa2d4fc20fea9 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Mon, 1 Apr 2024 16:21:34 +0800 Subject: [PATCH 027/311] chore: Introducing Punycode conversion for domain matching --- rules/common/domain.go | 4 +++- rules/common/domain_keyword.go | 4 +++- rules/common/domain_suffix.go | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/rules/common/domain.go b/rules/common/domain.go index 23f21185..306eb65f 100644 --- a/rules/common/domain.go +++ b/rules/common/domain.go @@ -4,6 +4,7 @@ import ( "strings" C "github.com/metacubex/mihomo/constant" + "golang.org/x/net/idna" ) type Domain struct { @@ -29,9 +30,10 @@ func (d *Domain) Payload() string { } func NewDomain(domain string, adapter string) *Domain { + punycode, _ := idna.ToASCII(strings.ToLower(domain)) return &Domain{ Base: &Base{}, - domain: strings.ToLower(domain), + domain: punycode, adapter: adapter, } } diff --git a/rules/common/domain_keyword.go b/rules/common/domain_keyword.go index ec01293a..9d6f1c15 100644 --- a/rules/common/domain_keyword.go +++ b/rules/common/domain_keyword.go @@ -4,6 +4,7 @@ import ( "strings" C "github.com/metacubex/mihomo/constant" + "golang.org/x/net/idna" ) type DomainKeyword struct { @@ -30,9 +31,10 @@ func (dk *DomainKeyword) Payload() string { } func NewDomainKeyword(keyword string, adapter string) *DomainKeyword { + punycode, _ := idna.ToASCII(strings.ToLower(keyword)) return &DomainKeyword{ Base: &Base{}, - keyword: strings.ToLower(keyword), + keyword: punycode, adapter: adapter, } } diff --git a/rules/common/domain_suffix.go b/rules/common/domain_suffix.go index b7b1794d..c5b87208 100644 --- a/rules/common/domain_suffix.go +++ b/rules/common/domain_suffix.go @@ -4,6 +4,7 @@ import ( "strings" C "github.com/metacubex/mihomo/constant" + "golang.org/x/net/idna" ) type DomainSuffix struct { @@ -30,9 +31,10 @@ func (ds *DomainSuffix) Payload() string { } func NewDomainSuffix(suffix string, adapter string) *DomainSuffix { + punycode, _ := idna.ToASCII(strings.ToLower(suffix)) return &DomainSuffix{ Base: &Base{}, - suffix: strings.ToLower(suffix), + suffix: punycode, adapter: adapter, } } From 3b472f786ece5511a9e56872132396474e1df5f5 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Mon, 1 Apr 2024 18:16:34 +0800 Subject: [PATCH 028/311] chore: Add source matching for ip type rules --- constant/rule.go | 12 +++++++++--- rules/common/geoip.go | 23 +++++++++++++++++++++-- rules/common/ipasn.go | 15 ++++++++++++--- rules/parser.go | 12 ++++++++---- 4 files changed, 50 insertions(+), 12 deletions(-) diff --git a/constant/rule.go b/constant/rule.go index fcefaba6..8fdd75a6 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -8,8 +8,10 @@ const ( DomainRegex GEOSITE GEOIP - IPCIDR + SrcGEOIP IPASN + SrcIPASN + IPCIDR SrcIPCIDR IPSuffix SrcIPSuffix @@ -48,10 +50,14 @@ func (rt RuleType) String() string { return "GeoSite" case GEOIP: return "GeoIP" - case IPCIDR: - return "IPCIDR" + case SrcGEOIP: + return "SrcGeoIP" case IPASN: return "IPASN" + case SrcIPASN: + return "SrcIPASN" + case IPCIDR: + return "IPCIDR" case SrcIPCIDR: return "SrcIPCIDR" case IPSuffix: diff --git a/rules/common/geoip.go b/rules/common/geoip.go index 223a7904..b50680a4 100644 --- a/rules/common/geoip.go +++ b/rules/common/geoip.go @@ -17,6 +17,7 @@ type GEOIP struct { country string adapter string noResolveIP bool + isSourceIP bool geoIPMatcher *router.GeoIPMatcher recodeSize int } @@ -24,11 +25,17 @@ type GEOIP struct { var _ C.Rule = (*GEOIP)(nil) func (g *GEOIP) RuleType() C.RuleType { + if g.isSourceIP { + return C.SrcGEOIP + } return C.GEOIP } func (g *GEOIP) Match(metadata *C.Metadata) (bool, string) { ip := metadata.DstIP + if g.isSourceIP { + ip = metadata.SrcIP + } if !ip.IsValid() { return false, "" } @@ -49,6 +56,16 @@ func (g *GEOIP) Match(metadata *C.Metadata) (bool, string) { } if !C.GeodataMode { + if g.isSourceIP { + codes := mmdb.IPInstance().LookupCode(ip.AsSlice()) + for _, code := range codes { + if g.country == code { + return true, g.adapter + } + } + return false, g.adapter + } + if metadata.DstGeoIP != nil { return false, g.adapter } @@ -62,7 +79,7 @@ func (g *GEOIP) Match(metadata *C.Metadata) (bool, string) { } match := g.geoIPMatcher.Match(ip) - if match { + if match && !g.isSourceIP { metadata.DstGeoIP = append(metadata.DstGeoIP, g.country) } return match, g.adapter @@ -92,7 +109,7 @@ func (g *GEOIP) GetRecodeSize() int { return g.recodeSize } -func NewGEOIP(country string, adapter string, noResolveIP bool) (*GEOIP, error) { +func NewGEOIP(country string, adapter string, isSrc, noResolveIP bool) (*GEOIP, error) { if err := geodata.InitGeoIP(); err != nil { log.Errorln("can't initial GeoIP: %s", err) return nil, err @@ -105,6 +122,7 @@ func NewGEOIP(country string, adapter string, noResolveIP bool) (*GEOIP, error) country: country, adapter: adapter, noResolveIP: noResolveIP, + isSourceIP: isSrc, } return geoip, nil } @@ -120,6 +138,7 @@ func NewGEOIP(country string, adapter string, noResolveIP bool) (*GEOIP, error) country: country, adapter: adapter, noResolveIP: noResolveIP, + isSourceIP: isSrc, geoIPMatcher: geoIPMatcher, recodeSize: size, } diff --git a/rules/common/ipasn.go b/rules/common/ipasn.go index 1fce8af4..df4b6531 100644 --- a/rules/common/ipasn.go +++ b/rules/common/ipasn.go @@ -14,24 +14,32 @@ type ASN struct { asn string adapter string noResolveIP bool + isSourceIP bool } func (a *ASN) Match(metadata *C.Metadata) (bool, string) { ip := metadata.DstIP + if a.isSourceIP { + ip = metadata.SrcIP + } if !ip.IsValid() { return false, "" } result := mmdb.ASNInstance().LookupASN(ip.AsSlice()) - asnNumber := strconv.FormatUint(uint64(result.AutonomousSystemNumber), 10) - metadata.DstIPASN = asnNumber + " " + result.AutonomousSystemOrganization + if !a.isSourceIP { + metadata.DstIPASN = asnNumber + " " + result.AutonomousSystemOrganization + } match := a.asn == asnNumber return match, a.adapter } func (a *ASN) RuleType() C.RuleType { + if a.isSourceIP { + return C.SrcIPASN + } return C.IPASN } @@ -51,7 +59,7 @@ func (a *ASN) GetASN() string { return a.asn } -func NewIPASN(asn string, adapter string, noResolveIP bool) (*ASN, error) { +func NewIPASN(asn string, adapter string, isSrc, noResolveIP bool) (*ASN, error) { C.ASNEnable = true if err := geodata.InitASN(); err != nil { log.Errorln("can't initial ASN: %s", err) @@ -63,5 +71,6 @@ func NewIPASN(asn string, adapter string, noResolveIP bool) (*ASN, error) { asn: asn, adapter: adapter, noResolveIP: noResolveIP, + isSourceIP: isSrc, }, nil } diff --git a/rules/parser.go b/rules/parser.go index b69cc4fd..032a02e4 100644 --- a/rules/parser.go +++ b/rules/parser.go @@ -23,13 +23,17 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string] parsed, parseErr = RC.NewGEOSITE(payload, target) case "GEOIP": noResolve := RC.HasNoResolve(params) - parsed, parseErr = RC.NewGEOIP(payload, target, noResolve) + parsed, parseErr = RC.NewGEOIP(payload, target, false, noResolve) + case "SRC-GEOIP": + parsed, parseErr = RC.NewGEOIP(payload, target, true, true) + case "IP-ASN": + noResolve := RC.HasNoResolve(params) + parsed, parseErr = RC.NewIPASN(payload, target, false, noResolve) + case "SRC-IP-ASN": + parsed, parseErr = RC.NewIPASN(payload, target, true, true) case "IP-CIDR", "IP-CIDR6": noResolve := RC.HasNoResolve(params) parsed, parseErr = RC.NewIPCIDR(payload, target, RC.WithIPCIDRNoResolve(noResolve)) - case "IP-ASN": - noResolve := RC.HasNoResolve(params) - parsed, parseErr = RC.NewIPASN(payload, target, noResolve) case "SRC-IP-CIDR": parsed, parseErr = RC.NewIPCIDR(payload, target, RC.WithIPCIDRSourceIP(true), RC.WithIPCIDRNoResolve(true)) case "IP-SUFFIX": From d48517b29d0ae20b7a6ccf0e82c5ccf08cb51ef7 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 2 Apr 2024 23:00:48 +0800 Subject: [PATCH 029/311] fix: timer usage for monitor check update --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c70aca97..78f50d02 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 - github.com/metacubex/sing-tun v0.2.1-0.20240320004934-5d2b35447bfd + github.com/metacubex/sing-tun v0.2.1-0.20240402145739-0223b8bb1c85 github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 diff --git a/go.sum b/go.sum index 7422a062..12c207e4 100644 --- a/go.sum +++ b/go.sum @@ -114,8 +114,8 @@ github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwV github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A= github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8= -github.com/metacubex/sing-tun v0.2.1-0.20240320004934-5d2b35447bfd h1:NgLb6Lvr8ZxX0inWswVYjal2SUzsJJ54dFQNOluUJuE= -github.com/metacubex/sing-tun v0.2.1-0.20240320004934-5d2b35447bfd/go.mod h1:GfLZG/QgGpW9+BPjltzONrL5vVms86TWqmZ23J68ISc= +github.com/metacubex/sing-tun v0.2.1-0.20240402145739-0223b8bb1c85 h1:r7XXIvooixabmv2Ry95I1Xv3T0c+9VWtes9LhkXGg34= +github.com/metacubex/sing-tun v0.2.1-0.20240402145739-0223b8bb1c85/go.mod h1:GfLZG/QgGpW9+BPjltzONrL5vVms86TWqmZ23J68ISc= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbTMBip/C+R79bvbfr42xH1gZl3uFb0RELdZiQ= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 h1:AGyIB55UfQm/0ZH0HtQO9u3l//yjtHUpjeRjjPGfGRI= From 40f5c5b98756122b1329cee7d25df23571170db0 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 3 Apr 2024 08:42:15 +0800 Subject: [PATCH 030/311] chore: don't retry when rejected loopback connection --- adapter/outbound/direct.go | 18 ++++----- .../loopback/detector.go | 38 ++++++++++++------- tunnel/tunnel.go | 4 ++ 3 files changed, 37 insertions(+), 23 deletions(-) rename adapter/outbound/direct_loopback_detect.go => component/loopback/detector.go (58%) diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index 7def7b20..1b01a576 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -3,18 +3,18 @@ package outbound import ( "context" "errors" - "fmt" "net/netip" N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/dialer" + "github.com/metacubex/mihomo/component/loopback" "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" ) type Direct struct { *Base - loopBack *loopBackDetector + loopBack *loopback.Detector } type DirectOption struct { @@ -24,8 +24,8 @@ type DirectOption struct { // DialContext implements C.ProxyAdapter func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { - if d.loopBack.CheckConn(metadata.SourceAddrPort()) { - return nil, fmt.Errorf("reject loopback connection to: %s", metadata.RemoteAddress()) + if err := d.loopBack.CheckConn(metadata); err != nil { + return nil, err } opts = append(opts, dialer.WithResolver(resolver.DefaultResolver)) c, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress(), d.Base.DialOptions(opts...)...) @@ -38,8 +38,8 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ... // ListenPacketContext implements C.ProxyAdapter func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { - if d.loopBack.CheckPacketConn(metadata.SourceAddrPort()) { - return nil, fmt.Errorf("reject loopback connection to: %s", metadata.RemoteAddress()) + if err := d.loopBack.CheckPacketConn(metadata); err != nil { + return nil, err } // net.UDPConn.WriteTo only working with *net.UDPAddr, so we need a net.UDPAddr if !metadata.Resolved() { @@ -68,7 +68,7 @@ func NewDirectWithOption(option DirectOption) *Direct { rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), }, - loopBack: newLoopBackDetector(), + loopBack: loopback.NewDetector(), } } @@ -80,7 +80,7 @@ func NewDirect() *Direct { udp: true, prefer: C.DualStack, }, - loopBack: newLoopBackDetector(), + loopBack: loopback.NewDetector(), } } @@ -92,6 +92,6 @@ func NewCompatible() *Direct { udp: true, prefer: C.DualStack, }, - loopBack: newLoopBackDetector(), + loopBack: loopback.NewDetector(), } } diff --git a/adapter/outbound/direct_loopback_detect.go b/component/loopback/detector.go similarity index 58% rename from adapter/outbound/direct_loopback_detect.go rename to component/loopback/detector.go index 410d5a2f..b07270ed 100644 --- a/adapter/outbound/direct_loopback_detect.go +++ b/component/loopback/detector.go @@ -1,6 +1,8 @@ -package outbound +package loopback import ( + "errors" + "fmt" "net/netip" "github.com/metacubex/mihomo/common/callback" @@ -9,19 +11,21 @@ import ( "github.com/puzpuzpuz/xsync/v3" ) -type loopBackDetector struct { +var ErrReject = errors.New("reject loopback connection") + +type Detector struct { connMap *xsync.MapOf[netip.AddrPort, struct{}] packetConnMap *xsync.MapOf[netip.AddrPort, struct{}] } -func newLoopBackDetector() *loopBackDetector { - return &loopBackDetector{ +func NewDetector() *Detector { + return &Detector{ connMap: xsync.NewMapOf[netip.AddrPort, struct{}](), packetConnMap: xsync.NewMapOf[netip.AddrPort, struct{}](), } } -func (l *loopBackDetector) NewConn(conn C.Conn) C.Conn { +func (l *Detector) NewConn(conn C.Conn) C.Conn { metadata := C.Metadata{} if metadata.SetRemoteAddr(conn.LocalAddr()) != nil { return conn @@ -36,7 +40,7 @@ func (l *loopBackDetector) NewConn(conn C.Conn) C.Conn { }) } -func (l *loopBackDetector) NewPacketConn(conn C.PacketConn) C.PacketConn { +func (l *Detector) NewPacketConn(conn C.PacketConn) C.PacketConn { metadata := C.Metadata{} if metadata.SetRemoteAddr(conn.LocalAddr()) != nil { return conn @@ -51,18 +55,24 @@ func (l *loopBackDetector) NewPacketConn(conn C.PacketConn) C.PacketConn { }) } -func (l *loopBackDetector) CheckConn(connAddr netip.AddrPort) bool { +func (l *Detector) CheckConn(metadata *C.Metadata) error { + connAddr := metadata.SourceAddrPort() if !connAddr.IsValid() { - return false + return nil } - _, ok := l.connMap.Load(connAddr) - return ok + if _, ok := l.connMap.Load(connAddr); ok { + return fmt.Errorf("%w to: %s", ErrReject, metadata.RemoteAddress()) + } + return nil } -func (l *loopBackDetector) CheckPacketConn(connAddr netip.AddrPort) bool { +func (l *Detector) CheckPacketConn(metadata *C.Metadata) error { + connAddr := metadata.SourceAddrPort() if !connAddr.IsValid() { - return false + return nil } - _, ok := l.packetConnMap.Load(connAddr) - return ok + if _, ok := l.packetConnMap.Load(connAddr); ok { + return fmt.Errorf("%w to: %s", ErrReject, metadata.RemoteAddress()) + } + return nil } diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index d5a226e9..608ab2c5 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -12,6 +12,7 @@ import ( "time" N "github.com/metacubex/mihomo/common/net" + "github.com/metacubex/mihomo/component/loopback" "github.com/metacubex/mihomo/component/nat" P "github.com/metacubex/mihomo/component/process" "github.com/metacubex/mihomo/component/resolver" @@ -694,6 +695,9 @@ func shouldStopRetry(err error) bool { if errors.Is(err, resolver.ErrIPv6Disabled) { return true } + if errors.Is(err, loopback.ErrReject) { + return true + } return false } From b56e73a02aee3a791ec3b45cb6a0f4b80f95d717 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 3 Apr 2024 23:41:04 +0800 Subject: [PATCH 031/311] fix: close mixed stack panic #1014 --- listener/sing_tun/server.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 384ff016..8fe534df 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -261,15 +261,17 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis } } l.tunIf = tunIf - l.tunStack, err = tun.NewStack(strings.ToLower(options.Stack.String()), stackOptions) + + tunStack, err := tun.NewStack(strings.ToLower(options.Stack.String()), stackOptions) if err != nil { return } - err = l.tunStack.Start() + err = tunStack.Start() if err != nil { return } + l.tunStack = tunStack //l.openAndroidHotspot(tunOptions) From c893e3c462b20bb2a4c4f9ae1cd3a3bad352cccc Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 4 Apr 2024 21:25:27 +0800 Subject: [PATCH 032/311] fix: IN-PORT not work in http inbound https://github.com/MetaCubeX/mihomo/issues/1162 --- adapter/inbound/addition.go | 4 ++-- adapter/inbound/http.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/adapter/inbound/addition.go b/adapter/inbound/addition.go index c38c1aa1..ed560818 100644 --- a/adapter/inbound/addition.go +++ b/adapter/inbound/addition.go @@ -47,7 +47,7 @@ func WithDstAddr(addr net.Addr) Addition { func WithSrcAddr(addr net.Addr) Addition { return func(metadata *C.Metadata) { m := C.Metadata{} - if err := m.SetRemoteAddr(addr);err ==nil{ + if err := m.SetRemoteAddr(addr); err == nil { metadata.SrcIP = m.DstIP metadata.SrcPort = m.DstPort } @@ -57,7 +57,7 @@ func WithSrcAddr(addr net.Addr) Addition { func WithInAddr(addr net.Addr) Addition { return func(metadata *C.Metadata) { m := C.Metadata{} - if err := m.SetRemoteAddr(addr);err ==nil{ + if err := m.SetRemoteAddr(addr); err == nil { metadata.InIP = m.DstIP metadata.InPort = m.DstPort } diff --git a/adapter/inbound/http.go b/adapter/inbound/http.go index 8f912fbe..f7d45399 100644 --- a/adapter/inbound/http.go +++ b/adapter/inbound/http.go @@ -14,7 +14,7 @@ func NewHTTP(target socks5.Addr, srcConn net.Conn, conn net.Conn, additions ...A metadata.Type = C.HTTP metadata.RawSrcAddr = srcConn.RemoteAddr() metadata.RawDstAddr = srcConn.LocalAddr() - ApplyAdditions(metadata, WithSrcAddr(srcConn.RemoteAddr()), WithInAddr(conn.LocalAddr())) + ApplyAdditions(metadata, WithSrcAddr(srcConn.RemoteAddr()), WithInAddr(srcConn.LocalAddr())) ApplyAdditions(metadata, additions...) return conn, metadata } From d1539e6c0727dd3972300b8c2592311bc355d1d5 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 4 Apr 2024 23:54:25 +0800 Subject: [PATCH 033/311] fix: IN-PORT not work in tproxy tcp inbound https://github.com/MetaCubeX/mihomo/issues/1162 --- listener/tproxy/tproxy.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/listener/tproxy/tproxy.go b/listener/tproxy/tproxy.go index efb144a9..fa7e7dbe 100644 --- a/listener/tproxy/tproxy.go +++ b/listener/tproxy/tproxy.go @@ -34,6 +34,8 @@ func (l *Listener) Close() error { func (l *Listener) handleTProxy(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) { target := socks5.ParseAddrToSocksAddr(conn.LocalAddr()) N.TCPKeepAlive(conn) + // TProxy's conn.LocalAddr() is target address, so we set from l.listener + additions = append([]inbound.Addition{inbound.WithInAddr(l.listener.Addr())}, additions...) tunnel.HandleTCPConn(inbound.NewSocket(target, conn, C.TPROXY, additions...)) } From 90bf158e9f0b5a57cf76124b06584e924201d589 Mon Sep 17 00:00:00 2001 From: riolurs Date: Fri, 5 Apr 2024 01:51:18 +0800 Subject: [PATCH 034/311] fix(group.parser): set default TestURL if empty --- adapter/outboundgroup/parser.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/adapter/outboundgroup/parser.go b/adapter/outboundgroup/parser.go index 74947587..bec262f2 100644 --- a/adapter/outboundgroup/parser.go +++ b/adapter/outboundgroup/parser.go @@ -98,14 +98,12 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide return nil, fmt.Errorf("%s: %w", groupName, errDuplicateProvider) } - // select don't need health check - if groupOption.Type != "select" && groupOption.Type != "relay" { - if groupOption.Interval == 0 { - groupOption.Interval = 300 - } - if groupOption.URL == "" { - groupOption.URL = C.DefaultTestURL - } + if groupOption.Interval == 0 { + groupOption.Interval = 300 + } + + if groupOption.URL == "" { + groupOption.URL = C.DefaultTestURL } hc := provider.NewHealthCheck(ps, groupOption.URL, uint(groupOption.TestTimeout), uint(groupOption.Interval), groupOption.Lazy, expectedStatus) From ba09139bd70dc6a424c61df39a5caf38dad386eb Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 5 Apr 2024 10:17:48 +0800 Subject: [PATCH 035/311] fix: avoid netlink dos networkUpdateMonitor --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 78f50d02..7270049a 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 - github.com/metacubex/sing-tun v0.2.1-0.20240402145739-0223b8bb1c85 + github.com/metacubex/sing-tun v0.2.1-0.20240405021556-f37a4aa3d060 github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 diff --git a/go.sum b/go.sum index 12c207e4..4bddc40e 100644 --- a/go.sum +++ b/go.sum @@ -114,8 +114,8 @@ github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwV github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A= github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8= -github.com/metacubex/sing-tun v0.2.1-0.20240402145739-0223b8bb1c85 h1:r7XXIvooixabmv2Ry95I1Xv3T0c+9VWtes9LhkXGg34= -github.com/metacubex/sing-tun v0.2.1-0.20240402145739-0223b8bb1c85/go.mod h1:GfLZG/QgGpW9+BPjltzONrL5vVms86TWqmZ23J68ISc= +github.com/metacubex/sing-tun v0.2.1-0.20240405021556-f37a4aa3d060 h1:SEkMqQlInU4KoyaISvEPKEzhDw0CnTr2TvIuj/hmEQ0= +github.com/metacubex/sing-tun v0.2.1-0.20240405021556-f37a4aa3d060/go.mod h1:GfLZG/QgGpW9+BPjltzONrL5vVms86TWqmZ23J68ISc= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbTMBip/C+R79bvbfr42xH1gZl3uFb0RELdZiQ= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 h1:AGyIB55UfQm/0ZH0HtQO9u3l//yjtHUpjeRjjPGfGRI= From 3249572dc154fd28f5e3612c6df9752648c6f44d Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 5 Apr 2024 10:23:15 +0800 Subject: [PATCH 036/311] action: typo --- .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 d95226b0..2639c04b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -143,7 +143,7 @@ jobs: run: | go test ./... - - name: Update UA + - name: Update CA run: | sudo apt-get install ca-certificates sudo update-ca-certificates From 19f7220c0b70eec1afe404935db71344e0b9b17f Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Fri, 5 Apr 2024 19:45:36 +0800 Subject: [PATCH 037/311] chore: adjust testURL priority --- adapter/outboundgroup/parser.go | 60 +++++++++++++++++---------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/adapter/outboundgroup/parser.go b/adapter/outboundgroup/parser.go index bec262f2..876c92fa 100644 --- a/adapter/outboundgroup/parser.go +++ b/adapter/outboundgroup/parser.go @@ -88,6 +88,29 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide } groupOption.ExpectedStatus = status + if len(groupOption.Use) != 0 { + PDs, err := getProviders(providersMap, groupOption.Use) + if err != nil { + return nil, fmt.Errorf("%s: %w", groupName, err) + } + + // if test URL is empty, use the first health check URL of providers + if groupOption.URL == "" { + for _, pd := range PDs { + if pd.HealthCheckURL() != "" { + groupOption.URL = pd.HealthCheckURL() + break + } + } + if groupOption.URL == "" { + groupOption.URL = C.DefaultTestURL + } + } else { + addTestUrlToProviders(PDs, groupOption.URL, expectedStatus, groupOption.Filter, uint(groupOption.Interval)) + } + providers = append(providers, PDs...) + } + if len(groupOption.Proxies) != 0 { ps, err := getProxies(proxyMap, groupOption.Proxies) if err != nil { @@ -98,14 +121,17 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide return nil, fmt.Errorf("%s: %w", groupName, errDuplicateProvider) } - if groupOption.Interval == 0 { - groupOption.Interval = 300 - } - if groupOption.URL == "" { groupOption.URL = C.DefaultTestURL } + // select don't need auto health check + if groupOption.Type != "select" && groupOption.Type != "relay" { + if groupOption.Interval == 0 { + groupOption.Interval = 300 + } + } + hc := provider.NewHealthCheck(ps, groupOption.URL, uint(groupOption.TestTimeout), uint(groupOption.Interval), groupOption.Lazy, expectedStatus) pd, err := provider.NewCompatibleProvider(groupName, ps, hc) @@ -113,34 +139,10 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide return nil, fmt.Errorf("%s: %w", groupName, err) } - providers = append(providers, pd) + providers = append([]types.ProxyProvider{pd}, providers...) providersMap[groupName] = pd } - if len(groupOption.Use) != 0 { - list, err := getProviders(providersMap, groupOption.Use) - if err != nil { - return nil, fmt.Errorf("%s: %w", groupName, err) - } - - if groupOption.URL == "" { - for _, p := range list { - if p.HealthCheckURL() != "" { - groupOption.URL = p.HealthCheckURL() - } - break - } - - if groupOption.URL == "" { - groupOption.URL = C.DefaultTestURL - } - } - - // different proxy groups use different test URL - addTestUrlToProviders(list, groupOption.URL, expectedStatus, groupOption.Filter, uint(groupOption.Interval)) - providers = append(providers, list...) - } - var group C.ProxyAdapter switch groupOption.Type { case "url-test": From f3e23b112896504b32c948c5453199f939b1718a Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sun, 7 Apr 2024 05:28:22 +0800 Subject: [PATCH 038/311] feat: Allow providers to set individual proxy and headers --- adapter/provider/parser.go | 14 ++-- component/http/http.go | 10 +-- component/resource/vehicle.go | 12 ++-- config/config.go | 2 +- docs/config.yaml | 124 ++++++++++++++++++---------------- listener/inner/tcp.go | 5 +- rules/provider/parse.go | 10 ++- 7 files changed, 96 insertions(+), 81 deletions(-) diff --git a/adapter/provider/parser.go b/adapter/provider/parser.go index 2e436669..1094668d 100644 --- a/adapter/provider/parser.go +++ b/adapter/provider/parser.go @@ -44,14 +44,16 @@ type proxyProviderSchema struct { Type string `provider:"type"` Path string `provider:"path,omitempty"` URL string `provider:"url,omitempty"` + Proxy string `provider:"proxy,omitempty"` Interval int `provider:"interval,omitempty"` Filter string `provider:"filter,omitempty"` ExcludeFilter string `provider:"exclude-filter,omitempty"` ExcludeType string `provider:"exclude-type,omitempty"` DialerProxy string `provider:"dialer-proxy,omitempty"` - HealthCheck healthCheckSchema `provider:"health-check,omitempty"` - Override OverrideSchema `provider:"override,omitempty"` + HealthCheck healthCheckSchema `provider:"health-check,omitempty"` + Override OverrideSchema `provider:"override,omitempty"` + Header map[string][]string `provider:"header,omitempty"` } func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvider, error) { @@ -86,16 +88,14 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide path := C.Path.Resolve(schema.Path) vehicle = resource.NewFileVehicle(path) case "http": + path := C.Path.GetPathByHash("proxies", schema.URL) if schema.Path != "" { - path := C.Path.Resolve(schema.Path) + path = C.Path.Resolve(schema.Path) if !features.CMFA && !C.Path.IsSafePath(path) { return nil, fmt.Errorf("%w: %s", errSubPath, path) } - vehicle = resource.NewHTTPVehicle(schema.URL, path) - } else { - path := C.Path.GetPathByHash("proxies", schema.URL) - vehicle = resource.NewHTTPVehicle(schema.URL, path) } + vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, schema.Header) default: return nil, fmt.Errorf("%w: %s", errVehicleType, schema.Type) } diff --git a/component/http/http.go b/component/http/http.go index 455db681..21d65d2e 100644 --- a/component/http/http.go +++ b/component/http/http.go @@ -17,7 +17,10 @@ import ( ) func HttpRequest(ctx context.Context, url, method string, header map[string][]string, body io.Reader) (*http.Response, error) { - UA := C.UA + return HttpRequestWithProxy(ctx, url, method, header, body, "") +} + +func HttpRequestWithProxy(ctx context.Context, url, method string, header map[string][]string, body io.Reader, specialProxy string) (*http.Response, error) { method = strings.ToUpper(method) urlRes, err := URL.Parse(url) if err != nil { @@ -32,7 +35,7 @@ func HttpRequest(ctx context.Context, url, method string, header map[string][]st } if _, ok := header["User-Agent"]; !ok { - req.Header.Set("User-Agent", UA) + req.Header.Set("User-Agent", C.UA) } if err != nil { @@ -54,7 +57,7 @@ func HttpRequest(ctx context.Context, url, method string, header map[string][]st TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, DialContext: func(ctx context.Context, network, address string) (net.Conn, error) { - if conn, err := inner.HandleTcp(address); err == nil { + if conn, err := inner.HandleTcp(address, specialProxy); err == nil { return conn, nil } else { d := net.Dialer{} @@ -66,5 +69,4 @@ func HttpRequest(ctx context.Context, url, method string, header map[string][]st client := http.Client{Transport: transport} return client.Do(req) - } diff --git a/component/resource/vehicle.go b/component/resource/vehicle.go index b2e29418..2d71be94 100644 --- a/component/resource/vehicle.go +++ b/component/resource/vehicle.go @@ -33,8 +33,10 @@ func NewFileVehicle(path string) *FileVehicle { } type HTTPVehicle struct { - url string - path string + url string + path string + proxy string + header http.Header } func (h *HTTPVehicle) Url() string { @@ -52,7 +54,7 @@ func (h *HTTPVehicle) Path() string { func (h *HTTPVehicle) Read() ([]byte, error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, h.url, http.MethodGet, nil, nil) + resp, err := mihomoHttp.HttpRequestWithProxy(ctx, h.url, http.MethodGet, h.header, nil, h.proxy) if err != nil { return nil, err } @@ -67,6 +69,6 @@ func (h *HTTPVehicle) Read() ([]byte, error) { return buf, nil } -func NewHTTPVehicle(url string, path string) *HTTPVehicle { - return &HTTPVehicle{url, path} +func NewHTTPVehicle(url string, path string, proxy string, header http.Header) *HTTPVehicle { + return &HTTPVehicle{url, path, proxy, header} } diff --git a/config/config.go b/config/config.go index c7931573..c5c4fa88 100644 --- a/config/config.go +++ b/config/config.go @@ -413,7 +413,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { ProxyGroup: []map[string]any{}, TCPConcurrent: false, FindProcessMode: P.FindProcessStrict, - GlobalUA: "clash.meta", + GlobalUA: "clash.meta/" + C.Version, Tun: RawTun{ Enable: false, Device: "", diff --git a/docs/config.yaml b/docs/config.yaml index 6aa06862..869b5231 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -8,15 +8,15 @@ mixed-port: 10801 # HTTP(S) 和 SOCKS 代理混合端口 allow-lan: true # 允许局域网连接 bind-address: "*" # 绑定 IP 地址,仅作用于 allow-lan 为 true,'*'表示所有地址 -authentication: # http,socks入口的验证用户名,密码 +authentication: # http,socks 入口的验证用户名,密码 - "username:password" -skip-auth-prefixes: # 设置跳过验证的IP段 +skip-auth-prefixes: # 设置跳过验证的 IP 段 - 127.0.0.1/8 - ::1/128 -lan-allowed-ips: # 允许连接的 IP 地址段,仅作用于 allow-lan 为 true, 默认值为0.0.0.0/0和::/0 +lan-allowed-ips: # 允许连接的 IP 地址段,仅作用于 allow-lan 为 true, 默认值为 0.0.0.0/0 和::/0 - 0.0.0.0/0 - ::/0 -lan-disallowed-ips: # 禁止连接的 IP 地址段, 黑名单优先级高于白名单, 默认值为空 +lan-disallowed-ips: # 禁止连接的 IP 地址段,黑名单优先级高于白名单,默认值为空 - 192.168.0.3/32 # find-process-mode has 3 values:always, strict, off @@ -109,9 +109,9 @@ tun: # auto-detect-interface: true # 自动识别出口网卡 # auto-route: true # 配置路由表 # mtu: 9000 # 最大传输单元 - # gso: false # 启用通用分段卸载, 仅支持 Linux + # gso: false # 启用通用分段卸载,仅支持 Linux # gso-max-size: 65536 # 通用分段卸载包的最大大小 - # strict-route: true # 将所有连接路由到tun来防止泄漏,但你的设备将无法其他设备被访问 + # strict-route: true # 将所有连接路由到 tun 来防止泄漏,但你的设备将无法其他设备被访问 inet4-route-address: # 启用 auto-route 时使用自定义路由而不是默认路由 - 0.0.0.0/1 - 128.0.0.0/1 @@ -119,9 +119,9 @@ tun: - "::/1" - "8000::/1" # endpoint-independent-nat: false # 启用独立于端点的 NAT - # include-interface: # 限制被路由的接口。默认不限制, 与 `exclude-interface` 冲突 + # include-interface: # 限制被路由的接口。默认不限制,与 `exclude-interface` 冲突 # - "lan0" - # exclude-interface: # 排除路由的接口, 与 `include-interface` 冲突 + # exclude-interface: # 排除路由的接口,与 `include-interface` 冲突 # - "lan1" # include-uid: # UID 规则仅在 Linux 下被支持,并且需要 auto-route # - 0 @@ -143,7 +143,7 @@ tun: # exclude-package: # 排除被路由的 Android 应用包名 # - com.android.captiveportallogin -#ebpf配置 +#ebpf 配置 ebpf: auto-redir: # redirect 模式,仅支持 TCP - eth0 @@ -200,7 +200,7 @@ tunnels: # one line config target: target.com proxy: proxy -# DNS配置 +# DNS 配置 dns: cache-algorithm: arc enable: false # 关闭将使用系统 DNS @@ -208,7 +208,7 @@ dns: listen: 0.0.0.0:53 # 开启 DNS 服务器监听 # ipv6: false # false 将返回 AAAA 的空结果 # ipv6-timeout: 300 # 单位:ms,内部双栈并发时,向上游查询 AAAA 时,等待 AAAA 的时间,默认 100ms - # 用于解析 nameserver,fallback 以及其他DNS服务器配置的,DNS 服务域名 + # 用于解析 nameserver,fallback 以及其他 DNS 服务器配置的,DNS 服务域名 # 只能使用纯 IP 地址,可使用加密 DNS default-nameserver: - 114.114.114.114 @@ -222,12 +222,12 @@ dns: # use-hosts: true # 查询 hosts - # 配置不使用fake-ip的域名 + # 配置不使用 fake-ip 的域名 # fake-ip-filter: # - '*.lan' # - localhost.ptlogin2.qq.com - # DNS主要域名配置 + # DNS 主要域名配置 # 支持 UDP,TCP,DoT,DoH,DoQ # 这部分为主要 DNS 配置,影响所有直连,确保使用对大陆解析精准的 DNS nameserver: @@ -239,7 +239,7 @@ dns: - https://mozilla.cloudflare-dns.com/dns-query#DNS&h3=true # 指定策略组和使用 HTTP/3 - dhcp://en0 # dns from dhcp - quic://dns.adguard.com:784 # DNS over QUIC - # - '8.8.8.8#en0' # 兼容指定DNS出口网卡 + # - '8.8.8.8#en0' # 兼容指定 DNS 出口网卡 # 当配置 fallback 时,会查询 nameserver 中返回的 IP 是否为 CN,非必要配置 # 当不是 CN,则使用 fallback 中的 DNS 查询结果 @@ -338,7 +338,7 @@ proxies: # socks5 # udp-over-tcp: false # ip-version: ipv4 # 设置节点使用 IP 版本,可选:dual,ipv4,ipv6,ipv4-prefer,ipv6-prefer。默认使用 dual # ipv4:仅使用 IPv4 ipv6:仅使用 IPv6 - # ipv4-prefer:优先使用 IPv4 对于 TCP 会进行双栈解析,并发链接但是优先使用 IPv4 链接, + # ipv4-prefer:优先使用 IPv4 对于 TCP 会进行双栈解析,并发链接但是优先使用 IPv4 链接, # UDP 则为双栈解析,获取结果中的第一个 IPv4 # ipv6-prefer 同 ipv4-prefer # 现有协议都支持此参数,TCP 效果仅在开启 tcp-concurrent 生效 @@ -350,7 +350,7 @@ proxies: # socks5 # max-streams: 0 # Maximum multiplexed streams in a connection before opening a new connection. Conflict with max-connections and min-streams. # padding: false # Enable padding. Requires sing-box server version 1.3-beta9 or later. # statistic: false # 控制是否将底层连接显示在面板中,方便打断底层连接 - # only-tcp: false # 如果设置为true, smux的设置将不会对udp生效,udp连接会直接走底层协议 + # only-tcp: false # 如果设置为 true, smux 的设置将不会对 udp 生效,udp 连接会直接走底层协议 - name: "ss2" type: ss @@ -406,18 +406,18 @@ proxies: # socks5 password: [YOUR_SS_PASSWORD] client-fingerprint: chrome # One of: chrome, ios, firefox or safari - # 可以是chrome, ios, firefox, safari中的一个 + # 可以是 chrome, ios, firefox, safari 中的一个 plugin: restls plugin-opts: host: "www.microsoft.com" # Must be a TLS 1.3 server - # 应当是一个TLS 1.3 服务器 + # 应当是一个 TLS 1.3 服务器 password: [YOUR_RESTLS_PASSWORD] version-hint: "tls13" # Control your post-handshake traffic through restls-script # Hide proxy behaviors like "tls in tls". # see https://github.com/3andne/restls/blob/main/Restls-Script:%20Hide%20Your%20Proxy%20Traffic%20Behavior.md - # 用restls剧本来控制握手后的行为,隐藏"tls in tls"等特征 + # 用 restls 剧本来控制握手后的行为,隐藏"tls in tls"等特征 # 详情:https://github.com/3andne/restls/blob/main/Restls-Script:%20%E9%9A%90%E8%97%8F%E4%BD%A0%E7%9A%84%E4%BB%A3%E7%90%86%E8%A1%8C%E4%B8%BA.md restls-script: "300?100<1,400~100,350~100,600~100,300~200,300~100" @@ -429,18 +429,18 @@ proxies: # socks5 password: [YOUR_SS_PASSWORD] client-fingerprint: chrome # One of: chrome, ios, firefox or safari - # 可以是chrome, ios, firefox, safari中的一个 + # 可以是 chrome, ios, firefox, safari 中的一个 plugin: restls plugin-opts: host: "vscode.dev" # Must be a TLS 1.2 server - # 应当是一个TLS 1.2 服务器 + # 应当是一个 TLS 1.2 服务器 password: [YOUR_RESTLS_PASSWORD] version-hint: "tls12" restls-script: "1000?100<1,500~100,350~100,600~100,400~200" # vmess - # cipher支持 auto/aes-128-gcm/chacha20-poly1305/none + # cipher 支持 auto/aes-128-gcm/chacha20-poly1305/none - name: "vmess" type: vmess server: server @@ -680,11 +680,11 @@ proxies: # socks5 port: 443 # ports: 1000,2000-3000,5000 # port 不可省略 # hop-interval: 15 - # up和down均不写或为0则使用BBR流控 + # up 和 down 均不写或为 0 则使用 BBR 流控 # up: "30 Mbps" # 若不写单位,默认为 Mbps # down: "200 Mbps" # 若不写单位,默认为 Mbps password: yourpassword - # obfs: salamander # 默认为空,如果填写则开启obfs,目前仅支持salamander + # obfs: salamander # 默认为空,如果填写则开启 obfs,目前仅支持 salamander # obfs-password: yourpassword # sni: server.com # skip-cert-verify: false @@ -710,9 +710,9 @@ proxies: # socks5 # reserved: [209,98,59] # 一个出站代理的标识。当值不为空时,将使用指定的 proxy 发出连接 # dialer-proxy: "ss1" - # remote-dns-resolve: true # 强制dns远程解析,默认值为false - # dns: [ 1.1.1.1, 8.8.8.8 ] # 仅在remote-dns-resolve为true时生效 - # 如果peers不为空,该段落中的allowed-ips不可为空;前面段落的server,port,public-key,pre-shared-key均会被忽略,但private-key会被保留且只能在顶层指定 + # remote-dns-resolve: true # 强制 dns 远程解析,默认值为 false + # dns: [ 1.1.1.1, 8.8.8.8 ] # 仅在 remote-dns-resolve 为 true 时生效 + # 如果 peers 不为空,该段落中的 allowed-ips 不可为空;前面段落的 server,port,public-key,pre-shared-key 均会被忽略,但 private-key 会被保留且只能在顶层指定 # peers: # - server: 162.159.192.1 # port: 2480 @@ -726,9 +726,9 @@ proxies: # socks5 server: www.example.com port: 10443 type: tuic - # tuicV4必须填写token (不可同时填写uuid和password) + # tuicV4 必须填写 token(不可同时填写 uuid 和 password) token: TOKEN - # tuicV5必须填写uuid和password(不可同时填写token) + # tuicV5 必须填写 uuid 和 password(不可同时填写 token) uuid: 00000000-0000-0000-0000-000000000001 password: PASSWORD_1 # ip: 127.0.0.1 # for overwriting the DNS lookup result of the server address set in option 'server' @@ -746,8 +746,8 @@ proxies: # socks5 # 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不兼容!!! + # meta 和 sing-box 私有扩展,将 ss-uot 用于 udp 中继,开启此选项后 udp-relay-mode 将失效 + # 警告,与原版 tuic 不兼容!!! # udp-over-stream: false # udp-over-stream-version: 1 @@ -780,12 +780,12 @@ proxies: # socks5 password: password privateKey: path -# dns出站会将请求劫持到内部dns模块,所有请求均在内部处理 +# dns 出站会将请求劫持到内部 dns 模块,所有请求均在内部处理 - name: "dns-out" type: dns proxy-groups: - # 代理链,目前relay可以支持udp的只有vmess/vless/trojan/ss/ssr/tuic - # wireguard目前不支持在relay中使用,请使用proxy中的dialer-proxy配置项 + # 代理链,目前 relay 可以支持 udp 的只有 vmess/vless/trojan/ss/ssr/tuic + # wireguard 目前不支持在 relay 中使用,请使用 proxy 中的 dialer-proxy 配置项 # Traffic: mihomo <-> http <-> vmess <-> ss1 <-> ss2 <-> Internet - name: "relay" type: relay @@ -859,10 +859,19 @@ proxy-groups: # Mihomo 格式的节点或支持 *ray 的分享格式 proxy-providers: provider1: - type: http # http 的 path 可空置,默认储存路径为 homedir的proxies文件夹,文件名为url的md5 + type: http # http 的 path 可空置,默认储存路径为 homedir 的 proxies 文件夹,文件名为 url 的 md5 url: "url" interval: 3600 path: ./provider1.yaml # 默认只允许存储在 mihomo 的 Home Dir,如果想存储到任意位置,添加环境变量 SKIP_SAFE_PATH_CHECK=1 + proxy: DIRECT + header: + User-Agent: + - "Clash/v1.18.0" + - "mihomo/1.18.3" + # Accept: + # - 'application/vnd.github.v3.raw' + # Authorization: + # - 'token 1231231' health-check: enable: true interval: 600 @@ -892,8 +901,9 @@ rule-providers: behavior: classical # domain ipcidr interval: 259200 path: /path/to/save/file.yaml # 默认只允许存储在 mihomo 的 Home Dir,如果想存储到任意位置,添加环境变量 SKIP_SAFE_PATH_CHECK=1 - type: http # http 的 path 可空置,默认储存路径为 homedir的rules文件夹,文件名为url的md5 + type: http # http 的 path 可空置,默认储存路径为 homedir 的 rules 文件夹,文件名为 url 的 md5 url: "url" + proxy: DIRECT rule2: behavior: classical interval: 259200 @@ -942,7 +952,7 @@ listeners: port: 10808 #listen: 0.0.0.0 # 默认监听 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules - # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理 + # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 # udp: false # 默认 true - name: http-in-1 @@ -950,14 +960,14 @@ listeners: port: 10809 listen: 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules - # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) + # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错) - name: mixed-in-1 type: mixed # HTTP(S) 和 SOCKS 代理混合 port: 10810 listen: 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules - # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) + # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错) # udp: false # 默认 true - name: reidr-in-1 @@ -965,14 +975,14 @@ listeners: port: 10811 listen: 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules - # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) + # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错) - name: tproxy-in-1 type: tproxy port: 10812 listen: 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules - # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) + # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错) # udp: false # 默认 true - name: shadowsocks-in-1 @@ -980,7 +990,7 @@ listeners: port: 10813 listen: 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules - # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) + # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错) password: vlmpIPSyHH6f4S8WVPdRIHIlzmB+GIRfoH3aNJ/t9Gg= cipher: 2022-blake3-aes-256-gcm @@ -989,13 +999,13 @@ listeners: port: 10814 listen: 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules - # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) + # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错) users: - username: 1 uuid: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68 alterId: 1 - # ws-path: "/" # 如果不为空则开启websocket传输层 - # 下面两项如果填写则开启tls(需要同时填写) + # ws-path: "/" # 如果不为空则开启 websocket 传输层 + # 下面两项如果填写则开启 tls(需要同时填写) # certificate: ./server.crt # private-key: ./server.key @@ -1004,10 +1014,10 @@ listeners: port: 10815 listen: 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules - # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) - # token: # tuicV4填写(可以同时填写users) + # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错) + # token: # tuicV4 填写(可以同时填写 users) # - TOKEN - # users: # tuicV5填写(可以同时填写token) + # users: # tuicV5 填写(可以同时填写 token) # 00000000-0000-0000-0000-000000000000: PASSWORD_0 # 00000000-0000-0000-0000-000000000001: PASSWORD_1 # certificate: ./server.crt @@ -1024,25 +1034,25 @@ listeners: port: 10816 listen: 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules - # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) + # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错) network: [tcp, udp] target: target.com - name: tun-in-1 type: tun # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules - # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) + # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错) stack: system # gvisor / mixed dns-hijack: - 0.0.0.0:53 # 需要劫持的 DNS # auto-detect-interface: false # 自动识别出口网卡 # auto-route: false # 配置路由表 # mtu: 9000 # 最大传输单元 - inet4-address: # 必须手动设置ipv4地址段 + inet4-address: # 必须手动设置 ipv4 地址段 - 198.19.0.1/30 - inet6-address: # 必须手动设置ipv6地址段 + inet6-address: # 必须手动设置 ipv6 地址段 - "fdfe:dcba:9877::1/126" - # strict-route: true # 将所有连接路由到tun来防止泄漏,但你的设备将无法其他设备被访问 + # strict-route: true # 将所有连接路由到 tun 来防止泄漏,但你的设备将无法其他设备被访问 # inet4-route-address: # 启用 auto-route 时使用自定义路由而不是默认路由 # - 0.0.0.0/1 # - 128.0.0.0/1 @@ -1070,17 +1080,17 @@ listeners: # exclude-package: # 排除被路由的 Android 应用包名 # - com.android.captiveportallogin # 入口配置与 Listener 等价,传入流量将和 socks,mixed 等入口一样按照 mode 所指定的方式进行匹配处理 -# shadowsocks,vmess 入口配置(传入流量将和socks,mixed等入口一样按照mode所指定的方式进行匹配处理) +# shadowsocks,vmess 入口配置(传入流量将和 socks,mixed 等入口一样按照 mode 所指定的方式进行匹配处理) # ss-config: ss://2022-blake3-aes-256-gcm:vlmpIPSyHH6f4S8WVPdRIHIlzmB+GIRfoH3aNJ/t9Gg=@:23456 # vmess-config: vmess://1:9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68@:12345 -# tuic服务器入口(传入流量将和socks,mixed等入口一样按照mode所指定的方式进行匹配处理) +# tuic 服务器入口(传入流量将和 socks,mixed 等入口一样按照 mode 所指定的方式进行匹配处理) # tuic-server: # enable: true # listen: 127.0.0.1:10443 -# token: # tuicV4填写(可以同时填写users) +# token: # tuicV4 填写(可以同时填写 users) # - TOKEN -# users: # tuicV5填写(可以同时填写token) +# users: # tuicV5 填写(可以同时填写 token) # 00000000-0000-0000-0000-000000000000: PASSWORD_0 # 00000000-0000-0000-0000-000000000001: PASSWORD_1 # certificate: ./server.crt diff --git a/listener/inner/tcp.go b/listener/inner/tcp.go index dac3d721..ee34ada6 100644 --- a/listener/inner/tcp.go +++ b/listener/inner/tcp.go @@ -16,7 +16,7 @@ func New(t C.Tunnel) { tunnel = t } -func HandleTcp(address string) (conn net.Conn, err error) { +func HandleTcp(address string, proxy string) (conn net.Conn, err error) { if tunnel == nil { return nil, errors.New("tcp uninitialized") } @@ -28,6 +28,9 @@ func HandleTcp(address string) (conn net.Conn, err error) { metadata.Type = C.INNER metadata.DNSMode = C.DNSNormal metadata.Process = C.MihomoName + if proxy != "" { + metadata.SpecialProxy = proxy + } if h, port, err := net.SplitHostPort(address); err == nil { if port, err := strconv.ParseUint(port, 10, 16); err == nil { metadata.DstPort = uint16(port) diff --git a/rules/provider/parse.go b/rules/provider/parse.go index a867d570..a20da28d 100644 --- a/rules/provider/parse.go +++ b/rules/provider/parse.go @@ -21,6 +21,7 @@ type ruleProviderSchema struct { Behavior string `provider:"behavior"` Path string `provider:"path,omitempty"` URL string `provider:"url,omitempty"` + Proxy string `provider:"proxy,omitempty"` Format string `provider:"format,omitempty"` Interval int `provider:"interval,omitempty"` } @@ -61,17 +62,14 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t path := C.Path.Resolve(schema.Path) vehicle = resource.NewFileVehicle(path) case "http": + path := C.Path.GetPathByHash("rules", schema.URL) if schema.Path != "" { - path := C.Path.Resolve(schema.Path) + path = C.Path.Resolve(schema.Path) if !features.CMFA && !C.Path.IsSafePath(path) { return nil, fmt.Errorf("%w: %s", errSubPath, path) } - vehicle = resource.NewHTTPVehicle(schema.URL, path) - } else { - path := C.Path.GetPathByHash("rules", schema.URL) - vehicle = resource.NewHTTPVehicle(schema.URL, path) } - + vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, nil) default: return nil, fmt.Errorf("unsupported vehicle type: %s", schema.Type) } From 72df27be44d17d581543eff1d874aa8448d024cc Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 8 Apr 2024 10:15:29 +0800 Subject: [PATCH 039/311] chore: update dependencies --- go.mod | 12 ++++++------ go.sum | 21 +++++++++++---------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 7270049a..8cdb8612 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 - github.com/metacubex/sing-tun v0.2.1-0.20240405021556-f37a4aa3d060 + github.com/metacubex/sing-tun v0.2.6 github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 @@ -34,7 +34,7 @@ require ( github.com/puzpuzpuz/xsync/v3 v3.1.0 github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.3.6 + github.com/sagernet/sing v0.3.8 github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 github.com/sagernet/sing-shadowtls v0.1.4 github.com/sagernet/utls v1.5.4 @@ -47,11 +47,11 @@ require ( github.com/zhangyunhao116/fastrand v0.3.0 go.uber.org/automaxprocs v1.5.3 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba - golang.org/x/crypto v0.21.0 + golang.org/x/crypto v0.22.0 golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 - golang.org/x/net v0.22.0 + golang.org/x/net v0.24.0 golang.org/x/sync v0.6.0 - golang.org/x/sys v0.18.0 + golang.org/x/sys v0.19.0 google.golang.org/protobuf v1.33.0 gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.2.1 @@ -110,4 +110,4 @@ require ( golang.org/x/tools v0.18.0 // indirect ) -replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240313064558-c197257f6542 +replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764 diff --git a/go.sum b/go.sum index 4bddc40e..b1ca415e 100644 --- a/go.sum +++ b/go.sum @@ -106,16 +106,16 @@ github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec h1:HxreOiFTUrJXJa github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= github.com/metacubex/quic-go v0.42.1-0.20240319071510-a251e5c66a5c h1:AhaPKvVqF3N/jXFmlW51Cf1+KddslKAsZqcdgGhZjr0= github.com/metacubex/quic-go v0.42.1-0.20240319071510-a251e5c66a5c/go.mod h1:iGx3Y1zynls/FjFgykLSqDcM81U0IKePRTXEz5g3iiQ= -github.com/metacubex/sing v0.0.0-20240313064558-c197257f6542 h1:e9nBnrJBv3HzZVeSzJN0G2SADjebd2ZLF1F5dmsjUTc= -github.com/metacubex/sing v0.0.0-20240313064558-c197257f6542/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= +github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764 h1:+czGKoynxYA90YaL3NlCAIJHnlqwoUlLWgmOhdm5ZU8= +github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01 h1:5INHs85Gp1JZsdF7fQp1pXUjfJOX2dhwZjuUQWJVSt8= github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01/go.mod h1:WyY0zYxv+o+18R/Ece+QFontlgXoobKbNqbtYn2zjz8= github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ= github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A= github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8= -github.com/metacubex/sing-tun v0.2.1-0.20240405021556-f37a4aa3d060 h1:SEkMqQlInU4KoyaISvEPKEzhDw0CnTr2TvIuj/hmEQ0= -github.com/metacubex/sing-tun v0.2.1-0.20240405021556-f37a4aa3d060/go.mod h1:GfLZG/QgGpW9+BPjltzONrL5vVms86TWqmZ23J68ISc= +github.com/metacubex/sing-tun v0.2.6 h1:frc58BqnIClqcC9KcYBfVAn5bgO6WW1ANKvZW3/HYAQ= +github.com/metacubex/sing-tun v0.2.6/go.mod h1:4VsMwZH1IlgPGFK1ZbBomZ/B2MYkTgs2+gnBAr5GOIo= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbTMBip/C+R79bvbfr42xH1gZl3uFb0RELdZiQ= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 h1:AGyIB55UfQm/0ZH0HtQO9u3l//yjtHUpjeRjjPGfGRI= @@ -222,8 +222,8 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= 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.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -232,8 +232,8 @@ golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -253,9 +253,10 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= From bd703b8ff29754c7f0a87b4ff60160bf3314d226 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 9 Apr 2024 14:16:09 +0800 Subject: [PATCH 040/311] fix: truncate dns message in udp response https://github.com/MetaCubeX/mihomo/issues/1168 --- component/resolver/relay.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/component/resolver/relay.go b/component/resolver/relay.go index 27b25af1..818b4152 100644 --- a/component/resolver/relay.go +++ b/component/resolver/relay.go @@ -46,7 +46,7 @@ func RelayDnsConn(ctx context.Context, conn net.Conn, readTimeout time.Duration) ctx, cancel := context.WithTimeout(ctx, DefaultDnsRelayTimeout) defer cancel() inData := buff[:n] - msg, err := RelayDnsPacket(ctx, inData, buff) + msg, err := relayDnsPacket(ctx, inData, buff, 0) if err != nil { return err } @@ -69,7 +69,7 @@ func RelayDnsConn(ctx context.Context, conn net.Conn, readTimeout time.Duration) return nil } -func RelayDnsPacket(ctx context.Context, payload []byte, target []byte) ([]byte, error) { +func relayDnsPacket(ctx context.Context, payload []byte, target []byte, maxSize int) ([]byte, error) { msg := &D.Msg{} if err := msg.Unpack(payload); err != nil { return nil, err @@ -83,6 +83,14 @@ func RelayDnsPacket(ctx context.Context, payload []byte, target []byte) ([]byte, } r.SetRcode(msg, r.Rcode) + if maxSize > 0 { + r.Truncate(maxSize) + } r.Compress = true return r.PackBuffer(target) } + +// RelayDnsPacket will truncate udp message up to SafeDnsPacketSize +func RelayDnsPacket(ctx context.Context, payload []byte, target []byte) ([]byte, error) { + return relayDnsPacket(ctx, payload, target, SafeDnsPacketSize) +} From de38fa882c5f47347367bcae12ac7f502e0ba9d7 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 9 Apr 2024 14:26:14 +0800 Subject: [PATCH 041/311] fix: dns outbound udp timeout --- adapter/outbound/dns.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/adapter/outbound/dns.go b/adapter/outbound/dns.go index 21a5b2b7..8686b288 100644 --- a/adapter/outbound/dns.go +++ b/adapter/outbound/dns.go @@ -89,14 +89,14 @@ func (d *dnsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { return len(p), nil } - ctx, cancel := context.WithTimeout(d.ctx, resolver.DefaultDnsRelayTimeout) - defer cancel() - buf := pool.Get(resolver.SafeDnsPacketSize) put := func() { _ = pool.Put(buf) } copy(buf, p) // avoid p be changed after WriteTo returned go func() { // don't block the WriteTo function + ctx, cancel := context.WithTimeout(d.ctx, resolver.DefaultDnsRelayTimeout) + defer cancel() + buf, err = resolver.RelayDnsPacket(ctx, buf[:len(p)], buf) if err != nil { put() From cff7df164fc747dcae7a37d3cbf0be8f521394d4 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 9 Apr 2024 14:52:22 +0800 Subject: [PATCH 042/311] fix: hy2 packetId race --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8cdb8612..e3800d64 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.42.1-0.20240319071510-a251e5c66a5c - github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01 + github.com/metacubex/sing-quic v0.0.0-20240409064816-c16ac6b1f4b4 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 github.com/metacubex/sing-tun v0.2.6 diff --git a/go.sum b/go.sum index b1ca415e..05d4683c 100644 --- a/go.sum +++ b/go.sum @@ -108,8 +108,8 @@ github.com/metacubex/quic-go v0.42.1-0.20240319071510-a251e5c66a5c h1:AhaPKvVqF3 github.com/metacubex/quic-go v0.42.1-0.20240319071510-a251e5c66a5c/go.mod h1:iGx3Y1zynls/FjFgykLSqDcM81U0IKePRTXEz5g3iiQ= github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764 h1:+czGKoynxYA90YaL3NlCAIJHnlqwoUlLWgmOhdm5ZU8= github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= -github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01 h1:5INHs85Gp1JZsdF7fQp1pXUjfJOX2dhwZjuUQWJVSt8= -github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01/go.mod h1:WyY0zYxv+o+18R/Ece+QFontlgXoobKbNqbtYn2zjz8= +github.com/metacubex/sing-quic v0.0.0-20240409064816-c16ac6b1f4b4 h1:JB+BgUgQVicS1oGiw63c0xQWEAkUeuTylDy5WIaco7o= +github.com/metacubex/sing-quic v0.0.0-20240409064816-c16ac6b1f4b4/go.mod h1:WyY0zYxv+o+18R/Ece+QFontlgXoobKbNqbtYn2zjz8= github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ= github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A= From 16fadd24418ea3606b57b793f1f6ce14b56ffb01 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 10 Apr 2024 15:34:28 +0800 Subject: [PATCH 043/311] fix: don't ignore bind6 error when udp's target is ipv6 address --- component/dhcp/conn.go | 3 ++- component/dialer/bind.go | 2 +- component/dialer/bind_darwin.go | 2 +- component/dialer/bind_linux.go | 2 +- component/dialer/bind_others.go | 4 ++-- component/dialer/bind_windows.go | 12 ++++++------ component/dialer/dialer.go | 20 +++++++++----------- 7 files changed, 22 insertions(+), 23 deletions(-) diff --git a/component/dhcp/conn.go b/component/dhcp/conn.go index ff26275b..61c801bd 100644 --- a/component/dhcp/conn.go +++ b/component/dhcp/conn.go @@ -3,6 +3,7 @@ package dhcp import ( "context" "net" + "net/netip" "runtime" "github.com/metacubex/mihomo/component/dialer" @@ -24,5 +25,5 @@ func ListenDHCPClient(ctx context.Context, ifaceName string) (net.PacketConn, er options = append(options, dialer.WithFallbackBind(true)) } - return dialer.ListenPacket(ctx, "udp4", listenAddr, options...) + return dialer.ListenPacket(ctx, "udp4", listenAddr, netip.AddrPortFrom(netip.AddrFrom4([4]byte{255, 255, 255, 255}), 67), options...) } diff --git a/component/dialer/bind.go b/component/dialer/bind.go index 9b6471a3..de6aacd3 100644 --- a/component/dialer/bind.go +++ b/component/dialer/bind.go @@ -75,7 +75,7 @@ func fallbackBindIfaceToDialer(ifaceName string, dialer *net.Dialer, network str return nil } -func fallbackBindIfaceToListenConfig(ifaceName string, _ *net.ListenConfig, network, address string) (string, error) { +func fallbackBindIfaceToListenConfig(ifaceName string, _ *net.ListenConfig, network, address string, rAddrPort netip.AddrPort) (string, error) { _, port, err := net.SplitHostPort(address) if err != nil { port = "0" diff --git a/component/dialer/bind_darwin.go b/component/dialer/bind_darwin.go index f83b86f8..fdea24bf 100644 --- a/component/dialer/bind_darwin.go +++ b/component/dialer/bind_darwin.go @@ -46,7 +46,7 @@ func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, _ netip.A return nil } -func bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, _, address string) (string, error) { +func bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, _, address string, rAddrPort netip.AddrPort) (string, error) { ifaceObj, err := iface.ResolveInterface(ifaceName) if err != nil { return "", err diff --git a/component/dialer/bind_linux.go b/component/dialer/bind_linux.go index 1ec98f3d..79cf735b 100644 --- a/component/dialer/bind_linux.go +++ b/component/dialer/bind_linux.go @@ -35,7 +35,7 @@ func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, _ netip.A return nil } -func bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, _, address string) (string, error) { +func bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, _, address string, rAddrPort netip.AddrPort) (string, error) { addControlToListenConfig(lc, bindControl(ifaceName)) return address, nil diff --git a/component/dialer/bind_others.go b/component/dialer/bind_others.go index 44181610..b7db962a 100644 --- a/component/dialer/bind_others.go +++ b/component/dialer/bind_others.go @@ -11,8 +11,8 @@ func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, network string, des return fallbackBindIfaceToDialer(ifaceName, dialer, network, destination) } -func bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, network, address string) (string, error) { - return fallbackBindIfaceToListenConfig(ifaceName, lc, network, address) +func bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, network, address string, rAddrPort netip.AddrPort) (string, error) { + return fallbackBindIfaceToListenConfig(ifaceName, lc, network, address, rAddrPort) } func ParseNetwork(network string, addr netip.Addr) string { diff --git a/component/dialer/bind_windows.go b/component/dialer/bind_windows.go index 120f1657..98a076d0 100644 --- a/component/dialer/bind_windows.go +++ b/component/dialer/bind_windows.go @@ -36,7 +36,7 @@ func bind6(handle syscall.Handle, ifaceIdx int) error { return err } -func bindControl(ifaceIdx int) controlFn { +func bindControl(ifaceIdx int, rAddrPort netip.AddrPort) controlFn { return func(ctx context.Context, network, address string, c syscall.RawConn) (err error) { addrPort, err := netip.ParseAddrPort(address) if err == nil && !addrPort.Addr().IsGlobalUnicast() { @@ -55,7 +55,7 @@ func bindControl(ifaceIdx int) controlFn { innerErr = bind4err case "udp6": // golang will set network to udp6 when listenUDP on wildcard ip (eg: ":0", "") - if (!addrPort.Addr().IsValid() || addrPort.Addr().IsUnspecified()) && bind6err != nil { + if (!addrPort.Addr().IsValid() || addrPort.Addr().IsUnspecified()) && bind6err != nil && rAddrPort.Addr().Unmap().Is4() { // try bind ipv6, if failed, ignore. it's a workaround for windows disable interface ipv6 if bind4err != nil { innerErr = fmt.Errorf("%w (%s)", bind6err, bind4err) @@ -76,23 +76,23 @@ func bindControl(ifaceIdx int) controlFn { } } -func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, _ netip.Addr) error { +func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, destination netip.Addr) error { ifaceObj, err := iface.ResolveInterface(ifaceName) if err != nil { return err } - addControlToDialer(dialer, bindControl(ifaceObj.Index)) + addControlToDialer(dialer, bindControl(ifaceObj.Index, netip.AddrPortFrom(destination, 0))) return nil } -func bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, _, address string) (string, error) { +func bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, _, address string, rAddrPort netip.AddrPort) (string, error) { ifaceObj, err := iface.ResolveInterface(ifaceName) if err != nil { return "", err } - addControlToListenConfig(lc, bindControl(ifaceObj.Index)) + addControlToListenConfig(lc, bindControl(ifaceObj.Index, rAddrPort)) return address, nil } diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 12e7c960..8fb5a0a6 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -78,7 +78,7 @@ func DialContext(ctx context.Context, network, address string, options ...Option } } -func ListenPacket(ctx context.Context, network, address string, options ...Option) (net.PacketConn, error) { +func ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort, options ...Option) (net.PacketConn, error) { if features.CMFA && DefaultSocketHook != nil { return listenPacketHooked(ctx, network, address) } @@ -91,7 +91,7 @@ func ListenPacket(ctx context.Context, network, address string, options ...Optio if cfg.fallbackBind { bind = fallbackBindIfaceToListenConfig } - addr, err := bind(cfg.interfaceName, lc, network, address) + addr, err := bind(cfg.interfaceName, lc, network, address, rAddrPort) if err != nil { return nil, err } @@ -133,11 +133,9 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po var address string if IP4PEnable { - NewDestination, NewPort := lookupIP4P(destination.String(), port) - address = net.JoinHostPort(NewDestination, NewPort) - } else { - address = net.JoinHostPort(destination.String(), port) + destination, port = lookupIP4P(destination, port) } + address = net.JoinHostPort(destination.String(), port) netDialer := opt.netDialer switch netDialer.(type) { @@ -385,7 +383,7 @@ func (d Dialer) ListenPacket(ctx context.Context, network, address string, rAddr // avoid "The requested address is not valid in its context." opt = WithInterface("") } - return ListenPacket(ctx, ParseNetwork(network, rAddrPort.Addr()), address, opt) + return ListenPacket(ctx, ParseNetwork(network, rAddrPort.Addr()), address, rAddrPort, opt) } func NewDialer(options ...Option) Dialer { @@ -399,13 +397,13 @@ func GetIP4PEnable(enableIP4PConvert bool) { // kanged from https://github.com/heiher/frp/blob/ip4p/client/ip4p.go -func lookupIP4P(addr string, port string) (string, string) { - ip := net.ParseIP(addr) +func lookupIP4P(addr netip.Addr, port string) (netip.Addr, string) { + ip := addr.AsSlice() if ip[0] == 0x20 && ip[1] == 0x01 && ip[2] == 0x00 && ip[3] == 0x00 { - addr = net.IPv4(ip[12], ip[13], ip[14], ip[15]).String() + addr = netip.AddrFrom4([4]byte{ip[12], ip[13], ip[14], ip[15]}) port = strconv.Itoa(int(ip[10])<<8 + int(ip[11])) - log.Debugln("Convert IP4P address %s to %s", ip, net.JoinHostPort(addr, port)) + log.Debugln("Convert IP4P address %s to %s", ip, net.JoinHostPort(addr.String(), port)) return addr, port } return addr, port From 91a7ffaad2d78b3fcb4936f999236fc3fa8ce210 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 11 Apr 2024 09:24:53 +0800 Subject: [PATCH 044/311] fix: udp loopback detector not working --- component/iface/iface.go | 23 +++++++++++++++++++++-- component/loopback/detector.go | 21 ++++++++++++++++----- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/component/iface/iface.go b/component/iface/iface.go index 1d0219df..d543725a 100644 --- a/component/iface/iface.go +++ b/component/iface/iface.go @@ -23,7 +23,7 @@ var ( var interfaces = singledo.NewSingle[map[string]*Interface](time.Second * 20) -func ResolveInterface(name string) (*Interface, error) { +func Interfaces() (map[string]*Interface, error) { value, err, _ := interfaces.Do(func() (map[string]*Interface, error) { ifaces, err := net.Interfaces() if err != nil { @@ -69,11 +69,15 @@ func ResolveInterface(name string) (*Interface, error) { return r, nil }) + return value, err +} + +func ResolveInterface(name string) (*Interface, error) { + ifaces, err := Interfaces() if err != nil { return nil, err } - ifaces := value iface, ok := ifaces[name] if !ok { return nil, ErrIfaceNotFound @@ -82,6 +86,21 @@ func ResolveInterface(name string) (*Interface, error) { return iface, nil } +func IsLocalIp(ip netip.Addr) (bool, error) { + ifaces, err := Interfaces() + if err != nil { + return false, err + } + for _, iface := range ifaces { + for _, addr := range iface.Addrs { + if addr.Contains(ip) { + return true, nil + } + } + } + return false, nil +} + func FlushCache() { interfaces.Reset() } diff --git a/component/loopback/detector.go b/component/loopback/detector.go index b07270ed..8ec96a9d 100644 --- a/component/loopback/detector.go +++ b/component/loopback/detector.go @@ -6,6 +6,7 @@ import ( "net/netip" "github.com/metacubex/mihomo/common/callback" + "github.com/metacubex/mihomo/component/iface" C "github.com/metacubex/mihomo/constant" "github.com/puzpuzpuz/xsync/v3" @@ -15,13 +16,13 @@ var ErrReject = errors.New("reject loopback connection") type Detector struct { connMap *xsync.MapOf[netip.AddrPort, struct{}] - packetConnMap *xsync.MapOf[netip.AddrPort, struct{}] + packetConnMap *xsync.MapOf[uint16, struct{}] } func NewDetector() *Detector { return &Detector{ connMap: xsync.NewMapOf[netip.AddrPort, struct{}](), - packetConnMap: xsync.NewMapOf[netip.AddrPort, struct{}](), + packetConnMap: xsync.NewMapOf[uint16, struct{}](), } } @@ -49,9 +50,10 @@ func (l *Detector) NewPacketConn(conn C.PacketConn) C.PacketConn { if !connAddr.IsValid() { return conn } - l.packetConnMap.Store(connAddr, struct{}{}) + port := connAddr.Port() + l.packetConnMap.Store(port, struct{}{}) return callback.NewCloseCallbackPacketConn(conn, func() { - l.packetConnMap.Delete(connAddr) + l.packetConnMap.Delete(port) }) } @@ -71,7 +73,16 @@ func (l *Detector) CheckPacketConn(metadata *C.Metadata) error { if !connAddr.IsValid() { return nil } - if _, ok := l.packetConnMap.Load(connAddr); ok { + + isLocalIp, err := iface.IsLocalIp(connAddr.Addr()) + if err != nil { + return err + } + if !isLocalIp && !connAddr.Addr().IsLoopback() { + return nil + } + + if _, ok := l.packetConnMap.Load(connAddr.Port()); ok { return fmt.Errorf("%w to: %s", ErrReject, metadata.RemoteAddress()) } return nil From e3b69b8ae217cb1c09b1f91902cb3cb77d16c748 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Fri, 12 Apr 2024 04:58:07 +0800 Subject: [PATCH 045/311] chore: Make SubScriptioninfo query also follow Proxy --- adapter/provider/provider.go | 8 ++++---- component/resource/vehicle.go | 8 ++++++++ constant/provider/interface.go | 1 + 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index 2715a309..a40a50ec 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -124,8 +124,8 @@ func (pp *proxySetProvider) getSubscriptionInfo() { go func() { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(), - http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) + resp, err := mihomoHttp.HttpRequestWithProxy(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(), + http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil, pp.Vehicle().Proxy()) if err != nil { return } @@ -133,8 +133,8 @@ func (pp *proxySetProvider) getSubscriptionInfo() { userInfoStr := strings.TrimSpace(resp.Header.Get("subscription-userinfo")) if userInfoStr == "" { - resp2, err := mihomoHttp.HttpRequest(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(), - http.MethodGet, http.Header{"User-Agent": {"Quantumultx"}}, nil) + resp2, err := mihomoHttp.HttpRequestWithProxy(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(), + http.MethodGet, http.Header{"User-Agent": {"Quantumultx"}}, nil, pp.Vehicle().Proxy()) if err != nil { return } diff --git a/component/resource/vehicle.go b/component/resource/vehicle.go index 2d71be94..b13369d2 100644 --- a/component/resource/vehicle.go +++ b/component/resource/vehicle.go @@ -28,6 +28,10 @@ func (f *FileVehicle) Read() ([]byte, error) { return os.ReadFile(f.path) } +func (f *FileVehicle) Proxy() string { + return "" +} + func NewFileVehicle(path string) *FileVehicle { return &FileVehicle{path: path} } @@ -51,6 +55,10 @@ func (h *HTTPVehicle) Path() string { return h.path } +func (h *HTTPVehicle) Proxy() string { + return h.proxy +} + func (h *HTTPVehicle) Read() ([]byte, error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) defer cancel() diff --git a/constant/provider/interface.go b/constant/provider/interface.go index f2b6939e..bb73d1bc 100644 --- a/constant/provider/interface.go +++ b/constant/provider/interface.go @@ -31,6 +31,7 @@ func (v VehicleType) String() string { type Vehicle interface { Read() ([]byte, error) Path() string + Proxy() string Type() VehicleType } From d84f88b50fb87d0b8e7761d7b14a40c4a41ea467 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 13 Apr 2024 08:02:43 +0800 Subject: [PATCH 046/311] fix: `system://` should ignore dns server setting by tun listener --- component/resolver/system.go | 39 ++++++++++++++++++++++++++++++++++++ dns/system.go | 4 ++++ listener/sing_tun/server.go | 12 +++++++++++ 3 files changed, 55 insertions(+) create mode 100644 component/resolver/system.go diff --git a/component/resolver/system.go b/component/resolver/system.go new file mode 100644 index 00000000..a134bb05 --- /dev/null +++ b/component/resolver/system.go @@ -0,0 +1,39 @@ +package resolver + +import "sync" + +var blacklist struct { + Map map[string]struct{} + Mutex sync.Mutex +} + +func init() { + blacklist.Map = make(map[string]struct{}) +} + +func AddSystemDnsBlacklist(names ...string) { + blacklist.Mutex.Lock() + defer blacklist.Mutex.Unlock() + for _, name := range names { + blacklist.Map[name] = struct{}{} + } +} + +func RemoveSystemDnsBlacklist(names ...string) { + blacklist.Mutex.Lock() + defer blacklist.Mutex.Unlock() + for _, name := range names { + delete(blacklist.Map, name) + } +} + +func IsSystemDnsBlacklisted(names ...string) bool { + blacklist.Mutex.Lock() + defer blacklist.Mutex.Unlock() + for _, name := range names { + if _, ok := blacklist.Map[name]; ok { + return true + } + } + return false +} diff --git a/dns/system.go b/dns/system.go index 37607a60..dc1006fa 100644 --- a/dns/system.go +++ b/dns/system.go @@ -8,6 +8,7 @@ import ( "sync" "time" + "github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/log" D "github.com/miekg/dns" @@ -39,6 +40,9 @@ func (c *systemClient) getDnsClients() ([]dnsClient, error) { if nameservers, err = dnsReadConfig(); err == nil { log.Debugln("[DNS] system dns update to %s", nameservers) for _, addr := range nameservers { + if resolver.IsSystemDnsBlacklisted(addr) { + continue + } if _, ok := c.dnsClients[addr]; !ok { clients := transform( []NameServer{{ diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 8fe534df..7cd9fc72 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -12,6 +12,7 @@ import ( "github.com/metacubex/mihomo/adapter/inbound" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/iface" + "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" LC "github.com/metacubex/mihomo/listener/config" "github.com/metacubex/mihomo/listener/sing" @@ -39,6 +40,8 @@ type Listener struct { networkUpdateMonitor tun.NetworkUpdateMonitor defaultInterfaceMonitor tun.DefaultInterfaceMonitor packageManager tun.PackageManager + + dnsServerIp []string } func CalculateInterfaceName(name string) (tunName string) { @@ -147,12 +150,16 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis dnsAdds = append(dnsAdds, addrPort) } + + var dnsServerIp []string for _, a := range options.Inet4Address { addrPort := netip.AddrPortFrom(a.Addr().Next(), 53) + dnsServerIp = append(dnsServerIp, a.Addr().Next().String()) dnsAdds = append(dnsAdds, addrPort) } for _, a := range options.Inet6Address { addrPort := netip.AddrPortFrom(a.Addr().Next(), 53) + dnsServerIp = append(dnsServerIp, a.Addr().Next().String()) dnsAdds = append(dnsAdds, addrPort) } @@ -244,6 +251,10 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis return } + l.dnsServerIp = dnsServerIp + // after tun.New sing-tun has set DNS to TUN interface + resolver.AddSystemDnsBlacklist(dnsServerIp...) + stackOptions := tun.StackOptions{ Context: context.TODO(), Tun: tunIf, @@ -336,6 +347,7 @@ func parseRange(uidRanges []ranges.Range[uint32], rangeList []string) ([]ranges. func (l *Listener) Close() error { l.closed = true + resolver.RemoveSystemDnsBlacklist(l.dnsServerIp...) return common.Close( l.tunStack, l.tunIf, From ca84ab1a94291fdd5b66f550c6613b69dbdeb9cc Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 17 Apr 2024 09:39:00 +0800 Subject: [PATCH 047/311] feat: support `external-controller-unix` --- config/config.go | 19 +++++++++------- docs/config.yaml | 5 +++++ hub/hub.go | 4 ++++ hub/route/server.go | 55 ++++++++++++++++++++++++++++++++++----------- 4 files changed, 62 insertions(+), 21 deletions(-) diff --git a/config/config.go b/config/config.go index c5c4fa88..f425092a 100644 --- a/config/config.go +++ b/config/config.go @@ -91,10 +91,11 @@ type Inbound struct { // Controller config type Controller struct { - ExternalController string `json:"-"` - ExternalControllerTLS string `json:"-"` - ExternalUI string `json:"-"` - Secret string `json:"-"` + ExternalController string `json:"-"` + ExternalControllerTLS string `json:"-"` + ExternalControllerUnix string `json:"-"` + ExternalUI string `json:"-"` + Secret string `json:"-"` } // NTP config @@ -304,6 +305,7 @@ type RawConfig struct { LogLevel log.LogLevel `yaml:"log-level" json:"log-level"` IPv6 bool `yaml:"ipv6" json:"ipv6"` ExternalController string `yaml:"external-controller"` + ExternalControllerUnix string `yaml:"external-controller-unix"` ExternalControllerTLS string `yaml:"external-controller-tls"` ExternalUI string `yaml:"external-ui"` ExternalUIURL string `yaml:"external-ui-url" json:"external-ui-url"` @@ -678,10 +680,11 @@ func parseGeneral(cfg *RawConfig) (*General, error) { InboundMPTCP: cfg.InboundMPTCP, }, Controller: Controller{ - ExternalController: cfg.ExternalController, - ExternalUI: cfg.ExternalUI, - Secret: cfg.Secret, - ExternalControllerTLS: cfg.ExternalControllerTLS, + ExternalController: cfg.ExternalController, + ExternalUI: cfg.ExternalUI, + Secret: cfg.Secret, + ExternalControllerUnix: cfg.ExternalControllerUnix, + ExternalControllerTLS: cfg.ExternalControllerTLS, }, UnifiedDelay: cfg.UnifiedDelay, Mode: cfg.Mode, diff --git a/docs/config.yaml b/docs/config.yaml index 869b5231..61238175 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -58,6 +58,11 @@ external-controller: 0.0.0.0:9093 # RESTful API 监听地址 external-controller-tls: 0.0.0.0:9443 # RESTful API HTTPS 监听地址,需要配置 tls 部分配置文件 # secret: "123456" # `Authorization:Bearer ${secret}` +# RESTful API Unix socket 监听地址( windows版本大于17063也可以使用,即大于等于1803/RS4版本即可使用 ) +# !!!注意: 从Unix socket访问api接口不会验证secret, 如果开启请自行保证安全问题 !!! +# 测试方法: curl -v --unix-socket "mihomo.sock" http://localhost/ +external-controller-unix: mihomo.sock + # tcp-concurrent: true # TCP 并发连接所有 IP, 将使用最快握手的 TCP # 配置 WEB UI 目录,使用 http://{{external-controller}}/ui 访问 diff --git a/hub/hub.go b/hub/hub.go index 323f8749..6d5bf49f 100644 --- a/hub/hub.go +++ b/hub/hub.go @@ -47,6 +47,10 @@ func Parse(options ...Option) error { cfg.General.Secret, cfg.TLS.Certificate, cfg.TLS.PrivateKey, cfg.General.LogLevel == log.DEBUG) } + if cfg.General.ExternalControllerUnix != "" { + go route.StartUnix(cfg.General.ExternalControllerUnix, cfg.General.LogLevel == log.DEBUG) + } + executor.ApplyConfig(cfg, true) return nil } diff --git a/hub/route/server.go b/hub/route/server.go index 8e7f225f..a1a9003e 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -9,6 +9,7 @@ import ( "net/http" "runtime/debug" "strings" + "syscall" "time" "github.com/metacubex/mihomo/adapter/inbound" @@ -47,15 +48,7 @@ func SetUIPath(path string) { uiPath = C.Path.Resolve(path) } -func Start(addr string, tlsAddr string, secret string, - certificat, privateKey string, isDebug bool) { - if serverAddr != "" { - return - } - - serverAddr = addr - serverSecret = secret - +func router(isDebug bool, withAuth bool) *chi.Mux { r := chi.NewRouter() corsM := cors.New(cors.Options{ AllowedOrigins: []string{"*"}, @@ -77,7 +70,9 @@ func Start(addr string, tlsAddr string, secret string, }()) } r.Group(func(r chi.Router) { - r.Use(authentication) + if withAuth { + r.Use(authentication) + } r.Get("/", hello) r.Get("/logs", getLogs) r.Get("/traffic", traffic) @@ -107,10 +102,21 @@ func Start(addr string, tlsAddr string, secret string, }) }) } + return r +} + +func Start(addr string, tlsAddr string, secret string, + certificate, privateKey string, isDebug bool) { + if serverAddr != "" { + return + } + + serverAddr = addr + serverSecret = secret if len(tlsAddr) > 0 { go func() { - c, err := CN.ParseCert(certificat, privateKey, C.Path) + c, err := CN.ParseCert(certificate, privateKey, C.Path) if err != nil { log.Errorln("External controller tls listen error: %s", err) return @@ -125,7 +131,7 @@ func Start(addr string, tlsAddr string, secret string, serverAddr = l.Addr().String() log.Infoln("RESTful API tls listening at: %s", serverAddr) tlsServe := &http.Server{ - Handler: r, + Handler: router(isDebug, true), TLSConfig: &tls.Config{ Certificates: []tls.Certificate{c}, }, @@ -144,12 +150,35 @@ func Start(addr string, tlsAddr string, secret string, serverAddr = l.Addr().String() log.Infoln("RESTful API listening at: %s", serverAddr) - if err = http.Serve(l, r); err != nil { + if err = http.Serve(l, router(isDebug, true)); err != nil { log.Errorln("External controller serve error: %s", err) } } +func StartUnix(addr string, isDebug bool) { + // https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ + // + // Note: As mentioned above in the ‘security’ section, when a socket binds a socket to a valid pathname address, + // a socket file is created within the filesystem. On Linux, the application is expected to unlink + // (see the notes section in the man page for AF_UNIX) before any other socket can be bound to the same address. + // The same applies to Windows unix sockets, except that, DeleteFile (or any other file delete API) + // should be used to delete the socket file prior to calling bind with the same path. + _ = syscall.Unlink(addr) + + l, err := inbound.Listen("unix", addr) + if err != nil { + log.Errorln("External controller unix listen error: %s", err) + return + } + serverAddr = l.Addr().String() + log.Infoln("RESTful API unix listening at: %s", serverAddr) + + if err = http.Serve(l, router(isDebug, false)); err != nil { + log.Errorln("External controller unix serve error: %s", err) + } +} + func setPrivateNetworkAccess(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" { From 3566542d0ea0c236525c526e391fba86c230e9c0 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 17 Apr 2024 09:39:30 +0800 Subject: [PATCH 048/311] doc: cleanup --- docs/config.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/config.yaml b/docs/config.yaml index 61238175..987491e3 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -254,7 +254,6 @@ dns: # - 'tcp://1.1.1.1#ProxyGroupName' # 指定 DNS 过代理查询,ProxyGroupName 为策略组名或节点名,过代理配置优先于配置出口网卡,当找不到策略组或节点名则设置为出口网卡 # 专用于节点域名解析的 DNS 服务器,非必要配置项 - # 配置服务器若查询失败将使用 nameserver,非并发查询 # proxy-server-nameserver: # - https://dns.google/dns-query # - tls://one.one.one.one From a87825466271753d7aef8f56c6b5ef77ca5027b2 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 17 Apr 2024 10:08:54 +0800 Subject: [PATCH 049/311] feat: support `-ext-ctl-unix` cmdline to override external controller unix address --- hub/hub.go | 6 ++++++ main.go | 25 +++++++++++++++---------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/hub/hub.go b/hub/hub.go index 6d5bf49f..38779e13 100644 --- a/hub/hub.go +++ b/hub/hub.go @@ -21,6 +21,12 @@ func WithExternalController(externalController string) Option { } } +func WithExternalControllerUnix(externalControllerUnix string) Option { + return func(cfg *config.Config) { + cfg.General.ExternalControllerUnix = externalControllerUnix + } +} + func WithSecret(secret string) Option { return func(cfg *config.Config) { cfg.General.Secret = secret diff --git a/main.go b/main.go index 748fa2e3..afe9cfd2 100644 --- a/main.go +++ b/main.go @@ -23,16 +23,17 @@ import ( ) var ( - version bool - testConfig bool - geodataMode bool - homeDir string - configFile string - externalUI string - externalController string - secret string - updateGeoMux sync.Mutex - updatingGeo = false + version bool + testConfig bool + geodataMode bool + homeDir string + configFile string + externalUI string + externalController string + externalControllerUnix string + secret string + updateGeoMux sync.Mutex + updatingGeo = false ) func init() { @@ -40,6 +41,7 @@ func init() { flag.StringVar(&configFile, "f", os.Getenv("CLASH_CONFIG_FILE"), "specify configuration file") flag.StringVar(&externalUI, "ext-ui", os.Getenv("CLASH_OVERRIDE_EXTERNAL_UI_DIR"), "override external ui directory") flag.StringVar(&externalController, "ext-ctl", os.Getenv("CLASH_OVERRIDE_EXTERNAL_CONTROLLER"), "override external controller address") + flag.StringVar(&externalControllerUnix, "ext-ctl-unix", os.Getenv("CLASH_OVERRIDE_EXTERNAL_CONTROLLER_UNIX"), "override external controller unix address") flag.StringVar(&secret, "secret", os.Getenv("CLASH_OVERRIDE_SECRET"), "override secret for RESTful API") flag.BoolVar(&geodataMode, "m", false, "set geodata mode") flag.BoolVar(&version, "v", false, "show current version of mihomo") @@ -102,6 +104,9 @@ func main() { if externalController != "" { options = append(options, hub.WithExternalController(externalController)) } + if externalControllerUnix != "" { + options = append(options, hub.WithExternalControllerUnix(externalControllerUnix)) + } if secret != "" { options = append(options, hub.WithSecret(secret)) } From 189b7b9c5ff4eaa89c0058a6a5aaf039176d3141 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Wed, 17 Apr 2024 11:12:29 +0800 Subject: [PATCH 050/311] chore: Working with unix directory --- hub/route/server.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hub/route/server.go b/hub/route/server.go index a1a9003e..b14722b4 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -7,6 +7,8 @@ import ( "encoding/json" "net" "net/http" + "os" + "path/filepath" "runtime/debug" "strings" "syscall" @@ -157,6 +159,14 @@ func Start(addr string, tlsAddr string, secret string, } func StartUnix(addr string, isDebug bool) { + dir := filepath.Dir(addr) + if _, err := os.Stat(dir); os.IsNotExist(err) { + if err := os.MkdirAll(dir, 0o755); err != nil { + log.Errorln("External controller unix listen error: %s", err) + return + } + } + // https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ // // Note: As mentioned above in the ‘security’ section, when a socket binds a socket to a valid pathname address, From ff2071c1da605f4ad1c1b1033759fc494fa4c4e9 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Wed, 17 Apr 2024 17:55:13 +0800 Subject: [PATCH 051/311] chore: Update systemd service file --- .github/mihomo.service | 4 ++-- .github/workflows/build.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/mihomo.service b/.github/mihomo.service index a884059f..f34b6a6a 100644 --- a/.github/mihomo.service +++ b/.github/mihomo.service @@ -6,8 +6,8 @@ After=network.target NetworkManager.service systemd-networkd.service iwd.service Type=simple LimitNPROC=500 LimitNOFILE=1000000 -CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH -AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH +CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE +AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE Restart=always ExecStartPre=/usr/bin/sleep 2s ExecStart=/usr/bin/mihomo -d /etc/mihomo diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2639c04b..570d2e96 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -194,7 +194,7 @@ jobs: cat > mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/DEBIAN/control < Date: Thu, 18 Apr 2024 18:02:12 +0800 Subject: [PATCH 052/311] chore: Update workflow --- .github/workflows/build.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 570d2e96..df320d29 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -177,6 +177,11 @@ jobs: else ARCH=${{matrix.jobs.goarch}} fi + PackageVersion=$(curl -s "https://api.github.com/repos/MetaCubeX/mihomo/releases/latest" | grep -o '"tag_name": "[^"]*' | grep -o '[^"]*$' | sed 's/v//g' ) + if [ $(git branch | awk -F ' ' '{print $2}') = "Alpha" ]; then + PackageVersion="$(echo "${PackageVersion}" | awk -F '.' '{$NF = $NF + 1; print}' OFS='.')-${VERSION}" + fi + mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/DEBIAN mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/usr/bin mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/etc/mihomo @@ -194,7 +199,7 @@ jobs: cat > mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/DEBIAN/control < Date: Fri, 19 Apr 2024 17:33:00 +0800 Subject: [PATCH 053/311] chore: Make unix socket follow homeDir --- hub/route/server.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hub/route/server.go b/hub/route/server.go index b14722b4..c28782b9 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -159,6 +159,8 @@ func Start(addr string, tlsAddr string, secret string, } func StartUnix(addr string, isDebug bool) { + addr = C.Path.Resolve(addr) + dir := filepath.Dir(addr) if _, err := os.Stat(dir); os.IsNotExist(err) { if err := os.MkdirAll(dir, 0o755); err != nil { From 002b8af94a6a0f73f140d6e11e0ce173805d5472 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sat, 20 Apr 2024 22:22:17 +0800 Subject: [PATCH 054/311] Chore: Let CA read following homeDir --- component/ca/config.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/component/ca/config.go b/component/ca/config.go index 53cb98ab..d56809a1 100644 --- a/component/ca/config.go +++ b/component/ca/config.go @@ -13,6 +13,8 @@ import ( "strconv" "strings" "sync" + + C "github.com/metacubex/mihomo/constant" ) var trustCerts []*x509.Certificate @@ -117,7 +119,7 @@ func GetTLSConfig(tlsConfig *tls.Config, fingerprint string, customCA string, cu var certificate []byte var err error if len(customCA) > 0 { - certificate, err = os.ReadFile(customCA) + certificate, err = os.ReadFile(C.Path.Resolve(customCA)) if err != nil { return nil, fmt.Errorf("load ca error: %w", err) } From b2280c85b726891ba33f28bdd8c39483db543d88 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 23 Apr 2024 14:40:21 +0800 Subject: [PATCH 055/311] chore: update dependencies --- go.mod | 24 ++++++++++++------------ go.sum | 47 ++++++++++++++++++++++++----------------------- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/go.mod b/go.mod index e3800d64..a8feb726 100644 --- a/go.mod +++ b/go.mod @@ -13,21 +13,21 @@ require ( github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.3 github.com/gobwas/ws v1.3.2 - github.com/gofrs/uuid/v5 v5.0.0 - github.com/insomniacslk/dhcp v0.0.0-20240227161007-c728f5dd21c8 + github.com/gofrs/uuid/v5 v5.1.0 + github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49 github.com/klauspost/cpuid/v2 v2.2.7 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.42.1-0.20240319071510-a251e5c66a5c - github.com/metacubex/sing-quic v0.0.0-20240409064816-c16ac6b1f4b4 + github.com/metacubex/quic-go v0.42.1-0.20240418003344-f006b5735d98 + github.com/metacubex/sing-quic v0.0.0-20240418004036-814c531c378d github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 github.com/metacubex/sing-tun v0.2.6 github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 - github.com/miekg/dns v1.1.58 + github.com/miekg/dns v1.1.59 github.com/mroth/weightedrand/v2 v2.1.0 github.com/openacid/low v0.1.21 github.com/oschwald/maxminddb-golang v1.12.0 @@ -40,21 +40,21 @@ require ( github.com/sagernet/utls v1.5.4 github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e github.com/samber/lo v1.39.0 - github.com/shirou/gopsutil/v3 v3.24.2 + github.com/shirou/gopsutil/v3 v3.24.3 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.9.0 github.com/wk8/go-ordered-map/v2 v2.1.8 - github.com/zhangyunhao116/fastrand v0.3.0 + github.com/zhangyunhao116/fastrand v0.4.0 go.uber.org/automaxprocs v1.5.3 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba golang.org/x/crypto v0.22.0 - golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 + golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f golang.org/x/net v0.24.0 - golang.org/x/sync v0.6.0 + golang.org/x/sync v0.7.0 golang.org/x/sys v0.19.0 google.golang.org/protobuf v1.33.0 gopkg.in/yaml.v3 v3.0.1 - lukechampine.com/blake3 v1.2.1 + lukechampine.com/blake3 v1.2.2 ) require ( @@ -104,10 +104,10 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect go.uber.org/mock v0.4.0 // indirect - golang.org/x/mod v0.15.0 // indirect + golang.org/x/mod v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.18.0 // indirect + golang.org/x/tools v0.20.0 // indirect ) replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764 diff --git a/go.sum b/go.sum index 05d4683c..8cd6d29d 100644 --- a/go.sum +++ b/go.sum @@ -62,8 +62,8 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.3.2 h1:zlnbNHxumkRvfPWgfXu8RBwyNR1x8wh9cf5PTOCqs9Q= github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= -github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M= -github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= +github.com/gofrs/uuid/v5 v5.1.0 h1:S5rqVKIigghZTCBKPCw0Y+bXkn26K3TB5mvQq2Ix8dk= +github.com/gofrs/uuid/v5 v5.1.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= @@ -80,6 +80,8 @@ github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbg github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/insomniacslk/dhcp v0.0.0-20240227161007-c728f5dd21c8 h1:V3plQrMHRWOB5zMm3yNqvBxDQVW1+/wHBSok5uPdmVs= github.com/insomniacslk/dhcp v0.0.0-20240227161007-c728f5dd21c8/go.mod h1:izxuNQZeFrbx2nK2fAyN5iNUB34Fe9j0nK4PwLzAkKw= +github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49 h1:/OuvSMGT9+xnyZ+7MZQ1zdngaCCAdPoSw8B/uurZ7pg= +github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 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= @@ -104,12 +106,12 @@ 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-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= -github.com/metacubex/quic-go v0.42.1-0.20240319071510-a251e5c66a5c h1:AhaPKvVqF3N/jXFmlW51Cf1+KddslKAsZqcdgGhZjr0= -github.com/metacubex/quic-go v0.42.1-0.20240319071510-a251e5c66a5c/go.mod h1:iGx3Y1zynls/FjFgykLSqDcM81U0IKePRTXEz5g3iiQ= +github.com/metacubex/quic-go v0.42.1-0.20240418003344-f006b5735d98 h1:oMLlJV4a9AylNo8ZLBNUiqZ02Vme6GLLHjuWJz8amSk= +github.com/metacubex/quic-go v0.42.1-0.20240418003344-f006b5735d98/go.mod h1:iGx3Y1zynls/FjFgykLSqDcM81U0IKePRTXEz5g3iiQ= github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764 h1:+czGKoynxYA90YaL3NlCAIJHnlqwoUlLWgmOhdm5ZU8= github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= -github.com/metacubex/sing-quic v0.0.0-20240409064816-c16ac6b1f4b4 h1:JB+BgUgQVicS1oGiw63c0xQWEAkUeuTylDy5WIaco7o= -github.com/metacubex/sing-quic v0.0.0-20240409064816-c16ac6b1f4b4/go.mod h1:WyY0zYxv+o+18R/Ece+QFontlgXoobKbNqbtYn2zjz8= +github.com/metacubex/sing-quic v0.0.0-20240418004036-814c531c378d h1:RAe0ND8J5SOPGI623oEXfaHKaaUrrzHx+U1DN9Awcco= +github.com/metacubex/sing-quic v0.0.0-20240418004036-814c531c378d/go.mod h1:WyY0zYxv+o+18R/Ece+QFontlgXoobKbNqbtYn2zjz8= github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ= github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A= @@ -122,8 +124,8 @@ github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 h1:AGyIB5 github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 h1:as/aO/fM8nv4W4pOr9EETP6kV/Oaujk3fUNyQSJK61c= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= -github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= -github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= +github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= +github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= 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= @@ -169,8 +171,8 @@ github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e h1:iGH0RMv2F github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e/go.mod h1:YbL4TKHRR6APYQv3U2RGfwLDpPYSyWz6oUlpISBEzBE= github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= -github.com/shirou/gopsutil/v3 v3.24.2 h1:kcR0erMbLg5/3LcInpw0X/rrPSqq4CDPyI6A6ZRC18Y= -github.com/shirou/gopsutil/v3 v3.24.2/go.mod h1:tSg/594BcA+8UdQU2XcW803GWYgdtauFFPgJCJKZlVk= +github.com/shirou/gopsutil/v3 v3.24.3 h1:eoUGJSmdfLzJ3mxIhmOAhgKEKgQkeOwKpz1NbhVnuPE= +github.com/shirou/gopsutil/v3 v3.24.3/go.mod h1:JpND7O217xa72ewWz9zN2eIIkPWsDN/3pl0H8Qt0uwg= 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= @@ -210,8 +212,8 @@ github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/ github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -github.com/zhangyunhao116/fastrand v0.3.0 h1:7bwe124xcckPulX6fxtr2lFdO2KQqaefdtbk+mqO/Ig= -github.com/zhangyunhao116/fastrand v0.3.0/go.mod h1:0v5KgHho0VE6HU192HnY15de/oDS8UrbBChIFjIhBtc= +github.com/zhangyunhao116/fastrand v0.4.0 h1:86QB6Y+GGgLZRFRDCjMmAS28QULwspK9sgL5d1Bx3H4= +github.com/zhangyunhao116/fastrand v0.4.0/go.mod h1:vIyo6EyBhjGKpZv6qVlkPl4JVAklpMM4DSKzbAkMguA= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ= go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= @@ -224,19 +226,19 @@ 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.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= -golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= +golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= 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.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -252,7 +254,6 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -263,8 +264,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= -golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= +golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= +golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= 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= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= @@ -274,5 +275,5 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= -lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +lukechampine.com/blake3 v1.2.2 h1:wEAbSg0IVU4ih44CVlpMqMZMpzr5hf/6aqodLlevd/w= +lukechampine.com/blake3 v1.2.2/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= From 2f8f139f7c344b8549d4a9605eaf54c983341101 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 24 Apr 2024 11:07:22 +0800 Subject: [PATCH 056/311] fix: wireguard can't be auto closed --- adapter/outbound/wireguard.go | 247 +++++++++++++++-------------- adapter/outbound/wireguard_test.go | 44 +++++ 2 files changed, 169 insertions(+), 122 deletions(-) create mode 100644 adapter/outbound/wireguard_test.go diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 1044c8ec..976f3959 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -38,9 +38,17 @@ type WireGuard struct { device *device.Device tunDevice wireguard.Device dialer proxydialer.SingDialer - init func(ctx context.Context) error resolver *dns.Resolver refP *refProxyAdapter + + initOk atomic.Bool + initMutex sync.Mutex + initErr error + option WireGuardOption + connectAddr M.Socksaddr + localPrefixes []netip.Prefix + + closeCh chan struct{} // for test } type WireGuardOption struct { @@ -141,19 +149,6 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { } runtime.SetFinalizer(outbound, closeWireGuard) - resolv := func(ctx context.Context, address M.Socksaddr) (netip.AddrPort, error) { - if address.Addr.IsValid() { - return address.AddrPort(), nil - } - udpAddr, err := resolveUDPAddrWithPrefer(ctx, "udp", address.String(), outbound.prefer) - if err != nil { - return netip.AddrPort{}, err - } - // net.ResolveUDPAddr maybe return 4in6 address, so unmap at here - addrPort := udpAddr.AddrPort() - return netip.AddrPortFrom(addrPort.Addr().Unmap(), addrPort.Port()), nil - } - var reserved [3]uint8 if len(option.Reserved) > 0 { if len(option.Reserved) != 3 { @@ -162,29 +157,28 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { copy(reserved[:], option.Reserved) } var isConnect bool - var connectAddr M.Socksaddr if len(option.Peers) < 2 { isConnect = true if len(option.Peers) == 1 { - connectAddr = option.Peers[0].Addr() + outbound.connectAddr = option.Peers[0].Addr() } else { - connectAddr = option.Addr() + outbound.connectAddr = option.Addr() } } - outbound.bind = wireguard.NewClientBind(context.Background(), wgSingErrorHandler{outbound.Name()}, outbound.dialer, isConnect, connectAddr.AddrPort(), reserved) + outbound.bind = wireguard.NewClientBind(context.Background(), wgSingErrorHandler{outbound.Name()}, outbound.dialer, isConnect, outbound.connectAddr.AddrPort(), reserved) - localPrefixes, err := option.Prefixes() + var err error + outbound.localPrefixes, err = option.Prefixes() if err != nil { return nil, err } - var privateKey string { bytes, err := base64.StdEncoding.DecodeString(option.PrivateKey) if err != nil { return nil, E.Cause(err, "decode private key") } - privateKey = hex.EncodeToString(bytes) + option.PrivateKey = hex.EncodeToString(bytes) } if len(option.Peers) > 0 { @@ -230,110 +224,16 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { option.PreSharedKey = hex.EncodeToString(bytes) } } - - var ( - initOk atomic.Bool - initMutex sync.Mutex - initErr error - ) - - outbound.init = func(ctx context.Context) error { - if initOk.Load() { - return nil - } - initMutex.Lock() - defer initMutex.Unlock() - // double check like sync.Once - if initOk.Load() { - return nil - } - if initErr != nil { - return initErr - } - - outbound.bind.ResetReservedForEndpoint() - ipcConf := "private_key=" + privateKey - if len(option.Peers) > 0 { - for i, peer := range option.Peers { - destination, err := resolv(ctx, peer.Addr()) - if err != nil { - // !!! do not set initErr here !!! - // let us can retry domain resolve in next time - return E.Cause(err, "resolve endpoint domain for peer ", i) - } - ipcConf += "\npublic_key=" + peer.PublicKey - ipcConf += "\nendpoint=" + destination.String() - if peer.PreSharedKey != "" { - ipcConf += "\npreshared_key=" + peer.PreSharedKey - } - for _, allowedIP := range peer.AllowedIPs { - ipcConf += "\nallowed_ip=" + allowedIP - } - if len(peer.Reserved) > 0 { - copy(reserved[:], option.Reserved) - outbound.bind.SetReservedForEndpoint(destination, reserved) - } - } - } else { - ipcConf += "\npublic_key=" + option.PublicKey - destination, err := resolv(ctx, connectAddr) - if err != nil { - // !!! do not set initErr here !!! - // let us can retry domain resolve in next time - return E.Cause(err, "resolve endpoint domain") - } - outbound.bind.SetConnectAddr(destination) - ipcConf += "\nendpoint=" + destination.String() - if option.PreSharedKey != "" { - ipcConf += "\npreshared_key=" + option.PreSharedKey - } - var has4, has6 bool - for _, address := range localPrefixes { - if address.Addr().Is4() { - has4 = true - } else { - has6 = true - } - } - if has4 { - ipcConf += "\nallowed_ip=0.0.0.0/0" - } - if has6 { - ipcConf += "\nallowed_ip=::/0" - } - } - - if option.PersistentKeepalive != 0 { - ipcConf += fmt.Sprintf("\npersistent_keepalive_interval=%d", option.PersistentKeepalive) - } - - if debug.Enabled { - log.SingLogger.Trace(fmt.Sprintf("[WG](%s) created wireguard ipc conf: \n %s", option.Name, ipcConf)) - } - err = outbound.device.IpcSet(ipcConf) - if err != nil { - initErr = E.Cause(err, "setup wireguard") - return initErr - } - - err = outbound.tunDevice.Start() - if err != nil { - initErr = err - return initErr - } - - initOk.Store(true) - return nil - } + outbound.option = option mtu := option.MTU if mtu == 0 { mtu = 1408 } - if len(localPrefixes) == 0 { + if len(outbound.localPrefixes) == 0 { return nil, E.New("missing local address") } - outbound.tunDevice, err = wireguard.NewStackDevice(localPrefixes, uint32(mtu)) + outbound.tunDevice, err = wireguard.NewStackDevice(outbound.localPrefixes, uint32(mtu)) if err != nil { return nil, E.Cause(err, "create WireGuard device") } @@ -347,7 +247,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { }, option.Workers) var has6 bool - for _, address := range localPrefixes { + for _, address := range outbound.localPrefixes { if !address.Addr().Unmap().Is4() { has6 = true break @@ -373,11 +273,117 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { return outbound, nil } +func (w *WireGuard) resolve(ctx context.Context, address M.Socksaddr) (netip.AddrPort, error) { + if address.Addr.IsValid() { + return address.AddrPort(), nil + } + udpAddr, err := resolveUDPAddrWithPrefer(ctx, "udp", address.String(), w.prefer) + if err != nil { + return netip.AddrPort{}, err + } + // net.ResolveUDPAddr maybe return 4in6 address, so unmap at here + addrPort := udpAddr.AddrPort() + return netip.AddrPortFrom(addrPort.Addr().Unmap(), addrPort.Port()), nil +} + +func (w *WireGuard) init(ctx context.Context) error { + if w.initOk.Load() { + return nil + } + w.initMutex.Lock() + defer w.initMutex.Unlock() + // double check like sync.Once + if w.initOk.Load() { + return nil + } + if w.initErr != nil { + return w.initErr + } + + w.bind.ResetReservedForEndpoint() + ipcConf := "private_key=" + w.option.PrivateKey + if len(w.option.Peers) > 0 { + for i, peer := range w.option.Peers { + destination, err := w.resolve(ctx, peer.Addr()) + if err != nil { + // !!! do not set initErr here !!! + // let us can retry domain resolve in next time + return E.Cause(err, "resolve endpoint domain for peer ", i) + } + ipcConf += "\npublic_key=" + peer.PublicKey + ipcConf += "\nendpoint=" + destination.String() + if peer.PreSharedKey != "" { + ipcConf += "\npreshared_key=" + peer.PreSharedKey + } + for _, allowedIP := range peer.AllowedIPs { + ipcConf += "\nallowed_ip=" + allowedIP + } + if len(peer.Reserved) > 0 { + var reserved [3]uint8 + copy(reserved[:], w.option.Reserved) + w.bind.SetReservedForEndpoint(destination, reserved) + } + } + } else { + ipcConf += "\npublic_key=" + w.option.PublicKey + destination, err := w.resolve(ctx, w.connectAddr) + if err != nil { + // !!! do not set initErr here !!! + // let us can retry domain resolve in next time + return E.Cause(err, "resolve endpoint domain") + } + w.bind.SetConnectAddr(destination) + ipcConf += "\nendpoint=" + destination.String() + if w.option.PreSharedKey != "" { + ipcConf += "\npreshared_key=" + w.option.PreSharedKey + } + var has4, has6 bool + for _, address := range w.localPrefixes { + if address.Addr().Is4() { + has4 = true + } else { + has6 = true + } + } + if has4 { + ipcConf += "\nallowed_ip=0.0.0.0/0" + } + if has6 { + ipcConf += "\nallowed_ip=::/0" + } + } + + if w.option.PersistentKeepalive != 0 { + ipcConf += fmt.Sprintf("\npersistent_keepalive_interval=%d", w.option.PersistentKeepalive) + } + + if debug.Enabled { + log.SingLogger.Trace(fmt.Sprintf("[WG](%s) created wireguard ipc conf: \n %s", w.option.Name, ipcConf)) + } + err := w.device.IpcSet(ipcConf) + if err != nil { + w.initErr = E.Cause(err, "setup wireguard") + return w.initErr + } + + err = w.tunDevice.Start() + if err != nil { + w.initErr = err + return w.initErr + } + + w.initOk.Store(true) + return nil +} + func closeWireGuard(w *WireGuard) { if w.device != nil { w.device.Close() } _ = common.Close(w.tunDevice) + if w.closeCh != nil { + close(w.closeCh) + } } func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { @@ -416,9 +422,6 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat if err = w.init(ctx); err != nil { return nil, err } - if err != nil { - return nil, err - } if (!metadata.Resolved() || w.resolver != nil) && metadata.Host != "" { r := resolver.DefaultResolver if w.resolver != nil { diff --git a/adapter/outbound/wireguard_test.go b/adapter/outbound/wireguard_test.go new file mode 100644 index 00000000..20dbdbdd --- /dev/null +++ b/adapter/outbound/wireguard_test.go @@ -0,0 +1,44 @@ +//go:build with_gvisor + +package outbound + +import ( + "context" + "runtime" + "testing" + "time" +) + +func TestWireGuardGC(t *testing.T) { + option := WireGuardOption{} + option.Server = "162.159.192.1" + option.Port = 2408 + option.PrivateKey = "iOx7749AdqH3IqluG7+0YbGKd0m1mcEXAfGRzpy9rG8=" + option.PublicKey = "bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=" + option.Ip = "172.16.0.2" + option.Ipv6 = "2606:4700:110:8d29:be92:3a6a:f4:c437" + option.Reserved = []uint8{51, 69, 125} + wg, err := NewWireGuard(option) + if err != nil { + t.Error(err) + } + closeCh := make(chan struct{}) + wg.closeCh = closeCh + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + err = wg.init(ctx) + if err != nil { + t.Error(err) + } + // must do a small sleep before test GC + // because it maybe deadlocks if w.device.Close call too fast after w.device.Start + time.Sleep(10 * time.Millisecond) + wg = nil + runtime.GC() + select { + case <-closeCh: + return + case <-ctx.Done(): + t.Error("timeout not GC") + } +} From 8ff56b5bb83c5c09d592ca22fb831135cf74efac Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Thu, 25 Apr 2024 11:48:53 +0800 Subject: [PATCH 057/311] chore: Add InUser for http/socks/mixed --- listener/http/proxy.go | 13 +++++++------ listener/socks/tcp.go | 6 ++++-- transport/socks4/socks4.go | 5 +++-- transport/socks5/socks5.go | 4 ++-- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/listener/http/proxy.go b/listener/http/proxy.go index 4822eabc..68e98708 100644 --- a/listener/http/proxy.go +++ b/listener/http/proxy.go @@ -51,8 +51,8 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool], var resp *http.Response if !trusted { - resp = authenticate(request, cache) - + resp, user := authenticate(request, cache) + additions = append(additions, inbound.WithInUser(user)) trusted = resp == nil } @@ -130,7 +130,7 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool], _ = conn.Close() } -func authenticate(request *http.Request, cache *lru.LruCache[string, bool]) *http.Response { +func authenticate(request *http.Request, cache *lru.LruCache[string, bool]) (resp *http.Response, u string) { authenticator := authStore.Authenticator() if inbound.SkipAuthRemoteAddress(request.RemoteAddr) { authenticator = nil @@ -140,23 +140,24 @@ func authenticate(request *http.Request, cache *lru.LruCache[string, bool]) *htt if credential == "" { resp := responseWith(request, http.StatusProxyAuthRequired) resp.Header.Set("Proxy-Authenticate", "Basic") - return resp + return resp, "" } authed, exist := cache.Get(credential) if !exist { user, pass, err := decodeBasicProxyAuthorization(credential) authed = err == nil && authenticator.Verify(user, pass) + u = user cache.Set(credential, authed) } if !authed { log.Infoln("Auth failed from %s", request.RemoteAddr) - return responseWith(request, http.StatusForbidden) + return responseWith(request, http.StatusForbidden), u } } - return nil + return nil, u } func responseWith(request *http.Request, statusCode int) *http.Response { diff --git a/listener/socks/tcp.go b/listener/socks/tcp.go index b6ea023a..f2696e3f 100644 --- a/listener/socks/tcp.go +++ b/listener/socks/tcp.go @@ -98,11 +98,12 @@ func HandleSocks4(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) if inbound.SkipAuthRemoteAddr(conn.RemoteAddr()) { authenticator = nil } - addr, _, err := socks4.ServerHandshake(conn, authenticator) + addr, _, user, err := socks4.ServerHandshake(conn, authenticator) if err != nil { conn.Close() return } + additions = append(additions, inbound.WithInUser(user)) tunnel.HandleTCPConn(inbound.NewSocket(socks5.ParseAddr(addr), conn, C.SOCKS4, additions...)) } @@ -111,7 +112,7 @@ func HandleSocks5(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) if inbound.SkipAuthRemoteAddr(conn.RemoteAddr()) { authenticator = nil } - target, command, err := socks5.ServerHandshake(conn, authenticator) + target, command, user, err := socks5.ServerHandshake(conn, authenticator) if err != nil { conn.Close() return @@ -121,5 +122,6 @@ func HandleSocks5(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) io.Copy(io.Discard, conn) return } + additions = append(additions, inbound.WithInUser(user)) tunnel.HandleTCPConn(inbound.NewSocket(target, conn, C.SOCKS5, additions...)) } diff --git a/transport/socks4/socks4.go b/transport/socks4/socks4.go index 9533a1c0..50708eda 100644 --- a/transport/socks4/socks4.go +++ b/transport/socks4/socks4.go @@ -43,7 +43,7 @@ var ( var subnet = netip.PrefixFrom(netip.IPv4Unspecified(), 24) -func ServerHandshake(rw io.ReadWriter, authenticator auth.Authenticator) (addr string, command Command, err error) { +func ServerHandshake(rw io.ReadWriter, authenticator auth.Authenticator) (addr string, command Command, user string, err error) { var req [8]byte if _, err = io.ReadFull(rw, req[:]); err != nil { return @@ -73,6 +73,7 @@ func ServerHandshake(rw io.ReadWriter, authenticator auth.Authenticator) (addr s if userID, err = readUntilNull(rw); err != nil { return } + user = string(userID) if isReservedIP(dstIP) { var target []byte @@ -90,7 +91,7 @@ func ServerHandshake(rw io.ReadWriter, authenticator auth.Authenticator) (addr s } // SOCKS4 only support USERID auth. - if authenticator == nil || authenticator.Verify(string(userID), "") { + if authenticator == nil || authenticator.Verify(user, "") { code = RequestGranted } else { code = RequestIdentdMismatched diff --git a/transport/socks5/socks5.go b/transport/socks5/socks5.go index c97c370c..61b555f4 100644 --- a/transport/socks5/socks5.go +++ b/transport/socks5/socks5.go @@ -106,7 +106,7 @@ type User struct { } // ServerHandshake fast-tracks SOCKS initialization to get target address to connect on server side. -func ServerHandshake(rw net.Conn, authenticator auth.Authenticator) (addr Addr, command Command, err error) { +func ServerHandshake(rw net.Conn, authenticator auth.Authenticator) (addr Addr, command Command, user string, err error) { // Read RFC 1928 for request and reply structure and sizes. buf := make([]byte, MaxAddrLen) // read VER, NMETHODS, METHODS @@ -141,7 +141,7 @@ func ServerHandshake(rw net.Conn, authenticator auth.Authenticator) (addr Addr, if _, err = io.ReadFull(rw, authBuf[:userLen]); err != nil { return } - user := string(authBuf[:userLen]) + user = string(authBuf[:userLen]) // Get password if _, err = rw.Read(header[:1]); err != nil { From df01582996d3c217a459f02a220d795f34b5e933 Mon Sep 17 00:00:00 2001 From: H1JK Date: Sat, 27 Apr 2024 01:05:34 +0800 Subject: [PATCH 058/311] fix: HTTP proxy variable shadowing --- listener/http/proxy.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/listener/http/proxy.go b/listener/http/proxy.go index 68e98708..f69c2b06 100644 --- a/listener/http/proxy.go +++ b/listener/http/proxy.go @@ -51,7 +51,8 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool], var resp *http.Response if !trusted { - resp, user := authenticate(request, cache) + var user string + resp, user = authenticate(request, cache) additions = append(additions, inbound.WithInUser(user)) trusted = resp == nil } From 89a097faa8f7242a108b3ea7a162d04dab77c864 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 28 Apr 2024 13:24:16 +0800 Subject: [PATCH 059/311] chore: update quic-go to 0.43.0 --- dns/doh.go | 2 +- go.mod | 4 ++-- go.sum | 10 ++++------ transport/hysteria/core/client.go | 2 +- transport/tuic/v4/packet.go | 2 +- transport/tuic/v5/packet.go | 4 ++-- 6 files changed, 11 insertions(+), 13 deletions(-) diff --git a/dns/doh.go b/dns/doh.go index ef4b653f..09d311b5 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -515,7 +515,7 @@ func (doh *dnsOverHTTPS) createTransportH3( }, DisableCompression: true, TLSClientConfig: tlsConfig, - QuicConfig: doh.getQUICConfig(), + QUICConfig: doh.getQUICConfig(), } return &http3Transport{baseTransport: rt}, nil diff --git a/go.mod b/go.mod index a8feb726..01d67032 100644 --- a/go.mod +++ b/go.mod @@ -19,8 +19,8 @@ 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.42.1-0.20240418003344-f006b5735d98 - github.com/metacubex/sing-quic v0.0.0-20240418004036-814c531c378d + github.com/metacubex/quic-go v0.43.1-0.20240428051621-a109abfb4cf6 + github.com/metacubex/sing-quic v0.0.0-20240428052223-bf4b8b6c1b22 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 github.com/metacubex/sing-tun v0.2.6 diff --git a/go.sum b/go.sum index 8cd6d29d..c250677c 100644 --- a/go.sum +++ b/go.sum @@ -78,8 +78,6 @@ github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I= 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-20240227161007-c728f5dd21c8 h1:V3plQrMHRWOB5zMm3yNqvBxDQVW1+/wHBSok5uPdmVs= -github.com/insomniacslk/dhcp v0.0.0-20240227161007-c728f5dd21c8/go.mod h1:izxuNQZeFrbx2nK2fAyN5iNUB34Fe9j0nK4PwLzAkKw= github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49 h1:/OuvSMGT9+xnyZ+7MZQ1zdngaCCAdPoSw8B/uurZ7pg= github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= @@ -106,12 +104,12 @@ 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-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= -github.com/metacubex/quic-go v0.42.1-0.20240418003344-f006b5735d98 h1:oMLlJV4a9AylNo8ZLBNUiqZ02Vme6GLLHjuWJz8amSk= -github.com/metacubex/quic-go v0.42.1-0.20240418003344-f006b5735d98/go.mod h1:iGx3Y1zynls/FjFgykLSqDcM81U0IKePRTXEz5g3iiQ= +github.com/metacubex/quic-go v0.43.1-0.20240428051621-a109abfb4cf6 h1:qqIZ8CjoxVJP38JU9ml1RkWJfaUd7QtmOk1vl7shuRY= +github.com/metacubex/quic-go v0.43.1-0.20240428051621-a109abfb4cf6/go.mod h1:uXHODgJFUfUnkkCMWLd5Er6L5QY/LFRZb9LD5jyyhsk= github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764 h1:+czGKoynxYA90YaL3NlCAIJHnlqwoUlLWgmOhdm5ZU8= github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= -github.com/metacubex/sing-quic v0.0.0-20240418004036-814c531c378d h1:RAe0ND8J5SOPGI623oEXfaHKaaUrrzHx+U1DN9Awcco= -github.com/metacubex/sing-quic v0.0.0-20240418004036-814c531c378d/go.mod h1:WyY0zYxv+o+18R/Ece+QFontlgXoobKbNqbtYn2zjz8= +github.com/metacubex/sing-quic v0.0.0-20240428052223-bf4b8b6c1b22 h1:LPiTgdUBxOeQqDmHn/W2gDb5W2C+JPWOf/mkRl9nfCM= +github.com/metacubex/sing-quic v0.0.0-20240428052223-bf4b8b6c1b22/go.mod h1:nfqibK+vkBtE6Ch8vYqrFTcaX4BC6TwKqNNl5rORll4= github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ= github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A= diff --git a/transport/hysteria/core/client.go b/transport/hysteria/core/client.go index 199fe0d4..97c9225e 100644 --- a/transport/hysteria/core/client.go +++ b/transport/hysteria/core/client.go @@ -406,7 +406,7 @@ func (c *quicPktConn) WriteTo(p []byte, addr string) error { if errors.As(err, &errSize) { // need to frag msg.MsgID = uint16(fastrand.Intn(0xFFFF)) + 1 // msgID must be > 0 when fragCount > 1 - fragMsgs := fragUDPMessage(msg, int(errSize.PeerMaxDatagramFrameSize)) + fragMsgs := fragUDPMessage(msg, int(errSize.MaxDatagramPayloadSize)) for _, fragMsg := range fragMsgs { msgBuf.Reset() _ = struc.Pack(&msgBuf, &fragMsg) diff --git a/transport/tuic/v4/packet.go b/transport/tuic/v4/packet.go index 8f5bb5b3..47484e78 100644 --- a/transport/tuic/v4/packet.go +++ b/transport/tuic/v4/packet.go @@ -123,7 +123,7 @@ func (q *quicStreamPacketConn) WaitReadFrom() (data []byte, put func(), addr net func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if q.udpRelayMode != common.QUIC && len(p) > q.maxUdpRelayPacketSize { - return 0, &quic.DatagramTooLargeError{PeerMaxDatagramFrameSize: int64(q.maxUdpRelayPacketSize)} + return 0, &quic.DatagramTooLargeError{MaxDatagramPayloadSize: int64(q.maxUdpRelayPacketSize)} } if q.closed { return 0, net.ErrClosed diff --git a/transport/tuic/v5/packet.go b/transport/tuic/v5/packet.go index 86f839a5..5608db81 100644 --- a/transport/tuic/v5/packet.go +++ b/transport/tuic/v5/packet.go @@ -137,7 +137,7 @@ func (q *quicStreamPacketConn) WaitReadFrom() (data []byte, put func(), addr net func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if len(p) > 0xffff { // uint16 max - return 0, &quic.DatagramTooLargeError{PeerMaxDatagramFrameSize: 0xffff} + return 0, &quic.DatagramTooLargeError{MaxDatagramPayloadSize: 0xffff} } if q.closed { return 0, net.ErrClosed @@ -189,7 +189,7 @@ func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro var tooLarge *quic.DatagramTooLargeError if errors.As(err, &tooLarge) { - err = fragWriteNative(q.quicConn, packet, buf, int(tooLarge.PeerMaxDatagramFrameSize)-PacketOverHead) + err = fragWriteNative(q.quicConn, packet, buf, int(tooLarge.MaxDatagramPayloadSize)-PacketOverHead) } if err != nil { return From 314c0bb34b27db978d63029d693eee02ef30a054 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 29 Apr 2024 12:14:11 +0800 Subject: [PATCH 060/311] fix: hy2 udp incompatible with quic-go 0.43.0 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 01d67032..dad18d9e 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.43.1-0.20240428051621-a109abfb4cf6 - github.com/metacubex/sing-quic v0.0.0-20240428052223-bf4b8b6c1b22 + github.com/metacubex/sing-quic v0.0.0-20240429040940-fa3a4ff2533e github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 github.com/metacubex/sing-tun v0.2.6 diff --git a/go.sum b/go.sum index c250677c..8c4836b2 100644 --- a/go.sum +++ b/go.sum @@ -108,8 +108,8 @@ github.com/metacubex/quic-go v0.43.1-0.20240428051621-a109abfb4cf6 h1:qqIZ8CjoxV github.com/metacubex/quic-go v0.43.1-0.20240428051621-a109abfb4cf6/go.mod h1:uXHODgJFUfUnkkCMWLd5Er6L5QY/LFRZb9LD5jyyhsk= github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764 h1:+czGKoynxYA90YaL3NlCAIJHnlqwoUlLWgmOhdm5ZU8= github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= -github.com/metacubex/sing-quic v0.0.0-20240428052223-bf4b8b6c1b22 h1:LPiTgdUBxOeQqDmHn/W2gDb5W2C+JPWOf/mkRl9nfCM= -github.com/metacubex/sing-quic v0.0.0-20240428052223-bf4b8b6c1b22/go.mod h1:nfqibK+vkBtE6Ch8vYqrFTcaX4BC6TwKqNNl5rORll4= +github.com/metacubex/sing-quic v0.0.0-20240429040940-fa3a4ff2533e h1:UW+yfzoLzjEhAoJ6A4mVnov3Aergi7uFM/h7ReNekKI= +github.com/metacubex/sing-quic v0.0.0-20240429040940-fa3a4ff2533e/go.mod h1:nfqibK+vkBtE6Ch8vYqrFTcaX4BC6TwKqNNl5rORll4= github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ= github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A= From 107e3e763009668b174a6ef4d36a4c06e3169930 Mon Sep 17 00:00:00 2001 From: Pylogmon Date: Tue, 30 Apr 2024 17:01:46 +0800 Subject: [PATCH 061/311] feat: Allow upgrade to latest release (#1235) --- hub/updater/updater.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hub/updater/updater.go b/hub/updater/updater.go index 02ff07ba..df5da3f4 100644 --- a/hub/updater/updater.go +++ b/hub/updater/updater.go @@ -16,7 +16,6 @@ import ( "time" mihomoHttp "github.com/metacubex/mihomo/component/http" - "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" @@ -52,6 +51,10 @@ func init() { if runtime.GOARCH == "amd64" && cpuid.CPU.X64Level() < 3 { amd64Compatible = "-compatible" } + if !strings.HasPrefix(C.Version, "alpha") { + baseURL = "https://github.com/MetaCubeX/mihomo/releases/latest/download/mihomo" + versionURL = "https://github.com/MetaCubeX/mihomo/releases/latest/download/version.txt" + } } type updateError struct { @@ -73,9 +76,9 @@ func Update(execPath string) (err error) { return err } - log.Infoln("current version %s, latest version %s", constant.Version, latestVersion) + log.Infoln("current version %s, latest version %s", C.Version, latestVersion) - if latestVersion == constant.Version { + if latestVersion == C.Version { err := &updateError{Message: "already using latest version"} return err } From 8861eaf9033db6d1db2da5f59962770884f92013 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 1 May 2024 09:41:04 +0800 Subject: [PATCH 062/311] chore: hysteria2 will only change remote port in hopLoop --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index dad18d9e..0769a212 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.43.1-0.20240428051621-a109abfb4cf6 - github.com/metacubex/sing-quic v0.0.0-20240429040940-fa3a4ff2533e + github.com/metacubex/sing-quic v0.0.0-20240501013754-2a2b0f262f9f github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 github.com/metacubex/sing-tun v0.2.6 diff --git a/go.sum b/go.sum index 8c4836b2..a4d90759 100644 --- a/go.sum +++ b/go.sum @@ -108,8 +108,8 @@ github.com/metacubex/quic-go v0.43.1-0.20240428051621-a109abfb4cf6 h1:qqIZ8CjoxV github.com/metacubex/quic-go v0.43.1-0.20240428051621-a109abfb4cf6/go.mod h1:uXHODgJFUfUnkkCMWLd5Er6L5QY/LFRZb9LD5jyyhsk= github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764 h1:+czGKoynxYA90YaL3NlCAIJHnlqwoUlLWgmOhdm5ZU8= github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= -github.com/metacubex/sing-quic v0.0.0-20240429040940-fa3a4ff2533e h1:UW+yfzoLzjEhAoJ6A4mVnov3Aergi7uFM/h7ReNekKI= -github.com/metacubex/sing-quic v0.0.0-20240429040940-fa3a4ff2533e/go.mod h1:nfqibK+vkBtE6Ch8vYqrFTcaX4BC6TwKqNNl5rORll4= +github.com/metacubex/sing-quic v0.0.0-20240501013754-2a2b0f262f9f h1:Xyd8SHaPHS3iX3xXPnus9PsYhJIo/2e7sJ6vTKLKUm8= +github.com/metacubex/sing-quic v0.0.0-20240501013754-2a2b0f262f9f/go.mod h1:nfqibK+vkBtE6Ch8vYqrFTcaX4BC6TwKqNNl5rORll4= github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ= github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A= From a2b43faa0ba8c959bb4e3ff8ad16abeee25148b7 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sat, 4 May 2024 18:41:08 +0800 Subject: [PATCH 063/311] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 975f1268..d38fbedd 100644 --- a/README.md +++ b/README.md @@ -98,4 +98,3 @@ API. This software is released under the GPL-3.0 license. -[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FMetaCubeX%2Fmihomo.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2FMetaCubeX%2Fmihomo?ref=badge_large) From 5dd883e790ef5525197fa2edfdcbba0627aa7fd2 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Mon, 6 May 2024 14:03:29 +0800 Subject: [PATCH 064/311] chore: Add use-system-hosts option --- component/resolver/host.go | 8 ++++++-- config/config.go | 28 ++++++++++++++++------------ hub/executor/executor.go | 1 + 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/component/resolver/host.go b/component/resolver/host.go index 4a429629..7e299a09 100644 --- a/component/resolver/host.go +++ b/component/resolver/host.go @@ -13,7 +13,10 @@ import ( "github.com/zhangyunhao116/fastrand" ) -var DisableSystemHosts, _ = strconv.ParseBool(os.Getenv("DISABLE_SYSTEM_HOSTS")) +var ( + DisableSystemHosts, _ = strconv.ParseBool(os.Getenv("DISABLE_SYSTEM_HOSTS")) + UseSystemHosts bool +) type Hosts struct { *trie.DomainTrie[HostValue] @@ -51,7 +54,8 @@ func (h *Hosts) Search(domain string, isDomain bool) (*HostValue, bool) { return &hostValue, false } - if !isDomain && !DisableSystemHosts { + + if !isDomain && !DisableSystemHosts && UseSystemHosts { addr, _ := lookupStaticHost(domain) if hostValue, err := NewHostValue(addr); err == nil { return &hostValue, true diff --git a/config/config.go b/config/config.go index f425092a..b76c40ff 100644 --- a/config/config.go +++ b/config/config.go @@ -114,6 +114,7 @@ type DNS struct { PreferH3 bool `yaml:"prefer-h3"` IPv6 bool `yaml:"ipv6"` IPv6Timeout uint `yaml:"ipv6-timeout"` + UseSystemHosts bool `yaml:"use-system-hosts"` NameServer []dns.NameServer `yaml:"nameserver"` Fallback []dns.NameServer `yaml:"fallback"` FallbackFilter FallbackFilter `yaml:"fallback-filter"` @@ -209,6 +210,7 @@ type RawDNS struct { IPv6 bool `yaml:"ipv6" json:"ipv6"` IPv6Timeout uint `yaml:"ipv6-timeout" json:"ipv6-timeout"` UseHosts bool `yaml:"use-hosts" json:"use-hosts"` + UseSystemHosts bool `yaml:"use-system-hosts" json:"use-system-hosts"` NameServer []string `yaml:"nameserver" json:"nameserver"` Fallback []string `yaml:"fallback" json:"fallback"` FallbackFilter RawFallbackFilter `yaml:"fallback-filter" json:"fallback-filter"` @@ -456,12 +458,13 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { Interval: 30, }, DNS: RawDNS{ - Enable: false, - IPv6: false, - UseHosts: true, - IPv6Timeout: 100, - EnhancedMode: C.DNSMapping, - FakeIPRange: "198.18.0.1/16", + Enable: false, + IPv6: false, + UseHosts: true, + UseSystemHosts: true, + IPv6Timeout: 100, + EnhancedMode: C.DNSMapping, + FakeIPRange: "198.18.0.1/16", FallbackFilter: RawFallbackFilter{ GeoIP: true, GeoIPCode: "CN", @@ -1285,12 +1288,13 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul } dnsCfg := &DNS{ - Enable: cfg.Enable, - Listen: cfg.Listen, - PreferH3: cfg.PreferH3, - IPv6Timeout: cfg.IPv6Timeout, - IPv6: cfg.IPv6, - EnhancedMode: cfg.EnhancedMode, + Enable: cfg.Enable, + Listen: cfg.Listen, + PreferH3: cfg.PreferH3, + IPv6Timeout: cfg.IPv6Timeout, + IPv6: cfg.IPv6, + UseSystemHosts: cfg.UseSystemHosts, + EnhancedMode: cfg.EnhancedMode, FallbackFilter: FallbackFilter{ IPCIDR: []netip.Prefix{}, GeoSite: []router.DomainMatcher{}, diff --git a/hub/executor/executor.go b/hub/executor/executor.go index c23eb9f3..56e71632 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -253,6 +253,7 @@ func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, gen resolver.DefaultResolver = r resolver.DefaultHostMapper = m resolver.DefaultLocalServer = dns.NewLocalServer(r, m) + resolver.UseSystemHosts = c.UseSystemHosts if pr.Invalid() { resolver.ProxyServerHostResolver = pr From 6d1c62bbf036e7a6c471a1bf0ca6fae27ffa48bd Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 8 May 2024 09:27:13 +0800 Subject: [PATCH 065/311] fix: shadowsocks uot not work with dialer-proxy --- adapter/outbound/shadowsocks.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 98932f0c..714c4a7d 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -166,12 +166,6 @@ func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Meta // ListenPacketWithDialer implements C.ProxyAdapter func (ss *ShadowSocks) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) { - if len(ss.option.DialerProxy) > 0 { - dialer, err = proxydialer.NewByName(ss.option.DialerProxy, dialer) - if err != nil { - return nil, err - } - } if ss.option.UDPOverTCP { tcpConn, err := ss.DialContextWithDialer(ctx, dialer, metadata) if err != nil { @@ -179,6 +173,12 @@ func (ss *ShadowSocks) ListenPacketWithDialer(ctx context.Context, dialer C.Dial } return ss.ListenPacketOnStreamConn(ctx, tcpConn, metadata) } + if len(ss.option.DialerProxy) > 0 { + dialer, err = proxydialer.NewByName(ss.option.DialerProxy, dialer) + if err != nil { + return nil, err + } + } addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ss.addr, ss.prefer) if err != nil { return nil, err From 619f34119efcf448cfb01ce085d1ba8f3bef1284 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 12 May 2024 00:10:51 +0800 Subject: [PATCH 066/311] action: add golang1.21 with special revert commit to work on Windows7 --- .github/workflows/build.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index df320d29..a588dc54 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -64,6 +64,12 @@ jobs: - { goos: android, goarch: arm, ndk: armv7a-linux-androideabi34, output: armv7 } - { goos: android, goarch: arm64, ndk: aarch64-linux-android34, output: arm64-v8 } + # Go 1.21 can revert commit `9e4385` to work on Windows 7 + # https://github.com/golang/go/issues/64622#issuecomment-1847475161 + - { goos: windows, goarch: '386', output: '386-go121', goversion: '1.21' } + - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible-go121, goversion: '1.21' } + - { goos: windows, goarch: amd64, goamd64: v3, output: amd64-go121, goversion: '1.21' } + # Go 1.20 is the last release that will run on any release of Windows 7, 8, Server 2008 and Server 2012. Go 1.21 will require at least Windows 10 or Server 2016. - { goos: windows, goarch: '386', output: '386-go120', goversion: '1.20' } - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, goversion: '1.20' } @@ -108,6 +114,13 @@ jobs: sudo tar zxf go1.21.5.linux-amd64-abi2.tar.gz -C /usr/local echo "/usr/local/go/bin" >> $GITHUB_PATH + # modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557 + - name: Set up Go1.21 for Windows + if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '1.21' }} + run: | + cd $(go env GOROOT) + curl https://github.com/golang/go/commit/9e43850a3298a9b8b1162ba0033d4c53f8637571.diff | patch --verbose -R -p 1 + - name: Set variables if: ${{github.ref_name=='Alpha'}} run: echo "VERSION=alpha-$(git rev-parse --short HEAD)" >> $GITHUB_ENV From b840eae4c60356e4b03f83e5f3a446eaff08c510 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 12 May 2024 12:36:48 +0800 Subject: [PATCH 067/311] fix: x509 error in windows7/8 --- component/ca/config.go | 3 --- component/ca/fix_windows.go | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 component/ca/fix_windows.go diff --git a/component/ca/config.go b/component/ca/config.go index d56809a1..9d002db6 100644 --- a/component/ca/config.go +++ b/component/ca/config.go @@ -67,9 +67,6 @@ func ResetCertificate() { } func getCertPool() *x509.CertPool { - if len(trustCerts) == 0 { - return nil - } if globalCertPool == nil { mutex.Lock() defer mutex.Unlock() diff --git a/component/ca/fix_windows.go b/component/ca/fix_windows.go new file mode 100644 index 00000000..00c894d8 --- /dev/null +++ b/component/ca/fix_windows.go @@ -0,0 +1,15 @@ +package ca + +import ( + "golang.org/x/sys/windows" +) + +func init() { + majorVersion, _, _ := windows.RtlGetNtVersionNumbers() + // crypto/x509: certificate validation in Windows fails to validate IP in SAN + // https://github.com/golang/go/issues/37176 + // As far as I can tell this is still the case on most older versions of Windows (but seems to be fixed in 10) + if majorVersion < 10 && len(_CaCertificates) > 0 { + DisableSystemCa = true + } +} From adf0ff588ff7ab1f6e76d4b869be7dc51fa4dfcc Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 12 May 2024 13:32:07 +0800 Subject: [PATCH 068/311] action: let golang1.22's build can work on windows7/8 --- ...def151adff1af707d82d28f55dba81ceb08e1.diff | 158 ++++++++++++++++++ .github/workflows/build.yml | 14 +- 2 files changed, 163 insertions(+), 9 deletions(-) create mode 100644 .github/693def151adff1af707d82d28f55dba81ceb08e1.diff diff --git a/.github/693def151adff1af707d82d28f55dba81ceb08e1.diff b/.github/693def151adff1af707d82d28f55dba81ceb08e1.diff new file mode 100644 index 00000000..ca41ec31 --- /dev/null +++ b/.github/693def151adff1af707d82d28f55dba81ceb08e1.diff @@ -0,0 +1,158 @@ +diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go +index 62738e2cb1a7d..d0dcc7cc71fc0 100644 +--- a/src/crypto/rand/rand.go ++++ b/src/crypto/rand/rand.go +@@ -15,7 +15,7 @@ import "io" + // available, /dev/urandom otherwise. + // On OpenBSD and macOS, Reader uses getentropy(2). + // On other Unix-like systems, Reader reads from /dev/urandom. +-// On Windows systems, Reader uses the RtlGenRandom API. ++// On Windows systems, Reader uses the ProcessPrng API. + // On JS/Wasm, Reader uses the Web Crypto API. + // On WASIP1/Wasm, Reader uses random_get from wasi_snapshot_preview1. + var Reader io.Reader +diff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go +index 6c0655c72b692..7380f1f0f1e6e 100644 +--- a/src/crypto/rand/rand_windows.go ++++ b/src/crypto/rand/rand_windows.go +@@ -15,11 +15,8 @@ func init() { Reader = &rngReader{} } + + type rngReader struct{} + +-func (r *rngReader) Read(b []byte) (n int, err error) { +- // RtlGenRandom only returns 1<<32-1 bytes at a time. We only read at +- // most 1<<31-1 bytes at a time so that this works the same on 32-bit +- // and 64-bit systems. +- if err := batched(windows.RtlGenRandom, 1<<31-1)(b); err != nil { ++func (r *rngReader) Read(b []byte) (int, error) { ++ if err := windows.ProcessPrng(b); err != nil { + return 0, err + } + return len(b), nil +diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go +index ab4ad2ec64108..5854ca60b5cef 100644 +--- a/src/internal/syscall/windows/syscall_windows.go ++++ b/src/internal/syscall/windows/syscall_windows.go +@@ -373,7 +373,7 @@ func ErrorLoadingGetTempPath2() error { + //sys DestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock + //sys CreateEvent(eventAttrs *SecurityAttributes, manualReset uint32, initialState uint32, name *uint16) (handle syscall.Handle, err error) = kernel32.CreateEventW + +-//sys RtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036 ++//sys ProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng + + type FILE_ID_BOTH_DIR_INFO struct { + NextEntryOffset uint32 +diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go +index e3f6d8d2a2208..5a587ad4f146c 100644 +--- a/src/internal/syscall/windows/zsyscall_windows.go ++++ b/src/internal/syscall/windows/zsyscall_windows.go +@@ -37,13 +37,14 @@ func errnoErr(e syscall.Errno) error { + } + + var ( +- modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll")) +- modiphlpapi = syscall.NewLazyDLL(sysdll.Add("iphlpapi.dll")) +- modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll")) +- modnetapi32 = syscall.NewLazyDLL(sysdll.Add("netapi32.dll")) +- modpsapi = syscall.NewLazyDLL(sysdll.Add("psapi.dll")) +- moduserenv = syscall.NewLazyDLL(sysdll.Add("userenv.dll")) +- modws2_32 = syscall.NewLazyDLL(sysdll.Add("ws2_32.dll")) ++ modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll")) ++ modbcryptprimitives = syscall.NewLazyDLL(sysdll.Add("bcryptprimitives.dll")) ++ modiphlpapi = syscall.NewLazyDLL(sysdll.Add("iphlpapi.dll")) ++ modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll")) ++ modnetapi32 = syscall.NewLazyDLL(sysdll.Add("netapi32.dll")) ++ modpsapi = syscall.NewLazyDLL(sysdll.Add("psapi.dll")) ++ moduserenv = syscall.NewLazyDLL(sysdll.Add("userenv.dll")) ++ modws2_32 = syscall.NewLazyDLL(sysdll.Add("ws2_32.dll")) + + procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges") + procDuplicateTokenEx = modadvapi32.NewProc("DuplicateTokenEx") +@@ -55,7 +56,7 @@ var ( + procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus") + procRevertToSelf = modadvapi32.NewProc("RevertToSelf") + procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation") +- procSystemFunction036 = modadvapi32.NewProc("SystemFunction036") ++ procProcessPrng = modbcryptprimitives.NewProc("ProcessPrng") + procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") + procCreateEventW = modkernel32.NewProc("CreateEventW") + procGetACP = modkernel32.NewProc("GetACP") +@@ -179,12 +180,12 @@ func SetTokenInformation(tokenHandle syscall.Token, tokenInformationClass uint32 + return + } + +-func RtlGenRandom(buf []byte) (err error) { ++func ProcessPrng(buf []byte) (err error) { + var _p0 *byte + if len(buf) > 0 { + _p0 = &buf[0] + } +- r1, _, e1 := syscall.Syscall(procSystemFunction036.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0) ++ r1, _, e1 := syscall.Syscall(procProcessPrng.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0) + if r1 == 0 { + err = errnoErr(e1) + } +diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go +index 8ca8d7790909e..3772a864b2ff4 100644 +--- a/src/runtime/os_windows.go ++++ b/src/runtime/os_windows.go +@@ -127,15 +127,8 @@ var ( + _WriteFile, + _ stdFunction + +- // Use RtlGenRandom to generate cryptographically random data. +- // This approach has been recommended by Microsoft (see issue +- // 15589 for details). +- // The RtlGenRandom is not listed in advapi32.dll, instead +- // RtlGenRandom function can be found by searching for SystemFunction036. +- // Also some versions of Mingw cannot link to SystemFunction036 +- // when building executable as Cgo. So load SystemFunction036 +- // manually during runtime startup. +- _RtlGenRandom stdFunction ++ // Use ProcessPrng to generate cryptographically random data. ++ _ProcessPrng stdFunction + + // Load ntdll.dll manually during startup, otherwise Mingw + // links wrong printf function to cgo executable (see issue +@@ -151,11 +144,11 @@ var ( + ) + + var ( +- advapi32dll = [...]uint16{'a', 'd', 'v', 'a', 'p', 'i', '3', '2', '.', 'd', 'l', 'l', 0} +- ntdlldll = [...]uint16{'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l', 0} +- powrprofdll = [...]uint16{'p', 'o', 'w', 'r', 'p', 'r', 'o', 'f', '.', 'd', 'l', 'l', 0} +- winmmdll = [...]uint16{'w', 'i', 'n', 'm', 'm', '.', 'd', 'l', 'l', 0} +- ws2_32dll = [...]uint16{'w', 's', '2', '_', '3', '2', '.', 'd', 'l', 'l', 0} ++ bcryptprimitivesdll = [...]uint16{'b', 'c', 'r', 'y', 'p', 't', 'p', 'r', 'i', 'm', 'i', 't', 'i', 'v', 'e', 's', '.', 'd', 'l', 'l', 0} ++ ntdlldll = [...]uint16{'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l', 0} ++ powrprofdll = [...]uint16{'p', 'o', 'w', 'r', 'p', 'r', 'o', 'f', '.', 'd', 'l', 'l', 0} ++ winmmdll = [...]uint16{'w', 'i', 'n', 'm', 'm', '.', 'd', 'l', 'l', 0} ++ ws2_32dll = [...]uint16{'w', 's', '2', '_', '3', '2', '.', 'd', 'l', 'l', 0} + ) + + // Function to be called by windows CreateThread +@@ -251,11 +244,11 @@ func windowsLoadSystemLib(name []uint16) uintptr { + } + + func loadOptionalSyscalls() { +- a32 := windowsLoadSystemLib(advapi32dll[:]) +- if a32 == 0 { +- throw("advapi32.dll not found") ++ bcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:]) ++ if bcryptPrimitives == 0 { ++ throw("bcryptprimitives.dll not found") + } +- _RtlGenRandom = windowsFindfunc(a32, []byte("SystemFunction036\000")) ++ _ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte("ProcessPrng\000")) + + n32 := windowsLoadSystemLib(ntdlldll[:]) + if n32 == 0 { +@@ -531,7 +524,7 @@ func osinit() { + //go:nosplit + func readRandom(r []byte) int { + n := 0 +- if stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { ++ if stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { + n = len(r) + } + return n \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a588dc54..4f36e40d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -64,12 +64,6 @@ jobs: - { goos: android, goarch: arm, ndk: armv7a-linux-androideabi34, output: armv7 } - { goos: android, goarch: arm64, ndk: aarch64-linux-android34, output: arm64-v8 } - # Go 1.21 can revert commit `9e4385` to work on Windows 7 - # https://github.com/golang/go/issues/64622#issuecomment-1847475161 - - { goos: windows, goarch: '386', output: '386-go121', goversion: '1.21' } - - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible-go121, goversion: '1.21' } - - { goos: windows, goarch: amd64, goamd64: v3, output: amd64-go121, goversion: '1.21' } - # Go 1.20 is the last release that will run on any release of Windows 7, 8, Server 2008 and Server 2012. Go 1.21 will require at least Windows 10 or Server 2016. - { goos: windows, goarch: '386', output: '386-go120', goversion: '1.20' } - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, goversion: '1.20' } @@ -115,11 +109,13 @@ jobs: echo "/usr/local/go/bin" >> $GITHUB_PATH # modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557 - - name: Set up Go1.21 for Windows - if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '1.21' }} + # this patch file only works on golang1.22.x + # that means after golang1.23 release it must be changed + - name: Revert Golang1.22 commit for Windows7/8 + if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '' }} run: | cd $(go env GOROOT) - curl https://github.com/golang/go/commit/9e43850a3298a9b8b1162ba0033d4c53f8637571.diff | patch --verbose -R -p 1 + patch --verbose -R -p 1 < $GITHUB_WORKSPACE/.github/693def151adff1af707d82d28f55dba81ceb08e1.diff - name: Set variables if: ${{github.ref_name=='Alpha'}} From fc82a32a483cc9bd2bc46ad6dee688b830c8db14 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 12 May 2024 15:52:10 +0800 Subject: [PATCH 069/311] fix: `system` tun stack not working in win7 --- go.mod | 2 +- go.sum | 4 ++-- listener/sing_tun/server.go | 4 ++++ listener/sing_tun/server_windows.go | 8 ++++++++ 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0769a212..904a9274 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240501013754-2a2b0f262f9f github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 - github.com/metacubex/sing-tun v0.2.6 + github.com/metacubex/sing-tun v0.2.7-0.20240512075008-89e7c6208eec github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 diff --git a/go.sum b/go.sum index a4d90759..ae199963 100644 --- a/go.sum +++ b/go.sum @@ -114,8 +114,8 @@ github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwV github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A= github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8= -github.com/metacubex/sing-tun v0.2.6 h1:frc58BqnIClqcC9KcYBfVAn5bgO6WW1ANKvZW3/HYAQ= -github.com/metacubex/sing-tun v0.2.6/go.mod h1:4VsMwZH1IlgPGFK1ZbBomZ/B2MYkTgs2+gnBAr5GOIo= +github.com/metacubex/sing-tun v0.2.7-0.20240512075008-89e7c6208eec h1:K4Wq3GOdLZ/xcqwyzAt4kmYQrjokyKQ3u/Xh5Yft14U= +github.com/metacubex/sing-tun v0.2.7-0.20240512075008-89e7c6208eec/go.mod h1:4VsMwZH1IlgPGFK1ZbBomZ/B2MYkTgs2+gnBAr5GOIo= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbTMBip/C+R79bvbfr42xH1gZl3uFb0RELdZiQ= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 h1:AGyIB55UfQm/0ZH0HtQO9u3l//yjtHUpjeRjjPGfGRI= diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 7cd9fc72..2dc6524e 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -20,12 +20,14 @@ import ( tun "github.com/metacubex/sing-tun" "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/control" E "github.com/sagernet/sing/common/exceptions" F "github.com/sagernet/sing/common/format" "github.com/sagernet/sing/common/ranges" ) var InterfaceName = "Meta" +var EnforceBindInterface = false type Listener struct { closed bool @@ -263,6 +265,8 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis UDPTimeout: udpTimeout, Handler: handler, Logger: log.SingLogger, + InterfaceFinder: control.DefaultInterfaceFinder(), + EnforceBindInterface: EnforceBindInterface, } if options.FileDescriptor > 0 { diff --git a/listener/sing_tun/server_windows.go b/listener/sing_tun/server_windows.go index 8da21287..4bc04835 100644 --- a/listener/sing_tun/server_windows.go +++ b/listener/sing_tun/server_windows.go @@ -6,6 +6,8 @@ import ( "github.com/metacubex/mihomo/log" tun "github.com/metacubex/sing-tun" + + "golang.org/x/sys/windows" ) func tunNew(options tun.Options) (tunIf tun.Tun, err error) { @@ -27,4 +29,10 @@ func tunNew(options tun.Options) (tunIf tun.Tun, err error) { func init() { tun.TunnelType = InterfaceName + + majorVersion, _, _ := windows.RtlGetNtVersionNumbers() + if majorVersion < 10 { + // to resolve "bind: The requested address is not valid in its context" + EnforceBindInterface = true + } } From 7df1c269427dcb6207e9741f20c72cf3b29de1b6 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 12 May 2024 19:34:25 +0800 Subject: [PATCH 070/311] fix: fingerprint passing --- adapter/adapter.go | 3 +++ adapter/outbound/shadowsocks.go | 1 + adapter/outbound/vmess.go | 2 ++ transport/trojan/trojan.go | 6 ++++++ 4 files changed, 12 insertions(+) diff --git a/adapter/adapter.go b/adapter/adapter.go index dbf3db6e..8136827a 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -2,6 +2,7 @@ package adapter import ( "context" + "crypto/tls" "encoding/json" "fmt" "net" @@ -14,6 +15,7 @@ import ( "github.com/metacubex/mihomo/common/atomic" "github.com/metacubex/mihomo/common/queue" "github.com/metacubex/mihomo/common/utils" + "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/dialer" C "github.com/metacubex/mihomo/constant" "github.com/puzpuzpuz/xsync/v3" @@ -230,6 +232,7 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In IdleConnTimeout: 90 * time.Second, TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, + TLSClientConfig: ca.GetGlobalTLSConfig(&tls.Config{}), } client := http.Client{ diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 714c4a7d..88fb8456 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -273,6 +273,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { if opts.TLS { v2rayOption.TLS = true v2rayOption.SkipCertVerify = opts.SkipCertVerify + v2rayOption.Fingerprint = opts.Fingerprint } } else if option.Plugin == shadowtls.Mode { obfsMode = shadowtls.Mode diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index c1c981ce..7d5a7224 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -179,6 +179,7 @@ func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M tlsOpts := mihomoVMess.TLSConfig{ Host: host, SkipCertVerify: v.option.SkipCertVerify, + FingerPrint: v.option.Fingerprint, NextProtos: []string{"h2"}, ClientFingerprint: v.option.ClientFingerprint, Reality: v.realityConfig, @@ -208,6 +209,7 @@ func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M tlsOpts := &mihomoVMess.TLSConfig{ Host: host, SkipCertVerify: v.option.SkipCertVerify, + FingerPrint: v.option.Fingerprint, ClientFingerprint: v.option.ClientFingerprint, Reality: v.realityConfig, NextProtos: v.option.ALPN, diff --git a/transport/trojan/trojan.go b/transport/trojan/trojan.go index 09be1124..17f403c1 100644 --- a/transport/trojan/trojan.go +++ b/transport/trojan/trojan.go @@ -129,6 +129,12 @@ func (t *Trojan) StreamWebsocketConn(ctx context.Context, conn net.Conn, wsOptio ServerName: t.option.ServerName, } + var err error + tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, t.option.Fingerprint) + if err != nil { + return nil, err + } + return vmess.StreamWebsocketConn(ctx, conn, &vmess.WebsocketConfig{ Host: wsOptions.Host, Port: wsOptions.Port, From a50339bd5f90ee5dcab2d00e5e35797bcc0b3c2d Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 12 May 2024 20:23:13 +0800 Subject: [PATCH 071/311] chore: swtich `RtlGetNtVersionNumbers` to `RtlGetVersion` https://go-review.googlesource.com/c/go/+/571015 --- component/ca/fix_windows.go | 5 ++--- constant/features/version.go | 5 +++++ constant/features/version_windows.go | 10 ++++++++++ listener/sing_tun/server_windows.go | 6 ++---- 4 files changed, 19 insertions(+), 7 deletions(-) create mode 100644 constant/features/version.go create mode 100644 constant/features/version_windows.go diff --git a/component/ca/fix_windows.go b/component/ca/fix_windows.go index 00c894d8..42511791 100644 --- a/component/ca/fix_windows.go +++ b/component/ca/fix_windows.go @@ -1,15 +1,14 @@ package ca import ( - "golang.org/x/sys/windows" + "github.com/metacubex/mihomo/constant/features" ) func init() { - majorVersion, _, _ := windows.RtlGetNtVersionNumbers() // crypto/x509: certificate validation in Windows fails to validate IP in SAN // https://github.com/golang/go/issues/37176 // As far as I can tell this is still the case on most older versions of Windows (but seems to be fixed in 10) - if majorVersion < 10 && len(_CaCertificates) > 0 { + if features.WindowsMajorVersion < 10 && len(_CaCertificates) > 0 { DisableSystemCa = true } } diff --git a/constant/features/version.go b/constant/features/version.go new file mode 100644 index 00000000..1710e218 --- /dev/null +++ b/constant/features/version.go @@ -0,0 +1,5 @@ +package features + +var WindowsMajorVersion uint32 +var WindowsMinorVersion uint32 +var WindowsBuildNumber uint32 diff --git a/constant/features/version_windows.go b/constant/features/version_windows.go new file mode 100644 index 00000000..192df950 --- /dev/null +++ b/constant/features/version_windows.go @@ -0,0 +1,10 @@ +package features + +import "golang.org/x/sys/windows" + +func init() { + version := windows.RtlGetVersion() + WindowsMajorVersion = version.MajorVersion + WindowsMinorVersion = version.MinorVersion + WindowsBuildNumber = version.MinorVersion +} diff --git a/listener/sing_tun/server_windows.go b/listener/sing_tun/server_windows.go index 4bc04835..325cf096 100644 --- a/listener/sing_tun/server_windows.go +++ b/listener/sing_tun/server_windows.go @@ -3,11 +3,10 @@ package sing_tun import ( "time" + "github.com/metacubex/mihomo/constant/features" "github.com/metacubex/mihomo/log" tun "github.com/metacubex/sing-tun" - - "golang.org/x/sys/windows" ) func tunNew(options tun.Options) (tunIf tun.Tun, err error) { @@ -30,8 +29,7 @@ func tunNew(options tun.Options) (tunIf tun.Tun, err error) { func init() { tun.TunnelType = InterfaceName - majorVersion, _, _ := windows.RtlGetNtVersionNumbers() - if majorVersion < 10 { + if features.WindowsMajorVersion < 10 { // to resolve "bind: The requested address is not valid in its context" EnforceBindInterface = true } From 3ae4014b393ea1dccb04f1f93c389cb1ab65b091 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 12 May 2024 20:44:12 +0800 Subject: [PATCH 072/311] chore: disable tfo when lower than Windows 10.0.14393 --- component/dialer/dialer.go | 2 +- component/dialer/tfo.go | 2 ++ component/dialer/tfo_windows.go | 11 +++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 component/dialer/tfo_windows.go diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 8fb5a0a6..c21e638e 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -164,7 +164,7 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po if opt.mpTcp { setMultiPathTCP(dialer) } - if opt.tfo { + if opt.tfo && !DisableTFO { return dialTFO(ctx, *dialer, network, address) } return dialer.DialContext(ctx, network, address) diff --git a/component/dialer/tfo.go b/component/dialer/tfo.go index 228e9689..76fe94d0 100644 --- a/component/dialer/tfo.go +++ b/component/dialer/tfo.go @@ -9,6 +9,8 @@ import ( "github.com/metacubex/tfo-go" ) +var DisableTFO = false + type tfoConn struct { net.Conn closed bool diff --git a/component/dialer/tfo_windows.go b/component/dialer/tfo_windows.go new file mode 100644 index 00000000..63266118 --- /dev/null +++ b/component/dialer/tfo_windows.go @@ -0,0 +1,11 @@ +package dialer + +import "github.com/metacubex/mihomo/constant/features" + +func init() { + // According to MSDN, this option is available since Windows 10, 1607 + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms738596(v=vs.85).aspx + if features.WindowsMajorVersion < 10 || (features.WindowsMajorVersion == 10 && features.WindowsBuildNumber < 14393) { + DisableTFO = true + } +} From fd7ecc004f8fdb018d9e61a2602d4d66e8c97f3f Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Mon, 13 May 2024 20:30:31 +0800 Subject: [PATCH 073/311] chore: Add filter for include-all-proxies --- adapter/outboundgroup/parser.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/adapter/outboundgroup/parser.go b/adapter/outboundgroup/parser.go index 876c92fa..941179e0 100644 --- a/adapter/outboundgroup/parser.go +++ b/adapter/outboundgroup/parser.go @@ -5,6 +5,8 @@ import ( "fmt" "strings" + "github.com/dlclark/regexp2" + "github.com/metacubex/mihomo/adapter/outbound" "github.com/metacubex/mihomo/adapter/provider" "github.com/metacubex/mihomo/common/structure" @@ -70,7 +72,22 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide groupOption.Use = append(groupOption.Use, AllProviders...) } if groupOption.IncludeAllProxies { - groupOption.Proxies = append(groupOption.Proxies, AllProxies...) + if groupOption.Filter != "" { + var filterRegs []*regexp2.Regexp + for _, filter := range strings.Split(groupOption.Filter, "`") { + filterReg := regexp2.MustCompile(filter, 0) + filterRegs = append(filterRegs, filterReg) + } + for _, p := range AllProxies { + for _, filterReg := range filterRegs { + if mat, _ := filterReg.FindStringMatch(p); mat != nil { + groupOption.Proxies = append(groupOption.Proxies, p) + } + } + } + } else { + groupOption.Proxies = append(groupOption.Proxies, AllProxies...) + } } if len(groupOption.Proxies) == 0 && len(groupOption.Use) == 0 { From 5da9ccaa98374f170e028e022bcf5fab881e31fb Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Wed, 15 May 2024 08:32:57 +0800 Subject: [PATCH 074/311] action: Upgrade loongarch golang version --- .github/workflows/build.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4f36e40d..1e2c9dc5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -94,18 +94,18 @@ jobs: with: go-version: ${{ matrix.jobs.goversion }} - - name: Set up Go1.21 loongarch abi1 + - name: Set up Go1.22 loongarch abi1 if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '1' }} run: | - wget -q https://github.com/xishang0128/loongarch64-golang/releases/download/1.21.5/go1.21.5.linux-amd64-abi1.tar.gz - sudo tar zxf go1.21.5.linux-amd64-abi1.tar.gz -C /usr/local + wget -q https://github.com/xishang0128/loongarch64-golang/releases/download/1.22.0/go1.22.0.linux-amd64-abi1.tar.gz + sudo tar zxf go1.22.0.linux-amd64-abi1.tar.gz -C /usr/local echo "/usr/local/go/bin" >> $GITHUB_PATH - - name: Set up Go1.21 loongarch abi2 + - name: Set up Go1.22 loongarch abi2 if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '2' }} run: | - wget -q https://github.com/xishang0128/loongarch64-golang/releases/download/1.21.5/go1.21.5.linux-amd64-abi2.tar.gz - sudo tar zxf go1.21.5.linux-amd64-abi2.tar.gz -C /usr/local + wget -q https://github.com/xishang0128/loongarch64-golang/releases/download/1.22.0/go1.22.0.linux-amd64-abi2.tar.gz + sudo tar zxf go1.22.0.linux-amd64-abi2.tar.gz -C /usr/local echo "/usr/local/go/bin" >> $GITHUB_PATH # modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557 From ed1e7e32c78f0b9a3a61786c9f2decbb022f69fd Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 15 May 2024 09:14:34 +0800 Subject: [PATCH 075/311] action: revert more golang1.22 commit for win7 --- ...42aa09c2f878c4faa576948b07fe625c4707a.diff | 54 ++++++ ...def151adff1af707d82d28f55dba81ceb08e1.diff | 0 ...157f9544922e96945196b47b95664b1e39108.diff | 162 ++++++++++++++++++ .github/workflows/build.yml | 22 ++- 4 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 .github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff rename .github/{ => patch_go122}/693def151adff1af707d82d28f55dba81ceb08e1.diff (100%) create mode 100644 .github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff diff --git a/.github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff b/.github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff new file mode 100644 index 00000000..2c682333 --- /dev/null +++ b/.github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff @@ -0,0 +1,54 @@ +diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go +index 06e684c7116b4..b311a5c74684b 100644 +--- a/src/syscall/exec_windows.go ++++ b/src/syscall/exec_windows.go +@@ -319,17 +319,6 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle + } + } + +- var maj, min, build uint32 +- rtlGetNtVersionNumbers(&maj, &min, &build) +- isWin7 := maj < 6 || (maj == 6 && min <= 1) +- // NT kernel handles are divisible by 4, with the bottom 3 bits left as +- // a tag. The fully set tag correlates with the types of handles we're +- // concerned about here. Except, the kernel will interpret some +- // special handle values, like -1, -2, and so forth, so kernelbase.dll +- // checks to see that those bottom three bits are checked, but that top +- // bit is not checked. +- isLegacyWin7ConsoleHandle := func(handle Handle) bool { return isWin7 && handle&0x10000003 == 3 } +- + p, _ := GetCurrentProcess() + parentProcess := p + if sys.ParentProcess != 0 { +@@ -338,15 +327,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle + fd := make([]Handle, len(attr.Files)) + for i := range attr.Files { + if attr.Files[i] > 0 { +- destinationProcessHandle := parentProcess +- +- // On Windows 7, console handles aren't real handles, and can only be duplicated +- // into the current process, not a parent one, which amounts to the same thing. +- if parentProcess != p && isLegacyWin7ConsoleHandle(Handle(attr.Files[i])) { +- destinationProcessHandle = p +- } +- +- err := DuplicateHandle(p, Handle(attr.Files[i]), destinationProcessHandle, &fd[i], 0, true, DUPLICATE_SAME_ACCESS) ++ err := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS) + if err != nil { + return 0, 0, err + } +@@ -377,14 +358,6 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle + + fd = append(fd, sys.AdditionalInheritedHandles...) + +- // On Windows 7, console handles aren't real handles, so don't pass them +- // through to PROC_THREAD_ATTRIBUTE_HANDLE_LIST. +- for i := range fd { +- if isLegacyWin7ConsoleHandle(fd[i]) { +- fd[i] = 0 +- } +- } +- + // The presence of a NULL handle in the list is enough to cause PROC_THREAD_ATTRIBUTE_HANDLE_LIST + // to treat the entire list as empty, so remove NULL handles. + j := 0 \ No newline at end of file diff --git a/.github/693def151adff1af707d82d28f55dba81ceb08e1.diff b/.github/patch_go122/693def151adff1af707d82d28f55dba81ceb08e1.diff similarity index 100% rename from .github/693def151adff1af707d82d28f55dba81ceb08e1.diff rename to .github/patch_go122/693def151adff1af707d82d28f55dba81ceb08e1.diff diff --git a/.github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff b/.github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff new file mode 100644 index 00000000..c1fc5f6d --- /dev/null +++ b/.github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff @@ -0,0 +1,162 @@ +diff --git a/src/net/hook_windows.go b/src/net/hook_windows.go +index ab8656cbbf343..28c49cc6de7e7 100644 +--- a/src/net/hook_windows.go ++++ b/src/net/hook_windows.go +@@ -14,7 +14,6 @@ var ( + testHookDialChannel = func() { time.Sleep(time.Millisecond) } // see golang.org/issue/5349 + + // Placeholders for socket system calls. +- socketFunc func(int, int, int) (syscall.Handle, error) = syscall.Socket + wsaSocketFunc func(int32, int32, int32, *syscall.WSAProtocolInfo, uint32, uint32) (syscall.Handle, error) = windows.WSASocket + connectFunc func(syscall.Handle, syscall.Sockaddr) error = syscall.Connect + listenFunc func(syscall.Handle, int) error = syscall.Listen +diff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go +index 0197feb3f199a..967ce6795aedb 100644 +--- a/src/net/internal/socktest/main_test.go ++++ b/src/net/internal/socktest/main_test.go +@@ -2,7 +2,7 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build !js && !plan9 && !wasip1 ++//go:build !js && !plan9 && !wasip1 && !windows + + package socktest_test + +diff --git a/src/net/internal/socktest/main_windows_test.go b/src/net/internal/socktest/main_windows_test.go +deleted file mode 100644 +index df1cb97784b51..0000000000000 +--- a/src/net/internal/socktest/main_windows_test.go ++++ /dev/null +@@ -1,22 +0,0 @@ +-// Copyright 2015 The Go Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style +-// license that can be found in the LICENSE file. +- +-package socktest_test +- +-import "syscall" +- +-var ( +- socketFunc func(int, int, int) (syscall.Handle, error) +- closeFunc func(syscall.Handle) error +-) +- +-func installTestHooks() { +- socketFunc = sw.Socket +- closeFunc = sw.Closesocket +-} +- +-func uninstallTestHooks() { +- socketFunc = syscall.Socket +- closeFunc = syscall.Closesocket +-} +diff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go +index 8c1c862f33c9b..1c42e5c7f34b7 100644 +--- a/src/net/internal/socktest/sys_windows.go ++++ b/src/net/internal/socktest/sys_windows.go +@@ -9,38 +9,6 @@ import ( + "syscall" + ) + +-// Socket wraps syscall.Socket. +-func (sw *Switch) Socket(family, sotype, proto int) (s syscall.Handle, err error) { +- sw.once.Do(sw.init) +- +- so := &Status{Cookie: cookie(family, sotype, proto)} +- sw.fmu.RLock() +- f, _ := sw.fltab[FilterSocket] +- sw.fmu.RUnlock() +- +- af, err := f.apply(so) +- if err != nil { +- return syscall.InvalidHandle, err +- } +- s, so.Err = syscall.Socket(family, sotype, proto) +- if err = af.apply(so); err != nil { +- if so.Err == nil { +- syscall.Closesocket(s) +- } +- return syscall.InvalidHandle, err +- } +- +- sw.smu.Lock() +- defer sw.smu.Unlock() +- if so.Err != nil { +- sw.stats.getLocked(so.Cookie).OpenFailed++ +- return syscall.InvalidHandle, so.Err +- } +- nso := sw.addLocked(s, family, sotype, proto) +- sw.stats.getLocked(nso.Cookie).Opened++ +- return s, nil +-} +- + // WSASocket wraps [syscall.WSASocket]. + func (sw *Switch) WSASocket(family, sotype, proto int32, protinfo *syscall.WSAProtocolInfo, group uint32, flags uint32) (s syscall.Handle, err error) { + sw.once.Do(sw.init) +diff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go +index 07f21b72eb1fc..bc024c0bbd82d 100644 +--- a/src/net/main_windows_test.go ++++ b/src/net/main_windows_test.go +@@ -8,7 +8,6 @@ import "internal/poll" + + var ( + // Placeholders for saving original socket system calls. +- origSocket = socketFunc + origWSASocket = wsaSocketFunc + origClosesocket = poll.CloseFunc + origConnect = connectFunc +@@ -18,7 +17,6 @@ var ( + ) + + func installTestHooks() { +- socketFunc = sw.Socket + wsaSocketFunc = sw.WSASocket + poll.CloseFunc = sw.Closesocket + connectFunc = sw.Connect +@@ -28,7 +26,6 @@ func installTestHooks() { + } + + func uninstallTestHooks() { +- socketFunc = origSocket + wsaSocketFunc = origWSASocket + poll.CloseFunc = origClosesocket + connectFunc = origConnect +diff --git a/src/net/sock_windows.go b/src/net/sock_windows.go +index fa11c7af2e727..5540135a2c43e 100644 +--- a/src/net/sock_windows.go ++++ b/src/net/sock_windows.go +@@ -19,21 +19,6 @@ func maxListenerBacklog() int { + func sysSocket(family, sotype, proto int) (syscall.Handle, error) { + s, err := wsaSocketFunc(int32(family), int32(sotype), int32(proto), + nil, 0, windows.WSA_FLAG_OVERLAPPED|windows.WSA_FLAG_NO_HANDLE_INHERIT) +- if err == nil { +- return s, nil +- } +- // WSA_FLAG_NO_HANDLE_INHERIT flag is not supported on some +- // old versions of Windows, see +- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms742212(v=vs.85).aspx +- // for details. Just use syscall.Socket, if windows.WSASocket failed. +- +- // See ../syscall/exec_unix.go for description of ForkLock. +- syscall.ForkLock.RLock() +- s, err = socketFunc(family, sotype, proto) +- if err == nil { +- syscall.CloseOnExec(s) +- } +- syscall.ForkLock.RUnlock() + if err != nil { + return syscall.InvalidHandle, os.NewSyscallError("socket", err) + } +diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go +index 0a93bc0a80d4e..06e684c7116b4 100644 +--- a/src/syscall/exec_windows.go ++++ b/src/syscall/exec_windows.go +@@ -14,6 +14,7 @@ import ( + "unsafe" + ) + ++// ForkLock is not used on Windows. + var ForkLock sync.RWMutex + + // EscapeArg rewrites command line argument s as prescribed \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1e2c9dc5..9556c7c3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -64,6 +64,13 @@ jobs: - { goos: android, goarch: arm, ndk: armv7a-linux-androideabi34, output: armv7 } - { goos: android, goarch: arm64, ndk: aarch64-linux-android34, output: arm64-v8 } + # Go 1.21 can revert commit `9e4385` to work on Windows 7 + # https://github.com/golang/go/issues/64622#issuecomment-1847475161 + # (OR we can just use golang1.21.4 which unneeded any patch) + - { goos: windows, goarch: '386', output: '386-go121', goversion: '1.21' } + - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible-go121, goversion: '1.21' } + - { goos: windows, goarch: amd64, goamd64: v3, output: amd64-go121, goversion: '1.21' } + # Go 1.20 is the last release that will run on any release of Windows 7, 8, Server 2008 and Server 2012. Go 1.21 will require at least Windows 10 or Server 2016. - { goos: windows, goarch: '386', output: '386-go120', goversion: '1.20' } - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, goversion: '1.20' } @@ -111,11 +118,24 @@ jobs: # modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557 # this patch file only works on golang1.22.x # that means after golang1.23 release it must be changed + # revert: + # 693def151adff1af707d82d28f55dba81ceb08e1: "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng" + # 7c1157f9544922e96945196b47b95664b1e39108: "net: remove sysSocket fallback for Windows 7" + # 48042aa09c2f878c4faa576948b07fe625c4707a: "syscall: remove Windows 7 console handle workaround" - name: Revert Golang1.22 commit for Windows7/8 if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '' }} run: | cd $(go env GOROOT) - patch --verbose -R -p 1 < $GITHUB_WORKSPACE/.github/693def151adff1af707d82d28f55dba81ceb08e1.diff + patch --verbose -R -p 1 < $GITHUB_WORKSPACE/.github/patch_go122/693def151adff1af707d82d28f55dba81ceb08e1.diff + patch --verbose -R -p 1 < $GITHUB_WORKSPACE/.github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff + patch --verbose -R -p 1 < $GITHUB_WORKSPACE/.github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff + + # modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557 + - name: Revert Golang1.21 commit for Windows7/8 + if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '1.21' }} + run: | + cd $(go env GOROOT) + curl https://github.com/golang/go/commit/9e43850a3298a9b8b1162ba0033d4c53f8637571.diff | patch --verbose -R -p 1 - name: Set variables if: ${{github.ref_name=='Alpha'}} From 1bc3c16b5938706fb8e5c576ecf62999fbd8ffc2 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 15 May 2024 10:44:56 +0800 Subject: [PATCH 076/311] feat: add `PROCESS-NAME-REGEX` and `PROCESS-PATH-REGEX` --- adapter/outboundgroup/groupbase.go | 10 +++++----- adapter/outboundgroup/parser.go | 4 ++-- adapter/provider/healthcheck.go | 4 ++-- adapter/provider/provider.go | 8 ++++---- constant/rule.go | 12 +++++++++--- rules/common/domain_regex.go | 11 ++++++----- rules/common/process.go | 30 ++++++++++++++++++++++++++++-- rules/parser.go | 8 ++++++-- 8 files changed, 62 insertions(+), 25 deletions(-) diff --git a/adapter/outboundgroup/groupbase.go b/adapter/outboundgroup/groupbase.go index b39ee3a6..69fbb617 100644 --- a/adapter/outboundgroup/groupbase.go +++ b/adapter/outboundgroup/groupbase.go @@ -48,7 +48,7 @@ type GroupBaseOption struct { func NewGroupBase(opt GroupBaseOption) *GroupBase { var excludeFilterReg *regexp2.Regexp if opt.excludeFilter != "" { - excludeFilterReg = regexp2.MustCompile(opt.excludeFilter, 0) + excludeFilterReg = regexp2.MustCompile(opt.excludeFilter, regexp2.None) } var excludeTypeArray []string if opt.excludeType != "" { @@ -58,7 +58,7 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase { var filterRegs []*regexp2.Regexp if opt.filter != "" { for _, filter := range strings.Split(opt.filter, "`") { - filterReg := regexp2.MustCompile(filter, 0) + filterReg := regexp2.MustCompile(filter, regexp2.None) filterRegs = append(filterRegs, filterReg) } } @@ -126,7 +126,7 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy { for _, filterReg := range gb.filterRegs { for _, p := range proxies { name := p.Name() - if mat, _ := filterReg.FindStringMatch(name); mat != nil { + if mat, _ := filterReg.MatchString(name); mat { if _, ok := proxiesSet[name]; !ok { proxiesSet[name] = struct{}{} newProxies = append(newProxies, p) @@ -150,7 +150,7 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy { for _, filterReg := range gb.filterRegs { for _, p := range proxies { name := p.Name() - if mat, _ := filterReg.FindStringMatch(name); mat != nil { + if mat, _ := filterReg.MatchString(name); mat { if _, ok := proxiesSet[name]; !ok { proxiesSet[name] = struct{}{} newProxies = append(newProxies, p) @@ -191,7 +191,7 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy { var newProxies []C.Proxy for _, p := range proxies { name := p.Name() - if mat, _ := gb.excludeFilterReg.FindStringMatch(name); mat != nil { + if mat, _ := gb.excludeFilterReg.MatchString(name); mat { continue } newProxies = append(newProxies, p) diff --git a/adapter/outboundgroup/parser.go b/adapter/outboundgroup/parser.go index 941179e0..67dc104d 100644 --- a/adapter/outboundgroup/parser.go +++ b/adapter/outboundgroup/parser.go @@ -75,12 +75,12 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide if groupOption.Filter != "" { var filterRegs []*regexp2.Regexp for _, filter := range strings.Split(groupOption.Filter, "`") { - filterReg := regexp2.MustCompile(filter, 0) + filterReg := regexp2.MustCompile(filter, regexp2.None) filterRegs = append(filterRegs, filterReg) } for _, p := range AllProxies { for _, filterReg := range filterRegs { - if mat, _ := filterReg.FindStringMatch(p); mat != nil { + if mat, _ := filterReg.MatchString(p); mat { groupOption.Proxies = append(groupOption.Proxies, p) } } diff --git a/adapter/provider/healthcheck.go b/adapter/provider/healthcheck.go index bfbcf8d9..8b5e8338 100644 --- a/adapter/provider/healthcheck.go +++ b/adapter/provider/healthcheck.go @@ -181,14 +181,14 @@ func (hc *HealthCheck) execute(b *batch.Batch[bool], url, uid string, option *ex filters = append(filters, filter) } - filterReg = regexp2.MustCompile(strings.Join(filters, "|"), 0) + filterReg = regexp2.MustCompile(strings.Join(filters, "|"), regexp2.None) } } for _, proxy := range hc.proxies { // skip proxies that do not require health check if filterReg != nil { - if match, _ := filterReg.FindStringMatch(proxy.Name()); match == nil { + if match, _ := filterReg.MatchString(proxy.Name()); !match { continue } } diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index a40a50ec..daef017c 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -169,7 +169,7 @@ func stopProxyProvider(pd *ProxySetProvider) { } func NewProxySetProvider(name string, interval time.Duration, filter string, excludeFilter string, excludeType string, dialerProxy string, override OverrideSchema, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) { - excludeFilterReg, err := regexp2.Compile(excludeFilter, 0) + excludeFilterReg, err := regexp2.Compile(excludeFilter, regexp2.None) if err != nil { return nil, fmt.Errorf("invalid excludeFilter regex: %w", err) } @@ -180,7 +180,7 @@ func NewProxySetProvider(name string, interval time.Duration, filter string, exc var filterRegs []*regexp2.Regexp for _, filter := range strings.Split(filter, "`") { - filterReg, err := regexp2.Compile(filter, 0) + filterReg, err := regexp2.Compile(filter, regexp2.None) if err != nil { return nil, fmt.Errorf("invalid filter regex: %w", err) } @@ -356,12 +356,12 @@ func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray continue } if len(excludeFilter) > 0 { - if mat, _ := excludeFilterReg.FindStringMatch(name); mat != nil { + if mat, _ := excludeFilterReg.MatchString(name); mat { continue } } if len(filter) > 0 { - if mat, _ := filterReg.FindStringMatch(name); mat == nil { + if mat, _ := filterReg.MatchString(name); !mat { continue } } diff --git a/constant/rule.go b/constant/rule.go index 8fdd75a6..161c200a 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -22,8 +22,10 @@ const ( InUser InName InType - Process + ProcessName ProcessPath + ProcessNameRegex + ProcessPathRegex RuleSet Network Uid @@ -76,10 +78,14 @@ func (rt RuleType) String() string { return "InName" case InType: return "InType" - case Process: - return "Process" + case ProcessName: + return "ProcessName" case ProcessPath: return "ProcessPath" + case ProcessNameRegex: + return "ProcessNameRegex" + case ProcessPathRegex: + return "ProcessPathRegex" case MATCH: return "Match" case RuleSet: diff --git a/rules/common/domain_regex.go b/rules/common/domain_regex.go index 3d961542..d214a772 100644 --- a/rules/common/domain_regex.go +++ b/rules/common/domain_regex.go @@ -1,14 +1,14 @@ package common import ( - "regexp" - C "github.com/metacubex/mihomo/constant" + + "github.com/dlclark/regexp2" ) type DomainRegex struct { *Base - regex *regexp.Regexp + regex *regexp2.Regexp adapter string } @@ -18,7 +18,8 @@ func (dr *DomainRegex) RuleType() C.RuleType { func (dr *DomainRegex) Match(metadata *C.Metadata) (bool, string) { domain := metadata.RuleHost() - return dr.regex.MatchString(domain), dr.adapter + match, _ := dr.regex.MatchString(domain) + return match, dr.adapter } func (dr *DomainRegex) Adapter() string { @@ -30,7 +31,7 @@ func (dr *DomainRegex) Payload() string { } func NewDomainRegex(regex string, adapter string) (*DomainRegex, error) { - r, err := regexp.Compile(regex) + r, err := regexp2.Compile(regex, regexp2.IgnoreCase) if err != nil { return nil, err } diff --git a/rules/common/process.go b/rules/common/process.go index ce643594..8932e946 100644 --- a/rules/common/process.go +++ b/rules/common/process.go @@ -4,6 +4,8 @@ import ( "strings" C "github.com/metacubex/mihomo/constant" + + "github.com/dlclark/regexp2" ) type Process struct { @@ -11,21 +13,36 @@ type Process struct { adapter string process string nameOnly bool + regexp *regexp2.Regexp } func (ps *Process) RuleType() C.RuleType { if ps.nameOnly { - return C.Process + if ps.regexp != nil { + return C.ProcessNameRegex + } + return C.ProcessName } + if ps.regexp != nil { + return C.ProcessPathRegex + } return C.ProcessPath } func (ps *Process) Match(metadata *C.Metadata) (bool, string) { if ps.nameOnly { + if ps.regexp != nil { + match, _ := ps.regexp.MatchString(metadata.Process) + return match, ps.adapter + } return strings.EqualFold(metadata.Process, ps.process), ps.adapter } + if ps.regexp != nil { + match, _ := ps.regexp.MatchString(metadata.ProcessPath) + return match, ps.adapter + } return strings.EqualFold(metadata.ProcessPath, ps.process), ps.adapter } @@ -41,11 +58,20 @@ func (ps *Process) ShouldFindProcess() bool { return true } -func NewProcess(process string, adapter string, nameOnly bool) (*Process, error) { +func NewProcess(process string, adapter string, nameOnly bool, regex bool) (*Process, error) { + var r *regexp2.Regexp + var err error + if regex { + r, err = regexp2.Compile(process, regexp2.IgnoreCase) + if err != nil { + return nil, err + } + } return &Process{ Base: &Base{}, adapter: adapter, process: process, nameOnly: nameOnly, + regexp: r, }, nil } diff --git a/rules/parser.go b/rules/parser.go index 032a02e4..9b1f5520 100644 --- a/rules/parser.go +++ b/rules/parser.go @@ -50,9 +50,13 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string] case "DSCP": parsed, parseErr = RC.NewDSCP(payload, target) case "PROCESS-NAME": - parsed, parseErr = RC.NewProcess(payload, target, true) + parsed, parseErr = RC.NewProcess(payload, target, true, false) case "PROCESS-PATH": - parsed, parseErr = RC.NewProcess(payload, target, false) + parsed, parseErr = RC.NewProcess(payload, target, false, false) + case "PROCESS-NAME-REGEX": + parsed, parseErr = RC.NewProcess(payload, target, true, true) + case "PROCESS-PATH-REGEX": + parsed, parseErr = RC.NewProcess(payload, target, false, true) case "NETWORK": parsed, parseErr = RC.NewNetworkType(payload, target) case "UID": From 87877d1b80587edb674fcd5bb65e7345eb871b18 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 15 May 2024 13:53:18 +0800 Subject: [PATCH 077/311] fix: don't ignore http.NewRequest's error --- component/tls/reality.go | 5 ++++- transport/simple-obfs/http.go | 7 +++++-- transport/vmess/http.go | 5 ++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/component/tls/reality.go b/component/tls/reality.go index 687ef1ef..7728b4b2 100644 --- a/component/tls/reality.go +++ b/component/tls/reality.go @@ -139,7 +139,10 @@ func realityClientFallback(uConn net.Conn, serverName string, fingerprint utls.C }, }, } - request, _ := http.NewRequest("GET", "https://"+serverName, nil) + request, err := http.NewRequest("GET", "https://"+serverName, nil) + if err != nil { + return + } request.Header.Set("User-Agent", fingerprint.Client) request.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", fastrand.Intn(32)+30)}) response, err := client.Do(request) diff --git a/transport/simple-obfs/http.go b/transport/simple-obfs/http.go index 681c1864..a38b1d69 100644 --- a/transport/simple-obfs/http.go +++ b/transport/simple-obfs/http.go @@ -65,7 +65,10 @@ func (ho *HTTPObfs) Write(b []byte) (int, error) { if ho.firstRequest { randBytes := make([]byte, 16) fastrand.Read(randBytes) - req, _ := http.NewRequest("GET", fmt.Sprintf("http://%s/", ho.host), bytes.NewBuffer(b[:])) + req, err := http.NewRequest("GET", fmt.Sprintf("http://%s/", ho.host), bytes.NewBuffer(b[:])) + if err != nil { + return 0, err + } req.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", fastrand.Int()%54, fastrand.Int()%2)) req.Header.Set("Upgrade", "websocket") req.Header.Set("Connection", "Upgrade") @@ -75,7 +78,7 @@ func (ho *HTTPObfs) Write(b []byte) (int, error) { } req.Header.Set("Sec-WebSocket-Key", base64.URLEncoding.EncodeToString(randBytes)) req.ContentLength = int64(len(b)) - err := req.Write(ho.Conn) + err = req.Write(ho.Conn) ho.firstRequest = false return len(b), err } diff --git a/transport/vmess/http.go b/transport/vmess/http.go index b023fee4..4a1b93ec 100644 --- a/transport/vmess/http.go +++ b/transport/vmess/http.go @@ -66,7 +66,10 @@ func (hc *httpConn) Write(b []byte) (int, error) { } u := fmt.Sprintf("http://%s%s", net.JoinHostPort(host, "80"), path) - req, _ := http.NewRequest(utils.EmptyOr(hc.cfg.Method, http.MethodGet), u, bytes.NewBuffer(b)) + req, err := http.NewRequest(utils.EmptyOr(hc.cfg.Method, http.MethodGet), u, bytes.NewBuffer(b)) + if err != nil { + return 0, err + } for key, list := range hc.cfg.Headers { req.Header.Set(key, list[fastrand.Intn(len(list))]) } From fe88f0e437fa7ea6a4b1614b902b3edb3acee2e0 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Wed, 15 May 2024 15:38:55 +0800 Subject: [PATCH 078/311] chore: Ensure that some expressions take effect --- config/config.go | 2 +- rules/provider/classical_strategy.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/config.go b/config/config.go index b76c40ff..311fd2e2 100644 --- a/config/config.go +++ b/config/config.go @@ -927,7 +927,7 @@ func parseRules(rulesConfig []string, proxies map[string]C.Proxy, subRules map[s l := len(rule) - if ruleName == "NOT" || ruleName == "OR" || ruleName == "AND" || ruleName == "SUB-RULE" || ruleName == "DOMAIN-REGEX" { + if ruleName == "NOT" || ruleName == "OR" || ruleName == "AND" || ruleName == "SUB-RULE" || ruleName == "DOMAIN-REGEX" || ruleName == "PROCESS-NAME-REGEX" || ruleName == "PROCESS-PATH-REGEX" { target = rule[l-1] payload = strings.Join(rule[1:l-1], ",") } else { diff --git a/rules/provider/classical_strategy.go b/rules/provider/classical_strategy.go index 6a2dccd5..8353ebce 100644 --- a/rules/provider/classical_strategy.go +++ b/rules/provider/classical_strategy.go @@ -77,7 +77,7 @@ func ruleParse(ruleRaw string) (string, string, []string) { } else if len(item) == 2 { return item[0], item[1], nil } else if len(item) > 2 { - if item[0] == "NOT" || item[0] == "OR" || item[0] == "AND" || item[0] == "SUB-RULE" || item[0] == "DOMAIN-REGEX" { + if item[0] == "NOT" || item[0] == "OR" || item[0] == "AND" || item[0] == "SUB-RULE" || item[0] == "DOMAIN-REGEX" || item[0] == "PROCESS-NAME-REGEX" || item[0] == "PROCESS-PATH-REGEX" { return item[0], strings.Join(item[1:len(item)], ","), nil } else { return item[0], item[1], item[2:] From 5c3a9b1dfcd4d115c7267781d1e1551f0df325a0 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Fri, 17 May 2024 11:49:09 +0800 Subject: [PATCH 079/311] fix: geo auto update #1261 --- .../updater/update_core.go | 2 +- {config => component/updater}/update_geo.go | 94 ++++++++++++++++++- {config => component/updater}/update_ui.go | 6 +- .../updater/utils.go | 23 +++++ config/config.go | 19 ++-- config/utils.go | 23 ----- hub/route/configs.go | 31 ++---- hub/route/upgrade.go | 10 +- main.go | 50 +--------- 9 files changed, 144 insertions(+), 114 deletions(-) rename hub/updater/updater.go => component/updater/update_core.go (99%) rename {config => component/updater}/update_geo.go (52%) rename {config => component/updater}/update_ui.go (97%) rename hub/updater/limitedreader.go => component/updater/utils.go (70%) diff --git a/hub/updater/updater.go b/component/updater/update_core.go similarity index 99% rename from hub/updater/updater.go rename to component/updater/update_core.go index df5da3f4..0070fbb1 100644 --- a/hub/updater/updater.go +++ b/component/updater/update_core.go @@ -67,7 +67,7 @@ func (e *updateError) Error() string { // Update performs the auto-updater. It returns an error if the updater failed. // If firstRun is true, it assumes the configuration file doesn't exist. -func Update(execPath string) (err error) { +func UpdateCore(execPath string) (err error) { mu.Lock() defer mu.Unlock() diff --git a/config/update_geo.go b/component/updater/update_geo.go similarity index 52% rename from config/update_geo.go rename to component/updater/update_geo.go index 43cac25c..a98d94dc 100644 --- a/config/update_geo.go +++ b/component/updater/update_geo.go @@ -1,18 +1,29 @@ -package config +package updater import ( + "errors" "fmt" + "os" "runtime" + "sync" + "time" + "github.com/metacubex/mihomo/common/atomic" "github.com/metacubex/mihomo/component/geodata" _ "github.com/metacubex/mihomo/component/geodata/standard" "github.com/metacubex/mihomo/component/mmdb" C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/log" "github.com/oschwald/maxminddb-golang" ) -func UpdateGeoDatabases() error { +var ( + updateGeoMux sync.Mutex + UpdatingGeo atomic.Bool +) + +func updateGeoDatabases() error { defer runtime.GC() geoLoader, err := geodata.GetGeoDataLoader("standard") if err != nil { @@ -88,3 +99,82 @@ func UpdateGeoDatabases() error { return nil } + +func UpdateGeoDatabases() error { + log.Infoln("[GEO] Start updating GEO database") + + updateGeoMux.Lock() + + if UpdatingGeo.Load() { + updateGeoMux.Unlock() + return errors.New("GEO database is updating, skip") + } + + UpdatingGeo.Store(true) + updateGeoMux.Unlock() + + defer func() { + UpdatingGeo.Store(false) + }() + + log.Infoln("[GEO] Updating GEO database") + + if err := updateGeoDatabases(); err != nil { + log.Errorln("[GEO] update GEO database error: %s", err.Error()) + return err + } + + return nil +} + +func getUpdateTime() (err error, time time.Time) { + var fileInfo os.FileInfo + if C.GeodataMode { + fileInfo, err = os.Stat(C.Path.GeoIP()) + if err != nil { + return err, time + } + } else { + fileInfo, err = os.Stat(C.Path.MMDB()) + if err != nil { + return err, time + } + } + + return nil, fileInfo.ModTime() +} + +func RegisterGeoUpdater() { + if C.GeoUpdateInterval <= 0 { + log.Errorln("[GEO] Invalid update interval: %d", C.GeoUpdateInterval) + return + } + + ticker := time.NewTicker(time.Duration(C.GeoUpdateInterval) * time.Hour) + defer ticker.Stop() + + log.Infoln("[GEO] update GEO database every %d hours", C.GeoUpdateInterval) + go func() { + err, lastUpdate := getUpdateTime() + if err != nil { + log.Errorln("[GEO] Get GEO database update time error: %s", err.Error()) + return + } + + log.Infoln("[GEO] last update time %s", lastUpdate) + if lastUpdate.Add(time.Duration(C.GeoUpdateInterval) * time.Hour).Before(time.Now()) { + log.Infoln("[GEO] Database has not been updated for %v, update now", time.Duration(C.GeoUpdateInterval)*time.Hour) + if err := UpdateGeoDatabases(); err != nil { + log.Errorln("[GEO] Failed to update GEO database: %s", err.Error()) + return + } + } + + for range ticker.C { + if err := UpdateGeoDatabases(); err != nil { + log.Errorln("[GEO] Failed to update GEO database: %s", err.Error()) + return + } + } + }() +} diff --git a/config/update_ui.go b/component/updater/update_ui.go similarity index 97% rename from config/update_ui.go rename to component/updater/update_ui.go index cff1d6d7..85452ba5 100644 --- a/config/update_ui.go +++ b/component/updater/update_ui.go @@ -1,4 +1,4 @@ -package config +package updater import ( "archive/zip" @@ -29,7 +29,7 @@ func UpdateUI() error { xdMutex.Lock() defer xdMutex.Unlock() - err := prepare() + err := prepare_ui() if err != nil { return err } @@ -64,7 +64,7 @@ func UpdateUI() error { return nil } -func prepare() error { +func prepare_ui() error { if ExternalUIPath == "" || ExternalUIURL == "" { return ErrIncompleteConf } diff --git a/hub/updater/limitedreader.go b/component/updater/utils.go similarity index 70% rename from hub/updater/limitedreader.go rename to component/updater/utils.go index c31db601..0eecfc6c 100644 --- a/hub/updater/limitedreader.go +++ b/component/updater/utils.go @@ -1,12 +1,35 @@ package updater import ( + "context" "fmt" "io" + "net/http" + "os" + "time" + + mihomoHttp "github.com/metacubex/mihomo/component/http" + C "github.com/metacubex/mihomo/constant" "golang.org/x/exp/constraints" ) +func downloadForBytes(url string) ([]byte, error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) + defer cancel() + resp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {C.UA}}, 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) +} + // LimitReachedError records the limit and the operation that caused it. type LimitReachedError struct { Limit int64 diff --git a/config/config.go b/config/config.go index 311fd2e2..9bc0afc8 100644 --- a/config/config.go +++ b/config/config.go @@ -28,6 +28,7 @@ import ( SNIFF "github.com/metacubex/mihomo/component/sniffer" tlsC "github.com/metacubex/mihomo/component/tls" "github.com/metacubex/mihomo/component/trie" + "github.com/metacubex/mihomo/component/updater" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/constant/features" providerTypes "github.com/metacubex/mihomo/constant/provider" @@ -640,28 +641,28 @@ func parseGeneral(cfg *RawConfig) (*General, error) { N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second } - ExternalUIPath = cfg.ExternalUI + updater.ExternalUIPath = cfg.ExternalUI // checkout externalUI exist - if ExternalUIPath != "" { - ExternalUIPath = C.Path.Resolve(ExternalUIPath) - if _, err := os.Stat(ExternalUIPath); os.IsNotExist(err) { + if updater.ExternalUIPath != "" { + updater.ExternalUIPath = C.Path.Resolve(updater.ExternalUIPath) + if _, err := os.Stat(updater.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) + log.Warnln("external-ui: %s does not exist, creating folder in %s", updater.ExternalUIPath, defaultUIpath) if err := os.MkdirAll(defaultUIpath, os.ModePerm); err != nil { return nil, err } - ExternalUIPath = defaultUIpath + updater.ExternalUIPath = defaultUIpath cfg.ExternalUI = defaultUIpath } } // checkout UIpath/name exist if cfg.ExternalUIName != "" { - ExternalUIName = cfg.ExternalUIName + updater.ExternalUIName = cfg.ExternalUIName } else { - ExternalUIFolder = ExternalUIPath + updater.ExternalUIFolder = updater.ExternalUIPath } if cfg.ExternalUIURL != "" { - ExternalUIURL = cfg.ExternalUIURL + updater.ExternalUIURL = cfg.ExternalUIURL } cfg.Tun.RedirectToTun = cfg.EBpf.RedirectToTun diff --git a/config/utils.go b/config/utils.go index 66bf3441..f87fb341 100644 --- a/config/utils.go +++ b/config/utils.go @@ -1,38 +1,15 @@ package config import ( - "context" "fmt" - "io" "net" - "net/http" "net/netip" - "os" "strings" - "time" "github.com/metacubex/mihomo/adapter/outboundgroup" "github.com/metacubex/mihomo/common/structure" - mihomoHttp "github.com/metacubex/mihomo/component/http" - C "github.com/metacubex/mihomo/constant" ) -func downloadForBytes(url string) ([]byte, error) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) - defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {C.UA}}, 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/hub/route/configs.go b/hub/route/configs.go index 653e4351..47fd26e0 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -4,11 +4,11 @@ import ( "net/http" "net/netip" "path/filepath" - "sync" "github.com/metacubex/mihomo/adapter/inbound" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/resolver" + "github.com/metacubex/mihomo/component/updater" "github.com/metacubex/mihomo/config" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/hub/executor" @@ -21,11 +21,6 @@ import ( "github.com/go-chi/render" ) -var ( - updateGeoMux sync.Mutex - updatingGeo = false -) - func configRouter() http.Handler { r := chi.NewRouter() r.Get("/", getConfigs) @@ -369,30 +364,20 @@ func updateConfigs(w http.ResponseWriter, r *http.Request) { } func updateGeoDatabases(w http.ResponseWriter, r *http.Request) { - updateGeoMux.Lock() - - if updatingGeo { - updateGeoMux.Unlock() + if updater.UpdatingGeo.Load() { render.Status(r, http.StatusBadRequest) render.JSON(w, r, newError("updating...")) return } - updatingGeo = true - updateGeoMux.Unlock() + err := updater.UpdateGeoDatabases() + if err != nil { + render.Status(r, http.StatusBadRequest) + render.JSON(w, r, newError(err.Error())) + return + } go func() { - defer func() { - updatingGeo = false - }() - - log.Warnln("[REST-API] updating GEO databases...") - - if err := config.UpdateGeoDatabases(); err != nil { - log.Errorln("[REST-API] update GEO databases failed: %v", err) - return - } - cfg, err := executor.ParseWithPath(C.Path.Config()) if err != nil { log.Errorln("[REST-API] update GEO databases failed: %v", err) diff --git a/hub/route/upgrade.go b/hub/route/upgrade.go index ea371798..db00af5c 100644 --- a/hub/route/upgrade.go +++ b/hub/route/upgrade.go @@ -6,8 +6,7 @@ import ( "net/http" "os" - "github.com/metacubex/mihomo/config" - "github.com/metacubex/mihomo/hub/updater" + "github.com/metacubex/mihomo/component/updater" "github.com/metacubex/mihomo/log" "github.com/go-chi/chi/v5" @@ -18,6 +17,7 @@ func upgradeRouter() http.Handler { r := chi.NewRouter() r.Post("/", upgradeCore) r.Post("/ui", updateUI) + r.Post("/geo", updateGeoDatabases) return r } @@ -31,7 +31,7 @@ func upgradeCore(w http.ResponseWriter, r *http.Request) { return } - err = updater.Update(execPath) + err = updater.UpdateCore(execPath) if err != nil { log.Warnln("%s", err) render.Status(r, http.StatusInternalServerError) @@ -48,9 +48,9 @@ func upgradeCore(w http.ResponseWriter, r *http.Request) { } func updateUI(w http.ResponseWriter, r *http.Request) { - err := config.UpdateUI() + err := updater.UpdateUI() if err != nil { - if errors.Is(err, config.ErrIncompleteConf) { + if errors.Is(err, updater.ErrIncompleteConf) { log.Warnln("%s", err) render.Status(r, http.StatusNotImplemented) render.JSON(w, r, newError(fmt.Sprintf("%s", err))) diff --git a/main.go b/main.go index afe9cfd2..1d16f8bf 100644 --- a/main.go +++ b/main.go @@ -8,10 +8,9 @@ import ( "path/filepath" "runtime" "strings" - "sync" "syscall" - "time" + "github.com/metacubex/mihomo/component/updater" "github.com/metacubex/mihomo/config" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/constant/features" @@ -32,8 +31,6 @@ var ( externalController string externalControllerUnix string secret string - updateGeoMux sync.Mutex - updatingGeo = false ) func init() { @@ -116,14 +113,7 @@ func main() { } if C.GeoAutoUpdate { - ticker := time.NewTicker(time.Duration(C.GeoUpdateInterval) * time.Hour) - - log.Infoln("[GEO] Start update GEO database every %d hours", C.GeoUpdateInterval) - go func() { - for range ticker.C { - updateGeoDatabases() - } - }() + updater.RegisterGeoUpdater() } defer executor.Shutdown() @@ -145,39 +135,3 @@ func main() { } } } - -func updateGeoDatabases() { - log.Infoln("[GEO] Start updating GEO database") - updateGeoMux.Lock() - - if updatingGeo { - updateGeoMux.Unlock() - log.Infoln("[GEO] GEO database is updating, skip") - return - } - - updatingGeo = true - updateGeoMux.Unlock() - - go func() { - defer func() { - updatingGeo = false - }() - - log.Infoln("[GEO] Updating GEO database") - - if err := config.UpdateGeoDatabases(); err != nil { - log.Errorln("[GEO] update GEO database error: %s", err.Error()) - return - } - - cfg, err := executor.ParseWithPath(C.Path.Config()) - if err != nil { - log.Errorln("[GEO] update GEO database failed: %s", err.Error()) - return - } - - log.Infoln("[GEO] Update GEO database success, apply new config") - executor.ApplyConfig(cfg, false) - }() -} From 2b52809d2c355b85f5460b589838d869b065fbad Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 18 May 2024 11:47:30 +0800 Subject: [PATCH 080/311] chore: update quic-go to 0.43.1 --- go.mod | 26 +++++++++++++------------- go.sum | 53 +++++++++++++++++++++++++++-------------------------- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/go.mod b/go.mod index 904a9274..c01dc643 100644 --- a/go.mod +++ b/go.mod @@ -12,15 +12,15 @@ require ( github.com/go-chi/chi/v5 v5.0.12 github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.3 - github.com/gobwas/ws v1.3.2 - github.com/gofrs/uuid/v5 v5.1.0 + github.com/gobwas/ws v1.4.0 + github.com/gofrs/uuid/v5 v5.2.0 github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49 github.com/klauspost/cpuid/v2 v2.2.7 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.43.1-0.20240428051621-a109abfb4cf6 - github.com/metacubex/sing-quic v0.0.0-20240501013754-2a2b0f262f9f + github.com/metacubex/quic-go v0.43.2-0.20240518033621-2c3d14c6b38e + github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 github.com/metacubex/sing-tun v0.2.7-0.20240512075008-89e7c6208eec @@ -40,21 +40,21 @@ require ( github.com/sagernet/utls v1.5.4 github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e github.com/samber/lo v1.39.0 - github.com/shirou/gopsutil/v3 v3.24.3 + github.com/shirou/gopsutil/v3 v3.24.4 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.9.0 github.com/wk8/go-ordered-map/v2 v2.1.8 github.com/zhangyunhao116/fastrand v0.4.0 go.uber.org/automaxprocs v1.5.3 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba - golang.org/x/crypto v0.22.0 - golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f - golang.org/x/net v0.24.0 + golang.org/x/crypto v0.23.0 + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 + golang.org/x/net v0.25.0 golang.org/x/sync v0.7.0 - golang.org/x/sys v0.19.0 - google.golang.org/protobuf v1.33.0 + golang.org/x/sys v0.20.0 + google.golang.org/protobuf v1.34.1 gopkg.in/yaml.v3 v3.0.1 - lukechampine.com/blake3 v1.2.2 + lukechampine.com/blake3 v1.3.0 ) require ( @@ -105,9 +105,9 @@ require ( gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect go.uber.org/mock v0.4.0 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/text v0.15.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.20.0 // indirect + golang.org/x/tools v0.21.0 // indirect ) replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764 diff --git a/go.sum b/go.sum index ae199963..41190ce8 100644 --- a/go.sum +++ b/go.sum @@ -60,10 +60,10 @@ github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.3.2 h1:zlnbNHxumkRvfPWgfXu8RBwyNR1x8wh9cf5PTOCqs9Q= -github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= -github.com/gofrs/uuid/v5 v5.1.0 h1:S5rqVKIigghZTCBKPCw0Y+bXkn26K3TB5mvQq2Ix8dk= -github.com/gofrs/uuid/v5 v5.1.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= +github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs= +github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc= +github.com/gofrs/uuid/v5 v5.2.0 h1:qw1GMx6/y8vhVsx626ImfKMuS5CvJmhIKKtuyvfajMM= +github.com/gofrs/uuid/v5 v5.2.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= @@ -104,12 +104,12 @@ 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-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= -github.com/metacubex/quic-go v0.43.1-0.20240428051621-a109abfb4cf6 h1:qqIZ8CjoxVJP38JU9ml1RkWJfaUd7QtmOk1vl7shuRY= -github.com/metacubex/quic-go v0.43.1-0.20240428051621-a109abfb4cf6/go.mod h1:uXHODgJFUfUnkkCMWLd5Er6L5QY/LFRZb9LD5jyyhsk= +github.com/metacubex/quic-go v0.43.2-0.20240518033621-2c3d14c6b38e h1:Nzwe08FNIJpExWpy9iXkG336dN/8nJqn69yijB7vJ8g= +github.com/metacubex/quic-go v0.43.2-0.20240518033621-2c3d14c6b38e/go.mod h1:uXHODgJFUfUnkkCMWLd5Er6L5QY/LFRZb9LD5jyyhsk= github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764 h1:+czGKoynxYA90YaL3NlCAIJHnlqwoUlLWgmOhdm5ZU8= github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= -github.com/metacubex/sing-quic v0.0.0-20240501013754-2a2b0f262f9f h1:Xyd8SHaPHS3iX3xXPnus9PsYhJIo/2e7sJ6vTKLKUm8= -github.com/metacubex/sing-quic v0.0.0-20240501013754-2a2b0f262f9f/go.mod h1:nfqibK+vkBtE6Ch8vYqrFTcaX4BC6TwKqNNl5rORll4= +github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 h1:Wr4g1HCb5Z/QIFwFiVNjO2qL+dRu25+Mdn9xtAZZ+ew= +github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8= github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ= github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A= @@ -169,8 +169,8 @@ github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e h1:iGH0RMv2F github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e/go.mod h1:YbL4TKHRR6APYQv3U2RGfwLDpPYSyWz6oUlpISBEzBE= github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= -github.com/shirou/gopsutil/v3 v3.24.3 h1:eoUGJSmdfLzJ3mxIhmOAhgKEKgQkeOwKpz1NbhVnuPE= -github.com/shirou/gopsutil/v3 v3.24.3/go.mod h1:JpND7O217xa72ewWz9zN2eIIkPWsDN/3pl0H8Qt0uwg= +github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU= +github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8= 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= @@ -222,18 +222,18 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= 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.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= 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.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -253,25 +253,26 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= -golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= +golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 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= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -lukechampine.com/blake3 v1.2.2 h1:wEAbSg0IVU4ih44CVlpMqMZMpzr5hf/6aqodLlevd/w= -lukechampine.com/blake3 v1.2.2/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= +lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= From 56edd8f671b84bee3f18d45e60b470f8a417a967 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sat, 18 May 2024 18:36:00 +0800 Subject: [PATCH 081/311] ci: better release --- .github/workflows/build.yml | 95 ++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 37 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9556c7c3..e3275679 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,10 @@ name: Build on: workflow_dispatch: + inputs: + version: + description: "Tag version to release" + required: true push: paths-ignore: - "docs/**" @@ -13,7 +17,6 @@ on: pull_request_target: branches: - Alpha - concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true @@ -138,13 +141,13 @@ jobs: curl https://github.com/golang/go/commit/9e43850a3298a9b8b1162ba0033d4c53f8637571.diff | patch --verbose -R -p 1 - name: Set variables - if: ${{github.ref_name=='Alpha'}} - run: echo "VERSION=alpha-$(git rev-parse --short HEAD)" >> $GITHUB_ENV + if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' }} + run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV shell: bash - name: Set variables - if: ${{github.ref_name=='' || github.ref_type=='tag'}} - run: echo "VERSION=$(git describe --tags)" >> $GITHUB_ENV + if: ${{ github.event_name != 'workflow_dispatch' && github.ref_name == 'Alpha' }} + run: echo "VERSION=alpha-$(git rev-parse --short HEAD)" >> $GITHUB_ENV shell: bash - name: Set Time Variable @@ -277,7 +280,7 @@ jobs: Upload-Prerelease: permissions: write-all - if: ${{ github.ref_type == 'branch' && !startsWith(github.event_name, 'pull_request') }} + if: ${{ github.event_name != 'workflow_dispatch' && github.ref_type == 'branch' && !startsWith(github.event_name, 'pull_request') }} needs: [build] runs-on: ubuntu-latest steps: @@ -328,44 +331,62 @@ jobs: Upload-Release: permissions: write-all - if: ${{ github.ref_type=='tag' }} + if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' }} needs: [build] runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 + - name: Checkout + uses: actions/checkout@v4 + with: + ref: Meta + fetch-depth: '0' + fetch-tags: 'true' - - name: Get tags - run: | - echo "CURRENTVERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV - git fetch --tags - echo "PREVERSION=$(git describe --tags --abbrev=0 HEAD^)" >> $GITHUB_ENV + - name: Get tags + run: | + echo "CURRENTVERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV + git fetch --tags + echo "PREVERSION=$(git describe --tags --abbrev=0 HEAD)" >> $GITHUB_ENV - - name: Generate release notes - run: | - cp ./.github/genReleaseNote.sh ./ - bash ./genReleaseNote.sh -v ${PREVERSION}...${CURRENTVERSION} - rm ./genReleaseNote.sh + - name: Merge Alpha branch into Meta + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git fetch origin Alpha:Alpha + git merge Alpha + git push origin Meta + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/download-artifact@v4 - with: - path: bin/ - merge-multiple: true + - name: Tag the commit + run: | + git tag ${{ github.event.inputs.version }} + git push origin ${{ github.event.inputs.version }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Display structure of downloaded files - run: ls -R - working-directory: bin - - - name: Upload Release - uses: softprops/action-gh-release@v1 - if: ${{ success() }} - with: - tag_name: ${{ github.ref_name }} - files: bin/* - generate_release_notes: true - body_path: release.md + - name: Generate release notes + run: | + cp ./.github/genReleaseNote.sh ./ + bash ./genReleaseNote.sh -v ${PREVERSION}...${CURRENTVERSION} + rm ./genReleaseNote.sh + + - uses: actions/download-artifact@v4 + with: + path: bin/ + merge-multiple: true + + - name: Display structure of downloaded files + run: ls -R + working-directory: bin + + - name: Upload Release + uses: softprops/action-gh-release@v2 + if: ${{ success() }} + with: + tag_name: ${{ github.event.inputs.version }} + files: bin/* + body_path: release.md Docker: if: ${{ !startsWith(github.event_name, 'pull_request') }} From 00e361c5acfa4fc31562ccc0b4d9c911843bf53f Mon Sep 17 00:00:00 2001 From: hunshcn Date: Sat, 18 May 2024 20:16:53 +0800 Subject: [PATCH 082/311] chore: stop using go:linkname for http.registerOnHitEOF, http.requestBodyRemains (#1275) relate to https://github.com/MetaCubeX/mihomo/pull/952#issuecomment-2118639385 --- listener/http/proxy.go | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/listener/http/proxy.go b/listener/http/proxy.go index f69c2b06..c77f9230 100644 --- a/listener/http/proxy.go +++ b/listener/http/proxy.go @@ -8,7 +8,6 @@ import ( "net/http" "strings" "sync" - _ "unsafe" "github.com/metacubex/mihomo/adapter/inbound" "github.com/metacubex/mihomo/common/lru" @@ -18,11 +17,19 @@ import ( "github.com/metacubex/mihomo/log" ) -//go:linkname registerOnHitEOF net/http.registerOnHitEOF -func registerOnHitEOF(rc io.ReadCloser, fn func()) +type bodyWrapper struct { + io.ReadCloser + once sync.Once + onHitEOF func() +} -//go:linkname requestBodyRemains net/http.requestBodyRemains -func requestBodyRemains(rc io.ReadCloser) bool +func (b *bodyWrapper) Read(p []byte) (n int, err error) { + n, err = b.ReadCloser.Read(p) + if err == io.EOF && b.onHitEOF != nil { + b.once.Do(b.onHitEOF) + } + return n, err +} func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool], additions ...inbound.Addition) { client := newClient(c, tunnel, additions...) @@ -100,10 +107,10 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool], } }() } - if requestBodyRemains(request.Body) { - registerOnHitEOF(request.Body, startBackgroundRead) - } else { + if request.Body == nil || request.Body == http.NoBody { startBackgroundRead() + } else { + request.Body = &bodyWrapper{ReadCloser: request.Body, onHitEOF: startBackgroundRead} } resp, err = client.Do(request) if err != nil { From 30a913aad62f2986799f0e58448b601f6fe28039 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 18 May 2024 20:45:15 +0800 Subject: [PATCH 083/311] chore: stop using go:linkname for net.lookupStaticHost --- component/resolver/host.go | 8 +- component/resolver/hosts/hosts.go | 309 ++++++++++++++++++++++ component/resolver/hosts/hosts_windows.go | 13 + 3 files changed, 324 insertions(+), 6 deletions(-) create mode 100644 component/resolver/hosts/hosts.go create mode 100644 component/resolver/hosts/hosts_windows.go diff --git a/component/resolver/host.go b/component/resolver/host.go index 7e299a09..53fa5924 100644 --- a/component/resolver/host.go +++ b/component/resolver/host.go @@ -9,6 +9,7 @@ import ( _ "unsafe" "github.com/metacubex/mihomo/common/utils" + "github.com/metacubex/mihomo/component/resolver/hosts" "github.com/metacubex/mihomo/component/trie" "github.com/zhangyunhao116/fastrand" ) @@ -28,11 +29,6 @@ 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) { if value := h.DomainTrie.Search(domain); value != nil { @@ -56,7 +52,7 @@ func (h *Hosts) Search(domain string, isDomain bool) (*HostValue, bool) { } if !isDomain && !DisableSystemHosts && UseSystemHosts { - addr, _ := lookupStaticHost(domain) + addr, _ := hosts.LookupStaticHost(domain) if hostValue, err := NewHostValue(addr); err == nil { return &hostValue, true } diff --git a/component/resolver/hosts/hosts.go b/component/resolver/hosts/hosts.go new file mode 100644 index 00000000..780ebb24 --- /dev/null +++ b/component/resolver/hosts/hosts.go @@ -0,0 +1,309 @@ +package hosts + +// this file copy and modify from golang's std net/hosts.go + +import ( + "errors" + "io" + "io/fs" + "net/netip" + "os" + "strings" + "sync" + "time" +) + +var hostsFilePath = "/etc/hosts" + +const cacheMaxAge = 5 * time.Second + +func parseLiteralIP(addr string) string { + ip, err := netip.ParseAddr(addr) + if err != nil { + return "" + } + return ip.String() +} + +type byName struct { + addrs []string + canonicalName string +} + +// hosts contains known host entries. +var hosts struct { + sync.Mutex + + // Key for the list of literal IP addresses must be a host + // name. It would be part of DNS labels, a FQDN or an absolute + // FQDN. + // For now the key is converted to lower case for convenience. + byName map[string]byName + + // Key for the list of host names must be a literal IP address + // including IPv6 address with zone identifier. + // We don't support old-classful IP address notation. + byAddr map[string][]string + + expire time.Time + path string + mtime time.Time + size int64 +} + +func readHosts() { + now := time.Now() + hp := hostsFilePath + + if now.Before(hosts.expire) && hosts.path == hp && len(hosts.byName) > 0 { + return + } + mtime, size, err := stat(hp) + if err == nil && hosts.path == hp && hosts.mtime.Equal(mtime) && hosts.size == size { + hosts.expire = now.Add(cacheMaxAge) + return + } + + hs := make(map[string]byName) + is := make(map[string][]string) + + file, err := open(hp) + if err != nil { + if !errors.Is(err, fs.ErrNotExist) && !errors.Is(err, fs.ErrPermission) { + return + } + } + + if file != nil { + defer file.close() + for line, ok := file.readLine(); ok; line, ok = file.readLine() { + if i := strings.IndexByte(line, '#'); i >= 0 { + // Discard comments. + line = line[0:i] + } + f := getFields(line) + if len(f) < 2 { + continue + } + addr := parseLiteralIP(f[0]) + if addr == "" { + continue + } + + var canonical string + for i := 1; i < len(f); i++ { + name := absDomainName(f[i]) + h := []byte(f[i]) + lowerASCIIBytes(h) + key := absDomainName(string(h)) + + if i == 1 { + canonical = key + } + + is[addr] = append(is[addr], name) + + if v, ok := hs[key]; ok { + hs[key] = byName{ + addrs: append(v.addrs, addr), + canonicalName: v.canonicalName, + } + continue + } + + hs[key] = byName{ + addrs: []string{addr}, + canonicalName: canonical, + } + } + } + } + // Update the data cache. + hosts.expire = now.Add(cacheMaxAge) + hosts.path = hp + hosts.byName = hs + hosts.byAddr = is + hosts.mtime = mtime + hosts.size = size +} + +// LookupStaticHost looks up the addresses and the canonical name for the given host from /etc/hosts. +func LookupStaticHost(host string) ([]string, string) { + hosts.Lock() + defer hosts.Unlock() + readHosts() + if len(hosts.byName) != 0 { + if hasUpperCase(host) { + lowerHost := []byte(host) + lowerASCIIBytes(lowerHost) + host = string(lowerHost) + } + if byName, ok := hosts.byName[absDomainName(host)]; ok { + ipsCp := make([]string, len(byName.addrs)) + copy(ipsCp, byName.addrs) + return ipsCp, byName.canonicalName + } + } + return nil, "" +} + +// LookupStaticAddr looks up the hosts for the given address from /etc/hosts. +func LookupStaticAddr(addr string) []string { + hosts.Lock() + defer hosts.Unlock() + readHosts() + addr = parseLiteralIP(addr) + if addr == "" { + return nil + } + if len(hosts.byAddr) != 0 { + if hosts, ok := hosts.byAddr[addr]; ok { + hostsCp := make([]string, len(hosts)) + copy(hostsCp, hosts) + return hostsCp + } + } + return nil +} + +func stat(name string) (mtime time.Time, size int64, err error) { + st, err := os.Stat(name) + if err != nil { + return time.Time{}, 0, err + } + return st.ModTime(), st.Size(), nil +} + +type file struct { + file *os.File + data []byte + atEOF bool +} + +func (f *file) close() { f.file.Close() } + +func (f *file) getLineFromData() (s string, ok bool) { + data := f.data + i := 0 + for i = 0; i < len(data); i++ { + if data[i] == '\n' { + s = string(data[0:i]) + ok = true + // move data + i++ + n := len(data) - i + copy(data[0:], data[i:]) + f.data = data[0:n] + return + } + } + if f.atEOF && len(f.data) > 0 { + // EOF, return all we have + s = string(data) + f.data = f.data[0:0] + ok = true + } + return +} + +func (f *file) readLine() (s string, ok bool) { + if s, ok = f.getLineFromData(); ok { + return + } + if len(f.data) < cap(f.data) { + ln := len(f.data) + n, err := io.ReadFull(f.file, f.data[ln:cap(f.data)]) + if n >= 0 { + f.data = f.data[0 : ln+n] + } + if err == io.EOF || err == io.ErrUnexpectedEOF { + f.atEOF = true + } + } + s, ok = f.getLineFromData() + return +} + +func (f *file) stat() (mtime time.Time, size int64, err error) { + st, err := f.file.Stat() + if err != nil { + return time.Time{}, 0, err + } + return st.ModTime(), st.Size(), nil +} + +func open(name string) (*file, error) { + fd, err := os.Open(name) + if err != nil { + return nil, err + } + return &file{fd, make([]byte, 0, 64*1024), false}, nil +} + +func getFields(s string) []string { return splitAtBytes(s, " \r\t\n") } + +// Count occurrences in s of any bytes in t. +func countAnyByte(s string, t string) int { + n := 0 + for i := 0; i < len(s); i++ { + if strings.IndexByte(t, s[i]) >= 0 { + n++ + } + } + return n +} + +// Split s at any bytes in t. +func splitAtBytes(s string, t string) []string { + a := make([]string, 1+countAnyByte(s, t)) + n := 0 + last := 0 + for i := 0; i < len(s); i++ { + if strings.IndexByte(t, s[i]) >= 0 { + if last < i { + a[n] = s[last:i] + n++ + } + last = i + 1 + } + } + if last < len(s) { + a[n] = s[last:] + n++ + } + return a[0:n] +} + +// lowerASCIIBytes makes x ASCII lowercase in-place. +func lowerASCIIBytes(x []byte) { + for i, b := range x { + if 'A' <= b && b <= 'Z' { + x[i] += 'a' - 'A' + } + } +} + +// hasUpperCase tells whether the given string contains at least one upper-case. +func hasUpperCase(s string) bool { + for i := range s { + if 'A' <= s[i] && s[i] <= 'Z' { + return true + } + } + return false +} + +// absDomainName returns an absolute domain name which ends with a +// trailing dot to match pure Go reverse resolver and all other lookup +// routines. +// See golang.org/issue/12189. +// But we don't want to add dots for local names from /etc/hosts. +// It's hard to tell so we settle on the heuristic that names without dots +// (like "localhost" or "myhost") do not get trailing dots, but any other +// names do. +func absDomainName(s string) string { + if strings.IndexByte(s, '.') != -1 && s[len(s)-1] != '.' { + s += "." + } + return s +} diff --git a/component/resolver/hosts/hosts_windows.go b/component/resolver/hosts/hosts_windows.go new file mode 100644 index 00000000..8ad76e0a --- /dev/null +++ b/component/resolver/hosts/hosts_windows.go @@ -0,0 +1,13 @@ +package hosts + +// this file copy and modify from golang's std net/hook_windows.go + +import ( + "golang.org/x/sys/windows" +) + +func init() { + if dir, err := windows.GetSystemDirectory(); err == nil { + hostsFilePath = dir + "/Drivers/etc/hosts" + } +} From bfb6caeeafefdc0711d0de4f12b33bb35c2c416e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 18 May 2024 20:54:28 +0800 Subject: [PATCH 084/311] chore: stop using go:linkname for x/sys/windows --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c01dc643..747783e2 100644 --- a/go.mod +++ b/go.mod @@ -110,4 +110,4 @@ require ( golang.org/x/tools v0.21.0 // indirect ) -replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764 +replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1 diff --git a/go.sum b/go.sum index 41190ce8..d5443e03 100644 --- a/go.sum +++ b/go.sum @@ -106,8 +106,8 @@ github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec h1:HxreOiFTUrJXJa github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= github.com/metacubex/quic-go v0.43.2-0.20240518033621-2c3d14c6b38e h1:Nzwe08FNIJpExWpy9iXkG336dN/8nJqn69yijB7vJ8g= github.com/metacubex/quic-go v0.43.2-0.20240518033621-2c3d14c6b38e/go.mod h1:uXHODgJFUfUnkkCMWLd5Er6L5QY/LFRZb9LD5jyyhsk= -github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764 h1:+czGKoynxYA90YaL3NlCAIJHnlqwoUlLWgmOhdm5ZU8= -github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= +github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1 h1:7hDHLTmjgtRoAp59STwPQpe5Pinwi4cWex+FB3Ohvco= +github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 h1:Wr4g1HCb5Z/QIFwFiVNjO2qL+dRu25+Mdn9xtAZZ+ew= github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8= github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ= From b7c02a59230986d17042a8eeb019f9a5436a99e3 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sat, 18 May 2024 23:09:21 +0800 Subject: [PATCH 085/311] ci: fix docker --- .github/workflows/build.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e3275679..b6b996ab 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -416,6 +416,18 @@ jobs: with: version: latest + - name: Set Docker tags and labels based on trigger + id: set-meta + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "tags=${{ github.event.inputs.version }}" >> $GITHUB_ENV + echo "labels=org.opencontainers.image.version=${{ github.event.inputs.version }}" >> $GITHUB_ENV + else + echo "tags=${{ steps.meta.outputs.tags }}" >> $GITHUB_ENV + echo "labels=${{ steps.meta.outputs.labels }}" >> $GITHUB_ENV + fi + + # Extract metadata (tags, labels) for Docker # https://github.com/docker/metadata-action - name: Extract Docker metadata From df69a31e6226df359a14165b81c3ba95732942c6 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 19 May 2024 11:31:37 +0800 Subject: [PATCH 086/311] chore: stop using go:linkname for crypto/tls.aesgcmPreferred and update utls to 1.6.6 --- component/tls/reality.go | 15 +++++---------- component/tls/utls.go | 2 +- go.mod | 4 ++-- go.sum | 8 ++++---- transport/sing-shadowtls/shadowtls.go | 2 +- transport/vless/vision/conn.go | 2 +- transport/vless/vision/vision.go | 2 +- 7 files changed, 15 insertions(+), 20 deletions(-) diff --git a/component/tls/reality.go b/component/tls/reality.go index 7728b4b2..48da3e92 100644 --- a/component/tls/reality.go +++ b/component/tls/reality.go @@ -16,16 +16,13 @@ import ( "errors" "net" "net/http" - "reflect" "strings" "time" - "unsafe" - "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/ntp" - utls "github.com/sagernet/utls" + utls "github.com/metacubex/utls" "github.com/zhangyunhao116/fastrand" "golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/hkdf" @@ -39,9 +36,6 @@ type RealityConfig struct { ShortID [RealityMaxShortIDLen]byte } -//go:linkname aesgcmPreferred crypto/tls.aesgcmPreferred -func aesgcmPreferred(ciphers []uint16) bool - func GetRealityConn(ctx context.Context, conn net.Conn, ClientFingerprint string, tlsConfig *tls.Config, realityConfig *RealityConfig) (net.Conn, error) { retry := 0 for fingerprint, exists := GetFingerprint(ClientFingerprint); exists; retry++ { @@ -102,7 +96,7 @@ func GetRealityConn(ctx context.Context, conn net.Conn, ClientFingerprint string return nil, err } var aeadCipher cipher.AEAD - if aesgcmPreferred(hello.CipherSuites) { + if utls.AesgcmPreferred(hello.CipherSuites) { aesBlock, _ := aes.NewCipher(authKey) aeadCipher, _ = cipher.NewGCM(aesBlock) } else { @@ -162,11 +156,12 @@ type realityVerifier struct { verified bool } -var pOffset = utils.MustOK(reflect.TypeOf((*utls.Conn)(nil)).Elem().FieldByName("peerCertificates")).Offset +//var pOffset = utils.MustOK(reflect.TypeOf((*utls.Conn)(nil)).Elem().FieldByName("peerCertificates")).Offset func (c *realityVerifier) VerifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { //p, _ := reflect.TypeOf(c.Conn).Elem().FieldByName("peerCertificates") - certs := *(*[]*x509.Certificate)(unsafe.Add(unsafe.Pointer(c.Conn), pOffset)) + //certs := *(*[]*x509.Certificate)(unsafe.Add(unsafe.Pointer(c.Conn), pOffset)) + certs := c.Conn.PeerCertificates() if pub, ok := certs[0].PublicKey.(ed25519.PublicKey); ok { h := hmac.New(sha512.New, c.authKey) h.Write(pub) diff --git a/component/tls/utls.go b/component/tls/utls.go index 3063fc55..31733e50 100644 --- a/component/tls/utls.go +++ b/component/tls/utls.go @@ -6,8 +6,8 @@ import ( "github.com/metacubex/mihomo/log" + utls "github.com/metacubex/utls" "github.com/mroth/weightedrand/v2" - utls "github.com/sagernet/utls" ) type UConn struct { diff --git a/go.mod b/go.mod index 747783e2..d9ed1228 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,7 @@ require ( github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 + github.com/metacubex/utls v1.6.6 github.com/miekg/dns v1.1.59 github.com/mroth/weightedrand/v2 v2.1.0 github.com/openacid/low v0.1.21 @@ -37,7 +38,6 @@ require ( github.com/sagernet/sing v0.3.8 github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 github.com/sagernet/sing-shadowtls v0.1.4 - github.com/sagernet/utls v1.5.4 github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e github.com/samber/lo v1.39.0 github.com/shirou/gopsutil/v3 v3.24.4 @@ -63,7 +63,7 @@ require ( github.com/ajg/form v1.5.1 // indirect github.com/andybalholm/brotli v1.0.6 // indirect github.com/buger/jsonparser v1.1.1 // indirect - github.com/cloudflare/circl v1.3.6 // indirect + github.com/cloudflare/circl v1.3.7 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 // indirect github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect diff --git a/go.sum b/go.sum index d5443e03..fb14d715 100644 --- a/go.sum +++ b/go.sum @@ -21,8 +21,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4= github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM= -github.com/cloudflare/circl v1.3.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg= -github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= +github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8= github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -122,6 +122,8 @@ github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 h1:AGyIB5 github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 h1:as/aO/fM8nv4W4pOr9EETP6kV/Oaujk3fUNyQSJK61c= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= +github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8= +github.com/metacubex/utls v1.6.6/go.mod h1:+WLFUnXjcpdxXCnyX25nggw8C6YonZ8zOK2Zm/oRvdo= github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU= @@ -163,8 +165,6 @@ github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnV github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= -github.com/sagernet/utls v1.5.4 h1:KmsEGbB2dKUtCNC+44NwAdNAqnqQ6GA4pTO0Yik56co= -github.com/sagernet/utls v1.5.4/go.mod h1:CTGxPWExIloRipK3XFpYv0OVyhO8kk3XCGW/ieyTh1s= github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e h1:iGH0RMv2FzELOFNFQtvsxH7NPmlo7X5JizEK51UCojo= github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e/go.mod h1:YbL4TKHRR6APYQv3U2RGfwLDpPYSyWz6oUlpISBEzBE= github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= diff --git a/transport/sing-shadowtls/shadowtls.go b/transport/sing-shadowtls/shadowtls.go index 982d847a..2012e20a 100644 --- a/transport/sing-shadowtls/shadowtls.go +++ b/transport/sing-shadowtls/shadowtls.go @@ -9,9 +9,9 @@ import ( tlsC "github.com/metacubex/mihomo/component/tls" "github.com/metacubex/mihomo/log" + utls "github.com/metacubex/utls" "github.com/sagernet/sing-shadowtls" sing_common "github.com/sagernet/sing/common" - utls "github.com/sagernet/utls" ) const ( diff --git a/transport/vless/vision/conn.go b/transport/vless/vision/conn.go index 5ad28134..69ccfba0 100644 --- a/transport/vless/vision/conn.go +++ b/transport/vless/vision/conn.go @@ -14,7 +14,7 @@ import ( "github.com/metacubex/mihomo/log" "github.com/gofrs/uuid/v5" - utls "github.com/sagernet/utls" + utls "github.com/metacubex/utls" ) var ( diff --git a/transport/vless/vision/vision.go b/transport/vless/vision/vision.go index 09299b23..3b9e9379 100644 --- a/transport/vless/vision/vision.go +++ b/transport/vless/vision/vision.go @@ -14,8 +14,8 @@ import ( tlsC "github.com/metacubex/mihomo/component/tls" "github.com/gofrs/uuid/v5" + utls "github.com/metacubex/utls" "github.com/sagernet/sing/common" - utls "github.com/sagernet/utls" ) var ErrNotTLS13 = errors.New("XTLS Vision based on TLS 1.3 outer connection") From c3ee921d30e4f8be7097d2f7d998e248befbaadf Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sun, 19 May 2024 15:46:23 +0800 Subject: [PATCH 087/311] chore: apply config when geo update --- component/updater/update_geo.go | 23 ++++--------- hub/route/configs.go | 57 +++++++++++++++++++++------------ main.go | 18 +++++++++-- 3 files changed, 60 insertions(+), 38 deletions(-) diff --git a/component/updater/update_geo.go b/component/updater/update_geo.go index a98d94dc..4bb57e06 100644 --- a/component/updater/update_geo.go +++ b/component/updater/update_geo.go @@ -5,7 +5,6 @@ import ( "fmt" "os" "runtime" - "sync" "time" "github.com/metacubex/mihomo/common/atomic" @@ -19,8 +18,7 @@ import ( ) var ( - updateGeoMux sync.Mutex - UpdatingGeo atomic.Bool + UpdatingGeo atomic.Bool ) func updateGeoDatabases() error { @@ -100,22 +98,15 @@ func updateGeoDatabases() error { return nil } -func UpdateGeoDatabases() error { +func UpdateGeoDatabases(updateNotification chan struct{}) error { log.Infoln("[GEO] Start updating GEO database") - updateGeoMux.Lock() - if UpdatingGeo.Load() { - updateGeoMux.Unlock() return errors.New("GEO database is updating, skip") } UpdatingGeo.Store(true) - updateGeoMux.Unlock() - - defer func() { - UpdatingGeo.Store(false) - }() + defer UpdatingGeo.Store(false) log.Infoln("[GEO] Updating GEO database") @@ -124,6 +115,7 @@ func UpdateGeoDatabases() error { return err } + updateNotification <- struct{}{} return nil } @@ -144,7 +136,7 @@ func getUpdateTime() (err error, time time.Time) { return nil, fileInfo.ModTime() } -func RegisterGeoUpdater() { +func RegisterGeoUpdater(updateNotification chan struct{}) { if C.GeoUpdateInterval <= 0 { log.Errorln("[GEO] Invalid update interval: %d", C.GeoUpdateInterval) return @@ -164,16 +156,15 @@ func RegisterGeoUpdater() { log.Infoln("[GEO] last update time %s", lastUpdate) if lastUpdate.Add(time.Duration(C.GeoUpdateInterval) * time.Hour).Before(time.Now()) { log.Infoln("[GEO] Database has not been updated for %v, update now", time.Duration(C.GeoUpdateInterval)*time.Hour) - if err := UpdateGeoDatabases(); err != nil { + if err := UpdateGeoDatabases(updateNotification); err != nil { log.Errorln("[GEO] Failed to update GEO database: %s", err.Error()) return } } for range ticker.C { - if err := UpdateGeoDatabases(); err != nil { + if err := UpdateGeoDatabases(updateNotification); err != nil { log.Errorln("[GEO] Failed to update GEO database: %s", err.Error()) - return } } }() diff --git a/hub/route/configs.go b/hub/route/configs.go index 47fd26e0..35977885 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -364,30 +364,47 @@ func updateConfigs(w http.ResponseWriter, r *http.Request) { } func updateGeoDatabases(w http.ResponseWriter, r *http.Request) { - if updater.UpdatingGeo.Load() { - render.Status(r, http.StatusBadRequest) - render.JSON(w, r, newError("updating...")) - return - } - - err := updater.UpdateGeoDatabases() - if err != nil { - render.Status(r, http.StatusBadRequest) - render.JSON(w, r, newError(err.Error())) - return - } + updateNotification := make(chan struct{}) + errorChannel := make(chan error, 1) + done := make(chan struct{}) + defer func() { + close(updateNotification) + close(errorChannel) + }() go func() { - cfg, err := executor.ParseWithPath(C.Path.Config()) - if err != nil { - log.Errorln("[REST-API] update GEO databases failed: %v", err) - return + defer close(done) + for { + select { + case <-updateNotification: + cfg, err := executor.ParseWithPath(C.Path.Config()) + if err != nil { + log.Errorln("[REST-API] update GEO databases failed: %v", err) + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, newError("Error parsing configuration")) + return + } + + log.Warnln("[REST-API] update GEO databases success, applying config") + executor.ApplyConfig(cfg, false) + return + case err := <-errorChannel: + log.Errorln("[REST-API] update GEO databases failed: %v", err) + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, err.Error()) + return + } } - - log.Warnln("[REST-API] update GEO databases successful, apply config...") - - executor.ApplyConfig(cfg, false) }() + go func() { + err := updater.UpdateGeoDatabases(updateNotification) + if err != nil { + errorChannel <- err + } + }() + + <-done + render.NoContent(w, r) } diff --git a/main.go b/main.go index 1d16f8bf..081a7eb5 100644 --- a/main.go +++ b/main.go @@ -113,9 +113,23 @@ func main() { } if C.GeoAutoUpdate { - updater.RegisterGeoUpdater() - } + updateNotification := make(chan struct{}) + go updater.RegisterGeoUpdater(updateNotification) + go func() { + for range updateNotification { + cfg, err := executor.ParseWithPath(C.Path.Config()) + if err != nil { + log.Errorln("[GEO] update GEO databases failed: %v", err) + return + } + + log.Warnln("[GEO] update GEO databases success, applying config") + + executor.ApplyConfig(cfg, false) + } + }() + } defer executor.Shutdown() termSign := make(chan os.Signal, 1) From e749c7e4923bb3993862048c5d72fd2dd1e6f4ac Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sun, 19 May 2024 16:41:05 +0800 Subject: [PATCH 088/311] ci: docker --- .github/workflows/build.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b6b996ab..84536a54 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -435,6 +435,9 @@ jobs: uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ github.repository }} + tags: ${{ env.tags }} + labels: ${{ env.labels }} + - name: Show files run: | @@ -463,4 +466,4 @@ jobs: linux/arm64 linux/arm/v7 tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} + labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file From 4243a7428478b786bde56421d57cc15d857c3cfc Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sun, 19 May 2024 17:30:39 +0800 Subject: [PATCH 089/311] chore: auto update geo --- component/updater/update_geo.go | 25 +++++++++------ hub/route/configs.go | 54 ++++++++++----------------------- main.go | 26 +++++++--------- 3 files changed, 42 insertions(+), 63 deletions(-) diff --git a/component/updater/update_geo.go b/component/updater/update_geo.go index 4bb57e06..b07cd315 100644 --- a/component/updater/update_geo.go +++ b/component/updater/update_geo.go @@ -98,11 +98,13 @@ func updateGeoDatabases() error { return nil } -func UpdateGeoDatabases(updateNotification chan struct{}) error { +var ErrGetDatabaseUpdateSkip = errors.New("GEO database is updating, skip") + +func UpdateGeoDatabases() error { log.Infoln("[GEO] Start updating GEO database") if UpdatingGeo.Load() { - return errors.New("GEO database is updating, skip") + return ErrGetDatabaseUpdateSkip } UpdatingGeo.Store(true) @@ -115,7 +117,6 @@ func UpdateGeoDatabases(updateNotification chan struct{}) error { return err } - updateNotification <- struct{}{} return nil } @@ -136,17 +137,16 @@ func getUpdateTime() (err error, time time.Time) { return nil, fileInfo.ModTime() } -func RegisterGeoUpdater(updateNotification chan struct{}) { +func RegisterGeoUpdater(onSuccess func()) { if C.GeoUpdateInterval <= 0 { log.Errorln("[GEO] Invalid update interval: %d", C.GeoUpdateInterval) return } - ticker := time.NewTicker(time.Duration(C.GeoUpdateInterval) * time.Hour) - defer ticker.Stop() - - log.Infoln("[GEO] update GEO database every %d hours", C.GeoUpdateInterval) go func() { + ticker := time.NewTicker(time.Duration(C.GeoUpdateInterval) * time.Hour) + defer ticker.Stop() + err, lastUpdate := getUpdateTime() if err != nil { log.Errorln("[GEO] Get GEO database update time error: %s", err.Error()) @@ -156,15 +156,20 @@ func RegisterGeoUpdater(updateNotification chan struct{}) { log.Infoln("[GEO] last update time %s", lastUpdate) if lastUpdate.Add(time.Duration(C.GeoUpdateInterval) * time.Hour).Before(time.Now()) { log.Infoln("[GEO] Database has not been updated for %v, update now", time.Duration(C.GeoUpdateInterval)*time.Hour) - if err := UpdateGeoDatabases(updateNotification); err != nil { + if err := UpdateGeoDatabases(); err != nil { log.Errorln("[GEO] Failed to update GEO database: %s", err.Error()) return + } else { + onSuccess() } } for range ticker.C { - if err := UpdateGeoDatabases(updateNotification); err != nil { + log.Infoln("[GEO] updating database every %d hours", C.GeoUpdateInterval) + if err := UpdateGeoDatabases(); err != nil { log.Errorln("[GEO] Failed to update GEO database: %s", err.Error()) + } else { + onSuccess() } } }() diff --git a/hub/route/configs.go b/hub/route/configs.go index 35977885..f2cf298a 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -364,47 +364,25 @@ func updateConfigs(w http.ResponseWriter, r *http.Request) { } func updateGeoDatabases(w http.ResponseWriter, r *http.Request) { - updateNotification := make(chan struct{}) - errorChannel := make(chan error, 1) - done := make(chan struct{}) - defer func() { - close(updateNotification) - close(errorChannel) - }() + err := updater.UpdateGeoDatabases() + if err != nil { + log.Errorln("[REST-API] update GEO databases failed: %v", err) + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, newError(err.Error())) + return + } - go func() { - defer close(done) - for { - select { - case <-updateNotification: - cfg, err := executor.ParseWithPath(C.Path.Config()) - if err != nil { - log.Errorln("[REST-API] update GEO databases failed: %v", err) - render.Status(r, http.StatusInternalServerError) - render.JSON(w, r, newError("Error parsing configuration")) - return - } + cfg, err := executor.ParseWithPath(C.Path.Config()) + if err != nil { + log.Errorln("[REST-API] update GEO databases failed: %v", err) + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, newError("Error parsing configuration")) + return + } - log.Warnln("[REST-API] update GEO databases success, applying config") - executor.ApplyConfig(cfg, false) - return - case err := <-errorChannel: - log.Errorln("[REST-API] update GEO databases failed: %v", err) - render.Status(r, http.StatusInternalServerError) - render.JSON(w, r, err.Error()) - return - } - } - }() + log.Warnln("[GEO] update GEO databases success, applying config") - go func() { - err := updater.UpdateGeoDatabases(updateNotification) - if err != nil { - errorChannel <- err - } - }() - - <-done + executor.ApplyConfig(cfg, false) render.NoContent(w, r) } diff --git a/main.go b/main.go index 081a7eb5..61f1d683 100644 --- a/main.go +++ b/main.go @@ -113,23 +113,19 @@ func main() { } if C.GeoAutoUpdate { - updateNotification := make(chan struct{}) - go updater.RegisterGeoUpdater(updateNotification) - - go func() { - for range updateNotification { - cfg, err := executor.ParseWithPath(C.Path.Config()) - if err != nil { - log.Errorln("[GEO] update GEO databases failed: %v", err) - return - } - - log.Warnln("[GEO] update GEO databases success, applying config") - - executor.ApplyConfig(cfg, false) + updater.RegisterGeoUpdater(func() { + cfg, err := executor.ParseWithPath(C.Path.Config()) + if err != nil { + log.Errorln("[GEO] update GEO databases failed: %v", err) + return } - }() + + log.Warnln("[GEO] update GEO databases success, applying config") + + executor.ApplyConfig(cfg, false) + }) } + defer executor.Shutdown() termSign := make(chan os.Signal, 1) From c504985b99c9e437be01d842b6589fc6095b708b Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sun, 19 May 2024 19:35:12 +0800 Subject: [PATCH 090/311] chore: Adjust sniff logs --- component/sniffer/dispatcher.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/component/sniffer/dispatcher.go b/component/sniffer/dispatcher.go index 96e9272c..97bf1629 100644 --- a/component/sniffer/dispatcher.go +++ b/component/sniffer/dispatcher.go @@ -116,14 +116,13 @@ func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata } 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 %s [%s]-->[%s] success, replace domain [%s]-->[%s]", - metadata.NetWork, - metadata.SourceDetail(), - metadata.RemoteAddress(), - metadata.Host, host) metadata.SniffHost = host if overrideDest { + log.Debugln("[Sniffer] Sniff %s [%s]-->[%s] success, replace domain [%s]-->[%s]", + metadata.NetWork, + metadata.SourceDetail(), + metadata.RemoteAddress(), + metadata.Host, host) metadata.Host = host } metadata.DNSMode = C.DNSNormal From bd43eca09d4ba82ede087177ade04a91fb0bacb2 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Mon, 20 May 2024 19:46:15 +0800 Subject: [PATCH 091/311] ci:docker tags --- .github/workflows/build.yml | 65 +++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 84536a54..42023755 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ on: branches: - Alpha concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: "${{ github.workflow }}-${{ github.ref }}" cancel-in-progress: true env: @@ -270,7 +270,7 @@ jobs: - name: Archive production artifacts uses: actions/upload-artifact@v4 with: - name: ${{ matrix.jobs.goos }}-${{ matrix.jobs.output }} + name: "${{ matrix.jobs.goos }}-${{ matrix.jobs.output }}" path: | mihomo*.gz mihomo*.deb @@ -415,35 +415,35 @@ jobs: uses: docker/setup-buildx-action@v3 with: version: latest - - - name: Set Docker tags and labels based on trigger - id: set-meta - run: | - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - echo "tags=${{ github.event.inputs.version }}" >> $GITHUB_ENV - echo "labels=org.opencontainers.image.version=${{ github.event.inputs.version }}" >> $GITHUB_ENV - else - echo "tags=${{ steps.meta.outputs.tags }}" >> $GITHUB_ENV - echo "labels=${{ steps.meta.outputs.labels }}" >> $GITHUB_ENV - fi - - + # Extract metadata (tags, labels) for Docker # https://github.com/docker/metadata-action - name: Extract Docker metadata - id: meta + if: ${{ github.event_name != 'workflow_dispatch' }} + id: meta_alpha uses: docker/metadata-action@v5 with: - images: ${{ env.REGISTRY }}/${{ github.repository }} - tags: ${{ env.tags }} - labels: ${{ env.labels }} - - + images: '${{ env.REGISTRY }}/${{ github.repository }}' + + # Extract metadata (tags, labels) for Docker + # https://github.com/docker/metadata-action + - name: Extract Docker metadata + if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' }} + id: meta_release + uses: docker/metadata-action@v5 + with: + images: '${{ env.REGISTRY }}/${{ github.repository }}' + tags: | + ${{ github.event.inputs.version }} + flavor: | + latest=true + labels: org.opencontainers.image.version=${{ github.event.inputs.version }} + - name: Show files run: | ls . ls bin/ - + - name: login to docker REGISTRY uses: docker/login-action@v3 with: @@ -454,7 +454,7 @@ jobs: # Build and push Docker image with Buildx (don't push on PR) # https://github.com/docker/build-push-action - name: Build and push Docker image - id: build-and-push + if: ${{ github.event_name != 'workflow_dispatch' }} uses: docker/build-push-action@v5 with: context: . @@ -465,5 +465,20 @@ jobs: linux/amd64 linux/arm64 linux/arm/v7 - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file + tags: ${{ steps.meta_alpha.outputs.tags }} + labels: ${{ steps.meta_alpha.outputs.labels }} + + - name: Build and push Docker image + if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' }} + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + push: ${{ github.event_name != 'pull_request' }} + platforms: | + linux/386 + linux/amd64 + linux/arm64 + linux/arm/v7 + tags: ${{ steps.meta_release.outputs.tags }} + labels: ${{ steps.meta_release.outputs.labels }} \ No newline at end of file From 3195c678c7b2118f4ec56a9af352b5722c4cdf5c Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 21 May 2024 00:47:59 +0800 Subject: [PATCH 092/311] chore: update quic-go to 0.44.0 --- go.mod | 2 +- go.sum | 4 ++-- transport/tuic/common/congestion.go | 8 ++++---- transport/tuic/congestion/bbr_sender.go | 21 +++++---------------- transport/tuic/congestion/cubic.go | 2 +- transport/tuic/congestion_v2/bbr_sender.go | 18 ++++-------------- transport/tuic/congestion_v2/pacer.go | 4 ++-- 7 files changed, 19 insertions(+), 40 deletions(-) diff --git a/go.mod b/go.mod index d9ed1228..ad2492ff 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.43.2-0.20240518033621-2c3d14c6b38e + github.com/metacubex/quic-go v0.44.1-0.20240521004242-fcd70d587e22 github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 diff --git a/go.sum b/go.sum index fb14d715..0b4b8d3c 100644 --- a/go.sum +++ b/go.sum @@ -104,8 +104,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-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= -github.com/metacubex/quic-go v0.43.2-0.20240518033621-2c3d14c6b38e h1:Nzwe08FNIJpExWpy9iXkG336dN/8nJqn69yijB7vJ8g= -github.com/metacubex/quic-go v0.43.2-0.20240518033621-2c3d14c6b38e/go.mod h1:uXHODgJFUfUnkkCMWLd5Er6L5QY/LFRZb9LD5jyyhsk= +github.com/metacubex/quic-go v0.44.1-0.20240521004242-fcd70d587e22 h1:hsQ0b2A509b6ubnLtLOcUgZ8vOb+d/667zVEJ1T2fao= +github.com/metacubex/quic-go v0.44.1-0.20240521004242-fcd70d587e22/go.mod h1:88wAATpevav4xdy5N8oejQ2cbbI6EcLYEklFeo+qywA= github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1 h1:7hDHLTmjgtRoAp59STwPQpe5Pinwi4cWex+FB3Ohvco= github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 h1:Wr4g1HCb5Z/QIFwFiVNjO2qL+dRu25+Mdn9xtAZZ+ew= diff --git a/transport/tuic/common/congestion.go b/transport/tuic/common/congestion.go index 485e2e6a..a0e2be68 100644 --- a/transport/tuic/common/congestion.go +++ b/transport/tuic/common/congestion.go @@ -22,7 +22,7 @@ func SetCongestionController(quicConn quic.Connection, cc string, cwnd int) { quicConn.SetCongestionControl( congestion.NewCubicSender( congestion.DefaultClock{}, - congestion.GetInitialPacketSize(quicConn.RemoteAddr()), + congestion.GetInitialPacketSize(quicConn), false, ), ) @@ -30,7 +30,7 @@ func SetCongestionController(quicConn quic.Connection, cc string, cwnd int) { quicConn.SetCongestionControl( congestion.NewCubicSender( congestion.DefaultClock{}, - congestion.GetInitialPacketSize(quicConn.RemoteAddr()), + congestion.GetInitialPacketSize(quicConn), true, ), ) @@ -38,7 +38,7 @@ func SetCongestionController(quicConn quic.Connection, cc string, cwnd int) { quicConn.SetCongestionControl( congestion.NewBBRSender( congestion.DefaultClock{}, - congestion.GetInitialPacketSize(quicConn.RemoteAddr()), + congestion.GetInitialPacketSize(quicConn), c.ByteCount(cwnd)*congestion.InitialMaxDatagramSize, congestion.DefaultBBRMaxCongestionWindow*congestion.InitialMaxDatagramSize, ), @@ -49,7 +49,7 @@ func SetCongestionController(quicConn quic.Connection, cc string, cwnd int) { quicConn.SetCongestionControl( congestionv2.NewBbrSender( congestionv2.DefaultClock{}, - congestionv2.GetInitialPacketSize(quicConn.RemoteAddr()), + congestionv2.GetInitialPacketSize(quicConn), c.ByteCount(cwnd), ), ) diff --git a/transport/tuic/congestion/bbr_sender.go b/transport/tuic/congestion/bbr_sender.go index 8c18c616..6cea9355 100644 --- a/transport/tuic/congestion/bbr_sender.go +++ b/transport/tuic/congestion/bbr_sender.go @@ -5,34 +5,23 @@ package congestion import ( "fmt" "math" - "net" "time" + "github.com/metacubex/quic-go" "github.com/metacubex/quic-go/congestion" "github.com/zhangyunhao116/fastrand" ) const ( // InitialMaxDatagramSize is the default maximum packet size used in QUIC for congestion window computations in bytes. - InitialMaxDatagramSize = 1252 - InitialPacketSizeIPv4 = 1252 - InitialPacketSizeIPv6 = 1232 + InitialMaxDatagramSize = 1280 + InitialPacketSize = 1280 InitialCongestionWindow = 32 DefaultBBRMaxCongestionWindow = 10000 ) -func GetInitialPacketSize(addr net.Addr) congestion.ByteCount { - maxSize := congestion.ByteCount(1200) - // If this is not a UDP address, we don't know anything about the MTU. - // Use the minimum size of an Initial packet as the max packet size. - if udpAddr, ok := addr.(*net.UDPAddr); ok { - if udpAddr.IP.To4() != nil { - maxSize = InitialPacketSizeIPv4 - } else { - maxSize = InitialPacketSizeIPv6 - } - } - return congestion.ByteCount(maxSize) +func GetInitialPacketSize(quicConn quic.Connection) congestion.ByteCount { + return congestion.ByteCount(quicConn.Config().InitialPacketSize) } var ( diff --git a/transport/tuic/congestion/cubic.go b/transport/tuic/congestion/cubic.go index dd491a32..a9bed43a 100644 --- a/transport/tuic/congestion/cubic.go +++ b/transport/tuic/congestion/cubic.go @@ -21,7 +21,7 @@ const ( cubeCongestionWindowScale = 410 cubeFactor congestion.ByteCount = 1 << cubeScale / cubeCongestionWindowScale / maxDatagramSize // TODO: when re-enabling cubic, make sure to use the actual packet size here - maxDatagramSize = congestion.ByteCount(InitialPacketSizeIPv4) + maxDatagramSize = congestion.ByteCount(InitialPacketSize) ) const defaultNumConnections = 1 diff --git a/transport/tuic/congestion_v2/bbr_sender.go b/transport/tuic/congestion_v2/bbr_sender.go index 084f85b1..54705978 100644 --- a/transport/tuic/congestion_v2/bbr_sender.go +++ b/transport/tuic/congestion_v2/bbr_sender.go @@ -4,9 +4,9 @@ package congestion import ( "fmt" - "net" "time" + "github.com/metacubex/quic-go" "github.com/metacubex/quic-go/congestion" "github.com/zhangyunhao116/fastrand" @@ -30,7 +30,7 @@ const ( // Constants based on TCP defaults. // The minimum CWND to ensure delayed acks don't reduce bandwidth measurements. // Does not inflate the pacing rate. - defaultMinimumCongestionWindow = 4 * congestion.ByteCount(congestion.InitialPacketSizeIPv4) + defaultMinimumCongestionWindow = 4 * congestion.ByteCount(congestion.InitialPacketSize) // The gain used for the STARTUP, equal to 2/ln(2). defaultHighGain = 2.885 @@ -931,16 +931,6 @@ func bdpFromRttAndBandwidth(rtt time.Duration, bandwidth Bandwidth) congestion.B return congestion.ByteCount(rtt) * congestion.ByteCount(bandwidth) / congestion.ByteCount(BytesPerSecond) / congestion.ByteCount(time.Second) } -func GetInitialPacketSize(addr net.Addr) congestion.ByteCount { - // If this is not a UDP address, we don't know anything about the MTU. - // Use the minimum size of an Initial packet as the max packet size. - if udpAddr, ok := addr.(*net.UDPAddr); ok { - if udpAddr.IP.To4() != nil { - return congestion.InitialPacketSizeIPv4 - } else { - return congestion.InitialPacketSizeIPv6 - } - } else { - return congestion.MinInitialPacketSize - } +func GetInitialPacketSize(quicConn quic.Connection) congestion.ByteCount { + return congestion.ByteCount(quicConn.Config().InitialPacketSize) } diff --git a/transport/tuic/congestion_v2/pacer.go b/transport/tuic/congestion_v2/pacer.go index ecaf3d11..174b3dbe 100644 --- a/transport/tuic/congestion_v2/pacer.go +++ b/transport/tuic/congestion_v2/pacer.go @@ -21,8 +21,8 @@ type Pacer struct { func NewPacer(getBandwidth func() congestion.ByteCount) *Pacer { p := &Pacer{ - budgetAtLastSent: maxBurstPackets * congestion.InitialPacketSizeIPv4, - maxDatagramSize: congestion.InitialPacketSizeIPv4, + budgetAtLastSent: maxBurstPackets * congestion.InitialPacketSize, + maxDatagramSize: congestion.InitialPacketSize, getBandwidth: getBandwidth, } return p From 43bdc76f87165bba04d0435e61b943ced945a872 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 21 May 2024 19:13:44 +0800 Subject: [PATCH 093/311] fix: darwin calculate correct tunIndex https://github.com/MetaCubeX/mihomo/pull/1285 --- listener/sing_tun/server.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 2dc6524e..09bf308c 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -59,15 +59,23 @@ func CalculateInterfaceName(name string) (tunName string) { if err != nil { return } - var tunIndex int + tunIndex := 0 + indexSet := make(map[int]struct{}) for _, netInterface := range interfaces { if strings.HasPrefix(netInterface.Name, tunName) { index, parseErr := strconv.ParseInt(netInterface.Name[len(tunName):], 10, 16) if parseErr == nil { - tunIndex = int(index) + 1 + indexSet[int(index)] = struct{}{} } } } + for index := range indexSet { + if index == tunIndex { + tunIndex += 1 + } else { // indexSet already sorted and distinct, so this tunIndex nobody used + break + } + } tunName = F.ToString(tunName, tunIndex) return } From ac2506154f4affa1986f2621795f024cc53cf5e4 Mon Sep 17 00:00:00 2001 From: 5aaee9 <7685264+5aaee9@users.noreply.github.com> Date: Tue, 21 May 2024 23:21:57 +0800 Subject: [PATCH 094/311] fix: possibly using released buffer in tproxy (#1286) --- listener/tproxy/packet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/listener/tproxy/packet.go b/listener/tproxy/packet.go index e4852665..b038d954 100644 --- a/listener/tproxy/packet.go +++ b/listener/tproxy/packet.go @@ -105,9 +105,9 @@ func listenLocalConn(rAddr, lAddr net.Addr, tunnel C.Tunnel) (*net.UDPConn, erro buf := pool.Get(pool.UDPBufferSize) br, err := lc.Read(buf) if err != nil { - pool.Put(buf) if errors.Is(err, net.ErrClosed) { log.Debugln("TProxy local conn listener exit.. rAddr=%s lAddr=%s", rAddr.String(), lAddr.String()) + pool.Put(buf) return } } From 5eb8958ff2ed8a45f47a4a79357160ca52bbef0b Mon Sep 17 00:00:00 2001 From: moexiami <1927254+Xiami2012@users.noreply.github.com> Date: Tue, 21 May 2024 23:31:28 +0800 Subject: [PATCH 095/311] fix: correct type for vmess.ws-opts.path in ConvertsV2Ray (#1145) It should be a string for the following reasons: 1. During conversion, it is conditionally assigned with `wsOpts["path"] = path.(string)` 2. After conversion, it is decoded into `WSOptions.Path` in `adapter/outbound/vmess.go` which requires a string. --- common/convert/converter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/convert/converter.go b/common/convert/converter.go index 222dd9fa..19886d25 100644 --- a/common/convert/converter.go +++ b/common/convert/converter.go @@ -333,7 +333,7 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { case "ws", "httpupgrade": headers := make(map[string]any) wsOpts := make(map[string]any) - wsOpts["path"] = []string{"/"} + wsOpts["path"] = "/" if host, ok := values["host"]; ok && host != "" { headers["Host"] = host.(string) } From 71922dd0b1098ca09abd62a33ffbcbce77bb9aba Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 21 May 2024 23:52:56 +0800 Subject: [PATCH 096/311] fix: bad usage for exec in sing-tun https://github.com/MetaCubeX/mihomo/issues/1234 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ad2492ff..08e5b5aa 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 - github.com/metacubex/sing-tun v0.2.7-0.20240512075008-89e7c6208eec + github.com/metacubex/sing-tun v0.2.7-0.20240521155100-e8316a45a414 github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 diff --git a/go.sum b/go.sum index 0b4b8d3c..b9f1af14 100644 --- a/go.sum +++ b/go.sum @@ -114,8 +114,8 @@ github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwV github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A= github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8= -github.com/metacubex/sing-tun v0.2.7-0.20240512075008-89e7c6208eec h1:K4Wq3GOdLZ/xcqwyzAt4kmYQrjokyKQ3u/Xh5Yft14U= -github.com/metacubex/sing-tun v0.2.7-0.20240512075008-89e7c6208eec/go.mod h1:4VsMwZH1IlgPGFK1ZbBomZ/B2MYkTgs2+gnBAr5GOIo= +github.com/metacubex/sing-tun v0.2.7-0.20240521155100-e8316a45a414 h1:IPxTZgQV6fVUBS8tozLMSFPHV3imYc/NbuGfp0bLQq0= +github.com/metacubex/sing-tun v0.2.7-0.20240521155100-e8316a45a414/go.mod h1:4VsMwZH1IlgPGFK1ZbBomZ/B2MYkTgs2+gnBAr5GOIo= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbTMBip/C+R79bvbfr42xH1gZl3uFb0RELdZiQ= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 h1:AGyIB55UfQm/0ZH0HtQO9u3l//yjtHUpjeRjjPGfGRI= From 0b6ae6ffb881872e3ff7a0b4454bb43a1b178dd3 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 22 May 2024 09:00:59 +0800 Subject: [PATCH 097/311] feat: add `ss-opts` for trojan outbound like trojan-go's `shadowsocks` config https://github.com/MetaCubeX/mihomo/issues/1269 --- adapter/outbound/trojan.go | 43 ++++++++++++++++++++++++++++++++++++++ docs/config.yaml | 4 ++++ 2 files changed, 47 insertions(+) diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index b14761a4..938a8858 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -3,6 +3,7 @@ package outbound import ( "context" "crypto/tls" + "errors" "fmt" "net" "net/http" @@ -15,6 +16,7 @@ import ( tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/transport/gun" + "github.com/metacubex/mihomo/transport/shadowsocks/core" "github.com/metacubex/mihomo/transport/trojan" ) @@ -29,6 +31,8 @@ type Trojan struct { transport *gun.TransportWrap realityConfig *tlsC.RealityConfig + + ssCipher core.Cipher } type TrojanOption struct { @@ -46,9 +50,17 @@ type TrojanOption struct { RealityOpts RealityOptions `proxy:"reality-opts,omitempty"` GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"` WSOpts WSOptions `proxy:"ws-opts,omitempty"` + SSOpts TrojanSSOption `proxy:"ss-opts,omitempty"` ClientFingerprint string `proxy:"client-fingerprint,omitempty"` } +// TrojanSSOption from https://github.com/p4gefau1t/trojan-go/blob/v0.10.6/tunnel/shadowsocks/config.go#L5 +type TrojanSSOption struct { + Enabled bool `proxy:"enabled,omitempty"` + Method string `proxy:"method,omitempty"` + Password string `proxy:"password,omitempty"` +} + func (t *Trojan) plainStream(ctx context.Context, c net.Conn) (net.Conn, error) { if t.option.Network == "ws" { host, port, _ := net.SplitHostPort(t.addr) @@ -95,6 +107,10 @@ func (t *Trojan) StreamConnContext(ctx context.Context, c net.Conn, metadata *C. return nil, fmt.Errorf("%s connect error: %w", t.addr, err) } + if t.ssCipher != nil { + c = t.ssCipher.StreamConn(c) + } + if metadata.NetWork == C.UDP { err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata)) return c, err @@ -112,6 +128,10 @@ func (t *Trojan) DialContext(ctx context.Context, metadata *C.Metadata, opts ... return nil, err } + if t.ssCipher != nil { + c = t.ssCipher.StreamConn(c) + } + if err = t.instance.WriteHeader(c, trojan.CommandTCP, serializesSocksAddr(metadata)); err != nil { c.Close() return nil, err @@ -161,6 +181,11 @@ func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata, defer func(c net.Conn) { safeConnClose(c, err) }(c) + + if t.ssCipher != nil { + c = t.ssCipher.StreamConn(c) + } + err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata)) if err != nil { return nil, err @@ -193,6 +218,10 @@ func (t *Trojan) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, me return nil, fmt.Errorf("%s connect error: %w", t.addr, err) } + if t.ssCipher != nil { + c = t.ssCipher.StreamConn(c) + } + err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata)) if err != nil { return nil, err @@ -257,6 +286,20 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { } tOption.Reality = t.realityConfig + if option.SSOpts.Enabled { + if option.SSOpts.Password == "" { + return nil, errors.New("empty password") + } + if option.SSOpts.Method == "" { + option.SSOpts.Method = "AES-128-GCM" + } + ciph, err := core.PickCipher(option.SSOpts.Method, nil, option.SSOpts.Password) + if err != nil { + return nil, err + } + t.ssCipher = ciph + } + if option.Network == "grpc" { dialFn := func(network, addr string) (net.Conn, error) { var err error diff --git a/docs/config.yaml b/docs/config.yaml index 987491e3..fe850163 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -611,6 +611,10 @@ proxies: # socks5 # - h2 # - http/1.1 # skip-cert-verify: true + # ss-opts: # like trojan-go's `shadowsocks` config + # enabled: false + # method: aes-128-gcm # aes-128-gcm/aes-256-gcm/chacha20-ietf-poly1305 + # password: "example" - name: trojan-grpc server: server From 846bdfa8129b9b984a62f04204b4a6925a016834 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sat, 25 May 2024 08:09:59 +0800 Subject: [PATCH 098/311] chore: Allow customization of GLOBAL --- config/config.go | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/config/config.go b/config/config.go index 9bc0afc8..74a2053e 100644 --- a/config/config.go +++ b/config/config.go @@ -716,8 +716,11 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[ groupsConfig := cfg.ProxyGroup providersConfig := cfg.ProxyProvider - var proxyList []string - var AllProxies []string + var ( + proxyList []string + AllProxies []string + hasGlobal bool + ) proxiesList := list.New() groupsList := list.New() @@ -750,6 +753,9 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[ if !existName { return nil, nil, fmt.Errorf("proxy group %d: missing name", idx) } + if groupName == "GLOBAL" { + hasGlobal = true + } proxyList = append(proxyList, groupName) groupsList.PushBack(mapping) } @@ -801,13 +807,15 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[ pd, _ := provider.NewCompatibleProvider(provider.ReservedName, ps, hc) providersMap[provider.ReservedName] = pd - global := outboundgroup.NewSelector( - &outboundgroup.GroupCommonOption{ - Name: "GLOBAL", - }, - []providerTypes.ProxyProvider{pd}, - ) - proxies["GLOBAL"] = adapter.NewProxy(global) + if !hasGlobal { + global := outboundgroup.NewSelector( + &outboundgroup.GroupCommonOption{ + Name: "GLOBAL", + }, + []providerTypes.ProxyProvider{pd}, + ) + proxies["GLOBAL"] = adapter.NewProxy(global) + } ProxiesList = proxiesList GroupsList = groupsList if ParsingProxiesCallback != nil { From 7eb70aeb4d1c68474f5abbfc96c0823ff89175b2 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 29 May 2024 00:08:17 +0800 Subject: [PATCH 099/311] fix: windows build number --- constant/features/version_windows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constant/features/version_windows.go b/constant/features/version_windows.go index 192df950..2f756188 100644 --- a/constant/features/version_windows.go +++ b/constant/features/version_windows.go @@ -6,5 +6,5 @@ func init() { version := windows.RtlGetVersion() WindowsMajorVersion = version.MajorVersion WindowsMinorVersion = version.MinorVersion - WindowsBuildNumber = version.MinorVersion + WindowsBuildNumber = version.BuildNumber } From d3fea909e98c0645aa73cfc60f52cb9038e3ebfa Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 30 May 2024 10:39:17 +0800 Subject: [PATCH 100/311] chore: remove tfo windows support Golang officially decided not to open `internal/poll.execIO` to third-party libraries after 1.23 was released, so we can only choose to remove tfo support on the Windows platform. --- adapter/inbound/listen.go | 14 +------------- adapter/inbound/listen_unix.go | 23 +++++++++++++++++++++++ adapter/inbound/listen_windows.go | 15 +++++++++++++++ component/dialer/tfo.go | 17 ----------------- component/dialer/tfo_unix.go | 25 +++++++++++++++++++++++++ component/dialer/tfo_windows.go | 15 ++++++++------- 6 files changed, 72 insertions(+), 37 deletions(-) create mode 100644 adapter/inbound/listen_unix.go create mode 100644 adapter/inbound/listen_windows.go create mode 100644 component/dialer/tfo_unix.go diff --git a/adapter/inbound/listen.go b/adapter/inbound/listen.go index 18dc1bc2..edbccea7 100644 --- a/adapter/inbound/listen.go +++ b/adapter/inbound/listen.go @@ -3,22 +3,10 @@ package inbound import ( "context" "net" - - "github.com/metacubex/tfo-go" ) -var ( - lc = tfo.ListenConfig{ - DisableTFO: true, - } -) - -func SetTfo(open bool) { - lc.DisableTFO = !open -} - func SetMPTCP(open bool) { - setMultiPathTCP(&lc.ListenConfig, open) + setMultiPathTCP(getListenConfig(), open) } func ListenContext(ctx context.Context, network, address string) (net.Listener, error) { diff --git a/adapter/inbound/listen_unix.go b/adapter/inbound/listen_unix.go new file mode 100644 index 00000000..bb78adb2 --- /dev/null +++ b/adapter/inbound/listen_unix.go @@ -0,0 +1,23 @@ +//go:build unix + +package inbound + +import ( + "net" + + "github.com/metacubex/tfo-go" +) + +var ( + lc = tfo.ListenConfig{ + DisableTFO: true, + } +) + +func SetTfo(open bool) { + lc.DisableTFO = !open +} + +func getListenConfig() *net.ListenConfig { + return &lc.ListenConfig +} diff --git a/adapter/inbound/listen_windows.go b/adapter/inbound/listen_windows.go new file mode 100644 index 00000000..a4223e2b --- /dev/null +++ b/adapter/inbound/listen_windows.go @@ -0,0 +1,15 @@ +package inbound + +import ( + "net" +) + +var ( + lc = net.ListenConfig{} +) + +func SetTfo(open bool) {} + +func getListenConfig() *net.ListenConfig { + return &lc +} diff --git a/component/dialer/tfo.go b/component/dialer/tfo.go index 76fe94d0..bc32b38a 100644 --- a/component/dialer/tfo.go +++ b/component/dialer/tfo.go @@ -5,12 +5,8 @@ import ( "io" "net" "time" - - "github.com/metacubex/tfo-go" ) -var DisableTFO = false - type tfoConn struct { net.Conn closed bool @@ -124,16 +120,3 @@ func (c *tfoConn) ReaderReplaceable() bool { func (c *tfoConn) WriterReplaceable() bool { return c.Conn != nil } - -func dialTFO(ctx context.Context, netDialer net.Dialer, network, address string) (net.Conn, error) { - ctx, cancel := context.WithTimeout(context.Background(), DefaultTCPTimeout) - dialer := tfo.Dialer{Dialer: netDialer, DisableTFO: false} - return &tfoConn{ - dialed: make(chan bool, 1), - cancel: cancel, - ctx: ctx, - dialFn: func(ctx context.Context, earlyData []byte) (net.Conn, error) { - return dialer.DialContext(ctx, network, address, earlyData) - }, - }, nil -} diff --git a/component/dialer/tfo_unix.go b/component/dialer/tfo_unix.go new file mode 100644 index 00000000..b8908849 --- /dev/null +++ b/component/dialer/tfo_unix.go @@ -0,0 +1,25 @@ +//go:build unix + +package dialer + +import ( + "context" + "net" + + "github.com/metacubex/tfo-go" +) + +const DisableTFO = false + +func dialTFO(ctx context.Context, netDialer net.Dialer, network, address string) (net.Conn, error) { + ctx, cancel := context.WithTimeout(context.Background(), DefaultTCPTimeout) + dialer := tfo.Dialer{Dialer: netDialer, DisableTFO: false} + return &tfoConn{ + dialed: make(chan bool, 1), + cancel: cancel, + ctx: ctx, + dialFn: func(ctx context.Context, earlyData []byte) (net.Conn, error) { + return dialer.DialContext(ctx, network, address, earlyData) + }, + }, nil +} diff --git a/component/dialer/tfo_windows.go b/component/dialer/tfo_windows.go index 63266118..f1dddcf4 100644 --- a/component/dialer/tfo_windows.go +++ b/component/dialer/tfo_windows.go @@ -1,11 +1,12 @@ package dialer -import "github.com/metacubex/mihomo/constant/features" +import ( + "context" + "net" +) -func init() { - // According to MSDN, this option is available since Windows 10, 1607 - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms738596(v=vs.85).aspx - if features.WindowsMajorVersion < 10 || (features.WindowsMajorVersion == 10 && features.WindowsBuildNumber < 14393) { - DisableTFO = true - } +const DisableTFO = true + +func dialTFO(ctx context.Context, netDialer net.Dialer, network, address string) (net.Conn, error) { + return netDialer.DialContext(ctx, network, address) } From 39eda257a77507bee161a422df053f5cea236590 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 31 May 2024 11:31:17 +0800 Subject: [PATCH 101/311] chore: replace zhangyunhao116/fastrand to our metacubex/randv2 --- adapter/outbound/hysteria2.go | 4 +-- adapter/outbound/ssh.go | 8 ++--- common/convert/util.go | 6 ++-- common/pool/alloc_test.go | 4 +-- common/utils/uuid.go | 38 +++++++++++++++++----- component/resolver/host.go | 4 +-- component/resolver/resolver.go | 10 +++--- component/tls/reality.go | 6 ++-- dns/client.go | 4 +-- go.mod | 2 +- go.sum | 4 +-- transport/hysteria/conns/udp/hop.go | 6 ++-- transport/hysteria/conns/wechat/obfs.go | 4 +-- transport/hysteria/core/client.go | 4 +-- transport/hysteria/obfs/xplus.go | 5 ++- transport/simple-obfs/http.go | 7 ++-- transport/simple-obfs/tls.go | 7 ++-- transport/ssr/obfs/http_simple.go | 10 +++--- transport/ssr/obfs/random_head.go | 7 ++-- transport/ssr/obfs/tls1.2_ticket_auth.go | 11 ++++--- transport/ssr/protocol/auth_aes128_sha1.go | 17 +++++----- transport/ssr/protocol/auth_sha1_v4.go | 6 ++-- transport/ssr/protocol/base.go | 7 ++-- transport/ssr/protocol/protocol.go | 4 +-- transport/tuic/congestion/bbr_sender.go | 4 +-- transport/tuic/congestion_v2/bbr_sender.go | 4 +-- transport/tuic/v4/client.go | 4 +-- transport/tuic/v5/client.go | 4 +-- transport/tuic/v5/packet.go | 4 +-- transport/vless/vision/padding.go | 10 +++--- transport/vmess/conn.go | 9 ++--- transport/vmess/h2.go | 4 +-- transport/vmess/http.go | 8 ++--- transport/vmess/vmess.go | 4 +-- transport/vmess/websocket.go | 7 ++-- 35 files changed, 136 insertions(+), 111 deletions(-) diff --git a/adapter/outbound/hysteria2.go b/adapter/outbound/hysteria2.go index 0ad7c214..b8abf39c 100644 --- a/adapter/outbound/hysteria2.go +++ b/adapter/outbound/hysteria2.go @@ -21,8 +21,8 @@ import ( "github.com/metacubex/sing-quic/hysteria2" + "github.com/metacubex/randv2" M "github.com/sagernet/sing/common/metadata" - "github.com/zhangyunhao116/fastrand" ) func init() { @@ -165,7 +165,7 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { }) if len(serverAddress) > 0 { clientOptions.ServerAddress = func(ctx context.Context) (*net.UDPAddr, error) { - return resolveUDPAddrWithPrefer(ctx, "udp", serverAddress[fastrand.Intn(len(serverAddress))], C.NewDNSPrefer(option.IPVersion)) + return resolveUDPAddrWithPrefer(ctx, "udp", serverAddress[randv2.IntN(len(serverAddress))], C.NewDNSPrefer(option.IPVersion)) } if option.HopInterval == 0 { diff --git a/adapter/outbound/ssh.go b/adapter/outbound/ssh.go index a0efabca..8b2776a6 100644 --- a/adapter/outbound/ssh.go +++ b/adapter/outbound/ssh.go @@ -17,7 +17,7 @@ import ( "github.com/metacubex/mihomo/component/proxydialer" C "github.com/metacubex/mihomo/constant" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" "golang.org/x/crypto/ssh" ) @@ -180,10 +180,10 @@ func NewSsh(option SshOption) (*Ssh, error) { } version := "SSH-2.0-OpenSSH_" - if fastrand.Intn(2) == 0 { - version += "7." + strconv.Itoa(fastrand.Intn(10)) + if randv2.IntN(2) == 0 { + version += "7." + strconv.Itoa(randv2.IntN(10)) } else { - version += "8." + strconv.Itoa(fastrand.Intn(9)) + version += "8." + strconv.Itoa(randv2.IntN(9)) } config.ClientVersion = version diff --git a/common/convert/util.go b/common/convert/util.go index a715b556..ab006374 100644 --- a/common/convert/util.go +++ b/common/convert/util.go @@ -8,8 +8,8 @@ import ( "github.com/metacubex/mihomo/common/utils" + "github.com/metacubex/randv2" "github.com/metacubex/sing-shadowsocks/shadowimpl" - "github.com/zhangyunhao116/fastrand" ) var hostsSuffix = []string{ @@ -302,11 +302,11 @@ func RandHost() string { prefix += string(buf[6:8]) + "-" prefix += string(buf[len(buf)-8:]) - return prefix + hostsSuffix[fastrand.Intn(hostsLen)] + return prefix + hostsSuffix[randv2.IntN(hostsLen)] } func RandUserAgent() string { - return userAgents[fastrand.Intn(uaLen)] + return userAgents[randv2.IntN(uaLen)] } func SetUserAgent(header http.Header) { diff --git a/common/pool/alloc_test.go b/common/pool/alloc_test.go index 0ac72ee0..c76ff26a 100644 --- a/common/pool/alloc_test.go +++ b/common/pool/alloc_test.go @@ -3,8 +3,8 @@ package pool import ( "testing" + "github.com/metacubex/randv2" "github.com/stretchr/testify/assert" - "github.com/zhangyunhao116/fastrand" ) func TestAllocGet(t *testing.T) { @@ -43,6 +43,6 @@ func TestAllocPutThenGet(t *testing.T) { func BenchmarkMSB(b *testing.B) { for i := 0; i < b.N; i++ { - msb(fastrand.Int()) + msb(randv2.Int()) } } diff --git a/common/utils/uuid.go b/common/utils/uuid.go index f559b471..d9a8b789 100644 --- a/common/utils/uuid.go +++ b/common/utils/uuid.go @@ -2,19 +2,39 @@ package utils import ( "github.com/gofrs/uuid/v5" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) -type fastRandReader struct{} +type unsafeRandReader struct{} -func (r fastRandReader) Read(p []byte) (int, error) { - return fastrand.Read(p) +func (r unsafeRandReader) Read(p []byte) (n int, err error) { + // modify from https://github.com/golang/go/blob/587c3847da81aa7cfc3b3db2677c8586c94df13a/src/runtime/rand.go#L70-L89 + // Inspired by wyrand. + n = len(p) + v := randv2.Uint64() + for len(p) > 0 { + v ^= 0xa0761d6478bd642f + v *= 0xe7037ed1a0b428db + size := 8 + if len(p) < 8 { + size = len(p) + } + for i := 0; i < size; i++ { + p[i] ^= byte(v >> (8 * i)) + } + p = p[size:] + v = v>>32 | v<<32 + } + + return } -var UnsafeUUIDGenerator = uuid.NewGenWithOptions(uuid.WithRandomReader(fastRandReader{})) +var UnsafeRandReader = unsafeRandReader{} + +var UnsafeUUIDGenerator = uuid.NewGenWithOptions(uuid.WithRandomReader(UnsafeRandReader)) func NewUUIDV1() uuid.UUID { - u, _ := UnsafeUUIDGenerator.NewV1() // fastrand.Read wouldn't cause error, so ignore err is safe + u, _ := UnsafeUUIDGenerator.NewV1() // unsafeRandReader wouldn't cause error, so ignore err is safe return u } @@ -23,7 +43,7 @@ func NewUUIDV3(ns uuid.UUID, name string) uuid.UUID { } func NewUUIDV4() uuid.UUID { - u, _ := UnsafeUUIDGenerator.NewV4() // fastrand.Read wouldn't cause error, so ignore err is safe + u, _ := UnsafeUUIDGenerator.NewV4() // unsafeRandReader wouldn't cause error, so ignore err is safe return u } @@ -32,12 +52,12 @@ func NewUUIDV5(ns uuid.UUID, name string) uuid.UUID { } func NewUUIDV6() uuid.UUID { - u, _ := UnsafeUUIDGenerator.NewV6() // fastrand.Read wouldn't cause error, so ignore err is safe + u, _ := UnsafeUUIDGenerator.NewV6() // unsafeRandReader wouldn't cause error, so ignore err is safe return u } func NewUUIDV7() uuid.UUID { - u, _ := UnsafeUUIDGenerator.NewV7() // fastrand.Read wouldn't cause error, so ignore err is safe + u, _ := UnsafeUUIDGenerator.NewV7() // unsafeRandReader wouldn't cause error, so ignore err is safe return u } diff --git a/component/resolver/host.go b/component/resolver/host.go index 53fa5924..34da8e9f 100644 --- a/component/resolver/host.go +++ b/component/resolver/host.go @@ -11,7 +11,7 @@ import ( "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/resolver/hosts" "github.com/metacubex/mihomo/component/trie" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) var ( @@ -125,5 +125,5 @@ func (hv HostValue) RandIP() (netip.Addr, error) { if hv.IsDomain { return netip.Addr{}, errors.New("value type is error") } - return hv.IPs[fastrand.Intn(len(hv.IPs))], nil + return hv.IPs[randv2.IntN(len(hv.IPs))], nil } diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index f9b56e47..07c68824 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -12,8 +12,8 @@ import ( "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/trie" + "github.com/metacubex/randv2" "github.com/miekg/dns" - "github.com/zhangyunhao116/fastrand" ) var ( @@ -93,7 +93,7 @@ func ResolveIPv4WithResolver(ctx context.Context, host string, r Resolver) (neti } else if len(ips) == 0 { return netip.Addr{}, fmt.Errorf("%w: %s", ErrIPNotFound, host) } - return ips[fastrand.Intn(len(ips))], nil + return ips[randv2.IntN(len(ips))], nil } // ResolveIPv4 with a host, return ipv4 @@ -149,7 +149,7 @@ func ResolveIPv6WithResolver(ctx context.Context, host string, r Resolver) (neti } else if len(ips) == 0 { return netip.Addr{}, fmt.Errorf("%w: %s", ErrIPNotFound, host) } - return ips[fastrand.Intn(len(ips))], nil + return ips[randv2.IntN(len(ips))], nil } func ResolveIPv6(ctx context.Context, host string) (netip.Addr, error) { @@ -200,9 +200,9 @@ func ResolveIPWithResolver(ctx context.Context, host string, r Resolver) (netip. } ipv4s, ipv6s := SortationAddr(ips) if len(ipv4s) > 0 { - return ipv4s[fastrand.Intn(len(ipv4s))], nil + return ipv4s[randv2.IntN(len(ipv4s))], nil } - return ipv6s[fastrand.Intn(len(ipv6s))], nil + return ipv6s[randv2.IntN(len(ipv6s))], nil } // ResolveIP with a host, return ip and priority return TypeA diff --git a/component/tls/reality.go b/component/tls/reality.go index 48da3e92..ff028257 100644 --- a/component/tls/reality.go +++ b/component/tls/reality.go @@ -22,8 +22,8 @@ import ( "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/ntp" + "github.com/metacubex/randv2" utls "github.com/metacubex/utls" - "github.com/zhangyunhao116/fastrand" "golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/hkdf" "golang.org/x/net/http2" @@ -138,13 +138,13 @@ func realityClientFallback(uConn net.Conn, serverName string, fingerprint utls.C return } request.Header.Set("User-Agent", fingerprint.Client) - request.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", fastrand.Intn(32)+30)}) + request.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", randv2.IntN(32)+30)}) response, err := client.Do(request) if err != nil { return } //_, _ = io.Copy(io.Discard, response.Body) - time.Sleep(time.Duration(5+fastrand.Int63n(10)) * time.Second) + time.Sleep(time.Duration(5+randv2.IntN(10)) * time.Second) response.Body.Close() client.CloseIdleConnections() } diff --git a/dns/client.go b/dns/client.go index 3b4efed1..a6f0a7d4 100644 --- a/dns/client.go +++ b/dns/client.go @@ -14,8 +14,8 @@ import ( C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" + "github.com/metacubex/randv2" D "github.com/miekg/dns" - "github.com/zhangyunhao116/fastrand" ) type client struct { @@ -65,7 +65,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) } else if len(ips) == 0 { return nil, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, c.host) } - ip = ips[fastrand.Intn(len(ips))] + ip = ips[randv2.IntN(len(ips))] } network := "udp" diff --git a/go.mod b/go.mod index 08e5b5aa..bc32a633 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.44.1-0.20240521004242-fcd70d587e22 + github.com/metacubex/randv2 v0.2.0 github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 @@ -44,7 +45,6 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.9.0 github.com/wk8/go-ordered-map/v2 v2.1.8 - github.com/zhangyunhao116/fastrand v0.4.0 go.uber.org/automaxprocs v1.5.3 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba golang.org/x/crypto v0.23.0 diff --git a/go.sum b/go.sum index b9f1af14..6dbbfd77 100644 --- a/go.sum +++ b/go.sum @@ -106,6 +106,8 @@ github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec h1:HxreOiFTUrJXJa github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= github.com/metacubex/quic-go v0.44.1-0.20240521004242-fcd70d587e22 h1:hsQ0b2A509b6ubnLtLOcUgZ8vOb+d/667zVEJ1T2fao= github.com/metacubex/quic-go v0.44.1-0.20240521004242-fcd70d587e22/go.mod h1:88wAATpevav4xdy5N8oejQ2cbbI6EcLYEklFeo+qywA= +github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= +github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1 h1:7hDHLTmjgtRoAp59STwPQpe5Pinwi4cWex+FB3Ohvco= github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 h1:Wr4g1HCb5Z/QIFwFiVNjO2qL+dRu25+Mdn9xtAZZ+ew= @@ -210,8 +212,6 @@ github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/ github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -github.com/zhangyunhao116/fastrand v0.4.0 h1:86QB6Y+GGgLZRFRDCjMmAS28QULwspK9sgL5d1Bx3H4= -github.com/zhangyunhao116/fastrand v0.4.0/go.mod h1:vIyo6EyBhjGKpZv6qVlkPl4JVAklpMM4DSKzbAkMguA= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ= go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= diff --git a/transport/hysteria/conns/udp/hop.go b/transport/hysteria/conns/udp/hop.go index eb0732f0..0a888749 100644 --- a/transport/hysteria/conns/udp/hop.go +++ b/transport/hysteria/conns/udp/hop.go @@ -12,7 +12,7 @@ import ( "github.com/metacubex/mihomo/transport/hysteria/obfs" "github.com/metacubex/mihomo/transport/hysteria/utils" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) const ( @@ -86,7 +86,7 @@ func NewObfsUDPHopClientPacketConn(server string, serverPorts string, hopInterva serverAddrs: serverAddrs, hopInterval: hopInterval, obfs: obfs, - addrIndex: fastrand.Intn(len(serverAddrs)), + addrIndex: randv2.IntN(len(serverAddrs)), recvQueue: make(chan *udpPacket, packetQueueSize), closeChan: make(chan struct{}), bufPool: sync.Pool{ @@ -177,7 +177,7 @@ func (c *ObfsUDPHopClientPacketConn) hop(dialer utils.PacketDialer, rAddr net.Ad _ = trySetPacketConnWriteBuffer(c.currentConn, c.writeBufferSize) } go c.recvRoutine(c.currentConn) - c.addrIndex = fastrand.Intn(len(c.serverAddrs)) + c.addrIndex = randv2.IntN(len(c.serverAddrs)) } func (c *ObfsUDPHopClientPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { diff --git a/transport/hysteria/conns/wechat/obfs.go b/transport/hysteria/conns/wechat/obfs.go index 4266d268..c5ec47ee 100644 --- a/transport/hysteria/conns/wechat/obfs.go +++ b/transport/hysteria/conns/wechat/obfs.go @@ -9,7 +9,7 @@ import ( "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/transport/hysteria/obfs" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) const udpBufferSize = 65535 @@ -31,7 +31,7 @@ func NewObfsWeChatUDPConn(orig net.PacketConn, obfs obfs.Obfuscator) *ObfsWeChat obfs: obfs, readBuf: make([]byte, udpBufferSize), writeBuf: make([]byte, udpBufferSize), - sn: fastrand.Uint32() & 0xFFFF, + sn: randv2.Uint32() & 0xFFFF, } } diff --git a/transport/hysteria/core/client.go b/transport/hysteria/core/client.go index 97c9225e..60db8fdf 100644 --- a/transport/hysteria/core/client.go +++ b/transport/hysteria/core/client.go @@ -19,7 +19,7 @@ import ( "github.com/lunixbochs/struc" "github.com/metacubex/quic-go" "github.com/metacubex/quic-go/congestion" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) var ( @@ -405,7 +405,7 @@ func (c *quicPktConn) WriteTo(p []byte, addr string) error { var errSize *quic.DatagramTooLargeError if errors.As(err, &errSize) { // need to frag - msg.MsgID = uint16(fastrand.Intn(0xFFFF)) + 1 // msgID must be > 0 when fragCount > 1 + msg.MsgID = uint16(randv2.IntN(0xFFFF)) + 1 // msgID must be > 0 when fragCount > 1 fragMsgs := fragUDPMessage(msg, int(errSize.MaxDatagramPayloadSize)) for _, fragMsg := range fragMsgs { msgBuf.Reset() diff --git a/transport/hysteria/obfs/xplus.go b/transport/hysteria/obfs/xplus.go index 171bf281..4d1e3f29 100644 --- a/transport/hysteria/obfs/xplus.go +++ b/transport/hysteria/obfs/xplus.go @@ -1,9 +1,8 @@ package obfs import ( + "crypto/rand" "crypto/sha256" - - "github.com/zhangyunhao116/fastrand" ) // [salt][obfuscated payload] @@ -35,7 +34,7 @@ func (x *XPlusObfuscator) Deobfuscate(in []byte, out []byte) int { } func (x *XPlusObfuscator) Obfuscate(in []byte, out []byte) int { - _, _ = fastrand.Read(out[:saltLen]) // salt + _, _ = rand.Read(out[:saltLen]) // salt // Obfuscate the payload key := sha256.Sum256(append(x.Key, out[:saltLen]...)) for i, c := range in { diff --git a/transport/simple-obfs/http.go b/transport/simple-obfs/http.go index a38b1d69..9c3f8e00 100644 --- a/transport/simple-obfs/http.go +++ b/transport/simple-obfs/http.go @@ -2,6 +2,7 @@ package obfs import ( "bytes" + "crypto/rand" "encoding/base64" "fmt" "io" @@ -10,7 +11,7 @@ import ( "github.com/metacubex/mihomo/common/pool" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) // HTTPObfs is shadowsocks http simple-obfs implementation @@ -64,12 +65,12 @@ func (ho *HTTPObfs) Read(b []byte) (int, error) { func (ho *HTTPObfs) Write(b []byte) (int, error) { if ho.firstRequest { randBytes := make([]byte, 16) - fastrand.Read(randBytes) + rand.Read(randBytes) req, err := http.NewRequest("GET", fmt.Sprintf("http://%s/", ho.host), bytes.NewBuffer(b[:])) if err != nil { return 0, err } - req.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", fastrand.Int()%54, fastrand.Int()%2)) + req.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", randv2.Int()%54, randv2.Int()%2)) req.Header.Set("Upgrade", "websocket") req.Header.Set("Connection", "Upgrade") req.Host = ho.host diff --git a/transport/simple-obfs/tls.go b/transport/simple-obfs/tls.go index 78317f0a..a0cbc350 100644 --- a/transport/simple-obfs/tls.go +++ b/transport/simple-obfs/tls.go @@ -2,14 +2,13 @@ package obfs import ( "bytes" + "crypto/rand" "encoding/binary" "io" "net" "time" "github.com/metacubex/mihomo/common/pool" - - "github.com/zhangyunhao116/fastrand" ) const ( @@ -127,8 +126,8 @@ func NewTLSObfs(conn net.Conn, server string) net.Conn { func makeClientHelloMsg(data []byte, server string) []byte { random := make([]byte, 28) sessionID := make([]byte, 32) - fastrand.Read(random) - fastrand.Read(sessionID) + rand.Read(random) + rand.Read(sessionID) buf := &bytes.Buffer{} diff --git a/transport/ssr/obfs/http_simple.go b/transport/ssr/obfs/http_simple.go index 359ca342..d59b490d 100644 --- a/transport/ssr/obfs/http_simple.go +++ b/transport/ssr/obfs/http_simple.go @@ -10,7 +10,7 @@ import ( "github.com/metacubex/mihomo/common/pool" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) func init() { @@ -82,7 +82,7 @@ func (c *httpConn) Write(b []byte) (int, error) { bLength := len(b) headDataLength := bLength if bLength-headLength > 64 { - headDataLength = headLength + fastrand.Intn(65) + headDataLength = headLength + randv2.IntN(65) } headData := b[:headDataLength] b = b[headDataLength:] @@ -100,7 +100,7 @@ func (c *httpConn) Write(b []byte) (int, error) { } } hosts := strings.Split(host, ",") - host = hosts[fastrand.Intn(len(hosts))] + host = hosts[randv2.IntN(len(hosts))] buf := pool.GetBuffer() defer pool.PutBuffer(buf) @@ -119,7 +119,7 @@ func (c *httpConn) Write(b []byte) (int, error) { buf.WriteString(body + "\r\n\r\n") } else { buf.WriteString("User-Agent: ") - buf.WriteString(userAgent[fastrand.Intn(len(userAgent))]) + buf.WriteString(userAgent[randv2.IntN(len(userAgent))]) buf.WriteString("\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.8\r\nAccept-Encoding: gzip, deflate\r\n") if c.post { packBoundary(buf) @@ -147,7 +147,7 @@ func packBoundary(buf *bytes.Buffer) { buf.WriteString("Content-Type: multipart/form-data; boundary=") set := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" for i := 0; i < 32; i++ { - buf.WriteByte(set[fastrand.Intn(62)]) + buf.WriteByte(set[randv2.IntN(62)]) } buf.WriteString("\r\n") } diff --git a/transport/ssr/obfs/random_head.go b/transport/ssr/obfs/random_head.go index 9a2072fe..a5ad2dab 100644 --- a/transport/ssr/obfs/random_head.go +++ b/transport/ssr/obfs/random_head.go @@ -1,13 +1,14 @@ package obfs import ( + "crypto/rand" "encoding/binary" "hash/crc32" "net" "github.com/metacubex/mihomo/common/pool" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) func init() { @@ -54,10 +55,10 @@ func (c *randomHeadConn) Write(b []byte) (int, error) { c.buf = append(c.buf, b...) if !c.hasSentHeader { c.hasSentHeader = true - dataLength := fastrand.Intn(96) + 4 + dataLength := randv2.IntN(96) + 4 buf := pool.Get(dataLength + 4) defer pool.Put(buf) - fastrand.Read(buf[:dataLength]) + rand.Read(buf[:dataLength]) binary.LittleEndian.PutUint32(buf[dataLength:], 0xffffffff-crc32.ChecksumIEEE(buf[:dataLength])) _, err := c.Conn.Write(buf) return len(b), err diff --git a/transport/ssr/obfs/tls1.2_ticket_auth.go b/transport/ssr/obfs/tls1.2_ticket_auth.go index d5e3ca88..d5955edc 100644 --- a/transport/ssr/obfs/tls1.2_ticket_auth.go +++ b/transport/ssr/obfs/tls1.2_ticket_auth.go @@ -3,6 +3,7 @@ package obfs import ( "bytes" "crypto/hmac" + "crypto/rand" "encoding/binary" "net" "strings" @@ -11,7 +12,7 @@ import ( "github.com/metacubex/mihomo/common/pool" "github.com/metacubex/mihomo/transport/ssr/tools" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) func init() { @@ -26,7 +27,7 @@ type tls12Ticket struct { func newTLS12Ticket(b *Base) Obfs { r := &tls12Ticket{Base: b, authData: &authData{}} - fastrand.Read(r.clientID[:]) + rand.Read(r.clientID[:]) return r } @@ -91,7 +92,7 @@ func (c *tls12TicketConn) Write(b []byte) (int, error) { buf := pool.GetBuffer() defer pool.PutBuffer(buf) for len(b) > 2048 { - size := fastrand.Intn(4096) + 100 + size := randv2.IntN(4096) + 100 if len(b) < size { size = len(b) } @@ -197,7 +198,7 @@ func packSNIData(buf *bytes.Buffer, u string) { } func (c *tls12TicketConn) packTicketBuf(buf *bytes.Buffer, u string) { - length := 16 * (fastrand.Intn(17) + 8) + length := 16 * (randv2.IntN(17) + 8) buf.Write([]byte{0, 0x23}) binary.Write(buf, binary.BigEndian, uint16(length)) tools.AppendRandBytes(buf, length) @@ -222,6 +223,6 @@ func (t *tls12Ticket) getHost() string { host = "" } hosts := strings.Split(host, ",") - host = hosts[fastrand.Intn(len(hosts))] + host = hosts[randv2.IntN(len(hosts))] return host } diff --git a/transport/ssr/protocol/auth_aes128_sha1.go b/transport/ssr/protocol/auth_aes128_sha1.go index 6ee4160e..cfd55510 100644 --- a/transport/ssr/protocol/auth_aes128_sha1.go +++ b/transport/ssr/protocol/auth_aes128_sha1.go @@ -2,6 +2,7 @@ package protocol import ( "bytes" + "crypto/rand" "encoding/binary" "math" "net" @@ -13,7 +14,7 @@ import ( "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/transport/ssr/tools" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) type ( @@ -66,7 +67,7 @@ func (a *authAES128) initUserData() { } if len(a.userKey) == 0 { a.userKey = a.Key - fastrand.Read(a.userID[:]) + rand.Read(a.userID[:]) } } @@ -200,7 +201,7 @@ func (a *authAES128) packData(poolBuf *bytes.Buffer, data []byte, fullDataLength } func trapezoidRandom(max int, d float64) int { - base := fastrand.Float64() + base := randv2.Float64() if d-0 > 1e-6 { a := 1 - d base = (math.Sqrt(a*a+4*d*base) - a) / (2 * d) @@ -221,10 +222,10 @@ func (a *authAES128) getRandDataLengthForPackData(dataLength, fullDataLength int if revLength > -1460 { return trapezoidRandom(revLength+1460, -0.3) } - return fastrand.Intn(32) + return randv2.IntN(32) } if dataLength > 900 { - return fastrand.Intn(revLength) + return randv2.IntN(revLength) } return trapezoidRandom(revLength, -0.3) } @@ -249,7 +250,7 @@ func (a *authAES128) packAuthData(poolBuf *bytes.Buffer, data []byte) { copy(macKey, a.iv) copy(macKey[len(a.iv):], a.Key) - poolBuf.WriteByte(byte(fastrand.Intn(256))) + poolBuf.WriteByte(byte(randv2.IntN(256))) poolBuf.Write(a.hmac(macKey, poolBuf.Bytes())[:6]) poolBuf.Write(a.userID[:]) err := a.authData.putEncryptedData(poolBuf, a.userKey, [2]int{packedAuthDataLength, randDataLength}, a.salt) @@ -265,9 +266,9 @@ func (a *authAES128) packAuthData(poolBuf *bytes.Buffer, data []byte) { func (a *authAES128) getRandDataLengthForPackAuthData(size int) int { if size > 400 { - return fastrand.Intn(512) + return randv2.IntN(512) } - return fastrand.Intn(1024) + return randv2.IntN(1024) } func (a *authAES128) packRandData(poolBuf *bytes.Buffer, size int) { diff --git a/transport/ssr/protocol/auth_sha1_v4.go b/transport/ssr/protocol/auth_sha1_v4.go index ed1a39f1..1c616c3f 100644 --- a/transport/ssr/protocol/auth_sha1_v4.go +++ b/transport/ssr/protocol/auth_sha1_v4.go @@ -11,7 +11,7 @@ import ( "github.com/metacubex/mihomo/common/pool" "github.com/metacubex/mihomo/transport/ssr/tools" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) func init() { @@ -178,7 +178,7 @@ func (a *authSHA1V4) getRandDataLength(size int) int { return 0 } if size > 400 { - return fastrand.Intn(256) + return randv2.IntN(256) } - return fastrand.Intn(512) + return randv2.IntN(512) } diff --git a/transport/ssr/protocol/base.go b/transport/ssr/protocol/base.go index e26a6587..79870177 100644 --- a/transport/ssr/protocol/base.go +++ b/transport/ssr/protocol/base.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/aes" "crypto/cipher" + "crypto/rand" "encoding/base64" "encoding/binary" "sync" @@ -13,7 +14,7 @@ import ( "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/transport/shadowsocks/core" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) type Base struct { @@ -38,8 +39,8 @@ func (a *authData) next() *authData { a.mutex.Lock() defer a.mutex.Unlock() if a.connectionID > 0xff000000 || a.connectionID == 0 { - fastrand.Read(a.clientID[:]) - a.connectionID = fastrand.Uint32() & 0xffffff + rand.Read(a.clientID[:]) + a.connectionID = randv2.Uint32() & 0xffffff } a.connectionID++ copy(r.clientID[:], a.clientID[:]) diff --git a/transport/ssr/protocol/protocol.go b/transport/ssr/protocol/protocol.go index a04e6bd4..ad6bf6ba 100644 --- a/transport/ssr/protocol/protocol.go +++ b/transport/ssr/protocol/protocol.go @@ -8,7 +8,7 @@ import ( N "github.com/metacubex/mihomo/common/net" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) var ( @@ -71,7 +71,7 @@ func getHeadSize(b []byte, defaultValue int) int { func getDataLength(b []byte) int { bLength := len(b) - dataLength := getHeadSize(b, 30) + fastrand.Intn(32) + dataLength := getHeadSize(b, 30) + randv2.IntN(32) if bLength < dataLength { return bLength } diff --git a/transport/tuic/congestion/bbr_sender.go b/transport/tuic/congestion/bbr_sender.go index 6cea9355..5a863362 100644 --- a/transport/tuic/congestion/bbr_sender.go +++ b/transport/tuic/congestion/bbr_sender.go @@ -9,7 +9,7 @@ import ( "github.com/metacubex/quic-go" "github.com/metacubex/quic-go/congestion" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) const ( @@ -716,7 +716,7 @@ func (b *bbrSender) EnterProbeBandwidthMode(now time.Time) { // Pick a random offset for the gain cycle out of {0, 2..7} range. 1 is // excluded because in that case increased gain and decreased gain would not // follow each other. - b.cycleCurrentOffset = fastrand.Int() % (GainCycleLength - 1) + b.cycleCurrentOffset = randv2.Int() % (GainCycleLength - 1) if b.cycleCurrentOffset >= 1 { b.cycleCurrentOffset += 1 } diff --git a/transport/tuic/congestion_v2/bbr_sender.go b/transport/tuic/congestion_v2/bbr_sender.go index 54705978..ed29bd7e 100644 --- a/transport/tuic/congestion_v2/bbr_sender.go +++ b/transport/tuic/congestion_v2/bbr_sender.go @@ -9,7 +9,7 @@ import ( "github.com/metacubex/quic-go" "github.com/metacubex/quic-go/congestion" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) // BbrSender implements BBR congestion control algorithm. BBR aims to estimate @@ -620,7 +620,7 @@ func (b *bbrSender) enterProbeBandwidthMode(now time.Time) { // Pick a random offset for the gain cycle out of {0, 2..7} range. 1 is // excluded because in that case increased gain and decreased gain would not // follow each other. - b.cycleCurrentOffset = int(fastrand.Int31n(congestion.PacketsPerConnectionID)) % (gainCycleLength - 1) + b.cycleCurrentOffset = int(randv2.Int32N(congestion.PacketsPerConnectionID)) % (gainCycleLength - 1) if b.cycleCurrentOffset >= 1 { b.cycleCurrentOffset += 1 } diff --git a/transport/tuic/v4/client.go b/transport/tuic/v4/client.go index 5c9c889c..62b419b7 100644 --- a/transport/tuic/v4/client.go +++ b/transport/tuic/v4/client.go @@ -20,8 +20,8 @@ import ( "github.com/metacubex/mihomo/transport/tuic/common" "github.com/metacubex/quic-go" + "github.com/metacubex/randv2" "github.com/puzpuzpuz/xsync/v3" - "github.com/zhangyunhao116/fastrand" ) type ClientOption struct { @@ -367,7 +367,7 @@ func (t *clientImpl) ListenPacketWithDialer(ctx context.Context, metadata *C.Met pipe1, pipe2 := N.Pipe() var connId uint32 for { - connId = fastrand.Uint32() + connId = randv2.Uint32() _, loaded := t.udpInputMap.LoadOrStore(connId, pipe1) if !loaded { break diff --git a/transport/tuic/v5/client.go b/transport/tuic/v5/client.go index 89454add..a3c13d2b 100644 --- a/transport/tuic/v5/client.go +++ b/transport/tuic/v5/client.go @@ -20,8 +20,8 @@ import ( "github.com/metacubex/mihomo/transport/tuic/common" "github.com/metacubex/quic-go" + "github.com/metacubex/randv2" "github.com/puzpuzpuz/xsync/v3" - "github.com/zhangyunhao116/fastrand" ) type ClientOption struct { @@ -351,7 +351,7 @@ func (t *clientImpl) ListenPacketWithDialer(ctx context.Context, metadata *C.Met pipe1, pipe2 := N.Pipe() var connId uint16 for { - connId = uint16(fastrand.Intn(0xFFFF)) + connId = uint16(randv2.IntN(0xFFFF)) _, loaded := t.udpInputMap.LoadOrStore(connId, pipe1) if !loaded { break diff --git a/transport/tuic/v5/packet.go b/transport/tuic/v5/packet.go index 5608db81..8281a11e 100644 --- a/transport/tuic/v5/packet.go +++ b/transport/tuic/v5/packet.go @@ -12,7 +12,7 @@ import ( "github.com/metacubex/mihomo/transport/tuic/common" "github.com/metacubex/quic-go" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) type quicStreamPacketConn struct { @@ -157,7 +157,7 @@ func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro if err != nil { return } - pktId := uint16(fastrand.Uint32()) + pktId := uint16(randv2.Uint32()) packet := NewPacket(q.connId, pktId, 1, 0, uint16(len(p)), address, p) switch q.udpRelayMode { case common.QUIC: diff --git a/transport/vless/vision/padding.go b/transport/vless/vision/padding.go index e5f9dc85..dd9cb261 100644 --- a/transport/vless/vision/padding.go +++ b/transport/vless/vision/padding.go @@ -8,7 +8,7 @@ import ( "github.com/metacubex/mihomo/log" "github.com/gofrs/uuid/v5" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) const ( @@ -25,9 +25,9 @@ func WriteWithPadding(buffer *buf.Buffer, p []byte, command byte, userUUID *uuid if contentLen < 900 { if paddingTLS { //log.Debugln("long padding") - paddingLen = fastrand.Int31n(500) + 900 - contentLen + paddingLen = randv2.Int32N(500) + 900 - contentLen } else { - paddingLen = fastrand.Int31n(256) + paddingLen = randv2.Int32N(256) } } if userUUID != nil { @@ -49,9 +49,9 @@ func ApplyPadding(buffer *buf.Buffer, command byte, userUUID *uuid.UUID, padding if contentLen < 900 { if paddingTLS { //log.Debugln("long padding") - paddingLen = fastrand.Int31n(500) + 900 - contentLen + paddingLen = randv2.Int32N(500) + 900 - contentLen } else { - paddingLen = fastrand.Int31n(256) + paddingLen = randv2.Int32N(256) } } diff --git a/transport/vmess/conn.go b/transport/vmess/conn.go index 292137ab..b65a447d 100644 --- a/transport/vmess/conn.go +++ b/transport/vmess/conn.go @@ -6,6 +6,7 @@ import ( "crypto/cipher" "crypto/hmac" "crypto/md5" + "crypto/rand" "crypto/sha256" "encoding/binary" "errors" @@ -14,7 +15,7 @@ import ( "net" "time" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" "golang.org/x/crypto/chacha20poly1305" ) @@ -72,7 +73,7 @@ func (vc *Conn) sendRequest() error { buf.WriteByte(vc.respV) buf.WriteByte(OptionChunkStream) - p := fastrand.Intn(16) + p := randv2.IntN(16) // P Sec Reserve Cmd buf.WriteByte(byte(p<<4) | byte(vc.security)) buf.WriteByte(0) @@ -90,7 +91,7 @@ func (vc *Conn) sendRequest() error { // padding if p > 0 { padding := make([]byte, p) - fastrand.Read(padding) + rand.Read(padding) buf.Write(padding) } @@ -196,7 +197,7 @@ func hashTimestamp(t time.Time) []byte { // newConn return a Conn instance func newConn(conn net.Conn, id *ID, dst *DstAddr, security Security, isAead bool) (*Conn, error) { randBytes := make([]byte, 33) - fastrand.Read(randBytes) + rand.Read(randBytes) reqBodyIV := make([]byte, 16) reqBodyKey := make([]byte, 16) copy(reqBodyIV[:], randBytes[:16]) diff --git a/transport/vmess/h2.go b/transport/vmess/h2.go index f91c2766..a39ec5d9 100644 --- a/transport/vmess/h2.go +++ b/transport/vmess/h2.go @@ -7,7 +7,7 @@ import ( "net/http" "net/url" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" "golang.org/x/net/http2" ) @@ -27,7 +27,7 @@ type H2Config struct { func (hc *h2Conn) establishConn() error { preader, pwriter := io.Pipe() - host := hc.cfg.Hosts[fastrand.Intn(len(hc.cfg.Hosts))] + host := hc.cfg.Hosts[randv2.IntN(len(hc.cfg.Hosts))] path := hc.cfg.Path // TODO: connect use VMess Host instead of H2 Host req := http.Request{ diff --git a/transport/vmess/http.go b/transport/vmess/http.go index 4a1b93ec..3c66fe6b 100644 --- a/transport/vmess/http.go +++ b/transport/vmess/http.go @@ -11,7 +11,7 @@ import ( "github.com/metacubex/mihomo/common/utils" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) type httpConn struct { @@ -59,10 +59,10 @@ func (hc *httpConn) Write(b []byte) (int, error) { return -1, errors.New("path is empty") } - path := hc.cfg.Path[fastrand.Intn(len(hc.cfg.Path))] + path := hc.cfg.Path[randv2.IntN(len(hc.cfg.Path))] host := hc.cfg.Host if header := hc.cfg.Headers["Host"]; len(header) != 0 { - host = header[fastrand.Intn(len(header))] + host = header[randv2.IntN(len(header))] } u := fmt.Sprintf("http://%s%s", net.JoinHostPort(host, "80"), path) @@ -71,7 +71,7 @@ func (hc *httpConn) Write(b []byte) (int, error) { return 0, err } for key, list := range hc.cfg.Headers { - req.Header.Set(key, list[fastrand.Intn(len(list))]) + req.Header.Set(key, list[randv2.IntN(len(list))]) } req.ContentLength = int64(len(b)) if err := req.Write(hc.Conn); err != nil { diff --git a/transport/vmess/vmess.go b/transport/vmess/vmess.go index 7c587c6a..22c77ab7 100644 --- a/transport/vmess/vmess.go +++ b/transport/vmess/vmess.go @@ -8,7 +8,7 @@ import ( "github.com/metacubex/mihomo/common/utils" "github.com/gofrs/uuid/v5" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) // Version of vmess @@ -78,7 +78,7 @@ type Config struct { // StreamConn return a Conn with net.Conn and DstAddr func (c *Client) StreamConn(conn net.Conn, dst *DstAddr) (net.Conn, error) { - r := fastrand.Intn(len(c.user)) + r := randv2.IntN(len(c.user)) return newConn(conn, c.user[r], dst, c.security, c.isAead) } diff --git a/transport/vmess/websocket.go b/transport/vmess/websocket.go index f6914199..ca3a57b7 100644 --- a/transport/vmess/websocket.go +++ b/transport/vmess/websocket.go @@ -4,6 +4,7 @@ import ( "bufio" "bytes" "context" + "crypto/rand" "crypto/sha1" "crypto/tls" "encoding/base64" @@ -25,7 +26,7 @@ import ( "github.com/gobwas/ws" "github.com/gobwas/ws/wsutil" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) type websocketConn struct { @@ -150,7 +151,7 @@ func (wsc *websocketConn) WriteBuffer(buffer *buf.Buffer) error { } if wsc.state.ClientSide() { - maskKey := fastrand.Uint32() + maskKey := randv2.Uint32() binary.LittleEndian.PutUint32(header[1+payloadBitLength:], maskKey) N.MaskWebSocket(maskKey, data) } @@ -398,7 +399,7 @@ func streamWebsocketConn(ctx context.Context, conn net.Conn, c *WebsocketConfig, const nonceKeySize = 16 // NOTE: bts does not escape. bts := make([]byte, nonceKeySize) - if _, err = fastrand.Read(bts); err != nil { + if _, err = rand.Read(bts); err != nil { return nil, fmt.Errorf("rand read error: %w", err) } secKey = base64.StdEncoding.EncodeToString(bts) From 59fd3cffe3767b51428cfd70473e4c295aa88089 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Fri, 31 May 2024 17:02:31 +0800 Subject: [PATCH 102/311] ci: fix arm package create --- .github/workflows/build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 42023755..9b8e000a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -206,6 +206,10 @@ jobs: sudo apt-get install dpkg if [ "${{matrix.jobs.abi}}" = "1" ]; then ARCH=loongarch64 + elif [ "${{matrix.jobs.goarm}}" = "7" ]; then + ARCH=armhf + elif [ "${{matrix.jobs.goarch}}" = "arm" ]; then + ARCH=armel else ARCH=${{matrix.jobs.goarch}} fi From be3d121ec633a0665d31fc66bff3707893910bcb Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 1 Jun 2024 13:34:02 +0800 Subject: [PATCH 103/311] fix: darwin calculate correct tunIndex --- listener/sing_tun/server.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 09bf308c..a5edb77f 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -24,6 +24,7 @@ import ( E "github.com/sagernet/sing/common/exceptions" F "github.com/sagernet/sing/common/format" "github.com/sagernet/sing/common/ranges" + "golang.org/x/exp/slices" ) var InterfaceName = "Meta" @@ -60,19 +61,21 @@ func CalculateInterfaceName(name string) (tunName string) { return } tunIndex := 0 - indexSet := make(map[int]struct{}) + indexArr := make([]int, 0, len(interfaces)) for _, netInterface := range interfaces { if strings.HasPrefix(netInterface.Name, tunName) { index, parseErr := strconv.ParseInt(netInterface.Name[len(tunName):], 10, 16) if parseErr == nil { - indexSet[int(index)] = struct{}{} + indexArr = append(indexArr, int(index)) } } } - for index := range indexSet { + slices.Sort(indexArr) + indexArr = slices.Compact(indexArr) + for _, index := range indexArr { if index == tunIndex { tunIndex += 1 - } else { // indexSet already sorted and distinct, so this tunIndex nobody used + } else { // indexArr already sorted and distinct, so this tunIndex nobody used break } } From 7b3c9e94e6bfb138b241e47eb83c5d019cd1e728 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sun, 2 Jun 2024 02:36:15 +0800 Subject: [PATCH 104/311] chore: Better package name handling on Android --- component/process/process_linux.go | 54 +++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/component/process/process_linux.go b/component/process/process_linux.go index f8174495..4667104c 100644 --- a/component/process/process_linux.go +++ b/component/process/process_linux.go @@ -2,19 +2,23 @@ package process import ( "bytes" + "context" "encoding/binary" "fmt" "net/netip" "os" - "path" "path/filepath" "runtime" "strings" + "sync" "syscall" "unicode" "unsafe" + "github.com/metacubex/mihomo/log" + "github.com/mdlayher/netlink" + tun "github.com/metacubex/sing-tun" "golang.org/x/sys/unix" ) @@ -59,6 +63,19 @@ type inetDiagResponse struct { INode uint32 } +type MyCallback struct{} + +var ( + packageManager tun.PackageManager + once sync.Once +) + +func (cb *MyCallback) OnPackagesUpdated(packageCount int, sharedCount int) {} + +func (cb *MyCallback) NewError(ctx context.Context, err error) { + log.Warnln("%s", err) +} + func findProcessName(network string, ip netip.Addr, srcPort int) (uint32, string, error) { uid, inode, err := resolveSocketByNetlink(network, ip, srcPort) if err != nil { @@ -162,12 +179,7 @@ func resolveProcessNameByProcSearch(inode, uid uint32) (string, error) { } if runtime.GOOS == "android" { if bytes.Equal(buffer[:n], socket) { - cmdline, err := os.ReadFile(path.Join(processPath, "cmdline")) - if err != nil { - return "", err - } - - return splitCmdline(cmdline), nil + return findPackageName(uid), nil } } else { if bytes.Equal(buffer[:n], socket) { @@ -181,17 +193,29 @@ func resolveProcessNameByProcSearch(inode, uid uint32) (string, error) { return "", fmt.Errorf("process of uid(%d),inode(%d) not found", uid, inode) } -func splitCmdline(cmdline []byte) string { - cmdline = bytes.Trim(cmdline, " ") - - idx := bytes.IndexFunc(cmdline, func(r rune) bool { - return unicode.IsControl(r) || unicode.IsSpace(r) || r == ':' +func findPackageName(uid uint32) string { + once.Do(func() { + callback := &MyCallback{} + var err error + packageManager, err = tun.NewPackageManager(callback) + if err != nil { + log.Warnln("%s", err) + } + err = packageManager.Start() + if err != nil { + log.Warnln("%s", err) + return + } }) - if idx == -1 { - return filepath.Base(string(cmdline)) + if sharedPackage, loaded := packageManager.SharedPackageByID(uid % 100000); loaded { + fmt.Println(loaded) + return sharedPackage } - return filepath.Base(string(cmdline[:idx])) + if packageName, loaded := packageManager.PackageByID(uid % 100000); loaded { + return packageName + } + return "" } func isPid(s string) bool { From 063836fe5dd3add744337b64512d41f2d12bd93a Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 5 Jun 2024 11:56:11 +0800 Subject: [PATCH 105/311] chore: sync hysteria2 bbr changes https://github.com/apernet/hysteria/commit/e0e75c46309eec2531b8613370cfbada259b5af3 --- transport/tuic/congestion_v2/bbr_sender.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/transport/tuic/congestion_v2/bbr_sender.go b/transport/tuic/congestion_v2/bbr_sender.go index ed29bd7e..d8852fbc 100644 --- a/transport/tuic/congestion_v2/bbr_sender.go +++ b/transport/tuic/congestion_v2/bbr_sender.go @@ -62,7 +62,7 @@ const ( // Flag. defaultStartupFullLossCount = 8 quicBbr2DefaultLossThreshold = 0.02 - maxBbrBurstPackets = 3 + maxBbrBurstPackets = 10 ) type bbrMode int @@ -334,6 +334,8 @@ func (b *bbrSender) OnPacketSent( } b.sampler.OnPacketSent(sentTime, packetNumber, bytes, bytesInFlight, isRetransmittable) + + b.maybeAppLimited(bytesInFlight) } // CanSend implements the SendAlgorithm interface. @@ -413,8 +415,6 @@ func (b *bbrSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, even // packet in lost_packets. var lastPacketSendState sendTimeState - b.maybeApplimited(priorInFlight) - // Update bytesInFlight b.bytesInFlight = priorInFlight for _, p := range ackedPackets { @@ -541,7 +541,7 @@ func (b *bbrSender) setDrainGain(drainGain float64) { b.drainGain = drainGain } -// What's the current estimated bandwidth in bytes per second. +// Get the current bandwidth estimate. Note that Bandwidth is in bits per second. func (b *bbrSender) bandwidthEstimate() Bandwidth { return b.maxBandwidth.GetBest() } @@ -700,14 +700,13 @@ func (b *bbrSender) checkIfFullBandwidthReached(lastPacketSendState *sendTimeSta } } -func (b *bbrSender) maybeApplimited(bytesInFlight congestion.ByteCount) { +func (b *bbrSender) maybeAppLimited(bytesInFlight congestion.ByteCount) { congestionWindow := b.GetCongestionWindow() if bytesInFlight >= congestionWindow { return } availableBytes := congestionWindow - bytesInFlight - drainLimited := b.mode == bbrModeDrain && bytesInFlight > congestionWindow/2 - if !drainLimited || availableBytes > maxBbrBurstPackets*b.maxDatagramSize { + if availableBytes > maxBbrBurstPackets*b.maxDatagramSize { b.sampler.OnAppLimited() } } From 0d4e57cb21a1b6fe59bdba2833dad8cef630b335 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 7 Jun 2024 19:17:01 +0800 Subject: [PATCH 106/311] chore: update quic-go to 0.45.0 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index bc32a633..a6664b59 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.44.1-0.20240521004242-fcd70d587e22 + github.com/metacubex/quic-go v0.45.1-0.20240607133845-b24f02b35a22 github.com/metacubex/randv2 v0.2.0 github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.6 diff --git a/go.sum b/go.sum index 6dbbfd77..c75275bc 100644 --- a/go.sum +++ b/go.sum @@ -104,8 +104,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-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= -github.com/metacubex/quic-go v0.44.1-0.20240521004242-fcd70d587e22 h1:hsQ0b2A509b6ubnLtLOcUgZ8vOb+d/667zVEJ1T2fao= -github.com/metacubex/quic-go v0.44.1-0.20240521004242-fcd70d587e22/go.mod h1:88wAATpevav4xdy5N8oejQ2cbbI6EcLYEklFeo+qywA= +github.com/metacubex/quic-go v0.45.1-0.20240607133845-b24f02b35a22 h1:dKYoWnrB5bbCMoMQit4INUDKiDcjc0Azsm3GltYf9Pw= +github.com/metacubex/quic-go v0.45.1-0.20240607133845-b24f02b35a22/go.mod h1:Yza2H7Ax1rxWPUcJx0vW+oAt9EsPuSiyQFhFabUPzwU= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1 h1:7hDHLTmjgtRoAp59STwPQpe5Pinwi4cWex+FB3Ohvco= From cacfefad4b5925b07d2340bcdee1240197f19d12 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 10 Jun 2024 08:48:23 +0800 Subject: [PATCH 107/311] fix: quic-go cached dial error --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a6664b59..5d109d8d 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.45.1-0.20240607133845-b24f02b35a22 + github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e github.com/metacubex/randv2 v0.2.0 github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.6 diff --git a/go.sum b/go.sum index c75275bc..5c8219c2 100644 --- a/go.sum +++ b/go.sum @@ -104,8 +104,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-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= -github.com/metacubex/quic-go v0.45.1-0.20240607133845-b24f02b35a22 h1:dKYoWnrB5bbCMoMQit4INUDKiDcjc0Azsm3GltYf9Pw= -github.com/metacubex/quic-go v0.45.1-0.20240607133845-b24f02b35a22/go.mod h1:Yza2H7Ax1rxWPUcJx0vW+oAt9EsPuSiyQFhFabUPzwU= +github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e h1:bLYn3GuRvWDcBDAkIv5kUYIhzHwafDVq635BuybnKqI= +github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e/go.mod h1:Yza2H7Ax1rxWPUcJx0vW+oAt9EsPuSiyQFhFabUPzwU= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1 h1:7hDHLTmjgtRoAp59STwPQpe5Pinwi4cWex+FB3Ohvco= From 10f8ba44345e24f26ec63bab8919fc287de7196d Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Wed, 12 Jun 2024 04:46:13 +0800 Subject: [PATCH 108/311] chore: Disable the loop back detector for CMFA --- adapter/outbound/direct.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index 1b01a576..09b9696b 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -4,14 +4,19 @@ import ( "context" "errors" "net/netip" + "os" + "strconv" N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/loopback" "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/constant/features" ) +var DisableLoopBackDetector, _ = strconv.ParseBool(os.Getenv("DISABLE_LOOPBACK_DETECTOR")) + type Direct struct { *Base loopBack *loopback.Detector @@ -24,8 +29,10 @@ type DirectOption struct { // DialContext implements C.ProxyAdapter func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { - if err := d.loopBack.CheckConn(metadata); err != nil { - return nil, err + if !features.CMFA && !DisableLoopBackDetector { + if err := d.loopBack.CheckConn(metadata); err != nil { + return nil, err + } } opts = append(opts, dialer.WithResolver(resolver.DefaultResolver)) c, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress(), d.Base.DialOptions(opts...)...) @@ -38,8 +45,10 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ... // ListenPacketContext implements C.ProxyAdapter func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { - if err := d.loopBack.CheckPacketConn(metadata); err != nil { - return nil, err + if !features.CMFA && !DisableLoopBackDetector { + if err := d.loopBack.CheckPacketConn(metadata); err != nil { + return nil, err + } } // net.UDPConn.WriteTo only working with *net.UDPAddr, so we need a net.UDPAddr if !metadata.Resolved() { From 5678131591afc4a7f60c1c7b6e34f189cf7ada22 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 12 Jun 2024 11:37:09 +0800 Subject: [PATCH 109/311] fix: wireguard server resolve when only a server in `peers` --- adapter/outbound/wireguard.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 976f3959..56ade71c 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -304,13 +304,16 @@ func (w *WireGuard) init(ctx context.Context) error { ipcConf := "private_key=" + w.option.PrivateKey if len(w.option.Peers) > 0 { for i, peer := range w.option.Peers { + ipcConf += "\npublic_key=" + peer.PublicKey destination, err := w.resolve(ctx, peer.Addr()) if err != nil { // !!! do not set initErr here !!! // let us can retry domain resolve in next time return E.Cause(err, "resolve endpoint domain for peer ", i) } - ipcConf += "\npublic_key=" + peer.PublicKey + if len(w.option.Peers) == 1 { // must call SetConnectAddr if isConnect == true + w.bind.SetConnectAddr(destination) + } ipcConf += "\nendpoint=" + destination.String() if peer.PreSharedKey != "" { ipcConf += "\npreshared_key=" + peer.PreSharedKey @@ -332,7 +335,7 @@ func (w *WireGuard) init(ctx context.Context) error { // let us can retry domain resolve in next time return E.Cause(err, "resolve endpoint domain") } - w.bind.SetConnectAddr(destination) + w.bind.SetConnectAddr(destination) // must call SetConnectAddr if isConnect == true ipcConf += "\nendpoint=" + destination.String() if w.option.PreSharedKey != "" { ipcConf += "\npreshared_key=" + w.option.PreSharedKey From f317baa8def8bbcdef0fa8259d12d8181ec8fe19 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 12 Jun 2024 15:25:34 +0800 Subject: [PATCH 110/311] feat: add `respect-rules` for dns --- config/config.go | 27 +++++++---- dns/util.go | 118 +++++++++++++++++++++++++++-------------------- docs/config.yaml | 10 +++- tunnel/tunnel.go | 6 +-- 4 files changed, 98 insertions(+), 63 deletions(-) diff --git a/config/config.go b/config/config.go index 74a2053e..fd12d2db 100644 --- a/config/config.go +++ b/config/config.go @@ -212,6 +212,7 @@ type RawDNS struct { IPv6Timeout uint `yaml:"ipv6-timeout" json:"ipv6-timeout"` UseHosts bool `yaml:"use-hosts" json:"use-hosts"` UseSystemHosts bool `yaml:"use-system-hosts" json:"use-system-hosts"` + RespectRules bool `yaml:"respect-rules" json:"respect-rules"` NameServer []string `yaml:"nameserver" json:"nameserver"` Fallback []string `yaml:"fallback" json:"fallback"` FallbackFilter RawFallbackFilter `yaml:"fallback-filter" json:"fallback-filter"` @@ -1039,7 +1040,7 @@ func hostWithDefaultPort(host string, defPort string) (string, error) { return net.JoinHostPort(hostname, port), nil } -func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error) { +func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns.NameServer, error) { var nameservers []dns.NameServer for idx, server := range servers { @@ -1114,6 +1115,10 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error) return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error()) } + if respectRules && len(proxyName) == 0 { + proxyName = dns.RespectRules + } + nameservers = append( nameservers, dns.NameServer{ @@ -1130,7 +1135,7 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error) func init() { dns.ParseNameServer = func(servers []string) ([]dns.NameServer, error) { // using by wireguard - return parseNameServer(servers, false) + return parseNameServer(servers, false, false) } } @@ -1156,7 +1161,7 @@ func parsePureDNSServer(server string) string { } } } -func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], ruleProviders map[string]providerTypes.RuleProvider, preferH3 bool) (*orderedmap.OrderedMap[string, []dns.NameServer], error) { +func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], ruleProviders map[string]providerTypes.RuleProvider, respectRules bool, preferH3 bool) (*orderedmap.OrderedMap[string, []dns.NameServer], error) { policy := orderedmap.New[string, []dns.NameServer]() updatedPolicy := orderedmap.New[string, any]() re := regexp.MustCompile(`[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?`) @@ -1202,7 +1207,7 @@ func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], rulePro if err != nil { return nil, err } - nameservers, err := parseNameServer(servers, preferH3) + nameservers, err := parseNameServer(servers, respectRules, preferH3) if err != nil { return nil, err } @@ -1296,6 +1301,10 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul return nil, fmt.Errorf("if DNS configuration is turned on, NameServer cannot be empty") } + if cfg.RespectRules && len(cfg.ProxyServerNameserver) == 0 { + return nil, fmt.Errorf("if “respect-rules” is turned on, “proxy-server-nameserver” cannot be empty") + } + dnsCfg := &DNS{ Enable: cfg.Enable, Listen: cfg.Listen, @@ -1310,26 +1319,26 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul }, } var err error - if dnsCfg.NameServer, err = parseNameServer(cfg.NameServer, cfg.PreferH3); err != nil { + if dnsCfg.NameServer, err = parseNameServer(cfg.NameServer, cfg.RespectRules, cfg.PreferH3); err != nil { return nil, err } - if dnsCfg.Fallback, err = parseNameServer(cfg.Fallback, cfg.PreferH3); err != nil { + if dnsCfg.Fallback, err = parseNameServer(cfg.Fallback, cfg.RespectRules, cfg.PreferH3); err != nil { return nil, err } - if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, ruleProviders, cfg.PreferH3); err != nil { + if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, ruleProviders, cfg.RespectRules, cfg.PreferH3); err != nil { return nil, err } - if dnsCfg.ProxyServerNameserver, err = parseNameServer(cfg.ProxyServerNameserver, cfg.PreferH3); err != nil { + if dnsCfg.ProxyServerNameserver, err = parseNameServer(cfg.ProxyServerNameserver, false, cfg.PreferH3); err != nil { return nil, err } if len(cfg.DefaultNameserver) == 0 { return nil, errors.New("default nameserver should have at least one nameserver") } - if dnsCfg.DefaultNameserver, err = parseNameServer(cfg.DefaultNameserver, cfg.PreferH3); err != nil { + if dnsCfg.DefaultNameserver, err = parseNameServer(cfg.DefaultNameserver, false, cfg.PreferH3); err != nil { return nil, err } // check default nameserver is pure ip addr diff --git a/dns/util.go b/dns/util.go index 516c63fb..9b84a507 100644 --- a/dns/util.go +++ b/dns/util.go @@ -7,7 +7,6 @@ import ( "fmt" "net" "net/netip" - "strconv" "strings" "time" @@ -175,6 +174,8 @@ func msgToDomain(msg *D.Msg) string { return "" } +const RespectRules = "RULES" + type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error) func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) dialHandler { @@ -183,54 +184,67 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts = append(opts, dialer.WithResolver(r)) return dialer.DialContext(ctx, network, addr, opts...) } else { - host, port, err := net.SplitHostPort(addr) + metadata := &C.Metadata{ + NetWork: C.TCP, + Type: C.INNER, + } + err := metadata.SetRemoteAddress(addr) // tcp can resolve host by remote if err != nil { return nil, err } - uintPort, err := strconv.ParseUint(port, 10, 16) - if err != nil { - return nil, err + if !strings.Contains(network, "tcp") { + metadata.NetWork = C.UDP + if !metadata.Resolved() { + // udp must resolve host first + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) + if err != nil { + return nil, err + } + metadata.DstIP = dstIP + } } + if proxyAdapter == nil { - var ok bool - proxyAdapter, ok = tunnel.Proxies()[proxyName] - if !ok { - opts = append(opts, dialer.WithInterface(proxyName)) + if proxyName == RespectRules { + if !metadata.Resolved() { + // resolve here before ResolveMetadata to avoid its inner resolver.ResolveIP + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) + if err != nil { + return nil, err + } + metadata.DstIP = dstIP + } + proxyAdapter, _, err = tunnel.ResolveMetadata(metadata) + if err != nil { + return nil, err + } + } else { + var ok bool + proxyAdapter, ok = tunnel.Proxies()[proxyName] + if !ok { + opts = append(opts, dialer.WithInterface(proxyName)) + } } } if strings.Contains(network, "tcp") { - // tcp can resolve host by remote - metadata := &C.Metadata{ - NetWork: C.TCP, - Host: host, - DstPort: uint16(uintPort), - } if proxyAdapter != nil { if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback - dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r) - if err != nil { - return nil, err + if !metadata.Resolved() { + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) + if err != nil { + return nil, err + } + metadata.DstIP = dstIP } - metadata.Host = "" - metadata.DstIP = dstIP + metadata.Host = "" // clear host to avoid double resolve in proxy } + log.Debugln("%s", metadata.RemoteAddress()) return proxyAdapter.DialContext(ctx, metadata, opts...) } opts = append(opts, dialer.WithResolver(r)) return dialer.DialContext(ctx, network, addr, opts...) } else { - // udp must resolve host first - dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r) - if err != nil { - return nil, err - } - metadata := &C.Metadata{ - NetWork: C.UDP, - Host: "", - DstIP: dstIP, - DstPort: uint16(uintPort), - } if proxyAdapter == nil { return dialer.DialContext(ctx, network, addr, opts...) } @@ -251,33 +265,37 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, } func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) { - host, port, err := net.SplitHostPort(addr) + metadata := &C.Metadata{ + NetWork: C.UDP, + } + err := metadata.SetRemoteAddress(addr) if err != nil { return nil, err } - uintPort, err := strconv.ParseUint(port, 10, 16) - if err != nil { - return nil, err + if !metadata.Resolved() { + // udp must resolve host first + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) + if err != nil { + return nil, err + } + metadata.DstIP = dstIP } + if proxyAdapter == nil { - var ok bool - proxyAdapter, ok = tunnel.Proxies()[proxyName] - if !ok { - opts = append(opts, dialer.WithInterface(proxyName)) + if proxyName == RespectRules { + proxyAdapter, _, err = tunnel.ResolveMetadata(metadata) + if err != nil { + return nil, err + } + } else { + var ok bool + proxyAdapter, ok = tunnel.Proxies()[proxyName] + if !ok { + opts = append(opts, dialer.WithInterface(proxyName)) + } } } - // udp must resolve host first - dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r) - if err != nil { - return nil, err - } - metadata := &C.Metadata{ - NetWork: C.UDP, - Host: "", - DstIP: dstIP, - DstPort: uint16(uintPort), - } if proxyAdapter == nil { return dialer.NewDialer(opts...).ListenPacket(ctx, network, "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort)) } diff --git a/docs/config.yaml b/docs/config.yaml index fe850163..bd263c14 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -209,7 +209,7 @@ tunnels: # one line config dns: cache-algorithm: arc enable: false # 关闭将使用系统 DNS - prefer-h3: true # 开启 DoH 支持 HTTP/3,将并发尝试 + prefer-h3: false # 是否开启 DoH 支持 HTTP/3,将并发尝试 listen: 0.0.0.0:53 # 开启 DNS 服务器监听 # ipv6: false # false 将返回 AAAA 的空结果 # ipv6-timeout: 300 # 单位:ms,内部双栈并发时,向上游查询 AAAA 时,等待 AAAA 的时间,默认 100ms @@ -227,6 +227,13 @@ dns: # use-hosts: true # 查询 hosts + # 配置后面的nameserver、fallback和nameserver-policy向dns服务器的连接过程是否遵守遵守rules规则 + # 如果为false(默认值)则这三部分的dns服务器在未特别指定的情况下会直连 + # 如果为true,将会按照rules的规则匹配链接方式(走代理或直连),如果有特别指定则任然以指定值为准 + # 仅当proxy-server-nameserver非空时可以开启此选项, 强烈不建议和prefer-h3一起使用 + # 此外,这三者配置中的dns服务器如果出现域名会采用default-nameserver配置项解析,也请确保正确配置default-nameserver + respect-rules: false + # 配置不使用 fake-ip 的域名 # fake-ip-filter: # - '*.lan' @@ -244,6 +251,7 @@ dns: - https://mozilla.cloudflare-dns.com/dns-query#DNS&h3=true # 指定策略组和使用 HTTP/3 - dhcp://en0 # dns from dhcp - quic://dns.adguard.com:784 # DNS over QUIC + # - '8.8.8.8#RULES' # 效果同respect-rules,但仅对该服务器生效 # - '8.8.8.8#en0' # 兼容指定 DNS 出口网卡 # 当配置 fallback 时,会查询 nameserver 中返回的 IP 是否为 CN,非必要配置 diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 608ab2c5..78628a56 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -278,7 +278,7 @@ func preHandleMetadata(metadata *C.Metadata) error { return nil } -func resolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err error) { +func ResolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err error) { if metadata.SpecialProxy != "" { var exist bool proxy, exist = proxies[metadata.SpecialProxy] @@ -375,7 +375,7 @@ func handleUDPConn(packet C.PacketAdapter) { cond.Broadcast() }() - proxy, rule, err := resolveMetadata(metadata) + proxy, rule, err := ResolveMetadata(metadata) if err != nil { log.Warnln("[UDP] Parse metadata failed: %s", err.Error()) return @@ -486,7 +486,7 @@ func handleTCPConn(connCtx C.ConnContext) { }() } - proxy, rule, err := resolveMetadata(metadata) + proxy, rule, err := ResolveMetadata(metadata) if err != nil { log.Warnln("[Metadata] parse failed: %s", err.Error()) return From 2b4741fbc71c1cc8ed1e4e94b65288337daf880b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 12 Jun 2024 17:09:21 +0800 Subject: [PATCH 111/311] chore: add inner dns proxied connection statistic to restful api --- dns/util.go | 56 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/dns/util.go b/dns/util.go index 9b84a507..f13279fa 100644 --- a/dns/util.go +++ b/dns/util.go @@ -18,6 +18,7 @@ import ( C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/tunnel" + "github.com/metacubex/mihomo/tunnel/statistic" D "github.com/miekg/dns" "github.com/samber/lo" @@ -204,6 +205,7 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, } } + var rule C.Rule if proxyAdapter == nil { if proxyName == RespectRules { if !metadata.Resolved() { @@ -214,7 +216,7 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, } metadata.DstIP = dstIP } - proxyAdapter, _, err = tunnel.ResolveMetadata(metadata) + proxyAdapter, rule, err = tunnel.ResolveMetadata(metadata) if err != nil { return nil, err } @@ -228,22 +230,30 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, } if strings.Contains(network, "tcp") { - if proxyAdapter != nil { - if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback - if !metadata.Resolved() { - dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) - if err != nil { - return nil, err - } - metadata.DstIP = dstIP - } - metadata.Host = "" // clear host to avoid double resolve in proxy - } - log.Debugln("%s", metadata.RemoteAddress()) - return proxyAdapter.DialContext(ctx, metadata, opts...) + if proxyAdapter == nil { + opts = append(opts, dialer.WithResolver(r)) + return dialer.DialContext(ctx, network, addr, opts...) } - opts = append(opts, dialer.WithResolver(r)) - return dialer.DialContext(ctx, network, addr, opts...) + + if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback + if !metadata.Resolved() { + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) + if err != nil { + return nil, err + } + metadata.DstIP = dstIP + } + metadata.Host = "" // clear host to avoid double resolve in proxy + } + + conn, err := proxyAdapter.DialContext(ctx, metadata, opts...) + if err != nil { + return nil, err + } + + conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, metadata, rule, 0, 0, false) + + return conn, nil } else { if proxyAdapter == nil { return dialer.DialContext(ctx, network, addr, opts...) @@ -258,6 +268,8 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, return nil, err } + packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) + return N.NewBindPacketConn(packetConn, metadata.UDPAddr()), nil } } @@ -281,9 +293,10 @@ func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName st metadata.DstIP = dstIP } + var rule C.Rule if proxyAdapter == nil { if proxyName == RespectRules { - proxyAdapter, _, err = tunnel.ResolveMetadata(metadata) + proxyAdapter, rule, err = tunnel.ResolveMetadata(metadata) if err != nil { return nil, err } @@ -304,7 +317,14 @@ func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName st return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter) } - return proxyAdapter.ListenPacketContext(ctx, metadata, opts...) + packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...) + if err != nil { + return nil, err + } + + packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) + + return packetConn, nil } func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, cache bool, err error) { From a5f25a22463110b87da06c46e777a029bad7894f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 12 Jun 2024 20:54:12 +0800 Subject: [PATCH 112/311] chore: code split --- dns/dial.go | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++++ dns/util.go | 157 ------------------------------------------------ 2 files changed, 169 insertions(+), 157 deletions(-) create mode 100644 dns/dial.go diff --git a/dns/dial.go b/dns/dial.go new file mode 100644 index 00000000..431707c5 --- /dev/null +++ b/dns/dial.go @@ -0,0 +1,169 @@ +package dns + +import ( + "context" + "fmt" + "net" + "net/netip" + "strings" + + N "github.com/metacubex/mihomo/common/net" + "github.com/metacubex/mihomo/component/dialer" + "github.com/metacubex/mihomo/component/resolver" + C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/tunnel" + "github.com/metacubex/mihomo/tunnel/statistic" +) + +const RespectRules = "RULES" + +type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error) + +func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) dialHandler { + return func(ctx context.Context, network, addr string) (net.Conn, error) { + if len(proxyName) == 0 && proxyAdapter == nil { + opts = append(opts, dialer.WithResolver(r)) + return dialer.DialContext(ctx, network, addr, opts...) + } else { + metadata := &C.Metadata{ + NetWork: C.TCP, + Type: C.INNER, + } + err := metadata.SetRemoteAddress(addr) // tcp can resolve host by remote + if err != nil { + return nil, err + } + if !strings.Contains(network, "tcp") { + metadata.NetWork = C.UDP + if !metadata.Resolved() { + // udp must resolve host first + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) + if err != nil { + return nil, err + } + metadata.DstIP = dstIP + } + } + + var rule C.Rule + if proxyAdapter == nil { + if proxyName == RespectRules { + if !metadata.Resolved() { + // resolve here before ResolveMetadata to avoid its inner resolver.ResolveIP + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) + if err != nil { + return nil, err + } + metadata.DstIP = dstIP + } + proxyAdapter, rule, err = tunnel.ResolveMetadata(metadata) + if err != nil { + return nil, err + } + } else { + var ok bool + proxyAdapter, ok = tunnel.Proxies()[proxyName] + if !ok { + opts = append(opts, dialer.WithInterface(proxyName)) + } + } + } + + if strings.Contains(network, "tcp") { + if proxyAdapter == nil { + opts = append(opts, dialer.WithResolver(r)) + return dialer.DialContext(ctx, network, addr, opts...) + } + + if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback + if !metadata.Resolved() { + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) + if err != nil { + return nil, err + } + metadata.DstIP = dstIP + } + metadata.Host = "" // clear host to avoid double resolve in proxy + } + + conn, err := proxyAdapter.DialContext(ctx, metadata, opts...) + if err != nil { + return nil, err + } + + conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, metadata, rule, 0, 0, false) + + return conn, nil + } else { + if proxyAdapter == nil { + return dialer.DialContext(ctx, network, addr, opts...) + } + + if !proxyAdapter.SupportUDP() { + return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter) + } + + packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...) + if err != nil { + return nil, err + } + + packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) + + return N.NewBindPacketConn(packetConn, metadata.UDPAddr()), nil + } + } + } +} + +func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) { + metadata := &C.Metadata{ + NetWork: C.UDP, + Type: C.INNER, + } + err := metadata.SetRemoteAddress(addr) + if err != nil { + return nil, err + } + if !metadata.Resolved() { + // udp must resolve host first + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) + if err != nil { + return nil, err + } + metadata.DstIP = dstIP + } + + var rule C.Rule + if proxyAdapter == nil { + if proxyName == RespectRules { + proxyAdapter, rule, err = tunnel.ResolveMetadata(metadata) + if err != nil { + return nil, err + } + } else { + var ok bool + proxyAdapter, ok = tunnel.Proxies()[proxyName] + if !ok { + opts = append(opts, dialer.WithInterface(proxyName)) + } + } + } + + if proxyAdapter == nil { + return dialer.NewDialer(opts...).ListenPacket(ctx, network, "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort)) + } + + if !proxyAdapter.SupportUDP() { + return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter) + } + + packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...) + if err != nil { + return nil, err + } + + packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) + + return packetConn, nil +} diff --git a/dns/util.go b/dns/util.go index f13279fa..9c5a0b58 100644 --- a/dns/util.go +++ b/dns/util.go @@ -10,15 +10,10 @@ import ( "strings" "time" - N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/common/nnip" "github.com/metacubex/mihomo/common/picker" - "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/resolver" - C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" - "github.com/metacubex/mihomo/tunnel" - "github.com/metacubex/mihomo/tunnel/statistic" D "github.com/miekg/dns" "github.com/samber/lo" @@ -175,158 +170,6 @@ func msgToDomain(msg *D.Msg) string { return "" } -const RespectRules = "RULES" - -type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error) - -func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) dialHandler { - return func(ctx context.Context, network, addr string) (net.Conn, error) { - if len(proxyName) == 0 && proxyAdapter == nil { - opts = append(opts, dialer.WithResolver(r)) - return dialer.DialContext(ctx, network, addr, opts...) - } else { - metadata := &C.Metadata{ - NetWork: C.TCP, - Type: C.INNER, - } - err := metadata.SetRemoteAddress(addr) // tcp can resolve host by remote - if err != nil { - return nil, err - } - if !strings.Contains(network, "tcp") { - metadata.NetWork = C.UDP - if !metadata.Resolved() { - // udp must resolve host first - dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) - if err != nil { - return nil, err - } - metadata.DstIP = dstIP - } - } - - var rule C.Rule - if proxyAdapter == nil { - if proxyName == RespectRules { - if !metadata.Resolved() { - // resolve here before ResolveMetadata to avoid its inner resolver.ResolveIP - dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) - if err != nil { - return nil, err - } - metadata.DstIP = dstIP - } - proxyAdapter, rule, err = tunnel.ResolveMetadata(metadata) - if err != nil { - return nil, err - } - } else { - var ok bool - proxyAdapter, ok = tunnel.Proxies()[proxyName] - if !ok { - opts = append(opts, dialer.WithInterface(proxyName)) - } - } - } - - if strings.Contains(network, "tcp") { - if proxyAdapter == nil { - opts = append(opts, dialer.WithResolver(r)) - return dialer.DialContext(ctx, network, addr, opts...) - } - - if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback - if !metadata.Resolved() { - dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) - if err != nil { - return nil, err - } - metadata.DstIP = dstIP - } - metadata.Host = "" // clear host to avoid double resolve in proxy - } - - conn, err := proxyAdapter.DialContext(ctx, metadata, opts...) - if err != nil { - return nil, err - } - - conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, metadata, rule, 0, 0, false) - - return conn, nil - } else { - if proxyAdapter == nil { - return dialer.DialContext(ctx, network, addr, opts...) - } - - if !proxyAdapter.SupportUDP() { - return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter) - } - - packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...) - if err != nil { - return nil, err - } - - packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) - - return N.NewBindPacketConn(packetConn, metadata.UDPAddr()), nil - } - } - } -} - -func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) { - metadata := &C.Metadata{ - NetWork: C.UDP, - } - err := metadata.SetRemoteAddress(addr) - if err != nil { - return nil, err - } - if !metadata.Resolved() { - // udp must resolve host first - dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) - if err != nil { - return nil, err - } - metadata.DstIP = dstIP - } - - var rule C.Rule - if proxyAdapter == nil { - if proxyName == RespectRules { - proxyAdapter, rule, err = tunnel.ResolveMetadata(metadata) - if err != nil { - return nil, err - } - } else { - var ok bool - proxyAdapter, ok = tunnel.Proxies()[proxyName] - if !ok { - opts = append(opts, dialer.WithInterface(proxyName)) - } - } - } - - if proxyAdapter == nil { - return dialer.NewDialer(opts...).ListenPacket(ctx, network, "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort)) - } - - if !proxyAdapter.SupportUDP() { - return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter) - } - - packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...) - if err != nil { - return nil, err - } - - packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) - - return packetConn, nil -} - func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, cache bool, err error) { cache = true fast, ctx := picker.WithTimeout[*D.Msg](ctx, resolver.DefaultDNSTimeout) From d96d7651ca7903e51a53033bfa7c18e6e7b15f50 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 13 Jun 2024 08:43:03 +0800 Subject: [PATCH 113/311] chore: add inner dns proxied connection log --- dns/dialer.go | 12 ++++ dns/dial.go => tunnel/dns_dialer.go | 33 +++++---- tunnel/tunnel.go | 103 ++++++++++------------------ 3 files changed, 70 insertions(+), 78 deletions(-) create mode 100644 dns/dialer.go rename dns/dial.go => tunnel/dns_dialer.go (77%) diff --git a/dns/dialer.go b/dns/dialer.go new file mode 100644 index 00000000..309b6c18 --- /dev/null +++ b/dns/dialer.go @@ -0,0 +1,12 @@ +package dns + +// export functions from tunnel module + +import "github.com/metacubex/mihomo/tunnel" + +const RespectRules = tunnel.DnsRespectRules + +type dialHandler = tunnel.DnsDialHandler + +var getDialHandler = tunnel.GetDnsDialHandler +var listenPacket = tunnel.DnsListenPacket diff --git a/dns/dial.go b/tunnel/dns_dialer.go similarity index 77% rename from dns/dial.go rename to tunnel/dns_dialer.go index 431707c5..f4c0be71 100644 --- a/dns/dial.go +++ b/tunnel/dns_dialer.go @@ -1,4 +1,6 @@ -package dns +package tunnel + +// WARNING: all function in this file should only be using in dns module import ( "context" @@ -11,15 +13,14 @@ import ( "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/tunnel" "github.com/metacubex/mihomo/tunnel/statistic" ) -const RespectRules = "RULES" +const DnsRespectRules = "RULES" -type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error) +type DnsDialHandler func(ctx context.Context, network, addr string) (net.Conn, error) -func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) dialHandler { +func GetDnsDialHandler(r resolver.Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) DnsDialHandler { return func(ctx context.Context, network, addr string) (net.Conn, error) { if len(proxyName) == 0 && proxyAdapter == nil { opts = append(opts, dialer.WithResolver(r)) @@ -47,22 +48,22 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, var rule C.Rule if proxyAdapter == nil { - if proxyName == RespectRules { + if proxyName == DnsRespectRules { if !metadata.Resolved() { - // resolve here before ResolveMetadata to avoid its inner resolver.ResolveIP + // resolve here before resolveMetadata to avoid its inner resolver.ResolveIP dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) if err != nil { return nil, err } metadata.DstIP = dstIP } - proxyAdapter, rule, err = tunnel.ResolveMetadata(metadata) + proxyAdapter, rule, err = resolveMetadata(metadata) if err != nil { return nil, err } } else { var ok bool - proxyAdapter, ok = tunnel.Proxies()[proxyName] + proxyAdapter, ok = Proxies()[proxyName] if !ok { opts = append(opts, dialer.WithInterface(proxyName)) } @@ -88,8 +89,10 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, conn, err := proxyAdapter.DialContext(ctx, metadata, opts...) if err != nil { + logMetadataErr(metadata, rule, proxyAdapter, err) return nil, err } + logMetadata(metadata, rule, conn) conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, metadata, rule, 0, 0, false) @@ -105,8 +108,10 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...) if err != nil { + logMetadataErr(metadata, rule, proxyAdapter, err) return nil, err } + logMetadata(metadata, rule, packetConn) packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) @@ -116,7 +121,7 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, } } -func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) { +func DnsListenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r resolver.Resolver, opts ...dialer.Option) (net.PacketConn, error) { metadata := &C.Metadata{ NetWork: C.UDP, Type: C.INNER, @@ -136,14 +141,14 @@ func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName st var rule C.Rule if proxyAdapter == nil { - if proxyName == RespectRules { - proxyAdapter, rule, err = tunnel.ResolveMetadata(metadata) + if proxyName == DnsRespectRules { + proxyAdapter, rule, err = resolveMetadata(metadata) if err != nil { return nil, err } } else { var ok bool - proxyAdapter, ok = tunnel.Proxies()[proxyName] + proxyAdapter, ok = Proxies()[proxyName] if !ok { opts = append(opts, dialer.WithInterface(proxyName)) } @@ -160,8 +165,10 @@ func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName st packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...) if err != nil { + logMetadataErr(metadata, rule, proxyAdapter, err) return nil, err } + logMetadata(metadata, rule, packetConn) packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 78628a56..2c1b894f 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -8,6 +8,7 @@ import ( "net/netip" "path/filepath" "runtime" + "strings" "sync" "time" @@ -278,7 +279,7 @@ func preHandleMetadata(metadata *C.Metadata) error { return nil } -func ResolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err error) { +func resolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err error) { if metadata.SpecialProxy != "" { var exist bool proxy, exist = proxies[metadata.SpecialProxy] @@ -375,7 +376,7 @@ func handleUDPConn(packet C.PacketAdapter) { cond.Broadcast() }() - proxy, rule, err := ResolveMetadata(metadata) + proxy, rule, err := resolveMetadata(metadata) if err != nil { log.Warnln("[UDP] Parse metadata failed: %s", err.Error()) return @@ -386,43 +387,18 @@ func handleUDPConn(packet C.PacketAdapter) { rawPc, err := retry(ctx, func(ctx context.Context) (C.PacketConn, error) { return proxy.ListenPacketContext(ctx, metadata.Pure()) }, func(err error) { - if rule == nil { - log.Warnln( - "[UDP] dial %s %s --> %s error: %s", - proxy.Name(), - metadata.SourceDetail(), - metadata.RemoteAddress(), - err.Error(), - ) - } else { - log.Warnln("[UDP] dial %s (match %s/%s) %s --> %s error: %s", proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.SourceDetail(), metadata.RemoteAddress(), err.Error()) - } + logMetadataErr(metadata, rule, proxy, err) }) if err != nil { return } + logMetadata(metadata, rule, rawPc) pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule, 0, 0, true) - switch true { - case metadata.SpecialProxy != "": - log.Infoln("[UDP] %s --> %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), metadata.SpecialProxy) - case rule != nil: - if rule.Payload() != "" { - log.Infoln("[UDP] %s --> %s match %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), fmt.Sprintf("%s(%s)", rule.RuleType().String(), rule.Payload()), rawPc.Chains().String()) - if rawPc.Chains().Last() == "REJECT-DROP" { - pc.Close() - return - } - } else { - log.Infoln("[UDP] %s --> %s match %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), rule.Payload(), rawPc.Chains().String()) - } - case mode == Global: - log.Infoln("[UDP] %s --> %s using GLOBAL", metadata.SourceDetail(), metadata.RemoteAddress()) - case mode == Direct: - log.Infoln("[UDP] %s --> %s using DIRECT", metadata.SourceDetail(), metadata.RemoteAddress()) - default: - log.Infoln("[UDP] %s --> %s doesn't match any rule using DIRECT", metadata.SourceDetail(), metadata.RemoteAddress()) + if rawPc.Chains().Last() == "REJECT-DROP" { + pc.Close() + return } oAddrPort := metadata.AddrPort() @@ -486,7 +462,7 @@ func handleTCPConn(connCtx C.ConnContext) { }() } - proxy, rule, err := ResolveMetadata(metadata) + proxy, rule, err := resolveMetadata(metadata) if err != nil { log.Warnln("[Metadata] parse failed: %s", err.Error()) return @@ -539,48 +515,18 @@ func handleTCPConn(connCtx C.ConnContext) { } return }, func(err error) { - if rule == nil { - log.Warnln( - "[TCP] dial %s %s --> %s error: %s", - proxy.Name(), - metadata.SourceDetail(), - metadata.RemoteAddress(), - err.Error(), - ) - } else { - log.Warnln("[TCP] dial %s (match %s/%s) %s --> %s error: %s", proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.SourceDetail(), metadata.RemoteAddress(), err.Error()) - } + logMetadataErr(metadata, rule, proxy, err) }) if err != nil { return } + logMetadata(metadata, rule, remoteConn) remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule, 0, int64(peekLen), true) defer func(remoteConn C.Conn) { _ = remoteConn.Close() }(remoteConn) - switch true { - case metadata.SpecialProxy != "": - log.Infoln("[TCP] %s --> %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), metadata.SpecialProxy) - case rule != nil: - if rule.Payload() != "" { - log.Infoln("[TCP] %s --> %s match %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), fmt.Sprintf("%s(%s)", rule.RuleType().String(), rule.Payload()), remoteConn.Chains().String()) - } else { - log.Infoln("[TCP] %s --> %s match %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), rule.RuleType().String(), remoteConn.Chains().String()) - } - case mode == Global: - log.Infoln("[TCP] %s --> %s using GLOBAL", metadata.SourceDetail(), metadata.RemoteAddress()) - case mode == Direct: - log.Infoln("[TCP] %s --> %s using DIRECT", metadata.SourceDetail(), metadata.RemoteAddress()) - default: - log.Infoln( - "[TCP] %s --> %s doesn't match any rule using DIRECT", - metadata.SourceDetail(), - metadata.RemoteAddress(), - ) - } - _ = conn.SetReadDeadline(time.Now()) // stop unfinished peek peekMutex.Lock() defer peekMutex.Unlock() @@ -588,6 +534,33 @@ func handleTCPConn(connCtx C.ConnContext) { handleSocket(conn, remoteConn) } +func logMetadataErr(metadata *C.Metadata, rule C.Rule, proxy C.ProxyAdapter, err error) { + if rule == nil { + log.Warnln("[%s] dial %s %s --> %s error: %s", strings.ToUpper(metadata.NetWork.String()), proxy.Name(), metadata.SourceDetail(), metadata.RemoteAddress(), err.Error()) + } else { + log.Warnln("[%s] dial %s (match %s/%s) %s --> %s error: %s", strings.ToUpper(metadata.NetWork.String()), proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.SourceDetail(), metadata.RemoteAddress(), err.Error()) + } +} + +func logMetadata(metadata *C.Metadata, rule C.Rule, remoteConn C.Connection) { + switch { + case metadata.SpecialProxy != "": + log.Infoln("[%s] %s --> %s using %s", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress(), metadata.SpecialProxy) + case rule != nil: + if rule.Payload() != "" { + log.Infoln("[%s] %s --> %s match %s using %s", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress(), fmt.Sprintf("%s(%s)", rule.RuleType().String(), rule.Payload()), remoteConn.Chains().String()) + } else { + log.Infoln("[%s] %s --> %s match %s using %s", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress(), rule.RuleType().String(), remoteConn.Chains().String()) + } + case mode == Global: + log.Infoln("[%s] %s --> %s using GLOBAL", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress()) + case mode == Direct: + log.Infoln("[%s] %s --> %s using DIRECT", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress()) + default: + log.Infoln("[%s] %s --> %s doesn't match any rule using %s", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress(), remoteConn.Chains().Last()) + } +} + func shouldResolveIP(rule C.Rule, metadata *C.Metadata) bool { return rule.ShouldResolveIP() && metadata.Host != "" && !metadata.DstIP.IsValid() } From 75c16f9b87aa7fa5522592181f025b0cd7b61f36 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 14 Jun 2024 14:01:52 +0800 Subject: [PATCH 114/311] feat: add `refresh-server-ip-interval` for wireguard outbound --- adapter/outbound/wireguard.go | 157 +++++++++++++++++++++++++--------- docs/config.yaml | 1 + 2 files changed, 116 insertions(+), 42 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 56ade71c..2e34dd83 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -12,6 +12,7 @@ import ( "strconv" "strings" "sync" + "time" "github.com/metacubex/mihomo/common/atomic" CN "github.com/metacubex/mihomo/common/net" @@ -48,6 +49,10 @@ type WireGuard struct { connectAddr M.Socksaddr localPrefixes []netip.Prefix + serverAddrMap map[M.Socksaddr]netip.AddrPort + serverAddrTime atomic.TypedValue[time.Time] + serverAddrMutex sync.Mutex + closeCh chan struct{} // for test } @@ -67,6 +72,8 @@ type WireGuardOption struct { RemoteDnsResolve bool `proxy:"remote-dns-resolve,omitempty"` Dns []string `proxy:"dns,omitempty"` + + RefreshServerIPInterval int `proxy:"refresh-server-ip-interval,omitempty"` } type WireGuardPeerOption struct { @@ -287,6 +294,15 @@ func (w *WireGuard) resolve(ctx context.Context, address M.Socksaddr) (netip.Add } func (w *WireGuard) init(ctx context.Context) error { + err := w.init0(ctx) + if err != nil { + return err + } + w.updateServerAddr(ctx) + return nil +} + +func (w *WireGuard) init0(ctx context.Context) error { if w.initOk.Load() { return nil } @@ -301,44 +317,118 @@ func (w *WireGuard) init(ctx context.Context) error { } w.bind.ResetReservedForEndpoint() - ipcConf := "private_key=" + w.option.PrivateKey + w.serverAddrMap = make(map[M.Socksaddr]netip.AddrPort) + ipcConf, err := w.genIpcConf(ctx, false) + if err != nil { + // !!! do not set initErr here !!! + // let us can retry domain resolve in next time + return err + } + + if debug.Enabled { + log.SingLogger.Trace(fmt.Sprintf("[WG](%s) created wireguard ipc conf: \n %s", w.option.Name, ipcConf)) + } + err = w.device.IpcSet(ipcConf) + if err != nil { + w.initErr = E.Cause(err, "setup wireguard") + return w.initErr + } + w.serverAddrTime.Store(time.Now()) + + err = w.tunDevice.Start() + if err != nil { + w.initErr = err + return w.initErr + } + + w.initOk.Store(true) + return nil +} + +func (w *WireGuard) updateServerAddr(ctx context.Context) { + if w.option.RefreshServerIPInterval != 0 && time.Since(w.serverAddrTime.Load()) > time.Second*time.Duration(w.option.RefreshServerIPInterval) { + if w.serverAddrMutex.TryLock() { + defer w.serverAddrMutex.Unlock() + ipcConf, err := w.genIpcConf(ctx, true) + if err != nil { + log.Warnln("[WG](%s)UpdateServerAddr failed to generate wireguard ipc conf: %s", w.option.Name, err) + return + } + err = w.device.IpcSet(ipcConf) + if err != nil { + log.Warnln("[WG](%s)UpdateServerAddr failed to update wireguard ipc conf: %s", w.option.Name, err) + return + } + w.serverAddrTime.Store(time.Now()) + } + } +} + +func (w *WireGuard) genIpcConf(ctx context.Context, updateOnly bool) (string, error) { + ipcConf := "" + if !updateOnly { + ipcConf += "private_key=" + w.option.PrivateKey + "\n" + } if len(w.option.Peers) > 0 { for i, peer := range w.option.Peers { - ipcConf += "\npublic_key=" + peer.PublicKey - destination, err := w.resolve(ctx, peer.Addr()) + peerAddr := peer.Addr() + destination, err := w.resolve(ctx, peerAddr) if err != nil { - // !!! do not set initErr here !!! - // let us can retry domain resolve in next time - return E.Cause(err, "resolve endpoint domain for peer ", i) + return "", E.Cause(err, "resolve endpoint domain for peer ", i) } + if w.serverAddrMap[peerAddr] != destination { + w.serverAddrMap[peerAddr] = destination + } else if updateOnly { + continue + } + if len(w.option.Peers) == 1 { // must call SetConnectAddr if isConnect == true w.bind.SetConnectAddr(destination) } - ipcConf += "\nendpoint=" + destination.String() - if peer.PreSharedKey != "" { - ipcConf += "\npreshared_key=" + peer.PreSharedKey - } - for _, allowedIP := range peer.AllowedIPs { - ipcConf += "\nallowed_ip=" + allowedIP + ipcConf += "public_key=" + peer.PublicKey + "\n" + if updateOnly { + ipcConf += "update_only=true\n" } + ipcConf += "endpoint=" + destination.String() + "\n" if len(peer.Reserved) > 0 { var reserved [3]uint8 copy(reserved[:], w.option.Reserved) w.bind.SetReservedForEndpoint(destination, reserved) } + if updateOnly { + continue + } + if peer.PreSharedKey != "" { + ipcConf += "preshared_key=" + peer.PreSharedKey + "\n" + } + for _, allowedIP := range peer.AllowedIPs { + ipcConf += "allowed_ip=" + allowedIP + "\n" + } + if w.option.PersistentKeepalive != 0 { + ipcConf += fmt.Sprintf("persistent_keepalive_interval=%d\n", w.option.PersistentKeepalive) + } } } else { - ipcConf += "\npublic_key=" + w.option.PublicKey destination, err := w.resolve(ctx, w.connectAddr) if err != nil { - // !!! do not set initErr here !!! - // let us can retry domain resolve in next time - return E.Cause(err, "resolve endpoint domain") + return "", E.Cause(err, "resolve endpoint domain") + } + if w.serverAddrMap[w.connectAddr] != destination { + w.serverAddrMap[w.connectAddr] = destination + } else if updateOnly { + return "", nil } w.bind.SetConnectAddr(destination) // must call SetConnectAddr if isConnect == true - ipcConf += "\nendpoint=" + destination.String() + ipcConf += "public_key=" + w.option.PublicKey + "\n" + if updateOnly { + ipcConf += "update_only=true\n" + } + ipcConf += "endpoint=" + destination.String() + "\n" + if updateOnly { + return ipcConf, nil + } if w.option.PreSharedKey != "" { - ipcConf += "\npreshared_key=" + w.option.PreSharedKey + ipcConf += "preshared_key=" + w.option.PreSharedKey + "\n" } var has4, has6 bool for _, address := range w.localPrefixes { @@ -349,34 +439,17 @@ func (w *WireGuard) init(ctx context.Context) error { } } if has4 { - ipcConf += "\nallowed_ip=0.0.0.0/0" + ipcConf += "allowed_ip=0.0.0.0/0\n" } if has6 { - ipcConf += "\nallowed_ip=::/0" + ipcConf += "allowed_ip=::/0\n" + } + + if w.option.PersistentKeepalive != 0 { + ipcConf += fmt.Sprintf("persistent_keepalive_interval=%d\n", w.option.PersistentKeepalive) } } - - if w.option.PersistentKeepalive != 0 { - ipcConf += fmt.Sprintf("\npersistent_keepalive_interval=%d", w.option.PersistentKeepalive) - } - - if debug.Enabled { - log.SingLogger.Trace(fmt.Sprintf("[WG](%s) created wireguard ipc conf: \n %s", w.option.Name, ipcConf)) - } - err := w.device.IpcSet(ipcConf) - if err != nil { - w.initErr = E.Cause(err, "setup wireguard") - return w.initErr - } - - err = w.tunDevice.Start() - if err != nil { - w.initErr = err - return w.initErr - } - - w.initOk.Store(true) - return nil + return ipcConf, nil } func closeWireGuard(w *WireGuard) { diff --git a/docs/config.yaml b/docs/config.yaml index bd263c14..462b3a44 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -728,6 +728,7 @@ proxies: # socks5 # dialer-proxy: "ss1" # remote-dns-resolve: true # 强制 dns 远程解析,默认值为 false # dns: [ 1.1.1.1, 8.8.8.8 ] # 仅在 remote-dns-resolve 为 true 时生效 + # refresh-server-ip-interval: 60 # 重新解析server ip的间隔,单位为秒,默认值为0即仅第一次链接时解析server域名,仅应在server域名对应的IP会发生变化时启用该选项(如家宽ddns) # 如果 peers 不为空,该段落中的 allowed-ips 不可为空;前面段落的 server,port,public-key,pre-shared-key 均会被忽略,但 private-key 会被保留且只能在顶层指定 # peers: # - server: 162.159.192.1 From ad5bc51c77088e0abb04dfbc8dbb5e7d81d6afbf Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 15 Jun 2024 12:14:46 +0800 Subject: [PATCH 115/311] chore: deprecated the relay group type, please using dialer-proxy instead --- adapter/outboundgroup/relay.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/adapter/outboundgroup/relay.go b/adapter/outboundgroup/relay.go index 07fbcd95..29aa9c6a 100644 --- a/adapter/outboundgroup/relay.go +++ b/adapter/outboundgroup/relay.go @@ -9,6 +9,7 @@ import ( "github.com/metacubex/mihomo/component/proxydialer" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/constant/provider" + "github.com/metacubex/mihomo/log" ) type Relay struct { @@ -149,6 +150,7 @@ func (r *Relay) Addr() string { } func NewRelay(option *GroupCommonOption, providers []provider.ProxyProvider) *Relay { + log.Warnln("The group [%s] with relay type is deprecated, please using dialer-proxy instead", option.Name) return &Relay{ GroupBase: NewGroupBase(GroupBaseOption{ outbound.BaseOption{ From 40f40f6d2423c093add49820894dd6d22346a9f4 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 15 Jun 2024 00:33:03 +0800 Subject: [PATCH 116/311] fix: dns dial to wrong target --- adapter/outbound/direct.go | 3 +- dns/client.go | 47 ++------- dns/dialer.go | 5 +- dns/doh.go | 35 +++---- dns/doq.go | 16 ++- dns/util.go | 15 +-- tunnel/dns_dialer.go | 196 +++++++++++++++++++------------------ 7 files changed, 142 insertions(+), 175 deletions(-) diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index 09b9696b..c904d3b6 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -3,7 +3,6 @@ package outbound import ( "context" "errors" - "net/netip" "os" "strconv" @@ -58,7 +57,7 @@ func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, } metadata.DstIP = ip } - pc, err := dialer.NewDialer(d.Base.DialOptions(opts...)...).ListenPacket(ctx, "udp", "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort)) + pc, err := dialer.NewDialer(d.Base.DialOptions(opts...)...).ListenPacket(ctx, "udp", "", metadata.AddrPort()) if err != nil { return nil, err } diff --git a/dns/client.go b/dns/client.go index a6f0a7d4..096b96a7 100644 --- a/dns/client.go +++ b/dns/client.go @@ -5,28 +5,20 @@ import ( "crypto/tls" "fmt" "net" - "net/netip" "strings" "github.com/metacubex/mihomo/component/ca" - "github.com/metacubex/mihomo/component/dialer" - "github.com/metacubex/mihomo/component/resolver" - C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" - "github.com/metacubex/randv2" D "github.com/miekg/dns" ) type client struct { *D.Client - r *Resolver - port string - host string - iface string - proxyAdapter C.ProxyAdapter - proxyName string - addr string + port string + host string + dialer *dnsDialer + addr string } var _ dnsClient = (*client)(nil) @@ -49,38 +41,13 @@ func (c *client) Address() string { } func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) { - var ( - ip netip.Addr - err error - ) - if c.r == nil { - // a default ip dns - if ip, err = netip.ParseAddr(c.host); err != nil { - return nil, fmt.Errorf("dns %s not a valid ip", c.host) - } - } else { - ips, err := resolver.LookupIPWithResolver(ctx, c.host, c.r) - if err != nil { - return nil, fmt.Errorf("use default dns resolve failed: %w", err) - } else if len(ips) == 0 { - return nil, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, c.host) - } - ip = ips[randv2.IntN(len(ips))] - } - network := "udp" if strings.HasPrefix(c.Client.Net, "tcp") { network = "tcp" } - var options []dialer.Option - if c.iface != "" { - options = append(options, dialer.WithInterface(c.iface)) - } - - dialHandler := getDialHandler(c.r, c.proxyAdapter, c.proxyName, options...) - addr := net.JoinHostPort(ip.String(), c.port) - conn, err := dialHandler(ctx, network, addr) + addr := net.JoinHostPort(c.host, c.port) + conn, err := c.dialer.DialContext(ctx, network, addr) if err != nil { return nil, err } @@ -115,7 +82,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) tcpClient.Net = "tcp" network = "tcp" log.Debugln("[DNS] Truncated reply from %s:%s for %s over UDP, retrying over TCP", c.host, c.port, m.Question[0].String()) - dConn.Conn, err = dialHandler(ctx, network, addr) + dConn.Conn, err = c.dialer.DialContext(ctx, network, addr) if err != nil { ch <- result{msg, err} return diff --git a/dns/dialer.go b/dns/dialer.go index 309b6c18..f4d9e128 100644 --- a/dns/dialer.go +++ b/dns/dialer.go @@ -6,7 +6,6 @@ import "github.com/metacubex/mihomo/tunnel" const RespectRules = tunnel.DnsRespectRules -type dialHandler = tunnel.DnsDialHandler +type dnsDialer = tunnel.DNSDialer -var getDialHandler = tunnel.GetDnsDialHandler -var listenPacket = tunnel.DnsListenPacket +var newDNSDialer = tunnel.NewDNSDialer diff --git a/dns/doh.go b/dns/doh.go index 09d311b5..54b82796 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -62,10 +62,8 @@ type dnsOverHTTPS struct { quicConfig *quic.Config quicConfigGuard sync.Mutex url *url.URL - r *Resolver httpVersions []C.HTTPVersion - proxyAdapter C.ProxyAdapter - proxyName string + dialer *dnsDialer addr string } @@ -85,11 +83,9 @@ func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[strin } doh := &dnsOverHTTPS{ - url: u, - addr: u.String(), - r: r, - proxyAdapter: proxyAdapter, - proxyName: proxyName, + url: u, + addr: u.String(), + dialer: newDNSDialer(r, proxyAdapter, proxyName), quicConfig: &quic.Config{ KeepAlivePeriod: QUICKeepAlivePeriod, TokenStore: newQUICTokenStore(), @@ -388,13 +384,12 @@ func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripp nextProtos = append(nextProtos, string(v)) } tlsConfig.NextProtos = nextProtos - dialContext := getDialHandler(doh.r, doh.proxyAdapter, doh.proxyName) if slices.Contains(doh.httpVersions, C.HTTPVersion3) { // First, we attempt to create an HTTP3 transport. If the probe QUIC // connection is established successfully, we'll be using HTTP3 for this // upstream. - transportH3, err := doh.createTransportH3(ctx, tlsConfig, dialContext) + transportH3, err := doh.createTransportH3(ctx, tlsConfig) if err == nil { log.Debugln("[%s] using HTTP/3 for this upstream: QUIC was faster", doh.url.String()) return transportH3, nil @@ -410,7 +405,7 @@ func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripp transport := &http.Transport{ TLSClientConfig: tlsConfig, DisableCompression: true, - DialContext: dialContext, + DialContext: doh.dialer.DialContext, IdleConnTimeout: transportDefaultIdleConnTimeout, MaxConnsPerHost: dohMaxConnsPerHost, MaxIdleConns: dohMaxIdleConns, @@ -490,13 +485,12 @@ func (h *http3Transport) Close() (err error) { func (doh *dnsOverHTTPS) createTransportH3( ctx context.Context, tlsConfig *tls.Config, - dialContext dialHandler, ) (roundTripper http.RoundTripper, err error) { if !doh.supportsH3() { return nil, errors.New("HTTP3 support is not enabled") } - addr, err := doh.probeH3(ctx, tlsConfig, dialContext) + addr, err := doh.probeH3(ctx, tlsConfig) if err != nil { return nil, err } @@ -534,7 +528,7 @@ func (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tls. IP: net.ParseIP(ip), Port: portInt, } - conn, err := listenPacket(ctx, doh.proxyAdapter, doh.proxyName, "udp", addr, doh.r) + conn, err := doh.dialer.ListenPacket(ctx, "udp", addr) if err != nil { return nil, err } @@ -557,12 +551,11 @@ func (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tls. func (doh *dnsOverHTTPS) probeH3( ctx context.Context, tlsConfig *tls.Config, - dialContext dialHandler, ) (addr string, err error) { // We're using bootstrapped address instead of what's passed to the function // it does not create an actual connection, but it helps us determine // what IP is actually reachable (when there are v4/v6 addresses). - rawConn, err := dialContext(ctx, "udp", doh.url.Host) + rawConn, err := doh.dialer.DialContext(ctx, "udp", doh.url.Host) if err != nil { return "", fmt.Errorf("failed to dial: %w", err) } @@ -592,7 +585,7 @@ func (doh *dnsOverHTTPS) probeH3( chQuic := make(chan error, 1) chTLS := make(chan error, 1) go doh.probeQUIC(ctx, addr, probeTLSCfg, chQuic) - go doh.probeTLS(ctx, dialContext, probeTLSCfg, chTLS) + go doh.probeTLS(ctx, probeTLSCfg, chTLS) select { case quicErr := <-chQuic: @@ -635,10 +628,10 @@ func (doh *dnsOverHTTPS) probeQUIC(ctx context.Context, addr string, tlsConfig * // probeTLS attempts to establish a TLS connection to the specified address. We // run probeQUIC and probeTLS in parallel and see which one is faster. -func (doh *dnsOverHTTPS) probeTLS(ctx context.Context, dialContext dialHandler, tlsConfig *tls.Config, ch chan error) { +func (doh *dnsOverHTTPS) probeTLS(ctx context.Context, tlsConfig *tls.Config, ch chan error) { startTime := time.Now() - conn, err := doh.tlsDial(ctx, dialContext, "tcp", tlsConfig) + conn, err := doh.tlsDial(ctx, "tcp", tlsConfig) if err != nil { ch <- fmt.Errorf("opening TLS connection: %w", err) return @@ -694,10 +687,10 @@ func isHTTP3(client *http.Client) (ok bool) { // tlsDial is basically the same as tls.DialWithDialer, but we will call our own // dialContext function to get connection. -func (doh *dnsOverHTTPS) tlsDial(ctx context.Context, dialContext dialHandler, network string, config *tls.Config) (*tls.Conn, error) { +func (doh *dnsOverHTTPS) tlsDial(ctx context.Context, network string, config *tls.Config) (*tls.Conn, error) { // We're using bootstrapped address instead of what's passed // to the function. - rawConn, err := dialContext(ctx, network, doh.url.Host) + rawConn, err := doh.dialer.DialContext(ctx, network, doh.url.Host) if err != nil { return nil, err } diff --git a/dns/doq.go b/dns/doq.go index 70b67c2a..ad936f95 100644 --- a/dns/doq.go +++ b/dns/doq.go @@ -60,10 +60,8 @@ type dnsOverQUIC struct { bytesPool *sync.Pool bytesPoolGuard sync.Mutex - addr string - proxyAdapter C.ProxyAdapter - proxyName string - r *Resolver + addr string + dialer *dnsDialer } // type check @@ -72,10 +70,8 @@ var _ dnsClient = (*dnsOverQUIC)(nil) // newDoQ returns the DNS-over-QUIC Upstream. func newDoQ(resolver *Resolver, addr string, proxyAdapter C.ProxyAdapter, proxyName string) (dnsClient, error) { doq := &dnsOverQUIC{ - addr: addr, - proxyAdapter: proxyAdapter, - proxyName: proxyName, - r: resolver, + addr: addr, + dialer: newDNSDialer(resolver, proxyAdapter, proxyName), quicConfig: &quic.Config{ KeepAlivePeriod: QUICKeepAlivePeriod, TokenStore: newQUICTokenStore(), @@ -300,7 +296,7 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio // we're using bootstrapped address instead of what's passed to the function // it does not create an actual connection, but it helps us determine // what IP is actually reachable (when there're v4/v6 addresses). - rawConn, err := getDialHandler(doq.r, doq.proxyAdapter, doq.proxyName)(ctx, "udp", doq.addr) + rawConn, err := doq.dialer.DialContext(ctx, "udp", doq.addr) if err != nil { return nil, fmt.Errorf("failed to open a QUIC connection: %w", err) } @@ -315,7 +311,7 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio p, err := strconv.Atoi(port) udpAddr := net.UDPAddr{IP: net.ParseIP(ip), Port: p} - udp, err := listenPacket(ctx, doq.proxyAdapter, doq.proxyName, "udp", addr, doq.r) + udp, err := doq.dialer.ListenPacket(ctx, "udp", addr) if err != nil { return nil, err } diff --git a/dns/util.go b/dns/util.go index 9c5a0b58..e4ec5917 100644 --- a/dns/util.go +++ b/dns/util.go @@ -12,6 +12,7 @@ import ( "github.com/metacubex/mihomo/common/nnip" "github.com/metacubex/mihomo/common/picker" + "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/log" @@ -115,6 +116,11 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient { continue } + var options []dialer.Option + if s.Interface != "" { + options = append(options, dialer.WithInterface(s.Interface)) + } + host, port, _ := net.SplitHostPort(s.Addr) ret = append(ret, &client{ Client: &D.Client{ @@ -125,12 +131,9 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient { UDPSize: 4096, Timeout: 5 * time.Second, }, - port: port, - host: host, - iface: s.Interface, - r: resolver, - proxyAdapter: s.ProxyAdapter, - proxyName: s.ProxyName, + port: port, + host: host, + dialer: newDNSDialer(resolver, s.ProxyAdapter, s.ProxyName, options...), }) } return ret diff --git a/tunnel/dns_dialer.go b/tunnel/dns_dialer.go index f4c0be71..1839869b 100644 --- a/tunnel/dns_dialer.go +++ b/tunnel/dns_dialer.go @@ -6,7 +6,6 @@ import ( "context" "fmt" "net" - "net/netip" "strings" N "github.com/metacubex/mihomo/common/net" @@ -18,110 +17,121 @@ import ( const DnsRespectRules = "RULES" -type DnsDialHandler func(ctx context.Context, network, addr string) (net.Conn, error) +type DNSDialer struct { + r resolver.Resolver + proxyAdapter C.ProxyAdapter + proxyName string + opts []dialer.Option +} -func GetDnsDialHandler(r resolver.Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) DnsDialHandler { - return func(ctx context.Context, network, addr string) (net.Conn, error) { - if len(proxyName) == 0 && proxyAdapter == nil { - opts = append(opts, dialer.WithResolver(r)) - return dialer.DialContext(ctx, network, addr, opts...) - } else { - metadata := &C.Metadata{ - NetWork: C.TCP, - Type: C.INNER, - } - err := metadata.SetRemoteAddress(addr) // tcp can resolve host by remote +func NewDNSDialer(r resolver.Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) *DNSDialer { + return &DNSDialer{r: r, proxyAdapter: proxyAdapter, proxyName: proxyName, opts: opts} +} + +func (d *DNSDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) { + r := d.r + proxyName := d.proxyName + proxyAdapter := d.proxyAdapter + opts := d.opts + var rule C.Rule + metadata := &C.Metadata{ + NetWork: C.TCP, + Type: C.INNER, + } + err := metadata.SetRemoteAddress(addr) // tcp can resolve host by remote + if err != nil { + return nil, err + } + if !strings.Contains(network, "tcp") { + metadata.NetWork = C.UDP + if !metadata.Resolved() { + // udp must resolve host first + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) if err != nil { return nil, err } - if !strings.Contains(network, "tcp") { - metadata.NetWork = C.UDP - if !metadata.Resolved() { - // udp must resolve host first - dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) - if err != nil { - return nil, err - } - metadata.DstIP = dstIP - } - } + metadata.DstIP = dstIP + } + } - var rule C.Rule - if proxyAdapter == nil { - if proxyName == DnsRespectRules { - if !metadata.Resolved() { - // resolve here before resolveMetadata to avoid its inner resolver.ResolveIP - dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) - if err != nil { - return nil, err - } - metadata.DstIP = dstIP - } - proxyAdapter, rule, err = resolveMetadata(metadata) - if err != nil { - return nil, err - } - } else { - var ok bool - proxyAdapter, ok = Proxies()[proxyName] - if !ok { - opts = append(opts, dialer.WithInterface(proxyName)) - } - } - } - - if strings.Contains(network, "tcp") { - if proxyAdapter == nil { - opts = append(opts, dialer.WithResolver(r)) - return dialer.DialContext(ctx, network, addr, opts...) - } - - if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback - if !metadata.Resolved() { - dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) - if err != nil { - return nil, err - } - metadata.DstIP = dstIP - } - metadata.Host = "" // clear host to avoid double resolve in proxy - } - - conn, err := proxyAdapter.DialContext(ctx, metadata, opts...) + if proxyAdapter == nil && len(proxyName) != 0 { + if proxyName == DnsRespectRules { + if !metadata.Resolved() { + // resolve here before resolveMetadata to avoid its inner resolver.ResolveIP + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) if err != nil { - logMetadataErr(metadata, rule, proxyAdapter, err) return nil, err } - logMetadata(metadata, rule, conn) - - conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, metadata, rule, 0, 0, false) - - return conn, nil - } else { - if proxyAdapter == nil { - return dialer.DialContext(ctx, network, addr, opts...) - } - - if !proxyAdapter.SupportUDP() { - return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter) - } - - packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...) - if err != nil { - logMetadataErr(metadata, rule, proxyAdapter, err) - return nil, err - } - logMetadata(metadata, rule, packetConn) - - packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) - - return N.NewBindPacketConn(packetConn, metadata.UDPAddr()), nil + metadata.DstIP = dstIP + } + proxyAdapter, rule, err = resolveMetadata(metadata) + if err != nil { + return nil, err + } + } else { + var ok bool + proxyAdapter, ok = Proxies()[proxyName] + if !ok { + opts = append(opts, dialer.WithInterface(proxyName)) } } } + + if metadata.NetWork == C.TCP { + if proxyAdapter == nil { + opts = append(opts, dialer.WithResolver(r)) + return dialer.DialContext(ctx, network, addr, opts...) + } + + if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback + if !metadata.Resolved() { + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) + if err != nil { + return nil, err + } + metadata.DstIP = dstIP + } + metadata.Host = "" // clear host to avoid double resolve in proxy + } + + conn, err := proxyAdapter.DialContext(ctx, metadata, opts...) + if err != nil { + logMetadataErr(metadata, rule, proxyAdapter, err) + return nil, err + } + logMetadata(metadata, rule, conn) + + conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, metadata, rule, 0, 0, false) + + return conn, nil + } else { + if proxyAdapter == nil { + return dialer.DialContext(ctx, network, metadata.AddrPort().String(), opts...) + } + + if !proxyAdapter.SupportUDP() { + return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter) + } + + packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...) + if err != nil { + logMetadataErr(metadata, rule, proxyAdapter, err) + return nil, err + } + logMetadata(metadata, rule, packetConn) + + packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) + + return N.NewBindPacketConn(packetConn, metadata.UDPAddr()), nil + } + } -func DnsListenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r resolver.Resolver, opts ...dialer.Option) (net.PacketConn, error) { +func (d *DNSDialer) ListenPacket(ctx context.Context, network, addr string) (net.PacketConn, error) { + r := d.r + proxyAdapter := d.proxyAdapter + proxyName := d.proxyName + opts := d.opts metadata := &C.Metadata{ NetWork: C.UDP, Type: C.INNER, @@ -156,7 +166,7 @@ func DnsListenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName } if proxyAdapter == nil { - return dialer.NewDialer(opts...).ListenPacket(ctx, network, "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort)) + return dialer.NewDialer(opts...).ListenPacket(ctx, network, "", metadata.AddrPort()) } if !proxyAdapter.SupportUDP() { From 0738e18100b625b0f3312f39e53c30a617514bbe Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sun, 16 Jun 2024 18:19:04 +0800 Subject: [PATCH 117/311] chore: add override fields --- adapter/provider/parser.go | 3 +++ adapter/provider/provider.go | 49 +++++++++++++----------------------- 2 files changed, 21 insertions(+), 31 deletions(-) diff --git a/adapter/provider/parser.go b/adapter/provider/parser.go index 1094668d..edb6b911 100644 --- a/adapter/provider/parser.go +++ b/adapter/provider/parser.go @@ -28,7 +28,10 @@ type healthCheckSchema struct { } type OverrideSchema struct { + TFO *bool `provider:"tfo,omitempty"` + MPTcp *bool `provider:"mptcp,omitempty"` UDP *bool `provider:"udp,omitempty"` + UDPOverTCP *bool `provider:"udp-over-tcp,omitempty"` Up *string `provider:"up,omitempty"` Down *string `provider:"down,omitempty"` DialerProxy *string `provider:"dialer-proxy,omitempty"` diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index daef017c..694eae43 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "net/http" + "reflect" "runtime" "strings" "time" @@ -373,37 +374,23 @@ func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray mapping["dialer-proxy"] = dialerProxy } - if override.UDP != nil { - mapping["udp"] = *override.UDP - } - if override.Up != nil { - mapping["up"] = *override.Up - } - if override.Down != nil { - mapping["down"] = *override.Down - } - if override.DialerProxy != nil { - mapping["dialer-proxy"] = *override.DialerProxy - } - if override.SkipCertVerify != nil { - mapping["skip-cert-verify"] = *override.SkipCertVerify - } - if override.Interface != nil { - mapping["interface-name"] = *override.Interface - } - if override.RoutingMark != nil { - mapping["routing-mark"] = *override.RoutingMark - } - if override.IPVersion != nil { - mapping["ip-version"] = *override.IPVersion - } - if override.AdditionalPrefix != nil { - name := mapping["name"].(string) - mapping["name"] = *override.AdditionalPrefix + name - } - if override.AdditionalSuffix != nil { - name := mapping["name"].(string) - mapping["name"] = name + *override.AdditionalSuffix + val := reflect.ValueOf(override) + for i := 0; i < val.NumField(); i++ { + field := val.Field(i) + if field.IsNil() { + continue + } + fieldName := strings.Split(val.Type().Field(i).Tag.Get("provider"), ",")[0] + switch fieldName { + case "additional-prefix": + name := mapping["name"].(string) + mapping["name"] = *field.Interface().(*string) + name + case "additional-suffix": + name := mapping["name"].(string) + mapping["name"] = name + *field.Interface().(*string) + default: + mapping[fieldName] = field.Elem().Interface() + } } proxy, err := adapter.ParseProxy(mapping) From 09be5cbc99f97238aa95b9ceab9db39e53e1b3a9 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 17 Jun 2024 22:04:51 +0800 Subject: [PATCH 118/311] feat: tun support `auto-redirect`, `route-address-set` and `route-exclude-address-set` --- common/utils/callback.go | 50 +++++++ component/cidr/ipcidr_set.go | 8 +- component/iface/iface.go | 10 +- config/config.go | 107 +++++++++------ constant/provider/interface.go | 8 +- constant/rule.go | 1 + dns/policy.go | 13 +- dns/resolver.go | 9 +- docs/config.yaml | 16 ++- go.mod | 25 ++-- go.sum | 49 +++---- hub/executor/executor.go | 12 +- hub/route/configs.go | 72 +++++++--- listener/config/tun.go | 47 ++++--- listener/inbound/tun.go | 116 ++++++++++------ listener/listener.go | 28 +++- listener/sing/sing.go | 6 + listener/sing_tun/dns.go | 7 + listener/sing_tun/iface.go | 70 ++++++++++ listener/sing_tun/server.go | 211 +++++++++++++++++++++++++++-- listener/tproxy/tproxy_iptables.go | 4 +- rules/common/base.go | 2 + rules/logic/logic.go | 7 + rules/provider/ipcidr_strategy.go | 6 + rules/provider/patch_android.go | 4 +- rules/provider/provider.go | 34 ++--- rules/provider/rule_set.go | 51 ++++--- tunnel/tunnel.go | 19 ++- 28 files changed, 745 insertions(+), 247 deletions(-) create mode 100644 common/utils/callback.go create mode 100644 listener/sing_tun/iface.go diff --git a/common/utils/callback.go b/common/utils/callback.go new file mode 100644 index 00000000..df950d3a --- /dev/null +++ b/common/utils/callback.go @@ -0,0 +1,50 @@ +package utils + +import ( + "io" + "sync" + + list "github.com/bahlo/generic-list-go" +) + +type Callback[T any] struct { + list list.List[func(T)] + mutex sync.RWMutex +} + +func NewCallback[T any]() *Callback[T] { + return &Callback[T]{} +} + +func (c *Callback[T]) Register(item func(T)) io.Closer { + c.mutex.RLock() + defer c.mutex.RUnlock() + element := c.list.PushBack(item) + return &callbackCloser[T]{ + element: element, + callback: c, + } +} + +func (c *Callback[T]) Emit(item T) { + c.mutex.RLock() + defer c.mutex.RUnlock() + for element := c.list.Front(); element != nil; element = element.Next() { + go element.Value(item) + } +} + +type callbackCloser[T any] struct { + element *list.Element[func(T)] + callback *Callback[T] + once sync.Once +} + +func (c *callbackCloser[T]) Close() error { + c.once.Do(func() { + c.callback.mutex.Lock() + defer c.callback.mutex.Unlock() + c.callback.list.Remove(c.element) + }) + return nil +} diff --git a/component/cidr/ipcidr_set.go b/component/cidr/ipcidr_set.go index 0cb55e36..521fabab 100644 --- a/component/cidr/ipcidr_set.go +++ b/component/cidr/ipcidr_set.go @@ -43,12 +43,12 @@ func (set *IpCidrSet) IsContainForString(ipString string) bool { } func (set *IpCidrSet) IsContain(ip netip.Addr) bool { - return set.toIPSet().Contains(ip.WithZone("")) + return set.ToIPSet().Contains(ip.WithZone("")) } func (set *IpCidrSet) Merge() error { var b netipx.IPSetBuilder - b.AddSet(set.toIPSet()) + b.AddSet(set.ToIPSet()) i, err := b.IPSet() if err != nil { return err @@ -57,7 +57,9 @@ func (set *IpCidrSet) Merge() error { return nil } -func (set *IpCidrSet) toIPSet() *netipx.IPSet { +// ToIPSet not safe convert to *netipx.IPSet +// be careful, must be used after Merge +func (set *IpCidrSet) ToIPSet() *netipx.IPSet { return (*netipx.IPSet)(unsafe.Pointer(set)) } diff --git a/component/iface/iface.go b/component/iface/iface.go index d543725a..272ee737 100644 --- a/component/iface/iface.go +++ b/component/iface/iface.go @@ -11,8 +11,9 @@ import ( type Interface struct { Index int + MTU int Name string - Addrs []netip.Prefix + Addresses []netip.Prefix HardwareAddr net.HardwareAddr } @@ -61,8 +62,9 @@ func Interfaces() (map[string]*Interface, error) { r[iface.Name] = &Interface{ Index: iface.Index, + MTU: iface.MTU, Name: iface.Name, - Addrs: ipNets, + Addresses: ipNets, HardwareAddr: iface.HardwareAddr, } } @@ -92,7 +94,7 @@ func IsLocalIp(ip netip.Addr) (bool, error) { return false, err } for _, iface := range ifaces { - for _, addr := range iface.Addrs { + for _, addr := range iface.Addresses { if addr.Contains(ip) { return true, nil } @@ -120,7 +122,7 @@ func (iface *Interface) PickIPv6Addr(destination netip.Addr) (netip.Prefix, erro func (iface *Interface) pickIPAddr(destination netip.Addr, accept func(addr netip.Prefix) bool) (netip.Prefix, error) { var fallback netip.Prefix - for _, addr := range iface.Addrs { + for _, addr := range iface.Addresses { if !accept(addr) { continue } diff --git a/config/config.go b/config/config.go index fd12d2db..2166b87d 100644 --- a/config/config.go +++ b/config/config.go @@ -246,31 +246,39 @@ type RawTun struct { DNSHijack []string `yaml:"dns-hijack" json:"dns-hijack"` AutoRoute bool `yaml:"auto-route" json:"auto-route"` AutoDetectInterface bool `yaml:"auto-detect-interface"` - RedirectToTun []string `yaml:"-" json:"-"` MTU uint32 `yaml:"mtu" json:"mtu,omitempty"` GSO bool `yaml:"gso" json:"gso,omitempty"` GSOMaxSize uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"` //Inet4Address []netip.Prefix `yaml:"inet4-address" json:"inet4_address,omitempty"` - Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6_address,omitempty"` - StrictRoute bool `yaml:"strict-route" json:"strict_route,omitempty"` + Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6_address,omitempty"` + IPRoute2TableIndex int `yaml:"iproute2-table-index" json:"iproute2_table_index,omitempty"` + IPRoute2RuleIndex int `yaml:"iproute2-rule-index" json:"iproute2_rule_index,omitempty"` + AutoRedirect bool `yaml:"auto-redirect" json:"auto_redirect,omitempty"` + AutoRedirectInputMark uint32 `yaml:"auto-redirect-input-mark" json:"auto_redirect_input_mark,omitempty"` + AutoRedirectOutputMark uint32 `yaml:"auto-redirect-output-mark" json:"auto_redirect_output_mark,omitempty"` + StrictRoute bool `yaml:"strict-route" json:"strict_route,omitempty"` + RouteAddress []netip.Prefix `yaml:"route-address" json:"route_address,omitempty"` + RouteAddressSet []string `yaml:"route-address-set" json:"route_address_set,omitempty"` + RouteExcludeAddress []netip.Prefix `yaml:"route-exclude-address" json:"route_exclude_address,omitempty"` + RouteExcludeAddressSet []string `yaml:"route-exclude-address-set" json:"route_exclude_address_set,omitempty"` + IncludeInterface []string `yaml:"include-interface" json:"include-interface,omitempty"` + ExcludeInterface []string `yaml:"exclude-interface" json:"exclude-interface,omitempty"` + IncludeUID []uint32 `yaml:"include-uid" json:"include_uid,omitempty"` + IncludeUIDRange []string `yaml:"include-uid-range" json:"include_uid_range,omitempty"` + ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"` + ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude_uid_range,omitempty"` + IncludeAndroidUser []int `yaml:"include-android-user" json:"include_android_user,omitempty"` + IncludePackage []string `yaml:"include-package" json:"include_package,omitempty"` + ExcludePackage []string `yaml:"exclude-package" json:"exclude_package,omitempty"` + EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"` + UDPTimeout int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"` + FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"` + Inet4RouteAddress []netip.Prefix `yaml:"inet4-route-address" json:"inet4_route_address,omitempty"` Inet6RouteAddress []netip.Prefix `yaml:"inet6-route-address" json:"inet6_route_address,omitempty"` Inet4RouteExcludeAddress []netip.Prefix `yaml:"inet4-route-exclude-address" json:"inet4_route_exclude_address,omitempty"` Inet6RouteExcludeAddress []netip.Prefix `yaml:"inet6-route-exclude-address" json:"inet6_route_exclude_address,omitempty"` - IncludeInterface []string `yaml:"include-interface" json:"include-interface,omitempty"` - ExcludeInterface []string `yaml:"exclude-interface" json:"exclude-interface,omitempty"` - IncludeUID []uint32 `yaml:"include-uid" json:"include_uid,omitempty"` - IncludeUIDRange []string `yaml:"include-uid-range" json:"include_uid_range,omitempty"` - ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"` - ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude_uid_range,omitempty"` - IncludeAndroidUser []int `yaml:"include-android-user" json:"include_android_user,omitempty"` - IncludePackage []string `yaml:"include-package" json:"include_package,omitempty"` - ExcludePackage []string `yaml:"exclude-package" json:"exclude_package,omitempty"` - EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"` - UDPTimeout int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"` - FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"` - TableIndex int `yaml:"table-index" json:"table-index"` } type RawTuicServer struct { @@ -564,13 +572,13 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } config.RuleProviders = ruleProviders - subRules, err := parseSubRules(rawCfg, proxies) + subRules, err := parseSubRules(rawCfg, proxies, ruleProviders) if err != nil { return nil, err } config.SubRules = subRules - rules, err := parseRules(rawCfg.Rule, proxies, subRules, "rules") + rules, err := parseRules(rawCfg.Rule, proxies, ruleProviders, subRules, "rules") if err != nil { return nil, err } @@ -666,7 +674,6 @@ func parseGeneral(cfg *RawConfig) (*General, error) { updater.ExternalUIURL = cfg.ExternalUIURL } - cfg.Tun.RedirectToTun = cfg.EBpf.RedirectToTun return &General{ Inbound: Inbound{ Port: cfg.Port, @@ -845,6 +852,7 @@ func parseListeners(cfg *RawConfig) (listeners map[string]C.InboundListener, err } func parseRuleProviders(cfg *RawConfig) (ruleProviders map[string]providerTypes.RuleProvider, err error) { + RP.SetTunnel(T.Tunnel) ruleProviders = map[string]providerTypes.RuleProvider{} // parse rule provider for name, mapping := range cfg.RuleProvider { @@ -854,12 +862,11 @@ func parseRuleProviders(cfg *RawConfig) (ruleProviders map[string]providerTypes. } ruleProviders[name] = rp - RP.SetRuleProvider(rp) } return } -func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy) (subRules map[string][]C.Rule, err error) { +func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy, ruleProviders map[string]providerTypes.RuleProvider) (subRules map[string][]C.Rule, err error) { subRules = map[string][]C.Rule{} for name := range cfg.SubRules { subRules[name] = make([]C.Rule, 0) @@ -869,7 +876,7 @@ func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy) (subRules map[str return nil, fmt.Errorf("sub-rule name is empty") } var rules []C.Rule - rules, err = parseRules(rawRules, proxies, subRules, fmt.Sprintf("sub-rules[%s]", name)) + rules, err = parseRules(rawRules, proxies, ruleProviders, subRules, fmt.Sprintf("sub-rules[%s]", name)) if err != nil { return nil, err } @@ -922,7 +929,7 @@ func verifySubRuleCircularReferences(n string, subRules map[string][]C.Rule, arr return nil } -func parseRules(rulesConfig []string, proxies map[string]C.Proxy, subRules map[string][]C.Rule, format string) ([]C.Rule, error) { +func parseRules(rulesConfig []string, proxies map[string]C.Proxy, ruleProviders map[string]providerTypes.RuleProvider, subRules map[string][]C.Rule, format string) ([]C.Rule, error) { var rules []C.Rule // parse rules @@ -971,6 +978,12 @@ func parseRules(rulesConfig []string, proxies map[string]C.Proxy, subRules map[s return nil, fmt.Errorf("%s[%d] [%s] error: %s", format, idx, line, parseErr.Error()) } + for _, name := range parsed.ProviderNames() { + if _, ok := ruleProviders[name]; !ok { + return nil, fmt.Errorf("%s[%d] [%s] error: rule set [%s] not found", format, idx, line, name) + } + } + rules = append(rules, parsed) } @@ -1455,31 +1468,39 @@ func parseTun(rawTun RawTun, general *General) error { DNSHijack: rawTun.DNSHijack, AutoRoute: rawTun.AutoRoute, AutoDetectInterface: rawTun.AutoDetectInterface, - RedirectToTun: rawTun.RedirectToTun, - MTU: rawTun.MTU, - GSO: rawTun.GSO, - GSOMaxSize: rawTun.GSOMaxSize, - Inet4Address: []netip.Prefix{tunAddressPrefix}, - Inet6Address: rawTun.Inet6Address, - StrictRoute: rawTun.StrictRoute, + MTU: rawTun.MTU, + GSO: rawTun.GSO, + GSOMaxSize: rawTun.GSOMaxSize, + Inet4Address: []netip.Prefix{tunAddressPrefix}, + Inet6Address: rawTun.Inet6Address, + IPRoute2TableIndex: rawTun.IPRoute2TableIndex, + IPRoute2RuleIndex: rawTun.IPRoute2RuleIndex, + AutoRedirect: rawTun.AutoRedirect, + AutoRedirectInputMark: rawTun.AutoRedirectInputMark, + AutoRedirectOutputMark: rawTun.AutoRedirectOutputMark, + StrictRoute: rawTun.StrictRoute, + RouteAddress: rawTun.RouteAddress, + RouteAddressSet: rawTun.RouteAddressSet, + RouteExcludeAddress: rawTun.RouteExcludeAddress, + RouteExcludeAddressSet: rawTun.RouteExcludeAddressSet, + IncludeInterface: rawTun.IncludeInterface, + ExcludeInterface: rawTun.ExcludeInterface, + IncludeUID: rawTun.IncludeUID, + IncludeUIDRange: rawTun.IncludeUIDRange, + ExcludeUID: rawTun.ExcludeUID, + ExcludeUIDRange: rawTun.ExcludeUIDRange, + IncludeAndroidUser: rawTun.IncludeAndroidUser, + IncludePackage: rawTun.IncludePackage, + ExcludePackage: rawTun.ExcludePackage, + EndpointIndependentNat: rawTun.EndpointIndependentNat, + UDPTimeout: rawTun.UDPTimeout, + FileDescriptor: rawTun.FileDescriptor, + Inet4RouteAddress: rawTun.Inet4RouteAddress, Inet6RouteAddress: rawTun.Inet6RouteAddress, Inet4RouteExcludeAddress: rawTun.Inet4RouteExcludeAddress, Inet6RouteExcludeAddress: rawTun.Inet6RouteExcludeAddress, - IncludeInterface: rawTun.IncludeInterface, - ExcludeInterface: rawTun.ExcludeInterface, - IncludeUID: rawTun.IncludeUID, - IncludeUIDRange: rawTun.IncludeUIDRange, - ExcludeUID: rawTun.ExcludeUID, - ExcludeUIDRange: rawTun.ExcludeUIDRange, - IncludeAndroidUser: rawTun.IncludeAndroidUser, - IncludePackage: rawTun.IncludePackage, - ExcludePackage: rawTun.ExcludePackage, - EndpointIndependentNat: rawTun.EndpointIndependentNat, - UDPTimeout: rawTun.UDPTimeout, - FileDescriptor: rawTun.FileDescriptor, - TableIndex: rawTun.TableIndex, } return nil diff --git a/constant/provider/interface.go b/constant/provider/interface.go index bb73d1bc..f7dfc9cc 100644 --- a/constant/provider/interface.go +++ b/constant/provider/interface.go @@ -84,7 +84,7 @@ type RuleProvider interface { Match(*constant.Metadata) bool ShouldResolveIP() bool ShouldFindProcess() bool - AsRule(adaptor string) constant.Rule + Strategy() any } // Rule Behavior @@ -127,3 +127,9 @@ func (rf RuleFormat) String() string { return "Unknown" } } + +type Tunnel interface { + Providers() map[string]ProxyProvider + RuleProviders() map[string]RuleProvider + RuleUpdateCallback() *utils.Callback[RuleProvider] +} diff --git a/constant/rule.go b/constant/rule.go index 161c200a..a91ee6cb 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -116,4 +116,5 @@ type Rule interface { Payload() string ShouldResolveIP() bool ShouldFindProcess() bool + ProviderNames() []string } diff --git a/dns/policy.go b/dns/policy.go index a58123e3..fc60401b 100644 --- a/dns/policy.go +++ b/dns/policy.go @@ -37,14 +37,17 @@ func (p geositePolicy) Match(domain string) []dnsClient { } type domainSetPolicy struct { - domainSetProvider provider.RuleProvider - dnsClients []dnsClient + tunnel provider.Tunnel + name string + dnsClients []dnsClient } func (p domainSetPolicy) Match(domain string) []dnsClient { - metadata := &C.Metadata{Host: domain} - if ok := p.domainSetProvider.Match(metadata); ok { - return p.dnsClients + if ruleProvider, ok := p.tunnel.RuleProviders()[p.name]; ok { + metadata := &C.Metadata{Host: domain} + if ok := ruleProvider.Match(metadata); ok { + return p.dnsClients + } } return nil } diff --git a/dns/resolver.go b/dns/resolver.go index 08de69ad..28ffec6f 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -414,7 +414,7 @@ type Config struct { Pool *fakeip.Pool Hosts *trie.DomainTrie[resolver.HostValue] Policy *orderedmap.OrderedMap[string, []NameServer] - RuleProviders map[string]provider.RuleProvider + Tunnel provider.Tunnel CacheAlgorithm string } @@ -502,11 +502,12 @@ func NewResolver(config Config) *Resolver { key := temp[1] switch prefix { case "rule-set": - if p, ok := config.RuleProviders[key]; ok { + if _, ok := config.Tunnel.RuleProviders()[key]; ok { log.Debugln("Adding rule-set policy: %s ", key) insertPolicy(domainSetPolicy{ - domainSetProvider: p, - dnsClients: cacheTransform(nameserver), + tunnel: config.Tunnel, + name: key, + dnsClients: cacheTransform(nameserver), }) continue } else { diff --git a/docs/config.yaml b/docs/config.yaml index 462b3a44..9c51bc10 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -116,13 +116,25 @@ tun: # mtu: 9000 # 最大传输单元 # gso: false # 启用通用分段卸载,仅支持 Linux # gso-max-size: 65536 # 通用分段卸载包的最大大小 + auto-redirect: false # 自动配置 iptables 以重定向 TCP 连接。仅支持 Linux。带有 auto-redirect 的 auto-route 现在可以在路由器上按预期工作,无需干预。 # strict-route: true # 将所有连接路由到 tun 来防止泄漏,但你的设备将无法其他设备被访问 - inet4-route-address: # 启用 auto-route 时使用自定义路由而不是默认路由 + route-address-set: # 将指定规则集中的目标 IP CIDR 规则添加到防火墙, 不匹配的流量将绕过路由, 仅支持 Linux,且需要 nftables,`auto-route` 和 `auto-redirect` 已启用。 + - ruleset-1 + - ruleset-2 + route-exclude-address-set: # 将指定规则集中的目标 IP CIDR 规则添加到防火墙, 匹配的流量将绕过路由, 仅支持 Linux,且需要 nftables,`auto-route` 和 `auto-redirect` 已启用。 + - ruleset-3 + - ruleset-4 + route-address: # 启用 auto-route 时使用自定义路由而不是默认路由 - 0.0.0.0/1 - 128.0.0.0/1 - inet6-route-address: # 启用 auto-route 时使用自定义路由而不是默认路由 - "::/1" - "8000::/1" + # inet4-route-address: # 启用 auto-route 时使用自定义路由而不是默认路由(旧写法) + # - 0.0.0.0/1 + # - 128.0.0.0/1 + # inet6-route-address: # 启用 auto-route 时使用自定义路由而不是默认路由(旧写法) + # - "::/1" + # - "8000::/1" # endpoint-independent-nat: false # 启用独立于端点的 NAT # include-interface: # 限制被路由的接口。默认不限制,与 `exclude-interface` 冲突 # - "lan0" diff --git a/go.mod b/go.mod index 5d109d8d..ee0eeeb4 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 - github.com/metacubex/sing-tun v0.2.7-0.20240521155100-e8316a45a414 + github.com/metacubex/sing-tun v0.2.7-0.20240617013029-d05cf9df9cfe github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 @@ -35,8 +35,8 @@ require ( github.com/oschwald/maxminddb-golang v1.12.0 github.com/puzpuzpuz/xsync/v3 v3.1.0 github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a - github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.3.8 + github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a + github.com/sagernet/sing v0.5.0-alpha.10 github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 github.com/sagernet/sing-shadowtls v0.1.4 github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e @@ -47,11 +47,11 @@ require ( github.com/wk8/go-ordered-map/v2 v2.1.8 go.uber.org/automaxprocs v1.5.3 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba - golang.org/x/crypto v0.23.0 - golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 - golang.org/x/net v0.25.0 + golang.org/x/crypto v0.24.0 + golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 + golang.org/x/net v0.26.0 golang.org/x/sync v0.7.0 - golang.org/x/sys v0.20.0 + golang.org/x/sys v0.21.0 google.golang.org/protobuf v1.34.1 gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.3.0 @@ -92,6 +92,7 @@ require ( 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.4.1 // indirect + github.com/sagernet/nftables v0.3.0-beta.4 // indirect github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect @@ -100,14 +101,14 @@ require ( 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/vishvananda/netns v0.0.4 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect go.uber.org/mock v0.4.0 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/text v0.15.0 // indirect + golang.org/x/mod v0.18.0 // indirect + golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.21.0 // indirect + golang.org/x/tools v0.22.0 // indirect ) -replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1 +replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240617013425-3e3bd9dab6a2 diff --git a/go.sum b/go.sum index 5c8219c2..a1d0cf5a 100644 --- a/go.sum +++ b/go.sum @@ -108,16 +108,16 @@ github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e h1:bLYn3GuRvW github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e/go.mod h1:Yza2H7Ax1rxWPUcJx0vW+oAt9EsPuSiyQFhFabUPzwU= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= -github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1 h1:7hDHLTmjgtRoAp59STwPQpe5Pinwi4cWex+FB3Ohvco= -github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= +github.com/metacubex/sing v0.0.0-20240617013425-3e3bd9dab6a2 h1:N5tidgg/FRmkgPw/AjRwhLUinKDx/ODCSbvv9xqRoLM= +github.com/metacubex/sing v0.0.0-20240617013425-3e3bd9dab6a2/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 h1:Wr4g1HCb5Z/QIFwFiVNjO2qL+dRu25+Mdn9xtAZZ+ew= github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8= github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ= github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A= github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8= -github.com/metacubex/sing-tun v0.2.7-0.20240521155100-e8316a45a414 h1:IPxTZgQV6fVUBS8tozLMSFPHV3imYc/NbuGfp0bLQq0= -github.com/metacubex/sing-tun v0.2.7-0.20240521155100-e8316a45a414/go.mod h1:4VsMwZH1IlgPGFK1ZbBomZ/B2MYkTgs2+gnBAr5GOIo= +github.com/metacubex/sing-tun v0.2.7-0.20240617013029-d05cf9df9cfe h1:NrWjVEkRmEkdREVSpohMgEBoznS0PrRfJDr6iCV4348= +github.com/metacubex/sing-tun v0.2.7-0.20240617013029-d05cf9df9cfe/go.mod h1:WwJGbCx7bQcBzuQXiDOJvZH27R0kIjKNNlISIWsL6kM= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbTMBip/C+R79bvbfr42xH1gZl3uFb0RELdZiQ= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 h1:AGyIB55UfQm/0ZH0HtQO9u3l//yjtHUpjeRjjPGfGRI= @@ -159,8 +159,10 @@ github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58 github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM= -github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= -github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= +github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis= +github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= +github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I= +github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8= github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 h1:5bCAkvDDzSMITiHFjolBwpdqYsvycdTu71FsMEFXQ14= github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ= github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k= @@ -206,8 +208,8 @@ github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -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/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= +github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= @@ -222,18 +224,18 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= 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.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= 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.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -252,19 +254,18 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= 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= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 56e71632..55c40b6d 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -97,7 +97,7 @@ func ApplyConfig(cfg *config.Config, force bool) { updateHosts(cfg.Hosts) updateGeneral(cfg.General) updateNTP(cfg.NTP) - updateDNS(cfg.DNS, cfg.RuleProviders, cfg.General.IPv6) + updateDNS(cfg.DNS, cfg.General.IPv6) updateListeners(cfg.General, cfg.Listeners, force) updateIPTables(cfg) updateTun(cfg.General) @@ -211,7 +211,7 @@ func updateNTP(c *config.NTP) { } } -func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, generalIPv6 bool) { +func updateDNS(c *config.DNS, generalIPv6 bool) { if !c.Enable { resolver.DefaultResolver = nil resolver.DefaultHostMapper = nil @@ -237,7 +237,7 @@ func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, gen Default: c.DefaultNameserver, Policy: c.NameServerPolicy, ProxyServer: c.ProxyServerNameserver, - RuleProviders: ruleProvider, + Tunnel: tunnel.Tunnel, CacheAlgorithm: c.CacheAlgorithm, } @@ -355,7 +355,7 @@ func updateTun(general *config.General) { return } listener.ReCreateTun(general.Tun, tunnel.Tunnel) - listener.ReCreateRedirToTun(general.Tun.RedirectToTun) + listener.ReCreateRedirToTun(general.EBpf.RedirectToTun) } func updateSniffer(sniffer *config.Sniffer) { @@ -507,9 +507,7 @@ func updateIPTables(cfg *config.Config) { inboundInterface = iptables.InboundInterface } - if dialer.DefaultRoutingMark.Load() == 0 { - dialer.DefaultRoutingMark.Store(2158) - } + dialer.DefaultRoutingMark.CompareAndSwap(0, 2158) err = tproxy.SetTProxyIPTables(inboundInterface, bypass, uint16(tProxyPort), DnsRedirect, dnsPort.Port()) if err != nil { diff --git a/hub/route/configs.go b/hub/route/configs.go index f2cf298a..17d858d4 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -68,25 +68,34 @@ type tunSchema struct { GSO *bool `yaml:"gso" json:"gso,omitempty"` GSOMaxSize *uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"` //Inet4Address *[]netip.Prefix `yaml:"inet4-address" json:"inet4-address,omitempty"` - Inet6Address *[]netip.Prefix `yaml:"inet6-address" json:"inet6-address,omitempty"` - StrictRoute *bool `yaml:"strict-route" json:"strict-route,omitempty"` + Inet6Address *[]netip.Prefix `yaml:"inet6-address" json:"inet6-address,omitempty"` + IPRoute2TableIndex *int `yaml:"iproute2-table-index" json:"iproute2_table_index,omitempty"` + IPRoute2RuleIndex *int `yaml:"iproute2-rule-index" json:"iproute2_rule_index,omitempty"` + AutoRedirect *bool `yaml:"auto-redirect" json:"auto_redirect,omitempty"` + AutoRedirectInputMark *uint32 `yaml:"auto-redirect-input-mark" json:"auto_redirect_input_mark,omitempty"` + AutoRedirectOutputMark *uint32 `yaml:"auto-redirect-output-mark" json:"auto_redirect_output_mark,omitempty"` + StrictRoute *bool `yaml:"strict-route" json:"strict-route,omitempty"` + RouteAddress *[]netip.Prefix `yaml:"route-address" json:"route_address,omitempty"` + RouteAddressSet *[]string `yaml:"route-address-set" json:"route_address_set,omitempty"` + RouteExcludeAddress *[]netip.Prefix `yaml:"route-exclude-address" json:"route_exclude_address,omitempty"` + RouteExcludeAddressSet *[]string `yaml:"route-exclude-address-set" json:"route_exclude_address_set,omitempty"` + IncludeInterface *[]string `yaml:"include-interface" json:"include-interface,omitempty"` + ExcludeInterface *[]string `yaml:"exclude-interface" json:"exclude-interface,omitempty"` + IncludeUID *[]uint32 `yaml:"include-uid" json:"include-uid,omitempty"` + IncludeUIDRange *[]string `yaml:"include-uid-range" json:"include-uid-range,omitempty"` + ExcludeUID *[]uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"` + ExcludeUIDRange *[]string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"` + IncludeAndroidUser *[]int `yaml:"include-android-user" json:"include-android-user,omitempty"` + IncludePackage *[]string `yaml:"include-package" json:"include-package,omitempty"` + ExcludePackage *[]string `yaml:"exclude-package" json:"exclude-package,omitempty"` + EndpointIndependentNat *bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"` + UDPTimeout *int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"` + FileDescriptor *int `yaml:"file-descriptor" json:"file-descriptor"` + Inet4RouteAddress *[]netip.Prefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"` Inet6RouteAddress *[]netip.Prefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"` Inet4RouteExcludeAddress *[]netip.Prefix `yaml:"inet4-route-exclude-address" json:"inet4-route-exclude-address,omitempty"` Inet6RouteExcludeAddress *[]netip.Prefix `yaml:"inet6-route-exclude-address" json:"inet6-route-exclude-address,omitempty"` - IncludeInterface *[]string `yaml:"include-interface" json:"include-interface,omitempty"` - ExcludeInterface *[]string `yaml:"exclude-interface" json:"exclude-interface,omitempty"` - IncludeUID *[]uint32 `yaml:"include-uid" json:"include-uid,omitempty"` - IncludeUIDRange *[]string `yaml:"include-uid-range" json:"include-uid-range,omitempty"` - ExcludeUID *[]uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"` - ExcludeUIDRange *[]string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"` - IncludeAndroidUser *[]int `yaml:"include-android-user" json:"include-android-user,omitempty"` - IncludePackage *[]string `yaml:"include-package" json:"include-package,omitempty"` - ExcludePackage *[]string `yaml:"exclude-package" json:"exclude-package,omitempty"` - EndpointIndependentNat *bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"` - UDPTimeout *int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"` - FileDescriptor *int `yaml:"file-descriptor" json:"file-descriptor"` - TableIndex *int `yaml:"table-index" json:"table-index"` } type tuicServerSchema struct { @@ -157,6 +166,36 @@ func pointerOrDefaultTun(p *tunSchema, def LC.Tun) LC.Tun { if p.Inet6Address != nil { def.Inet6Address = *p.Inet6Address } + if p.IPRoute2TableIndex != nil { + def.IPRoute2TableIndex = *p.IPRoute2TableIndex + } + if p.IPRoute2RuleIndex != nil { + def.IPRoute2RuleIndex = *p.IPRoute2RuleIndex + } + if p.AutoRedirect != nil { + def.AutoRedirect = *p.AutoRedirect + } + if p.AutoRedirectInputMark != nil { + def.AutoRedirectInputMark = *p.AutoRedirectInputMark + } + if p.AutoRedirectOutputMark != nil { + def.AutoRedirectOutputMark = *p.AutoRedirectOutputMark + } + if p.StrictRoute != nil { + def.StrictRoute = *p.StrictRoute + } + if p.RouteAddress != nil { + def.RouteAddress = *p.RouteAddress + } + if p.RouteAddressSet != nil { + def.RouteAddressSet = *p.RouteAddressSet + } + if p.RouteExcludeAddress != nil { + def.RouteExcludeAddress = *p.RouteExcludeAddress + } + if p.RouteExcludeAddressSet != nil { + def.RouteExcludeAddressSet = *p.RouteExcludeAddressSet + } if p.Inet4RouteAddress != nil { def.Inet4RouteAddress = *p.Inet4RouteAddress } @@ -205,9 +244,6 @@ func pointerOrDefaultTun(p *tunSchema, def LC.Tun) LC.Tun { if p.FileDescriptor != nil { def.FileDescriptor = *p.FileDescriptor } - if p.TableIndex != nil { - def.TableIndex = *p.TableIndex - } } return def } diff --git a/listener/config/tun.go b/listener/config/tun.go index 7467e4a6..cea22bfd 100644 --- a/listener/config/tun.go +++ b/listener/config/tun.go @@ -27,27 +27,36 @@ type Tun struct { AutoDetectInterface bool `yaml:"auto-detect-interface" json:"auto-detect-interface"` RedirectToTun []string `yaml:"-" json:"-"` - MTU uint32 `yaml:"mtu" json:"mtu,omitempty"` - GSO bool `yaml:"gso" json:"gso,omitempty"` - GSOMaxSize uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"` - Inet4Address []netip.Prefix `yaml:"inet4-address" json:"inet4-address,omitempty"` - Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6-address,omitempty"` - StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"` + MTU uint32 `yaml:"mtu" json:"mtu,omitempty"` + GSO bool `yaml:"gso" json:"gso,omitempty"` + GSOMaxSize uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"` + Inet4Address []netip.Prefix `yaml:"inet4-address" json:"inet4-address,omitempty"` + Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6-address,omitempty"` + IPRoute2TableIndex int `yaml:"iproute2-table-index" json:"iproute2_table_index,omitempty"` + IPRoute2RuleIndex int `yaml:"iproute2-rule-index" json:"iproute2_rule_index,omitempty"` + AutoRedirect bool `yaml:"auto-redirect" json:"auto_redirect,omitempty"` + AutoRedirectInputMark uint32 `yaml:"auto-redirect-input-mark" json:"auto_redirect_input_mark,omitempty"` + AutoRedirectOutputMark uint32 `yaml:"auto-redirect-output-mark" json:"auto_redirect_output_mark,omitempty"` + StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"` + RouteAddress []netip.Prefix `yaml:"route-address" json:"route_address,omitempty"` + RouteAddressSet []string `yaml:"route-address-set" json:"route_address_set,omitempty"` + RouteExcludeAddress []netip.Prefix `yaml:"route-exclude-address" json:"route_exclude_address,omitempty"` + RouteExcludeAddressSet []string `yaml:"route-exclude-address-set" json:"route_exclude_address_set,omitempty"` + IncludeInterface []string `yaml:"include-interface" json:"include-interface,omitempty"` + ExcludeInterface []string `yaml:"exclude-interface" json:"exclude-interface,omitempty"` + IncludeUID []uint32 `yaml:"include-uid" json:"include-uid,omitempty"` + IncludeUIDRange []string `yaml:"include-uid-range" json:"include-uid-range,omitempty"` + ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"` + ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"` + IncludeAndroidUser []int `yaml:"include-android-user" json:"include-android-user,omitempty"` + IncludePackage []string `yaml:"include-package" json:"include-package,omitempty"` + ExcludePackage []string `yaml:"exclude-package" json:"exclude-package,omitempty"` + EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"` + UDPTimeout int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"` + FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"` + Inet4RouteAddress []netip.Prefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"` Inet6RouteAddress []netip.Prefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"` Inet4RouteExcludeAddress []netip.Prefix `yaml:"inet4-route-exclude-address" json:"inet4-route-exclude-address,omitempty"` Inet6RouteExcludeAddress []netip.Prefix `yaml:"inet6-route-exclude-address" json:"inet6-route-exclude-address,omitempty"` - IncludeInterface []string `yaml:"include-interface" json:"include-interface,omitempty"` - ExcludeInterface []string `yaml:"exclude-interface" json:"exclude-interface,omitempty"` - IncludeUID []uint32 `yaml:"include-uid" json:"include-uid,omitempty"` - IncludeUIDRange []string `yaml:"include-uid-range" json:"include-uid-range,omitempty"` - ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"` - ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"` - IncludeAndroidUser []int `yaml:"include-android-user" json:"include-android-user,omitempty"` - IncludePackage []string `yaml:"include-package" json:"include-package,omitempty"` - ExcludePackage []string `yaml:"exclude-package" json:"exclude-package,omitempty"` - EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"` - UDPTimeout int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"` - FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"` - TableIndex int `yaml:"table-index" json:"table-index"` } diff --git a/listener/inbound/tun.go b/listener/inbound/tun.go index 51747c46..a950f80d 100644 --- a/listener/inbound/tun.go +++ b/listener/inbound/tun.go @@ -18,29 +18,38 @@ type TunOption struct { AutoRoute bool `inbound:"auto-route,omitempty"` AutoDetectInterface bool `inbound:"auto-detect-interface,omitempty"` - MTU uint32 `inbound:"mtu,omitempty"` - GSO bool `inbound:"gso,omitempty"` - GSOMaxSize uint32 `inbound:"gso-max-size,omitempty"` - Inet4Address []string `inbound:"inet4_address,omitempty"` - Inet6Address []string `inbound:"inet6_address,omitempty"` - StrictRoute bool `inbound:"strict_route,omitempty"` + MTU uint32 `inbound:"mtu,omitempty"` + GSO bool `inbound:"gso,omitempty"` + GSOMaxSize uint32 `inbound:"gso-max-size,omitempty"` + Inet4Address []string `inbound:"inet4_address,omitempty"` + Inet6Address []string `inbound:"inet6_address,omitempty"` + IPRoute2TableIndex int `inbound:"iproute2-table-index"` + IPRoute2RuleIndex int `inbound:"iproute2-rule-index"` + AutoRedirect bool `inbound:"auto-redirect"` + AutoRedirectInputMark uint32 `inbound:"auto-redirect-input-mark"` + AutoRedirectOutputMark uint32 `inbound:"auto-redirect-output-mark"` + StrictRoute bool `inbound:"strict_route,omitempty"` + RouteAddress []string `inbound:"route-address"` + RouteAddressSet []string `inbound:"route-address-set"` + RouteExcludeAddress []string `inbound:"route-exclude-address"` + RouteExcludeAddressSet []string `inbound:"route-exclude-address-set"` + IncludeInterface []string `inbound:"include-interface,omitempty"` + ExcludeInterface []string `inbound:"exclude-interface"` + IncludeUID []uint32 `inbound:"include_uid,omitempty"` + IncludeUIDRange []string `inbound:"include_uid_range,omitempty"` + ExcludeUID []uint32 `inbound:"exclude_uid,omitempty"` + ExcludeUIDRange []string `inbound:"exclude_uid_range,omitempty"` + IncludeAndroidUser []int `inbound:"include_android_user,omitempty"` + IncludePackage []string `inbound:"include_package,omitempty"` + ExcludePackage []string `inbound:"exclude_package,omitempty"` + EndpointIndependentNat bool `inbound:"endpoint_independent_nat,omitempty"` + UDPTimeout int64 `inbound:"udp_timeout,omitempty"` + FileDescriptor int `inbound:"file-descriptor,omitempty"` + Inet4RouteAddress []string `inbound:"inet4_route_address,omitempty"` Inet6RouteAddress []string `inbound:"inet6_route_address,omitempty"` Inet4RouteExcludeAddress []string `inbound:"inet4_route_exclude_address,omitempty"` Inet6RouteExcludeAddress []string `inbound:"inet6_route_exclude_address,omitempty"` - IncludeInterface []string `inbound:"include-interface,omitempty"` - ExcludeInterface []string `inbound:"exclude-interface" json:"exclude-interface,omitempty"` - IncludeUID []uint32 `inbound:"include_uid,omitempty"` - IncludeUIDRange []string `inbound:"include_uid_range,omitempty"` - ExcludeUID []uint32 `inbound:"exclude_uid,omitempty"` - ExcludeUIDRange []string `inbound:"exclude_uid_range,omitempty"` - IncludeAndroidUser []int `inbound:"include_android_user,omitempty"` - IncludePackage []string `inbound:"include_package,omitempty"` - ExcludePackage []string `inbound:"exclude_package,omitempty"` - EndpointIndependentNat bool `inbound:"endpoint_independent_nat,omitempty"` - UDPTimeout int64 `inbound:"udp_timeout,omitempty"` - FileDescriptor int `inbound:"file-descriptor,omitempty"` - TableIndex int `inbound:"table-index,omitempty"` } func (o TunOption) Equal(config C.InboundConfig) bool { @@ -63,6 +72,16 @@ func NewTun(options *TunOption) (*Tun, error) { if !exist { return nil, errors.New("invalid tun stack") } + + routeAddress, err := LC.StringSliceToNetipPrefixSlice(options.RouteAddress) + if err != nil { + return nil, err + } + routeExcludeAddress, err := LC.StringSliceToNetipPrefixSlice(options.RouteExcludeAddress) + if err != nil { + return nil, err + } + inet4Address, err := LC.StringSliceToNetipPrefixSlice(options.Inet4Address) if err != nil { return nil, err @@ -91,35 +110,44 @@ func NewTun(options *TunOption) (*Tun, error) { Base: base, config: options, tun: LC.Tun{ - Enable: true, - Device: options.Device, - Stack: stack, - DNSHijack: options.DNSHijack, - AutoRoute: options.AutoRoute, - AutoDetectInterface: options.AutoDetectInterface, - MTU: options.MTU, - GSO: options.GSO, - GSOMaxSize: options.GSOMaxSize, - Inet4Address: inet4Address, - Inet6Address: inet6Address, - StrictRoute: options.StrictRoute, + Enable: true, + Device: options.Device, + Stack: stack, + DNSHijack: options.DNSHijack, + AutoRoute: options.AutoRoute, + AutoDetectInterface: options.AutoDetectInterface, + MTU: options.MTU, + GSO: options.GSO, + GSOMaxSize: options.GSOMaxSize, + Inet4Address: inet4Address, + Inet6Address: inet6Address, + IPRoute2TableIndex: options.IPRoute2TableIndex, + IPRoute2RuleIndex: options.IPRoute2RuleIndex, + AutoRedirect: options.AutoRedirect, + AutoRedirectInputMark: options.AutoRedirectInputMark, + AutoRedirectOutputMark: options.AutoRedirectOutputMark, + StrictRoute: options.StrictRoute, + RouteAddress: routeAddress, + RouteAddressSet: options.RouteAddressSet, + RouteExcludeAddress: routeExcludeAddress, + RouteExcludeAddressSet: options.RouteExcludeAddressSet, + IncludeInterface: options.IncludeInterface, + ExcludeInterface: options.ExcludeInterface, + IncludeUID: options.IncludeUID, + IncludeUIDRange: options.IncludeUIDRange, + ExcludeUID: options.ExcludeUID, + ExcludeUIDRange: options.ExcludeUIDRange, + IncludeAndroidUser: options.IncludeAndroidUser, + IncludePackage: options.IncludePackage, + ExcludePackage: options.ExcludePackage, + EndpointIndependentNat: options.EndpointIndependentNat, + UDPTimeout: options.UDPTimeout, + FileDescriptor: options.FileDescriptor, + Inet4RouteAddress: inet4RouteAddress, Inet6RouteAddress: inet6RouteAddress, Inet4RouteExcludeAddress: inet4RouteExcludeAddress, Inet6RouteExcludeAddress: inet6RouteExcludeAddress, - IncludeInterface: options.IncludeInterface, - ExcludeInterface: options.ExcludeInterface, - IncludeUID: options.IncludeUID, - IncludeUIDRange: options.IncludeUIDRange, - ExcludeUID: options.ExcludeUID, - ExcludeUIDRange: options.ExcludeUIDRange, - IncludeAndroidUser: options.IncludeAndroidUser, - IncludePackage: options.IncludePackage, - ExcludePackage: options.ExcludePackage, - EndpointIndependentNat: options.EndpointIndependentNat, - UDPTimeout: options.UDPTimeout, - FileDescriptor: options.FileDescriptor, - TableIndex: options.TableIndex, }, }, nil } diff --git a/listener/listener.go b/listener/listener.go index e3506188..76860e0d 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -820,11 +820,15 @@ func hasTunConfigChange(tunConf *LC.Tun) bool { LastTunConf.MTU != tunConf.MTU || LastTunConf.GSO != tunConf.GSO || LastTunConf.GSOMaxSize != tunConf.GSOMaxSize || + LastTunConf.IPRoute2TableIndex != tunConf.IPRoute2TableIndex || + LastTunConf.IPRoute2RuleIndex != tunConf.IPRoute2RuleIndex || + LastTunConf.AutoRedirect != tunConf.AutoRedirect || + LastTunConf.AutoRedirectInputMark != tunConf.AutoRedirectInputMark || + LastTunConf.AutoRedirectOutputMark != tunConf.AutoRedirectOutputMark || LastTunConf.StrictRoute != tunConf.StrictRoute || LastTunConf.EndpointIndependentNat != tunConf.EndpointIndependentNat || LastTunConf.UDPTimeout != tunConf.UDPTimeout || - LastTunConf.FileDescriptor != tunConf.FileDescriptor || - LastTunConf.TableIndex != tunConf.TableIndex { + LastTunConf.FileDescriptor != tunConf.FileDescriptor { return true } @@ -836,6 +840,22 @@ func hasTunConfigChange(tunConf *LC.Tun) bool { return tunConf.DNSHijack[i] < tunConf.DNSHijack[j] }) + sort.Slice(tunConf.RouteAddress, func(i, j int) bool { + return tunConf.RouteAddress[i].String() < tunConf.RouteAddress[j].String() + }) + + sort.Slice(tunConf.RouteAddressSet, func(i, j int) bool { + return tunConf.RouteAddressSet[i] < tunConf.RouteAddressSet[j] + }) + + sort.Slice(tunConf.RouteExcludeAddress, func(i, j int) bool { + return tunConf.RouteExcludeAddress[i].String() < tunConf.RouteExcludeAddress[j].String() + }) + + sort.Slice(tunConf.RouteExcludeAddressSet, func(i, j int) bool { + return tunConf.RouteExcludeAddressSet[i] < tunConf.RouteExcludeAddressSet[j] + }) + sort.Slice(tunConf.Inet4Address, func(i, j int) bool { return tunConf.Inet4Address[i].String() < tunConf.Inet4Address[j].String() }) @@ -897,6 +917,10 @@ func hasTunConfigChange(tunConf *LC.Tun) bool { }) if !slices.Equal(tunConf.DNSHijack, LastTunConf.DNSHijack) || + !slices.Equal(tunConf.RouteAddress, LastTunConf.RouteAddress) || + !slices.Equal(tunConf.RouteAddressSet, LastTunConf.RouteAddressSet) || + !slices.Equal(tunConf.RouteExcludeAddress, LastTunConf.RouteExcludeAddress) || + !slices.Equal(tunConf.RouteExcludeAddressSet, LastTunConf.RouteExcludeAddressSet) || !slices.Equal(tunConf.Inet4Address, LastTunConf.Inet4Address) || !slices.Equal(tunConf.Inet6Address, LastTunConf.Inet6Address) || !slices.Equal(tunConf.Inet4RouteAddress, LastTunConf.Inet4RouteAddress) || diff --git a/listener/sing/sing.go b/listener/sing/sing.go index 4e31faeb..10390e73 100644 --- a/listener/sing/sing.go +++ b/listener/sing/sing.go @@ -198,6 +198,12 @@ func (h *ListenerHandler) NewError(ctx context.Context, err error) { log.Warnln("%s listener get error: %+v", h.Type.String(), err) } +func (h *ListenerHandler) TypeMutation(typ C.Type) *ListenerHandler { + handler := *h + handler.Type = typ + return &handler +} + func ShouldIgnorePacketError(err error) bool { // ignore simple error if E.IsTimeout(err) || E.IsClosed(err) || E.IsCanceled(err) { diff --git a/listener/sing_tun/dns.go b/listener/sing_tun/dns.go index 42926732..505f16ac 100644 --- a/listener/sing_tun/dns.go +++ b/listener/sing_tun/dns.go @@ -8,6 +8,7 @@ import ( "time" "github.com/metacubex/mihomo/component/resolver" + C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/listener/sing" "github.com/metacubex/mihomo/log" @@ -124,3 +125,9 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network. } return h.ListenerHandler.NewPacketConnection(ctx, conn, metadata) } + +func (h *ListenerHandler) TypeMutation(typ C.Type) *ListenerHandler { + handle := *h + handle.ListenerHandler = h.ListenerHandler.TypeMutation(typ) + return &handle +} diff --git a/listener/sing_tun/iface.go b/listener/sing_tun/iface.go new file mode 100644 index 00000000..cc142078 --- /dev/null +++ b/listener/sing_tun/iface.go @@ -0,0 +1,70 @@ +package sing_tun + +import ( + "errors" + "net/netip" + + "github.com/metacubex/mihomo/component/iface" + + "github.com/sagernet/sing/common/control" +) + +type defaultInterfaceFinder struct{} + +var DefaultInterfaceFinder control.InterfaceFinder = (*defaultInterfaceFinder)(nil) + +func (f *defaultInterfaceFinder) Interfaces() []control.Interface { + ifaces, err := iface.Interfaces() + if err != nil { + return nil + } + interfaces := make([]control.Interface, 0, len(ifaces)) + for _, _interface := range ifaces { + interfaces = append(interfaces, control.Interface(*_interface)) + } + + return interfaces +} + +var errNoSuchInterface = errors.New("no such network interface") + +func (f *defaultInterfaceFinder) InterfaceIndexByName(name string) (int, error) { + ifaces, err := iface.Interfaces() + if err != nil { + return 0, err + } + for _, netInterface := range ifaces { + if netInterface.Name == name { + return netInterface.Index, nil + } + } + return 0, errNoSuchInterface +} + +func (f *defaultInterfaceFinder) InterfaceNameByIndex(index int) (string, error) { + ifaces, err := iface.Interfaces() + if err != nil { + return "", err + } + for _, netInterface := range ifaces { + if netInterface.Index == index { + return netInterface.Name, nil + } + } + return "", errNoSuchInterface +} + +func (f *defaultInterfaceFinder) InterfaceByAddr(addr netip.Addr) (*control.Interface, error) { + ifaces, err := iface.Interfaces() + if err != nil { + return nil, err + } + for _, netInterface := range ifaces { + for _, prefix := range netInterface.Addresses { + if prefix.Contains(addr) { + return (*control.Interface)(netInterface), nil + } + } + } + return nil, errNoSuchInterface +} diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index a5edb77f..53b88528 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -3,27 +3,33 @@ package sing_tun import ( "context" "fmt" + "io" "net" "net/netip" + "os" "runtime" "strconv" "strings" + "sync" "github.com/metacubex/mihomo/adapter/inbound" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/iface" "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/constant/provider" LC "github.com/metacubex/mihomo/listener/config" "github.com/metacubex/mihomo/listener/sing" "github.com/metacubex/mihomo/log" tun "github.com/metacubex/sing-tun" "github.com/sagernet/sing/common" - "github.com/sagernet/sing/common/control" E "github.com/sagernet/sing/common/exceptions" F "github.com/sagernet/sing/common/format" "github.com/sagernet/sing/common/ranges" + + "go4.org/netipx" + "golang.org/x/exp/maps" "golang.org/x/exp/slices" ) @@ -43,10 +49,21 @@ type Listener struct { networkUpdateMonitor tun.NetworkUpdateMonitor defaultInterfaceMonitor tun.DefaultInterfaceMonitor packageManager tun.PackageManager + autoRedirect tun.AutoRedirect + autoRedirectOutputMark int32 + + ruleUpdateCallbackCloser io.Closer + ruleUpdateMutex sync.Mutex + routeAddressMap map[string]*netipx.IPSet + routeExcludeAddressMap map[string]*netipx.IPSet + routeAddressSet []*netipx.IPSet + routeExcludeAddressSet []*netipx.IPSet dnsServerIp []string } +var emptyAddressSet = []*netipx.IPSet{{}} + func CalculateInterfaceName(name string) (tunName string) { if runtime.GOOS == "darwin" { tunName = "utun" @@ -110,14 +127,45 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis inbound.WithSpecialRules(""), } } + ctx := context.TODO() + rpTunnel := tunnel.(provider.Tunnel) if options.GSOMaxSize == 0 { options.GSOMaxSize = 65536 } + if runtime.GOOS != "linux" { + options.AutoRedirect = false + } tunName := options.Device if tunName == "" || !checkTunName(tunName) { tunName = CalculateInterfaceName(InterfaceName) options.Device = tunName } + routeAddress := options.RouteAddress + if len(options.Inet4RouteAddress) > 0 { + routeAddress = append(routeAddress, options.Inet4RouteAddress...) + } + if len(options.Inet6RouteAddress) > 0 { + routeAddress = append(routeAddress, options.Inet6RouteAddress...) + } + inet4RouteAddress := common.Filter(routeAddress, func(it netip.Prefix) bool { + return it.Addr().Is4() + }) + inet6RouteAddress := common.Filter(routeAddress, func(it netip.Prefix) bool { + return it.Addr().Is6() + }) + routeExcludeAddress := options.RouteExcludeAddress + if len(options.Inet4RouteExcludeAddress) > 0 { + routeExcludeAddress = append(routeExcludeAddress, options.Inet4RouteExcludeAddress...) + } + if len(options.Inet6RouteExcludeAddress) > 0 { + routeExcludeAddress = append(routeExcludeAddress, options.Inet6RouteExcludeAddress...) + } + inet4RouteExcludeAddress := common.Filter(routeExcludeAddress, func(it netip.Prefix) bool { + return it.Addr().Is4() + }) + inet6RouteExcludeAddress := common.Filter(routeExcludeAddress, func(it netip.Prefix) bool { + return it.Addr().Is6() + }) tunMTU := options.MTU if tunMTU == 0 { tunMTU = 9000 @@ -128,9 +176,21 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis } else { udpTimeout = int64(sing.UDPTimeout.Seconds()) } - tableIndex := options.TableIndex + tableIndex := options.IPRoute2TableIndex if tableIndex == 0 { - tableIndex = 2022 + tableIndex = tun.DefaultIPRoute2TableIndex + } + ruleIndex := options.IPRoute2RuleIndex + if ruleIndex == 0 { + ruleIndex = tun.DefaultIPRoute2RuleIndex + } + inputMark := options.AutoRedirectInputMark + if inputMark == 0 { + inputMark = tun.DefaultAutoRedirectInputMark + } + outputMark := options.AutoRedirectOutputMark + if outputMark == 0 { + outputMark = tun.DefaultAutoRedirectOutputMark } includeUID := uidToRange(options.IncludeUID) if len(options.IncludeUIDRange) > 0 { @@ -202,6 +262,8 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis } }() + interfaceFinder := DefaultInterfaceFinder + networkUpdateMonitor, err := tun.NewNetworkUpdateMonitor(log.SingLogger) if err != nil { err = E.Cause(err, "create NetworkUpdateMonitor") @@ -236,11 +298,15 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis Inet4Address: options.Inet4Address, Inet6Address: options.Inet6Address, AutoRoute: options.AutoRoute, + IPRoute2TableIndex: tableIndex, + IPRoute2RuleIndex: ruleIndex, + AutoRedirectInputMark: inputMark, + AutoRedirectOutputMark: outputMark, StrictRoute: options.StrictRoute, - Inet4RouteAddress: options.Inet4RouteAddress, - Inet6RouteAddress: options.Inet6RouteAddress, - Inet4RouteExcludeAddress: options.Inet4RouteExcludeAddress, - Inet6RouteExcludeAddress: options.Inet6RouteExcludeAddress, + Inet4RouteAddress: inet4RouteAddress, + Inet6RouteAddress: inet6RouteAddress, + Inet4RouteExcludeAddress: inet4RouteExcludeAddress, + Inet6RouteExcludeAddress: inet6RouteExcludeAddress, IncludeInterface: options.IncludeInterface, ExcludeInterface: options.ExcludeInterface, IncludeUID: includeUID, @@ -250,7 +316,56 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis ExcludePackage: options.ExcludePackage, FileDescriptor: options.FileDescriptor, InterfaceMonitor: defaultInterfaceMonitor, - TableIndex: tableIndex, + } + + if options.AutoRedirect { + l.routeAddressMap = make(map[string]*netipx.IPSet) + l.routeExcludeAddressMap = make(map[string]*netipx.IPSet) + + if !options.AutoRoute { + return nil, E.New("`auto-route` is required by `auto-redirect`") + } + disableNFTables, dErr := strconv.ParseBool(os.Getenv("DISABLE_NFTABLES")) + l.autoRedirect, err = tun.NewAutoRedirect(tun.AutoRedirectOptions{ + TunOptions: &tunOptions, + Context: ctx, + Handler: handler.TypeMutation(C.REDIR), + Logger: log.SingLogger, + NetworkMonitor: networkUpdateMonitor, + InterfaceFinder: interfaceFinder, + TableName: "mihomo", + DisableNFTables: dErr == nil && disableNFTables, + RouteAddressSet: &l.routeAddressSet, + RouteExcludeAddressSet: &l.routeExcludeAddressSet, + }) + if err != nil { + err = E.Cause(err, "initialize auto redirect") + return + } + + var markMode bool + for _, routeAddressSet := range options.RouteAddressSet { + rp, loaded := rpTunnel.RuleProviders()[routeAddressSet] + if !loaded { + err = E.New("parse route-address-set: rule-set not found: ", routeAddressSet) + return + } + l.updateRule(rp, false, false) + markMode = true + } + for _, routeExcludeAddressSet := range options.RouteExcludeAddressSet { + rp, loaded := rpTunnel.RuleProviders()[routeExcludeAddressSet] + if !loaded { + err = E.New("parse route-exclude_address-set: rule-set not found: ", routeExcludeAddressSet) + return + } + l.updateRule(rp, true, false) + markMode = true + } + if markMode { + tunOptions.AutoRedirectMarkMode = true + } + } err = l.buildAndroidRules(&tunOptions) @@ -269,14 +384,14 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis resolver.AddSystemDnsBlacklist(dnsServerIp...) stackOptions := tun.StackOptions{ - Context: context.TODO(), + Context: ctx, Tun: tunIf, TunOptions: tunOptions, EndpointIndependentNat: options.EndpointIndependentNat, UDPTimeout: udpTimeout, Handler: handler, Logger: log.SingLogger, - InterfaceFinder: control.DefaultInterfaceFinder(), + InterfaceFinder: interfaceFinder, EnforceBindInterface: EnforceBindInterface, } @@ -299,13 +414,76 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis } l.tunStack = tunStack + if l.autoRedirect != nil { + if len(l.options.RouteAddressSet) > 0 && len(l.routeAddressSet) == 0 { + l.routeAddressSet = emptyAddressSet // without this we can't call UpdateRouteAddressSet after Start + } + if len(l.options.RouteExcludeAddressSet) > 0 && len(l.routeExcludeAddressSet) == 0 { + l.routeExcludeAddressSet = emptyAddressSet // without this we can't call UpdateRouteAddressSet after Start + } + err = l.autoRedirect.Start() + if err != nil { + err = E.Cause(err, "auto redirect") + return + } + if tunOptions.AutoRedirectMarkMode { + l.autoRedirectOutputMark = int32(outputMark) + dialer.DefaultRoutingMark.Store(l.autoRedirectOutputMark) + l.autoRedirect.UpdateRouteAddressSet() + l.ruleUpdateCallbackCloser = rpTunnel.RuleUpdateCallback().Register(l.ruleUpdateCallback) + } + } + //l.openAndroidHotspot(tunOptions) - l.addrStr = fmt.Sprintf("%s(%s,%s), mtu: %d, auto route: %v, ip stack: %s", - tunName, tunOptions.Inet4Address, tunOptions.Inet6Address, tunMTU, options.AutoRoute, options.Stack) + l.addrStr = fmt.Sprintf("%s(%s,%s), mtu: %d, auto route: %v, auto redir: %v, ip stack: %s", + tunName, tunOptions.Inet4Address, tunOptions.Inet6Address, tunMTU, options.AutoRoute, options.AutoRedirect, options.Stack) return } +func (l *Listener) ruleUpdateCallback(ruleProvider provider.RuleProvider) { + name := ruleProvider.Name() + if slices.Contains(l.options.RouteAddressSet, name) { + l.updateRule(ruleProvider, false, true) + return + } + if slices.Contains(l.options.RouteExcludeAddressSet, name) { + l.updateRule(ruleProvider, true, true) + return + } +} + +func (l *Listener) updateRule(ruleProvider provider.RuleProvider, exclude bool, update bool) { + l.ruleUpdateMutex.Lock() + defer l.ruleUpdateMutex.Unlock() + name := ruleProvider.Name() + switch rp := ruleProvider.Strategy().(type) { + case interface{ ToIpCidr() *netipx.IPSet }: + if !exclude { + ipCidr := rp.ToIpCidr() + if ipCidr != nil { + l.routeAddressMap[name] = ipCidr + } else { + delete(l.routeAddressMap, name) + } + l.routeAddressSet = maps.Values(l.routeAddressMap) + } else { + ipCidr := rp.ToIpCidr() + if ipCidr != nil { + l.routeExcludeAddressMap[name] = ipCidr + } else { + delete(l.routeExcludeAddressMap, name) + } + l.routeExcludeAddressSet = maps.Values(l.routeExcludeAddressMap) + } + default: + return + } + if update && l.autoRedirect != nil { + l.autoRedirect.UpdateRouteAddressSet() + } +} + func (l *Listener) FlushDefaultInterface() { if l.options.AutoDetectInterface { for _, destination := range []netip.Addr{netip.IPv4Unspecified(), netip.IPv6Unspecified(), netip.MustParseAddr("1.1.1.1")} { @@ -347,11 +525,11 @@ func parseRange(uidRanges []ranges.Range[uint32], rangeList []string) ([]ranges. } var start, end uint64 var err error - start, err = strconv.ParseUint(uidRange[:subIndex], 10, 32) + start, err = strconv.ParseUint(uidRange[:subIndex], 0, 32) if err != nil { return nil, E.Cause(err, "parse range start") } - end, err = strconv.ParseUint(uidRange[subIndex+1:], 10, 32) + end, err = strconv.ParseUint(uidRange[subIndex+1:], 0, 32) if err != nil { return nil, E.Cause(err, "parse range end") } @@ -363,9 +541,14 @@ func parseRange(uidRanges []ranges.Range[uint32], rangeList []string) ([]ranges. func (l *Listener) Close() error { l.closed = true resolver.RemoveSystemDnsBlacklist(l.dnsServerIp...) + if l.autoRedirectOutputMark != 0 { + dialer.DefaultRoutingMark.CompareAndSwap(l.autoRedirectOutputMark, 0) + } return common.Close( + l.ruleUpdateCallbackCloser, l.tunStack, l.tunIf, + l.autoRedirect, l.defaultInterfaceMonitor, l.networkUpdateMonitor, l.packageManager, diff --git a/listener/tproxy/tproxy_iptables.go b/listener/tproxy/tproxy_iptables.go index 6c6e2cc8..bc26b125 100644 --- a/listener/tproxy/tproxy_iptables.go +++ b/listener/tproxy/tproxy_iptables.go @@ -119,9 +119,7 @@ func CleanupTProxyIPTables() { log.Warnln("Cleanup tproxy linux iptables") - if int(dialer.DefaultRoutingMark.Load()) == 2158 { - dialer.DefaultRoutingMark.Store(0) - } + dialer.DefaultRoutingMark.CompareAndSwap(2158, 0) if _, err := cmd.ExecCmd("iptables -t mangle -L mihomo_divert"); err != nil { return diff --git a/rules/common/base.go b/rules/common/base.go index d912107c..670df1d9 100644 --- a/rules/common/base.go +++ b/rules/common/base.go @@ -20,6 +20,8 @@ func (b *Base) ShouldResolveIP() bool { return false } +func (b *Base) ProviderNames() []string { return nil } + func HasNoResolve(params []string) bool { for _, p := range params { if p == noResolve { diff --git a/rules/logic/logic.go b/rules/logic/logic.go index af8c31a4..397a16b7 100644 --- a/rules/logic/logic.go +++ b/rules/logic/logic.go @@ -298,3 +298,10 @@ func (logic *Logic) ShouldResolveIP() bool { func (logic *Logic) ShouldFindProcess() bool { return logic.needProcess } + +func (logic *Logic) ProviderNames() (names []string) { + for _, rule := range logic.rules { + names = append(names, rule.ProviderNames()...) + } + return +} diff --git a/rules/provider/ipcidr_strategy.go b/rules/provider/ipcidr_strategy.go index c93facd9..d0545c7c 100644 --- a/rules/provider/ipcidr_strategy.go +++ b/rules/provider/ipcidr_strategy.go @@ -4,6 +4,8 @@ import ( "github.com/metacubex/mihomo/component/cidr" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" + + "go4.org/netipx" ) type ipcidrStrategy struct { @@ -52,6 +54,10 @@ func (i *ipcidrStrategy) FinishInsert() { i.cidrSet.Merge() } +func (i *ipcidrStrategy) ToIpCidr() *netipx.IPSet { + return i.cidrSet.ToIPSet() +} + func NewIPCidrStrategy() *ipcidrStrategy { return &ipcidrStrategy{} } diff --git a/rules/provider/patch_android.go b/rules/provider/patch_android.go index 2bd5ffc8..7ef1df1b 100644 --- a/rules/provider/patch_android.go +++ b/rules/provider/patch_android.go @@ -12,8 +12,8 @@ type UpdatableProvider interface { UpdatedAt() time.Time } -func (f *ruleSetProvider) UpdatedAt() time.Time { - return f.Fetcher.UpdatedAt +func (rp *ruleSetProvider) UpdatedAt() time.Time { + return rp.Fetcher.UpdatedAt } func (rp *ruleSetProvider) Close() error { diff --git a/rules/provider/provider.go b/rules/provider/provider.go index adc2e44a..7616290f 100644 --- a/rules/provider/provider.go +++ b/rules/provider/provider.go @@ -15,12 +15,14 @@ import ( P "github.com/metacubex/mihomo/constant/provider" ) -var ( - ruleProviders = map[string]P.RuleProvider{} -) +var tunnel P.Tunnel + +func SetTunnel(t P.Tunnel) { + tunnel = t +} type ruleSetProvider struct { - *resource.Fetcher[any] + *resource.Fetcher[ruleStrategy] behavior P.RuleBehavior format P.RuleFormat strategy ruleStrategy @@ -49,16 +51,6 @@ type ruleStrategy interface { FinishInsert() } -func RuleProviders() map[string]P.RuleProvider { - return ruleProviders -} - -func SetRuleProvider(ruleProvider P.RuleProvider) { - if ruleProvider != nil { - ruleProviders[(ruleProvider).Name()] = ruleProvider - } -} - func (rp *ruleSetProvider) Type() P.ProviderType { return P.Rule } @@ -99,8 +91,8 @@ func (rp *ruleSetProvider) ShouldFindProcess() bool { return rp.strategy.ShouldFindProcess() } -func (rp *ruleSetProvider) AsRule(adaptor string) C.Rule { - panic("implement me") +func (rp *ruleSetProvider) Strategy() any { + return rp.strategy } func (rp *ruleSetProvider) MarshalJSON() ([]byte, error) { @@ -123,13 +115,15 @@ func NewRuleSetProvider(name string, behavior P.RuleBehavior, format P.RuleForma format: format, } - onUpdate := func(elm interface{}) { - strategy := elm.(ruleStrategy) + onUpdate := func(strategy ruleStrategy) { rp.strategy = strategy + tunnel.RuleUpdateCallback().Emit(rp) } rp.strategy = newStrategy(behavior, parse) - rp.Fetcher = resource.NewFetcher(name, interval, vehicle, func(bytes []byte) (any, error) { return rulesParse(bytes, newStrategy(behavior, parse), format) }, onUpdate) + rp.Fetcher = resource.NewFetcher(name, interval, vehicle, func(bytes []byte) (ruleStrategy, error) { + return rulesParse(bytes, newStrategy(behavior, parse), format) + }, onUpdate) wrapper := &RuleSetProvider{ rp, @@ -158,7 +152,7 @@ func newStrategy(behavior P.RuleBehavior, parse func(tp, payload, target string, var ErrNoPayload = errors.New("file must have a `payload` field") -func rulesParse(buf []byte, strategy ruleStrategy, format P.RuleFormat) (any, error) { +func rulesParse(buf []byte, strategy ruleStrategy, format P.RuleFormat) (ruleStrategy, error) { strategy.Reset() schema := &RulePayload{} diff --git a/rules/provider/rule_set.go b/rules/provider/rule_set.go index 1d940188..d85db805 100644 --- a/rules/provider/rule_set.go +++ b/rules/provider/rule_set.go @@ -1,7 +1,6 @@ package provider import ( - "fmt" C "github.com/metacubex/mihomo/constant" P "github.com/metacubex/mihomo/constant/provider" "github.com/metacubex/mihomo/rules/common" @@ -11,13 +10,18 @@ type RuleSet struct { *common.Base ruleProviderName string adapter string - ruleProvider P.RuleProvider noResolveIP bool shouldFindProcess bool } func (rs *RuleSet) ShouldFindProcess() bool { - return rs.shouldFindProcess || rs.getProviders().ShouldFindProcess() + if rs.shouldFindProcess { + return true + } + if provider, ok := rs.getProvider(); ok { + return provider.ShouldFindProcess() + } + return false } func (rs *RuleSet) RuleType() C.RuleType { @@ -25,7 +29,10 @@ func (rs *RuleSet) RuleType() C.RuleType { } func (rs *RuleSet) Match(metadata *C.Metadata) (bool, string) { - return rs.getProviders().Match(metadata), rs.adapter + if provider, ok := rs.getProvider(); ok { + return provider.Match(metadata), rs.adapter + } + return false, "" } func (rs *RuleSet) Adapter() string { @@ -33,31 +40,37 @@ func (rs *RuleSet) Adapter() string { } func (rs *RuleSet) Payload() string { - return rs.getProviders().Name() + if provider, ok := rs.getProvider(); ok { + return provider.Name() + } + return "" } func (rs *RuleSet) ShouldResolveIP() bool { - return !rs.noResolveIP && rs.getProviders().ShouldResolveIP() -} -func (rs *RuleSet) getProviders() P.RuleProvider { - if rs.ruleProvider == nil { - rp := RuleProviders()[rs.ruleProviderName] - rs.ruleProvider = rp + if rs.noResolveIP { + return false } + if provider, ok := rs.getProvider(); ok { + return provider.ShouldResolveIP() + } + return false +} - return rs.ruleProvider +func (rs *RuleSet) ProviderNames() []string { + return []string{rs.ruleProviderName} +} + +func (rs *RuleSet) getProvider() (P.RuleProvider, bool) { + pp, ok := tunnel.RuleProviders()[rs.ruleProviderName] + return pp, ok } func NewRuleSet(ruleProviderName string, adapter string, noResolveIP bool) (*RuleSet, error) { - rp, ok := RuleProviders()[ruleProviderName] - if !ok { - return nil, fmt.Errorf("rule set %s not found", ruleProviderName) - } - return &RuleSet{ + rs := &RuleSet{ Base: &common.Base{}, ruleProviderName: ruleProviderName, adapter: adapter, - ruleProvider: rp, noResolveIP: noResolveIP, - }, nil + } + return rs, nil } diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 2c1b894f..1a6f104d 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -13,6 +13,7 @@ import ( "time" N "github.com/metacubex/mihomo/common/net" + "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/loopback" "github.com/metacubex/mihomo/component/nat" P "github.com/metacubex/mihomo/component/process" @@ -50,11 +51,15 @@ var ( findProcessMode P.FindProcessMode fakeIPRange netip.Prefix + + ruleUpdateCallback = utils.NewCallback[provider.RuleProvider]() ) type tunnel struct{} -var Tunnel C.Tunnel = tunnel{} +var Tunnel = tunnel{} +var _ C.Tunnel = Tunnel +var _ provider.Tunnel = Tunnel func (t tunnel) HandleTCPConn(conn net.Conn, metadata *C.Metadata) { connCtx := icontext.NewConnContext(conn, metadata) @@ -73,6 +78,18 @@ func (t tunnel) NatTable() C.NatTable { return natTable } +func (t tunnel) Providers() map[string]provider.ProxyProvider { + return providers +} + +func (t tunnel) RuleProviders() map[string]provider.RuleProvider { + return ruleProviders +} + +func (t tunnel) RuleUpdateCallback() *utils.Callback[provider.RuleProvider] { + return ruleUpdateCallback +} + func OnSuspend() { status.Store(Suspend) } From 5ab8154e7e1e2aad431a5af358c804b81926853b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 18 Jun 2024 10:30:43 +0800 Subject: [PATCH 119/311] fix: wireguard ip update --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ee0eeeb4..6030dd1e 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/metacubex/sing-shadowsocks2 v0.2.0 github.com/metacubex/sing-tun v0.2.7-0.20240617013029-d05cf9df9cfe github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f - github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 + github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 github.com/metacubex/utls v1.6.6 github.com/miekg/dns v1.1.59 diff --git a/go.sum b/go.sum index a1d0cf5a..7f2f105f 100644 --- a/go.sum +++ b/go.sum @@ -120,8 +120,8 @@ github.com/metacubex/sing-tun v0.2.7-0.20240617013029-d05cf9df9cfe h1:NrWjVEkRmE github.com/metacubex/sing-tun v0.2.7-0.20240617013029-d05cf9df9cfe/go.mod h1:WwJGbCx7bQcBzuQXiDOJvZH27R0kIjKNNlISIWsL6kM= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbTMBip/C+R79bvbfr42xH1gZl3uFb0RELdZiQ= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= -github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 h1:AGyIB55UfQm/0ZH0HtQO9u3l//yjtHUpjeRjjPGfGRI= -github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= +github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a h1:NpSGclHJUYndUwBmyIpFBSoBVg8PoVX7QQKhYg0DjM0= +github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 h1:as/aO/fM8nv4W4pOr9EETP6kV/Oaujk3fUNyQSJK61c= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8= From 1457f83530a3d386f49c29c1bb0e846e6350e11a Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 18 Jun 2024 13:12:44 +0800 Subject: [PATCH 120/311] fix: dns server using direct outbound lookback resolve problem --- adapter/outbound/direct.go | 4 ++++ component/dialer/dialer.go | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index c904d3b6..7114045d 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -64,6 +64,10 @@ func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, return d.loopBack.NewPacketConn(newPacketConn(pc, d)), nil } +func (d *Direct) IsL3Protocol(metadata *C.Metadata) bool { + return true // tell DNSDialer don't send domain to DialContext, avoid lookback to DefaultResolver +} + func NewDirectWithOption(option DirectOption) *Direct { return &Direct{ Base: &Base{ diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index c21e638e..54a1aa6a 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -378,12 +378,12 @@ func (d Dialer) DialContext(ctx context.Context, network, address string) (net.C } func (d Dialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) { - opt := WithOption(d.Opt) + opt := d.Opt // make a copy if rAddrPort.Addr().Unmap().IsLoopback() { // avoid "The requested address is not valid in its context." - opt = WithInterface("") + WithInterface("")(&opt) } - return ListenPacket(ctx, ParseNetwork(network, rAddrPort.Addr()), address, rAddrPort, opt) + return ListenPacket(ctx, ParseNetwork(network, rAddrPort.Addr()), address, rAddrPort, WithOption(opt)) } func NewDialer(options ...Option) Dialer { From 917c5fdd80e760603650181190fa3c763a039622 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 19 Jun 2024 10:46:44 +0800 Subject: [PATCH 121/311] fix: auto-redirect android rules --- go.mod | 2 +- go.sum | 4 ++-- listener/sing_tun/redirect_linux.go | 3 +++ listener/sing_tun/redirect_stub.go | 5 +++++ listener/sing_tun/server.go | 8 ++++++-- 5 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 listener/sing_tun/redirect_linux.go create mode 100644 listener/sing_tun/redirect_stub.go diff --git a/go.mod b/go.mod index 6030dd1e..3361adf9 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 - github.com/metacubex/sing-tun v0.2.7-0.20240617013029-d05cf9df9cfe + github.com/metacubex/sing-tun v0.2.7-0.20240619023810-d442c40abab0 github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 diff --git a/go.sum b/go.sum index 7f2f105f..95e0404f 100644 --- a/go.sum +++ b/go.sum @@ -116,8 +116,8 @@ github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwV github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A= github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8= -github.com/metacubex/sing-tun v0.2.7-0.20240617013029-d05cf9df9cfe h1:NrWjVEkRmEkdREVSpohMgEBoznS0PrRfJDr6iCV4348= -github.com/metacubex/sing-tun v0.2.7-0.20240617013029-d05cf9df9cfe/go.mod h1:WwJGbCx7bQcBzuQXiDOJvZH27R0kIjKNNlISIWsL6kM= +github.com/metacubex/sing-tun v0.2.7-0.20240619023810-d442c40abab0 h1:J7YWMrEaYM9WF4qG8ZaCCHGw/ylbZc8FvIHr4rdOzP8= +github.com/metacubex/sing-tun v0.2.7-0.20240619023810-d442c40abab0/go.mod h1:WwJGbCx7bQcBzuQXiDOJvZH27R0kIjKNNlISIWsL6kM= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbTMBip/C+R79bvbfr42xH1gZl3uFb0RELdZiQ= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a h1:NpSGclHJUYndUwBmyIpFBSoBVg8PoVX7QQKhYg0DjM0= diff --git a/listener/sing_tun/redirect_linux.go b/listener/sing_tun/redirect_linux.go new file mode 100644 index 00000000..6ef6fc96 --- /dev/null +++ b/listener/sing_tun/redirect_linux.go @@ -0,0 +1,3 @@ +package sing_tun + +const supportRedirect = true diff --git a/listener/sing_tun/redirect_stub.go b/listener/sing_tun/redirect_stub.go new file mode 100644 index 00000000..d711af3c --- /dev/null +++ b/listener/sing_tun/redirect_stub.go @@ -0,0 +1,5 @@ +//go:build !linux + +package sing_tun + +const supportRedirect = false diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 53b88528..e8c2ad28 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -132,7 +132,7 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis if options.GSOMaxSize == 0 { options.GSOMaxSize = 65536 } - if runtime.GOOS != "linux" { + if !supportRedirect { options.AutoRedirect = false } tunName := options.Device @@ -453,12 +453,16 @@ func (l *Listener) ruleUpdateCallback(ruleProvider provider.RuleProvider) { } } +type toIpCidr interface { + ToIpCidr() *netipx.IPSet +} + func (l *Listener) updateRule(ruleProvider provider.RuleProvider, exclude bool, update bool) { l.ruleUpdateMutex.Lock() defer l.ruleUpdateMutex.Unlock() name := ruleProvider.Name() switch rp := ruleProvider.Strategy().(type) { - case interface{ ToIpCidr() *netipx.IPSet }: + case toIpCidr: if !exclude { ipCidr := rp.ToIpCidr() if ipCidr != nil { From 50286678bf51e9331a2e88eaa522687daaa4242e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 22 Jun 2024 13:08:15 +0800 Subject: [PATCH 122/311] fix: auto-redirect rule error --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3361adf9..8dea8e9a 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 - github.com/metacubex/sing-tun v0.2.7-0.20240619023810-d442c40abab0 + github.com/metacubex/sing-tun v0.2.7-0.20240622050320-d74a7240f063 github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 diff --git a/go.sum b/go.sum index 95e0404f..95c71fed 100644 --- a/go.sum +++ b/go.sum @@ -116,8 +116,8 @@ github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwV github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A= github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8= -github.com/metacubex/sing-tun v0.2.7-0.20240619023810-d442c40abab0 h1:J7YWMrEaYM9WF4qG8ZaCCHGw/ylbZc8FvIHr4rdOzP8= -github.com/metacubex/sing-tun v0.2.7-0.20240619023810-d442c40abab0/go.mod h1:WwJGbCx7bQcBzuQXiDOJvZH27R0kIjKNNlISIWsL6kM= +github.com/metacubex/sing-tun v0.2.7-0.20240622050320-d74a7240f063 h1:l0kqvpBCy1yUAlr79qc0w70nbELJiqs+tWH61b5poiU= +github.com/metacubex/sing-tun v0.2.7-0.20240622050320-d74a7240f063/go.mod h1:WwJGbCx7bQcBzuQXiDOJvZH27R0kIjKNNlISIWsL6kM= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbTMBip/C+R79bvbfr42xH1gZl3uFb0RELdZiQ= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a h1:NpSGclHJUYndUwBmyIpFBSoBVg8PoVX7QQKhYg0DjM0= From a9ecc627e6376bcb75d2dfc9ca20008e8c794f7b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 22 Jun 2024 13:10:09 +0800 Subject: [PATCH 123/311] fix: subrule can't recursion correctly (#1339) --- rules/logic/logic.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rules/logic/logic.go b/rules/logic/logic.go index 397a16b7..8c79cab5 100644 --- a/rules/logic/logic.go +++ b/rules/logic/logic.go @@ -2,12 +2,13 @@ package logic import ( "fmt" - list "github.com/bahlo/generic-list-go" "regexp" "strings" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/rules/common" + + list "github.com/bahlo/generic-list-go" ) type Logic struct { @@ -243,7 +244,7 @@ func matchSubRules(metadata *C.Metadata, name string, subRules map[string][]C.Ru for _, rule := range subRules[name] { if m, a := rule.Match(metadata); m { if rule.RuleType() == C.SubRules { - matchSubRules(metadata, rule.Adapter(), subRules) + return matchSubRules(metadata, rule.Adapter(), subRules) } else { return m, a } From 9f4cd646c23a8fd511c21446ed2c4d391a21b341 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sun, 23 Jun 2024 15:33:38 +0800 Subject: [PATCH 124/311] fix: `dhcp://` with special notation cannot be parsed --- component/process/process_linux.go | 1 - config/config.go | 14 +++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/component/process/process_linux.go b/component/process/process_linux.go index 4667104c..45c89e5a 100644 --- a/component/process/process_linux.go +++ b/component/process/process_linux.go @@ -209,7 +209,6 @@ func findPackageName(uid uint32) string { }) if sharedPackage, loaded := packageManager.SharedPackageByID(uid % 100000); loaded { - fmt.Println(loaded) return sharedPackage } if packageName, loaded := packageManager.PackageByID(uid % 100000); loaded { diff --git a/config/config.go b/config/config.go index 2166b87d..5676f7aa 100644 --- a/config/config.go +++ b/config/config.go @@ -1057,6 +1057,16 @@ func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns. var nameservers []dns.NameServer for idx, server := range servers { + if strings.HasPrefix(server, "dhcp://") { + nameservers = append( + nameservers, + dns.NameServer{ + Net: "dhcp", + Addr: server[len("dhcp://"):], + }, + ) + continue + } server = parsePureDNSServer(server) u, err := url.Parse(server) if err != nil { @@ -1099,9 +1109,6 @@ func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns. } } } - case "dhcp": - addr = u.Host - dnsNetType = "dhcp" // UDP from DHCP case "quic": addr, err = hostWithDefaultPort(u.Host, "853") dnsNetType = "quic" // DNS over QUIC @@ -1174,6 +1181,7 @@ func parsePureDNSServer(server string) string { } } } + func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], ruleProviders map[string]providerTypes.RuleProvider, respectRules bool, preferH3 bool) (*orderedmap.OrderedMap[string, []dns.NameServer], error) { policy := orderedmap.New[string, []dns.NameServer]() updatedPolicy := orderedmap.New[string, any]() From f45ccc07616cfd3ee6dbc1270f7c298cceeb8d04 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 27 Jun 2024 09:51:52 +0800 Subject: [PATCH 125/311] chore: update dependencies --- go.mod | 16 ++++++++-------- go.sum | 35 ++++++++++++++++------------------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/go.mod b/go.mod index 8dea8e9a..4a96d142 100644 --- a/go.mod +++ b/go.mod @@ -9,13 +9,13 @@ require ( github.com/cilium/ebpf v0.12.3 github.com/coreos/go-iptables v0.7.0 github.com/dlclark/regexp2 v1.11.0 - github.com/go-chi/chi/v5 v5.0.12 + github.com/go-chi/chi/v5 v5.0.14 github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.3 github.com/gobwas/ws v1.4.0 github.com/gofrs/uuid/v5 v5.2.0 - github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49 - github.com/klauspost/cpuid/v2 v2.2.7 + github.com/insomniacslk/dhcp v0.0.0-20240529192340-51bc6136a0a6 + github.com/klauspost/cpuid/v2 v2.2.8 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 @@ -24,16 +24,16 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 - github.com/metacubex/sing-tun v0.2.7-0.20240622050320-d74a7240f063 + github.com/metacubex/sing-tun v0.2.7-0.20240627012306-9d1f5fc0b45e github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 github.com/metacubex/utls v1.6.6 - github.com/miekg/dns v1.1.59 + github.com/miekg/dns v1.1.61 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/v3 v3.1.0 + github.com/puzpuzpuz/xsync/v3 v3.2.0 github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a github.com/sagernet/sing v0.5.0-alpha.10 @@ -41,7 +41,7 @@ require ( github.com/sagernet/sing-shadowtls v0.1.4 github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e github.com/samber/lo v1.39.0 - github.com/shirou/gopsutil/v3 v3.24.4 + github.com/shirou/gopsutil/v3 v3.24.5 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.9.0 github.com/wk8/go-ordered-map/v2 v2.1.8 @@ -52,7 +52,7 @@ require ( golang.org/x/net v0.26.0 golang.org/x/sync v0.7.0 golang.org/x/sys v0.21.0 - google.golang.org/protobuf v1.34.1 + google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.3.0 ) diff --git a/go.sum b/go.sum index 95c71fed..29a5d385 100644 --- a/go.sum +++ b/go.sum @@ -44,8 +44,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 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.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= -github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.0.14 h1:PyEwo2Vudraa0x/Wl6eDRRW2NXBvekgfxyydcM0WGE0= +github.com/go-chi/chi/v5 v5.0.14/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= @@ -69,7 +69,6 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= @@ -78,16 +77,16 @@ github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I= 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-20240419123447-f1cffa2c0c49 h1:/OuvSMGT9+xnyZ+7MZQ1zdngaCCAdPoSw8B/uurZ7pg= -github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= +github.com/insomniacslk/dhcp v0.0.0-20240529192340-51bc6136a0a6 h1:dh8D8FksyMhD64mRMbUhZHWYJfNoNMCxfVq6eexleMw= +github.com/insomniacslk/dhcp v0.0.0-20240529192340-51bc6136a0a6/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 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= github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= +github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= @@ -116,8 +115,8 @@ github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwV github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A= github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8= -github.com/metacubex/sing-tun v0.2.7-0.20240622050320-d74a7240f063 h1:l0kqvpBCy1yUAlr79qc0w70nbELJiqs+tWH61b5poiU= -github.com/metacubex/sing-tun v0.2.7-0.20240622050320-d74a7240f063/go.mod h1:WwJGbCx7bQcBzuQXiDOJvZH27R0kIjKNNlISIWsL6kM= +github.com/metacubex/sing-tun v0.2.7-0.20240627012306-9d1f5fc0b45e h1:o+zohxPRo45P35fS9u1zfdBgr+L/7S0ObGU6YjbVBIc= +github.com/metacubex/sing-tun v0.2.7-0.20240627012306-9d1f5fc0b45e/go.mod h1:WwJGbCx7bQcBzuQXiDOJvZH27R0kIjKNNlISIWsL6kM= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbTMBip/C+R79bvbfr42xH1gZl3uFb0RELdZiQ= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a h1:NpSGclHJUYndUwBmyIpFBSoBVg8PoVX7QQKhYg0DjM0= @@ -126,8 +125,8 @@ github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 h1:as/aO/fM8nv4W4 github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8= github.com/metacubex/utls v1.6.6/go.mod h1:+WLFUnXjcpdxXCnyX25nggw8C6YonZ8zOK2Zm/oRvdo= -github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= -github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= +github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs= +github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ= 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= @@ -150,8 +149,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/v3 v3.1.0 h1:EewKT7/LNac5SLiEblJeUu8z5eERHrmRLnMQL2d7qX4= -github.com/puzpuzpuz/xsync/v3 v3.1.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= +github.com/puzpuzpuz/xsync/v3 v3.2.0 h1:9AzuUeF88YC5bK8u2vEG1Fpvu4wgpM1wfPIExfaaDxQ= +github.com/puzpuzpuz/xsync/v3 v3.2.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= 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.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= @@ -173,12 +172,11 @@ github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e h1:iGH0RMv2F github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e/go.mod h1:YbL4TKHRR6APYQv3U2RGfwLDpPYSyWz6oUlpISBEzBE= github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= -github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU= -github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8= +github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= +github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= 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= -github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b h1:rXHg9GrUEtWZhEkrykicdND3VPjlVbYiLdX9J7gimS8= github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b/go.mod h1:X7qrxNQViEaAN9LNZOPl9PfvQtp3V3c7LTo0dvGi0fM= github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c h1:DjKMC30y6yjG3IxDaeAj3PCoRr+IsO+bzyT+Se2m2Hk= @@ -254,7 +252,6 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= @@ -268,8 +265,8 @@ golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= 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= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 0e228765fce4d709af1e672426dea5294e6b7544 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Fri, 28 Jun 2024 14:14:36 +0800 Subject: [PATCH 126/311] fix: Make the ruleset take effect in a single line --- rules/provider/provider.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rules/provider/provider.go b/rules/provider/provider.go index 7616290f..6c03c6e5 100644 --- a/rules/provider/provider.go +++ b/rules/provider/provider.go @@ -4,11 +4,12 @@ import ( "bytes" "encoding/json" "errors" - "gopkg.in/yaml.v3" "runtime" "strings" "time" + "gopkg.in/yaml.v3" + "github.com/metacubex/mihomo/common/pool" "github.com/metacubex/mihomo/component/resource" C "github.com/metacubex/mihomo/constant" @@ -170,15 +171,14 @@ func rulesParse(buf []byte, strategy ruleStrategy, format P.RuleFormat) (ruleStr line = buf[s : i+1] s = i + 1 } else { - s = len(buf) // stop loop in next step - if firstLineLength == 0 { // no head or only one line body + s = len(buf) // stop loop in next step + if firstLineLength == 0 && format == P.YamlRule { // no head or only one line body return nil, ErrNoPayload } } var str string switch format { case P.TextRule: - firstLineLength = -1 // don't return ErrNoPayload when read last line str = string(line) str = strings.TrimSpace(str) if len(str) == 0 { From fc03bd2f0d3b14ab728d78491597e2b4baf3cc9c Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Fri, 12 Jul 2024 02:59:14 +0800 Subject: [PATCH 127/311] chore: Modify the default value to avoid outputting Deprecated --- config/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/config.go b/config/config.go index 5676f7aa..ae82ac3f 100644 --- a/config/config.go +++ b/config/config.go @@ -504,7 +504,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { }, Sniffer: RawSniffer{ Enable: false, - Sniffing: []string{}, + Sniff: map[string]RawSniffingConfig{}, ForceDomain: []string{}, SkipDomain: []string{}, Ports: []string{}, @@ -1566,7 +1566,7 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) { } } } else { - if sniffer.Enable { + if sniffer.Enable && len(snifferRaw.Sniffing) != 0 { // Deprecated: Use Sniff instead log.Warnln("Deprecated: Use Sniff instead") } From 9e3589d638158dcad6a711803aad271d3338d475 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Mon, 15 Jul 2024 13:12:40 +0800 Subject: [PATCH 128/311] chore: include-all-providers logic correction --- adapter/outboundgroup/parser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapter/outboundgroup/parser.go b/adapter/outboundgroup/parser.go index 67dc104d..efc38aab 100644 --- a/adapter/outboundgroup/parser.go +++ b/adapter/outboundgroup/parser.go @@ -69,7 +69,7 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide } if groupOption.IncludeAllProviders { - groupOption.Use = append(groupOption.Use, AllProviders...) + groupOption.Use = AllProviders } if groupOption.IncludeAllProxies { if groupOption.Filter != "" { From a05016a5da860bcb492c921d5f9f26963d71ff66 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 19 Jul 2024 19:27:29 +0800 Subject: [PATCH 129/311] chore: better dns logging --- dns/resolver.go | 5 ++++- dns/util.go | 37 ++++++++++++++++++++++--------------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/dns/resolver.go b/dns/resolver.go index 28ffec6f..fc228761 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -146,9 +146,12 @@ func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, e }() q := m.Question[0] + domain := msgToDomain(m) + _, qTypeStr := msgToQtype(m) cacheM, expireTime, hit := r.cache.GetWithExpire(q.String()) if hit { - log.Debugln("[DNS] cache hit for %s, expire at %s", q.Name, expireTime.Format("2006-01-02 15:04:05")) + ips := msgToIP(cacheM) + log.Debugln("[DNS] cache hit %s --> %s %s, expire at %s", domain, ips, qTypeStr, expireTime.Format("2006-01-02 15:04:05")) now := time.Now() msg = cacheM.Copy() if expireTime.Before(now) { diff --git a/dns/util.go b/dns/util.go index e4ec5917..50459cc1 100644 --- a/dns/util.go +++ b/dns/util.go @@ -173,11 +173,20 @@ func msgToDomain(msg *D.Msg) string { return "" } +func msgToQtype(msg *D.Msg) (uint16, string) { + if len(msg.Question) > 0 { + qType := msg.Question[0].Qtype + return qType, D.Type(qType).String() + } + return 0, "" +} + func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, cache bool, err error) { cache = true fast, ctx := picker.WithTimeout[*D.Msg](ctx, resolver.DefaultDNSTimeout) defer fast.Close() domain := msgToDomain(m) + qType, qTypeStr := msgToQtype(m) var noIpMsg *D.Msg for _, client := range clients { if _, isRCodeClient := client.(rcodeClient); isRCodeClient { @@ -186,7 +195,7 @@ func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.M } client := client // shadow define client to ensure the value captured by the closure will not be changed in the next loop fast.Go(func() (*D.Msg, error) { - log.Debugln("[DNS] resolve %s from %s", domain, client.Address()) + log.Debugln("[DNS] resolve %s %s from %s", domain, qTypeStr, client.Address()) m, err := client.ExchangeContext(ctx, m) if err != nil { return nil, err @@ -195,20 +204,18 @@ func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.M // so we would ignore RCode errors from RCode clients. return nil, errors.New("server failure: " + D.RcodeToString[m.Rcode]) } - if ips := msgToIP(m); len(m.Question) > 0 { - qType := m.Question[0].Qtype - log.Debugln("[DNS] %s --> %s %s from %s", domain, ips, D.Type(qType), client.Address()) - switch qType { - case D.TypeAAAA: - if len(ips) == 0 { - noIpMsg = m - return nil, resolver.ErrIPNotFound - } - case D.TypeA: - if len(ips) == 0 { - noIpMsg = m - return nil, resolver.ErrIPNotFound - } + ips := msgToIP(m) + log.Debugln("[DNS] %s --> %s %s from %s", domain, ips, qTypeStr, client.Address()) + switch qType { + case D.TypeAAAA: + if len(ips) == 0 { + noIpMsg = m + return nil, resolver.ErrIPNotFound + } + case D.TypeA: + if len(ips) == 0 { + noIpMsg = m + return nil, resolver.ErrIPNotFound } } return m, nil From 345061a7cc1d533048e1519b2a71742a55dca31c Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 19 Jul 2024 22:08:05 +0800 Subject: [PATCH 130/311] chore: support some chacha8 method --- go.mod | 11 +++--- go.sum | 21 +++++----- transport/shadowsocks/core/cipher.go | 20 ++++++++++ transport/shadowsocks/shadowaead/cipher.go | 39 +++++++++++++++++++ .../shadowsocks/shadowstream/old_chacha20.go | 5 ++- 5 files changed, 80 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 4a96d142..a492f93d 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,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/bahlo/generic-list-go v0.2.0 github.com/cilium/ebpf v0.12.3 github.com/coreos/go-iptables v0.7.0 @@ -18,14 +17,15 @@ require ( github.com/klauspost/cpuid/v2 v2.2.8 github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.7.2 + github.com/metacubex/chacha v0.1.0 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e github.com/metacubex/randv2 v0.2.0 github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 - github.com/metacubex/sing-shadowsocks v0.2.6 - github.com/metacubex/sing-shadowsocks2 v0.2.0 + github.com/metacubex/sing-shadowsocks v0.2.7 + github.com/metacubex/sing-shadowsocks2 v0.2.1 github.com/metacubex/sing-tun v0.2.7-0.20240627012306-9d1f5fc0b45e - github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f + github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 github.com/metacubex/utls v1.6.6 @@ -45,13 +45,14 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.9.0 github.com/wk8/go-ordered-map/v2 v2.1.8 + gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 go.uber.org/automaxprocs v1.5.3 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba golang.org/x/crypto v0.24.0 golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 golang.org/x/net v0.26.0 golang.org/x/sync v0.7.0 - golang.org/x/sys v0.21.0 + golang.org/x/sys v0.22.0 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.3.0 diff --git a/go.sum b/go.sum index 29a5d385..88553161 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,6 @@ github.com/RyuaNerin/go-krypto v1.2.4 h1:mXuNdK6M317aPV0llW6Xpjbo4moOlPF7Yxz4tb4 github.com/RyuaNerin/go-krypto v1.2.4/go.mod h1:QqCYkoutU3yInyD9INt2PGolVRsc3W4oraQadVGXJ/8= github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 h1:cDVUiFo+npB0ZASqnw4q90ylaVAbnYyx0JYqK4YcGok= github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344/go.mod h1:9pIqrY6SXNL8vjRQE5Hd/OL5GyK/9MrGUWs87z/eFfk= -github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= -github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= @@ -99,6 +97,8 @@ github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/ github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= +github.com/metacubex/chacha v0.1.0 h1:tg9RSJ18NvL38cCWNyYH1eiG6qDCyyXIaTLQthon0sc= +github.com/metacubex/chacha v0.1.0/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8= github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI= github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc= @@ -111,14 +111,14 @@ github.com/metacubex/sing v0.0.0-20240617013425-3e3bd9dab6a2 h1:N5tidgg/FRmkgPw/ github.com/metacubex/sing v0.0.0-20240617013425-3e3bd9dab6a2/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 h1:Wr4g1HCb5Z/QIFwFiVNjO2qL+dRu25+Mdn9xtAZZ+ew= github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8= -github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ= -github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= -github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A= -github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8= +github.com/metacubex/sing-shadowsocks v0.2.7 h1:9f3Dt2+71TNp0e202llA2ug5h/rkWs2EZxQ5IMpf+9g= +github.com/metacubex/sing-shadowsocks v0.2.7/go.mod h1:X3x88XtJpBxG0W0/ECOJL6Ib0SJ3xdniAkU/6/RMWU0= +github.com/metacubex/sing-shadowsocks2 v0.2.1 h1:XIZBXlazp8EEoPp1S0DViAhLkJakjQ2f+AOwwdKKNYg= +github.com/metacubex/sing-shadowsocks2 v0.2.1/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q= github.com/metacubex/sing-tun v0.2.7-0.20240627012306-9d1f5fc0b45e h1:o+zohxPRo45P35fS9u1zfdBgr+L/7S0ObGU6YjbVBIc= github.com/metacubex/sing-tun v0.2.7-0.20240627012306-9d1f5fc0b45e/go.mod h1:WwJGbCx7bQcBzuQXiDOJvZH27R0kIjKNNlISIWsL6kM= -github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbTMBip/C+R79bvbfr42xH1gZl3uFb0RELdZiQ= -github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= +github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosqY8xKDp3pqTW3qbrCprZ1l6WkrXSFSCwyY4I= +github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a h1:NpSGclHJUYndUwBmyIpFBSoBVg8PoVX7QQKhYg0DjM0= github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 h1:as/aO/fM8nv4W4pOr9EETP6kV/Oaujk3fUNyQSJK61c= @@ -212,6 +212,8 @@ github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/ github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 h1:UNrDfkQqiEYzdMlNsVvBYOAJWZjdktqFE9tQh5BT2+4= +gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7/go.mod h1:E+rxHvJG9H6PUdzq9NRG6csuLN3XUx98BfGOVWNYnXs= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ= go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= @@ -252,8 +254,9 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= diff --git a/transport/shadowsocks/core/cipher.go b/transport/shadowsocks/core/cipher.go index 44b2e8d4..7cb12c45 100644 --- a/transport/shadowsocks/core/cipher.go +++ b/transport/shadowsocks/core/cipher.go @@ -34,6 +34,11 @@ const ( aeadAes256Gcm = "AEAD_AES_256_GCM" aeadChacha20Poly1305 = "AEAD_CHACHA20_POLY1305" aeadXChacha20Poly1305 = "AEAD_XCHACHA20_POLY1305" + aeadChacha8Poly1305 = "AEAD_CHACHA8_POLY1305" + aeadXChacha8Poly1305 = "AEAD_XCHACHA8_POLY1305" + aeadAes128Ccm = "AEAD_AES_128_CCM" + aeadAes192Ccm = "AEAD_AES_192_CCM" + aeadAes256Ccm = "AEAD_AES_256_CCM" ) // List of AEAD ciphers: key size in bytes and constructor @@ -46,6 +51,11 @@ var aeadList = map[string]struct { aeadAes256Gcm: {32, shadowaead.AESGCM}, aeadChacha20Poly1305: {32, shadowaead.Chacha20Poly1305}, aeadXChacha20Poly1305: {32, shadowaead.XChacha20Poly1305}, + aeadChacha8Poly1305: {32, shadowaead.Chacha8Poly1305}, + aeadXChacha8Poly1305: {32, shadowaead.XChacha8Poly1305}, + aeadAes128Ccm: {16, shadowaead.AESCCM}, + aeadAes192Ccm: {24, shadowaead.AESCCM}, + aeadAes256Ccm: {32, shadowaead.AESCCM}, } // List of stream ciphers: key size in bytes and constructor @@ -95,6 +105,16 @@ func PickCipher(name string, key []byte, password string) (Cipher, error) { name = aeadAes192Gcm case "AES-256-GCM": name = aeadAes256Gcm + case "CHACHA8-IETF-POLY1305": + name = aeadChacha8Poly1305 + case "XCHACHA8-IETF-POLY1305": + name = aeadXChacha8Poly1305 + case "AES-128-CCM": + name = aeadAes128Ccm + case "AES-192-CCM": + name = aeadAes192Ccm + case "AES-256-CCM": + name = aeadAes256Ccm } if choice, ok := aeadList[name]; ok { diff --git a/transport/shadowsocks/shadowaead/cipher.go b/transport/shadowsocks/shadowaead/cipher.go index 3cf75749..7981d5b1 100644 --- a/transport/shadowsocks/shadowaead/cipher.go +++ b/transport/shadowsocks/shadowaead/cipher.go @@ -7,6 +7,8 @@ import ( "io" "strconv" + "github.com/metacubex/chacha" + "gitlab.com/go-extension/aes-ccm" "golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/hkdf" ) @@ -75,6 +77,25 @@ func AESGCM(psk []byte) (Cipher, error) { return &metaCipher{psk: psk, makeAEAD: aesGCM}, nil } +func aesCCM(key []byte) (cipher.AEAD, error) { + blk, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + return ccm.NewCCM(blk) +} + +// AESCCM creates a new Cipher with a pre-shared key. len(psk) must be +// one of 16, 24, or 32 to select AES-128/196/256-GCM. +func AESCCM(psk []byte) (Cipher, error) { + switch l := len(psk); l { + case 16, 24, 32: // AES 128/196/256 + default: + return nil, aes.KeySizeError(l) + } + return &metaCipher{psk: psk, makeAEAD: aesCCM}, nil +} + // Chacha20Poly1305 creates a new Cipher with a pre-shared key. len(psk) // must be 32. func Chacha20Poly1305(psk []byte) (Cipher, error) { @@ -92,3 +113,21 @@ func XChacha20Poly1305(psk []byte) (Cipher, error) { } return &metaCipher{psk: psk, makeAEAD: chacha20poly1305.NewX}, nil } + +// Chacha8Poly1305 creates a new Cipher with a pre-shared key. len(psk) +// must be 32. +func Chacha8Poly1305(psk []byte) (Cipher, error) { + if len(psk) != chacha.KeySize { + return nil, KeySizeError(chacha.KeySize) + } + return &metaCipher{psk: psk, makeAEAD: chacha.NewChaCha8IETFPoly1305}, nil +} + +// XChacha8Poly1305 creates a new Cipher with a pre-shared key. len(psk) +// must be 32. +func XChacha8Poly1305(psk []byte) (Cipher, error) { + if len(psk) != chacha.KeySize { + return nil, KeySizeError(chacha.KeySize) + } + return &metaCipher{psk: psk, makeAEAD: chacha.NewXChaCha20IETFPoly1305}, nil +} diff --git a/transport/shadowsocks/shadowstream/old_chacha20.go b/transport/shadowsocks/shadowstream/old_chacha20.go index 65737fcc..eb61232e 100644 --- a/transport/shadowsocks/shadowstream/old_chacha20.go +++ b/transport/shadowsocks/shadowstream/old_chacha20.go @@ -2,7 +2,8 @@ package shadowstream import ( "crypto/cipher" - "github.com/aead/chacha20/chacha" + + "github.com/metacubex/chacha" ) type chacha20key []byte @@ -11,7 +12,7 @@ func (k chacha20key) IVSize() int { return chacha.NonceSize } func (k chacha20key) Encrypter(iv []byte) cipher.Stream { - c, _ := chacha.NewCipher(iv, k, 20) + c, _ := chacha.NewChaCha20(iv, k) return c } func (k chacha20key) Decrypter(iv []byte) cipher.Stream { From e263518f01a18fc38c8b7944e6438c8cdfcbb53c Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 19 Jul 2024 22:20:07 +0800 Subject: [PATCH 131/311] fix: some auto-redirect issue --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a492f93d..ab4125a0 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.7 github.com/metacubex/sing-shadowsocks2 v0.2.1 - github.com/metacubex/sing-tun v0.2.7-0.20240627012306-9d1f5fc0b45e + github.com/metacubex/sing-tun v0.2.7-0.20240719141828-5710f5d19a55 github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 diff --git a/go.sum b/go.sum index 88553161..836d9833 100644 --- a/go.sum +++ b/go.sum @@ -115,8 +115,8 @@ github.com/metacubex/sing-shadowsocks v0.2.7 h1:9f3Dt2+71TNp0e202llA2ug5h/rkWs2E github.com/metacubex/sing-shadowsocks v0.2.7/go.mod h1:X3x88XtJpBxG0W0/ECOJL6Ib0SJ3xdniAkU/6/RMWU0= github.com/metacubex/sing-shadowsocks2 v0.2.1 h1:XIZBXlazp8EEoPp1S0DViAhLkJakjQ2f+AOwwdKKNYg= github.com/metacubex/sing-shadowsocks2 v0.2.1/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q= -github.com/metacubex/sing-tun v0.2.7-0.20240627012306-9d1f5fc0b45e h1:o+zohxPRo45P35fS9u1zfdBgr+L/7S0ObGU6YjbVBIc= -github.com/metacubex/sing-tun v0.2.7-0.20240627012306-9d1f5fc0b45e/go.mod h1:WwJGbCx7bQcBzuQXiDOJvZH27R0kIjKNNlISIWsL6kM= +github.com/metacubex/sing-tun v0.2.7-0.20240719141828-5710f5d19a55 h1:q7RhLYjTNTmO9aTFCRlKJE3endUHBQNzc7KDFfvTHj8= +github.com/metacubex/sing-tun v0.2.7-0.20240719141828-5710f5d19a55/go.mod h1:WwJGbCx7bQcBzuQXiDOJvZH27R0kIjKNNlISIWsL6kM= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosqY8xKDp3pqTW3qbrCprZ1l6WkrXSFSCwyY4I= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a h1:NpSGclHJUYndUwBmyIpFBSoBVg8PoVX7QQKhYg0DjM0= From 28794c62c464dcba6a1199cc850d9795250b2a9f Mon Sep 17 00:00:00 2001 From: ruokeqx Date: Fri, 19 Jul 2024 22:24:27 +0800 Subject: [PATCH 132/311] chore: reduce func findProcessName mem allocs and copy (#1393) --- component/process/process_darwin.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/component/process/process_darwin.go b/component/process/process_darwin.go index 67d2e833..c02771ed 100644 --- a/component/process/process_darwin.go +++ b/component/process/process_darwin.go @@ -46,12 +46,12 @@ func findProcessName(network string, ip netip.Addr, port int) (uint32, string, e isIPv4 := ip.Is4() - value, err := syscall.Sysctl(spath) + value, err := unix.SysctlRaw(spath) if err != nil { return 0, "", err } - buf := []byte(value) + buf := value itemSize := structSize if network == TCP { // rup8(sizeof(xtcpcb_n)) From fd5b537ab1991ba16cf0f6e69e55c0b73d99d9a2 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 21 Jul 2024 22:52:16 +0800 Subject: [PATCH 133/311] fix: doh concurrent race issue --- dns/doh.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dns/doh.go b/dns/doh.go index 54b82796..504f79f2 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -204,24 +204,24 @@ func (doh *dnsOverHTTPS) exchangeHTTPSClient( method = http3.MethodGet0RTT } - url := doh.url - url.RawQuery = fmt.Sprintf("dns=%s", base64.RawURLEncoding.EncodeToString(buf)) - httpReq, err := http.NewRequestWithContext(ctx, method, url.String(), nil) + requestUrl := *doh.url // don't modify origin url + requestUrl.RawQuery = fmt.Sprintf("dns=%s", base64.RawURLEncoding.EncodeToString(buf)) + httpReq, err := http.NewRequestWithContext(ctx, method, requestUrl.String(), nil) if err != nil { - return nil, fmt.Errorf("creating http request to %s: %w", url, err) + return nil, fmt.Errorf("creating http request to %s: %w", doh.url, err) } httpReq.Header.Set("Accept", "application/dns-message") httpReq.Header.Set("User-Agent", "") httpResp, err := client.Do(httpReq) if err != nil { - return nil, fmt.Errorf("requesting %s: %w", url, err) + return nil, fmt.Errorf("requesting %s: %w", doh.url, err) } defer httpResp.Body.Close() body, err := io.ReadAll(httpResp.Body) if err != nil { - return nil, fmt.Errorf("reading %s: %w", url, err) + return nil, fmt.Errorf("reading %s: %w", doh.url, err) } if httpResp.StatusCode != http.StatusOK { @@ -230,7 +230,7 @@ func (doh *dnsOverHTTPS) exchangeHTTPSClient( "expected status %d, got %d from %s", http.StatusOK, httpResp.StatusCode, - url, + doh.url, ) } @@ -239,7 +239,7 @@ func (doh *dnsOverHTTPS) exchangeHTTPSClient( if err != nil { return nil, fmt.Errorf( "unpacking response from %s: body is %s: %w", - url, + doh.url, body, err, ) From 4eb13a73bf71caafcdd309c61659cad63fbdb240 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 22 Jul 2024 09:57:57 +0800 Subject: [PATCH 134/311] fix: wrong usage of RLock --- adapter/outboundgroup/loadbalance.go | 2 -- common/lru/lrucache.go | 32 ++++++++++++++++++++++++++++ common/queue/queue.go | 4 ++-- common/utils/callback.go | 4 ++-- component/sniffer/dispatcher.go | 20 ++++++----------- 5 files changed, 42 insertions(+), 20 deletions(-) diff --git a/adapter/outboundgroup/loadbalance.go b/adapter/outboundgroup/loadbalance.go index 4cb0db00..738ed154 100644 --- a/adapter/outboundgroup/loadbalance.go +++ b/adapter/outboundgroup/loadbalance.go @@ -205,7 +205,6 @@ func strategyStickySessions(url string) strategyFn { proxy := proxies[nowIdx] if proxy.AliveForTestUrl(url) { if nowIdx != idx { - lruCache.Delete(key) lruCache.Set(key, nowIdx) } @@ -215,7 +214,6 @@ func strategyStickySessions(url string) strategyFn { } } - lruCache.Delete(key) lruCache.Set(key, 0) return proxies[0] } diff --git a/common/lru/lrucache.go b/common/lru/lrucache.go index 6f32ed18..35f605b1 100644 --- a/common/lru/lrucache.go +++ b/common/lru/lrucache.go @@ -223,6 +223,10 @@ func (c *LruCache[K, V]) Delete(key K) { c.mu.Lock() defer c.mu.Unlock() + c.delete(key) +} + +func (c *LruCache[K, V]) delete(key K) { if le, ok := c.cache[key]; ok { c.deleteElement(le) } @@ -255,6 +259,34 @@ func (c *LruCache[K, V]) Clear() error { return nil } +// Compute either sets the computed new value for the key or deletes +// the value for the key. When the delete result of the valueFn function +// is set to true, the value will be deleted, if it exists. When delete +// is set to false, the value is updated to the newValue. +// The ok result indicates whether value was computed and stored, thus, is +// present in the map. The actual result contains the new value in cases where +// the value was computed and stored. +func (c *LruCache[K, V]) Compute( + key K, + valueFn func(oldValue V, loaded bool) (newValue V, delete bool), +) (actual V, ok bool) { + c.mu.Lock() + defer c.mu.Unlock() + + if el := c.get(key); el != nil { + actual, ok = el.value, true + } + if newValue, del := valueFn(actual, ok); del { + if ok { // data not in cache, so needn't delete + c.delete(key) + } + return lo.Empty[V](), false + } else { + c.set(key, newValue) + return newValue, true + } +} + type entry[K comparable, V any] struct { key K value V diff --git a/common/queue/queue.go b/common/queue/queue.go index cb58e2f5..d1b6beeb 100644 --- a/common/queue/queue.go +++ b/common/queue/queue.go @@ -59,8 +59,8 @@ func (q *Queue[T]) Copy() []T { // Len returns the number of items in this queue. func (q *Queue[T]) Len() int64 { - q.lock.Lock() - defer q.lock.Unlock() + q.lock.RLock() + defer q.lock.RUnlock() return int64(len(q.items)) } diff --git a/common/utils/callback.go b/common/utils/callback.go index df950d3a..ad734c0f 100644 --- a/common/utils/callback.go +++ b/common/utils/callback.go @@ -17,8 +17,8 @@ func NewCallback[T any]() *Callback[T] { } func (c *Callback[T]) Register(item func(T)) io.Closer { - c.mutex.RLock() - defer c.mutex.RUnlock() + c.mutex.Lock() + defer c.mutex.Unlock() element := c.list.PushBack(item) return &callbackCloser[T]{ element: element, diff --git a/component/sniffer/dispatcher.go b/component/sniffer/dispatcher.go index 97bf1629..4438638d 100644 --- a/component/sniffer/dispatcher.go +++ b/component/sniffer/dispatcher.go @@ -5,7 +5,6 @@ import ( "fmt" "net" "net/netip" - "sync" "time" "github.com/metacubex/mihomo/common/lru" @@ -30,7 +29,6 @@ type SnifferDispatcher struct { forceDomain *trie.DomainSet skipSNI *trie.DomainSet skipList *lru.LruCache[string, uint8] - rwMux sync.RWMutex forceDnsMapping bool parsePureIp bool } @@ -85,14 +83,11 @@ func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata return false } - sd.rwMux.RLock() dst := fmt.Sprintf("%s:%d", metadata.DstIP, metadata.DstPort) 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 false } - sd.rwMux.RUnlock() if host, err := sd.sniffDomain(conn, metadata); err != nil { sd.cacheSniffFailed(metadata) @@ -104,9 +99,7 @@ func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata return false } - sd.rwMux.RLock() sd.skipList.Delete(dst) - sd.rwMux.RUnlock() sd.replaceDomain(metadata, host, overrideDest) return true @@ -176,14 +169,13 @@ func (sd *SnifferDispatcher) sniffDomain(conn *N.BufferedConn, metadata *C.Metad } func (sd *SnifferDispatcher) cacheSniffFailed(metadata *C.Metadata) { - sd.rwMux.Lock() dst := fmt.Sprintf("%s:%d", metadata.DstIP, metadata.DstPort) - count, _ := sd.skipList.Get(dst) - if count <= 5 { - count++ - } - sd.skipList.Set(dst, count) - sd.rwMux.Unlock() + sd.skipList.Compute(dst, func(oldValue uint8, loaded bool) (newValue uint8, delete bool) { + if oldValue <= 5 { + oldValue++ + } + return oldValue, false + }) } func NewCloseSnifferDispatcher() (*SnifferDispatcher, error) { From de61e81ff7bafe3f7c696d863051ed997f7bfcee Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 23 Jul 2024 00:01:41 +0800 Subject: [PATCH 135/311] feat: support `external-doh-server` --- config/config.go | 3 ++ docs/config.yaml | 4 +++ hub/hub.go | 5 ++-- hub/route/doh.go | 67 +++++++++++++++++++++++++++++++++++++++++++++ hub/route/server.go | 16 +++++++---- 5 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 hub/route/doh.go diff --git a/config/config.go b/config/config.go index ae82ac3f..f58709c9 100644 --- a/config/config.go +++ b/config/config.go @@ -96,6 +96,7 @@ type Controller struct { ExternalControllerTLS string `json:"-"` ExternalControllerUnix string `json:"-"` ExternalUI string `json:"-"` + ExternalDohServer string `json:"-"` Secret string `json:"-"` } @@ -322,6 +323,7 @@ type RawConfig struct { 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"` + ExternalDohServer string `yaml:"external-doh-server"` Secret string `yaml:"secret"` Interface string `yaml:"interface-name"` RoutingMark int `yaml:"routing-mark"` @@ -697,6 +699,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) { Secret: cfg.Secret, ExternalControllerUnix: cfg.ExternalControllerUnix, ExternalControllerTLS: cfg.ExternalControllerTLS, + ExternalDohServer: cfg.ExternalDohServer, }, UnifiedDelay: cfg.UnifiedDelay, Mode: cfg.Mode, diff --git a/docs/config.yaml b/docs/config.yaml index 9c51bc10..4e4b9b16 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -70,6 +70,10 @@ external-ui: /path/to/ui/folder/ external-ui-name: xd external-ui-url: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip" +# 在RESTful API端口上开启DOH服务器 +# !!!该URL不会验证secret, 如果开启请自行保证安全问题 !!! +external-doh-server: /dns-query + # interface-name: en0 # 设置出口网卡 # 全局 TLS 指纹,优先低于 proxy 内的 client-fingerprint diff --git a/hub/hub.go b/hub/hub.go index 38779e13..57c91aae 100644 --- a/hub/hub.go +++ b/hub/hub.go @@ -50,11 +50,12 @@ func Parse(options ...Option) error { if cfg.General.ExternalController != "" { go route.Start(cfg.General.ExternalController, cfg.General.ExternalControllerTLS, - cfg.General.Secret, cfg.TLS.Certificate, cfg.TLS.PrivateKey, cfg.General.LogLevel == log.DEBUG) + cfg.General.Secret, cfg.TLS.Certificate, cfg.TLS.PrivateKey, cfg.General.ExternalDohServer, + cfg.General.LogLevel == log.DEBUG) } if cfg.General.ExternalControllerUnix != "" { - go route.StartUnix(cfg.General.ExternalControllerUnix, cfg.General.LogLevel == log.DEBUG) + go route.StartUnix(cfg.General.ExternalControllerUnix, cfg.General.ExternalDohServer, cfg.General.LogLevel == log.DEBUG) } executor.ApplyConfig(cfg, true) diff --git a/hub/route/doh.go b/hub/route/doh.go new file mode 100644 index 00000000..5f993c02 --- /dev/null +++ b/hub/route/doh.go @@ -0,0 +1,67 @@ +package route + +import ( + "context" + "encoding/base64" + "io" + "net/http" + + "github.com/metacubex/mihomo/component/resolver" + + "github.com/go-chi/render" +) + +func dohRouter() http.Handler { + return http.HandlerFunc(dohHandler) +} + +func dohHandler(w http.ResponseWriter, r *http.Request) { + if resolver.DefaultResolver == nil { + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, newError("DNS section is disabled")) + return + } + + if r.Header.Get("Accept") != "application/dns-message" { + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, newError("invalid accept header")) + return + } + + var dnsData []byte + var err error + switch r.Method { + case "GET": + dnsData, err = base64.RawURLEncoding.DecodeString(r.URL.Query().Get("dns")) + case "POST": + if r.Header.Get("Content-Type") != "application/dns-message" { + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, newError("invalid content-type")) + return + } + dnsData, err = io.ReadAll(r.Body) + _ = r.Body.Close() + default: + render.Status(r, http.StatusMethodNotAllowed) + render.JSON(w, r, newError("method not allowed")) + return + } + if err != nil { + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, newError(err.Error())) + return + } + + ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout) + defer cancel() + + dnsData, err = resolver.RelayDnsPacket(ctx, dnsData, dnsData) + if err != nil { + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, newError(err.Error())) + return + } + + render.Status(r, http.StatusOK) + render.Data(w, r, dnsData) +} diff --git a/hub/route/server.go b/hub/route/server.go index c28782b9..165c7c69 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -50,7 +50,7 @@ func SetUIPath(path string) { uiPath = C.Path.Resolve(path) } -func router(isDebug bool, withAuth bool) *chi.Mux { +func router(isDebug bool, withAuth bool, dohServer string) *chi.Mux { r := chi.NewRouter() corsM := cors.New(cors.Options{ AllowedOrigins: []string{"*"}, @@ -104,11 +104,15 @@ func router(isDebug bool, withAuth bool) *chi.Mux { }) }) } + if len(dohServer) > 0 && dohServer[0] == '/' { + r.Mount(dohServer, dohRouter()) + } + return r } func Start(addr string, tlsAddr string, secret string, - certificate, privateKey string, isDebug bool) { + certificate, privateKey string, dohServer string, isDebug bool) { if serverAddr != "" { return } @@ -133,7 +137,7 @@ func Start(addr string, tlsAddr string, secret string, serverAddr = l.Addr().String() log.Infoln("RESTful API tls listening at: %s", serverAddr) tlsServe := &http.Server{ - Handler: router(isDebug, true), + Handler: router(isDebug, true, dohServer), TLSConfig: &tls.Config{ Certificates: []tls.Certificate{c}, }, @@ -152,13 +156,13 @@ func Start(addr string, tlsAddr string, secret string, serverAddr = l.Addr().String() log.Infoln("RESTful API listening at: %s", serverAddr) - if err = http.Serve(l, router(isDebug, true)); err != nil { + if err = http.Serve(l, router(isDebug, true, dohServer)); err != nil { log.Errorln("External controller serve error: %s", err) } } -func StartUnix(addr string, isDebug bool) { +func StartUnix(addr string, dohServer string, isDebug bool) { addr = C.Path.Resolve(addr) dir := filepath.Dir(addr) @@ -186,7 +190,7 @@ func StartUnix(addr string, isDebug bool) { serverAddr = l.Addr().String() log.Infoln("RESTful API unix listening at: %s", serverAddr) - if err = http.Serve(l, router(isDebug, false)); err != nil { + if err = http.Serve(l, router(isDebug, false, dohServer)); err != nil { log.Errorln("External controller unix serve error: %s", err) } } From 13b7ab8da329b96b8260219229fd425bd03319c5 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 23 Jul 2024 08:46:27 +0800 Subject: [PATCH 136/311] fix: better doh server compatibility --- hub/route/doh.go | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/hub/route/doh.go b/hub/route/doh.go index 5f993c02..bb5416c4 100644 --- a/hub/route/doh.go +++ b/hub/route/doh.go @@ -18,13 +18,7 @@ func dohRouter() http.Handler { func dohHandler(w http.ResponseWriter, r *http.Request) { if resolver.DefaultResolver == nil { render.Status(r, http.StatusInternalServerError) - render.JSON(w, r, newError("DNS section is disabled")) - return - } - - if r.Header.Get("Accept") != "application/dns-message" { - render.Status(r, http.StatusInternalServerError) - render.JSON(w, r, newError("invalid accept header")) + render.PlainText(w, r, "DNS section is disabled") return } @@ -36,19 +30,20 @@ func dohHandler(w http.ResponseWriter, r *http.Request) { case "POST": if r.Header.Get("Content-Type") != "application/dns-message" { render.Status(r, http.StatusInternalServerError) - render.JSON(w, r, newError("invalid content-type")) + render.PlainText(w, r, "invalid content-type") return } - dnsData, err = io.ReadAll(r.Body) + reader := io.LimitReader(r.Body, 65535) // according to rfc8484, the maximum size of the DNS message is 65535 bytes + dnsData, err = io.ReadAll(reader) _ = r.Body.Close() default: render.Status(r, http.StatusMethodNotAllowed) - render.JSON(w, r, newError("method not allowed")) + render.PlainText(w, r, "method not allowed") return } if err != nil { render.Status(r, http.StatusInternalServerError) - render.JSON(w, r, newError(err.Error())) + render.PlainText(w, r, err.Error()) return } @@ -58,10 +53,11 @@ func dohHandler(w http.ResponseWriter, r *http.Request) { dnsData, err = resolver.RelayDnsPacket(ctx, dnsData, dnsData) if err != nil { render.Status(r, http.StatusInternalServerError) - render.JSON(w, r, newError(err.Error())) + render.PlainText(w, r, err.Error()) return } - render.Status(r, http.StatusOK) - render.Data(w, r, dnsData) + w.Header().Set("Content-Type", "application/dns-message") + w.WriteHeader(http.StatusOK) + _, _ = w.Write(dnsData) } From 4b9fdacbad148d429f57a97c7233c594ce623c27 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 23 Jul 2024 10:48:54 +0800 Subject: [PATCH 137/311] feat: doh client support plain http and `skip-cert-verify` --- config/config.go | 9 +++++--- dns/doh.go | 60 +++++++++++++++++++++++++----------------------- 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/config/config.go b/config/config.go index f58709c9..d614166d 100644 --- a/config/config.go +++ b/config/config.go @@ -1090,13 +1090,16 @@ func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns. case "tls": addr, err = hostWithDefaultPort(u.Host, "853") dnsNetType = "tcp-tls" // DNS over TLS - case "https": + case "http", "https": addr, err = hostWithDefaultPort(u.Host, "443") + dnsNetType = "https" // DNS over HTTPS + if u.Scheme == "http" { + addr, err = hostWithDefaultPort(u.Host, "80") + } if err == nil { proxyName = "" - clearURL := url.URL{Scheme: "https", Host: addr, Path: u.Path, User: u.User} + clearURL := url.URL{Scheme: u.Scheme, Host: addr, Path: u.Path, User: u.User} addr = clearURL.String() - dnsNetType = "https" // DNS over HTTPS if len(u.Fragment) != 0 { for _, s := range strings.Split(u.Fragment, "&") { arr := strings.Split(s, "=") diff --git a/dns/doh.go b/dns/doh.go index 504f79f2..97e01ea7 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -61,10 +61,12 @@ type dnsOverHTTPS struct { // for this upstream. quicConfig *quic.Config quicConfigGuard sync.Mutex - url *url.URL - httpVersions []C.HTTPVersion - dialer *dnsDialer - addr string + + url *url.URL + httpVersions []C.HTTPVersion + dialer *dnsDialer + addr string + skipCertVerify bool } // type check @@ -93,6 +95,10 @@ func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[strin httpVersions: httpVersions, } + if params["skip-cert-verify"] == "true" { + doh.skipCertVerify = true + } + runtime.SetFinalizer(doh, (*dnsOverHTTPS).Close) return doh @@ -102,6 +108,7 @@ func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[strin func (doh *dnsOverHTTPS) Address() string { return doh.addr } + func (doh *dnsOverHTTPS) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { // Quote from https://www.rfc-editor.org/rfc/rfc8484.html: // In order to maximize HTTP cache friendliness, DoH clients using media @@ -178,19 +185,9 @@ func (doh *dnsOverHTTPS) closeClient(client *http.Client) (err error) { return nil } -// exchangeHTTPS logs the request and its result and calls exchangeHTTPSClient. -func (doh *dnsOverHTTPS) exchangeHTTPS(ctx context.Context, client *http.Client, req *D.Msg) (resp *D.Msg, err error) { - resp, err = doh.exchangeHTTPSClient(ctx, client, req) - return resp, err -} - -// exchangeHTTPSClient sends the DNS query to a DoH resolver using the specified +// exchangeHTTPS sends the DNS query to a DoH resolver using the specified // http.Client instance. -func (doh *dnsOverHTTPS) exchangeHTTPSClient( - ctx context.Context, - client *http.Client, - req *D.Msg, -) (resp *D.Msg, err error) { +func (doh *dnsOverHTTPS) exchangeHTTPS(ctx context.Context, client *http.Client, req *D.Msg) (resp *D.Msg, err error) { buf, err := req.Pack() if err != nil { return nil, fmt.Errorf("packing message: %w", err) @@ -373,9 +370,21 @@ 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) { + transport := &http.Transport{ + DisableCompression: true, + DialContext: doh.dialer.DialContext, + IdleConnTimeout: transportDefaultIdleConnTimeout, + MaxConnsPerHost: dohMaxConnsPerHost, + MaxIdleConns: dohMaxIdleConns, + } + + if doh.url.Scheme == "http" { + return transport, nil + } + tlsConfig := ca.GetGlobalTLSConfig( &tls.Config{ - InsecureSkipVerify: false, + InsecureSkipVerify: doh.skipCertVerify, MinVersion: tls.VersionTLS12, SessionTicketsDisabled: false, }) @@ -384,6 +393,7 @@ func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripp nextProtos = append(nextProtos, string(v)) } tlsConfig.NextProtos = nextProtos + transport.TLSClientConfig = tlsConfig if slices.Contains(doh.httpVersions, C.HTTPVersion3) { // First, we attempt to create an HTTP3 transport. If the probe QUIC @@ -402,18 +412,10 @@ func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripp return nil, errors.New("HTTP1/1 and HTTP2 are not supported by this upstream") } - transport := &http.Transport{ - TLSClientConfig: tlsConfig, - DisableCompression: true, - DialContext: doh.dialer.DialContext, - IdleConnTimeout: transportDefaultIdleConnTimeout, - MaxConnsPerHost: dohMaxConnsPerHost, - MaxIdleConns: dohMaxIdleConns, - // Since we have a custom DialContext, we need to use this field to - // make golang http.Client attempt to use HTTP/2. Otherwise, it would - // only be used when negotiated on the TLS level. - ForceAttemptHTTP2: true, - } + // Since we have a custom DialContext, we need to use this field to + // make golang http.Client attempt to use HTTP/2. Otherwise, it would + // only be used when negotiated on the TLS level. + transport.ForceAttemptHTTP2 = true // Explicitly configure transport to use HTTP/2. // From 313493cc9467a599e52cf80957364063d40cbe68 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 24 Jul 2024 13:59:10 +0800 Subject: [PATCH 138/311] chore: add fswatch --- component/process/process.go | 17 +++++++ component/process/process_android.go | 16 ------- component/process/process_common.go | 9 ---- component/process/process_linux.go | 56 +++++++---------------- go.mod | 7 +-- go.sum | 10 +++-- listener/sing_tun/server_android.go | 67 ++++++++++++++++++++++++---- tunnel/tunnel.go | 4 ++ 8 files changed, 106 insertions(+), 80 deletions(-) delete mode 100644 component/process/process_android.go delete mode 100644 component/process/process_common.go diff --git a/component/process/process.go b/component/process/process.go index 76ec2c45..84020c4d 100644 --- a/component/process/process.go +++ b/component/process/process.go @@ -3,6 +3,8 @@ package process import ( "errors" "net/netip" + + C "github.com/metacubex/mihomo/constant" ) var ( @@ -19,3 +21,18 @@ const ( func FindProcessName(network string, srcIP netip.Addr, srcPort int) (uint32, string, error) { return findProcessName(network, srcIP, srcPort) } + +// PackageNameResolver +// never change type traits because it's used in CFMA +type PackageNameResolver func(metadata *C.Metadata) (string, error) + +// DefaultPackageNameResolver +// never change type traits because it's used in CFMA +var DefaultPackageNameResolver PackageNameResolver + +func FindPackageName(metadata *C.Metadata) (string, error) { + if resolver := DefaultPackageNameResolver; resolver != nil { + return resolver(metadata) + } + return "", ErrPlatformNotSupport +} diff --git a/component/process/process_android.go b/component/process/process_android.go deleted file mode 100644 index fd5d3b6c..00000000 --- a/component/process/process_android.go +++ /dev/null @@ -1,16 +0,0 @@ -//go:build android && cmfa - -package process - -import "github.com/metacubex/mihomo/constant" - -type PackageNameResolver func(metadata *constant.Metadata) (string, error) - -var DefaultPackageNameResolver PackageNameResolver - -func FindPackageName(metadata *constant.Metadata) (string, error) { - if resolver := DefaultPackageNameResolver; resolver != nil { - return resolver(metadata) - } - return "", ErrPlatformNotSupport -} diff --git a/component/process/process_common.go b/component/process/process_common.go deleted file mode 100644 index fa7eeb9f..00000000 --- a/component/process/process_common.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build !(android && cmfa) - -package process - -import "github.com/metacubex/mihomo/constant" - -func FindPackageName(metadata *constant.Metadata) (string, error) { - return "", nil -} diff --git a/component/process/process_linux.go b/component/process/process_linux.go index 45c89e5a..3ce45ae8 100644 --- a/component/process/process_linux.go +++ b/component/process/process_linux.go @@ -2,23 +2,19 @@ package process import ( "bytes" - "context" "encoding/binary" "fmt" "net/netip" "os" + "path" "path/filepath" "runtime" "strings" - "sync" "syscall" "unicode" "unsafe" - "github.com/metacubex/mihomo/log" - "github.com/mdlayher/netlink" - tun "github.com/metacubex/sing-tun" "golang.org/x/sys/unix" ) @@ -63,25 +59,11 @@ type inetDiagResponse struct { INode uint32 } -type MyCallback struct{} - -var ( - packageManager tun.PackageManager - once sync.Once -) - -func (cb *MyCallback) OnPackagesUpdated(packageCount int, sharedCount int) {} - -func (cb *MyCallback) NewError(ctx context.Context, err error) { - log.Warnln("%s", err) -} - func findProcessName(network string, ip netip.Addr, srcPort int) (uint32, string, error) { uid, inode, err := resolveSocketByNetlink(network, ip, srcPort) if err != nil { return 0, "", err } - pp, err := resolveProcessNameByProcSearch(inode, uid) return uid, pp, err } @@ -177,44 +159,38 @@ func resolveProcessNameByProcSearch(inode, uid uint32) (string, error) { if err != nil { continue } + if runtime.GOOS == "android" { if bytes.Equal(buffer[:n], socket) { - return findPackageName(uid), nil + cmdline, err := os.ReadFile(path.Join(processPath, "cmdline")) + if err != nil { + return "", err + } + + return splitCmdline(cmdline), nil } } else { if bytes.Equal(buffer[:n], socket) { return os.Readlink(filepath.Join(processPath, "exe")) } } - } } return "", fmt.Errorf("process of uid(%d),inode(%d) not found", uid, inode) } -func findPackageName(uid uint32) string { - once.Do(func() { - callback := &MyCallback{} - var err error - packageManager, err = tun.NewPackageManager(callback) - if err != nil { - log.Warnln("%s", err) - } - err = packageManager.Start() - if err != nil { - log.Warnln("%s", err) - return - } +func splitCmdline(cmdline []byte) string { + cmdline = bytes.Trim(cmdline, " ") + + idx := bytes.IndexFunc(cmdline, func(r rune) bool { + return unicode.IsControl(r) || unicode.IsSpace(r) }) - if sharedPackage, loaded := packageManager.SharedPackageByID(uid % 100000); loaded { - return sharedPackage + if idx == -1 { + return filepath.Base(string(cmdline)) } - if packageName, loaded := packageManager.PackageByID(uid % 100000); loaded { - return packageName - } - return "" + return filepath.Base(string(cmdline[:idx])) } func isPid(s string) bool { diff --git a/go.mod b/go.mod index ab4125a0..7085d8b1 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.7 github.com/metacubex/sing-shadowsocks2 v0.2.1 - github.com/metacubex/sing-tun v0.2.7-0.20240719141828-5710f5d19a55 + github.com/metacubex/sing-tun v0.2.7-0.20240719141246-19c49ac9589d github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 @@ -36,7 +36,7 @@ require ( github.com/puzpuzpuz/xsync/v3 v3.2.0 github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a - github.com/sagernet/sing v0.5.0-alpha.10 + github.com/sagernet/sing v0.5.0-alpha.13 github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 github.com/sagernet/sing-shadowtls v0.1.4 github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e @@ -93,6 +93,7 @@ require ( 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.4.1 // indirect + github.com/sagernet/fswatch v0.1.1 // indirect github.com/sagernet/nftables v0.3.0-beta.4 // indirect github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect @@ -112,4 +113,4 @@ require ( golang.org/x/tools v0.22.0 // indirect ) -replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240617013425-3e3bd9dab6a2 +replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297 diff --git a/go.sum b/go.sum index 836d9833..ea3b6b53 100644 --- a/go.sum +++ b/go.sum @@ -107,16 +107,16 @@ github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e h1:bLYn3GuRvW github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e/go.mod h1:Yza2H7Ax1rxWPUcJx0vW+oAt9EsPuSiyQFhFabUPzwU= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= -github.com/metacubex/sing v0.0.0-20240617013425-3e3bd9dab6a2 h1:N5tidgg/FRmkgPw/AjRwhLUinKDx/ODCSbvv9xqRoLM= -github.com/metacubex/sing v0.0.0-20240617013425-3e3bd9dab6a2/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297 h1:YG/JkwGPbca5rUtEMHIu8ZuqzR7BSVm1iqY8hNoMeMA= +github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 h1:Wr4g1HCb5Z/QIFwFiVNjO2qL+dRu25+Mdn9xtAZZ+ew= github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8= github.com/metacubex/sing-shadowsocks v0.2.7 h1:9f3Dt2+71TNp0e202llA2ug5h/rkWs2EZxQ5IMpf+9g= github.com/metacubex/sing-shadowsocks v0.2.7/go.mod h1:X3x88XtJpBxG0W0/ECOJL6Ib0SJ3xdniAkU/6/RMWU0= github.com/metacubex/sing-shadowsocks2 v0.2.1 h1:XIZBXlazp8EEoPp1S0DViAhLkJakjQ2f+AOwwdKKNYg= github.com/metacubex/sing-shadowsocks2 v0.2.1/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q= -github.com/metacubex/sing-tun v0.2.7-0.20240719141828-5710f5d19a55 h1:q7RhLYjTNTmO9aTFCRlKJE3endUHBQNzc7KDFfvTHj8= -github.com/metacubex/sing-tun v0.2.7-0.20240719141828-5710f5d19a55/go.mod h1:WwJGbCx7bQcBzuQXiDOJvZH27R0kIjKNNlISIWsL6kM= +github.com/metacubex/sing-tun v0.2.7-0.20240719141246-19c49ac9589d h1:iYlepjRCYlPXtELupDL+pQjGqkCnQz4KQOfKImP9sog= +github.com/metacubex/sing-tun v0.2.7-0.20240719141246-19c49ac9589d/go.mod h1:olbEx9yVcaw5tHTNlRamRoxmMKcvDvcVS1YLnQGzvWE= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosqY8xKDp3pqTW3qbrCprZ1l6WkrXSFSCwyY4I= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a h1:NpSGclHJUYndUwBmyIpFBSoBVg8PoVX7QQKhYg0DjM0= @@ -158,6 +158,8 @@ github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58 github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM= +github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs= +github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o= github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis= github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I= diff --git a/listener/sing_tun/server_android.go b/listener/sing_tun/server_android.go index ac41282d..bd5c4bd0 100644 --- a/listener/sing_tun/server_android.go +++ b/listener/sing_tun/server_android.go @@ -1,29 +1,80 @@ package sing_tun import ( + "errors" + "runtime" + "sync" + + "github.com/metacubex/mihomo/component/process" + "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/constant/features" "github.com/metacubex/mihomo/log" - tun "github.com/metacubex/sing-tun" + + "github.com/metacubex/sing-tun" "github.com/sagernet/netlink" "golang.org/x/sys/unix" - "runtime" ) -func (l *Listener) buildAndroidRules(tunOptions *tun.Options) error { - packageManager, err := tun.NewPackageManager(l.handler) +type packageManagerCallback struct{} + +func (cb *packageManagerCallback) OnPackagesUpdated(packageCount int, sharedCount int) {} + +func newPackageManager() (tun.PackageManager, error) { + packageManager, err := tun.NewPackageManager(tun.PackageManagerOptions{ + Callback: &packageManagerCallback{}, + Logger: log.SingLogger, + }) if err != nil { - return err + return nil, err } err = packageManager.Start() + if err != nil { + return nil, err + } + return packageManager, nil +} + +var ( + globalPM tun.PackageManager + pmOnce sync.Once + pmErr error +) + +func getPackageManager() (tun.PackageManager, error) { + pmOnce.Do(func() { + globalPM, pmErr = newPackageManager() + }) + return globalPM, pmErr +} + +func (l *Listener) buildAndroidRules(tunOptions *tun.Options) error { + packageManager, err := getPackageManager() if err != nil { return err } - l.packageManager = packageManager tunOptions.BuildAndroidRules(packageManager, l.handler) return nil } -func (h *ListenerHandler) OnPackagesUpdated(packages int, sharedUsers int) { - return +func findPackageName(metadata *constant.Metadata) (string, error) { + packageManager, err := getPackageManager() + if err != nil { + return "", err + } + uid := metadata.Uid + if sharedPackage, loaded := packageManager.SharedPackageByID(uid % 100000); loaded { + return sharedPackage, nil + } + if packageName, loaded := packageManager.PackageByID(uid % 100000); loaded { + return packageName, nil + } + return "", errors.New("package not found") +} + +func init() { + if !features.CMFA { + process.DefaultPackageNameResolver = findPackageName + } } func (l *Listener) openAndroidHotspot(tunOptions tun.Options) { diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 1a6f104d..5dd468f3 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -622,6 +622,10 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { metadata.Process = filepath.Base(path) metadata.ProcessPath = path metadata.Uid = uid + + if pkg, err := P.FindPackageName(metadata); err == nil { // for android (not CMFA) package names + metadata.Process = pkg + } } } else { // check package names From d6a1af23a7591d3c4ee03c736775a0f7213ea41e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 24 Jul 2024 14:26:15 +0800 Subject: [PATCH 139/311] feat: local file type provider will auto update after modify --- component/resource/fetcher.go | 54 ++++++++++++++++++++++++----------- go.mod | 2 +- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/component/resource/fetcher.go b/component/resource/fetcher.go index e6291293..c82a54a3 100644 --- a/component/resource/fetcher.go +++ b/component/resource/fetcher.go @@ -10,6 +10,7 @@ import ( types "github.com/metacubex/mihomo/constant/provider" "github.com/metacubex/mihomo/log" + "github.com/sagernet/fswatch" "github.com/samber/lo" ) @@ -30,6 +31,7 @@ type Fetcher[V any] struct { parser Parser[V] interval time.Duration OnUpdate func(V) + watcher *fswatch.Watcher } func (f *Fetcher[V]) Name() string { @@ -113,7 +115,20 @@ func (f *Fetcher[V]) Initial() (V, error) { f.hash = md5.Sum(buf) // pull contents automatically - if f.interval > 0 { + if f.vehicle.Type() == types.File { + f.watcher, err = fswatch.NewWatcher(fswatch.Options{ + Path: []string{f.vehicle.Path()}, + Direct: true, + Callback: f.update, + }) + if err != nil { + return lo.Empty[V](), err + } + err = f.watcher.Start() + if err != nil { + return lo.Empty[V](), err + } + } else if f.interval > 0 { go f.pullLoop() } @@ -155,6 +170,9 @@ func (f *Fetcher[V]) Destroy() error { if f.interval > 0 { f.done <- struct{}{} } + if f.watcher != nil { + _ = f.watcher.Close() + } return nil } @@ -170,27 +188,31 @@ func (f *Fetcher[V]) pullLoop() { select { case <-timer.C: timer.Reset(f.interval) - elm, same, err := f.Update() - if err != nil { - log.Errorln("[Provider] %s pull error: %s", f.Name(), err.Error()) - continue - } - - if same { - log.Debugln("[Provider] %s's content doesn't change", f.Name()) - continue - } - - log.Infoln("[Provider] %s's content update", f.Name()) - if f.OnUpdate != nil { - f.OnUpdate(elm) - } + f.update(f.vehicle.Path()) case <-f.done: return } } } +func (f *Fetcher[V]) update(path string) { + elm, same, err := f.Update() + if err != nil { + log.Errorln("[Provider] %s pull error: %s", f.Name(), err.Error()) + return + } + + if same { + log.Debugln("[Provider] %s's content doesn't change", f.Name()) + return + } + + log.Infoln("[Provider] %s's content update", f.Name()) + if f.OnUpdate != nil { + f.OnUpdate(elm) + } +} + func safeWrite(path string, buf []byte) error { dir := filepath.Dir(path) diff --git a/go.mod b/go.mod index 7085d8b1..9c3a2180 100644 --- a/go.mod +++ b/go.mod @@ -35,6 +35,7 @@ require ( github.com/oschwald/maxminddb-golang v1.12.0 github.com/puzpuzpuz/xsync/v3 v3.2.0 github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a + github.com/sagernet/fswatch v0.1.1 github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a github.com/sagernet/sing v0.5.0-alpha.13 github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 @@ -93,7 +94,6 @@ require ( 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.4.1 // indirect - github.com/sagernet/fswatch v0.1.1 // indirect github.com/sagernet/nftables v0.3.0-beta.4 // indirect github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect From cc7823dad80e1031335a582d85c23425907c668b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 24 Jul 2024 14:56:46 +0800 Subject: [PATCH 140/311] fix: remove unneeded http proxy compression --- listener/http/client.go | 1 + 1 file changed, 1 insertion(+) diff --git a/listener/http/client.go b/listener/http/client.go index dfd1985f..dcfbe185 100644 --- a/listener/http/client.go +++ b/listener/http/client.go @@ -21,6 +21,7 @@ func newClient(srcConn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) IdleConnTimeout: 90 * time.Second, TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, + DisableCompression: true, // prevents the Transport add "Accept-Encoding: gzip" DialContext: func(context context.Context, network, address string) (net.Conn, error) { if network != "tcp" && network != "tcp4" && network != "tcp6" { return nil, errors.New("unsupported network " + network) From 4051ea522ac943a1031404f01f4924b6008b2d92 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 25 Jul 2024 19:49:56 +0800 Subject: [PATCH 141/311] chore: improve authentication parsing logic in http listener (#1336) --- adapter/inbound/addition.go | 2 ++ listener/http/client.go | 2 +- listener/http/proxy.go | 67 ++++++++++++++++++------------------- listener/http/server.go | 14 +++----- listener/mixed/mixed.go | 10 +++--- 5 files changed, 44 insertions(+), 51 deletions(-) diff --git a/adapter/inbound/addition.go b/adapter/inbound/addition.go index ed560818..894910aa 100644 --- a/adapter/inbound/addition.go +++ b/adapter/inbound/addition.go @@ -69,3 +69,5 @@ func WithDSCP(dscp uint8) Addition { metadata.DSCP = dscp } } + +func Placeholder(metadata *C.Metadata) {} diff --git a/listener/http/client.go b/listener/http/client.go index dcfbe185..0f084fca 100644 --- a/listener/http/client.go +++ b/listener/http/client.go @@ -13,7 +13,7 @@ import ( "github.com/metacubex/mihomo/transport/socks5" ) -func newClient(srcConn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) *http.Client { +func newClient(srcConn net.Conn, tunnel C.Tunnel, additions []inbound.Addition) *http.Client { // additions using slice let caller can change its value (without size) after newClient return return &http.Client{ Transport: &http.Transport{ // from http.DefaultTransport diff --git a/listener/http/proxy.go b/listener/http/proxy.go index c77f9230..b2f312a5 100644 --- a/listener/http/proxy.go +++ b/listener/http/proxy.go @@ -10,10 +10,9 @@ import ( "sync" "github.com/metacubex/mihomo/adapter/inbound" - "github.com/metacubex/mihomo/common/lru" N "github.com/metacubex/mihomo/common/net" + "github.com/metacubex/mihomo/component/auth" C "github.com/metacubex/mihomo/constant" - authStore "github.com/metacubex/mihomo/listener/auth" "github.com/metacubex/mihomo/log" ) @@ -31,8 +30,10 @@ func (b *bodyWrapper) Read(p []byte) (n int, err error) { return n, err } -func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool], additions ...inbound.Addition) { - client := newClient(c, tunnel, additions...) +func HandleConn(c net.Conn, tunnel C.Tunnel, authenticator auth.Authenticator, additions ...inbound.Addition) { + additions = append(additions, inbound.Placeholder) // Add a placeholder for InUser + inUserIdx := len(additions) - 1 + client := newClient(c, tunnel, additions) defer client.CloseIdleConnections() ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -41,7 +42,8 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool], conn := N.NewBufferedConn(c) keepAlive := true - trusted := cache == nil // disable authenticate if lru is nil + trusted := authenticator == nil // disable authenticate if lru is nil + lastUser := "" for keepAlive { peekMutex.Lock() @@ -57,12 +59,10 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool], var resp *http.Response - if !trusted { - var user string - resp, user = authenticate(request, cache) - additions = append(additions, inbound.WithInUser(user)) - trusted = resp == nil - } + var user string + resp, user = authenticate(request, authenticator) // always call authenticate function to get user + trusted = trusted || resp == nil + additions[inUserIdx] = inbound.WithInUser(user) if trusted { if request.Method == http.MethodConnect { @@ -89,6 +89,13 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool], return // hijack connection } + // ensure there is a client with correct additions + // when the authenticated user changed, outbound client should close idle connections + if user != lastUser { + client.CloseIdleConnections() + lastUser = user + } + removeHopByHopHeaders(request.Header) removeExtraHTTPHostPort(request) @@ -138,34 +145,24 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool], _ = conn.Close() } -func authenticate(request *http.Request, cache *lru.LruCache[string, bool]) (resp *http.Response, u string) { - authenticator := authStore.Authenticator() +func authenticate(request *http.Request, authenticator auth.Authenticator) (resp *http.Response, user string) { if inbound.SkipAuthRemoteAddress(request.RemoteAddr) { authenticator = nil } - if authenticator != nil { - credential := parseBasicProxyAuthorization(request) - if credential == "" { - resp := responseWith(request, http.StatusProxyAuthRequired) - resp.Header.Set("Proxy-Authenticate", "Basic") - return resp, "" - } - - authed, exist := cache.Get(credential) - if !exist { - user, pass, err := decodeBasicProxyAuthorization(credential) - authed = err == nil && authenticator.Verify(user, pass) - u = user - cache.Set(credential, authed) - } - if !authed { - log.Infoln("Auth failed from %s", request.RemoteAddr) - - return responseWith(request, http.StatusForbidden), u - } + credential := parseBasicProxyAuthorization(request) + if credential == "" && authenticator != nil { + resp = responseWith(request, http.StatusProxyAuthRequired) + resp.Header.Set("Proxy-Authenticate", "Basic") + return } - - return nil, u + user, pass, err := decodeBasicProxyAuthorization(credential) + authed := authenticator == nil || (err == nil && authenticator.Verify(user, pass)) + if !authed { + log.Infoln("Auth failed from %s", request.RemoteAddr) + return responseWith(request, http.StatusForbidden), user + } + log.Debugln("Auth success from %s -> %s", request.RemoteAddr, user) + return } func responseWith(request *http.Request, statusCode int) *http.Response { diff --git a/listener/http/server.go b/listener/http/server.go index 8fc9da59..9b2797da 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -4,9 +4,10 @@ import ( "net" "github.com/metacubex/mihomo/adapter/inbound" - "github.com/metacubex/mihomo/common/lru" + "github.com/metacubex/mihomo/component/auth" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/constant/features" + authStore "github.com/metacubex/mihomo/listener/auth" ) type Listener struct { @@ -32,10 +33,10 @@ func (l *Listener) Close() error { } func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) { - return NewWithAuthenticate(addr, tunnel, true, additions...) + return NewWithAuthenticate(addr, tunnel, authStore.Authenticator(), additions...) } -func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additions ...inbound.Addition) (*Listener, error) { +func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticator auth.Authenticator, additions ...inbound.Addition) (*Listener, error) { isDefault := false if len(additions) == 0 { isDefault = true @@ -50,11 +51,6 @@ func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additi return nil, err } - var c *lru.LruCache[string, bool] - if authenticate { - c = lru.New[string, bool](lru.WithAge[string, bool](30)) - } - hl := &Listener{ listener: l, addr: addr, @@ -79,7 +75,7 @@ func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additi continue } } - go HandleConn(conn, tunnel, c, additions...) + go HandleConn(conn, tunnel, authenticator, additions...) } }() diff --git a/listener/mixed/mixed.go b/listener/mixed/mixed.go index 367b7a36..773cabe3 100644 --- a/listener/mixed/mixed.go +++ b/listener/mixed/mixed.go @@ -4,9 +4,9 @@ import ( "net" "github.com/metacubex/mihomo/adapter/inbound" - "github.com/metacubex/mihomo/common/lru" N "github.com/metacubex/mihomo/common/net" C "github.com/metacubex/mihomo/constant" + authStore "github.com/metacubex/mihomo/listener/auth" "github.com/metacubex/mihomo/listener/http" "github.com/metacubex/mihomo/listener/socks" "github.com/metacubex/mihomo/transport/socks4" @@ -16,7 +16,6 @@ import ( type Listener struct { listener net.Listener addr string - cache *lru.LruCache[string, bool] closed bool } @@ -53,7 +52,6 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener ml := &Listener{ listener: l, addr: addr, - cache: lru.New[string, bool](lru.WithAge[string, bool](30)), } go func() { for { @@ -70,14 +68,14 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener continue } } - go handleConn(c, tunnel, ml.cache, additions...) + go handleConn(c, tunnel, additions...) } }() return ml, nil } -func handleConn(conn net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool], additions ...inbound.Addition) { +func handleConn(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) { N.TCPKeepAlive(conn) bufConn := N.NewBufferedConn(conn) @@ -92,6 +90,6 @@ func handleConn(conn net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool case socks5.Version: socks.HandleSocks5(bufConn, tunnel, additions...) default: - http.HandleConn(bufConn, tunnel, cache, additions...) + http.HandleConn(bufConn, tunnel, authStore.Authenticator(), additions...) } } From 40c9829328d5c3e031541038f7b7817559dc841d Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 26 Jul 2024 10:55:03 +0800 Subject: [PATCH 142/311] fix: auth with CFMA compile issue --- listener/http/server.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/listener/http/server.go b/listener/http/server.go index 9b2797da..77e10f08 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -33,10 +33,20 @@ func (l *Listener) Close() error { } func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) { - return NewWithAuthenticate(addr, tunnel, authStore.Authenticator(), additions...) + return NewWithAuthenticator(addr, tunnel, authStore.Authenticator(), additions...) } -func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticator auth.Authenticator, additions ...inbound.Addition) (*Listener, error) { +// NewWithAuthenticate +// never change type traits because it's used in CFMA +func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additions ...inbound.Addition) (*Listener, error) { + authenticator := authStore.Authenticator() + if !authenticate { + authenticator = nil + } + return NewWithAuthenticator(addr, tunnel, authenticator, additions...) +} + +func NewWithAuthenticator(addr string, tunnel C.Tunnel, authenticator auth.Authenticator, additions ...inbound.Addition) (*Listener, error) { isDefault := false if len(additions) == 0 { isDefault = true From 0d90a936455b0e373f38b8b239614b5337c17283 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 26 Jul 2024 10:59:39 +0800 Subject: [PATCH 143/311] chore: sort proxies and providers by name before include all --- config/config.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/config.go b/config/config.go index d614166d..ecd94c7d 100644 --- a/config/config.go +++ b/config/config.go @@ -42,6 +42,7 @@ import ( T "github.com/metacubex/mihomo/tunnel" orderedmap "github.com/wk8/go-ordered-map/v2" + "golang.org/x/exp/slices" "gopkg.in/yaml.v3" ) @@ -792,6 +793,9 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[ AllProviders = append(AllProviders, name) } + slices.Sort(AllProxies) + slices.Sort(AllProviders) + // parse proxy group for idx, mapping := range groupsConfig { group, err := outboundgroup.ParseProxyGroup(mapping, proxies, providersMap, AllProxies, AllProviders) From 303f6e4567c36f9a8648439e095d336ec9a722c2 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 26 Jul 2024 22:30:42 +0800 Subject: [PATCH 144/311] feat: add `mrs` format domain ruleset --- component/trie/domain_set_bin.go | 127 ++++++++++++++++++++++++++++++ constant/provider/interface.go | 33 ++++++++ docs/config.yaml | 6 ++ go.mod | 2 +- go.sum | 4 +- main.go | 7 ++ rules/provider/domain_strategy.go | 22 ++++++ rules/provider/mrs_converter.go | 71 +++++++++++++++++ rules/provider/parse.go | 27 ++----- rules/provider/provider.go | 28 ++++++- 10 files changed, 301 insertions(+), 26 deletions(-) create mode 100644 component/trie/domain_set_bin.go create mode 100644 rules/provider/mrs_converter.go diff --git a/component/trie/domain_set_bin.go b/component/trie/domain_set_bin.go new file mode 100644 index 00000000..e32d4e1a --- /dev/null +++ b/component/trie/domain_set_bin.go @@ -0,0 +1,127 @@ +package trie + +import ( + "encoding/binary" + "errors" + "io" +) + +func (ss *DomainSet) WriteBin(w io.Writer, count int64) (err error) { + // version + _, err = w.Write([]byte{1}) + if err != nil { + return err + } + + // count + err = binary.Write(w, binary.BigEndian, count) + if err != nil { + return err + } + + // leaves + err = binary.Write(w, binary.BigEndian, int64(len(ss.leaves))) + if err != nil { + return err + } + for _, d := range ss.leaves { + err = binary.Write(w, binary.BigEndian, d) + if err != nil { + return err + } + } + + // labelBitmap + err = binary.Write(w, binary.BigEndian, int64(len(ss.labelBitmap))) + if err != nil { + return err + } + for _, d := range ss.labelBitmap { + err = binary.Write(w, binary.BigEndian, d) + if err != nil { + return err + } + } + + // labels + err = binary.Write(w, binary.BigEndian, int64(len(ss.labels))) + if err != nil { + return err + } + _, err = w.Write(ss.labels) + if err != nil { + return err + } + + return nil +} + +func ReadDomainSetBin(r io.Reader) (ds *DomainSet, count int64, err error) { + // version + version := make([]byte, 1) + _, err = io.ReadFull(r, version) + if err != nil { + return nil, 0, err + } + if version[0] != 1 { + return nil, 0, errors.New("version is invalid") + } + + // count + err = binary.Read(r, binary.BigEndian, &count) + if err != nil { + return nil, 0, err + } + + ds = &DomainSet{} + var length int64 + + // leaves + err = binary.Read(r, binary.BigEndian, &length) + if err != nil { + return nil, 0, err + } + if length < 1 { + return nil, 0, errors.New("length is invalid") + } + ds.leaves = make([]uint64, length) + for i := int64(0); i < length; i++ { + err = binary.Read(r, binary.BigEndian, &ds.leaves[i]) + if err != nil { + return nil, 0, err + } + } + + // labelBitmap + err = binary.Read(r, binary.BigEndian, &length) + if err != nil { + return nil, 0, err + } + if length < 1 { + return nil, 0, errors.New("length is invalid") + } + ds.labelBitmap = make([]uint64, length) + for i := int64(0); i < length; i++ { + err = binary.Read(r, binary.BigEndian, &ds.labelBitmap[i]) + if err != nil { + return nil, 0, err + } + } + + // labels + err = binary.Read(r, binary.BigEndian, &length) + if err != nil { + return nil, 0, err + } + if length < 1 { + return nil, 0, errors.New("length is invalid") + } + ds.labels = make([]byte, length) + _, err = io.ReadFull(r, ds.labels) + if err != nil { + return nil, 0, err + } + + ds.init() + return ds, count, nil +} diff --git a/constant/provider/interface.go b/constant/provider/interface.go index f7dfc9cc..c86e6163 100644 --- a/constant/provider/interface.go +++ b/constant/provider/interface.go @@ -1,6 +1,8 @@ package provider import ( + "fmt" + "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/constant" ) @@ -110,9 +112,24 @@ func (rt RuleBehavior) String() string { } } +func ParseBehavior(s string) (behavior RuleBehavior, err error) { + switch s { + case "domain": + behavior = Domain + case "ipcidr": + behavior = IPCIDR + case "classical": + behavior = Classical + default: + err = fmt.Errorf("unsupported behavior type: %s", s) + } + return +} + const ( YamlRule RuleFormat = iota TextRule + MrsRule ) type RuleFormat int @@ -123,11 +140,27 @@ func (rf RuleFormat) String() string { return "YamlRule" case TextRule: return "TextRule" + case MrsRule: + return "MrsRule" default: return "Unknown" } } +func ParseRuleFormat(s string) (format RuleFormat, err error) { + switch s { + case "", "yaml": + format = YamlRule + case "text": + format = TextRule + case "mrs": + format = MrsRule + default: + err = fmt.Errorf("unsupported format type: %s", s) + } + return +} + type Tunnel interface { Providers() map[string]ProxyProvider RuleProviders() map[string]RuleProvider diff --git a/docs/config.yaml b/docs/config.yaml index 4e4b9b16..2d3343cf 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -942,6 +942,12 @@ rule-providers: interval: 259200 path: /path/to/save/file.yaml type: file + rule3: # mrs类型ruleset,目前仅支持domain,可以通过“mihomo convert-ruleset domain yaml XXX.yaml XXX.mrs”转换得到 + type: http + url: "url" + format: mrs + behavior: domain + path: /path/to/save/file.mrs rules: - RULE-SET,rule1,REJECT - IP-ASN,1,PROXY diff --git a/go.mod b/go.mod index 9c3a2180..d5ac6bee 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/gobwas/ws v1.4.0 github.com/gofrs/uuid/v5 v5.2.0 github.com/insomniacslk/dhcp v0.0.0-20240529192340-51bc6136a0a6 + github.com/klauspost/compress v1.17.9 github.com/klauspost/cpuid/v2 v2.2.8 github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.7.2 @@ -82,7 +83,6 @@ 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.17.4 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mdlayher/socket v0.4.1 // indirect diff --git a/go.sum b/go.sum index ea3b6b53..7e9cd5d8 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF 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= -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= -github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= diff --git a/main.go b/main.go index 61f1d683..cd903ce6 100644 --- a/main.go +++ b/main.go @@ -17,6 +17,7 @@ import ( "github.com/metacubex/mihomo/hub" "github.com/metacubex/mihomo/hub/executor" "github.com/metacubex/mihomo/log" + "github.com/metacubex/mihomo/rules/provider" "go.uber.org/automaxprocs/maxprocs" ) @@ -48,6 +49,12 @@ func init() { func main() { _, _ = maxprocs.Set(maxprocs.Logger(func(string, ...any) {})) + + if len(os.Args) > 1 && os.Args[1] == "convert-ruleset" { + provider.ConvertMain(os.Args[2:]) + return + } + if version { fmt.Printf("Mihomo Meta %s %s %s with %s %s\n", C.Version, runtime.GOOS, runtime.GOARCH, runtime.Version(), C.BuildTime) diff --git a/rules/provider/domain_strategy.go b/rules/provider/domain_strategy.go index c0787d58..0104fdf9 100644 --- a/rules/provider/domain_strategy.go +++ b/rules/provider/domain_strategy.go @@ -1,6 +1,9 @@ package provider import ( + "errors" + "io" + "github.com/metacubex/mihomo/component/trie" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" @@ -48,6 +51,25 @@ func (d *domainStrategy) FinishInsert() { d.domainTrie = nil } +func (d *domainStrategy) FromMrs(r io.Reader) error { + domainSet, count, err := trie.ReadDomainSetBin(r) + if err != nil { + return err + } + d.count = int(count) + d.domainSet = domainSet + return nil +} + +func (d *domainStrategy) WriteMrs(w io.Writer) error { + if d.domainSet == nil { + return errors.New("nil domainSet") + } + return d.domainSet.WriteBin(w, int64(d.count)) +} + +var _ mrsRuleStrategy = (*domainStrategy)(nil) + func NewDomainStrategy() *domainStrategy { return &domainStrategy{} } diff --git a/rules/provider/mrs_converter.go b/rules/provider/mrs_converter.go new file mode 100644 index 00000000..3b93b4a4 --- /dev/null +++ b/rules/provider/mrs_converter.go @@ -0,0 +1,71 @@ +package provider + +import ( + "io" + "os" + + P "github.com/metacubex/mihomo/constant/provider" + + "github.com/klauspost/compress/zstd" +) + +func ConvertToMrs(buf []byte, behavior P.RuleBehavior, format P.RuleFormat, w io.Writer) (err error) { + strategy := newStrategy(behavior, nil) + strategy, err = rulesParse(buf, strategy, format) + if err != nil { + return err + } + if _strategy, ok := strategy.(mrsRuleStrategy); ok { + var encoder *zstd.Encoder + encoder, err = zstd.NewWriter(w) + if err != nil { + return err + } + defer func() { + zstdErr := encoder.Close() + if err == nil { + err = zstdErr + } + }() + return _strategy.WriteMrs(encoder) + } else { + return ErrInvalidFormat + } +} + +func ConvertMain(args []string) { + if len(args) > 3 { + behavior, err := P.ParseBehavior(args[0]) + if err != nil { + panic(err) + } + format, err := P.ParseRuleFormat(args[1]) + if err != nil { + panic(err) + } + source := args[2] + target := args[3] + + sourceFile, err := os.ReadFile(source) + if err != nil { + panic(err) + } + + targetFile, err := os.OpenFile(target, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + panic(err) + } + + err = ConvertToMrs(sourceFile, behavior, format, targetFile) + if err != nil { + panic(err) + } + + err = targetFile.Close() + if err != nil { + panic(err) + } + } else { + panic("Usage: convert-ruleset ") + } +} diff --git a/rules/provider/parse.go b/rules/provider/parse.go index a20da28d..227debb3 100644 --- a/rules/provider/parse.go +++ b/rules/provider/parse.go @@ -32,28 +32,13 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t if err := decoder.Decode(mapping, schema); err != nil { return nil, err } - var behavior P.RuleBehavior - - switch schema.Behavior { - case "domain": - behavior = P.Domain - case "ipcidr": - behavior = P.IPCIDR - case "classical": - behavior = P.Classical - default: - return nil, fmt.Errorf("unsupported behavior type: %s", schema.Behavior) + behavior, err := P.ParseBehavior(schema.Behavior) + if err != nil { + return nil, err } - - var format P.RuleFormat - - switch schema.Format { - case "", "yaml": - format = P.YamlRule - case "text": - format = P.TextRule - default: - return nil, fmt.Errorf("unsupported format type: %s", schema.Format) + format, err := P.ParseRuleFormat(schema.Format) + if err != nil { + return nil, err } var vehicle P.Vehicle diff --git a/rules/provider/provider.go b/rules/provider/provider.go index 6c03c6e5..a4d8883d 100644 --- a/rules/provider/provider.go +++ b/rules/provider/provider.go @@ -4,16 +4,18 @@ import ( "bytes" "encoding/json" "errors" + "io" "runtime" "strings" "time" - "gopkg.in/yaml.v3" - "github.com/metacubex/mihomo/common/pool" "github.com/metacubex/mihomo/component/resource" C "github.com/metacubex/mihomo/constant" P "github.com/metacubex/mihomo/constant/provider" + + "github.com/klauspost/compress/zstd" + "gopkg.in/yaml.v3" ) var tunnel P.Tunnel @@ -52,6 +54,12 @@ type ruleStrategy interface { FinishInsert() } +type mrsRuleStrategy interface { + ruleStrategy + FromMrs(r io.Reader) error + WriteMrs(w io.Writer) error +} + func (rp *ruleSetProvider) Type() P.ProviderType { return P.Rule } @@ -152,9 +160,23 @@ func newStrategy(behavior P.RuleBehavior, parse func(tp, payload, target string, } var ErrNoPayload = errors.New("file must have a `payload` field") +var ErrInvalidFormat = errors.New("invalid format") func rulesParse(buf []byte, strategy ruleStrategy, format P.RuleFormat) (ruleStrategy, error) { strategy.Reset() + if format == P.MrsRule { + if _strategy, ok := strategy.(mrsRuleStrategy); ok { + reader, err := zstd.NewReader(bytes.NewReader(buf)) + if err != nil { + return nil, err + } + defer reader.Close() + err = _strategy.FromMrs(reader) + return strategy, err + } else { + return nil, ErrInvalidFormat + } + } schema := &RulePayload{} @@ -228,6 +250,8 @@ func rulesParse(buf []byte, strategy ruleStrategy, format P.RuleFormat) (ruleStr if len(schema.Payload) > 0 { str = schema.Payload[0] } + default: + return nil, ErrInvalidFormat } if str == "" { From 4f8a5a5f54ef082dfe02d5db4179e82292ee61d4 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 27 Jul 2024 10:36:11 +0800 Subject: [PATCH 145/311] feat: add `mrs` format ipcidr ruleset --- component/cidr/ipcidr_set_bin.go | 77 ++++++++++++++++++++++++++++ component/trie/domain_set_bin.go | 40 +++++---------- constant/provider/interface.go | 13 +++++ docs/config.yaml | 2 +- rules/provider/classical_strategy.go | 5 ++ rules/provider/domain_strategy.go | 13 +++-- rules/provider/ipcidr_strategy.go | 28 ++++++++++ rules/provider/mrs_converter.go | 33 ++++++++++++ rules/provider/mrs_reader.go | 72 ++++++++++++++++++++++++++ rules/provider/provider.go | 16 ++---- 10 files changed, 255 insertions(+), 44 deletions(-) create mode 100644 component/cidr/ipcidr_set_bin.go create mode 100644 rules/provider/mrs_reader.go diff --git a/component/cidr/ipcidr_set_bin.go b/component/cidr/ipcidr_set_bin.go new file mode 100644 index 00000000..f6a03488 --- /dev/null +++ b/component/cidr/ipcidr_set_bin.go @@ -0,0 +1,77 @@ +package cidr + +import ( + "encoding/binary" + "errors" + "io" + "net/netip" + + "go4.org/netipx" +) + +func (ss *IpCidrSet) WriteBin(w io.Writer) (err error) { + // version + _, err = w.Write([]byte{1}) + if err != nil { + return err + } + + // rr + err = binary.Write(w, binary.BigEndian, int64(len(ss.rr))) + if err != nil { + return err + } + for _, r := range ss.rr { + err = binary.Write(w, binary.BigEndian, r.From().As16()) + if err != nil { + return err + } + err = binary.Write(w, binary.BigEndian, r.To().As16()) + if err != nil { + return err + } + } + + return nil +} + +func ReadIpCidrSet(r io.Reader) (ss *IpCidrSet, err error) { + // version + version := make([]byte, 1) + _, err = io.ReadFull(r, version) + if err != nil { + return nil, err + } + if version[0] != 1 { + return nil, errors.New("version is invalid") + } + + ss = NewIpCidrSet() + var length int64 + + // rr + err = binary.Read(r, binary.BigEndian, &length) + if err != nil { + return nil, err + } + if length < 1 { + return nil, errors.New("length is invalid") + } + ss.rr = make([]netipx.IPRange, length) + for i := int64(0); i < length; i++ { + var a16 [16]byte + err = binary.Read(r, binary.BigEndian, &a16) + if err != nil { + return nil, err + } + from := netip.AddrFrom16(a16).Unmap() + err = binary.Read(r, binary.BigEndian, &a16) + if err != nil { + return nil, err + } + to := netip.AddrFrom16(a16).Unmap() + ss.rr[i] = netipx.IPRangeFrom(from, to) + } + + return ss, nil +} diff --git a/component/trie/domain_set_bin.go b/component/trie/domain_set_bin.go index e32d4e1a..27d15802 100644 --- a/component/trie/domain_set_bin.go +++ b/component/trie/domain_set_bin.go @@ -6,19 +6,13 @@ import ( "io" ) -func (ss *DomainSet) WriteBin(w io.Writer, count int64) (err error) { +func (ss *DomainSet) WriteBin(w io.Writer) (err error) { // version _, err = w.Write([]byte{1}) if err != nil { return err } - // count - err = binary.Write(w, binary.BigEndian, count) - if err != nil { - return err - } - // leaves err = binary.Write(w, binary.BigEndian, int64(len(ss.leaves))) if err != nil { @@ -56,21 +50,15 @@ func (ss *DomainSet) WriteBin(w io.Writer, count int64) (err error) { return nil } -func ReadDomainSetBin(r io.Reader) (ds *DomainSet, count int64, err error) { +func ReadDomainSetBin(r io.Reader) (ds *DomainSet, err error) { // version version := make([]byte, 1) _, err = io.ReadFull(r, version) if err != nil { - return nil, 0, err + return nil, err } if version[0] != 1 { - return nil, 0, errors.New("version is invalid") - } - - // count - err = binary.Read(r, binary.BigEndian, &count) - if err != nil { - return nil, 0, err + return nil, errors.New("version is invalid") } ds = &DomainSet{} @@ -79,49 +67,49 @@ func ReadDomainSetBin(r io.Reader) (ds *DomainSet, count int64, err error) { // leaves err = binary.Read(r, binary.BigEndian, &length) if err != nil { - return nil, 0, err + return nil, err } if length < 1 { - return nil, 0, errors.New("length is invalid") + return nil, errors.New("length is invalid") } ds.leaves = make([]uint64, length) for i := int64(0); i < length; i++ { err = binary.Read(r, binary.BigEndian, &ds.leaves[i]) if err != nil { - return nil, 0, err + return nil, err } } // labelBitmap err = binary.Read(r, binary.BigEndian, &length) if err != nil { - return nil, 0, err + return nil, err } if length < 1 { - return nil, 0, errors.New("length is invalid") + return nil, errors.New("length is invalid") } ds.labelBitmap = make([]uint64, length) for i := int64(0); i < length; i++ { err = binary.Read(r, binary.BigEndian, &ds.labelBitmap[i]) if err != nil { - return nil, 0, err + return nil, err } } // labels err = binary.Read(r, binary.BigEndian, &length) if err != nil { - return nil, 0, err + return nil, err } if length < 1 { - return nil, 0, errors.New("length is invalid") + return nil, errors.New("length is invalid") } ds.labels = make([]byte, length) _, err = io.ReadFull(r, ds.labels) if err != nil { - return nil, 0, err + return nil, err } ds.init() - return ds, count, nil + return ds, nil } diff --git a/constant/provider/interface.go b/constant/provider/interface.go index c86e6163..bd6b6e94 100644 --- a/constant/provider/interface.go +++ b/constant/provider/interface.go @@ -112,6 +112,19 @@ func (rt RuleBehavior) String() string { } } +func (rt RuleBehavior) Byte() byte { + switch rt { + case Domain: + return 0 + case IPCIDR: + return 1 + case Classical: + return 2 + default: + return 255 + } +} + func ParseBehavior(s string) (behavior RuleBehavior, err error) { switch s { case "domain": diff --git a/docs/config.yaml b/docs/config.yaml index 2d3343cf..6e29f164 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -942,7 +942,7 @@ rule-providers: interval: 259200 path: /path/to/save/file.yaml type: file - rule3: # mrs类型ruleset,目前仅支持domain,可以通过“mihomo convert-ruleset domain yaml XXX.yaml XXX.mrs”转换得到 + rule3: # mrs类型ruleset,目前仅支持domain和ipcidr,可以通过“mihomo convert-ruleset domain yaml XXX.yaml XXX.mrs”转换得到 type: http url: "url" format: mrs diff --git a/rules/provider/classical_strategy.go b/rules/provider/classical_strategy.go index 8353ebce..205a8e59 100644 --- a/rules/provider/classical_strategy.go +++ b/rules/provider/classical_strategy.go @@ -5,6 +5,7 @@ import ( "strings" C "github.com/metacubex/mihomo/constant" + P "github.com/metacubex/mihomo/constant/provider" "github.com/metacubex/mihomo/log" ) @@ -16,6 +17,10 @@ type classicalStrategy struct { parse func(tp, payload, target string, params []string) (parsed C.Rule, parseErr error) } +func (c *classicalStrategy) Behavior() P.RuleBehavior { + return P.Classical +} + func (c *classicalStrategy) Match(metadata *C.Metadata) bool { for _, rule := range c.rules { if m, _ := rule.Match(metadata); m { diff --git a/rules/provider/domain_strategy.go b/rules/provider/domain_strategy.go index 0104fdf9..462d37dc 100644 --- a/rules/provider/domain_strategy.go +++ b/rules/provider/domain_strategy.go @@ -6,6 +6,7 @@ import ( "github.com/metacubex/mihomo/component/trie" C "github.com/metacubex/mihomo/constant" + P "github.com/metacubex/mihomo/constant/provider" "github.com/metacubex/mihomo/log" ) @@ -15,6 +16,10 @@ type domainStrategy struct { domainSet *trie.DomainSet } +func (d *domainStrategy) Behavior() P.RuleBehavior { + return P.Domain +} + func (d *domainStrategy) ShouldFindProcess() bool { return false } @@ -51,12 +56,12 @@ func (d *domainStrategy) FinishInsert() { d.domainTrie = nil } -func (d *domainStrategy) FromMrs(r io.Reader) error { - domainSet, count, err := trie.ReadDomainSetBin(r) +func (d *domainStrategy) FromMrs(r io.Reader, count int) error { + domainSet, err := trie.ReadDomainSetBin(r) if err != nil { return err } - d.count = int(count) + d.count = count d.domainSet = domainSet return nil } @@ -65,7 +70,7 @@ func (d *domainStrategy) WriteMrs(w io.Writer) error { if d.domainSet == nil { return errors.New("nil domainSet") } - return d.domainSet.WriteBin(w, int64(d.count)) + return d.domainSet.WriteBin(w) } var _ mrsRuleStrategy = (*domainStrategy)(nil) diff --git a/rules/provider/ipcidr_strategy.go b/rules/provider/ipcidr_strategy.go index d0545c7c..87cf7a2d 100644 --- a/rules/provider/ipcidr_strategy.go +++ b/rules/provider/ipcidr_strategy.go @@ -1,8 +1,12 @@ package provider import ( + "errors" + "io" + "github.com/metacubex/mihomo/component/cidr" C "github.com/metacubex/mihomo/constant" + P "github.com/metacubex/mihomo/constant/provider" "github.com/metacubex/mihomo/log" "go4.org/netipx" @@ -15,6 +19,10 @@ type ipcidrStrategy struct { //trie *trie.IpCidrTrie } +func (i *ipcidrStrategy) Behavior() P.RuleBehavior { + return P.IPCIDR +} + func (i *ipcidrStrategy) ShouldFindProcess() bool { return false } @@ -54,6 +62,26 @@ func (i *ipcidrStrategy) FinishInsert() { i.cidrSet.Merge() } +func (i *ipcidrStrategy) FromMrs(r io.Reader, count int) error { + cidrSet, err := cidr.ReadIpCidrSet(r) + if err != nil { + return err + } + i.count = count + i.cidrSet = cidrSet + if i.count > 0 { + i.shouldResolveIP = true + } + return nil +} + +func (i *ipcidrStrategy) WriteMrs(w io.Writer) error { + if i.cidrSet == nil { + return errors.New("nil cidrSet") + } + return i.cidrSet.WriteBin(w) +} + func (i *ipcidrStrategy) ToIpCidr() *netipx.IPSet { return i.cidrSet.ToIPSet() } diff --git a/rules/provider/mrs_converter.go b/rules/provider/mrs_converter.go index 3b93b4a4..c8f63fdf 100644 --- a/rules/provider/mrs_converter.go +++ b/rules/provider/mrs_converter.go @@ -1,6 +1,7 @@ package provider import ( + "encoding/binary" "io" "os" @@ -27,6 +28,38 @@ func ConvertToMrs(buf []byte, behavior P.RuleBehavior, format P.RuleFormat, w io err = zstdErr } }() + + // header + _, err = encoder.Write(MrsMagicBytes[:]) + if err != nil { + return err + } + + // behavior + _behavior := []byte{behavior.Byte()} + _, err = encoder.Write(_behavior[:]) + if err != nil { + return err + } + + // count + count := int64(_strategy.Count()) + err = binary.Write(encoder, binary.BigEndian, count) + if err != nil { + return err + } + + // extra (reserved for future using) + var extra []byte + err = binary.Write(encoder, binary.BigEndian, int64(len(extra))) + if err != nil { + return err + } + _, err = encoder.Write(extra) + if err != nil { + return err + } + return _strategy.WriteMrs(encoder) } else { return ErrInvalidFormat diff --git a/rules/provider/mrs_reader.go b/rules/provider/mrs_reader.go new file mode 100644 index 00000000..66f62127 --- /dev/null +++ b/rules/provider/mrs_reader.go @@ -0,0 +1,72 @@ +package provider + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + + "github.com/klauspost/compress/zstd" +) + +var MrsMagicBytes = [4]byte{'M', 'R', 'S', 1} // MRSv1 + +func rulesMrsParse(buf []byte, strategy ruleStrategy) (ruleStrategy, error) { + if _strategy, ok := strategy.(mrsRuleStrategy); ok { + reader, err := zstd.NewReader(bytes.NewReader(buf)) + if err != nil { + return nil, err + } + defer reader.Close() + + // header + var header [4]byte + _, err = io.ReadFull(reader, header[:]) + if err != nil { + return nil, err + } + if header != MrsMagicBytes { + return nil, fmt.Errorf("invalid MrsMagic bytes") + } + + // behavior + var _behavior [1]byte + _, err = io.ReadFull(reader, _behavior[:]) + if err != nil { + return nil, err + } + if _behavior[0] != strategy.Behavior().Byte() { + return nil, fmt.Errorf("invalid behavior") + } + + // count + var count int64 + err = binary.Read(reader, binary.BigEndian, &count) + if err != nil { + return nil, err + } + + // extra (reserved for future using) + var length int64 + err = binary.Read(reader, binary.BigEndian, &length) + if err != nil { + return nil, err + } + if length < 0 { + return nil, errors.New("length is invalid") + } + if length > 0 { + extra := make([]byte, length) + _, err = io.ReadFull(reader, extra) + if err != nil { + return nil, err + } + } + + err = _strategy.FromMrs(reader, int(count)) + return strategy, err + } else { + return nil, ErrInvalidFormat + } +} diff --git a/rules/provider/provider.go b/rules/provider/provider.go index a4d8883d..8c5d7f94 100644 --- a/rules/provider/provider.go +++ b/rules/provider/provider.go @@ -14,7 +14,6 @@ import ( C "github.com/metacubex/mihomo/constant" P "github.com/metacubex/mihomo/constant/provider" - "github.com/klauspost/compress/zstd" "gopkg.in/yaml.v3" ) @@ -45,6 +44,7 @@ type RulePayload struct { } type ruleStrategy interface { + Behavior() P.RuleBehavior Match(metadata *C.Metadata) bool Count() int ShouldResolveIP() bool @@ -56,7 +56,7 @@ type ruleStrategy interface { type mrsRuleStrategy interface { ruleStrategy - FromMrs(r io.Reader) error + FromMrs(r io.Reader, count int) error WriteMrs(w io.Writer) error } @@ -165,17 +165,7 @@ var ErrInvalidFormat = errors.New("invalid format") func rulesParse(buf []byte, strategy ruleStrategy, format P.RuleFormat) (ruleStrategy, error) { strategy.Reset() if format == P.MrsRule { - if _strategy, ok := strategy.(mrsRuleStrategy); ok { - reader, err := zstd.NewReader(bytes.NewReader(buf)) - if err != nil { - return nil, err - } - defer reader.Close() - err = _strategy.FromMrs(reader) - return strategy, err - } else { - return nil, ErrInvalidFormat - } + return rulesMrsParse(buf, strategy) } schema := &RulePayload{} From 1db3e4583ba0edbc6bee8bcfbd31fa94f4c438fc Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 27 Jul 2024 23:54:28 +0800 Subject: [PATCH 146/311] chore: better converter --- docs/config.yaml | 7 ++++++- rules/provider/domain_strategy.go | 5 +++++ rules/provider/mrs_converter.go | 4 ++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/config.yaml b/docs/config.yaml index 6e29f164..669c8be7 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -942,7 +942,12 @@ rule-providers: interval: 259200 path: /path/to/save/file.yaml type: file - rule3: # mrs类型ruleset,目前仅支持domain和ipcidr,可以通过“mihomo convert-ruleset domain yaml XXX.yaml XXX.mrs”转换得到 + rule3: + # mrs类型ruleset,目前仅支持domain和ipcidr(即不支持classical), + # behavior=domain,format=yaml 可以通过“mihomo convert-ruleset domain yaml XXX.yaml XXX.mrs”转换得到 + # behavior=domain,format=text 可以通过“mihomo convert-ruleset domain text XXX.text XXX.mrs”转换得到 + # behavior=ipcidr,format=yaml 可以通过“mihomo convert-ruleset ipcidr yaml XXX.yaml XXX.mrs”转换得到 + # behavior=ipcidr,format=text 可以通过“mihomo convert-ruleset ipcidr text XXX.text XXX.mrs”转换得到 type: http url: "url" format: mrs diff --git a/rules/provider/domain_strategy.go b/rules/provider/domain_strategy.go index 462d37dc..a999f5bd 100644 --- a/rules/provider/domain_strategy.go +++ b/rules/provider/domain_strategy.go @@ -3,6 +3,7 @@ package provider import ( "errors" "io" + "strings" "github.com/metacubex/mihomo/component/trie" C "github.com/metacubex/mihomo/constant" @@ -43,6 +44,10 @@ func (d *domainStrategy) Reset() { } func (d *domainStrategy) Insert(rule string) { + if strings.ContainsRune(rule, '/') { + log.Warnln("invalid domain:[%s]", rule) + return + } err := d.domainTrie.Insert(rule, struct{}{}) if err != nil { log.Warnln("invalid domain:[%s]", rule) diff --git a/rules/provider/mrs_converter.go b/rules/provider/mrs_converter.go index c8f63fdf..a0830198 100644 --- a/rules/provider/mrs_converter.go +++ b/rules/provider/mrs_converter.go @@ -2,6 +2,7 @@ package provider import ( "encoding/binary" + "errors" "io" "os" @@ -16,6 +17,9 @@ func ConvertToMrs(buf []byte, behavior P.RuleBehavior, format P.RuleFormat, w io if err != nil { return err } + if strategy.Count() == 0 { + return errors.New("empty rule") + } if _strategy, ok := strategy.(mrsRuleStrategy); ok { var encoder *zstd.Encoder encoder, err = zstd.NewWriter(w) From c830b8aaf7a8889e51a30972ad7b3a0e13e23a34 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 28 Jul 2024 10:07:37 +0800 Subject: [PATCH 147/311] feat: support convert `mrs` format back to `text` format --- component/cidr/ipcidr_set.go | 10 ++++++++ component/trie/domain.go | 19 +++++++++++----- component/trie/domain_set.go | 38 ++++++++++++++++++++++++++++++- component/trie/domain_set_test.go | 20 ++++++++++++++++ component/trie/domain_test.go | 3 ++- docs/config.yaml | 15 ++++++++---- rules/provider/domain_strategy.go | 22 ++++++++++++++++++ rules/provider/ipcidr_strategy.go | 9 ++++++++ rules/provider/mrs_converter.go | 12 ++++++++++ rules/provider/provider.go | 1 + 10 files changed, 137 insertions(+), 12 deletions(-) diff --git a/component/cidr/ipcidr_set.go b/component/cidr/ipcidr_set.go index 521fabab..49071460 100644 --- a/component/cidr/ipcidr_set.go +++ b/component/cidr/ipcidr_set.go @@ -57,6 +57,16 @@ func (set *IpCidrSet) Merge() error { return nil } +func (set *IpCidrSet) Foreach(f func(prefix netip.Prefix) bool) { + for _, r := range set.rr { + for _, prefix := range r.Prefixes() { + if !f(prefix) { + return + } + } + } +} + // ToIPSet not safe convert to *netipx.IPSet // be careful, must be used after Merge func (set *IpCidrSet) ToIPSet() *netipx.IPSet { diff --git a/component/trie/domain.go b/component/trie/domain.go index 3decbb02..db30402e 100644 --- a/component/trie/domain.go +++ b/component/trie/domain.go @@ -123,16 +123,18 @@ func (t *DomainTrie[T]) Optimize() { t.root.optimize() } -func (t *DomainTrie[T]) Foreach(print func(domain string, data T)) { +func (t *DomainTrie[T]) Foreach(fn func(domain string, data T) bool) { for key, data := range t.root.getChildren() { - recursion([]string{key}, data, print) + recursion([]string{key}, data, fn) if data != nil && data.inited { - print(joinDomain([]string{key}), data.data) + if !fn(joinDomain([]string{key}), data.data) { + return + } } } } -func recursion[T any](items []string, node *Node[T], fn func(domain string, data T)) { +func recursion[T any](items []string, node *Node[T], fn func(domain string, data T) bool) bool { for key, data := range node.getChildren() { newItems := append([]string{key}, items...) if data != nil && data.inited { @@ -140,10 +142,15 @@ func recursion[T any](items []string, node *Node[T], fn func(domain string, data if domain[0] == domainStepByte { domain = complexWildcard + domain } - fn(domain, data.Data()) + if !fn(domain, data.Data()) { + return false + } + } + if !recursion(newItems, data, fn) { + return false } - recursion(newItems, data, fn) } + return true } func joinDomain(items []string) string { diff --git a/component/trie/domain_set.go b/component/trie/domain_set.go index 860d1235..7778d133 100644 --- a/component/trie/domain_set.go +++ b/component/trie/domain_set.go @@ -28,8 +28,9 @@ type qElt struct{ s, e, col int } // NewDomainSet creates a new *DomainSet struct, from a DomainTrie. func (t *DomainTrie[T]) NewDomainSet() *DomainSet { reserveDomains := make([]string, 0) - t.Foreach(func(domain string, data T) { + t.Foreach(func(domain string, data T) bool { reserveDomains = append(reserveDomains, utils.Reverse(domain)) + return true }) // ensure that the same prefix is continuous // and according to the ascending sequence of length @@ -136,6 +137,41 @@ func (ss *DomainSet) Has(key string) bool { } +func (ss *DomainSet) keys(f func(key string) bool) { + var currentKey []byte + var traverse func(int, int) bool + traverse = func(nodeId, bmIdx int) bool { + if getBit(ss.leaves, nodeId) != 0 { + if !f(string(currentKey)) { + return false + } + } + + for ; ; bmIdx++ { + if getBit(ss.labelBitmap, bmIdx) != 0 { + return true + } + nextLabel := ss.labels[bmIdx-nodeId] + currentKey = append(currentKey, nextLabel) + nextNodeId := countZeros(ss.labelBitmap, ss.ranks, bmIdx+1) + nextBmIdx := selectIthOne(ss.labelBitmap, ss.ranks, ss.selects, nextNodeId-1) + 1 + if !traverse(nextNodeId, nextBmIdx) { + return false + } + currentKey = currentKey[:len(currentKey)-1] + } + } + + traverse(0, 0) + return +} + +func (ss *DomainSet) Foreach(f func(key string) bool) { + ss.keys(func(key string) bool { + return f(utils.Reverse(key)) + }) +} + func setBit(bm *[]uint64, i int, v int) { for i>>6 >= len(*bm) { *bm = append(*bm, 0) diff --git a/component/trie/domain_set_test.go b/component/trie/domain_set_test.go index 77106d5f..e343d11d 100644 --- a/component/trie/domain_set_test.go +++ b/component/trie/domain_set_test.go @@ -1,12 +1,29 @@ package trie_test import ( + "golang.org/x/exp/slices" "testing" "github.com/metacubex/mihomo/component/trie" "github.com/stretchr/testify/assert" ) +func testDump(t *testing.T, tree *trie.DomainTrie[struct{}], set *trie.DomainSet) { + var dataSrc []string + tree.Foreach(func(domain string, data struct{}) bool { + dataSrc = append(dataSrc, domain) + return true + }) + slices.Sort(dataSrc) + var dataSet []string + set.Foreach(func(key string) bool { + dataSet = append(dataSet, key) + return true + }) + slices.Sort(dataSet) + assert.Equal(t, dataSrc, dataSet) +} + func TestDomainSet(t *testing.T) { tree := trie.New[struct{}]() domainSet := []string{ @@ -33,6 +50,7 @@ func TestDomainSet(t *testing.T) { assert.True(t, set.Has("google.com")) assert.False(t, set.Has("qq.com")) assert.False(t, set.Has("www.baidu.com")) + testDump(t, tree, set) } func TestDomainSetComplexWildcard(t *testing.T) { @@ -55,6 +73,7 @@ func TestDomainSetComplexWildcard(t *testing.T) { assert.False(t, set.Has("google.com")) assert.True(t, set.Has("www.baidu.com")) assert.True(t, set.Has("test.test.baidu.com")) + testDump(t, tree, set) } func TestDomainSetWildcard(t *testing.T) { @@ -82,4 +101,5 @@ func TestDomainSetWildcard(t *testing.T) { assert.False(t, set.Has("a.www.google.com")) assert.False(t, set.Has("test.qq.com")) assert.False(t, set.Has("test.test.test.qq.com")) + testDump(t, tree, set) } diff --git a/component/trie/domain_test.go b/component/trie/domain_test.go index 4c5d8002..916f6107 100644 --- a/component/trie/domain_test.go +++ b/component/trie/domain_test.go @@ -121,8 +121,9 @@ func TestTrie_Foreach(t *testing.T) { assert.NoError(t, tree.Insert(domain, localIP)) } count := 0 - tree.Foreach(func(domain string, data netip.Addr) { + tree.Foreach(func(domain string, data netip.Addr) bool { count++ + return true }) assert.Equal(t, 7, count) } diff --git a/docs/config.yaml b/docs/config.yaml index 669c8be7..d7c686d0 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -944,10 +944,17 @@ rule-providers: type: file rule3: # mrs类型ruleset,目前仅支持domain和ipcidr(即不支持classical), - # behavior=domain,format=yaml 可以通过“mihomo convert-ruleset domain yaml XXX.yaml XXX.mrs”转换得到 - # behavior=domain,format=text 可以通过“mihomo convert-ruleset domain text XXX.text XXX.mrs”转换得到 - # behavior=ipcidr,format=yaml 可以通过“mihomo convert-ruleset ipcidr yaml XXX.yaml XXX.mrs”转换得到 - # behavior=ipcidr,format=text 可以通过“mihomo convert-ruleset ipcidr text XXX.text XXX.mrs”转换得到 + # + # 对于behavior=domain: + # - format=yaml 可以通过“mihomo convert-ruleset domain yaml XXX.yaml XXX.mrs”转换到mrs格式 + # - format=text 可以通过“mihomo convert-ruleset domain text XXX.text XXX.mrs”转换到mrs格式 + # - XXX.mrs 可以通过"mihomo convert-ruleset domain mrs XXX.mrs XXX.text"转换回text格式(暂不支持转换回ymal格式) + # + # 对于behavior=ipcidr: + # - format=yaml 可以通过“mihomo convert-ruleset ipcidr yaml XXX.yaml XXX.mrs”转换到mrs格式 + # - format=text 可以通过“mihomo convert-ruleset ipcidr text XXX.text XXX.mrs”转换到mrs格式 + # - XXX.mrs 可以通过"mihomo convert-ruleset ipcidr mrs XXX.mrs XXX.text"转换回text格式(暂不支持转换回ymal格式) + # type: http url: "url" format: mrs diff --git a/rules/provider/domain_strategy.go b/rules/provider/domain_strategy.go index a999f5bd..b893f038 100644 --- a/rules/provider/domain_strategy.go +++ b/rules/provider/domain_strategy.go @@ -9,6 +9,8 @@ import ( C "github.com/metacubex/mihomo/constant" P "github.com/metacubex/mihomo/constant/provider" "github.com/metacubex/mihomo/log" + + "golang.org/x/exp/slices" ) type domainStrategy struct { @@ -78,6 +80,26 @@ func (d *domainStrategy) WriteMrs(w io.Writer) error { return d.domainSet.WriteBin(w) } +func (d *domainStrategy) DumpMrs(f func(key string) bool) { + if d.domainSet != nil { + var keys []string + d.domainSet.Foreach(func(key string) bool { + keys = append(keys, key) + return true + }) + slices.Sort(keys) + + for _, key := range keys { + if _, ok := slices.BinarySearch(keys, "+."+key); ok { + continue // ignore the rules added by trie internal processing + } + if !f(key) { + return + } + } + } +} + var _ mrsRuleStrategy = (*domainStrategy)(nil) func NewDomainStrategy() *domainStrategy { diff --git a/rules/provider/ipcidr_strategy.go b/rules/provider/ipcidr_strategy.go index 87cf7a2d..9efffed9 100644 --- a/rules/provider/ipcidr_strategy.go +++ b/rules/provider/ipcidr_strategy.go @@ -3,6 +3,7 @@ package provider import ( "errors" "io" + "net/netip" "github.com/metacubex/mihomo/component/cidr" C "github.com/metacubex/mihomo/constant" @@ -82,6 +83,14 @@ func (i *ipcidrStrategy) WriteMrs(w io.Writer) error { return i.cidrSet.WriteBin(w) } +func (i *ipcidrStrategy) DumpMrs(f func(key string) bool) { + if i.cidrSet != nil { + i.cidrSet.Foreach(func(prefix netip.Prefix) bool { + return f(prefix.String()) + }) + } +} + func (i *ipcidrStrategy) ToIpCidr() *netipx.IPSet { return i.cidrSet.ToIPSet() } diff --git a/rules/provider/mrs_converter.go b/rules/provider/mrs_converter.go index a0830198..edc24e7e 100644 --- a/rules/provider/mrs_converter.go +++ b/rules/provider/mrs_converter.go @@ -3,6 +3,7 @@ package provider import ( "encoding/binary" "errors" + "fmt" "io" "os" @@ -21,6 +22,17 @@ func ConvertToMrs(buf []byte, behavior P.RuleBehavior, format P.RuleFormat, w io return errors.New("empty rule") } if _strategy, ok := strategy.(mrsRuleStrategy); ok { + if format == P.MrsRule { // export to TextRule + _strategy.DumpMrs(func(key string) bool { + _, err = fmt.Fprintln(w, key) + if err != nil { + return false + } + return true + }) + return nil + } + var encoder *zstd.Encoder encoder, err = zstd.NewWriter(w) if err != nil { diff --git a/rules/provider/provider.go b/rules/provider/provider.go index 8c5d7f94..b9524c35 100644 --- a/rules/provider/provider.go +++ b/rules/provider/provider.go @@ -58,6 +58,7 @@ type mrsRuleStrategy interface { ruleStrategy FromMrs(r io.Reader, count int) error WriteMrs(w io.Writer) error + DumpMrs(f func(key string) bool) } func (rp *ruleSetProvider) Type() P.ProviderType { From 117cdd8b541de0e1048f3a573679ff8b61893797 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 29 Jul 2024 21:14:59 +0800 Subject: [PATCH 148/311] chore: remove suppress_prefixlength in tun linux auto-route for inet4/6-route-address https://github.com/MetaCubeX/mihomo/issues/1368 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d5ac6bee..e18f1e55 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.7 github.com/metacubex/sing-shadowsocks2 v0.2.1 - github.com/metacubex/sing-tun v0.2.7-0.20240719141246-19c49ac9589d + github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 diff --git a/go.sum b/go.sum index 7e9cd5d8..dbc4eafc 100644 --- a/go.sum +++ b/go.sum @@ -115,8 +115,8 @@ github.com/metacubex/sing-shadowsocks v0.2.7 h1:9f3Dt2+71TNp0e202llA2ug5h/rkWs2E github.com/metacubex/sing-shadowsocks v0.2.7/go.mod h1:X3x88XtJpBxG0W0/ECOJL6Ib0SJ3xdniAkU/6/RMWU0= github.com/metacubex/sing-shadowsocks2 v0.2.1 h1:XIZBXlazp8EEoPp1S0DViAhLkJakjQ2f+AOwwdKKNYg= github.com/metacubex/sing-shadowsocks2 v0.2.1/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q= -github.com/metacubex/sing-tun v0.2.7-0.20240719141246-19c49ac9589d h1:iYlepjRCYlPXtELupDL+pQjGqkCnQz4KQOfKImP9sog= -github.com/metacubex/sing-tun v0.2.7-0.20240719141246-19c49ac9589d/go.mod h1:olbEx9yVcaw5tHTNlRamRoxmMKcvDvcVS1YLnQGzvWE= +github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 h1:ypfofGDZbP8p3Y4P/m74JYu7sQViesi3c8nbmT6cS0Y= +github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1/go.mod h1:olbEx9yVcaw5tHTNlRamRoxmMKcvDvcVS1YLnQGzvWE= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosqY8xKDp3pqTW3qbrCprZ1l6WkrXSFSCwyY4I= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a h1:NpSGclHJUYndUwBmyIpFBSoBVg8PoVX7QQKhYg0DjM0= From e7e14001262181b3bd396d18264df9da06da3cc1 Mon Sep 17 00:00:00 2001 From: OxO Date: Sat, 29 Jun 2024 17:44:07 +0800 Subject: [PATCH 149/311] chore: reduce image size --- Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 64b33cf7..67d4a6e5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ WORKDIR /mihomo COPY bin/ bin/ RUN FILE_NAME=`sh file-name.sh` && echo $FILE_NAME && \ FILE_NAME=`ls bin/ | egrep "$FILE_NAME.gz"|awk NR==1` && echo $FILE_NAME && \ - mv bin/$FILE_NAME mihomo.gz && gzip -d mihomo.gz && echo "$FILE_NAME" > /mihomo-config/test + mv bin/$FILE_NAME mihomo.gz && gzip -d mihomo.gz && chmod +x mihomo && echo "$FILE_NAME" > /mihomo-config/test FROM alpine:latest LABEL org.opencontainers.image.source="https://github.com/MetaCubeX/mihomo" @@ -23,5 +23,4 @@ VOLUME ["/root/.config/mihomo/"] COPY --from=builder /mihomo-config/ /root/.config/mihomo/ COPY --from=builder /mihomo/mihomo /mihomo -RUN chmod +x /mihomo ENTRYPOINT [ "/mihomo" ] From fd205bfa8dd62f9cd0290e165e18a1ba00c16953 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 3 Aug 2024 08:41:52 +0800 Subject: [PATCH 150/311] chore: update quic-go to 0.45.2 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e18f1e55..4cfdb189 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/mdlayher/netlink v1.7.2 github.com/metacubex/chacha v0.1.0 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 - github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e + github.com/metacubex/quic-go v0.45.1-0.20240803003931-60a15f6efd94 github.com/metacubex/randv2 v0.2.0 github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.7 diff --git a/go.sum b/go.sum index dbc4eafc..dce392a8 100644 --- a/go.sum +++ b/go.sum @@ -103,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-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= -github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e h1:bLYn3GuRvWDcBDAkIv5kUYIhzHwafDVq635BuybnKqI= -github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e/go.mod h1:Yza2H7Ax1rxWPUcJx0vW+oAt9EsPuSiyQFhFabUPzwU= +github.com/metacubex/quic-go v0.45.1-0.20240803003931-60a15f6efd94 h1:wlhwgxRzPLH8Ce0VME35iD2sr7jY2gFrL299/T4C2Sg= +github.com/metacubex/quic-go v0.45.1-0.20240803003931-60a15f6efd94/go.mod h1:Yza2H7Ax1rxWPUcJx0vW+oAt9EsPuSiyQFhFabUPzwU= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297 h1:YG/JkwGPbca5rUtEMHIu8ZuqzR7BSVm1iqY8hNoMeMA= From bb554e89d95296ccfdd4eaf3f31ac7a0475c6787 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 3 Aug 2024 09:12:10 +0800 Subject: [PATCH 151/311] action: add `GOTOOLCHAIN=local` to env --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9b8e000a..b936a57f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -155,6 +155,7 @@ jobs: echo "BUILDTIME=$(date)" >> $GITHUB_ENV echo "CGO_ENABLED=0" >> $GITHUB_ENV echo "BUILDTAG=-extldflags --static" >> $GITHUB_ENV + echo "GOTOOLCHAIN=local" >> $GITHUB_ENV - name: Setup NDK if: ${{ matrix.jobs.goos == 'android' }} From e4646fc3d2aa4f19bb6a706e4be69f826b7aa11e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 3 Aug 2024 09:51:13 +0800 Subject: [PATCH 152/311] chore: update dependencies --- go.mod | 20 ++++++++++---------- go.sum | 42 +++++++++++++++++++++--------------------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/go.mod b/go.mod index 4cfdb189..57d6eea7 100644 --- a/go.mod +++ b/go.mod @@ -7,13 +7,13 @@ require ( github.com/bahlo/generic-list-go v0.2.0 github.com/cilium/ebpf v0.12.3 github.com/coreos/go-iptables v0.7.0 - github.com/dlclark/regexp2 v1.11.0 - github.com/go-chi/chi/v5 v5.0.14 + github.com/dlclark/regexp2 v1.11.2 + github.com/go-chi/chi/v5 v5.1.0 github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.3 github.com/gobwas/ws v1.4.0 github.com/gofrs/uuid/v5 v5.2.0 - github.com/insomniacslk/dhcp v0.0.0-20240529192340-51bc6136a0a6 + github.com/insomniacslk/dhcp v0.0.0-20240710054256-ddd8a41251c9 github.com/klauspost/compress v1.17.9 github.com/klauspost/cpuid/v2 v2.2.8 github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 @@ -34,7 +34,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/v3 v3.2.0 + github.com/puzpuzpuz/xsync/v3 v3.4.0 github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a github.com/sagernet/fswatch v0.1.1 github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a @@ -42,7 +42,7 @@ require ( github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 github.com/sagernet/sing-shadowtls v0.1.4 github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e - github.com/samber/lo v1.39.0 + github.com/samber/lo v1.46.0 github.com/shirou/gopsutil/v3 v3.24.5 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.9.0 @@ -50,9 +50,9 @@ require ( gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 go.uber.org/automaxprocs v1.5.3 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba - golang.org/x/crypto v0.24.0 - golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 - golang.org/x/net v0.26.0 + golang.org/x/crypto v0.25.0 + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 + golang.org/x/net v0.27.0 golang.org/x/sync v0.7.0 golang.org/x/sys v0.22.0 google.golang.org/protobuf v1.34.2 @@ -107,10 +107,10 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect go.uber.org/mock v0.4.0 // indirect - golang.org/x/mod v0.18.0 // indirect + golang.org/x/mod v0.19.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.22.0 // indirect + golang.org/x/tools v0.23.0 // indirect ) replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297 diff --git a/go.sum b/go.sum index dce392a8..50dac8d2 100644 --- a/go.sum +++ b/go.sum @@ -26,8 +26,8 @@ github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFE github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= -github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.11.2 h1:/u628IuisSTwri5/UKloiIsH8+qF2Pu7xEQX+yIKg68= +github.com/dlclark/regexp2 v1.11.2/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8= github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I= github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g= @@ -42,8 +42,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 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.14 h1:PyEwo2Vudraa0x/Wl6eDRRW2NXBvekgfxyydcM0WGE0= -github.com/go-chi/chi/v5 v5.0.14/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= +github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= @@ -75,8 +75,8 @@ github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I= 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-20240529192340-51bc6136a0a6 h1:dh8D8FksyMhD64mRMbUhZHWYJfNoNMCxfVq6eexleMw= -github.com/insomniacslk/dhcp v0.0.0-20240529192340-51bc6136a0a6/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= +github.com/insomniacslk/dhcp v0.0.0-20240710054256-ddd8a41251c9 h1:LZJWucZz7ztCqY6Jsu7N9g124iJ2kt/O62j3+UchZFg= +github.com/insomniacslk/dhcp v0.0.0-20240710054256-ddd8a41251c9/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 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= @@ -149,8 +149,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/v3 v3.2.0 h1:9AzuUeF88YC5bK8u2vEG1Fpvu4wgpM1wfPIExfaaDxQ= -github.com/puzpuzpuz/xsync/v3 v3.2.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= +github.com/puzpuzpuz/xsync/v3 v3.4.0 h1:DuVBAdXuGFHv8adVXjWWZ63pJq+NRXOWVXlKDBZ+mJ4= +github.com/puzpuzpuz/xsync/v3 v3.4.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= 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.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= @@ -172,8 +172,8 @@ github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxe github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e h1:iGH0RMv2FzELOFNFQtvsxH7NPmlo7X5JizEK51UCojo= github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e/go.mod h1:YbL4TKHRR6APYQv3U2RGfwLDpPYSyWz6oUlpISBEzBE= -github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= -github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= +github.com/samber/lo v1.46.0 h1:w8G+oaCPgz1PoCJztqymCFaKwXt+5cCXn51uPxExFfQ= +github.com/samber/lo v1.46.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= @@ -226,18 +226,18 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= 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.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= -golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= 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.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -259,15 +259,15 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= 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= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= From 5a73d99c6f0aa1a86ab53286f48d3eead86cef4c Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Tue, 6 Aug 2024 16:30:44 +0800 Subject: [PATCH 153/311] fix: logic rules display error --- rules/provider/rule_set.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/rules/provider/rule_set.go b/rules/provider/rule_set.go index d85db805..04ab9943 100644 --- a/rules/provider/rule_set.go +++ b/rules/provider/rule_set.go @@ -40,10 +40,7 @@ func (rs *RuleSet) Adapter() string { } func (rs *RuleSet) Payload() string { - if provider, ok := rs.getProvider(); ok { - return provider.Name() - } - return "" + return rs.ruleProviderName } func (rs *RuleSet) ShouldResolveIP() bool { From beefe37260986ca6b0b688033c3784db23c2a143 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 6 Aug 2024 17:17:17 +0800 Subject: [PATCH 154/311] chore: logic rules dynamic obtain parameters --- rules/logic/logic.go | 114 +++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/rules/logic/logic.go b/rules/logic/logic.go index 8c79cab5..6e672852 100644 --- a/rules/logic/logic.go +++ b/rules/logic/logic.go @@ -4,6 +4,7 @@ import ( "fmt" "regexp" "strings" + "sync" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/rules/common" @@ -13,19 +14,19 @@ import ( type Logic struct { *common.Base - payload string - adapter string - ruleType C.RuleType - rules []C.Rule - subRules map[string][]C.Rule - needIP bool - needProcess bool + payload string + adapter string + ruleType C.RuleType + rules []C.Rule + subRules map[string][]C.Rule + + payloadOnce sync.Once } type ParseRuleFunc func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (C.Rule, error) func NewSubRule(payload, adapter string, subRules map[string][]C.Rule, parseRule ParseRuleFunc) (*Logic, error) { - logic := &Logic{Base: &common.Base{}, payload: payload, adapter: adapter, ruleType: C.SubRules} + logic := &Logic{Base: &common.Base{}, payload: payload, adapter: adapter, ruleType: C.SubRules, subRules: subRules} err := logic.parsePayload(fmt.Sprintf("(%s)", payload), parseRule) if err != nil { return nil, err @@ -34,15 +35,6 @@ func NewSubRule(payload, adapter string, subRules map[string][]C.Rule, parseRule if len(logic.rules) != 1 { return nil, fmt.Errorf("Sub-Rule rule must contain one rule") } - for _, rule := range subRules[adapter] { - if rule.ShouldResolveIP() { - logic.needIP = true - } - if rule.ShouldFindProcess() { - logic.needProcess = true - } - } - logic.subRules = subRules return logic, nil } @@ -56,9 +48,6 @@ func NewNOT(payload string, adapter string, parseRule ParseRuleFunc) (*Logic, er if len(logic.rules) != 1 { return nil, fmt.Errorf("not rule must contain one rule") } - logic.needIP = logic.rules[0].ShouldResolveIP() - logic.needProcess = logic.rules[0].ShouldFindProcess() - logic.payload = fmt.Sprintf("(!(%s,%s))", logic.rules[0].RuleType(), logic.rules[0].Payload()) return logic, nil } @@ -68,40 +57,15 @@ func NewOR(payload string, adapter string, parseRule ParseRuleFunc) (*Logic, err if err != nil { return nil, err } - - payloads := make([]string, 0, len(logic.rules)) - for _, rule := range logic.rules { - payloads = append(payloads, fmt.Sprintf("(%s,%s)", rule.RuleType().String(), rule.Payload())) - if rule.ShouldResolveIP() { - logic.needIP = true - } - if rule.ShouldFindProcess() { - logic.needProcess = true - } - } - logic.payload = fmt.Sprintf("(%s)", strings.Join(payloads, " || ")) - return logic, nil } + func NewAND(payload string, adapter string, parseRule ParseRuleFunc) (*Logic, error) { logic := &Logic{Base: &common.Base{}, payload: payload, adapter: adapter, ruleType: C.AND} err := logic.parsePayload(payload, parseRule) if err != nil { return nil, err } - - payloads := make([]string, 0, len(logic.rules)) - for _, rule := range logic.rules { - payloads = append(payloads, fmt.Sprintf("(%s,%s)", rule.RuleType().String(), rule.Payload())) - if rule.ShouldResolveIP() { - logic.needIP = true - } - if rule.ShouldFindProcess() { - logic.needProcess = true - } - } - logic.payload = fmt.Sprintf("(%s)", strings.Join(payloads, " && ")) - return logic, nil } @@ -218,13 +182,6 @@ func (logic *Logic) parsePayload(payload string, parseRule ParseRuleFunc) error return err } - if rule.ShouldResolveIP() { - logic.needIP = true - } - if rule.ShouldFindProcess() { - logic.needProcess = true - } - rules = append(rules, rule) } @@ -279,9 +236,9 @@ func (logic *Logic) Match(metadata *C.Metadata) (bool, string) { } } return true, logic.adapter + default: + return false, "" } - - return false, "" } func (logic *Logic) Adapter() string { @@ -289,15 +246,58 @@ func (logic *Logic) Adapter() string { } func (logic *Logic) Payload() string { + logic.payloadOnce.Do(func() { // a little bit expensive, so only computed once + switch logic.ruleType { + case C.NOT: + logic.payload = fmt.Sprintf("(!(%s,%s))", logic.rules[0].RuleType(), logic.rules[0].Payload()) + case C.OR: + payloads := make([]string, 0, len(logic.rules)) + for _, rule := range logic.rules { + payloads = append(payloads, fmt.Sprintf("(%s,%s)", rule.RuleType().String(), rule.Payload())) + } + logic.payload = fmt.Sprintf("(%s)", strings.Join(payloads, " || ")) + case C.AND: + payloads := make([]string, 0, len(logic.rules)) + for _, rule := range logic.rules { + payloads = append(payloads, fmt.Sprintf("(%s,%s)", rule.RuleType().String(), rule.Payload())) + } + logic.payload = fmt.Sprintf("(%s)", strings.Join(payloads, " && ")) + default: + } + }) return logic.payload } func (logic *Logic) ShouldResolveIP() bool { - return logic.needIP + if logic.ruleType == C.SubRules { + for _, rule := range logic.subRules[logic.adapter] { + if rule.ShouldResolveIP() { + return true + } + } + } + for _, rule := range logic.rules { + if rule.ShouldResolveIP() { + return true + } + } + return false } func (logic *Logic) ShouldFindProcess() bool { - return logic.needProcess + if logic.ruleType == C.SubRules { + for _, rule := range logic.subRules[logic.adapter] { + if rule.ShouldFindProcess() { + return true + } + } + } + for _, rule := range logic.rules { + if rule.ShouldFindProcess() { + return true + } + } + return false } func (logic *Logic) ProviderNames() (names []string) { From 030631607e26e7bfb3c1bc196cfb2bc46576d8f9 Mon Sep 17 00:00:00 2001 From: Chris Gardner Date: Wed, 7 Aug 2024 15:54:16 +0800 Subject: [PATCH 155/311] fix: parameter order in ChaCha20 constructor --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 57d6eea7..ca4c4bec 100644 --- a/go.mod +++ b/go.mod @@ -23,8 +23,8 @@ require ( github.com/metacubex/quic-go v0.45.1-0.20240803003931-60a15f6efd94 github.com/metacubex/randv2 v0.2.0 github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 - github.com/metacubex/sing-shadowsocks v0.2.7 - github.com/metacubex/sing-shadowsocks2 v0.2.1 + github.com/metacubex/sing-shadowsocks v0.2.8 + github.com/metacubex/sing-shadowsocks2 v0.2.2 github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a diff --git a/go.sum b/go.sum index 50dac8d2..aef02f4b 100644 --- a/go.sum +++ b/go.sum @@ -111,10 +111,10 @@ github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297 h1:YG/JkwGPbca5rUtE github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 h1:Wr4g1HCb5Z/QIFwFiVNjO2qL+dRu25+Mdn9xtAZZ+ew= github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8= -github.com/metacubex/sing-shadowsocks v0.2.7 h1:9f3Dt2+71TNp0e202llA2ug5h/rkWs2EZxQ5IMpf+9g= -github.com/metacubex/sing-shadowsocks v0.2.7/go.mod h1:X3x88XtJpBxG0W0/ECOJL6Ib0SJ3xdniAkU/6/RMWU0= -github.com/metacubex/sing-shadowsocks2 v0.2.1 h1:XIZBXlazp8EEoPp1S0DViAhLkJakjQ2f+AOwwdKKNYg= -github.com/metacubex/sing-shadowsocks2 v0.2.1/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q= +github.com/metacubex/sing-shadowsocks v0.2.8 h1:wIhlaigswzjPw4hej75sEvWte3QR0+AJRafgwBHO5B4= +github.com/metacubex/sing-shadowsocks v0.2.8/go.mod h1:X3x88XtJpBxG0W0/ECOJL6Ib0SJ3xdniAkU/6/RMWU0= +github.com/metacubex/sing-shadowsocks2 v0.2.2 h1:eaf42uVx4Lr21S6MDYs0ZdTvGA0GEhDpb9no4+gdXPo= +github.com/metacubex/sing-shadowsocks2 v0.2.2/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q= github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 h1:ypfofGDZbP8p3Y4P/m74JYu7sQViesi3c8nbmT6cS0Y= github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1/go.mod h1:olbEx9yVcaw5tHTNlRamRoxmMKcvDvcVS1YLnQGzvWE= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosqY8xKDp3pqTW3qbrCprZ1l6WkrXSFSCwyY4I= From ea4181308d4b10a2ab25c147de6ba6ce5c0f0e03 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 8 Aug 2024 07:25:16 +0800 Subject: [PATCH 156/311] chore: update quic-go to 0.46.0 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ca4c4bec..bf2302c1 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/mdlayher/netlink v1.7.2 github.com/metacubex/chacha v0.1.0 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 - github.com/metacubex/quic-go v0.45.1-0.20240803003931-60a15f6efd94 + github.com/metacubex/quic-go v0.46.1-0.20240807232329-1c6cb2d67f58 github.com/metacubex/randv2 v0.2.0 github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.8 diff --git a/go.sum b/go.sum index aef02f4b..86d905d4 100644 --- a/go.sum +++ b/go.sum @@ -103,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-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= -github.com/metacubex/quic-go v0.45.1-0.20240803003931-60a15f6efd94 h1:wlhwgxRzPLH8Ce0VME35iD2sr7jY2gFrL299/T4C2Sg= -github.com/metacubex/quic-go v0.45.1-0.20240803003931-60a15f6efd94/go.mod h1:Yza2H7Ax1rxWPUcJx0vW+oAt9EsPuSiyQFhFabUPzwU= +github.com/metacubex/quic-go v0.46.1-0.20240807232329-1c6cb2d67f58 h1:T6OxROLZBr9SOQxN5TzUslv81hEREy/dEgaUKVjaG7U= +github.com/metacubex/quic-go v0.46.1-0.20240807232329-1c6cb2d67f58/go.mod h1:Yza2H7Ax1rxWPUcJx0vW+oAt9EsPuSiyQFhFabUPzwU= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297 h1:YG/JkwGPbca5rUtEMHIu8ZuqzR7BSVm1iqY8hNoMeMA= From 4f339265d359af2cedb5015645ef865decce7108 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 8 Aug 2024 07:29:56 +0800 Subject: [PATCH 157/311] chore: update dependencies --- go.mod | 12 ++++++------ go.sum | 26 +++++++++++++------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index bf2302c1..90c25279 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/bahlo/generic-list-go v0.2.0 github.com/cilium/ebpf v0.12.3 github.com/coreos/go-iptables v0.7.0 - github.com/dlclark/regexp2 v1.11.2 + github.com/dlclark/regexp2 v1.11.4 github.com/go-chi/chi/v5 v5.1.0 github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.3 @@ -50,11 +50,11 @@ require ( gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 go.uber.org/automaxprocs v1.5.3 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba - golang.org/x/crypto v0.25.0 + golang.org/x/crypto v0.26.0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 - golang.org/x/net v0.27.0 - golang.org/x/sync v0.7.0 - golang.org/x/sys v0.22.0 + golang.org/x/net v0.28.0 + golang.org/x/sync v0.8.0 + golang.org/x/sys v0.23.0 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.3.0 @@ -108,7 +108,7 @@ require ( gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect go.uber.org/mock v0.4.0 // indirect golang.org/x/mod v0.19.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.23.0 // indirect ) diff --git a/go.sum b/go.sum index 86d905d4..d5b57053 100644 --- a/go.sum +++ b/go.sum @@ -26,8 +26,8 @@ github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFE github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dlclark/regexp2 v1.11.2 h1:/u628IuisSTwri5/UKloiIsH8+qF2Pu7xEQX+yIKg68= -github.com/dlclark/regexp2 v1.11.2/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= +github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8= github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I= github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g= @@ -226,8 +226,8 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= 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.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -236,11 +236,11 @@ golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -257,12 +257,12 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= From 951cae2156ee8f7ded6856fb783464ebccea4296 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 8 Aug 2024 13:27:06 +0800 Subject: [PATCH 158/311] chore: corrected the incoming parameters of `PowerUnregisterSuspendResumeNotification` --- component/power/event_windows.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/component/power/event_windows.go b/component/power/event_windows.go index 12655695..a9489df4 100644 --- a/component/power/event_windows.go +++ b/component/power/event_windows.go @@ -55,6 +55,11 @@ func NewEventListener(cb func(Type)) (func(), error) { } handle := uintptr(0) + // DWORD PowerRegisterSuspendResumeNotification( + // [in] DWORD Flags, + // [in] HANDLE Recipient, + // [out] PHPOWERNOTIFY RegistrationHandle + //); _, _, err := powerRegisterSuspendResumeNotification.Call( _DEVICE_NOTIFY_CALLBACK, uintptr(unsafe.Pointer(¶ms)), @@ -65,8 +70,11 @@ func NewEventListener(cb func(Type)) (func(), error) { } return func() { + // DWORD PowerUnregisterSuspendResumeNotification( + // [in, out] HPOWERNOTIFY RegistrationHandle + //); _, _, _ = powerUnregisterSuspendResumeNotification.Call( - uintptr(unsafe.Pointer(&handle)), + handle, ) runtime.KeepAlive(params) runtime.KeepAlive(handle) From c17d7c02814513022d2c8af833ae94fd64f1e74f Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Tue, 13 Aug 2024 00:57:08 +0800 Subject: [PATCH 159/311] ci: update loongarch golang and android ndk --- .github/workflows/build.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b936a57f..cd5202e1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -107,15 +107,15 @@ jobs: - name: Set up Go1.22 loongarch abi1 if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '1' }} run: | - wget -q https://github.com/xishang0128/loongarch64-golang/releases/download/1.22.0/go1.22.0.linux-amd64-abi1.tar.gz - sudo tar zxf go1.22.0.linux-amd64-abi1.tar.gz -C /usr/local + wget -q https://github.com/MetaCubeX/loongarch64-golang/releases/download/1.22.4/go1.22.4.linux-amd64-abi1.tar.gz + sudo tar zxf go1.22.4.linux-amd64-abi1.tar.gz -C /usr/local echo "/usr/local/go/bin" >> $GITHUB_PATH - name: Set up Go1.22 loongarch abi2 if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '2' }} run: | - wget -q https://github.com/xishang0128/loongarch64-golang/releases/download/1.22.0/go1.22.0.linux-amd64-abi2.tar.gz - sudo tar zxf go1.22.0.linux-amd64-abi2.tar.gz -C /usr/local + wget -q https://github.com/MetaCubeX/loongarch64-golang/releases/download/1.22.4/go1.22.4.linux-amd64-abi2.tar.gz + sudo tar zxf go1.22.4.linux-amd64-abi2.tar.gz -C /usr/local echo "/usr/local/go/bin" >> $GITHUB_PATH # modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557 @@ -162,7 +162,7 @@ jobs: uses: nttld/setup-ndk@v1 id: setup-ndk with: - ndk-version: r26c + ndk-version: r27 - name: Set NDK path if: ${{ matrix.jobs.goos == 'android' }} From 5bf22422d9653933a6dc481c6f1021f743b9772f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 13 Aug 2024 13:33:24 +0800 Subject: [PATCH 160/311] fix: wireguard not working in CMFA --- component/dialer/dialer.go | 15 ++++++++------- component/dialer/patch_android.go | 11 +++++------ component/dialer/patch_common.go | 3 +-- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 54a1aa6a..ba95c31b 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -127,10 +127,6 @@ func GetTcpConcurrent() bool { } func dialContext(ctx context.Context, network string, destination netip.Addr, port string, opt *option) (net.Conn, error) { - if features.CMFA && DefaultSocketHook != nil { - return dialContextHooked(ctx, network, destination, port) - } - var address string if IP4PEnable { destination, port = lookupIP4P(destination, port) @@ -149,6 +145,14 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po } dialer := netDialer.(*net.Dialer) + if opt.mpTcp { + setMultiPathTCP(dialer) + } + + if features.CMFA && DefaultSocketHook != nil { // ignore interfaceName, routingMark and tfo in CMFA + return dialContextHooked(ctx, dialer, network, address) + } + if opt.interfaceName != "" { bind := bindIfaceToDialer if opt.fallbackBind { @@ -161,9 +165,6 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po if opt.routingMark != 0 { bindMarkToDialer(opt.routingMark, dialer, network, destination) } - if opt.mpTcp { - setMultiPathTCP(dialer) - } if opt.tfo && !DisableTFO { return dialTFO(ctx, *dialer, network, address) } diff --git a/component/dialer/patch_android.go b/component/dialer/patch_android.go index 7c33a6c0..079b9772 100644 --- a/component/dialer/patch_android.go +++ b/component/dialer/patch_android.go @@ -5,7 +5,6 @@ package dialer import ( "context" "net" - "net/netip" "syscall" ) @@ -13,12 +12,12 @@ type SocketControl func(network, address string, conn syscall.RawConn) error var DefaultSocketHook SocketControl -func dialContextHooked(ctx context.Context, network string, destination netip.Addr, port string) (net.Conn, error) { - dialer := &net.Dialer{ - Control: DefaultSocketHook, - } +func dialContextHooked(ctx context.Context, dialer *net.Dialer, network string, address string) (net.Conn, error) { + addControlToDialer(dialer, func(ctx context.Context, network, address string, c syscall.RawConn) error { + return DefaultSocketHook(network, address, c) + }) - conn, err := dialer.DialContext(ctx, network, net.JoinHostPort(destination.String(), port)) + conn, err := dialer.DialContext(ctx, network, address) if err != nil { return nil, err } diff --git a/component/dialer/patch_common.go b/component/dialer/patch_common.go index bad0ef48..2c96fe60 100644 --- a/component/dialer/patch_common.go +++ b/component/dialer/patch_common.go @@ -5,7 +5,6 @@ package dialer import ( "context" "net" - "net/netip" "syscall" ) @@ -13,7 +12,7 @@ type SocketControl func(network, address string, conn syscall.RawConn) error var DefaultSocketHook SocketControl -func dialContextHooked(ctx context.Context, network string, destination netip.Addr, port string) (net.Conn, error) { +func dialContextHooked(ctx context.Context, dialer *net.Dialer, network string, address string) (net.Conn, error) { return nil, nil } From 50d0cd363c84c14a4982731d24082e0ee65265f0 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Tue, 13 Aug 2024 14:19:34 +0800 Subject: [PATCH 161/311] chore: auto download external UI when 'external-ui' is set and not empty --- component/updater/update_core.go | 2 +- component/updater/update_ui.go | 8 +------- config/config.go | 5 +++++ hub/executor/executor.go | 14 ++++++++++++++ hub/route/configs.go | 4 ++-- 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/component/updater/update_core.go b/component/updater/update_core.go index 0070fbb1..9f13af9c 100644 --- a/component/updater/update_core.go +++ b/component/updater/update_core.go @@ -230,7 +230,7 @@ func clean() { // MaxPackageFileSize is a maximum package file length in bytes. The largest // package whose size is limited by this constant currently has the size of -// approximately 9 MiB. +// approximately 32 MiB. const MaxPackageFileSize = 32 * 1024 * 1024 // Download package file and save it to disk diff --git a/component/updater/update_ui.go b/component/updater/update_ui.go index 85452ba5..a43648a9 100644 --- a/component/updater/update_ui.go +++ b/component/updater/update_ui.go @@ -29,11 +29,6 @@ func UpdateUI() error { xdMutex.Lock() defer xdMutex.Unlock() - err := prepare_ui() - if err != nil { - return err - } - data, err := downloadForBytes(ExternalUIURL) if err != nil { return fmt.Errorf("can't download file: %w", err) @@ -64,7 +59,7 @@ func UpdateUI() error { return nil } -func prepare_ui() error { +func PrepareUIPath() error { if ExternalUIPath == "" || ExternalUIURL == "" { return ErrIncompleteConf } @@ -79,7 +74,6 @@ func prepare_ui() error { } else { ExternalUIFolder = ExternalUIPath } - return nil } diff --git a/config/config.go b/config/config.go index ecd94c7d..5f2b6845 100644 --- a/config/config.go +++ b/config/config.go @@ -677,6 +677,11 @@ func parseGeneral(cfg *RawConfig) (*General, error) { updater.ExternalUIURL = cfg.ExternalUIURL } + err := updater.PrepareUIPath() + if err != nil { + log.Errorln("PrepareUIPath error: %s", err) + } + return &General{ Inbound: Inbound{ Port: cfg.Port, diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 55c40b6d..1e578190 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -23,6 +23,7 @@ import ( "github.com/metacubex/mihomo/component/resolver" SNI "github.com/metacubex/mihomo/component/sniffer" "github.com/metacubex/mihomo/component/trie" + "github.com/metacubex/mihomo/component/updater" "github.com/metacubex/mihomo/config" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/constant/features" @@ -113,6 +114,7 @@ func ApplyConfig(cfg *config.Config, force bool) { runtime.GC() tunnel.OnRunning() hcCompatibleProvider(cfg.Providers) + initExternalUI() log.SetLevel(cfg.General.LogLevel) } @@ -385,6 +387,18 @@ func updateTunnels(tunnels []LC.Tunnel) { listener.PatchTunnel(tunnels, tunnel.Tunnel) } +func initExternalUI() { + if updater.ExternalUIFolder != "" { + dirEntries, _ := os.ReadDir(updater.ExternalUIFolder) + if len(dirEntries) > 0 { + log.Infoln("UI already exists") + } else { + log.Infoln("UI not exists, downloading") + updater.UpdateUI() + } + } +} + func updateGeneral(general *config.General) { tunnel.SetMode(general.Mode) tunnel.SetFindProcessMode(general.FindProcessMode) diff --git a/hub/route/configs.go b/hub/route/configs.go index 17d858d4..a4dcaa52 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -402,7 +402,7 @@ func updateConfigs(w http.ResponseWriter, r *http.Request) { func updateGeoDatabases(w http.ResponseWriter, r *http.Request) { err := updater.UpdateGeoDatabases() if err != nil { - log.Errorln("[REST-API] update GEO databases failed: %v", err) + log.Errorln("[GEO] update GEO databases failed: %v", err) render.Status(r, http.StatusInternalServerError) render.JSON(w, r, newError(err.Error())) return @@ -410,7 +410,7 @@ func updateGeoDatabases(w http.ResponseWriter, r *http.Request) { cfg, err := executor.ParseWithPath(C.Path.Config()) if err != nil { - log.Errorln("[REST-API] update GEO databases failed: %v", err) + log.Errorln("[GEO] update GEO databases failed: %v", err) render.Status(r, http.StatusInternalServerError) render.JSON(w, r, newError("Error parsing configuration")) return From 12c5cf361ded7a6a9b03e6560b17f0f35a3fde4a Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 14 Aug 2024 09:03:29 +0800 Subject: [PATCH 162/311] chore: update golang to 1.23 --- ...42aa09c2f878c4faa576948b07fe625c4707a.diff | 54 ------ ...def151adff1af707d82d28f55dba81ceb08e1.diff | 158 ----------------- ...157f9544922e96945196b47b95664b1e39108.diff | 162 ------------------ .github/workflows/build.yml | 23 ++- 4 files changed, 18 insertions(+), 379 deletions(-) delete mode 100644 .github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff delete mode 100644 .github/patch_go122/693def151adff1af707d82d28f55dba81ceb08e1.diff delete mode 100644 .github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff diff --git a/.github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff b/.github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff deleted file mode 100644 index 2c682333..00000000 --- a/.github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff +++ /dev/null @@ -1,54 +0,0 @@ -diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go -index 06e684c7116b4..b311a5c74684b 100644 ---- a/src/syscall/exec_windows.go -+++ b/src/syscall/exec_windows.go -@@ -319,17 +319,6 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle - } - } - -- var maj, min, build uint32 -- rtlGetNtVersionNumbers(&maj, &min, &build) -- isWin7 := maj < 6 || (maj == 6 && min <= 1) -- // NT kernel handles are divisible by 4, with the bottom 3 bits left as -- // a tag. The fully set tag correlates with the types of handles we're -- // concerned about here. Except, the kernel will interpret some -- // special handle values, like -1, -2, and so forth, so kernelbase.dll -- // checks to see that those bottom three bits are checked, but that top -- // bit is not checked. -- isLegacyWin7ConsoleHandle := func(handle Handle) bool { return isWin7 && handle&0x10000003 == 3 } -- - p, _ := GetCurrentProcess() - parentProcess := p - if sys.ParentProcess != 0 { -@@ -338,15 +327,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle - fd := make([]Handle, len(attr.Files)) - for i := range attr.Files { - if attr.Files[i] > 0 { -- destinationProcessHandle := parentProcess -- -- // On Windows 7, console handles aren't real handles, and can only be duplicated -- // into the current process, not a parent one, which amounts to the same thing. -- if parentProcess != p && isLegacyWin7ConsoleHandle(Handle(attr.Files[i])) { -- destinationProcessHandle = p -- } -- -- err := DuplicateHandle(p, Handle(attr.Files[i]), destinationProcessHandle, &fd[i], 0, true, DUPLICATE_SAME_ACCESS) -+ err := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS) - if err != nil { - return 0, 0, err - } -@@ -377,14 +358,6 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle - - fd = append(fd, sys.AdditionalInheritedHandles...) - -- // On Windows 7, console handles aren't real handles, so don't pass them -- // through to PROC_THREAD_ATTRIBUTE_HANDLE_LIST. -- for i := range fd { -- if isLegacyWin7ConsoleHandle(fd[i]) { -- fd[i] = 0 -- } -- } -- - // The presence of a NULL handle in the list is enough to cause PROC_THREAD_ATTRIBUTE_HANDLE_LIST - // to treat the entire list as empty, so remove NULL handles. - j := 0 \ No newline at end of file diff --git a/.github/patch_go122/693def151adff1af707d82d28f55dba81ceb08e1.diff b/.github/patch_go122/693def151adff1af707d82d28f55dba81ceb08e1.diff deleted file mode 100644 index ca41ec31..00000000 --- a/.github/patch_go122/693def151adff1af707d82d28f55dba81ceb08e1.diff +++ /dev/null @@ -1,158 +0,0 @@ -diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go -index 62738e2cb1a7d..d0dcc7cc71fc0 100644 ---- a/src/crypto/rand/rand.go -+++ b/src/crypto/rand/rand.go -@@ -15,7 +15,7 @@ import "io" - // available, /dev/urandom otherwise. - // On OpenBSD and macOS, Reader uses getentropy(2). - // On other Unix-like systems, Reader reads from /dev/urandom. --// On Windows systems, Reader uses the RtlGenRandom API. -+// On Windows systems, Reader uses the ProcessPrng API. - // On JS/Wasm, Reader uses the Web Crypto API. - // On WASIP1/Wasm, Reader uses random_get from wasi_snapshot_preview1. - var Reader io.Reader -diff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go -index 6c0655c72b692..7380f1f0f1e6e 100644 ---- a/src/crypto/rand/rand_windows.go -+++ b/src/crypto/rand/rand_windows.go -@@ -15,11 +15,8 @@ func init() { Reader = &rngReader{} } - - type rngReader struct{} - --func (r *rngReader) Read(b []byte) (n int, err error) { -- // RtlGenRandom only returns 1<<32-1 bytes at a time. We only read at -- // most 1<<31-1 bytes at a time so that this works the same on 32-bit -- // and 64-bit systems. -- if err := batched(windows.RtlGenRandom, 1<<31-1)(b); err != nil { -+func (r *rngReader) Read(b []byte) (int, error) { -+ if err := windows.ProcessPrng(b); err != nil { - return 0, err - } - return len(b), nil -diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go -index ab4ad2ec64108..5854ca60b5cef 100644 ---- a/src/internal/syscall/windows/syscall_windows.go -+++ b/src/internal/syscall/windows/syscall_windows.go -@@ -373,7 +373,7 @@ func ErrorLoadingGetTempPath2() error { - //sys DestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock - //sys CreateEvent(eventAttrs *SecurityAttributes, manualReset uint32, initialState uint32, name *uint16) (handle syscall.Handle, err error) = kernel32.CreateEventW - --//sys RtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036 -+//sys ProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng - - type FILE_ID_BOTH_DIR_INFO struct { - NextEntryOffset uint32 -diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go -index e3f6d8d2a2208..5a587ad4f146c 100644 ---- a/src/internal/syscall/windows/zsyscall_windows.go -+++ b/src/internal/syscall/windows/zsyscall_windows.go -@@ -37,13 +37,14 @@ func errnoErr(e syscall.Errno) error { - } - - var ( -- modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll")) -- modiphlpapi = syscall.NewLazyDLL(sysdll.Add("iphlpapi.dll")) -- modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll")) -- modnetapi32 = syscall.NewLazyDLL(sysdll.Add("netapi32.dll")) -- modpsapi = syscall.NewLazyDLL(sysdll.Add("psapi.dll")) -- moduserenv = syscall.NewLazyDLL(sysdll.Add("userenv.dll")) -- modws2_32 = syscall.NewLazyDLL(sysdll.Add("ws2_32.dll")) -+ modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll")) -+ modbcryptprimitives = syscall.NewLazyDLL(sysdll.Add("bcryptprimitives.dll")) -+ modiphlpapi = syscall.NewLazyDLL(sysdll.Add("iphlpapi.dll")) -+ modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll")) -+ modnetapi32 = syscall.NewLazyDLL(sysdll.Add("netapi32.dll")) -+ modpsapi = syscall.NewLazyDLL(sysdll.Add("psapi.dll")) -+ moduserenv = syscall.NewLazyDLL(sysdll.Add("userenv.dll")) -+ modws2_32 = syscall.NewLazyDLL(sysdll.Add("ws2_32.dll")) - - procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges") - procDuplicateTokenEx = modadvapi32.NewProc("DuplicateTokenEx") -@@ -55,7 +56,7 @@ var ( - procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus") - procRevertToSelf = modadvapi32.NewProc("RevertToSelf") - procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation") -- procSystemFunction036 = modadvapi32.NewProc("SystemFunction036") -+ procProcessPrng = modbcryptprimitives.NewProc("ProcessPrng") - procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") - procCreateEventW = modkernel32.NewProc("CreateEventW") - procGetACP = modkernel32.NewProc("GetACP") -@@ -179,12 +180,12 @@ func SetTokenInformation(tokenHandle syscall.Token, tokenInformationClass uint32 - return - } - --func RtlGenRandom(buf []byte) (err error) { -+func ProcessPrng(buf []byte) (err error) { - var _p0 *byte - if len(buf) > 0 { - _p0 = &buf[0] - } -- r1, _, e1 := syscall.Syscall(procSystemFunction036.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0) -+ r1, _, e1 := syscall.Syscall(procProcessPrng.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0) - if r1 == 0 { - err = errnoErr(e1) - } -diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go -index 8ca8d7790909e..3772a864b2ff4 100644 ---- a/src/runtime/os_windows.go -+++ b/src/runtime/os_windows.go -@@ -127,15 +127,8 @@ var ( - _WriteFile, - _ stdFunction - -- // Use RtlGenRandom to generate cryptographically random data. -- // This approach has been recommended by Microsoft (see issue -- // 15589 for details). -- // The RtlGenRandom is not listed in advapi32.dll, instead -- // RtlGenRandom function can be found by searching for SystemFunction036. -- // Also some versions of Mingw cannot link to SystemFunction036 -- // when building executable as Cgo. So load SystemFunction036 -- // manually during runtime startup. -- _RtlGenRandom stdFunction -+ // Use ProcessPrng to generate cryptographically random data. -+ _ProcessPrng stdFunction - - // Load ntdll.dll manually during startup, otherwise Mingw - // links wrong printf function to cgo executable (see issue -@@ -151,11 +144,11 @@ var ( - ) - - var ( -- advapi32dll = [...]uint16{'a', 'd', 'v', 'a', 'p', 'i', '3', '2', '.', 'd', 'l', 'l', 0} -- ntdlldll = [...]uint16{'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l', 0} -- powrprofdll = [...]uint16{'p', 'o', 'w', 'r', 'p', 'r', 'o', 'f', '.', 'd', 'l', 'l', 0} -- winmmdll = [...]uint16{'w', 'i', 'n', 'm', 'm', '.', 'd', 'l', 'l', 0} -- ws2_32dll = [...]uint16{'w', 's', '2', '_', '3', '2', '.', 'd', 'l', 'l', 0} -+ bcryptprimitivesdll = [...]uint16{'b', 'c', 'r', 'y', 'p', 't', 'p', 'r', 'i', 'm', 'i', 't', 'i', 'v', 'e', 's', '.', 'd', 'l', 'l', 0} -+ ntdlldll = [...]uint16{'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l', 0} -+ powrprofdll = [...]uint16{'p', 'o', 'w', 'r', 'p', 'r', 'o', 'f', '.', 'd', 'l', 'l', 0} -+ winmmdll = [...]uint16{'w', 'i', 'n', 'm', 'm', '.', 'd', 'l', 'l', 0} -+ ws2_32dll = [...]uint16{'w', 's', '2', '_', '3', '2', '.', 'd', 'l', 'l', 0} - ) - - // Function to be called by windows CreateThread -@@ -251,11 +244,11 @@ func windowsLoadSystemLib(name []uint16) uintptr { - } - - func loadOptionalSyscalls() { -- a32 := windowsLoadSystemLib(advapi32dll[:]) -- if a32 == 0 { -- throw("advapi32.dll not found") -+ bcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:]) -+ if bcryptPrimitives == 0 { -+ throw("bcryptprimitives.dll not found") - } -- _RtlGenRandom = windowsFindfunc(a32, []byte("SystemFunction036\000")) -+ _ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte("ProcessPrng\000")) - - n32 := windowsLoadSystemLib(ntdlldll[:]) - if n32 == 0 { -@@ -531,7 +524,7 @@ func osinit() { - //go:nosplit - func readRandom(r []byte) int { - n := 0 -- if stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { -+ if stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { - n = len(r) - } - return n \ No newline at end of file diff --git a/.github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff b/.github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff deleted file mode 100644 index c1fc5f6d..00000000 --- a/.github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff +++ /dev/null @@ -1,162 +0,0 @@ -diff --git a/src/net/hook_windows.go b/src/net/hook_windows.go -index ab8656cbbf343..28c49cc6de7e7 100644 ---- a/src/net/hook_windows.go -+++ b/src/net/hook_windows.go -@@ -14,7 +14,6 @@ var ( - testHookDialChannel = func() { time.Sleep(time.Millisecond) } // see golang.org/issue/5349 - - // Placeholders for socket system calls. -- socketFunc func(int, int, int) (syscall.Handle, error) = syscall.Socket - wsaSocketFunc func(int32, int32, int32, *syscall.WSAProtocolInfo, uint32, uint32) (syscall.Handle, error) = windows.WSASocket - connectFunc func(syscall.Handle, syscall.Sockaddr) error = syscall.Connect - listenFunc func(syscall.Handle, int) error = syscall.Listen -diff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go -index 0197feb3f199a..967ce6795aedb 100644 ---- a/src/net/internal/socktest/main_test.go -+++ b/src/net/internal/socktest/main_test.go -@@ -2,7 +2,7 @@ - // Use of this source code is governed by a BSD-style - // license that can be found in the LICENSE file. - --//go:build !js && !plan9 && !wasip1 -+//go:build !js && !plan9 && !wasip1 && !windows - - package socktest_test - -diff --git a/src/net/internal/socktest/main_windows_test.go b/src/net/internal/socktest/main_windows_test.go -deleted file mode 100644 -index df1cb97784b51..0000000000000 ---- a/src/net/internal/socktest/main_windows_test.go -+++ /dev/null -@@ -1,22 +0,0 @@ --// Copyright 2015 The Go Authors. All rights reserved. --// Use of this source code is governed by a BSD-style --// license that can be found in the LICENSE file. -- --package socktest_test -- --import "syscall" -- --var ( -- socketFunc func(int, int, int) (syscall.Handle, error) -- closeFunc func(syscall.Handle) error --) -- --func installTestHooks() { -- socketFunc = sw.Socket -- closeFunc = sw.Closesocket --} -- --func uninstallTestHooks() { -- socketFunc = syscall.Socket -- closeFunc = syscall.Closesocket --} -diff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go -index 8c1c862f33c9b..1c42e5c7f34b7 100644 ---- a/src/net/internal/socktest/sys_windows.go -+++ b/src/net/internal/socktest/sys_windows.go -@@ -9,38 +9,6 @@ import ( - "syscall" - ) - --// Socket wraps syscall.Socket. --func (sw *Switch) Socket(family, sotype, proto int) (s syscall.Handle, err error) { -- sw.once.Do(sw.init) -- -- so := &Status{Cookie: cookie(family, sotype, proto)} -- sw.fmu.RLock() -- f, _ := sw.fltab[FilterSocket] -- sw.fmu.RUnlock() -- -- af, err := f.apply(so) -- if err != nil { -- return syscall.InvalidHandle, err -- } -- s, so.Err = syscall.Socket(family, sotype, proto) -- if err = af.apply(so); err != nil { -- if so.Err == nil { -- syscall.Closesocket(s) -- } -- return syscall.InvalidHandle, err -- } -- -- sw.smu.Lock() -- defer sw.smu.Unlock() -- if so.Err != nil { -- sw.stats.getLocked(so.Cookie).OpenFailed++ -- return syscall.InvalidHandle, so.Err -- } -- nso := sw.addLocked(s, family, sotype, proto) -- sw.stats.getLocked(nso.Cookie).Opened++ -- return s, nil --} -- - // WSASocket wraps [syscall.WSASocket]. - func (sw *Switch) WSASocket(family, sotype, proto int32, protinfo *syscall.WSAProtocolInfo, group uint32, flags uint32) (s syscall.Handle, err error) { - sw.once.Do(sw.init) -diff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go -index 07f21b72eb1fc..bc024c0bbd82d 100644 ---- a/src/net/main_windows_test.go -+++ b/src/net/main_windows_test.go -@@ -8,7 +8,6 @@ import "internal/poll" - - var ( - // Placeholders for saving original socket system calls. -- origSocket = socketFunc - origWSASocket = wsaSocketFunc - origClosesocket = poll.CloseFunc - origConnect = connectFunc -@@ -18,7 +17,6 @@ var ( - ) - - func installTestHooks() { -- socketFunc = sw.Socket - wsaSocketFunc = sw.WSASocket - poll.CloseFunc = sw.Closesocket - connectFunc = sw.Connect -@@ -28,7 +26,6 @@ func installTestHooks() { - } - - func uninstallTestHooks() { -- socketFunc = origSocket - wsaSocketFunc = origWSASocket - poll.CloseFunc = origClosesocket - connectFunc = origConnect -diff --git a/src/net/sock_windows.go b/src/net/sock_windows.go -index fa11c7af2e727..5540135a2c43e 100644 ---- a/src/net/sock_windows.go -+++ b/src/net/sock_windows.go -@@ -19,21 +19,6 @@ func maxListenerBacklog() int { - func sysSocket(family, sotype, proto int) (syscall.Handle, error) { - s, err := wsaSocketFunc(int32(family), int32(sotype), int32(proto), - nil, 0, windows.WSA_FLAG_OVERLAPPED|windows.WSA_FLAG_NO_HANDLE_INHERIT) -- if err == nil { -- return s, nil -- } -- // WSA_FLAG_NO_HANDLE_INHERIT flag is not supported on some -- // old versions of Windows, see -- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms742212(v=vs.85).aspx -- // for details. Just use syscall.Socket, if windows.WSASocket failed. -- -- // See ../syscall/exec_unix.go for description of ForkLock. -- syscall.ForkLock.RLock() -- s, err = socketFunc(family, sotype, proto) -- if err == nil { -- syscall.CloseOnExec(s) -- } -- syscall.ForkLock.RUnlock() - if err != nil { - return syscall.InvalidHandle, os.NewSyscallError("socket", err) - } -diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go -index 0a93bc0a80d4e..06e684c7116b4 100644 ---- a/src/syscall/exec_windows.go -+++ b/src/syscall/exec_windows.go -@@ -14,6 +14,7 @@ import ( - "unsafe" - ) - -+// ForkLock is not used on Windows. - var ForkLock sync.RWMutex - - // EscapeArg rewrites command line argument s as prescribed \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cd5202e1..1255fc22 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -67,6 +67,12 @@ jobs: - { goos: android, goarch: arm, ndk: armv7a-linux-androideabi34, output: armv7 } - { goos: android, goarch: arm64, ndk: aarch64-linux-android34, output: arm64-v8 } + # Go 1.22 with special patch can work on Windows 7 + # https://github.com/MetaCubeX/go/commits/release-branch.go1.22/ + - { goos: windows, goarch: '386', output: '386-go122', goversion: '1.22' } + - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible-go122, goversion: '1.22' } + - { goos: windows, goarch: amd64, goamd64: v3, output: amd64-go122, goversion: '1.22' } + # Go 1.21 can revert commit `9e4385` to work on Windows 7 # https://github.com/golang/go/issues/64622#issuecomment-1847475161 # (OR we can just use golang1.21.4 which unneeded any patch) @@ -79,6 +85,11 @@ jobs: - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, goversion: '1.20' } - { goos: windows, goarch: amd64, goamd64: v3, output: amd64-go120, goversion: '1.20' } + # Go 1.22 is the last release that will run on macOS 10.15 Catalina. Go 1.23 will require macOS 11 Big Sur or later. + - { goos: darwin, goarch: arm64, output: arm64-go122, goversion: '1.22' } + - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-compatible-go122, goversion: '1.22' } + - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64-go122, goversion: '1.22' } + # Go 1.20 is the last release that will run on macOS 10.13 High Sierra or 10.14 Mojave. Go 1.21 will require macOS 10.15 Catalina or later. - { goos: darwin, goarch: arm64, output: arm64-go120, goversion: '1.20' } - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, goversion: '1.20' } @@ -96,7 +107,7 @@ jobs: if: ${{ matrix.jobs.goversion == '' && matrix.jobs.goarch != 'loong64' }} uses: actions/setup-go@v5 with: - go-version: '1.22' + go-version: '1.23' - name: Set up Go if: ${{ matrix.jobs.goversion != '' && matrix.jobs.goarch != 'loong64' }} @@ -125,13 +136,15 @@ jobs: # 693def151adff1af707d82d28f55dba81ceb08e1: "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng" # 7c1157f9544922e96945196b47b95664b1e39108: "net: remove sysSocket fallback for Windows 7" # 48042aa09c2f878c4faa576948b07fe625c4707a: "syscall: remove Windows 7 console handle workaround" + # a17d959debdb04cd550016a3501dd09d50cd62e7: "runtime: always use LoadLibraryEx to load system libraries" - name: Revert Golang1.22 commit for Windows7/8 - if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '' }} + if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '1.22' }} run: | cd $(go env GOROOT) - patch --verbose -R -p 1 < $GITHUB_WORKSPACE/.github/patch_go122/693def151adff1af707d82d28f55dba81ceb08e1.diff - patch --verbose -R -p 1 < $GITHUB_WORKSPACE/.github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff - patch --verbose -R -p 1 < $GITHUB_WORKSPACE/.github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff + curl https://github.com/MetaCubeX/go/commit/9779155f18b6556a034f7bb79fb7fb2aad1e26a9.diff | patch --verbose -p 1 + curl https://github.com/MetaCubeX/go/commit/ef0606261340e608017860b423ffae5c1ce78239.diff | patch --verbose -p 1 + curl https://github.com/MetaCubeX/go/commit/7f83badcb925a7e743188041cb6e561fc9b5b642.diff | patch --verbose -p 1 + curl https://github.com/MetaCubeX/go/commit/83ff9782e024cb328b690cbf0da4e7848a327f4f.diff | patch --verbose -p 1 # modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557 - name: Revert Golang1.21 commit for Windows7/8 From acaacd8ab1405232e36054fa11933731430cf4d0 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 14 Aug 2024 10:13:57 +0800 Subject: [PATCH 163/311] action: let golang1.23's build can work on windows7/8 --- .github/workflows/build.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1255fc22..fb3eb621 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -129,9 +129,28 @@ jobs: sudo tar zxf go1.22.4.linux-amd64-abi2.tar.gz -C /usr/local echo "/usr/local/go/bin" >> $GITHUB_PATH + # modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557 + # this patch file only works on golang1.23.x + # that means after golang1.24 release it must be changed + # see: https://github.com/MetaCubeX/go/commits/release-branch.go1.23/ + # revert: + # 693def151adff1af707d82d28f55dba81ceb08e1: "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng" + # 7c1157f9544922e96945196b47b95664b1e39108: "net: remove sysSocket fallback for Windows 7" + # 48042aa09c2f878c4faa576948b07fe625c4707a: "syscall: remove Windows 7 console handle workaround" + # a17d959debdb04cd550016a3501dd09d50cd62e7: "runtime: always use LoadLibraryEx to load system libraries" + - name: Revert Golang1.23 commit for Windows7/8 + if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '' }} + run: | + cd $(go env GOROOT) + curl https://github.com/MetaCubeX/go/commit/9ac42137ef6730e8b7daca016ece831297a1d75b.diff | patch --verbose -p 1 + curl https://github.com/MetaCubeX/go/commit/21290de8a4c91408de7c2b5b68757b1e90af49dd.diff | patch --verbose -p 1 + curl https://github.com/MetaCubeX/go/commit/6a31d3fa8e47ddabc10bd97bff10d9a85f4cfb76.diff | patch --verbose -p 1 + curl https://github.com/MetaCubeX/go/commit/69e2eed6dd0f6d815ebf15797761c13f31213dd6.diff | patch --verbose -p 1 + # modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557 # this patch file only works on golang1.22.x # that means after golang1.23 release it must be changed + # see: https://github.com/MetaCubeX/go/commits/release-branch.go1.22/ # revert: # 693def151adff1af707d82d28f55dba81ceb08e1: "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng" # 7c1157f9544922e96945196b47b95664b1e39108: "net: remove sysSocket fallback for Windows 7" From 24c6e7d8193a5cc9a3937393a7d42c3cf36e626e Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Wed, 14 Aug 2024 11:51:39 +0800 Subject: [PATCH 164/311] chore: update tcp keepAlive setting for go1.23 --- common/net/tcp_keepalive_go122.go | 12 ++++++++++++ common/net/tcp_keepalive_go123.go | 15 +++++++++++++++ common/net/tcpip.go | 12 ++++-------- config/config.go | 5 +++++ docs/config.yaml | 3 ++- 5 files changed, 38 insertions(+), 9 deletions(-) create mode 100644 common/net/tcp_keepalive_go122.go create mode 100644 common/net/tcp_keepalive_go123.go diff --git a/common/net/tcp_keepalive_go122.go b/common/net/tcp_keepalive_go122.go new file mode 100644 index 00000000..09970157 --- /dev/null +++ b/common/net/tcp_keepalive_go122.go @@ -0,0 +1,12 @@ +//go:build !go1.23 + +package net + +import "net" + +func TCPKeepAlive(c net.Conn) { + if tcp, ok := c.(*net.TCPConn); ok { + _ = tcp.SetKeepAlive(true) + _ = tcp.SetKeepAlivePeriod(KeepAliveInterval) + } +} diff --git a/common/net/tcp_keepalive_go123.go b/common/net/tcp_keepalive_go123.go new file mode 100644 index 00000000..f5bca964 --- /dev/null +++ b/common/net/tcp_keepalive_go123.go @@ -0,0 +1,15 @@ +//go:build go1.23 + +package net + +import "net" + +func TCPKeepAlive(c net.Conn) { + if tcp, ok := c.(*net.TCPConn); ok { + _ = tcp.SetKeepAliveConfig(net.KeepAliveConfig{ + Enable: true, + Idle: KeepAliveIdle, + Interval: KeepAliveInterval, + }) + } +} diff --git a/common/net/tcpip.go b/common/net/tcpip.go index 0499e54c..4f4fa661 100644 --- a/common/net/tcpip.go +++ b/common/net/tcpip.go @@ -7,7 +7,10 @@ import ( "time" ) -var KeepAliveInterval = 15 * time.Second +var ( + KeepAliveIdle = 0 * time.Second + KeepAliveInterval = 0 * time.Second +) func SplitNetworkType(s string) (string, string, error) { var ( @@ -47,10 +50,3 @@ 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) - } -} diff --git a/config/config.go b/config/config.go index 5f2b6845..3b13146d 100644 --- a/config/config.go +++ b/config/config.go @@ -338,6 +338,7 @@ type RawConfig struct { FindProcessMode P.FindProcessMode `yaml:"find-process-mode" json:"find-process-mode"` GlobalClientFingerprint string `yaml:"global-client-fingerprint"` GlobalUA string `yaml:"global-ua"` + KeepAliveIdle int `yaml:"keep-alive-idle"` KeepAliveInterval int `yaml:"keep-alive-interval"` Sniffer RawSniffer `yaml:"sniffer" json:"sniffer"` @@ -649,6 +650,10 @@ func parseGeneral(cfg *RawConfig) (*General, error) { C.ASNUrl = cfg.GeoXUrl.ASN C.GeodataMode = cfg.GeodataMode C.UA = cfg.GlobalUA + + if cfg.KeepAliveIdle != 0 { + N.KeepAliveIdle = time.Duration(cfg.KeepAliveIdle) * time.Second + } if cfg.KeepAliveInterval != 0 { N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second } diff --git a/docs/config.yaml b/docs/config.yaml index d7c686d0..6feca27d 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -82,7 +82,8 @@ external-doh-server: /dns-query global-client-fingerprint: chrome # TCP keep alive interval -keep-alive-interval: 15 +# keep-alive-idle: 7200 +# keep-alive-interval: 75 # routing-mark:6666 # 配置 fwmark 仅用于 Linux experimental: From f20f371a612a6cb62fb1045fc63732b503e30071 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 14 Aug 2024 13:01:06 +0800 Subject: [PATCH 165/311] chore: better keepalive handle --- common/net/tcp_keepalive.go | 23 ++++++++ common/net/tcp_keepalive_go122.go | 8 +-- common/net/tcp_keepalive_go123.go | 18 +++--- common/net/tcp_keepalive_go123_unix.go | 15 +++++ common/net/tcp_keepalive_go123_windows.go | 63 ++++++++++++++++++++ common/net/tcpip.go | 6 -- component/dialer/dialer.go | 70 +++++++++++------------ component/dialer/patch_android.go | 38 ------------ component/dialer/patch_common.go | 21 ------- component/dialer/socket_hook.go | 27 +++++++++ config/config.go | 2 + docs/config.yaml | 5 +- listener/http/server.go | 8 +-- 13 files changed, 184 insertions(+), 120 deletions(-) create mode 100644 common/net/tcp_keepalive.go create mode 100644 common/net/tcp_keepalive_go123_unix.go create mode 100644 common/net/tcp_keepalive_go123_windows.go delete mode 100644 component/dialer/patch_android.go delete mode 100644 component/dialer/patch_common.go create mode 100644 component/dialer/socket_hook.go diff --git a/common/net/tcp_keepalive.go b/common/net/tcp_keepalive.go new file mode 100644 index 00000000..047a1c05 --- /dev/null +++ b/common/net/tcp_keepalive.go @@ -0,0 +1,23 @@ +package net + +import ( + "net" + "runtime" + "time" +) + +var ( + KeepAliveIdle = 0 * time.Second + KeepAliveInterval = 0 * time.Second + DisableKeepAlive = false +) + +func TCPKeepAlive(c net.Conn) { + if tcp, ok := c.(*net.TCPConn); ok { + if runtime.GOOS == "android" || DisableKeepAlive { + _ = tcp.SetKeepAlive(false) + } else { + tcpKeepAlive(tcp) + } + } +} diff --git a/common/net/tcp_keepalive_go122.go b/common/net/tcp_keepalive_go122.go index 09970157..12873168 100644 --- a/common/net/tcp_keepalive_go122.go +++ b/common/net/tcp_keepalive_go122.go @@ -4,9 +4,7 @@ package net import "net" -func TCPKeepAlive(c net.Conn) { - if tcp, ok := c.(*net.TCPConn); ok { - _ = tcp.SetKeepAlive(true) - _ = tcp.SetKeepAlivePeriod(KeepAliveInterval) - } +func tcpKeepAlive(tcp *net.TCPConn) { + _ = tcp.SetKeepAlive(true) + _ = tcp.SetKeepAlivePeriod(KeepAliveInterval) } diff --git a/common/net/tcp_keepalive_go123.go b/common/net/tcp_keepalive_go123.go index f5bca964..2dd4754b 100644 --- a/common/net/tcp_keepalive_go123.go +++ b/common/net/tcp_keepalive_go123.go @@ -4,12 +4,16 @@ package net import "net" -func TCPKeepAlive(c net.Conn) { - if tcp, ok := c.(*net.TCPConn); ok { - _ = tcp.SetKeepAliveConfig(net.KeepAliveConfig{ - Enable: true, - Idle: KeepAliveIdle, - Interval: KeepAliveInterval, - }) +func tcpKeepAlive(tcp *net.TCPConn) { + config := net.KeepAliveConfig{ + Enable: true, + Idle: KeepAliveIdle, + Interval: KeepAliveInterval, } + if !SupportTCPKeepAliveCount() { + // it's recommended to set both Idle and Interval to non-negative values in conjunction with a -1 + // for Count on those old Windows if you intend to customize the TCP keep-alive settings. + config.Count = -1 + } + _ = tcp.SetKeepAliveConfig(config) } diff --git a/common/net/tcp_keepalive_go123_unix.go b/common/net/tcp_keepalive_go123_unix.go new file mode 100644 index 00000000..0ead7ca4 --- /dev/null +++ b/common/net/tcp_keepalive_go123_unix.go @@ -0,0 +1,15 @@ +//go:build go1.23 && unix + +package net + +func SupportTCPKeepAliveIdle() bool { + return true +} + +func SupportTCPKeepAliveInterval() bool { + return true +} + +func SupportTCPKeepAliveCount() bool { + return true +} diff --git a/common/net/tcp_keepalive_go123_windows.go b/common/net/tcp_keepalive_go123_windows.go new file mode 100644 index 00000000..8f1e61f9 --- /dev/null +++ b/common/net/tcp_keepalive_go123_windows.go @@ -0,0 +1,63 @@ +//go:build go1.23 && windows + +// copy and modify from golang1.23's internal/syscall/windows/version_windows.go + +package net + +import ( + "errors" + "sync" + "syscall" + + "github.com/metacubex/mihomo/constant/features" + + "golang.org/x/sys/windows" +) + +var ( + supportTCPKeepAliveIdle bool + supportTCPKeepAliveInterval bool + supportTCPKeepAliveCount bool +) + +var initTCPKeepAlive = sync.OnceFunc(func() { + s, err := windows.WSASocket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP, nil, 0, windows.WSA_FLAG_NO_HANDLE_INHERIT) + if err != nil { + // Fallback to checking the Windows version. + major, build := features.WindowsMajorVersion, features.WindowsBuildNumber + supportTCPKeepAliveIdle = major >= 10 && build >= 16299 + supportTCPKeepAliveInterval = major >= 10 && build >= 16299 + supportTCPKeepAliveCount = major >= 10 && build >= 15063 + return + } + defer windows.Closesocket(s) + var optSupported = func(opt int) bool { + err := windows.SetsockoptInt(s, syscall.IPPROTO_TCP, opt, 1) + return !errors.Is(err, syscall.WSAENOPROTOOPT) + } + supportTCPKeepAliveIdle = optSupported(windows.TCP_KEEPIDLE) + supportTCPKeepAliveInterval = optSupported(windows.TCP_KEEPINTVL) + supportTCPKeepAliveCount = optSupported(windows.TCP_KEEPCNT) +}) + +// SupportTCPKeepAliveIdle indicates whether TCP_KEEPIDLE is supported. +// The minimal requirement is Windows 10.0.16299. +func SupportTCPKeepAliveIdle() bool { + initTCPKeepAlive() + return supportTCPKeepAliveIdle +} + +// SupportTCPKeepAliveInterval indicates whether TCP_KEEPINTVL is supported. +// The minimal requirement is Windows 10.0.16299. +func SupportTCPKeepAliveInterval() bool { + initTCPKeepAlive() + return supportTCPKeepAliveInterval +} + +// SupportTCPKeepAliveCount indicates whether TCP_KEEPCNT is supported. +// supports TCP_KEEPCNT. +// The minimal requirement is Windows 10.0.15063. +func SupportTCPKeepAliveCount() bool { + initTCPKeepAlive() + return supportTCPKeepAliveCount +} diff --git a/common/net/tcpip.go b/common/net/tcpip.go index 4f4fa661..a84e7e4c 100644 --- a/common/net/tcpip.go +++ b/common/net/tcpip.go @@ -4,12 +4,6 @@ import ( "fmt" "net" "strings" - "time" -) - -var ( - KeepAliveIdle = 0 * time.Second - KeepAliveInterval = 0 * time.Second ) func SplitNetworkType(s string) (string, string, error) { diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index ba95c31b..2a39508f 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -13,7 +13,6 @@ import ( "time" "github.com/metacubex/mihomo/component/resolver" - "github.com/metacubex/mihomo/constant/features" "github.com/metacubex/mihomo/log" ) @@ -79,29 +78,29 @@ func DialContext(ctx context.Context, network, address string, options ...Option } func ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort, options ...Option) (net.PacketConn, error) { - if features.CMFA && DefaultSocketHook != nil { - return listenPacketHooked(ctx, network, address) - } - cfg := applyOptions(options...) lc := &net.ListenConfig{} - if cfg.interfaceName != "" { - bind := bindIfaceToListenConfig - if cfg.fallbackBind { - bind = fallbackBindIfaceToListenConfig - } - addr, err := bind(cfg.interfaceName, lc, network, address, rAddrPort) - if err != nil { - return nil, err - } - address = addr - } if cfg.addrReuse { addrReuseToListenConfig(lc) } - if cfg.routingMark != 0 { - bindMarkToListenConfig(cfg.routingMark, lc, network, address) + if DefaultSocketHook != nil { // ignore interfaceName, routingMark when DefaultSocketHook not null (in CFMA) + socketHookToListenConfig(lc) + } else { + if cfg.interfaceName != "" { + bind := bindIfaceToListenConfig + if cfg.fallbackBind { + bind = fallbackBindIfaceToListenConfig + } + addr, err := bind(cfg.interfaceName, lc, network, address, rAddrPort) + if err != nil { + return nil, err + } + address = addr + } + if cfg.routingMark != 0 { + bindMarkToListenConfig(cfg.routingMark, lc, network, address) + } } return lc.ListenPacket(ctx, network, address) @@ -149,25 +148,26 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po setMultiPathTCP(dialer) } - if features.CMFA && DefaultSocketHook != nil { // ignore interfaceName, routingMark and tfo in CMFA - return dialContextHooked(ctx, dialer, network, address) + if DefaultSocketHook != nil { // ignore interfaceName, routingMark and tfo when DefaultSocketHook not null (in CFMA) + socketHookToToDialer(dialer) + } else { + if opt.interfaceName != "" { + bind := bindIfaceToDialer + if opt.fallbackBind { + bind = fallbackBindIfaceToDialer + } + if err := bind(opt.interfaceName, dialer, network, destination); err != nil { + return nil, err + } + } + if opt.routingMark != 0 { + bindMarkToDialer(opt.routingMark, dialer, network, destination) + } + if opt.tfo && !DisableTFO { + return dialTFO(ctx, *dialer, network, address) + } } - if opt.interfaceName != "" { - bind := bindIfaceToDialer - if opt.fallbackBind { - bind = fallbackBindIfaceToDialer - } - if err := bind(opt.interfaceName, dialer, network, destination); err != nil { - return nil, err - } - } - if opt.routingMark != 0 { - bindMarkToDialer(opt.routingMark, dialer, network, destination) - } - if opt.tfo && !DisableTFO { - return dialTFO(ctx, *dialer, network, address) - } return dialer.DialContext(ctx, network, address) } diff --git a/component/dialer/patch_android.go b/component/dialer/patch_android.go deleted file mode 100644 index 079b9772..00000000 --- a/component/dialer/patch_android.go +++ /dev/null @@ -1,38 +0,0 @@ -//go:build android && cmfa - -package dialer - -import ( - "context" - "net" - "syscall" -) - -type SocketControl func(network, address string, conn syscall.RawConn) error - -var DefaultSocketHook SocketControl - -func dialContextHooked(ctx context.Context, dialer *net.Dialer, network string, address string) (net.Conn, error) { - addControlToDialer(dialer, func(ctx context.Context, network, address string, c syscall.RawConn) error { - return DefaultSocketHook(network, address, c) - }) - - conn, err := dialer.DialContext(ctx, network, address) - if err != nil { - return nil, err - } - - if t, ok := conn.(*net.TCPConn); ok { - t.SetKeepAlive(false) - } - - return conn, nil -} - -func listenPacketHooked(ctx context.Context, network, address string) (net.PacketConn, error) { - lc := &net.ListenConfig{ - Control: DefaultSocketHook, - } - - return lc.ListenPacket(ctx, network, address) -} diff --git a/component/dialer/patch_common.go b/component/dialer/patch_common.go deleted file mode 100644 index 2c96fe60..00000000 --- a/component/dialer/patch_common.go +++ /dev/null @@ -1,21 +0,0 @@ -//go:build !(android && cmfa) - -package dialer - -import ( - "context" - "net" - "syscall" -) - -type SocketControl func(network, address string, conn syscall.RawConn) error - -var DefaultSocketHook SocketControl - -func dialContextHooked(ctx context.Context, dialer *net.Dialer, network string, address string) (net.Conn, error) { - return nil, nil -} - -func listenPacketHooked(ctx context.Context, network, address string) (net.PacketConn, error) { - return nil, nil -} diff --git a/component/dialer/socket_hook.go b/component/dialer/socket_hook.go new file mode 100644 index 00000000..7a2ea432 --- /dev/null +++ b/component/dialer/socket_hook.go @@ -0,0 +1,27 @@ +package dialer + +import ( + "context" + "net" + "syscall" +) + +// SocketControl +// never change type traits because it's used in CFMA +type SocketControl func(network, address string, conn syscall.RawConn) error + +// DefaultSocketHook +// never change type traits because it's used in CFMA +var DefaultSocketHook SocketControl + +func socketHookToToDialer(dialer *net.Dialer) { + addControlToDialer(dialer, func(ctx context.Context, network, address string, c syscall.RawConn) error { + return DefaultSocketHook(network, address, c) + }) +} + +func socketHookToListenConfig(lc *net.ListenConfig) { + addControlToListenConfig(lc, func(ctx context.Context, network, address string, c syscall.RawConn) error { + return DefaultSocketHook(network, address, c) + }) +} diff --git a/config/config.go b/config/config.go index 3b13146d..235d71ae 100644 --- a/config/config.go +++ b/config/config.go @@ -340,6 +340,7 @@ type RawConfig struct { GlobalUA string `yaml:"global-ua"` KeepAliveIdle int `yaml:"keep-alive-idle"` KeepAliveInterval int `yaml:"keep-alive-interval"` + DisableKeepAlive bool `yaml:"disable-keep-alive"` Sniffer RawSniffer `yaml:"sniffer" json:"sniffer"` ProxyProvider map[string]map[string]any `yaml:"proxy-providers"` @@ -657,6 +658,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) { if cfg.KeepAliveInterval != 0 { N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second } + N.DisableKeepAlive = cfg.DisableKeepAlive updater.ExternalUIPath = cfg.ExternalUI // checkout externalUI exist diff --git a/docs/config.yaml b/docs/config.yaml index 6feca27d..c4644550 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -82,8 +82,9 @@ external-doh-server: /dns-query global-client-fingerprint: chrome # TCP keep alive interval -# keep-alive-idle: 7200 -# keep-alive-interval: 75 +# disable-keep-alive: false #目前在android端强制为true +# keep-alive-idle: 15 +# keep-alive-interval: 15 # routing-mark:6666 # 配置 fwmark 仅用于 Linux experimental: diff --git a/listener/http/server.go b/listener/http/server.go index 77e10f08..f61dd036 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -4,9 +4,9 @@ import ( "net" "github.com/metacubex/mihomo/adapter/inbound" + N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/auth" C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/constant/features" authStore "github.com/metacubex/mihomo/listener/auth" ) @@ -74,11 +74,7 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, authenticator auth.Authe } continue } - if features.CMFA { - if t, ok := conn.(*net.TCPConn); ok { - t.SetKeepAlive(false) - } - } + N.TCPKeepAlive(conn) if isDefault { // only apply on default listener if !inbound.IsRemoteAddrDisAllowed(conn.RemoteAddr()) { _ = conn.Close() From 696b75ee375660bb8478ca94fe2f16b8ad2ff3c3 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 14 Aug 2024 20:02:02 +0800 Subject: [PATCH 166/311] feat: `fake-ip-filter` support `rule-set:` and `geosite:` --- component/fakeip/pool.go | 19 +++++++++++-- config/config.go | 59 ++++++++++++++++++++++++++++++++++++++-- docs/config.yaml | 15 ++++++---- 3 files changed, 83 insertions(+), 10 deletions(-) diff --git a/component/fakeip/pool.go b/component/fakeip/pool.go index 2b06fc0b..90e648fe 100644 --- a/component/fakeip/pool.go +++ b/component/fakeip/pool.go @@ -9,6 +9,7 @@ import ( "github.com/metacubex/mihomo/common/nnip" "github.com/metacubex/mihomo/component/profile/cachefile" "github.com/metacubex/mihomo/component/trie" + C "github.com/metacubex/mihomo/constant" ) const ( @@ -36,6 +37,7 @@ type Pool struct { cycle bool mux sync.Mutex host *trie.DomainTrie[struct{}] + rules []C.Rule ipnet netip.Prefix store store } @@ -66,10 +68,18 @@ func (p *Pool) LookBack(ip netip.Addr) (string, bool) { // ShouldSkipped return if domain should be skipped func (p *Pool) ShouldSkipped(domain string) bool { - if p.host == nil { - return false + if p.host != nil { + if p.host.Search(domain) != nil { + return true + } } - return p.host.Search(domain) != nil + for _, rule := range p.rules { + metadata := &C.Metadata{Host: domain} + if match, _ := rule.Match(metadata); match { + return true + } + } + return false } // Exist returns if given ip exists in fake-ip pool @@ -156,6 +166,8 @@ type Options struct { IPNet netip.Prefix Host *trie.DomainTrie[struct{}] + Rules []C.Rule + // Size sets the maximum number of entries in memory // and does not work if Persistence is true Size int @@ -185,6 +197,7 @@ func New(options Options) (*Pool, error) { offset: first.Prev(), cycle: false, host: options.Host, + rules: options.Rules, ipnet: options.IPNet, } if options.Persistence { diff --git a/config/config.go b/config/config.go index 235d71ae..fae88e1a 100644 --- a/config/config.go +++ b/config/config.go @@ -38,6 +38,7 @@ import ( LC "github.com/metacubex/mihomo/listener/config" "github.com/metacubex/mihomo/log" R "github.com/metacubex/mihomo/rules" + RC "github.com/metacubex/mihomo/rules/common" RP "github.com/metacubex/mihomo/rules/provider" T "github.com/metacubex/mihomo/tunnel" @@ -1408,13 +1409,63 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul } var host *trie.DomainTrie[struct{}] + var fakeIPRules []C.Rule // fake ip skip host filter if len(cfg.FakeIPFilter) != 0 { host = trie.New[struct{}]() for _, domain := range cfg.FakeIPFilter { - _ = host.Insert(domain, struct{}{}) + if strings.Contains(strings.ToLower(domain), ",") { + if strings.Contains(domain, "geosite:") { + subkeys := strings.Split(domain, ":") + subkeys = subkeys[1:] + subkeys = strings.Split(subkeys[0], ",") + for _, country := range subkeys { + found := false + for _, rule := range rules { + if rule.RuleType() == C.GEOSITE { + if strings.EqualFold(country, rule.Payload()) { + found = true + fakeIPRules = append(fakeIPRules, rule) + } + } + } + if !found { + rule, err := RC.NewGEOSITE(country, "") + if err != nil { + return nil, err + } + fakeIPRules = append(fakeIPRules, rule) + } + } + + } + } else if strings.Contains(strings.ToLower(domain), "rule-set:") { + subkeys := strings.Split(domain, ":") + subkeys = subkeys[1:] + subkeys = strings.Split(subkeys[0], ",") + for _, domainSetName := range subkeys { + if rp, ok := ruleProviders[domainSetName]; !ok { + return nil, fmt.Errorf("not found rule-set: %s", domainSetName) + } else { + switch rp.Behavior() { + case providerTypes.IPCIDR: + return nil, fmt.Errorf("rule provider type error, except domain,actual %s", rp.Behavior()) + case providerTypes.Classical: + log.Warnln("%s provider is %s, only matching it contain domain rule", rp.Name(), rp.Behavior()) + default: + } + } + rule, err := RP.NewRuleSet(domainSetName, "", true) + if err != nil { + return nil, err + } + + fakeIPRules = append(fakeIPRules, rule) + } + } else { + _ = host.Insert(domain, struct{}{}) + } } - host.Optimize() } if len(dnsCfg.Fallback) != 0 { @@ -1427,6 +1478,9 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul } _ = host.Insert(fb.Addr, struct{}{}) } + } + + if host != nil { host.Optimize() } @@ -1434,6 +1488,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul IPNet: fakeIPRange, Size: 1000, Host: host, + Rules: fakeIPRules, Persistence: rawCfg.Profile.StoreFakeIP, }) if err != nil { diff --git a/docs/config.yaml b/docs/config.yaml index c4644550..8db06b6d 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -243,6 +243,16 @@ dns: fake-ip-range: 198.18.0.1/16 # fake-ip 池设置 + # 配置不使用 fake-ip 的域名 + fake-ip-filter: + - '*.lan' + - localhost.ptlogin2.qq.com + # fakeip-filter 为 rule-providers 中的名为 fakeip-filter 规则订阅, + # 且 behavior 必须为 domain/classical,当为 classical 时仅会生效域名类规则 + - rule-set:fakeip-filter + # fakeip-filter 为 geosite 中名为 fakeip-filter 的分类(需要自行保证该分类存在) + - geosite:fakeip-filter + # use-hosts: true # 查询 hosts # 配置后面的nameserver、fallback和nameserver-policy向dns服务器的连接过程是否遵守遵守rules规则 @@ -252,11 +262,6 @@ dns: # 此外,这三者配置中的dns服务器如果出现域名会采用default-nameserver配置项解析,也请确保正确配置default-nameserver respect-rules: false - # 配置不使用 fake-ip 的域名 - # fake-ip-filter: - # - '*.lan' - # - localhost.ptlogin2.qq.com - # DNS 主要域名配置 # 支持 UDP,TCP,DoT,DoH,DoQ # 这部分为主要 DNS 配置,影响所有直连,确保使用对大陆解析精准的 DNS From 7fd0467aefee2cf286e3a4f69d08dde9bfe339f8 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 14 Aug 2024 22:38:17 +0800 Subject: [PATCH 167/311] feat: `sniffer`'s `force-domain` and `skip-domain` support `rule-set:` and `geosite:` --- component/fakeip/pool.go | 19 +--- component/fakeip/pool_test.go | 4 +- component/sniffer/dispatcher.go | 32 +++--- component/trie/domain.go | 7 ++ config/config.go | 170 ++++++++++++++++---------------- constant/rule.go | 3 + rules/provider/domain_set.go | 43 ++++++++ 7 files changed, 166 insertions(+), 112 deletions(-) create mode 100644 rules/provider/domain_set.go diff --git a/component/fakeip/pool.go b/component/fakeip/pool.go index 90e648fe..8096a868 100644 --- a/component/fakeip/pool.go +++ b/component/fakeip/pool.go @@ -8,7 +8,6 @@ import ( "github.com/metacubex/mihomo/common/nnip" "github.com/metacubex/mihomo/component/profile/cachefile" - "github.com/metacubex/mihomo/component/trie" C "github.com/metacubex/mihomo/constant" ) @@ -36,8 +35,7 @@ type Pool struct { offset netip.Addr cycle bool mux sync.Mutex - host *trie.DomainTrie[struct{}] - rules []C.Rule + host []C.Rule ipnet netip.Prefix store store } @@ -68,14 +66,8 @@ func (p *Pool) LookBack(ip netip.Addr) (string, bool) { // ShouldSkipped return if domain should be skipped func (p *Pool) ShouldSkipped(domain string) bool { - if p.host != nil { - if p.host.Search(domain) != nil { - return true - } - } - for _, rule := range p.rules { - metadata := &C.Metadata{Host: domain} - if match, _ := rule.Match(metadata); match { + for _, rule := range p.host { + if match, _ := rule.Match(&C.Metadata{Host: domain}); match { return true } } @@ -164,9 +156,7 @@ func (p *Pool) restoreState() { type Options struct { IPNet netip.Prefix - Host *trie.DomainTrie[struct{}] - - Rules []C.Rule + Host []C.Rule // Size sets the maximum number of entries in memory // and does not work if Persistence is true @@ -197,7 +187,6 @@ func New(options Options) (*Pool, error) { offset: first.Prev(), cycle: false, host: options.Host, - rules: options.Rules, ipnet: options.IPNet, } if options.Persistence { diff --git a/component/fakeip/pool_test.go b/component/fakeip/pool_test.go index cc50fcf7..9c05a327 100644 --- a/component/fakeip/pool_test.go +++ b/component/fakeip/pool_test.go @@ -9,6 +9,8 @@ import ( "github.com/metacubex/mihomo/component/profile/cachefile" "github.com/metacubex/mihomo/component/trie" + C "github.com/metacubex/mihomo/constant" + RP "github.com/metacubex/mihomo/rules/provider" "github.com/sagernet/bbolt" "github.com/stretchr/testify/assert" @@ -154,7 +156,7 @@ func TestPool_Skip(t *testing.T) { pools, tempfile, err := createPools(Options{ IPNet: ipnet, Size: 10, - Host: tree, + Host: []C.Rule{RP.NewDomainSet(tree.NewDomainSet(), "")}, }) assert.Nil(t, err) defer os.Remove(tempfile) diff --git a/component/sniffer/dispatcher.go b/component/sniffer/dispatcher.go index 4438638d..c96f5a4b 100644 --- a/component/sniffer/dispatcher.go +++ b/component/sniffer/dispatcher.go @@ -9,7 +9,6 @@ import ( "github.com/metacubex/mihomo/common/lru" N "github.com/metacubex/mihomo/common/net" - "github.com/metacubex/mihomo/component/trie" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/constant/sniffer" "github.com/metacubex/mihomo/log" @@ -26,17 +25,26 @@ var Dispatcher *SnifferDispatcher type SnifferDispatcher struct { enable bool sniffers map[sniffer.Sniffer]SnifferConfig - forceDomain *trie.DomainSet - skipSNI *trie.DomainSet + forceDomain []C.Rule + skipDomain []C.Rule skipList *lru.LruCache[string, uint8] forceDnsMapping bool parsePureIp bool } func (sd *SnifferDispatcher) shouldOverride(metadata *C.Metadata) bool { - return (metadata.Host == "" && sd.parsePureIp) || - sd.forceDomain.Has(metadata.Host) || - (metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping) + if metadata.Host == "" && sd.parsePureIp { + return true + } + if metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping { + return true + } + for _, rule := range sd.forceDomain { + if ok, _ := rule.Match(&C.Metadata{Host: metadata.Host}); ok { + return true + } + } + return false } func (sd *SnifferDispatcher) UDPSniff(packet C.PacketAdapter) bool { @@ -94,9 +102,11 @@ func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata log.Debugln("[Sniffer] All sniffing sniff failed with from [%s:%d] to [%s:%d]", metadata.SrcIP, metadata.SrcPort, metadata.String(), metadata.DstPort) return false } else { - if sd.skipSNI.Has(host) { - log.Debugln("[Sniffer] Skip sni[%s]", host) - return false + for _, rule := range sd.skipDomain { + if ok, _ := rule.Match(&C.Metadata{Host: host}); ok { + log.Debugln("[Sniffer] Skip sni[%s]", host) + return false + } } sd.skipList.Delete(dst) @@ -187,12 +197,12 @@ func NewCloseSnifferDispatcher() (*SnifferDispatcher, error) { } func NewSnifferDispatcher(snifferConfig map[sniffer.Type]SnifferConfig, - forceDomain *trie.DomainSet, skipSNI *trie.DomainSet, + forceDomain []C.Rule, skipDomain []C.Rule, forceDnsMapping bool, parsePureIp bool) (*SnifferDispatcher, error) { dispatcher := SnifferDispatcher{ enable: true, forceDomain: forceDomain, - skipSNI: skipSNI, + skipDomain: skipDomain, skipList: lru.New(lru.WithSize[string, uint8](128), lru.WithAge[string, uint8](600)), forceDnsMapping: forceDnsMapping, parsePureIp: parsePureIp, diff --git a/component/trie/domain.go b/component/trie/domain.go index db30402e..6d3e37f7 100644 --- a/component/trie/domain.go +++ b/component/trie/domain.go @@ -134,6 +134,13 @@ func (t *DomainTrie[T]) Foreach(fn func(domain string, data T) bool) { } } +func (t *DomainTrie[T]) IsEmpty() bool { + if t == nil { + return true + } + return t.root.isEmpty() +} + func recursion[T any](items []string, node *Node[T], fn func(domain string, data T) bool) bool { for key, data := range node.getChildren() { newItems := append([]string{key}, items...) diff --git a/config/config.go b/config/config.go index fae88e1a..74ffdd03 100644 --- a/config/config.go +++ b/config/config.go @@ -164,8 +164,8 @@ type IPTables struct { type Sniffer struct { Enable bool Sniffers map[snifferTypes.Type]SNIFF.SnifferConfig - ForceDomain *trie.DomainSet - SkipDomain *trie.DomainSet + ForceDomain []C.Rule + SkipDomain []C.Rule ForceDnsMapping bool ParsePureIp bool } @@ -627,7 +627,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } } - config.Sniffer, err = parseSniffer(rawCfg.Sniffer) + config.Sniffer, err = parseSniffer(rawCfg.Sniffer, rules, ruleProviders) if err != nil { return nil, err } @@ -1408,87 +1408,27 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul return nil, err } - var host *trie.DomainTrie[struct{}] - var fakeIPRules []C.Rule - // fake ip skip host filter - if len(cfg.FakeIPFilter) != 0 { - host = trie.New[struct{}]() - for _, domain := range cfg.FakeIPFilter { - if strings.Contains(strings.ToLower(domain), ",") { - if strings.Contains(domain, "geosite:") { - subkeys := strings.Split(domain, ":") - subkeys = subkeys[1:] - subkeys = strings.Split(subkeys[0], ",") - for _, country := range subkeys { - found := false - for _, rule := range rules { - if rule.RuleType() == C.GEOSITE { - if strings.EqualFold(country, rule.Payload()) { - found = true - fakeIPRules = append(fakeIPRules, rule) - } - } - } - if !found { - rule, err := RC.NewGEOSITE(country, "") - if err != nil { - return nil, err - } - fakeIPRules = append(fakeIPRules, rule) - } - } - - } - } else if strings.Contains(strings.ToLower(domain), "rule-set:") { - subkeys := strings.Split(domain, ":") - subkeys = subkeys[1:] - subkeys = strings.Split(subkeys[0], ",") - for _, domainSetName := range subkeys { - if rp, ok := ruleProviders[domainSetName]; !ok { - return nil, fmt.Errorf("not found rule-set: %s", domainSetName) - } else { - switch rp.Behavior() { - case providerTypes.IPCIDR: - return nil, fmt.Errorf("rule provider type error, except domain,actual %s", rp.Behavior()) - case providerTypes.Classical: - log.Warnln("%s provider is %s, only matching it contain domain rule", rp.Name(), rp.Behavior()) - default: - } - } - rule, err := RP.NewRuleSet(domainSetName, "", true) - if err != nil { - return nil, err - } - - fakeIPRules = append(fakeIPRules, rule) - } - } else { - _ = host.Insert(domain, struct{}{}) - } - } - } - + var fakeIPTrie *trie.DomainTrie[struct{}] if len(dnsCfg.Fallback) != 0 { - if host == nil { - host = trie.New[struct{}]() - } + fakeIPTrie = trie.New[struct{}]() for _, fb := range dnsCfg.Fallback { if net.ParseIP(fb.Addr) != nil { continue } - _ = host.Insert(fb.Addr, struct{}{}) + _ = fakeIPTrie.Insert(fb.Addr, struct{}{}) } } - if host != nil { - host.Optimize() + // fake ip skip host filter + host, err := parseDomain(cfg.FakeIPFilter, fakeIPTrie, rules, ruleProviders) + if err != nil { + return nil, err } pool, err := fakeip.New(fakeip.Options{ IPNet: fakeIPRange, Size: 1000, Host: host, - Rules: fakeIPRules, Persistence: rawCfg.Profile.StoreFakeIP, }) if err != nil { @@ -1609,7 +1549,7 @@ func parseTuicServer(rawTuic RawTuicServer, general *General) error { return nil } -func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) { +func parseSniffer(snifferRaw RawSniffer, rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider) (*Sniffer, error) { sniffer := &Sniffer{ Enable: snifferRaw.Enable, ForceDnsMapping: snifferRaw.ForceDnsMapping, @@ -1672,23 +1612,83 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) { sniffer.Sniffers = loadSniffer - forceDomainTrie := trie.New[struct{}]() - for _, domain := range snifferRaw.ForceDomain { - err := forceDomainTrie.Insert(domain, struct{}{}) - if err != nil { - return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err) - } + forceDomain, err := parseDomain(snifferRaw.ForceDomain, nil, rules, ruleProviders) + if err != nil { + return nil, fmt.Errorf("error in force-domain, error:%w", err) } - sniffer.ForceDomain = forceDomainTrie.NewDomainSet() + sniffer.ForceDomain = forceDomain - skipDomainTrie := trie.New[struct{}]() - for _, domain := range snifferRaw.SkipDomain { - err := skipDomainTrie.Insert(domain, struct{}{}) - if err != nil { - return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err) - } + skipDomain, err := parseDomain(snifferRaw.SkipDomain, nil, rules, ruleProviders) + if err != nil { + return nil, fmt.Errorf("error in skip-domain, error:%w", err) } - sniffer.SkipDomain = skipDomainTrie.NewDomainSet() + sniffer.SkipDomain = skipDomain return sniffer, nil } + +func parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider) (domainRules []C.Rule, err error) { + var rule C.Rule + for _, domain := range domains { + domainLower := strings.ToLower(domain) + if strings.Contains(domainLower, "geosite:") { + subkeys := strings.Split(domain, ":") + subkeys = subkeys[1:] + subkeys = strings.Split(subkeys[0], ",") + for _, country := range subkeys { + found := false + for _, rule = range rules { + if rule.RuleType() == C.GEOSITE { + if strings.EqualFold(country, rule.Payload()) { + found = true + domainRules = append(domainRules, rule) + } + } + } + if !found { + rule, err = RC.NewGEOSITE(country, "") + if err != nil { + return nil, err + } + domainRules = append(domainRules, rule) + } + } + } else if strings.Contains(domainLower, "rule-set:") { + subkeys := strings.Split(domain, ":") + subkeys = subkeys[1:] + subkeys = strings.Split(subkeys[0], ",") + for _, domainSetName := range subkeys { + if rp, ok := ruleProviders[domainSetName]; !ok { + return nil, fmt.Errorf("not found rule-set: %s", domainSetName) + } else { + switch rp.Behavior() { + case providerTypes.IPCIDR: + return nil, fmt.Errorf("rule provider type error, except domain,actual %s", rp.Behavior()) + case providerTypes.Classical: + log.Warnln("%s provider is %s, only matching it contain domain rule", rp.Name(), rp.Behavior()) + default: + } + } + rule, err = RP.NewRuleSet(domainSetName, "", true) + if err != nil { + return nil, err + } + + domainRules = append(domainRules, rule) + } + } else { + if domainTrie == nil { + domainTrie = trie.New[struct{}]() + } + err = domainTrie.Insert(domain, struct{}{}) + if err != nil { + return nil, err + } + } + } + if !domainTrie.IsEmpty() { + rule = RP.NewDomainSet(domainTrie.NewDomainSet(), "") + domainRules = append(domainRules, rule) + } + return +} diff --git a/constant/rule.go b/constant/rule.go index a91ee6cb..f9f987e6 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -27,6 +27,7 @@ const ( ProcessNameRegex ProcessPathRegex RuleSet + DomainSet Network Uid SubRules @@ -90,6 +91,8 @@ func (rt RuleType) String() string { return "Match" case RuleSet: return "RuleSet" + case DomainSet: + return "DomainSet" case Network: return "Network" case DSCP: diff --git a/rules/provider/domain_set.go b/rules/provider/domain_set.go new file mode 100644 index 00000000..372b438e --- /dev/null +++ b/rules/provider/domain_set.go @@ -0,0 +1,43 @@ +package provider + +import ( + "github.com/metacubex/mihomo/component/trie" + C "github.com/metacubex/mihomo/constant" +) + +type DomainSet struct { + *domainStrategy + adapter string +} + +func (d *DomainSet) ProviderNames() []string { + return nil +} + +func (d *DomainSet) RuleType() C.RuleType { + return C.DomainSet +} + +func (d *DomainSet) Match(metadata *C.Metadata) (bool, string) { + if d.domainSet == nil { + return false, "" + } + return d.domainSet.Has(metadata.RuleHost()), d.adapter +} + +func (d *DomainSet) Adapter() string { + return d.adapter +} + +func (d *DomainSet) Payload() string { + return "" +} + +func NewDomainSet(domainSet *trie.DomainSet, adapter string) *DomainSet { + return &DomainSet{ + domainStrategy: &domainStrategy{domainSet: domainSet}, + adapter: adapter, + } +} + +var _ C.Rule = (*DomainSet)(nil) From 4c10d42fbff0ba8e22570015be234a45da28c11e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 15 Aug 2024 07:42:59 +0800 Subject: [PATCH 168/311] fix: normal rule not working in `fake-ip-filter` --- component/fakeip/pool_test.go | 3 ++- component/trie/domain.go | 8 ++++---- component/trie/domain_set_test.go | 3 +++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/component/fakeip/pool_test.go b/component/fakeip/pool_test.go index 9c05a327..ed52fb6d 100644 --- a/component/fakeip/pool_test.go +++ b/component/fakeip/pool_test.go @@ -152,7 +152,8 @@ func TestPool_CycleUsed(t *testing.T) { func TestPool_Skip(t *testing.T) { ipnet := netip.MustParsePrefix("192.168.0.1/29") tree := trie.New[struct{}]() - tree.Insert("example.com", struct{}{}) + assert.NoError(t, tree.Insert("example.com", struct{}{})) + assert.False(t, tree.IsEmpty()) pools, tempfile, err := createPools(Options{ IPNet: ipnet, Size: 10, diff --git a/component/trie/domain.go b/component/trie/domain.go index 6d3e37f7..87dfeda6 100644 --- a/component/trie/domain.go +++ b/component/trie/domain.go @@ -126,7 +126,7 @@ func (t *DomainTrie[T]) Optimize() { func (t *DomainTrie[T]) Foreach(fn func(domain string, data T) bool) { for key, data := range t.root.getChildren() { recursion([]string{key}, data, fn) - if data != nil && data.inited { + if !data.isEmpty() { if !fn(joinDomain([]string{key}), data.data) { return } @@ -135,16 +135,16 @@ func (t *DomainTrie[T]) Foreach(fn func(domain string, data T) bool) { } func (t *DomainTrie[T]) IsEmpty() bool { - if t == nil { + if t == nil || t.root == nil { return true } - return t.root.isEmpty() + return len(t.root.getChildren()) == 0 } func recursion[T any](items []string, node *Node[T], fn func(domain string, data T) bool) bool { for key, data := range node.getChildren() { newItems := append([]string{key}, items...) - if data != nil && data.inited { + if !data.isEmpty() { domain := joinDomain(newItems) if domain[0] == domainStepByte { domain = complexWildcard + domain diff --git a/component/trie/domain_set_test.go b/component/trie/domain_set_test.go index e343d11d..38ba1622 100644 --- a/component/trie/domain_set_test.go +++ b/component/trie/domain_set_test.go @@ -40,6 +40,7 @@ func TestDomainSet(t *testing.T) { for _, domain := range domainSet { assert.NoError(t, tree.Insert(domain, struct{}{})) } + assert.False(t, tree.IsEmpty()) set := tree.NewDomainSet() assert.NotNil(t, set) assert.True(t, set.Has("test.cn")) @@ -68,6 +69,7 @@ func TestDomainSetComplexWildcard(t *testing.T) { for _, domain := range domainSet { assert.NoError(t, tree.Insert(domain, struct{}{})) } + assert.False(t, tree.IsEmpty()) set := tree.NewDomainSet() assert.NotNil(t, set) assert.False(t, set.Has("google.com")) @@ -90,6 +92,7 @@ func TestDomainSetWildcard(t *testing.T) { for _, domain := range domainSet { assert.NoError(t, tree.Insert(domain, struct{}{})) } + assert.False(t, tree.IsEmpty()) set := tree.NewDomainSet() assert.NotNil(t, set) assert.True(t, set.Has("www.baidu.com")) From 92ec5f223681a3dcf1f97e986717cfae353a66e1 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 15 Aug 2024 20:04:24 +0800 Subject: [PATCH 169/311] chore: cleanup dns policy match code --- config/config.go | 308 ++++++++++++++++------------------- constant/rule.go | 8 + constant/rule_extra.go | 17 -- dns/filters.go | 102 ------------ dns/policy.go | 27 +-- dns/resolver.go | 121 ++++---------- hub/executor/executor.go | 33 ++-- rules/provider/domain_set.go | 5 +- rules/provider/ipcidr_set.go | 40 +++++ 9 files changed, 241 insertions(+), 420 deletions(-) delete mode 100644 constant/rule_extra.go delete mode 100644 dns/filters.go create mode 100644 rules/provider/ipcidr_set.go diff --git a/config/config.go b/config/config.go index 74ffdd03..c1c155ec 100644 --- a/config/config.go +++ b/config/config.go @@ -20,9 +20,9 @@ import ( N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/auth" + "github.com/metacubex/mihomo/component/cidr" "github.com/metacubex/mihomo/component/fakeip" "github.com/metacubex/mihomo/component/geodata" - "github.com/metacubex/mihomo/component/geodata/router" P "github.com/metacubex/mihomo/component/process" "github.com/metacubex/mihomo/component/resolver" SNIFF "github.com/metacubex/mihomo/component/sniffer" @@ -114,33 +114,25 @@ type NTP struct { // DNS config type DNS struct { - Enable bool `yaml:"enable"` - PreferH3 bool `yaml:"prefer-h3"` - IPv6 bool `yaml:"ipv6"` - IPv6Timeout uint `yaml:"ipv6-timeout"` - UseSystemHosts bool `yaml:"use-system-hosts"` - NameServer []dns.NameServer `yaml:"nameserver"` - Fallback []dns.NameServer `yaml:"fallback"` - FallbackFilter FallbackFilter `yaml:"fallback-filter"` - Listen string `yaml:"listen"` - EnhancedMode C.DNSMode `yaml:"enhanced-mode"` - DefaultNameserver []dns.NameServer `yaml:"default-nameserver"` - CacheAlgorithm string `yaml:"cache-algorithm"` + Enable bool + PreferH3 bool + IPv6 bool + IPv6Timeout uint + UseSystemHosts bool + NameServer []dns.NameServer + Fallback []dns.NameServer + FallbackIPFilter []C.Rule + FallbackDomainFilter []C.Rule + Listen string + EnhancedMode C.DNSMode + DefaultNameserver []dns.NameServer + CacheAlgorithm string FakeIPRange *fakeip.Pool Hosts *trie.DomainTrie[resolver.HostValue] - NameServerPolicy *orderedmap.OrderedMap[string, []dns.NameServer] + NameServerPolicy []dns.Policy ProxyServerNameserver []dns.NameServer } -// FallbackFilter config -type FallbackFilter struct { - GeoIP bool `yaml:"geoip"` - GeoIPCode string `yaml:"geoip-code"` - IPCIDR []netip.Prefix `yaml:"ipcidr"` - Domain []string `yaml:"domain"` - GeoSite []router.DomainMatcher `yaml:"geosite"` -} - // Profile config type Profile struct { StoreSelected bool `yaml:"store-selected"` @@ -1205,49 +1197,13 @@ func parsePureDNSServer(server string) string { } } -func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], ruleProviders map[string]providerTypes.RuleProvider, respectRules bool, preferH3 bool) (*orderedmap.OrderedMap[string, []dns.NameServer], error) { - policy := orderedmap.New[string, []dns.NameServer]() - updatedPolicy := orderedmap.New[string, any]() +func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider, respectRules bool, preferH3 bool) ([]dns.Policy, error) { + var policy []dns.Policy re := regexp.MustCompile(`[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?`) for pair := nsPolicy.Oldest(); pair != nil; pair = pair.Next() { k, v := pair.Key, pair.Value - if strings.Contains(strings.ToLower(k), ",") { - if strings.Contains(k, "geosite:") { - subkeys := strings.Split(k, ":") - subkeys = subkeys[1:] - subkeys = strings.Split(subkeys[0], ",") - for _, subkey := range subkeys { - newKey := "geosite:" + subkey - updatedPolicy.Store(newKey, v) - } - } else if strings.Contains(strings.ToLower(k), "rule-set:") { - subkeys := strings.Split(k, ":") - subkeys = subkeys[1:] - subkeys = strings.Split(subkeys[0], ",") - for _, subkey := range subkeys { - newKey := "rule-set:" + subkey - updatedPolicy.Store(newKey, v) - } - } else if re.MatchString(k) { - subkeys := strings.Split(k, ",") - for _, subkey := range subkeys { - updatedPolicy.Store(subkey, v) - } - } - } else { - if strings.Contains(strings.ToLower(k), "geosite:") { - updatedPolicy.Store("geosite:"+k[8:], v) - } else if strings.Contains(strings.ToLower(k), "rule-set:") { - updatedPolicy.Store("rule-set:"+k[9:], v) - } - updatedPolicy.Store(k, v) - } - } - - for pair := updatedPolicy.Oldest(); pair != nil; pair = pair.Next() { - domain, server := pair.Key, pair.Value - servers, err := utils.ToStringSlice(server) + servers, err := utils.ToStringSlice(v) if err != nil { return nil, err } @@ -1255,75 +1211,65 @@ func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], rulePro if err != nil { return nil, err } - if _, valid := trie.ValidAndSplitDomain(domain); !valid { - return nil, fmt.Errorf("DNS ResoverRule invalid domain: %s", domain) + if strings.Contains(strings.ToLower(k), ",") { + if strings.Contains(k, "geosite:") { + subkeys := strings.Split(k, ":") + subkeys = subkeys[1:] + subkeys = strings.Split(subkeys[0], ",") + for _, subkey := range subkeys { + newKey := "geosite:" + subkey + policy = append(policy, dns.Policy{Domain: newKey, NameServers: nameservers}) + } + } else if strings.Contains(strings.ToLower(k), "rule-set:") { + subkeys := strings.Split(k, ":") + subkeys = subkeys[1:] + subkeys = strings.Split(subkeys[0], ",") + for _, subkey := range subkeys { + newKey := "rule-set:" + subkey + policy = append(policy, dns.Policy{Domain: newKey, NameServers: nameservers}) + } + } else if re.MatchString(k) { + subkeys := strings.Split(k, ",") + for _, subkey := range subkeys { + policy = append(policy, dns.Policy{Domain: subkey, NameServers: nameservers}) + } + } + } else { + if strings.Contains(strings.ToLower(k), "geosite:") { + policy = append(policy, dns.Policy{Domain: "geosite:" + k[8:], NameServers: nameservers}) + } else if strings.Contains(strings.ToLower(k), "rule-set:") { + policy = append(policy, dns.Policy{Domain: "rule-set:" + k[9:], NameServers: nameservers}) + } else { + policy = append(policy, dns.Policy{Domain: k, NameServers: nameservers}) + } } + } + + for idx, p := range policy { + domain, nameservers := p.Domain, p.NameServers + if strings.HasPrefix(domain, "rule-set:") { domainSetName := domain[9:] - if provider, ok := ruleProviders[domainSetName]; !ok { - return nil, fmt.Errorf("not found rule-set: %s", domainSetName) - } else { - switch provider.Behavior() { - case providerTypes.IPCIDR: - return nil, fmt.Errorf("rule provider type error, except domain,actual %s", provider.Behavior()) - case providerTypes.Classical: - log.Warnln("%s provider is %s, only matching it contain domain rule", provider.Name(), provider.Behavior()) - } - } - } - policy.Store(domain, nameservers) - } - - return policy, nil -} - -func parseFallbackIPCIDR(ips []string) ([]netip.Prefix, error) { - var ipNets []netip.Prefix - - for idx, ip := range ips { - ipnet, err := netip.ParsePrefix(ip) - if err != nil { - return nil, fmt.Errorf("DNS FallbackIP[%d] format error: %s", idx, err.Error()) - } - ipNets = append(ipNets, ipnet) - } - - return ipNets, nil -} - -func parseFallbackGeoSite(countries []string, rules []C.Rule) ([]router.DomainMatcher, error) { - var sites []router.DomainMatcher - if len(countries) > 0 { - if err := geodata.InitGeoSite(); err != nil { - return nil, fmt.Errorf("can't initial GeoSite: %s", err) - } - log.Warnln("replace fallback-filter.geosite with nameserver-policy, it will be removed in the future") - } - - for _, country := range countries { - found := false - for _, rule := range rules { - if rule.RuleType() == C.GEOSITE { - if strings.EqualFold(country, rule.Payload()) { - found = true - sites = append(sites, rule.(C.RuleGeoSite).GetDomainMatcher()) - log.Infoln("Start initial GeoSite dns fallback filter from rule `%s`", country) - } - } - } - - if !found { - matcher, recordsCount, err := geodata.LoadGeoSiteMatcher(country) + rule, err := parseDomainRuleSet(domainSetName, ruleProviders) if err != nil { return nil, err } - - sites = append(sites, matcher) - - log.Infoln("Start initial GeoSite dns fallback filter `%s`, records: %d", country, recordsCount) + policy[idx] = dns.Policy{Rule: rule, NameServers: nameservers} + } else if strings.HasPrefix(domain, "geosite:") { + country := domain[8:] + rule, err := parseGEOSITE(country, rules) + if err != nil { + return nil, err + } + policy[idx] = dns.Policy{Rule: rule, NameServers: nameservers} + } else { + if _, valid := trie.ValidAndSplitDomain(domain); !valid { + return nil, fmt.Errorf("DNS ResoverRule invalid domain: %s", domain) + } } } - return sites, nil + + return policy, nil } func paresNTP(rawCfg *RawConfig) *NTP { @@ -1357,10 +1303,6 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul IPv6: cfg.IPv6, UseSystemHosts: cfg.UseSystemHosts, EnhancedMode: cfg.EnhancedMode, - FallbackFilter: FallbackFilter{ - IPCIDR: []netip.Prefix{}, - GeoSite: []router.DomainMatcher{}, - }, } var err error if dnsCfg.NameServer, err = parseNameServer(cfg.NameServer, cfg.RespectRules, cfg.PreferH3); err != nil { @@ -1371,7 +1313,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul return nil, err } - if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, ruleProviders, cfg.RespectRules, cfg.PreferH3); err != nil { + if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, rules, ruleProviders, cfg.RespectRules, cfg.PreferH3); err != nil { return nil, err } @@ -1438,18 +1380,51 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul dnsCfg.FakeIPRange = pool } + var rule C.Rule if len(cfg.Fallback) != 0 { - dnsCfg.FallbackFilter.GeoIP = cfg.FallbackFilter.GeoIP - dnsCfg.FallbackFilter.GeoIPCode = cfg.FallbackFilter.GeoIPCode - if fallbackip, err := parseFallbackIPCIDR(cfg.FallbackFilter.IPCIDR); err == nil { - dnsCfg.FallbackFilter.IPCIDR = fallbackip + if cfg.FallbackFilter.GeoIP { + rule, err = RC.NewGEOIP(cfg.FallbackFilter.GeoIPCode, "", false, true) + if err != nil { + return nil, fmt.Errorf("load GeoIP dns fallback filter error, %w", err) + } + dnsCfg.FallbackIPFilter = append(dnsCfg.FallbackIPFilter, rule) } - dnsCfg.FallbackFilter.Domain = cfg.FallbackFilter.Domain - fallbackGeoSite, err := parseFallbackGeoSite(cfg.FallbackFilter.GeoSite, rules) - if err != nil { - return nil, fmt.Errorf("load GeoSite dns fallback filter error, %w", err) + if len(cfg.FallbackFilter.IPCIDR) > 0 { + cidrSet := cidr.NewIpCidrSet() + for idx, ipcidr := range cfg.FallbackFilter.IPCIDR { + err = cidrSet.AddIpCidrForString(ipcidr) + if err != nil { + return nil, fmt.Errorf("DNS FallbackIP[%d] format error: %w", idx, err) + } + } + err = cidrSet.Merge() + if err != nil { + return nil, err + } + rule = RP.NewIpCidrSet(cidrSet, "") + dnsCfg.FallbackIPFilter = append(dnsCfg.FallbackIPFilter, rule) + } + if len(cfg.FallbackFilter.Domain) > 0 { + domainTrie := trie.New[struct{}]() + for idx, domain := range cfg.FallbackFilter.Domain { + err = domainTrie.Insert(domain, struct{}{}) + if err != nil { + return nil, fmt.Errorf("DNS FallbackDomain[%d] format error: %w", idx, err) + } + } + rule = RP.NewDomainSet(domainTrie.NewDomainSet(), "") + dnsCfg.FallbackIPFilter = append(dnsCfg.FallbackIPFilter, rule) + } + if len(cfg.FallbackFilter.GeoSite) > 0 { + log.Warnln("replace fallback-filter.geosite with nameserver-policy, it will be removed in the future") + for idx, geoSite := range cfg.FallbackFilter.GeoSite { + rule, err = parseGEOSITE(geoSite, rules) + if err != nil { + return nil, fmt.Errorf("DNS FallbackGeosite[%d] format error: %w", idx, err) + } + dnsCfg.FallbackIPFilter = append(dnsCfg.FallbackIPFilter, rule) + } } - dnsCfg.FallbackFilter.GeoSite = fallbackGeoSite } if cfg.UseHosts { @@ -1636,44 +1611,21 @@ func parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], rules subkeys = subkeys[1:] subkeys = strings.Split(subkeys[0], ",") for _, country := range subkeys { - found := false - for _, rule = range rules { - if rule.RuleType() == C.GEOSITE { - if strings.EqualFold(country, rule.Payload()) { - found = true - domainRules = append(domainRules, rule) - } - } - } - if !found { - rule, err = RC.NewGEOSITE(country, "") - if err != nil { - return nil, err - } - domainRules = append(domainRules, rule) + rule, err = parseGEOSITE(country, rules) + if err != nil { + return nil, err } + domainRules = append(domainRules, rule) } } else if strings.Contains(domainLower, "rule-set:") { subkeys := strings.Split(domain, ":") subkeys = subkeys[1:] subkeys = strings.Split(subkeys[0], ",") for _, domainSetName := range subkeys { - if rp, ok := ruleProviders[domainSetName]; !ok { - return nil, fmt.Errorf("not found rule-set: %s", domainSetName) - } else { - switch rp.Behavior() { - case providerTypes.IPCIDR: - return nil, fmt.Errorf("rule provider type error, except domain,actual %s", rp.Behavior()) - case providerTypes.Classical: - log.Warnln("%s provider is %s, only matching it contain domain rule", rp.Name(), rp.Behavior()) - default: - } - } - rule, err = RP.NewRuleSet(domainSetName, "", true) + rule, err = parseDomainRuleSet(domainSetName, ruleProviders) if err != nil { return nil, err } - domainRules = append(domainRules, rule) } } else { @@ -1692,3 +1644,29 @@ func parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], rules } return } + +func parseDomainRuleSet(domainSetName string, ruleProviders map[string]providerTypes.RuleProvider) (C.Rule, error) { + if rp, ok := ruleProviders[domainSetName]; !ok { + return nil, fmt.Errorf("not found rule-set: %s", domainSetName) + } else { + switch rp.Behavior() { + case providerTypes.IPCIDR: + return nil, fmt.Errorf("rule provider type error, except domain,actual %s", rp.Behavior()) + case providerTypes.Classical: + log.Warnln("%s provider is %s, only matching it contain domain rule", rp.Name(), rp.Behavior()) + default: + } + } + return RP.NewRuleSet(domainSetName, "", true) +} + +func parseGEOSITE(country string, rules []C.Rule) (C.Rule, error) { + for _, rule := range rules { + if rule.RuleType() == C.GEOSITE { + if strings.EqualFold(country, rule.Payload()) { + return rule, nil + } + } + } + return RC.NewGEOSITE(country, "") +} diff --git a/constant/rule.go b/constant/rule.go index f9f987e6..30cb2f8e 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -28,6 +28,7 @@ const ( ProcessPathRegex RuleSet DomainSet + IpCidrSet Network Uid SubRules @@ -93,6 +94,8 @@ func (rt RuleType) String() string { return "RuleSet" case DomainSet: return "DomainSet" + case IpCidrSet: + return "IpCidrSet" case Network: return "Network" case DSCP: @@ -121,3 +124,8 @@ type Rule interface { ShouldFindProcess() bool ProviderNames() []string } + +type RuleGroup interface { + Rule + GetRecodeSize() int +} diff --git a/constant/rule_extra.go b/constant/rule_extra.go deleted file mode 100644 index b4ba65d9..00000000 --- a/constant/rule_extra.go +++ /dev/null @@ -1,17 +0,0 @@ -package constant - -import ( - "github.com/metacubex/mihomo/component/geodata/router" -) - -type RuleGeoSite interface { - GetDomainMatcher() router.DomainMatcher -} - -type RuleGeoIP interface { - GetIPMatcher() *router.GeoIPMatcher -} - -type RuleGroup interface { - GetRecodeSize() int -} diff --git a/dns/filters.go b/dns/filters.go deleted file mode 100644 index 138f3429..00000000 --- a/dns/filters.go +++ /dev/null @@ -1,102 +0,0 @@ -package dns - -import ( - "net/netip" - "strings" - - "github.com/metacubex/mihomo/component/geodata" - "github.com/metacubex/mihomo/component/geodata/router" - "github.com/metacubex/mihomo/component/mmdb" - "github.com/metacubex/mihomo/component/trie" - C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/log" -) - -type fallbackIPFilter interface { - Match(netip.Addr) bool -} - -type geoipFilter struct { - code string -} - -var geoIPMatcher *router.GeoIPMatcher - -func (gf *geoipFilter) Match(ip netip.Addr) bool { - if !C.GeodataMode { - codes := mmdb.IPInstance().LookupCode(ip.AsSlice()) - for _, code := range codes { - if !strings.EqualFold(code, gf.code) && !ip.IsPrivate() { - return true - } - } - return false - } - - if geoIPMatcher == nil { - var err error - geoIPMatcher, _, err = geodata.LoadGeoIPMatcher("CN") - if err != nil { - log.Errorln("[GeoIPFilter] LoadGeoIPMatcher error: %s", err.Error()) - return false - } - } - return !geoIPMatcher.Match(ip) -} - -type ipnetFilter struct { - ipnet netip.Prefix -} - -func (inf *ipnetFilter) Match(ip netip.Addr) bool { - return inf.ipnet.Contains(ip) -} - -type fallbackDomainFilter interface { - Match(domain string) bool -} - -type domainFilter struct { - tree *trie.DomainTrie[struct{}] -} - -func NewDomainFilter(domains []string) *domainFilter { - df := domainFilter{tree: trie.New[struct{}]()} - for _, domain := range domains { - _ = df.tree.Insert(domain, struct{}{}) - } - df.tree.Optimize() - return &df -} - -func (df *domainFilter) Match(domain string) bool { - return df.tree.Search(domain) != nil -} - -type geoSiteFilter struct { - matchers []router.DomainMatcher -} - -func NewGeoSite(group string) (fallbackDomainFilter, error) { - if err := geodata.InitGeoSite(); err != nil { - log.Errorln("can't initial GeoSite: %s", err) - return nil, err - } - matcher, _, err := geodata.LoadGeoSiteMatcher(group) - if err != nil { - return nil, err - } - filter := &geoSiteFilter{ - matchers: []router.DomainMatcher{matcher}, - } - return filter, nil -} - -func (gsf *geoSiteFilter) Match(domain string) bool { - for _, matcher := range gsf.matchers { - if matcher.ApplyDomain(domain) { - return true - } - } - return false -} diff --git a/dns/policy.go b/dns/policy.go index fc60401b..d872286c 100644 --- a/dns/policy.go +++ b/dns/policy.go @@ -3,7 +3,6 @@ package dns import ( "github.com/metacubex/mihomo/component/trie" C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/constant/provider" ) type dnsPolicy interface { @@ -22,32 +21,14 @@ func (p domainTriePolicy) Match(domain string) []dnsClient { return nil } -type geositePolicy struct { - matcher fallbackDomainFilter - inverse bool +type domainRulePolicy struct { + rule C.Rule dnsClients []dnsClient } -func (p geositePolicy) Match(domain string) []dnsClient { - matched := p.matcher.Match(domain) - if matched != p.inverse { +func (p domainRulePolicy) Match(domain string) []dnsClient { + if ok, _ := p.rule.Match(&C.Metadata{Host: domain}); ok { return p.dnsClients } return nil } - -type domainSetPolicy struct { - tunnel provider.Tunnel - name string - dnsClients []dnsClient -} - -func (p domainSetPolicy) Match(domain string) []dnsClient { - if ruleProvider, ok := p.tunnel.RuleProviders()[p.name]; ok { - metadata := &C.Metadata{Host: domain} - if ok := ruleProvider.Match(metadata); ok { - return p.dnsClients - } - } - return nil -} diff --git a/dns/resolver.go b/dns/resolver.go index fc228761..7b9aafd0 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -4,13 +4,11 @@ import ( "context" "errors" "net/netip" - "strings" "time" "github.com/metacubex/mihomo/common/arc" "github.com/metacubex/mihomo/common/lru" "github.com/metacubex/mihomo/component/fakeip" - "github.com/metacubex/mihomo/component/geodata/router" "github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/component/trie" C "github.com/metacubex/mihomo/constant" @@ -19,7 +17,6 @@ import ( D "github.com/miekg/dns" "github.com/samber/lo" - orderedmap "github.com/wk8/go-ordered-map/v2" "golang.org/x/exp/maps" "golang.org/x/sync/singleflight" ) @@ -45,8 +42,8 @@ type Resolver struct { hosts *trie.DomainTrie[resolver.HostValue] main []dnsClient fallback []dnsClient - fallbackDomainFilters []fallbackDomainFilter - fallbackIPFilters []fallbackIPFilter + fallbackDomainFilters []C.Rule + fallbackIPFilters []C.Rule group singleflight.Group cache dnsCache policy []dnsPolicy @@ -122,7 +119,7 @@ func (r *Resolver) LookupIPv6(ctx context.Context, host string) ([]netip.Addr, e func (r *Resolver) shouldIPFallback(ip netip.Addr) bool { for _, filter := range r.fallbackIPFilters { - if filter.Match(ip) { + if ok, _ := filter.Match(&C.Metadata{DstIP: ip}); ok { return true } } @@ -277,7 +274,7 @@ func (r *Resolver) shouldOnlyQueryFallback(m *D.Msg) bool { } for _, df := range r.fallbackDomainFilters { - if df.Match(domain) { + if ok, _ := df.Match(&C.Metadata{Host: domain}); ok { return true } } @@ -398,27 +395,26 @@ func (ns NameServer) Equal(ns2 NameServer) bool { return false } -type FallbackFilter struct { - GeoIP bool - GeoIPCode string - IPCIDR []netip.Prefix - Domain []string - GeoSite []router.DomainMatcher +type Policy struct { + Domain string + Rule C.Rule + NameServers []NameServer } type Config struct { - Main, Fallback []NameServer - Default []NameServer - ProxyServer []NameServer - IPv6 bool - IPv6Timeout uint - EnhancedMode C.DNSMode - FallbackFilter FallbackFilter - Pool *fakeip.Pool - Hosts *trie.DomainTrie[resolver.HostValue] - Policy *orderedmap.OrderedMap[string, []NameServer] - Tunnel provider.Tunnel - CacheAlgorithm string + Main, Fallback []NameServer + Default []NameServer + ProxyServer []NameServer + IPv6 bool + IPv6Timeout uint + EnhancedMode C.DNSMode + FallbackIPFilter []C.Rule + FallbackDomainFilter []C.Rule + Pool *fakeip.Pool + Hosts *trie.DomainTrie[resolver.HostValue] + Policy []Policy + Tunnel provider.Tunnel + CacheAlgorithm string } func NewResolver(config Config) *Resolver { @@ -482,7 +478,7 @@ func NewResolver(config Config) *Resolver { r.proxyServer = cacheTransform(config.ProxyServer) } - if config.Policy.Len() != 0 { + if len(config.Policy) != 0 { r.policy = make([]dnsPolicy, 0) var triePolicy *trie.DomainTrie[[]dnsClient] @@ -497,75 +493,20 @@ func NewResolver(config Config) *Resolver { } } - for pair := config.Policy.Oldest(); pair != nil; pair = pair.Next() { - domain, nameserver := pair.Key, pair.Value - - if temp := strings.Split(domain, ":"); len(temp) == 2 { - prefix := temp[0] - key := temp[1] - switch prefix { - case "rule-set": - if _, ok := config.Tunnel.RuleProviders()[key]; ok { - log.Debugln("Adding rule-set policy: %s ", key) - insertPolicy(domainSetPolicy{ - tunnel: config.Tunnel, - name: key, - dnsClients: cacheTransform(nameserver), - }) - continue - } else { - log.Warnln("Can't found ruleset policy: %s", key) - } - case "geosite": - inverse := false - if strings.HasPrefix(key, "!") { - inverse = true - key = key[1:] - } - log.Debugln("Adding geosite policy: %s inversed %t", key, inverse) - matcher, err := NewGeoSite(key) - if err != nil { - log.Warnln("adding geosite policy %s error: %s", key, err) - continue - } - insertPolicy(geositePolicy{ - matcher: matcher, - inverse: inverse, - dnsClients: cacheTransform(nameserver), - }) - continue // skip triePolicy new + for _, policy := range config.Policy { + if policy.Rule != nil { + insertPolicy(domainRulePolicy{rule: policy.Rule, dnsClients: cacheTransform(policy.NameServers)}) + } else { + if triePolicy == nil { + triePolicy = trie.New[[]dnsClient]() } + _ = triePolicy.Insert(policy.Domain, cacheTransform(policy.NameServers)) } - if triePolicy == nil { - triePolicy = trie.New[[]dnsClient]() - } - _ = triePolicy.Insert(domain, cacheTransform(nameserver)) } insertPolicy(nil) } - - fallbackIPFilters := []fallbackIPFilter{} - if config.FallbackFilter.GeoIP { - fallbackIPFilters = append(fallbackIPFilters, &geoipFilter{ - code: config.FallbackFilter.GeoIPCode, - }) - } - for _, ipnet := range config.FallbackFilter.IPCIDR { - fallbackIPFilters = append(fallbackIPFilters, &ipnetFilter{ipnet: ipnet}) - } - r.fallbackIPFilters = fallbackIPFilters - - fallbackDomainFilters := []fallbackDomainFilter{} - if len(config.FallbackFilter.Domain) != 0 { - fallbackDomainFilters = append(fallbackDomainFilters, NewDomainFilter(config.FallbackFilter.Domain)) - } - - if len(config.FallbackFilter.GeoSite) != 0 { - fallbackDomainFilters = append(fallbackDomainFilters, &geoSiteFilter{ - matchers: config.FallbackFilter.GeoSite, - }) - } - r.fallbackDomainFilters = fallbackDomainFilters + r.fallbackIPFilters = config.FallbackIPFilter + r.fallbackDomainFilters = config.FallbackDomainFilter return r } diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 1e578190..6504e96a 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -222,25 +222,20 @@ func updateDNS(c *config.DNS, generalIPv6 bool) { return } cfg := dns.Config{ - Main: c.NameServer, - Fallback: c.Fallback, - IPv6: c.IPv6 && generalIPv6, - IPv6Timeout: c.IPv6Timeout, - EnhancedMode: c.EnhancedMode, - Pool: c.FakeIPRange, - Hosts: c.Hosts, - FallbackFilter: dns.FallbackFilter{ - GeoIP: c.FallbackFilter.GeoIP, - GeoIPCode: c.FallbackFilter.GeoIPCode, - IPCIDR: c.FallbackFilter.IPCIDR, - Domain: c.FallbackFilter.Domain, - GeoSite: c.FallbackFilter.GeoSite, - }, - Default: c.DefaultNameserver, - Policy: c.NameServerPolicy, - ProxyServer: c.ProxyServerNameserver, - Tunnel: tunnel.Tunnel, - CacheAlgorithm: c.CacheAlgorithm, + Main: c.NameServer, + Fallback: c.Fallback, + IPv6: c.IPv6 && generalIPv6, + IPv6Timeout: c.IPv6Timeout, + EnhancedMode: c.EnhancedMode, + Pool: c.FakeIPRange, + Hosts: c.Hosts, + FallbackIPFilter: c.FallbackIPFilter, + FallbackDomainFilter: c.FallbackDomainFilter, + Default: c.DefaultNameserver, + Policy: c.NameServerPolicy, + ProxyServer: c.ProxyServerNameserver, + Tunnel: tunnel.Tunnel, + CacheAlgorithm: c.CacheAlgorithm, } r := dns.NewResolver(cfg) diff --git a/rules/provider/domain_set.go b/rules/provider/domain_set.go index 372b438e..573dd105 100644 --- a/rules/provider/domain_set.go +++ b/rules/provider/domain_set.go @@ -19,10 +19,7 @@ func (d *DomainSet) RuleType() C.RuleType { } func (d *DomainSet) Match(metadata *C.Metadata) (bool, string) { - if d.domainSet == nil { - return false, "" - } - return d.domainSet.Has(metadata.RuleHost()), d.adapter + return d.domainStrategy.Match(metadata), d.adapter } func (d *DomainSet) Adapter() string { diff --git a/rules/provider/ipcidr_set.go b/rules/provider/ipcidr_set.go new file mode 100644 index 00000000..348b6ab0 --- /dev/null +++ b/rules/provider/ipcidr_set.go @@ -0,0 +1,40 @@ +package provider + +import ( + "github.com/metacubex/mihomo/component/cidr" + C "github.com/metacubex/mihomo/constant" +) + +type IpCidrSet struct { + *ipcidrStrategy + adapter string +} + +func (d *IpCidrSet) ProviderNames() []string { + return nil +} + +func (d *IpCidrSet) RuleType() C.RuleType { + return C.IpCidrSet +} + +func (d *IpCidrSet) Match(metadata *C.Metadata) (bool, string) { + return d.ipcidrStrategy.Match(metadata), d.adapter +} + +func (d *IpCidrSet) Adapter() string { + return d.adapter +} + +func (d *IpCidrSet) Payload() string { + return "" +} + +func NewIpCidrSet(cidrSet *cidr.IpCidrSet, adapter string) *IpCidrSet { + return &IpCidrSet{ + ipcidrStrategy: &ipcidrStrategy{cidrSet: cidrSet}, + adapter: adapter, + } +} + +var _ C.Rule = (*IpCidrSet)(nil) From 4fedfc47b0e968b4b47d102826c463c808172618 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 16 Aug 2024 09:19:18 +0800 Subject: [PATCH 170/311] chore: update geo unneeded reload whole config --- common/singleflight/singleflight.go | 224 ++++++++++++++++++++++++++ component/geodata/attr.go | 10 +- component/geodata/router/condition.go | 161 +++++++----------- component/geodata/utils.go | 149 +++++++++-------- component/updater/update_geo.go | 6 +- dns/resolver.go | 19 +-- go.mod | 2 +- hub/route/configs.go | 12 -- main.go | 12 +- rules/common/geoip.go | 64 ++++---- rules/common/geosite.go | 42 +++-- 11 files changed, 447 insertions(+), 254 deletions(-) create mode 100644 common/singleflight/singleflight.go diff --git a/common/singleflight/singleflight.go b/common/singleflight/singleflight.go new file mode 100644 index 00000000..e31c4eb6 --- /dev/null +++ b/common/singleflight/singleflight.go @@ -0,0 +1,224 @@ +// copy and modify from "golang.org/x/sync/singleflight" + +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package singleflight provides a duplicate function call suppression +// mechanism. +package singleflight + +import ( + "bytes" + "errors" + "fmt" + "runtime" + "runtime/debug" + "sync" +) + +// errGoexit indicates the runtime.Goexit was called in +// the user given function. +var errGoexit = errors.New("runtime.Goexit was called") + +// A panicError is an arbitrary value recovered from a panic +// with the stack trace during the execution of given function. +type panicError struct { + value interface{} + stack []byte +} + +// Error implements error interface. +func (p *panicError) Error() string { + return fmt.Sprintf("%v\n\n%s", p.value, p.stack) +} + +func (p *panicError) Unwrap() error { + err, ok := p.value.(error) + if !ok { + return nil + } + + return err +} + +func newPanicError(v interface{}) error { + stack := debug.Stack() + + // The first line of the stack trace is of the form "goroutine N [status]:" + // but by the time the panic reaches Do the goroutine may no longer exist + // and its status will have changed. Trim out the misleading line. + if line := bytes.IndexByte(stack[:], '\n'); line >= 0 { + stack = stack[line+1:] + } + return &panicError{value: v, stack: stack} +} + +// call is an in-flight or completed singleflight.Do call +type call[T any] struct { + wg sync.WaitGroup + + // These fields are written once before the WaitGroup is done + // and are only read after the WaitGroup is done. + val T + err error + + // These fields are read and written with the singleflight + // mutex held before the WaitGroup is done, and are read but + // not written after the WaitGroup is done. + dups int + chans []chan<- Result[T] +} + +// Group represents a class of work and forms a namespace in +// which units of work can be executed with duplicate suppression. +type Group[T any] struct { + mu sync.Mutex // protects m + m map[string]*call[T] // lazily initialized + + StoreResult bool +} + +// Result holds the results of Do, so they can be passed +// on a channel. +type Result[T any] struct { + Val T + Err error + Shared bool +} + +// Do executes and returns the results of the given function, making +// sure that only one execution is in-flight for a given key at a +// time. If a duplicate comes in, the duplicate caller waits for the +// original to complete and receives the same results. +// The return value shared indicates whether v was given to multiple callers. +func (g *Group[T]) Do(key string, fn func() (T, error)) (v T, err error, shared bool) { + g.mu.Lock() + if g.m == nil { + g.m = make(map[string]*call[T]) + } + if c, ok := g.m[key]; ok { + c.dups++ + g.mu.Unlock() + c.wg.Wait() + + if e, ok := c.err.(*panicError); ok { + panic(e) + } else if c.err == errGoexit { + runtime.Goexit() + } + return c.val, c.err, true + } + c := new(call[T]) + c.wg.Add(1) + g.m[key] = c + g.mu.Unlock() + + g.doCall(c, key, fn) + return c.val, c.err, c.dups > 0 +} + +// DoChan is like Do but returns a channel that will receive the +// results when they are ready. +// +// The returned channel will not be closed. +func (g *Group[T]) DoChan(key string, fn func() (T, error)) <-chan Result[T] { + ch := make(chan Result[T], 1) + g.mu.Lock() + if g.m == nil { + g.m = make(map[string]*call[T]) + } + if c, ok := g.m[key]; ok { + c.dups++ + c.chans = append(c.chans, ch) + g.mu.Unlock() + return ch + } + c := &call[T]{chans: []chan<- Result[T]{ch}} + c.wg.Add(1) + g.m[key] = c + g.mu.Unlock() + + go g.doCall(c, key, fn) + + return ch +} + +// doCall handles the single call for a key. +func (g *Group[T]) doCall(c *call[T], key string, fn func() (T, error)) { + normalReturn := false + recovered := false + + // use double-defer to distinguish panic from runtime.Goexit, + // more details see https://golang.org/cl/134395 + defer func() { + // the given function invoked runtime.Goexit + if !normalReturn && !recovered { + c.err = errGoexit + } + + g.mu.Lock() + defer g.mu.Unlock() + c.wg.Done() + if g.m[key] == c && !g.StoreResult { + delete(g.m, key) + } + + if e, ok := c.err.(*panicError); ok { + // In order to prevent the waiting channels from being blocked forever, + // needs to ensure that this panic cannot be recovered. + if len(c.chans) > 0 { + go panic(e) + select {} // Keep this goroutine around so that it will appear in the crash dump. + } else { + panic(e) + } + } else if c.err == errGoexit { + // Already in the process of goexit, no need to call again + } else { + // Normal return + for _, ch := range c.chans { + ch <- Result[T]{c.val, c.err, c.dups > 0} + } + } + }() + + func() { + defer func() { + if !normalReturn { + // Ideally, we would wait to take a stack trace until we've determined + // whether this is a panic or a runtime.Goexit. + // + // Unfortunately, the only way we can distinguish the two is to see + // whether the recover stopped the goroutine from terminating, and by + // the time we know that, the part of the stack trace relevant to the + // panic has been discarded. + if r := recover(); r != nil { + c.err = newPanicError(r) + } + } + }() + + c.val, c.err = fn() + normalReturn = true + }() + + if !normalReturn { + recovered = true + } +} + +// Forget tells the singleflight to forget about a key. Future calls +// to Do for this key will call the function rather than waiting for +// an earlier call to complete. +func (g *Group[T]) Forget(key string) { + g.mu.Lock() + delete(g.m, key) + g.mu.Unlock() +} + +func (g *Group[T]) Reset() { + g.mu.Lock() + g.m = nil + g.mu.Unlock() +} diff --git a/component/geodata/attr.go b/component/geodata/attr.go index a9742aca..2fd41ad6 100644 --- a/component/geodata/attr.go +++ b/component/geodata/attr.go @@ -7,7 +7,7 @@ import ( ) type AttributeList struct { - matcher []AttributeMatcher + matcher []BooleanMatcher } func (al *AttributeList) Match(domain *router.Domain) bool { @@ -23,6 +23,14 @@ func (al *AttributeList) IsEmpty() bool { return len(al.matcher) == 0 } +func (al *AttributeList) String() string { + matcher := make([]string, len(al.matcher)) + for i, match := range al.matcher { + matcher[i] = string(match) + } + return strings.Join(matcher, ",") +} + func parseAttrs(attrs []string) *AttributeList { al := new(AttributeList) for _, attr := range attrs { diff --git a/component/geodata/router/condition.go b/component/geodata/router/condition.go index 5261d2fe..fb47e4a4 100644 --- a/component/geodata/router/condition.go +++ b/component/geodata/router/condition.go @@ -33,12 +33,13 @@ func domainToMatcher(domain *Domain) (strmatcher.Matcher, error) { type DomainMatcher interface { ApplyDomain(string) bool + Count() int } type succinctDomainMatcher struct { set *trie.DomainSet otherMatchers []strmatcher.Matcher - not bool + count int } func (m *succinctDomainMatcher) ApplyDomain(domain string) bool { @@ -51,16 +52,17 @@ func (m *succinctDomainMatcher) ApplyDomain(domain string) bool { } } } - if m.not { - isMatched = !isMatched - } return isMatched } -func NewSuccinctMatcherGroup(domains []*Domain, not bool) (DomainMatcher, error) { +func (m *succinctDomainMatcher) Count() int { + return m.count +} + +func NewSuccinctMatcherGroup(domains []*Domain) (DomainMatcher, error) { t := trie.New[struct{}]() m := &succinctDomainMatcher{ - not: not, + count: len(domains), } for _, d := range domains { switch d.Type { @@ -90,10 +92,10 @@ func NewSuccinctMatcherGroup(domains []*Domain, not bool) (DomainMatcher, error) type v2rayDomainMatcher struct { matchers strmatcher.IndexMatcher - not bool + count int } -func NewMphMatcherGroup(domains []*Domain, not bool) (DomainMatcher, error) { +func NewMphMatcherGroup(domains []*Domain) (DomainMatcher, error) { g := strmatcher.NewMphMatcherGroup() for _, d := range domains { matcherType, f := matcherTypeMap[d.Type] @@ -108,119 +110,80 @@ func NewMphMatcherGroup(domains []*Domain, not bool) (DomainMatcher, error) { g.Build() return &v2rayDomainMatcher{ matchers: g, - not: not, + count: len(domains), }, nil } func (m *v2rayDomainMatcher) ApplyDomain(domain string) bool { - isMatched := len(m.matchers.Match(strings.ToLower(domain))) > 0 - if m.not { - isMatched = !isMatched - } - return isMatched + return len(m.matchers.Match(strings.ToLower(domain))) > 0 } -type GeoIPMatcher struct { - countryCode string - reverseMatch bool - cidrSet *cidr.IpCidrSet +func (m *v2rayDomainMatcher) Count() int { + return m.count } -func (m *GeoIPMatcher) Init(cidrs []*CIDR) error { - for _, cidr := range cidrs { - addr, ok := netip.AddrFromSlice(cidr.Ip) - if !ok { - return fmt.Errorf("error when loading GeoIP: invalid IP: %s", cidr.Ip) - } - err := m.cidrSet.AddIpCidr(netip.PrefixFrom(addr, int(cidr.Prefix))) - if err != nil { - return fmt.Errorf("error when loading GeoIP: %w", err) - } - } - return m.cidrSet.Merge() +type notDomainMatcher struct { + DomainMatcher } -func (m *GeoIPMatcher) SetReverseMatch(isReverseMatch bool) { - m.reverseMatch = isReverseMatch +func (m notDomainMatcher) ApplyDomain(domain string) bool { + return !m.DomainMatcher.ApplyDomain(domain) +} + +func NewNotDomainMatcherGroup(matcher DomainMatcher) DomainMatcher { + return notDomainMatcher{matcher} +} + +type IPMatcher interface { + Match(ip netip.Addr) bool + Count() int +} + +type geoIPMatcher struct { + cidrSet *cidr.IpCidrSet + count int } // Match returns true if the given ip is included by the GeoIP. -func (m *GeoIPMatcher) Match(ip netip.Addr) bool { - match := m.cidrSet.IsContain(ip) - if m.reverseMatch { - return !match +func (m *geoIPMatcher) Match(ip netip.Addr) bool { + return m.cidrSet.IsContain(ip) +} + +func (m *geoIPMatcher) Count() int { + return m.count +} + +func NewGeoIPMatcher(cidrList []*CIDR) (IPMatcher, error) { + m := &geoIPMatcher{ + cidrSet: cidr.NewIpCidrSet(), + count: len(cidrList), } - return match -} - -// GeoIPMatcherContainer is a container for GeoIPMatchers. It keeps unique copies of GeoIPMatcher by country code. -type GeoIPMatcherContainer struct { - matchers []*GeoIPMatcher -} - -// Add adds a new GeoIP set into the container. -// If the country code of GeoIP is not empty, GeoIPMatcherContainer will try to find an existing one, instead of adding a new one. -func (c *GeoIPMatcherContainer) Add(geoip *GeoIP) (*GeoIPMatcher, error) { - if len(geoip.CountryCode) > 0 { - for _, m := range c.matchers { - if m.countryCode == geoip.CountryCode && m.reverseMatch == geoip.ReverseMatch { - return m, nil - } + for _, cidr := range cidrList { + addr, ok := netip.AddrFromSlice(cidr.Ip) + if !ok { + return nil, fmt.Errorf("error when loading GeoIP: invalid IP: %s", cidr.Ip) + } + err := m.cidrSet.AddIpCidr(netip.PrefixFrom(addr, int(cidr.Prefix))) + if err != nil { + return nil, fmt.Errorf("error when loading GeoIP: %w", err) } } - - m := &GeoIPMatcher{ - countryCode: geoip.CountryCode, - reverseMatch: geoip.ReverseMatch, - cidrSet: cidr.NewIpCidrSet(), - } - if err := m.Init(geoip.Cidr); err != nil { - return nil, err - } - if len(geoip.CountryCode) > 0 { - c.matchers = append(c.matchers, m) - } - return m, nil -} - -var globalGeoIPContainer GeoIPMatcherContainer - -type MultiGeoIPMatcher struct { - matchers []*GeoIPMatcher -} - -func NewGeoIPMatcher(geoip *GeoIP) (*GeoIPMatcher, error) { - matcher, err := globalGeoIPContainer.Add(geoip) + err := m.cidrSet.Merge() if err != nil { return nil, err } - return matcher, nil + return m, nil } -func (m *MultiGeoIPMatcher) ApplyIp(ip netip.Addr) bool { - for _, matcher := range m.matchers { - if matcher.Match(ip) { - return true - } - } - - return false +type notIPMatcher struct { + IPMatcher } -func NewMultiGeoIPMatcher(geoips []*GeoIP) (*MultiGeoIPMatcher, error) { - var matchers []*GeoIPMatcher - for _, geoip := range geoips { - matcher, err := globalGeoIPContainer.Add(geoip) - if err != nil { - return nil, err - } - matchers = append(matchers, matcher) - } - - matcher := &MultiGeoIPMatcher{ - matchers: matchers, - } - - return matcher, nil +func (m notIPMatcher) Match(ip netip.Addr) bool { + return !m.IPMatcher.Match(ip) +} + +func NewNotIpMatcherGroup(matcher IPMatcher) IPMatcher { + return notIPMatcher{matcher} } diff --git a/component/geodata/utils.go b/component/geodata/utils.go index 981d7eba..a16e255e 100644 --- a/component/geodata/utils.go +++ b/component/geodata/utils.go @@ -5,8 +5,7 @@ import ( "fmt" "strings" - "golang.org/x/sync/singleflight" - + "github.com/metacubex/mihomo/common/singleflight" "github.com/metacubex/mihomo/component/geodata/router" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" @@ -71,21 +70,22 @@ func SetSiteMatcher(newMatcher string) { func Verify(name string) error { switch name { case C.GeositeName: - _, _, err := LoadGeoSiteMatcher("CN") + _, err := LoadGeoSiteMatcher("CN") return err case C.GeoipName: - _, _, err := LoadGeoIPMatcher("CN") + _, err := LoadGeoIPMatcher("CN") return err default: return fmt.Errorf("not support name") } } -var loadGeoSiteMatcherSF = singleflight.Group{} +var loadGeoSiteMatcherListSF = singleflight.Group[[]*router.Domain]{StoreResult: true} +var loadGeoSiteMatcherSF = singleflight.Group[router.DomainMatcher]{StoreResult: true} -func LoadGeoSiteMatcher(countryCode string) (router.DomainMatcher, int, error) { +func LoadGeoSiteMatcher(countryCode string) (router.DomainMatcher, error) { if countryCode == "" { - return nil, 0, fmt.Errorf("country code could not be empty") + return nil, fmt.Errorf("country code could not be empty") } not := false @@ -97,73 +97,84 @@ func LoadGeoSiteMatcher(countryCode string) (router.DomainMatcher, int, error) { parts := strings.Split(countryCode, "@") if len(parts) == 0 { - return nil, 0, errors.New("empty rule") + return nil, errors.New("empty rule") } listName := strings.TrimSpace(parts[0]) attrVal := parts[1:] + attrs := parseAttrs(attrVal) if listName == "" { - return nil, 0, fmt.Errorf("empty listname in rule: %s", countryCode) + return nil, fmt.Errorf("empty listname in rule: %s", countryCode) } - v, err, shared := loadGeoSiteMatcherSF.Do(listName, func() (interface{}, error) { - geoLoader, err := GetGeoDataLoader(geoLoaderName) + matcherName := listName + if !attrs.IsEmpty() { + matcherName += "@" + attrs.String() + } + matcher, err, shared := loadGeoSiteMatcherSF.Do(matcherName, func() (router.DomainMatcher, error) { + log.Infoln("Load GeoSite rule: %s", matcherName) + domains, err, shared := loadGeoSiteMatcherListSF.Do(listName, func() ([]*router.Domain, error) { + geoLoader, err := GetGeoDataLoader(geoLoaderName) + if err != nil { + return nil, err + } + return geoLoader.LoadGeoSite(listName) + }) if err != nil { + if !shared { + loadGeoSiteMatcherListSF.Forget(listName) // don't store the error result + } return nil, err } - return geoLoader.LoadGeoSite(listName) + + if attrs.IsEmpty() { + if strings.Contains(countryCode, "@") { + log.Warnln("empty attribute list: %s", countryCode) + } + } else { + filteredDomains := make([]*router.Domain, 0, len(domains)) + hasAttrMatched := false + for _, domain := range domains { + if attrs.Match(domain) { + hasAttrMatched = true + filteredDomains = append(filteredDomains, domain) + } + } + if !hasAttrMatched { + log.Warnln("attribute match no rule: geosite: %s", countryCode) + } + domains = filteredDomains + } + + /** + linear: linear algorithm + matcher, err := router.NewDomainMatcher(domains) + mph:minimal perfect hash algorithm + */ + if geoSiteMatcher == "mph" { + return router.NewMphMatcherGroup(domains) + } else { + return router.NewSuccinctMatcherGroup(domains) + } }) if err != nil { if !shared { - loadGeoSiteMatcherSF.Forget(listName) // don't store the error result + loadGeoSiteMatcherSF.Forget(matcherName) // don't store the error result } - return nil, 0, err + return nil, err } - domains := v.([]*router.Domain) - - attrs := parseAttrs(attrVal) - if attrs.IsEmpty() { - if strings.Contains(countryCode, "@") { - log.Warnln("empty attribute list: %s", countryCode) - } - } else { - filteredDomains := make([]*router.Domain, 0, len(domains)) - hasAttrMatched := false - for _, domain := range domains { - if attrs.Match(domain) { - hasAttrMatched = true - filteredDomains = append(filteredDomains, domain) - } - } - if !hasAttrMatched { - log.Warnln("attribute match no rule: geosite: %s", countryCode) - } - domains = filteredDomains + if not { + matcher = router.NewNotDomainMatcherGroup(matcher) } - /** - linear: linear algorithm - matcher, err := router.NewDomainMatcher(domains) - mph:minimal perfect hash algorithm - */ - var matcher router.DomainMatcher - if geoSiteMatcher == "mph" { - matcher, err = router.NewMphMatcherGroup(domains, not) - } else { - matcher, err = router.NewSuccinctMatcherGroup(domains, not) - } - if err != nil { - return nil, 0, err - } - - return matcher, len(domains), nil + return matcher, nil } -var loadGeoIPMatcherSF = singleflight.Group{} +var loadGeoIPMatcherSF = singleflight.Group[router.IPMatcher]{StoreResult: true} -func LoadGeoIPMatcher(country string) (*router.GeoIPMatcher, int, error) { +func LoadGeoIPMatcher(country string) (router.IPMatcher, error) { if len(country) == 0 { - return nil, 0, fmt.Errorf("country code could not be empty") + return nil, fmt.Errorf("country code could not be empty") } not := false @@ -173,35 +184,33 @@ func LoadGeoIPMatcher(country string) (*router.GeoIPMatcher, int, error) { } country = strings.ToLower(country) - v, err, shared := loadGeoIPMatcherSF.Do(country, func() (interface{}, error) { + matcher, err, shared := loadGeoIPMatcherSF.Do(country, func() (router.IPMatcher, error) { + log.Infoln("Load GeoIP rule: %s", country) geoLoader, err := GetGeoDataLoader(geoLoaderName) if err != nil { return nil, err } - return geoLoader.LoadGeoIP(country) + cidrList, err := geoLoader.LoadGeoIP(country) + if err != nil { + return nil, err + } + return router.NewGeoIPMatcher(cidrList) }) if err != nil { if !shared { loadGeoIPMatcherSF.Forget(country) // don't store the error result + log.Warnln("Load GeoIP rule: %s", country) } - return nil, 0, err + return nil, err } - records := v.([]*router.CIDR) - - geoIP := &router.GeoIP{ - CountryCode: country, - Cidr: records, - ReverseMatch: not, + if not { + matcher = router.NewNotIpMatcherGroup(matcher) } - - matcher, err := router.NewGeoIPMatcher(geoIP) - if err != nil { - return nil, 0, err - } - return matcher, len(records), nil + return matcher, nil } func ClearCache() { - loadGeoSiteMatcherSF = singleflight.Group{} - loadGeoIPMatcherSF = singleflight.Group{} + loadGeoSiteMatcherListSF.Reset() + loadGeoSiteMatcherSF.Reset() + loadGeoIPMatcherSF.Reset() } diff --git a/component/updater/update_geo.go b/component/updater/update_geo.go index b07cd315..4d16c128 100644 --- a/component/updater/update_geo.go +++ b/component/updater/update_geo.go @@ -137,7 +137,7 @@ func getUpdateTime() (err error, time time.Time) { return nil, fileInfo.ModTime() } -func RegisterGeoUpdater(onSuccess func()) { +func RegisterGeoUpdater() { if C.GeoUpdateInterval <= 0 { log.Errorln("[GEO] Invalid update interval: %d", C.GeoUpdateInterval) return @@ -159,8 +159,6 @@ func RegisterGeoUpdater(onSuccess func()) { if err := UpdateGeoDatabases(); err != nil { log.Errorln("[GEO] Failed to update GEO database: %s", err.Error()) return - } else { - onSuccess() } } @@ -168,8 +166,6 @@ func RegisterGeoUpdater(onSuccess func()) { log.Infoln("[GEO] updating database every %d hours", C.GeoUpdateInterval) if err := UpdateGeoDatabases(); err != nil { log.Errorln("[GEO] Failed to update GEO database: %s", err.Error()) - } else { - onSuccess() } } }() diff --git a/dns/resolver.go b/dns/resolver.go index 7b9aafd0..3cc7a41e 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -8,6 +8,7 @@ import ( "github.com/metacubex/mihomo/common/arc" "github.com/metacubex/mihomo/common/lru" + "github.com/metacubex/mihomo/common/singleflight" "github.com/metacubex/mihomo/component/fakeip" "github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/component/trie" @@ -18,7 +19,6 @@ import ( D "github.com/miekg/dns" "github.com/samber/lo" "golang.org/x/exp/maps" - "golang.org/x/sync/singleflight" ) type dnsClient interface { @@ -44,7 +44,7 @@ type Resolver struct { fallback []dnsClient fallbackDomainFilters []C.Rule fallbackIPFilters []C.Rule - group singleflight.Group + group singleflight.Group[*D.Msg] cache dnsCache policy []dnsPolicy proxyServer []dnsClient @@ -169,19 +169,20 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M retryNum := 0 retryMax := 3 - fn := func() (result any, err error) { + fn := func() (result *D.Msg, err error) { ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout) // reset timeout in singleflight defer cancel() cache := false defer func() { if err != nil { - result = retryNum + result = &D.Msg{} + result.Opcode = retryNum retryNum++ return } - msg := result.(*D.Msg) + msg := result if cache { // OPT RRs MUST NOT be cached, forwarded, or stored in or loaded from master files. @@ -208,7 +209,7 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M ch := r.group.DoChan(q.String(), fn) - var result singleflight.Result + var result singleflight.Result[*D.Msg] select { case result = <-ch: @@ -221,7 +222,7 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M go func() { // start a retrying monitor in background result := <-ch ret, err, shared := result.Val, result.Err, result.Shared - if err != nil && !shared && ret.(int) < retryMax { // retry + if err != nil && !shared && ret.Opcode < retryMax { // retry r.group.DoChan(q.String(), fn) } }() @@ -230,12 +231,12 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M } ret, err, shared := result.Val, result.Err, result.Shared - if err != nil && !shared && ret.(int) < retryMax { // retry + if err != nil && !shared && ret.Opcode < retryMax { // retry r.group.DoChan(q.String(), fn) } if err == nil { - msg = ret.(*D.Msg) + msg = ret if shared { msg = msg.Copy() } diff --git a/go.mod b/go.mod index 90c25279..bc9fccba 100644 --- a/go.mod +++ b/go.mod @@ -53,7 +53,6 @@ require ( golang.org/x/crypto v0.26.0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 golang.org/x/net v0.28.0 - golang.org/x/sync v0.8.0 golang.org/x/sys v0.23.0 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 @@ -108,6 +107,7 @@ require ( gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect go.uber.org/mock v0.4.0 // indirect golang.org/x/mod v0.19.0 // indirect + golang.org/x/sync v0.8.0 // indirect golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.23.0 // indirect diff --git a/hub/route/configs.go b/hub/route/configs.go index a4dcaa52..c7b340c6 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -408,17 +408,5 @@ func updateGeoDatabases(w http.ResponseWriter, r *http.Request) { return } - cfg, err := executor.ParseWithPath(C.Path.Config()) - if err != nil { - log.Errorln("[GEO] update GEO databases failed: %v", err) - render.Status(r, http.StatusInternalServerError) - render.JSON(w, r, newError("Error parsing configuration")) - return - } - - log.Warnln("[GEO] update GEO databases success, applying config") - - executor.ApplyConfig(cfg, false) - render.NoContent(w, r) } diff --git a/main.go b/main.go index cd903ce6..06a04ca1 100644 --- a/main.go +++ b/main.go @@ -120,17 +120,7 @@ func main() { } if C.GeoAutoUpdate { - updater.RegisterGeoUpdater(func() { - cfg, err := executor.ParseWithPath(C.Path.Config()) - if err != nil { - log.Errorln("[GEO] update GEO databases failed: %v", err) - return - } - - log.Warnln("[GEO] update GEO databases success, applying config") - - executor.ApplyConfig(cfg, false) - }) + updater.RegisterGeoUpdater() } defer executor.Shutdown() diff --git a/rules/common/geoip.go b/rules/common/geoip.go index b50680a4..83925321 100644 --- a/rules/common/geoip.go +++ b/rules/common/geoip.go @@ -1,6 +1,7 @@ package common import ( + "errors" "fmt" "strings" @@ -14,12 +15,11 @@ import ( type GEOIP struct { *Base - country string - adapter string - noResolveIP bool - isSourceIP bool - geoIPMatcher *router.GeoIPMatcher - recodeSize int + country string + adapter string + noResolveIP bool + isSourceIP bool + geodata bool } var _ C.Rule = (*GEOIP)(nil) @@ -78,7 +78,11 @@ func (g *GEOIP) Match(metadata *C.Metadata) (bool, string) { return false, g.adapter } - match := g.geoIPMatcher.Match(ip) + matcher, err := g.GetIPMatcher() + if err != nil { + return false, "" + } + match := matcher.Match(ip) if match && !g.isSourceIP { metadata.DstGeoIP = append(metadata.DstGeoIP, g.country) } @@ -101,12 +105,22 @@ func (g *GEOIP) GetCountry() string { return g.country } -func (g *GEOIP) GetIPMatcher() *router.GeoIPMatcher { - return g.geoIPMatcher +func (g *GEOIP) GetIPMatcher() (router.IPMatcher, error) { + if g.geodata { + geoIPMatcher, err := geodata.LoadGeoIPMatcher(g.country) + if err != nil { + return nil, fmt.Errorf("[GeoIP] %w", err) + } + return geoIPMatcher, nil + } + return nil, errors.New("geoip country not set") } func (g *GEOIP) GetRecodeSize() int { - return g.recodeSize + if matcher, err := g.GetIPMatcher(); err == nil { + return matcher.Count() + } + return 0 } func NewGEOIP(country string, adapter string, isSrc, noResolveIP bool) (*GEOIP, error) { @@ -116,31 +130,23 @@ func NewGEOIP(country string, adapter string, isSrc, noResolveIP bool) (*GEOIP, } country = strings.ToLower(country) + geoip := &GEOIP{ + Base: &Base{}, + country: country, + adapter: adapter, + noResolveIP: noResolveIP, + isSourceIP: isSrc, + } if !C.GeodataMode || country == "lan" { - geoip := &GEOIP{ - Base: &Base{}, - country: country, - adapter: adapter, - noResolveIP: noResolveIP, - isSourceIP: isSrc, - } return geoip, nil } - geoIPMatcher, size, err := geodata.LoadGeoIPMatcher(country) + geoip.geodata = true + geoIPMatcher, err := geoip.GetIPMatcher() // test load if err != nil { - return nil, fmt.Errorf("[GeoIP] %w", err) + return nil, err } - log.Infoln("Start initial GeoIP rule %s => %s, records: %d", country, adapter, size) - geoip := &GEOIP{ - Base: &Base{}, - country: country, - adapter: adapter, - noResolveIP: noResolveIP, - isSourceIP: isSrc, - geoIPMatcher: geoIPMatcher, - recodeSize: size, - } + log.Infoln("Finished initial GeoIP rule %s => %s, records: %d", country, adapter, geoIPMatcher.Count()) return geoip, nil } diff --git a/rules/common/geosite.go b/rules/common/geosite.go index 1e3c1ab5..a728e991 100644 --- a/rules/common/geosite.go +++ b/rules/common/geosite.go @@ -15,7 +15,6 @@ type GEOSITE struct { *Base country string adapter string - matcher router.DomainMatcher recodeSize int } @@ -28,7 +27,11 @@ func (gs *GEOSITE) Match(metadata *C.Metadata) (bool, string) { if len(domain) == 0 { return false, "" } - return gs.matcher.ApplyDomain(domain), gs.adapter + matcher, err := gs.GetDomainMatcher() + if err != nil { + return false, "" + } + return matcher.ApplyDomain(domain), gs.adapter } func (gs *GEOSITE) Adapter() string { @@ -39,12 +42,19 @@ func (gs *GEOSITE) Payload() string { return gs.country } -func (gs *GEOSITE) GetDomainMatcher() router.DomainMatcher { - return gs.matcher +func (gs *GEOSITE) GetDomainMatcher() (router.DomainMatcher, error) { + matcher, err := geodata.LoadGeoSiteMatcher(gs.country) + if err != nil { + return nil, fmt.Errorf("load GeoSite data error, %w", err) + } + return matcher, nil } func (gs *GEOSITE) GetRecodeSize() int { - return gs.recodeSize + if matcher, err := gs.GetDomainMatcher(); err == nil { + return matcher.Count() + } + return 0 } func NewGEOSITE(country string, adapter string) (*GEOSITE, error) { @@ -53,21 +63,19 @@ func NewGEOSITE(country string, adapter string) (*GEOSITE, error) { return nil, err } - matcher, size, err := geodata.LoadGeoSiteMatcher(country) - if err != nil { - return nil, fmt.Errorf("load GeoSite data error, %s", err.Error()) - } - - log.Infoln("Start initial GeoSite rule %s => %s, records: %d", country, adapter, size) - geoSite := &GEOSITE{ - Base: &Base{}, - country: country, - adapter: adapter, - matcher: matcher, - recodeSize: size, + Base: &Base{}, + country: country, + adapter: adapter, } + matcher, err := geoSite.GetDomainMatcher() // test load + if err != nil { + return nil, err + } + + log.Infoln("Finished initial GeoSite rule %s => %s, records: %d", country, adapter, matcher.Count()) + return geoSite, nil } From 6bf419c5fea2cb2556f98a9b8580e1254484d91e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 16 Aug 2024 12:04:37 +0800 Subject: [PATCH 171/311] chore: better geo init logging --- config/config.go | 43 ++++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/config/config.go b/config/config.go index c1c155ec..d4868c02 100644 --- a/config/config.go +++ b/config/config.go @@ -619,7 +619,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } } - config.Sniffer, err = parseSniffer(rawCfg.Sniffer, rules, ruleProviders) + config.Sniffer, err = parseSniffer(rawCfg.Sniffer, ruleProviders) if err != nil { return nil, err } @@ -1250,14 +1250,14 @@ func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], rules [ if strings.HasPrefix(domain, "rule-set:") { domainSetName := domain[9:] - rule, err := parseDomainRuleSet(domainSetName, ruleProviders) + rule, err := parseDomainRuleSet(domainSetName, "dns.nameserver-policy", ruleProviders) if err != nil { return nil, err } policy[idx] = dns.Policy{Rule: rule, NameServers: nameservers} } else if strings.HasPrefix(domain, "geosite:") { country := domain[8:] - rule, err := parseGEOSITE(country, rules) + rule, err := RC.NewGEOSITE(country, "dns.nameserver-policy") if err != nil { return nil, err } @@ -1362,7 +1362,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul } // fake ip skip host filter - host, err := parseDomain(cfg.FakeIPFilter, fakeIPTrie, rules, ruleProviders) + host, err := parseDomain(cfg.FakeIPFilter, fakeIPTrie, "dns.fake-ip-filter", ruleProviders) if err != nil { return nil, err } @@ -1401,7 +1401,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul if err != nil { return nil, err } - rule = RP.NewIpCidrSet(cidrSet, "") + rule = RP.NewIpCidrSet(cidrSet, "dns.fallback-filter.ipcidr") dnsCfg.FallbackIPFilter = append(dnsCfg.FallbackIPFilter, rule) } if len(cfg.FallbackFilter.Domain) > 0 { @@ -1412,13 +1412,13 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul return nil, fmt.Errorf("DNS FallbackDomain[%d] format error: %w", idx, err) } } - rule = RP.NewDomainSet(domainTrie.NewDomainSet(), "") + rule = RP.NewDomainSet(domainTrie.NewDomainSet(), "dns.fallback-filter.domain") dnsCfg.FallbackIPFilter = append(dnsCfg.FallbackIPFilter, rule) } if len(cfg.FallbackFilter.GeoSite) > 0 { log.Warnln("replace fallback-filter.geosite with nameserver-policy, it will be removed in the future") for idx, geoSite := range cfg.FallbackFilter.GeoSite { - rule, err = parseGEOSITE(geoSite, rules) + rule, err = RC.NewGEOSITE(geoSite, "dns.fallback-filter.geosite") if err != nil { return nil, fmt.Errorf("DNS FallbackGeosite[%d] format error: %w", idx, err) } @@ -1524,7 +1524,7 @@ func parseTuicServer(rawTuic RawTuicServer, general *General) error { return nil } -func parseSniffer(snifferRaw RawSniffer, rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider) (*Sniffer, error) { +func parseSniffer(snifferRaw RawSniffer, ruleProviders map[string]providerTypes.RuleProvider) (*Sniffer, error) { sniffer := &Sniffer{ Enable: snifferRaw.Enable, ForceDnsMapping: snifferRaw.ForceDnsMapping, @@ -1587,13 +1587,13 @@ func parseSniffer(snifferRaw RawSniffer, rules []C.Rule, ruleProviders map[strin sniffer.Sniffers = loadSniffer - forceDomain, err := parseDomain(snifferRaw.ForceDomain, nil, rules, ruleProviders) + forceDomain, err := parseDomain(snifferRaw.ForceDomain, nil, "sniffer.force-domain", ruleProviders) if err != nil { return nil, fmt.Errorf("error in force-domain, error:%w", err) } sniffer.ForceDomain = forceDomain - skipDomain, err := parseDomain(snifferRaw.SkipDomain, nil, rules, ruleProviders) + skipDomain, err := parseDomain(snifferRaw.SkipDomain, nil, "sniffer.skip-domain", ruleProviders) if err != nil { return nil, fmt.Errorf("error in skip-domain, error:%w", err) } @@ -1602,7 +1602,7 @@ func parseSniffer(snifferRaw RawSniffer, rules []C.Rule, ruleProviders map[strin return sniffer, nil } -func parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider) (domainRules []C.Rule, err error) { +func parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (domainRules []C.Rule, err error) { var rule C.Rule for _, domain := range domains { domainLower := strings.ToLower(domain) @@ -1611,7 +1611,7 @@ func parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], rules subkeys = subkeys[1:] subkeys = strings.Split(subkeys[0], ",") for _, country := range subkeys { - rule, err = parseGEOSITE(country, rules) + rule, err = RC.NewGEOSITE(country, adapterName) if err != nil { return nil, err } @@ -1622,7 +1622,7 @@ func parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], rules subkeys = subkeys[1:] subkeys = strings.Split(subkeys[0], ",") for _, domainSetName := range subkeys { - rule, err = parseDomainRuleSet(domainSetName, ruleProviders) + rule, err = parseDomainRuleSet(domainSetName, adapterName, ruleProviders) if err != nil { return nil, err } @@ -1639,13 +1639,13 @@ func parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], rules } } if !domainTrie.IsEmpty() { - rule = RP.NewDomainSet(domainTrie.NewDomainSet(), "") + rule = RP.NewDomainSet(domainTrie.NewDomainSet(), adapterName) domainRules = append(domainRules, rule) } return } -func parseDomainRuleSet(domainSetName string, ruleProviders map[string]providerTypes.RuleProvider) (C.Rule, error) { +func parseDomainRuleSet(domainSetName string, adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (C.Rule, error) { if rp, ok := ruleProviders[domainSetName]; !ok { return nil, fmt.Errorf("not found rule-set: %s", domainSetName) } else { @@ -1657,16 +1657,5 @@ func parseDomainRuleSet(domainSetName string, ruleProviders map[string]providerT default: } } - return RP.NewRuleSet(domainSetName, "", true) -} - -func parseGEOSITE(country string, rules []C.Rule) (C.Rule, error) { - for _, rule := range rules { - if rule.RuleType() == C.GEOSITE { - if strings.EqualFold(country, rule.Payload()) { - return rule, nil - } - } - } - return RC.NewGEOSITE(country, "") + return RP.NewRuleSet(domainSetName, adapterName, true) } From 0793998de885cef77e9eeb455351ad5c2e4112e6 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Fri, 16 Aug 2024 14:15:36 +0800 Subject: [PATCH 172/311] chore: drop support of eBPF --- Makefile | 4 - component/ebpf/bpf/bpf_endian.h | 99 - component/ebpf/bpf/bpf_helper_defs.h | 4139 ----------------- component/ebpf/bpf/bpf_helpers.h | 262 -- component/ebpf/bpf/redir.c | 342 -- component/ebpf/bpf/tc.c | 103 - component/ebpf/byteorder/byteorder.go | 13 - .../ebpf/byteorder/byteorder_bigendian.go | 12 - .../ebpf/byteorder/byteorder_littleendian.go | 15 - component/ebpf/ebpf.go | 33 - component/ebpf/ebpf_linux.go | 137 - component/ebpf/ebpf_others.go | 21 - component/ebpf/redir/auto_redirect.go | 216 - component/ebpf/redir/bpf_bpfeb.go | 139 - component/ebpf/redir/bpf_bpfeb.o | Bin 15608 -> 0 bytes component/ebpf/redir/bpf_bpfel.go | 139 - component/ebpf/redir/bpf_bpfel.o | Bin 15624 -> 0 bytes component/ebpf/tc/bpf_bpfeb.go | 120 - component/ebpf/tc/bpf_bpfeb.o | Bin 5168 -> 0 bytes component/ebpf/tc/bpf_bpfel.go | 120 - component/ebpf/tc/bpf_bpfel.o | Bin 5168 -> 0 bytes component/ebpf/tc/redirect_to_tun.go | 147 - config/config.go | 13 - constant/ebpf.go | 20 - docs/config.yaml | 7 - go.mod | 2 +- go.sum | 8 +- hub/executor/executor.go | 5 - listener/autoredir/tcp.go | 95 - listener/listener.go | 94 - test/go.mod | 1 - test/go.sum | 2 - 32 files changed, 4 insertions(+), 6304 deletions(-) delete mode 100644 component/ebpf/bpf/bpf_endian.h delete mode 100644 component/ebpf/bpf/bpf_helper_defs.h delete mode 100644 component/ebpf/bpf/bpf_helpers.h delete mode 100644 component/ebpf/bpf/redir.c delete mode 100644 component/ebpf/bpf/tc.c delete mode 100644 component/ebpf/byteorder/byteorder.go delete mode 100644 component/ebpf/byteorder/byteorder_bigendian.go delete mode 100644 component/ebpf/byteorder/byteorder_littleendian.go delete mode 100644 component/ebpf/ebpf.go delete mode 100644 component/ebpf/ebpf_linux.go delete mode 100644 component/ebpf/ebpf_others.go delete mode 100644 component/ebpf/redir/auto_redirect.go delete mode 100644 component/ebpf/redir/bpf_bpfeb.go delete mode 100644 component/ebpf/redir/bpf_bpfeb.o delete mode 100644 component/ebpf/redir/bpf_bpfel.go delete mode 100644 component/ebpf/redir/bpf_bpfel.o delete mode 100644 component/ebpf/tc/bpf_bpfeb.go delete mode 100644 component/ebpf/tc/bpf_bpfeb.o delete mode 100644 component/ebpf/tc/bpf_bpfel.go delete mode 100644 component/ebpf/tc/bpf_bpfel.o delete mode 100644 component/ebpf/tc/redirect_to_tun.go delete mode 100644 constant/ebpf.go delete mode 100644 listener/autoredir/tcp.go diff --git a/Makefile b/Makefile index 59bec41e..36c640d5 100644 --- a/Makefile +++ b/Makefile @@ -163,7 +163,3 @@ clean: CLANG ?= clang-14 CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) -ebpf: export BPF_CLANG := $(CLANG) -ebpf: export BPF_CFLAGS := $(CFLAGS) -ebpf: - cd component/ebpf/ && go generate ./... diff --git a/component/ebpf/bpf/bpf_endian.h b/component/ebpf/bpf/bpf_endian.h deleted file mode 100644 index ec9db4fe..00000000 --- a/component/ebpf/bpf/bpf_endian.h +++ /dev/null @@ -1,99 +0,0 @@ -/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ -#ifndef __BPF_ENDIAN__ -#define __BPF_ENDIAN__ - -/* - * Isolate byte #n and put it into byte #m, for __u##b type. - * E.g., moving byte #6 (nnnnnnnn) into byte #1 (mmmmmmmm) for __u64: - * 1) xxxxxxxx nnnnnnnn xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx mmmmmmmm xxxxxxxx - * 2) nnnnnnnn xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx mmmmmmmm xxxxxxxx 00000000 - * 3) 00000000 00000000 00000000 00000000 00000000 00000000 00000000 nnnnnnnn - * 4) 00000000 00000000 00000000 00000000 00000000 00000000 nnnnnnnn 00000000 - */ -#define ___bpf_mvb(x, b, n, m) ((__u##b)(x) << (b-(n+1)*8) >> (b-8) << (m*8)) - -#define ___bpf_swab16(x) ((__u16)( \ - ___bpf_mvb(x, 16, 0, 1) | \ - ___bpf_mvb(x, 16, 1, 0))) - -#define ___bpf_swab32(x) ((__u32)( \ - ___bpf_mvb(x, 32, 0, 3) | \ - ___bpf_mvb(x, 32, 1, 2) | \ - ___bpf_mvb(x, 32, 2, 1) | \ - ___bpf_mvb(x, 32, 3, 0))) - -#define ___bpf_swab64(x) ((__u64)( \ - ___bpf_mvb(x, 64, 0, 7) | \ - ___bpf_mvb(x, 64, 1, 6) | \ - ___bpf_mvb(x, 64, 2, 5) | \ - ___bpf_mvb(x, 64, 3, 4) | \ - ___bpf_mvb(x, 64, 4, 3) | \ - ___bpf_mvb(x, 64, 5, 2) | \ - ___bpf_mvb(x, 64, 6, 1) | \ - ___bpf_mvb(x, 64, 7, 0))) - -/* LLVM's BPF target selects the endianness of the CPU - * it compiles on, or the user specifies (bpfel/bpfeb), - * respectively. The used __BYTE_ORDER__ is defined by - * the compiler, we cannot rely on __BYTE_ORDER from - * libc headers, since it doesn't reflect the actual - * requested byte order. - * - * Note, LLVM's BPF target has different __builtin_bswapX() - * semantics. It does map to BPF_ALU | BPF_END | BPF_TO_BE - * in bpfel and bpfeb case, which means below, that we map - * to cpu_to_be16(). We could use it unconditionally in BPF - * case, but better not rely on it, so that this header here - * can be used from application and BPF program side, which - * use different targets. - */ -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -# define __bpf_ntohs(x) __builtin_bswap16(x) -# define __bpf_htons(x) __builtin_bswap16(x) -# define __bpf_constant_ntohs(x) ___bpf_swab16(x) -# define __bpf_constant_htons(x) ___bpf_swab16(x) -# define __bpf_ntohl(x) __builtin_bswap32(x) -# define __bpf_htonl(x) __builtin_bswap32(x) -# define __bpf_constant_ntohl(x) ___bpf_swab32(x) -# define __bpf_constant_htonl(x) ___bpf_swab32(x) -# define __bpf_be64_to_cpu(x) __builtin_bswap64(x) -# define __bpf_cpu_to_be64(x) __builtin_bswap64(x) -# define __bpf_constant_be64_to_cpu(x) ___bpf_swab64(x) -# define __bpf_constant_cpu_to_be64(x) ___bpf_swab64(x) -#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -# define __bpf_ntohs(x) (x) -# define __bpf_htons(x) (x) -# define __bpf_constant_ntohs(x) (x) -# define __bpf_constant_htons(x) (x) -# define __bpf_ntohl(x) (x) -# define __bpf_htonl(x) (x) -# define __bpf_constant_ntohl(x) (x) -# define __bpf_constant_htonl(x) (x) -# define __bpf_be64_to_cpu(x) (x) -# define __bpf_cpu_to_be64(x) (x) -# define __bpf_constant_be64_to_cpu(x) (x) -# define __bpf_constant_cpu_to_be64(x) (x) -#else -# error "Fix your compiler's __BYTE_ORDER__?!" -#endif - -#define bpf_htons(x) \ - (__builtin_constant_p(x) ? \ - __bpf_constant_htons(x) : __bpf_htons(x)) -#define bpf_ntohs(x) \ - (__builtin_constant_p(x) ? \ - __bpf_constant_ntohs(x) : __bpf_ntohs(x)) -#define bpf_htonl(x) \ - (__builtin_constant_p(x) ? \ - __bpf_constant_htonl(x) : __bpf_htonl(x)) -#define bpf_ntohl(x) \ - (__builtin_constant_p(x) ? \ - __bpf_constant_ntohl(x) : __bpf_ntohl(x)) -#define bpf_cpu_to_be64(x) \ - (__builtin_constant_p(x) ? \ - __bpf_constant_cpu_to_be64(x) : __bpf_cpu_to_be64(x)) -#define bpf_be64_to_cpu(x) \ - (__builtin_constant_p(x) ? \ - __bpf_constant_be64_to_cpu(x) : __bpf_be64_to_cpu(x)) - -#endif /* __BPF_ENDIAN__ */ diff --git a/component/ebpf/bpf/bpf_helper_defs.h b/component/ebpf/bpf/bpf_helper_defs.h deleted file mode 100644 index 8a4edf61..00000000 --- a/component/ebpf/bpf/bpf_helper_defs.h +++ /dev/null @@ -1,4139 +0,0 @@ -/* This is auto-generated file. See bpf_doc.py for details. */ - -/* Forward declarations of BPF structs */ -struct bpf_fib_lookup; -struct bpf_sk_lookup; -struct bpf_perf_event_data; -struct bpf_perf_event_value; -struct bpf_pidns_info; -struct bpf_redir_neigh; -struct bpf_sock; -struct bpf_sock_addr; -struct bpf_sock_ops; -struct bpf_sock_tuple; -struct bpf_spin_lock; -struct bpf_sysctl; -struct bpf_tcp_sock; -struct bpf_tunnel_key; -struct bpf_xfrm_state; -struct linux_binprm; -struct pt_regs; -struct sk_reuseport_md; -struct sockaddr; -struct tcphdr; -struct seq_file; -struct tcp6_sock; -struct tcp_sock; -struct tcp_timewait_sock; -struct tcp_request_sock; -struct udp6_sock; -struct unix_sock; -struct task_struct; -struct __sk_buff; -struct sk_msg_md; -struct xdp_md; -struct path; -struct btf_ptr; -struct inode; -struct socket; -struct file; -struct bpf_timer; - -/* - * bpf_map_lookup_elem - * - * Perform a lookup in *map* for an entry associated to *key*. - * - * Returns - * Map value associated to *key*, or **NULL** if no entry was - * found. - */ -static void *(*bpf_map_lookup_elem)(void *map, const void *key) = (void *) 1; - -/* - * bpf_map_update_elem - * - * Add or update the value of the entry associated to *key* in - * *map* with *value*. *flags* is one of: - * - * **BPF_NOEXIST** - * The entry for *key* must not exist in the map. - * **BPF_EXIST** - * The entry for *key* must already exist in the map. - * **BPF_ANY** - * No condition on the existence of the entry for *key*. - * - * Flag value **BPF_NOEXIST** cannot be used for maps of types - * **BPF_MAP_TYPE_ARRAY** or **BPF_MAP_TYPE_PERCPU_ARRAY** (all - * elements always exist), the helper would return an error. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_map_update_elem)(void *map, const void *key, const void *value, __u64 flags) = (void *) 2; - -/* - * bpf_map_delete_elem - * - * Delete entry with *key* from *map*. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_map_delete_elem)(void *map, const void *key) = (void *) 3; - -/* - * bpf_probe_read - * - * For tracing programs, safely attempt to read *size* bytes from - * kernel space address *unsafe_ptr* and store the data in *dst*. - * - * Generally, use **bpf_probe_read_user**\ () or - * **bpf_probe_read_kernel**\ () instead. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_probe_read)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 4; - -/* - * bpf_ktime_get_ns - * - * Return the time elapsed since system boot, in nanoseconds. - * Does not include time the system was suspended. - * See: **clock_gettime**\ (**CLOCK_MONOTONIC**) - * - * Returns - * Current *ktime*. - */ -static __u64 (*bpf_ktime_get_ns)(void) = (void *) 5; - -/* - * bpf_trace_printk - * - * This helper is a "printk()-like" facility for debugging. It - * prints a message defined by format *fmt* (of size *fmt_size*) - * to file *\/sys/kernel/debug/tracing/trace* from DebugFS, if - * available. It can take up to three additional **u64** - * arguments (as an eBPF helpers, the total number of arguments is - * limited to five). - * - * Each time the helper is called, it appends a line to the trace. - * Lines are discarded while *\/sys/kernel/debug/tracing/trace* is - * open, use *\/sys/kernel/debug/tracing/trace_pipe* to avoid this. - * The format of the trace is customizable, and the exact output - * one will get depends on the options set in - * *\/sys/kernel/debug/tracing/trace_options* (see also the - * *README* file under the same directory). However, it usually - * defaults to something like: - * - * :: - * - * telnet-470 [001] .N.. 419421.045894: 0x00000001: - * - * In the above: - * - * * ``telnet`` is the name of the current task. - * * ``470`` is the PID of the current task. - * * ``001`` is the CPU number on which the task is - * running. - * * In ``.N..``, each character refers to a set of - * options (whether irqs are enabled, scheduling - * options, whether hard/softirqs are running, level of - * preempt_disabled respectively). **N** means that - * **TIF_NEED_RESCHED** and **PREEMPT_NEED_RESCHED** - * are set. - * * ``419421.045894`` is a timestamp. - * * ``0x00000001`` is a fake value used by BPF for the - * instruction pointer register. - * * ```` is the message formatted with - * *fmt*. - * - * The conversion specifiers supported by *fmt* are similar, but - * more limited than for printk(). They are **%d**, **%i**, - * **%u**, **%x**, **%ld**, **%li**, **%lu**, **%lx**, **%lld**, - * **%lli**, **%llu**, **%llx**, **%p**, **%s**. No modifier (size - * of field, padding with zeroes, etc.) is available, and the - * helper will return **-EINVAL** (but print nothing) if it - * encounters an unknown specifier. - * - * Also, note that **bpf_trace_printk**\ () is slow, and should - * only be used for debugging purposes. For this reason, a notice - * block (spanning several lines) is printed to kernel logs and - * states that the helper should not be used "for production use" - * the first time this helper is used (or more precisely, when - * **trace_printk**\ () buffers are allocated). For passing values - * to user space, perf events should be preferred. - * - * Returns - * The number of bytes written to the buffer, or a negative error - * in case of failure. - */ -static long (*bpf_trace_printk)(const char *fmt, __u32 fmt_size, ...) = (void *) 6; - -/* - * bpf_get_prandom_u32 - * - * Get a pseudo-random number. - * - * From a security point of view, this helper uses its own - * pseudo-random internal state, and cannot be used to infer the - * seed of other random functions in the kernel. However, it is - * essential to note that the generator used by the helper is not - * cryptographically secure. - * - * Returns - * A random 32-bit unsigned value. - */ -static __u32 (*bpf_get_prandom_u32)(void) = (void *) 7; - -/* - * bpf_get_smp_processor_id - * - * Get the SMP (symmetric multiprocessing) processor id. Note that - * all programs run with migration disabled, which means that the - * SMP processor id is stable during all the execution of the - * program. - * - * Returns - * The SMP id of the processor running the program. - */ -static __u32 (*bpf_get_smp_processor_id)(void) = (void *) 8; - -/* - * bpf_skb_store_bytes - * - * Store *len* bytes from address *from* into the packet - * associated to *skb*, at *offset*. *flags* are a combination of - * **BPF_F_RECOMPUTE_CSUM** (automatically recompute the - * checksum for the packet after storing the bytes) and - * **BPF_F_INVALIDATE_HASH** (set *skb*\ **->hash**, *skb*\ - * **->swhash** and *skb*\ **->l4hash** to 0). - * - * A call to this helper is susceptible to change the underlying - * packet buffer. Therefore, at load time, all checks on pointers - * previously done by the verifier are invalidated and must be - * performed again, if the helper is used in combination with - * direct packet access. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_skb_store_bytes)(struct __sk_buff *skb, __u32 offset, const void *from, __u32 len, __u64 flags) = (void *) 9; - -/* - * bpf_l3_csum_replace - * - * Recompute the layer 3 (e.g. IP) checksum for the packet - * associated to *skb*. Computation is incremental, so the helper - * must know the former value of the header field that was - * modified (*from*), the new value of this field (*to*), and the - * number of bytes (2 or 4) for this field, stored in *size*. - * Alternatively, it is possible to store the difference between - * the previous and the new values of the header field in *to*, by - * setting *from* and *size* to 0. For both methods, *offset* - * indicates the location of the IP checksum within the packet. - * - * This helper works in combination with **bpf_csum_diff**\ (), - * which does not update the checksum in-place, but offers more - * flexibility and can handle sizes larger than 2 or 4 for the - * checksum to update. - * - * A call to this helper is susceptible to change the underlying - * packet buffer. Therefore, at load time, all checks on pointers - * previously done by the verifier are invalidated and must be - * performed again, if the helper is used in combination with - * direct packet access. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_l3_csum_replace)(struct __sk_buff *skb, __u32 offset, __u64 from, __u64 to, __u64 size) = (void *) 10; - -/* - * bpf_l4_csum_replace - * - * Recompute the layer 4 (e.g. TCP, UDP or ICMP) checksum for the - * packet associated to *skb*. Computation is incremental, so the - * helper must know the former value of the header field that was - * modified (*from*), the new value of this field (*to*), and the - * number of bytes (2 or 4) for this field, stored on the lowest - * four bits of *flags*. Alternatively, it is possible to store - * the difference between the previous and the new values of the - * header field in *to*, by setting *from* and the four lowest - * bits of *flags* to 0. For both methods, *offset* indicates the - * location of the IP checksum within the packet. In addition to - * the size of the field, *flags* can be added (bitwise OR) actual - * flags. With **BPF_F_MARK_MANGLED_0**, a null checksum is left - * untouched (unless **BPF_F_MARK_ENFORCE** is added as well), and - * for updates resulting in a null checksum the value is set to - * **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates - * the checksum is to be computed against a pseudo-header. - * - * This helper works in combination with **bpf_csum_diff**\ (), - * which does not update the checksum in-place, but offers more - * flexibility and can handle sizes larger than 2 or 4 for the - * checksum to update. - * - * A call to this helper is susceptible to change the underlying - * packet buffer. Therefore, at load time, all checks on pointers - * previously done by the verifier are invalidated and must be - * performed again, if the helper is used in combination with - * direct packet access. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_l4_csum_replace)(struct __sk_buff *skb, __u32 offset, __u64 from, __u64 to, __u64 flags) = (void *) 11; - -/* - * bpf_tail_call - * - * This special helper is used to trigger a "tail call", or in - * other words, to jump into another eBPF program. The same stack - * frame is used (but values on stack and in registers for the - * caller are not accessible to the callee). This mechanism allows - * for program chaining, either for raising the maximum number of - * available eBPF instructions, or to execute given programs in - * conditional blocks. For security reasons, there is an upper - * limit to the number of successive tail calls that can be - * performed. - * - * Upon call of this helper, the program attempts to jump into a - * program referenced at index *index* in *prog_array_map*, a - * special map of type **BPF_MAP_TYPE_PROG_ARRAY**, and passes - * *ctx*, a pointer to the context. - * - * If the call succeeds, the kernel immediately runs the first - * instruction of the new program. This is not a function call, - * and it never returns to the previous program. If the call - * fails, then the helper has no effect, and the caller continues - * to run its subsequent instructions. A call can fail if the - * destination program for the jump does not exist (i.e. *index* - * is superior to the number of entries in *prog_array_map*), or - * if the maximum number of tail calls has been reached for this - * chain of programs. This limit is defined in the kernel by the - * macro **MAX_TAIL_CALL_CNT** (not accessible to user space), - * which is currently set to 33. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_tail_call)(void *ctx, void *prog_array_map, __u32 index) = (void *) 12; - -/* - * bpf_clone_redirect - * - * Clone and redirect the packet associated to *skb* to another - * net device of index *ifindex*. Both ingress and egress - * interfaces can be used for redirection. The **BPF_F_INGRESS** - * value in *flags* is used to make the distinction (ingress path - * is selected if the flag is present, egress path otherwise). - * This is the only flag supported for now. - * - * In comparison with **bpf_redirect**\ () helper, - * **bpf_clone_redirect**\ () has the associated cost of - * duplicating the packet buffer, but this can be executed out of - * the eBPF program. Conversely, **bpf_redirect**\ () is more - * efficient, but it is handled through an action code where the - * redirection happens only after the eBPF program has returned. - * - * A call to this helper is susceptible to change the underlying - * packet buffer. Therefore, at load time, all checks on pointers - * previously done by the verifier are invalidated and must be - * performed again, if the helper is used in combination with - * direct packet access. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_clone_redirect)(struct __sk_buff *skb, __u32 ifindex, __u64 flags) = (void *) 13; - -/* - * bpf_get_current_pid_tgid - * - * - * Returns - * A 64-bit integer containing the current tgid and pid, and - * created as such: - * *current_task*\ **->tgid << 32 \|** - * *current_task*\ **->pid**. - */ -static __u64 (*bpf_get_current_pid_tgid)(void) = (void *) 14; - -/* - * bpf_get_current_uid_gid - * - * - * Returns - * A 64-bit integer containing the current GID and UID, and - * created as such: *current_gid* **<< 32 \|** *current_uid*. - */ -static __u64 (*bpf_get_current_uid_gid)(void) = (void *) 15; - -/* - * bpf_get_current_comm - * - * Copy the **comm** attribute of the current task into *buf* of - * *size_of_buf*. The **comm** attribute contains the name of - * the executable (excluding the path) for the current task. The - * *size_of_buf* must be strictly positive. On success, the - * helper makes sure that the *buf* is NUL-terminated. On failure, - * it is filled with zeroes. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_get_current_comm)(void *buf, __u32 size_of_buf) = (void *) 16; - -/* - * bpf_get_cgroup_classid - * - * Retrieve the classid for the current task, i.e. for the net_cls - * cgroup to which *skb* belongs. - * - * This helper can be used on TC egress path, but not on ingress. - * - * The net_cls cgroup provides an interface to tag network packets - * based on a user-provided identifier for all traffic coming from - * the tasks belonging to the related cgroup. See also the related - * kernel documentation, available from the Linux sources in file - * *Documentation/admin-guide/cgroup-v1/net_cls.rst*. - * - * The Linux kernel has two versions for cgroups: there are - * cgroups v1 and cgroups v2. Both are available to users, who can - * use a mixture of them, but note that the net_cls cgroup is for - * cgroup v1 only. This makes it incompatible with BPF programs - * run on cgroups, which is a cgroup-v2-only feature (a socket can - * only hold data for one version of cgroups at a time). - * - * This helper is only available is the kernel was compiled with - * the **CONFIG_CGROUP_NET_CLASSID** configuration option set to - * "**y**" or to "**m**". - * - * Returns - * The classid, or 0 for the default unconfigured classid. - */ -static __u32 (*bpf_get_cgroup_classid)(struct __sk_buff *skb) = (void *) 17; - -/* - * bpf_skb_vlan_push - * - * Push a *vlan_tci* (VLAN tag control information) of protocol - * *vlan_proto* to the packet associated to *skb*, then update - * the checksum. Note that if *vlan_proto* is different from - * **ETH_P_8021Q** and **ETH_P_8021AD**, it is considered to - * be **ETH_P_8021Q**. - * - * A call to this helper is susceptible to change the underlying - * packet buffer. Therefore, at load time, all checks on pointers - * previously done by the verifier are invalidated and must be - * performed again, if the helper is used in combination with - * direct packet access. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_skb_vlan_push)(struct __sk_buff *skb, __be16 vlan_proto, __u16 vlan_tci) = (void *) 18; - -/* - * bpf_skb_vlan_pop - * - * Pop a VLAN header from the packet associated to *skb*. - * - * A call to this helper is susceptible to change the underlying - * packet buffer. Therefore, at load time, all checks on pointers - * previously done by the verifier are invalidated and must be - * performed again, if the helper is used in combination with - * direct packet access. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_skb_vlan_pop)(struct __sk_buff *skb) = (void *) 19; - -/* - * bpf_skb_get_tunnel_key - * - * Get tunnel metadata. This helper takes a pointer *key* to an - * empty **struct bpf_tunnel_key** of **size**, that will be - * filled with tunnel metadata for the packet associated to *skb*. - * The *flags* can be set to **BPF_F_TUNINFO_IPV6**, which - * indicates that the tunnel is based on IPv6 protocol instead of - * IPv4. - * - * The **struct bpf_tunnel_key** is an object that generalizes the - * principal parameters used by various tunneling protocols into a - * single struct. This way, it can be used to easily make a - * decision based on the contents of the encapsulation header, - * "summarized" in this struct. In particular, it holds the IP - * address of the remote end (IPv4 or IPv6, depending on the case) - * in *key*\ **->remote_ipv4** or *key*\ **->remote_ipv6**. Also, - * this struct exposes the *key*\ **->tunnel_id**, which is - * generally mapped to a VNI (Virtual Network Identifier), making - * it programmable together with the **bpf_skb_set_tunnel_key**\ - * () helper. - * - * Let's imagine that the following code is part of a program - * attached to the TC ingress interface, on one end of a GRE - * tunnel, and is supposed to filter out all messages coming from - * remote ends with IPv4 address other than 10.0.0.1: - * - * :: - * - * int ret; - * struct bpf_tunnel_key key = {}; - * - * ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0); - * if (ret < 0) - * return TC_ACT_SHOT; // drop packet - * - * if (key.remote_ipv4 != 0x0a000001) - * return TC_ACT_SHOT; // drop packet - * - * return TC_ACT_OK; // accept packet - * - * This interface can also be used with all encapsulation devices - * that can operate in "collect metadata" mode: instead of having - * one network device per specific configuration, the "collect - * metadata" mode only requires a single device where the - * configuration can be extracted from this helper. - * - * This can be used together with various tunnels such as VXLan, - * Geneve, GRE or IP in IP (IPIP). - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_skb_get_tunnel_key)(struct __sk_buff *skb, struct bpf_tunnel_key *key, __u32 size, __u64 flags) = (void *) 20; - -/* - * bpf_skb_set_tunnel_key - * - * Populate tunnel metadata for packet associated to *skb.* The - * tunnel metadata is set to the contents of *key*, of *size*. The - * *flags* can be set to a combination of the following values: - * - * **BPF_F_TUNINFO_IPV6** - * Indicate that the tunnel is based on IPv6 protocol - * instead of IPv4. - * **BPF_F_ZERO_CSUM_TX** - * For IPv4 packets, add a flag to tunnel metadata - * indicating that checksum computation should be skipped - * and checksum set to zeroes. - * **BPF_F_DONT_FRAGMENT** - * Add a flag to tunnel metadata indicating that the - * packet should not be fragmented. - * **BPF_F_SEQ_NUMBER** - * Add a flag to tunnel metadata indicating that a - * sequence number should be added to tunnel header before - * sending the packet. This flag was added for GRE - * encapsulation, but might be used with other protocols - * as well in the future. - * - * Here is a typical usage on the transmit path: - * - * :: - * - * struct bpf_tunnel_key key; - * populate key ... - * bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 0); - * bpf_clone_redirect(skb, vxlan_dev_ifindex, 0); - * - * See also the description of the **bpf_skb_get_tunnel_key**\ () - * helper for additional information. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_skb_set_tunnel_key)(struct __sk_buff *skb, struct bpf_tunnel_key *key, __u32 size, __u64 flags) = (void *) 21; - -/* - * bpf_perf_event_read - * - * Read the value of a perf event counter. This helper relies on a - * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of - * the perf event counter is selected when *map* is updated with - * perf event file descriptors. The *map* is an array whose size - * is the number of available CPUs, and each cell contains a value - * relative to one CPU. The value to retrieve is indicated by - * *flags*, that contains the index of the CPU to look up, masked - * with **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to - * **BPF_F_CURRENT_CPU** to indicate that the value for the - * current CPU should be retrieved. - * - * Note that before Linux 4.13, only hardware perf event can be - * retrieved. - * - * Also, be aware that the newer helper - * **bpf_perf_event_read_value**\ () is recommended over - * **bpf_perf_event_read**\ () in general. The latter has some ABI - * quirks where error and counter value are used as a return code - * (which is wrong to do since ranges may overlap). This issue is - * fixed with **bpf_perf_event_read_value**\ (), which at the same - * time provides more features over the **bpf_perf_event_read**\ - * () interface. Please refer to the description of - * **bpf_perf_event_read_value**\ () for details. - * - * Returns - * The value of the perf event counter read from the map, or a - * negative error code in case of failure. - */ -static __u64 (*bpf_perf_event_read)(void *map, __u64 flags) = (void *) 22; - -/* - * bpf_redirect - * - * Redirect the packet to another net device of index *ifindex*. - * This helper is somewhat similar to **bpf_clone_redirect**\ - * (), except that the packet is not cloned, which provides - * increased performance. - * - * Except for XDP, both ingress and egress interfaces can be used - * for redirection. The **BPF_F_INGRESS** value in *flags* is used - * to make the distinction (ingress path is selected if the flag - * is present, egress path otherwise). Currently, XDP only - * supports redirection to the egress interface, and accepts no - * flag at all. - * - * The same effect can also be attained with the more generic - * **bpf_redirect_map**\ (), which uses a BPF map to store the - * redirect target instead of providing it directly to the helper. - * - * Returns - * For XDP, the helper returns **XDP_REDIRECT** on success or - * **XDP_ABORTED** on error. For other program types, the values - * are **TC_ACT_REDIRECT** on success or **TC_ACT_SHOT** on - * error. - */ -static long (*bpf_redirect)(__u32 ifindex, __u64 flags) = (void *) 23; - -/* - * bpf_get_route_realm - * - * Retrieve the realm or the route, that is to say the - * **tclassid** field of the destination for the *skb*. The - * identifier retrieved is a user-provided tag, similar to the - * one used with the net_cls cgroup (see description for - * **bpf_get_cgroup_classid**\ () helper), but here this tag is - * held by a route (a destination entry), not by a task. - * - * Retrieving this identifier works with the clsact TC egress hook - * (see also **tc-bpf(8)**), or alternatively on conventional - * classful egress qdiscs, but not on TC ingress path. In case of - * clsact TC egress hook, this has the advantage that, internally, - * the destination entry has not been dropped yet in the transmit - * path. Therefore, the destination entry does not need to be - * artificially held via **netif_keep_dst**\ () for a classful - * qdisc until the *skb* is freed. - * - * This helper is available only if the kernel was compiled with - * **CONFIG_IP_ROUTE_CLASSID** configuration option. - * - * Returns - * The realm of the route for the packet associated to *skb*, or 0 - * if none was found. - */ -static __u32 (*bpf_get_route_realm)(struct __sk_buff *skb) = (void *) 24; - -/* - * bpf_perf_event_output - * - * Write raw *data* blob into a special BPF perf event held by - * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf - * event must have the following attributes: **PERF_SAMPLE_RAW** - * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and - * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. - * - * The *flags* are used to indicate the index in *map* for which - * the value must be put, masked with **BPF_F_INDEX_MASK**. - * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** - * to indicate that the index of the current CPU core should be - * used. - * - * The value to write, of *size*, is passed through eBPF stack and - * pointed by *data*. - * - * The context of the program *ctx* needs also be passed to the - * helper. - * - * On user space, a program willing to read the values needs to - * call **perf_event_open**\ () on the perf event (either for - * one or for all CPUs) and to store the file descriptor into the - * *map*. This must be done before the eBPF program can send data - * into it. An example is available in file - * *samples/bpf/trace_output_user.c* in the Linux kernel source - * tree (the eBPF program counterpart is in - * *samples/bpf/trace_output_kern.c*). - * - * **bpf_perf_event_output**\ () achieves better performance - * than **bpf_trace_printk**\ () for sharing data with user - * space, and is much better suitable for streaming data from eBPF - * programs. - * - * Note that this helper is not restricted to tracing use cases - * and can be used with programs attached to TC or XDP as well, - * where it allows for passing data to user space listeners. Data - * can be: - * - * * Only custom structs, - * * Only the packet payload, or - * * A combination of both. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_perf_event_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 25; - -/* - * bpf_skb_load_bytes - * - * This helper was provided as an easy way to load data from a - * packet. It can be used to load *len* bytes from *offset* from - * the packet associated to *skb*, into the buffer pointed by - * *to*. - * - * Since Linux 4.7, usage of this helper has mostly been replaced - * by "direct packet access", enabling packet data to be - * manipulated with *skb*\ **->data** and *skb*\ **->data_end** - * pointing respectively to the first byte of packet data and to - * the byte after the last byte of packet data. However, it - * remains useful if one wishes to read large quantities of data - * at once from a packet into the eBPF stack. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_skb_load_bytes)(const void *skb, __u32 offset, void *to, __u32 len) = (void *) 26; - -/* - * bpf_get_stackid - * - * Walk a user or a kernel stack and return its id. To achieve - * this, the helper needs *ctx*, which is a pointer to the context - * on which the tracing program is executed, and a pointer to a - * *map* of type **BPF_MAP_TYPE_STACK_TRACE**. - * - * The last argument, *flags*, holds the number of stack frames to - * skip (from 0 to 255), masked with - * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set - * a combination of the following flags: - * - * **BPF_F_USER_STACK** - * Collect a user space stack instead of a kernel stack. - * **BPF_F_FAST_STACK_CMP** - * Compare stacks by hash only. - * **BPF_F_REUSE_STACKID** - * If two different stacks hash into the same *stackid*, - * discard the old one. - * - * The stack id retrieved is a 32 bit long integer handle which - * can be further combined with other data (including other stack - * ids) and used as a key into maps. This can be useful for - * generating a variety of graphs (such as flame graphs or off-cpu - * graphs). - * - * For walking a stack, this helper is an improvement over - * **bpf_probe_read**\ (), which can be used with unrolled loops - * but is not efficient and consumes a lot of eBPF instructions. - * Instead, **bpf_get_stackid**\ () can collect up to - * **PERF_MAX_STACK_DEPTH** both kernel and user frames. Note that - * this limit can be controlled with the **sysctl** program, and - * that it should be manually increased in order to profile long - * user stacks (such as stacks for Java programs). To do so, use: - * - * :: - * - * # sysctl kernel.perf_event_max_stack= - * - * Returns - * The positive or null stack id on success, or a negative error - * in case of failure. - */ -static long (*bpf_get_stackid)(void *ctx, void *map, __u64 flags) = (void *) 27; - -/* - * bpf_csum_diff - * - * Compute a checksum difference, from the raw buffer pointed by - * *from*, of length *from_size* (that must be a multiple of 4), - * towards the raw buffer pointed by *to*, of size *to_size* - * (same remark). An optional *seed* can be added to the value - * (this can be cascaded, the seed may come from a previous call - * to the helper). - * - * This is flexible enough to be used in several ways: - * - * * With *from_size* == 0, *to_size* > 0 and *seed* set to - * checksum, it can be used when pushing new data. - * * With *from_size* > 0, *to_size* == 0 and *seed* set to - * checksum, it can be used when removing data from a packet. - * * With *from_size* > 0, *to_size* > 0 and *seed* set to 0, it - * can be used to compute a diff. Note that *from_size* and - * *to_size* do not need to be equal. - * - * This helper can be used in combination with - * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ (), to - * which one can feed in the difference computed with - * **bpf_csum_diff**\ (). - * - * Returns - * The checksum result, or a negative error code in case of - * failure. - */ -static __s64 (*bpf_csum_diff)(__be32 *from, __u32 from_size, __be32 *to, __u32 to_size, __wsum seed) = (void *) 28; - -/* - * bpf_skb_get_tunnel_opt - * - * Retrieve tunnel options metadata for the packet associated to - * *skb*, and store the raw tunnel option data to the buffer *opt* - * of *size*. - * - * This helper can be used with encapsulation devices that can - * operate in "collect metadata" mode (please refer to the related - * note in the description of **bpf_skb_get_tunnel_key**\ () for - * more details). A particular example where this can be used is - * in combination with the Geneve encapsulation protocol, where it - * allows for pushing (with **bpf_skb_get_tunnel_opt**\ () helper) - * and retrieving arbitrary TLVs (Type-Length-Value headers) from - * the eBPF program. This allows for full customization of these - * headers. - * - * Returns - * The size of the option data retrieved. - */ -static long (*bpf_skb_get_tunnel_opt)(struct __sk_buff *skb, void *opt, __u32 size) = (void *) 29; - -/* - * bpf_skb_set_tunnel_opt - * - * Set tunnel options metadata for the packet associated to *skb* - * to the option data contained in the raw buffer *opt* of *size*. - * - * See also the description of the **bpf_skb_get_tunnel_opt**\ () - * helper for additional information. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_skb_set_tunnel_opt)(struct __sk_buff *skb, void *opt, __u32 size) = (void *) 30; - -/* - * bpf_skb_change_proto - * - * Change the protocol of the *skb* to *proto*. Currently - * supported are transition from IPv4 to IPv6, and from IPv6 to - * IPv4. The helper takes care of the groundwork for the - * transition, including resizing the socket buffer. The eBPF - * program is expected to fill the new headers, if any, via - * **skb_store_bytes**\ () and to recompute the checksums with - * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ - * (). The main case for this helper is to perform NAT64 - * operations out of an eBPF program. - * - * Internally, the GSO type is marked as dodgy so that headers are - * checked and segments are recalculated by the GSO/GRO engine. - * The size for GSO target is adapted as well. - * - * All values for *flags* are reserved for future usage, and must - * be left at zero. - * - * A call to this helper is susceptible to change the underlying - * packet buffer. Therefore, at load time, all checks on pointers - * previously done by the verifier are invalidated and must be - * performed again, if the helper is used in combination with - * direct packet access. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_skb_change_proto)(struct __sk_buff *skb, __be16 proto, __u64 flags) = (void *) 31; - -/* - * bpf_skb_change_type - * - * Change the packet type for the packet associated to *skb*. This - * comes down to setting *skb*\ **->pkt_type** to *type*, except - * the eBPF program does not have a write access to *skb*\ - * **->pkt_type** beside this helper. Using a helper here allows - * for graceful handling of errors. - * - * The major use case is to change incoming *skb*s to - * **PACKET_HOST** in a programmatic way instead of having to - * recirculate via **redirect**\ (..., **BPF_F_INGRESS**), for - * example. - * - * Note that *type* only allows certain values. At this time, they - * are: - * - * **PACKET_HOST** - * Packet is for us. - * **PACKET_BROADCAST** - * Send packet to all. - * **PACKET_MULTICAST** - * Send packet to group. - * **PACKET_OTHERHOST** - * Send packet to someone else. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_skb_change_type)(struct __sk_buff *skb, __u32 type) = (void *) 32; - -/* - * bpf_skb_under_cgroup - * - * Check whether *skb* is a descendant of the cgroup2 held by - * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*. - * - * Returns - * The return value depends on the result of the test, and can be: - * - * * 0, if the *skb* failed the cgroup2 descendant test. - * * 1, if the *skb* succeeded the cgroup2 descendant test. - * * A negative error code, if an error occurred. - */ -static long (*bpf_skb_under_cgroup)(struct __sk_buff *skb, void *map, __u32 index) = (void *) 33; - -/* - * bpf_get_hash_recalc - * - * Retrieve the hash of the packet, *skb*\ **->hash**. If it is - * not set, in particular if the hash was cleared due to mangling, - * recompute this hash. Later accesses to the hash can be done - * directly with *skb*\ **->hash**. - * - * Calling **bpf_set_hash_invalid**\ (), changing a packet - * prototype with **bpf_skb_change_proto**\ (), or calling - * **bpf_skb_store_bytes**\ () with the - * **BPF_F_INVALIDATE_HASH** are actions susceptible to clear - * the hash and to trigger a new computation for the next call to - * **bpf_get_hash_recalc**\ (). - * - * Returns - * The 32-bit hash. - */ -static __u32 (*bpf_get_hash_recalc)(struct __sk_buff *skb) = (void *) 34; - -/* - * bpf_get_current_task - * - * - * Returns - * A pointer to the current task struct. - */ -static __u64 (*bpf_get_current_task)(void) = (void *) 35; - -/* - * bpf_probe_write_user - * - * Attempt in a safe way to write *len* bytes from the buffer - * *src* to *dst* in memory. It only works for threads that are in - * user context, and *dst* must be a valid user space address. - * - * This helper should not be used to implement any kind of - * security mechanism because of TOC-TOU attacks, but rather to - * debug, divert, and manipulate execution of semi-cooperative - * processes. - * - * Keep in mind that this feature is meant for experiments, and it - * has a risk of crashing the system and running programs. - * Therefore, when an eBPF program using this helper is attached, - * a warning including PID and process name is printed to kernel - * logs. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_probe_write_user)(void *dst, const void *src, __u32 len) = (void *) 36; - -/* - * bpf_current_task_under_cgroup - * - * Check whether the probe is being run is the context of a given - * subset of the cgroup2 hierarchy. The cgroup2 to test is held by - * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*. - * - * Returns - * The return value depends on the result of the test, and can be: - * - * * 0, if current task belongs to the cgroup2. - * * 1, if current task does not belong to the cgroup2. - * * A negative error code, if an error occurred. - */ -static long (*bpf_current_task_under_cgroup)(void *map, __u32 index) = (void *) 37; - -/* - * bpf_skb_change_tail - * - * Resize (trim or grow) the packet associated to *skb* to the - * new *len*. The *flags* are reserved for future usage, and must - * be left at zero. - * - * The basic idea is that the helper performs the needed work to - * change the size of the packet, then the eBPF program rewrites - * the rest via helpers like **bpf_skb_store_bytes**\ (), - * **bpf_l3_csum_replace**\ (), **bpf_l3_csum_replace**\ () - * and others. This helper is a slow path utility intended for - * replies with control messages. And because it is targeted for - * slow path, the helper itself can afford to be slow: it - * implicitly linearizes, unclones and drops offloads from the - * *skb*. - * - * A call to this helper is susceptible to change the underlying - * packet buffer. Therefore, at load time, all checks on pointers - * previously done by the verifier are invalidated and must be - * performed again, if the helper is used in combination with - * direct packet access. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_skb_change_tail)(struct __sk_buff *skb, __u32 len, __u64 flags) = (void *) 38; - -/* - * bpf_skb_pull_data - * - * Pull in non-linear data in case the *skb* is non-linear and not - * all of *len* are part of the linear section. Make *len* bytes - * from *skb* readable and writable. If a zero value is passed for - * *len*, then the whole length of the *skb* is pulled. - * - * This helper is only needed for reading and writing with direct - * packet access. - * - * For direct packet access, testing that offsets to access - * are within packet boundaries (test on *skb*\ **->data_end**) is - * susceptible to fail if offsets are invalid, or if the requested - * data is in non-linear parts of the *skb*. On failure the - * program can just bail out, or in the case of a non-linear - * buffer, use a helper to make the data available. The - * **bpf_skb_load_bytes**\ () helper is a first solution to access - * the data. Another one consists in using **bpf_skb_pull_data** - * to pull in once the non-linear parts, then retesting and - * eventually access the data. - * - * At the same time, this also makes sure the *skb* is uncloned, - * which is a necessary condition for direct write. As this needs - * to be an invariant for the write part only, the verifier - * detects writes and adds a prologue that is calling - * **bpf_skb_pull_data()** to effectively unclone the *skb* from - * the very beginning in case it is indeed cloned. - * - * A call to this helper is susceptible to change the underlying - * packet buffer. Therefore, at load time, all checks on pointers - * previously done by the verifier are invalidated and must be - * performed again, if the helper is used in combination with - * direct packet access. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_skb_pull_data)(struct __sk_buff *skb, __u32 len) = (void *) 39; - -/* - * bpf_csum_update - * - * Add the checksum *csum* into *skb*\ **->csum** in case the - * driver has supplied a checksum for the entire packet into that - * field. Return an error otherwise. This helper is intended to be - * used in combination with **bpf_csum_diff**\ (), in particular - * when the checksum needs to be updated after data has been - * written into the packet through direct packet access. - * - * Returns - * The checksum on success, or a negative error code in case of - * failure. - */ -static __s64 (*bpf_csum_update)(struct __sk_buff *skb, __wsum csum) = (void *) 40; - -/* - * bpf_set_hash_invalid - * - * Invalidate the current *skb*\ **->hash**. It can be used after - * mangling on headers through direct packet access, in order to - * indicate that the hash is outdated and to trigger a - * recalculation the next time the kernel tries to access this - * hash or when the **bpf_get_hash_recalc**\ () helper is called. - * - */ -static void (*bpf_set_hash_invalid)(struct __sk_buff *skb) = (void *) 41; - -/* - * bpf_get_numa_node_id - * - * Return the id of the current NUMA node. The primary use case - * for this helper is the selection of sockets for the local NUMA - * node, when the program is attached to sockets using the - * **SO_ATTACH_REUSEPORT_EBPF** option (see also **socket(7)**), - * but the helper is also available to other eBPF program types, - * similarly to **bpf_get_smp_processor_id**\ (). - * - * Returns - * The id of current NUMA node. - */ -static long (*bpf_get_numa_node_id)(void) = (void *) 42; - -/* - * bpf_skb_change_head - * - * Grows headroom of packet associated to *skb* and adjusts the - * offset of the MAC header accordingly, adding *len* bytes of - * space. It automatically extends and reallocates memory as - * required. - * - * This helper can be used on a layer 3 *skb* to push a MAC header - * for redirection into a layer 2 device. - * - * All values for *flags* are reserved for future usage, and must - * be left at zero. - * - * A call to this helper is susceptible to change the underlying - * packet buffer. Therefore, at load time, all checks on pointers - * previously done by the verifier are invalidated and must be - * performed again, if the helper is used in combination with - * direct packet access. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_skb_change_head)(struct __sk_buff *skb, __u32 len, __u64 flags) = (void *) 43; - -/* - * bpf_xdp_adjust_head - * - * Adjust (move) *xdp_md*\ **->data** by *delta* bytes. Note that - * it is possible to use a negative value for *delta*. This helper - * can be used to prepare the packet for pushing or popping - * headers. - * - * A call to this helper is susceptible to change the underlying - * packet buffer. Therefore, at load time, all checks on pointers - * previously done by the verifier are invalidated and must be - * performed again, if the helper is used in combination with - * direct packet access. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_xdp_adjust_head)(struct xdp_md *xdp_md, int delta) = (void *) 44; - -/* - * bpf_probe_read_str - * - * Copy a NUL terminated string from an unsafe kernel address - * *unsafe_ptr* to *dst*. See **bpf_probe_read_kernel_str**\ () for - * more details. - * - * Generally, use **bpf_probe_read_user_str**\ () or - * **bpf_probe_read_kernel_str**\ () instead. - * - * Returns - * On success, the strictly positive length of the string, - * including the trailing NUL character. On error, a negative - * value. - */ -static long (*bpf_probe_read_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 45; - -/* - * bpf_get_socket_cookie - * - * If the **struct sk_buff** pointed by *skb* has a known socket, - * retrieve the cookie (generated by the kernel) of this socket. - * If no cookie has been set yet, generate a new cookie. Once - * generated, the socket cookie remains stable for the life of the - * socket. This helper can be useful for monitoring per socket - * networking traffic statistics as it provides a global socket - * identifier that can be assumed unique. - * - * Returns - * A 8-byte long unique number on success, or 0 if the socket - * field is missing inside *skb*. - */ -static __u64 (*bpf_get_socket_cookie)(void *ctx) = (void *) 46; - -/* - * bpf_get_socket_uid - * - * - * Returns - * The owner UID of the socket associated to *skb*. If the socket - * is **NULL**, or if it is not a full socket (i.e. if it is a - * time-wait or a request socket instead), **overflowuid** value - * is returned (note that **overflowuid** might also be the actual - * UID value for the socket). - */ -static __u32 (*bpf_get_socket_uid)(struct __sk_buff *skb) = (void *) 47; - -/* - * bpf_set_hash - * - * Set the full hash for *skb* (set the field *skb*\ **->hash**) - * to value *hash*. - * - * Returns - * 0 - */ -static long (*bpf_set_hash)(struct __sk_buff *skb, __u32 hash) = (void *) 48; - -/* - * bpf_setsockopt - * - * Emulate a call to **setsockopt()** on the socket associated to - * *bpf_socket*, which must be a full socket. The *level* at - * which the option resides and the name *optname* of the option - * must be specified, see **setsockopt(2)** for more information. - * The option value of length *optlen* is pointed by *optval*. - * - * *bpf_socket* should be one of the following: - * - * * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**. - * * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT** - * and **BPF_CGROUP_INET6_CONNECT**. - * - * This helper actually implements a subset of **setsockopt()**. - * It supports the following *level*\ s: - * - * * **SOL_SOCKET**, which supports the following *optname*\ s: - * **SO_RCVBUF**, **SO_SNDBUF**, **SO_MAX_PACING_RATE**, - * **SO_PRIORITY**, **SO_RCVLOWAT**, **SO_MARK**, - * **SO_BINDTODEVICE**, **SO_KEEPALIVE**. - * * **IPPROTO_TCP**, which supports the following *optname*\ s: - * **TCP_CONGESTION**, **TCP_BPF_IW**, - * **TCP_BPF_SNDCWND_CLAMP**, **TCP_SAVE_SYN**, - * **TCP_KEEPIDLE**, **TCP_KEEPINTVL**, **TCP_KEEPCNT**, - * **TCP_SYNCNT**, **TCP_USER_TIMEOUT**, **TCP_NOTSENT_LOWAT**. - * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. - * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_setsockopt)(void *bpf_socket, int level, int optname, void *optval, int optlen) = (void *) 49; - -/* - * bpf_skb_adjust_room - * - * Grow or shrink the room for data in the packet associated to - * *skb* by *len_diff*, and according to the selected *mode*. - * - * By default, the helper will reset any offloaded checksum - * indicator of the skb to CHECKSUM_NONE. This can be avoided - * by the following flag: - * - * * **BPF_F_ADJ_ROOM_NO_CSUM_RESET**: Do not reset offloaded - * checksum data of the skb to CHECKSUM_NONE. - * - * There are two supported modes at this time: - * - * * **BPF_ADJ_ROOM_MAC**: Adjust room at the mac layer - * (room space is added or removed below the layer 2 header). - * - * * **BPF_ADJ_ROOM_NET**: Adjust room at the network layer - * (room space is added or removed below the layer 3 header). - * - * The following flags are supported at this time: - * - * * **BPF_F_ADJ_ROOM_FIXED_GSO**: Do not adjust gso_size. - * Adjusting mss in this way is not allowed for datagrams. - * - * * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV4**, - * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV6**: - * Any new space is reserved to hold a tunnel header. - * Configure skb offsets and other fields accordingly. - * - * * **BPF_F_ADJ_ROOM_ENCAP_L4_GRE**, - * **BPF_F_ADJ_ROOM_ENCAP_L4_UDP**: - * Use with ENCAP_L3 flags to further specify the tunnel type. - * - * * **BPF_F_ADJ_ROOM_ENCAP_L2**\ (*len*): - * Use with ENCAP_L3/L4 flags to further specify the tunnel - * type; *len* is the length of the inner MAC header. - * - * * **BPF_F_ADJ_ROOM_ENCAP_L2_ETH**: - * Use with BPF_F_ADJ_ROOM_ENCAP_L2 flag to further specify the - * L2 type as Ethernet. - * - * A call to this helper is susceptible to change the underlying - * packet buffer. Therefore, at load time, all checks on pointers - * previously done by the verifier are invalidated and must be - * performed again, if the helper is used in combination with - * direct packet access. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_skb_adjust_room)(struct __sk_buff *skb, __s32 len_diff, __u32 mode, __u64 flags) = (void *) 50; - -/* - * bpf_redirect_map - * - * Redirect the packet to the endpoint referenced by *map* at - * index *key*. Depending on its type, this *map* can contain - * references to net devices (for forwarding packets through other - * ports), or to CPUs (for redirecting XDP frames to another CPU; - * but this is only implemented for native XDP (with driver - * support) as of this writing). - * - * The lower two bits of *flags* are used as the return code if - * the map lookup fails. This is so that the return value can be - * one of the XDP program return codes up to **XDP_TX**, as chosen - * by the caller. The higher bits of *flags* can be set to - * BPF_F_BROADCAST or BPF_F_EXCLUDE_INGRESS as defined below. - * - * With BPF_F_BROADCAST the packet will be broadcasted to all the - * interfaces in the map, with BPF_F_EXCLUDE_INGRESS the ingress - * interface will be excluded when do broadcasting. - * - * See also **bpf_redirect**\ (), which only supports redirecting - * to an ifindex, but doesn't require a map to do so. - * - * Returns - * **XDP_REDIRECT** on success, or the value of the two lower bits - * of the *flags* argument on error. - */ -static long (*bpf_redirect_map)(void *map, __u32 key, __u64 flags) = (void *) 51; - -/* - * bpf_sk_redirect_map - * - * Redirect the packet to the socket referenced by *map* (of type - * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and - * egress interfaces can be used for redirection. The - * **BPF_F_INGRESS** value in *flags* is used to make the - * distinction (ingress path is selected if the flag is present, - * egress path otherwise). This is the only flag supported for now. - * - * Returns - * **SK_PASS** on success, or **SK_DROP** on error. - */ -static long (*bpf_sk_redirect_map)(struct __sk_buff *skb, void *map, __u32 key, __u64 flags) = (void *) 52; - -/* - * bpf_sock_map_update - * - * Add an entry to, or update a *map* referencing sockets. The - * *skops* is used as a new value for the entry associated to - * *key*. *flags* is one of: - * - * **BPF_NOEXIST** - * The entry for *key* must not exist in the map. - * **BPF_EXIST** - * The entry for *key* must already exist in the map. - * **BPF_ANY** - * No condition on the existence of the entry for *key*. - * - * If the *map* has eBPF programs (parser and verdict), those will - * be inherited by the socket being added. If the socket is - * already attached to eBPF programs, this results in an error. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_sock_map_update)(struct bpf_sock_ops *skops, void *map, void *key, __u64 flags) = (void *) 53; - -/* - * bpf_xdp_adjust_meta - * - * Adjust the address pointed by *xdp_md*\ **->data_meta** by - * *delta* (which can be positive or negative). Note that this - * operation modifies the address stored in *xdp_md*\ **->data**, - * so the latter must be loaded only after the helper has been - * called. - * - * The use of *xdp_md*\ **->data_meta** is optional and programs - * are not required to use it. The rationale is that when the - * packet is processed with XDP (e.g. as DoS filter), it is - * possible to push further meta data along with it before passing - * to the stack, and to give the guarantee that an ingress eBPF - * program attached as a TC classifier on the same device can pick - * this up for further post-processing. Since TC works with socket - * buffers, it remains possible to set from XDP the **mark** or - * **priority** pointers, or other pointers for the socket buffer. - * Having this scratch space generic and programmable allows for - * more flexibility as the user is free to store whatever meta - * data they need. - * - * A call to this helper is susceptible to change the underlying - * packet buffer. Therefore, at load time, all checks on pointers - * previously done by the verifier are invalidated and must be - * performed again, if the helper is used in combination with - * direct packet access. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_xdp_adjust_meta)(struct xdp_md *xdp_md, int delta) = (void *) 54; - -/* - * bpf_perf_event_read_value - * - * Read the value of a perf event counter, and store it into *buf* - * of size *buf_size*. This helper relies on a *map* of type - * **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of the perf event - * counter is selected when *map* is updated with perf event file - * descriptors. The *map* is an array whose size is the number of - * available CPUs, and each cell contains a value relative to one - * CPU. The value to retrieve is indicated by *flags*, that - * contains the index of the CPU to look up, masked with - * **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to - * **BPF_F_CURRENT_CPU** to indicate that the value for the - * current CPU should be retrieved. - * - * This helper behaves in a way close to - * **bpf_perf_event_read**\ () helper, save that instead of - * just returning the value observed, it fills the *buf* - * structure. This allows for additional data to be retrieved: in - * particular, the enabled and running times (in *buf*\ - * **->enabled** and *buf*\ **->running**, respectively) are - * copied. In general, **bpf_perf_event_read_value**\ () is - * recommended over **bpf_perf_event_read**\ (), which has some - * ABI issues and provides fewer functionalities. - * - * These values are interesting, because hardware PMU (Performance - * Monitoring Unit) counters are limited resources. When there are - * more PMU based perf events opened than available counters, - * kernel will multiplex these events so each event gets certain - * percentage (but not all) of the PMU time. In case that - * multiplexing happens, the number of samples or counter value - * will not reflect the case compared to when no multiplexing - * occurs. This makes comparison between different runs difficult. - * Typically, the counter value should be normalized before - * comparing to other experiments. The usual normalization is done - * as follows. - * - * :: - * - * normalized_counter = counter * t_enabled / t_running - * - * Where t_enabled is the time enabled for event and t_running is - * the time running for event since last normalization. The - * enabled and running times are accumulated since the perf event - * open. To achieve scaling factor between two invocations of an - * eBPF program, users can use CPU id as the key (which is - * typical for perf array usage model) to remember the previous - * value and do the calculation inside the eBPF program. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_perf_event_read_value)(void *map, __u64 flags, struct bpf_perf_event_value *buf, __u32 buf_size) = (void *) 55; - -/* - * bpf_perf_prog_read_value - * - * For en eBPF program attached to a perf event, retrieve the - * value of the event counter associated to *ctx* and store it in - * the structure pointed by *buf* and of size *buf_size*. Enabled - * and running times are also stored in the structure (see - * description of helper **bpf_perf_event_read_value**\ () for - * more details). - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_perf_prog_read_value)(struct bpf_perf_event_data *ctx, struct bpf_perf_event_value *buf, __u32 buf_size) = (void *) 56; - -/* - * bpf_getsockopt - * - * Emulate a call to **getsockopt()** on the socket associated to - * *bpf_socket*, which must be a full socket. The *level* at - * which the option resides and the name *optname* of the option - * must be specified, see **getsockopt(2)** for more information. - * The retrieved value is stored in the structure pointed by - * *opval* and of length *optlen*. - * - * *bpf_socket* should be one of the following: - * - * * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**. - * * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT** - * and **BPF_CGROUP_INET6_CONNECT**. - * - * This helper actually implements a subset of **getsockopt()**. - * It supports the following *level*\ s: - * - * * **IPPROTO_TCP**, which supports *optname* - * **TCP_CONGESTION**. - * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. - * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_getsockopt)(void *bpf_socket, int level, int optname, void *optval, int optlen) = (void *) 57; - -/* - * bpf_override_return - * - * Used for error injection, this helper uses kprobes to override - * the return value of the probed function, and to set it to *rc*. - * The first argument is the context *regs* on which the kprobe - * works. - * - * This helper works by setting the PC (program counter) - * to an override function which is run in place of the original - * probed function. This means the probed function is not run at - * all. The replacement function just returns with the required - * value. - * - * This helper has security implications, and thus is subject to - * restrictions. It is only available if the kernel was compiled - * with the **CONFIG_BPF_KPROBE_OVERRIDE** configuration - * option, and in this case it only works on functions tagged with - * **ALLOW_ERROR_INJECTION** in the kernel code. - * - * Also, the helper is only available for the architectures having - * the CONFIG_FUNCTION_ERROR_INJECTION option. As of this writing, - * x86 architecture is the only one to support this feature. - * - * Returns - * 0 - */ -static long (*bpf_override_return)(struct pt_regs *regs, __u64 rc) = (void *) 58; - -/* - * bpf_sock_ops_cb_flags_set - * - * Attempt to set the value of the **bpf_sock_ops_cb_flags** field - * for the full TCP socket associated to *bpf_sock_ops* to - * *argval*. - * - * The primary use of this field is to determine if there should - * be calls to eBPF programs of type - * **BPF_PROG_TYPE_SOCK_OPS** at various points in the TCP - * code. A program of the same type can change its value, per - * connection and as necessary, when the connection is - * established. This field is directly accessible for reading, but - * this helper must be used for updates in order to return an - * error if an eBPF program tries to set a callback that is not - * supported in the current kernel. - * - * *argval* is a flag array which can combine these flags: - * - * * **BPF_SOCK_OPS_RTO_CB_FLAG** (retransmission time out) - * * **BPF_SOCK_OPS_RETRANS_CB_FLAG** (retransmission) - * * **BPF_SOCK_OPS_STATE_CB_FLAG** (TCP state change) - * * **BPF_SOCK_OPS_RTT_CB_FLAG** (every RTT) - * - * Therefore, this function can be used to clear a callback flag by - * setting the appropriate bit to zero. e.g. to disable the RTO - * callback: - * - * **bpf_sock_ops_cb_flags_set(bpf_sock,** - * **bpf_sock->bpf_sock_ops_cb_flags & ~BPF_SOCK_OPS_RTO_CB_FLAG)** - * - * Here are some examples of where one could call such eBPF - * program: - * - * * When RTO fires. - * * When a packet is retransmitted. - * * When the connection terminates. - * * When a packet is sent. - * * When a packet is received. - * - * Returns - * Code **-EINVAL** if the socket is not a full TCP socket; - * otherwise, a positive number containing the bits that could not - * be set is returned (which comes down to 0 if all bits were set - * as required). - */ -static long (*bpf_sock_ops_cb_flags_set)(struct bpf_sock_ops *bpf_sock, int argval) = (void *) 59; - -/* - * bpf_msg_redirect_map - * - * This helper is used in programs implementing policies at the - * socket level. If the message *msg* is allowed to pass (i.e. if - * the verdict eBPF program returns **SK_PASS**), redirect it to - * the socket referenced by *map* (of type - * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and - * egress interfaces can be used for redirection. The - * **BPF_F_INGRESS** value in *flags* is used to make the - * distinction (ingress path is selected if the flag is present, - * egress path otherwise). This is the only flag supported for now. - * - * Returns - * **SK_PASS** on success, or **SK_DROP** on error. - */ -static long (*bpf_msg_redirect_map)(struct sk_msg_md *msg, void *map, __u32 key, __u64 flags) = (void *) 60; - -/* - * bpf_msg_apply_bytes - * - * For socket policies, apply the verdict of the eBPF program to - * the next *bytes* (number of bytes) of message *msg*. - * - * For example, this helper can be used in the following cases: - * - * * A single **sendmsg**\ () or **sendfile**\ () system call - * contains multiple logical messages that the eBPF program is - * supposed to read and for which it should apply a verdict. - * * An eBPF program only cares to read the first *bytes* of a - * *msg*. If the message has a large payload, then setting up - * and calling the eBPF program repeatedly for all bytes, even - * though the verdict is already known, would create unnecessary - * overhead. - * - * When called from within an eBPF program, the helper sets a - * counter internal to the BPF infrastructure, that is used to - * apply the last verdict to the next *bytes*. If *bytes* is - * smaller than the current data being processed from a - * **sendmsg**\ () or **sendfile**\ () system call, the first - * *bytes* will be sent and the eBPF program will be re-run with - * the pointer for start of data pointing to byte number *bytes* - * **+ 1**. If *bytes* is larger than the current data being - * processed, then the eBPF verdict will be applied to multiple - * **sendmsg**\ () or **sendfile**\ () calls until *bytes* are - * consumed. - * - * Note that if a socket closes with the internal counter holding - * a non-zero value, this is not a problem because data is not - * being buffered for *bytes* and is sent as it is received. - * - * Returns - * 0 - */ -static long (*bpf_msg_apply_bytes)(struct sk_msg_md *msg, __u32 bytes) = (void *) 61; - -/* - * bpf_msg_cork_bytes - * - * For socket policies, prevent the execution of the verdict eBPF - * program for message *msg* until *bytes* (byte number) have been - * accumulated. - * - * This can be used when one needs a specific number of bytes - * before a verdict can be assigned, even if the data spans - * multiple **sendmsg**\ () or **sendfile**\ () calls. The extreme - * case would be a user calling **sendmsg**\ () repeatedly with - * 1-byte long message segments. Obviously, this is bad for - * performance, but it is still valid. If the eBPF program needs - * *bytes* bytes to validate a header, this helper can be used to - * prevent the eBPF program to be called again until *bytes* have - * been accumulated. - * - * Returns - * 0 - */ -static long (*bpf_msg_cork_bytes)(struct sk_msg_md *msg, __u32 bytes) = (void *) 62; - -/* - * bpf_msg_pull_data - * - * For socket policies, pull in non-linear data from user space - * for *msg* and set pointers *msg*\ **->data** and *msg*\ - * **->data_end** to *start* and *end* bytes offsets into *msg*, - * respectively. - * - * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a - * *msg* it can only parse data that the (**data**, **data_end**) - * pointers have already consumed. For **sendmsg**\ () hooks this - * is likely the first scatterlist element. But for calls relying - * on the **sendpage** handler (e.g. **sendfile**\ ()) this will - * be the range (**0**, **0**) because the data is shared with - * user space and by default the objective is to avoid allowing - * user space to modify data while (or after) eBPF verdict is - * being decided. This helper can be used to pull in data and to - * set the start and end pointer to given values. Data will be - * copied if necessary (i.e. if data was not linear and if start - * and end pointers do not point to the same chunk). - * - * A call to this helper is susceptible to change the underlying - * packet buffer. Therefore, at load time, all checks on pointers - * previously done by the verifier are invalidated and must be - * performed again, if the helper is used in combination with - * direct packet access. - * - * All values for *flags* are reserved for future usage, and must - * be left at zero. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_msg_pull_data)(struct sk_msg_md *msg, __u32 start, __u32 end, __u64 flags) = (void *) 63; - -/* - * bpf_bind - * - * Bind the socket associated to *ctx* to the address pointed by - * *addr*, of length *addr_len*. This allows for making outgoing - * connection from the desired IP address, which can be useful for - * example when all processes inside a cgroup should use one - * single IP address on a host that has multiple IP configured. - * - * This helper works for IPv4 and IPv6, TCP and UDP sockets. The - * domain (*addr*\ **->sa_family**) must be **AF_INET** (or - * **AF_INET6**). It's advised to pass zero port (**sin_port** - * or **sin6_port**) which triggers IP_BIND_ADDRESS_NO_PORT-like - * behavior and lets the kernel efficiently pick up an unused - * port as long as 4-tuple is unique. Passing non-zero port might - * lead to degraded performance. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_bind)(struct bpf_sock_addr *ctx, struct sockaddr *addr, int addr_len) = (void *) 64; - -/* - * bpf_xdp_adjust_tail - * - * Adjust (move) *xdp_md*\ **->data_end** by *delta* bytes. It is - * possible to both shrink and grow the packet tail. - * Shrink done via *delta* being a negative integer. - * - * A call to this helper is susceptible to change the underlying - * packet buffer. Therefore, at load time, all checks on pointers - * previously done by the verifier are invalidated and must be - * performed again, if the helper is used in combination with - * direct packet access. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_xdp_adjust_tail)(struct xdp_md *xdp_md, int delta) = (void *) 65; - -/* - * bpf_skb_get_xfrm_state - * - * Retrieve the XFRM state (IP transform framework, see also - * **ip-xfrm(8)**) at *index* in XFRM "security path" for *skb*. - * - * The retrieved value is stored in the **struct bpf_xfrm_state** - * pointed by *xfrm_state* and of length *size*. - * - * All values for *flags* are reserved for future usage, and must - * be left at zero. - * - * This helper is available only if the kernel was compiled with - * **CONFIG_XFRM** configuration option. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_skb_get_xfrm_state)(struct __sk_buff *skb, __u32 index, struct bpf_xfrm_state *xfrm_state, __u32 size, __u64 flags) = (void *) 66; - -/* - * bpf_get_stack - * - * Return a user or a kernel stack in bpf program provided buffer. - * To achieve this, the helper needs *ctx*, which is a pointer - * to the context on which the tracing program is executed. - * To store the stacktrace, the bpf program provides *buf* with - * a nonnegative *size*. - * - * The last argument, *flags*, holds the number of stack frames to - * skip (from 0 to 255), masked with - * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set - * the following flags: - * - * **BPF_F_USER_STACK** - * Collect a user space stack instead of a kernel stack. - * **BPF_F_USER_BUILD_ID** - * Collect buildid+offset instead of ips for user stack, - * only valid if **BPF_F_USER_STACK** is also specified. - * - * **bpf_get_stack**\ () can collect up to - * **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject - * to sufficient large buffer size. Note that - * this limit can be controlled with the **sysctl** program, and - * that it should be manually increased in order to profile long - * user stacks (such as stacks for Java programs). To do so, use: - * - * :: - * - * # sysctl kernel.perf_event_max_stack= - * - * Returns - * A non-negative value equal to or less than *size* on success, - * or a negative error in case of failure. - */ -static long (*bpf_get_stack)(void *ctx, void *buf, __u32 size, __u64 flags) = (void *) 67; - -/* - * bpf_skb_load_bytes_relative - * - * This helper is similar to **bpf_skb_load_bytes**\ () in that - * it provides an easy way to load *len* bytes from *offset* - * from the packet associated to *skb*, into the buffer pointed - * by *to*. The difference to **bpf_skb_load_bytes**\ () is that - * a fifth argument *start_header* exists in order to select a - * base offset to start from. *start_header* can be one of: - * - * **BPF_HDR_START_MAC** - * Base offset to load data from is *skb*'s mac header. - * **BPF_HDR_START_NET** - * Base offset to load data from is *skb*'s network header. - * - * In general, "direct packet access" is the preferred method to - * access packet data, however, this helper is in particular useful - * in socket filters where *skb*\ **->data** does not always point - * to the start of the mac header and where "direct packet access" - * is not available. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_skb_load_bytes_relative)(const void *skb, __u32 offset, void *to, __u32 len, __u32 start_header) = (void *) 68; - -/* - * bpf_fib_lookup - * - * Do FIB lookup in kernel tables using parameters in *params*. - * If lookup is successful and result shows packet is to be - * forwarded, the neighbor tables are searched for the nexthop. - * If successful (ie., FIB lookup shows forwarding and nexthop - * is resolved), the nexthop address is returned in ipv4_dst - * or ipv6_dst based on family, smac is set to mac address of - * egress device, dmac is set to nexthop mac address, rt_metric - * is set to metric from route (IPv4/IPv6 only), and ifindex - * is set to the device index of the nexthop from the FIB lookup. - * - * *plen* argument is the size of the passed in struct. - * *flags* argument can be a combination of one or more of the - * following values: - * - * **BPF_FIB_LOOKUP_DIRECT** - * Do a direct table lookup vs full lookup using FIB - * rules. - * **BPF_FIB_LOOKUP_OUTPUT** - * Perform lookup from an egress perspective (default is - * ingress). - * - * *ctx* is either **struct xdp_md** for XDP programs or - * **struct sk_buff** tc cls_act programs. - * - * Returns - * * < 0 if any input argument is invalid - * * 0 on success (packet is forwarded, nexthop neighbor exists) - * * > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the - * packet is not forwarded or needs assist from full stack - * - * If lookup fails with BPF_FIB_LKUP_RET_FRAG_NEEDED, then the MTU - * was exceeded and output params->mtu_result contains the MTU. - */ -static long (*bpf_fib_lookup)(void *ctx, struct bpf_fib_lookup *params, int plen, __u32 flags) = (void *) 69; - -/* - * bpf_sock_hash_update - * - * Add an entry to, or update a sockhash *map* referencing sockets. - * The *skops* is used as a new value for the entry associated to - * *key*. *flags* is one of: - * - * **BPF_NOEXIST** - * The entry for *key* must not exist in the map. - * **BPF_EXIST** - * The entry for *key* must already exist in the map. - * **BPF_ANY** - * No condition on the existence of the entry for *key*. - * - * If the *map* has eBPF programs (parser and verdict), those will - * be inherited by the socket being added. If the socket is - * already attached to eBPF programs, this results in an error. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_sock_hash_update)(struct bpf_sock_ops *skops, void *map, void *key, __u64 flags) = (void *) 70; - -/* - * bpf_msg_redirect_hash - * - * This helper is used in programs implementing policies at the - * socket level. If the message *msg* is allowed to pass (i.e. if - * the verdict eBPF program returns **SK_PASS**), redirect it to - * the socket referenced by *map* (of type - * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and - * egress interfaces can be used for redirection. The - * **BPF_F_INGRESS** value in *flags* is used to make the - * distinction (ingress path is selected if the flag is present, - * egress path otherwise). This is the only flag supported for now. - * - * Returns - * **SK_PASS** on success, or **SK_DROP** on error. - */ -static long (*bpf_msg_redirect_hash)(struct sk_msg_md *msg, void *map, void *key, __u64 flags) = (void *) 71; - -/* - * bpf_sk_redirect_hash - * - * This helper is used in programs implementing policies at the - * skb socket level. If the sk_buff *skb* is allowed to pass (i.e. - * if the verdict eBPF program returns **SK_PASS**), redirect it - * to the socket referenced by *map* (of type - * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and - * egress interfaces can be used for redirection. The - * **BPF_F_INGRESS** value in *flags* is used to make the - * distinction (ingress path is selected if the flag is present, - * egress otherwise). This is the only flag supported for now. - * - * Returns - * **SK_PASS** on success, or **SK_DROP** on error. - */ -static long (*bpf_sk_redirect_hash)(struct __sk_buff *skb, void *map, void *key, __u64 flags) = (void *) 72; - -/* - * bpf_lwt_push_encap - * - * Encapsulate the packet associated to *skb* within a Layer 3 - * protocol header. This header is provided in the buffer at - * address *hdr*, with *len* its size in bytes. *type* indicates - * the protocol of the header and can be one of: - * - * **BPF_LWT_ENCAP_SEG6** - * IPv6 encapsulation with Segment Routing Header - * (**struct ipv6_sr_hdr**). *hdr* only contains the SRH, - * the IPv6 header is computed by the kernel. - * **BPF_LWT_ENCAP_SEG6_INLINE** - * Only works if *skb* contains an IPv6 packet. Insert a - * Segment Routing Header (**struct ipv6_sr_hdr**) inside - * the IPv6 header. - * **BPF_LWT_ENCAP_IP** - * IP encapsulation (GRE/GUE/IPIP/etc). The outer header - * must be IPv4 or IPv6, followed by zero or more - * additional headers, up to **LWT_BPF_MAX_HEADROOM** - * total bytes in all prepended headers. Please note that - * if **skb_is_gso**\ (*skb*) is true, no more than two - * headers can be prepended, and the inner header, if - * present, should be either GRE or UDP/GUE. - * - * **BPF_LWT_ENCAP_SEG6**\ \* types can be called by BPF programs - * of type **BPF_PROG_TYPE_LWT_IN**; **BPF_LWT_ENCAP_IP** type can - * be called by bpf programs of types **BPF_PROG_TYPE_LWT_IN** and - * **BPF_PROG_TYPE_LWT_XMIT**. - * - * A call to this helper is susceptible to change the underlying - * packet buffer. Therefore, at load time, all checks on pointers - * previously done by the verifier are invalidated and must be - * performed again, if the helper is used in combination with - * direct packet access. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_lwt_push_encap)(struct __sk_buff *skb, __u32 type, void *hdr, __u32 len) = (void *) 73; - -/* - * bpf_lwt_seg6_store_bytes - * - * Store *len* bytes from address *from* into the packet - * associated to *skb*, at *offset*. Only the flags, tag and TLVs - * inside the outermost IPv6 Segment Routing Header can be - * modified through this helper. - * - * A call to this helper is susceptible to change the underlying - * packet buffer. Therefore, at load time, all checks on pointers - * previously done by the verifier are invalidated and must be - * performed again, if the helper is used in combination with - * direct packet access. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_lwt_seg6_store_bytes)(struct __sk_buff *skb, __u32 offset, const void *from, __u32 len) = (void *) 74; - -/* - * bpf_lwt_seg6_adjust_srh - * - * Adjust the size allocated to TLVs in the outermost IPv6 - * Segment Routing Header contained in the packet associated to - * *skb*, at position *offset* by *delta* bytes. Only offsets - * after the segments are accepted. *delta* can be as well - * positive (growing) as negative (shrinking). - * - * A call to this helper is susceptible to change the underlying - * packet buffer. Therefore, at load time, all checks on pointers - * previously done by the verifier are invalidated and must be - * performed again, if the helper is used in combination with - * direct packet access. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_lwt_seg6_adjust_srh)(struct __sk_buff *skb, __u32 offset, __s32 delta) = (void *) 75; - -/* - * bpf_lwt_seg6_action - * - * Apply an IPv6 Segment Routing action of type *action* to the - * packet associated to *skb*. Each action takes a parameter - * contained at address *param*, and of length *param_len* bytes. - * *action* can be one of: - * - * **SEG6_LOCAL_ACTION_END_X** - * End.X action: Endpoint with Layer-3 cross-connect. - * Type of *param*: **struct in6_addr**. - * **SEG6_LOCAL_ACTION_END_T** - * End.T action: Endpoint with specific IPv6 table lookup. - * Type of *param*: **int**. - * **SEG6_LOCAL_ACTION_END_B6** - * End.B6 action: Endpoint bound to an SRv6 policy. - * Type of *param*: **struct ipv6_sr_hdr**. - * **SEG6_LOCAL_ACTION_END_B6_ENCAP** - * End.B6.Encap action: Endpoint bound to an SRv6 - * encapsulation policy. - * Type of *param*: **struct ipv6_sr_hdr**. - * - * A call to this helper is susceptible to change the underlying - * packet buffer. Therefore, at load time, all checks on pointers - * previously done by the verifier are invalidated and must be - * performed again, if the helper is used in combination with - * direct packet access. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_lwt_seg6_action)(struct __sk_buff *skb, __u32 action, void *param, __u32 param_len) = (void *) 76; - -/* - * bpf_rc_repeat - * - * This helper is used in programs implementing IR decoding, to - * report a successfully decoded repeat key message. This delays - * the generation of a key up event for previously generated - * key down event. - * - * Some IR protocols like NEC have a special IR message for - * repeating last button, for when a button is held down. - * - * The *ctx* should point to the lirc sample as passed into - * the program. - * - * This helper is only available is the kernel was compiled with - * the **CONFIG_BPF_LIRC_MODE2** configuration option set to - * "**y**". - * - * Returns - * 0 - */ -static long (*bpf_rc_repeat)(void *ctx) = (void *) 77; - -/* - * bpf_rc_keydown - * - * This helper is used in programs implementing IR decoding, to - * report a successfully decoded key press with *scancode*, - * *toggle* value in the given *protocol*. The scancode will be - * translated to a keycode using the rc keymap, and reported as - * an input key down event. After a period a key up event is - * generated. This period can be extended by calling either - * **bpf_rc_keydown**\ () again with the same values, or calling - * **bpf_rc_repeat**\ (). - * - * Some protocols include a toggle bit, in case the button was - * released and pressed again between consecutive scancodes. - * - * The *ctx* should point to the lirc sample as passed into - * the program. - * - * The *protocol* is the decoded protocol number (see - * **enum rc_proto** for some predefined values). - * - * This helper is only available is the kernel was compiled with - * the **CONFIG_BPF_LIRC_MODE2** configuration option set to - * "**y**". - * - * Returns - * 0 - */ -static long (*bpf_rc_keydown)(void *ctx, __u32 protocol, __u64 scancode, __u32 toggle) = (void *) 78; - -/* - * bpf_skb_cgroup_id - * - * Return the cgroup v2 id of the socket associated with the *skb*. - * This is roughly similar to the **bpf_get_cgroup_classid**\ () - * helper for cgroup v1 by providing a tag resp. identifier that - * can be matched on or used for map lookups e.g. to implement - * policy. The cgroup v2 id of a given path in the hierarchy is - * exposed in user space through the f_handle API in order to get - * to the same 64-bit id. - * - * This helper can be used on TC egress path, but not on ingress, - * and is available only if the kernel was compiled with the - * **CONFIG_SOCK_CGROUP_DATA** configuration option. - * - * Returns - * The id is returned or 0 in case the id could not be retrieved. - */ -static __u64 (*bpf_skb_cgroup_id)(struct __sk_buff *skb) = (void *) 79; - -/* - * bpf_get_current_cgroup_id - * - * - * Returns - * A 64-bit integer containing the current cgroup id based - * on the cgroup within which the current task is running. - */ -static __u64 (*bpf_get_current_cgroup_id)(void) = (void *) 80; - -/* - * bpf_get_local_storage - * - * Get the pointer to the local storage area. - * The type and the size of the local storage is defined - * by the *map* argument. - * The *flags* meaning is specific for each map type, - * and has to be 0 for cgroup local storage. - * - * Depending on the BPF program type, a local storage area - * can be shared between multiple instances of the BPF program, - * running simultaneously. - * - * A user should care about the synchronization by himself. - * For example, by using the **BPF_ATOMIC** instructions to alter - * the shared data. - * - * Returns - * A pointer to the local storage area. - */ -static void *(*bpf_get_local_storage)(void *map, __u64 flags) = (void *) 81; - -/* - * bpf_sk_select_reuseport - * - * Select a **SO_REUSEPORT** socket from a - * **BPF_MAP_TYPE_REUSEPORT_SOCKARRAY** *map*. - * It checks the selected socket is matching the incoming - * request in the socket buffer. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_sk_select_reuseport)(struct sk_reuseport_md *reuse, void *map, void *key, __u64 flags) = (void *) 82; - -/* - * bpf_skb_ancestor_cgroup_id - * - * Return id of cgroup v2 that is ancestor of cgroup associated - * with the *skb* at the *ancestor_level*. The root cgroup is at - * *ancestor_level* zero and each step down the hierarchy - * increments the level. If *ancestor_level* == level of cgroup - * associated with *skb*, then return value will be same as that - * of **bpf_skb_cgroup_id**\ (). - * - * The helper is useful to implement policies based on cgroups - * that are upper in hierarchy than immediate cgroup associated - * with *skb*. - * - * The format of returned id and helper limitations are same as in - * **bpf_skb_cgroup_id**\ (). - * - * Returns - * The id is returned or 0 in case the id could not be retrieved. - */ -static __u64 (*bpf_skb_ancestor_cgroup_id)(struct __sk_buff *skb, int ancestor_level) = (void *) 83; - -/* - * bpf_sk_lookup_tcp - * - * Look for TCP socket matching *tuple*, optionally in a child - * network namespace *netns*. The return value must be checked, - * and if non-**NULL**, released via **bpf_sk_release**\ (). - * - * The *ctx* should point to the context of the program, such as - * the skb or socket (depending on the hook in use). This is used - * to determine the base network namespace for the lookup. - * - * *tuple_size* must be one of: - * - * **sizeof**\ (*tuple*\ **->ipv4**) - * Look for an IPv4 socket. - * **sizeof**\ (*tuple*\ **->ipv6**) - * Look for an IPv6 socket. - * - * If the *netns* is a negative signed 32-bit integer, then the - * socket lookup table in the netns associated with the *ctx* - * will be used. For the TC hooks, this is the netns of the device - * in the skb. For socket hooks, this is the netns of the socket. - * If *netns* is any other signed 32-bit value greater than or - * equal to zero then it specifies the ID of the netns relative to - * the netns associated with the *ctx*. *netns* values beyond the - * range of 32-bit integers are reserved for future use. - * - * All values for *flags* are reserved for future usage, and must - * be left at zero. - * - * This helper is available only if the kernel was compiled with - * **CONFIG_NET** configuration option. - * - * Returns - * Pointer to **struct bpf_sock**, or **NULL** in case of failure. - * For sockets with reuseport option, the **struct bpf_sock** - * result is from *reuse*\ **->socks**\ [] using the hash of the - * tuple. - */ -static struct bpf_sock *(*bpf_sk_lookup_tcp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 84; - -/* - * bpf_sk_lookup_udp - * - * Look for UDP socket matching *tuple*, optionally in a child - * network namespace *netns*. The return value must be checked, - * and if non-**NULL**, released via **bpf_sk_release**\ (). - * - * The *ctx* should point to the context of the program, such as - * the skb or socket (depending on the hook in use). This is used - * to determine the base network namespace for the lookup. - * - * *tuple_size* must be one of: - * - * **sizeof**\ (*tuple*\ **->ipv4**) - * Look for an IPv4 socket. - * **sizeof**\ (*tuple*\ **->ipv6**) - * Look for an IPv6 socket. - * - * If the *netns* is a negative signed 32-bit integer, then the - * socket lookup table in the netns associated with the *ctx* - * will be used. For the TC hooks, this is the netns of the device - * in the skb. For socket hooks, this is the netns of the socket. - * If *netns* is any other signed 32-bit value greater than or - * equal to zero then it specifies the ID of the netns relative to - * the netns associated with the *ctx*. *netns* values beyond the - * range of 32-bit integers are reserved for future use. - * - * All values for *flags* are reserved for future usage, and must - * be left at zero. - * - * This helper is available only if the kernel was compiled with - * **CONFIG_NET** configuration option. - * - * Returns - * Pointer to **struct bpf_sock**, or **NULL** in case of failure. - * For sockets with reuseport option, the **struct bpf_sock** - * result is from *reuse*\ **->socks**\ [] using the hash of the - * tuple. - */ -static struct bpf_sock *(*bpf_sk_lookup_udp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 85; - -/* - * bpf_sk_release - * - * Release the reference held by *sock*. *sock* must be a - * non-**NULL** pointer that was returned from - * **bpf_sk_lookup_xxx**\ (). - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_sk_release)(void *sock) = (void *) 86; - -/* - * bpf_map_push_elem - * - * Push an element *value* in *map*. *flags* is one of: - * - * **BPF_EXIST** - * If the queue/stack is full, the oldest element is - * removed to make room for this. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_map_push_elem)(void *map, const void *value, __u64 flags) = (void *) 87; - -/* - * bpf_map_pop_elem - * - * Pop an element from *map*. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_map_pop_elem)(void *map, void *value) = (void *) 88; - -/* - * bpf_map_peek_elem - * - * Get an element from *map* without removing it. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_map_peek_elem)(void *map, void *value) = (void *) 89; - -/* - * bpf_msg_push_data - * - * For socket policies, insert *len* bytes into *msg* at offset - * *start*. - * - * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a - * *msg* it may want to insert metadata or options into the *msg*. - * This can later be read and used by any of the lower layer BPF - * hooks. - * - * This helper may fail if under memory pressure (a malloc - * fails) in these cases BPF programs will get an appropriate - * error and BPF programs will need to handle them. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_msg_push_data)(struct sk_msg_md *msg, __u32 start, __u32 len, __u64 flags) = (void *) 90; - -/* - * bpf_msg_pop_data - * - * Will remove *len* bytes from a *msg* starting at byte *start*. - * This may result in **ENOMEM** errors under certain situations if - * an allocation and copy are required due to a full ring buffer. - * However, the helper will try to avoid doing the allocation - * if possible. Other errors can occur if input parameters are - * invalid either due to *start* byte not being valid part of *msg* - * payload and/or *pop* value being to large. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_msg_pop_data)(struct sk_msg_md *msg, __u32 start, __u32 len, __u64 flags) = (void *) 91; - -/* - * bpf_rc_pointer_rel - * - * This helper is used in programs implementing IR decoding, to - * report a successfully decoded pointer movement. - * - * The *ctx* should point to the lirc sample as passed into - * the program. - * - * This helper is only available is the kernel was compiled with - * the **CONFIG_BPF_LIRC_MODE2** configuration option set to - * "**y**". - * - * Returns - * 0 - */ -static long (*bpf_rc_pointer_rel)(void *ctx, __s32 rel_x, __s32 rel_y) = (void *) 92; - -/* - * bpf_spin_lock - * - * Acquire a spinlock represented by the pointer *lock*, which is - * stored as part of a value of a map. Taking the lock allows to - * safely update the rest of the fields in that value. The - * spinlock can (and must) later be released with a call to - * **bpf_spin_unlock**\ (\ *lock*\ ). - * - * Spinlocks in BPF programs come with a number of restrictions - * and constraints: - * - * * **bpf_spin_lock** objects are only allowed inside maps of - * types **BPF_MAP_TYPE_HASH** and **BPF_MAP_TYPE_ARRAY** (this - * list could be extended in the future). - * * BTF description of the map is mandatory. - * * The BPF program can take ONE lock at a time, since taking two - * or more could cause dead locks. - * * Only one **struct bpf_spin_lock** is allowed per map element. - * * When the lock is taken, calls (either BPF to BPF or helpers) - * are not allowed. - * * The **BPF_LD_ABS** and **BPF_LD_IND** instructions are not - * allowed inside a spinlock-ed region. - * * The BPF program MUST call **bpf_spin_unlock**\ () to release - * the lock, on all execution paths, before it returns. - * * The BPF program can access **struct bpf_spin_lock** only via - * the **bpf_spin_lock**\ () and **bpf_spin_unlock**\ () - * helpers. Loading or storing data into the **struct - * bpf_spin_lock** *lock*\ **;** field of a map is not allowed. - * * To use the **bpf_spin_lock**\ () helper, the BTF description - * of the map value must be a struct and have **struct - * bpf_spin_lock** *anyname*\ **;** field at the top level. - * Nested lock inside another struct is not allowed. - * * The **struct bpf_spin_lock** *lock* field in a map value must - * be aligned on a multiple of 4 bytes in that value. - * * Syscall with command **BPF_MAP_LOOKUP_ELEM** does not copy - * the **bpf_spin_lock** field to user space. - * * Syscall with command **BPF_MAP_UPDATE_ELEM**, or update from - * a BPF program, do not update the **bpf_spin_lock** field. - * * **bpf_spin_lock** cannot be on the stack or inside a - * networking packet (it can only be inside of a map values). - * * **bpf_spin_lock** is available to root only. - * * Tracing programs and socket filter programs cannot use - * **bpf_spin_lock**\ () due to insufficient preemption checks - * (but this may change in the future). - * * **bpf_spin_lock** is not allowed in inner maps of map-in-map. - * - * Returns - * 0 - */ -static long (*bpf_spin_lock)(struct bpf_spin_lock *lock) = (void *) 93; - -/* - * bpf_spin_unlock - * - * Release the *lock* previously locked by a call to - * **bpf_spin_lock**\ (\ *lock*\ ). - * - * Returns - * 0 - */ -static long (*bpf_spin_unlock)(struct bpf_spin_lock *lock) = (void *) 94; - -/* - * bpf_sk_fullsock - * - * This helper gets a **struct bpf_sock** pointer such - * that all the fields in this **bpf_sock** can be accessed. - * - * Returns - * A **struct bpf_sock** pointer on success, or **NULL** in - * case of failure. - */ -static struct bpf_sock *(*bpf_sk_fullsock)(struct bpf_sock *sk) = (void *) 95; - -/* - * bpf_tcp_sock - * - * This helper gets a **struct bpf_tcp_sock** pointer from a - * **struct bpf_sock** pointer. - * - * Returns - * A **struct bpf_tcp_sock** pointer on success, or **NULL** in - * case of failure. - */ -static struct bpf_tcp_sock *(*bpf_tcp_sock)(struct bpf_sock *sk) = (void *) 96; - -/* - * bpf_skb_ecn_set_ce - * - * Set ECN (Explicit Congestion Notification) field of IP header - * to **CE** (Congestion Encountered) if current value is **ECT** - * (ECN Capable Transport). Otherwise, do nothing. Works with IPv6 - * and IPv4. - * - * Returns - * 1 if the **CE** flag is set (either by the current helper call - * or because it was already present), 0 if it is not set. - */ -static long (*bpf_skb_ecn_set_ce)(struct __sk_buff *skb) = (void *) 97; - -/* - * bpf_get_listener_sock - * - * Return a **struct bpf_sock** pointer in **TCP_LISTEN** state. - * **bpf_sk_release**\ () is unnecessary and not allowed. - * - * Returns - * A **struct bpf_sock** pointer on success, or **NULL** in - * case of failure. - */ -static struct bpf_sock *(*bpf_get_listener_sock)(struct bpf_sock *sk) = (void *) 98; - -/* - * bpf_skc_lookup_tcp - * - * Look for TCP socket matching *tuple*, optionally in a child - * network namespace *netns*. The return value must be checked, - * and if non-**NULL**, released via **bpf_sk_release**\ (). - * - * This function is identical to **bpf_sk_lookup_tcp**\ (), except - * that it also returns timewait or request sockets. Use - * **bpf_sk_fullsock**\ () or **bpf_tcp_sock**\ () to access the - * full structure. - * - * This helper is available only if the kernel was compiled with - * **CONFIG_NET** configuration option. - * - * Returns - * Pointer to **struct bpf_sock**, or **NULL** in case of failure. - * For sockets with reuseport option, the **struct bpf_sock** - * result is from *reuse*\ **->socks**\ [] using the hash of the - * tuple. - */ -static struct bpf_sock *(*bpf_skc_lookup_tcp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 99; - -/* - * bpf_tcp_check_syncookie - * - * Check whether *iph* and *th* contain a valid SYN cookie ACK for - * the listening socket in *sk*. - * - * *iph* points to the start of the IPv4 or IPv6 header, while - * *iph_len* contains **sizeof**\ (**struct iphdr**) or - * **sizeof**\ (**struct ip6hdr**). - * - * *th* points to the start of the TCP header, while *th_len* - * contains **sizeof**\ (**struct tcphdr**). - * - * Returns - * 0 if *iph* and *th* are a valid SYN cookie ACK, or a negative - * error otherwise. - */ -static long (*bpf_tcp_check_syncookie)(void *sk, void *iph, __u32 iph_len, struct tcphdr *th, __u32 th_len) = (void *) 100; - -/* - * bpf_sysctl_get_name - * - * Get name of sysctl in /proc/sys/ and copy it into provided by - * program buffer *buf* of size *buf_len*. - * - * The buffer is always NUL terminated, unless it's zero-sized. - * - * If *flags* is zero, full name (e.g. "net/ipv4/tcp_mem") is - * copied. Use **BPF_F_SYSCTL_BASE_NAME** flag to copy base name - * only (e.g. "tcp_mem"). - * - * Returns - * Number of character copied (not including the trailing NUL). - * - * **-E2BIG** if the buffer wasn't big enough (*buf* will contain - * truncated name in this case). - */ -static long (*bpf_sysctl_get_name)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len, __u64 flags) = (void *) 101; - -/* - * bpf_sysctl_get_current_value - * - * Get current value of sysctl as it is presented in /proc/sys - * (incl. newline, etc), and copy it as a string into provided - * by program buffer *buf* of size *buf_len*. - * - * The whole value is copied, no matter what file position user - * space issued e.g. sys_read at. - * - * The buffer is always NUL terminated, unless it's zero-sized. - * - * Returns - * Number of character copied (not including the trailing NUL). - * - * **-E2BIG** if the buffer wasn't big enough (*buf* will contain - * truncated name in this case). - * - * **-EINVAL** if current value was unavailable, e.g. because - * sysctl is uninitialized and read returns -EIO for it. - */ -static long (*bpf_sysctl_get_current_value)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len) = (void *) 102; - -/* - * bpf_sysctl_get_new_value - * - * Get new value being written by user space to sysctl (before - * the actual write happens) and copy it as a string into - * provided by program buffer *buf* of size *buf_len*. - * - * User space may write new value at file position > 0. - * - * The buffer is always NUL terminated, unless it's zero-sized. - * - * Returns - * Number of character copied (not including the trailing NUL). - * - * **-E2BIG** if the buffer wasn't big enough (*buf* will contain - * truncated name in this case). - * - * **-EINVAL** if sysctl is being read. - */ -static long (*bpf_sysctl_get_new_value)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len) = (void *) 103; - -/* - * bpf_sysctl_set_new_value - * - * Override new value being written by user space to sysctl with - * value provided by program in buffer *buf* of size *buf_len*. - * - * *buf* should contain a string in same form as provided by user - * space on sysctl write. - * - * User space may write new value at file position > 0. To override - * the whole sysctl value file position should be set to zero. - * - * Returns - * 0 on success. - * - * **-E2BIG** if the *buf_len* is too big. - * - * **-EINVAL** if sysctl is being read. - */ -static long (*bpf_sysctl_set_new_value)(struct bpf_sysctl *ctx, const char *buf, unsigned long buf_len) = (void *) 104; - -/* - * bpf_strtol - * - * Convert the initial part of the string from buffer *buf* of - * size *buf_len* to a long integer according to the given base - * and save the result in *res*. - * - * The string may begin with an arbitrary amount of white space - * (as determined by **isspace**\ (3)) followed by a single - * optional '**-**' sign. - * - * Five least significant bits of *flags* encode base, other bits - * are currently unused. - * - * Base must be either 8, 10, 16 or 0 to detect it automatically - * similar to user space **strtol**\ (3). - * - * Returns - * Number of characters consumed on success. Must be positive but - * no more than *buf_len*. - * - * **-EINVAL** if no valid digits were found or unsupported base - * was provided. - * - * **-ERANGE** if resulting value was out of range. - */ -static long (*bpf_strtol)(const char *buf, unsigned long buf_len, __u64 flags, long *res) = (void *) 105; - -/* - * bpf_strtoul - * - * Convert the initial part of the string from buffer *buf* of - * size *buf_len* to an unsigned long integer according to the - * given base and save the result in *res*. - * - * The string may begin with an arbitrary amount of white space - * (as determined by **isspace**\ (3)). - * - * Five least significant bits of *flags* encode base, other bits - * are currently unused. - * - * Base must be either 8, 10, 16 or 0 to detect it automatically - * similar to user space **strtoul**\ (3). - * - * Returns - * Number of characters consumed on success. Must be positive but - * no more than *buf_len*. - * - * **-EINVAL** if no valid digits were found or unsupported base - * was provided. - * - * **-ERANGE** if resulting value was out of range. - */ -static long (*bpf_strtoul)(const char *buf, unsigned long buf_len, __u64 flags, unsigned long *res) = (void *) 106; - -/* - * bpf_sk_storage_get - * - * Get a bpf-local-storage from a *sk*. - * - * Logically, it could be thought of getting the value from - * a *map* with *sk* as the **key**. From this - * perspective, the usage is not much different from - * **bpf_map_lookup_elem**\ (*map*, **&**\ *sk*) except this - * helper enforces the key must be a full socket and the map must - * be a **BPF_MAP_TYPE_SK_STORAGE** also. - * - * Underneath, the value is stored locally at *sk* instead of - * the *map*. The *map* is used as the bpf-local-storage - * "type". The bpf-local-storage "type" (i.e. the *map*) is - * searched against all bpf-local-storages residing at *sk*. - * - * *sk* is a kernel **struct sock** pointer for LSM program. - * *sk* is a **struct bpf_sock** pointer for other program types. - * - * An optional *flags* (**BPF_SK_STORAGE_GET_F_CREATE**) can be - * used such that a new bpf-local-storage will be - * created if one does not exist. *value* can be used - * together with **BPF_SK_STORAGE_GET_F_CREATE** to specify - * the initial value of a bpf-local-storage. If *value* is - * **NULL**, the new bpf-local-storage will be zero initialized. - * - * Returns - * A bpf-local-storage pointer is returned on success. - * - * **NULL** if not found or there was an error in adding - * a new bpf-local-storage. - */ -static void *(*bpf_sk_storage_get)(void *map, void *sk, void *value, __u64 flags) = (void *) 107; - -/* - * bpf_sk_storage_delete - * - * Delete a bpf-local-storage from a *sk*. - * - * Returns - * 0 on success. - * - * **-ENOENT** if the bpf-local-storage cannot be found. - * **-EINVAL** if sk is not a fullsock (e.g. a request_sock). - */ -static long (*bpf_sk_storage_delete)(void *map, void *sk) = (void *) 108; - -/* - * bpf_send_signal - * - * Send signal *sig* to the process of the current task. - * The signal may be delivered to any of this process's threads. - * - * Returns - * 0 on success or successfully queued. - * - * **-EBUSY** if work queue under nmi is full. - * - * **-EINVAL** if *sig* is invalid. - * - * **-EPERM** if no permission to send the *sig*. - * - * **-EAGAIN** if bpf program can try again. - */ -static long (*bpf_send_signal)(__u32 sig) = (void *) 109; - -/* - * bpf_tcp_gen_syncookie - * - * Try to issue a SYN cookie for the packet with corresponding - * IP/TCP headers, *iph* and *th*, on the listening socket in *sk*. - * - * *iph* points to the start of the IPv4 or IPv6 header, while - * *iph_len* contains **sizeof**\ (**struct iphdr**) or - * **sizeof**\ (**struct ip6hdr**). - * - * *th* points to the start of the TCP header, while *th_len* - * contains the length of the TCP header. - * - * Returns - * On success, lower 32 bits hold the generated SYN cookie in - * followed by 16 bits which hold the MSS value for that cookie, - * and the top 16 bits are unused. - * - * On failure, the returned value is one of the following: - * - * **-EINVAL** SYN cookie cannot be issued due to error - * - * **-ENOENT** SYN cookie should not be issued (no SYN flood) - * - * **-EOPNOTSUPP** kernel configuration does not enable SYN cookies - * - * **-EPROTONOSUPPORT** IP packet version is not 4 or 6 - */ -static __s64 (*bpf_tcp_gen_syncookie)(void *sk, void *iph, __u32 iph_len, struct tcphdr *th, __u32 th_len) = (void *) 110; - -/* - * bpf_skb_output - * - * Write raw *data* blob into a special BPF perf event held by - * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf - * event must have the following attributes: **PERF_SAMPLE_RAW** - * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and - * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. - * - * The *flags* are used to indicate the index in *map* for which - * the value must be put, masked with **BPF_F_INDEX_MASK**. - * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** - * to indicate that the index of the current CPU core should be - * used. - * - * The value to write, of *size*, is passed through eBPF stack and - * pointed by *data*. - * - * *ctx* is a pointer to in-kernel struct sk_buff. - * - * This helper is similar to **bpf_perf_event_output**\ () but - * restricted to raw_tracepoint bpf programs. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_skb_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 111; - -/* - * bpf_probe_read_user - * - * Safely attempt to read *size* bytes from user space address - * *unsafe_ptr* and store the data in *dst*. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_probe_read_user)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 112; - -/* - * bpf_probe_read_kernel - * - * Safely attempt to read *size* bytes from kernel space address - * *unsafe_ptr* and store the data in *dst*. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_probe_read_kernel)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 113; - -/* - * bpf_probe_read_user_str - * - * Copy a NUL terminated string from an unsafe user address - * *unsafe_ptr* to *dst*. The *size* should include the - * terminating NUL byte. In case the string length is smaller than - * *size*, the target is not padded with further NUL bytes. If the - * string length is larger than *size*, just *size*-1 bytes are - * copied and the last byte is set to NUL. - * - * On success, returns the number of bytes that were written, - * including the terminal NUL. This makes this helper useful in - * tracing programs for reading strings, and more importantly to - * get its length at runtime. See the following snippet: - * - * :: - * - * SEC("kprobe/sys_open") - * void bpf_sys_open(struct pt_regs *ctx) - * { - * char buf[PATHLEN]; // PATHLEN is defined to 256 - * int res = bpf_probe_read_user_str(buf, sizeof(buf), - * ctx->di); - * - * // Consume buf, for example push it to - * // userspace via bpf_perf_event_output(); we - * // can use res (the string length) as event - * // size, after checking its boundaries. - * } - * - * In comparison, using **bpf_probe_read_user**\ () helper here - * instead to read the string would require to estimate the length - * at compile time, and would often result in copying more memory - * than necessary. - * - * Another useful use case is when parsing individual process - * arguments or individual environment variables navigating - * *current*\ **->mm->arg_start** and *current*\ - * **->mm->env_start**: using this helper and the return value, - * one can quickly iterate at the right offset of the memory area. - * - * Returns - * On success, the strictly positive length of the output string, - * including the trailing NUL character. On error, a negative - * value. - */ -static long (*bpf_probe_read_user_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 114; - -/* - * bpf_probe_read_kernel_str - * - * Copy a NUL terminated string from an unsafe kernel address *unsafe_ptr* - * to *dst*. Same semantics as with **bpf_probe_read_user_str**\ () apply. - * - * Returns - * On success, the strictly positive length of the string, including - * the trailing NUL character. On error, a negative value. - */ -static long (*bpf_probe_read_kernel_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 115; - -/* - * bpf_tcp_send_ack - * - * Send out a tcp-ack. *tp* is the in-kernel struct **tcp_sock**. - * *rcv_nxt* is the ack_seq to be sent out. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_tcp_send_ack)(void *tp, __u32 rcv_nxt) = (void *) 116; - -/* - * bpf_send_signal_thread - * - * Send signal *sig* to the thread corresponding to the current task. - * - * Returns - * 0 on success or successfully queued. - * - * **-EBUSY** if work queue under nmi is full. - * - * **-EINVAL** if *sig* is invalid. - * - * **-EPERM** if no permission to send the *sig*. - * - * **-EAGAIN** if bpf program can try again. - */ -static long (*bpf_send_signal_thread)(__u32 sig) = (void *) 117; - -/* - * bpf_jiffies64 - * - * Obtain the 64bit jiffies - * - * Returns - * The 64 bit jiffies - */ -static __u64 (*bpf_jiffies64)(void) = (void *) 118; - -/* - * bpf_read_branch_records - * - * For an eBPF program attached to a perf event, retrieve the - * branch records (**struct perf_branch_entry**) associated to *ctx* - * and store it in the buffer pointed by *buf* up to size - * *size* bytes. - * - * Returns - * On success, number of bytes written to *buf*. On error, a - * negative value. - * - * The *flags* can be set to **BPF_F_GET_BRANCH_RECORDS_SIZE** to - * instead return the number of bytes required to store all the - * branch entries. If this flag is set, *buf* may be NULL. - * - * **-EINVAL** if arguments invalid or **size** not a multiple - * of **sizeof**\ (**struct perf_branch_entry**\ ). - * - * **-ENOENT** if architecture does not support branch records. - */ -static long (*bpf_read_branch_records)(struct bpf_perf_event_data *ctx, void *buf, __u32 size, __u64 flags) = (void *) 119; - -/* - * bpf_get_ns_current_pid_tgid - * - * Returns 0 on success, values for *pid* and *tgid* as seen from the current - * *namespace* will be returned in *nsdata*. - * - * Returns - * 0 on success, or one of the following in case of failure: - * - * **-EINVAL** if dev and inum supplied don't match dev_t and inode number - * with nsfs of current task, or if dev conversion to dev_t lost high bits. - * - * **-ENOENT** if pidns does not exists for the current task. - */ -static long (*bpf_get_ns_current_pid_tgid)(__u64 dev, __u64 ino, struct bpf_pidns_info *nsdata, __u32 size) = (void *) 120; - -/* - * bpf_xdp_output - * - * Write raw *data* blob into a special BPF perf event held by - * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf - * event must have the following attributes: **PERF_SAMPLE_RAW** - * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and - * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. - * - * The *flags* are used to indicate the index in *map* for which - * the value must be put, masked with **BPF_F_INDEX_MASK**. - * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** - * to indicate that the index of the current CPU core should be - * used. - * - * The value to write, of *size*, is passed through eBPF stack and - * pointed by *data*. - * - * *ctx* is a pointer to in-kernel struct xdp_buff. - * - * This helper is similar to **bpf_perf_eventoutput**\ () but - * restricted to raw_tracepoint bpf programs. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_xdp_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 121; - -/* - * bpf_get_netns_cookie - * - * Retrieve the cookie (generated by the kernel) of the network - * namespace the input *ctx* is associated with. The network - * namespace cookie remains stable for its lifetime and provides - * a global identifier that can be assumed unique. If *ctx* is - * NULL, then the helper returns the cookie for the initial - * network namespace. The cookie itself is very similar to that - * of **bpf_get_socket_cookie**\ () helper, but for network - * namespaces instead of sockets. - * - * Returns - * A 8-byte long opaque number. - */ -static __u64 (*bpf_get_netns_cookie)(void *ctx) = (void *) 122; - -/* - * bpf_get_current_ancestor_cgroup_id - * - * Return id of cgroup v2 that is ancestor of the cgroup associated - * with the current task at the *ancestor_level*. The root cgroup - * is at *ancestor_level* zero and each step down the hierarchy - * increments the level. If *ancestor_level* == level of cgroup - * associated with the current task, then return value will be the - * same as that of **bpf_get_current_cgroup_id**\ (). - * - * The helper is useful to implement policies based on cgroups - * that are upper in hierarchy than immediate cgroup associated - * with the current task. - * - * The format of returned id and helper limitations are same as in - * **bpf_get_current_cgroup_id**\ (). - * - * Returns - * The id is returned or 0 in case the id could not be retrieved. - */ -static __u64 (*bpf_get_current_ancestor_cgroup_id)(int ancestor_level) = (void *) 123; - -/* - * bpf_sk_assign - * - * Helper is overloaded depending on BPF program type. This - * description applies to **BPF_PROG_TYPE_SCHED_CLS** and - * **BPF_PROG_TYPE_SCHED_ACT** programs. - * - * Assign the *sk* to the *skb*. When combined with appropriate - * routing configuration to receive the packet towards the socket, - * will cause *skb* to be delivered to the specified socket. - * Subsequent redirection of *skb* via **bpf_redirect**\ (), - * **bpf_clone_redirect**\ () or other methods outside of BPF may - * interfere with successful delivery to the socket. - * - * This operation is only valid from TC ingress path. - * - * The *flags* argument must be zero. - * - * Returns - * 0 on success, or a negative error in case of failure: - * - * **-EINVAL** if specified *flags* are not supported. - * - * **-ENOENT** if the socket is unavailable for assignment. - * - * **-ENETUNREACH** if the socket is unreachable (wrong netns). - * - * **-EOPNOTSUPP** if the operation is not supported, for example - * a call from outside of TC ingress. - * - * **-ESOCKTNOSUPPORT** if the socket type is not supported - * (reuseport). - */ -static long (*bpf_sk_assign)(void *ctx, void *sk, __u64 flags) = (void *) 124; - -/* - * bpf_ktime_get_boot_ns - * - * Return the time elapsed since system boot, in nanoseconds. - * Does include the time the system was suspended. - * See: **clock_gettime**\ (**CLOCK_BOOTTIME**) - * - * Returns - * Current *ktime*. - */ -static __u64 (*bpf_ktime_get_boot_ns)(void) = (void *) 125; - -/* - * bpf_seq_printf - * - * **bpf_seq_printf**\ () uses seq_file **seq_printf**\ () to print - * out the format string. - * The *m* represents the seq_file. The *fmt* and *fmt_size* are for - * the format string itself. The *data* and *data_len* are format string - * arguments. The *data* are a **u64** array and corresponding format string - * values are stored in the array. For strings and pointers where pointees - * are accessed, only the pointer values are stored in the *data* array. - * The *data_len* is the size of *data* in bytes - must be a multiple of 8. - * - * Formats **%s**, **%p{i,I}{4,6}** requires to read kernel memory. - * Reading kernel memory may fail due to either invalid address or - * valid address but requiring a major memory fault. If reading kernel memory - * fails, the string for **%s** will be an empty string, and the ip - * address for **%p{i,I}{4,6}** will be 0. Not returning error to - * bpf program is consistent with what **bpf_trace_printk**\ () does for now. - * - * Returns - * 0 on success, or a negative error in case of failure: - * - * **-EBUSY** if per-CPU memory copy buffer is busy, can try again - * by returning 1 from bpf program. - * - * **-EINVAL** if arguments are invalid, or if *fmt* is invalid/unsupported. - * - * **-E2BIG** if *fmt* contains too many format specifiers. - * - * **-EOVERFLOW** if an overflow happened: The same object will be tried again. - */ -static long (*bpf_seq_printf)(struct seq_file *m, const char *fmt, __u32 fmt_size, const void *data, __u32 data_len) = (void *) 126; - -/* - * bpf_seq_write - * - * **bpf_seq_write**\ () uses seq_file **seq_write**\ () to write the data. - * The *m* represents the seq_file. The *data* and *len* represent the - * data to write in bytes. - * - * Returns - * 0 on success, or a negative error in case of failure: - * - * **-EOVERFLOW** if an overflow happened: The same object will be tried again. - */ -static long (*bpf_seq_write)(struct seq_file *m, const void *data, __u32 len) = (void *) 127; - -/* - * bpf_sk_cgroup_id - * - * Return the cgroup v2 id of the socket *sk*. - * - * *sk* must be a non-**NULL** pointer to a socket, e.g. one - * returned from **bpf_sk_lookup_xxx**\ (), - * **bpf_sk_fullsock**\ (), etc. The format of returned id is - * same as in **bpf_skb_cgroup_id**\ (). - * - * This helper is available only if the kernel was compiled with - * the **CONFIG_SOCK_CGROUP_DATA** configuration option. - * - * Returns - * The id is returned or 0 in case the id could not be retrieved. - */ -static __u64 (*bpf_sk_cgroup_id)(void *sk) = (void *) 128; - -/* - * bpf_sk_ancestor_cgroup_id - * - * Return id of cgroup v2 that is ancestor of cgroup associated - * with the *sk* at the *ancestor_level*. The root cgroup is at - * *ancestor_level* zero and each step down the hierarchy - * increments the level. If *ancestor_level* == level of cgroup - * associated with *sk*, then return value will be same as that - * of **bpf_sk_cgroup_id**\ (). - * - * The helper is useful to implement policies based on cgroups - * that are upper in hierarchy than immediate cgroup associated - * with *sk*. - * - * The format of returned id and helper limitations are same as in - * **bpf_sk_cgroup_id**\ (). - * - * Returns - * The id is returned or 0 in case the id could not be retrieved. - */ -static __u64 (*bpf_sk_ancestor_cgroup_id)(void *sk, int ancestor_level) = (void *) 129; - -/* - * bpf_ringbuf_output - * - * Copy *size* bytes from *data* into a ring buffer *ringbuf*. - * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification - * of new data availability is sent. - * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification - * of new data availability is sent unconditionally. - * If **0** is specified in *flags*, an adaptive notification - * of new data availability is sent. - * - * An adaptive notification is a notification sent whenever the user-space - * process has caught up and consumed all available payloads. In case the user-space - * process is still processing a previous payload, then no notification is needed - * as it will process the newly added payload automatically. - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_ringbuf_output)(void *ringbuf, void *data, __u64 size, __u64 flags) = (void *) 130; - -/* - * bpf_ringbuf_reserve - * - * Reserve *size* bytes of payload in a ring buffer *ringbuf*. - * *flags* must be 0. - * - * Returns - * Valid pointer with *size* bytes of memory available; NULL, - * otherwise. - */ -static void *(*bpf_ringbuf_reserve)(void *ringbuf, __u64 size, __u64 flags) = (void *) 131; - -/* - * bpf_ringbuf_submit - * - * Submit reserved ring buffer sample, pointed to by *data*. - * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification - * of new data availability is sent. - * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification - * of new data availability is sent unconditionally. - * If **0** is specified in *flags*, an adaptive notification - * of new data availability is sent. - * - * See 'bpf_ringbuf_output()' for the definition of adaptive notification. - * - * Returns - * Nothing. Always succeeds. - */ -static void (*bpf_ringbuf_submit)(void *data, __u64 flags) = (void *) 132; - -/* - * bpf_ringbuf_discard - * - * Discard reserved ring buffer sample, pointed to by *data*. - * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification - * of new data availability is sent. - * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification - * of new data availability is sent unconditionally. - * If **0** is specified in *flags*, an adaptive notification - * of new data availability is sent. - * - * See 'bpf_ringbuf_output()' for the definition of adaptive notification. - * - * Returns - * Nothing. Always succeeds. - */ -static void (*bpf_ringbuf_discard)(void *data, __u64 flags) = (void *) 133; - -/* - * bpf_ringbuf_query - * - * Query various characteristics of provided ring buffer. What - * exactly is queries is determined by *flags*: - * - * * **BPF_RB_AVAIL_DATA**: Amount of data not yet consumed. - * * **BPF_RB_RING_SIZE**: The size of ring buffer. - * * **BPF_RB_CONS_POS**: Consumer position (can wrap around). - * * **BPF_RB_PROD_POS**: Producer(s) position (can wrap around). - * - * Data returned is just a momentary snapshot of actual values - * and could be inaccurate, so this facility should be used to - * power heuristics and for reporting, not to make 100% correct - * calculation. - * - * Returns - * Requested value, or 0, if *flags* are not recognized. - */ -static __u64 (*bpf_ringbuf_query)(void *ringbuf, __u64 flags) = (void *) 134; - -/* - * bpf_csum_level - * - * Change the skbs checksum level by one layer up or down, or - * reset it entirely to none in order to have the stack perform - * checksum validation. The level is applicable to the following - * protocols: TCP, UDP, GRE, SCTP, FCOE. For example, a decap of - * | ETH | IP | UDP | GUE | IP | TCP | into | ETH | IP | TCP | - * through **bpf_skb_adjust_room**\ () helper with passing in - * **BPF_F_ADJ_ROOM_NO_CSUM_RESET** flag would require one call - * to **bpf_csum_level**\ () with **BPF_CSUM_LEVEL_DEC** since - * the UDP header is removed. Similarly, an encap of the latter - * into the former could be accompanied by a helper call to - * **bpf_csum_level**\ () with **BPF_CSUM_LEVEL_INC** if the - * skb is still intended to be processed in higher layers of the - * stack instead of just egressing at tc. - * - * There are three supported level settings at this time: - * - * * **BPF_CSUM_LEVEL_INC**: Increases skb->csum_level for skbs - * with CHECKSUM_UNNECESSARY. - * * **BPF_CSUM_LEVEL_DEC**: Decreases skb->csum_level for skbs - * with CHECKSUM_UNNECESSARY. - * * **BPF_CSUM_LEVEL_RESET**: Resets skb->csum_level to 0 and - * sets CHECKSUM_NONE to force checksum validation by the stack. - * * **BPF_CSUM_LEVEL_QUERY**: No-op, returns the current - * skb->csum_level. - * - * Returns - * 0 on success, or a negative error in case of failure. In the - * case of **BPF_CSUM_LEVEL_QUERY**, the current skb->csum_level - * is returned or the error code -EACCES in case the skb is not - * subject to CHECKSUM_UNNECESSARY. - */ -static long (*bpf_csum_level)(struct __sk_buff *skb, __u64 level) = (void *) 135; - -/* - * bpf_skc_to_tcp6_sock - * - * Dynamically cast a *sk* pointer to a *tcp6_sock* pointer. - * - * Returns - * *sk* if casting is valid, or **NULL** otherwise. - */ -static struct tcp6_sock *(*bpf_skc_to_tcp6_sock)(void *sk) = (void *) 136; - -/* - * bpf_skc_to_tcp_sock - * - * Dynamically cast a *sk* pointer to a *tcp_sock* pointer. - * - * Returns - * *sk* if casting is valid, or **NULL** otherwise. - */ -static struct tcp_sock *(*bpf_skc_to_tcp_sock)(void *sk) = (void *) 137; - -/* - * bpf_skc_to_tcp_timewait_sock - * - * Dynamically cast a *sk* pointer to a *tcp_timewait_sock* pointer. - * - * Returns - * *sk* if casting is valid, or **NULL** otherwise. - */ -static struct tcp_timewait_sock *(*bpf_skc_to_tcp_timewait_sock)(void *sk) = (void *) 138; - -/* - * bpf_skc_to_tcp_request_sock - * - * Dynamically cast a *sk* pointer to a *tcp_request_sock* pointer. - * - * Returns - * *sk* if casting is valid, or **NULL** otherwise. - */ -static struct tcp_request_sock *(*bpf_skc_to_tcp_request_sock)(void *sk) = (void *) 139; - -/* - * bpf_skc_to_udp6_sock - * - * Dynamically cast a *sk* pointer to a *udp6_sock* pointer. - * - * Returns - * *sk* if casting is valid, or **NULL** otherwise. - */ -static struct udp6_sock *(*bpf_skc_to_udp6_sock)(void *sk) = (void *) 140; - -/* - * bpf_get_task_stack - * - * Return a user or a kernel stack in bpf program provided buffer. - * To achieve this, the helper needs *task*, which is a valid - * pointer to **struct task_struct**. To store the stacktrace, the - * bpf program provides *buf* with a nonnegative *size*. - * - * The last argument, *flags*, holds the number of stack frames to - * skip (from 0 to 255), masked with - * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set - * the following flags: - * - * **BPF_F_USER_STACK** - * Collect a user space stack instead of a kernel stack. - * **BPF_F_USER_BUILD_ID** - * Collect buildid+offset instead of ips for user stack, - * only valid if **BPF_F_USER_STACK** is also specified. - * - * **bpf_get_task_stack**\ () can collect up to - * **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject - * to sufficient large buffer size. Note that - * this limit can be controlled with the **sysctl** program, and - * that it should be manually increased in order to profile long - * user stacks (such as stacks for Java programs). To do so, use: - * - * :: - * - * # sysctl kernel.perf_event_max_stack= - * - * Returns - * A non-negative value equal to or less than *size* on success, - * or a negative error in case of failure. - */ -static long (*bpf_get_task_stack)(struct task_struct *task, void *buf, __u32 size, __u64 flags) = (void *) 141; - -/* - * bpf_load_hdr_opt - * - * Load header option. Support reading a particular TCP header - * option for bpf program (**BPF_PROG_TYPE_SOCK_OPS**). - * - * If *flags* is 0, it will search the option from the - * *skops*\ **->skb_data**. The comment in **struct bpf_sock_ops** - * has details on what skb_data contains under different - * *skops*\ **->op**. - * - * The first byte of the *searchby_res* specifies the - * kind that it wants to search. - * - * If the searching kind is an experimental kind - * (i.e. 253 or 254 according to RFC6994). It also - * needs to specify the "magic" which is either - * 2 bytes or 4 bytes. It then also needs to - * specify the size of the magic by using - * the 2nd byte which is "kind-length" of a TCP - * header option and the "kind-length" also - * includes the first 2 bytes "kind" and "kind-length" - * itself as a normal TCP header option also does. - * - * For example, to search experimental kind 254 with - * 2 byte magic 0xeB9F, the searchby_res should be - * [ 254, 4, 0xeB, 0x9F, 0, 0, .... 0 ]. - * - * To search for the standard window scale option (3), - * the *searchby_res* should be [ 3, 0, 0, .... 0 ]. - * Note, kind-length must be 0 for regular option. - * - * Searching for No-Op (0) and End-of-Option-List (1) are - * not supported. - * - * *len* must be at least 2 bytes which is the minimal size - * of a header option. - * - * Supported flags: - * - * * **BPF_LOAD_HDR_OPT_TCP_SYN** to search from the - * saved_syn packet or the just-received syn packet. - * - * - * Returns - * > 0 when found, the header option is copied to *searchby_res*. - * The return value is the total length copied. On failure, a - * negative error code is returned: - * - * **-EINVAL** if a parameter is invalid. - * - * **-ENOMSG** if the option is not found. - * - * **-ENOENT** if no syn packet is available when - * **BPF_LOAD_HDR_OPT_TCP_SYN** is used. - * - * **-ENOSPC** if there is not enough space. Only *len* number of - * bytes are copied. - * - * **-EFAULT** on failure to parse the header options in the - * packet. - * - * **-EPERM** if the helper cannot be used under the current - * *skops*\ **->op**. - */ -static long (*bpf_load_hdr_opt)(struct bpf_sock_ops *skops, void *searchby_res, __u32 len, __u64 flags) = (void *) 142; - -/* - * bpf_store_hdr_opt - * - * Store header option. The data will be copied - * from buffer *from* with length *len* to the TCP header. - * - * The buffer *from* should have the whole option that - * includes the kind, kind-length, and the actual - * option data. The *len* must be at least kind-length - * long. The kind-length does not have to be 4 byte - * aligned. The kernel will take care of the padding - * and setting the 4 bytes aligned value to th->doff. - * - * This helper will check for duplicated option - * by searching the same option in the outgoing skb. - * - * This helper can only be called during - * **BPF_SOCK_OPS_WRITE_HDR_OPT_CB**. - * - * - * Returns - * 0 on success, or negative error in case of failure: - * - * **-EINVAL** If param is invalid. - * - * **-ENOSPC** if there is not enough space in the header. - * Nothing has been written - * - * **-EEXIST** if the option already exists. - * - * **-EFAULT** on failrue to parse the existing header options. - * - * **-EPERM** if the helper cannot be used under the current - * *skops*\ **->op**. - */ -static long (*bpf_store_hdr_opt)(struct bpf_sock_ops *skops, const void *from, __u32 len, __u64 flags) = (void *) 143; - -/* - * bpf_reserve_hdr_opt - * - * Reserve *len* bytes for the bpf header option. The - * space will be used by **bpf_store_hdr_opt**\ () later in - * **BPF_SOCK_OPS_WRITE_HDR_OPT_CB**. - * - * If **bpf_reserve_hdr_opt**\ () is called multiple times, - * the total number of bytes will be reserved. - * - * This helper can only be called during - * **BPF_SOCK_OPS_HDR_OPT_LEN_CB**. - * - * - * Returns - * 0 on success, or negative error in case of failure: - * - * **-EINVAL** if a parameter is invalid. - * - * **-ENOSPC** if there is not enough space in the header. - * - * **-EPERM** if the helper cannot be used under the current - * *skops*\ **->op**. - */ -static long (*bpf_reserve_hdr_opt)(struct bpf_sock_ops *skops, __u32 len, __u64 flags) = (void *) 144; - -/* - * bpf_inode_storage_get - * - * Get a bpf_local_storage from an *inode*. - * - * Logically, it could be thought of as getting the value from - * a *map* with *inode* as the **key**. From this - * perspective, the usage is not much different from - * **bpf_map_lookup_elem**\ (*map*, **&**\ *inode*) except this - * helper enforces the key must be an inode and the map must also - * be a **BPF_MAP_TYPE_INODE_STORAGE**. - * - * Underneath, the value is stored locally at *inode* instead of - * the *map*. The *map* is used as the bpf-local-storage - * "type". The bpf-local-storage "type" (i.e. the *map*) is - * searched against all bpf_local_storage residing at *inode*. - * - * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be - * used such that a new bpf_local_storage will be - * created if one does not exist. *value* can be used - * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify - * the initial value of a bpf_local_storage. If *value* is - * **NULL**, the new bpf_local_storage will be zero initialized. - * - * Returns - * A bpf_local_storage pointer is returned on success. - * - * **NULL** if not found or there was an error in adding - * a new bpf_local_storage. - */ -static void *(*bpf_inode_storage_get)(void *map, void *inode, void *value, __u64 flags) = (void *) 145; - -/* - * bpf_inode_storage_delete - * - * Delete a bpf_local_storage from an *inode*. - * - * Returns - * 0 on success. - * - * **-ENOENT** if the bpf_local_storage cannot be found. - */ -static int (*bpf_inode_storage_delete)(void *map, void *inode) = (void *) 146; - -/* - * bpf_d_path - * - * Return full path for given **struct path** object, which - * needs to be the kernel BTF *path* object. The path is - * returned in the provided buffer *buf* of size *sz* and - * is zero terminated. - * - * - * Returns - * On success, the strictly positive length of the string, - * including the trailing NUL character. On error, a negative - * value. - */ -static long (*bpf_d_path)(struct path *path, char *buf, __u32 sz) = (void *) 147; - -/* - * bpf_copy_from_user - * - * Read *size* bytes from user space address *user_ptr* and store - * the data in *dst*. This is a wrapper of **copy_from_user**\ (). - * - * Returns - * 0 on success, or a negative error in case of failure. - */ -static long (*bpf_copy_from_user)(void *dst, __u32 size, const void *user_ptr) = (void *) 148; - -/* - * bpf_snprintf_btf - * - * Use BTF to store a string representation of *ptr*->ptr in *str*, - * using *ptr*->type_id. This value should specify the type - * that *ptr*->ptr points to. LLVM __builtin_btf_type_id(type, 1) - * can be used to look up vmlinux BTF type ids. Traversing the - * data structure using BTF, the type information and values are - * stored in the first *str_size* - 1 bytes of *str*. Safe copy of - * the pointer data is carried out to avoid kernel crashes during - * operation. Smaller types can use string space on the stack; - * larger programs can use map data to store the string - * representation. - * - * The string can be subsequently shared with userspace via - * bpf_perf_event_output() or ring buffer interfaces. - * bpf_trace_printk() is to be avoided as it places too small - * a limit on string size to be useful. - * - * *flags* is a combination of - * - * **BTF_F_COMPACT** - * no formatting around type information - * **BTF_F_NONAME** - * no struct/union member names/types - * **BTF_F_PTR_RAW** - * show raw (unobfuscated) pointer values; - * equivalent to printk specifier %px. - * **BTF_F_ZERO** - * show zero-valued struct/union members; they - * are not displayed by default - * - * - * Returns - * The number of bytes that were written (or would have been - * written if output had to be truncated due to string size), - * or a negative error in cases of failure. - */ -static long (*bpf_snprintf_btf)(char *str, __u32 str_size, struct btf_ptr *ptr, __u32 btf_ptr_size, __u64 flags) = (void *) 149; - -/* - * bpf_seq_printf_btf - * - * Use BTF to write to seq_write a string representation of - * *ptr*->ptr, using *ptr*->type_id as per bpf_snprintf_btf(). - * *flags* are identical to those used for bpf_snprintf_btf. - * - * Returns - * 0 on success or a negative error in case of failure. - */ -static long (*bpf_seq_printf_btf)(struct seq_file *m, struct btf_ptr *ptr, __u32 ptr_size, __u64 flags) = (void *) 150; - -/* - * bpf_skb_cgroup_classid - * - * See **bpf_get_cgroup_classid**\ () for the main description. - * This helper differs from **bpf_get_cgroup_classid**\ () in that - * the cgroup v1 net_cls class is retrieved only from the *skb*'s - * associated socket instead of the current process. - * - * Returns - * The id is returned or 0 in case the id could not be retrieved. - */ -static __u64 (*bpf_skb_cgroup_classid)(struct __sk_buff *skb) = (void *) 151; - -/* - * bpf_redirect_neigh - * - * Redirect the packet to another net device of index *ifindex* - * and fill in L2 addresses from neighboring subsystem. This helper - * is somewhat similar to **bpf_redirect**\ (), except that it - * populates L2 addresses as well, meaning, internally, the helper - * relies on the neighbor lookup for the L2 address of the nexthop. - * - * The helper will perform a FIB lookup based on the skb's - * networking header to get the address of the next hop, unless - * this is supplied by the caller in the *params* argument. The - * *plen* argument indicates the len of *params* and should be set - * to 0 if *params* is NULL. - * - * The *flags* argument is reserved and must be 0. The helper is - * currently only supported for tc BPF program types, and enabled - * for IPv4 and IPv6 protocols. - * - * Returns - * The helper returns **TC_ACT_REDIRECT** on success or - * **TC_ACT_SHOT** on error. - */ -static long (*bpf_redirect_neigh)(__u32 ifindex, struct bpf_redir_neigh *params, int plen, __u64 flags) = (void *) 152; - -/* - * bpf_per_cpu_ptr - * - * Take a pointer to a percpu ksym, *percpu_ptr*, and return a - * pointer to the percpu kernel variable on *cpu*. A ksym is an - * extern variable decorated with '__ksym'. For ksym, there is a - * global var (either static or global) defined of the same name - * in the kernel. The ksym is percpu if the global var is percpu. - * The returned pointer points to the global percpu var on *cpu*. - * - * bpf_per_cpu_ptr() has the same semantic as per_cpu_ptr() in the - * kernel, except that bpf_per_cpu_ptr() may return NULL. This - * happens if *cpu* is larger than nr_cpu_ids. The caller of - * bpf_per_cpu_ptr() must check the returned value. - * - * Returns - * A pointer pointing to the kernel percpu variable on *cpu*, or - * NULL, if *cpu* is invalid. - */ -static void *(*bpf_per_cpu_ptr)(const void *percpu_ptr, __u32 cpu) = (void *) 153; - -/* - * bpf_this_cpu_ptr - * - * Take a pointer to a percpu ksym, *percpu_ptr*, and return a - * pointer to the percpu kernel variable on this cpu. See the - * description of 'ksym' in **bpf_per_cpu_ptr**\ (). - * - * bpf_this_cpu_ptr() has the same semantic as this_cpu_ptr() in - * the kernel. Different from **bpf_per_cpu_ptr**\ (), it would - * never return NULL. - * - * Returns - * A pointer pointing to the kernel percpu variable on this cpu. - */ -static void *(*bpf_this_cpu_ptr)(const void *percpu_ptr) = (void *) 154; - -/* - * bpf_redirect_peer - * - * Redirect the packet to another net device of index *ifindex*. - * This helper is somewhat similar to **bpf_redirect**\ (), except - * that the redirection happens to the *ifindex*' peer device and - * the netns switch takes place from ingress to ingress without - * going through the CPU's backlog queue. - * - * The *flags* argument is reserved and must be 0. The helper is - * currently only supported for tc BPF program types at the ingress - * hook and for veth device types. The peer device must reside in a - * different network namespace. - * - * Returns - * The helper returns **TC_ACT_REDIRECT** on success or - * **TC_ACT_SHOT** on error. - */ -static long (*bpf_redirect_peer)(__u32 ifindex, __u64 flags) = (void *) 155; - -/* - * bpf_task_storage_get - * - * Get a bpf_local_storage from the *task*. - * - * Logically, it could be thought of as getting the value from - * a *map* with *task* as the **key**. From this - * perspective, the usage is not much different from - * **bpf_map_lookup_elem**\ (*map*, **&**\ *task*) except this - * helper enforces the key must be an task_struct and the map must also - * be a **BPF_MAP_TYPE_TASK_STORAGE**. - * - * Underneath, the value is stored locally at *task* instead of - * the *map*. The *map* is used as the bpf-local-storage - * "type". The bpf-local-storage "type" (i.e. the *map*) is - * searched against all bpf_local_storage residing at *task*. - * - * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be - * used such that a new bpf_local_storage will be - * created if one does not exist. *value* can be used - * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify - * the initial value of a bpf_local_storage. If *value* is - * **NULL**, the new bpf_local_storage will be zero initialized. - * - * Returns - * A bpf_local_storage pointer is returned on success. - * - * **NULL** if not found or there was an error in adding - * a new bpf_local_storage. - */ -static void *(*bpf_task_storage_get)(void *map, struct task_struct *task, void *value, __u64 flags) = (void *) 156; - -/* - * bpf_task_storage_delete - * - * Delete a bpf_local_storage from a *task*. - * - * Returns - * 0 on success. - * - * **-ENOENT** if the bpf_local_storage cannot be found. - */ -static long (*bpf_task_storage_delete)(void *map, struct task_struct *task) = (void *) 157; - -/* - * bpf_get_current_task_btf - * - * Return a BTF pointer to the "current" task. - * This pointer can also be used in helpers that accept an - * *ARG_PTR_TO_BTF_ID* of type *task_struct*. - * - * Returns - * Pointer to the current task. - */ -static struct task_struct *(*bpf_get_current_task_btf)(void) = (void *) 158; - -/* - * bpf_bprm_opts_set - * - * Set or clear certain options on *bprm*: - * - * **BPF_F_BPRM_SECUREEXEC** Set the secureexec bit - * which sets the **AT_SECURE** auxv for glibc. The bit - * is cleared if the flag is not specified. - * - * Returns - * **-EINVAL** if invalid *flags* are passed, zero otherwise. - */ -static long (*bpf_bprm_opts_set)(struct linux_binprm *bprm, __u64 flags) = (void *) 159; - -/* - * bpf_ktime_get_coarse_ns - * - * Return a coarse-grained version of the time elapsed since - * system boot, in nanoseconds. Does not include time the system - * was suspended. - * - * See: **clock_gettime**\ (**CLOCK_MONOTONIC_COARSE**) - * - * Returns - * Current *ktime*. - */ -static __u64 (*bpf_ktime_get_coarse_ns)(void) = (void *) 160; - -/* - * bpf_ima_inode_hash - * - * Returns the stored IMA hash of the *inode* (if it's avaialable). - * If the hash is larger than *size*, then only *size* - * bytes will be copied to *dst* - * - * Returns - * The **hash_algo** is returned on success, - * **-EOPNOTSUP** if IMA is disabled or **-EINVAL** if - * invalid arguments are passed. - */ -static long (*bpf_ima_inode_hash)(struct inode *inode, void *dst, __u32 size) = (void *) 161; - -/* - * bpf_sock_from_file - * - * If the given file represents a socket, returns the associated - * socket. - * - * Returns - * A pointer to a struct socket on success or NULL if the file is - * not a socket. - */ -static struct socket *(*bpf_sock_from_file)(struct file *file) = (void *) 162; - -/* - * bpf_check_mtu - * - * Check packet size against exceeding MTU of net device (based - * on *ifindex*). This helper will likely be used in combination - * with helpers that adjust/change the packet size. - * - * The argument *len_diff* can be used for querying with a planned - * size change. This allows to check MTU prior to changing packet - * ctx. Providing an *len_diff* adjustment that is larger than the - * actual packet size (resulting in negative packet size) will in - * principle not exceed the MTU, why it is not considered a - * failure. Other BPF-helpers are needed for performing the - * planned size change, why the responsability for catch a negative - * packet size belong in those helpers. - * - * Specifying *ifindex* zero means the MTU check is performed - * against the current net device. This is practical if this isn't - * used prior to redirect. - * - * On input *mtu_len* must be a valid pointer, else verifier will - * reject BPF program. If the value *mtu_len* is initialized to - * zero then the ctx packet size is use. When value *mtu_len* is - * provided as input this specify the L3 length that the MTU check - * is done against. Remember XDP and TC length operate at L2, but - * this value is L3 as this correlate to MTU and IP-header tot_len - * values which are L3 (similar behavior as bpf_fib_lookup). - * - * The Linux kernel route table can configure MTUs on a more - * specific per route level, which is not provided by this helper. - * For route level MTU checks use the **bpf_fib_lookup**\ () - * helper. - * - * *ctx* is either **struct xdp_md** for XDP programs or - * **struct sk_buff** for tc cls_act programs. - * - * The *flags* argument can be a combination of one or more of the - * following values: - * - * **BPF_MTU_CHK_SEGS** - * This flag will only works for *ctx* **struct sk_buff**. - * If packet context contains extra packet segment buffers - * (often knows as GSO skb), then MTU check is harder to - * check at this point, because in transmit path it is - * possible for the skb packet to get re-segmented - * (depending on net device features). This could still be - * a MTU violation, so this flag enables performing MTU - * check against segments, with a different violation - * return code to tell it apart. Check cannot use len_diff. - * - * On return *mtu_len* pointer contains the MTU value of the net - * device. Remember the net device configured MTU is the L3 size, - * which is returned here and XDP and TC length operate at L2. - * Helper take this into account for you, but remember when using - * MTU value in your BPF-code. - * - * - * Returns - * * 0 on success, and populate MTU value in *mtu_len* pointer. - * - * * < 0 if any input argument is invalid (*mtu_len* not updated) - * - * MTU violations return positive values, but also populate MTU - * value in *mtu_len* pointer, as this can be needed for - * implementing PMTU handing: - * - * * **BPF_MTU_CHK_RET_FRAG_NEEDED** - * * **BPF_MTU_CHK_RET_SEGS_TOOBIG** - */ -static long (*bpf_check_mtu)(void *ctx, __u32 ifindex, __u32 *mtu_len, __s32 len_diff, __u64 flags) = (void *) 163; - -/* - * bpf_for_each_map_elem - * - * For each element in **map**, call **callback_fn** function with - * **map**, **callback_ctx** and other map-specific parameters. - * The **callback_fn** should be a static function and - * the **callback_ctx** should be a pointer to the stack. - * The **flags** is used to control certain aspects of the helper. - * Currently, the **flags** must be 0. - * - * The following are a list of supported map types and their - * respective expected callback signatures: - * - * BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_PERCPU_HASH, - * BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH, - * BPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_PERCPU_ARRAY - * - * long (\*callback_fn)(struct bpf_map \*map, const void \*key, void \*value, void \*ctx); - * - * For per_cpu maps, the map_value is the value on the cpu where the - * bpf_prog is running. - * - * If **callback_fn** return 0, the helper will continue to the next - * element. If return value is 1, the helper will skip the rest of - * elements and return. Other return values are not used now. - * - * - * Returns - * The number of traversed map elements for success, **-EINVAL** for - * invalid **flags**. - */ -static long (*bpf_for_each_map_elem)(void *map, void *callback_fn, void *callback_ctx, __u64 flags) = (void *) 164; - -/* - * bpf_snprintf - * - * Outputs a string into the **str** buffer of size **str_size** - * based on a format string stored in a read-only map pointed by - * **fmt**. - * - * Each format specifier in **fmt** corresponds to one u64 element - * in the **data** array. For strings and pointers where pointees - * are accessed, only the pointer values are stored in the *data* - * array. The *data_len* is the size of *data* in bytes - must be - * a multiple of 8. - * - * Formats **%s** and **%p{i,I}{4,6}** require to read kernel - * memory. Reading kernel memory may fail due to either invalid - * address or valid address but requiring a major memory fault. If - * reading kernel memory fails, the string for **%s** will be an - * empty string, and the ip address for **%p{i,I}{4,6}** will be 0. - * Not returning error to bpf program is consistent with what - * **bpf_trace_printk**\ () does for now. - * - * - * Returns - * The strictly positive length of the formatted string, including - * the trailing zero character. If the return value is greater than - * **str_size**, **str** contains a truncated string, guaranteed to - * be zero-terminated except when **str_size** is 0. - * - * Or **-EBUSY** if the per-CPU memory copy buffer is busy. - */ -static long (*bpf_snprintf)(char *str, __u32 str_size, const char *fmt, __u64 *data, __u32 data_len) = (void *) 165; - -/* - * bpf_sys_bpf - * - * Execute bpf syscall with given arguments. - * - * Returns - * A syscall result. - */ -static long (*bpf_sys_bpf)(__u32 cmd, void *attr, __u32 attr_size) = (void *) 166; - -/* - * bpf_btf_find_by_name_kind - * - * Find BTF type with given name and kind in vmlinux BTF or in module's BTFs. - * - * Returns - * Returns btf_id and btf_obj_fd in lower and upper 32 bits. - */ -static long (*bpf_btf_find_by_name_kind)(char *name, int name_sz, __u32 kind, int flags) = (void *) 167; - -/* - * bpf_sys_close - * - * Execute close syscall for given FD. - * - * Returns - * A syscall result. - */ -static long (*bpf_sys_close)(__u32 fd) = (void *) 168; - -/* - * bpf_timer_init - * - * Initialize the timer. - * First 4 bits of *flags* specify clockid. - * Only CLOCK_MONOTONIC, CLOCK_REALTIME, CLOCK_BOOTTIME are allowed. - * All other bits of *flags* are reserved. - * The verifier will reject the program if *timer* is not from - * the same *map*. - * - * Returns - * 0 on success. - * **-EBUSY** if *timer* is already initialized. - * **-EINVAL** if invalid *flags* are passed. - * **-EPERM** if *timer* is in a map that doesn't have any user references. - * The user space should either hold a file descriptor to a map with timers - * or pin such map in bpffs. When map is unpinned or file descriptor is - * closed all timers in the map will be cancelled and freed. - */ -static long (*bpf_timer_init)(struct bpf_timer *timer, void *map, __u64 flags) = (void *) 169; - -/* - * bpf_timer_set_callback - * - * Configure the timer to call *callback_fn* static function. - * - * Returns - * 0 on success. - * **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier. - * **-EPERM** if *timer* is in a map that doesn't have any user references. - * The user space should either hold a file descriptor to a map with timers - * or pin such map in bpffs. When map is unpinned or file descriptor is - * closed all timers in the map will be cancelled and freed. - */ -static long (*bpf_timer_set_callback)(struct bpf_timer *timer, void *callback_fn) = (void *) 170; - -/* - * bpf_timer_start - * - * Set timer expiration N nanoseconds from the current time. The - * configured callback will be invoked in soft irq context on some cpu - * and will not repeat unless another bpf_timer_start() is made. - * In such case the next invocation can migrate to a different cpu. - * Since struct bpf_timer is a field inside map element the map - * owns the timer. The bpf_timer_set_callback() will increment refcnt - * of BPF program to make sure that callback_fn code stays valid. - * When user space reference to a map reaches zero all timers - * in a map are cancelled and corresponding program's refcnts are - * decremented. This is done to make sure that Ctrl-C of a user - * process doesn't leave any timers running. If map is pinned in - * bpffs the callback_fn can re-arm itself indefinitely. - * bpf_map_update/delete_elem() helpers and user space sys_bpf commands - * cancel and free the timer in the given map element. - * The map can contain timers that invoke callback_fn-s from different - * programs. The same callback_fn can serve different timers from - * different maps if key/value layout matches across maps. - * Every bpf_timer_set_callback() can have different callback_fn. - * - * - * Returns - * 0 on success. - * **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier - * or invalid *flags* are passed. - */ -static long (*bpf_timer_start)(struct bpf_timer *timer, __u64 nsecs, __u64 flags) = (void *) 171; - -/* - * bpf_timer_cancel - * - * Cancel the timer and wait for callback_fn to finish if it was running. - * - * Returns - * 0 if the timer was not active. - * 1 if the timer was active. - * **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier. - * **-EDEADLK** if callback_fn tried to call bpf_timer_cancel() on its - * own timer which would have led to a deadlock otherwise. - */ -static long (*bpf_timer_cancel)(struct bpf_timer *timer) = (void *) 172; - -/* - * bpf_get_func_ip - * - * Get address of the traced function (for tracing and kprobe programs). - * - * Returns - * Address of the traced function. - */ -static __u64 (*bpf_get_func_ip)(void *ctx) = (void *) 173; - -/* - * bpf_get_attach_cookie - * - * Get bpf_cookie value provided (optionally) during the program - * attachment. It might be different for each individual - * attachment, even if BPF program itself is the same. - * Expects BPF program context *ctx* as a first argument. - * - * Supported for the following program types: - * - kprobe/uprobe; - * - tracepoint; - * - perf_event. - * - * Returns - * Value specified by user at BPF link creation/attachment time - * or 0, if it was not specified. - */ -static __u64 (*bpf_get_attach_cookie)(void *ctx) = (void *) 174; - -/* - * bpf_task_pt_regs - * - * Get the struct pt_regs associated with **task**. - * - * Returns - * A pointer to struct pt_regs. - */ -static long (*bpf_task_pt_regs)(struct task_struct *task) = (void *) 175; - -/* - * bpf_get_branch_snapshot - * - * Get branch trace from hardware engines like Intel LBR. The - * hardware engine is stopped shortly after the helper is - * called. Therefore, the user need to filter branch entries - * based on the actual use case. To capture branch trace - * before the trigger point of the BPF program, the helper - * should be called at the beginning of the BPF program. - * - * The data is stored as struct perf_branch_entry into output - * buffer *entries*. *size* is the size of *entries* in bytes. - * *flags* is reserved for now and must be zero. - * - * - * Returns - * On success, number of bytes written to *buf*. On error, a - * negative value. - * - * **-EINVAL** if *flags* is not zero. - * - * **-ENOENT** if architecture does not support branch records. - */ -static long (*bpf_get_branch_snapshot)(void *entries, __u32 size, __u64 flags) = (void *) 176; - -/* - * bpf_trace_vprintk - * - * Behaves like **bpf_trace_printk**\ () helper, but takes an array of u64 - * to format and can handle more format args as a result. - * - * Arguments are to be used as in **bpf_seq_printf**\ () helper. - * - * Returns - * The number of bytes written to the buffer, or a negative error - * in case of failure. - */ -static long (*bpf_trace_vprintk)(const char *fmt, __u32 fmt_size, const void *data, __u32 data_len) = (void *) 177; - -/* - * bpf_skc_to_unix_sock - * - * Dynamically cast a *sk* pointer to a *unix_sock* pointer. - * - * Returns - * *sk* if casting is valid, or **NULL** otherwise. - */ -static struct unix_sock *(*bpf_skc_to_unix_sock)(void *sk) = (void *) 178; - -/* - * bpf_kallsyms_lookup_name - * - * Get the address of a kernel symbol, returned in *res*. *res* is - * set to 0 if the symbol is not found. - * - * Returns - * On success, zero. On error, a negative value. - * - * **-EINVAL** if *flags* is not zero. - * - * **-EINVAL** if string *name* is not the same size as *name_sz*. - * - * **-ENOENT** if symbol is not found. - * - * **-EPERM** if caller does not have permission to obtain kernel address. - */ -static long (*bpf_kallsyms_lookup_name)(const char *name, int name_sz, int flags, __u64 *res) = (void *) 179; - -/* - * bpf_find_vma - * - * Find vma of *task* that contains *addr*, call *callback_fn* - * function with *task*, *vma*, and *callback_ctx*. - * The *callback_fn* should be a static function and - * the *callback_ctx* should be a pointer to the stack. - * The *flags* is used to control certain aspects of the helper. - * Currently, the *flags* must be 0. - * - * The expected callback signature is - * - * long (\*callback_fn)(struct task_struct \*task, struct vm_area_struct \*vma, void \*callback_ctx); - * - * - * Returns - * 0 on success. - * **-ENOENT** if *task->mm* is NULL, or no vma contains *addr*. - * **-EBUSY** if failed to try lock mmap_lock. - * **-EINVAL** for invalid **flags**. - */ -static long (*bpf_find_vma)(struct task_struct *task, __u64 addr, void *callback_fn, void *callback_ctx, __u64 flags) = (void *) 180; - - diff --git a/component/ebpf/bpf/bpf_helpers.h b/component/ebpf/bpf/bpf_helpers.h deleted file mode 100644 index 963b1060..00000000 --- a/component/ebpf/bpf/bpf_helpers.h +++ /dev/null @@ -1,262 +0,0 @@ -/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ -#ifndef __BPF_HELPERS__ -#define __BPF_HELPERS__ - -/* - * Note that bpf programs need to include either - * vmlinux.h (auto-generated from BTF) or linux/types.h - * in advance since bpf_helper_defs.h uses such types - * as __u64. - */ -#include "bpf_helper_defs.h" - -#define __uint(name, val) int (*name)[val] -#define __type(name, val) typeof(val) *name -#define __array(name, val) typeof(val) *name[] - -/* - * Helper macro to place programs, maps, license in - * different sections in elf_bpf file. Section names - * are interpreted by libbpf depending on the context (BPF programs, BPF maps, - * extern variables, etc). - * To allow use of SEC() with externs (e.g., for extern .maps declarations), - * make sure __attribute__((unused)) doesn't trigger compilation warning. - */ -#define SEC(name) \ - _Pragma("GCC diagnostic push") \ - _Pragma("GCC diagnostic ignored \"-Wignored-attributes\"") \ - __attribute__((section(name), used)) \ - _Pragma("GCC diagnostic pop") \ - -/* Avoid 'linux/stddef.h' definition of '__always_inline'. */ -#undef __always_inline -#define __always_inline inline __attribute__((always_inline)) - -#ifndef __noinline -#define __noinline __attribute__((noinline)) -#endif -#ifndef __weak -#define __weak __attribute__((weak)) -#endif - -/* - * Use __hidden attribute to mark a non-static BPF subprogram effectively - * static for BPF verifier's verification algorithm purposes, allowing more - * extensive and permissive BPF verification process, taking into account - * subprogram's caller context. - */ -#define __hidden __attribute__((visibility("hidden"))) - -/* When utilizing vmlinux.h with BPF CO-RE, user BPF programs can't include - * any system-level headers (such as stddef.h, linux/version.h, etc), and - * commonly-used macros like NULL and KERNEL_VERSION aren't available through - * vmlinux.h. This just adds unnecessary hurdles and forces users to re-define - * them on their own. So as a convenience, provide such definitions here. - */ -#ifndef NULL -#define NULL ((void *)0) -#endif - -#ifndef KERNEL_VERSION -#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + ((c) > 255 ? 255 : (c))) -#endif - -/* - * Helper macros to manipulate data structures - */ -#ifndef offsetof -#define offsetof(TYPE, MEMBER) ((unsigned long)&((TYPE *)0)->MEMBER) -#endif -#ifndef container_of -#define container_of(ptr, type, member) \ - ({ \ - void *__mptr = (void *)(ptr); \ - ((type *)(__mptr - offsetof(type, member))); \ - }) -#endif - -/* - * Helper macro to throw a compilation error if __bpf_unreachable() gets - * built into the resulting code. This works given BPF back end does not - * implement __builtin_trap(). This is useful to assert that certain paths - * of the program code are never used and hence eliminated by the compiler. - * - * For example, consider a switch statement that covers known cases used by - * the program. __bpf_unreachable() can then reside in the default case. If - * the program gets extended such that a case is not covered in the switch - * statement, then it will throw a build error due to the default case not - * being compiled out. - */ -#ifndef __bpf_unreachable -# define __bpf_unreachable() __builtin_trap() -#endif - -/* - * Helper function to perform a tail call with a constant/immediate map slot. - */ -#if __clang_major__ >= 8 && defined(__bpf__) -static __always_inline void -bpf_tail_call_static(void *ctx, const void *map, const __u32 slot) -{ - if (!__builtin_constant_p(slot)) - __bpf_unreachable(); - - /* - * Provide a hard guarantee that LLVM won't optimize setting r2 (map - * pointer) and r3 (constant map index) from _different paths_ ending - * up at the _same_ call insn as otherwise we won't be able to use the - * jmpq/nopl retpoline-free patching by the x86-64 JIT in the kernel - * given they mismatch. See also d2e4c1e6c294 ("bpf: Constant map key - * tracking for prog array pokes") for details on verifier tracking. - * - * Note on clobber list: we need to stay in-line with BPF calling - * convention, so even if we don't end up using r0, r4, r5, we need - * to mark them as clobber so that LLVM doesn't end up using them - * before / after the call. - */ - asm volatile("r1 = %[ctx]\n\t" - "r2 = %[map]\n\t" - "r3 = %[slot]\n\t" - "call 12" - :: [ctx]"r"(ctx), [map]"r"(map), [slot]"i"(slot) - : "r0", "r1", "r2", "r3", "r4", "r5"); -} -#endif - -/* - * Helper structure used by eBPF C program - * to describe BPF map attributes to libbpf loader - */ -struct bpf_map_def { - unsigned int type; - unsigned int key_size; - unsigned int value_size; - unsigned int max_entries; - unsigned int map_flags; -}; - -enum libbpf_pin_type { - LIBBPF_PIN_NONE, - /* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */ - LIBBPF_PIN_BY_NAME, -}; - -enum libbpf_tristate { - TRI_NO = 0, - TRI_YES = 1, - TRI_MODULE = 2, -}; - -#define __kconfig __attribute__((section(".kconfig"))) -#define __ksym __attribute__((section(".ksyms"))) - -#ifndef ___bpf_concat -#define ___bpf_concat(a, b) a ## b -#endif -#ifndef ___bpf_apply -#define ___bpf_apply(fn, n) ___bpf_concat(fn, n) -#endif -#ifndef ___bpf_nth -#define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N -#endif -#ifndef ___bpf_narg -#define ___bpf_narg(...) \ - ___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) -#endif - -#define ___bpf_fill0(arr, p, x) do {} while (0) -#define ___bpf_fill1(arr, p, x) arr[p] = x -#define ___bpf_fill2(arr, p, x, args...) arr[p] = x; ___bpf_fill1(arr, p + 1, args) -#define ___bpf_fill3(arr, p, x, args...) arr[p] = x; ___bpf_fill2(arr, p + 1, args) -#define ___bpf_fill4(arr, p, x, args...) arr[p] = x; ___bpf_fill3(arr, p + 1, args) -#define ___bpf_fill5(arr, p, x, args...) arr[p] = x; ___bpf_fill4(arr, p + 1, args) -#define ___bpf_fill6(arr, p, x, args...) arr[p] = x; ___bpf_fill5(arr, p + 1, args) -#define ___bpf_fill7(arr, p, x, args...) arr[p] = x; ___bpf_fill6(arr, p + 1, args) -#define ___bpf_fill8(arr, p, x, args...) arr[p] = x; ___bpf_fill7(arr, p + 1, args) -#define ___bpf_fill9(arr, p, x, args...) arr[p] = x; ___bpf_fill8(arr, p + 1, args) -#define ___bpf_fill10(arr, p, x, args...) arr[p] = x; ___bpf_fill9(arr, p + 1, args) -#define ___bpf_fill11(arr, p, x, args...) arr[p] = x; ___bpf_fill10(arr, p + 1, args) -#define ___bpf_fill12(arr, p, x, args...) arr[p] = x; ___bpf_fill11(arr, p + 1, args) -#define ___bpf_fill(arr, args...) \ - ___bpf_apply(___bpf_fill, ___bpf_narg(args))(arr, 0, args) - -/* - * BPF_SEQ_PRINTF to wrap bpf_seq_printf to-be-printed values - * in a structure. - */ -#define BPF_SEQ_PRINTF(seq, fmt, args...) \ -({ \ - static const char ___fmt[] = fmt; \ - unsigned long long ___param[___bpf_narg(args)]; \ - \ - _Pragma("GCC diagnostic push") \ - _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ - ___bpf_fill(___param, args); \ - _Pragma("GCC diagnostic pop") \ - \ - bpf_seq_printf(seq, ___fmt, sizeof(___fmt), \ - ___param, sizeof(___param)); \ -}) - -/* - * BPF_SNPRINTF wraps the bpf_snprintf helper with variadic arguments instead of - * an array of u64. - */ -#define BPF_SNPRINTF(out, out_size, fmt, args...) \ -({ \ - static const char ___fmt[] = fmt; \ - unsigned long long ___param[___bpf_narg(args)]; \ - \ - _Pragma("GCC diagnostic push") \ - _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ - ___bpf_fill(___param, args); \ - _Pragma("GCC diagnostic pop") \ - \ - bpf_snprintf(out, out_size, ___fmt, \ - ___param, sizeof(___param)); \ -}) - -#ifdef BPF_NO_GLOBAL_DATA -#define BPF_PRINTK_FMT_MOD -#else -#define BPF_PRINTK_FMT_MOD static const -#endif - -#define __bpf_printk(fmt, ...) \ -({ \ - BPF_PRINTK_FMT_MOD char ____fmt[] = fmt; \ - bpf_trace_printk(____fmt, sizeof(____fmt), \ - ##__VA_ARGS__); \ -}) - -/* - * __bpf_vprintk wraps the bpf_trace_vprintk helper with variadic arguments - * instead of an array of u64. - */ -#define __bpf_vprintk(fmt, args...) \ -({ \ - static const char ___fmt[] = fmt; \ - unsigned long long ___param[___bpf_narg(args)]; \ - \ - _Pragma("GCC diagnostic push") \ - _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ - ___bpf_fill(___param, args); \ - _Pragma("GCC diagnostic pop") \ - \ - bpf_trace_vprintk(___fmt, sizeof(___fmt), \ - ___param, sizeof(___param)); \ -}) - -/* Use __bpf_printk when bpf_printk call has 3 or fewer fmt args - * Otherwise use __bpf_vprintk - */ -#define ___bpf_pick_printk(...) \ - ___bpf_nth(_, ##__VA_ARGS__, __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, \ - __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, \ - __bpf_vprintk, __bpf_vprintk, __bpf_printk /*3*/, __bpf_printk /*2*/,\ - __bpf_printk /*1*/, __bpf_printk /*0*/) - -/* Helper macro to print out debug messages */ -#define bpf_printk(fmt, args...) ___bpf_pick_printk(args)(fmt, ##args) - -#endif diff --git a/component/ebpf/bpf/redir.c b/component/ebpf/bpf/redir.c deleted file mode 100644 index 6ef5ee0c..00000000 --- a/component/ebpf/bpf/redir.c +++ /dev/null @@ -1,342 +0,0 @@ -#include -#include -//#include - -#include -#include -//#include -//#include -#include -#include -#include -//#include - -#include - -#include "bpf_endian.h" -#include "bpf_helpers.h" - -#define IP_CSUM_OFF (ETH_HLEN + offsetof(struct iphdr, check)) -#define IP_DST_OFF (ETH_HLEN + offsetof(struct iphdr, daddr)) -#define IP_SRC_OFF (ETH_HLEN + offsetof(struct iphdr, saddr)) -#define IP_PROTO_OFF (ETH_HLEN + offsetof(struct iphdr, protocol)) -#define TCP_CSUM_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct tcphdr, check)) -#define TCP_SRC_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct tcphdr, source)) -#define TCP_DST_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct tcphdr, dest)) -//#define UDP_CSUM_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct udphdr, check)) -//#define UDP_SRC_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct udphdr, source)) -//#define UDP_DST_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct udphdr, dest)) -#define IS_PSEUDO 0x10 - -struct origin_info { - __be32 ip; - __be16 port; - __u16 pad; -}; - -struct origin_info *origin_info_unused __attribute__((unused)); - -struct redir_info { - __be32 sip; - __be32 dip; - __be16 sport; - __be16 dport; -}; - -struct redir_info *redir_info_unused __attribute__((unused)); - -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __type(key, struct redir_info); - __type(value, struct origin_info); - __uint(max_entries, 65535); - __uint(pinning, LIBBPF_PIN_BY_NAME); -} pair_original_dst_map SEC(".maps"); - -struct { - __uint(type, BPF_MAP_TYPE_ARRAY); - __type(key, __u32); - __type(value, __u32); - __uint(max_entries, 3); - __uint(pinning, LIBBPF_PIN_BY_NAME); -} redir_params_map SEC(".maps"); - -static __always_inline int rewrite_ip(struct __sk_buff *skb, __be32 new_ip, bool is_dest) { - int ret, off = 0, flags = IS_PSEUDO; - __be32 old_ip; - - if (is_dest) - ret = bpf_skb_load_bytes(skb, IP_DST_OFF, &old_ip, 4); - else - ret = bpf_skb_load_bytes(skb, IP_SRC_OFF, &old_ip, 4); - - if (ret < 0) { - return ret; - } - - off = TCP_CSUM_OFF; -// __u8 proto; -// -// ret = bpf_skb_load_bytes(skb, IP_PROTO_OFF, &proto, 1); -// if (ret < 0) { -// return BPF_DROP; -// } -// -// switch (proto) { -// case IPPROTO_TCP: -// off = TCP_CSUM_OFF; -// break; -// -// case IPPROTO_UDP: -// off = UDP_CSUM_OFF; -// flags |= BPF_F_MARK_MANGLED_0; -// break; -// -// case IPPROTO_ICMPV6: -// off = offsetof(struct icmp6hdr, icmp6_cksum); -// break; -// } -// -// if (off) { - ret = bpf_l4_csum_replace(skb, off, old_ip, new_ip, flags | sizeof(new_ip)); - if (ret < 0) { - return ret; - } -// } - - ret = bpf_l3_csum_replace(skb, IP_CSUM_OFF, old_ip, new_ip, sizeof(new_ip)); - if (ret < 0) { - return ret; - } - - if (is_dest) - ret = bpf_skb_store_bytes(skb, IP_DST_OFF, &new_ip, sizeof(new_ip), 0); - else - ret = bpf_skb_store_bytes(skb, IP_SRC_OFF, &new_ip, sizeof(new_ip), 0); - - if (ret < 0) { - return ret; - } - - return 1; -} - -static __always_inline int rewrite_port(struct __sk_buff *skb, __be16 new_port, bool is_dest) { - int ret, off = 0; - __be16 old_port; - - if (is_dest) - ret = bpf_skb_load_bytes(skb, TCP_DST_OFF, &old_port, 2); - else - ret = bpf_skb_load_bytes(skb, TCP_SRC_OFF, &old_port, 2); - - if (ret < 0) { - return ret; - } - - off = TCP_CSUM_OFF; - - ret = bpf_l4_csum_replace(skb, off, old_port, new_port, sizeof(new_port)); - if (ret < 0) { - return ret; - } - - if (is_dest) - ret = bpf_skb_store_bytes(skb, TCP_DST_OFF, &new_port, sizeof(new_port), 0); - else - ret = bpf_skb_store_bytes(skb, TCP_SRC_OFF, &new_port, sizeof(new_port), 0); - - if (ret < 0) { - return ret; - } - - return 1; -} - -static __always_inline bool is_lan_ip(__be32 addr) { - if (addr == 0xffffffff) - return true; - - __u8 fist = (__u8)(addr & 0xff); - - if (fist == 127 || fist == 10) - return true; - - __u8 second = (__u8)((addr >> 8) & 0xff); - - if (fist == 172 && second >= 16 && second <= 31) - return true; - - if (fist == 192 && second == 168) - return true; - - return false; -} - -SEC("tc_mihomo_auto_redir_ingress") -int tc_redir_ingress_func(struct __sk_buff *skb) { - void *data = (void *)(long)skb->data; - void *data_end = (void *)(long)skb->data_end; - struct ethhdr *eth = data; - - if ((void *)(eth + 1) > data_end) - return TC_ACT_OK; - - if (eth->h_proto != bpf_htons(ETH_P_IP)) - return TC_ACT_OK; - - struct iphdr *iph = (struct iphdr *)(eth + 1); - if ((void *)(iph + 1) > data_end) - return TC_ACT_OK; - - __u32 key = 0, *route_index, *redir_ip, *redir_port; - - route_index = bpf_map_lookup_elem(&redir_params_map, &key); - if (!route_index) - return TC_ACT_OK; - - if (iph->protocol == IPPROTO_ICMP && *route_index != 0) - return bpf_redirect(*route_index, 0); - - if (iph->protocol != IPPROTO_TCP) - return TC_ACT_OK; - - struct tcphdr *tcph = (struct tcphdr *)(iph + 1); - if ((void *)(tcph + 1) > data_end) - return TC_ACT_SHOT; - - key = 1; - redir_ip = bpf_map_lookup_elem(&redir_params_map, &key); - if (!redir_ip) - return TC_ACT_OK; - - key = 2; - redir_port = bpf_map_lookup_elem(&redir_params_map, &key); - if (!redir_port) - return TC_ACT_OK; - - __be32 new_ip = bpf_htonl(*redir_ip); - __be16 new_port = bpf_htonl(*redir_port) >> 16; - __be32 old_ip = iph->daddr; - __be16 old_port = tcph->dest; - - if (old_ip == new_ip || is_lan_ip(old_ip) || bpf_ntohs(old_port) == 53) { - return TC_ACT_OK; - } - - struct redir_info p_key = { - .sip = iph->saddr, - .sport = tcph->source, - .dip = new_ip, - .dport = new_port, - }; - - if (tcph->syn && !tcph->ack) { - struct origin_info origin = { - .ip = old_ip, - .port = old_port, - }; - - bpf_map_update_elem(&pair_original_dst_map, &p_key, &origin, BPF_NOEXIST); - - if (rewrite_ip(skb, new_ip, true) < 0) { - return TC_ACT_SHOT; - } - - if (rewrite_port(skb, new_port, true) < 0) { - return TC_ACT_SHOT; - } - } else { - struct origin_info *origin = bpf_map_lookup_elem(&pair_original_dst_map, &p_key); - if (!origin) { - return TC_ACT_OK; - } - - if (rewrite_ip(skb, new_ip, true) < 0) { - return TC_ACT_SHOT; - } - - if (rewrite_port(skb, new_port, true) < 0) { - return TC_ACT_SHOT; - } - } - - return TC_ACT_OK; -} - -SEC("tc_mihomo_auto_redir_egress") -int tc_redir_egress_func(struct __sk_buff *skb) { - void *data = (void *)(long)skb->data; - void *data_end = (void *)(long)skb->data_end; - struct ethhdr *eth = data; - - if ((void *)(eth + 1) > data_end) - return TC_ACT_OK; - - if (eth->h_proto != bpf_htons(ETH_P_IP)) - return TC_ACT_OK; - - __u32 key = 0, *redir_ip, *redir_port; // *mihomo_mark - -// mihomo_mark = bpf_map_lookup_elem(&redir_params_map, &key); -// if (mihomo_mark && *mihomo_mark != 0 && *mihomo_mark == skb->mark) -// return TC_ACT_OK; - - struct iphdr *iph = (struct iphdr *)(eth + 1); - if ((void *)(iph + 1) > data_end) - return TC_ACT_OK; - - if (iph->protocol != IPPROTO_TCP) - return TC_ACT_OK; - - struct tcphdr *tcph = (struct tcphdr *)(iph + 1); - if ((void *)(tcph + 1) > data_end) - return TC_ACT_SHOT; - - key = 1; - redir_ip = bpf_map_lookup_elem(&redir_params_map, &key); - if (!redir_ip) - return TC_ACT_OK; - - key = 2; - redir_port = bpf_map_lookup_elem(&redir_params_map, &key); - if (!redir_port) - return TC_ACT_OK; - - __be32 new_ip = bpf_htonl(*redir_ip); - __be16 new_port = bpf_htonl(*redir_port) >> 16; - __be32 old_ip = iph->saddr; - __be16 old_port = tcph->source; - - if (old_ip != new_ip || old_port != new_port) { - return TC_ACT_OK; - } - - struct redir_info p_key = { - .sip = iph->daddr, - .sport = tcph->dest, - .dip = iph->saddr, - .dport = tcph->source, - }; - - struct origin_info *origin = bpf_map_lookup_elem(&pair_original_dst_map, &p_key); - if (!origin) { - return TC_ACT_OK; - } - - if (tcph->fin && tcph->ack) { - bpf_map_delete_elem(&pair_original_dst_map, &p_key); - } - - if (rewrite_ip(skb, origin->ip, false) < 0) { - return TC_ACT_SHOT; - } - - if (rewrite_port(skb, origin->port, false) < 0) { - return TC_ACT_SHOT; - } - - return TC_ACT_OK; -} - -char _license[] SEC("license") = "GPL"; diff --git a/component/ebpf/bpf/tc.c b/component/ebpf/bpf/tc.c deleted file mode 100644 index 3513bf04..00000000 --- a/component/ebpf/bpf/tc.c +++ /dev/null @@ -1,103 +0,0 @@ -#include -#include -#include -#include -#include -//#include -//#include -#include - -#include "bpf_endian.h" -#include "bpf_helpers.h" - -struct { - __uint(type, BPF_MAP_TYPE_ARRAY); - __type(key, __u32); - __type(value, __u32); - __uint(max_entries, 2); - __uint(pinning, LIBBPF_PIN_BY_NAME); -} tc_params_map SEC(".maps"); - -static __always_inline bool is_lan_ip(__be32 addr) { - if (addr == 0xffffffff) - return true; - - __u8 fist = (__u8)(addr & 0xff); - - if (fist == 127 || fist == 10) - return true; - - __u8 second = (__u8)((addr >> 8) & 0xff); - - if (fist == 172 && second >= 16 && second <= 31) - return true; - - if (fist == 192 && second == 168) - return true; - - return false; -} - -SEC("tc_mihomo_redirect_to_tun") -int tc_tun_func(struct __sk_buff *skb) { - void *data = (void *)(long)skb->data; - void *data_end = (void *)(long)skb->data_end; - struct ethhdr *eth = data; - - if ((void *)(eth + 1) > data_end) - return TC_ACT_OK; - - if (eth->h_proto == bpf_htons(ETH_P_ARP)) - return TC_ACT_OK; - - __u32 key = 0, *mihomo_mark, *tun_ifindex; - - mihomo_mark = bpf_map_lookup_elem(&tc_params_map, &key); - if (!mihomo_mark) - return TC_ACT_OK; - - if (skb->mark == *mihomo_mark) - return TC_ACT_OK; - - if (eth->h_proto == bpf_htons(ETH_P_IP)) { - struct iphdr *iph = (struct iphdr *)(eth + 1); - if ((void *)(iph + 1) > data_end) - return TC_ACT_OK; - - if (iph->protocol == IPPROTO_ICMP) - return TC_ACT_OK; - - __be32 daddr = iph->daddr; - - if (is_lan_ip(daddr)) - return TC_ACT_OK; - -// if (iph->protocol == IPPROTO_TCP) { -// struct tcphdr *tcph = (struct tcphdr *)(iph + 1); -// if ((void *)(tcph + 1) > data_end) -// return TC_ACT_OK; -// -// __u16 source = bpf_ntohs(tcph->source); -// if (source == 22 || source == 80 || source == 443 || source == 8080 || source == 8443 || source == 9090 || (source >= 7890 && source <= 7895)) -// return TC_ACT_OK; -// } else if (iph->protocol == IPPROTO_UDP) { -// struct udphdr *udph = (struct udphdr *)(iph + 1); -// if ((void *)(udph + 1) > data_end) -// return TC_ACT_OK; -// -// __u16 source = bpf_ntohs(udph->source); -// if (source == 53 || (source >= 135 && source <= 139)) -// return TC_ACT_OK; -// } - } - - key = 1; - tun_ifindex = bpf_map_lookup_elem(&tc_params_map, &key); - if (!tun_ifindex) - return TC_ACT_OK; - - //return bpf_redirect(*tun_ifindex, BPF_F_INGRESS); // __bpf_rx_skb - return bpf_redirect(*tun_ifindex, 0); // __bpf_tx_skb / __dev_xmit_skb -} - -char _license[] SEC("license") = "GPL"; diff --git a/component/ebpf/byteorder/byteorder.go b/component/ebpf/byteorder/byteorder.go deleted file mode 100644 index 63e0c611..00000000 --- a/component/ebpf/byteorder/byteorder.go +++ /dev/null @@ -1,13 +0,0 @@ -package byteorder - -import ( - "net" -) - -// NetIPv4ToHost32 converts an net.IP to a uint32 in host byte order. ip -// must be a IPv4 address, otherwise the function will panic. -func NetIPv4ToHost32(ip net.IP) uint32 { - ipv4 := ip.To4() - _ = ipv4[3] // Assert length of ipv4. - return Native.Uint32(ipv4) -} diff --git a/component/ebpf/byteorder/byteorder_bigendian.go b/component/ebpf/byteorder/byteorder_bigendian.go deleted file mode 100644 index 4c5d7105..00000000 --- a/component/ebpf/byteorder/byteorder_bigendian.go +++ /dev/null @@ -1,12 +0,0 @@ -//go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64 - -package byteorder - -import "encoding/binary" - -var Native binary.ByteOrder = binary.BigEndian - -func HostToNetwork16(u uint16) uint16 { return u } -func HostToNetwork32(u uint32) uint32 { return u } -func NetworkToHost16(u uint16) uint16 { return u } -func NetworkToHost32(u uint32) uint32 { return u } diff --git a/component/ebpf/byteorder/byteorder_littleendian.go b/component/ebpf/byteorder/byteorder_littleendian.go deleted file mode 100644 index d40f3517..00000000 --- a/component/ebpf/byteorder/byteorder_littleendian.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build 386 || amd64 || amd64p32 || arm || arm64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64 || loong64 - -package byteorder - -import ( - "encoding/binary" - "math/bits" -) - -var Native binary.ByteOrder = binary.LittleEndian - -func HostToNetwork16(u uint16) uint16 { return bits.ReverseBytes16(u) } -func HostToNetwork32(u uint32) uint32 { return bits.ReverseBytes32(u) } -func NetworkToHost16(u uint16) uint16 { return bits.ReverseBytes16(u) } -func NetworkToHost32(u uint32) uint32 { return bits.ReverseBytes32(u) } diff --git a/component/ebpf/ebpf.go b/component/ebpf/ebpf.go deleted file mode 100644 index b0f5a65f..00000000 --- a/component/ebpf/ebpf.go +++ /dev/null @@ -1,33 +0,0 @@ -package ebpf - -import ( - "net/netip" - - C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/transport/socks5" -) - -type TcEBpfProgram struct { - pros []C.EBpf - rawNICs []string -} - -func (t *TcEBpfProgram) RawNICs() []string { - return t.rawNICs -} - -func (t *TcEBpfProgram) Close() { - for _, p := range t.pros { - p.Close() - } -} - -func (t *TcEBpfProgram) Lookup(srcAddrPort netip.AddrPort) (addr socks5.Addr, err error) { - for _, p := range t.pros { - addr, err = p.Lookup(srcAddrPort) - if err == nil { - return - } - } - return -} diff --git a/component/ebpf/ebpf_linux.go b/component/ebpf/ebpf_linux.go deleted file mode 100644 index 304f32fe..00000000 --- a/component/ebpf/ebpf_linux.go +++ /dev/null @@ -1,137 +0,0 @@ -//go:build !android - -package ebpf - -import ( - "fmt" - "net/netip" - - "github.com/metacubex/mihomo/common/cmd" - "github.com/metacubex/mihomo/component/dialer" - "github.com/metacubex/mihomo/component/ebpf/redir" - "github.com/metacubex/mihomo/component/ebpf/tc" - C "github.com/metacubex/mihomo/constant" - "github.com/sagernet/netlink" -) - -func GetAutoDetectInterface() (string, error) { - routes, err := netlink.RouteList(nil, netlink.FAMILY_V4) - if err != nil { - return "", err - } - - for _, route := range routes { - if route.Dst == nil { - lk, err := netlink.LinkByIndex(route.LinkIndex) - if err != nil { - return "", err - } - - if lk.Type() == "tuntap" { - continue - } - - return lk.Attrs().Name, nil - } - } - - return "", fmt.Errorf("interface not found") -} - -// NewTcEBpfProgram new redirect to tun ebpf program -func NewTcEBpfProgram(ifaceNames []string, tunName string) (*TcEBpfProgram, error) { - tunIface, err := netlink.LinkByName(tunName) - if err != nil { - return nil, fmt.Errorf("lookup network iface %q: %w", tunName, err) - } - - tunIndex := uint32(tunIface.Attrs().Index) - - dialer.DefaultRoutingMark.Store(C.MihomoTrafficMark) - - ifMark := uint32(dialer.DefaultRoutingMark.Load()) - - var pros []C.EBpf - for _, ifaceName := range ifaceNames { - iface, err := netlink.LinkByName(ifaceName) - if err != nil { - return nil, fmt.Errorf("lookup network iface %q: %w", ifaceName, err) - } - if iface.Attrs().OperState != netlink.OperUp { - return nil, fmt.Errorf("network iface %q is down", ifaceName) - } - - attrs := iface.Attrs() - index := attrs.Index - - tcPro := tc.NewEBpfTc(ifaceName, index, ifMark, tunIndex) - if err = tcPro.Start(); err != nil { - return nil, err - } - - pros = append(pros, tcPro) - } - - systemSetting(ifaceNames...) - - return &TcEBpfProgram{pros: pros, rawNICs: ifaceNames}, nil -} - -// NewRedirEBpfProgram new auto redirect ebpf program -func NewRedirEBpfProgram(ifaceNames []string, redirPort uint16, defaultRouteInterfaceName string) (*TcEBpfProgram, error) { - defaultRouteInterface, err := netlink.LinkByName(defaultRouteInterfaceName) - if err != nil { - return nil, fmt.Errorf("lookup network iface %q: %w", defaultRouteInterfaceName, err) - } - - defaultRouteIndex := uint32(defaultRouteInterface.Attrs().Index) - - var pros []C.EBpf - for _, ifaceName := range ifaceNames { - iface, err := netlink.LinkByName(ifaceName) - if err != nil { - return nil, fmt.Errorf("lookup network iface %q: %w", ifaceName, err) - } - - attrs := iface.Attrs() - index := attrs.Index - - addrs, err := netlink.AddrList(iface, netlink.FAMILY_V4) - if err != nil { - return nil, fmt.Errorf("lookup network iface %q address: %w", ifaceName, err) - } - - if len(addrs) == 0 { - return nil, fmt.Errorf("network iface %q does not contain any ipv4 addresses", ifaceName) - } - - address, _ := netip.AddrFromSlice(addrs[0].IP) - redirAddrPort := netip.AddrPortFrom(address, redirPort) - - redirPro := redir.NewEBpfRedirect(ifaceName, index, 0, defaultRouteIndex, redirAddrPort) - if err = redirPro.Start(); err != nil { - return nil, err - } - - pros = append(pros, redirPro) - } - - systemSetting(ifaceNames...) - - return &TcEBpfProgram{pros: pros, rawNICs: ifaceNames}, nil -} - -func systemSetting(ifaceNames ...string) { - _, _ = cmd.ExecCmd("sysctl -w net.ipv4.ip_forward=1") - _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.forwarding=1") - _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.accept_local=1") - _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.accept_redirects=1") - _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.rp_filter=0") - - for _, ifaceName := range ifaceNames { - _, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.forwarding=1", ifaceName)) - _, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.accept_local=1", ifaceName)) - _, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.accept_redirects=1", ifaceName)) - _, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.rp_filter=0", ifaceName)) - } -} diff --git a/component/ebpf/ebpf_others.go b/component/ebpf/ebpf_others.go deleted file mode 100644 index 44cf1c3a..00000000 --- a/component/ebpf/ebpf_others.go +++ /dev/null @@ -1,21 +0,0 @@ -//go:build !linux || android - -package ebpf - -import ( - "fmt" -) - -// NewTcEBpfProgram new ebpf tc program -func NewTcEBpfProgram(_ []string, _ string) (*TcEBpfProgram, error) { - return nil, fmt.Errorf("system not supported") -} - -// NewRedirEBpfProgram new ebpf redirect program -func NewRedirEBpfProgram(_ []string, _ uint16, _ string) (*TcEBpfProgram, error) { - return nil, fmt.Errorf("system not supported") -} - -func GetAutoDetectInterface() (string, error) { - return "", fmt.Errorf("system not supported") -} diff --git a/component/ebpf/redir/auto_redirect.go b/component/ebpf/redir/auto_redirect.go deleted file mode 100644 index 57c99616..00000000 --- a/component/ebpf/redir/auto_redirect.go +++ /dev/null @@ -1,216 +0,0 @@ -//go:build linux - -package redir - -import ( - "encoding/binary" - "fmt" - "io" - "net" - "net/netip" - "os" - "path/filepath" - - "github.com/cilium/ebpf" - "github.com/cilium/ebpf/rlimit" - "github.com/sagernet/netlink" - "golang.org/x/sys/unix" - - "github.com/metacubex/mihomo/component/ebpf/byteorder" - C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/transport/socks5" -) - -//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf ../bpf/redir.c - -const ( - mapKey1 uint32 = 0 - mapKey2 uint32 = 1 - mapKey3 uint32 = 2 -) - -type EBpfRedirect struct { - objs io.Closer - originMap *ebpf.Map - qdisc netlink.Qdisc - filter netlink.Filter - filterEgress netlink.Filter - - ifName string - ifIndex int - ifMark uint32 - rtIndex uint32 - redirIp uint32 - redirPort uint16 - - bpfPath string -} - -func NewEBpfRedirect(ifName string, ifIndex int, ifMark uint32, routeIndex uint32, redirAddrPort netip.AddrPort) *EBpfRedirect { - return &EBpfRedirect{ - ifName: ifName, - ifIndex: ifIndex, - ifMark: ifMark, - rtIndex: routeIndex, - redirIp: binary.BigEndian.Uint32(redirAddrPort.Addr().AsSlice()), - redirPort: redirAddrPort.Port(), - } -} - -func (e *EBpfRedirect) Start() error { - if err := rlimit.RemoveMemlock(); err != nil { - return fmt.Errorf("remove memory lock: %w", err) - } - - e.bpfPath = filepath.Join(C.BpfFSPath, e.ifName) - if err := os.MkdirAll(e.bpfPath, os.ModePerm); err != nil { - return fmt.Errorf("failed to create bpf fs subpath: %w", err) - } - - var objs bpfObjects - if err := loadBpfObjects(&objs, &ebpf.CollectionOptions{ - Maps: ebpf.MapOptions{ - PinPath: e.bpfPath, - }, - }); err != nil { - e.Close() - return fmt.Errorf("loading objects: %w", err) - } - - e.objs = &objs - e.originMap = objs.bpfMaps.PairOriginalDstMap - - if err := objs.bpfMaps.RedirParamsMap.Update(mapKey1, e.rtIndex, ebpf.UpdateAny); err != nil { - e.Close() - return fmt.Errorf("storing objects: %w", err) - } - - if err := objs.bpfMaps.RedirParamsMap.Update(mapKey2, e.redirIp, ebpf.UpdateAny); err != nil { - e.Close() - return fmt.Errorf("storing objects: %w", err) - } - - if err := objs.bpfMaps.RedirParamsMap.Update(mapKey3, uint32(e.redirPort), ebpf.UpdateAny); err != nil { - e.Close() - return fmt.Errorf("storing objects: %w", err) - } - - attrs := netlink.QdiscAttrs{ - LinkIndex: e.ifIndex, - Handle: netlink.MakeHandle(0xffff, 0), - Parent: netlink.HANDLE_CLSACT, - } - - qdisc := &netlink.GenericQdisc{ - QdiscAttrs: attrs, - QdiscType: "clsact", - } - - e.qdisc = qdisc - - if err := netlink.QdiscAdd(qdisc); err != nil { - if os.IsExist(err) { - _ = netlink.QdiscDel(qdisc) - err = netlink.QdiscAdd(qdisc) - } - - if err != nil { - e.Close() - return fmt.Errorf("cannot add clsact qdisc: %w", err) - } - } - - filterAttrs := netlink.FilterAttrs{ - LinkIndex: e.ifIndex, - Parent: netlink.HANDLE_MIN_INGRESS, - Handle: netlink.MakeHandle(0, 1), - Protocol: unix.ETH_P_IP, - Priority: 0, - } - - filter := &netlink.BpfFilter{ - FilterAttrs: filterAttrs, - Fd: objs.bpfPrograms.TcRedirIngressFunc.FD(), - Name: "mihomo-redir-ingress-" + e.ifName, - DirectAction: true, - } - - if err := netlink.FilterAdd(filter); err != nil { - e.Close() - return fmt.Errorf("cannot attach ebpf object to filter ingress: %w", err) - } - - e.filter = filter - - filterAttrsEgress := netlink.FilterAttrs{ - LinkIndex: e.ifIndex, - Parent: netlink.HANDLE_MIN_EGRESS, - Handle: netlink.MakeHandle(0, 1), - Protocol: unix.ETH_P_IP, - Priority: 0, - } - - filterEgress := &netlink.BpfFilter{ - FilterAttrs: filterAttrsEgress, - Fd: objs.bpfPrograms.TcRedirEgressFunc.FD(), - Name: "mihomo-redir-egress-" + e.ifName, - DirectAction: true, - } - - if err := netlink.FilterAdd(filterEgress); err != nil { - e.Close() - return fmt.Errorf("cannot attach ebpf object to filter egress: %w", err) - } - - e.filterEgress = filterEgress - - return nil -} - -func (e *EBpfRedirect) Close() { - if e.filter != nil { - _ = netlink.FilterDel(e.filter) - } - if e.filterEgress != nil { - _ = netlink.FilterDel(e.filterEgress) - } - if e.qdisc != nil { - _ = netlink.QdiscDel(e.qdisc) - } - if e.objs != nil { - _ = e.objs.Close() - } - _ = os.Remove(filepath.Join(e.bpfPath, "redir_params_map")) - _ = os.Remove(filepath.Join(e.bpfPath, "pair_original_dst_map")) -} - -func (e *EBpfRedirect) Lookup(srcAddrPort netip.AddrPort) (socks5.Addr, error) { - rAddr := srcAddrPort.Addr().Unmap() - if rAddr.Is6() { - return nil, fmt.Errorf("remote address is ipv6") - } - - srcIp := binary.BigEndian.Uint32(rAddr.AsSlice()) - scrPort := srcAddrPort.Port() - - key := bpfRedirInfo{ - Sip: byteorder.HostToNetwork32(srcIp), - Sport: byteorder.HostToNetwork16(scrPort), - Dip: byteorder.HostToNetwork32(e.redirIp), - Dport: byteorder.HostToNetwork16(e.redirPort), - } - - origin := bpfOriginInfo{} - - err := e.originMap.Lookup(key, &origin) - if err != nil { - return nil, err - } - - addr := make([]byte, net.IPv4len+3) - addr[0] = socks5.AtypIPv4 - - binary.BigEndian.PutUint32(addr[1:1+net.IPv4len], byteorder.NetworkToHost32(origin.Ip)) // big end - binary.BigEndian.PutUint16(addr[1+net.IPv4len:3+net.IPv4len], byteorder.NetworkToHost16(origin.Port)) // big end - return addr, nil -} diff --git a/component/ebpf/redir/bpf_bpfeb.go b/component/ebpf/redir/bpf_bpfeb.go deleted file mode 100644 index 57a526e8..00000000 --- a/component/ebpf/redir/bpf_bpfeb.go +++ /dev/null @@ -1,139 +0,0 @@ -// Code generated by bpf2go; DO NOT EDIT. -//go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64 -// +build arm64be armbe mips mips64 mips64p32 ppc64 s390 s390x sparc sparc64 - -package redir - -import ( - "bytes" - _ "embed" - "fmt" - "io" - - "github.com/cilium/ebpf" -) - -type bpfOriginInfo struct { - Ip uint32 - Port uint16 - Pad uint16 -} - -type bpfRedirInfo struct { - Sip uint32 - Dip uint32 - Sport uint16 - Dport uint16 -} - -// loadBpf returns the embedded CollectionSpec for bpf. -func loadBpf() (*ebpf.CollectionSpec, error) { - reader := bytes.NewReader(_BpfBytes) - spec, err := ebpf.LoadCollectionSpecFromReader(reader) - if err != nil { - return nil, fmt.Errorf("can't load bpf: %w", err) - } - - return spec, err -} - -// loadBpfObjects loads bpf and converts it into a struct. -// -// The following types are suitable as obj argument: -// -// *bpfObjects -// *bpfPrograms -// *bpfMaps -// -// See ebpf.CollectionSpec.LoadAndAssign documentation for details. -func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error { - spec, err := loadBpf() - if err != nil { - return err - } - - return spec.LoadAndAssign(obj, opts) -} - -// bpfSpecs contains maps and programs before they are loaded into the kernel. -// -// It can be passed ebpf.CollectionSpec.Assign. -type bpfSpecs struct { - bpfProgramSpecs - bpfMapSpecs -} - -// bpfSpecs contains programs before they are loaded into the kernel. -// -// It can be passed ebpf.CollectionSpec.Assign. -type bpfProgramSpecs struct { - TcRedirEgressFunc *ebpf.ProgramSpec `ebpf:"tc_redir_egress_func"` - TcRedirIngressFunc *ebpf.ProgramSpec `ebpf:"tc_redir_ingress_func"` -} - -// bpfMapSpecs contains maps before they are loaded into the kernel. -// -// It can be passed ebpf.CollectionSpec.Assign. -type bpfMapSpecs struct { - PairOriginalDstMap *ebpf.MapSpec `ebpf:"pair_original_dst_map"` - RedirParamsMap *ebpf.MapSpec `ebpf:"redir_params_map"` -} - -// bpfObjects contains all objects after they have been loaded into the kernel. -// -// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. -type bpfObjects struct { - bpfPrograms - bpfMaps -} - -func (o *bpfObjects) Close() error { - return _BpfClose( - &o.bpfPrograms, - &o.bpfMaps, - ) -} - -// bpfMaps contains all maps after they have been loaded into the kernel. -// -// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. -type bpfMaps struct { - PairOriginalDstMap *ebpf.Map `ebpf:"pair_original_dst_map"` - RedirParamsMap *ebpf.Map `ebpf:"redir_params_map"` -} - -func (m *bpfMaps) Close() error { - return _BpfClose( - m.PairOriginalDstMap, - m.RedirParamsMap, - ) -} - -// bpfPrograms contains all programs after they have been loaded into the kernel. -// -// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. -type bpfPrograms struct { - TcRedirEgressFunc *ebpf.Program `ebpf:"tc_redir_egress_func"` - TcRedirIngressFunc *ebpf.Program `ebpf:"tc_redir_ingress_func"` -} - -func (p *bpfPrograms) Close() error { - return _BpfClose( - p.TcRedirEgressFunc, - p.TcRedirIngressFunc, - ) -} - -func _BpfClose(closers ...io.Closer) error { - for _, closer := range closers { - if err := closer.Close(); err != nil { - return err - } - } - return nil -} - -// Do not access this directly. -// -//go:embed bpf_bpfeb.o -var _BpfBytes []byte diff --git a/component/ebpf/redir/bpf_bpfeb.o b/component/ebpf/redir/bpf_bpfeb.o deleted file mode 100644 index ed511691940a8ecc39bcf82283fd234bad18af19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15608 zcmb_i4QyP;ao+nQDalTmM2cz0KTozQlMYFflq}PUrJjFeOMzouY^i1nr^nOrNFI3P zop?t}veo8VG>O{O01G>)3M46Q7YG9w0gDEeP!u2=6o?800Z|yK>IMN(1dY%*C|lU5 z;HbEM-|pF;VTxU<=JUs z=jSbLawklNRPGPVV=R}u`81|Iu*FCE?ld=(Ix%dnw`I4vj^#war|CFyhb_&qz}x_6 zmM0x{w3hcs}G3ls_Nv8QQ&JdEVjWEw22!D~#yHr61e-CK|tD zmG~cfzy2>*oZ--)O5?vH_Tul?UTDWOF4In~%iLDMhcG=4_h6&Qo~Hb^st z^&e}j5ijrL)0n3MKGz$*en-&wi7}n8w$__yJ;!xxME=LXUuuP`zL<+^}HqG*FkING@o`HH;kXmueh?m zOX3%Q5Ao}u{i4M0^i8rK1>_nXKW{IL-wwBno*@2l`sL>V#)0eE zU>xl@O!@WOx?Zn(dDY&pGjD7>^}IR#S=YYI?~UfoN9ZT~-NRDF;i|G1`lZ(k=T)5s zBxU8K_x(eB?D>p#c2h*Pv-V-NbIdgCJ}m9BUv`Eq=bU5b#lPGq zy5(LmEcc25YoOzZoD0~uC+xlAoHN`$Z_C?#JAHp<^5npdy$^Uk?jxgMw+W6f_fRUl z*KF_GX`Uwjz~?!QAnmA_Cs>NT_kEr(V|=Z?D|oN5`%Ugn$MEN$@81`P%)_iH-Y4}R zb>&HCr!$oMxcM#C`1?WrWjQ}zlJm36ox?Z}-TC>FJ3rs+z82nVT=R^>Aj#s5ZaklI z{+vD5iK$}D+4pESk^8DMm|xuZJYD{TtC#!SO>zz-!*c-hr0zuQGi!Gtdyd{rMtBak z+8J85QQzPkZR*?m>?ZnMO5gjlwQF;K?0r_P*Q@92)@`#{KlT3L`xow$PCfp0SDNBU zlO#E{$^AsNbGyXT?`Jn*5i$iokaD_iY~=iTs`T|anI%R(Gmv*C;Q#iGNlK^?! zUl=5Npc(sK5|iybi85fFeOV#l#5i^oPkA}e0kXHjTjGuOf`cs%^lh8S*;%}SZOamW zw$bheGS(y<;X3X{?nRbn?3yP(jeHL>={oXfkm=S6@}tO@>sIB6Jrs={bIXvne*-cb zwQc=B9jh~e=c+yQlwSZ%-$<{3X8%a9f!^h4x)AFVIsm<2=rrhqLXU!e95mivV|C=uJAOO>{o6vP zLFa@X1wAfw9W=)r?Zy{Cb9s|q0sZ?zuYvw!N7MD#3wYdykqAJ4OXvmAi=d&KB>#It zr$H|ZJ&KYkfJ`0ye=>&ep2-!^tDvEqLJ|8{p#zjmh(#(5{=b8!OdVx(9)M@(GL<*t zW7w=^1`~5G_6w9d$zzJUVhH@VWd7a(`d1W2A}dV-#zjBQDe{}-F^T>j(zrlU2CO*O z7|zM~4lFS8b|Y_4-|;P}@5Y{XzqHA{%(}v8OX61^_5*p_12RWH1q{(`pB2oxP5ec{ zoLj_YYzpG0;ap;({eDYXdRr|bWADMJgHOi33psk*vyg#3n|h_BKY!RR0Q7uF7iVfF zn|u2EO>K!hRobdwKy?T7z-#?J6$Q5slsXVOs+InFw?nH z*}_b1wpgf|N^xeUI5S~p3uDFEY$Z3Fo33W3a}{9}XU5B>TCA8c_T-N)9c#%3LWsR;}6gvf1iX_W0cRxG5E8 zOl7K;wO&u>W~a=Fxx!q5Qt)=dRA$Sya=u(L&y;dA*~)C8iq1Gt%NIQtGLS5yo!V&w zHHcNS#qr|ISmBiMWi1c)$jBczley}o0V7|^Rjb7@GnT96>;thHGc&nLF$?Z^Zn{`H zjp$65YlUpFve%T#d31wR$lMn)_j#u6y%Tj&g!Sxn0Zr7ZwHz)NCyTR%EW18YEoZBR ziK-)t&lb#hseCev;j1#v`}Q{DTq@5@1bmq46#kCWeK(N4!{c)^dEcqYY;LYrZWS>7 zclY<-eY`S$x7F*{FYF2h+NuK&8ZZ}gYM97p*9(ODj4hu4g}tCkD(b#p|bD5BxVTa zLa=jxfNo|dYvq}0_rarwvYG7ROixodG+C@{V$v?G0ONp;4fF;fuQ zLXyMmJdOxLm0n;fXG`Vs)LbQ7C>5r=ckAWX8|+3yJf-I5^ z4-Tvp>n2KPr#E;o^GNpbBL_zgKY28=f-v8M`>_C|H-v66miA`z)wyYGjFnO@U+Bg* z)f<$@$MLXJp;tz)HyAJFCaS@ipo(3&Jl^dFwMW{B7~Hdg!NZyCLr)(2T=vK#kG#|H zyBWNuhnz0XM@L1B*_kB%Z4&&u!sz>$w`@$;jCsT|M}Ma?#R!KdNWmSvo1R0izV+0J z=v@2IVWX{EAhtNa1s;7UbIo;aXZ}0ZDhxE&D#xE8ck{7R#EP^oiROMW zxGz+d`NktC+%KHT0|&4|oeqa7xm3R8!mG{nZ|Iwy|)G2vQ$sv+Q#dZEJk~i>^s2Ibv#$97J8y9nGVesoCbr_szZ*} z%1`EIO}13b7iOx3=sh(%H#1i)jN#J8+c0mgp3{H4S~dMRx2wkMv5U>C8Bw*I=exfD zrV_}teHTjPtuvB+0j@wbf_aREL zoumF0!Oxr6tAp;NJLq940Qhy2h$#heH3Hr&5!`|QIh{E-z{A<)dtLmkHp}zDd z#7+O>+o|95Kfayqd;foG;=Sm93X0Z$V)&ox4%?3pKtDAQ!O%~M{~dh@6~3LMPPzJy zubRXgl%IFl`;%Hwcr~Hsv`!n5Q;=Vf~uI>#evw2=(pww0r-0j2$2TH%2%f zJ(yR+5sdL^A8qP$eA=7-#|Pkld)?I=N$qD9UTNwdrT%4Aul((Z_(yqr);!>h)6b3{ znIv3po+Dqw5{RB7-#5v<#DQxk{+3BT3_tn(nED}>e&Sq7X1K49xO$5zKPvc;NuJ`l zBIA@GW_{h&gG~Mr_2(m)^$QMTUr)ZsZ{14~%=#6<^Cr1Kyy`G0mo7>DH%;=dI@)Dn|ubEWuPGIT3krZEW@_wI>i~oL~_ve(~=lwZlG1gIB{Pp|1KZh)4 z`|Dy~_L2CN2!?(_{G~ki5r0lu41LT?+duDbVnpG(*n>Xzk;GY5f6igtOHvt3wuJO| zTboJUe+Tf2lK1|~J`$c&vd<^s2;LvlADFQzDe?CB+9ghP z{OJ_KCw9;0`td22itq!^#T?}C+hQk`j_{vu^4l&%_>VRDso@BJZmS+ba=1zqN}$?Q0SK_9nk0-Q-(g_D!YO7#PA&AkIF;N=-i3gHN&hoBYJQ=ORA4 zBK(C2AMX#|KF2Lg|A381jY|jHso_VG_E5?Z-6(!H!Y5{8`7f}e`juP({{_f*po}I? z#P5QSIoFaX_l*eO>-my#;rjqLUKS&GMDb!P=6bZhQ;K|w@*Eez=2ELX_vaRSYrqlt zxVFbQ@f=})kW!q3UC16Y=Je0wI%wMS_#9}SV}irqoIU0kbA5Qs_UOCEqY76Pt}8sR z@Pfii3a==e^>#f*^IpVKf-%%VW`2mH~3TG4^Rk)&XUEz6!7ZhGnctzn=h1V2b zZ^d!I=y>t`Yr%oS0}7`V&L}*pa7E#|!t)9*D7>Wbio&Z3uPMCVitV-%8Sg}=st*(% zP&ln{M&VI~D+<>Yo>zE5;U$Gv6kb($P2u%cY?o=I|H)2OA1FMaa9ZJv!lMdT6s{{g zukeDxOA4r^;UctGK_!Wo4}6|N{;S9o4wo|lpH#on8u_@b)kc^R$0tnigq+}@!u z&&G&8-+No|u)>EF9#MEw;Zq8qRrs93iwa**_@cs>6uzwRl~&x*p>UVN-3kvYd`RID zg(nq0rSMsW&ndj9@CAi0Dtt-d%L-p<#alWQ?ozm0;bDajDLkU^q{62ZKCAFKg%=gR zpzuY7FDZOk;VZ3pYlp&J3U@0!tneX)M--k^_>{tD6+Wl%qQ^JicFV1S>8}+|)lC0v zq16B2(MS3{kbi60_&-1(yivXUG8g&bW5eG9<+qZ^e-8DRO3zGZ`MWay1*!Qn%R7Hl ziTqaOe`X1PH`M>Y;tk?4aO1 z`-E>x@CEO=NBC0i*(3IbSPo`~q#X2k5aaM+r8i(mz0Y~m_s%&gcE)q0HCp+bNjmAif`k{q*{*A4cA!{SKag zNcO$dk?!gBiS{CI*8V63kkZI(-@osAeYQV>?Aur4a6SEI@})zq@5e8KxtacgC`98; zmX{~}B(nEMx4%dMB>EXmULHJaDrzqxcfII;^z#uW$*4b9fj5ia0)>&-f5s30FKv@= zpMK%HT1$VNgniyh+elk4xH5c3+vhin&Dvjg8e8iq**@dm`&_O9gwn zs`gX$hqZrUjZ<8;XU%_A^|7B1tFNlZT(NpmFMrB*kUHu5c?bPP$a=ZEj{l35Ye#38 zW}ByEwSMbA*|LR=EH&m?$}QLG+hRYs_SOc>S6B}@-@YyOy=z>V8vp&Zm%w%M=%H5s zch?r@jLDwa4k^gpW0P8asxfQO zZfS*mdcMi+Q|>FMH|ZDNcY1<$SJ7^I&YgpWmz_TDN3I;(JWU*QJnoZ338!OKr_Ydbc)W03f`C~ct-gy4#wXpAG{-(bvehH2B z{CR!h{I!_@+rHUM+xdU-jY});m!I#)j@x$W7i7N$`-JoN(f7$Gt(~biWgdg|(Bq_k z2{PAp4}vuCntZT%Tq$VCQlAn{FM({rL_0;e394 z`^L?u-Z#_VcKvgIue@(~o6_E0Zoeq|LVe+W*5^g`RmCd9N0Lqv!H=)axcj-;+30r^sj67ewBF`6%@_ccRQ*^X( z%spRx#qkfBGoN#WegxZF|Wo^Px7VW7wQ z$uBv%>+aKPd+s9G=kMd7z4enp`zFZI&)WNRReRj?ZRjVh|8m!sS#6x!Px$dw{U!S4 ze)P|`*^{&e>%7WyO4!df$m#oa6lD?OUr9RA;tsyQSHt%DzM&odzR`Nl4TE^P4&#+lA=Mc;ZoR;)r;JxA_Xv&meLeFCu;p(e89KI*dHRdak(F zfo$v+mby@9Uto6(F2oEWVosXaejITlB0~X@`3SLu$S0aO;Grfd}(84+Uvdx+n zp6J&k--c9NY6It-zElF;K846L zSwZ>*DUUGuzbN@Q@_fH%zeC86NWOx6LGrW6pOpL}^1mwiCFH*%dCvS-k>~k{kpG6{ zXOW*1nFt%}JCcthPrqXOMV=u-tRVl_DBp(sBJ!6+h8zDMB_HuXAZ$mBBmd7*UXgMX z+kHUYR9!2sFJ~PCnm2RL!JU|`wxs$Iqrd$L$`M-b!N<>BzJc;_Lc4>wSl@yAea>+-fO+PbFxWBfM26#Fe8kDEzYlo| z{em#}Hu-sB?t5|p3^S~2=ms-zTs=1D^5sCrJxv-zo7@|b%fA2_=(Bq0yBKKC8P^}( zrkM%yCYvvtWb%=xpMKWF}vp%w|fan9b+2`B5{O8O}~7i>b-fL@7CuDoQ~%KTgyNx?$3{mwn>1+GlBuf`!TKXf~g;D$v?CE2f4`F@>I7Wh$2(E|qP6$z*9f zd2DKA#N;x0QyecRt<@8$$#HXhDl?U#6s#RJ#mPdskS^rRiCik5EKX)h7>t8*I_paz z1IaAkCqC_Qf9Q|GT9e0W+YcQnZ)vyIL~|c)aE%?$d5++Fr{()9i#bfA^m_yrt)b& zsIg>fs$8fSFg^G8^xS`}IC8&j*OTV9idIz9Q7TVPrOQ!up`xAWvNL+oL z=Zw?Ek|^3Ab-3cr4jS1B@$Lg&x>RWUU8?P z8cLzE`@k4>2=+p>b$^6mCdbN!e5vE$k%43)c_`6Y6RxVv7FSVe4_1VAz`%ODqMeh4 zDQ*GoACf&l* z?gRcjMEmzghZ2dW4<9+4JoMP-6VZ+xbw+XudR<%2&}61OHJP{O*xobga!37`deQ%{ z7|Pboc{#n>47s`KlTpire1sv324}NVzAIGDDgLPHsl#>PTv}Af4dXOp(EB4hy~C;D z;Yru4NaQmoy@cOVIZU0mwzg=Z3hN?GPM?mlr6k=}w%FlnJ0lcQO}<LE7lGwM(WW2w>njv;pz>N zaTo8Z_mHcvzjcB(H@Xo;jP^;g`v7m(kyNgf=?qUY z4Vuij7Mz^=HZ0Mt(qs7VWippdXY!>?_?()Y%1@Ot!+2=p(=eZ0eM!%;Qpxn--Yyy6 zjzg?HnvqI{^f;f>`9GD28HDW;%lPNQIrpzL`+jhrdv~!ipT_4O2QMX^V7^~aCSxV= z9*z7UqWR01ZBPCU#GdcQtStF2$QNVw-JARq#Ma9(`>qjkFVv7fiMOBiKZ=<|z^&jx z;Q`d&@m|b~1>6d*2)J-MDEzV_^R+jctP5*rP03UQ~xNqVSAJ5z>5?)3*NM+(ewt~3XTiUpuRQU zXcEFuYr~-M?@|BbjrP5t`o2$jOv;SZ{|EUufsl7e?|B;)Za1(djq}=+=6wbz4NHQ_btq~@IQcC z7aI+RTvuNa_T!ls?!;T~I{dTdLcpzHsIcw#p}zC=26Hjso59P0{JL`u=4!yLV7^$> zzSq#c@2v*Y67bF7xUlVizQGIx+zK8P_Vx}bJ{!odd%MAKpRqq5<`pk0UJl#e9y3=1 zZUyt7G1T`%j=u~3AmE$9y~5UhY@YsrTfu|E-S9a(u3&!){~CDRyN%|Yu(x+k@nvDZ zK1%^}eXa=m^;r&>>%;HvX}@ic^@sxI`g92U_2~_m>oXR}ufuv21LpcvguVS|6?0!! z`^WlRQ1w?7Usc?Jd9Sv|dh`a&_30P(>k|){>oXR}ufuv21Lpdi754VeD83lTuR9Vm z%c}mW;$Ez4)n3T+u6ab1v;1$trnjkjkJx*qzo~kU*n4G9lZhZkd3&!s-emuO9CE+Z z+j}L^RJ}*Yt>7W49{}HRs>z%RxD{Lx<_|H}f2pZ(?(dRqqY!*Z&OT6?yB& zFE*Jm;T^Dl%O#9gcmTZl9gJ7_H2AK!o6K3^S@5RsV*CO3fc-tg_N{0?C-rZG+m=+*HpcSeEpJYe?@pLo>R8LscgX9asG~hTh}(5cHt@TEnAvR6!2|e ze=mKF@=>#WUSa#U!CSkU%_))h^NFjewlCo~<^!f5xghNQh`*=E^dtTr@_uAFY!5$j zRoMFxBliHAexzU6w#Vef1EwEI2zz^n6rT;`t)Ex*v%>bdB++cDM)|F# zHmDA1^vhE2*{9#LdTQf>`?7)GHQs99I2`-#`Q75J_7`)mT)H)1mvYZOJ?F#>hr#b5 z=_?sL)0c%Dmw90^zpES)(>&9cGx+u$sy%&~uV?$X%)f7s<7flN!|zR>3%DIz4w&{< z0;WDbBQdZ&zZ(j_vtb|m5E)p{?}7P_#Xx5L5F&%;qQfdz6wfH0Rm{7P!RuSJRO3sE zmlQ86wnn1N>+|Q^b0qWYxmR&qaYFHs;-cb;;u*!WisuzCD!!z6N%68`Bm2dVk9Q%1 z=SXp{V!z+Kd_vU^DK09mD4tO~t9V}VqT)-6mlQ86_AbTSzd_EU=SXp{;<#eorQ!Sy zDK09mD4tO~t9V}VqT)-6mlQ86whJAs_XZ9;;7D<=;<)03;vvOF#TCUfif0wiD_&H5 zN%4~6WySuAV=<4$hQQyEBgMUn5~ z`u6_0%=4I%Kc)Dr;&Y1U6kkw$QSoKPR}^1W+=6$;a6Q`x%y-nV{h;D8#itaX zReVnIoZ<_LFDkyQ_=@7Iid*o`8rt8kxI=Nj;sM2jipLb6QhZkNImL5|x7>Z#ry|o+ z&YUWnp2vNv_z6<& zYnJQ3QwhGS@?W!5zc&UFseqP$^ z`KD|bo&iv&?*D?c_w(f2=>FNc)=634{tNtpauBpV@1t&|`I}#1{pf;x|K2YCL~*75 z*UFE+FNojrCg}co4X@O{>2@tD(w^(L7E$;AD#{w4yjDO`L9B*~oF942tCnk9-$RYO Z3RPv8x!CE@{~w3#_}gmVKhF)r{{ePC2d)4B diff --git a/component/ebpf/tc/bpf_bpfeb.go b/component/ebpf/tc/bpf_bpfeb.go deleted file mode 100644 index 623986dc..00000000 --- a/component/ebpf/tc/bpf_bpfeb.go +++ /dev/null @@ -1,120 +0,0 @@ -// Code generated by bpf2go; DO NOT EDIT. -//go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64 -// +build arm64be armbe mips mips64 mips64p32 ppc64 s390 s390x sparc sparc64 - -package tc - -import ( - "bytes" - _ "embed" - "fmt" - "io" - - "github.com/cilium/ebpf" -) - -// loadBpf returns the embedded CollectionSpec for bpf. -func loadBpf() (*ebpf.CollectionSpec, error) { - reader := bytes.NewReader(_BpfBytes) - spec, err := ebpf.LoadCollectionSpecFromReader(reader) - if err != nil { - return nil, fmt.Errorf("can't load bpf: %w", err) - } - - return spec, err -} - -// loadBpfObjects loads bpf and converts it into a struct. -// -// The following types are suitable as obj argument: -// -// *bpfObjects -// *bpfPrograms -// *bpfMaps -// -// See ebpf.CollectionSpec.LoadAndAssign documentation for details. -func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error { - spec, err := loadBpf() - if err != nil { - return err - } - - return spec.LoadAndAssign(obj, opts) -} - -// bpfSpecs contains maps and programs before they are loaded into the kernel. -// -// It can be passed ebpf.CollectionSpec.Assign. -type bpfSpecs struct { - bpfProgramSpecs - bpfMapSpecs -} - -// bpfSpecs contains programs before they are loaded into the kernel. -// -// It can be passed ebpf.CollectionSpec.Assign. -type bpfProgramSpecs struct { - TcTunFunc *ebpf.ProgramSpec `ebpf:"tc_tun_func"` -} - -// bpfMapSpecs contains maps before they are loaded into the kernel. -// -// It can be passed ebpf.CollectionSpec.Assign. -type bpfMapSpecs struct { - TcParamsMap *ebpf.MapSpec `ebpf:"tc_params_map"` -} - -// bpfObjects contains all objects after they have been loaded into the kernel. -// -// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. -type bpfObjects struct { - bpfPrograms - bpfMaps -} - -func (o *bpfObjects) Close() error { - return _BpfClose( - &o.bpfPrograms, - &o.bpfMaps, - ) -} - -// bpfMaps contains all maps after they have been loaded into the kernel. -// -// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. -type bpfMaps struct { - TcParamsMap *ebpf.Map `ebpf:"tc_params_map"` -} - -func (m *bpfMaps) Close() error { - return _BpfClose( - m.TcParamsMap, - ) -} - -// bpfPrograms contains all programs after they have been loaded into the kernel. -// -// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. -type bpfPrograms struct { - TcTunFunc *ebpf.Program `ebpf:"tc_tun_func"` -} - -func (p *bpfPrograms) Close() error { - return _BpfClose( - p.TcTunFunc, - ) -} - -func _BpfClose(closers ...io.Closer) error { - for _, closer := range closers { - if err := closer.Close(); err != nil { - return err - } - } - return nil -} - -// Do not access this directly. -// -//go:embed bpf_bpfeb.o -var _BpfBytes []byte diff --git a/component/ebpf/tc/bpf_bpfeb.o b/component/ebpf/tc/bpf_bpfeb.o deleted file mode 100644 index f0e7e608f54b58168a4598bf254cedd82c996a43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5168 zcmb_fZ-`u16+drw(yVQ=U8}L1NbHS;IGb)}casJFZbW!mdz8U zeZL~=_xtZm7{5k{?^g}y93nc-tS9Z>$7q!C(PQ*5)4TQmX+;m9|7`ppMf@KY`v;jG z8mGe`jyFLcVLYL9Tz%%b9DRTg3gVV=J*^n+^f5m8AHTU>wM(7XPb}PiqRln4{k1MNSpT<`fWr0~pcRB=JG;8|Wm{kZTw&~vD)vxWKuD!SJuS9p?8?nTuC87HspXVHfOeI0cfRWeH} z!Yj~3_!8)v@Qa`?2;T!;7k&fu`@(O5zAXGU=uZu=LwG}Y1^P?jOQ63Jei8I{s0iKZ zf&Kv%vE>x#pM)=gz9sx3=--9!f$~0N{SDajTrs}|{yyQi!GFl`db$;OtmzR@=MmvI zz;k|Kn`ik&;Y;B8++g`4C>N~wd=K)^fXAN6Z-IXnJal-foSN_ol*AMo;jI3&t1^>vy6+(?@`FVB5Uvi;9u04*ZNdqy_Eg;0QDKbaT?MAT_jsfiRXk%0TJa> z@*_sf_WKU(7XbdqfDc*;x$ecQS1&%}K6UxCbMC@356+{Z`Tr{FtJk)e2h6lvLcvYkSSuZF&)W#4GMnr&gne-=b(GakY!4 z*IuFP9lztV7EG5ZYKKW!2^+N9@LF!v_G5T9AgKga3LR($VLM3H2xd@=-JlkVvS)#fZ*Eyz2!;9mfN>wlM^ap`gspUn13u(=328}gp`^_-%-5@$cjj-Z1G&c~Q z9SF}_%_wXqBGoK=cbh)CNaDn6M)X|J_FW$OavZv`zZ@GT_<~QhM);hIT*jQ}vuB2h zZiKC6#UF}S@LS^WlTOR6by^iWl)BsYtMFYxLPKt);&ib%y%g1^lS;9I)FdipR)~{! zr;@0_bf^<>JgL4&3S~76s_KM}9Z6w{Qs-5{h$jmi$|TfJUa%_Xh*gGWj7-_n%$Ni< zRT%j7le&6D%}%Nd$~vATcEeg|Jb9sxb$}UH=g%vQ%&jM3D=y3}Jn5FPT+5S2TQ5_E zL?Y&yDRqKpx{hVlUIE~|$+9pJ3|pbjPO<*nMi{PiBG+&D&BAe{ZMcA^)Ny#897ca^ z=wKMEP7e3Tk){F|YTRu&moXZd4V!^Mo`PtPJXuXBoV;N74~KBMT)ui`;fj0tu}_sv z46N$9OaAFo$aS^a#^AM;2E);rCgibOlQJAaEr=6#K6~mD>c$Ns6P@&@i; zr(`fmamtzYuHeUT?X&%h8U1x>FOzeF?|m76{u|`Yt^0kya8c2ygk3?nsINNAHf=7&g^Q3eO;Gcq=-$d!cZwoFx zRJM`Wo%P*#^o zK&M_YRKznT@s>c>nwm<7G=J&Rj5~Wq=qZHue<^i%$?Pxdcl>k0ealLO>V=cyEza({ zQpV0gyI&Yjq29$_?{{mm;IAslpZgOtUmKMGMHsja`T26{hO5tP5Z+czvuXIU+ALn@|?v?**#!l}u?%(R!{Bi$tVSirdy=RnD-*K@Y%$sdX a#~VQ?e?x7vr3bl<%PXjCW8=2{Py7o4L$vMy diff --git a/component/ebpf/tc/bpf_bpfel.go b/component/ebpf/tc/bpf_bpfel.go deleted file mode 100644 index 3bfa1655..00000000 --- a/component/ebpf/tc/bpf_bpfel.go +++ /dev/null @@ -1,120 +0,0 @@ -// -//go:build 386 || amd64 || amd64p32 || arm || arm64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64 || loong64 -// +build 386 amd64 amd64p32 arm arm64 mips64le mips64p32le mipsle ppc64le riscv64 loong64 - -package tc - -import ( - "bytes" - _ "embed" - "fmt" - "io" - - "github.com/cilium/ebpf" -) - -// loadBpf returns the embedded CollectionSpec for bpf. -func loadBpf() (*ebpf.CollectionSpec, error) { - reader := bytes.NewReader(_BpfBytes) - spec, err := ebpf.LoadCollectionSpecFromReader(reader) - if err != nil { - return nil, fmt.Errorf("can't load bpf: %w", err) - } - - return spec, err -} - -// loadBpfObjects loads bpf and converts it into a struct. -// -// The following types are suitable as obj argument: -// -// *bpfObjects -// *bpfPrograms -// *bpfMaps -// -// See ebpf.CollectionSpec.LoadAndAssign documentation for details. -func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error { - spec, err := loadBpf() - if err != nil { - return err - } - - return spec.LoadAndAssign(obj, opts) -} - -// bpfSpecs contains maps and programs before they are loaded into the kernel. -// -// It can be passed ebpf.CollectionSpec.Assign. -type bpfSpecs struct { - bpfProgramSpecs - bpfMapSpecs -} - -// bpfSpecs contains programs before they are loaded into the kernel. -// -// It can be passed ebpf.CollectionSpec.Assign. -type bpfProgramSpecs struct { - TcTunFunc *ebpf.ProgramSpec `ebpf:"tc_tun_func"` -} - -// bpfMapSpecs contains maps before they are loaded into the kernel. -// -// It can be passed ebpf.CollectionSpec.Assign. -type bpfMapSpecs struct { - TcParamsMap *ebpf.MapSpec `ebpf:"tc_params_map"` -} - -// bpfObjects contains all objects after they have been loaded into the kernel. -// -// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. -type bpfObjects struct { - bpfPrograms - bpfMaps -} - -func (o *bpfObjects) Close() error { - return _BpfClose( - &o.bpfPrograms, - &o.bpfMaps, - ) -} - -// bpfMaps contains all maps after they have been loaded into the kernel. -// -// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. -type bpfMaps struct { - TcParamsMap *ebpf.Map `ebpf:"tc_params_map"` -} - -func (m *bpfMaps) Close() error { - return _BpfClose( - m.TcParamsMap, - ) -} - -// bpfPrograms contains all programs after they have been loaded into the kernel. -// -// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. -type bpfPrograms struct { - TcTunFunc *ebpf.Program `ebpf:"tc_tun_func"` -} - -func (p *bpfPrograms) Close() error { - return _BpfClose( - p.TcTunFunc, - ) -} - -func _BpfClose(closers ...io.Closer) error { - for _, closer := range closers { - if err := closer.Close(); err != nil { - return err - } - } - return nil -} - -// Do not access this directly. -// -//go:embed bpf_bpfel.o -var _BpfBytes []byte diff --git a/component/ebpf/tc/bpf_bpfel.o b/component/ebpf/tc/bpf_bpfel.o deleted file mode 100644 index 290ae9cad7b7873a457a1a8757c0bee298fa8840..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5168 zcmbtYZ)jXu6+bi0+QcSlH*0FPQoZX!nRJs$(tj3e%d|^&w-D*jjc!*hFE2B1GSAGP zxAWd)6RqrYK~zv_VFf|du!8hMkqY82vXFdJ{8U6iP$c3vl{FxuAIiqxx%b?em%CZ( zpF`d|zu&p%o_p@O_r3cjuguOplg(tvN{0SHJ!Y*$+lP9%pkaZA;JNWZV}Ij)M*C|; z4gA$Ptv7BO`+fu+2AsToQX5{nIcW5&Ls~CoiTZ)AjA%-u**x_lrj1+r+=Cr%*?8Tw z?}z_`ot>RAC)qEdnN6c~OgsA0?zcEP(b177wSL(6pUjTveA4-U6g?+ze@Ob9iH~L; z*W>v&KdkLz$O~!dT-k8+iBWCeka@kp?c2x?@$aIhyX^2B%^sxpYduV_K)-y1i8v*;vzBhw4;C$1PV;et`}aeC#Nc=N4H!G$cg5z~u^@h5 z4tfHYwapki6`)TG?m(Zz#Xg(3p2eki5)4;>A;Y|0HU?mpcl32!3S+zpy@X53OUr`M zMv348^qSxW=r0TIK;II)4*ea$o6tWHyaoLu!JNcT1uNiR2rdBsQt$%sZ-Mz)>;V5> z>JY+kzK>|0cKt{6O$Ja3A{fyf%Rk2;KrdESM{=fDgh)0Y4>p9r(2PC@lCz z!3E$s!3(?*Ab3Qm1N277r2jG8zG85O{R_BQZ2xZqm^Xm2+i;In4bxDdIubuOHK#)2Fc;-!-TE&?Gdob^1|+9yv?yAg*=ji9X9Em?~KS`OA|#jmvk zs{5;6&`4Tg5K|O38ewCJl9CtsEx#Unbw5Ikc-dQQS1MEs8Wb%jo(@s>Tg&uPJ7@>& zg|15!wVFw@)U44;&2M;7D~J)!&cz&)R*F>7Y9qR0paHZs??eMn;m zj=)cTkwkUO8<&1ee$El3Nee!~JIY;c$>&DE;dv-Hj!ivgu;|57p zJ*8&G)dgh(j}wPsFEmbGsA3;r#nt)q3N!PnNwX2>X6K*tirB8japSGGsX`%<^Ynx| z&MRHTwrVYdaNT5Em<)QYaOa@de_pNGTy95RPz&n0V<=m115c=9h&;G|9DiE)JB!aVw#j!3{S5fm z>~P1lF@FbF{vPRXDdwjUFMpRv!HszpeBdpjX@{Q`{tdJb|D5PriVuKSg};UN1HZ!A zCLBsj2X~JDC$x|Jj_6y$kKxoA*dkgNeh&QbUv$dapZPF2Qtt8r@Hd5D=k|XR{U*f+ zz_)~d4ek6}V%yBWay6Yud{zAL-&M#AAAtb_l3U=o_P)AA+pbW zitO^bso6dqE8Xx_u}4nW&%!>EinAr{eC9uz`s&F^u;;biew~_c0&eQ?4Z_{|vjEf4 zV)<*RISZzy+wZ10N1NbA2)A>e0xlNIi-vajg2N++cN~7p;p+~+=kQI3-*@)8hZh`Pbohe9BZqe!e#_zO4!`H{O^4ri_?E-B9Zq;Bu-N(9cbw(Q zX&)LsNckjKO(@?AYWYj^&*U}HFV@3NY`pEhw>`RYA!rnO+y zyY5`ywD5EF@A}(yw0-(55Y5j$|28fNw`z=k5JHr`o8KcmPupLnr_VSZ->rdNe~zCP z+uz#e0ru+uTx$PVU)JoKmKz|hjoas>|6iQ}-TJMZwv>;M1& diff --git a/component/ebpf/tc/redirect_to_tun.go b/component/ebpf/tc/redirect_to_tun.go deleted file mode 100644 index d7be64af..00000000 --- a/component/ebpf/tc/redirect_to_tun.go +++ /dev/null @@ -1,147 +0,0 @@ -//go:build linux - -package tc - -import ( - "fmt" - "io" - "net/netip" - "os" - "path/filepath" - - "github.com/cilium/ebpf" - "github.com/cilium/ebpf/rlimit" - "github.com/sagernet/netlink" - "golang.org/x/sys/unix" - - C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/transport/socks5" -) - -//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf ../bpf/tc.c - -const ( - mapKey1 uint32 = 0 - mapKey2 uint32 = 1 -) - -type EBpfTC struct { - objs io.Closer - qdisc netlink.Qdisc - filter netlink.Filter - - ifName string - ifIndex int - ifMark uint32 - tunIfIndex uint32 - - bpfPath string -} - -func NewEBpfTc(ifName string, ifIndex int, ifMark uint32, tunIfIndex uint32) *EBpfTC { - return &EBpfTC{ - ifName: ifName, - ifIndex: ifIndex, - ifMark: ifMark, - tunIfIndex: tunIfIndex, - } -} - -func (e *EBpfTC) Start() error { - if err := rlimit.RemoveMemlock(); err != nil { - return fmt.Errorf("remove memory lock: %w", err) - } - - e.bpfPath = filepath.Join(C.BpfFSPath, e.ifName) - if err := os.MkdirAll(e.bpfPath, os.ModePerm); err != nil { - return fmt.Errorf("failed to create bpf fs subpath: %w", err) - } - - var objs bpfObjects - if err := loadBpfObjects(&objs, &ebpf.CollectionOptions{ - Maps: ebpf.MapOptions{ - PinPath: e.bpfPath, - }, - }); err != nil { - e.Close() - return fmt.Errorf("loading objects: %w", err) - } - - e.objs = &objs - - if err := objs.bpfMaps.TcParamsMap.Update(mapKey1, e.ifMark, ebpf.UpdateAny); err != nil { - e.Close() - return fmt.Errorf("storing objects: %w", err) - } - - if err := objs.bpfMaps.TcParamsMap.Update(mapKey2, e.tunIfIndex, ebpf.UpdateAny); err != nil { - e.Close() - return fmt.Errorf("storing objects: %w", err) - } - - attrs := netlink.QdiscAttrs{ - LinkIndex: e.ifIndex, - Handle: netlink.MakeHandle(0xffff, 0), - Parent: netlink.HANDLE_CLSACT, - } - - qdisc := &netlink.GenericQdisc{ - QdiscAttrs: attrs, - QdiscType: "clsact", - } - - e.qdisc = qdisc - - if err := netlink.QdiscAdd(qdisc); err != nil { - if os.IsExist(err) { - _ = netlink.QdiscDel(qdisc) - err = netlink.QdiscAdd(qdisc) - } - - if err != nil { - e.Close() - return fmt.Errorf("cannot add clsact qdisc: %w", err) - } - } - - filterAttrs := netlink.FilterAttrs{ - LinkIndex: e.ifIndex, - Parent: netlink.HANDLE_MIN_EGRESS, - Handle: netlink.MakeHandle(0, 1), - Protocol: unix.ETH_P_ALL, - Priority: 1, - } - - filter := &netlink.BpfFilter{ - FilterAttrs: filterAttrs, - Fd: objs.bpfPrograms.TcTunFunc.FD(), - Name: "mihomo-tc-" + e.ifName, - DirectAction: true, - } - - if err := netlink.FilterAdd(filter); err != nil { - e.Close() - return fmt.Errorf("cannot attach ebpf object to filter: %w", err) - } - - e.filter = filter - - return nil -} - -func (e *EBpfTC) Close() { - if e.filter != nil { - _ = netlink.FilterDel(e.filter) - } - if e.qdisc != nil { - _ = netlink.QdiscDel(e.qdisc) - } - if e.objs != nil { - _ = e.objs.Close() - } - _ = os.Remove(filepath.Join(e.bpfPath, "tc_params_map")) -} - -func (e *EBpfTC) Lookup(_ netip.AddrPort) (socks5.Addr, error) { - return nil, fmt.Errorf("not supported") -} diff --git a/config/config.go b/config/config.go index d4868c02..788f227b 100644 --- a/config/config.go +++ b/config/config.go @@ -66,7 +66,6 @@ type General struct { TCPConcurrent bool `json:"tcp-concurrent"` FindProcessMode P.FindProcessMode `json:"find-process-mode"` Sniffing bool `json:"sniffing"` - EBpf EBpf `json:"-"` GlobalClientFingerprint string `json:"global-client-fingerprint"` GlobalUA string `json:"global-ua"` } @@ -343,7 +342,6 @@ type RawConfig struct { DNS RawDNS `yaml:"dns" json:"dns"` Tun RawTun `yaml:"tun"` TuicServer RawTuicServer `yaml:"tuic-server"` - EBpf EBpf `yaml:"ebpf"` IPTables IPTables `yaml:"iptables"` Experimental Experimental `yaml:"experimental"` Profile Profile `yaml:"profile"` @@ -382,12 +380,6 @@ type RawSniffingConfig struct { OverrideDest *bool `yaml:"override-destination" json:"override-destination"` } -// EBpf config -type EBpf struct { - RedirectToTun []string `yaml:"redirect-to-tun" json:"redirect-to-tun"` - AutoRedir []string `yaml:"auto-redir" json:"auto-redir"` -} - var ( GroupsList = list.New() ProxiesList = list.New() @@ -448,10 +440,6 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { ALPN: []string{"h3"}, MaxUdpRelayPacketSize: 1500, }, - EBpf: EBpf{ - RedirectToTun: []string{}, - AutoRedir: []string{}, - }, IPTables: IPTables{ Enable: false, InboundInterface: "lo", @@ -720,7 +708,6 @@ func parseGeneral(cfg *RawConfig) (*General, error) { GeodataLoader: cfg.GeodataLoader, TCPConcurrent: cfg.TCPConcurrent, FindProcessMode: cfg.FindProcessMode, - EBpf: cfg.EBpf, GlobalClientFingerprint: cfg.GlobalClientFingerprint, GlobalUA: cfg.GlobalUA, }, nil diff --git a/constant/ebpf.go b/constant/ebpf.go deleted file mode 100644 index e3bb62fe..00000000 --- a/constant/ebpf.go +++ /dev/null @@ -1,20 +0,0 @@ -package constant - -import ( - "net/netip" - - "github.com/metacubex/mihomo/transport/socks5" -) - -const ( - BpfFSPath = "/sys/fs/bpf/mihomo" - - TcpAutoRedirPort = 't'<<8 | 'r'<<0 - MihomoTrafficMark = 'c'<<24 | 'l'<<16 | 't'<<8 | 'm'<<0 -) - -type EBpf interface { - Start() error - Close() - Lookup(srcAddrPort netip.AddrPort) (socks5.Addr, error) -} diff --git a/docs/config.yaml b/docs/config.yaml index 8db06b6d..647cf14c 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -166,13 +166,6 @@ tun: # exclude-package: # 排除被路由的 Android 应用包名 # - com.android.captiveportallogin -#ebpf 配置 -ebpf: - auto-redir: # redirect 模式,仅支持 TCP - - eth0 - redirect-to-tun: # UDP+TCP 使用该功能请勿启用 auto-route - - eth0 - # 嗅探域名 可选配置 sniffer: enable: false diff --git a/go.mod b/go.mod index bc9fccba..796e0962 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/bahlo/generic-list-go v0.2.0 - github.com/cilium/ebpf v0.12.3 github.com/coreos/go-iptables v0.7.0 github.com/dlclark/regexp2 v1.11.4 github.com/go-chi/chi/v5 v5.1.0 @@ -82,6 +81,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/kr/text v0.2.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mdlayher/socket v0.4.1 // indirect diff --git a/go.sum b/go.sum index d5b57053..a3478860 100644 --- a/go.sum +++ b/go.sum @@ -17,12 +17,11 @@ github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx2 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4= -github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8= github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -37,7 +36,6 @@ github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 h1:tlDMEdcPRQKBE github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1/go.mod h1:4RfsapbGx2j/vU5xC/5/9qB3kn9Awp1YDiEnN43QrJ4= github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 h1:fuGucgPk5dN6wzfnxl3D0D3rVLw4v2SbBT9jb4VnxzA= github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtBcj7sBuTTRupn7c2bFspMDIObMJsVK8TeUvpShPok= -github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk= @@ -85,8 +83,9 @@ github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2 github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= @@ -155,7 +154,6 @@ 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.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM= github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs= diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 6504e96a..89145241 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -26,7 +26,6 @@ import ( "github.com/metacubex/mihomo/component/updater" "github.com/metacubex/mihomo/config" C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/constant/features" "github.com/metacubex/mihomo/constant/provider" "github.com/metacubex/mihomo/dns" "github.com/metacubex/mihomo/listener" @@ -182,9 +181,6 @@ func updateListeners(general *config.General, listeners map[string]C.InboundList listener.ReCreateHTTP(general.Port, tunnel.Tunnel) listener.ReCreateSocks(general.SocksPort, tunnel.Tunnel) listener.ReCreateRedir(general.RedirPort, tunnel.Tunnel) - if !features.CMFA { - listener.ReCreateAutoRedir(general.EBpf.AutoRedir, tunnel.Tunnel) - } listener.ReCreateTProxy(general.TProxyPort, tunnel.Tunnel) listener.ReCreateMixed(general.MixedPort, tunnel.Tunnel) listener.ReCreateShadowSocks(general.ShadowSocksConfig, tunnel.Tunnel) @@ -352,7 +348,6 @@ func updateTun(general *config.General) { return } listener.ReCreateTun(general.Tun, tunnel.Tunnel) - listener.ReCreateRedirToTun(general.EBpf.RedirectToTun) } func updateSniffer(sniffer *config.Sniffer) { diff --git a/listener/autoredir/tcp.go b/listener/autoredir/tcp.go deleted file mode 100644 index 2b21b087..00000000 --- a/listener/autoredir/tcp.go +++ /dev/null @@ -1,95 +0,0 @@ -package autoredir - -import ( - "net" - "net/netip" - - "github.com/metacubex/mihomo/adapter/inbound" - N "github.com/metacubex/mihomo/common/net" - C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/log" - "github.com/metacubex/mihomo/transport/socks5" -) - -type Listener struct { - listener net.Listener - addr string - closed bool - additions []inbound.Addition - lookupFunc func(netip.AddrPort) (socks5.Addr, error) -} - -// RawAddress implements C.Listener -func (l *Listener) RawAddress() string { - return l.addr -} - -// Address implements C.Listener -func (l *Listener) Address() string { - return l.listener.Addr().String() -} - -// Close implements C.Listener -func (l *Listener) Close() error { - l.closed = true - return l.listener.Close() -} - -func (l *Listener) TCPAddr() netip.AddrPort { - return l.listener.Addr().(*net.TCPAddr).AddrPort() -} - -func (l *Listener) SetLookupFunc(lookupFunc func(netip.AddrPort) (socks5.Addr, error)) { - l.lookupFunc = lookupFunc -} - -func (l *Listener) handleRedir(conn net.Conn, tunnel C.Tunnel) { - if l.lookupFunc == nil { - log.Errorln("[Auto Redirect] lookup function is nil") - return - } - - target, err := l.lookupFunc(conn.RemoteAddr().(*net.TCPAddr).AddrPort()) - if err != nil { - log.Warnln("[Auto Redirect] %v", err) - _ = conn.Close() - return - } - - N.TCPKeepAlive(conn) - - tunnel.HandleTCPConn(inbound.NewSocket(target, conn, C.REDIR, l.additions...)) -} - -func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) { - if len(additions) == 0 { - additions = []inbound.Addition{ - inbound.WithInName("DEFAULT-REDIR"), - inbound.WithSpecialRules(""), - } - } - l, err := net.Listen("tcp", addr) - if err != nil { - return nil, err - } - rl := &Listener{ - listener: l, - addr: addr, - additions: additions, - } - - go func() { - for { - c, err := l.Accept() - if err != nil { - if rl.closed { - break - } - continue - } - go rl.handleRedir(c, tunnel) - } - }() - - return rl, nil -} diff --git a/listener/listener.go b/listener/listener.go index 76860e0d..4d106778 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -9,9 +9,7 @@ import ( "strings" "sync" - "github.com/metacubex/mihomo/component/ebpf" C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/listener/autoredir" LC "github.com/metacubex/mihomo/listener/config" "github.com/metacubex/mihomo/listener/http" "github.com/metacubex/mihomo/listener/mixed" @@ -49,9 +47,6 @@ var ( shadowSocksListener C.MultiAddrListener vmessListener *sing_vmess.Listener tuicListener *tuic.Listener - autoRedirListener *autoredir.Listener - autoRedirProgram *ebpf.TcEBpfProgram - tcProgram *ebpf.TcEBpfProgram // lock for recreate function socksMux sync.Mutex @@ -540,95 +535,6 @@ func ReCreateTun(tunConf LC.Tun, tunnel C.Tunnel) { log.Infoln("[TUN] Tun adapter listening at: %s", tunLister.Address()) } -func ReCreateRedirToTun(ifaceNames []string) { - tcMux.Lock() - defer tcMux.Unlock() - - nicArr := ifaceNames - slices.Sort(nicArr) - nicArr = slices.Compact(nicArr) - - if tcProgram != nil { - tcProgram.Close() - tcProgram = nil - } - - if len(nicArr) == 0 { - return - } - - tunConf := GetTunConf() - - if !tunConf.Enable { - return - } - - program, err := ebpf.NewTcEBpfProgram(nicArr, tunConf.Device) - if err != nil { - log.Errorln("Attached tc ebpf program error: %v", err) - return - } - tcProgram = program - - log.Infoln("Attached tc ebpf program to interfaces %v", tcProgram.RawNICs()) -} - -func ReCreateAutoRedir(ifaceNames []string, tunnel C.Tunnel) { - autoRedirMux.Lock() - defer autoRedirMux.Unlock() - - var err error - defer func() { - if err != nil { - if autoRedirListener != nil { - _ = autoRedirListener.Close() - autoRedirListener = nil - } - if autoRedirProgram != nil { - autoRedirProgram.Close() - autoRedirProgram = nil - } - log.Errorln("Start auto redirect server error: %s", err.Error()) - } - }() - - nicArr := ifaceNames - slices.Sort(nicArr) - nicArr = slices.Compact(nicArr) - - if autoRedirListener != nil && autoRedirProgram != nil { - _ = autoRedirListener.Close() - autoRedirProgram.Close() - autoRedirListener = nil - autoRedirProgram = nil - } - - if len(nicArr) == 0 { - return - } - - defaultRouteInterfaceName, err := ebpf.GetAutoDetectInterface() - if err != nil { - return - } - - addr := genAddr("*", C.TcpAutoRedirPort, true) - - autoRedirListener, err = autoredir.New(addr, tunnel) - if err != nil { - return - } - - autoRedirProgram, err = ebpf.NewRedirEBpfProgram(nicArr, autoRedirListener.TCPAddr().Port(), defaultRouteInterfaceName) - if err != nil { - return - } - - autoRedirListener.SetLookupFunc(autoRedirProgram.Lookup) - - log.Infoln("Auto redirect proxy listening at: %s, attached tc ebpf program to interfaces %v", autoRedirListener.Address(), autoRedirProgram.RawNICs()) -} - func PatchTunnel(tunnels []LC.Tunnel, tunnel C.Tunnel) { tunnelMux.Lock() defer tunnelMux.Unlock() diff --git a/test/go.mod b/test/go.mod index 92374886..f364c2c8 100644 --- a/test/go.mod +++ b/test/go.mod @@ -22,7 +22,6 @@ require ( github.com/andybalholm/brotli v1.0.5 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/buger/jsonparser v1.1.1 // indirect - github.com/cilium/ebpf v0.12.3 // indirect github.com/coreos/go-iptables v0.7.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/distribution/reference v0.5.0 // indirect diff --git a/test/go.sum b/test/go.sum index af34b356..05a23ce9 100644 --- a/test/go.sum +++ b/test/go.sum @@ -19,8 +19,6 @@ github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx2 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4= -github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM= github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8= github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From 82984891e4f5b8f8541d2c8213d59e8ca11e0014 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 19 Aug 2024 10:01:05 +0800 Subject: [PATCH 173/311] action: fix build for armv5/6/7 --- .github/workflows/build.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fb3eb621..e0b964f4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -40,10 +40,10 @@ jobs: - { goos: linux, goarch: arm, goarm: '5', output: armv5 } - { goos: linux, goarch: arm, goarm: '6', output: armv6 } - { goos: linux, goarch: arm, goarm: '7', output: armv7 } - - { goos: linux, goarch: mips, mips: hardfloat, output: mips-hardfloat } - - { goos: linux, goarch: mips, mips: softfloat, output: mips-softfloat } - - { goos: linux, goarch: mipsle, mips: hardfloat, output: mipsle-hardfloat } - - { goos: linux, goarch: mipsle, mips: softfloat, output: mipsle-softfloat } + - { goos: linux, goarch: mips, gomips: hardfloat, output: mips-hardfloat } + - { goos: linux, goarch: mips, gomips: softfloat, output: mips-softfloat } + - { goos: linux, goarch: mipsle, gomips: hardfloat, output: mipsle-hardfloat } + - { goos: linux, goarch: mipsle, gomips: softfloat, output: mipsle-softfloat } - { goos: linux, goarch: mips64, output: mips64 } - { goos: linux, goarch: mips64le, output: mips64le } - { goos: linux, goarch: loong64, output: loong64-abi1, abi: '1' } @@ -219,8 +219,8 @@ jobs: GOOS: ${{matrix.jobs.goos}} GOARCH: ${{matrix.jobs.goarch}} GOAMD64: ${{matrix.jobs.goamd64}} - GOARM: ${{matrix.jobs.arm}} - GOMIPS: ${{matrix.jobs.mips}} + GOARM: ${{matrix.jobs.goarm}} + GOMIPS: ${{matrix.jobs.gomips}} run: | echo $CGO_ENABLED go build -v -tags "with_gvisor" -trimpath -ldflags "${BUILDTAG} -X 'github.com/metacubex/mihomo/constant.Version=${VERSION}' -X 'github.com/metacubex/mihomo/constant.BuildTime=${BUILDTIME}' -w -s -buildid=" From c8380335cbdea02965876b443c070ff8cd048956 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Mon, 19 Aug 2024 16:02:43 +0800 Subject: [PATCH 174/311] chore: improve `include-all-proxies` compatibility --- adapter/outboundgroup/parser.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/adapter/outboundgroup/parser.go b/adapter/outboundgroup/parser.go index efc38aab..b073c4bb 100644 --- a/adapter/outboundgroup/parser.go +++ b/adapter/outboundgroup/parser.go @@ -88,6 +88,9 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide } else { groupOption.Proxies = append(groupOption.Proxies, AllProxies...) } + if len(groupOption.Proxies) == 0 && len(groupOption.Use) == 0 { + groupOption.Proxies = []string{"COMPATIBLE"} + } } if len(groupOption.Proxies) == 0 && len(groupOption.Use) == 0 { From ba605b693b4ce2f7a50e31508d99032b04232051 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 20 Aug 2024 09:39:33 +0800 Subject: [PATCH 175/311] action: show go env when build --- .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 e0b964f4..94854f27 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -222,7 +222,7 @@ jobs: GOARM: ${{matrix.jobs.goarm}} GOMIPS: ${{matrix.jobs.gomips}} run: | - echo $CGO_ENABLED + go env go build -v -tags "with_gvisor" -trimpath -ldflags "${BUILDTAG} -X 'github.com/metacubex/mihomo/constant.Version=${VERSION}' -X 'github.com/metacubex/mihomo/constant.BuildTime=${BUILDTIME}' -w -s -buildid=" if [ "${{matrix.jobs.goos}}" = "windows" ]; then cp mihomo.exe mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}.exe From 512d188384ca0468df8daf23f59a247c025cedd5 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Tue, 20 Aug 2024 21:13:26 +0800 Subject: [PATCH 176/311] fix redundant WindowsDNS (#1456) Co-authored-by: ForestL <45709305+forestl18@users.noreply.github.com> --- dns/system_windows.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dns/system_windows.go b/dns/system_windows.go index 47c1ebaa..2f4d1b63 100644 --- a/dns/system_windows.go +++ b/dns/system_windows.go @@ -8,6 +8,7 @@ import ( "syscall" "unsafe" + "golang.org/x/exp/slices" "golang.org/x/sys/windows" ) @@ -40,6 +41,9 @@ func dnsReadConfig() (servers []string, err error) { // Unexpected type. continue } + if slices.Contains(servers, ip.String()) { + continue + } servers = append(servers, ip.String()) } } From c4660e1aad5cf8f5a89874dc191e8c110c15eb58 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 21 Aug 2024 10:51:49 +0800 Subject: [PATCH 177/311] chore: reopen tfo support on windows for golang1.23 maybe broken again when golang1.24 release --- adapter/inbound/listen.go | 14 +++++++++++++- adapter/inbound/listen_unix.go | 23 ----------------------- adapter/inbound/listen_windows.go | 15 --------------- component/dialer/tfo.go | 17 +++++++++++++++++ component/dialer/tfo_unix.go | 25 ------------------------- component/dialer/tfo_windows.go | 15 +++++++-------- go.mod | 2 +- go.sum | 4 ++-- 8 files changed, 40 insertions(+), 75 deletions(-) delete mode 100644 adapter/inbound/listen_unix.go delete mode 100644 adapter/inbound/listen_windows.go delete mode 100644 component/dialer/tfo_unix.go diff --git a/adapter/inbound/listen.go b/adapter/inbound/listen.go index edbccea7..18dc1bc2 100644 --- a/adapter/inbound/listen.go +++ b/adapter/inbound/listen.go @@ -3,10 +3,22 @@ package inbound import ( "context" "net" + + "github.com/metacubex/tfo-go" ) +var ( + lc = tfo.ListenConfig{ + DisableTFO: true, + } +) + +func SetTfo(open bool) { + lc.DisableTFO = !open +} + func SetMPTCP(open bool) { - setMultiPathTCP(getListenConfig(), open) + setMultiPathTCP(&lc.ListenConfig, open) } func ListenContext(ctx context.Context, network, address string) (net.Listener, error) { diff --git a/adapter/inbound/listen_unix.go b/adapter/inbound/listen_unix.go deleted file mode 100644 index bb78adb2..00000000 --- a/adapter/inbound/listen_unix.go +++ /dev/null @@ -1,23 +0,0 @@ -//go:build unix - -package inbound - -import ( - "net" - - "github.com/metacubex/tfo-go" -) - -var ( - lc = tfo.ListenConfig{ - DisableTFO: true, - } -) - -func SetTfo(open bool) { - lc.DisableTFO = !open -} - -func getListenConfig() *net.ListenConfig { - return &lc.ListenConfig -} diff --git a/adapter/inbound/listen_windows.go b/adapter/inbound/listen_windows.go deleted file mode 100644 index a4223e2b..00000000 --- a/adapter/inbound/listen_windows.go +++ /dev/null @@ -1,15 +0,0 @@ -package inbound - -import ( - "net" -) - -var ( - lc = net.ListenConfig{} -) - -func SetTfo(open bool) {} - -func getListenConfig() *net.ListenConfig { - return &lc -} diff --git a/component/dialer/tfo.go b/component/dialer/tfo.go index bc32b38a..76fe94d0 100644 --- a/component/dialer/tfo.go +++ b/component/dialer/tfo.go @@ -5,8 +5,12 @@ import ( "io" "net" "time" + + "github.com/metacubex/tfo-go" ) +var DisableTFO = false + type tfoConn struct { net.Conn closed bool @@ -120,3 +124,16 @@ func (c *tfoConn) ReaderReplaceable() bool { func (c *tfoConn) WriterReplaceable() bool { return c.Conn != nil } + +func dialTFO(ctx context.Context, netDialer net.Dialer, network, address string) (net.Conn, error) { + ctx, cancel := context.WithTimeout(context.Background(), DefaultTCPTimeout) + dialer := tfo.Dialer{Dialer: netDialer, DisableTFO: false} + return &tfoConn{ + dialed: make(chan bool, 1), + cancel: cancel, + ctx: ctx, + dialFn: func(ctx context.Context, earlyData []byte) (net.Conn, error) { + return dialer.DialContext(ctx, network, address, earlyData) + }, + }, nil +} diff --git a/component/dialer/tfo_unix.go b/component/dialer/tfo_unix.go deleted file mode 100644 index b8908849..00000000 --- a/component/dialer/tfo_unix.go +++ /dev/null @@ -1,25 +0,0 @@ -//go:build unix - -package dialer - -import ( - "context" - "net" - - "github.com/metacubex/tfo-go" -) - -const DisableTFO = false - -func dialTFO(ctx context.Context, netDialer net.Dialer, network, address string) (net.Conn, error) { - ctx, cancel := context.WithTimeout(context.Background(), DefaultTCPTimeout) - dialer := tfo.Dialer{Dialer: netDialer, DisableTFO: false} - return &tfoConn{ - dialed: make(chan bool, 1), - cancel: cancel, - ctx: ctx, - dialFn: func(ctx context.Context, earlyData []byte) (net.Conn, error) { - return dialer.DialContext(ctx, network, address, earlyData) - }, - }, nil -} diff --git a/component/dialer/tfo_windows.go b/component/dialer/tfo_windows.go index f1dddcf4..63266118 100644 --- a/component/dialer/tfo_windows.go +++ b/component/dialer/tfo_windows.go @@ -1,12 +1,11 @@ package dialer -import ( - "context" - "net" -) +import "github.com/metacubex/mihomo/constant/features" -const DisableTFO = true - -func dialTFO(ctx context.Context, netDialer net.Dialer, network, address string) (net.Conn, error) { - return netDialer.DialContext(ctx, network, address) +func init() { + // According to MSDN, this option is available since Windows 10, 1607 + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms738596(v=vs.85).aspx + if features.WindowsMajorVersion < 10 || (features.WindowsMajorVersion == 10 && features.WindowsBuildNumber < 14393) { + DisableTFO = true + } } diff --git a/go.mod b/go.mod index 796e0962..cc1c8b63 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a - github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 + github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d github.com/metacubex/utls v1.6.6 github.com/miekg/dns v1.1.61 github.com/mroth/weightedrand/v2 v2.1.0 diff --git a/go.sum b/go.sum index a3478860..9e63c337 100644 --- a/go.sum +++ b/go.sum @@ -120,8 +120,8 @@ github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosq github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a h1:NpSGclHJUYndUwBmyIpFBSoBVg8PoVX7QQKhYg0DjM0= github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= -github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 h1:as/aO/fM8nv4W4pOr9EETP6kV/Oaujk3fUNyQSJK61c= -github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= +github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d h1:j9LtzkYstLFoNvXW824QQeN7Y26uPL5249kzWKbzO9U= +github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8= github.com/metacubex/utls v1.6.6/go.mod h1:+WLFUnXjcpdxXCnyX25nggw8C6YonZ8zOK2Zm/oRvdo= github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs= From 41efc5e5aba871aa43ea26656326f5c35979b6e1 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 22 Aug 2024 09:24:27 +0800 Subject: [PATCH 178/311] chore: update dependencies --- component/fakeip/pool_test.go | 2 +- component/profile/cachefile/cache.go | 2 +- go.mod | 18 +++++++------- go.sum | 36 ++++++++++++++-------------- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/component/fakeip/pool_test.go b/component/fakeip/pool_test.go index ed52fb6d..c2e46584 100644 --- a/component/fakeip/pool_test.go +++ b/component/fakeip/pool_test.go @@ -12,7 +12,7 @@ import ( C "github.com/metacubex/mihomo/constant" RP "github.com/metacubex/mihomo/rules/provider" - "github.com/sagernet/bbolt" + "github.com/metacubex/bbolt" "github.com/stretchr/testify/assert" ) diff --git a/component/profile/cachefile/cache.go b/component/profile/cachefile/cache.go index 11068647..e3da0369 100644 --- a/component/profile/cachefile/cache.go +++ b/component/profile/cachefile/cache.go @@ -9,7 +9,7 @@ import ( C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" - "github.com/sagernet/bbolt" + "github.com/metacubex/bbolt" ) var ( diff --git a/go.mod b/go.mod index cc1c8b63..b0c1b3dd 100644 --- a/go.mod +++ b/go.mod @@ -11,12 +11,13 @@ require ( github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.3 github.com/gobwas/ws v1.4.0 - github.com/gofrs/uuid/v5 v5.2.0 - github.com/insomniacslk/dhcp v0.0.0-20240710054256-ddd8a41251c9 + github.com/gofrs/uuid/v5 v5.3.0 + github.com/insomniacslk/dhcp v0.0.0-20240812123929-b105c29bd1b5 github.com/klauspost/compress v1.17.9 github.com/klauspost/cpuid/v2 v2.2.8 github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.7.2 + github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 github.com/metacubex/chacha v0.1.0 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 github.com/metacubex/quic-go v0.46.1-0.20240807232329-1c6cb2d67f58 @@ -29,19 +30,18 @@ require ( github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d github.com/metacubex/utls v1.6.6 - github.com/miekg/dns v1.1.61 + github.com/miekg/dns v1.1.62 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/v3 v3.4.0 - github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a github.com/sagernet/fswatch v0.1.1 github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a github.com/sagernet/sing v0.5.0-alpha.13 github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 github.com/sagernet/sing-shadowtls v0.1.4 github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e - github.com/samber/lo v1.46.0 + github.com/samber/lo v1.47.0 github.com/shirou/gopsutil/v3 v3.24.5 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.9.0 @@ -50,9 +50,9 @@ require ( go.uber.org/automaxprocs v1.5.3 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba golang.org/x/crypto v0.26.0 - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 + golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa golang.org/x/net v0.28.0 - golang.org/x/sys v0.23.0 + golang.org/x/sys v0.24.0 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.3.0 @@ -106,11 +106,11 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect go.uber.org/mock v0.4.0 // indirect - golang.org/x/mod v0.19.0 // indirect + golang.org/x/mod v0.20.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.23.0 // indirect + golang.org/x/tools v0.24.0 // indirect ) replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297 diff --git a/go.sum b/go.sum index 9e63c337..8194b68a 100644 --- a/go.sum +++ b/go.sum @@ -58,8 +58,8 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs= github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc= -github.com/gofrs/uuid/v5 v5.2.0 h1:qw1GMx6/y8vhVsx626ImfKMuS5CvJmhIKKtuyvfajMM= -github.com/gofrs/uuid/v5 v5.2.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= +github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk= +github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= @@ -73,8 +73,8 @@ github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I= 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-20240710054256-ddd8a41251c9 h1:LZJWucZz7ztCqY6Jsu7N9g124iJ2kt/O62j3+UchZFg= -github.com/insomniacslk/dhcp v0.0.0-20240710054256-ddd8a41251c9/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= +github.com/insomniacslk/dhcp v0.0.0-20240812123929-b105c29bd1b5 h1:GkMacU5ftc+IEg1449N3UEy2XLDz58W4fkrRu2fibb8= +github.com/insomniacslk/dhcp v0.0.0-20240812123929-b105c29bd1b5/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 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= @@ -96,6 +96,8 @@ github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/ github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= +github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 h1:oBowHVKZycNtAFbZ6avaCSZJYeme2Nrj+4RpV2cNJig= +github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399/go.mod h1:4xcieuIK+M4bGQmQYZVqEaIYqjS1ahO4kXG7EmDgEro= github.com/metacubex/chacha v0.1.0 h1:tg9RSJ18NvL38cCWNyYH1eiG6qDCyyXIaTLQthon0sc= github.com/metacubex/chacha v0.1.0/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8= github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI= @@ -124,8 +126,8 @@ github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d h1:j9LtzkYstLFoNv github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8= github.com/metacubex/utls v1.6.6/go.mod h1:+WLFUnXjcpdxXCnyX25nggw8C6YonZ8zOK2Zm/oRvdo= -github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs= -github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ= +github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= +github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= 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= @@ -154,8 +156,6 @@ 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.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= -github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0= -github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM= github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs= github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o= github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis= @@ -170,8 +170,8 @@ github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxe github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e h1:iGH0RMv2FzELOFNFQtvsxH7NPmlo7X5JizEK51UCojo= github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e/go.mod h1:YbL4TKHRR6APYQv3U2RGfwLDpPYSyWz6oUlpISBEzBE= -github.com/samber/lo v1.46.0 h1:w8G+oaCPgz1PoCJztqymCFaKwXt+5cCXn51uPxExFfQ= -github.com/samber/lo v1.46.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= +github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc= +github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= @@ -226,12 +226,12 @@ 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.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= +golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= 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.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= -golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= @@ -255,8 +255,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= @@ -264,8 +264,8 @@ golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= -golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= 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= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= From 16c95fca87a51ad1ff70a960e5035552a33c7474 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 23 Aug 2024 21:05:43 +0800 Subject: [PATCH 179/311] fix: tradition shadowsocks server not apply additions https://github.com/MetaCubeX/mihomo/issues/1466 --- listener/shadowsocks/tcp.go | 6 +++--- listener/shadowsocks/udp.go | 4 ++-- listener/sing_shadowsocks/server.go | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/listener/shadowsocks/tcp.go b/listener/shadowsocks/tcp.go index c08667de..c3843814 100644 --- a/listener/shadowsocks/tcp.go +++ b/listener/shadowsocks/tcp.go @@ -22,7 +22,7 @@ type Listener struct { var _listener *Listener -func New(config LC.ShadowsocksServer, tunnel C.Tunnel) (*Listener, error) { +func New(config LC.ShadowsocksServer, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) { pickCipher, err := core.PickCipher(config.Cipher, nil, config.Password) if err != nil { return nil, err @@ -36,7 +36,7 @@ func New(config LC.ShadowsocksServer, tunnel C.Tunnel) (*Listener, error) { if config.Udp { //UDP - ul, err := NewUDP(addr, pickCipher, tunnel) + ul, err := NewUDP(addr, pickCipher, tunnel, additions...) if err != nil { return nil, err } @@ -60,7 +60,7 @@ func New(config LC.ShadowsocksServer, tunnel C.Tunnel) (*Listener, error) { continue } N.TCPKeepAlive(c) - go sl.HandleConn(c, tunnel) + go sl.HandleConn(c, tunnel, additions...) } }() } diff --git a/listener/shadowsocks/udp.go b/listener/shadowsocks/udp.go index 4336db22..77932ed1 100644 --- a/listener/shadowsocks/udp.go +++ b/listener/shadowsocks/udp.go @@ -17,7 +17,7 @@ type UDPListener struct { closed bool } -func NewUDP(addr string, pickCipher core.Cipher, tunnel C.Tunnel) (*UDPListener, error) { +func NewUDP(addr string, pickCipher core.Cipher, tunnel C.Tunnel, additions ...inbound.Addition) (*UDPListener, error) { l, err := net.ListenPacket("udp", addr) if err != nil { return nil, err @@ -42,7 +42,7 @@ func NewUDP(addr string, pickCipher core.Cipher, tunnel C.Tunnel) (*UDPListener, } continue } - handleSocksUDP(conn, tunnel, data, put, remoteAddr) + handleSocksUDP(conn, tunnel, data, put, remoteAddr, additions...) } }() diff --git a/listener/sing_shadowsocks/server.go b/listener/sing_shadowsocks/server.go index bd5002a4..1cb798f7 100644 --- a/listener/sing_shadowsocks/server.go +++ b/listener/sing_shadowsocks/server.go @@ -72,7 +72,7 @@ func New(config LC.ShadowsocksServer, tunnel C.Tunnel, additions ...inbound.Addi 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, tunnel) + return embedSS.New(config, tunnel, additions...) } if err != nil { return nil, err From f5834dd5e272d69a9f494ad318c8f3f6e5f34c1f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 24 Aug 2024 13:25:23 +0800 Subject: [PATCH 180/311] chore: code cleanup --- common/nnip/netip.go | 20 +++++ hub/executor/executor.go | 10 +-- hub/route/configs.go | 15 +--- listener/config/tun.go | 147 ++++++++++++++++++++++++++++++++++- listener/listener.go | 161 ++++----------------------------------- 5 files changed, 185 insertions(+), 168 deletions(-) diff --git a/common/nnip/netip.go b/common/nnip/netip.go index e4566138..b987bb45 100644 --- a/common/nnip/netip.go +++ b/common/nnip/netip.go @@ -51,3 +51,23 @@ func UnMasked(p netip.Prefix) netip.Addr { } return addr } + +// PrefixCompare returns an integer comparing two prefixes. +// The result will be 0 if p == p2, -1 if p < p2, and +1 if p > p2. +// modify from https://github.com/golang/go/issues/61642#issuecomment-1848587909 +func PrefixCompare(p, p2 netip.Prefix) int { + // compare by validity, address family and prefix base address + if c := p.Masked().Addr().Compare(p2.Masked().Addr()); c != 0 { + return c + } + // compare by prefix length + f1, f2 := p.Bits(), p2.Bits() + if f1 < f2 { + return -1 + } + if f1 > f2 { + return 1 + } + // compare by prefix address + return p.Addr().Compare(p2.Addr()) +} diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 89145241..8eb54ac4 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -90,6 +90,7 @@ func ApplyConfig(cfg *config.Config, force bool) { } } + updateExperimental(cfg) updateUsers(cfg.Users) updateProxies(cfg.Proxies, cfg.Providers) updateRules(cfg.Rules, cfg.SubRules, cfg.RuleProviders) @@ -100,8 +101,6 @@ func ApplyConfig(cfg *config.Config, force bool) { updateDNS(cfg.DNS, cfg.General.IPv6) updateListeners(cfg.General, cfg.Listeners, force) updateIPTables(cfg) - updateTun(cfg.General) - updateExperimental(cfg) updateTunnels(cfg.Tunnels) tunnel.OnInnerLoading() @@ -186,6 +185,7 @@ func updateListeners(general *config.General, listeners map[string]C.InboundList listener.ReCreateShadowSocks(general.ShadowSocksConfig, tunnel.Tunnel) listener.ReCreateVmess(general.VmessConfig, tunnel.Tunnel) listener.ReCreateTuic(general.TuicServer, tunnel.Tunnel) + listener.ReCreateTun(general.Tun, tunnel.Tunnel) } func updateExperimental(c *config.Config) { @@ -343,12 +343,6 @@ func hcCompatibleProvider(proxyProviders map[string]provider.ProxyProvider) { } } -func updateTun(general *config.General) { - if general == nil { - return - } - listener.ReCreateTun(general.Tun, tunnel.Tunnel) -} func updateSniffer(sniffer *config.Sniffer) { if sniffer.Enable { diff --git a/hub/route/configs.go b/hub/route/configs.go index c7b340c6..b31a607e 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -62,7 +62,6 @@ type tunSchema struct { DNSHijack *[]string `yaml:"dns-hijack" json:"dns-hijack"` AutoRoute *bool `yaml:"auto-route" json:"auto-route"` AutoDetectInterface *bool `yaml:"auto-detect-interface" json:"auto-detect-interface"` - //RedirectToTun []string `yaml:"-" json:"-"` MTU *uint32 `yaml:"mtu" json:"mtu,omitempty"` GSO *bool `yaml:"gso" json:"gso,omitempty"` @@ -118,21 +117,13 @@ func getConfigs(w http.ResponseWriter, r *http.Request) { render.JSON(w, r, general) } -func pointerOrDefault(p *int, def int) int { +func pointerOrDefault[T any](p *T, def T) T { if p != nil { return *p } return def } -func pointerOrDefaultString(p *string, def string) string { - if p != nil { - return *p - } - - return def -} - func pointerOrDefaultTun(p *tunSchema, def LC.Tun) LC.Tun { if p != nil { def.Enable = p.Enable @@ -336,8 +327,8 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) { P.ReCreateTProxy(pointerOrDefault(general.TProxyPort, ports.TProxyPort), tunnel.Tunnel) P.ReCreateMixed(pointerOrDefault(general.MixedPort, ports.MixedPort), tunnel.Tunnel) P.ReCreateTun(pointerOrDefaultTun(general.Tun, P.LastTunConf), tunnel.Tunnel) - P.ReCreateShadowSocks(pointerOrDefaultString(general.ShadowSocksConfig, ports.ShadowSocksConfig), tunnel.Tunnel) - P.ReCreateVmess(pointerOrDefaultString(general.VmessConfig, ports.VmessConfig), tunnel.Tunnel) + P.ReCreateShadowSocks(pointerOrDefault(general.ShadowSocksConfig, ports.ShadowSocksConfig), tunnel.Tunnel) + P.ReCreateVmess(pointerOrDefault(general.VmessConfig, ports.VmessConfig), tunnel.Tunnel) P.ReCreateTuic(pointerOrDefaultTuicServer(general.TuicServer, P.LastTuicConf), tunnel.Tunnel) if general.Mode != nil { diff --git a/listener/config/tun.go b/listener/config/tun.go index cea22bfd..06dc53ef 100644 --- a/listener/config/tun.go +++ b/listener/config/tun.go @@ -3,7 +3,10 @@ package config import ( "net/netip" + "github.com/metacubex/mihomo/common/nnip" C "github.com/metacubex/mihomo/constant" + + "golang.org/x/exp/slices" ) func StringSliceToNetipPrefixSlice(ss []string) ([]netip.Prefix, error) { @@ -25,7 +28,6 @@ type Tun struct { DNSHijack []string `yaml:"dns-hijack" json:"dns-hijack"` AutoRoute bool `yaml:"auto-route" json:"auto-route"` AutoDetectInterface bool `yaml:"auto-detect-interface" json:"auto-detect-interface"` - RedirectToTun []string `yaml:"-" json:"-"` MTU uint32 `yaml:"mtu" json:"mtu,omitempty"` GSO bool `yaml:"gso" json:"gso,omitempty"` @@ -60,3 +62,146 @@ type Tun struct { Inet4RouteExcludeAddress []netip.Prefix `yaml:"inet4-route-exclude-address" json:"inet4-route-exclude-address,omitempty"` Inet6RouteExcludeAddress []netip.Prefix `yaml:"inet6-route-exclude-address" json:"inet6-route-exclude-address,omitempty"` } + +func (t *Tun) Sort() { + slices.Sort(t.DNSHijack) + + slices.SortFunc(t.Inet4Address, nnip.PrefixCompare) + slices.SortFunc(t.Inet6Address, nnip.PrefixCompare) + slices.SortFunc(t.RouteAddress, nnip.PrefixCompare) + slices.Sort(t.RouteAddressSet) + slices.SortFunc(t.RouteExcludeAddress, nnip.PrefixCompare) + slices.Sort(t.RouteExcludeAddressSet) + slices.Sort(t.IncludeInterface) + slices.Sort(t.ExcludeInterface) + slices.Sort(t.IncludeUID) + slices.Sort(t.IncludeUIDRange) + slices.Sort(t.ExcludeUID) + slices.Sort(t.ExcludeUIDRange) + slices.Sort(t.IncludeAndroidUser) + slices.Sort(t.IncludePackage) + slices.Sort(t.ExcludePackage) + + slices.SortFunc(t.Inet4RouteAddress, nnip.PrefixCompare) + slices.SortFunc(t.Inet6RouteAddress, nnip.PrefixCompare) + slices.SortFunc(t.Inet4RouteExcludeAddress, nnip.PrefixCompare) + slices.SortFunc(t.Inet6RouteExcludeAddress, nnip.PrefixCompare) +} + +func (t *Tun) Equal(other Tun) bool { + if t.Enable != other.Enable { + return false + } + if t.Device != other.Device { + return false + } + if t.Stack != other.Stack { + return false + } + if !slices.Equal(t.DNSHijack, other.DNSHijack) { + return false + } + if t.AutoRoute != other.AutoRoute { + return false + } + if t.AutoDetectInterface != other.AutoDetectInterface { + return false + } + + if t.MTU != other.MTU { + return false + } + if t.GSO != other.GSO { + return false + } + if t.GSOMaxSize != other.GSOMaxSize { + return false + } + if !slices.Equal(t.Inet4Address, other.Inet4Address) { + return false + } + if !slices.Equal(t.Inet6Address, other.Inet6Address) { + return false + } + if t.IPRoute2TableIndex != other.IPRoute2TableIndex { + return false + } + if t.IPRoute2RuleIndex != other.IPRoute2RuleIndex { + return false + } + if t.AutoRedirect != other.AutoRedirect { + return false + } + if t.AutoRedirectInputMark != other.AutoRedirectInputMark { + return false + } + if t.AutoRedirectOutputMark != other.AutoRedirectOutputMark { + return false + } + if t.StrictRoute != other.StrictRoute { + return false + } + if !slices.Equal(t.RouteAddress, other.RouteAddress) { + return false + } + if !slices.Equal(t.RouteAddressSet, other.RouteAddressSet) { + return false + } + if !slices.Equal(t.RouteExcludeAddress, other.RouteExcludeAddress) { + return false + } + if !slices.Equal(t.RouteExcludeAddressSet, other.RouteExcludeAddressSet) { + return false + } + if !slices.Equal(t.IncludeInterface, other.IncludeInterface) { + return false + } + if !slices.Equal(t.ExcludeInterface, other.ExcludeInterface) { + return false + } + if !slices.Equal(t.IncludeUID, other.IncludeUID) { + return false + } + if !slices.Equal(t.IncludeUIDRange, other.IncludeUIDRange) { + return false + } + if !slices.Equal(t.ExcludeUID, other.ExcludeUID) { + return false + } + if !slices.Equal(t.ExcludeUIDRange, other.ExcludeUIDRange) { + return false + } + if !slices.Equal(t.IncludeAndroidUser, other.IncludeAndroidUser) { + return false + } + if !slices.Equal(t.IncludePackage, other.IncludePackage) { + return false + } + if !slices.Equal(t.ExcludePackage, other.ExcludePackage) { + return false + } + if t.EndpointIndependentNat != other.EndpointIndependentNat { + return false + } + if t.UDPTimeout != other.UDPTimeout { + return false + } + if t.FileDescriptor != other.FileDescriptor { + return false + } + + if !slices.Equal(t.Inet4RouteAddress, other.Inet4RouteAddress) { + return false + } + if !slices.Equal(t.Inet6RouteAddress, other.Inet6RouteAddress) { + return false + } + if !slices.Equal(t.Inet4RouteExcludeAddress, other.Inet4RouteExcludeAddress) { + return false + } + if !slices.Equal(t.Inet6RouteExcludeAddress, other.Inet6RouteExcludeAddress) { + return false + } + + return true +} diff --git a/listener/listener.go b/listener/listener.go index 4d106778..2e25c8b8 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -2,9 +2,7 @@ package listener import ( "fmt" - "golang.org/x/exp/slices" "net" - "sort" "strconv" "strings" "sync" @@ -49,19 +47,17 @@ var ( tuicListener *tuic.Listener // lock for recreate function - socksMux sync.Mutex - httpMux sync.Mutex - redirMux sync.Mutex - tproxyMux sync.Mutex - mixedMux sync.Mutex - tunnelMux sync.Mutex - inboundMux sync.Mutex - tunMux sync.Mutex - ssMux sync.Mutex - vmessMux sync.Mutex - tuicMux sync.Mutex - autoRedirMux sync.Mutex - tcMux sync.Mutex + socksMux sync.Mutex + httpMux sync.Mutex + redirMux sync.Mutex + tproxyMux sync.Mutex + mixedMux sync.Mutex + tunnelMux sync.Mutex + inboundMux sync.Mutex + tunMux sync.Mutex + ssMux sync.Mutex + vmessMux sync.Mutex + tuicMux sync.Mutex LastTunConf LC.Tun LastTuicConf LC.TuicServer @@ -499,6 +495,8 @@ func ReCreateMixed(port int, tunnel C.Tunnel) { } func ReCreateTun(tunConf LC.Tun, tunnel C.Tunnel) { + tunConf.Sort() + tunMux.Lock() defer func() { LastTunConf = tunConf @@ -513,7 +511,7 @@ func ReCreateTun(tunConf LC.Tun, tunnel C.Tunnel) { } }() - if !hasTunConfigChange(&tunConf) { + if tunConf.Equal(LastTunConf) { if tunLister != nil { tunLister.FlushDefaultInterface() } @@ -717,137 +715,6 @@ func genAddr(host string, port int, allowLan bool) string { return fmt.Sprintf("127.0.0.1:%d", port) } -func hasTunConfigChange(tunConf *LC.Tun) bool { - if LastTunConf.Enable != tunConf.Enable || - LastTunConf.Device != tunConf.Device || - LastTunConf.Stack != tunConf.Stack || - LastTunConf.AutoRoute != tunConf.AutoRoute || - LastTunConf.AutoDetectInterface != tunConf.AutoDetectInterface || - LastTunConf.MTU != tunConf.MTU || - LastTunConf.GSO != tunConf.GSO || - LastTunConf.GSOMaxSize != tunConf.GSOMaxSize || - LastTunConf.IPRoute2TableIndex != tunConf.IPRoute2TableIndex || - LastTunConf.IPRoute2RuleIndex != tunConf.IPRoute2RuleIndex || - LastTunConf.AutoRedirect != tunConf.AutoRedirect || - LastTunConf.AutoRedirectInputMark != tunConf.AutoRedirectInputMark || - LastTunConf.AutoRedirectOutputMark != tunConf.AutoRedirectOutputMark || - LastTunConf.StrictRoute != tunConf.StrictRoute || - LastTunConf.EndpointIndependentNat != tunConf.EndpointIndependentNat || - LastTunConf.UDPTimeout != tunConf.UDPTimeout || - LastTunConf.FileDescriptor != tunConf.FileDescriptor { - return true - } - - if len(LastTunConf.DNSHijack) != len(tunConf.DNSHijack) { - return true - } - - sort.Slice(tunConf.DNSHijack, func(i, j int) bool { - return tunConf.DNSHijack[i] < tunConf.DNSHijack[j] - }) - - sort.Slice(tunConf.RouteAddress, func(i, j int) bool { - return tunConf.RouteAddress[i].String() < tunConf.RouteAddress[j].String() - }) - - sort.Slice(tunConf.RouteAddressSet, func(i, j int) bool { - return tunConf.RouteAddressSet[i] < tunConf.RouteAddressSet[j] - }) - - sort.Slice(tunConf.RouteExcludeAddress, func(i, j int) bool { - return tunConf.RouteExcludeAddress[i].String() < tunConf.RouteExcludeAddress[j].String() - }) - - sort.Slice(tunConf.RouteExcludeAddressSet, func(i, j int) bool { - return tunConf.RouteExcludeAddressSet[i] < tunConf.RouteExcludeAddressSet[j] - }) - - sort.Slice(tunConf.Inet4Address, func(i, j int) bool { - return tunConf.Inet4Address[i].String() < tunConf.Inet4Address[j].String() - }) - - sort.Slice(tunConf.Inet6Address, func(i, j int) bool { - return tunConf.Inet6Address[i].String() < tunConf.Inet6Address[j].String() - }) - - sort.Slice(tunConf.Inet4RouteAddress, func(i, j int) bool { - return tunConf.Inet4RouteAddress[i].String() < tunConf.Inet4RouteAddress[j].String() - }) - - sort.Slice(tunConf.Inet6RouteAddress, func(i, j int) bool { - return tunConf.Inet6RouteAddress[i].String() < tunConf.Inet6RouteAddress[j].String() - }) - - sort.Slice(tunConf.Inet4RouteExcludeAddress, func(i, j int) bool { - return tunConf.Inet4RouteExcludeAddress[i].String() < tunConf.Inet4RouteExcludeAddress[j].String() - }) - - sort.Slice(tunConf.Inet6RouteExcludeAddress, func(i, j int) bool { - return tunConf.Inet6RouteExcludeAddress[i].String() < tunConf.Inet6RouteExcludeAddress[j].String() - }) - - sort.Slice(tunConf.IncludeInterface, func(i, j int) bool { - return tunConf.IncludeInterface[i] < tunConf.IncludeInterface[j] - }) - - sort.Slice(tunConf.ExcludeInterface, func(i, j int) bool { - return tunConf.ExcludeInterface[i] < tunConf.ExcludeInterface[j] - }) - - sort.Slice(tunConf.IncludeUID, func(i, j int) bool { - return tunConf.IncludeUID[i] < tunConf.IncludeUID[j] - }) - - sort.Slice(tunConf.IncludeUIDRange, func(i, j int) bool { - return tunConf.IncludeUIDRange[i] < tunConf.IncludeUIDRange[j] - }) - - sort.Slice(tunConf.ExcludeUID, func(i, j int) bool { - return tunConf.ExcludeUID[i] < tunConf.ExcludeUID[j] - }) - - sort.Slice(tunConf.ExcludeUIDRange, func(i, j int) bool { - return tunConf.ExcludeUIDRange[i] < tunConf.ExcludeUIDRange[j] - }) - - sort.Slice(tunConf.IncludeAndroidUser, func(i, j int) bool { - return tunConf.IncludeAndroidUser[i] < tunConf.IncludeAndroidUser[j] - }) - - sort.Slice(tunConf.IncludePackage, func(i, j int) bool { - return tunConf.IncludePackage[i] < tunConf.IncludePackage[j] - }) - - sort.Slice(tunConf.ExcludePackage, func(i, j int) bool { - return tunConf.ExcludePackage[i] < tunConf.ExcludePackage[j] - }) - - if !slices.Equal(tunConf.DNSHijack, LastTunConf.DNSHijack) || - !slices.Equal(tunConf.RouteAddress, LastTunConf.RouteAddress) || - !slices.Equal(tunConf.RouteAddressSet, LastTunConf.RouteAddressSet) || - !slices.Equal(tunConf.RouteExcludeAddress, LastTunConf.RouteExcludeAddress) || - !slices.Equal(tunConf.RouteExcludeAddressSet, LastTunConf.RouteExcludeAddressSet) || - !slices.Equal(tunConf.Inet4Address, LastTunConf.Inet4Address) || - !slices.Equal(tunConf.Inet6Address, LastTunConf.Inet6Address) || - !slices.Equal(tunConf.Inet4RouteAddress, LastTunConf.Inet4RouteAddress) || - !slices.Equal(tunConf.Inet6RouteAddress, LastTunConf.Inet6RouteAddress) || - !slices.Equal(tunConf.Inet4RouteExcludeAddress, LastTunConf.Inet4RouteExcludeAddress) || - !slices.Equal(tunConf.Inet6RouteExcludeAddress, LastTunConf.Inet6RouteExcludeAddress) || - !slices.Equal(tunConf.IncludeInterface, LastTunConf.IncludeInterface) || - !slices.Equal(tunConf.ExcludeInterface, LastTunConf.ExcludeInterface) || - !slices.Equal(tunConf.IncludeUID, LastTunConf.IncludeUID) || - !slices.Equal(tunConf.IncludeUIDRange, LastTunConf.IncludeUIDRange) || - !slices.Equal(tunConf.ExcludeUID, LastTunConf.ExcludeUID) || - !slices.Equal(tunConf.ExcludeUIDRange, LastTunConf.ExcludeUIDRange) || - !slices.Equal(tunConf.IncludeAndroidUser, LastTunConf.IncludeAndroidUser) || - !slices.Equal(tunConf.IncludePackage, LastTunConf.IncludePackage) || - !slices.Equal(tunConf.ExcludePackage, LastTunConf.ExcludePackage) { - return true - } - - return false -} - func closeTunListener() { if tunLister != nil { tunLister.Close() From 53425bb9f2e34c4fd0ac0438d4b6edc7622cdb6c Mon Sep 17 00:00:00 2001 From: karin0 Date: Sat, 24 Aug 2024 19:10:09 +0900 Subject: [PATCH 181/311] chore: add `json` struct tags for more fields in `config.RawConfig` (#1469) Applications like CMFA marshall their config overrides in JSON, so `json` tags are required for parsing configurations of features such as external controllers. --- config/config.go | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/config/config.go b/config/config.go index 788f227b..5208d835 100644 --- a/config/config.go +++ b/config/config.go @@ -296,10 +296,10 @@ type RawConfig struct { RedirPort int `yaml:"redir-port" json:"redir-port"` TProxyPort int `yaml:"tproxy-port" json:"tproxy-port"` MixedPort int `yaml:"mixed-port" json:"mixed-port"` - ShadowSocksConfig string `yaml:"ss-config"` - VmessConfig string `yaml:"vmess-config"` - InboundTfo bool `yaml:"inbound-tfo"` - InboundMPTCP bool `yaml:"inbound-mptcp"` + ShadowSocksConfig string `yaml:"ss-config" json:"ss-config"` + VmessConfig string `yaml:"vmess-config" json:"vmess-config"` + InboundTfo bool `yaml:"inbound-tfo" json:"inbound-tfo"` + InboundMPTCP bool `yaml:"inbound-mptcp" json:"inbound-mptcp"` Authentication []string `yaml:"authentication" json:"authentication"` SkipAuthPrefixes []netip.Prefix `yaml:"skip-auth-prefixes"` LanAllowedIPs []netip.Prefix `yaml:"lan-allowed-ips"` @@ -310,17 +310,17 @@ type RawConfig struct { UnifiedDelay bool `yaml:"unified-delay" json:"unified-delay"` LogLevel log.LogLevel `yaml:"log-level" json:"log-level"` IPv6 bool `yaml:"ipv6" json:"ipv6"` - ExternalController string `yaml:"external-controller"` - ExternalControllerUnix string `yaml:"external-controller-unix"` - ExternalControllerTLS string `yaml:"external-controller-tls"` - ExternalUI string `yaml:"external-ui"` + ExternalController string `yaml:"external-controller" json:"external-controller"` + ExternalControllerUnix string `yaml:"external-controller-unix" json:"external-controller-unix"` + ExternalControllerTLS string `yaml:"external-controller-tls" json:"external-controller-tls"` + ExternalUI string `yaml:"external-ui" json:"external-ui"` ExternalUIURL string `yaml:"external-ui-url" json:"external-ui-url"` ExternalUIName string `yaml:"external-ui-name" json:"external-ui-name"` - ExternalDohServer string `yaml:"external-doh-server"` - Secret string `yaml:"secret"` - Interface string `yaml:"interface-name"` - RoutingMark int `yaml:"routing-mark"` - Tunnels []LC.Tunnel `yaml:"tunnels"` + ExternalDohServer string `yaml:"external-doh-server" json:"external-doh-server"` + Secret string `yaml:"secret" json:"secret"` + Interface string `yaml:"interface-name" json:"interface-name"` + RoutingMark int `yaml:"routing-mark" json:"routing-mark"` + Tunnels []LC.Tunnel `yaml:"tunnels" json:"tunnels"` GeoAutoUpdate bool `yaml:"geo-auto-update" json:"geo-auto-update"` GeoUpdateInterval int `yaml:"geo-update-interval" json:"geo-update-interval"` GeodataMode bool `yaml:"geodata-mode" json:"geodata-mode"` @@ -328,11 +328,11 @@ type RawConfig struct { GeositeMatcher string `yaml:"geosite-matcher" json:"geosite-matcher"` 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"` - KeepAliveIdle int `yaml:"keep-alive-idle"` - KeepAliveInterval int `yaml:"keep-alive-interval"` - DisableKeepAlive bool `yaml:"disable-keep-alive"` + GlobalClientFingerprint string `yaml:"global-client-fingerprint" json:"global-client-fingerprint"` + GlobalUA string `yaml:"global-ua" json:"global-ua"` + KeepAliveIdle int `yaml:"keep-alive-idle" json:"keep-alive-idle"` + KeepAliveInterval int `yaml:"keep-alive-interval" json:"keep-alive-interval"` + DisableKeepAlive bool `yaml:"disable-keep-alive" json:"disable-keep-alive"` Sniffer RawSniffer `yaml:"sniffer" json:"sniffer"` ProxyProvider map[string]map[string]any `yaml:"proxy-providers"` From 27bcb26ecdb1828e042047e154f2083748a90d23 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 24 Aug 2024 20:49:12 +0800 Subject: [PATCH 182/311] chore: better config internal structure --- adapter/inbound/listen.go | 8 + adapter/inbound/mptcp_go120.go | 4 + adapter/inbound/mptcp_go121.go | 4 + config/config.go | 493 ++++++++++++++++++++------------- hub/executor/executor.go | 47 ++-- hub/hub.go | 22 +- hub/route/configs.go | 18 +- listener/config/tun.go | 18 +- listener/inbound/tun.go | 32 +-- tunnel/tunnel.go | 4 + 10 files changed, 393 insertions(+), 257 deletions(-) diff --git a/adapter/inbound/listen.go b/adapter/inbound/listen.go index 18dc1bc2..1b86c811 100644 --- a/adapter/inbound/listen.go +++ b/adapter/inbound/listen.go @@ -17,10 +17,18 @@ func SetTfo(open bool) { lc.DisableTFO = !open } +func Tfo() bool { + return !lc.DisableTFO +} + func SetMPTCP(open bool) { setMultiPathTCP(&lc.ListenConfig, open) } +func MPTCP() bool { + return getMultiPathTCP(&lc.ListenConfig) +} + func ListenContext(ctx context.Context, network, address string) (net.Listener, error) { return lc.Listen(ctx, network, address) } diff --git a/adapter/inbound/mptcp_go120.go b/adapter/inbound/mptcp_go120.go index f9b22533..faae6ec5 100644 --- a/adapter/inbound/mptcp_go120.go +++ b/adapter/inbound/mptcp_go120.go @@ -8,3 +8,7 @@ const multipathTCPAvailable = false func setMultiPathTCP(listenConfig *net.ListenConfig, open bool) { } + +func getMultiPathTCP(listenConfig *net.ListenConfig) bool { + return false +} diff --git a/adapter/inbound/mptcp_go121.go b/adapter/inbound/mptcp_go121.go index 6b35d1a8..9163878a 100644 --- a/adapter/inbound/mptcp_go121.go +++ b/adapter/inbound/mptcp_go121.go @@ -9,3 +9,7 @@ const multipathTCPAvailable = true func setMultiPathTCP(listenConfig *net.ListenConfig, open bool) { listenConfig.SetMultipathTCP(open) } + +func getMultiPathTCP(listenConfig *net.ListenConfig) bool { + return listenConfig.MultipathTCP() +} diff --git a/config/config.go b/config/config.go index 5208d835..f1ffbce1 100644 --- a/config/config.go +++ b/config/config.go @@ -50,13 +50,12 @@ import ( // General config type General struct { Inbound - Controller - Mode T.TunnelMode `json:"mode"` - UnifiedDelay bool + Mode T.TunnelMode `json:"mode"` + UnifiedDelay bool `json:"unified-delay"` LogLevel log.LogLevel `json:"log-level"` IPv6 bool `json:"ipv6"` Interface string `json:"interface-name"` - RoutingMark int `json:"-"` + RoutingMark int `json:"routing-mark"` GeoXUrl GeoXUrl `json:"geox-url"` GeoAutoUpdate bool `json:"geo-auto-update"` GeoUpdateInterval int `json:"geo-update-interval"` @@ -91,24 +90,48 @@ type Inbound struct { InboundMPTCP bool `json:"inbound-mptcp"` } +// GeoXUrl config +type GeoXUrl struct { + GeoIp string `json:"geo-ip"` + Mmdb string `json:"mmdb"` + ASN string `json:"asn"` + GeoSite string `json:"geo-site"` +} + // Controller config type Controller struct { - ExternalController string `json:"-"` - ExternalControllerTLS string `json:"-"` - ExternalControllerUnix string `json:"-"` - ExternalUI string `json:"-"` - ExternalDohServer string `json:"-"` - Secret string `json:"-"` + ExternalController string + ExternalControllerTLS string + ExternalControllerUnix string + ExternalUI string + ExternalDohServer string + Secret string +} + +// Experimental config +type Experimental struct { + Fingerprints []string + QUICGoDisableGSO bool + QUICGoDisableECN bool + IP4PEnable bool +} + +// IPTables config +type IPTables struct { + Enable bool + InboundInterface string + Bypass []string + DnsRedirect bool } // NTP config type NTP struct { - Enable bool `yaml:"enable"` - Server string `yaml:"server"` - Port int `yaml:"port"` - Interval int `yaml:"interval"` - DialerProxy string `yaml:"dialer-proxy"` - WriteToSystem bool `yaml:"write-to-system"` + Enable bool + Server string + Port int + Interval int + DialerProxy string + WriteToSystem bool } // DNS config @@ -134,24 +157,11 @@ type DNS struct { // Profile config type Profile struct { - StoreSelected bool `yaml:"store-selected"` - StoreFakeIP bool `yaml:"store-fake-ip"` -} - -type TLS struct { - Certificate string `yaml:"certificate"` - PrivateKey string `yaml:"private-key"` - CustomTrustCert []string `yaml:"custom-certifactes"` -} - -// IPTables config -type IPTables struct { - Enable bool `yaml:"enable" json:"enable"` - InboundInterface string `yaml:"inbound-interface" json:"inbound-interface"` - Bypass []string `yaml:"bypass" json:"bypass"` - DnsRedirect bool `yaml:"dns-redirect" json:"dns-redirect"` + StoreSelected bool + StoreFakeIP bool } +// Sniffer config type Sniffer struct { Enable bool Sniffers map[snifferTypes.Type]SNIFF.SnifferConfig @@ -161,21 +171,21 @@ type Sniffer struct { ParsePureIp bool } -// Experimental config -type Experimental struct { - Fingerprints []string `yaml:"fingerprints"` - QUICGoDisableGSO bool `yaml:"quic-go-disable-gso"` - QUICGoDisableECN bool `yaml:"quic-go-disable-ecn"` - IP4PEnable bool `yaml:"dialer-ip4p-convert"` +// TLS config +type TLS struct { + Certificate string + PrivateKey string + CustomTrustCert []string } // Config is mihomo config manager type Config struct { General *General + Controller *Controller + Experimental *Experimental IPTables *IPTables NTP *NTP DNS *DNS - Experimental *Experimental Hosts *trie.DomainTrie[resolver.HostValue] Profile *Profile Rules []C.Rule @@ -190,15 +200,6 @@ 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"` - DialerProxy string `yaml:"dialer-proxy"` - WriteToSystem bool `yaml:"write-to-system"` -} - type RawDNS struct { Enable bool `yaml:"enable" json:"enable"` PreferH3 bool `yaml:"prefer-h3" json:"prefer-h3"` @@ -233,6 +234,15 @@ type RawClashForAndroid struct { UiSubtitlePattern string `yaml:"ui-subtitle-pattern" json:"ui-subtitle-pattern"` } +type RawNTP struct { + Enable bool `yaml:"enable" json:"enable"` + Server string `yaml:"server" json:"server"` + Port int `yaml:"port" json:"port"` + Interval int `yaml:"interval" json:"interval"` + DialerProxy string `yaml:"dialer-proxy" json:"dialer-proxy"` + WriteToSystem bool `yaml:"write-to-system" json:"write-to-system"` +} + type RawTun struct { Enable bool `yaml:"enable" json:"enable"` Device string `yaml:"device" json:"device"` @@ -244,35 +254,35 @@ type RawTun struct { MTU uint32 `yaml:"mtu" json:"mtu,omitempty"` GSO bool `yaml:"gso" json:"gso,omitempty"` GSOMaxSize uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"` - //Inet4Address []netip.Prefix `yaml:"inet4-address" json:"inet4_address,omitempty"` - Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6_address,omitempty"` - IPRoute2TableIndex int `yaml:"iproute2-table-index" json:"iproute2_table_index,omitempty"` - IPRoute2RuleIndex int `yaml:"iproute2-rule-index" json:"iproute2_rule_index,omitempty"` - AutoRedirect bool `yaml:"auto-redirect" json:"auto_redirect,omitempty"` - AutoRedirectInputMark uint32 `yaml:"auto-redirect-input-mark" json:"auto_redirect_input_mark,omitempty"` - AutoRedirectOutputMark uint32 `yaml:"auto-redirect-output-mark" json:"auto_redirect_output_mark,omitempty"` - StrictRoute bool `yaml:"strict-route" json:"strict_route,omitempty"` - RouteAddress []netip.Prefix `yaml:"route-address" json:"route_address,omitempty"` - RouteAddressSet []string `yaml:"route-address-set" json:"route_address_set,omitempty"` - RouteExcludeAddress []netip.Prefix `yaml:"route-exclude-address" json:"route_exclude_address,omitempty"` - RouteExcludeAddressSet []string `yaml:"route-exclude-address-set" json:"route_exclude_address_set,omitempty"` + //Inet4Address []netip.Prefix `yaml:"inet4-address" json:"inet4-address,omitempty"` + Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6-address,omitempty"` + IPRoute2TableIndex int `yaml:"iproute2-table-index" json:"iproute2-table-index,omitempty"` + IPRoute2RuleIndex int `yaml:"iproute2-rule-index" json:"iproute2-rule-index,omitempty"` + AutoRedirect bool `yaml:"auto-redirect" json:"auto-redirect,omitempty"` + AutoRedirectInputMark uint32 `yaml:"auto-redirect-input-mark" json:"auto-redirect-input-mark,omitempty"` + AutoRedirectOutputMark uint32 `yaml:"auto-redirect-output-mark" json:"auto-redirect-output-mark,omitempty"` + StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"` + RouteAddress []netip.Prefix `yaml:"route-address" json:"route-address,omitempty"` + RouteAddressSet []string `yaml:"route-address-set" json:"route-address-set,omitempty"` + RouteExcludeAddress []netip.Prefix `yaml:"route-exclude-address" json:"route-exclude-address,omitempty"` + RouteExcludeAddressSet []string `yaml:"route-exclude-address-set" json:"route-exclude-address-set,omitempty"` IncludeInterface []string `yaml:"include-interface" json:"include-interface,omitempty"` ExcludeInterface []string `yaml:"exclude-interface" json:"exclude-interface,omitempty"` - IncludeUID []uint32 `yaml:"include-uid" json:"include_uid,omitempty"` - IncludeUIDRange []string `yaml:"include-uid-range" json:"include_uid_range,omitempty"` - ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"` - ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude_uid_range,omitempty"` - IncludeAndroidUser []int `yaml:"include-android-user" json:"include_android_user,omitempty"` - IncludePackage []string `yaml:"include-package" json:"include_package,omitempty"` - ExcludePackage []string `yaml:"exclude-package" json:"exclude_package,omitempty"` - EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"` - UDPTimeout int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"` + IncludeUID []uint32 `yaml:"include-uid" json:"include-uid,omitempty"` + IncludeUIDRange []string `yaml:"include-uid-range" json:"include-uid-range,omitempty"` + ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"` + ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"` + IncludeAndroidUser []int `yaml:"include-android-user" json:"include-android-user,omitempty"` + IncludePackage []string `yaml:"include-package" json:"include-package,omitempty"` + ExcludePackage []string `yaml:"exclude-package" json:"exclude-package,omitempty"` + EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"` + UDPTimeout int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"` FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"` - Inet4RouteAddress []netip.Prefix `yaml:"inet4-route-address" json:"inet4_route_address,omitempty"` - Inet6RouteAddress []netip.Prefix `yaml:"inet6-route-address" json:"inet6_route_address,omitempty"` - Inet4RouteExcludeAddress []netip.Prefix `yaml:"inet4-route-exclude-address" json:"inet4_route_exclude_address,omitempty"` - Inet6RouteExcludeAddress []netip.Prefix `yaml:"inet6-route-exclude-address" json:"inet6_route_exclude_address,omitempty"` + Inet4RouteAddress []netip.Prefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"` + Inet6RouteAddress []netip.Prefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"` + Inet4RouteExcludeAddress []netip.Prefix `yaml:"inet4-route-exclude-address" json:"inet4-route-exclude-address,omitempty"` + Inet6RouteExcludeAddress []netip.Prefix `yaml:"inet6-route-exclude-address" json:"inet6-route-exclude-address,omitempty"` } type RawTuicServer struct { @@ -290,6 +300,55 @@ type RawTuicServer struct { CWND int `yaml:"cwnd" json:"cwnd,omitempty"` } +type RawIPTables struct { + Enable bool `yaml:"enable" json:"enable"` + InboundInterface string `yaml:"inbound-interface" json:"inbound-interface"` + Bypass []string `yaml:"bypass" json:"bypass"` + DnsRedirect bool `yaml:"dns-redirect" json:"dns-redirect"` +} + +type RawExperimental struct { + Fingerprints []string `yaml:"fingerprints"` + QUICGoDisableGSO bool `yaml:"quic-go-disable-gso"` + QUICGoDisableECN bool `yaml:"quic-go-disable-ecn"` + IP4PEnable bool `yaml:"dialer-ip4p-convert"` +} + +type RawProfile struct { + StoreSelected bool `yaml:"store-selected" json:"store-selected"` + StoreFakeIP bool `yaml:"store-fake-ip" json:"store-fake-ip"` +} + +type RawGeoXUrl struct { + GeoIp string `yaml:"geoip" json:"geoip"` + Mmdb string `yaml:"mmdb" json:"mmdb"` + ASN string `yaml:"asn" json:"asn"` + GeoSite string `yaml:"geosite" json:"geosite"` +} + +type RawSniffer struct { + Enable bool `yaml:"enable" json:"enable"` + OverrideDest bool `yaml:"override-destination" json:"override-destination"` + Sniffing []string `yaml:"sniffing" json:"sniffing"` + ForceDomain []string `yaml:"force-domain" json:"force-domain"` + SkipDomain []string `yaml:"skip-domain" json:"skip-domain"` + Ports []string `yaml:"port-whitelist" json:"port-whitelist"` + ForceDnsMapping bool `yaml:"force-dns-mapping" json:"force-dns-mapping"` + ParsePureIp bool `yaml:"parse-pure-ip" json:"parse-pure-ip"` + Sniff map[string]RawSniffingConfig `yaml:"sniff" json:"sniff"` +} + +type RawSniffingConfig struct { + Ports []string `yaml:"ports" json:"ports"` + OverrideDest *bool `yaml:"override-destination" json:"override-destination"` +} + +type RawTLS struct { + Certificate string `yaml:"certificate" json:"certificate"` + PrivateKey string `yaml:"private-key" json:"private-key"` + CustomTrustCert []string `yaml:"custom-certifactes" json:"custom-certifactes"` +} + type RawConfig struct { Port int `yaml:"port" json:"port"` SocksPort int `yaml:"socks-port" json:"socks-port"` @@ -301,9 +360,9 @@ type RawConfig struct { InboundTfo bool `yaml:"inbound-tfo" json:"inbound-tfo"` InboundMPTCP bool `yaml:"inbound-mptcp" json:"inbound-mptcp"` Authentication []string `yaml:"authentication" json:"authentication"` - SkipAuthPrefixes []netip.Prefix `yaml:"skip-auth-prefixes"` - LanAllowedIPs []netip.Prefix `yaml:"lan-allowed-ips"` - LanDisAllowedIPs []netip.Prefix `yaml:"lan-disallowed-ips"` + SkipAuthPrefixes []netip.Prefix `yaml:"skip-auth-prefixes" json:"skip-auth-prefixes"` + LanAllowedIPs []netip.Prefix `yaml:"lan-allowed-ips" json:"lan-allowed-ips"` + LanDisAllowedIPs []netip.Prefix `yaml:"lan-disallowed-ips" json:"lan-disallowed-ips"` AllowLan bool `yaml:"allow-lan" json:"allow-lan"` BindAddress string `yaml:"bind-address" json:"bind-address"` Mode T.TunnelMode `yaml:"mode" json:"mode"` @@ -334,52 +393,28 @@ type RawConfig struct { KeepAliveInterval int `yaml:"keep-alive-interval" json:"keep-alive-interval"` DisableKeepAlive bool `yaml:"disable-keep-alive" json:"disable-keep-alive"` - Sniffer RawSniffer `yaml:"sniffer" json:"sniffer"` - ProxyProvider map[string]map[string]any `yaml:"proxy-providers"` - RuleProvider map[string]map[string]any `yaml:"rule-providers"` + ProxyProvider map[string]map[string]any `yaml:"proxy-providers" json:"proxy-providers"` + RuleProvider map[string]map[string]any `yaml:"rule-providers" json:"rule-providers"` + Proxy []map[string]any `yaml:"proxies" json:"proxies"` + ProxyGroup []map[string]any `yaml:"proxy-groups" json:"proxy-groups"` + Rule []string `yaml:"rules" json:"rule"` + SubRules map[string][]string `yaml:"sub-rules" json:"sub-rules"` + Listeners []map[string]any `yaml:"listeners" json:"listeners"` Hosts map[string]any `yaml:"hosts" json:"hosts"` - NTP RawNTP `yaml:"ntp" json:"ntp"` DNS RawDNS `yaml:"dns" json:"dns"` - Tun RawTun `yaml:"tun"` - TuicServer RawTuicServer `yaml:"tuic-server"` - IPTables IPTables `yaml:"iptables"` - Experimental Experimental `yaml:"experimental"` - Profile Profile `yaml:"profile"` - GeoXUrl GeoXUrl `yaml:"geox-url"` - Proxy []map[string]any `yaml:"proxies"` - ProxyGroup []map[string]any `yaml:"proxy-groups"` - Rule []string `yaml:"rules"` - SubRules map[string][]string `yaml:"sub-rules"` - RawTLS TLS `yaml:"tls"` - Listeners []map[string]any `yaml:"listeners"` + NTP RawNTP `yaml:"ntp" json:"ntp"` + Tun RawTun `yaml:"tun" json:"tun"` + TuicServer RawTuicServer `yaml:"tuic-server" json:"tuic-server"` + IPTables RawIPTables `yaml:"iptables" json:"iptables"` + Experimental RawExperimental `yaml:"experimental" json:"experimental"` + Profile RawProfile `yaml:"profile" json:"profile"` + GeoXUrl RawGeoXUrl `yaml:"geox-url" json:"geox-url"` + Sniffer RawSniffer `yaml:"sniffer" json:"sniffer"` + TLS RawTLS `yaml:"tls" json:"tls"` ClashForAndroid RawClashForAndroid `yaml:"clash-for-android" json:"clash-for-android"` } -type GeoXUrl struct { - GeoIp string `yaml:"geoip" json:"geoip"` - Mmdb string `yaml:"mmdb" json:"mmdb"` - ASN string `yaml:"asn" json:"asn"` - GeoSite string `yaml:"geosite" json:"geosite"` -} - -type RawSniffer struct { - Enable bool `yaml:"enable" json:"enable"` - OverrideDest bool `yaml:"override-destination" json:"override-destination"` - Sniffing []string `yaml:"sniffing" json:"sniffing"` - ForceDomain []string `yaml:"force-domain" json:"force-domain"` - SkipDomain []string `yaml:"skip-domain" json:"skip-domain"` - Ports []string `yaml:"port-whitelist" json:"port-whitelist"` - ForceDnsMapping bool `yaml:"force-dns-mapping" json:"force-dns-mapping"` - ParsePureIp bool `yaml:"parse-pure-ip" json:"parse-pure-ip"` - Sniff map[string]RawSniffingConfig `yaml:"sniff" json:"sniff"` -} - -type RawSniffingConfig struct { - Ports []string `yaml:"ports" json:"ports"` - OverrideDest *bool `yaml:"override-destination" json:"override-destination"` -} - var ( GroupsList = list.New() ProxiesList = list.New() @@ -418,41 +453,6 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { TCPConcurrent: false, FindProcessMode: P.FindProcessStrict, GlobalUA: "clash.meta/" + C.Version, - Tun: RawTun{ - Enable: false, - Device: "", - Stack: C.TunGvisor, - DNSHijack: []string{"0.0.0.0:53"}, // default hijack all dns query - AutoRoute: true, - AutoDetectInterface: true, - Inet6Address: []netip.Prefix{netip.MustParsePrefix("fdfe:dcba:9876::1/126")}, - }, - TuicServer: RawTuicServer{ - Enable: false, - Token: nil, - Users: nil, - Certificate: "", - PrivateKey: "", - Listen: "", - CongestionController: "", - MaxIdleTime: 15000, - AuthenticationTimeout: 1000, - ALPN: []string{"h3"}, - MaxUdpRelayPacketSize: 1500, - }, - IPTables: IPTables{ - Enable: false, - InboundInterface: "lo", - Bypass: []string{}, - DnsRedirect: true, - }, - NTP: RawNTP{ - Enable: false, - WriteToSystem: false, - Server: "time.apple.com", - ServerPort: 123, - Interval: 30, - }, DNS: RawDNS{ Enable: false, IPv6: false, @@ -483,11 +483,55 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { "www.msftconnecttest.com", }, }, - Experimental: Experimental{ + NTP: RawNTP{ + Enable: false, + WriteToSystem: false, + Server: "time.apple.com", + Port: 123, + Interval: 30, + }, + Tun: RawTun{ + Enable: false, + Device: "", + Stack: C.TunGvisor, + DNSHijack: []string{"0.0.0.0:53"}, // default hijack all dns query + AutoRoute: true, + AutoDetectInterface: true, + Inet6Address: []netip.Prefix{netip.MustParsePrefix("fdfe:dcba:9876::1/126")}, + }, + TuicServer: RawTuicServer{ + Enable: false, + Token: nil, + Users: nil, + Certificate: "", + PrivateKey: "", + Listen: "", + CongestionController: "", + MaxIdleTime: 15000, + AuthenticationTimeout: 1000, + ALPN: []string{"h3"}, + MaxUdpRelayPacketSize: 1500, + }, + IPTables: RawIPTables{ + Enable: false, + InboundInterface: "lo", + Bypass: []string{}, + DnsRedirect: true, + }, + Experimental: RawExperimental{ // https://github.com/quic-go/quic-go/issues/4178 // Quic-go currently cannot automatically fall back on platforms that do not support ecn, so this feature is turned off by default. QUICGoDisableECN: true, }, + Profile: RawProfile{ + StoreSelected: true, + }, + GeoXUrl: RawGeoXUrl{ + Mmdb: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb", + ASN: "https://github.com/xishang0128/geoip/releases/download/latest/GeoLite2-ASN.mmdb", + GeoIp: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat", + GeoSite: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat", + }, Sniffer: RawSniffer{ Enable: false, Sniff: map[string]RawSniffingConfig{}, @@ -498,15 +542,6 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { ParsePureIp: true, OverrideDest: true, }, - Profile: Profile{ - StoreSelected: true, - }, - GeoXUrl: GeoXUrl{ - Mmdb: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb", - ASN: "https://github.com/xishang0128/geoip/releases/download/latest/GeoLite2-ASN.mmdb", - GeoIp: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat", - GeoSite: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat", - }, ExternalUIURL: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip", } @@ -521,10 +556,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { config := &Config{} log.Infoln("Start initial configuration in progress") //Segment finished in xxm startTime := time.Now() - config.Experimental = &rawCfg.Experimental - config.Profile = &rawCfg.Profile - config.IPTables = &rawCfg.IPTables - config.TLS = &rawCfg.RawTLS general, err := parseGeneral(rawCfg) if err != nil { @@ -537,6 +568,42 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { tlsC.SetGlobalUtlsClient(config.General.GlobalClientFingerprint) } + controller, err := parseController(rawCfg) + if err != nil { + return nil, err + } + config.Controller = controller + + experimental, err := parseExperimental(rawCfg) + if err != nil { + return nil, err + } + config.Experimental = experimental + + iptables, err := parseIPTables(rawCfg) + if err != nil { + return nil, err + } + config.IPTables = iptables + + ntpCfg, err := parseNTP(rawCfg) + if err != nil { + return nil, err + } + config.NTP = ntpCfg + + profile, err := parseProfile(rawCfg) + if err != nil { + return nil, err + } + config.Profile = profile + + tlsCfg, err := parseTLS(rawCfg) + if err != nil { + return nil, err + } + config.TLS = tlsCfg + proxies, providers, err := parseProxies(rawCfg) if err != nil { return nil, err @@ -576,9 +643,6 @@ 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 @@ -687,21 +751,18 @@ func parseGeneral(cfg *RawConfig) (*General, error) { InboundTfo: cfg.InboundTfo, InboundMPTCP: cfg.InboundMPTCP, }, - Controller: Controller{ - ExternalController: cfg.ExternalController, - ExternalUI: cfg.ExternalUI, - Secret: cfg.Secret, - ExternalControllerUnix: cfg.ExternalControllerUnix, - ExternalControllerTLS: cfg.ExternalControllerTLS, - ExternalDohServer: cfg.ExternalDohServer, + UnifiedDelay: cfg.UnifiedDelay, + Mode: cfg.Mode, + LogLevel: cfg.LogLevel, + IPv6: cfg.IPv6, + Interface: cfg.Interface, + RoutingMark: cfg.RoutingMark, + GeoXUrl: GeoXUrl{ + GeoIp: cfg.GeoXUrl.GeoIp, + Mmdb: cfg.GeoXUrl.Mmdb, + ASN: cfg.GeoXUrl.ASN, + GeoSite: cfg.GeoXUrl.GeoSite, }, - UnifiedDelay: cfg.UnifiedDelay, - Mode: cfg.Mode, - LogLevel: cfg.LogLevel, - IPv6: cfg.IPv6, - Interface: cfg.Interface, - RoutingMark: cfg.RoutingMark, - GeoXUrl: cfg.GeoXUrl, GeoAutoUpdate: cfg.GeoAutoUpdate, GeoUpdateInterval: cfg.GeoUpdateInterval, GeodataMode: cfg.GeodataMode, @@ -713,6 +774,61 @@ func parseGeneral(cfg *RawConfig) (*General, error) { }, nil } +func parseController(cfg *RawConfig) (*Controller, error) { + return &Controller{ + ExternalController: cfg.ExternalController, + ExternalUI: cfg.ExternalUI, + Secret: cfg.Secret, + ExternalControllerUnix: cfg.ExternalControllerUnix, + ExternalControllerTLS: cfg.ExternalControllerTLS, + ExternalDohServer: cfg.ExternalDohServer, + }, nil +} + +func parseExperimental(cfg *RawConfig) (*Experimental, error) { + return &Experimental{ + Fingerprints: cfg.Experimental.Fingerprints, + QUICGoDisableGSO: cfg.Experimental.QUICGoDisableGSO, + QUICGoDisableECN: cfg.Experimental.QUICGoDisableECN, + IP4PEnable: cfg.Experimental.IP4PEnable, + }, nil +} + +func parseIPTables(cfg *RawConfig) (*IPTables, error) { + return &IPTables{ + Enable: cfg.IPTables.Enable, + InboundInterface: cfg.IPTables.InboundInterface, + Bypass: cfg.IPTables.Bypass, + DnsRedirect: cfg.IPTables.DnsRedirect, + }, nil +} + +func parseNTP(cfg *RawConfig) (*NTP, error) { + return &NTP{ + Enable: cfg.NTP.Enable, + Server: cfg.NTP.Server, + Port: cfg.NTP.Port, + Interval: cfg.NTP.Interval, + DialerProxy: cfg.NTP.DialerProxy, + WriteToSystem: cfg.NTP.WriteToSystem, + }, nil +} + +func parseProfile(cfg *RawConfig) (*Profile, error) { + return &Profile{ + StoreSelected: cfg.Profile.StoreSelected, + StoreFakeIP: cfg.Profile.StoreFakeIP, + }, nil +} + +func parseTLS(cfg *RawConfig) (*TLS, error) { + return &TLS{ + Certificate: cfg.TLS.Certificate, + PrivateKey: cfg.TLS.PrivateKey, + CustomTrustCert: cfg.TLS.CustomTrustCert, + }, nil +} + func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[string]providerTypes.ProxyProvider, err error) { proxies = make(map[string]C.Proxy) providersMap = make(map[string]providerTypes.ProxyProvider) @@ -1259,19 +1375,6 @@ func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], rules [ return policy, nil } -func paresNTP(rawCfg *RawConfig) *NTP { - cfg := rawCfg.NTP - ntpCfg := &NTP{ - Enable: cfg.Enable, - Server: cfg.Server, - Port: cfg.ServerPort, - Interval: cfg.Interval, - DialerProxy: cfg.DialerProxy, - WriteToSystem: cfg.WriteToSystem, - } - 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/hub/executor/executor.go b/hub/executor/executor.go index 8eb54ac4..d54d55b7 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -22,6 +22,7 @@ import ( "github.com/metacubex/mihomo/component/profile/cachefile" "github.com/metacubex/mihomo/component/resolver" SNI "github.com/metacubex/mihomo/component/sniffer" + tlsC "github.com/metacubex/mihomo/component/tls" "github.com/metacubex/mihomo/component/trie" "github.com/metacubex/mihomo/component/updater" "github.com/metacubex/mihomo/config" @@ -90,7 +91,7 @@ func ApplyConfig(cfg *config.Config, force bool) { } } - updateExperimental(cfg) + updateExperimental(cfg.Experimental) updateUsers(cfg.Users) updateProxies(cfg.Proxies, cfg.Providers) updateRules(cfg.Rules, cfg.SubRules, cfg.RuleProviders) @@ -145,19 +146,31 @@ func GetGeneral() *config.General { LanDisAllowedIPs: inbound.DisAllowedIPs(), AllowLan: listener.AllowLan(), BindAddress: listener.BindAddress(), + InboundTfo: inbound.Tfo(), + InboundMPTCP: inbound.MPTCP(), }, - Controller: config.Controller{}, - Mode: tunnel.Mode(), - LogLevel: log.Level(), - IPv6: !resolver.DisableIPv6, - GeodataMode: G.GeodataMode(), - GeoAutoUpdate: G.GeoAutoUpdate(), - GeoUpdateInterval: G.GeoUpdateInterval(), - GeodataLoader: G.LoaderName(), - GeositeMatcher: G.SiteMatcherName(), - Interface: dialer.DefaultInterface.Load(), - Sniffing: tunnel.IsSniffing(), - TCPConcurrent: dialer.GetTcpConcurrent(), + Mode: tunnel.Mode(), + UnifiedDelay: adapter.UnifiedDelay.Load(), + LogLevel: log.Level(), + IPv6: !resolver.DisableIPv6, + Interface: dialer.DefaultInterface.Load(), + RoutingMark: int(dialer.DefaultRoutingMark.Load()), + GeoXUrl: config.GeoXUrl{ + GeoIp: C.GeoIpUrl, + Mmdb: C.MmdbUrl, + ASN: C.ASNUrl, + GeoSite: C.GeoSiteUrl, + }, + GeoAutoUpdate: G.GeoAutoUpdate(), + GeoUpdateInterval: G.GeoUpdateInterval(), + GeodataMode: G.GeodataMode(), + GeodataLoader: G.LoaderName(), + GeositeMatcher: G.SiteMatcherName(), + TCPConcurrent: dialer.GetTcpConcurrent(), + FindProcessMode: tunnel.FindProcessMode(), + Sniffing: tunnel.IsSniffing(), + GlobalClientFingerprint: tlsC.GetGlobalFingerprint(), + GlobalUA: C.UA, } return general @@ -188,14 +201,14 @@ func updateListeners(general *config.General, listeners map[string]C.InboundList listener.ReCreateTun(general.Tun, tunnel.Tunnel) } -func updateExperimental(c *config.Config) { - if c.Experimental.QUICGoDisableGSO { +func updateExperimental(c *config.Experimental) { + if c.QUICGoDisableGSO { _ = os.Setenv("QUIC_GO_DISABLE_GSO", strconv.FormatBool(true)) } - if c.Experimental.QUICGoDisableECN { + if c.QUICGoDisableECN { _ = os.Setenv("QUIC_GO_DISABLE_ECN", strconv.FormatBool(true)) } - dialer.GetIP4PEnable(c.Experimental.IP4PEnable) + dialer.GetIP4PEnable(c.IP4PEnable) } func updateNTP(c *config.NTP) { diff --git a/hub/hub.go b/hub/hub.go index 57c91aae..2a53b197 100644 --- a/hub/hub.go +++ b/hub/hub.go @@ -11,25 +11,25 @@ type Option func(*config.Config) func WithExternalUI(externalUI string) Option { return func(cfg *config.Config) { - cfg.General.ExternalUI = externalUI + cfg.Controller.ExternalUI = externalUI } } func WithExternalController(externalController string) Option { return func(cfg *config.Config) { - cfg.General.ExternalController = externalController + cfg.Controller.ExternalController = externalController } } func WithExternalControllerUnix(externalControllerUnix string) Option { return func(cfg *config.Config) { - cfg.General.ExternalControllerUnix = externalControllerUnix + cfg.Controller.ExternalControllerUnix = externalControllerUnix } } func WithSecret(secret string) Option { return func(cfg *config.Config) { - cfg.General.Secret = secret + cfg.Controller.Secret = secret } } @@ -44,18 +44,18 @@ func Parse(options ...Option) error { option(cfg) } - if cfg.General.ExternalUI != "" { - route.SetUIPath(cfg.General.ExternalUI) + if cfg.Controller.ExternalUI != "" { + route.SetUIPath(cfg.Controller.ExternalUI) } - if cfg.General.ExternalController != "" { - go route.Start(cfg.General.ExternalController, cfg.General.ExternalControllerTLS, - cfg.General.Secret, cfg.TLS.Certificate, cfg.TLS.PrivateKey, cfg.General.ExternalDohServer, + if cfg.Controller.ExternalController != "" { + go route.Start(cfg.Controller.ExternalController, cfg.Controller.ExternalControllerTLS, + cfg.Controller.Secret, cfg.TLS.Certificate, cfg.TLS.PrivateKey, cfg.Controller.ExternalDohServer, cfg.General.LogLevel == log.DEBUG) } - if cfg.General.ExternalControllerUnix != "" { - go route.StartUnix(cfg.General.ExternalControllerUnix, cfg.General.ExternalDohServer, cfg.General.LogLevel == log.DEBUG) + if cfg.Controller.ExternalControllerUnix != "" { + go route.StartUnix(cfg.Controller.ExternalControllerUnix, cfg.Controller.ExternalDohServer, cfg.General.LogLevel == log.DEBUG) } executor.ApplyConfig(cfg, true) diff --git a/hub/route/configs.go b/hub/route/configs.go index b31a607e..d4bda2bf 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -68,16 +68,16 @@ type tunSchema struct { GSOMaxSize *uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"` //Inet4Address *[]netip.Prefix `yaml:"inet4-address" json:"inet4-address,omitempty"` Inet6Address *[]netip.Prefix `yaml:"inet6-address" json:"inet6-address,omitempty"` - IPRoute2TableIndex *int `yaml:"iproute2-table-index" json:"iproute2_table_index,omitempty"` - IPRoute2RuleIndex *int `yaml:"iproute2-rule-index" json:"iproute2_rule_index,omitempty"` - AutoRedirect *bool `yaml:"auto-redirect" json:"auto_redirect,omitempty"` - AutoRedirectInputMark *uint32 `yaml:"auto-redirect-input-mark" json:"auto_redirect_input_mark,omitempty"` - AutoRedirectOutputMark *uint32 `yaml:"auto-redirect-output-mark" json:"auto_redirect_output_mark,omitempty"` + IPRoute2TableIndex *int `yaml:"iproute2-table-index" json:"iproute2-table-index,omitempty"` + IPRoute2RuleIndex *int `yaml:"iproute2-rule-index" json:"iproute2-rule-index,omitempty"` + AutoRedirect *bool `yaml:"auto-redirect" json:"auto-redirect,omitempty"` + AutoRedirectInputMark *uint32 `yaml:"auto-redirect-input-mark" json:"auto-redirect-input-mark,omitempty"` + AutoRedirectOutputMark *uint32 `yaml:"auto-redirect-output-mark" json:"auto-redirect-output-mark,omitempty"` StrictRoute *bool `yaml:"strict-route" json:"strict-route,omitempty"` - RouteAddress *[]netip.Prefix `yaml:"route-address" json:"route_address,omitempty"` - RouteAddressSet *[]string `yaml:"route-address-set" json:"route_address_set,omitempty"` - RouteExcludeAddress *[]netip.Prefix `yaml:"route-exclude-address" json:"route_exclude_address,omitempty"` - RouteExcludeAddressSet *[]string `yaml:"route-exclude-address-set" json:"route_exclude_address_set,omitempty"` + RouteAddress *[]netip.Prefix `yaml:"route-address" json:"route-address,omitempty"` + RouteAddressSet *[]string `yaml:"route-address-set" json:"route-address-set,omitempty"` + RouteExcludeAddress *[]netip.Prefix `yaml:"route-exclude-address" json:"route-exclude-address,omitempty"` + RouteExcludeAddressSet *[]string `yaml:"route-exclude-address-set" json:"route-exclude-address-set,omitempty"` IncludeInterface *[]string `yaml:"include-interface" json:"include-interface,omitempty"` ExcludeInterface *[]string `yaml:"exclude-interface" json:"exclude-interface,omitempty"` IncludeUID *[]uint32 `yaml:"include-uid" json:"include-uid,omitempty"` diff --git a/listener/config/tun.go b/listener/config/tun.go index 06dc53ef..3901bb1d 100644 --- a/listener/config/tun.go +++ b/listener/config/tun.go @@ -34,16 +34,16 @@ type Tun struct { GSOMaxSize uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"` Inet4Address []netip.Prefix `yaml:"inet4-address" json:"inet4-address,omitempty"` Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6-address,omitempty"` - IPRoute2TableIndex int `yaml:"iproute2-table-index" json:"iproute2_table_index,omitempty"` - IPRoute2RuleIndex int `yaml:"iproute2-rule-index" json:"iproute2_rule_index,omitempty"` - AutoRedirect bool `yaml:"auto-redirect" json:"auto_redirect,omitempty"` - AutoRedirectInputMark uint32 `yaml:"auto-redirect-input-mark" json:"auto_redirect_input_mark,omitempty"` - AutoRedirectOutputMark uint32 `yaml:"auto-redirect-output-mark" json:"auto_redirect_output_mark,omitempty"` + IPRoute2TableIndex int `yaml:"iproute2-table-index" json:"iproute2-table-index,omitempty"` + IPRoute2RuleIndex int `yaml:"iproute2-rule-index" json:"iproute2-rule-index,omitempty"` + AutoRedirect bool `yaml:"auto-redirect" json:"auto-redirect,omitempty"` + AutoRedirectInputMark uint32 `yaml:"auto-redirect-input-mark" json:"auto-redirect-input-mark,omitempty"` + AutoRedirectOutputMark uint32 `yaml:"auto-redirect-output-mark" json:"auto-redirect-output-mark,omitempty"` StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"` - RouteAddress []netip.Prefix `yaml:"route-address" json:"route_address,omitempty"` - RouteAddressSet []string `yaml:"route-address-set" json:"route_address_set,omitempty"` - RouteExcludeAddress []netip.Prefix `yaml:"route-exclude-address" json:"route_exclude_address,omitempty"` - RouteExcludeAddressSet []string `yaml:"route-exclude-address-set" json:"route_exclude_address_set,omitempty"` + RouteAddress []netip.Prefix `yaml:"route-address" json:"route-address,omitempty"` + RouteAddressSet []string `yaml:"route-address-set" json:"route-address-set,omitempty"` + RouteExcludeAddress []netip.Prefix `yaml:"route-exclude-address" json:"route-exclude-address,omitempty"` + RouteExcludeAddressSet []string `yaml:"route-exclude-address-set" json:"route-exclude-address-set,omitempty"` IncludeInterface []string `yaml:"include-interface" json:"include-interface,omitempty"` ExcludeInterface []string `yaml:"exclude-interface" json:"exclude-interface,omitempty"` IncludeUID []uint32 `yaml:"include-uid" json:"include-uid,omitempty"` diff --git a/listener/inbound/tun.go b/listener/inbound/tun.go index a950f80d..77ad6bd6 100644 --- a/listener/inbound/tun.go +++ b/listener/inbound/tun.go @@ -21,35 +21,35 @@ type TunOption struct { MTU uint32 `inbound:"mtu,omitempty"` GSO bool `inbound:"gso,omitempty"` GSOMaxSize uint32 `inbound:"gso-max-size,omitempty"` - Inet4Address []string `inbound:"inet4_address,omitempty"` - Inet6Address []string `inbound:"inet6_address,omitempty"` + Inet4Address []string `inbound:"inet4-address,omitempty"` + Inet6Address []string `inbound:"inet6-address,omitempty"` IPRoute2TableIndex int `inbound:"iproute2-table-index"` IPRoute2RuleIndex int `inbound:"iproute2-rule-index"` AutoRedirect bool `inbound:"auto-redirect"` AutoRedirectInputMark uint32 `inbound:"auto-redirect-input-mark"` AutoRedirectOutputMark uint32 `inbound:"auto-redirect-output-mark"` - StrictRoute bool `inbound:"strict_route,omitempty"` + StrictRoute bool `inbound:"strict-route,omitempty"` RouteAddress []string `inbound:"route-address"` RouteAddressSet []string `inbound:"route-address-set"` RouteExcludeAddress []string `inbound:"route-exclude-address"` RouteExcludeAddressSet []string `inbound:"route-exclude-address-set"` IncludeInterface []string `inbound:"include-interface,omitempty"` ExcludeInterface []string `inbound:"exclude-interface"` - IncludeUID []uint32 `inbound:"include_uid,omitempty"` - IncludeUIDRange []string `inbound:"include_uid_range,omitempty"` - ExcludeUID []uint32 `inbound:"exclude_uid,omitempty"` - ExcludeUIDRange []string `inbound:"exclude_uid_range,omitempty"` - IncludeAndroidUser []int `inbound:"include_android_user,omitempty"` - IncludePackage []string `inbound:"include_package,omitempty"` - ExcludePackage []string `inbound:"exclude_package,omitempty"` - EndpointIndependentNat bool `inbound:"endpoint_independent_nat,omitempty"` - UDPTimeout int64 `inbound:"udp_timeout,omitempty"` + IncludeUID []uint32 `inbound:"include-uid,omitempty"` + IncludeUIDRange []string `inbound:"include-uid-range,omitempty"` + ExcludeUID []uint32 `inbound:"exclude-uid,omitempty"` + ExcludeUIDRange []string `inbound:"exclude-uid-range,omitempty"` + IncludeAndroidUser []int `inbound:"include-android-user,omitempty"` + IncludePackage []string `inbound:"include-package,omitempty"` + ExcludePackage []string `inbound:"exclude-package,omitempty"` + EndpointIndependentNat bool `inbound:"endpoint-independent-nat,omitempty"` + UDPTimeout int64 `inbound:"udp-timeout,omitempty"` FileDescriptor int `inbound:"file-descriptor,omitempty"` - Inet4RouteAddress []string `inbound:"inet4_route_address,omitempty"` - Inet6RouteAddress []string `inbound:"inet6_route_address,omitempty"` - Inet4RouteExcludeAddress []string `inbound:"inet4_route_exclude_address,omitempty"` - Inet6RouteExcludeAddress []string `inbound:"inet6_route_exclude_address,omitempty"` + Inet4RouteAddress []string `inbound:"inet4-route-address,omitempty"` + Inet6RouteAddress []string `inbound:"inet6-route-address,omitempty"` + Inet4RouteExcludeAddress []string `inbound:"inet4-route-exclude-address,omitempty"` + Inet6RouteExcludeAddress []string `inbound:"inet6-route-exclude-address,omitempty"` } func (o TunOption) Equal(config C.InboundConfig) bool { diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 5dd468f3..b1b417be 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -225,6 +225,10 @@ func SetMode(m TunnelMode) { mode = m } +func FindProcessMode() P.FindProcessMode { + return findProcessMode +} + // SetFindProcessMode replace SetAlwaysFindProcess // always find process info if legacyAlways = true or mode.Always() = true, may be increase many memory func SetFindProcessMode(mode P.FindProcessMode) { From 518e9bdb0b5b46c5699365a7f3485e42547e85e2 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 25 Aug 2024 19:26:06 +0800 Subject: [PATCH 183/311] feat: socks5, http and mixed listeners support independence `users` --- docs/config.yaml | 9 +++++++++ listener/auth/auth.go | 2 ++ listener/http/proxy.go | 6 ++---- listener/http/server.go | 17 +++++++++++------ listener/inbound/auth.go | 31 +++++++++++++++++++++++++++++++ listener/inbound/http.go | 3 ++- listener/inbound/mixed.go | 5 +++-- listener/inbound/socks.go | 5 +++-- listener/mixed/mixed.go | 19 ++++++++++++++----- listener/socks/tcp.go | 31 +++++++++++++++++-------------- 10 files changed, 94 insertions(+), 34 deletions(-) create mode 100644 listener/inbound/auth.go diff --git a/docs/config.yaml b/docs/config.yaml index 647cf14c..b67880f7 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -1005,6 +1005,9 @@ listeners: # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 # udp: false # 默认 true + # users: # 如果不填写users项,则遵从全局authentication设置,如果填写会忽略全局设置, 如想跳过该入站的验证可填写 users: [] + # - username: aaa + # password: aaa - name: http-in-1 type: http @@ -1012,6 +1015,9 @@ listeners: listen: 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错) + # users: # 如果不填写users项,则遵从全局authentication设置,如果填写会忽略全局设置, 如想跳过该入站的验证可填写 users: [] + # - username: aaa + # password: aaa - name: mixed-in-1 type: mixed # HTTP(S) 和 SOCKS 代理混合 @@ -1020,6 +1026,9 @@ listeners: # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错) # udp: false # 默认 true + # users: # 如果不填写users项,则遵从全局authentication设置,如果填写会忽略全局设置, 如想跳过该入站的验证可填写 users: [] + # - username: aaa + # password: aaa - name: reidr-in-1 type: redir diff --git a/listener/auth/auth.go b/listener/auth/auth.go index 46f552b8..772be3bd 100644 --- a/listener/auth/auth.go +++ b/listener/auth/auth.go @@ -13,3 +13,5 @@ func Authenticator() auth.Authenticator { func SetAuthenticator(au auth.Authenticator) { authenticator = au } + +func Nil() auth.Authenticator { return nil } diff --git a/listener/http/proxy.go b/listener/http/proxy.go index b2f312a5..04ab98eb 100644 --- a/listener/http/proxy.go +++ b/listener/http/proxy.go @@ -30,7 +30,7 @@ func (b *bodyWrapper) Read(p []byte) (n int, err error) { return n, err } -func HandleConn(c net.Conn, tunnel C.Tunnel, authenticator auth.Authenticator, additions ...inbound.Addition) { +func HandleConn(c net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) { additions = append(additions, inbound.Placeholder) // Add a placeholder for InUser inUserIdx := len(additions) - 1 client := newClient(c, tunnel, additions) @@ -41,6 +41,7 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, authenticator auth.Authenticator, a conn := N.NewBufferedConn(c) + authenticator := getAuth() keepAlive := true trusted := authenticator == nil // disable authenticate if lru is nil lastUser := "" @@ -146,9 +147,6 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, authenticator auth.Authenticator, a } func authenticate(request *http.Request, authenticator auth.Authenticator) (resp *http.Response, user string) { - if inbound.SkipAuthRemoteAddress(request.RemoteAddr) { - authenticator = nil - } credential := parseBasicProxyAuthorization(request) if credential == "" && authenticator != nil { resp = responseWith(request, http.StatusProxyAuthRequired) diff --git a/listener/http/server.go b/listener/http/server.go index f61dd036..74e117f2 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -33,20 +33,20 @@ func (l *Listener) Close() error { } func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) { - return NewWithAuthenticator(addr, tunnel, authStore.Authenticator(), additions...) + return NewWithAuthenticator(addr, tunnel, authStore.Authenticator, additions...) } // NewWithAuthenticate // never change type traits because it's used in CFMA func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additions ...inbound.Addition) (*Listener, error) { - authenticator := authStore.Authenticator() + getAuth := authStore.Authenticator if !authenticate { - authenticator = nil + getAuth = authStore.Nil } - return NewWithAuthenticator(addr, tunnel, authenticator, additions...) + return NewWithAuthenticator(addr, tunnel, getAuth, additions...) } -func NewWithAuthenticator(addr string, tunnel C.Tunnel, authenticator auth.Authenticator, additions ...inbound.Addition) (*Listener, error) { +func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) (*Listener, error) { isDefault := false if len(additions) == 0 { isDefault = true @@ -75,13 +75,18 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, authenticator auth.Authe continue } N.TCPKeepAlive(conn) + + getAuth := getAuth if isDefault { // only apply on default listener if !inbound.IsRemoteAddrDisAllowed(conn.RemoteAddr()) { _ = conn.Close() continue } + if inbound.SkipAuthRemoteAddr(conn.RemoteAddr()) { + getAuth = authStore.Nil + } } - go HandleConn(conn, tunnel, authenticator, additions...) + go HandleConn(conn, tunnel, getAuth, additions...) } }() diff --git a/listener/inbound/auth.go b/listener/inbound/auth.go new file mode 100644 index 00000000..41f18fc0 --- /dev/null +++ b/listener/inbound/auth.go @@ -0,0 +1,31 @@ +package inbound + +import ( + "github.com/metacubex/mihomo/component/auth" + authStore "github.com/metacubex/mihomo/listener/auth" +) + +type AuthUser struct { + Username string `inbound:"username"` + Password string `inbound:"password"` +} + +type AuthUsers []AuthUser + +func (a AuthUsers) GetAuth() func() auth.Authenticator { + if a != nil { // structure's Decode will ensure value not nil when input has value even it was set an empty array + if len(a) == 0 { + return authStore.Nil + } + users := make([]auth.AuthUser, len(a)) + for i, user := range a { + users[i] = auth.AuthUser{ + User: user.Username, + Pass: user.Password, + } + } + authenticator := auth.NewAuthenticator(users) + return func() auth.Authenticator { return authenticator } + } + return authStore.Authenticator +} diff --git a/listener/inbound/http.go b/listener/inbound/http.go index f5301f46..c78abefd 100644 --- a/listener/inbound/http.go +++ b/listener/inbound/http.go @@ -8,6 +8,7 @@ import ( type HTTPOption struct { BaseOption + Users AuthUsers `inbound:"users,omitempty"` } func (o HTTPOption) Equal(config C.InboundConfig) bool { @@ -44,7 +45,7 @@ func (h *HTTP) Address() string { // Listen implements constant.InboundListener func (h *HTTP) Listen(tunnel C.Tunnel) error { var err error - h.l, err = http.New(h.RawAddress(), tunnel, h.Additions()...) + h.l, err = http.NewWithAuthenticator(h.RawAddress(), tunnel, h.config.Users.GetAuth(), h.Additions()...) if err != nil { return err } diff --git a/listener/inbound/mixed.go b/listener/inbound/mixed.go index fc643821..443a2564 100644 --- a/listener/inbound/mixed.go +++ b/listener/inbound/mixed.go @@ -12,7 +12,8 @@ import ( type MixedOption struct { BaseOption - UDP bool `inbound:"udp,omitempty"` + Users AuthUsers `inbound:"users,omitempty"` + UDP bool `inbound:"udp,omitempty"` } func (o MixedOption) Equal(config C.InboundConfig) bool { @@ -52,7 +53,7 @@ func (m *Mixed) Address() string { // Listen implements constant.InboundListener func (m *Mixed) Listen(tunnel C.Tunnel) error { var err error - m.l, err = mixed.New(m.RawAddress(), tunnel, m.Additions()...) + m.l, err = mixed.NewWithAuthenticator(m.RawAddress(), tunnel, m.config.Users.GetAuth(), m.Additions()...) if err != nil { return err } diff --git a/listener/inbound/socks.go b/listener/inbound/socks.go index 7e10d93a..cf6d1ce4 100644 --- a/listener/inbound/socks.go +++ b/listener/inbound/socks.go @@ -9,7 +9,8 @@ import ( type SocksOption struct { BaseOption - UDP bool `inbound:"udp,omitempty"` + Users AuthUsers `inbound:"users,omitempty"` + UDP bool `inbound:"udp,omitempty"` } func (o SocksOption) Equal(config C.InboundConfig) bool { @@ -70,7 +71,7 @@ func (s *Socks) Address() string { // Listen implements constant.InboundListener func (s *Socks) Listen(tunnel C.Tunnel) error { var err error - if s.stl, err = socks.New(s.RawAddress(), tunnel, s.Additions()...); err != nil { + if s.stl, err = socks.NewWithAuthenticator(s.RawAddress(), tunnel, s.config.Users.GetAuth(), s.Additions()...); err != nil { return err } if s.udp { diff --git a/listener/mixed/mixed.go b/listener/mixed/mixed.go index 773cabe3..12390061 100644 --- a/listener/mixed/mixed.go +++ b/listener/mixed/mixed.go @@ -5,6 +5,7 @@ import ( "github.com/metacubex/mihomo/adapter/inbound" N "github.com/metacubex/mihomo/common/net" + "github.com/metacubex/mihomo/component/auth" C "github.com/metacubex/mihomo/constant" authStore "github.com/metacubex/mihomo/listener/auth" "github.com/metacubex/mihomo/listener/http" @@ -36,6 +37,10 @@ func (l *Listener) Close() error { } func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) { + return NewWithAuthenticator(addr, tunnel, authStore.Authenticator, additions...) +} + +func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) (*Listener, error) { isDefault := false if len(additions) == 0 { isDefault = true @@ -62,20 +67,24 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener } continue } + getAuth := getAuth if isDefault { // only apply on default listener if !inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) { _ = c.Close() continue } + if inbound.SkipAuthRemoteAddr(c.RemoteAddr()) { + getAuth = authStore.Nil + } } - go handleConn(c, tunnel, additions...) + go handleConn(c, tunnel, getAuth, additions...) } }() return ml, nil } -func handleConn(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) { +func handleConn(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) { N.TCPKeepAlive(conn) bufConn := N.NewBufferedConn(conn) @@ -86,10 +95,10 @@ func handleConn(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) { switch head[0] { case socks4.Version: - socks.HandleSocks4(bufConn, tunnel, additions...) + socks.HandleSocks4(bufConn, tunnel, getAuth, additions...) case socks5.Version: - socks.HandleSocks5(bufConn, tunnel, additions...) + socks.HandleSocks5(bufConn, tunnel, getAuth, additions...) default: - http.HandleConn(bufConn, tunnel, authStore.Authenticator(), additions...) + http.HandleConn(bufConn, tunnel, getAuth, additions...) } } diff --git a/listener/socks/tcp.go b/listener/socks/tcp.go index f2696e3f..3e98a602 100644 --- a/listener/socks/tcp.go +++ b/listener/socks/tcp.go @@ -6,6 +6,7 @@ import ( "github.com/metacubex/mihomo/adapter/inbound" N "github.com/metacubex/mihomo/common/net" + "github.com/metacubex/mihomo/component/auth" C "github.com/metacubex/mihomo/constant" authStore "github.com/metacubex/mihomo/listener/auth" "github.com/metacubex/mihomo/transport/socks4" @@ -35,6 +36,10 @@ func (l *Listener) Close() error { } func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) { + return NewWithAuthenticator(addr, tunnel, authStore.Authenticator, additions...) +} + +func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) (*Listener, error) { isDefault := false if len(additions) == 0 { isDefault = true @@ -61,20 +66,24 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener } continue } + getAuth := getAuth if isDefault { // only apply on default listener if !inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) { _ = c.Close() continue } + if inbound.SkipAuthRemoteAddr(c.RemoteAddr()) { + getAuth = authStore.Nil + } } - go handleSocks(c, tunnel, additions...) + go handleSocks(c, tunnel, getAuth, additions...) } }() return sl, nil } -func handleSocks(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) { +func handleSocks(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) { N.TCPKeepAlive(conn) bufConn := N.NewBufferedConn(conn) head, err := bufConn.Peek(1) @@ -85,19 +94,16 @@ func handleSocks(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) switch head[0] { case socks4.Version: - HandleSocks4(bufConn, tunnel, additions...) + HandleSocks4(bufConn, tunnel, getAuth, additions...) case socks5.Version: - HandleSocks5(bufConn, tunnel, additions...) + HandleSocks5(bufConn, tunnel, getAuth, additions...) default: conn.Close() } } -func HandleSocks4(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) { - authenticator := authStore.Authenticator() - if inbound.SkipAuthRemoteAddr(conn.RemoteAddr()) { - authenticator = nil - } +func HandleSocks4(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) { + authenticator := getAuth() addr, _, user, err := socks4.ServerHandshake(conn, authenticator) if err != nil { conn.Close() @@ -107,11 +113,8 @@ func HandleSocks4(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) tunnel.HandleTCPConn(inbound.NewSocket(socks5.ParseAddr(addr), conn, C.SOCKS4, additions...)) } -func HandleSocks5(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) { - authenticator := authStore.Authenticator() - if inbound.SkipAuthRemoteAddr(conn.RemoteAddr()) { - authenticator = nil - } +func HandleSocks5(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) { + authenticator := getAuth() target, command, user, err := socks5.ServerHandshake(conn, authenticator) if err != nil { conn.Close() From 81756fc927f00f360d8c74709c6cd05c23c8645d Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 26 Aug 2024 14:02:56 +0800 Subject: [PATCH 184/311] fix: wireguard outbound memory leaks when close --- adapter/outbound/wireguard.go | 2 -- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 2e34dd83..0382debb 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -26,7 +26,6 @@ import ( wireguard "github.com/metacubex/sing-wireguard" - "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/debug" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" @@ -456,7 +455,6 @@ func closeWireGuard(w *WireGuard) { if w.device != nil { w.device.Close() } - _ = common.Close(w.tunDevice) if w.closeCh != nil { close(w.closeCh) } diff --git a/go.mod b/go.mod index b0c1b3dd..dbfa9ba1 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/metacubex/sing-shadowsocks2 v0.2.2 github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 - github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a + github.com/metacubex/sing-wireguard v0.0.0-20240826061955-1e4e67afe5cd github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d github.com/metacubex/utls v1.6.6 github.com/miekg/dns v1.1.62 diff --git a/go.sum b/go.sum index 8194b68a..1bb4b5ba 100644 --- a/go.sum +++ b/go.sum @@ -120,8 +120,8 @@ github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 h1:ypfofGDZbP github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1/go.mod h1:olbEx9yVcaw5tHTNlRamRoxmMKcvDvcVS1YLnQGzvWE= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosqY8xKDp3pqTW3qbrCprZ1l6WkrXSFSCwyY4I= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= -github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a h1:NpSGclHJUYndUwBmyIpFBSoBVg8PoVX7QQKhYg0DjM0= -github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= +github.com/metacubex/sing-wireguard v0.0.0-20240826061955-1e4e67afe5cd h1:r7alry8u4qlUFLNMwGvG1A8ZcfPM6AMSmrm6E2yKdB4= +github.com/metacubex/sing-wireguard v0.0.0-20240826061955-1e4e67afe5cd/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d h1:j9LtzkYstLFoNvXW824QQeN7Y26uPL5249kzWKbzO9U= github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8= From 9cf3eb39f5120dcab008810eec5d0d5fba40f39e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 26 Aug 2024 18:33:04 +0800 Subject: [PATCH 185/311] fix: hysteria1 outbound should be closed when proxy removed --- .github/workflows/build.yml | 2 ++ adapter/outbound/hysteria.go | 24 +++++++++++++++--- adapter/outbound/hysteria2.go | 5 ++++ adapter/outbound/hysteria2_test.go | 38 +++++++++++++++++++++++++++++ adapter/outbound/hysteria_test.go | 39 ++++++++++++++++++++++++++++++ adapter/outbound/wireguard_test.go | 1 + transport/hysteria/core/client.go | 5 +++- 7 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 adapter/outbound/hysteria2_test.go create mode 100644 adapter/outbound/hysteria_test.go diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 94854f27..b62e9820 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -207,6 +207,8 @@ jobs: if: ${{ matrix.jobs.test == 'test' }} run: | go test ./... + echo "---test with_gvisor---" + go test ./... -tags "with_gvisor" -count=1 - name: Update CA run: | diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index dacffd10..ccab16c1 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -7,6 +7,7 @@ import ( "fmt" "net" "net/netip" + "runtime" "strconv" "time" @@ -14,6 +15,7 @@ import ( "github.com/metacubex/quic-go/congestion" M "github.com/sagernet/sing/common/metadata" + CN "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/proxydialer" @@ -43,6 +45,8 @@ type Hysteria struct { option *HysteriaOption client *core.Client + + closeCh chan struct{} // for test } func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { @@ -51,7 +55,7 @@ func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts . return nil, err } - return NewConn(tcpConn, h), nil + return NewConn(CN.NewRefConn(tcpConn, h), h), nil } func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { @@ -59,7 +63,7 @@ func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata if err != nil { return nil, err } - return newPacketConn(&hyPacketConn{udpConn}, h), nil + return newPacketConn(CN.NewRefPacketConn(&hyPacketConn{udpConn}, h), h), nil } func (h *Hysteria) genHdc(ctx context.Context, opts ...dialer.Option) utils.PacketDialer { @@ -218,7 +222,7 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { if err != nil { return nil, fmt.Errorf("hysteria %s create error: %w", addr, err) } - return &Hysteria{ + outbound := &Hysteria{ Base: &Base{ name: option.Name, addr: addr, @@ -231,7 +235,19 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { }, option: &option, client: client, - }, nil + } + runtime.SetFinalizer(outbound, closeHysteria) + + return outbound, nil +} + +func closeHysteria(h *Hysteria) { + if h.client != nil { + _ = h.client.Close() + } + if h.closeCh != nil { + close(h.closeCh) + } } type hyPacketConn struct { diff --git a/adapter/outbound/hysteria2.go b/adapter/outbound/hysteria2.go index b8abf39c..c1a255a7 100644 --- a/adapter/outbound/hysteria2.go +++ b/adapter/outbound/hysteria2.go @@ -38,6 +38,8 @@ type Hysteria2 struct { option *Hysteria2Option client *hysteria2.Client dialer proxydialer.SingDialer + + closeCh chan struct{} // for test } type Hysteria2Option struct { @@ -89,6 +91,9 @@ func closeHysteria2(h *Hysteria2) { if h.client != nil { _ = h.client.CloseWithError(errors.New("proxy removed")) } + if h.closeCh != nil { + close(h.closeCh) + } } func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { diff --git a/adapter/outbound/hysteria2_test.go b/adapter/outbound/hysteria2_test.go new file mode 100644 index 00000000..de7d8227 --- /dev/null +++ b/adapter/outbound/hysteria2_test.go @@ -0,0 +1,38 @@ +package outbound + +import ( + "context" + "runtime" + "testing" + "time" +) + +func TestHysteria2GC(t *testing.T) { + option := Hysteria2Option{} + option.Server = "127.0.0.1" + option.Ports = "200,204,401-429,501-503" + option.HopInterval = 30 + option.Password = "password" + option.Obfs = "salamander" + option.ObfsPassword = "password" + option.SNI = "example.com" + option.ALPN = []string{"h3"} + hy, err := NewHysteria2(option) + if err != nil { + t.Error(err) + return + } + closeCh := make(chan struct{}) + hy.closeCh = closeCh + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + + hy = nil + runtime.GC() + select { + case <-closeCh: + return + case <-ctx.Done(): + t.Error("timeout not GC") + } +} diff --git a/adapter/outbound/hysteria_test.go b/adapter/outbound/hysteria_test.go new file mode 100644 index 00000000..f2297c60 --- /dev/null +++ b/adapter/outbound/hysteria_test.go @@ -0,0 +1,39 @@ +package outbound + +import ( + "context" + "runtime" + "testing" + "time" +) + +func TestHysteriaGC(t *testing.T) { + option := HysteriaOption{} + option.Server = "127.0.0.1" + option.Ports = "200,204,401-429,501-503" + option.Protocol = "udp" + option.Up = "1Mbps" + option.Down = "1Mbps" + option.HopInterval = 30 + option.Obfs = "salamander" + option.SNI = "example.com" + option.ALPN = []string{"h3"} + hy, err := NewHysteria(option) + if err != nil { + t.Error(err) + return + } + closeCh := make(chan struct{}) + hy.closeCh = closeCh + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + + hy = nil + runtime.GC() + select { + case <-closeCh: + return + case <-ctx.Done(): + t.Error("timeout not GC") + } +} diff --git a/adapter/outbound/wireguard_test.go b/adapter/outbound/wireguard_test.go index 20dbdbdd..2248bb7b 100644 --- a/adapter/outbound/wireguard_test.go +++ b/adapter/outbound/wireguard_test.go @@ -29,6 +29,7 @@ func TestWireGuardGC(t *testing.T) { err = wg.init(ctx) if err != nil { t.Error(err) + return } // must do a small sleep before test GC // because it maybe deadlocks if w.device.Close call too fast after w.device.Start diff --git a/transport/hysteria/core/client.go b/transport/hysteria/core/client.go index 60db8fdf..782948c0 100644 --- a/transport/hysteria/core/client.go +++ b/transport/hysteria/core/client.go @@ -289,7 +289,10 @@ func (c *Client) DialUDP(dialer utils.PacketDialer) (UDPConn, error) { func (c *Client) Close() error { c.reconnectMutex.Lock() defer c.reconnectMutex.Unlock() - err := c.quicSession.CloseWithError(closeErrorCodeGeneric, "") + var err error + if c.quicSession != nil { + err = c.quicSession.CloseWithError(closeErrorCodeGeneric, "") + } c.closed = true return err } From d79423a4fa0f4a67ab4e9ba82b45a14eaac6b847 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 26 Aug 2024 20:52:56 +0800 Subject: [PATCH 186/311] fix: tun should not care "force" when Put configs from restful api --- hub/executor/executor.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hub/executor/executor.go b/hub/executor/executor.go index d54d55b7..97072ec8 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -101,6 +101,7 @@ func ApplyConfig(cfg *config.Config, force bool) { updateNTP(cfg.NTP) updateDNS(cfg.DNS, cfg.General.IPv6) updateListeners(cfg.General, cfg.Listeners, force) + updateTun(cfg.General) // tun should not care "force" updateIPTables(cfg) updateTunnels(cfg.Tunnels) @@ -198,6 +199,9 @@ func updateListeners(general *config.General, listeners map[string]C.InboundList listener.ReCreateShadowSocks(general.ShadowSocksConfig, tunnel.Tunnel) listener.ReCreateVmess(general.VmessConfig, tunnel.Tunnel) listener.ReCreateTuic(general.TuicServer, tunnel.Tunnel) +} + +func updateTun(general *config.General) { listener.ReCreateTun(general.Tun, tunnel.Tunnel) } From 6e04e1e9dc0916621beb1f8cdccced2fad5eb8f8 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 27 Aug 2024 08:41:57 +0800 Subject: [PATCH 187/311] fix: hysteria2 close safety --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index dbfa9ba1..6a43c730 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 github.com/metacubex/quic-go v0.46.1-0.20240807232329-1c6cb2d67f58 github.com/metacubex/randv2 v0.2.0 - github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 + github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 github.com/metacubex/sing-shadowsocks v0.2.8 github.com/metacubex/sing-shadowsocks2 v0.2.2 github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 diff --git a/go.sum b/go.sum index 1bb4b5ba..3b059db0 100644 --- a/go.sum +++ b/go.sum @@ -110,8 +110,8 @@ github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiL github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297 h1:YG/JkwGPbca5rUtEMHIu8ZuqzR7BSVm1iqY8hNoMeMA= github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= -github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 h1:Wr4g1HCb5Z/QIFwFiVNjO2qL+dRu25+Mdn9xtAZZ+ew= -github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8= +github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 h1:HobpULaPK6OoxrHMmgcwLkwwIduXVmwdcznwUfH1GQM= +github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8= github.com/metacubex/sing-shadowsocks v0.2.8 h1:wIhlaigswzjPw4hej75sEvWte3QR0+AJRafgwBHO5B4= github.com/metacubex/sing-shadowsocks v0.2.8/go.mod h1:X3x88XtJpBxG0W0/ECOJL6Ib0SJ3xdniAkU/6/RMWU0= github.com/metacubex/sing-shadowsocks2 v0.2.2 h1:eaf42uVx4Lr21S6MDYs0ZdTvGA0GEhDpb9no4+gdXPo= From 3e2c9ce8216d38ab9416070ffe23cf4c9c20f9e6 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 27 Aug 2024 11:04:42 +0800 Subject: [PATCH 188/311] chore: cleanup patch code --- adapter/provider/healthcheck.go | 15 +++++---- adapter/provider/patch_android.go | 17 ---------- adapter/provider/provider.go | 27 +++++++++++----- component/resource/fetcher.go | 54 ++++++++++++++++++------------- component/resource/vehicle.go | 6 ++-- config/config.go | 10 ++++-- constant/provider/interface.go | 4 ++- rules/provider/patch_android.go | 10 ------ rules/provider/provider.go | 14 ++++++-- 9 files changed, 83 insertions(+), 74 deletions(-) diff --git a/adapter/provider/healthcheck.go b/adapter/provider/healthcheck.go index 8b5e8338..8737ff96 100644 --- a/adapter/provider/healthcheck.go +++ b/adapter/provider/healthcheck.go @@ -27,6 +27,8 @@ type extraOption struct { } type HealthCheck struct { + ctx context.Context + ctxCancel context.CancelFunc url string extra map[string]*extraOption mu sync.Mutex @@ -36,7 +38,6 @@ type HealthCheck struct { lazy bool expectedStatus utils.IntRanges[uint16] lastTouch atomic.TypedValue[time.Time] - done chan struct{} singleDo *singledo.Single[struct{}] timeout time.Duration } @@ -59,7 +60,7 @@ func (hc *HealthCheck) process() { } else { log.Debugln("Skip once health check because we are lazy") } - case <-hc.done: + case <-hc.ctx.Done(): ticker.Stop() hc.stop() return @@ -146,7 +147,7 @@ func (hc *HealthCheck) check() { _, _, _ = hc.singleDo.Do(func() (struct{}, error) { id := utils.NewUUIDV4().String() log.Debugln("Start New Health Checking {%s}", id) - b, _ := batch.New[bool](context.Background(), batch.WithConcurrencyNum[bool](10)) + b, _ := batch.New[bool](hc.ctx, batch.WithConcurrencyNum[bool](10)) // execute default health check option := &extraOption{filters: nil, expectedStatus: hc.expectedStatus} @@ -195,7 +196,7 @@ func (hc *HealthCheck) execute(b *batch.Batch[bool], url, uid string, option *ex p := proxy b.Go(p.Name(), func() (bool, error) { - ctx, cancel := context.WithTimeout(context.Background(), hc.timeout) + ctx, cancel := context.WithTimeout(hc.ctx, hc.timeout) defer cancel() log.Debugln("Health Checking, proxy: %s, url: %s, id: {%s}", p.Name(), url, uid) _, _ = p.URLTest(ctx, url, expectedStatus) @@ -206,7 +207,7 @@ func (hc *HealthCheck) execute(b *batch.Batch[bool], url, uid string, option *ex } func (hc *HealthCheck) close() { - hc.done <- struct{}{} + hc.ctxCancel() } func NewHealthCheck(proxies []C.Proxy, url string, timeout uint, interval uint, lazy bool, expectedStatus utils.IntRanges[uint16]) *HealthCheck { @@ -217,8 +218,11 @@ func NewHealthCheck(proxies []C.Proxy, url string, timeout uint, interval uint, if timeout == 0 { timeout = 5000 } + ctx, cancel := context.WithCancel(context.Background()) return &HealthCheck{ + ctx: ctx, + ctxCancel: cancel, proxies: proxies, url: url, timeout: time.Duration(timeout) * time.Millisecond, @@ -226,7 +230,6 @@ func NewHealthCheck(proxies []C.Proxy, url string, timeout uint, interval uint, interval: time.Duration(interval) * time.Second, lazy: lazy, expectedStatus: expectedStatus, - done: make(chan struct{}, 1), singleDo: singledo.NewSingle[struct{}](time.Second), } } diff --git a/adapter/provider/patch_android.go b/adapter/provider/patch_android.go index e9042bda..2a91d7f1 100644 --- a/adapter/provider/patch_android.go +++ b/adapter/provider/patch_android.go @@ -14,23 +14,6 @@ type UpdatableProvider interface { UpdatedAt() time.Time } -func (pp *proxySetProvider) UpdatedAt() time.Time { - return pp.Fetcher.UpdatedAt -} - -func (pp *proxySetProvider) Close() error { - pp.healthCheck.close() - pp.Fetcher.Destroy() - - return nil -} - -func (cp *compatibleProvider) Close() error { - cp.healthCheck.close() - - return nil -} - func Suspend(s bool) { suspended = s } diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index 694eae43..a99c1d96 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -54,7 +54,7 @@ func (pp *proxySetProvider) MarshalJSON() ([]byte, error) { "proxies": pp.Proxies(), "testUrl": pp.healthCheck.url, "expectedStatus": pp.healthCheck.expectedStatus.String(), - "updatedAt": pp.UpdatedAt, + "updatedAt": pp.UpdatedAt(), "subscriptionInfo": pp.subscriptionInfo, }) } @@ -164,9 +164,9 @@ func (pp *proxySetProvider) closeAllConnections() { }) } -func stopProxyProvider(pd *ProxySetProvider) { - pd.healthCheck.close() - _ = pd.Fetcher.Destroy() +func (pp *proxySetProvider) Close() error { + pp.healthCheck.close() + return pp.Fetcher.Close() } func NewProxySetProvider(name string, interval time.Duration, filter string, excludeFilter string, excludeType string, dialerProxy string, override OverrideSchema, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) { @@ -200,10 +200,15 @@ func NewProxySetProvider(name string, interval time.Duration, filter string, exc fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, excludeTypeArray, filterRegs, excludeFilterReg, dialerProxy, override), proxiesOnUpdate(pd)) pd.Fetcher = fetcher wrapper := &ProxySetProvider{pd} - runtime.SetFinalizer(wrapper, stopProxyProvider) + runtime.SetFinalizer(wrapper, (*ProxySetProvider).Close) return wrapper, nil } +func (pp *ProxySetProvider) Close() error { + runtime.SetFinalizer(pp, nil) + return pp.proxySetProvider.Close() +} + // CompatibleProvider for auto gc type CompatibleProvider struct { *compatibleProvider @@ -274,8 +279,9 @@ func (cp *compatibleProvider) RegisterHealthCheckTask(url string, expectedStatus cp.healthCheck.registerHealthCheckTask(url, expectedStatus, filter, interval) } -func stopCompatibleProvider(pd *CompatibleProvider) { - pd.healthCheck.close() +func (cp *compatibleProvider) Close() error { + cp.healthCheck.close() + return nil } func NewCompatibleProvider(name string, proxies []C.Proxy, hc *HealthCheck) (*CompatibleProvider, error) { @@ -294,10 +300,15 @@ func NewCompatibleProvider(name string, proxies []C.Proxy, hc *HealthCheck) (*Co } wrapper := &CompatibleProvider{pd} - runtime.SetFinalizer(wrapper, stopCompatibleProvider) + runtime.SetFinalizer(wrapper, (*CompatibleProvider).Close) return wrapper, nil } +func (cp *CompatibleProvider) Close() error { + runtime.SetFinalizer(cp, nil) + return cp.compatibleProvider.Close() +} + func proxiesOnUpdate(pd *proxySetProvider) func([]C.Proxy) { return func(elm []C.Proxy) { pd.setProxies(elm) diff --git a/component/resource/fetcher.go b/component/resource/fetcher.go index c82a54a3..9a4f5a75 100644 --- a/component/resource/fetcher.go +++ b/component/resource/fetcher.go @@ -2,6 +2,7 @@ package resource import ( "bytes" + "context" "crypto/md5" "os" "path/filepath" @@ -22,11 +23,12 @@ var ( type Parser[V any] func([]byte) (V, error) type Fetcher[V any] struct { + ctx context.Context + ctxCancel context.CancelFunc resourceType string name string vehicle types.Vehicle - UpdatedAt time.Time - done chan struct{} + updatedAt time.Time hash [16]byte parser Parser[V] interval time.Duration @@ -46,6 +48,10 @@ func (f *Fetcher[V]) VehicleType() types.VehicleType { return f.vehicle.Type() } +func (f *Fetcher[V]) UpdatedAt() time.Time { + return f.updatedAt +} + func (f *Fetcher[V]) Initial() (V, error) { var ( buf []byte @@ -57,15 +63,15 @@ func (f *Fetcher[V]) Initial() (V, error) { if stat, fErr := os.Stat(f.vehicle.Path()); fErr == nil { buf, err = os.ReadFile(f.vehicle.Path()) modTime := stat.ModTime() - f.UpdatedAt = modTime + f.updatedAt = modTime isLocal = true if f.interval != 0 && modTime.Add(f.interval).Before(time.Now()) { log.Warnln("[Provider] %s not updated for a long time, force refresh", f.Name()) forceUpdate = true } } else { - buf, err = f.vehicle.Read() - f.UpdatedAt = time.Now() + buf, err = f.vehicle.Read(f.ctx) + f.updatedAt = time.Now() } if err != nil { @@ -75,7 +81,7 @@ func (f *Fetcher[V]) Initial() (V, error) { var contents V if forceUpdate { var forceBuf []byte - if forceBuf, err = f.vehicle.Read(); err == nil { + if forceBuf, err = f.vehicle.Read(f.ctx); err == nil { if contents, err = f.parser(forceBuf); err == nil { isLocal = false buf = forceBuf @@ -93,7 +99,7 @@ func (f *Fetcher[V]) Initial() (V, error) { } // parse local file error, fallback to remote - buf, err = f.vehicle.Read() + buf, err = f.vehicle.Read(f.ctx) if err != nil { return lo.Empty[V](), err } @@ -136,15 +142,18 @@ func (f *Fetcher[V]) Initial() (V, error) { } func (f *Fetcher[V]) Update() (V, bool, error) { - buf, err := f.vehicle.Read() + buf, err := f.vehicle.Read(f.ctx) if err != nil { return lo.Empty[V](), false, err } + return f.SideUpdate(buf) +} +func (f *Fetcher[V]) SideUpdate(buf []byte) (V, bool, error) { now := time.Now() hash := md5.Sum(buf) if bytes.Equal(f.hash[:], hash[:]) { - f.UpdatedAt = now + f.updatedAt = now _ = os.Chtimes(f.vehicle.Path(), now, now) return lo.Empty[V](), true, nil } @@ -160,16 +169,14 @@ func (f *Fetcher[V]) Update() (V, bool, error) { } } - f.UpdatedAt = now + f.updatedAt = now f.hash = hash return contents, false, nil } -func (f *Fetcher[V]) Destroy() error { - if f.interval > 0 { - f.done <- struct{}{} - } +func (f *Fetcher[V]) Close() error { + f.ctxCancel() if f.watcher != nil { _ = f.watcher.Close() } @@ -177,7 +184,7 @@ func (f *Fetcher[V]) Destroy() error { } func (f *Fetcher[V]) pullLoop() { - initialInterval := f.interval - time.Since(f.UpdatedAt) + initialInterval := f.interval - time.Since(f.updatedAt) if initialInterval > f.interval { initialInterval = f.interval } @@ -189,7 +196,7 @@ func (f *Fetcher[V]) pullLoop() { case <-timer.C: timer.Reset(f.interval) f.update(f.vehicle.Path()) - case <-f.done: + case <-f.ctx.Done(): return } } @@ -226,13 +233,14 @@ func safeWrite(path string, buf []byte) error { } func NewFetcher[V any](name string, interval time.Duration, vehicle types.Vehicle, parser Parser[V], onUpdate func(V)) *Fetcher[V] { - + ctx, cancel := context.WithCancel(context.Background()) return &Fetcher[V]{ - name: name, - vehicle: vehicle, - parser: parser, - done: make(chan struct{}, 8), - OnUpdate: onUpdate, - interval: interval, + ctx: ctx, + ctxCancel: cancel, + name: name, + vehicle: vehicle, + parser: parser, + OnUpdate: onUpdate, + interval: interval, } } diff --git a/component/resource/vehicle.go b/component/resource/vehicle.go index b13369d2..4618ef52 100644 --- a/component/resource/vehicle.go +++ b/component/resource/vehicle.go @@ -24,7 +24,7 @@ func (f *FileVehicle) Path() string { return f.path } -func (f *FileVehicle) Read() ([]byte, error) { +func (f *FileVehicle) Read(ctx context.Context) ([]byte, error) { return os.ReadFile(f.path) } @@ -59,8 +59,8 @@ func (h *HTTPVehicle) Proxy() string { return h.proxy } -func (h *HTTPVehicle) Read() ([]byte, error) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) +func (h *HTTPVehicle) Read(ctx context.Context) ([]byte, error) { + ctx, cancel := context.WithTimeout(ctx, time.Second*20) defer cancel() resp, err := mihomoHttp.HttpRequestWithProxy(ctx, h.url, http.MethodGet, h.header, nil, h.proxy) if err != nil { diff --git a/config/config.go b/config/config.go index f1ffbce1..48a99e41 100644 --- a/config/config.go +++ b/config/config.go @@ -431,9 +431,8 @@ func Parse(buf []byte) (*Config, error) { return ParseRawConfig(rawCfg) } -func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { - // config with default value - rawCfg := &RawConfig{ +func DefaultRawConfig() *RawConfig { + return &RawConfig{ AllowLan: false, BindAddress: "*", LanAllowedIPs: []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0")}, @@ -544,6 +543,11 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { }, ExternalUIURL: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip", } +} + +func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { + // config with default value + rawCfg := DefaultRawConfig() if err := yaml.Unmarshal(buf, rawCfg); err != nil { return nil, err diff --git a/constant/provider/interface.go b/constant/provider/interface.go index bd6b6e94..911f774a 100644 --- a/constant/provider/interface.go +++ b/constant/provider/interface.go @@ -1,6 +1,7 @@ package provider import ( + "context" "fmt" "github.com/metacubex/mihomo/common/utils" @@ -31,7 +32,7 @@ func (v VehicleType) String() string { } type Vehicle interface { - Read() ([]byte, error) + Read(ctx context.Context) ([]byte, error) Path() string Proxy() string Type() VehicleType @@ -83,6 +84,7 @@ type ProxyProvider interface { type RuleProvider interface { Provider Behavior() RuleBehavior + Count() int Match(*constant.Metadata) bool ShouldResolveIP() bool ShouldFindProcess() bool diff --git a/rules/provider/patch_android.go b/rules/provider/patch_android.go index 7ef1df1b..d4b752c3 100644 --- a/rules/provider/patch_android.go +++ b/rules/provider/patch_android.go @@ -12,16 +12,6 @@ type UpdatableProvider interface { UpdatedAt() time.Time } -func (rp *ruleSetProvider) UpdatedAt() time.Time { - return rp.Fetcher.UpdatedAt -} - -func (rp *ruleSetProvider) Close() error { - rp.Fetcher.Destroy() - - return nil -} - func Suspend(s bool) { suspended = s } diff --git a/rules/provider/provider.go b/rules/provider/provider.go index b9524c35..ad720d47 100644 --- a/rules/provider/provider.go +++ b/rules/provider/provider.go @@ -89,6 +89,10 @@ func (rp *ruleSetProvider) Behavior() P.RuleBehavior { return rp.behavior } +func (rp *ruleSetProvider) Count() int { + return rp.strategy.Count() +} + func (rp *ruleSetProvider) Match(metadata *C.Metadata) bool { return rp.strategy != nil && rp.strategy.Match(metadata) } @@ -113,11 +117,16 @@ func (rp *ruleSetProvider) MarshalJSON() ([]byte, error) { "name": rp.Name(), "ruleCount": rp.strategy.Count(), "type": rp.Type().String(), - "updatedAt": rp.UpdatedAt, + "updatedAt": rp.UpdatedAt(), "vehicleType": rp.VehicleType().String(), }) } +func (rp *RuleSetProvider) Close() error { + runtime.SetFinalizer(rp, nil) + return rp.ruleSetProvider.Close() +} + func NewRuleSetProvider(name string, behavior P.RuleBehavior, format P.RuleFormat, interval time.Duration, vehicle P.Vehicle, parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) P.RuleProvider { rp := &ruleSetProvider{ @@ -139,8 +148,7 @@ func NewRuleSetProvider(name string, behavior P.RuleBehavior, format P.RuleForma rp, } - final := func(provider *RuleSetProvider) { _ = rp.Fetcher.Destroy() } - runtime.SetFinalizer(wrapper, final) + runtime.SetFinalizer(wrapper, (*RuleSetProvider).Close) return wrapper } From 84831785242ddb409591ad4954e74e2a799d1e7e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 27 Aug 2024 20:33:43 +0800 Subject: [PATCH 189/311] feat: `sniff` add `skip-src-address` and `skip-dst-address` --- component/cidr/ipcidr_set.go | 4 + component/sniffer/dispatcher.go | 110 ++++++++++++++------------ config/config.go | 135 ++++++++++++++++++++++++-------- docs/config.yaml | 4 + hub/executor/executor.go | 26 +++--- tunnel/tunnel.go | 40 +++++----- 6 files changed, 200 insertions(+), 119 deletions(-) diff --git a/component/cidr/ipcidr_set.go b/component/cidr/ipcidr_set.go index 49071460..00ad7259 100644 --- a/component/cidr/ipcidr_set.go +++ b/component/cidr/ipcidr_set.go @@ -57,6 +57,10 @@ func (set *IpCidrSet) Merge() error { return nil } +func (set *IpCidrSet) IsEmpty() bool { + return set == nil || len(set.rr) == 0 +} + func (set *IpCidrSet) Foreach(f func(prefix netip.Prefix) bool) { for _, r := range set.rr { for _, prefix := range r.Prefixes() { diff --git a/component/sniffer/dispatcher.go b/component/sniffer/dispatcher.go index c96f5a4b..8c9f692d 100644 --- a/component/sniffer/dispatcher.go +++ b/component/sniffer/dispatcher.go @@ -2,7 +2,6 @@ package sniffer import ( "errors" - "fmt" "net" "net/netip" "time" @@ -20,19 +19,29 @@ var ( ErrNoClue = errors.New("not enough information for making a decision") ) -var Dispatcher *SnifferDispatcher - -type SnifferDispatcher struct { +type Dispatcher struct { enable bool sniffers map[sniffer.Sniffer]SnifferConfig forceDomain []C.Rule + skipSrcAddress []C.Rule + skipDstAddress []C.Rule skipDomain []C.Rule - skipList *lru.LruCache[string, uint8] + skipList *lru.LruCache[netip.AddrPort, uint8] forceDnsMapping bool parsePureIp bool } -func (sd *SnifferDispatcher) shouldOverride(metadata *C.Metadata) bool { +func (sd *Dispatcher) shouldOverride(metadata *C.Metadata) bool { + for _, rule := range sd.skipDstAddress { + if ok, _ := rule.Match(&C.Metadata{DstIP: metadata.DstIP}); ok { + return false + } + } + for _, rule := range sd.skipSrcAddress { + if ok, _ := rule.Match(&C.Metadata{DstIP: metadata.SrcIP}); ok { + return false + } + } if metadata.Host == "" && sd.parsePureIp { return true } @@ -47,10 +56,9 @@ func (sd *SnifferDispatcher) shouldOverride(metadata *C.Metadata) bool { return false } -func (sd *SnifferDispatcher) UDPSniff(packet C.PacketAdapter) bool { +func (sd *Dispatcher) UDPSniff(packet C.PacketAdapter) bool { metadata := packet.Metadata() - - if sd.shouldOverride(packet.Metadata()) { + if sd.shouldOverride(metadata) { for sniffer, config := range sd.sniffers { if sniffer.SupportNetwork() == C.UDP || sniffer.SupportNetwork() == C.ALLNet { inWhitelist := sniffer.SupportPort(metadata.DstPort) @@ -73,7 +81,7 @@ func (sd *SnifferDispatcher) UDPSniff(packet C.PacketAdapter) bool { } // TCPSniff returns true if the connection is sniffed to have a domain -func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) bool { +func (sd *Dispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) bool { if sd.shouldOverride(metadata) { inWhitelist := false overrideDest := false @@ -91,34 +99,35 @@ func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata return false } - dst := fmt.Sprintf("%s:%d", metadata.DstIP, metadata.DstPort) + dst := metadata.AddrPort() if count, ok := sd.skipList.Get(dst); ok && count > 5 { log.Debugln("[Sniffer] Skip sniffing[%s] due to multiple failures", dst) return false } - if host, err := sd.sniffDomain(conn, metadata); err != nil { + host, err := sd.sniffDomain(conn, metadata) + if 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 false - } else { - for _, rule := range sd.skipDomain { - if ok, _ := rule.Match(&C.Metadata{Host: host}); ok { - log.Debugln("[Sniffer] Skip sni[%s]", host) - return false - } - } - - sd.skipList.Delete(dst) - - sd.replaceDomain(metadata, host, overrideDest) - return true } + + for _, rule := range sd.skipDomain { + if ok, _ := rule.Match(&C.Metadata{Host: host}); ok { + log.Debugln("[Sniffer] Skip sni[%s]", host) + return false + } + } + + sd.skipList.Delete(dst) + + sd.replaceDomain(metadata, host, overrideDest) + return true } return false } -func (sd *SnifferDispatcher) replaceDomain(metadata *C.Metadata, host string, overrideDest bool) { +func (sd *Dispatcher) replaceDomain(metadata *C.Metadata, host string, overrideDest bool) { metadata.SniffHost = host if overrideDest { log.Debugln("[Sniffer] Sniff %s [%s]-->[%s] success, replace domain [%s]-->[%s]", @@ -131,11 +140,11 @@ func (sd *SnifferDispatcher) replaceDomain(metadata *C.Metadata, host string, ov metadata.DNSMode = C.DNSNormal } -func (sd *SnifferDispatcher) Enable() bool { - return sd.enable +func (sd *Dispatcher) Enable() bool { + return sd != nil && sd.enable } -func (sd *SnifferDispatcher) sniffDomain(conn *N.BufferedConn, metadata *C.Metadata) (string, error) { +func (sd *Dispatcher) sniffDomain(conn *N.BufferedConn, metadata *C.Metadata) (string, error) { for s := range sd.sniffers { if s.SupportNetwork() == C.TCP { _ = conn.SetReadDeadline(time.Now().Add(1 * time.Second)) @@ -178,8 +187,8 @@ func (sd *SnifferDispatcher) sniffDomain(conn *N.BufferedConn, metadata *C.Metad return "", ErrorSniffFailed } -func (sd *SnifferDispatcher) cacheSniffFailed(metadata *C.Metadata) { - dst := fmt.Sprintf("%s:%d", metadata.DstIP, metadata.DstPort) +func (sd *Dispatcher) cacheSniffFailed(metadata *C.Metadata) { + dst := metadata.AddrPort() sd.skipList.Compute(dst, func(oldValue uint8, loaded bool) (newValue uint8, delete bool) { if oldValue <= 5 { oldValue++ @@ -188,32 +197,35 @@ func (sd *SnifferDispatcher) cacheSniffFailed(metadata *C.Metadata) { }) } -func NewCloseSnifferDispatcher() (*SnifferDispatcher, error) { - dispatcher := SnifferDispatcher{ - enable: false, - } - - return &dispatcher, nil +type Config struct { + Enable bool + Sniffers map[sniffer.Type]SnifferConfig + ForceDomain []C.Rule + SkipSrcAddress []C.Rule + SkipDstAddress []C.Rule + SkipDomain []C.Rule + ForceDnsMapping bool + ParsePureIp bool } -func NewSnifferDispatcher(snifferConfig map[sniffer.Type]SnifferConfig, - forceDomain []C.Rule, skipDomain []C.Rule, - forceDnsMapping bool, parsePureIp bool) (*SnifferDispatcher, error) { - dispatcher := SnifferDispatcher{ - enable: true, - forceDomain: forceDomain, - skipDomain: skipDomain, - skipList: lru.New(lru.WithSize[string, uint8](128), lru.WithAge[string, uint8](600)), - forceDnsMapping: forceDnsMapping, - parsePureIp: parsePureIp, - sniffers: make(map[sniffer.Sniffer]SnifferConfig, 0), +func NewDispatcher(snifferConfig *Config) (*Dispatcher, error) { + dispatcher := Dispatcher{ + enable: snifferConfig.Enable, + forceDomain: snifferConfig.ForceDomain, + skipSrcAddress: snifferConfig.SkipSrcAddress, + skipDstAddress: snifferConfig.SkipDstAddress, + skipDomain: snifferConfig.SkipDomain, + skipList: lru.New(lru.WithSize[netip.AddrPort, uint8](128), lru.WithAge[netip.AddrPort, uint8](600)), + forceDnsMapping: snifferConfig.ForceDnsMapping, + parsePureIp: snifferConfig.ParsePureIp, + sniffers: make(map[sniffer.Sniffer]SnifferConfig, len(snifferConfig.Sniffers)), } - for snifferName, config := range snifferConfig { + for snifferName, config := range snifferConfig.Sniffers { s, err := NewSniffer(snifferName, config) if err != nil { log.Errorln("Sniffer name[%s] is error", snifferName) - return &SnifferDispatcher{enable: false}, err + return &Dispatcher{enable: false}, err } dispatcher.sniffers[s] = config } diff --git a/config/config.go b/config/config.go index 48a99e41..7197f524 100644 --- a/config/config.go +++ b/config/config.go @@ -25,7 +25,7 @@ import ( "github.com/metacubex/mihomo/component/geodata" P "github.com/metacubex/mihomo/component/process" "github.com/metacubex/mihomo/component/resolver" - SNIFF "github.com/metacubex/mihomo/component/sniffer" + "github.com/metacubex/mihomo/component/sniffer" tlsC "github.com/metacubex/mihomo/component/tls" "github.com/metacubex/mihomo/component/trie" "github.com/metacubex/mihomo/component/updater" @@ -161,16 +161,6 @@ type Profile struct { StoreFakeIP bool } -// Sniffer config -type Sniffer struct { - Enable bool - Sniffers map[snifferTypes.Type]SNIFF.SnifferConfig - ForceDomain []C.Rule - SkipDomain []C.Rule - ForceDnsMapping bool - ParsePureIp bool -} - // TLS config type TLS struct { Certificate string @@ -196,7 +186,7 @@ type Config struct { Providers map[string]providerTypes.ProxyProvider RuleProviders map[string]providerTypes.RuleProvider Tunnels []LC.Tunnel - Sniffer *Sniffer + Sniffer *sniffer.Config TLS *TLS } @@ -327,15 +317,18 @@ type RawGeoXUrl struct { } type RawSniffer struct { - Enable bool `yaml:"enable" json:"enable"` - OverrideDest bool `yaml:"override-destination" json:"override-destination"` - Sniffing []string `yaml:"sniffing" json:"sniffing"` - ForceDomain []string `yaml:"force-domain" json:"force-domain"` - SkipDomain []string `yaml:"skip-domain" json:"skip-domain"` - Ports []string `yaml:"port-whitelist" json:"port-whitelist"` - ForceDnsMapping bool `yaml:"force-dns-mapping" json:"force-dns-mapping"` - ParsePureIp bool `yaml:"parse-pure-ip" json:"parse-pure-ip"` - Sniff map[string]RawSniffingConfig `yaml:"sniff" json:"sniff"` + Enable bool `yaml:"enable" json:"enable"` + OverrideDest bool `yaml:"override-destination" json:"override-destination"` + Sniffing []string `yaml:"sniffing" json:"sniffing"` + ForceDomain []string `yaml:"force-domain" json:"force-domain"` + SkipSrcAddress []string `yaml:"skip-src-address" json:"skip-src-address"` + SkipDstAddress []string `yaml:"skip-dst-address" json:"skip-dst-address"` + SkipDomain []string `yaml:"skip-domain" json:"skip-domain"` + Ports []string `yaml:"port-whitelist" json:"port-whitelist"` + ForceDnsMapping bool `yaml:"force-dns-mapping" json:"force-dns-mapping"` + ParsePureIp bool `yaml:"parse-pure-ip" json:"parse-pure-ip"` + + Sniff map[string]RawSniffingConfig `yaml:"sniff" json:"sniff"` } type RawSniffingConfig struct { @@ -1477,7 +1470,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul var rule C.Rule if len(cfg.Fallback) != 0 { if cfg.FallbackFilter.GeoIP { - rule, err = RC.NewGEOIP(cfg.FallbackFilter.GeoIPCode, "", false, true) + rule, err = RC.NewGEOIP(cfg.FallbackFilter.GeoIPCode, "dns.fallback-filter.geoip", false, true) if err != nil { return nil, fmt.Errorf("load GeoIP dns fallback filter error, %w", err) } @@ -1507,7 +1500,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul } } rule = RP.NewDomainSet(domainTrie.NewDomainSet(), "dns.fallback-filter.domain") - dnsCfg.FallbackIPFilter = append(dnsCfg.FallbackIPFilter, rule) + dnsCfg.FallbackDomainFilter = append(dnsCfg.FallbackDomainFilter, rule) } if len(cfg.FallbackFilter.GeoSite) > 0 { log.Warnln("replace fallback-filter.geosite with nameserver-policy, it will be removed in the future") @@ -1516,7 +1509,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul if err != nil { return nil, fmt.Errorf("DNS FallbackGeosite[%d] format error: %w", idx, err) } - dnsCfg.FallbackIPFilter = append(dnsCfg.FallbackIPFilter, rule) + dnsCfg.FallbackDomainFilter = append(dnsCfg.FallbackDomainFilter, rule) } } } @@ -1618,13 +1611,13 @@ func parseTuicServer(rawTuic RawTuicServer, general *General) error { return nil } -func parseSniffer(snifferRaw RawSniffer, ruleProviders map[string]providerTypes.RuleProvider) (*Sniffer, error) { - sniffer := &Sniffer{ +func parseSniffer(snifferRaw RawSniffer, ruleProviders map[string]providerTypes.RuleProvider) (*sniffer.Config, error) { + snifferConfig := &sniffer.Config{ Enable: snifferRaw.Enable, ForceDnsMapping: snifferRaw.ForceDnsMapping, ParsePureIp: snifferRaw.ParsePureIp, } - loadSniffer := make(map[snifferTypes.Type]SNIFF.SnifferConfig) + loadSniffer := make(map[snifferTypes.Type]sniffer.SnifferConfig) if len(snifferRaw.Sniff) != 0 { for sniffType, sniffConfig := range snifferRaw.Sniff { @@ -1640,7 +1633,7 @@ func parseSniffer(snifferRaw RawSniffer, ruleProviders map[string]providerTypes. for _, snifferType := range snifferTypes.List { if snifferType.String() == strings.ToUpper(sniffType) { find = true - loadSniffer[snifferType] = SNIFF.SnifferConfig{ + loadSniffer[snifferType] = sniffer.SnifferConfig{ Ports: ports, OverrideDest: overrideDest, } @@ -1652,7 +1645,7 @@ func parseSniffer(snifferRaw RawSniffer, ruleProviders map[string]providerTypes. } } } else { - if sniffer.Enable && len(snifferRaw.Sniffing) != 0 { + if snifferConfig.Enable && len(snifferRaw.Sniffing) != 0 { // Deprecated: Use Sniff instead log.Warnln("Deprecated: Use Sniff instead") } @@ -1666,7 +1659,7 @@ func parseSniffer(snifferRaw RawSniffer, ruleProviders map[string]providerTypes. for _, snifferType := range snifferTypes.List { if snifferType.String() == strings.ToUpper(snifferName) { find = true - loadSniffer[snifferType] = SNIFF.SnifferConfig{ + loadSniffer[snifferType] = sniffer.SnifferConfig{ Ports: globalPorts, OverrideDest: snifferRaw.OverrideDest, } @@ -1679,21 +1672,80 @@ func parseSniffer(snifferRaw RawSniffer, ruleProviders map[string]providerTypes. } } - sniffer.Sniffers = loadSniffer + snifferConfig.Sniffers = loadSniffer forceDomain, err := parseDomain(snifferRaw.ForceDomain, nil, "sniffer.force-domain", ruleProviders) if err != nil { return nil, fmt.Errorf("error in force-domain, error:%w", err) } - sniffer.ForceDomain = forceDomain + snifferConfig.ForceDomain = forceDomain + + skipSrcAddress, err := parseIPCIDR(snifferRaw.SkipSrcAddress, nil, "sniffer.skip-src-address", ruleProviders) + if err != nil { + return nil, fmt.Errorf("error in skip-src-address, error:%w", err) + } + snifferConfig.SkipSrcAddress = skipSrcAddress + + skipDstAddress, err := parseIPCIDR(snifferRaw.SkipDstAddress, nil, "sniffer.skip-src-address", ruleProviders) + if err != nil { + return nil, fmt.Errorf("error in skip-dst-address, error:%w", err) + } + snifferConfig.SkipDstAddress = skipDstAddress skipDomain, err := parseDomain(snifferRaw.SkipDomain, nil, "sniffer.skip-domain", ruleProviders) if err != nil { return nil, fmt.Errorf("error in skip-domain, error:%w", err) } - sniffer.SkipDomain = skipDomain + snifferConfig.SkipDomain = skipDomain - return sniffer, nil + return snifferConfig, nil +} + +func parseIPCIDR(addresses []string, cidrSet *cidr.IpCidrSet, adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (ipRules []C.Rule, err error) { + var rule C.Rule + for _, ipcidr := range addresses { + ipcidrLower := strings.ToLower(ipcidr) + if strings.Contains(ipcidrLower, "geoip:") { + subkeys := strings.Split(ipcidr, ":") + subkeys = subkeys[1:] + subkeys = strings.Split(subkeys[0], ",") + for _, country := range subkeys { + rule, err = RC.NewGEOIP(country, adapterName, false, false) + if err != nil { + return nil, err + } + ipRules = append(ipRules, rule) + } + } else if strings.Contains(ipcidrLower, "rule-set:") { + subkeys := strings.Split(ipcidr, ":") + subkeys = subkeys[1:] + subkeys = strings.Split(subkeys[0], ",") + for _, domainSetName := range subkeys { + rule, err = parseIPRuleSet(domainSetName, adapterName, ruleProviders) + if err != nil { + return nil, err + } + ipRules = append(ipRules, rule) + } + } else { + if cidrSet == nil { + cidrSet = cidr.NewIpCidrSet() + } + err = cidrSet.AddIpCidrForString(ipcidr) + if err != nil { + return nil, err + } + } + } + if !cidrSet.IsEmpty() { + err = cidrSet.Merge() + if err != nil { + return nil, err + } + rule = RP.NewIpCidrSet(cidrSet, adapterName) + ipRules = append(ipRules, rule) + } + return } func parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (domainRules []C.Rule, err error) { @@ -1739,6 +1791,21 @@ func parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], adapte return } +func parseIPRuleSet(domainSetName string, adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (C.Rule, error) { + if rp, ok := ruleProviders[domainSetName]; !ok { + return nil, fmt.Errorf("not found rule-set: %s", domainSetName) + } else { + switch rp.Behavior() { + case providerTypes.Domain: + return nil, fmt.Errorf("rule provider type error, except ipcidr,actual %s", rp.Behavior()) + case providerTypes.Classical: + log.Warnln("%s provider is %s, only matching it contain ip rule", rp.Name(), rp.Behavior()) + default: + } + } + return RP.NewRuleSet(domainSetName, adapterName, true) +} + func parseDomainRuleSet(domainSetName string, adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (C.Rule, error) { if rp, ok := ruleProviders[domainSetName]; !ok { return nil, fmt.Errorf("not found rule-set: %s", domainSetName) diff --git a/docs/config.yaml b/docs/config.yaml index b67880f7..bb60b286 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -190,6 +190,10 @@ sniffer: override-destination: true force-domain: - +.v2ex.com + # skip-src-address: # 对于来源ip跳过嗅探 + # - 192.168.0.3/32 + # skip-dst-address: # 对于目标ip跳过嗅探 + # - 192.168.0.3/32 ## 对嗅探结果进行跳过 # skip-domain: # - Mijia Cloud diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 97072ec8..442666f0 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -21,7 +21,7 @@ import ( "github.com/metacubex/mihomo/component/profile" "github.com/metacubex/mihomo/component/profile/cachefile" "github.com/metacubex/mihomo/component/resolver" - SNI "github.com/metacubex/mihomo/component/sniffer" + "github.com/metacubex/mihomo/component/sniffer" tlsC "github.com/metacubex/mihomo/component/tls" "github.com/metacubex/mihomo/component/trie" "github.com/metacubex/mihomo/component/updater" @@ -361,25 +361,17 @@ func hcCompatibleProvider(proxyProviders map[string]provider.ProxyProvider) { } -func updateSniffer(sniffer *config.Sniffer) { - if sniffer.Enable { - dispatcher, err := SNI.NewSnifferDispatcher( - sniffer.Sniffers, sniffer.ForceDomain, sniffer.SkipDomain, - sniffer.ForceDnsMapping, sniffer.ParsePureIp, - ) - if err != nil { - log.Warnln("initial sniffer failed, err:%v", err) - } +func updateSniffer(snifferConfig *sniffer.Config) { + dispatcher, err := sniffer.NewDispatcher(snifferConfig) + if err != nil { + log.Warnln("initial sniffer failed, err:%v", err) + } - tunnel.UpdateSniffer(dispatcher) + tunnel.UpdateSniffer(dispatcher) + + if snifferConfig.Enable { log.Infoln("Sniffer is loaded and working") } else { - dispatcher, err := SNI.NewCloseSnifferDispatcher() - if err != nil { - log.Warnln("initial sniffer failed, err:%v", err) - } - - tunnel.UpdateSniffer(dispatcher) log.Infoln("Sniffer is closed") } } diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index b1b417be..60ba0323 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -29,18 +29,17 @@ import ( ) var ( - status = newAtomicStatus(Suspend) - tcpQueue = make(chan C.ConnContext, 200) - udpQueue = make(chan C.PacketAdapter, 200) - natTable = nat.New() - rules []C.Rule - listeners = make(map[string]C.InboundListener) - subRules map[string][]C.Rule - proxies = make(map[string]C.Proxy) - providers map[string]provider.ProxyProvider - ruleProviders map[string]provider.RuleProvider - sniffingEnable = false - configMux sync.RWMutex + status = newAtomicStatus(Suspend) + tcpQueue = make(chan C.ConnContext, 200) + udpQueue = make(chan C.PacketAdapter, 200) + natTable = nat.New() + rules []C.Rule + listeners = make(map[string]C.InboundListener) + subRules map[string][]C.Rule + proxies = make(map[string]C.Proxy) + providers map[string]provider.ProxyProvider + ruleProviders map[string]provider.RuleProvider + configMux sync.RWMutex // Outbound Rule mode = Rule @@ -52,6 +51,9 @@ var ( fakeIPRange netip.Prefix + snifferDispatcher *sniffer.Dispatcher + sniffingEnable = false + ruleUpdateCallback = utils.NewCallback[provider.RuleProvider]() ) @@ -115,7 +117,7 @@ func FakeIPRange() netip.Prefix { } func SetSniffing(b bool) { - if sniffer.Dispatcher.Enable() { + if snifferDispatcher.Enable() { configMux.Lock() sniffingEnable = b configMux.Unlock() @@ -208,9 +210,9 @@ func UpdateListeners(newListeners map[string]C.InboundListener) { listeners = newListeners } -func UpdateSniffer(dispatcher *sniffer.SnifferDispatcher) { +func UpdateSniffer(dispatcher *sniffer.Dispatcher) { configMux.Lock() - sniffer.Dispatcher = dispatcher + snifferDispatcher = dispatcher sniffingEnable = dispatcher.Enable() configMux.Unlock() } @@ -347,8 +349,8 @@ func handleUDPConn(packet C.PacketAdapter) { return } - if sniffer.Dispatcher.Enable() && sniffingEnable { - sniffer.Dispatcher.UDPSniff(packet) + if sniffingEnable && snifferDispatcher.Enable() { + snifferDispatcher.UDPSniff(packet) } // local resolve UDP dns @@ -456,10 +458,10 @@ func handleTCPConn(connCtx C.ConnContext) { conn := connCtx.Conn() conn.ResetPeeked() // reset before sniffer - if sniffer.Dispatcher.Enable() && sniffingEnable { + if sniffingEnable && snifferDispatcher.Enable() { // 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) { + if snifferDispatcher.TCPSniff(conn, metadata) { // we now have a domain name preHandleFailed = false } From 4fecf68b8ba3f6e95257607fa67af94f35bdc08e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 28 Aug 2024 12:25:45 +0800 Subject: [PATCH 190/311] chore: add `sourceGeoIP` and `sourceIPASN` to metadata --- component/cidr/ipcidr_set.go | 8 ++ component/fakeip/pool.go | 8 +- component/fakeip/pool_test.go | 3 +- component/sniffer/dispatcher.go | 32 ++++---- component/trie/domain_set.go | 5 ++ config/config.go | 73 +++++++++-------- constant/matcher.go | 11 +++ constant/metadata.go | 2 + constant/rule.go | 6 -- dns/policy.go | 8 +- dns/resolver.go | 18 ++--- rules/common/geoip.go | 135 +++++++++++++++++++++----------- rules/common/geosite.go | 12 ++- rules/common/ipasn.go | 7 +- rules/provider/domain_set.go | 40 ---------- rules/provider/ipcidr_set.go | 40 ---------- rules/provider/rule_set.go | 14 ++++ 17 files changed, 211 insertions(+), 211 deletions(-) create mode 100644 constant/matcher.go delete mode 100644 rules/provider/domain_set.go delete mode 100644 rules/provider/ipcidr_set.go diff --git a/component/cidr/ipcidr_set.go b/component/cidr/ipcidr_set.go index 00ad7259..4bde9671 100644 --- a/component/cidr/ipcidr_set.go +++ b/component/cidr/ipcidr_set.go @@ -46,6 +46,14 @@ func (set *IpCidrSet) IsContain(ip netip.Addr) bool { return set.ToIPSet().Contains(ip.WithZone("")) } +// MatchIp implements C.IpMatcher +func (set *IpCidrSet) MatchIp(ip netip.Addr) bool { + if set.IsEmpty() { + return false + } + return set.IsContain(ip) +} + func (set *IpCidrSet) Merge() error { var b netipx.IPSetBuilder b.AddSet(set.ToIPSet()) diff --git a/component/fakeip/pool.go b/component/fakeip/pool.go index 8096a868..e2c10722 100644 --- a/component/fakeip/pool.go +++ b/component/fakeip/pool.go @@ -35,7 +35,7 @@ type Pool struct { offset netip.Addr cycle bool mux sync.Mutex - host []C.Rule + host []C.DomainMatcher ipnet netip.Prefix store store } @@ -66,8 +66,8 @@ func (p *Pool) LookBack(ip netip.Addr) (string, bool) { // ShouldSkipped return if domain should be skipped func (p *Pool) ShouldSkipped(domain string) bool { - for _, rule := range p.host { - if match, _ := rule.Match(&C.Metadata{Host: domain}); match { + for _, matcher := range p.host { + if matcher.MatchDomain(domain) { return true } } @@ -156,7 +156,7 @@ func (p *Pool) restoreState() { type Options struct { IPNet netip.Prefix - Host []C.Rule + Host []C.DomainMatcher // Size sets the maximum number of entries in memory // and does not work if Persistence is true diff --git a/component/fakeip/pool_test.go b/component/fakeip/pool_test.go index c2e46584..1d4fa05f 100644 --- a/component/fakeip/pool_test.go +++ b/component/fakeip/pool_test.go @@ -10,7 +10,6 @@ import ( "github.com/metacubex/mihomo/component/profile/cachefile" "github.com/metacubex/mihomo/component/trie" C "github.com/metacubex/mihomo/constant" - RP "github.com/metacubex/mihomo/rules/provider" "github.com/metacubex/bbolt" "github.com/stretchr/testify/assert" @@ -157,7 +156,7 @@ func TestPool_Skip(t *testing.T) { pools, tempfile, err := createPools(Options{ IPNet: ipnet, Size: 10, - Host: []C.Rule{RP.NewDomainSet(tree.NewDomainSet(), "")}, + Host: []C.DomainMatcher{tree.NewDomainSet()}, }) assert.Nil(t, err) defer os.Remove(tempfile) diff --git a/component/sniffer/dispatcher.go b/component/sniffer/dispatcher.go index 8c9f692d..4d13ddd0 100644 --- a/component/sniffer/dispatcher.go +++ b/component/sniffer/dispatcher.go @@ -22,23 +22,23 @@ var ( type Dispatcher struct { enable bool sniffers map[sniffer.Sniffer]SnifferConfig - forceDomain []C.Rule - skipSrcAddress []C.Rule - skipDstAddress []C.Rule - skipDomain []C.Rule + forceDomain []C.DomainMatcher + skipSrcAddress []C.IpMatcher + skipDstAddress []C.IpMatcher + skipDomain []C.DomainMatcher skipList *lru.LruCache[netip.AddrPort, uint8] forceDnsMapping bool parsePureIp bool } func (sd *Dispatcher) shouldOverride(metadata *C.Metadata) bool { - for _, rule := range sd.skipDstAddress { - if ok, _ := rule.Match(&C.Metadata{DstIP: metadata.DstIP}); ok { + for _, matcher := range sd.skipDstAddress { + if matcher.MatchIp(metadata.DstIP) { return false } } - for _, rule := range sd.skipSrcAddress { - if ok, _ := rule.Match(&C.Metadata{DstIP: metadata.SrcIP}); ok { + for _, matcher := range sd.skipSrcAddress { + if matcher.MatchIp(metadata.SrcIP) { return false } } @@ -48,8 +48,8 @@ func (sd *Dispatcher) shouldOverride(metadata *C.Metadata) bool { if metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping { return true } - for _, rule := range sd.forceDomain { - if ok, _ := rule.Match(&C.Metadata{Host: metadata.Host}); ok { + for _, matcher := range sd.forceDomain { + if matcher.MatchDomain(metadata.Host) { return true } } @@ -112,8 +112,8 @@ func (sd *Dispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) bool return false } - for _, rule := range sd.skipDomain { - if ok, _ := rule.Match(&C.Metadata{Host: host}); ok { + for _, matcher := range sd.skipDomain { + if matcher.MatchDomain(host) { log.Debugln("[Sniffer] Skip sni[%s]", host) return false } @@ -200,10 +200,10 @@ func (sd *Dispatcher) cacheSniffFailed(metadata *C.Metadata) { type Config struct { Enable bool Sniffers map[sniffer.Type]SnifferConfig - ForceDomain []C.Rule - SkipSrcAddress []C.Rule - SkipDstAddress []C.Rule - SkipDomain []C.Rule + ForceDomain []C.DomainMatcher + SkipSrcAddress []C.IpMatcher + SkipDstAddress []C.IpMatcher + SkipDomain []C.DomainMatcher ForceDnsMapping bool ParsePureIp bool } diff --git a/component/trie/domain_set.go b/component/trie/domain_set.go index 7778d133..3fd8041f 100644 --- a/component/trie/domain_set.go +++ b/component/trie/domain_set.go @@ -172,6 +172,11 @@ func (ss *DomainSet) Foreach(f func(key string) bool) { }) } +// MatchDomain implements C.DomainMatcher +func (ss *DomainSet) MatchDomain(domain string) bool { + return ss.Has(domain) +} + func setBit(bm *[]uint64, i int, v int) { for i>>6 >= len(*bm) { *bm = append(*bm, 0) diff --git a/config/config.go b/config/config.go index 7197f524..cd8eb469 100644 --- a/config/config.go +++ b/config/config.go @@ -143,8 +143,8 @@ type DNS struct { UseSystemHosts bool NameServer []dns.NameServer Fallback []dns.NameServer - FallbackIPFilter []C.Rule - FallbackDomainFilter []C.Rule + FallbackIPFilter []C.IpMatcher + FallbackDomainFilter []C.DomainMatcher Listen string EnhancedMode C.DNSMode DefaultNameserver []dns.NameServer @@ -640,7 +640,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } config.Hosts = hosts - dnsCfg, err := parseDNS(rawCfg, hosts, rules, ruleProviders) + dnsCfg, err := parseDNS(rawCfg, hosts, ruleProviders) if err != nil { return nil, err } @@ -1297,7 +1297,7 @@ func parsePureDNSServer(server string) string { } } -func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider, respectRules bool, preferH3 bool) ([]dns.Policy, error) { +func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], ruleProviders map[string]providerTypes.RuleProvider, respectRules bool, preferH3 bool) ([]dns.Policy, error) { var policy []dns.Policy re := regexp.MustCompile(`[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?`) @@ -1350,18 +1350,18 @@ func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], rules [ if strings.HasPrefix(domain, "rule-set:") { domainSetName := domain[9:] - rule, err := parseDomainRuleSet(domainSetName, "dns.nameserver-policy", ruleProviders) + matcher, err := parseDomainRuleSet(domainSetName, "dns.nameserver-policy", ruleProviders) if err != nil { return nil, err } - policy[idx] = dns.Policy{Rule: rule, NameServers: nameservers} + policy[idx] = dns.Policy{Matcher: matcher, NameServers: nameservers} } else if strings.HasPrefix(domain, "geosite:") { country := domain[8:] - rule, err := RC.NewGEOSITE(country, "dns.nameserver-policy") + matcher, err := RC.NewGEOSITE(country, "dns.nameserver-policy") if err != nil { return nil, err } - policy[idx] = dns.Policy{Rule: rule, NameServers: nameservers} + policy[idx] = dns.Policy{Matcher: matcher, NameServers: nameservers} } else { if _, valid := trie.ValidAndSplitDomain(domain); !valid { return nil, fmt.Errorf("DNS ResoverRule invalid domain: %s", domain) @@ -1372,7 +1372,7 @@ func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], rules [ return policy, nil } -func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider) (*DNS, error) { +func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], ruleProviders map[string]providerTypes.RuleProvider) (*DNS, error) { cfg := rawCfg.DNS if cfg.Enable && len(cfg.NameServer) == 0 { return nil, fmt.Errorf("if DNS configuration is turned on, NameServer cannot be empty") @@ -1400,7 +1400,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul return nil, err } - if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, rules, ruleProviders, cfg.RespectRules, cfg.PreferH3); err != nil { + if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, ruleProviders, cfg.RespectRules, cfg.PreferH3); err != nil { return nil, err } @@ -1467,14 +1467,13 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul dnsCfg.FakeIPRange = pool } - var rule C.Rule if len(cfg.Fallback) != 0 { if cfg.FallbackFilter.GeoIP { - rule, err = RC.NewGEOIP(cfg.FallbackFilter.GeoIPCode, "dns.fallback-filter.geoip", false, true) + matcher, err := RC.NewGEOIP(cfg.FallbackFilter.GeoIPCode, "dns.fallback-filter.geoip", false, true) if err != nil { return nil, fmt.Errorf("load GeoIP dns fallback filter error, %w", err) } - dnsCfg.FallbackIPFilter = append(dnsCfg.FallbackIPFilter, rule) + dnsCfg.FallbackIPFilter = append(dnsCfg.FallbackIPFilter, matcher) } if len(cfg.FallbackFilter.IPCIDR) > 0 { cidrSet := cidr.NewIpCidrSet() @@ -1488,8 +1487,8 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul if err != nil { return nil, err } - rule = RP.NewIpCidrSet(cidrSet, "dns.fallback-filter.ipcidr") - dnsCfg.FallbackIPFilter = append(dnsCfg.FallbackIPFilter, rule) + matcher := cidrSet // dns.fallback-filter.ipcidr + dnsCfg.FallbackIPFilter = append(dnsCfg.FallbackIPFilter, matcher) } if len(cfg.FallbackFilter.Domain) > 0 { domainTrie := trie.New[struct{}]() @@ -1499,17 +1498,17 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul return nil, fmt.Errorf("DNS FallbackDomain[%d] format error: %w", idx, err) } } - rule = RP.NewDomainSet(domainTrie.NewDomainSet(), "dns.fallback-filter.domain") - dnsCfg.FallbackDomainFilter = append(dnsCfg.FallbackDomainFilter, rule) + matcher := domainTrie.NewDomainSet() // dns.fallback-filter.domain + dnsCfg.FallbackDomainFilter = append(dnsCfg.FallbackDomainFilter, matcher) } if len(cfg.FallbackFilter.GeoSite) > 0 { log.Warnln("replace fallback-filter.geosite with nameserver-policy, it will be removed in the future") for idx, geoSite := range cfg.FallbackFilter.GeoSite { - rule, err = RC.NewGEOSITE(geoSite, "dns.fallback-filter.geosite") + matcher, err := RC.NewGEOSITE(geoSite, "dns.fallback-filter.geosite") if err != nil { return nil, fmt.Errorf("DNS FallbackGeosite[%d] format error: %w", idx, err) } - dnsCfg.FallbackDomainFilter = append(dnsCfg.FallbackDomainFilter, rule) + dnsCfg.FallbackDomainFilter = append(dnsCfg.FallbackDomainFilter, matcher) } } } @@ -1701,8 +1700,8 @@ func parseSniffer(snifferRaw RawSniffer, ruleProviders map[string]providerTypes. return snifferConfig, nil } -func parseIPCIDR(addresses []string, cidrSet *cidr.IpCidrSet, adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (ipRules []C.Rule, err error) { - var rule C.Rule +func parseIPCIDR(addresses []string, cidrSet *cidr.IpCidrSet, adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (matchers []C.IpMatcher, err error) { + var matcher C.IpMatcher for _, ipcidr := range addresses { ipcidrLower := strings.ToLower(ipcidr) if strings.Contains(ipcidrLower, "geoip:") { @@ -1710,22 +1709,22 @@ func parseIPCIDR(addresses []string, cidrSet *cidr.IpCidrSet, adapterName string subkeys = subkeys[1:] subkeys = strings.Split(subkeys[0], ",") for _, country := range subkeys { - rule, err = RC.NewGEOIP(country, adapterName, false, false) + matcher, err = RC.NewGEOIP(country, adapterName, false, false) if err != nil { return nil, err } - ipRules = append(ipRules, rule) + matchers = append(matchers, matcher) } } else if strings.Contains(ipcidrLower, "rule-set:") { subkeys := strings.Split(ipcidr, ":") subkeys = subkeys[1:] subkeys = strings.Split(subkeys[0], ",") for _, domainSetName := range subkeys { - rule, err = parseIPRuleSet(domainSetName, adapterName, ruleProviders) + matcher, err = parseIPRuleSet(domainSetName, adapterName, ruleProviders) if err != nil { return nil, err } - ipRules = append(ipRules, rule) + matchers = append(matchers, matcher) } } else { if cidrSet == nil { @@ -1742,14 +1741,14 @@ func parseIPCIDR(addresses []string, cidrSet *cidr.IpCidrSet, adapterName string if err != nil { return nil, err } - rule = RP.NewIpCidrSet(cidrSet, adapterName) - ipRules = append(ipRules, rule) + matcher = cidrSet + matchers = append(matchers, matcher) } return } -func parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (domainRules []C.Rule, err error) { - var rule C.Rule +func parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (matchers []C.DomainMatcher, err error) { + var matcher C.DomainMatcher for _, domain := range domains { domainLower := strings.ToLower(domain) if strings.Contains(domainLower, "geosite:") { @@ -1757,22 +1756,22 @@ func parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], adapte subkeys = subkeys[1:] subkeys = strings.Split(subkeys[0], ",") for _, country := range subkeys { - rule, err = RC.NewGEOSITE(country, adapterName) + matcher, err = RC.NewGEOSITE(country, adapterName) if err != nil { return nil, err } - domainRules = append(domainRules, rule) + matchers = append(matchers, matcher) } } else if strings.Contains(domainLower, "rule-set:") { subkeys := strings.Split(domain, ":") subkeys = subkeys[1:] subkeys = strings.Split(subkeys[0], ",") for _, domainSetName := range subkeys { - rule, err = parseDomainRuleSet(domainSetName, adapterName, ruleProviders) + matcher, err = parseDomainRuleSet(domainSetName, adapterName, ruleProviders) if err != nil { return nil, err } - domainRules = append(domainRules, rule) + matchers = append(matchers, matcher) } } else { if domainTrie == nil { @@ -1785,13 +1784,13 @@ func parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], adapte } } if !domainTrie.IsEmpty() { - rule = RP.NewDomainSet(domainTrie.NewDomainSet(), adapterName) - domainRules = append(domainRules, rule) + matcher = domainTrie.NewDomainSet() + matchers = append(matchers, matcher) } return } -func parseIPRuleSet(domainSetName string, adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (C.Rule, error) { +func parseIPRuleSet(domainSetName string, adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (C.IpMatcher, error) { if rp, ok := ruleProviders[domainSetName]; !ok { return nil, fmt.Errorf("not found rule-set: %s", domainSetName) } else { @@ -1806,7 +1805,7 @@ func parseIPRuleSet(domainSetName string, adapterName string, ruleProviders map[ return RP.NewRuleSet(domainSetName, adapterName, true) } -func parseDomainRuleSet(domainSetName string, adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (C.Rule, error) { +func parseDomainRuleSet(domainSetName string, adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (C.DomainMatcher, error) { if rp, ok := ruleProviders[domainSetName]; !ok { return nil, fmt.Errorf("not found rule-set: %s", domainSetName) } else { diff --git a/constant/matcher.go b/constant/matcher.go new file mode 100644 index 00000000..107f843d --- /dev/null +++ b/constant/matcher.go @@ -0,0 +1,11 @@ +package constant + +import "net/netip" + +type DomainMatcher interface { + MatchDomain(domain string) bool +} + +type IpMatcher interface { + MatchIp(ip netip.Addr) bool +} diff --git a/constant/metadata.go b/constant/metadata.go index 381e2dd4..04537688 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -133,7 +133,9 @@ type Metadata struct { Type Type `json:"type"` SrcIP netip.Addr `json:"sourceIP"` DstIP netip.Addr `json:"destinationIP"` + SrcGeoIP []string `json:"sourceGeoIP"` // can be nil if never queried, empty slice if got no result DstGeoIP []string `json:"destinationGeoIP"` // can be nil if never queried, empty slice if got no result + SrcIPASN string `json:"sourceIPASN"` DstIPASN string `json:"destinationIPASN"` SrcPort uint16 `json:"sourcePort,string"` // `,string` is used to compatible with old version json output DstPort uint16 `json:"destinationPort,string"` // `,string` is used to compatible with old version json output diff --git a/constant/rule.go b/constant/rule.go index 30cb2f8e..31702ddc 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -27,8 +27,6 @@ const ( ProcessNameRegex ProcessPathRegex RuleSet - DomainSet - IpCidrSet Network Uid SubRules @@ -92,10 +90,6 @@ func (rt RuleType) String() string { return "Match" case RuleSet: return "RuleSet" - case DomainSet: - return "DomainSet" - case IpCidrSet: - return "IpCidrSet" case Network: return "Network" case DSCP: diff --git a/dns/policy.go b/dns/policy.go index d872286c..50dc1719 100644 --- a/dns/policy.go +++ b/dns/policy.go @@ -21,13 +21,13 @@ func (p domainTriePolicy) Match(domain string) []dnsClient { return nil } -type domainRulePolicy struct { - rule C.Rule +type domainMatcherPolicy struct { + matcher C.DomainMatcher dnsClients []dnsClient } -func (p domainRulePolicy) Match(domain string) []dnsClient { - if ok, _ := p.rule.Match(&C.Metadata{Host: domain}); ok { +func (p domainMatcherPolicy) Match(domain string) []dnsClient { + if p.matcher.MatchDomain(domain) { return p.dnsClients } return nil diff --git a/dns/resolver.go b/dns/resolver.go index 3cc7a41e..232f3b33 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -42,8 +42,8 @@ type Resolver struct { hosts *trie.DomainTrie[resolver.HostValue] main []dnsClient fallback []dnsClient - fallbackDomainFilters []C.Rule - fallbackIPFilters []C.Rule + fallbackDomainFilters []C.DomainMatcher + fallbackIPFilters []C.IpMatcher group singleflight.Group[*D.Msg] cache dnsCache policy []dnsPolicy @@ -119,7 +119,7 @@ func (r *Resolver) LookupIPv6(ctx context.Context, host string) ([]netip.Addr, e func (r *Resolver) shouldIPFallback(ip netip.Addr) bool { for _, filter := range r.fallbackIPFilters { - if ok, _ := filter.Match(&C.Metadata{DstIP: ip}); ok { + if filter.MatchIp(ip) { return true } } @@ -275,7 +275,7 @@ func (r *Resolver) shouldOnlyQueryFallback(m *D.Msg) bool { } for _, df := range r.fallbackDomainFilters { - if ok, _ := df.Match(&C.Metadata{Host: domain}); ok { + if df.MatchDomain(domain) { return true } } @@ -398,7 +398,7 @@ func (ns NameServer) Equal(ns2 NameServer) bool { type Policy struct { Domain string - Rule C.Rule + Matcher C.DomainMatcher NameServers []NameServer } @@ -409,8 +409,8 @@ type Config struct { IPv6 bool IPv6Timeout uint EnhancedMode C.DNSMode - FallbackIPFilter []C.Rule - FallbackDomainFilter []C.Rule + FallbackIPFilter []C.IpMatcher + FallbackDomainFilter []C.DomainMatcher Pool *fakeip.Pool Hosts *trie.DomainTrie[resolver.HostValue] Policy []Policy @@ -495,8 +495,8 @@ func NewResolver(config Config) *Resolver { } for _, policy := range config.Policy { - if policy.Rule != nil { - insertPolicy(domainRulePolicy{rule: policy.Rule, dnsClients: cacheTransform(policy.NameServers)}) + if policy.Matcher != nil { + insertPolicy(domainMatcherPolicy{matcher: policy.Matcher, dnsClients: cacheTransform(policy.NameServers)}) } else { if triePolicy == nil { triePolicy = trie.New[[]dnsClient]() diff --git a/rules/common/geoip.go b/rules/common/geoip.go index 83925321..c4f7ecf3 100644 --- a/rules/common/geoip.go +++ b/rules/common/geoip.go @@ -3,6 +3,7 @@ package common import ( "errors" "fmt" + "net/netip" "strings" "github.com/metacubex/mihomo/component/geodata" @@ -11,6 +12,8 @@ import ( "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" + + "golang.org/x/exp/slices" ) type GEOIP struct { @@ -41,52 +44,84 @@ func (g *GEOIP) Match(metadata *C.Metadata) (bool, string) { } if g.country == "lan" { - return ip.IsPrivate() || - ip.IsUnspecified() || - ip.IsLoopback() || - ip.IsMulticast() || - ip.IsLinkLocalUnicast() || - resolver.IsFakeBroadcastIP(ip), g.adapter + return g.isLan(ip), g.adapter } - for _, code := range metadata.DstGeoIP { - if g.country == code { - return true, g.adapter - } - } - - if !C.GeodataMode { + if C.GeodataMode { if g.isSourceIP { - codes := mmdb.IPInstance().LookupCode(ip.AsSlice()) - for _, code := range codes { - if g.country == code { - return true, g.adapter - } + if slices.Contains(metadata.SrcGeoIP, g.country) { + return true, g.adapter } - return false, g.adapter - } - - if metadata.DstGeoIP != nil { - return false, g.adapter - } - metadata.DstGeoIP = mmdb.IPInstance().LookupCode(ip.AsSlice()) - for _, code := range metadata.DstGeoIP { - if g.country == code { + } else { + if slices.Contains(metadata.DstGeoIP, g.country) { return true, g.adapter } } - return false, g.adapter + matcher, err := g.getIPMatcher() + if err != nil { + return false, "" + } + match := matcher.Match(ip) + if match { + if g.isSourceIP { + metadata.SrcGeoIP = append(metadata.SrcGeoIP, g.country) + } else { + metadata.DstGeoIP = append(metadata.DstGeoIP, g.country) + } + } + return match, g.adapter } - matcher, err := g.GetIPMatcher() - if err != nil { - return false, "" + if g.isSourceIP { + if metadata.SrcGeoIP != nil { + return slices.Contains(metadata.SrcGeoIP, g.country), g.adapter + } + } else { + if metadata.DstGeoIP != nil { + return slices.Contains(metadata.DstGeoIP, g.country), g.adapter + } } - match := matcher.Match(ip) - if match && !g.isSourceIP { - metadata.DstGeoIP = append(metadata.DstGeoIP, g.country) + codes := mmdb.IPInstance().LookupCode(ip.AsSlice()) + if g.isSourceIP { + metadata.SrcGeoIP = codes + } else { + metadata.DstGeoIP = codes } - return match, g.adapter + if slices.Contains(codes, g.country) { + return true, g.adapter + } + return false, "" +} + +// MatchIp implements C.IpMatcher +func (g *GEOIP) MatchIp(ip netip.Addr) bool { + if !ip.IsValid() { + return false + } + + if g.country == "lan" { + return g.isLan(ip) + } + + if C.GeodataMode { + matcher, err := g.getIPMatcher() + if err != nil { + return false + } + return matcher.Match(ip) + } + + codes := mmdb.IPInstance().LookupCode(ip.AsSlice()) + return slices.Contains(codes, g.country) +} + +func (g *GEOIP) isLan(ip netip.Addr) bool { + return ip.IsPrivate() || + ip.IsUnspecified() || + ip.IsLoopback() || + ip.IsMulticast() || + ip.IsLinkLocalUnicast() || + resolver.IsFakeBroadcastIP(ip) } func (g *GEOIP) Adapter() string { @@ -106,14 +141,19 @@ func (g *GEOIP) GetCountry() string { } func (g *GEOIP) GetIPMatcher() (router.IPMatcher, error) { - if g.geodata { - geoIPMatcher, err := geodata.LoadGeoIPMatcher(g.country) - if err != nil { - return nil, fmt.Errorf("[GeoIP] %w", err) - } - return geoIPMatcher, nil + if C.GeodataMode { + return g.getIPMatcher() } - return nil, errors.New("geoip country not set") + return nil, errors.New("not geodata mode") +} + +func (g *GEOIP) getIPMatcher() (router.IPMatcher, error) { + geoIPMatcher, err := geodata.LoadGeoIPMatcher(g.country) + if err != nil { + return nil, fmt.Errorf("[GeoIP] %w", err) + } + return geoIPMatcher, nil + } func (g *GEOIP) GetRecodeSize() int { @@ -141,12 +181,13 @@ func NewGEOIP(country string, adapter string, isSrc, noResolveIP bool) (*GEOIP, return geoip, nil } - geoip.geodata = true - geoIPMatcher, err := geoip.GetIPMatcher() // test load - if err != nil { - return nil, err + if C.GeodataMode { + geoIPMatcher, err := geoip.getIPMatcher() // test load + if err != nil { + return nil, err + } + log.Infoln("Finished initial GeoIP rule %s => %s, records: %d", country, adapter, geoIPMatcher.Count()) } - log.Infoln("Finished initial GeoIP rule %s => %s, records: %d", country, adapter, geoIPMatcher.Count()) return geoip, nil } diff --git a/rules/common/geosite.go b/rules/common/geosite.go index a728e991..851bc8a4 100644 --- a/rules/common/geosite.go +++ b/rules/common/geosite.go @@ -23,15 +23,19 @@ func (gs *GEOSITE) RuleType() C.RuleType { } func (gs *GEOSITE) Match(metadata *C.Metadata) (bool, string) { - domain := metadata.RuleHost() + return gs.MatchDomain(metadata.RuleHost()), gs.adapter +} + +// MatchDomain implements C.DomainMatcher +func (gs *GEOSITE) MatchDomain(domain string) bool { if len(domain) == 0 { - return false, "" + return false } matcher, err := gs.GetDomainMatcher() if err != nil { - return false, "" + return false } - return matcher.ApplyDomain(domain), gs.adapter + return matcher.ApplyDomain(domain) } func (gs *GEOSITE) Adapter() string { diff --git a/rules/common/ipasn.go b/rules/common/ipasn.go index df4b6531..bcff4e72 100644 --- a/rules/common/ipasn.go +++ b/rules/common/ipasn.go @@ -28,8 +28,11 @@ func (a *ASN) Match(metadata *C.Metadata) (bool, string) { result := mmdb.ASNInstance().LookupASN(ip.AsSlice()) asnNumber := strconv.FormatUint(uint64(result.AutonomousSystemNumber), 10) - if !a.isSourceIP { - metadata.DstIPASN = asnNumber + " " + result.AutonomousSystemOrganization + ipASN := asnNumber + " " + result.AutonomousSystemOrganization + if a.isSourceIP { + metadata.SrcIPASN = ipASN + } else { + metadata.DstIPASN = ipASN } match := a.asn == asnNumber diff --git a/rules/provider/domain_set.go b/rules/provider/domain_set.go deleted file mode 100644 index 573dd105..00000000 --- a/rules/provider/domain_set.go +++ /dev/null @@ -1,40 +0,0 @@ -package provider - -import ( - "github.com/metacubex/mihomo/component/trie" - C "github.com/metacubex/mihomo/constant" -) - -type DomainSet struct { - *domainStrategy - adapter string -} - -func (d *DomainSet) ProviderNames() []string { - return nil -} - -func (d *DomainSet) RuleType() C.RuleType { - return C.DomainSet -} - -func (d *DomainSet) Match(metadata *C.Metadata) (bool, string) { - return d.domainStrategy.Match(metadata), d.adapter -} - -func (d *DomainSet) Adapter() string { - return d.adapter -} - -func (d *DomainSet) Payload() string { - return "" -} - -func NewDomainSet(domainSet *trie.DomainSet, adapter string) *DomainSet { - return &DomainSet{ - domainStrategy: &domainStrategy{domainSet: domainSet}, - adapter: adapter, - } -} - -var _ C.Rule = (*DomainSet)(nil) diff --git a/rules/provider/ipcidr_set.go b/rules/provider/ipcidr_set.go deleted file mode 100644 index 348b6ab0..00000000 --- a/rules/provider/ipcidr_set.go +++ /dev/null @@ -1,40 +0,0 @@ -package provider - -import ( - "github.com/metacubex/mihomo/component/cidr" - C "github.com/metacubex/mihomo/constant" -) - -type IpCidrSet struct { - *ipcidrStrategy - adapter string -} - -func (d *IpCidrSet) ProviderNames() []string { - return nil -} - -func (d *IpCidrSet) RuleType() C.RuleType { - return C.IpCidrSet -} - -func (d *IpCidrSet) Match(metadata *C.Metadata) (bool, string) { - return d.ipcidrStrategy.Match(metadata), d.adapter -} - -func (d *IpCidrSet) Adapter() string { - return d.adapter -} - -func (d *IpCidrSet) Payload() string { - return "" -} - -func NewIpCidrSet(cidrSet *cidr.IpCidrSet, adapter string) *IpCidrSet { - return &IpCidrSet{ - ipcidrStrategy: &ipcidrStrategy{cidrSet: cidrSet}, - adapter: adapter, - } -} - -var _ C.Rule = (*IpCidrSet)(nil) diff --git a/rules/provider/rule_set.go b/rules/provider/rule_set.go index 04ab9943..23864e12 100644 --- a/rules/provider/rule_set.go +++ b/rules/provider/rule_set.go @@ -1,6 +1,8 @@ package provider import ( + "net/netip" + C "github.com/metacubex/mihomo/constant" P "github.com/metacubex/mihomo/constant/provider" "github.com/metacubex/mihomo/rules/common" @@ -35,6 +37,18 @@ func (rs *RuleSet) Match(metadata *C.Metadata) (bool, string) { return false, "" } +// MatchDomain implements C.DomainMatcher +func (rs *RuleSet) MatchDomain(domain string) bool { + ok, _ := rs.Match(&C.Metadata{Host: domain}) + return ok +} + +// MatchIp implements C.IpMatcher +func (rs *RuleSet) MatchIp(ip netip.Addr) bool { + ok, _ := rs.Match(&C.Metadata{DstIP: ip}) + return ok +} + func (rs *RuleSet) Adapter() string { return rs.adapter } From a96f72ade4d395e96e94d24d9b2b6261e21e6f98 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 29 Aug 2024 22:00:55 +0800 Subject: [PATCH 191/311] fix: geoip wrong matching logic in fallback-filter https://github.com/MetaCubeX/mihomo/issues/1478 --- config/config.go | 2 +- rules/common/geoip.go | 31 ++++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/config/config.go b/config/config.go index cd8eb469..370506d4 100644 --- a/config/config.go +++ b/config/config.go @@ -1473,7 +1473,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul if err != nil { return nil, fmt.Errorf("load GeoIP dns fallback filter error, %w", err) } - dnsCfg.FallbackIPFilter = append(dnsCfg.FallbackIPFilter, matcher) + dnsCfg.FallbackIPFilter = append(dnsCfg.FallbackIPFilter, matcher.DnsFallbackFilter()) } if len(cfg.FallbackFilter.IPCIDR) > 0 { cidrSet := cidr.NewIpCidrSet() diff --git a/rules/common/geoip.go b/rules/common/geoip.go index c4f7ecf3..7d6871c7 100644 --- a/rules/common/geoip.go +++ b/rules/common/geoip.go @@ -22,7 +22,6 @@ type GEOIP struct { adapter string noResolveIP bool isSourceIP bool - geodata bool } var _ C.Rule = (*GEOIP)(nil) @@ -115,6 +114,36 @@ func (g *GEOIP) MatchIp(ip netip.Addr) bool { return slices.Contains(codes, g.country) } +// MatchIp implements C.IpMatcher +func (g dnsFallbackFilter) MatchIp(ip netip.Addr) bool { + if !ip.IsValid() { + return false + } + + if g.isLan(ip) { // compatible with original behavior + return false + } + + if C.GeodataMode { + matcher, err := g.getIPMatcher() + if err != nil { + return false + } + return !matcher.Match(ip) + } + + codes := mmdb.IPInstance().LookupCode(ip.AsSlice()) + return !slices.Contains(codes, g.country) +} + +type dnsFallbackFilter struct { + *GEOIP +} + +func (g *GEOIP) DnsFallbackFilter() C.IpMatcher { // for dns.fallback-filter.geoip + return dnsFallbackFilter{GEOIP: g} +} + func (g *GEOIP) isLan(ip netip.Addr) bool { return ip.IsPrivate() || ip.IsUnspecified() || From 763a127287697fe471f0e643e3bd3f5abfad85e9 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 29 Aug 2024 23:49:16 +0800 Subject: [PATCH 192/311] feat: `RULE-SET` in rules support `,src` option should only be used with `ipcidr` behavior --- config/config.go | 4 ++-- constant/metadata.go | 7 +++++++ rules/common/base.go | 14 ++++++++------ rules/parser.go | 6 +++++- rules/provider/rule_set.go | 8 +++++++- 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/config/config.go b/config/config.go index 370506d4..c250d3ec 100644 --- a/config/config.go +++ b/config/config.go @@ -1802,7 +1802,7 @@ func parseIPRuleSet(domainSetName string, adapterName string, ruleProviders map[ default: } } - return RP.NewRuleSet(domainSetName, adapterName, true) + return RP.NewRuleSet(domainSetName, adapterName, false, true) } func parseDomainRuleSet(domainSetName string, adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (C.DomainMatcher, error) { @@ -1817,5 +1817,5 @@ func parseDomainRuleSet(domainSetName string, adapterName string, ruleProviders default: } } - return RP.NewRuleSet(domainSetName, adapterName, true) + return RP.NewRuleSet(domainSetName, adapterName, false, true) } diff --git a/constant/metadata.go b/constant/metadata.go index 04537688..54362989 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -302,3 +302,10 @@ func (m *Metadata) SetRemoteAddress(rawAddress string) error { return nil } + +func (m *Metadata) SwapSrcDst() { + m.SrcIP, m.DstIP = m.DstIP, m.SrcIP + m.SrcPort, m.DstPort = m.DstPort, m.SrcPort + m.SrcIPASN, m.DstIPASN = m.DstIPASN, m.SrcIPASN + m.SrcGeoIP, m.DstGeoIP = m.DstGeoIP, m.SrcGeoIP +} diff --git a/rules/common/base.go b/rules/common/base.go index 670df1d9..04e3aec5 100644 --- a/rules/common/base.go +++ b/rules/common/base.go @@ -2,11 +2,14 @@ package common import ( "errors" + + "golang.org/x/exp/slices" ) var ( errPayload = errors.New("payloadRule error") noResolve = "no-resolve" + src = "src" ) type Base struct { @@ -23,10 +26,9 @@ func (b *Base) ShouldResolveIP() bool { func (b *Base) ProviderNames() []string { return nil } func HasNoResolve(params []string) bool { - for _, p := range params { - if p == noResolve { - return true - } - } - return false + return slices.Contains(params, noResolve) +} + +func HasSrc(params []string) bool { + return slices.Contains(params, src) } diff --git a/rules/parser.go b/rules/parser.go index 9b1f5520..2189cda9 100644 --- a/rules/parser.go +++ b/rules/parser.go @@ -77,7 +77,11 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string] parsed, parseErr = logic.NewNOT(payload, target, ParseRule) case "RULE-SET": noResolve := RC.HasNoResolve(params) - parsed, parseErr = RP.NewRuleSet(payload, target, noResolve) + isSrc := RC.HasSrc(params) + if isSrc { + noResolve = true + } + parsed, parseErr = RP.NewRuleSet(payload, target, isSrc, noResolve) case "MATCH": parsed = RC.NewMatch(target) parseErr = nil diff --git a/rules/provider/rule_set.go b/rules/provider/rule_set.go index 23864e12..2ad0bd3d 100644 --- a/rules/provider/rule_set.go +++ b/rules/provider/rule_set.go @@ -12,6 +12,7 @@ type RuleSet struct { *common.Base ruleProviderName string adapter string + isSrc bool noResolveIP bool shouldFindProcess bool } @@ -32,6 +33,10 @@ func (rs *RuleSet) RuleType() C.RuleType { func (rs *RuleSet) Match(metadata *C.Metadata) (bool, string) { if provider, ok := rs.getProvider(); ok { + if rs.isSrc { + metadata.SwapSrcDst() + defer metadata.SwapSrcDst() + } return provider.Match(metadata), rs.adapter } return false, "" @@ -76,11 +81,12 @@ func (rs *RuleSet) getProvider() (P.RuleProvider, bool) { return pp, ok } -func NewRuleSet(ruleProviderName string, adapter string, noResolveIP bool) (*RuleSet, error) { +func NewRuleSet(ruleProviderName string, adapter string, isSrc bool, noResolveIP bool) (*RuleSet, error) { rs := &RuleSet{ Base: &common.Base{}, ruleProviderName: ruleProviderName, adapter: adapter, + isSrc: isSrc, noResolveIP: noResolveIP, } return rs, nil From 38fd37108b22b0e937cae7743c562a2147c65cca Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 30 Aug 2024 00:04:28 +0800 Subject: [PATCH 193/311] feat: `GEOIP`,`IP-ASN`,`IP-CIDR`,`IP-CIDR6` and `IP-SUFFIX` in rules support `,src` option keep the same writing style as `RULE-SET` --- rules/common/base.go | 22 ++++++++++++++-------- rules/parser.go | 22 +++++++++------------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/rules/common/base.go b/rules/common/base.go index 04e3aec5..496bcaee 100644 --- a/rules/common/base.go +++ b/rules/common/base.go @@ -8,8 +8,12 @@ import ( var ( errPayload = errors.New("payloadRule error") - noResolve = "no-resolve" - src = "src" +) + +// params +var ( + NoResolve = "no-resolve" + Src = "src" ) type Base struct { @@ -25,10 +29,12 @@ func (b *Base) ShouldResolveIP() bool { func (b *Base) ProviderNames() []string { return nil } -func HasNoResolve(params []string) bool { - return slices.Contains(params, noResolve) -} - -func HasSrc(params []string) bool { - return slices.Contains(params, src) +func ParseParams(params []string) (isSrc bool, noResolve bool) { + isSrc = slices.Contains(params, Src) + if isSrc { + noResolve = true + } else { + noResolve = slices.Contains(params, NoResolve) + } + return } diff --git a/rules/parser.go b/rules/parser.go index 2189cda9..4f7ddbe1 100644 --- a/rules/parser.go +++ b/rules/parser.go @@ -22,23 +22,23 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string] case "GEOSITE": parsed, parseErr = RC.NewGEOSITE(payload, target) case "GEOIP": - noResolve := RC.HasNoResolve(params) - parsed, parseErr = RC.NewGEOIP(payload, target, false, noResolve) + isSrc, noResolve := RC.ParseParams(params) + parsed, parseErr = RC.NewGEOIP(payload, target, isSrc, noResolve) case "SRC-GEOIP": parsed, parseErr = RC.NewGEOIP(payload, target, true, true) case "IP-ASN": - noResolve := RC.HasNoResolve(params) - parsed, parseErr = RC.NewIPASN(payload, target, false, noResolve) + isSrc, noResolve := RC.ParseParams(params) + parsed, parseErr = RC.NewIPASN(payload, target, isSrc, noResolve) case "SRC-IP-ASN": parsed, parseErr = RC.NewIPASN(payload, target, true, true) case "IP-CIDR", "IP-CIDR6": - noResolve := RC.HasNoResolve(params) - parsed, parseErr = RC.NewIPCIDR(payload, target, RC.WithIPCIDRNoResolve(noResolve)) + isSrc, noResolve := RC.ParseParams(params) + parsed, parseErr = RC.NewIPCIDR(payload, target, RC.WithIPCIDRSourceIP(isSrc), RC.WithIPCIDRNoResolve(noResolve)) case "SRC-IP-CIDR": parsed, parseErr = RC.NewIPCIDR(payload, target, RC.WithIPCIDRSourceIP(true), RC.WithIPCIDRNoResolve(true)) case "IP-SUFFIX": - noResolve := RC.HasNoResolve(params) - parsed, parseErr = RC.NewIPSuffix(payload, target, false, noResolve) + isSrc, noResolve := RC.ParseParams(params) + parsed, parseErr = RC.NewIPSuffix(payload, target, isSrc, noResolve) case "SRC-IP-SUFFIX": parsed, parseErr = RC.NewIPSuffix(payload, target, true, true) case "SRC-PORT": @@ -76,11 +76,7 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string] case "NOT": parsed, parseErr = logic.NewNOT(payload, target, ParseRule) case "RULE-SET": - noResolve := RC.HasNoResolve(params) - isSrc := RC.HasSrc(params) - if isSrc { - noResolve = true - } + isSrc, noResolve := RC.ParseParams(params) parsed, parseErr = RP.NewRuleSet(payload, target, isSrc, noResolve) case "MATCH": parsed = RC.NewMatch(target) From 08ac9a3fae545d521626ecb6cb9b290e51a394b0 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 30 Aug 2024 20:09:50 +0800 Subject: [PATCH 194/311] fix: tfo ipv6 addr zone --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6a43c730..75aea3f0 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 github.com/metacubex/sing-wireguard v0.0.0-20240826061955-1e4e67afe5cd - github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d + github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785 github.com/metacubex/utls v1.6.6 github.com/miekg/dns v1.1.62 github.com/mroth/weightedrand/v2 v2.1.0 diff --git a/go.sum b/go.sum index 3b059db0..40ca0264 100644 --- a/go.sum +++ b/go.sum @@ -122,8 +122,8 @@ github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosq github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240826061955-1e4e67afe5cd h1:r7alry8u4qlUFLNMwGvG1A8ZcfPM6AMSmrm6E2yKdB4= github.com/metacubex/sing-wireguard v0.0.0-20240826061955-1e4e67afe5cd/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= -github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d h1:j9LtzkYstLFoNvXW824QQeN7Y26uPL5249kzWKbzO9U= -github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= +github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785 h1:NNmI+ZV0DzNuqaAInRQuZFLHlWVuyHeow8jYpdKjHjo= +github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8= github.com/metacubex/utls v1.6.6/go.mod h1:+WLFUnXjcpdxXCnyX25nggw8C6YonZ8zOK2Zm/oRvdo= github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= From f6164ac1959945cc5366bf3207ec41db13966a52 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 31 Aug 2024 09:59:48 +0800 Subject: [PATCH 195/311] feat: add `fake-ip-filter-mode` in `dns` https://github.com/MetaCubeX/mihomo/issues/1479 --- component/fakeip/pool.go | 11 +++++++ component/fakeip/pool_test.go | 22 +++++++++++++ config/config.go | 3 ++ constant/dns.go | 62 ++++++++++++++++++++++++++++++++++- docs/config.yaml | 3 ++ 5 files changed, 100 insertions(+), 1 deletion(-) diff --git a/component/fakeip/pool.go b/component/fakeip/pool.go index e2c10722..12c06332 100644 --- a/component/fakeip/pool.go +++ b/component/fakeip/pool.go @@ -36,6 +36,7 @@ type Pool struct { cycle bool mux sync.Mutex host []C.DomainMatcher + mode C.FilterMode ipnet netip.Prefix store store } @@ -66,6 +67,14 @@ func (p *Pool) LookBack(ip netip.Addr) (string, bool) { // ShouldSkipped return if domain should be skipped func (p *Pool) ShouldSkipped(domain string) bool { + should := p.shouldSkipped(domain) + if p.mode == C.FilterWhiteList { + return !should + } + return should +} + +func (p *Pool) shouldSkipped(domain string) bool { for _, matcher := range p.host { if matcher.MatchDomain(domain) { return true @@ -157,6 +166,7 @@ func (p *Pool) restoreState() { type Options struct { IPNet netip.Prefix Host []C.DomainMatcher + Mode C.FilterMode // Size sets the maximum number of entries in memory // and does not work if Persistence is true @@ -187,6 +197,7 @@ func New(options Options) (*Pool, error) { offset: first.Prev(), cycle: false, host: options.Host, + mode: options.Mode, ipnet: options.IPNet, } if options.Persistence { diff --git a/component/fakeip/pool_test.go b/component/fakeip/pool_test.go index 1d4fa05f..923cca57 100644 --- a/component/fakeip/pool_test.go +++ b/component/fakeip/pool_test.go @@ -164,6 +164,28 @@ func TestPool_Skip(t *testing.T) { for _, pool := range pools { assert.True(t, pool.ShouldSkipped("example.com")) assert.False(t, pool.ShouldSkipped("foo.com")) + assert.False(t, pool.shouldSkipped("baz.com")) + } +} + +func TestPool_SkipWhiteList(t *testing.T) { + ipnet := netip.MustParsePrefix("192.168.0.1/29") + tree := trie.New[struct{}]() + assert.NoError(t, tree.Insert("example.com", struct{}{})) + assert.False(t, tree.IsEmpty()) + pools, tempfile, err := createPools(Options{ + IPNet: ipnet, + Size: 10, + Host: []C.DomainMatcher{tree.NewDomainSet()}, + Mode: C.FilterWhiteList, + }) + assert.Nil(t, err) + defer os.Remove(tempfile) + + for _, pool := range pools { + assert.False(t, pool.ShouldSkipped("example.com")) + assert.True(t, pool.ShouldSkipped("foo.com")) + assert.True(t, pool.ShouldSkipped("baz.com")) } } diff --git a/config/config.go b/config/config.go index c250d3ec..ed30bfe4 100644 --- a/config/config.go +++ b/config/config.go @@ -205,6 +205,7 @@ type RawDNS struct { EnhancedMode C.DNSMode `yaml:"enhanced-mode" json:"enhanced-mode"` FakeIPRange string `yaml:"fake-ip-range" json:"fake-ip-range"` FakeIPFilter []string `yaml:"fake-ip-filter" json:"fake-ip-filter"` + FakeIPFilterMode C.FilterMode `yaml:"fake-ip-filter-mode" json:"fake-ip-filter-mode"` DefaultNameserver []string `yaml:"default-nameserver" json:"default-nameserver"` CacheAlgorithm string `yaml:"cache-algorithm" json:"cache-algorithm"` NameServerPolicy *orderedmap.OrderedMap[string, any] `yaml:"nameserver-policy" json:"nameserver-policy"` @@ -474,6 +475,7 @@ func DefaultRawConfig() *RawConfig { "www.msftnsci.com", "www.msftconnecttest.com", }, + FakeIPFilterMode: C.FilterBlackList, }, NTP: RawNTP{ Enable: false, @@ -1458,6 +1460,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul IPNet: fakeIPRange, Size: 1000, Host: host, + Mode: cfg.FakeIPFilterMode, Persistence: rawCfg.Profile.StoreFakeIP, }) if err != nil { diff --git a/constant/dns.go b/constant/dns.go index 3d97d97b..8d038a6b 100644 --- a/constant/dns.go +++ b/constant/dns.go @@ -43,7 +43,9 @@ func (e DNSMode) MarshalYAML() (any, error) { // UnmarshalJSON unserialize EnhancedMode with json func (e *DNSMode) UnmarshalJSON(data []byte) error { var tp string - json.Unmarshal(data, &tp) + if err := json.Unmarshal(data, &tp); err != nil { + return err + } mode, exist := DNSModeMapping[tp] if !exist { return errors.New("invalid mode") @@ -115,6 +117,64 @@ func NewDNSPrefer(prefer string) DNSPrefer { } } +// FilterModeMapping is a mapping for FilterMode enum +var FilterModeMapping = map[string]FilterMode{ + FilterBlackList.String(): FilterBlackList, + FilterWhiteList.String(): FilterWhiteList, +} + +type FilterMode int + +const ( + FilterBlackList FilterMode = iota + FilterWhiteList +) + +func (e FilterMode) String() string { + switch e { + case FilterBlackList: + return "blacklist" + case FilterWhiteList: + return "whitelist" + default: + return "unknown" + } +} + +func (e FilterMode) MarshalYAML() (interface{}, error) { + return e.String(), nil +} + +func (e *FilterMode) UnmarshalYAML(unmarshal func(interface{}) error) error { + var tp string + if err := unmarshal(&tp); err != nil { + return err + } + mode, exist := FilterModeMapping[tp] + if !exist { + return errors.New("invalid mode") + } + *e = mode + return nil +} + +func (e FilterMode) MarshalJSON() ([]byte, error) { + return json.Marshal(e.String()) +} + +func (e *FilterMode) UnmarshalJSON(data []byte) error { + var tp string + if err := json.Unmarshal(data, &tp); err != nil { + return err + } + mode, exist := FilterModeMapping[tp] + if !exist { + return errors.New("invalid mode") + } + *e = mode + return nil +} + type HTTPVersion string const ( diff --git a/docs/config.yaml b/docs/config.yaml index bb60b286..1da37841 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -249,6 +249,9 @@ dns: - rule-set:fakeip-filter # fakeip-filter 为 geosite 中名为 fakeip-filter 的分类(需要自行保证该分类存在) - geosite:fakeip-filter + # 配置fake-ip-filter的匹配模式,默认为blacklist,即如果匹配成功不返回fake-ip + # 可设置为whitelist,即只有匹配成功才返回fake-ip + fake-ip-filter-mode: blacklist # use-hosts: true # 查询 hosts From 6306c6b58070038c99a8765576a52a3fabc06537 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 31 Aug 2024 19:23:40 +0800 Subject: [PATCH 196/311] chore: add `route.ApplyConfig` for CMFA --- hub/executor/executor.go | 2 +- hub/hub.go | 46 +++-- hub/route/server.go | 238 ++++++++++++++----------- listener/sing_tun/server_android.go | 2 + listener/sing_tun/server_notandroid.go | 2 +- main.go | 2 +- 6 files changed, 174 insertions(+), 118 deletions(-) diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 442666f0..e7e9b72c 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -77,7 +77,7 @@ func ParseWithBytes(buf []byte) (*config.Config, error) { return config.Parse(buf) } -// ApplyConfig dispatch configure to all parts +// ApplyConfig dispatch configure to all parts without ExternalController func ApplyConfig(cfg *config.Config, force bool) { mux.Lock() defer mux.Unlock() diff --git a/hub/hub.go b/hub/hub.go index 2a53b197..d439d32e 100644 --- a/hub/hub.go +++ b/hub/hub.go @@ -1,7 +1,10 @@ package hub import ( + "strings" + "github.com/metacubex/mihomo/config" + "github.com/metacubex/mihomo/constant/features" "github.com/metacubex/mihomo/hub/executor" "github.com/metacubex/mihomo/hub/route" "github.com/metacubex/mihomo/log" @@ -33,6 +36,33 @@ func WithSecret(secret string) Option { } } +// ApplyConfig dispatch configure to all parts include ExternalController +func ApplyConfig(cfg *config.Config) { + applyRoute(cfg) + executor.ApplyConfig(cfg, true) +} + +func applyRoute(cfg *config.Config) { + if features.CMFA && strings.HasSuffix(cfg.Controller.ExternalUI, ":0") { + // CMFA have set its default override value to end with ":0" for security. + // so we direct return at here + return + } + if cfg.Controller.ExternalUI != "" { + route.SetUIPath(cfg.Controller.ExternalUI) + } + route.ReCreateServer(&route.Config{ + Addr: cfg.Controller.ExternalController, + TLSAddr: cfg.Controller.ExternalControllerTLS, + UnixAddr: cfg.Controller.ExternalControllerUnix, + Secret: cfg.Controller.Secret, + Certificate: cfg.TLS.Certificate, + PrivateKey: cfg.TLS.PrivateKey, + DohServer: cfg.Controller.ExternalDohServer, + IsDebug: cfg.General.LogLevel == log.DEBUG, + }) +} + // Parse call at the beginning of mihomo func Parse(options ...Option) error { cfg, err := executor.Parse() @@ -44,20 +74,6 @@ func Parse(options ...Option) error { option(cfg) } - if cfg.Controller.ExternalUI != "" { - route.SetUIPath(cfg.Controller.ExternalUI) - } - - if cfg.Controller.ExternalController != "" { - go route.Start(cfg.Controller.ExternalController, cfg.Controller.ExternalControllerTLS, - cfg.Controller.Secret, cfg.TLS.Certificate, cfg.TLS.PrivateKey, cfg.Controller.ExternalDohServer, - cfg.General.LogLevel == log.DEBUG) - } - - if cfg.Controller.ExternalControllerUnix != "" { - go route.StartUnix(cfg.Controller.ExternalControllerUnix, cfg.Controller.ExternalDohServer, cfg.General.LogLevel == log.DEBUG) - } - - executor.ApplyConfig(cfg, true) + ApplyConfig(cfg) return nil } diff --git a/hub/route/server.go b/hub/route/server.go index 165c7c69..1605b4bf 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -30,10 +30,11 @@ import ( ) var ( - serverSecret = "" - serverAddr = "" - uiPath = "" + + httpServer *http.Server + tlsServer *http.Server + unixServer *http.Server ) type Traffic struct { @@ -46,11 +47,28 @@ type Memory struct { OSLimit uint64 `json:"oslimit"` // maybe we need it in the future } +type Config struct { + Addr string + TLSAddr string + UnixAddr string + Secret string + Certificate string + PrivateKey string + DohServer string + IsDebug bool +} + +func ReCreateServer(cfg *Config) { + go start(cfg) + go startTLS(cfg) + go startUnix(cfg) +} + func SetUIPath(path string) { uiPath = C.Path.Resolve(path) } -func router(isDebug bool, withAuth bool, dohServer string) *chi.Mux { +func router(isDebug bool, secret string, dohServer string) *chi.Mux { r := chi.NewRouter() corsM := cors.New(cors.Options{ AllowedOrigins: []string{"*"}, @@ -72,8 +90,8 @@ func router(isDebug bool, withAuth bool, dohServer string) *chi.Mux { }()) } r.Group(func(r chi.Router) { - if withAuth { - r.Use(authentication) + if secret != "" { + r.Use(authentication(secret)) } r.Get("/", hello) r.Get("/logs", getLogs) @@ -111,88 +129,111 @@ func router(isDebug bool, withAuth bool, dohServer string) *chi.Mux { return r } -func Start(addr string, tlsAddr string, secret string, - certificate, privateKey string, dohServer string, isDebug bool) { - if serverAddr != "" { - return +func start(cfg *Config) { + // first stop existing server + if httpServer != nil { + _ = httpServer.Close() + httpServer = nil } - serverAddr = addr - serverSecret = secret + // handle addr + if len(cfg.Addr) > 0 { + l, err := inbound.Listen("tcp", cfg.Addr) + if err != nil { + log.Errorln("External controller listen error: %s", err) + return + } + log.Infoln("RESTful API listening at: %s", l.Addr().String()) - if len(tlsAddr) > 0 { - go func() { - c, err := CN.ParseCert(certificate, privateKey, C.Path) - if err != nil { - log.Errorln("External controller tls listen error: %s", err) - return - } - - l, err := inbound.Listen("tcp", tlsAddr) - if err != nil { - log.Errorln("External controller tls listen error: %s", err) - return - } - - serverAddr = l.Addr().String() - log.Infoln("RESTful API tls listening at: %s", serverAddr) - tlsServe := &http.Server{ - Handler: router(isDebug, true, dohServer), - TLSConfig: &tls.Config{ - Certificates: []tls.Certificate{c}, - }, - } - if err = tlsServe.ServeTLS(l, "", ""); err != nil { - log.Errorln("External controller tls serve error: %s", err) - } - }() + server := &http.Server{ + Handler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer), + } + if err = server.Serve(l); err != nil { + log.Errorln("External controller serve error: %s", err) + } + httpServer = server } - - l, err := inbound.Listen("tcp", addr) - if err != nil { - log.Errorln("External controller listen error: %s", err) - return - } - serverAddr = l.Addr().String() - log.Infoln("RESTful API listening at: %s", serverAddr) - - if err = http.Serve(l, router(isDebug, true, dohServer)); err != nil { - log.Errorln("External controller serve error: %s", err) - } - } -func StartUnix(addr string, dohServer string, isDebug bool) { - addr = C.Path.Resolve(addr) +func startTLS(cfg *Config) { + // first stop existing server + if tlsServer != nil { + _ = tlsServer.Close() + tlsServer = nil + } - dir := filepath.Dir(addr) - if _, err := os.Stat(dir); os.IsNotExist(err) { - if err := os.MkdirAll(dir, 0o755); err != nil { + // handle tlsAddr + if len(cfg.TLSAddr) > 0 { + c, err := CN.ParseCert(cfg.Certificate, cfg.PrivateKey, C.Path) + if err != nil { + log.Errorln("External controller tls listen error: %s", err) + return + } + + l, err := inbound.Listen("tcp", cfg.TLSAddr) + if err != nil { + log.Errorln("External controller tls listen error: %s", err) + return + } + + log.Infoln("RESTful API tls listening at: %s", l.Addr().String()) + server := &http.Server{ + Handler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer), + TLSConfig: &tls.Config{ + Certificates: []tls.Certificate{c}, + }, + } + if err = server.ServeTLS(l, "", ""); err != nil { + log.Errorln("External controller tls serve error: %s", err) + } + tlsServer = server + } +} + +func startUnix(cfg *Config) { + // first stop existing server + if unixServer != nil { + _ = unixServer.Close() + unixServer = nil + } + + // handle addr + if len(cfg.UnixAddr) > 0 { + addr := C.Path.Resolve(cfg.UnixAddr) + + dir := filepath.Dir(addr) + if _, err := os.Stat(dir); os.IsNotExist(err) { + if err := os.MkdirAll(dir, 0o755); err != nil { + log.Errorln("External controller unix listen error: %s", err) + return + } + } + + // https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ + // + // Note: As mentioned above in the ‘security’ section, when a socket binds a socket to a valid pathname address, + // a socket file is created within the filesystem. On Linux, the application is expected to unlink + // (see the notes section in the man page for AF_UNIX) before any other socket can be bound to the same address. + // The same applies to Windows unix sockets, except that, DeleteFile (or any other file delete API) + // should be used to delete the socket file prior to calling bind with the same path. + _ = syscall.Unlink(addr) + + l, err := inbound.Listen("unix", addr) + if err != nil { log.Errorln("External controller unix listen error: %s", err) return } + log.Infoln("RESTful API unix listening at: %s", l.Addr().String()) + + server := &http.Server{ + Handler: router(cfg.IsDebug, "", cfg.DohServer), + } + if err = server.Serve(l); err != nil { + log.Errorln("External controller unix serve error: %s", err) + } + unixServer = server } - // https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ - // - // Note: As mentioned above in the ‘security’ section, when a socket binds a socket to a valid pathname address, - // a socket file is created within the filesystem. On Linux, the application is expected to unlink - // (see the notes section in the man page for AF_UNIX) before any other socket can be bound to the same address. - // The same applies to Windows unix sockets, except that, DeleteFile (or any other file delete API) - // should be used to delete the socket file prior to calling bind with the same path. - _ = syscall.Unlink(addr) - - l, err := inbound.Listen("unix", addr) - if err != nil { - log.Errorln("External controller unix listen error: %s", err) - return - } - serverAddr = l.Addr().String() - log.Infoln("RESTful API unix listening at: %s", serverAddr) - - if err = http.Serve(l, router(isDebug, false, dohServer)); err != nil { - log.Errorln("External controller unix serve error: %s", err) - } } func setPrivateNetworkAccess(next http.Handler) http.Handler { @@ -210,38 +251,35 @@ func safeEuqal(a, b string) bool { return subtle.ConstantTimeCompare(aBuf, bBuf) == 1 } -func authentication(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - if serverSecret == "" { - next.ServeHTTP(w, r) - return - } +func authentication(secret string) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + // Browser websocket not support custom header + if r.Header.Get("Upgrade") == "websocket" && r.URL.Query().Get("token") != "" { + token := r.URL.Query().Get("token") + if !safeEuqal(token, secret) { + render.Status(r, http.StatusUnauthorized) + render.JSON(w, r, ErrUnauthorized) + return + } + next.ServeHTTP(w, r) + return + } - // Browser websocket not support custom header - if r.Header.Get("Upgrade") == "websocket" && r.URL.Query().Get("token") != "" { - token := r.URL.Query().Get("token") - if !safeEuqal(token, serverSecret) { + header := r.Header.Get("Authorization") + bearer, token, found := strings.Cut(header, " ") + + hasInvalidHeader := bearer != "Bearer" + hasInvalidSecret := !found || !safeEuqal(token, secret) + if hasInvalidHeader || hasInvalidSecret { render.Status(r, http.StatusUnauthorized) render.JSON(w, r, ErrUnauthorized) return } next.ServeHTTP(w, r) - return } - - header := r.Header.Get("Authorization") - bearer, token, found := strings.Cut(header, " ") - - hasInvalidHeader := bearer != "Bearer" - hasInvalidSecret := !found || !safeEuqal(token, serverSecret) - if hasInvalidHeader || hasInvalidSecret { - render.Status(r, http.StatusUnauthorized) - render.JSON(w, r, ErrUnauthorized) - return - } - next.ServeHTTP(w, r) + return http.HandlerFunc(fn) } - return http.HandlerFunc(fn) } func hello(w http.ResponseWriter, r *http.Request) { diff --git a/listener/sing_tun/server_android.go b/listener/sing_tun/server_android.go index bd5c4bd0..d8240534 100644 --- a/listener/sing_tun/server_android.go +++ b/listener/sing_tun/server_android.go @@ -1,3 +1,5 @@ +//go:build android && !cmfa + package sing_tun import ( diff --git a/listener/sing_tun/server_notandroid.go b/listener/sing_tun/server_notandroid.go index 6b30ee03..10fd3997 100644 --- a/listener/sing_tun/server_notandroid.go +++ b/listener/sing_tun/server_notandroid.go @@ -1,4 +1,4 @@ -//go:build !android +//go:build !android || cmfa package sing_tun diff --git a/main.go b/main.go index 06a04ca1..c7a7acbc 100644 --- a/main.go +++ b/main.go @@ -135,7 +135,7 @@ func main() { return case <-hupSign: if cfg, err := executor.ParseWithPath(C.Path.Config()); err == nil { - executor.ApplyConfig(cfg, true) + hub.ApplyConfig(cfg) } else { log.Errorln("Parse config error: %s", err.Error()) } From 802267fb5b0a17bf7915b78abbb80afa407d8815 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sat, 31 Aug 2024 23:38:31 +0800 Subject: [PATCH 197/311] ci: better release --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b62e9820..5ba2ef6f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -387,18 +387,18 @@ jobs: git fetch --tags echo "PREVERSION=$(git describe --tags --abbrev=0 HEAD)" >> $GITHUB_ENV - - name: Merge Alpha branch into Meta + - name: Force push Alpha branch to Meta run: | git config --global user.email "github-actions[bot]@users.noreply.github.com" git config --global user.name "github-actions[bot]" git fetch origin Alpha:Alpha - git merge Alpha - git push origin Meta + git push origin Alpha:Meta --force env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Tag the commit + - name: Tag the commit on Alpha run: | + git checkout Alpha git tag ${{ github.event.inputs.version }} git push origin ${{ github.event.inputs.version }} env: From 56fe7d5304f624e3df3c22113e8134004d136d63 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Mon, 2 Sep 2024 11:17:35 +0800 Subject: [PATCH 198/311] chore: clean up update_ui code --- component/updater/update_ui.go | 42 +++++++++++++++------------------- config/config.go | 37 +++++++++++------------------- hub/executor/executor.go | 8 +++---- hub/route/upgrade.go | 13 +++-------- 4 files changed, 38 insertions(+), 62 deletions(-) diff --git a/component/updater/update_ui.go b/component/updater/update_ui.go index a43648a9..b29bee9f 100644 --- a/component/updater/update_ui.go +++ b/component/updater/update_ui.go @@ -2,7 +2,6 @@ package updater import ( "archive/zip" - "errors" "fmt" "io" "os" @@ -12,23 +11,26 @@ import ( "sync" C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/log" ) var ( - ExternalUIURL string - ExternalUIPath string - ExternalUIFolder string - ExternalUIName string -) -var ( - ErrIncompleteConf = errors.New("ExternalUI configure incomplete") + ExternalUIURL string + ExternalUIPath string + AutoUpdateUI bool ) + var xdMutex sync.Mutex func UpdateUI() error { xdMutex.Lock() defer xdMutex.Unlock() + err := prepareUIPath() + if err != nil { + return fmt.Errorf("prepare UI path failed: %w", err) + } + data, err := downloadForBytes(ExternalUIURL) if err != nil { return fmt.Errorf("can't download file: %w", err) @@ -40,7 +42,7 @@ func UpdateUI() error { } defer os.Remove(saved) - err = cleanup(ExternalUIFolder) + err = cleanup(ExternalUIPath) if err != nil { if !os.IsNotExist(err) { return fmt.Errorf("cleanup exist file error: %w", err) @@ -52,27 +54,19 @@ func UpdateUI() error { return fmt.Errorf("can't extract zip file: %w", err) } - err = os.Rename(unzipFolder, ExternalUIFolder) + err = os.Rename(unzipFolder, ExternalUIPath) if err != nil { - return fmt.Errorf("can't rename folder: %w", err) + return fmt.Errorf("rename UI folder failed: %w", err) } return nil } -func PrepareUIPath() error { - if ExternalUIPath == "" || ExternalUIURL == "" { - return ErrIncompleteConf - } - - 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 - } +func prepareUIPath() error { + if _, err := os.Stat(ExternalUIPath); os.IsNotExist(err) { + log.Infoln("dir %s does not exist, creating", ExternalUIPath) + if err := os.MkdirAll(ExternalUIPath, os.ModePerm); err != nil { + log.Warnln("create dir %s error: %s", ExternalUIPath, err) } - } else { - ExternalUIFolder = ExternalUIPath } return nil } diff --git a/config/config.go b/config/config.go index ed30bfe4..4490ebf6 100644 --- a/config/config.go +++ b/config/config.go @@ -7,7 +7,6 @@ import ( "net" "net/netip" "net/url" - "os" "path" "regexp" "strings" @@ -704,33 +703,23 @@ func parseGeneral(cfg *RawConfig) (*General, error) { } N.DisableKeepAlive = cfg.DisableKeepAlive - updater.ExternalUIPath = cfg.ExternalUI // checkout externalUI exist - if updater.ExternalUIPath != "" { - updater.ExternalUIPath = C.Path.Resolve(updater.ExternalUIPath) - if _, err := os.Stat(updater.ExternalUIPath); os.IsNotExist(err) { - defaultUIpath := path.Join(C.Path.HomeDir(), "ui") - log.Warnln("external-ui: %s does not exist, creating folder in %s", updater.ExternalUIPath, defaultUIpath) - if err := os.MkdirAll(defaultUIpath, os.ModePerm); err != nil { - return nil, err - } - updater.ExternalUIPath = defaultUIpath - cfg.ExternalUI = defaultUIpath - } - } - // checkout UIpath/name exist - if cfg.ExternalUIName != "" { - updater.ExternalUIName = cfg.ExternalUIName + if cfg.ExternalUI != "" { + updater.AutoUpdateUI = true + updater.ExternalUIPath = C.Path.Resolve(cfg.ExternalUI) } else { - updater.ExternalUIFolder = updater.ExternalUIPath - } - if cfg.ExternalUIURL != "" { - updater.ExternalUIURL = cfg.ExternalUIURL + // default externalUI path + updater.ExternalUIPath = path.Join(C.Path.HomeDir(), "ui") } - err := updater.PrepareUIPath() - if err != nil { - log.Errorln("PrepareUIPath error: %s", err) + // checkout UIpath/name exist + if cfg.ExternalUIName != "" { + updater.AutoUpdateUI = true + updater.ExternalUIPath = path.Join(updater.ExternalUIPath, cfg.ExternalUIName) + } + + if cfg.ExternalUIURL != "" { + updater.ExternalUIURL = cfg.ExternalUIURL } return &General{ diff --git a/hub/executor/executor.go b/hub/executor/executor.go index e7e9b72c..c83b254c 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -381,12 +381,12 @@ func updateTunnels(tunnels []LC.Tunnel) { } func initExternalUI() { - if updater.ExternalUIFolder != "" { - dirEntries, _ := os.ReadDir(updater.ExternalUIFolder) + if updater.AutoUpdateUI { + dirEntries, _ := os.ReadDir(updater.ExternalUIPath) if len(dirEntries) > 0 { - log.Infoln("UI already exists") + log.Infoln("UI already exists, skip downloading") } else { - log.Infoln("UI not exists, downloading") + log.Infoln("External UI downloading ...") updater.UpdateUI() } } diff --git a/hub/route/upgrade.go b/hub/route/upgrade.go index db00af5c..76ac26a1 100644 --- a/hub/route/upgrade.go +++ b/hub/route/upgrade.go @@ -1,7 +1,6 @@ package route import ( - "errors" "fmt" "net/http" "os" @@ -50,15 +49,9 @@ func upgradeCore(w http.ResponseWriter, r *http.Request) { func updateUI(w http.ResponseWriter, r *http.Request) { err := updater.UpdateUI() if err != nil { - if errors.Is(err, updater.ErrIncompleteConf) { - 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))) - } + log.Warnln("%s", err) + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, newError(fmt.Sprintf("%s", err))) return } From 43f21c0b412b7a8701fe7a2ea6510c5b985a53d6 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Mon, 2 Sep 2024 16:18:28 +0800 Subject: [PATCH 199/311] fix: fallback cannot be unfixed --- hub/route/groups.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/hub/route/groups.go b/hub/route/groups.go index 30dec5f0..c4e9501f 100644 --- a/hub/route/groups.go +++ b/hub/route/groups.go @@ -60,9 +60,15 @@ 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("") + switch proxy.(*adapter.Proxy).Type() { + case C.URLTest: + if urlTestGroup, ok := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.URLTest); ok { + urlTestGroup.ForceSet("") + } + case C.Fallback: + if fallbackGroup, ok := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.Fallback); ok { + fallbackGroup.ForceSet("") + } } if proxy.(*adapter.Proxy).Type() != C.Selector { From faaa90f8a621499ba86f8734a05bb80edb38ada9 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Tue, 3 Sep 2024 17:55:11 +0800 Subject: [PATCH 200/311] feat: Allows passing in base64-encoded configuration strings --- hub/hub.go | 12 ++++++++-- main.go | 65 +++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 57 insertions(+), 20 deletions(-) diff --git a/hub/hub.go b/hub/hub.go index d439d32e..04662a7b 100644 --- a/hub/hub.go +++ b/hub/hub.go @@ -64,8 +64,16 @@ func applyRoute(cfg *config.Config) { } // Parse call at the beginning of mihomo -func Parse(options ...Option) error { - cfg, err := executor.Parse() +func Parse(configBytes []byte, options ...Option) error { + var cfg *config.Config + var err error + + if len(configBytes) != 0 { + cfg, err = executor.ParseWithBytes(configBytes) + } else { + cfg, err = executor.Parse() + } + if err != nil { return err } diff --git a/main.go b/main.go index c7a7acbc..26f1d2c0 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "encoding/base64" "flag" "fmt" "os" @@ -28,6 +29,8 @@ var ( geodataMode bool homeDir string configFile string + configString string + configBytes []byte externalUI string externalController string externalControllerUnix string @@ -37,6 +40,7 @@ var ( func init() { flag.StringVar(&homeDir, "d", os.Getenv("CLASH_HOME_DIR"), "set configuration directory") flag.StringVar(&configFile, "f", os.Getenv("CLASH_CONFIG_FILE"), "specify configuration file") + flag.StringVar(&configString, "config", os.Getenv("CLASH_CONFIG_STRING"), "specify base64-encoded configuration string") flag.StringVar(&externalUI, "ext-ui", os.Getenv("CLASH_OVERRIDE_EXTERNAL_UI_DIR"), "override external ui directory") flag.StringVar(&externalController, "ext-ctl", os.Getenv("CLASH_OVERRIDE_EXTERNAL_CONTROLLER"), "override external controller address") flag.StringVar(&externalControllerUnix, "ext-ctl-unix", os.Getenv("CLASH_OVERRIDE_EXTERNAL_CONTROLLER_UNIX"), "override external controller unix address") @@ -73,29 +77,46 @@ func main() { C.SetHomeDir(homeDir) } - if configFile != "" { - if !filepath.IsAbs(configFile) { - currentDir, _ := os.Getwd() - configFile = filepath.Join(currentDir, configFile) - } - } else { - configFile = filepath.Join(C.Path.HomeDir(), C.Path.Config()) - } - C.SetConfig(configFile) - if geodataMode { C.GeodataMode = true } - if err := config.Init(C.Path.HomeDir()); err != nil { - log.Fatalln("Initial configuration directory error: %s", err.Error()) + if configString != "" { + var err error + configBytes, err = base64.StdEncoding.DecodeString(configString) + if err != nil { + log.Fatalln("Initial configuration error: %s", err.Error()) + return + } + } else { + if configFile != "" { + if !filepath.IsAbs(configFile) { + currentDir, _ := os.Getwd() + configFile = filepath.Join(currentDir, configFile) + } + } else { + configFile = filepath.Join(C.Path.HomeDir(), C.Path.Config()) + } + C.SetConfig(configFile) + + if err := config.Init(C.Path.HomeDir()); err != nil { + log.Fatalln("Initial configuration directory error: %s", err.Error()) + } } if testConfig { - if _, err := executor.Parse(); err != nil { - log.Errorln(err.Error()) - fmt.Printf("configuration file %s test failed\n", C.Path.Config()) - os.Exit(1) + if len(configBytes) != 0 { + if _, err := executor.ParseWithBytes(configBytes); err != nil { + log.Errorln(err.Error()) + fmt.Println("configuration test failed") + os.Exit(1) + } + } else { + if _, err := executor.Parse(); err != nil { + log.Errorln(err.Error()) + fmt.Printf("configuration file %s test failed\n", C.Path.Config()) + os.Exit(1) + } } fmt.Printf("configuration file %s test is successful\n", C.Path.Config()) return @@ -115,7 +136,7 @@ func main() { options = append(options, hub.WithSecret(secret)) } - if err := hub.Parse(options...); err != nil { + if err := hub.Parse(configBytes, options...); err != nil { log.Fatalln("Parse config error: %s", err.Error()) } @@ -134,11 +155,19 @@ func main() { case <-termSign: return case <-hupSign: - if cfg, err := executor.ParseWithPath(C.Path.Config()); err == nil { + var cfg *config.Config + var err error + if configString != "" { + cfg, err = executor.ParseWithBytes(configBytes) + } else { + cfg, err = executor.ParseWithPath(C.Path.Config()) + } + if err == nil { hub.ApplyConfig(cfg) } else { log.Errorln("Parse config error: %s", err.Error()) } + } } } From ade4234615e7747ff79e98b27ff117a05c8110fa Mon Sep 17 00:00:00 2001 From: H1JK Date: Sat, 7 Sep 2024 20:22:21 +0800 Subject: [PATCH 201/311] chore: mrs use best compression --- rules/provider/mrs_converter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/provider/mrs_converter.go b/rules/provider/mrs_converter.go index edc24e7e..dbbe51cb 100644 --- a/rules/provider/mrs_converter.go +++ b/rules/provider/mrs_converter.go @@ -34,7 +34,7 @@ func ConvertToMrs(buf []byte, behavior P.RuleBehavior, format P.RuleFormat, w io } var encoder *zstd.Encoder - encoder, err = zstd.NewWriter(w) + encoder, err = zstd.NewWriter(w, zstd.WithEncoderLevel(zstd.SpeedBestCompression)) if err != nil { return err } From 8940bdd56fcfedcf17f14e6e0939784cc5094370 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 9 Sep 2024 08:35:59 +0800 Subject: [PATCH 202/311] chore: better pool_test --- component/fakeip/pool_test.go | 60 +++++++++++++++++------------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/component/fakeip/pool_test.go b/component/fakeip/pool_test.go index 923cca57..ee607b68 100644 --- a/component/fakeip/pool_test.go +++ b/component/fakeip/pool_test.go @@ -63,13 +63,13 @@ func TestPool_Basic(t *testing.T) { last := pool.Lookup("bar.com") bar, exist := pool.LookBack(last) - assert.True(t, first == netip.AddrFrom4([4]byte{192, 168, 0, 4})) - assert.True(t, pool.Lookup("foo.com") == netip.AddrFrom4([4]byte{192, 168, 0, 4})) - assert.True(t, last == netip.AddrFrom4([4]byte{192, 168, 0, 5})) + assert.Equal(t, first, netip.AddrFrom4([4]byte{192, 168, 0, 4})) + assert.Equal(t, pool.Lookup("foo.com"), netip.AddrFrom4([4]byte{192, 168, 0, 4})) + assert.Equal(t, last, netip.AddrFrom4([4]byte{192, 168, 0, 5})) assert.True(t, exist) assert.Equal(t, bar, "bar.com") - assert.True(t, pool.Gateway() == netip.AddrFrom4([4]byte{192, 168, 0, 1})) - assert.True(t, pool.Broadcast() == netip.AddrFrom4([4]byte{192, 168, 0, 15})) + assert.Equal(t, pool.Gateway(), netip.AddrFrom4([4]byte{192, 168, 0, 1})) + assert.Equal(t, pool.Broadcast(), netip.AddrFrom4([4]byte{192, 168, 0, 15})) assert.Equal(t, pool.IPNet().String(), ipnet.String()) assert.True(t, pool.Exist(netip.AddrFrom4([4]byte{192, 168, 0, 5}))) assert.False(t, pool.Exist(netip.AddrFrom4([4]byte{192, 168, 0, 6}))) @@ -91,13 +91,13 @@ func TestPool_BasicV6(t *testing.T) { last := pool.Lookup("bar.com") bar, exist := pool.LookBack(last) - assert.True(t, first == netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8804")) - assert.True(t, pool.Lookup("foo.com") == netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8804")) - assert.True(t, last == netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8805")) + assert.Equal(t, first, netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8804")) + assert.Equal(t, pool.Lookup("foo.com"), netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8804")) + assert.Equal(t, last, netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8805")) assert.True(t, exist) assert.Equal(t, bar, "bar.com") - assert.True(t, pool.Gateway() == netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8801")) - assert.True(t, pool.Broadcast() == netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8bff")) + assert.Equal(t, pool.Gateway(), netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8801")) + assert.Equal(t, pool.Broadcast(), netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8bff")) assert.Equal(t, pool.IPNet().String(), ipnet.String()) assert.True(t, pool.Exist(netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8805"))) assert.False(t, pool.Exist(netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8806"))) @@ -143,8 +143,8 @@ func TestPool_CycleUsed(t *testing.T) { } baz := pool.Lookup("baz.com") next := pool.Lookup("foo.com") - assert.True(t, foo == baz) - assert.True(t, next == bar) + assert.Equal(t, foo, baz) + assert.Equal(t, next, bar) } } @@ -201,7 +201,7 @@ func TestPool_MaxCacheSize(t *testing.T) { pool.Lookup("baz.com") next := pool.Lookup("foo.com") - assert.False(t, first == next) + assert.NotEqual(t, first, next) } func TestPool_DoubleMapping(t *testing.T) { @@ -231,7 +231,7 @@ func TestPool_DoubleMapping(t *testing.T) { assert.False(t, bazExist) assert.True(t, barExist) - assert.False(t, bazIP == newBazIP) + assert.NotEqual(t, bazIP, newBazIP) } func TestPool_Clone(t *testing.T) { @@ -243,8 +243,8 @@ func TestPool_Clone(t *testing.T) { first := pool.Lookup("foo.com") last := pool.Lookup("bar.com") - assert.True(t, first == netip.AddrFrom4([4]byte{192, 168, 0, 4})) - assert.True(t, last == netip.AddrFrom4([4]byte{192, 168, 0, 5})) + assert.Equal(t, first, netip.AddrFrom4([4]byte{192, 168, 0, 4})) + assert.Equal(t, last, netip.AddrFrom4([4]byte{192, 168, 0, 5})) newPool, _ := New(Options{ IPNet: ipnet, @@ -289,13 +289,13 @@ func TestPool_FlushFileCache(t *testing.T) { baz := pool.Lookup("foo.com") nero := pool.Lookup("foo.com") - assert.True(t, foo == fox) - assert.True(t, foo == next) - assert.False(t, foo == baz) - assert.True(t, bar == bax) - assert.True(t, bar == baz) - assert.False(t, bar == next) - assert.True(t, baz == nero) + assert.Equal(t, foo, fox) + assert.Equal(t, foo, next) + assert.NotEqual(t, foo, baz) + assert.Equal(t, bar, bax) + assert.Equal(t, bar, baz) + assert.NotEqual(t, bar, next) + assert.Equal(t, baz, nero) } } @@ -318,11 +318,11 @@ func TestPool_FlushMemoryCache(t *testing.T) { baz := pool.Lookup("foo.com") nero := pool.Lookup("foo.com") - assert.True(t, foo == fox) - assert.True(t, foo == next) - assert.False(t, foo == baz) - assert.True(t, bar == bax) - assert.True(t, bar == baz) - assert.False(t, bar == next) - assert.True(t, baz == nero) + assert.Equal(t, foo, fox) + assert.Equal(t, foo, next) + assert.NotEqual(t, foo, baz) + assert.Equal(t, bar, bax) + assert.Equal(t, bar, baz) + assert.NotEqual(t, bar, next) + assert.Equal(t, baz, nero) } From dc29514fb6a2ee10424a38f631183f8f83355df7 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 9 Sep 2024 09:08:24 +0800 Subject: [PATCH 203/311] chore: typo --- component/dialer/dialer.go | 4 ++-- component/dialer/socket_hook.go | 4 ++-- component/process/process.go | 4 ++-- docs/config.yaml | 4 ++-- listener/http/server.go | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 2a39508f..41f79b8e 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -84,7 +84,7 @@ func ListenPacket(ctx context.Context, network, address string, rAddrPort netip. if cfg.addrReuse { addrReuseToListenConfig(lc) } - if DefaultSocketHook != nil { // ignore interfaceName, routingMark when DefaultSocketHook not null (in CFMA) + if DefaultSocketHook != nil { // ignore interfaceName, routingMark when DefaultSocketHook not null (in CMFA) socketHookToListenConfig(lc) } else { if cfg.interfaceName != "" { @@ -148,7 +148,7 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po setMultiPathTCP(dialer) } - if DefaultSocketHook != nil { // ignore interfaceName, routingMark and tfo when DefaultSocketHook not null (in CFMA) + if DefaultSocketHook != nil { // ignore interfaceName, routingMark and tfo when DefaultSocketHook not null (in CMFA) socketHookToToDialer(dialer) } else { if opt.interfaceName != "" { diff --git a/component/dialer/socket_hook.go b/component/dialer/socket_hook.go index 7a2ea432..f8605525 100644 --- a/component/dialer/socket_hook.go +++ b/component/dialer/socket_hook.go @@ -7,11 +7,11 @@ import ( ) // SocketControl -// never change type traits because it's used in CFMA +// never change type traits because it's used in CMFA type SocketControl func(network, address string, conn syscall.RawConn) error // DefaultSocketHook -// never change type traits because it's used in CFMA +// never change type traits because it's used in CMFA var DefaultSocketHook SocketControl func socketHookToToDialer(dialer *net.Dialer) { diff --git a/component/process/process.go b/component/process/process.go index 84020c4d..464f5a79 100644 --- a/component/process/process.go +++ b/component/process/process.go @@ -23,11 +23,11 @@ func FindProcessName(network string, srcIP netip.Addr, srcPort int) (uint32, str } // PackageNameResolver -// never change type traits because it's used in CFMA +// never change type traits because it's used in CMFA type PackageNameResolver func(metadata *C.Metadata) (string, error) // DefaultPackageNameResolver -// never change type traits because it's used in CFMA +// never change type traits because it's used in CMFA var DefaultPackageNameResolver PackageNameResolver func FindPackageName(metadata *C.Metadata) (string, error) { diff --git a/docs/config.yaml b/docs/config.yaml index 1da37841..78e62d12 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -955,12 +955,12 @@ rule-providers: # 对于behavior=domain: # - format=yaml 可以通过“mihomo convert-ruleset domain yaml XXX.yaml XXX.mrs”转换到mrs格式 # - format=text 可以通过“mihomo convert-ruleset domain text XXX.text XXX.mrs”转换到mrs格式 - # - XXX.mrs 可以通过"mihomo convert-ruleset domain mrs XXX.mrs XXX.text"转换回text格式(暂不支持转换回ymal格式) + # - XXX.mrs 可以通过"mihomo convert-ruleset domain mrs XXX.mrs XXX.text"转换回text格式(暂不支持转换回yaml格式) # # 对于behavior=ipcidr: # - format=yaml 可以通过“mihomo convert-ruleset ipcidr yaml XXX.yaml XXX.mrs”转换到mrs格式 # - format=text 可以通过“mihomo convert-ruleset ipcidr text XXX.text XXX.mrs”转换到mrs格式 - # - XXX.mrs 可以通过"mihomo convert-ruleset ipcidr mrs XXX.mrs XXX.text"转换回text格式(暂不支持转换回ymal格式) + # - XXX.mrs 可以通过"mihomo convert-ruleset ipcidr mrs XXX.mrs XXX.text"转换回text格式(暂不支持转换回yaml格式) # type: http url: "url" diff --git a/listener/http/server.go b/listener/http/server.go index 74e117f2..48f12dc5 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -37,7 +37,7 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener } // NewWithAuthenticate -// never change type traits because it's used in CFMA +// never change type traits because it's used in CMFA func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additions ...inbound.Addition) (*Listener, error) { getAuth := authStore.Authenticator if !authenticate { From b1301b1b419b4494bef968def73fb06e8af4b20f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 9 Sep 2024 09:10:22 +0800 Subject: [PATCH 204/311] chore: update quic-go to 0.47.0 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 75aea3f0..7121901b 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 github.com/metacubex/chacha v0.1.0 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 - github.com/metacubex/quic-go v0.46.1-0.20240807232329-1c6cb2d67f58 + github.com/metacubex/quic-go v0.47.1-0.20240909010619-6b38f24bfcc4 github.com/metacubex/randv2 v0.2.0 github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 github.com/metacubex/sing-shadowsocks v0.2.8 diff --git a/go.sum b/go.sum index 40ca0264..3b1d2b50 100644 --- a/go.sum +++ b/go.sum @@ -104,8 +104,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-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= -github.com/metacubex/quic-go v0.46.1-0.20240807232329-1c6cb2d67f58 h1:T6OxROLZBr9SOQxN5TzUslv81hEREy/dEgaUKVjaG7U= -github.com/metacubex/quic-go v0.46.1-0.20240807232329-1c6cb2d67f58/go.mod h1:Yza2H7Ax1rxWPUcJx0vW+oAt9EsPuSiyQFhFabUPzwU= +github.com/metacubex/quic-go v0.47.1-0.20240909010619-6b38f24bfcc4 h1:CgdUBRxmNlxEGkp35HwvgQ10jwOOUJKWdOxpi8yWi8o= +github.com/metacubex/quic-go v0.47.1-0.20240909010619-6b38f24bfcc4/go.mod h1:Y7yRGqFE6UQL/3aKPYmiYdjfVkeujJaStP4+jiZMcN8= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297 h1:YG/JkwGPbca5rUtEMHIu8ZuqzR7BSVm1iqY8hNoMeMA= From 595a575cdebaf3ea8806e74b35f895906286b23e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 9 Sep 2024 09:15:37 +0800 Subject: [PATCH 205/311] chore: add Count for ProxyProvider --- adapter/provider/provider.go | 8 ++++++++ constant/provider/interface.go | 1 + 2 files changed, 9 insertions(+) diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index a99c1d96..923cddba 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -98,6 +98,10 @@ func (pp *proxySetProvider) Proxies() []C.Proxy { return pp.proxies } +func (pp *proxySetProvider) Count() int { + return len(pp.proxies) +} + func (pp *proxySetProvider) Touch() { pp.healthCheck.touch() } @@ -267,6 +271,10 @@ func (cp *compatibleProvider) Proxies() []C.Proxy { return cp.proxies } +func (cp *compatibleProvider) Count() int { + return len(cp.proxies) +} + func (cp *compatibleProvider) Touch() { cp.healthCheck.touch() } diff --git a/constant/provider/interface.go b/constant/provider/interface.go index 911f774a..880bdadf 100644 --- a/constant/provider/interface.go +++ b/constant/provider/interface.go @@ -71,6 +71,7 @@ type Provider interface { type ProxyProvider interface { Provider Proxies() []constant.Proxy + Count() int // Touch is used to inform the provider that the proxy is actually being used while getting the list of proxies. // Commonly used in DialContext and DialPacketConn Touch() From ef244b896abf4d4afaf1e2e5713683d2055b49b6 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 9 Sep 2024 10:05:38 +0800 Subject: [PATCH 206/311] chore: update geo in a batch --- component/geodata/utils.go | 5 +- component/updater/update_geo.go | 148 ++++++++++++++++++++------------ 2 files changed, 95 insertions(+), 58 deletions(-) diff --git a/component/geodata/utils.go b/component/geodata/utils.go index a16e255e..04c4293f 100644 --- a/component/geodata/utils.go +++ b/component/geodata/utils.go @@ -209,8 +209,11 @@ func LoadGeoIPMatcher(country string) (router.IPMatcher, error) { return matcher, nil } -func ClearCache() { +func ClearGeoSiteCache() { loadGeoSiteMatcherListSF.Reset() loadGeoSiteMatcherSF.Reset() +} + +func ClearGeoIPCache() { loadGeoIPMatcherSF.Reset() } diff --git a/component/updater/update_geo.go b/component/updater/update_geo.go index 4d16c128..d2d1e28d 100644 --- a/component/updater/update_geo.go +++ b/component/updater/update_geo.go @@ -1,6 +1,7 @@ package updater import ( + "context" "errors" "fmt" "os" @@ -8,6 +9,7 @@ import ( "time" "github.com/metacubex/mihomo/common/atomic" + "github.com/metacubex/mihomo/common/batch" "github.com/metacubex/mihomo/component/geodata" _ "github.com/metacubex/mihomo/component/geodata/standard" "github.com/metacubex/mihomo/component/mmdb" @@ -18,68 +20,67 @@ import ( ) var ( - UpdatingGeo atomic.Bool + updatingGeo atomic.Bool ) -func updateGeoDatabases() error { - defer runtime.GC() - geoLoader, err := geodata.GetGeoDataLoader("standard") +func UpdateMMDB() (err error) { + defer mmdb.ReloadIP() + data, err := downloadForBytes(C.MmdbUrl) if err != nil { - return err + return fmt.Errorf("can't download MMDB database file: %w", err) + } + instance, err := maxminddb.FromBytes(data) + if err != nil { + return fmt.Errorf("invalid MMDB database file: %s", err) + } + _ = instance.Close() + + mmdb.IPInstance().Reader.Close() // mmdb is loaded with mmap, so it needs to be closed before overwriting the file + if err = saveFile(data, C.Path.MMDB()); err != nil { + return fmt.Errorf("can't save MMDB database file: %w", err) + } + return nil +} + +func UpdateASN() (err error) { + defer mmdb.ReloadASN() + data, err := downloadForBytes(C.ASNUrl) + if err != nil { + return fmt.Errorf("can't download ASN database file: %w", err) } - if C.GeodataMode { - data, err := downloadForBytes(C.GeoIpUrl) - if err != nil { - return fmt.Errorf("can't download GeoIP database file: %w", err) - } - - if _, err = geoLoader.LoadIPByBytes(data, "cn"); err != nil { - return fmt.Errorf("invalid GeoIP database file: %s", err) - } - - if err = saveFile(data, C.Path.GeoIP()); err != nil { - return fmt.Errorf("can't save GeoIP database file: %w", err) - } - - } else { - defer mmdb.ReloadIP() - data, err := downloadForBytes(C.MmdbUrl) - if err != nil { - return fmt.Errorf("can't download MMDB database file: %w", err) - } - - instance, err := maxminddb.FromBytes(data) - if err != nil { - return fmt.Errorf("invalid MMDB database file: %s", err) - } - _ = instance.Close() - - mmdb.IPInstance().Reader.Close() // mmdb is loaded with mmap, so it needs to be closed before overwriting the file - if err = saveFile(data, C.Path.MMDB()); err != nil { - return fmt.Errorf("can't save MMDB database file: %w", err) - } + instance, err := maxminddb.FromBytes(data) + if err != nil { + return fmt.Errorf("invalid ASN database file: %s", err) } + _ = instance.Close() - if C.ASNEnable { - defer mmdb.ReloadASN() - data, err := downloadForBytes(C.ASNUrl) - if err != nil { - return fmt.Errorf("can't download ASN database file: %w", err) - } - - instance, err := maxminddb.FromBytes(data) - if err != nil { - return fmt.Errorf("invalid ASN database file: %s", err) - } - _ = instance.Close() - - mmdb.ASNInstance().Reader.Close() - if err = saveFile(data, C.Path.ASN()); err != nil { - return fmt.Errorf("can't save ASN database file: %w", err) - } + mmdb.ASNInstance().Reader.Close() // mmdb is loaded with mmap, so it needs to be closed before overwriting the file + if err = saveFile(data, C.Path.ASN()); err != nil { + return fmt.Errorf("can't save ASN database file: %w", err) } + return nil +} +func UpdateGeoIp() (err error) { + defer geodata.ClearGeoIPCache() + geoLoader, err := geodata.GetGeoDataLoader("standard") + data, err := downloadForBytes(C.GeoIpUrl) + if err != nil { + return fmt.Errorf("can't download GeoIP database file: %w", err) + } + if _, err = geoLoader.LoadIPByBytes(data, "cn"); err != nil { + return fmt.Errorf("invalid GeoIP database file: %s", err) + } + if err = saveFile(data, C.Path.GeoIP()); err != nil { + return fmt.Errorf("can't save GeoIP database file: %w", err) + } + return nil +} + +func UpdateGeoSite() (err error) { + defer geodata.ClearGeoSiteCache() + geoLoader, err := geodata.GetGeoDataLoader("standard") data, err := downloadForBytes(C.GeoSiteUrl) if err != nil { return fmt.Errorf("can't download GeoSite database file: %w", err) @@ -92,8 +93,41 @@ func updateGeoDatabases() error { if err = saveFile(data, C.Path.GeoSite()); err != nil { return fmt.Errorf("can't save GeoSite database file: %w", err) } + return nil +} - geodata.ClearCache() +func updateGeoDatabases() error { + defer runtime.GC() + + b, _ := batch.New[interface{}](context.Background()) + + if C.GeodataMode { + b.Go("UpdateGeoIp", func() (_ interface{}, err error) { + err = UpdateGeoIp() + return + }) + } else { + b.Go("UpdateMMDB", func() (_ interface{}, err error) { + err = UpdateMMDB() + return + }) + } + + if C.ASNEnable { + b.Go("UpdateASN", func() (_ interface{}, err error) { + err = UpdateASN() + return + }) + } + + b.Go("UpdateGeoSite", func() (_ interface{}, err error) { + err = UpdateGeoSite() + return + }) + + if e := b.Wait(); e != nil { + return e.Err + } return nil } @@ -103,12 +137,12 @@ var ErrGetDatabaseUpdateSkip = errors.New("GEO database is updating, skip") func UpdateGeoDatabases() error { log.Infoln("[GEO] Start updating GEO database") - if UpdatingGeo.Load() { + if updatingGeo.Load() { return ErrGetDatabaseUpdateSkip } - UpdatingGeo.Store(true) - defer UpdatingGeo.Store(false) + updatingGeo.Store(true) + defer updatingGeo.Store(false) log.Infoln("[GEO] Updating GEO database") From 7c8f451892258afe80e211867a118cd1d6aba63c Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 9 Sep 2024 16:08:48 +0800 Subject: [PATCH 207/311] chore: cleanup geo internal code --- adapter/provider/provider.go | 2 +- component/geodata/init.go | 143 +++++++++++++++++++++---------- component/geodata/utils.go | 16 ---- component/http/http.go | 15 +++- component/mmdb/mmdb.go | 84 +++++------------- component/mmdb/patch_android.go | 8 +- component/updater/update_core.go | 4 +- component/updater/update_geo.go | 75 ++++++++++------ component/updater/utils.go | 3 +- config/config.go | 20 ++--- constant/geodata.go | 12 --- constant/http.go | 5 -- hub/executor/executor.go | 18 ++-- main.go | 5 +- rules/common/geoip.go | 22 ++--- rules/common/ipasn.go | 1 - 16 files changed, 219 insertions(+), 214 deletions(-) delete mode 100644 constant/geodata.go delete mode 100644 constant/http.go diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index 923cddba..107b621b 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -130,7 +130,7 @@ func (pp *proxySetProvider) getSubscriptionInfo() { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() resp, err := mihomoHttp.HttpRequestWithProxy(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(), - http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil, pp.Vehicle().Proxy()) + http.MethodGet, nil, nil, pp.Vehicle().Proxy()) if err != nil { return } diff --git a/component/geodata/init.go b/component/geodata/init.go index 834567a4..08ec1b94 100644 --- a/component/geodata/init.go +++ b/component/geodata/init.go @@ -6,8 +6,10 @@ import ( "io" "net/http" "os" + "sync" "time" + "github.com/metacubex/mihomo/common/atomic" mihomoHttp "github.com/metacubex/mihomo/component/http" "github.com/metacubex/mihomo/component/mmdb" C "github.com/metacubex/mihomo/constant" @@ -18,12 +20,79 @@ var ( initGeoSite bool initGeoIP int initASN bool + + initGeoSiteMutex sync.Mutex + initGeoIPMutex sync.Mutex + initASNMutex sync.Mutex + + geoIpEnable atomic.Bool + geoSiteEnable atomic.Bool + asnEnable atomic.Bool + + geoIpUrl string + mmdbUrl string + geoSiteUrl string + asnUrl string ) +func GeoIpUrl() string { + return geoIpUrl +} + +func SetGeoIpUrl(url string) { + geoIpUrl = url +} + +func MmdbUrl() string { + return mmdbUrl +} + +func SetMmdbUrl(url string) { + mmdbUrl = url +} + +func GeoSiteUrl() string { + return geoSiteUrl +} + +func SetGeoSiteUrl(url string) { + geoSiteUrl = url +} + +func ASNUrl() string { + return asnUrl +} + +func SetASNUrl(url string) { + asnUrl = url +} + +func downloadToPath(url string, path string) (err error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) + defer cancel() + resp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, nil, nil) + if err != nil { + return + } + defer resp.Body.Close() + + f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644) + if err != nil { + return err + } + defer f.Close() + _, err = io.Copy(f, resp.Body) + + return err +} + func InitGeoSite() error { + geoSiteEnable.Store(true) + initGeoSiteMutex.Lock() + defer initGeoSiteMutex.Unlock() if _, err := os.Stat(C.Path.GeoSite()); os.IsNotExist(err) { log.Infoln("Can't find GeoSite.dat, start download") - if err := downloadGeoSite(C.Path.GeoSite()); err != nil { + if err := downloadToPath(GeoSiteUrl(), C.Path.GeoSite()); err != nil { return fmt.Errorf("can't download GeoSite.dat: %s", err.Error()) } log.Infoln("Download GeoSite.dat finish") @@ -35,7 +104,7 @@ func InitGeoSite() error { if err := os.Remove(C.Path.GeoSite()); err != nil { return fmt.Errorf("can't remove invalid GeoSite.dat: %s", err.Error()) } - if err := downloadGeoSite(C.Path.GeoSite()); err != nil { + if err := downloadToPath(GeoSiteUrl(), C.Path.GeoSite()); err != nil { return fmt.Errorf("can't download GeoSite.dat: %s", err.Error()) } } @@ -44,49 +113,14 @@ func InitGeoSite() error { return nil } -func downloadGeoSite(path string) (err error) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) - defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, C.GeoSiteUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) - if err != nil { - return - } - defer resp.Body.Close() - - f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644) - if err != nil { - return err - } - defer f.Close() - _, err = io.Copy(f, resp.Body) - - return err -} - -func downloadGeoIP(path string) (err error) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) - defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, C.GeoIpUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) - if err != nil { - return - } - defer resp.Body.Close() - - f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644) - if err != nil { - return err - } - defer f.Close() - _, err = io.Copy(f, resp.Body) - - return err -} - func InitGeoIP() error { - if C.GeodataMode { + geoIpEnable.Store(true) + initGeoIPMutex.Lock() + defer initGeoIPMutex.Unlock() + if GeodataMode() { if _, err := os.Stat(C.Path.GeoIP()); os.IsNotExist(err) { log.Infoln("Can't find GeoIP.dat, start download") - if err := downloadGeoIP(C.Path.GeoIP()); err != nil { + if err := downloadToPath(GeoIpUrl(), C.Path.GeoIP()); err != nil { return fmt.Errorf("can't download GeoIP.dat: %s", err.Error()) } log.Infoln("Download GeoIP.dat finish") @@ -99,7 +133,7 @@ func InitGeoIP() error { if err := os.Remove(C.Path.GeoIP()); err != nil { return fmt.Errorf("can't remove invalid GeoIP.dat: %s", err.Error()) } - if err := downloadGeoIP(C.Path.GeoIP()); err != nil { + if err := downloadToPath(GeoIpUrl(), C.Path.GeoIP()); err != nil { return fmt.Errorf("can't download GeoIP.dat: %s", err.Error()) } } @@ -110,7 +144,7 @@ func InitGeoIP() error { if _, err := os.Stat(C.Path.MMDB()); os.IsNotExist(err) { log.Infoln("Can't find MMDB, start download") - if err := mmdb.DownloadMMDB(C.Path.MMDB()); err != nil { + if err := downloadToPath(MmdbUrl(), C.Path.MMDB()); err != nil { return fmt.Errorf("can't download MMDB: %s", err.Error()) } } @@ -121,7 +155,7 @@ func InitGeoIP() error { if err := os.Remove(C.Path.MMDB()); err != nil { return fmt.Errorf("can't remove invalid MMDB: %s", err.Error()) } - if err := mmdb.DownloadMMDB(C.Path.MMDB()); err != nil { + if err := downloadToPath(MmdbUrl(), C.Path.MMDB()); err != nil { return fmt.Errorf("can't download MMDB: %s", err.Error()) } } @@ -131,9 +165,12 @@ func InitGeoIP() error { } func InitASN() error { + asnEnable.Store(true) + initASNMutex.Lock() + defer initASNMutex.Unlock() if _, err := os.Stat(C.Path.ASN()); os.IsNotExist(err) { log.Infoln("Can't find ASN.mmdb, start download") - if err := mmdb.DownloadASN(C.Path.ASN()); err != nil { + if err := downloadToPath(ASNUrl(), C.Path.ASN()); err != nil { return fmt.Errorf("can't download ASN.mmdb: %s", err.Error()) } log.Infoln("Download ASN.mmdb finish") @@ -145,7 +182,7 @@ func InitASN() error { if err := os.Remove(C.Path.ASN()); err != nil { return fmt.Errorf("can't remove invalid ASN: %s", err.Error()) } - if err := mmdb.DownloadASN(C.Path.ASN()); err != nil { + if err := downloadToPath(ASNUrl(), C.Path.ASN()); err != nil { return fmt.Errorf("can't download ASN: %s", err.Error()) } } @@ -153,3 +190,15 @@ func InitASN() error { } return nil } + +func GeoIpEnable() bool { + return geoIpEnable.Load() +} + +func GeoSiteEnable() bool { + return geoSiteEnable.Load() +} + +func ASNEnable() bool { + return asnEnable.Load() +} diff --git a/component/geodata/utils.go b/component/geodata/utils.go index 04c4293f..47966249 100644 --- a/component/geodata/utils.go +++ b/component/geodata/utils.go @@ -13,8 +13,6 @@ import ( var ( geoMode bool - AutoUpdate bool - UpdateInterval int geoLoaderName = "memconservative" geoSiteMatcher = "succinct" ) @@ -25,14 +23,6 @@ func GeodataMode() bool { return geoMode } -func GeoAutoUpdate() bool { - return AutoUpdate -} - -func GeoUpdateInterval() int { - return UpdateInterval -} - func LoaderName() string { return geoLoaderName } @@ -44,12 +34,6 @@ func SiteMatcherName() string { func SetGeodataMode(newGeodataMode bool) { geoMode = newGeodataMode } -func SetGeoAutoUpdate(newAutoUpdate bool) { - AutoUpdate = newAutoUpdate -} -func SetGeoUpdateInterval(newGeoUpdateInterval int) { - UpdateInterval = newGeoUpdateInterval -} func SetLoader(newLoader string) { if newLoader == "memc" { diff --git a/component/http/http.go b/component/http/http.go index 21d65d2e..3fc06da3 100644 --- a/component/http/http.go +++ b/component/http/http.go @@ -12,10 +12,21 @@ import ( "time" "github.com/metacubex/mihomo/component/ca" - C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/listener/inner" ) +var ( + ua string +) + +func UA() string { + return ua +} + +func SetUA(UA string) { + ua = UA +} + func HttpRequest(ctx context.Context, url, method string, header map[string][]string, body io.Reader) (*http.Response, error) { return HttpRequestWithProxy(ctx, url, method, header, body, "") } @@ -35,7 +46,7 @@ func HttpRequestWithProxy(ctx context.Context, url, method string, header map[st } if _, ok := header["User-Agent"]; !ok { - req.Header.Set("User-Agent", C.UA) + req.Header.Set("User-Agent", UA()) } if err != nil { diff --git a/component/mmdb/mmdb.go b/component/mmdb/mmdb.go index 81156bc6..81644b00 100644 --- a/component/mmdb/mmdb.go +++ b/component/mmdb/mmdb.go @@ -1,15 +1,9 @@ package mmdb import ( - "context" - "io" - "net/http" - "os" "sync" - "time" mihomoOnce "github.com/metacubex/mihomo/common/once" - mihomoHttp "github.com/metacubex/mihomo/component/http" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" @@ -25,26 +19,26 @@ const ( ) var ( - IPreader IPReader - ASNreader ASNReader - IPonce sync.Once - ASNonce sync.Once + ipReader IPReader + asnReader ASNReader + ipOnce sync.Once + asnOnce sync.Once ) func LoadFromBytes(buffer []byte) { - IPonce.Do(func() { + ipOnce.Do(func() { mmdb, err := maxminddb.FromBytes(buffer) if err != nil { log.Fatalln("Can't load mmdb: %s", err.Error()) } - IPreader = IPReader{Reader: mmdb} + ipReader = IPReader{Reader: mmdb} switch mmdb.Metadata.DatabaseType { case "sing-geoip": - IPreader.databaseType = typeSing + ipReader.databaseType = typeSing case "Meta-geoip0": - IPreader.databaseType = typeMetaV0 + ipReader.databaseType = typeMetaV0 default: - IPreader.databaseType = typeMaxmind + ipReader.databaseType = typeMaxmind } }) } @@ -58,83 +52,45 @@ func Verify(path string) bool { } func IPInstance() IPReader { - IPonce.Do(func() { + ipOnce.Do(func() { mmdbPath := C.Path.MMDB() log.Infoln("Load MMDB file: %s", mmdbPath) mmdb, err := maxminddb.Open(mmdbPath) if err != nil { log.Fatalln("Can't load MMDB: %s", err.Error()) } - IPreader = IPReader{Reader: mmdb} + ipReader = IPReader{Reader: mmdb} switch mmdb.Metadata.DatabaseType { case "sing-geoip": - IPreader.databaseType = typeSing + ipReader.databaseType = typeSing case "Meta-geoip0": - IPreader.databaseType = typeMetaV0 + ipReader.databaseType = typeMetaV0 default: - IPreader.databaseType = typeMaxmind + ipReader.databaseType = typeMaxmind } }) - return IPreader -} - -func DownloadMMDB(path string) (err error) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) - defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, C.MmdbUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) - if err != nil { - return - } - defer resp.Body.Close() - - f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644) - if err != nil { - return err - } - defer f.Close() - _, err = io.Copy(f, resp.Body) - - return err + return ipReader } func ASNInstance() ASNReader { - ASNonce.Do(func() { + asnOnce.Do(func() { ASNPath := C.Path.ASN() log.Infoln("Load ASN file: %s", ASNPath) asn, err := maxminddb.Open(ASNPath) if err != nil { log.Fatalln("Can't load ASN: %s", err.Error()) } - ASNreader = ASNReader{Reader: asn} + asnReader = ASNReader{Reader: asn} }) - return ASNreader -} - -func DownloadASN(path string) (err error) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) - defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, C.ASNUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) - if err != nil { - return - } - defer resp.Body.Close() - - f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644) - if err != nil { - return err - } - defer f.Close() - _, err = io.Copy(f, resp.Body) - - return err + return asnReader } func ReloadIP() { - mihomoOnce.Reset(&IPonce) + mihomoOnce.Reset(&ipOnce) } func ReloadASN() { - mihomoOnce.Reset(&ASNonce) + mihomoOnce.Reset(&asnOnce) } diff --git a/component/mmdb/patch_android.go b/component/mmdb/patch_android.go index 147a3324..dfa96cac 100644 --- a/component/mmdb/patch_android.go +++ b/component/mmdb/patch_android.go @@ -8,11 +8,11 @@ func InstallOverride(override *maxminddb.Reader) { newReader := IPReader{Reader: override} switch override.Metadata.DatabaseType { case "sing-geoip": - IPreader.databaseType = typeSing + ipReader.databaseType = typeSing case "Meta-geoip0": - IPreader.databaseType = typeMetaV0 + ipReader.databaseType = typeMetaV0 default: - IPreader.databaseType = typeMaxmind + ipReader.databaseType = typeMaxmind } - IPreader = newReader + ipReader = newReader } diff --git a/component/updater/update_core.go b/component/updater/update_core.go index 9f13af9c..2aab7833 100644 --- a/component/updater/update_core.go +++ b/component/updater/update_core.go @@ -237,7 +237,7 @@ const MaxPackageFileSize = 32 * 1024 * 1024 func downloadPackageFile() (err error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, packageURL, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) + resp, err := mihomoHttp.HttpRequest(ctx, packageURL, http.MethodGet, nil, nil) if err != nil { return fmt.Errorf("http request failed: %w", err) } @@ -418,7 +418,7 @@ func copyFile(src, dst string) error { func getLatestVersion() (version string, err error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, versionURL, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) + resp, err := mihomoHttp.HttpRequest(ctx, versionURL, http.MethodGet, nil, nil) if err != nil { return "", fmt.Errorf("get Latest Version fail: %w", err) } diff --git a/component/updater/update_geo.go b/component/updater/update_geo.go index d2d1e28d..6246b1c6 100644 --- a/component/updater/update_geo.go +++ b/component/updater/update_geo.go @@ -20,12 +20,31 @@ import ( ) var ( + autoUpdate bool + updateInterval int + updatingGeo atomic.Bool ) +func GeoAutoUpdate() bool { + return autoUpdate +} + +func GeoUpdateInterval() int { + return updateInterval +} + +func SetGeoAutoUpdate(newAutoUpdate bool) { + autoUpdate = newAutoUpdate +} + +func SetGeoUpdateInterval(newGeoUpdateInterval int) { + updateInterval = newGeoUpdateInterval +} + func UpdateMMDB() (err error) { defer mmdb.ReloadIP() - data, err := downloadForBytes(C.MmdbUrl) + data, err := downloadForBytes(geodata.MmdbUrl()) if err != nil { return fmt.Errorf("can't download MMDB database file: %w", err) } @@ -44,7 +63,7 @@ func UpdateMMDB() (err error) { func UpdateASN() (err error) { defer mmdb.ReloadASN() - data, err := downloadForBytes(C.ASNUrl) + data, err := downloadForBytes(geodata.ASNUrl()) if err != nil { return fmt.Errorf("can't download ASN database file: %w", err) } @@ -65,7 +84,7 @@ func UpdateASN() (err error) { func UpdateGeoIp() (err error) { defer geodata.ClearGeoIPCache() geoLoader, err := geodata.GetGeoDataLoader("standard") - data, err := downloadForBytes(C.GeoIpUrl) + data, err := downloadForBytes(geodata.GeoIpUrl()) if err != nil { return fmt.Errorf("can't download GeoIP database file: %w", err) } @@ -81,7 +100,7 @@ func UpdateGeoIp() (err error) { func UpdateGeoSite() (err error) { defer geodata.ClearGeoSiteCache() geoLoader, err := geodata.GetGeoDataLoader("standard") - data, err := downloadForBytes(C.GeoSiteUrl) + data, err := downloadForBytes(geodata.GeoSiteUrl()) if err != nil { return fmt.Errorf("can't download GeoSite database file: %w", err) } @@ -101,29 +120,33 @@ func updateGeoDatabases() error { b, _ := batch.New[interface{}](context.Background()) - if C.GeodataMode { - b.Go("UpdateGeoIp", func() (_ interface{}, err error) { - err = UpdateGeoIp() - return - }) - } else { - b.Go("UpdateMMDB", func() (_ interface{}, err error) { - err = UpdateMMDB() - return - }) + if geodata.GeoIpEnable() { + if geodata.GeodataMode() { + b.Go("UpdateGeoIp", func() (_ interface{}, err error) { + err = UpdateGeoIp() + return + }) + } else { + b.Go("UpdateMMDB", func() (_ interface{}, err error) { + err = UpdateMMDB() + return + }) + } } - if C.ASNEnable { + if geodata.ASNEnable() { b.Go("UpdateASN", func() (_ interface{}, err error) { err = UpdateASN() return }) } - b.Go("UpdateGeoSite", func() (_ interface{}, err error) { - err = UpdateGeoSite() - return - }) + if geodata.GeoSiteEnable() { + b.Go("UpdateGeoSite", func() (_ interface{}, err error) { + err = UpdateGeoSite() + return + }) + } if e := b.Wait(); e != nil { return e.Err @@ -156,7 +179,7 @@ func UpdateGeoDatabases() error { func getUpdateTime() (err error, time time.Time) { var fileInfo os.FileInfo - if C.GeodataMode { + if geodata.GeodataMode() { fileInfo, err = os.Stat(C.Path.GeoIP()) if err != nil { return err, time @@ -172,13 +195,13 @@ func getUpdateTime() (err error, time time.Time) { } func RegisterGeoUpdater() { - if C.GeoUpdateInterval <= 0 { - log.Errorln("[GEO] Invalid update interval: %d", C.GeoUpdateInterval) + if updateInterval <= 0 { + log.Errorln("[GEO] Invalid update interval: %d", updateInterval) return } go func() { - ticker := time.NewTicker(time.Duration(C.GeoUpdateInterval) * time.Hour) + ticker := time.NewTicker(time.Duration(updateInterval) * time.Hour) defer ticker.Stop() err, lastUpdate := getUpdateTime() @@ -188,8 +211,8 @@ func RegisterGeoUpdater() { } log.Infoln("[GEO] last update time %s", lastUpdate) - if lastUpdate.Add(time.Duration(C.GeoUpdateInterval) * time.Hour).Before(time.Now()) { - log.Infoln("[GEO] Database has not been updated for %v, update now", time.Duration(C.GeoUpdateInterval)*time.Hour) + if lastUpdate.Add(time.Duration(updateInterval) * time.Hour).Before(time.Now()) { + log.Infoln("[GEO] Database has not been updated for %v, update now", time.Duration(updateInterval)*time.Hour) if err := UpdateGeoDatabases(); err != nil { log.Errorln("[GEO] Failed to update GEO database: %s", err.Error()) return @@ -197,7 +220,7 @@ func RegisterGeoUpdater() { } for range ticker.C { - log.Infoln("[GEO] updating database every %d hours", C.GeoUpdateInterval) + log.Infoln("[GEO] updating database every %d hours", updateInterval) if err := UpdateGeoDatabases(); err != nil { log.Errorln("[GEO] Failed to update GEO database: %s", err.Error()) } diff --git a/component/updater/utils.go b/component/updater/utils.go index 0eecfc6c..d9da48c3 100644 --- a/component/updater/utils.go +++ b/component/updater/utils.go @@ -9,7 +9,6 @@ import ( "time" mihomoHttp "github.com/metacubex/mihomo/component/http" - C "github.com/metacubex/mihomo/constant" "golang.org/x/exp/constraints" ) @@ -17,7 +16,7 @@ import ( func downloadForBytes(url string) ([]byte, error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) + resp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, nil, nil) if err != nil { return nil, err } diff --git a/config/config.go b/config/config.go index 4490ebf6..26518758 100644 --- a/config/config.go +++ b/config/config.go @@ -22,6 +22,7 @@ import ( "github.com/metacubex/mihomo/component/cidr" "github.com/metacubex/mihomo/component/fakeip" "github.com/metacubex/mihomo/component/geodata" + mihomoHttp "github.com/metacubex/mihomo/component/http" P "github.com/metacubex/mihomo/component/process" "github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/component/sniffer" @@ -433,7 +434,7 @@ func DefaultRawConfig() *RawConfig { Mode: T.Rule, GeoAutoUpdate: false, GeoUpdateInterval: 24, - GeodataMode: C.GeodataMode, + GeodataMode: geodata.GeodataMode(), GeodataLoader: "memconservative", UnifiedDelay: false, Authentication: []string{}, @@ -681,19 +682,16 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } func parseGeneral(cfg *RawConfig) (*General, error) { + updater.SetGeoAutoUpdate(cfg.GeoAutoUpdate) + updater.SetGeoUpdateInterval(cfg.GeoUpdateInterval) geodata.SetGeodataMode(cfg.GeodataMode) - geodata.SetGeoAutoUpdate(cfg.GeoAutoUpdate) - geodata.SetGeoUpdateInterval(cfg.GeoUpdateInterval) geodata.SetLoader(cfg.GeodataLoader) geodata.SetSiteMatcher(cfg.GeositeMatcher) - C.GeoAutoUpdate = cfg.GeoAutoUpdate - C.GeoUpdateInterval = cfg.GeoUpdateInterval - C.GeoIpUrl = cfg.GeoXUrl.GeoIp - C.GeoSiteUrl = cfg.GeoXUrl.GeoSite - C.MmdbUrl = cfg.GeoXUrl.Mmdb - C.ASNUrl = cfg.GeoXUrl.ASN - C.GeodataMode = cfg.GeodataMode - C.UA = cfg.GlobalUA + geodata.SetGeoIpUrl(cfg.GeoXUrl.GeoIp) + geodata.SetGeoSiteUrl(cfg.GeoXUrl.GeoSite) + geodata.SetMmdbUrl(cfg.GeoXUrl.Mmdb) + geodata.SetASNUrl(cfg.GeoXUrl.ASN) + mihomoHttp.SetUA(cfg.GlobalUA) if cfg.KeepAliveIdle != 0 { N.KeepAliveIdle = time.Duration(cfg.KeepAliveIdle) * time.Second diff --git a/constant/geodata.go b/constant/geodata.go deleted file mode 100644 index cd3f74e3..00000000 --- a/constant/geodata.go +++ /dev/null @@ -1,12 +0,0 @@ -package constant - -var ( - ASNEnable bool - GeodataMode bool - GeoAutoUpdate bool - GeoUpdateInterval int - GeoIpUrl string - MmdbUrl string - GeoSiteUrl string - ASNUrl string -) diff --git a/constant/http.go b/constant/http.go deleted file mode 100644 index 8e321f6b..00000000 --- a/constant/http.go +++ /dev/null @@ -1,5 +0,0 @@ -package constant - -var ( - UA string -) diff --git a/hub/executor/executor.go b/hub/executor/executor.go index c83b254c..fddcc42e 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -17,6 +17,7 @@ import ( "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/dialer" G "github.com/metacubex/mihomo/component/geodata" + mihomoHttp "github.com/metacubex/mihomo/component/http" "github.com/metacubex/mihomo/component/iface" "github.com/metacubex/mihomo/component/profile" "github.com/metacubex/mihomo/component/profile/cachefile" @@ -81,6 +82,7 @@ func ParseWithBytes(buf []byte) (*config.Config, error) { func ApplyConfig(cfg *config.Config, force bool) { mux.Lock() defer mux.Unlock() + log.SetLevel(cfg.General.LogLevel) tunnel.OnSuspend() @@ -115,8 +117,6 @@ func ApplyConfig(cfg *config.Config, force bool) { tunnel.OnRunning() hcCompatibleProvider(cfg.Providers) initExternalUI() - - log.SetLevel(cfg.General.LogLevel) } func initInnerTcp() { @@ -157,13 +157,13 @@ func GetGeneral() *config.General { Interface: dialer.DefaultInterface.Load(), RoutingMark: int(dialer.DefaultRoutingMark.Load()), GeoXUrl: config.GeoXUrl{ - GeoIp: C.GeoIpUrl, - Mmdb: C.MmdbUrl, - ASN: C.ASNUrl, - GeoSite: C.GeoSiteUrl, + GeoIp: G.GeoIpUrl(), + Mmdb: G.MmdbUrl(), + ASN: G.ASNUrl(), + GeoSite: G.GeoSiteUrl(), }, - GeoAutoUpdate: G.GeoAutoUpdate(), - GeoUpdateInterval: G.GeoUpdateInterval(), + GeoAutoUpdate: updater.GeoAutoUpdate(), + GeoUpdateInterval: updater.GeoUpdateInterval(), GeodataMode: G.GeodataMode(), GeodataLoader: G.LoaderName(), GeositeMatcher: G.SiteMatcherName(), @@ -171,7 +171,7 @@ func GetGeneral() *config.General { FindProcessMode: tunnel.FindProcessMode(), Sniffing: tunnel.IsSniffing(), GlobalClientFingerprint: tlsC.GetGlobalFingerprint(), - GlobalUA: C.UA, + GlobalUA: mihomoHttp.UA(), } return general diff --git a/main.go b/main.go index 26f1d2c0..8910a006 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,7 @@ import ( "strings" "syscall" + "github.com/metacubex/mihomo/component/geodata" "github.com/metacubex/mihomo/component/updater" "github.com/metacubex/mihomo/config" C "github.com/metacubex/mihomo/constant" @@ -78,7 +79,7 @@ func main() { } if geodataMode { - C.GeodataMode = true + geodata.SetGeodataMode(true) } if configString != "" { @@ -140,7 +141,7 @@ func main() { log.Fatalln("Parse config error: %s", err.Error()) } - if C.GeoAutoUpdate { + if updater.GeoAutoUpdate() { updater.RegisterGeoUpdater() } diff --git a/rules/common/geoip.go b/rules/common/geoip.go index 7d6871c7..61fae504 100644 --- a/rules/common/geoip.go +++ b/rules/common/geoip.go @@ -46,7 +46,7 @@ func (g *GEOIP) Match(metadata *C.Metadata) (bool, string) { return g.isLan(ip), g.adapter } - if C.GeodataMode { + if geodata.GeodataMode() { if g.isSourceIP { if slices.Contains(metadata.SrcGeoIP, g.country) { return true, g.adapter @@ -102,7 +102,7 @@ func (g *GEOIP) MatchIp(ip netip.Addr) bool { return g.isLan(ip) } - if C.GeodataMode { + if geodata.GeodataMode() { matcher, err := g.getIPMatcher() if err != nil { return false @@ -124,7 +124,7 @@ func (g dnsFallbackFilter) MatchIp(ip netip.Addr) bool { return false } - if C.GeodataMode { + if geodata.GeodataMode() { matcher, err := g.getIPMatcher() if err != nil { return false @@ -170,7 +170,7 @@ func (g *GEOIP) GetCountry() string { } func (g *GEOIP) GetIPMatcher() (router.IPMatcher, error) { - if C.GeodataMode { + if geodata.GeodataMode() { return g.getIPMatcher() } return nil, errors.New("not geodata mode") @@ -193,10 +193,6 @@ func (g *GEOIP) GetRecodeSize() int { } func NewGEOIP(country string, adapter string, isSrc, noResolveIP bool) (*GEOIP, error) { - if err := geodata.InitGeoIP(); err != nil { - log.Errorln("can't initial GeoIP: %s", err) - return nil, err - } country = strings.ToLower(country) geoip := &GEOIP{ @@ -206,11 +202,17 @@ func NewGEOIP(country string, adapter string, isSrc, noResolveIP bool) (*GEOIP, noResolveIP: noResolveIP, isSourceIP: isSrc, } - if !C.GeodataMode || country == "lan" { + + if country == "lan" { return geoip, nil } - if C.GeodataMode { + if err := geodata.InitGeoIP(); err != nil { + log.Errorln("can't initial GeoIP: %s", err) + return nil, err + } + + if geodata.GeodataMode() { geoIPMatcher, err := geoip.getIPMatcher() // test load if err != nil { return nil, err diff --git a/rules/common/ipasn.go b/rules/common/ipasn.go index bcff4e72..813923ac 100644 --- a/rules/common/ipasn.go +++ b/rules/common/ipasn.go @@ -63,7 +63,6 @@ func (a *ASN) GetASN() string { } func NewIPASN(asn string, adapter string, isSrc, noResolveIP bool) (*ASN, error) { - C.ASNEnable = true if err := geodata.InitASN(); err != nil { log.Errorln("can't initial ASN: %s", err) return nil, err From 89b9438fc06fccaea2d6b85d428107b6487ca4b2 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 10 Sep 2024 16:43:00 +0800 Subject: [PATCH 208/311] fix: restful server restart --- hub/route/server.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hub/route/server.go b/hub/route/server.go index 1605b4bf..b7077563 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -148,10 +148,10 @@ func start(cfg *Config) { server := &http.Server{ Handler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer), } + httpServer = server if err = server.Serve(l); err != nil { log.Errorln("External controller serve error: %s", err) } - httpServer = server } } @@ -183,10 +183,10 @@ func startTLS(cfg *Config) { Certificates: []tls.Certificate{c}, }, } + tlsServer = server if err = server.ServeTLS(l, "", ""); err != nil { log.Errorln("External controller tls serve error: %s", err) } - tlsServer = server } } @@ -228,10 +228,10 @@ func startUnix(cfg *Config) { server := &http.Server{ Handler: router(cfg.IsDebug, "", cfg.DohServer), } + unixServer = server if err = server.Serve(l); err != nil { log.Errorln("External controller unix serve error: %s", err) } - unixServer = server } } From f8557f5fd8e18991a0c9830b4fef08a73fd384a0 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 10 Sep 2024 17:06:44 +0800 Subject: [PATCH 209/311] chore: don't start UpdateMonitor when AutoRoute not enabled --- listener/sing_tun/server.go | 58 ++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index e8c2ad28..e843eae8 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -132,7 +132,7 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis if options.GSOMaxSize == 0 { options.GSOMaxSize = 65536 } - if !supportRedirect { + if !supportRedirect || !options.AutoRoute { options.AutoRedirect = false } tunName := options.Device @@ -264,31 +264,35 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis interfaceFinder := DefaultInterfaceFinder - networkUpdateMonitor, err := tun.NewNetworkUpdateMonitor(log.SingLogger) - if err != nil { - err = E.Cause(err, "create NetworkUpdateMonitor") - return - } - l.networkUpdateMonitor = networkUpdateMonitor - err = networkUpdateMonitor.Start() - if err != nil { - err = E.Cause(err, "start NetworkUpdateMonitor") - return - } + var networkUpdateMonitor tun.NetworkUpdateMonitor + var defaultInterfaceMonitor tun.DefaultInterfaceMonitor + if options.AutoRoute { // don't start NetworkUpdateMonitor because netlink banned by google on Android14+ + networkUpdateMonitor, err = tun.NewNetworkUpdateMonitor(log.SingLogger) + if err != nil { + err = E.Cause(err, "create NetworkUpdateMonitor") + return + } + l.networkUpdateMonitor = networkUpdateMonitor + err = networkUpdateMonitor.Start() + if err != nil { + err = E.Cause(err, "start NetworkUpdateMonitor") + return + } - 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) { - l.FlushDefaultInterface() - }) - err = defaultInterfaceMonitor.Start() - if err != nil { - err = E.Cause(err, "start DefaultInterfaceMonitor") - return + 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) { + l.FlushDefaultInterface() + }) + err = defaultInterfaceMonitor.Start() + if err != nil { + err = E.Cause(err, "start DefaultInterfaceMonitor") + return + } } tunOptions := tun.Options{ @@ -331,7 +335,7 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis Context: ctx, Handler: handler.TypeMutation(C.REDIR), Logger: log.SingLogger, - NetworkMonitor: networkUpdateMonitor, + NetworkMonitor: l.networkUpdateMonitor, InterfaceFinder: interfaceFinder, TableName: "mihomo", DisableNFTables: dErr == nil && disableNFTables, @@ -489,7 +493,7 @@ func (l *Listener) updateRule(ruleProvider provider.RuleProvider, exclude bool, } func (l *Listener) FlushDefaultInterface() { - if l.options.AutoDetectInterface { + if l.options.AutoDetectInterface && l.defaultInterfaceMonitor != nil { for _, destination := range []netip.Addr{netip.IPv4Unspecified(), netip.IPv6Unspecified(), netip.MustParseAddr("1.1.1.1")} { autoDetectInterfaceName := l.defaultInterfaceMonitor.DefaultInterfaceName(destination) if autoDetectInterfaceName == l.tunName { From 417d709d605a070d3e59cb7cb04e763eccffa372 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 10 Sep 2024 20:57:28 +0800 Subject: [PATCH 210/311] fix: RawSrcAddr and RawSrcAddr in metadata --- adapter/inbound/https.go | 2 ++ listener/sing/sing.go | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/adapter/inbound/https.go b/adapter/inbound/https.go index 55f6731a..24b30804 100644 --- a/adapter/inbound/https.go +++ b/adapter/inbound/https.go @@ -11,6 +11,8 @@ import ( func NewHTTPS(request *http.Request, conn net.Conn, additions ...Addition) (net.Conn, *C.Metadata) { metadata := parseHTTPAddr(request) metadata.Type = C.HTTPS + metadata.RawSrcAddr = conn.RemoteAddr() + metadata.RawDstAddr = conn.LocalAddr() ApplyAdditions(metadata, WithSrcAddr(conn.RemoteAddr()), WithInAddr(conn.LocalAddr())) ApplyAdditions(metadata, additions...) return conn, metadata diff --git a/listener/sing/sing.go b/listener/sing/sing.go index 10390e73..0d4bb926 100644 --- a/listener/sing/sing.go +++ b/listener/sing/sing.go @@ -129,6 +129,12 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta NetWork: C.TCP, Type: h.Type, } + if metadata.Source.IsIP() && metadata.Source.Fqdn == "" { + cMetadata.RawSrcAddr = metadata.Source.Unwrap().TCPAddr() + } + if metadata.Destination.IsIP() && metadata.Destination.Fqdn == "" { + cMetadata.RawDstAddr = metadata.Destination.Unwrap().TCPAddr() + } inbound.ApplyAdditions(cMetadata, inbound.WithDstAddr(metadata.Destination), inbound.WithSrcAddr(metadata.Source), inbound.WithInAddr(conn.LocalAddr())) inbound.ApplyAdditions(cMetadata, getAdditions(ctx)...) inbound.ApplyAdditions(cMetadata, h.Additions...) @@ -185,6 +191,12 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network. NetWork: C.UDP, Type: h.Type, } + if metadata.Source.IsIP() && metadata.Source.Fqdn == "" { + cMetadata.RawSrcAddr = metadata.Source.Unwrap().UDPAddr() + } + if dest.IsIP() && dest.Fqdn == "" { + cMetadata.RawDstAddr = dest.Unwrap().UDPAddr() + } inbound.ApplyAdditions(cMetadata, inbound.WithDstAddr(dest), inbound.WithSrcAddr(metadata.Source), inbound.WithInAddr(conn.LocalAddr())) inbound.ApplyAdditions(cMetadata, getAdditions(ctx)...) inbound.ApplyAdditions(cMetadata, h.Additions...) From 910f236696c19b7f52d6bd7fe090f949fa50987c Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 11 Sep 2024 09:52:21 +0800 Subject: [PATCH 211/311] fix: UpdateMonitor should start when AutoDetectInterface enabled --- listener/sing_tun/server.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index e843eae8..15d30a17 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -132,7 +132,7 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis if options.GSOMaxSize == 0 { options.GSOMaxSize = 65536 } - if !supportRedirect || !options.AutoRoute { + if !supportRedirect { options.AutoRedirect = false } tunName := options.Device @@ -266,7 +266,7 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis var networkUpdateMonitor tun.NetworkUpdateMonitor var defaultInterfaceMonitor tun.DefaultInterfaceMonitor - if options.AutoRoute { // don't start NetworkUpdateMonitor because netlink banned by google on Android14+ + if options.AutoRoute || options.AutoDetectInterface { // don't start NetworkUpdateMonitor because netlink banned by google on Android14+ networkUpdateMonitor, err = tun.NewNetworkUpdateMonitor(log.SingLogger) if err != nil { err = E.Cause(err, "create NetworkUpdateMonitor") From f305e440ef0067b0bae42d6377000c841b6e9130 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 11 Sep 2024 10:04:50 +0800 Subject: [PATCH 212/311] fix: new tun with fd unneeded calculate interface name --- listener/sing_tun/server.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 15d30a17..a135a301 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -136,9 +136,8 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis options.AutoRedirect = false } tunName := options.Device - if tunName == "" || !checkTunName(tunName) { + if options.FileDescriptor == 0 && (tunName == "" || !checkTunName(tunName)) { tunName = CalculateInterfaceName(InterfaceName) - options.Device = tunName } routeAddress := options.RouteAddress if len(options.Inet4RouteAddress) > 0 { @@ -440,6 +439,9 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis //l.openAndroidHotspot(tunOptions) + if options.FileDescriptor != 0 { + tunName = fmt.Sprintf("%s(fd=%d)", tunName, options.FileDescriptor) + } l.addrStr = fmt.Sprintf("%s(%s,%s), mtu: %d, auto route: %v, auto redir: %v, ip stack: %s", tunName, tunOptions.Inet4Address, tunOptions.Inet6Address, tunMTU, options.AutoRoute, options.AutoRedirect, options.Stack) return From ecbbf9d2201d4e4c3d1f2f11fa91fb19878f94d0 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 11 Sep 2024 10:28:34 +0800 Subject: [PATCH 213/311] feat: doh client support `ecs` and `ecs-override` --- dns/doh.go | 29 ++++++++++++++++++++++++++ dns/edns0_subnet.go | 51 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 dns/edns0_subnet.go diff --git a/dns/doh.go b/dns/doh.go index 97e01ea7..ffb65fce 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -9,6 +9,7 @@ import ( "io" "net" "net/http" + "net/netip" "net/url" "runtime" "strconv" @@ -67,6 +68,8 @@ type dnsOverHTTPS struct { dialer *dnsDialer addr string skipCertVerify bool + ecsPrefix netip.Prefix + ecsOverride bool } // type check @@ -99,6 +102,28 @@ func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[strin doh.skipCertVerify = true } + if ecs := params["ecs"]; ecs != "" { + prefix, err := netip.ParsePrefix(ecs) + if err != nil { + addr, err := netip.ParseAddr(ecs) + if err != nil { + log.Warnln("DOH [%s] config with invalid ecs: %s", doh.addr, ecs) + } else { + doh.ecsPrefix = netip.PrefixFrom(addr, addr.BitLen()) + } + } else { + doh.ecsPrefix = prefix + } + } + + if doh.ecsPrefix.IsValid() { + log.Debugln("DOH [%s] config with ecs: %s", doh.addr, doh.ecsPrefix) + } + + if params["ecs-override"] == "true" { + doh.ecsOverride = true + } + runtime.SetFinalizer(doh, (*dnsOverHTTPS).Close) return doh @@ -126,6 +151,10 @@ func (doh *dnsOverHTTPS) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D. } }() + if doh.ecsPrefix.IsValid() { + setEdns0Subnet(m, doh.ecsPrefix, doh.ecsOverride) + } + // Check if there was already an active client before sending the request. // We'll only attempt to re-connect if there was one. client, isCached, err := doh.getClient(ctx) diff --git a/dns/edns0_subnet.go b/dns/edns0_subnet.go new file mode 100644 index 00000000..2ed4f140 --- /dev/null +++ b/dns/edns0_subnet.go @@ -0,0 +1,51 @@ +package dns + +import ( + "net/netip" + + "github.com/miekg/dns" +) + +func setEdns0Subnet(message *dns.Msg, clientSubnet netip.Prefix, override bool) bool { + var ( + optRecord *dns.OPT + subnetOption *dns.EDNS0_SUBNET + ) +findExists: + for _, record := range message.Extra { + var isOPTRecord bool + if optRecord, isOPTRecord = record.(*dns.OPT); isOPTRecord { + for _, option := range optRecord.Option { + var isEDNS0Subnet bool + if subnetOption, isEDNS0Subnet = option.(*dns.EDNS0_SUBNET); isEDNS0Subnet { + if !override { + return false + } + break findExists + } + } + } + } + if optRecord == nil { + optRecord = &dns.OPT{ + Hdr: dns.RR_Header{ + Name: ".", + Rrtype: dns.TypeOPT, + }, + } + message.Extra = append(message.Extra, optRecord) + } + if subnetOption == nil { + subnetOption = new(dns.EDNS0_SUBNET) + optRecord.Option = append(optRecord.Option, subnetOption) + } + subnetOption.Code = dns.EDNS0SUBNET + if clientSubnet.Addr().Is4() { + subnetOption.Family = 1 + } else { + subnetOption.Family = 2 + } + subnetOption.SourceNetmask = uint8(clientSubnet.Bits()) + subnetOption.Address = clientSubnet.Addr().AsSlice() + return true +} From 8230bc8e7d8e9d431615e439804bccb45401a200 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Wed, 11 Sep 2024 13:34:59 +0800 Subject: [PATCH 214/311] chore: parse float in subscription info --- adapter/provider/provider.go | 6 +-- adapter/provider/subscription_info.go | 53 +++++++++++++++++++-------- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index 107b621b..5f86777c 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -18,7 +18,6 @@ import ( "github.com/metacubex/mihomo/component/resource" C "github.com/metacubex/mihomo/constant" types "github.com/metacubex/mihomo/constant/provider" - "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/tunnel/statistic" "github.com/dlclark/regexp2" @@ -149,10 +148,7 @@ func (pp *proxySetProvider) getSubscriptionInfo() { return } } - pp.subscriptionInfo, err = NewSubscriptionInfo(userInfoStr) - if err != nil { - log.Warnln("[Provider] get subscription-userinfo: %e", err) - } + pp.subscriptionInfo = NewSubscriptionInfo(userInfoStr) }() } diff --git a/adapter/provider/subscription_info.go b/adapter/provider/subscription_info.go index 3a9e2d72..b72c7b61 100644 --- a/adapter/provider/subscription_info.go +++ b/adapter/provider/subscription_info.go @@ -1,8 +1,11 @@ package provider import ( + "fmt" "strconv" "strings" + + "github.com/metacubex/mihomo/log" ) type SubscriptionInfo struct { @@ -12,28 +15,46 @@ type SubscriptionInfo struct { Expire int64 } -func NewSubscriptionInfo(userinfo string) (si *SubscriptionInfo, err error) { +func NewSubscriptionInfo(userinfo string) (si *SubscriptionInfo) { userinfo = strings.ToLower(userinfo) userinfo = strings.ReplaceAll(userinfo, " ", "") si = new(SubscriptionInfo) + for _, field := range strings.Split(userinfo, ";") { - switch name, value, _ := strings.Cut(field, "="); name { - case "upload": - si.Upload, err = strconv.ParseInt(value, 10, 64) - case "download": - si.Download, err = strconv.ParseInt(value, 10, 64) - case "total": - si.Total, err = strconv.ParseInt(value, 10, 64) - case "expire": - if value == "" { - si.Expire = 0 - } else { - si.Expire, err = strconv.ParseInt(value, 10, 64) - } + name, value, ok := strings.Cut(field, "=") + if !ok { + continue } + + intValue, err := parseValue(value) if err != nil { - return + log.Warnln("[Provider] get subscription-userinfo: %e", err) + continue + } + + switch name { + case "upload": + si.Upload = intValue + case "download": + si.Download = intValue + case "total": + si.Total = intValue + case "expire": + si.Expire = intValue } } - return + + return si +} + +func parseValue(value string) (int64, error) { + if intValue, err := strconv.ParseInt(value, 10, 64); err == nil { + return intValue, nil + } + + if floatValue, err := strconv.ParseFloat(value, 64); err == nil { + return int64(floatValue), nil + } + + return 0, fmt.Errorf("failed to parse value '%s'", value) } From 0a2f606e1b6f4e726e92aebb28aeb703c969eca7 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 11 Sep 2024 16:10:35 +0800 Subject: [PATCH 215/311] chore: cleanup the patch code --- adapter/provider/parser.go | 3 +- common/arc/arc.go | 24 ++++++----- common/lru/lrucache.go | 23 +++++------ component/fakeip/memory.go | 5 ++- component/mmdb/patch_android.go | 18 --------- component/resolver/resolver.go | 1 + config/config.go | 3 +- constant/path.go | 4 +- dns/dhcp.go | 2 - dns/patch_android.go | 57 +++----------------------- dns/patch_common.go | 6 --- dns/resolver.go | 7 ++++ dns/server.go | 5 --- dns/system.go | 63 ----------------------------- dns/system_common.go | 71 +++++++++++++++++++++++++++++++++ dns/util.go | 4 ++ hub/hub.go | 8 ---- rules/provider/parse.go | 3 +- 18 files changed, 122 insertions(+), 185 deletions(-) delete mode 100644 component/mmdb/patch_android.go delete mode 100644 dns/patch_common.go create mode 100644 dns/system_common.go diff --git a/adapter/provider/parser.go b/adapter/provider/parser.go index edb6b911..0c606a95 100644 --- a/adapter/provider/parser.go +++ b/adapter/provider/parser.go @@ -9,7 +9,6 @@ import ( "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/resource" C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/constant/features" types "github.com/metacubex/mihomo/constant/provider" ) @@ -94,7 +93,7 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide path := C.Path.GetPathByHash("proxies", schema.URL) if schema.Path != "" { path = C.Path.Resolve(schema.Path) - if !features.CMFA && !C.Path.IsSafePath(path) { + if !C.Path.IsSafePath(path) { return nil, fmt.Errorf("%w: %s", errSubPath, path) } } diff --git a/common/arc/arc.go b/common/arc/arc.go index da78b1c1..8d44a180 100644 --- a/common/arc/arc.go +++ b/common/arc/arc.go @@ -33,15 +33,8 @@ type ARC[K comparable, V any] struct { // New returns a new Adaptive Replacement Cache (ARC). func New[K comparable, V any](options ...Option[K, V]) *ARC[K, V] { - arc := &ARC[K, V]{ - p: 0, - t1: list.New[*entry[K, V]](), - b1: list.New[*entry[K, V]](), - t2: list.New[*entry[K, V]](), - b2: list.New[*entry[K, V]](), - len: 0, - cache: make(map[K]*entry[K, V]), - } + arc := &ARC[K, V]{} + arc.Clear() for _, option := range options { option(arc) @@ -49,6 +42,19 @@ func New[K comparable, V any](options ...Option[K, V]) *ARC[K, V] { return arc } +func (a *ARC[K, V]) Clear() { + a.mutex.Lock() + defer a.mutex.Unlock() + + a.p = 0 + a.t1 = list.New[*entry[K, V]]() + a.b1 = list.New[*entry[K, V]]() + a.t2 = list.New[*entry[K, V]]() + a.b2 = list.New[*entry[K, V]]() + a.len = 0 + a.cache = make(map[K]*entry[K, V]) +} + // Set inserts a new key-value pair into the cache. // This optimizes future access to this entry (side effect). func (a *ARC[K, V]) Set(key K, value V) { diff --git a/common/lru/lrucache.go b/common/lru/lrucache.go index 35f605b1..4afe8e0c 100644 --- a/common/lru/lrucache.go +++ b/common/lru/lrucache.go @@ -68,10 +68,8 @@ type LruCache[K comparable, V any] struct { // New creates an LruCache func New[K comparable, V any](options ...Option[K, V]) *LruCache[K, V] { - lc := &LruCache[K, V]{ - lru: list.New[*entry[K, V]](), - cache: make(map[K]*list.Element[*entry[K, V]]), - } + lc := &LruCache[K, V]{} + lc.Clear() for _, option := range options { option(lc) @@ -80,6 +78,14 @@ func New[K comparable, V any](options ...Option[K, V]) *LruCache[K, V] { return lc } +func (c *LruCache[K, V]) Clear() { + c.mu.Lock() + defer c.mu.Unlock() + + c.lru = list.New[*entry[K, V]]() + c.cache = make(map[K]*list.Element[*entry[K, V]]) +} + // Get returns any representation of a cached response and a bool // set to true if the key was found. func (c *LruCache[K, V]) Get(key K) (V, bool) { @@ -250,15 +256,6 @@ func (c *LruCache[K, V]) deleteElement(le *list.Element[*entry[K, V]]) { } } -func (c *LruCache[K, V]) Clear() error { - c.mu.Lock() - defer c.mu.Unlock() - - c.cache = make(map[K]*list.Element[*entry[K, V]]) - - return nil -} - // Compute either sets the computed new value for the key or deletes // the value for the key. When the delete result of the valueFn function // is set to true, the value will be deleted, if it exists. When delete diff --git a/component/fakeip/memory.go b/component/fakeip/memory.go index 00eff810..0a8492f8 100644 --- a/component/fakeip/memory.go +++ b/component/fakeip/memory.go @@ -67,8 +67,9 @@ func (m *memoryStore) CloneTo(store store) { // FlushFakeIP implements store.FlushFakeIP func (m *memoryStore) FlushFakeIP() error { - _ = m.cacheIP.Clear() - return m.cacheHost.Clear() + m.cacheIP.Clear() + m.cacheHost.Clear() + return nil } func newMemoryStore(size int) *memoryStore { diff --git a/component/mmdb/patch_android.go b/component/mmdb/patch_android.go deleted file mode 100644 index dfa96cac..00000000 --- a/component/mmdb/patch_android.go +++ /dev/null @@ -1,18 +0,0 @@ -//go:build android && cmfa - -package mmdb - -import "github.com/oschwald/maxminddb-golang" - -func InstallOverride(override *maxminddb.Reader) { - newReader := IPReader{Reader: override} - switch override.Metadata.DatabaseType { - case "sing-geoip": - ipReader.databaseType = typeSing - case "Meta-geoip0": - ipReader.databaseType = typeMetaV0 - default: - ipReader.databaseType = typeMaxmind - } - ipReader = newReader -} diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index 07c68824..feb3f98f 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -46,6 +46,7 @@ type Resolver interface { LookupIPv6(ctx context.Context, host string) (ips []netip.Addr, err error) ExchangeContext(ctx context.Context, m *dns.Msg) (msg *dns.Msg, err error) Invalid() bool + ClearCache() } // LookupIPv4WithResolver same as LookupIPv4, but with a resolver diff --git a/config/config.go b/config/config.go index 26518758..0af30b39 100644 --- a/config/config.go +++ b/config/config.go @@ -30,7 +30,6 @@ import ( "github.com/metacubex/mihomo/component/trie" "github.com/metacubex/mihomo/component/updater" C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/constant/features" providerTypes "github.com/metacubex/mihomo/constant/provider" snifferTypes "github.com/metacubex/mihomo/constant/sniffer" "github.com/metacubex/mihomo/dns" @@ -649,7 +648,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { config.DNS = dnsCfg err = parseTun(rawCfg.Tun, config.General) - if !features.CMFA && err != nil { + if err != nil { return nil, err } diff --git a/constant/path.go b/constant/path.go index 77f7d0ef..02279371 100644 --- a/constant/path.go +++ b/constant/path.go @@ -8,6 +8,8 @@ import ( "path/filepath" "strconv" "strings" + + "github.com/metacubex/mihomo/constant/features" ) const Name = "mihomo" @@ -73,7 +75,7 @@ func (p *path) Resolve(path string) string { // IsSafePath return true if path is a subpath of homedir func (p *path) IsSafePath(path string) bool { - if p.allowUnsafePath { + if p.allowUnsafePath || features.CMFA { return true } homedir := p.HomeDir() diff --git a/dns/dhcp.go b/dns/dhcp.go index d4944a96..dc1344f5 100644 --- a/dns/dhcp.go +++ b/dns/dhcp.go @@ -1,5 +1,3 @@ -//go:build !(android && cmfa) - package dns import ( diff --git a/dns/patch_android.go b/dns/patch_android.go index e62aabcd..386680e7 100644 --- a/dns/patch_android.go +++ b/dns/patch_android.go @@ -3,53 +3,16 @@ package dns import ( - "context" - - D "github.com/miekg/dns" - - "github.com/metacubex/mihomo/common/lru" - "github.com/metacubex/mihomo/component/dhcp" "github.com/metacubex/mihomo/component/resolver" ) const SystemDNSPlaceholder = "system" -var systemResolver *Resolver -var isolateHandler handler - -var _ dnsClient = (*dhcpClient)(nil) - -type dhcpClient struct { - enable bool -} - -func (d *dhcpClient) Address() string { - return SystemDNSPlaceholder -} - -func (d *dhcpClient) Exchange(m *D.Msg) (msg *D.Msg, err error) { - return d.ExchangeContext(context.Background(), m) -} - -func (d *dhcpClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { - if s := systemResolver; s != nil { - return s.ExchangeContext(ctx, m) - } - - return nil, dhcp.ErrNotFound -} - -func ServeDNSWithDefaultServer(msg *D.Msg) (*D.Msg, error) { - if h := isolateHandler; h != nil { - return handlerWithContext(context.Background(), h, msg) - } - - return nil, D.ErrTime -} +var systemResolver []dnsClient func FlushCacheWithDefaultResolver() { if r := resolver.DefaultResolver; r != nil { - r.(*Resolver).cache = lru.New(lru.WithSize[string, *D.Msg](4096), lru.WithStale[string, *D.Msg](true)) + r.ClearCache() } } @@ -63,19 +26,9 @@ func UpdateSystemDNS(addr []string) { ns = append(ns, NameServer{Addr: d}) } - systemResolver = NewResolver(Config{Main: ns}) + systemResolver = transform(ns, nil) } -func UpdateIsolateHandler(resolver *Resolver, mapper *ResolverEnhancer) { - if resolver == nil { - isolateHandler = nil - - return - } - - isolateHandler = NewHandler(resolver, mapper) -} - -func newDHCPClient(ifaceName string) *dhcpClient { - return &dhcpClient{enable: ifaceName == SystemDNSPlaceholder} +func (c *systemClient) getDnsClients() ([]dnsClient, error) { + return systemResolver, nil } diff --git a/dns/patch_common.go b/dns/patch_common.go deleted file mode 100644 index fae1e126..00000000 --- a/dns/patch_common.go +++ /dev/null @@ -1,6 +0,0 @@ -//go:build !(android && cmfa) - -package dns - -func UpdateIsolateHandler(resolver *Resolver, mapper *ResolverEnhancer) { -} diff --git a/dns/resolver.go b/dns/resolver.go index 232f3b33..e03feef4 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -29,6 +29,7 @@ type dnsClient interface { type dnsCache interface { GetWithExpire(key string) (*D.Msg, time.Time, bool) SetWithExpire(key string, value *D.Msg, expire time.Time) + Clear() } type result struct { @@ -369,6 +370,12 @@ func (r *Resolver) Invalid() bool { return len(r.main) > 0 } +func (r *Resolver) ClearCache() { + if r != nil && r.cache != nil { + r.cache.Clear() + } +} + type NameServer struct { Net string Addr string diff --git a/dns/server.go b/dns/server.go index d45fb5eb..1cf58d4d 100644 --- a/dns/server.go +++ b/dns/server.go @@ -6,7 +6,6 @@ import ( "net" "github.com/metacubex/mihomo/common/sockopt" - "github.com/metacubex/mihomo/constant/features" "github.com/metacubex/mihomo/context" "github.com/metacubex/mihomo/log" @@ -50,10 +49,6 @@ func (s *Server) SetHandler(handler handler) { } func ReCreateServer(addr string, resolver *Resolver, mapper *ResolverEnhancer) { - if features.CMFA { - UpdateIsolateHandler(resolver, mapper) - } - if addr == address && resolver != nil { handler := NewHandler(resolver, mapper) server.SetHandler(handler) diff --git a/dns/system.go b/dns/system.go index dc1006fa..944f2824 100644 --- a/dns/system.go +++ b/dns/system.go @@ -3,16 +3,11 @@ package dns import ( "context" "fmt" - "net" "strings" "sync" "time" - "github.com/metacubex/mihomo/component/resolver" - "github.com/metacubex/mihomo/log" - D "github.com/miekg/dns" - "golang.org/x/exp/slices" ) const ( @@ -31,64 +26,6 @@ type systemClient struct { lastFlush time.Time } -func (c *systemClient) getDnsClients() ([]dnsClient, error) { - c.mu.Lock() - defer c.mu.Unlock() - var err error - if time.Since(c.lastFlush) > SystemDnsFlushTime { - var nameservers []string - if nameservers, err = dnsReadConfig(); err == nil { - log.Debugln("[DNS] system dns update to %s", nameservers) - for _, addr := range nameservers { - if resolver.IsSystemDnsBlacklisted(addr) { - continue - } - if _, ok := c.dnsClients[addr]; !ok { - clients := transform( - []NameServer{{ - Addr: net.JoinHostPort(addr, "53"), - Net: "udp", - }}, - nil, - ) - if len(clients) > 0 { - c.dnsClients[addr] = &systemDnsClient{ - disableTimes: 0, - dnsClient: clients[0], - } - } - } - } - available := 0 - for nameserver, sdc := range c.dnsClients { - if slices.Contains(nameservers, nameserver) { - sdc.disableTimes = 0 // enable - available++ - } else { - if sdc.disableTimes > SystemDnsDeleteTimes { - delete(c.dnsClients, nameserver) // drop too old dnsClient - } else { - sdc.disableTimes++ - } - } - } - if available > 0 { - c.lastFlush = time.Now() - } - } - } - dnsClients := make([]dnsClient, 0, len(c.dnsClients)) - for _, sdc := range c.dnsClients { - if sdc.disableTimes == 0 { - dnsClients = append(dnsClients, sdc.dnsClient) - } - } - if len(dnsClients) > 0 { - return dnsClients, nil - } - return nil, err -} - func (c *systemClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { dnsClients, err := c.getDnsClients() if err != nil { diff --git a/dns/system_common.go b/dns/system_common.go new file mode 100644 index 00000000..06dc0b30 --- /dev/null +++ b/dns/system_common.go @@ -0,0 +1,71 @@ +//go:build !(android && cmfa) + +package dns + +import ( + "net" + "time" + + "github.com/metacubex/mihomo/component/resolver" + "github.com/metacubex/mihomo/log" + + "golang.org/x/exp/slices" +) + +func (c *systemClient) getDnsClients() ([]dnsClient, error) { + c.mu.Lock() + defer c.mu.Unlock() + var err error + if time.Since(c.lastFlush) > SystemDnsFlushTime { + var nameservers []string + if nameservers, err = dnsReadConfig(); err == nil { + log.Debugln("[DNS] system dns update to %s", nameservers) + for _, addr := range nameservers { + if resolver.IsSystemDnsBlacklisted(addr) { + continue + } + if _, ok := c.dnsClients[addr]; !ok { + clients := transform( + []NameServer{{ + Addr: net.JoinHostPort(addr, "53"), + Net: "udp", + }}, + nil, + ) + if len(clients) > 0 { + c.dnsClients[addr] = &systemDnsClient{ + disableTimes: 0, + dnsClient: clients[0], + } + } + } + } + available := 0 + for nameserver, sdc := range c.dnsClients { + if slices.Contains(nameservers, nameserver) { + sdc.disableTimes = 0 // enable + available++ + } else { + if sdc.disableTimes > SystemDnsDeleteTimes { + delete(c.dnsClients, nameserver) // drop too old dnsClient + } else { + sdc.disableTimes++ + } + } + } + if available > 0 { + c.lastFlush = time.Now() + } + } + } + dnsClients := make([]dnsClient, 0, len(c.dnsClients)) + for _, sdc := range c.dnsClients { + if sdc.disableTimes == 0 { + dnsClients = append(dnsClients, sdc.dnsClient) + } + } + if len(dnsClients) > 0 { + return dnsClients, nil + } + return nil, err +} diff --git a/dns/util.go b/dns/util.go index 50459cc1..92a86dbc 100644 --- a/dns/util.go +++ b/dns/util.go @@ -99,6 +99,10 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient { ret = append(ret, newDoHClient(s.Addr, resolver, s.PreferH3, s.Params, s.ProxyAdapter, s.ProxyName)) continue case "dhcp": + if s.Addr == "system" { // Compatible with old writing + ret = append(ret, newSystemClient()) + continue + } ret = append(ret, newDHCPClient(s.Addr)) continue case "system": diff --git a/hub/hub.go b/hub/hub.go index 04662a7b..e22f7219 100644 --- a/hub/hub.go +++ b/hub/hub.go @@ -1,10 +1,7 @@ package hub import ( - "strings" - "github.com/metacubex/mihomo/config" - "github.com/metacubex/mihomo/constant/features" "github.com/metacubex/mihomo/hub/executor" "github.com/metacubex/mihomo/hub/route" "github.com/metacubex/mihomo/log" @@ -43,11 +40,6 @@ func ApplyConfig(cfg *config.Config) { } func applyRoute(cfg *config.Config) { - if features.CMFA && strings.HasSuffix(cfg.Controller.ExternalUI, ":0") { - // CMFA have set its default override value to end with ":0" for security. - // so we direct return at here - return - } if cfg.Controller.ExternalUI != "" { route.SetUIPath(cfg.Controller.ExternalUI) } diff --git a/rules/provider/parse.go b/rules/provider/parse.go index 227debb3..b3c6af3c 100644 --- a/rules/provider/parse.go +++ b/rules/provider/parse.go @@ -8,7 +8,6 @@ import ( "github.com/metacubex/mihomo/common/structure" "github.com/metacubex/mihomo/component/resource" C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/constant/features" P "github.com/metacubex/mihomo/constant/provider" ) @@ -50,7 +49,7 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t path := C.Path.GetPathByHash("rules", schema.URL) if schema.Path != "" { path = C.Path.Resolve(schema.Path) - if !features.CMFA && !C.Path.IsSafePath(path) { + if !C.Path.IsSafePath(path) { return nil, fmt.Errorf("%w: %s", errSubPath, path) } } From 4c3fe98ebd85bc995320c99baa5937f9cd67aeaf Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Wed, 11 Sep 2024 19:24:53 +0800 Subject: [PATCH 216/311] chore: modify the default download address of ASN --- config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 0af30b39..907d40bd 100644 --- a/config/config.go +++ b/config/config.go @@ -521,7 +521,7 @@ func DefaultRawConfig() *RawConfig { }, GeoXUrl: RawGeoXUrl{ Mmdb: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb", - ASN: "https://github.com/xishang0128/geoip/releases/download/latest/GeoLite2-ASN.mmdb", + ASN: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/GeoLite2-ASN.mmdb", GeoIp: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat", GeoSite: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat", }, From e33d4a4769ac943d3fa9940bdb62b59240556cc4 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 12 Sep 2024 11:19:40 +0800 Subject: [PATCH 217/311] chore: cleanup the patch code --- adapter/provider/patch_android.go | 19 ------------------- dns/patch_android.go | 2 -- listener/http/patch_android.go | 9 --------- rules/provider/patch_android.go | 17 ----------------- 4 files changed, 47 deletions(-) delete mode 100644 adapter/provider/patch_android.go delete mode 100644 listener/http/patch_android.go delete mode 100644 rules/provider/patch_android.go diff --git a/adapter/provider/patch_android.go b/adapter/provider/patch_android.go deleted file mode 100644 index 2a91d7f1..00000000 --- a/adapter/provider/patch_android.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build android && cmfa - -package provider - -import ( - "time" -) - -var ( - suspended bool -) - -type UpdatableProvider interface { - UpdatedAt() time.Time -} - -func Suspend(s bool) { - suspended = s -} diff --git a/dns/patch_android.go b/dns/patch_android.go index 386680e7..6579ef07 100644 --- a/dns/patch_android.go +++ b/dns/patch_android.go @@ -6,8 +6,6 @@ import ( "github.com/metacubex/mihomo/component/resolver" ) -const SystemDNSPlaceholder = "system" - var systemResolver []dnsClient func FlushCacheWithDefaultResolver() { diff --git a/listener/http/patch_android.go b/listener/http/patch_android.go deleted file mode 100644 index b1b8bfc7..00000000 --- a/listener/http/patch_android.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build android && cmfa - -package http - -import "net" - -func (l *Listener) Listener() net.Listener { - return l.listener -} diff --git a/rules/provider/patch_android.go b/rules/provider/patch_android.go deleted file mode 100644 index d4b752c3..00000000 --- a/rules/provider/patch_android.go +++ /dev/null @@ -1,17 +0,0 @@ -//go:build android && cmfa - -package provider - -import "time" - -var ( - suspended bool -) - -type UpdatableProvider interface { - UpdatedAt() time.Time -} - -func Suspend(s bool) { - suspended = s -} From fb4d3c41c81c378d7b1c680bf4941cccf9f77ffa Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Tue, 17 Sep 2024 12:03:24 +0800 Subject: [PATCH 218/311] chore: simplify VlessFlow Option --- adapter/outbound/vless.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index 43b4aa21..b18bf4da 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -505,17 +505,14 @@ func NewVless(option VlessOption) (*Vless, error) { var addons *vless.Addons if option.Network != "ws" && len(option.Flow) >= 16 { option.Flow = option.Flow[:16] - switch option.Flow { - case vless.XRV: - log.Warnln("To use %s, ensure your server is upgrade to Xray-core v1.8.0+", vless.XRV) - addons = &vless.Addons{ - Flow: option.Flow, - } - case vless.XRO, vless.XRD, vless.XRS: - log.Fatalln("Legacy XTLS protocol %s is deprecated and no longer supported", option.Flow) - default: + if option.Flow != vless.XRV { return nil, fmt.Errorf("unsupported xtls flow type: %s", option.Flow) } + + log.Warnln("To use %s, ensure your server is upgrade to Xray-core v1.8.0+", vless.XRV) + addons = &vless.Addons{ + Flow: option.Flow, + } } switch option.PacketEncoding { From 58c973ee2b70a99ed7db9f10604a4dc1c4052d26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=90=BD=E5=BF=83?= <33619903+Luoxin@users.noreply.github.com> Date: Wed, 18 Sep 2024 22:36:20 +0800 Subject: [PATCH 219/311] fix: `NewRejectWithOption` has wrong type (#1518) Co-authored-by: nico --- adapter/outbound/reject.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapter/outbound/reject.go b/adapter/outbound/reject.go index 62f4aaa5..a8e2100d 100644 --- a/adapter/outbound/reject.go +++ b/adapter/outbound/reject.go @@ -37,7 +37,7 @@ func NewRejectWithOption(option RejectOption) *Reject { return &Reject{ Base: &Base{ name: option.Name, - tp: C.Direct, + tp: C.Reject, udp: true, }, } From 3676d1b79fe8567c9375d3e812f84eb683c8f77d Mon Sep 17 00:00:00 2001 From: Chun <54227644+pmnmq@users.noreply.github.com> Date: Wed, 18 Sep 2024 22:41:06 +0800 Subject: [PATCH 220/311] feat: add proxy name replacement functionality for override (#1481) * feat: add proxy name replacement functionality for override * style: modify `override schema` info and provider parse error message --------- Co-authored-by: chun --- adapter/provider/parser.go | 9 +++++++++ adapter/provider/provider.go | 18 ++++++++++++++++++ docs/config.yaml | 7 +++++++ 3 files changed, 34 insertions(+) diff --git a/adapter/provider/parser.go b/adapter/provider/parser.go index 0c606a95..4e168f7a 100644 --- a/adapter/provider/parser.go +++ b/adapter/provider/parser.go @@ -26,6 +26,13 @@ type healthCheckSchema struct { ExpectedStatus string `provider:"expected-status,omitempty"` } +type OverrideProxyNameSchema struct { + // matching expression for regex replacement + Pattern string `provider:"pattern"` + // the new content after regex matching + Target string `provider:"target"` +} + type OverrideSchema struct { TFO *bool `provider:"tfo,omitempty"` MPTcp *bool `provider:"mptcp,omitempty"` @@ -40,6 +47,8 @@ type OverrideSchema struct { IPVersion *string `provider:"ip-version,omitempty"` AdditionalPrefix *string `provider:"additional-prefix,omitempty"` AdditionalSuffix *string `provider:"additional-suffix,omitempty"` + + ProxyName []OverrideProxyNameSchema `provider:"proxy-name,omitempty"` } type proxyProviderSchema struct { diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index 5f86777c..789d6654 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -403,6 +403,24 @@ func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray case "additional-suffix": name := mapping["name"].(string) mapping["name"] = name + *field.Interface().(*string) + case "proxy-name": + exprList, ok := field.Interface().([]OverrideProxyNameSchema) + if !ok { + return nil, fmt.Errorf("can't parse proxy-provider override proxy-name, please see the docs example config") + } + // Iterate through all naming replacement rules and perform the replacements. + for _, expr := range exprList { + name := mapping["name"].(string) + nameReg, err := regexp2.Compile(expr.Pattern, regexp2.None) + if err != nil { + return nil, fmt.Errorf("parse proxy name regular expression %q error: %w", expr.Pattern, err) + } + newName, err := nameReg.Replace(name, expr.Target, 0, -1) + if err != nil { + return nil, fmt.Errorf("proxy name replace error: %w", err) + } + mapping["name"] = newName + } default: mapping[fieldName] = field.Elem().Interface() } diff --git a/docs/config.yaml b/docs/config.yaml index 78e62d12..f7b573eb 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -929,6 +929,13 @@ proxy-providers: # ip-version: ipv4-prefer # additional-prefix: "[provider1]" # additional-suffix: "test" + # # 名字替换,支持正则表达式 + # proxy-name: + # - pattern: "test" + # target: "TEST" + # - pattern: "IPLC-(.*?)倍" + # target: "iplc x $1" + test: type: file path: /test.yaml From f020b20ab932bd68108fed34323add4d20e7236e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 19 Sep 2024 14:04:38 +0800 Subject: [PATCH 221/311] chore: structure support encoding.TextUnmarshaler --- common/structure/structure.go | 83 +++++++++++++++++++--------- common/structure/structure_test.go | 88 ++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 27 deletions(-) diff --git a/common/structure/structure.go b/common/structure/structure.go index fde22309..99c980bc 100644 --- a/common/structure/structure.go +++ b/common/structure/structure.go @@ -3,6 +3,7 @@ package structure // references: https://github.com/mitchellh/mapstructure import ( + "encoding" "encoding/base64" "fmt" "reflect" @@ -86,35 +87,41 @@ func (d *Decoder) Decode(src map[string]any, dst any) error { } func (d *Decoder) decode(name string, data any, val reflect.Value) error { - kind := val.Kind() - switch { - case isInt(kind): - return d.decodeInt(name, data, val) - case isUint(kind): - return d.decodeUint(name, data, val) - case isFloat(kind): - return d.decodeFloat(name, data, val) - } - switch kind { - case reflect.Pointer: - if val.IsNil() { + for { + kind := val.Kind() + if kind == reflect.Pointer && val.IsNil() { val.Set(reflect.New(val.Type().Elem())) } - return d.decode(name, data, val.Elem()) - case reflect.String: - return d.decodeString(name, data, val) - case reflect.Bool: - return d.decodeBool(name, data, val) - case reflect.Slice: - return d.decodeSlice(name, data, val) - case reflect.Map: - return d.decodeMap(name, data, val) - case reflect.Interface: - return d.setInterface(name, data, val) - case reflect.Struct: - return d.decodeStruct(name, data, val) - default: - return fmt.Errorf("type %s not support", val.Kind().String()) + if ok, err := d.decodeTextUnmarshaller(name, data, val); ok { + return err + } + switch { + case isInt(kind): + return d.decodeInt(name, data, val) + case isUint(kind): + return d.decodeUint(name, data, val) + case isFloat(kind): + return d.decodeFloat(name, data, val) + } + switch kind { + case reflect.Pointer: + val = val.Elem() + continue + case reflect.String: + return d.decodeString(name, data, val) + case reflect.Bool: + return d.decodeBool(name, data, val) + case reflect.Slice: + return d.decodeSlice(name, data, val) + case reflect.Map: + return d.decodeMap(name, data, val) + case reflect.Interface: + return d.setInterface(name, data, val) + case reflect.Struct: + return d.decodeStruct(name, data, val) + default: + return fmt.Errorf("type %s not support", val.Kind().String()) + } } } @@ -553,3 +560,25 @@ func (d *Decoder) setInterface(name string, data any, val reflect.Value) (err er val.Set(dataVal) return nil } + +func (d *Decoder) decodeTextUnmarshaller(name string, data any, val reflect.Value) (bool, error) { + if !val.CanAddr() { + return false, nil + } + valAddr := val.Addr() + if !valAddr.CanInterface() { + return false, nil + } + unmarshaller, ok := valAddr.Interface().(encoding.TextUnmarshaler) + if !ok { + return false, nil + } + var str string + if err := d.decodeString(name, data, reflect.Indirect(reflect.ValueOf(&str))); err != nil { + return false, err + } + if err := unmarshaller.UnmarshalText([]byte(str)); err != nil { + return true, fmt.Errorf("cannot parse '%s' as %s: %s", name, val.Type(), err) + } + return true, nil +} diff --git a/common/structure/structure_test.go b/common/structure/structure_test.go index 9f31d3d1..e5fe693d 100644 --- a/common/structure/structure_test.go +++ b/common/structure/structure_test.go @@ -1,6 +1,7 @@ package structure import ( + "strconv" "testing" "github.com/stretchr/testify/assert" @@ -179,3 +180,90 @@ func TestStructure_SliceNilValueComplex(t *testing.T) { err = decoder.Decode(rawMap, ss) assert.NotNil(t, err) } + +func TestStructure_SliceCap(t *testing.T) { + rawMap := map[string]any{ + "foo": []string{}, + } + + s := &struct { + Foo []string `test:"foo,omitempty"` + Bar []string `test:"bar,omitempty"` + }{} + + err := decoder.Decode(rawMap, s) + assert.Nil(t, err) + assert.NotNil(t, s.Foo) // structure's Decode will ensure value not nil when input has value even it was set an empty array + assert.Nil(t, s.Bar) +} + +func TestStructure_Base64(t *testing.T) { + rawMap := map[string]any{ + "foo": "AQID", + } + + s := &struct { + Foo []byte `test:"foo"` + }{} + + err := decoder.Decode(rawMap, s) + assert.Nil(t, err) + assert.Equal(t, []byte{1, 2, 3}, s.Foo) +} + +func TestStructure_Pointer(t *testing.T) { + rawMap := map[string]any{ + "foo": "foo", + } + + s := &struct { + Foo *string `test:"foo,omitempty"` + Bar *string `test:"bar,omitempty"` + }{} + + err := decoder.Decode(rawMap, s) + assert.Nil(t, err) + assert.NotNil(t, s.Foo) + assert.Equal(t, "foo", *s.Foo) + assert.Nil(t, s.Bar) +} + +type num struct { + a int +} + +func (n *num) UnmarshalText(text []byte) (err error) { + n.a, err = strconv.Atoi(string(text)) + return +} + +func TestStructure_TextUnmarshaller(t *testing.T) { + rawMap := map[string]any{ + "num": "255", + "num_p": "127", + } + + s := &struct { + Num num `test:"num"` + NumP *num `test:"num_p"` + }{} + + err := decoder.Decode(rawMap, s) + assert.Nil(t, err) + assert.Equal(t, 255, s.Num.a) + assert.NotNil(t, s.NumP) + assert.Equal(t, s.NumP.a, 127) + + // test WeaklyTypedInput + rawMap["num"] = 256 + err = decoder.Decode(rawMap, s) + assert.NotNilf(t, err, "should throw error: %#v", s) + err = weakTypeDecoder.Decode(rawMap, s) + assert.Nil(t, err) + assert.Equal(t, 256, s.Num.a) + + // test invalid input + rawMap["num_p"] = "abc" + err = decoder.Decode(rawMap, s) + assert.NotNilf(t, err, "should throw error: %#v", s) +} From 794645b7f895f8d2484a4006dd8f12d9f31552b8 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 19 Sep 2024 14:24:35 +0800 Subject: [PATCH 222/311] chore: direct using structure package decode `proxy-name` --- adapter/provider/parser.go | 7 ++++++- adapter/provider/provider.go | 12 ++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/adapter/provider/parser.go b/adapter/provider/parser.go index 4e168f7a..078e8695 100644 --- a/adapter/provider/parser.go +++ b/adapter/provider/parser.go @@ -1,6 +1,7 @@ package provider import ( + "encoding" "errors" "fmt" "time" @@ -10,6 +11,8 @@ import ( "github.com/metacubex/mihomo/component/resource" C "github.com/metacubex/mihomo/constant" types "github.com/metacubex/mihomo/constant/provider" + + "github.com/dlclark/regexp2" ) var ( @@ -28,11 +31,13 @@ type healthCheckSchema struct { type OverrideProxyNameSchema struct { // matching expression for regex replacement - Pattern string `provider:"pattern"` + Pattern *regexp2.Regexp `provider:"pattern"` // the new content after regex matching Target string `provider:"target"` } +var _ encoding.TextUnmarshaler = (*regexp2.Regexp)(nil) // ensure *regexp2.Regexp can decode direct by structure package + type OverrideSchema struct { TFO *bool `provider:"tfo,omitempty"` MPTcp *bool `provider:"mptcp,omitempty"` diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index 789d6654..3103b508 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -404,18 +404,10 @@ func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray name := mapping["name"].(string) mapping["name"] = name + *field.Interface().(*string) case "proxy-name": - exprList, ok := field.Interface().([]OverrideProxyNameSchema) - if !ok { - return nil, fmt.Errorf("can't parse proxy-provider override proxy-name, please see the docs example config") - } // Iterate through all naming replacement rules and perform the replacements. - for _, expr := range exprList { + for _, expr := range override.ProxyName { name := mapping["name"].(string) - nameReg, err := regexp2.Compile(expr.Pattern, regexp2.None) - if err != nil { - return nil, fmt.Errorf("parse proxy name regular expression %q error: %w", expr.Pattern, err) - } - newName, err := nameReg.Replace(name, expr.Target, 0, -1) + newName, err := expr.Pattern.Replace(name, expr.Target, 0, -1) if err != nil { return nil, fmt.Errorf("proxy name replace error: %w", err) } From a08aa10630813485d7899e65ff59c3fae56f8364 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 19 Sep 2024 18:06:36 +0800 Subject: [PATCH 223/311] chore: some internal types support encoding.TextUnmarshaler --- constant/dns.go | 37 +++++++++++++++++++++++++++++++++---- constant/tun.go | 15 +++++++++++++++ log/level.go | 26 +++++++++++++++++++++----- tunnel/mode.go | 45 ++++++++++++++++++++++++++++++--------------- tunnel/status.go | 45 ++++++++++++++++++++++++++++++--------------- 5 files changed, 129 insertions(+), 39 deletions(-) diff --git a/constant/dns.go b/constant/dns.go index 8d038a6b..b0874eef 100644 --- a/constant/dns.go +++ b/constant/dns.go @@ -3,6 +3,7 @@ package constant import ( "encoding/json" "errors" + "strings" ) // DNSModeMapping is a mapping for EnhancedMode enum @@ -27,7 +28,7 @@ func (e *DNSMode) UnmarshalYAML(unmarshal func(any) error) error { if err := unmarshal(&tp); err != nil { return err } - mode, exist := DNSModeMapping[tp] + mode, exist := DNSModeMapping[strings.ToLower(tp)] if !exist { return errors.New("invalid mode") } @@ -46,7 +47,7 @@ func (e *DNSMode) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &tp); err != nil { return err } - mode, exist := DNSModeMapping[tp] + mode, exist := DNSModeMapping[strings.ToLower(tp)] if !exist { return errors.New("invalid mode") } @@ -59,6 +60,21 @@ func (e DNSMode) MarshalJSON() ([]byte, error) { return json.Marshal(e.String()) } +// UnmarshalText unserialize EnhancedMode +func (e *DNSMode) UnmarshalText(data []byte) error { + mode, exist := DNSModeMapping[strings.ToLower(string(data))] + if !exist { + return errors.New("invalid mode") + } + *e = mode + return nil +} + +// MarshalText serialize EnhancedMode +func (e DNSMode) MarshalText() ([]byte, error) { + return []byte(e.String()), nil +} + func (e DNSMode) String() string { switch e { case DNSNormal: @@ -150,7 +166,7 @@ func (e *FilterMode) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := unmarshal(&tp); err != nil { return err } - mode, exist := FilterModeMapping[tp] + mode, exist := FilterModeMapping[strings.ToLower(tp)] if !exist { return errors.New("invalid mode") } @@ -167,7 +183,20 @@ func (e *FilterMode) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &tp); err != nil { return err } - mode, exist := FilterModeMapping[tp] + mode, exist := FilterModeMapping[strings.ToLower(tp)] + if !exist { + return errors.New("invalid mode") + } + *e = mode + return nil +} + +func (e FilterMode) MarshalText() ([]byte, error) { + return []byte(e.String()), nil +} + +func (e *FilterMode) UnmarshalText(data []byte) error { + mode, exist := FilterModeMapping[strings.ToLower(string(data))] if !exist { return errors.New("invalid mode") } diff --git a/constant/tun.go b/constant/tun.go index f6c0e011..669f7a2e 100644 --- a/constant/tun.go +++ b/constant/tun.go @@ -56,6 +56,21 @@ func (e TUNStack) MarshalJSON() ([]byte, error) { return json.Marshal(e.String()) } +// UnmarshalText unserialize TUNStack +func (e *TUNStack) UnmarshalText(data []byte) error { + mode, exist := StackTypeMapping[strings.ToLower(string(data))] + if !exist { + return errors.New("invalid tun stack") + } + *e = mode + return nil +} + +// MarshalText serialize TUNStack with json +func (e TUNStack) MarshalText() ([]byte, error) { + return []byte(e.String()), nil +} + func (e TUNStack) String() string { switch e { case TunGvisor: diff --git a/log/level.go b/log/level.go index ea06ee4b..a4c6ecbd 100644 --- a/log/level.go +++ b/log/level.go @@ -3,6 +3,7 @@ package log import ( "encoding/json" "errors" + "strings" ) // LogLevelMapping is a mapping for LogLevel enum @@ -28,7 +29,7 @@ type LogLevel int func (l *LogLevel) UnmarshalYAML(unmarshal func(any) error) error { var tp string unmarshal(&tp) - level, exist := LogLevelMapping[tp] + level, exist := LogLevelMapping[strings.ToLower(tp)] if !exist { return errors.New("invalid mode") } @@ -40,7 +41,7 @@ func (l *LogLevel) UnmarshalYAML(unmarshal func(any) error) error { func (l *LogLevel) UnmarshalJSON(data []byte) error { var tp string json.Unmarshal(data, &tp) - level, exist := LogLevelMapping[tp] + level, exist := LogLevelMapping[strings.ToLower(tp)] if !exist { return errors.New("invalid mode") } @@ -48,9 +49,14 @@ func (l *LogLevel) UnmarshalJSON(data []byte) error { return nil } -// MarshalJSON serialize LogLevel with json -func (l LogLevel) MarshalJSON() ([]byte, error) { - return json.Marshal(l.String()) +// UnmarshalText unserialize LogLevel +func (l *LogLevel) UnmarshalText(data []byte) error { + level, exist := LogLevelMapping[strings.ToLower(string(data))] + if !exist { + return errors.New("invalid mode") + } + *l = level + return nil } // MarshalYAML serialize LogLevel with yaml @@ -58,6 +64,16 @@ func (l LogLevel) MarshalYAML() (any, error) { return l.String(), nil } +// MarshalJSON serialize LogLevel with json +func (l LogLevel) MarshalJSON() ([]byte, error) { + return json.Marshal(l.String()) +} + +// MarshalText serialize LogLevel +func (l LogLevel) MarshalText() ([]byte, error) { + return []byte(l.String()), nil +} + func (l LogLevel) String() string { switch l { case INFO: diff --git a/tunnel/mode.go b/tunnel/mode.go index a1697a32..dd89c3a7 100644 --- a/tunnel/mode.go +++ b/tunnel/mode.go @@ -21,18 +21,6 @@ const ( Direct ) -// UnmarshalJSON unserialize Mode -func (m *TunnelMode) UnmarshalJSON(data []byte) error { - var tp string - json.Unmarshal(data, &tp) - mode, exist := ModeMapping[strings.ToLower(tp)] - if !exist { - return errors.New("invalid mode") - } - *m = mode - return nil -} - // UnmarshalYAML unserialize Mode with yaml func (m *TunnelMode) UnmarshalYAML(unmarshal func(any) error) error { var tp string @@ -45,9 +33,26 @@ func (m *TunnelMode) UnmarshalYAML(unmarshal func(any) error) error { return nil } -// MarshalJSON serialize Mode -func (m TunnelMode) MarshalJSON() ([]byte, error) { - return json.Marshal(m.String()) +// UnmarshalJSON unserialize Mode +func (m *TunnelMode) UnmarshalJSON(data []byte) error { + var tp string + json.Unmarshal(data, &tp) + mode, exist := ModeMapping[strings.ToLower(tp)] + if !exist { + return errors.New("invalid mode") + } + *m = mode + return nil +} + +// UnmarshalText unserialize Mode +func (m *TunnelMode) UnmarshalText(data []byte) error { + mode, exist := ModeMapping[strings.ToLower(string(data))] + if !exist { + return errors.New("invalid mode") + } + *m = mode + return nil } // MarshalYAML serialize TunnelMode with yaml @@ -55,6 +60,16 @@ func (m TunnelMode) MarshalYAML() (any, error) { return m.String(), nil } +// MarshalJSON serialize Mode +func (m TunnelMode) MarshalJSON() ([]byte, error) { + return json.Marshal(m.String()) +} + +// MarshalText serialize Mode +func (m TunnelMode) MarshalText() ([]byte, error) { + return []byte(m.String()), nil +} + func (m TunnelMode) String() string { switch m { case Global: diff --git a/tunnel/status.go b/tunnel/status.go index d81dd45e..388d597b 100644 --- a/tunnel/status.go +++ b/tunnel/status.go @@ -22,18 +22,6 @@ const ( Running ) -// UnmarshalJSON unserialize Status -func (s *TunnelStatus) UnmarshalJSON(data []byte) error { - var tp string - json.Unmarshal(data, &tp) - status, exist := StatusMapping[strings.ToLower(tp)] - if !exist { - return errors.New("invalid mode") - } - *s = status - return nil -} - // UnmarshalYAML unserialize Status with yaml func (s *TunnelStatus) UnmarshalYAML(unmarshal func(any) error) error { var tp string @@ -46,9 +34,26 @@ func (s *TunnelStatus) UnmarshalYAML(unmarshal func(any) error) error { return nil } -// MarshalJSON serialize Status -func (s TunnelStatus) MarshalJSON() ([]byte, error) { - return json.Marshal(s.String()) +// UnmarshalJSON unserialize Status +func (s *TunnelStatus) UnmarshalJSON(data []byte) error { + var tp string + json.Unmarshal(data, &tp) + status, exist := StatusMapping[strings.ToLower(tp)] + if !exist { + return errors.New("invalid status") + } + *s = status + return nil +} + +// UnmarshalText unserialize Status +func (s *TunnelStatus) UnmarshalText(data []byte) error { + status, exist := StatusMapping[strings.ToLower(string(data))] + if !exist { + return errors.New("invalid status") + } + *s = status + return nil } // MarshalYAML serialize TunnelMode with yaml @@ -56,6 +61,16 @@ func (s TunnelStatus) MarshalYAML() (any, error) { return s.String(), nil } +// MarshalJSON serialize Status +func (s TunnelStatus) MarshalJSON() ([]byte, error) { + return json.Marshal(s.String()) +} + +// MarshalText serialize Status +func (s TunnelStatus) MarshalText() ([]byte, error) { + return []byte(s.String()), nil +} + func (s TunnelStatus) String() string { switch s { case Suspend: From f52fe6aa74131a4273347c0d1fcad92891d86920 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 21 Sep 2024 19:46:39 +0800 Subject: [PATCH 224/311] fix: tun.device not shown in restful api --- listener/sing_tun/server.go | 1 + 1 file changed, 1 insertion(+) diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index a135a301..c2c668b3 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -138,6 +138,7 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis tunName := options.Device if options.FileDescriptor == 0 && (tunName == "" || !checkTunName(tunName)) { tunName = CalculateInterfaceName(InterfaceName) + options.Device = tunName } routeAddress := options.RouteAddress if len(options.Inet4RouteAddress) > 0 { From d80e8bb0c29d7a878722513cf2513bca82607a4f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 21 Sep 2024 20:03:17 +0800 Subject: [PATCH 225/311] chore: remove some confusing restrictions on comma separation in NameServerPolicy configuration --- config/config.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/config/config.go b/config/config.go index 907d40bd..5a95de79 100644 --- a/config/config.go +++ b/config/config.go @@ -8,7 +8,6 @@ import ( "net/netip" "net/url" "path" - "regexp" "strings" "time" @@ -1287,7 +1286,6 @@ func parsePureDNSServer(server string) string { func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], ruleProviders map[string]providerTypes.RuleProvider, respectRules bool, preferH3 bool) ([]dns.Policy, error) { var policy []dns.Policy - re := regexp.MustCompile(`[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?`) for pair := nsPolicy.Oldest(); pair != nil; pair = pair.Next() { k, v := pair.Key, pair.Value @@ -1299,8 +1297,9 @@ func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], rulePro if err != nil { return nil, err } - if strings.Contains(strings.ToLower(k), ",") { - if strings.Contains(k, "geosite:") { + kLower := strings.ToLower(k) + if strings.Contains(kLower, ",") { + if strings.Contains(kLower, "geosite:") { subkeys := strings.Split(k, ":") subkeys = subkeys[1:] subkeys = strings.Split(subkeys[0], ",") @@ -1308,7 +1307,7 @@ func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], rulePro newKey := "geosite:" + subkey policy = append(policy, dns.Policy{Domain: newKey, NameServers: nameservers}) } - } else if strings.Contains(strings.ToLower(k), "rule-set:") { + } else if strings.Contains(kLower, "rule-set:") { subkeys := strings.Split(k, ":") subkeys = subkeys[1:] subkeys = strings.Split(subkeys[0], ",") @@ -1316,16 +1315,16 @@ func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], rulePro newKey := "rule-set:" + subkey policy = append(policy, dns.Policy{Domain: newKey, NameServers: nameservers}) } - } else if re.MatchString(k) { + } else { subkeys := strings.Split(k, ",") for _, subkey := range subkeys { policy = append(policy, dns.Policy{Domain: subkey, NameServers: nameservers}) } } } else { - if strings.Contains(strings.ToLower(k), "geosite:") { + if strings.Contains(kLower, "geosite:") { policy = append(policy, dns.Policy{Domain: "geosite:" + k[8:], NameServers: nameservers}) - } else if strings.Contains(strings.ToLower(k), "rule-set:") { + } else if strings.Contains(kLower, "rule-set:") { policy = append(policy, dns.Policy{Domain: "rule-set:" + k[9:], NameServers: nameservers}) } else { policy = append(policy, dns.Policy{Domain: k, NameServers: nameservers}) From 7dafe7889e994ff140e8e5c71910afd3d6f81760 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 21 Sep 2024 21:03:59 +0800 Subject: [PATCH 226/311] chore: disallow space at begin or end in DomainTrie --- component/trie/domain.go | 10 ++++++++++ component/trie/domain_test.go | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/component/trie/domain.go b/component/trie/domain.go index 87dfeda6..574a59ca 100644 --- a/component/trie/domain.go +++ b/component/trie/domain.go @@ -3,6 +3,8 @@ package trie import ( "errors" "strings" + "unicode" + "unicode/utf8" ) const ( @@ -25,6 +27,14 @@ func ValidAndSplitDomain(domain string) ([]string, bool) { if domain != "" && domain[len(domain)-1] == '.' { return nil, false } + if domain != "" { + if r, _ := utf8.DecodeRuneInString(domain); unicode.IsSpace(r) { + return nil, false + } + if r, _ := utf8.DecodeLastRuneInString(domain); unicode.IsSpace(r) { + return nil, false + } + } domain = strings.ToLower(domain) parts := strings.Split(domain, domainStep) if len(parts) == 1 { diff --git a/component/trie/domain_test.go b/component/trie/domain_test.go index 916f6107..6aab72d3 100644 --- a/component/trie/domain_test.go +++ b/component/trie/domain_test.go @@ -127,3 +127,14 @@ func TestTrie_Foreach(t *testing.T) { }) assert.Equal(t, 7, count) } + +func TestTrie_Space(t *testing.T) { + validDomain := func(domain string) bool { + _, ok := trie.ValidAndSplitDomain(domain) + return ok + } + assert.True(t, validDomain("google.com")) + assert.False(t, validDomain(" google.com")) + assert.False(t, validDomain(" google.com ")) + assert.True(t, validDomain("Mijia Cloud")) +} From 223eae0e067429d8a19bf833058c412478ef5b57 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 22 Sep 2024 00:24:49 +0800 Subject: [PATCH 227/311] chore: force refresh provider in background --- adapter/provider/provider.go | 4 ++-- component/resource/fetcher.go | 30 ++++++++++-------------------- component/resource/vehicle.go | 4 ++++ constant/provider/interface.go | 1 + 4 files changed, 17 insertions(+), 22 deletions(-) diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index 3103b508..4381c24d 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -128,7 +128,7 @@ func (pp *proxySetProvider) getSubscriptionInfo() { go func() { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() - resp, err := mihomoHttp.HttpRequestWithProxy(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(), + resp, err := mihomoHttp.HttpRequestWithProxy(ctx, pp.Vehicle().Url(), http.MethodGet, nil, nil, pp.Vehicle().Proxy()) if err != nil { return @@ -137,7 +137,7 @@ func (pp *proxySetProvider) getSubscriptionInfo() { userInfoStr := strings.TrimSpace(resp.Header.Get("subscription-userinfo")) if userInfoStr == "" { - resp2, err := mihomoHttp.HttpRequestWithProxy(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(), + resp2, err := mihomoHttp.HttpRequestWithProxy(ctx, pp.Vehicle().Url(), http.MethodGet, http.Header{"User-Agent": {"Quantumultx"}}, nil, pp.Vehicle().Proxy()) if err != nil { return diff --git a/component/resource/fetcher.go b/component/resource/fetcher.go index 9a4f5a75..fec9fe77 100644 --- a/component/resource/fetcher.go +++ b/component/resource/fetcher.go @@ -65,8 +65,7 @@ func (f *Fetcher[V]) Initial() (V, error) { modTime := stat.ModTime() f.updatedAt = modTime isLocal = true - if f.interval != 0 && modTime.Add(f.interval).Before(time.Now()) { - log.Warnln("[Provider] %s not updated for a long time, force refresh", f.Name()) + if time.Since(modTime) > f.interval { forceUpdate = true } } else { @@ -78,21 +77,7 @@ func (f *Fetcher[V]) Initial() (V, error) { return lo.Empty[V](), err } - var contents V - if forceUpdate { - var forceBuf []byte - if forceBuf, err = f.vehicle.Read(f.ctx); err == nil { - if contents, err = f.parser(forceBuf); err == nil { - isLocal = false - buf = forceBuf - } - } - } - - if err != nil || !forceUpdate { - contents, err = f.parser(buf) - } - + contents, err := f.parser(buf) if err != nil { if !isLocal { return lo.Empty[V](), err @@ -135,7 +120,7 @@ func (f *Fetcher[V]) Initial() (V, error) { return lo.Empty[V](), err } } else if f.interval > 0 { - go f.pullLoop() + go f.pullLoop(forceUpdate) } return contents, nil @@ -164,7 +149,7 @@ func (f *Fetcher[V]) SideUpdate(buf []byte) (V, bool, error) { } if f.vehicle.Type() != types.File { - if err := safeWrite(f.vehicle.Path(), buf); err != nil { + if err = safeWrite(f.vehicle.Path(), buf); err != nil { return lo.Empty[V](), false, err } } @@ -183,12 +168,17 @@ func (f *Fetcher[V]) Close() error { return nil } -func (f *Fetcher[V]) pullLoop() { +func (f *Fetcher[V]) pullLoop(forceUpdate bool) { initialInterval := f.interval - time.Since(f.updatedAt) if initialInterval > f.interval { initialInterval = f.interval } + if forceUpdate { + log.Warnln("[Provider] %s not updated for a long time, force refresh", f.Name()) + f.update(f.vehicle.Path()) + } + timer := time.NewTimer(initialInterval) defer timer.Stop() for { diff --git a/component/resource/vehicle.go b/component/resource/vehicle.go index 4618ef52..74324e6d 100644 --- a/component/resource/vehicle.go +++ b/component/resource/vehicle.go @@ -24,6 +24,10 @@ func (f *FileVehicle) Path() string { return f.path } +func (f *FileVehicle) Url() string { + return "file://" + f.path +} + func (f *FileVehicle) Read(ctx context.Context) ([]byte, error) { return os.ReadFile(f.path) } diff --git a/constant/provider/interface.go b/constant/provider/interface.go index 880bdadf..9d24f691 100644 --- a/constant/provider/interface.go +++ b/constant/provider/interface.go @@ -34,6 +34,7 @@ func (v VehicleType) String() string { type Vehicle interface { Read(ctx context.Context) ([]byte, error) Path() string + Url() string Proxy() string Type() VehicleType } From 5d242510c8caf8ecbe41ee6e68d3b692a29bb50b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 22 Sep 2024 11:36:31 +0800 Subject: [PATCH 228/311] chore: support ETag for providers --- adapter/provider/provider.go | 8 +- component/profile/cachefile/cache.go | 55 ++++++++++ component/resource/fetcher.go | 145 +++++++++++++-------------- component/resource/vehicle.go | 51 ++++++++-- constant/provider/hash.go | 29 ++++++ constant/provider/interface.go | 2 +- rules/provider/provider.go | 16 +-- 7 files changed, 201 insertions(+), 105 deletions(-) create mode 100644 constant/provider/hash.go diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index 4381c24d..79a752a6 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -71,19 +71,15 @@ func (pp *proxySetProvider) HealthCheck() { } func (pp *proxySetProvider) Update() error { - elm, same, err := pp.Fetcher.Update() - if err == nil && !same { - pp.OnUpdate(elm) - } + _, _, err := pp.Fetcher.Update() return err } func (pp *proxySetProvider) Initial() error { - elm, err := pp.Fetcher.Initial() + _, err := pp.Fetcher.Initial() if err != nil { return err } - pp.OnUpdate(elm) pp.getSubscriptionInfo() pp.closeAllConnections() return nil diff --git a/component/profile/cachefile/cache.go b/component/profile/cachefile/cache.go index e3da0369..0591c92b 100644 --- a/component/profile/cachefile/cache.go +++ b/component/profile/cachefile/cache.go @@ -1,6 +1,7 @@ package cachefile import ( + "math" "os" "sync" "time" @@ -19,6 +20,7 @@ var ( bucketSelected = []byte("selected") bucketFakeip = []byte("fakeip") + bucketETag = []byte("etag") ) // CacheFile store and update the cache file @@ -143,6 +145,59 @@ func (c *CacheFile) FlushFakeIP() error { return err } +func (c *CacheFile) SetETagWithHash(url string, hash []byte, etag string) { + if c.DB == nil { + return + } + + lenHash := len(hash) + if lenHash > math.MaxUint8 { + return // maybe panic is better + } + + data := make([]byte, 1, 1+lenHash+len(etag)) + data[0] = uint8(lenHash) + data = append(data, hash...) + data = append(data, etag...) + + err := c.DB.Batch(func(t *bbolt.Tx) error { + bucket, err := t.CreateBucketIfNotExists(bucketETag) + if err != nil { + return err + } + + return bucket.Put([]byte(url), data) + }) + if err != nil { + log.Warnln("[CacheFile] write cache to %s failed: %s", c.DB.Path(), err.Error()) + return + } +} +func (c *CacheFile) GetETagWithHash(key string) (hash []byte, etag string) { + if c.DB == nil { + return + } + var value []byte + c.DB.View(func(t *bbolt.Tx) error { + if bucket := t.Bucket(bucketETag); bucket != nil { + if v := bucket.Get([]byte(key)); v != nil { + value = v + } + } + return nil + }) + if len(value) == 0 { + return + } + lenHash := int(value[0]) + if len(value) < 1+lenHash { + return + } + hash = value[1 : 1+lenHash] + etag = string(value[1+lenHash:]) + return +} + func (c *CacheFile) Close() error { return c.DB.Close() } diff --git a/component/resource/fetcher.go b/component/resource/fetcher.go index fec9fe77..0b15e6c3 100644 --- a/component/resource/fetcher.go +++ b/component/resource/fetcher.go @@ -1,9 +1,7 @@ package resource import ( - "bytes" "context" - "crypto/md5" "os" "path/filepath" "time" @@ -29,10 +27,10 @@ type Fetcher[V any] struct { name string vehicle types.Vehicle updatedAt time.Time - hash [16]byte + hash types.HashType parser Parser[V] interval time.Duration - OnUpdate func(V) + onUpdate func(V) watcher *fswatch.Watcher } @@ -54,92 +52,63 @@ func (f *Fetcher[V]) UpdatedAt() time.Time { func (f *Fetcher[V]) Initial() (V, error) { var ( - buf []byte - err error - isLocal bool - forceUpdate bool + buf []byte + contents V + err error ) if stat, fErr := os.Stat(f.vehicle.Path()); fErr == nil { + // local file exists, use it first buf, err = os.ReadFile(f.vehicle.Path()) modTime := stat.ModTime() - f.updatedAt = modTime - isLocal = true - if time.Since(modTime) > f.interval { - forceUpdate = true + contents, _, err = f.loadBuf(buf, types.MakeHash(buf), false) + f.updatedAt = modTime // reset updatedAt to file's modTime + + if err == nil { + err = f.startPullLoop(time.Since(modTime) > f.interval) + if err != nil { + return lo.Empty[V](), err + } + return contents, nil } - } else { - buf, err = f.vehicle.Read(f.ctx) - f.updatedAt = time.Now() } + // parse local file error, fallback to remote + contents, _, err = f.Update() + if err != nil { return lo.Empty[V](), err } - - contents, err := f.parser(buf) + err = f.startPullLoop(false) if err != nil { - if !isLocal { - return lo.Empty[V](), err - } - - // parse local file error, fallback to remote - buf, err = f.vehicle.Read(f.ctx) - if err != nil { - return lo.Empty[V](), err - } - - contents, err = f.parser(buf) - if err != nil { - return lo.Empty[V](), err - } - - isLocal = false + return lo.Empty[V](), err } - - if f.vehicle.Type() != types.File && !isLocal { - if err := safeWrite(f.vehicle.Path(), buf); err != nil { - return lo.Empty[V](), err - } - } - - f.hash = md5.Sum(buf) - - // pull contents automatically - if f.vehicle.Type() == types.File { - f.watcher, err = fswatch.NewWatcher(fswatch.Options{ - Path: []string{f.vehicle.Path()}, - Direct: true, - Callback: f.update, - }) - if err != nil { - return lo.Empty[V](), err - } - err = f.watcher.Start() - if err != nil { - return lo.Empty[V](), err - } - } else if f.interval > 0 { - go f.pullLoop(forceUpdate) - } - return contents, nil } func (f *Fetcher[V]) Update() (V, bool, error) { - buf, err := f.vehicle.Read(f.ctx) + buf, hash, err := f.vehicle.Read(f.ctx, f.hash) if err != nil { return lo.Empty[V](), false, err } - return f.SideUpdate(buf) + return f.loadBuf(buf, hash, f.vehicle.Type() != types.File) } func (f *Fetcher[V]) SideUpdate(buf []byte) (V, bool, error) { + return f.loadBuf(buf, types.MakeHash(buf), true) +} + +func (f *Fetcher[V]) loadBuf(buf []byte, hash types.HashType, updateFile bool) (V, bool, error) { now := time.Now() - hash := md5.Sum(buf) - if bytes.Equal(f.hash[:], hash[:]) { + if f.hash.Equal(hash) { + if updateFile { + _ = os.Chtimes(f.vehicle.Path(), now, now) + } f.updatedAt = now - _ = os.Chtimes(f.vehicle.Path(), now, now) + return lo.Empty[V](), true, nil + } + + if buf == nil { // f.hash has been changed between f.vehicle.Read but should not happen (cause by concurrent) return lo.Empty[V](), true, nil } @@ -148,15 +117,18 @@ func (f *Fetcher[V]) SideUpdate(buf []byte) (V, bool, error) { return lo.Empty[V](), false, err } - if f.vehicle.Type() != types.File { + if updateFile { if err = safeWrite(f.vehicle.Path(), buf); err != nil { return lo.Empty[V](), false, err } } - f.updatedAt = now f.hash = hash + if f.onUpdate != nil { + f.onUpdate(contents) + } + return contents, false, nil } @@ -176,7 +148,7 @@ func (f *Fetcher[V]) pullLoop(forceUpdate bool) { if forceUpdate { log.Warnln("[Provider] %s not updated for a long time, force refresh", f.Name()) - f.update(f.vehicle.Path()) + f.updateWithLog() } timer := time.NewTimer(initialInterval) @@ -185,15 +157,40 @@ func (f *Fetcher[V]) pullLoop(forceUpdate bool) { select { case <-timer.C: timer.Reset(f.interval) - f.update(f.vehicle.Path()) + f.updateWithLog() case <-f.ctx.Done(): return } } } -func (f *Fetcher[V]) update(path string) { - elm, same, err := f.Update() +func (f *Fetcher[V]) startPullLoop(forceUpdate bool) (err error) { + // pull contents automatically + if f.vehicle.Type() == types.File { + f.watcher, err = fswatch.NewWatcher(fswatch.Options{ + Path: []string{f.vehicle.Path()}, + Direct: true, + Callback: f.updateCallback, + }) + if err != nil { + return err + } + err = f.watcher.Start() + if err != nil { + return err + } + } else if f.interval > 0 { + go f.pullLoop(forceUpdate) + } + return +} + +func (f *Fetcher[V]) updateCallback(path string) { + f.updateWithLog() +} + +func (f *Fetcher[V]) updateWithLog() { + _, same, err := f.Update() if err != nil { log.Errorln("[Provider] %s pull error: %s", f.Name(), err.Error()) return @@ -205,9 +202,7 @@ func (f *Fetcher[V]) update(path string) { } log.Infoln("[Provider] %s's content update", f.Name()) - if f.OnUpdate != nil { - f.OnUpdate(elm) - } + return } func safeWrite(path string, buf []byte) error { @@ -230,7 +225,7 @@ func NewFetcher[V any](name string, interval time.Duration, vehicle types.Vehicl name: name, vehicle: vehicle, parser: parser, - OnUpdate: onUpdate, + onUpdate: onUpdate, interval: interval, } } diff --git a/component/resource/vehicle.go b/component/resource/vehicle.go index 74324e6d..ccc59aec 100644 --- a/component/resource/vehicle.go +++ b/component/resource/vehicle.go @@ -9,6 +9,7 @@ import ( "time" mihomoHttp "github.com/metacubex/mihomo/component/http" + "github.com/metacubex/mihomo/component/profile/cachefile" types "github.com/metacubex/mihomo/constant/provider" ) @@ -28,8 +29,13 @@ func (f *FileVehicle) Url() string { return "file://" + f.path } -func (f *FileVehicle) Read(ctx context.Context) ([]byte, error) { - return os.ReadFile(f.path) +func (f *FileVehicle) Read(ctx context.Context, oldHash types.HashType) (buf []byte, hash types.HashType, err error) { + buf, err = os.ReadFile(f.path) + if err != nil { + return + } + hash = types.MakeHash(buf) + return } func (f *FileVehicle) Proxy() string { @@ -63,24 +69,49 @@ func (h *HTTPVehicle) Proxy() string { return h.proxy } -func (h *HTTPVehicle) Read(ctx context.Context) ([]byte, error) { +func (h *HTTPVehicle) Read(ctx context.Context, oldHash types.HashType) (buf []byte, hash types.HashType, err error) { ctx, cancel := context.WithTimeout(ctx, time.Second*20) defer cancel() - resp, err := mihomoHttp.HttpRequestWithProxy(ctx, h.url, http.MethodGet, h.header, nil, h.proxy) + header := h.header + setIfNoneMatch := false + if oldHash.IsValid() { + hashBytes, etag := cachefile.Cache().GetETagWithHash(h.url) + if oldHash.EqualBytes(hashBytes) && etag != "" { + if header == nil { + header = http.Header{} + } else { + header = header.Clone() + } + header.Set("If-None-Match", etag) + setIfNoneMatch = true + } + } + resp, err := mihomoHttp.HttpRequestWithProxy(ctx, h.url, http.MethodGet, header, nil, h.proxy) if err != nil { - return nil, err + return } defer resp.Body.Close() if resp.StatusCode < 200 || resp.StatusCode > 299 { - return nil, errors.New(resp.Status) + if setIfNoneMatch && resp.StatusCode == http.StatusNotModified { + return nil, oldHash, nil + } + err = errors.New(resp.Status) + return } - buf, err := io.ReadAll(resp.Body) + buf, err = io.ReadAll(resp.Body) if err != nil { - return nil, err + return } - return buf, nil + hash = types.MakeHash(buf) + cachefile.Cache().SetETagWithHash(h.url, hash.Bytes(), resp.Header.Get("ETag")) + return } func NewHTTPVehicle(url string, path string, proxy string, header http.Header) *HTTPVehicle { - return &HTTPVehicle{url, path, proxy, header} + return &HTTPVehicle{ + url: url, + path: path, + proxy: proxy, + header: header, + } } diff --git a/constant/provider/hash.go b/constant/provider/hash.go new file mode 100644 index 00000000..b95ffe23 --- /dev/null +++ b/constant/provider/hash.go @@ -0,0 +1,29 @@ +package provider + +import ( + "bytes" + "crypto/md5" +) + +type HashType [md5.Size]byte // MD5 + +func MakeHash(data []byte) HashType { + return md5.Sum(data) +} + +func (h HashType) Equal(hash HashType) bool { + return h == hash +} + +func (h HashType) EqualBytes(hashBytes []byte) bool { + return bytes.Equal(hashBytes, h[:]) +} + +func (h HashType) Bytes() []byte { + return h[:] +} + +func (h HashType) IsValid() bool { + var zero HashType + return h != zero +} diff --git a/constant/provider/interface.go b/constant/provider/interface.go index 9d24f691..2c83a1b8 100644 --- a/constant/provider/interface.go +++ b/constant/provider/interface.go @@ -32,7 +32,7 @@ func (v VehicleType) String() string { } type Vehicle interface { - Read(ctx context.Context) ([]byte, error) + Read(ctx context.Context, oldHash HashType) (buf []byte, hash HashType, err error) Path() string Url() string Proxy() string diff --git a/rules/provider/provider.go b/rules/provider/provider.go index ad720d47..0cbf83ba 100644 --- a/rules/provider/provider.go +++ b/rules/provider/provider.go @@ -66,22 +66,12 @@ func (rp *ruleSetProvider) Type() P.ProviderType { } func (rp *ruleSetProvider) Initial() error { - elm, err := rp.Fetcher.Initial() - if err != nil { - return err - } - - rp.OnUpdate(elm) - return nil + _, err := rp.Fetcher.Initial() + return err } func (rp *ruleSetProvider) Update() error { - elm, same, err := rp.Fetcher.Update() - if err == nil && !same { - rp.OnUpdate(elm) - return nil - } - + _, _, err := rp.Fetcher.Update() return err } From b7cb6774bf4d07bfb0bd23d8070447573f3386ef Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 22 Sep 2024 13:57:57 +0800 Subject: [PATCH 229/311] chore: support ETag for update geo --- adapter/provider/parser.go | 2 +- component/resource/fetcher.go | 20 +-------- component/resource/vehicle.go | 50 +++++++++++++++++----- component/updater/update_geo.go | 75 +++++++++++++++++++++++++++------ component/updater/update_ui.go | 4 +- component/updater/utils.go | 4 +- config/config.go | 4 +- constant/provider/interface.go | 1 + hub/executor/executor.go | 4 +- hub/route/upgrade.go | 2 +- rules/provider/parse.go | 2 +- 11 files changed, 117 insertions(+), 51 deletions(-) diff --git a/adapter/provider/parser.go b/adapter/provider/parser.go index 078e8695..b0db5db3 100644 --- a/adapter/provider/parser.go +++ b/adapter/provider/parser.go @@ -111,7 +111,7 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide return nil, fmt.Errorf("%w: %s", errSubPath, path) } } - vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, schema.Header) + vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, schema.Header, resource.DefaultHttpTimeout) default: return nil, fmt.Errorf("%w: %s", errVehicleType, schema.Type) } diff --git a/component/resource/fetcher.go b/component/resource/fetcher.go index 0b15e6c3..3e2ec239 100644 --- a/component/resource/fetcher.go +++ b/component/resource/fetcher.go @@ -3,7 +3,6 @@ package resource import ( "context" "os" - "path/filepath" "time" types "github.com/metacubex/mihomo/constant/provider" @@ -13,11 +12,6 @@ import ( "github.com/samber/lo" ) -var ( - fileMode os.FileMode = 0o666 - dirMode os.FileMode = 0o755 -) - type Parser[V any] func([]byte) (V, error) type Fetcher[V any] struct { @@ -118,7 +112,7 @@ func (f *Fetcher[V]) loadBuf(buf []byte, hash types.HashType, updateFile bool) ( } if updateFile { - if err = safeWrite(f.vehicle.Path(), buf); err != nil { + if err = f.vehicle.Write(buf); err != nil { return lo.Empty[V](), false, err } } @@ -205,18 +199,6 @@ func (f *Fetcher[V]) updateWithLog() { return } -func safeWrite(path string, buf []byte) error { - dir := filepath.Dir(path) - - if _, err := os.Stat(dir); os.IsNotExist(err) { - if err := os.MkdirAll(dir, dirMode); err != nil { - return err - } - } - - return os.WriteFile(path, buf, fileMode) -} - func NewFetcher[V any](name string, interval time.Duration, vehicle types.Vehicle, parser Parser[V], onUpdate func(V)) *Fetcher[V] { ctx, cancel := context.WithCancel(context.Background()) return &Fetcher[V]{ diff --git a/component/resource/vehicle.go b/component/resource/vehicle.go index ccc59aec..200b950e 100644 --- a/component/resource/vehicle.go +++ b/component/resource/vehicle.go @@ -6,6 +6,7 @@ import ( "io" "net/http" "os" + "path/filepath" "time" mihomoHttp "github.com/metacubex/mihomo/component/http" @@ -13,6 +14,25 @@ import ( types "github.com/metacubex/mihomo/constant/provider" ) +const ( + DefaultHttpTimeout = time.Second * 20 + + fileMode os.FileMode = 0o666 + dirMode os.FileMode = 0o755 +) + +func safeWrite(path string, buf []byte) error { + dir := filepath.Dir(path) + + if _, err := os.Stat(dir); os.IsNotExist(err) { + if err := os.MkdirAll(dir, dirMode); err != nil { + return err + } + } + + return os.WriteFile(path, buf, fileMode) +} + type FileVehicle struct { path string } @@ -42,15 +62,20 @@ func (f *FileVehicle) Proxy() string { return "" } +func (f *FileVehicle) Write(buf []byte) error { + return safeWrite(f.path, buf) +} + func NewFileVehicle(path string) *FileVehicle { return &FileVehicle{path: path} } type HTTPVehicle struct { - url string - path string - proxy string - header http.Header + url string + path string + proxy string + header http.Header + timeout time.Duration } func (h *HTTPVehicle) Url() string { @@ -69,8 +94,12 @@ func (h *HTTPVehicle) Proxy() string { return h.proxy } +func (h *HTTPVehicle) Write(buf []byte) error { + return safeWrite(h.path, buf) +} + func (h *HTTPVehicle) Read(ctx context.Context, oldHash types.HashType) (buf []byte, hash types.HashType, err error) { - ctx, cancel := context.WithTimeout(ctx, time.Second*20) + ctx, cancel := context.WithTimeout(ctx, h.timeout) defer cancel() header := h.header setIfNoneMatch := false @@ -107,11 +136,12 @@ func (h *HTTPVehicle) Read(ctx context.Context, oldHash types.HashType) (buf []b return } -func NewHTTPVehicle(url string, path string, proxy string, header http.Header) *HTTPVehicle { +func NewHTTPVehicle(url string, path string, proxy string, header http.Header, timeout time.Duration) *HTTPVehicle { return &HTTPVehicle{ - url: url, - path: path, - proxy: proxy, - header: header, + url: url, + path: path, + proxy: proxy, + header: header, + timeout: timeout, } } diff --git a/component/updater/update_geo.go b/component/updater/update_geo.go index 6246b1c6..454cd84d 100644 --- a/component/updater/update_geo.go +++ b/component/updater/update_geo.go @@ -13,7 +13,9 @@ import ( "github.com/metacubex/mihomo/component/geodata" _ "github.com/metacubex/mihomo/component/geodata/standard" "github.com/metacubex/mihomo/component/mmdb" + "github.com/metacubex/mihomo/component/resource" C "github.com/metacubex/mihomo/constant" + P "github.com/metacubex/mihomo/constant/provider" "github.com/metacubex/mihomo/log" "github.com/oschwald/maxminddb-golang" @@ -43,30 +45,52 @@ func SetGeoUpdateInterval(newGeoUpdateInterval int) { } func UpdateMMDB() (err error) { - defer mmdb.ReloadIP() - data, err := downloadForBytes(geodata.MmdbUrl()) + vehicle := resource.NewHTTPVehicle(geodata.MmdbUrl(), C.Path.MMDB(), "", nil, defaultHttpTimeout) + var oldHash P.HashType + if buf, err := os.ReadFile(vehicle.Path()); err == nil { + oldHash = P.MakeHash(buf) + } + data, hash, err := vehicle.Read(context.Background(), oldHash) if err != nil { return fmt.Errorf("can't download MMDB database file: %w", err) } + if oldHash.Equal(hash) { // same hash, ignored + return nil + } + if len(data) == 0 { + return fmt.Errorf("can't download MMDB database file: no data") + } + instance, err := maxminddb.FromBytes(data) if err != nil { return fmt.Errorf("invalid MMDB database file: %s", err) } _ = instance.Close() + defer mmdb.ReloadIP() mmdb.IPInstance().Reader.Close() // mmdb is loaded with mmap, so it needs to be closed before overwriting the file - if err = saveFile(data, C.Path.MMDB()); err != nil { + if err = vehicle.Write(data); err != nil { return fmt.Errorf("can't save MMDB database file: %w", err) } return nil } func UpdateASN() (err error) { - defer mmdb.ReloadASN() - data, err := downloadForBytes(geodata.ASNUrl()) + vehicle := resource.NewHTTPVehicle(geodata.ASNUrl(), C.Path.ASN(), "", nil, defaultHttpTimeout) + var oldHash P.HashType + if buf, err := os.ReadFile(vehicle.Path()); err == nil { + oldHash = P.MakeHash(buf) + } + data, hash, err := vehicle.Read(context.Background(), oldHash) if err != nil { return fmt.Errorf("can't download ASN database file: %w", err) } + if oldHash.Equal(hash) { // same hash, ignored + return nil + } + if len(data) == 0 { + return fmt.Errorf("can't download ASN database file: no data") + } instance, err := maxminddb.FromBytes(data) if err != nil { @@ -74,42 +98,69 @@ func UpdateASN() (err error) { } _ = instance.Close() + defer mmdb.ReloadASN() mmdb.ASNInstance().Reader.Close() // mmdb is loaded with mmap, so it needs to be closed before overwriting the file - if err = saveFile(data, C.Path.ASN()); err != nil { + if err = vehicle.Write(data); err != nil { return fmt.Errorf("can't save ASN database file: %w", err) } return nil } func UpdateGeoIp() (err error) { - defer geodata.ClearGeoIPCache() geoLoader, err := geodata.GetGeoDataLoader("standard") - data, err := downloadForBytes(geodata.GeoIpUrl()) + + vehicle := resource.NewHTTPVehicle(geodata.GeoIpUrl(), C.Path.GeoIP(), "", nil, defaultHttpTimeout) + var oldHash P.HashType + if buf, err := os.ReadFile(vehicle.Path()); err == nil { + oldHash = P.MakeHash(buf) + } + data, hash, err := vehicle.Read(context.Background(), oldHash) if err != nil { return fmt.Errorf("can't download GeoIP database file: %w", err) } + if oldHash.Equal(hash) { // same hash, ignored + return nil + } + if len(data) == 0 { + return fmt.Errorf("can't download GeoIP database file: no data") + } + if _, err = geoLoader.LoadIPByBytes(data, "cn"); err != nil { return fmt.Errorf("invalid GeoIP database file: %s", err) } - if err = saveFile(data, C.Path.GeoIP()); err != nil { + + defer geodata.ClearGeoIPCache() + if err = vehicle.Write(data); err != nil { return fmt.Errorf("can't save GeoIP database file: %w", err) } return nil } func UpdateGeoSite() (err error) { - defer geodata.ClearGeoSiteCache() geoLoader, err := geodata.GetGeoDataLoader("standard") - data, err := downloadForBytes(geodata.GeoSiteUrl()) + + vehicle := resource.NewHTTPVehicle(geodata.GeoSiteUrl(), C.Path.GeoSite(), "", nil, defaultHttpTimeout) + var oldHash P.HashType + if buf, err := os.ReadFile(vehicle.Path()); err == nil { + oldHash = P.MakeHash(buf) + } + data, hash, err := vehicle.Read(context.Background(), oldHash) if err != nil { return fmt.Errorf("can't download GeoSite database file: %w", err) } + if oldHash.Equal(hash) { // same hash, ignored + return nil + } + if len(data) == 0 { + return fmt.Errorf("can't download GeoSite database file: no data") + } if _, err = geoLoader.LoadSiteByBytes(data, "cn"); err != nil { return fmt.Errorf("invalid GeoSite database file: %s", err) } - if err = saveFile(data, C.Path.GeoSite()); err != nil { + defer geodata.ClearGeoSiteCache() + if err = vehicle.Write(data); err != nil { return fmt.Errorf("can't save GeoSite database file: %w", err) } return nil diff --git a/component/updater/update_ui.go b/component/updater/update_ui.go index b29bee9f..29081761 100644 --- a/component/updater/update_ui.go +++ b/component/updater/update_ui.go @@ -17,12 +17,12 @@ import ( var ( ExternalUIURL string ExternalUIPath string - AutoUpdateUI bool + AutoDownloadUI bool ) var xdMutex sync.Mutex -func UpdateUI() error { +func DownloadUI() error { xdMutex.Lock() defer xdMutex.Unlock() diff --git a/component/updater/utils.go b/component/updater/utils.go index d9da48c3..b5c694ff 100644 --- a/component/updater/utils.go +++ b/component/updater/utils.go @@ -13,8 +13,10 @@ import ( "golang.org/x/exp/constraints" ) +const defaultHttpTimeout = time.Second * 90 + func downloadForBytes(url string) ([]byte, error) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) + ctx, cancel := context.WithTimeout(context.Background(), defaultHttpTimeout) defer cancel() resp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, nil, nil) if err != nil { diff --git a/config/config.go b/config/config.go index 5a95de79..8fde168c 100644 --- a/config/config.go +++ b/config/config.go @@ -701,7 +701,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) { // checkout externalUI exist if cfg.ExternalUI != "" { - updater.AutoUpdateUI = true + updater.AutoDownloadUI = true updater.ExternalUIPath = C.Path.Resolve(cfg.ExternalUI) } else { // default externalUI path @@ -710,7 +710,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) { // checkout UIpath/name exist if cfg.ExternalUIName != "" { - updater.AutoUpdateUI = true + updater.AutoDownloadUI = true updater.ExternalUIPath = path.Join(updater.ExternalUIPath, cfg.ExternalUIName) } diff --git a/constant/provider/interface.go b/constant/provider/interface.go index 2c83a1b8..511e8f18 100644 --- a/constant/provider/interface.go +++ b/constant/provider/interface.go @@ -33,6 +33,7 @@ func (v VehicleType) String() string { type Vehicle interface { Read(ctx context.Context, oldHash HashType) (buf []byte, hash HashType, err error) + Write(buf []byte) error Path() string Url() string Proxy() string diff --git a/hub/executor/executor.go b/hub/executor/executor.go index fddcc42e..07b3b090 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -381,13 +381,13 @@ func updateTunnels(tunnels []LC.Tunnel) { } func initExternalUI() { - if updater.AutoUpdateUI { + if updater.AutoDownloadUI { dirEntries, _ := os.ReadDir(updater.ExternalUIPath) if len(dirEntries) > 0 { log.Infoln("UI already exists, skip downloading") } else { log.Infoln("External UI downloading ...") - updater.UpdateUI() + updater.DownloadUI() } } } diff --git a/hub/route/upgrade.go b/hub/route/upgrade.go index 76ac26a1..25c326dd 100644 --- a/hub/route/upgrade.go +++ b/hub/route/upgrade.go @@ -47,7 +47,7 @@ func upgradeCore(w http.ResponseWriter, r *http.Request) { } func updateUI(w http.ResponseWriter, r *http.Request) { - err := updater.UpdateUI() + err := updater.DownloadUI() if err != nil { log.Warnln("%s", err) render.Status(r, http.StatusInternalServerError) diff --git a/rules/provider/parse.go b/rules/provider/parse.go index b3c6af3c..3bd8f54c 100644 --- a/rules/provider/parse.go +++ b/rules/provider/parse.go @@ -53,7 +53,7 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t return nil, fmt.Errorf("%w: %s", errSubPath, path) } } - vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, nil) + vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, nil, resource.DefaultHttpTimeout) default: return nil, fmt.Errorf("unsupported vehicle type: %s", schema.Type) } From ddfa9e8671dc21999668fb0c3532b3702a10e120 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 22 Sep 2024 14:41:45 +0800 Subject: [PATCH 230/311] feat: add `etag-support` to let user can disable this feature manually --- component/resource/vehicle.go | 18 ++++++++++++++++-- config/config.go | 6 ++++++ hub/executor/executor.go | 2 ++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/component/resource/vehicle.go b/component/resource/vehicle.go index 200b950e..f30e22d0 100644 --- a/component/resource/vehicle.go +++ b/component/resource/vehicle.go @@ -21,6 +21,18 @@ const ( dirMode os.FileMode = 0o755 ) +var ( + etag = false +) + +func ETag() bool { + return etag +} + +func SetETag(b bool) { + etag = b +} + func safeWrite(path string, buf []byte) error { dir := filepath.Dir(path) @@ -103,7 +115,7 @@ func (h *HTTPVehicle) Read(ctx context.Context, oldHash types.HashType) (buf []b defer cancel() header := h.header setIfNoneMatch := false - if oldHash.IsValid() { + if etag && oldHash.IsValid() { hashBytes, etag := cachefile.Cache().GetETagWithHash(h.url) if oldHash.EqualBytes(hashBytes) && etag != "" { if header == nil { @@ -132,7 +144,9 @@ func (h *HTTPVehicle) Read(ctx context.Context, oldHash types.HashType) (buf []b return } hash = types.MakeHash(buf) - cachefile.Cache().SetETagWithHash(h.url, hash.Bytes(), resp.Header.Get("ETag")) + if etag { + cachefile.Cache().SetETagWithHash(h.url, hash.Bytes(), resp.Header.Get("ETag")) + } return } diff --git a/config/config.go b/config/config.go index 8fde168c..75616edc 100644 --- a/config/config.go +++ b/config/config.go @@ -24,6 +24,7 @@ import ( mihomoHttp "github.com/metacubex/mihomo/component/http" P "github.com/metacubex/mihomo/component/process" "github.com/metacubex/mihomo/component/resolver" + "github.com/metacubex/mihomo/component/resource" "github.com/metacubex/mihomo/component/sniffer" tlsC "github.com/metacubex/mihomo/component/tls" "github.com/metacubex/mihomo/component/trie" @@ -65,6 +66,7 @@ type General struct { Sniffing bool `json:"sniffing"` GlobalClientFingerprint string `json:"global-client-fingerprint"` GlobalUA string `json:"global-ua"` + ETagSupport bool `json:"etag-support"` } // Inbound config @@ -381,6 +383,7 @@ type RawConfig struct { FindProcessMode P.FindProcessMode `yaml:"find-process-mode" json:"find-process-mode"` GlobalClientFingerprint string `yaml:"global-client-fingerprint" json:"global-client-fingerprint"` GlobalUA string `yaml:"global-ua" json:"global-ua"` + ETagSupport bool `yaml:"etag-support" json:"etag-support"` KeepAliveIdle int `yaml:"keep-alive-idle" json:"keep-alive-idle"` KeepAliveInterval int `yaml:"keep-alive-interval" json:"keep-alive-interval"` DisableKeepAlive bool `yaml:"disable-keep-alive" json:"disable-keep-alive"` @@ -444,6 +447,7 @@ func DefaultRawConfig() *RawConfig { TCPConcurrent: false, FindProcessMode: P.FindProcessStrict, GlobalUA: "clash.meta/" + C.Version, + ETagSupport: true, DNS: RawDNS{ Enable: false, IPv6: false, @@ -690,6 +694,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) { geodata.SetMmdbUrl(cfg.GeoXUrl.Mmdb) geodata.SetASNUrl(cfg.GeoXUrl.ASN) mihomoHttp.SetUA(cfg.GlobalUA) + resource.SetETag(cfg.ETagSupport) if cfg.KeepAliveIdle != 0 { N.KeepAliveIdle = time.Duration(cfg.KeepAliveIdle) * time.Second @@ -755,6 +760,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) { FindProcessMode: cfg.FindProcessMode, GlobalClientFingerprint: cfg.GlobalClientFingerprint, GlobalUA: cfg.GlobalUA, + ETagSupport: cfg.ETagSupport, }, nil } diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 07b3b090..66bbc89b 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -22,6 +22,7 @@ import ( "github.com/metacubex/mihomo/component/profile" "github.com/metacubex/mihomo/component/profile/cachefile" "github.com/metacubex/mihomo/component/resolver" + "github.com/metacubex/mihomo/component/resource" "github.com/metacubex/mihomo/component/sniffer" tlsC "github.com/metacubex/mihomo/component/tls" "github.com/metacubex/mihomo/component/trie" @@ -172,6 +173,7 @@ func GetGeneral() *config.General { Sniffing: tunnel.IsSniffing(), GlobalClientFingerprint: tlsC.GetGlobalFingerprint(), GlobalUA: mihomoHttp.UA(), + ETagSupport: resource.ETag(), } return general From 781b783346f58bbba3370ed4708a806e4b71c24f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 22 Sep 2024 22:07:14 +0800 Subject: [PATCH 231/311] feat: add `amnezia-wg-option` to wireguard outbound --- adapter/outbound/wireguard.go | 44 +++++++++++++++++++++++++++++++---- docs/config.yaml | 11 +++++++++ go.mod | 5 ++-- go.sum | 10 ++++---- 4 files changed, 60 insertions(+), 10 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 0382debb..43046384 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -24,18 +24,24 @@ import ( "github.com/metacubex/mihomo/dns" "github.com/metacubex/mihomo/log" + amnezia "github.com/metacubex/amneziawg-go/device" wireguard "github.com/metacubex/sing-wireguard" + "github.com/metacubex/wireguard-go/device" "github.com/sagernet/sing/common/debug" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" - "github.com/sagernet/wireguard-go/device" ) +type wireguardGoDevice interface { + Close() + IpcSet(uapiConf string) error +} + type WireGuard struct { *Base bind *wireguard.ClientBind - device *device.Device + device wireguardGoDevice tunDevice wireguard.Device dialer proxydialer.SingDialer resolver *dns.Resolver @@ -67,6 +73,8 @@ type WireGuardOption struct { UDP bool `proxy:"udp,omitempty"` PersistentKeepalive int `proxy:"persistent-keepalive,omitempty"` + AmneziaWGOption *AmneziaWGOption `proxy:"amnezia-wg-option,omitempty"` + Peers []WireGuardPeerOption `proxy:"peers,omitempty"` RemoteDnsResolve bool `proxy:"remote-dns-resolve,omitempty"` @@ -84,6 +92,18 @@ type WireGuardPeerOption struct { AllowedIPs []string `proxy:"allowed-ips,omitempty"` } +type AmneziaWGOption struct { + JC int `proxy:"jc"` + JMin int `proxy:"jmin"` + JMax int `proxy:"jmax"` + S1 int `proxy:"s1"` + S2 int `proxy:"s2"` + H1 uint32 `proxy:"h1"` + H2 uint32 `proxy:"h2"` + H3 uint32 `proxy:"h3"` + H4 uint32 `proxy:"h4"` +} + type wgSingErrorHandler struct { name string } @@ -243,14 +263,19 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { if err != nil { return nil, E.Cause(err, "create WireGuard device") } - outbound.device = device.NewDevice(context.Background(), outbound.tunDevice, outbound.bind, &device.Logger{ + logger := &device.Logger{ Verbosef: func(format string, args ...interface{}) { log.SingLogger.Debug(fmt.Sprintf("[WG](%s) %s", option.Name, fmt.Sprintf(format, args...))) }, Errorf: func(format string, args ...interface{}) { log.SingLogger.Error(fmt.Sprintf("[WG](%s) %s", option.Name, fmt.Sprintf(format, args...))) }, - }, option.Workers) + } + if option.AmneziaWGOption != nil { + outbound.device = amnezia.NewDevice(outbound.tunDevice, outbound.bind, logger, option.Workers) + } else { + outbound.device = device.NewDevice(outbound.tunDevice, outbound.bind, logger, option.Workers) + } var has6 bool for _, address := range outbound.localPrefixes { @@ -367,6 +392,17 @@ func (w *WireGuard) genIpcConf(ctx context.Context, updateOnly bool) (string, er ipcConf := "" if !updateOnly { ipcConf += "private_key=" + w.option.PrivateKey + "\n" + if w.option.AmneziaWGOption != nil { + ipcConf += "jc=" + strconv.Itoa(w.option.AmneziaWGOption.JC) + "\n" + ipcConf += "jmin=" + strconv.Itoa(w.option.AmneziaWGOption.JMin) + "\n" + ipcConf += "jmax=" + strconv.Itoa(w.option.AmneziaWGOption.JMax) + "\n" + ipcConf += "s1=" + strconv.Itoa(w.option.AmneziaWGOption.S1) + "\n" + ipcConf += "s2=" + strconv.Itoa(w.option.AmneziaWGOption.S2) + "\n" + ipcConf += "h1=" + strconv.FormatUint(uint64(w.option.AmneziaWGOption.H1), 10) + "\n" + ipcConf += "h2=" + strconv.FormatUint(uint64(w.option.AmneziaWGOption.H2), 10) + "\n" + ipcConf += "h3=" + strconv.FormatUint(uint64(w.option.AmneziaWGOption.H3), 10) + "\n" + ipcConf += "h4=" + strconv.FormatUint(uint64(w.option.AmneziaWGOption.H4), 10) + "\n" + } } if len(w.option.Peers) > 0 { for i, peer := range w.option.Peers { diff --git a/docs/config.yaml b/docs/config.yaml index f7b573eb..b3515a20 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -760,6 +760,17 @@ proxies: # socks5 # # pre-shared-key: 31aIhAPwktDGpH4JDhA8GNvjFXEf/a6+UaQRyOAiyfM= # allowed-ips: ['0.0.0.0/0'] # reserved: [209,98,59] + # 如果存在则开启AmneziaWG功能 + # amnezia-wg-option: + # jc: 5 + # jmin: 500 + # jmax: 501 + # s1: 30 + # s2: 40 + # h1: 123456 + # h2: 67543 + # h4: 32345 + # h3: 123123 # tuic - name: tuic diff --git a/go.mod b/go.mod index 7121901b..e3eeb456 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/klauspost/cpuid/v2 v2.2.8 github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.7.2 + github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 github.com/metacubex/chacha v0.1.0 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 @@ -27,9 +28,10 @@ require ( github.com/metacubex/sing-shadowsocks2 v0.2.2 github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 - github.com/metacubex/sing-wireguard v0.0.0-20240826061955-1e4e67afe5cd + github.com/metacubex/sing-wireguard v0.0.0-20240922131718-0f10c39a5531 github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785 github.com/metacubex/utls v1.6.6 + github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 github.com/miekg/dns v1.1.62 github.com/mroth/weightedrand/v2 v2.1.0 github.com/openacid/low v0.1.21 @@ -40,7 +42,6 @@ require ( github.com/sagernet/sing v0.5.0-alpha.13 github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 github.com/sagernet/sing-shadowtls v0.1.4 - github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e github.com/samber/lo v1.47.0 github.com/shirou/gopsutil/v3 v3.24.5 github.com/sirupsen/logrus v1.9.3 diff --git a/go.sum b/go.sum index 3b1d2b50..e1f5490d 100644 --- a/go.sum +++ b/go.sum @@ -96,6 +96,8 @@ github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/ github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= +github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab h1:Chbw+/31UC14YFNr78pESt5Vowlc62zziw05JCUqoL4= +github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab/go.mod h1:xVKK8jC5Sd3hfh7WjmCq+HorehIbrBijaUWmcuKjPcI= github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 h1:oBowHVKZycNtAFbZ6avaCSZJYeme2Nrj+4RpV2cNJig= github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399/go.mod h1:4xcieuIK+M4bGQmQYZVqEaIYqjS1ahO4kXG7EmDgEro= github.com/metacubex/chacha v0.1.0 h1:tg9RSJ18NvL38cCWNyYH1eiG6qDCyyXIaTLQthon0sc= @@ -120,12 +122,14 @@ github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 h1:ypfofGDZbP github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1/go.mod h1:olbEx9yVcaw5tHTNlRamRoxmMKcvDvcVS1YLnQGzvWE= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosqY8xKDp3pqTW3qbrCprZ1l6WkrXSFSCwyY4I= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= -github.com/metacubex/sing-wireguard v0.0.0-20240826061955-1e4e67afe5cd h1:r7alry8u4qlUFLNMwGvG1A8ZcfPM6AMSmrm6E2yKdB4= -github.com/metacubex/sing-wireguard v0.0.0-20240826061955-1e4e67afe5cd/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= +github.com/metacubex/sing-wireguard v0.0.0-20240922131718-0f10c39a5531 h1:BoIL2fZZTPzvSxuhng9kWwvUZ8fiMJyrWbgdHIX0CDs= +github.com/metacubex/sing-wireguard v0.0.0-20240922131718-0f10c39a5531/go.mod h1:6nitcmzPDL3MXnLdhu6Hm126Zk4S1fBbX3P7jxUxSFw= github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785 h1:NNmI+ZV0DzNuqaAInRQuZFLHlWVuyHeow8jYpdKjHjo= github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8= github.com/metacubex/utls v1.6.6/go.mod h1:+WLFUnXjcpdxXCnyX25nggw8C6YonZ8zOK2Zm/oRvdo= +github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 h1:hJLQviGySBuaynlCwf/oYgIxbVbGRUIKZCxdya9YrbQ= +github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181/go.mod h1:phewKljNYiTVT31Gcif8RiCKnTUOgVWFJjccqYM8s+Y= github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU= @@ -168,8 +172,6 @@ github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnV github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= -github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e h1:iGH0RMv2FzELOFNFQtvsxH7NPmlo7X5JizEK51UCojo= -github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e/go.mod h1:YbL4TKHRR6APYQv3U2RGfwLDpPYSyWz6oUlpISBEzBE= github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc= github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= From 33823f1728d97217bf0cf401bd8ae029986dec0c Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 22 Sep 2024 22:45:55 +0800 Subject: [PATCH 232/311] chore: sync internal interface --- listener/sing_tun/iface.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/listener/sing_tun/iface.go b/listener/sing_tun/iface.go index cc142078..543d206c 100644 --- a/listener/sing_tun/iface.go +++ b/listener/sing_tun/iface.go @@ -13,6 +13,11 @@ type defaultInterfaceFinder struct{} var DefaultInterfaceFinder control.InterfaceFinder = (*defaultInterfaceFinder)(nil) +func (f *defaultInterfaceFinder) Update() error { + iface.FlushCache() + return nil +} + func (f *defaultInterfaceFinder) Interfaces() []control.Interface { ifaces, err := iface.Interfaces() if err != nil { From 150c6ccd2514459cb160c4722aeb760fbbc23a61 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 23 Sep 2024 08:54:07 +0800 Subject: [PATCH 233/311] chore: skip duplicates nameserver when parse --- config/config.go | 39 +++++++++++++++++++-------------------- dns/util.go | 4 ---- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/config/config.go b/config/config.go index 75616edc..2a75ddd6 100644 --- a/config/config.go +++ b/config/config.go @@ -1162,16 +1162,6 @@ func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns. var nameservers []dns.NameServer for idx, server := range servers { - if strings.HasPrefix(server, "dhcp://") { - nameservers = append( - nameservers, - dns.NameServer{ - Net: "dhcp", - Addr: server[len("dhcp://"):], - }, - ) - continue - } server = parsePureDNSServer(server) u, err := url.Parse(server) if err != nil { @@ -1222,6 +1212,13 @@ func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns. dnsNetType = "quic" // DNS over QUIC case "system": dnsNetType = "system" // System DNS + case "dhcp": + addr = server[len("dhcp://"):] // some special notation cannot be parsed by url + dnsNetType = "dhcp" // UDP from DHCP + if addr == "system" { // Compatible with old writing "dhcp://system" + dnsNetType = "system" + addr = "" + } case "rcode": dnsNetType = "rcode" addr = u.Host @@ -1247,16 +1244,18 @@ func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns. proxyName = dns.RespectRules } - nameservers = append( - nameservers, - dns.NameServer{ - Net: dnsNetType, - Addr: addr, - ProxyName: proxyName, - Params: params, - PreferH3: preferH3, - }, - ) + nameserver := dns.NameServer{ + Net: dnsNetType, + Addr: addr, + ProxyName: proxyName, + Params: params, + PreferH3: preferH3, + } + if slices.ContainsFunc(nameservers, nameserver.Equal) { + continue // skip duplicates nameserver + } + + nameservers = append(nameservers, nameserver) } return nameservers, nil } diff --git a/dns/util.go b/dns/util.go index 92a86dbc..50459cc1 100644 --- a/dns/util.go +++ b/dns/util.go @@ -99,10 +99,6 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient { ret = append(ret, newDoHClient(s.Addr, resolver, s.PreferH3, s.Params, s.ProxyAdapter, s.ProxyName)) continue case "dhcp": - if s.Addr == "system" { // Compatible with old writing - ret = append(ret, newSystemClient()) - continue - } ret = append(ret, newDHCPClient(s.Addr)) continue case "system": From 966eeae41b557e906a0b55e9740223a788706e1a Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 23 Sep 2024 09:35:48 +0800 Subject: [PATCH 234/311] chore: rewrite bbolt cachefile implements never use returned byte slices outside the transaction, ref: https://pkg.go.dev/go.etcd.io/bbolt#hdr-Caveats --- common/utils/hash.go | 45 ++++++++++ component/fakeip/cachefile.go | 30 +++---- component/fakeip/pool.go | 4 +- component/fakeip/pool_test.go | 4 +- component/profile/cachefile/cache.go | 104 ++++------------------- component/profile/cachefile/fakeip.go | 115 ++++++++++++++++++++++++++ component/resource/fetcher.go | 9 +- component/resource/vehicle.go | 13 +-- component/updater/update_geo.go | 18 ++-- constant/path.go | 7 +- constant/provider/hash.go | 29 ------- constant/provider/interface.go | 2 +- 12 files changed, 212 insertions(+), 168 deletions(-) create mode 100644 common/utils/hash.go create mode 100644 component/profile/cachefile/fakeip.go delete mode 100644 constant/provider/hash.go diff --git a/common/utils/hash.go b/common/utils/hash.go new file mode 100644 index 00000000..38ba15b4 --- /dev/null +++ b/common/utils/hash.go @@ -0,0 +1,45 @@ +package utils + +import ( + "crypto/md5" + "encoding/hex" +) + +// HashType warps hash array inside struct +// someday can change to other hash algorithm simply +type HashType struct { + md5 [md5.Size]byte // MD5 +} + +func MakeHash(data []byte) HashType { + return HashType{md5.Sum(data)} +} + +func MakeHashFromBytes(hashBytes []byte) (h HashType) { + if len(hashBytes) != md5.Size { + return + } + copy(h.md5[:], hashBytes) + return +} + +func (h HashType) Equal(hash HashType) bool { + return h.md5 == hash.md5 +} + +func (h HashType) Bytes() []byte { + return h.md5[:] +} + +func (h HashType) String() string { + return hex.EncodeToString(h.Bytes()) +} + +func (h HashType) Len() int { + return len(h.md5) +} + +func (h HashType) IsValid() bool { + var zero HashType + return h != zero +} diff --git a/component/fakeip/cachefile.go b/component/fakeip/cachefile.go index 6f0cc48b..92d09721 100644 --- a/component/fakeip/cachefile.go +++ b/component/fakeip/cachefile.go @@ -7,46 +7,32 @@ import ( ) type cachefileStore struct { - cache *cachefile.CacheFile + cache *cachefile.FakeIpStore } // GetByHost implements store.GetByHost func (c *cachefileStore) GetByHost(host string) (netip.Addr, bool) { - elm := c.cache.GetFakeip([]byte(host)) - if elm == nil { - return netip.Addr{}, false - } - - if len(elm) == 4 { - return netip.AddrFrom4(*(*[4]byte)(elm)), true - } else { - return netip.AddrFrom16(*(*[16]byte)(elm)), true - } + return c.cache.GetByHost(host) } // PutByHost implements store.PutByHost func (c *cachefileStore) PutByHost(host string, ip netip.Addr) { - c.cache.PutFakeip([]byte(host), ip.AsSlice()) + c.cache.PutByHost(host, ip) } // GetByIP implements store.GetByIP func (c *cachefileStore) GetByIP(ip netip.Addr) (string, bool) { - elm := c.cache.GetFakeip(ip.AsSlice()) - if elm == nil { - return "", false - } - return string(elm), true + return c.cache.GetByIP(ip) } // PutByIP implements store.PutByIP func (c *cachefileStore) PutByIP(ip netip.Addr, host string) { - c.cache.PutFakeip(ip.AsSlice(), []byte(host)) + c.cache.PutByIP(ip, host) } // DelByIP implements store.DelByIP func (c *cachefileStore) DelByIP(ip netip.Addr) { - addr := ip.AsSlice() - c.cache.DelFakeipPair(addr, c.cache.GetFakeip(addr)) + c.cache.DelByIP(ip) } // Exist implements store.Exist @@ -63,3 +49,7 @@ func (c *cachefileStore) CloneTo(store store) {} func (c *cachefileStore) FlushFakeIP() error { return c.cache.FlushFakeIP() } + +func newCachefileStore(cache *cachefile.CacheFile) *cachefileStore { + return &cachefileStore{cache.FakeIpStore()} +} diff --git a/component/fakeip/pool.go b/component/fakeip/pool.go index 12c06332..41b848b3 100644 --- a/component/fakeip/pool.go +++ b/component/fakeip/pool.go @@ -201,9 +201,7 @@ func New(options Options) (*Pool, error) { ipnet: options.IPNet, } if options.Persistence { - pool.store = &cachefileStore{ - cache: cachefile.Cache(), - } + pool.store = newCachefileStore(cachefile.Cache()) } else { pool.store = newMemoryStore(options.Size) } diff --git a/component/fakeip/pool_test.go b/component/fakeip/pool_test.go index ee607b68..be78b87c 100644 --- a/component/fakeip/pool_test.go +++ b/component/fakeip/pool_test.go @@ -43,9 +43,7 @@ func createCachefileStore(options Options) (*Pool, string, error) { return nil, "", err } - pool.store = &cachefileStore{ - cache: &cachefile.CacheFile{DB: db}, - } + pool.store = newCachefileStore(&cachefile.CacheFile{DB: db}) return pool, f.Name(), nil } diff --git a/component/profile/cachefile/cache.go b/component/profile/cachefile/cache.go index 0591c92b..6a918041 100644 --- a/component/profile/cachefile/cache.go +++ b/component/profile/cachefile/cache.go @@ -6,6 +6,7 @@ import ( "sync" "time" + "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/profile" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" @@ -71,93 +72,19 @@ func (c *CacheFile) SelectedMap() map[string]string { return mapping } -func (c *CacheFile) PutFakeip(key, value []byte) error { - if c.DB == nil { - return nil - } - - err := c.DB.Batch(func(t *bbolt.Tx) error { - bucket, err := t.CreateBucketIfNotExists(bucketFakeip) - if err != nil { - return err - } - return bucket.Put(key, value) - }) - if err != nil { - log.Warnln("[CacheFile] write cache to %s failed: %s", c.DB.Path(), err.Error()) - } - - return err -} - -func (c *CacheFile) DelFakeipPair(ip, host []byte) error { - if c.DB == nil { - return nil - } - - err := c.DB.Batch(func(t *bbolt.Tx) error { - bucket, err := t.CreateBucketIfNotExists(bucketFakeip) - if err != nil { - return err - } - err = bucket.Delete(ip) - if len(host) > 0 { - if err := bucket.Delete(host); err != nil { - return err - } - } - return err - }) - if err != nil { - log.Warnln("[CacheFile] write cache to %s failed: %s", c.DB.Path(), err.Error()) - } - - return err -} - -func (c *CacheFile) GetFakeip(key []byte) []byte { - if c.DB == nil { - return nil - } - - tx, err := c.DB.Begin(false) - if err != nil { - return nil - } - defer tx.Rollback() - - bucket := tx.Bucket(bucketFakeip) - if bucket == nil { - return nil - } - - return bucket.Get(key) -} - -func (c *CacheFile) FlushFakeIP() error { - err := c.DB.Batch(func(t *bbolt.Tx) error { - bucket := t.Bucket(bucketFakeip) - if bucket == nil { - return nil - } - return t.DeleteBucket(bucketFakeip) - }) - return err -} - -func (c *CacheFile) SetETagWithHash(url string, hash []byte, etag string) { +func (c *CacheFile) SetETagWithHash(url string, hash utils.HashType, etag string) { if c.DB == nil { return } - lenHash := len(hash) + lenHash := hash.Len() if lenHash > math.MaxUint8 { return // maybe panic is better } data := make([]byte, 1, 1+lenHash+len(etag)) data[0] = uint8(lenHash) - data = append(data, hash...) + data = append(data, hash.Bytes()...) data = append(data, etag...) err := c.DB.Batch(func(t *bbolt.Tx) error { @@ -173,28 +100,27 @@ func (c *CacheFile) SetETagWithHash(url string, hash []byte, etag string) { return } } -func (c *CacheFile) GetETagWithHash(key string) (hash []byte, etag string) { +func (c *CacheFile) GetETagWithHash(key string) (hash utils.HashType, etag string) { if c.DB == nil { return } - var value []byte c.DB.View(func(t *bbolt.Tx) error { if bucket := t.Bucket(bucketETag); bucket != nil { if v := bucket.Get([]byte(key)); v != nil { - value = v + if len(v) == 0 { + return nil + } + lenHash := int(v[0]) + if len(v) < 1+lenHash { + return nil + } + hash = utils.MakeHashFromBytes(v[1 : 1+lenHash]) + etag = string(v[1+lenHash:]) } } return nil }) - if len(value) == 0 { - return - } - lenHash := int(value[0]) - if len(value) < 1+lenHash { - return - } - hash = value[1 : 1+lenHash] - etag = string(value[1+lenHash:]) + return } diff --git a/component/profile/cachefile/fakeip.go b/component/profile/cachefile/fakeip.go new file mode 100644 index 00000000..20a09f9c --- /dev/null +++ b/component/profile/cachefile/fakeip.go @@ -0,0 +1,115 @@ +package cachefile + +import ( + "net/netip" + + "github.com/metacubex/mihomo/log" + + "github.com/metacubex/bbolt" +) + +type FakeIpStore struct { + *CacheFile +} + +func (c *CacheFile) FakeIpStore() *FakeIpStore { + return &FakeIpStore{c} +} + +func (c *FakeIpStore) GetByHost(host string) (ip netip.Addr, exist bool) { + if c.DB == nil { + return + } + c.DB.View(func(t *bbolt.Tx) error { + if bucket := t.Bucket(bucketFakeip); bucket != nil { + if v := bucket.Get([]byte(host)); v != nil { + ip, exist = netip.AddrFromSlice(v) + } + } + return nil + }) + return +} + +func (c *FakeIpStore) PutByHost(host string, ip netip.Addr) { + if c.DB == nil { + return + } + err := c.DB.Batch(func(t *bbolt.Tx) error { + bucket, err := t.CreateBucketIfNotExists(bucketFakeip) + if err != nil { + return err + } + return bucket.Put([]byte(host), ip.AsSlice()) + }) + if err != nil { + log.Warnln("[CacheFile] write cache to %s failed: %s", c.DB.Path(), err.Error()) + } +} + +func (c *FakeIpStore) GetByIP(ip netip.Addr) (host string, exist bool) { + if c.DB == nil { + return + } + c.DB.View(func(t *bbolt.Tx) error { + if bucket := t.Bucket(bucketFakeip); bucket != nil { + if v := bucket.Get(ip.AsSlice()); v != nil { + host, exist = string(v), true + } + } + return nil + }) + return +} + +func (c *FakeIpStore) PutByIP(ip netip.Addr, host string) { + if c.DB == nil { + return + } + err := c.DB.Batch(func(t *bbolt.Tx) error { + bucket, err := t.CreateBucketIfNotExists(bucketFakeip) + if err != nil { + return err + } + return bucket.Put(ip.AsSlice(), []byte(host)) + }) + if err != nil { + log.Warnln("[CacheFile] write cache to %s failed: %s", c.DB.Path(), err.Error()) + } +} + +func (c *FakeIpStore) DelByIP(ip netip.Addr) { + if c.DB == nil { + return + } + + addr := ip.AsSlice() + err := c.DB.Batch(func(t *bbolt.Tx) error { + bucket, err := t.CreateBucketIfNotExists(bucketFakeip) + if err != nil { + return err + } + host := bucket.Get(addr) + err = bucket.Delete(addr) + if len(host) > 0 { + if err = bucket.Delete(host); err != nil { + return err + } + } + return err + }) + if err != nil { + log.Warnln("[CacheFile] write cache to %s failed: %s", c.DB.Path(), err.Error()) + } +} + +func (c *FakeIpStore) FlushFakeIP() error { + err := c.DB.Batch(func(t *bbolt.Tx) error { + bucket := t.Bucket(bucketFakeip) + if bucket == nil { + return nil + } + return t.DeleteBucket(bucketFakeip) + }) + return err +} diff --git a/component/resource/fetcher.go b/component/resource/fetcher.go index 3e2ec239..39beee85 100644 --- a/component/resource/fetcher.go +++ b/component/resource/fetcher.go @@ -5,6 +5,7 @@ import ( "os" "time" + "github.com/metacubex/mihomo/common/utils" types "github.com/metacubex/mihomo/constant/provider" "github.com/metacubex/mihomo/log" @@ -21,7 +22,7 @@ type Fetcher[V any] struct { name string vehicle types.Vehicle updatedAt time.Time - hash types.HashType + hash utils.HashType parser Parser[V] interval time.Duration onUpdate func(V) @@ -55,7 +56,7 @@ func (f *Fetcher[V]) Initial() (V, error) { // local file exists, use it first buf, err = os.ReadFile(f.vehicle.Path()) modTime := stat.ModTime() - contents, _, err = f.loadBuf(buf, types.MakeHash(buf), false) + contents, _, err = f.loadBuf(buf, utils.MakeHash(buf), false) f.updatedAt = modTime // reset updatedAt to file's modTime if err == nil { @@ -89,10 +90,10 @@ func (f *Fetcher[V]) Update() (V, bool, error) { } func (f *Fetcher[V]) SideUpdate(buf []byte) (V, bool, error) { - return f.loadBuf(buf, types.MakeHash(buf), true) + return f.loadBuf(buf, utils.MakeHash(buf), true) } -func (f *Fetcher[V]) loadBuf(buf []byte, hash types.HashType, updateFile bool) (V, bool, error) { +func (f *Fetcher[V]) loadBuf(buf []byte, hash utils.HashType, updateFile bool) (V, bool, error) { now := time.Now() if f.hash.Equal(hash) { if updateFile { diff --git a/component/resource/vehicle.go b/component/resource/vehicle.go index f30e22d0..b24adfa9 100644 --- a/component/resource/vehicle.go +++ b/component/resource/vehicle.go @@ -9,6 +9,7 @@ import ( "path/filepath" "time" + "github.com/metacubex/mihomo/common/utils" mihomoHttp "github.com/metacubex/mihomo/component/http" "github.com/metacubex/mihomo/component/profile/cachefile" types "github.com/metacubex/mihomo/constant/provider" @@ -61,12 +62,12 @@ func (f *FileVehicle) Url() string { return "file://" + f.path } -func (f *FileVehicle) Read(ctx context.Context, oldHash types.HashType) (buf []byte, hash types.HashType, err error) { +func (f *FileVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []byte, hash utils.HashType, err error) { buf, err = os.ReadFile(f.path) if err != nil { return } - hash = types.MakeHash(buf) + hash = utils.MakeHash(buf) return } @@ -110,14 +111,14 @@ func (h *HTTPVehicle) Write(buf []byte) error { return safeWrite(h.path, buf) } -func (h *HTTPVehicle) Read(ctx context.Context, oldHash types.HashType) (buf []byte, hash types.HashType, err error) { +func (h *HTTPVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []byte, hash utils.HashType, err error) { ctx, cancel := context.WithTimeout(ctx, h.timeout) defer cancel() header := h.header setIfNoneMatch := false if etag && oldHash.IsValid() { hashBytes, etag := cachefile.Cache().GetETagWithHash(h.url) - if oldHash.EqualBytes(hashBytes) && etag != "" { + if oldHash.Equal(hashBytes) && etag != "" { if header == nil { header = http.Header{} } else { @@ -143,9 +144,9 @@ func (h *HTTPVehicle) Read(ctx context.Context, oldHash types.HashType) (buf []b if err != nil { return } - hash = types.MakeHash(buf) + hash = utils.MakeHash(buf) if etag { - cachefile.Cache().SetETagWithHash(h.url, hash.Bytes(), resp.Header.Get("ETag")) + cachefile.Cache().SetETagWithHash(h.url, hash, resp.Header.Get("ETag")) } return } diff --git a/component/updater/update_geo.go b/component/updater/update_geo.go index 454cd84d..b5dc9677 100644 --- a/component/updater/update_geo.go +++ b/component/updater/update_geo.go @@ -10,12 +10,12 @@ import ( "github.com/metacubex/mihomo/common/atomic" "github.com/metacubex/mihomo/common/batch" + "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/geodata" _ "github.com/metacubex/mihomo/component/geodata/standard" "github.com/metacubex/mihomo/component/mmdb" "github.com/metacubex/mihomo/component/resource" C "github.com/metacubex/mihomo/constant" - P "github.com/metacubex/mihomo/constant/provider" "github.com/metacubex/mihomo/log" "github.com/oschwald/maxminddb-golang" @@ -46,9 +46,9 @@ func SetGeoUpdateInterval(newGeoUpdateInterval int) { func UpdateMMDB() (err error) { vehicle := resource.NewHTTPVehicle(geodata.MmdbUrl(), C.Path.MMDB(), "", nil, defaultHttpTimeout) - var oldHash P.HashType + var oldHash utils.HashType if buf, err := os.ReadFile(vehicle.Path()); err == nil { - oldHash = P.MakeHash(buf) + oldHash = utils.MakeHash(buf) } data, hash, err := vehicle.Read(context.Background(), oldHash) if err != nil { @@ -77,9 +77,9 @@ func UpdateMMDB() (err error) { func UpdateASN() (err error) { vehicle := resource.NewHTTPVehicle(geodata.ASNUrl(), C.Path.ASN(), "", nil, defaultHttpTimeout) - var oldHash P.HashType + var oldHash utils.HashType if buf, err := os.ReadFile(vehicle.Path()); err == nil { - oldHash = P.MakeHash(buf) + oldHash = utils.MakeHash(buf) } data, hash, err := vehicle.Read(context.Background(), oldHash) if err != nil { @@ -110,9 +110,9 @@ func UpdateGeoIp() (err error) { geoLoader, err := geodata.GetGeoDataLoader("standard") vehicle := resource.NewHTTPVehicle(geodata.GeoIpUrl(), C.Path.GeoIP(), "", nil, defaultHttpTimeout) - var oldHash P.HashType + var oldHash utils.HashType if buf, err := os.ReadFile(vehicle.Path()); err == nil { - oldHash = P.MakeHash(buf) + oldHash = utils.MakeHash(buf) } data, hash, err := vehicle.Read(context.Background(), oldHash) if err != nil { @@ -140,9 +140,9 @@ func UpdateGeoSite() (err error) { geoLoader, err := geodata.GetGeoDataLoader("standard") vehicle := resource.NewHTTPVehicle(geodata.GeoSiteUrl(), C.Path.GeoSite(), "", nil, defaultHttpTimeout) - var oldHash P.HashType + var oldHash utils.HashType if buf, err := os.ReadFile(vehicle.Path()); err == nil { - oldHash = P.MakeHash(buf) + oldHash = utils.MakeHash(buf) } data, hash, err := vehicle.Read(context.Background(), oldHash) if err != nil { diff --git a/constant/path.go b/constant/path.go index 02279371..1594441c 100644 --- a/constant/path.go +++ b/constant/path.go @@ -1,14 +1,13 @@ package constant import ( - "crypto/md5" - "encoding/hex" "os" P "path" "path/filepath" "strconv" "strings" + "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/constant/features" ) @@ -89,8 +88,8 @@ func (p *path) IsSafePath(path string) bool { } func (p *path) GetPathByHash(prefix, name string) string { - hash := md5.Sum([]byte(name)) - filename := hex.EncodeToString(hash[:]) + hash := utils.MakeHash([]byte(name)) + filename := hash.String() return filepath.Join(p.HomeDir(), prefix, filename) } diff --git a/constant/provider/hash.go b/constant/provider/hash.go deleted file mode 100644 index b95ffe23..00000000 --- a/constant/provider/hash.go +++ /dev/null @@ -1,29 +0,0 @@ -package provider - -import ( - "bytes" - "crypto/md5" -) - -type HashType [md5.Size]byte // MD5 - -func MakeHash(data []byte) HashType { - return md5.Sum(data) -} - -func (h HashType) Equal(hash HashType) bool { - return h == hash -} - -func (h HashType) EqualBytes(hashBytes []byte) bool { - return bytes.Equal(hashBytes, h[:]) -} - -func (h HashType) Bytes() []byte { - return h[:] -} - -func (h HashType) IsValid() bool { - var zero HashType - return h != zero -} diff --git a/constant/provider/interface.go b/constant/provider/interface.go index 511e8f18..065b801a 100644 --- a/constant/provider/interface.go +++ b/constant/provider/interface.go @@ -32,7 +32,7 @@ func (v VehicleType) String() string { } type Vehicle interface { - Read(ctx context.Context, oldHash HashType) (buf []byte, hash HashType, err error) + Read(ctx context.Context, oldHash utils.HashType) (buf []byte, hash utils.HashType, err error) Write(buf []byte) error Path() string Url() string From 59a2b24593fce55244cf3ad3817855b0b1b7f6b3 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 23 Sep 2024 19:25:35 +0800 Subject: [PATCH 235/311] chore: save etag in bbolt by msgpack --- common/utils/hash.go | 33 ++++++++++++---- component/profile/cachefile/cache.go | 54 -------------------------- component/profile/cachefile/etag.go | 58 ++++++++++++++++++++++++++++ component/resource/vehicle.go | 12 ++++-- go.mod | 2 + go.sum | 4 ++ 6 files changed, 97 insertions(+), 66 deletions(-) create mode 100644 component/profile/cachefile/etag.go diff --git a/common/utils/hash.go b/common/utils/hash.go index 38ba15b4..6957e2c3 100644 --- a/common/utils/hash.go +++ b/common/utils/hash.go @@ -3,6 +3,7 @@ package utils import ( "crypto/md5" "encoding/hex" + "errors" ) // HashType warps hash array inside struct @@ -15,14 +16,6 @@ func MakeHash(data []byte) HashType { return HashType{md5.Sum(data)} } -func MakeHashFromBytes(hashBytes []byte) (h HashType) { - if len(hashBytes) != md5.Size { - return - } - copy(h.md5[:], hashBytes) - return -} - func (h HashType) Equal(hash HashType) bool { return h.md5 == hash.md5 } @@ -35,6 +28,30 @@ func (h HashType) String() string { return hex.EncodeToString(h.Bytes()) } +func (h HashType) MarshalText() ([]byte, error) { + return []byte(h.String()), nil +} + +func (h *HashType) UnmarshalText(data []byte) error { + if hex.DecodedLen(len(data)) != md5.Size { + return errors.New("invalid hash length") + } + _, err := hex.Decode(h.md5[:], data) + return err +} + +func (h HashType) MarshalBinary() ([]byte, error) { + return h.md5[:], nil +} + +func (h *HashType) UnmarshalBinary(data []byte) error { + if len(data) != md5.Size { + return errors.New("invalid hash length") + } + copy(h.md5[:], data) + return nil +} + func (h HashType) Len() int { return len(h.md5) } diff --git a/component/profile/cachefile/cache.go b/component/profile/cachefile/cache.go index 6a918041..f5101f5b 100644 --- a/component/profile/cachefile/cache.go +++ b/component/profile/cachefile/cache.go @@ -1,12 +1,10 @@ package cachefile import ( - "math" "os" "sync" "time" - "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/profile" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" @@ -72,58 +70,6 @@ func (c *CacheFile) SelectedMap() map[string]string { return mapping } -func (c *CacheFile) SetETagWithHash(url string, hash utils.HashType, etag string) { - if c.DB == nil { - return - } - - lenHash := hash.Len() - if lenHash > math.MaxUint8 { - return // maybe panic is better - } - - data := make([]byte, 1, 1+lenHash+len(etag)) - data[0] = uint8(lenHash) - data = append(data, hash.Bytes()...) - data = append(data, etag...) - - err := c.DB.Batch(func(t *bbolt.Tx) error { - bucket, err := t.CreateBucketIfNotExists(bucketETag) - if err != nil { - return err - } - - return bucket.Put([]byte(url), data) - }) - if err != nil { - log.Warnln("[CacheFile] write cache to %s failed: %s", c.DB.Path(), err.Error()) - return - } -} -func (c *CacheFile) GetETagWithHash(key string) (hash utils.HashType, etag string) { - if c.DB == nil { - return - } - c.DB.View(func(t *bbolt.Tx) error { - if bucket := t.Bucket(bucketETag); bucket != nil { - if v := bucket.Get([]byte(key)); v != nil { - if len(v) == 0 { - return nil - } - lenHash := int(v[0]) - if len(v) < 1+lenHash { - return nil - } - hash = utils.MakeHashFromBytes(v[1 : 1+lenHash]) - etag = string(v[1+lenHash:]) - } - } - return nil - }) - - return -} - func (c *CacheFile) Close() error { return c.DB.Close() } diff --git a/component/profile/cachefile/etag.go b/component/profile/cachefile/etag.go new file mode 100644 index 00000000..028fe504 --- /dev/null +++ b/component/profile/cachefile/etag.go @@ -0,0 +1,58 @@ +package cachefile + +import ( + "time" + + "github.com/metacubex/mihomo/common/utils" + "github.com/metacubex/mihomo/log" + + "github.com/metacubex/bbolt" + "github.com/vmihailenco/msgpack/v5" +) + +type EtagWithHash struct { + Hash utils.HashType + ETag string + Time time.Time +} + +func (c *CacheFile) SetETagWithHash(url string, etagWithHash EtagWithHash) { + if c.DB == nil { + return + } + + data, err := msgpack.Marshal(etagWithHash) + if err != nil { + return // maybe panic is better + } + + err = c.DB.Batch(func(t *bbolt.Tx) error { + bucket, err := t.CreateBucketIfNotExists(bucketETag) + if err != nil { + return err + } + + return bucket.Put([]byte(url), data) + }) + if err != nil { + log.Warnln("[CacheFile] write cache to %s failed: %s", c.DB.Path(), err.Error()) + return + } +} +func (c *CacheFile) GetETagWithHash(key string) (etagWithHash EtagWithHash) { + if c.DB == nil { + return + } + c.DB.View(func(t *bbolt.Tx) error { + if bucket := t.Bucket(bucketETag); bucket != nil { + if v := bucket.Get([]byte(key)); v != nil { + if err := msgpack.Unmarshal(v, &etagWithHash); err != nil { + return err + } + } + } + return nil + }) + + return +} diff --git a/component/resource/vehicle.go b/component/resource/vehicle.go index b24adfa9..a9382329 100644 --- a/component/resource/vehicle.go +++ b/component/resource/vehicle.go @@ -117,14 +117,14 @@ func (h *HTTPVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []b header := h.header setIfNoneMatch := false if etag && oldHash.IsValid() { - hashBytes, etag := cachefile.Cache().GetETagWithHash(h.url) - if oldHash.Equal(hashBytes) && etag != "" { + etagWithHash := cachefile.Cache().GetETagWithHash(h.url) + if oldHash.Equal(etagWithHash.Hash) && etagWithHash.ETag != "" { if header == nil { header = http.Header{} } else { header = header.Clone() } - header.Set("If-None-Match", etag) + header.Set("If-None-Match", etagWithHash.ETag) setIfNoneMatch = true } } @@ -146,7 +146,11 @@ func (h *HTTPVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []b } hash = utils.MakeHash(buf) if etag { - cachefile.Cache().SetETagWithHash(h.url, hash, resp.Header.Get("ETag")) + cachefile.Cache().SetETagWithHash(h.url, cachefile.EtagWithHash{ + Hash: hash, + ETag: resp.Header.Get("ETag"), + Time: time.Now(), + }) } return } diff --git a/go.mod b/go.mod index e3eeb456..c1a8fab3 100644 --- a/go.mod +++ b/go.mod @@ -46,6 +46,7 @@ require ( github.com/shirou/gopsutil/v3 v3.24.5 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.9.0 + github.com/vmihailenco/msgpack/v5 v5.4.1 github.com/wk8/go-ordered-map/v2 v2.1.8 gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 go.uber.org/automaxprocs v1.5.3 @@ -104,6 +105,7 @@ require ( 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.4 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect go.uber.org/mock v0.4.0 // indirect diff --git a/go.sum b/go.sum index e1f5490d..001e76af 100644 --- a/go.sum +++ b/go.sum @@ -210,6 +210,10 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17 github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= From 6c0383026eeeab77853d048fc1434b3529e468a8 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 24 Sep 2024 13:18:58 +0800 Subject: [PATCH 236/311] fix: AmneziaWG not working --- adapter/outbound/wireguard.go | 1 + go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 43046384..6f5a18f3 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -272,6 +272,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { }, } if option.AmneziaWGOption != nil { + outbound.bind.SetParseReserved(false) // AmneziaWG don't need parse reserved outbound.device = amnezia.NewDevice(outbound.tunDevice, outbound.bind, logger, option.Workers) } else { outbound.device = device.NewDevice(outbound.tunDevice, outbound.bind, logger, option.Workers) diff --git a/go.mod b/go.mod index c1a8fab3..22af95b3 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/metacubex/sing-shadowsocks2 v0.2.2 github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 - github.com/metacubex/sing-wireguard v0.0.0-20240922131718-0f10c39a5531 + github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785 github.com/metacubex/utls v1.6.6 github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 diff --git a/go.sum b/go.sum index 001e76af..9b824fc5 100644 --- a/go.sum +++ b/go.sum @@ -122,8 +122,8 @@ github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 h1:ypfofGDZbP github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1/go.mod h1:olbEx9yVcaw5tHTNlRamRoxmMKcvDvcVS1YLnQGzvWE= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosqY8xKDp3pqTW3qbrCprZ1l6WkrXSFSCwyY4I= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= -github.com/metacubex/sing-wireguard v0.0.0-20240922131718-0f10c39a5531 h1:BoIL2fZZTPzvSxuhng9kWwvUZ8fiMJyrWbgdHIX0CDs= -github.com/metacubex/sing-wireguard v0.0.0-20240922131718-0f10c39a5531/go.mod h1:6nitcmzPDL3MXnLdhu6Hm126Zk4S1fBbX3P7jxUxSFw= +github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 h1:xg71VmzLS6ByAzi/57phwDvjE+dLLs+ozH00k4DnOns= +github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3/go.mod h1:6nitcmzPDL3MXnLdhu6Hm126Zk4S1fBbX3P7jxUxSFw= github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785 h1:NNmI+ZV0DzNuqaAInRQuZFLHlWVuyHeow8jYpdKjHjo= github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8= From a4e84f047918fc692aa9e83439964367c228feae Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 24 Sep 2024 21:42:28 +0800 Subject: [PATCH 237/311] chore: better apply tcp keepalive to listeners --- adapter/inbound/listen.go | 22 +++++++ adapter/outbound/direct.go | 2 - adapter/outbound/http.go | 3 - adapter/outbound/shadowsocks.go | 1 - adapter/outbound/shadowsocksr.go | 1 - adapter/outbound/snell.go | 6 +- adapter/outbound/socks5.go | 3 - adapter/outbound/ssh.go | 1 - adapter/outbound/trojan.go | 4 -- adapter/outbound/vless.go | 3 - adapter/outbound/vmess.go | 3 - common/net/tcp_keepalive.go | 23 ------- common/net/tcp_keepalive_go122.go | 10 --- common/net/tcp_keepalive_go123.go | 19 ------ component/dialer/dialer.go | 2 + component/keepalive/tcp_keepalive.go | 65 +++++++++++++++++++ component/keepalive/tcp_keepalive_go122.go | 30 +++++++++ component/keepalive/tcp_keepalive_go123.go | 45 +++++++++++++ .../keepalive}/tcp_keepalive_go123_unix.go | 2 +- .../keepalive}/tcp_keepalive_go123_windows.go | 2 +- config/config.go | 8 +-- listener/http/server.go | 4 +- listener/mixed/mixed.go | 3 +- listener/redir/tcp.go | 6 +- listener/shadowsocks/tcp.go | 1 - listener/sing_shadowsocks/server.go | 2 - listener/sing_vmess/server.go | 1 - listener/socks/tcp.go | 2 +- listener/tproxy/tproxy.go | 4 +- listener/tunnel/tcp.go | 2 - 30 files changed, 180 insertions(+), 100 deletions(-) delete mode 100644 common/net/tcp_keepalive.go delete mode 100644 common/net/tcp_keepalive_go122.go delete mode 100644 common/net/tcp_keepalive_go123.go create mode 100644 component/keepalive/tcp_keepalive.go create mode 100644 component/keepalive/tcp_keepalive_go122.go create mode 100644 component/keepalive/tcp_keepalive_go123.go rename {common/net => component/keepalive}/tcp_keepalive_go123_unix.go (91%) rename {common/net => component/keepalive}/tcp_keepalive_go123_windows.go (99%) diff --git a/adapter/inbound/listen.go b/adapter/inbound/listen.go index 1b86c811..318c9675 100644 --- a/adapter/inbound/listen.go +++ b/adapter/inbound/listen.go @@ -3,6 +3,9 @@ package inbound import ( "context" "net" + "sync" + + "github.com/metacubex/mihomo/component/keepalive" "github.com/metacubex/tfo-go" ) @@ -11,28 +14,47 @@ var ( lc = tfo.ListenConfig{ DisableTFO: true, } + mutex sync.RWMutex ) func SetTfo(open bool) { + mutex.Lock() + defer mutex.Unlock() lc.DisableTFO = !open } func Tfo() bool { + mutex.RLock() + defer mutex.RUnlock() return !lc.DisableTFO } func SetMPTCP(open bool) { + mutex.Lock() + defer mutex.Unlock() setMultiPathTCP(&lc.ListenConfig, open) } func MPTCP() bool { + mutex.RLock() + defer mutex.RUnlock() return getMultiPathTCP(&lc.ListenConfig) } func ListenContext(ctx context.Context, network, address string) (net.Listener, error) { + mutex.RLock() + defer mutex.RUnlock() return lc.Listen(ctx, network, address) } func Listen(network, address string) (net.Listener, error) { return ListenContext(context.Background(), network, address) } + +func init() { + keepalive.SetDisableKeepAliveCallback.Register(func(b bool) { + mutex.Lock() + defer mutex.Unlock() + keepalive.SetNetListenConfig(&lc.ListenConfig) + }) +} diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index 7114045d..15f081f2 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -6,7 +6,6 @@ import ( "os" "strconv" - N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/loopback" "github.com/metacubex/mihomo/component/resolver" @@ -38,7 +37,6 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ... if err != nil { return nil, err } - N.TCPKeepAlive(c) return d.loopBack.NewConn(NewConn(c, d)), nil } diff --git a/adapter/outbound/http.go b/adapter/outbound/http.go index b837e49a..ebb1d67c 100644 --- a/adapter/outbound/http.go +++ b/adapter/outbound/http.go @@ -7,13 +7,11 @@ import ( "encoding/base64" "errors" "fmt" - "io" "net" "net/http" "strconv" - N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/proxydialer" @@ -76,7 +74,6 @@ 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) } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 88fb8456..021fbc0a 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -149,7 +149,6 @@ func (ss *ShadowSocks) DialContextWithDialer(ctx context.Context, dialer C.Diale if err != nil { return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index 07d78047..437695b4 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -80,7 +80,6 @@ func (ssr *ShadowSocksR) DialContextWithDialer(ctx context.Context, dialer C.Dia if err != nil { return nil, fmt.Errorf("%s connect error: %w", ssr.addr, err) } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) diff --git a/adapter/outbound/snell.go b/adapter/outbound/snell.go index 76ed4be9..f6a4b4f9 100644 --- a/adapter/outbound/snell.go +++ b/adapter/outbound/snell.go @@ -6,7 +6,6 @@ import ( "net" "strconv" - N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/common/structure" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/proxydialer" @@ -94,7 +93,6 @@ 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) } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) @@ -122,7 +120,6 @@ func (s *Snell) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met if err != nil { return nil, err } - N.TCPKeepAlive(c) c = streamConn(c, streamOption{s.psk, s.version, s.addr, s.obfsOption}) err = snell.WriteUDPHeader(c, s.version) @@ -207,8 +204,7 @@ func NewSnell(option SnellOption) (*Snell, error) { if err != nil { return nil, err } - - 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 c17ee6a7..1908167a 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -10,7 +10,6 @@ import ( "net/netip" "strconv" - N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/proxydialer" @@ -82,7 +81,6 @@ 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) } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) @@ -128,7 +126,6 @@ func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, safeConnClose(c, err) }(c) - N.TCPKeepAlive(c) var user *socks5.User if ss.user != "" { user = &socks5.User{ diff --git a/adapter/outbound/ssh.go b/adapter/outbound/ssh.go index 8b2776a6..9e23b463 100644 --- a/adapter/outbound/ssh.go +++ b/adapter/outbound/ssh.go @@ -77,7 +77,6 @@ func (s *sshClient) connect(ctx context.Context, cDialer C.Dialer, addr string) if err != nil { return nil, err } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index 938a8858..b3a611af 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -9,7 +9,6 @@ import ( "net/http" "strconv" - N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/proxydialer" @@ -154,7 +153,6 @@ 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) } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) @@ -212,7 +210,6 @@ func (t *Trojan) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, me defer func(c net.Conn) { safeConnClose(c, err) }(c) - N.TCPKeepAlive(c) c, err = t.plainStream(ctx, c) if err != nil { return nil, fmt.Errorf("%s connect error: %w", t.addr, err) @@ -314,7 +311,6 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { if err != nil { return nil, fmt.Errorf("%s connect error: %s", t.addr, err.Error()) } - N.TCPKeepAlive(c) return c, nil } diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index b18bf4da..79058874 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -262,7 +262,6 @@ 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()) } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) }(c) @@ -327,7 +326,6 @@ 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()) } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) }(c) @@ -574,7 +572,6 @@ func NewVless(option VlessOption) (*Vless, error) { if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } - N.TCPKeepAlive(c) return c, nil } diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 7d5a7224..8797374d 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -312,7 +312,6 @@ 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()) } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) }(c) @@ -373,7 +372,6 @@ 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()) } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) }(c) @@ -473,7 +471,6 @@ func NewVmess(option VmessOption) (*Vmess, error) { if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } - N.TCPKeepAlive(c) return c, nil } diff --git a/common/net/tcp_keepalive.go b/common/net/tcp_keepalive.go deleted file mode 100644 index 047a1c05..00000000 --- a/common/net/tcp_keepalive.go +++ /dev/null @@ -1,23 +0,0 @@ -package net - -import ( - "net" - "runtime" - "time" -) - -var ( - KeepAliveIdle = 0 * time.Second - KeepAliveInterval = 0 * time.Second - DisableKeepAlive = false -) - -func TCPKeepAlive(c net.Conn) { - if tcp, ok := c.(*net.TCPConn); ok { - if runtime.GOOS == "android" || DisableKeepAlive { - _ = tcp.SetKeepAlive(false) - } else { - tcpKeepAlive(tcp) - } - } -} diff --git a/common/net/tcp_keepalive_go122.go b/common/net/tcp_keepalive_go122.go deleted file mode 100644 index 12873168..00000000 --- a/common/net/tcp_keepalive_go122.go +++ /dev/null @@ -1,10 +0,0 @@ -//go:build !go1.23 - -package net - -import "net" - -func tcpKeepAlive(tcp *net.TCPConn) { - _ = tcp.SetKeepAlive(true) - _ = tcp.SetKeepAlivePeriod(KeepAliveInterval) -} diff --git a/common/net/tcp_keepalive_go123.go b/common/net/tcp_keepalive_go123.go deleted file mode 100644 index 2dd4754b..00000000 --- a/common/net/tcp_keepalive_go123.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build go1.23 - -package net - -import "net" - -func tcpKeepAlive(tcp *net.TCPConn) { - config := net.KeepAliveConfig{ - Enable: true, - Idle: KeepAliveIdle, - Interval: KeepAliveInterval, - } - if !SupportTCPKeepAliveCount() { - // it's recommended to set both Idle and Interval to non-negative values in conjunction with a -1 - // for Count on those old Windows if you intend to customize the TCP keep-alive settings. - config.Count = -1 - } - _ = tcp.SetKeepAliveConfig(config) -} diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 41f79b8e..3dfd3159 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -12,6 +12,7 @@ import ( "sync" "time" + "github.com/metacubex/mihomo/component/keepalive" "github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/log" ) @@ -144,6 +145,7 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po } dialer := netDialer.(*net.Dialer) + keepalive.SetNetDialer(dialer) if opt.mpTcp { setMultiPathTCP(dialer) } diff --git a/component/keepalive/tcp_keepalive.go b/component/keepalive/tcp_keepalive.go new file mode 100644 index 00000000..9b24c45a --- /dev/null +++ b/component/keepalive/tcp_keepalive.go @@ -0,0 +1,65 @@ +package keepalive + +import ( + "net" + "runtime" + "time" + + "github.com/metacubex/mihomo/common/atomic" + "github.com/metacubex/mihomo/common/utils" +) + +var ( + keepAliveIdle = atomic.NewTypedValue[time.Duration](0 * time.Second) + keepAliveInterval = atomic.NewTypedValue[time.Duration](0 * time.Second) + disableKeepAlive = atomic.NewBool(false) + + SetDisableKeepAliveCallback = utils.NewCallback[bool]() +) + +func SetKeepAliveIdle(t time.Duration) { + keepAliveIdle.Store(t) +} + +func SetKeepAliveInterval(t time.Duration) { + keepAliveInterval.Store(t) +} + +func KeepAliveIdle() time.Duration { + return keepAliveIdle.Load() +} + +func KeepAliveInterval() time.Duration { + return keepAliveInterval.Load() +} + +func SetDisableKeepAlive(disable bool) { + if runtime.GOOS == "android" { + setDisableKeepAlive(false) + } else { + setDisableKeepAlive(disable) + } +} + +func setDisableKeepAlive(disable bool) { + disableKeepAlive.Store(disable) + SetDisableKeepAliveCallback.Emit(disable) +} + +func DisableKeepAlive() bool { + return disableKeepAlive.Load() +} + +func SetNetDialer(dialer *net.Dialer) { + setNetDialer(dialer) +} + +func SetNetListenConfig(lc *net.ListenConfig) { + setNetListenConfig(lc) +} + +func TCPKeepAlive(c net.Conn) { + if tcp, ok := c.(*net.TCPConn); ok && tcp != nil { + tcpKeepAlive(tcp) + } +} diff --git a/component/keepalive/tcp_keepalive_go122.go b/component/keepalive/tcp_keepalive_go122.go new file mode 100644 index 00000000..5d88827d --- /dev/null +++ b/component/keepalive/tcp_keepalive_go122.go @@ -0,0 +1,30 @@ +//go:build !go1.23 + +package keepalive + +import "net" + +func tcpKeepAlive(tcp *net.TCPConn) { + if DisableKeepAlive() { + _ = tcp.SetKeepAlive(false) + } else { + _ = tcp.SetKeepAlive(true) + _ = tcp.SetKeepAlivePeriod(KeepAliveInterval()) + } +} + +func setNetDialer(dialer *net.Dialer) { + if DisableKeepAlive() { + dialer.KeepAlive = -1 // If negative, keep-alive probes are disabled. + } else { + dialer.KeepAlive = KeepAliveInterval() + } +} + +func setNetListenConfig(lc *net.ListenConfig) { + if DisableKeepAlive() { + lc.KeepAlive = -1 // If negative, keep-alive probes are disabled. + } else { + lc.KeepAlive = KeepAliveInterval() + } +} diff --git a/component/keepalive/tcp_keepalive_go123.go b/component/keepalive/tcp_keepalive_go123.go new file mode 100644 index 00000000..4c08118b --- /dev/null +++ b/component/keepalive/tcp_keepalive_go123.go @@ -0,0 +1,45 @@ +//go:build go1.23 + +package keepalive + +import "net" + +func keepAliveConfig() net.KeepAliveConfig { + config := net.KeepAliveConfig{ + Enable: true, + Idle: KeepAliveIdle(), + Interval: KeepAliveInterval(), + } + if !SupportTCPKeepAliveCount() { + // it's recommended to set both Idle and Interval to non-negative values in conjunction with a -1 + // for Count on those old Windows if you intend to customize the TCP keep-alive settings. + config.Count = -1 + } + return config +} + +func tcpKeepAlive(tcp *net.TCPConn) { + if DisableKeepAlive() { + _ = tcp.SetKeepAlive(false) + } else { + _ = tcp.SetKeepAliveConfig(keepAliveConfig()) + } +} + +func setNetDialer(dialer *net.Dialer) { + if DisableKeepAlive() { + dialer.KeepAlive = -1 // If negative, keep-alive probes are disabled. + dialer.KeepAliveConfig.Enable = false + } else { + dialer.KeepAliveConfig = keepAliveConfig() + } +} + +func setNetListenConfig(lc *net.ListenConfig) { + if DisableKeepAlive() { + lc.KeepAlive = -1 // If negative, keep-alive probes are disabled. + lc.KeepAliveConfig.Enable = false + } else { + lc.KeepAliveConfig = keepAliveConfig() + } +} diff --git a/common/net/tcp_keepalive_go123_unix.go b/component/keepalive/tcp_keepalive_go123_unix.go similarity index 91% rename from common/net/tcp_keepalive_go123_unix.go rename to component/keepalive/tcp_keepalive_go123_unix.go index 0ead7ca4..8033cc6c 100644 --- a/common/net/tcp_keepalive_go123_unix.go +++ b/component/keepalive/tcp_keepalive_go123_unix.go @@ -1,6 +1,6 @@ //go:build go1.23 && unix -package net +package keepalive func SupportTCPKeepAliveIdle() bool { return true diff --git a/common/net/tcp_keepalive_go123_windows.go b/component/keepalive/tcp_keepalive_go123_windows.go similarity index 99% rename from common/net/tcp_keepalive_go123_windows.go rename to component/keepalive/tcp_keepalive_go123_windows.go index 8f1e61f9..2462e80c 100644 --- a/common/net/tcp_keepalive_go123_windows.go +++ b/component/keepalive/tcp_keepalive_go123_windows.go @@ -2,7 +2,7 @@ // copy and modify from golang1.23's internal/syscall/windows/version_windows.go -package net +package keepalive import ( "errors" diff --git a/config/config.go b/config/config.go index 2a75ddd6..27cde1fb 100644 --- a/config/config.go +++ b/config/config.go @@ -15,13 +15,13 @@ import ( "github.com/metacubex/mihomo/adapter/outbound" "github.com/metacubex/mihomo/adapter/outboundgroup" "github.com/metacubex/mihomo/adapter/provider" - N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/auth" "github.com/metacubex/mihomo/component/cidr" "github.com/metacubex/mihomo/component/fakeip" "github.com/metacubex/mihomo/component/geodata" mihomoHttp "github.com/metacubex/mihomo/component/http" + "github.com/metacubex/mihomo/component/keepalive" P "github.com/metacubex/mihomo/component/process" "github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/component/resource" @@ -697,12 +697,12 @@ func parseGeneral(cfg *RawConfig) (*General, error) { resource.SetETag(cfg.ETagSupport) if cfg.KeepAliveIdle != 0 { - N.KeepAliveIdle = time.Duration(cfg.KeepAliveIdle) * time.Second + keepalive.SetKeepAliveIdle(time.Duration(cfg.KeepAliveIdle) * time.Second) } if cfg.KeepAliveInterval != 0 { - N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second + keepalive.SetKeepAliveInterval(time.Duration(cfg.KeepAliveInterval) * time.Second) } - N.DisableKeepAlive = cfg.DisableKeepAlive + keepalive.SetDisableKeepAlive(cfg.DisableKeepAlive) // checkout externalUI exist if cfg.ExternalUI != "" { diff --git a/listener/http/server.go b/listener/http/server.go index 48f12dc5..04f32f4f 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -4,7 +4,6 @@ import ( "net" "github.com/metacubex/mihomo/adapter/inbound" - N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/auth" C "github.com/metacubex/mihomo/constant" authStore "github.com/metacubex/mihomo/listener/auth" @@ -55,8 +54,8 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Auth inbound.WithSpecialRules(""), } } - l, err := inbound.Listen("tcp", addr) + l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err } @@ -74,7 +73,6 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Auth } continue } - N.TCPKeepAlive(conn) getAuth := getAuth if isDefault { // only apply on default listener diff --git a/listener/mixed/mixed.go b/listener/mixed/mixed.go index 12390061..ac3a0c58 100644 --- a/listener/mixed/mixed.go +++ b/listener/mixed/mixed.go @@ -49,6 +49,7 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Auth inbound.WithSpecialRules(""), } } + l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err @@ -85,8 +86,6 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Auth } func handleConn(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) { - N.TCPKeepAlive(conn) - bufConn := N.NewBufferedConn(conn) head, err := bufConn.Peek(1) if err != nil { diff --git a/listener/redir/tcp.go b/listener/redir/tcp.go index 8474a8e2..47363182 100644 --- a/listener/redir/tcp.go +++ b/listener/redir/tcp.go @@ -4,7 +4,7 @@ import ( "net" "github.com/metacubex/mihomo/adapter/inbound" - N "github.com/metacubex/mihomo/common/net" + "github.com/metacubex/mihomo/component/keepalive" C "github.com/metacubex/mihomo/constant" ) @@ -37,10 +37,12 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener inbound.WithSpecialRules(""), } } + l, err := net.Listen("tcp", addr) if err != nil { return nil, err } + rl := &Listener{ listener: l, addr: addr, @@ -68,6 +70,6 @@ func handleRedir(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) conn.Close() return } - N.TCPKeepAlive(conn) + keepalive.TCPKeepAlive(conn) tunnel.HandleTCPConn(inbound.NewSocket(target, conn, C.REDIR, additions...)) } diff --git a/listener/shadowsocks/tcp.go b/listener/shadowsocks/tcp.go index c3843814..b150e4cb 100644 --- a/listener/shadowsocks/tcp.go +++ b/listener/shadowsocks/tcp.go @@ -59,7 +59,6 @@ func New(config LC.ShadowsocksServer, tunnel C.Tunnel, additions ...inbound.Addi } continue } - N.TCPKeepAlive(c) go sl.HandleConn(c, tunnel, additions...) } }() diff --git a/listener/sing_shadowsocks/server.go b/listener/sing_shadowsocks/server.go index 1cb798f7..5f2a4292 100644 --- a/listener/sing_shadowsocks/server.go +++ b/listener/sing_shadowsocks/server.go @@ -7,7 +7,6 @@ import ( "strings" "github.com/metacubex/mihomo/adapter/inbound" - N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/common/sockopt" C "github.com/metacubex/mihomo/constant" LC "github.com/metacubex/mihomo/listener/config" @@ -153,7 +152,6 @@ func New(config LC.ShadowsocksServer, tunnel C.Tunnel, additions ...inbound.Addi } continue } - N.TCPKeepAlive(c) go sl.HandleConn(c, tunnel) } diff --git a/listener/sing_vmess/server.go b/listener/sing_vmess/server.go index ce422b16..7a0afa0b 100644 --- a/listener/sing_vmess/server.go +++ b/listener/sing_vmess/server.go @@ -121,7 +121,6 @@ func New(config LC.VmessServer, tunnel C.Tunnel, additions ...inbound.Addition) } continue } - N.TCPKeepAlive(c) go sl.HandleConn(c, tunnel) } diff --git a/listener/socks/tcp.go b/listener/socks/tcp.go index 3e98a602..950384c1 100644 --- a/listener/socks/tcp.go +++ b/listener/socks/tcp.go @@ -48,6 +48,7 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Auth inbound.WithSpecialRules(""), } } + l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err @@ -84,7 +85,6 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Auth } func handleSocks(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) { - 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 fa7e7dbe..6056047a 100644 --- a/listener/tproxy/tproxy.go +++ b/listener/tproxy/tproxy.go @@ -4,7 +4,7 @@ import ( "net" "github.com/metacubex/mihomo/adapter/inbound" - N "github.com/metacubex/mihomo/common/net" + "github.com/metacubex/mihomo/component/keepalive" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/transport/socks5" ) @@ -33,7 +33,7 @@ func (l *Listener) Close() error { func (l *Listener) handleTProxy(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) { target := socks5.ParseAddrToSocksAddr(conn.LocalAddr()) - N.TCPKeepAlive(conn) + keepalive.TCPKeepAlive(conn) // TProxy's conn.LocalAddr() is target address, so we set from l.listener additions = append([]inbound.Addition{inbound.WithInAddr(l.listener.Addr())}, additions...) tunnel.HandleTCPConn(inbound.NewSocket(target, conn, C.TPROXY, additions...)) diff --git a/listener/tunnel/tcp.go b/listener/tunnel/tcp.go index 794dc8ac..7c916a38 100644 --- a/listener/tunnel/tcp.go +++ b/listener/tunnel/tcp.go @@ -5,7 +5,6 @@ import ( "net" "github.com/metacubex/mihomo/adapter/inbound" - N "github.com/metacubex/mihomo/common/net" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/transport/socks5" ) @@ -35,7 +34,6 @@ func (l *Listener) Close() error { } func (l *Listener) handleTCP(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) { - N.TCPKeepAlive(conn) tunnel.HandleTCPConn(inbound.NewSocket(l.target, conn, C.TUNNEL, additions...)) } From 3922b17067e3555bfffb83ade97affee082e55da Mon Sep 17 00:00:00 2001 From: HamsterReserved Date: Wed, 25 Sep 2024 21:28:30 +0800 Subject: [PATCH 238/311] chore: deliver UDP packets from same connection in receiving order (#1540) All UDP packets are queued into a single channel, and multiple workers are launched to poll the channel in current design. This introduces a problem where UDP packets from a single connection are delivered to different workers, thus forwarded in a random order if workers are on different CPU cores. Though UDP peers normally have their own logic to handle out-of-order packets, this behavior will inevitably cause significant variance in delay and harm connection quality. Furthermore, this out-of-order behavior is noticeable even if the underlying transport could provide guaranteed orderly delivery - this is unacceptable. This commit takes the idea of RSS in terms of NICs: it creates a distinct queue for each worker, hashes incoming packets, and distribute the packet to a worker by hash result. The tuple (SrcIP, SrcPort, DstIP, DstPort, Proto) is used for hashing (Proto is always UDP so it's dropped from final implementation), thus packets from the same connection can be sent to the same worker, keeping the receiving order. Different connections can be hashed to different workers to maintain performance. Performance for single UDP connection is not affected, as there is already a lock in natTable that prevents multiple packets being processed in different workers, limiting single connection forwarding performance to 1 worker. The only performance penalty is the hashing code, which should be neglectable given the footprint of en/decryption work. Co-authored-by: Hamster Tian --- tunnel/tunnel.go | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 60ba0323..5b55f07d 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "hash/maphash" "net" "net/netip" "path/filepath" @@ -31,7 +32,8 @@ import ( var ( status = newAtomicStatus(Suspend) tcpQueue = make(chan C.ConnContext, 200) - udpQueue = make(chan C.PacketAdapter, 200) + udpQueues []chan C.PacketAdapter + udpHashSeed = maphash.MakeSeed() natTable = nat.New() rules []C.Rule listeners = make(map[string]C.InboundListener) @@ -70,8 +72,17 @@ func (t tunnel) HandleTCPConn(conn net.Conn, metadata *C.Metadata) { func (t tunnel) HandleUDPPacket(packet C.UDPPacket, metadata *C.Metadata) { packetAdapter := C.NewPacketAdapter(packet, metadata) + + var h maphash.Hash + + h.SetSeed(udpHashSeed) + h.WriteString(metadata.SourceAddress()) + h.WriteString(metadata.RemoteAddress()) + + queueNo := uint(h.Sum64()) % uint(len(udpQueues)) + select { - case udpQueue <- packetAdapter: + case udpQueues[queueNo] <- packetAdapter: default: } } @@ -141,7 +152,8 @@ func TCPIn() chan<- C.ConnContext { // UDPIn return fan-in udp queue // Deprecated: using Tunnel instead func UDPIn() chan<- C.PacketAdapter { - return udpQueue + // compatibility: first queue is always available for external callers + return udpQueues[0] } // NatTable return nat table @@ -243,8 +255,8 @@ func isHandle(t C.Type) bool { } // processUDP starts a loop to handle udp packet -func processUDP() { - queue := udpQueue +func processUDP(queueNo int) { + queue := udpQueues[queueNo] for conn := range queue { handleUDPConn(conn) } @@ -255,8 +267,11 @@ func process() { if num := runtime.GOMAXPROCS(0); num > numUDPWorkers { numUDPWorkers = num } + + udpQueues = make([]chan C.PacketAdapter, numUDPWorkers) for i := 0; i < numUDPWorkers; i++ { - go processUDP() + udpQueues[i] = make(chan C.PacketAdapter, 200) + go processUDP(i) } queue := tcpQueue From 5812a7bdeb5928ee5e33d18f677d21c8cf4b1bde Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 25 Sep 2024 21:37:15 +0800 Subject: [PATCH 239/311] chore: simplify the code --- tunnel/tunnel.go | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 5b55f07d..b6c61d76 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "hash/maphash" "net" "net/netip" "path/filepath" @@ -29,11 +28,12 @@ import ( "github.com/metacubex/mihomo/tunnel/statistic" ) +const queueSize = 200 + var ( status = newAtomicStatus(Suspend) - tcpQueue = make(chan C.ConnContext, 200) + tcpQueue = make(chan C.ConnContext, queueSize) udpQueues []chan C.PacketAdapter - udpHashSeed = maphash.MakeSeed() natTable = nat.New() rules []C.Rule listeners = make(map[string]C.InboundListener) @@ -73,13 +73,8 @@ func (t tunnel) HandleTCPConn(conn net.Conn, metadata *C.Metadata) { func (t tunnel) HandleUDPPacket(packet C.UDPPacket, metadata *C.Metadata) { packetAdapter := C.NewPacketAdapter(packet, metadata) - var h maphash.Hash - - h.SetSeed(udpHashSeed) - h.WriteString(metadata.SourceAddress()) - h.WriteString(metadata.RemoteAddress()) - - queueNo := uint(h.Sum64()) % uint(len(udpQueues)) + hash := utils.MapHash(metadata.SourceAddress() + "-" + metadata.RemoteAddress()) + queueNo := uint(hash) % uint(len(udpQueues)) select { case udpQueues[queueNo] <- packetAdapter: @@ -255,8 +250,7 @@ func isHandle(t C.Type) bool { } // processUDP starts a loop to handle udp packet -func processUDP(queueNo int) { - queue := udpQueues[queueNo] +func processUDP(queue chan C.PacketAdapter) { for conn := range queue { handleUDPConn(conn) } @@ -270,8 +264,9 @@ func process() { udpQueues = make([]chan C.PacketAdapter, numUDPWorkers) for i := 0; i < numUDPWorkers; i++ { - udpQueues[i] = make(chan C.PacketAdapter, 200) - go processUDP(i) + queue := make(chan C.PacketAdapter, queueSize) + udpQueues[i] = queue + go processUDP(queue) } queue := tcpQueue From 4fa15c633494f6cf2fac2bef282667b4b0ee9db2 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 26 Sep 2024 11:21:07 +0800 Subject: [PATCH 240/311] chore: ensures packets can be sent without blocking the tunnel --- component/nat/table.go | 44 +++------ constant/adapters.go | 26 +++-- tunnel/connection.go | 75 +++++++++++++- tunnel/tunnel.go | 220 +++++++++++++++++++++-------------------- 4 files changed, 218 insertions(+), 147 deletions(-) diff --git a/component/nat/table.go b/component/nat/table.go index bb5ab755..66241fb4 100644 --- a/component/nat/table.go +++ b/component/nat/table.go @@ -10,47 +10,30 @@ import ( ) type Table struct { - mapping *xsync.MapOf[string, *Entry] - lockMap *xsync.MapOf[string, *sync.Cond] + mapping *xsync.MapOf[string, *entry] } -type Entry struct { - PacketConn C.PacketConn - WriteBackProxy C.WriteBackProxy +type entry struct { + PacketSender C.PacketSender 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: xsync.NewMapOf[string, *net.UDPConn](), - LocalLockMap: xsync.NewMapOf[string, *sync.Cond](), +func (t *Table) GetOrCreate(key string, maker func() C.PacketSender) (C.PacketSender, bool) { + item, loaded := t.mapping.LoadOrCompute(key, func() *entry { + return &entry{ + PacketSender: maker(), + LocalUDPConnMap: xsync.NewMapOf[string, *net.UDPConn](), + LocalLockMap: xsync.NewMapOf[string, *sync.Cond](), + } }) -} - -func (t *Table) Get(key string) (C.PacketConn, C.WriteBackProxy) { - entry, exist := t.getEntry(key) - if !exist { - return nil, nil - } - return entry.PacketConn, entry.WriteBackProxy -} - -func (t *Table) GetOrCreateLock(key string) (*sync.Cond, bool) { - item, loaded := t.lockMap.LoadOrCompute(key, makeLock) - return item, loaded + return item.PacketSender, loaded } func (t *Table) Delete(key string) { t.mapping.Delete(key) } -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 { @@ -105,7 +88,7 @@ func (t *Table) DeleteLockForLocalConn(lAddr, key string) { entry.LocalLockMap.Delete(key) } -func (t *Table) getEntry(key string) (*Entry, bool) { +func (t *Table) getEntry(key string) (*entry, bool) { return t.mapping.Load(key) } @@ -116,7 +99,6 @@ func makeLock() *sync.Cond { // New return *Cache func New() *Table { return &Table{ - mapping: xsync.NewMapOf[string, *Entry](), - lockMap: xsync.NewMapOf[string, *sync.Cond](), + mapping: xsync.NewMapOf[string, *entry](), } } diff --git a/constant/adapters.go b/constant/adapters.go index cb213b3c..3731cd60 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -255,12 +255,16 @@ type UDPPacketInAddr interface { // PacketAdapter is a UDP Packet adapter for socks/redir/tun type PacketAdapter interface { UDPPacket + // Metadata returns destination metadata Metadata() *Metadata + // Key is a SNAT key + Key() string } type packetAdapter struct { UDPPacket metadata *Metadata + key string } // Metadata returns destination metadata @@ -268,10 +272,16 @@ func (s *packetAdapter) Metadata() *Metadata { return s.metadata } +// Key is a SNAT key +func (s *packetAdapter) Key() string { + return s.key +} + func NewPacketAdapter(packet UDPPacket, metadata *Metadata) PacketAdapter { return &packetAdapter{ packet, metadata, + packet.LocalAddr().String(), } } @@ -284,17 +294,19 @@ type WriteBackProxy interface { UpdateWriteBack(wb WriteBack) } +type PacketSender interface { + // Send will send PacketAdapter nonblocking + // the implement must call UDPPacket.Drop() inside Send + Send(PacketAdapter) + Process(PacketConn, WriteBackProxy) + Close() +} + type NatTable interface { - Set(key string, e PacketConn, w WriteBackProxy) - - Get(key string) (PacketConn, WriteBackProxy) - - GetOrCreateLock(key string) (*sync.Cond, bool) + GetOrCreate(key string, maker func() PacketSender) (PacketSender, bool) Delete(key string) - DeleteLock(key string) - GetForLocalConn(lAddr, rAddr string) *net.UDPConn AddForLocalConn(lAddr, rAddr string, conn *net.UDPConn) bool diff --git a/tunnel/connection.go b/tunnel/connection.go index e96545e8..17e4efd0 100644 --- a/tunnel/connection.go +++ b/tunnel/connection.go @@ -1,6 +1,7 @@ package tunnel import ( + "context" "errors" "net" "net/netip" @@ -11,7 +12,78 @@ import ( "github.com/metacubex/mihomo/log" ) +type packetSender struct { + ctx context.Context + cancel context.CancelFunc + ch chan C.PacketAdapter +} + +// newPacketSender return a chan based C.PacketSender +// It ensures that packets can be sent sequentially and without blocking +func newPacketSender() C.PacketSender { + ctx, cancel := context.WithCancel(context.Background()) + ch := make(chan C.PacketAdapter, senderCapacity) + return &packetSender{ + ctx: ctx, + cancel: cancel, + ch: ch, + } +} + +func (s *packetSender) Process(pc C.PacketConn, proxy C.WriteBackProxy) { + for { + select { + case <-s.ctx.Done(): + return // sender closed + case packet := <-s.ch: + if proxy != nil { + proxy.UpdateWriteBack(packet) + } + _ = handleUDPToRemote(packet, pc, packet.Metadata()) + packet.Drop() + } + } +} + +func (s *packetSender) dropAll() { + for { + select { + case data := <-s.ch: + data.Drop() // drop all data still in chan + default: + return // no data, exit goroutine + } + } +} + +func (s *packetSender) Send(packet C.PacketAdapter) { + select { + case <-s.ctx.Done(): + packet.Drop() // sender closed before Send() + return + default: + } + + select { + case s.ch <- packet: + // put ok, so don't drop packet, will process by other side of chan + case <-s.ctx.Done(): + packet.Drop() // sender closed when putting data to chan + default: + packet.Drop() // chan is full + } +} + +func (s *packetSender) Close() { + s.cancel() + s.dropAll() +} + func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata) error { + if err := resolveUDP(metadata); err != nil { + return err + } + addr := metadata.UDPAddr() if addr == nil { return errors.New("udp addr invalid") @@ -26,8 +98,9 @@ func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata return nil } -func handleUDPToLocal(writeBack C.WriteBack, pc N.EnhancePacketConn, key string, oAddrPort netip.AddrPort, fAddr netip.Addr) { +func handleUDPToLocal(writeBack C.WriteBack, pc N.EnhancePacketConn, sender C.PacketSender, key string, oAddrPort netip.AddrPort, fAddr netip.Addr) { defer func() { + sender.Close() _ = pc.Close() closeAllLocalCoon(key) natTable.Delete(key) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index b6c61d76..af16e4ae 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -28,11 +28,14 @@ import ( "github.com/metacubex/mihomo/tunnel/statistic" ) -const queueSize = 200 +const ( + queueCapacity = 64 // chan capacity tcpQueue and udpQueue + senderCapacity = 128 // chan capacity of PacketSender +) var ( status = newAtomicStatus(Suspend) - tcpQueue = make(chan C.ConnContext, queueSize) + udpInit sync.Once udpQueues []chan C.PacketAdapter natTable = nat.New() rules []C.Rule @@ -43,6 +46,12 @@ var ( ruleProviders map[string]provider.RuleProvider configMux sync.RWMutex + // for compatibility, lazy init + tcpQueue chan C.ConnContext + tcpInOnce sync.Once + udpQueue chan C.PacketAdapter + udpInOnce sync.Once + // Outbound Rule mode = Rule @@ -70,15 +79,33 @@ func (t tunnel) HandleTCPConn(conn net.Conn, metadata *C.Metadata) { handleTCPConn(connCtx) } -func (t tunnel) HandleUDPPacket(packet C.UDPPacket, metadata *C.Metadata) { - packetAdapter := C.NewPacketAdapter(packet, metadata) +func initUDP() { + numUDPWorkers := 4 + if num := runtime.GOMAXPROCS(0); num > numUDPWorkers { + numUDPWorkers = num + } - hash := utils.MapHash(metadata.SourceAddress() + "-" + metadata.RemoteAddress()) + udpQueues = make([]chan C.PacketAdapter, numUDPWorkers) + for i := 0; i < numUDPWorkers; i++ { + queue := make(chan C.PacketAdapter, queueCapacity) + udpQueues[i] = queue + go processUDP(queue) + } +} + +func (t tunnel) HandleUDPPacket(packet C.UDPPacket, metadata *C.Metadata) { + udpInit.Do(initUDP) + + packetAdapter := C.NewPacketAdapter(packet, metadata) + key := packetAdapter.Key() + + hash := utils.MapHash(key) queueNo := uint(hash) % uint(len(udpQueues)) select { case udpQueues[queueNo] <- packetAdapter: default: + packet.Drop() } } @@ -134,21 +161,32 @@ func IsSniffing() bool { return sniffingEnable } -func init() { - go process() -} - // TCPIn return fan-in queue // Deprecated: using Tunnel instead func TCPIn() chan<- C.ConnContext { + tcpInOnce.Do(func() { + tcpQueue = make(chan C.ConnContext, queueCapacity) + go func() { + for connCtx := range tcpQueue { + go handleTCPConn(connCtx) + } + }() + }) return tcpQueue } // UDPIn return fan-in udp queue // Deprecated: using Tunnel instead func UDPIn() chan<- C.PacketAdapter { - // compatibility: first queue is always available for external callers - return udpQueues[0] + udpInOnce.Do(func() { + udpQueue = make(chan C.PacketAdapter, queueCapacity) + go func() { + for packet := range udpQueue { + Tunnel.HandleUDPPacket(packet, packet.Metadata()) + } + }() + }) + return udpQueue } // NatTable return nat table @@ -249,32 +287,6 @@ func isHandle(t C.Type) bool { return status == Running || (status == Inner && t == C.INNER) } -// processUDP starts a loop to handle udp packet -func processUDP(queue chan C.PacketAdapter) { - for conn := range queue { - handleUDPConn(conn) - } -} - -func process() { - numUDPWorkers := 4 - if num := runtime.GOMAXPROCS(0); num > numUDPWorkers { - numUDPWorkers = num - } - - udpQueues = make([]chan C.PacketAdapter, numUDPWorkers) - for i := 0; i < numUDPWorkers; i++ { - queue := make(chan C.PacketAdapter, queueSize) - udpQueues[i] = queue - go processUDP(queue) - } - - queue := tcpQueue - for conn := range queue { - go handleTCPConn(conn) - } -} - func needLookupIP(metadata *C.Metadata) bool { return resolver.MappingEnabled() && metadata.Host == "" && metadata.DstIP.IsValid() } @@ -334,6 +346,25 @@ func resolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err erro return } +func resolveUDP(metadata *C.Metadata) error { + // local resolve UDP dns + if !metadata.Resolved() { + ip, err := resolver.ResolveIP(context.Background(), metadata.Host) + if err != nil { + return err + } + metadata.DstIP = ip + } + return nil +} + +// processUDP starts a loop to handle udp packet +func processUDP(queue chan C.PacketAdapter) { + for conn := range queue { + handleUDPConn(conn) + } +} + func handleUDPConn(packet C.PacketAdapter) { if !isHandle(packet.Metadata().Type) { packet.Drop() @@ -363,85 +394,58 @@ func handleUDPConn(packet C.PacketAdapter) { snifferDispatcher.UDPSniff(packet) } - // local resolve UDP dns - if !metadata.Resolved() { - ip, err := resolver.ResolveIP(context.Background(), metadata.Host) - if err != nil { - return - } - metadata.DstIP = ip - } - - key := packet.LocalAddr().String() - - handle := func() bool { - pc, proxy := natTable.Get(key) - if pc != nil { - if proxy != nil { - proxy.UpdateWriteBack(packet) + key := packet.Key() + sender, loaded := natTable.GetOrCreate(key, newPacketSender) + if !loaded { + dial := func() (C.PacketConn, C.WriteBackProxy, error) { + if err := resolveUDP(metadata); err != nil { + log.Warnln("[UDP] Resolve Ip error: %s", err) + return nil, nil, err } - _ = handleUDPToRemote(packet, pc, metadata) - return true - } - return false - } - if handle() { - packet.Drop() - return - } + proxy, rule, err := resolveMetadata(metadata) + if err != nil { + log.Warnln("[UDP] Parse metadata failed: %s", err.Error()) + return nil, nil, err + } - cond, loaded := natTable.GetOrCreateLock(key) + ctx, cancel := context.WithTimeout(context.Background(), C.DefaultUDPTimeout) + defer cancel() + rawPc, err := retry(ctx, func(ctx context.Context) (C.PacketConn, error) { + return proxy.ListenPacketContext(ctx, metadata.Pure()) + }, func(err error) { + logMetadataErr(metadata, rule, proxy, err) + }) + if err != nil { + return nil, nil, err + } + logMetadata(metadata, rule, rawPc) - go func() { - defer packet.Drop() + pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule, 0, 0, true) - if loaded { - cond.L.Lock() - cond.Wait() - handle() - cond.L.Unlock() - return + if rawPc.Chains().Last() == "REJECT-DROP" { + _ = pc.Close() + return nil, nil, errors.New("rejected drop packet") + } + + oAddrPort := metadata.AddrPort() + writeBackProxy := nat.NewWriteBackProxy(packet) + + go handleUDPToLocal(writeBackProxy, pc, sender, key, oAddrPort, fAddr) + return pc, writeBackProxy, nil } - defer func() { - natTable.DeleteLock(key) - cond.Broadcast() + go func() { + pc, proxy, err := dial() + if err != nil { + sender.Close() + natTable.Delete(key) + return + } + sender.Process(pc, proxy) }() - - proxy, rule, err := resolveMetadata(metadata) - if err != nil { - log.Warnln("[UDP] Parse metadata failed: %s", err.Error()) - return - } - - ctx, cancel := context.WithTimeout(context.Background(), C.DefaultUDPTimeout) - defer cancel() - rawPc, err := retry(ctx, func(ctx context.Context) (C.PacketConn, error) { - return proxy.ListenPacketContext(ctx, metadata.Pure()) - }, func(err error) { - logMetadataErr(metadata, rule, proxy, err) - }) - if err != nil { - return - } - logMetadata(metadata, rule, rawPc) - - pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule, 0, 0, true) - - if rawPc.Chains().Last() == "REJECT-DROP" { - pc.Close() - return - } - - oAddrPort := metadata.AddrPort() - writeBackProxy := nat.NewWriteBackProxy(packet) - natTable.Set(key, pc, writeBackProxy) - - go handleUDPToLocal(writeBackProxy, pc, key, oAddrPort, fAddr) - - handle() - }() + } + sender.Send(packet) // nonblocking } func handleTCPConn(connCtx C.ConnContext) { From 43cb48231ac5adc5404868ff61d044aeb34e0608 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 26 Sep 2024 22:21:59 +0800 Subject: [PATCH 241/311] cache: add dns cache in udp packet sender reduce the cost of re-resolving DNS for each packet received and prevent the target IP from jumping between multiple resolution results --- constant/adapters.go | 4 ++++ tunnel/connection.go | 31 ++++++++++++++++++++++++++----- tunnel/tunnel.go | 14 +------------- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/constant/adapters.go b/constant/adapters.go index 3731cd60..cb47f871 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -298,7 +298,11 @@ type PacketSender interface { // Send will send PacketAdapter nonblocking // the implement must call UDPPacket.Drop() inside Send Send(PacketAdapter) + // Process is a blocking loop to send PacketAdapter to PacketConn and update the WriteBackProxy Process(PacketConn, WriteBackProxy) + // ResolveUDP do a local resolve UDP dns blocking if metadata is not resolved + ResolveUDP(*Metadata) error + // Close stop the Process loop Close() } diff --git a/tunnel/connection.go b/tunnel/connection.go index 17e4efd0..1ea0678c 100644 --- a/tunnel/connection.go +++ b/tunnel/connection.go @@ -7,7 +7,9 @@ import ( "net/netip" "time" + "github.com/metacubex/mihomo/common/lru" N "github.com/metacubex/mihomo/common/net" + "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" ) @@ -16,6 +18,7 @@ type packetSender struct { ctx context.Context cancel context.CancelFunc ch chan C.PacketAdapter + cache *lru.LruCache[string, netip.Addr] } // newPacketSender return a chan based C.PacketSender @@ -27,6 +30,7 @@ func newPacketSender() C.PacketSender { ctx: ctx, cancel: cancel, ch: ch, + cache: lru.New[string, netip.Addr](lru.WithSize[string, netip.Addr](senderCapacity)), } } @@ -39,7 +43,11 @@ func (s *packetSender) Process(pc C.PacketConn, proxy C.WriteBackProxy) { if proxy != nil { proxy.UpdateWriteBack(packet) } - _ = handleUDPToRemote(packet, pc, packet.Metadata()) + if err := s.ResolveUDP(packet.Metadata()); err != nil { + log.Warnln("[UDP] Resolve Ip error: %s", err) + } else { + _ = handleUDPToRemote(packet, pc, packet.Metadata()) + } packet.Drop() } } @@ -79,11 +87,24 @@ func (s *packetSender) Close() { s.dropAll() } -func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata) error { - if err := resolveUDP(metadata); err != nil { - return err - } +func (s *packetSender) ResolveUDP(metadata *C.Metadata) (err error) { + // local resolve UDP dns + if !metadata.Resolved() { + ip, ok := s.cache.Get(metadata.Host) + if !ok { + ip, err = resolver.ResolveIP(s.ctx, metadata.Host) + if err != nil { + return err + } + s.cache.Set(metadata.Host, ip) + } + metadata.DstIP = ip + } + return nil +} + +func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata) error { addr := metadata.UDPAddr() if addr == nil { return errors.New("udp addr invalid") diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index af16e4ae..5c136eb2 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -346,18 +346,6 @@ func resolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err erro return } -func resolveUDP(metadata *C.Metadata) error { - // local resolve UDP dns - if !metadata.Resolved() { - ip, err := resolver.ResolveIP(context.Background(), metadata.Host) - if err != nil { - return err - } - metadata.DstIP = ip - } - return nil -} - // processUDP starts a loop to handle udp packet func processUDP(queue chan C.PacketAdapter) { for conn := range queue { @@ -398,7 +386,7 @@ func handleUDPConn(packet C.PacketAdapter) { sender, loaded := natTable.GetOrCreate(key, newPacketSender) if !loaded { dial := func() (C.PacketConn, C.WriteBackProxy, error) { - if err := resolveUDP(metadata); err != nil { + if err := sender.ResolveUDP(metadata); err != nil { log.Warnln("[UDP] Resolve Ip error: %s", err) return nil, nil, err } From 88bfe7cffea00fee448ef0e6ebb2f373a7a23c2b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 27 Sep 2024 09:57:09 +0800 Subject: [PATCH 242/311] feat: add `external-controller-pipe` for windows maybe useful for electron and tauri client, node.js and rust still not support AF_UNIX on windows --- adapter/inbound/listen_notwindows.go | 14 +++++++++++ adapter/inbound/listen_windows.go | 27 +++++++++++++++++++++ config/config.go | 3 +++ docs/config.yaml | 4 ++++ hub/hub.go | 7 ++++++ hub/route/server.go | 35 ++++++++++++++++++++++++++++ main.go | 17 +++++--------- 7 files changed, 96 insertions(+), 11 deletions(-) create mode 100644 adapter/inbound/listen_notwindows.go create mode 100644 adapter/inbound/listen_windows.go diff --git a/adapter/inbound/listen_notwindows.go b/adapter/inbound/listen_notwindows.go new file mode 100644 index 00000000..8fdfb7b8 --- /dev/null +++ b/adapter/inbound/listen_notwindows.go @@ -0,0 +1,14 @@ +//go:build !windows + +package inbound + +import ( + "net" + "os" +) + +const SupportNamedPipe = false + +func ListenNamedPipe(path string) (net.Listener, error) { + return nil, os.ErrInvalid +} diff --git a/adapter/inbound/listen_windows.go b/adapter/inbound/listen_windows.go new file mode 100644 index 00000000..0dc8e8ca --- /dev/null +++ b/adapter/inbound/listen_windows.go @@ -0,0 +1,27 @@ +package inbound + +import ( + "net" + + "github.com/metacubex/wireguard-go/ipc/namedpipe" + "golang.org/x/sys/windows" +) + +const SupportNamedPipe = true + +// windowsSDDL is the Security Descriptor set on the namedpipe. +// It provides read/write access to all users and the local system. +const windowsSDDL = "D:PAI(A;OICI;GWGR;;;BU)(A;OICI;GWGR;;;SY)" + +func ListenNamedPipe(path string) (net.Listener, error) { + securityDescriptor, err := windows.SecurityDescriptorFromString(windowsSDDL) + if err != nil { + return nil, err + } + namedpipeLC := namedpipe.ListenConfig{ + SecurityDescriptor: securityDescriptor, + InputBufferSize: 256 * 1024, + OutputBufferSize: 256 * 1024, + } + return namedpipeLC.Listen(path) +} diff --git a/config/config.go b/config/config.go index 27cde1fb..9067d14f 100644 --- a/config/config.go +++ b/config/config.go @@ -103,6 +103,7 @@ type Controller struct { ExternalController string ExternalControllerTLS string ExternalControllerUnix string + ExternalControllerPipe string ExternalUI string ExternalDohServer string Secret string @@ -364,6 +365,7 @@ type RawConfig struct { LogLevel log.LogLevel `yaml:"log-level" json:"log-level"` IPv6 bool `yaml:"ipv6" json:"ipv6"` ExternalController string `yaml:"external-controller" json:"external-controller"` + ExternalControllerPipe string `yaml:"external-controller-pipe" json:"external-controller-pipe"` ExternalControllerUnix string `yaml:"external-controller-unix" json:"external-controller-unix"` ExternalControllerTLS string `yaml:"external-controller-tls" json:"external-controller-tls"` ExternalUI string `yaml:"external-ui" json:"external-ui"` @@ -769,6 +771,7 @@ func parseController(cfg *RawConfig) (*Controller, error) { ExternalController: cfg.ExternalController, ExternalUI: cfg.ExternalUI, Secret: cfg.Secret, + ExternalControllerPipe: cfg.ExternalControllerPipe, ExternalControllerUnix: cfg.ExternalControllerUnix, ExternalControllerTLS: cfg.ExternalControllerTLS, ExternalDohServer: cfg.ExternalDohServer, diff --git a/docs/config.yaml b/docs/config.yaml index b3515a20..9c480b3f 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -63,6 +63,10 @@ external-controller-tls: 0.0.0.0:9443 # RESTful API HTTPS 监听地址,需要 # 测试方法: curl -v --unix-socket "mihomo.sock" http://localhost/ external-controller-unix: mihomo.sock +# RESTful API Windows namedpipe 监听地址 +# !!!注意: 从Windows namedpipe访问api接口不会验证secret, 如果开启请自行保证安全问题 !!! +external-controller-pipe: \\.\pipe\mihomo + # tcp-concurrent: true # TCP 并发连接所有 IP, 将使用最快握手的 TCP # 配置 WEB UI 目录,使用 http://{{external-controller}}/ui 访问 diff --git a/hub/hub.go b/hub/hub.go index e22f7219..73a44eee 100644 --- a/hub/hub.go +++ b/hub/hub.go @@ -27,6 +27,12 @@ func WithExternalControllerUnix(externalControllerUnix string) Option { } } +func WithExternalControllerPipe(externalControllerPipe string) Option { + return func(cfg *config.Config) { + cfg.Controller.ExternalControllerPipe = externalControllerPipe + } +} + func WithSecret(secret string) Option { return func(cfg *config.Config) { cfg.Controller.Secret = secret @@ -47,6 +53,7 @@ func applyRoute(cfg *config.Config) { Addr: cfg.Controller.ExternalController, TLSAddr: cfg.Controller.ExternalControllerTLS, UnixAddr: cfg.Controller.ExternalControllerUnix, + PipeAddr: cfg.Controller.ExternalControllerPipe, Secret: cfg.Controller.Secret, Certificate: cfg.TLS.Certificate, PrivateKey: cfg.TLS.PrivateKey, diff --git a/hub/route/server.go b/hub/route/server.go index b7077563..4c22609c 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -35,6 +35,7 @@ var ( httpServer *http.Server tlsServer *http.Server unixServer *http.Server + pipeServer *http.Server ) type Traffic struct { @@ -51,6 +52,7 @@ type Config struct { Addr string TLSAddr string UnixAddr string + PipeAddr string Secret string Certificate string PrivateKey string @@ -62,6 +64,9 @@ func ReCreateServer(cfg *Config) { go start(cfg) go startTLS(cfg) go startUnix(cfg) + if inbound.SupportNamedPipe { + go startPipe(cfg) + } } func SetUIPath(path string) { @@ -233,7 +238,37 @@ func startUnix(cfg *Config) { log.Errorln("External controller unix serve error: %s", err) } } +} +func startPipe(cfg *Config) { + // first stop existing server + if pipeServer != nil { + _ = pipeServer.Close() + pipeServer = nil + } + + // handle addr + if len(cfg.PipeAddr) > 0 { + if !strings.HasPrefix(cfg.PipeAddr, "\\\\.\\pipe\\") { // windows namedpipe must start with "\\.\pipe\" + log.Errorln("External controller pipe listen error: windows namedpipe must start with \"\\\\.\\pipe\\\"") + return + } + + l, err := inbound.ListenNamedPipe(cfg.PipeAddr) + if err != nil { + log.Errorln("External controller pipe listen error: %s", err) + return + } + log.Infoln("RESTful API pipe listening at: %s", l.Addr().String()) + + server := &http.Server{ + Handler: router(cfg.IsDebug, "", cfg.DohServer), + } + pipeServer = server + if err = server.Serve(l); err != nil { + log.Errorln("External controller pipe serve error: %s", err) + } + } } func setPrivateNetworkAccess(next http.Handler) http.Handler { diff --git a/main.go b/main.go index 8910a006..505cdb25 100644 --- a/main.go +++ b/main.go @@ -35,6 +35,7 @@ var ( externalUI string externalController string externalControllerUnix string + externalControllerPipe string secret string ) @@ -45,6 +46,7 @@ func init() { flag.StringVar(&externalUI, "ext-ui", os.Getenv("CLASH_OVERRIDE_EXTERNAL_UI_DIR"), "override external ui directory") flag.StringVar(&externalController, "ext-ctl", os.Getenv("CLASH_OVERRIDE_EXTERNAL_CONTROLLER"), "override external controller address") flag.StringVar(&externalControllerUnix, "ext-ctl-unix", os.Getenv("CLASH_OVERRIDE_EXTERNAL_CONTROLLER_UNIX"), "override external controller unix address") + flag.StringVar(&externalControllerPipe, "ext-ctl-pipe", os.Getenv("CLASH_OVERRIDE_EXTERNAL_CONTROLLER_PIPE"), "override external controller pipe address") flag.StringVar(&secret, "secret", os.Getenv("CLASH_OVERRIDE_SECRET"), "override secret for RESTful API") flag.BoolVar(&geodataMode, "m", false, "set geodata mode") flag.BoolVar(&version, "v", false, "show current version of mihomo") @@ -133,6 +135,9 @@ func main() { if externalControllerUnix != "" { options = append(options, hub.WithExternalControllerUnix(externalControllerUnix)) } + if externalControllerPipe != "" { + options = append(options, hub.WithExternalControllerPipe(externalControllerPipe)) + } if secret != "" { options = append(options, hub.WithSecret(secret)) } @@ -156,19 +161,9 @@ func main() { case <-termSign: return case <-hupSign: - var cfg *config.Config - var err error - if configString != "" { - cfg, err = executor.ParseWithBytes(configBytes) - } else { - cfg, err = executor.ParseWithPath(C.Path.Config()) - } - if err == nil { - hub.ApplyConfig(cfg) - } else { + if err := hub.Parse(configBytes, options...); err != nil { log.Errorln("Parse config error: %s", err.Error()) } - } } } From cd2d1c6bb0e88c8833fed7e335f3874f78ced4d6 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 27 Sep 2024 18:10:05 +0800 Subject: [PATCH 243/311] fix: `skip-auth-prefixes` not apply on listeners when `users` is unset --- component/auth/auth.go | 5 +++++ hub/executor/executor.go | 4 ++-- listener/auth/auth.go | 30 +++++++++++++++++++++++------- listener/http/proxy.go | 4 ++-- listener/http/server.go | 18 +++++++++--------- listener/inbound/auth.go | 6 +++--- listener/inbound/http.go | 2 +- listener/inbound/mixed.go | 2 +- listener/inbound/socks.go | 2 +- listener/mixed/mixed.go | 20 ++++++++++---------- listener/socks/tcp.go | 26 +++++++++++++------------- 11 files changed, 70 insertions(+), 49 deletions(-) diff --git a/component/auth/auth.go b/component/auth/auth.go index b52fa135..176b21d7 100644 --- a/component/auth/auth.go +++ b/component/auth/auth.go @@ -5,6 +5,11 @@ type Authenticator interface { Users() []string } +type AuthStore interface { + Authenticator() Authenticator + SetAuthenticator(Authenticator) +} + type AuthUser struct { User string Pass string diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 66bbc89b..39bf28d2 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -127,7 +127,7 @@ func initInnerTcp() { func GetGeneral() *config.General { ports := listener.GetPorts() var authenticator []string - if auth := authStore.Authenticator(); auth != nil { + if auth := authStore.Default.Authenticator(); auth != nil { authenticator = auth.Users() } @@ -422,7 +422,7 @@ func updateGeneral(general *config.General) { func updateUsers(users []auth.AuthUser) { authenticator := auth.NewAuthenticator(users) - authStore.SetAuthenticator(authenticator) + authStore.Default.SetAuthenticator(authenticator) if authenticator != nil { log.Infoln("Authentication of local server updated") } diff --git a/listener/auth/auth.go b/listener/auth/auth.go index 772be3bd..9e7632e8 100644 --- a/listener/auth/auth.go +++ b/listener/auth/auth.go @@ -4,14 +4,30 @@ import ( "github.com/metacubex/mihomo/component/auth" ) -var authenticator auth.Authenticator - -func Authenticator() auth.Authenticator { - return authenticator +type authStore struct { + authenticator auth.Authenticator } -func SetAuthenticator(au auth.Authenticator) { - authenticator = au +func (a *authStore) Authenticator() auth.Authenticator { + return a.authenticator } -func Nil() auth.Authenticator { return nil } +func (a *authStore) SetAuthenticator(authenticator auth.Authenticator) { + a.authenticator = authenticator +} + +func NewAuthStore(authenticator auth.Authenticator) auth.AuthStore { + return &authStore{authenticator} +} + +var Default auth.AuthStore = NewAuthStore(nil) + +type nilAuthStore struct{} + +func (a *nilAuthStore) Authenticator() auth.Authenticator { + return nil +} + +func (a *nilAuthStore) SetAuthenticator(authenticator auth.Authenticator) {} + +var Nil auth.AuthStore = (*nilAuthStore)(nil) // always return nil, even call SetAuthenticator() with a non-nil authenticator diff --git a/listener/http/proxy.go b/listener/http/proxy.go index 04ab98eb..5c08cd45 100644 --- a/listener/http/proxy.go +++ b/listener/http/proxy.go @@ -30,7 +30,7 @@ func (b *bodyWrapper) Read(p []byte) (n int, err error) { return n, err } -func HandleConn(c net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) { +func HandleConn(c net.Conn, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) { additions = append(additions, inbound.Placeholder) // Add a placeholder for InUser inUserIdx := len(additions) - 1 client := newClient(c, tunnel, additions) @@ -41,7 +41,7 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticator, conn := N.NewBufferedConn(c) - authenticator := getAuth() + authenticator := store.Authenticator() keepAlive := true trusted := authenticator == nil // disable authenticate if lru is nil lastUser := "" diff --git a/listener/http/server.go b/listener/http/server.go index 04f32f4f..24f07e8b 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -32,20 +32,20 @@ func (l *Listener) Close() error { } func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) { - return NewWithAuthenticator(addr, tunnel, authStore.Authenticator, additions...) + return NewWithAuthenticator(addr, tunnel, authStore.Default, additions...) } // NewWithAuthenticate // never change type traits because it's used in CMFA func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additions ...inbound.Addition) (*Listener, error) { - getAuth := authStore.Authenticator + store := authStore.Default if !authenticate { - getAuth = authStore.Nil + store = authStore.Default } - return NewWithAuthenticator(addr, tunnel, getAuth, additions...) + return NewWithAuthenticator(addr, tunnel, store, additions...) } -func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) (*Listener, error) { +func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) (*Listener, error) { isDefault := false if len(additions) == 0 { isDefault = true @@ -74,17 +74,17 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Auth continue } - getAuth := getAuth - if isDefault { // only apply on default listener + store := store + if isDefault || store == authStore.Default { // only apply on default listener if !inbound.IsRemoteAddrDisAllowed(conn.RemoteAddr()) { _ = conn.Close() continue } if inbound.SkipAuthRemoteAddr(conn.RemoteAddr()) { - getAuth = authStore.Nil + store = authStore.Nil } } - go HandleConn(conn, tunnel, getAuth, additions...) + go HandleConn(conn, tunnel, store, additions...) } }() diff --git a/listener/inbound/auth.go b/listener/inbound/auth.go index 41f18fc0..85e72494 100644 --- a/listener/inbound/auth.go +++ b/listener/inbound/auth.go @@ -12,7 +12,7 @@ type AuthUser struct { type AuthUsers []AuthUser -func (a AuthUsers) GetAuth() func() auth.Authenticator { +func (a AuthUsers) GetAuthStore() auth.AuthStore { if a != nil { // structure's Decode will ensure value not nil when input has value even it was set an empty array if len(a) == 0 { return authStore.Nil @@ -25,7 +25,7 @@ func (a AuthUsers) GetAuth() func() auth.Authenticator { } } authenticator := auth.NewAuthenticator(users) - return func() auth.Authenticator { return authenticator } + return authStore.NewAuthStore(authenticator) } - return authStore.Authenticator + return authStore.Default } diff --git a/listener/inbound/http.go b/listener/inbound/http.go index c78abefd..e20a9a23 100644 --- a/listener/inbound/http.go +++ b/listener/inbound/http.go @@ -45,7 +45,7 @@ func (h *HTTP) Address() string { // Listen implements constant.InboundListener func (h *HTTP) Listen(tunnel C.Tunnel) error { var err error - h.l, err = http.NewWithAuthenticator(h.RawAddress(), tunnel, h.config.Users.GetAuth(), h.Additions()...) + h.l, err = http.NewWithAuthenticator(h.RawAddress(), tunnel, h.config.Users.GetAuthStore(), h.Additions()...) if err != nil { return err } diff --git a/listener/inbound/mixed.go b/listener/inbound/mixed.go index 443a2564..1d79929a 100644 --- a/listener/inbound/mixed.go +++ b/listener/inbound/mixed.go @@ -53,7 +53,7 @@ func (m *Mixed) Address() string { // Listen implements constant.InboundListener func (m *Mixed) Listen(tunnel C.Tunnel) error { var err error - m.l, err = mixed.NewWithAuthenticator(m.RawAddress(), tunnel, m.config.Users.GetAuth(), m.Additions()...) + m.l, err = mixed.NewWithAuthenticator(m.RawAddress(), tunnel, m.config.Users.GetAuthStore(), m.Additions()...) if err != nil { return err } diff --git a/listener/inbound/socks.go b/listener/inbound/socks.go index cf6d1ce4..119eec82 100644 --- a/listener/inbound/socks.go +++ b/listener/inbound/socks.go @@ -71,7 +71,7 @@ func (s *Socks) Address() string { // Listen implements constant.InboundListener func (s *Socks) Listen(tunnel C.Tunnel) error { var err error - if s.stl, err = socks.NewWithAuthenticator(s.RawAddress(), tunnel, s.config.Users.GetAuth(), s.Additions()...); err != nil { + if s.stl, err = socks.NewWithAuthenticator(s.RawAddress(), tunnel, s.config.Users.GetAuthStore(), s.Additions()...); err != nil { return err } if s.udp { diff --git a/listener/mixed/mixed.go b/listener/mixed/mixed.go index ac3a0c58..5ac63011 100644 --- a/listener/mixed/mixed.go +++ b/listener/mixed/mixed.go @@ -37,10 +37,10 @@ func (l *Listener) Close() error { } func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) { - return NewWithAuthenticator(addr, tunnel, authStore.Authenticator, additions...) + return NewWithAuthenticator(addr, tunnel, authStore.Default, additions...) } -func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) (*Listener, error) { +func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) (*Listener, error) { isDefault := false if len(additions) == 0 { isDefault = true @@ -68,24 +68,24 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Auth } continue } - getAuth := getAuth - if isDefault { // only apply on default listener + store := store + if isDefault || store == authStore.Default { // only apply on default listener if !inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) { _ = c.Close() continue } if inbound.SkipAuthRemoteAddr(c.RemoteAddr()) { - getAuth = authStore.Nil + store = authStore.Nil } } - go handleConn(c, tunnel, getAuth, additions...) + go handleConn(c, tunnel, store, additions...) } }() return ml, nil } -func handleConn(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) { +func handleConn(conn net.Conn, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) { bufConn := N.NewBufferedConn(conn) head, err := bufConn.Peek(1) if err != nil { @@ -94,10 +94,10 @@ func handleConn(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticato switch head[0] { case socks4.Version: - socks.HandleSocks4(bufConn, tunnel, getAuth, additions...) + socks.HandleSocks4(bufConn, tunnel, store, additions...) case socks5.Version: - socks.HandleSocks5(bufConn, tunnel, getAuth, additions...) + socks.HandleSocks5(bufConn, tunnel, store, additions...) default: - http.HandleConn(bufConn, tunnel, getAuth, additions...) + http.HandleConn(bufConn, tunnel, store, additions...) } } diff --git a/listener/socks/tcp.go b/listener/socks/tcp.go index 950384c1..cc66613e 100644 --- a/listener/socks/tcp.go +++ b/listener/socks/tcp.go @@ -36,10 +36,10 @@ func (l *Listener) Close() error { } func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) { - return NewWithAuthenticator(addr, tunnel, authStore.Authenticator, additions...) + return NewWithAuthenticator(addr, tunnel, authStore.Default, additions...) } -func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) (*Listener, error) { +func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) (*Listener, error) { isDefault := false if len(additions) == 0 { isDefault = true @@ -67,24 +67,24 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Auth } continue } - getAuth := getAuth - if isDefault { // only apply on default listener + store := store + if isDefault || store == authStore.Default { // only apply on default listener if !inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) { _ = c.Close() continue } if inbound.SkipAuthRemoteAddr(c.RemoteAddr()) { - getAuth = authStore.Nil + store = authStore.Nil } } - go handleSocks(c, tunnel, getAuth, additions...) + go handleSocks(c, tunnel, store, additions...) } }() return sl, nil } -func handleSocks(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) { +func handleSocks(conn net.Conn, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) { bufConn := N.NewBufferedConn(conn) head, err := bufConn.Peek(1) if err != nil { @@ -94,16 +94,16 @@ func handleSocks(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticat switch head[0] { case socks4.Version: - HandleSocks4(bufConn, tunnel, getAuth, additions...) + HandleSocks4(bufConn, tunnel, store, additions...) case socks5.Version: - HandleSocks5(bufConn, tunnel, getAuth, additions...) + HandleSocks5(bufConn, tunnel, store, additions...) default: conn.Close() } } -func HandleSocks4(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) { - authenticator := getAuth() +func HandleSocks4(conn net.Conn, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) { + authenticator := store.Authenticator() addr, _, user, err := socks4.ServerHandshake(conn, authenticator) if err != nil { conn.Close() @@ -113,8 +113,8 @@ func HandleSocks4(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authentica tunnel.HandleTCPConn(inbound.NewSocket(socks5.ParseAddr(addr), conn, C.SOCKS4, additions...)) } -func HandleSocks5(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) { - authenticator := getAuth() +func HandleSocks5(conn net.Conn, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) { + authenticator := store.Authenticator() target, command, user, err := socks5.ServerHandshake(conn, authenticator) if err != nil { conn.Close() From 2afa2798b176f079fe44ef659f11f21559c9bf91 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 27 Sep 2024 18:31:50 +0800 Subject: [PATCH 244/311] chore: allow set security descriptor of namedpipe by environment variable `LISTEN_NAMEDPIPE_SDDL` --- adapter/inbound/listen_windows.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/adapter/inbound/listen_windows.go b/adapter/inbound/listen_windows.go index 0dc8e8ca..d19239da 100644 --- a/adapter/inbound/listen_windows.go +++ b/adapter/inbound/listen_windows.go @@ -2,6 +2,7 @@ package inbound import ( "net" + "os" "github.com/metacubex/wireguard-go/ipc/namedpipe" "golang.org/x/sys/windows" @@ -14,7 +15,11 @@ const SupportNamedPipe = true const windowsSDDL = "D:PAI(A;OICI;GWGR;;;BU)(A;OICI;GWGR;;;SY)" func ListenNamedPipe(path string) (net.Listener, error) { - securityDescriptor, err := windows.SecurityDescriptorFromString(windowsSDDL) + sddl := os.Getenv("LISTEN_NAMEDPIPE_SDDL") + if sddl == "" { + sddl = windowsSDDL + } + securityDescriptor, err := windows.SecurityDescriptorFromString(sddl) if err != nil { return nil, err } From 1633885794954a55f8b0576203522553127a8ed8 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 27 Sep 2024 20:31:46 +0800 Subject: [PATCH 245/311] chore: update dependencies --- go.mod | 10 +++++----- go.sum | 22 +++++++++++----------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 22af95b3..b74cee92 100644 --- a/go.mod +++ b/go.mod @@ -49,12 +49,12 @@ require ( github.com/vmihailenco/msgpack/v5 v5.4.1 github.com/wk8/go-ordered-map/v2 v2.1.8 gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 - go.uber.org/automaxprocs v1.5.3 + go.uber.org/automaxprocs v1.6.0 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba - golang.org/x/crypto v0.26.0 + golang.org/x/crypto v0.27.0 golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa - golang.org/x/net v0.28.0 - golang.org/x/sys v0.24.0 + golang.org/x/net v0.29.0 + golang.org/x/sys v0.25.0 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.3.0 @@ -111,7 +111,7 @@ require ( go.uber.org/mock v0.4.0 // indirect golang.org/x/mod v0.20.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/text v0.18.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.24.0 // indirect ) diff --git a/go.sum b/go.sum index 9b824fc5..ba2d1819 100644 --- a/go.sum +++ b/go.sum @@ -222,16 +222,16 @@ gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 h1:UNrDfkQqiE gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7/go.mod h1:E+rxHvJG9H6PUdzq9NRG6csuLN3XUx98BfGOVWNYnXs= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ= -go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= -go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= 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.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -240,8 +240,8 @@ golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -261,12 +261,12 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= From acfc9f8baacbdc0662a2a984a22d2e5512455481 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 27 Sep 2024 20:09:35 +0800 Subject: [PATCH 246/311] chore: reset resolver's connection after default interface changed --- adapter/outbound/wireguard.go | 2 +- component/resolver/resolver.go | 10 +++++ dns/client.go | 2 + dns/dhcp.go | 6 +++ dns/doh.go | 27 ++++++++++++-- dns/doq.go | 4 ++ dns/patch_android.go | 7 ++++ dns/rcode.go | 2 + dns/resolver.go | 68 +++++++++++++++++++--------------- dns/system_common.go | 2 + hub/executor/executor.go | 5 ++- listener/sing_tun/server.go | 5 +++ 12 files changed, 103 insertions(+), 37 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 6f5a18f3..3928ab1b 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -296,7 +296,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { for i := range nss { nss[i].ProxyAdapter = refP } - outbound.resolver = dns.NewResolver(dns.Config{ + outbound.resolver, _ = dns.NewResolver(dns.Config{ Main: nss, IPv6: has6, }) diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index feb3f98f..bcdbb7e2 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -47,6 +47,7 @@ type Resolver interface { ExchangeContext(ctx context.Context, m *dns.Msg) (msg *dns.Msg, err error) Invalid() bool ClearCache() + ResetConnection() } // LookupIPv4WithResolver same as LookupIPv4, but with a resolver @@ -256,6 +257,15 @@ func LookupIPProxyServerHost(ctx context.Context, host string) ([]netip.Addr, er return LookupIP(ctx, host) } +func ResetConnection() { + if DefaultResolver != nil { + go DefaultResolver.ResetConnection() + } + if ProxyServerHostResolver != nil { + go ProxyServerHostResolver.ResetConnection() + } +} + func SortationAddr(ips []netip.Addr) (ipv4s, ipv6s []netip.Addr) { for _, v := range ips { if v.Unmap().Is4() { diff --git a/dns/client.go b/dns/client.go index 096b96a7..62fc12f9 100644 --- a/dns/client.go +++ b/dns/client.go @@ -103,3 +103,5 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) return ret.msg, ret.err } } + +func (c *client) ResetConnection() {} diff --git a/dns/dhcp.go b/dns/dhcp.go index dc1344f5..e3829b7c 100644 --- a/dns/dhcp.go +++ b/dns/dhcp.go @@ -53,6 +53,12 @@ func (d *dhcpClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, return } +func (d *dhcpClient) ResetConnection() { + for _, client := range d.clients { + client.ResetConnection() + } +} + func (d *dhcpClient) resolve(ctx context.Context) ([]dnsClient, error) { d.lock.Lock() diff --git a/dns/doh.go b/dns/doh.go index ffb65fce..027afd58 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -203,11 +203,23 @@ func (doh *dnsOverHTTPS) Close() (err error) { return doh.closeClient(doh.client) } -// closeClient cleans up resources used by client if necessary. Note, that at -// this point it should only be done for HTTP/3 as it may leak due to keep-alive -// connections. +func (doh *dnsOverHTTPS) ResetConnection() { + doh.clientMu.Lock() + defer doh.clientMu.Unlock() + + if doh.client == nil { + return + } + + _ = doh.closeClient(doh.client) + doh.client = nil +} + +// closeClient cleans up resources used by client if necessary. func (doh *dnsOverHTTPS) closeClient(client *http.Client) (err error) { - if isHTTP3(client) { + client.CloseIdleConnections() + + if isHTTP3(client) { // HTTP/3 may leak due to keep-alive connections. return client.Transport.(io.Closer).Close() } @@ -508,6 +520,13 @@ func (h *http3Transport) Close() (err error) { return h.baseTransport.Close() } +func (h *http3Transport) CloseIdleConnections() { + h.mu.RLock() + defer h.mu.RUnlock() + + h.baseTransport.CloseIdleConnections() +} + // createTransportH3 tries to create an HTTP/3 transport for this upstream. // We should be able to fall back to H1/H2 in case if HTTP/3 is unavailable or // if it is too slow. In order to do that, this method will run two probes diff --git a/dns/doq.go b/dns/doq.go index ad936f95..29fdd006 100644 --- a/dns/doq.go +++ b/dns/doq.go @@ -144,6 +144,10 @@ func (doq *dnsOverQUIC) Close() (err error) { return err } +func (doq *dnsOverQUIC) ResetConnection() { + doq.closeConnWithError(nil) +} + // exchangeQUIC attempts to open a QUIC connection, send the DNS message // through it and return the response it got from the server. func (doq *dnsOverQUIC) exchangeQUIC(ctx context.Context, msg *D.Msg) (resp *D.Msg, err error) { diff --git a/dns/patch_android.go b/dns/patch_android.go index 6579ef07..e3dcd249 100644 --- a/dns/patch_android.go +++ b/dns/patch_android.go @@ -12,6 +12,7 @@ func FlushCacheWithDefaultResolver() { if r := resolver.DefaultResolver; r != nil { r.ClearCache() } + resolver.ResetConnection() } func UpdateSystemDNS(addr []string) { @@ -30,3 +31,9 @@ func UpdateSystemDNS(addr []string) { func (c *systemClient) getDnsClients() ([]dnsClient, error) { return systemResolver, nil } + +func (c *systemClient) ResetConnection() { + for _, r := range systemResolver { + r.ResetConnection() + } +} diff --git a/dns/rcode.go b/dns/rcode.go index 9777d2e7..901d1019 100644 --- a/dns/rcode.go +++ b/dns/rcode.go @@ -48,3 +48,5 @@ func (r rcodeClient) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, err func (r rcodeClient) Address() string { return r.addr } + +func (r rcodeClient) ResetConnection() {} diff --git a/dns/resolver.go b/dns/resolver.go index e03feef4..ec59f428 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -24,6 +24,7 @@ import ( type dnsClient interface { ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) Address() string + ResetConnection() } type dnsCache interface { @@ -48,7 +49,7 @@ type Resolver struct { group singleflight.Group[*D.Msg] cache dnsCache policy []dnsPolicy - proxyServer []dnsClient + defaultResolver *Resolver } func (r *Resolver) LookupIPPrimaryIPv4(ctx context.Context, host string) (ips []netip.Addr, err error) { @@ -376,6 +377,20 @@ func (r *Resolver) ClearCache() { } } +func (r *Resolver) ResetConnection() { + if r != nil { + for _, c := range r.main { + c.ResetConnection() + } + for _, c := range r.fallback { + c.ResetConnection() + } + if dr := r.defaultResolver; dr != nil { + dr.ResetConnection() + } + } +} + type NameServer struct { Net string Addr string @@ -425,16 +440,18 @@ type Config struct { CacheAlgorithm string } -func NewResolver(config Config) *Resolver { - var cache dnsCache - if config.CacheAlgorithm == "lru" { - cache = lru.New(lru.WithSize[string, *D.Msg](4096), lru.WithStale[string, *D.Msg](true)) +func (config Config) newCache() dnsCache { + if config.CacheAlgorithm == "" || config.CacheAlgorithm == "lru" { + return lru.New(lru.WithSize[string, *D.Msg](4096), lru.WithStale[string, *D.Msg](true)) } else { - cache = arc.New(arc.WithSize[string, *D.Msg](4096)) + return arc.New(arc.WithSize[string, *D.Msg](4096)) } +} + +func NewResolver(config Config) (r *Resolver, pr *Resolver) { defaultResolver := &Resolver{ main: transform(config.Default, nil), - cache: cache, + cache: config.newCache(), ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond, } @@ -465,27 +482,29 @@ func NewResolver(config Config) *Resolver { return } - if config.CacheAlgorithm == "" || config.CacheAlgorithm == "lru" { - cache = lru.New(lru.WithSize[string, *D.Msg](4096), lru.WithStale[string, *D.Msg](true)) - } else { - cache = arc.New(arc.WithSize[string, *D.Msg](4096)) - } - r := &Resolver{ + r = &Resolver{ ipv6: config.IPv6, main: cacheTransform(config.Main), - cache: cache, + cache: config.newCache(), hosts: config.Hosts, ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond, } + r.defaultResolver = defaultResolver + + if len(config.ProxyServer) != 0 { + pr = &Resolver{ + ipv6: config.IPv6, + main: cacheTransform(config.ProxyServer), + cache: config.newCache(), + hosts: config.Hosts, + ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond, + } + } if len(config.Fallback) != 0 { r.fallback = cacheTransform(config.Fallback) } - if len(config.ProxyServer) != 0 { - r.proxyServer = cacheTransform(config.ProxyServer) - } - if len(config.Policy) != 0 { r.policy = make([]dnsPolicy, 0) @@ -516,18 +535,7 @@ func NewResolver(config Config) *Resolver { r.fallbackIPFilters = config.FallbackIPFilter r.fallbackDomainFilters = config.FallbackDomainFilter - return r -} - -func NewProxyServerHostResolver(old *Resolver) *Resolver { - r := &Resolver{ - ipv6: old.ipv6, - main: old.proxyServer, - cache: old.cache, - hosts: old.hosts, - ipv6Timeout: old.ipv6Timeout, - } - return r + return } var ParseNameServer func(servers []string) ([]NameServer, error) // define in config/config.go diff --git a/dns/system_common.go b/dns/system_common.go index 06dc0b30..e6dabdcf 100644 --- a/dns/system_common.go +++ b/dns/system_common.go @@ -69,3 +69,5 @@ func (c *systemClient) getDnsClients() ([]dnsClient, error) { } return nil, err } + +func (c *systemClient) ResetConnection() {} diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 39bf28d2..66514e39 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -118,6 +118,8 @@ func ApplyConfig(cfg *config.Config, force bool) { tunnel.OnRunning() hcCompatibleProvider(cfg.Providers) initExternalUI() + + resolver.ResetConnection() } func initInnerTcp() { @@ -253,8 +255,7 @@ func updateDNS(c *config.DNS, generalIPv6 bool) { CacheAlgorithm: c.CacheAlgorithm, } - r := dns.NewResolver(cfg) - pr := dns.NewProxyServerHostResolver(r) + r, pr := dns.NewResolver(cfg) m := dns.NewEnhancer(cfg) // reuse cache of old host mapper diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index c2c668b3..79856c46 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -440,6 +440,10 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis //l.openAndroidHotspot(tunOptions) + if !l.options.AutoDetectInterface { + resolver.ResetConnection() + } + if options.FileDescriptor != 0 { tunName = fmt.Sprintf("%s(fd=%d)", tunName, options.FileDescriptor) } @@ -507,6 +511,7 @@ func (l *Listener) FlushDefaultInterface() { if old := dialer.DefaultInterface.Swap(autoDetectInterfaceName); old != autoDetectInterfaceName { log.Warnln("[TUN] default interface changed by monitor, %s => %s", old, autoDetectInterfaceName) iface.FlushCache() + resolver.ResetConnection() // reset resolver's connection after default interface changed } return } From af5ad3254b0ab8fef43c6870019f73761856f29f Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Fri, 27 Sep 2024 21:14:04 +0800 Subject: [PATCH 247/311] chore: Use DELETE to clear the proxy group fixed --- hub/route/proxies.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/hub/route/proxies.go b/hub/route/proxies.go index 69c8e446..9ff27c2d 100644 --- a/hub/route/proxies.go +++ b/hub/route/proxies.go @@ -31,6 +31,7 @@ func proxyRouter() http.Handler { r.Get("/", getProxy) r.Get("/delay", getProxyDelay) r.Put("/", updateProxy) + r.Delete("/", unfixedProxy) }) return r } @@ -146,3 +147,27 @@ func getProxyDelay(w http.ResponseWriter, r *http.Request) { "delay": delay, }) } + +func unfixedProxy(w http.ResponseWriter, r *http.Request) { + proxy := r.Context().Value(CtxKeyProxy).(C.Proxy) + switch proxy.(*adapter.Proxy).Type() { + case C.URLTest: + if urlTestGroup, ok := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.URLTest); ok { + urlTestGroup.ForceSet("") + } + case C.Fallback: + if fallbackGroup, ok := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.Fallback); ok { + fallbackGroup.ForceSet("") + } + default: + render.Status(r, http.StatusBadRequest) + render.JSON(w, r, ErrBadRequest) + return + } + + if proxy.(*adapter.Proxy).Type() != C.Selector { + cachefile.Cache().SetSelected(proxy.Name(), "") + } + + render.NoContent(w, r) +} From a67c3798844901a5206a7af3c2aa64efb757f55b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 27 Sep 2024 21:33:37 +0800 Subject: [PATCH 248/311] chore: code cleanup --- adapter/adapter.go | 5 +++++ adapter/outboundgroup/util.go | 4 ++++ constant/adapters.go | 1 + hub/executor/executor.go | 4 ++-- hub/route/groups.go | 21 +++++---------------- hub/route/proxies.go | 29 ++++++++--------------------- 6 files changed, 25 insertions(+), 39 deletions(-) diff --git a/adapter/adapter.go b/adapter/adapter.go index 8136827a..3efc8166 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -39,6 +39,11 @@ type Proxy struct { extra *xsync.MapOf[string, *internalProxyState] } +// Adapter implements C.Proxy +func (p *Proxy) Adapter() C.ProxyAdapter { + return p.ProxyAdapter +} + // AliveForTestUrl implements C.Proxy func (p *Proxy) AliveForTestUrl(url string) bool { if state, ok := p.extra.Load(url); ok { diff --git a/adapter/outboundgroup/util.go b/adapter/outboundgroup/util.go index 84216377..66b2510c 100644 --- a/adapter/outboundgroup/util.go +++ b/adapter/outboundgroup/util.go @@ -4,3 +4,7 @@ type SelectAble interface { Set(string) error ForceSet(name string) } + +var _ SelectAble = (*Fallback)(nil) +var _ SelectAble = (*URLTest)(nil) +var _ SelectAble = (*Selector)(nil) diff --git a/constant/adapters.go b/constant/adapters.go index cb47f871..b303eb84 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -158,6 +158,7 @@ type DelayHistoryStoreType int type Proxy interface { ProxyAdapter + Adapter() ProxyAdapter AliveForTestUrl(url string) bool DelayHistory() []DelayHistory ExtraDelayHistories() map[string]ProxyState diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 66514e39..214407b4 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -445,12 +445,12 @@ func patchSelectGroup(proxies map[string]C.Proxy) { } for name, proxy := range proxies { - outbound, ok := proxy.(*adapter.Proxy) + outbound, ok := proxy.(C.Proxy) if !ok { continue } - selector, ok := outbound.ProxyAdapter.(outboundgroup.SelectAble) + selector, ok := outbound.Adapter().(outboundgroup.SelectAble) if !ok { continue } diff --git a/hub/route/groups.go b/hub/route/groups.go index c4e9501f..68d1f354 100644 --- a/hub/route/groups.go +++ b/hub/route/groups.go @@ -9,7 +9,6 @@ import ( "github.com/go-chi/chi/v5" "github.com/go-chi/render" - "github.com/metacubex/mihomo/adapter" "github.com/metacubex/mihomo/adapter/outboundgroup" "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/profile/cachefile" @@ -32,7 +31,7 @@ func GroupRouter() http.Handler { func getGroups(w http.ResponseWriter, r *http.Request) { var gs []C.Proxy for _, p := range tunnel.Proxies() { - if _, ok := p.(*adapter.Proxy).ProxyAdapter.(C.Group); ok { + if _, ok := p.Adapter().(C.Group); ok { gs = append(gs, p) } } @@ -43,7 +42,7 @@ func getGroups(w http.ResponseWriter, r *http.Request) { func getGroup(w http.ResponseWriter, r *http.Request) { proxy := r.Context().Value(CtxKeyProxy).(C.Proxy) - if _, ok := proxy.(*adapter.Proxy).ProxyAdapter.(C.Group); ok { + if _, ok := proxy.Adapter().(C.Group); ok { render.JSON(w, r, proxy) return } @@ -53,25 +52,15 @@ func getGroup(w http.ResponseWriter, r *http.Request) { func getGroupDelay(w http.ResponseWriter, r *http.Request) { proxy := r.Context().Value(CtxKeyProxy).(C.Proxy) - group, ok := proxy.(*adapter.Proxy).ProxyAdapter.(C.Group) + group, ok := proxy.Adapter().(C.Group) if !ok { render.Status(r, http.StatusNotFound) render.JSON(w, r, ErrNotFound) return } - switch proxy.(*adapter.Proxy).Type() { - case C.URLTest: - if urlTestGroup, ok := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.URLTest); ok { - urlTestGroup.ForceSet("") - } - case C.Fallback: - if fallbackGroup, ok := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.Fallback); ok { - fallbackGroup.ForceSet("") - } - } - - if proxy.(*adapter.Proxy).Type() != C.Selector { + if selectAble, ok := proxy.Adapter().(outboundgroup.SelectAble); ok && proxy.Type() != C.Selector { + selectAble.ForceSet("") cachefile.Cache().SetSelected(proxy.Name(), "") } diff --git a/hub/route/proxies.go b/hub/route/proxies.go index 9ff27c2d..ba4e03f9 100644 --- a/hub/route/proxies.go +++ b/hub/route/proxies.go @@ -7,7 +7,6 @@ import ( "strconv" "time" - "github.com/metacubex/mihomo/adapter" "github.com/metacubex/mihomo/adapter/outboundgroup" "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/profile/cachefile" @@ -82,8 +81,8 @@ func updateProxy(w http.ResponseWriter, r *http.Request) { return } - proxy := r.Context().Value(CtxKeyProxy).(*adapter.Proxy) - selector, ok := proxy.ProxyAdapter.(outboundgroup.SelectAble) + proxy := r.Context().Value(CtxKeyProxy).(C.Proxy) + selector, ok := proxy.Adapter().(outboundgroup.SelectAble) if !ok { render.Status(r, http.StatusBadRequest) render.JSON(w, r, newError("Must be a Selector")) @@ -150,24 +149,12 @@ func getProxyDelay(w http.ResponseWriter, r *http.Request) { func unfixedProxy(w http.ResponseWriter, r *http.Request) { proxy := r.Context().Value(CtxKeyProxy).(C.Proxy) - switch proxy.(*adapter.Proxy).Type() { - case C.URLTest: - if urlTestGroup, ok := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.URLTest); ok { - urlTestGroup.ForceSet("") - } - case C.Fallback: - if fallbackGroup, ok := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.Fallback); ok { - fallbackGroup.ForceSet("") - } - default: - render.Status(r, http.StatusBadRequest) - render.JSON(w, r, ErrBadRequest) + if selectAble, ok := proxy.Adapter().(outboundgroup.SelectAble); ok && proxy.Type() != C.Selector { + selectAble.ForceSet("") + cachefile.Cache().SetSelected(proxy.Name(), "") + render.NoContent(w, r) return } - - if proxy.(*adapter.Proxy).Type() != C.Selector { - cachefile.Cache().SetSelected(proxy.Name(), "") - } - - render.NoContent(w, r) + render.Status(r, http.StatusBadRequest) + render.JSON(w, r, ErrBadRequest) } From 264713571d349d214ec938911de0de033b8673af Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 27 Sep 2024 22:36:19 +0800 Subject: [PATCH 249/311] chore: set 0o666 to unix socket file --- hub/route/server.go | 1 + 1 file changed, 1 insertion(+) diff --git a/hub/route/server.go b/hub/route/server.go index 4c22609c..1810a707 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -228,6 +228,7 @@ func startUnix(cfg *Config) { log.Errorln("External controller unix listen error: %s", err) return } + _ = os.Chmod(addr, 0o666) log.Infoln("RESTful API unix listening at: %s", l.Addr().String()) server := &http.Server{ From fc9d5cfee944a75b989d17c637a321e73c52093a Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 29 Sep 2024 17:13:43 +0800 Subject: [PATCH 250/311] feat: add `external-controller-cors` can config `allow-origins` and `allow-private-network` --- config/config.go | 20 ++++++++++++++++++++ docs/config.yaml | 6 ++++++ go.mod | 2 +- go.sum | 4 ++-- hub/hub.go | 4 ++++ hub/route/server.go | 46 ++++++++++++++++++++++----------------------- 6 files changed, 56 insertions(+), 26 deletions(-) diff --git a/config/config.go b/config/config.go index 9067d14f..3117853c 100644 --- a/config/config.go +++ b/config/config.go @@ -107,6 +107,12 @@ type Controller struct { ExternalUI string ExternalDohServer string Secret string + Cors Cors +} + +type Cors struct { + AllowOrigins []string + AllowPrivateNetwork bool } // Experimental config @@ -191,6 +197,11 @@ type Config struct { TLS *TLS } +type RawCors struct { + AllowOrigins []string `yaml:"allow-origins" json:"allow-origins"` + AllowPrivateNetwork bool `yaml:"allow-private-network" json:"allow-private-network"` +} + type RawDNS struct { Enable bool `yaml:"enable" json:"enable"` PreferH3 bool `yaml:"prefer-h3" json:"prefer-h3"` @@ -368,6 +379,7 @@ type RawConfig struct { ExternalControllerPipe string `yaml:"external-controller-pipe" json:"external-controller-pipe"` ExternalControllerUnix string `yaml:"external-controller-unix" json:"external-controller-unix"` ExternalControllerTLS string `yaml:"external-controller-tls" json:"external-controller-tls"` + ExternalControllerCors RawCors `yaml:"external-controller-cors" json:"external-controller-cors"` ExternalUI string `yaml:"external-ui" json:"external-ui"` ExternalUIURL string `yaml:"external-ui-url" json:"external-ui-url"` ExternalUIName string `yaml:"external-ui-name" json:"external-ui-name"` @@ -541,6 +553,10 @@ func DefaultRawConfig() *RawConfig { OverrideDest: true, }, ExternalUIURL: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip", + ExternalControllerCors: RawCors{ + AllowOrigins: []string{"*"}, + AllowPrivateNetwork: true, + }, } } @@ -775,6 +791,10 @@ func parseController(cfg *RawConfig) (*Controller, error) { ExternalControllerUnix: cfg.ExternalControllerUnix, ExternalControllerTLS: cfg.ExternalControllerTLS, ExternalDohServer: cfg.ExternalDohServer, + Cors: Cors{ + AllowOrigins: cfg.ExternalControllerCors.AllowOrigins, + AllowPrivateNetwork: cfg.ExternalControllerCors.AllowPrivateNetwork, + }, }, nil } diff --git a/docs/config.yaml b/docs/config.yaml index 9c480b3f..15bcf607 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -58,6 +58,12 @@ external-controller: 0.0.0.0:9093 # RESTful API 监听地址 external-controller-tls: 0.0.0.0:9443 # RESTful API HTTPS 监听地址,需要配置 tls 部分配置文件 # secret: "123456" # `Authorization:Bearer ${secret}` +# RESTful API CORS标头配置 +external-controller-cors: + allow-origins: + - * + allow-private-network: true + # RESTful API Unix socket 监听地址( windows版本大于17063也可以使用,即大于等于1803/RS4版本即可使用 ) # !!!注意: 从Unix socket访问api接口不会验证secret, 如果开启请自行保证安全问题 !!! # 测试方法: curl -v --unix-socket "mihomo.sock" http://localhost/ diff --git a/go.mod b/go.mod index b74cee92..4b066c90 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/coreos/go-iptables v0.7.0 github.com/dlclark/regexp2 v1.11.4 github.com/go-chi/chi/v5 v5.1.0 - github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.3 github.com/gobwas/ws v1.4.0 github.com/gofrs/uuid/v5 v5.3.0 @@ -37,6 +36,7 @@ require ( github.com/openacid/low v0.1.21 github.com/oschwald/maxminddb-golang v1.12.0 github.com/puzpuzpuz/xsync/v3 v3.4.0 + github.com/sagernet/cors v1.2.1 github.com/sagernet/fswatch v0.1.1 github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a github.com/sagernet/sing v0.5.0-alpha.13 diff --git a/go.sum b/go.sum index ba2d1819..60818034 100644 --- a/go.sum +++ b/go.sum @@ -42,8 +42,6 @@ github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXb github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI= github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= -github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= 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= @@ -160,6 +158,8 @@ 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.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ= +github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI= github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs= github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o= github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis= diff --git a/hub/hub.go b/hub/hub.go index 73a44eee..69f627ff 100644 --- a/hub/hub.go +++ b/hub/hub.go @@ -59,6 +59,10 @@ func applyRoute(cfg *config.Config) { PrivateKey: cfg.TLS.PrivateKey, DohServer: cfg.Controller.ExternalDohServer, IsDebug: cfg.General.LogLevel == log.DEBUG, + Cors: route.Cors{ + AllowOrigins: cfg.Controller.Cors.AllowOrigins, + AllowPrivateNetwork: cfg.Controller.Cors.AllowPrivateNetwork, + }, }) } diff --git a/hub/route/server.go b/hub/route/server.go index 1810a707..e71347fc 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -23,10 +23,10 @@ import ( "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" - "github.com/go-chi/cors" "github.com/go-chi/render" "github.com/gobwas/ws" "github.com/gobwas/ws/wsutil" + "github.com/sagernet/cors" ) var ( @@ -58,6 +58,22 @@ type Config struct { PrivateKey string DohServer string IsDebug bool + Cors Cors +} + +type Cors struct { + AllowOrigins []string + AllowPrivateNetwork bool +} + +func (c Cors) Apply(r chi.Router) { + r.Use(cors.New(cors.Options{ + AllowedOrigins: c.AllowOrigins, + AllowedMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE"}, + AllowedHeaders: []string{"Content-Type", "Authorization"}, + AllowPrivateNetwork: c.AllowPrivateNetwork, + MaxAge: 300, + }).Handler) } func ReCreateServer(cfg *Config) { @@ -73,16 +89,9 @@ func SetUIPath(path string) { uiPath = C.Path.Resolve(path) } -func router(isDebug bool, secret string, dohServer string) *chi.Mux { +func router(isDebug bool, secret string, dohServer string, cors Cors) *chi.Mux { r := chi.NewRouter() - corsM := cors.New(cors.Options{ - AllowedOrigins: []string{"*"}, - AllowedMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE"}, - AllowedHeaders: []string{"Content-Type", "Authorization"}, - MaxAge: 300, - }) - r.Use(setPrivateNetworkAccess) - r.Use(corsM.Handler) + cors.Apply(r) if isDebug { r.Mount("/debug", func() http.Handler { r := chi.NewRouter() @@ -151,7 +160,7 @@ func start(cfg *Config) { log.Infoln("RESTful API listening at: %s", l.Addr().String()) server := &http.Server{ - Handler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer), + Handler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer, cfg.Cors), } httpServer = server if err = server.Serve(l); err != nil { @@ -183,7 +192,7 @@ func startTLS(cfg *Config) { log.Infoln("RESTful API tls listening at: %s", l.Addr().String()) server := &http.Server{ - Handler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer), + Handler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer, cfg.Cors), TLSConfig: &tls.Config{ Certificates: []tls.Certificate{c}, }, @@ -232,7 +241,7 @@ func startUnix(cfg *Config) { log.Infoln("RESTful API unix listening at: %s", l.Addr().String()) server := &http.Server{ - Handler: router(cfg.IsDebug, "", cfg.DohServer), + Handler: router(cfg.IsDebug, "", cfg.DohServer, cfg.Cors), } unixServer = server if err = server.Serve(l); err != nil { @@ -263,7 +272,7 @@ func startPipe(cfg *Config) { log.Infoln("RESTful API pipe listening at: %s", l.Addr().String()) server := &http.Server{ - Handler: router(cfg.IsDebug, "", cfg.DohServer), + Handler: router(cfg.IsDebug, "", cfg.DohServer, cfg.Cors), } pipeServer = server if err = server.Serve(l); err != nil { @@ -272,15 +281,6 @@ func startPipe(cfg *Config) { } } -func setPrivateNetworkAccess(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" { - w.Header().Add("Access-Control-Allow-Private-Network", "true") - } - next.ServeHTTP(w, r) - }) -} - func safeEuqal(a, b string) bool { aBuf := utils.ImmutableBytesFromString(a) bBuf := utils.ImmutableBytesFromString(b) From a330fa1506f84841d0f80724d12fa4add5205328 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 30 Sep 2024 13:08:50 +0800 Subject: [PATCH 251/311] chore: disallow some restful api for CMFA --- hub/route/configs.go | 8 +++++--- hub/route/groups.go | 2 +- hub/route/patch_android.go | 7 +++++++ hub/route/server.go | 14 +++++++++++--- 4 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 hub/route/patch_android.go diff --git a/hub/route/configs.go b/hub/route/configs.go index d4bda2bf..b23a35a5 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -24,9 +24,11 @@ import ( func configRouter() http.Handler { r := chi.NewRouter() r.Get("/", getConfigs) - r.Put("/", updateConfigs) - r.Post("/geo", updateGeoDatabases) - r.Patch("/", patchConfigs) + if !embedMode { // disallow update/patch configs in embed mode + r.Put("/", updateConfigs) + r.Post("/geo", updateGeoDatabases) + r.Patch("/", patchConfigs) + } return r } diff --git a/hub/route/groups.go b/hub/route/groups.go index 68d1f354..873a94df 100644 --- a/hub/route/groups.go +++ b/hub/route/groups.go @@ -16,7 +16,7 @@ import ( "github.com/metacubex/mihomo/tunnel" ) -func GroupRouter() http.Handler { +func groupRouter() http.Handler { r := chi.NewRouter() r.Get("/", getGroups) diff --git a/hub/route/patch_android.go b/hub/route/patch_android.go new file mode 100644 index 00000000..5eba2796 --- /dev/null +++ b/hub/route/patch_android.go @@ -0,0 +1,7 @@ +//go:build android && cmfa + +package route + +func init() { + SetEmbedMode(true) // set embed mode default +} diff --git a/hub/route/server.go b/hub/route/server.go index e71347fc..21102cc3 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -36,8 +36,14 @@ var ( tlsServer *http.Server unixServer *http.Server pipeServer *http.Server + + embedMode = false ) +func SetEmbedMode(embed bool) { + embedMode = embed +} + type Traffic struct { Up int64 `json:"up"` Down int64 `json:"down"` @@ -114,15 +120,17 @@ func router(isDebug bool, secret string, dohServer string, cors Cors) *chi.Mux { r.Get("/version", version) r.Mount("/configs", configRouter()) r.Mount("/proxies", proxyRouter()) - r.Mount("/group", GroupRouter()) + r.Mount("/group", groupRouter()) r.Mount("/rules", ruleRouter()) r.Mount("/connections", connectionRouter()) r.Mount("/providers/proxies", proxyProviderRouter()) r.Mount("/providers/rules", ruleProviderRouter()) r.Mount("/cache", cacheRouter()) r.Mount("/dns", dnsRouter()) - r.Mount("/restart", restartRouter()) - r.Mount("/upgrade", upgradeRouter()) + if !embedMode { // disallow restart and upgrade in embed mode + r.Mount("/restart", restartRouter()) + r.Mount("/upgrade", upgradeRouter()) + } addExternalRouters(r) }) From ecd8facd81a1f53f7e3c74cfe6d81dd4655eb4fb Mon Sep 17 00:00:00 2001 From: Skyxim Date: Tue, 1 Oct 2024 03:14:37 +0000 Subject: [PATCH 252/311] chore: add warning for unified delay test when second failed --- adapter/adapter.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/adapter/adapter.go b/adapter/adapter.go index 3efc8166..6de7fb92 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -10,6 +10,7 @@ import ( "net/netip" "net/url" "strconv" + "strings" "time" "github.com/metacubex/mihomo/common/atomic" @@ -18,6 +19,7 @@ import ( "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/dialer" C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/log" "github.com/puzpuzpuz/xsync/v3" ) @@ -260,10 +262,18 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In if unifiedDelay { second := time.Now() - resp, err = client.Do(req) - if err == nil { + var ignoredErr error + var secondResp *http.Response + secondResp, ignoredErr = client.Do(req) + if ignoredErr == nil { + resp = secondResp _ = resp.Body.Close() start = second + } else { + if strings.HasPrefix(url, "http://") { + log.Errorln("%s failed to get the second response from %s: %v", p.Name(), url, ignoredErr) + log.Warnln("It is recommended to use HTTPS for provider.health-check.url and group.url to ensure better reliability. Due to some proxy providers hijacking test addresses and not being compatible with repeated HEAD requests, using HTTP may result in failed tests.") + } } } From 990de84391f1883242d01c81208a74db23e9e915 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 2 Oct 2024 14:34:38 +0800 Subject: [PATCH 253/311] chore: better atomic using --- tunnel/statistic/manager.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tunnel/statistic/manager.go b/tunnel/statistic/manager.go index 08747118..0b309299 100644 --- a/tunnel/statistic/manager.go +++ b/tunnel/statistic/manager.go @@ -114,10 +114,8 @@ func (m *Manager) handle() { ticker := time.NewTicker(time.Second) for range ticker.C { - m.uploadBlip.Store(m.uploadTemp.Load()) - m.uploadTemp.Store(0) - m.downloadBlip.Store(m.downloadTemp.Load()) - m.downloadTemp.Store(0) + m.uploadBlip.Store(m.uploadTemp.Swap(0)) + m.downloadBlip.Store(m.downloadTemp.Swap(0)) } } From 4a16d22398116a88a230071278be536491a8cdce Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 2 Oct 2024 14:44:52 +0800 Subject: [PATCH 254/311] chore: no longer used net.DefaultResolver when `dns` section is disabled, now is equally only "system://" --- adapter/outbound/hysteria.go | 8 ++++---- component/dialer/resolver.go | 29 ---------------------------- component/resolver/defaults.go | 12 ------------ component/resolver/host_windows.go | 19 ------------------ component/resolver/resolver.go | 31 ++++++------------------------ dns/patch_android.go | 6 ++++++ dns/resolver.go | 2 -- dns/system.go | 17 +++++++++++++++- hub/executor/executor.go | 1 - main.go | 8 ++++++++ 10 files changed, 40 insertions(+), 93 deletions(-) delete mode 100644 component/dialer/resolver.go delete mode 100644 component/resolver/defaults.go delete mode 100644 component/resolver/host_windows.go diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index ccab16c1..a7367b83 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -69,7 +69,7 @@ func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata func (h *Hysteria) genHdc(ctx context.Context, opts ...dialer.Option) utils.PacketDialer { return &hyDialerWithContext{ ctx: context.Background(), - hyDialer: func(network string) (net.PacketConn, error) { + hyDialer: func(network string, rAddr net.Addr) (net.PacketConn, error) { var err error var cDialer C.Dialer = dialer.NewDialer(h.Base.DialOptions(opts...)...) if len(h.option.DialerProxy) > 0 { @@ -78,7 +78,7 @@ func (h *Hysteria) genHdc(ctx context.Context, opts ...dialer.Option) utils.Pack return nil, err } } - rAddrPort, _ := netip.ParseAddrPort(h.Addr()) + rAddrPort, _ := netip.ParseAddrPort(rAddr.String()) return cDialer.ListenPacket(ctx, network, "", rAddrPort) }, remoteAddr: func(addr string) (net.Addr, error) { @@ -284,7 +284,7 @@ func (c *hyPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { } type hyDialerWithContext struct { - hyDialer func(network string) (net.PacketConn, error) + hyDialer func(network string, rAddr net.Addr) (net.PacketConn, error) ctx context.Context remoteAddr func(host string) (net.Addr, error) } @@ -294,7 +294,7 @@ func (h *hyDialerWithContext) ListenPacket(rAddr net.Addr) (net.PacketConn, erro if addrPort, err := netip.ParseAddrPort(rAddr.String()); err == nil { network = dialer.ParseNetwork(network, addrPort.Addr()) } - return h.hyDialer(network) + return h.hyDialer(network, rAddr) } func (h *hyDialerWithContext) Context() context.Context { diff --git a/component/dialer/resolver.go b/component/dialer/resolver.go deleted file mode 100644 index ea38a90e..00000000 --- a/component/dialer/resolver.go +++ /dev/null @@ -1,29 +0,0 @@ -package dialer - -import ( - "context" - "net" - "net/netip" -) - -func init() { - // We must use this DialContext to query DNS - // when using net default resolver. - net.DefaultResolver.PreferGo = true - net.DefaultResolver.Dial = resolverDialContext -} - -func resolverDialContext(ctx context.Context, network, address string) (net.Conn, error) { - d := &net.Dialer{} - - interfaceName := DefaultInterface.Load() - - if interfaceName != "" { - dstIP, err := netip.ParseAddr(address) - if err == nil { - _ = bindIfaceToDialer(interfaceName, d, network, dstIP) - } - } - - return d.DialContext(ctx, network, address) -} diff --git a/component/resolver/defaults.go b/component/resolver/defaults.go deleted file mode 100644 index 8a04bd17..00000000 --- a/component/resolver/defaults.go +++ /dev/null @@ -1,12 +0,0 @@ -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris - -package resolver - -import _ "unsafe" - -//go:linkname defaultNS net.defaultNS -var defaultNS []string - -func init() { - defaultNS = []string{"114.114.114.114:53", "8.8.8.8:53"} -} diff --git a/component/resolver/host_windows.go b/component/resolver/host_windows.go deleted file mode 100644 index 669f9547..00000000 --- a/component/resolver/host_windows.go +++ /dev/null @@ -1,19 +0,0 @@ -//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" - } -} diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index bcdbb7e2..8b180c1e 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "net" "net/netip" "strings" "time" @@ -23,6 +22,9 @@ var ( // ProxyServerHostResolver resolve ip to proxies server host ProxyServerHostResolver Resolver + // SystemResolver always using system dns, and was init in dns module + SystemResolver Resolver + // DisableIPv6 means don't resolve ipv6 host // default value is true DisableIPv6 = true @@ -72,14 +74,7 @@ func LookupIPv4WithResolver(ctx context.Context, host string, r Resolver) ([]net return r.LookupIPv4(ctx, host) } - ipAddrs, err := net.DefaultResolver.LookupNetIP(ctx, "ip4", host) - if err != nil { - return nil, err - } else if len(ipAddrs) == 0 { - return nil, ErrIPNotFound - } - - return ipAddrs, nil + return SystemResolver.LookupIPv4(ctx, host) } // LookupIPv4 with a host, return ipv4 list @@ -128,14 +123,7 @@ func LookupIPv6WithResolver(ctx context.Context, host string, r Resolver) ([]net return r.LookupIPv6(ctx, host) } - ipAddrs, err := net.DefaultResolver.LookupNetIP(ctx, "ip6", host) - if err != nil { - return nil, err - } else if len(ipAddrs) == 0 { - return nil, ErrIPNotFound - } - - return ipAddrs, nil + return SystemResolver.LookupIPv6(ctx, host) } // LookupIPv6 with a host, return ipv6 list @@ -177,14 +165,7 @@ func LookupIPWithResolver(ctx context.Context, host string, r Resolver) ([]netip return []netip.Addr{ip}, nil } - ips, err := net.DefaultResolver.LookupNetIP(ctx, "ip", host) - if err != nil { - return nil, err - } else if len(ips) == 0 { - return nil, ErrIPNotFound - } - - return ips, nil + return SystemResolver.LookupIP(ctx, host) } // LookupIP with a host, return ip diff --git a/dns/patch_android.go b/dns/patch_android.go index e3dcd249..5a98e86c 100644 --- a/dns/patch_android.go +++ b/dns/patch_android.go @@ -12,6 +12,12 @@ func FlushCacheWithDefaultResolver() { if r := resolver.DefaultResolver; r != nil { r.ClearCache() } + if r := resolver.ProxyServerHostResolver; r != nil { + r.ClearCache() + } + if r := resolver.SystemResolver; r != nil { + r.ClearCache() + } resolver.ResetConnection() } diff --git a/dns/resolver.go b/dns/resolver.go index ec59f428..ea8888cc 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -13,7 +13,6 @@ import ( "github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/component/trie" C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/constant/provider" "github.com/metacubex/mihomo/log" D "github.com/miekg/dns" @@ -436,7 +435,6 @@ type Config struct { Pool *fakeip.Pool Hosts *trie.DomainTrie[resolver.HostValue] Policy []Policy - Tunnel provider.Tunnel CacheAlgorithm string } diff --git a/dns/system.go b/dns/system.go index 944f2824..f05cb0f3 100644 --- a/dns/system.go +++ b/dns/system.go @@ -7,6 +7,8 @@ import ( "sync" "time" + "github.com/metacubex/mihomo/component/resolver" + D "github.com/miekg/dns" ) @@ -24,12 +26,17 @@ type systemClient struct { mu sync.Mutex dnsClients map[string]*systemDnsClient lastFlush time.Time + defaultNS []dnsClient } func (c *systemClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { dnsClients, err := c.getDnsClients() if err != nil { - return + if len(c.defaultNS) > 0 { + dnsClients = c.defaultNS + } else { + return + } } msg, _, err = batchExchange(ctx, dnsClients, m) return @@ -52,3 +59,11 @@ func newSystemClient() *systemClient { dnsClients: map[string]*systemDnsClient{}, } } + +func init() { + r, _ := NewResolver(Config{}) + c := newSystemClient() + c.defaultNS = transform([]NameServer{{Addr: "114.114.114.114:53"}, {Addr: "8.8.8.8:53"}}, nil) + r.main = []dnsClient{c} + resolver.SystemResolver = r +} diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 214407b4..10ea21b0 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -251,7 +251,6 @@ func updateDNS(c *config.DNS, generalIPv6 bool) { Default: c.DefaultNameserver, Policy: c.NameServerPolicy, ProxyServer: c.ProxyServerNameserver, - Tunnel: tunnel.Tunnel, CacheAlgorithm: c.CacheAlgorithm, } diff --git a/main.go b/main.go index 505cdb25..685fc89f 100644 --- a/main.go +++ b/main.go @@ -1,9 +1,11 @@ package main import ( + "context" "encoding/base64" "flag" "fmt" + "net" "os" "os/signal" "path/filepath" @@ -55,6 +57,12 @@ func init() { } func main() { + // Defensive programming: panic when code mistakenly calls net.DefaultResolver + net.DefaultResolver.PreferGo = true + net.DefaultResolver.Dial = func(ctx context.Context, network, address string) (net.Conn, error) { + panic("should never be called") + } + _, _ = maxprocs.Set(maxprocs.Logger(func(string, ...any) {})) if len(os.Args) > 1 && os.Args[1] == "convert-ruleset" { From c63a851bba09856adfc60f2a856eedc56b500d62 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 4 Oct 2024 13:19:41 +0800 Subject: [PATCH 255/311] feat: add `direct-nameserver` and `direct-nameserver-follow-policy` in `dns` section --- adapter/outbound/direct.go | 4 +-- adapter/outbound/util.go | 10 +++---- adapter/outbound/wireguard.go | 4 +-- component/dialer/dialer.go | 22 +++++--------- component/resolver/resolver.go | 53 +++------------------------------- config/config.go | 47 ++++++++++++++++++------------ dns/patch_android.go | 3 -- dns/resolver.go | 45 +++++++++++++++++++++++++---- dns/system.go | 2 +- docs/config.yaml | 11 +++++-- hub/executor/executor.go | 22 ++++++++++---- 11 files changed, 114 insertions(+), 109 deletions(-) diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index 15f081f2..9ee237fa 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -32,7 +32,7 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ... return nil, err } } - opts = append(opts, dialer.WithResolver(resolver.DefaultResolver)) + opts = append(opts, dialer.WithResolver(resolver.DirectHostResolver)) c, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress(), d.Base.DialOptions(opts...)...) if err != nil { return nil, err @@ -49,7 +49,7 @@ func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, } // net.UDPConn.WriteTo only working with *net.UDPAddr, so we need a net.UDPAddr if !metadata.Resolved() { - ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, resolver.DefaultResolver) + ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, resolver.DirectHostResolver) if err != nil { return nil, errors.New("can't resolve ip") } diff --git a/adapter/outbound/util.go b/adapter/outbound/util.go index 2c85c7c8..9f0636a6 100644 --- a/adapter/outbound/util.go +++ b/adapter/outbound/util.go @@ -55,7 +55,7 @@ func resolveUDPAddr(ctx context.Context, network, address string) (*net.UDPAddr, return nil, err } - ip, err := resolver.ResolveProxyServerHost(ctx, host) + ip, err := resolver.ResolveIPWithResolver(ctx, host, resolver.ProxyServerHostResolver) if err != nil { return nil, err } @@ -71,12 +71,12 @@ func resolveUDPAddrWithPrefer(ctx context.Context, network, address string, pref var fallback netip.Addr switch prefer { case C.IPv4Only: - ip, err = resolver.ResolveIPv4ProxyServerHost(ctx, host) + ip, err = resolver.ResolveIPv4WithResolver(ctx, host, resolver.ProxyServerHostResolver) case C.IPv6Only: - ip, err = resolver.ResolveIPv6ProxyServerHost(ctx, host) + ip, err = resolver.ResolveIPv6WithResolver(ctx, host, resolver.ProxyServerHostResolver) case C.IPv6Prefer: var ips []netip.Addr - ips, err = resolver.LookupIPProxyServerHost(ctx, host) + ips, err = resolver.LookupIPWithResolver(ctx, host, resolver.ProxyServerHostResolver) if err == nil { for _, addr := range ips { if addr.Is6() { @@ -92,7 +92,7 @@ func resolveUDPAddrWithPrefer(ctx context.Context, network, address string, pref default: // C.IPv4Prefer, C.DualStack and other var ips []netip.Addr - ips, err = resolver.LookupIPProxyServerHost(ctx, host) + ips, err = resolver.LookupIPWithResolver(ctx, host, resolver.ProxyServerHostResolver) if err == nil { for _, addr := range ips { if addr.Is4() { diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 3928ab1b..03145c37 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -44,7 +44,7 @@ type WireGuard struct { device wireguardGoDevice tunDevice wireguard.Device dialer proxydialer.SingDialer - resolver *dns.Resolver + resolver resolver.Resolver refP *refProxyAdapter initOk atomic.Bool @@ -296,7 +296,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { for i := range nss { nss[i].ProxyAdapter = refP } - outbound.resolver, _ = dns.NewResolver(dns.Config{ + outbound.resolver = dns.NewResolver(dns.Config{ Main: nss, IPv6: has6, }) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 3dfd3159..4fd051ef 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -340,26 +340,18 @@ func parseAddr(ctx context.Context, network, address string, preferResolver reso return nil, "-1", err } + if preferResolver == nil { + preferResolver = resolver.ProxyServerHostResolver + } + var ips []netip.Addr switch network { case "tcp4", "udp4": - if preferResolver == nil { - ips, err = resolver.LookupIPv4ProxyServerHost(ctx, host) - } else { - ips, err = resolver.LookupIPv4WithResolver(ctx, host, preferResolver) - } + ips, err = resolver.LookupIPv4WithResolver(ctx, host, preferResolver) case "tcp6", "udp6": - if preferResolver == nil { - ips, err = resolver.LookupIPv6ProxyServerHost(ctx, host) - } else { - ips, err = resolver.LookupIPv6WithResolver(ctx, host, preferResolver) - } + ips, err = resolver.LookupIPv6WithResolver(ctx, host, preferResolver) default: - if preferResolver == nil { - ips, err = resolver.LookupIPProxyServerHost(ctx, host) - } else { - ips, err = resolver.LookupIPWithResolver(ctx, host, preferResolver) - } + ips, err = resolver.LookupIPWithResolver(ctx, host, preferResolver) } if err != nil { return nil, "-1", fmt.Errorf("dns resolve failed: %w", err) diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index 8b180c1e..1eb3d642 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -19,9 +19,12 @@ var ( // DefaultResolver aim to resolve ip DefaultResolver Resolver - // ProxyServerHostResolver resolve ip to proxies server host + // ProxyServerHostResolver resolve ip for proxies server host, only nil when DefaultResolver is nil ProxyServerHostResolver Resolver + // DirectHostResolver resolve ip for direct outbound host, only nil when DefaultResolver is nil + DirectHostResolver Resolver + // SystemResolver always using system dns, and was init in dns module SystemResolver Resolver @@ -193,58 +196,10 @@ func ResolveIP(ctx context.Context, host string) (netip.Addr, error) { return ResolveIPWithResolver(ctx, host, DefaultResolver) } -// ResolveIPv4ProxyServerHost proxies server host only -func ResolveIPv4ProxyServerHost(ctx context.Context, host string) (netip.Addr, error) { - if ProxyServerHostResolver != nil { - return ResolveIPv4WithResolver(ctx, host, ProxyServerHostResolver) - } - return ResolveIPv4(ctx, host) -} - -// ResolveIPv6ProxyServerHost proxies server host only -func ResolveIPv6ProxyServerHost(ctx context.Context, host string) (netip.Addr, error) { - if ProxyServerHostResolver != nil { - return ResolveIPv6WithResolver(ctx, host, ProxyServerHostResolver) - } - return ResolveIPv6(ctx, host) -} - -// ResolveProxyServerHost proxies server host only -func ResolveProxyServerHost(ctx context.Context, host string) (netip.Addr, error) { - if ProxyServerHostResolver != nil { - return ResolveIPWithResolver(ctx, host, ProxyServerHostResolver) - } - return ResolveIP(ctx, host) -} - -func LookupIPv6ProxyServerHost(ctx context.Context, host string) ([]netip.Addr, error) { - if ProxyServerHostResolver != nil { - return LookupIPv6WithResolver(ctx, host, ProxyServerHostResolver) - } - return LookupIPv6(ctx, host) -} - -func LookupIPv4ProxyServerHost(ctx context.Context, host string) ([]netip.Addr, error) { - if ProxyServerHostResolver != nil { - return LookupIPv4WithResolver(ctx, host, ProxyServerHostResolver) - } - return LookupIPv4(ctx, host) -} - -func LookupIPProxyServerHost(ctx context.Context, host string) ([]netip.Addr, error) { - if ProxyServerHostResolver != nil { - return LookupIPWithResolver(ctx, host, ProxyServerHostResolver) - } - return LookupIP(ctx, host) -} - func ResetConnection() { if DefaultResolver != nil { go DefaultResolver.ResetConnection() } - if ProxyServerHostResolver != nil { - go ProxyServerHostResolver.ResetConnection() - } } func SortationAddr(ips []netip.Addr) (ipv4s, ipv6s []netip.Addr) { diff --git a/config/config.go b/config/config.go index 3117853c..3ca57a45 100644 --- a/config/config.go +++ b/config/config.go @@ -160,6 +160,8 @@ type DNS struct { Hosts *trie.DomainTrie[resolver.HostValue] NameServerPolicy []dns.Policy ProxyServerNameserver []dns.NameServer + DirectNameServer []dns.NameServer + DirectFollowPolicy bool } // Profile config @@ -203,25 +205,27 @@ type RawCors struct { } type RawDNS struct { - Enable bool `yaml:"enable" json:"enable"` - PreferH3 bool `yaml:"prefer-h3" json:"prefer-h3"` - IPv6 bool `yaml:"ipv6" json:"ipv6"` - IPv6Timeout uint `yaml:"ipv6-timeout" json:"ipv6-timeout"` - UseHosts bool `yaml:"use-hosts" json:"use-hosts"` - UseSystemHosts bool `yaml:"use-system-hosts" json:"use-system-hosts"` - RespectRules bool `yaml:"respect-rules" json:"respect-rules"` - NameServer []string `yaml:"nameserver" json:"nameserver"` - Fallback []string `yaml:"fallback" json:"fallback"` - FallbackFilter RawFallbackFilter `yaml:"fallback-filter" json:"fallback-filter"` - Listen string `yaml:"listen" json:"listen"` - EnhancedMode C.DNSMode `yaml:"enhanced-mode" json:"enhanced-mode"` - FakeIPRange string `yaml:"fake-ip-range" json:"fake-ip-range"` - FakeIPFilter []string `yaml:"fake-ip-filter" json:"fake-ip-filter"` - FakeIPFilterMode C.FilterMode `yaml:"fake-ip-filter-mode" json:"fake-ip-filter-mode"` - DefaultNameserver []string `yaml:"default-nameserver" json:"default-nameserver"` - CacheAlgorithm string `yaml:"cache-algorithm" json:"cache-algorithm"` - NameServerPolicy *orderedmap.OrderedMap[string, any] `yaml:"nameserver-policy" json:"nameserver-policy"` - ProxyServerNameserver []string `yaml:"proxy-server-nameserver" json:"proxy-server-nameserver"` + Enable bool `yaml:"enable" json:"enable"` + PreferH3 bool `yaml:"prefer-h3" json:"prefer-h3"` + IPv6 bool `yaml:"ipv6" json:"ipv6"` + IPv6Timeout uint `yaml:"ipv6-timeout" json:"ipv6-timeout"` + UseHosts bool `yaml:"use-hosts" json:"use-hosts"` + UseSystemHosts bool `yaml:"use-system-hosts" json:"use-system-hosts"` + RespectRules bool `yaml:"respect-rules" json:"respect-rules"` + NameServer []string `yaml:"nameserver" json:"nameserver"` + Fallback []string `yaml:"fallback" json:"fallback"` + FallbackFilter RawFallbackFilter `yaml:"fallback-filter" json:"fallback-filter"` + Listen string `yaml:"listen" json:"listen"` + EnhancedMode C.DNSMode `yaml:"enhanced-mode" json:"enhanced-mode"` + FakeIPRange string `yaml:"fake-ip-range" json:"fake-ip-range"` + FakeIPFilter []string `yaml:"fake-ip-filter" json:"fake-ip-filter"` + FakeIPFilterMode C.FilterMode `yaml:"fake-ip-filter-mode" json:"fake-ip-filter-mode"` + DefaultNameserver []string `yaml:"default-nameserver" json:"default-nameserver"` + CacheAlgorithm string `yaml:"cache-algorithm" json:"cache-algorithm"` + NameServerPolicy *orderedmap.OrderedMap[string, any] `yaml:"nameserver-policy" json:"nameserver-policy"` + ProxyServerNameserver []string `yaml:"proxy-server-nameserver" json:"proxy-server-nameserver"` + DirectNameServer []string `yaml:"direct-nameserver" json:"direct-nameserver"` + DirectNameServerFollowPolicy bool `yaml:"direct-nameserver-follow-policy" json:"direct-nameserver-follow-policy"` } type RawFallbackFilter struct { @@ -1423,6 +1427,11 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul return nil, err } + if dnsCfg.DirectNameServer, err = parseNameServer(cfg.DirectNameServer, false, cfg.PreferH3); err != nil { + return nil, err + } + dnsCfg.DirectFollowPolicy = cfg.DirectNameServerFollowPolicy + if len(cfg.DefaultNameserver) == 0 { return nil, errors.New("default nameserver should have at least one nameserver") } diff --git a/dns/patch_android.go b/dns/patch_android.go index 5a98e86c..8e744fcd 100644 --- a/dns/patch_android.go +++ b/dns/patch_android.go @@ -12,9 +12,6 @@ func FlushCacheWithDefaultResolver() { if r := resolver.DefaultResolver; r != nil { r.ClearCache() } - if r := resolver.ProxyServerHostResolver; r != nil { - r.ClearCache() - } if r := resolver.SystemResolver; r != nil { r.ClearCache() } diff --git a/dns/resolver.go b/dns/resolver.go index ea8888cc..9f7e28f3 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -427,6 +427,8 @@ type Config struct { Main, Fallback []NameServer Default []NameServer ProxyServer []NameServer + DirectServer []NameServer + DirectFollowPolicy bool IPv6 bool IPv6Timeout uint EnhancedMode C.DNSMode @@ -446,7 +448,25 @@ func (config Config) newCache() dnsCache { } } -func NewResolver(config Config) (r *Resolver, pr *Resolver) { +type Resolvers struct { + *Resolver + ProxyResolver *Resolver + DirectResolver *Resolver +} + +func (rs Resolvers) ClearCache() { + rs.Resolver.ClearCache() + rs.ProxyResolver.ClearCache() + rs.DirectResolver.ClearCache() +} + +func (rs Resolvers) ResetConnection() { + rs.Resolver.ResetConnection() + rs.ProxyResolver.ResetConnection() + rs.DirectResolver.ResetConnection() +} + +func NewResolver(config Config) (rs Resolvers) { defaultResolver := &Resolver{ main: transform(config.Default, nil), cache: config.newCache(), @@ -480,7 +500,7 @@ func NewResolver(config Config) (r *Resolver, pr *Resolver) { return } - r = &Resolver{ + r := &Resolver{ ipv6: config.IPv6, main: cacheTransform(config.Main), cache: config.newCache(), @@ -488,9 +508,10 @@ func NewResolver(config Config) (r *Resolver, pr *Resolver) { ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond, } r.defaultResolver = defaultResolver + rs.Resolver = r if len(config.ProxyServer) != 0 { - pr = &Resolver{ + rs.ProxyResolver = &Resolver{ ipv6: config.IPv6, main: cacheTransform(config.ProxyServer), cache: config.newCache(), @@ -499,8 +520,20 @@ func NewResolver(config Config) (r *Resolver, pr *Resolver) { } } + if len(config.DirectServer) != 0 { + rs.DirectResolver = &Resolver{ + ipv6: config.IPv6, + main: cacheTransform(config.DirectServer), + cache: config.newCache(), + hosts: config.Hosts, + ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond, + } + } + if len(config.Fallback) != 0 { r.fallback = cacheTransform(config.Fallback) + r.fallbackIPFilters = config.FallbackIPFilter + r.fallbackDomainFilters = config.FallbackDomainFilter } if len(config.Policy) != 0 { @@ -529,9 +562,11 @@ func NewResolver(config Config) (r *Resolver, pr *Resolver) { } } insertPolicy(nil) + + if rs.DirectResolver != nil && config.DirectFollowPolicy { + rs.DirectResolver.policy = r.policy + } } - r.fallbackIPFilters = config.FallbackIPFilter - r.fallbackDomainFilters = config.FallbackDomainFilter return } diff --git a/dns/system.go b/dns/system.go index f05cb0f3..9fb803dd 100644 --- a/dns/system.go +++ b/dns/system.go @@ -61,7 +61,7 @@ func newSystemClient() *systemClient { } func init() { - r, _ := NewResolver(Config{}) + r := NewResolver(Config{}) c := newSystemClient() c.defaultNS = transform([]NameServer{{Addr: "114.114.114.114:53"}, {Addr: "8.8.8.8:53"}}, nil) r.main = []dnsClient{c} diff --git a/docs/config.yaml b/docs/config.yaml index 15bcf607..e75e5bd5 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -294,10 +294,15 @@ dns: # - tcp://1.1.1.1 # - 'tcp://1.1.1.1#ProxyGroupName' # 指定 DNS 过代理查询,ProxyGroupName 为策略组名或节点名,过代理配置优先于配置出口网卡,当找不到策略组或节点名则设置为出口网卡 - # 专用于节点域名解析的 DNS 服务器,非必要配置项 + # 专用于节点域名解析的 DNS 服务器,非必要配置项,如果不填则遵循nameserver-policy、nameserver和fallback的配置 # proxy-server-nameserver: - # - https://dns.google/dns-query - # - tls://one.one.one.one + # - https://dns.google/dns-query + # - tls://one.one.one.one + + # 专用于direct出口域名解析的 DNS 服务器,非必要配置项,如果不填则遵循nameserver-policy、nameserver和fallback的配置 + # direct-nameserver: + # - system:// + # direct-nameserver-follow-policy: false # 是否遵循nameserver-policy,默认为不遵守,仅当direct-nameserver不为空时生效 # 配置 fallback 使用条件 # fallback-filter: diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 10ea21b0..b8d9cddb 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -235,6 +235,8 @@ func updateDNS(c *config.DNS, generalIPv6 bool) { resolver.DefaultResolver = nil resolver.DefaultHostMapper = nil resolver.DefaultLocalServer = nil + resolver.ProxyServerHostResolver = nil + resolver.DirectHostResolver = nil dns.ReCreateServer("", nil, nil) return } @@ -251,10 +253,12 @@ func updateDNS(c *config.DNS, generalIPv6 bool) { Default: c.DefaultNameserver, Policy: c.NameServerPolicy, ProxyServer: c.ProxyServerNameserver, + DirectServer: c.DirectNameServer, + DirectFollowPolicy: c.DirectFollowPolicy, CacheAlgorithm: c.CacheAlgorithm, } - r, pr := dns.NewResolver(cfg) + r := dns.NewResolver(cfg) m := dns.NewEnhancer(cfg) // reuse cache of old host mapper @@ -264,14 +268,22 @@ func updateDNS(c *config.DNS, generalIPv6 bool) { resolver.DefaultResolver = r resolver.DefaultHostMapper = m - resolver.DefaultLocalServer = dns.NewLocalServer(r, m) + resolver.DefaultLocalServer = dns.NewLocalServer(r.Resolver, m) resolver.UseSystemHosts = c.UseSystemHosts - if pr.Invalid() { - resolver.ProxyServerHostResolver = pr + if r.ProxyResolver.Invalid() { + resolver.ProxyServerHostResolver = r.ProxyResolver + } else { + resolver.ProxyServerHostResolver = r.Resolver } - dns.ReCreateServer(c.Listen, r, m) + if r.DirectResolver.Invalid() { + resolver.DirectHostResolver = r.DirectResolver + } else { + resolver.DirectHostResolver = r.Resolver + } + + dns.ReCreateServer(c.Listen, r.Resolver, m) } func updateHosts(tree *trie.DomainTrie[resolver.HostValue]) { From 9286e21026d5343622dc0678166fbf075e910d79 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 5 Oct 2024 13:40:00 +0800 Subject: [PATCH 256/311] chore: rebuild external ui updater --- component/updater/update_ui.go | 81 ++++++++++++++++++++++++++-------- config/config.go | 32 +++----------- hub/executor/executor.go | 15 ++----- hub/route/upgrade.go | 2 +- 4 files changed, 74 insertions(+), 56 deletions(-) diff --git a/component/updater/update_ui.go b/component/updater/update_ui.go index 29081761..48d6536c 100644 --- a/component/updater/update_ui.go +++ b/component/updater/update_ui.go @@ -14,24 +14,69 @@ import ( "github.com/metacubex/mihomo/log" ) -var ( - ExternalUIURL string - ExternalUIPath string - AutoDownloadUI bool -) +type UIUpdater struct { + externalUIURL string + externalUIPath string + autoDownloadUI bool -var xdMutex sync.Mutex + mutex sync.Mutex +} -func DownloadUI() error { - xdMutex.Lock() - defer xdMutex.Unlock() +var DefaultUiUpdater = &UIUpdater{} - err := prepareUIPath() +func NewUiUpdater(externalUI, externalUIURL, externalUIName string) *UIUpdater { + updater := &UIUpdater{} + // checkout externalUI exist + if externalUI != "" { + updater.autoDownloadUI = true + updater.externalUIPath = C.Path.Resolve(externalUI) + } else { + // default externalUI path + updater.externalUIPath = path.Join(C.Path.HomeDir(), "ui") + } + + // checkout UIpath/name exist + if externalUIName != "" { + updater.autoDownloadUI = true + updater.externalUIPath = path.Join(updater.externalUIPath, externalUIName) + } + + if externalUIURL != "" { + updater.externalUIURL = externalUIURL + } + return updater +} + +func (u *UIUpdater) AutoDownloadUI() { + u.mutex.Lock() + defer u.mutex.Unlock() + if u.autoDownloadUI { + dirEntries, _ := os.ReadDir(u.externalUIPath) + if len(dirEntries) > 0 { + log.Infoln("UI already exists, skip downloading") + } else { + log.Infoln("External UI downloading ...") + err := u.downloadUI() + if err != nil { + log.Errorln("Error downloading UI:", err) + } + } + } +} + +func (u *UIUpdater) DownloadUI() error { + u.mutex.Lock() + defer u.mutex.Unlock() + return u.downloadUI() +} + +func (u *UIUpdater) downloadUI() error { + err := u.prepareUIPath() if err != nil { return fmt.Errorf("prepare UI path failed: %w", err) } - data, err := downloadForBytes(ExternalUIURL) + data, err := downloadForBytes(u.externalUIURL) if err != nil { return fmt.Errorf("can't download file: %w", err) } @@ -42,7 +87,7 @@ func DownloadUI() error { } defer os.Remove(saved) - err = cleanup(ExternalUIPath) + err = cleanup(u.externalUIPath) if err != nil { if !os.IsNotExist(err) { return fmt.Errorf("cleanup exist file error: %w", err) @@ -54,18 +99,18 @@ func DownloadUI() error { return fmt.Errorf("can't extract zip file: %w", err) } - err = os.Rename(unzipFolder, ExternalUIPath) + err = os.Rename(unzipFolder, u.externalUIPath) if err != nil { return fmt.Errorf("rename UI folder failed: %w", err) } return nil } -func prepareUIPath() error { - if _, err := os.Stat(ExternalUIPath); os.IsNotExist(err) { - log.Infoln("dir %s does not exist, creating", ExternalUIPath) - if err := os.MkdirAll(ExternalUIPath, os.ModePerm); err != nil { - log.Warnln("create dir %s error: %s", ExternalUIPath, err) +func (u *UIUpdater) prepareUIPath() error { + if _, err := os.Stat(u.externalUIPath); os.IsNotExist(err) { + log.Infoln("dir %s does not exist, creating", u.externalUIPath) + if err := os.MkdirAll(u.externalUIPath, os.ModePerm); err != nil { + log.Warnln("create dir %s error: %s", u.externalUIPath, err) } } return nil diff --git a/config/config.go b/config/config.go index 3ca57a45..c1d70e81 100644 --- a/config/config.go +++ b/config/config.go @@ -7,7 +7,6 @@ import ( "net" "net/netip" "net/url" - "path" "strings" "time" @@ -105,6 +104,8 @@ type Controller struct { ExternalControllerUnix string ExternalControllerPipe string ExternalUI string + ExternalUIURL string + ExternalUIName string ExternalDohServer string Secret string Cors Cors @@ -718,33 +719,10 @@ func parseGeneral(cfg *RawConfig) (*General, error) { mihomoHttp.SetUA(cfg.GlobalUA) resource.SetETag(cfg.ETagSupport) - if cfg.KeepAliveIdle != 0 { - keepalive.SetKeepAliveIdle(time.Duration(cfg.KeepAliveIdle) * time.Second) - } - if cfg.KeepAliveInterval != 0 { - keepalive.SetKeepAliveInterval(time.Duration(cfg.KeepAliveInterval) * time.Second) - } + keepalive.SetKeepAliveIdle(time.Duration(cfg.KeepAliveIdle) * time.Second) + keepalive.SetKeepAliveInterval(time.Duration(cfg.KeepAliveInterval) * time.Second) keepalive.SetDisableKeepAlive(cfg.DisableKeepAlive) - // checkout externalUI exist - if cfg.ExternalUI != "" { - updater.AutoDownloadUI = true - updater.ExternalUIPath = C.Path.Resolve(cfg.ExternalUI) - } else { - // default externalUI path - updater.ExternalUIPath = path.Join(C.Path.HomeDir(), "ui") - } - - // checkout UIpath/name exist - if cfg.ExternalUIName != "" { - updater.AutoDownloadUI = true - updater.ExternalUIPath = path.Join(updater.ExternalUIPath, cfg.ExternalUIName) - } - - if cfg.ExternalUIURL != "" { - updater.ExternalUIURL = cfg.ExternalUIURL - } - return &General{ Inbound: Inbound{ Port: cfg.Port, @@ -790,6 +768,8 @@ func parseController(cfg *RawConfig) (*Controller, error) { return &Controller{ ExternalController: cfg.ExternalController, ExternalUI: cfg.ExternalUI, + ExternalUIURL: cfg.ExternalUIURL, + ExternalUIName: cfg.ExternalUIName, Secret: cfg.Secret, ExternalControllerPipe: cfg.ExternalControllerPipe, ExternalControllerUnix: cfg.ExternalControllerUnix, diff --git a/hub/executor/executor.go b/hub/executor/executor.go index b8d9cddb..0492c430 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -117,7 +117,7 @@ func ApplyConfig(cfg *config.Config, force bool) { runtime.GC() tunnel.OnRunning() hcCompatibleProvider(cfg.Providers) - initExternalUI() + initExternalUI(cfg.Controller) resolver.ResetConnection() } @@ -394,16 +394,9 @@ func updateTunnels(tunnels []LC.Tunnel) { listener.PatchTunnel(tunnels, tunnel.Tunnel) } -func initExternalUI() { - if updater.AutoDownloadUI { - dirEntries, _ := os.ReadDir(updater.ExternalUIPath) - if len(dirEntries) > 0 { - log.Infoln("UI already exists, skip downloading") - } else { - log.Infoln("External UI downloading ...") - updater.DownloadUI() - } - } +func initExternalUI(controller *config.Controller) { + updater.DefaultUiUpdater = updater.NewUiUpdater(controller.ExternalUI, controller.ExternalUIURL, controller.ExternalUIName) + updater.DefaultUiUpdater.AutoDownloadUI() } func updateGeneral(general *config.General) { diff --git a/hub/route/upgrade.go b/hub/route/upgrade.go index 25c326dd..2fed3f67 100644 --- a/hub/route/upgrade.go +++ b/hub/route/upgrade.go @@ -47,7 +47,7 @@ func upgradeCore(w http.ResponseWriter, r *http.Request) { } func updateUI(w http.ResponseWriter, r *http.Request) { - err := updater.DownloadUI() + err := updater.DefaultUiUpdater.DownloadUI() if err != nil { log.Warnln("%s", err) render.Status(r, http.StatusInternalServerError) From 8f5a86410c9f1539573d9518e9e497369568fc95 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 5 Oct 2024 13:58:49 +0800 Subject: [PATCH 257/311] chore: cleanup unneeded setting in parseGeneral, move to executor --- config/config.go | 14 ++++++-------- hub/executor/executor.go | 17 +++++++++++++++-- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/config/config.go b/config/config.go index c1d70e81..69957cee 100644 --- a/config/config.go +++ b/config/config.go @@ -20,14 +20,12 @@ import ( "github.com/metacubex/mihomo/component/fakeip" "github.com/metacubex/mihomo/component/geodata" mihomoHttp "github.com/metacubex/mihomo/component/http" - "github.com/metacubex/mihomo/component/keepalive" P "github.com/metacubex/mihomo/component/process" "github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/component/resource" "github.com/metacubex/mihomo/component/sniffer" tlsC "github.com/metacubex/mihomo/component/tls" "github.com/metacubex/mihomo/component/trie" - "github.com/metacubex/mihomo/component/updater" C "github.com/metacubex/mihomo/constant" providerTypes "github.com/metacubex/mihomo/constant/provider" snifferTypes "github.com/metacubex/mihomo/constant/sniffer" @@ -66,6 +64,9 @@ type General struct { GlobalClientFingerprint string `json:"global-client-fingerprint"` GlobalUA string `json:"global-ua"` ETagSupport bool `json:"etag-support"` + KeepAliveIdle int `json:"keep-alive-idle"` + KeepAliveInterval int `json:"keep-alive-interval"` + DisableKeepAlive bool `json:"disable-keep-alive"` } // Inbound config @@ -707,8 +708,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } func parseGeneral(cfg *RawConfig) (*General, error) { - updater.SetGeoAutoUpdate(cfg.GeoAutoUpdate) - updater.SetGeoUpdateInterval(cfg.GeoUpdateInterval) geodata.SetGeodataMode(cfg.GeodataMode) geodata.SetLoader(cfg.GeodataLoader) geodata.SetSiteMatcher(cfg.GeositeMatcher) @@ -719,10 +718,6 @@ func parseGeneral(cfg *RawConfig) (*General, error) { mihomoHttp.SetUA(cfg.GlobalUA) resource.SetETag(cfg.ETagSupport) - keepalive.SetKeepAliveIdle(time.Duration(cfg.KeepAliveIdle) * time.Second) - keepalive.SetKeepAliveInterval(time.Duration(cfg.KeepAliveInterval) * time.Second) - keepalive.SetDisableKeepAlive(cfg.DisableKeepAlive) - return &General{ Inbound: Inbound{ Port: cfg.Port, @@ -761,6 +756,9 @@ func parseGeneral(cfg *RawConfig) (*General, error) { GlobalClientFingerprint: cfg.GlobalClientFingerprint, GlobalUA: cfg.GlobalUA, ETagSupport: cfg.ETagSupport, + KeepAliveIdle: cfg.KeepAliveIdle, + KeepAliveInterval: cfg.KeepAliveInterval, + DisableKeepAlive: cfg.DisableKeepAlive, }, nil } diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 0492c430..19979063 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -19,6 +19,7 @@ import ( G "github.com/metacubex/mihomo/component/geodata" mihomoHttp "github.com/metacubex/mihomo/component/http" "github.com/metacubex/mihomo/component/iface" + "github.com/metacubex/mihomo/component/keepalive" "github.com/metacubex/mihomo/component/profile" "github.com/metacubex/mihomo/component/profile/cachefile" "github.com/metacubex/mihomo/component/resolver" @@ -117,7 +118,7 @@ func ApplyConfig(cfg *config.Config, force bool) { runtime.GC() tunnel.OnRunning() hcCompatibleProvider(cfg.Providers) - initExternalUI(cfg.Controller) + updateUpdater(cfg) resolver.ResetConnection() } @@ -176,6 +177,9 @@ func GetGeneral() *config.General { GlobalClientFingerprint: tlsC.GetGlobalFingerprint(), GlobalUA: mihomoHttp.UA(), ETagSupport: resource.ETag(), + KeepAliveInterval: int(keepalive.KeepAliveInterval() / time.Second), + KeepAliveIdle: int(keepalive.KeepAliveIdle() / time.Second), + DisableKeepAlive: keepalive.DisableKeepAlive(), } return general @@ -394,7 +398,12 @@ func updateTunnels(tunnels []LC.Tunnel) { listener.PatchTunnel(tunnels, tunnel.Tunnel) } -func initExternalUI(controller *config.Controller) { +func updateUpdater(cfg *config.Config) { + general := cfg.General + updater.SetGeoAutoUpdate(general.GeoAutoUpdate) + updater.SetGeoUpdateInterval(general.GeoUpdateInterval) + + controller := cfg.Controller updater.DefaultUiUpdater = updater.NewUiUpdater(controller.ExternalUI, controller.ExternalUIURL, controller.ExternalUIName) updater.DefaultUiUpdater.AutoDownloadUI() } @@ -412,6 +421,10 @@ func updateGeneral(general *config.General) { inbound.SetTfo(general.InboundTfo) inbound.SetMPTCP(general.InboundMPTCP) + keepalive.SetKeepAliveIdle(time.Duration(general.KeepAliveIdle) * time.Second) + keepalive.SetKeepAliveInterval(time.Duration(general.KeepAliveInterval) * time.Second) + keepalive.SetDisableKeepAlive(general.DisableKeepAlive) + adapter.UnifiedDelay.Store(general.UnifiedDelay) dialer.DefaultInterface.Store(general.Interface) From 9937ae1002a38fd159593bf0cacb6f86864e9303 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 5 Oct 2024 14:16:50 +0800 Subject: [PATCH 258/311] fix: defaultNS not working in system dns --- dns/system.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/dns/system.go b/dns/system.go index 9fb803dd..ab6c0100 100644 --- a/dns/system.go +++ b/dns/system.go @@ -31,12 +31,12 @@ type systemClient struct { func (c *systemClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { dnsClients, err := c.getDnsClients() + if len(dnsClients) == 0 && len(c.defaultNS) > 0 { + dnsClients = c.defaultNS + err = nil + } if err != nil { - if len(c.defaultNS) > 0 { - dnsClients = c.defaultNS - } else { - return - } + return } msg, _, err = batchExchange(ctx, dnsClients, m) return @@ -45,11 +45,16 @@ func (c *systemClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Ms // Address implements dnsClient func (c *systemClient) Address() string { dnsClients, _ := c.getDnsClients() + isDefault := "" + if len(dnsClients) == 0 && len(c.defaultNS) > 0 { + dnsClients = c.defaultNS + isDefault = "[defaultNS]" + } addrs := make([]string, 0, len(dnsClients)) for _, c := range dnsClients { addrs = append(addrs, c.Address()) } - return fmt.Sprintf("system(%s)", strings.Join(addrs, ",")) + return fmt.Sprintf("system%s(%s)", isDefault, strings.Join(addrs, ",")) } var _ dnsClient = (*systemClient)(nil) From 8e6eb70e714d44f26ba407adbd7b255762f48b97 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 5 Oct 2024 19:23:01 +0800 Subject: [PATCH 259/311] chore: temporary update general in ParseRawConfig and rollback before its retur --- component/updater/update_ui.go | 2 +- config/config.go | 27 +++++++----------- hub/executor/executor.go | 50 ++++++++++++++++++++++++---------- 3 files changed, 46 insertions(+), 33 deletions(-) diff --git a/component/updater/update_ui.go b/component/updater/update_ui.go index 48d6536c..bd2a5881 100644 --- a/component/updater/update_ui.go +++ b/component/updater/update_ui.go @@ -58,7 +58,7 @@ func (u *UIUpdater) AutoDownloadUI() { log.Infoln("External UI downloading ...") err := u.downloadUI() if err != nil { - log.Errorln("Error downloading UI:", err) + log.Errorln("Error downloading UI: %s", err) } } } diff --git a/config/config.go b/config/config.go index 69957cee..ba6097bc 100644 --- a/config/config.go +++ b/config/config.go @@ -9,6 +9,7 @@ import ( "net/url" "strings" "time" + _ "unsafe" "github.com/metacubex/mihomo/adapter" "github.com/metacubex/mihomo/adapter/outbound" @@ -19,12 +20,9 @@ import ( "github.com/metacubex/mihomo/component/cidr" "github.com/metacubex/mihomo/component/fakeip" "github.com/metacubex/mihomo/component/geodata" - mihomoHttp "github.com/metacubex/mihomo/component/http" P "github.com/metacubex/mihomo/component/process" "github.com/metacubex/mihomo/component/resolver" - "github.com/metacubex/mihomo/component/resource" "github.com/metacubex/mihomo/component/sniffer" - tlsC "github.com/metacubex/mihomo/component/tls" "github.com/metacubex/mihomo/component/trie" C "github.com/metacubex/mihomo/constant" providerTypes "github.com/metacubex/mihomo/constant/provider" @@ -588,10 +586,11 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } config.General = general - if len(config.General.GlobalClientFingerprint) != 0 { - log.Debugln("GlobalClientFingerprint: %s", config.General.GlobalClientFingerprint) - tlsC.SetGlobalUtlsClient(config.General.GlobalClientFingerprint) - } + // We need to temporarily apply some configuration in general and roll back after parsing the complete configuration. + // The loading and downloading of geodata in the parseRules and parseRuleProviders rely on these. + // This implementation is very disgusting, but there is currently no better solution + rollback := temporaryUpdateGeneral(config.General) + defer rollback() controller, err := parseController(rawCfg) if err != nil { @@ -707,17 +706,10 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { return config, nil } -func parseGeneral(cfg *RawConfig) (*General, error) { - geodata.SetGeodataMode(cfg.GeodataMode) - geodata.SetLoader(cfg.GeodataLoader) - geodata.SetSiteMatcher(cfg.GeositeMatcher) - geodata.SetGeoIpUrl(cfg.GeoXUrl.GeoIp) - geodata.SetGeoSiteUrl(cfg.GeoXUrl.GeoSite) - geodata.SetMmdbUrl(cfg.GeoXUrl.Mmdb) - geodata.SetASNUrl(cfg.GeoXUrl.ASN) - mihomoHttp.SetUA(cfg.GlobalUA) - resource.SetETag(cfg.ETagSupport) +//go:linkname temporaryUpdateGeneral +func temporaryUpdateGeneral(general *General) func() +func parseGeneral(cfg *RawConfig) (*General, error) { return &General{ Inbound: Inbound{ Port: cfg.Port, @@ -751,6 +743,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) { GeoUpdateInterval: cfg.GeoUpdateInterval, GeodataMode: cfg.GeodataMode, GeodataLoader: cfg.GeodataLoader, + GeositeMatcher: cfg.GeositeMatcher, TCPConcurrent: cfg.TCPConcurrent, FindProcessMode: cfg.FindProcessMode, GlobalClientFingerprint: cfg.GlobalClientFingerprint, diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 19979063..3fed360c 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -9,6 +9,7 @@ import ( "strconv" "sync" "time" + _ "unsafe" "github.com/metacubex/mihomo/adapter" "github.com/metacubex/mihomo/adapter/inbound" @@ -16,7 +17,7 @@ import ( "github.com/metacubex/mihomo/component/auth" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/dialer" - G "github.com/metacubex/mihomo/component/geodata" + "github.com/metacubex/mihomo/component/geodata" mihomoHttp "github.com/metacubex/mihomo/component/http" "github.com/metacubex/mihomo/component/iface" "github.com/metacubex/mihomo/component/keepalive" @@ -101,7 +102,7 @@ func ApplyConfig(cfg *config.Config, force bool) { updateRules(cfg.Rules, cfg.SubRules, cfg.RuleProviders) updateSniffer(cfg.Sniffer) updateHosts(cfg.Hosts) - updateGeneral(cfg.General) + updateGeneral(cfg.General, true) updateNTP(cfg.NTP) updateDNS(cfg.DNS, cfg.General.IPv6) updateListeners(cfg.General, cfg.Listeners, force) @@ -161,16 +162,16 @@ func GetGeneral() *config.General { Interface: dialer.DefaultInterface.Load(), RoutingMark: int(dialer.DefaultRoutingMark.Load()), GeoXUrl: config.GeoXUrl{ - GeoIp: G.GeoIpUrl(), - Mmdb: G.MmdbUrl(), - ASN: G.ASNUrl(), - GeoSite: G.GeoSiteUrl(), + GeoIp: geodata.GeoIpUrl(), + Mmdb: geodata.MmdbUrl(), + ASN: geodata.ASNUrl(), + GeoSite: geodata.GeoSiteUrl(), }, GeoAutoUpdate: updater.GeoAutoUpdate(), GeoUpdateInterval: updater.GeoUpdateInterval(), - GeodataMode: G.GeodataMode(), - GeodataLoader: G.LoaderName(), - GeositeMatcher: G.SiteMatcherName(), + GeodataMode: geodata.GeodataMode(), + GeodataLoader: geodata.LoaderName(), + GeositeMatcher: geodata.SiteMatcherName(), TCPConcurrent: dialer.GetTcpConcurrent(), FindProcessMode: tunnel.FindProcessMode(), Sniffing: tunnel.IsSniffing(), @@ -408,13 +409,22 @@ func updateUpdater(cfg *config.Config) { updater.DefaultUiUpdater.AutoDownloadUI() } -func updateGeneral(general *config.General) { +//go:linkname temporaryUpdateGeneral github.com/metacubex/mihomo/config.temporaryUpdateGeneral +func temporaryUpdateGeneral(general *config.General) func() { + oldGeneral := GetGeneral() + updateGeneral(general, false) + return func() { + updateGeneral(oldGeneral, false) + } +} + +func updateGeneral(general *config.General, logging bool) { tunnel.SetMode(general.Mode) tunnel.SetFindProcessMode(general.FindProcessMode) resolver.DisableIPv6 = !general.IPv6 - if general.TCPConcurrent { - dialer.SetTcpConcurrent(general.TCPConcurrent) + dialer.SetTcpConcurrent(general.TCPConcurrent) + if logging && general.TCPConcurrent { log.Infoln("Use tcp concurrent") } @@ -429,13 +439,23 @@ func updateGeneral(general *config.General) { dialer.DefaultInterface.Store(general.Interface) dialer.DefaultRoutingMark.Store(int32(general.RoutingMark)) - if general.RoutingMark > 0 { + if logging && general.RoutingMark > 0 { log.Infoln("Use routing mark: %#x", general.RoutingMark) } iface.FlushCache() - G.SetLoader(general.GeodataLoader) - G.SetSiteMatcher(general.GeositeMatcher) + + geodata.SetGeodataMode(general.GeodataMode) + geodata.SetLoader(general.GeodataLoader) + geodata.SetSiteMatcher(general.GeositeMatcher) + geodata.SetGeoIpUrl(general.GeoXUrl.GeoIp) + geodata.SetGeoSiteUrl(general.GeoXUrl.GeoSite) + geodata.SetMmdbUrl(general.GeoXUrl.Mmdb) + geodata.SetASNUrl(general.GeoXUrl.ASN) + mihomoHttp.SetUA(general.GlobalUA) + resource.SetETag(general.ETagSupport) + + tlsC.SetGlobalUtlsClient(general.GlobalClientFingerprint) } func updateUsers(users []auth.AuthUser) { From 9fd63fe93803c89243452f9b033625bd22f75282 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 6 Oct 2024 10:34:54 +0800 Subject: [PATCH 260/311] chore: update dependencies --- go.mod | 16 ++++++++-------- go.sum | 34 +++++++++++++++++----------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/go.mod b/go.mod index 4b066c90..a223cf77 100644 --- a/go.mod +++ b/go.mod @@ -5,13 +5,13 @@ go 1.20 require ( github.com/3andne/restls-client-go v0.1.6 github.com/bahlo/generic-list-go v0.2.0 - github.com/coreos/go-iptables v0.7.0 + github.com/coreos/go-iptables v0.8.0 github.com/dlclark/regexp2 v1.11.4 github.com/go-chi/chi/v5 v5.1.0 github.com/go-chi/render v1.0.3 github.com/gobwas/ws v1.4.0 github.com/gofrs/uuid/v5 v5.3.0 - github.com/insomniacslk/dhcp v0.0.0-20240812123929-b105c29bd1b5 + github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475 github.com/klauspost/compress v1.17.9 github.com/klauspost/cpuid/v2 v2.2.8 github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 @@ -28,7 +28,7 @@ require ( github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 - github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785 + github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa github.com/metacubex/utls v1.6.6 github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 github.com/miekg/dns v1.1.62 @@ -51,10 +51,10 @@ require ( gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 go.uber.org/automaxprocs v1.6.0 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba - golang.org/x/crypto v0.27.0 - golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa - golang.org/x/net v0.29.0 - golang.org/x/sys v0.25.0 + golang.org/x/crypto v0.28.0 + golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // lastest version compatible with golang1.20 + golang.org/x/net v0.30.0 + golang.org/x/sys v0.26.0 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.3.0 @@ -111,7 +111,7 @@ require ( go.uber.org/mock v0.4.0 // indirect golang.org/x/mod v0.20.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/text v0.18.0 // indirect + golang.org/x/text v0.19.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.24.0 // indirect ) diff --git a/go.sum b/go.sum index 60818034..2b3bba65 100644 --- a/go.sum +++ b/go.sum @@ -19,8 +19,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= -github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8= -github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= +github.com/coreos/go-iptables v0.8.0 h1:MPc2P89IhuVpLI7ETL/2tx3XZ61VeICZjYqDEgNsPRc= +github.com/coreos/go-iptables v0.8.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -71,8 +71,8 @@ github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I= 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-20240812123929-b105c29bd1b5 h1:GkMacU5ftc+IEg1449N3UEy2XLDz58W4fkrRu2fibb8= -github.com/insomniacslk/dhcp v0.0.0-20240812123929-b105c29bd1b5/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= +github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475 h1:hxST5pwMBEOWmxpkX20w9oZG+hXdhKmAIPQ3NGGAxas= +github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 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= @@ -122,8 +122,8 @@ github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosq github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 h1:xg71VmzLS6ByAzi/57phwDvjE+dLLs+ozH00k4DnOns= github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3/go.mod h1:6nitcmzPDL3MXnLdhu6Hm126Zk4S1fBbX3P7jxUxSFw= -github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785 h1:NNmI+ZV0DzNuqaAInRQuZFLHlWVuyHeow8jYpdKjHjo= -github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= +github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa h1:9mcjV+RGZVC3reJBNDjjNPyS8PmFG97zq56X7WNaFO4= +github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa/go.mod h1:4tLB5c8U0CxpkFM+AJJB77jEaVDbLH5XQvy42vAGsWw= github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8= github.com/metacubex/utls v1.6.6/go.mod h1:+WLFUnXjcpdxXCnyX25nggw8C6YonZ8zOK2Zm/oRvdo= github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 h1:hJLQviGySBuaynlCwf/oYgIxbVbGRUIKZCxdya9YrbQ= @@ -230,18 +230,18 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= 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.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= -golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk= +golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= 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.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -261,12 +261,12 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= From 08dcef80bf9a528d36cf8d516cc251f6449f9336 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 9 Oct 2024 12:04:56 +0800 Subject: [PATCH 261/311] fix: mistaken using net.Dialer https://github.com/MetaCubeX/mihomo/issues/1572 --- adapter/outbound/hysteria.go | 6 +----- component/http/http.go | 4 ++-- transport/hysteria/transport/client.go | 25 +------------------------ 3 files changed, 4 insertions(+), 31 deletions(-) diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index a7367b83..55c66c45 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -131,11 +131,7 @@ func (c *HysteriaOption) Speed() (uint64, uint64, error) { } func NewHysteria(option HysteriaOption) (*Hysteria, error) { - clientTransport := &transport.ClientTransport{ - Dialer: &net.Dialer{ - Timeout: 8 * time.Second, - }, - } + clientTransport := &transport.ClientTransport{} addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) ports := option.Ports diff --git a/component/http/http.go b/component/http/http.go index 3fc06da3..63ea5be7 100644 --- a/component/http/http.go +++ b/component/http/http.go @@ -12,6 +12,7 @@ import ( "time" "github.com/metacubex/mihomo/component/ca" + "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/listener/inner" ) @@ -71,8 +72,7 @@ func HttpRequestWithProxy(ctx context.Context, url, method string, header map[st if conn, err := inner.HandleTcp(address, specialProxy); err == nil { return conn, nil } else { - d := net.Dialer{} - return d.DialContext(ctx, network, address) + return dialer.DialContext(ctx, network, address) } }, TLSClientConfig: ca.GetGlobalTLSConfig(&tls.Config{}), diff --git a/transport/hysteria/transport/client.go b/transport/hysteria/transport/client.go index f5cc9f07..91876ea9 100644 --- a/transport/hysteria/transport/client.go +++ b/transport/hysteria/transport/client.go @@ -4,7 +4,6 @@ import ( "crypto/tls" "fmt" "net" - "strings" "time" "github.com/metacubex/quic-go" @@ -16,9 +15,7 @@ import ( "github.com/metacubex/mihomo/transport/hysteria/utils" ) -type ClientTransport struct { - Dialer *net.Dialer -} +type ClientTransport struct{} func (ct *ClientTransport) quicPacketConn(proto string, rAddr net.Addr, serverPorts string, obfs obfsPkg.Obfuscator, hopInterval time.Duration, dialer utils.PacketDialer) (net.PacketConn, error) { server := rAddr.String() @@ -86,23 +83,3 @@ func (ct *ClientTransport) QUICDial(proto string, server string, serverPorts str } return qs, nil } - -func (ct *ClientTransport) DialTCP(raddr *net.TCPAddr) (*net.TCPConn, error) { - conn, err := ct.Dialer.Dial("tcp", raddr.String()) - if err != nil { - return nil, err - } - return conn.(*net.TCPConn), nil -} - -func (ct *ClientTransport) ListenUDP() (*net.UDPConn, error) { - return net.ListenUDP("udp", nil) -} - -func isMultiPortAddr(addr string) bool { - _, portStr, err := net.SplitHostPort(addr) - if err == nil && (strings.Contains(portStr, ",") || strings.Contains(portStr, "-")) { - return true - } - return false -} From 57725078e00170f67abb5d9a8cb082c81213a449 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Fri, 11 Oct 2024 07:35:51 +0800 Subject: [PATCH 262/311] chore: Adjust the error log for the search process --- tunnel/tunnel.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 5c136eb2..b1b4add5 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -625,7 +625,7 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { // normal check for process uid, path, err := P.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, int(metadata.SrcPort)) if err != nil { - log.Debugln("[Process] find process %s error: %v", metadata.String(), err) + log.Debugln("[Process] find process error for %s: %v", metadata.String(), err) } else { metadata.Process = filepath.Base(path) metadata.ProcessPath = path @@ -639,7 +639,7 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { // check package names pkg, err := P.FindPackageName(metadata) if err != nil { - log.Debugln("[Process] find process %s error: %v", metadata.String(), err) + log.Debugln("[Process] find process error for %s: %v", metadata.String(), err) } else { metadata.Process = pkg } From 4437c8861c3a0815aa2f338df8676de21176a286 Mon Sep 17 00:00:00 2001 From: ForestL <45709305+ForestL18@users.noreply.github.com> Date: Fri, 11 Oct 2024 08:46:31 +0800 Subject: [PATCH 263/311] chore: better getUpdateTime() for iterating all Geofiles (#1570) --- component/updater/update_geo.go | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/component/updater/update_geo.go b/component/updater/update_geo.go index b5dc9677..bba0dabd 100644 --- a/component/updater/update_geo.go +++ b/component/updater/update_geo.go @@ -229,20 +229,22 @@ func UpdateGeoDatabases() error { } func getUpdateTime() (err error, time time.Time) { - var fileInfo os.FileInfo - if geodata.GeodataMode() { - fileInfo, err = os.Stat(C.Path.GeoIP()) - if err != nil { - return err, time - } - } else { - fileInfo, err = os.Stat(C.Path.MMDB()) - if err != nil { - return err, time + filesToCheck := []string{ + C.Path.GeoIP(), + C.Path.MMDB(), + C.Path.ASN(), + C.Path.GeoSite(), + } + + for _, file := range filesToCheck { + var fileInfo os.FileInfo + fileInfo, err = os.Stat(file) + if err == nil { + return nil, fileInfo.ModTime() } } - return nil, fileInfo.ModTime() + return } func RegisterGeoUpdater() { From ca3f1ebae6b71c2224987f1cf7828cf1338d5509 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sat, 12 Oct 2024 08:26:37 +0800 Subject: [PATCH 264/311] fix: sticky-sessions may not be effective --- adapter/outboundgroup/loadbalance.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapter/outboundgroup/loadbalance.go b/adapter/outboundgroup/loadbalance.go index 738ed154..048cc34c 100644 --- a/adapter/outboundgroup/loadbalance.go +++ b/adapter/outboundgroup/loadbalance.go @@ -204,7 +204,7 @@ func strategyStickySessions(url string) strategyFn { for i := 1; i < maxRetry; i++ { proxy := proxies[nowIdx] if proxy.AliveForTestUrl(url) { - if nowIdx != idx { + if !has || nowIdx != idx { lruCache.Set(key, nowIdx) } From 95af5f7325fcd9c945b3ad52e617e6ee5ae12d50 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sun, 20 Oct 2024 06:01:02 +0800 Subject: [PATCH 265/311] chore: change subscription-userinfo retrieval --- adapter/provider/provider.go | 64 ++++++++----------- adapter/provider/subscription_info.go | 3 +- component/profile/cachefile/cache.go | 7 +- .../profile/cachefile/subscriptioninfo.go | 41 ++++++++++++ component/resource/vehicle.go | 21 ++++-- constant/adapters.go | 4 +- constant/provider/interface.go | 1 + 7 files changed, 92 insertions(+), 49 deletions(-) create mode 100644 component/profile/cachefile/subscriptioninfo.go diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index 79a752a6..2fe9633c 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -1,11 +1,9 @@ package provider import ( - "context" "encoding/json" "errors" "fmt" - "net/http" "reflect" "runtime" "strings" @@ -14,7 +12,7 @@ import ( "github.com/metacubex/mihomo/adapter" "github.com/metacubex/mihomo/common/convert" "github.com/metacubex/mihomo/common/utils" - mihomoHttp "github.com/metacubex/mihomo/component/http" + "github.com/metacubex/mihomo/component/profile/cachefile" "github.com/metacubex/mihomo/component/resource" C "github.com/metacubex/mihomo/constant" types "github.com/metacubex/mihomo/constant/provider" @@ -80,7 +78,9 @@ func (pp *proxySetProvider) Initial() error { if err != nil { return err } - pp.getSubscriptionInfo() + if subscriptionInfo := cachefile.Cache().GetSubscriptionInfo(pp.Name()); subscriptionInfo != "" { + pp.SetSubscriptionInfo(subscriptionInfo) + } pp.closeAllConnections() return nil } @@ -117,35 +117,14 @@ func (pp *proxySetProvider) setProxies(proxies []C.Proxy) { } } -func (pp *proxySetProvider) getSubscriptionInfo() { - if pp.VehicleType() != types.HTTP { - return - } - go func() { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) - defer cancel() - resp, err := mihomoHttp.HttpRequestWithProxy(ctx, pp.Vehicle().Url(), - http.MethodGet, nil, nil, pp.Vehicle().Proxy()) - if err != nil { - return - } - defer resp.Body.Close() +func (pp *proxySetProvider) SetSubscriptionInfo(userInfo string) { + pp.subscriptionInfo = NewSubscriptionInfo(userInfo) +} - userInfoStr := strings.TrimSpace(resp.Header.Get("subscription-userinfo")) - if userInfoStr == "" { - resp2, err := mihomoHttp.HttpRequestWithProxy(ctx, pp.Vehicle().Url(), - http.MethodGet, http.Header{"User-Agent": {"Quantumultx"}}, nil, pp.Vehicle().Proxy()) - if err != nil { - return - } - defer resp2.Body.Close() - userInfoStr = strings.TrimSpace(resp2.Header.Get("subscription-userinfo")) - if userInfoStr == "" { - return - } - } - pp.subscriptionInfo = NewSubscriptionInfo(userInfoStr) - }() +func (pp *proxySetProvider) SetProvider(provider types.ProxyProvider) { + if httpVehicle, ok := pp.Vehicle().(*resource.HTTPVehicle); ok { + httpVehicle.SetProvider(provider) + } } func (pp *proxySetProvider) closeAllConnections() { @@ -196,6 +175,9 @@ func NewProxySetProvider(name string, interval time.Duration, filter string, exc fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, excludeTypeArray, filterRegs, excludeFilterReg, dialerProxy, override), proxiesOnUpdate(pd)) pd.Fetcher = fetcher wrapper := &ProxySetProvider{pd} + if httpVehicle, ok := vehicle.(*resource.HTTPVehicle); ok { + httpVehicle.SetProvider(wrapper) + } runtime.SetFinalizer(wrapper, (*ProxySetProvider).Close) return wrapper, nil } @@ -205,16 +187,21 @@ func (pp *ProxySetProvider) Close() error { return pp.proxySetProvider.Close() } +func (pp *ProxySetProvider) SetProvider(provider types.ProxyProvider) { + pp.proxySetProvider.SetProvider(provider) +} + // CompatibleProvider for auto gc type CompatibleProvider struct { *compatibleProvider } type compatibleProvider struct { - name string - healthCheck *HealthCheck - proxies []C.Proxy - version uint32 + name string + healthCheck *HealthCheck + subscriptionInfo *SubscriptionInfo + proxies []C.Proxy + version uint32 } func (cp *compatibleProvider) MarshalJSON() ([]byte, error) { @@ -284,6 +271,10 @@ func (cp *compatibleProvider) Close() error { return nil } +func (cp *compatibleProvider) SetSubscriptionInfo(userInfo string) { + cp.subscriptionInfo = NewSubscriptionInfo(userInfo) +} + func NewCompatibleProvider(name string, proxies []C.Proxy, hc *HealthCheck) (*CompatibleProvider, error) { if len(proxies) == 0 { return nil, errors.New("provider need one proxy at least") @@ -313,7 +304,6 @@ func proxiesOnUpdate(pd *proxySetProvider) func([]C.Proxy) { return func(elm []C.Proxy) { pd.setProxies(elm) pd.version += 1 - pd.getSubscriptionInfo() } } diff --git a/adapter/provider/subscription_info.go b/adapter/provider/subscription_info.go index b72c7b61..412b4342 100644 --- a/adapter/provider/subscription_info.go +++ b/adapter/provider/subscription_info.go @@ -16,8 +16,7 @@ type SubscriptionInfo struct { } func NewSubscriptionInfo(userinfo string) (si *SubscriptionInfo) { - userinfo = strings.ToLower(userinfo) - userinfo = strings.ReplaceAll(userinfo, " ", "") + userinfo = strings.ReplaceAll(strings.ToLower(userinfo), " ", "") si = new(SubscriptionInfo) for _, field := range strings.Split(userinfo, ";") { diff --git a/component/profile/cachefile/cache.go b/component/profile/cachefile/cache.go index f5101f5b..7b4cdfc2 100644 --- a/component/profile/cachefile/cache.go +++ b/component/profile/cachefile/cache.go @@ -17,9 +17,10 @@ var ( fileMode os.FileMode = 0o666 defaultCache *CacheFile - bucketSelected = []byte("selected") - bucketFakeip = []byte("fakeip") - bucketETag = []byte("etag") + bucketSelected = []byte("selected") + bucketFakeip = []byte("fakeip") + bucketETag = []byte("etag") + bucketSubscriptionInfo = []byte("subscriptioninfo") ) // CacheFile store and update the cache file diff --git a/component/profile/cachefile/subscriptioninfo.go b/component/profile/cachefile/subscriptioninfo.go new file mode 100644 index 00000000..c68f92eb --- /dev/null +++ b/component/profile/cachefile/subscriptioninfo.go @@ -0,0 +1,41 @@ +package cachefile + +import ( + "github.com/metacubex/mihomo/log" + + "github.com/metacubex/bbolt" +) + +func (c *CacheFile) SetSubscriptionInfo(name string, userInfo string) { + if c.DB == nil { + return + } + + err := c.DB.Batch(func(t *bbolt.Tx) error { + bucket, err := t.CreateBucketIfNotExists(bucketSubscriptionInfo) + if err != nil { + return err + } + + return bucket.Put([]byte(name), []byte(userInfo)) + }) + if err != nil { + log.Warnln("[CacheFile] write cache to %s failed: %s", c.DB.Path(), err.Error()) + return + } +} +func (c *CacheFile) GetSubscriptionInfo(name string) (userInfo string) { + if c.DB == nil { + return + } + c.DB.View(func(t *bbolt.Tx) error { + if bucket := t.Bucket(bucketSubscriptionInfo); bucket != nil { + if v := bucket.Get([]byte(name)); v != nil { + userInfo = string(v) + } + } + return nil + }) + + return +} diff --git a/component/resource/vehicle.go b/component/resource/vehicle.go index a9382329..7c3cb1c2 100644 --- a/component/resource/vehicle.go +++ b/component/resource/vehicle.go @@ -84,11 +84,12 @@ func NewFileVehicle(path string) *FileVehicle { } type HTTPVehicle struct { - url string - path string - proxy string - header http.Header - timeout time.Duration + url string + path string + proxy string + header http.Header + timeout time.Duration + provider types.ProxyProvider } func (h *HTTPVehicle) Url() string { @@ -111,6 +112,10 @@ func (h *HTTPVehicle) Write(buf []byte) error { return safeWrite(h.path, buf) } +func (h *HTTPVehicle) SetProvider(provider types.ProxyProvider) { + h.provider = provider +} + func (h *HTTPVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []byte, hash utils.HashType, err error) { ctx, cancel := context.WithTimeout(ctx, h.timeout) defer cancel() @@ -133,6 +138,12 @@ func (h *HTTPVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []b return } defer resp.Body.Close() + + if subscriptionInfo := resp.Header.Get("subscription-userinfo"); h.provider != nil && subscriptionInfo != "" { + cachefile.Cache().SetSubscriptionInfo(h.provider.Name(), subscriptionInfo) + h.provider.SetSubscriptionInfo(subscriptionInfo) + } + if resp.StatusCode < 200 || resp.StatusCode > 299 { if setIfNoneMatch && resp.StatusCode == http.StatusNotModified { return nil, oldHash, nil diff --git a/constant/adapters.go b/constant/adapters.go index b303eb84..c7b73a06 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -213,6 +213,8 @@ func (at AdapterType) String() string { return "WireGuard" case Tuic: return "Tuic" + case Ssh: + return "Ssh" case Relay: return "Relay" @@ -224,8 +226,6 @@ func (at AdapterType) String() string { return "URLTest" case LoadBalance: return "LoadBalance" - case Ssh: - return "Ssh" default: return "Unknown" } diff --git a/constant/provider/interface.go b/constant/provider/interface.go index 065b801a..925c1734 100644 --- a/constant/provider/interface.go +++ b/constant/provider/interface.go @@ -81,6 +81,7 @@ type ProxyProvider interface { Version() uint32 RegisterHealthCheckTask(url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint) HealthCheckURL() string + SetSubscriptionInfo(userInfo string) } // RuleProvider interface From b9171ade7f91b19388522e8338f401fba734ac97 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 21 Oct 2024 09:15:57 +0800 Subject: [PATCH 266/311] chore: update sing-tun to v0.4.0-rc.4 --- go.mod | 6 +++--- go.sum | 8 ++++---- listener/sing_tun/server.go | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index a223cf77..67afd143 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 github.com/metacubex/sing-shadowsocks v0.2.8 github.com/metacubex/sing-shadowsocks2 v0.2.2 - github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 + github.com/metacubex/sing-tun v0.2.7-0.20241021011113-857bcd6ee47c github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa @@ -39,7 +39,7 @@ require ( github.com/sagernet/cors v1.2.1 github.com/sagernet/fswatch v0.1.1 github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a - github.com/sagernet/sing v0.5.0-alpha.13 + github.com/sagernet/sing v0.5.0-rc.4 github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 github.com/sagernet/sing-shadowtls v0.1.4 github.com/samber/lo v1.47.0 @@ -116,4 +116,4 @@ require ( golang.org/x/tools v0.24.0 // indirect ) -replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297 +replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20241021005542-18b67490300a diff --git a/go.sum b/go.sum index 2b3bba65..b43d779e 100644 --- a/go.sum +++ b/go.sum @@ -108,16 +108,16 @@ github.com/metacubex/quic-go v0.47.1-0.20240909010619-6b38f24bfcc4 h1:CgdUBRxmNl github.com/metacubex/quic-go v0.47.1-0.20240909010619-6b38f24bfcc4/go.mod h1:Y7yRGqFE6UQL/3aKPYmiYdjfVkeujJaStP4+jiZMcN8= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= -github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297 h1:YG/JkwGPbca5rUtEMHIu8ZuqzR7BSVm1iqY8hNoMeMA= -github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/metacubex/sing v0.0.0-20241021005542-18b67490300a h1:JuR0/7RDxQtlZp/GOzrdqNq04HplTxavPRHrek8ouJk= +github.com/metacubex/sing v0.0.0-20241021005542-18b67490300a/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 h1:HobpULaPK6OoxrHMmgcwLkwwIduXVmwdcznwUfH1GQM= github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8= github.com/metacubex/sing-shadowsocks v0.2.8 h1:wIhlaigswzjPw4hej75sEvWte3QR0+AJRafgwBHO5B4= github.com/metacubex/sing-shadowsocks v0.2.8/go.mod h1:X3x88XtJpBxG0W0/ECOJL6Ib0SJ3xdniAkU/6/RMWU0= github.com/metacubex/sing-shadowsocks2 v0.2.2 h1:eaf42uVx4Lr21S6MDYs0ZdTvGA0GEhDpb9no4+gdXPo= github.com/metacubex/sing-shadowsocks2 v0.2.2/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q= -github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 h1:ypfofGDZbP8p3Y4P/m74JYu7sQViesi3c8nbmT6cS0Y= -github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1/go.mod h1:olbEx9yVcaw5tHTNlRamRoxmMKcvDvcVS1YLnQGzvWE= +github.com/metacubex/sing-tun v0.2.7-0.20241021011113-857bcd6ee47c h1:qfUZ8xBrViOCZamvcC8CyV7Ii8sAUrn7RqZxFGn56tQ= +github.com/metacubex/sing-tun v0.2.7-0.20241021011113-857bcd6ee47c/go.mod h1:lCrP0AW7ieKnXG1JEeZLW+9h99QzjuOX0MfCQfz6TgE= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosqY8xKDp3pqTW3qbrCprZ1l6WkrXSFSCwyY4I= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 h1:xg71VmzLS6ByAzi/57phwDvjE+dLLs+ozH00k4DnOns= diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 79856c46..ad3d113e 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -279,7 +279,7 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis return } - defaultInterfaceMonitor, err = tun.NewDefaultInterfaceMonitor(networkUpdateMonitor, log.SingLogger, tun.DefaultInterfaceMonitorOptions{OverrideAndroidVPN: true}) + defaultInterfaceMonitor, err = tun.NewDefaultInterfaceMonitor(networkUpdateMonitor, log.SingLogger, tun.DefaultInterfaceMonitorOptions{InterfaceFinder: interfaceFinder, OverrideAndroidVPN: true}) if err != nil { err = E.Cause(err, "create DefaultInterfaceMonitor") return From 3e966e82c793ca99e3badc84bf3f2907b100edae Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 21 Oct 2024 09:38:21 +0800 Subject: [PATCH 267/311] chore: update quic-go to 0.48.0 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 67afd143..09803062 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 github.com/metacubex/chacha v0.1.0 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 - github.com/metacubex/quic-go v0.47.1-0.20240909010619-6b38f24bfcc4 + github.com/metacubex/quic-go v0.48.1-0.20241021013658-51ca987e0174 github.com/metacubex/randv2 v0.2.0 github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 github.com/metacubex/sing-shadowsocks v0.2.8 diff --git a/go.sum b/go.sum index b43d779e..0508ac59 100644 --- a/go.sum +++ b/go.sum @@ -104,8 +104,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-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= -github.com/metacubex/quic-go v0.47.1-0.20240909010619-6b38f24bfcc4 h1:CgdUBRxmNlxEGkp35HwvgQ10jwOOUJKWdOxpi8yWi8o= -github.com/metacubex/quic-go v0.47.1-0.20240909010619-6b38f24bfcc4/go.mod h1:Y7yRGqFE6UQL/3aKPYmiYdjfVkeujJaStP4+jiZMcN8= +github.com/metacubex/quic-go v0.48.1-0.20241021013658-51ca987e0174 h1:GvigRPEU+cbnzdLWne47cxLrc28Abohl3ECtVdnrbq0= +github.com/metacubex/quic-go v0.48.1-0.20241021013658-51ca987e0174/go.mod h1:AiZ+UPgrkO1DTnmiAX4b+kRoV1Vfc65UkYD7RbFlIZA= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= github.com/metacubex/sing v0.0.0-20241021005542-18b67490300a h1:JuR0/7RDxQtlZp/GOzrdqNq04HplTxavPRHrek8ouJk= From a86c5628526934892b24c4667af646fc61c3d4f5 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Mon, 4 Nov 2024 19:31:43 +0800 Subject: [PATCH 268/311] chore: Increase support for other format of ASN --- component/mmdb/reader.go | 26 +++++++++++++++++++++----- rules/common/ipasn.go | 13 ++++--------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/component/mmdb/reader.go b/component/mmdb/reader.go index e76e9939..42e500c9 100644 --- a/component/mmdb/reader.go +++ b/component/mmdb/reader.go @@ -5,6 +5,7 @@ import ( "net" "strings" + "github.com/metacubex/mihomo/log" "github.com/oschwald/maxminddb-golang" ) @@ -23,11 +24,16 @@ type ASNReader struct { *maxminddb.Reader } -type ASNResult struct { +type GeoLite2 struct { AutonomousSystemNumber uint32 `maxminddb:"autonomous_system_number"` AutonomousSystemOrganization string `maxminddb:"autonomous_system_organization"` } +type IPInfo struct { + ASN string `maxminddb:"asn"` + Name string `maxminddb:"name"` +} + func (r IPReader) LookupCode(ipAddress net.IP) []string { switch r.databaseType { case typeMaxmind: @@ -66,8 +72,18 @@ func (r IPReader) LookupCode(ipAddress net.IP) []string { } } -func (r ASNReader) LookupASN(ip net.IP) ASNResult { - var result ASNResult - r.Lookup(ip, &result) - return result +func (r ASNReader) LookupASN(ip net.IP) (string, string) { + switch r.Metadata.DatabaseType { + case "GeoLite2-ASN", "DBIP-ASN-Lite (compat=GeoLite2-ASN)": + var result GeoLite2 + _ = r.Lookup(ip, &result) + return fmt.Sprint(result.AutonomousSystemNumber), result.AutonomousSystemOrganization + case "ipinfo generic_asn_free.mmdb": + var result IPInfo + _ = r.Lookup(ip, &result) + return result.ASN[2:], result.Name + default: + log.Warnln("Unsupported ASN type: %s", r.Metadata.DatabaseType) + } + return "", "" } diff --git a/rules/common/ipasn.go b/rules/common/ipasn.go index 813923ac..7d554103 100644 --- a/rules/common/ipasn.go +++ b/rules/common/ipasn.go @@ -1,8 +1,6 @@ package common import ( - "strconv" - "github.com/metacubex/mihomo/component/geodata" "github.com/metacubex/mihomo/component/mmdb" C "github.com/metacubex/mihomo/constant" @@ -26,17 +24,14 @@ func (a *ASN) Match(metadata *C.Metadata) (bool, string) { return false, "" } - result := mmdb.ASNInstance().LookupASN(ip.AsSlice()) - asnNumber := strconv.FormatUint(uint64(result.AutonomousSystemNumber), 10) - ipASN := asnNumber + " " + result.AutonomousSystemOrganization + asn, aso := mmdb.ASNInstance().LookupASN(ip.AsSlice()) if a.isSourceIP { - metadata.SrcIPASN = ipASN + metadata.SrcIPASN = asn + " " + aso } else { - metadata.DstIPASN = ipASN + metadata.DstIPASN = asn + " " + aso } - match := a.asn == asnNumber - return match, a.adapter + return a.asn == asn, a.adapter } func (a *ASN) RuleType() C.RuleType { From fabd216c349cf79291e8d2f01d4b11c7e83e3819 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 5 Nov 2024 08:58:41 +0800 Subject: [PATCH 269/311] chore: update quic-go to 0.48.1 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 09803062..1278b0ed 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 github.com/metacubex/chacha v0.1.0 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 - github.com/metacubex/quic-go v0.48.1-0.20241021013658-51ca987e0174 + github.com/metacubex/quic-go v0.48.2-0.20241105005628-a3e65bac65b2 github.com/metacubex/randv2 v0.2.0 github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 github.com/metacubex/sing-shadowsocks v0.2.8 diff --git a/go.sum b/go.sum index 0508ac59..0139ca20 100644 --- a/go.sum +++ b/go.sum @@ -104,8 +104,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-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= -github.com/metacubex/quic-go v0.48.1-0.20241021013658-51ca987e0174 h1:GvigRPEU+cbnzdLWne47cxLrc28Abohl3ECtVdnrbq0= -github.com/metacubex/quic-go v0.48.1-0.20241021013658-51ca987e0174/go.mod h1:AiZ+UPgrkO1DTnmiAX4b+kRoV1Vfc65UkYD7RbFlIZA= +github.com/metacubex/quic-go v0.48.2-0.20241105005628-a3e65bac65b2 h1:1prpWzQnhN/LgGTMA6nz86MGcppDUOwfRkhxPOnrzAk= +github.com/metacubex/quic-go v0.48.2-0.20241105005628-a3e65bac65b2/go.mod h1:AiZ+UPgrkO1DTnmiAX4b+kRoV1Vfc65UkYD7RbFlIZA= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= github.com/metacubex/sing v0.0.0-20241021005542-18b67490300a h1:JuR0/7RDxQtlZp/GOzrdqNq04HplTxavPRHrek8ouJk= From e6d1c8cedf55c3d868e1791f4ed6a76d4156e1a0 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 5 Nov 2024 09:12:20 +0800 Subject: [PATCH 270/311] chore: update sing-tun to v0.4.0-rc.5 --- component/iface/iface.go | 2 ++ go.mod | 6 +++--- go.sum | 8 ++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/component/iface/iface.go b/component/iface/iface.go index 272ee737..a0fa4d5b 100644 --- a/component/iface/iface.go +++ b/component/iface/iface.go @@ -15,6 +15,7 @@ type Interface struct { Name string Addresses []netip.Prefix HardwareAddr net.HardwareAddr + Flags net.Flags } var ( @@ -66,6 +67,7 @@ func Interfaces() (map[string]*Interface, error) { Name: iface.Name, Addresses: ipNets, HardwareAddr: iface.HardwareAddr, + Flags: iface.Flags, } } diff --git a/go.mod b/go.mod index 1278b0ed..f62556c3 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 github.com/metacubex/sing-shadowsocks v0.2.8 github.com/metacubex/sing-shadowsocks2 v0.2.2 - github.com/metacubex/sing-tun v0.2.7-0.20241021011113-857bcd6ee47c + github.com/metacubex/sing-tun v0.2.7-0.20241105010444-9ece61968014 github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa @@ -39,7 +39,7 @@ require ( github.com/sagernet/cors v1.2.1 github.com/sagernet/fswatch v0.1.1 github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a - github.com/sagernet/sing v0.5.0-rc.4 + github.com/sagernet/sing v0.5.0 github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 github.com/sagernet/sing-shadowtls v0.1.4 github.com/samber/lo v1.47.0 @@ -116,4 +116,4 @@ require ( golang.org/x/tools v0.24.0 // indirect ) -replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20241021005542-18b67490300a +replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20241105005934-13bf5e941908 diff --git a/go.sum b/go.sum index 0139ca20..8d75ee4a 100644 --- a/go.sum +++ b/go.sum @@ -108,16 +108,16 @@ github.com/metacubex/quic-go v0.48.2-0.20241105005628-a3e65bac65b2 h1:1prpWzQnhN github.com/metacubex/quic-go v0.48.2-0.20241105005628-a3e65bac65b2/go.mod h1:AiZ+UPgrkO1DTnmiAX4b+kRoV1Vfc65UkYD7RbFlIZA= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= -github.com/metacubex/sing v0.0.0-20241021005542-18b67490300a h1:JuR0/7RDxQtlZp/GOzrdqNq04HplTxavPRHrek8ouJk= -github.com/metacubex/sing v0.0.0-20241021005542-18b67490300a/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/metacubex/sing v0.0.0-20241105005934-13bf5e941908 h1:cZYdGEQKfLsw//TI7dk9bdplz48zitpEDbDGusB9d40= +github.com/metacubex/sing v0.0.0-20241105005934-13bf5e941908/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 h1:HobpULaPK6OoxrHMmgcwLkwwIduXVmwdcznwUfH1GQM= github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8= github.com/metacubex/sing-shadowsocks v0.2.8 h1:wIhlaigswzjPw4hej75sEvWte3QR0+AJRafgwBHO5B4= github.com/metacubex/sing-shadowsocks v0.2.8/go.mod h1:X3x88XtJpBxG0W0/ECOJL6Ib0SJ3xdniAkU/6/RMWU0= github.com/metacubex/sing-shadowsocks2 v0.2.2 h1:eaf42uVx4Lr21S6MDYs0ZdTvGA0GEhDpb9no4+gdXPo= github.com/metacubex/sing-shadowsocks2 v0.2.2/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q= -github.com/metacubex/sing-tun v0.2.7-0.20241021011113-857bcd6ee47c h1:qfUZ8xBrViOCZamvcC8CyV7Ii8sAUrn7RqZxFGn56tQ= -github.com/metacubex/sing-tun v0.2.7-0.20241021011113-857bcd6ee47c/go.mod h1:lCrP0AW7ieKnXG1JEeZLW+9h99QzjuOX0MfCQfz6TgE= +github.com/metacubex/sing-tun v0.2.7-0.20241105010444-9ece61968014 h1:5zAAcCCyuorvkXu3su4VmUkYDXOdzvRKPGEdeyc+ljY= +github.com/metacubex/sing-tun v0.2.7-0.20241105010444-9ece61968014/go.mod h1:GRcrj7VnhvYFsS34cv0J2qTVm5h9DvQuGwliVyVLVvE= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosqY8xKDp3pqTW3qbrCprZ1l6WkrXSFSCwyY4I= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 h1:xg71VmzLS6ByAzi/57phwDvjE+dLLs+ozH00k4DnOns= From 69454b030ee9500d65fcb258adf28948deb7f7d7 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 5 Nov 2024 09:15:30 +0800 Subject: [PATCH 271/311] chore: allow disabled overrideAndroidVPN by environment variable `DISABLE_OVERRIDE_ANDROID_VPN` --- listener/sing_tun/server.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index ad3d113e..ba337b01 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -279,7 +279,11 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis return } - defaultInterfaceMonitor, err = tun.NewDefaultInterfaceMonitor(networkUpdateMonitor, log.SingLogger, tun.DefaultInterfaceMonitorOptions{InterfaceFinder: interfaceFinder, OverrideAndroidVPN: true}) + overrideAndroidVPN := true + if disable, _ := strconv.ParseBool(os.Getenv("DISABLE_OVERRIDE_ANDROID_VPN")); disable { + overrideAndroidVPN = false + } + defaultInterfaceMonitor, err = tun.NewDefaultInterfaceMonitor(networkUpdateMonitor, log.SingLogger, tun.DefaultInterfaceMonitorOptions{InterfaceFinder: interfaceFinder, OverrideAndroidVPN: overrideAndroidVPN}) if err != nil { err = E.Cause(err, "create DefaultInterfaceMonitor") return From d4478dbfa29328a62f1ce94d98e2e68490bcaa55 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 5 Nov 2024 09:29:01 +0800 Subject: [PATCH 272/311] chore: reduce the performance overhead of not enabling LoopBackDetector --- adapter/outbound/direct.go | 18 ++++-------------- component/loopback/detector.go | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index 9ee237fa..dbde5593 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -3,18 +3,12 @@ package outbound import ( "context" "errors" - "os" - "strconv" - "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/loopback" "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/constant/features" ) -var DisableLoopBackDetector, _ = strconv.ParseBool(os.Getenv("DISABLE_LOOPBACK_DETECTOR")) - type Direct struct { *Base loopBack *loopback.Detector @@ -27,10 +21,8 @@ type DirectOption struct { // DialContext implements C.ProxyAdapter func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { - if !features.CMFA && !DisableLoopBackDetector { - if err := d.loopBack.CheckConn(metadata); err != nil { - return nil, err - } + if err := d.loopBack.CheckConn(metadata); err != nil { + return nil, err } opts = append(opts, dialer.WithResolver(resolver.DirectHostResolver)) c, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress(), d.Base.DialOptions(opts...)...) @@ -42,10 +34,8 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ... // ListenPacketContext implements C.ProxyAdapter func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { - if !features.CMFA && !DisableLoopBackDetector { - if err := d.loopBack.CheckPacketConn(metadata); err != nil { - return nil, err - } + if err := d.loopBack.CheckPacketConn(metadata); err != nil { + return nil, err } // net.UDPConn.WriteTo only working with *net.UDPAddr, so we need a net.UDPAddr if !metadata.Resolved() { diff --git a/component/loopback/detector.go b/component/loopback/detector.go index 8ec96a9d..c639ab22 100644 --- a/component/loopback/detector.go +++ b/component/loopback/detector.go @@ -4,14 +4,25 @@ import ( "errors" "fmt" "net/netip" + "os" + "strconv" "github.com/metacubex/mihomo/common/callback" "github.com/metacubex/mihomo/component/iface" C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/constant/features" "github.com/puzpuzpuz/xsync/v3" ) +var disableLoopBackDetector, _ = strconv.ParseBool(os.Getenv("DISABLE_LOOPBACK_DETECTOR")) + +func init() { + if features.CMFA { + disableLoopBackDetector = true + } +} + var ErrReject = errors.New("reject loopback connection") type Detector struct { @@ -20,6 +31,9 @@ type Detector struct { } func NewDetector() *Detector { + if disableLoopBackDetector { + return nil + } return &Detector{ connMap: xsync.NewMapOf[netip.AddrPort, struct{}](), packetConnMap: xsync.NewMapOf[uint16, struct{}](), @@ -27,6 +41,9 @@ func NewDetector() *Detector { } func (l *Detector) NewConn(conn C.Conn) C.Conn { + if l == nil || l.connMap == nil { + return conn + } metadata := C.Metadata{} if metadata.SetRemoteAddr(conn.LocalAddr()) != nil { return conn @@ -42,6 +59,9 @@ func (l *Detector) NewConn(conn C.Conn) C.Conn { } func (l *Detector) NewPacketConn(conn C.PacketConn) C.PacketConn { + if l == nil || l.packetConnMap == nil { + return conn + } metadata := C.Metadata{} if metadata.SetRemoteAddr(conn.LocalAddr()) != nil { return conn @@ -58,6 +78,9 @@ func (l *Detector) NewPacketConn(conn C.PacketConn) C.PacketConn { } func (l *Detector) CheckConn(metadata *C.Metadata) error { + if l == nil || l.connMap == nil { + return nil + } connAddr := metadata.SourceAddrPort() if !connAddr.IsValid() { return nil @@ -69,6 +92,9 @@ func (l *Detector) CheckConn(metadata *C.Metadata) error { } func (l *Detector) CheckPacketConn(metadata *C.Metadata) error { + if l == nil || l.packetConnMap == nil { + return nil + } connAddr := metadata.SourceAddrPort() if !connAddr.IsValid() { return nil From ce52c3438bf5c09f1fe58f6034a9ff7ee4ac3c25 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 5 Nov 2024 10:03:21 +0800 Subject: [PATCH 273/311] chore: cleaned up some confusing code --- tunnel/statistic/tracker.go | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/tunnel/statistic/tracker.go b/tunnel/statistic/tracker.go index 0bf7995d..ca592d97 100644 --- a/tunnel/statistic/tracker.go +++ b/tunnel/statistic/tracker.go @@ -117,24 +117,19 @@ func (tt *tcpTracker) Upstream() any { } func parseRemoteDestination(addr net.Addr, conn C.Connection) string { - if addr == nil && conn != nil { - return conn.RemoteDestination() - } - if addrPort, err := netip.ParseAddrPort(addr.String()); err == nil && addrPort.Addr().IsValid() { - return addrPort.Addr().String() - } else { - if conn != nil { - return conn.RemoteDestination() - } else { - return "" + if addr != nil { + if addrPort, err := netip.ParseAddrPort(addr.String()); err == nil && addrPort.Addr().IsValid() { + return addrPort.Addr().String() } } + if conn != nil { + return conn.RemoteDestination() + } + return "" } func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.Rule, uploadTotal int64, downloadTotal int64, pushToManager bool) *tcpTracker { - if conn != nil { - metadata.RemoteDst = parseRemoteDestination(conn.RemoteAddr(), conn) - } + metadata.RemoteDst = parseRemoteDestination(conn.RemoteAddr(), conn) t := &tcpTracker{ Conn: conn, From 91d54bdac142f1b7bb0eff2859d1b6c12a7a33ef Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 6 Nov 2024 19:45:39 +0800 Subject: [PATCH 274/311] fix: android tun start error --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f62556c3..ff870121 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 github.com/metacubex/sing-shadowsocks v0.2.8 github.com/metacubex/sing-shadowsocks2 v0.2.2 - github.com/metacubex/sing-tun v0.2.7-0.20241105010444-9ece61968014 + github.com/metacubex/sing-tun v0.2.7-0.20241106120309-53606a70db98 github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa diff --git a/go.sum b/go.sum index 8d75ee4a..d7b3c640 100644 --- a/go.sum +++ b/go.sum @@ -116,8 +116,8 @@ github.com/metacubex/sing-shadowsocks v0.2.8 h1:wIhlaigswzjPw4hej75sEvWte3QR0+AJ github.com/metacubex/sing-shadowsocks v0.2.8/go.mod h1:X3x88XtJpBxG0W0/ECOJL6Ib0SJ3xdniAkU/6/RMWU0= github.com/metacubex/sing-shadowsocks2 v0.2.2 h1:eaf42uVx4Lr21S6MDYs0ZdTvGA0GEhDpb9no4+gdXPo= github.com/metacubex/sing-shadowsocks2 v0.2.2/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q= -github.com/metacubex/sing-tun v0.2.7-0.20241105010444-9ece61968014 h1:5zAAcCCyuorvkXu3su4VmUkYDXOdzvRKPGEdeyc+ljY= -github.com/metacubex/sing-tun v0.2.7-0.20241105010444-9ece61968014/go.mod h1:GRcrj7VnhvYFsS34cv0J2qTVm5h9DvQuGwliVyVLVvE= +github.com/metacubex/sing-tun v0.2.7-0.20241106120309-53606a70db98 h1:vW0QDrzUc4k1yi3A76lDW064zonPj880QFcpTD58u7A= +github.com/metacubex/sing-tun v0.2.7-0.20241106120309-53606a70db98/go.mod h1:GRcrj7VnhvYFsS34cv0J2qTVm5h9DvQuGwliVyVLVvE= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosqY8xKDp3pqTW3qbrCprZ1l6WkrXSFSCwyY4I= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 h1:xg71VmzLS6ByAzi/57phwDvjE+dLLs+ozH00k4DnOns= From 215bf0995f5427dba0a5833e2ece115a0b4e6a5e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 8 Nov 2024 09:40:38 +0800 Subject: [PATCH 275/311] chore: switch syscall.SyscallN back to syscall.Syscall6 Until the current version, SyscallN always escapes the variadic argument --- component/process/process_windows.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/component/process/process_windows.go b/component/process/process_windows.go index f8cd00d8..b48a6430 100644 --- a/component/process/process_windows.go +++ b/component/process/process_windows.go @@ -180,7 +180,7 @@ func newSearcher(isV4, isTCP bool) *searcher { func getTransportTable(fn uintptr, family int, class int) ([]byte, error) { for size, buf := uint32(8), make([]byte, 8); ; { ptr := unsafe.Pointer(&buf[0]) - err, _, _ := syscall.SyscallN(fn, uintptr(ptr), uintptr(unsafe.Pointer(&size)), 0, uintptr(family), uintptr(class), 0) + err, _, _ := syscall.Syscall6(fn, uintptr(ptr), uintptr(unsafe.Pointer(&size)), 0, uintptr(family), uintptr(class), 0, 0) switch err { case 0: @@ -215,12 +215,15 @@ func getExecPathFromPID(pid uint32) (string, error) { buf := make([]uint16, syscall.MAX_LONG_PATH) size := uint32(len(buf)) - r1, _, err := syscall.SyscallN( + r1, _, err := syscall.Syscall6( queryProcName, uintptr(h), uintptr(0), uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&size)), + 0, + 0, + 0, ) if r1 == 0 { return "", err From 792f16265e3845b7922350d59ced224ac35b72c9 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:29:32 +0800 Subject: [PATCH 276/311] fix: find process panic --- component/process/process_windows.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/component/process/process_windows.go b/component/process/process_windows.go index b48a6430..73ac0255 100644 --- a/component/process/process_windows.go +++ b/component/process/process_windows.go @@ -180,7 +180,7 @@ func newSearcher(isV4, isTCP bool) *searcher { func getTransportTable(fn uintptr, family int, class int) ([]byte, error) { for size, buf := uint32(8), make([]byte, 8); ; { ptr := unsafe.Pointer(&buf[0]) - err, _, _ := syscall.Syscall6(fn, uintptr(ptr), uintptr(unsafe.Pointer(&size)), 0, uintptr(family), uintptr(class), 0, 0) + err, _, _ := syscall.Syscall6(fn, 6, uintptr(ptr), uintptr(unsafe.Pointer(&size)), 0, uintptr(family), uintptr(class), 0) switch err { case 0: @@ -216,15 +216,12 @@ func getExecPathFromPID(pid uint32) (string, error) { buf := make([]uint16, syscall.MAX_LONG_PATH) size := uint32(len(buf)) r1, _, err := syscall.Syscall6( - queryProcName, + queryProcName, 4, uintptr(h), uintptr(0), uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&size)), - 0, - 0, - 0, - ) + 0, 0) if r1 == 0 { return "", err } From de19f927e86573a7a97159e3b9925049f06d4563 Mon Sep 17 00:00:00 2001 From: Chenx Dust Date: Tue, 12 Nov 2024 07:58:24 +0000 Subject: [PATCH 277/311] chore: restful api display smux and mptcp --- adapter/adapter.go | 2 ++ adapter/outbound/base.go | 10 ++++++++++ adapter/outbound/singmux.go | 4 ++++ adapter/outbound/wireguard.go | 14 ++++++++++++++ constant/adapters.go | 2 ++ 5 files changed, 32 insertions(+) diff --git a/adapter/adapter.go b/adapter/adapter.go index 6de7fb92..411069fb 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -165,6 +165,8 @@ func (p *Proxy) MarshalJSON() ([]byte, error) { mapping["udp"] = p.SupportUDP() mapping["xudp"] = p.SupportXUDP() mapping["tfo"] = p.SupportTFO() + mapping["mptcp"] = p.SupportMPTCP() + mapping["smux"] = p.SupportSMUX() return json.Marshal(mapping) } diff --git a/adapter/outbound/base.go b/adapter/outbound/base.go index ae8c4651..93e8d979 100644 --- a/adapter/outbound/base.go +++ b/adapter/outbound/base.go @@ -95,6 +95,16 @@ func (b *Base) SupportTFO() bool { return b.tfo } +// SupportMPTCP implements C.ProxyAdapter +func (b *Base) SupportMPTCP() bool { + return b.mpTcp +} + +// SupportSMUX implements C.ProxyAdapter +func (b *Base) SupportSMUX() bool { + return false +} + // IsL3Protocol implements C.ProxyAdapter func (b *Base) IsL3Protocol(metadata *C.Metadata) bool { return false diff --git a/adapter/outbound/singmux.go b/adapter/outbound/singmux.go index 67267744..5cc4e748 100644 --- a/adapter/outbound/singmux.go +++ b/adapter/outbound/singmux.go @@ -97,6 +97,10 @@ func (s *SingMux) SupportUOT() bool { return true } +func (s *SingMux) SupportSMUX() bool { + return true +} + func closeSingMux(s *SingMux) { _ = s.client.Close() } diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 03145c37..ca732baa 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -625,6 +625,20 @@ func (r *refProxyAdapter) SupportTFO() bool { return false } +func (r *refProxyAdapter) SupportMPTCP() bool { + if r.proxyAdapter != nil { + return r.proxyAdapter.SupportMPTCP() + } + return false +} + +func (r *refProxyAdapter) SupportSMUX() bool { + if r.proxyAdapter != nil { + return r.proxyAdapter.SupportSMUX() + } + return false +} + func (r *refProxyAdapter) MarshalJSON() ([]byte, error) { if r.proxyAdapter != nil { return r.proxyAdapter.MarshalJSON() diff --git a/constant/adapters.go b/constant/adapters.go index c7b73a06..2bbcffba 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -106,6 +106,8 @@ type ProxyAdapter interface { SupportUDP() bool SupportXUDP() bool SupportTFO() bool + SupportMPTCP() bool + SupportSMUX() bool MarshalJSON() ([]byte, error) // Deprecated: use DialContextWithDialer and ListenPacketWithDialer instead. From 25b3c86d31c8a9370b4a7ff6f868c8aeb766f397 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sun, 17 Nov 2024 23:31:46 +0800 Subject: [PATCH 278/311] ci: update loongarch golang and android ndk --- .github/workflows/build.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5ba2ef6f..ee498d3a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -118,16 +118,16 @@ jobs: - name: Set up Go1.22 loongarch abi1 if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '1' }} run: | - wget -q https://github.com/MetaCubeX/loongarch64-golang/releases/download/1.22.4/go1.22.4.linux-amd64-abi1.tar.gz - sudo tar zxf go1.22.4.linux-amd64-abi1.tar.gz -C /usr/local + wget -q https://github.com/MetaCubeX/loongarch64-golang/releases/download/1.23.0/go1.23.0.linux-amd64-abi1.tar.gz + sudo tar zxf go1.23.0.linux-amd64-abi1.tar.gz -C /usr/local echo "/usr/local/go/bin" >> $GITHUB_PATH - - name: Set up Go1.22 loongarch abi2 - if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '2' }} - run: | - wget -q https://github.com/MetaCubeX/loongarch64-golang/releases/download/1.22.4/go1.22.4.linux-amd64-abi2.tar.gz - sudo tar zxf go1.22.4.linux-amd64-abi2.tar.gz -C /usr/local - echo "/usr/local/go/bin" >> $GITHUB_PATH + # - name: Set up Go1.22 loongarch abi2 + # if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '2' }} + # run: | + # wget -q https://github.com/MetaCubeX/loongarch64-golang/releases/download/1.22.4/go1.22.4.linux-amd64-abi2.tar.gz + # sudo tar zxf go1.22.4.linux-amd64-abi2.tar.gz -C /usr/local + # echo "/usr/local/go/bin" >> $GITHUB_PATH # modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557 # this patch file only works on golang1.23.x @@ -194,7 +194,7 @@ jobs: uses: nttld/setup-ndk@v1 id: setup-ndk with: - ndk-version: r27 + ndk-version: r28-beta1 - name: Set NDK path if: ${{ matrix.jobs.goos == 'android' }} From 80e4eaad14638ce6bb851ac3eadee7897059ef04 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 18 Nov 2024 10:34:43 +0800 Subject: [PATCH 279/311] fix: process IPv6 Link-Local address (#1657) --- adapter/inbound/auth.go | 9 +-------- adapter/inbound/ipfilter.go | 24 +++++++----------------- adapter/inbound/util.go | 16 ++++++++++++++++ rules/common/ipcidr.go | 2 +- 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/adapter/inbound/auth.go b/adapter/inbound/auth.go index 984c9bd6..83172746 100644 --- a/adapter/inbound/auth.go +++ b/adapter/inbound/auth.go @@ -34,12 +34,5 @@ func SkipAuthRemoteAddress(addr string) bool { } func skipAuth(addr netip.Addr) bool { - if addr.IsValid() { - for _, prefix := range skipAuthPrefixes { - if prefix.Contains(addr.Unmap()) { - return true - } - } - } - return false + return prefixesContains(skipAuthPrefixes, addr) } diff --git a/adapter/inbound/ipfilter.go b/adapter/inbound/ipfilter.go index 7fa218c1..872d0c85 100644 --- a/adapter/inbound/ipfilter.go +++ b/adapter/inbound/ipfilter.go @@ -31,27 +31,17 @@ func IsRemoteAddrDisAllowed(addr net.Addr) bool { if err := m.SetRemoteAddr(addr); err != nil { return false } - return isAllowed(m.AddrPort().Addr().Unmap()) && !isDisAllowed(m.AddrPort().Addr().Unmap()) + ipAddr := m.AddrPort().Addr() + if ipAddr.IsValid() { + return isAllowed(ipAddr) && !isDisAllowed(ipAddr) + } + return false } func isAllowed(addr netip.Addr) bool { - if addr.IsValid() { - for _, prefix := range lanAllowedIPs { - if prefix.Contains(addr) { - return true - } - } - } - return false + return prefixesContains(lanAllowedIPs, addr) } func isDisAllowed(addr netip.Addr) bool { - if addr.IsValid() { - for _, prefix := range lanDisAllowedIPs { - if prefix.Contains(addr) { - return true - } - } - } - return false + return prefixesContains(lanDisAllowedIPs, addr) } diff --git a/adapter/inbound/util.go b/adapter/inbound/util.go index 743337fc..3bcd2808 100644 --- a/adapter/inbound/util.go +++ b/adapter/inbound/util.go @@ -61,3 +61,19 @@ func parseHTTPAddr(request *http.Request) *C.Metadata { return metadata } + +func prefixesContains(prefixes []netip.Prefix, addr netip.Addr) bool { + if len(prefixes) == 0 { + return false + } + if !addr.IsValid() { + return false + } + addr = addr.Unmap().WithZone("") // netip.Prefix.Contains returns false if ip has an IPv6 zone + for _, prefix := range prefixes { + if prefix.Contains(addr) { + return true + } + } + return false +} diff --git a/rules/common/ipcidr.go b/rules/common/ipcidr.go index 663c9397..9c159502 100644 --- a/rules/common/ipcidr.go +++ b/rules/common/ipcidr.go @@ -40,7 +40,7 @@ func (i *IPCIDR) Match(metadata *C.Metadata) (bool, string) { if i.isSourceIP { ip = metadata.SrcIP } - return ip.IsValid() && i.ipnet.Contains(ip), i.adapter + return ip.IsValid() && i.ipnet.Contains(ip.WithZone("")), i.adapter } func (i *IPCIDR) Adapter() string { From 671d901ee24b580464be5d0d33a8e7fbaade8a10 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 18 Nov 2024 10:41:15 +0800 Subject: [PATCH 280/311] ci: align loongarch golang version when it is not abi1 --- .github/workflows/build.yml | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ee498d3a..56c7b66d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -104,31 +104,24 @@ jobs: - uses: actions/checkout@v4 - name: Set up Go - if: ${{ matrix.jobs.goversion == '' && matrix.jobs.goarch != 'loong64' }} + if: ${{ matrix.jobs.goversion == '' && matrix.jobs.abi != '1' }} uses: actions/setup-go@v5 with: go-version: '1.23' - name: Set up Go - if: ${{ matrix.jobs.goversion != '' && matrix.jobs.goarch != 'loong64' }} + if: ${{ matrix.jobs.goversion != '' && matrix.jobs.abi != '1' }} uses: actions/setup-go@v5 with: go-version: ${{ matrix.jobs.goversion }} - - name: Set up Go1.22 loongarch abi1 + - name: Set up Go1.23 loongarch abi1 if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '1' }} run: | wget -q https://github.com/MetaCubeX/loongarch64-golang/releases/download/1.23.0/go1.23.0.linux-amd64-abi1.tar.gz sudo tar zxf go1.23.0.linux-amd64-abi1.tar.gz -C /usr/local echo "/usr/local/go/bin" >> $GITHUB_PATH - # - name: Set up Go1.22 loongarch abi2 - # if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '2' }} - # run: | - # wget -q https://github.com/MetaCubeX/loongarch64-golang/releases/download/1.22.4/go1.22.4.linux-amd64-abi2.tar.gz - # sudo tar zxf go1.22.4.linux-amd64-abi2.tar.gz -C /usr/local - # echo "/usr/local/go/bin" >> $GITHUB_PATH - # modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557 # this patch file only works on golang1.23.x # that means after golang1.24 release it must be changed From 462343531e4d3c954bcdc0b189952e4783b998ca Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 21 Nov 2024 11:06:25 +0800 Subject: [PATCH 281/311] chore: update sing-tun to v0.4.1 --- go.mod | 20 ++++++++++---------- go.sum | 38 +++++++++++++++++++------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/go.mod b/go.mod index ff870121..0b1477ef 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 github.com/metacubex/sing-shadowsocks v0.2.8 github.com/metacubex/sing-shadowsocks2 v0.2.2 - github.com/metacubex/sing-tun v0.2.7-0.20241106120309-53606a70db98 + github.com/metacubex/sing-tun v0.2.7-0.20241121025804-6229062404b6 github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa @@ -39,7 +39,7 @@ require ( github.com/sagernet/cors v1.2.1 github.com/sagernet/fswatch v0.1.1 github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a - github.com/sagernet/sing v0.5.0 + github.com/sagernet/sing v0.5.1 github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 github.com/sagernet/sing-shadowtls v0.1.4 github.com/samber/lo v1.47.0 @@ -51,10 +51,10 @@ require ( gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 go.uber.org/automaxprocs v1.6.0 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba - golang.org/x/crypto v0.28.0 + golang.org/x/crypto v0.29.0 golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // lastest version compatible with golang1.20 - golang.org/x/net v0.30.0 - golang.org/x/sys v0.26.0 + golang.org/x/net v0.31.0 + golang.org/x/sys v0.27.0 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.3.0 @@ -78,7 +78,7 @@ require ( github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/pool v0.2.1 // indirect - github.com/google/btree v1.1.2 // indirect + github.com/google/btree v1.1.3 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/hashicorp/yamux v0.1.1 // indirect @@ -110,10 +110,10 @@ require ( gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect go.uber.org/mock v0.4.0 // indirect golang.org/x/mod v0.20.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/text v0.19.0 // indirect - golang.org/x/time v0.5.0 // indirect + golang.org/x/sync v0.9.0 // indirect + golang.org/x/text v0.20.0 // indirect + golang.org/x/time v0.7.0 // indirect golang.org/x/tools v0.24.0 // indirect ) -replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20241105005934-13bf5e941908 +replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000 diff --git a/go.sum b/go.sum index d7b3c640..71909d46 100644 --- a/go.sum +++ b/go.sum @@ -60,8 +60,8 @@ github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk= github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= -github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -108,16 +108,16 @@ github.com/metacubex/quic-go v0.48.2-0.20241105005628-a3e65bac65b2 h1:1prpWzQnhN github.com/metacubex/quic-go v0.48.2-0.20241105005628-a3e65bac65b2/go.mod h1:AiZ+UPgrkO1DTnmiAX4b+kRoV1Vfc65UkYD7RbFlIZA= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= -github.com/metacubex/sing v0.0.0-20241105005934-13bf5e941908 h1:cZYdGEQKfLsw//TI7dk9bdplz48zitpEDbDGusB9d40= -github.com/metacubex/sing v0.0.0-20241105005934-13bf5e941908/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000 h1:gUbMXcQXhXGj0vCpCVFTUyIH7TMpD1dpTcNv/MCS+ok= +github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 h1:HobpULaPK6OoxrHMmgcwLkwwIduXVmwdcznwUfH1GQM= github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8= github.com/metacubex/sing-shadowsocks v0.2.8 h1:wIhlaigswzjPw4hej75sEvWte3QR0+AJRafgwBHO5B4= github.com/metacubex/sing-shadowsocks v0.2.8/go.mod h1:X3x88XtJpBxG0W0/ECOJL6Ib0SJ3xdniAkU/6/RMWU0= github.com/metacubex/sing-shadowsocks2 v0.2.2 h1:eaf42uVx4Lr21S6MDYs0ZdTvGA0GEhDpb9no4+gdXPo= github.com/metacubex/sing-shadowsocks2 v0.2.2/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q= -github.com/metacubex/sing-tun v0.2.7-0.20241106120309-53606a70db98 h1:vW0QDrzUc4k1yi3A76lDW064zonPj880QFcpTD58u7A= -github.com/metacubex/sing-tun v0.2.7-0.20241106120309-53606a70db98/go.mod h1:GRcrj7VnhvYFsS34cv0J2qTVm5h9DvQuGwliVyVLVvE= +github.com/metacubex/sing-tun v0.2.7-0.20241121025804-6229062404b6 h1:wGk/RJk/EYoTP5WalEPcVzfjSW/sB90Lav1LNBJYFdk= +github.com/metacubex/sing-tun v0.2.7-0.20241121025804-6229062404b6/go.mod h1:OLVO6D3su3ifZR740ROPHiOmRqRs7LxFY/xLh5SgPi8= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosqY8xKDp3pqTW3qbrCprZ1l6WkrXSFSCwyY4I= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 h1:xg71VmzLS6ByAzi/57phwDvjE+dLLs+ozH00k4DnOns= @@ -230,8 +230,8 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= 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.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= +golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk= golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -240,11 +240,11 @@ golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -261,14 +261,14 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= From eb985b002e5aa61c4c2208646f33180c4fc938f9 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Thu, 21 Nov 2024 22:50:54 +0800 Subject: [PATCH 282/311] chore: restful api displays more information --- adapter/adapter.go | 4 ++++ adapter/outbound/base.go | 12 ++++++++++++ adapter/outbound/http.go | 5 +++++ adapter/outbound/hysteria.go | 5 +++++ adapter/outbound/hysteria2.go | 5 +++++ adapter/outbound/shadowsocks.go | 5 +++++ adapter/outbound/shadowsocksr.go | 5 +++++ adapter/outbound/snell.go | 7 ++++++- adapter/outbound/socks5.go | 5 +++++ adapter/outbound/ssh.go | 5 +++++ adapter/outbound/trojan.go | 5 +++++ adapter/outbound/tuic.go | 5 +++++ adapter/outbound/vless.go | 5 +++++ adapter/outbound/vmess.go | 4 ++++ adapter/outbound/wireguard.go | 21 +++++++++++++++++++++ constant/adapters.go | 3 +++ 16 files changed, 100 insertions(+), 1 deletion(-) diff --git a/adapter/adapter.go b/adapter/adapter.go index 411069fb..befe2113 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -163,10 +163,14 @@ func (p *Proxy) MarshalJSON() ([]byte, error) { mapping["alive"] = p.alive.Load() mapping["name"] = p.Name() mapping["udp"] = p.SupportUDP() + mapping["uot"] = p.SupportUOT() mapping["xudp"] = p.SupportXUDP() mapping["tfo"] = p.SupportTFO() mapping["mptcp"] = p.SupportMPTCP() mapping["smux"] = p.SupportSMUX() + mapping["interface"] = p.SupportInterface() + mapping["dialer-proxy"] = p.SupportDialerProxy() + mapping["routing-mark"] = p.SupportRoutingMark() return json.Marshal(mapping) } diff --git a/adapter/outbound/base.go b/adapter/outbound/base.go index 93e8d979..c15e7115 100644 --- a/adapter/outbound/base.go +++ b/adapter/outbound/base.go @@ -105,6 +105,18 @@ func (b *Base) SupportSMUX() bool { return false } +func (b *Base) SupportDialerProxy() string { + return "" +} + +func (b *Base) SupportInterface() string { + return b.iface +} + +func (b *Base) SupportRoutingMark() int { + return b.rmark +} + // IsL3Protocol implements C.ProxyAdapter func (b *Base) IsL3Protocol(metadata *C.Metadata) bool { return false diff --git a/adapter/outbound/http.go b/adapter/outbound/http.go index ebb1d67c..0f72f759 100644 --- a/adapter/outbound/http.go +++ b/adapter/outbound/http.go @@ -92,6 +92,11 @@ func (h *Http) SupportWithDialer() C.NetWork { return C.TCP } +// SupportDialerProxy implements C.ProxyAdapter +func (h *Http) SupportDialerProxy() string { + return h.option.DialerProxy +} + func (h *Http) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error { addr := metadata.RemoteAddress() HeaderString := "CONNECT " + addr + " HTTP/1.1\r\n" diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index 55c66c45..964acb96 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -87,6 +87,11 @@ func (h *Hysteria) genHdc(ctx context.Context, opts ...dialer.Option) utils.Pack } } +// SupportDialerProxy implements C.ProxyAdapter +func (h *Hysteria) SupportDialerProxy() string { + return h.option.DialerProxy +} + type HysteriaOption struct { BasicOption Name string `proxy:"name"` diff --git a/adapter/outbound/hysteria2.go b/adapter/outbound/hysteria2.go index c1a255a7..fbf0c2ec 100644 --- a/adapter/outbound/hysteria2.go +++ b/adapter/outbound/hysteria2.go @@ -96,6 +96,11 @@ func closeHysteria2(h *Hysteria2) { } } +// SupportDialerProxy implements C.ProxyAdapter +func (h *Hysteria2) SupportDialerProxy() string { + return h.option.DialerProxy +} + func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) var salamanderPassword string diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 021fbc0a..a7c8f5bb 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -196,6 +196,11 @@ func (ss *ShadowSocks) SupportWithDialer() C.NetWork { return C.ALLNet } +// SupportDialerProxy implements C.ProxyAdapter +func (ss *ShadowSocks) SupportDialerProxy() string { + return ss.option.DialerProxy +} + // ListenPacketOnStreamConn implements C.ProxyAdapter func (ss *ShadowSocks) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { if ss.option.UDPOverTCP { diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index 437695b4..d217dd16 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -122,6 +122,11 @@ func (ssr *ShadowSocksR) SupportWithDialer() C.NetWork { return C.ALLNet } +// SupportDialerProxy implements C.ProxyAdapter +func (ssr *ShadowSocksR) SupportDialerProxy() string { + return ssr.option.DialerProxy +} + func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) { // SSR protocol compatibility // https://github.com/metacubex/mihomo/pull/2056 diff --git a/adapter/outbound/snell.go b/adapter/outbound/snell.go index f6a4b4f9..7df3dd48 100644 --- a/adapter/outbound/snell.go +++ b/adapter/outbound/snell.go @@ -141,6 +141,11 @@ func (s *Snell) SupportUOT() bool { return true } +// SupportDialerProxy implements C.ProxyAdapter +func (s *Snell) SupportDialerProxy() string { + return s.option.DialerProxy +} + func NewSnell(option SnellOption) (*Snell, error) { addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) psk := []byte(option.Psk) @@ -204,7 +209,7 @@ func NewSnell(option SnellOption) (*Snell, error) { if err != nil { return nil, err } - + return streamConn(c, streamOption{psk, option.Version, addr, obfsOption}), nil }) } diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index 1908167a..1b3f93c5 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -171,6 +171,11 @@ func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, return newPacketConn(&socksPacketConn{PacketConn: pc, rAddr: bindUDPAddr, tcpConn: c}, ss), nil } +// SupportDialerProxy implements C.ProxyAdapter +func (ss *Socks5) SupportDialerProxy() string { + return ss.option.DialerProxy +} + func NewSocks5(option Socks5Option) (*Socks5, error) { var tlsConfig *tls.Config if option.TLS { diff --git a/adapter/outbound/ssh.go b/adapter/outbound/ssh.go index 9e23b463..5ea9b29c 100644 --- a/adapter/outbound/ssh.go +++ b/adapter/outbound/ssh.go @@ -121,6 +121,11 @@ func closeSsh(s *Ssh) { _ = s.client.Close() } +// SupportWithDialer implements C.ProxyAdapter +func (s *Ssh) SupportDialerProxy() string { + return s.option.DialerProxy +} + func NewSsh(option SshOption) (*Ssh, error) { addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index b3a611af..a23a52f3 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -244,6 +244,11 @@ func (t *Trojan) SupportUOT() bool { return true } +// SupportDialerProxy implements C.ProxyAdapter +func (t *Trojan) SupportDialerProxy() string { + return t.option.DialerProxy +} + func NewTrojan(option TrojanOption) (*Trojan, error) { addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index 666e72fa..f2c218db 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -146,6 +146,11 @@ func (t *Tuic) dialWithDialer(ctx context.Context, dialer C.Dialer) (transport * return } +// SupportDialerProxy implements C.ProxyAdapter +func (t *Tuic) SupportDialerProxy() string { + return t.option.DialerProxy +} + func NewTuic(option TuicOption) (*Tuic, error) { addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) serverName := option.Server diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index 79058874..7fb409ae 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -379,6 +379,11 @@ func (v *Vless) SupportUOT() bool { return true } +// SupportDialerProxy implements C.ProxyAdapter +func (v *Vless) SupportDialerProxy() string { + return v.option.DialerProxy +} + func parseVlessAddr(metadata *C.Metadata, xudp bool) *vless.DstAddr { var addrType byte var addr []byte diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 8797374d..a10ac2e1 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -388,6 +388,10 @@ func (v *Vmess) SupportWithDialer() C.NetWork { return C.ALLNet } +func (v *Vmess) SupportDialerProxy() string { + return v.option.DialerProxy +} + // ListenPacketOnStreamConn implements C.ProxyAdapter func (v *Vmess) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { // vmess use stream-oriented udp with a special address, so we need a net.UDPAddr diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index ca732baa..470dc523 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -674,6 +674,27 @@ func (r *refProxyAdapter) SupportUOT() bool { return false } +func (r *refProxyAdapter) SupportDialerProxy() string { + if r.proxyAdapter != nil { + return r.proxyAdapter.SupportDialerProxy() + } + return "" +} + +func (r *refProxyAdapter) SupportInterface() string { + if r.proxyAdapter != nil { + return r.proxyAdapter.SupportInterface() + } + return "" +} + +func (r *refProxyAdapter) SupportRoutingMark() int { + if r.proxyAdapter != nil { + return r.proxyAdapter.SupportRoutingMark() + } + return 0 +} + func (r *refProxyAdapter) SupportWithDialer() C.NetWork { if r.proxyAdapter != nil { return r.proxyAdapter.SupportWithDialer() diff --git a/constant/adapters.go b/constant/adapters.go index 2bbcffba..ade0100c 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -108,6 +108,9 @@ type ProxyAdapter interface { SupportTFO() bool SupportMPTCP() bool SupportSMUX() bool + SupportInterface() string + SupportRoutingMark() int + SupportDialerProxy() string MarshalJSON() ([]byte, error) // Deprecated: use DialContextWithDialer and ListenPacketWithDialer instead. From f805a9f4c694d384b689a977a0acf86cf6fba020 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 26 Nov 2024 10:04:41 +0800 Subject: [PATCH 283/311] chore: cleaned up some weird code --- adapter/adapter.go | 17 ++++++----- adapter/outbound/base.go | 39 ++++++-------------------- adapter/outbound/http.go | 8 ++++-- adapter/outbound/hysteria.go | 8 ++++-- adapter/outbound/hysteria2.go | 8 ++++-- adapter/outbound/shadowsocks.go | 8 ++++-- adapter/outbound/shadowsocksr.go | 8 ++++-- adapter/outbound/singmux.go | 6 ++-- adapter/outbound/snell.go | 8 ++++-- adapter/outbound/socks5.go | 8 ++++-- adapter/outbound/ssh.go | 8 ++++-- adapter/outbound/trojan.go | 8 ++++-- adapter/outbound/tuic.go | 8 ++++-- adapter/outbound/vless.go | 8 ++++-- adapter/outbound/vmess.go | 7 +++-- adapter/outbound/wireguard.go | 48 ++------------------------------ constant/adapters.go | 20 ++++++++----- 17 files changed, 99 insertions(+), 126 deletions(-) diff --git a/adapter/adapter.go b/adapter/adapter.go index befe2113..31706a2e 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -164,13 +164,16 @@ func (p *Proxy) MarshalJSON() ([]byte, error) { mapping["name"] = p.Name() mapping["udp"] = p.SupportUDP() mapping["uot"] = p.SupportUOT() - mapping["xudp"] = p.SupportXUDP() - mapping["tfo"] = p.SupportTFO() - mapping["mptcp"] = p.SupportMPTCP() - mapping["smux"] = p.SupportSMUX() - mapping["interface"] = p.SupportInterface() - mapping["dialer-proxy"] = p.SupportDialerProxy() - mapping["routing-mark"] = p.SupportRoutingMark() + + proxyInfo := p.ProxyInfo() + mapping["xudp"] = proxyInfo.XUDP + mapping["tfo"] = proxyInfo.TFO + mapping["mptcp"] = proxyInfo.MPTCP + mapping["smux"] = proxyInfo.SMUX + mapping["interface"] = proxyInfo.Interface + mapping["dialer-proxy"] = proxyInfo.DialerProxy + mapping["routing-mark"] = proxyInfo.RoutingMark + return json.Marshal(mapping) } diff --git a/adapter/outbound/base.go b/adapter/outbound/base.go index c15e7115..dd226f74 100644 --- a/adapter/outbound/base.go +++ b/adapter/outbound/base.go @@ -85,36 +85,15 @@ func (b *Base) SupportUDP() bool { return b.udp } -// SupportXUDP implements C.ProxyAdapter -func (b *Base) SupportXUDP() bool { - return b.xudp -} - -// SupportTFO implements C.ProxyAdapter -func (b *Base) SupportTFO() bool { - return b.tfo -} - -// SupportMPTCP implements C.ProxyAdapter -func (b *Base) SupportMPTCP() bool { - return b.mpTcp -} - -// SupportSMUX implements C.ProxyAdapter -func (b *Base) SupportSMUX() bool { - return false -} - -func (b *Base) SupportDialerProxy() string { - return "" -} - -func (b *Base) SupportInterface() string { - return b.iface -} - -func (b *Base) SupportRoutingMark() int { - return b.rmark +// ProxyInfo implements C.ProxyAdapter +func (b *Base) ProxyInfo() (info C.ProxyInfo) { + info.XUDP = b.xudp + info.TFO = b.tfo + info.MPTCP = b.mpTcp + info.SMUX = false + info.Interface = b.iface + info.RoutingMark = b.rmark + return } // IsL3Protocol implements C.ProxyAdapter diff --git a/adapter/outbound/http.go b/adapter/outbound/http.go index 0f72f759..54e05f9d 100644 --- a/adapter/outbound/http.go +++ b/adapter/outbound/http.go @@ -92,9 +92,11 @@ func (h *Http) SupportWithDialer() C.NetWork { return C.TCP } -// SupportDialerProxy implements C.ProxyAdapter -func (h *Http) SupportDialerProxy() string { - return h.option.DialerProxy +// ProxyInfo implements C.ProxyAdapter +func (h *Http) ProxyInfo() C.ProxyInfo { + info := h.Base.ProxyInfo() + info.DialerProxy = h.option.DialerProxy + return info } func (h *Http) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error { diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index 964acb96..b0edab02 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -87,9 +87,11 @@ func (h *Hysteria) genHdc(ctx context.Context, opts ...dialer.Option) utils.Pack } } -// SupportDialerProxy implements C.ProxyAdapter -func (h *Hysteria) SupportDialerProxy() string { - return h.option.DialerProxy +// ProxyInfo implements C.ProxyAdapter +func (h *Hysteria) ProxyInfo() C.ProxyInfo { + info := h.Base.ProxyInfo() + info.DialerProxy = h.option.DialerProxy + return info } type HysteriaOption struct { diff --git a/adapter/outbound/hysteria2.go b/adapter/outbound/hysteria2.go index fbf0c2ec..e7a9adae 100644 --- a/adapter/outbound/hysteria2.go +++ b/adapter/outbound/hysteria2.go @@ -96,9 +96,11 @@ func closeHysteria2(h *Hysteria2) { } } -// SupportDialerProxy implements C.ProxyAdapter -func (h *Hysteria2) SupportDialerProxy() string { - return h.option.DialerProxy +// ProxyInfo implements C.ProxyAdapter +func (h *Hysteria2) ProxyInfo() C.ProxyInfo { + info := h.Base.ProxyInfo() + info.DialerProxy = h.option.DialerProxy + return info } func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index a7c8f5bb..f73d5302 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -196,9 +196,11 @@ func (ss *ShadowSocks) SupportWithDialer() C.NetWork { return C.ALLNet } -// SupportDialerProxy implements C.ProxyAdapter -func (ss *ShadowSocks) SupportDialerProxy() string { - return ss.option.DialerProxy +// ProxyInfo implements C.ProxyAdapter +func (ss *ShadowSocks) ProxyInfo() C.ProxyInfo { + info := ss.Base.ProxyInfo() + info.DialerProxy = ss.option.DialerProxy + return info } // ListenPacketOnStreamConn implements C.ProxyAdapter diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index d217dd16..d0752e79 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -122,9 +122,11 @@ func (ssr *ShadowSocksR) SupportWithDialer() C.NetWork { return C.ALLNet } -// SupportDialerProxy implements C.ProxyAdapter -func (ssr *ShadowSocksR) SupportDialerProxy() string { - return ssr.option.DialerProxy +// ProxyInfo implements C.ProxyAdapter +func (ssr *ShadowSocksR) ProxyInfo() C.ProxyInfo { + info := ssr.Base.ProxyInfo() + info.DialerProxy = ssr.option.DialerProxy + return info } func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) { diff --git a/adapter/outbound/singmux.go b/adapter/outbound/singmux.go index 5cc4e748..26a8d26e 100644 --- a/adapter/outbound/singmux.go +++ b/adapter/outbound/singmux.go @@ -97,8 +97,10 @@ func (s *SingMux) SupportUOT() bool { return true } -func (s *SingMux) SupportSMUX() bool { - return true +func (s *SingMux) ProxyInfo() C.ProxyInfo { + info := s.ProxyAdapter.ProxyInfo() + info.SMUX = true + return info } func closeSingMux(s *SingMux) { diff --git a/adapter/outbound/snell.go b/adapter/outbound/snell.go index 7df3dd48..f4b94787 100644 --- a/adapter/outbound/snell.go +++ b/adapter/outbound/snell.go @@ -141,9 +141,11 @@ func (s *Snell) SupportUOT() bool { return true } -// SupportDialerProxy implements C.ProxyAdapter -func (s *Snell) SupportDialerProxy() string { - return s.option.DialerProxy +// ProxyInfo implements C.ProxyAdapter +func (s *Snell) ProxyInfo() C.ProxyInfo { + info := s.Base.ProxyInfo() + info.DialerProxy = s.option.DialerProxy + return info } func NewSnell(option SnellOption) (*Snell, error) { diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index 1b3f93c5..b8dd3b39 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -171,9 +171,11 @@ func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, return newPacketConn(&socksPacketConn{PacketConn: pc, rAddr: bindUDPAddr, tcpConn: c}, ss), nil } -// SupportDialerProxy implements C.ProxyAdapter -func (ss *Socks5) SupportDialerProxy() string { - return ss.option.DialerProxy +// ProxyInfo implements C.ProxyAdapter +func (ss *Socks5) ProxyInfo() C.ProxyInfo { + info := ss.Base.ProxyInfo() + info.DialerProxy = ss.option.DialerProxy + return info } func NewSocks5(option Socks5Option) (*Socks5, error) { diff --git a/adapter/outbound/ssh.go b/adapter/outbound/ssh.go index 5ea9b29c..d746842d 100644 --- a/adapter/outbound/ssh.go +++ b/adapter/outbound/ssh.go @@ -121,9 +121,11 @@ func closeSsh(s *Ssh) { _ = s.client.Close() } -// SupportWithDialer implements C.ProxyAdapter -func (s *Ssh) SupportDialerProxy() string { - return s.option.DialerProxy +// ProxyInfo implements C.ProxyAdapter +func (s *Ssh) ProxyInfo() C.ProxyInfo { + info := s.Base.ProxyInfo() + info.DialerProxy = s.option.DialerProxy + return info } func NewSsh(option SshOption) (*Ssh, error) { diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index a23a52f3..b6265439 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -244,9 +244,11 @@ func (t *Trojan) SupportUOT() bool { return true } -// SupportDialerProxy implements C.ProxyAdapter -func (t *Trojan) SupportDialerProxy() string { - return t.option.DialerProxy +// ProxyInfo implements C.ProxyAdapter +func (t *Trojan) ProxyInfo() C.ProxyInfo { + info := t.Base.ProxyInfo() + info.DialerProxy = t.option.DialerProxy + return info } func NewTrojan(option TrojanOption) (*Trojan, error) { diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index f2c218db..eaacb817 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -146,9 +146,11 @@ func (t *Tuic) dialWithDialer(ctx context.Context, dialer C.Dialer) (transport * return } -// SupportDialerProxy implements C.ProxyAdapter -func (t *Tuic) SupportDialerProxy() string { - return t.option.DialerProxy +// ProxyInfo implements C.ProxyAdapter +func (t *Tuic) ProxyInfo() C.ProxyInfo { + info := t.Base.ProxyInfo() + info.DialerProxy = t.option.DialerProxy + return info } func NewTuic(option TuicOption) (*Tuic, error) { diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index 7fb409ae..7ab61ff6 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -379,9 +379,11 @@ func (v *Vless) SupportUOT() bool { return true } -// SupportDialerProxy implements C.ProxyAdapter -func (v *Vless) SupportDialerProxy() string { - return v.option.DialerProxy +// ProxyInfo implements C.ProxyAdapter +func (v *Vless) ProxyInfo() C.ProxyInfo { + info := v.Base.ProxyInfo() + info.DialerProxy = v.option.DialerProxy + return info } func parseVlessAddr(metadata *C.Metadata, xudp bool) *vless.DstAddr { diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index a10ac2e1..f7de4e8e 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -388,8 +388,11 @@ func (v *Vmess) SupportWithDialer() C.NetWork { return C.ALLNet } -func (v *Vmess) SupportDialerProxy() string { - return v.option.DialerProxy +// ProxyInfo implements C.ProxyAdapter +func (v *Vmess) ProxyInfo() C.ProxyInfo { + info := v.Base.ProxyInfo() + info.DialerProxy = v.option.DialerProxy + return info } // ListenPacketOnStreamConn implements C.ProxyAdapter diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 470dc523..2834d324 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -611,32 +611,11 @@ func (r *refProxyAdapter) SupportUDP() bool { return false } -func (r *refProxyAdapter) SupportXUDP() bool { +func (r *refProxyAdapter) ProxyInfo() C.ProxyInfo { if r.proxyAdapter != nil { - return r.proxyAdapter.SupportXUDP() + return r.proxyAdapter.ProxyInfo() } - return false -} - -func (r *refProxyAdapter) SupportTFO() bool { - if r.proxyAdapter != nil { - return r.proxyAdapter.SupportTFO() - } - return false -} - -func (r *refProxyAdapter) SupportMPTCP() bool { - if r.proxyAdapter != nil { - return r.proxyAdapter.SupportMPTCP() - } - return false -} - -func (r *refProxyAdapter) SupportSMUX() bool { - if r.proxyAdapter != nil { - return r.proxyAdapter.SupportSMUX() - } - return false + return C.ProxyInfo{} } func (r *refProxyAdapter) MarshalJSON() ([]byte, error) { @@ -674,27 +653,6 @@ func (r *refProxyAdapter) SupportUOT() bool { return false } -func (r *refProxyAdapter) SupportDialerProxy() string { - if r.proxyAdapter != nil { - return r.proxyAdapter.SupportDialerProxy() - } - return "" -} - -func (r *refProxyAdapter) SupportInterface() string { - if r.proxyAdapter != nil { - return r.proxyAdapter.SupportInterface() - } - return "" -} - -func (r *refProxyAdapter) SupportRoutingMark() int { - if r.proxyAdapter != nil { - return r.proxyAdapter.SupportRoutingMark() - } - return 0 -} - func (r *refProxyAdapter) SupportWithDialer() C.NetWork { if r.proxyAdapter != nil { return r.proxyAdapter.SupportWithDialer() diff --git a/constant/adapters.go b/constant/adapters.go index ade0100c..664054d7 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -99,18 +99,24 @@ type Dialer interface { ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) } +type ProxyInfo struct { + XUDP bool + TFO bool + MPTCP bool + SMUX bool + Interface string + RoutingMark int + DialerProxy string +} + type ProxyAdapter interface { Name() string Type() AdapterType Addr() string SupportUDP() bool - SupportXUDP() bool - SupportTFO() bool - SupportMPTCP() bool - SupportSMUX() bool - SupportInterface() string - SupportRoutingMark() int - SupportDialerProxy() string + + // ProxyInfo contains some extra information maybe useful for MarshalJSON + ProxyInfo() ProxyInfo MarshalJSON() ([]byte, error) // Deprecated: use DialContextWithDialer and ListenPacketWithDialer instead. From a35f712478c367baf639142aaa39da9f9e0ba04e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 26 Nov 2024 10:28:07 +0800 Subject: [PATCH 284/311] chore: update gvisor --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 0b1477ef..75fb89f4 100644 --- a/go.mod +++ b/go.mod @@ -25,9 +25,9 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 github.com/metacubex/sing-shadowsocks v0.2.8 github.com/metacubex/sing-shadowsocks2 v0.2.2 - github.com/metacubex/sing-tun v0.2.7-0.20241121025804-6229062404b6 + github.com/metacubex/sing-tun v0.4.2 github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 - github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 + github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589 github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa github.com/metacubex/utls v1.6.6 github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 @@ -87,7 +87,7 @@ require ( github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mdlayher/socket v0.4.1 // indirect - github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec // indirect + github.com/metacubex/gvisor v0.0.0-20241126021258-5b028898cc5a // indirect github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/pierrec/lz4/v4 v4.1.14 // indirect diff --git a/go.sum b/go.sum index 71909d46..03eaa3d4 100644 --- a/go.sum +++ b/go.sum @@ -102,8 +102,8 @@ github.com/metacubex/chacha v0.1.0 h1:tg9RSJ18NvL38cCWNyYH1eiG6qDCyyXIaTLQthon0s github.com/metacubex/chacha v0.1.0/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8= github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI= github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88= -github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc= -github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= +github.com/metacubex/gvisor v0.0.0-20241126021258-5b028898cc5a h1:cZ6oNVrsmsi3SNlnSnRio4zOgtQq+/XidwsaNgKICcg= +github.com/metacubex/gvisor v0.0.0-20241126021258-5b028898cc5a/go.mod h1:xBw/SYJPgUMPQ1tklV/brGn2nxhfr3BnvBzNlyi4Nic= github.com/metacubex/quic-go v0.48.2-0.20241105005628-a3e65bac65b2 h1:1prpWzQnhN/LgGTMA6nz86MGcppDUOwfRkhxPOnrzAk= github.com/metacubex/quic-go v0.48.2-0.20241105005628-a3e65bac65b2/go.mod h1:AiZ+UPgrkO1DTnmiAX4b+kRoV1Vfc65UkYD7RbFlIZA= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= @@ -116,12 +116,12 @@ github.com/metacubex/sing-shadowsocks v0.2.8 h1:wIhlaigswzjPw4hej75sEvWte3QR0+AJ github.com/metacubex/sing-shadowsocks v0.2.8/go.mod h1:X3x88XtJpBxG0W0/ECOJL6Ib0SJ3xdniAkU/6/RMWU0= github.com/metacubex/sing-shadowsocks2 v0.2.2 h1:eaf42uVx4Lr21S6MDYs0ZdTvGA0GEhDpb9no4+gdXPo= github.com/metacubex/sing-shadowsocks2 v0.2.2/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q= -github.com/metacubex/sing-tun v0.2.7-0.20241121025804-6229062404b6 h1:wGk/RJk/EYoTP5WalEPcVzfjSW/sB90Lav1LNBJYFdk= -github.com/metacubex/sing-tun v0.2.7-0.20241121025804-6229062404b6/go.mod h1:OLVO6D3su3ifZR740ROPHiOmRqRs7LxFY/xLh5SgPi8= +github.com/metacubex/sing-tun v0.4.2 h1:fwrQp3P536Pswu6gR1FJ+8GH55e+t2+B8LHIjwRtWbc= +github.com/metacubex/sing-tun v0.4.2/go.mod h1:V0N4rr0dWPBEE20ESkTXdbtx2riQYcb6YtwC5w/9wl0= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosqY8xKDp3pqTW3qbrCprZ1l6WkrXSFSCwyY4I= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= -github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 h1:xg71VmzLS6ByAzi/57phwDvjE+dLLs+ozH00k4DnOns= -github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3/go.mod h1:6nitcmzPDL3MXnLdhu6Hm126Zk4S1fBbX3P7jxUxSFw= +github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589 h1:Z6bNy0HLTjx6BKIkV48sV/yia/GP8Bnyb5JQuGgSGzg= +github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589/go.mod h1:4NclTLIZuk+QkHVCGrP87rHi/y8YjgPytxTgApJNMhc= github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa h1:9mcjV+RGZVC3reJBNDjjNPyS8PmFG97zq56X7WNaFO4= github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa/go.mod h1:4tLB5c8U0CxpkFM+AJJB77jEaVDbLH5XQvy42vAGsWw= github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8= From 1fff34d30e18ea8091cb93f18ca851ed29d08b50 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 26 Nov 2024 13:39:54 +0800 Subject: [PATCH 285/311] chore: update quic-go to 0.48.2 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 75fb89f4..905b1597 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 github.com/metacubex/chacha v0.1.0 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 - github.com/metacubex/quic-go v0.48.2-0.20241105005628-a3e65bac65b2 + github.com/metacubex/quic-go v0.48.3-0.20241126053724-b69fea3888da github.com/metacubex/randv2 v0.2.0 github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 github.com/metacubex/sing-shadowsocks v0.2.8 diff --git a/go.sum b/go.sum index 03eaa3d4..7845d294 100644 --- a/go.sum +++ b/go.sum @@ -104,8 +104,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-20241126021258-5b028898cc5a h1:cZ6oNVrsmsi3SNlnSnRio4zOgtQq+/XidwsaNgKICcg= github.com/metacubex/gvisor v0.0.0-20241126021258-5b028898cc5a/go.mod h1:xBw/SYJPgUMPQ1tklV/brGn2nxhfr3BnvBzNlyi4Nic= -github.com/metacubex/quic-go v0.48.2-0.20241105005628-a3e65bac65b2 h1:1prpWzQnhN/LgGTMA6nz86MGcppDUOwfRkhxPOnrzAk= -github.com/metacubex/quic-go v0.48.2-0.20241105005628-a3e65bac65b2/go.mod h1:AiZ+UPgrkO1DTnmiAX4b+kRoV1Vfc65UkYD7RbFlIZA= +github.com/metacubex/quic-go v0.48.3-0.20241126053724-b69fea3888da h1:Mq6cbHbPTLLTUfA9scrwBmOGkvl6y99E3WmtMIMqo30= +github.com/metacubex/quic-go v0.48.3-0.20241126053724-b69fea3888da/go.mod h1:AiZ+UPgrkO1DTnmiAX4b+kRoV1Vfc65UkYD7RbFlIZA= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000 h1:gUbMXcQXhXGj0vCpCVFTUyIH7TMpD1dpTcNv/MCS+ok= From fbead56ec97ae93f904f4476df1741af718c9c2a Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 27 Nov 2024 09:28:38 +0800 Subject: [PATCH 286/311] feat: add `size-limit` for provider https://github.com/MetaCubeX/mihomo/issues/1645 --- adapter/provider/parser.go | 3 ++- component/resource/vehicle.go | 32 +++++++++++++++++++------------- component/updater/update_geo.go | 8 ++++---- docs/config.yaml | 2 ++ rules/provider/parse.go | 17 +++++++++-------- 5 files changed, 36 insertions(+), 26 deletions(-) diff --git a/adapter/provider/parser.go b/adapter/provider/parser.go index b0db5db3..b305df7f 100644 --- a/adapter/provider/parser.go +++ b/adapter/provider/parser.go @@ -66,6 +66,7 @@ type proxyProviderSchema struct { ExcludeFilter string `provider:"exclude-filter,omitempty"` ExcludeType string `provider:"exclude-type,omitempty"` DialerProxy string `provider:"dialer-proxy,omitempty"` + SizeLimit int64 `provider:"size-limit,omitempty"` HealthCheck healthCheckSchema `provider:"health-check,omitempty"` Override OverrideSchema `provider:"override,omitempty"` @@ -111,7 +112,7 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide return nil, fmt.Errorf("%w: %s", errSubPath, path) } } - vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, schema.Header, resource.DefaultHttpTimeout) + vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, schema.Header, resource.DefaultHttpTimeout, schema.SizeLimit) default: return nil, fmt.Errorf("%w: %s", errVehicleType, schema.Type) } diff --git a/component/resource/vehicle.go b/component/resource/vehicle.go index 7c3cb1c2..18cebf00 100644 --- a/component/resource/vehicle.go +++ b/component/resource/vehicle.go @@ -84,12 +84,13 @@ func NewFileVehicle(path string) *FileVehicle { } type HTTPVehicle struct { - url string - path string - proxy string - header http.Header - timeout time.Duration - provider types.ProxyProvider + url string + path string + proxy string + header http.Header + timeout time.Duration + sizeLimit int64 + provider types.ProxyProvider } func (h *HTTPVehicle) Url() string { @@ -151,7 +152,11 @@ func (h *HTTPVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []b err = errors.New(resp.Status) return } - buf, err = io.ReadAll(resp.Body) + var reader io.Reader = resp.Body + if h.sizeLimit > 0 { + reader = io.LimitReader(reader, h.sizeLimit) + } + buf, err = io.ReadAll(reader) if err != nil { return } @@ -166,12 +171,13 @@ func (h *HTTPVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []b return } -func NewHTTPVehicle(url string, path string, proxy string, header http.Header, timeout time.Duration) *HTTPVehicle { +func NewHTTPVehicle(url string, path string, proxy string, header http.Header, timeout time.Duration, sizeLimit int64) *HTTPVehicle { return &HTTPVehicle{ - url: url, - path: path, - proxy: proxy, - header: header, - timeout: timeout, + url: url, + path: path, + proxy: proxy, + header: header, + timeout: timeout, + sizeLimit: sizeLimit, } } diff --git a/component/updater/update_geo.go b/component/updater/update_geo.go index bba0dabd..719a5215 100644 --- a/component/updater/update_geo.go +++ b/component/updater/update_geo.go @@ -45,7 +45,7 @@ func SetGeoUpdateInterval(newGeoUpdateInterval int) { } func UpdateMMDB() (err error) { - vehicle := resource.NewHTTPVehicle(geodata.MmdbUrl(), C.Path.MMDB(), "", nil, defaultHttpTimeout) + vehicle := resource.NewHTTPVehicle(geodata.MmdbUrl(), C.Path.MMDB(), "", nil, defaultHttpTimeout, 0) var oldHash utils.HashType if buf, err := os.ReadFile(vehicle.Path()); err == nil { oldHash = utils.MakeHash(buf) @@ -76,7 +76,7 @@ func UpdateMMDB() (err error) { } func UpdateASN() (err error) { - vehicle := resource.NewHTTPVehicle(geodata.ASNUrl(), C.Path.ASN(), "", nil, defaultHttpTimeout) + vehicle := resource.NewHTTPVehicle(geodata.ASNUrl(), C.Path.ASN(), "", nil, defaultHttpTimeout, 0) var oldHash utils.HashType if buf, err := os.ReadFile(vehicle.Path()); err == nil { oldHash = utils.MakeHash(buf) @@ -109,7 +109,7 @@ func UpdateASN() (err error) { func UpdateGeoIp() (err error) { geoLoader, err := geodata.GetGeoDataLoader("standard") - vehicle := resource.NewHTTPVehicle(geodata.GeoIpUrl(), C.Path.GeoIP(), "", nil, defaultHttpTimeout) + vehicle := resource.NewHTTPVehicle(geodata.GeoIpUrl(), C.Path.GeoIP(), "", nil, defaultHttpTimeout, 0) var oldHash utils.HashType if buf, err := os.ReadFile(vehicle.Path()); err == nil { oldHash = utils.MakeHash(buf) @@ -139,7 +139,7 @@ func UpdateGeoIp() (err error) { func UpdateGeoSite() (err error) { geoLoader, err := geodata.GetGeoDataLoader("standard") - vehicle := resource.NewHTTPVehicle(geodata.GeoSiteUrl(), C.Path.GeoSite(), "", nil, defaultHttpTimeout) + vehicle := resource.NewHTTPVehicle(geodata.GeoSiteUrl(), C.Path.GeoSite(), "", nil, defaultHttpTimeout, 0) var oldHash utils.HashType if buf, err := os.ReadFile(vehicle.Path()); err == nil { oldHash = utils.MakeHash(buf) diff --git a/docs/config.yaml b/docs/config.yaml index e75e5bd5..5e83eea3 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -930,6 +930,7 @@ proxy-providers: interval: 3600 path: ./provider1.yaml # 默认只允许存储在 mihomo 的 Home Dir,如果想存储到任意位置,添加环境变量 SKIP_SAFE_PATH_CHECK=1 proxy: DIRECT + # size-limit: 10240 # 限制下载文件最大为10kb,默认为0即不限制文件大小 header: User-Agent: - "Clash/v1.18.0" @@ -977,6 +978,7 @@ rule-providers: type: http # http 的 path 可空置,默认储存路径为 homedir 的 rules 文件夹,文件名为 url 的 md5 url: "url" proxy: DIRECT + # size-limit: 10240 # 限制下载文件最大为10kb,默认为0即不限制文件大小 rule2: behavior: classical interval: 259200 diff --git a/rules/provider/parse.go b/rules/provider/parse.go index 3bd8f54c..b04096fb 100644 --- a/rules/provider/parse.go +++ b/rules/provider/parse.go @@ -16,13 +16,14 @@ var ( ) type ruleProviderSchema struct { - Type string `provider:"type"` - Behavior string `provider:"behavior"` - Path string `provider:"path,omitempty"` - URL string `provider:"url,omitempty"` - Proxy string `provider:"proxy,omitempty"` - Format string `provider:"format,omitempty"` - Interval int `provider:"interval,omitempty"` + Type string `provider:"type"` + Behavior string `provider:"behavior"` + Path string `provider:"path,omitempty"` + URL string `provider:"url,omitempty"` + Proxy string `provider:"proxy,omitempty"` + Format string `provider:"format,omitempty"` + Interval int `provider:"interval,omitempty"` + SizeLimit int64 `provider:"size-limit,omitempty"` } func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) (P.RuleProvider, error) { @@ -53,7 +54,7 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t return nil, fmt.Errorf("%w: %s", errSubPath, path) } } - vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, nil, resource.DefaultHttpTimeout) + vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, nil, resource.DefaultHttpTimeout, schema.SizeLimit) default: return nil, fmt.Errorf("unsupported vehicle type: %s", schema.Type) } From 9de9f1ef518206e43e9f8300cfc724d8e128d049 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 27 Nov 2024 11:02:55 +0800 Subject: [PATCH 287/311] fix: don't panic when listen on `localhost` https://github.com/MetaCubeX/mihomo/issues/1655 --- adapter/inbound/listen.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/adapter/inbound/listen.go b/adapter/inbound/listen.go index 318c9675..ad944006 100644 --- a/adapter/inbound/listen.go +++ b/adapter/inbound/listen.go @@ -2,7 +2,9 @@ package inbound import ( "context" + "fmt" "net" + "net/netip" "sync" "github.com/metacubex/mihomo/component/keepalive" @@ -42,6 +44,27 @@ func MPTCP() bool { } func ListenContext(ctx context.Context, network, address string) (net.Listener, error) { + switch network { // like net.Resolver.internetAddrList but filter domain to avoid call net.Resolver.lookupIPAddr + case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6", "ip", "ip4", "ip6": + if host, port, err := net.SplitHostPort(address); err == nil { + switch host { + case "localhost": + switch network { + case "tcp6", "udp6", "ip6": + address = net.JoinHostPort("::1", port) + default: + address = net.JoinHostPort("127.0.0.1", port) + } + case "": // internetAddrList can handle this special case + break + default: + if _, err := netip.ParseAddr(host); err != nil { // not ip + return nil, fmt.Errorf("invalid network address: %s", address) + } + } + } + } + mutex.RLock() defer mutex.RUnlock() return lc.Listen(ctx, network, address) From 5a24efdabfba39bc9ed35a85bfe51d252ffc5444 Mon Sep 17 00:00:00 2001 From: ForestL <45709305+ForestL18@users.noreply.github.com> Date: Mon, 2 Dec 2024 22:49:16 +0800 Subject: [PATCH 288/311] fix: DisableKeepAlive default value of android (#1690) --- component/keepalive/tcp_keepalive.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/component/keepalive/tcp_keepalive.go b/component/keepalive/tcp_keepalive.go index 9b24c45a..14ec862b 100644 --- a/component/keepalive/tcp_keepalive.go +++ b/component/keepalive/tcp_keepalive.go @@ -35,7 +35,7 @@ func KeepAliveInterval() time.Duration { func SetDisableKeepAlive(disable bool) { if runtime.GOOS == "android" { - setDisableKeepAlive(false) + setDisableKeepAlive(true) } else { setDisableKeepAlive(disable) } From d6b496d3c0eb99cf966e0f93ed00434797e5216c Mon Sep 17 00:00:00 2001 From: hingbong Date: Wed, 4 Dec 2024 00:54:01 +0000 Subject: [PATCH 289/311] chore: allow upgrade ui in embed mode (#1692) --- hub/route/server.go | 4 ++-- hub/route/upgrade.go | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/hub/route/server.go b/hub/route/server.go index 21102cc3..3c3e5ca4 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -127,10 +127,10 @@ func router(isDebug bool, secret string, dohServer string, cors Cors) *chi.Mux { r.Mount("/providers/rules", ruleProviderRouter()) r.Mount("/cache", cacheRouter()) r.Mount("/dns", dnsRouter()) - if !embedMode { // disallow restart and upgrade in embed mode + if !embedMode { // disallow restart in embed mode r.Mount("/restart", restartRouter()) - r.Mount("/upgrade", upgradeRouter()) } + r.Mount("/upgrade", upgradeRouter()) addExternalRouters(r) }) diff --git a/hub/route/upgrade.go b/hub/route/upgrade.go index 2fed3f67..beee77a2 100644 --- a/hub/route/upgrade.go +++ b/hub/route/upgrade.go @@ -14,9 +14,11 @@ import ( func upgradeRouter() http.Handler { r := chi.NewRouter() - r.Post("/", upgradeCore) r.Post("/ui", updateUI) - r.Post("/geo", updateGeoDatabases) + if !embedMode { // disallow upgrade core/geo in embed mode + r.Post("/", upgradeCore) + r.Post("/geo", updateGeoDatabases) + } return r } From 613becd8eac3bfe1f3584c186b78f77760263311 Mon Sep 17 00:00:00 2001 From: enfein <83481737+enfein@users.noreply.github.com> Date: Mon, 9 Dec 2024 04:05:11 +0000 Subject: [PATCH 290/311] feat: support mieru protocol (#1702) --- adapter/outbound/mieru.go | 268 +++++++++++++++++++++++++++++++++ adapter/outbound/mieru_test.go | 92 +++++++++++ adapter/parser.go | 7 + constant/adapters.go | 4 +- docs/config.yaml | 12 +- go.mod | 3 + go.sum | 8 +- 7 files changed, 391 insertions(+), 3 deletions(-) create mode 100644 adapter/outbound/mieru.go create mode 100644 adapter/outbound/mieru_test.go diff --git a/adapter/outbound/mieru.go b/adapter/outbound/mieru.go new file mode 100644 index 00000000..0d32ca41 --- /dev/null +++ b/adapter/outbound/mieru.go @@ -0,0 +1,268 @@ +package outbound + +import ( + "context" + "fmt" + "net" + "runtime" + "strconv" + "sync" + + mieruclient "github.com/enfein/mieru/v3/apis/client" + mierumodel "github.com/enfein/mieru/v3/apis/model" + mierupb "github.com/enfein/mieru/v3/pkg/appctl/appctlpb" + "github.com/metacubex/mihomo/component/dialer" + "github.com/metacubex/mihomo/component/proxydialer" + C "github.com/metacubex/mihomo/constant" + "google.golang.org/protobuf/proto" +) + +type Mieru struct { + *Base + option *MieruOption + client mieruclient.Client + mu sync.Mutex +} + +type MieruOption struct { + BasicOption + Name string `proxy:"name"` + Server string `proxy:"server"` + Port int `proxy:"port,omitempty"` + PortRange string `proxy:"port-range,omitempty"` + Transport string `proxy:"transport"` + UserName string `proxy:"username"` + Password string `proxy:"password"` +} + +// DialContext implements C.ProxyAdapter +func (m *Mieru) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { + if err := m.ensureClientIsRunning(opts...); err != nil { + return nil, err + } + addr := metadataToMieruNetAddrSpec(metadata) + c, err := m.client.DialContext(ctx, addr) + if err != nil { + return nil, fmt.Errorf("dial to %s failed: %w", addr, err) + } + return NewConn(c, m), nil +} + +// ProxyInfo implements C.ProxyAdapter +func (m *Mieru) ProxyInfo() C.ProxyInfo { + info := m.Base.ProxyInfo() + info.DialerProxy = m.option.DialerProxy + return info +} + +func (m *Mieru) ensureClientIsRunning(opts ...dialer.Option) error { + m.mu.Lock() + defer m.mu.Unlock() + + if m.client.IsRunning() { + return nil + } + + // Create a dialer and add it to the client config, before starting the client. + var dialer C.Dialer = dialer.NewDialer(m.Base.DialOptions(opts...)...) + var err error + if len(m.option.DialerProxy) > 0 { + dialer, err = proxydialer.NewByName(m.option.DialerProxy, dialer) + if err != nil { + return err + } + } + config, err := m.client.Load() + if err != nil { + return err + } + config.Dialer = dialer + if err := m.client.Store(config); err != nil { + return err + } + + if err := m.client.Start(); err != nil { + return fmt.Errorf("failed to start mieru client: %w", err) + } + return nil +} + +func NewMieru(option MieruOption) (*Mieru, error) { + config, err := buildMieruClientConfig(option) + if err != nil { + return nil, fmt.Errorf("failed to build mieru client config: %w", err) + } + c := mieruclient.NewClient() + if err := c.Store(config); err != nil { + return nil, fmt.Errorf("failed to store mieru client config: %w", err) + } + // Client is started lazily on the first use. + + var addr string + if option.Port != 0 { + addr = net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) + } else { + beginPort, _, _ := beginAndEndPortFromPortRange(option.PortRange) + addr = net.JoinHostPort(option.Server, strconv.Itoa(beginPort)) + } + outbound := &Mieru{ + Base: &Base{ + name: option.Name, + addr: addr, + iface: option.Interface, + tp: C.Mieru, + udp: false, + xudp: false, + rmark: option.RoutingMark, + prefer: C.NewDNSPrefer(option.IPVersion), + }, + option: &option, + client: c, + } + runtime.SetFinalizer(outbound, closeMieru) + return outbound, nil +} + +func closeMieru(m *Mieru) { + m.mu.Lock() + defer m.mu.Unlock() + if m.client != nil && m.client.IsRunning() { + m.client.Stop() + } +} + +func metadataToMieruNetAddrSpec(metadata *C.Metadata) mierumodel.NetAddrSpec { + if metadata.Host != "" { + return mierumodel.NetAddrSpec{ + AddrSpec: mierumodel.AddrSpec{ + FQDN: metadata.Host, + Port: int(metadata.DstPort), + }, + Net: "tcp", + } + } else { + return mierumodel.NetAddrSpec{ + AddrSpec: mierumodel.AddrSpec{ + IP: metadata.DstIP.AsSlice(), + Port: int(metadata.DstPort), + }, + Net: "tcp", + } + } +} + +func buildMieruClientConfig(option MieruOption) (*mieruclient.ClientConfig, error) { + if err := validateMieruOption(option); err != nil { + return nil, fmt.Errorf("failed to validate mieru option: %w", err) + } + + transportProtocol := mierupb.TransportProtocol_TCP.Enum() + var server *mierupb.ServerEndpoint + if net.ParseIP(option.Server) != nil { + // server is an IP address + if option.PortRange != "" { + server = &mierupb.ServerEndpoint{ + IpAddress: proto.String(option.Server), + PortBindings: []*mierupb.PortBinding{ + { + PortRange: proto.String(option.PortRange), + Protocol: transportProtocol, + }, + }, + } + } else { + server = &mierupb.ServerEndpoint{ + IpAddress: proto.String(option.Server), + PortBindings: []*mierupb.PortBinding{ + { + Port: proto.Int32(int32(option.Port)), + Protocol: transportProtocol, + }, + }, + } + } + } else { + // server is a domain name + if option.PortRange != "" { + server = &mierupb.ServerEndpoint{ + DomainName: proto.String(option.Server), + PortBindings: []*mierupb.PortBinding{ + { + PortRange: proto.String(option.PortRange), + Protocol: transportProtocol, + }, + }, + } + } else { + server = &mierupb.ServerEndpoint{ + DomainName: proto.String(option.Server), + PortBindings: []*mierupb.PortBinding{ + { + Port: proto.Int32(int32(option.Port)), + Protocol: transportProtocol, + }, + }, + } + } + } + return &mieruclient.ClientConfig{ + Profile: &mierupb.ClientProfile{ + ProfileName: proto.String(option.Name), + User: &mierupb.User{ + Name: proto.String(option.UserName), + Password: proto.String(option.Password), + }, + Servers: []*mierupb.ServerEndpoint{server}, + }, + }, nil +} + +func validateMieruOption(option MieruOption) error { + if option.Name == "" { + return fmt.Errorf("name is empty") + } + if option.Server == "" { + return fmt.Errorf("server is empty") + } + if option.Port == 0 && option.PortRange == "" { + return fmt.Errorf("either port or port-range must be set") + } + if option.Port != 0 && option.PortRange != "" { + return fmt.Errorf("port and port-range cannot be set at the same time") + } + if option.Port != 0 && (option.Port < 1 || option.Port > 65535) { + return fmt.Errorf("port must be between 1 and 65535") + } + if option.PortRange != "" { + begin, end, err := beginAndEndPortFromPortRange(option.PortRange) + if err != nil { + return fmt.Errorf("invalid port-range format") + } + if begin < 1 || begin > 65535 { + return fmt.Errorf("begin port must be between 1 and 65535") + } + if end < 1 || end > 65535 { + return fmt.Errorf("end port must be between 1 and 65535") + } + if begin > end { + return fmt.Errorf("begin port must be less than or equal to end port") + } + } + + if option.Transport != "TCP" { + return fmt.Errorf("transport must be TCP") + } + if option.UserName == "" { + return fmt.Errorf("username is empty") + } + if option.Password == "" { + return fmt.Errorf("password is empty") + } + return nil +} + +func beginAndEndPortFromPortRange(portRange string) (int, int, error) { + var begin, end int + _, err := fmt.Sscanf(portRange, "%d-%d", &begin, &end) + return begin, end, err +} diff --git a/adapter/outbound/mieru_test.go b/adapter/outbound/mieru_test.go new file mode 100644 index 00000000..086b7910 --- /dev/null +++ b/adapter/outbound/mieru_test.go @@ -0,0 +1,92 @@ +package outbound + +import "testing" + +func TestNewMieru(t *testing.T) { + testCases := []struct { + option MieruOption + wantBaseAddr string + }{ + { + option: MieruOption{ + Name: "test", + Server: "1.2.3.4", + Port: 10000, + Transport: "TCP", + UserName: "test", + Password: "test", + }, + wantBaseAddr: "1.2.3.4:10000", + }, + { + option: MieruOption{ + Name: "test", + Server: "2001:db8::1", + PortRange: "10001-10002", + Transport: "TCP", + UserName: "test", + Password: "test", + }, + wantBaseAddr: "[2001:db8::1]:10001", + }, + { + option: MieruOption{ + Name: "test", + Server: "example.com", + Port: 10003, + Transport: "TCP", + UserName: "test", + Password: "test", + }, + wantBaseAddr: "example.com:10003", + }, + } + + for _, testCase := range testCases { + mieru, err := NewMieru(testCase.option) + if err != nil { + t.Error(err) + } + if mieru.addr != testCase.wantBaseAddr { + t.Errorf("got addr %q, want %q", mieru.addr, testCase.wantBaseAddr) + } + } +} + +func TestBeginAndEndPortFromPortRange(t *testing.T) { + testCases := []struct { + input string + begin int + end int + hasErr bool + }{ + {"1-10", 1, 10, false}, + {"1000-2000", 1000, 2000, false}, + {"65535-65535", 65535, 65535, false}, + {"1", 0, 0, true}, + {"1-", 0, 0, true}, + {"-10", 0, 0, true}, + {"a-b", 0, 0, true}, + {"1-b", 0, 0, true}, + {"a-10", 0, 0, true}, + } + + for _, testCase := range testCases { + begin, end, err := beginAndEndPortFromPortRange(testCase.input) + if testCase.hasErr { + if err == nil { + t.Errorf("beginAndEndPortFromPortRange(%s) should return an error", testCase.input) + } + } else { + if err != nil { + t.Errorf("beginAndEndPortFromPortRange(%s) should not return an error, but got %v", testCase.input, err) + } + if begin != testCase.begin { + t.Errorf("beginAndEndPortFromPortRange(%s) begin port mismatch, got %d, want %d", testCase.input, begin, testCase.begin) + } + if end != testCase.end { + t.Errorf("beginAndEndPortFromPortRange(%s) end port mismatch, got %d, want %d", testCase.input, end, testCase.end) + } + } + } +} diff --git a/adapter/parser.go b/adapter/parser.go index c64ee13a..ce4e91d5 100644 --- a/adapter/parser.go +++ b/adapter/parser.go @@ -141,6 +141,13 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { break } proxy, err = outbound.NewSsh(*sshOption) + case "mieru": + mieruOption := &outbound.MieruOption{} + err = decoder.Decode(mapping, mieruOption) + if err != nil { + break + } + proxy, err = outbound.NewMieru(*mieruOption) default: return nil, fmt.Errorf("unsupport proxy type: %s", proxyType) } diff --git a/constant/adapters.go b/constant/adapters.go index 664054d7..420a797f 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -42,6 +42,7 @@ const ( WireGuard Tuic Ssh + Mieru ) const ( @@ -226,7 +227,8 @@ func (at AdapterType) String() string { return "Tuic" case Ssh: return "Ssh" - + case Mieru: + return "Mieru" case Relay: return "Relay" case Selector: diff --git a/docs/config.yaml b/docs/config.yaml index 5e83eea3..23acb78f 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -846,6 +846,16 @@ proxies: # socks5 password: password privateKey: path + # mieru + - name: mieru + type: mieru + server: 1.2.3.4 + port: 2999 + # port-range: 2090-2099 #(不可同时填写 port 和 port-range) + transport: TCP # 只支持 TCP + username: user + password: password + # dns 出站会将请求劫持到内部 dns 模块,所有请求均在内部处理 - name: "dns-out" type: dns @@ -1202,4 +1212,4 @@ listeners: # authentication-timeout: 1000 # alpn: # - h3 -# max-udp-relay-packet-size: 1500 \ No newline at end of file +# max-udp-relay-packet-size: 1500 diff --git a/go.mod b/go.mod index 905b1597..1ae7c26d 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/bahlo/generic-list-go v0.2.0 github.com/coreos/go-iptables v0.8.0 github.com/dlclark/regexp2 v1.11.4 + github.com/enfein/mieru/v3 v3.8.3 github.com/go-chi/chi/v5 v5.1.0 github.com/go-chi/render v1.0.3 github.com/gobwas/ws v1.4.0 @@ -114,6 +115,8 @@ require ( golang.org/x/text v0.20.0 // indirect golang.org/x/time v0.7.0 // indirect golang.org/x/tools v0.24.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 // indirect + google.golang.org/grpc v1.64.1 // indirect ) replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000 diff --git a/go.sum b/go.sum index 7845d294..7aac4628 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/enfein/mieru/v3 v3.8.3 h1:s4K0hMFDg6LHltokR8/nBTVCq15XnnxPsvc1LrHwpoo= +github.com/enfein/mieru/v3 v3.8.3/go.mod h1:YtU00qjAEt54mCBQu4WZPCey6cBdB1BUtXjvrHLEUNQ= github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8= github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I= github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g= @@ -59,7 +61,7 @@ github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakr github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk= github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -274,6 +276,10 @@ golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= 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= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 h1:9Xyg6I9IWQZhRVfCWjKK+l6kI0jHcPesVlMnT//aHNo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= +google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From cd23112dc586422b1e9e15ade11d0cd665d04be8 Mon Sep 17 00:00:00 2001 From: enfein <83481737+enfein@users.noreply.github.com> Date: Tue, 10 Dec 2024 00:03:17 +0000 Subject: [PATCH 291/311] chore: remove gRPC dependency from mieru (#1705) --- go.mod | 4 +--- go.sum | 10 +++------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 1ae7c26d..d19ad0db 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/bahlo/generic-list-go v0.2.0 github.com/coreos/go-iptables v0.8.0 github.com/dlclark/regexp2 v1.11.4 - github.com/enfein/mieru/v3 v3.8.3 + github.com/enfein/mieru/v3 v3.8.4 github.com/go-chi/chi/v5 v5.1.0 github.com/go-chi/render v1.0.3 github.com/gobwas/ws v1.4.0 @@ -115,8 +115,6 @@ require ( golang.org/x/text v0.20.0 // indirect golang.org/x/time v0.7.0 // indirect golang.org/x/tools v0.24.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 // indirect - google.golang.org/grpc v1.64.1 // indirect ) replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000 diff --git a/go.sum b/go.sum index 7aac4628..5d070472 100644 --- a/go.sum +++ b/go.sum @@ -27,8 +27,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/enfein/mieru/v3 v3.8.3 h1:s4K0hMFDg6LHltokR8/nBTVCq15XnnxPsvc1LrHwpoo= -github.com/enfein/mieru/v3 v3.8.3/go.mod h1:YtU00qjAEt54mCBQu4WZPCey6cBdB1BUtXjvrHLEUNQ= +github.com/enfein/mieru/v3 v3.8.4 h1:PmBQykuEcl8yKcQ647pg8Qbjl433CRYgUbW6VLBgGn4= +github.com/enfein/mieru/v3 v3.8.4/go.mod h1:YtU00qjAEt54mCBQu4WZPCey6cBdB1BUtXjvrHLEUNQ= github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8= github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I= github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g= @@ -61,7 +61,7 @@ github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakr github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk= github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -276,10 +276,6 @@ golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= 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= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 h1:9Xyg6I9IWQZhRVfCWjKK+l6kI0jHcPesVlMnT//aHNo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= -google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 9a959202ed4376bcab97d8ff0e29a5f898737032 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 10 Dec 2024 09:19:59 +0800 Subject: [PATCH 292/311] chore: support config `multiplexing` of mieru --- adapter/outbound/mieru.go | 37 +++++++++++++++++++++++++------------ docs/config.yaml | 2 ++ 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/adapter/outbound/mieru.go b/adapter/outbound/mieru.go index 0d32ca41..7aab2f58 100644 --- a/adapter/outbound/mieru.go +++ b/adapter/outbound/mieru.go @@ -8,12 +8,13 @@ import ( "strconv" "sync" - mieruclient "github.com/enfein/mieru/v3/apis/client" - mierumodel "github.com/enfein/mieru/v3/apis/model" - mierupb "github.com/enfein/mieru/v3/pkg/appctl/appctlpb" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/proxydialer" C "github.com/metacubex/mihomo/constant" + + mieruclient "github.com/enfein/mieru/v3/apis/client" + mierumodel "github.com/enfein/mieru/v3/apis/model" + mierupb "github.com/enfein/mieru/v3/pkg/appctl/appctlpb" "google.golang.org/protobuf/proto" ) @@ -26,13 +27,14 @@ type Mieru struct { type MieruOption struct { BasicOption - Name string `proxy:"name"` - Server string `proxy:"server"` - Port int `proxy:"port,omitempty"` - PortRange string `proxy:"port-range,omitempty"` - Transport string `proxy:"transport"` - UserName string `proxy:"username"` - Password string `proxy:"password"` + Name string `proxy:"name"` + Server string `proxy:"server"` + Port int `proxy:"port,omitempty"` + PortRange string `proxy:"port-range,omitempty"` + Transport string `proxy:"transport"` + UserName string `proxy:"username"` + Password string `proxy:"password"` + Multiplexing string `proxy:"multiplexing,omitempty"` } // DialContext implements C.ProxyAdapter @@ -205,7 +207,7 @@ func buildMieruClientConfig(option MieruOption) (*mieruclient.ClientConfig, erro } } } - return &mieruclient.ClientConfig{ + config := &mieruclient.ClientConfig{ Profile: &mierupb.ClientProfile{ ProfileName: proto.String(option.Name), User: &mierupb.User{ @@ -214,7 +216,13 @@ func buildMieruClientConfig(option MieruOption) (*mieruclient.ClientConfig, erro }, Servers: []*mierupb.ServerEndpoint{server}, }, - }, nil + } + if multiplexing, ok := mierupb.MultiplexingLevel_value[option.Multiplexing]; ok { + config.Profile.Multiplexing = &mierupb.MultiplexingConfig{ + Level: mierupb.MultiplexingLevel(multiplexing).Enum(), + } + } + return config, nil } func validateMieruOption(option MieruOption) error { @@ -258,6 +266,11 @@ func validateMieruOption(option MieruOption) error { if option.Password == "" { return fmt.Errorf("password is empty") } + if option.Multiplexing != "" { + if _, ok := mierupb.MultiplexingLevel_value[option.Multiplexing]; !ok { + return fmt.Errorf("invalid multiplexing level: %s", option.Multiplexing) + } + } return nil } diff --git a/docs/config.yaml b/docs/config.yaml index 23acb78f..fda14820 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -855,6 +855,8 @@ proxies: # socks5 transport: TCP # 只支持 TCP username: user password: password + # 可以使用的值包括 MULTIPLEXING_OFF, MULTIPLEXING_LOW, MULTIPLEXING_MIDDLE, MULTIPLEXING_HIGH。其中 MULTIPLEXING_OFF 会关闭多路复用功能。默认值为 MULTIPLEXING_LOW。 + # multiplexing: MULTIPLEXING_LOW # dns 出站会将请求劫持到内部 dns 模块,所有请求均在内部处理 - name: "dns-out" From f3a43fe3a612a1dffca4f5816ebdaa9a9d93d059 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 10 Dec 2024 09:57:20 +0800 Subject: [PATCH 293/311] feat: support read config file from stdin via `-f -` --- main.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/main.go b/main.go index 685fc89f..9a2222df 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "flag" "fmt" + "io" "net" "os" "os/signal" @@ -99,6 +100,13 @@ func main() { log.Fatalln("Initial configuration error: %s", err.Error()) return } + } else if configFile == "-" { + var err error + configBytes, err = io.ReadAll(os.Stdin) + if err != nil { + log.Fatalln("Initial configuration error: %s", err.Error()) + return + } } else { if configFile != "" { if !filepath.IsAbs(configFile) { From 5d9d8f4d3bb6563563f37bef56fb1a09906995c6 Mon Sep 17 00:00:00 2001 From: laburaps Date: Tue, 10 Dec 2024 16:15:08 +0800 Subject: [PATCH 294/311] fix: check whether the dst port is within the specified range (#1706) --- component/sniffer/dispatcher.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/component/sniffer/dispatcher.go b/component/sniffer/dispatcher.go index 4d13ddd0..f198c4ae 100644 --- a/component/sniffer/dispatcher.go +++ b/component/sniffer/dispatcher.go @@ -146,7 +146,7 @@ func (sd *Dispatcher) Enable() bool { func (sd *Dispatcher) sniffDomain(conn *N.BufferedConn, metadata *C.Metadata) (string, error) { for s := range sd.sniffers { - if s.SupportNetwork() == C.TCP { + if s.SupportNetwork() == C.TCP && s.SupportPort(metadata.DstPort) { _ = conn.SetReadDeadline(time.Now().Add(1 * time.Second)) _, err := conn.Peek(1) _ = conn.SetReadDeadline(time.Time{}) From c7fc93df3757207f57fae47419087b2f902b6a88 Mon Sep 17 00:00:00 2001 From: laburaps Date: Thu, 12 Dec 2024 19:02:34 +0800 Subject: [PATCH 295/311] fix: the TLS Sniffer fails when the length of the ClientHello packet exceeds the TCP MSS (#1711) * chore: add uniformly formatted debug info to sniffDomain * fix: when data is not enough, attempt to peek more data and retry * chore: reduce debug info of sniffDomain --- component/sniffer/dispatcher.go | 26 ++++++++++++++++++++++---- component/sniffer/tls_sniffer.go | 19 ++++++++++++++++++- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/component/sniffer/dispatcher.go b/component/sniffer/dispatcher.go index f198c4ae..ada43176 100644 --- a/component/sniffer/dispatcher.go +++ b/component/sniffer/dispatcher.go @@ -145,6 +145,10 @@ func (sd *Dispatcher) Enable() bool { } func (sd *Dispatcher) sniffDomain(conn *N.BufferedConn, metadata *C.Metadata) (string, error) { + //defer func(start time.Time) { + // log.Debugln("[Sniffer] [%s] Sniffing took %s", metadata.DstIP, time.Since(start)) + //}(time.Now()) + for s := range sd.sniffers { if s.SupportNetwork() == C.TCP && s.SupportPort(metadata.DstPort) { _ = conn.SetReadDeadline(time.Now().Add(1 * time.Second)) @@ -154,7 +158,7 @@ func (sd *Dispatcher) sniffDomain(conn *N.BufferedConn, metadata *C.Metadata) (s _, ok := err.(*net.OpError) if ok { sd.cacheSniffFailed(metadata) - log.Errorln("[Sniffer] [%s] may not have any sent data, Consider adding skip", metadata.DstIP.String()) + log.Errorln("[Sniffer] [%s] [%s] may not have any sent data, Consider adding skip", metadata.DstIP, s.Protocol()) _ = conn.Close() } @@ -164,22 +168,36 @@ func (sd *Dispatcher) sniffDomain(conn *N.BufferedConn, metadata *C.Metadata) (s bufferedLen := conn.Buffered() bytes, err := conn.Peek(bufferedLen) if err != nil { - log.Debugln("[Sniffer] the data length not enough") + log.Debugln("[Sniffer] [%s] [%s] the data length not enough, error: %v", metadata.DstIP, s.Protocol(), err) continue } host, err := s.SniffData(bytes) + var e *errNeedAtLeastData + if errors.As(err, &e) { + //log.Debugln("[Sniffer] [%s] [%s] %v, got length: %d", metadata.DstIP, s.Protocol(), e, len(bytes)) + _ = conn.SetReadDeadline(time.Now().Add(1 * time.Second)) + bytes, err = conn.Peek(e.length) + _ = conn.SetReadDeadline(time.Time{}) + //log.Debugln("[Sniffer] [%s] [%s] try again, got length: %d", metadata.DstIP, s.Protocol(), len(bytes)) + if err != nil { + log.Debugln("[Sniffer] [%s] [%s] the data length not enough, error: %v", metadata.DstIP, s.Protocol(), err) + continue + } + host, err = s.SniffData(bytes) + } if err != nil { - //log.Debugln("[Sniffer] [%s] Sniff data failed %s", s.Protocol(), metadata.DstIP) + //log.Debugln("[Sniffer] [%s] [%s] Sniff data failed, error: %v", metadata.DstIP, s.Protocol(), err) continue } _, err = netip.ParseAddr(host) if err == nil { - //log.Debugln("[Sniffer] [%s] Sniff data failed %s", s.Protocol(), metadata.DstIP) + //log.Debugln("[Sniffer] [%s] [%s] Sniff data failed, got host [%s]", metadata.DstIP, s.Protocol(), host) continue } + //log.Debugln("[Sniffer] [%s] [%s] Sniffed [%s]", metadata.DstIP, s.Protocol(), host) return host, nil } } diff --git a/component/sniffer/tls_sniffer.go b/component/sniffer/tls_sniffer.go index 974df79a..b57f36ec 100644 --- a/component/sniffer/tls_sniffer.go +++ b/component/sniffer/tls_sniffer.go @@ -3,6 +3,7 @@ package sniffer import ( "encoding/binary" "errors" + "fmt" "strings" "github.com/metacubex/mihomo/common/utils" @@ -15,6 +16,19 @@ var ( errNotClientHello = errors.New("not client hello") ) +type errNeedAtLeastData struct { + length int + err error +} + +func (e *errNeedAtLeastData) Error() string { + return fmt.Sprintf("%v, need at least length: %d", e.err, e.length) +} + +func (e *errNeedAtLeastData) Unwrap() error { + return e.err +} + var _ sniffer.Sniffer = (*TLSSniffer)(nil) type TLSSniffer struct { @@ -160,7 +174,10 @@ func SniffTLS(b []byte) (*string, error) { } headerLen := int(binary.BigEndian.Uint16(b[3:5])) if 5+headerLen > len(b) { - return nil, ErrNoClue + return nil, &errNeedAtLeastData{ + length: 5 + headerLen, + err: ErrNoClue, + } } domain, err := ReadClientHello(b[5 : 5+headerLen]) From 269c52575c3fd243e62663bf68679c267829e406 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 14 Dec 2024 11:09:31 +0800 Subject: [PATCH 296/311] chore: update gopsutil to v4 --- go.mod | 4 ++-- go.sum | 9 ++++----- tunnel/statistic/manager.go | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index d19ad0db..d5de18e8 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 github.com/sagernet/sing-shadowtls v0.1.4 github.com/samber/lo v1.47.0 - github.com/shirou/gopsutil/v3 v3.24.5 + github.com/shirou/gopsutil/v4 v4.24.11 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.9.0 github.com/vmihailenco/msgpack/v5 v5.4.1 @@ -69,6 +69,7 @@ require ( github.com/buger/jsonparser v1.1.1 // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/ebitengine/purego v0.8.1 // indirect github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 // indirect github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect @@ -98,7 +99,6 @@ require ( github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/sagernet/nftables v0.3.0-beta.4 // indirect github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // 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 diff --git a/go.sum b/go.sum index 5d070472..0fb1bf70 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE= +github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/enfein/mieru/v3 v3.8.4 h1:PmBQykuEcl8yKcQ647pg8Qbjl433CRYgUbW6VLBgGn4= github.com/enfein/mieru/v3 v3.8.4/go.mod h1:YtU00qjAEt54mCBQu4WZPCey6cBdB1BUtXjvrHLEUNQ= github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8= @@ -176,11 +178,8 @@ github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxe github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc= github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= -github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= -github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= -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= +github.com/shirou/gopsutil/v4 v4.24.11 h1:WaU9xqGFKvFfsUv94SXcUPD7rCkU0vr/asVdQOBZNj8= +github.com/shirou/gopsutil/v4 v4.24.11/go.mod h1:s4D/wg+ag4rG0WO7AiTj2BeYCRhym0vM7DHbZRxnIT8= github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b h1:rXHg9GrUEtWZhEkrykicdND3VPjlVbYiLdX9J7gimS8= github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b/go.mod h1:X7qrxNQViEaAN9LNZOPl9PfvQtp3V3c7LTo0dvGi0fM= github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c h1:DjKMC30y6yjG3IxDaeAj3PCoRr+IsO+bzyT+Se2m2Hk= diff --git a/tunnel/statistic/manager.go b/tunnel/statistic/manager.go index 0b309299..3f2770c2 100644 --- a/tunnel/statistic/manager.go +++ b/tunnel/statistic/manager.go @@ -7,7 +7,7 @@ import ( "github.com/metacubex/mihomo/common/atomic" "github.com/puzpuzpuz/xsync/v3" - "github.com/shirou/gopsutil/v3/process" + "github.com/shirou/gopsutil/v4/process" ) var DefaultManager *Manager From c786b720304e6d241c30da09d57d08824b2604c8 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 14 Dec 2024 13:27:28 +0800 Subject: [PATCH 297/311] chore: update dependencies --- go.mod | 28 ++++++++++++++-------------- go.sum | 46 +++++++++++++++++++++++----------------------- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/go.mod b/go.mod index d5de18e8..dbc8acea 100644 --- a/go.mod +++ b/go.mod @@ -12,9 +12,9 @@ require ( github.com/go-chi/render v1.0.3 github.com/gobwas/ws v1.4.0 github.com/gofrs/uuid/v5 v5.3.0 - github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475 - github.com/klauspost/compress v1.17.9 - github.com/klauspost/cpuid/v2 v2.2.8 + github.com/insomniacslk/dhcp v0.0.0-20241203100832-a481575ed0ef + github.com/klauspost/compress v1.17.9 // lastest version compatible with golang1.20 + github.com/klauspost/cpuid/v2 v2.2.9 github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.7.2 github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab @@ -35,28 +35,28 @@ require ( github.com/miekg/dns v1.1.62 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/oschwald/maxminddb-golang v1.12.0 // lastest version compatible with golang1.20 github.com/puzpuzpuz/xsync/v3 v3.4.0 github.com/sagernet/cors v1.2.1 github.com/sagernet/fswatch v0.1.1 github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a github.com/sagernet/sing v0.5.1 - github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 - github.com/sagernet/sing-shadowtls v0.1.4 + github.com/sagernet/sing-mux v0.2.1 + github.com/sagernet/sing-shadowtls v0.1.5 github.com/samber/lo v1.47.0 github.com/shirou/gopsutil/v4 v4.24.11 github.com/sirupsen/logrus v1.9.3 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 github.com/vmihailenco/msgpack/v5 v5.4.1 github.com/wk8/go-ordered-map/v2 v2.1.8 gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 go.uber.org/automaxprocs v1.6.0 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba - golang.org/x/crypto v0.29.0 + golang.org/x/crypto v0.31.0 golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // lastest version compatible with golang1.20 - golang.org/x/net v0.31.0 - golang.org/x/sys v0.27.0 - google.golang.org/protobuf v1.34.2 + golang.org/x/net v0.32.0 + golang.org/x/sys v0.28.0 + google.golang.org/protobuf v1.34.2 // lastest version compatible with golang1.20 gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.3.0 ) @@ -83,7 +83,7 @@ require ( github.com/google/btree v1.1.3 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect - github.com/hashicorp/yamux v0.1.1 // indirect + github.com/hashicorp/yamux v0.1.2 // indirect github.com/josharian/native v1.1.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect @@ -111,8 +111,8 @@ require ( gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect go.uber.org/mock v0.4.0 // indirect golang.org/x/mod v0.20.0 // indirect - golang.org/x/sync v0.9.0 // indirect - golang.org/x/text v0.20.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.7.0 // indirect golang.org/x/tools v0.24.0 // indirect ) diff --git a/go.sum b/go.sum index 0fb1bf70..63e7a6ad 100644 --- a/go.sum +++ b/go.sum @@ -72,19 +72,19 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I= -github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= -github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475 h1:hxST5pwMBEOWmxpkX20w9oZG+hXdhKmAIPQ3NGGAxas= -github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= +github.com/insomniacslk/dhcp v0.0.0-20241203100832-a481575ed0ef h1:NzQKDfd5ZOPnuZYf9MnRee8x2qecsVqzsnaLjEZiBko= +github.com/insomniacslk/dhcp v0.0.0-20241203100832-a481575ed0ef/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 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= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= -github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -170,10 +170,10 @@ github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZN github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I= github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8= -github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 h1:5bCAkvDDzSMITiHFjolBwpdqYsvycdTu71FsMEFXQ14= -github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ= -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/sing-mux v0.2.1 h1:N/3MHymfnFZRd29tE3TaXwPUVVgKvxhtOkiCMLp9HVo= +github.com/sagernet/sing-mux v0.2.1/go.mod h1:dm3BWL6NvES9pbib7llpylrq7Gq+LjlzG+0RacdxcyE= +github.com/sagernet/sing-shadowtls v0.1.5 h1:uXxmq/HXh8DIiBGLzpMjCbWnzIAFs+lIxiTOjdgG5qo= +github.com/sagernet/sing-shadowtls v0.1.5/go.mod h1:tvrDPTGLrSM46Wnf7mSr+L8NHvgvF8M4YnJF790rZX4= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc= @@ -198,8 +198,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 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= @@ -231,8 +232,8 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= 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.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk= golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -241,11 +242,11 @@ golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= -golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -257,17 +258,16 @@ golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7w 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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= From 3f6823ba49a5f82fa0ca7355ce75a94346d057a2 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 16 Dec 2024 09:26:11 +0800 Subject: [PATCH 298/311] fix: handle invalid values in Decoder's decode method --- common/structure/structure.go | 20 ++++++++++++++++++++ common/structure/structure_test.go | 18 ++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/common/structure/structure.go b/common/structure/structure.go index 99c980bc..5bafc178 100644 --- a/common/structure/structure.go +++ b/common/structure/structure.go @@ -86,7 +86,27 @@ func (d *Decoder) Decode(src map[string]any, dst any) error { return nil } +// isNil returns true if the input is nil or a typed nil pointer. +func isNil(input any) bool { + if input == nil { + return true + } + val := reflect.ValueOf(input) + return val.Kind() == reflect.Pointer && val.IsNil() +} + func (d *Decoder) decode(name string, data any, val reflect.Value) error { + if isNil(data) { + // If the data is nil, then we don't set anything + // Maybe we should set to zero value? + return nil + } + if !reflect.ValueOf(data).IsValid() { + // If the input value is invalid, then we just set the value + // to be the zero value. + val.Set(reflect.Zero(val.Type())) + return nil + } for { kind := val.Kind() if kind == reflect.Pointer && val.IsNil() { diff --git a/common/structure/structure_test.go b/common/structure/structure_test.go index e5fe693d..266b828f 100644 --- a/common/structure/structure_test.go +++ b/common/structure/structure_test.go @@ -267,3 +267,21 @@ func TestStructure_TextUnmarshaller(t *testing.T) { err = decoder.Decode(rawMap, s) assert.NotNilf(t, err, "should throw error: %#v", s) } + +func TestStructure_Null(t *testing.T) { + rawMap := map[string]any{ + "opt": map[string]any{ + "bar": nil, + }, + } + + s := struct { + Opt struct { + Bar string `test:"bar,optional"` + } `test:"opt,optional"` + }{} + + err := decoder.Decode(rawMap, &s) + assert.Nil(t, err) + assert.Equal(t, s.Opt.Bar, "") +} From bb803249fa1237c37f384368f8168b425953974e Mon Sep 17 00:00:00 2001 From: qianlongzt <18493471+qianlongzt@users.noreply.github.com> Date: Thu, 19 Dec 2024 09:16:45 +0800 Subject: [PATCH 299/311] feat: support inline rule provider (#1731) --- constant/provider/interface.go | 3 + docs/config.yaml | 8 ++ rules/provider/parse.go | 23 +++--- rules/provider/provider.go | 132 +++++++++++++++++++++++++++------ 4 files changed, 135 insertions(+), 31 deletions(-) diff --git a/constant/provider/interface.go b/constant/provider/interface.go index 925c1734..4309adac 100644 --- a/constant/provider/interface.go +++ b/constant/provider/interface.go @@ -13,6 +13,7 @@ const ( File VehicleType = iota HTTP Compatible + Inline ) // VehicleType defined @@ -26,6 +27,8 @@ func (v VehicleType) String() string { return "HTTP" case Compatible: return "Compatible" + case Inline: + return "Inline" default: return "Unknown" } diff --git a/docs/config.yaml b/docs/config.yaml index fda14820..ca48f0e2 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -1014,6 +1014,14 @@ rule-providers: format: mrs behavior: domain path: /path/to/save/file.mrs + rule4: + type: inline + behavior: domain # classical / ipcidr + payload: + - '.blogger.com' + - '*.*.microsoft.com' + - 'books.itunes.apple.com' + rules: - RULE-SET,rule1,REJECT - IP-ASN,1,PROXY diff --git a/rules/provider/parse.go b/rules/provider/parse.go index b04096fb..51042631 100644 --- a/rules/provider/parse.go +++ b/rules/provider/parse.go @@ -16,17 +16,20 @@ var ( ) type ruleProviderSchema struct { - Type string `provider:"type"` - Behavior string `provider:"behavior"` - Path string `provider:"path,omitempty"` - URL string `provider:"url,omitempty"` - Proxy string `provider:"proxy,omitempty"` - Format string `provider:"format,omitempty"` - Interval int `provider:"interval,omitempty"` - SizeLimit int64 `provider:"size-limit,omitempty"` + Type string `provider:"type"` + Behavior string `provider:"behavior"` + Path string `provider:"path,omitempty"` + URL string `provider:"url,omitempty"` + Proxy string `provider:"proxy,omitempty"` + Format string `provider:"format,omitempty"` + Interval int `provider:"interval,omitempty"` + SizeLimit int64 `provider:"size-limit,omitempty"` + Payload []string `provider:"payload,omitempty"` } -func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) (P.RuleProvider, error) { +type parseRuleFunc func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error) + +func ParseRuleProvider(name string, mapping map[string]any, parse parseRuleFunc) (P.RuleProvider, error) { schema := &ruleProviderSchema{} decoder := structure.NewDecoder(structure.Option{TagName: "provider", WeaklyTypedInput: true}) if err := decoder.Decode(mapping, schema); err != nil { @@ -55,6 +58,8 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t } } vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, nil, resource.DefaultHttpTimeout, schema.SizeLimit) + case "inline": + return newInlineProvider(name, behavior, schema.Payload, parse), nil default: return nil, fmt.Errorf("unsupported vehicle type: %s", schema.Type) } diff --git a/rules/provider/provider.go b/rules/provider/provider.go index 0cbf83ba..a62b1009 100644 --- a/rules/provider/provider.go +++ b/rules/provider/provider.go @@ -24,9 +24,13 @@ func SetTunnel(t P.Tunnel) { } type ruleSetProvider struct { + ruleSetProviderBase *resource.Fetcher[ruleStrategy] + format P.RuleFormat +} + +type ruleSetProviderBase struct { behavior P.RuleBehavior - format P.RuleFormat strategy ruleStrategy } @@ -61,7 +65,7 @@ type mrsRuleStrategy interface { DumpMrs(f func(key string) bool) } -func (rp *ruleSetProvider) Type() P.ProviderType { +func (rp *ruleSetProviderBase) Type() P.ProviderType { return P.Rule } @@ -75,40 +79,51 @@ func (rp *ruleSetProvider) Update() error { return err } -func (rp *ruleSetProvider) Behavior() P.RuleBehavior { +func (rp *ruleSetProviderBase) Behavior() P.RuleBehavior { return rp.behavior } -func (rp *ruleSetProvider) Count() int { +func (rp *ruleSetProviderBase) Count() int { return rp.strategy.Count() } -func (rp *ruleSetProvider) Match(metadata *C.Metadata) bool { +func (rp *ruleSetProviderBase) Match(metadata *C.Metadata) bool { return rp.strategy != nil && rp.strategy.Match(metadata) } -func (rp *ruleSetProvider) ShouldResolveIP() bool { +func (rp *ruleSetProviderBase) ShouldResolveIP() bool { return rp.strategy.ShouldResolveIP() } -func (rp *ruleSetProvider) ShouldFindProcess() bool { +func (rp *ruleSetProviderBase) ShouldFindProcess() bool { return rp.strategy.ShouldFindProcess() } -func (rp *ruleSetProvider) Strategy() any { +func (rp *ruleSetProviderBase) Strategy() any { return rp.strategy } +type providerForApi struct { + Behavior string `json:"behavior"` + Format string `json:"format"` + Name string `json:"name"` + RuleCount int `json:"ruleCount"` + Type string `json:"type"` + VehicleType string `json:"vehicleType"` + UpdatedAt time.Time `json:"updatedAt"` + Payload []string `json:"payload,omitempty"` +} + func (rp *ruleSetProvider) MarshalJSON() ([]byte, error) { return json.Marshal( - map[string]interface{}{ - "behavior": rp.behavior.String(), - "format": rp.format.String(), - "name": rp.Name(), - "ruleCount": rp.strategy.Count(), - "type": rp.Type().String(), - "updatedAt": rp.UpdatedAt(), - "vehicleType": rp.VehicleType().String(), + providerForApi{ + Behavior: rp.behavior.String(), + Format: rp.format.String(), + Name: rp.Fetcher.Name(), + RuleCount: rp.strategy.Count(), + Type: rp.Type().String(), + UpdatedAt: rp.UpdatedAt(), + VehicleType: rp.VehicleType().String(), }) } @@ -118,10 +133,13 @@ func (rp *RuleSetProvider) Close() error { } func NewRuleSetProvider(name string, behavior P.RuleBehavior, format P.RuleFormat, interval time.Duration, vehicle P.Vehicle, - parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) P.RuleProvider { + parse parseRuleFunc, +) P.RuleProvider { rp := &ruleSetProvider{ - behavior: behavior, - format: format, + ruleSetProviderBase: ruleSetProviderBase{ + behavior: behavior, + }, + format: format, } onUpdate := func(strategy ruleStrategy) { @@ -142,7 +160,7 @@ func NewRuleSetProvider(name string, behavior P.RuleBehavior, format P.RuleForma return wrapper } -func newStrategy(behavior P.RuleBehavior, parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) ruleStrategy { +func newStrategy(behavior P.RuleBehavior, parse parseRuleFunc) ruleStrategy { switch behavior { case P.Domain: strategy := NewDomainStrategy() @@ -158,8 +176,10 @@ func newStrategy(behavior P.RuleBehavior, parse func(tp, payload, target string, } } -var ErrNoPayload = errors.New("file must have a `payload` field") -var ErrInvalidFormat = errors.New("invalid format") +var ( + ErrNoPayload = errors.New("file must have a `payload` field") + ErrInvalidFormat = errors.New("invalid format") +) func rulesParse(buf []byte, strategy ruleStrategy, format P.RuleFormat) (ruleStrategy, error) { strategy.Reset() @@ -254,3 +274,71 @@ func rulesParse(buf []byte, strategy ruleStrategy, format P.RuleFormat) (ruleStr return strategy, nil } + +func rulesParseInline(rs []string, strategy ruleStrategy) ruleStrategy { + strategy.Reset() + for _, r := range rs { + if r != "" { + strategy.Insert(r) + } + } + strategy.FinishInsert() + return strategy +} + +type inlineProvider struct { + ruleSetProviderBase + name string + updateTime time.Time + payload []string +} + +func (i *inlineProvider) Name() string { + return i.name +} + +func (i *inlineProvider) Initial() error { + return nil +} + +func (i *inlineProvider) Update() error { + // make api update happy + i.updateTime = time.Now() + return nil +} + +func (i *inlineProvider) VehicleType() P.VehicleType { + return P.Inline +} + +func (i *inlineProvider) MarshalJSON() ([]byte, error) { + return json.Marshal( + providerForApi{ + Behavior: i.behavior.String(), + Name: i.Name(), + RuleCount: i.strategy.Count(), + Type: i.Type().String(), + VehicleType: i.VehicleType().String(), + UpdatedAt: i.updateTime, + Payload: i.payload, + }) +} + +func newInlineProvider( + name string, + behavior P.RuleBehavior, + payload []string, + parse parseRuleFunc, +) P.RuleProvider { + rp := &inlineProvider{ + ruleSetProviderBase: ruleSetProviderBase{ + behavior: behavior, + strategy: newStrategy(behavior, parse), + }, + payload: payload, + name: name, + updateTime: time.Now(), + } + rp.strategy = rulesParseInline(payload, rp.strategy) + return rp +} From 5a9ad0ed3cb2039e741d3bd1f155666b0518b1fc Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 19 Dec 2024 09:29:17 +0800 Subject: [PATCH 300/311] chore: code cleanup --- rules/common/base.go | 4 ++++ rules/logic/logic.go | 14 ++++++-------- rules/parser.go | 2 ++ rules/provider/parse.go | 5 ++--- rules/provider/provider.go | 14 ++++---------- 5 files changed, 18 insertions(+), 21 deletions(-) diff --git a/rules/common/base.go b/rules/common/base.go index 496bcaee..1abbe72c 100644 --- a/rules/common/base.go +++ b/rules/common/base.go @@ -3,6 +3,8 @@ package common import ( "errors" + C "github.com/metacubex/mihomo/constant" + "golang.org/x/exp/slices" ) @@ -38,3 +40,5 @@ func ParseParams(params []string) (isSrc bool, noResolve bool) { } return } + +type ParseRuleFunc func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (C.Rule, error) diff --git a/rules/logic/logic.go b/rules/logic/logic.go index 6e672852..f7b5a987 100644 --- a/rules/logic/logic.go +++ b/rules/logic/logic.go @@ -23,9 +23,7 @@ type Logic struct { payloadOnce sync.Once } -type ParseRuleFunc func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (C.Rule, error) - -func NewSubRule(payload, adapter string, subRules map[string][]C.Rule, parseRule ParseRuleFunc) (*Logic, error) { +func NewSubRule(payload, adapter string, subRules map[string][]C.Rule, parseRule common.ParseRuleFunc) (*Logic, error) { logic := &Logic{Base: &common.Base{}, payload: payload, adapter: adapter, ruleType: C.SubRules, subRules: subRules} err := logic.parsePayload(fmt.Sprintf("(%s)", payload), parseRule) if err != nil { @@ -38,7 +36,7 @@ func NewSubRule(payload, adapter string, subRules map[string][]C.Rule, parseRule return logic, nil } -func NewNOT(payload string, adapter string, parseRule ParseRuleFunc) (*Logic, error) { +func NewNOT(payload string, adapter string, parseRule common.ParseRuleFunc) (*Logic, error) { logic := &Logic{Base: &common.Base{}, payload: payload, adapter: adapter, ruleType: C.NOT} err := logic.parsePayload(payload, parseRule) if err != nil { @@ -51,7 +49,7 @@ func NewNOT(payload string, adapter string, parseRule ParseRuleFunc) (*Logic, er return logic, nil } -func NewOR(payload string, adapter string, parseRule ParseRuleFunc) (*Logic, error) { +func NewOR(payload string, adapter string, parseRule common.ParseRuleFunc) (*Logic, error) { logic := &Logic{Base: &common.Base{}, payload: payload, adapter: adapter, ruleType: C.OR} err := logic.parsePayload(payload, parseRule) if err != nil { @@ -60,7 +58,7 @@ func NewOR(payload string, adapter string, parseRule ParseRuleFunc) (*Logic, err return logic, nil } -func NewAND(payload string, adapter string, parseRule ParseRuleFunc) (*Logic, error) { +func NewAND(payload string, adapter string, parseRule common.ParseRuleFunc) (*Logic, error) { logic := &Logic{Base: &common.Base{}, payload: payload, adapter: adapter, ruleType: C.AND} err := logic.parsePayload(payload, parseRule) if err != nil { @@ -79,7 +77,7 @@ func (r Range) containRange(preStart, preEnd int) bool { return preStart < r.start && preEnd > r.end } -func (logic *Logic) payloadToRule(subPayload string, parseRule ParseRuleFunc) (C.Rule, error) { +func (logic *Logic) payloadToRule(subPayload string, parseRule common.ParseRuleFunc) (C.Rule, error) { splitStr := strings.SplitN(subPayload, ",", 2) if len(splitStr) < 2 { return nil, fmt.Errorf("[%s] format is error", subPayload) @@ -160,7 +158,7 @@ func (logic *Logic) findSubRuleRange(payload string, ruleRanges []Range) []Range return subRuleRange } -func (logic *Logic) parsePayload(payload string, parseRule ParseRuleFunc) error { +func (logic *Logic) parsePayload(payload string, parseRule common.ParseRuleFunc) error { regex, err := regexp.Compile("\\(.*\\)") if err != nil { return err diff --git a/rules/parser.go b/rules/parser.go index 4f7ddbe1..83325433 100644 --- a/rules/parser.go +++ b/rules/parser.go @@ -91,3 +91,5 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string] return } + +var _ RC.ParseRuleFunc = ParseRule diff --git a/rules/provider/parse.go b/rules/provider/parse.go index 51042631..4589317d 100644 --- a/rules/provider/parse.go +++ b/rules/provider/parse.go @@ -9,6 +9,7 @@ import ( "github.com/metacubex/mihomo/component/resource" C "github.com/metacubex/mihomo/constant" P "github.com/metacubex/mihomo/constant/provider" + "github.com/metacubex/mihomo/rules/common" ) var ( @@ -27,9 +28,7 @@ type ruleProviderSchema struct { Payload []string `provider:"payload,omitempty"` } -type parseRuleFunc func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error) - -func ParseRuleProvider(name string, mapping map[string]any, parse parseRuleFunc) (P.RuleProvider, error) { +func ParseRuleProvider(name string, mapping map[string]any, parse common.ParseRuleFunc) (P.RuleProvider, error) { schema := &ruleProviderSchema{} decoder := structure.NewDecoder(structure.Option{TagName: "provider", WeaklyTypedInput: true}) if err := decoder.Decode(mapping, schema); err != nil { diff --git a/rules/provider/provider.go b/rules/provider/provider.go index a62b1009..13c713c8 100644 --- a/rules/provider/provider.go +++ b/rules/provider/provider.go @@ -13,6 +13,7 @@ import ( "github.com/metacubex/mihomo/component/resource" C "github.com/metacubex/mihomo/constant" P "github.com/metacubex/mihomo/constant/provider" + "github.com/metacubex/mihomo/rules/common" "gopkg.in/yaml.v3" ) @@ -132,9 +133,7 @@ func (rp *RuleSetProvider) Close() error { return rp.ruleSetProvider.Close() } -func NewRuleSetProvider(name string, behavior P.RuleBehavior, format P.RuleFormat, interval time.Duration, vehicle P.Vehicle, - parse parseRuleFunc, -) P.RuleProvider { +func NewRuleSetProvider(name string, behavior P.RuleBehavior, format P.RuleFormat, interval time.Duration, vehicle P.Vehicle, parse common.ParseRuleFunc) P.RuleProvider { rp := &ruleSetProvider{ ruleSetProviderBase: ruleSetProviderBase{ behavior: behavior, @@ -160,7 +159,7 @@ func NewRuleSetProvider(name string, behavior P.RuleBehavior, format P.RuleForma return wrapper } -func newStrategy(behavior P.RuleBehavior, parse parseRuleFunc) ruleStrategy { +func newStrategy(behavior P.RuleBehavior, parse common.ParseRuleFunc) ruleStrategy { switch behavior { case P.Domain: strategy := NewDomainStrategy() @@ -324,12 +323,7 @@ func (i *inlineProvider) MarshalJSON() ([]byte, error) { }) } -func newInlineProvider( - name string, - behavior P.RuleBehavior, - payload []string, - parse parseRuleFunc, -) P.RuleProvider { +func newInlineProvider(name string, behavior P.RuleBehavior, payload []string, parse common.ParseRuleFunc) P.RuleProvider { rp := &inlineProvider{ ruleSetProviderBase: ruleSetProviderBase{ behavior: behavior, From 89dfabe9b36b22df896dcd1ab03c67c667ec20f3 Mon Sep 17 00:00:00 2001 From: valord577 <30427460+valord577@users.noreply.github.com> Date: Thu, 19 Dec 2024 15:55:47 +0800 Subject: [PATCH 301/311] chore: align time fields in logs (#1704) ref: A comma or decimal point followed by one or more zeros represents a fractional second, printed to the given number of decimal places. A comma or decimal point followed by one or more nines represents a fractional second, printed to the given number of decimal places, with trailing zeros removed. For example "15:04:05,000" or "15:04:05.000" formats or parses with millisecond precision. --- log/log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/log/log.go b/log/log.go index 6f565e7c..f1c68b42 100644 --- a/log/log.go +++ b/log/log.go @@ -20,7 +20,7 @@ func init() { log.SetLevel(log.DebugLevel) log.SetFormatter(&log.TextFormatter{ FullTimestamp: true, - TimestampFormat: "2006-01-02T15:04:05.999999999Z07:00", + TimestampFormat: "2006-01-02T15:04:05.000000000Z07:00", EnvironmentOverrideColors: true, }) } From 20739f5db7a787e6a6642d8cd40ade18cfa04795 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 25 Dec 2024 10:31:40 +0800 Subject: [PATCH 302/311] chore: code cleanup --- rules/provider/parse.go | 2 +- rules/provider/provider.go | 142 ++++++++++++++++++++----------------- 2 files changed, 77 insertions(+), 67 deletions(-) diff --git a/rules/provider/parse.go b/rules/provider/parse.go index 4589317d..97d513fc 100644 --- a/rules/provider/parse.go +++ b/rules/provider/parse.go @@ -58,7 +58,7 @@ func ParseRuleProvider(name string, mapping map[string]any, parse common.ParseRu } vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, nil, resource.DefaultHttpTimeout, schema.SizeLimit) case "inline": - return newInlineProvider(name, behavior, schema.Payload, parse), nil + return NewInlineProvider(name, behavior, schema.Payload, parse), nil default: return nil, fmt.Errorf("unsupported vehicle type: %s", schema.Type) } diff --git a/rules/provider/provider.go b/rules/provider/provider.go index 13c713c8..5c456832 100644 --- a/rules/provider/provider.go +++ b/rules/provider/provider.go @@ -24,21 +24,6 @@ func SetTunnel(t P.Tunnel) { tunnel = t } -type ruleSetProvider struct { - ruleSetProviderBase - *resource.Fetcher[ruleStrategy] - format P.RuleFormat -} - -type ruleSetProviderBase struct { - behavior P.RuleBehavior - strategy ruleStrategy -} - -type RuleSetProvider struct { - *ruleSetProvider -} - type RulePayload struct { /** key: Domain or IP Cidr @@ -48,6 +33,17 @@ type RulePayload struct { Rules []string `yaml:"rules"` } +type providerForApi struct { + Behavior string `json:"behavior"` + Format string `json:"format"` + Name string `json:"name"` + RuleCount int `json:"ruleCount"` + Type string `json:"type"` + VehicleType string `json:"vehicleType"` + UpdatedAt time.Time `json:"updatedAt"` + Payload []string `json:"payload,omitempty"` +} + type ruleStrategy interface { Behavior() P.RuleBehavior Match(metadata *C.Metadata) bool @@ -66,10 +62,49 @@ type mrsRuleStrategy interface { DumpMrs(f func(key string) bool) } -func (rp *ruleSetProviderBase) Type() P.ProviderType { +type baseProvider struct { + behavior P.RuleBehavior + strategy ruleStrategy +} + +func (bp *baseProvider) Type() P.ProviderType { return P.Rule } +func (bp *baseProvider) Behavior() P.RuleBehavior { + return bp.behavior +} + +func (bp *baseProvider) Count() int { + return bp.strategy.Count() +} + +func (bp *baseProvider) Match(metadata *C.Metadata) bool { + return bp.strategy != nil && bp.strategy.Match(metadata) +} + +func (bp *baseProvider) ShouldResolveIP() bool { + return bp.strategy.ShouldResolveIP() +} + +func (bp *baseProvider) ShouldFindProcess() bool { + return bp.strategy.ShouldFindProcess() +} + +func (bp *baseProvider) Strategy() any { + return bp.strategy +} + +type ruleSetProvider struct { + baseProvider + *resource.Fetcher[ruleStrategy] + format P.RuleFormat +} + +type RuleSetProvider struct { + *ruleSetProvider +} + func (rp *ruleSetProvider) Initial() error { _, err := rp.Fetcher.Initial() return err @@ -80,41 +115,6 @@ func (rp *ruleSetProvider) Update() error { return err } -func (rp *ruleSetProviderBase) Behavior() P.RuleBehavior { - return rp.behavior -} - -func (rp *ruleSetProviderBase) Count() int { - return rp.strategy.Count() -} - -func (rp *ruleSetProviderBase) Match(metadata *C.Metadata) bool { - return rp.strategy != nil && rp.strategy.Match(metadata) -} - -func (rp *ruleSetProviderBase) ShouldResolveIP() bool { - return rp.strategy.ShouldResolveIP() -} - -func (rp *ruleSetProviderBase) ShouldFindProcess() bool { - return rp.strategy.ShouldFindProcess() -} - -func (rp *ruleSetProviderBase) Strategy() any { - return rp.strategy -} - -type providerForApi struct { - Behavior string `json:"behavior"` - Format string `json:"format"` - Name string `json:"name"` - RuleCount int `json:"ruleCount"` - Type string `json:"type"` - VehicleType string `json:"vehicleType"` - UpdatedAt time.Time `json:"updatedAt"` - Payload []string `json:"payload,omitempty"` -} - func (rp *ruleSetProvider) MarshalJSON() ([]byte, error) { return json.Marshal( providerForApi{ @@ -135,7 +135,7 @@ func (rp *RuleSetProvider) Close() error { func NewRuleSetProvider(name string, behavior P.RuleBehavior, format P.RuleFormat, interval time.Duration, vehicle P.Vehicle, parse common.ParseRuleFunc) P.RuleProvider { rp := &ruleSetProvider{ - ruleSetProviderBase: ruleSetProviderBase{ + baseProvider: baseProvider{ behavior: behavior, }, format: format, @@ -285,11 +285,15 @@ func rulesParseInline(rs []string, strategy ruleStrategy) ruleStrategy { return strategy } +type InlineProvider struct { + *inlineProvider +} + type inlineProvider struct { - ruleSetProviderBase - name string - updateTime time.Time - payload []string + baseProvider + name string + updateAt time.Time + payload []string } func (i *inlineProvider) Name() string { @@ -302,7 +306,7 @@ func (i *inlineProvider) Initial() error { func (i *inlineProvider) Update() error { // make api update happy - i.updateTime = time.Now() + i.updateAt = time.Now() return nil } @@ -318,21 +322,27 @@ func (i *inlineProvider) MarshalJSON() ([]byte, error) { RuleCount: i.strategy.Count(), Type: i.Type().String(), VehicleType: i.VehicleType().String(), - UpdatedAt: i.updateTime, + UpdatedAt: i.updateAt, Payload: i.payload, }) } -func newInlineProvider(name string, behavior P.RuleBehavior, payload []string, parse common.ParseRuleFunc) P.RuleProvider { - rp := &inlineProvider{ - ruleSetProviderBase: ruleSetProviderBase{ +func NewInlineProvider(name string, behavior P.RuleBehavior, payload []string, parse common.ParseRuleFunc) P.RuleProvider { + ip := &inlineProvider{ + baseProvider: baseProvider{ behavior: behavior, strategy: newStrategy(behavior, parse), }, - payload: payload, - name: name, - updateTime: time.Now(), + payload: payload, + name: name, + updateAt: time.Now(), } - rp.strategy = rulesParseInline(payload, rp.strategy) - return rp + ip.strategy = rulesParseInline(payload, ip.strategy) + + wrapper := &InlineProvider{ + ip, + } + + //runtime.SetFinalizer(wrapper, (*InlineProvider).Close) + return wrapper } From 72a126e5809acae4e9058c51f4e883dc7c8c6e19 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 25 Dec 2024 10:23:55 +0800 Subject: [PATCH 303/311] feat: support inline proxy provider --- adapter/provider/parser.go | 35 +-- adapter/provider/provider.go | 349 +++++++++++++++----------- adapter/provider/subscription_info.go | 13 +- component/resource/vehicle.go | 10 +- constant/provider/interface.go | 1 - docs/config.yaml | 11 + 6 files changed, 237 insertions(+), 182 deletions(-) diff --git a/adapter/provider/parser.go b/adapter/provider/parser.go index b305df7f..cd5c3ff7 100644 --- a/adapter/provider/parser.go +++ b/adapter/provider/parser.go @@ -57,16 +57,17 @@ type OverrideSchema struct { } type proxyProviderSchema struct { - Type string `provider:"type"` - Path string `provider:"path,omitempty"` - URL string `provider:"url,omitempty"` - Proxy string `provider:"proxy,omitempty"` - Interval int `provider:"interval,omitempty"` - Filter string `provider:"filter,omitempty"` - ExcludeFilter string `provider:"exclude-filter,omitempty"` - ExcludeType string `provider:"exclude-type,omitempty"` - DialerProxy string `provider:"dialer-proxy,omitempty"` - SizeLimit int64 `provider:"size-limit,omitempty"` + Type string `provider:"type"` + Path string `provider:"path,omitempty"` + URL string `provider:"url,omitempty"` + Proxy string `provider:"proxy,omitempty"` + Interval int `provider:"interval,omitempty"` + Filter string `provider:"filter,omitempty"` + ExcludeFilter string `provider:"exclude-filter,omitempty"` + ExcludeType string `provider:"exclude-type,omitempty"` + DialerProxy string `provider:"dialer-proxy,omitempty"` + SizeLimit int64 `provider:"size-limit,omitempty"` + Payload []map[string]any `provider:"payload,omitempty"` HealthCheck healthCheckSchema `provider:"health-check,omitempty"` Override OverrideSchema `provider:"override,omitempty"` @@ -99,6 +100,11 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide } hc := NewHealthCheck([]C.Proxy{}, schema.HealthCheck.URL, uint(schema.HealthCheck.TestTimeout), hcInterval, schema.HealthCheck.Lazy, expectedStatus) + parser, err := NewProxiesParser(schema.Filter, schema.ExcludeFilter, schema.ExcludeType, schema.DialerProxy, schema.Override) + if err != nil { + return nil, err + } + var vehicle types.Vehicle switch schema.Type { case "file": @@ -113,16 +119,13 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide } } vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, schema.Header, resource.DefaultHttpTimeout, schema.SizeLimit) + case "inline": + return NewInlineProvider(name, schema.Payload, parser, hc) default: return nil, fmt.Errorf("%w: %s", errVehicleType, schema.Type) } interval := time.Duration(uint(schema.Interval)) * time.Second - filter := schema.Filter - excludeFilter := schema.ExcludeFilter - excludeType := schema.ExcludeType - dialerProxy := schema.DialerProxy - override := schema.Override - return NewProxySetProvider(name, interval, filter, excludeFilter, excludeType, dialerProxy, override, vehicle, hc) + return NewProxySetProvider(name, interval, parser, vehicle, hc) } diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index 2fe9633c..8558c434 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "net/http" "reflect" "runtime" "strings" @@ -30,44 +31,101 @@ type ProxySchema struct { Proxies []map[string]any `yaml:"proxies"` } +type providerForApi struct { + Name string `json:"name"` + Type string `json:"type"` + VehicleType string `json:"vehicleType"` + Proxies []C.Proxy `json:"proxies"` + TestUrl string `json:"testUrl"` + ExpectedStatus string `json:"expectedStatus"` + UpdatedAt time.Time `json:"updatedAt,omitempty"` + SubscriptionInfo *SubscriptionInfo `json:"subscriptionInfo,omitempty"` +} + +type baseProvider struct { + name string + proxies []C.Proxy + healthCheck *HealthCheck + version uint32 +} + +func (bp *baseProvider) Name() string { + return bp.name +} + +func (bp *baseProvider) Version() uint32 { + return bp.version +} + +func (bp *baseProvider) HealthCheck() { + bp.healthCheck.check() +} + +func (bp *baseProvider) Type() types.ProviderType { + return types.Proxy +} + +func (bp *baseProvider) Proxies() []C.Proxy { + return bp.proxies +} + +func (bp *baseProvider) Count() int { + return len(bp.proxies) +} + +func (bp *baseProvider) Touch() { + bp.healthCheck.touch() +} + +func (bp *baseProvider) HealthCheckURL() string { + return bp.healthCheck.url +} + +func (bp *baseProvider) RegisterHealthCheckTask(url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint) { + bp.healthCheck.registerHealthCheckTask(url, expectedStatus, filter, interval) +} + +func (bp *baseProvider) setProxies(proxies []C.Proxy) { + bp.proxies = proxies + bp.healthCheck.setProxy(proxies) + if bp.healthCheck.auto() { + go bp.healthCheck.check() + } +} + +func (bp *baseProvider) Close() error { + bp.healthCheck.close() + return nil +} + // ProxySetProvider for auto gc type ProxySetProvider struct { *proxySetProvider } type proxySetProvider struct { + baseProvider *resource.Fetcher[[]C.Proxy] - proxies []C.Proxy - healthCheck *HealthCheck - version uint32 subscriptionInfo *SubscriptionInfo } func (pp *proxySetProvider) MarshalJSON() ([]byte, error) { - return json.Marshal(map[string]any{ - "name": pp.Name(), - "type": pp.Type().String(), - "vehicleType": pp.VehicleType().String(), - "proxies": pp.Proxies(), - "testUrl": pp.healthCheck.url, - "expectedStatus": pp.healthCheck.expectedStatus.String(), - "updatedAt": pp.UpdatedAt(), - "subscriptionInfo": pp.subscriptionInfo, + return json.Marshal(providerForApi{ + Name: pp.Name(), + Type: pp.Type().String(), + VehicleType: pp.VehicleType().String(), + Proxies: pp.Proxies(), + TestUrl: pp.healthCheck.url, + ExpectedStatus: pp.healthCheck.expectedStatus.String(), + UpdatedAt: pp.UpdatedAt(), + SubscriptionInfo: pp.subscriptionInfo, }) } -func (pp *proxySetProvider) Version() uint32 { - return pp.version -} - func (pp *proxySetProvider) Name() string { return pp.Fetcher.Name() } -func (pp *proxySetProvider) HealthCheck() { - pp.healthCheck.check() -} - func (pp *proxySetProvider) Update() error { _, _, err := pp.Fetcher.Update() return err @@ -79,54 +137,12 @@ func (pp *proxySetProvider) Initial() error { return err } if subscriptionInfo := cachefile.Cache().GetSubscriptionInfo(pp.Name()); subscriptionInfo != "" { - pp.SetSubscriptionInfo(subscriptionInfo) + pp.subscriptionInfo.Update(subscriptionInfo) } pp.closeAllConnections() return nil } -func (pp *proxySetProvider) Type() types.ProviderType { - return types.Proxy -} - -func (pp *proxySetProvider) Proxies() []C.Proxy { - return pp.proxies -} - -func (pp *proxySetProvider) Count() int { - return len(pp.proxies) -} - -func (pp *proxySetProvider) Touch() { - pp.healthCheck.touch() -} - -func (pp *proxySetProvider) HealthCheckURL() string { - return pp.healthCheck.url -} - -func (pp *proxySetProvider) RegisterHealthCheckTask(url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint) { - pp.healthCheck.registerHealthCheckTask(url, expectedStatus, filter, interval) -} - -func (pp *proxySetProvider) setProxies(proxies []C.Proxy) { - pp.proxies = proxies - pp.healthCheck.setProxy(proxies) - if pp.healthCheck.auto() { - go pp.healthCheck.check() - } -} - -func (pp *proxySetProvider) SetSubscriptionInfo(userInfo string) { - pp.subscriptionInfo = NewSubscriptionInfo(userInfo) -} - -func (pp *proxySetProvider) SetProvider(provider types.ProxyProvider) { - if httpVehicle, ok := pp.Vehicle().(*resource.HTTPVehicle); ok { - httpVehicle.SetProvider(provider) - } -} - func (pp *proxySetProvider) closeAllConnections() { statistic.DefaultManager.Range(func(c statistic.Tracker) bool { for _, chain := range c.Chains() { @@ -140,44 +156,37 @@ func (pp *proxySetProvider) closeAllConnections() { } func (pp *proxySetProvider) Close() error { - pp.healthCheck.close() + _ = pp.baseProvider.Close() return pp.Fetcher.Close() } -func NewProxySetProvider(name string, interval time.Duration, filter string, excludeFilter string, excludeType string, dialerProxy string, override OverrideSchema, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) { - excludeFilterReg, err := regexp2.Compile(excludeFilter, regexp2.None) - if err != nil { - return nil, fmt.Errorf("invalid excludeFilter regex: %w", err) - } - var excludeTypeArray []string - if excludeType != "" { - excludeTypeArray = strings.Split(excludeType, "|") - } - - var filterRegs []*regexp2.Regexp - for _, filter := range strings.Split(filter, "`") { - filterReg, err := regexp2.Compile(filter, regexp2.None) - if err != nil { - return nil, fmt.Errorf("invalid filter regex: %w", err) - } - filterRegs = append(filterRegs, filterReg) - } - +func NewProxySetProvider(name string, interval time.Duration, parser resource.Parser[[]C.Proxy], vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) { if hc.auto() { go hc.process() } + si := new(SubscriptionInfo) pd := &proxySetProvider{ - proxies: []C.Proxy{}, - healthCheck: hc, + baseProvider: baseProvider{ + name: name, + proxies: []C.Proxy{}, + healthCheck: hc, + }, + subscriptionInfo: si, } - fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, excludeTypeArray, filterRegs, excludeFilterReg, dialerProxy, override), proxiesOnUpdate(pd)) + fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, parser, proxiesOnUpdate(pd)) pd.Fetcher = fetcher - wrapper := &ProxySetProvider{pd} if httpVehicle, ok := vehicle.(*resource.HTTPVehicle); ok { - httpVehicle.SetProvider(wrapper) + httpVehicle.SetInRead(func(resp *http.Response) { + if subscriptionInfo := resp.Header.Get("subscription-userinfo"); subscriptionInfo != "" { + cachefile.Cache().SetSubscriptionInfo(name, subscriptionInfo) + si.Update(subscriptionInfo) + } + }) } + + wrapper := &ProxySetProvider{pd} runtime.SetFinalizer(wrapper, (*ProxySetProvider).Close) return wrapper, nil } @@ -187,8 +196,73 @@ func (pp *ProxySetProvider) Close() error { return pp.proxySetProvider.Close() } -func (pp *ProxySetProvider) SetProvider(provider types.ProxyProvider) { - pp.proxySetProvider.SetProvider(provider) +// InlineProvider for auto gc +type InlineProvider struct { + *inlineProvider +} + +type inlineProvider struct { + baseProvider + updateAt time.Time +} + +func (ip *inlineProvider) MarshalJSON() ([]byte, error) { + return json.Marshal(providerForApi{ + Name: ip.Name(), + Type: ip.Type().String(), + VehicleType: ip.VehicleType().String(), + Proxies: ip.Proxies(), + TestUrl: ip.healthCheck.url, + ExpectedStatus: ip.healthCheck.expectedStatus.String(), + UpdatedAt: ip.updateAt, + }) +} + +func (ip *inlineProvider) VehicleType() types.VehicleType { + return types.Inline +} + +func (ip *inlineProvider) Initial() error { + return nil +} + +func (ip *inlineProvider) Update() error { + // make api update happy + ip.updateAt = time.Now() + return nil +} + +func NewInlineProvider(name string, payload []map[string]any, parser resource.Parser[[]C.Proxy], hc *HealthCheck) (*InlineProvider, error) { + if hc.auto() { + go hc.process() + } + + ps := ProxySchema{Proxies: payload} + buf, err := yaml.Marshal(ps) + if err != nil { + return nil, err + } + proxies, err := parser(buf) + if err != nil { + return nil, err + } + + ip := &inlineProvider{ + baseProvider: baseProvider{ + name: name, + proxies: proxies, + healthCheck: hc, + }, + updateAt: time.Now(), + } + wrapper := &InlineProvider{ip} + runtime.SetFinalizer(wrapper, (*InlineProvider).Close) + return wrapper, nil +} + +func (ip *InlineProvider) Close() error { + runtime.SetFinalizer(ip, nil) + return ip.baseProvider.Close() } // CompatibleProvider for auto gc @@ -197,36 +271,20 @@ type CompatibleProvider struct { } type compatibleProvider struct { - name string - healthCheck *HealthCheck - subscriptionInfo *SubscriptionInfo - proxies []C.Proxy - version uint32 + baseProvider } func (cp *compatibleProvider) MarshalJSON() ([]byte, error) { - return json.Marshal(map[string]any{ - "name": cp.Name(), - "type": cp.Type().String(), - "vehicleType": cp.VehicleType().String(), - "proxies": cp.Proxies(), - "testUrl": cp.healthCheck.url, - "expectedStatus": cp.healthCheck.expectedStatus.String(), + return json.Marshal(providerForApi{ + Name: cp.Name(), + Type: cp.Type().String(), + VehicleType: cp.VehicleType().String(), + Proxies: cp.Proxies(), + TestUrl: cp.healthCheck.url, + ExpectedStatus: cp.healthCheck.expectedStatus.String(), }) } -func (cp *compatibleProvider) Version() uint32 { - return cp.version -} - -func (cp *compatibleProvider) Name() string { - return cp.name -} - -func (cp *compatibleProvider) HealthCheck() { - cp.healthCheck.check() -} - func (cp *compatibleProvider) Update() error { return nil } @@ -242,39 +300,6 @@ func (cp *compatibleProvider) VehicleType() types.VehicleType { return types.Compatible } -func (cp *compatibleProvider) Type() types.ProviderType { - return types.Proxy -} - -func (cp *compatibleProvider) Proxies() []C.Proxy { - return cp.proxies -} - -func (cp *compatibleProvider) Count() int { - return len(cp.proxies) -} - -func (cp *compatibleProvider) Touch() { - cp.healthCheck.touch() -} - -func (cp *compatibleProvider) HealthCheckURL() string { - return cp.healthCheck.url -} - -func (cp *compatibleProvider) RegisterHealthCheckTask(url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint) { - cp.healthCheck.registerHealthCheckTask(url, expectedStatus, filter, interval) -} - -func (cp *compatibleProvider) Close() error { - cp.healthCheck.close() - return nil -} - -func (cp *compatibleProvider) SetSubscriptionInfo(userInfo string) { - cp.subscriptionInfo = NewSubscriptionInfo(userInfo) -} - func NewCompatibleProvider(name string, proxies []C.Proxy, hc *HealthCheck) (*CompatibleProvider, error) { if len(proxies) == 0 { return nil, errors.New("provider need one proxy at least") @@ -285,9 +310,11 @@ func NewCompatibleProvider(name string, proxies []C.Proxy, hc *HealthCheck) (*Co } pd := &compatibleProvider{ - name: name, - proxies: proxies, - healthCheck: hc, + baseProvider: baseProvider{ + name: name, + proxies: proxies, + healthCheck: hc, + }, } wrapper := &CompatibleProvider{pd} @@ -307,7 +334,25 @@ func proxiesOnUpdate(pd *proxySetProvider) func([]C.Proxy) { } } -func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray []string, filterRegs []*regexp2.Regexp, excludeFilterReg *regexp2.Regexp, dialerProxy string, override OverrideSchema) resource.Parser[[]C.Proxy] { +func NewProxiesParser(filter string, excludeFilter string, excludeType string, dialerProxy string, override OverrideSchema) (resource.Parser[[]C.Proxy], error) { + excludeFilterReg, err := regexp2.Compile(excludeFilter, regexp2.None) + if err != nil { + return nil, fmt.Errorf("invalid excludeFilter regex: %w", err) + } + var excludeTypeArray []string + if excludeType != "" { + excludeTypeArray = strings.Split(excludeType, "|") + } + + var filterRegs []*regexp2.Regexp + for _, filter := range strings.Split(filter, "`") { + filterReg, err := regexp2.Compile(filter, regexp2.None) + if err != nil { + return nil, fmt.Errorf("invalid filter regex: %w", err) + } + filterRegs = append(filterRegs, filterReg) + } + return func(buf []byte) ([]C.Proxy, error) { schema := &ProxySchema{} @@ -422,5 +467,5 @@ func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray } return proxies, nil - } + }, nil } diff --git a/adapter/provider/subscription_info.go b/adapter/provider/subscription_info.go index 412b4342..194e3984 100644 --- a/adapter/provider/subscription_info.go +++ b/adapter/provider/subscription_info.go @@ -15,9 +15,8 @@ type SubscriptionInfo struct { Expire int64 } -func NewSubscriptionInfo(userinfo string) (si *SubscriptionInfo) { +func (info *SubscriptionInfo) Update(userinfo string) { userinfo = strings.ReplaceAll(strings.ToLower(userinfo), " ", "") - si = new(SubscriptionInfo) for _, field := range strings.Split(userinfo, ";") { name, value, ok := strings.Cut(field, "=") @@ -33,17 +32,15 @@ func NewSubscriptionInfo(userinfo string) (si *SubscriptionInfo) { switch name { case "upload": - si.Upload = intValue + info.Upload = intValue case "download": - si.Download = intValue + info.Download = intValue case "total": - si.Total = intValue + info.Total = intValue case "expire": - si.Expire = intValue + info.Expire = intValue } } - - return si } func parseValue(value string) (int64, error) { diff --git a/component/resource/vehicle.go b/component/resource/vehicle.go index 18cebf00..00b3170b 100644 --- a/component/resource/vehicle.go +++ b/component/resource/vehicle.go @@ -90,6 +90,7 @@ type HTTPVehicle struct { header http.Header timeout time.Duration sizeLimit int64 + inRead func(response *http.Response) provider types.ProxyProvider } @@ -113,8 +114,8 @@ func (h *HTTPVehicle) Write(buf []byte) error { return safeWrite(h.path, buf) } -func (h *HTTPVehicle) SetProvider(provider types.ProxyProvider) { - h.provider = provider +func (h *HTTPVehicle) SetInRead(fn func(response *http.Response)) { + h.inRead = fn } func (h *HTTPVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []byte, hash utils.HashType, err error) { @@ -140,9 +141,8 @@ func (h *HTTPVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []b } defer resp.Body.Close() - if subscriptionInfo := resp.Header.Get("subscription-userinfo"); h.provider != nil && subscriptionInfo != "" { - cachefile.Cache().SetSubscriptionInfo(h.provider.Name(), subscriptionInfo) - h.provider.SetSubscriptionInfo(subscriptionInfo) + if h.inRead != nil { + h.inRead(resp) } if resp.StatusCode < 200 || resp.StatusCode > 299 { diff --git a/constant/provider/interface.go b/constant/provider/interface.go index 4309adac..8c6ee6f8 100644 --- a/constant/provider/interface.go +++ b/constant/provider/interface.go @@ -84,7 +84,6 @@ type ProxyProvider interface { Version() uint32 RegisterHealthCheckTask(url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint) HealthCheckURL() string - SetSubscriptionInfo(userInfo string) } // RuleProvider interface diff --git a/docs/config.yaml b/docs/config.yaml index ca48f0e2..6dfeb586 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -975,6 +975,17 @@ proxy-providers: # - pattern: "IPLC-(.*?)倍" # target: "iplc x $1" + provider2: + type: inline + dialer-proxy: proxy + payload: + - name: "ss1" + type: ss + server: server + port: 443 + cipher: chacha20-ietf-poly1305 + password: "password" + test: type: file path: /test.yaml From 301c78ff9ae5fccce801a03b720b1413fe25a13e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 26 Dec 2024 10:50:08 +0800 Subject: [PATCH 304/311] chore: update sing-tun to v0.4.5 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index dbc8acea..4d21fbb0 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 github.com/metacubex/sing-shadowsocks v0.2.8 github.com/metacubex/sing-shadowsocks2 v0.2.2 - github.com/metacubex/sing-tun v0.4.2 + github.com/metacubex/sing-tun v0.4.5 github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589 github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa diff --git a/go.sum b/go.sum index 63e7a6ad..1d874f38 100644 --- a/go.sum +++ b/go.sum @@ -120,8 +120,8 @@ github.com/metacubex/sing-shadowsocks v0.2.8 h1:wIhlaigswzjPw4hej75sEvWte3QR0+AJ github.com/metacubex/sing-shadowsocks v0.2.8/go.mod h1:X3x88XtJpBxG0W0/ECOJL6Ib0SJ3xdniAkU/6/RMWU0= github.com/metacubex/sing-shadowsocks2 v0.2.2 h1:eaf42uVx4Lr21S6MDYs0ZdTvGA0GEhDpb9no4+gdXPo= github.com/metacubex/sing-shadowsocks2 v0.2.2/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q= -github.com/metacubex/sing-tun v0.4.2 h1:fwrQp3P536Pswu6gR1FJ+8GH55e+t2+B8LHIjwRtWbc= -github.com/metacubex/sing-tun v0.4.2/go.mod h1:V0N4rr0dWPBEE20ESkTXdbtx2riQYcb6YtwC5w/9wl0= +github.com/metacubex/sing-tun v0.4.5 h1:kWSyQzuzHI40r50OFBczfWIDvMBMy1RIk+JsXeBPRB0= +github.com/metacubex/sing-tun v0.4.5/go.mod h1:V0N4rr0dWPBEE20ESkTXdbtx2riQYcb6YtwC5w/9wl0= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosqY8xKDp3pqTW3qbrCprZ1l6WkrXSFSCwyY4I= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589 h1:Z6bNy0HLTjx6BKIkV48sV/yia/GP8Bnyb5JQuGgSGzg= From a9ce5da09d38f6057bd248cd146cbd8c05dc9fd6 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 28 Dec 2024 10:59:06 +0800 Subject: [PATCH 305/311] fix: key missing for tun inbound https://github.com/MetaCubeX/mihomo/issues/1672 --- listener/inbound/tun.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/listener/inbound/tun.go b/listener/inbound/tun.go index 77ad6bd6..1b0a6a80 100644 --- a/listener/inbound/tun.go +++ b/listener/inbound/tun.go @@ -23,18 +23,18 @@ type TunOption struct { GSOMaxSize uint32 `inbound:"gso-max-size,omitempty"` Inet4Address []string `inbound:"inet4-address,omitempty"` Inet6Address []string `inbound:"inet6-address,omitempty"` - IPRoute2TableIndex int `inbound:"iproute2-table-index"` - IPRoute2RuleIndex int `inbound:"iproute2-rule-index"` - AutoRedirect bool `inbound:"auto-redirect"` - AutoRedirectInputMark uint32 `inbound:"auto-redirect-input-mark"` - AutoRedirectOutputMark uint32 `inbound:"auto-redirect-output-mark"` + IPRoute2TableIndex int `inbound:"iproute2-table-index,omitempty"` + IPRoute2RuleIndex int `inbound:"iproute2-rule-index,omitempty"` + AutoRedirect bool `inbound:"auto-redirect,omitempty"` + AutoRedirectInputMark uint32 `inbound:"auto-redirect-input-mark,omitempty"` + AutoRedirectOutputMark uint32 `inbound:"auto-redirect-output-mark,omitempty"` StrictRoute bool `inbound:"strict-route,omitempty"` - RouteAddress []string `inbound:"route-address"` - RouteAddressSet []string `inbound:"route-address-set"` - RouteExcludeAddress []string `inbound:"route-exclude-address"` - RouteExcludeAddressSet []string `inbound:"route-exclude-address-set"` + RouteAddress []string `inbound:"route-address,omitempty"` + RouteAddressSet []string `inbound:"route-address-set,omitempty"` + RouteExcludeAddress []string `inbound:"route-exclude-address,omitempty"` + RouteExcludeAddressSet []string `inbound:"route-exclude-address-set,omitempty"` IncludeInterface []string `inbound:"include-interface,omitempty"` - ExcludeInterface []string `inbound:"exclude-interface"` + ExcludeInterface []string `inbound:"exclude-interface,omitempty"` IncludeUID []uint32 `inbound:"include-uid,omitempty"` IncludeUIDRange []string `inbound:"include-uid-range,omitempty"` ExcludeUID []uint32 `inbound:"exclude-uid,omitempty"` From 368b1e129611957a91ddf3df6bb2dd23e68cbe2e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 30 Dec 2024 22:33:13 +0800 Subject: [PATCH 306/311] chore: rollback tfo-go version --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4d21fbb0..76b9b754 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( github.com/metacubex/sing-tun v0.4.5 github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589 - github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa + github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785 github.com/metacubex/utls v1.6.6 github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 github.com/miekg/dns v1.1.62 diff --git a/go.sum b/go.sum index 1d874f38..35a80920 100644 --- a/go.sum +++ b/go.sum @@ -126,8 +126,8 @@ github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosq github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589 h1:Z6bNy0HLTjx6BKIkV48sV/yia/GP8Bnyb5JQuGgSGzg= github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589/go.mod h1:4NclTLIZuk+QkHVCGrP87rHi/y8YjgPytxTgApJNMhc= -github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa h1:9mcjV+RGZVC3reJBNDjjNPyS8PmFG97zq56X7WNaFO4= -github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa/go.mod h1:4tLB5c8U0CxpkFM+AJJB77jEaVDbLH5XQvy42vAGsWw= +github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785 h1:NNmI+ZV0DzNuqaAInRQuZFLHlWVuyHeow8jYpdKjHjo= +github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8= github.com/metacubex/utls v1.6.6/go.mod h1:+WLFUnXjcpdxXCnyX25nggw8C6YonZ8zOK2Zm/oRvdo= github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 h1:hJLQviGySBuaynlCwf/oYgIxbVbGRUIKZCxdya9YrbQ= From 1c5f4a3ab12ae748cf85b1f14e1841f3b570e9c3 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 31 Dec 2024 16:42:33 +0800 Subject: [PATCH 307/311] chore: update dependencies --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 76b9b754..39b39152 100644 --- a/go.mod +++ b/go.mod @@ -7,12 +7,12 @@ require ( github.com/bahlo/generic-list-go v0.2.0 github.com/coreos/go-iptables v0.8.0 github.com/dlclark/regexp2 v1.11.4 - github.com/enfein/mieru/v3 v3.8.4 - github.com/go-chi/chi/v5 v5.1.0 + github.com/enfein/mieru/v3 v3.9.0 + github.com/go-chi/chi/v5 v5.2.0 github.com/go-chi/render v1.0.3 github.com/gobwas/ws v1.4.0 github.com/gofrs/uuid/v5 v5.3.0 - github.com/insomniacslk/dhcp v0.0.0-20241203100832-a481575ed0ef + github.com/insomniacslk/dhcp v0.0.0-20241224095048-b56fa0d5f25d github.com/klauspost/compress v1.17.9 // lastest version compatible with golang1.20 github.com/klauspost/cpuid/v2 v2.2.9 github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 @@ -29,7 +29,7 @@ require ( github.com/metacubex/sing-tun v0.4.5 github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589 - github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785 + github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 github.com/metacubex/utls v1.6.6 github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 github.com/miekg/dns v1.1.62 @@ -54,7 +54,7 @@ require ( go4.org/netipx v0.0.0-20231129151722-fdeea329fbba golang.org/x/crypto v0.31.0 golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // lastest version compatible with golang1.20 - golang.org/x/net v0.32.0 + golang.org/x/net v0.33.0 golang.org/x/sys v0.28.0 google.golang.org/protobuf v1.34.2 // lastest version compatible with golang1.20 gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index 35a80920..2d7cbd53 100644 --- a/go.sum +++ b/go.sum @@ -29,8 +29,8 @@ github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yA github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE= github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= -github.com/enfein/mieru/v3 v3.8.4 h1:PmBQykuEcl8yKcQ647pg8Qbjl433CRYgUbW6VLBgGn4= -github.com/enfein/mieru/v3 v3.8.4/go.mod h1:YtU00qjAEt54mCBQu4WZPCey6cBdB1BUtXjvrHLEUNQ= +github.com/enfein/mieru/v3 v3.9.0 h1:h8KIXKxwPg8jT0uFdABIi0864O10UMAAOkkWo4zKl04= +github.com/enfein/mieru/v3 v3.9.0/go.mod h1:jH2nXzJSNUn6UWuzD8E8AsRVa9Ca0CqcTcr9Z+CJO1o= github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8= github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I= github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g= @@ -44,8 +44,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 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.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= -github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0= +github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= 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= @@ -75,8 +75,8 @@ github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I= github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/insomniacslk/dhcp v0.0.0-20241203100832-a481575ed0ef h1:NzQKDfd5ZOPnuZYf9MnRee8x2qecsVqzsnaLjEZiBko= -github.com/insomniacslk/dhcp v0.0.0-20241203100832-a481575ed0ef/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= +github.com/insomniacslk/dhcp v0.0.0-20241224095048-b56fa0d5f25d h1:VkCNWh6tuQLgDBc6KrUOz/L1mCUQGnR1Ujj8uTgpwwk= +github.com/insomniacslk/dhcp v0.0.0-20241224095048-b56fa0d5f25d/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 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= @@ -126,8 +126,8 @@ github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosq github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589 h1:Z6bNy0HLTjx6BKIkV48sV/yia/GP8Bnyb5JQuGgSGzg= github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589/go.mod h1:4NclTLIZuk+QkHVCGrP87rHi/y8YjgPytxTgApJNMhc= -github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785 h1:NNmI+ZV0DzNuqaAInRQuZFLHlWVuyHeow8jYpdKjHjo= -github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= +github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 h1:zGeQt3UyNydIVrMRB97AA5WsYEau/TyCnRtTf1yUmJY= +github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw= github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8= github.com/metacubex/utls v1.6.6/go.mod h1:+WLFUnXjcpdxXCnyX25nggw8C6YonZ8zOK2Zm/oRvdo= github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 h1:hJLQviGySBuaynlCwf/oYgIxbVbGRUIKZCxdya9YrbQ= @@ -242,8 +242,8 @@ golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= -golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= From 49d54cc293ae80f5278d1b9fba466225c6c635e1 Mon Sep 17 00:00:00 2001 From: "J.K.SAGE" Date: Tue, 7 Jan 2025 13:23:05 +0800 Subject: [PATCH 308/311] fix: remote conn statistic error (#1776) TCP handshake traffic should be counted as upload traffic for the remote connection --- tunnel/tunnel.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index b1b4add5..a4486df7 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -547,7 +547,7 @@ func handleTCPConn(connCtx C.ConnContext) { } logMetadata(metadata, rule, remoteConn) - remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule, 0, int64(peekLen), true) + remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule, int64(peekLen), 0, true) defer func(remoteConn C.Conn) { _ = remoteConn.Close() }(remoteConn) From f4806b49b452a5d41885c896482273ef62032a89 Mon Sep 17 00:00:00 2001 From: enfein <83481737+enfein@users.noreply.github.com> Date: Tue, 7 Jan 2025 05:25:32 +0000 Subject: [PATCH 309/311] chore: update mieru version (#1762) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 39b39152..1db2d3b5 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/bahlo/generic-list-go v0.2.0 github.com/coreos/go-iptables v0.8.0 github.com/dlclark/regexp2 v1.11.4 - github.com/enfein/mieru/v3 v3.9.0 + github.com/enfein/mieru/v3 v3.10.0 github.com/go-chi/chi/v5 v5.2.0 github.com/go-chi/render v1.0.3 github.com/gobwas/ws v1.4.0 diff --git a/go.sum b/go.sum index 2d7cbd53..3e105593 100644 --- a/go.sum +++ b/go.sum @@ -29,8 +29,8 @@ github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yA github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE= github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= -github.com/enfein/mieru/v3 v3.9.0 h1:h8KIXKxwPg8jT0uFdABIi0864O10UMAAOkkWo4zKl04= -github.com/enfein/mieru/v3 v3.9.0/go.mod h1:jH2nXzJSNUn6UWuzD8E8AsRVa9Ca0CqcTcr9Z+CJO1o= +github.com/enfein/mieru/v3 v3.10.0 h1:KMnAtY4s8MB74sUg4GbvF9R9v3jkXPQTSkxPxl1emxQ= +github.com/enfein/mieru/v3 v3.10.0/go.mod h1:jH2nXzJSNUn6UWuzD8E8AsRVa9Ca0CqcTcr9Z+CJO1o= github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8= github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I= github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g= From 56c128880c894fa4320757d4d1893702272c006d Mon Sep 17 00:00:00 2001 From: Mossia <63834850+MuXia-0326@users.noreply.github.com> Date: Tue, 7 Jan 2025 13:26:56 +0800 Subject: [PATCH 310/311] fix: empty proxy provider subscription info not omitted (#1759) --- adapter/provider/provider.go | 6 ++---- adapter/provider/subscription_info.go | 12 +++++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index 8558c434..64d83b08 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -137,7 +137,7 @@ func (pp *proxySetProvider) Initial() error { return err } if subscriptionInfo := cachefile.Cache().GetSubscriptionInfo(pp.Name()); subscriptionInfo != "" { - pp.subscriptionInfo.Update(subscriptionInfo) + pp.subscriptionInfo = NewSubscriptionInfo(subscriptionInfo) } pp.closeAllConnections() return nil @@ -165,14 +165,12 @@ func NewProxySetProvider(name string, interval time.Duration, parser resource.Pa go hc.process() } - si := new(SubscriptionInfo) pd := &proxySetProvider{ baseProvider: baseProvider{ name: name, proxies: []C.Proxy{}, healthCheck: hc, }, - subscriptionInfo: si, } fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, parser, proxiesOnUpdate(pd)) @@ -181,7 +179,7 @@ func NewProxySetProvider(name string, interval time.Duration, parser resource.Pa httpVehicle.SetInRead(func(resp *http.Response) { if subscriptionInfo := resp.Header.Get("subscription-userinfo"); subscriptionInfo != "" { cachefile.Cache().SetSubscriptionInfo(name, subscriptionInfo) - si.Update(subscriptionInfo) + pd.subscriptionInfo = NewSubscriptionInfo(subscriptionInfo) } }) } diff --git a/adapter/provider/subscription_info.go b/adapter/provider/subscription_info.go index 194e3984..2ec8537d 100644 --- a/adapter/provider/subscription_info.go +++ b/adapter/provider/subscription_info.go @@ -15,8 +15,9 @@ type SubscriptionInfo struct { Expire int64 } -func (info *SubscriptionInfo) Update(userinfo string) { +func NewSubscriptionInfo(userinfo string) (si *SubscriptionInfo) { userinfo = strings.ReplaceAll(strings.ToLower(userinfo), " ", "") + si = new(SubscriptionInfo) for _, field := range strings.Split(userinfo, ";") { name, value, ok := strings.Cut(field, "=") @@ -32,15 +33,16 @@ func (info *SubscriptionInfo) Update(userinfo string) { switch name { case "upload": - info.Upload = intValue + si.Upload = intValue case "download": - info.Download = intValue + si.Download = intValue case "total": - info.Total = intValue + si.Total = intValue case "expire": - info.Expire = intValue + si.Expire = intValue } } + return si } func parseValue(value string) (int64, error) { From c7661d7765fb62acbc6571e76f2922083b0b2b04 Mon Sep 17 00:00:00 2001 From: lucidhz <38235555+lucidhz@users.noreply.github.com> Date: Tue, 7 Jan 2025 13:25:54 +0800 Subject: [PATCH 311/311] fix: initialize error message with cipher (#1760) --- adapter/outbound/shadowsocks.go | 2 +- adapter/outbound/shadowsocksr.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index f73d5302..94a7763d 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -236,7 +236,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { Password: option.Password, }) if err != nil { - return nil, fmt.Errorf("ss %s initialize error: %w", addr, err) + return nil, fmt.Errorf("ss %s cipher: %s initialize error: %w", addr, option.Cipher, err) } var v2rayOption *v2rayObfs.Option diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index d0752e79..7012b30b 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -141,7 +141,7 @@ func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) { password := option.Password coreCiph, err := core.PickCipher(cipher, nil, password) if err != nil { - return nil, fmt.Errorf("ssr %s initialize error: %w", addr, err) + return nil, fmt.Errorf("ssr %s cipher: %s initialize error: %w", addr, cipher, err) } var ( ivSize int