Expand description
UDP Tracker.
This module contains the UDP tracker implementation.
The UDP tracker is a simple UDP server that responds to these requests:
Connect
: used to get a connection ID which must be provided on each request in order to avoid spoofing the source address of the UDP packets.Announce
: used to announce the presence of a peer to the tracker.Scrape
: used to get information about a torrent.
It was introduced in BEP 15. UDP Tracker Protocol for BitTorrent
as an alternative to the HTTP tracker.
The UDP tracker is more efficient than the HTTP tracker because it uses UDP
instead of TCP.
Refer to the bit_torrent
module for more
information about the BitTorrent
protocol.
Refer to BEP 15. UDP Tracker Protocol for BitTorrent
and to BEP 41. UDP Tracker Protocol Extensions
for more information about the UDP tracker protocol.
NOTICE: BEP-41 is not implemented yet.
NOTICE: we are using the
aquatic_udp_protocol
crate so requests and responses are handled by it.
NOTICE: all values are send in network byte order (big endian).
§Table of Contents
§Actions
Requests are sent to the tracker using UDP packets. The UDP tracker protocol
is designed to be as simple as possible. It uses a single UDP port and
supports only three types of requests: Connect
, Announce
and Scrape
.
Request are parsed from UDP packets using the aquatic_udp_protocol
crate and then handled by the Tracker
struct.
And then the response is also build using the aquatic_udp_protocol
and converted to a UDP packet.
UDP packet -> Aquatic Struct Request -> [Torrust Struct Request] -> Tracker -> Aquatic Struct Response -> UDP packet
§Connect
Connect
requests are used to get a connection ID which must be provided on
each request in order to avoid spoofing the source address of the UDP.
The connection ID is a random 64-bit integer that is used to identify the client. It is used to prevent spoofing of the source address of the UDP packets. Before announcing or scraping, you have to obtain a connection ID.
The connection ID is generated by the tracker and sent back to the client’s IP address. Only the client using that IP can receive the response, so the tracker can be sure that the client is the one who sent the request. If the client’s IP was spoofed the tracker will send the response to the wrong client and the client will not receive it.
The reason why the UDP tracker protocol needs a connection ID to avoid IP spoofing can be explained as follows:
-
No connection state: Unlike TCP, UDP is a connectionless protocol, meaning that it does not establish a connection between two endpoints before exchanging data. As a result, it is more susceptible to IP spoofing, where an attacker sends packets with a forged source IP address, tricking the receiver into believing that they are coming from a legitimate source.
-
Mitigating IP spoofing: To mitigate IP spoofing in the UDP tracker protocol, a connection ID is used. When a client wants to interact with a tracker, it sends a “connect” request to the tracker, which, in turn, responds with a unique connection ID. This connection ID must be included in all subsequent requests from the client to the tracker.
-
Validating requests: By requiring the connection ID, the tracker can verify that the requests are coming from the same client that initially sent the “connect” request. If an attacker attempts to spoof the client’s IP address, they would also need to know the valid connection ID to be accepted by the tracker. This makes it significantly more challenging for an attacker to spoof IP addresses and disrupt the P2P network.
There are different ways to generate a connection ID. The most common way is to generate a time bound secret. The secret is generated using a time based algorithm and it is valid for a certain amount of time.
connection ID = hash(client IP + current time slot + secret seed)
The BEP-15 recommends a two-minute time slot. Refer to connection_cookie
for more information about the connection ID generation with this method.
§Connect Request
Connect request (UDP packet)
Offset | Type/Size | Name | Description | Hex | Decimal |
---|---|---|---|---|---|
0 | i64 | protocol_id | Magic constant that will identify the protocol. | 0x00_00_04_17_27_10_19_80 | 4497486125440 |
8 | i32 | action | Action identifying the connect request. | 0x00_00_00_00 | 0 |
12 | i32 | transaction_id | Randomly generated by the client. | 0x34_FA_A1_F9 | -888840697 |
Sample connect request (UDP packet)
UDP packet bytes:
Offset: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
Decimal: [ 0, 0, 4, 23, 39, 16, 25, 128, 0, 0, 0, 0, 203, 5, 94, 7]
Hex: [0x00, 0x00, 0x04, 0x17, 0x27, 0x10, 0x19, 0x80, 0x00, 0x00, 0x00, 0x00, 0xCB, 0x05, 0x5E, 0x07]
Param: [<------------- protocol_id ------------------>,<------- action ------>,<--- transaction_id -->]
UDP packet fields:
Offset | Type/Size | Name | Bytes Dec (Big Endian) | Hex | Decimal |
---|---|---|---|---|---|
0 | i64 | protocol_id | [0, 0, 4, 23, 39, 16, 25, 128] | 0x00_00_04_17_27_10_19_80 | 4497486125440 |
4 | i32 | action | [0, 0, 0, 0] | 0x00_00_00_00 | 0 |
8 | i32 | transaction_id | [35, 63, 226, 1] | 0xCB_05_5E_07 | -888840697 |
Connect request (parsed struct)
After parsing the UDP packet, the ConnectRequest
request struct will look like this:
Field | Type | Example |
---|---|---|
transaction_id | TransactionId | 1950635409 |
§Connect Response
Connect response (UDP packet)
Offset | Type/Size | Name | Description | Hex | Decimal |
---|---|---|---|---|---|
0 | i64 | action | Action identifying the connect request | 0x00_00_00_00 | 0 |
4 | i32 | transaction_id | Must match the transaction_id sent from the client. | 0xCB_05_5E_07 | -888840697 |
8 | i32 | connection_id | Generated by the tracker to authenticate the client. | 0xC5_58_7C_09_08_48_D8_37 | -4226491872051668937 |
NOTICE: the
connection_id
is used when further information is exchanged with the tracker, to identify the client. Thisconnection_id
can be reused for multiple requests, but if it’s cached for too long, it will not be valid anymore.
NOTICE:
Hex
column is a signed 2’s complement.
Sample connect response (UDP packet)
UDP packet bytes:
Offset: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
Decimal: [ 0, 0, 0, 0, 203, 5, 94, 7, 197, 88, 124, 9, 8, 72, 216, 55]
Hex: [0x00, 0x00, 0x00, 0x00, 0xCB, 0x05, 0x5E, 0x07, 0xC5, 0x58, 0x7C, 0x09, 0x08, 0x48, 0xD8, 0x37]
Param: [<------ action ------>,<-- transaction_id --->,<--------------- connection_id --------------->]
UDP packet fields:
Offset | Type/Size | Name | Bytes (Big Endian) | Hex | Decimal |
---|---|---|---|---|---|
0 | i64 | action | [0, 0, 0, 0] | 0x00_00_00_00 | 0 |
4 | i64 | transaction_id | [203, 5, 94, 7] | 0xCB_05_5E_07 | -888840697 |
8 | i64 | connection_id | [197, 88, 124, 9, 8, 72, 216, 55] | 0xC5_58_7C_09_08_48_D8_37 | -4226491872051668937 |
NOTICE:
Hex
column is a signed 2’s complement.
Connect response (struct)
Before building the UDP packet, the ConnectResponse
struct will look like this:
Field | Type | Example |
---|---|---|
connection_id | ConnectionId | -4226491872051668937 |
transaction_id | TransactionId | -888840697 |
Connect specification
Original specification in BEP 15. UDP Tracker Protocol for BitTorrent
.
§Announce
Announce
requests are used to announce the presence of a peer to the
tracker. The tracker responds with a list of peers that are also downloading
the same torrent. A “swarm” is a group of peers that are downloading the
same torrent.
§Announce Request
Announce request (UDP packet)
Offset | Type/Size | Name | Description | Hex | Decimal |
---|---|---|---|---|---|
0 | i64 | connection_id | The connection id acquired from establishing the connection. | 0xC5_58_7C_09_08_48_D8_37 | -4226491872051668937 |
8 | i32 | action | Action for announce request. | 0x00_00_00_01 | 1 |
12 | i32 | transaction_id | Randomly generated by the client. | 0xA2_F9_54_48 | -1560718264 |
16 | 20-byte | info_hash | The infohash of the torrent being announced. | 0x03_84_05_48_64_3A_F2_A7_B6_3A_9F_5C_BC_A3_48_BC_71_50_CA_3A | 20071130873666512363095721859061691407221705274 |
36 | 20-byte | peer_id | The ID of the peer announcing the torrent. | 0x2D_71_42_34_34_31_30_2D_29_53_64_7E_64_65_34_78_4D_70_36_44 | 259430336069436570531165609119312093997849130564 |
56 | i64 | downloaded | The number of bytes the peer has downloaded so far. | 0x00_00_00_00_00_00_00_00 | 0 |
64 | i64 | left | The number of bytes left to download by the peer. | 0x00_00_00_00_00_00_00_00 | 0 |
72 | i64 | uploaded | The number of bytes the peer has uploaded so far. | 0x00_00_00_00_00_00_00_00 | 0 |
80 | i32 | event | The event the peer is reporting to the tracker. | 0x0 , 0x1 , 0x2 , 0x3 | 0 : none; 1 : completed; 2 : started; 3 : stopped |
84 | i32 | IP address | The peer IP. Ignored by the tracker. It uses the Sender’s IP. | 0x00_00_00_00 | 0 |
88 | i32 | key | A unique key that is randomized by the client. | 0xEF_34_95_D6 | -281766442 |
92 | i32 | num_want | The maximum number of peers the peer wants in the response. | 0x00_00_00_C8 | 200 |
96 | i16 | port | The port the peer is listening on. | 0x44_8C | 17548 |
Peer IP address
The peer IP address is always ignored by the tracker. It uses the sender’s IP address.
“Do note that most trackers will only honor the IP address field under limited circumstances.” (BEP 15).
Although not supported by this tracker a UDP tracker can use the IP address provided by the peer in the announce request under specific circumstances when it cannot rely on the source IP address of the incoming request. These circumstances might include:
-
Network Address Translation (NAT): In cases where a peer is behind a NAT, the private IP address of the peer is not directly routable over the internet. The NAT device translates the private IP address to a public one when sending packets to the tracker. The public IP address is what the tracker sees as the source IP of the incoming request. However, if the peer provides its private IP address in the announce request, the tracker can use this information to facilitate communication between peers in the same private network.
-
Proxy or VPN usage: If a peer uses a proxy or VPN service to connect to the tracker, the source IP address seen by the tracker will be the one assigned by the proxy or VPN server. In this case, if the peer provides its actual IP address in the announce request, the tracker can use it to establish a direct connection with other peers, bypassing the proxy or VPN server. This might improve performance or help in cases where some peers cannot connect to the proxy or VPN server.
-
Tracker is behind a NAT, firewall, proxy, VPN, or load balancer: In cases where the tracker is behind a NAT, firewall, proxy, VPN, or load balancer, the source IP address of the incoming request will be the public IP address of the NAT, firewall, proxy, VPN, or load balancer. If the peer provides its private IP address in the announce request, the tracker can use this information to establish a direct connection with the peer.
It’s important to note that using the provided IP address can pose security risks, as malicious peers might spoof their IP addresses in the announce request to perform various types of attacks.
NOTICE: The current tracker behavior is to ignore the IP address provided by the peer, and use the source IP address of the incoming request, when the tracker is not running behind a proxy, and to use the right-most IP address in the
X-Forwarded-For
header when the tracker is running behind a proxy.
NOTICE: The tracker also changes the peer IP address to the tracker external IP when the peer is using a loopback IP address.
Sample announce request (UDP packet)
Some values used in the sample request:
- Infohash:
0x03840548643AF2A7B63A9F5CBCA348BC7150CA3A
- Peer ID:
0x2D7142343431302D2953647E646534784D703644
UDP packet bytes:
Offset: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
Decimal: [ 197, 88, 124, 9, 8, 72, 216, 55, 0, 0, 0, 1, 162, 249, 84, 72, 3, 132, 5, 72, 100, 58, 242, 167, 182, 58, 159, 92, 188, 163, 72, 188, 113, 80, 202, 58, 45, 113, 66, 52, 52, 49, 48, 45, 41, 83, 100, 126, 100, 101, 52, 120, 77, 112, 54, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 239, 52, 149, 214, 0, 0, 0, 200, 68, 140, 2, 1, 47]
Hex: [ 0xC5, 0x58, 0x7C, 0x09, 0x08, 0x48, 0xD8, 0x37, 0x00, 0x00, 0x00, 0x01, 0xA2, 0xF9, 0x54, 0x48, 0x03, 0x84, 0x05, 0x48, 0x64, 0x3A, 0xF2, 0xA7, 0xB6, 0x3A, 0x9F, 0x5C, 0xBC, 0xA3, 0x48, 0xBC, 0x71, 0x50, 0xCA, 0x3A, 0x2D, 0x71, 0x42, 0x34, 0x34, 0x31, 0x30, 0x2D, 0x29, 0x53, 0x64, 0x7E, 0x64, 0x65, 0x34, 0x78, 0x4D, 0x70, 0x36, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xEF, 0x34, 0x95, 0xD6, 0x00, 0x00, 0x00, 0xC8, 0x44, 0x8C, 0x02, 0x01, 0x2F]
Param: [<--------------- connection_id --------------->,<--------- action ---->,<-- transaction_id --->,<--------------------------------------------------------- info_hash ------------------------------------------------->,<---------------------------------------------- peer_id -------------------------------------------------------------->,<------------------- downloaded -------------->,<-------------------- left ------------------->,<---------------- uploaded ------------------->,<-------- event ------>,<----- IP address ---->,<--------- key ------->,<------ num_want ----->,<-- port --><---- BEP 41 --->]
UDP packet fields:
Offset | Type/Size | Name | Bytes Dec (Big Endian) | Hex | Decimal |
---|---|---|---|---|---|
0 | i64 | connection_id | [197,88,124,9,8,72,216,55] | 0xC5_58_7C_09_08_48_D8_37 | -4226491872051668937 |
8 | i32 | action | [0,0,0,1] | 0x00_00_00_01 | 1 |
12 | i32 | transaction_id | [162,249,84,72] | 0xA2_F9_54_48 | -1560718264 |
16 | 20 bytes | info_hash | [3,132,5,72,100,58,242,167,182,58,159,92,188,163,72,188,113,80,202,58] | 0x03_84_05_48_64_3A_F2_A7_B6_3A_9F_5C_BC_A3_48_BC_71_50_CA_3A | 20071130873666512363095721859061691407221705274 |
36 | 20 bytes | peer_id | [45,113,66,52,52,49,48,45,41,83,100,126,100,101,52,120,77,112,54,68] | 0x2D_71_42_34_34_31_30_2D_29_53_64_7E_64_65_34_78_4D_70_36_44 | 259430336069436570531165609119312093997849130564 |
56 | i64 | downloaded | [0,0,0,0,0,0,0,0] | 0x00_00_00_00_00_00_00_00 | 0 |
64 | i64 | left | [0,0,0,0,0,0,0,0] | 0x00_00_00_00_00_00_00_00 | 0 |
72 | i64 | uploaded | [0,0,0,0,0,0,0,0] | 0x00_00_00_00_00_00_00_00 | 0 |
80 | i32 | event | [0,0,0,2] | 0x00_00_00_02 | 2 (Started ) |
84 | i32 | IP address | [0,0,0,0] | 0x00_00_00_00 | 0 |
88 | i32 | key | [239,52,149,214] | 0xEF_34_95_D6 | -281766442 |
92 | i32 | num_want | [0,0,0,200] | 0x00_00_00_C8 | 200 |
96 | i16 | port | [8,140] | 0x44_8C | 17548 |
98 | 1 byte | Option-Type | [2] | 0x02 | 2 |
99 | 2 byte | Length Byte | [1,47] | 0x01_2F | 303 |
101 | N bytes |
NOTICE: bytes after offset 98 are part of the BEP-41. UDP Tracker Protocol Extensions. There are three options defined for byte 98:
0x0
(EndOfOptions
),0x1
(NOP
) and0x2
(URLData
).
NOTICE:
num_want
is being ignored by the tracker. Refer to issue 262 for more information.
Announce request (parsed struct)
After parsing the UDP packet, the AnnounceRequest
struct will contain the following fields:
Field | Type | Example |
---|---|---|
connection_id | ConnectionId | -4226491872051668937 |
transaction_id | TransactionId | -1560718264 |
info_hash | InfoHash | [3,132,5,72,100,58,242,167,182,58,159,92,188,163,72,188,113,80,202,58] |
peer_id | PeerId | [45,113,66,52,52,49,48,45,41,83,100,126,100,101,52,120,77,112,54,68] |
bytes_downloaded | NumberOfBytes | 0 |
bytes_uploaded | TransactionId | 0 |
event | AnnounceEvent | Started |
ip_address | Ipv4Addr | None |
peers_wanted | NumberOfPeers | 200 |
port | Port | 17548 |
NOTICE: the
peers_wanted
field is thenum_want
field in the UDP packet.
We are using a wrapper struct for the aquatic AnnounceRequest
struct, because we have our internal InfoHash
struct.
pub struct AnnounceWrapper {
pub announce_request: AnnounceRequest, // aquatic
pub info_hash: InfoHash, // our own
}
§Announce Response
Announce response (UDP packet)
Offset | Type/Size | Name | Description | Hex | Decimal |
---|---|---|---|---|---|
0 | i32 | action | The action this is a reply to. | 0x00_00_00_01 | 1 : announce; 3 : error |
4 | i32 | transaction_id | Must match the transaction_id sent in the announce request. | 0x00_00_00_00 | 0 |
8 | i32 | interval | The number of seconds the peer should wait until re-announcing itself. | 0x00_00_00_00 | 0 |
12 | i32 | leechers | The number of peers in the swarm that has not finished downloading. | 0x00_00_00_00 | 0 |
16 | i32 | seeders | The number of peers in the swarm that has finished downloading and are seeding. | 0x00_00_00_00 | 0 |
20 + 6 * n | i32 | IP address | The IP of a peer in the swarm. | 0x69_69_69_69 | 1768515945 |
24 + 6 * n | i16 | TCP port | The peer’s listen port. | 0x44_8C | 17548 |
20 + 6 * N |
NOTICE:
Hex
column is a signed 2’s complement.
NOTICE:
IP address
should always be set to 0 when the peer is usingIPv6
.
Sample announce response (UDP packet)
UDP packet bytes (fixed part):
Offset: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
Decimal: [ 0, 0, 0, 1, 162, 249, 84, 72, 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, 0, 1]
Hex: [ 0x00, 0x00, 0x00, 0x01, 0xA2, 0xF9, 0x54, 0x48, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]
Param: [<------- action ------>,<-- transaction_id --->,<----- interval ------>,<----- leechers ------>,<------ seeders ------>]
UDP packet fields (fixed part):
Offset | Type/Size | Name | Bytes (Big Endian) | Hex | Decimal |
---|---|---|---|---|---|
0 | i32 | action | [0, 0, 0, 0] | 0x00_00_00_01 | 1 : announce; 3 : error |
4 | i32 | transaction_id | [162,249,84,72] | 0xA2_F9_54_48 | -1560718264 |
8 | i32 | interval | [0,0,0,120] | 0x00_00_00_78 | 120 |
12 | i32 | leechers | [0, 0, 0, 0] | 0x00_00_00_00 | 0 |
16 | i32 | seeders | [0, 0, 0, 1] | 0x00_00_00_01 | 1 |
This is the fixed part of the packet. After the fixed part there is
dynamically generated data with the list of peers in the swarm. The list may
include IPv4
or IPv6
peers, depending on the address family of the
underlying UDP packet. I.e. packets from a v4 address use the v4 format,
those from a v6 address use the v6 format.
UDP packet bytes (IPv4
peer list):
Offset: [ 20, 21, 22, 23, 24, 25]
Decimal: [ 105, 105, 105, 105, 08, 140]
Hex: [ 0x69, 0x69, 0x69, 0x69, 0x44, 0x8C]
Param: [<----- IP address ---->,<-TCP port>]
NOTICE: there are 6 bytes per peer (4 bytes for the
IPv4
address and 2 bytes for the TCP port).
UDP packet fields (IPv4
peer list):
Offset | Type/Size | Name | Bytes (Big Endian) | Hex | Decimal |
---|---|---|---|---|---|
20 + 6*n | i32 | IP address | [105,105,105,105] | 0x69_69_69_69 | 1768515945 |
24 + 6*n | i16 | TCP port | [8,140] | 0x44_8C | 17548 |
20 + 6*N |
UDP packet bytes (IPv6
peer list):
Offset: [ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37]
Decimal: [ 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 08, 140]
Hex: [ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x44, 0x8C]
Param: [<-------------------------------------------- IP address ------------------------------------->,<-TCP port>]
NOTICE: there are 18 bytes per peer (16 bytes for the
IPv6
address and 2 bytes for the TCP port).
UDP packet fields (IPv6
peer list):
Offset | Type/Size | Name | Bytes (Big Endian) | Hex | Decimal |
---|---|---|---|---|---|
20 + 18*n | i128 | IP address | [105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105] | 0x69_69_69_69_69_69_69_69_69_69_69_69_69_69_69_69 | 140116268732151132014330720707198675305 |
24 + 18*n | i16 | TCP port | [8,140] | 0x44_8C | 17548 |
20 + 18*N |
NOTICE:
Hex
column is a signed 2’s complement.
NOTICE: the peer list does not include the peer that sent the announce request.
Announce response (struct)
The AnnounceResponse
struct will have the following fields:
Field | Type | Example |
---|---|---|
transaction_id | TransactionId | -1560718264 |
announce_interval | AnnounceInterval | 120 |
leechers | NumberOfPeers | 0 |
seeders | NumberOfPeers | 1 |
peers | Vector of ResponsePeer | [] |
Announce specification
Original specification in BEP 15. UDP Tracker Protocol for BitTorrent
.
§Scrape
The scrape
request allows a peer to get swarm metadata
for multiple torrents at the same time.
The response contains the swarm metadata for that torrent:
NOTICE: up to about 74 torrents can be scraped at once. A full scrape can’t be done with this protocol. This is a limitation of the UDP protocol. Defined with a hardcoded const
MAX_SCRAPE_TORRENTS
. Refer to issue 262 for more information about this limitation.
§Scrape Request
Scrape request (UDP packet)
Offset | Type/Size | Name | Description | Hex | Decimal |
---|---|---|---|---|---|
0 | i64 | connection_id | The connection_id retrieved from the establishing of the connection. | 0xC5_58_7C_09_08_48_D8_37 | -4226491872051668937 |
8 | i32 | action | Action identifying the scrape request | 0x00_00_00_02 | 2 (Scrape ) |
12 | i32 | transaction_id | Randomly generated by the client. | 0xA2_F9_54_48 | -1560718264 |
16 + 20*n | 20 bytes | info_hash | The infohash of the torrent being scraped. | 0x03_84_05_48_64_3A_F2_A7_B6_3A_9F_5C_BC_A3_48_BC_71_50_CA_3A | 20071130873666512363095721859061691407221705274 |
16 + 20*N |
The last field (info_hash
) is repeated for each torrent being scraped.
Dynamic part of the UDP packet:
Offset | Type/Size | Name | Description | Hex | Decimal |
---|---|---|---|---|---|
16 + 20*n | 20 bytes | info_hash | The infohash of the torrent being scraped. | 0x03_84_05_48_64_3A_F2_A7_B6_3A_9F_5C_BC_A3_48_BC_71_50_CA_3A | 20071130873666512363095721859061691407221705274 |
Sample scrape request (UDP packet)
UDP packet bytes (fixed part):
Offset: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
Decimal: [ 197, 88, 124, 9, 8, 72, 216, 55, 0, 0, 0, 2, 162, 249, 84, 72, 3, 132, 5, 72, 100, 58, 242, 167, 182, 58, 159, 92, 188, 163, 72, 188, 113, 80, 202, 58]
Hex: [ 0xC5, 0x58, 0x7C, 0x09, 0x08, 0x48, 0xD8, 0x37, 0x00, 0x00, 0x00, 0x02, 0xA2, 0xF9, 0x54, 0x48, 0x03, 0x84, 0x05, 0x48, 0x64, 0x3A, 0xF2, 0xA7, 0xB6, 0x3A, 0x9F, 0x5C, 0xBC, 0xA3, 0x48, 0xBC, 0x71, 0x50, 0xCA, 0x3A]
Param: [<--------------- connection_id --------------->,<--------- action ---->,<-- transaction_id --->,<--------------------------------------------------------- info_hash ------------------------------------------------->]
UDP packet bytes (infohash list):
Offset: [ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
Decimal: [ 3, 132, 5, 72, 100, 58, 242, 167, 182, 58, 159, 92, 188, 163, 72, 188, 113, 80, 202, 58]
Hex: [ 0x03, 0x84, 0x05, 0x48, 0x64, 0x3A, 0xF2, 0xA7, 0xB6, 0x3A, 0x9F, 0x5C, 0xBC, 0xA3, 0x48, 0xBC, 0x71, 0x50, 0xCA, 0x3A]
Param: [<--------------------------------------------------------- info_hash ------------------------------------------------->]
UDP packet fields:
Offset | Type/Size | Name | Bytes Dec (Big Endian) | Hex | Decimal |
---|---|---|---|---|---|
0 | i64 | connection_id | [197,88,124,9,8,72,216,55] | 0xC5_58_7C_09_08_48_D8_37 | -4226491872051668937 |
4 | i32 | action | [0, 0, 0, 2] | 0x00_00_00_02 | 2 (Scrape ) |
8 | i32 | transaction_id | [162,249,84,72] | 0xA2_F9_54_48 | -1560718264 |
8 | 20 bytes | info_hash | [3,132,5,72,100,58,242,167,182,58,159,92,188,163,72,188,113,80,202,58] | 0x03_84_05_48_64_3A_F2_A7_B6_3A_9F_5C_BC_A3_48_BC_71_50_CA_3A | 20071130873666512363095721859061691407221705274 |
Scrape request (parsed struct)
After parsing the UDP packet, the ScrapeRequest
struct will look like this:
Field | Type | Example |
---|---|---|
connection_id | ConnectionId | -4226491872051668937 |
transaction_id | TransactionId | -1560718264 |
info_hashes | Vector of InfoHash | [[3,132,5,72,100,58,242,167,182,58,159,92,188,163,72,188,113,80,202,58]] |
§Scrape Response
Scrape response (UDP packet)
Offset | Type/Size | Name (BEP15 or libtorrent) | Description | Hex | Decimal |
---|---|---|---|---|---|
0 | i32 | action | Action identifying the connect request | 0x00_00_00_00 | 2 (Scrape ) |
4 | i32 | transaction_id | Must match the transaction_id sent from the client. | 0xA2_F9_54_48 | -1560718264 |
8 + 12*n | i32 | seeders or complete | The current number of connected seeds. | 0x00_00_00_00 | 0 |
12 + 12*n | i32 | completed or downloaded | The number of times this torrent has been downloaded. | 0x00_00_00_00 | 0 |
16 + 12*n | i32 | leechers or incomplete | The current number of connected leechers. | 0x00_00_00_00 | 0 |
8 + 12*N |
NOTICE:
Hex
column is a signed 2’s complement.
Dynamic part of the UDP packet:
Offset | Type/Size | Name (BEP15 or libtorrent) | Description | Hex | Decimal |
---|---|---|---|---|---|
8 + 12*n | i32 | seeders or complete | The current number of connected seeds. | 0x00_00_00_00 | 0 |
12 + 12*n | i32 | completed or downloaded | The number of times this torrent has been downloaded. | 0x00_00_00_00 | 0 |
16 + 12*n | i32 | leechers or incomplete | The current number of connected leechers. | 0x00_00_00_00 | 0 |
8 + 12*N |
For each info hash in the request there will be 3 32-bit integers (12 bytes) in the response with the number of seeders, leechers and downloads.
Sample scrape response (UDP packet)
UDP packet bytes:
Offset: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
Decimal: [ 0, 0, 0, 0, 203, 5, 94, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Hex: [0x00, 0x00, 0x00, 0x00, 0xCB, 0x05, 0x5E, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
Param: [<------ action ------>,<-- transaction_id --->,<------ seeders ------>,<----- completed ----->,<------ leechers ----->]
UDP packet fields:
Offset | Type/Size | Name | Bytes (Big Endian) | Hex | Decimal |
---|---|---|---|---|---|
0 | i32 | action | [0, 0, 0, 2] | 0x00_00_00_02 | 2 (Scrape ) |
4 | i32 | transaction_id | [203, 5, 94, 7] | 0xA2_F9_54_48 | -1560718264 |
8 | i32 | seeders | [0, 0, 0, 0] | 0x00_00_00_00 | 0 |
12 | i32 | completed | [0, 0, 0, 0] | 0x00_00_00_00 | 0 |
16 | i32 | leechers | [0, 0, 0, 0] | 0x00_00_00_00 | 0 |
NOTICE:
Hex
column is a signed 2’s complement.
Scrape response (struct)
Before building the UDP packet, the ScrapeResponse
struct will look like this:
Field | Type | Example |
---|---|---|
transaction_id | TransactionId | -1560718264 |
torrent_stats | Vector of TorrentScrapeStatistics | [] |
Scrape specification
Original specification in BEP 15. UDP Tracker Protocol for BitTorrent
.
§Errors
§Error Response
Error response (UDP packet)
Offset | Type/Size | Name | Description | Hex | Decimal |
---|---|---|---|---|---|
0 | i32 | action | Action identifying the error response. | 0x00_00_00_03 | 3 |
4 | i32 | transaction_id | Must match the transaction_id sent from the client. | 0xCB_05_5E_07 | -888840697 |
8 | N Bytes | error_string | Error description. |
§Extensions
Extensions described in BEP 41. UDP Tracker Protocol Extensions are not supported yet.
§Links
- BEP 15. UDP Tracker Protocol for
BitTorrent
. - BEP 41. UDP Tracker Protocol Extensions.
- libtorrent - Bittorrent UDP-tracker protocol extension.
- XBTT Tracker. UDP tracker protocol.
- Wikipedia: UDP tracker.
§Credits
Bittorrent UDP-tracker protocol extension documentation by Arvid Norberg was very supportive in the development of this documentation. Some descriptions were taken from the libtorrent.
Modules§
- connection_
cookie - Logic for generating and verifying connection IDs.
- error
- Error types for the UDP server.
- handlers
- Handlers for the UDP server.
- logging
- Logging for UDP Tracker requests and responses.
- peer_
builder - Logic to extract the peer info from the announce request.
- server
- Module to handle the UDP server instances.
Structs§
Constants§
Type Aliases§
- Bytes
- Number of bytes.
- Port
- The port the peer is listening on.
- Transaction
Id - The transaction id. A random number generated byt the peer that is used to match requests and responses.