From b0e76ec791a86c23cf142408ef88b866aebb649f Mon Sep 17 00:00:00 2001 From: H1JK Date: Mon, 17 Jul 2023 10:33:20 +0800 Subject: [PATCH] feat: Add Meta-geoip V0 database support --- component/mmdb/mmdb.go | 21 +++++++++++++++------ component/mmdb/reader.go | 26 +++++++++++++++++++++++--- constant/path.go | 3 ++- dns/filters.go | 9 +++++++-- rules/common/geoip.go | 9 +++++++-- 5 files changed, 54 insertions(+), 14 deletions(-) diff --git a/component/mmdb/mmdb.go b/component/mmdb/mmdb.go index 16d4d2df..5db8bee9 100644 --- a/component/mmdb/mmdb.go +++ b/component/mmdb/mmdb.go @@ -20,6 +20,7 @@ type databaseType = uint8 const ( typeMaxmind databaseType = iota typeSing + typeMetaV0 ) var ( @@ -34,9 +35,12 @@ func LoadFromBytes(buffer []byte) { log.Fatalln("Can't load mmdb: %s", err.Error()) } reader = Reader{Reader: mmdb} - if mmdb.Metadata.DatabaseType == "sing-geoip" { + switch mmdb.Metadata.DatabaseType { + case "sing-geoip": reader.databaseType = typeSing - } else { + case "Meta-geoip0": + reader.databaseType = typeMetaV0 + default: reader.databaseType = typeMaxmind } }) @@ -52,14 +56,19 @@ func Verify() bool { func Instance() Reader { once.Do(func() { - mmdb, err := maxminddb.Open(C.Path.MMDB()) + mmdbPath := C.Path.MMDB() + log.Debugln("Load MMDB file: %s", mmdbPath) + mmdb, err := maxminddb.Open(mmdbPath) if err != nil { - log.Fatalln("Can't load mmdb: %s", err.Error()) + log.Fatalln("Can't load MMDB: %s", err.Error()) } reader = Reader{Reader: mmdb} - if mmdb.Metadata.DatabaseType == "sing-geoip" { + switch mmdb.Metadata.DatabaseType { + case "sing-geoip": reader.databaseType = typeSing - } else { + case "Meta-geoip0": + reader.databaseType = typeMetaV0 + default: reader.databaseType = typeMaxmind } }) diff --git a/component/mmdb/reader.go b/component/mmdb/reader.go index 35040344..4db53d4f 100644 --- a/component/mmdb/reader.go +++ b/component/mmdb/reader.go @@ -5,6 +5,7 @@ import ( "net" "github.com/oschwald/maxminddb-golang" + "github.com/sagernet/sing/common" ) type geoip2Country struct { @@ -18,17 +19,36 @@ type Reader struct { databaseType } -func (r Reader) LookupCode(ipAddress net.IP) string { +func (r Reader) LookupCode(ipAddress net.IP) []string { switch r.databaseType { case typeMaxmind: var country geoip2Country _ = r.Lookup(ipAddress, &country) - return country.Country.IsoCode + if country.Country.IsoCode == "" { + return []string{} + } + return []string{country.Country.IsoCode} case typeSing: var code string _ = r.Lookup(ipAddress, &code) - return code + if code == "" { + return []string{} + } + return []string{code} + + case typeMetaV0: + var record any + _ = r.Lookup(ipAddress, &record) + switch record := record.(type) { + case string: + return []string{record} + case []any: // lookup returned type of slice is []any + return common.Map(record, func(it any) string { + return it.(string) + }) + } + return []string{} default: panic(fmt.Sprint("unknown geoip database type:", r.databaseType)) diff --git a/constant/path.go b/constant/path.go index 09f1d89b..e23ae886 100644 --- a/constant/path.go +++ b/constant/path.go @@ -92,7 +92,8 @@ func (p *path) MMDB() string { continue } else { if strings.EqualFold(fi.Name(), "Country.mmdb") || - strings.EqualFold(fi.Name(), "geoip.db") { + strings.EqualFold(fi.Name(), "geoip.db") || + strings.EqualFold(fi.Name(), "geoip.metadb") { GeoipName = fi.Name() return P.Join(p.homeDir, fi.Name()) } diff --git a/dns/filters.go b/dns/filters.go index 811fc976..47e7adcd 100644 --- a/dns/filters.go +++ b/dns/filters.go @@ -24,8 +24,13 @@ var geoIPMatcher *router.GeoIPMatcher func (gf *geoipFilter) Match(ip netip.Addr) bool { if !C.GeodataMode { - code := mmdb.Instance().LookupCode(ip.AsSlice()) - return !strings.EqualFold(code, gf.code) && !ip.IsPrivate() + codes := mmdb.Instance().LookupCode(ip.AsSlice()) + for _, code := range codes { + if !strings.EqualFold(code, gf.code) && !ip.IsPrivate() { + return true + } + } + return false } if geoIPMatcher == nil { diff --git a/rules/common/geoip.go b/rules/common/geoip.go index 07b342ac..2f96c2ef 100644 --- a/rules/common/geoip.go +++ b/rules/common/geoip.go @@ -40,8 +40,13 @@ func (g *GEOIP) Match(metadata *C.Metadata) (bool, string) { resolver.IsFakeBroadcastIP(ip), g.adapter } if !C.GeodataMode { - code := mmdb.Instance().LookupCode(ip.AsSlice()) - return strings.EqualFold(code, g.country), g.adapter + codes := mmdb.Instance().LookupCode(ip.AsSlice()) + for _, code := range codes { + if strings.EqualFold(code, g.country) { + return true, g.adapter + } + } + return false, g.adapter } return g.geoIPMatcher.Match(ip.AsSlice()), g.adapter }