DNS Response Record parsing error
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 2.6k
- Forks
- 329
- PR merge metrics
- No merged PRs in 30d
Description
Hello,
I am trying to use the pnet crate to do some creative things to DNS packets, and I have encountered an unexpected parsing issue relating to DNS response records. It seems that in the case where a DNS response packet contains multiple response records, these records do not all get added to the correct packet field, but instead each get allocated to singleton vectors in the responses, authorities and additional fields. If the packet contains more than three response records (in any of those categories), these get dropped from the packet by the parser.
Example:
use pnet::packet::ipv4::Ipv4Packet;
use pnet::packet::udp::UdpPacket;
use pnet::packet::Packet;
use pnet::packet::dns::DnsPacket;
const DNS_PACKET: &[u8; 410] = b"\x45\x00\x01\x9a\x03\x59\x00\x00\x40\x11\xc1\xbe\x08\x08\x08\x08\xac\x13\xf8\x18\x00\x35\xae\x5e\x01\x86\x74\xaa\xa5\x14\x81\x80\x00\x01\x00\x03\x00\x04\x00\x05\x0c\x64\x65\x74\x65\x63\x74\x70\x6f\x72\x74\x61\x6c\x07\x66\x69\x72\x65\x66\x6f\x78\x03\x63\x6f\x6d\x00\x00\x01\x00\x01\xc0\x0c\x00\x05\x00\x01\x00\x00\x00\xb9\x00\x1e\x0c\x64\x65\x74\x65\x63\x74\x70\x6f\x72\x74\x61\x6c\x04\x70\x72\x6f\x64\x06\x6d\x6f\x7a\x61\x77\x73\x03\x6e\x65\x74\x00\xc0\x36\x00\x05\x00\x01\x00\x00\x01\xcb\x00\x29\x04\x70\x72\x6f\x64\x0c\x64\x65\x74\x65\x63\x74\x70\x6f\x72\x74\x61\x6c\x04\x70\x72\x6f\x64\x08\x63\x6c\x6f\x75\x64\x6f\x70\x73\x06\x6d\x6f\x7a\x67\x63\x70\xc0\x4f\xc0\x60\x00\x01\x00\x01\x00\x00\x02\x69\x00\x04\x22\x6b\xdd\x52\xc0\x80\x00\x02\x00\x01\x00\x00\x86\xc5\x00\x1c\x0b\x6e\x73\x2d\x63\x6c\x6f\x75\x64\x2d\x63\x31\x0d\x67\x6f\x6f\x67\x6c\x65\x64\x6f\x6d\x61\x69\x6e\x73\xc0\x21\xc0\x80\x00\x02\x00\x01\x00\x00\x86\xc5\x00\x0e\x0b\x6e\x73\x2d\x63\x6c\x6f\x75\x64\x2d\x63\x34\xc0\xb1\xc0\x80\x00\x02\x00\x01\x00\x00\x86\xc5\x00\x0e\x0b\x6e\x73\x2d\x63\x6c\x6f\x75\x64\x2d\x63\x32\xc0\xb1\xc0\x80\x00\x02\x00\x01\x00\x00\x86\xc5\x00\x0e\x0b\x6e\x73\x2d\x63\x6c\x6f\x75\x64\x2d\x63\x33\xc0\xb1\xc0\xcd\x00\x01\x00\x01\x00\x00\x04\xec\x00\x04\xd8\xef\x26\x6c\xc0\xe7\x00\x1c\x00\x01\x00\x00\x01\x2c\x00\x10\x20\x01\x48\x60\x48\x02\x00\x34\x00\x00\x00\x00\x00\x00\x00\x6c\xc1\x01\x00\x1c\x00\x01\x00\x01\xd0\x59\x00\x10\x20\x01\x48\x60\x48\x02\x00\x36\x00\x00\x00\x00\x00\x00\x00\x6c\xc0\xcd\x00\x1c\x00\x01\x00\x00\x34\x9b\x00\x10\x20\x01\x48\x60\x48\x02\x00\x38\x00\x00\x00\x00\x00\x00\x00\x6c\x00\x00\x29\x10\x00\x00\x00\x00\x00\x00\x00";
fn main() {
let dns_pkt = Ipv4Packet::new(DNS_PACKET.as_slice()).and_then(|ip_pkt| UdpPacket::owned(ip_pkt.payload().to_vec())).and_then(|udp_pkt| DnsPacket::owned(udp_pkt.payload().to_vec())).unwrap();
println!("{dns_pkt:?}");
}
This gives the following output. There should be 3 response records in the answers field, 4 in the authorities field, and 5 in the additional field. Instead, the 3 from the responses field are distributed over all three fields. The rest are missing completely.
DnsPacket {
id : 42260,
is_response : 1,
opcode : StandardQuery,
is_authoriative : 0,
is_truncated : 0,
is_recursion_desirable : 1,
is_recursion_available : 1,
zero_reserved : 0,
is_answer_authenticated : 0,
is_non_authenticated_data : 0,
rcode : NoError,
query_count : 1,
response_count : 3,
authority_rr_count : 4,
additional_rr_count : 5,
queries : [DnsQuery { qname: [12, 100, 101, 116, 101, 99, 116, 112, 111, 114, 116, 97, 108, 7, 102, 105, 114, 101, 102, 111, 120, 3, 99, 111, 109, 0], qtype: DnsType(1), qclass: DnsClass(1), payload: [] }],
responses : [DnsResponse { name_tag: 49164, rtype: DnsType(5), rclass: DnsClass(1), ttl: 185, data_len: 30, data: [12, 100, 101, 116, 101, 99, 116, 112, 111, 114, 116, 97, 108, 4, 112, 114, 111, 100, 6, 109, 111, 122, 97, 119, 115, 3, 110, 101, 116, 0], payload: [] }],
authorities : [DnsResponse { name_tag: 49206, rtype: DnsType(5), rclass: DnsClass(1), ttl: 459, data_len: 41, data: [4, 112, 114, 111, 100, 12, 100, 101, 116, 101, 99, 116, 112, 111, 114, 116, 97, 108, 4, 112, 114, 111, 100, 8, 99, 108, 111, 117, 100, 111, 112, 115, 6, 109, 111, 122, 103, 99, 112, 192, 79], payload: [] }],
additional : [DnsResponse { name_tag: 49248, rtype: DnsType(1), rclass: DnsClass(1), ttl: 617, data_len: 4, data: [34, 107, 221, 82], payload: [] }]
}
Compare this to Python's scapy parsing the same packet.
In [1]: from scapy.all import IP, UDP, DNS, DNSRR
In [2]: p = IP(b"\x45\x00\x01\x9a\x03\x59\x00\x00\x40\x11\xc1\xbe\x08\x08\x08\x08\xac\x13\xf8\x18\x00\x35\xae\x5e\x01\x86\x74\xaa\xa5\x1
⋮ 4\x81\x80\x00\x01\x00\x03\x00\x04\x00\x05\x0c\x64\x65\x74\x65\x63\x74\x70\x6f\x72\x74\x61\x6c\x07\x66\x69\x72\x65\x66\x6f\x78\x0
⋮ 3\x63\x6f\x6d\x00\x00\x01\x00\x01\xc0\x0c\x00\x05\x00\x01\x00\x00\x00\xb9\x00\x1e\x0c\x64\x65\x74\x65\x63\x74\x70\x6f\x72\x74\x6
⋮ 1\x6c\x04\x70\x72\x6f\x64\x06\x6d\x6f\x7a\x61\x77\x73\x03\x6e\x65\x74\x00\xc0\x36\x00\x05\x00\x01\x00\x00\x01\xcb\x00\x29\x04\x7
⋮ 0\x72\x6f\x64\x0c\x64\x65\x74\x65\x63\x74\x70\x6f\x72\x74\x61\x6c\x04\x70\x72\x6f\x64\x08\x63\x6c\x6f\x75\x64\x6f\x70\x73\x06\x6
⋮ d\x6f\x7a\x67\x63\x70\xc0\x4f\xc0\x60\x00\x01\x00\x01\x00\x00\x02\x69\x00\x04\x22\x6b\xdd\x52\xc0\x80\x00\x02\x00\x01\x00\x00\x8
⋮ 6\xc5\x00\x1c\x0b\x6e\x73\x2d\x63\x6c\x6f\x75\x64\x2d\x63\x31\x0d\x67\x6f\x6f\x67\x6c\x65\x64\x6f\x6d\x61\x69\x6e\x73\xc0\x21\xc
⋮ 0\x80\x00\x02\x00\x01\x00\x00\x86\xc5\x00\x0e\x0b\x6e\x73\x2d\x63\x6c\x6f\x75\x64\x2d\x63\x34\xc0\xb1\xc0\x80\x00\x02\x00\x01\x0
⋮ 0\x00\x86\xc5\x00\x0e\x0b\x6e\x73\x2d\x63\x6c\x6f\x75\x64\x2d\x63\x32\xc0\xb1\xc0\x80\x00\x02\x00\x01\x00\x00\x86\xc5\x00\x0e\x0
⋮ b\x6e\x73\x2d\x63\x6c\x6f\x75\x64\x2d\x63\x33\xc0\xb1\xc0\xcd\x00\x01\x00\x01\x00\x00\x04\xec\x00\x04\xd8\xef\x26\x6c\xc0\xe7\x0
⋮ 0\x1c\x00\x01\x00\x00\x01\x2c\x00\x10\x20\x01\x48\x60\x48\x02\x00\x34\x00\x00\x00\x00\x00\x00\x00\x6c\xc1\x01\x00\x1c\x00\x01\x0
⋮ 0\x01\xd0\x59\x00\x10\x20\x01\x48\x60\x48\x02\x00\x36\x00\x00\x00\x00\x00\x00\x00\x6c\xc0\xcd\x00\x1c\x00\x01\x00\x00\x34\x9b\x0
⋮ 0\x10\x20\x01\x48\x60\x48\x02\x00\x38\x00\x00\x00\x00\x00\x00\x00\x6c\x00\x00\x29\x10\x00\x00\x00\x00\x00\x00\x00")
In [3]: p.show()
###[ IP ]###
version = 4
ihl = 5
tos = 0x0
len = 410
id = 857
flags =
frag = 0
ttl = 64
proto = udp
chksum = 0xc1be
src = 8.8.8.8
dst = 172.19.248.24
\options \
###[ UDP ]###
sport = domain
dport = 44638
len = 390
chksum = 0x74aa
###[ DNS ]###
id = 42260
qr = 1
opcode = QUERY
aa = 0
tc = 0
rd = 1
ra = 1
z = 0
ad = 0
cd = 0
rcode = ok
qdcount = 1
ancount = 3
nscount = 4
arcount = 5
\qd \
|###[ DNS Question Record ]###
| qname = b'detectportal.firefox.com.'
| qtype = A
| unicastresponse= 0
| qclass = IN
\an \
|###[ DNS Resource Record ]###
| rrname = b'detectportal.firefox.com.'
| type = CNAME
| cacheflush= 0
| rclass = IN
| ttl = 185
| rdlen = None
| rdata = b'detectportal.prod.mozaws.net.'
|###[ DNS Resource Record ]###
| rrname = b'detectportal.prod.mozaws.net.'
| type = CNAME
| cacheflush= 0
| rclass = IN
| ttl = 459
| rdlen = None
| rdata = b'prod.detectportal.prod.cloudops.mozgcp.net.'
|###[ DNS Resource Record ]###
| rrname = b'prod.detectportal.prod.cloudops.mozgcp.net.'
| type = A
| cacheflush= 0
| rclass = IN
| ttl = 617
| rdlen = None
| rdata = 34.107.221.82
\ns \
|###[ DNS Resource Record ]###
| rrname = b'mozgcp.net.'
| type = NS
| cacheflush= 0
| rclass = IN
| ttl = 34501
| rdlen = None
| rdata = b'ns-cloud-c1.googledomains.com.'
|###[ DNS Resource Record ]###
| rrname = b'mozgcp.net.'
| type = NS
| cacheflush= 0
| rclass = IN
| ttl = 34501
| rdlen = None
| rdata = b'ns-cloud-c4.googledomains.com.'
|###[ DNS Resource Record ]###
| rrname = b'mozgcp.net.'
| type = NS
| cacheflush= 0
| rclass = IN
| ttl = 34501
| rdlen = None
| rdata = b'ns-cloud-c2.googledomains.com.'
|###[ DNS Resource Record ]###
| rrname = b'mozgcp.net.'
| type = NS
| cacheflush= 0
| rclass = IN
| ttl = 34501
| rdlen = None
| rdata = b'ns-cloud-c3.googledomains.com.'
\ar \
|###[ DNS Resource Record ]###
| rrname = b'ns-cloud-c4.googledomains.com.'
| type = A
| cacheflush= 0
| rclass = IN
| ttl = 1260
| rdlen = None
| rdata = 216.239.38.108
|###[ DNS Resource Record ]###
| rrname = b'ns-cloud-c2.googledomains.com.'
| type = AAAA
| cacheflush= 0
| rclass = IN
| ttl = 300
| rdlen = None
| rdata = 2001:4860:4802:34::6c
|###[ DNS Resource Record ]###
| rrname = b'ns-cloud-c3.googledomains.com.'
| type = AAAA
| cacheflush= 0
| rclass = IN
| ttl = 118873
| rdlen = None
| rdata = 2001:4860:4802:36::6c
|###[ DNS Resource Record ]###
| rrname = b'ns-cloud-c4.googledomains.com.'
| type = AAAA
| cacheflush= 0
| rclass = IN
| ttl = 13467
| rdlen = None
| rdata = 2001:4860:4802:38::6c
|###[ DNS OPT Resource Record ]###
| rrname = b'.'
| type = OPT
| rclass = 4096
| extrcode = 0
| version = 0
| z = 0
| rdlen = None
| \rdata \
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the DnsPacket::owned entry point in the reproduction and inspect how the DNS response, authority, and additional counts are parsed. Use the supplied packet to verify that all 3 responses, 4 authorities, and 5 additional records remain in their respective fields after parsing.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- networking
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100