feat: Add Meta-geoip V0 database support

This commit is contained in:
H1JK 2023-07-17 10:33:20 +08:00
parent a82745f544
commit b0e76ec791
5 changed files with 54 additions and 14 deletions

View file

@ -20,6 +20,7 @@ type databaseType = uint8
const ( const (
typeMaxmind databaseType = iota typeMaxmind databaseType = iota
typeSing typeSing
typeMetaV0
) )
var ( var (
@ -34,9 +35,12 @@ func LoadFromBytes(buffer []byte) {
log.Fatalln("Can't load mmdb: %s", err.Error()) log.Fatalln("Can't load mmdb: %s", err.Error())
} }
reader = Reader{Reader: mmdb} reader = Reader{Reader: mmdb}
if mmdb.Metadata.DatabaseType == "sing-geoip" { switch mmdb.Metadata.DatabaseType {
case "sing-geoip":
reader.databaseType = typeSing reader.databaseType = typeSing
} else { case "Meta-geoip0":
reader.databaseType = typeMetaV0
default:
reader.databaseType = typeMaxmind reader.databaseType = typeMaxmind
} }
}) })
@ -52,14 +56,19 @@ func Verify() bool {
func Instance() Reader { func Instance() Reader {
once.Do(func() { 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 { 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} reader = Reader{Reader: mmdb}
if mmdb.Metadata.DatabaseType == "sing-geoip" { switch mmdb.Metadata.DatabaseType {
case "sing-geoip":
reader.databaseType = typeSing reader.databaseType = typeSing
} else { case "Meta-geoip0":
reader.databaseType = typeMetaV0
default:
reader.databaseType = typeMaxmind reader.databaseType = typeMaxmind
} }
}) })

View file

@ -5,6 +5,7 @@ import (
"net" "net"
"github.com/oschwald/maxminddb-golang" "github.com/oschwald/maxminddb-golang"
"github.com/sagernet/sing/common"
) )
type geoip2Country struct { type geoip2Country struct {
@ -18,17 +19,36 @@ type Reader struct {
databaseType databaseType
} }
func (r Reader) LookupCode(ipAddress net.IP) string { func (r Reader) LookupCode(ipAddress net.IP) []string {
switch r.databaseType { switch r.databaseType {
case typeMaxmind: case typeMaxmind:
var country geoip2Country var country geoip2Country
_ = r.Lookup(ipAddress, &country) _ = r.Lookup(ipAddress, &country)
return country.Country.IsoCode if country.Country.IsoCode == "" {
return []string{}
}
return []string{country.Country.IsoCode}
case typeSing: case typeSing:
var code string var code string
_ = r.Lookup(ipAddress, &code) _ = 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: default:
panic(fmt.Sprint("unknown geoip database type:", r.databaseType)) panic(fmt.Sprint("unknown geoip database type:", r.databaseType))

View file

@ -92,7 +92,8 @@ func (p *path) MMDB() string {
continue continue
} else { } else {
if strings.EqualFold(fi.Name(), "Country.mmdb") || 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() GeoipName = fi.Name()
return P.Join(p.homeDir, fi.Name()) return P.Join(p.homeDir, fi.Name())
} }

View file

@ -24,8 +24,13 @@ var geoIPMatcher *router.GeoIPMatcher
func (gf *geoipFilter) Match(ip netip.Addr) bool { func (gf *geoipFilter) Match(ip netip.Addr) bool {
if !C.GeodataMode { if !C.GeodataMode {
code := mmdb.Instance().LookupCode(ip.AsSlice()) codes := mmdb.Instance().LookupCode(ip.AsSlice())
return !strings.EqualFold(code, gf.code) && !ip.IsPrivate() for _, code := range codes {
if !strings.EqualFold(code, gf.code) && !ip.IsPrivate() {
return true
}
}
return false
} }
if geoIPMatcher == nil { if geoIPMatcher == nil {

View file

@ -40,8 +40,13 @@ func (g *GEOIP) Match(metadata *C.Metadata) (bool, string) {
resolver.IsFakeBroadcastIP(ip), g.adapter resolver.IsFakeBroadcastIP(ip), g.adapter
} }
if !C.GeodataMode { if !C.GeodataMode {
code := mmdb.Instance().LookupCode(ip.AsSlice()) codes := mmdb.Instance().LookupCode(ip.AsSlice())
return strings.EqualFold(code, g.country), g.adapter 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 return g.geoIPMatcher.Match(ip.AsSlice()), g.adapter
} }