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 9576ba6674c104b060e4d3a549614db129d5c03f Author: Quan Tran <[email protected]> AuthorDate: Wed Nov 4 22:28:21 2026 +0700 [FIX] ptr mechanism: no match on a DNS error of its lookups A DNS error of a lookup of the ptr mechanism, as SERVFAIL for the PTR records of the client address, made the whole check a temperror. RFC 7208 5.5 says otherwise: "If a DNS error occurs while doing the PTR RR lookup, then this mechanism fails to match", and "If a DNS error occurs while doing an A RR lookup, then that domain name is skipped and the search continues". Reverse zones answering SERVFAIL are common, so "v=spf1 ptr ip4:192.0.2.10 -all" gave temperror for 192.0.2.10 instead of pass. On a DNS error of the PTR lookup the mechanism does not match; on one of an address lookup, the next name is checked. --- .../org/apache/james/jspf/terms/PTRMechanism.java | 7 ++++- .../org/apache/james/jspf/SPFExecutorBaseTest.java | 35 ++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/resolver/src/main/java/org/apache/james/jspf/terms/PTRMechanism.java b/resolver/src/main/java/org/apache/james/jspf/terms/PTRMechanism.java index 5ac51d3..0a0dfb2 100644 --- a/resolver/src/main/java/org/apache/james/jspf/terms/PTRMechanism.java +++ b/resolver/src/main/java/org/apache/james/jspf/terms/PTRMechanism.java @@ -187,7 +187,12 @@ public class PTRMechanism extends GenericMechanism implements DNSServiceEnabled, } } catch (TimeoutException e) { - throw new TempErrorException("Timeout querying the dns server"); + // RFC 7208 5.5: "If a DNS error occurs while doing the PTR RR lookup, then this mechanism + // fails to match", and of an address lookup, "that domain name is skipped" + if (domainList == null) { + spfSession.setAttribute(Directive.ATTRIBUTE_MECHANISM_RESULT, Boolean.FALSE); + return null; + } } 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 bdec8fb..bcc20c4 100644 --- a/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java +++ b/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java @@ -808,6 +808,41 @@ public abstract class SPFExecutorBaseTest { assertEquals(SPFErrorConstants.PASS_CONV, result.getResult()); } + @Test(timeout = 10000) + public void ptrShouldNotMatchWhenThePtrLookupFails() throws IOException { + String hostname = "ptrservfail.spfexecutor.fake"; + // the names of no record of fakeResolver, as the reverse name of the client, answer SERVFAIL + FakeResolver fakeResolver = new FakeResolver(AnsweringResolver.withRcode(Rcode.SERVFAIL)); + fakeResolver.addRecord(new TXTRecord(Name.fromString(hostname + "."), DClass.IN, 30L, + "v=spf1 ptr ip4:192.0.2.10 -all")); + SPF spf = createCustomSPF(new DNSServiceXBillImpl(fakeResolver)); + + SPFResult result = spf.checkSPF("192.0.2.10", "a_user@" + hostname, hostname); + + // RFC 7208 5.5: "If a DNS error occurs while doing the PTR RR lookup, then this mechanism fails to match" + assertEquals(SPFErrorConstants.PASS_CONV, result.getResult()); + } + + @Test(timeout = 10000) + public void ptrShouldSkipANameWhoseAddressLookupFails() throws IOException { + String hostname = "ptrskip.spfexecutor.fake"; + FakeResolver fakeResolver = new FakeResolver(AnsweringResolver.withRcode(Rcode.SERVFAIL)); + fakeResolver.addRecord(new TXTRecord(Name.fromString(hostname + "."), DClass.IN, 30L, "v=spf1 ptr -all")); + // the client is named by broken.servfail.fake, of no record here, then by mail.<hostname> + fakeResolver.addRecord(new PTRRecord(Name.fromString("10.2.0.192.in-addr.arpa."), DClass.IN, 30L, + Name.fromString("broken.servfail.fake."))); + fakeResolver.addRecord(new PTRRecord(Name.fromString("10.2.0.192.in-addr.arpa."), DClass.IN, 30L, + Name.fromString("mail." + hostname + "."))); + fakeResolver.addRecord(new ARecord(Name.fromString("mail." + hostname + "."), DClass.IN, 30L, + InetAddress.getByName("192.0.2.10"))); + SPF spf = createCustomSPF(new DNSServiceXBillImpl(fakeResolver)); + + SPFResult result = spf.checkSPF("192.0.2.10", "a_user@" + hostname, hostname); + + // RFC 7208 5.5: "If a DNS error occurs while doing an A RR lookup, then that domain name is skipped" + 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]
