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 62f174129545452d60ed88735c83a2e22645d2db
Author: Quan Tran <[email protected]>
AuthorDate: Thu Nov 5 10:13:22 2026 +0700

    [FIX] DNSServiceXBillImpl.getRecords: temperror for an extended rcode
    
    An answer whose rcode has extended bits, in its OPT record (RFC 6891
    6.1.3), as BADVERS (16), is an error, but dnsjava's Lookup reads only
    the 4 bits of the header: getRecords took such an answer for NOERROR,
    so no record, or the records it holds. The synchronous executor, the
    default one, and the staged one then gave none or the result of those
    records, where the asynchronous executor gives temperror (RFC 7208
    4.4: "some other error (RCODE other than 0 or 3)").
    
    Check the answers of getRecords as those of getRecordsAsync: an rcode
    other than NOERROR and NXDOMAIN, extended bits included, is a DNS
    error, a TimeoutException.
---
 .../james/jspf/impl/DNSServiceXBillImpl.java       | 34 ++++++++++++++++------
 .../apache/james/jspf/DNSServiceXBillImplTest.java | 14 +++++++++
 .../org/apache/james/jspf/SPFExecutorBaseTest.java | 11 +++++++
 .../james/jspf/helpers/AnsweringResolver.java      |  8 +++--
 4 files changed, 56 insertions(+), 11 deletions(-)

diff --git 
a/resolver/src/main/java/org/apache/james/jspf/impl/DNSServiceXBillImpl.java 
b/resolver/src/main/java/org/apache/james/jspf/impl/DNSServiceXBillImpl.java
index 76d2c20..2090467 100644
--- a/resolver/src/main/java/org/apache/james/jspf/impl/DNSServiceXBillImpl.java
+++ b/resolver/src/main/java/org/apache/james/jspf/impl/DNSServiceXBillImpl.java
@@ -80,7 +80,8 @@ public class DNSServiceXBillImpl implements DNSService {
      * an error rcode, as YXRRSET), or of another class.
      * It also fails, as Lookup does, an answer with an rcode other than 
NOERROR and NXDOMAIN (with its
      * extended bits, RFC 6891 6.1.3), or for another question: LookupSession 
would read an NXRRSET
-     * answer as no record.
+     * answer as no record. Its {@link #send(Message)}, of the synchronous 
Lookup, is not bounded but
+     * checks the answers the same way: Lookup reads only the 4 bits of the 
rcode in the header.
      */
     private static final class BoundedResolver implements Resolver {
         /**
@@ -94,17 +95,26 @@ public class DNSServiceXBillImpl implements DNSService {
         }
 
         /**
-         * @return the answer, or a failed lookup when it is a DNS error (RFC 
7208 4.4)
+         * @return why the answer is a DNS error (RFC 7208 4.4), or null when 
it is none
          */
-        private static CompletionStage<Message> checked(Message query, Message 
answer) {
+        private static String errorOf(Message query, Message answer) {
             int rcode = answer.getRcode();
             if (rcode != Rcode.NOERROR && rcode != Rcode.NXDOMAIN) {
-                return CompletableFuture.failedFuture(new 
LookupFailedException(
-                    "Answer " + Rcode.string(rcode) + " for " + 
query.getQuestion()));
+                return "Answer " + Rcode.string(rcode) + " for " + 
query.getQuestion();
             }
             if (!query.getQuestion().equals(answer.getQuestion())) {
-                return CompletableFuture.failedFuture(new 
LookupFailedException(
-                    "Answer for another question than " + 
query.getQuestion()));
+                return "Answer for another question than " + 
query.getQuestion();
+            }
+            return null;
+        }
+
+        /**
+         * @return the answer, or a failed lookup when it is a DNS error (RFC 
7208 4.4)
+         */
+        private static CompletionStage<Message> checked(Message query, Message 
answer) {
+            String error = errorOf(query, answer);
+            if (error != null) {
+                return CompletableFuture.failedFuture(new 
LookupFailedException(error));
             }
             return CompletableFuture.completedFuture(answer);
         }
@@ -167,9 +177,15 @@ public class DNSServiceXBillImpl implements DNSService {
 
         /**
          * @see org.xbill.DNS.Resolver#send(Message)
+         * @throws IOException also when the answer is a DNS error: Lookup 
then reports TRY_AGAIN
          */
         public Message send(Message query) throws IOException {
-            return resolver.send(query);
+            Message answer = resolver.send(query);
+            String error = errorOf(query, answer);
+            if (error != null) {
+                throw new IOException(error);
+            }
+            return answer;
         }
 
         /**
@@ -351,7 +367,7 @@ public class DNSServiceXBillImpl implements DNSService {
             // RFC 7208 4.3: a fully qualified name, not looked up under the 
search path of the host
             Name name = Name.fromString(request.getHostname(), Name.root);
             Lookup query = new Lookup(name, dnsJavaType);
-            query.setResolver(resolver);
+            query.setResolver(new BoundedResolver(resolver));
 
             Record[] rr = run(query, name, dnsJavaType);
             int queryResult = query.getResult();
diff --git 
a/resolver/src/test/java/org/apache/james/jspf/DNSServiceXBillImplTest.java 
b/resolver/src/test/java/org/apache/james/jspf/DNSServiceXBillImplTest.java
index 0bf0980..0f0e1ab 100644
--- a/resolver/src/test/java/org/apache/james/jspf/DNSServiceXBillImplTest.java
+++ b/resolver/src/test/java/org/apache/james/jspf/DNSServiceXBillImplTest.java
@@ -36,6 +36,7 @@ import org.xbill.DNS.Flags;
 import org.xbill.DNS.Lookup;
 import org.xbill.DNS.Message;
 import org.xbill.DNS.Name;
+import org.xbill.DNS.Rcode;
 import org.xbill.DNS.Record;
 import org.xbill.DNS.SPFRecord;
 import org.xbill.DNS.Section;
@@ -128,6 +129,19 @@ public class DNSServiceXBillImplTest {
         }
     }
 
+    @Test(timeout = 10000)
+    public void getRecordsShouldReportADnsErrorForAnExtendedErrorRcode() 
throws Exception {
+        // BADVERS, of which dnsjava's Lookup reads only the header bits: 
NOERROR
+        DNSServiceXBillImpl dns = new 
DNSServiceXBillImpl(AnsweringResolver.withRcode(Rcode.BADVERS));
+
+        try {
+            dns.getRecords(new DNSRequest("badvers.example.com", 
DNSRequest.TXT));
+            fail("the lookup should fail");
+        } catch (TimeoutException e) {
+            // an error rcode: a DNS error (RFC 7208 4.4)
+        }
+    }
+
     @Test(timeout = 10000)
     public void 
getRecordsAsyncShouldReportADnsErrorForAnAnswerDnsjavaCannotProcess() throws 
Exception {
         // opcode UPDATE, and the answer "example.com CNAME" of RDLENGTH 0: 
dnsjava's LookupSession fails with
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 45626d4..0da1c37 100644
--- a/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java
+++ b/resolver/src/test/java/org/apache/james/jspf/SPFExecutorBaseTest.java
@@ -741,6 +741,17 @@ public abstract class SPFExecutorBaseTest {
         assertEquals(SPFErrorConstants.TEMP_ERROR_CONV, result.getResult());
     }
 
+    @Test(timeout = 10000)
+    public void shouldReturnTempErrorWhenTheLookupGetsAnExtendedErrorRcode() {
+        // BADVERS (16): 0 in the header, the upper bits in the OPT record 
(RFC 6891 6.1.3)
+        SPF spf = createCustomSPF(new 
DNSServiceXBillImpl(AnsweringResolver.withRcode(Rcode.BADVERS)));
+
+        SPFResult result = spf.checkSPF("192.0.2.10", 
"[email protected]", "badvers.spfexecutor.fake");
+
+        // RFC 7208 4.4: "some other error (RCODE other than 0 or 3)"
+        assertEquals(SPFErrorConstants.TEMP_ERROR_CONV, result.getResult());
+    }
+
     @Test(timeout = 10000)
     public void shouldReturnTempErrorWhenTheLookupGetsANetworkError() {
         SPF spf = createCustomSPF(new 
DNSServiceXBillImpl(AnsweringResolver.unreachable()));
diff --git 
a/resolver/src/test/java/org/apache/james/jspf/helpers/AnsweringResolver.java 
b/resolver/src/test/java/org/apache/james/jspf/helpers/AnsweringResolver.java
index 7d05fca..874a339 100644
--- 
a/resolver/src/test/java/org/apache/james/jspf/helpers/AnsweringResolver.java
+++ 
b/resolver/src/test/java/org/apache/james/jspf/helpers/AnsweringResolver.java
@@ -30,6 +30,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 import org.xbill.DNS.EDNSOption;
 import org.xbill.DNS.Flags;
 import org.xbill.DNS.Message;
+import org.xbill.DNS.OPTRecord;
 import org.xbill.DNS.Resolver;
 import org.xbill.DNS.Section;
 import org.xbill.DNS.TSIG;
@@ -40,13 +41,16 @@ import org.xbill.DNS.TSIG;
  */
 public final class AnsweringResolver implements Resolver {
     /**
-     * @param rcode an rcode
+     * @param rcode an rcode, of the header, or with extended bits, of an OPT 
record (RFC 6891 6.1.3)
      * @return a resolver answering every query with that rcode and no record
      */
     public static AnsweringResolver withRcode(int rcode) {
         Message answer = new Message();
         answer.getHeader().setFlag(Flags.QR);
-        answer.getHeader().setRcode(rcode);
+        answer.getHeader().setRcode(rcode & 0xF);
+        if (rcode > 0xF) {
+            answer.addRecord(new OPTRecord(1232, rcode >> 4, 0), 
Section.ADDITIONAL);
+        }
         return new AnsweringResolver(answer.toWire());
     }
 


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

Reply via email to