Skip to content

DNS Overview

Elbasiouny, Mahmoud edited this page May 29, 2026 · 8 revisions

This example builds a complete structural validator for DNS packets (RFC 1035) using Hammer. By the end, you'll have a working parser that accepts or rejects raw DNS messages based purely on grammar correctness and protocol constraints.

Unlike many DNS implementations, this example focuses strictly on validation, not semantic extraction. If the packet is structurally correct, it passes. If any invariant is violated, parsing fails.

Prerequisites: Make sure you've completed Getting Started and have either built Hammer in the checkout with scons examples or installed Hammer system-wide.


The DNS Protocol

DNS maps human-readable domain names (like example.com) to machine-readable records (like IPv4 or IPv6 addresses).

A DNS message consists of:

  1. A 12-byte header
  2. A variable-length body containing:
    • Questions
    • Answers
    • Authority records
    • Additional records

DNS Header Format

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                ID             |QR|Opcode|  DNS Flags  | RCode |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         Total Questions       |         Total Answers         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Total Auth Resource Record   |  Total Add'l Resource Record  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Note: The flags section consists of:

  • AA (Authoritative Answer) - 1 bit: Indicates the responding server is authoritative for the queried domain
  • TC (Truncated) - 1 bit: Indicates message was truncated
  • RD (Recursion Desired) - 1 bit: Client requests recursive resolution
  • RA (Recursion Available) - 1 bit: Server indicates it supports recursion
  • Z (Reserved) - 3 bits: Always set to 0

DNS Name Encoding

DNS names are encoded as:

  • One or more labels
  • Each label is length-prefixed
  • Terminated with 0x00

Example:

03 'w' 'w' 'w'
07 'e' 'x' 'a' 'm' 'p' 'l' 'e'
03 'c' 'o' 'm'
00

DNS also supports compression pointers:

11xxxxxx xxxxxxxx
  • The top two bits must be 11
  • The remaining 14 bits are an offset from the start of the DNS message

Example:

C0 0C

This means “jump to byte offset 12”.


Why DNS Is a Good Hammer Example

DNS exercises a wide range of Hammer features:

  • Sub-byte fields - QR, Opcode, and flag bits require h_bits
  • Reserved bit validation - Z bits must be zero (h_attr_bool)
  • Length-prefixed data - Labels and RDATA use h_length_value
  • Pointer validation - Compression requires contextual checks (h_tell)
  • Repetition controlled by header counts - h_repeat_n
  • Alternatives in grammar - Names may be label-based or pointer-based (h_choice)
  • Context storage - Section counts are saved using h_action

DNS provides real-world complexity without requiring external state.


Project Structure

examples/dns/
├── Makefile              # Build system (library + CLI)
├── main.c                # Entry point: reads input and runs the parser
├── dns.c                 # DNS protocol grammar defined with Hammer
├── dns.h                 # DNSContext and parser prototypes
├── tests/
│   ├── good_packets/     # Structurally valid test packets
│   ├── bad_packets/      # Structurally invalid test packets
│   └── runtests.py       # Automated test runner
File Role
main.c Entry point. Reads input (stdin or file), runs the header and body parsers, prints result.
dns.c Defines the DNS protocol grammar using Hammer combinators.
dns.h Defines DNSContext and parser declarations (headerParser, bodyParser).
tests/runtests.py Runs all good and bad packet fixtures and checks results.

Building

From the Hammer repository root, build the checkout-local library first:

scons examples

Then build the DNS example:

cd examples/dns
make

This produces the dns_parser executable. The Makefile first looks for a checkout-local Hammer library at ../../build/opt/src. If that library is not present, it falls back to pkg-config libhammer.

Quick Test

Run the parser against a known-good packet:

./dns_parser tests/good_packets/1q_pass.bin

Expected output:

Packet Passed

Run against a malformed packet:

./dns_parser tests/bad_packets/truncated_header.bin

Expected output:

Packet Failed

Walkthrough Pages

Follow these pages in order for the full tutorial:

# Page Hammer Concepts
1 Parsing the Header h_bits, h_attr_bool, h_sequence
2 Parsing the Body h_length_value, h_choice, h_repeat_n, h_tell, h_action
3 Assembling the Parser Combining header and body parsers
4 Running and Testing Testing with good and bad packets

Next: Parsing the Header

Back to: Examples Index · Hammer Fundamentals

Clone this wiki locally