cybersecurity · intermediate · ~15 min · safe pentest lab
Learn to write a strict, deny-by-default syntactic validator for attacker-controlled hostnames: enforce total-length and per-label bounds, restrict the character set, forbid leading/trailing hyphens and empty labels, and reject NULL/empty — all without reading out of bounds.
Many services take a hostname from an attacker-controlled source — a Host: header, an API parameter, an SSRF-prone "fetch this URL" field — and hand it straight to a resolver, a connection routine, or a log line. The insecure assumption is that "it looks like a name, so it's fine." Un-validated names smuggle in path characters, spaces, embedded NULs, over-long labels that overflow downstream fixed buffers, and empty labels that confuse parsers. Your job is the input gate: decide, with strict RFC-1035-style syntax rules, whether a name is even shaped like a legal hostname before anything else touches it.
Implement int valid_hostname(const char *h) operating only on the string you are given (a fixed buffer in the test harness — never a live target, socket, or file). Return 1 iff h is syntactically valid, else 0.
A name is valid when: total length is 1..253; it is one or more labels separated by .; each label is 1..63 characters drawn from [A-Za-z0-9-]; and no label starts or ends with -. NULL or an empty string is invalid. Deny by default and bounds-check every length before you trust it.
valid_hostname("www.example.com") -> 1valid_hostname("-bad.com") -> 0valid_hostname(".") -> 0A single C string pointer h. It may be NULL, empty, or arbitrary bytes ending in a NUL terminator. Treat every byte as untrusted.
Return int 1 if h is a syntactically valid DNS hostname, otherwise 0. No printing, no allocation.
C11, single function, no dynamic allocation. Total length must be in 1..253. Each dot-separated label must be 1..63 chars of [A-Za-z0-9-] and may not begin or end with '-'. NULL or empty => 0. Do not read past the NUL terminator.
int valid_hostname(const char *h){
/* TODO: validate DNS hostname syntax defensively.
Rules: total length 1..253; one or more '.'-separated labels;
each label 1..63 chars of [A-Za-z0-9-]; no leading/trailing '-';
NULL or empty => 0. Bounds-check before you trust any length.
This stub is intentionally wrong so the tests fail. */
(void)h;
return -1;
}Dereferencing h before the NULL check; using <=/< off-by-one on the 63 or 253 bounds; forgetting that an empty label ("..", leading/trailing dot) is invalid; allowing a leading or trailing '-' inside a label; permitting underscores because they are common in practice but not valid DNS syntax; scanning past the NUL when checking the character after a hyphen; treating an empty string as valid because the loop never runs.
NULL pointer; empty string; single-character host ("x"); a lone "."; leading dot; trailing dot; consecutive dots (empty label); label of exactly 63 (valid) vs 64 (invalid); total length exactly 253 (valid) vs 254 (invalid); leading hyphen; trailing hyphen; underscore or space or other disallowed byte.
Solve this exercise in the browser editor — compile and run against the test harness, no setup required.