This is an automated email from the ASF dual-hosted git repository.

sebastian-nagel pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nutch.git


The following commit(s) were added to refs/heads/master by this push:
     new c336054cf NUTCH-3187 - protocol-okhttp: IP address filter fails to 
compare first byte for /32 resp. /128 CIDRs
c336054cf is described below

commit c336054cfe70f166609546086c119b7209de740f
Author: Sebastian Nagel <[email protected]>
AuthorDate: Tue Jun 16 19:11:02 2026 +0200

    NUTCH-3187 - protocol-okhttp: IP address filter fails to compare first byte 
for /32 resp. /128 CIDRs
---
 .../org/apache/nutch/protocol/okhttp/CIDR.java     | 38 +++++++++++++++++-----
 .../nutch/protocol/okhttp/IPFilterRules.java       |  6 ++--
 .../protocol/okhttp/TestIPAddressFiltering.java    | 19 +++++++++++
 3 files changed, 51 insertions(+), 12 deletions(-)

diff --git 
a/src/plugin/protocol-okhttp/src/java/org/apache/nutch/protocol/okhttp/CIDR.java
 
b/src/plugin/protocol-okhttp/src/java/org/apache/nutch/protocol/okhttp/CIDR.java
index 3add082a8..1d97e6728 100644
--- 
a/src/plugin/protocol-okhttp/src/java/org/apache/nutch/protocol/okhttp/CIDR.java
+++ 
b/src/plugin/protocol-okhttp/src/java/org/apache/nutch/protocol/okhttp/CIDR.java
@@ -27,10 +27,16 @@ import com.google.common.net.InetAddresses;
  * defined by the CIDR.
  */
 public class CIDR {
-  InetAddress addr;
-  int mask;
 
-  public CIDR(InetAddress address, int mask) {
+  private final InetAddress addr;
+  private final int mask;
+
+  public CIDR(InetAddress address, int mask) throws IllegalArgumentException {
+    int maxMask = address.getAddress().length * 8;
+    if (mask < 0 || mask > maxMask) {
+      throw new IllegalArgumentException(
+          "Invalid CIDR mask /" + mask + " for " + address);
+    }
     this.addr = address;
     this.mask = mask;
   }
@@ -42,16 +48,23 @@ public class CIDR {
       ipStr = cidr.substring(0, sep);
     }
     addr = InetAddresses.forString(ipStr);
+    int parsedMask;
     if (sep > -1) {
-      mask = Integer.parseInt(cidr.substring(sep + 1));
+      parsedMask = Integer.parseInt(cidr.substring(sep + 1));
     } else {
-      mask = addr.getAddress().length * 8;
+      parsedMask = addr.getAddress().length * 8;
     }
     if (cidr.indexOf(':') > -1 && addr.getAddress().length == 4) {
       // IPv4-mapped IPv6 addresses are automatically converted to IPv4,
       // need to shift the mask
-      mask = Math.max(0, mask - 96);
+      parsedMask = Math.max(0, parsedMask - 96);
+    }
+    int maxMask = addr.getAddress().length * 8;
+    if (parsedMask < 0 || parsedMask > maxMask) {
+      throw new IllegalArgumentException(
+          "Invalid CIDR mask /" + parsedMask + " for " + ipStr);
     }
+    this.mask = parsedMask;
   }
 
   public boolean contains(InetAddress address) {
@@ -63,11 +76,18 @@ public class CIDR {
     }
     for (int i = 0; i < addr0.length; i++) {
       int remainingMaskBits = mask - (i * 8);
-      if (remainingMaskBits <= 0)
+      if (remainingMaskBits <= 0) {
         return true;
-      int m = ~(0xff >> remainingMaskBits); // mask for byte under cursor
-      if ((addr0[i] & m) != (addr1[i] & m))
+      }
+      /*
+       * keep the mask within one byte so the shift does not wrap (Java shifts
+       * mod 32)
+       */
+      int m = remainingMaskBits >= 8 ? 0xff
+          : (0xff << (8 - remainingMaskBits)) & 0xff;
+      if ((addr0[i] & m) != (addr1[i] & m)) {
         return false;
+      }
     }
     return true;
   }
diff --git 
a/src/plugin/protocol-okhttp/src/java/org/apache/nutch/protocol/okhttp/IPFilterRules.java
 
b/src/plugin/protocol-okhttp/src/java/org/apache/nutch/protocol/okhttp/IPFilterRules.java
index a9252ddc5..5ba0a9fd6 100644
--- 
a/src/plugin/protocol-okhttp/src/java/org/apache/nutch/protocol/okhttp/IPFilterRules.java
+++ 
b/src/plugin/protocol-okhttp/src/java/org/apache/nutch/protocol/okhttp/IPFilterRules.java
@@ -105,15 +105,15 @@ public class IPFilterRules {
       switch (ipRule.toLowerCase(Locale.ROOT)) {
       case "localhost":
       case "loopback":
-        rules.add((InetAddress a) -> a.isLoopbackAddress());
+        rules.add(InetAddress::isLoopbackAddress);
         break;
       case "sitelocal":
-        rules.add((InetAddress a) -> a.isSiteLocalAddress());
+        rules.add(InetAddress::isSiteLocalAddress);
         break;
       default:
         try {
           CIDR cidr = new CIDR(ipRule);
-          rules.add((InetAddress a) -> cidr.contains(a));
+          rules.add(cidr::contains);
         } catch (IllegalArgumentException e) {
           LOG.error(
               "Failed to parse {} as CIDR, ignoring to configure IP rules 
({})",
diff --git 
a/src/plugin/protocol-okhttp/src/test/org/apache/nutch/protocol/okhttp/TestIPAddressFiltering.java
 
b/src/plugin/protocol-okhttp/src/test/org/apache/nutch/protocol/okhttp/TestIPAddressFiltering.java
index 309c977b1..63627bc97 100644
--- 
a/src/plugin/protocol-okhttp/src/test/org/apache/nutch/protocol/okhttp/TestIPAddressFiltering.java
+++ 
b/src/plugin/protocol-okhttp/src/test/org/apache/nutch/protocol/okhttp/TestIPAddressFiltering.java
@@ -28,6 +28,7 @@ import com.google.common.net.InetAddresses;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
 
@@ -63,6 +64,17 @@ public class TestIPAddressFiltering extends 
AbstractHttpProtocolPluginTest {
     assertFalse(c.contains(i), i + " should not be in " + c);
   }
 
+  @Test
+  public void testInvalidCIDR() {
+    assertThrows(IllegalArgumentException.class,
+        () -> new CIDR(InetAddress.getByName("1.2.3.4"), 33));
+    assertThrows(IllegalArgumentException.class, () -> new CIDR("1.2.3.4/33"));
+    assertThrows(IllegalArgumentException.class, () -> new 
CIDR("::ffff:7f00:0/129"));
+    assertThrows(IllegalArgumentException.class, () -> new CIDR("1.2.3.4/-1"));
+    // invalid rule
+    assertThrows(IllegalArgumentException.class, () -> new CIDR("foobar"));
+  }
+
   /** Tests for {@link CIDR} */
   @Test
   public void testCIDRs() {
@@ -89,6 +101,13 @@ public class TestIPAddressFiltering extends 
AbstractHttpProtocolPluginTest {
     testCIDRnotContains("10.0.0.0/8", "9.255.255.255");
     testCIDRnotContains("172.16.0.0/12", "172.32.0.0");
     testCIDRnotContains("172.16.0.0/12", "171.255.255.255");
+
+    testCIDRnotContains("127.0.0.1/31", "0.0.0.1");
+    // NUTCH-3187 CIDR edge cases - /32, /128 - test bit shifting
+    testCIDRnotContains("127.0.0.1", "0.0.0.1");
+    testCIDRnotContains("127.0.0.1/32", "0.0.0.1");
+    testCIDRnotContains("127.0.0.1", "255.0.0.1");
+    testCIDRnotContains("fe80::2f29:b6f0:a4c:32ae/128", 
"::2f29:b6f0:a4c:32ae");
   }
 
   public void testFilter(Configuration conf, String[] included, String[] 
excluded) {

Reply via email to