From 5b021ce1e6f313eaf6cc7e9666840d41bc709f82 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Mon, 21 Apr 2025 12:23:23 -0700 Subject: [PATCH 1/5] Add database name to docs --- lib/maxmind/geoip2/reader.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/maxmind/geoip2/reader.rb b/lib/maxmind/geoip2/reader.rb index 6c546e7..786f80f 100644 --- a/lib/maxmind/geoip2/reader.rb +++ b/lib/maxmind/geoip2/reader.rb @@ -86,7 +86,7 @@ def initialize(*args) # rubocop:enable Metrics/CyclomaticComplexity # rubocop:enable Metrics/PerceivedComplexity - # Look up the IP address in the database. + # Look up the IP address in the Anonymous IP database. # # @param ip_address [String] a string in the standard notation. It may be # IPv4 or IPv6. From 010fcf8d4429a16c18b435d7d76850b2bb4b4aca Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Mon, 21 Apr 2025 12:58:16 -0700 Subject: [PATCH 2/5] Add Anonymous Plus support --- CHANGELOG.md | 3 ++ README.md | 15 +++++++ lib/maxmind/geoip2/model/anonymous_plus.rb | 46 ++++++++++++++++++++++ lib/maxmind/geoip2/reader.rb | 25 ++++++++++++ test/data | 2 +- test/test_reader.rb | 23 +++++++++++ 6 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 lib/maxmind/geoip2/model/anonymous_plus.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index a8647f7..48860e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## 1.3.0 +* Support for the GeoIP Anonymous Plus database has been added. To do a + lookup in this database, use the `anonymous_plus` method on + `MaxMind::GeoIP2::Reader`. * Ruby 3.0+ is now required. If you're using Ruby 2.5, 2.6, or 2.7, please use version 1.2.0 of this gem. * Deprecated `metro_code` on `MaxMind::GeoIP2::Record::Location`. The code diff --git a/README.md b/README.md index 6f4edf6..73b93ac 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,21 @@ record = reader.anonymous_ip('128.101.101.101') puts "Anonymous" if record.is_anonymous ``` +### Anonymous Plus Example + +```ruby +require 'maxmind/geoip2' + +# This creates the Reader object which should be reused across lookups. +reader = MaxMind::GeoIP2::Reader.new( + database: '/usr/share/GeoIP/GeoIP-Anonymous-Plus.mmdb', +) + +record = reader.anonymous_ip('128.101.101.101') + +puts record.anonymizer_confidence # 30 +``` + ### ASN Example ```ruby diff --git a/lib/maxmind/geoip2/model/anonymous_plus.rb b/lib/maxmind/geoip2/model/anonymous_plus.rb new file mode 100644 index 0000000..109c9a5 --- /dev/null +++ b/lib/maxmind/geoip2/model/anonymous_plus.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'date' +require 'maxmind/geoip2/model/anonymous_ip' + +module MaxMind + module GeoIP2 + module Model + # Model class for the Anonymous Plus database. + class AnonymousPlus < AnonymousIP + # A score ranging from 1 to 99 that is our percent confidence that the + # network is currently part of an actively used VPN service. + # + # @return [Integer, nil] + def anonymizer_confidence + get('anonymizer_confidence') + end + + # The last day that the network was sighted in our analysis of + # anonymized networks. This value is parsed lazily. + # + # @return [Date, nil] A Date object representing the last seen date, + # or nil if the date is not available. + def network_last_seen + return @network_last_seen if defined?(@network_last_seen) + + date_string = get('network_last_seen') + + if !date_string + return nil + end + + @network_last_seen = Date.parse(date_string) + end + + # The name of the VPN provider (e.g., NordVPN, SurfShark, etc.) + # associated with the network. + # + # @return [String, nil] + def provider_name + get('provider_name') + end + end + end + end +end diff --git a/lib/maxmind/geoip2/reader.rb b/lib/maxmind/geoip2/reader.rb index 786f80f..26e4712 100644 --- a/lib/maxmind/geoip2/reader.rb +++ b/lib/maxmind/geoip2/reader.rb @@ -3,6 +3,7 @@ require 'maxmind/db' require 'maxmind/geoip2/errors' require 'maxmind/geoip2/model/anonymous_ip' +require 'maxmind/geoip2/model/anonymous_plus' require 'maxmind/geoip2/model/asn' require 'maxmind/geoip2/model/city' require 'maxmind/geoip2/model/connection_type' @@ -110,6 +111,30 @@ def anonymous_ip(ip_address) ) end + # Look up the IP address in the Anonymous Plus database. + # + # @param ip_address [String] a string in the standard notation. It may be + # IPv4 or IPv6. + # + # @return [MaxMind::GeoIP2::Model::AnonymousPlus] + # + # @raise [ArgumentError] if used against a non-Anonymous Plus database + # or if you attempt to look up an IPv6 address in an IPv4 only database. + # + # @raise [AddressNotFoundError] if the IP address is not found in the + # database. + # + # @raise [MaxMind::DB::InvalidDatabaseError] if the database appears + # corrupt. + def anonymous_plus(ip_address) + flat_model_for( + Model::AnonymousPlus, + 'anonymous_plus', + 'GeoIP-Anonymous-Plus', + ip_address, + ) + end + # Look up the IP address in an ASN database. # # @param ip_address [String] a string in the standard notation. It may be diff --git a/test/data b/test/data index 1271107..f387805 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 1271107ccad72c320bc7dc8aefd767cba550101a +Subproject commit f387805e4af30036ec4245c9069ac1f2fdae4953 diff --git a/test/test_reader.rb b/test/test_reader.rb index 7691b41..54593c6 100644 --- a/test/test_reader.rb +++ b/test/test_reader.rb @@ -37,6 +37,29 @@ def test_anonymous_ip_residential_proxy reader.close end + def test_anonymous_plus + reader = MaxMind::GeoIP2::Reader.new( + 'test/data/test-data/GeoIP-Anonymous-Plus-Test.mmdb', + ) + ip = '1.2.0.1' + record = reader.anonymous_plus(ip) + + assert_equal(30, record.anonymizer_confidence) + assert_equal(true, record.anonymous?) + assert_equal(true, record.anonymous_vpn?) + assert_equal(false, record.hosting_provider?) + assert_equal(Date.new(2025, 4, 14), record.network_last_seen) + assert_equal('foo', record.provider_name) + assert_equal(false, record.public_proxy?) + assert_equal(false, record.residential_proxy?) + assert_equal(false, record.tor_exit_node?) + + assert_equal(ip, record.ip_address) + assert_equal('1.2.0.1/32', record.network) + + reader.close + end + def test_asn reader = MaxMind::GeoIP2::Reader.new( 'test/data/test-data/GeoLite2-ASN-Test.mmdb', From 39bdcbd9757530f58c787bbd537cc4063bdf4caa Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Mon, 21 Apr 2025 13:13:06 -0700 Subject: [PATCH 3/5] Update to the new plugin sytax --- .rubocop.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index 973ecd0..8906fa1 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,4 +1,5 @@ -require: rubocop-performance +plugins: + - rubocop-performance AllCops: TargetRubyVersion: '3.0' From b10b0fcc467402e168a6f4ce789190d09d7f4c0c Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Mon, 21 Apr 2025 13:13:23 -0700 Subject: [PATCH 4/5] Make spacing consistent --- .rubocop.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index 8906fa1..460c27c 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -41,4 +41,4 @@ Style/HashTransformKeys: Enabled: true Gemspec/DevelopmentDependencies: - Enabled: false + Enabled: false From a8e7af0fd47cb7c1e6c6981c2a18d04cf3e20ca7 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Tue, 6 May 2025 08:38:06 -0700 Subject: [PATCH 5/5] Fix method name in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 73b93ac..3fff9f2 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,7 @@ reader = MaxMind::GeoIP2::Reader.new( database: '/usr/share/GeoIP/GeoIP-Anonymous-Plus.mmdb', ) -record = reader.anonymous_ip('128.101.101.101') +record = reader.anonymous_plus('128.101.101.101') puts record.anonymizer_confidence # 30 ```