diff --git a/.gitignore b/.gitignore index 59b9502..d383c56 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1 @@ -testdata/GeoIP2-City.mmdb -testdata/GeoIP2-Connection-Type.mmdb -testdata/GeoIP2-Country.mmdb -testdata/GeoIP2-ISP.mmdb -testdata/GeoLite2-ASN.mmdb -testdata/GeoLite2-City.mmdb -testdata/GeoLite2-Country.mmdb - -testdata/GeoIP2-Anonymous-IP-Test.mmdb -testdata/GeoIP2-City-Test.mmdb -testdata/GeoIP2-Connection-Type-Test.mmdb -testdata/GeoIP2-Country-Test.mmdb -testdata/GeoIP2-Domain-Test.mmdb -testdata/GeoIP2-Enterprise-Test.mmdb -testdata/GeoIP2-ISP-Test.mmdb -testdata/GeoLite2-ASN-Test.mmdb +testdata diff --git a/README.md b/README.md index d8bda1e..5e4c9c9 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,38 @@ connection_type-24 3883234 305 ns/op 32 B/op 2 allocs/o connection_type_parallel-24 34284831 32.1 ns/op 32 B/op 2 allocs/op ``` +## Supported databases types + +### Country +- GeoIP2-Country +- GeoLite2-Country +- DBIP-Country +- DBIP-Country-Lite +- Geoacumen-Country + +### City +- GeoIP2-City +- GeoLite2-City +- GeoIP2-Enterprise +- DBIP-City-Lite + +### ISP +- GeoIP2-ISP + +### ASN +- GeoLite2-ASN +- DBIP-ASN-Lite +- DBIP-ASN-Lite (compat=GeoLite2-ASN) + +### Connection Type +- GeoIP2-Connection-Type + +### Anonymous IP +- GeoIP2-Anonymous-IP + +### Domain +- GeoIP2-Domain + ## License [MIT License](LICENSE). diff --git a/reader.go b/reader.go index 918b0bf..b919da6 100644 --- a/reader.go +++ b/reader.go @@ -122,3 +122,15 @@ func newReader(buffer []byte) (*reader, error) { } return reader, nil } + +func isExpectedDatabaseType(databaseType string, expectedTypes ...string) bool { + if len(expectedTypes) == 0 { + return true + } + for _, expectedType := range expectedTypes { + if databaseType == expectedType { + return true + } + } + return false +} diff --git a/reader_anonymous_ip.go b/reader_anonymous_ip.go index 1768aff..98927ce 100644 --- a/reader_anonymous_ip.go +++ b/reader_anonymous_ip.go @@ -49,12 +49,15 @@ func (r *AnonymousIPReader) Lookup(ip net.IP) (*AnonymousIP, error) { return result, nil } -func NewAnonymousIPReader(buffer []byte) (*AnonymousIPReader, error) { +// NewAnonymousIPReaderWithType creates a new AnonymousIPReader that accepts MMDB files with a custom database type. +// Note that AnonymousIPReader only implements the fields provided by MaxMind GeoIP2-Anonymous-IP databases, and will +// ignore other fields. It is up to the developer to ensure that the database provides a compatible selection of fields. +func NewAnonymousIPReaderWithType(buffer []byte, expectedTypes ...string) (*AnonymousIPReader, error) { reader, err := newReader(buffer) if err != nil { return nil, err } - if reader.metadata.DatabaseType != "GeoIP2-Anonymous-IP" { + if !isExpectedDatabaseType(reader.metadata.DatabaseType, expectedTypes...) { return nil, errors.New("wrong MaxMind DB Anonymous-IP type: " + reader.metadata.DatabaseType) } return &AnonymousIPReader{ @@ -62,6 +65,10 @@ func NewAnonymousIPReader(buffer []byte) (*AnonymousIPReader, error) { }, nil } +func NewAnonymousIPReader(buffer []byte) (*AnonymousIPReader, error) { + return NewAnonymousIPReaderWithType(buffer, "GeoIP2-Anonymous-IP") +} + func NewAnonymousIPReaderFromFile(filename string) (*AnonymousIPReader, error) { buffer, err := ioutil.ReadFile(filename) if err != nil { diff --git a/reader_asn.go b/reader_asn.go index 44b7470..b117f12 100644 --- a/reader_asn.go +++ b/reader_asn.go @@ -49,12 +49,15 @@ func (r *ASNReader) Lookup(ip net.IP) (*ASN, error) { return result, nil } -func NewASNReader(buffer []byte) (*ASNReader, error) { +// NewASNReaderWithType creates a new ASNReader that accepts MMDB files with a custom database type. Note that +// ASNReader only implements the fields provided by MaxMind GeoLite2-ASN databases, and will ignore other fields. +// It is up to the developer to ensure that the database provides a compatible selection of fields. +func NewASNReaderWithType(buffer []byte, expectedTypes ...string) (*ASNReader, error) { reader, err := newReader(buffer) if err != nil { return nil, err } - if reader.metadata.DatabaseType != "GeoLite2-ASN" { + if !isExpectedDatabaseType(reader.metadata.DatabaseType, expectedTypes...) { return nil, errors.New("wrong MaxMind DB ASN type: " + reader.metadata.DatabaseType) } return &ASNReader{ @@ -62,6 +65,10 @@ func NewASNReader(buffer []byte) (*ASNReader, error) { }, nil } +func NewASNReader(buffer []byte) (*ASNReader, error) { + return NewASNReaderWithType(buffer, "GeoLite2-ASN", "DBIP-ASN-Lite", "DBIP-ASN-Lite (compat=GeoLite2-ASN)") +} + func NewASNReaderFromFile(filename string) (*ASNReader, error) { buffer, err := ioutil.ReadFile(filename) if err != nil { diff --git a/reader_city.go b/reader_city.go index 6ccd8ce..55f764d 100644 --- a/reader_city.go +++ b/reader_city.go @@ -83,14 +83,15 @@ func (r *CityReader) Lookup(ip net.IP) (*CityResult, error) { return result, nil } -func NewCityReader(buffer []byte) (*CityReader, error) { +// NewCityReaderWithType creates a new CityReader that accepts MMDB files with a custom database type. Note that +// CityReader only implements the fields provided by MaxMind Geo*-City and GeoIP2-Enterprise databases, and will ignore +// other fields. It is up to the developer to ensure that the database provides a compatible selection of fields. +func NewCityReaderWithType(buffer []byte, expectedTypes ...string) (*CityReader, error) { reader, err := newReader(buffer) if err != nil { return nil, err } - if reader.metadata.DatabaseType != "GeoIP2-City" && - reader.metadata.DatabaseType != "GeoLite2-City" && - reader.metadata.DatabaseType != "GeoIP2-Enterprise" { + if !isExpectedDatabaseType(reader.metadata.DatabaseType, expectedTypes...) { return nil, errors.New("wrong MaxMind DB City type: " + reader.metadata.DatabaseType) } return &CityReader{ @@ -98,6 +99,10 @@ func NewCityReader(buffer []byte) (*CityReader, error) { }, nil } +func NewCityReader(buffer []byte) (*CityReader, error) { + return NewCityReaderWithType(buffer, "GeoIP2-City", "GeoLite2-City", "GeoIP2-Enterprise", "DBIP-City-Lite") +} + func NewCityReaderFromFile(filename string) (*CityReader, error) { buffer, err := ioutil.ReadFile(filename) if err != nil { diff --git a/reader_connection_type.go b/reader_connection_type.go index cb0e748..9863681 100644 --- a/reader_connection_type.go +++ b/reader_connection_type.go @@ -49,12 +49,16 @@ func (r *ConnectionTypeReader) Lookup(ip net.IP) (string, error) { return result.ConnectionType, nil } -func NewConnectionTypeReader(buffer []byte) (*ConnectionTypeReader, error) { +// NewConnectionTypeReaderWithType creates a new ConnectionTypeReader that accepts MMDB files with a custom database +// type. Note that ConnectionTypeReader only implements the fields provided by MaxMind GeoIP2-Connection-Type databases, +// and will ignore other fields. It is up to the developer to ensure that the database provides a compatible selection +// of fields. +func NewConnectionTypeReaderWithType(buffer []byte, expectedTypes ...string) (*ConnectionTypeReader, error) { reader, err := newReader(buffer) if err != nil { return nil, err } - if reader.metadata.DatabaseType != "GeoIP2-Connection-Type" { + if !isExpectedDatabaseType(reader.metadata.DatabaseType, expectedTypes...) { return nil, errors.New("wrong MaxMind DB Connection-Type type: " + reader.metadata.DatabaseType) } return &ConnectionTypeReader{ @@ -62,6 +66,10 @@ func NewConnectionTypeReader(buffer []byte) (*ConnectionTypeReader, error) { }, nil } +func NewConnectionTypeReader(buffer []byte) (*ConnectionTypeReader, error) { + return NewConnectionTypeReaderWithType(buffer, "GeoIP2-Connection-Type") +} + func NewConnectionTypeReaderFromFile(filename string) (*ConnectionTypeReader, error) { buffer, err := ioutil.ReadFile(filename) if err != nil { diff --git a/reader_country.go b/reader_country.go index e1e804b..f0ed3d9 100644 --- a/reader_country.go +++ b/reader_country.go @@ -63,13 +63,15 @@ func (r *CountryReader) Lookup(ip net.IP) (*CountryResult, error) { return result, nil } -func NewCountryReader(buffer []byte) (*CountryReader, error) { +// NewCountryReaderWithType creates a new CountryReader that accepts MMDB files with a custom database type. Note that +// CountryReader only implements the fields provided by MaxMind Geo*-Country databases, and will ignore other fields. +// It is up to the developer to ensure that the database provides a compatible selection of fields. +func NewCountryReaderWithType(buffer []byte, expectedTypes ...string) (*CountryReader, error) { reader, err := newReader(buffer) if err != nil { return nil, err } - if reader.metadata.DatabaseType != "GeoIP2-Country" && - reader.metadata.DatabaseType != "GeoLite2-Country" { + if !isExpectedDatabaseType(reader.metadata.DatabaseType, expectedTypes...) { return nil, errors.New("wrong MaxMind DB Country type: " + reader.metadata.DatabaseType) } return &CountryReader{ @@ -77,6 +79,10 @@ func NewCountryReader(buffer []byte) (*CountryReader, error) { }, nil } +func NewCountryReader(buffer []byte) (*CountryReader, error) { + return NewCountryReaderWithType(buffer, "GeoIP2-Country", "GeoLite2-Country", "Geoacumen-Country", "DBIP-Country", "DBIP-Country-Lite") +} + func NewCountryReaderFromFile(filename string) (*CountryReader, error) { buffer, err := ioutil.ReadFile(filename) if err != nil { diff --git a/reader_domain.go b/reader_domain.go index 12ac07e..9e0f861 100644 --- a/reader_domain.go +++ b/reader_domain.go @@ -49,12 +49,15 @@ func (r *DomainReader) Lookup(ip net.IP) (string, error) { return result.Domain, nil } -func NewDomainReader(buffer []byte) (*DomainReader, error) { +// NewDomainReaderWithType creates a new DomainReader that accepts MMDB files with a custom database type. Note that +// DomainReader only implements the fields provided by MaxMind GeoIP2-Domain databases, and will ignore other fields. +// It is up to the developer to ensure that the database provides a compatible selection of fields. +func NewDomainReaderWithType(buffer []byte, expectedTypes ...string) (*DomainReader, error) { reader, err := newReader(buffer) if err != nil { return nil, err } - if reader.metadata.DatabaseType != "GeoIP2-Domain" { + if !isExpectedDatabaseType(reader.metadata.DatabaseType, expectedTypes...) { return nil, errors.New("wrong MaxMind DB Domain type: " + reader.metadata.DatabaseType) } return &DomainReader{ @@ -62,6 +65,10 @@ func NewDomainReader(buffer []byte) (*DomainReader, error) { }, nil } +func NewDomainReader(buffer []byte) (*DomainReader, error) { + return NewDomainReaderWithType(buffer, "GeoIP2-Domain") +} + func NewDomainReaderFromFile(filename string) (*DomainReader, error) { buffer, err := ioutil.ReadFile(filename) if err != nil { diff --git a/reader_isp.go b/reader_isp.go index 068c13d..c069bd4 100644 --- a/reader_isp.go +++ b/reader_isp.go @@ -49,12 +49,15 @@ func (r *ISPReader) Lookup(ip net.IP) (*ISP, error) { return result, nil } -func NewISPReader(buffer []byte) (*ISPReader, error) { +// NewISPReaderWithType creates a new ISPReader that accepts MMDB files with a custom database type. Note that +// ISPReader only implements the fields provided by MaxMind GeoIP2-ISP databases, and will ignore other fields. +// It is up to the developer to ensure that the database provides a compatible selection of fields. +func NewISPReaderWithType(buffer []byte, expectedTypes ...string) (*ISPReader, error) { reader, err := newReader(buffer) if err != nil { return nil, err } - if reader.metadata.DatabaseType != "GeoIP2-ISP" { + if !isExpectedDatabaseType(reader.metadata.DatabaseType, expectedTypes...) { return nil, errors.New("wrong MaxMind DB ISP type: " + reader.metadata.DatabaseType) } return &ISPReader{ @@ -62,6 +65,10 @@ func NewISPReader(buffer []byte) (*ISPReader, error) { }, nil } +func NewISPReader(buffer []byte) (*ISPReader, error) { + return NewISPReaderWithType(buffer, "GeoIP2-ISP") +} + func NewISPReaderFromFile(filename string) (*ISPReader, error) { buffer, err := ioutil.ReadFile(filename) if err != nil { diff --git a/reader_test.go b/reader_test.go index 9ed1e4a..9182a2d 100644 --- a/reader_test.go +++ b/reader_test.go @@ -1,4 +1,6 @@ -// Test DB https://github.com/maxmind/MaxMind-DB +// Test DB +// https://github.com/maxmind/MaxMind-DB +// https://db-ip.com/db/lite.php package geoip2 import ( @@ -406,3 +408,83 @@ func TestASN(t *testing.T) { t.Fatal() } } + +func TestDBIPCity(t *testing.T) { + reader, err := NewCityReaderFromFile("testdata/dbip-city-lite.mmdb") + if err != nil { + t.Fatal(err) + } + record, err := reader.Lookup(net.ParseIP("66.30.184.198")) + if err != nil { + t.Fatal(err) + } + if record.City.GeoNameID != 0 { + t.Fatal() + } + if record.City.Names["en"] != "Boston" { + t.Fatal() + } + if record.Location.Latitude != 42.3601 { + t.Fatal() + } + if record.Location.Longitude != -71.0589 { + t.Fatal() + } + if len(record.Subdivisions) != 1 { + t.Fatal() + } + if record.Subdivisions[0].Names["en"] != "Massachusetts" { + t.Fatal() + } +} + +func TestDBIPCountry(t *testing.T) { + reader, err := NewCountryReaderFromFile("testdata/dbip-country-lite.mmdb") + if err != nil { + t.Fatal(err) + } + record, err := reader.Lookup(net.ParseIP("66.30.184.198")) + if err != nil { + t.Fatal(err) + } + if record.Continent.GeoNameID != 6255149 { + t.Fatal() + } + if record.Continent.Code != "NA" { + t.Fatal() + } + if record.Continent.Names["en"] != "North America" || + record.Continent.Names["ru"] != "Северная Америка" { + t.Fatal() + } + if record.Country.GeoNameID != 6252001 { + t.Fatal() + } + if record.Country.ISOCode != "US" { + t.Fatal() + } + if record.Country.Names["fr"] != "États-Unis" || + record.Country.Names["pt-BR"] != "Estados Unidos" { + t.Fatal() + } + if record.Country.IsInEuropeanUnion { + t.Fatal() + } +} + +func TestDBIPASN(t *testing.T) { + reader, err := NewASNReaderFromFile("testdata/dbip-asn-lite.mmdb") + if err != nil { + t.Fatal(err) + } + record, err := reader.Lookup(net.ParseIP("66.30.184.198")) + if err != nil { + t.Fatal(err) + } + if record.AutonomousSystemNumber != 7922 { + t.Fatal() + } + if record.AutonomousSystemOrganization != "Comcast Cable Communications, LLC" { + t.Fatal() + } +}