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 9fb0612f9277f13a4b9441a1bffc95aadffcb6c1
Author: Quan Tran <[email protected]>
AuthorDate: Wed Nov 4 22:31:12 2026 +0700

    [FIX] SPFRetriever: look up TXT when the SPF type lookup fails
    
    SPFRetriever looks up the record of a domain as the obsolete SPF type
    first, then as TXT when there is none. A DNS error of that first
    lookup, as from a server that does not handle the SPF type, made the
    check a temperror, without the TXT record being looked up. Since RFC
    7208, the record is published as TXT only, and looked up as "type TXT
    only" (RFC 7208 3.1, 4.4): "v=spf1 -all" published as TXT failed no
    client of such a server.
    
    On a DNS error of the SPF type lookup, look up the TXT record, as when
    there is no SPF type record. The same with setSPFMustEqualsTXT, which
    compares the records of both types: there too a DNS error of the SPF
    type lookup gave temperror.
---
 .../apache/james/jspf/policies/SPFRetriever.java   |  4 +-
 .../jspf/policies/SPFStrictCheckerRetriever.java   |  5 ++-
 .../org/apache/james/jspf/SPFExecutorBaseTest.java | 44 ++++++++++++++++++++++
 3 files changed, 51 insertions(+), 2 deletions(-)

diff --git 
a/resolver/src/main/java/org/apache/james/jspf/policies/SPFRetriever.java 
b/resolver/src/main/java/org/apache/james/jspf/policies/SPFRetriever.java
index 84a130a..0c6629e 100644
--- a/resolver/src/main/java/org/apache/james/jspf/policies/SPFRetriever.java
+++ b/resolver/src/main/java/org/apache/james/jspf/policies/SPFRetriever.java
@@ -98,7 +98,9 @@ public class SPFRetriever implements SPFChecker {
                 return null;
                 
             } catch (TimeoutException e) {
-                throw new TempErrorException("Timeout querying dns");
+                // RFC 7208 4.4: the record is looked up as "type TXT only": a 
DNS error of the obsolete SPF type is not one
+                String currentDomain = session.getCurrentDomain();
+                return new DNSLookupContinuation(new DNSRequest(currentDomain, 
DNSRequest.TXT), new SPFRecordHandlerDNSResponseListener());
             }
         }
         
diff --git 
a/resolver/src/main/java/org/apache/james/jspf/policies/SPFStrictCheckerRetriever.java
 
b/resolver/src/main/java/org/apache/james/jspf/policies/SPFStrictCheckerRetriever.java
index b077911..24234b7 100644
--- 
a/resolver/src/main/java/org/apache/james/jspf/policies/SPFStrictCheckerRetriever.java
+++ 
b/resolver/src/main/java/org/apache/james/jspf/policies/SPFStrictCheckerRetriever.java
@@ -93,7 +93,10 @@ public class SPFStrictCheckerRetriever extends SPFRetriever {
                 return new DNSLookupContinuation(new DNSRequest(currentDomain, 
DNSRequest.TXT), new SPFStrictSPFRecordsDNSResponseListener());
                     
             } catch (TimeoutException e) {
-                throw new TempErrorException("Timeout querying dns");
+                // RFC 7208 4.4: the record is looked up as "type TXT only": a 
DNS error of the obsolete SPF type is not one
+                session.removeAttribute(ATTRIBUTE_SPFSTRICT_CHECK_SPFRECORDS);
+                String currentDomain = session.getCurrentDomain();
+                return new DNSLookupContinuation(new DNSRequest(currentDomain, 
DNSRequest.TXT), new SPFStrictSPFRecordsDNSResponseListener());
             }
         }
         
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 bcc20c4..bee20fc 100644
--- a/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java
+++ b/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java
@@ -82,6 +82,29 @@ public abstract class SPFExecutorBaseTest {
         return fakeResolver;
     }
 
+    // hostname publishes "v=spf1 -all" as TXT, on a server failing the 
lookups of the obsolete SPF type
+    private static DNSService dnsFailingTheSpfType(String hostname) throws 
IOException {
+        FakeResolver fakeResolver = new FakeResolver();
+        fakeResolver.addRecord(new TXTRecord(Name.fromString(hostname + "."), 
DClass.IN, 30L, "v=spf1 -all"));
+        return new DNSServiceXBillImpl(fakeResolver) {
+            @Override
+            public List<String> getRecords(DNSRequest request) throws 
TimeoutException {
+                if (request.getRecordType() == DNSRequest.SPF) {
+                    throw new TimeoutException("SERVFAIL");
+                }
+                return super.getRecords(request);
+            }
+
+            @Override
+            public CompletionStage<List<String>> getRecordsAsync(DNSRequest 
request) {
+                if (request.getRecordType() == DNSRequest.SPF) {
+                    return CompletableFuture.failedFuture(new 
TimeoutException("SERVFAIL"));
+                }
+                return super.getRecordsAsync(request);
+            }
+        };
+    }
+
     protected abstract SPF createSPF();
 
     protected abstract SPF createCustomSPF(DNSService dnsService);
@@ -843,6 +866,27 @@ public abstract class SPFExecutorBaseTest {
         assertEquals(SPFErrorConstants.PASS_CONV, result.getResult());
     }
 
+    @Test(timeout = 10000)
+    public void shouldCheckTheTxtRecordWhenTheLookupOfTheSpfTypeFails() throws 
IOException {
+        String hostname = "spftypeservfail.spfexecutor.fake";
+
+        SPFResult result = 
createCustomSPF(dnsFailingTheSpfType(hostname)).checkSPF("192.0.2.10", 
"a_user@" + hostname, hostname);
+
+        // RFC 7208 4.4: the record is looked up as "type TXT only"
+        assertEquals(SPFErrorConstants.FAIL_CONV, result.getResult());
+    }
+
+    @Test(timeout = 10000)
+    public void 
shouldCheckTheTxtRecordWhenTheLookupOfTheSpfTypeFailsInStrictMode() throws 
IOException {
+        String hostname = "spftypeservfailstrict.spfexecutor.fake";
+        SPF spf = createCustomSPF(dnsFailingTheSpfType(hostname));
+        spf.setSPFMustEqualsTXT(true);
+
+        SPFResult result = spf.checkSPF("192.0.2.10", "a_user@" + hostname, 
hostname);
+
+        assertEquals(SPFErrorConstants.FAIL_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