Reading — step 1 of 5
Read
UDP Tracker Protocol
Do the arithmetic on HTTP announces. Each one costs a TCP handshake, HTTP request headers, response headers, and a teardown — several hundred bytes and two round-trips of latency for what is logically "here I am, who else is here?". Now multiply by a public tracker's load: millions of peers, each re-announcing every 30 minutes, across thousands of torrents. BEP 15 replaces the whole exchange with two UDP datagrams each way and a fixed binary layout.
Why there is a connect step at all
UDP has no handshake, and that is a security problem, not just a convenience: the source address of a datagram is trivially forged. A naive UDP tracker could be tricked into announcing victims into swarms, or used as a DDoS amplifier (small spoofed request in, large peer list out, aimed at the victim). The fix is a cheap proof of address ownership:
Step 1 — connect. You send a request containing a magic constant and a random transaction_id; the tracker replies with a connection_id. Only someone who can receive at the source address ever learns that value.
Step 2 — announce. You present the connection_id. No valid id, no service — spoofed announces die at step 1.
Connect, byte by byte
Request — exactly 16 bytes, every integer big-endian:
| Offset | Size | Field | Value |
|---|---|---|---|
| 0 | 8 | protocol_id | 0x41727101980 (magic) |
| 8 | 4 | action | 0 = connect |
| 12 | 4 | transaction_id | random, chosen by you |
Response — 16 bytes:
| Offset | Size | Field |
|---|---|---|
| 0 | 4 | action (0, echoed) |
| 4 | 4 | transaction_id (echoed — MUST match yours) |
| 8 | 8 | connection_id |
Check the echoed transaction_id before believing anything — a mismatched reply is stale or forged, so drop it. UDP also loses packets silently; BEP 15 prescribes retransmission with exponential backoff (15 × 2^n seconds).
The connection_id is short-lived by design: a client may keep using it for about a minute, and trackers honor it for at most two — after that you reconnect. Within that window one connect can front many requests (announces for every torrent you manage, scrapes), which is where the savings multiply. The routine 30-minute re-announce does need a fresh connect first — still just two tiny packets instead of a whole TCP + HTTP exchange.
Announce (step 2), for context
The announce packet is a fixed 98 bytes: connection_id(8) action=1(4) transaction_id(4) info_hash(20) peer_id(20) downloaded(8) left(8) uploaded(8) event(4) ip(4) key(4) num_want(4) port(2). The reply is action(4) transaction_id(4) interval(4) leechers(4) seeders(4) followed by 6-byte compact peer entries — the same 4-byte-IP + 2-byte-port format the HTTP tracker uses.
Your exercise
Build CONNECT-REQ and parse CONNECT-RESP, both as hex. Mistakes the grader catches:
- The magic constant is only 6 non-zero bytes. 0x41727101980 fits in
04 17 27 10 19 80; the field is 8 bytes, so left-pad with zeros:CONNECT-REQ 12345678must print exactly00000417271019800000000012345678(32 hex chars). Emit a 6-byte protocol_id and you produce 28 chars and fail every BUILD test. - Dropping leading zeros on output. The hidden test expects
transaction_id=00000001— fixed-width, zero-padded hex (8 chars for the id, 16 for connection_id), never1. - Parsing the response with the request layout. The response is action(4) + transaction_id(4) + connection_id(8) — there is no protocol_id. Skip 8 bytes first and you will report garbage instead of
action=0.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…