Browse Source

* http and udp routines now use thread local buffers passed in workstruct containers. In other words they do not use static_buffer anymore and are considered to be thread safe.

* the new workstruct also introduces a well defined buffer and result passing path
* a new function scan_find_keywords is a wrapper around scan_urlencoded_query that maps keys in url to values passed in an array of ot_keywords structs
* this new function cleans up much of url parameter parsing work, where read_ptr and write_ptr have been introduced rather than the confusing char *c, *data variables
* I now use memcmp instead of byte_diff to allow compiler to optimize constant size string compares
* got rid of UTORRENT_1600_WORKAROUND
* livesync_ticker is now only called from one (currently main) thread to avoid race conditions
dynamic-accesslists
erdgeist 16 years ago
parent
commit
779d6c235f
  1. 3
      Makefile
  2. 67
      opentracker.c
  3. 448
      ot_http.c
  4. 6
      ot_http.h
  5. 3
      ot_livesync.c
  6. 29
      ot_udp.c
  7. 2
      ot_udp.h
  8. 23
      scan_urlencoded_query.c
  9. 16
      scan_urlencoded_query.h
  10. 25
      trackerlogic.h

3
Makefile

@ -24,7 +24,6 @@ BINDIR?=$(PREFIX)/bin
#FEATURES+=-DWANT_SYNC_LIVE #FEATURES+=-DWANT_SYNC_LIVE
#FEATURES+=-DWANT_SYNC_SCRAPE #FEATURES+=-DWANT_SYNC_SCRAPE
#FEATURES+=-DWANT_UTORRENT1600_WORKAROUND
#FEATURES+=-DWANT_IP_FROM_QUERY_STRING #FEATURES+=-DWANT_IP_FROM_QUERY_STRING
#FEATURES+=-DWANT_COMPRESSION_GZIP #FEATURES+=-DWANT_COMPRESSION_GZIP
#FEATURES+=-DWANT_LOG_NETWORKS #FEATURES+=-DWANT_LOG_NETWORKS
@ -37,7 +36,7 @@ FEATURES+=-DWANT_FULLSCRAPE
OPTS_debug=-D_DEBUG -g -ggdb # -pg -fprofile-arcs -ftest-coverage OPTS_debug=-D_DEBUG -g -ggdb # -pg -fprofile-arcs -ftest-coverage
OPTS_production=-Os OPTS_production=-Os
CFLAGS+=-I$(LIBOWFAT_HEADERS) -Wall -pipe -Wextra #-pedantic -ansi CFLAGS+=-I$(LIBOWFAT_HEADERS) -Wall -pipe -Wextra #-ansi -pedantic
LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread -lz LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread -lz
BINARY =opentracker BINARY =opentracker

67
opentracker.c

@ -41,9 +41,6 @@ volatile int g_opentracker_running = 1;
static char * g_serverdir = NULL; static char * g_serverdir = NULL;
/* To always have space for error messages ;) */
static char static_inbuf[8192];
static void panic( const char *routine ) { static void panic( const char *routine ) {
fprintf( stderr, "%s: %s\n", routine, strerror(errno) ); fprintf( stderr, "%s: %s\n", routine, strerror(errno) );
exit( 111 ); exit( 111 );
@ -107,37 +104,44 @@ static void handle_dead( const int64 socket ) {
io_close( socket ); io_close( socket );
} }
static ssize_t handle_read( const int64 clientsocket ) { static ssize_t handle_read( const int64 clientsocket, struct ot_workstruct *ws ) {
struct http_data* h = io_getcookie( clientsocket ); struct http_data* h = io_getcookie( clientsocket );
ssize_t l; ssize_t l;
if( ( l = io_tryread( clientsocket, static_inbuf, sizeof static_inbuf ) ) <= 0 ) { if( ( l = io_tryread( clientsocket, ws->inbuf, ws->inbuf_size ) ) <= 0 ) {
handle_dead( clientsocket ); handle_dead( clientsocket );
return 0; return 0;
} }
/* If we get the whole request in one packet, handle it without copying */ /* If we get the whole request in one packet, handle it without copying */
if( !array_start( &h->data.request ) ) { if( !array_start( &h->data.request ) ) {
if( memchr( static_inbuf, '\n', l ) ) if( memchr( ws->inbuf, '\n', l ) ) {
return http_handle_request( clientsocket, static_inbuf, l ); ws->request = ws->inbuf;
ws->request_size = l;
return http_handle_request( clientsocket, ws );
}
/* ... else take a copy */
h->flag |= STRUCT_HTTP_FLAG_ARRAY_USED; h->flag |= STRUCT_HTTP_FLAG_ARRAY_USED;
array_catb( &h->data.request, static_inbuf, l ); array_catb( &h->data.request, ws->inbuf, l );
return 0; return 0;
} }
h->flag |= STRUCT_HTTP_FLAG_ARRAY_USED; h->flag |= STRUCT_HTTP_FLAG_ARRAY_USED;
array_catb( &h->data.request, static_inbuf, l ); array_catb( &h->data.request, ws->inbuf, l );
if( array_failed( &h->data.request ) ) if( array_failed( &h->data.request ) )
return http_issue_error( clientsocket, CODE_HTTPERROR_500 ); return http_issue_error( clientsocket, ws, CODE_HTTPERROR_500 );
if( array_bytes( &h->data.request ) > 8192 ) if( array_bytes( &h->data.request ) > 8192 )
return http_issue_error( clientsocket, CODE_HTTPERROR_500 ); return http_issue_error( clientsocket, ws, CODE_HTTPERROR_500 );
if( memchr( array_start( &h->data.request ), '\n', array_bytes( &h->data.request ) ) ) if( !memchr( array_start( &h->data.request ), '\n', array_bytes( &h->data.request ) ) )
return http_handle_request( clientsocket, array_start( &h->data.request ), array_bytes( &h->data.request ) ); return 0;
return 0; ws->request = array_start( &h->data.request );
ws->request_size = array_bytes( &h->data.request );
return http_handle_request( clientsocket, ws );
} }
static void handle_write( const int64 clientsocket ) { static void handle_write( const int64 clientsocket ) {
@ -183,9 +187,25 @@ static void handle_accept( const int64 serversocket ) {
} }
static void server_mainloop( ) { static void server_mainloop( ) {
time_t next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL; struct ot_workstruct ws;
struct iovec *iovector; time_t next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL;
int iovec_entries; struct iovec *iovector;
int iovec_entries;
/* Initialize our "thread local storage" */
ws.inbuf = malloc( THREAD_INBUF_SIZE );
ws.outbuf = malloc( THREAD_OUTBUF_SIZE );
#ifdef _DEBUG_HTTPERROR
ws.debugbuf= malloc( THREAD_INBUF_SIZE );
#endif
if( !ws.inbuf || !ws.outbuf )
panic( "Initializing worker failed" );
ws.inbuf_size = THREAD_INBUF_SIZE;
ws.outbuf_size = THREAD_OUTBUF_SIZE;
#ifdef _DEBUG_HTTPERROR
ws.debugbuf_size= THREAD_INBUF_SIZE;
#endif
for( ; ; ) { for( ; ; ) {
int64 i; int64 i;
@ -197,13 +217,13 @@ static void server_mainloop( ) {
if( (intptr_t)cookie == FLAG_TCP ) if( (intptr_t)cookie == FLAG_TCP )
handle_accept( i ); handle_accept( i );
else if( (intptr_t)cookie == FLAG_UDP ) else if( (intptr_t)cookie == FLAG_UDP )
handle_udp4( i ); handle_udp4( i, &ws );
else else
handle_read( i ); handle_read( i, &ws );
} }
while( ( i = mutex_workqueue_popresult( &iovec_entries, &iovector ) ) != -1 ) while( ( i = mutex_workqueue_popresult( &iovec_entries, &iovector ) ) != -1 )
http_sendiovecdata( i, iovec_entries, iovector ); http_sendiovecdata( i, &ws, iovec_entries, iovector );
while( ( i = io_canwrite( ) ) != -1 ) while( ( i = io_canwrite( ) ) != -1 )
handle_write( i ); handle_write( i );
@ -431,7 +451,12 @@ while( scanon ) {
break; break;
case 'f': bound += parse_configfile( optarg ); break; case 'f': bound += parse_configfile( optarg ); break;
case 'h': help( argv[0] ); exit( 0 ); case 'h': help( argv[0] ); exit( 0 );
case 'v': stats_return_tracker_version( static_inbuf ); fputs( static_inbuf, stderr ); exit( 0 ); case 'v': {
char buffer[8192];
stats_return_tracker_version( buffer );
fputs( buffer, stderr );
exit( 0 );
}
default: default:
case '?': usage( argv[0] ); exit( 1 ); case '?': usage( argv[0] ); exit( 1 );
} }

448
ot_http.c

@ -27,26 +27,19 @@
#include "ot_accesslist.h" #include "ot_accesslist.h"
#define OT_MAXMULTISCRAPE_COUNT 64 #define OT_MAXMULTISCRAPE_COUNT 64
static ot_hash multiscrape_buf[OT_MAXMULTISCRAPE_COUNT];
extern char *g_redirecturl; extern char *g_redirecturl;
enum { enum {
SUCCESS_HTTP_HEADER_LENGTH = 80, SUCCESS_HTTP_HEADER_LENGTH = 80,
SUCCESS_HTTP_HEADER_LENGHT_CONTENT_ENCODING = 32, SUCCESS_HTTP_HEADER_LENGTH_CONTENT_ENCODING = 32,
SUCCESS_HTTP_SIZE_OFF = 17 }; SUCCESS_HTTP_SIZE_OFF = 17 };
/* Our static output buffer */
static char static_outbuf[8192];
#ifdef _DEBUG_HTTPERROR
static char debug_request[8192];
#endif
#ifdef _DEBUG_PEERID #ifdef _DEBUG_PEERID
size_t g_this_peerid_len = 0; size_t g_this_peerid_len = 0;
char *g_this_peerid_data = NULL; char *g_this_peerid_data = NULL;
#endif #endif
static void http_senddata( const int64 client_socket, char *buffer, size_t size ) { static void http_senddata( const int64 client_socket, struct ot_workstruct *ws ) {
struct http_data *h = io_getcookie( client_socket ); struct http_data *h = io_getcookie( client_socket );
ssize_t written_size; ssize_t written_size;
@ -56,22 +49,22 @@ static void http_senddata( const int64 client_socket, char *buffer, size_t size
array_reset( &h->data.request ); array_reset( &h->data.request );
} }
written_size = write( client_socket, buffer, size ); written_size = write( client_socket, ws->reply, ws->reply_size );
if( ( written_size < 0 ) || ( (size_t)written_size == size ) ) { if( ( written_size < 0 ) || ( written_size == ws->reply_size ) ) {
free( h ); io_close( client_socket ); free( h ); io_close( client_socket );
} else { } else {
char * outbuf; char * outbuf;
tai6464 t; tai6464 t;
if( !h ) return; if( !h ) return;
if( !( outbuf = malloc( size - written_size ) ) ) { if( !( outbuf = malloc( ws->reply_size - written_size ) ) ) {
free(h); io_close( client_socket ); free(h); io_close( client_socket );
return; return;
} }
iob_reset( &h->data.batch ); iob_reset( &h->data.batch );
memmove( outbuf, buffer + written_size, size - written_size ); memmove( outbuf, ws->reply + written_size, ws->reply_size - written_size );
iob_addbuf_free( &h->data.batch, outbuf, size - written_size ); iob_addbuf_free( &h->data.batch, outbuf, ws->reply_size - written_size );
h->flag |= STRUCT_HTTP_FLAG_IOB_USED; h->flag |= STRUCT_HTTP_FLAG_IOB_USED;
/* writeable short data sockets just have a tcp timeout */ /* writeable short data sockets just have a tcp timeout */
@ -81,33 +74,34 @@ static void http_senddata( const int64 client_socket, char *buffer, size_t size
} }
} }
#define HTTPERROR_302 return http_issue_error( client_socket, CODE_HTTPERROR_302 ) #define HTTPERROR_302 return http_issue_error( client_socket, ws, CODE_HTTPERROR_302 )
#define HTTPERROR_400 return http_issue_error( client_socket, CODE_HTTPERROR_400 ) #define HTTPERROR_400 return http_issue_error( client_socket, ws, CODE_HTTPERROR_400 )
#define HTTPERROR_400_PARAM return http_issue_error( client_socket, CODE_HTTPERROR_400_PARAM ) #define HTTPERROR_400_PARAM return http_issue_error( client_socket, ws, CODE_HTTPERROR_400_PARAM )
#define HTTPERROR_400_COMPACT return http_issue_error( client_socket, CODE_HTTPERROR_400_COMPACT ) #define HTTPERROR_400_COMPACT return http_issue_error( client_socket, ws, CODE_HTTPERROR_400_COMPACT )
#define HTTPERROR_403_IP return http_issue_error( client_socket, CODE_HTTPERROR_403_IP ) #define HTTPERROR_400_DOUBLEHASH return http_issue_error( client_socket, ws, CODE_HTTPERROR_400_PARAM )
#define HTTPERROR_404 return http_issue_error( client_socket, CODE_HTTPERROR_404 ) #define HTTPERROR_403_IP return http_issue_error( client_socket, ws, CODE_HTTPERROR_403_IP )
#define HTTPERROR_500 return http_issue_error( client_socket, CODE_HTTPERROR_500 ) #define HTTPERROR_404 return http_issue_error( client_socket, ws, CODE_HTTPERROR_404 )
ssize_t http_issue_error( const int64 client_socket, int code ) { #define HTTPERROR_500 return http_issue_error( client_socket, ws, CODE_HTTPERROR_500 )
ssize_t http_issue_error( const int64 client_socket, struct ot_workstruct *ws, int code ) {
char *error_code[] = { "302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", char *error_code[] = { "302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request",
"403 Access Denied", "404 Not Found", "500 Internal Server Error" }; "403 Access Denied", "404 Not Found", "500 Internal Server Error" };
char *title = error_code[code]; char *title = error_code[code];
size_t reply_size;
ws->reply = ws->outbuf;
if( code == CODE_HTTPERROR_302 ) if( code == CODE_HTTPERROR_302 )
reply_size = sprintf( static_outbuf, "HTTP/1.0 302 Found\r\nContent-Length: 0\r\nLocation: %s\r\n\r\n", g_redirecturl ); ws->reply_size = snprintf( ws->reply, ws->outbuf_size, "HTTP/1.0 302 Found\r\nContent-Length: 0\r\nLocation: %s\r\n\r\n", g_redirecturl );
else else
reply_size = sprintf( static_outbuf, "HTTP/1.0 %s\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: %zd\r\n\r\n<title>%s</title>\n", title, strlen(title)+16-4,title+4); ws->reply_size = snprintf( ws->reply, ws->outbuf_size, "HTTP/1.0 %s\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: %zd\r\n\r\n<title>%s</title>\n", title, strlen(title)+16-4,title+4);
#ifdef _DEBUG_HTTPERROR #ifdef _DEBUG_HTTPERROR
fprintf( stderr, "DEBUG: invalid request was: %s\n", debug_request ); fprintf( stderr, "DEBUG: invalid request was: %s\n", ws->debugbuf );
#endif #endif
stats_issue_event( EVENT_FAILED, FLAG_TCP, code ); stats_issue_event( EVENT_FAILED, FLAG_TCP, code );
http_senddata( client_socket, static_outbuf, reply_size); http_senddata( client_socket, ws );
return -2; return ws->reply_size = -2;
} }
ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct iovec *iovector ) { ssize_t http_sendiovecdata( const int64 client_socket, struct ot_workstruct *ws, int iovec_entries, struct iovec *iovector ) {
struct http_data *h = io_getcookie( client_socket ); struct http_data *h = io_getcookie( client_socket );
char *header; char *header;
int i; int i;
@ -136,7 +130,7 @@ ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct
} }
/* Prepare space for http header */ /* Prepare space for http header */
header = malloc( SUCCESS_HTTP_HEADER_LENGTH + SUCCESS_HTTP_HEADER_LENGHT_CONTENT_ENCODING ); header = malloc( SUCCESS_HTTP_HEADER_LENGTH + SUCCESS_HTTP_HEADER_LENGTH_CONTENT_ENCODING );
if( !header ) { if( !header ) {
iovec_free( &iovec_entries, &iovector ); iovec_free( &iovec_entries, &iovector );
HTTPERROR_500; HTTPERROR_500;
@ -159,7 +153,7 @@ ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct
h->flag |= STRUCT_HTTP_FLAG_IOB_USED; h->flag |= STRUCT_HTTP_FLAG_IOB_USED;
/* writeable sockets timeout after 10 minutes) */ /* writeable sockets timeout after 10 minutes */
taia_now( &t ); taia_addsec( &t, &t, OT_CLIENT_TIMEOUT_SEND ); taia_now( &t ); taia_addsec( &t, &t, OT_CLIENT_TIMEOUT_SEND );
io_timeout( client_socket, t ); io_timeout( client_socket, t );
io_dontwantread( client_socket ); io_dontwantread( client_socket );
@ -167,9 +161,21 @@ ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct
return 0; return 0;
} }
static ssize_t http_handle_stats( const int64 client_socket, char *data, char *d, size_t l ) { static ssize_t http_handle_stats( const int64 client_socket, struct ot_workstruct *ws, char *read_ptr ) {
char *c = data; static const ot_keywords keywords_main[] =
{ { "mode", 1 }, {"format", 2 }, { NULL, -3 } };
static const ot_keywords keywords_mode[] =
{ { "peer", TASK_STATS_PEERS }, { "conn", TASK_STATS_CONNS }, { "scrp", TASK_STATS_SCRAPE }, { "udp4", TASK_STATS_UDP },
{ "busy", TASK_STATS_BUSY_NETWORKS }, { "torr", TASK_STATS_TORRENTS }, { "fscr", TASK_STATS_FULLSCRAPE },
{ "s24s", TASK_STATS_SLASH24S }, { "tpbs", TASK_STATS_TPB }, { "herr", TASK_STATS_HTTPERRORS },
{ "top10", TASK_STATS_TOP10 }, { "renew", TASK_STATS_RENEW }, { "syncs", TASK_STATS_SYNCS }, { "version", TASK_STATS_VERSION },
{ "startstop", TASK_STATS_STARTSTOP }, { "toraddrem", TASK_STATS_TORADDREM }, { NULL, -3 } };
static const ot_keywords keywords_format[] =
{ { "bin", TASK_FULLSCRAPE_TPB_BINARY }, { "ben", TASK_FULLSCRAPE }, { "url", TASK_FULLSCRAPE_TPB_URLENCODED },
{ "txt", TASK_FULLSCRAPE_TPB_ASCII }, { NULL, -3 } };
int mode = TASK_STATS_PEERS, scanon = 1, format = 0; int mode = TASK_STATS_PEERS, scanon = 1, format = 0;
#ifdef WANT_RESTRICT_STATS #ifdef WANT_RESTRICT_STATS
struct http_data *h = io_getcookie( client_socket ); struct http_data *h = io_getcookie( client_socket );
@ -178,97 +184,26 @@ static ssize_t http_handle_stats( const int64 client_socket, char *data, char *d
#endif #endif
while( scanon ) { while( scanon ) {
switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) { switch( scan_find_keywords( keywords_main, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) {
case -2: scanon = 0; break; /* TERMINATOR */ case -2: scanon = 0; break; /* TERMINATOR */
case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */
default: scan_urlencoded_skipvalue( &c ); break; case -3: scan_urlencoded_skipvalue( &read_ptr ); break;
case 4: case 1: /* matched "mode" */
if( byte_diff(data,4,"mode")) { if( ( mode = scan_find_keywords( keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM;
scan_urlencoded_skipvalue( &c );
continue;
}
switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) {
case 4:
if( !byte_diff(data,4,"peer"))
mode = TASK_STATS_PEERS;
else if( !byte_diff(data,4,"conn"))
mode = TASK_STATS_CONNS;
else if( !byte_diff(data,4,"scrp"))
mode = TASK_STATS_SCRAPE;
else if( !byte_diff(data,4,"tcp4"))
mode = TASK_STATS_TCP;
else if( !byte_diff(data,4,"udp4"))
mode = TASK_STATS_UDP;
else if( !byte_diff(data,4,"busy"))
mode = TASK_STATS_BUSY_NETWORKS;
else if( !byte_diff(data,4,"torr"))
mode = TASK_STATS_TORRENTS;
else if( !byte_diff(data,4,"fscr"))
mode = TASK_STATS_FULLSCRAPE;
else if( !byte_diff(data,4,"s24s"))
mode = TASK_STATS_SLASH24S;
else if( !byte_diff(data,4,"tpbs"))
mode = TASK_STATS_TPB;
else if( !byte_diff(data,4,"herr"))
mode = TASK_STATS_HTTPERRORS;
else
HTTPERROR_400_PARAM;
break;
case 5:
if( !byte_diff(data,5,"top10"))
mode = TASK_STATS_TOP10;
else if( !byte_diff(data,5,"renew"))
mode = TASK_STATS_RENEW;
else if( !byte_diff(data,5,"syncs"))
mode = TASK_STATS_SYNCS;
else
HTTPERROR_400_PARAM;
break;
case 7:
if( !byte_diff(data,7,"version"))
mode = TASK_STATS_VERSION;
else
HTTPERROR_400_PARAM;
break;
case 9:
if( !byte_diff(data,9,"startstop"))
mode = TASK_STATS_STARTSTOP;
else if( !byte_diff(data,9,"toraddrem"))
mode = TASK_STATS_TORADDREM;
else
HTTPERROR_400_PARAM;
break;
}
break; break;
case 6: case 2: /* matched "format" */
if( byte_diff(data,6,"format")) { if( ( format = scan_find_keywords( keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM;
scan_urlencoded_skipvalue( &c );
continue;
}
if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 3 ) HTTPERROR_400_PARAM;
if( !byte_diff(data,3,"bin"))
format = TASK_FULLSCRAPE_TPB_BINARY;
else if( !byte_diff(data,3,"ben"))
format = TASK_FULLSCRAPE;
else if( !byte_diff(data,3,"url"))
format = TASK_FULLSCRAPE_TPB_URLENCODED;
else if( !byte_diff(data,3,"txt"))
format = TASK_FULLSCRAPE_TPB_ASCII;
else
HTTPERROR_400_PARAM;
break; break;
} }
} }
/* Touch variable */
d=d;
#ifdef WANT_FULLSCRAPE #ifdef WANT_FULLSCRAPE
if( mode == TASK_STATS_TPB ) { if( mode == TASK_STATS_TPB ) {
struct http_data* h = io_getcookie( client_socket ); struct http_data* h = io_getcookie( client_socket );
tai6464 t; tai6464 t;
#ifdef WANT_COMPRESSION_GZIP #ifdef WANT_COMPRESSION_GZIP
d[l-1] = 0; ws->request[ws->request_size] = 0;
if( strstr( d, "gzip" ) ) { if( strstr( read_ptr - 1, "gzip" ) ) {
h->flag |= STRUCT_HTTP_FLAG_GZIP; h->flag |= STRUCT_HTTP_FLAG_GZIP;
format |= TASK_FLAG_GZIP; format |= TASK_FLAG_GZIP;
} }
@ -280,7 +215,7 @@ static ssize_t http_handle_stats( const int64 client_socket, char *data, char *d
taia_uint( &t, 0 ); io_timeout( client_socket, t ); taia_uint( &t, 0 ); io_timeout( client_socket, t );
fullscrape_deliver( client_socket, format ); fullscrape_deliver( client_socket, format );
io_dontwantread( client_socket ); io_dontwantread( client_socket );
return -2; return ws->reply_size = -2;
} }
#endif #endif
@ -290,27 +225,24 @@ static ssize_t http_handle_stats( const int64 client_socket, char *data, char *d
/* Complex stats also include expensive memory debugging tools */ /* Complex stats also include expensive memory debugging tools */
taia_uint( &t, 0 ); io_timeout( client_socket, t ); taia_uint( &t, 0 ); io_timeout( client_socket, t );
stats_deliver( client_socket, mode ); stats_deliver( client_socket, mode );
return -2; return ws->reply_size = -2;
} }
/* Simple stats can be answerred immediately */ /* Simple stats can be answerred immediately */
if( !( l = return_stats_for_tracker( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH, mode, 0 ) ) ) HTTPERROR_500; if( !( ws->reply_size = return_stats_for_tracker( ws->reply, mode, 0 ) ) ) HTTPERROR_500;
return l; return ws->reply_size;
} }
#ifdef WANT_FULLSCRAPE #ifdef WANT_FULLSCRAPE
static ssize_t http_handle_fullscrape( const int64 client_socket, char *d, size_t l ) { static ssize_t http_handle_fullscrape( const int64 client_socket, struct ot_workstruct *ws ) {
struct http_data* h = io_getcookie( client_socket ); struct http_data* h = io_getcookie( client_socket );
int format = 0; int format = 0;
tai6464 t; tai6464 t;
/* Touch variables */
d=d;l=l;
#ifdef WANT_COMPRESSION_GZIP #ifdef WANT_COMPRESSION_GZIP
d[l-1] = 0; ws->request[ws->request_size-1] = 0;
if( strstr( d, "gzip" ) ) { if( strstr( ws->request, "gzip" ) ) {
h->flag |= STRUCT_HTTP_FLAG_GZIP; h->flag |= STRUCT_HTTP_FLAG_GZIP;
format = TASK_FLAG_GZIP; format = TASK_FLAG_GZIP;
stats_issue_event( EVENT_FULLSCRAPE_REQUEST_GZIP, *(int*)h->ip, 0 ); stats_issue_event( EVENT_FULLSCRAPE_REQUEST_GZIP, *(int*)h->ip, 0 );
@ -319,7 +251,7 @@ static ssize_t http_handle_fullscrape( const int64 client_socket, char *d, size_
stats_issue_event( EVENT_FULLSCRAPE_REQUEST, *(int*)h->ip, 0 ); stats_issue_event( EVENT_FULLSCRAPE_REQUEST, *(int*)h->ip, 0 );
#ifdef _DEBUG_HTTPERROR #ifdef _DEBUG_HTTPERROR
write( 2, debug_request, l ); write( 2, ws->debugbuf, ws->debugbuf_size );
#endif #endif
/* Pass this task to the worker thread */ /* Pass this task to the worker thread */
@ -328,72 +260,70 @@ write( 2, debug_request, l );
taia_uint( &t, 0 ); io_timeout( client_socket, t ); taia_uint( &t, 0 ); io_timeout( client_socket, t );
fullscrape_deliver( client_socket, TASK_FULLSCRAPE | format ); fullscrape_deliver( client_socket, TASK_FULLSCRAPE | format );
io_dontwantread( client_socket ); io_dontwantread( client_socket );
return -2; return ws->reply_size = -2;
} }
#endif #endif
static ssize_t http_handle_scrape( const int64 client_socket, struct ot_workstruct *ws, char *read_ptr ) {
static const ot_keywords keywords_scrape[] = { { "info_hash", 1 }, { NULL, -3 } };
static ssize_t http_handle_scrape( const int64 client_socket, char *data ) { ot_hash * multiscrape_buf = (ot_hash*)ws->request;
int scanon = 1, numwant = 0; int scanon = 1, numwant = 0;
char *c = data;
size_t l;
/* This is to hack around stupid clients that send "scrape ?info_hash" */ /* This is to hack around stupid clients that send "scrape ?info_hash" */
if( c[-1] != '?' ) { if( read_ptr[-1] != '?' ) {
while( ( *c != '?' ) && ( *c != '\n' ) ) ++c; while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr;
if( *c == '\n' ) HTTPERROR_400_PARAM; if( *read_ptr == '\n' ) HTTPERROR_400_PARAM;
++c; ++read_ptr;
} }
while( scanon ) { while( scanon ) {
switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) { switch( scan_find_keywords( keywords_scrape, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) {
case -2: scanon = 0; break; /* TERMINATOR */ case -2: scanon = 0; break; /* TERMINATOR */
case -1: default: HTTPERROR_400_PARAM; /* PARSE ERROR */
if( numwant ) case -3: scan_urlencoded_skipvalue( &read_ptr ); break;
goto UTORRENT1600_WORKAROUND; case 1: /* matched "info_hash" */
HTTPERROR_400_PARAM; /* PARSE ERROR */
default: scan_urlencoded_skipvalue( &c ); break;
case 9:
if(byte_diff(data,9,"info_hash")) {
scan_urlencoded_skipvalue( &c );
continue;
}
/* ignore this, when we have less than 20 bytes */ /* ignore this, when we have less than 20 bytes */
if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != (ssize_t)sizeof(ot_hash) ) { if( scan_urlencoded_query( &read_ptr, (char*)(multiscrape_buf + numwant++), SCAN_SEARCHPATH_VALUE ) != (ssize_t)sizeof(ot_hash) )
#ifdef WANT_UTORRENT1600_WORKAROUND
if( data[20] != '?' )
#endif
HTTPERROR_400_PARAM; HTTPERROR_400_PARAM;
}
if( numwant < OT_MAXMULTISCRAPE_COUNT )
memmove( multiscrape_buf + numwant++, data, sizeof(ot_hash) );
break; break;
} }
} }
UTORRENT1600_WORKAROUND:
/* No info_hash found? Inform user */ /* No info_hash found? Inform user */
if( !numwant ) HTTPERROR_400_PARAM; if( !numwant ) HTTPERROR_400_PARAM;
/* Limit number of hashes to process */
if( numwant > OT_MAXMULTISCRAPE_COUNT )
numwant = OT_MAXMULTISCRAPE_COUNT;
/* Enough for http header + whole scrape string */ /* Enough for http header + whole scrape string */
if( !( l = return_tcp_scrape_for_torrent( multiscrape_buf, numwant, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf ) ) ) HTTPERROR_500; if( !( ws->reply_size = return_tcp_scrape_for_torrent( multiscrape_buf, numwant, ws->reply ) ) ) HTTPERROR_500;
stats_issue_event( EVENT_SCRAPE, FLAG_TCP, l ); stats_issue_event( EVENT_SCRAPE, FLAG_TCP, ws->reply_size );
return l; return ws->reply_size;
} }
static ssize_t http_handle_announce( const int64 client_socket, char *data ) { static ot_keywords keywords_announce[] = { { "port", 1 }, { "left", 2 }, { "event", 3 }, { "numwant", 4 }, { "compact", 5 }, { "info_hash", 6 },
char *c = data; #ifdef WANT_IP_FROM_QUERY_STRING
int numwant, tmp, scanon; { "ip", 7 },
ot_peer peer; #endif
ot_hash *hash = NULL; #ifdef _DEBUG_PEERID
{ "peer_id", 8 },
#endif
{ NULL, -3 } };
static ot_keywords keywords_announce_event[] = { { "completed", 1 }, { "stopped", 2 }, { NULL, -3 } };
static ssize_t http_handle_announce( const int64 client_socket, struct ot_workstruct *ws, char *read_ptr ) {
int numwant, tmp, scanon;
ot_peer peer;
ot_hash *hash = NULL;
unsigned short port = htons(6881); unsigned short port = htons(6881);
ssize_t len; char *write_ptr;
ssize_t len;
/* This is to hack around stupid clients that send "announce ?info_hash" */ /* This is to hack around stupid clients that send "announce ?info_hash" */
if( c[-1] != '?' ) { if( read_ptr[-1] != '?' ) {
while( ( *c != '?' ) && ( *c != '\n' ) ) ++c; while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr;
if( *c == '\n' ) HTTPERROR_400_PARAM; if( *read_ptr == '\n' ) HTTPERROR_400_PARAM;
++c; ++read_ptr;
} }
OT_SETIP( &peer, ((struct http_data*)io_getcookie( client_socket ) )->ip ); OT_SETIP( &peer, ((struct http_data*)io_getcookie( client_socket ) )->ip );
@ -403,168 +333,156 @@ static ssize_t http_handle_announce( const int64 client_socket, char *data ) {
scanon = 1; scanon = 1;
#ifdef _DEBUG_PEERID #ifdef _DEBUG_PEERID
g_this_peerid_data = NULL; ws->peer_id = NULL;
#endif #endif
while( scanon ) { while( scanon ) {
switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) { switch( scan_find_keywords(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) {
case -2: scanon = 0; break; /* TERMINATOR */ case -2: scanon = 0; break; /* TERMINATOR */
case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */
default: scan_urlencoded_skipvalue( &c ); break; case -3: scan_urlencoded_skipvalue( &read_ptr ); break;
#ifdef WANT_IP_FROM_QUERY_STRING case 1: /* matched "port" */
case 2: len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE );
if(!byte_diff(data,2,"ip")) { if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) || ( tmp > 0xffff ) ) HTTPERROR_400_PARAM;
unsigned char ip[4]; port = htons( tmp ); OT_SETPORT( &peer, &port );
len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
if( ( len <= 0 ) || scan_fixed_ip( data, len, ip ) ) HTTPERROR_400_PARAM;
OT_SETIP( &peer, ip );
} else
scan_urlencoded_skipvalue( &c );
break;
#endif
case 4:
if( !byte_diff( data, 4, "port" ) ) {
len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) || ( tmp > 0xffff ) ) HTTPERROR_400_PARAM;
port = htons( tmp ); OT_SETPORT( &peer, &port );
} else if( !byte_diff( data, 4, "left" ) ) {
if( ( len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM;
if( scan_fixed_int( data, len, &tmp ) ) tmp = 0;
if( !tmp ) OT_PEERFLAG( &peer ) |= PEER_FLAG_SEEDING;
} else
scan_urlencoded_skipvalue( &c );
break; break;
case 5: case 2: /* matched "left" */
if( byte_diff( data, 5, "event" ) ) if( ( len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM;
scan_urlencoded_skipvalue( &c ); if( scan_fixed_int( write_ptr, len, &tmp ) ) tmp = 0;
else switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) { if( !tmp ) OT_PEERFLAG( &peer ) |= PEER_FLAG_SEEDING;
case -1: break;
HTTPERROR_400_PARAM; case 3: /* matched "event" */
case 7: switch( scan_find_keywords( keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) {
if( !byte_diff( data, 7, "stopped" ) ) OT_PEERFLAG( &peer ) |= PEER_FLAG_STOPPED; case -1: HTTPERROR_400_PARAM;
break; case 1: /* matched "completed" */
case 9: OT_PEERFLAG( &peer ) |= PEER_FLAG_COMPLETED;
if( !byte_diff( data, 9, "completed" ) ) OT_PEERFLAG( &peer ) |= PEER_FLAG_COMPLETED; break;
default: /* Fall through intended */ case 2: /* matched "stopped" */
break; OT_PEERFLAG( &peer ) |= PEER_FLAG_STOPPED;
break;
default:
break;
} }
break; break;
case 7: case 4: /* matched "numwant" */
if(!byte_diff(data,7,"numwant")) { len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE );
len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &numwant ) ) HTTPERROR_400_PARAM;
if( ( len <= 0 ) || scan_fixed_int( data, len, &numwant ) ) HTTPERROR_400_PARAM; if( numwant < 0 ) numwant = 50;
if( numwant < 0 ) numwant = 50; if( numwant > 200 ) numwant = 200;
if( numwant > 200 ) numwant = 200;
} else if(!byte_diff(data,7,"compact")) {
len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) ) HTTPERROR_400_PARAM;
if( !tmp ) HTTPERROR_400_COMPACT;
} else
#ifdef _DEBUG_PEERID
if(!byte_diff(data,7,"peer_id")) {
g_this_peerid_len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
g_this_peerid_data = g_this_peerid_len > 0 ? data : 0;
} else
#endif
scan_urlencoded_skipvalue( &c );
break; break;
case 9: case 5: /* matched "compact" */
if(byte_diff(data,9,"info_hash")) { len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE );
scan_urlencoded_skipvalue( &c ); if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) ) HTTPERROR_400_PARAM;
continue; if( !tmp ) HTTPERROR_400_COMPACT;
} break;
case 6: /* matched "info_hash" */
if( hash ) HTTPERROR_400_DOUBLEHASH;
/* ignore this, when we have less than 20 bytes */ /* ignore this, when we have less than 20 bytes */
if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM; if( scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM;
hash = (ot_hash*)data; hash = (ot_hash*)write_ptr;
break; break;
#ifdef WANT_IP_FROM_QUERY_STRING
case 7: /* matched "ip" */
len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE );
if( ( len <= 0 ) || scan_fixed_ip( write_ptr, len, (unsigned char*)/*tmp*/ws->reply ) ) HTTPERROR_400_PARAM;
OT_SETIP( &peer, /*tmp*/ws->reply );
break;
#endif
#ifdef _DEBUG_PEERID
case 8: /* matched "peer_id" */
ws->peer_id_size = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE );
ws->peer_id = ws->peer_id_size > 0 ? write_ptr : 0;
break;
#endif
} }
} }
/* Scanned whole query string */ /* Scanned whole query string */
if( !hash ) if( !hash )
return sprintf( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH, "d14:failure reason80:Your client forgot to send your torrent's info_hash. Please upgrade your client.e" ); return ws->reply_size = sprintf( ws->reply, "d14:failure reason80:Your client forgot to send your torrent's info_hash. Please upgrade your client.e" );
if( OT_PEERFLAG( &peer ) & PEER_FLAG_STOPPED ) if( OT_PEERFLAG( &peer ) & PEER_FLAG_STOPPED )
len = remove_peer_from_torrent( hash, &peer, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf, FLAG_TCP ); ws->reply_size = remove_peer_from_torrent( hash, &peer, ws->reply, FLAG_TCP );
else else
len = add_peer_to_torrent_and_return_peers(hash, &peer, FLAG_TCP, numwant, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf); ws->reply_size = add_peer_to_torrent_and_return_peers(hash, &peer, FLAG_TCP, numwant, ws->reply );
if( !len ) HTTPERROR_500; if( !ws->reply_size ) HTTPERROR_500;
stats_issue_event( EVENT_ANNOUNCE, FLAG_TCP, len); stats_issue_event( EVENT_ANNOUNCE, FLAG_TCP, ws->reply_size);
return len; return ws->reply_size;
} }
ssize_t http_handle_request( const int64 client_socket, char *data, size_t recv_length ) { ssize_t http_handle_request( const int64 client_socket, struct ot_workstruct *ws ) {
char *c, *recv_header=data; ssize_t reply_off, len;
ssize_t reply_size = 0, reply_off, len; char *read_ptr = ws->request, *write_ptr;
#ifdef _DEBUG_HTTPERROR #ifdef _DEBUG_HTTPERROR
if( recv_length >= sizeof( debug_request ) ) reply_off = ws->request_size;
recv_length = sizeof( debug_request) - 1; if( ws->request_size >= (ssize_t)ws->debugbuf_size )
memmove( debug_request, recv_header, recv_length ); reply_off = ws->debugbuf_size - 1;
debug_request[ recv_length ] = 0; memmove( ws->debugbuf, ws->request, reply_off );
ws->debugbuf[ reply_off ] = 0;
#endif #endif
/* Tell subroutines where to put reply data */
ws->reply = ws->outbuf + SUCCESS_HTTP_HEADER_LENGTH;
/* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */ /* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */
if( byte_diff( data, 5, "GET /") ) HTTPERROR_400; if( memcmp( read_ptr, "GET /", 5) ) HTTPERROR_400;
/* Skip leading '/' */ /* Skip leading '/' */
for( c = data+4; *c == '/'; ++c); for( read_ptr+=4; *read_ptr == '/'; ++read_ptr);
/* Try to parse the request. /* Try to parse the request.
In reality we abandoned requiring the url to be correct. This now In reality we abandoned requiring the url to be correct. This now
only decodes url encoded characters, we check for announces and only decodes url encoded characters, we check for announces and
scrapes by looking for "a*" or "sc" */ scrapes by looking for "a*" or "sc" */
len = scan_urlencoded_query( &c, data = c, SCAN_PATH ); len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_PATH );
/* If parsing returned an error, leave with not found */ /* If parsing returned an error, leave with not found */
if( g_redirecturl && ( len == -2 ) ) HTTPERROR_302; if( g_redirecturl && ( len == -2 ) ) HTTPERROR_302;
if( len <= 0 ) HTTPERROR_404; if( len <= 0 ) HTTPERROR_404;
/* This is the hardcore match for announce*/ /* This is the hardcore match for announce*/
if( ( *data == 'a' ) || ( *data == '?' ) ) if( ( *write_ptr == 'a' ) || ( *write_ptr == '?' ) )
reply_size = http_handle_announce( client_socket, c ); http_handle_announce( client_socket, ws, read_ptr );
#ifdef WANT_FULLSCRAPE #ifdef WANT_FULLSCRAPE
else if( !byte_diff( data, 12, "scrape HTTP/" ) ) else if( !memcmp( write_ptr, "scrape HTTP/", 12 ) )
reply_size = http_handle_fullscrape( client_socket, recv_header, recv_length ); http_handle_fullscrape( client_socket, ws );
#endif #endif
/* This is the hardcore match for scrape */ /* This is the hardcore match for scrape */
else if( !byte_diff( data, 2, "sc" ) ) else if( !memcmp( write_ptr, "sc", 2 ) )
reply_size = http_handle_scrape( client_socket, c ); http_handle_scrape( client_socket, ws, read_ptr );
/* All the rest is matched the standard way */ /* All the rest is matched the standard way */
else switch( len ) { else if( !memcmp( write_ptr, "stats", 5) )
case 5: /* stats ? */ http_handle_stats( client_socket, ws, read_ptr );
if( byte_diff( data, 5, "stats") ) HTTPERROR_404; else
reply_size = http_handle_stats( client_socket, c, recv_header, recv_length );
break;
default:
HTTPERROR_404; HTTPERROR_404;
}
/* If routines handled sending themselves, just return */ /* If routines handled sending themselves, just return */
if( reply_size == -2 ) return 0; if( ws->reply_size == -2 ) return 0;
/* If routine failed, let http error take over */ /* If routine failed, let http error take over */
if( reply_size == -1 ) HTTPERROR_500; if( ws->reply_size == -1 ) HTTPERROR_500;
/* This one is rather ugly, so I take you step by step through it. /* This one is rather ugly, so I take you step by step through it.
1. In order to avoid having two buffers, one for header and one for content, we allow all above functions from trackerlogic to 1. In order to avoid having two buffers, one for header and one for content, we allow all above functions from trackerlogic to
write to a fixed location, leaving SUCCESS_HTTP_HEADER_LENGTH bytes in our static buffer, which is enough for the static string write to a fixed location, leaving SUCCESS_HTTP_HEADER_LENGTH bytes in our work buffer, which is enough for the static string
plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for its expansion and calculate plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for its expansion and calculate
the space NOT needed to expand in reply_off the space NOT needed to expand in reply_off
*/ */
reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf( static_outbuf, 0, "%zd", reply_size ); reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf( ws->outbuf, 0, "%zd", ws->reply_size );
ws->reply = ws->outbuf + reply_off;
/* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete
packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */ packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */
reply_size += 1 + sprintf( static_outbuf + reply_off, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", reply_size ); ws->reply_size += 1 + sprintf( ws->reply, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", ws->reply_size );
/* 3. Finally we join both blocks neatly */ /* 3. Finally we join both blocks neatly */
static_outbuf[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n'; ws->outbuf[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n';
http_senddata( client_socket, static_outbuf + reply_off, reply_size ); http_senddata( client_socket, ws );
return reply_size; return ws->reply_size;
} }
const char *g_version_http_c = "$Source$: $Revision$\n"; const char *g_version_http_c = "$Source$: $Revision$\n";

6
ot_http.h

@ -23,8 +23,8 @@ struct http_data {
STRUCT_HTTP_FLAG flag; STRUCT_HTTP_FLAG flag;
}; };
ssize_t http_handle_request( const int64 s, char *data, size_t l ); ssize_t http_handle_request( const int64 s, struct ot_workstruct *ws );
ssize_t http_sendiovecdata( const int64 s, int iovec_entries, struct iovec *iovector ); ssize_t http_sendiovecdata( const int64 s, struct ot_workstruct *ws, int iovec_entries, struct iovec *iovector );
ssize_t http_issue_error( const int64 s, int code ); ssize_t http_issue_error( const int64 s, struct ot_workstruct *ws, int code );
#endif #endif

3
ot_livesync.c

@ -400,9 +400,6 @@ static void * livesync_worker( void * args ) {
default: default:
break; break;
} }
/* Handle outstanding requests */
livesync_ticker( );
} }
/* Never returns. */ /* Never returns. */

29
ot_udp.c

@ -17,9 +17,6 @@
#include "ot_udp.h" #include "ot_udp.h"
#include "ot_stats.h" #include "ot_stats.h"
static char static_inbuf[8192];
static char static_outbuf[8192];
static const uint8_t g_static_connid[8] = { 0x23, 0x42, 0x05, 0x17, 0xde, 0x41, 0x50, 0xff }; static const uint8_t g_static_connid[8] = { 0x23, 0x42, 0x05, 0x17, 0xde, 0x41, 0x50, 0xff };
static void udp_make_connectionid( uint32_t * connid, const char * remoteip ) { static void udp_make_connectionid( uint32_t * connid, const char * remoteip ) {
@ -39,17 +36,17 @@ static int udp_test_connectionid( const uint32_t * const connid, const char * re
} }
/* UDP implementation according to http://xbtt.sourceforge.net/udp_tracker_protocol.html */ /* UDP implementation according to http://xbtt.sourceforge.net/udp_tracker_protocol.html */
void handle_udp4( int64 serversocket ) { void handle_udp4( int64 serversocket, struct ot_workstruct *ws ) {
ot_peer peer; ot_peer peer;
ot_hash *hash = NULL; ot_hash *hash = NULL;
char remoteip[4]; char remoteip[4];
uint32_t *inpacket = (uint32_t*)static_inbuf; uint32_t *inpacket = (uint32_t*)ws->inbuf;
uint32_t *outpacket = (uint32_t*)static_outbuf; uint32_t *outpacket = (uint32_t*)ws->outbuf;
uint32_t numwant, left, event; uint32_t numwant, left, event;
uint16_t port, remoteport; uint16_t port, remoteport;
size_t r, r_out; size_t r, r_out;
r = socket_recv4( serversocket, static_inbuf, sizeof( static_inbuf ), remoteip, &remoteport); r = socket_recv4( serversocket, ws->inbuf, ws->inbuf_size, remoteip, &remoteport);
stats_issue_event( EVENT_ACCEPT, FLAG_UDP, ntohl(*(uint32_t*)remoteip) ); stats_issue_event( EVENT_ACCEPT, FLAG_UDP, ntohl(*(uint32_t*)remoteip) );
stats_issue_event( EVENT_READ, FLAG_UDP, r ); stats_issue_event( EVENT_READ, FLAG_UDP, r );
@ -58,8 +55,6 @@ void handle_udp4( int64 serversocket ) {
if( r < 16 ) if( r < 16 )
return; return;
/* fprintf( stderr, "UDP Connection id: %16llX\n", *(uint64_t*)inpacket ); */
switch( ntohl( inpacket[2] ) ) { switch( ntohl( inpacket[2] ) ) {
case 0: /* This is a connect action */ case 0: /* This is a connect action */
/* look for udp bittorrent magic id */ /* look for udp bittorrent magic id */
@ -70,7 +65,7 @@ void handle_udp4( int64 serversocket ) {
outpacket[1] = inpacket[3]; outpacket[1] = inpacket[3];
udp_make_connectionid( outpacket + 2, remoteip ); udp_make_connectionid( outpacket + 2, remoteip );
socket_send4( serversocket, static_outbuf, 16, remoteip, remoteport ); socket_send4( serversocket, ws->outbuf, 16, remoteip, remoteport );
stats_issue_event( EVENT_CONNECT, FLAG_UDP, 16 ); stats_issue_event( EVENT_CONNECT, FLAG_UDP, 16 );
break; break;
case 1: /* This is an announce action */ case 1: /* This is an announce action */
@ -88,8 +83,8 @@ void handle_udp4( int64 serversocket ) {
if (numwant > 200) numwant = 200; if (numwant > 200) numwant = 200;
event = ntohl( inpacket[80/4] ); event = ntohl( inpacket[80/4] );
port = *(uint16_t*)( static_inbuf + 96 ); port = *(uint16_t*)( ((char*)inpacket) + 96 );
hash = (ot_hash*)( static_inbuf + 16 ); hash = (ot_hash*)( ((char*)inpacket) + 16 );
OT_SETIP( &peer, remoteip ); OT_SETIP( &peer, remoteip );
OT_SETPORT( &peer, &port ); OT_SETPORT( &peer, &port );
@ -108,11 +103,11 @@ void handle_udp4( int64 serversocket ) {
outpacket[1] = inpacket[12/4]; outpacket[1] = inpacket[12/4];
if( OT_PEERFLAG( &peer ) & PEER_FLAG_STOPPED ) /* Peer is gone. */ if( OT_PEERFLAG( &peer ) & PEER_FLAG_STOPPED ) /* Peer is gone. */
r = remove_peer_from_torrent( hash, &peer, static_outbuf, FLAG_UDP ); r = remove_peer_from_torrent( hash, &peer, ws->outbuf, FLAG_UDP );
else else
r = 8 + add_peer_to_torrent_and_return_peers( hash, &peer, FLAG_UDP, numwant, static_outbuf + 8 ); r = 8 + add_peer_to_torrent_and_return_peers( hash, &peer, FLAG_UDP, numwant, ((char*)outpacket) + 8 );
socket_send4( serversocket, static_outbuf, r, remoteip, remoteport ); socket_send4( serversocket, ws->outbuf, r, remoteip, remoteport );
stats_issue_event( EVENT_ANNOUNCE, FLAG_UDP, r ); stats_issue_event( EVENT_ANNOUNCE, FLAG_UDP, r );
break; break;
@ -124,9 +119,9 @@ void handle_udp4( int64 serversocket ) {
outpacket[1] = inpacket[12/4]; outpacket[1] = inpacket[12/4];
for( r_out = 0; ( r_out * 20 < r - 16) && ( r_out <= 74 ); r_out++ ) for( r_out = 0; ( r_out * 20 < r - 16) && ( r_out <= 74 ); r_out++ )
return_udp_scrape_for_torrent( (ot_hash*)( static_inbuf + 16 + 20 * r_out ), static_outbuf + 8 + 12 * r_out ); return_udp_scrape_for_torrent( (ot_hash*)( ((char*)inpacket) + 16 + 20 * r_out ), ((char*)outpacket) + 8 + 12 * r_out );
socket_send4( serversocket, static_outbuf, 8 + 12 * r_out, remoteip, remoteport ); socket_send4( serversocket, ws->outbuf, 8 + 12 * r_out, remoteip, remoteport );
stats_issue_event( EVENT_SCRAPE, FLAG_UDP, r ); stats_issue_event( EVENT_SCRAPE, FLAG_UDP, r );
break; break;
} }

2
ot_udp.h

@ -6,6 +6,6 @@
#ifndef __OT_UDP_H__ #ifndef __OT_UDP_H__
#define __OT_UDP_H__ #define __OT_UDP_H__
void handle_udp4( int64 serversocket ); void handle_udp4( int64 serversocket, struct ot_workstruct *ws );
#endif #endif

23
scan_urlencoded_query.c

@ -9,6 +9,9 @@
/* Libwofat */ /* Libwofat */
#include "scan.h" #include "scan.h"
/* System */
#include <string.h>
/* Idea is to do a in place replacement or guarantee at least /* Idea is to do a in place replacement or guarantee at least
strlen( string ) bytes in deststring strlen( string ) bytes in deststring
watch http://www.ietf.org/rfc/rfc2396.txt watch http://www.ietf.org/rfc/rfc2396.txt
@ -64,6 +67,22 @@ void scan_urlencoded_skipvalue( char **string ) {
*string = (char*)s; *string = (char*)s;
} }
int scan_find_keywords( const ot_keywords * keywords, char **string, SCAN_SEARCHPATH_FLAG flags) {
char *deststring = *string;
ssize_t match_length = scan_urlencoded_query(string, deststring, flags );
if( match_length < 0 ) return match_length;
if( match_length == 0 ) return -3;
while( keywords->key ) {
if( !memcmp( keywords->key, deststring, match_length ) )
return keywords->value;
keywords++;
}
return -3;
}
ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags) { ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags) {
const unsigned char* s=*(const unsigned char**) string; const unsigned char* s=*(const unsigned char**) string;
unsigned char *d = (unsigned char*)deststring; unsigned char *d = (unsigned char*)deststring;
@ -95,9 +114,7 @@ ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_F
--s; --s;
break; break;
case '?': case '?':
/* XXX to help us parse path?param=value?param=value?... sent by µTorrent 1600 if( flags != SCAN_PATH ) return -1;
do not return an error but silently terminate
if( flags != SCAN_PATH ) return -1; */
break; break;
case '=': case '=':
if( flags != SCAN_SEARCHPATH_PARAM ) return -1; if( flags != SCAN_SEARCHPATH_PARAM ) return -1;

16
scan_urlencoded_query.h

@ -8,6 +8,11 @@
#include <sys/types.h> #include <sys/types.h>
typedef struct {
char *key;
int value;
} ot_keywords;
typedef enum { typedef enum {
SCAN_PATH = 1, SCAN_PATH = 1,
SCAN_SEARCHPATH_PARAM = 2, SCAN_SEARCHPATH_PARAM = 2,
@ -21,9 +26,20 @@ typedef enum {
flags determines, what to parse flags determines, what to parse
returns number of valid converted characters in deststring returns number of valid converted characters in deststring
or -1 for parse error or -1 for parse error
or -2 for terminator found
*/ */
ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags); ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags);
/* string in: pointer to source
out: pointer to next scan position
flags determines, what to parse
returns value for matched keyword
or -1 for parse error
or -2 for terminator found
or -3 for no keyword matched
*/
int scan_find_keywords( const ot_keywords * keywords, char **string, SCAN_SEARCHPATH_FLAG flags);
/* string in: pointer to value of a param=value pair to skip /* string in: pointer to value of a param=value pair to skip
out: pointer to next scan position on return out: pointer to next scan position on return
*/ */

25
trackerlogic.h

@ -96,6 +96,31 @@ struct ot_peerlist {
}; };
#define OT_PEERLIST_HASBUCKETS(peer_list) ((peer_list) && ((peer_list)->peers.size > (peer_list)->peers.space)) #define OT_PEERLIST_HASBUCKETS(peer_list) ((peer_list) && ((peer_list)->peers.size > (peer_list)->peers.space))
struct ot_workstruct {
/* Thread specific, static */
#define THREAD_INBUF_SIZE 8192
char *inbuf;
size_t inbuf_size;
#define THREAD_OUTBUF_SIZE 8192
char *outbuf;
size_t outbuf_size;
#ifdef _DEBUG_HTTPERROR
#define THREAD_DEBUGBUF_SIZE 8192
char *debugbuf;
size_t debugbuf_size;
#endif
/* HTTP specific, non static */
char *request;
ssize_t request_size;
char *reply;
ssize_t reply_size;
#ifdef _DEBUG_PEERID
char *peer_id;
ssize_t peer_id_size;
#endif
};
/* /*
Exported functions Exported functions
*/ */

Loading…
Cancel
Save