maxmind-geoip2 0.1.0 → 0.6.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 +32 -0
- data/Gemfile +1 -5
- data/README.dev.md +1 -1
- data/README.md +82 -8
- data/lib/maxmind/geoip2.rb +1 -0
- data/lib/maxmind/geoip2/client.rb +333 -0
- data/lib/maxmind/geoip2/errors.rb +37 -3
- data/lib/maxmind/geoip2/model/abstract.rb +19 -15
- data/lib/maxmind/geoip2/model/anonymous_ip.rb +62 -50
- data/lib/maxmind/geoip2/model/asn.rb +33 -29
- data/lib/maxmind/geoip2/model/city.rb +59 -55
- data/lib/maxmind/geoip2/model/connection_type.rb +27 -23
- data/lib/maxmind/geoip2/model/country.rb +64 -53
- data/lib/maxmind/geoip2/model/domain.rb +27 -23
- data/lib/maxmind/geoip2/model/enterprise.rb +13 -9
- data/lib/maxmind/geoip2/model/insights.rb +18 -0
- data/lib/maxmind/geoip2/model/isp.rb +45 -41
- data/lib/maxmind/geoip2/reader.rb +260 -233
- data/lib/maxmind/geoip2/record/abstract.rb +17 -13
- data/lib/maxmind/geoip2/record/city.rb +33 -29
- data/lib/maxmind/geoip2/record/continent.rb +32 -28
- data/lib/maxmind/geoip2/record/country.rb +47 -43
- data/lib/maxmind/geoip2/record/location.rb +64 -60
- data/lib/maxmind/geoip2/record/maxmind.rb +21 -0
- data/lib/maxmind/geoip2/record/place.rb +22 -18
- data/lib/maxmind/geoip2/record/postal.rb +26 -22
- data/lib/maxmind/geoip2/record/represented_country.rb +20 -16
- data/lib/maxmind/geoip2/record/subdivision.rb +42 -38
- data/lib/maxmind/geoip2/record/traits.rb +204 -191
- data/maxmind-geoip2.gemspec +11 -3
- data/test/data/bad-data/maxminddb-python/bad-unicode-in-map-key.mmdb +0 -0
- data/test/data/source-data/GeoIP2-Anonymous-IP-Test.json +1 -0
- data/test/data/source-data/GeoIP2-ISP-Test.json +3 -1
- data/test/data/source-data/GeoIP2-Precision-Enterprise-Test.json +87 -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-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/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/write-test-data.pl +68 -18
- data/test/test_client.rb +426 -0
- data/test/test_model_country.rb +16 -0
- data/test/test_reader.rb +59 -0
- metadata +113 -10
- data/Gemfile.lock +0 -38
@@ -1,7 +1,41 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module MaxMind
|
4
|
-
|
5
|
-
|
3
|
+
module MaxMind
|
4
|
+
module GeoIP2
|
5
|
+
# An AddressNotFoundError means the IP address was not found in the
|
6
|
+
# database or the web service said the IP address was not found.
|
7
|
+
class AddressNotFoundError < RuntimeError
|
8
|
+
end
|
9
|
+
|
10
|
+
# An HTTPError means there was an unexpected HTTP status or response.
|
11
|
+
class HTTPError < RuntimeError
|
12
|
+
end
|
13
|
+
|
14
|
+
# An AddressInvalidError means the IP address was invalid.
|
15
|
+
class AddressInvalidError < RuntimeError
|
16
|
+
end
|
17
|
+
|
18
|
+
# An AddressReservedError means the IP address is reserved.
|
19
|
+
class AddressReservedError < RuntimeError
|
20
|
+
end
|
21
|
+
|
22
|
+
# An AuthenticationError means there was a problem authenticating to the
|
23
|
+
# web service.
|
24
|
+
class AuthenticationError < RuntimeError
|
25
|
+
end
|
26
|
+
|
27
|
+
# An InsufficientFundsError means the account is out of credits.
|
28
|
+
class InsufficientFundsError < RuntimeError
|
29
|
+
end
|
30
|
+
|
31
|
+
# A PermissionRequiredError means the account does not have permission to
|
32
|
+
# use the requested service.
|
33
|
+
class PermissionRequiredError < RuntimeError
|
34
|
+
end
|
35
|
+
|
36
|
+
# An InvalidRequestError means the web service returned an error and there
|
37
|
+
# is no more specific error class.
|
38
|
+
class InvalidRequestError < RuntimeError
|
39
|
+
end
|
6
40
|
end
|
7
41
|
end
|
@@ -2,26 +2,30 @@
|
|
2
2
|
|
3
3
|
require 'ipaddr'
|
4
4
|
|
5
|
-
module MaxMind
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
module MaxMind
|
6
|
+
module GeoIP2
|
7
|
+
module Model
|
8
|
+
# @!visibility private
|
9
|
+
class Abstract
|
10
|
+
def initialize(record)
|
11
|
+
@record = record
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
13
|
+
ip = IPAddr.new(record['ip_address']).mask(record['prefix_length'])
|
14
|
+
record['network'] = format('%s/%d', ip.to_s, record['prefix_length'])
|
15
|
+
end
|
14
16
|
|
15
|
-
|
17
|
+
protected
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
|
19
|
+
def get(key)
|
20
|
+
if @record.nil? || !@record.key?(key)
|
21
|
+
return false if key.start_with?('is_')
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
+
return nil
|
24
|
+
end
|
23
25
|
|
24
|
-
|
26
|
+
@record[key]
|
27
|
+
end
|
28
|
+
end
|
25
29
|
end
|
26
30
|
end
|
27
31
|
end
|
@@ -2,62 +2,74 @@
|
|
2
2
|
|
3
3
|
require 'maxmind/geoip2/model/abstract'
|
4
4
|
|
5
|
-
module MaxMind
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
5
|
+
module MaxMind
|
6
|
+
module GeoIP2
|
7
|
+
module Model
|
8
|
+
# Model class for the Anonymous IP database.
|
9
|
+
class AnonymousIP < Abstract
|
10
|
+
# This is true if the IP address belongs to any sort of anonymous network.
|
11
|
+
#
|
12
|
+
# @return [Boolean]
|
13
|
+
def anonymous?
|
14
|
+
get('is_anonymous')
|
15
|
+
end
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
17
|
+
# This is true if the IP address is registered to an anonymous VPN
|
18
|
+
# provider. If a VPN provider does not register subnets under names
|
19
|
+
# associated with them, we will likely only flag their IP ranges using the
|
20
|
+
# hosting_provider? method.
|
21
|
+
#
|
22
|
+
# @return [Boolean]
|
23
|
+
def anonymous_vpn?
|
24
|
+
get('is_anonymous_vpn')
|
25
|
+
end
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
# This is true if the IP address belongs to a hosting or VPN provider (see
|
28
|
+
# description of the anonymous_vpn? method).
|
29
|
+
#
|
30
|
+
# @return [Boolean]
|
31
|
+
def hosting_provider?
|
32
|
+
get('is_hosting_provider')
|
33
|
+
end
|
32
34
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
35
|
+
# The IP address that the data in the model is for.
|
36
|
+
#
|
37
|
+
# @return [String]
|
38
|
+
def ip_address
|
39
|
+
get('ip_address')
|
40
|
+
end
|
39
41
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
42
|
+
# The network in CIDR notation associated with the record. In particular,
|
43
|
+
# this is the largest network where all of the fields besides ip_address
|
44
|
+
# have the same value.
|
45
|
+
#
|
46
|
+
# @return [String]
|
47
|
+
def network
|
48
|
+
get('network')
|
49
|
+
end
|
48
50
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
51
|
+
# This is true if the IP address belongs to a public proxy.
|
52
|
+
#
|
53
|
+
# @return [Boolean]
|
54
|
+
def public_proxy?
|
55
|
+
get('is_public_proxy')
|
56
|
+
end
|
57
|
+
|
58
|
+
# This is true if the IP address is on a suspected anonymizing network
|
59
|
+
# and belongs to a residential ISP.
|
60
|
+
#
|
61
|
+
# @return [Boolean]
|
62
|
+
def residential_proxy?
|
63
|
+
get('is_residential_proxy')
|
64
|
+
end
|
55
65
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
66
|
+
# This is true if the IP address is a Tor exit node.
|
67
|
+
#
|
68
|
+
# @return [Boolean]
|
69
|
+
def tor_exit_node?
|
70
|
+
get('is_tor_exit_node')
|
71
|
+
end
|
72
|
+
end
|
61
73
|
end
|
62
74
|
end
|
63
75
|
end
|
@@ -2,38 +2,42 @@
|
|
2
2
|
|
3
3
|
require 'maxmind/geoip2/model/abstract'
|
4
4
|
|
5
|
-
module MaxMind
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
5
|
+
module MaxMind
|
6
|
+
module GeoIP2
|
7
|
+
module Model
|
8
|
+
# Model class for the GeoLite2 ASN database.
|
9
|
+
class ASN < Abstract
|
10
|
+
# The autonomous system number associated with the IP address.
|
11
|
+
#
|
12
|
+
# @return [Integer, nil]
|
13
|
+
def autonomous_system_number
|
14
|
+
get('autonomous_system_number')
|
15
|
+
end
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
# The organization associated with the registered autonomous system number
|
18
|
+
# for the IP address.
|
19
|
+
#
|
20
|
+
# @return [String, nil]
|
21
|
+
def autonomous_system_organization
|
22
|
+
get('autonomous_system_organization')
|
23
|
+
end
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
# The IP address that the data in the model is for.
|
26
|
+
#
|
27
|
+
# @return [String]
|
28
|
+
def ip_address
|
29
|
+
get('ip_address')
|
30
|
+
end
|
29
31
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
32
|
+
# The network in CIDR notation associated with the record. In particular,
|
33
|
+
# this is the largest network where all of the fields besides ip_address
|
34
|
+
# have the same value.
|
35
|
+
#
|
36
|
+
# @return [String]
|
37
|
+
def network
|
38
|
+
get('network')
|
39
|
+
end
|
40
|
+
end
|
37
41
|
end
|
38
42
|
end
|
39
43
|
end
|
@@ -6,69 +6,73 @@ require 'maxmind/geoip2/record/location'
|
|
6
6
|
require 'maxmind/geoip2/record/postal'
|
7
7
|
require 'maxmind/geoip2/record/subdivision'
|
8
8
|
|
9
|
-
module MaxMind
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
9
|
+
module MaxMind
|
10
|
+
module GeoIP2
|
11
|
+
module Model
|
12
|
+
# Model class for the data returned by the GeoIP2 City web service and
|
13
|
+
# database. It is also used for GeoLite2 City lookups.
|
14
|
+
#
|
15
|
+
# The only difference between the City and Insights model classes is which
|
16
|
+
# fields in each record may be populated. See
|
17
|
+
# https://dev.maxmind.com/geoip/geoip2/web-services for more details.
|
18
|
+
#
|
19
|
+
# See {MaxMind::GeoIP2::Model::Country} for inherited methods.
|
20
|
+
class City < Country
|
21
|
+
# City data for the IP address.
|
22
|
+
#
|
23
|
+
# @return [MaxMind::GeoIP2::Record::City]
|
24
|
+
attr_reader :city
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
# Location data for the IP address.
|
27
|
+
#
|
28
|
+
# @return [MaxMind::GeoIP2::Record::Location]
|
29
|
+
attr_reader :location
|
28
30
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
# Postal data for the IP address.
|
32
|
+
#
|
33
|
+
# @return [MaxMind::GeoIP2::Record::Postal]
|
34
|
+
attr_reader :postal
|
33
35
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
36
|
+
# The country subdivisions for the IP address.
|
37
|
+
#
|
38
|
+
# The number and type of subdivisions varies by country, but a subdivision
|
39
|
+
# is typically a state, province, country, etc. Subdivisions are ordered
|
40
|
+
# from most general (largest) to most specific (smallest).
|
41
|
+
#
|
42
|
+
# If the response did not contain any subdivisions, this attribute will be
|
43
|
+
# an empty array.
|
44
|
+
#
|
45
|
+
# @return [Array<MaxMind::GeoIP2::Record::Subdivision>]
|
46
|
+
attr_reader :subdivisions
|
45
47
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
48
|
+
# @!visibility private
|
49
|
+
def initialize(record, locales)
|
50
|
+
super(record, locales)
|
51
|
+
@city = MaxMind::GeoIP2::Record::City.new(record['city'], locales)
|
52
|
+
@location = MaxMind::GeoIP2::Record::Location.new(record['location'])
|
53
|
+
@postal = MaxMind::GeoIP2::Record::Postal.new(record['postal'])
|
54
|
+
@subdivisions = create_subdivisions(record['subdivisions'], locales)
|
55
|
+
end
|
54
56
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
57
|
+
# The most specific subdivision returned.
|
58
|
+
#
|
59
|
+
# If the response did not contain any subdivisions, this method returns
|
60
|
+
# nil.
|
61
|
+
#
|
62
|
+
# @return [MaxMind::GeoIP2::Record::Subdivision, nil]
|
63
|
+
def most_specific_subdivision
|
64
|
+
@subdivisions.last
|
65
|
+
end
|
64
66
|
|
65
|
-
|
67
|
+
private
|
66
68
|
|
67
|
-
|
68
|
-
|
69
|
+
def create_subdivisions(subdivisions, locales)
|
70
|
+
return [] if subdivisions.nil?
|
69
71
|
|
70
|
-
|
71
|
-
|
72
|
+
subdivisions.map do |s|
|
73
|
+
MaxMind::GeoIP2::Record::Subdivision.new(s, locales)
|
74
|
+
end
|
75
|
+
end
|
72
76
|
end
|
73
77
|
end
|
74
78
|
end
|
@@ -2,31 +2,35 @@
|
|
2
2
|
|
3
3
|
require 'maxmind/geoip2/model/abstract'
|
4
4
|
|
5
|
-
module MaxMind
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
5
|
+
module MaxMind
|
6
|
+
module GeoIP2
|
7
|
+
module Model
|
8
|
+
# Model class for the GeoIP2 Connection Type database.
|
9
|
+
class ConnectionType < Abstract
|
10
|
+
# The connection type may take the following values: "Dialup", "Cable/DSL",
|
11
|
+
# "Corporate", "Cellular". Additional values may be added in the future.
|
12
|
+
#
|
13
|
+
# @return [String, nil]
|
14
|
+
def connection_type
|
15
|
+
get('connection_type')
|
16
|
+
end
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
# The IP address that the data in the model is for.
|
19
|
+
#
|
20
|
+
# @return [String]
|
21
|
+
def ip_address
|
22
|
+
get('ip_address')
|
23
|
+
end
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
# The network in CIDR notation associated with the record. In particular,
|
26
|
+
# this is the largest network where all of the fields besides ip_address
|
27
|
+
# have the same value.
|
28
|
+
#
|
29
|
+
# @return [String]
|
30
|
+
def network
|
31
|
+
get('network')
|
32
|
+
end
|
33
|
+
end
|
30
34
|
end
|
31
35
|
end
|
32
36
|
end
|