Naut-Tracker/include/tracker.h

338 lines
14 KiB
C

/*
* 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 <stddef.h>
#include <stdint.h>
#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 */