/
location.go
144 lines (126 loc) Β· 3.89 KB
/
location.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package geoip
import (
"encoding/binary"
"net"
"github.com/umahmood/haversine"
)
const (
earthCircumferenceInKm float64 = 40100 // earth circumference in km
)
// Location holds information regarding the geographical and network location of an IP address
type Location struct {
Continent struct {
Code string `maxminddb:"code"`
} `maxminddb:"continent"`
Country struct {
ISOCode string `maxminddb:"iso_code"`
} `maxminddb:"country"`
Coordinates struct {
AccuracyRadius uint16 `maxminddb:"accuracy_radius"`
Latitude float64 `maxminddb:"latitude"`
Longitude float64 `maxminddb:"longitude"`
} `maxminddb:"location"`
AutonomousSystemNumber uint `maxminddb:"autonomous_system_number"`
AutonomousSystemOrganization string `maxminddb:"autonomous_system_organization"`
}
// About GeoLite2 City accuracy_radius:
//
// range: 1-1000
// seen values (from memory): 1,5,10,20,50,100,200,500,1000
// default seems to be 100
//
// examples:
// 1.1.1/24 has 1000: Anycast
// 8.8.0/19 has 1000: Anycast
// 8.8.52/22 has 1: City of Westfield
//
// Conclusion:
// - Ignore location data completely if accuracy_radius > 500
// 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
// 100: same network/datacenter
// Weighting:
// continent match: 25
// country match: 20
// AS owner match: 25
// AS network match: 20
// coordinate distance: 0-10
// continent match: 25
if l.Continent.Code == to.Continent.Code {
proximity += 25
// country match: 20
if l.Country.ISOCode == to.Country.ISOCode {
proximity += 20
}
}
// AS owner match: 25
if l.AutonomousSystemOrganization == to.AutonomousSystemOrganization {
proximity += 25
// AS network match: 20
if l.AutonomousSystemNumber == to.AutonomousSystemNumber {
proximity += 20
}
}
// coordinate distance: 0-10
fromCoords := haversine.Coord{Lat: l.Coordinates.Latitude, Lon: l.Coordinates.Longitude}
toCoords := haversine.Coord{Lat: to.Coordinates.Latitude, Lon: to.Coordinates.Longitude}
_, km := haversine.Distance(fromCoords, toCoords)
// adjust accuracy value
accuracy := l.Coordinates.AccuracyRadius
switch {
case l.Coordinates.Latitude == 0 && l.Coordinates.Longitude == 0:
fallthrough
case to.Coordinates.Latitude == 0 && to.Coordinates.Longitude == 0:
// If we don't have any on any side coordinates, set accuracy to worst
// effective value.
accuracy = 1000
case to.Coordinates.AccuracyRadius > accuracy:
// If the destination accuracy is worse, use that one.
accuracy = to.Coordinates.AccuracyRadius
}
if km <= 10 && accuracy <= 100 {
proximity += 10
} else {
distanceInPercent := (earthCircumferenceInKm - km) * 100 / earthCircumferenceInKm
// apply penalty for locations with low accuracy (targeting accuracy radius >100)
accuracyModifier := 1 - float64(accuracy)/1000
proximity += int(distanceInPercent * 0.10 * accuracyModifier)
}
return //nolint:nakedret
}
// 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
switch ipVersion {
case 4:
// TODO: use ip.To4() and :4
a := binary.BigEndian.Uint32(from[12:])
b := binary.BigEndian.Uint32(to[12:])
if a > b {
diff = float64(a - b)
} else {
diff = float64(b - a)
}
case 6:
a := binary.BigEndian.Uint64(from[:8])
b := binary.BigEndian.Uint64(to[:8])
if a > b {
diff = float64(a - b)
} else {
diff = float64(b - a)
}
default:
return 0
}
switch ipVersion {
case 4:
diff /= 256
return int((1 - diff/16777216) * 100)
case 6:
return int((1 - diff/18446744073709552000) * 100)
default:
return 0
}
}