summaryrefslogtreecommitdiff
path: root/opentracker.c
diff options
context:
space:
mode:
Diffstat (limited to 'opentracker.c')
-rw-r--r--opentracker.c356
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 */
29static unsigned int ot_overall_connections = 0; 30static unsigned int ot_overall_connections = 0;
30static unsigned int ot_overall_successfulannounces = 0; 31static unsigned int ot_overall_successfulannounces = 0;
31static time_t ot_start_time; 32static 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 ;) */
35static char static_scratch[8192]; 36static char static_scratch[8192];
36 37
37#ifdef _DEBUG_FDS
38static char fd_debug_space[0x10000];
39#endif
40#ifdef _DEBUG_HTTPERROR 38#ifdef _DEBUG_HTTPERROR
41static char debug_request[8192]; 39static char debug_request[8192];
42#endif 40#endif
43 41
44static void carp(const char* routine ) { 42struct http_data {
43 union {
44 array request;
45 io_batch batch;
46 };
47 unsigned char ip[4];
48};
49
50/* Prototypes */
51
52int main( int argc, char **argv );
53
54static int httpheader_complete( struct http_data *h );
55static void httperror( const int64 s, struct http_data *h, const char *title, const char *message );
56static void httpresponse( const int64 s, struct http_data *h);
57
58static void sendmallocdata( const int64 s, struct http_data *h, char *buffer, const size_t size );
59static void senddata( const int64 s, struct http_data *h, char *buffer, const size_t size );
60
61static void server_mainloop( const int64 serversocket );
62static void handle_timeouted( void );
63static void handle_accept( const int64 serversocket );
64static void handle_read( const int64 clientsocket );
65static void handle_write( const int64 clientsocket );
66
67static void usage( char *name );
68static void help( char *name );
69
70static void carp( const char *routine );
71static void panic( const char *routine );
72static 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
82static 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
51static void panic( const char* routine ) { 89static void panic( const char *routine ) {
52 carp( routine ); 90 carp( routine );
53 exit( 111 ); 91 exit( 111 );
54} 92}
55 93
56struct http_data { 94static 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
64int 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
75void sendmallocdata( int64 s, struct http_data *h, char * buffer, size_t size ) { 105static 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
114static 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
98void senddata(int64 s, struct http_data* h, char *buffer, size_t size ) { 141static 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
136void httperror(int64 s,struct http_data* h,const char* title,const char* message) { 171static 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
145void 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;
162e400:
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;
230e400_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;
350e500:
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 */
370e404: 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
403void graceful( int s ) { 408static 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 416static void usage( char *name ) {
412void 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
420void 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
431void help( char *name ) { 427static 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
456void handle_read( int64 clientsocket ) { 452static 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
487void handle_write( int64 clientsocket ) { 479static 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
501void handle_accept( int64 serversocket ) { 489static 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
540void handle_timeouted( ) { 519static 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
556void server_mainloop( int64 serversocket ) { 531static 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