diff options
author | erdgeist <> | 2007-01-29 02:02:03 +0000 |
---|---|---|
committer | erdgeist <> | 2007-01-29 02:02:03 +0000 |
commit | 5a3d6b402979d7d9bf8da470f081bb6ca4122cf6 (patch) | |
tree | 21033b84fbd2e7cb98bdd1a5259f0b6e6c56275a /opentracker.c | |
parent | 4c6bcc89cef49841e5eba095435c49cbdfbf2801 (diff) |
We now answer as soon as the first \n is seen
We do not copy input buffer to socket's input array anymore, if we find \n in first buffer
We simplified parsing of input string
We removed asserts that were needed to debug negative seeder counts
We split static input and output buffers for obvious reasons
We enlarged static output buffer to prepare multi scrape functionality
We removed -lm from Makefile, this lib was only needed, when our random algorithm worked on floats
We close fds now that come with an empty iocookie into write loop, those don't make sense
We documented query string parser in opentracker.c a lot better to help auditing
Diffstat (limited to 'opentracker.c')
-rw-r--r-- | opentracker.c | 122 |
1 files changed, 58 insertions, 64 deletions
diff --git a/opentracker.c b/opentracker.c index 00b73a3..54fb80f 100644 --- a/opentracker.c +++ b/opentracker.c | |||
@@ -33,7 +33,8 @@ static time_t ot_start_time; | |||
33 | static const size_t SUCCESS_HTTP_HEADER_LENGTH = 80; | 33 | static const size_t SUCCESS_HTTP_HEADER_LENGTH = 80; |
34 | static const size_t SUCCESS_HTTP_SIZE_OFF = 17; | 34 | static const size_t SUCCESS_HTTP_SIZE_OFF = 17; |
35 | /* To always have space for error messages ;) */ | 35 | /* To always have space for error messages ;) */ |
36 | static char static_scratch[8192]; | 36 | static char static_inbuf[8192]; |
37 | static char static_outbuf[8192*4]; | ||
37 | 38 | ||
38 | #ifdef _DEBUG_HTTPERROR | 39 | #ifdef _DEBUG_HTTPERROR |
39 | static char debug_request[8192]; | 40 | static char debug_request[8192]; |
@@ -51,12 +52,11 @@ struct http_data { | |||
51 | 52 | ||
52 | int main( int argc, char **argv ); | 53 | int main( int argc, char **argv ); |
53 | 54 | ||
54 | static int httpheader_complete( struct http_data *h ); | 55 | static void httperror( const int64 s, const char *title, const char *message ); |
55 | static void httperror( const int64 s, struct http_data *h, const char *title, const char *message ); | 56 | static void httpresponse( const int64 s, char *data ); |
56 | static void httpresponse( const int64 s, struct http_data *h); | ||
57 | 57 | ||
58 | static void sendmallocdata( const int64 s, struct http_data *h, char *buffer, const size_t size ); | 58 | static void sendmallocdata( const int64 s, char *buffer, const size_t size ); |
59 | static void senddata( const int64 s, struct http_data *h, char *buffer, const size_t size ); | 59 | static void senddata( const int64 s, char *buffer, const size_t size ); |
60 | 60 | ||
61 | static void server_mainloop( const int64 serversocket ); | 61 | static void server_mainloop( const int64 serversocket ); |
62 | static void handle_timeouted( void ); | 62 | static void handle_timeouted( void ); |
@@ -71,11 +71,11 @@ static void carp( const char *routine ); | |||
71 | static void panic( const char *routine ); | 71 | static void panic( const char *routine ); |
72 | static void graceful( int s ); | 72 | static void graceful( int s ); |
73 | 73 | ||
74 | #define HTTPERROR_400 return httperror( s, h, "400 Invalid Request", "This server only understands GET." ) | 74 | #define HTTPERROR_400 return httperror( s, "400 Invalid Request", "This server only understands GET." ) |
75 | #define HTTPERROR_400_PARAM return httperror( s, h, "400 Invalid Request", "Invalid parameter" ) | 75 | #define HTTPERROR_400_PARAM return httperror( s, "400 Invalid Request", "Invalid parameter" ) |
76 | #define HTTPERROR_400_COMPACT return httperror( s, h, "400 Invalid Request", "This server only delivers compact results." ) | 76 | #define HTTPERROR_400_COMPACT return httperror( s, "400 Invalid Request", "This server only delivers compact results." ) |
77 | #define HTTPERROR_404 return httperror( s, h, "404 Not Found", "No such file or directory." ) | 77 | #define HTTPERROR_404 return httperror( s, "404 Not Found", "No such file or directory." ) |
78 | #define HTTPERROR_500 return httperror( s, h, "500 Internal Server Error", "A server error has occured. Please retry later." ) | 78 | #define HTTPERROR_500 return httperror( s, "500 Internal Server Error", "A server error has occured. Please retry later." ) |
79 | 79 | ||
80 | /* End of prototypes */ | 80 | /* End of prototypes */ |
81 | 81 | ||
@@ -91,33 +91,23 @@ static void panic( const char *routine ) { | |||
91 | exit( 111 ); | 91 | exit( 111 ); |
92 | } | 92 | } |
93 | 93 | ||
94 | static int httpheader_complete( struct http_data *h ) { | 94 | static void httperror( const int64 s, const char *title, const char *message ) { |
95 | size_t l = array_bytes( &h->request ), i; | 95 | size_t 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", |
96 | const char* c = array_start( &h->request ); | ||
97 | |||
98 | for( i=0; i+1<l; ++i) { | ||
99 | if( c[i]=='\n' && c[i+1]=='\n') return i+2; | ||
100 | if( i+3<l && c[i]=='\r' && c[i+1]=='\n' && c[i+2]=='\r' && c[i+3]=='\n' ) return i+4; | ||
101 | } | ||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | static void httperror( const int64 s, struct http_data *h, const char *title, const char *message ) { | ||
106 | size_t reply_size = sprintf( static_scratch, "HTTP/1.0 %s\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: %zd\r\n\r\n<title>%s</title>\n", | ||
107 | title, strlen(message)+strlen(title)+16-4,title+4); | 96 | title, strlen(message)+strlen(title)+16-4,title+4); |
108 | #ifdef _DEBUG_HTTPERROR | 97 | #ifdef _DEBUG_HTTPERROR |
109 | fprintf( stderr, "DEBUG: invalid request was: %s\n", debug_request ); | 98 | fprintf( stderr, "DEBUG: invalid request was: %s\n", debug_request ); |
110 | #endif | 99 | #endif |
111 | senddata(s,h,static_scratch,reply_size); | 100 | senddata(s,static_outbuf,reply_size); |
112 | } | 101 | } |
113 | 102 | ||
114 | static void sendmallocdata( const int64 s, struct http_data *h, char *buffer, size_t size ) { | 103 | static void sendmallocdata( const int64 s, char *buffer, size_t size ) { |
115 | tai6464 t; | 104 | struct http_data *h = io_getcookie( s ); |
116 | char *header; | 105 | char *header; |
117 | size_t header_size; | 106 | size_t header_size; |
107 | tai6464 t; | ||
118 | 108 | ||
119 | if( !h ) | 109 | if( !h ) |
120 | return free( buffer); | 110 | return free( buffer ); |
121 | array_reset( &h->request ); | 111 | array_reset( &h->request ); |
122 | 112 | ||
123 | header = malloc( SUCCESS_HTTP_HEADER_LENGTH ); | 113 | header = malloc( SUCCESS_HTTP_HEADER_LENGTH ); |
@@ -133,12 +123,13 @@ static void sendmallocdata( const int64 s, struct http_data *h, char *buffer, si | |||
133 | iob_addbuf_free( &h->batch, buffer, size ); | 123 | iob_addbuf_free( &h->batch, buffer, size ); |
134 | 124 | ||
135 | /* writeable sockets just have a tcp timeout */ | 125 | /* writeable sockets just have a tcp timeout */ |
136 | taia_uint(&t,0); io_timeout( s, t ); | 126 | taia_uint( &t, 0 ); io_timeout( s, t ); |
137 | io_dontwantread( s ); | 127 | io_dontwantread( s ); |
138 | io_wantwrite( s ); | 128 | io_wantwrite( s ); |
139 | } | 129 | } |
140 | 130 | ||
141 | static void senddata( const int64 s, struct http_data *h, char *buffer, size_t size ) { | 131 | static void senddata( const int64 s, char *buffer, size_t size ) { |
132 | struct http_data *h = io_getcookie( s ); | ||
142 | size_t written_size; | 133 | size_t written_size; |
143 | 134 | ||
144 | /* whoever sends data is not interested in its input-array */ | 135 | /* whoever sends data is not interested in its input-array */ |
@@ -168,8 +159,8 @@ static void senddata( const int64 s, struct http_data *h, char *buffer, size_t s | |||
168 | } | 159 | } |
169 | } | 160 | } |
170 | 161 | ||
171 | static void httpresponse( const int64 s, struct http_data *h) { | 162 | static void httpresponse( const int64 s, char *data ) { |
172 | char *c, *data, *reply; | 163 | char *c, *reply; |
173 | ot_peer peer; | 164 | ot_peer peer; |
174 | ot_torrent *torrent; | 165 | ot_torrent *torrent; |
175 | ot_hash *hash = NULL; | 166 | ot_hash *hash = NULL; |
@@ -178,22 +169,19 @@ static void httpresponse( const int64 s, struct http_data *h) { | |||
178 | time_t t; | 169 | time_t t; |
179 | size_t reply_size = 0, reply_off; | 170 | size_t reply_size = 0, reply_off; |
180 | 171 | ||
181 | array_cat0( &h->request ); | ||
182 | c = array_start( &h->request ); | ||
183 | |||
184 | #ifdef _DEBUG_HTTPERROR | 172 | #ifdef _DEBUG_HTTPERROR |
185 | memcpy( debug_request, array_start( &h->request ), array_bytes( &h->request ) ); | 173 | memcpy( debug_request, data, sizeof( debug_request ) ); |
186 | #endif | 174 | #endif |
187 | 175 | ||
188 | if( byte_diff( c, 4, "GET ") ) HTTPERROR_400; | 176 | /* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */ |
177 | if( byte_diff( data, 5, "GET /") ) HTTPERROR_400; | ||
189 | 178 | ||
190 | c+=4; | 179 | /* Query string MUST terminate with SP -- we know that theres at least a '\n' where this search terminates */ |
191 | for( data = c; *data!=' ' && *data != '\t' && *data != '\n' && *data != '\r'; ++data ) ; | 180 | for( c = data + 5; *c!=' ' && *c != '\t' && *c != '\n' && *c != '\r'; ++c ) ; |
181 | if( *c != ' ' ) HTTPERROR_400; | ||
192 | 182 | ||
193 | if( *data != ' ' ) HTTPERROR_400; | 183 | /* Skip leading '/' */ |
194 | *data = 0; | 184 | for( c = data+4; *c == '/'; ++c); |
195 | if( c[0] != '/' ) HTTPERROR_404; | ||
196 | while( *c == '/' ) ++c; | ||
197 | 185 | ||
198 | switch( scan_urlencoded_query( &c, data = c, SCAN_PATH ) ) { | 186 | switch( scan_urlencoded_query( &c, data = c, SCAN_PATH ) ) { |
199 | case 4: /* sync ? */ | 187 | case 4: /* sync ? */ |
@@ -222,7 +210,7 @@ static void httpresponse( const int64 s, struct http_data *h) { | |||
222 | if( !hash ) HTTPERROR_400_PARAM; | 210 | if( !hash ) HTTPERROR_400_PARAM; |
223 | if( ( reply_size = return_sync_for_torrent( hash, &reply ) ) <= 0 ) HTTPERROR_500; | 211 | if( ( reply_size = return_sync_for_torrent( hash, &reply ) ) <= 0 ) HTTPERROR_500; |
224 | 212 | ||
225 | return sendmallocdata( s, h, reply, reply_size ); | 213 | return sendmallocdata( s, reply, reply_size ); |
226 | case 5: /* stats ? */ | 214 | case 5: /* stats ? */ |
227 | if( byte_diff(data,5,"stats")) HTTPERROR_404; | 215 | if( byte_diff(data,5,"stats")) HTTPERROR_404; |
228 | scanon = 1; | 216 | scanon = 1; |
@@ -250,7 +238,7 @@ static void httpresponse( const int64 s, struct http_data *h) { | |||
250 | } | 238 | } |
251 | 239 | ||
252 | /* Enough for http header + whole scrape string */ | 240 | /* Enough for http header + whole scrape string */ |
253 | if( ( reply_size = return_stats_for_tracker( SUCCESS_HTTP_HEADER_LENGTH + static_scratch, mode ) ) <= 0 ) HTTPERROR_500; | 241 | if( ( reply_size = return_stats_for_tracker( SUCCESS_HTTP_HEADER_LENGTH + static_outbuf, mode ) ) <= 0 ) HTTPERROR_500; |
254 | 242 | ||
255 | break; | 243 | break; |
256 | case 6: /* scrape ? */ | 244 | case 6: /* scrape ? */ |
@@ -279,18 +267,18 @@ SCRAPE_WORKAROUND: | |||
279 | /* Scanned whole query string, no hash means full scrape... you might want to limit that */ | 267 | /* Scanned whole query string, no hash means full scrape... you might want to limit that */ |
280 | if( !hash ) { | 268 | if( !hash ) { |
281 | if( ( reply_size = return_fullscrape_for_tracker( &reply ) ) <= 0 ) HTTPERROR_500; | 269 | if( ( reply_size = return_fullscrape_for_tracker( &reply ) ) <= 0 ) HTTPERROR_500; |
282 | return sendmallocdata( s, h, reply, reply_size ); | 270 | return sendmallocdata( s, reply, reply_size ); |
283 | } | 271 | } |
284 | 272 | ||
285 | /* Enough for http header + whole scrape string */ | 273 | /* Enough for http header + whole scrape string */ |
286 | if( ( reply_size = return_scrape_for_torrent( hash, SUCCESS_HTTP_HEADER_LENGTH + static_scratch ) ) <= 0 ) HTTPERROR_500; | 274 | if( ( reply_size = return_scrape_for_torrent( hash, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf ) ) <= 0 ) HTTPERROR_500; |
287 | break; | 275 | break; |
288 | case 8: | 276 | case 8: |
289 | if( byte_diff(data,8,"announce")) HTTPERROR_404; | 277 | if( byte_diff(data,8,"announce")) HTTPERROR_404; |
290 | 278 | ||
291 | ANNOUNCE_WORKAROUND: | 279 | ANNOUNCE_WORKAROUND: |
292 | 280 | ||
293 | OT_SETIP( &peer, h->ip); | 281 | OT_SETIP( &peer, ((struct http_data*)io_getcookie( s ))->ip); |
294 | OT_SETPORT( &peer, &port ); | 282 | OT_SETPORT( &peer, &port ); |
295 | OT_FLAG( &peer ) = 0; | 283 | OT_FLAG( &peer ) = 0; |
296 | numwant = 50; | 284 | numwant = 50; |
@@ -369,10 +357,10 @@ ANNOUNCE_WORKAROUND: | |||
369 | 357 | ||
370 | if( OT_FLAG( &peer ) & PEER_FLAG_STOPPED ) { | 358 | if( OT_FLAG( &peer ) & PEER_FLAG_STOPPED ) { |
371 | remove_peer_from_torrent( hash, &peer ); | 359 | remove_peer_from_torrent( hash, &peer ); |
372 | reply_size = sprintf( static_scratch + SUCCESS_HTTP_HEADER_LENGTH, "d8:completei0e10:incompletei0e8:intervali%ie5:peers0:e", OT_CLIENT_REQUEST_INTERVAL_RANDOM ); | 360 | reply_size = sprintf( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH, "d8:completei0e10:incompletei0e8:intervali%ie5:peers0:e", OT_CLIENT_REQUEST_INTERVAL_RANDOM ); |
373 | } else { | 361 | } else { |
374 | torrent = add_peer_to_torrent( hash, &peer ); | 362 | torrent = add_peer_to_torrent( hash, &peer ); |
375 | if( !torrent || ( reply_size = return_peers_for_torrent( torrent, numwant, SUCCESS_HTTP_HEADER_LENGTH + static_scratch ) ) <= 0 ) HTTPERROR_500; | 363 | if( !torrent || ( reply_size = return_peers_for_torrent( torrent, numwant, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf ) ) <= 0 ) HTTPERROR_500; |
376 | } | 364 | } |
377 | ot_overall_successfulannounces++; | 365 | ot_overall_successfulannounces++; |
378 | break; | 366 | break; |
@@ -383,7 +371,7 @@ ANNOUNCE_WORKAROUND: | |||
383 | if( byte_diff(data,11,"mrtg_scrape")) HTTPERROR_404; | 371 | if( byte_diff(data,11,"mrtg_scrape")) HTTPERROR_404; |
384 | 372 | ||
385 | t = time( NULL ) - ot_start_time; | 373 | t = time( NULL ) - ot_start_time; |
386 | reply_size = sprintf( static_scratch + SUCCESS_HTTP_HEADER_LENGTH, | 374 | reply_size = sprintf( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH, |
387 | "%i\n%i\nUp: %i seconds (%i hours)\nPretuned by german engineers, currently handling %i connections per second.", | 375 | "%i\n%i\nUp: %i seconds (%i hours)\nPretuned by german engineers, currently handling %i connections per second.", |
388 | ot_overall_connections, ot_overall_successfulannounces, (int)t, (int)(t / 3600), (int)ot_overall_connections / ( (int)t ? (int)t : 1 ) ); | 376 | ot_overall_connections, ot_overall_successfulannounces, (int)t, (int)(t / 3600), (int)ot_overall_connections / ( (int)t ? (int)t : 1 ) ); |
389 | break; | 377 | break; |
@@ -403,16 +391,16 @@ ANNOUNCE_WORKAROUND: | |||
403 | plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for it expansion and calculate | 391 | plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for it expansion and calculate |
404 | the space NOT needed to expand in reply_off | 392 | the space NOT needed to expand in reply_off |
405 | */ | 393 | */ |
406 | reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf( static_scratch, 0, "%zd", reply_size ); | 394 | reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf( static_outbuf, 0, "%zd", reply_size ); |
407 | 395 | ||
408 | /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete | 396 | /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete |
409 | packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */ | 397 | packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */ |
410 | reply_size += 1 + sprintf( static_scratch + reply_off, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", reply_size ); | 398 | 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 ); |
411 | 399 | ||
412 | /* 3. Finally we join both blocks neatly */ | 400 | /* 3. Finally we join both blocks neatly */ |
413 | static_scratch[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n'; | 401 | static_outbuf[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n'; |
414 | 402 | ||
415 | senddata( s, h, static_scratch + reply_off, reply_size ); | 403 | senddata( s, static_outbuf + reply_off, reply_size ); |
416 | } | 404 | } |
417 | 405 | ||
418 | static void graceful( int s ) { | 406 | static void graceful( int s ) { |
@@ -463,7 +451,7 @@ static void handle_read( const int64 clientsocket ) { | |||
463 | struct http_data* h = io_getcookie( clientsocket ); | 451 | struct http_data* h = io_getcookie( clientsocket ); |
464 | size_t l; | 452 | size_t l; |
465 | 453 | ||
466 | if( ( l = io_tryread( clientsocket, static_scratch, sizeof static_scratch ) ) <= 0 ) { | 454 | if( ( l = io_tryread( clientsocket, static_inbuf, sizeof static_inbuf ) ) <= 0 ) { |
467 | if( h ) { | 455 | if( h ) { |
468 | array_reset( &h->request ); | 456 | array_reset( &h->request ); |
469 | free( h ); | 457 | free( h ); |
@@ -472,24 +460,30 @@ static void handle_read( const int64 clientsocket ) { | |||
472 | return; | 460 | return; |
473 | } | 461 | } |
474 | 462 | ||
475 | array_catb( &h->request, static_scratch, l ); | ||
476 | |||
477 | #ifdef _DEBUG_HTTPERROR | 463 | #ifdef _DEBUG_HTTPERROR |
478 | memcpy( debug_request, "500!\0", 5 ); | 464 | memcpy( debug_request, "500!\0", 5 ); |
479 | #endif | 465 | #endif |
480 | 466 | ||
467 | /* If we get the whole request in one packet, handle it without copying */ | ||
468 | if( !array_start( &h->request ) ) { | ||
469 | if( memchr( static_inbuf, '\n', l ) ) | ||
470 | return httpresponse( clientsocket, static_inbuf ); | ||
471 | return array_catb( &h->request, static_inbuf, l ); | ||
472 | } | ||
473 | |||
474 | array_catb( &h->request, static_inbuf, l ); | ||
475 | |||
481 | if( array_failed( &h->request ) ) | 476 | if( array_failed( &h->request ) ) |
482 | httperror( clientsocket, h, "500 Server Error", "Request too long."); | 477 | httperror( clientsocket, "500 Server Error", "Request too long."); |
483 | else if( array_bytes( &h->request ) > 8192 ) | 478 | else if( array_bytes( &h->request ) > 8192 ) |
484 | httperror( clientsocket, h, "500 request too long", "You sent too much headers"); | 479 | httperror( clientsocket, "500 request too long", "You sent too much headers"); |
485 | else if( ( l = httpheader_complete( h ) ) ) | 480 | else if( memchr( array_start( &h->request ), '\n', array_length( &h->request, 1 ) ) ) |
486 | httpresponse( clientsocket, h); | 481 | httpresponse( clientsocket, array_start( &h->request ) ); |
487 | } | 482 | } |
488 | 483 | ||
489 | static void handle_write( const int64 clientsocket ) { | 484 | static void handle_write( const int64 clientsocket ) { |
490 | struct http_data* h=io_getcookie( clientsocket ); | 485 | struct http_data* h=io_getcookie( clientsocket ); |
491 | if( !h ) return; | 486 | if( !h || ( iob_send( clientsocket, &h->batch ) <= 0 ) ) { |
492 | if( iob_send( clientsocket, &h->batch ) <= 0 ) { | ||
493 | iob_reset( &h->batch ); | 487 | iob_reset( &h->batch ); |
494 | io_close( clientsocket ); | 488 | io_close( clientsocket ); |
495 | free( h ); | 489 | free( h ); |