Skip to content

TFTP readWritePacket

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

Read Requests (RRQ, opcode 1) and Write Requests (WRQ, opcode 2) share the same packet structure. They are the first message a client sends to initiate a file transfer.

Source file: tftp.c
Hammer concepts: h_int_range · h_ch_range · h_token · h_many1 · h_sequence


RRQ/WRQ Packet Layout

 2 bytes     string    1 byte    string   1 byte
+---------+-----------+------+-----------+------+
| Opcode  | Filename  |  0   |   Mode    |  0   |
+---------+-----------+------+-----------+------+

According to the RFC, a read or write request consists of:

  • Opcode - 2 bytes, value 1 (RRQ) or 2 (WRQ)
  • Filename - Variable-length string in netascii, terminated by a zero byte (\x00)
  • Mode - One of "netascii", "octet", or "mail", terminated by a zero byte (\x00)

All three sections must be valid for the packet to pass.


Step 1: Opcode with h_int_range

The opcode must be either 1 or 2:

H_RULE(opc, h_int_range(h_uint16(), 1, 2));

This reads 16 bits and rejects any value outside the range [1, 2].


Step 2: Zero Byte Terminator

Both the filename and mode are terminated by a zero byte:

H_RULE(zbyt, h_int_range(h_uint8(), 0, 0));

This reads 8 bits and only accepts a value of 0.


Step 3: Filename with h_ch_range and h_many1

A TFTP filename is a sequence of printable netascii characters. We define parsers for each valid character class:

H_RULE(lcAlph, h_ch_range('a', 'z'));
H_RULE(ucAlph, h_ch_range('A', 'Z'));
H_RULE(numbers, h_ch_range('0', '9'));
H_RULE(spclChars, h_choice(h_ch('.'), h_ch('_'), h_ch('/'),
                            h_ch('-'), h_ch(' '), NULL));

Then combine them into the filename parser using h_many1 (one or more characters) followed by the zero byte terminator:

H_RULE(filename, h_sequence(h_many1(h_choice(lcAlph, ucAlph,
    numbers, spclChars, NULL)), zbyt, NULL));

h_many1 requires at least one character, so empty filenames are rejected.


Step 4: Mode String with h_token

The mode field must be exactly one of three strings. h_token matches a specific byte sequence:

H_RULE(netascii, h_token((const uint8_t *)"netascii", 8));
H_RULE(octet, h_token((const uint8_t *)"octet", 5));
H_RULE(mail, h_token((const uint8_t *)"mail", 4));
H_RULE(mode, h_sequence(h_choice(netascii, octet, mail, NULL),
    zbyt, NULL));

h_token(string, length) succeeds only if the next length bytes match string exactly. The mode is followed by a zero byte terminator.


Step 5: Assemble the RRQ/WRQ Parser

H_RULE(rrqwrq, h_sequence(opc, filename, mode, NULL));

When rrqwrq runs, it parses the opcode, filename (with terminator), and mode (with terminator) in order. If any part fails, the entire parse fails.


Summary

Concept Hammer Function Why It Matters in TFTP
Constrain values h_int_range Opcode must be 1 or 2
Match characters h_ch_range Filename characters must be printable ASCII
Match exact strings h_token Mode must be "netascii", "octet", or "mail"
One or more h_many1 Filenames must have at least one character
Parse in order h_sequence Fields appear consecutively

Next: DATA Packets

Previous: TFTP Overview

Clone this wiki locally