"io.netty.handler.codec.CorruptedFrameException: name has an out-of-range pointer" on some TXT lookup with long SPF values
yo000 opened this issue · comments
Hello, We discovered this bug through Graylog DNS Lookups. I isolated problematic code, but my java skills are too weak to precisely locate the problem
Expected behavior
TXT lookup returns values
$ drill -t TXT outlook.com
;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 22722
;; flags: qr rd ra ; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;; outlook.com. IN TXT
;; ANSWER SECTION:
outlook.com. 300 IN TXT "v=spf1 include:spf-a.outlook.com include:spf-b.outlook.com ip4:157.55.9.128/25 include:spf.protection.outlook.com include:spf-a.hotmail.com include:_spf-ssg-b.microsoft.com include:_spf-ssg-c.microsoft.com ~all"
outlook.com. 300 IN TXT "google-site-verification=0iLWhIMhXEkeWwWfFU4ursTn-_OvoOjaA0Lr7Pg1sEM"
outlook.com. 300 IN TXT "google-site-verification=DC2uC-T8kD33lINhNzfo0bNBrw-vrCXs5BPF5BXY56g"
Actual behavior
TXT Lookup return exception "io.netty.handler.codec.CorruptedFrameException: name has an out-of-range pointer" on seemingly valid TXT.
For example on outlook.com :
$ drill -t TXT outlook.com
;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 22722
;; flags: qr rd ra ; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;; outlook.com. IN TXT
;; ANSWER SECTION:
outlook.com. 300 IN TXT "v=spf1 include:spf-a.outlook.com include:spf-b.outlook.com ip4:157.55.9.128/25 include:spf.protection.outlook.com include:spf-a.hotmail.com include:_spf-ssg-b.microsoft.com include:_spf-ssg-c.microsoft.com ~all"
outlook.com. 300 IN TXT "google-site-verification=0iLWhIMhXEkeWwWfFU4ursTn-_OvoOjaA0Lr7Pg1sEM"
outlook.com. 300 IN TXT "google-site-verification=DC2uC-T8kD33lINhNzfo0bNBrw-vrCXs5BPF5BXY56g"
We have this exception
TXT record [0] retrieved with content [DefaultDnsRawRecord(outlook.com. 300 IN TXT 69B)].
The decoded TXT record is [google-site-verification=0iLWhIMhXEkeWwWfFU4ursTn-_OvoOjaA0Lr7Pg1sEM.]
TXT record [1] retrieved with content [DefaultDnsRawRecord(outlook.com. 300 IN TXT 69B)].
The decoded TXT record is [google-site-verification=DC2uC-T8kD33lINhNzfo0bNBrw-vrCXs5BPF5BXY56g.]
TXT record [2] retrieved with content [DefaultDnsRawRecord(outlook.com. 300 IN TXT 211B)].
Exception : io.netty.handler.codec.CorruptedFrameException: name has an out-of-range pointer
Steps to reproduce
NioEventLoopGroup nettyEventLoop = new NioEventLoopGroup(8);
DnsNameResolver resolver = new DnsNameResolverBuilder(nettyEventLoop.next())
.channelType(NioDatagramChannel.class)
.queryTimeoutMillis(2000)
.recursionDesired(true)
.build();
String hostName = new String("outlook.com");
DnsResponse content = null;
try {
content = resolver.query(new DefaultDnsQuestion(hostName, DnsRecordType.TXT)).get(10000, TimeUnit.MILLISECONDS).content();
int count = content.count(DnsSection.ANSWER);
for (int i = 0; i < count; i++) {
final DnsRecord dnsRecord = content.recordAt(DnsSection.ANSWER, i);
System.out.printf("TXT record [%d] retrieved with content [%s].\n", i, dnsRecord);
if (dnsRecord instanceof DefaultDnsRawRecord) {
final DefaultDnsRawRecord txtRecord = (DefaultDnsRawRecord) dnsRecord;
final String decodeTxtRecord = DefaultDnsRecordDecoder.decodeName(txtRecord.content());
System.out.printf("The decoded TXT record is [%s]\n", decodeTxtRecord);
}
}
} catch (Exception e) {
System.out.printf("Exception : %s", e);
} finally {
if (content != null) {
// Must manually release references on content object since the DnsResponse class extends ReferenceCounted
content.release();
}
}
Netty version
4.1.106.Final
JVM version (e.g. java -version
)
Reproduced on OpenJDK 64-Bit Server VM (build 17.0.10+7-1, mixed mode, sharing)@FreeBSD13.2, Java(TM) SE Runtime Environment (build 20.0.1+9-29)@Windows 11
OS version (e.g. uname -a
)
Reproduced on FreeBSD 13.2, Debian 12.4 and Windows 11
Will have a look
@yo000 can you share the decodeTxtRecord
method ?
Sure, it's just a call to DefaultDnsRecordDecoder.decodeName.
I updated the code above to supress decodeTxtRecord method
@yo000 decodeName
is not the right method for decoding TXT records as TXT records don't follow the same format as domain names. I think you want to use something like:
private static List<String> decodeTxt(DnsRecord record) {
if (!(record instanceof DnsRawRecord)) {
return Collections.emptyList();
}
List<String> list = new ArrayList<String>();
ByteBuf data = ((DnsRawRecord) record).content();
int idx = data.readerIndex();
int wIdx = data.writerIndex();
while (idx < wIdx) {
int len = data.getUnsignedByte(idx++);
list.add(data.toString(idx, len, CharsetUtil.UTF_8));
idx += len;
}
return list;
}
From the RFC:
3.3.14. TXT RDATA format
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/ TXT-DATA /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
where:
TXT-DATA One or more <character-string>s.
<character-string> is a single
length octet followed by that number of characters. <character-string>
is treated as binary information, and can be up to 256 characters in
length (including the length octet).