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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/Gemfile.lock +114 -0
  4. data/README.dev.md +1 -1
  5. data/README.md +26 -6
  6. data/Rakefile +1 -0
  7. data/lib/maxmind/geoip2/client.rb +18 -9
  8. data/lib/maxmind/geoip2/model/anonymous_plus.rb +46 -0
  9. data/lib/maxmind/geoip2/model/city.rb +5 -6
  10. data/lib/maxmind/geoip2/model/connection_type.rb +3 -2
  11. data/lib/maxmind/geoip2/model/enterprise.rb +2 -3
  12. data/lib/maxmind/geoip2/model/insights.rb +3 -5
  13. data/lib/maxmind/geoip2/reader.rb +26 -1
  14. data/lib/maxmind/geoip2/record/location.rb +3 -3
  15. data/lib/maxmind/geoip2/record/traits.rb +40 -28
  16. data/lib/maxmind/geoip2/version.rb +8 -0
  17. data/maxmind-geoip2.gemspec +11 -6
  18. data/test/data/LICENSE-APACHE +202 -0
  19. data/test/data/LICENSE-MIT +17 -0
  20. data/test/data/MaxMind-DB-spec.md +1 -2
  21. data/test/data/README.md +8 -1
  22. data/test/data/cmd/write-test-data/main.go +68 -0
  23. data/test/data/go.mod +13 -0
  24. data/test/data/go.sum +16 -0
  25. data/test/data/pkg/writer/decoder.go +178 -0
  26. data/test/data/pkg/writer/geoip2.go +184 -0
  27. data/test/data/pkg/writer/ip.go +39 -0
  28. data/test/data/pkg/writer/maxmind.go +246 -0
  29. data/test/data/pkg/writer/nestedstructures.go +73 -0
  30. data/test/data/pkg/writer/writer.go +61 -0
  31. data/test/data/source-data/GeoIP-Anonymous-Plus-Test.json +175 -0
  32. data/test/data/source-data/GeoIP2-Anonymous-IP-Test.json +6 -0
  33. data/test/data/source-data/GeoIP2-City-Test.json +392 -5
  34. data/test/data/source-data/GeoIP2-Connection-Type-Test.json +15 -10
  35. data/test/data/source-data/GeoIP2-Country-Test.json +99 -25
  36. data/test/data/source-data/GeoIP2-Domain-Test.json +5 -0
  37. data/test/data/source-data/GeoIP2-Enterprise-Test.json +371 -6
  38. data/test/data/source-data/GeoIP2-IP-Risk-Test.json +31 -0
  39. data/test/data/source-data/GeoIP2-Precision-Enterprise-Sandbox-Test.json +296 -0
  40. data/test/data/source-data/GeoIP2-Precision-Enterprise-Test.json +1159 -175
  41. data/test/data/source-data/GeoIP2-Static-IP-Score-Test.json +15 -0
  42. data/test/data/source-data/GeoIP2-User-Count-Test.json +18 -0
  43. data/test/data/source-data/GeoLite2-City-Test.json +168 -3
  44. data/test/data/source-data/GeoLite2-Country-Test.json +92 -3
  45. data/test/data/test-data/GeoIP-Anonymous-Plus-Test.mmdb +0 -0
  46. data/test/data/test-data/GeoIP2-Anonymous-IP-Test.mmdb +0 -0
  47. data/test/data/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb +0 -0
  48. data/test/data/test-data/GeoIP2-City-Test-Invalid-Node-Count.mmdb +0 -0
  49. data/test/data/test-data/GeoIP2-City-Test.mmdb +0 -0
  50. data/test/data/test-data/GeoIP2-Connection-Type-Test.mmdb +0 -0
  51. data/test/data/test-data/GeoIP2-Country-Test.mmdb +0 -0
  52. data/test/data/test-data/GeoIP2-DensityIncome-Test.mmdb +0 -0
  53. data/test/data/test-data/GeoIP2-Domain-Test.mmdb +0 -0
  54. data/test/data/test-data/GeoIP2-Enterprise-Test.mmdb +0 -0
  55. data/test/data/test-data/GeoIP2-IP-Risk-Test.mmdb +0 -0
  56. data/test/data/test-data/GeoIP2-ISP-Test.mmdb +0 -0
  57. data/test/data/test-data/GeoIP2-Precision-Enterprise-Test.mmdb +0 -0
  58. data/test/data/test-data/GeoIP2-Static-IP-Score-Test.mmdb +0 -0
  59. data/test/data/test-data/GeoIP2-User-Count-Test.mmdb +0 -0
  60. data/test/data/test-data/GeoLite2-ASN-Test.mmdb +0 -0
  61. data/test/data/test-data/GeoLite2-City-Test.mmdb +0 -0
  62. data/test/data/test-data/GeoLite2-Country-Test.mmdb +0 -0
  63. data/test/data/test-data/MaxMind-DB-no-ipv4-search-tree.mmdb +0 -0
  64. data/test/data/test-data/MaxMind-DB-string-value-entries.mmdb +0 -0
  65. data/test/data/test-data/MaxMind-DB-test-broken-pointers-24.mmdb +0 -0
  66. data/test/data/test-data/MaxMind-DB-test-broken-search-tree-24.mmdb +0 -0
  67. data/test/data/test-data/MaxMind-DB-test-decoder.mmdb +0 -0
  68. data/test/data/test-data/MaxMind-DB-test-ipv4-24.mmdb +0 -0
  69. data/test/data/test-data/MaxMind-DB-test-ipv4-28.mmdb +0 -0
  70. data/test/data/test-data/MaxMind-DB-test-ipv4-32.mmdb +0 -0
  71. data/test/data/test-data/MaxMind-DB-test-ipv6-24.mmdb +0 -0
  72. data/test/data/test-data/MaxMind-DB-test-ipv6-28.mmdb +0 -0
  73. data/test/data/test-data/MaxMind-DB-test-ipv6-32.mmdb +0 -0
  74. data/test/data/test-data/MaxMind-DB-test-metadata-pointers.mmdb +0 -0
  75. data/test/data/test-data/MaxMind-DB-test-mixed-24.mmdb +0 -0
  76. data/test/data/test-data/MaxMind-DB-test-mixed-28.mmdb +0 -0
  77. data/test/data/test-data/MaxMind-DB-test-mixed-32.mmdb +0 -0
  78. data/test/data/test-data/MaxMind-DB-test-nested.mmdb +0 -0
  79. data/test/data/test-data/MaxMind-DB-test-pointer-decoder.mmdb +0 -0
  80. data/test/data/test-data/README.md +28 -12
  81. data/test/test_client.rb +18 -2
  82. data/test/test_reader.rb +42 -1
  83. metadata +25 -13
  84. data/test/data/LICENSE +0 -4
  85. data/test/data/perltidyrc +0 -12
  86. data/test/data/source-data/README +0 -15
  87. 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
+ ]
@@ -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
  ]