networking · advanced · ~25 min

Decode a DNS-wire label sequence (no pointer compression)

Length-prefixed wire-format parsing.

Challenge

Decode a DNS-wire domain name — a sequence of length-prefixed labels — into a dotted string. Getting the bounds right is what keeps real resolvers from looping forever.

Task

Implement int decode_dns_labels(const unsigned char *in, int in_len, char *out, int cap).

A DNS name on the wire is each label prefixed by its 1-byte length, ending with a 0-length terminator:

[3]'w' 'w' 'w' [7]'e' 'x' 'a' 'm' 'p' 'l' 'e' [3]'c' 'o' 'm' [0]

Decode this into "www.example.com" (labels joined by .), NUL-terminated. This exercise IGNORES pointer compression: any length byte with its top 2 bits set is rejected.

Input

  • in: the DNS-wire bytes.
  • in_len: number of bytes available in in.
  • out: buffer for the decoded name.
  • cap: capacity of out in bytes.

Output

Return the number of input bytes consumed (through and including the 0 terminator) on success, or -1 on any parse failure.

Example

{3,'w','w','w', 7,'e','x','a','m','p','l','e', 3,'c','o','m', 0}  ->  17, out = "www.example.com"
{0}            ->  1, out = ""        (root label)
{3,'w','w'}    -> -1                  (truncated: missing bytes)
{0xC0, 0x0C}   -> -1                  (compression pointer, refused)

Edge cases

  • The root label (just a single 0 byte) decodes to the empty string.
  • Truncated input (a label claiming more bytes than remain) returns -1.

Rules

  • Reject any label length > 63, or with the top 2 bits set (compression).
  • Never overflow out; return -1 rather than truncate.

Why this matters

DNS wire format uses length-prefixed labels. A parser that does pointer-compression wrong can loop forever — a classic CVE primitive in resolvers.

Input format

in, the DNS-wire bytes; in_len, bytes available; out buffer; cap, its size.

Output format

Input bytes consumed on success (out filled, NUL-terminated); -1 on any parse failure.

Constraints

Refuse labels >63 bytes and compression pointers (top 2 bits set). Bound every read against in_len and every write against cap.

Starter code

#include <stddef.h>
int decode_dns_labels(const unsigned char *in, int in_len, char *out, int cap) { /* TODO */ (void)in; (void)in_len; (void)out; (void)cap; return -1; }

Common mistakes

Forgetting that each label is <= 63 bytes (top 2 bits clear).

Edge cases to handle

Root label (just 00) → empty output. Truncated input.

Complexity

O(in_len).

Background lessons

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