From af61f0dcaaaa4ba2b716c3997dd5baa5e8d16ce3 Mon Sep 17 00:00:00 2001 From: ale <ale@incal.net> Date: Sun, 19 Nov 2017 10:17:21 +0000 Subject: [PATCH] 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. --- server/device/geo.go | 48 +++++++++++++++++++++++++++++++++------- server/device/manager.go | 35 ++++++++++++++++++----------- 2 files changed, 62 insertions(+), 21 deletions(-) diff --git a/server/device/geo.go b/server/device/geo.go index 136c76d..83a66b3 100644 --- a/server/device/geo.go +++ b/server/device/geo.go @@ -1,21 +1,53 @@ 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 } diff --git a/server/device/manager.go b/server/device/manager.go index 981da2b..c0fa0af 100644 --- a/server/device/manager.go +++ b/server/device/manager.go @@ -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 + } + } } } -- GitLab