From 150c6ccd2514459cb160c4722aeb760fbbc23a61 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 23 Sep 2024 08:54:07 +0800 Subject: [PATCH 01/44] chore: skip duplicates nameserver when parse --- config/config.go | 39 +++++++++++++++++++-------------------- dns/util.go | 4 ---- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/config/config.go b/config/config.go index 75616edc..2a75ddd6 100644 --- a/config/config.go +++ b/config/config.go @@ -1162,16 +1162,6 @@ func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns. var nameservers []dns.NameServer for idx, server := range servers { - if strings.HasPrefix(server, "dhcp://") { - nameservers = append( - nameservers, - dns.NameServer{ - Net: "dhcp", - Addr: server[len("dhcp://"):], - }, - ) - continue - } server = parsePureDNSServer(server) u, err := url.Parse(server) if err != nil { @@ -1222,6 +1212,13 @@ func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns. dnsNetType = "quic" // DNS over QUIC case "system": dnsNetType = "system" // System DNS + case "dhcp": + addr = server[len("dhcp://"):] // some special notation cannot be parsed by url + dnsNetType = "dhcp" // UDP from DHCP + if addr == "system" { // Compatible with old writing "dhcp://system" + dnsNetType = "system" + addr = "" + } case "rcode": dnsNetType = "rcode" addr = u.Host @@ -1247,16 +1244,18 @@ func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns. proxyName = dns.RespectRules } - nameservers = append( - nameservers, - dns.NameServer{ - Net: dnsNetType, - Addr: addr, - ProxyName: proxyName, - Params: params, - PreferH3: preferH3, - }, - ) + nameserver := dns.NameServer{ + Net: dnsNetType, + Addr: addr, + ProxyName: proxyName, + Params: params, + PreferH3: preferH3, + } + if slices.ContainsFunc(nameservers, nameserver.Equal) { + continue // skip duplicates nameserver + } + + nameservers = append(nameservers, nameserver) } return nameservers, nil } diff --git a/dns/util.go b/dns/util.go index 92a86dbc..50459cc1 100644 --- a/dns/util.go +++ b/dns/util.go @@ -99,10 +99,6 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient { ret = append(ret, newDoHClient(s.Addr, resolver, s.PreferH3, s.Params, s.ProxyAdapter, s.ProxyName)) continue case "dhcp": - if s.Addr == "system" { // Compatible with old writing - ret = append(ret, newSystemClient()) - continue - } ret = append(ret, newDHCPClient(s.Addr)) continue case "system": From 966eeae41b557e906a0b55e9740223a788706e1a Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 23 Sep 2024 09:35:48 +0800 Subject: [PATCH 02/44] chore: rewrite bbolt cachefile implements never use returned byte slices outside the transaction, ref: https://pkg.go.dev/go.etcd.io/bbolt#hdr-Caveats --- common/utils/hash.go | 45 ++++++++++ component/fakeip/cachefile.go | 30 +++---- component/fakeip/pool.go | 4 +- component/fakeip/pool_test.go | 4 +- component/profile/cachefile/cache.go | 104 ++++------------------- component/profile/cachefile/fakeip.go | 115 ++++++++++++++++++++++++++ component/resource/fetcher.go | 9 +- component/resource/vehicle.go | 13 +-- component/updater/update_geo.go | 18 ++-- constant/path.go | 7 +- constant/provider/hash.go | 29 ------- constant/provider/interface.go | 2 +- 12 files changed, 212 insertions(+), 168 deletions(-) create mode 100644 common/utils/hash.go create mode 100644 component/profile/cachefile/fakeip.go delete mode 100644 constant/provider/hash.go diff --git a/common/utils/hash.go b/common/utils/hash.go new file mode 100644 index 00000000..38ba15b4 --- /dev/null +++ b/common/utils/hash.go @@ -0,0 +1,45 @@ +package utils + +import ( + "crypto/md5" + "encoding/hex" +) + +// HashType warps hash array inside struct +// someday can change to other hash algorithm simply +type HashType struct { + md5 [md5.Size]byte // MD5 +} + +func MakeHash(data []byte) HashType { + return HashType{md5.Sum(data)} +} + +func MakeHashFromBytes(hashBytes []byte) (h HashType) { + if len(hashBytes) != md5.Size { + return + } + copy(h.md5[:], hashBytes) + return +} + +func (h HashType) Equal(hash HashType) bool { + return h.md5 == hash.md5 +} + +func (h HashType) Bytes() []byte { + return h.md5[:] +} + +func (h HashType) String() string { + return hex.EncodeToString(h.Bytes()) +} + +func (h HashType) Len() int { + return len(h.md5) +} + +func (h HashType) IsValid() bool { + var zero HashType + return h != zero +} diff --git a/component/fakeip/cachefile.go b/component/fakeip/cachefile.go index 6f0cc48b..92d09721 100644 --- a/component/fakeip/cachefile.go +++ b/component/fakeip/cachefile.go @@ -7,46 +7,32 @@ import ( ) type cachefileStore struct { - cache *cachefile.CacheFile + cache *cachefile.FakeIpStore } // GetByHost implements store.GetByHost func (c *cachefileStore) GetByHost(host string) (netip.Addr, bool) { - elm := c.cache.GetFakeip([]byte(host)) - if elm == nil { - return netip.Addr{}, false - } - - if len(elm) == 4 { - return netip.AddrFrom4(*(*[4]byte)(elm)), true - } else { - return netip.AddrFrom16(*(*[16]byte)(elm)), true - } + return c.cache.GetByHost(host) } // PutByHost implements store.PutByHost func (c *cachefileStore) PutByHost(host string, ip netip.Addr) { - c.cache.PutFakeip([]byte(host), ip.AsSlice()) + c.cache.PutByHost(host, ip) } // GetByIP implements store.GetByIP func (c *cachefileStore) GetByIP(ip netip.Addr) (string, bool) { - elm := c.cache.GetFakeip(ip.AsSlice()) - if elm == nil { - return "", false - } - return string(elm), true + return c.cache.GetByIP(ip) } // PutByIP implements store.PutByIP func (c *cachefileStore) PutByIP(ip netip.Addr, host string) { - c.cache.PutFakeip(ip.AsSlice(), []byte(host)) + c.cache.PutByIP(ip, host) } // DelByIP implements store.DelByIP func (c *cachefileStore) DelByIP(ip netip.Addr) { - addr := ip.AsSlice() - c.cache.DelFakeipPair(addr, c.cache.GetFakeip(addr)) + c.cache.DelByIP(ip) } // Exist implements store.Exist @@ -63,3 +49,7 @@ func (c *cachefileStore) CloneTo(store store) {} func (c *cachefileStore) FlushFakeIP() error { return c.cache.FlushFakeIP() } + +func newCachefileStore(cache *cachefile.CacheFile) *cachefileStore { + return &cachefileStore{cache.FakeIpStore()} +} diff --git a/component/fakeip/pool.go b/component/fakeip/pool.go index 12c06332..41b848b3 100644 --- a/component/fakeip/pool.go +++ b/component/fakeip/pool.go @@ -201,9 +201,7 @@ func New(options Options) (*Pool, error) { ipnet: options.IPNet, } if options.Persistence { - pool.store = &cachefileStore{ - cache: cachefile.Cache(), - } + pool.store = newCachefileStore(cachefile.Cache()) } else { pool.store = newMemoryStore(options.Size) } diff --git a/component/fakeip/pool_test.go b/component/fakeip/pool_test.go index ee607b68..be78b87c 100644 --- a/component/fakeip/pool_test.go +++ b/component/fakeip/pool_test.go @@ -43,9 +43,7 @@ func createCachefileStore(options Options) (*Pool, string, error) { return nil, "", err } - pool.store = &cachefileStore{ - cache: &cachefile.CacheFile{DB: db}, - } + pool.store = newCachefileStore(&cachefile.CacheFile{DB: db}) return pool, f.Name(), nil } diff --git a/component/profile/cachefile/cache.go b/component/profile/cachefile/cache.go index 0591c92b..6a918041 100644 --- a/component/profile/cachefile/cache.go +++ b/component/profile/cachefile/cache.go @@ -6,6 +6,7 @@ import ( "sync" "time" + "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/profile" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" @@ -71,93 +72,19 @@ func (c *CacheFile) SelectedMap() map[string]string { return mapping } -func (c *CacheFile) PutFakeip(key, value []byte) error { - if c.DB == nil { - return nil - } - - err := c.DB.Batch(func(t *bbolt.Tx) error { - bucket, err := t.CreateBucketIfNotExists(bucketFakeip) - if err != nil { - return err - } - return bucket.Put(key, value) - }) - if err != nil { - log.Warnln("[CacheFile] write cache to %s failed: %s", c.DB.Path(), err.Error()) - } - - return err -} - -func (c *CacheFile) DelFakeipPair(ip, host []byte) error { - if c.DB == nil { - return nil - } - - err := c.DB.Batch(func(t *bbolt.Tx) error { - bucket, err := t.CreateBucketIfNotExists(bucketFakeip) - if err != nil { - return err - } - err = bucket.Delete(ip) - if len(host) > 0 { - if err := bucket.Delete(host); err != nil { - return err - } - } - return err - }) - if err != nil { - log.Warnln("[CacheFile] write cache to %s failed: %s", c.DB.Path(), err.Error()) - } - - return err -} - -func (c *CacheFile) GetFakeip(key []byte) []byte { - if c.DB == nil { - return nil - } - - tx, err := c.DB.Begin(false) - if err != nil { - return nil - } - defer tx.Rollback() - - bucket := tx.Bucket(bucketFakeip) - if bucket == nil { - return nil - } - - return bucket.Get(key) -} - -func (c *CacheFile) FlushFakeIP() error { - err := c.DB.Batch(func(t *bbolt.Tx) error { - bucket := t.Bucket(bucketFakeip) - if bucket == nil { - return nil - } - return t.DeleteBucket(bucketFakeip) - }) - return err -} - -func (c *CacheFile) SetETagWithHash(url string, hash []byte, etag string) { +func (c *CacheFile) SetETagWithHash(url string, hash utils.HashType, etag string) { if c.DB == nil { return } - lenHash := len(hash) + lenHash := hash.Len() if lenHash > math.MaxUint8 { return // maybe panic is better } data := make([]byte, 1, 1+lenHash+len(etag)) data[0] = uint8(lenHash) - data = append(data, hash...) + data = append(data, hash.Bytes()...) data = append(data, etag...) err := c.DB.Batch(func(t *bbolt.Tx) error { @@ -173,28 +100,27 @@ func (c *CacheFile) SetETagWithHash(url string, hash []byte, etag string) { return } } -func (c *CacheFile) GetETagWithHash(key string) (hash []byte, etag string) { +func (c *CacheFile) GetETagWithHash(key string) (hash utils.HashType, etag string) { if c.DB == nil { return } - var value []byte c.DB.View(func(t *bbolt.Tx) error { if bucket := t.Bucket(bucketETag); bucket != nil { if v := bucket.Get([]byte(key)); v != nil { - value = v + if len(v) == 0 { + return nil + } + lenHash := int(v[0]) + if len(v) < 1+lenHash { + return nil + } + hash = utils.MakeHashFromBytes(v[1 : 1+lenHash]) + etag = string(v[1+lenHash:]) } } return nil }) - if len(value) == 0 { - return - } - lenHash := int(value[0]) - if len(value) < 1+lenHash { - return - } - hash = value[1 : 1+lenHash] - etag = string(value[1+lenHash:]) + return } diff --git a/component/profile/cachefile/fakeip.go b/component/profile/cachefile/fakeip.go new file mode 100644 index 00000000..20a09f9c --- /dev/null +++ b/component/profile/cachefile/fakeip.go @@ -0,0 +1,115 @@ +package cachefile + +import ( + "net/netip" + + "github.com/metacubex/mihomo/log" + + "github.com/metacubex/bbolt" +) + +type FakeIpStore struct { + *CacheFile +} + +func (c *CacheFile) FakeIpStore() *FakeIpStore { + return &FakeIpStore{c} +} + +func (c *FakeIpStore) GetByHost(host string) (ip netip.Addr, exist bool) { + if c.DB == nil { + return + } + c.DB.View(func(t *bbolt.Tx) error { + if bucket := t.Bucket(bucketFakeip); bucket != nil { + if v := bucket.Get([]byte(host)); v != nil { + ip, exist = netip.AddrFromSlice(v) + } + } + return nil + }) + return +} + +func (c *FakeIpStore) PutByHost(host string, ip netip.Addr) { + if c.DB == nil { + return + } + err := c.DB.Batch(func(t *bbolt.Tx) error { + bucket, err := t.CreateBucketIfNotExists(bucketFakeip) + if err != nil { + return err + } + return bucket.Put([]byte(host), ip.AsSlice()) + }) + if err != nil { + log.Warnln("[CacheFile] write cache to %s failed: %s", c.DB.Path(), err.Error()) + } +} + +func (c *FakeIpStore) GetByIP(ip netip.Addr) (host string, exist bool) { + if c.DB == nil { + return + } + c.DB.View(func(t *bbolt.Tx) error { + if bucket := t.Bucket(bucketFakeip); bucket != nil { + if v := bucket.Get(ip.AsSlice()); v != nil { + host, exist = string(v), true + } + } + return nil + }) + return +} + +func (c *FakeIpStore) PutByIP(ip netip.Addr, host string) { + if c.DB == nil { + return + } + err := c.DB.Batch(func(t *bbolt.Tx) error { + bucket, err := t.CreateBucketIfNotExists(bucketFakeip) + if err != nil { + return err + } + return bucket.Put(ip.AsSlice(), []byte(host)) + }) + if err != nil { + log.Warnln("[CacheFile] write cache to %s failed: %s", c.DB.Path(), err.Error()) + } +} + +func (c *FakeIpStore) DelByIP(ip netip.Addr) { + if c.DB == nil { + return + } + + addr := ip.AsSlice() + err := c.DB.Batch(func(t *bbolt.Tx) error { + bucket, err := t.CreateBucketIfNotExists(bucketFakeip) + if err != nil { + return err + } + host := bucket.Get(addr) + err = bucket.Delete(addr) + if len(host) > 0 { + if err = bucket.Delete(host); err != nil { + return err + } + } + return err + }) + if err != nil { + log.Warnln("[CacheFile] write cache to %s failed: %s", c.DB.Path(), err.Error()) + } +} + +func (c *FakeIpStore) FlushFakeIP() error { + err := c.DB.Batch(func(t *bbolt.Tx) error { + bucket := t.Bucket(bucketFakeip) + if bucket == nil { + return nil + } + return t.DeleteBucket(bucketFakeip) + }) + return err +} diff --git a/component/resource/fetcher.go b/component/resource/fetcher.go index 3e2ec239..39beee85 100644 --- a/component/resource/fetcher.go +++ b/component/resource/fetcher.go @@ -5,6 +5,7 @@ import ( "os" "time" + "github.com/metacubex/mihomo/common/utils" types "github.com/metacubex/mihomo/constant/provider" "github.com/metacubex/mihomo/log" @@ -21,7 +22,7 @@ type Fetcher[V any] struct { name string vehicle types.Vehicle updatedAt time.Time - hash types.HashType + hash utils.HashType parser Parser[V] interval time.Duration onUpdate func(V) @@ -55,7 +56,7 @@ func (f *Fetcher[V]) Initial() (V, error) { // local file exists, use it first buf, err = os.ReadFile(f.vehicle.Path()) modTime := stat.ModTime() - contents, _, err = f.loadBuf(buf, types.MakeHash(buf), false) + contents, _, err = f.loadBuf(buf, utils.MakeHash(buf), false) f.updatedAt = modTime // reset updatedAt to file's modTime if err == nil { @@ -89,10 +90,10 @@ func (f *Fetcher[V]) Update() (V, bool, error) { } func (f *Fetcher[V]) SideUpdate(buf []byte) (V, bool, error) { - return f.loadBuf(buf, types.MakeHash(buf), true) + return f.loadBuf(buf, utils.MakeHash(buf), true) } -func (f *Fetcher[V]) loadBuf(buf []byte, hash types.HashType, updateFile bool) (V, bool, error) { +func (f *Fetcher[V]) loadBuf(buf []byte, hash utils.HashType, updateFile bool) (V, bool, error) { now := time.Now() if f.hash.Equal(hash) { if updateFile { diff --git a/component/resource/vehicle.go b/component/resource/vehicle.go index f30e22d0..b24adfa9 100644 --- a/component/resource/vehicle.go +++ b/component/resource/vehicle.go @@ -9,6 +9,7 @@ import ( "path/filepath" "time" + "github.com/metacubex/mihomo/common/utils" mihomoHttp "github.com/metacubex/mihomo/component/http" "github.com/metacubex/mihomo/component/profile/cachefile" types "github.com/metacubex/mihomo/constant/provider" @@ -61,12 +62,12 @@ func (f *FileVehicle) Url() string { return "file://" + f.path } -func (f *FileVehicle) Read(ctx context.Context, oldHash types.HashType) (buf []byte, hash types.HashType, err error) { +func (f *FileVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []byte, hash utils.HashType, err error) { buf, err = os.ReadFile(f.path) if err != nil { return } - hash = types.MakeHash(buf) + hash = utils.MakeHash(buf) return } @@ -110,14 +111,14 @@ func (h *HTTPVehicle) Write(buf []byte) error { return safeWrite(h.path, buf) } -func (h *HTTPVehicle) Read(ctx context.Context, oldHash types.HashType) (buf []byte, hash types.HashType, err error) { +func (h *HTTPVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []byte, hash utils.HashType, err error) { ctx, cancel := context.WithTimeout(ctx, h.timeout) defer cancel() header := h.header setIfNoneMatch := false if etag && oldHash.IsValid() { hashBytes, etag := cachefile.Cache().GetETagWithHash(h.url) - if oldHash.EqualBytes(hashBytes) && etag != "" { + if oldHash.Equal(hashBytes) && etag != "" { if header == nil { header = http.Header{} } else { @@ -143,9 +144,9 @@ func (h *HTTPVehicle) Read(ctx context.Context, oldHash types.HashType) (buf []b if err != nil { return } - hash = types.MakeHash(buf) + hash = utils.MakeHash(buf) if etag { - cachefile.Cache().SetETagWithHash(h.url, hash.Bytes(), resp.Header.Get("ETag")) + cachefile.Cache().SetETagWithHash(h.url, hash, resp.Header.Get("ETag")) } return } diff --git a/component/updater/update_geo.go b/component/updater/update_geo.go index 454cd84d..b5dc9677 100644 --- a/component/updater/update_geo.go +++ b/component/updater/update_geo.go @@ -10,12 +10,12 @@ import ( "github.com/metacubex/mihomo/common/atomic" "github.com/metacubex/mihomo/common/batch" + "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/geodata" _ "github.com/metacubex/mihomo/component/geodata/standard" "github.com/metacubex/mihomo/component/mmdb" "github.com/metacubex/mihomo/component/resource" C "github.com/metacubex/mihomo/constant" - P "github.com/metacubex/mihomo/constant/provider" "github.com/metacubex/mihomo/log" "github.com/oschwald/maxminddb-golang" @@ -46,9 +46,9 @@ func SetGeoUpdateInterval(newGeoUpdateInterval int) { func UpdateMMDB() (err error) { vehicle := resource.NewHTTPVehicle(geodata.MmdbUrl(), C.Path.MMDB(), "", nil, defaultHttpTimeout) - var oldHash P.HashType + var oldHash utils.HashType if buf, err := os.ReadFile(vehicle.Path()); err == nil { - oldHash = P.MakeHash(buf) + oldHash = utils.MakeHash(buf) } data, hash, err := vehicle.Read(context.Background(), oldHash) if err != nil { @@ -77,9 +77,9 @@ func UpdateMMDB() (err error) { func UpdateASN() (err error) { vehicle := resource.NewHTTPVehicle(geodata.ASNUrl(), C.Path.ASN(), "", nil, defaultHttpTimeout) - var oldHash P.HashType + var oldHash utils.HashType if buf, err := os.ReadFile(vehicle.Path()); err == nil { - oldHash = P.MakeHash(buf) + oldHash = utils.MakeHash(buf) } data, hash, err := vehicle.Read(context.Background(), oldHash) if err != nil { @@ -110,9 +110,9 @@ func UpdateGeoIp() (err error) { geoLoader, err := geodata.GetGeoDataLoader("standard") vehicle := resource.NewHTTPVehicle(geodata.GeoIpUrl(), C.Path.GeoIP(), "", nil, defaultHttpTimeout) - var oldHash P.HashType + var oldHash utils.HashType if buf, err := os.ReadFile(vehicle.Path()); err == nil { - oldHash = P.MakeHash(buf) + oldHash = utils.MakeHash(buf) } data, hash, err := vehicle.Read(context.Background(), oldHash) if err != nil { @@ -140,9 +140,9 @@ func UpdateGeoSite() (err error) { geoLoader, err := geodata.GetGeoDataLoader("standard") vehicle := resource.NewHTTPVehicle(geodata.GeoSiteUrl(), C.Path.GeoSite(), "", nil, defaultHttpTimeout) - var oldHash P.HashType + var oldHash utils.HashType if buf, err := os.ReadFile(vehicle.Path()); err == nil { - oldHash = P.MakeHash(buf) + oldHash = utils.MakeHash(buf) } data, hash, err := vehicle.Read(context.Background(), oldHash) if err != nil { diff --git a/constant/path.go b/constant/path.go index 02279371..1594441c 100644 --- a/constant/path.go +++ b/constant/path.go @@ -1,14 +1,13 @@ package constant import ( - "crypto/md5" - "encoding/hex" "os" P "path" "path/filepath" "strconv" "strings" + "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/constant/features" ) @@ -89,8 +88,8 @@ func (p *path) IsSafePath(path string) bool { } func (p *path) GetPathByHash(prefix, name string) string { - hash := md5.Sum([]byte(name)) - filename := hex.EncodeToString(hash[:]) + hash := utils.MakeHash([]byte(name)) + filename := hash.String() return filepath.Join(p.HomeDir(), prefix, filename) } diff --git a/constant/provider/hash.go b/constant/provider/hash.go deleted file mode 100644 index b95ffe23..00000000 --- a/constant/provider/hash.go +++ /dev/null @@ -1,29 +0,0 @@ -package provider - -import ( - "bytes" - "crypto/md5" -) - -type HashType [md5.Size]byte // MD5 - -func MakeHash(data []byte) HashType { - return md5.Sum(data) -} - -func (h HashType) Equal(hash HashType) bool { - return h == hash -} - -func (h HashType) EqualBytes(hashBytes []byte) bool { - return bytes.Equal(hashBytes, h[:]) -} - -func (h HashType) Bytes() []byte { - return h[:] -} - -func (h HashType) IsValid() bool { - var zero HashType - return h != zero -} diff --git a/constant/provider/interface.go b/constant/provider/interface.go index 511e8f18..065b801a 100644 --- a/constant/provider/interface.go +++ b/constant/provider/interface.go @@ -32,7 +32,7 @@ func (v VehicleType) String() string { } type Vehicle interface { - Read(ctx context.Context, oldHash HashType) (buf []byte, hash HashType, err error) + Read(ctx context.Context, oldHash utils.HashType) (buf []byte, hash utils.HashType, err error) Write(buf []byte) error Path() string Url() string From 59a2b24593fce55244cf3ad3817855b0b1b7f6b3 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 23 Sep 2024 19:25:35 +0800 Subject: [PATCH 03/44] chore: save etag in bbolt by msgpack --- common/utils/hash.go | 33 ++++++++++++---- component/profile/cachefile/cache.go | 54 -------------------------- component/profile/cachefile/etag.go | 58 ++++++++++++++++++++++++++++ component/resource/vehicle.go | 12 ++++-- go.mod | 2 + go.sum | 4 ++ 6 files changed, 97 insertions(+), 66 deletions(-) create mode 100644 component/profile/cachefile/etag.go diff --git a/common/utils/hash.go b/common/utils/hash.go index 38ba15b4..6957e2c3 100644 --- a/common/utils/hash.go +++ b/common/utils/hash.go @@ -3,6 +3,7 @@ package utils import ( "crypto/md5" "encoding/hex" + "errors" ) // HashType warps hash array inside struct @@ -15,14 +16,6 @@ func MakeHash(data []byte) HashType { return HashType{md5.Sum(data)} } -func MakeHashFromBytes(hashBytes []byte) (h HashType) { - if len(hashBytes) != md5.Size { - return - } - copy(h.md5[:], hashBytes) - return -} - func (h HashType) Equal(hash HashType) bool { return h.md5 == hash.md5 } @@ -35,6 +28,30 @@ func (h HashType) String() string { return hex.EncodeToString(h.Bytes()) } +func (h HashType) MarshalText() ([]byte, error) { + return []byte(h.String()), nil +} + +func (h *HashType) UnmarshalText(data []byte) error { + if hex.DecodedLen(len(data)) != md5.Size { + return errors.New("invalid hash length") + } + _, err := hex.Decode(h.md5[:], data) + return err +} + +func (h HashType) MarshalBinary() ([]byte, error) { + return h.md5[:], nil +} + +func (h *HashType) UnmarshalBinary(data []byte) error { + if len(data) != md5.Size { + return errors.New("invalid hash length") + } + copy(h.md5[:], data) + return nil +} + func (h HashType) Len() int { return len(h.md5) } diff --git a/component/profile/cachefile/cache.go b/component/profile/cachefile/cache.go index 6a918041..f5101f5b 100644 --- a/component/profile/cachefile/cache.go +++ b/component/profile/cachefile/cache.go @@ -1,12 +1,10 @@ package cachefile import ( - "math" "os" "sync" "time" - "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/profile" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" @@ -72,58 +70,6 @@ func (c *CacheFile) SelectedMap() map[string]string { return mapping } -func (c *CacheFile) SetETagWithHash(url string, hash utils.HashType, etag string) { - if c.DB == nil { - return - } - - lenHash := hash.Len() - if lenHash > math.MaxUint8 { - return // maybe panic is better - } - - data := make([]byte, 1, 1+lenHash+len(etag)) - data[0] = uint8(lenHash) - data = append(data, hash.Bytes()...) - data = append(data, etag...) - - err := c.DB.Batch(func(t *bbolt.Tx) error { - bucket, err := t.CreateBucketIfNotExists(bucketETag) - if err != nil { - return err - } - - return bucket.Put([]byte(url), data) - }) - if err != nil { - log.Warnln("[CacheFile] write cache to %s failed: %s", c.DB.Path(), err.Error()) - return - } -} -func (c *CacheFile) GetETagWithHash(key string) (hash utils.HashType, etag string) { - if c.DB == nil { - return - } - c.DB.View(func(t *bbolt.Tx) error { - if bucket := t.Bucket(bucketETag); bucket != nil { - if v := bucket.Get([]byte(key)); v != nil { - if len(v) == 0 { - return nil - } - lenHash := int(v[0]) - if len(v) < 1+lenHash { - return nil - } - hash = utils.MakeHashFromBytes(v[1 : 1+lenHash]) - etag = string(v[1+lenHash:]) - } - } - return nil - }) - - return -} - func (c *CacheFile) Close() error { return c.DB.Close() } diff --git a/component/profile/cachefile/etag.go b/component/profile/cachefile/etag.go new file mode 100644 index 00000000..028fe504 --- /dev/null +++ b/component/profile/cachefile/etag.go @@ -0,0 +1,58 @@ +package cachefile + +import ( + "time" + + "github.com/metacubex/mihomo/common/utils" + "github.com/metacubex/mihomo/log" + + "github.com/metacubex/bbolt" + "github.com/vmihailenco/msgpack/v5" +) + +type EtagWithHash struct { + Hash utils.HashType + ETag string + Time time.Time +} + +func (c *CacheFile) SetETagWithHash(url string, etagWithHash EtagWithHash) { + if c.DB == nil { + return + } + + data, err := msgpack.Marshal(etagWithHash) + if err != nil { + return // maybe panic is better + } + + err = c.DB.Batch(func(t *bbolt.Tx) error { + bucket, err := t.CreateBucketIfNotExists(bucketETag) + if err != nil { + return err + } + + return bucket.Put([]byte(url), data) + }) + if err != nil { + log.Warnln("[CacheFile] write cache to %s failed: %s", c.DB.Path(), err.Error()) + return + } +} +func (c *CacheFile) GetETagWithHash(key string) (etagWithHash EtagWithHash) { + if c.DB == nil { + return + } + c.DB.View(func(t *bbolt.Tx) error { + if bucket := t.Bucket(bucketETag); bucket != nil { + if v := bucket.Get([]byte(key)); v != nil { + if err := msgpack.Unmarshal(v, &etagWithHash); err != nil { + return err + } + } + } + return nil + }) + + return +} diff --git a/component/resource/vehicle.go b/component/resource/vehicle.go index b24adfa9..a9382329 100644 --- a/component/resource/vehicle.go +++ b/component/resource/vehicle.go @@ -117,14 +117,14 @@ func (h *HTTPVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []b header := h.header setIfNoneMatch := false if etag && oldHash.IsValid() { - hashBytes, etag := cachefile.Cache().GetETagWithHash(h.url) - if oldHash.Equal(hashBytes) && etag != "" { + etagWithHash := cachefile.Cache().GetETagWithHash(h.url) + if oldHash.Equal(etagWithHash.Hash) && etagWithHash.ETag != "" { if header == nil { header = http.Header{} } else { header = header.Clone() } - header.Set("If-None-Match", etag) + header.Set("If-None-Match", etagWithHash.ETag) setIfNoneMatch = true } } @@ -146,7 +146,11 @@ func (h *HTTPVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []b } hash = utils.MakeHash(buf) if etag { - cachefile.Cache().SetETagWithHash(h.url, hash, resp.Header.Get("ETag")) + cachefile.Cache().SetETagWithHash(h.url, cachefile.EtagWithHash{ + Hash: hash, + ETag: resp.Header.Get("ETag"), + Time: time.Now(), + }) } return } diff --git a/go.mod b/go.mod index e3eeb456..c1a8fab3 100644 --- a/go.mod +++ b/go.mod @@ -46,6 +46,7 @@ require ( github.com/shirou/gopsutil/v3 v3.24.5 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.9.0 + github.com/vmihailenco/msgpack/v5 v5.4.1 github.com/wk8/go-ordered-map/v2 v2.1.8 gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 go.uber.org/automaxprocs v1.5.3 @@ -104,6 +105,7 @@ require ( github.com/tklauser/numcpus v0.6.1 // indirect github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect github.com/vishvananda/netns v0.0.4 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect go.uber.org/mock v0.4.0 // indirect diff --git a/go.sum b/go.sum index e1f5490d..001e76af 100644 --- a/go.sum +++ b/go.sum @@ -210,6 +210,10 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17 github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= From 6c0383026eeeab77853d048fc1434b3529e468a8 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 24 Sep 2024 13:18:58 +0800 Subject: [PATCH 04/44] fix: AmneziaWG not working --- adapter/outbound/wireguard.go | 1 + go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 43046384..6f5a18f3 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -272,6 +272,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { }, } if option.AmneziaWGOption != nil { + outbound.bind.SetParseReserved(false) // AmneziaWG don't need parse reserved outbound.device = amnezia.NewDevice(outbound.tunDevice, outbound.bind, logger, option.Workers) } else { outbound.device = device.NewDevice(outbound.tunDevice, outbound.bind, logger, option.Workers) diff --git a/go.mod b/go.mod index c1a8fab3..22af95b3 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/metacubex/sing-shadowsocks2 v0.2.2 github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 - github.com/metacubex/sing-wireguard v0.0.0-20240922131718-0f10c39a5531 + github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785 github.com/metacubex/utls v1.6.6 github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 diff --git a/go.sum b/go.sum index 001e76af..9b824fc5 100644 --- a/go.sum +++ b/go.sum @@ -122,8 +122,8 @@ github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 h1:ypfofGDZbP github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1/go.mod h1:olbEx9yVcaw5tHTNlRamRoxmMKcvDvcVS1YLnQGzvWE= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosqY8xKDp3pqTW3qbrCprZ1l6WkrXSFSCwyY4I= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= -github.com/metacubex/sing-wireguard v0.0.0-20240922131718-0f10c39a5531 h1:BoIL2fZZTPzvSxuhng9kWwvUZ8fiMJyrWbgdHIX0CDs= -github.com/metacubex/sing-wireguard v0.0.0-20240922131718-0f10c39a5531/go.mod h1:6nitcmzPDL3MXnLdhu6Hm126Zk4S1fBbX3P7jxUxSFw= +github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 h1:xg71VmzLS6ByAzi/57phwDvjE+dLLs+ozH00k4DnOns= +github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3/go.mod h1:6nitcmzPDL3MXnLdhu6Hm126Zk4S1fBbX3P7jxUxSFw= github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785 h1:NNmI+ZV0DzNuqaAInRQuZFLHlWVuyHeow8jYpdKjHjo= github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8= From a4e84f047918fc692aa9e83439964367c228feae Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 24 Sep 2024 21:42:28 +0800 Subject: [PATCH 05/44] chore: better apply tcp keepalive to listeners --- adapter/inbound/listen.go | 22 +++++++ adapter/outbound/direct.go | 2 - adapter/outbound/http.go | 3 - adapter/outbound/shadowsocks.go | 1 - adapter/outbound/shadowsocksr.go | 1 - adapter/outbound/snell.go | 6 +- adapter/outbound/socks5.go | 3 - adapter/outbound/ssh.go | 1 - adapter/outbound/trojan.go | 4 -- adapter/outbound/vless.go | 3 - adapter/outbound/vmess.go | 3 - common/net/tcp_keepalive.go | 23 ------- common/net/tcp_keepalive_go122.go | 10 --- common/net/tcp_keepalive_go123.go | 19 ------ component/dialer/dialer.go | 2 + component/keepalive/tcp_keepalive.go | 65 +++++++++++++++++++ component/keepalive/tcp_keepalive_go122.go | 30 +++++++++ component/keepalive/tcp_keepalive_go123.go | 45 +++++++++++++ .../keepalive}/tcp_keepalive_go123_unix.go | 2 +- .../keepalive}/tcp_keepalive_go123_windows.go | 2 +- config/config.go | 8 +-- listener/http/server.go | 4 +- listener/mixed/mixed.go | 3 +- listener/redir/tcp.go | 6 +- listener/shadowsocks/tcp.go | 1 - listener/sing_shadowsocks/server.go | 2 - listener/sing_vmess/server.go | 1 - listener/socks/tcp.go | 2 +- listener/tproxy/tproxy.go | 4 +- listener/tunnel/tcp.go | 2 - 30 files changed, 180 insertions(+), 100 deletions(-) delete mode 100644 common/net/tcp_keepalive.go delete mode 100644 common/net/tcp_keepalive_go122.go delete mode 100644 common/net/tcp_keepalive_go123.go create mode 100644 component/keepalive/tcp_keepalive.go create mode 100644 component/keepalive/tcp_keepalive_go122.go create mode 100644 component/keepalive/tcp_keepalive_go123.go rename {common/net => component/keepalive}/tcp_keepalive_go123_unix.go (91%) rename {common/net => component/keepalive}/tcp_keepalive_go123_windows.go (99%) diff --git a/adapter/inbound/listen.go b/adapter/inbound/listen.go index 1b86c811..318c9675 100644 --- a/adapter/inbound/listen.go +++ b/adapter/inbound/listen.go @@ -3,6 +3,9 @@ package inbound import ( "context" "net" + "sync" + + "github.com/metacubex/mihomo/component/keepalive" "github.com/metacubex/tfo-go" ) @@ -11,28 +14,47 @@ var ( lc = tfo.ListenConfig{ DisableTFO: true, } + mutex sync.RWMutex ) func SetTfo(open bool) { + mutex.Lock() + defer mutex.Unlock() lc.DisableTFO = !open } func Tfo() bool { + mutex.RLock() + defer mutex.RUnlock() return !lc.DisableTFO } func SetMPTCP(open bool) { + mutex.Lock() + defer mutex.Unlock() setMultiPathTCP(&lc.ListenConfig, open) } func MPTCP() bool { + mutex.RLock() + defer mutex.RUnlock() return getMultiPathTCP(&lc.ListenConfig) } func ListenContext(ctx context.Context, network, address string) (net.Listener, error) { + mutex.RLock() + defer mutex.RUnlock() return lc.Listen(ctx, network, address) } func Listen(network, address string) (net.Listener, error) { return ListenContext(context.Background(), network, address) } + +func init() { + keepalive.SetDisableKeepAliveCallback.Register(func(b bool) { + mutex.Lock() + defer mutex.Unlock() + keepalive.SetNetListenConfig(&lc.ListenConfig) + }) +} diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index 7114045d..15f081f2 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -6,7 +6,6 @@ import ( "os" "strconv" - N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/loopback" "github.com/metacubex/mihomo/component/resolver" @@ -38,7 +37,6 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ... if err != nil { return nil, err } - N.TCPKeepAlive(c) return d.loopBack.NewConn(NewConn(c, d)), nil } diff --git a/adapter/outbound/http.go b/adapter/outbound/http.go index b837e49a..ebb1d67c 100644 --- a/adapter/outbound/http.go +++ b/adapter/outbound/http.go @@ -7,13 +7,11 @@ import ( "encoding/base64" "errors" "fmt" - "io" "net" "net/http" "strconv" - N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/proxydialer" @@ -76,7 +74,6 @@ func (h *Http) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metad if err != nil { return nil, fmt.Errorf("%s connect error: %w", h.addr, err) } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 88fb8456..021fbc0a 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -149,7 +149,6 @@ func (ss *ShadowSocks) DialContextWithDialer(ctx context.Context, dialer C.Diale if err != nil { return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index 07d78047..437695b4 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -80,7 +80,6 @@ func (ssr *ShadowSocksR) DialContextWithDialer(ctx context.Context, dialer C.Dia if err != nil { return nil, fmt.Errorf("%s connect error: %w", ssr.addr, err) } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) diff --git a/adapter/outbound/snell.go b/adapter/outbound/snell.go index 76ed4be9..f6a4b4f9 100644 --- a/adapter/outbound/snell.go +++ b/adapter/outbound/snell.go @@ -6,7 +6,6 @@ import ( "net" "strconv" - N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/common/structure" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/proxydialer" @@ -94,7 +93,6 @@ func (s *Snell) DialContextWithDialer(ctx context.Context, dialer C.Dialer, meta if err != nil { return nil, fmt.Errorf("%s connect error: %w", s.addr, err) } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) @@ -122,7 +120,6 @@ func (s *Snell) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met if err != nil { return nil, err } - N.TCPKeepAlive(c) c = streamConn(c, streamOption{s.psk, s.version, s.addr, s.obfsOption}) err = snell.WriteUDPHeader(c, s.version) @@ -207,8 +204,7 @@ func NewSnell(option SnellOption) (*Snell, error) { if err != nil { return nil, err } - - N.TCPKeepAlive(c) + return streamConn(c, streamOption{psk, option.Version, addr, obfsOption}), nil }) } diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index c17ee6a7..1908167a 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -10,7 +10,6 @@ import ( "net/netip" "strconv" - N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/proxydialer" @@ -82,7 +81,6 @@ func (ss *Socks5) DialContextWithDialer(ctx context.Context, dialer C.Dialer, me if err != nil { return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) @@ -128,7 +126,6 @@ func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, safeConnClose(c, err) }(c) - N.TCPKeepAlive(c) var user *socks5.User if ss.user != "" { user = &socks5.User{ diff --git a/adapter/outbound/ssh.go b/adapter/outbound/ssh.go index 8b2776a6..9e23b463 100644 --- a/adapter/outbound/ssh.go +++ b/adapter/outbound/ssh.go @@ -77,7 +77,6 @@ func (s *sshClient) connect(ctx context.Context, cDialer C.Dialer, addr string) if err != nil { return nil, err } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index 938a8858..b3a611af 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -9,7 +9,6 @@ import ( "net/http" "strconv" - N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/proxydialer" @@ -154,7 +153,6 @@ func (t *Trojan) DialContextWithDialer(ctx context.Context, dialer C.Dialer, met if err != nil { return nil, fmt.Errorf("%s connect error: %w", t.addr, err) } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) @@ -212,7 +210,6 @@ func (t *Trojan) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, me defer func(c net.Conn) { safeConnClose(c, err) }(c) - N.TCPKeepAlive(c) c, err = t.plainStream(ctx, c) if err != nil { return nil, fmt.Errorf("%s connect error: %w", t.addr, err) @@ -314,7 +311,6 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { if err != nil { return nil, fmt.Errorf("%s connect error: %s", t.addr, err.Error()) } - N.TCPKeepAlive(c) return c, nil } diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index b18bf4da..79058874 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -262,7 +262,6 @@ func (v *Vless) DialContextWithDialer(ctx context.Context, dialer C.Dialer, meta if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) }(c) @@ -327,7 +326,6 @@ func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) }(c) @@ -574,7 +572,6 @@ func NewVless(option VlessOption) (*Vless, error) { if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } - N.TCPKeepAlive(c) return c, nil } diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 7d5a7224..8797374d 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -312,7 +312,6 @@ func (v *Vmess) DialContextWithDialer(ctx context.Context, dialer C.Dialer, meta if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) }(c) @@ -373,7 +372,6 @@ func (v *Vmess) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } - N.TCPKeepAlive(c) defer func(c net.Conn) { safeConnClose(c, err) }(c) @@ -473,7 +471,6 @@ func NewVmess(option VmessOption) (*Vmess, error) { if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } - N.TCPKeepAlive(c) return c, nil } diff --git a/common/net/tcp_keepalive.go b/common/net/tcp_keepalive.go deleted file mode 100644 index 047a1c05..00000000 --- a/common/net/tcp_keepalive.go +++ /dev/null @@ -1,23 +0,0 @@ -package net - -import ( - "net" - "runtime" - "time" -) - -var ( - KeepAliveIdle = 0 * time.Second - KeepAliveInterval = 0 * time.Second - DisableKeepAlive = false -) - -func TCPKeepAlive(c net.Conn) { - if tcp, ok := c.(*net.TCPConn); ok { - if runtime.GOOS == "android" || DisableKeepAlive { - _ = tcp.SetKeepAlive(false) - } else { - tcpKeepAlive(tcp) - } - } -} diff --git a/common/net/tcp_keepalive_go122.go b/common/net/tcp_keepalive_go122.go deleted file mode 100644 index 12873168..00000000 --- a/common/net/tcp_keepalive_go122.go +++ /dev/null @@ -1,10 +0,0 @@ -//go:build !go1.23 - -package net - -import "net" - -func tcpKeepAlive(tcp *net.TCPConn) { - _ = tcp.SetKeepAlive(true) - _ = tcp.SetKeepAlivePeriod(KeepAliveInterval) -} diff --git a/common/net/tcp_keepalive_go123.go b/common/net/tcp_keepalive_go123.go deleted file mode 100644 index 2dd4754b..00000000 --- a/common/net/tcp_keepalive_go123.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build go1.23 - -package net - -import "net" - -func tcpKeepAlive(tcp *net.TCPConn) { - config := net.KeepAliveConfig{ - Enable: true, - Idle: KeepAliveIdle, - Interval: KeepAliveInterval, - } - if !SupportTCPKeepAliveCount() { - // it's recommended to set both Idle and Interval to non-negative values in conjunction with a -1 - // for Count on those old Windows if you intend to customize the TCP keep-alive settings. - config.Count = -1 - } - _ = tcp.SetKeepAliveConfig(config) -} diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 41f79b8e..3dfd3159 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -12,6 +12,7 @@ import ( "sync" "time" + "github.com/metacubex/mihomo/component/keepalive" "github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/log" ) @@ -144,6 +145,7 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po } dialer := netDialer.(*net.Dialer) + keepalive.SetNetDialer(dialer) if opt.mpTcp { setMultiPathTCP(dialer) } diff --git a/component/keepalive/tcp_keepalive.go b/component/keepalive/tcp_keepalive.go new file mode 100644 index 00000000..9b24c45a --- /dev/null +++ b/component/keepalive/tcp_keepalive.go @@ -0,0 +1,65 @@ +package keepalive + +import ( + "net" + "runtime" + "time" + + "github.com/metacubex/mihomo/common/atomic" + "github.com/metacubex/mihomo/common/utils" +) + +var ( + keepAliveIdle = atomic.NewTypedValue[time.Duration](0 * time.Second) + keepAliveInterval = atomic.NewTypedValue[time.Duration](0 * time.Second) + disableKeepAlive = atomic.NewBool(false) + + SetDisableKeepAliveCallback = utils.NewCallback[bool]() +) + +func SetKeepAliveIdle(t time.Duration) { + keepAliveIdle.Store(t) +} + +func SetKeepAliveInterval(t time.Duration) { + keepAliveInterval.Store(t) +} + +func KeepAliveIdle() time.Duration { + return keepAliveIdle.Load() +} + +func KeepAliveInterval() time.Duration { + return keepAliveInterval.Load() +} + +func SetDisableKeepAlive(disable bool) { + if runtime.GOOS == "android" { + setDisableKeepAlive(false) + } else { + setDisableKeepAlive(disable) + } +} + +func setDisableKeepAlive(disable bool) { + disableKeepAlive.Store(disable) + SetDisableKeepAliveCallback.Emit(disable) +} + +func DisableKeepAlive() bool { + return disableKeepAlive.Load() +} + +func SetNetDialer(dialer *net.Dialer) { + setNetDialer(dialer) +} + +func SetNetListenConfig(lc *net.ListenConfig) { + setNetListenConfig(lc) +} + +func TCPKeepAlive(c net.Conn) { + if tcp, ok := c.(*net.TCPConn); ok && tcp != nil { + tcpKeepAlive(tcp) + } +} diff --git a/component/keepalive/tcp_keepalive_go122.go b/component/keepalive/tcp_keepalive_go122.go new file mode 100644 index 00000000..5d88827d --- /dev/null +++ b/component/keepalive/tcp_keepalive_go122.go @@ -0,0 +1,30 @@ +//go:build !go1.23 + +package keepalive + +import "net" + +func tcpKeepAlive(tcp *net.TCPConn) { + if DisableKeepAlive() { + _ = tcp.SetKeepAlive(false) + } else { + _ = tcp.SetKeepAlive(true) + _ = tcp.SetKeepAlivePeriod(KeepAliveInterval()) + } +} + +func setNetDialer(dialer *net.Dialer) { + if DisableKeepAlive() { + dialer.KeepAlive = -1 // If negative, keep-alive probes are disabled. + } else { + dialer.KeepAlive = KeepAliveInterval() + } +} + +func setNetListenConfig(lc *net.ListenConfig) { + if DisableKeepAlive() { + lc.KeepAlive = -1 // If negative, keep-alive probes are disabled. + } else { + lc.KeepAlive = KeepAliveInterval() + } +} diff --git a/component/keepalive/tcp_keepalive_go123.go b/component/keepalive/tcp_keepalive_go123.go new file mode 100644 index 00000000..4c08118b --- /dev/null +++ b/component/keepalive/tcp_keepalive_go123.go @@ -0,0 +1,45 @@ +//go:build go1.23 + +package keepalive + +import "net" + +func keepAliveConfig() net.KeepAliveConfig { + config := net.KeepAliveConfig{ + Enable: true, + Idle: KeepAliveIdle(), + Interval: KeepAliveInterval(), + } + if !SupportTCPKeepAliveCount() { + // it's recommended to set both Idle and Interval to non-negative values in conjunction with a -1 + // for Count on those old Windows if you intend to customize the TCP keep-alive settings. + config.Count = -1 + } + return config +} + +func tcpKeepAlive(tcp *net.TCPConn) { + if DisableKeepAlive() { + _ = tcp.SetKeepAlive(false) + } else { + _ = tcp.SetKeepAliveConfig(keepAliveConfig()) + } +} + +func setNetDialer(dialer *net.Dialer) { + if DisableKeepAlive() { + dialer.KeepAlive = -1 // If negative, keep-alive probes are disabled. + dialer.KeepAliveConfig.Enable = false + } else { + dialer.KeepAliveConfig = keepAliveConfig() + } +} + +func setNetListenConfig(lc *net.ListenConfig) { + if DisableKeepAlive() { + lc.KeepAlive = -1 // If negative, keep-alive probes are disabled. + lc.KeepAliveConfig.Enable = false + } else { + lc.KeepAliveConfig = keepAliveConfig() + } +} diff --git a/common/net/tcp_keepalive_go123_unix.go b/component/keepalive/tcp_keepalive_go123_unix.go similarity index 91% rename from common/net/tcp_keepalive_go123_unix.go rename to component/keepalive/tcp_keepalive_go123_unix.go index 0ead7ca4..8033cc6c 100644 --- a/common/net/tcp_keepalive_go123_unix.go +++ b/component/keepalive/tcp_keepalive_go123_unix.go @@ -1,6 +1,6 @@ //go:build go1.23 && unix -package net +package keepalive func SupportTCPKeepAliveIdle() bool { return true diff --git a/common/net/tcp_keepalive_go123_windows.go b/component/keepalive/tcp_keepalive_go123_windows.go similarity index 99% rename from common/net/tcp_keepalive_go123_windows.go rename to component/keepalive/tcp_keepalive_go123_windows.go index 8f1e61f9..2462e80c 100644 --- a/common/net/tcp_keepalive_go123_windows.go +++ b/component/keepalive/tcp_keepalive_go123_windows.go @@ -2,7 +2,7 @@ // copy and modify from golang1.23's internal/syscall/windows/version_windows.go -package net +package keepalive import ( "errors" diff --git a/config/config.go b/config/config.go index 2a75ddd6..27cde1fb 100644 --- a/config/config.go +++ b/config/config.go @@ -15,13 +15,13 @@ import ( "github.com/metacubex/mihomo/adapter/outbound" "github.com/metacubex/mihomo/adapter/outboundgroup" "github.com/metacubex/mihomo/adapter/provider" - N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/auth" "github.com/metacubex/mihomo/component/cidr" "github.com/metacubex/mihomo/component/fakeip" "github.com/metacubex/mihomo/component/geodata" mihomoHttp "github.com/metacubex/mihomo/component/http" + "github.com/metacubex/mihomo/component/keepalive" P "github.com/metacubex/mihomo/component/process" "github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/component/resource" @@ -697,12 +697,12 @@ func parseGeneral(cfg *RawConfig) (*General, error) { resource.SetETag(cfg.ETagSupport) if cfg.KeepAliveIdle != 0 { - N.KeepAliveIdle = time.Duration(cfg.KeepAliveIdle) * time.Second + keepalive.SetKeepAliveIdle(time.Duration(cfg.KeepAliveIdle) * time.Second) } if cfg.KeepAliveInterval != 0 { - N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second + keepalive.SetKeepAliveInterval(time.Duration(cfg.KeepAliveInterval) * time.Second) } - N.DisableKeepAlive = cfg.DisableKeepAlive + keepalive.SetDisableKeepAlive(cfg.DisableKeepAlive) // checkout externalUI exist if cfg.ExternalUI != "" { diff --git a/listener/http/server.go b/listener/http/server.go index 48f12dc5..04f32f4f 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -4,7 +4,6 @@ import ( "net" "github.com/metacubex/mihomo/adapter/inbound" - N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/auth" C "github.com/metacubex/mihomo/constant" authStore "github.com/metacubex/mihomo/listener/auth" @@ -55,8 +54,8 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Auth inbound.WithSpecialRules(""), } } - l, err := inbound.Listen("tcp", addr) + l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err } @@ -74,7 +73,6 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Auth } continue } - N.TCPKeepAlive(conn) getAuth := getAuth if isDefault { // only apply on default listener diff --git a/listener/mixed/mixed.go b/listener/mixed/mixed.go index 12390061..ac3a0c58 100644 --- a/listener/mixed/mixed.go +++ b/listener/mixed/mixed.go @@ -49,6 +49,7 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Auth inbound.WithSpecialRules(""), } } + l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err @@ -85,8 +86,6 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Auth } func handleConn(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) { - N.TCPKeepAlive(conn) - bufConn := N.NewBufferedConn(conn) head, err := bufConn.Peek(1) if err != nil { diff --git a/listener/redir/tcp.go b/listener/redir/tcp.go index 8474a8e2..47363182 100644 --- a/listener/redir/tcp.go +++ b/listener/redir/tcp.go @@ -4,7 +4,7 @@ import ( "net" "github.com/metacubex/mihomo/adapter/inbound" - N "github.com/metacubex/mihomo/common/net" + "github.com/metacubex/mihomo/component/keepalive" C "github.com/metacubex/mihomo/constant" ) @@ -37,10 +37,12 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener inbound.WithSpecialRules(""), } } + l, err := net.Listen("tcp", addr) if err != nil { return nil, err } + rl := &Listener{ listener: l, addr: addr, @@ -68,6 +70,6 @@ func handleRedir(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) conn.Close() return } - N.TCPKeepAlive(conn) + keepalive.TCPKeepAlive(conn) tunnel.HandleTCPConn(inbound.NewSocket(target, conn, C.REDIR, additions...)) } diff --git a/listener/shadowsocks/tcp.go b/listener/shadowsocks/tcp.go index c3843814..b150e4cb 100644 --- a/listener/shadowsocks/tcp.go +++ b/listener/shadowsocks/tcp.go @@ -59,7 +59,6 @@ func New(config LC.ShadowsocksServer, tunnel C.Tunnel, additions ...inbound.Addi } continue } - N.TCPKeepAlive(c) go sl.HandleConn(c, tunnel, additions...) } }() diff --git a/listener/sing_shadowsocks/server.go b/listener/sing_shadowsocks/server.go index 1cb798f7..5f2a4292 100644 --- a/listener/sing_shadowsocks/server.go +++ b/listener/sing_shadowsocks/server.go @@ -7,7 +7,6 @@ import ( "strings" "github.com/metacubex/mihomo/adapter/inbound" - N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/common/sockopt" C "github.com/metacubex/mihomo/constant" LC "github.com/metacubex/mihomo/listener/config" @@ -153,7 +152,6 @@ func New(config LC.ShadowsocksServer, tunnel C.Tunnel, additions ...inbound.Addi } continue } - N.TCPKeepAlive(c) go sl.HandleConn(c, tunnel) } diff --git a/listener/sing_vmess/server.go b/listener/sing_vmess/server.go index ce422b16..7a0afa0b 100644 --- a/listener/sing_vmess/server.go +++ b/listener/sing_vmess/server.go @@ -121,7 +121,6 @@ func New(config LC.VmessServer, tunnel C.Tunnel, additions ...inbound.Addition) } continue } - N.TCPKeepAlive(c) go sl.HandleConn(c, tunnel) } diff --git a/listener/socks/tcp.go b/listener/socks/tcp.go index 3e98a602..950384c1 100644 --- a/listener/socks/tcp.go +++ b/listener/socks/tcp.go @@ -48,6 +48,7 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Auth inbound.WithSpecialRules(""), } } + l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err @@ -84,7 +85,6 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Auth } func handleSocks(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) { - N.TCPKeepAlive(conn) bufConn := N.NewBufferedConn(conn) head, err := bufConn.Peek(1) if err != nil { diff --git a/listener/tproxy/tproxy.go b/listener/tproxy/tproxy.go index fa7e7dbe..6056047a 100644 --- a/listener/tproxy/tproxy.go +++ b/listener/tproxy/tproxy.go @@ -4,7 +4,7 @@ import ( "net" "github.com/metacubex/mihomo/adapter/inbound" - N "github.com/metacubex/mihomo/common/net" + "github.com/metacubex/mihomo/component/keepalive" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/transport/socks5" ) @@ -33,7 +33,7 @@ func (l *Listener) Close() error { func (l *Listener) handleTProxy(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) { target := socks5.ParseAddrToSocksAddr(conn.LocalAddr()) - N.TCPKeepAlive(conn) + keepalive.TCPKeepAlive(conn) // TProxy's conn.LocalAddr() is target address, so we set from l.listener additions = append([]inbound.Addition{inbound.WithInAddr(l.listener.Addr())}, additions...) tunnel.HandleTCPConn(inbound.NewSocket(target, conn, C.TPROXY, additions...)) diff --git a/listener/tunnel/tcp.go b/listener/tunnel/tcp.go index 794dc8ac..7c916a38 100644 --- a/listener/tunnel/tcp.go +++ b/listener/tunnel/tcp.go @@ -5,7 +5,6 @@ import ( "net" "github.com/metacubex/mihomo/adapter/inbound" - N "github.com/metacubex/mihomo/common/net" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/transport/socks5" ) @@ -35,7 +34,6 @@ func (l *Listener) Close() error { } func (l *Listener) handleTCP(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) { - N.TCPKeepAlive(conn) tunnel.HandleTCPConn(inbound.NewSocket(l.target, conn, C.TUNNEL, additions...)) } From 3922b17067e3555bfffb83ade97affee082e55da Mon Sep 17 00:00:00 2001 From: HamsterReserved Date: Wed, 25 Sep 2024 21:28:30 +0800 Subject: [PATCH 06/44] chore: deliver UDP packets from same connection in receiving order (#1540) All UDP packets are queued into a single channel, and multiple workers are launched to poll the channel in current design. This introduces a problem where UDP packets from a single connection are delivered to different workers, thus forwarded in a random order if workers are on different CPU cores. Though UDP peers normally have their own logic to handle out-of-order packets, this behavior will inevitably cause significant variance in delay and harm connection quality. Furthermore, this out-of-order behavior is noticeable even if the underlying transport could provide guaranteed orderly delivery - this is unacceptable. This commit takes the idea of RSS in terms of NICs: it creates a distinct queue for each worker, hashes incoming packets, and distribute the packet to a worker by hash result. The tuple (SrcIP, SrcPort, DstIP, DstPort, Proto) is used for hashing (Proto is always UDP so it's dropped from final implementation), thus packets from the same connection can be sent to the same worker, keeping the receiving order. Different connections can be hashed to different workers to maintain performance. Performance for single UDP connection is not affected, as there is already a lock in natTable that prevents multiple packets being processed in different workers, limiting single connection forwarding performance to 1 worker. The only performance penalty is the hashing code, which should be neglectable given the footprint of en/decryption work. Co-authored-by: Hamster Tian --- tunnel/tunnel.go | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 60ba0323..5b55f07d 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "hash/maphash" "net" "net/netip" "path/filepath" @@ -31,7 +32,8 @@ import ( var ( status = newAtomicStatus(Suspend) tcpQueue = make(chan C.ConnContext, 200) - udpQueue = make(chan C.PacketAdapter, 200) + udpQueues []chan C.PacketAdapter + udpHashSeed = maphash.MakeSeed() natTable = nat.New() rules []C.Rule listeners = make(map[string]C.InboundListener) @@ -70,8 +72,17 @@ func (t tunnel) HandleTCPConn(conn net.Conn, metadata *C.Metadata) { func (t tunnel) HandleUDPPacket(packet C.UDPPacket, metadata *C.Metadata) { packetAdapter := C.NewPacketAdapter(packet, metadata) + + var h maphash.Hash + + h.SetSeed(udpHashSeed) + h.WriteString(metadata.SourceAddress()) + h.WriteString(metadata.RemoteAddress()) + + queueNo := uint(h.Sum64()) % uint(len(udpQueues)) + select { - case udpQueue <- packetAdapter: + case udpQueues[queueNo] <- packetAdapter: default: } } @@ -141,7 +152,8 @@ func TCPIn() chan<- C.ConnContext { // UDPIn return fan-in udp queue // Deprecated: using Tunnel instead func UDPIn() chan<- C.PacketAdapter { - return udpQueue + // compatibility: first queue is always available for external callers + return udpQueues[0] } // NatTable return nat table @@ -243,8 +255,8 @@ func isHandle(t C.Type) bool { } // processUDP starts a loop to handle udp packet -func processUDP() { - queue := udpQueue +func processUDP(queueNo int) { + queue := udpQueues[queueNo] for conn := range queue { handleUDPConn(conn) } @@ -255,8 +267,11 @@ func process() { if num := runtime.GOMAXPROCS(0); num > numUDPWorkers { numUDPWorkers = num } + + udpQueues = make([]chan C.PacketAdapter, numUDPWorkers) for i := 0; i < numUDPWorkers; i++ { - go processUDP() + udpQueues[i] = make(chan C.PacketAdapter, 200) + go processUDP(i) } queue := tcpQueue From 5812a7bdeb5928ee5e33d18f677d21c8cf4b1bde Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 25 Sep 2024 21:37:15 +0800 Subject: [PATCH 07/44] chore: simplify the code --- tunnel/tunnel.go | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 5b55f07d..b6c61d76 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "hash/maphash" "net" "net/netip" "path/filepath" @@ -29,11 +28,12 @@ import ( "github.com/metacubex/mihomo/tunnel/statistic" ) +const queueSize = 200 + var ( status = newAtomicStatus(Suspend) - tcpQueue = make(chan C.ConnContext, 200) + tcpQueue = make(chan C.ConnContext, queueSize) udpQueues []chan C.PacketAdapter - udpHashSeed = maphash.MakeSeed() natTable = nat.New() rules []C.Rule listeners = make(map[string]C.InboundListener) @@ -73,13 +73,8 @@ func (t tunnel) HandleTCPConn(conn net.Conn, metadata *C.Metadata) { func (t tunnel) HandleUDPPacket(packet C.UDPPacket, metadata *C.Metadata) { packetAdapter := C.NewPacketAdapter(packet, metadata) - var h maphash.Hash - - h.SetSeed(udpHashSeed) - h.WriteString(metadata.SourceAddress()) - h.WriteString(metadata.RemoteAddress()) - - queueNo := uint(h.Sum64()) % uint(len(udpQueues)) + hash := utils.MapHash(metadata.SourceAddress() + "-" + metadata.RemoteAddress()) + queueNo := uint(hash) % uint(len(udpQueues)) select { case udpQueues[queueNo] <- packetAdapter: @@ -255,8 +250,7 @@ func isHandle(t C.Type) bool { } // processUDP starts a loop to handle udp packet -func processUDP(queueNo int) { - queue := udpQueues[queueNo] +func processUDP(queue chan C.PacketAdapter) { for conn := range queue { handleUDPConn(conn) } @@ -270,8 +264,9 @@ func process() { udpQueues = make([]chan C.PacketAdapter, numUDPWorkers) for i := 0; i < numUDPWorkers; i++ { - udpQueues[i] = make(chan C.PacketAdapter, 200) - go processUDP(i) + queue := make(chan C.PacketAdapter, queueSize) + udpQueues[i] = queue + go processUDP(queue) } queue := tcpQueue From 4fa15c633494f6cf2fac2bef282667b4b0ee9db2 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 26 Sep 2024 11:21:07 +0800 Subject: [PATCH 08/44] chore: ensures packets can be sent without blocking the tunnel --- component/nat/table.go | 44 +++------ constant/adapters.go | 26 +++-- tunnel/connection.go | 75 +++++++++++++- tunnel/tunnel.go | 220 +++++++++++++++++++++-------------------- 4 files changed, 218 insertions(+), 147 deletions(-) diff --git a/component/nat/table.go b/component/nat/table.go index bb5ab755..66241fb4 100644 --- a/component/nat/table.go +++ b/component/nat/table.go @@ -10,47 +10,30 @@ import ( ) type Table struct { - mapping *xsync.MapOf[string, *Entry] - lockMap *xsync.MapOf[string, *sync.Cond] + mapping *xsync.MapOf[string, *entry] } -type Entry struct { - PacketConn C.PacketConn - WriteBackProxy C.WriteBackProxy +type entry struct { + PacketSender C.PacketSender LocalUDPConnMap *xsync.MapOf[string, *net.UDPConn] LocalLockMap *xsync.MapOf[string, *sync.Cond] } -func (t *Table) Set(key string, e C.PacketConn, w C.WriteBackProxy) { - t.mapping.Store(key, &Entry{ - PacketConn: e, - WriteBackProxy: w, - LocalUDPConnMap: xsync.NewMapOf[string, *net.UDPConn](), - LocalLockMap: xsync.NewMapOf[string, *sync.Cond](), +func (t *Table) GetOrCreate(key string, maker func() C.PacketSender) (C.PacketSender, bool) { + item, loaded := t.mapping.LoadOrCompute(key, func() *entry { + return &entry{ + PacketSender: maker(), + LocalUDPConnMap: xsync.NewMapOf[string, *net.UDPConn](), + LocalLockMap: xsync.NewMapOf[string, *sync.Cond](), + } }) -} - -func (t *Table) Get(key string) (C.PacketConn, C.WriteBackProxy) { - entry, exist := t.getEntry(key) - if !exist { - return nil, nil - } - return entry.PacketConn, entry.WriteBackProxy -} - -func (t *Table) GetOrCreateLock(key string) (*sync.Cond, bool) { - item, loaded := t.lockMap.LoadOrCompute(key, makeLock) - return item, loaded + return item.PacketSender, loaded } func (t *Table) Delete(key string) { t.mapping.Delete(key) } -func (t *Table) DeleteLock(lockKey string) { - t.lockMap.Delete(lockKey) -} - func (t *Table) GetForLocalConn(lAddr, rAddr string) *net.UDPConn { entry, exist := t.getEntry(lAddr) if !exist { @@ -105,7 +88,7 @@ func (t *Table) DeleteLockForLocalConn(lAddr, key string) { entry.LocalLockMap.Delete(key) } -func (t *Table) getEntry(key string) (*Entry, bool) { +func (t *Table) getEntry(key string) (*entry, bool) { return t.mapping.Load(key) } @@ -116,7 +99,6 @@ func makeLock() *sync.Cond { // New return *Cache func New() *Table { return &Table{ - mapping: xsync.NewMapOf[string, *Entry](), - lockMap: xsync.NewMapOf[string, *sync.Cond](), + mapping: xsync.NewMapOf[string, *entry](), } } diff --git a/constant/adapters.go b/constant/adapters.go index cb213b3c..3731cd60 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -255,12 +255,16 @@ type UDPPacketInAddr interface { // PacketAdapter is a UDP Packet adapter for socks/redir/tun type PacketAdapter interface { UDPPacket + // Metadata returns destination metadata Metadata() *Metadata + // Key is a SNAT key + Key() string } type packetAdapter struct { UDPPacket metadata *Metadata + key string } // Metadata returns destination metadata @@ -268,10 +272,16 @@ func (s *packetAdapter) Metadata() *Metadata { return s.metadata } +// Key is a SNAT key +func (s *packetAdapter) Key() string { + return s.key +} + func NewPacketAdapter(packet UDPPacket, metadata *Metadata) PacketAdapter { return &packetAdapter{ packet, metadata, + packet.LocalAddr().String(), } } @@ -284,17 +294,19 @@ type WriteBackProxy interface { UpdateWriteBack(wb WriteBack) } +type PacketSender interface { + // Send will send PacketAdapter nonblocking + // the implement must call UDPPacket.Drop() inside Send + Send(PacketAdapter) + Process(PacketConn, WriteBackProxy) + Close() +} + type NatTable interface { - Set(key string, e PacketConn, w WriteBackProxy) - - Get(key string) (PacketConn, WriteBackProxy) - - GetOrCreateLock(key string) (*sync.Cond, bool) + GetOrCreate(key string, maker func() PacketSender) (PacketSender, bool) Delete(key string) - DeleteLock(key string) - GetForLocalConn(lAddr, rAddr string) *net.UDPConn AddForLocalConn(lAddr, rAddr string, conn *net.UDPConn) bool diff --git a/tunnel/connection.go b/tunnel/connection.go index e96545e8..17e4efd0 100644 --- a/tunnel/connection.go +++ b/tunnel/connection.go @@ -1,6 +1,7 @@ package tunnel import ( + "context" "errors" "net" "net/netip" @@ -11,7 +12,78 @@ import ( "github.com/metacubex/mihomo/log" ) +type packetSender struct { + ctx context.Context + cancel context.CancelFunc + ch chan C.PacketAdapter +} + +// newPacketSender return a chan based C.PacketSender +// It ensures that packets can be sent sequentially and without blocking +func newPacketSender() C.PacketSender { + ctx, cancel := context.WithCancel(context.Background()) + ch := make(chan C.PacketAdapter, senderCapacity) + return &packetSender{ + ctx: ctx, + cancel: cancel, + ch: ch, + } +} + +func (s *packetSender) Process(pc C.PacketConn, proxy C.WriteBackProxy) { + for { + select { + case <-s.ctx.Done(): + return // sender closed + case packet := <-s.ch: + if proxy != nil { + proxy.UpdateWriteBack(packet) + } + _ = handleUDPToRemote(packet, pc, packet.Metadata()) + packet.Drop() + } + } +} + +func (s *packetSender) dropAll() { + for { + select { + case data := <-s.ch: + data.Drop() // drop all data still in chan + default: + return // no data, exit goroutine + } + } +} + +func (s *packetSender) Send(packet C.PacketAdapter) { + select { + case <-s.ctx.Done(): + packet.Drop() // sender closed before Send() + return + default: + } + + select { + case s.ch <- packet: + // put ok, so don't drop packet, will process by other side of chan + case <-s.ctx.Done(): + packet.Drop() // sender closed when putting data to chan + default: + packet.Drop() // chan is full + } +} + +func (s *packetSender) Close() { + s.cancel() + s.dropAll() +} + func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata) error { + if err := resolveUDP(metadata); err != nil { + return err + } + addr := metadata.UDPAddr() if addr == nil { return errors.New("udp addr invalid") @@ -26,8 +98,9 @@ func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata return nil } -func handleUDPToLocal(writeBack C.WriteBack, pc N.EnhancePacketConn, key string, oAddrPort netip.AddrPort, fAddr netip.Addr) { +func handleUDPToLocal(writeBack C.WriteBack, pc N.EnhancePacketConn, sender C.PacketSender, key string, oAddrPort netip.AddrPort, fAddr netip.Addr) { defer func() { + sender.Close() _ = pc.Close() closeAllLocalCoon(key) natTable.Delete(key) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index b6c61d76..af16e4ae 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -28,11 +28,14 @@ import ( "github.com/metacubex/mihomo/tunnel/statistic" ) -const queueSize = 200 +const ( + queueCapacity = 64 // chan capacity tcpQueue and udpQueue + senderCapacity = 128 // chan capacity of PacketSender +) var ( status = newAtomicStatus(Suspend) - tcpQueue = make(chan C.ConnContext, queueSize) + udpInit sync.Once udpQueues []chan C.PacketAdapter natTable = nat.New() rules []C.Rule @@ -43,6 +46,12 @@ var ( ruleProviders map[string]provider.RuleProvider configMux sync.RWMutex + // for compatibility, lazy init + tcpQueue chan C.ConnContext + tcpInOnce sync.Once + udpQueue chan C.PacketAdapter + udpInOnce sync.Once + // Outbound Rule mode = Rule @@ -70,15 +79,33 @@ func (t tunnel) HandleTCPConn(conn net.Conn, metadata *C.Metadata) { handleTCPConn(connCtx) } -func (t tunnel) HandleUDPPacket(packet C.UDPPacket, metadata *C.Metadata) { - packetAdapter := C.NewPacketAdapter(packet, metadata) +func initUDP() { + numUDPWorkers := 4 + if num := runtime.GOMAXPROCS(0); num > numUDPWorkers { + numUDPWorkers = num + } - hash := utils.MapHash(metadata.SourceAddress() + "-" + metadata.RemoteAddress()) + udpQueues = make([]chan C.PacketAdapter, numUDPWorkers) + for i := 0; i < numUDPWorkers; i++ { + queue := make(chan C.PacketAdapter, queueCapacity) + udpQueues[i] = queue + go processUDP(queue) + } +} + +func (t tunnel) HandleUDPPacket(packet C.UDPPacket, metadata *C.Metadata) { + udpInit.Do(initUDP) + + packetAdapter := C.NewPacketAdapter(packet, metadata) + key := packetAdapter.Key() + + hash := utils.MapHash(key) queueNo := uint(hash) % uint(len(udpQueues)) select { case udpQueues[queueNo] <- packetAdapter: default: + packet.Drop() } } @@ -134,21 +161,32 @@ func IsSniffing() bool { return sniffingEnable } -func init() { - go process() -} - // TCPIn return fan-in queue // Deprecated: using Tunnel instead func TCPIn() chan<- C.ConnContext { + tcpInOnce.Do(func() { + tcpQueue = make(chan C.ConnContext, queueCapacity) + go func() { + for connCtx := range tcpQueue { + go handleTCPConn(connCtx) + } + }() + }) return tcpQueue } // UDPIn return fan-in udp queue // Deprecated: using Tunnel instead func UDPIn() chan<- C.PacketAdapter { - // compatibility: first queue is always available for external callers - return udpQueues[0] + udpInOnce.Do(func() { + udpQueue = make(chan C.PacketAdapter, queueCapacity) + go func() { + for packet := range udpQueue { + Tunnel.HandleUDPPacket(packet, packet.Metadata()) + } + }() + }) + return udpQueue } // NatTable return nat table @@ -249,32 +287,6 @@ func isHandle(t C.Type) bool { return status == Running || (status == Inner && t == C.INNER) } -// processUDP starts a loop to handle udp packet -func processUDP(queue chan C.PacketAdapter) { - for conn := range queue { - handleUDPConn(conn) - } -} - -func process() { - numUDPWorkers := 4 - if num := runtime.GOMAXPROCS(0); num > numUDPWorkers { - numUDPWorkers = num - } - - udpQueues = make([]chan C.PacketAdapter, numUDPWorkers) - for i := 0; i < numUDPWorkers; i++ { - queue := make(chan C.PacketAdapter, queueSize) - udpQueues[i] = queue - go processUDP(queue) - } - - queue := tcpQueue - for conn := range queue { - go handleTCPConn(conn) - } -} - func needLookupIP(metadata *C.Metadata) bool { return resolver.MappingEnabled() && metadata.Host == "" && metadata.DstIP.IsValid() } @@ -334,6 +346,25 @@ func resolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err erro return } +func resolveUDP(metadata *C.Metadata) error { + // local resolve UDP dns + if !metadata.Resolved() { + ip, err := resolver.ResolveIP(context.Background(), metadata.Host) + if err != nil { + return err + } + metadata.DstIP = ip + } + return nil +} + +// processUDP starts a loop to handle udp packet +func processUDP(queue chan C.PacketAdapter) { + for conn := range queue { + handleUDPConn(conn) + } +} + func handleUDPConn(packet C.PacketAdapter) { if !isHandle(packet.Metadata().Type) { packet.Drop() @@ -363,85 +394,58 @@ func handleUDPConn(packet C.PacketAdapter) { snifferDispatcher.UDPSniff(packet) } - // local resolve UDP dns - if !metadata.Resolved() { - ip, err := resolver.ResolveIP(context.Background(), metadata.Host) - if err != nil { - return - } - metadata.DstIP = ip - } - - key := packet.LocalAddr().String() - - handle := func() bool { - pc, proxy := natTable.Get(key) - if pc != nil { - if proxy != nil { - proxy.UpdateWriteBack(packet) + key := packet.Key() + sender, loaded := natTable.GetOrCreate(key, newPacketSender) + if !loaded { + dial := func() (C.PacketConn, C.WriteBackProxy, error) { + if err := resolveUDP(metadata); err != nil { + log.Warnln("[UDP] Resolve Ip error: %s", err) + return nil, nil, err } - _ = handleUDPToRemote(packet, pc, metadata) - return true - } - return false - } - if handle() { - packet.Drop() - return - } + proxy, rule, err := resolveMetadata(metadata) + if err != nil { + log.Warnln("[UDP] Parse metadata failed: %s", err.Error()) + return nil, nil, err + } - cond, loaded := natTable.GetOrCreateLock(key) + ctx, cancel := context.WithTimeout(context.Background(), C.DefaultUDPTimeout) + defer cancel() + rawPc, err := retry(ctx, func(ctx context.Context) (C.PacketConn, error) { + return proxy.ListenPacketContext(ctx, metadata.Pure()) + }, func(err error) { + logMetadataErr(metadata, rule, proxy, err) + }) + if err != nil { + return nil, nil, err + } + logMetadata(metadata, rule, rawPc) - go func() { - defer packet.Drop() + pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule, 0, 0, true) - if loaded { - cond.L.Lock() - cond.Wait() - handle() - cond.L.Unlock() - return + if rawPc.Chains().Last() == "REJECT-DROP" { + _ = pc.Close() + return nil, nil, errors.New("rejected drop packet") + } + + oAddrPort := metadata.AddrPort() + writeBackProxy := nat.NewWriteBackProxy(packet) + + go handleUDPToLocal(writeBackProxy, pc, sender, key, oAddrPort, fAddr) + return pc, writeBackProxy, nil } - defer func() { - natTable.DeleteLock(key) - cond.Broadcast() + go func() { + pc, proxy, err := dial() + if err != nil { + sender.Close() + natTable.Delete(key) + return + } + sender.Process(pc, proxy) }() - - proxy, rule, err := resolveMetadata(metadata) - if err != nil { - log.Warnln("[UDP] Parse metadata failed: %s", err.Error()) - return - } - - ctx, cancel := context.WithTimeout(context.Background(), C.DefaultUDPTimeout) - defer cancel() - rawPc, err := retry(ctx, func(ctx context.Context) (C.PacketConn, error) { - return proxy.ListenPacketContext(ctx, metadata.Pure()) - }, func(err error) { - logMetadataErr(metadata, rule, proxy, err) - }) - if err != nil { - return - } - logMetadata(metadata, rule, rawPc) - - pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule, 0, 0, true) - - if rawPc.Chains().Last() == "REJECT-DROP" { - pc.Close() - return - } - - oAddrPort := metadata.AddrPort() - writeBackProxy := nat.NewWriteBackProxy(packet) - natTable.Set(key, pc, writeBackProxy) - - go handleUDPToLocal(writeBackProxy, pc, key, oAddrPort, fAddr) - - handle() - }() + } + sender.Send(packet) // nonblocking } func handleTCPConn(connCtx C.ConnContext) { From 43cb48231ac5adc5404868ff61d044aeb34e0608 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 26 Sep 2024 22:21:59 +0800 Subject: [PATCH 09/44] cache: add dns cache in udp packet sender reduce the cost of re-resolving DNS for each packet received and prevent the target IP from jumping between multiple resolution results --- constant/adapters.go | 4 ++++ tunnel/connection.go | 31 ++++++++++++++++++++++++++----- tunnel/tunnel.go | 14 +------------- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/constant/adapters.go b/constant/adapters.go index 3731cd60..cb47f871 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -298,7 +298,11 @@ type PacketSender interface { // Send will send PacketAdapter nonblocking // the implement must call UDPPacket.Drop() inside Send Send(PacketAdapter) + // Process is a blocking loop to send PacketAdapter to PacketConn and update the WriteBackProxy Process(PacketConn, WriteBackProxy) + // ResolveUDP do a local resolve UDP dns blocking if metadata is not resolved + ResolveUDP(*Metadata) error + // Close stop the Process loop Close() } diff --git a/tunnel/connection.go b/tunnel/connection.go index 17e4efd0..1ea0678c 100644 --- a/tunnel/connection.go +++ b/tunnel/connection.go @@ -7,7 +7,9 @@ import ( "net/netip" "time" + "github.com/metacubex/mihomo/common/lru" N "github.com/metacubex/mihomo/common/net" + "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" ) @@ -16,6 +18,7 @@ type packetSender struct { ctx context.Context cancel context.CancelFunc ch chan C.PacketAdapter + cache *lru.LruCache[string, netip.Addr] } // newPacketSender return a chan based C.PacketSender @@ -27,6 +30,7 @@ func newPacketSender() C.PacketSender { ctx: ctx, cancel: cancel, ch: ch, + cache: lru.New[string, netip.Addr](lru.WithSize[string, netip.Addr](senderCapacity)), } } @@ -39,7 +43,11 @@ func (s *packetSender) Process(pc C.PacketConn, proxy C.WriteBackProxy) { if proxy != nil { proxy.UpdateWriteBack(packet) } - _ = handleUDPToRemote(packet, pc, packet.Metadata()) + if err := s.ResolveUDP(packet.Metadata()); err != nil { + log.Warnln("[UDP] Resolve Ip error: %s", err) + } else { + _ = handleUDPToRemote(packet, pc, packet.Metadata()) + } packet.Drop() } } @@ -79,11 +87,24 @@ func (s *packetSender) Close() { s.dropAll() } -func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata) error { - if err := resolveUDP(metadata); err != nil { - return err - } +func (s *packetSender) ResolveUDP(metadata *C.Metadata) (err error) { + // local resolve UDP dns + if !metadata.Resolved() { + ip, ok := s.cache.Get(metadata.Host) + if !ok { + ip, err = resolver.ResolveIP(s.ctx, metadata.Host) + if err != nil { + return err + } + s.cache.Set(metadata.Host, ip) + } + metadata.DstIP = ip + } + return nil +} + +func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata) error { addr := metadata.UDPAddr() if addr == nil { return errors.New("udp addr invalid") diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index af16e4ae..5c136eb2 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -346,18 +346,6 @@ func resolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err erro return } -func resolveUDP(metadata *C.Metadata) error { - // local resolve UDP dns - if !metadata.Resolved() { - ip, err := resolver.ResolveIP(context.Background(), metadata.Host) - if err != nil { - return err - } - metadata.DstIP = ip - } - return nil -} - // processUDP starts a loop to handle udp packet func processUDP(queue chan C.PacketAdapter) { for conn := range queue { @@ -398,7 +386,7 @@ func handleUDPConn(packet C.PacketAdapter) { sender, loaded := natTable.GetOrCreate(key, newPacketSender) if !loaded { dial := func() (C.PacketConn, C.WriteBackProxy, error) { - if err := resolveUDP(metadata); err != nil { + if err := sender.ResolveUDP(metadata); err != nil { log.Warnln("[UDP] Resolve Ip error: %s", err) return nil, nil, err } From 88bfe7cffea00fee448ef0e6ebb2f373a7a23c2b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 27 Sep 2024 09:57:09 +0800 Subject: [PATCH 10/44] feat: add `external-controller-pipe` for windows maybe useful for electron and tauri client, node.js and rust still not support AF_UNIX on windows --- adapter/inbound/listen_notwindows.go | 14 +++++++++++ adapter/inbound/listen_windows.go | 27 +++++++++++++++++++++ config/config.go | 3 +++ docs/config.yaml | 4 ++++ hub/hub.go | 7 ++++++ hub/route/server.go | 35 ++++++++++++++++++++++++++++ main.go | 17 +++++--------- 7 files changed, 96 insertions(+), 11 deletions(-) create mode 100644 adapter/inbound/listen_notwindows.go create mode 100644 adapter/inbound/listen_windows.go diff --git a/adapter/inbound/listen_notwindows.go b/adapter/inbound/listen_notwindows.go new file mode 100644 index 00000000..8fdfb7b8 --- /dev/null +++ b/adapter/inbound/listen_notwindows.go @@ -0,0 +1,14 @@ +//go:build !windows + +package inbound + +import ( + "net" + "os" +) + +const SupportNamedPipe = false + +func ListenNamedPipe(path string) (net.Listener, error) { + return nil, os.ErrInvalid +} diff --git a/adapter/inbound/listen_windows.go b/adapter/inbound/listen_windows.go new file mode 100644 index 00000000..0dc8e8ca --- /dev/null +++ b/adapter/inbound/listen_windows.go @@ -0,0 +1,27 @@ +package inbound + +import ( + "net" + + "github.com/metacubex/wireguard-go/ipc/namedpipe" + "golang.org/x/sys/windows" +) + +const SupportNamedPipe = true + +// windowsSDDL is the Security Descriptor set on the namedpipe. +// It provides read/write access to all users and the local system. +const windowsSDDL = "D:PAI(A;OICI;GWGR;;;BU)(A;OICI;GWGR;;;SY)" + +func ListenNamedPipe(path string) (net.Listener, error) { + securityDescriptor, err := windows.SecurityDescriptorFromString(windowsSDDL) + if err != nil { + return nil, err + } + namedpipeLC := namedpipe.ListenConfig{ + SecurityDescriptor: securityDescriptor, + InputBufferSize: 256 * 1024, + OutputBufferSize: 256 * 1024, + } + return namedpipeLC.Listen(path) +} diff --git a/config/config.go b/config/config.go index 27cde1fb..9067d14f 100644 --- a/config/config.go +++ b/config/config.go @@ -103,6 +103,7 @@ type Controller struct { ExternalController string ExternalControllerTLS string ExternalControllerUnix string + ExternalControllerPipe string ExternalUI string ExternalDohServer string Secret string @@ -364,6 +365,7 @@ type RawConfig struct { LogLevel log.LogLevel `yaml:"log-level" json:"log-level"` IPv6 bool `yaml:"ipv6" json:"ipv6"` ExternalController string `yaml:"external-controller" json:"external-controller"` + ExternalControllerPipe string `yaml:"external-controller-pipe" json:"external-controller-pipe"` ExternalControllerUnix string `yaml:"external-controller-unix" json:"external-controller-unix"` ExternalControllerTLS string `yaml:"external-controller-tls" json:"external-controller-tls"` ExternalUI string `yaml:"external-ui" json:"external-ui"` @@ -769,6 +771,7 @@ func parseController(cfg *RawConfig) (*Controller, error) { ExternalController: cfg.ExternalController, ExternalUI: cfg.ExternalUI, Secret: cfg.Secret, + ExternalControllerPipe: cfg.ExternalControllerPipe, ExternalControllerUnix: cfg.ExternalControllerUnix, ExternalControllerTLS: cfg.ExternalControllerTLS, ExternalDohServer: cfg.ExternalDohServer, diff --git a/docs/config.yaml b/docs/config.yaml index b3515a20..9c480b3f 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -63,6 +63,10 @@ external-controller-tls: 0.0.0.0:9443 # RESTful API HTTPS 监听地址,需要 # 测试方法: curl -v --unix-socket "mihomo.sock" http://localhost/ external-controller-unix: mihomo.sock +# RESTful API Windows namedpipe 监听地址 +# !!!注意: 从Windows namedpipe访问api接口不会验证secret, 如果开启请自行保证安全问题 !!! +external-controller-pipe: \\.\pipe\mihomo + # tcp-concurrent: true # TCP 并发连接所有 IP, 将使用最快握手的 TCP # 配置 WEB UI 目录,使用 http://{{external-controller}}/ui 访问 diff --git a/hub/hub.go b/hub/hub.go index e22f7219..73a44eee 100644 --- a/hub/hub.go +++ b/hub/hub.go @@ -27,6 +27,12 @@ func WithExternalControllerUnix(externalControllerUnix string) Option { } } +func WithExternalControllerPipe(externalControllerPipe string) Option { + return func(cfg *config.Config) { + cfg.Controller.ExternalControllerPipe = externalControllerPipe + } +} + func WithSecret(secret string) Option { return func(cfg *config.Config) { cfg.Controller.Secret = secret @@ -47,6 +53,7 @@ func applyRoute(cfg *config.Config) { Addr: cfg.Controller.ExternalController, TLSAddr: cfg.Controller.ExternalControllerTLS, UnixAddr: cfg.Controller.ExternalControllerUnix, + PipeAddr: cfg.Controller.ExternalControllerPipe, Secret: cfg.Controller.Secret, Certificate: cfg.TLS.Certificate, PrivateKey: cfg.TLS.PrivateKey, diff --git a/hub/route/server.go b/hub/route/server.go index b7077563..4c22609c 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -35,6 +35,7 @@ var ( httpServer *http.Server tlsServer *http.Server unixServer *http.Server + pipeServer *http.Server ) type Traffic struct { @@ -51,6 +52,7 @@ type Config struct { Addr string TLSAddr string UnixAddr string + PipeAddr string Secret string Certificate string PrivateKey string @@ -62,6 +64,9 @@ func ReCreateServer(cfg *Config) { go start(cfg) go startTLS(cfg) go startUnix(cfg) + if inbound.SupportNamedPipe { + go startPipe(cfg) + } } func SetUIPath(path string) { @@ -233,7 +238,37 @@ func startUnix(cfg *Config) { log.Errorln("External controller unix serve error: %s", err) } } +} +func startPipe(cfg *Config) { + // first stop existing server + if pipeServer != nil { + _ = pipeServer.Close() + pipeServer = nil + } + + // handle addr + if len(cfg.PipeAddr) > 0 { + if !strings.HasPrefix(cfg.PipeAddr, "\\\\.\\pipe\\") { // windows namedpipe must start with "\\.\pipe\" + log.Errorln("External controller pipe listen error: windows namedpipe must start with \"\\\\.\\pipe\\\"") + return + } + + l, err := inbound.ListenNamedPipe(cfg.PipeAddr) + if err != nil { + log.Errorln("External controller pipe listen error: %s", err) + return + } + log.Infoln("RESTful API pipe listening at: %s", l.Addr().String()) + + server := &http.Server{ + Handler: router(cfg.IsDebug, "", cfg.DohServer), + } + pipeServer = server + if err = server.Serve(l); err != nil { + log.Errorln("External controller pipe serve error: %s", err) + } + } } func setPrivateNetworkAccess(next http.Handler) http.Handler { diff --git a/main.go b/main.go index 8910a006..505cdb25 100644 --- a/main.go +++ b/main.go @@ -35,6 +35,7 @@ var ( externalUI string externalController string externalControllerUnix string + externalControllerPipe string secret string ) @@ -45,6 +46,7 @@ func init() { flag.StringVar(&externalUI, "ext-ui", os.Getenv("CLASH_OVERRIDE_EXTERNAL_UI_DIR"), "override external ui directory") flag.StringVar(&externalController, "ext-ctl", os.Getenv("CLASH_OVERRIDE_EXTERNAL_CONTROLLER"), "override external controller address") flag.StringVar(&externalControllerUnix, "ext-ctl-unix", os.Getenv("CLASH_OVERRIDE_EXTERNAL_CONTROLLER_UNIX"), "override external controller unix address") + flag.StringVar(&externalControllerPipe, "ext-ctl-pipe", os.Getenv("CLASH_OVERRIDE_EXTERNAL_CONTROLLER_PIPE"), "override external controller pipe address") flag.StringVar(&secret, "secret", os.Getenv("CLASH_OVERRIDE_SECRET"), "override secret for RESTful API") flag.BoolVar(&geodataMode, "m", false, "set geodata mode") flag.BoolVar(&version, "v", false, "show current version of mihomo") @@ -133,6 +135,9 @@ func main() { if externalControllerUnix != "" { options = append(options, hub.WithExternalControllerUnix(externalControllerUnix)) } + if externalControllerPipe != "" { + options = append(options, hub.WithExternalControllerPipe(externalControllerPipe)) + } if secret != "" { options = append(options, hub.WithSecret(secret)) } @@ -156,19 +161,9 @@ func main() { case <-termSign: return case <-hupSign: - var cfg *config.Config - var err error - if configString != "" { - cfg, err = executor.ParseWithBytes(configBytes) - } else { - cfg, err = executor.ParseWithPath(C.Path.Config()) - } - if err == nil { - hub.ApplyConfig(cfg) - } else { + if err := hub.Parse(configBytes, options...); err != nil { log.Errorln("Parse config error: %s", err.Error()) } - } } } From cd2d1c6bb0e88c8833fed7e335f3874f78ced4d6 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 27 Sep 2024 18:10:05 +0800 Subject: [PATCH 11/44] fix: `skip-auth-prefixes` not apply on listeners when `users` is unset --- component/auth/auth.go | 5 +++++ hub/executor/executor.go | 4 ++-- listener/auth/auth.go | 30 +++++++++++++++++++++++------- listener/http/proxy.go | 4 ++-- listener/http/server.go | 18 +++++++++--------- listener/inbound/auth.go | 6 +++--- listener/inbound/http.go | 2 +- listener/inbound/mixed.go | 2 +- listener/inbound/socks.go | 2 +- listener/mixed/mixed.go | 20 ++++++++++---------- listener/socks/tcp.go | 26 +++++++++++++------------- 11 files changed, 70 insertions(+), 49 deletions(-) diff --git a/component/auth/auth.go b/component/auth/auth.go index b52fa135..176b21d7 100644 --- a/component/auth/auth.go +++ b/component/auth/auth.go @@ -5,6 +5,11 @@ type Authenticator interface { Users() []string } +type AuthStore interface { + Authenticator() Authenticator + SetAuthenticator(Authenticator) +} + type AuthUser struct { User string Pass string diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 66bbc89b..39bf28d2 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -127,7 +127,7 @@ func initInnerTcp() { func GetGeneral() *config.General { ports := listener.GetPorts() var authenticator []string - if auth := authStore.Authenticator(); auth != nil { + if auth := authStore.Default.Authenticator(); auth != nil { authenticator = auth.Users() } @@ -422,7 +422,7 @@ func updateGeneral(general *config.General) { func updateUsers(users []auth.AuthUser) { authenticator := auth.NewAuthenticator(users) - authStore.SetAuthenticator(authenticator) + authStore.Default.SetAuthenticator(authenticator) if authenticator != nil { log.Infoln("Authentication of local server updated") } diff --git a/listener/auth/auth.go b/listener/auth/auth.go index 772be3bd..9e7632e8 100644 --- a/listener/auth/auth.go +++ b/listener/auth/auth.go @@ -4,14 +4,30 @@ import ( "github.com/metacubex/mihomo/component/auth" ) -var authenticator auth.Authenticator - -func Authenticator() auth.Authenticator { - return authenticator +type authStore struct { + authenticator auth.Authenticator } -func SetAuthenticator(au auth.Authenticator) { - authenticator = au +func (a *authStore) Authenticator() auth.Authenticator { + return a.authenticator } -func Nil() auth.Authenticator { return nil } +func (a *authStore) SetAuthenticator(authenticator auth.Authenticator) { + a.authenticator = authenticator +} + +func NewAuthStore(authenticator auth.Authenticator) auth.AuthStore { + return &authStore{authenticator} +} + +var Default auth.AuthStore = NewAuthStore(nil) + +type nilAuthStore struct{} + +func (a *nilAuthStore) Authenticator() auth.Authenticator { + return nil +} + +func (a *nilAuthStore) SetAuthenticator(authenticator auth.Authenticator) {} + +var Nil auth.AuthStore = (*nilAuthStore)(nil) // always return nil, even call SetAuthenticator() with a non-nil authenticator diff --git a/listener/http/proxy.go b/listener/http/proxy.go index 04ab98eb..5c08cd45 100644 --- a/listener/http/proxy.go +++ b/listener/http/proxy.go @@ -30,7 +30,7 @@ func (b *bodyWrapper) Read(p []byte) (n int, err error) { return n, err } -func HandleConn(c net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) { +func HandleConn(c net.Conn, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) { additions = append(additions, inbound.Placeholder) // Add a placeholder for InUser inUserIdx := len(additions) - 1 client := newClient(c, tunnel, additions) @@ -41,7 +41,7 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticator, conn := N.NewBufferedConn(c) - authenticator := getAuth() + authenticator := store.Authenticator() keepAlive := true trusted := authenticator == nil // disable authenticate if lru is nil lastUser := "" diff --git a/listener/http/server.go b/listener/http/server.go index 04f32f4f..24f07e8b 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -32,20 +32,20 @@ func (l *Listener) Close() error { } func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) { - return NewWithAuthenticator(addr, tunnel, authStore.Authenticator, additions...) + return NewWithAuthenticator(addr, tunnel, authStore.Default, additions...) } // NewWithAuthenticate // never change type traits because it's used in CMFA func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additions ...inbound.Addition) (*Listener, error) { - getAuth := authStore.Authenticator + store := authStore.Default if !authenticate { - getAuth = authStore.Nil + store = authStore.Default } - return NewWithAuthenticator(addr, tunnel, getAuth, additions...) + return NewWithAuthenticator(addr, tunnel, store, additions...) } -func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) (*Listener, error) { +func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) (*Listener, error) { isDefault := false if len(additions) == 0 { isDefault = true @@ -74,17 +74,17 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Auth continue } - getAuth := getAuth - if isDefault { // only apply on default listener + store := store + if isDefault || store == authStore.Default { // only apply on default listener if !inbound.IsRemoteAddrDisAllowed(conn.RemoteAddr()) { _ = conn.Close() continue } if inbound.SkipAuthRemoteAddr(conn.RemoteAddr()) { - getAuth = authStore.Nil + store = authStore.Nil } } - go HandleConn(conn, tunnel, getAuth, additions...) + go HandleConn(conn, tunnel, store, additions...) } }() diff --git a/listener/inbound/auth.go b/listener/inbound/auth.go index 41f18fc0..85e72494 100644 --- a/listener/inbound/auth.go +++ b/listener/inbound/auth.go @@ -12,7 +12,7 @@ type AuthUser struct { type AuthUsers []AuthUser -func (a AuthUsers) GetAuth() func() auth.Authenticator { +func (a AuthUsers) GetAuthStore() auth.AuthStore { if a != nil { // structure's Decode will ensure value not nil when input has value even it was set an empty array if len(a) == 0 { return authStore.Nil @@ -25,7 +25,7 @@ func (a AuthUsers) GetAuth() func() auth.Authenticator { } } authenticator := auth.NewAuthenticator(users) - return func() auth.Authenticator { return authenticator } + return authStore.NewAuthStore(authenticator) } - return authStore.Authenticator + return authStore.Default } diff --git a/listener/inbound/http.go b/listener/inbound/http.go index c78abefd..e20a9a23 100644 --- a/listener/inbound/http.go +++ b/listener/inbound/http.go @@ -45,7 +45,7 @@ func (h *HTTP) Address() string { // Listen implements constant.InboundListener func (h *HTTP) Listen(tunnel C.Tunnel) error { var err error - h.l, err = http.NewWithAuthenticator(h.RawAddress(), tunnel, h.config.Users.GetAuth(), h.Additions()...) + h.l, err = http.NewWithAuthenticator(h.RawAddress(), tunnel, h.config.Users.GetAuthStore(), h.Additions()...) if err != nil { return err } diff --git a/listener/inbound/mixed.go b/listener/inbound/mixed.go index 443a2564..1d79929a 100644 --- a/listener/inbound/mixed.go +++ b/listener/inbound/mixed.go @@ -53,7 +53,7 @@ func (m *Mixed) Address() string { // Listen implements constant.InboundListener func (m *Mixed) Listen(tunnel C.Tunnel) error { var err error - m.l, err = mixed.NewWithAuthenticator(m.RawAddress(), tunnel, m.config.Users.GetAuth(), m.Additions()...) + m.l, err = mixed.NewWithAuthenticator(m.RawAddress(), tunnel, m.config.Users.GetAuthStore(), m.Additions()...) if err != nil { return err } diff --git a/listener/inbound/socks.go b/listener/inbound/socks.go index cf6d1ce4..119eec82 100644 --- a/listener/inbound/socks.go +++ b/listener/inbound/socks.go @@ -71,7 +71,7 @@ func (s *Socks) Address() string { // Listen implements constant.InboundListener func (s *Socks) Listen(tunnel C.Tunnel) error { var err error - if s.stl, err = socks.NewWithAuthenticator(s.RawAddress(), tunnel, s.config.Users.GetAuth(), s.Additions()...); err != nil { + if s.stl, err = socks.NewWithAuthenticator(s.RawAddress(), tunnel, s.config.Users.GetAuthStore(), s.Additions()...); err != nil { return err } if s.udp { diff --git a/listener/mixed/mixed.go b/listener/mixed/mixed.go index ac3a0c58..5ac63011 100644 --- a/listener/mixed/mixed.go +++ b/listener/mixed/mixed.go @@ -37,10 +37,10 @@ func (l *Listener) Close() error { } func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) { - return NewWithAuthenticator(addr, tunnel, authStore.Authenticator, additions...) + return NewWithAuthenticator(addr, tunnel, authStore.Default, additions...) } -func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) (*Listener, error) { +func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) (*Listener, error) { isDefault := false if len(additions) == 0 { isDefault = true @@ -68,24 +68,24 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Auth } continue } - getAuth := getAuth - if isDefault { // only apply on default listener + store := store + if isDefault || store == authStore.Default { // only apply on default listener if !inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) { _ = c.Close() continue } if inbound.SkipAuthRemoteAddr(c.RemoteAddr()) { - getAuth = authStore.Nil + store = authStore.Nil } } - go handleConn(c, tunnel, getAuth, additions...) + go handleConn(c, tunnel, store, additions...) } }() return ml, nil } -func handleConn(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) { +func handleConn(conn net.Conn, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) { bufConn := N.NewBufferedConn(conn) head, err := bufConn.Peek(1) if err != nil { @@ -94,10 +94,10 @@ func handleConn(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticato switch head[0] { case socks4.Version: - socks.HandleSocks4(bufConn, tunnel, getAuth, additions...) + socks.HandleSocks4(bufConn, tunnel, store, additions...) case socks5.Version: - socks.HandleSocks5(bufConn, tunnel, getAuth, additions...) + socks.HandleSocks5(bufConn, tunnel, store, additions...) default: - http.HandleConn(bufConn, tunnel, getAuth, additions...) + http.HandleConn(bufConn, tunnel, store, additions...) } } diff --git a/listener/socks/tcp.go b/listener/socks/tcp.go index 950384c1..cc66613e 100644 --- a/listener/socks/tcp.go +++ b/listener/socks/tcp.go @@ -36,10 +36,10 @@ func (l *Listener) Close() error { } func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) { - return NewWithAuthenticator(addr, tunnel, authStore.Authenticator, additions...) + return NewWithAuthenticator(addr, tunnel, authStore.Default, additions...) } -func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) (*Listener, error) { +func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) (*Listener, error) { isDefault := false if len(additions) == 0 { isDefault = true @@ -67,24 +67,24 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, getAuth func() auth.Auth } continue } - getAuth := getAuth - if isDefault { // only apply on default listener + store := store + if isDefault || store == authStore.Default { // only apply on default listener if !inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) { _ = c.Close() continue } if inbound.SkipAuthRemoteAddr(c.RemoteAddr()) { - getAuth = authStore.Nil + store = authStore.Nil } } - go handleSocks(c, tunnel, getAuth, additions...) + go handleSocks(c, tunnel, store, additions...) } }() return sl, nil } -func handleSocks(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) { +func handleSocks(conn net.Conn, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) { bufConn := N.NewBufferedConn(conn) head, err := bufConn.Peek(1) if err != nil { @@ -94,16 +94,16 @@ func handleSocks(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticat switch head[0] { case socks4.Version: - HandleSocks4(bufConn, tunnel, getAuth, additions...) + HandleSocks4(bufConn, tunnel, store, additions...) case socks5.Version: - HandleSocks5(bufConn, tunnel, getAuth, additions...) + HandleSocks5(bufConn, tunnel, store, additions...) default: conn.Close() } } -func HandleSocks4(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) { - authenticator := getAuth() +func HandleSocks4(conn net.Conn, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) { + authenticator := store.Authenticator() addr, _, user, err := socks4.ServerHandshake(conn, authenticator) if err != nil { conn.Close() @@ -113,8 +113,8 @@ func HandleSocks4(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authentica tunnel.HandleTCPConn(inbound.NewSocket(socks5.ParseAddr(addr), conn, C.SOCKS4, additions...)) } -func HandleSocks5(conn net.Conn, tunnel C.Tunnel, getAuth func() auth.Authenticator, additions ...inbound.Addition) { - authenticator := getAuth() +func HandleSocks5(conn net.Conn, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) { + authenticator := store.Authenticator() target, command, user, err := socks5.ServerHandshake(conn, authenticator) if err != nil { conn.Close() From 2afa2798b176f079fe44ef659f11f21559c9bf91 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 27 Sep 2024 18:31:50 +0800 Subject: [PATCH 12/44] chore: allow set security descriptor of namedpipe by environment variable `LISTEN_NAMEDPIPE_SDDL` --- adapter/inbound/listen_windows.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/adapter/inbound/listen_windows.go b/adapter/inbound/listen_windows.go index 0dc8e8ca..d19239da 100644 --- a/adapter/inbound/listen_windows.go +++ b/adapter/inbound/listen_windows.go @@ -2,6 +2,7 @@ package inbound import ( "net" + "os" "github.com/metacubex/wireguard-go/ipc/namedpipe" "golang.org/x/sys/windows" @@ -14,7 +15,11 @@ const SupportNamedPipe = true const windowsSDDL = "D:PAI(A;OICI;GWGR;;;BU)(A;OICI;GWGR;;;SY)" func ListenNamedPipe(path string) (net.Listener, error) { - securityDescriptor, err := windows.SecurityDescriptorFromString(windowsSDDL) + sddl := os.Getenv("LISTEN_NAMEDPIPE_SDDL") + if sddl == "" { + sddl = windowsSDDL + } + securityDescriptor, err := windows.SecurityDescriptorFromString(sddl) if err != nil { return nil, err } From 1633885794954a55f8b0576203522553127a8ed8 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 27 Sep 2024 20:31:46 +0800 Subject: [PATCH 13/44] chore: update dependencies --- go.mod | 10 +++++----- go.sum | 22 +++++++++++----------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 22af95b3..b74cee92 100644 --- a/go.mod +++ b/go.mod @@ -49,12 +49,12 @@ require ( github.com/vmihailenco/msgpack/v5 v5.4.1 github.com/wk8/go-ordered-map/v2 v2.1.8 gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 - go.uber.org/automaxprocs v1.5.3 + go.uber.org/automaxprocs v1.6.0 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba - golang.org/x/crypto v0.26.0 + golang.org/x/crypto v0.27.0 golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa - golang.org/x/net v0.28.0 - golang.org/x/sys v0.24.0 + golang.org/x/net v0.29.0 + golang.org/x/sys v0.25.0 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.3.0 @@ -111,7 +111,7 @@ require ( go.uber.org/mock v0.4.0 // indirect golang.org/x/mod v0.20.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/text v0.18.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.24.0 // indirect ) diff --git a/go.sum b/go.sum index 9b824fc5..ba2d1819 100644 --- a/go.sum +++ b/go.sum @@ -222,16 +222,16 @@ gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 h1:UNrDfkQqiE gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7/go.mod h1:E+rxHvJG9H6PUdzq9NRG6csuLN3XUx98BfGOVWNYnXs= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ= -go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= -go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -240,8 +240,8 @@ golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -261,12 +261,12 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= From acfc9f8baacbdc0662a2a984a22d2e5512455481 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 27 Sep 2024 20:09:35 +0800 Subject: [PATCH 14/44] chore: reset resolver's connection after default interface changed --- adapter/outbound/wireguard.go | 2 +- component/resolver/resolver.go | 10 +++++ dns/client.go | 2 + dns/dhcp.go | 6 +++ dns/doh.go | 27 ++++++++++++-- dns/doq.go | 4 ++ dns/patch_android.go | 7 ++++ dns/rcode.go | 2 + dns/resolver.go | 68 +++++++++++++++++++--------------- dns/system_common.go | 2 + hub/executor/executor.go | 5 ++- listener/sing_tun/server.go | 5 +++ 12 files changed, 103 insertions(+), 37 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 6f5a18f3..3928ab1b 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -296,7 +296,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { for i := range nss { nss[i].ProxyAdapter = refP } - outbound.resolver = dns.NewResolver(dns.Config{ + outbound.resolver, _ = dns.NewResolver(dns.Config{ Main: nss, IPv6: has6, }) diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index feb3f98f..bcdbb7e2 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -47,6 +47,7 @@ type Resolver interface { ExchangeContext(ctx context.Context, m *dns.Msg) (msg *dns.Msg, err error) Invalid() bool ClearCache() + ResetConnection() } // LookupIPv4WithResolver same as LookupIPv4, but with a resolver @@ -256,6 +257,15 @@ func LookupIPProxyServerHost(ctx context.Context, host string) ([]netip.Addr, er return LookupIP(ctx, host) } +func ResetConnection() { + if DefaultResolver != nil { + go DefaultResolver.ResetConnection() + } + if ProxyServerHostResolver != nil { + go ProxyServerHostResolver.ResetConnection() + } +} + func SortationAddr(ips []netip.Addr) (ipv4s, ipv6s []netip.Addr) { for _, v := range ips { if v.Unmap().Is4() { diff --git a/dns/client.go b/dns/client.go index 096b96a7..62fc12f9 100644 --- a/dns/client.go +++ b/dns/client.go @@ -103,3 +103,5 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) return ret.msg, ret.err } } + +func (c *client) ResetConnection() {} diff --git a/dns/dhcp.go b/dns/dhcp.go index dc1344f5..e3829b7c 100644 --- a/dns/dhcp.go +++ b/dns/dhcp.go @@ -53,6 +53,12 @@ func (d *dhcpClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, return } +func (d *dhcpClient) ResetConnection() { + for _, client := range d.clients { + client.ResetConnection() + } +} + func (d *dhcpClient) resolve(ctx context.Context) ([]dnsClient, error) { d.lock.Lock() diff --git a/dns/doh.go b/dns/doh.go index ffb65fce..027afd58 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -203,11 +203,23 @@ func (doh *dnsOverHTTPS) Close() (err error) { return doh.closeClient(doh.client) } -// closeClient cleans up resources used by client if necessary. Note, that at -// this point it should only be done for HTTP/3 as it may leak due to keep-alive -// connections. +func (doh *dnsOverHTTPS) ResetConnection() { + doh.clientMu.Lock() + defer doh.clientMu.Unlock() + + if doh.client == nil { + return + } + + _ = doh.closeClient(doh.client) + doh.client = nil +} + +// closeClient cleans up resources used by client if necessary. func (doh *dnsOverHTTPS) closeClient(client *http.Client) (err error) { - if isHTTP3(client) { + client.CloseIdleConnections() + + if isHTTP3(client) { // HTTP/3 may leak due to keep-alive connections. return client.Transport.(io.Closer).Close() } @@ -508,6 +520,13 @@ func (h *http3Transport) Close() (err error) { return h.baseTransport.Close() } +func (h *http3Transport) CloseIdleConnections() { + h.mu.RLock() + defer h.mu.RUnlock() + + h.baseTransport.CloseIdleConnections() +} + // createTransportH3 tries to create an HTTP/3 transport for this upstream. // We should be able to fall back to H1/H2 in case if HTTP/3 is unavailable or // if it is too slow. In order to do that, this method will run two probes diff --git a/dns/doq.go b/dns/doq.go index ad936f95..29fdd006 100644 --- a/dns/doq.go +++ b/dns/doq.go @@ -144,6 +144,10 @@ func (doq *dnsOverQUIC) Close() (err error) { return err } +func (doq *dnsOverQUIC) ResetConnection() { + doq.closeConnWithError(nil) +} + // exchangeQUIC attempts to open a QUIC connection, send the DNS message // through it and return the response it got from the server. func (doq *dnsOverQUIC) exchangeQUIC(ctx context.Context, msg *D.Msg) (resp *D.Msg, err error) { diff --git a/dns/patch_android.go b/dns/patch_android.go index 6579ef07..e3dcd249 100644 --- a/dns/patch_android.go +++ b/dns/patch_android.go @@ -12,6 +12,7 @@ func FlushCacheWithDefaultResolver() { if r := resolver.DefaultResolver; r != nil { r.ClearCache() } + resolver.ResetConnection() } func UpdateSystemDNS(addr []string) { @@ -30,3 +31,9 @@ func UpdateSystemDNS(addr []string) { func (c *systemClient) getDnsClients() ([]dnsClient, error) { return systemResolver, nil } + +func (c *systemClient) ResetConnection() { + for _, r := range systemResolver { + r.ResetConnection() + } +} diff --git a/dns/rcode.go b/dns/rcode.go index 9777d2e7..901d1019 100644 --- a/dns/rcode.go +++ b/dns/rcode.go @@ -48,3 +48,5 @@ func (r rcodeClient) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, err func (r rcodeClient) Address() string { return r.addr } + +func (r rcodeClient) ResetConnection() {} diff --git a/dns/resolver.go b/dns/resolver.go index e03feef4..ec59f428 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -24,6 +24,7 @@ import ( type dnsClient interface { ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) Address() string + ResetConnection() } type dnsCache interface { @@ -48,7 +49,7 @@ type Resolver struct { group singleflight.Group[*D.Msg] cache dnsCache policy []dnsPolicy - proxyServer []dnsClient + defaultResolver *Resolver } func (r *Resolver) LookupIPPrimaryIPv4(ctx context.Context, host string) (ips []netip.Addr, err error) { @@ -376,6 +377,20 @@ func (r *Resolver) ClearCache() { } } +func (r *Resolver) ResetConnection() { + if r != nil { + for _, c := range r.main { + c.ResetConnection() + } + for _, c := range r.fallback { + c.ResetConnection() + } + if dr := r.defaultResolver; dr != nil { + dr.ResetConnection() + } + } +} + type NameServer struct { Net string Addr string @@ -425,16 +440,18 @@ type Config struct { CacheAlgorithm string } -func NewResolver(config Config) *Resolver { - var cache dnsCache - if config.CacheAlgorithm == "lru" { - cache = lru.New(lru.WithSize[string, *D.Msg](4096), lru.WithStale[string, *D.Msg](true)) +func (config Config) newCache() dnsCache { + if config.CacheAlgorithm == "" || config.CacheAlgorithm == "lru" { + return lru.New(lru.WithSize[string, *D.Msg](4096), lru.WithStale[string, *D.Msg](true)) } else { - cache = arc.New(arc.WithSize[string, *D.Msg](4096)) + return arc.New(arc.WithSize[string, *D.Msg](4096)) } +} + +func NewResolver(config Config) (r *Resolver, pr *Resolver) { defaultResolver := &Resolver{ main: transform(config.Default, nil), - cache: cache, + cache: config.newCache(), ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond, } @@ -465,27 +482,29 @@ func NewResolver(config Config) *Resolver { return } - if config.CacheAlgorithm == "" || config.CacheAlgorithm == "lru" { - cache = lru.New(lru.WithSize[string, *D.Msg](4096), lru.WithStale[string, *D.Msg](true)) - } else { - cache = arc.New(arc.WithSize[string, *D.Msg](4096)) - } - r := &Resolver{ + r = &Resolver{ ipv6: config.IPv6, main: cacheTransform(config.Main), - cache: cache, + cache: config.newCache(), hosts: config.Hosts, ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond, } + r.defaultResolver = defaultResolver + + if len(config.ProxyServer) != 0 { + pr = &Resolver{ + ipv6: config.IPv6, + main: cacheTransform(config.ProxyServer), + cache: config.newCache(), + hosts: config.Hosts, + ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond, + } + } if len(config.Fallback) != 0 { r.fallback = cacheTransform(config.Fallback) } - if len(config.ProxyServer) != 0 { - r.proxyServer = cacheTransform(config.ProxyServer) - } - if len(config.Policy) != 0 { r.policy = make([]dnsPolicy, 0) @@ -516,18 +535,7 @@ func NewResolver(config Config) *Resolver { r.fallbackIPFilters = config.FallbackIPFilter r.fallbackDomainFilters = config.FallbackDomainFilter - return r -} - -func NewProxyServerHostResolver(old *Resolver) *Resolver { - r := &Resolver{ - ipv6: old.ipv6, - main: old.proxyServer, - cache: old.cache, - hosts: old.hosts, - ipv6Timeout: old.ipv6Timeout, - } - return r + return } var ParseNameServer func(servers []string) ([]NameServer, error) // define in config/config.go diff --git a/dns/system_common.go b/dns/system_common.go index 06dc0b30..e6dabdcf 100644 --- a/dns/system_common.go +++ b/dns/system_common.go @@ -69,3 +69,5 @@ func (c *systemClient) getDnsClients() ([]dnsClient, error) { } return nil, err } + +func (c *systemClient) ResetConnection() {} diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 39bf28d2..66514e39 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -118,6 +118,8 @@ func ApplyConfig(cfg *config.Config, force bool) { tunnel.OnRunning() hcCompatibleProvider(cfg.Providers) initExternalUI() + + resolver.ResetConnection() } func initInnerTcp() { @@ -253,8 +255,7 @@ func updateDNS(c *config.DNS, generalIPv6 bool) { CacheAlgorithm: c.CacheAlgorithm, } - r := dns.NewResolver(cfg) - pr := dns.NewProxyServerHostResolver(r) + r, pr := dns.NewResolver(cfg) m := dns.NewEnhancer(cfg) // reuse cache of old host mapper diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index c2c668b3..79856c46 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -440,6 +440,10 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis //l.openAndroidHotspot(tunOptions) + if !l.options.AutoDetectInterface { + resolver.ResetConnection() + } + if options.FileDescriptor != 0 { tunName = fmt.Sprintf("%s(fd=%d)", tunName, options.FileDescriptor) } @@ -507,6 +511,7 @@ func (l *Listener) FlushDefaultInterface() { if old := dialer.DefaultInterface.Swap(autoDetectInterfaceName); old != autoDetectInterfaceName { log.Warnln("[TUN] default interface changed by monitor, %s => %s", old, autoDetectInterfaceName) iface.FlushCache() + resolver.ResetConnection() // reset resolver's connection after default interface changed } return } From af5ad3254b0ab8fef43c6870019f73761856f29f Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Fri, 27 Sep 2024 21:14:04 +0800 Subject: [PATCH 15/44] chore: Use DELETE to clear the proxy group fixed --- hub/route/proxies.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/hub/route/proxies.go b/hub/route/proxies.go index 69c8e446..9ff27c2d 100644 --- a/hub/route/proxies.go +++ b/hub/route/proxies.go @@ -31,6 +31,7 @@ func proxyRouter() http.Handler { r.Get("/", getProxy) r.Get("/delay", getProxyDelay) r.Put("/", updateProxy) + r.Delete("/", unfixedProxy) }) return r } @@ -146,3 +147,27 @@ func getProxyDelay(w http.ResponseWriter, r *http.Request) { "delay": delay, }) } + +func unfixedProxy(w http.ResponseWriter, r *http.Request) { + proxy := r.Context().Value(CtxKeyProxy).(C.Proxy) + switch proxy.(*adapter.Proxy).Type() { + case C.URLTest: + if urlTestGroup, ok := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.URLTest); ok { + urlTestGroup.ForceSet("") + } + case C.Fallback: + if fallbackGroup, ok := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.Fallback); ok { + fallbackGroup.ForceSet("") + } + default: + render.Status(r, http.StatusBadRequest) + render.JSON(w, r, ErrBadRequest) + return + } + + if proxy.(*adapter.Proxy).Type() != C.Selector { + cachefile.Cache().SetSelected(proxy.Name(), "") + } + + render.NoContent(w, r) +} From a67c3798844901a5206a7af3c2aa64efb757f55b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 27 Sep 2024 21:33:37 +0800 Subject: [PATCH 16/44] chore: code cleanup --- adapter/adapter.go | 5 +++++ adapter/outboundgroup/util.go | 4 ++++ constant/adapters.go | 1 + hub/executor/executor.go | 4 ++-- hub/route/groups.go | 21 +++++---------------- hub/route/proxies.go | 29 ++++++++--------------------- 6 files changed, 25 insertions(+), 39 deletions(-) diff --git a/adapter/adapter.go b/adapter/adapter.go index 8136827a..3efc8166 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -39,6 +39,11 @@ type Proxy struct { extra *xsync.MapOf[string, *internalProxyState] } +// Adapter implements C.Proxy +func (p *Proxy) Adapter() C.ProxyAdapter { + return p.ProxyAdapter +} + // AliveForTestUrl implements C.Proxy func (p *Proxy) AliveForTestUrl(url string) bool { if state, ok := p.extra.Load(url); ok { diff --git a/adapter/outboundgroup/util.go b/adapter/outboundgroup/util.go index 84216377..66b2510c 100644 --- a/adapter/outboundgroup/util.go +++ b/adapter/outboundgroup/util.go @@ -4,3 +4,7 @@ type SelectAble interface { Set(string) error ForceSet(name string) } + +var _ SelectAble = (*Fallback)(nil) +var _ SelectAble = (*URLTest)(nil) +var _ SelectAble = (*Selector)(nil) diff --git a/constant/adapters.go b/constant/adapters.go index cb47f871..b303eb84 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -158,6 +158,7 @@ type DelayHistoryStoreType int type Proxy interface { ProxyAdapter + Adapter() ProxyAdapter AliveForTestUrl(url string) bool DelayHistory() []DelayHistory ExtraDelayHistories() map[string]ProxyState diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 66514e39..214407b4 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -445,12 +445,12 @@ func patchSelectGroup(proxies map[string]C.Proxy) { } for name, proxy := range proxies { - outbound, ok := proxy.(*adapter.Proxy) + outbound, ok := proxy.(C.Proxy) if !ok { continue } - selector, ok := outbound.ProxyAdapter.(outboundgroup.SelectAble) + selector, ok := outbound.Adapter().(outboundgroup.SelectAble) if !ok { continue } diff --git a/hub/route/groups.go b/hub/route/groups.go index c4e9501f..68d1f354 100644 --- a/hub/route/groups.go +++ b/hub/route/groups.go @@ -9,7 +9,6 @@ import ( "github.com/go-chi/chi/v5" "github.com/go-chi/render" - "github.com/metacubex/mihomo/adapter" "github.com/metacubex/mihomo/adapter/outboundgroup" "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/profile/cachefile" @@ -32,7 +31,7 @@ func GroupRouter() http.Handler { func getGroups(w http.ResponseWriter, r *http.Request) { var gs []C.Proxy for _, p := range tunnel.Proxies() { - if _, ok := p.(*adapter.Proxy).ProxyAdapter.(C.Group); ok { + if _, ok := p.Adapter().(C.Group); ok { gs = append(gs, p) } } @@ -43,7 +42,7 @@ func getGroups(w http.ResponseWriter, r *http.Request) { func getGroup(w http.ResponseWriter, r *http.Request) { proxy := r.Context().Value(CtxKeyProxy).(C.Proxy) - if _, ok := proxy.(*adapter.Proxy).ProxyAdapter.(C.Group); ok { + if _, ok := proxy.Adapter().(C.Group); ok { render.JSON(w, r, proxy) return } @@ -53,25 +52,15 @@ func getGroup(w http.ResponseWriter, r *http.Request) { func getGroupDelay(w http.ResponseWriter, r *http.Request) { proxy := r.Context().Value(CtxKeyProxy).(C.Proxy) - group, ok := proxy.(*adapter.Proxy).ProxyAdapter.(C.Group) + group, ok := proxy.Adapter().(C.Group) if !ok { render.Status(r, http.StatusNotFound) render.JSON(w, r, ErrNotFound) return } - switch proxy.(*adapter.Proxy).Type() { - case C.URLTest: - if urlTestGroup, ok := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.URLTest); ok { - urlTestGroup.ForceSet("") - } - case C.Fallback: - if fallbackGroup, ok := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.Fallback); ok { - fallbackGroup.ForceSet("") - } - } - - if proxy.(*adapter.Proxy).Type() != C.Selector { + if selectAble, ok := proxy.Adapter().(outboundgroup.SelectAble); ok && proxy.Type() != C.Selector { + selectAble.ForceSet("") cachefile.Cache().SetSelected(proxy.Name(), "") } diff --git a/hub/route/proxies.go b/hub/route/proxies.go index 9ff27c2d..ba4e03f9 100644 --- a/hub/route/proxies.go +++ b/hub/route/proxies.go @@ -7,7 +7,6 @@ import ( "strconv" "time" - "github.com/metacubex/mihomo/adapter" "github.com/metacubex/mihomo/adapter/outboundgroup" "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/profile/cachefile" @@ -82,8 +81,8 @@ func updateProxy(w http.ResponseWriter, r *http.Request) { return } - proxy := r.Context().Value(CtxKeyProxy).(*adapter.Proxy) - selector, ok := proxy.ProxyAdapter.(outboundgroup.SelectAble) + proxy := r.Context().Value(CtxKeyProxy).(C.Proxy) + selector, ok := proxy.Adapter().(outboundgroup.SelectAble) if !ok { render.Status(r, http.StatusBadRequest) render.JSON(w, r, newError("Must be a Selector")) @@ -150,24 +149,12 @@ func getProxyDelay(w http.ResponseWriter, r *http.Request) { func unfixedProxy(w http.ResponseWriter, r *http.Request) { proxy := r.Context().Value(CtxKeyProxy).(C.Proxy) - switch proxy.(*adapter.Proxy).Type() { - case C.URLTest: - if urlTestGroup, ok := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.URLTest); ok { - urlTestGroup.ForceSet("") - } - case C.Fallback: - if fallbackGroup, ok := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.Fallback); ok { - fallbackGroup.ForceSet("") - } - default: - render.Status(r, http.StatusBadRequest) - render.JSON(w, r, ErrBadRequest) + if selectAble, ok := proxy.Adapter().(outboundgroup.SelectAble); ok && proxy.Type() != C.Selector { + selectAble.ForceSet("") + cachefile.Cache().SetSelected(proxy.Name(), "") + render.NoContent(w, r) return } - - if proxy.(*adapter.Proxy).Type() != C.Selector { - cachefile.Cache().SetSelected(proxy.Name(), "") - } - - render.NoContent(w, r) + render.Status(r, http.StatusBadRequest) + render.JSON(w, r, ErrBadRequest) } From 264713571d349d214ec938911de0de033b8673af Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 27 Sep 2024 22:36:19 +0800 Subject: [PATCH 17/44] chore: set 0o666 to unix socket file --- hub/route/server.go | 1 + 1 file changed, 1 insertion(+) diff --git a/hub/route/server.go b/hub/route/server.go index 4c22609c..1810a707 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -228,6 +228,7 @@ func startUnix(cfg *Config) { log.Errorln("External controller unix listen error: %s", err) return } + _ = os.Chmod(addr, 0o666) log.Infoln("RESTful API unix listening at: %s", l.Addr().String()) server := &http.Server{ From fc9d5cfee944a75b989d17c637a321e73c52093a Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 29 Sep 2024 17:13:43 +0800 Subject: [PATCH 18/44] feat: add `external-controller-cors` can config `allow-origins` and `allow-private-network` --- config/config.go | 20 ++++++++++++++++++++ docs/config.yaml | 6 ++++++ go.mod | 2 +- go.sum | 4 ++-- hub/hub.go | 4 ++++ hub/route/server.go | 46 ++++++++++++++++++++++----------------------- 6 files changed, 56 insertions(+), 26 deletions(-) diff --git a/config/config.go b/config/config.go index 9067d14f..3117853c 100644 --- a/config/config.go +++ b/config/config.go @@ -107,6 +107,12 @@ type Controller struct { ExternalUI string ExternalDohServer string Secret string + Cors Cors +} + +type Cors struct { + AllowOrigins []string + AllowPrivateNetwork bool } // Experimental config @@ -191,6 +197,11 @@ type Config struct { TLS *TLS } +type RawCors struct { + AllowOrigins []string `yaml:"allow-origins" json:"allow-origins"` + AllowPrivateNetwork bool `yaml:"allow-private-network" json:"allow-private-network"` +} + type RawDNS struct { Enable bool `yaml:"enable" json:"enable"` PreferH3 bool `yaml:"prefer-h3" json:"prefer-h3"` @@ -368,6 +379,7 @@ type RawConfig struct { ExternalControllerPipe string `yaml:"external-controller-pipe" json:"external-controller-pipe"` ExternalControllerUnix string `yaml:"external-controller-unix" json:"external-controller-unix"` ExternalControllerTLS string `yaml:"external-controller-tls" json:"external-controller-tls"` + ExternalControllerCors RawCors `yaml:"external-controller-cors" json:"external-controller-cors"` ExternalUI string `yaml:"external-ui" json:"external-ui"` ExternalUIURL string `yaml:"external-ui-url" json:"external-ui-url"` ExternalUIName string `yaml:"external-ui-name" json:"external-ui-name"` @@ -541,6 +553,10 @@ func DefaultRawConfig() *RawConfig { OverrideDest: true, }, ExternalUIURL: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip", + ExternalControllerCors: RawCors{ + AllowOrigins: []string{"*"}, + AllowPrivateNetwork: true, + }, } } @@ -775,6 +791,10 @@ func parseController(cfg *RawConfig) (*Controller, error) { ExternalControllerUnix: cfg.ExternalControllerUnix, ExternalControllerTLS: cfg.ExternalControllerTLS, ExternalDohServer: cfg.ExternalDohServer, + Cors: Cors{ + AllowOrigins: cfg.ExternalControllerCors.AllowOrigins, + AllowPrivateNetwork: cfg.ExternalControllerCors.AllowPrivateNetwork, + }, }, nil } diff --git a/docs/config.yaml b/docs/config.yaml index 9c480b3f..15bcf607 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -58,6 +58,12 @@ external-controller: 0.0.0.0:9093 # RESTful API 监听地址 external-controller-tls: 0.0.0.0:9443 # RESTful API HTTPS 监听地址,需要配置 tls 部分配置文件 # secret: "123456" # `Authorization:Bearer ${secret}` +# RESTful API CORS标头配置 +external-controller-cors: + allow-origins: + - * + allow-private-network: true + # RESTful API Unix socket 监听地址( windows版本大于17063也可以使用,即大于等于1803/RS4版本即可使用 ) # !!!注意: 从Unix socket访问api接口不会验证secret, 如果开启请自行保证安全问题 !!! # 测试方法: curl -v --unix-socket "mihomo.sock" http://localhost/ diff --git a/go.mod b/go.mod index b74cee92..4b066c90 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/coreos/go-iptables v0.7.0 github.com/dlclark/regexp2 v1.11.4 github.com/go-chi/chi/v5 v5.1.0 - github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.3 github.com/gobwas/ws v1.4.0 github.com/gofrs/uuid/v5 v5.3.0 @@ -37,6 +36,7 @@ require ( github.com/openacid/low v0.1.21 github.com/oschwald/maxminddb-golang v1.12.0 github.com/puzpuzpuz/xsync/v3 v3.4.0 + github.com/sagernet/cors v1.2.1 github.com/sagernet/fswatch v0.1.1 github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a github.com/sagernet/sing v0.5.0-alpha.13 diff --git a/go.sum b/go.sum index ba2d1819..60818034 100644 --- a/go.sum +++ b/go.sum @@ -42,8 +42,6 @@ github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXb github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI= github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= -github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= @@ -160,6 +158,8 @@ github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ= +github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI= github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs= github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o= github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis= diff --git a/hub/hub.go b/hub/hub.go index 73a44eee..69f627ff 100644 --- a/hub/hub.go +++ b/hub/hub.go @@ -59,6 +59,10 @@ func applyRoute(cfg *config.Config) { PrivateKey: cfg.TLS.PrivateKey, DohServer: cfg.Controller.ExternalDohServer, IsDebug: cfg.General.LogLevel == log.DEBUG, + Cors: route.Cors{ + AllowOrigins: cfg.Controller.Cors.AllowOrigins, + AllowPrivateNetwork: cfg.Controller.Cors.AllowPrivateNetwork, + }, }) } diff --git a/hub/route/server.go b/hub/route/server.go index 1810a707..e71347fc 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -23,10 +23,10 @@ import ( "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" - "github.com/go-chi/cors" "github.com/go-chi/render" "github.com/gobwas/ws" "github.com/gobwas/ws/wsutil" + "github.com/sagernet/cors" ) var ( @@ -58,6 +58,22 @@ type Config struct { PrivateKey string DohServer string IsDebug bool + Cors Cors +} + +type Cors struct { + AllowOrigins []string + AllowPrivateNetwork bool +} + +func (c Cors) Apply(r chi.Router) { + r.Use(cors.New(cors.Options{ + AllowedOrigins: c.AllowOrigins, + AllowedMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE"}, + AllowedHeaders: []string{"Content-Type", "Authorization"}, + AllowPrivateNetwork: c.AllowPrivateNetwork, + MaxAge: 300, + }).Handler) } func ReCreateServer(cfg *Config) { @@ -73,16 +89,9 @@ func SetUIPath(path string) { uiPath = C.Path.Resolve(path) } -func router(isDebug bool, secret string, dohServer string) *chi.Mux { +func router(isDebug bool, secret string, dohServer string, cors Cors) *chi.Mux { r := chi.NewRouter() - corsM := cors.New(cors.Options{ - AllowedOrigins: []string{"*"}, - AllowedMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE"}, - AllowedHeaders: []string{"Content-Type", "Authorization"}, - MaxAge: 300, - }) - r.Use(setPrivateNetworkAccess) - r.Use(corsM.Handler) + cors.Apply(r) if isDebug { r.Mount("/debug", func() http.Handler { r := chi.NewRouter() @@ -151,7 +160,7 @@ func start(cfg *Config) { log.Infoln("RESTful API listening at: %s", l.Addr().String()) server := &http.Server{ - Handler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer), + Handler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer, cfg.Cors), } httpServer = server if err = server.Serve(l); err != nil { @@ -183,7 +192,7 @@ func startTLS(cfg *Config) { log.Infoln("RESTful API tls listening at: %s", l.Addr().String()) server := &http.Server{ - Handler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer), + Handler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer, cfg.Cors), TLSConfig: &tls.Config{ Certificates: []tls.Certificate{c}, }, @@ -232,7 +241,7 @@ func startUnix(cfg *Config) { log.Infoln("RESTful API unix listening at: %s", l.Addr().String()) server := &http.Server{ - Handler: router(cfg.IsDebug, "", cfg.DohServer), + Handler: router(cfg.IsDebug, "", cfg.DohServer, cfg.Cors), } unixServer = server if err = server.Serve(l); err != nil { @@ -263,7 +272,7 @@ func startPipe(cfg *Config) { log.Infoln("RESTful API pipe listening at: %s", l.Addr().String()) server := &http.Server{ - Handler: router(cfg.IsDebug, "", cfg.DohServer), + Handler: router(cfg.IsDebug, "", cfg.DohServer, cfg.Cors), } pipeServer = server if err = server.Serve(l); err != nil { @@ -272,15 +281,6 @@ func startPipe(cfg *Config) { } } -func setPrivateNetworkAccess(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" { - w.Header().Add("Access-Control-Allow-Private-Network", "true") - } - next.ServeHTTP(w, r) - }) -} - func safeEuqal(a, b string) bool { aBuf := utils.ImmutableBytesFromString(a) bBuf := utils.ImmutableBytesFromString(b) From a330fa1506f84841d0f80724d12fa4add5205328 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 30 Sep 2024 13:08:50 +0800 Subject: [PATCH 19/44] chore: disallow some restful api for CMFA --- hub/route/configs.go | 8 +++++--- hub/route/groups.go | 2 +- hub/route/patch_android.go | 7 +++++++ hub/route/server.go | 14 +++++++++++--- 4 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 hub/route/patch_android.go diff --git a/hub/route/configs.go b/hub/route/configs.go index d4bda2bf..b23a35a5 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -24,9 +24,11 @@ import ( func configRouter() http.Handler { r := chi.NewRouter() r.Get("/", getConfigs) - r.Put("/", updateConfigs) - r.Post("/geo", updateGeoDatabases) - r.Patch("/", patchConfigs) + if !embedMode { // disallow update/patch configs in embed mode + r.Put("/", updateConfigs) + r.Post("/geo", updateGeoDatabases) + r.Patch("/", patchConfigs) + } return r } diff --git a/hub/route/groups.go b/hub/route/groups.go index 68d1f354..873a94df 100644 --- a/hub/route/groups.go +++ b/hub/route/groups.go @@ -16,7 +16,7 @@ import ( "github.com/metacubex/mihomo/tunnel" ) -func GroupRouter() http.Handler { +func groupRouter() http.Handler { r := chi.NewRouter() r.Get("/", getGroups) diff --git a/hub/route/patch_android.go b/hub/route/patch_android.go new file mode 100644 index 00000000..5eba2796 --- /dev/null +++ b/hub/route/patch_android.go @@ -0,0 +1,7 @@ +//go:build android && cmfa + +package route + +func init() { + SetEmbedMode(true) // set embed mode default +} diff --git a/hub/route/server.go b/hub/route/server.go index e71347fc..21102cc3 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -36,8 +36,14 @@ var ( tlsServer *http.Server unixServer *http.Server pipeServer *http.Server + + embedMode = false ) +func SetEmbedMode(embed bool) { + embedMode = embed +} + type Traffic struct { Up int64 `json:"up"` Down int64 `json:"down"` @@ -114,15 +120,17 @@ func router(isDebug bool, secret string, dohServer string, cors Cors) *chi.Mux { r.Get("/version", version) r.Mount("/configs", configRouter()) r.Mount("/proxies", proxyRouter()) - r.Mount("/group", GroupRouter()) + r.Mount("/group", groupRouter()) r.Mount("/rules", ruleRouter()) r.Mount("/connections", connectionRouter()) r.Mount("/providers/proxies", proxyProviderRouter()) r.Mount("/providers/rules", ruleProviderRouter()) r.Mount("/cache", cacheRouter()) r.Mount("/dns", dnsRouter()) - r.Mount("/restart", restartRouter()) - r.Mount("/upgrade", upgradeRouter()) + if !embedMode { // disallow restart and upgrade in embed mode + r.Mount("/restart", restartRouter()) + r.Mount("/upgrade", upgradeRouter()) + } addExternalRouters(r) }) From ecd8facd81a1f53f7e3c74cfe6d81dd4655eb4fb Mon Sep 17 00:00:00 2001 From: Skyxim Date: Tue, 1 Oct 2024 03:14:37 +0000 Subject: [PATCH 20/44] chore: add warning for unified delay test when second failed --- adapter/adapter.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/adapter/adapter.go b/adapter/adapter.go index 3efc8166..6de7fb92 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -10,6 +10,7 @@ import ( "net/netip" "net/url" "strconv" + "strings" "time" "github.com/metacubex/mihomo/common/atomic" @@ -18,6 +19,7 @@ import ( "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/dialer" C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/log" "github.com/puzpuzpuz/xsync/v3" ) @@ -260,10 +262,18 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In if unifiedDelay { second := time.Now() - resp, err = client.Do(req) - if err == nil { + var ignoredErr error + var secondResp *http.Response + secondResp, ignoredErr = client.Do(req) + if ignoredErr == nil { + resp = secondResp _ = resp.Body.Close() start = second + } else { + if strings.HasPrefix(url, "http://") { + log.Errorln("%s failed to get the second response from %s: %v", p.Name(), url, ignoredErr) + log.Warnln("It is recommended to use HTTPS for provider.health-check.url and group.url to ensure better reliability. Due to some proxy providers hijacking test addresses and not being compatible with repeated HEAD requests, using HTTP may result in failed tests.") + } } } From 990de84391f1883242d01c81208a74db23e9e915 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 2 Oct 2024 14:34:38 +0800 Subject: [PATCH 21/44] chore: better atomic using --- tunnel/statistic/manager.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tunnel/statistic/manager.go b/tunnel/statistic/manager.go index 08747118..0b309299 100644 --- a/tunnel/statistic/manager.go +++ b/tunnel/statistic/manager.go @@ -114,10 +114,8 @@ func (m *Manager) handle() { ticker := time.NewTicker(time.Second) for range ticker.C { - m.uploadBlip.Store(m.uploadTemp.Load()) - m.uploadTemp.Store(0) - m.downloadBlip.Store(m.downloadTemp.Load()) - m.downloadTemp.Store(0) + m.uploadBlip.Store(m.uploadTemp.Swap(0)) + m.downloadBlip.Store(m.downloadTemp.Swap(0)) } } From 4a16d22398116a88a230071278be536491a8cdce Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 2 Oct 2024 14:44:52 +0800 Subject: [PATCH 22/44] chore: no longer used net.DefaultResolver when `dns` section is disabled, now is equally only "system://" --- adapter/outbound/hysteria.go | 8 ++++---- component/dialer/resolver.go | 29 ---------------------------- component/resolver/defaults.go | 12 ------------ component/resolver/host_windows.go | 19 ------------------ component/resolver/resolver.go | 31 ++++++------------------------ dns/patch_android.go | 6 ++++++ dns/resolver.go | 2 -- dns/system.go | 17 +++++++++++++++- hub/executor/executor.go | 1 - main.go | 8 ++++++++ 10 files changed, 40 insertions(+), 93 deletions(-) delete mode 100644 component/dialer/resolver.go delete mode 100644 component/resolver/defaults.go delete mode 100644 component/resolver/host_windows.go diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index ccab16c1..a7367b83 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -69,7 +69,7 @@ func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata func (h *Hysteria) genHdc(ctx context.Context, opts ...dialer.Option) utils.PacketDialer { return &hyDialerWithContext{ ctx: context.Background(), - hyDialer: func(network string) (net.PacketConn, error) { + hyDialer: func(network string, rAddr net.Addr) (net.PacketConn, error) { var err error var cDialer C.Dialer = dialer.NewDialer(h.Base.DialOptions(opts...)...) if len(h.option.DialerProxy) > 0 { @@ -78,7 +78,7 @@ func (h *Hysteria) genHdc(ctx context.Context, opts ...dialer.Option) utils.Pack return nil, err } } - rAddrPort, _ := netip.ParseAddrPort(h.Addr()) + rAddrPort, _ := netip.ParseAddrPort(rAddr.String()) return cDialer.ListenPacket(ctx, network, "", rAddrPort) }, remoteAddr: func(addr string) (net.Addr, error) { @@ -284,7 +284,7 @@ func (c *hyPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { } type hyDialerWithContext struct { - hyDialer func(network string) (net.PacketConn, error) + hyDialer func(network string, rAddr net.Addr) (net.PacketConn, error) ctx context.Context remoteAddr func(host string) (net.Addr, error) } @@ -294,7 +294,7 @@ func (h *hyDialerWithContext) ListenPacket(rAddr net.Addr) (net.PacketConn, erro if addrPort, err := netip.ParseAddrPort(rAddr.String()); err == nil { network = dialer.ParseNetwork(network, addrPort.Addr()) } - return h.hyDialer(network) + return h.hyDialer(network, rAddr) } func (h *hyDialerWithContext) Context() context.Context { diff --git a/component/dialer/resolver.go b/component/dialer/resolver.go deleted file mode 100644 index ea38a90e..00000000 --- a/component/dialer/resolver.go +++ /dev/null @@ -1,29 +0,0 @@ -package dialer - -import ( - "context" - "net" - "net/netip" -) - -func init() { - // We must use this DialContext to query DNS - // when using net default resolver. - net.DefaultResolver.PreferGo = true - net.DefaultResolver.Dial = resolverDialContext -} - -func resolverDialContext(ctx context.Context, network, address string) (net.Conn, error) { - d := &net.Dialer{} - - interfaceName := DefaultInterface.Load() - - if interfaceName != "" { - dstIP, err := netip.ParseAddr(address) - if err == nil { - _ = bindIfaceToDialer(interfaceName, d, network, dstIP) - } - } - - return d.DialContext(ctx, network, address) -} diff --git a/component/resolver/defaults.go b/component/resolver/defaults.go deleted file mode 100644 index 8a04bd17..00000000 --- a/component/resolver/defaults.go +++ /dev/null @@ -1,12 +0,0 @@ -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris - -package resolver - -import _ "unsafe" - -//go:linkname defaultNS net.defaultNS -var defaultNS []string - -func init() { - defaultNS = []string{"114.114.114.114:53", "8.8.8.8:53"} -} diff --git a/component/resolver/host_windows.go b/component/resolver/host_windows.go deleted file mode 100644 index 669f9547..00000000 --- a/component/resolver/host_windows.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build !go1.22 - -// a simple standard lib fix from: https://github.com/golang/go/commit/33d4a5105cf2b2d549922e909e9239a48b8cefcc - -package resolver - -import ( - "golang.org/x/sys/windows" - _ "unsafe" -) - -//go:linkname testHookHostsPath net.testHookHostsPath -var testHookHostsPath string - -func init() { - if dir, err := windows.GetSystemDirectory(); err == nil { - testHookHostsPath = dir + "/Drivers/etc/hosts" - } -} diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index bcdbb7e2..8b180c1e 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "net" "net/netip" "strings" "time" @@ -23,6 +22,9 @@ var ( // ProxyServerHostResolver resolve ip to proxies server host ProxyServerHostResolver Resolver + // SystemResolver always using system dns, and was init in dns module + SystemResolver Resolver + // DisableIPv6 means don't resolve ipv6 host // default value is true DisableIPv6 = true @@ -72,14 +74,7 @@ func LookupIPv4WithResolver(ctx context.Context, host string, r Resolver) ([]net return r.LookupIPv4(ctx, host) } - ipAddrs, err := net.DefaultResolver.LookupNetIP(ctx, "ip4", host) - if err != nil { - return nil, err - } else if len(ipAddrs) == 0 { - return nil, ErrIPNotFound - } - - return ipAddrs, nil + return SystemResolver.LookupIPv4(ctx, host) } // LookupIPv4 with a host, return ipv4 list @@ -128,14 +123,7 @@ func LookupIPv6WithResolver(ctx context.Context, host string, r Resolver) ([]net return r.LookupIPv6(ctx, host) } - ipAddrs, err := net.DefaultResolver.LookupNetIP(ctx, "ip6", host) - if err != nil { - return nil, err - } else if len(ipAddrs) == 0 { - return nil, ErrIPNotFound - } - - return ipAddrs, nil + return SystemResolver.LookupIPv6(ctx, host) } // LookupIPv6 with a host, return ipv6 list @@ -177,14 +165,7 @@ func LookupIPWithResolver(ctx context.Context, host string, r Resolver) ([]netip return []netip.Addr{ip}, nil } - ips, err := net.DefaultResolver.LookupNetIP(ctx, "ip", host) - if err != nil { - return nil, err - } else if len(ips) == 0 { - return nil, ErrIPNotFound - } - - return ips, nil + return SystemResolver.LookupIP(ctx, host) } // LookupIP with a host, return ip diff --git a/dns/patch_android.go b/dns/patch_android.go index e3dcd249..5a98e86c 100644 --- a/dns/patch_android.go +++ b/dns/patch_android.go @@ -12,6 +12,12 @@ func FlushCacheWithDefaultResolver() { if r := resolver.DefaultResolver; r != nil { r.ClearCache() } + if r := resolver.ProxyServerHostResolver; r != nil { + r.ClearCache() + } + if r := resolver.SystemResolver; r != nil { + r.ClearCache() + } resolver.ResetConnection() } diff --git a/dns/resolver.go b/dns/resolver.go index ec59f428..ea8888cc 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -13,7 +13,6 @@ import ( "github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/component/trie" C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/constant/provider" "github.com/metacubex/mihomo/log" D "github.com/miekg/dns" @@ -436,7 +435,6 @@ type Config struct { Pool *fakeip.Pool Hosts *trie.DomainTrie[resolver.HostValue] Policy []Policy - Tunnel provider.Tunnel CacheAlgorithm string } diff --git a/dns/system.go b/dns/system.go index 944f2824..f05cb0f3 100644 --- a/dns/system.go +++ b/dns/system.go @@ -7,6 +7,8 @@ import ( "sync" "time" + "github.com/metacubex/mihomo/component/resolver" + D "github.com/miekg/dns" ) @@ -24,12 +26,17 @@ type systemClient struct { mu sync.Mutex dnsClients map[string]*systemDnsClient lastFlush time.Time + defaultNS []dnsClient } func (c *systemClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { dnsClients, err := c.getDnsClients() if err != nil { - return + if len(c.defaultNS) > 0 { + dnsClients = c.defaultNS + } else { + return + } } msg, _, err = batchExchange(ctx, dnsClients, m) return @@ -52,3 +59,11 @@ func newSystemClient() *systemClient { dnsClients: map[string]*systemDnsClient{}, } } + +func init() { + r, _ := NewResolver(Config{}) + c := newSystemClient() + c.defaultNS = transform([]NameServer{{Addr: "114.114.114.114:53"}, {Addr: "8.8.8.8:53"}}, nil) + r.main = []dnsClient{c} + resolver.SystemResolver = r +} diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 214407b4..10ea21b0 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -251,7 +251,6 @@ func updateDNS(c *config.DNS, generalIPv6 bool) { Default: c.DefaultNameserver, Policy: c.NameServerPolicy, ProxyServer: c.ProxyServerNameserver, - Tunnel: tunnel.Tunnel, CacheAlgorithm: c.CacheAlgorithm, } diff --git a/main.go b/main.go index 505cdb25..685fc89f 100644 --- a/main.go +++ b/main.go @@ -1,9 +1,11 @@ package main import ( + "context" "encoding/base64" "flag" "fmt" + "net" "os" "os/signal" "path/filepath" @@ -55,6 +57,12 @@ func init() { } func main() { + // Defensive programming: panic when code mistakenly calls net.DefaultResolver + net.DefaultResolver.PreferGo = true + net.DefaultResolver.Dial = func(ctx context.Context, network, address string) (net.Conn, error) { + panic("should never be called") + } + _, _ = maxprocs.Set(maxprocs.Logger(func(string, ...any) {})) if len(os.Args) > 1 && os.Args[1] == "convert-ruleset" { From c63a851bba09856adfc60f2a856eedc56b500d62 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 4 Oct 2024 13:19:41 +0800 Subject: [PATCH 23/44] feat: add `direct-nameserver` and `direct-nameserver-follow-policy` in `dns` section --- adapter/outbound/direct.go | 4 +-- adapter/outbound/util.go | 10 +++---- adapter/outbound/wireguard.go | 4 +-- component/dialer/dialer.go | 22 +++++--------- component/resolver/resolver.go | 53 +++------------------------------- config/config.go | 47 ++++++++++++++++++------------ dns/patch_android.go | 3 -- dns/resolver.go | 45 +++++++++++++++++++++++++---- dns/system.go | 2 +- docs/config.yaml | 11 +++++-- hub/executor/executor.go | 22 ++++++++++---- 11 files changed, 114 insertions(+), 109 deletions(-) diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index 15f081f2..9ee237fa 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -32,7 +32,7 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ... return nil, err } } - opts = append(opts, dialer.WithResolver(resolver.DefaultResolver)) + opts = append(opts, dialer.WithResolver(resolver.DirectHostResolver)) c, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress(), d.Base.DialOptions(opts...)...) if err != nil { return nil, err @@ -49,7 +49,7 @@ func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, } // net.UDPConn.WriteTo only working with *net.UDPAddr, so we need a net.UDPAddr if !metadata.Resolved() { - ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, resolver.DefaultResolver) + ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, resolver.DirectHostResolver) if err != nil { return nil, errors.New("can't resolve ip") } diff --git a/adapter/outbound/util.go b/adapter/outbound/util.go index 2c85c7c8..9f0636a6 100644 --- a/adapter/outbound/util.go +++ b/adapter/outbound/util.go @@ -55,7 +55,7 @@ func resolveUDPAddr(ctx context.Context, network, address string) (*net.UDPAddr, return nil, err } - ip, err := resolver.ResolveProxyServerHost(ctx, host) + ip, err := resolver.ResolveIPWithResolver(ctx, host, resolver.ProxyServerHostResolver) if err != nil { return nil, err } @@ -71,12 +71,12 @@ func resolveUDPAddrWithPrefer(ctx context.Context, network, address string, pref var fallback netip.Addr switch prefer { case C.IPv4Only: - ip, err = resolver.ResolveIPv4ProxyServerHost(ctx, host) + ip, err = resolver.ResolveIPv4WithResolver(ctx, host, resolver.ProxyServerHostResolver) case C.IPv6Only: - ip, err = resolver.ResolveIPv6ProxyServerHost(ctx, host) + ip, err = resolver.ResolveIPv6WithResolver(ctx, host, resolver.ProxyServerHostResolver) case C.IPv6Prefer: var ips []netip.Addr - ips, err = resolver.LookupIPProxyServerHost(ctx, host) + ips, err = resolver.LookupIPWithResolver(ctx, host, resolver.ProxyServerHostResolver) if err == nil { for _, addr := range ips { if addr.Is6() { @@ -92,7 +92,7 @@ func resolveUDPAddrWithPrefer(ctx context.Context, network, address string, pref default: // C.IPv4Prefer, C.DualStack and other var ips []netip.Addr - ips, err = resolver.LookupIPProxyServerHost(ctx, host) + ips, err = resolver.LookupIPWithResolver(ctx, host, resolver.ProxyServerHostResolver) if err == nil { for _, addr := range ips { if addr.Is4() { diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 3928ab1b..03145c37 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -44,7 +44,7 @@ type WireGuard struct { device wireguardGoDevice tunDevice wireguard.Device dialer proxydialer.SingDialer - resolver *dns.Resolver + resolver resolver.Resolver refP *refProxyAdapter initOk atomic.Bool @@ -296,7 +296,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { for i := range nss { nss[i].ProxyAdapter = refP } - outbound.resolver, _ = dns.NewResolver(dns.Config{ + outbound.resolver = dns.NewResolver(dns.Config{ Main: nss, IPv6: has6, }) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 3dfd3159..4fd051ef 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -340,26 +340,18 @@ func parseAddr(ctx context.Context, network, address string, preferResolver reso return nil, "-1", err } + if preferResolver == nil { + preferResolver = resolver.ProxyServerHostResolver + } + var ips []netip.Addr switch network { case "tcp4", "udp4": - if preferResolver == nil { - ips, err = resolver.LookupIPv4ProxyServerHost(ctx, host) - } else { - ips, err = resolver.LookupIPv4WithResolver(ctx, host, preferResolver) - } + ips, err = resolver.LookupIPv4WithResolver(ctx, host, preferResolver) case "tcp6", "udp6": - if preferResolver == nil { - ips, err = resolver.LookupIPv6ProxyServerHost(ctx, host) - } else { - ips, err = resolver.LookupIPv6WithResolver(ctx, host, preferResolver) - } + ips, err = resolver.LookupIPv6WithResolver(ctx, host, preferResolver) default: - if preferResolver == nil { - ips, err = resolver.LookupIPProxyServerHost(ctx, host) - } else { - ips, err = resolver.LookupIPWithResolver(ctx, host, preferResolver) - } + ips, err = resolver.LookupIPWithResolver(ctx, host, preferResolver) } if err != nil { return nil, "-1", fmt.Errorf("dns resolve failed: %w", err) diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index 8b180c1e..1eb3d642 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -19,9 +19,12 @@ var ( // DefaultResolver aim to resolve ip DefaultResolver Resolver - // ProxyServerHostResolver resolve ip to proxies server host + // ProxyServerHostResolver resolve ip for proxies server host, only nil when DefaultResolver is nil ProxyServerHostResolver Resolver + // DirectHostResolver resolve ip for direct outbound host, only nil when DefaultResolver is nil + DirectHostResolver Resolver + // SystemResolver always using system dns, and was init in dns module SystemResolver Resolver @@ -193,58 +196,10 @@ func ResolveIP(ctx context.Context, host string) (netip.Addr, error) { return ResolveIPWithResolver(ctx, host, DefaultResolver) } -// ResolveIPv4ProxyServerHost proxies server host only -func ResolveIPv4ProxyServerHost(ctx context.Context, host string) (netip.Addr, error) { - if ProxyServerHostResolver != nil { - return ResolveIPv4WithResolver(ctx, host, ProxyServerHostResolver) - } - return ResolveIPv4(ctx, host) -} - -// ResolveIPv6ProxyServerHost proxies server host only -func ResolveIPv6ProxyServerHost(ctx context.Context, host string) (netip.Addr, error) { - if ProxyServerHostResolver != nil { - return ResolveIPv6WithResolver(ctx, host, ProxyServerHostResolver) - } - return ResolveIPv6(ctx, host) -} - -// ResolveProxyServerHost proxies server host only -func ResolveProxyServerHost(ctx context.Context, host string) (netip.Addr, error) { - if ProxyServerHostResolver != nil { - return ResolveIPWithResolver(ctx, host, ProxyServerHostResolver) - } - return ResolveIP(ctx, host) -} - -func LookupIPv6ProxyServerHost(ctx context.Context, host string) ([]netip.Addr, error) { - if ProxyServerHostResolver != nil { - return LookupIPv6WithResolver(ctx, host, ProxyServerHostResolver) - } - return LookupIPv6(ctx, host) -} - -func LookupIPv4ProxyServerHost(ctx context.Context, host string) ([]netip.Addr, error) { - if ProxyServerHostResolver != nil { - return LookupIPv4WithResolver(ctx, host, ProxyServerHostResolver) - } - return LookupIPv4(ctx, host) -} - -func LookupIPProxyServerHost(ctx context.Context, host string) ([]netip.Addr, error) { - if ProxyServerHostResolver != nil { - return LookupIPWithResolver(ctx, host, ProxyServerHostResolver) - } - return LookupIP(ctx, host) -} - func ResetConnection() { if DefaultResolver != nil { go DefaultResolver.ResetConnection() } - if ProxyServerHostResolver != nil { - go ProxyServerHostResolver.ResetConnection() - } } func SortationAddr(ips []netip.Addr) (ipv4s, ipv6s []netip.Addr) { diff --git a/config/config.go b/config/config.go index 3117853c..3ca57a45 100644 --- a/config/config.go +++ b/config/config.go @@ -160,6 +160,8 @@ type DNS struct { Hosts *trie.DomainTrie[resolver.HostValue] NameServerPolicy []dns.Policy ProxyServerNameserver []dns.NameServer + DirectNameServer []dns.NameServer + DirectFollowPolicy bool } // Profile config @@ -203,25 +205,27 @@ type RawCors struct { } type RawDNS struct { - Enable bool `yaml:"enable" json:"enable"` - PreferH3 bool `yaml:"prefer-h3" json:"prefer-h3"` - IPv6 bool `yaml:"ipv6" json:"ipv6"` - IPv6Timeout uint `yaml:"ipv6-timeout" json:"ipv6-timeout"` - UseHosts bool `yaml:"use-hosts" json:"use-hosts"` - UseSystemHosts bool `yaml:"use-system-hosts" json:"use-system-hosts"` - RespectRules bool `yaml:"respect-rules" json:"respect-rules"` - NameServer []string `yaml:"nameserver" json:"nameserver"` - Fallback []string `yaml:"fallback" json:"fallback"` - FallbackFilter RawFallbackFilter `yaml:"fallback-filter" json:"fallback-filter"` - Listen string `yaml:"listen" json:"listen"` - EnhancedMode C.DNSMode `yaml:"enhanced-mode" json:"enhanced-mode"` - FakeIPRange string `yaml:"fake-ip-range" json:"fake-ip-range"` - FakeIPFilter []string `yaml:"fake-ip-filter" json:"fake-ip-filter"` - FakeIPFilterMode C.FilterMode `yaml:"fake-ip-filter-mode" json:"fake-ip-filter-mode"` - DefaultNameserver []string `yaml:"default-nameserver" json:"default-nameserver"` - CacheAlgorithm string `yaml:"cache-algorithm" json:"cache-algorithm"` - NameServerPolicy *orderedmap.OrderedMap[string, any] `yaml:"nameserver-policy" json:"nameserver-policy"` - ProxyServerNameserver []string `yaml:"proxy-server-nameserver" json:"proxy-server-nameserver"` + Enable bool `yaml:"enable" json:"enable"` + PreferH3 bool `yaml:"prefer-h3" json:"prefer-h3"` + IPv6 bool `yaml:"ipv6" json:"ipv6"` + IPv6Timeout uint `yaml:"ipv6-timeout" json:"ipv6-timeout"` + UseHosts bool `yaml:"use-hosts" json:"use-hosts"` + UseSystemHosts bool `yaml:"use-system-hosts" json:"use-system-hosts"` + RespectRules bool `yaml:"respect-rules" json:"respect-rules"` + NameServer []string `yaml:"nameserver" json:"nameserver"` + Fallback []string `yaml:"fallback" json:"fallback"` + FallbackFilter RawFallbackFilter `yaml:"fallback-filter" json:"fallback-filter"` + Listen string `yaml:"listen" json:"listen"` + EnhancedMode C.DNSMode `yaml:"enhanced-mode" json:"enhanced-mode"` + FakeIPRange string `yaml:"fake-ip-range" json:"fake-ip-range"` + FakeIPFilter []string `yaml:"fake-ip-filter" json:"fake-ip-filter"` + FakeIPFilterMode C.FilterMode `yaml:"fake-ip-filter-mode" json:"fake-ip-filter-mode"` + DefaultNameserver []string `yaml:"default-nameserver" json:"default-nameserver"` + CacheAlgorithm string `yaml:"cache-algorithm" json:"cache-algorithm"` + NameServerPolicy *orderedmap.OrderedMap[string, any] `yaml:"nameserver-policy" json:"nameserver-policy"` + ProxyServerNameserver []string `yaml:"proxy-server-nameserver" json:"proxy-server-nameserver"` + DirectNameServer []string `yaml:"direct-nameserver" json:"direct-nameserver"` + DirectNameServerFollowPolicy bool `yaml:"direct-nameserver-follow-policy" json:"direct-nameserver-follow-policy"` } type RawFallbackFilter struct { @@ -1423,6 +1427,11 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul return nil, err } + if dnsCfg.DirectNameServer, err = parseNameServer(cfg.DirectNameServer, false, cfg.PreferH3); err != nil { + return nil, err + } + dnsCfg.DirectFollowPolicy = cfg.DirectNameServerFollowPolicy + if len(cfg.DefaultNameserver) == 0 { return nil, errors.New("default nameserver should have at least one nameserver") } diff --git a/dns/patch_android.go b/dns/patch_android.go index 5a98e86c..8e744fcd 100644 --- a/dns/patch_android.go +++ b/dns/patch_android.go @@ -12,9 +12,6 @@ func FlushCacheWithDefaultResolver() { if r := resolver.DefaultResolver; r != nil { r.ClearCache() } - if r := resolver.ProxyServerHostResolver; r != nil { - r.ClearCache() - } if r := resolver.SystemResolver; r != nil { r.ClearCache() } diff --git a/dns/resolver.go b/dns/resolver.go index ea8888cc..9f7e28f3 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -427,6 +427,8 @@ type Config struct { Main, Fallback []NameServer Default []NameServer ProxyServer []NameServer + DirectServer []NameServer + DirectFollowPolicy bool IPv6 bool IPv6Timeout uint EnhancedMode C.DNSMode @@ -446,7 +448,25 @@ func (config Config) newCache() dnsCache { } } -func NewResolver(config Config) (r *Resolver, pr *Resolver) { +type Resolvers struct { + *Resolver + ProxyResolver *Resolver + DirectResolver *Resolver +} + +func (rs Resolvers) ClearCache() { + rs.Resolver.ClearCache() + rs.ProxyResolver.ClearCache() + rs.DirectResolver.ClearCache() +} + +func (rs Resolvers) ResetConnection() { + rs.Resolver.ResetConnection() + rs.ProxyResolver.ResetConnection() + rs.DirectResolver.ResetConnection() +} + +func NewResolver(config Config) (rs Resolvers) { defaultResolver := &Resolver{ main: transform(config.Default, nil), cache: config.newCache(), @@ -480,7 +500,7 @@ func NewResolver(config Config) (r *Resolver, pr *Resolver) { return } - r = &Resolver{ + r := &Resolver{ ipv6: config.IPv6, main: cacheTransform(config.Main), cache: config.newCache(), @@ -488,9 +508,10 @@ func NewResolver(config Config) (r *Resolver, pr *Resolver) { ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond, } r.defaultResolver = defaultResolver + rs.Resolver = r if len(config.ProxyServer) != 0 { - pr = &Resolver{ + rs.ProxyResolver = &Resolver{ ipv6: config.IPv6, main: cacheTransform(config.ProxyServer), cache: config.newCache(), @@ -499,8 +520,20 @@ func NewResolver(config Config) (r *Resolver, pr *Resolver) { } } + if len(config.DirectServer) != 0 { + rs.DirectResolver = &Resolver{ + ipv6: config.IPv6, + main: cacheTransform(config.DirectServer), + cache: config.newCache(), + hosts: config.Hosts, + ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond, + } + } + if len(config.Fallback) != 0 { r.fallback = cacheTransform(config.Fallback) + r.fallbackIPFilters = config.FallbackIPFilter + r.fallbackDomainFilters = config.FallbackDomainFilter } if len(config.Policy) != 0 { @@ -529,9 +562,11 @@ func NewResolver(config Config) (r *Resolver, pr *Resolver) { } } insertPolicy(nil) + + if rs.DirectResolver != nil && config.DirectFollowPolicy { + rs.DirectResolver.policy = r.policy + } } - r.fallbackIPFilters = config.FallbackIPFilter - r.fallbackDomainFilters = config.FallbackDomainFilter return } diff --git a/dns/system.go b/dns/system.go index f05cb0f3..9fb803dd 100644 --- a/dns/system.go +++ b/dns/system.go @@ -61,7 +61,7 @@ func newSystemClient() *systemClient { } func init() { - r, _ := NewResolver(Config{}) + r := NewResolver(Config{}) c := newSystemClient() c.defaultNS = transform([]NameServer{{Addr: "114.114.114.114:53"}, {Addr: "8.8.8.8:53"}}, nil) r.main = []dnsClient{c} diff --git a/docs/config.yaml b/docs/config.yaml index 15bcf607..e75e5bd5 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -294,10 +294,15 @@ dns: # - tcp://1.1.1.1 # - 'tcp://1.1.1.1#ProxyGroupName' # 指定 DNS 过代理查询,ProxyGroupName 为策略组名或节点名,过代理配置优先于配置出口网卡,当找不到策略组或节点名则设置为出口网卡 - # 专用于节点域名解析的 DNS 服务器,非必要配置项 + # 专用于节点域名解析的 DNS 服务器,非必要配置项,如果不填则遵循nameserver-policy、nameserver和fallback的配置 # proxy-server-nameserver: - # - https://dns.google/dns-query - # - tls://one.one.one.one + # - https://dns.google/dns-query + # - tls://one.one.one.one + + # 专用于direct出口域名解析的 DNS 服务器,非必要配置项,如果不填则遵循nameserver-policy、nameserver和fallback的配置 + # direct-nameserver: + # - system:// + # direct-nameserver-follow-policy: false # 是否遵循nameserver-policy,默认为不遵守,仅当direct-nameserver不为空时生效 # 配置 fallback 使用条件 # fallback-filter: diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 10ea21b0..b8d9cddb 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -235,6 +235,8 @@ func updateDNS(c *config.DNS, generalIPv6 bool) { resolver.DefaultResolver = nil resolver.DefaultHostMapper = nil resolver.DefaultLocalServer = nil + resolver.ProxyServerHostResolver = nil + resolver.DirectHostResolver = nil dns.ReCreateServer("", nil, nil) return } @@ -251,10 +253,12 @@ func updateDNS(c *config.DNS, generalIPv6 bool) { Default: c.DefaultNameserver, Policy: c.NameServerPolicy, ProxyServer: c.ProxyServerNameserver, + DirectServer: c.DirectNameServer, + DirectFollowPolicy: c.DirectFollowPolicy, CacheAlgorithm: c.CacheAlgorithm, } - r, pr := dns.NewResolver(cfg) + r := dns.NewResolver(cfg) m := dns.NewEnhancer(cfg) // reuse cache of old host mapper @@ -264,14 +268,22 @@ func updateDNS(c *config.DNS, generalIPv6 bool) { resolver.DefaultResolver = r resolver.DefaultHostMapper = m - resolver.DefaultLocalServer = dns.NewLocalServer(r, m) + resolver.DefaultLocalServer = dns.NewLocalServer(r.Resolver, m) resolver.UseSystemHosts = c.UseSystemHosts - if pr.Invalid() { - resolver.ProxyServerHostResolver = pr + if r.ProxyResolver.Invalid() { + resolver.ProxyServerHostResolver = r.ProxyResolver + } else { + resolver.ProxyServerHostResolver = r.Resolver } - dns.ReCreateServer(c.Listen, r, m) + if r.DirectResolver.Invalid() { + resolver.DirectHostResolver = r.DirectResolver + } else { + resolver.DirectHostResolver = r.Resolver + } + + dns.ReCreateServer(c.Listen, r.Resolver, m) } func updateHosts(tree *trie.DomainTrie[resolver.HostValue]) { From 9286e21026d5343622dc0678166fbf075e910d79 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 5 Oct 2024 13:40:00 +0800 Subject: [PATCH 24/44] chore: rebuild external ui updater --- component/updater/update_ui.go | 81 ++++++++++++++++++++++++++-------- config/config.go | 32 +++----------- hub/executor/executor.go | 15 ++----- hub/route/upgrade.go | 2 +- 4 files changed, 74 insertions(+), 56 deletions(-) diff --git a/component/updater/update_ui.go b/component/updater/update_ui.go index 29081761..48d6536c 100644 --- a/component/updater/update_ui.go +++ b/component/updater/update_ui.go @@ -14,24 +14,69 @@ import ( "github.com/metacubex/mihomo/log" ) -var ( - ExternalUIURL string - ExternalUIPath string - AutoDownloadUI bool -) +type UIUpdater struct { + externalUIURL string + externalUIPath string + autoDownloadUI bool -var xdMutex sync.Mutex + mutex sync.Mutex +} -func DownloadUI() error { - xdMutex.Lock() - defer xdMutex.Unlock() +var DefaultUiUpdater = &UIUpdater{} - err := prepareUIPath() +func NewUiUpdater(externalUI, externalUIURL, externalUIName string) *UIUpdater { + updater := &UIUpdater{} + // checkout externalUI exist + if externalUI != "" { + updater.autoDownloadUI = true + updater.externalUIPath = C.Path.Resolve(externalUI) + } else { + // default externalUI path + updater.externalUIPath = path.Join(C.Path.HomeDir(), "ui") + } + + // checkout UIpath/name exist + if externalUIName != "" { + updater.autoDownloadUI = true + updater.externalUIPath = path.Join(updater.externalUIPath, externalUIName) + } + + if externalUIURL != "" { + updater.externalUIURL = externalUIURL + } + return updater +} + +func (u *UIUpdater) AutoDownloadUI() { + u.mutex.Lock() + defer u.mutex.Unlock() + if u.autoDownloadUI { + dirEntries, _ := os.ReadDir(u.externalUIPath) + if len(dirEntries) > 0 { + log.Infoln("UI already exists, skip downloading") + } else { + log.Infoln("External UI downloading ...") + err := u.downloadUI() + if err != nil { + log.Errorln("Error downloading UI:", err) + } + } + } +} + +func (u *UIUpdater) DownloadUI() error { + u.mutex.Lock() + defer u.mutex.Unlock() + return u.downloadUI() +} + +func (u *UIUpdater) downloadUI() error { + err := u.prepareUIPath() if err != nil { return fmt.Errorf("prepare UI path failed: %w", err) } - data, err := downloadForBytes(ExternalUIURL) + data, err := downloadForBytes(u.externalUIURL) if err != nil { return fmt.Errorf("can't download file: %w", err) } @@ -42,7 +87,7 @@ func DownloadUI() error { } defer os.Remove(saved) - err = cleanup(ExternalUIPath) + err = cleanup(u.externalUIPath) if err != nil { if !os.IsNotExist(err) { return fmt.Errorf("cleanup exist file error: %w", err) @@ -54,18 +99,18 @@ func DownloadUI() error { return fmt.Errorf("can't extract zip file: %w", err) } - err = os.Rename(unzipFolder, ExternalUIPath) + err = os.Rename(unzipFolder, u.externalUIPath) if err != nil { return fmt.Errorf("rename UI folder failed: %w", err) } return nil } -func prepareUIPath() error { - if _, err := os.Stat(ExternalUIPath); os.IsNotExist(err) { - log.Infoln("dir %s does not exist, creating", ExternalUIPath) - if err := os.MkdirAll(ExternalUIPath, os.ModePerm); err != nil { - log.Warnln("create dir %s error: %s", ExternalUIPath, err) +func (u *UIUpdater) prepareUIPath() error { + if _, err := os.Stat(u.externalUIPath); os.IsNotExist(err) { + log.Infoln("dir %s does not exist, creating", u.externalUIPath) + if err := os.MkdirAll(u.externalUIPath, os.ModePerm); err != nil { + log.Warnln("create dir %s error: %s", u.externalUIPath, err) } } return nil diff --git a/config/config.go b/config/config.go index 3ca57a45..c1d70e81 100644 --- a/config/config.go +++ b/config/config.go @@ -7,7 +7,6 @@ import ( "net" "net/netip" "net/url" - "path" "strings" "time" @@ -105,6 +104,8 @@ type Controller struct { ExternalControllerUnix string ExternalControllerPipe string ExternalUI string + ExternalUIURL string + ExternalUIName string ExternalDohServer string Secret string Cors Cors @@ -718,33 +719,10 @@ func parseGeneral(cfg *RawConfig) (*General, error) { mihomoHttp.SetUA(cfg.GlobalUA) resource.SetETag(cfg.ETagSupport) - if cfg.KeepAliveIdle != 0 { - keepalive.SetKeepAliveIdle(time.Duration(cfg.KeepAliveIdle) * time.Second) - } - if cfg.KeepAliveInterval != 0 { - keepalive.SetKeepAliveInterval(time.Duration(cfg.KeepAliveInterval) * time.Second) - } + keepalive.SetKeepAliveIdle(time.Duration(cfg.KeepAliveIdle) * time.Second) + keepalive.SetKeepAliveInterval(time.Duration(cfg.KeepAliveInterval) * time.Second) keepalive.SetDisableKeepAlive(cfg.DisableKeepAlive) - // checkout externalUI exist - if cfg.ExternalUI != "" { - updater.AutoDownloadUI = true - updater.ExternalUIPath = C.Path.Resolve(cfg.ExternalUI) - } else { - // default externalUI path - updater.ExternalUIPath = path.Join(C.Path.HomeDir(), "ui") - } - - // checkout UIpath/name exist - if cfg.ExternalUIName != "" { - updater.AutoDownloadUI = true - updater.ExternalUIPath = path.Join(updater.ExternalUIPath, cfg.ExternalUIName) - } - - if cfg.ExternalUIURL != "" { - updater.ExternalUIURL = cfg.ExternalUIURL - } - return &General{ Inbound: Inbound{ Port: cfg.Port, @@ -790,6 +768,8 @@ func parseController(cfg *RawConfig) (*Controller, error) { return &Controller{ ExternalController: cfg.ExternalController, ExternalUI: cfg.ExternalUI, + ExternalUIURL: cfg.ExternalUIURL, + ExternalUIName: cfg.ExternalUIName, Secret: cfg.Secret, ExternalControllerPipe: cfg.ExternalControllerPipe, ExternalControllerUnix: cfg.ExternalControllerUnix, diff --git a/hub/executor/executor.go b/hub/executor/executor.go index b8d9cddb..0492c430 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -117,7 +117,7 @@ func ApplyConfig(cfg *config.Config, force bool) { runtime.GC() tunnel.OnRunning() hcCompatibleProvider(cfg.Providers) - initExternalUI() + initExternalUI(cfg.Controller) resolver.ResetConnection() } @@ -394,16 +394,9 @@ func updateTunnels(tunnels []LC.Tunnel) { listener.PatchTunnel(tunnels, tunnel.Tunnel) } -func initExternalUI() { - if updater.AutoDownloadUI { - dirEntries, _ := os.ReadDir(updater.ExternalUIPath) - if len(dirEntries) > 0 { - log.Infoln("UI already exists, skip downloading") - } else { - log.Infoln("External UI downloading ...") - updater.DownloadUI() - } - } +func initExternalUI(controller *config.Controller) { + updater.DefaultUiUpdater = updater.NewUiUpdater(controller.ExternalUI, controller.ExternalUIURL, controller.ExternalUIName) + updater.DefaultUiUpdater.AutoDownloadUI() } func updateGeneral(general *config.General) { diff --git a/hub/route/upgrade.go b/hub/route/upgrade.go index 25c326dd..2fed3f67 100644 --- a/hub/route/upgrade.go +++ b/hub/route/upgrade.go @@ -47,7 +47,7 @@ func upgradeCore(w http.ResponseWriter, r *http.Request) { } func updateUI(w http.ResponseWriter, r *http.Request) { - err := updater.DownloadUI() + err := updater.DefaultUiUpdater.DownloadUI() if err != nil { log.Warnln("%s", err) render.Status(r, http.StatusInternalServerError) From 8f5a86410c9f1539573d9518e9e497369568fc95 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 5 Oct 2024 13:58:49 +0800 Subject: [PATCH 25/44] chore: cleanup unneeded setting in parseGeneral, move to executor --- config/config.go | 14 ++++++-------- hub/executor/executor.go | 17 +++++++++++++++-- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/config/config.go b/config/config.go index c1d70e81..69957cee 100644 --- a/config/config.go +++ b/config/config.go @@ -20,14 +20,12 @@ import ( "github.com/metacubex/mihomo/component/fakeip" "github.com/metacubex/mihomo/component/geodata" mihomoHttp "github.com/metacubex/mihomo/component/http" - "github.com/metacubex/mihomo/component/keepalive" P "github.com/metacubex/mihomo/component/process" "github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/component/resource" "github.com/metacubex/mihomo/component/sniffer" tlsC "github.com/metacubex/mihomo/component/tls" "github.com/metacubex/mihomo/component/trie" - "github.com/metacubex/mihomo/component/updater" C "github.com/metacubex/mihomo/constant" providerTypes "github.com/metacubex/mihomo/constant/provider" snifferTypes "github.com/metacubex/mihomo/constant/sniffer" @@ -66,6 +64,9 @@ type General struct { GlobalClientFingerprint string `json:"global-client-fingerprint"` GlobalUA string `json:"global-ua"` ETagSupport bool `json:"etag-support"` + KeepAliveIdle int `json:"keep-alive-idle"` + KeepAliveInterval int `json:"keep-alive-interval"` + DisableKeepAlive bool `json:"disable-keep-alive"` } // Inbound config @@ -707,8 +708,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } func parseGeneral(cfg *RawConfig) (*General, error) { - updater.SetGeoAutoUpdate(cfg.GeoAutoUpdate) - updater.SetGeoUpdateInterval(cfg.GeoUpdateInterval) geodata.SetGeodataMode(cfg.GeodataMode) geodata.SetLoader(cfg.GeodataLoader) geodata.SetSiteMatcher(cfg.GeositeMatcher) @@ -719,10 +718,6 @@ func parseGeneral(cfg *RawConfig) (*General, error) { mihomoHttp.SetUA(cfg.GlobalUA) resource.SetETag(cfg.ETagSupport) - keepalive.SetKeepAliveIdle(time.Duration(cfg.KeepAliveIdle) * time.Second) - keepalive.SetKeepAliveInterval(time.Duration(cfg.KeepAliveInterval) * time.Second) - keepalive.SetDisableKeepAlive(cfg.DisableKeepAlive) - return &General{ Inbound: Inbound{ Port: cfg.Port, @@ -761,6 +756,9 @@ func parseGeneral(cfg *RawConfig) (*General, error) { GlobalClientFingerprint: cfg.GlobalClientFingerprint, GlobalUA: cfg.GlobalUA, ETagSupport: cfg.ETagSupport, + KeepAliveIdle: cfg.KeepAliveIdle, + KeepAliveInterval: cfg.KeepAliveInterval, + DisableKeepAlive: cfg.DisableKeepAlive, }, nil } diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 0492c430..19979063 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -19,6 +19,7 @@ import ( G "github.com/metacubex/mihomo/component/geodata" mihomoHttp "github.com/metacubex/mihomo/component/http" "github.com/metacubex/mihomo/component/iface" + "github.com/metacubex/mihomo/component/keepalive" "github.com/metacubex/mihomo/component/profile" "github.com/metacubex/mihomo/component/profile/cachefile" "github.com/metacubex/mihomo/component/resolver" @@ -117,7 +118,7 @@ func ApplyConfig(cfg *config.Config, force bool) { runtime.GC() tunnel.OnRunning() hcCompatibleProvider(cfg.Providers) - initExternalUI(cfg.Controller) + updateUpdater(cfg) resolver.ResetConnection() } @@ -176,6 +177,9 @@ func GetGeneral() *config.General { GlobalClientFingerprint: tlsC.GetGlobalFingerprint(), GlobalUA: mihomoHttp.UA(), ETagSupport: resource.ETag(), + KeepAliveInterval: int(keepalive.KeepAliveInterval() / time.Second), + KeepAliveIdle: int(keepalive.KeepAliveIdle() / time.Second), + DisableKeepAlive: keepalive.DisableKeepAlive(), } return general @@ -394,7 +398,12 @@ func updateTunnels(tunnels []LC.Tunnel) { listener.PatchTunnel(tunnels, tunnel.Tunnel) } -func initExternalUI(controller *config.Controller) { +func updateUpdater(cfg *config.Config) { + general := cfg.General + updater.SetGeoAutoUpdate(general.GeoAutoUpdate) + updater.SetGeoUpdateInterval(general.GeoUpdateInterval) + + controller := cfg.Controller updater.DefaultUiUpdater = updater.NewUiUpdater(controller.ExternalUI, controller.ExternalUIURL, controller.ExternalUIName) updater.DefaultUiUpdater.AutoDownloadUI() } @@ -412,6 +421,10 @@ func updateGeneral(general *config.General) { inbound.SetTfo(general.InboundTfo) inbound.SetMPTCP(general.InboundMPTCP) + keepalive.SetKeepAliveIdle(time.Duration(general.KeepAliveIdle) * time.Second) + keepalive.SetKeepAliveInterval(time.Duration(general.KeepAliveInterval) * time.Second) + keepalive.SetDisableKeepAlive(general.DisableKeepAlive) + adapter.UnifiedDelay.Store(general.UnifiedDelay) dialer.DefaultInterface.Store(general.Interface) From 9937ae1002a38fd159593bf0cacb6f86864e9303 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 5 Oct 2024 14:16:50 +0800 Subject: [PATCH 26/44] fix: defaultNS not working in system dns --- dns/system.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/dns/system.go b/dns/system.go index 9fb803dd..ab6c0100 100644 --- a/dns/system.go +++ b/dns/system.go @@ -31,12 +31,12 @@ type systemClient struct { func (c *systemClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { dnsClients, err := c.getDnsClients() + if len(dnsClients) == 0 && len(c.defaultNS) > 0 { + dnsClients = c.defaultNS + err = nil + } if err != nil { - if len(c.defaultNS) > 0 { - dnsClients = c.defaultNS - } else { - return - } + return } msg, _, err = batchExchange(ctx, dnsClients, m) return @@ -45,11 +45,16 @@ func (c *systemClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Ms // Address implements dnsClient func (c *systemClient) Address() string { dnsClients, _ := c.getDnsClients() + isDefault := "" + if len(dnsClients) == 0 && len(c.defaultNS) > 0 { + dnsClients = c.defaultNS + isDefault = "[defaultNS]" + } addrs := make([]string, 0, len(dnsClients)) for _, c := range dnsClients { addrs = append(addrs, c.Address()) } - return fmt.Sprintf("system(%s)", strings.Join(addrs, ",")) + return fmt.Sprintf("system%s(%s)", isDefault, strings.Join(addrs, ",")) } var _ dnsClient = (*systemClient)(nil) From 8e6eb70e714d44f26ba407adbd7b255762f48b97 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 5 Oct 2024 19:23:01 +0800 Subject: [PATCH 27/44] chore: temporary update general in ParseRawConfig and rollback before its retur --- component/updater/update_ui.go | 2 +- config/config.go | 27 +++++++----------- hub/executor/executor.go | 50 ++++++++++++++++++++++++---------- 3 files changed, 46 insertions(+), 33 deletions(-) diff --git a/component/updater/update_ui.go b/component/updater/update_ui.go index 48d6536c..bd2a5881 100644 --- a/component/updater/update_ui.go +++ b/component/updater/update_ui.go @@ -58,7 +58,7 @@ func (u *UIUpdater) AutoDownloadUI() { log.Infoln("External UI downloading ...") err := u.downloadUI() if err != nil { - log.Errorln("Error downloading UI:", err) + log.Errorln("Error downloading UI: %s", err) } } } diff --git a/config/config.go b/config/config.go index 69957cee..ba6097bc 100644 --- a/config/config.go +++ b/config/config.go @@ -9,6 +9,7 @@ import ( "net/url" "strings" "time" + _ "unsafe" "github.com/metacubex/mihomo/adapter" "github.com/metacubex/mihomo/adapter/outbound" @@ -19,12 +20,9 @@ import ( "github.com/metacubex/mihomo/component/cidr" "github.com/metacubex/mihomo/component/fakeip" "github.com/metacubex/mihomo/component/geodata" - mihomoHttp "github.com/metacubex/mihomo/component/http" P "github.com/metacubex/mihomo/component/process" "github.com/metacubex/mihomo/component/resolver" - "github.com/metacubex/mihomo/component/resource" "github.com/metacubex/mihomo/component/sniffer" - tlsC "github.com/metacubex/mihomo/component/tls" "github.com/metacubex/mihomo/component/trie" C "github.com/metacubex/mihomo/constant" providerTypes "github.com/metacubex/mihomo/constant/provider" @@ -588,10 +586,11 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } config.General = general - if len(config.General.GlobalClientFingerprint) != 0 { - log.Debugln("GlobalClientFingerprint: %s", config.General.GlobalClientFingerprint) - tlsC.SetGlobalUtlsClient(config.General.GlobalClientFingerprint) - } + // We need to temporarily apply some configuration in general and roll back after parsing the complete configuration. + // The loading and downloading of geodata in the parseRules and parseRuleProviders rely on these. + // This implementation is very disgusting, but there is currently no better solution + rollback := temporaryUpdateGeneral(config.General) + defer rollback() controller, err := parseController(rawCfg) if err != nil { @@ -707,17 +706,10 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { return config, nil } -func parseGeneral(cfg *RawConfig) (*General, error) { - geodata.SetGeodataMode(cfg.GeodataMode) - geodata.SetLoader(cfg.GeodataLoader) - geodata.SetSiteMatcher(cfg.GeositeMatcher) - geodata.SetGeoIpUrl(cfg.GeoXUrl.GeoIp) - geodata.SetGeoSiteUrl(cfg.GeoXUrl.GeoSite) - geodata.SetMmdbUrl(cfg.GeoXUrl.Mmdb) - geodata.SetASNUrl(cfg.GeoXUrl.ASN) - mihomoHttp.SetUA(cfg.GlobalUA) - resource.SetETag(cfg.ETagSupport) +//go:linkname temporaryUpdateGeneral +func temporaryUpdateGeneral(general *General) func() +func parseGeneral(cfg *RawConfig) (*General, error) { return &General{ Inbound: Inbound{ Port: cfg.Port, @@ -751,6 +743,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) { GeoUpdateInterval: cfg.GeoUpdateInterval, GeodataMode: cfg.GeodataMode, GeodataLoader: cfg.GeodataLoader, + GeositeMatcher: cfg.GeositeMatcher, TCPConcurrent: cfg.TCPConcurrent, FindProcessMode: cfg.FindProcessMode, GlobalClientFingerprint: cfg.GlobalClientFingerprint, diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 19979063..3fed360c 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -9,6 +9,7 @@ import ( "strconv" "sync" "time" + _ "unsafe" "github.com/metacubex/mihomo/adapter" "github.com/metacubex/mihomo/adapter/inbound" @@ -16,7 +17,7 @@ import ( "github.com/metacubex/mihomo/component/auth" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/dialer" - G "github.com/metacubex/mihomo/component/geodata" + "github.com/metacubex/mihomo/component/geodata" mihomoHttp "github.com/metacubex/mihomo/component/http" "github.com/metacubex/mihomo/component/iface" "github.com/metacubex/mihomo/component/keepalive" @@ -101,7 +102,7 @@ func ApplyConfig(cfg *config.Config, force bool) { updateRules(cfg.Rules, cfg.SubRules, cfg.RuleProviders) updateSniffer(cfg.Sniffer) updateHosts(cfg.Hosts) - updateGeneral(cfg.General) + updateGeneral(cfg.General, true) updateNTP(cfg.NTP) updateDNS(cfg.DNS, cfg.General.IPv6) updateListeners(cfg.General, cfg.Listeners, force) @@ -161,16 +162,16 @@ func GetGeneral() *config.General { Interface: dialer.DefaultInterface.Load(), RoutingMark: int(dialer.DefaultRoutingMark.Load()), GeoXUrl: config.GeoXUrl{ - GeoIp: G.GeoIpUrl(), - Mmdb: G.MmdbUrl(), - ASN: G.ASNUrl(), - GeoSite: G.GeoSiteUrl(), + GeoIp: geodata.GeoIpUrl(), + Mmdb: geodata.MmdbUrl(), + ASN: geodata.ASNUrl(), + GeoSite: geodata.GeoSiteUrl(), }, GeoAutoUpdate: updater.GeoAutoUpdate(), GeoUpdateInterval: updater.GeoUpdateInterval(), - GeodataMode: G.GeodataMode(), - GeodataLoader: G.LoaderName(), - GeositeMatcher: G.SiteMatcherName(), + GeodataMode: geodata.GeodataMode(), + GeodataLoader: geodata.LoaderName(), + GeositeMatcher: geodata.SiteMatcherName(), TCPConcurrent: dialer.GetTcpConcurrent(), FindProcessMode: tunnel.FindProcessMode(), Sniffing: tunnel.IsSniffing(), @@ -408,13 +409,22 @@ func updateUpdater(cfg *config.Config) { updater.DefaultUiUpdater.AutoDownloadUI() } -func updateGeneral(general *config.General) { +//go:linkname temporaryUpdateGeneral github.com/metacubex/mihomo/config.temporaryUpdateGeneral +func temporaryUpdateGeneral(general *config.General) func() { + oldGeneral := GetGeneral() + updateGeneral(general, false) + return func() { + updateGeneral(oldGeneral, false) + } +} + +func updateGeneral(general *config.General, logging bool) { tunnel.SetMode(general.Mode) tunnel.SetFindProcessMode(general.FindProcessMode) resolver.DisableIPv6 = !general.IPv6 - if general.TCPConcurrent { - dialer.SetTcpConcurrent(general.TCPConcurrent) + dialer.SetTcpConcurrent(general.TCPConcurrent) + if logging && general.TCPConcurrent { log.Infoln("Use tcp concurrent") } @@ -429,13 +439,23 @@ func updateGeneral(general *config.General) { dialer.DefaultInterface.Store(general.Interface) dialer.DefaultRoutingMark.Store(int32(general.RoutingMark)) - if general.RoutingMark > 0 { + if logging && general.RoutingMark > 0 { log.Infoln("Use routing mark: %#x", general.RoutingMark) } iface.FlushCache() - G.SetLoader(general.GeodataLoader) - G.SetSiteMatcher(general.GeositeMatcher) + + geodata.SetGeodataMode(general.GeodataMode) + geodata.SetLoader(general.GeodataLoader) + geodata.SetSiteMatcher(general.GeositeMatcher) + geodata.SetGeoIpUrl(general.GeoXUrl.GeoIp) + geodata.SetGeoSiteUrl(general.GeoXUrl.GeoSite) + geodata.SetMmdbUrl(general.GeoXUrl.Mmdb) + geodata.SetASNUrl(general.GeoXUrl.ASN) + mihomoHttp.SetUA(general.GlobalUA) + resource.SetETag(general.ETagSupport) + + tlsC.SetGlobalUtlsClient(general.GlobalClientFingerprint) } func updateUsers(users []auth.AuthUser) { From 9fd63fe93803c89243452f9b033625bd22f75282 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 6 Oct 2024 10:34:54 +0800 Subject: [PATCH 28/44] chore: update dependencies --- go.mod | 16 ++++++++-------- go.sum | 34 +++++++++++++++++----------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/go.mod b/go.mod index 4b066c90..a223cf77 100644 --- a/go.mod +++ b/go.mod @@ -5,13 +5,13 @@ go 1.20 require ( github.com/3andne/restls-client-go v0.1.6 github.com/bahlo/generic-list-go v0.2.0 - github.com/coreos/go-iptables v0.7.0 + github.com/coreos/go-iptables v0.8.0 github.com/dlclark/regexp2 v1.11.4 github.com/go-chi/chi/v5 v5.1.0 github.com/go-chi/render v1.0.3 github.com/gobwas/ws v1.4.0 github.com/gofrs/uuid/v5 v5.3.0 - github.com/insomniacslk/dhcp v0.0.0-20240812123929-b105c29bd1b5 + github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475 github.com/klauspost/compress v1.17.9 github.com/klauspost/cpuid/v2 v2.2.8 github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 @@ -28,7 +28,7 @@ require ( github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 - github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785 + github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa github.com/metacubex/utls v1.6.6 github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 github.com/miekg/dns v1.1.62 @@ -51,10 +51,10 @@ require ( gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 go.uber.org/automaxprocs v1.6.0 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba - golang.org/x/crypto v0.27.0 - golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa - golang.org/x/net v0.29.0 - golang.org/x/sys v0.25.0 + golang.org/x/crypto v0.28.0 + golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // lastest version compatible with golang1.20 + golang.org/x/net v0.30.0 + golang.org/x/sys v0.26.0 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.3.0 @@ -111,7 +111,7 @@ require ( go.uber.org/mock v0.4.0 // indirect golang.org/x/mod v0.20.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/text v0.18.0 // indirect + golang.org/x/text v0.19.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.24.0 // indirect ) diff --git a/go.sum b/go.sum index 60818034..2b3bba65 100644 --- a/go.sum +++ b/go.sum @@ -19,8 +19,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= -github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8= -github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= +github.com/coreos/go-iptables v0.8.0 h1:MPc2P89IhuVpLI7ETL/2tx3XZ61VeICZjYqDEgNsPRc= +github.com/coreos/go-iptables v0.8.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -71,8 +71,8 @@ github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/insomniacslk/dhcp v0.0.0-20240812123929-b105c29bd1b5 h1:GkMacU5ftc+IEg1449N3UEy2XLDz58W4fkrRu2fibb8= -github.com/insomniacslk/dhcp v0.0.0-20240812123929-b105c29bd1b5/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= +github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475 h1:hxST5pwMBEOWmxpkX20w9oZG+hXdhKmAIPQ3NGGAxas= +github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= @@ -122,8 +122,8 @@ github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosq github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 h1:xg71VmzLS6ByAzi/57phwDvjE+dLLs+ozH00k4DnOns= github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3/go.mod h1:6nitcmzPDL3MXnLdhu6Hm126Zk4S1fBbX3P7jxUxSFw= -github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785 h1:NNmI+ZV0DzNuqaAInRQuZFLHlWVuyHeow8jYpdKjHjo= -github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= +github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa h1:9mcjV+RGZVC3reJBNDjjNPyS8PmFG97zq56X7WNaFO4= +github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa/go.mod h1:4tLB5c8U0CxpkFM+AJJB77jEaVDbLH5XQvy42vAGsWw= github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8= github.com/metacubex/utls v1.6.6/go.mod h1:+WLFUnXjcpdxXCnyX25nggw8C6YonZ8zOK2Zm/oRvdo= github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 h1:hJLQviGySBuaynlCwf/oYgIxbVbGRUIKZCxdya9YrbQ= @@ -230,18 +230,18 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= -golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk= +golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -261,12 +261,12 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= From 08dcef80bf9a528d36cf8d516cc251f6449f9336 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 9 Oct 2024 12:04:56 +0800 Subject: [PATCH 29/44] fix: mistaken using net.Dialer https://github.com/MetaCubeX/mihomo/issues/1572 --- adapter/outbound/hysteria.go | 6 +----- component/http/http.go | 4 ++-- transport/hysteria/transport/client.go | 25 +------------------------ 3 files changed, 4 insertions(+), 31 deletions(-) diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index a7367b83..55c66c45 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -131,11 +131,7 @@ func (c *HysteriaOption) Speed() (uint64, uint64, error) { } func NewHysteria(option HysteriaOption) (*Hysteria, error) { - clientTransport := &transport.ClientTransport{ - Dialer: &net.Dialer{ - Timeout: 8 * time.Second, - }, - } + clientTransport := &transport.ClientTransport{} addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) ports := option.Ports diff --git a/component/http/http.go b/component/http/http.go index 3fc06da3..63ea5be7 100644 --- a/component/http/http.go +++ b/component/http/http.go @@ -12,6 +12,7 @@ import ( "time" "github.com/metacubex/mihomo/component/ca" + "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/listener/inner" ) @@ -71,8 +72,7 @@ func HttpRequestWithProxy(ctx context.Context, url, method string, header map[st if conn, err := inner.HandleTcp(address, specialProxy); err == nil { return conn, nil } else { - d := net.Dialer{} - return d.DialContext(ctx, network, address) + return dialer.DialContext(ctx, network, address) } }, TLSClientConfig: ca.GetGlobalTLSConfig(&tls.Config{}), diff --git a/transport/hysteria/transport/client.go b/transport/hysteria/transport/client.go index f5cc9f07..91876ea9 100644 --- a/transport/hysteria/transport/client.go +++ b/transport/hysteria/transport/client.go @@ -4,7 +4,6 @@ import ( "crypto/tls" "fmt" "net" - "strings" "time" "github.com/metacubex/quic-go" @@ -16,9 +15,7 @@ import ( "github.com/metacubex/mihomo/transport/hysteria/utils" ) -type ClientTransport struct { - Dialer *net.Dialer -} +type ClientTransport struct{} func (ct *ClientTransport) quicPacketConn(proto string, rAddr net.Addr, serverPorts string, obfs obfsPkg.Obfuscator, hopInterval time.Duration, dialer utils.PacketDialer) (net.PacketConn, error) { server := rAddr.String() @@ -86,23 +83,3 @@ func (ct *ClientTransport) QUICDial(proto string, server string, serverPorts str } return qs, nil } - -func (ct *ClientTransport) DialTCP(raddr *net.TCPAddr) (*net.TCPConn, error) { - conn, err := ct.Dialer.Dial("tcp", raddr.String()) - if err != nil { - return nil, err - } - return conn.(*net.TCPConn), nil -} - -func (ct *ClientTransport) ListenUDP() (*net.UDPConn, error) { - return net.ListenUDP("udp", nil) -} - -func isMultiPortAddr(addr string) bool { - _, portStr, err := net.SplitHostPort(addr) - if err == nil && (strings.Contains(portStr, ",") || strings.Contains(portStr, "-")) { - return true - } - return false -} From 57725078e00170f67abb5d9a8cb082c81213a449 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Fri, 11 Oct 2024 07:35:51 +0800 Subject: [PATCH 30/44] chore: Adjust the error log for the search process --- tunnel/tunnel.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 5c136eb2..b1b4add5 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -625,7 +625,7 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { // normal check for process uid, path, err := P.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, int(metadata.SrcPort)) if err != nil { - log.Debugln("[Process] find process %s error: %v", metadata.String(), err) + log.Debugln("[Process] find process error for %s: %v", metadata.String(), err) } else { metadata.Process = filepath.Base(path) metadata.ProcessPath = path @@ -639,7 +639,7 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { // check package names pkg, err := P.FindPackageName(metadata) if err != nil { - log.Debugln("[Process] find process %s error: %v", metadata.String(), err) + log.Debugln("[Process] find process error for %s: %v", metadata.String(), err) } else { metadata.Process = pkg } From 4437c8861c3a0815aa2f338df8676de21176a286 Mon Sep 17 00:00:00 2001 From: ForestL <45709305+ForestL18@users.noreply.github.com> Date: Fri, 11 Oct 2024 08:46:31 +0800 Subject: [PATCH 31/44] chore: better getUpdateTime() for iterating all Geofiles (#1570) --- component/updater/update_geo.go | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/component/updater/update_geo.go b/component/updater/update_geo.go index b5dc9677..bba0dabd 100644 --- a/component/updater/update_geo.go +++ b/component/updater/update_geo.go @@ -229,20 +229,22 @@ func UpdateGeoDatabases() error { } func getUpdateTime() (err error, time time.Time) { - var fileInfo os.FileInfo - if geodata.GeodataMode() { - fileInfo, err = os.Stat(C.Path.GeoIP()) - if err != nil { - return err, time - } - } else { - fileInfo, err = os.Stat(C.Path.MMDB()) - if err != nil { - return err, time + filesToCheck := []string{ + C.Path.GeoIP(), + C.Path.MMDB(), + C.Path.ASN(), + C.Path.GeoSite(), + } + + for _, file := range filesToCheck { + var fileInfo os.FileInfo + fileInfo, err = os.Stat(file) + if err == nil { + return nil, fileInfo.ModTime() } } - return nil, fileInfo.ModTime() + return } func RegisterGeoUpdater() { From ca3f1ebae6b71c2224987f1cf7828cf1338d5509 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sat, 12 Oct 2024 08:26:37 +0800 Subject: [PATCH 32/44] fix: sticky-sessions may not be effective --- adapter/outboundgroup/loadbalance.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapter/outboundgroup/loadbalance.go b/adapter/outboundgroup/loadbalance.go index 738ed154..048cc34c 100644 --- a/adapter/outboundgroup/loadbalance.go +++ b/adapter/outboundgroup/loadbalance.go @@ -204,7 +204,7 @@ func strategyStickySessions(url string) strategyFn { for i := 1; i < maxRetry; i++ { proxy := proxies[nowIdx] if proxy.AliveForTestUrl(url) { - if nowIdx != idx { + if !has || nowIdx != idx { lruCache.Set(key, nowIdx) } From 95af5f7325fcd9c945b3ad52e617e6ee5ae12d50 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sun, 20 Oct 2024 06:01:02 +0800 Subject: [PATCH 33/44] chore: change subscription-userinfo retrieval --- adapter/provider/provider.go | 64 ++++++++----------- adapter/provider/subscription_info.go | 3 +- component/profile/cachefile/cache.go | 7 +- .../profile/cachefile/subscriptioninfo.go | 41 ++++++++++++ component/resource/vehicle.go | 21 ++++-- constant/adapters.go | 4 +- constant/provider/interface.go | 1 + 7 files changed, 92 insertions(+), 49 deletions(-) create mode 100644 component/profile/cachefile/subscriptioninfo.go diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index 79a752a6..2fe9633c 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -1,11 +1,9 @@ package provider import ( - "context" "encoding/json" "errors" "fmt" - "net/http" "reflect" "runtime" "strings" @@ -14,7 +12,7 @@ import ( "github.com/metacubex/mihomo/adapter" "github.com/metacubex/mihomo/common/convert" "github.com/metacubex/mihomo/common/utils" - mihomoHttp "github.com/metacubex/mihomo/component/http" + "github.com/metacubex/mihomo/component/profile/cachefile" "github.com/metacubex/mihomo/component/resource" C "github.com/metacubex/mihomo/constant" types "github.com/metacubex/mihomo/constant/provider" @@ -80,7 +78,9 @@ func (pp *proxySetProvider) Initial() error { if err != nil { return err } - pp.getSubscriptionInfo() + if subscriptionInfo := cachefile.Cache().GetSubscriptionInfo(pp.Name()); subscriptionInfo != "" { + pp.SetSubscriptionInfo(subscriptionInfo) + } pp.closeAllConnections() return nil } @@ -117,35 +117,14 @@ func (pp *proxySetProvider) setProxies(proxies []C.Proxy) { } } -func (pp *proxySetProvider) getSubscriptionInfo() { - if pp.VehicleType() != types.HTTP { - return - } - go func() { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) - defer cancel() - resp, err := mihomoHttp.HttpRequestWithProxy(ctx, pp.Vehicle().Url(), - http.MethodGet, nil, nil, pp.Vehicle().Proxy()) - if err != nil { - return - } - defer resp.Body.Close() +func (pp *proxySetProvider) SetSubscriptionInfo(userInfo string) { + pp.subscriptionInfo = NewSubscriptionInfo(userInfo) +} - userInfoStr := strings.TrimSpace(resp.Header.Get("subscription-userinfo")) - if userInfoStr == "" { - resp2, err := mihomoHttp.HttpRequestWithProxy(ctx, pp.Vehicle().Url(), - http.MethodGet, http.Header{"User-Agent": {"Quantumultx"}}, nil, pp.Vehicle().Proxy()) - if err != nil { - return - } - defer resp2.Body.Close() - userInfoStr = strings.TrimSpace(resp2.Header.Get("subscription-userinfo")) - if userInfoStr == "" { - return - } - } - pp.subscriptionInfo = NewSubscriptionInfo(userInfoStr) - }() +func (pp *proxySetProvider) SetProvider(provider types.ProxyProvider) { + if httpVehicle, ok := pp.Vehicle().(*resource.HTTPVehicle); ok { + httpVehicle.SetProvider(provider) + } } func (pp *proxySetProvider) closeAllConnections() { @@ -196,6 +175,9 @@ func NewProxySetProvider(name string, interval time.Duration, filter string, exc fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, excludeTypeArray, filterRegs, excludeFilterReg, dialerProxy, override), proxiesOnUpdate(pd)) pd.Fetcher = fetcher wrapper := &ProxySetProvider{pd} + if httpVehicle, ok := vehicle.(*resource.HTTPVehicle); ok { + httpVehicle.SetProvider(wrapper) + } runtime.SetFinalizer(wrapper, (*ProxySetProvider).Close) return wrapper, nil } @@ -205,16 +187,21 @@ func (pp *ProxySetProvider) Close() error { return pp.proxySetProvider.Close() } +func (pp *ProxySetProvider) SetProvider(provider types.ProxyProvider) { + pp.proxySetProvider.SetProvider(provider) +} + // CompatibleProvider for auto gc type CompatibleProvider struct { *compatibleProvider } type compatibleProvider struct { - name string - healthCheck *HealthCheck - proxies []C.Proxy - version uint32 + name string + healthCheck *HealthCheck + subscriptionInfo *SubscriptionInfo + proxies []C.Proxy + version uint32 } func (cp *compatibleProvider) MarshalJSON() ([]byte, error) { @@ -284,6 +271,10 @@ func (cp *compatibleProvider) Close() error { return nil } +func (cp *compatibleProvider) SetSubscriptionInfo(userInfo string) { + cp.subscriptionInfo = NewSubscriptionInfo(userInfo) +} + func NewCompatibleProvider(name string, proxies []C.Proxy, hc *HealthCheck) (*CompatibleProvider, error) { if len(proxies) == 0 { return nil, errors.New("provider need one proxy at least") @@ -313,7 +304,6 @@ func proxiesOnUpdate(pd *proxySetProvider) func([]C.Proxy) { return func(elm []C.Proxy) { pd.setProxies(elm) pd.version += 1 - pd.getSubscriptionInfo() } } diff --git a/adapter/provider/subscription_info.go b/adapter/provider/subscription_info.go index b72c7b61..412b4342 100644 --- a/adapter/provider/subscription_info.go +++ b/adapter/provider/subscription_info.go @@ -16,8 +16,7 @@ type SubscriptionInfo struct { } func NewSubscriptionInfo(userinfo string) (si *SubscriptionInfo) { - userinfo = strings.ToLower(userinfo) - userinfo = strings.ReplaceAll(userinfo, " ", "") + userinfo = strings.ReplaceAll(strings.ToLower(userinfo), " ", "") si = new(SubscriptionInfo) for _, field := range strings.Split(userinfo, ";") { diff --git a/component/profile/cachefile/cache.go b/component/profile/cachefile/cache.go index f5101f5b..7b4cdfc2 100644 --- a/component/profile/cachefile/cache.go +++ b/component/profile/cachefile/cache.go @@ -17,9 +17,10 @@ var ( fileMode os.FileMode = 0o666 defaultCache *CacheFile - bucketSelected = []byte("selected") - bucketFakeip = []byte("fakeip") - bucketETag = []byte("etag") + bucketSelected = []byte("selected") + bucketFakeip = []byte("fakeip") + bucketETag = []byte("etag") + bucketSubscriptionInfo = []byte("subscriptioninfo") ) // CacheFile store and update the cache file diff --git a/component/profile/cachefile/subscriptioninfo.go b/component/profile/cachefile/subscriptioninfo.go new file mode 100644 index 00000000..c68f92eb --- /dev/null +++ b/component/profile/cachefile/subscriptioninfo.go @@ -0,0 +1,41 @@ +package cachefile + +import ( + "github.com/metacubex/mihomo/log" + + "github.com/metacubex/bbolt" +) + +func (c *CacheFile) SetSubscriptionInfo(name string, userInfo string) { + if c.DB == nil { + return + } + + err := c.DB.Batch(func(t *bbolt.Tx) error { + bucket, err := t.CreateBucketIfNotExists(bucketSubscriptionInfo) + if err != nil { + return err + } + + return bucket.Put([]byte(name), []byte(userInfo)) + }) + if err != nil { + log.Warnln("[CacheFile] write cache to %s failed: %s", c.DB.Path(), err.Error()) + return + } +} +func (c *CacheFile) GetSubscriptionInfo(name string) (userInfo string) { + if c.DB == nil { + return + } + c.DB.View(func(t *bbolt.Tx) error { + if bucket := t.Bucket(bucketSubscriptionInfo); bucket != nil { + if v := bucket.Get([]byte(name)); v != nil { + userInfo = string(v) + } + } + return nil + }) + + return +} diff --git a/component/resource/vehicle.go b/component/resource/vehicle.go index a9382329..7c3cb1c2 100644 --- a/component/resource/vehicle.go +++ b/component/resource/vehicle.go @@ -84,11 +84,12 @@ func NewFileVehicle(path string) *FileVehicle { } type HTTPVehicle struct { - url string - path string - proxy string - header http.Header - timeout time.Duration + url string + path string + proxy string + header http.Header + timeout time.Duration + provider types.ProxyProvider } func (h *HTTPVehicle) Url() string { @@ -111,6 +112,10 @@ func (h *HTTPVehicle) Write(buf []byte) error { return safeWrite(h.path, buf) } +func (h *HTTPVehicle) SetProvider(provider types.ProxyProvider) { + h.provider = provider +} + func (h *HTTPVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []byte, hash utils.HashType, err error) { ctx, cancel := context.WithTimeout(ctx, h.timeout) defer cancel() @@ -133,6 +138,12 @@ func (h *HTTPVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []b return } defer resp.Body.Close() + + if subscriptionInfo := resp.Header.Get("subscription-userinfo"); h.provider != nil && subscriptionInfo != "" { + cachefile.Cache().SetSubscriptionInfo(h.provider.Name(), subscriptionInfo) + h.provider.SetSubscriptionInfo(subscriptionInfo) + } + if resp.StatusCode < 200 || resp.StatusCode > 299 { if setIfNoneMatch && resp.StatusCode == http.StatusNotModified { return nil, oldHash, nil diff --git a/constant/adapters.go b/constant/adapters.go index b303eb84..c7b73a06 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -213,6 +213,8 @@ func (at AdapterType) String() string { return "WireGuard" case Tuic: return "Tuic" + case Ssh: + return "Ssh" case Relay: return "Relay" @@ -224,8 +226,6 @@ func (at AdapterType) String() string { return "URLTest" case LoadBalance: return "LoadBalance" - case Ssh: - return "Ssh" default: return "Unknown" } diff --git a/constant/provider/interface.go b/constant/provider/interface.go index 065b801a..925c1734 100644 --- a/constant/provider/interface.go +++ b/constant/provider/interface.go @@ -81,6 +81,7 @@ type ProxyProvider interface { Version() uint32 RegisterHealthCheckTask(url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint) HealthCheckURL() string + SetSubscriptionInfo(userInfo string) } // RuleProvider interface From b9171ade7f91b19388522e8338f401fba734ac97 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 21 Oct 2024 09:15:57 +0800 Subject: [PATCH 34/44] chore: update sing-tun to v0.4.0-rc.4 --- go.mod | 6 +++--- go.sum | 8 ++++---- listener/sing_tun/server.go | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index a223cf77..67afd143 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 github.com/metacubex/sing-shadowsocks v0.2.8 github.com/metacubex/sing-shadowsocks2 v0.2.2 - github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 + github.com/metacubex/sing-tun v0.2.7-0.20241021011113-857bcd6ee47c github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa @@ -39,7 +39,7 @@ require ( github.com/sagernet/cors v1.2.1 github.com/sagernet/fswatch v0.1.1 github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a - github.com/sagernet/sing v0.5.0-alpha.13 + github.com/sagernet/sing v0.5.0-rc.4 github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 github.com/sagernet/sing-shadowtls v0.1.4 github.com/samber/lo v1.47.0 @@ -116,4 +116,4 @@ require ( golang.org/x/tools v0.24.0 // indirect ) -replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297 +replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20241021005542-18b67490300a diff --git a/go.sum b/go.sum index 2b3bba65..b43d779e 100644 --- a/go.sum +++ b/go.sum @@ -108,16 +108,16 @@ github.com/metacubex/quic-go v0.47.1-0.20240909010619-6b38f24bfcc4 h1:CgdUBRxmNl github.com/metacubex/quic-go v0.47.1-0.20240909010619-6b38f24bfcc4/go.mod h1:Y7yRGqFE6UQL/3aKPYmiYdjfVkeujJaStP4+jiZMcN8= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= -github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297 h1:YG/JkwGPbca5rUtEMHIu8ZuqzR7BSVm1iqY8hNoMeMA= -github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/metacubex/sing v0.0.0-20241021005542-18b67490300a h1:JuR0/7RDxQtlZp/GOzrdqNq04HplTxavPRHrek8ouJk= +github.com/metacubex/sing v0.0.0-20241021005542-18b67490300a/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 h1:HobpULaPK6OoxrHMmgcwLkwwIduXVmwdcznwUfH1GQM= github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8= github.com/metacubex/sing-shadowsocks v0.2.8 h1:wIhlaigswzjPw4hej75sEvWte3QR0+AJRafgwBHO5B4= github.com/metacubex/sing-shadowsocks v0.2.8/go.mod h1:X3x88XtJpBxG0W0/ECOJL6Ib0SJ3xdniAkU/6/RMWU0= github.com/metacubex/sing-shadowsocks2 v0.2.2 h1:eaf42uVx4Lr21S6MDYs0ZdTvGA0GEhDpb9no4+gdXPo= github.com/metacubex/sing-shadowsocks2 v0.2.2/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q= -github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 h1:ypfofGDZbP8p3Y4P/m74JYu7sQViesi3c8nbmT6cS0Y= -github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1/go.mod h1:olbEx9yVcaw5tHTNlRamRoxmMKcvDvcVS1YLnQGzvWE= +github.com/metacubex/sing-tun v0.2.7-0.20241021011113-857bcd6ee47c h1:qfUZ8xBrViOCZamvcC8CyV7Ii8sAUrn7RqZxFGn56tQ= +github.com/metacubex/sing-tun v0.2.7-0.20241021011113-857bcd6ee47c/go.mod h1:lCrP0AW7ieKnXG1JEeZLW+9h99QzjuOX0MfCQfz6TgE= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosqY8xKDp3pqTW3qbrCprZ1l6WkrXSFSCwyY4I= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 h1:xg71VmzLS6ByAzi/57phwDvjE+dLLs+ozH00k4DnOns= diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 79856c46..ad3d113e 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -279,7 +279,7 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis return } - defaultInterfaceMonitor, err = tun.NewDefaultInterfaceMonitor(networkUpdateMonitor, log.SingLogger, tun.DefaultInterfaceMonitorOptions{OverrideAndroidVPN: true}) + defaultInterfaceMonitor, err = tun.NewDefaultInterfaceMonitor(networkUpdateMonitor, log.SingLogger, tun.DefaultInterfaceMonitorOptions{InterfaceFinder: interfaceFinder, OverrideAndroidVPN: true}) if err != nil { err = E.Cause(err, "create DefaultInterfaceMonitor") return From 3e966e82c793ca99e3badc84bf3f2907b100edae Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 21 Oct 2024 09:38:21 +0800 Subject: [PATCH 35/44] chore: update quic-go to 0.48.0 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 67afd143..09803062 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 github.com/metacubex/chacha v0.1.0 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 - github.com/metacubex/quic-go v0.47.1-0.20240909010619-6b38f24bfcc4 + github.com/metacubex/quic-go v0.48.1-0.20241021013658-51ca987e0174 github.com/metacubex/randv2 v0.2.0 github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 github.com/metacubex/sing-shadowsocks v0.2.8 diff --git a/go.sum b/go.sum index b43d779e..0508ac59 100644 --- a/go.sum +++ b/go.sum @@ -104,8 +104,8 @@ github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvO github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= -github.com/metacubex/quic-go v0.47.1-0.20240909010619-6b38f24bfcc4 h1:CgdUBRxmNlxEGkp35HwvgQ10jwOOUJKWdOxpi8yWi8o= -github.com/metacubex/quic-go v0.47.1-0.20240909010619-6b38f24bfcc4/go.mod h1:Y7yRGqFE6UQL/3aKPYmiYdjfVkeujJaStP4+jiZMcN8= +github.com/metacubex/quic-go v0.48.1-0.20241021013658-51ca987e0174 h1:GvigRPEU+cbnzdLWne47cxLrc28Abohl3ECtVdnrbq0= +github.com/metacubex/quic-go v0.48.1-0.20241021013658-51ca987e0174/go.mod h1:AiZ+UPgrkO1DTnmiAX4b+kRoV1Vfc65UkYD7RbFlIZA= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= github.com/metacubex/sing v0.0.0-20241021005542-18b67490300a h1:JuR0/7RDxQtlZp/GOzrdqNq04HplTxavPRHrek8ouJk= From a86c5628526934892b24c4667af646fc61c3d4f5 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Mon, 4 Nov 2024 19:31:43 +0800 Subject: [PATCH 36/44] chore: Increase support for other format of ASN --- component/mmdb/reader.go | 26 +++++++++++++++++++++----- rules/common/ipasn.go | 13 ++++--------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/component/mmdb/reader.go b/component/mmdb/reader.go index e76e9939..42e500c9 100644 --- a/component/mmdb/reader.go +++ b/component/mmdb/reader.go @@ -5,6 +5,7 @@ import ( "net" "strings" + "github.com/metacubex/mihomo/log" "github.com/oschwald/maxminddb-golang" ) @@ -23,11 +24,16 @@ type ASNReader struct { *maxminddb.Reader } -type ASNResult struct { +type GeoLite2 struct { AutonomousSystemNumber uint32 `maxminddb:"autonomous_system_number"` AutonomousSystemOrganization string `maxminddb:"autonomous_system_organization"` } +type IPInfo struct { + ASN string `maxminddb:"asn"` + Name string `maxminddb:"name"` +} + func (r IPReader) LookupCode(ipAddress net.IP) []string { switch r.databaseType { case typeMaxmind: @@ -66,8 +72,18 @@ func (r IPReader) LookupCode(ipAddress net.IP) []string { } } -func (r ASNReader) LookupASN(ip net.IP) ASNResult { - var result ASNResult - r.Lookup(ip, &result) - return result +func (r ASNReader) LookupASN(ip net.IP) (string, string) { + switch r.Metadata.DatabaseType { + case "GeoLite2-ASN", "DBIP-ASN-Lite (compat=GeoLite2-ASN)": + var result GeoLite2 + _ = r.Lookup(ip, &result) + return fmt.Sprint(result.AutonomousSystemNumber), result.AutonomousSystemOrganization + case "ipinfo generic_asn_free.mmdb": + var result IPInfo + _ = r.Lookup(ip, &result) + return result.ASN[2:], result.Name + default: + log.Warnln("Unsupported ASN type: %s", r.Metadata.DatabaseType) + } + return "", "" } diff --git a/rules/common/ipasn.go b/rules/common/ipasn.go index 813923ac..7d554103 100644 --- a/rules/common/ipasn.go +++ b/rules/common/ipasn.go @@ -1,8 +1,6 @@ package common import ( - "strconv" - "github.com/metacubex/mihomo/component/geodata" "github.com/metacubex/mihomo/component/mmdb" C "github.com/metacubex/mihomo/constant" @@ -26,17 +24,14 @@ func (a *ASN) Match(metadata *C.Metadata) (bool, string) { return false, "" } - result := mmdb.ASNInstance().LookupASN(ip.AsSlice()) - asnNumber := strconv.FormatUint(uint64(result.AutonomousSystemNumber), 10) - ipASN := asnNumber + " " + result.AutonomousSystemOrganization + asn, aso := mmdb.ASNInstance().LookupASN(ip.AsSlice()) if a.isSourceIP { - metadata.SrcIPASN = ipASN + metadata.SrcIPASN = asn + " " + aso } else { - metadata.DstIPASN = ipASN + metadata.DstIPASN = asn + " " + aso } - match := a.asn == asnNumber - return match, a.adapter + return a.asn == asn, a.adapter } func (a *ASN) RuleType() C.RuleType { From fabd216c349cf79291e8d2f01d4b11c7e83e3819 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 5 Nov 2024 08:58:41 +0800 Subject: [PATCH 37/44] chore: update quic-go to 0.48.1 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 09803062..1278b0ed 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 github.com/metacubex/chacha v0.1.0 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 - github.com/metacubex/quic-go v0.48.1-0.20241021013658-51ca987e0174 + github.com/metacubex/quic-go v0.48.2-0.20241105005628-a3e65bac65b2 github.com/metacubex/randv2 v0.2.0 github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 github.com/metacubex/sing-shadowsocks v0.2.8 diff --git a/go.sum b/go.sum index 0508ac59..0139ca20 100644 --- a/go.sum +++ b/go.sum @@ -104,8 +104,8 @@ github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvO github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= -github.com/metacubex/quic-go v0.48.1-0.20241021013658-51ca987e0174 h1:GvigRPEU+cbnzdLWne47cxLrc28Abohl3ECtVdnrbq0= -github.com/metacubex/quic-go v0.48.1-0.20241021013658-51ca987e0174/go.mod h1:AiZ+UPgrkO1DTnmiAX4b+kRoV1Vfc65UkYD7RbFlIZA= +github.com/metacubex/quic-go v0.48.2-0.20241105005628-a3e65bac65b2 h1:1prpWzQnhN/LgGTMA6nz86MGcppDUOwfRkhxPOnrzAk= +github.com/metacubex/quic-go v0.48.2-0.20241105005628-a3e65bac65b2/go.mod h1:AiZ+UPgrkO1DTnmiAX4b+kRoV1Vfc65UkYD7RbFlIZA= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= github.com/metacubex/sing v0.0.0-20241021005542-18b67490300a h1:JuR0/7RDxQtlZp/GOzrdqNq04HplTxavPRHrek8ouJk= From e6d1c8cedf55c3d868e1791f4ed6a76d4156e1a0 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 5 Nov 2024 09:12:20 +0800 Subject: [PATCH 38/44] chore: update sing-tun to v0.4.0-rc.5 --- component/iface/iface.go | 2 ++ go.mod | 6 +++--- go.sum | 8 ++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/component/iface/iface.go b/component/iface/iface.go index 272ee737..a0fa4d5b 100644 --- a/component/iface/iface.go +++ b/component/iface/iface.go @@ -15,6 +15,7 @@ type Interface struct { Name string Addresses []netip.Prefix HardwareAddr net.HardwareAddr + Flags net.Flags } var ( @@ -66,6 +67,7 @@ func Interfaces() (map[string]*Interface, error) { Name: iface.Name, Addresses: ipNets, HardwareAddr: iface.HardwareAddr, + Flags: iface.Flags, } } diff --git a/go.mod b/go.mod index 1278b0ed..f62556c3 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 github.com/metacubex/sing-shadowsocks v0.2.8 github.com/metacubex/sing-shadowsocks2 v0.2.2 - github.com/metacubex/sing-tun v0.2.7-0.20241021011113-857bcd6ee47c + github.com/metacubex/sing-tun v0.2.7-0.20241105010444-9ece61968014 github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa @@ -39,7 +39,7 @@ require ( github.com/sagernet/cors v1.2.1 github.com/sagernet/fswatch v0.1.1 github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a - github.com/sagernet/sing v0.5.0-rc.4 + github.com/sagernet/sing v0.5.0 github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 github.com/sagernet/sing-shadowtls v0.1.4 github.com/samber/lo v1.47.0 @@ -116,4 +116,4 @@ require ( golang.org/x/tools v0.24.0 // indirect ) -replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20241021005542-18b67490300a +replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20241105005934-13bf5e941908 diff --git a/go.sum b/go.sum index 0139ca20..8d75ee4a 100644 --- a/go.sum +++ b/go.sum @@ -108,16 +108,16 @@ github.com/metacubex/quic-go v0.48.2-0.20241105005628-a3e65bac65b2 h1:1prpWzQnhN github.com/metacubex/quic-go v0.48.2-0.20241105005628-a3e65bac65b2/go.mod h1:AiZ+UPgrkO1DTnmiAX4b+kRoV1Vfc65UkYD7RbFlIZA= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= -github.com/metacubex/sing v0.0.0-20241021005542-18b67490300a h1:JuR0/7RDxQtlZp/GOzrdqNq04HplTxavPRHrek8ouJk= -github.com/metacubex/sing v0.0.0-20241021005542-18b67490300a/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/metacubex/sing v0.0.0-20241105005934-13bf5e941908 h1:cZYdGEQKfLsw//TI7dk9bdplz48zitpEDbDGusB9d40= +github.com/metacubex/sing v0.0.0-20241105005934-13bf5e941908/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 h1:HobpULaPK6OoxrHMmgcwLkwwIduXVmwdcznwUfH1GQM= github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8= github.com/metacubex/sing-shadowsocks v0.2.8 h1:wIhlaigswzjPw4hej75sEvWte3QR0+AJRafgwBHO5B4= github.com/metacubex/sing-shadowsocks v0.2.8/go.mod h1:X3x88XtJpBxG0W0/ECOJL6Ib0SJ3xdniAkU/6/RMWU0= github.com/metacubex/sing-shadowsocks2 v0.2.2 h1:eaf42uVx4Lr21S6MDYs0ZdTvGA0GEhDpb9no4+gdXPo= github.com/metacubex/sing-shadowsocks2 v0.2.2/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q= -github.com/metacubex/sing-tun v0.2.7-0.20241021011113-857bcd6ee47c h1:qfUZ8xBrViOCZamvcC8CyV7Ii8sAUrn7RqZxFGn56tQ= -github.com/metacubex/sing-tun v0.2.7-0.20241021011113-857bcd6ee47c/go.mod h1:lCrP0AW7ieKnXG1JEeZLW+9h99QzjuOX0MfCQfz6TgE= +github.com/metacubex/sing-tun v0.2.7-0.20241105010444-9ece61968014 h1:5zAAcCCyuorvkXu3su4VmUkYDXOdzvRKPGEdeyc+ljY= +github.com/metacubex/sing-tun v0.2.7-0.20241105010444-9ece61968014/go.mod h1:GRcrj7VnhvYFsS34cv0J2qTVm5h9DvQuGwliVyVLVvE= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosqY8xKDp3pqTW3qbrCprZ1l6WkrXSFSCwyY4I= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 h1:xg71VmzLS6ByAzi/57phwDvjE+dLLs+ozH00k4DnOns= From 69454b030ee9500d65fcb258adf28948deb7f7d7 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 5 Nov 2024 09:15:30 +0800 Subject: [PATCH 39/44] chore: allow disabled overrideAndroidVPN by environment variable `DISABLE_OVERRIDE_ANDROID_VPN` --- listener/sing_tun/server.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index ad3d113e..ba337b01 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -279,7 +279,11 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis return } - defaultInterfaceMonitor, err = tun.NewDefaultInterfaceMonitor(networkUpdateMonitor, log.SingLogger, tun.DefaultInterfaceMonitorOptions{InterfaceFinder: interfaceFinder, OverrideAndroidVPN: true}) + overrideAndroidVPN := true + if disable, _ := strconv.ParseBool(os.Getenv("DISABLE_OVERRIDE_ANDROID_VPN")); disable { + overrideAndroidVPN = false + } + defaultInterfaceMonitor, err = tun.NewDefaultInterfaceMonitor(networkUpdateMonitor, log.SingLogger, tun.DefaultInterfaceMonitorOptions{InterfaceFinder: interfaceFinder, OverrideAndroidVPN: overrideAndroidVPN}) if err != nil { err = E.Cause(err, "create DefaultInterfaceMonitor") return From d4478dbfa29328a62f1ce94d98e2e68490bcaa55 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 5 Nov 2024 09:29:01 +0800 Subject: [PATCH 40/44] chore: reduce the performance overhead of not enabling LoopBackDetector --- adapter/outbound/direct.go | 18 ++++-------------- component/loopback/detector.go | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index 9ee237fa..dbde5593 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -3,18 +3,12 @@ package outbound import ( "context" "errors" - "os" - "strconv" - "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/loopback" "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/constant/features" ) -var DisableLoopBackDetector, _ = strconv.ParseBool(os.Getenv("DISABLE_LOOPBACK_DETECTOR")) - type Direct struct { *Base loopBack *loopback.Detector @@ -27,10 +21,8 @@ type DirectOption struct { // DialContext implements C.ProxyAdapter func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { - if !features.CMFA && !DisableLoopBackDetector { - if err := d.loopBack.CheckConn(metadata); err != nil { - return nil, err - } + if err := d.loopBack.CheckConn(metadata); err != nil { + return nil, err } opts = append(opts, dialer.WithResolver(resolver.DirectHostResolver)) c, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress(), d.Base.DialOptions(opts...)...) @@ -42,10 +34,8 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ... // ListenPacketContext implements C.ProxyAdapter func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { - if !features.CMFA && !DisableLoopBackDetector { - if err := d.loopBack.CheckPacketConn(metadata); err != nil { - return nil, err - } + if err := d.loopBack.CheckPacketConn(metadata); err != nil { + return nil, err } // net.UDPConn.WriteTo only working with *net.UDPAddr, so we need a net.UDPAddr if !metadata.Resolved() { diff --git a/component/loopback/detector.go b/component/loopback/detector.go index 8ec96a9d..c639ab22 100644 --- a/component/loopback/detector.go +++ b/component/loopback/detector.go @@ -4,14 +4,25 @@ import ( "errors" "fmt" "net/netip" + "os" + "strconv" "github.com/metacubex/mihomo/common/callback" "github.com/metacubex/mihomo/component/iface" C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/constant/features" "github.com/puzpuzpuz/xsync/v3" ) +var disableLoopBackDetector, _ = strconv.ParseBool(os.Getenv("DISABLE_LOOPBACK_DETECTOR")) + +func init() { + if features.CMFA { + disableLoopBackDetector = true + } +} + var ErrReject = errors.New("reject loopback connection") type Detector struct { @@ -20,6 +31,9 @@ type Detector struct { } func NewDetector() *Detector { + if disableLoopBackDetector { + return nil + } return &Detector{ connMap: xsync.NewMapOf[netip.AddrPort, struct{}](), packetConnMap: xsync.NewMapOf[uint16, struct{}](), @@ -27,6 +41,9 @@ func NewDetector() *Detector { } func (l *Detector) NewConn(conn C.Conn) C.Conn { + if l == nil || l.connMap == nil { + return conn + } metadata := C.Metadata{} if metadata.SetRemoteAddr(conn.LocalAddr()) != nil { return conn @@ -42,6 +59,9 @@ func (l *Detector) NewConn(conn C.Conn) C.Conn { } func (l *Detector) NewPacketConn(conn C.PacketConn) C.PacketConn { + if l == nil || l.packetConnMap == nil { + return conn + } metadata := C.Metadata{} if metadata.SetRemoteAddr(conn.LocalAddr()) != nil { return conn @@ -58,6 +78,9 @@ func (l *Detector) NewPacketConn(conn C.PacketConn) C.PacketConn { } func (l *Detector) CheckConn(metadata *C.Metadata) error { + if l == nil || l.connMap == nil { + return nil + } connAddr := metadata.SourceAddrPort() if !connAddr.IsValid() { return nil @@ -69,6 +92,9 @@ func (l *Detector) CheckConn(metadata *C.Metadata) error { } func (l *Detector) CheckPacketConn(metadata *C.Metadata) error { + if l == nil || l.packetConnMap == nil { + return nil + } connAddr := metadata.SourceAddrPort() if !connAddr.IsValid() { return nil From ce52c3438bf5c09f1fe58f6034a9ff7ee4ac3c25 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 5 Nov 2024 10:03:21 +0800 Subject: [PATCH 41/44] chore: cleaned up some confusing code --- tunnel/statistic/tracker.go | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/tunnel/statistic/tracker.go b/tunnel/statistic/tracker.go index 0bf7995d..ca592d97 100644 --- a/tunnel/statistic/tracker.go +++ b/tunnel/statistic/tracker.go @@ -117,24 +117,19 @@ func (tt *tcpTracker) Upstream() any { } func parseRemoteDestination(addr net.Addr, conn C.Connection) string { - if addr == nil && conn != nil { - return conn.RemoteDestination() - } - if addrPort, err := netip.ParseAddrPort(addr.String()); err == nil && addrPort.Addr().IsValid() { - return addrPort.Addr().String() - } else { - if conn != nil { - return conn.RemoteDestination() - } else { - return "" + if addr != nil { + if addrPort, err := netip.ParseAddrPort(addr.String()); err == nil && addrPort.Addr().IsValid() { + return addrPort.Addr().String() } } + if conn != nil { + return conn.RemoteDestination() + } + return "" } func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.Rule, uploadTotal int64, downloadTotal int64, pushToManager bool) *tcpTracker { - if conn != nil { - metadata.RemoteDst = parseRemoteDestination(conn.RemoteAddr(), conn) - } + metadata.RemoteDst = parseRemoteDestination(conn.RemoteAddr(), conn) t := &tcpTracker{ Conn: conn, From 91d54bdac142f1b7bb0eff2859d1b6c12a7a33ef Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 6 Nov 2024 19:45:39 +0800 Subject: [PATCH 42/44] fix: android tun start error --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f62556c3..ff870121 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 github.com/metacubex/sing-shadowsocks v0.2.8 github.com/metacubex/sing-shadowsocks2 v0.2.2 - github.com/metacubex/sing-tun v0.2.7-0.20241105010444-9ece61968014 + github.com/metacubex/sing-tun v0.2.7-0.20241106120309-53606a70db98 github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa diff --git a/go.sum b/go.sum index 8d75ee4a..d7b3c640 100644 --- a/go.sum +++ b/go.sum @@ -116,8 +116,8 @@ github.com/metacubex/sing-shadowsocks v0.2.8 h1:wIhlaigswzjPw4hej75sEvWte3QR0+AJ github.com/metacubex/sing-shadowsocks v0.2.8/go.mod h1:X3x88XtJpBxG0W0/ECOJL6Ib0SJ3xdniAkU/6/RMWU0= github.com/metacubex/sing-shadowsocks2 v0.2.2 h1:eaf42uVx4Lr21S6MDYs0ZdTvGA0GEhDpb9no4+gdXPo= github.com/metacubex/sing-shadowsocks2 v0.2.2/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q= -github.com/metacubex/sing-tun v0.2.7-0.20241105010444-9ece61968014 h1:5zAAcCCyuorvkXu3su4VmUkYDXOdzvRKPGEdeyc+ljY= -github.com/metacubex/sing-tun v0.2.7-0.20241105010444-9ece61968014/go.mod h1:GRcrj7VnhvYFsS34cv0J2qTVm5h9DvQuGwliVyVLVvE= +github.com/metacubex/sing-tun v0.2.7-0.20241106120309-53606a70db98 h1:vW0QDrzUc4k1yi3A76lDW064zonPj880QFcpTD58u7A= +github.com/metacubex/sing-tun v0.2.7-0.20241106120309-53606a70db98/go.mod h1:GRcrj7VnhvYFsS34cv0J2qTVm5h9DvQuGwliVyVLVvE= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosqY8xKDp3pqTW3qbrCprZ1l6WkrXSFSCwyY4I= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 h1:xg71VmzLS6ByAzi/57phwDvjE+dLLs+ozH00k4DnOns= From 215bf0995f5427dba0a5833e2ece115a0b4e6a5e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 8 Nov 2024 09:40:38 +0800 Subject: [PATCH 43/44] chore: switch syscall.SyscallN back to syscall.Syscall6 Until the current version, SyscallN always escapes the variadic argument --- component/process/process_windows.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/component/process/process_windows.go b/component/process/process_windows.go index f8cd00d8..b48a6430 100644 --- a/component/process/process_windows.go +++ b/component/process/process_windows.go @@ -180,7 +180,7 @@ func newSearcher(isV4, isTCP bool) *searcher { func getTransportTable(fn uintptr, family int, class int) ([]byte, error) { for size, buf := uint32(8), make([]byte, 8); ; { ptr := unsafe.Pointer(&buf[0]) - err, _, _ := syscall.SyscallN(fn, uintptr(ptr), uintptr(unsafe.Pointer(&size)), 0, uintptr(family), uintptr(class), 0) + err, _, _ := syscall.Syscall6(fn, uintptr(ptr), uintptr(unsafe.Pointer(&size)), 0, uintptr(family), uintptr(class), 0, 0) switch err { case 0: @@ -215,12 +215,15 @@ func getExecPathFromPID(pid uint32) (string, error) { buf := make([]uint16, syscall.MAX_LONG_PATH) size := uint32(len(buf)) - r1, _, err := syscall.SyscallN( + r1, _, err := syscall.Syscall6( queryProcName, uintptr(h), uintptr(0), uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&size)), + 0, + 0, + 0, ) if r1 == 0 { return "", err From 792f16265e3845b7922350d59ced224ac35b72c9 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:29:32 +0800 Subject: [PATCH 44/44] fix: find process panic --- component/process/process_windows.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/component/process/process_windows.go b/component/process/process_windows.go index b48a6430..73ac0255 100644 --- a/component/process/process_windows.go +++ b/component/process/process_windows.go @@ -180,7 +180,7 @@ func newSearcher(isV4, isTCP bool) *searcher { func getTransportTable(fn uintptr, family int, class int) ([]byte, error) { for size, buf := uint32(8), make([]byte, 8); ; { ptr := unsafe.Pointer(&buf[0]) - err, _, _ := syscall.Syscall6(fn, uintptr(ptr), uintptr(unsafe.Pointer(&size)), 0, uintptr(family), uintptr(class), 0, 0) + err, _, _ := syscall.Syscall6(fn, 6, uintptr(ptr), uintptr(unsafe.Pointer(&size)), 0, uintptr(family), uintptr(class), 0) switch err { case 0: @@ -216,15 +216,12 @@ func getExecPathFromPID(pid uint32) (string, error) { buf := make([]uint16, syscall.MAX_LONG_PATH) size := uint32(len(buf)) r1, _, err := syscall.Syscall6( - queryProcName, + queryProcName, 4, uintptr(h), uintptr(0), uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&size)), - 0, - 0, - 0, - ) + 0, 0) if r1 == 0 { return "", err }