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
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

@ -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);

@ -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;
}

@ -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

@ -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; i<OT_POOLS_COUNT; ++i )
if( peer_list->peers[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; 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 );
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; 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 ) )
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; 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 ) {
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<all_torrents[i].size; ++j )
free_peerlist( torrents_list[j].peer_list );
free( all_torrents[i].data );

@ -32,40 +32,31 @@ typedef time_t ot_time;
#define OT_POOLS_TIMEOUT 300
#define NOW (time(NULL)/OT_POOLS_TIMEOUT)
typedef struct ot_vector {
typedef struct {
void *data;
size_t size;
size_t space;
} *ot_vector;
} ot_vector;
typedef struct ot_peer {
typedef struct {
ot_ip ip;
ot_dword port_flags;
} *ot_peer;
} ot_peer;
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 {
ot_time base;
unsigned long seed_count[ OT_POOLS_COUNT ];
struct ot_vector peers[ OT_POOLS_COUNT ];
} *ot_peerlist;
typedef struct {
ot_time base;
unsigned long seed_count[ OT_POOLS_COUNT ];
unsigned long downloaded;
ot_vector peers[ OT_POOLS_COUNT ];
} ot_peerlist;
typedef struct ot_torrent {
ot_hash hash;
ot_peerlist peer_list;
} *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 );
typedef struct {
ot_hash hash;
ot_peerlist *peer_list;
} ot_torrent;
//
// Exported functions
@ -74,7 +65,10 @@ void *binary_search( const void *key, const void *base,
int init_logic( char *chdir_directory );
void deinit_logic( );
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 );
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_scrape_for_torrent( ot_hash *hash, char *reply );
void remove_peer_from_torrent( ot_hash *hash, ot_peer *peer );
void cleanup_torrents( void );
#endif

Loading…
Cancel
Save