diff --git a/CHANGELOG.md b/CHANGELOG.md index 0799b303..8da00550 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,17 @@ CHANGELOG ========= +5.2.0 (unreleased) +------------------ + +* A new `residential` field has been added to the `Anonymizer` record. This + is an `AnonymizerFeed` record containing `confidence`, `networkLastSeen`, + and `providerName` fields with residential proxy data for the network. + `residential` may be populated even when none of the other fields on + `Anonymizer` are set. The `AnonymizerFeed` record is intended to be reused + for additional feeds, such as VPNs, mobile networks, and hosting or + datacenter providers, that may be added in the future. + 5.1.0 (2026-05-12) ------------------ diff --git a/src/main/java/com/maxmind/geoip2/record/Anonymizer.java b/src/main/java/com/maxmind/geoip2/record/Anonymizer.java index 6c94527a..cbc56ea2 100644 --- a/src/main/java/com/maxmind/geoip2/record/Anonymizer.java +++ b/src/main/java/com/maxmind/geoip2/record/Anonymizer.java @@ -31,6 +31,9 @@ * @param providerName The name of the VPN provider (e.g., NordVPN, SurfShark, etc.) associated * with the network. This is only available from the GeoIP Insights * web service. + * @param residential Residential proxy data for the network. This may be populated even when + * none of the other fields on this record are set. This is only available + * from the GeoIP Insights web service. */ public record Anonymizer( @JsonProperty("confidence") @@ -58,14 +61,68 @@ public record Anonymizer( LocalDate networkLastSeen, @JsonProperty("provider_name") - String providerName + String providerName, + + @JsonProperty("residential") + AnonymizerFeed residential ) implements JsonSerializable { + /** + * Compact canonical constructor that sets a default for a null {@code residential} value. + */ + public Anonymizer { + residential = residential != null ? residential : new AnonymizerFeed(); + } + /** * Constructs an {@code Anonymizer} record with {@code null} values for all the nullable * fields and {@code false} for all boolean fields. */ public Anonymizer() { - this(null, false, false, false, false, false, false, null, null); + this(null, false, false, false, false, false, false, null, null, null); + } + + /** + * Constructs an {@code Anonymizer} record without the {@code residential} field. + * + * @param confidence the confidence that the network is an actively used VPN service + * @param isAnonymous whether the IP address belongs to any sort of anonymous network + * @param isAnonymousVpn whether the IP address is registered to an anonymous VPN provider + * @param isHostingProvider whether the IP address belongs to a hosting or VPN provider + * @param isPublicProxy whether the IP address belongs to a public proxy + * @param isResidentialProxy whether the IP address is on a suspected anonymizing network + * and belongs to a residential ISP + * @param isTorExitNode whether the IP address is a Tor exit node + * @param networkLastSeen the last day that the network was sighted in our analysis of + * anonymized networks + * @param providerName the name of the VPN provider associated with the network + * @deprecated Use the canonical constructor that also accepts the {@code residential} + * field. This constructor is provided for backward compatibility and will be + * removed in version 6.0.0. + */ + @Deprecated(since = "5.2.0", forRemoval = true) + public Anonymizer( + Integer confidence, + boolean isAnonymous, + boolean isAnonymousVpn, + boolean isHostingProvider, + boolean isPublicProxy, + boolean isResidentialProxy, + boolean isTorExitNode, + LocalDate networkLastSeen, + String providerName + ) { + this( + confidence, + isAnonymous, + isAnonymousVpn, + isHostingProvider, + isPublicProxy, + isResidentialProxy, + isTorExitNode, + networkLastSeen, + providerName, + null + ); } } diff --git a/src/main/java/com/maxmind/geoip2/record/AnonymizerFeed.java b/src/main/java/com/maxmind/geoip2/record/AnonymizerFeed.java new file mode 100644 index 00000000..cf5d5c26 --- /dev/null +++ b/src/main/java/com/maxmind/geoip2/record/AnonymizerFeed.java @@ -0,0 +1,43 @@ +package com.maxmind.geoip2.record; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.maxmind.geoip2.JsonSerializable; +import java.time.LocalDate; + +/** + *
+ * Contains data for one type of anonymizer detection, currently residential proxies. Additional + * feeds, such as VPNs, mobile networks, and hosting or datacenter providers, may be added to the + * {@link Anonymizer} record in the future using this same record type. + *
+ *+ * This record is returned by the GeoIP Insights web service. + *
+ * + * @param confidence A score ranging from 1 to 99 that represents our percent confidence that + * the network is an actively used residential proxy. This is only available + * from the GeoIP Insights web service. + * @param networkLastSeen The last day that the network was sighted in our analysis of + * residential proxies. This is only available from the GeoIP Insights + * web service. + * @param providerName The name of the residential proxy provider associated with the network. + * This is only available from the GeoIP Insights web service. + */ +public record AnonymizerFeed( + @JsonProperty("confidence") + Integer confidence, + + @JsonProperty("network_last_seen") + LocalDate networkLastSeen, + + @JsonProperty("provider_name") + String providerName +) implements JsonSerializable { + + /** + * Constructs an {@code AnonymizerFeed} record with {@code null} values for all fields. + */ + public AnonymizerFeed() { + this(null, null, null); + } +} diff --git a/src/test/java/com/maxmind/geoip2/model/InsightsResponseTest.java b/src/test/java/com/maxmind/geoip2/model/InsightsResponseTest.java index 38905e77..f4081dd4 100644 --- a/src/test/java/com/maxmind/geoip2/model/InsightsResponseTest.java +++ b/src/test/java/com/maxmind/geoip2/model/InsightsResponseTest.java @@ -16,6 +16,7 @@ import com.maxmind.geoip2.exception.GeoIp2Exception; import com.maxmind.geoip2.model.ConnectionTypeResponse.ConnectionType; import com.maxmind.geoip2.record.Anonymizer; +import com.maxmind.geoip2.record.AnonymizerFeed; import com.maxmind.geoip2.record.Location; import com.maxmind.geoip2.record.MaxMind; import com.maxmind.geoip2.record.Postal; @@ -206,6 +207,25 @@ public void testAnonymizer() { anonymizer.providerName(), "anonymizer.providerName() does not return NordVPN" ); + + AnonymizerFeed residential = anonymizer.residential(); + + assertNotNull(residential, "anonymizer.residential() returns null"); + assertEquals( + Integer.valueOf(82), + residential.confidence(), + "anonymizer.residential().confidence() does not return 82" + ); + assertEquals( + LocalDate.parse("2026-05-11"), + residential.networkLastSeen(), + "anonymizer.residential().networkLastSeen() does not return 2026-05-11" + ); + assertEquals( + "quickshift", + residential.providerName(), + "anonymizer.residential().providerName() does not return quickshift" + ); } @Test diff --git a/src/test/java/com/maxmind/geoip2/model/JsonTest.java b/src/test/java/com/maxmind/geoip2/model/JsonTest.java index 97e69a8b..f75157f2 100644 --- a/src/test/java/com/maxmind/geoip2/model/JsonTest.java +++ b/src/test/java/com/maxmind/geoip2/model/JsonTest.java @@ -62,6 +62,11 @@ public void testInsightsSerialization() throws IOException { .put("is_tor_exit_node", true) .put("network_last_seen", "2024-12-31") .put("provider_name", "NordVPN") + .startObjectField("residential") + .put("confidence", 82) + .put("network_last_seen", "2026-05-11") + .put("provider_name", "quickshift") + .end() .end() .startObjectField("country") .startObjectField("names") diff --git a/src/test/resources/test-data/insights0.json b/src/test/resources/test-data/insights0.json index 45118585..1f56bb33 100644 --- a/src/test/resources/test-data/insights0.json +++ b/src/test/resources/test-data/insights0.json @@ -94,6 +94,11 @@ "is_residential_proxy": true, "is_tor_exit_node": true, "network_last_seen": "2024-12-31", - "provider_name": "NordVPN" + "provider_name": "NordVPN", + "residential": { + "confidence": 82, + "network_last_seen": "2026-05-11", + "provider_name": "quickshift" + } } } \ No newline at end of file diff --git a/src/test/resources/test-data/insights1.json b/src/test/resources/test-data/insights1.json index 6cc70cba..666bcbb1 100644 --- a/src/test/resources/test-data/insights1.json +++ b/src/test/resources/test-data/insights1.json @@ -110,6 +110,11 @@ "is_residential_proxy": true, "is_tor_exit_node": true, "network_last_seen": "2024-12-31", - "provider_name": "NordVPN" + "provider_name": "NordVPN", + "residential": { + "confidence": 82, + "network_last_seen": "2026-05-11", + "provider_name": "quickshift" + } } } \ No newline at end of file