Pointer-to-pointer name compression when encoding RRsets with multiple RRs
Hi,
It looks like knot_pkt_pkt()
encodes the non-initial RR owner names in an RRset as pointers to the first RR owner name in the RRset. In some cases the initial RR owner name in an RRset will itself be a pointer, e.g. to a part of the QNAME. I believe this behavior is RFC compliant, but I was able to find a piece of software that did not like parsing the pointer-to-pointer representation of an owner name.
Here is a libknot test program that generates a packet and prints its hex encoding to stdout:
I then decoded the packet that libknot generated by hand and got the following:
Packet bytes
------------
+ + +
00008000000100010002000003777777076578616d706c6503636f6d0000010001c00c0001000100001c200004c20c0001c0100002000100001c200006036e7331c010c0310002000100001c200006036e7332c010
(Pointer targets marked with "+")
DNS header (12 bytes)
---------------------
0000 ID
8000 Flags/OPCODE/RCODE
0001 QDCOUNT
0001 ANCOUNT
0002 NSCOUNT
0000 ARCOUNT
Question section (21 bytes)
---------------------------
03777777076578616d706c6503636f6d00 QNAME: "www.example.com"
0001 QTYPE A
0001 QCLASS IN
Answer section (16 bytes)
-------------------------
c00c Pointer to byte 12 ("www.example.com")
0001 TYPE A
0001 CLASS IN
0000 1c20 TTL 7200
0004 RDLENGTH 4
c2 0c 00 01 194.12.0.1
Authority section (36 bytes)
----------------------------
c010 Pointer to byte 16 ("example.com")
0002 TYPE NS
0001 CLASS IN
0000 1c20 TTL 7200
0006 RDLENGTH 6
036e7331 c010 "ns1" + Pointer to byte 16 ("example.com") -> "ns1.example.com"
c031 Pointer to byte 49 -> Pointer to byte 16 -> "example.com"
0002 TYPE NS
0001 CLASS IN
0000 1c20 TTL 7200
0006 RDLENGTH 6
036e7332 c010 "ns2" + Pointer to byte 16 ("example.com") -> "ns2.example.com"
In the last RR in the authority section, which is the second RR in the NS RRset, the owner name is represented as a pointer to byte 49, which is a pointer to byte 16, where the name "example.com" is located.
The Rust DNS parsing library https://github.com/jedisct1/dnssector does not like this representation and produces an error.
Here is a Cargo.toml
:
[package]
name = "dns-message-decode"
version = "0.1.0"
edition = "2018"
[dependencies]
dnssector = "0.2.9"
hex = "0.4.3"
and a src/main.rs
:
fn main() -> Result<(), Box<dyn std::error::Error>> {
let arg = std::env::args()
.nth(1)
.expect("Need a hex-encoded DNS message to decode");
let raw_msg_bytes = hex::decode(arg)?;
let msg = dnssector::DNSSector::new(raw_msg_bytes)?.parse()?;
dbg!(msg);
Ok(())
}
When run with the hex-encoded output of the packet produced by libknot above, it generates the following output:
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/dns-message-decode 00008000000100010002000003777777076578616d706c6503636f6d0000010001c00c0001000100001c200004c20c0001c0100002000100001c200006036e7331c010c0310002000100001c200006036e7332c010`
Error: Invalid name in a DNS record: Double reference
I think this is a bug in the dnssector library but thought you might want to know about the issue.