summaryrefslogtreecommitdiff
path: root/ot_udp.c
diff options
context:
space:
mode:
Diffstat (limited to 'ot_udp.c')
-rw-r--r--ot_udp.c287
1 files changed, 0 insertions, 287 deletions
diff --git a/ot_udp.c b/ot_udp.c
deleted file mode 100644
index 00ec566..0000000
--- a/ot_udp.c
+++ /dev/null
@@ -1,287 +0,0 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <arpa/inet.h>
8#include <pthread.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <unistd.h>
13
14/* Opentracker */
15#include "ot_ip.h"
16#include "ot_random.h"
17#include "ot_rijndael.h"
18#include "ot_socket.h"
19#include "ot_stats.h"
20#include "ot_udp.h"
21#include "trackerlogic.h"
22
23/* One of these per worker, each allocated on its own so that growing the
24 array below never moves the one a running worker holds. */
25struct udp_worker {
26 pthread_t thread;
27 int64_t sock;
28 _Atomic int left;
29};
30
31static struct udp_worker **g_workers;
32static size_t g_worker_count;
33
34static uint32_t g_rijndael_round_key[44] = {0};
35static volatile uint32_t g_key_of_the_hour[2] = {0};
36static volatile ot_time g_hour_of_the_key;
37
38
39static void udp_generate_rijndael_round_key(void) {
40 uint32_t key[16];
41 random_bytes(key, sizeof(key));
42 rijndaelKeySetupEnc128(g_rijndael_round_key, (uint8_t *)key);
43
44 g_key_of_the_hour[0] = random_uint32();
45 g_hour_of_the_key = time_now_minutes();
46}
47
48/* Generate current and previous connection id for ip */
49static void udp_make_connectionid(uint32_t connid[2], const ot_ip6 remoteip, int age) {
50 uint32_t plain[4], crypt[4];
51 int i;
52 uint32_t current_key_of_the_hour;
53
54 if (time_now_minutes() - g_hour_of_the_key >= 60) {
55 uint32_t old_key_of_the_hour = g_key_of_the_hour[0];
56 uint32_t new_key_of_the_hour = random_uint32();
57 /* If in the meantime another thread has performed
58 key rotation, do not overwrite their results */
59 if (time_now_minutes() - g_hour_of_the_key >= 60) {
60 /* Upgrade en bloc */
61 g_hour_of_the_key = time_now_minutes();
62 g_key_of_the_hour[0] = new_key_of_the_hour;
63 g_key_of_the_hour[1] = old_key_of_the_hour;
64 }
65 }
66
67 memcpy(plain, remoteip, sizeof(plain));
68 current_key_of_the_hour = g_key_of_the_hour[age];
69 for (i = 0; i < 4; ++i)
70 plain[i] ^= current_key_of_the_hour;
71 rijndaelEncrypt128(g_rijndael_round_key, (uint8_t *)plain, (uint8_t *)crypt);
72 connid[0] = crypt[0] ^ crypt[1];
73 connid[1] = crypt[2] ^ crypt[3];
74}
75
76/* UDP implementation according to http://xbtt.sourceforge.net/udp_tracker_protocol.html */
77int handle_udp6(int64_t serversocket, struct ot_workstruct *ws) {
78 ot_ip6 remoteip;
79 uint32_t *inpacket = (uint32_t *)ws->inbuf;
80 uint32_t *outpacket = (uint32_t *)ws->outbuf;
81 uint32_t left, event;
82 uint32_t connid[2];
83 uint32_t action;
84 uint16_t port, remoteport;
85 size_t byte_count, scrape_count;
86
87 ssize_t result = socket_recv(serversocket, ws->inbuf, G_INBUF_SIZE, remoteip, &remoteport);
88 if (result <= 0)
89 return 0;
90 byte_count = (size_t)result;
91
92 stats_issue_event(ws, EVENT_ACCEPT, FLAG_UDP, (uintptr_t)remoteip);
93 stats_issue_event(ws, EVENT_READ, FLAG_UDP, byte_count);
94
95 /* Minimum udp tracker packet size, also catches error */
96 if (byte_count < 16)
97 return 1;
98
99 /* Get action to take. Ignore error messages and broken packets */
100 action = ntohl(inpacket[2]);
101 if (action > 2)
102 return 1;
103
104 /* Generate the connection id we give out and expect to and from
105 the requesting ip address, this prevents udp spoofing */
106 udp_make_connectionid(connid, remoteip, 0);
107
108 /* Initialise hash pointer */
109 ws->hash = NULL;
110 ws->peer_id = NULL;
111
112 /* If action is not 0 (connect), then we expect the derived
113 connection id in first 64 bit */
114 if ((action > 0) && (inpacket[0] != connid[0] || inpacket[1] != connid[1])) {
115 /* If connection id does not match, try the one that was
116 valid in the previous hour. Only if this also does not
117 match, return an error packet */
118 udp_make_connectionid(connid, remoteip, 1);
119 if (inpacket[0] != connid[0] || inpacket[1] != connid[1]) {
120 const char err_idmiss[] = "Connection ID missmatch.";
121 size_t s = sizeof(err_idmiss);
122 outpacket[0] = htonl(3);
123 outpacket[1] = inpacket[3];
124 memcpy(&outpacket[2], &err_idmiss[0], s);
125 socket_send(serversocket, ws->outbuf, 8 + s, remoteip, remoteport);
126 stats_issue_event(ws, EVENT_CONNID_MISSMATCH, FLAG_UDP, 8 + s);
127 return 1;
128 }
129 }
130
131 switch (action) {
132 case 0: /* This is a connect action */
133 /* look for udp bittorrent magic id */
134 if ((ntohl(inpacket[0]) != 0x00000417) || (ntohl(inpacket[1]) != 0x27101980))
135 return 1;
136
137 outpacket[0] = 0;
138 outpacket[1] = inpacket[3];
139 outpacket[2] = connid[0];
140 outpacket[3] = connid[1];
141
142 socket_send(serversocket, ws->outbuf, 16, remoteip, remoteport);
143 stats_issue_event(ws, EVENT_CONNECT, FLAG_UDP, 16);
144 break;
145 case 1: /* This is an announce action */
146 /* Minimum udp announce packet size */
147 if (byte_count < 98)
148 return 1;
149
150 /* We do only want to know, if it is zero */
151 left = inpacket[64 / 4] | inpacket[68 / 4];
152
153 event = ntohl(inpacket[80 / 4]);
154 port = *(uint16_t *)(((char *)inpacket) + 96);
155 ws->hash = (ot_hash *)(((char *)inpacket) + 16);
156
157 OT_SETIP(ws->peer, remoteip);
158 OT_SETPORT(ws->peer, &port);
159 OT_PEERFLAG(ws->peer) = 0;
160
161 switch (event) {
162 case 1:
163 OT_PEERFLAG(ws->peer) |= PEER_FLAG_COMPLETED;
164 break;
165 case 3:
166 OT_PEERFLAG(ws->peer) |= PEER_FLAG_STOPPED;
167 break;
168 default:
169 break;
170 }
171
172 if (!left)
173 OT_PEERFLAG(ws->peer) |= PEER_FLAG_SEEDING;
174
175 outpacket[0] = htonl(1); /* announce action */
176 outpacket[1] = inpacket[12 / 4];
177
178 if (OT_PEERFLAG(ws->peer) & PEER_FLAG_STOPPED) { /* Peer is gone. */
179 ws->reply = ws->outbuf;
180 ws->reply_size = remove_peer_from_torrent(FLAG_UDP, ws);
181 } else {
182 /* Limit amount of peers to OT_MAX_PEERS_UDP */
183 uint32_t numwant = ntohl(inpacket[92 / 4]);
184 size_t max_peers = ip_is_v4mapped(remoteip) ? OT_MAX_PEERS_UDP4 : OT_MAX_PEERS_UDP6;
185 if (numwant > max_peers)
186 numwant = max_peers;
187
188 ws->reply = ws->outbuf + 8;
189 ws->reply_size = add_peer_to_torrent_and_return_peers(FLAG_UDP, ws, numwant);
190
191 if (!ws->reply_size) {
192 const char err_unauth[] = "Requested download is not authorized for use with this tracker.";
193 size_t s = sizeof(err_unauth);
194 outpacket[0] = htonl(3);
195 memcpy(&outpacket[2], &err_unauth[0], s);
196 socket_send(serversocket, ws->outbuf, 8 + s, remoteip, remoteport);
197 return 1;
198 }
199 ws->reply_size += 8;
200 }
201
202 socket_send(serversocket, ws->outbuf, ws->reply_size, remoteip, remoteport);
203 stats_issue_event(ws, EVENT_ANNOUNCE, FLAG_UDP, ws->reply_size);
204 break;
205
206 case 2: /* This is a scrape action */
207 outpacket[0] = htonl(2); /* scrape action */
208 outpacket[1] = inpacket[12 / 4];
209
210 for (scrape_count = 0; (scrape_count * 20 < byte_count - 16) && (scrape_count <= 74); scrape_count++)
211 return_udp_scrape_for_torrent(*(ot_hash *)(((char *)inpacket) + 16 + 20 * scrape_count), ((char *)outpacket) + 8 + 12 * scrape_count);
212
213 socket_send(serversocket, ws->outbuf, 8 + 12 * scrape_count, remoteip, remoteport);
214 stats_issue_event(ws, EVENT_SCRAPE, FLAG_UDP, scrape_count);
215 break;
216 }
217 return 1;
218}
219
220static void *udp_worker(void *args) {
221 struct udp_worker *me = args;
222 struct ot_workstruct ws;
223
224 if (workstruct_init(&ws) == -1) {
225 fprintf(stderr, "Warning: a udp worker could not get its buffers and will not run.\n");
226 atomic_store(&me->left, 1);
227 return NULL;
228 }
229
230 while (g_opentracker_running)
231 handle_udp6(me->sock, &ws);
232
233 workstruct_deinit(&ws);
234 atomic_store(&me->left, 1);
235 return NULL;
236}
237
238void udp_init(void) {
239 if (!g_rijndael_round_key[0])
240 udp_generate_rijndael_round_key();
241}
242
243void udp_worker_install(int64_t sock) {
244 struct udp_worker **grown = realloc(g_workers, (g_worker_count + 1) * sizeof(*g_workers));
245 struct udp_worker *worker;
246
247 if (!grown || !(worker = calloc(1, sizeof(*worker)))) {
248 fprintf(stderr, "Warning: out of memory installing a udp worker, this socket stays with the workers already running.\n");
249 g_workers = grown ? grown : g_workers;
250 return;
251 }
252 g_workers = grown;
253 worker->sock = sock;
254
255#ifdef _DEBUG
256 fprintf(stderr, "Installing a worker on udp socket %ld\n", (unsigned long)sock);
257#endif
258 socket_set_blocking((int)sock, 1);
259
260 if (pthread_create(&worker->thread, NULL, udp_worker, worker))
261 free(worker);
262 else
263 g_workers[g_worker_count++] = worker;
264}
265
266void udp_workers_deinit(void) {
267 size_t i;
268
269 for (i = 0; i < g_worker_count; ++i) {
270 struct udp_worker *worker = g_workers[i];
271
272 /* Repeated because a signal only interrupts a read already under way: a
273 worker that was between two reads when the first one arrived is inside
274 the next one by now. */
275 while (!atomic_load(&worker->left)) {
276 pthread_kill(worker->thread, OT_SIGNAL_WAKE);
277 usleep(1000);
278 }
279 pthread_join(worker->thread, NULL);
280 free(worker);
281 }
282
283 free(g_workers);
284 g_workers = NULL;
285 g_worker_count = 0;
286}
287