switch( scan_urlencoded_query( &c, data = c, SCAN_PATH ) ) {
case 4: /* sync ? */
if( byte_diff( data, 4, "sync") ) HTTPERROR_404;
- scanon = 1;
-
- while( scanon ) {
- switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) {
- case -2: scanon = 0; break; /* TERMINATOR */
- case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */
- case 9:
- if(byte_diff(data,9,"info_hash")) {
- scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE );
- continue;
- }
- /* ignore this, when we have less than 20 bytes */
- if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM;
- hash = (ot_hash*)data; /* Fall through intended */
- break;
- default:
- scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE );
- break;
- }
- }
-
- if( !hash ) HTTPERROR_400_PARAM;
- if( !( reply_size = return_sync_for_torrent( hash, &reply ) ) ) HTTPERROR_500;
-
+ if( !( reply_size = return_changeset_for_tracker( &reply ) ) ) HTTPERROR_500;
return sendmallocdata( s, reply, reply_size );
+
case 5: /* stats ? */
if( byte_diff(data,5,"stats")) HTTPERROR_404;
scanon = 1;
array_catb( &h->request, static_inbuf, l );
if( array_failed( &h->request ) )
- httperror( clientsocket, "500 Server Error", "Request too long.");
- else if( array_bytes( &h->request ) > 8192 )
- httperror( clientsocket, "500 request too long", "You sent too much headers");
- else if( memchr( array_start( &h->request ), '\n', array_length( &h->request, 1 ) ) )
- httpresponse( clientsocket, array_start( &h->request ) );
+ return httperror( clientsocket, "500 Server Error", "Request too long.");
+
+ if( array_bytes( &h->request ) > 8192 )
+ return httperror( clientsocket, "500 request too long", "You sent too much headers");
+
+ if( memchr( array_start( &h->request ), '\n', array_length( &h->request, 1 ) ) )
+ return httpresponse( clientsocket, array_start( &h->request ) );
}
static void handle_write( const int64 clientsocket ) {
taia_now( &next_timeout_check );
taia_addsec( &next_timeout_check, &next_timeout_check, OT_CLIENT_TIMEOUT_CHECKINTERVAL);
}
+
+ /* See if we need to move our pools */
+ clean_all_torrents();
}
}
/* GLOBAL VARIABLES */
static ot_vector all_torrents[256];
+static ot_vector changeset;
+size_t changeset_size = 0;
+time_t last_clean_time = 0;
+
#ifdef WANT_CLOSED_TRACKER
int g_closedtracker = 1;
static ot_torrent* const OT_TORRENT_NOT_ON_WHITELIST = (ot_torrent*)1;
return 1;
}
-/* This function deallocates all timedouted pools and shifts all other pools
- it Returns 1 if torrent itself has not seen an announce for more than OT_TORRENT_TIMEOUT time units
- 0 if torrent is not yet timed out
- Note: We expect NOW as a parameter since calling time() may be expensive
-*/
-static int clean_peerlist( time_t time_now, ot_peerlist *peer_list ) {
- int i, timedout = (int)( time_now - peer_list->base );
-
- if( !timedout ) return 0;
- if( timedout > OT_POOLS_COUNT ) timedout = OT_POOLS_COUNT;
-
- for( i = OT_POOLS_COUNT - timedout; i < OT_POOLS_COUNT; ++i )
- free( peer_list->peers[i].data);
-
- memmove( peer_list->peers + timedout, peer_list->peers, sizeof( ot_vector ) * (OT_POOLS_COUNT-timedout) );
- byte_zero( peer_list->peers, sizeof( ot_vector ) * timedout );
-
- memmove( peer_list->seed_count + timedout, peer_list->seed_count, sizeof( size_t ) * ( OT_POOLS_COUNT - timedout) );
- byte_zero( peer_list->seed_count, sizeof( size_t ) * timedout );
-
- if( timedout == OT_POOLS_COUNT )
- return time_now - peer_list->base > OT_TORRENT_TIMEOUT;
-
- peer_list->base = time_now;
- return 0;
-}
-
ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer ) {
int exactmatch;
ot_torrent *torrent;
byte_zero( torrent->peer_list, sizeof( ot_peerlist ) );
torrent->peer_list->base = NOW;
- } else
- clean_peerlist( NOW, torrent->peer_list );
+ }
/* Sanitize flags: Whoever claims to have completed download, must be a seeder */
if( ( OT_FLAG( peer ) & ( PEER_FLAG_COMPLETED | PEER_FLAG_SEEDING ) ) == PEER_FLAG_COMPLETED )
peer_count += torrent->peer_list->peers[index].size;
seed_count += torrent->peer_list->seed_count[index];
}
- if( peer_count < amount ) amount = peer_count;
+
+ if( peer_count < amount )
+ amount = peer_count;
if( is_tcp )
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 );
int i, k;
char *r;
- for( i=0; i<256; ++i ) {
- ot_vector *torrents_list = &all_torrents[i];
- torrent_count += torrents_list->size;
- }
+ for( i=0; i<256; ++i )
+ torrent_count += all_torrents[i].size;
if( !( r = *reply = malloc( 128*torrent_count ) ) ) return 0;
size_t torrent_count = 0, j;
int i, k;
char *r;
- time_t time_now = NOW;
for( i=0; i<256; ++i ) {
ot_vector *torrents_list = &all_torrents[i];
ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
ot_hash *hash =&( ((ot_torrent*)(torrents_list->data))[j] ).hash;
r += sprintf( r, "\n%s:\n", to_hex( (ot_byte*)hash ) );
- clean_peerlist( time_now, peer_list );
for( k=0; k<OT_POOLS_COUNT; ++k )
r += sprintf( r, "\t%04X %04X\n", ((unsigned int)peer_list->peers[k].size), (unsigned int)peer_list->peers[k].space );
}
memset( reply, 0, 12);
} else {
unsigned long *r = (unsigned long*) reply;
- clean_peerlist( NOW, torrent->peer_list );
for( i=0; i<OT_POOLS_COUNT; ++i ) {
peers += torrent->peer_list->peers[i].size;
ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch );
if( !exactmatch ) return sprintf( r, "d5:filesdee" );
- clean_peerlist( NOW, torrent->peer_list );
for( i=0; i<OT_POOLS_COUNT; ++i ) {
peers += torrent->peer_list->peers[i].size;
return r - reply;
}
-size_t return_sync_for_torrent( ot_hash *hash, char **reply ) {
- int exactmatch;
- size_t peers = 0;
- char *r;
- ot_vector *torrents_list = &all_torrents[*hash[0]];
- ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch );
+/* Throw away old changeset */
+static void release_changeset( void ) {
+ ot_byte **changeset_ptrs = (ot_byte**)(changeset.data);
+ int i;
+
+ for( i = 0; i < changeset.size; ++i )
+ free( changeset_ptrs[i] );
+
+ free( changeset_ptrs );
+ byte_zero( &changeset, sizeof( changeset ) );
+
+ changeset_size = 0;
+}
+
+static void add_pool_to_changeset( ot_hash *hash, ot_peer *peers, size_t peer_count ) {
+ ot_byte *pool_copy = (ot_byte *)malloc( sizeof( size_t ) + sizeof( ot_hash ) + sizeof( ot_peer ) * peer_count + 13 );
+ size_t r = 0;
+
+ if( !pool_copy )
+ return;
+
+ memmove( pool_copy + sizeof( size_t ), "20:", 3 );
+ memmove( pool_copy + sizeof( size_t ) + 3, hash, sizeof( ot_hash ) );
+ r = sizeof( size_t ) + 3 + sizeof( ot_hash );
+ r += sprintf( (char*)pool_copy + r, "%zd:", sizeof( ot_peer ) * peer_count );
+ memmove( pool_copy + r, peers, sizeof( ot_peer ) * peer_count );
+ r += sizeof( ot_peer ) * peer_count;
+
+ /* Without the length field */
+ *(size_t*)pool_copy = r - sizeof( size_t );
+
+ if( changeset.size + 1 >= changeset.space ) {
+ size_t new_space = changeset.space ? OT_VECTOR_GROW_RATIO * changeset.space : OT_VECTOR_MIN_MEMBERS;
+ ot_byte *new_data = realloc( changeset.data, new_space * sizeof( ot_byte *) );
+
+ if( !new_data )
+ return free( pool_copy );
+
+ changeset.data = new_data;
+ changeset.space = new_space;
+ }
+
+ ((ot_byte**)changeset.data)[changeset.size++] = pool_copy;
+
+ /* Without the length field */
+ changeset_size += r - sizeof( size_t );
+}
+
+/* Proposed output format
+ d4:syncd20:<info_hash>8*N:(xxxxyy)*Nee
+*/
+size_t return_changeset_for_tracker( char **reply ) {
+ size_t i, r = 8;
+
+ clean_all_torrents();
+
+ *reply = malloc( 8 + changeset_size + 2 );
+ if( !*reply )
+ return 0;
- if( exactmatch ) {
- clean_peerlist( NOW, torrent->peer_list );
- peers = torrent->peer_list->peers[0].size;
+ memmove( *reply, "d4:syncd", 8 );
+ for( i = 0; i < changeset.size; ++i ) {
+ ot_byte *data = ((ot_byte**)changeset.data)[i];
+ memmove( *reply + r, data + sizeof( size_t ), *(size_t*)data );
+ r += *(size_t*)data;
}
- if( !( r = *reply = malloc( 10 + peers * sizeof( ot_peer ) ) ) ) return 0;
+ (*reply)[r++] = 'e';
+ (*reply)[r++] = 'e';
+
+ return r;
+}
+
+/* Clean up all torrents, remove timedout pools and
+ torrents, also prepare new changeset */
+void clean_all_torrents( void ) {
+ int i, j, k;
+ time_t time_now = NOW;
+ size_t peers_count;
+
+ if( time_now <= last_clean_time )
+ return;
+ last_clean_time = time_now;
+
+ release_changeset();
- memmove( r, "d4:sync", 7 );
- r += 7;
- r += sprintf( r, "%zd:", peers * sizeof( ot_peer ) );
- if( peers ) {
- memmove( r, torrent->peer_list->peers[0].data, peers * sizeof( ot_peer ) );
- r += peers * sizeof( ot_peer );
+ for( i=0; i<256; ++i ) {
+ ot_vector *torrents_list = &all_torrents[i];
+ for( j=0; j<torrents_list->size; ++j ) {
+ ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
+ ot_hash *hash =&( ((ot_torrent*)(torrents_list->data))[j] ).hash;
+
+ time_t timedout = (int)( time_now - peer_list->base );
+
+ /* Torrent has idled out */
+ if( timedout > OT_TORRENT_TIMEOUT ) {
+ vector_remove_torrent( torrents_list, hash );
+ --j;
+ }
+
+ /* If nothing to be cleaned here, handle next torrent */
+ if( timedout > OT_POOLS_COUNT )
+ continue;
+
+ /* Release vectors that have timed out */
+ for( k = OT_POOLS_COUNT - timedout; k < OT_POOLS_COUNT; ++k )
+ free( peer_list->peers[k].data);
+
+ /* Shift vectors back by the amount of pools that were shifted out */
+ memmove( peer_list->peers + timedout, peer_list->peers, sizeof( ot_vector ) * ( OT_POOLS_COUNT - timedout ) );
+ byte_zero( peer_list->peers, sizeof( ot_vector ) * timedout );
+
+ /* Shift back seed counts as well */
+ memmove( peer_list->seed_count + timedout, peer_list->seed_count, sizeof( size_t ) * ( OT_POOLS_COUNT - timedout ) );
+ byte_zero( peer_list->seed_count, sizeof( size_t ) * timedout );
+
+ /* Save the block modified within last OT_POOLS_TIMEOUT */
+ if( peer_list->peers[1].size )
+ add_pool_to_changeset( hash, peer_list->peers[1].data, peer_list->peers[1].size );
+
+ peers_count = 0;
+ for( k = 0; k < OT_POOLS_COUNT; ++k )
+ peers_count += peer_list->peers[k].size;
+
+ if( peers_count ) {
+ peer_list->base = time_now;
+ } else {
+ /* When we got here, the last time that torrent
+ has been touched is OT_POOLS_COUNT units before */
+ peer_list->base = time_now - OT_POOLS_COUNT;
+ }
+ }
}
- *r++ = 'e';
- return r - *reply;
}
typedef struct { int val; ot_torrent * torrent; } ot_record;
/* Fetches stats from tracker */
size_t return_stats_for_tracker( char *reply, int mode ) {
- time_t time_now = NOW;
size_t torrent_count = 0, peer_count = 0, seed_count = 0, j;
ot_record top5s[5], top5c[5];
char *r = reply;
ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
size_t local_peers = 0, local_seeds = 0;
- if( clean_peerlist( time_now, peer_list ) ) {
- ot_hash *hash =&( ((ot_torrent*)(torrents_list->data))[j] ).hash;
- vector_remove_torrent( torrents_list, hash );
- --j;
- continue;
- }
for( k=0; k<OT_POOLS_COUNT; ++k ) {
local_peers += peer_list->peers[k].size;
local_seeds += peer_list->seed_count[k];
if( !exactmatch ) return;
- /* Maybe this does the job */
- if( clean_peerlist( NOW, torrent->peer_list ) ) {
- vector_remove_torrent( torrents_list, hash );
- return;
- }
-
for( i=0; i<OT_POOLS_COUNT; ++i )
switch( vector_remove_peer( &torrent->peer_list->peers[i], peer, i == 0 ) ) {
case 0: continue;
srandom( time(NULL) );
/* Initialize control structures */
- byte_zero( all_torrents, sizeof (all_torrents) );
+ byte_zero( all_torrents, sizeof( all_torrents ) );
+ byte_zero( &changeset, sizeof( changeset ) );
+ changeset_size = 0;
return 0;
}
}
}
byte_zero( all_torrents, sizeof (all_torrents));
+ byte_zero( &changeset, sizeof( changeset ) );
+ changeset_size = 0;
}