01. DNS message format (RFC 1035)
1. Problem
In real networking systems, dns message format (rfc 1035) is a critical component that you encounter constantly. If you cannot implement it correctly from first principles, you will be at the mercy of library bugs, misconfigurations, and subtle protocol violations that are nearly impossible to debug without deep understanding.
The challenge is that dns message format (rfc 1035) involves precise binary layouts, strict protocol rules, and edge cases that only manifest under specific network conditions. Getting even one byte wrong means packets are silently dropped or connections mysteriously fail.
2. Theory
DNS message format (RFC 1035) is a core concept in Application Protocols. Understanding it requires grasping both the design philosophy and the implementation details.
The authoritative specification is RFC 1035.
DNS message format (RFC 1035):
+---------------------+
| Header | 12 bytes (ID, flags, counts)
+---------------------+
| Question | QNAME + QTYPE + QCLASS
+---------------------+
| Answer | Resource Records (RRs)
+---------------------+
| Authority | NS RRs
+---------------------+
| Additional | Extra RRs (e.g., A records for NS)
+---------------------+
Header:
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ID |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR| Opcode |AA|TC|RD|RA| Z | RCODE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QDCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
QNAME: sequence of labels (length-prefixed), terminated by 0x00. Example: www.example.com = 03 77 77 77 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
3. Math / Spec
The authoritative specification is RFC 1035. Key fields and their sizes are defined with bit-level precision.
The protocol defines specific algorithms and data formats that must be implemented exactly for interoperability:
- Header format: fixed and variable-length fields with specific byte ordering
- Checksum: error detection method (one's complement sum or CRC)
- State transitions: valid sequences of operations and responses
- Timer values: retransmission timeouts, keepalive intervals, expiration times
All multi-byte integer fields are in network byte order (big-endian) unless explicitly stated otherwise.
4. Code
"""
dns_message_format_rfc_1035.py -- DNS message format (RFC 1035)
"""
import struct
import socket
class PacketBuilder:
"""Build and parse protocol packets for DNS message format (RFC 1035)."""
HDR_FMT = '!BBH I' # ver_type, flags, length, id
HDR_SIZE = struct.calcsize(HDR_FMT)
@staticmethod
def build(pkt_type: int, payload: bytes, flags: int = 0, pkt_id: int = 0) -> bytes:
ver_type = (1 << 4) | (pkt_type & 0xF)
length = PacketBuilder.HDR_SIZE + len(payload)
header = struct.pack(PacketBuilder.HDR_FMT, ver_type, flags, length, pkt_id)
return header + payload
@staticmethod
def parse(data: bytes) -> dict:
if len(data) < PacketBuilder.HDR_SIZE:
raise ValueError(f"Too short: {len(data)} bytes")
ver_type, flags, length, pkt_id = struct.unpack(
PacketBuilder.HDR_FMT, data[:PacketBuilder.HDR_SIZE])
return {
'version': (ver_type >> 4) & 0xF,
'type': ver_type & 0xF,
'flags': flags,
'length': length,
'id': pkt_id,
'payload': data[PacketBuilder.HDR_SIZE:length],
}
@staticmethod
def checksum(data: bytes) -> int:
"""Internet checksum (RFC 1071)."""
if len(data) % 2:
data += b'\x00'
s = sum(struct.unpack('!%dH' % (len(data) // 2), data))
while s >> 16:
s = (s & 0xFFFF) + (s >> 16)
return ~s & 0xFFFF
def demo():
payload = b"Hello from DNS message format (RFC 1035)"
pkt = PacketBuilder.build(pkt_type=1, payload=payload, pkt_id=42)
print(f"Built {len(pkt)}-byte packet: {pkt.hex(' ')}")
parsed = PacketBuilder.parse(pkt)
print(f"Parsed: ver={parsed['version']} type={parsed['type']} "
f"flags=0x{parsed['flags']:02x} len={parsed['length']} id={parsed['id']}")
print(f"Payload: {parsed['payload']}")
print(f"Checksum: 0x{PacketBuilder.checksum(pkt):04x}")
if __name__ == '__main__':
demo()
5. Tests
import pytest
import struct
def test_roundtrip():
payload = b"test data"
pkt = PacketBuilder.build(pkt_type=1, payload=payload, pkt_id=42)
parsed = PacketBuilder.parse(pkt)
assert parsed['version'] == 1
assert parsed['type'] == 1
assert parsed['payload'] == payload
assert parsed['id'] == 42
def test_truncated():
with pytest.raises(ValueError):
PacketBuilder.parse(b"\x10\x00")
def test_checksum_roundtrip():
data = b"\x00\x01\x00\x02"
cs = PacketBuilder.checksum(data)
combined = data + struct.pack('!H', cs)
assert PacketBuilder.checksum(combined) == 0
def test_empty_payload():
pkt = PacketBuilder.build(0, b"")
parsed = PacketBuilder.parse(pkt)
assert parsed['length'] == PacketBuilder.HDR_SIZE
assert parsed['payload'] == b""
def test_large_payload():
big = b"X" * 10000
pkt = PacketBuilder.build(1, big)
parsed = PacketBuilder.parse(pkt)
assert parsed['payload'] == big
6. Exercises
★ Parse a hex dump of a real dns message format packet and identify every field manually.
★ Implement the basic parser and verify it produces byte-identical output to a reference implementation.
★★ Add comprehensive input validation: reject packets with invalid field values and return appropriate error codes.
★★ Handle all edge cases: minimum-size packets, maximum-size packets, optional fields, and malformed input.
★★ Write a pcap analyzer that reads capture files and decodes dns message format packets with full field breakdown.
★★★ Implement the complete protocol state machine. Verify all transitions with a test harness.
★★★ Benchmark parsing throughput (packets/sec) and compare to theoretical line rate.
★★★ Test against real network traffic: capture live packets and verify your parser handles all observed variations.