1 /* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
23 #include "ot_accesslist.h"
25 #include "ot_fullscrape.h"
27 #include "ot_livesync.h"
30 #include "ot_vector.h"
31 #include "trackerlogic.h"
33 /* Forward declaration */
34 size_t return_peers_for_torrent(struct ot_workstruct
*ws
, ot_torrent
*torrent
, size_t amount
, char *reply
, PROTO_FLAG proto
);
36 void free_peerlist(ot_peerlist
*peer_list
) {
37 if (peer_list
->peers
.data
) {
38 if (OT_PEERLIST_HASBUCKETS(peer_list
))
39 vector_clean_list((ot_vector
*)peer_list
->peers
.data
, peer_list
->peers
.size
);
41 free(peer_list
->peers
.data
);
46 void add_torrent_from_saved_state(ot_hash
const hash
, ot_time base
, size_t down_count
) {
49 ot_vector
*torrents_list
= mutex_bucket_lock_by_hash(hash
);
51 if (!accesslist_hashisvalid(hash
))
52 return mutex_bucket_unlock_by_hash(hash
, 0);
54 torrent
= vector_find_or_insert(torrents_list
, (void *)hash
, sizeof(ot_torrent
), OT_HASH_COMPARE_SIZE
, &exactmatch
);
55 if (!torrent
|| exactmatch
)
56 return mutex_bucket_unlock_by_hash(hash
, 0);
58 /* Create a new torrent entry, then */
59 byte_zero(torrent
, sizeof(ot_torrent
));
60 memcpy(torrent
->hash
, hash
, sizeof(ot_hash
));
62 if (!(torrent
->peer_list6
= malloc(sizeof(ot_peerlist
))) || !(torrent
->peer_list4
= malloc(sizeof(ot_peerlist
)))) {
63 vector_remove_torrent(torrents_list
, torrent
);
64 return mutex_bucket_unlock_by_hash(hash
, 0);
67 byte_zero(torrent
->peer_list6
, sizeof(ot_peerlist
));
68 byte_zero(torrent
->peer_list4
, 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
;
74 return mutex_bucket_unlock_by_hash(hash
, 1);
77 size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto
, struct ot_workstruct
*ws
, size_t amount
) {
78 int exactmatch
, delta_torrentcount
= 0;
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
);
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
);
96 torrent
= vector_find_or_insert(torrents_list
, (void *)ws
->hash
, sizeof(ot_torrent
), OT_HASH_COMPARE_SIZE
, &exactmatch
);
98 mutex_bucket_unlock_by_hash(*ws
->hash
, 0);
103 /* Create a new torrent entry, then */
104 byte_zero(torrent
, sizeof(ot_torrent
));
105 memcpy(torrent
->hash
, *ws
->hash
, sizeof(ot_hash
));
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);
113 byte_zero(torrent
->peer_list6
, sizeof(ot_peerlist
));
114 byte_zero(torrent
->peer_list4
, sizeof(ot_peerlist
));
115 delta_torrentcount
= 1;
117 clean_single_torrent(torrent
);
119 torrent
->peer_list6
->base
= g_now_minutes
;
120 torrent
->peer_list4
->base
= g_now_minutes
;
122 peer_list
= peer_size
== OT_PEER_SIZE6
? torrent
->peer_list6
: torrent
->peer_list4
;
124 /* Check for peer in torrent */
125 peer_dest
= vector_find_or_insert_peer(&(peer_list
->peers
), peer_src
, peer_size
, &exactmatch
);
127 mutex_bucket_unlock_by_hash(*ws
->hash
, delta_torrentcount
);
131 /* Tell peer that it's fresh */
132 OT_PEERTIME(ws
->peer
, OT_PEER_SIZE6
) = 0;
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
;
138 /* If we hadn't had a match create peer there */
141 #ifdef WANT_SYNC_LIVE
142 if (proto
== FLAG_MCA
)
143 OT_PEERFLAG(ws
->peer
) |= PEER_FLAG_FROM_SYNC
;
148 peer_list
->peer_count
++;
149 if (OT_PEERFLAG(ws
->peer
) & PEER_FLAG_COMPLETED
) {
150 peer_list
->down_count
++;
151 stats_issue_event(EVENT_COMPLETED
, 0, (uintptr_t)ws
);
153 if (OT_PEERFLAG(ws
->peer
) & PEER_FLAG_SEEDING
)
154 peer_list
->seed_count
++;
157 stats_issue_event(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(EVENT_WOODPECKER
, 0, (uintptr_t)&ws
->peer
);
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
)))
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(EVENT_COMPLETED
, 0, (uintptr_t)ws
);
180 if (OT_PEERFLAG_D(peer_dest
, peer_size
) & PEER_FLAG_COMPLETED
)
181 OT_PEERFLAG(ws
->peer
) |= PEER_FLAG_COMPLETED
;
184 memcpy(peer_dest
, peer_src
, peer_size
);
186 if (proto
== FLAG_MCA
) {
187 mutex_bucket_unlock_by_hash(*ws
->hash
, delta_torrentcount
);
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
;
197 static size_t return_peers_all(ot_peerlist
*peer_list
, size_t peer_size
, char *reply
) {
198 unsigned int 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
;
204 if (OT_PEERLIST_HASBUCKETS(peer_list
)) {
205 num_buckets
= bucket_list
->size
;
206 bucket_list
= (ot_vector
*)bucket_list
->data
;
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
);
217 memcpy(reply
, peers
, compare_size
);
218 reply
+= compare_size
;
226 static size_t return_peers_selection(struct ot_workstruct
*ws
, ot_peerlist
*peer_list
, size_t peer_size
, size_t amount
, char *reply
) {
227 unsigned int 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
;
236 if (OT_PEERLIST_HASBUCKETS(peer_list
)) {
237 num_buckets
= bucket_list
->size
;
238 bucket_list
= (ot_vector
*)bucket_list
->data
;
241 /* Make fixpoint arithmetic as exact as possible */
242 #define MAXPRECBIT (1 << (8 * sizeof(int) - 3))
243 while (!(shifted_pc
& MAXPRECBIT
)) {
247 shifted_step
= shifted_pc
/ amount
;
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
;
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
;
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
;
265 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
);
270 memcpy(reply
, peer
, compare_size
);
271 reply
+= compare_size
;
277 static size_t return_peers_for_torrent_udp(struct ot_workstruct
*ws
, ot_torrent
*torrent
, size_t amount
, char *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
;
284 if (amount
> peer_list
->peer_count
)
285 amount
= peer_list
->peer_count
;
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
);
293 if (amount
== peer_list
->peer_count
)
294 r
+= return_peers_all(peer_list
, peer_size
, r
);
296 r
+= return_peers_selection(ws
, peer_list
, peer_size
, amount
, r
);
301 static size_t return_peers_for_torrent_tcp(struct ot_workstruct
*ws
, ot_torrent
*torrent
, size_t amount
, char *reply
) {
303 int erval
= OT_CLIENT_REQUEST_INTERVAL_RANDOM
;
304 size_t seed_count
= torrent
->peer_list6
->seed_count
+ torrent
->peer_list4
->seed_count
;
305 size_t down_count
= torrent
->peer_list6
->down_count
+ torrent
->peer_list4
->down_count
;
306 size_t peer_count
= torrent
->peer_list6
->peer_count
+ torrent
->peer_list4
->peer_count
- seed_count
;
308 /* Simple case: amount of peers in both lists is less than requested, here we return all results */
309 size_t amount_v4
= torrent
->peer_list4
->peer_count
;
310 size_t amount_v6
= torrent
->peer_list6
->peer_count
;
312 /* Complex case: both lists have more than enough entries and we need to split between v4 and v6 clients */
313 if (amount_v4
+ amount_v6
> amount
) {
314 size_t amount_left
, percent_v6
= 0, percent_v4
= 0, left_v6
, left_v4
;
315 const size_t SCALE
= 1024;
317 /* If possible, fill at least a quarter of peer from each family */
318 if (amount
/ 4 <= amount_v4
)
319 amount_v4
= amount
/ 4;
320 if (amount
/ 4 <= amount_v6
)
321 amount_v6
= amount
/ 4;
323 /* Fill the rest according to which family's pool provides more peers */
324 amount_left
= amount
- (amount_v4
+ amount_v6
);
326 left_v4
= torrent
->peer_list4
->peer_count
- amount_v4
;
327 left_v6
= torrent
->peer_list6
->peer_count
- amount_v6
;
329 if (left_v4
+ left_v6
) {
330 percent_v4
= (SCALE
* left_v4
) / (left_v4
+ left_v6
);
331 percent_v6
= (SCALE
* left_v6
) / (left_v4
+ left_v6
);
334 amount_v4
+= (amount_left
* percent_v4
) / SCALE
;
335 amount_v6
+= (amount_left
* percent_v6
) / SCALE
;
337 /* Integer division rounding can leave out a peer */
338 if (amount_v4
+ amount_v6
< amount
&& amount_v6
< torrent
->peer_list6
->peer_count
)
340 if (amount_v4
+ amount_v6
< amount
&& amount_v4
< torrent
->peer_list4
->peer_count
)
345 sprintf(r
, "d8:completei%zde10:downloadedi%zde10:incompletei%zde8:intervali%ie12:min intervali%ie", seed_count
, down_count
, peer_count
, erval
, erval
/ 2);
348 r
+= sprintf(r
, PEERS_BENCODED4
"%zd:", OT_PEER_COMPARE_SIZE4
* amount_v4
);
349 if (amount_v4
== torrent
->peer_list4
->peer_count
)
350 r
+= return_peers_all(torrent
->peer_list4
, OT_PEER_SIZE4
, r
);
352 r
+= return_peers_selection(ws
, torrent
->peer_list4
, OT_PEER_SIZE4
, amount_v4
, r
);
356 r
+= sprintf(r
, PEERS_BENCODED6
"%zd:", OT_PEER_COMPARE_SIZE6
* amount_v6
);
357 if (amount_v6
== torrent
->peer_list6
->peer_count
)
358 r
+= return_peers_all(torrent
->peer_list6
, OT_PEER_SIZE6
, r
);
360 r
+= return_peers_selection(ws
, torrent
->peer_list6
, OT_PEER_SIZE6
, amount_v6
, r
);
368 /* Compiles a list of random peers for a torrent
369 * Reply must have enough space to hold:
370 * 92 + 6 * amount bytes for TCP/IPv4
371 * 92 + 18 * amount bytes for TCP/IPv6
372 * 12 + 6 * amount bytes for UDP/IPv4
373 * 12 + 18 * amount bytes for UDP/IPv6
374 * Does not yet check not to return self
376 size_t return_peers_for_torrent(struct ot_workstruct
*ws
, ot_torrent
*torrent
, size_t amount
, char *reply
, PROTO_FLAG proto
) {
377 return proto
== FLAG_TCP
? return_peers_for_torrent_tcp(ws
, torrent
, amount
, reply
) : return_peers_for_torrent_udp(ws
, torrent
, amount
, reply
);
380 /* Fetches scrape info for a specific torrent */
381 size_t return_udp_scrape_for_torrent(ot_hash
const hash
, char *reply
) {
382 int exactmatch
, delta_torrentcount
= 0;
383 ot_vector
*torrents_list
= mutex_bucket_lock_by_hash(hash
);
384 ot_torrent
*torrent
= binary_search(hash
, torrents_list
->data
, torrents_list
->size
, sizeof(ot_torrent
), OT_HASH_COMPARE_SIZE
, &exactmatch
);
387 memset(reply
, 0, 12);
389 uint32_t *r
= (uint32_t *)reply
;
391 if (clean_single_torrent(torrent
)) {
392 vector_remove_torrent(torrents_list
, torrent
);
393 memset(reply
, 0, 12);
394 delta_torrentcount
= -1;
396 r
[0] = htonl(torrent
->peer_list6
->seed_count
+ torrent
->peer_list4
->seed_count
);
397 r
[1] = htonl(torrent
->peer_list6
->down_count
+ torrent
->peer_list4
->down_count
);
398 r
[2] = htonl(torrent
->peer_list6
->peer_count
+ torrent
->peer_list4
->peer_count
- torrent
->peer_list6
->seed_count
- torrent
->peer_list4
->seed_count
);
401 mutex_bucket_unlock_by_hash(hash
, delta_torrentcount
);
405 /* Fetches scrape info for a specific torrent */
406 size_t return_tcp_scrape_for_torrent(ot_hash
const *hash_list
, int amount
, char *reply
) {
410 r
+= sprintf(r
, "d5:filesd");
412 for (i
= 0; i
< amount
; ++i
) {
413 int delta_torrentcount
= 0;
414 ot_hash
const *hash
= hash_list
+ i
;
415 ot_vector
*torrents_list
= mutex_bucket_lock_by_hash(*hash
);
416 ot_torrent
*torrent
= binary_search(hash
, torrents_list
->data
, torrents_list
->size
, sizeof(ot_torrent
), OT_HASH_COMPARE_SIZE
, &exactmatch
);
419 if (clean_single_torrent(torrent
)) {
420 vector_remove_torrent(torrents_list
, torrent
);
421 delta_torrentcount
= -1;
426 memcpy(r
, hash
, sizeof(ot_hash
));
427 r
+= sizeof(ot_hash
);
428 r
+= sprintf(r
, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", torrent
->peer_list6
->seed_count
+ torrent
->peer_list4
->seed_count
,
429 torrent
->peer_list6
->down_count
+ torrent
->peer_list4
->down_count
,
430 torrent
->peer_list6
->peer_count
+ torrent
->peer_list4
->peer_count
- torrent
->peer_list6
->seed_count
- torrent
->peer_list4
->seed_count
);
433 mutex_bucket_unlock_by_hash(*hash
, delta_torrentcount
);
441 static ot_peerlist dummy_list
;
442 size_t remove_peer_from_torrent(PROTO_FLAG proto
, struct ot_workstruct
*ws
) {
444 ot_vector
*torrents_list
= mutex_bucket_lock_by_hash(*ws
->hash
);
445 ot_torrent
*torrent
= binary_search(ws
->hash
, torrents_list
->data
, torrents_list
->size
, sizeof(ot_torrent
), OT_HASH_COMPARE_SIZE
, &exactmatch
);
446 ot_peerlist
*peer_list
= &dummy_list
;
447 size_t peer_size
; /* initialized in next line */
448 ot_peer
const *peer_src
= peer_from_peer6(&ws
->peer
, &peer_size
);
449 size_t peer_count
= 0, seed_count
= 0;
451 #ifdef WANT_SYNC_LIVE
452 if (proto
!= FLAG_MCA
) {
453 OT_PEERFLAG(ws
->peer
) |= PEER_FLAG_STOPPED
;
459 peer_list
= peer_size
== OT_PEER_SIZE6
? torrent
->peer_list6
: torrent
->peer_list4
;
460 switch (vector_remove_peer(&peer_list
->peers
, peer_src
, peer_size
)) {
462 peer_list
->seed_count
--; /* Intentional fallthrough */
464 peer_list
->peer_count
--; /* Intentional fallthrough */
469 peer_count
= torrent
->peer_list6
->peer_count
+ torrent
->peer_list4
->peer_count
;
470 seed_count
= torrent
->peer_list6
->seed_count
+ torrent
->peer_list4
->seed_count
;
473 if (proto
== FLAG_TCP
) {
474 int erval
= OT_CLIENT_REQUEST_INTERVAL_RANDOM
;
475 ws
->reply_size
= sprintf(ws
->reply
, "d8:completei%zde10:incompletei%zde8:intervali%ie12:min intervali%ie%s0:e", seed_count
, peer_count
- seed_count
, erval
,
476 erval
/ 2, peer_size
== OT_PEER_SIZE6
? PEERS_BENCODED6
: PEERS_BENCODED4
);
479 /* Handle UDP reply */
480 if (proto
== FLAG_UDP
) {
481 ((uint32_t *)ws
->reply
)[2] = htonl(OT_CLIENT_REQUEST_INTERVAL_RANDOM
);
482 ((uint32_t *)ws
->reply
)[3] = htonl(peer_count
- seed_count
);
483 ((uint32_t *)ws
->reply
)[4] = htonl(seed_count
);
487 mutex_bucket_unlock_by_hash(*ws
->hash
, 0);
488 return ws
->reply_size
;
491 void iterate_all_torrents(int (*for_each
)(ot_torrent
*torrent
, uintptr_t data
), uintptr_t data
) {
495 for (bucket
= 0; bucket
< OT_BUCKET_COUNT
; ++bucket
) {
496 ot_vector
*torrents_list
= mutex_bucket_lock(bucket
);
497 ot_torrent
*torrents
= (ot_torrent
*)(torrents_list
->data
);
499 for (j
= 0; j
< torrents_list
->size
; ++j
)
500 if (for_each(torrents
+ j
, data
))
503 mutex_bucket_unlock(bucket
, 0);
504 if (!g_opentracker_running
)
509 ot_peer
*peer_from_peer6(ot_peer6
*peer
, size_t *peer_size
) {
510 ot_ip6
*ip
= (ot_ip6
*)peer
;
511 if (!ip6_isv4mapped(ip
)) {
512 *peer_size
= OT_PEER_SIZE6
;
513 return (ot_peer
*)peer
;
515 *peer_size
= OT_PEER_SIZE4
;
516 return (ot_peer
*)(((uint8_t *)peer
) + 12);
519 size_t peer_size_from_peer6(ot_peer6
*peer
) {
520 ot_ip6
*ip
= (ot_ip6
*)peer
;
521 if (!ip6_isv4mapped(ip
))
522 return OT_PEER_SIZE6
;
523 return OT_PEER_SIZE4
;
526 #ifdef _DEBUG_RANDOMTORRENTS
527 void trackerlogic_add_random_torrents(size_t amount
) {
528 struct ot_workstruct ws
;
529 memset(&ws
, 0, sizeof(ws
));
531 ws
.inbuf
= malloc(G_INBUF_SIZE
);
532 ws
.outbuf
= malloc(G_OUTBUF_SIZE
);
533 ws
.reply
= ws
.outbuf
;
534 ws
.hash
= (ot_hash
*)ws
.inbuf
;
537 arc4random_buf(ws
.hash
, sizeof(ot_hash
));
538 arc4random_buf(&ws
.peer
, sizeof(ws
.peer
));
540 OT_PEERFLAG(ws
.peer
) &= PEER_FLAG_SEEDING
| PEER_FLAG_COMPLETED
| PEER_FLAG_STOPPED
;
542 add_peer_to_torrent_and_return_peers(FLAG_TCP
, &ws
, 1);
550 void exerr(char *message
) {
551 fprintf(stderr
, "%s\n", message
);
555 void trackerlogic_init() {
556 g_tracker_id
= random();
559 g_stats_path
= "stats";
560 g_stats_path_len
= strlen(g_stats_path
);
562 /* Initialise background worker threads */
571 void trackerlogic_deinit(void) {
572 int bucket
, delta_torrentcount
= 0;
575 /* Free all torrents... */
576 for (bucket
= 0; bucket
< OT_BUCKET_COUNT
; ++bucket
) {
577 ot_vector
*torrents_list
= mutex_bucket_lock(bucket
);
578 if (torrents_list
->size
) {
579 for (j
= 0; j
< torrents_list
->size
; ++j
) {
580 ot_torrent
*torrent
= ((ot_torrent
*)(torrents_list
->data
)) + j
;
581 free_peerlist(torrent
->peer_list6
);
582 free_peerlist(torrent
->peer_list4
);
583 delta_torrentcount
-= 1;
585 free(torrents_list
->data
);
587 mutex_bucket_unlock(bucket
, delta_torrentcount
);
590 /* Deinitialise background worker threads */
596 /* Release mutexes */