From 688a4edd9e489de26a9b53ea79f513e6461c7533 Mon Sep 17 00:00:00 2001 From: erdgeist <> Date: Thu, 14 Dec 2006 02:44:50 +0000 Subject: Now actually seems to work for the most parts Added scraping Added graceful disconnect --- Makefile | 4 +- opentracker.c | 142 ++++++++++++++++++++++++++++++++++++------------ scan_urlencoded_query.c | 6 ++ scan_urlencoded_query.h | 5 ++ trackerlogic.c | 111 ++++++++++++++++++++++++++----------- trackerlogic.h | 48 +++++++--------- 6 files changed, 219 insertions(+), 97 deletions(-) diff --git a/Makefile b/Makefile index a85ab2b..21689e5 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CC?=gcc -CFLAGS+=-I../libowfat -Wall -pipe -g -ggdb -LDFLAGS+=-L../libowfat/ -lowfat +CFLAGS+=-I../libowfat -Wall -pipe -O2 +LDFLAGS+=-L../libowfat/ -lowfat -s SOURCES=opentracker.c trackerlogic.c scan_urlencoded_query.c diff --git a/opentracker.c b/opentracker.c index bd8f5ca..6858f37 100644 --- a/opentracker.c +++ b/opentracker.c @@ -106,13 +106,12 @@ const char* http_header(struct http_data* r,const char* h) void httpresponse(struct http_data* h,int64 s) { - char *c, *d, *data, *reply = NULL; - struct ot_peer peer; - ot_torrent torrent; - ot_hash *hash = NULL; - unsigned long numwant; - int compact, scanon; - size_t reply_size = 0; + char *c, *d, *data, *reply = NULL; + ot_peer peer; + ot_torrent *torrent; + ot_hash *hash = NULL; + int numwant, tmp, scanon; + size_t reply_size = 0; array_cat0(&h->r); @@ -137,14 +136,57 @@ e400: case 6: /* scrape ? */ if (byte_diff(data,6,"scrape")) goto e404; + scanon = 1; + + while( scanon ) { + switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) { + case -2: /* terminator */ + scanon = 0; + break; + case -1: /* error */ + goto e404; + 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 */ + switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) { + case -1: + goto e404; + case 20: + hash = (ot_hash*)data; /* Fall through intended */ + default: + continue; + } + default: + scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); + break; + } + } + + /* Scanned whole query string, wo */ + if( !hash ) { + httperror(h,"400 Invalid Request","This server only serves specific scrapes."); + goto bailout; + } + + // Enough for whole scrape string + reply = malloc( 128 ); + if( reply ) + reply_size = return_scrape_for_torrent( hash, reply ); + if( !reply || ( reply_size < 0 ) ) { + if( reply ) free( reply ); + goto e500; + } break; case 8: if( byte_diff(data,8,"announce")) goto e404; + peer.ip = h->ip; peer.port_flags = 6881 << 16; numwant = 50; - compact = 1; scanon = 1; while( scanon ) { @@ -155,19 +197,44 @@ e400: case -1: /* error */ goto e404; case 4: - if(!byte_diff(data,4,"port")) - /* scan int */ c; - else if(!byte_diff(data,4,"left")) - /* scan int */ c; - else + if(!byte_diff(data,4,"port")) { + size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); + if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) || (tmp > 65536) ) goto e404; + peer.port_flags = ( tmp << 16 ) | ( peer.port_flags & 0xffff ); + } else if(!byte_diff(data,4,"left")) { + size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); + if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) ) goto e404; + if( !tmp ) peer.port_flags |= PEER_FLAG_SEEDING; + } else scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); break; + case 5: + if(byte_diff(data,5,"event")) + scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); + else switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) { + case -1: + goto e404; + case 7: + if(!byte_diff(data,7,"stopped")) peer.port_flags |= PEER_FLAG_STOPPED; + break; + case 9: + if(!byte_diff(data,9,"complete")) peer.port_flags |= PEER_FLAG_COMPLETED; + default: // Fall through intended + break; + } + break; case 7: - if(!byte_diff(data,7,"numwant")) - /* scan int */ c; - else if(!byte_diff(data,7,"compact")) - /* scan flag */ c; - else + if(!byte_diff(data,7,"numwant")) { + size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); + if( ( len <= 0 ) || scan_fixed_int( data, len, &numwant ) ) goto e404; + } else if(!byte_diff(data,7,"compact")) { + size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); + if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) ) goto e404; + if( !tmp ) { + httperror(h,"400 Invalid Request","This server only delivers compact results."); + goto bailout; + } + } else scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); break; case 9: @@ -180,9 +247,8 @@ e400: case -1: goto e404; case 20: - hash = (ot_hash*)data; /* Fall through intended */ - printf("hash: %s\n",*hash); - default: + hash = (ot_hash*)data; + default: // Fall through intended continue; } default: @@ -192,20 +258,25 @@ e400: } /* Scanned whole query string */ - if( !hash || ( compact == 0 ) ) goto e404; - - torrent = add_peer_to_torrent( hash, &peer ); - if( !torrent ) { + if( !hash ) goto e404; + + if( peer.port_flags & PEER_FLAG_STOPPED ) { + remove_peer_from_torrent( hash, &peer ); + reply = strdup( "d15:warning message4:Okaye" ); reply_size = 26; + } else { + torrent = add_peer_to_torrent( hash, &peer ); + if( !torrent ) { e500: - httperror(h,"500 Internal Server Error","A server error has occured. Please retry later."); - goto bailout; - } - reply = malloc( numwant*6+24 ); - if( reply ) - reply_size = return_peers_for_torrent( torrent, numwant, reply ); - if( !reply || ( reply_size < 0 ) ) { - if( reply ) free( reply ); - goto e500; + httperror(h,"500 Internal Server Error","A server error has occured. Please retry later."); + goto bailout; + } + reply = malloc( numwant*6+64 ); // peerlist + seeder, peers and lametta + if( reply ) + reply_size = return_peers_for_torrent( torrent, numwant, reply ); + if( !reply || ( reply_size < 0 ) ) { + if( reply ) free( reply ); + goto e500; + } } break; default: /* neither scrape nor announce */ @@ -213,6 +284,7 @@ e404: httperror(h,"404 Not Found","No such file or directory."); goto bailout; } + c=h->hdrbuf=(char*)malloc(500); c+=fmt_str(c,"HTTP/1.1 200 OK\r\nContent-Type: text/plain"); c+=fmt_str(c,"\r\nContent-Length: "); @@ -239,7 +311,6 @@ void graceful( int s ) { int main() { int s=socket_tcp4(); - uint32 scope_id; unsigned long ip; uint16 port; @@ -284,7 +355,6 @@ int main() io_close(n); } else io_close(n); - buffer_putnlflush(buffer_2); } if (errno==EAGAIN) io_eagain(s); diff --git a/scan_urlencoded_query.c b/scan_urlencoded_query.c index 3ac01cd..7bd5ee1 100644 --- a/scan_urlencoded_query.c +++ b/scan_urlencoded_query.c @@ -49,3 +49,9 @@ size_t scan_urlencoded_query(char **string, char *deststring, int flags) { *string = (char *)s; return d - (unsigned char*)deststring; } + +size_t scan_fixed_int( char *data, size_t len, int *tmp ) { + *tmp = 0; + while( (len > 0) && (*data >= '0') && (*data <= '9') ) { --len; *tmp = 10**tmp + *data++-'0'; } + return len; +} diff --git a/scan_urlencoded_query.h b/scan_urlencoded_query.h index 1e59745..c87dbee 100644 --- a/scan_urlencoded_query.h +++ b/scan_urlencoded_query.h @@ -12,4 +12,9 @@ // or -1 for parse error size_t scan_urlencoded_query(char **string, char *deststring, int flags); +// data pointer to len chars of string +// len length of chars in data to parse +// number number to receive result +size_t scan_fixed_int( char *data, size_t len, int *number ); + #endif diff --git a/trackerlogic.c b/trackerlogic.c index 9b8c541..0207fad 100644 --- a/trackerlogic.c +++ b/trackerlogic.c @@ -19,13 +19,14 @@ // int compare_hash( const void *hash1, const void *hash2 ) { return memcmp( hash1, hash2, sizeof( ot_hash )); } int compare_ip_port( const void *peer1, const void *peer2 ) { -if( ((ot_peer)peer1)->ip != ((ot_peer)peer2)->ip ) return ((ot_peer)peer1)->ip - ((ot_peer)peer2)->ip; -return ((ot_peer)peer1)->port_flags - ((ot_peer)peer2)->port_flags; } - -void *binary_search( const void *key, const void *base, - unsigned long member_count, const unsigned long member_size, - int (*compar) (const void *, const void *), - int *exactmatch ) { +if( ((ot_peer*)peer1)->ip != ((ot_peer*)peer2)->ip ) return ((ot_peer*)peer1)->ip - ((ot_peer*)peer2)->ip; +return ((ot_peer*)peer1)->port_flags - ((ot_peer*)peer2)->port_flags; } + +static void *binary_search( const void *key, const void *base, + unsigned long member_count, const unsigned long member_size, + int (*compar) (const void *, const void *), + int *exactmatch ) +{ ot_byte *lookat = ((ot_byte*)base) + member_size * (member_count >> 1); *exactmatch = 1; @@ -51,9 +52,9 @@ char ths[1+2*20];char*to_hex(ot_byte*s){char*m="0123456789ABCDEF";char*e=ths+40; // GLOBAL VARIABLES // -struct ot_vector all_torrents[256]; +static ot_vector all_torrents[256]; -void *vector_find_or_insert( ot_vector vector, void *key, size_t member_size, int(*compare_func)(const void*, const void*), int *exactmatch ) { +static void *vector_find_or_insert( ot_vector *vector, void *key, size_t member_size, int(*compare_func)(const void*, const void*), int *exactmatch ) { ot_byte *match = BINARY_FIND( key, vector->data, vector->size, member_size, compare_func, exactmatch ); if( *exactmatch ) return match; @@ -72,22 +73,22 @@ void *vector_find_or_insert( ot_vector vector, void *key, size_t member_size, in vector->size++; return match; } - -int vector_remove_peer( ot_vector vector, ot_peer peer ) { + +static int vector_remove_peer( ot_vector *vector, ot_peer *peer ) { int exactmatch; - ot_peer match; + ot_peer *match; if( !vector->size ) return 0; - match = BINARY_FIND( peer, vector->data, vector->size, sizeof( struct ot_peer ), compare_ip_port, &exactmatch ); + match = BINARY_FIND( peer, vector->data, vector->size, sizeof( ot_peer ), compare_ip_port, &exactmatch ); if( !exactmatch ) return 0; exactmatch = match->port_flags & PEER_FLAG_SEEDING ? 2 : 1; - MEMMOVE( match, match + 1, ((ot_peer)vector->data) + vector->size - match - 1 ); + MEMMOVE( match, match + 1, ((ot_peer*)vector->data) + vector->size - match - 1 ); vector->size--; return exactmatch; } -void free_peerlist( ot_peerlist peer_list ) { +static void free_peerlist( ot_peerlist *peer_list ) { int i; for( i=0; ipeers[i].data ) @@ -95,10 +96,10 @@ void free_peerlist( ot_peerlist peer_list ) { free( peer_list ); } -int vector_remove_torrent( ot_vector vector, ot_hash *hash ) { +static int vector_remove_torrent( ot_vector *vector, ot_hash *hash ) { int exactmatch; - ot_torrent end = ((ot_torrent)vector->data) + vector->size; - ot_torrent match = BINARY_FIND( hash, vector->data, vector->size, sizeof( struct ot_torrent ), compare_hash, &exactmatch ); + ot_torrent *end = ((ot_torrent*)vector->data) + vector->size; + ot_torrent *match = BINARY_FIND( hash, vector->data, vector->size, sizeof( ot_torrent ), compare_hash, &exactmatch ); if( !exactmatch ) return -1; free_peerlist( match->peer_list ); @@ -111,11 +112,12 @@ int vector_remove_torrent( ot_vector vector, ot_hash *hash ) { return 0; } -void clean_peerlist( ot_peerlist peer_list ) { +// Returns 1, if torrent is gone, 0 otherwise +static int clean_peerlist( ot_peerlist *peer_list ) { long timedout = NOW-peer_list->base; int i; - if( !timedout ) return; + if( !timedout ) return 0; if( timedout > OT_POOLS_COUNT ) timedout = OT_POOLS_COUNT; for( i=OT_POOLS_COUNT-timedout; iseed_count, sizeof( unsigned long ) * timedout ); peer_list->base = NOW; + return timedout == OT_POOLS_COUNT; } -ot_torrent add_peer_to_torrent( ot_hash *hash, ot_peer peer ) { - int exactmatch; - ot_torrent torrent; - ot_peer peer_dest; - ot_vector torrents_list = all_torrents + *hash[0], peer_pool; +ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer ) { + int exactmatch; + ot_torrent *torrent; + ot_peer *peer_dest; + ot_vector *torrents_list = &all_torrents[*hash[0]], *peer_pool; - torrent = vector_find_or_insert( torrents_list, (void*)hash, sizeof( struct ot_torrent ), compare_hash, &exactmatch ); + torrent = vector_find_or_insert( torrents_list, (void*)hash, sizeof( ot_torrent ), compare_hash, &exactmatch ); if( !torrent ) return NULL; if( !exactmatch ) { // Create a new torrent entry, then - torrent->peer_list = malloc( sizeof (struct ot_peerlist) ); + torrent->peer_list = malloc( sizeof (ot_peerlist) ); if( !torrent->peer_list ) { vector_remove_torrent( torrents_list, hash ); return NULL; } MEMMOVE( &torrent->hash, hash, sizeof( ot_hash ) ); - byte_zero( torrent->peer_list, sizeof( struct ot_peerlist )); + byte_zero( torrent->peer_list, sizeof( ot_peerlist )); torrent->peer_list->base = NOW; } else clean_peerlist( torrent->peer_list ); peer_pool = &torrent->peer_list->peers[0]; - peer_dest = vector_find_or_insert( peer_pool, (void*)peer, sizeof( struct ot_peer ), compare_ip_port, &exactmatch ); + peer_dest = vector_find_or_insert( peer_pool, (void*)peer, sizeof( ot_peer ), compare_ip_port, &exactmatch ); // If we hadn't had a match in current pool, create peer there and // remove it from all older pools if( !exactmatch ) { int i; - MEMMOVE( peer_dest, peer, sizeof( struct ot_peer ) ); + MEMMOVE( peer_dest, peer, sizeof( ot_peer ) ); if( peer->port_flags & PEER_FLAG_SEEDING ) torrent->peer_list->seed_count[0]++; for( i=1; iport_flags & PEER_FLAG_SEEDING ) && (peer->port_flags & PEER_FLAG_SEEDING ) ) torrent->peer_list->seed_count[0]++; } + if( peer->port_flags & PEER_FLAG_COMPLETED ) + torrent->peer_list->downloaded++; return torrent; } @@ -186,7 +191,7 @@ ot_torrent add_peer_to_torrent( ot_hash *hash, ot_peer peer ) { // * RANDOM may return huge values // * does not yet check not to return self // -size_t return_peers_for_torrent( ot_torrent torrent, unsigned long amount, char *reply ) { +size_t return_peers_for_torrent( ot_torrent *torrent, unsigned long amount, char *reply ) { char *r = reply; unsigned long peer_count, index; signed long pool_offset = -1, pool_index = 0; @@ -214,6 +219,48 @@ size_t return_peers_for_torrent( ot_torrent torrent, unsigned long amount, char return r - reply; } +// Fetches scrape info for a specific torrent +size_t return_scrape_for_torrent( ot_hash *hash, char *reply ) { + char *r = reply; + int exactmatch, peers = 0, seeds = 0, i; + ot_vector *torrents_list = &all_torrents[*hash[0]]; + ot_torrent *torrent = BINARY_FIND( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), compare_hash, &exactmatch ); + + if( !exactmatch ) return 0; + clean_peerlist( torrent->peer_list ); + + for( i=0; ipeer_list->peers[i].size; + seeds += torrent->peer_list->seed_count[i]; + } + + MEMMOVE( r, "d5:filesd20:", 12 ); MEMMOVE( r+12, hash, 20 ); + r += FORMAT_FORMAT_STRING( r+32, "d8:completei%de10:downloadedi%lde10:incompletei%deeee", seeds, torrent->peer_list->downloaded, peers-seeds ) + 32; + + return r - reply; +} + +void remove_peer_from_torrent( ot_hash *hash, ot_peer *peer ) { + int exactmatch, i; + ot_vector *torrents_list = &all_torrents[*hash[0]]; + ot_torrent *torrent = BINARY_FIND( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), compare_hash, &exactmatch ); + + if( !exactmatch ) return; + + for( i=0; ipeer_list->peers[i], peer ) ) { + case 0: continue; + case 2: torrent->peer_list->seed_count[i]--; + case 1: default: return; + } + + clean_peerlist( torrent->peer_list ); +} + +void cleanup_torrents( void ) { + +} + int init_logic( char *directory ) { glob_t globber; int i; @@ -257,7 +304,7 @@ void deinit_logic( ) { // Free all torrents... for(i=0; i<256; ++i ) { if( all_torrents[i].size ) { - ot_torrent torrents_list = (ot_torrent)all_torrents[i].data; + ot_torrent *torrents_list = (ot_torrent*)all_torrents[i].data; for( j=0; j