summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorerdgeist <>2009-01-05 18:05:39 +0000
committererdgeist <>2009-01-05 18:05:39 +0000
commit779d6c235ff8fe5284fd10dc82a9b99e7fa38d06 (patch)
tree043369d2a98a45b902e5d0968e28d78c1771b143
parent8bdc0d73f6f0bcaf83b7fb3d39e79e8fa4e6050d (diff)
* 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
-rw-r--r--Makefile3
-rw-r--r--opentracker.c67
-rw-r--r--ot_http.c454
-rw-r--r--ot_http.h6
-rw-r--r--ot_livesync.c3
-rw-r--r--ot_udp.c29
-rw-r--r--ot_udp.h2
-rw-r--r--scan_urlencoded_query.c23
-rw-r--r--scan_urlencoded_query.h16
-rw-r--r--trackerlogic.h25
10 files changed, 310 insertions, 318 deletions
diff --git a/Makefile b/Makefile
index 000103e..ca4e71b 100644
--- a/Makefile
+++ b/Makefile
@@ -24,7 +24,6 @@ BINDIR?=$(PREFIX)/bin
24 24
25#FEATURES+=-DWANT_SYNC_LIVE 25#FEATURES+=-DWANT_SYNC_LIVE
26#FEATURES+=-DWANT_SYNC_SCRAPE 26#FEATURES+=-DWANT_SYNC_SCRAPE
27#FEATURES+=-DWANT_UTORRENT1600_WORKAROUND
28#FEATURES+=-DWANT_IP_FROM_QUERY_STRING 27#FEATURES+=-DWANT_IP_FROM_QUERY_STRING
29#FEATURES+=-DWANT_COMPRESSION_GZIP 28#FEATURES+=-DWANT_COMPRESSION_GZIP
30#FEATURES+=-DWANT_LOG_NETWORKS 29#FEATURES+=-DWANT_LOG_NETWORKS
@@ -37,7 +36,7 @@ FEATURES+=-DWANT_FULLSCRAPE
37OPTS_debug=-D_DEBUG -g -ggdb # -pg -fprofile-arcs -ftest-coverage 36OPTS_debug=-D_DEBUG -g -ggdb # -pg -fprofile-arcs -ftest-coverage
38OPTS_production=-Os 37OPTS_production=-Os
39 38
40CFLAGS+=-I$(LIBOWFAT_HEADERS) -Wall -pipe -Wextra #-pedantic -ansi 39CFLAGS+=-I$(LIBOWFAT_HEADERS) -Wall -pipe -Wextra #-ansi -pedantic
41LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread -lz 40LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread -lz
42 41
43BINARY =opentracker 42BINARY =opentracker
diff --git a/opentracker.c b/opentracker.c
index bce5be0..8a4126c 100644
--- a/opentracker.c
+++ b/opentracker.c
@@ -41,9 +41,6 @@ volatile int g_opentracker_running = 1;
41 41
42static char * g_serverdir = NULL; 42static char * g_serverdir = NULL;
43 43
44/* To always have space for error messages ;) */
45static char static_inbuf[8192];
46
47static void panic( const char *routine ) { 44static void panic( const char *routine ) {
48 fprintf( stderr, "%s: %s\n", routine, strerror(errno) ); 45 fprintf( stderr, "%s: %s\n", routine, strerror(errno) );
49 exit( 111 ); 46 exit( 111 );
@@ -107,37 +104,44 @@ static void handle_dead( const int64 socket ) {
107 io_close( socket ); 104 io_close( socket );
108} 105}
109 106
110static ssize_t handle_read( const int64 clientsocket ) { 107static ssize_t handle_read( const int64 clientsocket, struct ot_workstruct *ws ) {
111 struct http_data* h = io_getcookie( clientsocket ); 108 struct http_data* h = io_getcookie( clientsocket );
112 ssize_t l; 109 ssize_t l;
113 110
114 if( ( l = io_tryread( clientsocket, static_inbuf, sizeof static_inbuf ) ) <= 0 ) { 111 if( ( l = io_tryread( clientsocket, ws->inbuf, ws->inbuf_size ) ) <= 0 ) {
115 handle_dead( clientsocket ); 112 handle_dead( clientsocket );
116 return 0; 113 return 0;
117 } 114 }
118 115
119 /* If we get the whole request in one packet, handle it without copying */ 116 /* If we get the whole request in one packet, handle it without copying */
120 if( !array_start( &h->data.request ) ) { 117 if( !array_start( &h->data.request ) ) {
121 if( memchr( static_inbuf, '\n', l ) ) 118 if( memchr( ws->inbuf, '\n', l ) ) {
122 return http_handle_request( clientsocket, static_inbuf, l ); 119 ws->request = ws->inbuf;
120 ws->request_size = l;
121 return http_handle_request( clientsocket, ws );
122 }
123
124 /* ... else take a copy */
123 h->flag |= STRUCT_HTTP_FLAG_ARRAY_USED; 125 h->flag |= STRUCT_HTTP_FLAG_ARRAY_USED;
124 array_catb( &h->data.request, static_inbuf, l ); 126 array_catb( &h->data.request, ws->inbuf, l );
125 return 0; 127 return 0;
126 } 128 }
127 129
128 h->flag |= STRUCT_HTTP_FLAG_ARRAY_USED; 130 h->flag |= STRUCT_HTTP_FLAG_ARRAY_USED;
129 array_catb( &h->data.request, static_inbuf, l ); 131 array_catb( &h->data.request, ws->inbuf, l );
130 132
131 if( array_failed( &h->data.request ) ) 133 if( array_failed( &h->data.request ) )
132 return http_issue_error( clientsocket, CODE_HTTPERROR_500 ); 134 return http_issue_error( clientsocket, ws, CODE_HTTPERROR_500 );
133 135
134 if( array_bytes( &h->data.request ) > 8192 ) 136 if( array_bytes( &h->data.request ) > 8192 )
135 return http_issue_error( clientsocket, CODE_HTTPERROR_500 ); 137 return http_issue_error( clientsocket, ws, CODE_HTTPERROR_500 );
136 138
137 if( memchr( array_start( &h->data.request ), '\n', array_bytes( &h->data.request ) ) ) 139 if( !memchr( array_start( &h->data.request ), '\n', array_bytes( &h->data.request ) ) )
138 return http_handle_request( clientsocket, array_start( &h->data.request ), array_bytes( &h->data.request ) ); 140 return 0;
139 141
140 return 0; 142 ws->request = array_start( &h->data.request );
143 ws->request_size = array_bytes( &h->data.request );
144 return http_handle_request( clientsocket, ws );
141} 145}
142 146
143static void handle_write( const int64 clientsocket ) { 147static void handle_write( const int64 clientsocket ) {
@@ -183,9 +187,25 @@ static void handle_accept( const int64 serversocket ) {
183} 187}
184 188
185static void server_mainloop( ) { 189static void server_mainloop( ) {
186 time_t next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL; 190 struct ot_workstruct ws;
187 struct iovec *iovector; 191 time_t next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL;
188 int iovec_entries; 192 struct iovec *iovector;
193 int iovec_entries;
194
195 /* Initialize our "thread local storage" */
196 ws.inbuf = malloc( THREAD_INBUF_SIZE );
197 ws.outbuf = malloc( THREAD_OUTBUF_SIZE );
198#ifdef _DEBUG_HTTPERROR
199 ws.debugbuf= malloc( THREAD_INBUF_SIZE );
200#endif
201 if( !ws.inbuf || !ws.outbuf )
202 panic( "Initializing worker failed" );
203
204 ws.inbuf_size = THREAD_INBUF_SIZE;
205 ws.outbuf_size = THREAD_OUTBUF_SIZE;
206#ifdef _DEBUG_HTTPERROR
207 ws.debugbuf_size= THREAD_INBUF_SIZE;
208#endif
189 209
190 for( ; ; ) { 210 for( ; ; ) {
191 int64 i; 211 int64 i;
@@ -197,13 +217,13 @@ static void server_mainloop( ) {
197 if( (intptr_t)cookie == FLAG_TCP ) 217 if( (intptr_t)cookie == FLAG_TCP )
198 handle_accept( i ); 218 handle_accept( i );
199 else if( (intptr_t)cookie == FLAG_UDP ) 219 else if( (intptr_t)cookie == FLAG_UDP )
200 handle_udp4( i ); 220 handle_udp4( i, &ws );
201 else 221 else
202 handle_read( i ); 222 handle_read( i, &ws );
203 } 223 }
204 224
205 while( ( i = mutex_workqueue_popresult( &iovec_entries, &iovector ) ) != -1 ) 225 while( ( i = mutex_workqueue_popresult( &iovec_entries, &iovector ) ) != -1 )
206 http_sendiovecdata( i, iovec_entries, iovector ); 226 http_sendiovecdata( i, &ws, iovec_entries, iovector );
207 227
208 while( ( i = io_canwrite( ) ) != -1 ) 228 while( ( i = io_canwrite( ) ) != -1 )
209 handle_write( i ); 229 handle_write( i );
@@ -431,7 +451,12 @@ while( scanon ) {
431 break; 451 break;
432 case 'f': bound += parse_configfile( optarg ); break; 452 case 'f': bound += parse_configfile( optarg ); break;
433 case 'h': help( argv[0] ); exit( 0 ); 453 case 'h': help( argv[0] ); exit( 0 );
434 case 'v': stats_return_tracker_version( static_inbuf ); fputs( static_inbuf, stderr ); exit( 0 ); 454 case 'v': {
455 char buffer[8192];
456 stats_return_tracker_version( buffer );
457 fputs( buffer, stderr );
458 exit( 0 );
459 }
435 default: 460 default:
436 case '?': usage( argv[0] ); exit( 1 ); 461 case '?': usage( argv[0] ); exit( 1 );
437 } 462 }
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 @@
27#include "ot_accesslist.h" 27#include "ot_accesslist.h"
28 28
29#define OT_MAXMULTISCRAPE_COUNT 64 29#define OT_MAXMULTISCRAPE_COUNT 64
30static ot_hash multiscrape_buf[OT_MAXMULTISCRAPE_COUNT];
31extern char *g_redirecturl; 30extern char *g_redirecturl;
32 31
33enum { 32enum {
34 SUCCESS_HTTP_HEADER_LENGTH = 80, 33 SUCCESS_HTTP_HEADER_LENGTH = 80,
35 SUCCESS_HTTP_HEADER_LENGHT_CONTENT_ENCODING = 32, 34 SUCCESS_HTTP_HEADER_LENGTH_CONTENT_ENCODING = 32,
36 SUCCESS_HTTP_SIZE_OFF = 17 }; 35 SUCCESS_HTTP_SIZE_OFF = 17 };
37 36
38/* Our static output buffer */
39static char static_outbuf[8192];
40#ifdef _DEBUG_HTTPERROR
41static char debug_request[8192];
42#endif
43
44#ifdef _DEBUG_PEERID 37#ifdef _DEBUG_PEERID
45size_t g_this_peerid_len = 0; 38size_t g_this_peerid_len = 0;
46char *g_this_peerid_data = NULL; 39char *g_this_peerid_data = NULL;
47#endif 40#endif
48 41
49static void http_senddata( const int64 client_socket, char *buffer, size_t size ) { 42static void http_senddata( const int64 client_socket, struct ot_workstruct *ws ) {
50 struct http_data *h = io_getcookie( client_socket ); 43 struct http_data *h = io_getcookie( client_socket );
51 ssize_t written_size; 44 ssize_t written_size;
52 45
@@ -56,22 +49,22 @@ static void http_senddata( const int64 client_socket, char *buffer, size_t size
56 array_reset( &h->data.request ); 49 array_reset( &h->data.request );
57 } 50 }
58 51
59 written_size = write( client_socket, buffer, size ); 52 written_size = write( client_socket, ws->reply, ws->reply_size );
60 if( ( written_size < 0 ) || ( (size_t)written_size == size ) ) { 53 if( ( written_size < 0 ) || ( written_size == ws->reply_size ) ) {
61 free( h ); io_close( client_socket ); 54 free( h ); io_close( client_socket );
62 } else { 55 } else {
63 char * outbuf; 56 char * outbuf;
64 tai6464 t; 57 tai6464 t;
65 58
66 if( !h ) return; 59 if( !h ) return;
67 if( !( outbuf = malloc( size - written_size ) ) ) { 60 if( !( outbuf = malloc( ws->reply_size - written_size ) ) ) {
68 free(h); io_close( client_socket ); 61 free(h); io_close( client_socket );
69 return; 62 return;
70 } 63 }
71 64
72 iob_reset( &h->data.batch ); 65 iob_reset( &h->data.batch );
73 memmove( outbuf, buffer + written_size, size - written_size ); 66 memmove( outbuf, ws->reply + written_size, ws->reply_size - written_size );
74 iob_addbuf_free( &h->data.batch, outbuf, size - written_size ); 67 iob_addbuf_free( &h->data.batch, outbuf, ws->reply_size - written_size );
75 h->flag |= STRUCT_HTTP_FLAG_IOB_USED; 68 h->flag |= STRUCT_HTTP_FLAG_IOB_USED;
76 69
77 /* writeable short data sockets just have a tcp timeout */ 70 /* 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
81 } 74 }
82} 75}
83 76
84#define HTTPERROR_302 return http_issue_error( client_socket, CODE_HTTPERROR_302 ) 77#define HTTPERROR_302 return http_issue_error( client_socket, ws, CODE_HTTPERROR_302 )
85#define HTTPERROR_400 return http_issue_error( client_socket, CODE_HTTPERROR_400 ) 78#define HTTPERROR_400 return http_issue_error( client_socket, ws, CODE_HTTPERROR_400 )
86#define HTTPERROR_400_PARAM return http_issue_error( client_socket, CODE_HTTPERROR_400_PARAM ) 79#define HTTPERROR_400_PARAM return http_issue_error( client_socket, ws, CODE_HTTPERROR_400_PARAM )
87#define HTTPERROR_400_COMPACT return http_issue_error( client_socket, CODE_HTTPERROR_400_COMPACT ) 80#define HTTPERROR_400_COMPACT return http_issue_error( client_socket, ws, CODE_HTTPERROR_400_COMPACT )
88#define HTTPERROR_403_IP return http_issue_error( client_socket, CODE_HTTPERROR_403_IP ) 81#define HTTPERROR_400_DOUBLEHASH return http_issue_error( client_socket, ws, CODE_HTTPERROR_400_PARAM )
89#define HTTPERROR_404 return http_issue_error( client_socket, CODE_HTTPERROR_404 ) 82#define HTTPERROR_403_IP return http_issue_error( client_socket, ws, CODE_HTTPERROR_403_IP )
90#define HTTPERROR_500 return http_issue_error( client_socket, CODE_HTTPERROR_500 ) 83#define HTTPERROR_404 return http_issue_error( client_socket, ws, CODE_HTTPERROR_404 )
91ssize_t http_issue_error( const int64 client_socket, int code ) { 84#define HTTPERROR_500 return http_issue_error( client_socket, ws, CODE_HTTPERROR_500 )
85ssize_t http_issue_error( const int64 client_socket, struct ot_workstruct *ws, int code ) {
92 char *error_code[] = { "302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", 86 char *error_code[] = { "302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request",
93 "403 Access Denied", "404 Not Found", "500 Internal Server Error" }; 87 "403 Access Denied", "404 Not Found", "500 Internal Server Error" };
94 char *title = error_code[code]; 88 char *title = error_code[code];
95 size_t reply_size;
96 89
90 ws->reply = ws->outbuf;
97 if( code == CODE_HTTPERROR_302 ) 91 if( code == CODE_HTTPERROR_302 )
98 reply_size = sprintf( static_outbuf, "HTTP/1.0 302 Found\r\nContent-Length: 0\r\nLocation: %s\r\n\r\n", g_redirecturl ); 92 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 );
99 else 93 else
100 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); 94 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);
101 95
102#ifdef _DEBUG_HTTPERROR 96#ifdef _DEBUG_HTTPERROR
103 fprintf( stderr, "DEBUG: invalid request was: %s\n", debug_request ); 97 fprintf( stderr, "DEBUG: invalid request was: %s\n", ws->debugbuf );
104#endif 98#endif
105 stats_issue_event( EVENT_FAILED, FLAG_TCP, code ); 99 stats_issue_event( EVENT_FAILED, FLAG_TCP, code );
106 http_senddata( client_socket, static_outbuf, reply_size); 100 http_senddata( client_socket, ws );
107 return -2; 101 return ws->reply_size = -2;
108} 102}
109 103
110ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct iovec *iovector ) { 104ssize_t http_sendiovecdata( const int64 client_socket, struct ot_workstruct *ws, int iovec_entries, struct iovec *iovector ) {
111 struct http_data *h = io_getcookie( client_socket ); 105 struct http_data *h = io_getcookie( client_socket );
112 char *header; 106 char *header;
113 int i; 107 int i;
@@ -136,7 +130,7 @@ ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct
136 } 130 }
137 131
138 /* Prepare space for http header */ 132 /* Prepare space for http header */
139 header = malloc( SUCCESS_HTTP_HEADER_LENGTH + SUCCESS_HTTP_HEADER_LENGHT_CONTENT_ENCODING ); 133 header = malloc( SUCCESS_HTTP_HEADER_LENGTH + SUCCESS_HTTP_HEADER_LENGTH_CONTENT_ENCODING );
140 if( !header ) { 134 if( !header ) {
141 iovec_free( &iovec_entries, &iovector ); 135 iovec_free( &iovec_entries, &iovector );
142 HTTPERROR_500; 136 HTTPERROR_500;
@@ -159,7 +153,7 @@ ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct
159 153
160 h->flag |= STRUCT_HTTP_FLAG_IOB_USED; 154 h->flag |= STRUCT_HTTP_FLAG_IOB_USED;
161 155
162 /* writeable sockets timeout after 10 minutes) */ 156 /* writeable sockets timeout after 10 minutes */
163 taia_now( &t ); taia_addsec( &t, &t, OT_CLIENT_TIMEOUT_SEND ); 157 taia_now( &t ); taia_addsec( &t, &t, OT_CLIENT_TIMEOUT_SEND );
164 io_timeout( client_socket, t ); 158 io_timeout( client_socket, t );
165 io_dontwantread( client_socket ); 159 io_dontwantread( client_socket );
@@ -167,9 +161,21 @@ ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct
167 return 0; 161 return 0;
168} 162}
169 163
170static ssize_t http_handle_stats( const int64 client_socket, char *data, char *d, size_t l ) { 164static ssize_t http_handle_stats( const int64 client_socket, struct ot_workstruct *ws, char *read_ptr ) {
171 char *c = data; 165static const ot_keywords keywords_main[] =
166 { { "mode", 1 }, {"format", 2 }, { NULL, -3 } };
167static const ot_keywords keywords_mode[] =
168 { { "peer", TASK_STATS_PEERS }, { "conn", TASK_STATS_CONNS }, { "scrp", TASK_STATS_SCRAPE }, { "udp4", TASK_STATS_UDP },
169 { "busy", TASK_STATS_BUSY_NETWORKS }, { "torr", TASK_STATS_TORRENTS }, { "fscr", TASK_STATS_FULLSCRAPE },
170 { "s24s", TASK_STATS_SLASH24S }, { "tpbs", TASK_STATS_TPB }, { "herr", TASK_STATS_HTTPERRORS },
171 { "top10", TASK_STATS_TOP10 }, { "renew", TASK_STATS_RENEW }, { "syncs", TASK_STATS_SYNCS }, { "version", TASK_STATS_VERSION },
172 { "startstop", TASK_STATS_STARTSTOP }, { "toraddrem", TASK_STATS_TORADDREM }, { NULL, -3 } };
173static const ot_keywords keywords_format[] =
174 { { "bin", TASK_FULLSCRAPE_TPB_BINARY }, { "ben", TASK_FULLSCRAPE }, { "url", TASK_FULLSCRAPE_TPB_URLENCODED },
175 { "txt", TASK_FULLSCRAPE_TPB_ASCII }, { NULL, -3 } };
176
172 int mode = TASK_STATS_PEERS, scanon = 1, format = 0; 177 int mode = TASK_STATS_PEERS, scanon = 1, format = 0;
178
173#ifdef WANT_RESTRICT_STATS 179#ifdef WANT_RESTRICT_STATS
174 struct http_data *h = io_getcookie( client_socket ); 180 struct http_data *h = io_getcookie( client_socket );
175 181
@@ -178,97 +184,26 @@ static ssize_t http_handle_stats( const int64 client_socket, char *data, char *d
178#endif 184#endif
179 185
180 while( scanon ) { 186 while( scanon ) {
181 switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) { 187 switch( scan_find_keywords( keywords_main, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) {
182 case -2: scanon = 0; break; /* TERMINATOR */ 188 case -2: scanon = 0; break; /* TERMINATOR */
183 case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ 189 case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */
184 default: scan_urlencoded_skipvalue( &c ); break; 190 case -3: scan_urlencoded_skipvalue( &read_ptr ); break;
185 case 4: 191 case 1: /* matched "mode" */
186 if( byte_diff(data,4,"mode")) { 192 if( ( mode = scan_find_keywords( keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM;
187 scan_urlencoded_skipvalue( &c );
188 continue;
189 }
190 switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) {
191 case 4:
192 if( !byte_diff(data,4,"peer"))
193 mode = TASK_STATS_PEERS;
194 else if( !byte_diff(data,4,"conn"))
195 mode = TASK_STATS_CONNS;
196 else if( !byte_diff(data,4,"scrp"))
197 mode = TASK_STATS_SCRAPE;
198 else if( !byte_diff(data,4,"tcp4"))
199 mode = TASK_STATS_TCP;
200 else if( !byte_diff(data,4,"udp4"))
201 mode = TASK_STATS_UDP;
202 else if( !byte_diff(data,4,"busy"))
203 mode = TASK_STATS_BUSY_NETWORKS;
204 else if( !byte_diff(data,4,"torr"))
205 mode = TASK_STATS_TORRENTS;
206 else if( !byte_diff(data,4,"fscr"))
207 mode = TASK_STATS_FULLSCRAPE;
208 else if( !byte_diff(data,4,"s24s"))
209 mode = TASK_STATS_SLASH24S;
210 else if( !byte_diff(data,4,"tpbs"))
211 mode = TASK_STATS_TPB;
212 else if( !byte_diff(data,4,"herr"))
213 mode = TASK_STATS_HTTPERRORS;
214 else
215 HTTPERROR_400_PARAM;
216 break;
217 case 5:
218 if( !byte_diff(data,5,"top10"))
219 mode = TASK_STATS_TOP10;
220 else if( !byte_diff(data,5,"renew"))
221 mode = TASK_STATS_RENEW;
222 else if( !byte_diff(data,5,"syncs"))
223 mode = TASK_STATS_SYNCS;
224 else
225 HTTPERROR_400_PARAM;
226 break;
227 case 7:
228 if( !byte_diff(data,7,"version"))
229 mode = TASK_STATS_VERSION;
230 else
231 HTTPERROR_400_PARAM;
232 break;
233 case 9:
234 if( !byte_diff(data,9,"startstop"))
235 mode = TASK_STATS_STARTSTOP;
236 else if( !byte_diff(data,9,"toraddrem"))
237 mode = TASK_STATS_TORADDREM;
238 else
239 HTTPERROR_400_PARAM;
240 break;
241 }
242 break; 193 break;
243 case 6: 194 case 2: /* matched "format" */
244 if( byte_diff(data,6,"format")) { 195 if( ( format = scan_find_keywords( keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM;
245 scan_urlencoded_skipvalue( &c );
246 continue;
247 }
248 if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 3 ) HTTPERROR_400_PARAM;
249 if( !byte_diff(data,3,"bin"))
250 format = TASK_FULLSCRAPE_TPB_BINARY;
251 else if( !byte_diff(data,3,"ben"))
252 format = TASK_FULLSCRAPE;
253 else if( !byte_diff(data,3,"url"))
254 format = TASK_FULLSCRAPE_TPB_URLENCODED;
255 else if( !byte_diff(data,3,"txt"))
256 format = TASK_FULLSCRAPE_TPB_ASCII;
257 else
258 HTTPERROR_400_PARAM;
259 break; 196 break;
260 } 197 }
261 } 198 }
262 199
263 /* Touch variable */
264 d=d;
265#ifdef WANT_FULLSCRAPE 200#ifdef WANT_FULLSCRAPE
266 if( mode == TASK_STATS_TPB ) { 201 if( mode == TASK_STATS_TPB ) {
267 struct http_data* h = io_getcookie( client_socket ); 202 struct http_data* h = io_getcookie( client_socket );
268 tai6464 t; 203 tai6464 t;
269#ifdef WANT_COMPRESSION_GZIP 204#ifdef WANT_COMPRESSION_GZIP
270 d[l-1] = 0; 205 ws->request[ws->request_size] = 0;
271 if( strstr( d, "gzip" ) ) { 206 if( strstr( read_ptr - 1, "gzip" ) ) {
272 h->flag |= STRUCT_HTTP_FLAG_GZIP; 207 h->flag |= STRUCT_HTTP_FLAG_GZIP;
273 format |= TASK_FLAG_GZIP; 208 format |= TASK_FLAG_GZIP;
274 } 209 }
@@ -280,7 +215,7 @@ static ssize_t http_handle_stats( const int64 client_socket, char *data, char *d
280 taia_uint( &t, 0 ); io_timeout( client_socket, t ); 215 taia_uint( &t, 0 ); io_timeout( client_socket, t );
281 fullscrape_deliver( client_socket, format ); 216 fullscrape_deliver( client_socket, format );
282 io_dontwantread( client_socket ); 217 io_dontwantread( client_socket );
283 return -2; 218 return ws->reply_size = -2;
284 } 219 }
285#endif 220#endif
286 221
@@ -290,27 +225,24 @@ static ssize_t http_handle_stats( const int64 client_socket, char *data, char *d
290 /* Complex stats also include expensive memory debugging tools */ 225 /* Complex stats also include expensive memory debugging tools */
291 taia_uint( &t, 0 ); io_timeout( client_socket, t ); 226 taia_uint( &t, 0 ); io_timeout( client_socket, t );
292 stats_deliver( client_socket, mode ); 227 stats_deliver( client_socket, mode );
293 return -2; 228 return ws->reply_size = -2;
294 } 229 }
295 230
296 /* Simple stats can be answerred immediately */ 231 /* Simple stats can be answerred immediately */
297 if( !( l = return_stats_for_tracker( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH, mode, 0 ) ) ) HTTPERROR_500; 232 if( !( ws->reply_size = return_stats_for_tracker( ws->reply, mode, 0 ) ) ) HTTPERROR_500;
298 233
299 return l; 234 return ws->reply_size;
300} 235}
301 236
302#ifdef WANT_FULLSCRAPE 237#ifdef WANT_FULLSCRAPE
303static ssize_t http_handle_fullscrape( const int64 client_socket, char *d, size_t l ) { 238static ssize_t http_handle_fullscrape( const int64 client_socket, struct ot_workstruct *ws ) {
304 struct http_data* h = io_getcookie( client_socket ); 239 struct http_data* h = io_getcookie( client_socket );
305 int format = 0; 240 int format = 0;
306 tai6464 t; 241 tai6464 t;
307 242
308 /* Touch variables */
309 d=d;l=l;
310
311#ifdef WANT_COMPRESSION_GZIP 243#ifdef WANT_COMPRESSION_GZIP
312 d[l-1] = 0; 244 ws->request[ws->request_size-1] = 0;
313 if( strstr( d, "gzip" ) ) { 245 if( strstr( ws->request, "gzip" ) ) {
314 h->flag |= STRUCT_HTTP_FLAG_GZIP; 246 h->flag |= STRUCT_HTTP_FLAG_GZIP;
315 format = TASK_FLAG_GZIP; 247 format = TASK_FLAG_GZIP;
316 stats_issue_event( EVENT_FULLSCRAPE_REQUEST_GZIP, *(int*)h->ip, 0 ); 248 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_
319 stats_issue_event( EVENT_FULLSCRAPE_REQUEST, *(int*)h->ip, 0 ); 251 stats_issue_event( EVENT_FULLSCRAPE_REQUEST, *(int*)h->ip, 0 );
320 252
321#ifdef _DEBUG_HTTPERROR 253#ifdef _DEBUG_HTTPERROR
322write( 2, debug_request, l ); 254write( 2, ws->debugbuf, ws->debugbuf_size );
323#endif 255#endif
324 256
325 /* Pass this task to the worker thread */ 257 /* Pass this task to the worker thread */
@@ -328,72 +260,70 @@ write( 2, debug_request, l );
328 taia_uint( &t, 0 ); io_timeout( client_socket, t ); 260 taia_uint( &t, 0 ); io_timeout( client_socket, t );
329 fullscrape_deliver( client_socket, TASK_FULLSCRAPE | format ); 261 fullscrape_deliver( client_socket, TASK_FULLSCRAPE | format );
330 io_dontwantread( client_socket ); 262 io_dontwantread( client_socket );
331 return -2; 263 return ws->reply_size = -2;
332} 264}
333#endif 265#endif
266static ssize_t http_handle_scrape( const int64 client_socket, struct ot_workstruct *ws, char *read_ptr ) {
267 static const ot_keywords keywords_scrape[] = { { "info_hash", 1 }, { NULL, -3 } };
334 268
335static ssize_t http_handle_scrape( const int64 client_socket, char *data ) { 269 ot_hash * multiscrape_buf = (ot_hash*)ws->request;
336 int scanon = 1, numwant = 0; 270 int scanon = 1, numwant = 0;
337 char *c = data;
338 size_t l;
339 271
340 /* This is to hack around stupid clients that send "scrape ?info_hash" */ 272 /* This is to hack around stupid clients that send "scrape ?info_hash" */
341 if( c[-1] != '?' ) { 273 if( read_ptr[-1] != '?' ) {
342 while( ( *c != '?' ) && ( *c != '\n' ) ) ++c; 274 while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr;
343 if( *c == '\n' ) HTTPERROR_400_PARAM; 275 if( *read_ptr == '\n' ) HTTPERROR_400_PARAM;
344 ++c; 276 ++read_ptr;
345 } 277 }
346 278
347 while( scanon ) { 279 while( scanon ) {
348 switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) { 280 switch( scan_find_keywords( keywords_scrape, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) {
349 case -2: scanon = 0; break; /* TERMINATOR */ 281 case -2: scanon = 0; break; /* TERMINATOR */
350 case -1: 282 default: HTTPERROR_400_PARAM; /* PARSE ERROR */
351 if( numwant ) 283 case -3: scan_urlencoded_skipvalue( &read_ptr ); break;
352 goto UTORRENT1600_WORKAROUND; 284 case 1: /* matched "info_hash" */
353 HTTPERROR_400_PARAM; /* PARSE ERROR */
354 default: scan_urlencoded_skipvalue( &c ); break;
355 case 9:
356 if(byte_diff(data,9,"info_hash")) {
357 scan_urlencoded_skipvalue( &c );
358 continue;
359 }
360 /* ignore this, when we have less than 20 bytes */ 285 /* ignore this, when we have less than 20 bytes */
361 if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != (ssize_t)sizeof(ot_hash) ) { 286 if( scan_urlencoded_query( &read_ptr, (char*)(multiscrape_buf + numwant++), SCAN_SEARCHPATH_VALUE ) != (ssize_t)sizeof(ot_hash) )
362#ifdef WANT_UTORRENT1600_WORKAROUND
363 if( data[20] != '?' )
364#endif
365 HTTPERROR_400_PARAM; 287 HTTPERROR_400_PARAM;
366 }
367 if( numwant < OT_MAXMULTISCRAPE_COUNT )
368 memmove( multiscrape_buf + numwant++, data, sizeof(ot_hash) );
369 break; 288 break;
370 } 289 }
371 } 290 }
372 291
373UTORRENT1600_WORKAROUND:
374
375 /* No info_hash found? Inform user */ 292 /* No info_hash found? Inform user */
376 if( !numwant ) HTTPERROR_400_PARAM; 293 if( !numwant ) HTTPERROR_400_PARAM;
294
295 /* Limit number of hashes to process */
296 if( numwant > OT_MAXMULTISCRAPE_COUNT )
297 numwant = OT_MAXMULTISCRAPE_COUNT;
377 298
378 /* Enough for http header + whole scrape string */ 299 /* Enough for http header + whole scrape string */
379 if( !( l = return_tcp_scrape_for_torrent( multiscrape_buf, numwant, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf ) ) ) HTTPERROR_500; 300 if( !( ws->reply_size = return_tcp_scrape_for_torrent( multiscrape_buf, numwant, ws->reply ) ) ) HTTPERROR_500;
380 stats_issue_event( EVENT_SCRAPE, FLAG_TCP, l ); 301 stats_issue_event( EVENT_SCRAPE, FLAG_TCP, ws->reply_size );
381 return l; 302 return ws->reply_size;
382} 303}
383 304
384static ssize_t http_handle_announce( const int64 client_socket, char *data ) { 305static ot_keywords keywords_announce[] = { { "port", 1 }, { "left", 2 }, { "event", 3 }, { "numwant", 4 }, { "compact", 5 }, { "info_hash", 6 },
385 char *c = data; 306#ifdef WANT_IP_FROM_QUERY_STRING
386 int numwant, tmp, scanon; 307{ "ip", 7 },
387 ot_peer peer; 308#endif
388 ot_hash *hash = NULL; 309#ifdef _DEBUG_PEERID
310{ "peer_id", 8 },
311#endif
312{ NULL, -3 } };
313static ot_keywords keywords_announce_event[] = { { "completed", 1 }, { "stopped", 2 }, { NULL, -3 } };
314static ssize_t http_handle_announce( const int64 client_socket, struct ot_workstruct *ws, char *read_ptr ) {
315 int numwant, tmp, scanon;
316 ot_peer peer;
317 ot_hash *hash = NULL;
389 unsigned short port = htons(6881); 318 unsigned short port = htons(6881);
390 ssize_t len; 319 char *write_ptr;
391 320 ssize_t len;
321
392 /* This is to hack around stupid clients that send "announce ?info_hash" */ 322 /* This is to hack around stupid clients that send "announce ?info_hash" */
393 if( c[-1] != '?' ) { 323 if( read_ptr[-1] != '?' ) {
394 while( ( *c != '?' ) && ( *c != '\n' ) ) ++c; 324 while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr;
395 if( *c == '\n' ) HTTPERROR_400_PARAM; 325 if( *read_ptr == '\n' ) HTTPERROR_400_PARAM;
396 ++c; 326 ++read_ptr;
397 } 327 }
398 328
399 OT_SETIP( &peer, ((struct http_data*)io_getcookie( client_socket ) )->ip ); 329 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 ) {
403 scanon = 1; 333 scanon = 1;
404 334
405#ifdef _DEBUG_PEERID 335#ifdef _DEBUG_PEERID
406 g_this_peerid_data = NULL; 336 ws->peer_id = NULL;
407#endif 337#endif
408 338
409 while( scanon ) { 339 while( scanon ) {
410 switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) { 340 switch( scan_find_keywords(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) {
411 case -2: scanon = 0; break; /* TERMINATOR */ 341 case -2: scanon = 0; break; /* TERMINATOR */
412 case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ 342 case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */
413 default: scan_urlencoded_skipvalue( &c ); break; 343 case -3: scan_urlencoded_skipvalue( &read_ptr ); break;
414#ifdef WANT_IP_FROM_QUERY_STRING 344 case 1: /* matched "port" */
415 case 2: 345 len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE );
416 if(!byte_diff(data,2,"ip")) { 346 if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) || ( tmp > 0xffff ) ) HTTPERROR_400_PARAM;
417 unsigned char ip[4]; 347 port = htons( tmp ); OT_SETPORT( &peer, &port );
418 len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
419 if( ( len <= 0 ) || scan_fixed_ip( data, len, ip ) ) HTTPERROR_400_PARAM;
420 OT_SETIP( &peer, ip );
421 } else
422 scan_urlencoded_skipvalue( &c );
423 break;
424#endif
425 case 4:
426 if( !byte_diff( data, 4, "port" ) ) {
427 len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
428 if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) || ( tmp > 0xffff ) ) HTTPERROR_400_PARAM;
429 port = htons( tmp ); OT_SETPORT( &peer, &port );
430 } else if( !byte_diff( data, 4, "left" ) ) {
431 if( ( len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM;
432 if( scan_fixed_int( data, len, &tmp ) ) tmp = 0;
433 if( !tmp ) OT_PEERFLAG( &peer ) |= PEER_FLAG_SEEDING;
434 } else
435 scan_urlencoded_skipvalue( &c );
436 break; 348 break;
437 case 5: 349 case 2: /* matched "left" */
438 if( byte_diff( data, 5, "event" ) ) 350 if( ( len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM;
439 scan_urlencoded_skipvalue( &c ); 351 if( scan_fixed_int( write_ptr, len, &tmp ) ) tmp = 0;
440 else switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) { 352 if( !tmp ) OT_PEERFLAG( &peer ) |= PEER_FLAG_SEEDING;
441 case -1: 353 break;
442 HTTPERROR_400_PARAM; 354 case 3: /* matched "event" */
443 case 7: 355 switch( scan_find_keywords( keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) {
444 if( !byte_diff( data, 7, "stopped" ) ) OT_PEERFLAG( &peer ) |= PEER_FLAG_STOPPED; 356 case -1: HTTPERROR_400_PARAM;
445 break; 357 case 1: /* matched "completed" */
446 case 9: 358 OT_PEERFLAG( &peer ) |= PEER_FLAG_COMPLETED;
447 if( !byte_diff( data, 9, "completed" ) ) OT_PEERFLAG( &peer ) |= PEER_FLAG_COMPLETED; 359 break;
448 default: /* Fall through intended */ 360 case 2: /* matched "stopped" */
449 break; 361 OT_PEERFLAG( &peer ) |= PEER_FLAG_STOPPED;
362 break;
363 default:
364 break;
450 } 365 }
451 break; 366 break;
452 case 7: 367 case 4: /* matched "numwant" */
453 if(!byte_diff(data,7,"numwant")) { 368 len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE );
454 len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); 369 if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &numwant ) ) HTTPERROR_400_PARAM;
455 if( ( len <= 0 ) || scan_fixed_int( data, len, &numwant ) ) HTTPERROR_400_PARAM; 370 if( numwant < 0 ) numwant = 50;
456 if( numwant < 0 ) numwant = 50; 371 if( numwant > 200 ) numwant = 200;
457 if( numwant > 200 ) numwant = 200;
458 } else if(!byte_diff(data,7,"compact")) {
459 len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
460 if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) ) HTTPERROR_400_PARAM;
461 if( !tmp ) HTTPERROR_400_COMPACT;
462 } else
463#ifdef _DEBUG_PEERID
464 if(!byte_diff(data,7,"peer_id")) {
465 g_this_peerid_len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
466 g_this_peerid_data = g_this_peerid_len > 0 ? data : 0;
467 } else
468#endif
469 scan_urlencoded_skipvalue( &c );
470 break; 372 break;
471 case 9: 373 case 5: /* matched "compact" */
472 if(byte_diff(data,9,"info_hash")) { 374 len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE );
473 scan_urlencoded_skipvalue( &c ); 375 if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) ) HTTPERROR_400_PARAM;
474 continue; 376 if( !tmp ) HTTPERROR_400_COMPACT;
475 } 377 break;
378 case 6: /* matched "info_hash" */
379 if( hash ) HTTPERROR_400_DOUBLEHASH;
476 /* ignore this, when we have less than 20 bytes */ 380 /* ignore this, when we have less than 20 bytes */
477 if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM; 381 if( scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM;
478 hash = (ot_hash*)data; 382 hash = (ot_hash*)write_ptr;
383 break;
384#ifdef WANT_IP_FROM_QUERY_STRING
385 case 7: /* matched "ip" */
386 len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE );
387 if( ( len <= 0 ) || scan_fixed_ip( write_ptr, len, (unsigned char*)/*tmp*/ws->reply ) ) HTTPERROR_400_PARAM;
388 OT_SETIP( &peer, /*tmp*/ws->reply );
479 break; 389 break;
390#endif
391#ifdef _DEBUG_PEERID
392 case 8: /* matched "peer_id" */
393 ws->peer_id_size = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE );
394 ws->peer_id = ws->peer_id_size > 0 ? write_ptr : 0;
395 break;
396#endif
480 } 397 }
481 } 398 }
482 399
483 /* Scanned whole query string */ 400 /* Scanned whole query string */
484 if( !hash ) 401 if( !hash )
485 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" ); 402 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" );
486 403
487 if( OT_PEERFLAG( &peer ) & PEER_FLAG_STOPPED ) 404 if( OT_PEERFLAG( &peer ) & PEER_FLAG_STOPPED )
488 len = remove_peer_from_torrent( hash, &peer, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf, FLAG_TCP ); 405 ws->reply_size = remove_peer_from_torrent( hash, &peer, ws->reply, FLAG_TCP );
489 else 406 else
490 len = add_peer_to_torrent_and_return_peers(hash, &peer, FLAG_TCP, numwant, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf); 407 ws->reply_size = add_peer_to_torrent_and_return_peers(hash, &peer, FLAG_TCP, numwant, ws->reply );
491 408
492 if( !len ) HTTPERROR_500; 409 if( !ws->reply_size ) HTTPERROR_500;
493 410
494 stats_issue_event( EVENT_ANNOUNCE, FLAG_TCP, len); 411 stats_issue_event( EVENT_ANNOUNCE, FLAG_TCP, ws->reply_size);
495 return len; 412 return ws->reply_size;
496} 413}
497 414
498ssize_t http_handle_request( const int64 client_socket, char *data, size_t recv_length ) { 415ssize_t http_handle_request( const int64 client_socket, struct ot_workstruct *ws ) {
499 char *c, *recv_header=data; 416 ssize_t reply_off, len;
500 ssize_t reply_size = 0, reply_off, len; 417 char *read_ptr = ws->request, *write_ptr;
501 418
502#ifdef _DEBUG_HTTPERROR 419#ifdef _DEBUG_HTTPERROR
503 if( recv_length >= sizeof( debug_request ) ) 420 reply_off = ws->request_size;
504 recv_length = sizeof( debug_request) - 1; 421 if( ws->request_size >= (ssize_t)ws->debugbuf_size )
505 memmove( debug_request, recv_header, recv_length ); 422 reply_off = ws->debugbuf_size - 1;
506 debug_request[ recv_length ] = 0; 423 memmove( ws->debugbuf, ws->request, reply_off );
424 ws->debugbuf[ reply_off ] = 0;
507#endif 425#endif
508 426
427 /* Tell subroutines where to put reply data */
428 ws->reply = ws->outbuf + SUCCESS_HTTP_HEADER_LENGTH;
429
509 /* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */ 430 /* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */
510 if( byte_diff( data, 5, "GET /") ) HTTPERROR_400; 431 if( memcmp( read_ptr, "GET /", 5) ) HTTPERROR_400;
511 432
512 /* Skip leading '/' */ 433 /* Skip leading '/' */
513 for( c = data+4; *c == '/'; ++c); 434 for( read_ptr+=4; *read_ptr == '/'; ++read_ptr);
514 435
515 /* Try to parse the request. 436 /* Try to parse the request.
516 In reality we abandoned requiring the url to be correct. This now 437 In reality we abandoned requiring the url to be correct. This now
517 only decodes url encoded characters, we check for announces and 438 only decodes url encoded characters, we check for announces and
518 scrapes by looking for "a*" or "sc" */ 439 scrapes by looking for "a*" or "sc" */
519 len = scan_urlencoded_query( &c, data = c, SCAN_PATH ); 440 len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_PATH );
520 441
521 /* If parsing returned an error, leave with not found */ 442 /* If parsing returned an error, leave with not found */
522 if( g_redirecturl && ( len == -2 ) ) HTTPERROR_302; 443 if( g_redirecturl && ( len == -2 ) ) HTTPERROR_302;
523 if( len <= 0 ) HTTPERROR_404; 444 if( len <= 0 ) HTTPERROR_404;
524 445
525 /* This is the hardcore match for announce*/ 446 /* This is the hardcore match for announce*/
526 if( ( *data == 'a' ) || ( *data == '?' ) ) 447 if( ( *write_ptr == 'a' ) || ( *write_ptr == '?' ) )
527 reply_size = http_handle_announce( client_socket, c ); 448 http_handle_announce( client_socket, ws, read_ptr );
528#ifdef WANT_FULLSCRAPE 449#ifdef WANT_FULLSCRAPE
529 else if( !byte_diff( data, 12, "scrape HTTP/" ) ) 450 else if( !memcmp( write_ptr, "scrape HTTP/", 12 ) )
530 reply_size = http_handle_fullscrape( client_socket, recv_header, recv_length ); 451 http_handle_fullscrape( client_socket, ws );
531#endif 452#endif
532 /* This is the hardcore match for scrape */ 453 /* This is the hardcore match for scrape */
533 else if( !byte_diff( data, 2, "sc" ) ) 454 else if( !memcmp( write_ptr, "sc", 2 ) )
534 reply_size = http_handle_scrape( client_socket, c ); 455 http_handle_scrape( client_socket, ws, read_ptr );
535 /* All the rest is matched the standard way */ 456 /* All the rest is matched the standard way */
536 else switch( len ) { 457 else if( !memcmp( write_ptr, "stats", 5) )
537 case 5: /* stats ? */ 458 http_handle_stats( client_socket, ws, read_ptr );
538 if( byte_diff( data, 5, "stats") ) HTTPERROR_404; 459 else
539 reply_size = http_handle_stats( client_socket, c, recv_header, recv_length );
540 break;
541 default:
542 HTTPERROR_404; 460 HTTPERROR_404;
543 }
544 461
545 /* If routines handled sending themselves, just return */ 462 /* If routines handled sending themselves, just return */
546 if( reply_size == -2 ) return 0; 463 if( ws->reply_size == -2 ) return 0;
547 /* If routine failed, let http error take over */ 464 /* If routine failed, let http error take over */
548 if( reply_size == -1 ) HTTPERROR_500; 465 if( ws->reply_size == -1 ) HTTPERROR_500;
549 466
550 /* This one is rather ugly, so I take you step by step through it. 467 /* This one is rather ugly, so I take you step by step through it.
551 468
552 1. In order to avoid having two buffers, one for header and one for content, we allow all above functions from trackerlogic to 469 1. In order to avoid having two buffers, one for header and one for content, we allow all above functions from trackerlogic to
553 write to a fixed location, leaving SUCCESS_HTTP_HEADER_LENGTH bytes in our static buffer, which is enough for the static string 470 write to a fixed location, leaving SUCCESS_HTTP_HEADER_LENGTH bytes in our work buffer, which is enough for the static string
554 plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for its expansion and calculate 471 plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for its expansion and calculate
555 the space NOT needed to expand in reply_off 472 the space NOT needed to expand in reply_off
556 */ 473 */
557 reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf( static_outbuf, 0, "%zd", reply_size ); 474 reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf( ws->outbuf, 0, "%zd", ws->reply_size );
558 475 ws->reply = ws->outbuf + reply_off;
476
559 /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete 477 /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete
560 packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */ 478 packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */
561 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 ); 479 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 );
562 480
563 /* 3. Finally we join both blocks neatly */ 481 /* 3. Finally we join both blocks neatly */
564 static_outbuf[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n'; 482 ws->outbuf[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n';
565 483
566 http_senddata( client_socket, static_outbuf + reply_off, reply_size ); 484 http_senddata( client_socket, ws );
567 return reply_size; 485 return ws->reply_size;
568} 486}
569 487
570const char *g_version_http_c = "$Source$: $Revision$\n"; 488const char *g_version_http_c = "$Source$: $Revision$\n";
diff --git a/ot_http.h b/ot_http.h
index cd8c3cb..18e8156 100644
--- a/ot_http.h
+++ b/ot_http.h
@@ -23,8 +23,8 @@ struct http_data {
23 STRUCT_HTTP_FLAG flag; 23 STRUCT_HTTP_FLAG flag;
24}; 24};
25 25
26ssize_t http_handle_request( const int64 s, char *data, size_t l ); 26ssize_t http_handle_request( const int64 s, struct ot_workstruct *ws );
27ssize_t http_sendiovecdata( const int64 s, int iovec_entries, struct iovec *iovector ); 27ssize_t http_sendiovecdata( const int64 s, struct ot_workstruct *ws, int iovec_entries, struct iovec *iovector );
28ssize_t http_issue_error( const int64 s, int code ); 28ssize_t http_issue_error( const int64 s, struct ot_workstruct *ws, int code );
29 29
30#endif 30#endif
diff --git a/ot_livesync.c b/ot_livesync.c
index 47a371a..a47edba 100644
--- a/ot_livesync.c
+++ b/ot_livesync.c
@@ -400,9 +400,6 @@ static void * livesync_worker( void * args ) {
400 default: 400 default:
401 break; 401 break;
402 } 402 }
403
404 /* Handle outstanding requests */
405 livesync_ticker( );
406 } 403 }
407 404
408 /* Never returns. */ 405 /* Never returns. */
diff --git a/ot_udp.c b/ot_udp.c
index fb171e7..81c4d63 100644
--- a/ot_udp.c
+++ b/ot_udp.c
@@ -17,9 +17,6 @@
17#include "ot_udp.h" 17#include "ot_udp.h"
18#include "ot_stats.h" 18#include "ot_stats.h"
19 19
20static char static_inbuf[8192];
21static char static_outbuf[8192];
22
23static const uint8_t g_static_connid[8] = { 0x23, 0x42, 0x05, 0x17, 0xde, 0x41, 0x50, 0xff }; 20static const uint8_t g_static_connid[8] = { 0x23, 0x42, 0x05, 0x17, 0xde, 0x41, 0x50, 0xff };
24 21
25static void udp_make_connectionid( uint32_t * connid, const char * remoteip ) { 22static 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
39} 36}
40 37
41/* UDP implementation according to http://xbtt.sourceforge.net/udp_tracker_protocol.html */ 38/* UDP implementation according to http://xbtt.sourceforge.net/udp_tracker_protocol.html */
42void handle_udp4( int64 serversocket ) { 39void handle_udp4( int64 serversocket, struct ot_workstruct *ws ) {
43 ot_peer peer; 40 ot_peer peer;
44 ot_hash *hash = NULL; 41 ot_hash *hash = NULL;
45 char remoteip[4]; 42 char remoteip[4];
46 uint32_t *inpacket = (uint32_t*)static_inbuf; 43 uint32_t *inpacket = (uint32_t*)ws->inbuf;
47 uint32_t *outpacket = (uint32_t*)static_outbuf; 44 uint32_t *outpacket = (uint32_t*)ws->outbuf;
48 uint32_t numwant, left, event; 45 uint32_t numwant, left, event;
49 uint16_t port, remoteport; 46 uint16_t port, remoteport;
50 size_t r, r_out; 47 size_t r, r_out;
51 48
52 r = socket_recv4( serversocket, static_inbuf, sizeof( static_inbuf ), remoteip, &remoteport); 49 r = socket_recv4( serversocket, ws->inbuf, ws->inbuf_size, remoteip, &remoteport);
53 50
54 stats_issue_event( EVENT_ACCEPT, FLAG_UDP, ntohl(*(uint32_t*)remoteip) ); 51 stats_issue_event( EVENT_ACCEPT, FLAG_UDP, ntohl(*(uint32_t*)remoteip) );
55 stats_issue_event( EVENT_READ, FLAG_UDP, r ); 52 stats_issue_event( EVENT_READ, FLAG_UDP, r );
@@ -58,8 +55,6 @@ void handle_udp4( int64 serversocket ) {
58 if( r < 16 ) 55 if( r < 16 )
59 return; 56 return;
60 57
61/* fprintf( stderr, "UDP Connection id: %16llX\n", *(uint64_t*)inpacket ); */
62
63 switch( ntohl( inpacket[2] ) ) { 58 switch( ntohl( inpacket[2] ) ) {
64 case 0: /* This is a connect action */ 59 case 0: /* This is a connect action */
65 /* look for udp bittorrent magic id */ 60 /* look for udp bittorrent magic id */
@@ -70,7 +65,7 @@ void handle_udp4( int64 serversocket ) {
70 outpacket[1] = inpacket[3]; 65 outpacket[1] = inpacket[3];
71 udp_make_connectionid( outpacket + 2, remoteip ); 66 udp_make_connectionid( outpacket + 2, remoteip );
72 67
73 socket_send4( serversocket, static_outbuf, 16, remoteip, remoteport ); 68 socket_send4( serversocket, ws->outbuf, 16, remoteip, remoteport );
74 stats_issue_event( EVENT_CONNECT, FLAG_UDP, 16 ); 69 stats_issue_event( EVENT_CONNECT, FLAG_UDP, 16 );
75 break; 70 break;
76 case 1: /* This is an announce action */ 71 case 1: /* This is an announce action */
@@ -88,8 +83,8 @@ void handle_udp4( int64 serversocket ) {
88 if (numwant > 200) numwant = 200; 83 if (numwant > 200) numwant = 200;
89 84
90 event = ntohl( inpacket[80/4] ); 85 event = ntohl( inpacket[80/4] );
91 port = *(uint16_t*)( static_inbuf + 96 ); 86 port = *(uint16_t*)( ((char*)inpacket) + 96 );
92 hash = (ot_hash*)( static_inbuf + 16 ); 87 hash = (ot_hash*)( ((char*)inpacket) + 16 );
93 88
94 OT_SETIP( &peer, remoteip ); 89 OT_SETIP( &peer, remoteip );
95 OT_SETPORT( &peer, &port ); 90 OT_SETPORT( &peer, &port );
@@ -108,11 +103,11 @@ void handle_udp4( int64 serversocket ) {
108 outpacket[1] = inpacket[12/4]; 103 outpacket[1] = inpacket[12/4];
109 104
110 if( OT_PEERFLAG( &peer ) & PEER_FLAG_STOPPED ) /* Peer is gone. */ 105 if( OT_PEERFLAG( &peer ) & PEER_FLAG_STOPPED ) /* Peer is gone. */
111 r = remove_peer_from_torrent( hash, &peer, static_outbuf, FLAG_UDP ); 106 r = remove_peer_from_torrent( hash, &peer, ws->outbuf, FLAG_UDP );
112 else 107 else
113 r = 8 + add_peer_to_torrent_and_return_peers( hash, &peer, FLAG_UDP, numwant, static_outbuf + 8 ); 108 r = 8 + add_peer_to_torrent_and_return_peers( hash, &peer, FLAG_UDP, numwant, ((char*)outpacket) + 8 );
114 109
115 socket_send4( serversocket, static_outbuf, r, remoteip, remoteport ); 110 socket_send4( serversocket, ws->outbuf, r, remoteip, remoteport );
116 stats_issue_event( EVENT_ANNOUNCE, FLAG_UDP, r ); 111 stats_issue_event( EVENT_ANNOUNCE, FLAG_UDP, r );
117 break; 112 break;
118 113
@@ -124,9 +119,9 @@ void handle_udp4( int64 serversocket ) {
124 outpacket[1] = inpacket[12/4]; 119 outpacket[1] = inpacket[12/4];
125 120
126 for( r_out = 0; ( r_out * 20 < r - 16) && ( r_out <= 74 ); r_out++ ) 121 for( r_out = 0; ( r_out * 20 < r - 16) && ( r_out <= 74 ); r_out++ )
127 return_udp_scrape_for_torrent( (ot_hash*)( static_inbuf + 16 + 20 * r_out ), static_outbuf + 8 + 12 * r_out ); 122 return_udp_scrape_for_torrent( (ot_hash*)( ((char*)inpacket) + 16 + 20 * r_out ), ((char*)outpacket) + 8 + 12 * r_out );
128 123
129 socket_send4( serversocket, static_outbuf, 8 + 12 * r_out, remoteip, remoteport ); 124 socket_send4( serversocket, ws->outbuf, 8 + 12 * r_out, remoteip, remoteport );
130 stats_issue_event( EVENT_SCRAPE, FLAG_UDP, r ); 125 stats_issue_event( EVENT_SCRAPE, FLAG_UDP, r );
131 break; 126 break;
132 } 127 }
diff --git a/ot_udp.h b/ot_udp.h
index c5f3959..c146392 100644
--- a/ot_udp.h
+++ b/ot_udp.h
@@ -6,6 +6,6 @@
6#ifndef __OT_UDP_H__ 6#ifndef __OT_UDP_H__
7#define __OT_UDP_H__ 7#define __OT_UDP_H__
8 8
9void handle_udp4( int64 serversocket ); 9void handle_udp4( int64 serversocket, struct ot_workstruct *ws );
10 10
11#endif 11#endif
diff --git a/scan_urlencoded_query.c b/scan_urlencoded_query.c
index a17db2a..c3acefc 100644
--- a/scan_urlencoded_query.c
+++ b/scan_urlencoded_query.c
@@ -9,6 +9,9 @@
9/* Libwofat */ 9/* Libwofat */
10#include "scan.h" 10#include "scan.h"
11 11
12/* System */
13#include <string.h>
14
12/* Idea is to do a in place replacement or guarantee at least 15/* Idea is to do a in place replacement or guarantee at least
13 strlen( string ) bytes in deststring 16 strlen( string ) bytes in deststring
14 watch http://www.ietf.org/rfc/rfc2396.txt 17 watch http://www.ietf.org/rfc/rfc2396.txt
@@ -64,6 +67,22 @@ void scan_urlencoded_skipvalue( char **string ) {
64 *string = (char*)s; 67 *string = (char*)s;
65} 68}
66 69
70int scan_find_keywords( const ot_keywords * keywords, char **string, SCAN_SEARCHPATH_FLAG flags) {
71 char *deststring = *string;
72 ssize_t match_length = scan_urlencoded_query(string, deststring, flags );
73
74 if( match_length < 0 ) return match_length;
75 if( match_length == 0 ) return -3;
76
77 while( keywords->key ) {
78 if( !memcmp( keywords->key, deststring, match_length ) )
79 return keywords->value;
80 keywords++;
81 }
82
83 return -3;
84}
85
67ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags) { 86ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags) {
68 const unsigned char* s=*(const unsigned char**) string; 87 const unsigned char* s=*(const unsigned char**) string;
69 unsigned char *d = (unsigned char*)deststring; 88 unsigned char *d = (unsigned char*)deststring;
@@ -95,9 +114,7 @@ ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_F
95 --s; 114 --s;
96 break; 115 break;
97 case '?': 116 case '?':
98 /* XXX to help us parse path?param=value?param=value?... sent by µTorrent 1600 117 if( flags != SCAN_PATH ) return -1;
99 do not return an error but silently terminate
100 if( flags != SCAN_PATH ) return -1; */
101 break; 118 break;
102 case '=': 119 case '=':
103 if( flags != SCAN_SEARCHPATH_PARAM ) return -1; 120 if( flags != SCAN_SEARCHPATH_PARAM ) return -1;
diff --git a/scan_urlencoded_query.h b/scan_urlencoded_query.h
index 7ff6e42..a0b77af 100644
--- a/scan_urlencoded_query.h
+++ b/scan_urlencoded_query.h
@@ -8,6 +8,11 @@
8 8
9#include <sys/types.h> 9#include <sys/types.h>
10 10
11typedef struct {
12 char *key;
13 int value;
14} ot_keywords;
15
11typedef enum { 16typedef enum {
12 SCAN_PATH = 1, 17 SCAN_PATH = 1,
13 SCAN_SEARCHPATH_PARAM = 2, 18 SCAN_SEARCHPATH_PARAM = 2,
@@ -21,9 +26,20 @@ typedef enum {
21 flags determines, what to parse 26 flags determines, what to parse
22 returns number of valid converted characters in deststring 27 returns number of valid converted characters in deststring
23 or -1 for parse error 28 or -1 for parse error
29 or -2 for terminator found
24*/ 30*/
25ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags); 31ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags);
26 32
33/* string in: pointer to source
34 out: pointer to next scan position
35 flags determines, what to parse
36 returns value for matched keyword
37 or -1 for parse error
38 or -2 for terminator found
39 or -3 for no keyword matched
40 */
41int scan_find_keywords( const ot_keywords * keywords, char **string, SCAN_SEARCHPATH_FLAG flags);
42
27/* string in: pointer to value of a param=value pair to skip 43/* string in: pointer to value of a param=value pair to skip
28 out: pointer to next scan position on return 44 out: pointer to next scan position on return
29*/ 45*/
diff --git a/trackerlogic.h b/trackerlogic.h
index 34cee3b..eb2906b 100644
--- a/trackerlogic.h
+++ b/trackerlogic.h
@@ -96,6 +96,31 @@ struct ot_peerlist {
96}; 96};
97#define OT_PEERLIST_HASBUCKETS(peer_list) ((peer_list) && ((peer_list)->peers.size > (peer_list)->peers.space)) 97#define OT_PEERLIST_HASBUCKETS(peer_list) ((peer_list) && ((peer_list)->peers.size > (peer_list)->peers.space))
98 98
99struct ot_workstruct {
100 /* Thread specific, static */
101#define THREAD_INBUF_SIZE 8192
102 char *inbuf;
103 size_t inbuf_size;
104#define THREAD_OUTBUF_SIZE 8192
105 char *outbuf;
106 size_t outbuf_size;
107#ifdef _DEBUG_HTTPERROR
108#define THREAD_DEBUGBUF_SIZE 8192
109 char *debugbuf;
110 size_t debugbuf_size;
111#endif
112
113 /* HTTP specific, non static */
114 char *request;
115 ssize_t request_size;
116 char *reply;
117 ssize_t reply_size;
118#ifdef _DEBUG_PEERID
119 char *peer_id;
120 ssize_t peer_id_size;
121#endif
122};
123
99/* 124/*
100 Exported functions 125 Exported functions
101*/ 126*/