TCP segment encoding and decoding utilities (RFC 9293).
A TCP segment is a 20-byte fixed header, an optional 0–40 byte options
trailer (sized by the dataOffset field), and a variable-length payload:
0 7 8 15 16 23 24 31 +--------+--------+--------+--------+ | Source | Destination | | Port | Port | +--------+--------+--------+--------+ | Sequence Number | +--------+--------+--------+--------+ | Acknowledgment Number | +--------+--------+--------+--------+ | DOff |Rsrvd|C E U A P R S F| Window| | 4b | 4b |W C R C S S Y I| 16 bit| | | |R E G K H T N N| | +--------+--------+--------+--------+ | Checksum | Urgent Pointer | +--------+--------+--------+--------+ | Options (0-40 bytes) | +-----------------------------------+ | Payload (variable) | +-----------------------------------+
Unlike UDP, TCP carries no segment-length field — the payload size must come
from the carrier (typically IPv4.totalLength - IPv4.ihl * 4). The coder
therefore absorbs the rest of the buffer it is given as the payload, so it
is round-trip-safe only when handed a buffer slice already trimmed to the
segment length. @binstruct/inet's inetFrame() does this trimming; stand-
alone callers should slice before decoding.
The checksum field is computed over a TCP pseudo-header followed by the
segment; assembling the pseudo-header is the caller's job. Use
internetChecksum from @binstruct/inet once the bytes are concatenated.
Round-trip a SYN segment with no options or payload
Round-trip a SYN segment with no options or payload
import { assertEquals } from "@std/assert"; import { TCP_HEADER_MIN_SIZE, tcpPacket } from "@binstruct/tcp"; const coder = tcpPacket(); const segment = { sourcePort: 49152, destinationPort: 80, sequenceNumber: 0xdeadbeef, acknowledgmentNumber: 0, dataOffsetReserved: { dataOffset: 5, reserved: 0 }, flags: { cwr: 0, ece: 0, urg: 0, ack: 0, psh: 0, rst: 0, syn: 1, fin: 0 }, window: 65535, checksum: 0, urgentPointer: 0, options: new Uint8Array(0), payload: new Uint8Array(0), }; const buffer = new Uint8Array(TCP_HEADER_MIN_SIZE); const written = coder.encode(segment, buffer); const [decoded, read] = coder.decode(buffer); assertEquals(written, TCP_HEADER_MIN_SIZE); assertEquals(read, TCP_HEADER_MIN_SIZE); assertEquals(decoded.flags.syn, 1); assertEquals(decoded.sequenceNumber, 0xdeadbeef);