summaryrefslogtreecommitdiff
path: root/trackerlogic.c
diff options
context:
space:
mode:
Diffstat (limited to 'trackerlogic.c')
-rw-r--r--trackerlogic.c600
1 files changed, 58 insertions, 542 deletions
diff --git a/trackerlogic.c b/trackerlogic.c
index 2725399..c09f1b9 100644
--- a/trackerlogic.c
+++ b/trackerlogic.c
@@ -1,149 +1,33 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org> 1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever. */ 2 It is considered beerware. Prost. Skol. Cheers or whatever. */
3 3
4#include "trackerlogic.h" 4/* System */
5
6#include <stdlib.h> 5#include <stdlib.h>
7#include <string.h> 6#include <string.h>
8#include <stdio.h> 7#include <stdio.h>
9#include <fcntl.h>
10#include <sys/types.h> 8#include <sys/types.h>
11#include <sys/mman.h> 9#include <sys/mman.h>
12#include <arpa/inet.h>
13#include <unistd.h> 10#include <unistd.h>
14#include <time.h> 11#include <time.h>
15#include <math.h> 12#include <math.h>
16#include <glob.h>
17 13
18#include <errno.h> 14/* Libowfat */
19#include "scan.h" 15#include "scan.h"
20#include "byte.h" 16#include "byte.h"
21#include "mutex.h"
22 17
23/* GLOBAL VARIABLES */ 18/* Opentracker */
19#include "trackerlogic.h"
20#include "ot_mutex.h"
21#include "ot_stats.h"
22#include "ot_clean.h"
24 23
25/* We maintain a list of 1024 pointers to sorted list of ot_torrent structs 24/* GLOBAL VARIABLES */
26 Sort key is, of course, its hash */
27#define OT_BUCKET_COUNT 1024
28static ot_vector all_torrents[OT_BUCKET_COUNT];
29static ot_time all_torrents_clean[OT_BUCKET_COUNT];
30#if defined ( WANT_BLACKLISTING ) || defined( WANT_CLOSED_TRACKER ) 25#if defined ( WANT_BLACKLISTING ) || defined( WANT_CLOSED_TRACKER )
31static ot_vector accesslist; 26static ot_vector accesslist;
32#define WANT_ACCESS_CONTROL 27#define WANT_ACCESS_CONTROL
33#endif 28#endif
34 29
35static int clean_single_torrent( ot_torrent *torrent ); 30void free_peerlist( ot_peerlist *peer_list ) {
36
37/* these functions protect our buckets from other threads that
38 try to commit announces or clean up */
39static ot_vector *lock_bucket_by_hash( ot_hash *hash ) {
40 unsigned char *local_hash = hash[0];
41 int bucket = ( local_hash[0] << 2 ) | ( local_hash[1] >> 6 );
42
43 /* Can block */
44 mutex_bucket_lock( bucket );
45
46 return all_torrents + bucket;
47}
48
49static void *unlock_bucket_by_hash( ot_hash *hash ) {
50 unsigned char *local_hash = hash[0];
51 int bucket = ( local_hash[0] << 2 ) | ( local_hash[1] >> 6 );
52 mutex_bucket_unlock( bucket );
53
54 /* To make caller's code look better, allow
55 return unlock_bucket_by_hash() */
56 return NULL;
57}
58
59/* Converter function from memory to human readable hex strings */
60static char*to_hex(char*d,ot_byte*s){const char*m="0123456789ABCDEF";char*e=d+40;while(d<e){*d++=m[*s>>4];*d++=m[*s++&15];}*d=0;return d;}
61
62/* This function gives us a binary search that returns a pointer, even if
63 no exact match is found. In that case it sets exactmatch 0 and gives
64 calling functions the chance to insert data
65*/
66static void *binary_search( const void * const key, const void * base, const size_t member_count, const size_t member_size,
67 size_t compare_size, int *exactmatch ) {
68 size_t mc = member_count;
69 ot_byte *lookat = ((ot_byte*)base) + member_size * (member_count >> 1);
70 *exactmatch = 1;
71
72 while( mc ) {
73 int cmp = memcmp( lookat, key, compare_size);
74 if (cmp == 0) return (void *)lookat;
75 if (cmp < 0) {
76 base = (void*)(lookat + member_size);
77 --mc;
78 }
79 mc >>= 1;
80 lookat = ((ot_byte*)base) + member_size * (mc >> 1);
81 }
82 *exactmatch = 0;
83 return (void*)lookat;
84}
85
86/* This is the generic insert operation for our vector type.
87 It tries to locate the object at "key" with size "member_size" by comparing its first "compare_size" bytes with
88 those of objects in vector. Our special "binary_search" function does that and either returns the match or a
89 pointer to where the object is to be inserted. vector_find_or_insert makes space for the object and copies it,
90 if it wasn't found in vector. Caller needs to check the passed "exactmatch" variable to see, whether an insert
91 took place. If resizing the vector failed, NULL is returned, else the pointer to the object in vector.
92*/
93static void *vector_find_or_insert( ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch ) {
94 ot_byte *match = binary_search( key, vector->data, vector->size, member_size, compare_size, exactmatch );
95
96 if( *exactmatch ) return match;
97
98 if( vector->size + 1 >= vector->space ) {
99 size_t new_space = vector->space ? OT_VECTOR_GROW_RATIO * vector->space : OT_VECTOR_MIN_MEMBERS;
100 ot_byte *new_data = realloc( vector->data, new_space * member_size );
101 if( !new_data ) return NULL;
102
103 /* Adjust pointer if it moved by realloc */
104 match = new_data + (match - (ot_byte*)vector->data);
105
106 vector->data = new_data;
107 vector->space = new_space;
108 }
109 memmove( match + member_size, match, ((ot_byte*)vector->data) + member_size * vector->size - match );
110 vector->size++;
111 return match;
112}
113
114/* This is the non-generic delete from vector-operation specialized for peers in pools.
115 Set hysteresis == 0 if you expect the vector not to ever grow again.
116 It returns 0 if no peer was found (and thus not removed)
117 1 if a non-seeding peer was removed
118 2 if a seeding peer was removed
119*/
120static int vector_remove_peer( ot_vector *vector, ot_peer *peer, int hysteresis ) {
121 int exactmatch;
122 size_t shrink_thresh = hysteresis ? OT_VECTOR_SHRINK_THRESH : OT_VECTOR_SHRINK_RATIO;
123 ot_peer *end = ((ot_peer*)vector->data) + vector->size;
124 ot_peer *match;
125
126 if( !vector->size ) return 0;
127 match = binary_search( peer, vector->data, vector->size, sizeof( ot_peer ), OT_PEER_COMPARE_SIZE, &exactmatch );
128
129 if( !exactmatch ) return 0;
130 exactmatch = ( OT_FLAG( match ) & PEER_FLAG_SEEDING ) ? 2 : 1;
131 memmove( match, match + 1, sizeof(ot_peer) * ( end - match - 1 ) );
132 if( ( --vector->size * shrink_thresh < vector->space ) && ( vector->space > OT_VECTOR_MIN_MEMBERS ) ) {
133 vector->space /= OT_VECTOR_SHRINK_RATIO;
134 vector->data = realloc( vector->data, vector->space * sizeof( ot_peer ) );
135 }
136 if( !vector->size ) {
137 /* for peer pools its safe to let them go,
138 in 999 of 1000 this happens in older pools, that won't ever grow again */
139 free( vector->data );
140 vector->data = NULL;
141 vector->space = 0;
142 }
143 return exactmatch;
144}
145
146static void free_peerlist( ot_peerlist *peer_list ) {
147 size_t i; 31 size_t i;
148 for( i=0; i<OT_POOLS_COUNT; ++i ) 32 for( i=0; i<OT_POOLS_COUNT; ++i )
149 if( peer_list->peers[i].data ) 33 if( peer_list->peers[i].data )
@@ -154,27 +38,11 @@ static void free_peerlist( ot_peerlist *peer_list ) {
154 free( peer_list ); 38 free( peer_list );
155} 39}
156 40
157static void vector_remove_torrent( ot_vector *vector, ot_torrent *match ) {
158 ot_torrent *end = ((ot_torrent*)vector->data) + vector->size;
159
160 if( !vector->size ) return;
161
162 /* If this is being called after a unsuccessful malloc() for peer_list
163 in add_peer_to_torrent, match->peer_list actually might be NULL */
164 if( match->peer_list) free_peerlist( match->peer_list );
165
166 memmove( match, match + 1, sizeof(ot_torrent) * ( end - match - 1 ) );
167 if( ( --vector->size * OT_VECTOR_SHRINK_THRESH < vector->space ) && ( vector->space > OT_VECTOR_MIN_MEMBERS ) ) {
168 vector->space /= OT_VECTOR_SHRINK_RATIO;
169 vector->data = realloc( vector->data, vector->space * sizeof( ot_torrent ) );
170 }
171}
172
173ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer WANT_TRACKER_SYNC_PARAM( int from_changeset ) ) { 41ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer WANT_TRACKER_SYNC_PARAM( int from_changeset ) ) {
174 int exactmatch; 42 int exactmatch;
175 ot_torrent *torrent; 43 ot_torrent *torrent;
176 ot_peer *peer_dest; 44 ot_peer *peer_dest;
177 ot_vector *torrents_list = lock_bucket_by_hash( hash ), *peer_pool; 45 ot_vector *torrents_list = mutex_bucket_lock_by_hash( hash ), *peer_pool;
178 int base_pool = 0; 46 int base_pool = 0;
179 47
180#ifdef WANT_ACCESS_CONTROL 48#ifdef WANT_ACCESS_CONTROL
@@ -184,13 +52,17 @@ ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer WANT_TRACKER_SYNC
184 exactmatch = !exactmatch; 52 exactmatch = !exactmatch;
185#endif 53#endif
186 54
187 if( exactmatch ) 55 if( exactmatch ) {
188 return unlock_bucket_by_hash( hash ); 56 mutex_bucket_unlock_by_hash( hash );
57 return NULL;
58 }
189#endif 59#endif
190 60
191 torrent = vector_find_or_insert( torrents_list, (void*)hash, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 61 torrent = vector_find_or_insert( torrents_list, (void*)hash, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch );
192 if( !torrent ) 62 if( !torrent ) {
193 return unlock_bucket_by_hash( hash ); 63 mutex_bucket_unlock_by_hash( hash );
64 return NULL;
65 }
194 66
195 if( !exactmatch ) { 67 if( !exactmatch ) {
196 /* Create a new torrent entry, then */ 68 /* Create a new torrent entry, then */
@@ -198,7 +70,8 @@ ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer WANT_TRACKER_SYNC
198 70
199 if( !( torrent->peer_list = malloc( sizeof (ot_peerlist) ) ) ) { 71 if( !( torrent->peer_list = malloc( sizeof (ot_peerlist) ) ) ) {
200 vector_remove_torrent( torrents_list, torrent ); 72 vector_remove_torrent( torrents_list, torrent );
201 return unlock_bucket_by_hash( hash ); 73 mutex_bucket_unlock_by_hash( hash );
74 return NULL;
202 } 75 }
203 76
204 byte_zero( torrent->peer_list, sizeof( ot_peerlist ) ); 77 byte_zero( torrent->peer_list, sizeof( ot_peerlist ) );
@@ -216,7 +89,7 @@ ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer WANT_TRACKER_SYNC
216 peer_pool = &torrent->peer_list->peers[0]; 89 peer_pool = &torrent->peer_list->peers[0];
217 binary_search( peer, peer_pool->data, peer_pool->size, sizeof(ot_peer), OT_PEER_COMPARE_SIZE, &exactmatch ); 90 binary_search( peer, peer_pool->data, peer_pool->size, sizeof(ot_peer), OT_PEER_COMPARE_SIZE, &exactmatch );
218 if( exactmatch ) { 91 if( exactmatch ) {
219 unlock_bucket_by_hash( hash ); 92 mutex_bucket_unlock_by_hash( hash );
220 return torrent; 93 return torrent;
221 } 94 }
222 base_pool = 1; 95 base_pool = 1;
@@ -248,7 +121,7 @@ ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer WANT_TRACKER_SYNC
248 torrent->peer_list->seed_count--; 121 torrent->peer_list->seed_count--;
249 case 1: default: 122 case 1: default:
250 torrent->peer_list->peer_count--; 123 torrent->peer_list->peer_count--;
251 unlock_bucket_by_hash( hash ); 124 mutex_bucket_unlock_by_hash( hash );
252 return torrent; 125 return torrent;
253 } 126 }
254 } 127 }
@@ -269,7 +142,7 @@ ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer WANT_TRACKER_SYNC
269 memmove( peer_dest, peer, sizeof( ot_peer ) ); 142 memmove( peer_dest, peer, sizeof( ot_peer ) );
270 } 143 }
271 144
272 unlock_bucket_by_hash( hash ); 145 mutex_bucket_unlock_by_hash( hash );
273 return torrent; 146 return torrent;
274} 147}
275 148
@@ -282,13 +155,13 @@ ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer WANT_TRACKER_SYNC
282size_t return_peers_for_torrent( ot_hash *hash, size_t amount, char *reply, int is_tcp ) { 155size_t return_peers_for_torrent( ot_hash *hash, size_t amount, char *reply, int is_tcp ) {
283 char *r = reply; 156 char *r = reply;
284 int exactmatch; 157 int exactmatch;
285 ot_vector *torrents_list = lock_bucket_by_hash( hash ); 158 ot_vector *torrents_list = mutex_bucket_lock_by_hash( hash );
286 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 159 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch );
287 ot_peerlist *peer_list = torrent->peer_list; 160 ot_peerlist *peer_list = torrent->peer_list;
288 size_t index; 161 size_t index;
289 162
290 if( !torrent ) { 163 if( !torrent ) {
291 unlock_bucket_by_hash( hash ); 164 mutex_bucket_unlock_by_hash( hash );
292 return 0; 165 return 0;
293 } 166 }
294 167
@@ -338,12 +211,12 @@ size_t return_peers_for_torrent( ot_hash *hash, size_t amount, char *reply, int
338 if( is_tcp ) 211 if( is_tcp )
339 *r++ = 'e'; 212 *r++ = 'e';
340 213
341 unlock_bucket_by_hash( hash ); 214 mutex_bucket_unlock_by_hash( hash );
342 return r - reply; 215 return r - reply;
343} 216}
344 217
345/* Release memory we allocated too much */ 218/* Release memory we allocated too much */
346static void fix_mmapallocation( void *buf, size_t old_alloc, size_t new_alloc ) { 219void fix_mmapallocation( void *buf, size_t old_alloc, size_t new_alloc ) {
347 int page_size = getpagesize(); 220 int page_size = getpagesize();
348 size_t old_pages = 1 + old_alloc / page_size; 221 size_t old_pages = 1 + old_alloc / page_size;
349 size_t new_pages = 1 + new_alloc / page_size; 222 size_t new_pages = 1 + new_alloc / page_size;
@@ -356,19 +229,23 @@ static void fix_mmapallocation( void *buf, size_t old_alloc, size_t new_alloc )
356size_t return_fullscrape_for_tracker( char **reply ) { 229size_t return_fullscrape_for_tracker( char **reply ) {
357 size_t torrent_count = 0, j; 230 size_t torrent_count = 0, j;
358 size_t allocated, replysize; 231 size_t allocated, replysize;
359 int i; 232 ot_vector *torrents_list;
233 int bucket;
360 char *r; 234 char *r;
361 235
362 for( i=0; i<OT_BUCKET_COUNT; ++i ) 236 for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) {
363 torrent_count += all_torrents[i].size; 237 ot_vector *torrents_list = mutex_bucket_lock( bucket );
238 torrent_count += torrents_list->size;
239 mutex_bucket_unlock( bucket );
240 }
364 241
365 /* one extra for pro- and epilogue */ 242 /* one extra for pro- and epilogue */
366 allocated = 100*(1+torrent_count); 243 allocated = 100*(1+torrent_count);
367 if( !( r = *reply = mmap( NULL, allocated, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 ) ) ) return 0; 244 if( !( r = *reply = mmap( NULL, allocated, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 ) ) ) return 0;
368 245
369 memmove( r, "d5:filesd", 9 ); r += 9; 246 memmove( r, "d5:filesd", 9 ); r += 9;
370 for( i=0; i<OT_BUCKET_COUNT; ++i ) { 247 for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) {
371 ot_vector *torrents_list = all_torrents + i; 248 torrents_list = mutex_bucket_lock( bucket );
372 for( j=0; j<torrents_list->size; ++j ) { 249 for( j=0; j<torrents_list->size; ++j ) {
373 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list; 250 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
374 ot_hash *hash =&( ((ot_torrent*)(torrents_list->data))[j] ).hash; 251 ot_hash *hash =&( ((ot_torrent*)(torrents_list->data))[j] ).hash;
@@ -378,6 +255,7 @@ size_t return_fullscrape_for_tracker( char **reply ) {
378 r += sprintf( r, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", peer_list->seed_count, peer_list->down_count, peer_list->peer_count-peer_list->seed_count ); 255 r += sprintf( r, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", peer_list->seed_count, peer_list->down_count, peer_list->peer_count-peer_list->seed_count );
379 } 256 }
380 } 257 }
258 mutex_bucket_unlock( bucket );
381 } 259 }
382 260
383 *r++='e'; *r++='e'; 261 *r++='e'; *r++='e';
@@ -388,45 +266,10 @@ size_t return_fullscrape_for_tracker( char **reply ) {
388 return replysize; 266 return replysize;
389} 267}
390 268
391size_t return_memstat_for_tracker( char **reply ) {
392 size_t torrent_count = 0, j;
393 size_t allocated, replysize;
394 int i, k;
395 char *r;
396
397 for( i=0; i<OT_BUCKET_COUNT; ++i ) {
398 ot_vector *torrents_list = all_torrents + i;
399 torrent_count += torrents_list->size;
400 }
401
402 allocated = OT_BUCKET_COUNT*32 + (43+OT_POOLS_COUNT*32)*torrent_count;
403 if( !( r = *reply = mmap( NULL, allocated, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 ) ) ) return 0;
404
405 for( i=0; i<OT_BUCKET_COUNT; ++i )
406 r += sprintf( r, "%02X: %08X %08X\n", i, (unsigned int)all_torrents[i].size, (unsigned int)all_torrents[i].space );
407
408 for( i=0; i<OT_BUCKET_COUNT; ++i ) {
409 ot_vector *torrents_list = all_torrents + i;
410 char hex_out[42];
411 for( j=0; j<torrents_list->size; ++j ) {
412 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
413 ot_hash *hash =&( ((ot_torrent*)(torrents_list->data))[j] ).hash;
414 r += sprintf( r, "\n%s:\n", to_hex( hex_out, (ot_byte*)hash) );
415 for( k=0; k<OT_POOLS_COUNT; ++k )
416 r += sprintf( r, "\t%05X %05X\n", ((unsigned int)peer_list->peers[k].size), (unsigned int)peer_list->peers[k].space );
417 }
418 }
419
420 replysize = ( r - *reply );
421 fix_mmapallocation( *reply, allocated, replysize );
422
423 return replysize;
424}
425
426/* Fetches scrape info for a specific torrent */ 269/* Fetches scrape info for a specific torrent */
427size_t return_udp_scrape_for_torrent( ot_hash *hash, char *reply ) { 270size_t return_udp_scrape_for_torrent( ot_hash *hash, char *reply ) {
428 int exactmatch; 271 int exactmatch;
429 ot_vector *torrents_list = lock_bucket_by_hash( hash ); 272 ot_vector *torrents_list = mutex_bucket_lock_by_hash( hash );
430 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 273 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch );
431 274
432 if( !exactmatch ) { 275 if( !exactmatch ) {
@@ -443,7 +286,7 @@ size_t return_udp_scrape_for_torrent( ot_hash *hash, char *reply ) {
443 r[2] = htonl( torrent->peer_list->peer_count-torrent->peer_list->seed_count ); 286 r[2] = htonl( torrent->peer_list->peer_count-torrent->peer_list->seed_count );
444 } 287 }
445 } 288 }
446 unlock_bucket_by_hash( hash ); 289 mutex_bucket_unlock_by_hash( hash );
447 return 12; 290 return 12;
448} 291}
449 292
@@ -456,7 +299,7 @@ size_t return_tcp_scrape_for_torrent( ot_hash *hash_list, int amount, char *repl
456 299
457 for( i=0; i<amount; ++i ) { 300 for( i=0; i<amount; ++i ) {
458 ot_hash *hash = hash_list + i; 301 ot_hash *hash = hash_list + i;
459 ot_vector *torrents_list = lock_bucket_by_hash( hash ); 302 ot_vector *torrents_list = mutex_bucket_lock_by_hash( hash );
460 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 303 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch );
461 304
462 if( exactmatch ) { 305 if( exactmatch ) {
@@ -468,347 +311,22 @@ size_t return_tcp_scrape_for_torrent( ot_hash *hash_list, int amount, char *repl
468 torrent->peer_list->seed_count, torrent->peer_list->down_count, torrent->peer_list->peer_count-torrent->peer_list->seed_count ) + 23; 311 torrent->peer_list->seed_count, torrent->peer_list->down_count, torrent->peer_list->peer_count-torrent->peer_list->seed_count ) + 23;
469 } 312 }
470 } 313 }
471 unlock_bucket_by_hash( hash ); 314 mutex_bucket_unlock_by_hash( hash );
472 }
473
474 *r++ = 'e'; *r++ = 'e';
475 return r - reply;
476}
477
478#ifdef WANT_TRACKER_SYNC
479/* Import Changeset from an external authority
480 format: d4:syncd[..]ee
481 [..]: ( 20:01234567890abcdefghij16:XXXXYYYY )+
482*/
483int add_changeset_to_tracker( ot_byte *data, size_t len ) {
484 ot_hash *hash;
485 ot_byte *end = data + len;
486 unsigned long peer_count;
487
488 /* We do know, that the string is \n terminated, so it cant
489 overflow */
490 if( byte_diff( data, 8, "d4:syncd" ) ) return -1;
491 data += 8;
492
493 while( 1 ) {
494 if( byte_diff( data, 3, "20:" ) ) {
495 if( byte_diff( data, 2, "ee" ) )
496 return -1;
497 return 0;
498 }
499 data += 3;
500 hash = (ot_hash*)data;
501 data += sizeof( ot_hash );
502
503 /* Scan string length indicator */
504 data += ( len = scan_ulong( (char*)data, &peer_count ) );
505
506 /* If no long was scanned, it is not divisible by 8, it is not
507 followed by a colon or claims to need to much memory, we fail */
508 if( !len || !peer_count || ( peer_count & 7 ) || ( *data++ != ':' ) || ( data + peer_count > end ) )
509 return -1;
510
511 while( peer_count > 0 ) {
512 add_peer_to_torrent( hash, (ot_peer*)data, 1 );
513 data += 8; peer_count -= 8;
514 }
515 }
516 return 0;
517}
518
519/* Proposed output format
520 d4:syncd20:<info_hash>8*N:(xxxxyyyy)*Nee
521*/
522size_t return_changeset_for_tracker( char **reply ) {
523 size_t allocated = 0, i, replysize;
524 int bucket;
525 char *r;
526
527 /* Maybe there is time to clean_all_torrents(); */
528
529 /* Determine space needed for whole changeset */
530 for( bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket ) {
531 ot_vector *torrents_list = all_torrents + bucket;
532 for( i=0; i<torrents_list->size; ++i ) {
533 ot_torrent *torrent = ((ot_torrent*)(torrents_list->data)) + i;
534 allocated += sizeof( ot_hash ) + sizeof(ot_peer) * torrent->peer_list->changeset.size + 13;
535 }
536 } 315 }
537 316
538 /* add "d4:syncd" and "ee" */
539 allocated += 8 + 2;
540
541 if( !( r = *reply = mmap( NULL, allocated, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 ) ) )
542 return 0;
543
544 memmove( r, "d4:syncd", 8 ); r += 8;
545 for( bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket ) {
546 ot_vector *torrents_list = all_torrents + bucket;
547 for( i=0; i<torrents_list->size; ++i ) {
548 ot_torrent *torrent = ((ot_torrent*)(torrents_list->data)) + i;
549 const size_t byte_count = sizeof(ot_peer) * torrent->peer_list->changeset.size;
550 *r++ = '2'; *r++ = '0'; *r++ = ':';
551 memmove( r, torrent->hash, sizeof( ot_hash ) ); r += sizeof( ot_hash );
552 r += sprintf( r, "%zd:", byte_count );
553 memmove( r, torrent->peer_list->changeset.data, byte_count ); r += byte_count;
554 }
555 }
556 *r++ = 'e'; *r++ = 'e'; 317 *r++ = 'e'; *r++ = 'e';
557
558 replysize = ( r - *reply );
559 fix_mmapallocation( *reply, allocated, replysize );
560
561 return replysize;
562}
563#endif
564
565/* Clean a single torrent
566 return 1 if torrent timed out
567*/
568static int clean_single_torrent( ot_torrent *torrent ) {
569 ot_peerlist *peer_list = torrent->peer_list;
570 size_t peers_count = 0, seeds_count;
571 time_t timedout = (int)( NOW - peer_list->base );
572 int i;
573#ifdef WANT_TRACKER_SYNC
574 char *new_peers;
575#endif
576
577 /* Torrent has idled out */
578 if( timedout > OT_TORRENT_TIMEOUT )
579 return 1;
580
581 /* Nothing to be cleaned here? Test if torrent is worth keeping */
582 if( timedout > OT_POOLS_COUNT ) {
583 if( !peer_list->peer_count )
584 return peer_list->down_count ? 0 : 1;
585 timedout = OT_POOLS_COUNT;
586 }
587
588 /* Release vectors that have timed out */
589 for( i = OT_POOLS_COUNT - timedout; i < OT_POOLS_COUNT; ++i )
590 free( peer_list->peers[i].data);
591
592 /* Shift vectors back by the amount of pools that were shifted out */
593 memmove( peer_list->peers + timedout, peer_list->peers, sizeof( ot_vector ) * ( OT_POOLS_COUNT - timedout ) );
594 byte_zero( peer_list->peers, sizeof( ot_vector ) * timedout );
595
596 /* Shift back seed counts as well */
597 memmove( peer_list->seed_counts + timedout, peer_list->seed_counts, sizeof( size_t ) * ( OT_POOLS_COUNT - timedout ) );
598 byte_zero( peer_list->seed_counts, sizeof( size_t ) * timedout );
599
600#ifdef WANT_TRACKER_SYNC
601 /* Save the block modified within last OT_POOLS_TIMEOUT */
602 if( peer_list->peers[1].size &&
603 ( new_peers = realloc( peer_list->changeset.data, sizeof( ot_peer ) * peer_list->peers[1].size ) ) )
604 {
605 memmove( new_peers, peer_list->peers[1].data, peer_list->peers[1].size );
606 peer_list->changeset.data = new_peers;
607 peer_list->changeset.size = sizeof( ot_peer ) * peer_list->peers[1].size;
608 } else {
609 free( peer_list->changeset.data );
610
611 memset( &peer_list->changeset, 0, sizeof( ot_vector ) );
612 }
613#endif
614
615 peers_count = seeds_count = 0;
616 for( i = 0; i < OT_POOLS_COUNT; ++i ) {
617 peers_count += peer_list->peers[i].size;
618 seeds_count += peer_list->seed_counts[i];
619 }
620 peer_list->seed_count = seeds_count;
621 peer_list->peer_count = peers_count;
622
623 if( peers_count )
624 peer_list->base = NOW;
625 else {
626 /* When we got here, the last time that torrent
627 has been touched is OT_POOLS_COUNT units before */
628 peer_list->base = NOW - OT_POOLS_COUNT;
629 }
630 return 0;
631}
632
633/* Clean up all peers in current bucket, remove timedout pools and
634 torrents */
635void clean_all_torrents( void ) {
636 ot_vector *torrents_list;
637 size_t i;
638 static int bucket;
639 ot_time time_now = NOW;
640
641 /* Search for an uncleaned bucked */
642 while( ( all_torrents_clean[bucket] == time_now ) && ( ++bucket < OT_BUCKET_COUNT ) );
643 if( bucket >= OT_BUCKET_COUNT ) {
644 bucket = 0; return;
645 }
646
647 all_torrents_clean[bucket] = time_now;
648
649 mutex_bucket_lock( bucket );
650 torrents_list = all_torrents + bucket;
651 for( i=0; i<torrents_list->size; ++i ) {
652 ot_torrent *torrent = ((ot_torrent*)(torrents_list->data)) + i;
653 if( clean_single_torrent( torrent ) ) {
654 vector_remove_torrent( torrents_list, torrent );
655 --i; continue;
656 }
657 }
658 mutex_bucket_unlock( bucket );
659}
660
661typedef struct { size_t val; ot_torrent * torrent; } ot_record;
662
663/* Fetches stats from tracker */
664size_t return_stats_for_tracker( char *reply, int mode ) {
665 size_t torrent_count = 0, peer_count = 0, seed_count = 0, j;
666 ot_record top5s[5], top5c[5];
667 char *r = reply;
668 int bucket;
669
670 byte_zero( top5s, sizeof( top5s ) );
671 byte_zero( top5c, sizeof( top5c ) );
672
673 for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) {
674 ot_vector *torrents_list = all_torrents + bucket;
675 mutex_bucket_lock( bucket );
676 torrent_count += torrents_list->size;
677 for( j=0; j<torrents_list->size; ++j ) {
678 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
679 if( mode == STATS_TOP5 ) {
680 int idx = 4; while( (idx >= 0) && ( peer_list->peer_count > top5c[idx].val ) ) --idx;
681 if ( idx++ != 4 ) {
682 memmove( top5c + idx + 1, top5c + idx, ( 4 - idx ) * sizeof( ot_record ) );
683 top5c[idx].val = peer_list->peer_count;
684 top5c[idx].torrent = (ot_torrent*)(torrents_list->data) + j;
685 }
686 idx = 4; while( (idx >= 0) && ( peer_list->seed_count > top5s[idx].val ) ) --idx;
687 if ( idx++ != 4 ) {
688 memmove( top5s + idx + 1, top5s + idx, ( 4 - idx ) * sizeof( ot_record ) );
689 top5s[idx].val = peer_list->seed_count;
690 top5s[idx].torrent = (ot_torrent*)(torrents_list->data) + j;
691 }
692 }
693 peer_count += peer_list->peer_count; seed_count += peer_list->seed_count;
694 }
695 mutex_bucket_unlock( bucket );
696 }
697 if( mode == STATS_TOP5 ) {
698 char hex_out[42];
699 int idx;
700 r += sprintf( r, "Top5 torrents by peers:\n" );
701 for( idx=0; idx<5; ++idx )
702 if( top5c[idx].torrent )
703 r += sprintf( r, "\t%zd\t%s\n", top5c[idx].val, to_hex( hex_out, top5c[idx].torrent->hash) );
704 r += sprintf( r, "Top5 torrents by seeds:\n" );
705 for( idx=0; idx<5; ++idx )
706 if( top5s[idx].torrent )
707 r += sprintf( r, "\t%zd\t%s\n", top5s[idx].val, to_hex( hex_out, top5s[idx].torrent->hash) );
708 } else
709 r += sprintf( r, "%zd\n%zd\nopentracker serving %zd torrents\nopentracker", peer_count, seed_count, torrent_count );
710
711 return r - reply;
712}
713
714/* This function collects 4096 /24s in 4096 possible
715 malloc blocks
716*/
717size_t return_stats_for_slash24s( char *reply, size_t amount, ot_dword thresh ) {
718
719#define NUM_TOPBITS 12
720#define NUM_LOWBITS (24-NUM_TOPBITS)
721#define NUM_BUFS (1<<NUM_TOPBITS)
722#define NUM_S24S (1<<NUM_LOWBITS)
723#define MSK_S24S (NUM_S24S-1)
724
725 ot_dword *counts[ NUM_BUFS ];
726 ot_dword slash24s[amount*2]; /* first dword amount, second dword subnet */
727 int bucket;
728 size_t i, j, k, l;
729 char *r = reply;
730
731 byte_zero( counts, sizeof( counts ) );
732 byte_zero( slash24s, amount * 2 * sizeof(ot_dword) );
733
734 r += sprintf( r, "Stats for all /24s with more than %u announced torrents:\n\n", thresh );
735
736 for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) {
737 ot_vector *torrents_list = all_torrents + bucket;
738 mutex_bucket_lock( bucket );
739 for( j=0; j<torrents_list->size; ++j ) {
740 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
741 for( k=0; k<OT_POOLS_COUNT; ++k ) {
742 ot_peer *peers = peer_list->peers[k].data;
743 size_t numpeers = peer_list->peers[k].size;
744 for( l=0; l<numpeers; ++l ) {
745 ot_dword s24 = ntohl(*(ot_dword*)(peers+l)) >> 8;
746 ot_dword *count = counts[ s24 >> NUM_LOWBITS ];
747 if( !count ) {
748 count = malloc( sizeof(ot_dword) * NUM_S24S );
749 if( !count )
750 goto bailout_cleanup;
751 byte_zero( count, sizeof( ot_dword ) * NUM_S24S );
752 counts[ s24 >> NUM_LOWBITS ] = count;
753 }
754 count[ s24 & MSK_S24S ]++;
755 }
756 }
757 }
758 mutex_bucket_unlock( bucket );
759 }
760
761 k = l = 0; /* Debug: count allocated bufs */
762 for( i=0; i < NUM_BUFS; ++i ) {
763 ot_dword *count = counts[i];
764 if( !counts[i] )
765 continue;
766 ++k; /* Debug: count allocated bufs */
767 for( j=0; j < NUM_S24S; ++j ) {
768 if( count[j] > thresh ) {
769 /* This subnet seems to announce more torrents than the last in our list */
770 int insert_pos = amount - 1;
771 while( ( insert_pos >= 0 ) && ( count[j] > slash24s[ 2 * insert_pos ] ) )
772 --insert_pos;
773 ++insert_pos;
774 memmove( slash24s + 2 * ( insert_pos + 1 ), slash24s + 2 * ( insert_pos ), 2 * sizeof( ot_dword ) * ( amount - insert_pos - 1 ) );
775 slash24s[ 2 * insert_pos ] = count[j];
776 slash24s[ 2 * insert_pos + 1 ] = ( i << NUM_TOPBITS ) + j;
777 if( slash24s[ 2 * amount - 2 ] > thresh )
778 thresh = slash24s[ 2 * amount - 2 ];
779 }
780 if( count[j] ) ++l;
781 }
782 free( count );
783 }
784
785 r += sprintf( r, "Allocated bufs: %zd, used s24s: %zd\n", k, l );
786
787 for( i=0; i < amount; ++i )
788 if( slash24s[ 2*i ] >= thresh ) {
789 ot_dword ip = slash24s[ 2*i +1 ];
790 r += sprintf( r, "% 10ld %d.%d.%d.0/24\n", (long)slash24s[ 2*i ], (int)(ip >> 16), (int)(255 & ( ip >> 8 )), (int)(ip & 255) );
791 }
792
793 return r - reply; 318 return r - reply;
794
795bailout_cleanup:
796
797 for( i=0; i < NUM_BUFS; ++i )
798 free( counts[i] );
799
800 return 0;
801} 319}
802 320
803size_t remove_peer_from_torrent( ot_hash *hash, ot_peer *peer, char *reply, int is_tcp ) { 321size_t remove_peer_from_torrent( ot_hash *hash, ot_peer *peer, char *reply, int is_tcp ) {
804 int exactmatch; 322 int exactmatch;
805 size_t index; 323 size_t index;
806 ot_vector *torrents_list = lock_bucket_by_hash( hash ); 324 ot_vector *torrents_list = mutex_bucket_lock_by_hash( hash );
807 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 325 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch );
808 ot_peerlist *peer_list; 326 ot_peerlist *peer_list;
809 327
810 if( !exactmatch ) { 328 if( !exactmatch ) {
811 unlock_bucket_by_hash( hash ); 329 mutex_bucket_unlock_by_hash( hash );
812 330
813 if( is_tcp ) 331 if( is_tcp )
814 return sprintf( reply, "d8:completei0e10:incompletei0e8:intervali%ie5:peers0:e", OT_CLIENT_REQUEST_INTERVAL_RANDOM ); 332 return sprintf( reply, "d8:completei0e10:incompletei0e8:intervali%ie5:peers0:e", OT_CLIENT_REQUEST_INTERVAL_RANDOM );
@@ -835,7 +353,7 @@ exit_loop:
835 353
836 if( is_tcp ) { 354 if( is_tcp ) {
837 size_t reply_size = sprintf( reply, "d8:completei%zde10:incompletei%zde8:intervali%ie5:peers0:e", peer_list->seed_count, peer_list->peer_count - peer_list->seed_count, OT_CLIENT_REQUEST_INTERVAL_RANDOM ); 355 size_t reply_size = sprintf( reply, "d8:completei%zde10:incompletei%zde8:intervali%ie5:peers0:e", peer_list->seed_count, peer_list->peer_count - peer_list->seed_count, OT_CLIENT_REQUEST_INTERVAL_RANDOM );
838 unlock_bucket_by_hash( hash ); 356 mutex_bucket_unlock_by_hash( hash );
839 return reply_size; 357 return reply_size;
840 } 358 }
841 359
@@ -844,7 +362,7 @@ exit_loop:
844 ((ot_dword*)reply)[3] = peer_list->peer_count - peer_list->seed_count; 362 ((ot_dword*)reply)[3] = peer_list->peer_count - peer_list->seed_count;
845 ((ot_dword*)reply)[4] = peer_list->seed_count; 363 ((ot_dword*)reply)[4] = peer_list->seed_count;
846 364
847 unlock_bucket_by_hash( hash ); 365 mutex_bucket_unlock_by_hash( hash );
848 return (size_t)20; 366 return (size_t)20;
849} 367}
850 368
@@ -874,30 +392,28 @@ int trackerlogic_init( const char * const serverdir ) {
874 } 392 }
875 393
876 srandom( time(NULL) ); 394 srandom( time(NULL) );
877 395
878 /* Initialize control structures */ 396 clean_init( );
879 byte_zero( all_torrents, sizeof( all_torrents ) );
880
881 mutex_init( ); 397 mutex_init( );
882 398
883 return 0; 399 return 0;
884} 400}
885 401
886void trackerlogic_deinit( void ) { 402void trackerlogic_deinit( void ) {
887 int i; 403 int bucket;
888 size_t j; 404 size_t j;
889 405
890 /* Free all torrents... */ 406 /* Free all torrents... */
891 for(i=0; i<OT_BUCKET_COUNT; ++i ) { 407 for(bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) {
892 if( all_torrents[i].size ) { 408 ot_vector *torrents_list = mutex_bucket_lock( bucket );
893 ot_torrent *torrents_list = (ot_torrent*)all_torrents[i].data; 409 if( torrents_list->size ) {
894 for( j=0; j<all_torrents[i].size; ++j ) 410 for( j=0; j<torrents_list->size; ++j ) {
895 free_peerlist( torrents_list[j].peer_list ); 411 ot_torrent *torrent = ((ot_torrent*)(torrents_list->data)) + j;
896 free( all_torrents[i].data ); 412 free_peerlist( torrent->peer_list );
413 }
414 free( torrents_list->data );
897 } 415 }
898 } 416 }
899 byte_zero( all_torrents, sizeof (all_torrents));
900 byte_zero( all_torrents_clean, sizeof (all_torrents_clean));
901
902 mutex_deinit( ); 417 mutex_deinit( );
903} 418 clean_deinit( );
419} \ No newline at end of file