diff options
Diffstat (limited to 'opentracker.c')
| -rw-r--r-- | opentracker.c | 356 |
1 files changed, 164 insertions, 192 deletions
diff --git a/opentracker.c b/opentracker.c index 403fa21..9121103 100644 --- a/opentracker.c +++ b/opentracker.c | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | /* This software was written by Dirk Engling <erdgeist@erdgeist.org> | 1 | /* This software was written by Dirk Engling <erdgeist@erdgeist.org> |
| 2 | It is considered beerware. Prost. Skol. Cheers or whatever. | 2 | It is considered beerware. Prost. Skol. Cheers or whatever. |
| 3 | Some of the stuff below is stolen from Fefes example libowfat httpd. | 3 | Some of the stuff below is stolen from Fefes example libowfat httpd. |
| 4 | */ | 4 | */ |
| @@ -26,6 +26,7 @@ | |||
| 26 | #include "trackerlogic.h" | 26 | #include "trackerlogic.h" |
| 27 | #include "scan_urlencoded_query.h" | 27 | #include "scan_urlencoded_query.h" |
| 28 | 28 | ||
| 29 | /* Globals */ | ||
| 29 | static unsigned int ot_overall_connections = 0; | 30 | static unsigned int ot_overall_connections = 0; |
| 30 | static unsigned int ot_overall_successfulannounces = 0; | 31 | static unsigned int ot_overall_successfulannounces = 0; |
| 31 | static time_t ot_start_time; | 32 | static time_t ot_start_time; |
| @@ -34,35 +35,64 @@ static const size_t SUCCESS_HTTP_SIZE_OFF = 17; | |||
| 34 | /* To always have space for error messages ;) */ | 35 | /* To always have space for error messages ;) */ |
| 35 | static char static_scratch[8192]; | 36 | static char static_scratch[8192]; |
| 36 | 37 | ||
| 37 | #ifdef _DEBUG_FDS | ||
| 38 | static char fd_debug_space[0x10000]; | ||
| 39 | #endif | ||
| 40 | #ifdef _DEBUG_HTTPERROR | 38 | #ifdef _DEBUG_HTTPERROR |
| 41 | static char debug_request[8192]; | 39 | static char debug_request[8192]; |
| 42 | #endif | 40 | #endif |
| 43 | 41 | ||
| 44 | static void carp(const char* routine ) { | 42 | struct http_data { |
| 43 | union { | ||
| 44 | array request; | ||
| 45 | io_batch batch; | ||
| 46 | }; | ||
| 47 | unsigned char ip[4]; | ||
| 48 | }; | ||
| 49 | |||
| 50 | /* Prototypes */ | ||
| 51 | |||
| 52 | int main( int argc, char **argv ); | ||
| 53 | |||
| 54 | static int httpheader_complete( struct http_data *h ); | ||
| 55 | static void httperror( const int64 s, struct http_data *h, const char *title, const char *message ); | ||
| 56 | static void httpresponse( const int64 s, struct http_data *h); | ||
| 57 | |||
| 58 | static void sendmallocdata( const int64 s, struct http_data *h, char *buffer, const size_t size ); | ||
| 59 | static void senddata( const int64 s, struct http_data *h, char *buffer, const size_t size ); | ||
| 60 | |||
| 61 | static void server_mainloop( const int64 serversocket ); | ||
| 62 | static void handle_timeouted( void ); | ||
| 63 | static void handle_accept( const int64 serversocket ); | ||
| 64 | static void handle_read( const int64 clientsocket ); | ||
| 65 | static void handle_write( const int64 clientsocket ); | ||
| 66 | |||
| 67 | static void usage( char *name ); | ||
| 68 | static void help( char *name ); | ||
| 69 | |||
| 70 | static void carp( const char *routine ); | ||
| 71 | static void panic( const char *routine ); | ||
| 72 | static void graceful( int s ); | ||
| 73 | |||
| 74 | #define HTTPERROR_400 return httperror( s, h, "400 Invalid Request", "This server only understands GET." ) | ||
| 75 | #define HTTPERROR_400_PARAM return httperror( s, h, "400 Invalid Request", "Invalid parameter" ) | ||
| 76 | #define HTTPERROR_400_COMPACT return httperror( s, h, "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." ) | ||
| 78 | #define HTTPERROR_500 return httperror( s, h, "500 Internal Server Error", "A server error has occured. Please retry later." ) | ||
| 79 | |||
| 80 | /* End of prototypes */ | ||
| 81 | |||
| 82 | static void carp( const char *routine ) { | ||
| 45 | buffer_puts( buffer_2, routine ); | 83 | buffer_puts( buffer_2, routine ); |
| 46 | buffer_puts( buffer_2, ": " ); | 84 | buffer_puts( buffer_2, ": " ); |
| 47 | buffer_puterror( buffer_2 ); | 85 | buffer_puterror( buffer_2 ); |
| 48 | buffer_putnlflush( buffer_2 ); | 86 | buffer_putnlflush( buffer_2 ); |
| 49 | } | 87 | } |
| 50 | 88 | ||
| 51 | static void panic( const char* routine ) { | 89 | static void panic( const char *routine ) { |
| 52 | carp( routine ); | 90 | carp( routine ); |
| 53 | exit( 111 ); | 91 | exit( 111 ); |
| 54 | } | 92 | } |
| 55 | 93 | ||
| 56 | struct http_data { | 94 | static int httpheader_complete( struct http_data *h ) { |
| 57 | union { | 95 | size_t l = array_bytes( &h->request ), i; |
| 58 | array request; | ||
| 59 | io_batch batch; | ||
| 60 | }; | ||
| 61 | unsigned char ip[4]; | ||
| 62 | }; | ||
| 63 | |||
| 64 | int header_complete( struct http_data* h ) { | ||
| 65 | int l = array_bytes( &h->request ), i; | ||
| 66 | const char* c = array_start( &h->request ); | 96 | const char* c = array_start( &h->request ); |
| 67 | 97 | ||
| 68 | for( i=0; i+1<l; ++i) { | 98 | for( i=0; i+1<l; ++i) { |
| @@ -72,16 +102,29 @@ int header_complete( struct http_data* h ) { | |||
| 72 | return 0; | 102 | return 0; |
| 73 | } | 103 | } |
| 74 | 104 | ||
| 75 | void sendmallocdata( int64 s, struct http_data *h, char * buffer, size_t size ) { | 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); | ||
| 108 | #ifdef _DEBUG_HTTPERROR | ||
| 109 | fprintf( stderr, "DEBUG: invalid request was: %s\n", debug_request ); | ||
| 110 | #endif | ||
| 111 | senddata(s,h,static_scratch,reply_size); | ||
| 112 | } | ||
| 113 | |||
| 114 | static void sendmallocdata( const int64 s, struct http_data *h, char *buffer, size_t size ) { | ||
| 76 | tai6464 t; | 115 | tai6464 t; |
| 77 | char *header; | 116 | char *header; |
| 78 | size_t header_size; | 117 | size_t header_size; |
| 79 | 118 | ||
| 80 | if( !h ) { free( buffer); return; } | 119 | if( !h ) |
| 120 | return free( buffer); | ||
| 81 | array_reset( &h->request ); | 121 | array_reset( &h->request ); |
| 82 | 122 | ||
| 83 | header = malloc( SUCCESS_HTTP_HEADER_LENGTH ); | 123 | header = malloc( SUCCESS_HTTP_HEADER_LENGTH ); |
| 84 | if( !header ) { free( buffer ); return; } | 124 | if( !header ) { |
| 125 | free( buffer ); | ||
| 126 | HTTPERROR_500; | ||
| 127 | } | ||
| 85 | 128 | ||
| 86 | header_size = sprintf( header, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r\n", size ); | 129 | header_size = sprintf( header, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r\n", size ); |
| 87 | 130 | ||
| @@ -89,13 +132,13 @@ void sendmallocdata( int64 s, struct http_data *h, char * buffer, size_t size ) | |||
| 89 | iob_addbuf_free( &h->batch, header, header_size ); | 132 | iob_addbuf_free( &h->batch, header, header_size ); |
| 90 | iob_addbuf_free( &h->batch, buffer, size ); | 133 | iob_addbuf_free( &h->batch, buffer, size ); |
| 91 | 134 | ||
| 92 | // writeable sockets just have a tcp timeout | 135 | /* writeable sockets just have a tcp timeout */ |
| 93 | taia_uint(&t,0); io_timeout( s, t ); | 136 | taia_uint(&t,0); io_timeout( s, t ); |
| 94 | io_dontwantread( s ); | 137 | io_dontwantread( s ); |
| 95 | io_wantwrite( s ); | 138 | io_wantwrite( s ); |
| 96 | } | 139 | } |
| 97 | 140 | ||
| 98 | void senddata(int64 s, struct http_data* h, char *buffer, size_t size ) { | 141 | static void senddata( const int64 s, struct http_data *h, char *buffer, size_t size ) { |
| 99 | size_t written_size; | 142 | size_t written_size; |
| 100 | 143 | ||
| 101 | /* whoever sends data is not interested in its input-array */ | 144 | /* whoever sends data is not interested in its input-array */ |
| @@ -104,20 +147,12 @@ void senddata(int64 s, struct http_data* h, char *buffer, size_t size ) { | |||
| 104 | 147 | ||
| 105 | written_size = write( s, buffer, size ); | 148 | written_size = write( s, buffer, size ); |
| 106 | if( ( written_size < 0 ) || ( written_size == size ) ) { | 149 | if( ( written_size < 0 ) || ( written_size == size ) ) { |
| 107 | #ifdef _DEBUG_FDS | ||
| 108 | if( !fd_debug_space[s] ) fprintf( stderr, "close on non-open fd\n" ); | ||
| 109 | fd_debug_space[s] = 0; | ||
| 110 | #endif | ||
| 111 | free( h ); io_close( s ); | 150 | free( h ); io_close( s ); |
| 112 | } else { | 151 | } else { |
| 113 | char * outbuf = malloc( size - written_size ); | 152 | char * outbuf = malloc( size - written_size ); |
| 114 | tai6464 t; | 153 | tai6464 t; |
| 115 | 154 | ||
| 116 | if( !outbuf ) { | 155 | if( !outbuf ) { |
| 117 | #ifdef _DEBUG_FDS | ||
| 118 | if( !fd_debug_space[s] ) fprintf( stderr, "close on non-open fd\n" ); | ||
| 119 | fd_debug_space[s] = 0; | ||
| 120 | #endif | ||
| 121 | free(h); io_close( s ); | 156 | free(h); io_close( s ); |
| 122 | return; | 157 | return; |
| 123 | } | 158 | } |
| @@ -126,29 +161,21 @@ void senddata(int64 s, struct http_data* h, char *buffer, size_t size ) { | |||
| 126 | memmove( outbuf, buffer + written_size, size - written_size ); | 161 | memmove( outbuf, buffer + written_size, size - written_size ); |
| 127 | iob_addbuf_free( &h->batch, outbuf, size - written_size ); | 162 | iob_addbuf_free( &h->batch, outbuf, size - written_size ); |
| 128 | 163 | ||
| 129 | // writeable sockets just have a tcp timeout | 164 | /* writeable sockets just have a tcp timeout */ |
| 130 | taia_uint( &t, 0 ); io_timeout( s, t ); | 165 | taia_uint( &t, 0 ); io_timeout( s, t ); |
| 131 | io_dontwantread( s ); | 166 | io_dontwantread( s ); |
| 132 | io_wantwrite( s ); | 167 | io_wantwrite( s ); |
| 133 | } | 168 | } |
| 134 | } | 169 | } |
| 135 | 170 | ||
| 136 | void httperror(int64 s,struct http_data* h,const char* title,const char* message) { | 171 | static void httpresponse( const int64 s, struct http_data *h) { |
| 137 | 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", | 172 | char *c, *data, *reply; |
| 138 | title, strlen(message)+strlen(title)+16-4,title+4); | ||
| 139 | #ifdef _DEBUG_HTTPERROR | ||
| 140 | fprintf( stderr, "DEBUG: invalid request was: %s\n", debug_request ); | ||
| 141 | #endif | ||
| 142 | senddata(s,h,static_scratch,reply_size); | ||
| 143 | } | ||
| 144 | |||
| 145 | void httpresponse( int64 s, struct http_data* h) { | ||
| 146 | char *c, *data; | ||
| 147 | ot_peer peer; | 173 | ot_peer peer; |
| 148 | ot_torrent *torrent; | 174 | ot_torrent *torrent; |
| 149 | ot_hash *hash = NULL; | 175 | ot_hash *hash = NULL; |
| 150 | int numwant, tmp, scanon, mode; | 176 | int numwant, tmp, scanon, mode; |
| 151 | unsigned short port = htons(6881); | 177 | unsigned short port = htons(6881); |
| 178 | time_t t; | ||
| 152 | size_t reply_size = 0; | 179 | size_t reply_size = 0; |
| 153 | 180 | ||
| 154 | array_cat0( &h->request ); | 181 | array_cat0( &h->request ); |
| @@ -158,104 +185,106 @@ void httpresponse( int64 s, struct http_data* h) { | |||
| 158 | memcpy( debug_request, array_start( &h->request ), array_bytes( &h->request ) ); | 185 | memcpy( debug_request, array_start( &h->request ), array_bytes( &h->request ) ); |
| 159 | #endif | 186 | #endif |
| 160 | 187 | ||
| 161 | if( byte_diff( c, 4, "GET ") ) { | 188 | if( byte_diff( c, 4, "GET ") ) HTTPERROR_400; |
| 162 | e400: | ||
| 163 | return httperror( s, h, "400 Invalid Request", "This server only understands GET." ); | ||
| 164 | } | ||
| 165 | 189 | ||
| 166 | c+=4; | 190 | c+=4; |
| 167 | for( data = c; *data!=' ' && *data != '\t' && *data != '\n' && *data != '\r'; ++data ) ; | 191 | for( data = c; *data!=' ' && *data != '\t' && *data != '\n' && *data != '\r'; ++data ) ; |
| 168 | 192 | ||
| 169 | if( *data != ' ' ) goto e400; | 193 | if( *data != ' ' ) HTTPERROR_400; |
| 170 | *data = 0; | 194 | *data = 0; |
| 171 | if( c[0] != '/' ) goto e404; | 195 | if( c[0] != '/' ) HTTPERROR_404; |
| 172 | while( *c == '/' ) ++c; | 196 | while( *c == '/' ) ++c; |
| 173 | 197 | ||
| 174 | switch( scan_urlencoded_query( &c, data = c, SCAN_PATH ) ) { | 198 | switch( scan_urlencoded_query( &c, data = c, SCAN_PATH ) ) { |
| 199 | case 4: /* sync ? */ | ||
| 200 | if( byte_diff( data, 4, "sync") ) HTTPERROR_404; | ||
| 201 | scanon = 1; | ||
| 202 | |||
| 203 | while( scanon ) { | ||
| 204 | switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) { | ||
| 205 | case -2: scanon = 0; break; /* TERMINATOR */ | ||
| 206 | case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ | ||
| 207 | case 9: | ||
| 208 | if(byte_diff(data,9,"info_hash")) { | ||
| 209 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); | ||
| 210 | continue; | ||
| 211 | } | ||
| 212 | /* ignore this, when we have less than 20 bytes */ | ||
| 213 | if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM; | ||
| 214 | hash = (ot_hash*)data; /* Fall through intended */ | ||
| 215 | break; | ||
| 216 | default: | ||
| 217 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); | ||
| 218 | break; | ||
| 219 | } | ||
| 220 | } | ||
| 221 | |||
| 222 | if( !hash ) HTTPERROR_400_PARAM; | ||
| 223 | if( ( reply_size = return_sync_for_torrent( hash, &reply ) ) <= 0 ) HTTPERROR_500; | ||
| 224 | |||
| 225 | return sendmallocdata( s, h, reply, reply_size ); | ||
| 175 | case 5: /* stats ? */ | 226 | case 5: /* stats ? */ |
| 176 | if( byte_diff(data,5,"stats")) | 227 | if( byte_diff(data,5,"stats")) HTTPERROR_404; |
| 177 | goto e404; | ||
| 178 | scanon = 1; | 228 | scanon = 1; |
| 179 | mode = STATS_MRTG; | 229 | mode = STATS_MRTG; |
| 180 | 230 | ||
| 181 | while( scanon ) { | 231 | while( scanon ) { |
| 182 | switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) { | 232 | switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) { |
| 183 | case -2: /* terminator */ | 233 | case -2: scanon = 0; break; /* TERMINATOR */ |
| 184 | scanon = 0; | 234 | case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ |
| 185 | break; | 235 | default: scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); break; |
| 186 | case -1: /* error */ | ||
| 187 | goto e404; | ||
| 188 | case 4: | 236 | case 4: |
| 189 | if( byte_diff(data,4,"mode")) { | 237 | if( byte_diff(data,4,"mode")) { |
| 190 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); | 238 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); |
| 191 | continue; | 239 | continue; |
| 192 | } | 240 | } |
| 193 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); | 241 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); |
| 194 | if( len <= 0 ) goto e400_param; | 242 | if( len <= 0 ) HTTPERROR_400_PARAM; |
| 195 | if( !byte_diff(data,4,"mrtg")) | 243 | if( !byte_diff(data,4,"mrtg")) |
| 196 | mode = STATS_MRTG; | 244 | mode = STATS_MRTG; |
| 197 | else if( !byte_diff(data,4,"top5")) | 245 | else if( !byte_diff(data,4,"top5")) |
| 198 | mode = STATS_TOP5; | 246 | mode = STATS_TOP5; |
| 199 | else | 247 | else |
| 200 | goto e400_param; | 248 | HTTPERROR_400_PARAM; |
| 201 | default: | ||
| 202 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); | ||
| 203 | break; | ||
| 204 | } | 249 | } |
| 205 | } | 250 | } |
| 206 | 251 | ||
| 207 | /* Enough for http header + whole scrape string */ | 252 | /* Enough for http header + whole scrape string */ |
| 208 | if( ( reply_size = return_stats_for_tracker( SUCCESS_HTTP_HEADER_LENGTH + static_scratch, mode ) ) <= 0 ) | 253 | if( ( reply_size = return_stats_for_tracker( SUCCESS_HTTP_HEADER_LENGTH + static_scratch, mode ) ) <= 0 ) HTTPERROR_500; |
| 209 | goto e500; | 254 | |
| 210 | break; | 255 | break; |
| 211 | case 6: /* scrape ? */ | 256 | case 6: /* scrape ? */ |
| 212 | if( byte_diff( data, 6, "scrape") ) | 257 | if( byte_diff( data, 6, "scrape") ) HTTPERROR_404; |
| 213 | goto e404; | ||
| 214 | scanon = 1; | 258 | scanon = 1; |
| 215 | 259 | ||
| 216 | while( scanon ) { | 260 | while( scanon ) { |
| 217 | switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) { | 261 | switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) { |
| 218 | case -2: /* terminator */ | 262 | case -2: scanon = 0; break; /* TERMINATOR */ |
| 219 | scanon = 0; | 263 | case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ |
| 220 | break; | 264 | default: scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); break; |
| 221 | case -1: /* error */ | ||
| 222 | goto e404; | ||
| 223 | case 9: | 265 | case 9: |
| 224 | if(byte_diff(data,9,"info_hash")) { | 266 | if(byte_diff(data,9,"info_hash")) { |
| 225 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); | 267 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); |
| 226 | continue; | 268 | continue; |
| 227 | } | 269 | } |
| 228 | /* ignore this, when we have less than 20 bytes */ | 270 | /* ignore this, when we have less than 20 bytes */ |
| 229 | if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 20 ) { | 271 | if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM; |
| 230 | e400_param: | ||
| 231 | return httperror(s,h,"400 Invalid Request","Invalid parameter"); | ||
| 232 | } | ||
| 233 | hash = (ot_hash*)data; /* Fall through intended */ | 272 | hash = (ot_hash*)data; /* Fall through intended */ |
| 234 | break; | 273 | break; |
| 235 | default: | ||
| 236 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); | ||
| 237 | break; | ||
| 238 | } | 274 | } |
| 239 | } | 275 | } |
| 240 | 276 | ||
| 241 | /* Scanned whole query string, no hash means full scrape... you might want to limit that */ | 277 | /* Scanned whole query string, no hash means full scrape... you might want to limit that */ |
| 242 | if( !hash ) { | 278 | if( !hash ) { |
| 243 | char * reply; | 279 | if( ( reply_size = return_fullscrape_for_tracker( &reply ) ) <= 0 ) HTTPERROR_500; |
| 244 | 280 | return sendmallocdata( s, h, reply, reply_size ); | |
| 245 | reply_size = return_fullscrape_for_tracker( &reply ); | ||
| 246 | if( reply_size ) | ||
| 247 | return sendmallocdata( s, h, reply, reply_size ); | ||
| 248 | |||
| 249 | goto e500; | ||
| 250 | } else { | ||
| 251 | /* Enough for http header + whole scrape string */ | ||
| 252 | if( ( reply_size = return_scrape_for_torrent( hash, SUCCESS_HTTP_HEADER_LENGTH + static_scratch ) ) <= 0 ) | ||
| 253 | goto e500; | ||
| 254 | } | 281 | } |
| 282 | |||
| 283 | /* Enough for http header + whole scrape string */ | ||
| 284 | if( ( reply_size = return_scrape_for_torrent( hash, SUCCESS_HTTP_HEADER_LENGTH + static_scratch ) ) <= 0 ) HTTPERROR_500; | ||
| 255 | break; | 285 | break; |
| 256 | case 8: | 286 | case 8: |
| 257 | if( byte_diff(data,8,"announce")) | 287 | if( byte_diff(data,8,"announce")) HTTPERROR_404; |
| 258 | goto e404; | ||
| 259 | 288 | ||
| 260 | OT_SETIP( &peer, h->ip); | 289 | OT_SETIP( &peer, h->ip); |
| 261 | OT_SETPORT( &peer, &port ); | 290 | OT_SETPORT( &peer, &port ); |
| @@ -265,17 +294,15 @@ e400_param: | |||
| 265 | 294 | ||
| 266 | while( scanon ) { | 295 | while( scanon ) { |
| 267 | switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) { | 296 | switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) { |
| 268 | case -2: /* terminator */ | 297 | case -2: scanon = 0; break; /* TERMINATOR */ |
| 269 | scanon = 0; | 298 | case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ |
| 270 | break; | 299 | default: scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); break; |
| 271 | case -1: /* error */ | ||
| 272 | goto e404; | ||
| 273 | #ifdef WANT_IP_FROM_QUERY_STRING | 300 | #ifdef WANT_IP_FROM_QUERY_STRING |
| 274 | case 2: | 301 | case 2: |
| 275 | if(!byte_diff(data,2,"ip")) { | 302 | if(!byte_diff(data,2,"ip")) { |
| 276 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); | 303 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); |
| 277 | unsigned char ip[4]; | 304 | unsigned char ip[4]; |
| 278 | if( ( len <= 0 ) || scan_fixed_ip( data, len, ip ) ) goto e400_param; | 305 | if( ( len <= 0 ) || scan_fixed_ip( data, len, ip ) ) HTTPERROR_400_PARAM; |
| 279 | OT_SETIP( &peer, ip ); | 306 | OT_SETIP( &peer, ip ); |
| 280 | } else | 307 | } else |
| 281 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); | 308 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); |
| @@ -284,11 +311,11 @@ e400_param: | |||
| 284 | case 4: | 311 | case 4: |
| 285 | if(!byte_diff(data,4,"port")) { | 312 | if(!byte_diff(data,4,"port")) { |
| 286 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); | 313 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); |
| 287 | if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) || ( tmp > 0xffff ) ) goto e400_param; | 314 | if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) || ( tmp > 0xffff ) ) HTTPERROR_400_PARAM; |
| 288 | port = htons( tmp ); OT_SETPORT( &peer, &port ); | 315 | port = htons( tmp ); OT_SETPORT( &peer, &port ); |
| 289 | } else if(!byte_diff(data,4,"left")) { | 316 | } else if(!byte_diff(data,4,"left")) { |
| 290 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); | 317 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); |
| 291 | if( len <= 0 ) goto e400_param; | 318 | if( len <= 0 ) HTTPERROR_400_PARAM; |
| 292 | if( scan_fixed_int( data, len, &tmp ) ) tmp = 0; | 319 | if( scan_fixed_int( data, len, &tmp ) ) tmp = 0; |
| 293 | if( !tmp ) OT_FLAG( &peer ) |= PEER_FLAG_SEEDING; | 320 | if( !tmp ) OT_FLAG( &peer ) |= PEER_FLAG_SEEDING; |
| 294 | } else | 321 | } else |
| @@ -299,7 +326,7 @@ e400_param: | |||
| 299 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); | 326 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); |
| 300 | else switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) { | 327 | else switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) { |
| 301 | case -1: | 328 | case -1: |
| 302 | goto e400_param; | 329 | HTTPERROR_400_PARAM; |
| 303 | case 7: | 330 | case 7: |
| 304 | if(!byte_diff(data,7,"stopped")) OT_FLAG( &peer ) |= PEER_FLAG_STOPPED; | 331 | if(!byte_diff(data,7,"stopped")) OT_FLAG( &peer ) |= PEER_FLAG_STOPPED; |
| 305 | break; | 332 | break; |
| @@ -312,13 +339,12 @@ e400_param: | |||
| 312 | case 7: | 339 | case 7: |
| 313 | if(!byte_diff(data,7,"numwant")) { | 340 | if(!byte_diff(data,7,"numwant")) { |
| 314 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); | 341 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); |
| 315 | if( ( len <= 0 ) || scan_fixed_int( data, len, &numwant ) ) goto e400_param; | 342 | if( ( len <= 0 ) || scan_fixed_int( data, len, &numwant ) ) HTTPERROR_400_PARAM; |
| 316 | if( numwant > 200 ) numwant = 200; | 343 | if( numwant > 200 ) numwant = 200; |
| 317 | } else if(!byte_diff(data,7,"compact")) { | 344 | } else if(!byte_diff(data,7,"compact")) { |
| 318 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); | 345 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); |
| 319 | if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) ) goto e400_param; | 346 | if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) ) HTTPERROR_400_PARAM; |
| 320 | if( !tmp ) | 347 | if( !tmp ) HTTPERROR_400_COMPACT; |
| 321 | return httperror(s,h,"400 Invalid Request","This server only delivers compact results."); | ||
| 322 | } else | 348 | } else |
| 323 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); | 349 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); |
| 324 | break; | 350 | break; |
| @@ -328,79 +354,58 @@ e400_param: | |||
| 328 | continue; | 354 | continue; |
| 329 | } | 355 | } |
| 330 | /* ignore this, when we have less than 20 bytes */ | 356 | /* ignore this, when we have less than 20 bytes */ |
| 331 | if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 20 ) | 357 | if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM; |
| 332 | goto e400; | ||
| 333 | hash = (ot_hash*)data; | 358 | hash = (ot_hash*)data; |
| 334 | break; | 359 | break; |
| 335 | default: | ||
| 336 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); | ||
| 337 | break; | ||
| 338 | } | 360 | } |
| 339 | } | 361 | } |
| 340 | 362 | ||
| 341 | /* Scanned whole query string */ | 363 | /* Scanned whole query string */ |
| 342 | if( !hash ) goto e400; | 364 | if( !hash ) HTTPERROR_400_PARAM; |
| 343 | 365 | ||
| 344 | if( OT_FLAG( &peer ) & PEER_FLAG_STOPPED ) { | 366 | if( OT_FLAG( &peer ) & PEER_FLAG_STOPPED ) { |
| 345 | remove_peer_from_torrent( hash, &peer ); | 367 | remove_peer_from_torrent( hash, &peer ); |
| 346 | reply_size = sprintf( static_scratch + SUCCESS_HTTP_HEADER_LENGTH, "d8:completei0e10:incompletei0e8:intervali%ie5:peers0:e", OT_CLIENT_REQUEST_INTERVAL_RANDOM ); | 368 | reply_size = sprintf( static_scratch + SUCCESS_HTTP_HEADER_LENGTH, "d8:completei0e10:incompletei0e8:intervali%ie5:peers0:e", OT_CLIENT_REQUEST_INTERVAL_RANDOM ); |
| 347 | } else { | 369 | } else { |
| 348 | torrent = add_peer_to_torrent( hash, &peer ); | 370 | torrent = add_peer_to_torrent( hash, &peer ); |
| 349 | if( !torrent ) { | 371 | if( !torrent || ( reply_size = return_peers_for_torrent( torrent, numwant, SUCCESS_HTTP_HEADER_LENGTH + static_scratch ) ) <= 0 ) HTTPERROR_500; |
| 350 | e500: | ||
| 351 | return httperror(s,h,"500 Internal Server Error","A server error has occured. Please retry later."); | ||
| 352 | } | ||
| 353 | if( ( reply_size = return_peers_for_torrent( torrent, numwant, SUCCESS_HTTP_HEADER_LENGTH + static_scratch ) ) <= 0 ) | ||
| 354 | goto e500; | ||
| 355 | } | 372 | } |
| 356 | ot_overall_successfulannounces++; | 373 | ot_overall_successfulannounces++; |
| 357 | break; | 374 | break; |
| 358 | case 11: | 375 | case 11: |
| 359 | if( byte_diff(data,11,"mrtg_scrape")) | 376 | if( byte_diff(data,11,"mrtg_scrape")) HTTPERROR_404; |
| 360 | goto e404; | 377 | |
| 361 | { | 378 | t = time( NULL ) - ot_start_time; |
| 362 | time_t seconds_elapsed = time( NULL ) - ot_start_time; | 379 | reply_size = sprintf( static_scratch + SUCCESS_HTTP_HEADER_LENGTH, |
| 363 | reply_size = sprintf( static_scratch + SUCCESS_HTTP_HEADER_LENGTH, | 380 | "%i\n%i\nUp: %i seconds (%i hours)\nPretuned by german engineers, currently handling %i connections per second.", |
| 364 | "%i\n%i\nUp: %i seconds (%i hours)\nPretuned by german engineers, currently handling %i connections per second.", | 381 | ot_overall_connections, ot_overall_successfulannounces, (int)t, (int)(t / 3600), (int)ot_overall_connections / ( (int)t ? (int)t : 1 ) ); |
| 365 | ot_overall_connections, ot_overall_successfulannounces, (int)seconds_elapsed, | ||
| 366 | (int)(seconds_elapsed / 3600), (int)ot_overall_connections / ( (int)seconds_elapsed ? (int)seconds_elapsed : 1 ) ); | ||
| 367 | } | ||
| 368 | break; | 382 | break; |
| 369 | default: /* neither *scrape nor announce */ | 383 | default: /* neither *scrape nor announce */ |
| 370 | e404: | 384 | HTTPERROR_404; |
| 371 | return httperror( s, h, "404 Not Found", "No such file or directory." ); | ||
| 372 | } | 385 | } |
| 373 | 386 | ||
| 374 | if( reply_size ) { | 387 | if( reply_size <= 0 ) HTTPERROR_500; |
| 375 | /* This one is rather ugly, so I take you step by step through it. | ||
| 376 | 388 | ||
| 377 | 1. In order to avoid having two buffers, one for header and one for content, we allow all above functions from trackerlogic to | 389 | /* This one is rather ugly, so I take you step by step through it. |
| 378 | write to a fixed location, leaving SUCCESS_HTTP_HEADER_LENGTH bytes in our static buffer, which is enough for the static string | ||
| 379 | plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for it expansion and calculate | ||
| 380 | the space NOT needed to expand in reply_off | ||
| 381 | */ | ||
| 382 | size_t reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf( static_scratch, 0, "%zd", reply_size ); | ||
| 383 | 390 | ||
| 384 | /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete | 391 | 1. In order to avoid having two buffers, one for header and one for content, we allow all above functions from trackerlogic to |
| 385 | packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */ | 392 | write to a fixed location, leaving SUCCESS_HTTP_HEADER_LENGTH bytes in our static buffer, which is enough for the static string |
| 386 | 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 ); | 393 | plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for it expansion and calculate |
| 394 | the space NOT needed to expand in reply_off | ||
| 395 | */ | ||
| 396 | size_t reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf( static_scratch, 0, "%zd", reply_size ); | ||
| 387 | 397 | ||
| 388 | /* 3. Finally we join both blocks neatly */ | 398 | /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete |
| 389 | static_scratch[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n'; | 399 | packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */ |
| 400 | 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 ); | ||
| 390 | 401 | ||
| 391 | senddata( s, h, static_scratch + reply_off, reply_size ); | 402 | /* 3. Finally we join both blocks neatly */ |
| 392 | } else { | 403 | static_scratch[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n'; |
| 393 | if( h ) | 404 | |
| 394 | array_reset( &h->request ); | 405 | senddata( s, h, static_scratch + reply_off, reply_size ); |
| 395 | #ifdef _DEBUG_FDS | ||
| 396 | if( !fd_debug_space[s] ) fprintf( stderr, "close on non-open fd\n" ); | ||
| 397 | fd_debug_space[s] = 0; | ||
| 398 | #endif | ||
| 399 | free( h ); io_close( s ); | ||
| 400 | } | ||
| 401 | } | 406 | } |
| 402 | 407 | ||
| 403 | void graceful( int s ) { | 408 | static void graceful( int s ) { |
| 404 | if( s == SIGINT ) { | 409 | if( s == SIGINT ) { |
| 405 | signal( SIGINT, SIG_IGN); | 410 | signal( SIGINT, SIG_IGN); |
| 406 | deinit_logic(); | 411 | deinit_logic(); |
| @@ -408,16 +413,7 @@ void graceful( int s ) { | |||
| 408 | } | 413 | } |
| 409 | } | 414 | } |
| 410 | 415 | ||
| 411 | #ifdef _DEBUG_FDS | 416 | static void usage( char *name ) { |
| 412 | void count_fds( int s ) { | ||
| 413 | int i, count = 0; | ||
| 414 | for( i=0; i<sizeof(fd_debug_space); ++i ) | ||
| 415 | if( fd_debug_space[i] ) ++count; | ||
| 416 | fprintf( stderr, "Open fds here: %i\n", count ); | ||
| 417 | } | ||
| 418 | #endif | ||
| 419 | |||
| 420 | void usage( char *name ) { | ||
| 421 | fprintf( stderr, "Usage: %s [-i serverip] [-p serverport] [-d serverdirectory]" | 417 | fprintf( stderr, "Usage: %s [-i serverip] [-p serverport] [-d serverdirectory]" |
| 422 | #ifdef WANT_CLOSED_TRACKER | 418 | #ifdef WANT_CLOSED_TRACKER |
| 423 | " [-oc]" | 419 | " [-oc]" |
| @@ -428,7 +424,7 @@ void usage( char *name ) { | |||
| 428 | "\n", name ); | 424 | "\n", name ); |
| 429 | } | 425 | } |
| 430 | 426 | ||
| 431 | void help( char *name ) { | 427 | static void help( char *name ) { |
| 432 | usage( name ); | 428 | usage( name ); |
| 433 | fprintf( stderr, "\t-i serverip\tspecify ip to bind to (default: *)\n" | 429 | fprintf( stderr, "\t-i serverip\tspecify ip to bind to (default: *)\n" |
| 434 | "\t-p serverport\tspecify port to bind to (default: 6969)\n" | 430 | "\t-p serverport\tspecify port to bind to (default: 6969)\n" |
| @@ -453,19 +449,15 @@ void help( char *name ) { | |||
| 453 | ); | 449 | ); |
| 454 | } | 450 | } |
| 455 | 451 | ||
| 456 | void handle_read( int64 clientsocket ) { | 452 | static void handle_read( const int64 clientsocket ) { |
| 457 | struct http_data* h = io_getcookie( clientsocket ); | 453 | struct http_data* h = io_getcookie( clientsocket ); |
| 458 | int l = io_tryread( clientsocket, static_scratch, sizeof static_scratch ); | 454 | size_t l; |
| 459 | 455 | ||
| 460 | if( l <= 0 ) { | 456 | if( ( l = io_tryread( clientsocket, static_scratch, sizeof static_scratch ) ) <= 0 ) { |
| 461 | if( h ) { | 457 | if( h ) { |
| 462 | array_reset( &h->request ); | 458 | array_reset( &h->request ); |
| 463 | free( h ); | 459 | free( h ); |
| 464 | } | 460 | } |
| 465 | #ifdef _DEBUG_FDS | ||
| 466 | if( !fd_debug_space[clientsocket] ) fprintf( stderr, "close on non-open fd\n" ); | ||
| 467 | fd_debug_space[clientsocket] = 0; | ||
| 468 | #endif | ||
| 469 | io_close( clientsocket ); | 461 | io_close( clientsocket ); |
| 470 | return; | 462 | return; |
| 471 | } | 463 | } |
| @@ -480,26 +472,22 @@ void handle_read( int64 clientsocket ) { | |||
| 480 | httperror( clientsocket, h, "500 Server Error", "Request too long."); | 472 | httperror( clientsocket, h, "500 Server Error", "Request too long."); |
| 481 | else if( array_bytes( &h->request ) > 8192 ) | 473 | else if( array_bytes( &h->request ) > 8192 ) |
| 482 | httperror( clientsocket, h, "500 request too long", "You sent too much headers"); | 474 | httperror( clientsocket, h, "500 request too long", "You sent too much headers"); |
| 483 | else if( ( l = header_complete( h ) ) ) | 475 | else if( ( l = httpheader_complete( h ) ) ) |
| 484 | httpresponse( clientsocket, h); | 476 | httpresponse( clientsocket, h); |
| 485 | } | 477 | } |
| 486 | 478 | ||
| 487 | void handle_write( int64 clientsocket ) { | 479 | static void handle_write( const int64 clientsocket ) { |
| 488 | struct http_data* h=io_getcookie( clientsocket ); | 480 | struct http_data* h=io_getcookie( clientsocket ); |
| 489 | if( !h ) return; | 481 | if( !h ) return; |
| 490 | if( iob_send( clientsocket, &h->batch ) <= 0 ) { | 482 | if( iob_send( clientsocket, &h->batch ) <= 0 ) { |
| 491 | iob_reset( &h->batch ); | 483 | iob_reset( &h->batch ); |
| 492 | #ifdef _DEBUG_FDS | ||
| 493 | if( !fd_debug_space[clientsocket] ) fprintf( stderr, "close on non-open fd\n" ); | ||
| 494 | fd_debug_space[clientsocket] = 0; | ||
| 495 | #endif | ||
| 496 | io_close( clientsocket ); | 484 | io_close( clientsocket ); |
| 497 | free( h ); | 485 | free( h ); |
| 498 | } | 486 | } |
| 499 | } | 487 | } |
| 500 | 488 | ||
| 501 | void handle_accept( int64 serversocket ) { | 489 | static void handle_accept( const int64 serversocket ) { |
| 502 | struct http_data* h; | 490 | struct http_data *h; |
| 503 | unsigned char ip[4]; | 491 | unsigned char ip[4]; |
| 504 | uint16 port; | 492 | uint16 port; |
| 505 | tai6464 t; | 493 | tai6464 t; |
| @@ -513,11 +501,6 @@ void handle_accept( int64 serversocket ) { | |||
| 513 | continue; | 501 | continue; |
| 514 | } | 502 | } |
| 515 | 503 | ||
| 516 | #ifdef _DEBUG_FDS | ||
| 517 | if( fd_debug_space[i] ) fprintf( stderr, "double use of fd: %i\n", (int)i ); | ||
| 518 | fd_debug_space[i] = 1; | ||
| 519 | #endif | ||
| 520 | |||
| 521 | io_wantread( i ); | 504 | io_wantread( i ); |
| 522 | 505 | ||
| 523 | byte_zero(h,sizeof(struct http_data)); | 506 | byte_zero(h,sizeof(struct http_data)); |
| @@ -531,13 +514,9 @@ void handle_accept( int64 serversocket ) { | |||
| 531 | 514 | ||
| 532 | if( errno==EAGAIN ) | 515 | if( errno==EAGAIN ) |
| 533 | io_eagain( serversocket ); | 516 | io_eagain( serversocket ); |
| 534 | /* | ||
| 535 | else | ||
| 536 | carp( "socket_accept4" ); | ||
| 537 | */ | ||
| 538 | } | 517 | } |
| 539 | 518 | ||
| 540 | void handle_timeouted( ) { | 519 | static void handle_timeouted( void ) { |
| 541 | int64 i; | 520 | int64 i; |
| 542 | while( ( i = io_timeouted() ) != -1 ) { | 521 | while( ( i = io_timeouted() ) != -1 ) { |
| 543 | struct http_data* h=io_getcookie( i ); | 522 | struct http_data* h=io_getcookie( i ); |
| @@ -545,15 +524,11 @@ void handle_timeouted( ) { | |||
| 545 | array_reset( &h->request ); | 524 | array_reset( &h->request ); |
| 546 | free( h ); | 525 | free( h ); |
| 547 | } | 526 | } |
| 548 | #ifdef _DEBUG_FDS | ||
| 549 | if( !fd_debug_space[i] ) fprintf( stderr, "close on non-open fd\n" ); | ||
| 550 | fd_debug_space[i] = 0; | ||
| 551 | #endif | ||
| 552 | io_close(i); | 527 | io_close(i); |
| 553 | } | 528 | } |
| 554 | } | 529 | } |
| 555 | 530 | ||
| 556 | void server_mainloop( int64 serversocket ) { | 531 | static void server_mainloop( const int64 serversocket ) { |
| 557 | tai6464 t, next_timeout_check; | 532 | tai6464 t, next_timeout_check; |
| 558 | 533 | ||
| 559 | io_wantread( serversocket ); | 534 | io_wantread( serversocket ); |
| @@ -626,9 +601,6 @@ int main( int argc, char **argv ) { | |||
| 626 | 601 | ||
| 627 | signal( SIGPIPE, SIG_IGN ); | 602 | signal( SIGPIPE, SIG_IGN ); |
| 628 | signal( SIGINT, graceful ); | 603 | signal( SIGINT, graceful ); |
| 629 | #ifdef _DEBUG_FDS | ||
| 630 | signal( SIGINFO, count_fds ); | ||
| 631 | #endif | ||
| 632 | if( init_logic( serverdir ) == -1 ) | 604 | if( init_logic( serverdir ) == -1 ) |
| 633 | panic( "Logic not started" ); | 605 | panic( "Logic not started" ); |
| 634 | 606 | ||
