summaryrefslogtreecommitdiff
path: root/ot_http.c
diff options
context:
space:
mode:
Diffstat (limited to 'ot_http.c')
-rw-r--r--ot_http.c454
1 files changed, 186 insertions, 268 deletions
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";