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-jdkim.git

commit 57b86a05a370dec9408974137702f82aba2a3353
Author: Benoit TELLIER <[email protected]>
AuthorDate: Fri Oct 30 08:55:25 2026 +0100

    [FIX] DNSPublicKeyRecordRetriever: query the absolute name, return raw 
strings
    
    - The key was looked up as a relative name, so the resolver search list
      applied: with no record at <s>._domainkey.<d>., a record found under a
      search domain was used to verify the signature. The lookup is now
      absolute (RFC 6376 section 3.6.2.1).
    - The TXT strings were joined from dnsjava's presentation form, where
      quotes, backslashes and non-printable bytes come back escaped. The raw
      character-strings are now joined instead.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../jdkim/impl/DNSPublicKeyRecordRetriever.java    | 10 +++-
 .../james/jdkim/DNSPublicKeyRetrieverTest.java     | 59 ++++++++++++++++++++++
 2 files changed, 67 insertions(+), 2 deletions(-)

diff --git 
a/main/src/main/java/org/apache/james/jdkim/impl/DNSPublicKeyRecordRetriever.java
 
b/main/src/main/java/org/apache/james/jdkim/impl/DNSPublicKeyRecordRetriever.java
index e396806..f313aad 100644
--- 
a/main/src/main/java/org/apache/james/jdkim/impl/DNSPublicKeyRecordRetriever.java
+++ 
b/main/src/main/java/org/apache/james/jdkim/impl/DNSPublicKeyRecordRetriever.java
@@ -19,6 +19,7 @@
 
 package org.apache.james.jdkim.impl;
 
+import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -28,6 +29,7 @@ import org.apache.james.jdkim.api.PublicKeyRecordRetriever;
 import org.apache.james.jdkim.exceptions.PermFailException;
 import org.apache.james.jdkim.exceptions.TempFailException;
 import org.xbill.DNS.Lookup;
+import org.xbill.DNS.Name;
 import org.xbill.DNS.Record;
 import org.xbill.DNS.Resolver;
 import org.xbill.DNS.TXTRecord;
@@ -58,7 +60,8 @@ public class DNSPublicKeyRecordRetriever implements 
PublicKeyRecordRetriever {
                     + methodAndOptions + " options unsupported.");
         Lookup query;
         try {
-            query = new Lookup(selector + "._domainkey." + token, Type.TXT);
+            // RFC 6376 section 3.6.2.1: only the absolute name, never a 
search-list expansion
+            query = new Lookup(Name.fromString(selector + "._domainkey." + 
token, Name.root), Type.TXT);
         } catch (TextParseException e) {
             throw new PermFailException("Invalid dns record", e);
         }
@@ -82,7 +85,10 @@ public class DNSPublicKeyRecordRetriever implements 
PublicKeyRecordRetriever {
 
         return Arrays.stream(rr)
                 .filter(r -> r.getType() == Type.TXT)
-                .map(r -> String.join("", ((TXTRecord) r).getStrings()))
+                // getStrings() returns the escaped presentation form: join 
the raw character-strings
+                .map(r -> ((TXTRecord) r).getStringsAsByteArrays().stream()
+                        .map(bytes -> new String(bytes, 
StandardCharsets.ISO_8859_1))
+                        .collect(Collectors.joining()))
                 .collect(Collectors.toList());
     }
 }
diff --git 
a/main/src/test/java/org/apache/james/jdkim/DNSPublicKeyRetrieverTest.java 
b/main/src/test/java/org/apache/james/jdkim/DNSPublicKeyRetrieverTest.java
index 34920b7..7d21fa7 100644
--- a/main/src/test/java/org/apache/james/jdkim/DNSPublicKeyRetrieverTest.java
+++ b/main/src/test/java/org/apache/james/jdkim/DNSPublicKeyRetrieverTest.java
@@ -19,6 +19,16 @@
 
 package org.apache.james.jdkim;
 
+import static org.assertj.core.api.Assertions.assertThat;
+import java.util.List;
+import java.util.Arrays;
+import org.xbill.DNS.Name;
+import org.xbill.DNS.Lookup;
+import org.xbill.DNS.Rcode;
+import org.xbill.DNS.DClass;
+import org.xbill.DNS.TXTRecord;
+import org.xbill.DNS.Section;
+import org.xbill.DNS.Flags;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import org.xbill.DNS.SimpleResolver;
 import org.xbill.DNS.Resolver;
@@ -134,4 +144,53 @@ public class DNSPublicKeyRetrieverTest {
         assertThatThrownBy(() -> new 
DNSPublicKeyRecordRetriever(resolver).getRecords("dns/txt", "sel", 
"example.com"))
                 .isInstanceOf(TempFailException.class);
     }
+
+    @Test
+    public void txtStringsShouldBeReturnedRaw() throws Exception {
+        TXTRecord[] answer = new TXTRecord[1];
+        Resolver resolver = new SimpleResolver("127.0.0.1") {
+            @Override
+            public Message send(Message query) {
+                Message response = new Message(query.getHeader().getID());
+                response.getHeader().setFlag(Flags.QR);
+                response.addRecord(query.getQuestion(), Section.QUESTION);
+                answer[0] = new TXTRecord(query.getQuestion().getName(), 
DClass.IN, 3600,
+                        Arrays.asList("v=DKIM1; n=\\\"quoted\\\" \\\\ note 
\\147;", " p=AAAA"));
+                response.addRecord(answer[0], Section.ANSWER);
+                return response;
+            }
+        };
+
+        Lookup.getDefaultCache(DClass.IN).clearCache();
+
+        assertThat(new 
DNSPublicKeyRecordRetriever(resolver).getRecords("dns/txt", "raw", 
"example.com"))
+                .containsExactly("v=DKIM1; n=\"quoted\" \\ note \u0093; 
p=AAAA");
+    }
+
+    @Test
+    public void keyRecordsShouldOnlyBeLookedUpAtTheAbsoluteName() throws 
Exception {
+        Resolver resolver = new SimpleResolver("127.0.0.1") {
+            @Override
+            public Message send(Message query) {
+                Message response = new Message(query.getHeader().getID());
+                response.getHeader().setFlag(Flags.QR);
+                response.addRecord(query.getQuestion(), Section.QUESTION);
+                Name name = query.getQuestion().getName();
+                if 
(name.toString().equals("search._domainkey.example.com.search.invalid.")) {
+                    response.addRecord(new TXTRecord(name, DClass.IN, 3600, 
"v=DKIM1; p=AAAA"), Section.ANSWER);
+                } else {
+                    response.getHeader().setRcode(Rcode.NXDOMAIN);
+                }
+                return response;
+            }
+        };
+        Lookup.getDefaultCache(DClass.IN).clearCache();
+        List<Name> savedSearchPath = Lookup.getDefaultSearchPath();
+        Lookup.setDefaultSearchPath(Name.fromString("search.invalid."));
+        try {
+            assertThat(new 
DNSPublicKeyRecordRetriever(resolver).getRecords("dns/txt", "search", 
"example.com")).isEmpty();
+        } finally {
+            Lookup.setDefaultSearchPath(savedSearchPath);
+        }
+    }
 }


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

Reply via email to