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 | ||