Header plus question is a real, sendable DNS query. Today you assemble the full packet with a fixed ID and recursion desired, producing the exact bytes a resolver would put on the wire - the first genuinely usable artifact of the project.
Assemble a complete query message from a header and one question.
Everything so far comes together here: a query message is the 12-byte header
followed by the question section, and nothing else for a simple lookup. Set
QDCOUNT to 1 because there is one question, set the RD flag so the header
reads 01 00 in its flags bytes, pick an ID the reply must echo back, and
append the encoded question. The result is a 33-byte packet that is byte-for-byte
what a real resolver sends.
This is the project’s first genuinely usable output. In a real program you would hand these bytes to a UDP socket aimed at a DNS server and read the reply; because we keep the socket out of the testable core, you instead pass them through an injectable transport later. Either way, the packet is correct and complete. The next chapter tackles the trickiest part of reading the reply that comes back: names compressed with pointers.
func BuildQuery(id uint16, name string) []byte {h := Header{ID: id, Flags: flagRD, QDCOUNT: 1}msg := h.Encode()return append(msg, encodeQuestion(name, TypeA, ClassIN)...)}