Clean up network/* packages, revamp online status detection

This commit is contained in:
Daniel
2019-10-25 13:33:36 +02:00
parent c72f956fe8
commit fdb5f6fcf7
27 changed files with 738 additions and 268 deletions

View File

@@ -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() {

View File

@@ -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)

View File

@@ -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
View 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
}