Clean up network/* packages, revamp online status detection
This commit is contained in:
@@ -4,46 +4,52 @@ import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/tevino/abool"
|
||||
|
||||
maxminddb "github.com/oschwald/maxminddb-golang"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/updater"
|
||||
"github.com/safing/portmaster/updates"
|
||||
)
|
||||
|
||||
var (
|
||||
dbCityFile *updater.File
|
||||
dbASNFile *updater.File
|
||||
dbFileLock sync.Mutex
|
||||
|
||||
dbCity *maxminddb.Reader
|
||||
dbASN *maxminddb.Reader
|
||||
dbLock sync.Mutex
|
||||
|
||||
dbLock sync.Mutex
|
||||
dbInUse = false // only activate if used for first time
|
||||
dbDoReload = true // if database should be reloaded
|
||||
dbInUse = abool.NewBool(false) // only activate if used for first time
|
||||
dbDoReload = abool.NewBool(true) // if database should be reloaded
|
||||
)
|
||||
|
||||
// ReloadDatabases reloads the geoip database, if they are in use.
|
||||
func ReloadDatabases() error {
|
||||
dbLock.Lock()
|
||||
defer dbLock.Unlock()
|
||||
|
||||
// don't do anything if the database isn't actually used
|
||||
if !dbInUse {
|
||||
if !dbInUse.IsSet() {
|
||||
return nil
|
||||
}
|
||||
|
||||
dbDoReload = true
|
||||
dbFileLock.Lock()
|
||||
defer dbFileLock.Unlock()
|
||||
dbLock.Lock()
|
||||
defer dbLock.Unlock()
|
||||
|
||||
dbDoReload.Set()
|
||||
return doReload()
|
||||
}
|
||||
|
||||
func prepDatabaseForUse() error {
|
||||
dbInUse = true
|
||||
dbInUse.Set()
|
||||
return doReload()
|
||||
}
|
||||
|
||||
func doReload() error {
|
||||
// reload if needed
|
||||
if dbDoReload {
|
||||
defer func() {
|
||||
dbDoReload = false
|
||||
}()
|
||||
|
||||
if dbDoReload.SetToIf(true, false) {
|
||||
closeDBs()
|
||||
return openDBs()
|
||||
}
|
||||
@@ -53,7 +59,7 @@ func doReload() error {
|
||||
|
||||
func openDBs() error {
|
||||
var err error
|
||||
file, err := updates.GetFile("intel/geoip-city.mmdb")
|
||||
file, err := updates.GetFile("intel/geoip/geoip-city.mmdb")
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get GeoIP City database file: %s", err)
|
||||
}
|
||||
@@ -61,7 +67,8 @@ func openDBs() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file, err = updates.GetFile("intel/geoip-asn.mmdb")
|
||||
|
||||
file, err = updates.GetFile("intel/geoip/geoip-asn.mmdb")
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get GeoIP ASN database file: %s", err)
|
||||
}
|
||||
@@ -73,8 +80,8 @@ func openDBs() error {
|
||||
}
|
||||
|
||||
func handleError(err error) {
|
||||
log.Warningf("network/geoip: lookup failed, reloading databases...")
|
||||
dbDoReload = true
|
||||
log.Errorf("network/geoip: lookup failed, reloading databases: %s", err)
|
||||
dbDoReload.Set()
|
||||
}
|
||||
|
||||
func closeDBs() {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
earthCircumferenceKm float64 = 40100 // earth circumference in km
|
||||
earthCircumferenceInKm float64 = 40100 // earth circumference in km
|
||||
)
|
||||
|
||||
// Location holds information regarding the geographical and network location of an IP address
|
||||
@@ -42,7 +42,7 @@ type Location struct {
|
||||
// Conclusion:
|
||||
// - Ignore location data completely if accuracy_radius > 500
|
||||
|
||||
// EstimateNetworkProximity aims to calculate a distance value between 0 and 100.
|
||||
// EstimateNetworkProximity aims to calculate the distance between two network locations. Returns a proximity value between 0 (far away) and 100 (nearby).
|
||||
func (l *Location) EstimateNetworkProximity(to *Location) (proximity int) {
|
||||
// Distance Value:
|
||||
// 0: other side of the Internet
|
||||
@@ -50,12 +50,10 @@ func (l *Location) EstimateNetworkProximity(to *Location) (proximity int) {
|
||||
|
||||
// Weighting:
|
||||
// coordinate distance: 0-50
|
||||
// continent match: 10
|
||||
// continent match: 15
|
||||
// country match: 10
|
||||
// AS owner match: 15
|
||||
// AS network match: 15
|
||||
//
|
||||
// We prioritize AS information over country information, as it is more accurate and we expect better privacy if we already are in the destination AS.
|
||||
// AS network match: 10
|
||||
|
||||
// coordinate distance: 0-50
|
||||
fromCoords := haversine.Coord{Lat: l.Coordinates.Latitude, Lon: l.Coordinates.Longitude}
|
||||
@@ -69,19 +67,19 @@ func (l *Location) EstimateNetworkProximity(to *Location) (proximity int) {
|
||||
accuracy = to.Coordinates.AccuracyRadius
|
||||
}
|
||||
|
||||
if km <= 10 && accuracy <= 200 {
|
||||
if km <= 10 && accuracy <= 100 {
|
||||
proximity += 50
|
||||
} else {
|
||||
distanceIn50Percent := ((earthCircumferenceKm - km) / earthCircumferenceKm) * 50
|
||||
distanceIn50Percent := ((earthCircumferenceInKm - km) / earthCircumferenceInKm) * 50
|
||||
|
||||
// apply penalty for values high values (targeting >100)
|
||||
// apply penalty for locations with low accuracy (targeting accuracy radius >100)
|
||||
accuracyModifier := 1 - float64(accuracy)/1000
|
||||
proximity += int(distanceIn50Percent * accuracyModifier)
|
||||
}
|
||||
|
||||
// continent match: 10
|
||||
// continent match: 15
|
||||
if l.Continent.Code == to.Continent.Code {
|
||||
proximity += 10
|
||||
proximity += 15
|
||||
// country match: 10
|
||||
if l.Country.ISOCode == to.Country.ISOCode {
|
||||
proximity += 10
|
||||
@@ -91,16 +89,16 @@ func (l *Location) EstimateNetworkProximity(to *Location) (proximity int) {
|
||||
// AS owner match: 15
|
||||
if l.AutonomousSystemOrganization == to.AutonomousSystemOrganization {
|
||||
proximity += 15
|
||||
// AS network match: 15
|
||||
// AS network match: 10
|
||||
if l.AutonomousSystemNumber == to.AutonomousSystemNumber {
|
||||
proximity += 15
|
||||
proximity += 10
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
return //nolint:nakedreturn
|
||||
}
|
||||
|
||||
// PrimitiveNetworkProximity calculates the numerical distance between two IP addresses. Returns a proximity value between 0 (far away) and 100 (nearby).
|
||||
func PrimitiveNetworkProximity(from net.IP, to net.IP, ipVersion uint8) int {
|
||||
|
||||
var diff float64
|
||||
@@ -128,7 +126,7 @@ func PrimitiveNetworkProximity(from net.IP, to net.IP, ipVersion uint8) int {
|
||||
|
||||
switch ipVersion {
|
||||
case 4:
|
||||
diff = diff / 256
|
||||
diff /= 256
|
||||
return int((1 - diff/16777216) * 100)
|
||||
case 6:
|
||||
return int((1 - diff/18446744073709552000) * 100)
|
||||
|
||||
@@ -3,9 +3,16 @@ package geoip
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/safing/portmaster/updates"
|
||||
)
|
||||
|
||||
func TestLocationLookup(t *testing.T) {
|
||||
err := updates.InitForTesting()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ip1 := net.ParseIP("81.2.69.142")
|
||||
loc1, err := GetLocation(ip1)
|
||||
if err != nil {
|
||||
|
||||
58
network/geoip/module.go
Normal file
58
network/geoip/module.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package geoip
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/modules"
|
||||
)
|
||||
|
||||
var (
|
||||
module *modules.Module
|
||||
)
|
||||
|
||||
func init() {
|
||||
module = modules.Register("geoip", nil, start, nil, "updates")
|
||||
}
|
||||
|
||||
func start() error {
|
||||
err := prepDatabaseForUse()
|
||||
if err != nil {
|
||||
return fmt.Errorf("goeip: failed to load databases: %s", err)
|
||||
}
|
||||
|
||||
module.RegisterEventHook(
|
||||
"updates",
|
||||
"resource update",
|
||||
"upgrade databases",
|
||||
upgradeDatabases,
|
||||
)
|
||||
|
||||
// TODO: replace with update subscription
|
||||
module.NewTask("update databases", func(ctx context.Context, task *modules.Task) {
|
||||
|
||||
dbFileLock.Lock()
|
||||
defer dbFileLock.Unlock()
|
||||
|
||||
}).Repeat(10 * time.Minute).MaxDelay(1 * time.Hour)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func upgradeDatabases(_ context.Context, _ interface{}) error {
|
||||
dbFileLock.Lock()
|
||||
reload := false
|
||||
if dbCityFile != nil && dbCityFile.UpgradeAvailable() {
|
||||
reload = true
|
||||
}
|
||||
if dbASNFile != nil && dbASNFile.UpgradeAvailable() {
|
||||
reload = true
|
||||
}
|
||||
dbFileLock.Unlock()
|
||||
|
||||
if reload {
|
||||
return ReloadDatabases()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user