public
Created

geoip code review in L.A. Gophers meetup

  • Download Gist
geoip.go
Go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
/* Go (cgo) interface to libgeoip */
package geoip
 
/*
#cgo CFLAGS: -I/opt/local/include -I/usr/local/include
#cgo LDFLAGS: -lGeoIP -L/opt/local/lib -L/usr/local/lib
#include <stdio.h>
#include <errno.h>
#include <GeoIP.h>
 
//typedef GeoIP* GeoIP_pnt
*/
import "C"
 
import (
"log"
"os"
"unsafe"
)
 
// Set Quiet to true to silence warnings when the database can't be opened
// (though libgeoip still prints to errout)
// XXX: Flip to verbose
var Quiet bool = false
 
type GeoIP struct {
db *C.GeoIP
}
 
func (gi *GeoIP) Free() {
// FIXME: Free Willy
// Check everything is OK before free and null everything after
}
 
// Open opens a GeoIP database, all formats supported by libgeoip are supported though
// there are only functions to access the country information 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.
// If you don't pass a filename, it will try opening the database from
// a list of common paths.
func Open(files ...string) *GeoIP {
if len(files) == 0 {
files = []string{
"/usr/share/GeoIP/GeoIP.dat", // Linux default
"/usr/share/local/GeoIP/GeoIP.dat", // source install?
"/usr/local/share/GeoIP/GeoIP.dat", // FreeBSD
"/opt/local/share/GeoIP/GeoIP.dat", // MacPorts
}
}
 
g := &GeoIP{}
runtime.SetFinalizer(g, (*GeoIP).Free)
 
var err error
 
for _, file := range files {
 
// libgeoip prints errors if it can't open the file, so check first
if _, err := os.Stat(file); err != nil {
if os.IsExist(err) {
log.Println(err)
}
continue
}
 
cbase := C.CString(file)
defer C.free(unsafe.Pointer(cbase))
g.db, err = C.GeoIP_open(cbase, C.GEOIP_MEMORY_CACHE)
if g.db != nil && err != nil {
break
}
}
// FIXME: API decision - return error
if err != nil && !Quiet {
log.Println("Error opening GeoIP database", files, err)
}
 
if g.db != nil {
C.GeoIP_set_charset(g.db, C.GEOIP_CHARSET_UTF8)
return g
}
 
 
return nil
}
 
// GetOrg takes an IPv4 address string and returns the org name for that IP.
// Requires the geoip org database
func (gi *GeoIP) GetOrg(ip string) string {
if gi.db == nil {
return ""
}
cip := C.CString(ip)
defer C.free(unsafe.Pointer(cip))
cname := C.GeoIP_name_by_addr(gi.db, cip)
defer C.free(unsafe.Pointer(cname))
if cname != nil {
rets := C.GoString(cname)
return rets
}
return ""
}
 
// GetCountry takes an IPv4 address string and returns the country code for that IP.
func (gi *GeoIP) GetCountry(ip string) string {
// FIXME: Return error?
if gi.db == nil {
return ""
}
cip := C.CString(ip)
defer C.free(unsafe.Pointer(cip))
 
ccountry := C.GeoIP_country_code_by_addr(gi.db, cip)
defer C.free(unsafe.Pointer(ccountry))
 
if ccountry != nil {
rets := C.GoString(ccountry)
return rets
}
return ""
}
 
// GetCountry_v6 works the same as GetCountry except for IPv6 addresses, be sure to
// load a database with IPv6 data to get any results.
func (gi *GeoIP) GetCountry_v6(ip string) string {
if gi.db == nil {
return ""
}
cip := C.CString(ip)
ccountry := C.GeoIP_country_code_by_addr_v6(gi.db, cip)
C.free(unsafe.Pointer(cip))
if ccountry != nil {
rets := C.GoString(ccountry)
return rets
}
return ""
}
 
 
/* FIXME: Dispatch magic
type CFunc func(db *C.GeoIP, *C.char value)
 
func call(fn CFunc, string value) {
cstr := C.CString(value)
defer C.free(unsafe.Pointer(cstr))
res := CFunc(cstr)
defer C.free(unsafe.Pointer(res))
return C.GoString(res)
}
*/

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.