/* * tracker.h - Protocol core for BitTorrent trackers. * * This library focuses on tracker-side parsing and response formatting. The * ABI deliberately stays protocol-neutral: HTTP(S) and UDP announce/scrape * inputs normalize into the same request structs, and servers can format * compact IPv4/IPv6 peer responses from one endpoint list. */ #ifndef TORRENT_TRACKER_H #define TORRENT_TRACKER_H #include #include #ifdef __cplusplus extern "C" { #endif #define TRACKER_INFO_HASH_LEN 20u #define TRACKER_PEER_ID_LEN 20u #define TRACKER_MAX_PEERS 256u #define TRACKER_MAX_SCRAPE 74u #define TRACKER_MAX_URL_DATA 512u #define DHT_NODE_ID_LEN 20u #define DHT_MAX_TRANSACTION 16u #define DHT_MAX_TOKEN 64u #define DHT_MAX_NODES 256u #define DHT_MAX_ERROR 128u typedef enum { TRACKER_OK = 0, TRACKER_EINVAL = -1, TRACKER_ETOOSMALL= -2, TRACKER_ETOOBIG = -3, TRACKER_ENOSPC = -4, TRACKER_EPARSE = -5 } tracker_result; typedef enum { TRACKER_EVENT_NONE = 0, TRACKER_EVENT_COMPLETED = 1, TRACKER_EVENT_STARTED = 2, TRACKER_EVENT_STOPPED = 3 } tracker_event; typedef enum { TRACKER_ADDR_IPV4 = 4, TRACKER_ADDR_IPV6 = 6 } tracker_addr_family; typedef struct { uint8_t family; /* TRACKER_ADDR_IPV4 or TRACKER_ADDR_IPV6 */ uint8_t addr[16]; /* first 4 bytes used for IPv4 */ uint16_t port; /* host byte order */ uint8_t peer_id[20]; uint8_t has_peer_id; } tracker_peer; typedef struct { uint8_t info_hash[20]; uint8_t peer_id[20]; uint16_t port; uint64_t uploaded; uint64_t downloaded; uint64_t left; int32_t numwant; /* -1 means default */ uint32_t key; uint32_t ip4; /* host byte order, 0 means use source IP */ tracker_event event; uint8_t compact; uint8_t no_peer_id; uint8_t has_key; uint8_t has_ip4; char ip[64]; /* HTTP ip= value, if supplied */ char tracker_id[128]; char url_data[TRACKER_MAX_URL_DATA]; /* BEP-41 UDP URLData */ } tracker_announce_request; typedef struct { uint32_t interval; uint32_t min_interval; uint32_t complete; /* seeders */ uint32_t incomplete; /* leechers */ const char *tracker_id; const tracker_peer *peers; size_t peer_count; uint8_t compact; /* compact peers/peers6 response */ } tracker_announce_response; typedef struct { uint8_t info_hash[20]; uint32_t complete; uint32_t downloaded; uint32_t incomplete; } tracker_scrape_file; typedef struct { const tracker_scrape_file *files; size_t file_count; } tracker_scrape_response; typedef struct { uint32_t interval; /* announce interval advertised to clients */ uint32_t min_interval; /* optional minimum announce interval */ uint32_t peer_timeout; /* expire peers older than this many seconds */ uint32_t default_numwant; /* used when request numwant is -1 */ uint32_t max_numwant; /* hard cap on returned peers */ uint64_t random_seed; /* 0 => deterministic default seed */ } tracker_store_config; typedef struct tracker_store tracker_store; tracker_store *tracker_store_create(const tracker_store_config *cfg); void tracker_store_destroy(tracker_store *store); /* Apply one announce to the in-memory swarm table and prepare a response. * source_addr is the observed remote address; source_addr->port is ignored and * req->port is advertised. Trackers should prefer this source address over * user-supplied ip= values to avoid reflector abuse. out_peers is caller-owned * response storage and is referenced by resp->peers on success. */ int tracker_store_announce(tracker_store *store, const tracker_announce_request *req, const tracker_peer *source_addr, uint64_t now_sec, tracker_peer *out_peers, size_t out_peer_cap, tracker_announce_response *resp); int tracker_store_scrape(tracker_store *store, const uint8_t hashes[][20], size_t hash_count, tracker_scrape_file *out_files, size_t out_file_cap, tracker_scrape_response *resp); /* Remove peers that have not announced within cfg.peer_timeout seconds. */ size_t tracker_store_prune(tracker_store *store, uint64_t now_sec); size_t tracker_store_swarm_count(const tracker_store *store); size_t tracker_store_peer_count(const tracker_store *store); typedef enum { TRACKER_UDP_CONNECT = 0, TRACKER_UDP_ANNOUNCE = 1, TRACKER_UDP_SCRAPE = 2, TRACKER_UDP_ERROR = 3 } tracker_udp_action; typedef struct { tracker_udp_action action; uint32_t transaction_id; uint64_t connection_id; tracker_announce_request announce; uint8_t scrape_hashes[TRACKER_MAX_SCRAPE][20]; size_t scrape_count; } tracker_udp_request; /* HTTP(S) tracker protocol. Query may be the raw query string or a full path * containing '?'. info_hash and peer_id are percent-decoded and must be 20 * bytes for announce requests. */ int tracker_http_parse_announce_query(const char *query, tracker_announce_request *out); int tracker_http_parse_scrape_query(const char *query, uint8_t hashes[][20], size_t max_hashes, size_t *hash_count); int tracker_http_write_announce_response(const tracker_announce_response *resp, uint8_t *buf, size_t cap, size_t *written); int tracker_http_write_scrape_response(const tracker_scrape_response *resp, uint8_t *buf, size_t cap, size_t *written); int tracker_http_write_failure(const char *message, uint8_t *buf, size_t cap, size_t *written); /* Client-side HTTP helpers. Query writers produce the path query component * without a leading '?'. Response parsers accept a raw bencoded tracker body * and fill caller-owned peer/file arrays. */ int tracker_http_write_announce_query(const tracker_announce_request *req, char *buf, size_t cap, size_t *written); int tracker_http_write_scrape_query(const uint8_t hashes[][20], size_t hash_count, char *buf, size_t cap, size_t *written); int tracker_http_parse_announce_response(const uint8_t *buf, size_t len, tracker_peer *out_peers, size_t out_peer_cap, tracker_announce_response *resp); int tracker_http_parse_scrape_response(const uint8_t *buf, size_t len, tracker_scrape_file *out_files, size_t out_file_cap, tracker_scrape_response *resp); /* UDP tracker protocol (BEP-15 plus BEP-41 URLData parsing on announces). */ int tracker_udp_parse_request(const uint8_t *packet, size_t len, tracker_addr_family source_family, tracker_udp_request *out); int tracker_udp_write_connect_response(uint32_t transaction_id, uint64_t connection_id, uint8_t *buf, size_t cap, size_t *written); int tracker_udp_write_announce_response(uint32_t transaction_id, tracker_addr_family family, const tracker_announce_response *resp, uint8_t *buf, size_t cap, size_t *written); int tracker_udp_write_scrape_response(uint32_t transaction_id, const tracker_scrape_response *resp, uint8_t *buf, size_t cap, size_t *written); int tracker_udp_write_error(uint32_t transaction_id, const char *message, uint8_t *buf, size_t cap, size_t *written); /* Client-side UDP helpers. */ int tracker_udp_write_connect_request(uint32_t transaction_id, uint8_t *buf, size_t cap, size_t *written); int tracker_udp_parse_connect_response(const uint8_t *packet, size_t len, uint32_t transaction_id, uint64_t *connection_id); int tracker_udp_write_announce_request(uint64_t connection_id, uint32_t transaction_id, const tracker_announce_request *req, uint8_t *buf, size_t cap, size_t *written); int tracker_udp_parse_announce_response(const uint8_t *packet, size_t len, uint32_t transaction_id, tracker_addr_family family, tracker_peer *out_peers, size_t out_peer_cap, tracker_announce_response *resp); int tracker_udp_write_scrape_request(uint64_t connection_id, uint32_t transaction_id, const uint8_t hashes[][20], size_t hash_count, uint8_t *buf, size_t cap, size_t *written); int tracker_udp_parse_scrape_response(const uint8_t *packet, size_t len, uint32_t transaction_id, tracker_scrape_file *out_files, size_t out_file_cap, tracker_scrape_response *resp); typedef enum { DHT_MSG_QUERY = 1, DHT_MSG_RESPONSE = 2, DHT_MSG_ERROR = 3 } dht_message_type; typedef enum { DHT_QUERY_NONE = 0, DHT_QUERY_PING = 1, DHT_QUERY_FIND_NODE = 2, DHT_QUERY_GET_PEERS = 3, DHT_QUERY_ANNOUNCE_PEER = 4 } dht_query_type; typedef enum { DHT_ERR_GENERIC = 201, DHT_ERR_SERVER = 202, DHT_ERR_PROTOCOL = 203, DHT_ERR_METHOD_UNKNOWN = 204 } dht_error_code; typedef struct { uint8_t id[20]; uint8_t family; /* TRACKER_ADDR_IPV4 or TRACKER_ADDR_IPV6 */ uint8_t addr[16]; uint16_t port; /* host byte order */ } dht_node; typedef struct { dht_message_type type; dht_query_type query; uint8_t transaction[DHT_MAX_TRANSACTION]; size_t transaction_len; uint8_t id[20]; uint8_t target[20]; uint8_t info_hash[20]; uint16_t port; uint8_t implied_port; uint8_t want_ipv4; uint8_t want_ipv6; uint8_t token[DHT_MAX_TOKEN]; size_t token_len; dht_node nodes[DHT_MAX_NODES]; size_t node_count; tracker_peer peers[TRACKER_MAX_PEERS]; size_t peer_count; int error_code; char error_message[DHT_MAX_ERROR]; } dht_message; int dht_write_ping_query(const uint8_t *tx, size_t tx_len, const uint8_t id[20], uint8_t *buf, size_t cap, size_t *written); int dht_write_find_node_query(const uint8_t *tx, size_t tx_len, const uint8_t id[20], const uint8_t target[20], uint8_t want_ipv4, uint8_t want_ipv6, uint8_t *buf, size_t cap, size_t *written); int dht_write_get_peers_query(const uint8_t *tx, size_t tx_len, const uint8_t id[20], const uint8_t info_hash[20], uint8_t want_ipv4, uint8_t want_ipv6, uint8_t *buf, size_t cap, size_t *written); int dht_write_announce_peer_query(const uint8_t *tx, size_t tx_len, const uint8_t id[20], const uint8_t info_hash[20], uint16_t port, const uint8_t *token, size_t token_len, uint8_t implied_port, uint8_t *buf, size_t cap, size_t *written); int dht_write_ping_response(const uint8_t *tx, size_t tx_len, const uint8_t id[20], uint8_t *buf, size_t cap, size_t *written); int dht_write_nodes_response(const uint8_t *tx, size_t tx_len, const uint8_t id[20], const uint8_t *token, size_t token_len, const dht_node *nodes, size_t node_count, uint8_t *buf, size_t cap, size_t *written); int dht_write_peers_response(const uint8_t *tx, size_t tx_len, const uint8_t id[20], const uint8_t *token, size_t token_len, const tracker_peer *peers, size_t peer_count, uint8_t *buf, size_t cap, size_t *written); int dht_write_error(const uint8_t *tx, size_t tx_len, int code, const char *message, uint8_t *buf, size_t cap, size_t *written); int dht_parse_message(const uint8_t *packet, size_t len, dht_message *out); #ifdef __cplusplus } #endif #endif /* TORRENT_TRACKER_H */