summaryrefslogtreecommitdiff
path: root/opentracker.c
diff options
context:
space:
mode:
authorerdgeist <>2007-01-29 02:02:03 +0000
committererdgeist <>2007-01-29 02:02:03 +0000
commit5a3d6b402979d7d9bf8da470f081bb6ca4122cf6 (patch)
tree21033b84fbd2e7cb98bdd1a5259f0b6e6c56275a /opentracker.c
parent4c6bcc89cef49841e5eba095435c49cbdfbf2801 (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.c122
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;
33static const size_t SUCCESS_HTTP_HEADER_LENGTH = 80; 33static const size_t SUCCESS_HTTP_HEADER_LENGTH = 80;
34static const size_t SUCCESS_HTTP_SIZE_OFF = 17; 34static const size_t SUCCESS_HTTP_SIZE_OFF = 17;
35/* To always have space for error messages ;) */ 35/* To always have space for error messages ;) */
36static char static_scratch[8192]; 36static char static_inbuf[8192];
37static char static_outbuf[8192*4];
37 38
38#ifdef _DEBUG_HTTPERROR 39#ifdef _DEBUG_HTTPERROR
39static char debug_request[8192]; 40static char debug_request[8192];
@@ -51,12 +52,11 @@ struct http_data {
51 52
52int main( int argc, char **argv ); 53int main( int argc, char **argv );
53 54
54static int httpheader_complete( struct http_data *h ); 55static void httperror( const int64 s, const char *title, const char *message );
55static void httperror( const int64 s, struct http_data *h, const char *title, const char *message ); 56static void httpresponse( const int64 s, char *data );
56static void httpresponse( const int64 s, struct http_data *h);
57 57
58static void sendmallocdata( const int64 s, struct http_data *h, char *buffer, const size_t size ); 58static void sendmallocdata( const int64 s, char *buffer, const size_t size );
59static void senddata( const int64 s, struct http_data *h, char *buffer, const size_t size ); 59static void senddata( const int64 s, char *buffer, const size_t size );
60 60
61static void server_mainloop( const int64 serversocket ); 61static void server_mainloop( const int64 serversocket );
62static void handle_timeouted( void ); 62static void handle_timeouted( void );
@@ -71,11 +71,11 @@ static void carp( const char *routine );
71static void panic( const char *routine ); 71static void panic( const char *routine );
72static void graceful( int s ); 72static 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
94static int httpheader_complete( struct http_data *h ) { 94static 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
105static 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
114static void sendmallocdata( const int64 s, struct http_data *h, char *buffer, size_t size ) { 103static 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
141static void senddata( const int64 s, struct http_data *h, char *buffer, size_t size ) { 131static 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
171static void httpresponse( const int64 s, struct http_data *h) { 162static 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
291ANNOUNCE_WORKAROUND: 279ANNOUNCE_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
418static void graceful( int s ) { 406static 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
489static void handle_write( const int64 clientsocket ) { 484static 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 );