@ -27,26 +27,19 @@
# include "ot_accesslist.h"
# define OT_MAXMULTISCRAPE_COUNT 64
static ot_hash multiscrape_buf [ OT_MAXMULTISCRAPE_COUNT ] ;
extern char * g_redirecturl ;
enum {
SUCCESS_HTTP_HEADER_LENGTH = 80 ,
SUCCESS_HTTP_HEADER_LENG H T_CONTENT_ENCODING = 32 ,
SUCCESS_HTTP_HEADER_LENG TH _CONTENT_ENCODING = 32 ,
SUCCESS_HTTP_SIZE_OFF = 17 } ;
/* Our static output buffer */
static char static_outbuf [ 8192 ] ;
# ifdef _DEBUG_HTTPERROR
static char debug_request [ 8192 ] ;
# endif
# ifdef _DEBUG_PEERID
size_t g_this_peerid_len = 0 ;
char * g_this_peerid_data = NULL ;
# endif
static void http_senddata ( const int64 client_socket , char * buffer , size_t size ) {
static void http_senddata ( const int64 client_socket , struct ot_workstruct * ws ) {
struct http_data * h = io_getcookie ( client_socket ) ;
ssize_t written_size ;
@ -56,22 +49,22 @@ static void http_senddata( const int64 client_socket, char *buffer, size_t size
array_reset ( & h - > data . request ) ;
}
written_size = write ( client_socket , buffer, size ) ;
if ( ( written_size < 0 ) | | ( ( size_t ) written_size = = size ) ) {
written_size = write ( client_socket , ws- > reply , ws - > reply_ size ) ;
if ( ( written_size < 0 ) | | ( written_size = = ws- > reply_ size ) ) {
free ( h ) ; io_close ( client_socket ) ;
} else {
char * outbuf ;
tai6464 t ;
if ( ! h ) return ;
if ( ! ( outbuf = malloc ( size - written_size ) ) ) {
if ( ! ( outbuf = malloc ( ws- > reply_ size - written_size ) ) ) {
free ( h ) ; io_close ( client_socket ) ;
return ;
}
iob_reset ( & h - > data . batch ) ;
memmove ( outbuf , buffer + written_size , size - written_size ) ;
iob_addbuf_free ( & h - > data . batch , outbuf , size - written_size ) ;
memmove ( outbuf , ws- > reply + written_size , ws - > reply_ size - written_size ) ;
iob_addbuf_free ( & h - > data . batch , outbuf , ws- > reply_ size - written_size ) ;
h - > flag | = STRUCT_HTTP_FLAG_IOB_USED ;
/* 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
}
}
# define HTTPERROR_302 return http_issue_error( client_socket, CODE_HTTPERROR_302 )
# define HTTPERROR_400 return http_issue_error( client_socket, CODE_HTTPERROR_400 )
# define HTTPERROR_400_PARAM return http_issue_error( client_socket, CODE_HTTPERROR_400_PARAM )
# define HTTPERROR_400_COMPACT return http_issue_error( client_socket, CODE_HTTPERROR_400_COMPACT )
# define HTTPERROR_403_IP return http_issue_error( client_socket, CODE_HTTPERROR_403_IP )
# define HTTPERROR_404 return http_issue_error( client_socket, CODE_HTTPERROR_404 )
# define HTTPERROR_500 return http_issue_error( client_socket, CODE_HTTPERROR_500 )
ssize_t http_issue_error ( const int64 client_socket , int code ) {
# define HTTPERROR_302 return http_issue_error( client_socket, ws, CODE_HTTPERROR_302 )
# define HTTPERROR_400 return http_issue_error( client_socket, ws, CODE_HTTPERROR_400 )
# define HTTPERROR_400_PARAM return http_issue_error( client_socket, ws, CODE_HTTPERROR_400_PARAM )
# define HTTPERROR_400_COMPACT return http_issue_error( client_socket, ws, CODE_HTTPERROR_400_COMPACT )
# define HTTPERROR_400_DOUBLEHASH return http_issue_error( client_socket, ws, CODE_HTTPERROR_400_PARAM )
# define HTTPERROR_403_IP return http_issue_error( client_socket, ws, CODE_HTTPERROR_403_IP )
# define HTTPERROR_404 return http_issue_error( client_socket, ws, CODE_HTTPERROR_404 )
# define HTTPERROR_500 return http_issue_error( client_socket, ws, CODE_HTTPERROR_500 )
ssize_t http_issue_error ( const int64 client_socket , struct ot_workstruct * ws , int code ) {
char * error_code [ ] = { " 302 Found " , " 400 Invalid Request " , " 400 Invalid Request " , " 400 Invalid Request " ,
" 403 Access Denied " , " 404 Not Found " , " 500 Internal Server Error " } ;
char * title = error_code [ code ] ;
size_t reply_size ;
char * title = error_code [ code ] ;
ws - > reply = ws - > outbuf ;
if ( code = = CODE_HTTPERROR_302 )
reply_size = sprintf ( static_outbuf , " HTTP/1.0 302 Found \r \n Content-Length: 0 \r \n Location: %s \r \n \r \n " , g_redirecturl ) ;
ws- > reply_size = snprintf ( ws - > reply , ws - > outbuf_size , " HTTP/1.0 302 Found \r \n Content-Length: 0 \r \n Location: %s \r \n \r \n " , g_redirecturl ) ;
else
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 " , title , strlen ( title ) + 16 - 4 , title + 4 ) ;
ws- > reply_size = snprintf ( ws - > reply , ws - > outbuf_size , " 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 " , title , strlen ( title ) + 16 - 4 , title + 4 ) ;
# ifdef _DEBUG_HTTPERROR
fprintf ( stderr , " DEBUG: invalid request was: %s \n " , debug_request ) ;
fprintf ( stderr , " DEBUG: invalid request was: %s \n " , ws- > debugbuf ) ;
# endif
stats_issue_event ( EVENT_FAILED , FLAG_TCP , code ) ;
http_senddata ( client_socket , static_outbuf, reply_size ) ;
return - 2 ;
http_senddata ( client_socket , w s ) ;
return ws - > reply_size = - 2 ;
}
ssize_t http_sendiovecdata ( const int64 client_socket , int iovec_entries , struct iovec * iovector ) {
ssize_t http_sendiovecdata ( const int64 client_socket , struct ot_workstruct * ws , int iovec_entries , struct iovec * iovector ) {
struct http_data * h = io_getcookie ( client_socket ) ;
char * header ;
int i ;
@ -136,7 +130,7 @@ ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct
}
/* Prepare space for http header */
header = malloc ( SUCCESS_HTTP_HEADER_LENGTH + SUCCESS_HTTP_HEADER_LENG H T_CONTENT_ENCODING ) ;
header = malloc ( SUCCESS_HTTP_HEADER_LENGTH + SUCCESS_HTTP_HEADER_LENG TH _CONTENT_ENCODING ) ;
if ( ! header ) {
iovec_free ( & iovec_entries , & iovector ) ;
HTTPERROR_500 ;
@ -159,7 +153,7 @@ ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct
h - > flag | = STRUCT_HTTP_FLAG_IOB_USED ;
/* writeable sockets timeout after 10 minutes ) */
/* writeable sockets timeout after 10 minutes */
taia_now ( & t ) ; taia_addsec ( & t , & t , OT_CLIENT_TIMEOUT_SEND ) ;
io_timeout ( client_socket , t ) ;
io_dontwantread ( client_socket ) ;
@ -167,9 +161,21 @@ ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct
return 0 ;
}
static ssize_t http_handle_stats ( const int64 client_socket , char * data , char * d , size_t l ) {
char * c = data ;
static ssize_t http_handle_stats ( const int64 client_socket , struct ot_workstruct * ws , char * read_ptr ) {
static const ot_keywords keywords_main [ ] =
{ { " mode " , 1 } , { " format " , 2 } , { NULL , - 3 } } ;
static const ot_keywords keywords_mode [ ] =
{ { " peer " , TASK_STATS_PEERS } , { " conn " , TASK_STATS_CONNS } , { " scrp " , TASK_STATS_SCRAPE } , { " udp4 " , TASK_STATS_UDP } ,
{ " busy " , TASK_STATS_BUSY_NETWORKS } , { " torr " , TASK_STATS_TORRENTS } , { " fscr " , TASK_STATS_FULLSCRAPE } ,
{ " s24s " , TASK_STATS_SLASH24S } , { " tpbs " , TASK_STATS_TPB } , { " herr " , TASK_STATS_HTTPERRORS } ,
{ " top10 " , TASK_STATS_TOP10 } , { " renew " , TASK_STATS_RENEW } , { " syncs " , TASK_STATS_SYNCS } , { " version " , TASK_STATS_VERSION } ,
{ " startstop " , TASK_STATS_STARTSTOP } , { " toraddrem " , TASK_STATS_TORADDREM } , { NULL , - 3 } } ;
static const ot_keywords keywords_format [ ] =
{ { " bin " , TASK_FULLSCRAPE_TPB_BINARY } , { " ben " , TASK_FULLSCRAPE } , { " url " , TASK_FULLSCRAPE_TPB_URLENCODED } ,
{ " txt " , TASK_FULLSCRAPE_TPB_ASCII } , { NULL , - 3 } } ;
int mode = TASK_STATS_PEERS , scanon = 1 , format = 0 ;
# ifdef WANT_RESTRICT_STATS
struct http_data * h = io_getcookie ( client_socket ) ;
@ -178,97 +184,26 @@ static ssize_t http_handle_stats( const int64 client_socket, char *data, char *d
# endif
while ( scanon ) {
switch ( scan_ urlencoded_query( & c , data = c , SCAN_SEARCHPATH_PARAM ) ) {
switch ( scan_ find_keywords( keywords_main , & read_ptr , SCAN_SEARCHPATH_PARAM ) ) {
case - 2 : scanon = 0 ; break ; /* TERMINATOR */
case - 1 : HTTPERROR_400_PARAM ; /* PARSE ERROR */
default : scan_urlencoded_skipvalue ( & c ) ; break ;
case 4 :
if ( byte_diff ( data , 4 , " mode " ) ) {
scan_urlencoded_skipvalue ( & c ) ;
continue ;
}
switch ( scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_VALUE ) ) {
case 4 :
if ( ! byte_diff ( data , 4 , " peer " ) )
mode = TASK_STATS_PEERS ;
else if ( ! byte_diff ( data , 4 , " conn " ) )
mode = TASK_STATS_CONNS ;
else if ( ! byte_diff ( data , 4 , " scrp " ) )
mode = TASK_STATS_SCRAPE ;
else if ( ! byte_diff ( data , 4 , " tcp4 " ) )
mode = TASK_STATS_TCP ;
else if ( ! byte_diff ( data , 4 , " udp4 " ) )
mode = TASK_STATS_UDP ;
else if ( ! byte_diff ( data , 4 , " busy " ) )
mode = TASK_STATS_BUSY_NETWORKS ;
else if ( ! byte_diff ( data , 4 , " torr " ) )
mode = TASK_STATS_TORRENTS ;
else if ( ! byte_diff ( data , 4 , " fscr " ) )
mode = TASK_STATS_FULLSCRAPE ;
else if ( ! byte_diff ( data , 4 , " s24s " ) )
mode = TASK_STATS_SLASH24S ;
else if ( ! byte_diff ( data , 4 , " tpbs " ) )
mode = TASK_STATS_TPB ;
else if ( ! byte_diff ( data , 4 , " herr " ) )
mode = TASK_STATS_HTTPERRORS ;
else
HTTPERROR_400_PARAM ;
break ;
case 5 :
if ( ! byte_diff ( data , 5 , " top10 " ) )
mode = TASK_STATS_TOP10 ;
else if ( ! byte_diff ( data , 5 , " renew " ) )
mode = TASK_STATS_RENEW ;
else if ( ! byte_diff ( data , 5 , " syncs " ) )
mode = TASK_STATS_SYNCS ;
else
HTTPERROR_400_PARAM ;
break ;
case 7 :
if ( ! byte_diff ( data , 7 , " version " ) )
mode = TASK_STATS_VERSION ;
else
HTTPERROR_400_PARAM ;
break ;
case 9 :
if ( ! byte_diff ( data , 9 , " startstop " ) )
mode = TASK_STATS_STARTSTOP ;
else if ( ! byte_diff ( data , 9 , " toraddrem " ) )
mode = TASK_STATS_TORADDREM ;
else
HTTPERROR_400_PARAM ;
break ;
}
case - 3 : scan_urlencoded_skipvalue ( & read_ptr ) ; break ;
case 1 : /* matched "mode" */
if ( ( mode = scan_find_keywords ( keywords_mode , & read_ptr , SCAN_SEARCHPATH_VALUE ) ) < = 0 ) HTTPERROR_400_PARAM ;
break ;
case 6 :
if ( byte_diff ( data , 6 , " format " ) ) {
scan_urlencoded_skipvalue ( & c ) ;
continue ;
}
if ( scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_VALUE ) ! = 3 ) HTTPERROR_400_PARAM ;
if ( ! byte_diff ( data , 3 , " bin " ) )
format = TASK_FULLSCRAPE_TPB_BINARY ;
else if ( ! byte_diff ( data , 3 , " ben " ) )
format = TASK_FULLSCRAPE ;
else if ( ! byte_diff ( data , 3 , " url " ) )
format = TASK_FULLSCRAPE_TPB_URLENCODED ;
else if ( ! byte_diff ( data , 3 , " txt " ) )
format = TASK_FULLSCRAPE_TPB_ASCII ;
else
HTTPERROR_400_PARAM ;
case 2 : /* matched "format" */
if ( ( format = scan_find_keywords ( keywords_format , & read_ptr , SCAN_SEARCHPATH_VALUE ) ) < = 0 ) HTTPERROR_400_PARAM ;
break ;
}
}
/* Touch variable */
d = d ;
# ifdef WANT_FULLSCRAPE
if ( mode = = TASK_STATS_TPB ) {
struct http_data * h = io_getcookie ( client_socket ) ;
tai6464 t ;
# ifdef WANT_COMPRESSION_GZIP
d[ l - 1 ] = 0 ;
if ( strstr ( d, " gzip " ) ) {
ws - > request [ ws - > request_size ] = 0 ;
if ( strstr ( read_ptr - 1 , " gzip " ) ) {
h - > flag | = STRUCT_HTTP_FLAG_GZIP ;
format | = TASK_FLAG_GZIP ;
}
@ -280,7 +215,7 @@ static ssize_t http_handle_stats( const int64 client_socket, char *data, char *d
taia_uint ( & t , 0 ) ; io_timeout ( client_socket , t ) ;
fullscrape_deliver ( client_socket , format ) ;
io_dontwantread ( client_socket ) ;
return - 2 ;
return ws - > reply_size = - 2 ;
}
# endif
@ -290,27 +225,24 @@ static ssize_t http_handle_stats( const int64 client_socket, char *data, char *d
/* Complex stats also include expensive memory debugging tools */
taia_uint ( & t , 0 ) ; io_timeout ( client_socket , t ) ;
stats_deliver ( client_socket , mode ) ;
return - 2 ;
return ws - > reply_size = - 2 ;
}
/* Simple stats can be answerred immediately */
if ( ! ( l = return_stats_for_tracker ( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH , mode , 0 ) ) ) HTTPERROR_500 ;
if ( ! ( ws- > rep ly_size = return_stats_for_tracker ( ws- > reply , mode , 0 ) ) ) HTTPERROR_500 ;
return l;
return ws- > rep ly_size ;
}
# ifdef WANT_FULLSCRAPE
static ssize_t http_handle_fullscrape ( const int64 client_socket , char * d , size_t l ) {
static ssize_t http_handle_fullscrape ( const int64 client_socket , struct ot_workstruct * ws ) {
struct http_data * h = io_getcookie ( client_socket ) ;
int format = 0 ;
tai6464 t ;
/* Touch variables */
d = d ; l = l ;
# ifdef WANT_COMPRESSION_GZIP
d[ l - 1 ] = 0 ;
if ( strstr ( d , " gzip " ) ) {
ws - > request [ ws - > request_size - 1 ] = 0 ;
if ( strstr ( ws - > request , " gzip " ) ) {
h - > flag | = STRUCT_HTTP_FLAG_GZIP ;
format = TASK_FLAG_GZIP ;
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_
stats_issue_event ( EVENT_FULLSCRAPE_REQUEST , * ( int * ) h - > ip , 0 ) ;
# ifdef _DEBUG_HTTPERROR
write ( 2 , debug_request, l ) ;
write ( 2 , ws- > debugbuf , ws - > debugbuf_size ) ;
# endif
/* Pass this task to the worker thread */
@ -328,72 +260,70 @@ write( 2, debug_request, l );
taia_uint ( & t , 0 ) ; io_timeout ( client_socket , t ) ;
fullscrape_deliver ( client_socket , TASK_FULLSCRAPE | format ) ;
io_dontwantread ( client_socket ) ;
return - 2 ;
return ws - > reply_size = - 2 ;
}
# endif
static ssize_t http_handle_scrape ( const int64 client_socket , struct ot_workstruct * ws , char * read_ptr ) {
static const ot_keywords keywords_scrape [ ] = { { " info_hash " , 1 } , { NULL , - 3 } } ;
static ssize_t http_handle_scrape ( const int64 client_socket , char * data ) {
ot_hash * multiscrape_buf = ( ot_hash * ) ws - > request ;
int scanon = 1 , numwant = 0 ;
char * c = data ;
size_t l ;
/* This is to hack around stupid clients that send "scrape ?info_hash" */
if ( c [ - 1 ] ! = ' ? ' ) {
while ( ( * c ! = ' ? ' ) & & ( * c ! = ' \n ' ) ) + + c ;
if ( * c = = ' \n ' ) HTTPERROR_400_PARAM ;
+ + c ;
if ( read_ptr [ - 1 ] ! = ' ? ' ) {
while ( ( * read_ptr ! = ' ? ' ) & & ( * read_ptr ! = ' \n ' ) ) + + read_ptr ;
if ( * read_ptr = = ' \n ' ) HTTPERROR_400_PARAM ;
+ + read_ptr ;
}
while ( scanon ) {
switch ( scan_ urlencoded_query( & c , data = c , SCAN_SEARCHPATH_PARAM ) ) {
switch ( scan_ find_keywords( keywords_scrape , & read_ptr , SCAN_SEARCHPATH_PARAM ) ) {
case - 2 : scanon = 0 ; break ; /* TERMINATOR */
case - 1 :
if ( numwant )
goto UTORRENT1600_WORKAROUND ;
HTTPERROR_400_PARAM ; /* PARSE ERROR */
default : scan_urlencoded_skipvalue ( & c ) ; break ;
case 9 :
if ( byte_diff ( data , 9 , " info_hash " ) ) {
scan_urlencoded_skipvalue ( & c ) ;
continue ;
}
default : HTTPERROR_400_PARAM ; /* PARSE ERROR */
case - 3 : scan_urlencoded_skipvalue ( & read_ptr ) ; break ;
case 1 : /* matched "info_hash" */
/* ignore this, when we have less than 20 bytes */
if ( scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_VALUE ) ! = ( ssize_t ) sizeof ( ot_hash ) ) {
# ifdef WANT_UTORRENT1600_WORKAROUND
if ( data [ 20 ] ! = ' ? ' )
# endif
if ( scan_urlencoded_query ( & read_ptr , ( char * ) ( multiscrape_buf + numwant + + ) , SCAN_SEARCHPATH_VALUE ) ! = ( ssize_t ) sizeof ( ot_hash ) )
HTTPERROR_400_PARAM ;
}
if ( numwant < OT_MAXMULTISCRAPE_COUNT )
memmove ( multiscrape_buf + numwant + + , data , sizeof ( ot_hash ) ) ;
break ;
}
}
UTORRENT1600_WORKAROUND :
/* No info_hash found? Inform user */
if ( ! numwant ) HTTPERROR_400_PARAM ;
/* Limit number of hashes to process */
if ( numwant > OT_MAXMULTISCRAPE_COUNT )
numwant = OT_MAXMULTISCRAPE_COUNT ;
/* Enough for http header + whole scrape string */
if ( ! ( l = return_tcp_scrape_for_torrent ( multiscrape_buf , numwant , SUCCESS_HTTP_HEADER_LENGTH + static_outbuf ) ) ) HTTPERROR_500 ;
stats_issue_event ( EVENT_SCRAPE , FLAG_TCP , l ) ;
return l ;
if ( ! ( ws- > rep ly_size = return_tcp_scrape_for_torrent ( multiscrape_buf , numwant , ws- > reply ) ) ) HTTPERROR_500 ;
stats_issue_event ( EVENT_SCRAPE , FLAG_TCP , ws- > rep ly_size ) ;
return ws- > rep ly_size ;
}
static ssize_t http_handle_announce ( const int64 client_socket , char * data ) {
char * c = data ;
int numwant , tmp , scanon ;
ot_peer peer ;
ot_hash * hash = NULL ;
static ot_keywords keywords_announce [ ] = { { " port " , 1 } , { " left " , 2 } , { " event " , 3 } , { " numwant " , 4 } , { " compact " , 5 } , { " info_hash " , 6 } ,
# ifdef WANT_IP_FROM_QUERY_STRING
{ " ip " , 7 } ,
# endif
# ifdef _DEBUG_PEERID
{ " peer_id " , 8 } ,
# endif
{ NULL , - 3 } } ;
static ot_keywords keywords_announce_event [ ] = { { " completed " , 1 } , { " stopped " , 2 } , { NULL , - 3 } } ;
static ssize_t http_handle_announce ( const int64 client_socket , struct ot_workstruct * ws , char * read_ptr ) {
int numwant , tmp , scanon ;
ot_peer peer ;
ot_hash * hash = NULL ;
unsigned short port = htons ( 6881 ) ;
ssize_t len ;
char * write_ptr ;
ssize_t len ;
/* This is to hack around stupid clients that send "announce ?info_hash" */
if ( c [ - 1 ] ! = ' ? ' ) {
while ( ( * c ! = ' ? ' ) & & ( * c ! = ' \n ' ) ) + + c ;
if ( * c = = ' \n ' ) HTTPERROR_400_PARAM ;
+ + c ;
if ( read_ptr [ - 1 ] ! = ' ? ' ) {
while ( ( * read_ptr ! = ' ? ' ) & & ( * read_ptr ! = ' \n ' ) ) + + read_ptr ;
if ( * read_ptr = = ' \n ' ) HTTPERROR_400_PARAM ;
+ + read_ptr ;
}
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 ) {
scanon = 1 ;
# ifdef _DEBUG_PEERID
g_this_peerid_data = NULL ;
ws- > peer_id = NULL ;
# endif
while ( scanon ) {
switch ( scan_ urlencoded_query( & c , data = c , SCAN_SEARCHPATH_PARAM ) ) {
switch ( scan_ find_keywords( keywords_announce , & read_ptr , SCAN_SEARCHPATH_PARAM ) ) {
case - 2 : scanon = 0 ; break ; /* TERMINATOR */
case - 1 : HTTPERROR_400_PARAM ; /* PARSE ERROR */
default : scan_urlencoded_skipvalue ( & c ) ; break ;
# ifdef WANT_IP_FROM_QUERY_STRING
case 2 :
if ( ! byte_diff ( data , 2 , " ip " ) ) {
unsigned char ip [ 4 ] ;
len = scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_VALUE ) ;
if ( ( len < = 0 ) | | scan_fixed_ip ( data , len , ip ) ) HTTPERROR_400_PARAM ;
OT_SETIP ( & peer , ip ) ;
} else
scan_urlencoded_skipvalue ( & c ) ;
break ;
# endif
case 4 :
if ( ! byte_diff ( data , 4 , " port " ) ) {
len = scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_VALUE ) ;
if ( ( len < = 0 ) | | scan_fixed_int ( data , len , & tmp ) | | ( tmp > 0xffff ) ) HTTPERROR_400_PARAM ;
port = htons ( tmp ) ; OT_SETPORT ( & peer , & port ) ;
} else if ( ! byte_diff ( data , 4 , " left " ) ) {
if ( ( len = scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_VALUE ) ) < = 0 ) HTTPERROR_400_PARAM ;
if ( scan_fixed_int ( data , len , & tmp ) ) tmp = 0 ;
if ( ! tmp ) OT_PEERFLAG ( & peer ) | = PEER_FLAG_SEEDING ;
} else
scan_urlencoded_skipvalue ( & c ) ;
case - 3 : scan_urlencoded_skipvalue ( & read_ptr ) ; break ;
case 1 : /* matched "port" */
len = scan_urlencoded_query ( & read_ptr , write_ptr = read_ptr , SCAN_SEARCHPATH_VALUE ) ;
if ( ( len < = 0 ) | | scan_fixed_int ( write_ptr , len , & tmp ) | | ( tmp > 0xffff ) ) HTTPERROR_400_PARAM ;
port = htons ( tmp ) ; OT_SETPORT ( & peer , & port ) ;
break ;
case 5 :
if ( byte_diff ( data , 5 , " event " ) )
scan_urlencoded_skipvalue ( & c ) ;
else switch ( scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_VALUE ) ) {
case - 1 :
HTTPERROR_400_PARAM ;
case 7 :
if ( ! byte_diff ( data , 7 , " stopped " ) ) OT_PEERFLAG ( & peer ) | = PEER_FLAG_STOPPED ;
break ;
case 9 :
if ( ! byte_diff ( data , 9 , " completed " ) ) OT_PEERFLAG ( & peer ) | = PEER_FLAG_COMPLETED ;
default : /* Fall through intended */
break ;
case 2 : /* matched "left" */
if ( ( len = scan_urlencoded_query ( & read_ptr , write_ptr = read_ptr , SCAN_SEARCHPATH_VALUE ) ) < = 0 ) HTTPERROR_400_PARAM ;
if ( scan_fixed_int ( write_ptr , len , & tmp ) ) tmp = 0 ;
if ( ! tmp ) OT_PEERFLAG ( & peer ) | = PEER_FLAG_SEEDING ;
break ;
case 3 : /* matched "event" */
switch ( scan_find_keywords ( keywords_announce_event , & read_ptr , SCAN_SEARCHPATH_VALUE ) ) {
case - 1 : HTTPERROR_400_PARAM ;
case 1 : /* matched "completed" */
OT_PEERFLAG ( & peer ) | = PEER_FLAG_COMPLETED ;
break ;
case 2 : /* matched "stopped" */
OT_PEERFLAG ( & peer ) | = PEER_FLAG_STOPPED ;
break ;
default :
break ;
}
break ;
case 7 :
if ( ! byte_diff ( data , 7 , " numwant " ) ) {
len = scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_VALUE ) ;
if ( ( len < = 0 ) | | scan_fixed_int ( data , len , & numwant ) ) HTTPERROR_400_PARAM ;
if ( numwant < 0 ) numwant = 50 ;
if ( numwant > 200 ) numwant = 200 ;
} else if ( ! byte_diff ( data , 7 , " compact " ) ) {
len = scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_VALUE ) ;
if ( ( len < = 0 ) | | scan_fixed_int ( data , len , & tmp ) ) HTTPERROR_400_PARAM ;
if ( ! tmp ) HTTPERROR_400_COMPACT ;
} else
# ifdef _DEBUG_PEERID
if ( ! byte_diff ( data , 7 , " peer_id " ) ) {
g_this_peerid_len = scan_urlencoded_query ( & c , data = c , SCAN_SEARCHPATH_VALUE ) ;
g_this_peerid_data = g_this_peerid_len > 0 ? data : 0 ;
} else
# endif
scan_urlencoded_skipvalue ( & c ) ;
case 4 : /* matched "numwant" */
len = scan_urlencoded_query ( & read_ptr , write_ptr = read_ptr , SCAN_SEARCHPATH_VALUE ) ;
if ( ( len < = 0 ) | | scan_fixed_int ( write_ptr , len , & numwant ) ) HTTPERROR_400_PARAM ;
if ( numwant < 0 ) numwant = 50 ;
if ( numwant > 200 ) numwant = 200 ;
break ;
case 9 :
if ( byte_diff ( data , 9 , " info_hash " ) ) {
scan_urlencoded_skipvalue ( & c ) ;
continue ;
}
case 5 : /* matched "compact" */
len = scan_urlencoded_query ( & read_ptr , write_ptr = read_ptr , SCAN_SEARCHPATH_VALUE ) ;
if ( ( len < = 0 ) | | scan_fixed_int ( write_ptr , len , & tmp ) ) HTTPERROR_400_PARAM ;
if ( ! tmp ) HTTPERROR_400_COMPACT ;
break ;
case 6 : /* matched "info_hash" */
if ( hash ) HTTPERROR_400_DOUBLEHASH ;
/* 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 ;
if ( scan_urlencoded_query ( & read_ptr , write_ptr = read_ptr , SCAN_SEARCHPATH_VALUE ) ! = 20 ) HTTPERROR_400_PARAM ;
hash = ( ot_hash * ) write_ptr ;
break ;
# ifdef WANT_IP_FROM_QUERY_STRING
case 7 : /* matched "ip" */
len = scan_urlencoded_query ( & read_ptr , write_ptr = read_ptr , SCAN_SEARCHPATH_VALUE ) ;
if ( ( len < = 0 ) | | scan_fixed_ip ( write_ptr , len , ( unsigned char * ) /*tmp*/ ws - > reply ) ) HTTPERROR_400_PARAM ;
OT_SETIP ( & peer , /*tmp*/ ws - > reply ) ;
break ;
# endif
# ifdef _DEBUG_PEERID
case 8 : /* matched "peer_id" */
ws - > peer_id_size = scan_urlencoded_query ( & read_ptr , write_ptr = read_ptr , SCAN_SEARCHPATH_VALUE ) ;
ws - > peer_id = ws - > peer_id_size > 0 ? write_ptr : 0 ;
break ;
# endif
}
}
/* Scanned whole query string */
if ( ! hash )
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 " ) ;
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 " ) ;
if ( OT_PEERFLAG ( & peer ) & PEER_FLAG_STOPPED )
len = remove_peer_from_torrent ( hash , & peer , SUCCESS_HTTP_HEADER_LENGTH + static_outbuf , FLAG_TCP ) ;
ws- > rep ly_siz e = remove_peer_from_torrent ( hash , & peer , ws- > reply , FLAG_TCP ) ;
else
len = add_peer_to_torrent_and_return_peers ( hash , & peer , FLAG_TCP , numwant , SUCCESS_HTTP_HEADER_LENGTH + static_outbuf ) ;
ws- > rep ly_siz e = add_peer_to_torrent_and_return_peers ( hash , & peer , FLAG_TCP , numwant , ws- > reply ) ;
if ( ! len ) HTTPERROR_500 ;
if ( ! ws- > rep ly_siz e ) HTTPERROR_500 ;
stats_issue_event ( EVENT_ANNOUNCE , FLAG_TCP , len ) ;
return len ;
stats_issue_event ( EVENT_ANNOUNCE , FLAG_TCP , ws- > rep ly_siz e) ;
return ws- > rep ly_siz e;
}
ssize_t http_handle_request ( const int64 client_socket , char * data , size_t recv_length ) {
char * c , * recv_header = data ;
ssize_t reply_size = 0 , reply_off , len ;
ssize_t http_handle_request ( const int64 client_socket , struct ot_workstruct * ws ) {
ssize_t reply_off , len ;
char * read_ptr = ws - > request , * write_ptr ;
# ifdef _DEBUG_HTTPERROR
if ( recv_length > = sizeof ( debug_request ) )
recv_length = sizeof ( debug_request ) - 1 ;
memmove ( debug_request , recv_header , recv_length ) ;
debug_request [ recv_length ] = 0 ;
reply_off = ws - > request_size ;
if ( ws - > request_size > = ( ssize_t ) ws - > debugbuf_size )
reply_off = ws - > debugbuf_size - 1 ;
memmove ( ws - > debugbuf , ws - > request , reply_off ) ;
ws - > debugbuf [ reply_off ] = 0 ;
# endif
/* Tell subroutines where to put reply data */
ws - > reply = ws - > outbuf + SUCCESS_HTTP_HEADER_LENGTH ;
/* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */
if ( byte_diff ( data , 5 , " GET / " ) ) HTTPERROR_400 ;
if ( memcmp( read_ptr , " GET / " , 5 ) ) HTTPERROR_400 ;
/* Skip leading '/' */
for ( c = data + 4 ; * c = = ' / ' ; + + c ) ;
for ( read_ptr+ = 4 ; * read_ptr = = ' / ' ; + + read_ptr ) ;
/* Try to parse the request.
In reality we abandoned requiring the url to be correct . This now
only decodes url encoded characters , we check for announces and
scrapes by looking for " a* " or " sc " */
len = scan_urlencoded_query ( & c, data = c , SCAN_PATH ) ;
len = scan_urlencoded_query ( & read_ptr, write_ptr = read_ptr , SCAN_PATH ) ;
/* If parsing returned an error, leave with not found */
if ( g_redirecturl & & ( len = = - 2 ) ) HTTPERROR_302 ;
if ( len < = 0 ) HTTPERROR_404 ;
/* This is the hardcore match for announce*/
if ( ( * data = = ' a ' ) | | ( * data = = ' ? ' ) )
reply_size = http_handle_announce ( client_socket , c ) ;
if ( ( * write_ptr = = ' a ' ) | | ( * write_ptr = = ' ? ' ) )
http_handle_announce ( client_socket , ws, read_ptr ) ;
# ifdef WANT_FULLSCRAPE
else if ( ! byte_diff( data , 12 , " scrape HTTP/ " ) )
reply_size = http_handle_fullscrape ( client_socket , recv_header, recv_length ) ;
else if ( ! memcmp( write_ptr , " scrape HTTP/ " , 12 ) )
http_handle_fullscrape ( client_socket , ws ) ;
# endif
/* This is the hardcore match for scrape */
else if ( ! byte_diff( data , 2 , " sc " ) )
reply_size = http_handle_scrape ( client_socket , c ) ;
else if ( ! memcmp( write_ptr , " sc " , 2 ) )
http_handle_scrape ( client_socket , ws, read_ptr ) ;
/* All the rest is matched the standard way */
else switch ( len ) {
case 5 : /* stats ? */
if ( byte_diff ( data , 5 , " stats " ) ) HTTPERROR_404 ;
reply_size = http_handle_stats ( client_socket , c , recv_header , recv_length ) ;
break ;
default :
else if ( ! memcmp ( write_ptr , " stats " , 5 ) )
http_handle_stats ( client_socket , ws , read_ptr ) ;
else
HTTPERROR_404 ;
}
/* If routines handled sending themselves, just return */
if ( reply_size = = - 2 ) return 0 ;
if ( ws- > reply_size = = - 2 ) return 0 ;
/* If routine failed, let http error take over */
if ( reply_size = = - 1 ) HTTPERROR_500 ;
if ( ws- > reply_size = = - 1 ) HTTPERROR_500 ;
/* This one is rather ugly, so I take you step by step through it.
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
write to a fixed location , leaving SUCCESS_HTTP_HEADER_LENGTH bytes in our work 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 its expansion and calculate
the space NOT needed to expand in reply_off
*/
reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf ( static_outbuf , 0 , " %zd " , reply_size ) ;
reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf ( ws - > outbuf , 0 , " %zd " , ws - > reply_size ) ;
ws - > reply = ws - > outbuf + reply_off ;
/* 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 */
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 ) ;
ws- > reply_size + = 1 + sprintf ( ws- > reply , " HTTP/1.0 200 OK \r \n Content-Type: text/plain \r \n Content-Length: %zd \r \n \r " , ws - > reply_size ) ;
/* 3. Finally we join both blocks neatly */
static_ outbuf[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = ' \n ' ;
ws- > outbuf[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = ' \n ' ;
http_senddata ( client_socket , static_outbuf + reply_off , reply_size ) ;
return reply_size;
http_senddata ( client_socket , w s ) ;
return ws- > reply_size;
}
const char * g_version_http_c = " $Source$: $Revision$ \n " ;