2007-01-26 16:26:49 +00:00
/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2007-01-05 00:00:42 +00:00
It is considered beerware . Prost . Skol . Cheers or whatever .
Some of the stuff below is stolen from Fefes example libowfat httpd .
*/
2006-12-05 12:56:56 +00:00
# include "socket.h"
# include "io.h"
2007-01-20 01:50:28 +00:00
# include "iob.h"
2006-12-05 12:56:56 +00:00
# include "buffer.h"
# include "array.h"
2007-01-11 01:06:10 +00:00
# include "byte.h"
2006-12-05 12:56:56 +00:00
# include "case.h"
# include "fmt.h"
# include "str.h"
2006-12-06 18:36:14 +00:00
# include <string.h>
2006-12-05 12:56:56 +00:00
# include <sys/types.h>
# include <sys/stat.h>
2006-12-16 16:14:34 +00:00
# include <sys/socket.h>
2006-12-15 23:29:38 +00:00
# include <arpa/inet.h>
2006-12-05 12:56:56 +00:00
# include <unistd.h>
# include <stdlib.h>
# include <errno.h>
2006-12-08 22:37:44 +00:00
# include <signal.h>
# include <stdio.h>
2006-12-05 12:56:56 +00:00
2006-12-08 20:28:17 +00:00
# include "trackerlogic.h"
# include "scan_urlencoded_query.h"
2007-01-26 16:26:49 +00:00
/* Globals */
2006-12-15 22:40:33 +00:00
static unsigned int ot_overall_connections = 0 ;
2007-01-23 15:48:51 +00:00
static unsigned int ot_overall_successfulannounces = 0 ;
2006-12-15 22:40:33 +00:00
static time_t ot_start_time ;
2007-01-11 01:06:10 +00:00
static const size_t SUCCESS_HTTP_HEADER_LENGTH = 80 ;
static const size_t SUCCESS_HTTP_SIZE_OFF = 17 ;
2007-02-01 13:30:58 +00:00
2007-01-11 01:06:10 +00:00
/* To always have space for error messages ;) */
2007-02-01 13:30:58 +00:00
# define static_outbuf_size 8192
# define static_outbuf_count 64
# define static_outbuf ( static_outbufs + static_outbuf_size * static_outbuf_off )
# define static_outbuf_next ( static_outbuf_off = ( static_outbuf_off + 1 ) & ( static_outbuf_count - 1 ) )
2007-01-29 02:02:03 +00:00
static char static_inbuf [ 8192 ] ;
2007-02-01 13:30:58 +00:00
static char static_outbufs [ static_outbuf_size * static_outbuf_count ] ;
static int static_outbuf_off = 0 ;
2006-12-15 22:07:33 +00:00
2007-01-24 20:13:30 +00:00
# ifdef _DEBUG_HTTPERROR
static char debug_request [ 8192 ] ;
# endif
2007-01-19 17:50:36 +00:00
2007-01-26 16:26:49 +00:00
struct http_data {
union {
array request ;
io_batch batch ;
} ;
unsigned char ip [ 4 ] ;
} ;
/* Prototypes */
int main ( int argc , char * * argv ) ;
2007-01-29 02:02:03 +00:00
static void httperror ( const int64 s , const char * title , const char * message ) ;
static void httpresponse ( const int64 s , char * data ) ;
2007-01-26 16:26:49 +00:00
2007-01-29 02:02:03 +00:00
static void sendmallocdata ( const int64 s , char * buffer , const size_t size ) ;
static void senddata ( const int64 s , char * buffer , const size_t size ) ;
2007-01-26 16:26:49 +00:00
static void server_mainloop ( const int64 serversocket ) ;
static void handle_timeouted ( void ) ;
static void handle_accept ( const int64 serversocket ) ;
static void handle_read ( const int64 clientsocket ) ;
static void handle_write ( const int64 clientsocket ) ;
static void usage ( char * name ) ;
static void help ( char * name ) ;
static void carp ( const char * routine ) ;
static void panic ( const char * routine ) ;
static void graceful ( int s ) ;
2007-01-29 02:02:03 +00:00
# define HTTPERROR_400 return httperror( s, "400 Invalid Request", "This server only understands GET." )
# define HTTPERROR_400_PARAM return httperror( s, "400 Invalid Request", "Invalid parameter" )
# define HTTPERROR_400_COMPACT return httperror( s, "400 Invalid Request", "This server only delivers compact results." )
# define HTTPERROR_404 return httperror( s, "404 Not Found", "No such file or directory." )
# define HTTPERROR_500 return httperror( s, "500 Internal Server Error", "A server error has occured. Please retry later." )
2007-01-26 16:26:49 +00:00
/* End of prototypes */
static void carp ( const char * routine ) {
2007-01-24 22:23:18 +00:00
buffer_puts ( buffer_2 , routine ) ;
buffer_puts ( buffer_2 , " : " ) ;
buffer_puterror ( buffer_2 ) ;
buffer_putnlflush ( buffer_2 ) ;
2006-12-05 12:56:56 +00:00
}
2007-01-26 16:26:49 +00:00
static void panic ( const char * routine ) {
2007-01-24 22:23:18 +00:00
carp ( routine ) ;
exit ( 111 ) ;
2006-12-05 12:56:56 +00:00
}
2007-01-29 02:02:03 +00:00
static void httperror ( const int64 s , const char * title , const char * message ) {
size_t reply_size = sprintf ( static_outbuf , " HTTP/1.0 %s \r \n Content-Type: text/html \r \n Connection: close \r \n Content-Length: %zd \r \n \r \n <title>%s</title> \n " ,
2007-01-26 16:26:49 +00:00
title , strlen ( message ) + strlen ( title ) + 16 - 4 , title + 4 ) ;
# ifdef _DEBUG_HTTPERROR
fprintf ( stderr , " DEBUG: invalid request was: %s \n " , debug_request ) ;
# endif
2007-01-29 02:02:03 +00:00
senddata ( s , static_outbuf , reply_size ) ;
2007-01-26 16:26:49 +00:00
}
2007-01-29 02:02:03 +00:00
static void sendmallocdata ( const int64 s , char * buffer , size_t size ) {
struct http_data * h = io_getcookie ( s ) ;
2007-01-20 11:13:30 +00:00
char * header ;
size_t header_size ;
2007-01-29 02:02:03 +00:00
tai6464 t ;
2007-01-20 11:13:30 +00:00
2007-01-26 16:26:49 +00:00
if ( ! h )
2007-01-29 02:02:03 +00:00
return free ( buffer ) ;
2007-01-24 22:23:18 +00:00
array_reset ( & h - > request ) ;
2007-01-20 11:13:30 +00:00
header = malloc ( SUCCESS_HTTP_HEADER_LENGTH ) ;
2007-01-26 16:26:49 +00:00
if ( ! header ) {
free ( buffer ) ;
HTTPERROR_500 ;
}
2007-01-20 11:13:30 +00:00
header_size = sprintf ( header , " HTTP/1.0 200 OK \r \n Content-Type: text/plain \r \n Content-Length: %zd \r \n \r \n " , size ) ;
iob_reset ( & h - > batch ) ;
iob_addbuf_free ( & h - > batch , header , header_size ) ;
iob_addbuf_free ( & h - > batch , buffer , size ) ;
2007-01-26 16:26:49 +00:00
/* writeable sockets just have a tcp timeout */
2007-01-29 02:02:03 +00:00
taia_uint ( & t , 0 ) ; io_timeout ( s , t ) ;
2007-01-20 11:13:30 +00:00
io_dontwantread ( s ) ;
io_wantwrite ( s ) ;
}
2007-01-29 02:02:03 +00:00
static void senddata ( const int64 s , char * buffer , size_t size ) {
struct http_data * h = io_getcookie ( s ) ;
2007-01-31 09:50:46 +00:00
ssize_t written_size ;
2007-01-08 00:34:37 +00:00
2007-01-24 22:23:18 +00:00
/* whoever sends data is not interested in its input-array */
if ( h )
array_reset ( & h - > request ) ;
2007-01-09 06:30:37 +00:00
written_size = write ( s , buffer , size ) ;
if ( ( written_size < 0 ) | | ( written_size = = size ) ) {
2007-02-01 13:30:58 +00:00
static_outbuf_next ;
2007-01-24 22:23:18 +00:00
free ( h ) ; io_close ( s ) ;
2007-01-08 00:34:37 +00:00
} else {
2007-01-20 01:50:28 +00:00
char * outbuf = malloc ( size - written_size ) ;
tai6464 t ;
if ( ! outbuf ) {
free ( h ) ; io_close ( s ) ;
return ;
}
iob_reset ( & h - > batch ) ;
memmove ( outbuf , buffer + written_size , size - written_size ) ;
iob_addbuf_free ( & h - > batch , outbuf , size - written_size ) ;
2007-01-26 16:26:49 +00:00
/* writeable sockets just have a tcp timeout */
2007-01-24 22:23:18 +00:00
taia_uint ( & t , 0 ) ; io_timeout ( s , t ) ;
2007-01-20 11:13:30 +00:00
io_dontwantread ( s ) ;
io_wantwrite ( s ) ;
2007-01-08 00:34:37 +00:00
}
2006-12-05 12:56:56 +00:00
}
2007-01-29 02:02:03 +00:00
static void httpresponse ( const int64 s , char * data ) {
char * c , * reply ;
2007-01-11 01:06:10 +00:00
ot_peer peer ;
ot_torrent * torrent ;
ot_hash * hash = NULL ;
2007-01-17 11:51:55 +00:00
int numwant , tmp , scanon , mode ;
2007-01-11 01:06:10 +00:00
unsigned short port = htons ( 6881 ) ;
2007-01-26 16:26:49 +00:00
time_t t ;
2007-01-31 09:50:46 +00:00
ssize_t len ;
2007-01-26 18:09:14 +00:00
size_t reply_size = 0 , reply_off ;
2007-01-11 01:06:10 +00:00
2007-01-24 20:13:30 +00:00
# ifdef _DEBUG_HTTPERROR
2007-01-29 02:02:03 +00:00
memcpy ( debug_request , data , sizeof ( debug_request ) ) ;
2007-01-24 20:13:30 +00:00
# endif
2007-01-29 02:02:03 +00:00
/* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */
if ( byte_diff ( data , 5 , " GET / " ) ) HTTPERROR_400 ;
2006-12-08 20:50:06 +00:00
2007-01-29 02:02:03 +00:00
/* Query string MUST terminate with SP -- we know that theres at least a '\n' where this search terminates */
for ( c = data + 5 ; * c ! = ' ' & & * c ! = ' \t ' & & * c ! = ' \n ' & & * c ! = ' \r ' ; + + c ) ;
if ( * c ! = ' ' ) HTTPERROR_400 ;
2007-01-11 01:06:10 +00:00
2007-01-29 02:02:03 +00:00
/* Skip leading '/' */
for ( c = data + 4 ; * c = = ' / ' ; + + c ) ;
2007-01-11 01:06:10 +00:00
switch ( scan_urlencoded_query ( & c , data = c , SCAN_PATH ) ) {
2007-01-26 16:26:49 +00:00
case 4 : /* sync ? */
if ( byte_diff ( data , 4 , " sync " ) ) HTTPERROR_404 ;
scanon = 1 ;
while ( scanon ) {
switch ( scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_PARAM ) ) {
case - 2 : scanon = 0 ; break ; /* TERMINATOR */
case - 1 : HTTPERROR_400_PARAM ; /* PARSE ERROR */
case 9 :
if ( byte_diff ( data , 9 , " info_hash " ) ) {
scan_urlencoded_query ( & c , NULL , SCAN_SEARCHPATH_VALUE ) ;
continue ;
}
/* ignore this, when we have less than 20 bytes */
if ( scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_VALUE ) ! = 20 ) HTTPERROR_400_PARAM ;
hash = ( ot_hash * ) data ; /* Fall through intended */
break ;
default :
scan_urlencoded_query ( & c , NULL , SCAN_SEARCHPATH_VALUE ) ;
break ;
}
}
if ( ! hash ) HTTPERROR_400_PARAM ;
2007-01-31 09:50:46 +00:00
if ( ! ( reply_size = return_sync_for_torrent ( hash , & reply ) ) ) HTTPERROR_500 ;
2007-01-26 16:26:49 +00:00
2007-01-29 02:02:03 +00:00
return sendmallocdata ( s , reply , reply_size ) ;
2007-01-24 22:23:18 +00:00
case 5 : /* stats ? */
2007-01-26 16:26:49 +00:00
if ( byte_diff ( data , 5 , " stats " ) ) HTTPERROR_404 ;
2007-01-17 11:51:55 +00:00
scanon = 1 ;
mode = STATS_MRTG ;
while ( scanon ) {
switch ( scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_PARAM ) ) {
2007-01-26 16:26:49 +00:00
case - 2 : scanon = 0 ; break ; /* TERMINATOR */
case - 1 : HTTPERROR_400_PARAM ; /* PARSE ERROR */
default : scan_urlencoded_query ( & c , NULL , SCAN_SEARCHPATH_VALUE ) ; break ;
2007-01-17 11:51:55 +00:00
case 4 :
2007-01-24 22:23:18 +00:00
if ( byte_diff ( data , 4 , " mode " ) ) {
2007-01-17 11:51:55 +00:00
scan_urlencoded_query ( & c , NULL , SCAN_SEARCHPATH_VALUE ) ;
continue ;
}
2007-01-31 09:50:46 +00:00
if ( scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_VALUE ) ! = 4 ) HTTPERROR_400_PARAM ;
2007-01-17 11:51:55 +00:00
if ( ! byte_diff ( data , 4 , " mrtg " ) )
mode = STATS_MRTG ;
else if ( ! byte_diff ( data , 4 , " top5 " ) )
mode = STATS_TOP5 ;
2007-02-01 13:51:40 +00:00
else if ( ! byte_diff ( data , 4 , " dmem " ) )
mode = STATS_DMEM ;
2007-01-17 11:51:55 +00:00
else
2007-01-26 16:26:49 +00:00
HTTPERROR_400_PARAM ;
2007-01-17 11:51:55 +00:00
}
}
2007-02-01 13:51:40 +00:00
if ( mode = = STATS_DMEM ) {
if ( ! ( reply_size = return_memstat_for_tracker ( & reply ) ) ) HTTPERROR_500 ;
return sendmallocdata ( s , reply , reply_size ) ;
}
2007-01-16 02:59:39 +00:00
/* Enough for http header + whole scrape string */
2007-01-31 09:50:46 +00:00
if ( ! ( reply_size = return_stats_for_tracker ( SUCCESS_HTTP_HEADER_LENGTH + static_outbuf , mode ) ) ) HTTPERROR_500 ;
2007-01-26 16:26:49 +00:00
2007-02-01 15:35:01 +00:00
ot_overall_successfulannounces + + ;
2007-01-16 02:59:39 +00:00
break ;
2007-01-11 01:06:10 +00:00
case 6 : /* scrape ? */
2007-01-26 16:26:49 +00:00
if ( byte_diff ( data , 6 , " scrape " ) ) HTTPERROR_404 ;
2007-01-11 01:06:10 +00:00
2007-01-26 18:09:14 +00:00
SCRAPE_WORKAROUND :
scanon = 1 ;
2007-01-11 01:06:10 +00:00
while ( scanon ) {
switch ( scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_PARAM ) ) {
2007-01-26 16:26:49 +00:00
case - 2 : scanon = 0 ; break ; /* TERMINATOR */
case - 1 : HTTPERROR_400_PARAM ; /* PARSE ERROR */
default : scan_urlencoded_query ( & c , NULL , SCAN_SEARCHPATH_VALUE ) ; break ;
2007-01-11 01:06:10 +00:00
case 9 :
if ( byte_diff ( data , 9 , " info_hash " ) ) {
2006-12-14 02:44:50 +00:00
scan_urlencoded_query ( & c , NULL , SCAN_SEARCHPATH_VALUE ) ;
2007-01-11 01:06:10 +00:00
continue ;
}
/* ignore this, when we have less than 20 bytes */
2007-01-26 16:26:49 +00:00
if ( scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_VALUE ) ! = 20 ) HTTPERROR_400_PARAM ;
2007-01-11 01:06:10 +00:00
hash = ( ot_hash * ) data ; /* Fall through intended */
break ;
2006-12-14 02:44:50 +00:00
}
2007-01-11 01:06:10 +00:00
}
2006-12-14 02:44:50 +00:00
2007-01-20 11:13:30 +00:00
/* Scanned whole query string, no hash means full scrape... you might want to limit that */
2007-01-24 21:06:19 +00:00
if ( ! hash ) {
2007-01-31 09:50:46 +00:00
if ( ! ( reply_size = return_fullscrape_for_tracker ( & reply ) ) ) HTTPERROR_500 ;
2007-01-29 02:02:03 +00:00
return sendmallocdata ( s , reply , reply_size ) ;
2007-01-20 11:13:30 +00:00
}
2007-01-26 16:26:49 +00:00
/* Enough for http header + whole scrape string */
2007-01-31 09:50:46 +00:00
if ( ! ( reply_size = return_scrape_for_torrent ( hash , SUCCESS_HTTP_HEADER_LENGTH + static_outbuf ) ) ) HTTPERROR_500 ;
2007-01-11 01:06:10 +00:00
break ;
2007-01-27 16:06:13 +00:00
case 8 :
2007-01-31 09:50:46 +00:00
if ( byte_diff ( data , 8 , " announce " ) ) HTTPERROR_404 ;
2007-01-11 01:06:10 +00:00
2007-01-26 18:09:14 +00:00
ANNOUNCE_WORKAROUND :
2007-01-31 09:50:46 +00:00
OT_SETIP ( & peer , ( ( struct http_data * ) io_getcookie ( s ) ) - > ip ) ;
2007-01-11 01:06:10 +00:00
OT_SETPORT ( & peer , & port ) ;
OT_FLAG ( & peer ) = 0 ;
numwant = 50 ;
scanon = 1 ;
while ( scanon ) {
switch ( scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_PARAM ) ) {
2007-01-26 16:26:49 +00:00
case - 2 : scanon = 0 ; break ; /* TERMINATOR */
case - 1 : HTTPERROR_400_PARAM ; /* PARSE ERROR */
default : scan_urlencoded_query ( & c , NULL , SCAN_SEARCHPATH_VALUE ) ; break ;
2007-01-03 05:11:48 +00:00
# ifdef WANT_IP_FROM_QUERY_STRING
2007-01-11 01:06:10 +00:00
case 2 :
if ( ! byte_diff ( data , 2 , " ip " ) ) {
unsigned char ip [ 4 ] ;
2007-01-31 09:50:46 +00:00
len = scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_VALUE ) ;
2007-01-26 16:26:49 +00:00
if ( ( len < = 0 ) | | scan_fixed_ip ( data , len , ip ) ) HTTPERROR_400_PARAM ;
2007-01-24 22:23:18 +00:00
OT_SETIP ( & peer , ip ) ;
2007-01-11 01:06:10 +00:00
} else
scan_urlencoded_query ( & c , NULL , SCAN_SEARCHPATH_VALUE ) ;
break ;
2007-01-03 05:11:48 +00:00
# endif
2007-01-11 01:06:10 +00:00
case 4 :
2007-01-31 09:50:46 +00:00
if ( ! byte_diff ( data , 4 , " port " ) ) {
len = scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_VALUE ) ;
2007-01-26 16:26:49 +00:00
if ( ( len < = 0 ) | | scan_fixed_int ( data , len , & tmp ) | | ( tmp > 0xffff ) ) HTTPERROR_400_PARAM ;
2007-01-24 22:23:18 +00:00
port = htons ( tmp ) ; OT_SETPORT ( & peer , & port ) ;
2007-01-31 09:50:46 +00:00
} else if ( ! byte_diff ( data , 4 , " left " ) ) {
if ( ( len = scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_VALUE ) ) < = 0 ) HTTPERROR_400_PARAM ;
2007-01-24 20:48:25 +00:00
if ( scan_fixed_int ( data , len , & tmp ) ) tmp = 0 ;
2007-01-11 01:06:10 +00:00
if ( ! tmp ) OT_FLAG ( & peer ) | = PEER_FLAG_SEEDING ;
} else
scan_urlencoded_query ( & c , NULL , SCAN_SEARCHPATH_VALUE ) ;
break ;
case 5 :
2007-01-31 09:50:46 +00:00
if ( byte_diff ( data , 5 , " event " ) )
2007-01-11 01:06:10 +00:00
scan_urlencoded_query ( & c , NULL , SCAN_SEARCHPATH_VALUE ) ;
else switch ( scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_VALUE ) ) {
case - 1 :
2007-01-26 16:26:49 +00:00
HTTPERROR_400_PARAM ;
2006-12-08 21:00:56 +00:00
case 7 :
2007-01-31 09:50:46 +00:00
if ( ! byte_diff ( data , 7 , " stopped " ) ) OT_FLAG ( & peer ) | = PEER_FLAG_STOPPED ;
2006-12-08 21:36:26 +00:00
break ;
case 9 :
2007-01-31 09:50:46 +00:00
if ( ! byte_diff ( data , 9 , " completed " ) ) OT_FLAG ( & peer ) | = PEER_FLAG_COMPLETED ;
2007-01-11 01:06:10 +00:00
default : /* Fall through intended */
2007-01-08 00:34:37 +00:00
break ;
2007-01-11 01:06:10 +00:00
}
break ;
case 7 :
if ( ! byte_diff ( data , 7 , " numwant " ) ) {
2007-01-31 09:50:46 +00:00
len = scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_VALUE ) ;
2007-01-26 16:26:49 +00:00
if ( ( len < = 0 ) | | scan_fixed_int ( data , len , & numwant ) ) HTTPERROR_400_PARAM ;
2007-01-11 01:06:10 +00:00
if ( numwant > 200 ) numwant = 200 ;
} else if ( ! byte_diff ( data , 7 , " compact " ) ) {
2007-01-31 09:50:46 +00:00
len = scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_VALUE ) ;
2007-01-26 16:26:49 +00:00
if ( ( len < = 0 ) | | scan_fixed_int ( data , len , & tmp ) ) HTTPERROR_400_PARAM ;
if ( ! tmp ) HTTPERROR_400_COMPACT ;
2007-01-11 01:06:10 +00:00
} else
2006-12-09 12:50:42 +00:00
scan_urlencoded_query ( & c , NULL , SCAN_SEARCHPATH_VALUE ) ;
2007-01-11 01:06:10 +00:00
break ;
case 9 :
if ( byte_diff ( data , 9 , " info_hash " ) ) {
scan_urlencoded_query ( & c , NULL , SCAN_SEARCHPATH_VALUE ) ;
continue ;
2006-12-08 20:07:26 +00:00
}
2007-01-11 01:06:10 +00:00
/* ignore this, when we have less than 20 bytes */
2007-01-26 16:26:49 +00:00
if ( scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_VALUE ) ! = 20 ) HTTPERROR_400_PARAM ;
2007-01-11 01:06:10 +00:00
hash = ( ot_hash * ) data ;
break ;
2006-12-08 20:50:06 +00:00
}
2007-01-11 01:06:10 +00:00
}
2006-12-08 21:36:26 +00:00
2007-01-31 09:58:32 +00:00
/* Scanned whole query string */
if ( ! hash ) {
reply_size = sprintf ( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH , " d14:failure reason81:Your client forgot to send your torrent's info_hash. Please upgrade your client.e " ) ;
break ;
}
2007-01-11 01:06:10 +00:00
if ( OT_FLAG ( & peer ) & PEER_FLAG_STOPPED ) {
remove_peer_from_torrent ( hash , & peer ) ;
2007-01-29 02:02:03 +00:00
reply_size = sprintf ( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH , " d8:completei0e10:incompletei0e8:intervali%ie5:peers0:e " , OT_CLIENT_REQUEST_INTERVAL_RANDOM ) ;
2007-01-11 01:06:10 +00:00
} else {
torrent = add_peer_to_torrent ( hash , & peer ) ;
2007-01-31 09:50:46 +00:00
if ( ! torrent | | ! ( reply_size = return_peers_for_torrent ( torrent , numwant , SUCCESS_HTTP_HEADER_LENGTH + static_outbuf ) ) ) HTTPERROR_500 ;
2006-12-08 20:07:26 +00:00
}
2007-01-23 15:48:51 +00:00
ot_overall_successfulannounces + + ;
2007-01-11 01:06:10 +00:00
break ;
2007-01-26 18:09:14 +00:00
case 10 :
2007-01-31 09:50:46 +00:00
if ( byte_diff ( data , 10 , " scrape.php " ) ) HTTPERROR_404 ;
2007-01-26 18:09:14 +00:00
goto SCRAPE_WORKAROUND ;
2007-01-11 01:06:10 +00:00
case 11 :
2007-01-31 09:50:46 +00:00
if ( byte_diff ( data , 11 , " mrtg_scrape " ) ) HTTPERROR_404 ;
2007-01-26 16:26:49 +00:00
t = time ( NULL ) - ot_start_time ;
2007-01-29 02:02:03 +00:00
reply_size = sprintf ( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH ,
2007-01-29 13:41:04 +00:00
" %i \n %i \n %i seconds (%i hours) \n opentracker - Pretuned by german engineers, currently handling %i connections per second. " ,
2007-01-26 16:26:49 +00:00
ot_overall_connections , ot_overall_successfulannounces , ( int ) t , ( int ) ( t / 3600 ) , ( int ) ot_overall_connections / ( ( int ) t ? ( int ) t : 1 ) ) ;
2007-01-11 01:06:10 +00:00
break ;
2007-01-26 18:09:14 +00:00
case 12 :
2007-01-31 09:50:46 +00:00
if ( byte_diff ( data , 12 , " announce.php " ) ) HTTPERROR_404 ;
2007-01-26 18:09:14 +00:00
goto ANNOUNCE_WORKAROUND ;
2007-01-11 01:06:10 +00:00
default : /* neither *scrape nor announce */
2007-01-26 16:26:49 +00:00
HTTPERROR_404 ;
2007-01-11 01:06:10 +00:00
}
2007-01-31 09:50:46 +00:00
if ( ! reply_size ) HTTPERROR_500 ;
2007-01-11 01:06:10 +00:00
2007-01-26 16:26:49 +00:00
/* This one is rather ugly, so I take you step by step through it.
2007-01-11 01:06:10 +00:00
2007-01-26 16:26:49 +00:00
1. In order to avoid having two buffers , one for header and one for content , we allow all above functions from trackerlogic to
write to a fixed location , leaving SUCCESS_HTTP_HEADER_LENGTH bytes in our static buffer , which is enough for the static string
plus dynamic space needed to expand our Content - Length value . We reserve SUCCESS_HTTP_SIZE_OFF for it expansion and calculate
the space NOT needed to expand in reply_off
*/
2007-01-29 02:02:03 +00:00
reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf ( static_outbuf , 0 , " %zd " , reply_size ) ;
2007-01-11 01:06:10 +00:00
2007-01-26 16:26:49 +00:00
/* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete
packet size is increased by size of header plus one byte ' \n ' , we will copy over ' \0 ' in next step */
2007-01-29 02:02:03 +00:00
reply_size + = 1 + sprintf ( static_outbuf + reply_off , " HTTP/1.0 200 OK \r \n Content-Type: text/plain \r \n Content-Length: %zd \r \n \r " , reply_size ) ;
2007-01-11 01:06:10 +00:00
2007-01-26 16:26:49 +00:00
/* 3. Finally we join both blocks neatly */
2007-01-29 02:02:03 +00:00
static_outbuf [ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = ' \n ' ;
2007-01-26 16:26:49 +00:00
2007-01-29 02:02:03 +00:00
senddata ( s , static_outbuf + reply_off , reply_size ) ;
2006-12-05 12:56:56 +00:00
}
2007-01-26 16:26:49 +00:00
static void graceful ( int s ) {
2006-12-08 22:53:32 +00:00
if ( s = = SIGINT ) {
signal ( SIGINT , SIG_IGN ) ;
deinit_logic ( ) ;
exit ( 0 ) ;
}
2006-12-08 22:37:44 +00:00
}
2007-01-26 16:26:49 +00:00
static void usage ( char * name ) {
2007-01-05 12:25:44 +00:00
fprintf ( stderr , " Usage: %s [-i serverip] [-p serverport] [-d serverdirectory] "
# ifdef WANT_CLOSED_TRACKER
2007-01-05 13:00:06 +00:00
" [-oc] "
# endif
# ifdef WANT_BLACKLIST
" [-bB] "
2007-01-05 12:25:44 +00:00
# endif
" \n " , name ) ;
2007-01-05 13:00:06 +00:00
}
2007-01-26 16:26:49 +00:00
static void help ( char * name ) {
2007-01-05 13:00:06 +00:00
usage ( name ) ;
fprintf ( stderr , " \t -i serverip \t specify ip to bind to (default: *) \n "
" \t -p serverport \t specify port to bind to (default: 6969) \n "
" \t -d serverdir \t specify directory containing white- or black listed torrent info_hashes (default: \" . \" ) \n "
# ifdef WANT_CLOSED_TRACKER
" \t -o \t \t make tracker an open tracker, e.g. do not check for white list (default: off) \n "
" \t -c \t \t make tracker a closed tracker, e.g. check each announced torrent against white list (default: on) \n "
# endif
# ifdef WANT_BLACKLIST
" \t -b \t \t make tracker check its black list, e.g. check each announced torrent against black list (default: on) \n "
" \t -B \t \t make tracker check its black list, e.g. check each announced torrent against black list (default: off) \n "
# endif
# ifdef WANT_CLOSED_TRACKER
" \n * To white list a torrent, touch a file inside serverdir with info_hash hex string. \n "
# endif
# ifdef WANT_BLACKLIST
# ifndef WANT_CLOSED_TRACKER
" \n "
# endif
" * To white list a torrent, touch a file inside serverdir with info_hash hex string, preprended by '-'. \n "
# endif
) ;
2007-01-05 12:25:44 +00:00
}
2007-01-26 16:26:49 +00:00
static void handle_read ( const int64 clientsocket ) {
2007-01-18 02:23:18 +00:00
struct http_data * h = io_getcookie ( clientsocket ) ;
2007-01-31 09:50:46 +00:00
ssize_t l ;
2007-01-18 02:23:18 +00:00
2007-01-29 02:02:03 +00:00
if ( ( l = io_tryread ( clientsocket , static_inbuf , sizeof static_inbuf ) ) < = 0 ) {
2007-01-18 02:23:18 +00:00
if ( h ) {
2007-01-24 22:23:18 +00:00
array_reset ( & h - > request ) ;
free ( h ) ;
2007-01-18 02:23:18 +00:00
}
2007-01-24 22:23:18 +00:00
io_close ( clientsocket ) ;
2007-01-18 02:23:18 +00:00
return ;
}
2007-01-24 20:13:30 +00:00
# ifdef _DEBUG_HTTPERROR
memcpy ( debug_request , " 500! \0 " , 5 ) ;
# endif
2007-01-29 02:02:03 +00:00
/* If we get the whole request in one packet, handle it without copying */
if ( ! array_start ( & h - > request ) ) {
if ( memchr ( static_inbuf , ' \n ' , l ) )
return httpresponse ( clientsocket , static_inbuf ) ;
return array_catb ( & h - > request , static_inbuf , l ) ;
}
array_catb ( & h - > request , static_inbuf , l ) ;
2007-01-24 22:23:18 +00:00
if ( array_failed ( & h - > request ) )
2007-01-29 02:02:03 +00:00
httperror ( clientsocket , " 500 Server Error " , " Request too long. " ) ;
2007-01-24 22:23:18 +00:00
else if ( array_bytes ( & h - > request ) > 8192 )
2007-01-29 02:02:03 +00:00
httperror ( clientsocket , " 500 request too long " , " You sent too much headers " ) ;
else if ( memchr ( array_start ( & h - > request ) , ' \n ' , array_length ( & h - > request , 1 ) ) )
httpresponse ( clientsocket , array_start ( & h - > request ) ) ;
2007-01-18 02:23:18 +00:00
}
2007-01-26 16:26:49 +00:00
static void handle_write ( const int64 clientsocket ) {
2007-01-24 22:23:18 +00:00
struct http_data * h = io_getcookie ( clientsocket ) ;
2007-01-29 02:02:03 +00:00
if ( ! h | | ( iob_send ( clientsocket , & h - > batch ) < = 0 ) ) {
2007-01-20 01:50:28 +00:00
iob_reset ( & h - > batch ) ;
io_close ( clientsocket ) ;
free ( h ) ;
}
}
2007-01-26 16:26:49 +00:00
static void handle_accept ( const int64 serversocket ) {
struct http_data * h ;
2007-01-18 02:23:18 +00:00
unsigned char ip [ 4 ] ;
uint16 port ;
2007-01-18 12:27:17 +00:00
tai6464 t ;
int64 i ;
while ( ( i = socket_accept4 ( serversocket , ( char * ) ip , & port ) ) ! = - 1 ) {
if ( ! io_fd ( i ) | |
2007-01-25 14:16:26 +00:00
! ( h = ( struct http_data * ) malloc ( sizeof ( struct http_data ) ) ) ) {
2007-01-18 12:27:17 +00:00
io_close ( i ) ;
continue ;
}
io_wantread ( i ) ;
byte_zero ( h , sizeof ( struct http_data ) ) ;
memmove ( h - > ip , ip , sizeof ( ip ) ) ;
io_setcookie ( i , h ) ;
+ + ot_overall_connections ;
taia_now ( & t ) ;
taia_addsec ( & t , & t , OT_CLIENT_TIMEOUT ) ;
io_timeout ( i , t ) ;
}
if ( errno = = EAGAIN )
io_eagain ( serversocket ) ;
}
2007-01-26 16:26:49 +00:00
static void handle_timeouted ( void ) {
2007-01-18 12:27:17 +00:00
int64 i ;
while ( ( i = io_timeouted ( ) ) ! = - 1 ) {
2007-01-24 22:23:18 +00:00
struct http_data * h = io_getcookie ( i ) ;
2007-01-18 12:27:17 +00:00
if ( h ) {
2007-01-24 22:23:18 +00:00
array_reset ( & h - > request ) ;
2007-01-18 12:27:17 +00:00
free ( h ) ;
}
io_close ( i ) ;
}
}
2007-01-26 16:26:49 +00:00
static void server_mainloop ( const int64 serversocket ) {
2007-01-18 12:27:17 +00:00
tai6464 t , next_timeout_check ;
2007-01-18 02:23:18 +00:00
io_wantread ( serversocket ) ;
taia_now ( & next_timeout_check ) ;
2007-01-24 22:23:18 +00:00
for ( ; ; ) {
2007-01-18 02:23:18 +00:00
int64 i ;
2007-01-18 02:40:18 +00:00
2007-01-24 22:23:18 +00:00
taia_now ( & t ) ;
taia_addsec ( & t , & t , OT_CLIENT_TIMEOUT_CHECKINTERVAL ) ;
io_waituntil ( t ) ;
2007-01-18 02:23:18 +00:00
2007-01-24 22:23:18 +00:00
while ( ( i = io_canread ( ) ) ! = - 1 ) {
2007-01-18 12:27:17 +00:00
if ( i = = serversocket )
handle_accept ( i ) ;
else
handle_read ( i ) ;
}
2007-01-24 22:23:18 +00:00
while ( ( i = io_canwrite ( ) ) ! = - 1 )
2007-01-20 01:50:28 +00:00
handle_write ( i ) ;
2007-01-24 22:23:18 +00:00
taia_now ( & t ) ;
2007-01-18 02:23:18 +00:00
if ( taia_less ( & next_timeout_check , & t ) ) {
2007-01-18 12:27:17 +00:00
handle_timeouted ( ) ;
2007-01-24 22:23:18 +00:00
taia_now ( & next_timeout_check ) ;
taia_addsec ( & next_timeout_check , & next_timeout_check , OT_CLIENT_TIMEOUT_CHECKINTERVAL ) ;
2007-01-18 02:23:18 +00:00
}
}
}
int main ( int argc , char * * argv ) {
int64 s = socket_tcp4 ( ) ;
2007-01-08 00:57:35 +00:00
char * serverip = NULL ;
char * serverdir = " . " ;
uint16 port = 6969 ;
2007-01-18 02:23:18 +00:00
int scanon = 1 ;
2007-01-08 00:57:35 +00:00
2007-01-18 02:23:18 +00:00
while ( scanon ) {
2007-01-24 22:23:18 +00:00
switch ( getopt ( argc , argv , " :i:p:d:ocbBh " ) ) {
2007-01-18 02:23:18 +00:00
case - 1 : scanon = 0 ; break ;
2007-01-08 00:57:35 +00:00
case ' i ' : serverip = optarg ; break ;
case ' p ' : port = ( uint16 ) atol ( optarg ) ; break ;
case ' d ' : serverdir = optarg ; break ;
2007-01-24 22:23:18 +00:00
case ' h ' : help ( argv [ 0 ] ) ; exit ( 0 ) ;
2007-01-05 12:25:44 +00:00
# ifdef WANT_CLOSED_TRACKER
2007-01-08 00:57:35 +00:00
case ' o ' : g_closedtracker = 0 ; break ;
case ' c ' : g_closedtracker = 1 ; break ;
2007-01-05 13:00:06 +00:00
# endif
# ifdef WANT_BLACKLIST
2007-01-08 00:57:35 +00:00
case ' b ' : g_check_blacklist = 1 ; break ;
case ' B ' : g_check_blacklist = 0 ; break ;
2007-01-05 12:25:44 +00:00
# endif
2007-01-08 00:57:35 +00:00
default :
2007-01-24 22:23:18 +00:00
case ' ? ' : usage ( argv [ 0 ] ) ; exit ( 1 ) ;
2007-01-05 12:25:44 +00:00
}
2007-01-08 00:57:35 +00:00
}
2006-12-08 20:07:26 +00:00
2007-01-24 22:23:18 +00:00
if ( socket_bind4_reuse ( s , serverip , port ) = = - 1 )
panic ( " socket_bind4_reuse " ) ;
2007-01-08 00:57:35 +00:00
2007-01-24 22:23:18 +00:00
setegid ( ( gid_t ) - 2 ) ; setuid ( ( uid_t ) - 2 ) ;
setgid ( ( gid_t ) - 2 ) ; seteuid ( ( uid_t ) - 2 ) ;
2007-01-14 20:15:04 +00:00
2007-01-24 22:23:18 +00:00
if ( socket_listen ( s , SOMAXCONN ) = = - 1 )
panic ( " socket_listen " ) ;
2007-01-08 00:57:35 +00:00
2007-01-24 22:23:18 +00:00
if ( ! io_fd ( s ) )
panic ( " io_fd " ) ;
2007-01-08 00:57:35 +00:00
2007-01-10 16:42:39 +00:00
signal ( SIGPIPE , SIG_IGN ) ;
2007-01-19 17:50:36 +00:00
signal ( SIGINT , graceful ) ;
2007-01-08 00:57:35 +00:00
if ( init_logic ( serverdir ) = = - 1 )
2007-01-24 22:23:18 +00:00
panic ( " Logic not started " ) ;
2007-01-08 00:57:35 +00:00
2007-01-14 20:15:04 +00:00
ot_start_time = time ( NULL ) ;
2007-01-24 22:23:18 +00:00
server_mainloop ( s ) ;
2007-01-08 00:57:35 +00:00
return 0 ;
2006-12-05 12:56:56 +00:00
}