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]
