Skip to content
hollow

Protocol x-ray

Every octet, named by the code that read it

The DNS codec is 1,341 lines over encoding/binary and net/netip, with name compression in both directions and a fuzz target over the decoder that has run 38.4 million executions clean. It found two compression bugs that code review and twenty hand-written malformed cases had both missed.

hollow inspect example.com72 octets
headerquestionansweradditional
offset 0x0000header

ID (Transaction ID)

Octets

85 ea

Decoded

0x85ea generated with crypto/rand to prevent off-path answer forgery.

Every span comes from the decoder the resolver itself runs, so this column records what the parser did rather than a second reading of the bytes.

The counts are not trusted

A header claiming 65,535 answers in 40 octets is a message that fails with a typed error rather than one that allocates. Each count bounds a loop; none of them sizes a buffer, and every read is bounded by the octets actually present.

Pointers must go strictly backwards

The first compression pointer must target an offset below its own position, and each one after it below the previous target. Targets therefore strictly decrease, so the walk terminates by construction — no visited set, no jump budget. Comparing against the previous target rather than the read position is what makes it sound.

An unknown type still round-trips

A record this codec does not model keeps its octets verbatim under RFC 3597 and re-encodes unchanged, because a resolver that rejects an unfamiliar type is broken. Nine types are parsed into fields; everything else stays opaque and intact.

The annotation comes from the parser

wire.Annotate walks a message with the same decoder the resolver uses and records a span per field. The spans are contiguous and cover every octet, asserted against captured fixtures, so a region nobody can name fails a test rather than being skipped over.

The same reply, as printed

One row per field rather than per fixed-width line, so a field and its meaning stay together and nothing is split across two rows.

hollow inspect example.com
$ hollow inspect example.com;; 72 octets from [2803:f800:50::6ca2:c0a2]:53 over udp in 21.438ms0000  85 ea                    ID 0x85ea0002  84 00                    flags QR=1 opcode=0 AA=1 TC=0 RD=0 RA=0 AD=0 CD=0 rcode=00004  00 01                    QDCOUNT 10006  00 02                    ANCOUNT 20008  00 00                    NSCOUNT 0000a  00 01                    ARCOUNT 1000c  07 65 78 61 6d 70 6c 65  QNAME example.com. = "example" "com"0014  03 63 6f 6d 000019  00 01                    QTYPE A (1)001b  00 01                    QCLASS IN (1)001d  c0 0c                    NAME example.com. = pointer to 0x000c001f  00 01                    TYPE A (1)0021  00 01                    CLASS IN (1)0023  00 00 01 2c              TTL 300 seconds0027  00 04                    RDLENGTH 40029  ac 42 93 f3              RDATA address 172.66.147.243002d  c0 0c                    NAME example.com. = pointer to 0x000c002f  00 01                    TYPE A (1)0031  00 01                    CLASS IN (1)0033  00 00 01 2c              TTL 300 seconds0037  00 04                    RDLENGTH 40039  68 14 17 9a              RDATA address 104.20.23.154003d  00                       NAME . = root, one zero octet003e  00 29                    TYPE OPT (41)0040  04 d0                    UDP size 1232 octets the sender will accept0042  00 00 00 00              extended rcode and flags version 0, DO=00046  00 00                    RDLENGTH 0