diff --git a/.gitignore b/.gitignore index 66e60fc..0fa9e49 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ geoip-demo -*.dat +*~ +/db/GeoLiteCity.dat # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o diff --git a/.travis.yml b/.travis.yml index f13abf6..4c1fd43 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,18 @@ language: go + +go: + - 1.3 + - 1.4 + - tip + before_install: - sudo apt-get install libgeoip-dev bzr + install: - mkdir -p $TRAVIS_BUILD_DIR/db - curl http://geodns.bitnames.com/geoip/GeoLiteCity.dat.gz | gzip -cd > $TRAVIS_BUILD_DIR/db/GeoLiteCity.dat - - go get launchpad.net/gocheck + - go get gopkg.in/check.v1 + script: - cd $TRAVIS_BUILD_DIR && go test -gocheck.v - go test -gocheck.v -gocheck.b -gocheck.btime=2s diff --git a/const.go b/const.go index 3eaef3f..9c8d0be 100644 --- a/const.go +++ b/const.go @@ -1,5 +1,6 @@ package geoip +// GeoIPDBTypes enum in GeoIP.h const ( GEOIP_COUNTRY_EDITION = 1 GEOIP_REGION_EDITION_REV0 = 7 @@ -33,3 +34,12 @@ const ( GEOIP_NETSPEED_EDITION_REV1 = 32 GEOIP_NETSPEED_EDITION_REV1_V6 = 33 ) + +// GeoIPOptions enum in GeoIP.h +const ( + GEOIP_STANDARD = 0 + GEOIP_MEMORY_CACHE = 1 + GEOIP_CHECK_CACHE = 2 + GEOIP_INDEX_CACHE = 4 + GEOIP_MMAP_CACHE = 8 +) diff --git a/ex/geoip-demo.go b/ex/geoip-demo.go index bbafd1e..e2cde8d 100644 --- a/ex/geoip-demo.go +++ b/ex/geoip-demo.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "github.com/abh/geoip" ) @@ -54,7 +55,7 @@ func main() { } -func test4(g geoip.GeoIP, ip string) { +func test4(g *geoip.GeoIP, ip string) { test(func(s string) (string, int) { return g.GetCountry(s) }, ip) } diff --git a/geoip.go b/geoip.go index 22eee0e..4d264fd 100644 --- a/geoip.go +++ b/geoip.go @@ -2,8 +2,7 @@ package geoip /* -#cgo CFLAGS: -I/opt/local/include -I/usr/local/include -I/usr/include -#cgo LDFLAGS: -lGeoIP -L/opt/local/lib -L/usr/local/lib -L/usr/lib +#cgo pkg-config: geoip #include #include #include @@ -46,13 +45,17 @@ func (gi *GeoIP) free() { return } -// Opens a GeoIP database by filename, all formats supported by libgeoip are -// supported though there are only functions to access some of the databases in this API. -// The database is opened in MEMORY_CACHE mode, if you need to optimize for memory -// instead of performance you should change this. +// Default convenience wrapper around OpenDb +func Open(files ...string) (*GeoIP, error) { + return OpenDb(files, GEOIP_MEMORY_CACHE) +} + +// Opens a GeoIP database by filename with specified GeoIPOptions flag. +// All formats supported by libgeoip are supported though there are only +// functions to access some of the databases in this API. // If you don't pass a filename, it will try opening the database from // a list of common paths. -func Open(files ...string) (*GeoIP, error) { +func OpenDb(files []string, flag int) (*GeoIP, error) { if len(files) == 0 { files = []string{ "/usr/share/GeoIP/GeoIP.dat", // Linux default @@ -81,7 +84,7 @@ func Open(files ...string) (*GeoIP, error) { cbase := C.CString(file) defer C.free(unsafe.Pointer(cbase)) - g.db, err = C.GeoIP_open(cbase, C.GEOIP_MEMORY_CACHE) + g.db, err = C.GeoIP_open(cbase, C.int(flag)) if g.db != nil && err != nil { break } @@ -107,15 +110,16 @@ func SetCustomDirectory(dir string) { C.GeoIP_setup_custom_directory(cdir) } -// OpenType opens a specified GeoIP database type in the default location. Constants -// are defined for each database type (for example GEOIP_COUNTRY_EDITION). -func OpenType(dbType int) (*GeoIP, error) { +// OpenType opens a specified GeoIP database type in the default location with the +// specified GeoIPOptions flag. Constants are defined for each database type +// (for example GEOIP_COUNTRY_EDITION). +func OpenTypeFlag(dbType int, flag int) (*GeoIP, error) { g := &GeoIP{} runtime.SetFinalizer(g, (*GeoIP).free) var err error - g.db, err = C.GeoIP_open_type(C.int(dbType), C.GEOIP_MEMORY_CACHE) + g.db, err = C.GeoIP_open_type(C.int(dbType), C.int(flag)) if err != nil { return nil, fmt.Errorf("Error opening GeoIP database (%d): %s", dbType, err) } @@ -129,6 +133,12 @@ func OpenType(dbType int) (*GeoIP, error) { return g, nil } +// OpenType opens a specified GeoIP database type in the default location +// and the 'memory cache' flag. Use OpenTypeFlag() to specify flag. +func OpenType(dbType int) (*GeoIP, error) { + return OpenTypeFlag(dbType, GEOIP_MEMORY_CACHE) +} + // Takes an IPv4 address string and returns the organization name for that IP. // Requires the GeoIP organization database. func (gi *GeoIP) GetOrg(ip string) string { @@ -161,15 +171,15 @@ func (gi *GeoIP) GetName(ip string) (name string, netmask int) { } type GeoIPRecord struct { - CountryCode string - CountryCode3 string - CountryName string - Region string - City string - PostalCode string - Latitude float32 - Longitude float32 - // DMACode int + CountryCode string + CountryCode3 string + CountryName string + Region string + City string + PostalCode string + Latitude float32 + Longitude float32 + MetroCode int AreaCode int CharSet int ContinentCode string @@ -203,10 +213,19 @@ func (gi *GeoIP) GetRecord(ip string) *GeoIPRecord { rec.PostalCode = C.GoString(record.postal_code) rec.Latitude = float32(record.latitude) rec.Longitude = float32(record.longitude) - rec.AreaCode = int(record.area_code) rec.CharSet = int(record.charset) rec.ContinentCode = C.GoString(record.continent_code) + if gi.db.databaseType != C.GEOIP_CITY_EDITION_REV0 { + /* DIRTY HACK BELOW: + The GeoIPRecord struct in GeoIPCity.h contains an int32 union of metro_code and dma_code. + The union is unnamed, so cgo names it anon0 and assumes it's a 4-byte array. + */ + union_int := (*int32)(unsafe.Pointer(&record.anon0)) + rec.MetroCode = int(*union_int) + rec.AreaCode = int(record.area_code) + } + return rec } diff --git a/geoip_test.go b/geoip_test.go index e23f84e..c8695c1 100644 --- a/geoip_test.go +++ b/geoip_test.go @@ -2,8 +2,9 @@ package geoip import ( "fmt" - . "launchpad.net/gocheck" "testing" + + . "gopkg.in/check.v1" ) // Hook up gocheck into the gotest runner. @@ -23,25 +24,25 @@ func (s *GeoIPSuite) Testv4(c *C) { c.Check(gi, NotNil) - country, netmask := gi.GetCountry("207.171.7.51") + country, netmask := gi.GetCountry("64.17.254.216") c.Check(country, Equals, "US") - c.Check(netmask, Equals, 15) + c.Check(netmask, Equals, 17) - country, netmask = gi.GetCountry("149.20.64.42") - c.Check(country, Equals, "US") - c.Check(netmask, Equals, 13) + country, netmask = gi.GetCountry("222.230.136.0") + c.Check(country, Equals, "JP") + c.Check(netmask, Equals, 16) } func (s *GeoIPSuite) TestOpenType(c *C) { - // SetCustomDirectory("/Users/ask/go/src/geoip/db") + SetCustomDirectory("test-db") // Open Country database gi, err := OpenType(GEOIP_COUNTRY_EDITION) c.Check(err, IsNil) c.Assert(gi, NotNil) - country, _ := gi.GetCountry("207.171.7.51") - c.Check(country, Equals, "US") + country, _ := gi.GetCountry("81.2.69.160") + c.Check(country, Equals, "GB") } func (s *GeoIPSuite) Benchmark_GetCountry(c *C) { @@ -57,7 +58,7 @@ func (s *GeoIPSuite) Benchmark_GetCountry(c *C) { } func (s *GeoIPSuite) Testv4Record(c *C) { - gi, err := Open("db/GeoLiteCity.dat") + gi, err := Open("test-db/GeoIPCity.dat") if gi == nil || err != nil { fmt.Printf("Could not open GeoIP database: %s\n", err) return @@ -65,16 +66,31 @@ func (s *GeoIPSuite) Testv4Record(c *C) { c.Check(gi, NotNil) - record := gi.GetRecord("207.171.7.51") + record := gi.GetRecord("66.92.181.240") c.Assert(record, NotNil) - c.Check(record.CountryCode, Equals, "US") - fmt.Printf("Record: %#v\n", record) - + c.Check( + *record, + Equals, + GeoIPRecord{ + CountryCode: "US", + CountryCode3: "USA", + CountryName: "United States", + Region: "CA", + City: "Fremont", + PostalCode: "94538", + Latitude: 37.5079, + Longitude: -121.96, + AreaCode: 510, + MetroCode: 807, + CharSet: 1, + ContinentCode: "NA", + }, + ) } func (s *GeoIPSuite) Benchmark_GetRecord(c *C) { - gi, err := Open("db/GeoIPCity.dat") + gi, err := Open("db/GeoLiteCity.dat") if gi == nil || err != nil { fmt.Printf("Could not open GeoIP database: %s\n", err) return @@ -89,13 +105,13 @@ func (s *GeoIPSuite) Benchmark_GetRecord(c *C) { } func (s *GeoIPSuite) Testv4Region(c *C) { - gi, err := Open("db/GeoIPRegion.dat") + gi, err := Open("test-db/GeoIPRegion.dat") if gi == nil || err != nil { fmt.Printf("Could not open GeoIP database: %s\n", err) return } - country, region := gi.GetRegion("207.171.7.51") + country, region := gi.GetRegion("64.17.254.223") c.Check(country, Equals, "US") c.Check(region, Equals, "CA") } diff --git a/test-db/GeoIP.dat b/test-db/GeoIP.dat new file mode 100644 index 0000000..5374dff Binary files /dev/null and b/test-db/GeoIP.dat differ diff --git a/test-db/GeoIPCity.dat b/test-db/GeoIPCity.dat new file mode 100644 index 0000000..230526c Binary files /dev/null and b/test-db/GeoIPCity.dat differ diff --git a/test-db/GeoIPRegion.dat b/test-db/GeoIPRegion.dat new file mode 100644 index 0000000..bb9d743 Binary files /dev/null and b/test-db/GeoIPRegion.dat differ