Skip to content
Snippets Groups Projects
Commit af61f0dc authored by ale's avatar ale
Browse files

Refactor GeoIP lookups to support multiple dbs

This enables correct (hopefully) support for IPv6 lookups. Also add
support for the special "onion" zone for Tor Hidden Services.
parent ef9ce4ba
No related branches found
No related tags found
No related merge requests found
package device
import "net"
import (
"errors"
"net"
func (m *Manager) getZoneForIP(ip net.IP) (string, error) {
if m.geodb == nil {
return "", nil
"github.com/oschwald/maxminddb-golang"
)
var (
errGeoNotFound = errors.New("no record found")
defaultGeoIPPaths = []string{
"/usr/share/GeoIP/GeoIP.dat",
"/usr/share/GeoIP/GeoIPv6.dat",
}
)
type geoIPDb struct {
readers []*maxminddb.Reader
}
func newGeoIP(paths []string) (*geoIPDb, error) {
if len(paths) == 0 {
paths = defaultGeoIPPaths
}
db := new(geoIPDb)
for _, path := range paths {
geodb, err := maxminddb.Open(path)
if err != nil {
return nil, err
}
db.readers = append(db.readers, geodb)
}
return db, nil
}
func (db *geoIPDb) getZoneForIP(ip net.IP) (string, error) {
// Only look up a single attribute (country).
var record struct {
Country struct {
ISOCode string `maxminddb:"iso_code"`
} `maxminddb:"country"`
}
if err := m.geodb.Lookup(ip, &record); err != nil {
return "", err
}
return record.Country.ISOCode, nil
for _, r := range db.readers {
if err := r.Lookup(ip, &record); err == nil {
return record.Country.ISOCode, nil
}
}
return "", errGeoNotFound
}
......@@ -3,13 +3,14 @@ package device
import (
"crypto/rand"
"encoding/hex"
"log"
"net"
"net/http"
"strings"
"git.autistici.org/id/auth"
"github.com/gorilla/sessions"
"github.com/mssola/user_agent"
"github.com/oschwald/maxminddb-golang"
)
func randomDeviceID() string {
......@@ -21,7 +22,7 @@ func randomDeviceID() string {
// Manager can provide DeviceInfo entries for incoming HTTP requests.
type Manager struct {
store sessions.Store
geodb *maxminddb.Reader
geodb *geoIPDb
trustedForwarders []net.IPNet
remoteAddrHeader string
}
......@@ -29,7 +30,7 @@ type Manager struct {
// Config stores options for the device info manager.
type Config struct {
AuthKey string `yaml:"auth_key"`
GeoIPDataFile string `yaml:"geo_ip_data"`
GeoIPDataFiles []string `yaml:"geo_ip_data_files"`
TrustedForwarders []string `yaml:"trusted_forwarders"`
RemoteAddrHeader string `yaml:"remote_addr_header"`
}
......@@ -44,12 +45,9 @@ func New(config *Config) (*Manager, error) {
return nil, err
}
var geodb *maxminddb.Reader
if config.GeoIPDataFile != "" {
geodb, err = maxminddb.Open(config.GeoIPDataFile)
if err != nil {
return nil, err
}
var geodb *geoIPDb
if geodb, err = newGeoIP(config.GeoIPDataFiles); err != nil {
log.Printf("Warning: GeoIP disabled: %v", err)
}
// The remote IP header (if any) defaults to X-Forwarded-For.
......@@ -93,10 +91,21 @@ func (m *Manager) GetDeviceInfoFromRequest(w http.ResponseWriter, req *http.Requ
Browser: browser,
}
if ip := m.getIPFromRequest(req); ip != nil {
d.RemoteAddr = ip.String()
if zone, err := m.getZoneForIP(ip); err == nil {
d.RemoteZone = zone
// Special check for .onion HTTP hosts - sets the zone to
// "onion" and skips the IP-based checks.
if req.Host != "" && strings.HasSuffix(req.Host, ".onion") {
d.RemoteZone = "onion"
} else {
// If we have an IP address, we can add more information.
if ip := m.getIPFromRequest(req); ip != nil {
d.RemoteAddr = ip.String()
// Country lookup.
if m.geodb != nil {
if zone, err := m.geodb.getZoneForIP(ip); err == nil {
d.RemoteZone = zone
}
}
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment