maxmind-geoip2 0.1.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|