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 920ec4d676b061f55d39f4dd923fb0e07dfa978d Author: Quan Tran <[email protected]> AuthorDate: Fri Oct 30 16:55:04 2026 +0700 [FIX] DNSServiceXBillImpl: convert the answer records of the type asked for convertRecordsToList cast each record on its getType() only. dnsjava parses a record without data as an EmptyRecord that keeps the type of the record it stands for, so such an answer (here a TXT of RDLENGTH 0 in an UPDATE message) threw ClassCastException: the check ended as neutral. The answer could also hold records of another type, returned as answers (a TXT string for an AAAA query, through getRecordsAsync). Keep the records of the type asked for, and treat a record of none of the classes converted as a record of an unknown type: no data. --- .../apache/james/jspf/impl/DNSServiceXBillImpl.java | 21 +++++++++++++++++++-- .../apache/james/jspf/DNSServiceXBillImplTest.java | 14 ++++++++++++++ 2 files changed, 33 insertions(+), 2 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 fbb8eb9..ed36c7f 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 @@ -22,6 +22,7 @@ package org.apache.james.jspf.impl; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -54,6 +55,16 @@ import org.xbill.DNS.lookup.LookupSession; public class DNSServiceXBillImpl implements DNSService { private static final Logger LOGGER = LoggerFactory.getLogger(DNSServiceXBillImpl.class); + /** + * @return the records of the given type: an answer can hold others + */ + private static Record[] ofType(Record[] rr, int type) { + if (rr == null) { + return null; + } + return Arrays.stream(rr).filter(record -> record.getType() == type).toArray(Record[]::new); + } + // The record limit for lookups protected int recordLimit; @@ -161,7 +172,7 @@ public class DNSServiceXBillImpl implements DNSService { throw new TimeoutException(query.getErrorString()); } - List<String> records = convertRecordsToList(rr); + List<String> records = convertRecordsToList(ofType(rr, dnsJavaType)); LOGGER.debug("Found {} {}-Records", (rr != null ? rr.length : 0), recordTypeDescription); return records; @@ -191,7 +202,7 @@ public class DNSServiceXBillImpl implements DNSService { try { return lookupSession.lookupAsync(Name.fromString(request.getHostname()), dnsJavaType) - .thenApply(result -> convertRecordsToList(result.getRecords().toArray(new Record[0]))); + .thenApply(result -> convertRecordsToList(ofType(result.getRecords().toArray(new Record[0]), dnsJavaType))); } catch (TextParseException e) { // no record, as getRecords answers LOGGER.debug("No {} Record found for host: {}", recordTypeDescription, request.getHostname()); @@ -211,6 +222,12 @@ public class DNSServiceXBillImpl implements DNSService { if (rr != null && rr.length > 0) { records = new ArrayList<String>(); for (int i = 0; i < rr.length; i++) { + // a record without data (dnsjava's EmptyRecord) has the type of the record it stands for, + // not its class: as a record of another type, it makes no data + if (!(rr[i] instanceof ARecord || rr[i] instanceof AAAARecord || rr[i] instanceof MXRecord + || rr[i] instanceof PTRRecord || rr[i] instanceof TXTRecord || rr[i] instanceof SPFRecord)) { + return null; + } switch (rr[i].getType()) { case Type.A: ARecord a = (ARecord) rr[i]; 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 e51969a..22c556a 100644 --- a/resolver/src/test/java/org/apache/james/jspf/DNSServiceXBillImplTest.java +++ b/resolver/src/test/java/org/apache/james/jspf/DNSServiceXBillImplTest.java @@ -23,18 +23,22 @@ package org.apache.james.jspf; import org.apache.james.jspf.impl.DNSServiceXBillImpl; import org.junit.Test; import org.xbill.DNS.DClass; +import org.xbill.DNS.Message; import org.xbill.DNS.Name; import org.xbill.DNS.Record; import org.xbill.DNS.SPFRecord; +import org.xbill.DNS.Section; import org.xbill.DNS.TXTRecord; import org.xbill.DNS.Type; import java.net.UnknownHostException; +import java.util.Base64; import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; public class DNSServiceXBillImplTest { @Test @@ -73,4 +77,14 @@ public class DNSServiceXBillImplTest { assertEquals("quoted string", records.get(0)); } + @Test + public void convertRecordsToListShouldIgnoreAnswersWithoutData() throws Exception { + // an UPDATE message holding a TXT answer of RDLENGTH 0: dnsjava parses it as an EmptyRecord of type TXT + byte[] message = Base64.getDecoder().decode("AAEpgAABAAEAAAAAB2V4YW1wbGUDY29tAIGDAAHADAAQAAEAAA4QAABs"); + Record[] answer = new Message(message).getSection(Section.ANSWER).toArray(new Record[0]); + + assertEquals(Type.TXT, answer[0].getType()); + assertNull(DNSServiceXBillImpl.convertRecordsToList(answer)); + } + } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
