diff options
-rw-r--r-- | opentracker.c | 173 | ||||
-rw-r--r-- | testsuite.sh | 8 |
2 files changed, 67 insertions, 114 deletions
diff --git a/opentracker.c b/opentracker.c index 47e6f8d..0e499fe 100644 --- a/opentracker.c +++ b/opentracker.c | |||
@@ -31,6 +31,8 @@ unsigned long const OT_CLIENT_TIMEOUT = 15; | |||
31 | static unsigned int ot_overall_connections = 0; | 31 | static unsigned int ot_overall_connections = 0; |
32 | static time_t ot_start_time; | 32 | static time_t ot_start_time; |
33 | static const unsigned int SUCCESS_HTTP_HEADER_LENGTH = 80; | 33 | static const unsigned int SUCCESS_HTTP_HEADER_LENGTH = 80; |
34 | static char reply[8192]; | ||
35 | static size_t reply_size; | ||
34 | 36 | ||
35 | static void carp(const char* routine) { | 37 | static void carp(const char* routine) { |
36 | buffer_puts(buffer_2,routine); | 38 | buffer_puts(buffer_2,routine); |
@@ -50,36 +52,40 @@ struct http_data { | |||
50 | unsigned long ip; | 52 | unsigned long ip; |
51 | }; | 53 | }; |
52 | 54 | ||
53 | int header_complete(struct http_data* r) | 55 | int header_complete(struct http_data* r) { |
54 | { | 56 | long l = array_bytes(&r->r); |
55 | long i; | 57 | const char* c = array_start(&r->r); |
56 | 58 | long i; | |
57 | long l = array_bytes(&r->r); | ||
58 | const char* c = array_start(&r->r); | ||
59 | 59 | ||
60 | for (i=0; i+1<l; ++i) | 60 | for (i=0; i+1<l; ++i) { |
61 | { | 61 | if (c[i]=='\n' && c[i+1]=='\n') return i+2; |
62 | if (c[i]=='\n' && c[i+1]=='\n') | 62 | if (i+3<l && c[i]=='\r' && c[i+1]=='\n' && c[i+2]=='\r' && c[i+3]=='\n') return i+4; |
63 | return i+2; | 63 | } |
64 | return 0; | ||
65 | } | ||
64 | 66 | ||
65 | if (i+3<l && c[i]=='\r' && c[i+1]=='\n' && c[i+2]=='\r' && c[i+3]=='\n') | 67 | // whoever sends data is not interested in its input-array |
66 | return i+4; | 68 | void senddata(int64 s,struct http_data* h) { |
67 | } | 69 | size_t written_size; |
68 | return 0; | 70 | |
71 | if( h ) array_reset(&h->r); | ||
72 | written_size = write( s, reply, reply_size ); | ||
73 | if( ( written_size < 0 ) || ( written_size == reply_size ) ) { | ||
74 | free(h); io_close( s ); | ||
75 | } else { | ||
76 | fprintf( stderr, "Should have handled this.\n" ); | ||
77 | free(h); io_close( s ); | ||
78 | } | ||
69 | } | 79 | } |
70 | 80 | ||
71 | void httperror(struct http_data* h,const char* title,const char* message) | 81 | void httperror(int64 s,struct http_data* h,const char* title,const char* message) { |
72 | { | 82 | reply_size = sprintf( reply, "HTTP/1.0 %s\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: %zd\r\n\r\n<title>%s</title>\n", |
73 | char* c = (char*)malloc(strlen(message)+strlen(title)+200); | 83 | title, strlen(message)+strlen(title)+16-4,title+4); |
74 | if( !c) iob_addbuf(&h->iob, "HTTP/1.0 500 internal error\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nout of memory\n", 90); | 84 | senddata(s,h); |
75 | else iob_addbuf_free( &h->iob, c, | ||
76 | sprintf( c, "HTTP/1.0 %s\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: %zd\r\n\r\n<title>%s</title>\n", | ||
77 | title, strlen(message)+strlen(title)+16-4,title+4) ); | ||
78 | } | 85 | } |
79 | 86 | ||
80 | // bestimmten http parameter auslesen und adresse zurueckgeben | 87 | // bestimmten http parameter auslesen und adresse zurueckgeben |
81 | const char* http_header(struct http_data* r,const char* h) | 88 | const char* http_header(struct http_data* r,const char* h) { |
82 | { | ||
83 | long i; | 89 | long i; |
84 | 90 | ||
85 | long l = array_bytes(&r->r); | 91 | long l = array_bytes(&r->r); |
@@ -99,24 +105,23 @@ const char* http_header(struct http_data* r,const char* h) | |||
99 | return 0; | 105 | return 0; |
100 | } | 106 | } |
101 | 107 | ||
102 | void httpresponse(struct http_data* h,int64 s) | 108 | void httpresponse(int64 s,struct http_data* h) |
103 | { | 109 | { |
104 | char *c, *data, *reply = NULL; | 110 | char *c, *data; // must be enough |
105 | ot_peer peer; | 111 | ot_peer peer; |
106 | ot_torrent *torrent; | 112 | ot_torrent *torrent; |
107 | ot_hash *hash = NULL; | 113 | ot_hash *hash = NULL; |
108 | int numwant, tmp, scanon; | 114 | int numwant, tmp, scanon; |
109 | unsigned short port = htons(6881); | 115 | unsigned short port = htons(6881); |
110 | size_t reply_size = 0; | ||
111 | 116 | ||
117 | reply_size = 0; | ||
112 | array_cat0(&h->r); | 118 | array_cat0(&h->r); |
113 | 119 | ||
114 | c = array_start(&h->r); | 120 | c = array_start(&h->r); |
115 | 121 | ||
116 | if (byte_diff(c,4,"GET ")) { | 122 | if (byte_diff(c,4,"GET ")) { |
117 | e400: | 123 | e400: |
118 | httperror(h,"400 Invalid Request","This server only understands GET."); | 124 | return httperror(s,h,"400 Invalid Request","This server only understands GET."); |
119 | goto bailout; | ||
120 | } | 125 | } |
121 | 126 | ||
122 | c+=4; | 127 | c+=4; |
@@ -147,14 +152,12 @@ e400: | |||
147 | continue; | 152 | continue; |
148 | } | 153 | } |
149 | /* ignore this, when we have less than 20 bytes */ | 154 | /* ignore this, when we have less than 20 bytes */ |
150 | switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) { | 155 | if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 20 ) { |
151 | case -1: | 156 | e400_param: |
152 | goto e404; | 157 | return httperror(s,h,"400 Invalid Request","Invalid parameter"); |
153 | case 20: | ||
154 | hash = (ot_hash*)data; /* Fall through intended */ | ||
155 | default: | ||
156 | continue; | ||
157 | } | 158 | } |
159 | hash = (ot_hash*)data; /* Fall through intended */ | ||
160 | break; | ||
158 | default: | 161 | default: |
159 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); | 162 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); |
160 | break; | 163 | break; |
@@ -162,19 +165,12 @@ e400: | |||
162 | } | 165 | } |
163 | 166 | ||
164 | /* Scanned whole query string, wo */ | 167 | /* Scanned whole query string, wo */ |
165 | if( !hash ) { | 168 | if( !hash ) |
166 | httperror(h,"400 Invalid Request","This server only serves specific scrapes."); | 169 | return httperror(s,h,"400 Invalid Request","This server only serves specific scrapes."); |
167 | goto bailout; | ||
168 | } | ||
169 | 170 | ||
170 | // Enough for http header + whole scrape string | 171 | // Enough for http header + whole scrape string |
171 | reply = malloc( SUCCESS_HTTP_HEADER_LENGTH + 128 ); | 172 | if( ( reply_size = return_scrape_for_torrent( hash, SUCCESS_HTTP_HEADER_LENGTH + reply ) ) <= 0 ) |
172 | if( reply ) | ||
173 | reply_size = return_scrape_for_torrent( hash, SUCCESS_HTTP_HEADER_LENGTH + reply ); | ||
174 | if( !reply || ( reply_size < 0 ) ) { | ||
175 | if( reply ) free( reply ); | ||
176 | goto e500; | 173 | goto e500; |
177 | } | ||
178 | break; | 174 | break; |
179 | case 8: | 175 | case 8: |
180 | if( byte_diff(data,8,"announce")) | 176 | if( byte_diff(data,8,"announce")) |
@@ -198,7 +194,7 @@ e400: | |||
198 | if(!byte_diff(data,2,"ip")) { | 194 | if(!byte_diff(data,2,"ip")) { |
199 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); | 195 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); |
200 | unsigned char ip[4]; | 196 | unsigned char ip[4]; |
201 | if( ( len <= 0 ) || scan_fixed_ip( data, len, ip ) ) goto e404; | 197 | if( ( len <= 0 ) || scan_fixed_ip( data, len, ip ) ) goto e400_param; |
202 | OT_SETIP ( &peer, ip ); | 198 | OT_SETIP ( &peer, ip ); |
203 | } else | 199 | } else |
204 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); | 200 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); |
@@ -207,11 +203,11 @@ e400: | |||
207 | case 4: | 203 | case 4: |
208 | if(!byte_diff(data,4,"port")) { | 204 | if(!byte_diff(data,4,"port")) { |
209 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); | 205 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); |
210 | if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) || ( tmp > 0xffff ) ) goto e404; | 206 | if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) || ( tmp > 0xffff ) ) goto e400_param; |
211 | port = htons( tmp ); OT_SETPORT ( &peer, &port ); | 207 | port = htons( tmp ); OT_SETPORT ( &peer, &port ); |
212 | } else if(!byte_diff(data,4,"left")) { | 208 | } else if(!byte_diff(data,4,"left")) { |
213 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); | 209 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); |
214 | if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) ) goto e404; | 210 | if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) ) goto e400_param; |
215 | if( !tmp ) OT_FLAG( &peer ) |= PEER_FLAG_SEEDING; | 211 | if( !tmp ) OT_FLAG( &peer ) |= PEER_FLAG_SEEDING; |
216 | } else | 212 | } else |
217 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); | 213 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); |
@@ -221,7 +217,7 @@ e400: | |||
221 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); | 217 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); |
222 | else switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) { | 218 | else switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) { |
223 | case -1: | 219 | case -1: |
224 | goto e404; | 220 | goto e400_param; |
225 | case 7: | 221 | case 7: |
226 | if(!byte_diff(data,7,"stopped")) OT_FLAG( &peer ) |= PEER_FLAG_STOPPED; | 222 | if(!byte_diff(data,7,"stopped")) OT_FLAG( &peer ) |= PEER_FLAG_STOPPED; |
227 | break; | 223 | break; |
@@ -234,15 +230,13 @@ e400: | |||
234 | case 7: | 230 | case 7: |
235 | if(!byte_diff(data,7,"numwant")) { | 231 | if(!byte_diff(data,7,"numwant")) { |
236 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); | 232 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); |
237 | if( ( len <= 0 ) || scan_fixed_int( data, len, &numwant ) ) goto e404; | 233 | if( ( len <= 0 ) || scan_fixed_int( data, len, &numwant ) ) goto e400_param; |
238 | if( numwant > 200 ) numwant = 200; | 234 | if( numwant > 200 ) numwant = 200; |
239 | } else if(!byte_diff(data,7,"compact")) { | 235 | } else if(!byte_diff(data,7,"compact")) { |
240 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); | 236 | size_t len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); |
241 | if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) ) goto e404; | 237 | if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) ) goto e400_param; |
242 | if( !tmp ) { | 238 | if( !tmp ) |
243 | httperror(h,"400 Invalid Request","This server only delivers compact results."); | 239 | return httperror(s,h,"400 Invalid Request","This server only delivers compact results."); |
244 | goto bailout; | ||
245 | } | ||
246 | } else | 240 | } else |
247 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); | 241 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); |
248 | break; | 242 | break; |
@@ -252,14 +246,10 @@ e400: | |||
252 | continue; | 246 | continue; |
253 | } | 247 | } |
254 | /* ignore this, when we have less than 20 bytes */ | 248 | /* ignore this, when we have less than 20 bytes */ |
255 | switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) { | 249 | if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 20 ) |
256 | case -1: | 250 | goto e400; |
257 | goto e404; | 251 | hash = (ot_hash*)data; |
258 | case 20: | 252 | break; |
259 | hash = (ot_hash*)data; | ||
260 | default: // Fall through intended | ||
261 | continue; | ||
262 | } | ||
263 | default: | 253 | default: |
264 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); | 254 | scan_urlencoded_query( &c, NULL, SCAN_SEARCHPATH_VALUE ); |
265 | break; | 255 | break; |
@@ -267,34 +257,24 @@ e400: | |||
267 | } | 257 | } |
268 | 258 | ||
269 | /* Scanned whole query string */ | 259 | /* Scanned whole query string */ |
270 | if( !hash ) goto e404; | 260 | if( !hash ) goto e400; |
271 | 261 | ||
272 | if( OT_FLAG( &peer ) & PEER_FLAG_STOPPED ) { | 262 | if( OT_FLAG( &peer ) & PEER_FLAG_STOPPED ) { |
273 | remove_peer_from_torrent( hash, &peer ); | 263 | remove_peer_from_torrent( hash, &peer ); |
274 | reply = malloc( SUCCESS_HTTP_HEADER_LENGTH + 26 ); | ||
275 | if( !reply ) | ||
276 | goto e500; | ||
277 | MEMMOVE( reply + SUCCESS_HTTP_HEADER_LENGTH, "d15:warning message4:Okaye", reply_size = 26 ); | 264 | MEMMOVE( reply + SUCCESS_HTTP_HEADER_LENGTH, "d15:warning message4:Okaye", reply_size = 26 ); |
278 | } else { | 265 | } else { |
279 | torrent = add_peer_to_torrent( hash, &peer ); | 266 | torrent = add_peer_to_torrent( hash, &peer ); |
280 | if( !torrent ) { | 267 | if( !torrent ) { |
281 | e500: | 268 | e500: |
282 | httperror(h,"500 Internal Server Error","A server error has occured. Please retry later."); | 269 | return httperror(s,h,"500 Internal Server Error","A server error has occured. Please retry later."); |
283 | goto bailout; | ||
284 | } | 270 | } |
285 | reply = malloc( SUCCESS_HTTP_HEADER_LENGTH + numwant * 6 + 128 ); // http header + peerlist + seeder, peers and lametta 80 + n*6+81 a.t.m. | 271 | if( ( reply_size = return_peers_for_torrent( torrent, numwant, SUCCESS_HTTP_HEADER_LENGTH + reply ) ) <= 0 ) |
286 | if( reply ) | ||
287 | reply_size = return_peers_for_torrent( torrent, numwant, SUCCESS_HTTP_HEADER_LENGTH + reply ); | ||
288 | if( !reply || ( reply_size <= 0 ) ) { | ||
289 | if( reply ) free( reply ); | ||
290 | goto e500; | 272 | goto e500; |
291 | } | ||
292 | } | 273 | } |
293 | break; | 274 | break; |
294 | case 11: | 275 | case 11: |
295 | if( byte_diff(data,11,"mrtg_scrape")) | 276 | if( byte_diff(data,11,"mrtg_scrape")) |
296 | goto e404; | 277 | goto e404; |
297 | reply = malloc( SUCCESS_HTTP_HEADER_LENGTH + 128 ); | ||
298 | { | 278 | { |
299 | unsigned long seconds_elapsed = time( NULL ) - ot_start_time; | 279 | unsigned long seconds_elapsed = time( NULL ) - ot_start_time; |
300 | reply_size = sprintf( reply + SUCCESS_HTTP_HEADER_LENGTH, | 280 | reply_size = sprintf( reply + SUCCESS_HTTP_HEADER_LENGTH, |
@@ -305,31 +285,16 @@ e500: | |||
305 | break; | 285 | break; |
306 | default: /* neither *scrape nor announce */ | 286 | default: /* neither *scrape nor announce */ |
307 | e404: | 287 | e404: |
308 | httperror(h,"404 Not Found","No such file or directory."); | 288 | return httperror(s,h,"404 Not Found","No such file or directory."); |
309 | goto bailout; | ||
310 | } | 289 | } |
311 | 290 | ||
312 | if( reply && reply_size ) { | 291 | if( reply_size ) { |
313 | MEMMOVE( reply, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: X \r\n\r\n", SUCCESS_HTTP_HEADER_LENGTH ); | 292 | MEMMOVE( reply, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: X \r\n\r\n", SUCCESS_HTTP_HEADER_LENGTH ); |
314 | fmt_ulonglong( reply+59, (long long)reply_size ); | 293 | fmt_ulonglong( reply+59, (long long)reply_size ); |
315 | iob_addbuf_free(&h->iob, reply, SUCCESS_HTTP_HEADER_LENGTH + reply_size ); | ||
316 | } | 294 | } |
317 | 295 | reply_size += SUCCESS_HTTP_HEADER_LENGTH; | |
318 | bailout: | ||
319 | io_dontwantread(s); | 296 | io_dontwantread(s); |
320 | io_wantwrite(s); | 297 | senddata(s,h); |
321 | |||
322 | reply_size=iob_send(s,&h->iob); | ||
323 | if (reply_size==-1) { | ||
324 | io_eagain(s); | ||
325 | } else | ||
326 | if ((reply_size<=0)||(h->iob.bytesleft==0)) | ||
327 | { | ||
328 | array_reset(&h->r); | ||
329 | iob_reset(&h->iob); | ||
330 | free(h); | ||
331 | io_close(s); | ||
332 | } | ||
333 | } | 298 | } |
334 | 299 | ||
335 | void graceful( int s ) { | 300 | void graceful( int s ) { |
@@ -468,7 +433,6 @@ allparsed: | |||
468 | if (h) | 433 | if (h) |
469 | { | 434 | { |
470 | array_reset(&h->r); | 435 | array_reset(&h->r); |
471 | iob_reset(&h->iob); | ||
472 | free(h); | 436 | free(h); |
473 | } | 437 | } |
474 | io_close(i); | 438 | io_close(i); |
@@ -478,21 +442,11 @@ allparsed: | |||
478 | array_catb(&h->r,buf,l); | 442 | array_catb(&h->r,buf,l); |
479 | 443 | ||
480 | if (array_failed(&h->r)) | 444 | if (array_failed(&h->r)) |
481 | { | 445 | httperror(i,h,"500 Server Error","Request too long."); |
482 | httperror(h,"500 Server Error","request too long."); | ||
483 | emerge: | ||
484 | io_dontwantread(i); | ||
485 | io_wantwrite(i); | ||
486 | } | ||
487 | else if (array_bytes(&h->r)>8192) | 446 | else if (array_bytes(&h->r)>8192) |
488 | { | 447 | httperror(i,h,"500 request too long","You sent too much headers"); |
489 | httperror(h,"500 request too long","You sent too much headers"); | ||
490 | goto emerge; | ||
491 | } | ||
492 | else if ((l=header_complete(h))) | 448 | else if ((l=header_complete(h))) |
493 | { | 449 | httpresponse(i,h); |
494 | httpresponse(h,i); | ||
495 | } | ||
496 | } | 450 | } |
497 | } | 451 | } |
498 | } | 452 | } |
@@ -507,7 +461,6 @@ emerge: | |||
507 | else | 461 | else |
508 | if ((r<=0)||(h->iob.bytesleft==0)) | 462 | if ((r<=0)||(h->iob.bytesleft==0)) |
509 | { | 463 | { |
510 | array_reset(&h->r); | ||
511 | iob_reset(&h->iob); | 464 | iob_reset(&h->iob); |
512 | free(h); | 465 | free(h); |
513 | io_close(i); | 466 | io_close(i); |
diff --git a/testsuite.sh b/testsuite.sh index 6b2dad9..1af1499 100644 --- a/testsuite.sh +++ b/testsuite.sh | |||
@@ -4,9 +4,9 @@ while true; do | |||
4 | request_string="GET /announce?info_hash=0123456789012345678%$(printf %02X $(( $RANDOM & 0xff )) )&\ | 4 | request_string="GET /announce?info_hash=0123456789012345678%$(printf %02X $(( $RANDOM & 0xff )) )&\ |
5 | ip=10.1.1.$(( $RANDOM & 0xff ))&port=$(( $RANDOM & 0xff )) HTTP/1.0\n" | 5 | ip=10.1.1.$(( $RANDOM & 0xff ))&port=$(( $RANDOM & 0xff )) HTTP/1.0\n" |
6 | 6 | ||
7 | echo -e $request_string | 7 | # echo -e $request_string |
8 | echo | 8 | # echo |
9 | echo -e $request_string | nc 213.73.88.214 6969 | tr -C "[:print:]" _ | 9 | echo -e $request_string | nc 23.23.23.182 6969 >/dev/null & |
10 | echo | 10 | # echo |
11 | 11 | ||
12 | done | 12 | done |