diff options
Diffstat (limited to 'opentracker.c')
-rw-r--r-- | opentracker.c | 210 |
1 files changed, 115 insertions, 95 deletions
diff --git a/opentracker.c b/opentracker.c index 5fa6548..1d18e49 100644 --- a/opentracker.c +++ b/opentracker.c | |||
@@ -33,7 +33,7 @@ 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_reply[8192]; | 36 | static char static_scratch[8192]; |
37 | 37 | ||
38 | static void carp(const char* routine) { | 38 | static void carp(const char* routine) { |
39 | buffer_puts(buffer_2,routine); | 39 | buffer_puts(buffer_2,routine); |
@@ -79,9 +79,9 @@ void senddata(int64 s, struct http_data* h, char *buffer, size_t size ) { | |||
79 | } | 79 | } |
80 | 80 | ||
81 | void httperror(int64 s,struct http_data* h,const char* title,const char* message) { | 81 | void httperror(int64 s,struct http_data* h,const char* title,const char* message) { |
82 | size_t reply_size = sprintf( static_reply, "HTTP/1.0 %s\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: %zd\r\n\r\n<title>%s</title>\n", | 82 | 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", |
83 | title, strlen(message)+strlen(title)+16-4,title+4); | 83 | title, strlen(message)+strlen(title)+16-4,title+4); |
84 | senddata(s,h,static_reply,reply_size); | 84 | senddata(s,h,static_scratch,reply_size); |
85 | } | 85 | } |
86 | 86 | ||
87 | const char* http_header(struct http_data* r,const char* h) { | 87 | const char* http_header(struct http_data* r,const char* h) { |
@@ -160,7 +160,7 @@ e400: | |||
160 | } | 160 | } |
161 | 161 | ||
162 | /* Enough for http header + whole scrape string */ | 162 | /* Enough for http header + whole scrape string */ |
163 | if( ( reply_size = return_stats_for_tracker( SUCCESS_HTTP_HEADER_LENGTH + static_reply, mode ) ) <= 0 ) | 163 | if( ( reply_size = return_stats_for_tracker( SUCCESS_HTTP_HEADER_LENGTH + static_scratch, mode ) ) <= 0 ) |
164 | goto e500; | 164 | goto e500; |
165 | break; | 165 | break; |
166 | case 6: /* scrape ? */ | 166 | case 6: /* scrape ? */ |
@@ -198,7 +198,7 @@ e400_param: | |||
198 | return httperror(s,h,"400 Invalid Request","This server only serves specific scrapes."); | 198 | return httperror(s,h,"400 Invalid Request","This server only serves specific scrapes."); |
199 | 199 | ||
200 | /* Enough for http header + whole scrape string */ | 200 | /* Enough for http header + whole scrape string */ |
201 | if( ( reply_size = return_scrape_for_torrent( hash, SUCCESS_HTTP_HEADER_LENGTH + static_reply ) ) <= 0 ) | 201 | if( ( reply_size = return_scrape_for_torrent( hash, SUCCESS_HTTP_HEADER_LENGTH + static_scratch ) ) <= 0 ) |
202 | goto e500; | 202 | goto e500; |
203 | break; | 203 | break; |
204 | case 8: | 204 | case 8: |
@@ -290,14 +290,14 @@ e400_param: | |||
290 | 290 | ||
291 | if( OT_FLAG( &peer ) & PEER_FLAG_STOPPED ) { | 291 | if( OT_FLAG( &peer ) & PEER_FLAG_STOPPED ) { |
292 | remove_peer_from_torrent( hash, &peer ); | 292 | remove_peer_from_torrent( hash, &peer ); |
293 | memmove( static_reply + SUCCESS_HTTP_HEADER_LENGTH, "d8:completei0e10:incompletei0e8:intervali600e5:peers0:e", reply_size = 55 ); | 293 | memmove( static_scratch + SUCCESS_HTTP_HEADER_LENGTH, "d8:completei0e10:incompletei0e8:intervali600e5:peers0:e", reply_size = 55 ); |
294 | } else { | 294 | } else { |
295 | torrent = add_peer_to_torrent( hash, &peer ); | 295 | torrent = add_peer_to_torrent( hash, &peer ); |
296 | if( !torrent ) { | 296 | if( !torrent ) { |
297 | e500: | 297 | e500: |
298 | return httperror(s,h,"500 Internal Server Error","A server error has occured. Please retry later."); | 298 | return httperror(s,h,"500 Internal Server Error","A server error has occured. Please retry later."); |
299 | } | 299 | } |
300 | if( ( reply_size = return_peers_for_torrent( torrent, numwant, SUCCESS_HTTP_HEADER_LENGTH + static_reply ) ) <= 0 ) | 300 | if( ( reply_size = return_peers_for_torrent( torrent, numwant, SUCCESS_HTTP_HEADER_LENGTH + static_scratch ) ) <= 0 ) |
301 | goto e500; | 301 | goto e500; |
302 | } | 302 | } |
303 | break; | 303 | break; |
@@ -306,7 +306,7 @@ e500: | |||
306 | goto e404; | 306 | goto e404; |
307 | { | 307 | { |
308 | time_t seconds_elapsed = time( NULL ) - ot_start_time; | 308 | time_t seconds_elapsed = time( NULL ) - ot_start_time; |
309 | reply_size = sprintf( static_reply + SUCCESS_HTTP_HEADER_LENGTH, | 309 | reply_size = sprintf( static_scratch + SUCCESS_HTTP_HEADER_LENGTH, |
310 | "%i\n%i\nUp: %i seconds (%i hours)\nPretuned by german engineers, currently handling %i connections per second.", | 310 | "%i\n%i\nUp: %i seconds (%i hours)\nPretuned by german engineers, currently handling %i connections per second.", |
311 | ot_overall_connections, ot_overall_connections, (int)seconds_elapsed, | 311 | ot_overall_connections, ot_overall_connections, (int)seconds_elapsed, |
312 | (int)(seconds_elapsed / 3600), (int)ot_overall_connections / ( (int)seconds_elapsed ? (int)seconds_elapsed : 1 ) ); | 312 | (int)(seconds_elapsed / 3600), (int)ot_overall_connections / ( (int)seconds_elapsed ? (int)seconds_elapsed : 1 ) ); |
@@ -325,16 +325,16 @@ e404: | |||
325 | plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for it expansion and calculate | 325 | plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for it expansion and calculate |
326 | the space NOT needed to expand in reply_off | 326 | the space NOT needed to expand in reply_off |
327 | */ | 327 | */ |
328 | size_t reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf( static_reply, 0, "%zd", reply_size ); | 328 | size_t reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf( static_scratch, 0, "%zd", reply_size ); |
329 | 329 | ||
330 | /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete | 330 | /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete |
331 | packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */ | 331 | packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */ |
332 | reply_size += 1 + sprintf( static_reply + reply_off, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", reply_size ); | 332 | 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 ); |
333 | 333 | ||
334 | /* 3. Finally we join both blocks neatly */ | 334 | /* 3. Finally we join both blocks neatly */ |
335 | static_reply[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n'; | 335 | static_scratch[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n'; |
336 | 336 | ||
337 | senddata( s, h, static_reply + reply_off, reply_size ); | 337 | senddata( s, h, static_scratch + reply_off, reply_size ); |
338 | } else { | 338 | } else { |
339 | if( h ) array_reset(&h->r); | 339 | if( h ) array_reset(&h->r); |
340 | free( h ); io_close( s ); | 340 | free( h ); io_close( s ); |
@@ -385,17 +385,113 @@ void help( char *name ) { | |||
385 | ); | 385 | ); |
386 | } | 386 | } |
387 | 387 | ||
388 | int main( int argc, char **argv ) { | 388 | void handle_read( int64 clientsocket, int afteraccept ) { |
389 | int s=socket_tcp4(); | 389 | struct http_data* h = io_getcookie( clientsocket ); |
390 | int l = io_tryread( clientsocket, static_scratch, sizeof static_scratch ); | ||
391 | tai6464 t; | ||
392 | |||
393 | taia_now(&t); | ||
394 | taia_addsec(&t,&t,OT_CLIENT_TIMEOUT); | ||
395 | io_timeout(clientsocket,t); | ||
396 | |||
397 | if( l <= 0 ) { | ||
398 | // getting 0 bytes doesn't mean connection closed, | ||
399 | // when we try the shortcut read() after accept() | ||
400 | // and nothing is there yet | ||
401 | if( afteraccept && ( errno == EAGAIN ) ) | ||
402 | return; | ||
403 | if( h ) { | ||
404 | array_reset(&h->r); | ||
405 | free(h); | ||
406 | } | ||
407 | io_close(clientsocket); | ||
408 | return; | ||
409 | } | ||
410 | |||
411 | if( afteraccept ) fprintf( stderr, "*" ); | ||
412 | |||
413 | array_catb(&h->r,static_scratch,l); | ||
414 | |||
415 | if( array_failed(&h->r)) | ||
416 | httperror(clientsocket,h,"500 Server Error","Request too long."); | ||
417 | else if (array_bytes(&h->r)>8192) | ||
418 | httperror(clientsocket,h,"500 request too long","You sent too much headers"); | ||
419 | else if ((l=header_complete(h))) | ||
420 | httpresponse(clientsocket,h); | ||
421 | } | ||
422 | |||
423 | void server_mainloop( int64 serversocket ) { | ||
390 | tai6464 t, next_timeout_check; | 424 | tai6464 t, next_timeout_check; |
425 | unsigned char ip[4]; | ||
426 | uint16 port; | ||
427 | |||
428 | io_wantread( serversocket ); | ||
429 | taia_now( &next_timeout_check ); | ||
430 | |||
431 | for (;;) { | ||
432 | int64 i; | ||
433 | taia_now(&t); | ||
434 | taia_addsec(&t,&t,OT_CLIENT_TIMEOUT_CHECKINTERVAL); | ||
435 | io_waituntil(t); | ||
436 | |||
437 | taia_now(&t); | ||
438 | if( taia_less( &next_timeout_check, &t ) ) { | ||
439 | while( ( i = io_timeouted() ) != -1 ) { | ||
440 | struct http_data* h=io_getcookie(i); | ||
441 | if( h ) { | ||
442 | array_reset( &h->r ); | ||
443 | free( h ); | ||
444 | } | ||
445 | io_close(i); | ||
446 | } | ||
447 | taia_now(&next_timeout_check); | ||
448 | taia_addsec(&next_timeout_check,&next_timeout_check,OT_CLIENT_TIMEOUT_CHECKINTERVAL); | ||
449 | } | ||
450 | |||
451 | while( ( i = io_canread() ) != -1 ) { | ||
452 | |||
453 | if( i != serversocket ) { | ||
454 | handle_read( i, 0 ); | ||
455 | continue; | ||
456 | } | ||
457 | |||
458 | // Attention, i changes from what io_canread() returned to | ||
459 | // what socket_accept4 returns as new socket | ||
460 | while( ( i = socket_accept4( serversocket, (char*)ip, &port) ) != -1 ) { | ||
461 | if( io_fd( i ) ) { | ||
462 | struct http_data* h=(struct http_data*)malloc(sizeof(struct http_data)); | ||
463 | io_wantread( i ); | ||
464 | |||
465 | if (h) { | ||
466 | byte_zero(h,sizeof(struct http_data)); | ||
467 | memmove(h->ip,ip,sizeof(ip)); | ||
468 | io_setcookie(i,h); | ||
469 | ++ot_overall_connections; | ||
470 | handle_read(i,1); | ||
471 | } else | ||
472 | io_close(i); | ||
473 | } else | ||
474 | io_close(i); | ||
475 | } | ||
476 | |||
477 | if( errno==EAGAIN ) | ||
478 | io_eagain( serversocket ); | ||
479 | else | ||
480 | carp( "socket_accept4" ); | ||
481 | } | ||
482 | } | ||
483 | } | ||
484 | |||
485 | int main( int argc, char **argv ) { | ||
486 | int64 s = socket_tcp4( ); | ||
391 | char *serverip = NULL; | 487 | char *serverip = NULL; |
392 | char *serverdir = "."; | 488 | char *serverdir = "."; |
393 | uint16 port = 6969; | 489 | uint16 port = 6969; |
394 | unsigned char ip[4]; | 490 | int scanon = 1; |
395 | 491 | ||
396 | while( 1 ) { | 492 | while( scanon ) { |
397 | switch( getopt(argc,argv,":i:p:d:ocbBh") ) { | 493 | switch( getopt(argc,argv,":i:p:d:ocbBh") ) { |
398 | case -1: goto allparsed; | 494 | case -1 : scanon = 0; break; |
399 | case 'i': serverip = optarg; break; | 495 | case 'i': serverip = optarg; break; |
400 | case 'p': port = (uint16)atol( optarg ); break; | 496 | case 'p': port = (uint16)atol( optarg ); break; |
401 | case 'd': serverdir = optarg; break; | 497 | case 'd': serverdir = optarg; break; |
@@ -413,14 +509,13 @@ int main( int argc, char **argv ) { | |||
413 | } | 509 | } |
414 | } | 510 | } |
415 | 511 | ||
416 | allparsed: | ||
417 | if (socket_bind4_reuse(s,serverip,port)==-1) | 512 | if (socket_bind4_reuse(s,serverip,port)==-1) |
418 | panic("socket_bind4_reuse"); | 513 | panic("socket_bind4_reuse"); |
419 | 514 | ||
420 | setegid((gid_t)-2); setuid((uid_t)-2); | 515 | setegid((gid_t)-2); setuid((uid_t)-2); |
421 | setgid((gid_t)-2); seteuid((uid_t)-2); | 516 | setgid((gid_t)-2); seteuid((uid_t)-2); |
422 | 517 | ||
423 | if (socket_listen(s,16)==-1) | 518 | if (socket_listen(s,SOMAXCONN)==-1) |
424 | panic("socket_listen"); | 519 | panic("socket_listen"); |
425 | 520 | ||
426 | if (!io_fd(s)) | 521 | if (!io_fd(s)) |
@@ -433,82 +528,7 @@ allparsed: | |||
433 | 528 | ||
434 | ot_start_time = time( NULL ); | 529 | ot_start_time = time( NULL ); |
435 | 530 | ||
436 | io_wantread( s ); | 531 | server_mainloop(s); |
437 | taia_now( &next_timeout_check ); | ||
438 | |||
439 | for (;;) { | ||
440 | int64 i; | ||
441 | taia_now(&t); | ||
442 | taia_addsec(&t,&t,OT_CLIENT_TIMEOUT_CHECKINTERVAL); | ||
443 | io_waituntil(t); | ||
444 | 532 | ||
445 | taia_now(&t); | ||
446 | if( taia_less( &next_timeout_check, &t ) ) { | ||
447 | while( ( i = io_timeouted() ) != -1 ) { | ||
448 | struct http_data* h=io_getcookie(i); | ||
449 | if( h ) { | ||
450 | array_reset( &h->r ); | ||
451 | free( h ); | ||
452 | } | ||
453 | io_close(i); | ||
454 | } | ||
455 | taia_now(&next_timeout_check); | ||
456 | taia_addsec(&next_timeout_check,&next_timeout_check,OT_CLIENT_TIMEOUT_CHECKINTERVAL); | ||
457 | } | ||
458 | |||
459 | while( ( i = io_canread() ) != -1 ) { | ||
460 | if( i == s ) { | ||
461 | int n; | ||
462 | while( ( n = socket_accept4( s, (char*)ip, &port) ) != -1 ) { | ||
463 | if( io_fd( n ) ) { | ||
464 | struct http_data* h=(struct http_data*)malloc(sizeof(struct http_data)); | ||
465 | io_wantread(n); | ||
466 | |||
467 | if (h) { | ||
468 | byte_zero(h,sizeof(struct http_data)); | ||
469 | memmove(h->ip,ip,sizeof(ip)); | ||
470 | taia_now(&t); | ||
471 | taia_addsec(&t,&t,OT_CLIENT_TIMEOUT); | ||
472 | io_timeout(n,t); | ||
473 | io_setcookie(n,h); | ||
474 | ++ot_overall_connections; | ||
475 | } else | ||
476 | io_close(n); | ||
477 | } else | ||
478 | io_close(n); | ||
479 | } | ||
480 | if( errno==EAGAIN ) | ||
481 | io_eagain(s); | ||
482 | else | ||
483 | carp("socket_accept4"); | ||
484 | } else { | ||
485 | /* unsigned (sic!) */ char buf[8192]; | ||
486 | struct http_data* h=io_getcookie(i); | ||
487 | |||
488 | int l=io_tryread(i,buf,sizeof buf); | ||
489 | if( l <= 0 ) { | ||
490 | if( h ) { | ||
491 | array_reset(&h->r); | ||
492 | free(h); | ||
493 | } | ||
494 | io_close(i); | ||
495 | } else { | ||
496 | array_catb(&h->r,buf,l); | ||
497 | |||
498 | if( array_failed(&h->r)) | ||
499 | httperror(i,h,"500 Server Error","Request too long."); | ||
500 | else if (array_bytes(&h->r)>8192) | ||
501 | httperror(i,h,"500 request too long","You sent too much headers"); | ||
502 | else if ((l=header_complete(h))) | ||
503 | httpresponse(i,h); | ||
504 | else { | ||
505 | taia_now(&t); | ||
506 | taia_addsec(&t,&t,OT_CLIENT_TIMEOUT); | ||
507 | io_timeout(i,t); | ||
508 | } | ||
509 | } | ||
510 | } | ||
511 | } | ||
512 | } | ||
513 | return 0; | 533 | return 0; |
514 | } | 534 | } |