Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
language: go

go:
- 1.3
- 1.4
- tip
- 1.11
- 1.12

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 gopkg.in/check.v1

script:
Expand Down
29 changes: 0 additions & 29 deletions db/download

This file was deleted.

174 changes: 165 additions & 9 deletions geoip.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
package geoip

/*
#cgo pkg-config: geoip
#cgo pkg-config: geoip
#include <stdio.h>
#include <errno.h>
#include <GeoIP.h>
Expand All @@ -15,6 +15,7 @@ import "C"
import (
"fmt"
"log"
"net"
"os"
"runtime"
"sync"
Expand Down Expand Up @@ -139,6 +140,13 @@ func OpenType(dbType int) (*GeoIP, error) {
return OpenTypeFlag(dbType, GEOIP_MEMORY_CACHE)
}

// DatabaseType returns the database type.
func (gi *GeoIP) DatabaseType() int {
gi.mu.Lock()
defer gi.mu.Unlock()
return int(gi.db.databaseType)
}

// 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 {
Expand All @@ -161,10 +169,11 @@ func (gi *GeoIP) GetName(ip string) (name string, netmask int) {
defer C.free(unsafe.Pointer(cip))
cname := C.GeoIP_name_by_addr(gi.db, cip)

netmask = int(C.GeoIP_last_netmask(gi.db))

if cname != nil {
name = C.GoString(cname)
defer C.free(unsafe.Pointer(cname))
netmask = int(C.GeoIP_last_netmask(gi.db))
return
}
return
Expand All @@ -183,27 +192,76 @@ type GeoIPRecord struct {
AreaCode int
CharSet int
ContinentCode string
Netmask int
}

// CityResult holds the result of looking up an IP in a City type database. The
// lookup may be from either an IPv4 or IPv6 database.
type CityResult struct {
Record *GeoIPRecord
Netmask int
}

// LookupIPv4City looks up the IP in the database. The database must be an IPv4
// City database.
//
// This is the same as GetRecord() except it also provides information when the
// IP is not in the database. Specifically, the netmask. The netmask is useful
// for iterating the database.
func (gi *GeoIP) LookupIPv4City(ipString string) (CityResult, error) {
return gi.lookupIPv4City(ipString)
}

// Returns the "City Record" for an IP address. Requires the GeoCity(Lite)
// database - http://www.maxmind.com/en/city
func (gi *GeoIP) GetRecord(ip string) *GeoIPRecord {
if gi.db == nil {
return nil
func (gi *GeoIP) GetRecord(ipString string) *GeoIPRecord {
// We could return whether there was an error, but I'm trying to not change
// the API.
result, _ := gi.lookupIPv4City(ipString)
return result.Record
}

func (gi *GeoIP) lookupIPv4City(ipString string) (CityResult, error) {
if gi == nil || gi.db == nil {
return CityResult{}, fmt.Errorf("database is not loaded")
}

cip := C.CString(ip)
if gi.db.databaseType != GEOIP_CITY_EDITION_REV0 &&
gi.db.databaseType != GEOIP_CITY_EDITION_REV1 {
return CityResult{}, fmt.Errorf("invalid database type")
}

ip := net.ParseIP(ipString)
if ip == nil {
return CityResult{}, fmt.Errorf("invalid IP address")
}

// It's only valid to look up IPv4 IPs this way.
if ip := ip.To4(); ip == nil {
return CityResult{}, fmt.Errorf("IPv6 IP given for IPv4-only lookup")
}

cip := C.CString(ipString)
defer C.free(unsafe.Pointer(cip))

gi.mu.Lock()
record := C.GeoIP_record_by_addr(gi.db, cip)
netmask := int(gi.db.netmask)
gi.mu.Unlock()

if record == nil {
return nil
return CityResult{Netmask: netmask}, nil
}
// defer C.free(unsafe.Pointer(record))

defer C.GeoIPRecord_delete(record)

return CityResult{
Record: gi.convertRecord(record),
Netmask: netmask,
}, nil
}

func (gi *GeoIP) convertRecord(record *C.GeoIPRecord) *GeoIPRecord {
rec := new(GeoIPRecord)
rec.CountryCode = C.GoString(record.country_code)
rec.CountryCode3 = C.GoString(record.country_code3)
Expand All @@ -226,9 +284,53 @@ func (gi *GeoIP) GetRecord(ip string) *GeoIPRecord {
rec.AreaCode = int(record.area_code)
}

rec.Netmask = int(record.netmask)

return rec
}

// LookupIPv6City returns the city record for an IPv6 IP. This is only
// valid for the GeoLite City IPv6 database.
func (gi *GeoIP) LookupIPv6City(ipString string) (CityResult, error) {
if gi == nil || gi.db == nil {
return CityResult{}, fmt.Errorf("database is not loaded")
}

if gi.db.databaseType != GEOIP_CITY_EDITION_REV0_V6 &&
gi.db.databaseType != GEOIP_CITY_EDITION_REV1_V6 {
return CityResult{}, fmt.Errorf("invalid database type")
}

ip := net.ParseIP(ipString)
if ip == nil {
return CityResult{}, fmt.Errorf("invalid IP address")
}

// It's only valid to look up IPv6 IPs this way.
if ip := ip.To4(); ip != nil {
return CityResult{}, fmt.Errorf("IPv4 IP given for IPv6-only lookup")
}

cip := C.CString(ipString)
defer C.free(unsafe.Pointer(cip))

gi.mu.Lock()
record := C.GeoIP_record_by_addr_v6(gi.db, cip)
netmask := int(gi.db.netmask)
gi.mu.Unlock()

if record == nil {
return CityResult{Netmask: netmask}, nil
}

defer C.GeoIPRecord_delete(record)

return CityResult{
Record: gi.convertRecord(record),
Netmask: netmask,
}, nil
}

// Returns the country code and region code for an IP address. Requires
// the GeoIP Region database.
func (gi *GeoIP) GetRegion(ip string) (string, string) {
Expand Down Expand Up @@ -274,6 +376,25 @@ func GetRegionName(countryCode, regionCode string) string {
return regionName
}

// Returns the time zone given a country code and region code
func GetTimeZone(countryCode, regionCode string) string {
cc := C.CString(countryCode)
defer C.free(unsafe.Pointer(cc))

rc := C.CString(regionCode)
defer C.free(unsafe.Pointer(rc))

tz := C.GeoIP_time_zone_by_country_and_region(cc, rc)
if tz == nil {
return ""
}

// it's a static string constant, don't free this
timeZone := C.GoString(tz)

return timeZone
}

// Same as GetName() but for IPv6 addresses.
func (gi *GeoIP) GetNameV6(ip string) (name string, netmask int) {
if gi.db == nil {
Expand All @@ -287,10 +408,11 @@ func (gi *GeoIP) GetNameV6(ip string) (name string, netmask int) {
defer C.free(unsafe.Pointer(cip))
cname := C.GeoIP_name_by_addr_v6(gi.db, cip)

netmask = int(C.GeoIP_last_netmask(gi.db))

if cname != nil {
name = C.GoString(cname)
defer C.free(unsafe.Pointer(cname))
netmask = int(C.GeoIP_last_netmask(gi.db))
return
}
return
Expand Down Expand Up @@ -338,3 +460,37 @@ func (gi *GeoIP) GetCountry_v6(ip string) (cc string, netmask int) {
}
return
}

// GetCountryNameFromCode returns the country name for the 2 letter country
// code. If there is no name for the specified code, the empty string will
// be returned.
func (gi *GeoIP) GetCountryNameFromCode(code string) string {
if gi.db == nil {
return ""
}

gi.mu.Lock()
defer gi.mu.Unlock()

ccode := C.CString(code)
defer C.free(unsafe.Pointer(ccode))

cid := C.GeoIP_id_by_code(ccode)
cname := C.GeoIP_country_name_by_id(gi.db, cid)

return C.GoString(cname)
}

// EnableTeredo enables Teredo support. It is on by default.
func (gi *GeoIP) EnableTeredo() {
gi.mu.Lock()
defer gi.mu.Unlock()
C.GeoIP_enable_teredo(gi.db, C.int(1))
}

// DisableTeredo disable Teredo support. It is on by default.
func (gi *GeoIP) DisableTeredo() {
gi.mu.Lock()
defer gi.mu.Unlock()
C.GeoIP_enable_teredo(gi.db, C.int(0))
}
Loading