maxmind-geoip2 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/Gemfile.lock +114 -0
  4. data/README.dev.md +1 -1
  5. data/README.md +17 -2
  6. data/Rakefile +1 -0
  7. data/lib/maxmind/geoip2/model/anonymous_plus.rb +46 -0
  8. data/lib/maxmind/geoip2/model/city.rb +1 -1
  9. data/lib/maxmind/geoip2/reader.rb +26 -1
  10. data/lib/maxmind/geoip2/record/location.rb +3 -3
  11. data/lib/maxmind/geoip2/record/traits.rb +1 -1
  12. data/lib/maxmind/geoip2/version.rb +1 -1
  13. data/maxmind-geoip2.gemspec +5 -5
  14. data/test/data/LICENSE-APACHE +202 -0
  15. data/test/data/LICENSE-MIT +17 -0
  16. data/test/data/MaxMind-DB-spec.md +1 -2
  17. data/test/data/README.md +8 -1
  18. data/test/data/go.mod +1 -1
  19. data/test/data/pkg/writer/geoip2.go +7 -5
  20. data/test/data/pkg/writer/maxmind.go +2 -1
  21. data/test/data/pkg/writer/writer.go +4 -1
  22. data/test/data/source-data/GeoIP-Anonymous-Plus-Test.json +175 -0
  23. data/test/data/source-data/GeoIP2-Anonymous-IP-Test.json +6 -0
  24. data/test/data/source-data/GeoIP2-City-Test.json +73 -7
  25. data/test/data/source-data/GeoIP2-Country-Test.json +3 -28
  26. data/test/data/source-data/GeoIP2-Enterprise-Test.json +27 -8
  27. data/test/data/source-data/GeoIP2-IP-Risk-Test.json +31 -0
  28. data/test/data/source-data/GeoIP2-Precision-Enterprise-Test.json +585 -11
  29. data/test/data/source-data/GeoLite2-City-Test.json +0 -3
  30. data/test/data/source-data/GeoLite2-Country-Test.json +0 -3
  31. data/test/data/test-data/GeoIP-Anonymous-Plus-Test.mmdb +0 -0
  32. data/test/data/test-data/GeoIP2-Anonymous-IP-Test.mmdb +0 -0
  33. data/test/data/test-data/GeoIP2-City-Test.mmdb +0 -0
  34. data/test/data/test-data/GeoIP2-Connection-Type-Test.mmdb +0 -0
  35. data/test/data/test-data/GeoIP2-Country-Test.mmdb +0 -0
  36. data/test/data/test-data/GeoIP2-DensityIncome-Test.mmdb +0 -0
  37. data/test/data/test-data/GeoIP2-Domain-Test.mmdb +0 -0
  38. data/test/data/test-data/GeoIP2-Enterprise-Test.mmdb +0 -0
  39. data/test/data/test-data/GeoIP2-IP-Risk-Test.mmdb +0 -0
  40. data/test/data/test-data/GeoIP2-ISP-Test.mmdb +0 -0
  41. data/test/data/test-data/GeoIP2-Precision-Enterprise-Test.mmdb +0 -0
  42. data/test/data/test-data/GeoIP2-Static-IP-Score-Test.mmdb +0 -0
  43. data/test/data/test-data/GeoIP2-User-Count-Test.mmdb +0 -0
  44. data/test/data/test-data/GeoLite2-ASN-Test.mmdb +0 -0
  45. data/test/data/test-data/GeoLite2-City-Test.mmdb +0 -0
  46. data/test/data/test-data/GeoLite2-Country-Test.mmdb +0 -0
  47. data/test/data/test-data/MaxMind-DB-no-ipv4-search-tree.mmdb +0 -0
  48. data/test/data/test-data/MaxMind-DB-string-value-entries.mmdb +0 -0
  49. data/test/data/test-data/MaxMind-DB-test-decoder.mmdb +0 -0
  50. data/test/data/test-data/MaxMind-DB-test-ipv4-24.mmdb +0 -0
  51. data/test/data/test-data/MaxMind-DB-test-ipv4-28.mmdb +0 -0
  52. data/test/data/test-data/MaxMind-DB-test-ipv4-32.mmdb +0 -0
  53. data/test/data/test-data/MaxMind-DB-test-ipv6-24.mmdb +0 -0
  54. data/test/data/test-data/MaxMind-DB-test-ipv6-28.mmdb +0 -0
  55. data/test/data/test-data/MaxMind-DB-test-ipv6-32.mmdb +0 -0
  56. data/test/data/test-data/MaxMind-DB-test-metadata-pointers.mmdb +0 -0
  57. data/test/data/test-data/MaxMind-DB-test-mixed-24.mmdb +0 -0
  58. data/test/data/test-data/MaxMind-DB-test-mixed-28.mmdb +0 -0
  59. data/test/data/test-data/MaxMind-DB-test-mixed-32.mmdb +0 -0
  60. data/test/data/test-data/MaxMind-DB-test-nested.mmdb +0 -0
  61. data/test/test_reader.rb +23 -0
  62. metadata +12 -9
  63. data/test/data/LICENSE +0 -4
  64. data/test/data/perltidyrc +0 -18
@@ -16,6 +16,7 @@ import (
16
16
  // WriteGeoIP2TestDB writes GeoIP2 test mmdb files.
17
17
  func (w *Writer) WriteGeoIP2TestDB() error {
18
18
  dbTypes := []string{
19
+ "GeoIP-Anonymous-Plus",
19
20
  "GeoIP2-Anonymous-IP",
20
21
  "GeoIP2-City",
21
22
  "GeoIP2-Connection-Type",
@@ -23,6 +24,7 @@ func (w *Writer) WriteGeoIP2TestDB() error {
23
24
  "GeoIP2-DensityIncome",
24
25
  "GeoIP2-Domain",
25
26
  "GeoIP2-Enterprise",
27
+ "GeoIP2-IP-Risk",
26
28
  "GeoIP2-ISP",
27
29
  "GeoIP2-Precision-Enterprise",
28
30
  "GeoIP2-Static-IP-Score",
@@ -58,18 +60,18 @@ func (w *Writer) WriteGeoIP2TestDB() error {
58
60
  return fmt.Errorf("creating mmdbwriter: %w", err)
59
61
  }
60
62
 
61
- if dbType == "GeoIP2-Anonymous-IP" {
63
+ if dbType == "GeoIP2-Anonymous-IP" || dbType == "GeoIP-Anonymous-Plus" {
62
64
  if err := populateAllNetworks(dbWriter); err != nil {
63
65
  return fmt.Errorf("inserting all networks: %w", err)
64
66
  }
65
67
  }
66
68
 
67
- jsonFileName := fmt.Sprintf("%s-Test.json", dbType)
69
+ jsonFileName := dbType + "-Test.json"
68
70
  if err := w.insertJSON(dbWriter, jsonFileName); err != nil {
69
71
  return fmt.Errorf("inserting json: %w", err)
70
72
  }
71
73
 
72
- dbFileName := fmt.Sprintf("%s-Test.mmdb", dbType)
74
+ dbFileName := dbType + "-Test.mmdb"
73
75
  if err := w.write(dbWriter, dbFileName); err != nil {
74
76
  return fmt.Errorf("writing database: %w", err)
75
77
  }
@@ -146,7 +148,7 @@ func toMMDBType(key string, value any) (mmdbtype.DataType, error) {
146
148
  return s, nil
147
149
  case float64:
148
150
  switch key {
149
- case "accuracy_radius", "confidence", "metro_code":
151
+ case "accuracy_radius", "anonymizer_confidence", "confidence", "metro_code":
150
152
  return mmdbtype.Uint16(v), nil
151
153
  case "autonomous_system_number", "average_income",
152
154
  "geoname_id", "ipv4_24", "ipv4_32", "ipv6_32",
@@ -156,7 +158,7 @@ func toMMDBType(key string, value any) (mmdbtype.DataType, error) {
156
158
  "static_ip_score":
157
159
  return mmdbtype.Float64(v), nil
158
160
  default:
159
- return nil, fmt.Errorf("unsupported numberic type for key %q: %T", key, value)
161
+ return nil, fmt.Errorf("unsupported numeric type for key %q: %T", key, value)
160
162
  }
161
163
  default:
162
164
  return nil, fmt.Errorf("unsupported type for key %q: %T", key, value)
@@ -53,7 +53,8 @@ func (w *Writer) WriteIPv6TestDB() error {
53
53
  return nil
54
54
  }
55
55
 
56
- // WriteMixedIPTestDB writes mmdb files for a mixed ip version range between ::1:ffff:ffff and ::2:0000:0059
56
+ // WriteMixedIPTestDB writes mmdb files for a mixed ip version range between ::1:ffff:ffff and
57
+ // ::2:0000:0059
57
58
  // with various record sizes.
58
59
  func (w *Writer) WriteMixedIPTestDB() error {
59
60
  ipv6Range, err := parseIPRange("::1:ffff:ffff", "::2:0000:0059")
@@ -3,7 +3,9 @@
3
3
  package writer
4
4
 
5
5
  import (
6
+ "errors"
6
7
  "fmt"
8
+ "io/fs"
7
9
  "os"
8
10
  "path/filepath"
9
11
 
@@ -29,11 +31,12 @@ type Writer struct {
29
31
  // New initializes a new test database writer struct.
30
32
  func New(source, target string) (*Writer, error) {
31
33
  s := filepath.Clean(source)
32
- if _, err := os.Stat(s); os.IsNotExist(err) {
34
+ if _, err := os.Stat(s); errors.Is(err, fs.ErrNotExist) {
33
35
  return nil, fmt.Errorf("source directory does not exist: %w", err)
34
36
  }
35
37
 
36
38
  t := filepath.Clean(target)
39
+ //nolint:gosec // not security sensitive.
37
40
  if err := os.MkdirAll(t, os.ModePerm); err != nil {
38
41
  return nil, fmt.Errorf("creating target directory: %w", err)
39
42
  }
@@ -0,0 +1,175 @@
1
+ [
2
+ {
3
+ "1.2.0.0/32": {
4
+ "is_anonymous": true,
5
+ "is_anonymous_vpn": true
6
+ }
7
+ },
8
+ {
9
+ "1.2.0.1/32": {
10
+ "anonymizer_confidence": 30,
11
+ "is_anonymous": true,
12
+ "is_anonymous_vpn": true,
13
+ "network_last_seen": "2025-04-14",
14
+ "provider_name": "foo"
15
+ }
16
+ },
17
+ {
18
+ "1.2.0.2/32": {
19
+ "anonymizer_confidence": 99,
20
+ "is_anonymous": true,
21
+ "is_anonymous_vpn": true
22
+ }
23
+ },
24
+ {
25
+ "1.2.0.3/32": {
26
+ "is_anonymous": true,
27
+ "is_anonymous_vpn": true,
28
+ "network_last_seen": "2025-04-14"
29
+ }
30
+ },
31
+ {
32
+ "1.2.0.4/32": {
33
+ "is_anonymous": true,
34
+ "is_anonymous_vpn": true,
35
+ "provider_name": "foo"
36
+ }
37
+ },
38
+ {
39
+ "1.2.0.5/32": {
40
+ "is_anonymous": true,
41
+ "is_anonymous_vpn": true
42
+ }
43
+ },
44
+ {
45
+ "1.2.0.6/31": {
46
+ "is_anonymous": true,
47
+ "is_anonymous_vpn": true
48
+ }
49
+ },
50
+ {
51
+ "1.2.0.8/29": {
52
+ "is_anonymous": true,
53
+ "is_anonymous_vpn": true
54
+ }
55
+ },
56
+ {
57
+ "1.2.0.16/28": {
58
+ "is_anonymous": true,
59
+ "is_anonymous_vpn": true
60
+ }
61
+ },
62
+ {
63
+ "1.2.0.32/27": {
64
+ "is_anonymous": true,
65
+ "is_anonymous_vpn": true
66
+ }
67
+ },
68
+ {
69
+ "1.2.0.64/26": {
70
+ "is_anonymous": true,
71
+ "is_anonymous_vpn": true
72
+ }
73
+ },
74
+ {
75
+ "1.2.0.128/25": {
76
+ "is_anonymous": true,
77
+ "is_anonymous_vpn": true
78
+ }
79
+ },
80
+ {
81
+ "1.2.1.0/24": {
82
+ "is_anonymous": true,
83
+ "is_anonymous_vpn": true
84
+ }
85
+ },
86
+ {
87
+ "1.2.2.0/23": {
88
+ "is_anonymous": true,
89
+ "is_anonymous_vpn": true
90
+ }
91
+ },
92
+ {
93
+ "1.2.4.0/22": {
94
+ "is_anonymous": true,
95
+ "is_anonymous_vpn": true
96
+ }
97
+ },
98
+ {
99
+ "1.2.8.0/21": {
100
+ "is_anonymous": true,
101
+ "is_anonymous_vpn": true
102
+ }
103
+ },
104
+ {
105
+ "1.2.16.0/20": {
106
+ "is_anonymous": true,
107
+ "is_anonymous_vpn": true
108
+ }
109
+ },
110
+ {
111
+ "1.2.32.0/19": {
112
+ "is_anonymous": true,
113
+ "is_anonymous_vpn": true
114
+ }
115
+ },
116
+ {
117
+ "1.2.64.0/18": {
118
+ "is_anonymous": true,
119
+ "is_anonymous_vpn": true
120
+ }
121
+ },
122
+ {
123
+ "1.2.128.0/17": {
124
+ "is_anonymous": true,
125
+ "is_anonymous_vpn": true
126
+ }
127
+ },
128
+ {
129
+ "::1.124.213.1/128": {
130
+ "is_anonymous": true,
131
+ "is_anonymous_vpn": true,
132
+ "is_tor_exit_node": true
133
+ }
134
+ },
135
+ {
136
+ "::81.2.69.0/120": {
137
+ "is_anonymous": true,
138
+ "is_anonymous_vpn": true,
139
+ "is_hosting_provider": true,
140
+ "is_public_proxy": true,
141
+ "is_residential_proxy": true,
142
+ "is_tor_exit_node": true
143
+ }
144
+ },
145
+ {
146
+ "::71.160.223.0/120": {
147
+ "is_anonymous": true,
148
+ "is_hosting_provider": true
149
+ }
150
+ },
151
+ {
152
+ "::186.30.236.0/120": {
153
+ "is_anonymous": true,
154
+ "is_public_proxy": true
155
+ }
156
+ },
157
+ {
158
+ "::65.0.0.0/109": {
159
+ "is_anonymous": true,
160
+ "is_tor_exit_node": true
161
+ }
162
+ },
163
+ {
164
+ "abcd:1000::/112": {
165
+ "is_anonymous": true,
166
+ "is_public_proxy": true
167
+ }
168
+ },
169
+ {
170
+ "2001:480:3a::/64": {
171
+ "is_anonymous": true,
172
+ "is_public_proxy": true
173
+ }
174
+ }
175
+ ]
@@ -45,5 +45,11 @@
45
45
  "is_anonymous" : true,
46
46
  "is_public_proxy" : true
47
47
  }
48
+ },
49
+ {
50
+ "2001:480:3a::/64" : {
51
+ "is_anonymous" : true,
52
+ "is_public_proxy" : true
53
+ }
48
54
  }
49
55
  ]
@@ -252,9 +252,9 @@
252
252
  },
253
253
  {
254
254
  "214.1.1.0/24" : {
255
- "traits": {
256
- "is_anycast": true
257
- }
255
+ "traits" : {
256
+ "is_anycast" : true
257
+ }
258
258
  }
259
259
  },
260
260
  {
@@ -12151,7 +12151,76 @@
12151
12151
  "names" : {
12152
12152
  "en" : "Boxford"
12153
12153
  }
12154
- }
12154
+ },
12155
+ "continent" : {
12156
+ "code" : "EU",
12157
+ "geoname_id" : 6255148,
12158
+ "names" : {
12159
+ "de" : "Europa",
12160
+ "en" : "Europe",
12161
+ "es" : "Europa",
12162
+ "fr" : "Europe",
12163
+ "ja" : "ヨーロッパ",
12164
+ "pt-BR" : "Europa",
12165
+ "ru" : "Европа",
12166
+ "zh-CN" : "欧洲"
12167
+ }
12168
+ },
12169
+ "country" : {
12170
+ "geoname_id" : 2635167,
12171
+ "iso_code" : "GB",
12172
+ "names" : {
12173
+ "de" : "Vereinigtes Königreich",
12174
+ "en" : "United Kingdom",
12175
+ "es" : "Reino Unido",
12176
+ "fr" : "Royaume-Uni",
12177
+ "ja" : "イギリス",
12178
+ "pt-BR" : "Reino Unido",
12179
+ "ru" : "Великобритания",
12180
+ "zh-CN" : "英国"
12181
+ }
12182
+ },
12183
+ "location" : {
12184
+ "accuracy_radius" : 100,
12185
+ "latitude" : 51.75,
12186
+ "longitude" : -1.25,
12187
+ "time_zone" : "Europe/London"
12188
+ },
12189
+ "registered_country" : {
12190
+ "geoname_id" : 2635167,
12191
+ "iso_code" : "GB",
12192
+ "names" : {
12193
+ "de" : "Vereinigtes Königreich",
12194
+ "en" : "United Kingdom",
12195
+ "es" : "Reino Unido",
12196
+ "fr" : "Royaume-Uni",
12197
+ "ja" : "イギリス",
12198
+ "pt-BR" : "Reino Unido",
12199
+ "ru" : "Великобритания",
12200
+ "zh-CN" : "英国"
12201
+ }
12202
+ },
12203
+ "subdivisions" : [
12204
+ {
12205
+ "geoname_id" : 6269131,
12206
+ "iso_code" : "ENG",
12207
+ "names" : {
12208
+ "en" : "England",
12209
+ "es" : "Inglaterra",
12210
+ "fr" : "Angleterre",
12211
+ "pt-BR" : "Inglaterra"
12212
+ }
12213
+ },
12214
+ {
12215
+ "geoname_id" : 3333217,
12216
+ "iso_code" : "WBK",
12217
+ "names" : {
12218
+ "en" : "West Berkshire",
12219
+ "ru" : "Западный Беркшир",
12220
+ "zh-CN" : "西伯克郡"
12221
+ }
12222
+ }
12223
+ ]
12155
12224
  }
12156
12225
  },
12157
12226
  {
@@ -12886,9 +12955,6 @@
12886
12955
  "ru" : "Румыния",
12887
12956
  "zh-CN" : "罗马尼亚"
12888
12957
  }
12889
- },
12890
- "traits" : {
12891
- "is_anonymous_proxy" : true
12892
12958
  }
12893
12959
  }
12894
12960
  },
@@ -15211,10 +15211,6 @@
15211
15211
  "ru" : "США",
15212
15212
  "zh-CN" : "美国"
15213
15213
  }
15214
- },
15215
- "traits" : {
15216
- "is_anonymous_proxy" : true,
15217
- "is_satellite_provider" : true
15218
15214
  }
15219
15215
  }
15220
15216
  },
@@ -15261,10 +15257,6 @@
15261
15257
  "ru" : "США",
15262
15258
  "zh-CN" : "美国"
15263
15259
  }
15264
- },
15265
- "traits" : {
15266
- "is_anonymous_proxy" : true,
15267
- "is_satellite_provider" : true
15268
15260
  }
15269
15261
  }
15270
15262
  },
@@ -15638,9 +15630,6 @@
15638
15630
  "ru" : "Румыния",
15639
15631
  "zh-CN" : "罗马尼亚"
15640
15632
  }
15641
- },
15642
- "traits" : {
15643
- "is_anonymous_proxy" : true
15644
15633
  }
15645
15634
  }
15646
15635
  },
@@ -15797,20 +15786,6 @@
15797
15786
  }
15798
15787
  }
15799
15788
  },
15800
- {
15801
- "::212.47.235.81/128" : {
15802
- "traits" : {
15803
- "is_anonymous_proxy" : true
15804
- }
15805
- }
15806
- },
15807
- {
15808
- "::212.47.235.82/128" : {
15809
- "traits" : {
15810
- "is_satellite_provider" : true
15811
- }
15812
- }
15813
- },
15814
15789
  {
15815
15790
  "::217.65.48.0/125" : {
15816
15791
  "continent" : {
@@ -15903,9 +15878,9 @@
15903
15878
  },
15904
15879
  {
15905
15880
  "214.1.1.0/24" : {
15906
- "traits": {
15907
- "is_anycast": true
15908
- }
15881
+ "traits" : {
15882
+ "is_anycast" : true
15883
+ }
15909
15884
  }
15910
15885
  },
15911
15886
  {
@@ -75,6 +75,7 @@
75
75
  }
76
76
  },
77
77
  {
78
+ "confidence" : 60,
78
79
  "geoname_id" : 3333217,
79
80
  "iso_code" : "WBK",
80
81
  "names" : {
@@ -144,7 +145,6 @@
144
145
  "traits" : {
145
146
  "autonomous_system_number" : 35908,
146
147
  "domain" : "shoesfin.NET",
147
- "is_anonymous_proxy" : true,
148
148
  "isp" : "Loud Packet",
149
149
  "organization" : "zudoarichikito_",
150
150
  "static_ip_score" : 0.34,
@@ -237,9 +237,7 @@
237
237
  "autonomous_system_organization" : "FairPoint Communications",
238
238
  "connection_type" : "Cable/DSL",
239
239
  "domain" : "frpt.net",
240
- "is_anonymous_proxy" : true,
241
240
  "is_legitimate_proxy" : true,
242
- "is_satellite_provider" : true,
243
241
  "isp" : "Fairpoint Communications",
244
242
  "organization" : "Fairpoint Communications",
245
243
  "static_ip_score" : 0.34,
@@ -780,9 +778,9 @@
780
778
  },
781
779
  {
782
780
  "214.1.1.0/24" : {
783
- "traits": {
784
- "is_anycast": true
785
- }
781
+ "traits" : {
782
+ "is_anycast" : true
783
+ }
786
784
  }
787
785
  },
788
786
  {
@@ -1075,7 +1073,6 @@
1075
1073
  {
1076
1074
  "::212.47.235.81/128" : {
1077
1075
  "traits" : {
1078
- "is_anonymous_proxy" : true,
1079
1076
  "static_ip_score" : 1.47
1080
1077
  }
1081
1078
  }
@@ -1083,9 +1080,31 @@
1083
1080
  {
1084
1081
  "::212.47.235.82/128" : {
1085
1082
  "traits" : {
1086
- "is_satellite_provider" : true,
1087
1083
  "static_ip_score" : 1.47
1088
1084
  }
1089
1085
  }
1086
+ },
1087
+ {
1088
+ "214.214.214.216/32" : {
1089
+ "traits" : {
1090
+ "static_ip_score" : 0.4,
1091
+ "user_type" : "traveler"
1092
+ }
1093
+ }
1094
+ },
1095
+ {
1096
+ "214.214.214.220/32" : {
1097
+ "traits" : {
1098
+ "static_ip_score" : 0.4,
1099
+ "user_type" : "hosting"
1100
+ }
1101
+ }
1102
+ },
1103
+ {
1104
+ "214.214.214.221/32" : {
1105
+ "traits" : {
1106
+ "user_type" : "content_delivery_network"
1107
+ }
1108
+ }
1090
1109
  }
1091
1110
  ]
@@ -0,0 +1,31 @@
1
+ [
2
+ {
3
+ "::214.2.3.0/126" : {
4
+ "ip_risk" : 25,
5
+ "is_anonymous" : true,
6
+ "is_anonymous_vpn" : true
7
+ }
8
+ },
9
+ {
10
+ "::214.2.3.4/128" : {
11
+ "ip_risk" : 50,
12
+ "is_anonymous" : true,
13
+ "is_anonymous_vpn" : true,
14
+ "is_tor_exit_node" : true
15
+ }
16
+ },
17
+ {
18
+ "::214.2.3.5/128" : {
19
+ "ip_risk" : 90,
20
+ "is_anonymous" : true,
21
+ "is_anonymous_vpn" : true,
22
+ "is_residential_proxy" : true,
23
+ "is_tor_exit_node" : true
24
+ }
25
+ },
26
+ {
27
+ "::214.2.3.6/128" : {
28
+ "ip_risk" : 85
29
+ }
30
+ }
31
+ ]