summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--opentracker.c3
-rw-r--r--trackerlogic.c348
-rw-r--r--trackerlogic.h38
4 files changed, 195 insertions, 196 deletions
diff --git a/Makefile b/Makefile
index 11e4c50..2e2cfb8 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
1CC?=gcc 1CC?=gcc
2FEATURES=#-DWANT_CLOSED_TRACKER -DWANT_IP_FROM_QUERY_STRING -D_DEBUG_HTTPERROR 2FEATURES=#-DWANT_CLOSED_TRACKER -DWANT_UTORRENT1600_WORKAROUND #-DWANT_IP_FROM_QUERY_STRING -D_DEBUG_HTTPERROR -DWANT_TRACKER_SYNC
3OPTS_debug=-g -ggdb #-pg # -fprofile-arcs -ftest-coverage 3OPTS_debug=-g -ggdb #-pg # -fprofile-arcs -ftest-coverage
4OPTS_production=-s -Os 4OPTS_production=-s -Os
5CFLAGS+=-I../libowfat -Wall -pipe -Wextra #-pedantic #-ansi 5CFLAGS+=-I../libowfat -Wall -pipe -Wextra #-pedantic #-ansi
diff --git a/opentracker.c b/opentracker.c
index cfa051a..d9336da 100644
--- a/opentracker.c
+++ b/opentracker.c
@@ -243,7 +243,7 @@ static void httpresponse( const int64 s, char *data, size_t l ) {
243 for( c = data+4; *c == '/'; ++c); 243 for( c = data+4; *c == '/'; ++c);
244 244
245 switch( scan_urlencoded_query( &c, data = c, SCAN_PATH ) ) { 245 switch( scan_urlencoded_query( &c, data = c, SCAN_PATH ) ) {
246 246#ifdef WANT_TRACKER_SYNC
247/****************************** 247/******************************
248 * S Y N C * 248 * S Y N C *
249 ******************************/ 249 ******************************/
@@ -286,6 +286,7 @@ LOG_TO_STDERR( "sync: %d.%d.%d.%d\n", h->ip[0], h->ip[1], h->ip[2], h->ip[3] );
286 reply_size = 2; 286 reply_size = 2;
287 287
288 break; 288 break;
289#endif
289/****************************** 290/******************************
290 * S T A T S * 291 * S T A T S *
291 ******************************/ 292 ******************************/
diff --git a/trackerlogic.c b/trackerlogic.c
index 3560834..3aa3752 100644
--- a/trackerlogic.c
+++ b/trackerlogic.c
@@ -20,7 +20,8 @@
20#include "byte.h" 20#include "byte.h"
21 21
22/* GLOBAL VARIABLES */ 22/* GLOBAL VARIABLES */
23static ot_vector all_torrents[256]; 23static ot_vector all_torrents[OT_BUCKET_COUNT];
24static ot_time all_torrents_clean[OT_BUCKET_COUNT];
24static ot_vector changeset; 25static ot_vector changeset;
25#if defined ( WANT_BLACKLISTING ) || defined( WANT_CLOSED_TRACKER ) 26#if defined ( WANT_BLACKLISTING ) || defined( WANT_CLOSED_TRACKER )
26static ot_vector accesslist; 27static ot_vector accesslist;
@@ -28,7 +29,8 @@ static ot_vector accesslist;
28#endif 29#endif
29 30
30static size_t changeset_size = 0; 31static size_t changeset_size = 0;
31static time_t last_clean_time = 0; 32
33static int clean_single_torrent( ot_torrent *torrent );
32 34
33/* Converter function from memory to human readable hex strings 35/* Converter function from memory to human readable hex strings
34 - definitely not thread safe!!! 36 - definitely not thread safe!!!
@@ -127,19 +129,10 @@ static void free_peerlist( ot_peerlist *peer_list ) {
127 free( peer_list ); 129 free( peer_list );
128} 130}
129 131
130/* This is the non-generic delete from vector-operation specialized for torrents in buckets. 132static void vector_remove_torrent( ot_vector *vector, ot_torrent *match ) {
131 it returns 0 if the hash wasn't found in vector
132 1 if the torrent was removed from vector
133*/
134static int vector_remove_torrent( ot_vector *vector, ot_hash *hash ) {
135 int exactmatch;
136 ot_torrent *end = ((ot_torrent*)vector->data) + vector->size; 133 ot_torrent *end = ((ot_torrent*)vector->data) + vector->size;
137 ot_torrent *match;
138 134
139 if( !vector->size ) return 0; 135 if( !vector->size ) return;
140
141 match = binary_search( hash, vector->data, vector->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch );
142 if( !exactmatch ) return 0;
143 136
144 /* If this is being called after a unsuccessful malloc() for peer_list 137 /* If this is being called after a unsuccessful malloc() for peer_list
145 in add_peer_to_torrent, match->peer_list actually might be NULL */ 138 in add_peer_to_torrent, match->peer_list actually might be NULL */
@@ -150,14 +143,13 @@ static int vector_remove_torrent( ot_vector *vector, ot_hash *hash ) {
150 vector->space /= OT_VECTOR_SHRINK_RATIO; 143 vector->space /= OT_VECTOR_SHRINK_RATIO;
151 vector->data = realloc( vector->data, vector->space * sizeof( ot_torrent ) ); 144 vector->data = realloc( vector->data, vector->space * sizeof( ot_torrent ) );
152 } 145 }
153 return 1;
154} 146}
155 147
156ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer, int from_changeset ) { 148ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer, int from_changeset ) {
157 int exactmatch; 149 int exactmatch;
158 ot_torrent *torrent; 150 ot_torrent *torrent;
159 ot_peer *peer_dest; 151 ot_peer *peer_dest;
160 ot_vector *torrents_list = &all_torrents[*hash[0]], *peer_pool; 152 ot_vector *torrents_list = hash_to_bucket( all_torrents, hash ), *peer_pool;
161 int base_pool = 0; 153 int base_pool = 0;
162 154
163#ifdef WANT_ACCESS_CONTROL 155#ifdef WANT_ACCESS_CONTROL
@@ -179,13 +171,14 @@ ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer, int from_changese
179 memmove( &torrent->hash, hash, sizeof( ot_hash ) ); 171 memmove( &torrent->hash, hash, sizeof( ot_hash ) );
180 172
181 if( !( torrent->peer_list = malloc( sizeof (ot_peerlist) ) ) ) { 173 if( !( torrent->peer_list = malloc( sizeof (ot_peerlist) ) ) ) {
182 vector_remove_torrent( torrents_list, hash ); 174 vector_remove_torrent( torrents_list, torrent );
183 return NULL; 175 return NULL;
184 } 176 }
185 177
186 byte_zero( torrent->peer_list, sizeof( ot_peerlist ) ); 178 byte_zero( torrent->peer_list, sizeof( ot_peerlist ) );
187 torrent->peer_list->base = NOW; 179 torrent->peer_list->base = NOW;
188 } 180 } else
181 clean_single_torrent( torrent );
189 182
190 /* Sanitize flags: Whoever claims to have completed download, must be a seeder */ 183 /* Sanitize flags: Whoever claims to have completed download, must be a seeder */
191 if( ( OT_FLAG( peer ) & ( PEER_FLAG_COMPLETED | PEER_FLAG_SEEDING ) ) == PEER_FLAG_COMPLETED ) 184 if( ( OT_FLAG( peer ) & ( PEER_FLAG_COMPLETED | PEER_FLAG_SEEDING ) ) == PEER_FLAG_COMPLETED )
@@ -208,27 +201,37 @@ ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer, int from_changese
208 if( !exactmatch ) { 201 if( !exactmatch ) {
209 int i; 202 int i;
210 memmove( peer_dest, peer, sizeof( ot_peer ) ); 203 memmove( peer_dest, peer, sizeof( ot_peer ) );
204 torrent->peer_list->peer_count++;
211 205
212 if( OT_FLAG( peer ) & PEER_FLAG_COMPLETED ) 206 if( OT_FLAG( peer ) & PEER_FLAG_COMPLETED )
213 torrent->peer_list->downloaded++; 207 torrent->peer_list->down_count++;
214 208
215 if( OT_FLAG(peer) & PEER_FLAG_SEEDING ) 209 if( OT_FLAG(peer) & PEER_FLAG_SEEDING ) {
216 torrent->peer_list->seed_count[ base_pool ]++; 210 torrent->peer_list->seed_counts[ base_pool ]++;
211 torrent->peer_list->seed_count++;
212 }
217 213
218 for( i= base_pool + 1; i<OT_POOLS_COUNT; ++i ) { 214 for( i= base_pool + 1; i<OT_POOLS_COUNT; ++i ) {
219 switch( vector_remove_peer( &torrent->peer_list->peers[i], peer, 0 ) ) { 215 switch( vector_remove_peer( &torrent->peer_list->peers[i], peer, 0 ) ) {
220 case 0: continue; 216 case 0: continue;
221 case 2: torrent->peer_list->seed_count[i]--; 217 case 2: torrent->peer_list->seed_counts[i]--;
222 case 1: default: return torrent; 218 torrent->peer_list->seed_count--;
219 case 1: default:
220 torrent->peer_list->peer_count--;
221 return torrent;
223 } 222 }
224 } 223 }
225 } else { 224 } else {
226 if( (OT_FLAG(peer_dest) & PEER_FLAG_SEEDING ) && !(OT_FLAG(peer) & PEER_FLAG_SEEDING ) ) 225 if( (OT_FLAG(peer_dest) & PEER_FLAG_SEEDING ) && !(OT_FLAG(peer) & PEER_FLAG_SEEDING ) ) {
227 torrent->peer_list->seed_count[ base_pool ]--; 226 torrent->peer_list->seed_counts[ base_pool ]--;
228 if( !(OT_FLAG(peer_dest) & PEER_FLAG_SEEDING ) && (OT_FLAG(peer) & PEER_FLAG_SEEDING ) ) 227 torrent->peer_list->seed_count--;
229 torrent->peer_list->seed_count[ base_pool ]++; 228 }
229 if( !(OT_FLAG(peer_dest) & PEER_FLAG_SEEDING ) && (OT_FLAG(peer) & PEER_FLAG_SEEDING ) ) {
230 torrent->peer_list->seed_counts[ base_pool ]++;
231 torrent->peer_list->seed_count++;
232 }
230 if( !(OT_FLAG( peer_dest ) & PEER_FLAG_COMPLETED ) && (OT_FLAG( peer ) & PEER_FLAG_COMPLETED ) ) 233 if( !(OT_FLAG( peer_dest ) & PEER_FLAG_COMPLETED ) && (OT_FLAG( peer ) & PEER_FLAG_COMPLETED ) )
231 torrent->peer_list->downloaded++; 234 torrent->peer_list->down_count++;
232 if( OT_FLAG( peer_dest ) & PEER_FLAG_COMPLETED ) 235 if( OT_FLAG( peer_dest ) & PEER_FLAG_COMPLETED )
233 OT_FLAG( peer ) |= PEER_FLAG_COMPLETED; 236 OT_FLAG( peer ) |= PEER_FLAG_COMPLETED;
234 237
@@ -245,29 +248,25 @@ ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer, int from_changese
245 * does not yet check not to return self 248 * does not yet check not to return self
246*/ 249*/
247size_t return_peers_for_torrent( ot_torrent *torrent, size_t amount, char *reply, int is_tcp ) { 250size_t return_peers_for_torrent( ot_torrent *torrent, size_t amount, char *reply, int is_tcp ) {
248 char *r = reply; 251 char *r = reply;
249 size_t peer_count, seed_count, index; 252 ot_peerlist *peer_list = torrent->peer_list;
250 253 size_t index;
251 for( peer_count = seed_count = index = 0; index < OT_POOLS_COUNT; ++index ) {
252 peer_count += torrent->peer_list->peers[index].size;
253 seed_count += torrent->peer_list->seed_count[index];
254 }
255 254
256 if( peer_count < amount ) 255 if( peer_list->peer_count < amount )
257 amount = peer_count; 256 amount = peer_list->peer_count;
258 257
259 if( is_tcp ) 258 if( is_tcp )
260 r += sprintf( r, "d8:completei%zde10:incompletei%zde8:intervali%ie5:peers%zd:", seed_count, peer_count-seed_count, OT_CLIENT_REQUEST_INTERVAL_RANDOM, 6*amount ); 259 r += sprintf( r, "d8:completei%zde10:incompletei%zde8:intervali%ie5:peers%zd:", peer_list->seed_count, peer_list->peer_count-peer_list->seed_count, OT_CLIENT_REQUEST_INTERVAL_RANDOM, 6*amount );
261 else { 260 else {
262 *(ot_dword*)(r+0) = htonl( OT_CLIENT_REQUEST_INTERVAL_RANDOM ); 261 *(ot_dword*)(r+0) = htonl( OT_CLIENT_REQUEST_INTERVAL_RANDOM );
263 *(ot_dword*)(r+4) = htonl( peer_count ); 262 *(ot_dword*)(r+4) = htonl( peer_list->peer_count );
264 *(ot_dword*)(r+8) = htonl( seed_count ); 263 *(ot_dword*)(r+8) = htonl( peer_list->seed_count );
265 r += 12; 264 r += 12;
266 } 265 }
267 266
268 if( amount ) { 267 if( amount ) {
269 unsigned int pool_offset, pool_index = 0;; 268 unsigned int pool_offset, pool_index = 0;;
270 unsigned int shifted_pc = peer_count; 269 unsigned int shifted_pc = peer_list->peer_count;
271 unsigned int shifted_step = 0; 270 unsigned int shifted_step = 0;
272 unsigned int shift = 0; 271 unsigned int shift = 0;
273 272
@@ -279,7 +278,7 @@ size_t return_peers_for_torrent( ot_torrent *torrent, size_t amount, char *reply
279 278
280 /* Initialize somewhere in the middle of peers so that 279 /* Initialize somewhere in the middle of peers so that
281 fixpoint's aliasing doesn't alway miss the same peers */ 280 fixpoint's aliasing doesn't alway miss the same peers */
282 pool_offset = random() % peer_count; 281 pool_offset = random() % peer_list->peer_count;
283 282
284 for( index = 0; index < amount; ++index ) { 283 for( index = 0; index < amount; ++index ) {
285 /* This is the aliased, non shifted range, next value may fall into */ 284 /* This is the aliased, non shifted range, next value may fall into */
@@ -287,12 +286,12 @@ size_t return_peers_for_torrent( ot_torrent *torrent, size_t amount, char *reply
287 ( ( index * shifted_step ) >> shift ); 286 ( ( index * shifted_step ) >> shift );
288 pool_offset += 1 + random() % diff; 287 pool_offset += 1 + random() % diff;
289 288
290 while( pool_offset >= torrent->peer_list->peers[pool_index].size ) { 289 while( pool_offset >= peer_list->peers[pool_index].size ) {
291 pool_offset -= torrent->peer_list->peers[pool_index].size; 290 pool_offset -= peer_list->peers[pool_index].size;
292 pool_index = ( pool_index + 1 ) % OT_POOLS_COUNT; 291 pool_index = ( pool_index + 1 ) % OT_POOLS_COUNT;
293 } 292 }
294 293
295 memmove( r, ((ot_peer*)torrent->peer_list->peers[pool_index].data) + pool_offset, 6 ); 294 memmove( r, ((ot_peer*)peer_list->peers[pool_index].data) + pool_offset, 6 );
296 r += 6; 295 r += 6;
297 } 296 }
298 } 297 }
@@ -316,10 +315,10 @@ static void fix_mmapallocation( void *buf, size_t old_alloc, size_t new_alloc )
316size_t return_fullscrape_for_tracker( char **reply ) { 315size_t return_fullscrape_for_tracker( char **reply ) {
317 size_t torrent_count = 0, j; 316 size_t torrent_count = 0, j;
318 size_t allocated, replysize; 317 size_t allocated, replysize;
319 int i, k; 318 int i;
320 char *r; 319 char *r;
321 320
322 for( i=0; i<256; ++i ) 321 for( i=0; i<OT_BUCKET_COUNT; ++i )
323 torrent_count += all_torrents[i].size; 322 torrent_count += all_torrents[i].size;
324 323
325 /* one extra for pro- and epilogue */ 324 /* one extra for pro- and epilogue */
@@ -327,20 +326,15 @@ size_t return_fullscrape_for_tracker( char **reply ) {
327 if( !( r = *reply = mmap( NULL, allocated, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 ) ) ) return 0; 326 if( !( r = *reply = mmap( NULL, allocated, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 ) ) ) return 0;
328 327
329 memmove( r, "d5:filesd", 9 ); r += 9; 328 memmove( r, "d5:filesd", 9 ); r += 9;
330 for( i=0; i<256; ++i ) { 329 for( i=0; i<OT_BUCKET_COUNT; ++i ) {
331 ot_vector *torrents_list = &all_torrents[i]; 330 ot_vector *torrents_list = all_torrents + i;
332 for( j=0; j<torrents_list->size; ++j ) { 331 for( j=0; j<torrents_list->size; ++j ) {
333 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list; 332 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
334 ot_hash *hash =&( ((ot_torrent*)(torrents_list->data))[j] ).hash; 333 ot_hash *hash =&( ((ot_torrent*)(torrents_list->data))[j] ).hash;
335 size_t peers = 0, seeds = 0; 334 if( peer_list->peer_count || peer_list->down_count ) {
336 for( k=0; k<OT_POOLS_COUNT; ++k ) {
337 peers += peer_list->peers[k].size;
338 seeds += peer_list->seed_count[k];
339 }
340 if( peers || peer_list->downloaded ) {
341 *r++='2'; *r++='0'; *r++=':'; 335 *r++='2'; *r++='0'; *r++=':';
342 memmove( r, hash, 20 ); r+=20; 336 memmove( r, hash, 20 ); r+=20;
343 r += sprintf( r, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", seeds, peer_list->downloaded, peers-seeds ); 337 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 );
344 } 338 }
345 } 339 }
346 } 340 }
@@ -359,19 +353,19 @@ size_t return_memstat_for_tracker( char **reply ) {
359 int i, k; 353 int i, k;
360 char *r; 354 char *r;
361 355
362 for( i=0; i<256; ++i ) { 356 for( i=0; i<OT_BUCKET_COUNT; ++i ) {
363 ot_vector *torrents_list = &all_torrents[i]; 357 ot_vector *torrents_list = all_torrents + i;
364 torrent_count += torrents_list->size; 358 torrent_count += torrents_list->size;
365 } 359 }
366 360
367 allocated = 256*32 + (43+OT_POOLS_COUNT*32)*torrent_count; 361 allocated = OT_BUCKET_COUNT*32 + (43+OT_POOLS_COUNT*32)*torrent_count;
368 if( !( r = *reply = mmap( NULL, allocated, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 ) ) ) return 0; 362 if( !( r = *reply = mmap( NULL, allocated, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 ) ) ) return 0;
369 363
370 for( i=0; i<256; ++i ) 364 for( i=0; i<OT_BUCKET_COUNT; ++i )
371 r += sprintf( r, "%02X: %08X %08X\n", i, (unsigned int)all_torrents[i].size, (unsigned int)all_torrents[i].space ); 365 r += sprintf( r, "%02X: %08X %08X\n", i, (unsigned int)all_torrents[i].size, (unsigned int)all_torrents[i].space );
372 366
373 for( i=0; i<256; ++i ) { 367 for( i=0; i<OT_BUCKET_COUNT; ++i ) {
374 ot_vector *torrents_list = &all_torrents[i]; 368 ot_vector *torrents_list = all_torrents + i;
375 for( j=0; j<torrents_list->size; ++j ) { 369 for( j=0; j<torrents_list->size; ++j ) {
376 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list; 370 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
377 ot_hash *hash =&( ((ot_torrent*)(torrents_list->data))[j] ).hash; 371 ot_hash *hash =&( ((ot_torrent*)(torrents_list->data))[j] ).hash;
@@ -389,9 +383,8 @@ size_t return_memstat_for_tracker( char **reply ) {
389 383
390/* Fetches scrape info for a specific torrent */ 384/* Fetches scrape info for a specific torrent */
391size_t return_udp_scrape_for_torrent( ot_hash *hash, char *reply ) { 385size_t return_udp_scrape_for_torrent( ot_hash *hash, char *reply ) {
392 int exactmatch, i; 386 int exactmatch ;
393 size_t peers = 0, seeds = 0; 387 ot_vector *torrents_list = hash_to_bucket( all_torrents, hash );
394 ot_vector *torrents_list = &all_torrents[*hash[0]];
395 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 388 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch );
396 389
397 if( !exactmatch ) { 390 if( !exactmatch ) {
@@ -399,13 +392,14 @@ size_t return_udp_scrape_for_torrent( ot_hash *hash, char *reply ) {
399 } else { 392 } else {
400 ot_dword *r = (ot_dword*) reply; 393 ot_dword *r = (ot_dword*) reply;
401 394
402 for( i=0; i<OT_POOLS_COUNT; ++i ) { 395 if( clean_single_torrent( torrent ) ) {
403 peers += torrent->peer_list->peers[i].size; 396 vector_remove_torrent( torrents_list, torrent );
404 seeds += torrent->peer_list->seed_count[i]; 397 memset( reply, 0, 12);
398 } else {
399 r[0] = htonl( torrent->peer_list->seed_count );
400 r[1] = htonl( torrent->peer_list->down_count );
401 r[2] = htonl( torrent->peer_list->peer_count-torrent->peer_list->seed_count );
405 } 402 }
406 r[0] = htonl( seeds );
407 r[1] = htonl( torrent->peer_list->downloaded );
408 r[2] = htonl( peers-seeds );
409 } 403 }
410 return 12; 404 return 12;
411} 405}
@@ -413,31 +407,30 @@ size_t return_udp_scrape_for_torrent( ot_hash *hash, char *reply ) {
413/* Fetches scrape info for a specific torrent */ 407/* Fetches scrape info for a specific torrent */
414size_t return_tcp_scrape_for_torrent( ot_hash *hash_list, int amount, char *reply ) { 408size_t return_tcp_scrape_for_torrent( ot_hash *hash_list, int amount, char *reply ) {
415 char *r = reply; 409 char *r = reply;
416 int exactmatch, i, j; 410 int exactmatch, i;
417 411
418 r += sprintf( r, "d5:filesd" ); 412 r += sprintf( r, "d5:filesd" );
419 413
420 for( i=0; i<amount; ++i ) { 414 for( i=0; i<amount; ++i ) {
421 ot_hash *hash = hash_list + i; 415 ot_hash *hash = hash_list + i;
422 ot_vector *torrents_list = &all_torrents[*hash[0]]; 416 ot_vector *torrents_list = hash_to_bucket( all_torrents, hash );
423 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 417 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch );
424 size_t peers = 0, seeds = 0;
425 418
426 if( !exactmatch ) continue; 419 if( !exactmatch ) continue;
427 420 if( clean_single_torrent( torrent ) ) {
428 for( j=0; j<OT_POOLS_COUNT; ++j ) { 421 vector_remove_torrent( torrents_list, torrent );
429 peers += torrent->peer_list->peers[j].size; 422 } else {
430 seeds += torrent->peer_list->seed_count[j]; 423 memmove( r, "20:", 3 ); memmove( r+3, hash, 20 );
424 r += sprintf( r+23, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee",
425 torrent->peer_list->seed_count, torrent->peer_list->down_count, torrent->peer_list->peer_count-torrent->peer_list->seed_count ) + 23;
431 } 426 }
432
433 memmove( r, "20:", 3 ); memmove( r+3, hash, 20 );
434 r += sprintf( r+23, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", seeds, torrent->peer_list->downloaded, peers-seeds ) + 23;
435 } 427 }
436 428
437 *r++ = 'e'; *r++ = 'e'; 429 *r++ = 'e'; *r++ = 'e';
438 return r - reply; 430 return r - reply;
439} 431}
440 432
433#ifdef WANT_TRACKER_SYNC
441/* Throw away old changeset */ 434/* Throw away old changeset */
442static void release_changeset( void ) { 435static void release_changeset( void ) {
443 ot_byte **changeset_ptrs = (ot_byte**)(changeset.data); 436 ot_byte **changeset_ptrs = (ot_byte**)(changeset.data);
@@ -548,86 +541,91 @@ size_t return_changeset_for_tracker( char **reply ) {
548 541
549 return r; 542 return r;
550} 543}
544#endif
551 545
552/* Clean up all torrents, remove timedout pools and 546/* Clean a single torrent
553 torrents, also prepare new changeset */ 547 return 1 if torrent timed out
554void clean_all_torrents( void ) { 548*/
555 int i, k; 549static int clean_single_torrent( ot_torrent *torrent ) {
556 size_t j; 550 ot_peerlist *peer_list = torrent->peer_list;
557 time_t time_now = NOW; 551 size_t peers_count = 0, seeds_count;
558 size_t peers_count; 552 time_t timedout = (int)( NOW - peer_list->base );
559 ot_dword diff; struct timeval tv1, tv2; gettimeofday( &tv1, NULL ); 553 int i;
560
561 if( time_now <= last_clean_time )
562 return;
563 last_clean_time = time_now;
564
565 release_changeset();
566 554
567 for( i=0; i<256; ++i ) { 555 /* Torrent has idled out */
568 ot_vector *torrents_list = &all_torrents[i]; 556 if( timedout > OT_TORRENT_TIMEOUT )
569 for( j=0; j<torrents_list->size; ++j ) { 557 return 1;
570 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
571 ot_hash *hash =&( ((ot_torrent*)(torrents_list->data))[j] ).hash;
572 558
573 time_t timedout = (int)( time_now - peer_list->base ); 559 /* Nothing to be cleaned here? Test if torrent is worth keeping */
560 if( timedout > OT_POOLS_COUNT ) {
561 if( !peer_list->peer_count )
562 return peer_list->down_count ? 0 : 1;
563 timedout = OT_POOLS_COUNT;
564 }
574 565
575 /* Torrent has idled out */ 566 /* Release vectors that have timed out */
576 if( timedout > OT_TORRENT_TIMEOUT ) { 567 for( i = OT_POOLS_COUNT - timedout; i < OT_POOLS_COUNT; ++i )
577 vector_remove_torrent( torrents_list, hash ); 568 free( peer_list->peers[i].data);
578 --j; continue;
579 }
580 569
581 /* If nothing to be cleaned here, handle next torrent */ 570 /* Shift vectors back by the amount of pools that were shifted out */
582 if( timedout > OT_POOLS_COUNT ) { 571 memmove( peer_list->peers + timedout, peer_list->peers, sizeof( ot_vector ) * ( OT_POOLS_COUNT - timedout ) );
572 byte_zero( peer_list->peers, sizeof( ot_vector ) * timedout );
583 573
584 peers_count = 0; 574 /* Shift back seed counts as well */
585 for( k = 0; k < OT_POOLS_COUNT; ++k ) 575 memmove( peer_list->seed_counts + timedout, peer_list->seed_counts, sizeof( size_t ) * ( OT_POOLS_COUNT - timedout ) );
586 peers_count += peer_list->peers[k].size; 576 byte_zero( peer_list->seed_counts, sizeof( size_t ) * timedout );
587 577
588 if( !peers_count ) { 578 /* Save the block modified within last OT_POOLS_TIMEOUT --- XXX no sync for now
589 if( !peer_list->downloaded ) { 579 if( peer_list->peers[1].size )
590 vector_remove_torrent( torrents_list, hash ); 580 add_pool_to_changeset( hash, peer_list->peers[1].data, peer_list->peers[1].size );
591 --j; 581 */
592 }
593 continue;
594 }
595 582
596 timedout = OT_POOLS_COUNT; 583 peers_count = seeds_count = 0;
597 } 584 for( i = 0; i < OT_POOLS_COUNT; ++i ) {
585 peers_count += peer_list->peers[i].size;
586 seeds_count += peer_list->seed_counts[i];
587 }
588 peer_list->seed_count = seeds_count;
589 peer_list->peer_count = peers_count;
598 590
599 /* Release vectors that have timed out */ 591 if( peers_count )
600 for( k = OT_POOLS_COUNT - timedout; k < OT_POOLS_COUNT; ++k ) 592 peer_list->base = NOW;
601 free( peer_list->peers[k].data); 593 else {
594 /* When we got here, the last time that torrent
595 has been touched is OT_POOLS_COUNT units before */
596 peer_list->base = NOW - OT_POOLS_COUNT;
597 }
598 return 0;
599}
602 600
603 /* Shift vectors back by the amount of pools that were shifted out */ 601/* Clean up all peers in current bucket, remove timedout pools and
604 memmove( peer_list->peers + timedout, peer_list->peers, sizeof( ot_vector ) * ( OT_POOLS_COUNT - timedout ) ); 602 torrents */
605 byte_zero( peer_list->peers, sizeof( ot_vector ) * timedout ); 603void clean_all_torrents( void ) {
604 ot_vector *torrents_list;
605 size_t i;
606 static int bucket;
607 ot_time time_now = NOW;
606 608
607 /* Shift back seed counts as well */ 609/* No sync for now
608 memmove( peer_list->seed_count + timedout, peer_list->seed_count, sizeof( size_t ) * ( OT_POOLS_COUNT - timedout ) ); 610 release_changeset();
609 byte_zero( peer_list->seed_count, sizeof( size_t ) * timedout ); 611*/
610 612
611 /* Save the block modified within last OT_POOLS_TIMEOUT */ 613 /* Search for an uncleaned bucked */
612 if( peer_list->peers[1].size ) 614 while( ( all_torrents_clean[bucket] == time_now ) && ( ++bucket < OT_BUCKET_COUNT ) );
613 add_pool_to_changeset( hash, peer_list->peers[1].data, peer_list->peers[1].size ); 615 if( bucket >= OT_BUCKET_COUNT ) {
616 bucket = 0; return;
617 }
614 618
615 peers_count = 0; 619 all_torrents_clean[bucket] = time_now;
616 for( k = 0; k < OT_POOLS_COUNT; ++k )
617 peers_count += peer_list->peers[k].size;
618 620
619 if( peers_count ) { 621 torrents_list = all_torrents + bucket;
620 peer_list->base = time_now; 622 for( i=0; i<torrents_list->size; ++i ) {
621 } else { 623 ot_torrent *torrent = ((ot_torrent*)(torrents_list->data)) + i;
622 /* When we got here, the last time that torrent 624 if( clean_single_torrent( torrent ) ) {
623 has been touched is OT_POOLS_COUNT units before */ 625 vector_remove_torrent( torrents_list, torrent );
624 peer_list->base = time_now - OT_POOLS_COUNT; 626 --i; continue;
625 }
626 } 627 }
627 } 628 }
628
629 gettimeofday( &tv2, NULL ); diff = ( tv2.tv_sec - tv1.tv_sec ) * 1000000 + tv2.tv_usec - tv1.tv_usec;
630 fprintf( stderr, "Cleanup time taken: %u\n", diff );
631} 629}
632 630
633typedef struct { size_t val; ot_torrent * torrent; } ot_record; 631typedef struct { size_t val; ot_torrent * torrent; } ot_record;
@@ -637,37 +635,31 @@ size_t return_stats_for_tracker( char *reply, int mode ) {
637 size_t torrent_count = 0, peer_count = 0, seed_count = 0, j; 635 size_t torrent_count = 0, peer_count = 0, seed_count = 0, j;
638 ot_record top5s[5], top5c[5]; 636 ot_record top5s[5], top5c[5];
639 char *r = reply; 637 char *r = reply;
640 int i,k; 638 int i;
641 639
642 byte_zero( top5s, sizeof( top5s ) ); 640 byte_zero( top5s, sizeof( top5s ) );
643 byte_zero( top5c, sizeof( top5c ) ); 641 byte_zero( top5c, sizeof( top5c ) );
644 642
645 for( i=0; i<256; ++i ) { 643 for( i=0; i<OT_BUCKET_COUNT; ++i ) {
646 ot_vector *torrents_list = &all_torrents[i]; 644 ot_vector *torrents_list = all_torrents + i;
647 torrent_count += torrents_list->size; 645 torrent_count += torrents_list->size;
648 for( j=0; j<torrents_list->size; ++j ) { 646 for( j=0; j<torrents_list->size; ++j ) {
649 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list; 647 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
650 size_t local_peers = 0, local_seeds = 0;
651
652 for( k=0; k<OT_POOLS_COUNT; ++k ) {
653 local_peers += peer_list->peers[k].size;
654 local_seeds += peer_list->seed_count[k];
655 }
656 if( mode == STATS_TOP5 ) { 648 if( mode == STATS_TOP5 ) {
657 int idx = 4; while( (idx >= 0) && ( local_peers > top5c[idx].val ) ) --idx; 649 int idx = 4; while( (idx >= 0) && ( peer_list->peer_count > top5c[idx].val ) ) --idx;
658 if ( idx++ != 4 ) { 650 if ( idx++ != 4 ) {
659 memmove( top5c + idx + 1, top5c + idx, ( 4 - idx ) * sizeof( ot_record ) ); 651 memmove( top5c + idx + 1, top5c + idx, ( 4 - idx ) * sizeof( ot_record ) );
660 top5c[idx].val = local_peers; 652 top5c[idx].val = peer_list->peer_count;
661 top5c[idx].torrent = (ot_torrent*)(torrents_list->data) + j; 653 top5c[idx].torrent = (ot_torrent*)(torrents_list->data) + j;
662 } 654 }
663 idx = 4; while( (idx >= 0) && ( local_seeds > top5s[idx].val ) ) --idx; 655 idx = 4; while( (idx >= 0) && ( peer_list->seed_count > top5s[idx].val ) ) --idx;
664 if ( idx++ != 4 ) { 656 if ( idx++ != 4 ) {
665 memmove( top5s + idx + 1, top5s + idx, ( 4 - idx ) * sizeof( ot_record ) ); 657 memmove( top5s + idx + 1, top5s + idx, ( 4 - idx ) * sizeof( ot_record ) );
666 top5s[idx].val = local_seeds; 658 top5s[idx].val = peer_list->seed_count;
667 top5s[idx].torrent = (ot_torrent*)(torrents_list->data) + j; 659 top5s[idx].torrent = (ot_torrent*)(torrents_list->data) + j;
668 } 660 }
669 } 661 }
670 peer_count += local_peers; seed_count += local_seeds; 662 peer_count += peer_list->peer_count; seed_count += peer_list->seed_count;
671 } 663 }
672 } 664 }
673 if( mode == STATS_TOP5 ) { 665 if( mode == STATS_TOP5 ) {
@@ -708,8 +700,8 @@ size_t return_stats_for_slash24s( char *reply, size_t amount, ot_dword thresh )
708 700
709 r += sprintf( r, "Stats for all /24s with more than %u announced torrents:\n\n", thresh ); 701 r += sprintf( r, "Stats for all /24s with more than %u announced torrents:\n\n", thresh );
710 702
711 for( i=0; i<256; ++i ) { 703 for( i=0; i<OT_BUCKET_COUNT; ++i ) {
712 ot_vector *torrents_list = &all_torrents[i]; 704 ot_vector *torrents_list = all_torrents + i;
713 for( j=0; j<torrents_list->size; ++j ) { 705 for( j=0; j<torrents_list->size; ++j ) {
714 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list; 706 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
715 for( k=0; k<OT_POOLS_COUNT; ++k ) { 707 for( k=0; k<OT_POOLS_COUNT; ++k ) {
@@ -787,8 +779,8 @@ size_t return_stats_for_slash24s_old( char *reply, size_t amount, ot_dword thres
787 779
788 r += sprintf( r, "Stats for all /24s with more than %d announced torrents:\n\n", ((int)thresh) ); 780 r += sprintf( r, "Stats for all /24s with more than %d announced torrents:\n\n", ((int)thresh) );
789 781
790 for( i=0; i<256; ++i ) { 782 for( i=0; i<OT_BUCKET_COUNT; ++i ) {
791 ot_vector *torrents_list = &all_torrents[i]; 783 ot_vector *torrents_list = all_torrents + i;
792 for( j=0; j<torrents_list->size; ++j ) { 784 for( j=0; j<torrents_list->size; ++j ) {
793 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list; 785 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
794 for( k=0; k<OT_POOLS_COUNT; ++k ) { 786 for( k=0; k<OT_POOLS_COUNT; ++k ) {
@@ -828,9 +820,10 @@ size_t return_stats_for_slash24s_old( char *reply, size_t amount, ot_dword thres
828 820
829size_t remove_peer_from_torrent( ot_hash *hash, ot_peer *peer, char *reply, int is_tcp ) { 821size_t remove_peer_from_torrent( ot_hash *hash, ot_peer *peer, char *reply, int is_tcp ) {
830 int exactmatch; 822 int exactmatch;
831 size_t peer_count, seed_count, index; 823 size_t index;
832 ot_vector *torrents_list = &all_torrents[*hash[0]]; 824 ot_vector *torrents_list = hash_to_bucket( all_torrents, hash );
833 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 825 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch );
826 ot_peerlist *peer_list;
834 827
835 if( !exactmatch ) { 828 if( !exactmatch ) {
836 if( is_tcp ) 829 if( is_tcp )
@@ -842,33 +835,27 @@ size_t remove_peer_from_torrent( ot_hash *hash, ot_peer *peer, char *reply, int
842 return (size_t)20; 835 return (size_t)20;
843 } 836 }
844 837
845 for( peer_count = seed_count = index = 0; index<OT_POOLS_COUNT; ++index ) { 838 peer_list = torrent->peer_list;
846 peer_count += torrent->peer_list->peers[index].size; 839 for( index = 0; index<OT_POOLS_COUNT; ++index ) {
847 seed_count += torrent->peer_list->seed_count[index]; 840 switch( vector_remove_peer( &peer_list->peers[index], peer, index == 0 ) ) {
848
849 switch( vector_remove_peer( &torrent->peer_list->peers[index], peer, index == 0 ) ) {
850 case 0: continue; 841 case 0: continue;
851 case 2: torrent->peer_list->seed_count[index]--; 842 case 2: peer_list->seed_counts[index]--;
852 seed_count--; 843 peer_list->seed_count--;
853 case 1: default: 844 case 1: default:
854 peer_count--; 845 peer_list->peer_count--;
855 goto exit_loop; 846 goto exit_loop;
856 } 847 }
857 } 848 }
858 849
859exit_loop: 850exit_loop:
860 for( ++index; index < OT_POOLS_COUNT; ++index ) {
861 peer_count += torrent->peer_list->peers[index].size;
862 seed_count += torrent->peer_list->seed_count[index];
863 }
864 851
865 if( is_tcp ) 852 if( is_tcp )
866 return sprintf( reply, "d8:completei%zde10:incompletei%zde8:intervali%ie5:peers0:e", seed_count, peer_count - seed_count, OT_CLIENT_REQUEST_INTERVAL_RANDOM ); 853 return 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 );
867 854
868 /* else { Handle UDP reply */ 855 /* else { Handle UDP reply */
869 ((ot_dword*)reply)[2] = htonl( OT_CLIENT_REQUEST_INTERVAL_RANDOM ); 856 ((ot_dword*)reply)[2] = htonl( OT_CLIENT_REQUEST_INTERVAL_RANDOM );
870 ((ot_dword*)reply)[3] = peer_count - seed_count; 857 ((ot_dword*)reply)[3] = peer_list->peer_count - peer_list->seed_count;
871 ((ot_dword*)reply)[4] = seed_count; 858 ((ot_dword*)reply)[4] = peer_list->seed_count;
872 return (size_t)20; 859 return (size_t)20;
873} 860}
874 861
@@ -893,7 +880,7 @@ void deinit_logic( void ) {
893 size_t j; 880 size_t j;
894 881
895 /* Free all torrents... */ 882 /* Free all torrents... */
896 for(i=0; i<256; ++i ) { 883 for(i=0; i<OT_BUCKET_COUNT; ++i ) {
897 if( all_torrents[i].size ) { 884 if( all_torrents[i].size ) {
898 ot_torrent *torrents_list = (ot_torrent*)all_torrents[i].data; 885 ot_torrent *torrents_list = (ot_torrent*)all_torrents[i].data;
899 for( j=0; j<all_torrents[i].size; ++j ) 886 for( j=0; j<all_torrents[i].size; ++j )
@@ -902,6 +889,7 @@ void deinit_logic( void ) {
902 } 889 }
903 } 890 }
904 byte_zero( all_torrents, sizeof (all_torrents)); 891 byte_zero( all_torrents, sizeof (all_torrents));
892 byte_zero( all_torrents_clean, sizeof (all_torrents_clean));
905 byte_zero( &changeset, sizeof( changeset ) ); 893 byte_zero( &changeset, sizeof( changeset ) );
906 changeset_size = 0; 894 changeset_size = 0;
907} 895}
diff --git a/trackerlogic.h b/trackerlogic.h
index 96b59f3..fd8f48a 100644
--- a/trackerlogic.h
+++ b/trackerlogic.h
@@ -20,6 +20,16 @@ typedef ot_byte ot_hash[20];
20typedef ot_dword ot_ip; 20typedef ot_dword ot_ip;
21typedef time_t ot_time; 21typedef time_t ot_time;
22 22
23#define OT_VECTOR_MIN_MEMBERS 4
24#define OT_VECTOR_GROW_RATIO 8
25#define OT_VECTOR_SHRINK_THRESH 6
26#define OT_VECTOR_SHRINK_RATIO 4
27typedef struct {
28 void *data;
29 size_t size;
30 size_t space;
31} ot_vector;
32
23/* Some tracker behaviour tunable */ 33/* Some tracker behaviour tunable */
24#define OT_CLIENT_TIMEOUT 30 34#define OT_CLIENT_TIMEOUT 30
25#define OT_CLIENT_TIMEOUT_CHECKINTERVAL 10 35#define OT_CLIENT_TIMEOUT_CHECKINTERVAL 10
@@ -32,8 +42,13 @@ typedef time_t ot_time;
32 42
33#define OT_CLIENT_REQUEST_INTERVAL_RANDOM ( OT_CLIENT_REQUEST_INTERVAL - OT_CLIENT_REQUEST_VARIATION/2 + (int)( random( ) % OT_CLIENT_REQUEST_VARIATION ) ) 43#define OT_CLIENT_REQUEST_INTERVAL_RANDOM ( OT_CLIENT_REQUEST_INTERVAL - OT_CLIENT_REQUEST_VARIATION/2 + (int)( random( ) % OT_CLIENT_REQUEST_VARIATION ) )
34 44
35/* We maintain a list of 256 pointers to sorted list of ot_torrent structs 45/* We maintain a list of 4096 pointers to sorted list of ot_torrent structs
36 Sort key is, of course, its hash */ 46 Sort key is, of course, its hash */
47#define OT_BUCKET_COUNT 1024
48static inline ot_vector *hash_to_bucket( ot_vector *vectors, ot_hash *hash ) {
49 unsigned char *local_hash = hash[0];
50 return vectors + ( ( local_hash[0] << 2 ) | ( local_hash[1] >> 6 ) );
51}
37 52
38/* This list points to 9 pools of peers each grouped in five-minute-intervals 53/* This list points to 9 pools of peers each grouped in five-minute-intervals
39 thus achieving a timeout of 2700s or 45 minutes 54 thus achieving a timeout of 2700s or 45 minutes
@@ -45,16 +60,6 @@ typedef time_t ot_time;
45extern time_t g_now; 60extern time_t g_now;
46#define NOW (g_now/OT_POOLS_TIMEOUT) 61#define NOW (g_now/OT_POOLS_TIMEOUT)
47 62
48#define OT_VECTOR_MIN_MEMBERS 4
49#define OT_VECTOR_GROW_RATIO 8
50#define OT_VECTOR_SHRINK_THRESH 6
51#define OT_VECTOR_SHRINK_RATIO 4
52typedef struct {
53 void *data;
54 size_t size;
55 size_t space;
56} ot_vector;
57
58typedef struct { 63typedef struct {
59 ot_byte data[8]; 64 ot_byte data[8];
60} ot_peer; 65} ot_peer;
@@ -71,8 +76,10 @@ static const ot_byte PEER_FLAG_STOPPED = 0x20;
71 76
72typedef struct { 77typedef struct {
73 ot_time base; 78 ot_time base;
74 size_t seed_count[ OT_POOLS_COUNT ]; 79 size_t seed_count;
75 size_t downloaded; 80 size_t peer_count;
81 size_t down_count;
82 size_t seed_counts[ OT_POOLS_COUNT ];
76 ot_vector peers[ OT_POOLS_COUNT ]; 83 ot_vector peers[ OT_POOLS_COUNT ];
77} ot_peerlist; 84} ot_peerlist;
78 85
@@ -100,9 +107,12 @@ size_t return_stats_for_tracker( char *reply, int mode );
100size_t return_stats_for_slash24s( char *reply, size_t amount, ot_dword thresh ); 107size_t return_stats_for_slash24s( char *reply, size_t amount, ot_dword thresh );
101size_t return_stats_for_slash24s_old( char *reply, size_t amount, ot_dword thresh ); 108size_t return_stats_for_slash24s_old( char *reply, size_t amount, ot_dword thresh );
102size_t return_memstat_for_tracker( char **reply ); 109size_t return_memstat_for_tracker( char **reply );
110void clean_all_torrents( void );
111
112#ifdef WANT_TRACKER_SYNC
103size_t return_changeset_for_tracker( char **reply ); 113size_t return_changeset_for_tracker( char **reply );
104int add_changeset_to_tracker( ot_byte *data, size_t len ); 114int add_changeset_to_tracker( ot_byte *data, size_t len );
105void clean_all_torrents( void ); 115#endif
106 116
107#if defined ( WANT_BLACKLISTING ) || defined ( WANT_CLOSED_TRACKER ) 117#if defined ( WANT_BLACKLISTING ) || defined ( WANT_CLOSED_TRACKER )
108int accesslist_addentry( ot_hash *hash ); 118int accesslist_addentry( ot_hash *hash );