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

chibenwa pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-jspf.git

commit f588529c67fa1f454f7ef019163675948848529f
Author: Quan Tran <[email protected]>
AuthorDate: Fri Oct 30 16:48:35 2026 +0700

    [FIX] ip6 mechanism: an IPv4-mapped network stays an IPv6 network
    
    IPAddr turns an IPv4-mapped address (::ffff:a.b.c.d) into the IPv4
    address a.b.c.d, as it should for the client address. For an ip6
    network it kept the IPv6 prefix length, now applied to 32 bits:
    ip6:::ffff:192.0.2.0/120 matched the IPv4 192.0.2.0/32 only, and
    ip6:::ffff:0.2.3.4/3 matched 0.0.0.0/3, an eighth of the IPv4 space.
    
    Keep such a network as an IPv6 network with its prefix length: as any
    ip6 network, it does not match an IPv4 client (RFC 7208 5: "IPv4 <ip>
    addresses are only listed in an SPF record using the "ip4" mechanism").
---
 .../java/org/apache/james/jspf/core/IPAddr.java    | 28 ++++++++++++++++++++++
 .../org/apache/james/jspf/terms/IP4Mechanism.java  | 12 +++++++++-
 .../org/apache/james/jspf/terms/IP6Mechanism.java  | 12 ++++++++++
 .../org/apache/james/jspf/SPFExecutorBaseTest.java | 15 ++++++++++++
 4 files changed, 66 insertions(+), 1 deletion(-)

diff --git a/resolver/src/main/java/org/apache/james/jspf/core/IPAddr.java 
b/resolver/src/main/java/org/apache/james/jspf/core/IPAddr.java
index e4d58ee..9f8211f 100644
--- a/resolver/src/main/java/org/apache/james/jspf/core/IPAddr.java
+++ b/resolver/src/main/java/org/apache/james/jspf/core/IPAddr.java
@@ -82,6 +82,34 @@ public class IPAddr {
         return returnAddress;
     }
 
+    /**
+     * Get the address of an ip6 network. Unlike {@link #getAddress(String, 
int)}, an IPv4-mapped
+     * address (::ffff:a.b.c.d) stays an IPv6 address: its prefix length 
counts the 96 bits before
+     * the IPv4 part.
+     *
+     * @param netAddress
+     *            The IPv6 address
+     * @param maskLength
+     *            The netmask
+     * @return the IPv6 address
+     * @throws PermErrorException
+     *             on error
+     */
+    public static IPAddr getIP6Address(String netAddress, int maskLength)
+            throws PermErrorException {
+        IPAddr returnAddress = new IPAddr();
+        returnAddress.stringToInternal(netAddress);
+        if (returnAddress.ipLength == 4) {
+            int[] ip4Address = returnAddress.address;
+            returnAddress.setIP6Defaults();
+            returnAddress.address[5] = 0xffff;
+            returnAddress.address[6] = (ip4Address[0] & 0xff) * 256 + 
(ip4Address[1] & 0xff);
+            returnAddress.address[7] = (ip4Address[2] & 0xff) * 256 + 
(ip4Address[3] & 0xff);
+        }
+        returnAddress.setMask(maskLength);
+        return returnAddress;
+    }
+
     /**
      * Check if a the Object is instance of this class
      * 
diff --git 
a/resolver/src/main/java/org/apache/james/jspf/terms/IP4Mechanism.java 
b/resolver/src/main/java/org/apache/james/jspf/terms/IP4Mechanism.java
index b483255..b5fab5a 100644
--- a/resolver/src/main/java/org/apache/james/jspf/terms/IP4Mechanism.java
+++ b/resolver/src/main/java/org/apache/james/jspf/terms/IP4Mechanism.java
@@ -78,7 +78,17 @@ public class IP4Mechanism extends GenericMechanism {
                 throw new PermErrorException("Invalid CIDR: " + 
maskLengthString);
             }
         }
-        ip = IPAddr.getAddress(ipString, maskLength);
+        ip = createNetwork(ipString, maskLength);
+    }
+
+    /**
+     * @param ipString the address of the network
+     * @param maskLength its prefix length
+     * @return the network
+     * @throws PermErrorException if the address is not valid
+     */
+    protected IPAddr createNetwork(String ipString, int maskLength) throws 
PermErrorException {
+        return IPAddr.getAddress(ipString, maskLength);
     }
 
     /**
diff --git 
a/resolver/src/main/java/org/apache/james/jspf/terms/IP6Mechanism.java 
b/resolver/src/main/java/org/apache/james/jspf/terms/IP6Mechanism.java
index f707f96..0a2717b 100644
--- a/resolver/src/main/java/org/apache/james/jspf/terms/IP6Mechanism.java
+++ b/resolver/src/main/java/org/apache/james/jspf/terms/IP6Mechanism.java
@@ -20,7 +20,9 @@
 
 package org.apache.james.jspf.terms;
 
+import org.apache.james.jspf.core.IPAddr;
 import org.apache.james.jspf.core.Inet6Util;
+import org.apache.james.jspf.core.exceptions.PermErrorException;
 
 /**
  * This class represent the ip6 mechanism
@@ -41,6 +43,16 @@ public class IP6Mechanism extends IP4Mechanism {
         return Inet6Util.isValidIP6Address(ipString);
     }
 
+    /**
+     * An IPv4-mapped network (::ffff:a.b.c.d) stays an IPv6 network, of an 
IPv6 prefix length: as
+     * any ip6 network, it does not match an IPv4 client (RFC 7208 5.6).
+     *
+     * @see org.apache.james.jspf.terms.IP4Mechanism#createNetwork(String, int)
+     */
+    protected IPAddr createNetwork(String ipString, int maskLength) throws 
PermErrorException {
+        return IPAddr.getIP6Address(ipString, maskLength);
+    }
+
     /**
      * @see org.apache.james.jspf.terms.IP4Mechanism#getMaxCidr()
      */
diff --git 
a/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java 
b/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java
index 536025a..7fb0bf1 100644
--- a/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java
+++ b/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java
@@ -471,6 +471,21 @@ public abstract class SPFExecutorBaseTest {
         assertEquals(SPFErrorConstants.PERM_ERROR_CONV, result.getResult());
     }
 
+    @Test(timeout = 10000)
+    public void ipv4MappedIp6NetworkShouldNotMatchAnIp4Client() throws 
IOException {
+        SPFResult result = 
checkRecord("ipv4MappedIp6Network.spfexecutor.fake", "v=spf1 
ip6:::ffff:0.2.3.4/3 -all", "1.2.3.4");
+
+        assertEquals(SPFErrorConstants.FAIL_CONV, result.getResult());
+    }
+
+    @Test(timeout = 10000)
+    public void ipv4MappedIp6NetworkShouldKeepItsIp6PrefixLength() throws 
IOException {
+        // ::ffff:0.2.3.4/3 is ::/3
+        SPFResult result = checkRecord("ipv4MappedIp6Prefix.spfexecutor.fake", 
"v=spf1 ip6:::ffff:0.2.3.4/3 -all", "::1");
+
+        assertEquals(SPFErrorConstants.PASS_CONV, result.getResult());
+    }
+
     @Test(timeout = 10000)
     public void shouldReturnPermErrorOnAIp4CidrOverflowingAnInt() throws 
IOException {
         SPFResult result = checkRecord("aIp4CidrOverflow.spfexecutor.fake", 
"v=spf1 a/4294967296 -all", "1.2.3.4");


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to