cybersecurity · intermediate · ~15 min · safe pentest lab
Write a bounds-safe, deny-by-default parser for attacker-controlled network input, rejecting every malformed form (out-of-range octet, wrong octet count, empty octet, trailing junk, non-digit) instead of best-effort accepting it.
An IPv4 parser is a classic soft spot in network-facing code: allow-lists, ACL rules, and firewall configs all depend on turning an attacker-supplied string like 192.168.1.10 into four bytes. If the parser is sloppy, an attacker can smuggle malformed input past a filter — an octet of 256 that wraps, a string with too many or too few octets, a leading/empty octet, or trailing junk after a valid-looking address. Any of these can desync your view of an address from the kernel's, letting a blocked host slip through an ACL.
Your job: implement parse_ipv4 to convert a NUL-terminated dotted-quad string into out[0..3]. Deny by default — reject anything that is not exactly four decimal octets in 0..255 separated by single dots. Return 0 only on a fully valid address; return -1 for a non-digit, an octet greater than 255, fewer or more than four octets, an empty octet (e.g. 1..2.3 or a leading/trailing dot), or trailing junk. Never read past the string, and validate before you store into out.
parse_ipv4("192.168.1.10", out) -> 0 /* out becomes {192,168,1,10} */
parse_ipv4("256.0.0.1", out) -> -1 /* octet out of range, rejected */
parse_ipv4("1.2.3", out) -> -1 /* only three octets, rejected */
A NUL-terminated C string s (attacker-controlled) and a caller-provided 4-byte array out. s may be empty, may be NULL, and may contain any bytes before the terminating NUL.
Return int: 0 on success with out[0..3] set to the four parsed octets; -1 on any malformed input (out is not required to be meaningful on failure).
C11, no libc parsing helpers required (no need for inet_pton/sscanf/strtol — a hand-rolled scan is expected). Read s only up to and including its NUL terminator; never index out beyond [0..3]. Octets are decimal, 0..255. Leading zeros within an octet (e.g. "01") are accepted as long as the value stays <= 255 and the octet has at most 3 digits.
int parse_ipv4(const char *s, unsigned char out[4]) {
/* TODO: parse the dotted-quad string into out[0..3].
Return 0 on success, -1 if malformed. Bounds-check everything. */
(void)s; (void)out;
return 0; /* insecure stub: claims success without validating */
}Using atoi/strtol and ignoring leftover characters, so "1.2.3.4x" or "1.2.3.4.5" is wrongly accepted; accumulating an octet without checking >255 (integer overflow / silent wrap when cast to unsigned char); forgetting to reject an empty octet; not requiring exactly four octets; storing into out before validating; reading past the NUL when the string ends unexpectedly.
Empty string; NULL pointer; leading dot (".1.2.3"); trailing dot ("1.2.3.4."); empty octet in the middle ("1..2.3"); octet exactly 255 (valid) vs 256 (invalid); all-zero address "0.0.0.0" (valid); more than 3 digits in an octet; trailing junk after a valid address ("1.2.3.4x"); too few or too many octets.
Solve this exercise in the browser editor — compile and run against the test harness, no setup required.