Skip to main content

Framing & Parsing

beginner12 min readLesson 193 of 204

Everything wrong with network code starts here: TCP has no messages, so you invent them โ€” and every framing choice has attack surface.

The stream, not the message

recv() returns some bytes โ€” not "a message". Ten calls might deliver one request; one call might deliver three and a half. Before any parsing, you need framing: a rule for where messages end.

  • Delimiter-based (\n): simple, but payload cannot contain the delimiter without escaping โ€” and naive find('\n') on attacker input is how request smuggling starts.
  • Length-prefixed (4-byte length then payload): O(1) framing, but the length field is attacker input: read it as uint32_t, validate against a sane maximum before allocating, and only then reserve. std::string(len, 'x') with len = 0xFFFFFFFF from a hostile peer is an OOM you shipped.

Parsing in an incremental world

Robust parsers are state machines: feed bytes, keep leftover state, return "need more" or "one frame + consumed N". A parser that assumes the whole frame arrived in one buffer is a bug on day one. The same accumulator must also reject garbage: bad magic, absurd lengths, over-long lines โ€” with distinguishable errors, because operators debug by error class.

The security lens early

Every length is untrusted. Every delimiter can be spoofed by content. Frame limits (max size, max count) are not performance tuning โ€” they are the difference between a service and a DoS amplifier.

Now practice

Practice: Protocol HardeningA frame decoder that must survive byte-dribbling and a 2 GiB length lie, plus a bounded queue whose counters stay honest.2 challenges ยท ยท ~18 min