Now actually seems to work for the most parts

Added scraping
Added graceful disconnect
dynamic-accesslists
erdgeist 18 years ago
parent c0f667defe
commit 688a4edd9e

@ -1,6 +1,6 @@
CC?=gcc CC?=gcc
CFLAGS+=-I../libowfat -Wall -pipe -g -ggdb CFLAGS+=-I../libowfat -Wall -pipe -O2
LDFLAGS+=-L../libowfat/ -lowfat LDFLAGS+=-L../libowfat/ -lowfat -s
SOURCES=opentracker.c trackerlogic.c scan_urlencoded_query.c SOURCES=opentracker.c trackerlogic.c scan_urlencoded_query.c

@ -106,13 +106,12 @@ const char* http_header(struct http_data* r,const char* h)
void httpresponse(struct http_data* h,int64 s) void httpresponse(struct http_data* h,int64 s)
{ {
char *c, *d, *data, *reply = NULL; char *c, *d, *data, *reply = NULL;
struct ot_peer peer; ot_peer peer;
ot_torrent torrent; ot_torrent *torrent;
ot_hash *hash = NULL; ot_hash *hash = NULL;
unsigned long numwant; int numwant, tmp, scanon;
int compact, scanon; size_t reply_size = 0;
size_t reply_size = 0;
array_cat0(&h->r); array_cat0(&h->r);
@ -137,14 +136,57 @@ e400:
case 6: /* scrape ? */ case 6: /* scrape ? */
if (byte_diff(data,6,"scrape")) if (byte_diff(data,6,"scrape"))
goto e404; 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; break;
case 8: case 8:
if( byte_diff(data,8,"announce")) if( byte_diff(data,8,"announce"))
goto e404; goto e404;
peer.ip = h->ip; peer.ip = h->ip;
peer.port_flags = 6881 << 16; peer.port_flags = 6881 << 16;
numwant = 50; numwant = 50;
compact = 1;
scanon = 1; scanon = 1;
while( scanon ) { while( scanon ) {
@ -155,19 +197,44 @@ e400:
case -1: /* error */ case -1: /* error */
goto e404; goto e404;
case 4: case 4:
if(!byte_diff(data,4,"port")) if(!byte_diff(data,4,"port")) {
/* scan int */ c; size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
else if(!byte_diff(data,4,"left")) if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) || (tmp > 65536) ) goto e404;
/* scan int */ c; peer.port_flags = ( tmp << 16 ) | ( peer.port_flags & 0xffff );
else } 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 ); scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE );
break; 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: case 7:
if(!byte_diff(data,7,"numwant")) if(!byte_diff(data,7,"numwant")) {
/* scan int */ c; size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
else if(!byte_diff(data,7,"compact")) if( ( len <= 0 ) || scan_fixed_int( data, len, &numwant ) ) goto e404;
/* scan flag */ c; } else if(!byte_diff(data,7,"compact")) {
else 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 ); scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE );
break; break;
case 9: case 9:
@ -180,9 +247,8 @@ e400:
case -1: case -1:
goto e404; goto e404;
case 20: case 20:
hash = (ot_hash*)data; /* Fall through intended */ hash = (ot_hash*)data;
printf("hash: %s\n",*hash); default: // Fall through intended
default:
continue; continue;
} }
default: default:
@ -192,20 +258,25 @@ e400:
} }
/* Scanned whole query string */ /* Scanned whole query string */
if( !hash || ( compact == 0 ) ) goto e404; if( !hash ) goto e404;
torrent = add_peer_to_torrent( hash, &peer ); if( peer.port_flags & PEER_FLAG_STOPPED ) {
if( !torrent ) { 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: e500:
httperror(h,"500 Internal Server Error","A server error has occured. Please retry later."); httperror(h,"500 Internal Server Error","A server error has occured. Please retry later.");
goto bailout; goto bailout;
} }
reply = malloc( numwant*6+24 ); reply = malloc( numwant*6+64 ); // peerlist + seeder, peers and lametta
if( reply ) if( reply )
reply_size = return_peers_for_torrent( torrent, numwant, reply ); reply_size = return_peers_for_torrent( torrent, numwant, reply );
if( !reply || ( reply_size < 0 ) ) { if( !reply || ( reply_size < 0 ) ) {
if( reply ) free( reply ); if( reply ) free( reply );
goto e500; goto e500;
}
} }
break; break;
default: /* neither scrape nor announce */ default: /* neither scrape nor announce */
@ -213,6 +284,7 @@ e404:
httperror(h,"404 Not Found","No such file or directory."); httperror(h,"404 Not Found","No such file or directory.");
goto bailout; goto bailout;
} }
c=h->hdrbuf=(char*)malloc(500); c=h->hdrbuf=(char*)malloc(500);
c+=fmt_str(c,"HTTP/1.1 200 OK\r\nContent-Type: text/plain"); c+=fmt_str(c,"HTTP/1.1 200 OK\r\nContent-Type: text/plain");
c+=fmt_str(c,"\r\nContent-Length: "); c+=fmt_str(c,"\r\nContent-Length: ");
@ -239,7 +311,6 @@ void graceful( int s ) {
int main() int main()
{ {
int s=socket_tcp4(); int s=socket_tcp4();
uint32 scope_id;
unsigned long ip; unsigned long ip;
uint16 port; uint16 port;
@ -284,7 +355,6 @@ int main()
io_close(n); io_close(n);
} else } else
io_close(n); io_close(n);
buffer_putnlflush(buffer_2);
} }
if (errno==EAGAIN) if (errno==EAGAIN)
io_eagain(s); io_eagain(s);

@ -49,3 +49,9 @@ size_t scan_urlencoded_query(char **string, char *deststring, int flags) {
*string = (char *)s; *string = (char *)s;
return d - (unsigned char*)deststring; 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;
}

@ -12,4 +12,9 @@
// or -1 for parse error // or -1 for parse error
size_t scan_urlencoded_query(char **string, char *deststring, int flags); 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 #endif

@ -19,13 +19,14 @@
// //
int compare_hash( const void *hash1, const void *hash2 ) { return memcmp( hash1, hash2, sizeof( ot_hash )); } 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 ) { 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; 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; } return ((ot_peer*)peer1)->port_flags - ((ot_peer*)peer2)->port_flags; }
void *binary_search( const void *key, const void *base, static void *binary_search( const void *key, const void *base,
unsigned long member_count, const unsigned long member_size, unsigned long member_count, const unsigned long member_size,
int (*compar) (const void *, const void *), int (*compar) (const void *, const void *),
int *exactmatch ) { int *exactmatch )
{
ot_byte *lookat = ((ot_byte*)base) + member_size * (member_count >> 1); ot_byte *lookat = ((ot_byte*)base) + member_size * (member_count >> 1);
*exactmatch = 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 // 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 ); ot_byte *match = BINARY_FIND( key, vector->data, vector->size, member_size, compare_func, exactmatch );
if( *exactmatch ) return match; 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++; vector->size++;
return match; 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; int exactmatch;
ot_peer match; ot_peer *match;
if( !vector->size ) return 0; 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; if( !exactmatch ) return 0;
exactmatch = match->port_flags & PEER_FLAG_SEEDING ? 2 : 1; 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--; vector->size--;
return exactmatch; return exactmatch;
} }
void free_peerlist( ot_peerlist peer_list ) { static void free_peerlist( ot_peerlist *peer_list ) {
int i; int i;
for( i=0; i<OT_POOLS_COUNT; ++i ) for( i=0; i<OT_POOLS_COUNT; ++i )
if( peer_list->peers[i].data ) if( peer_list->peers[i].data )
@ -95,10 +96,10 @@ void free_peerlist( ot_peerlist peer_list ) {
free( 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; int exactmatch;
ot_torrent end = ((ot_torrent)vector->data) + vector->size; 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 *match = BINARY_FIND( hash, vector->data, vector->size, sizeof( ot_torrent ), compare_hash, &exactmatch );
if( !exactmatch ) return -1; if( !exactmatch ) return -1;
free_peerlist( match->peer_list ); free_peerlist( match->peer_list );
@ -111,11 +112,12 @@ int vector_remove_torrent( ot_vector vector, ot_hash *hash ) {
return 0; 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; long timedout = NOW-peer_list->base;
int i; int i;
if( !timedout ) return; if( !timedout ) return 0;
if( timedout > OT_POOLS_COUNT ) timedout = OT_POOLS_COUNT; if( timedout > OT_POOLS_COUNT ) timedout = OT_POOLS_COUNT;
for( i=OT_POOLS_COUNT-timedout; i<OT_POOLS_COUNT; ++i ) for( i=OT_POOLS_COUNT-timedout; i<OT_POOLS_COUNT; ++i )
@ -128,39 +130,40 @@ void clean_peerlist( ot_peerlist peer_list ) {
byte_zero( peer_list->seed_count, sizeof( unsigned long ) * timedout ); byte_zero( peer_list->seed_count, sizeof( unsigned long ) * timedout );
peer_list->base = NOW; peer_list->base = NOW;
return timedout == OT_POOLS_COUNT;
} }
ot_torrent add_peer_to_torrent( ot_hash *hash, ot_peer peer ) { ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer ) {
int exactmatch; int exactmatch;
ot_torrent torrent; ot_torrent *torrent;
ot_peer peer_dest; ot_peer *peer_dest;
ot_vector torrents_list = all_torrents + *hash[0], peer_pool; 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( !torrent ) return NULL;
if( !exactmatch ) { if( !exactmatch ) {
// Create a new torrent entry, then // 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 ) { if( !torrent->peer_list ) {
vector_remove_torrent( torrents_list, hash ); vector_remove_torrent( torrents_list, hash );
return NULL; return NULL;
} }
MEMMOVE( &torrent->hash, hash, sizeof( ot_hash ) ); 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; torrent->peer_list->base = NOW;
} else } else
clean_peerlist( torrent->peer_list ); clean_peerlist( torrent->peer_list );
peer_pool = &torrent->peer_list->peers[0]; 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 // If we hadn't had a match in current pool, create peer there and
// remove it from all older pools // remove it from all older pools
if( !exactmatch ) { if( !exactmatch ) {
int i; int i;
MEMMOVE( peer_dest, peer, sizeof( struct ot_peer ) ); MEMMOVE( peer_dest, peer, sizeof( ot_peer ) );
if( peer->port_flags & PEER_FLAG_SEEDING ) if( peer->port_flags & PEER_FLAG_SEEDING )
torrent->peer_list->seed_count[0]++; torrent->peer_list->seed_count[0]++;
for( i=1; i<OT_POOLS_COUNT; ++i ) { for( i=1; i<OT_POOLS_COUNT; ++i ) {
@ -176,6 +179,8 @@ ot_torrent add_peer_to_torrent( ot_hash *hash, ot_peer peer ) {
if( !(peer_dest->port_flags & PEER_FLAG_SEEDING ) && (peer->port_flags & PEER_FLAG_SEEDING ) ) if( !(peer_dest->port_flags & PEER_FLAG_SEEDING ) && (peer->port_flags & PEER_FLAG_SEEDING ) )
torrent->peer_list->seed_count[0]++; torrent->peer_list->seed_count[0]++;
} }
if( peer->port_flags & PEER_FLAG_COMPLETED )
torrent->peer_list->downloaded++;
return torrent; return torrent;
} }
@ -186,7 +191,7 @@ ot_torrent add_peer_to_torrent( ot_hash *hash, ot_peer peer ) {
// * RANDOM may return huge values // * RANDOM may return huge values
// * does not yet check not to return self // * 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; char *r = reply;
unsigned long peer_count, index; unsigned long peer_count, index;
signed long pool_offset = -1, pool_index = 0; 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; 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; i<OT_POOLS_COUNT; ++i ) {
peers += torrent->peer_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; i<OT_POOLS_COUNT; ++i )
switch( vector_remove_peer( &torrent->peer_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 ) { int init_logic( char *directory ) {
glob_t globber; glob_t globber;
int i; int i;
@ -257,7 +304,7 @@ void deinit_logic( ) {
// Free all torrents... // Free all torrents...
for(i=0; i<256; ++i ) { for(i=0; i<256; ++i ) {
if( all_torrents[i].size ) { 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<all_torrents[i].size; ++j ) for( j=0; j<all_torrents[i].size; ++j )
free_peerlist( torrents_list[j].peer_list ); free_peerlist( torrents_list[j].peer_list );
free( all_torrents[i].data ); free( all_torrents[i].data );

@ -32,40 +32,31 @@ typedef time_t ot_time;
#define OT_POOLS_TIMEOUT 300 #define OT_POOLS_TIMEOUT 300
#define NOW (time(NULL)/OT_POOLS_TIMEOUT) #define NOW (time(NULL)/OT_POOLS_TIMEOUT)
typedef struct ot_vector { typedef struct {
void *data; void *data;
size_t size; size_t size;
size_t space; size_t space;
} *ot_vector; } ot_vector;
typedef struct ot_peer { typedef struct {
ot_ip ip; ot_ip ip;
ot_dword port_flags; ot_dword port_flags;
} *ot_peer; } ot_peer;
static const ot_byte PEER_FLAG_SEEDING = 0x80; static const ot_byte PEER_FLAG_SEEDING = 0x80;
static const ot_byte PEER_FLAG_COMPLETED = 0x40;
static const ot_byte PEER_FLAG_STOPPED = 0x20;
typedef struct ot_peerlist { typedef struct {
ot_time base; ot_time base;
unsigned long seed_count[ OT_POOLS_COUNT ]; unsigned long seed_count[ OT_POOLS_COUNT ];
struct ot_vector peers[ OT_POOLS_COUNT ]; unsigned long downloaded;
} *ot_peerlist; ot_vector peers[ OT_POOLS_COUNT ];
} ot_peerlist;
typedef struct ot_torrent { typedef struct {
ot_hash hash; ot_hash hash;
ot_peerlist peer_list; ot_peerlist *peer_list;
} *ot_torrent; } ot_torrent;
void *map_file( char *file_name, size_t map_size );
void unmap_file( char *file_name, void *map, size_t mapped_size, unsigned long real_size );
// This behaves quite like bsearch but allows to find
// the insertion point for inserts after unsuccessful searches
// in this case exactmatch is 0 on exit
//
void *binary_search( const void *key, const void *base,
const unsigned long member_count, const unsigned long member_size,
int (*compar) (const void *, const void *),
int *exactmatch );
// //
// Exported functions // Exported functions
@ -74,7 +65,10 @@ void *binary_search( const void *key, const void *base,
int init_logic( char *chdir_directory ); int init_logic( char *chdir_directory );
void deinit_logic( ); void deinit_logic( );
ot_torrent add_peer_to_torrent( ot_hash *hash, ot_peer peer ); ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer );
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 );
size_t return_scrape_for_torrent( ot_hash *hash, char *reply );
void remove_peer_from_torrent( ot_hash *hash, ot_peer *peer );
void cleanup_torrents( void );
#endif #endif

Loading…
Cancel
Save