summaryrefslogtreecommitdiff
path: root/opentracker.c
diff options
context:
space:
mode:
Diffstat (limited to 'opentracker.c')
-rw-r--r--opentracker.c656
1 files changed, 38 insertions, 618 deletions
diff --git a/opentracker.c b/opentracker.c
index debb868..cd6205c 100644
--- a/opentracker.c
+++ b/opentracker.c
@@ -6,9 +6,8 @@
6/* System */ 6/* System */
7#include <string.h> 7#include <string.h>
8#include <sys/types.h> 8#include <sys/types.h>
9#include <sys/stat.h>
10#include <sys/socket.h> 9#include <sys/socket.h>
11#include <arpa/inet.h> 10//#include <arpa/inet.h>
12#include <unistd.h> 11#include <unistd.h>
13#include <stdlib.h> 12#include <stdlib.h>
14#include <errno.h> 13#include <errno.h>
@@ -20,605 +19,35 @@
20#include "socket.h" 19#include "socket.h"
21#include "io.h" 20#include "io.h"
22#include "iob.h" 21#include "iob.h"
23#include "buffer.h"
24#include "array.h" 22#include "array.h"
25#include "byte.h"
26#include "case.h"
27#include "fmt.h" 23#include "fmt.h"
28#include "str.h"
29#include "scan.h" 24#include "scan.h"
30#include "ip4.h" 25#include "ip4.h"
31 26
32/* Opentracker */ 27/* Opentracker */
33#include "trackerlogic.h" 28#include "trackerlogic.h"
34#include "scan_urlencoded_query.h"
35#include "ot_stats.h"
36#include "ot_sync.h"
37#include "ot_udp.h"
38#include "ot_fullscrape.h"
39#include "ot_iovec.h" 29#include "ot_iovec.h"
40#include "ot_accesslist.h"
41#include "ot_mutex.h" 30#include "ot_mutex.h"
31#include "ot_http.h"
32#include "ot_udp.h"
42#include "ot_clean.h" 33#include "ot_clean.h"
34#include "ot_accesslist.h"
35#include "ot_stats.h"
43 36
44/* Globals */ 37/* Globals */
45static const size_t SUCCESS_HTTP_HEADER_LENGTH = 80;
46static const size_t SUCCESS_HTTP_HEADER_LENGHT_CONTENT_ENCODING = 32;
47static const size_t SUCCESS_HTTP_SIZE_OFF = 17;
48static uint32_t g_adminip_addresses[OT_ADMINIP_MAX];
49static unsigned int g_adminip_count = 0;
50static time_t ot_last_clean_time;
51time_t ot_start_time;
52time_t g_now; 38time_t g_now;
53 39
54#ifndef WANT_TRACKER_SYNC
55#define add_peer_to_torrent(A,B,C) add_peer_to_torrent(A,B)
56#endif
57
58#ifndef NO_FULLSCRAPE_LOGGING
59#define LOG_TO_STDERR( ... ) fprintf( stderr, __VA_ARGS__ )
60#else
61#define LOG_TO_STDERR( ... )
62#endif
63
64/* To always have space for error messages ;) */ 40/* To always have space for error messages ;) */
65static char static_inbuf[8192]; 41static char static_inbuf[8192];
66static char static_outbuf[8192];
67
68#define OT_MAXMULTISCRAPE_COUNT 64
69static ot_hash multiscrape_buf[OT_MAXMULTISCRAPE_COUNT];
70
71static char *FLAG_TCP = "TCP";
72static char *FLAG_UDP = "UDP";
73static size_t ot_sockets_count = 0;
74
75#ifdef _DEBUG_HTTPERROR
76static char debug_request[8192];
77#endif
78 42
79typedef enum { 43static char *FLAG_TCP = "T";
80 STRUCT_HTTP_FLAG_ARRAY_USED = 1, 44static char *FLAG_UDP = "U";
81 STRUCT_HTTP_FLAG_IOB_USED = 2,
82 STRUCT_HTTP_FLAG_WAITINGFORTASK = 4,
83 STRUCT_HTTP_FLAG_GZIP = 8,
84 STRUCT_HTTP_FLAG_BZIP2 = 16
85} STRUCT_HTTP_FLAG;
86
87struct http_data {
88 union {
89 array request;
90 io_batch batch;
91 };
92 unsigned char ip[4];
93 STRUCT_HTTP_FLAG flag;
94};
95#define NOTBLESSED( h ) (!bsearch( &h->ip, g_adminip_addresses, g_adminip_count, 4, ot_ip_compare ))
96static int ot_ip_compare( const void *a, const void *b ) { return memcmp( a,b,4 ); }
97
98/* Prototypes */
99
100int main( int argc, char **argv );
101
102static void httperror( const int64 s, const char *title, const char *message );
103static void httpresponse( const int64 s, char *data, size_t l );
104
105static void sendiovecdata( const int64 s, int iovec_entries, struct iovec *iovector );
106static void senddata( const int64 s, char *buffer, const size_t size );
107
108static void server_mainloop( );
109static void handle_timeouted( void );
110static void handle_accept( const int64 serversocket );
111static void handle_read( const int64 clientsocket );
112static void handle_write( const int64 clientsocket );
113
114static void ot_try_bind( char ip[4], uint16 port, int is_tcp );
115
116static void usage( char *name );
117static void help( char *name );
118
119static void carp( const char *routine );
120static void panic( const char *routine );
121static void signal_handler( int s );
122
123#define HTTPERROR_400 return httperror( s, "400 Invalid Request", "This server only understands GET." )
124#define HTTPERROR_400_PARAM return httperror( s, "400 Invalid Request", "Invalid parameter" )
125#define HTTPERROR_400_COMPACT return httperror( s, "400 Invalid Request", "This server only delivers compact results." )
126#define HTTPERROR_403_IP return httperror( s, "403 Access Denied", "Your ip address is not allowed to administrate this server." )
127#define HTTPERROR_404 return httperror( s, "404 Not Found", "No such file or directory." )
128#define HTTPERROR_500 return httperror( s, "500 Internal Server Error", "A server error has occured. Please retry later." )
129
130/* End of prototypes */
131
132static void carp( const char *routine ) {
133 buffer_puts( buffer_2, routine );
134 buffer_puts( buffer_2, ": " );
135 buffer_puterror( buffer_2 );
136 buffer_putnlflush( buffer_2 );
137}
138 45
139static void panic( const char *routine ) { 46static void panic( const char *routine ) {
140 carp( routine ); 47 fprintf( stderr, "%s: %s\n", routine, strerror(errno) );
141 exit( 111 ); 48 exit( 111 );
142} 49}
143 50
144static void httperror( const int64 s, const char *title, const char *message ) {
145 size_t reply_size = sprintf( static_outbuf, "HTTP/1.0 %s\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: %zd\r\n\r\n<title>%s</title>\n",
146 title, strlen(message)+strlen(title)+16-4,title+4);
147#ifdef _DEBUG_HTTPERROR
148 fprintf( stderr, "DEBUG: invalid request was: %s\n", debug_request );
149#endif
150 senddata(s,static_outbuf,reply_size);
151}
152
153static void sendiovecdata( const int64 s, int iovec_entries, struct iovec *iovector ) {
154 struct http_data *h = io_getcookie( s );
155 char *header;
156 int i;
157 size_t header_size, size = iovec_length( &iovec_entries, &iovector );
158 tai6464 t;
159
160 /* No cookie? Bad socket. Leave. */
161 if( !h ) {
162 iovec_free( &iovec_entries, &iovector );
163 HTTPERROR_500;
164 }
165
166 /* If this socket collected request in a buffer,
167 free it now */
168 if( h->flag & STRUCT_HTTP_FLAG_ARRAY_USED ) {
169 h->flag &= ~STRUCT_HTTP_FLAG_ARRAY_USED;
170 array_reset( &h->request );
171 }
172
173 /* If we came here, wait for the answer is over */
174 h->flag &= ~STRUCT_HTTP_FLAG_WAITINGFORTASK;
175
176 /* Our answers never are 0 vectors. Return an error. */
177 if( !iovec_entries ) {
178 HTTPERROR_500;
179 }
180
181 /* Prepare space for http header */
182 header = malloc( SUCCESS_HTTP_HEADER_LENGTH + SUCCESS_HTTP_HEADER_LENGHT_CONTENT_ENCODING );
183 if( !header ) {
184 iovec_free( &iovec_entries, &iovector );
185 HTTPERROR_500;
186 }
187
188 if( h->flag & STRUCT_HTTP_FLAG_GZIP )
189 header_size = sprintf( header, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Encoding: gzip\r\nContent-Length: %zd\r\n\r\n", size );
190 else if( h->flag & STRUCT_HTTP_FLAG_BZIP2 )
191 header_size = sprintf( header, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Encoding: bzip2\r\nContent-Length: %zd\r\n\r\n", size );
192 else
193 header_size = sprintf( header, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r\n", size );
194
195 iob_reset( &h->batch );
196 iob_addbuf_free( &h->batch, header, header_size );
197
198 /* Will move to ot_iovec.c */
199 for( i=0; i<iovec_entries; ++i )
200 iob_addbuf_munmap( &h->batch, iovector[i].iov_base, iovector[i].iov_len );
201 free( iovector );
202
203 h->flag |= STRUCT_HTTP_FLAG_IOB_USED;
204
205 /* writeable sockets timeout after twice the pool timeout
206 which defaults to 5 minutes (e.g. after 10 minutes) */
207 taia_now( &t ); taia_addsec( &t, &t, OT_CLIENT_TIMEOUT_SEND );
208 io_timeout( s, t );
209 io_dontwantread( s );
210 io_wantwrite( s );
211}
212
213static void senddata( const int64 s, char *buffer, size_t size ) {
214 struct http_data *h = io_getcookie( s );
215 ssize_t written_size;
216
217 /* whoever sends data is not interested in its input-array */
218 if( h && ( h->flag & STRUCT_HTTP_FLAG_ARRAY_USED ) ) {
219 h->flag &= ~STRUCT_HTTP_FLAG_ARRAY_USED;
220 array_reset( &h->request );
221 }
222
223 written_size = write( s, buffer, size );
224 if( ( written_size < 0 ) || ( (size_t)written_size == size ) ) {
225 free( h ); io_close( s );
226 } else {
227 char * outbuf;
228 tai6464 t;
229
230 if( !h ) return;
231 if( !( outbuf = malloc( size - written_size ) ) ) {
232 free(h); io_close( s );
233 return;
234 }
235
236 iob_reset( &h->batch );
237 memmove( outbuf, buffer + written_size, size - written_size );
238 iob_addbuf_free( &h->batch, outbuf, size - written_size );
239 h->flag |= STRUCT_HTTP_FLAG_IOB_USED;
240
241 /* writeable short data sockets just have a tcp timeout */
242 taia_uint( &t, 0 ); io_timeout( s, t );
243 io_dontwantread( s );
244 io_wantwrite( s );
245 }
246}
247
248static void httpresponse( const int64 s, char *data, size_t l ) {
249 struct http_data* h = io_getcookie( s );
250 char *c, *d=data;
251 ot_peer peer;
252 ot_torrent *torrent;
253 ot_hash *hash = NULL;
254 int numwant, tmp, scanon, mode;
255 ot_tasktype format = TASK_FULLSCRAPE;
256 unsigned short port = htons(6881);
257 ssize_t len;
258 size_t reply_size = 0, reply_off;
259 tai6464 t;
260
261 /* Touch l and d in case it is unused */
262 l = l; d = d;
263
264#ifdef _DEBUG_HTTPERROR
265 if( l >= sizeof( debug_request ) )
266 l = sizeof( debug_request) - 1;
267 memcpy( debug_request, data, l );
268 debug_request[ l ] = 0;
269#endif
270
271 /* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */
272 if( byte_diff( data, 5, "GET /") ) HTTPERROR_400;
273
274 /* Query string MUST terminate with SP -- we know that theres at least a '\n' where this search terminates */
275 for( c = data + 5; *c!=' ' && *c != '\t' && *c != '\n' && *c != '\r'; ++c ) ;
276 if( *c != ' ' ) HTTPERROR_400;
277
278 /* Skip leading '/' */
279 for( c = data+4; *c == '/'; ++c);
280
281 switch( scan_urlencoded_query( &c, data = c, SCAN_PATH ) ) {
282#ifdef WANT_TRACKER_SYNC
283/******************************
284 * S Y N C *
285 ******************************/
286 case 4: /* sync ? */
287 if( *data == 'a' ) goto ANNOUNCE_WORKAROUND;
288 if( !byte_diff( data, 2, "sc" ) ) goto SCRAPE_WORKAROUND;
289 if( byte_diff( data, 4, "sync") ) HTTPERROR_404;
290 if( NOTBLESSED( h ) ) HTTPERROR_403_IP;
291
292LOG_TO_STDERR( "sync: %d.%d.%d.%d\n", h->ip[0], h->ip[1], h->ip[2], h->ip[3] );
293
294 mode = SYNC_OUT;
295 scanon = 1;
296
297 while( scanon ) {
298 switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) {
299 case -2: scanon = 0; break; /* TERMINATOR */
300 case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */
301 default: scan_urlencoded_skipvalue( &c ); break;
302 case 9:
303 if(byte_diff(data,9,"changeset")) {
304 scan_urlencoded_skipvalue( &c );
305 continue;
306 }
307 /* ignore this, when we dont at least see "d4:syncdee" */
308 if( ( len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) < 10 ) HTTPERROR_400_PARAM;
309 if( add_changeset_to_tracker( (ot_byte*)data, len ) ) HTTPERROR_400_PARAM;
310 mode = SYNC_IN;
311 break;
312 }
313 }
314
315 if( mode == SYNC_OUT ) {
316 /* Pass this task to the worker thread */
317 h->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK;
318 sync_deliver( s );
319 io_dontwantread( s );
320 return;
321 }
322
323 /* Simple but proof for now */
324 memmove( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH, "OK", 2);
325 reply_size = 2;
326
327 break;
328#endif
329/******************************
330 * S T A T S *
331 ******************************/
332 case 5: /* stats ? */
333 if( *data == 'a' ) goto ANNOUNCE_WORKAROUND;
334 if( !byte_diff( data, 2, "sc" ) ) goto SCRAPE_WORKAROUND;
335 if( byte_diff(data,5,"stats")) HTTPERROR_404;
336 scanon = 1;
337 mode = TASK_STATS_PEERS;
338
339 while( scanon ) {
340 switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) {
341 case -2: scanon = 0; break; /* TERMINATOR */
342 case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */
343 default: scan_urlencoded_skipvalue( &c ); break;
344 case 4:
345 if( byte_diff(data,4,"mode")) {
346 scan_urlencoded_skipvalue( &c );
347 continue;
348 }
349 if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 4 ) HTTPERROR_400_PARAM;
350 if( !byte_diff(data,4,"peer"))
351 mode = TASK_STATS_PEERS;
352 else if( !byte_diff(data,4,"conn"))
353 mode = TASK_STATS_CONNS;
354 else if( !byte_diff(data,4,"scrp"))
355 mode = TASK_STATS_SCRAPE;
356 else if( !byte_diff(data,4,"top5"))
357 mode = TASK_STATS_TOP5;
358 else if( !byte_diff(data,4,"fscr"))
359 mode = TASK_STATS_FULLSCRAPE;
360 else if( !byte_diff(data,4,"tcp4"))
361 mode = TASK_STATS_TCP;
362 else if( !byte_diff(data,4,"udp4"))
363 mode = TASK_STATS_UDP;
364 else if( !byte_diff(data,4,"s24s"))
365 mode = TASK_STATS_SLASH24S;
366 else if( !byte_diff(data,4,"tpbs"))
367 mode = TASK_STATS_TPB;
368 else
369 HTTPERROR_400_PARAM;
370 break;
371 case 6:
372 if( byte_diff(data,6,"format")) {
373 scan_urlencoded_skipvalue( &c );
374 continue;
375 }
376 if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 3 ) HTTPERROR_400_PARAM;
377 if( !byte_diff(data,3,"bin"))
378 format = TASK_FULLSCRAPE_TPB_BINARY;
379 else if( !byte_diff(data,3,"ben"))
380 format = TASK_FULLSCRAPE;
381 else if( !byte_diff(data,3,"url"))
382 format = TASK_FULLSCRAPE_TPB_URLENCODED;
383 else if( !byte_diff(data,3,"txt"))
384 format = TASK_FULLSCRAPE_TPB_ASCII;
385 else
386 HTTPERROR_400_PARAM;
387 break;
388 }
389 }
390
391 if( mode == TASK_STATS_TPB ) {
392#ifdef WANT_COMPRESSION_GZIP
393 d[l-1] = 0;
394 if( strstr( d, "gzip" ) ) {
395 h->flag |= STRUCT_HTTP_FLAG_GZIP;
396 format |= TASK_FLAG_GZIP;
397 }
398#endif
399 /* Pass this task to the worker thread */
400 h->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK;
401
402 /* Clients waiting for us should not easily timeout */
403 taia_uint( &t, 0 ); io_timeout( s, t );
404 fullscrape_deliver( s, format );
405 io_dontwantread( s );
406 return;
407 }
408
409 // default format for now
410 if( !( reply_size = return_stats_for_tracker( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH, mode, 0 ) ) ) HTTPERROR_500;
411 break;
412
413/******************************
414 * S C R A P E *
415 ******************************/
416 case 6: /* scrape ? */
417 if( *data == 'a' ) goto ANNOUNCE_WORKAROUND;
418 if( byte_diff( data, 6, "scrape") ) HTTPERROR_404;
419
420 /* Full scrape... you might want to limit that */
421 if( !byte_diff( data, 12, "scrape HTTP/" ) ) {
422 format = 0;
423#ifdef WANT_COMPRESSION_GZIP
424 d[l-1] = 0;
425 if( strstr( d, "gzip" ) ) {
426 h->flag |= STRUCT_HTTP_FLAG_GZIP;
427 format = TASK_FLAG_GZIP;
428LOG_TO_STDERR( "[%08d] scrp: %d.%d.%d.%d - FULL SCRAPE GZIP\n", (unsigned int)(g_now - ot_start_time), h->ip[0], h->ip[1], h->ip[2], h->ip[3] );
429 } else
430#endif
431 LOG_TO_STDERR( "[%08d] scrp: %d.%d.%d.%d - FULL SCRAPE\n", (unsigned int)(g_now - ot_start_time), h->ip[0], h->ip[1], h->ip[2], h->ip[3] );
432
433#ifdef _DEBUG_HTTPERROR
434write( 2, debug_request, l );
435#endif
436
437 /* Pass this task to the worker thread */
438 h->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK;
439 /* Clients waiting for us should not easily timeout */
440 taia_uint( &t, 0 ); io_timeout( s, t );
441 fullscrape_deliver( s, TASK_FULLSCRAPE | format );
442 io_dontwantread( s );
443 return;
444 }
445
446SCRAPE_WORKAROUND:
447
448 /* This is to hack around stupid clients that send "scrape ?info_hash" */
449 if( c[-1] != '?' ) {
450 while( ( *c != '?' ) && ( *c != '\n' ) ) ++c;
451 if( *c == '\n' ) HTTPERROR_400_PARAM;
452 ++c;
453 }
454
455 scanon = 1;
456 numwant = 0;
457 while( scanon ) {
458 switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) {
459 case -2: scanon = 0; break; /* TERMINATOR */
460 case -1:
461 if( numwant )
462 goto UTORRENT1600_WORKAROUND;
463 HTTPERROR_400_PARAM; /* PARSE ERROR */
464 default: scan_urlencoded_skipvalue( &c ); break;
465 case 9:
466 if(byte_diff(data,9,"info_hash")) {
467 scan_urlencoded_skipvalue( &c );
468 continue;
469 }
470 /* ignore this, when we have less than 20 bytes */
471 if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != (ssize_t)sizeof(ot_hash) ) {
472#ifdef WANT_UTORRENT1600_WORKAROUND
473 if( data[20] != '?' )
474#endif
475 HTTPERROR_400_PARAM;
476 }
477 if( numwant < OT_MAXMULTISCRAPE_COUNT )
478 memmove( multiscrape_buf + numwant++, data, sizeof(ot_hash) );
479 break;
480 }
481 }
482
483UTORRENT1600_WORKAROUND:
484
485 /* No info_hash found? Inform user */
486 if( !numwant ) HTTPERROR_400_PARAM;
487
488 /* Enough for http header + whole scrape string */
489 if( !( reply_size = return_tcp_scrape_for_torrent( multiscrape_buf, numwant, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf ) ) ) HTTPERROR_500;
490 stats_issue_event( EVENT_SCRAPE, 1, reply_size );
491 break;
492/******************************
493 * A N N O U N C E *
494 ******************************/
495 case 8:
496 if( !byte_diff( data, 2, "sc" ) ) goto SCRAPE_WORKAROUND;
497 if( *data != 'a' ) HTTPERROR_404;
498
499ANNOUNCE_WORKAROUND:
500
501 /* This is to hack around stupid clients that send "announce ?info_hash" */
502 if( c[-1] != '?' ) {
503 while( ( *c != '?' ) && ( *c != '\n' ) ) ++c;
504 if( *c == '\n' ) HTTPERROR_400_PARAM;
505 ++c;
506 }
507
508 OT_SETIP( &peer, ((struct http_data*)io_getcookie( s ) )->ip );
509 OT_SETPORT( &peer, &port );
510 OT_FLAG( &peer ) = 0;
511 numwant = 50;
512 scanon = 1;
513
514 while( scanon ) {
515 switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) {
516 case -2: scanon = 0; break; /* TERMINATOR */
517 case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */
518 default: scan_urlencoded_skipvalue( &c ); break;
519#ifdef WANT_IP_FROM_QUERY_STRING
520 case 2:
521 if(!byte_diff(data,2,"ip")) {
522 unsigned char ip[4];
523 len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
524 if( ( len <= 0 ) || scan_fixed_ip( data, len, ip ) ) HTTPERROR_400_PARAM;
525 OT_SETIP( &peer, ip );
526 } else
527 scan_urlencoded_skipvalue( &c );
528 break;
529#endif
530 case 4:
531 if( !byte_diff( data, 4, "port" ) ) {
532 len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
533 if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) || ( tmp > 0xffff ) ) HTTPERROR_400_PARAM;
534 port = htons( tmp ); OT_SETPORT( &peer, &port );
535 } else if( !byte_diff( data, 4, "left" ) ) {
536 if( ( len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM;
537 if( scan_fixed_int( data, len, &tmp ) ) tmp = 0;
538 if( !tmp ) OT_FLAG( &peer ) |= PEER_FLAG_SEEDING;
539 } else
540 scan_urlencoded_skipvalue( &c );
541 break;
542 case 5:
543 if( byte_diff( data, 5, "event" ) )
544 scan_urlencoded_skipvalue( &c );
545 else switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) {
546 case -1:
547 HTTPERROR_400_PARAM;
548 case 7:
549 if( !byte_diff( data, 7, "stopped" ) ) OT_FLAG( &peer ) |= PEER_FLAG_STOPPED;
550 break;
551 case 9:
552 if( !byte_diff( data, 9, "completed" ) ) OT_FLAG( &peer ) |= PEER_FLAG_COMPLETED;
553 default: /* Fall through intended */
554 break;
555 }
556 break;
557 case 7:
558 if(!byte_diff(data,7,"numwant")) {
559 len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
560 if( ( len <= 0 ) || scan_fixed_int( data, len, &numwant ) ) HTTPERROR_400_PARAM;
561 if( numwant < 0 ) numwant = 50;
562 if( numwant > 200 ) numwant = 200;
563 } else if(!byte_diff(data,7,"compact")) {
564 len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
565 if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) ) HTTPERROR_400_PARAM;
566 if( !tmp ) HTTPERROR_400_COMPACT;
567 } else
568 scan_urlencoded_skipvalue( &c );
569 break;
570 case 9:
571 if(byte_diff(data,9,"info_hash")) {
572 scan_urlencoded_skipvalue( &c );
573 continue;
574 }
575 /* ignore this, when we have less than 20 bytes */
576 if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM;
577 hash = (ot_hash*)data;
578 break;
579 }
580 }
581
582 /* Scanned whole query string */
583 if( !hash ) {
584 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" );
585 break;
586 }
587 if( OT_FLAG( &peer ) & PEER_FLAG_STOPPED )
588 reply_size = remove_peer_from_torrent( hash, &peer, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf, 1 );
589 else {
590 torrent = add_peer_to_torrent( hash, &peer, 0 );
591 if( !torrent || !( reply_size = return_peers_for_torrent( hash, numwant, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf, 1 ) ) ) HTTPERROR_500;
592 }
593 stats_issue_event( EVENT_ANNOUNCE, 1, reply_size);
594 break;
595 default:
596 if( ( *data == 'a' ) || ( *data == '?' ) ) goto ANNOUNCE_WORKAROUND;
597 if( !byte_diff( data, 2, "sc" ) ) goto SCRAPE_WORKAROUND;
598 HTTPERROR_404;
599 }
600
601 if( !reply_size ) HTTPERROR_500;
602
603 /* This one is rather ugly, so I take you step by step through it.
604
605 1. In order to avoid having two buffers, one for header and one for content, we allow all above functions from trackerlogic to
606 write to a fixed location, leaving SUCCESS_HTTP_HEADER_LENGTH bytes in our static buffer, which is enough for the static string
607 plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for its expansion and calculate
608 the space NOT needed to expand in reply_off
609 */
610 reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf( static_outbuf, 0, "%zd", reply_size );
611
612 /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete
613 packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */
614 reply_size += 1 + sprintf( static_outbuf + reply_off, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", reply_size );
615
616 /* 3. Finally we join both blocks neatly */
617 static_outbuf[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n';
618
619 senddata( s, static_outbuf + reply_off, reply_size );
620}
621
622static void signal_handler( int s ) { 51static void signal_handler( int s ) {
623 if( s == SIGINT ) { 52 if( s == SIGINT ) {
624 signal( SIGINT, SIG_IGN); 53 signal( SIGINT, SIG_IGN);
@@ -674,36 +103,37 @@ static void handle_dead( const int64 socket ) {
674 io_close( socket ); 103 io_close( socket );
675} 104}
676 105
677static void handle_read( const int64 clientsocket ) { 106static ssize_t handle_read( const int64 clientsocket ) {
678 struct http_data* h = io_getcookie( clientsocket ); 107 struct http_data* h = io_getcookie( clientsocket );
679 ssize_t l; 108 ssize_t l;
680 109
681 if( ( l = io_tryread( clientsocket, static_inbuf, sizeof static_inbuf ) ) <= 0 ) 110 if( ( l = io_tryread( clientsocket, static_inbuf, sizeof static_inbuf ) ) <= 0 ) {
682 return handle_dead( clientsocket ); 111 handle_dead( clientsocket );
683 112 return 0;
684#ifdef _DEBUG_HTTPERROR 113 }
685 memcpy( debug_request, "500!\0", 5 );
686#endif
687 114
688 /* If we get the whole request in one packet, handle it without copying */ 115 /* If we get the whole request in one packet, handle it without copying */
689 if( !array_start( &h->request ) ) { 116 if( !array_start( &h->request ) ) {
690 if( memchr( static_inbuf, '\n', l ) ) 117 if( memchr( static_inbuf, '\n', l ) )
691 return httpresponse( clientsocket, static_inbuf, l ); 118 return http_handle_request( clientsocket, static_inbuf, l );
692 h->flag |= STRUCT_HTTP_FLAG_ARRAY_USED; 119 h->flag |= STRUCT_HTTP_FLAG_ARRAY_USED;
693 return array_catb( &h->request, static_inbuf, l ); 120 array_catb( &h->request, static_inbuf, l );
121 return 0;
694 } 122 }
695 123
696 h->flag |= STRUCT_HTTP_FLAG_ARRAY_USED; 124 h->flag |= STRUCT_HTTP_FLAG_ARRAY_USED;
697 array_catb( &h->request, static_inbuf, l ); 125 array_catb( &h->request, static_inbuf, l );
698 126
699 if( array_failed( &h->request ) ) 127 if( array_failed( &h->request ) )
700 return httperror( clientsocket, "500 Server Error", "Request too long."); 128 return http_issue_error( clientsocket, "500 Server Error", "Request too long.");
701 129
702 if( ( array_bytes( &h->request ) > 8192 ) && NOTBLESSED( h ) ) 130 if( ( array_bytes( &h->request ) > 8192 ) && !accesslist_isblessed( (char*)&h->ip, OT_PERMISSION_MAY_SYNC ) )
703 return httperror( clientsocket, "500 request too long", "You sent too much headers"); 131 return http_issue_error( clientsocket, "500 request too long", "You sent too much headers");
704 132
705 if( memchr( array_start( &h->request ), '\n', array_bytes( &h->request ) ) ) 133 if( memchr( array_start( &h->request ), '\n', array_bytes( &h->request ) ) )
706 return httpresponse( clientsocket, array_start( &h->request ), array_bytes( &h->request ) ); 134 return http_handle_request( clientsocket, array_start( &h->request ), array_bytes( &h->request ) );
135
136 return 0;
707} 137}
708 138
709static void handle_write( const int64 clientsocket ) { 139static void handle_write( const int64 clientsocket ) {
@@ -732,7 +162,7 @@ static void handle_accept( const int64 serversocket ) {
732 io_setcookie( i, h ); 162 io_setcookie( i, h );
733 io_wantread( i ); 163 io_wantread( i );
734 164
735 byte_zero( h, sizeof( struct http_data ) ); 165 memset( h, 0, sizeof( struct http_data ) );
736 memmove( h->ip, ip, sizeof( ip ) ); 166 memmove( h->ip, ip, sizeof( ip ) );
737 167
738 stats_issue_event( EVENT_ACCEPT, 1, 0); 168 stats_issue_event( EVENT_ACCEPT, 1, 0);
@@ -748,16 +178,11 @@ static void handle_accept( const int64 serversocket ) {
748 io_eagain( serversocket ); 178 io_eagain( serversocket );
749} 179}
750 180
751static void handle_timeouted( void ) {
752 int64 i;
753 while( ( i = io_timeouted() ) != -1 )
754 handle_dead( i );
755}
756
757static void server_mainloop( ) { 181static void server_mainloop( ) {
758 time_t next_timeout_check = g_now + OT_CLIENT_TIMEOUT_CHECKINTERVAL; 182 static time_t ot_last_clean_time;
759 struct iovec *iovector; 183 time_t next_timeout_check = g_now + OT_CLIENT_TIMEOUT_CHECKINTERVAL;
760 int iovec_entries; 184 struct iovec *iovector;
185 int iovec_entries;
761 186
762 for( ; ; ) { 187 for( ; ; ) {
763 int64 i; 188 int64 i;
@@ -775,13 +200,14 @@ static void server_mainloop( ) {
775 } 200 }
776 201
777 while( ( i = mutex_workqueue_popresult( &iovec_entries, &iovector ) ) != -1 ) 202 while( ( i = mutex_workqueue_popresult( &iovec_entries, &iovector ) ) != -1 )
778 sendiovecdata( i, iovec_entries, iovector ); 203 http_sendiovecdata( i, iovec_entries, iovector );
779 204
780 while( ( i = io_canwrite( ) ) != -1 ) 205 while( ( i = io_canwrite( ) ) != -1 )
781 handle_write( i ); 206 handle_write( i );
782 207
783 if( g_now > next_timeout_check ) { 208 if( g_now > next_timeout_check ) {
784 handle_timeouted( ); 209 while( ( i = io_timeouted() ) != -1 )
210 handle_dead( i );
785 next_timeout_check = g_now + OT_CLIENT_TIMEOUT_CHECKINTERVAL; 211 next_timeout_check = g_now + OT_CLIENT_TIMEOUT_CHECKINTERVAL;
786 } 212 }
787 213
@@ -811,15 +237,13 @@ static void ot_try_bind( char ip[4], uint16 port, int is_tcp ) {
811 io_setcookie( s, is_tcp ? FLAG_TCP : FLAG_UDP ); 237 io_setcookie( s, is_tcp ? FLAG_TCP : FLAG_UDP );
812 238
813 io_wantread( s ); 239 io_wantread( s );
814
815 ++ot_sockets_count;
816} 240}
817 241
818int main( int argc, char **argv ) { 242int main( int argc, char **argv ) {
819 struct passwd *pws = NULL; 243 struct passwd *pws = NULL;
820 char serverip[4] = {0,0,0,0}; 244 char serverip[4] = {0,0,0,0}, tmpip[4];
821 char *serverdir = "."; 245 char *serverdir = ".";
822 int scanon = 1; 246 int bound = 0, scanon = 1;
823#ifdef WANT_ACCESS_CONTROL 247#ifdef WANT_ACCESS_CONTROL
824 char *accesslist_filename = NULL; 248 char *accesslist_filename = NULL;
825#endif 249#endif
@@ -839,12 +263,12 @@ int main( int argc, char **argv ) {
839#elif defined( WANT_CLOSED_TRACKER ) 263#elif defined( WANT_CLOSED_TRACKER )
840 case 'w': accesslist_filename = optarg; break; 264 case 'w': accesslist_filename = optarg; break;
841#endif 265#endif
842 case 'p': ot_try_bind( serverip, (uint16)atol( optarg ), 1 ); break; 266 case 'p': ot_try_bind( serverip, (uint16)atol( optarg ), 1 ); bound++; break;
843 case 'P': ot_try_bind( serverip, (uint16)atol( optarg ), 0 ); break; 267 case 'P': ot_try_bind( serverip, (uint16)atol( optarg ), 0 ); bound++; break;
844 case 'd': serverdir = optarg; break; 268 case 'd': serverdir = optarg; break;
845 case 'A': 269 case 'A':
846 if( g_adminip_count < OT_ADMINIP_MAX ) 270 scan_ip4( optarg, tmpip );
847 scan_ip4( optarg, (char*)(g_adminip_addresses + g_adminip_count++) ); 271 accesslist_blessip( tmpip, 0xffff ); /* Allow everything for now */
848 break; 272 break;
849 case 'h': help( argv[0] ); exit( 0 ); 273 case 'h': help( argv[0] ); exit( 0 );
850 default: 274 default:
@@ -852,11 +276,8 @@ int main( int argc, char **argv ) {
852 } 276 }
853 } 277 }
854 278
855 /* Sort our admin ips for quick lookup */
856 qsort( g_adminip_addresses, g_adminip_count, 4, ot_ip_compare );
857
858 /* Bind to our default tcp/udp ports */ 279 /* Bind to our default tcp/udp ports */
859 if( !ot_sockets_count ) { 280 if( !bound) {
860 ot_try_bind( serverip, 6969, 1 ); 281 ot_try_bind( serverip, 6969, 1 );
861 ot_try_bind( serverip, 6969, 0 ); 282 ot_try_bind( serverip, 6969, 0 );
862 } 283 }
@@ -881,8 +302,7 @@ int main( int argc, char **argv ) {
881 if( trackerlogic_init( serverdir ) == -1 ) 302 if( trackerlogic_init( serverdir ) == -1 )
882 panic( "Logic not started" ); 303 panic( "Logic not started" );
883 304
884 g_now = ot_start_time = time( NULL ); 305 g_now = time( NULL );
885 ot_last_clean_time = NOW;
886 alarm(5); 306 alarm(5);
887 307
888 server_mainloop( ); 308 server_mainloop( );