2020-10-07 08:14:33 +02:00
|
|
|
// This package utilizes the MaxMind GeoLite2 GeoIP database https://dev.maxmind.com/geoip/geoip2/geolite2/.
|
|
|
|
// You must provide your own copy of this database for it to work.
|
|
|
|
// Read more about how this works at http://owncast.online/docs/geoip
|
|
|
|
|
|
|
|
package geoip
|
|
|
|
|
|
|
|
import (
|
|
|
|
"net"
|
|
|
|
|
|
|
|
"github.com/oschwald/geoip2-golang"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
|
|
|
var _geoIPCache = map[string]GeoDetails{}
|
|
|
|
var _enabled = true // Try to use GeoIP support it by default.
|
2021-02-19 08:05:52 +01:00
|
|
|
var geoIPDatabasePath = "data/GeoLite2-City.mmdb"
|
2020-10-07 08:14:33 +02:00
|
|
|
|
2020-11-13 00:14:59 +01:00
|
|
|
// GeoDetails stores details about a location.
|
2020-10-07 08:14:33 +02:00
|
|
|
type GeoDetails struct {
|
|
|
|
CountryCode string `json:"countryCode"`
|
|
|
|
RegionName string `json:"regionName"`
|
|
|
|
TimeZone string `json:"timeZone"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetGeoFromIP returns geo details associated with an IP address if we
|
|
|
|
// have previously fetched it.
|
|
|
|
func GetGeoFromIP(ip string) *GeoDetails {
|
|
|
|
if cachedGeoDetails, ok := _geoIPCache[ip]; ok {
|
|
|
|
return &cachedGeoDetails
|
|
|
|
}
|
|
|
|
|
2020-12-05 22:33:15 +01:00
|
|
|
if ip == "::1" || ip == "127.0.0.1" {
|
|
|
|
return &GeoDetails{
|
|
|
|
CountryCode: "N/A",
|
|
|
|
RegionName: "Localhost",
|
|
|
|
TimeZone: "",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-12 23:55:21 +02:00
|
|
|
return fetchGeoForIP(ip)
|
2020-10-07 08:14:33 +02:00
|
|
|
}
|
|
|
|
|
2021-08-12 23:55:21 +02:00
|
|
|
// fetchGeoForIP makes an API call to get geo details for an IP address.
|
|
|
|
func fetchGeoForIP(ip string) *GeoDetails {
|
2020-10-07 08:14:33 +02:00
|
|
|
// If GeoIP has been disabled then don't try to access it.
|
|
|
|
if !_enabled {
|
2021-08-12 23:55:21 +02:00
|
|
|
return nil
|
2020-10-07 08:14:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Don't re-fetch if we already have it.
|
2021-08-12 23:55:21 +02:00
|
|
|
if geoDetails, ok := _geoIPCache[ip]; ok {
|
|
|
|
return &geoDetails
|
2020-10-07 08:14:33 +02:00
|
|
|
}
|
|
|
|
|
2021-08-12 23:55:21 +02:00
|
|
|
db, err := geoip2.Open(geoIPDatabasePath)
|
|
|
|
if err != nil {
|
|
|
|
log.Traceln("GeoIP support is disabled. visit http://owncast.online/docs/geoip to learn how to enable.", err)
|
|
|
|
_enabled = false
|
|
|
|
return nil
|
|
|
|
}
|
2020-10-07 08:14:33 +02:00
|
|
|
|
2021-08-12 23:55:21 +02:00
|
|
|
defer db.Close()
|
2020-10-07 08:14:33 +02:00
|
|
|
|
2021-08-12 23:55:21 +02:00
|
|
|
ipObject := net.ParseIP(ip)
|
2020-10-07 08:14:33 +02:00
|
|
|
|
2021-08-12 23:55:21 +02:00
|
|
|
record, err := db.City(ipObject)
|
|
|
|
if err != nil {
|
|
|
|
log.Warnln(err)
|
|
|
|
return nil
|
|
|
|
}
|
2020-10-07 08:14:33 +02:00
|
|
|
|
2021-08-12 23:55:21 +02:00
|
|
|
// If no country is available then exit
|
|
|
|
if record.Country.IsoCode == "" {
|
|
|
|
return nil
|
|
|
|
}
|
2020-10-07 08:14:33 +02:00
|
|
|
|
2021-08-12 23:55:21 +02:00
|
|
|
// If we believe this IP to be anonymous then no reason to report it
|
|
|
|
if record.Traits.IsAnonymousProxy {
|
|
|
|
return nil
|
|
|
|
}
|
2020-10-07 08:14:33 +02:00
|
|
|
|
2021-08-12 23:55:21 +02:00
|
|
|
var regionName = "Unknown"
|
|
|
|
if len(record.Subdivisions) > 0 {
|
|
|
|
if region, ok := record.Subdivisions[0].Names["en"]; ok {
|
|
|
|
regionName = region
|
2020-12-05 22:33:15 +01:00
|
|
|
}
|
2021-08-12 23:55:21 +02:00
|
|
|
}
|
2020-12-05 22:33:15 +01:00
|
|
|
|
2021-08-12 23:55:21 +02:00
|
|
|
response := GeoDetails{
|
|
|
|
CountryCode: record.Country.IsoCode,
|
|
|
|
RegionName: regionName,
|
|
|
|
TimeZone: record.Location.TimeZone,
|
|
|
|
}
|
|
|
|
|
|
|
|
_geoIPCache[ip] = response
|
2020-10-07 08:14:33 +02:00
|
|
|
|
2021-08-12 23:55:21 +02:00
|
|
|
return &response
|
2020-10-07 08:14:33 +02:00
|
|
|
}
|