From 50286678bf51e9331a2e88eaa522687daaa4242e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 22 Jun 2024 13:08:15 +0800 Subject: [PATCH 01/31] 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 3361adf932..8dea8e9a93 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 95e0404f8b..95c71fede5 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 02/31] 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 397a16b722..8c79cab537 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 03/31] 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 4667104cc2..45c89e5a5a 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 2166b87d70..5676f7aae4 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 04/31] 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 8dea8e9a93..4a96d14208 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 95c71fede5..29a5d385de 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 05/31] 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 7616290f74..6c03c6e5f3 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 06/31] 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 5676f7aae4..ae82ac3f7e 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 07/31] 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 67dc104dfb..efc38aabee 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 08/31] 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 28ffec6f15..fc228761a3 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 e4ec5917cf..50459cc120 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 09/31] 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 4a96d14208..a492f93dd8 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 29a5d385de..885531613a 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 44b2e8d42c..7cb12c45c3 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 3cf7574974..7981d5b15a 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 65737fccd1..eb61232e30 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 10/31] 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 a492f93dd8..ab4125a062 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 885531613a..836d983308 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 11/31] 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 67d2e8332b..c02771ed78 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 12/31] 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 54b8279657..504f79f2a5 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 13/31] 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 4cb0db004f..738ed15479 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 6f32ed18b1..35f605b10c 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 cb58e2f5a2..d1b6beebe5 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 df950d3a81..ad734c0fd6 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 97bf162969..4438638dad 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 14/31] 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 ae82ac3f7e..f58709c9c0 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 9c51bc10b3..4e4b9b16ef 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 38779e13b6..57c91aaef9 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 0000000000..5f993c021d --- /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 c28782b9c3..165c7c6970 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 15/31] 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 5f993c021d..bb5416c436 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 16/31] 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 f58709c9c0..d614166d71 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 504f79f2a5..97e01ea720 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 17/31] 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 76ec2c45e2..84020c4d38 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 fd5d3b6cba..0000000000 --- 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 fa7eeb9fc8..0000000000 --- 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 45c89e5a5a..3ce45ae816 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 packageName, loaded := packageManager.PackageByID(uid % 100000); loaded { - return packageName + if idx == -1 { + return filepath.Base(string(cmdline)) } - return "" + return filepath.Base(string(cmdline[:idx])) } func isPid(s string) bool { diff --git a/go.mod b/go.mod index ab4125a062..7085d8b173 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 836d983308..ea3b6b539e 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 ac41282d51..bd5c4bd071 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 1a6f104dc9..5dd468f3b4 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 18/31] 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 e62912937d..c82a54a3d1 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 7085d8b173..9c3a218064 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 19/31] 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 dfd1985f8c..dcfbe185f6 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 20/31] 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 ed560818d8..894910aac1 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 dcfbe185f6..0f084fca7b 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 c77f92307f..b2f312a578 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 8fc9da5962..9b2797da1a 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 367b7a3683..773cabe3f1 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 21/31] 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 9b2797da1a..77e10f0841 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 22/31] 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 d614166d71..ecd94c7dd9 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 23/31] 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 0000000000..e32d4e1a3e --- /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 f7dfc9cc60..c86e616336 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 4e4b9b16ef..2d3343cf15 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 9c3a218064..d5ac6beed6 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 ea3b6b539e..7e9cd5d8a9 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 61f1d683bd..cd903ce6d9 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 c0787d5839..0104fdf907 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 0000000000..3b93b4a4bf --- /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 a20da28d5c..227debb3a0 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 6c03c6e5f3..a4d8883df5 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 24/31] 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 0000000000..f6a0348856 --- /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 e32d4e1a3e..27d15802e0 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 c86e616336..bd6b6e9470 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 2d3343cf15..6e29f1642e 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 8353ebce40..205a8e5996 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 0104fdf907..462d37dcf5 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 d0545c7cc2..87cf7a2d8e 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 3b93b4a4bf..c8f63fdfe8 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 0000000000..66f62127c8 --- /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 a4d8883df5..8c5d7f9407 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 25/31] 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 6e29f1642e..669c8be7ef 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 462d37dcf5..a999f5bd1c 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 c8f63fdfe8..a08301982e 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 26/31] 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 521fabab13..4907146039 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 3decbb0255..db30402ede 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 860d1235d5..7778d13379 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 77106d5ffc..e343d11d1c 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 4c5d8002d8..916f61076d 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 669c8be7ef..d7c686d01f 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 a999f5bd1c..b893f038b2 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 87cf7a2d8e..9efffed96b 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 a08301982e..edc24e7eea 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 8c5d7f9407..b9524c35e6 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 27/31] 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 d5ac6beed6..e18f1e554d 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 7e9cd5d8a9..dbc4eafce3 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 28/31] chore: reduce image size --- Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 64b33cf748..67d4a6e56e 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 29/31] 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 e18f1e554d..4cfdb189e7 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 dbc4eafce3..dce392a823 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 30/31] 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 9b8e000a12..b936a57fea 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 31/31] 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 4cfdb189e7..57d6eea7ff 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 dce392a823..50dac8d203 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=