networking · intermediate · ~15 min

DNS Question Header Packer

Construct raw binary DNS headers and encode domain names into RFC 1035 wire labels.

Challenge

DNS queries use a 12-byte binary header followed by labeled domain name questions (e.g. "\x03www\x06google\x03com\x00"). Network tools pack these wire records in big-endian network byte order.

Your Task

Implement:

int pack_dns_query_header(uint16_t tx_id, uint16_t question_count, uint8_t *wire, size_t wire_cap);
int encode_dns_qname(const char *domain, uint8_t *wire, size_t wire_cap, size_t *qname_len);

Rules

  1. In pack_dns_query_header:
    • If wire == NULL or wire_cap < 12, return -1.
    • Write 12-byte header:
      • bytes 0..1: tx_id (big endian)
      • bytes 2..3: flags = 0x0100 (standard query with recursion desired)
      • bytes 4..5: question_count (big endian)
      • bytes 6..11: zeros (answer RRs, authority RRs, additional RRs all 0)
    • Return 12.
  2. In encode_dns_qname:
    • If domain == NULL, wire == NULL, qname_len == NULL, or wire_cap == 0, return -1.
    • Convert dot-separated domain (e.g. "abc.com") to label format (\x03abc\x03com\x00).
    • Each label length must be in 1..63. Empty labels (consecutive dots ..) return -1.
    • If encoded qname exceeds wire_cap, return -1.
    • On success: write bytes to wire, set *qname_len, and return 0.

Example

uint8_t wire[64];
pack_dns_query_header(0x1234, 1, wire, sizeof(wire)); // returns 12
size_t qlen = 0;
encode_dns_qname("example.org", wire + 12, sizeof(wire) - 12, &qlen); // wire becomes \x07example\x03org\x00

Input format

tx_id, question_count: uint16_t; domain: string; wire buffers and capacities.

Output format

Returns 12 on header pack, 0 on qname encode, or -1 on error.

Constraints

Big-endian network byte order. Bounded label format validation.

Starter code

#include <stddef.h>
#include <stdint.h>

int pack_dns_query_header(uint16_t tx_id, uint16_t question_count, uint8_t *wire, size_t wire_cap) {
    (void)tx_id; (void)question_count; (void)wire; (void)wire_cap;
    return -1;
}
int encode_dns_qname(const char *domain, uint8_t *wire, size_t wire_cap, size_t *qname_len) {
    (void)domain; (void)wire; (void)wire_cap; (void)qname_len;
    return -1;
}

Common mistakes

Forgetting terminating 0x00 byte on QNAME; wrong byte order for transaction ID.

Edge cases to handle

Labels longer than 63 bytes rejected; consecutive dots rejected; trailing dot handled gracefully.

Background lessons

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.