maxmind-geoip2 1.1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/Gemfile.lock +114 -0
- data/README.dev.md +1 -1
- data/README.md +26 -6
- data/Rakefile +1 -0
- data/lib/maxmind/geoip2/client.rb +18 -9
- data/lib/maxmind/geoip2/model/anonymous_plus.rb +46 -0
- data/lib/maxmind/geoip2/model/city.rb +5 -6
- data/lib/maxmind/geoip2/model/connection_type.rb +3 -2
- data/lib/maxmind/geoip2/model/enterprise.rb +2 -3
- data/lib/maxmind/geoip2/model/insights.rb +3 -5
- data/lib/maxmind/geoip2/reader.rb +26 -1
- data/lib/maxmind/geoip2/record/location.rb +3 -3
- data/lib/maxmind/geoip2/record/traits.rb +40 -28
- data/lib/maxmind/geoip2/version.rb +8 -0
- data/maxmind-geoip2.gemspec +11 -6
- data/test/data/LICENSE-APACHE +202 -0
- data/test/data/LICENSE-MIT +17 -0
- data/test/data/MaxMind-DB-spec.md +1 -2
- data/test/data/README.md +8 -1
- data/test/data/cmd/write-test-data/main.go +68 -0
- data/test/data/go.mod +13 -0
- data/test/data/go.sum +16 -0
- data/test/data/pkg/writer/decoder.go +178 -0
- data/test/data/pkg/writer/geoip2.go +184 -0
- data/test/data/pkg/writer/ip.go +39 -0
- data/test/data/pkg/writer/maxmind.go +246 -0
- data/test/data/pkg/writer/nestedstructures.go +73 -0
- data/test/data/pkg/writer/writer.go +61 -0
- data/test/data/source-data/GeoIP-Anonymous-Plus-Test.json +175 -0
- data/test/data/source-data/GeoIP2-Anonymous-IP-Test.json +6 -0
- data/test/data/source-data/GeoIP2-City-Test.json +392 -5
- data/test/data/source-data/GeoIP2-Connection-Type-Test.json +15 -10
- data/test/data/source-data/GeoIP2-Country-Test.json +99 -25
- data/test/data/source-data/GeoIP2-Domain-Test.json +5 -0
- data/test/data/source-data/GeoIP2-Enterprise-Test.json +371 -6
- data/test/data/source-data/GeoIP2-IP-Risk-Test.json +31 -0
- data/test/data/source-data/GeoIP2-Precision-Enterprise-Sandbox-Test.json +296 -0
- data/test/data/source-data/GeoIP2-Precision-Enterprise-Test.json +1159 -175
- data/test/data/source-data/GeoIP2-Static-IP-Score-Test.json +15 -0
- data/test/data/source-data/GeoIP2-User-Count-Test.json +18 -0
- data/test/data/source-data/GeoLite2-City-Test.json +168 -3
- data/test/data/source-data/GeoLite2-Country-Test.json +92 -3
- data/test/data/test-data/GeoIP-Anonymous-Plus-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Anonymous-IP-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb +0 -0
- data/test/data/test-data/GeoIP2-City-Test-Invalid-Node-Count.mmdb +0 -0
- data/test/data/test-data/GeoIP2-City-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Connection-Type-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Country-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-DensityIncome-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Domain-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Enterprise-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-IP-Risk-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-ISP-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Precision-Enterprise-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Static-IP-Score-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-User-Count-Test.mmdb +0 -0
- data/test/data/test-data/GeoLite2-ASN-Test.mmdb +0 -0
- data/test/data/test-data/GeoLite2-City-Test.mmdb +0 -0
- data/test/data/test-data/GeoLite2-Country-Test.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-no-ipv4-search-tree.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-string-value-entries.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-broken-pointers-24.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-broken-search-tree-24.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-decoder.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv4-24.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv4-28.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv4-32.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv6-24.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv6-28.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv6-32.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-metadata-pointers.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-mixed-24.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-mixed-28.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-mixed-32.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-nested.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-pointer-decoder.mmdb +0 -0
- data/test/data/test-data/README.md +28 -12
- data/test/test_client.rb +18 -2
- data/test/test_reader.rb +42 -1
- metadata +25 -13
- data/test/data/LICENSE +0 -4
- data/test/data/perltidyrc +0 -12
- data/test/data/source-data/README +0 -15
- data/test/data/test-data/write-test-data.pl +0 -695
@@ -0,0 +1,73 @@
|
|
1
|
+
package writer
|
2
|
+
|
3
|
+
import (
|
4
|
+
"fmt"
|
5
|
+
"net/netip"
|
6
|
+
|
7
|
+
"github.com/maxmind/mmdbwriter"
|
8
|
+
"github.com/maxmind/mmdbwriter/mmdbtype"
|
9
|
+
"go4.org/netipx"
|
10
|
+
)
|
11
|
+
|
12
|
+
// WriteDeeplyNestedStructuresTestDB writes an mmdb file with deeply nested record value types.
|
13
|
+
func (w *Writer) WriteDeeplyNestedStructuresTestDB() error {
|
14
|
+
dbWriter, err := mmdbwriter.New(
|
15
|
+
mmdbwriter.Options{
|
16
|
+
DatabaseType: "MaxMind DB Nested Data Structures",
|
17
|
+
Description: map[string]string{
|
18
|
+
"en": "MaxMind DB Nested Data Structures Test database - contains deeply nested map/array structures",
|
19
|
+
},
|
20
|
+
DisableIPv4Aliasing: false,
|
21
|
+
IncludeReservedNetworks: true,
|
22
|
+
IPVersion: 6,
|
23
|
+
Languages: []string{"en"},
|
24
|
+
RecordSize: 24,
|
25
|
+
},
|
26
|
+
)
|
27
|
+
if err != nil {
|
28
|
+
return fmt.Errorf("creating mmdbwriter: %w", err)
|
29
|
+
}
|
30
|
+
|
31
|
+
addrs, err := parseIPSlice(ipSample)
|
32
|
+
if err != nil {
|
33
|
+
return fmt.Errorf("parsing ip addresses: %w", err)
|
34
|
+
}
|
35
|
+
if err := insertNestedStructure(dbWriter, addrs); err != nil {
|
36
|
+
return fmt.Errorf("inserting all types records: %w", err)
|
37
|
+
}
|
38
|
+
|
39
|
+
if err := w.write(dbWriter, "MaxMind-DB-test-nested.mmdb"); err != nil {
|
40
|
+
return fmt.Errorf("writing database: %w", err)
|
41
|
+
}
|
42
|
+
return nil
|
43
|
+
}
|
44
|
+
|
45
|
+
// insertNestedStructure inserts records with deeply nested structures.
|
46
|
+
func insertNestedStructure(w *mmdbwriter.Tree, ipAddresses []netip.Prefix) error {
|
47
|
+
nestedStruct := mmdbtype.Map{
|
48
|
+
"map1": mmdbtype.Map{
|
49
|
+
"map2": mmdbtype.Map{
|
50
|
+
"array": mmdbtype.Slice{
|
51
|
+
mmdbtype.Map{
|
52
|
+
"map3": mmdbtype.Map{
|
53
|
+
"a": mmdbtype.Uint32(1),
|
54
|
+
"b": mmdbtype.Uint32(2),
|
55
|
+
"c": mmdbtype.Uint32(3),
|
56
|
+
},
|
57
|
+
},
|
58
|
+
},
|
59
|
+
},
|
60
|
+
},
|
61
|
+
}
|
62
|
+
|
63
|
+
for _, addr := range ipAddresses {
|
64
|
+
err := w.Insert(
|
65
|
+
netipx.PrefixIPNet(addr),
|
66
|
+
nestedStruct,
|
67
|
+
)
|
68
|
+
if err != nil {
|
69
|
+
return fmt.Errorf("inserting ip: %w", err)
|
70
|
+
}
|
71
|
+
}
|
72
|
+
return nil
|
73
|
+
}
|
@@ -0,0 +1,61 @@
|
|
1
|
+
// Package writer defines database writers responsible
|
2
|
+
// for generating test mmdb files.
|
3
|
+
package writer
|
4
|
+
|
5
|
+
import (
|
6
|
+
"errors"
|
7
|
+
"fmt"
|
8
|
+
"io/fs"
|
9
|
+
"os"
|
10
|
+
"path/filepath"
|
11
|
+
|
12
|
+
"github.com/maxmind/mmdbwriter"
|
13
|
+
)
|
14
|
+
|
15
|
+
var ipSample = []string{
|
16
|
+
"::1.1.1.0/120",
|
17
|
+
"::2.2.0.0/112",
|
18
|
+
"::3.0.0.0/104",
|
19
|
+
"::4.5.6.7/128",
|
20
|
+
"abcd::/64",
|
21
|
+
"1000::1234:0000/112",
|
22
|
+
}
|
23
|
+
|
24
|
+
// Writer is responsible for writing test mmdb databases
|
25
|
+
// based on the provided data sources.
|
26
|
+
type Writer struct {
|
27
|
+
source string
|
28
|
+
target string
|
29
|
+
}
|
30
|
+
|
31
|
+
// New initializes a new test database writer struct.
|
32
|
+
func New(source, target string) (*Writer, error) {
|
33
|
+
s := filepath.Clean(source)
|
34
|
+
if _, err := os.Stat(s); errors.Is(err, fs.ErrNotExist) {
|
35
|
+
return nil, fmt.Errorf("source directory does not exist: %w", err)
|
36
|
+
}
|
37
|
+
|
38
|
+
t := filepath.Clean(target)
|
39
|
+
//nolint:gosec // not security sensitive.
|
40
|
+
if err := os.MkdirAll(t, os.ModePerm); err != nil {
|
41
|
+
return nil, fmt.Errorf("creating target directory: %w", err)
|
42
|
+
}
|
43
|
+
|
44
|
+
return &Writer{
|
45
|
+
source: s,
|
46
|
+
target: t,
|
47
|
+
}, nil
|
48
|
+
}
|
49
|
+
|
50
|
+
func (w *Writer) write(dbWriter *mmdbwriter.Tree, fileName string) error {
|
51
|
+
outputFile, err := os.Create(filepath.Clean(filepath.Join(w.target, fileName)))
|
52
|
+
if err != nil {
|
53
|
+
return fmt.Errorf("creating mmdb file: %w", err)
|
54
|
+
}
|
55
|
+
defer outputFile.Close()
|
56
|
+
|
57
|
+
if _, err := dbWriter.WriteTo(outputFile); err != nil {
|
58
|
+
return fmt.Errorf("writing mmdb file: %w", err)
|
59
|
+
}
|
60
|
+
return nil
|
61
|
+
}
|
@@ -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
|
+
]
|