cybersecurity · intermediate · ~15 min · safe pentest lab

Parse a Dotted-Quad IPv4 Address Safely

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.

Challenge

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.

Example

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 */

Input format

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.

Output format

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).

Constraints

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.

Starter code

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 */
}

Common mistakes

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.

Edge cases to handle

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.

Background lessons

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