From 779d6c235ff8fe5284fd10dc82a9b99e7fa38d06 Mon Sep 17 00:00:00 2001 From: erdgeist <> Date: Mon, 5 Jan 2009 18:05:39 +0000 Subject: * 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 --- ot_http.c | 454 +++++++++++++++++++++++++------------------------------------- 1 file changed, 186 insertions(+), 268 deletions(-) (limited to 'ot_http.c') diff --git a/ot_http.c b/ot_http.c index 55c524d..8c85689 100644 --- a/ot_http.c +++ b/ot_http.c @@ -27,26 +27,19 @@ #include "ot_accesslist.h" #define OT_MAXMULTISCRAPE_COUNT 64 -static ot_hash multiscrape_buf[OT_MAXMULTISCRAPE_COUNT]; extern char *g_redirecturl; enum { SUCCESS_HTTP_HEADER_LENGTH = 80, - SUCCESS_HTTP_HEADER_LENGHT_CONTENT_ENCODING = 32, + SUCCESS_HTTP_HEADER_LENGTH_CONTENT_ENCODING = 32, 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 size_t g_this_peerid_len = 0; char *g_this_peerid_data = NULL; #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 ); 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 ); } - written_size = write( client_socket, buffer, size ); - if( ( written_size < 0 ) || ( (size_t)written_size == size ) ) { + written_size = write( client_socket, ws->reply, ws->reply_size ); + if( ( written_size < 0 ) || ( written_size == ws->reply_size ) ) { free( h ); io_close( client_socket ); } else { char * outbuf; tai6464 t; if( !h ) return; - if( !( outbuf = malloc( size - written_size ) ) ) { + if( !( outbuf = malloc( ws->reply_size - written_size ) ) ) { free(h); io_close( client_socket ); return; } iob_reset( &h->data.batch ); - memmove( outbuf, buffer + written_size, size - written_size ); - iob_addbuf_free( &h->data.batch, outbuf, size - written_size ); + memmove( outbuf, ws->reply + written_size, ws->reply_size - written_size ); + iob_addbuf_free( &h->data.batch, outbuf, ws->reply_size - written_size ); h->flag |= STRUCT_HTTP_FLAG_IOB_USED; /* 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_400 return http_issue_error( client_socket, CODE_HTTPERROR_400 ) -#define HTTPERROR_400_PARAM return http_issue_error( client_socket, CODE_HTTPERROR_400_PARAM ) -#define HTTPERROR_400_COMPACT return http_issue_error( client_socket, CODE_HTTPERROR_400_COMPACT ) -#define HTTPERROR_403_IP return http_issue_error( client_socket, CODE_HTTPERROR_403_IP ) -#define HTTPERROR_404 return http_issue_error( client_socket, CODE_HTTPERROR_404 ) -#define HTTPERROR_500 return http_issue_error( client_socket, CODE_HTTPERROR_500 ) -ssize_t http_issue_error( const int64 client_socket, int code ) { +#define HTTPERROR_302 return http_issue_error( client_socket, ws, CODE_HTTPERROR_302 ) +#define HTTPERROR_400 return http_issue_error( client_socket, ws, CODE_HTTPERROR_400 ) +#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, ws, CODE_HTTPERROR_400_COMPACT ) +#define HTTPERROR_400_DOUBLEHASH return http_issue_error( client_socket, ws, CODE_HTTPERROR_400_PARAM ) +#define HTTPERROR_403_IP return http_issue_error( client_socket, ws, CODE_HTTPERROR_403_IP ) +#define HTTPERROR_404 return http_issue_error( client_socket, ws, CODE_HTTPERROR_404 ) +#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", "403 Access Denied", "404 Not Found", "500 Internal Server Error" }; - char *title = error_code[code]; - size_t reply_size; + char *title = error_code[code]; + ws->reply = ws->outbuf; 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 - 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%s\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%s\n", title, strlen(title)+16-4,title+4); #ifdef _DEBUG_HTTPERROR - fprintf( stderr, "DEBUG: invalid request was: %s\n", debug_request ); + fprintf( stderr, "DEBUG: invalid request was: %s\n", ws->debugbuf ); #endif stats_issue_event( EVENT_FAILED, FLAG_TCP, code ); - http_senddata( client_socket, static_outbuf, reply_size); - return -2; + http_senddata( client_socket, ws ); + 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 ); char *header; int i; @@ -136,7 +130,7 @@ ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct } /* 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 ) { iovec_free( &iovec_entries, &iovector ); 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; - /* writeable sockets timeout after 10 minutes) */ + /* writeable sockets timeout after 10 minutes */ taia_now( &t ); taia_addsec( &t, &t, OT_CLIENT_TIMEOUT_SEND ); io_timeout( client_socket, t ); io_dontwantread( client_socket ); @@ -167,9 +161,21 @@ ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct return 0; } -static ssize_t http_handle_stats( const int64 client_socket, char *data, char *d, size_t l ) { - char *c = data; +static ssize_t http_handle_stats( const int64 client_socket, struct ot_workstruct *ws, char *read_ptr ) { +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; + #ifdef WANT_RESTRICT_STATS 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 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 -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ - default: scan_urlencoded_skipvalue( &c ); break; - case 4: - if( byte_diff(data,4,"mode")) { - 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; - } + case -3: scan_urlencoded_skipvalue( &read_ptr ); break; + case 1: /* matched "mode" */ + if( ( mode = scan_find_keywords( keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; break; - case 6: - if( byte_diff(data,6,"format")) { - 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; + case 2: /* matched "format" */ + if( ( format = scan_find_keywords( keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; break; } } - /* Touch variable */ - d=d; #ifdef WANT_FULLSCRAPE if( mode == TASK_STATS_TPB ) { struct http_data* h = io_getcookie( client_socket ); tai6464 t; #ifdef WANT_COMPRESSION_GZIP - d[l-1] = 0; - if( strstr( d, "gzip" ) ) { + ws->request[ws->request_size] = 0; + if( strstr( read_ptr - 1, "gzip" ) ) { h->flag |= STRUCT_HTTP_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 ); fullscrape_deliver( client_socket, format ); io_dontwantread( client_socket ); - return -2; + return ws->reply_size = -2; } #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 */ taia_uint( &t, 0 ); io_timeout( client_socket, t ); stats_deliver( client_socket, mode ); - return -2; + return ws->reply_size = -2; } /* 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 -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 ); int format = 0; tai6464 t; - /* Touch variables */ - d=d;l=l; - #ifdef WANT_COMPRESSION_GZIP - d[l-1] = 0; - if( strstr( d, "gzip" ) ) { + ws->request[ws->request_size-1] = 0; + if( strstr( ws->request, "gzip" ) ) { h->flag |= STRUCT_HTTP_FLAG_GZIP; format = TASK_FLAG_GZIP; 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 ); #ifdef _DEBUG_HTTPERROR -write( 2, debug_request, l ); +write( 2, ws->debugbuf, ws->debugbuf_size ); #endif /* 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 ); fullscrape_deliver( client_socket, TASK_FULLSCRAPE | format ); io_dontwantread( client_socket ); - return -2; + return ws->reply_size = -2; } #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; - char *c = data; - size_t l; /* This is to hack around stupid clients that send "scrape ?info_hash" */ - if( c[-1] != '?' ) { - while( ( *c != '?' ) && ( *c != '\n' ) ) ++c; - if( *c == '\n' ) HTTPERROR_400_PARAM; - ++c; + if( read_ptr[-1] != '?' ) { + while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr; + if( *read_ptr == '\n' ) HTTPERROR_400_PARAM; + ++read_ptr; } 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 -1: - if( numwant ) - goto UTORRENT1600_WORKAROUND; - 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; - } + default: HTTPERROR_400_PARAM; /* PARSE ERROR */ + case -3: scan_urlencoded_skipvalue( &read_ptr ); break; + case 1: /* matched "info_hash" */ /* ignore this, when we have less than 20 bytes */ - if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != (ssize_t)sizeof(ot_hash) ) { -#ifdef WANT_UTORRENT1600_WORKAROUND - if( data[20] != '?' ) -#endif + if( scan_urlencoded_query( &read_ptr, (char*)(multiscrape_buf + numwant++), SCAN_SEARCHPATH_VALUE ) != (ssize_t)sizeof(ot_hash) ) HTTPERROR_400_PARAM; - } - if( numwant < OT_MAXMULTISCRAPE_COUNT ) - memmove( multiscrape_buf + numwant++, data, sizeof(ot_hash) ); break; } } -UTORRENT1600_WORKAROUND: - /* No info_hash found? Inform user */ 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 */ - if( !( l = return_tcp_scrape_for_torrent( multiscrape_buf, numwant, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf ) ) ) HTTPERROR_500; - stats_issue_event( EVENT_SCRAPE, FLAG_TCP, l ); - return l; + if( !( ws->reply_size = return_tcp_scrape_for_torrent( multiscrape_buf, numwant, ws->reply ) ) ) HTTPERROR_500; + stats_issue_event( EVENT_SCRAPE, FLAG_TCP, ws->reply_size ); + return ws->reply_size; } -static ssize_t http_handle_announce( const int64 client_socket, char *data ) { - char *c = data; - int numwant, tmp, scanon; - ot_peer peer; - ot_hash *hash = NULL; +static ot_keywords keywords_announce[] = { { "port", 1 }, { "left", 2 }, { "event", 3 }, { "numwant", 4 }, { "compact", 5 }, { "info_hash", 6 }, +#ifdef WANT_IP_FROM_QUERY_STRING +{ "ip", 7 }, +#endif +#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); - ssize_t len; - + char *write_ptr; + ssize_t len; + /* This is to hack around stupid clients that send "announce ?info_hash" */ - if( c[-1] != '?' ) { - while( ( *c != '?' ) && ( *c != '\n' ) ) ++c; - if( *c == '\n' ) HTTPERROR_400_PARAM; - ++c; + if( read_ptr[-1] != '?' ) { + while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr; + if( *read_ptr == '\n' ) HTTPERROR_400_PARAM; + ++read_ptr; } 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; #ifdef _DEBUG_PEERID - g_this_peerid_data = NULL; + ws->peer_id = NULL; #endif 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 -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ - default: scan_urlencoded_skipvalue( &c ); break; -#ifdef WANT_IP_FROM_QUERY_STRING - case 2: - if(!byte_diff(data,2,"ip")) { - unsigned char ip[4]; - 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 ); + case -3: scan_urlencoded_skipvalue( &read_ptr ); break; + case 1: /* matched "port" */ + len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); + if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) || ( tmp > 0xffff ) ) HTTPERROR_400_PARAM; + port = htons( tmp ); OT_SETPORT( &peer, &port ); break; - case 5: - if( byte_diff( data, 5, "event" ) ) - scan_urlencoded_skipvalue( &c ); - else switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) { - case -1: - HTTPERROR_400_PARAM; - case 7: - if( !byte_diff( data, 7, "stopped" ) ) OT_PEERFLAG( &peer ) |= PEER_FLAG_STOPPED; - break; - case 9: - if( !byte_diff( data, 9, "completed" ) ) OT_PEERFLAG( &peer ) |= PEER_FLAG_COMPLETED; - default: /* Fall through intended */ - break; + case 2: /* matched "left" */ + if( ( len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; + if( scan_fixed_int( write_ptr, len, &tmp ) ) tmp = 0; + if( !tmp ) OT_PEERFLAG( &peer ) |= PEER_FLAG_SEEDING; + break; + case 3: /* matched "event" */ + switch( scan_find_keywords( keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) { + case -1: HTTPERROR_400_PARAM; + case 1: /* matched "completed" */ + OT_PEERFLAG( &peer ) |= PEER_FLAG_COMPLETED; + break; + case 2: /* matched "stopped" */ + OT_PEERFLAG( &peer ) |= PEER_FLAG_STOPPED; + break; + default: + break; } break; - case 7: - if(!byte_diff(data,7,"numwant")) { - len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); - if( ( len <= 0 ) || scan_fixed_int( data, len, &numwant ) ) HTTPERROR_400_PARAM; - if( numwant < 0 ) numwant = 50; - 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 ); + case 4: /* matched "numwant" */ + len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); + if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &numwant ) ) HTTPERROR_400_PARAM; + if( numwant < 0 ) numwant = 50; + if( numwant > 200 ) numwant = 200; break; - case 9: - if(byte_diff(data,9,"info_hash")) { - scan_urlencoded_skipvalue( &c ); - continue; - } + case 5: /* matched "compact" */ + len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); + if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) ) HTTPERROR_400_PARAM; + 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 */ - if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM; - hash = (ot_hash*)data; + if( scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM; + hash = (ot_hash*)write_ptr; + 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 */ 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 ) - 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 - 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); - return len; + stats_issue_event( EVENT_ANNOUNCE, FLAG_TCP, ws->reply_size); + return ws->reply_size; } -ssize_t http_handle_request( const int64 client_socket, char *data, size_t recv_length ) { - char *c, *recv_header=data; - ssize_t reply_size = 0, reply_off, len; +ssize_t http_handle_request( const int64 client_socket, struct ot_workstruct *ws ) { + ssize_t reply_off, len; + char *read_ptr = ws->request, *write_ptr; #ifdef _DEBUG_HTTPERROR - if( recv_length >= sizeof( debug_request ) ) - recv_length = sizeof( debug_request) - 1; - memmove( debug_request, recv_header, recv_length ); - debug_request[ recv_length ] = 0; + reply_off = ws->request_size; + if( ws->request_size >= (ssize_t)ws->debugbuf_size ) + reply_off = ws->debugbuf_size - 1; + memmove( ws->debugbuf, ws->request, reply_off ); + ws->debugbuf[ reply_off ] = 0; #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 */ - if( byte_diff( data, 5, "GET /") ) HTTPERROR_400; + if( memcmp( read_ptr, "GET /", 5) ) HTTPERROR_400; /* Skip leading '/' */ - for( c = data+4; *c == '/'; ++c); + for( read_ptr+=4; *read_ptr == '/'; ++read_ptr); /* Try to parse the request. In reality we abandoned requiring the url to be correct. This now only decodes url encoded characters, we check for announces and 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( g_redirecturl && ( len == -2 ) ) HTTPERROR_302; if( len <= 0 ) HTTPERROR_404; /* This is the hardcore match for announce*/ - if( ( *data == 'a' ) || ( *data == '?' ) ) - reply_size = http_handle_announce( client_socket, c ); + if( ( *write_ptr == 'a' ) || ( *write_ptr == '?' ) ) + http_handle_announce( client_socket, ws, read_ptr ); #ifdef WANT_FULLSCRAPE - else if( !byte_diff( data, 12, "scrape HTTP/" ) ) - reply_size = http_handle_fullscrape( client_socket, recv_header, recv_length ); + else if( !memcmp( write_ptr, "scrape HTTP/", 12 ) ) + http_handle_fullscrape( client_socket, ws ); #endif /* This is the hardcore match for scrape */ - else if( !byte_diff( data, 2, "sc" ) ) - reply_size = http_handle_scrape( client_socket, c ); + else if( !memcmp( write_ptr, "sc", 2 ) ) + http_handle_scrape( client_socket, ws, read_ptr ); /* All the rest is matched the standard way */ - else switch( len ) { - case 5: /* stats ? */ - if( byte_diff( data, 5, "stats") ) HTTPERROR_404; - reply_size = http_handle_stats( client_socket, c, recv_header, recv_length ); - break; - default: + else if( !memcmp( write_ptr, "stats", 5) ) + http_handle_stats( client_socket, ws, read_ptr ); + else HTTPERROR_404; - } /* 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( 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. 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 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 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 */ - static_outbuf[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n'; - - http_senddata( client_socket, static_outbuf + reply_off, reply_size ); - return reply_size; + ws->outbuf[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n'; + + http_senddata( client_socket, ws ); + return ws->reply_size; } const char *g_version_http_c = "$Source$: $Revision$\n"; -- cgit v1.2.3