diff --git a/component/mmdb/mmdb.go b/component/mmdb/mmdb.go index 14f6f997..16d4d2df 100644 --- a/component/mmdb/mmdb.go +++ b/component/mmdb/mmdb.go @@ -12,42 +12,59 @@ import ( C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" - "github.com/oschwald/geoip2-golang" + "github.com/oschwald/maxminddb-golang" +) + +type databaseType = uint8 + +const ( + typeMaxmind databaseType = iota + typeSing ) var ( - mmdb *geoip2.Reader - once sync.Once + reader Reader + once sync.Once ) func LoadFromBytes(buffer []byte) { once.Do(func() { - var err error - mmdb, err = geoip2.FromBytes(buffer) + mmdb, err := maxminddb.FromBytes(buffer) if err != nil { log.Fatalln("Can't load mmdb: %s", err.Error()) } + reader = Reader{Reader: mmdb} + if mmdb.Metadata.DatabaseType == "sing-geoip" { + reader.databaseType = typeSing + } else { + reader.databaseType = typeMaxmind + } }) } func Verify() bool { - instance, err := geoip2.Open(C.Path.MMDB()) + instance, err := maxminddb.Open(C.Path.MMDB()) if err == nil { instance.Close() } return err == nil } -func Instance() *geoip2.Reader { +func Instance() Reader { once.Do(func() { - var err error - mmdb, err = geoip2.Open(C.Path.MMDB()) + mmdb, err := maxminddb.Open(C.Path.MMDB()) if err != nil { log.Fatalln("Can't load mmdb: %s", err.Error()) } + reader = Reader{Reader: mmdb} + if mmdb.Metadata.DatabaseType == "sing-geoip" { + reader.databaseType = typeSing + } else { + reader.databaseType = typeMaxmind + } }) - return mmdb + return reader } func DownloadMMDB(path string) (err error) { diff --git a/component/mmdb/reader.go b/component/mmdb/reader.go new file mode 100644 index 00000000..35040344 --- /dev/null +++ b/component/mmdb/reader.go @@ -0,0 +1,36 @@ +package mmdb + +import ( + "fmt" + "net" + + "github.com/oschwald/maxminddb-golang" +) + +type geoip2Country struct { + Country struct { + IsoCode string `maxminddb:"iso_code"` + } `maxminddb:"country"` +} + +type Reader struct { + *maxminddb.Reader + databaseType +} + +func (r Reader) LookupCode(ipAddress net.IP) string { + switch r.databaseType { + case typeMaxmind: + var country geoip2Country + _ = r.Lookup(ipAddress, &country) + return country.Country.IsoCode + + case typeSing: + var code string + _ = r.Lookup(ipAddress, &code) + return code + + default: + panic(fmt.Sprint("unknown geoip database type:", r.databaseType)) + } +} diff --git a/config/updateGeo.go b/config/updateGeo.go index e76301ba..b75d3184 100644 --- a/config/updateGeo.go +++ b/config/updateGeo.go @@ -14,7 +14,7 @@ import ( clashHttp "github.com/Dreamacro/clash/component/http" C "github.com/Dreamacro/clash/constant" - "github.com/oschwald/geoip2-golang" + "github.com/oschwald/maxminddb-golang" ) func UpdateGeoDatabases() error { @@ -44,7 +44,7 @@ func UpdateGeoDatabases() error { return fmt.Errorf("can't download MMDB database file: %w", err) } - instance, err := geoip2.FromBytes(data) + instance, err := maxminddb.FromBytes(data) if err != nil { return fmt.Errorf("invalid MMDB database file: %s", err) } diff --git a/constant/path.go b/constant/path.go index 10d143d7..09f1d89b 100644 --- a/constant/path.go +++ b/constant/path.go @@ -91,7 +91,8 @@ func (p *path) MMDB() string { // 目录则直接跳过 continue } else { - if strings.EqualFold(fi.Name(), "Country.mmdb") { + if strings.EqualFold(fi.Name(), "Country.mmdb") || + strings.EqualFold(fi.Name(), "geoip.db") { GeoipName = fi.Name() return P.Join(p.homeDir, fi.Name()) } diff --git a/dns/filters.go b/dns/filters.go index 58b261ac..811fc976 100644 --- a/dns/filters.go +++ b/dns/filters.go @@ -2,6 +2,7 @@ package dns import ( "net/netip" + "strings" "github.com/Dreamacro/clash/component/geodata" "github.com/Dreamacro/clash/component/geodata/router" @@ -9,7 +10,6 @@ import ( "github.com/Dreamacro/clash/component/trie" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" - "strings" ) type fallbackIPFilter interface { @@ -24,8 +24,8 @@ var geoIPMatcher *router.GeoIPMatcher func (gf *geoipFilter) Match(ip netip.Addr) bool { if !C.GeodataMode { - record, _ := mmdb.Instance().Country(ip.AsSlice()) - return !strings.EqualFold(record.Country.IsoCode, gf.code) && !ip.IsPrivate() + code := mmdb.Instance().LookupCode(ip.AsSlice()) + return !strings.EqualFold(code, gf.code) && !ip.IsPrivate() } if geoIPMatcher == nil { diff --git a/go.mod b/go.mod index d3206598..bb6115ba 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,7 @@ require ( github.com/mroth/weightedrand/v2 v2.0.1 github.com/openacid/low v0.1.21 github.com/oschwald/geoip2-golang v1.9.0 + github.com/oschwald/maxminddb-golang v1.11.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 github.com/sagernet/sing v0.2.8-0.20230703002104-c68251b6d059 github.com/sagernet/sing-mux v0.1.1-0.20230703132253-2cedde0fbc90 @@ -80,7 +81,6 @@ require ( github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475 // indirect github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect - github.com/oschwald/maxminddb-golang v1.11.0 // indirect github.com/pierrec/lz4/v4 v4.1.14 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect diff --git a/rules/common/geoip.go b/rules/common/geoip.go index 0c134c63..07b342ac 100644 --- a/rules/common/geoip.go +++ b/rules/common/geoip.go @@ -40,8 +40,8 @@ func (g *GEOIP) Match(metadata *C.Metadata) (bool, string) { resolver.IsFakeBroadcastIP(ip), g.adapter } if !C.GeodataMode { - record, _ := mmdb.Instance().Country(ip.AsSlice()) - return strings.EqualFold(record.Country.IsoCode, g.country), g.adapter + code := mmdb.Instance().LookupCode(ip.AsSlice()) + return strings.EqualFold(code, g.country), g.adapter } return g.geoIPMatcher.Match(ip.AsSlice()), g.adapter }