summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--opentracker.c41
-rw-r--r--trackerlogic.c213
-rw-r--r--trackerlogic.h3
3 files changed, 157 insertions, 100 deletions
diff --git a/opentracker.c b/opentracker.c
index b8c90d2..7be1421 100644
--- a/opentracker.c
+++ b/opentracker.c
@@ -200,31 +200,9 @@ static void httpresponse( const int64 s, char *data ) {
200 switch( scan_urlencoded_query( &c, data = c, SCAN_PATH ) ) { 200 switch( scan_urlencoded_query( &c, data = c, SCAN_PATH ) ) {
201 case 4: /* sync ? */ 201 case 4: /* sync ? */
202 if( byte_diff( data, 4, "sync") ) HTTPERROR_404; 202 if( byte_diff( data, 4, "sync") ) HTTPERROR_404;
203 scanon = 1; 203 if( !( reply_size = return_changeset_for_tracker( &reply ) ) ) HTTPERROR_500;
204
205 while( scanon ) {
206 switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) {
207 case -2: scanon = 0; break; /* TERMINATOR */
208 case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */
209 case 9:
210 if(byte_diff(data,9,"info_hash")) {
211 scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE );
212 continue;
213 }
214 /* ignore this, when we have less than 20 bytes */
215 if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM;
216 hash = (ot_hash*)data; /* Fall through intended */
217 break;
218 default:
219 scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE );
220 break;
221 }
222 }
223
224 if( !hash ) HTTPERROR_400_PARAM;
225 if( !( reply_size = return_sync_for_torrent( hash, &reply ) ) ) HTTPERROR_500;
226
227 return sendmallocdata( s, reply, reply_size ); 204 return sendmallocdata( s, reply, reply_size );
205
228 case 5: /* stats ? */ 206 case 5: /* stats ? */
229 if( byte_diff(data,5,"stats")) HTTPERROR_404; 207 if( byte_diff(data,5,"stats")) HTTPERROR_404;
230 scanon = 1; 208 scanon = 1;
@@ -523,11 +501,13 @@ static void handle_read( const int64 clientsocket ) {
523 array_catb( &h->request, static_inbuf, l ); 501 array_catb( &h->request, static_inbuf, l );
524 502
525 if( array_failed( &h->request ) ) 503 if( array_failed( &h->request ) )
526 httperror( clientsocket, "500 Server Error", "Request too long."); 504 return httperror( clientsocket, "500 Server Error", "Request too long.");
527 else if( array_bytes( &h->request ) > 8192 ) 505
528 httperror( clientsocket, "500 request too long", "You sent too much headers"); 506 if( array_bytes( &h->request ) > 8192 )
529 else if( memchr( array_start( &h->request ), '\n', array_length( &h->request, 1 ) ) ) 507 return httperror( clientsocket, "500 request too long", "You sent too much headers");
530 httpresponse( clientsocket, array_start( &h->request ) ); 508
509 if( memchr( array_start( &h->request ), '\n', array_length( &h->request, 1 ) ) )
510 return httpresponse( clientsocket, array_start( &h->request ) );
531} 511}
532 512
533static void handle_write( const int64 clientsocket ) { 513static void handle_write( const int64 clientsocket ) {
@@ -701,6 +681,9 @@ static void server_mainloop( ) {
701 taia_now( &next_timeout_check ); 681 taia_now( &next_timeout_check );
702 taia_addsec( &next_timeout_check, &next_timeout_check, OT_CLIENT_TIMEOUT_CHECKINTERVAL); 682 taia_addsec( &next_timeout_check, &next_timeout_check, OT_CLIENT_TIMEOUT_CHECKINTERVAL);
703 } 683 }
684
685 /* See if we need to move our pools */
686 clean_all_torrents();
704 } 687 }
705} 688}
706 689
diff --git a/trackerlogic.c b/trackerlogic.c
index c8576f6..d2d279e 100644
--- a/trackerlogic.c
+++ b/trackerlogic.c
@@ -25,6 +25,10 @@
25 25
26/* GLOBAL VARIABLES */ 26/* GLOBAL VARIABLES */
27static ot_vector all_torrents[256]; 27static ot_vector all_torrents[256];
28static ot_vector changeset;
29size_t changeset_size = 0;
30time_t last_clean_time = 0;
31
28#ifdef WANT_CLOSED_TRACKER 32#ifdef WANT_CLOSED_TRACKER
29int g_closedtracker = 1; 33int g_closedtracker = 1;
30static ot_torrent* const OT_TORRENT_NOT_ON_WHITELIST = (ot_torrent*)1; 34static ot_torrent* const OT_TORRENT_NOT_ON_WHITELIST = (ot_torrent*)1;
@@ -158,33 +162,6 @@ static int vector_remove_torrent( ot_vector *vector, ot_hash *hash ) {
158 return 1; 162 return 1;
159} 163}
160 164
161/* This function deallocates all timedouted pools and shifts all other pools
162 it Returns 1 if torrent itself has not seen an announce for more than OT_TORRENT_TIMEOUT time units
163 0 if torrent is not yet timed out
164 Note: We expect NOW as a parameter since calling time() may be expensive
165*/
166static int clean_peerlist( time_t time_now, ot_peerlist *peer_list ) {
167 int i, timedout = (int)( time_now - peer_list->base );
168
169 if( !timedout ) return 0;
170 if( timedout > OT_POOLS_COUNT ) timedout = OT_POOLS_COUNT;
171
172 for( i = OT_POOLS_COUNT - timedout; i < OT_POOLS_COUNT; ++i )
173 free( peer_list->peers[i].data);
174
175 memmove( peer_list->peers + timedout, peer_list->peers, sizeof( ot_vector ) * (OT_POOLS_COUNT-timedout) );
176 byte_zero( peer_list->peers, sizeof( ot_vector ) * timedout );
177
178 memmove( peer_list->seed_count + timedout, peer_list->seed_count, sizeof( size_t ) * ( OT_POOLS_COUNT - timedout) );
179 byte_zero( peer_list->seed_count, sizeof( size_t ) * timedout );
180
181 if( timedout == OT_POOLS_COUNT )
182 return time_now - peer_list->base > OT_TORRENT_TIMEOUT;
183
184 peer_list->base = time_now;
185 return 0;
186}
187
188ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer ) { 165ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer ) {
189 int exactmatch; 166 int exactmatch;
190 ot_torrent *torrent; 167 ot_torrent *torrent;
@@ -219,8 +196,7 @@ ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer ) {
219 196
220 byte_zero( torrent->peer_list, sizeof( ot_peerlist ) ); 197 byte_zero( torrent->peer_list, sizeof( ot_peerlist ) );
221 torrent->peer_list->base = NOW; 198 torrent->peer_list->base = NOW;
222 } else 199 }
223 clean_peerlist( NOW, torrent->peer_list );
224 200
225 /* Sanitize flags: Whoever claims to have completed download, must be a seeder */ 201 /* Sanitize flags: Whoever claims to have completed download, must be a seeder */
226 if( ( OT_FLAG( peer ) & ( PEER_FLAG_COMPLETED | PEER_FLAG_SEEDING ) ) == PEER_FLAG_COMPLETED ) 202 if( ( OT_FLAG( peer ) & ( PEER_FLAG_COMPLETED | PEER_FLAG_SEEDING ) ) == PEER_FLAG_COMPLETED )
@@ -294,7 +270,9 @@ size_t return_peers_for_torrent( ot_torrent *torrent, size_t amount, char *reply
294 peer_count += torrent->peer_list->peers[index].size; 270 peer_count += torrent->peer_list->peers[index].size;
295 seed_count += torrent->peer_list->seed_count[index]; 271 seed_count += torrent->peer_list->seed_count[index];
296 } 272 }
297 if( peer_count < amount ) amount = peer_count; 273
274 if( peer_count < amount )
275 amount = peer_count;
298 276
299 if( is_tcp ) 277 if( is_tcp )
300 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 ); 278 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 );
@@ -348,10 +326,8 @@ size_t return_fullscrape_for_tracker( char **reply ) {
348 int i, k; 326 int i, k;
349 char *r; 327 char *r;
350 328
351 for( i=0; i<256; ++i ) { 329 for( i=0; i<256; ++i )
352 ot_vector *torrents_list = &all_torrents[i]; 330 torrent_count += all_torrents[i].size;
353 torrent_count += torrents_list->size;
354 }
355 331
356 if( !( r = *reply = malloc( 128*torrent_count ) ) ) return 0; 332 if( !( r = *reply = malloc( 128*torrent_count ) ) ) return 0;
357 333
@@ -380,7 +356,6 @@ size_t return_memstat_for_tracker( char **reply ) {
380 size_t torrent_count = 0, j; 356 size_t torrent_count = 0, j;
381 int i, k; 357 int i, k;
382 char *r; 358 char *r;
383 time_t time_now = NOW;
384 359
385 for( i=0; i<256; ++i ) { 360 for( i=0; i<256; ++i ) {
386 ot_vector *torrents_list = &all_torrents[i]; 361 ot_vector *torrents_list = &all_torrents[i];
@@ -398,7 +373,6 @@ size_t return_memstat_for_tracker( char **reply ) {
398 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list; 373 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
399 ot_hash *hash =&( ((ot_torrent*)(torrents_list->data))[j] ).hash; 374 ot_hash *hash =&( ((ot_torrent*)(torrents_list->data))[j] ).hash;
400 r += sprintf( r, "\n%s:\n", to_hex( (ot_byte*)hash ) ); 375 r += sprintf( r, "\n%s:\n", to_hex( (ot_byte*)hash ) );
401 clean_peerlist( time_now, peer_list );
402 for( k=0; k<OT_POOLS_COUNT; ++k ) 376 for( k=0; k<OT_POOLS_COUNT; ++k )
403 r += sprintf( r, "\t%04X %04X\n", ((unsigned int)peer_list->peers[k].size), (unsigned int)peer_list->peers[k].space ); 377 r += sprintf( r, "\t%04X %04X\n", ((unsigned int)peer_list->peers[k].size), (unsigned int)peer_list->peers[k].space );
404 } 378 }
@@ -418,7 +392,6 @@ size_t return_udp_scrape_for_torrent( ot_hash *hash, char *reply ) {
418 memset( reply, 0, 12); 392 memset( reply, 0, 12);
419 } else { 393 } else {
420 unsigned long *r = (unsigned long*) reply; 394 unsigned long *r = (unsigned long*) reply;
421 clean_peerlist( NOW, torrent->peer_list );
422 395
423 for( i=0; i<OT_POOLS_COUNT; ++i ) { 396 for( i=0; i<OT_POOLS_COUNT; ++i ) {
424 peers += torrent->peer_list->peers[i].size; 397 peers += torrent->peer_list->peers[i].size;
@@ -440,7 +413,6 @@ size_t return_tcp_scrape_for_torrent( ot_hash *hash, char *reply ) {
440 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 413 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch );
441 414
442 if( !exactmatch ) return sprintf( r, "d5:filesdee" ); 415 if( !exactmatch ) return sprintf( r, "d5:filesdee" );
443 clean_peerlist( NOW, torrent->peer_list );
444 416
445 for( i=0; i<OT_POOLS_COUNT; ++i ) { 417 for( i=0; i<OT_POOLS_COUNT; ++i ) {
446 peers += torrent->peer_list->peers[i].size; 418 peers += torrent->peer_list->peers[i].size;
@@ -453,36 +425,145 @@ size_t return_tcp_scrape_for_torrent( ot_hash *hash, char *reply ) {
453 return r - reply; 425 return r - reply;
454} 426}
455 427
456size_t return_sync_for_torrent( ot_hash *hash, char **reply ) { 428/* Throw away old changeset */
457 int exactmatch; 429static void release_changeset( void ) {
458 size_t peers = 0; 430 ot_byte **changeset_ptrs = (ot_byte**)(changeset.data);
459 char *r; 431 int i;
460 ot_vector *torrents_list = &all_torrents[*hash[0]]; 432
461 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 433 for( i = 0; i < changeset.size; ++i )
434 free( changeset_ptrs[i] );
435
436 free( changeset_ptrs );
437 byte_zero( &changeset, sizeof( changeset ) );
438
439 changeset_size = 0;
440}
441
442static void add_pool_to_changeset( ot_hash *hash, ot_peer *peers, size_t peer_count ) {
443 ot_byte *pool_copy = (ot_byte *)malloc( sizeof( size_t ) + sizeof( ot_hash ) + sizeof( ot_peer ) * peer_count + 13 );
444 size_t r = 0;
445
446 if( !pool_copy )
447 return;
448
449 memmove( pool_copy + sizeof( size_t ), "20:", 3 );
450 memmove( pool_copy + sizeof( size_t ) + 3, hash, sizeof( ot_hash ) );
451 r = sizeof( size_t ) + 3 + sizeof( ot_hash );
452 r += sprintf( (char*)pool_copy + r, "%zd:", sizeof( ot_peer ) * peer_count );
453 memmove( pool_copy + r, peers, sizeof( ot_peer ) * peer_count );
454 r += sizeof( ot_peer ) * peer_count;
455
456 /* Without the length field */
457 *(size_t*)pool_copy = r - sizeof( size_t );
458
459 if( changeset.size + 1 >= changeset.space ) {
460 size_t new_space = changeset.space ? OT_VECTOR_GROW_RATIO * changeset.space : OT_VECTOR_MIN_MEMBERS;
461 ot_byte *new_data = realloc( changeset.data, new_space * sizeof( ot_byte *) );
462
463 if( !new_data )
464 return free( pool_copy );
465
466 changeset.data = new_data;
467 changeset.space = new_space;
468 }
469
470 ((ot_byte**)changeset.data)[changeset.size++] = pool_copy;
471
472 /* Without the length field */
473 changeset_size += r - sizeof( size_t );
474}
475
476/* Proposed output format
477 d4:syncd20:<info_hash>8*N:(xxxxyy)*Nee
478*/
479size_t return_changeset_for_tracker( char **reply ) {
480 size_t i, r = 8;
481
482 clean_all_torrents();
483
484 *reply = malloc( 8 + changeset_size + 2 );
485 if( !*reply )
486 return 0;
462 487
463 if( exactmatch ) { 488 memmove( *reply, "d4:syncd", 8 );
464 clean_peerlist( NOW, torrent->peer_list ); 489 for( i = 0; i < changeset.size; ++i ) {
465 peers = torrent->peer_list->peers[0].size; 490 ot_byte *data = ((ot_byte**)changeset.data)[i];
491 memmove( *reply + r, data + sizeof( size_t ), *(size_t*)data );
492 r += *(size_t*)data;
466 } 493 }
467 494
468 if( !( r = *reply = malloc( 10 + peers * sizeof( ot_peer ) ) ) ) return 0; 495 (*reply)[r++] = 'e';
496 (*reply)[r++] = 'e';
497
498 return r;
499}
500
501/* Clean up all torrents, remove timedout pools and
502 torrents, also prepare new changeset */
503void clean_all_torrents( void ) {
504 int i, j, k;
505 time_t time_now = NOW;
506 size_t peers_count;
507
508 if( time_now <= last_clean_time )
509 return;
510 last_clean_time = time_now;
511
512 release_changeset();
469 513
470 memmove( r, "d4:sync", 7 ); 514 for( i=0; i<256; ++i ) {
471 r += 7; 515 ot_vector *torrents_list = &all_torrents[i];
472 r += sprintf( r, "%zd:", peers * sizeof( ot_peer ) ); 516 for( j=0; j<torrents_list->size; ++j ) {
473 if( peers ) { 517 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
474 memmove( r, torrent->peer_list->peers[0].data, peers * sizeof( ot_peer ) ); 518 ot_hash *hash =&( ((ot_torrent*)(torrents_list->data))[j] ).hash;
475 r += peers * sizeof( ot_peer ); 519
520 time_t timedout = (int)( time_now - peer_list->base );
521
522 /* Torrent has idled out */
523 if( timedout > OT_TORRENT_TIMEOUT ) {
524 vector_remove_torrent( torrents_list, hash );
525 --j;
526 }
527
528 /* If nothing to be cleaned here, handle next torrent */
529 if( timedout > OT_POOLS_COUNT )
530 continue;
531
532 /* Release vectors that have timed out */
533 for( k = OT_POOLS_COUNT - timedout; k < OT_POOLS_COUNT; ++k )
534 free( peer_list->peers[k].data);
535
536 /* Shift vectors back by the amount of pools that were shifted out */
537 memmove( peer_list->peers + timedout, peer_list->peers, sizeof( ot_vector ) * ( OT_POOLS_COUNT - timedout ) );
538 byte_zero( peer_list->peers, sizeof( ot_vector ) * timedout );
539
540 /* Shift back seed counts as well */
541 memmove( peer_list->seed_count + timedout, peer_list->seed_count, sizeof( size_t ) * ( OT_POOLS_COUNT - timedout ) );
542 byte_zero( peer_list->seed_count, sizeof( size_t ) * timedout );
543
544 /* Save the block modified within last OT_POOLS_TIMEOUT */
545 if( peer_list->peers[1].size )
546 add_pool_to_changeset( hash, peer_list->peers[1].data, peer_list->peers[1].size );
547
548 peers_count = 0;
549 for( k = 0; k < OT_POOLS_COUNT; ++k )
550 peers_count += peer_list->peers[k].size;
551
552 if( peers_count ) {
553 peer_list->base = time_now;
554 } else {
555 /* When we got here, the last time that torrent
556 has been touched is OT_POOLS_COUNT units before */
557 peer_list->base = time_now - OT_POOLS_COUNT;
558 }
559 }
476 } 560 }
477 *r++ = 'e';
478 return r - *reply;
479} 561}
480 562
481typedef struct { int val; ot_torrent * torrent; } ot_record; 563typedef struct { int val; ot_torrent * torrent; } ot_record;
482 564
483/* Fetches stats from tracker */ 565/* Fetches stats from tracker */
484size_t return_stats_for_tracker( char *reply, int mode ) { 566size_t return_stats_for_tracker( char *reply, int mode ) {
485 time_t time_now = NOW;
486 size_t torrent_count = 0, peer_count = 0, seed_count = 0, j; 567 size_t torrent_count = 0, peer_count = 0, seed_count = 0, j;
487 ot_record top5s[5], top5c[5]; 568 ot_record top5s[5], top5c[5];
488 char *r = reply; 569 char *r = reply;
@@ -498,12 +579,6 @@ size_t return_stats_for_tracker( char *reply, int mode ) {
498 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list; 579 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
499 size_t local_peers = 0, local_seeds = 0; 580 size_t local_peers = 0, local_seeds = 0;
500 581
501 if( clean_peerlist( time_now, peer_list ) ) {
502 ot_hash *hash =&( ((ot_torrent*)(torrents_list->data))[j] ).hash;
503 vector_remove_torrent( torrents_list, hash );
504 --j;
505 continue;
506 }
507 for( k=0; k<OT_POOLS_COUNT; ++k ) { 582 for( k=0; k<OT_POOLS_COUNT; ++k ) {
508 local_peers += peer_list->peers[k].size; 583 local_peers += peer_list->peers[k].size;
509 local_seeds += peer_list->seed_count[k]; 584 local_seeds += peer_list->seed_count[k];
@@ -549,12 +624,6 @@ void remove_peer_from_torrent( ot_hash *hash, ot_peer *peer ) {
549 624
550 if( !exactmatch ) return; 625 if( !exactmatch ) return;
551 626
552 /* Maybe this does the job */
553 if( clean_peerlist( NOW, torrent->peer_list ) ) {
554 vector_remove_torrent( torrents_list, hash );
555 return;
556 }
557
558 for( i=0; i<OT_POOLS_COUNT; ++i ) 627 for( i=0; i<OT_POOLS_COUNT; ++i )
559 switch( vector_remove_peer( &torrent->peer_list->peers[i], peer, i == 0 ) ) { 628 switch( vector_remove_peer( &torrent->peer_list->peers[i], peer, i == 0 ) ) {
560 case 0: continue; 629 case 0: continue;
@@ -572,7 +641,9 @@ int init_logic( const char * const serverdir ) {
572 srandom( time(NULL) ); 641 srandom( time(NULL) );
573 642
574 /* Initialize control structures */ 643 /* Initialize control structures */
575 byte_zero( all_torrents, sizeof (all_torrents) ); 644 byte_zero( all_torrents, sizeof( all_torrents ) );
645 byte_zero( &changeset, sizeof( changeset ) );
646 changeset_size = 0;
576 647
577 return 0; 648 return 0;
578} 649}
@@ -591,4 +662,6 @@ void deinit_logic( void ) {
591 } 662 }
592 } 663 }
593 byte_zero( all_torrents, sizeof (all_torrents)); 664 byte_zero( all_torrents, sizeof (all_torrents));
665 byte_zero( &changeset, sizeof( changeset ) );
666 changeset_size = 0;
594} 667}
diff --git a/trackerlogic.h b/trackerlogic.h
index fbc86ea..dfd5047 100644
--- a/trackerlogic.h
+++ b/trackerlogic.h
@@ -98,9 +98,10 @@ size_t return_peers_for_torrent( ot_torrent *torrent, size_t amount, char *reply
98size_t return_fullscrape_for_tracker( char **reply ); 98size_t return_fullscrape_for_tracker( char **reply );
99size_t return_tcp_scrape_for_torrent( ot_hash *hash, char *reply ); 99size_t return_tcp_scrape_for_torrent( ot_hash *hash, char *reply );
100size_t return_udp_scrape_for_torrent( ot_hash *hash, char *reply ); 100size_t return_udp_scrape_for_torrent( ot_hash *hash, char *reply );
101size_t return_sync_for_torrent( ot_hash *hash, char **reply );
102size_t return_stats_for_tracker( char *reply, int mode ); 101size_t return_stats_for_tracker( char *reply, int mode );
103size_t return_memstat_for_tracker( char **reply ); 102size_t return_memstat_for_tracker( char **reply );
103size_t return_changeset_for_tracker( char **reply );
104void clean_all_torrents( void );
104void remove_peer_from_torrent( ot_hash *hash, ot_peer *peer ); 105void remove_peer_from_torrent( ot_hash *hash, ot_peer *peer );
105 106
106#endif 107#endif