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

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

commit 60410f3d8f25afbaf35a7d903726438dcf0c82d1
Author: Benoit TELLIER <[email protected]>
AuthorDate: Wed Oct 28 22:11:22 2026 +0100

    [FIX] Look up the address of no more than 10 MX names
    
    RFC 7208 4.6.4: there must be a limit of no more than 10 address
    lookups per mx mechanism. One "mx" term with a large MX answer made a
    single SPF check send hundreds of A/AAAA queries.
    
    Truncate the MX list to the DNS service record limit (10 by default),
    as PTRMechanism does: this keeps the #mx-limit rfc4408 test result.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../org/apache/james/jspf/terms/MXMechanism.java   | 19 +++++++-
 .../org/apache/james/jspf/SPFExecutorBaseTest.java | 54 ++++++++++++++++++++++
 2 files changed, 72 insertions(+), 1 deletion(-)

diff --git 
a/resolver/src/main/java/org/apache/james/jspf/terms/MXMechanism.java 
b/resolver/src/main/java/org/apache/james/jspf/terms/MXMechanism.java
index 1e54ad9..20cfe34 100644
--- a/resolver/src/main/java/org/apache/james/jspf/terms/MXMechanism.java
+++ b/resolver/src/main/java/org/apache/james/jspf/terms/MXMechanism.java
@@ -26,6 +26,8 @@ import java.util.List;
 import org.apache.james.jspf.core.DNSLookupContinuation;
 import org.apache.james.jspf.core.DNSRequest;
 import org.apache.james.jspf.core.DNSResponse;
+import org.apache.james.jspf.core.DNSService;
+import org.apache.james.jspf.core.DNSServiceEnabled;
 import org.apache.james.jspf.core.IPAddr;
 import org.apache.james.jspf.core.MacroExpand;
 import org.apache.james.jspf.core.SPFChecker;
@@ -44,7 +46,7 @@ import org.slf4j.LoggerFactory;
  * This class represent the mx mechanism
  * 
  */
-public class MXMechanism extends AMechanism implements 
SPFCheckerDNSResponseListener {
+public class MXMechanism extends AMechanism implements DNSServiceEnabled, 
SPFCheckerDNSResponseListener {
     private static final Logger LOGGER = 
LoggerFactory.getLogger(MXMechanism.class);
 
     private final class ExpandedChecker implements SPFChecker {
@@ -72,6 +74,15 @@ public class MXMechanism extends AMechanism implements 
SPFCheckerDNSResponseList
             + DUAL_CIDR_LENGTH_REGEX + ")?";
     
     private SPFChecker expandedChecker = new ExpandedChecker();
+
+    private DNSService dnsService;
+
+    /**
+     * @see 
org.apache.james.jspf.core.DNSServiceEnabled#enableDNSService(org.apache.james.jspf.core.DNSService)
+     */
+    public void enableDNSService(DNSService service) {
+        this.dnsService = service;
+    }
     
     /**
      * @see 
org.apache.james.jspf.terms.AMechanism#checkSPF(org.apache.james.jspf.core.SPFSession)
@@ -106,6 +117,12 @@ public class MXMechanism extends AMechanism implements 
SPFCheckerDNSResponseList
                     
spfSession.setAttribute(Directive.ATTRIBUTE_MECHANISM_RESULT, Boolean.FALSE);
                     return null;
                 }
+
+                // RFC 7208 4.6.4: no more than 10 MX names get an address 
lookup.
+                // Truncate rather than permerror, see #mx-limit rfc4408 test
+                if (dnsService != null && dnsService.getRecordLimit() > 0 && 
records.size() > dnsService.getRecordLimit()) {
+                    records = new ArrayList<String>(records.subList(0, 
dnsService.getRecordLimit()));
+                }
                 
                 spfSession.setAttribute(ATTRIBUTE_CHECK_RECORDS, records);
                 
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 8042c22..4369ee3 100644
--- a/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java
+++ b/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java
@@ -1,5 +1,6 @@
 package org.apache.james.jspf;
 
+import org.apache.james.jspf.core.DNSRequest;
 import org.apache.james.jspf.core.DNSService;
 import org.apache.james.jspf.core.exceptions.SPFErrorConstants;
 import org.apache.james.jspf.executor.SPFResult;
@@ -20,7 +21,12 @@ import org.xbill.DNS.TextParseException;
 import java.io.IOException;
 import java.net.UnknownHostException;
 import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
 import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
@@ -280,4 +286,52 @@ public abstract class SPFExecutorBaseTest {
                         ip, hostname, SPFErrorConstants.FAIL_CONV),
                 result.getHeader());
     }
+
+    /**
+     * Test the limit described in RFC7208 section "4.6.4.  DNS Lookup Limits"
+     */
+    @Test
+    public void shouldLookUpNoMoreThan10MxNames() {
+        AtomicInteger addressLookups = new AtomicInteger();
+        DNSService dns = new DNSService() {
+            public List<String> getRecords(DNSRequest request) {
+                switch (request.getRecordType()) {
+                    case DNSRequest.TXT:
+                        return request.getHostname().equals("example.com")
+                            ? new ArrayList<>(List.of("v=spf1 mx -all")) : 
null;
+                    case DNSRequest.MX:
+                        return IntStream.range(0, 400).mapToObj(i -> "m" + i + 
".example.net")
+                            .collect(Collectors.toCollection(ArrayList::new));
+                    case DNSRequest.A:
+                        addressLookups.incrementAndGet();
+                        return null;
+                    default:
+                        return null;
+                }
+            }
+
+            public CompletionStage<List<String>> getRecordsAsync(DNSRequest 
request) {
+                return CompletableFuture.completedFuture(getRecords(request));
+            }
+
+            public List<String> getLocalDomainNames() {
+                return List.of("localhost");
+            }
+
+            public void setTimeOut(int timeOut) {
+            }
+
+            public int getRecordLimit() {
+                return 10;
+            }
+
+            public void setRecordLimit(int recordLimit) {
+            }
+        };
+
+        SPFResult result = createCustomSPF(dns).checkSPF("1.2.3.4", 
"[email protected]", "example.com");
+
+        assertEquals(SPFErrorConstants.FAIL_CONV, result.getResult());
+        assertEquals(10, addressLookups.get());
+    }
 }


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

Reply via email to