From ad541c67761b8aa6da1566802c347cac4018d0c2 Mon Sep 17 00:00:00 2001 From: Dirk Engling Date: Mon, 2 Nov 2026 20:50:24 +0100 Subject: Move the build sources into src/ --- src/ot_udp.c | 226 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 src/ot_udp.c (limited to 'src/ot_udp.c') diff --git a/src/ot_udp.c b/src/ot_udp.c new file mode 100644 index 0000000..644b71d --- /dev/null +++ b/src/ot_udp.c @@ -0,0 +1,226 @@ +/* This software was written by Dirk Engling + It is considered beerware. Prost. Skol. Cheers or whatever. */ + +/* System */ +#include +#include +#include +#include +#include +#include + +/* Opentracker */ +#include "ot_ip.h" +#include "ot_random.h" +#include "ot_rijndael.h" +#include "ot_socket.h" +#include "ot_worker.h" +#include "ot_stats.h" +#include "ot_udp.h" +#include "trackerlogic.h" + +static uint32_t g_rijndael_round_key[44] = {0}; +static volatile uint32_t g_key_of_the_hour[2] = {0}; +static volatile ot_time g_hour_of_the_key; + + +static void udp_generate_rijndael_round_key(void) { + uint32_t key[16]; + random_bytes(key, sizeof(key)); + rijndaelKeySetupEnc128(g_rijndael_round_key, (uint8_t *)key); + + g_key_of_the_hour[0] = random_uint32(); + g_hour_of_the_key = time_now_minutes(); +} + +/* Generate current and previous connection id for ip */ +static void udp_make_connectionid(uint32_t connid[2], const ot_ip6 remoteip, int age) { + uint32_t plain[4], crypt[4]; + int i; + uint32_t current_key_of_the_hour; + + if (time_now_minutes() - g_hour_of_the_key >= 60) { + uint32_t old_key_of_the_hour = g_key_of_the_hour[0]; + uint32_t new_key_of_the_hour = random_uint32(); + /* If in the meantime another thread has performed + key rotation, do not overwrite their results */ + if (time_now_minutes() - g_hour_of_the_key >= 60) { + /* Upgrade en bloc */ + g_hour_of_the_key = time_now_minutes(); + g_key_of_the_hour[0] = new_key_of_the_hour; + g_key_of_the_hour[1] = old_key_of_the_hour; + } + } + + memcpy(plain, remoteip, sizeof(plain)); + current_key_of_the_hour = g_key_of_the_hour[age]; + for (i = 0; i < 4; ++i) + plain[i] ^= current_key_of_the_hour; + rijndaelEncrypt128(g_rijndael_round_key, (uint8_t *)plain, (uint8_t *)crypt); + connid[0] = crypt[0] ^ crypt[1]; + connid[1] = crypt[2] ^ crypt[3]; +} + +/* UDP implementation according to http://xbtt.sourceforge.net/udp_tracker_protocol.html */ +int handle_udp6(int64_t serversocket, struct ot_workstruct *ws) { + ot_ip6 remoteip; + uint32_t *inpacket = (uint32_t *)ws->inbuf; + uint32_t *outpacket = (uint32_t *)ws->outbuf; + uint32_t left, event; + uint32_t connid[2]; + uint32_t action; + uint16_t port, remoteport; + size_t byte_count, scrape_count; + + ssize_t result = socket_recv(serversocket, ws->inbuf, G_INBUF_SIZE, remoteip, &remoteport); + if (result <= 0) + return 0; + byte_count = (size_t)result; + + stats_issue_event(ws, EVENT_ACCEPT, FLAG_UDP, (uintptr_t)remoteip); + stats_issue_event(ws, EVENT_READ, FLAG_UDP, byte_count); + + /* Minimum udp tracker packet size, also catches error */ + if (byte_count < 16) + return 1; + + /* Get action to take. Ignore error messages and broken packets */ + action = ntohl(inpacket[2]); + if (action > 2) + return 1; + + /* Generate the connection id we give out and expect to and from + the requesting ip address, this prevents udp spoofing */ + udp_make_connectionid(connid, remoteip, 0); + + /* Initialise hash pointer */ + ws->hash = NULL; + ws->peer_id = NULL; + + /* If action is not 0 (connect), then we expect the derived + connection id in first 64 bit */ + if ((action > 0) && (inpacket[0] != connid[0] || inpacket[1] != connid[1])) { + /* If connection id does not match, try the one that was + valid in the previous hour. Only if this also does not + match, return an error packet */ + udp_make_connectionid(connid, remoteip, 1); + if (inpacket[0] != connid[0] || inpacket[1] != connid[1]) { + const char err_idmiss[] = "Connection ID missmatch."; + size_t s = sizeof(err_idmiss); + outpacket[0] = htonl(3); + outpacket[1] = inpacket[3]; + memcpy(&outpacket[2], &err_idmiss[0], s); + socket_send(serversocket, ws->outbuf, 8 + s, remoteip, remoteport); + stats_issue_event(ws, EVENT_CONNID_MISSMATCH, FLAG_UDP, 8 + s); + return 1; + } + } + + switch (action) { + case 0: /* This is a connect action */ + /* look for udp bittorrent magic id */ + if ((ntohl(inpacket[0]) != 0x00000417) || (ntohl(inpacket[1]) != 0x27101980)) + return 1; + + outpacket[0] = 0; + outpacket[1] = inpacket[3]; + outpacket[2] = connid[0]; + outpacket[3] = connid[1]; + + socket_send(serversocket, ws->outbuf, 16, remoteip, remoteport); + stats_issue_event(ws, EVENT_CONNECT, FLAG_UDP, 16); + break; + case 1: /* This is an announce action */ + /* Minimum udp announce packet size */ + if (byte_count < 98) + return 1; + + /* We do only want to know, if it is zero */ + left = inpacket[64 / 4] | inpacket[68 / 4]; + + event = ntohl(inpacket[80 / 4]); + port = *(uint16_t *)(((char *)inpacket) + 96); + ws->hash = (ot_hash *)(((char *)inpacket) + 16); + + OT_SETIP(ws->peer, remoteip); + OT_SETPORT(ws->peer, &port); + OT_PEERFLAG(ws->peer) = 0; + + switch (event) { + case 1: + OT_PEERFLAG(ws->peer) |= PEER_FLAG_COMPLETED; + break; + case 3: + OT_PEERFLAG(ws->peer) |= PEER_FLAG_STOPPED; + break; + default: + break; + } + + if (!left) + OT_PEERFLAG(ws->peer) |= PEER_FLAG_SEEDING; + + outpacket[0] = htonl(1); /* announce action */ + outpacket[1] = inpacket[12 / 4]; + + if (OT_PEERFLAG(ws->peer) & PEER_FLAG_STOPPED) { /* Peer is gone. */ + ws->reply = ws->outbuf; + ws->reply_size = remove_peer_from_torrent(FLAG_UDP, ws); + } else { + /* Limit amount of peers to OT_MAX_PEERS_UDP */ + uint32_t numwant = ntohl(inpacket[92 / 4]); + size_t max_peers = ip_is_v4mapped(remoteip) ? OT_MAX_PEERS_UDP4 : OT_MAX_PEERS_UDP6; + if (numwant > max_peers) + numwant = max_peers; + + ws->reply = ws->outbuf + 8; + ws->reply_size = add_peer_to_torrent_and_return_peers(FLAG_UDP, ws, numwant); + + if (!ws->reply_size) { + const char err_unauth[] = "Requested download is not authorized for use with this tracker."; + size_t s = sizeof(err_unauth); + outpacket[0] = htonl(3); + memcpy(&outpacket[2], &err_unauth[0], s); + socket_send(serversocket, ws->outbuf, 8 + s, remoteip, remoteport); + return 1; + } + ws->reply_size += 8; + } + + socket_send(serversocket, ws->outbuf, ws->reply_size, remoteip, remoteport); + stats_issue_event(ws, EVENT_ANNOUNCE, FLAG_UDP, ws->reply_size); + break; + + case 2: /* This is a scrape action */ + outpacket[0] = htonl(2); /* scrape action */ + outpacket[1] = inpacket[12 / 4]; + + for (scrape_count = 0; (scrape_count * 20 < byte_count - 16) && (scrape_count <= 74); scrape_count++) + return_udp_scrape_for_torrent(*(ot_hash *)(((char *)inpacket) + 16 + 20 * scrape_count), ((char *)outpacket) + 8 + 12 * scrape_count); + + socket_send(serversocket, ws->outbuf, 8 + 12 * scrape_count, remoteip, remoteport); + stats_issue_event(ws, EVENT_SCRAPE, FLAG_UDP, scrape_count); + break; + } + return 1; +} + +static void udp_run(struct ot_worker *me) { + while (g_opentracker_running) + handle_udp6(me->sock, &me->ws); +} + +void udp_init(void) { + if (!g_rijndael_round_key[0]) + udp_generate_rijndael_round_key(); +} + +void udp_worker_install(int64_t sock) { +#ifdef _DEBUG + fprintf(stderr, "Installing a worker on udp socket %ld\n", (unsigned long)sock); +#endif + socket_set_blocking((int)sock, 1); + + if (worker_start(udp_run, sock) == -1) + fprintf(stderr, "Warning: could not install a udp worker, this socket stays with the workers already running.\n"); +} -- cgit v1.3