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]
