pax_global_header00006660000000000000000000000064130772216420014516gustar00rootroot0000000000000052 comment=5b1dc16861f81d05d9836bb21c2d0d65282fc0b8 geoip2-golang-1.1.0/000077500000000000000000000000001307722164200141475ustar00rootroot00000000000000geoip2-golang-1.1.0/.gitignore000066400000000000000000000000251307722164200161340ustar00rootroot00000000000000.vscode *.out *.test geoip2-golang-1.1.0/.gitmodules000066400000000000000000000001311307722164200163170ustar00rootroot00000000000000[submodule "test-data"] path = test-data url = git://github.com/maxmind/MaxMind-DB.git geoip2-golang-1.1.0/.travis.yml000066400000000000000000000006461307722164200162660ustar00rootroot00000000000000language: go go: - 1.4 - 1.5 - 1.6 - 1.7 - 1.8 - tip matrix: allow_failures: - go: tip before_install: - "if [[ $TRAVIS_GO_VERSION == 1.8 ]]; then go get -v github.com/golang/lint/golint; fi" script: - go test -race -cpu 1,4 -v - go test -race -v -tags appengine - "if [[ $TRAVIS_GO_VERSION == 1.8 ]]; then go vet ./...; fi" - "if [[ $TRAVIS_GO_VERSION == 1.8 ]]; then golint .; fi" sudo: false geoip2-golang-1.1.0/LICENSE000066400000000000000000000014041307722164200151530ustar00rootroot00000000000000ISC License Copyright (c) 2015, Gregory J. Oschwald Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. geoip2-golang-1.1.0/README.md000066400000000000000000000050511307722164200154270ustar00rootroot00000000000000# GeoIP2 Reader for Go # [![Build Status](https://travis-ci.org/oschwald/geoip2-golang.png?branch=master)](https://travis-ci.org/oschwald/geoip2-golang) [![GoDoc](https://godoc.org/github.com/oschwald/geoip2-golang?status.png)](https://godoc.org/github.com/oschwald/geoip2-golang) This library reads MaxMind [GeoLite2](http://dev.maxmind.com/geoip/geoip2/geolite2/) and [GeoIP2](http://www.maxmind.com/en/geolocation_landing) databases. This library is built using [the Go maxminddb reader](https://github.com/oschwald/maxminddb-golang). All data for the database record is decoded using this library. If you only need several fields, you may get superior performance by using maxminddb's `Lookup` directly with a result struct that only contains the required fields. (See [example_test.go](https://github.com/oschwald/maxminddb-golang/blob/master/example_test.go) in the maxminddb repository for an example of this.) ## Installation ## ``` go get github.com/oschwald/geoip2-golang ``` ## Usage ## [See GoDoc](http://godoc.org/github.com/oschwald/geoip2-golang) for documentation and examples. ## Example ## ```go package main import ( "fmt" "github.com/oschwald/geoip2-golang" "log" "net" ) func main() { db, err := geoip2.Open("GeoIP2-City.mmdb") if err != nil { log.Fatal(err) } defer db.Close() // If you are using strings that may be invalid, check that ip is not nil ip := net.ParseIP("81.2.69.142") record, err := db.City(ip) if err != nil { log.Fatal(err) } fmt.Printf("Portuguese (BR) city name: %v\n", record.City.Names["pt-BR"]) fmt.Printf("English subdivision name: %v\n", record.Subdivisions[0].Names["en"]) fmt.Printf("Russian country name: %v\n", record.Country.Names["ru"]) fmt.Printf("ISO country code: %v\n", record.Country.IsoCode) fmt.Printf("Time zone: %v\n", record.Location.TimeZone) fmt.Printf("Coordinates: %v, %v\n", record.Location.Latitude, record.Location.Longitude) // Output: // Portuguese (BR) city name: Londres // English subdivision name: England // Russian country name: Великобритания // ISO country code: GB // Time zone: Europe/London // Coordinates: 51.5142, -0.0931 } ``` ## Testing ## Make sure you checked out test data submodule: ``` git submodule init git submodule update ``` Execute test suite: ``` go test ``` ## Contributing ## Contributions welcome! Please fork the repository and open a pull request with your changes. ## License ## This is free software, licensed under the ISC license. geoip2-golang-1.1.0/example_test.go000066400000000000000000000021771307722164200171770ustar00rootroot00000000000000package geoip2 import ( "fmt" "log" "net" ) // Example provides a basic example of using the API. Use of the Country // method is analogous to that of the City method. func Example() { db, err := Open("test-data/test-data/GeoIP2-City-Test.mmdb") if err != nil { log.Fatal(err) } defer db.Close() // If you are using strings that may be invalid, check that ip is not nil ip := net.ParseIP("81.2.69.142") record, err := db.City(ip) if err != nil { log.Fatal(err) } fmt.Printf("Portuguese (BR) city name: %v\n", record.City.Names["pt-BR"]) fmt.Printf("English subdivision name: %v\n", record.Subdivisions[0].Names["en"]) fmt.Printf("Russian country name: %v\n", record.Country.Names["ru"]) fmt.Printf("ISO country code: %v\n", record.Country.IsoCode) fmt.Printf("Time zone: %v\n", record.Location.TimeZone) fmt.Printf("Coordinates: %v, %v\n", record.Location.Latitude, record.Location.Longitude) // Output: // Portuguese (BR) city name: Londres // English subdivision name: England // Russian country name: Великобритания // ISO country code: GB // Time zone: Europe/London // Coordinates: 51.5142, -0.0931 } geoip2-golang-1.1.0/reader.go000066400000000000000000000247561307722164200157560ustar00rootroot00000000000000// Package geoip2 provides an easy-to-use API for the MaxMind GeoIP2 and // GeoLite2 databases; this package does not support GeoIP Legacy databases. // // The structs provided by this package match the internal structure of // the data in the MaxMind databases. // // See github.com/oschwald/maxminddb-golang for more advanced used cases. package geoip2 import ( "fmt" "net" "github.com/oschwald/maxminddb-golang" ) // The City struct corresponds to the data in the GeoIP2/GeoLite2 City // databases. type City struct { City struct { GeoNameID uint `maxminddb:"geoname_id"` Names map[string]string `maxminddb:"names"` } `maxminddb:"city"` Continent struct { Code string `maxminddb:"code"` GeoNameID uint `maxminddb:"geoname_id"` Names map[string]string `maxminddb:"names"` } `maxminddb:"continent"` Country struct { GeoNameID uint `maxminddb:"geoname_id"` IsoCode string `maxminddb:"iso_code"` Names map[string]string `maxminddb:"names"` } `maxminddb:"country"` Location struct { AccuracyRadius uint16 `maxminddb:"accuracy_radius"` Latitude float64 `maxminddb:"latitude"` Longitude float64 `maxminddb:"longitude"` MetroCode uint `maxminddb:"metro_code"` TimeZone string `maxminddb:"time_zone"` } `maxminddb:"location"` Postal struct { Code string `maxminddb:"code"` } `maxminddb:"postal"` RegisteredCountry struct { GeoNameID uint `maxminddb:"geoname_id"` IsoCode string `maxminddb:"iso_code"` Names map[string]string `maxminddb:"names"` } `maxminddb:"registered_country"` RepresentedCountry struct { GeoNameID uint `maxminddb:"geoname_id"` IsoCode string `maxminddb:"iso_code"` Names map[string]string `maxminddb:"names"` Type string `maxminddb:"type"` } `maxminddb:"represented_country"` Subdivisions []struct { GeoNameID uint `maxminddb:"geoname_id"` IsoCode string `maxminddb:"iso_code"` Names map[string]string `maxminddb:"names"` } `maxminddb:"subdivisions"` Traits struct { IsAnonymousProxy bool `maxminddb:"is_anonymous_proxy"` IsSatelliteProvider bool `maxminddb:"is_satellite_provider"` } `maxminddb:"traits"` } // The Country struct corresponds to the data in the GeoIP2/GeoLite2 // Country databases. type Country struct { Continent struct { Code string `maxminddb:"code"` GeoNameID uint `maxminddb:"geoname_id"` Names map[string]string `maxminddb:"names"` } `maxminddb:"continent"` Country struct { GeoNameID uint `maxminddb:"geoname_id"` IsoCode string `maxminddb:"iso_code"` Names map[string]string `maxminddb:"names"` } `maxminddb:"country"` RegisteredCountry struct { GeoNameID uint `maxminddb:"geoname_id"` IsoCode string `maxminddb:"iso_code"` Names map[string]string `maxminddb:"names"` } `maxminddb:"registered_country"` RepresentedCountry struct { GeoNameID uint `maxminddb:"geoname_id"` IsoCode string `maxminddb:"iso_code"` Names map[string]string `maxminddb:"names"` Type string `maxminddb:"type"` } `maxminddb:"represented_country"` Traits struct { IsAnonymousProxy bool `maxminddb:"is_anonymous_proxy"` IsSatelliteProvider bool `maxminddb:"is_satellite_provider"` } `maxminddb:"traits"` } // The AnonymousIP struct corresponds to the data in the GeoIP2 // Anonymous IP database. type AnonymousIP struct { IsAnonymous bool `maxminddb:"is_anonymous"` IsAnonymousVPN bool `maxminddb:"is_anonymous_vpn"` IsHostingProvider bool `maxminddb:"is_hosting_provider"` IsPublicProxy bool `maxminddb:"is_public_proxy"` IsTorExitNode bool `maxminddb:"is_tor_exit_node"` } // The ASN struct corresponds to the data in the GeoLite2 ASN database. type ASN struct { AutonomousSystemNumber uint `maxminddb:"autonomous_system_number"` AutonomousSystemOrganization string `maxminddb:"autonomous_system_organization"` } // The ConnectionType struct corresponds to the data in the GeoIP2 // Connection-Type database. type ConnectionType struct { ConnectionType string `maxminddb:"connection_type"` } // The Domain struct corresponds to the data in the GeoIP2 Domain database. type Domain struct { Domain string `maxminddb:"domain"` } // The ISP struct corresponds to the data in the GeoIP2 ISP database. type ISP struct { AutonomousSystemNumber uint `maxminddb:"autonomous_system_number"` AutonomousSystemOrganization string `maxminddb:"autonomous_system_organization"` ISP string `maxminddb:"isp"` Organization string `maxminddb:"organization"` } type databaseType int const ( isAnonymousIP = 1 << iota isASN isCity isConnectionType isCountry isDomain isEnterprise isISP ) // Reader holds the maxminddb.Reader struct. It can be created using the // Open and FromBytes functions. type Reader struct { mmdbReader *maxminddb.Reader databaseType databaseType } // InvalidMethodError is returned when a lookup method is called on a // database that it does not support. For instance, calling the ISP method // on a City database. type InvalidMethodError struct { Method string DatabaseType string } func (e InvalidMethodError) Error() string { return fmt.Sprintf(`geoip2: the %s method does not support the %s database`, e.Method, e.DatabaseType) } // UnknownDatabaseTypeError is returned when an unknown database type is // opened. type UnknownDatabaseTypeError struct { DatabaseType string } func (e UnknownDatabaseTypeError) Error() string { return fmt.Sprintf(`geoip2: reader does not support the "%s" database type`, e.DatabaseType) } // Open takes a string path to a file and returns a Reader struct or an error. // The database file is opened using a memory map. Use the Close method on the // Reader object to return the resources to the system. func Open(file string) (*Reader, error) { reader, err := maxminddb.Open(file) if err != nil { return nil, err } dbType, err := getDBType(reader) return &Reader{reader, dbType}, err } // FromBytes takes a byte slice corresponding to a GeoIP2/GeoLite2 database // file and returns a Reader struct or an error. Note that the byte slice is // use directly; any modification of it after opening the database will result // in errors while reading from the database. func FromBytes(bytes []byte) (*Reader, error) { reader, err := maxminddb.FromBytes(bytes) if err != nil { return nil, err } dbType, err := getDBType(reader) return &Reader{reader, dbType}, err } func getDBType(reader *maxminddb.Reader) (databaseType, error) { switch reader.Metadata.DatabaseType { case "GeoIP2-Anonymous-IP": return isAnonymousIP, nil case "GeoLite2-ASN": return isASN, nil // We allow City lookups on Country for back compat case "GeoLite2-City", "GeoIP2-City", "GeoIP2-City-Africa", "GeoIP2-City-Asia-Pacific", "GeoIP2-City-Europe", "GeoIP2-City-North-America", "GeoIP2-City-South-America", "GeoIP2-Precision-City", "GeoLite2-Country", "GeoIP2-Country": return isCity | isCountry, nil case "GeoIP2-Connection-Type": return isConnectionType, nil case "GeoIP2-Domain": return isDomain, nil case "GeoIP2-Enterprise": return isEnterprise | isCity | isCountry, nil case "GeoIP2-ISP", "GeoIP2-Precision-ISP": return isISP, nil default: return 0, UnknownDatabaseTypeError{reader.Metadata.DatabaseType} } } // City takes an IP address as a net.IP struct and returns a City struct // and/or an error. Although this can be used with other databases, this // method generally should be used with the GeoIP2 or GeoLite2 City databases. func (r *Reader) City(ipAddress net.IP) (*City, error) { if isCity&r.databaseType == 0 { return nil, InvalidMethodError{"City", r.Metadata().DatabaseType} } var city City err := r.mmdbReader.Lookup(ipAddress, &city) return &city, err } // Country takes an IP address as a net.IP struct and returns a Country struct // and/or an error. Although this can be used with other databases, this // method generally should be used with the GeoIP2 or GeoLite2 Country // databases. func (r *Reader) Country(ipAddress net.IP) (*Country, error) { if isCountry&r.databaseType == 0 { return nil, InvalidMethodError{"Country", r.Metadata().DatabaseType} } var country Country err := r.mmdbReader.Lookup(ipAddress, &country) return &country, err } // AnonymousIP takes an IP address as a net.IP struct and returns a // AnonymousIP struct and/or an error. func (r *Reader) AnonymousIP(ipAddress net.IP) (*AnonymousIP, error) { if isAnonymousIP&r.databaseType == 0 { return nil, InvalidMethodError{"AnonymousIP", r.Metadata().DatabaseType} } var anonIP AnonymousIP err := r.mmdbReader.Lookup(ipAddress, &anonIP) return &anonIP, err } // ASN takes an IP address as a net.IP struct and returns a ASN struct and/or // an error func (r *Reader) ASN(ipAddress net.IP) (*ASN, error) { if isASN&r.databaseType == 0 { return nil, InvalidMethodError{"ASN", r.Metadata().DatabaseType} } var val ASN err := r.mmdbReader.Lookup(ipAddress, &val) return &val, err } // ConnectionType takes an IP address as a net.IP struct and returns a // ConnectionType struct and/or an error func (r *Reader) ConnectionType(ipAddress net.IP) (*ConnectionType, error) { if isConnectionType&r.databaseType == 0 { return nil, InvalidMethodError{"ConnectionType", r.Metadata().DatabaseType} } var val ConnectionType err := r.mmdbReader.Lookup(ipAddress, &val) return &val, err } // Domain takes an IP address as a net.IP struct and returns a // Domain struct and/or an error func (r *Reader) Domain(ipAddress net.IP) (*Domain, error) { if isDomain&r.databaseType == 0 { return nil, InvalidMethodError{"Domain", r.Metadata().DatabaseType} } var val Domain err := r.mmdbReader.Lookup(ipAddress, &val) return &val, err } // ISP takes an IP address as a net.IP struct and returns a ISP struct and/or // an error func (r *Reader) ISP(ipAddress net.IP) (*ISP, error) { if isISP&r.databaseType == 0 { return nil, InvalidMethodError{"ISP", r.Metadata().DatabaseType} } var val ISP err := r.mmdbReader.Lookup(ipAddress, &val) return &val, err } // Metadata takes no arguments and returns a struct containing metadata about // the MaxMind database in use by the Reader. func (r *Reader) Metadata() maxminddb.Metadata { return r.mmdbReader.Metadata } // Close unmaps the database file from virtual memory and returns the // resources to the system. func (r *Reader) Close() error { return r.mmdbReader.Close() } geoip2-golang-1.1.0/reader_test.go000066400000000000000000000132071307722164200170020ustar00rootroot00000000000000package geoip2 import ( "math/rand" "net" "testing" . "gopkg.in/check.v1" ) func TestGeoIP2(t *testing.T) { TestingT(t) } type MySuite struct{} var _ = Suite(&MySuite{}) func (s *MySuite) TestReader(c *C) { reader, err := Open("test-data/test-data/GeoIP2-City-Test.mmdb") c.Assert(err, IsNil) defer reader.Close() record, err := reader.City(net.ParseIP("81.2.69.160")) c.Assert(err, IsNil) m := reader.Metadata() c.Assert(m.BinaryFormatMajorVersion, Equals, uint(2)) c.Assert(m.BinaryFormatMinorVersion, Equals, uint(0)) c.Assert(m.BuildEpoch, Equals, uint(0x58f5327d)) c.Assert(m.DatabaseType, Equals, "GeoIP2-City") c.Assert(m.Description, DeepEquals, map[string]string{ "en": "GeoIP2 City Test Database (fake GeoIP2 data, for example purposes only)", "zh": "小型数据库", }) c.Assert(m.IPVersion, Equals, uint(6)) c.Assert(m.Languages, DeepEquals, []string{"en", "zh"}) c.Assert(m.NodeCount, Equals, uint(1240)) c.Assert(m.RecordSize, Equals, uint(28)) c.Assert(record.City.GeoNameID, Equals, uint(2643743)) c.Assert(record.City.Names, DeepEquals, map[string]string{ "de": "London", "en": "London", "es": "Londres", "fr": "Londres", "ja": "ロンドン", "pt-BR": "Londres", "ru": "Лондон", }) c.Assert(record.Continent.GeoNameID, Equals, uint(6255148)) c.Assert(record.Continent.Code, Equals, "EU") c.Assert(record.Continent.Names, DeepEquals, map[string]string{ "de": "Europa", "en": "Europe", "es": "Europa", "fr": "Europe", "ja": "ヨーロッパ", "pt-BR": "Europa", "ru": "Европа", "zh-CN": "欧洲", }) c.Assert(record.Country.GeoNameID, Equals, uint(2635167)) c.Assert(record.Country.IsoCode, Equals, "GB") c.Assert(record.Country.Names, DeepEquals, map[string]string{ "de": "Vereinigtes Königreich", "en": "United Kingdom", "es": "Reino Unido", "fr": "Royaume-Uni", "ja": "イギリス", "pt-BR": "Reino Unido", "ru": "Великобритания", "zh-CN": "英国", }) c.Assert(record.Location.AccuracyRadius, Equals, uint16(100)) c.Assert(record.Location.Latitude, Equals, 51.5142) c.Assert(record.Location.Longitude, Equals, -0.0931) c.Assert(record.Location.TimeZone, Equals, "Europe/London") c.Assert(record.Subdivisions[0].GeoNameID, Equals, uint(6269131)) c.Assert(record.Subdivisions[0].IsoCode, Equals, "ENG") c.Assert(record.Subdivisions[0].Names, DeepEquals, map[string]string{ "en": "England", "pt-BR": "Inglaterra", "fr": "Angleterre", "es": "Inglaterra", }) c.Assert(record.RegisteredCountry.GeoNameID, Equals, uint(6252001)) c.Assert(record.RegisteredCountry.IsoCode, Equals, "US") c.Assert(record.RegisteredCountry.Names, DeepEquals, map[string]string{ "de": "USA", "en": "United States", "es": "Estados Unidos", "fr": "États-Unis", "ja": "アメリカ合衆国", "pt-BR": "Estados Unidos", "ru": "США", "zh-CN": "美国", }) } func (s *MySuite) TestMetroCode(c *C) { reader, err := Open("test-data/test-data/GeoIP2-City-Test.mmdb") c.Assert(err, IsNil) defer reader.Close() record, err := reader.City(net.ParseIP("216.160.83.56")) c.Assert(err, IsNil) c.Assert(record.Location.MetroCode, Equals, uint(819)) } func (s *MySuite) TestAnonymousIP(c *C) { reader, err := Open("test-data/test-data/GeoIP2-Anonymous-IP-Test.mmdb") c.Assert(err, IsNil) defer reader.Close() record, err := reader.AnonymousIP(net.ParseIP("1.2.0.0")) c.Assert(err, IsNil) c.Assert(record.IsAnonymous, Equals, true) c.Assert(record.IsAnonymousVPN, Equals, true) c.Assert(record.IsHostingProvider, Equals, false) c.Assert(record.IsPublicProxy, Equals, false) c.Assert(record.IsTorExitNode, Equals, false) } func (s *MySuite) TestASN(c *C) { reader, err := Open("test-data/test-data/GeoLite2-ASN-Test.mmdb") c.Assert(err, IsNil) defer reader.Close() record, err := reader.ASN(net.ParseIP("1.128.0.0")) c.Assert(err, IsNil) c.Assert(record.AutonomousSystemNumber, Equals, uint(1221)) c.Assert(record.AutonomousSystemOrganization, Equals, "Telstra Pty Ltd") } func (s *MySuite) TestConnectionType(c *C) { reader, err := Open("test-data/test-data/GeoIP2-Connection-Type-Test.mmdb") c.Assert(err, IsNil) defer reader.Close() record, err := reader.ConnectionType(net.ParseIP("1.0.1.0")) c.Assert(err, IsNil) c.Assert(record.ConnectionType, Equals, "Cable/DSL") } func (s *MySuite) TestDomain(c *C) { reader, err := Open("test-data/test-data/GeoIP2-Domain-Test.mmdb") c.Assert(err, IsNil) defer reader.Close() record, err := reader.Domain(net.ParseIP("1.2.0.0")) c.Assert(err, IsNil) c.Assert(record.Domain, Equals, "maxmind.com") } func (s *MySuite) TestISP(c *C) { reader, err := Open("test-data/test-data/GeoIP2-ISP-Test.mmdb") c.Assert(err, IsNil) defer reader.Close() record, err := reader.ISP(net.ParseIP("1.128.0.0")) c.Assert(err, IsNil) c.Assert(record.AutonomousSystemNumber, Equals, uint(1221)) c.Assert(record.AutonomousSystemOrganization, Equals, "Telstra Pty Ltd") c.Assert(record.ISP, Equals, "Telstra Internet") c.Assert(record.Organization, Equals, "Telstra Internet") } // This ensures the compiler does not optimize away the function call var cityResult *City func BenchmarkMaxMindDB(b *testing.B) { db, err := Open("GeoLite2-City.mmdb") if err != nil { b.Fatal(err) } defer db.Close() r := rand.New(rand.NewSource(0)) var city *City for i := 0; i < b.N; i++ { ip := randomIPv4Address(b, r) city, err = db.City(ip) if err != nil { b.Fatal(err) } } cityResult = city } func randomIPv4Address(b *testing.B, r *rand.Rand) net.IP { num := r.Uint32() return []byte{byte(num >> 24), byte(num >> 16), byte(num >> 8), byte(num)} } geoip2-golang-1.1.0/test-data/000077500000000000000000000000001307722164200160355ustar00rootroot00000000000000