NS and CNAME records carry a domain name as their RDATA, and that name can use compression - so it must be decoded against the whole message, not just the RDATA slice. Today you parse a name-valued record whose target is a compression pointer.
Decode an NS or CNAME record whose RDATA is a possibly-compressed name.
CNAME (a canonical-name alias) and NS (a name server for a zone) both hold
a single domain name as their RDATA, and here is the catch that makes them
trickier than A or AAAA: that name can use compression, so its pointer offsets
are relative to the start of the whole message, not the RDATA slice. If you
tried to decode the name from an isolated copy of the RDATA bytes, the pointer
C0 0C would have nothing at offset 12 to point at. You must decode against the
full message buffer.
That is exactly what your chapter-three decoder does, so the record parse is a
one-liner: call decodeName at the RDATA’s offset within the message. The RDATA
03 77 77 77 C0 0C is the label www followed by a pointer to example.com at
offset 12, yielding www.example.com. NS records work identically - a lone name -
which is why they share this handling. Keeping name decoding message-relative is
what lets these records reference domains introduced earlier in the response.
// NS and CNAME RDATA is just a name - decode it against the WHOLE// message so its pointer can reach back to offset 12func decodeNameRData(msg []byte, rdataOff int) (string, error) {name, _, err := decodeName(msg, rdataOff) // decodeName returns name, consumed, errreturn name, err}