summaryrefslogtreecommitdiff
path: root/src/trackerlogic.c
diff options
context:
space:
mode:
authorDirk Engling <erdgeist@erdgeist.org>2026-11-02 20:50:24 +0100
committerDirk Engling <erdgeist@erdgeist.org>2026-11-02 21:14:02 +0100
commitad541c67761b8aa6da1566802c347cac4018d0c2 (patch)
treea9166fa9d349911d69a6948e53b05344dcc348a0 /src/trackerlogic.c
parentc679ff8a3b1418a5960cc49ba6458a0007df54fa (diff)
Move the build sources into src/
Diffstat (limited to 'src/trackerlogic.c')
-rw-r--r--src/trackerlogic.c628
1 files changed, 628 insertions, 0 deletions
diff --git a/src/trackerlogic.c b/src/trackerlogic.c
new file mode 100644
index 0000000..9565090
--- /dev/null
+++ b/src/trackerlogic.c
@@ -0,0 +1,628 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever. */
3
4/* System */
5#include <arpa/inet.h>
6#include <stdint.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10
11
12/* Opentracker */
13#include "ot_accesslist.h"
14#include "ot_clean.h"
15#include "ot_fmt.h"
16#include "ot_fullscrape.h"
17#include "ot_http.h"
18#include "ot_ip.h"
19#include "ot_livesync.h"
20#include "ot_mutex.h"
21#include "ot_workqueue.h"
22#include "ot_random.h"
23#include "ot_stats.h"
24#include "ot_udp.h"
25#include "ot_vector.h"
26#include "trackerlogic.h"
27
28/* Forward declaration */
29size_t return_peers_for_torrent(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto);
30
31void free_peerlist(ot_peerlist *peer_list) {
32 if (peer_list && peer_list->peers.data) {
33 if (OT_PEERLIST_HASBUCKETS(peer_list))
34 vector_clean_list((ot_vector *)peer_list->peers.data, peer_list->peers.size);
35 else
36 free(peer_list->peers.data);
37 }
38 free(peer_list);
39}
40
41void add_torrent_from_saved_state(ot_hash const hash, ot_time base, size_t down_count) {
42 int exactmatch;
43 ot_torrent *torrent;
44 ot_vector *torrents_list = mutex_bucket_lock_by_hash(hash);
45
46 if (!accesslist_hashisvalid(hash)) {
47 mutex_bucket_unlock_by_hash(hash, 0);
48 return;
49 }
50
51 torrent = vector_find_or_insert(torrents_list, (void *)hash, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch);
52 if (!torrent || exactmatch) {
53 mutex_bucket_unlock_by_hash(hash, 0);
54 return;
55 }
56
57 /* Create a new torrent entry, then */
58 memset(torrent, 0, sizeof(ot_torrent));
59 memcpy(torrent->hash, hash, sizeof(ot_hash));
60
61 if (!(torrent->peer_list6 = malloc(sizeof(ot_peerlist))) || !(torrent->peer_list4 = malloc(sizeof(ot_peerlist)))) {
62 vector_remove_torrent(torrents_list, torrent);
63 mutex_bucket_unlock_by_hash(hash, 0);
64 return;
65 }
66
67 memset(torrent->peer_list6, 0, sizeof(ot_peerlist));
68 memset(torrent->peer_list4, 0, sizeof(ot_peerlist));
69 torrent->peer_list6->base = base;
70 torrent->peer_list4->base = base;
71 torrent->peer_list6->down_count = down_count;
72 torrent->peer_list4->down_count = down_count;
73
74 mutex_bucket_unlock_by_hash(hash, 1);
75}
76
77size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstruct *ws, size_t amount) {
78 int exactmatch, delta_torrentcount = 0;
79 ot_torrent *torrent;
80 ot_peer *peer_dest;
81 ot_vector *torrents_list = mutex_bucket_lock_by_hash(*ws->hash);
82 ot_peerlist *peer_list;
83 size_t peer_size; /* initialized in next line */
84 ot_peer const *peer_src = peer_from_peer6(&ws->peer, &peer_size);
85
86 if (!accesslist_hashisvalid(*ws->hash)) {
87 mutex_bucket_unlock_by_hash(*ws->hash, 0);
88 if (proto == FLAG_TCP) {
89 const char invalid_hash[] = "d14:failure reason63:Requested download is not authorized for use with this tracker.e";
90 memcpy(ws->reply, invalid_hash, strlen(invalid_hash));
91 return strlen(invalid_hash);
92 }
93 return 0;
94 }
95
96 torrent = vector_find_or_insert(torrents_list, (void *)ws->hash, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch);
97 if (!torrent) {
98 mutex_bucket_unlock_by_hash(*ws->hash, 0);
99 return 0;
100 }
101
102 if (!exactmatch) {
103 /* Create a new torrent entry, then */
104 memset(torrent, 0, sizeof(ot_torrent));
105 memcpy(torrent->hash, *ws->hash, sizeof(ot_hash));
106
107 if (!(torrent->peer_list6 = malloc(sizeof(ot_peerlist))) || !(torrent->peer_list4 = malloc(sizeof(ot_peerlist)))) {
108 vector_remove_torrent(torrents_list, torrent);
109 mutex_bucket_unlock_by_hash(*ws->hash, 0);
110 return 0;
111 }
112
113 memset(torrent->peer_list6, 0, sizeof(ot_peerlist));
114 memset(torrent->peer_list4, 0, sizeof(ot_peerlist));
115 delta_torrentcount = 1;
116 } else
117 clean_single_torrent(torrent);
118
119 torrent->peer_list6->base = time_now_minutes();
120 torrent->peer_list4->base = time_now_minutes();
121
122 peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4;
123
124 /* Check for peer in torrent */
125 peer_dest = vector_find_or_insert_peer(&(peer_list->peers), peer_src, peer_size, &exactmatch);
126 if (!peer_dest) {
127 mutex_bucket_unlock_by_hash(*ws->hash, delta_torrentcount);
128 return 0;
129 }
130
131 /* Tell peer that it's fresh */
132 OT_PEERTIME(ws->peer, OT_PEER_SIZE6) = 0;
133
134 /* Sanitize flags: Whoever claims to have completed download, must be a seeder */
135 if ((OT_PEERFLAG(ws->peer) & (PEER_FLAG_COMPLETED | PEER_FLAG_SEEDING)) == PEER_FLAG_COMPLETED)
136 OT_PEERFLAG(ws->peer) ^= PEER_FLAG_COMPLETED;
137
138 /* If we hadn't had a match create peer there */
139 if (!exactmatch) {
140
141#ifdef WANT_SYNC_LIVE
142 if (proto == FLAG_MCA)
143 OT_PEERFLAG(ws->peer) |= PEER_FLAG_FROM_SYNC;
144 else
145 livesync_tell(ws);
146#endif
147
148 peer_list->peer_count++;
149 if (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED) {
150 peer_list->down_count++;
151 stats_issue_event(ws, EVENT_COMPLETED, 0, 0);
152 }
153 if (OT_PEERFLAG(ws->peer) & PEER_FLAG_SEEDING)
154 peer_list->seed_count++;
155
156 } else {
157 stats_issue_event(ws, EVENT_RENEW, 0, OT_PEERTIME(peer_dest, peer_size));
158#ifdef WANT_SPOT_WOODPECKER
159 if ((OT_PEERTIME(peer_dest, peer_size) > 0) && (OT_PEERTIME(peer_dest, peer_size) < 20))
160 stats_issue_event(ws, EVENT_WOODPECKER, 0, (uintptr_t)&ws->peer);
161#endif
162#ifdef WANT_SYNC_LIVE
163 /* Won't live sync peers that come back too fast. Only exception:
164 fresh "completed" reports */
165 if (proto != FLAG_MCA) {
166 if (OT_PEERTIME(peer_dest, peer_size) > OT_CLIENT_SYNC_RENEW_BOUNDARY ||
167 (!(OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED) && (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED)))
168 livesync_tell(ws);
169 }
170#endif
171
172 if ((OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_SEEDING) && !(OT_PEERFLAG(ws->peer) & PEER_FLAG_SEEDING))
173 peer_list->seed_count--;
174 if (!(OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_SEEDING) && (OT_PEERFLAG(ws->peer) & PEER_FLAG_SEEDING))
175 peer_list->seed_count++;
176 if (!(OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED) && (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED)) {
177 peer_list->down_count++;
178 stats_issue_event(ws, EVENT_COMPLETED, 0, 0);
179 }
180 if (OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED)
181 OT_PEERFLAG(ws->peer) |= PEER_FLAG_COMPLETED;
182 }
183
184 memcpy(peer_dest, peer_src, peer_size);
185#ifdef WANT_SYNC
186 if (proto == FLAG_MCA) {
187 mutex_bucket_unlock_by_hash(*ws->hash, delta_torrentcount);
188 return 0;
189 }
190#endif
191
192 ws->reply_size = return_peers_for_torrent(ws, torrent, amount, ws->reply, proto);
193 mutex_bucket_unlock_by_hash(*ws->hash, delta_torrentcount);
194 return ws->reply_size;
195}
196
197static size_t return_peers_all(ot_peerlist *peer_list, size_t peer_size, char *reply) {
198 size_t bucket, num_buckets = 1;
199 ot_vector *bucket_list = &peer_list->peers;
200 size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
201 size_t result = compare_size * peer_list->peer_count;
202 char *r_end = reply + result;
203
204 if (OT_PEERLIST_HASBUCKETS(peer_list)) {
205 num_buckets = bucket_list->size;
206 bucket_list = (ot_vector *)bucket_list->data;
207 }
208
209 for (bucket = 0; bucket < num_buckets; ++bucket) {
210 ot_peer *peers = bucket_list[bucket].data;
211 size_t peer_count = bucket_list[bucket].size;
212 while (peer_count--) {
213 if (OT_PEERFLAG_D(peers, peer_size) & PEER_FLAG_SEEDING) {
214 r_end -= compare_size;
215 memcpy(r_end, peers, compare_size);
216 } else {
217 memcpy(reply, peers, compare_size);
218 reply += compare_size;
219 }
220 peers += peer_size;
221 }
222 }
223 return result;
224}
225
226static size_t return_peers_selection(struct ot_workstruct *ws, ot_peerlist *peer_list, size_t peer_size, size_t amount, char *reply) {
227 size_t bucket_offset, bucket_index = 0, num_buckets = 1;
228 ot_vector *bucket_list = &peer_list->peers;
229 unsigned int shifted_pc = peer_list->peer_count;
230 unsigned int shifted_step = 0;
231 unsigned int shift = 0;
232 size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
233 size_t result = compare_size * amount;
234 char *r_end = reply + result;
235
236 if (OT_PEERLIST_HASBUCKETS(peer_list)) {
237 num_buckets = bucket_list->size;
238 bucket_list = (ot_vector *)bucket_list->data;
239 }
240
241 /* Make fixpoint arithmetic as exact as possible */
242#define MAXPRECBIT (1 << (8 * sizeof(int) - 3))
243 while (!(shifted_pc & MAXPRECBIT)) {
244 shifted_pc <<= 1;
245 shift++;
246 }
247 shifted_step = shifted_pc / amount;
248#undef MAXPRECBIT
249
250 /* Initialize somewhere in the middle of peers so that
251 fixpoint's aliasing doesn't alway miss the same peers */
252 bucket_offset = nrand48(ws->rand48_state) % peer_list->peer_count;
253
254 while (amount--) {
255 ot_peer *peer;
256
257 /* This is the aliased, non shifted range, next value may fall into */
258 unsigned int diff = (((amount + 1) * shifted_step) >> shift) - ((amount * shifted_step) >> shift);
259 bucket_offset += 1 + nrand48(ws->rand48_state) % diff;
260
261 while (bucket_offset >= bucket_list[bucket_index].size) {
262 bucket_offset -= bucket_list[bucket_index].size;
263 bucket_index = (bucket_index + 1) % num_buckets;
264 }
265 peer = (ot_peer*)bucket_list[bucket_index].data + peer_size * bucket_offset;
266 if (OT_PEERFLAG_D(peer, peer_size) & PEER_FLAG_SEEDING) {
267 r_end -= compare_size;
268 memcpy(r_end, peer, compare_size);
269 } else {
270 memcpy(reply, peer, compare_size);
271 reply += compare_size;
272 }
273 }
274 return result;
275}
276
277static size_t return_peers_for_torrent_udp(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply) {
278 char *r = reply;
279 size_t peer_size = peer_size_from_peer6(&ws->peer);
280 ot_peerlist *peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4;
281 size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count;
282 size_t seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count;
283
284 if (amount > peer_list->peer_count)
285 amount = peer_list->peer_count;
286
287 *(uint32_t *)(r + 0) = htonl(OT_CLIENT_REQUEST_INTERVAL_RANDOM);
288 *(uint32_t *)(r + 4) = htonl(peer_count - seed_count);
289 *(uint32_t *)(r + 8) = htonl(seed_count);
290 r += 12;
291
292 if (amount) {
293 if (amount == peer_list->peer_count)
294 r += return_peers_all(peer_list, peer_size, r);
295 else
296 r += return_peers_selection(ws, peer_list, peer_size, amount, r);
297 }
298 return r - reply;
299}
300
301static size_t return_peers_for_torrent_tcp(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply) {
302 char *r = reply;
303 const char *r_end = ws->outbuf + G_OUTBUF_SIZE;
304 int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM;
305 size_t seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count;
306 size_t down_count = torrent->peer_list6->down_count + torrent->peer_list4->down_count;
307 size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count - seed_count;
308
309 /* Simple case: amount of peers in both lists is less than requested, here we return all results */
310 size_t amount_v4 = torrent->peer_list4->peer_count;
311 size_t amount_v6 = torrent->peer_list6->peer_count;
312
313 /* Complex case: both lists have more than enough entries and we need to split between v4 and v6 clients */
314 if (amount_v4 + amount_v6 > amount) {
315 size_t amount_left, percent_v6 = 0, percent_v4 = 0, left_v6, left_v4;
316 const size_t SCALE = 1024;
317
318 /* If possible, fill at least a quarter of peer from each family */
319 if (amount / 4 <= amount_v4)
320 amount_v4 = amount / 4;
321 if (amount / 4 <= amount_v6)
322 amount_v6 = amount / 4;
323
324 /* Fill the rest according to which family's pool provides more peers */
325 amount_left = amount - (amount_v4 + amount_v6);
326
327 left_v4 = torrent->peer_list4->peer_count - amount_v4;
328 left_v6 = torrent->peer_list6->peer_count - amount_v6;
329
330 if (left_v4 + left_v6) {
331 percent_v4 = (SCALE * left_v4) / (left_v4 + left_v6);
332 percent_v6 = (SCALE * left_v6) / (left_v4 + left_v6);
333 }
334
335 amount_v4 += (amount_left * percent_v4) / SCALE;
336 amount_v6 += (amount_left * percent_v6) / SCALE;
337
338 /* Integer division rounding can leave out a peer */
339 if (amount_v4 + amount_v6 < amount && amount_v6 < torrent->peer_list6->peer_count)
340 ++amount_v6;
341 if (amount_v4 + amount_v6 < amount && amount_v4 < torrent->peer_list4->peer_count)
342 ++amount_v4;
343 }
344
345 fmt_append(&r, r_end, "d8:completei%zde10:downloadedi%zde10:incompletei%zde8:intervali%ie12:min intervali%ie", seed_count, down_count, peer_count, erval,
346 erval / 2);
347
348 if (amount_v4) {
349 fmt_append(&r, r_end, PEERS_BENCODED4 "%zd:", OT_PEER_COMPARE_SIZE4 * amount_v4);
350 if (amount_v4 == torrent->peer_list4->peer_count)
351 r += return_peers_all(torrent->peer_list4, OT_PEER_SIZE4, r);
352 else
353 r += return_peers_selection(ws, torrent->peer_list4, OT_PEER_SIZE4, amount_v4, r);
354 }
355
356 if (amount_v6) {
357 fmt_append(&r, r_end, PEERS_BENCODED6 "%zd:", OT_PEER_COMPARE_SIZE6 * amount_v6);
358 if (amount_v6 == torrent->peer_list6->peer_count)
359 r += return_peers_all(torrent->peer_list6, OT_PEER_SIZE6, r);
360 else
361 r += return_peers_selection(ws, torrent->peer_list6, OT_PEER_SIZE6, amount_v6, r);
362 }
363
364 fmt_append(&r, r_end, "e");
365
366 return r - reply;
367}
368
369/* Compiles a list of random peers for a torrent
370 * Reply must have enough space to hold:
371 * 92 + 6 * amount bytes for TCP/IPv4
372 * 92 + 18 * amount bytes for TCP/IPv6
373 * 12 + 6 * amount bytes for UDP/IPv4
374 * 12 + 18 * amount bytes for UDP/IPv6
375 * Does not yet check not to return self
376 */
377size_t return_peers_for_torrent(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto) {
378 return proto == FLAG_TCP ? return_peers_for_torrent_tcp(ws, torrent, amount, reply) : return_peers_for_torrent_udp(ws, torrent, amount, reply);
379}
380
381/* Fetches scrape info for a specific torrent */
382size_t return_udp_scrape_for_torrent(ot_hash const hash, char *reply) {
383 int exactmatch, delta_torrentcount = 0;
384 ot_vector *torrents_list = mutex_bucket_lock_by_hash(hash);
385 ot_torrent *torrent = vector_binary_search(hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch);
386
387 if (!exactmatch) {
388 memset(reply, 0, 12);
389 } else {
390 uint32_t *r = (uint32_t *)reply;
391
392 if (clean_single_torrent(torrent)) {
393 vector_remove_torrent(torrents_list, torrent);
394 memset(reply, 0, 12);
395 delta_torrentcount = -1;
396 } else {
397 r[0] = htonl(torrent->peer_list6->seed_count + torrent->peer_list4->seed_count);
398 r[1] = htonl(torrent->peer_list6->down_count + torrent->peer_list4->down_count);
399 r[2] = htonl(torrent->peer_list6->peer_count + torrent->peer_list4->peer_count - torrent->peer_list6->seed_count - torrent->peer_list4->seed_count);
400 }
401 }
402 mutex_bucket_unlock_by_hash(hash, delta_torrentcount);
403 return 12;
404}
405
406/* Fetches scrape info for a specific torrent */
407void return_tcp_scrape_for_torrent(ot_hash const *hash_list, size_t amount, char **reply, const char *reply_end) {
408 int exactmatch;
409 size_t i;
410
411 fmt_append(reply, reply_end, "d5:filesd");
412
413 for (i = 0; i < amount; ++i) {
414 int delta_torrentcount = 0;
415 ot_hash const *hash = hash_list + i;
416 ot_vector *torrents_list = mutex_bucket_lock_by_hash(*hash);
417 ot_torrent *torrent = vector_binary_search(hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch);
418
419 if (exactmatch) {
420 if (clean_single_torrent(torrent)) {
421 vector_remove_torrent(torrents_list, torrent);
422 delta_torrentcount = -1;
423 } else {
424 fmt_append(reply, reply_end, "20:");
425 fmt_bytes(reply, reply_end, hash, sizeof(ot_hash));
426 fmt_append(reply, reply_end, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee",
427 torrent->peer_list6->seed_count + torrent->peer_list4->seed_count, torrent->peer_list6->down_count + torrent->peer_list4->down_count,
428 torrent->peer_list6->peer_count + torrent->peer_list4->peer_count - torrent->peer_list6->seed_count - torrent->peer_list4->seed_count);
429 }
430 }
431 mutex_bucket_unlock_by_hash(*hash, delta_torrentcount);
432 }
433
434 fmt_append(reply, reply_end, "ee");
435}
436
437static ot_peerlist dummy_list;
438size_t remove_peer_from_torrent(PROTO_FLAG proto, struct ot_workstruct *ws) {
439 int exactmatch;
440 ot_vector *torrents_list = mutex_bucket_lock_by_hash(*ws->hash);
441 ot_torrent *torrent = vector_binary_search(ws->hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch);
442 ot_peerlist *peer_list = &dummy_list;
443 size_t peer_size; /* initialized in next line */
444 ot_peer const *peer_src = peer_from_peer6(&ws->peer, &peer_size);
445 size_t peer_count = 0, seed_count = 0;
446
447#ifdef WANT_SYNC_LIVE
448 if (proto != FLAG_MCA) {
449 OT_PEERFLAG(ws->peer) |= PEER_FLAG_STOPPED;
450 livesync_tell(ws);
451 }
452#endif
453
454 if (exactmatch) {
455 peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4;
456 switch (vector_remove_peer(&peer_list->peers, peer_src, peer_size)) {
457 case 2:
458 peer_list->seed_count--; /* Intentional fallthrough */
459 case 1:
460 peer_list->peer_count--; /* Intentional fallthrough */
461 default:
462 break;
463 }
464
465 peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count;
466 seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count;
467 }
468
469 if (proto == FLAG_TCP) {
470 int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM;
471 char *r = ws->reply;
472
473 fmt_append(&r, ws->outbuf + G_OUTBUF_SIZE, "d8:completei%zde10:incompletei%zde8:intervali%ie12:min intervali%ie%s0:e", seed_count, peer_count - seed_count,
474 erval, erval / 2, peer_size == OT_PEER_SIZE6 ? PEERS_BENCODED6 : PEERS_BENCODED4);
475 ws->reply_size = r - ws->reply;
476 }
477
478 /* Handle UDP reply */
479 if (proto == FLAG_UDP) {
480 ((uint32_t *)ws->reply)[2] = htonl(OT_CLIENT_REQUEST_INTERVAL_RANDOM);
481 ((uint32_t *)ws->reply)[3] = htonl(peer_count - seed_count);
482 ((uint32_t *)ws->reply)[4] = htonl(seed_count);
483 ws->reply_size = 20;
484 }
485
486 mutex_bucket_unlock_by_hash(*ws->hash, 0);
487 return ws->reply_size;
488}
489
490void iterate_all_torrents(int (*for_each)(ot_torrent *torrent, uintptr_t data), uintptr_t data) {
491 size_t bucket, j;
492
493 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
494 ot_vector *torrents_list = mutex_bucket_lock(bucket);
495 ot_torrent *torrents = (ot_torrent *)(torrents_list->data);
496
497 for (j = 0; j < torrents_list->size; ++j)
498 if (for_each(torrents + j, data))
499 break;
500
501 mutex_bucket_unlock(bucket, 0);
502 if (!g_opentracker_running)
503 return;
504 }
505}
506
507ot_peer *peer_from_peer6(ot_peer6 *peer, size_t *peer_size) {
508 ot_ip6 *ip = (ot_ip6 *)peer;
509 if (!ip_is_v4mapped(ip)) {
510 *peer_size = OT_PEER_SIZE6;
511 return (ot_peer *)peer;
512 }
513 *peer_size = OT_PEER_SIZE4;
514 return (ot_peer *)(((uint8_t *)peer) + 12);
515}
516
517size_t peer_size_from_peer6(ot_peer6 *peer) {
518 ot_ip6 *ip = (ot_ip6 *)peer;
519 if (!ip_is_v4mapped(ip))
520 return OT_PEER_SIZE6;
521 return OT_PEER_SIZE4;
522}
523
524#ifdef _DEBUG_RANDOMTORRENTS
525void trackerlogic_add_random_torrents(size_t amount) {
526 struct ot_workstruct ws;
527 memset(&ws, 0, sizeof(ws));
528
529 ws.inbuf = malloc(G_INBUF_SIZE);
530 ws.outbuf = malloc(G_OUTBUF_SIZE);
531 ws.reply = ws.outbuf;
532 ws.hash = (ot_hash *)ws.inbuf;
533
534 while (amount--) {
535 random_bytes(ws.hash, sizeof(ot_hash));
536 random_bytes(&ws.peer, sizeof(ws.peer));
537
538 OT_PEERFLAG(ws.peer) &= PEER_FLAG_SEEDING | PEER_FLAG_COMPLETED | PEER_FLAG_STOPPED;
539
540 add_peer_to_torrent_and_return_peers(FLAG_TCP, &ws, 1);
541 }
542
543 free(ws.inbuf);
544 free(ws.outbuf);
545}
546#endif
547
548void exerr(const char *message) {
549 fprintf(stderr, "%s\n", message);
550 exit(111);
551}
552
553void trackerlogic_init(void) {
554 g_tracker_id = random_uint32();
555
556 if (!g_stats_path)
557 g_stats_path = "stats";
558 g_stats_path_len = strlen(g_stats_path);
559
560 /* Initialise background worker threads */
561 mutex_init();
562 workqueue_init();
563 clean_init();
564 fullscrape_init();
565 accesslist_init();
566 livesync_init();
567 stats_init();
568}
569
570int workstruct_init(struct ot_workstruct *ws) {
571 memset(ws, 0, sizeof(*ws));
572 ws->inbuf = malloc(G_INBUF_SIZE);
573 ws->outbuf = malloc(G_OUTBUF_SIZE);
574#ifdef _DEBUG_HTTPERROR
575 ws->debugbuf = malloc(G_DEBUGBUF_SIZE);
576#endif
577 if (!ws->inbuf || !ws->outbuf)
578 return -1;
579
580 /* Picking which peers to hand out starts here, so two workers seeded alike
581 would hand out alike */
582 random_bytes(ws->rand48_state, sizeof(ws->rand48_state));
583
584 stats_counters_register(ws);
585 return 0;
586}
587
588void workstruct_deinit(struct ot_workstruct *ws) {
589 /* The tally goes to the global before this struct goes out of scope */
590 stats_counters_retire(ws);
591
592 free(ws->inbuf);
593 free(ws->outbuf);
594#ifdef _DEBUG_HTTPERROR
595 free(ws->debugbuf);
596#endif
597}
598
599void trackerlogic_deinit(void) {
600 int delta_torrentcount = 0;
601 size_t bucket, j;
602
603 /* Stop the background workers first */
604 stats_deinit();
605 livesync_deinit();
606 accesslist_deinit();
607 fullscrape_deinit();
608 clean_deinit();
609
610 /* Free all torrents... */
611 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
612 ot_vector *torrents_list = mutex_bucket_lock(bucket);
613 if (torrents_list->size) {
614 for (j = 0; j < torrents_list->size; ++j) {
615 ot_torrent *torrent = ((ot_torrent *)(torrents_list->data)) + j;
616 free_peerlist(torrent->peer_list6);
617 free_peerlist(torrent->peer_list4);
618 delta_torrentcount -= 1;
619 }
620 free(torrents_list->data);
621 }
622 mutex_bucket_unlock(bucket, delta_torrentcount);
623 }
624
625 /* Release mutexes */
626 workqueue_deinit();
627 mutex_deinit();
628}