DNS
The Domain Name System turns names into addresses (example.com → 192.0.2.2), and addresses back into names. A client sends a query to a resolver, usually over UDP port 53, and gets a response with the same 16-bit id. Queries and responses share one message format.
The message
+---------------------+
| Header | 12 bytes
+---------------------+
| Question | what is asked
+---------------------+
| Answer | resource records that answer it
+---------------------+
| Authority | records pointing to the authoritative servers
+---------------------+
| Additional | extra records (EDNS options...)
+---------------------+
Header
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ID |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR| Opcode |AA|TC|RD|RA| Z|AD|CD| RCODE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QDCOUNT |
| ANCOUNT |
| NSCOUNT |
| ARCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| Field | Meaning |
|---|---|
| ID | Chosen by the client and copied into the response. It should be random, because a predictable id lets an attacker forge an answer. |
| QR | 0 = query, 1 = response |
| Opcode | 0 = standard query |
| AA, TC | authoritative answer; truncated (retry over TCP) |
| RD, RA | recursion desired (by the client); recursion available (from the server) |
| AD, CD | DNSSEC: authenticated data; checking disabled |
| RCODE | 0 NOERROR, 2 SERVFAIL, 3 NXDOMAIN (no such name), 5 REFUSED |
| counts | how many entries each section has |
Names
A name is a list of labels, each preceded by its length, ending with a 0 byte. example.com is:
07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
7 e x a m p l e 3 c o m end
To save space, a name can end with a pointer to an earlier name in the same message: two bytes starting with the bits 11, the other 14 bits being an offset. c0 0c means "the name at offset 12", which is the question's name, since the header is 12 bytes. Pointers can point to pointers, so a careless parser can be sent into a loop.
Question and resource records
A question is a name, a type (2 bytes) and a class (2 bytes, 1 = IN). Record types:
| Type | Value | Holds |
|---|---|---|
| A | 1 | an IPv4 address |
| NS | 2 | a name server's name |
| CNAME | 5 | another name (an alias) |
| PTR | 12 | a name, for reverse lookups |
| MX | 15 | a mail server |
| TXT | 16 | text |
| AAAA | 28 | an IPv6 address |
| OPT | 41 | EDNS options (a pseudo-record) |
| HTTPS | 65 | HTTPS service parameters (ALPN, ECH...) |
A resource record is: name, type, class, TTL (4 bytes, seconds it may be cached), data length (2 bytes), data.
Example: query and answer
The query, after the UDP header:
9e 28 01 20 00 01 00 00 00 00 00 01 07 65 78 61
6d 70 6c 65 03 63 6f 6d 00 00 01 00 01 00 00 29
04 d0 00 00 00 00 00 0c 00 0a 00 08 41 f8 43 a3
bc b7 57 11
| Bytes | Field | Value |
|---|---|---|
9e 28 |
ID | 0x9e28 |
01 20 |
Flags | 0000 0001 0010 0000: query, RD (recursion desired), AD |
00 01 00 00 00 00 00 01 |
Counts | 1 question, 0 answers, 0 authority, 1 additional |
07 example 03 com 00 |
Question name | example.com |
00 01 00 01 |
Type, class | A, IN |
00 00 29 04 d0 00 00 00 00 |
Additional: OPT | root name, type 41 (EDNS): UDP size 1232, no extended flags |
00 0c 00 0a 00 08 … |
OPT data | 12 bytes: option 10 (cookie), 8 random bytes |
The answer:
9e 28 81 80 00 01 00 01 00 00 00 00 07 65 78 61
6d 70 6c 65 03 63 6f 6d 00 00 01 00 01 c0 0c 00
01 00 01 00 00 01 2c 00 04 c0 00 02 02
| Bytes | Field | Value |
|---|---|---|
9e 28 |
ID | same as the query |
81 80 |
Flags | 1000 0001 1000 0000: response, RD, RA, RCODE 0 (NOERROR) |
00 01 00 01 00 00 00 00 |
Counts | 1 question (repeated), 1 answer |
07 example 03 com 00 00 01 00 01 |
Question | example.com, A, IN |
c0 0c |
Answer: name | pointer to offset 12: example.com |
00 01 00 01 |
Type, class | A, IN |
00 00 01 2c |
TTL | 300 s |
00 04 c0 00 02 02 |
Data | 4 bytes: 192.0.2.2 |
The IPv6 lookup asks for type AAAA (00 1c) and gets 16 bytes back: 20 01 0d b8 … 00 02, which is 2001:db8::2.
Reverse lookups: PTR
To find the name of 192.0.2.1, the client asks for a PTR record under a special domain:
- IPv4: the address bytes reversed, under
in-addr.arpa.192.0.2.1becomes1.2.0.192.in-addr.arpa. - IPv6: the 32 hex digits, one label each, reversed, under
ip6.arpa.2001:db8::1becomes1.0.0.0.…8.b.d.0.1.0.0.2.ip6.arpa(74 bytes once encoded).
A query NetOtter sent, and its answer:
Reverse DNS query #13505 for 192.0.2.1 -> 192.0.2.2:53 (40 bytes)
0000 34 c1 01 00 00 01 00 00 00 00 00 00 01 31 01 32 |4............1.2|
0010 01 30 03 31 39 32 07 69 6e 2d 61 64 64 72 04 61 |.0.192.in-addr.a|
0020 72 70 61 00 00 0c 00 01 |rpa.....|
Reverse DNS answer #13505: client.example.com (72 bytes)
0000 34 c1 81 80 00 01 00 01 00 00 00 00 01 31 01 32 |4............1.2|
0010 01 30 03 31 39 32 07 69 6e 2d 61 64 64 72 04 61 |.0.192.in-addr.a|
0020 72 70 61 00 00 0c 00 01 c0 0c 00 0c 00 01 00 00 |rpa.............|
0030 0e 10 00 14 06 63 6c 69 65 6e 74 07 65 78 61 6d |.....client.exam|
0040 70 6c 65 03 63 6f 6d 00 |ple.com.|
- Query: flags
01 00(just RD), then labels1,2,0,192,in-addr,arpa, then type00 0c(PTR) and class IN. - Answer: the record's data is a name,
client.example.com, 20 bytes long (00 14).
When no name exists, the server answers NXDOMAIN (flags 81 83: RCODE 3).
Over TCP and encrypted
| Transport | When |
|---|---|
| UDP 53 | Almost every query. |
| TCP 53 | When the answer was truncated (TC), and zone transfers. Messages get a 2-byte length prefix. |
| DNS over TLS (port 853), DNS over HTTPS (443), DNS over QUIC | Encrypted: the network can't read the names. |
In NetOtter
- Hostname learning:
dns_learn()(Parsers) reads UDP answers from port 53. Each A / AAAA record maps its address to the question name, so a CNAME chain still names the address after what was asked.dns_read_name()follows at most 16 pointers and caps names at 255 characters. - Analysis view: after the answer above,
192.0.2.2is shown asexample.comini(Dest. host example.com). - Reverse lookups: NetOtter's own PTR queries are built by
build_query(); see Reverse DNS and privacy. - Not decoded: DNS over TCP, DoT and DoH.
Read more
User guide
- Overview
- Installation
- Getting started
- Command-line options
- Interactive commands
- Display filters
- Analysis view
- Reverse DNS and privacy
- Anomaly detection
- Save files
- Memory and limits
- Troubleshooting
Developer documentation
- Overview
- Building
- Architecture
- OS backends
- Packet parsing
- Packet storage
- Save format
- Hostname learning
- Reverse DNS internals
- Filters and analysis internals
- Anomaly lab
- Coding conventions
- Function explorer
Learning