summaryrefslogtreecommitdiff
path: root/ot_http.c
diff options
context:
space:
mode:
Diffstat (limited to 'ot_http.c')
-rw-r--r--ot_http.c837
1 files changed, 478 insertions, 359 deletions
diff --git a/ot_http.c b/ot_http.c
index 90b7a4b..5c622e2 100644
--- a/ot_http.c
+++ b/ot_http.c
@@ -4,531 +4,645 @@
4 $id$ */ 4 $id$ */
5 5
6/* System */ 6/* System */
7#include <sys/types.h> 7#define _GNU_SOURCE
8#include <arpa/inet.h> 8#include <arpa/inet.h>
9#include <stdlib.h> 9#include <pthread.h>
10#include <stdio.h> 10#include <stdio.h>
11#include <stdlib.h>
11#include <string.h> 12#include <string.h>
13#include <sys/types.h>
12#include <unistd.h> 14#include <unistd.h>
13#include <pthread.h>
14 15
15/* Libowfat */ 16/* Libowfat */
16#include "byte.h"
17#include "array.h" 17#include "array.h"
18#include "byte.h"
19#include "case.h"
18#include "iob.h" 20#include "iob.h"
19#include "ip6.h" 21#include "ip6.h"
20#include "scan.h" 22#include "scan.h"
21#include "case.h"
22 23
23/* Opentracker */ 24/* Opentracker */
24#include "trackerlogic.h" 25#include "ot_accesslist.h"
25#include "ot_mutex.h" 26#include "ot_fullscrape.h"
26#include "ot_http.h" 27#include "ot_http.h"
27#include "ot_iovec.h" 28#include "ot_iovec.h"
28#include "scan_urlencoded_query.h" 29#include "ot_mutex.h"
29#include "ot_fullscrape.h"
30#include "ot_stats.h" 30#include "ot_stats.h"
31#include "ot_accesslist.h" 31#include "scan_urlencoded_query.h"
32#include "trackerlogic.h"
33
34#ifdef WANT_NO_AUTO_FREE
35#define OT_IOB_INIT(B) bzero(B, sizeof(io_batch))
36#else
37#define OT_IOB_INIT(B) iob_init_autofree(B, 0)
38#endif
32 39
33#define OT_MAXMULTISCRAPE_COUNT 64 40#define OT_MAXMULTISCRAPE_COUNT 64
34#define OT_BATCH_LIMIT (1024*1024*16) 41#define OT_BATCH_LIMIT (1024 * 1024 * 16)
35extern char *g_redirecturl; 42extern char *g_redirecturl;
36 43
37char *g_stats_path; 44char *g_stats_path;
38ssize_t g_stats_path_len; 45ssize_t g_stats_path_len;
39 46
40enum { 47enum { SUCCESS_HTTP_HEADER_LENGTH = 80, SUCCESS_HTTP_SIZE_OFF = 17 };
41 SUCCESS_HTTP_HEADER_LENGTH = 80,
42 SUCCESS_HTTP_HEADER_LENGTH_CONTENT_ENCODING = 32,
43 SUCCESS_HTTP_SIZE_OFF = 17 };
44 48
45static void http_senddata( const int64 sock, struct ot_workstruct *ws ) { 49static void http_senddata(const int64 sock, struct ot_workstruct *ws) {
46 struct http_data *cookie = io_getcookie( sock ); 50 struct http_data *cookie = io_getcookie(sock);
47 ssize_t written_size; 51 ssize_t written_size;
48 52
49 if( !cookie ) { io_close(sock); return; } 53 if (!cookie) {
54 io_close(sock);
55 return;
56 }
50 57
51 /* whoever sends data is not interested in its input-array */ 58 /* whoever sends data is not interested in its input-array */
52 if( ws->keep_alive && ws->header_size != ws->request_size ) { 59 if (ws->keep_alive && ws->header_size != ws->request_size) {
53 size_t rest = ws->request_size - ws->header_size; 60 size_t rest = ws->request_size - ws->header_size;
54 if( array_start(&cookie->request) ) { 61 if (array_start(&cookie->request)) {
55 memmove( array_start(&cookie->request), ws->request + ws->header_size, rest ); 62 memmove(array_start(&cookie->request), ws->request + ws->header_size, rest);
56 array_truncate( &cookie->request, 1, rest ); 63 array_truncate(&cookie->request, 1, rest);
57 } else 64 } else
58 array_catb(&cookie->request, ws->request + ws->header_size, rest ); 65 array_catb(&cookie->request, ws->request + ws->header_size, rest);
59 } else 66 } else
60 array_reset( &cookie->request ); 67 array_reset(&cookie->request);
61 68
62 written_size = write( sock, ws->reply, ws->reply_size ); 69 written_size = write(sock, ws->reply, ws->reply_size);
63 if( ( written_size < 0 ) || ( ( written_size == ws->reply_size ) && !ws->keep_alive ) ) { 70 if ((written_size < 0) || ((written_size == ws->reply_size) && !ws->keep_alive)) {
64 array_reset( &cookie->request ); 71 array_reset(&cookie->request);
65 free( cookie ); io_close( sock ); return; 72 free(cookie);
73 io_close(sock);
74 return;
66 } 75 }
67 76
68 if( written_size < ws->reply_size ) { 77 if (written_size < ws->reply_size) {
69 char * outbuf; 78 char *outbuf;
70 tai6464 t; 79 tai6464 t;
71 80
72 if( !( outbuf = malloc( ws->reply_size - written_size ) ) ) { 81 if (!(outbuf = malloc(ws->reply_size - written_size))) {
73 array_reset( &cookie->request ); 82 array_reset(&cookie->request);
74 free(cookie); io_close( sock ); 83 free(cookie);
84 io_close(sock);
75 return; 85 return;
76 } 86 }
77 87
78 memcpy( outbuf, ws->reply + written_size, ws->reply_size - written_size ); 88 memcpy(outbuf, ws->reply + written_size, ws->reply_size - written_size);
79 if ( !cookie->batch ) { 89 if (!cookie->batch) {
80 cookie->batch = malloc( sizeof(io_batch) ); 90 cookie->batch = malloc(sizeof(io_batch));
81 memset( cookie->batch, 0, sizeof(io_batch) ); 91 OT_IOB_INIT(cookie->batch);
82 cookie->batches = 1; 92 cookie->batches = 1;
83 } 93 }
84 94
85 iob_addbuf_free( cookie->batch, outbuf, ws->reply_size - written_size ); 95 iob_addbuf_free(cookie->batch, outbuf, ws->reply_size - written_size);
86 96
87 /* writeable short data sockets just have a tcp timeout */ 97 /* writeable short data sockets just have a tcp timeout */
88 if( !ws->keep_alive ) { 98 if (!ws->keep_alive) {
89 taia_uint( &t, 0 ); io_timeout( sock, t ); 99 taia_uint(&t, 0);
90 io_dontwantread( sock ); 100 io_timeout(sock, t);
101 io_dontwantread(sock);
91 } 102 }
92 io_wantwrite( sock ); 103 io_wantwrite(sock);
93 } 104 }
94} 105}
95 106
96#define HTTPERROR_302 return http_issue_error( sock, ws, CODE_HTTPERROR_302 ) 107#define HTTPERROR_302 return http_issue_error(sock, ws, CODE_HTTPERROR_302)
97#define HTTPERROR_400 return http_issue_error( sock, ws, CODE_HTTPERROR_400 ) 108#define HTTPERROR_400 return http_issue_error(sock, ws, CODE_HTTPERROR_400)
98#define HTTPERROR_400_PARAM return http_issue_error( sock, ws, CODE_HTTPERROR_400_PARAM ) 109#define HTTPERROR_400_PARAM return http_issue_error(sock, ws, CODE_HTTPERROR_400_PARAM)
99#define HTTPERROR_400_COMPACT return http_issue_error( sock, ws, CODE_HTTPERROR_400_COMPACT ) 110#define HTTPERROR_400_COMPACT return http_issue_error(sock, ws, CODE_HTTPERROR_400_COMPACT)
100#define HTTPERROR_400_DOUBLEHASH return http_issue_error( sock, ws, CODE_HTTPERROR_400_PARAM ) 111#define HTTPERROR_400_DOUBLEHASH return http_issue_error(sock, ws, CODE_HTTPERROR_400_PARAM)
101#define HTTPERROR_402_NOTMODEST return http_issue_error( sock, ws, CODE_HTTPERROR_402_NOTMODEST ) 112#define HTTPERROR_402_NOTMODEST return http_issue_error(sock, ws, CODE_HTTPERROR_402_NOTMODEST)
102#define HTTPERROR_403_IP return http_issue_error( sock, ws, CODE_HTTPERROR_403_IP ) 113#define HTTPERROR_403_IP return http_issue_error(sock, ws, CODE_HTTPERROR_403_IP)
103#define HTTPERROR_404 return http_issue_error( sock, ws, CODE_HTTPERROR_404 ) 114#define HTTPERROR_404 return http_issue_error(sock, ws, CODE_HTTPERROR_404)
104#define HTTPERROR_500 return http_issue_error( sock, ws, CODE_HTTPERROR_500 ) 115#define HTTPERROR_500 return http_issue_error(sock, ws, CODE_HTTPERROR_500)
105ssize_t http_issue_error( const int64 sock, struct ot_workstruct *ws, int code ) { 116ssize_t http_issue_error(const int64 sock, struct ot_workstruct *ws, int code) {
106 char *error_code[] = { "302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", "402 Payment Required", 117 char *error_code[] = {"302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", "402 Payment Required",
107 "403 Not Modest", "403 Access Denied", "404 Not Found", "500 Internal Server Error" }; 118 "403 Not Modest", "403 Access Denied", "404 Not Found", "500 Internal Server Error"};
108 char *title = error_code[code]; 119 char *title = error_code[code];
109 120
110 ws->reply = ws->outbuf; 121 ws->reply = ws->outbuf;
111 if( code == CODE_HTTPERROR_302 ) 122 if (code == CODE_HTTPERROR_302)
112 ws->reply_size = snprintf( ws->reply, G_OUTBUF_SIZE, "HTTP/1.0 302 Found\r\nContent-Length: 0\r\nLocation: %s\r\n\r\n", g_redirecturl ); 123 ws->reply_size = snprintf(ws->reply, G_OUTBUF_SIZE, "HTTP/1.0 302 Found\r\nContent-Length: 0\r\nLocation: %s\r\n\r\n", g_redirecturl);
113 else 124 else
114 ws->reply_size = snprintf( ws->reply, G_OUTBUF_SIZE, "HTTP/1.0 %s\r\nContent-Type: text/html\r\nContent-Length: %zd\r\n\r\n<title>%s</title>\n", title, strlen(title)+16-4,title+4); 125 ws->reply_size = snprintf(ws->reply, G_OUTBUF_SIZE, "HTTP/1.0 %s\r\nContent-Type: text/html\r\nContent-Length: %zd\r\n\r\n<title>%s</title>\n", title,
126 strlen(title) + 16 - 4, title + 4);
115 127
116#ifdef _DEBUG_HTTPERROR 128#ifdef _DEBUG_HTTPERROR
117 fprintf( stderr, "DEBUG: invalid request was: %s\n", ws->debugbuf ); 129 fprintf(stderr, "DEBUG: invalid request was: %s\n", ws->debugbuf);
118#endif 130#endif
119 stats_issue_event( EVENT_FAILED, FLAG_TCP, code ); 131 stats_issue_event(EVENT_FAILED, FLAG_TCP, code);
120 http_senddata( sock, ws ); 132 http_senddata(sock, ws);
121 return ws->reply_size = -2; 133 return ws->reply_size = -2;
122} 134}
123 135
124ssize_t http_sendiovecdata( const int64 sock, struct ot_workstruct *ws, int iovec_entries, struct iovec *iovector ) { 136ssize_t http_sendiovecdata(const int64 sock, struct ot_workstruct *ws, int iovec_entries, struct iovec *iovector, int is_partial) {
125 struct http_data *cookie = io_getcookie( sock ); 137 struct http_data *cookie = io_getcookie(sock);
126 char *header; 138 io_batch *current;
127 int i; 139 char *header;
128 size_t header_size, size = iovec_length( &iovec_entries, (const struct iovec **)&iovector ); 140 const char *encoding = "";
129 tai6464 t; 141 int i;
142 size_t header_size, size = iovec_length(&iovec_entries, (const struct iovec **)&iovector);
143 tai6464 t;
130 144
131 /* No cookie? Bad socket. Leave. */ 145 /* No cookie? Bad socket. Leave. */
132 if( !cookie ) { 146 if (!cookie) {
133 iovec_free( &iovec_entries, &iovector ); 147 iovec_free(&iovec_entries, &iovector);
134 HTTPERROR_500; 148 HTTPERROR_500;
135 } 149 }
136 150
137 /* If this socket collected request in a buffer, free it now */ 151 /* If this socket collected request in a buffer, free it now */
138 array_reset( &cookie->request ); 152 array_reset(&cookie->request);
139 153
140 /* If we came here, wait for the answer is over */ 154 /* If we came here, wait for the answer is over */
141 cookie->flag &= ~STRUCT_HTTP_FLAG_WAITINGFORTASK; 155 if (cookie->flag & STRUCT_HTTP_FLAG_WAITINGFORTASK) {
142 156 io_dontwantread(sock);
143 /* Our answers never are 0 vectors. Return an error. */ 157 cookie->flag &= ~STRUCT_HTTP_FLAG_WAITINGFORTASK;
144 if( !iovec_entries ) {
145 HTTPERROR_500;
146 } 158 }
147 159
148 /* Prepare space for http header */ 160 if (iovec_entries) {
149 header = malloc( SUCCESS_HTTP_HEADER_LENGTH + SUCCESS_HTTP_HEADER_LENGTH_CONTENT_ENCODING ); 161
150 if( !header ) { 162 if (cookie->flag & STRUCT_HTTP_FLAG_ZSTD)
151 iovec_free( &iovec_entries, &iovector ); 163 encoding = "Content-Encoding: zstd\r\n";
152 HTTPERROR_500; 164 else if (cookie->flag & STRUCT_HTTP_FLAG_GZIP)
153 } 165 encoding = "Content-Encoding: gzip\r\n";
154 166 else if (cookie->flag & STRUCT_HTTP_FLAG_BZIP2)
155 if( cookie->flag & STRUCT_HTTP_FLAG_GZIP ) 167 encoding = "Content-Encoding: bzip2\r\n";
156 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 ); 168
157 else if( cookie->flag & STRUCT_HTTP_FLAG_BZIP2 ) 169 if (!(cookie->flag & STRUCT_HTTP_FLAG_CHUNKED))
158 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 ); 170 header_size = asprintf(&header, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n%sContent-Length: %zd\r\n\r\n", encoding, size);
159 else 171 else {
160 header_size = sprintf( header, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r\n", size ); 172 if (!(cookie->flag & STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER)) {
173 header_size =
174 asprintf(&header, "HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\n%sTransfer-Encoding: chunked\r\n\r\n%zx\r\n", encoding, size);
175 cookie->flag |= STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER;
176 } else
177 header_size = asprintf(&header, "%zx\r\n", size);
178 }
179 if (!header) {
180 iovec_free(&iovec_entries, &iovector);
181 HTTPERROR_500;
182 }
161 183
162 if (!cookie->batch ) { 184 if (!cookie->batch) {
163 cookie->batch = malloc( sizeof(io_batch) ); 185 cookie->batch = malloc(sizeof(io_batch));
164 memset( cookie->batch, 0, sizeof(io_batch) ); 186 if (!cookie->batch) {
165 cookie->batches = 1; 187 free(header);
166 } 188 iovec_free(&iovec_entries, &iovector);
167 iob_addbuf_free( cookie->batch, header, header_size ); 189 HTTPERROR_500;
168 190 }
169 /* Split huge iovectors into separate io_batches */ 191 OT_IOB_INIT(cookie->batch);
170 for( i=0; i<iovec_entries; ++i ) { 192 cookie->batches = 1;
171 io_batch *current = cookie->batch + cookie->batches - 1; 193 }
172 194 current = cookie->batch + cookie->batches - 1;
173 /* If the current batch's limit is reached, try to reallocate a new batch to work on */ 195 iob_addbuf_free(current, header, header_size);
174 if( current->bytesleft > OT_BATCH_LIMIT ) { 196
175 io_batch * new_batch = realloc( current, (cookie->batches + 1) * sizeof(io_batch) ); 197 /* Split huge iovectors into separate io_batches */
176 if( new_batch ) { 198 for (i = 0; i < iovec_entries; ++i) {
177 cookie->batches++; 199 /* If the current batch's limit is reached, try to reallocate a new batch to work on */
178 current = cookie->batch = new_batch; 200 if (current->bytesleft > OT_BATCH_LIMIT) {
179 memset( current, 0, sizeof(io_batch) ); 201 io_batch *new_batch = realloc(cookie->batch, (cookie->batches + 1) * sizeof(io_batch));
202 if (new_batch) {
203 cookie->batch = new_batch;
204 current = cookie->batch + cookie->batches++;
205 OT_IOB_INIT(current);
180 } 206 }
207 }
208 iob_addbuf_free(current, iovector[i].iov_base, iovector[i].iov_len);
181 } 209 }
210 free(iovector);
211 if (cookie->flag & STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER)
212 iob_addbuf(current, "\r\n", 2);
213 }
182 214
183 iob_addbuf_free( current, iovector[i].iov_base, iovector[i].iov_len ); 215 if ((cookie->flag & STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER) && cookie->batch && !is_partial) {
216 current = cookie->batch + cookie->batches - 1;
217 iob_addbuf(current, "0\r\n\r\n", 5);
218 cookie->flag &= ~STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER;
184 } 219 }
185 free( iovector );
186 220
187 /* writeable sockets timeout after 10 minutes */ 221 /* writeable sockets timeout after 10 minutes */
188 taia_now( &t ); taia_addsec( &t, &t, OT_CLIENT_TIMEOUT_SEND ); 222 taia_now(&t);
189 io_timeout( sock, t ); 223 taia_addsec(&t, &t, OT_CLIENT_TIMEOUT_SEND);
190 io_dontwantread( sock ); 224 io_timeout(sock, t);
191 io_wantwrite( sock ); 225 io_wantwrite(sock);
192 return 0; 226 return 0;
193} 227}
194 228
195static ssize_t http_handle_stats( const int64 sock, struct ot_workstruct *ws, char *read_ptr ) { 229static ssize_t http_handle_stats(const int64 sock, struct ot_workstruct *ws, char *read_ptr) {
196static const ot_keywords keywords_main[] = 230 static const ot_keywords keywords_main[] = {{"mode", 1}, {"format", 2}, {"info_hash", 3}, {NULL, -3}};
197 { { "mode", 1 }, {"format", 2 }, {"info_hash", 3}, { NULL, -3 } }; 231 static const ot_keywords keywords_mode[] = {{"peer", TASK_STATS_PEERS},
198static const ot_keywords keywords_mode[] = 232 {"conn", TASK_STATS_CONNS},
199 { { "peer", TASK_STATS_PEERS }, { "conn", TASK_STATS_CONNS }, { "scrp", TASK_STATS_SCRAPE }, { "udp4", TASK_STATS_UDP }, { "tcp4", TASK_STATS_TCP }, 233 {"scrp", TASK_STATS_SCRAPE},
200 { "busy", TASK_STATS_BUSY_NETWORKS }, { "torr", TASK_STATS_TORRENTS }, { "fscr", TASK_STATS_FULLSCRAPE }, 234 {"udp4", TASK_STATS_UDP},
201 { "s24s", TASK_STATS_SLASH24S }, { "tpbs", TASK_STATS_TPB }, { "herr", TASK_STATS_HTTPERRORS }, { "completed", TASK_STATS_COMPLETED }, 235 {"tcp4", TASK_STATS_TCP},
202 { "top100", TASK_STATS_TOP100 }, { "top10", TASK_STATS_TOP10 }, { "renew", TASK_STATS_RENEW }, { "syncs", TASK_STATS_SYNCS }, { "version", TASK_STATS_VERSION }, 236 {"busy", TASK_STATS_BUSY_NETWORKS},
203 { "everything", TASK_STATS_EVERYTHING }, { "statedump", TASK_FULLSCRAPE_TRACKERSTATE }, { "fulllog", TASK_STATS_FULLLOG }, 237 {"torr", TASK_STATS_TORRENTS},
204 { "woodpeckers", TASK_STATS_WOODPECKERS}, 238 {"fscr", TASK_STATS_FULLSCRAPE},
239 {"s24s", TASK_STATS_SLASH24S},
240 {"tpbs", TASK_STATS_TPB},
241 {"herr", TASK_STATS_HTTPERRORS},
242 {"completed", TASK_STATS_COMPLETED},
243 {"top100", TASK_STATS_TOP100},
244 {"top10", TASK_STATS_TOP10},
245 {"renew", TASK_STATS_RENEW},
246 {"syncs", TASK_STATS_SYNCS},
247 {"version", TASK_STATS_VERSION},
248 {"everything", TASK_STATS_EVERYTHING},
249 {"statedump", TASK_FULLSCRAPE_TRACKERSTATE},
250 {"fulllog", TASK_STATS_FULLLOG},
251 {"woodpeckers", TASK_STATS_WOODPECKERS},
205#ifdef WANT_LOG_NUMWANT 252#ifdef WANT_LOG_NUMWANT
206 { "numwants", TASK_STATS_NUMWANTS}, 253 {"numwants", TASK_STATS_NUMWANTS},
207#endif 254#endif
208 { NULL, -3 } }; 255 {NULL, -3}};
209static const ot_keywords keywords_format[] = 256 static const ot_keywords keywords_format[] = {{"bin", TASK_FULLSCRAPE_TPB_BINARY}, {"ben", TASK_FULLSCRAPE},
210 { { "bin", TASK_FULLSCRAPE_TPB_BINARY }, { "ben", TASK_FULLSCRAPE }, { "url", TASK_FULLSCRAPE_TPB_URLENCODED }, 257 {"url", TASK_FULLSCRAPE_TPB_URLENCODED}, {"txt", TASK_FULLSCRAPE_TPB_ASCII},
211 { "txt", TASK_FULLSCRAPE_TPB_ASCII }, { "txtp", TASK_FULLSCRAPE_TPB_ASCII_PLUS }, { NULL, -3 } }; 258 {"txtp", TASK_FULLSCRAPE_TPB_ASCII_PLUS}, {NULL, -3}};
212 259
213 int mode = TASK_STATS_PEERS, scanon = 1, format = 0; 260 int mode = TASK_STATS_PEERS, scanon = 1, format = 0;
214 261
215#ifdef WANT_RESTRICT_STATS 262#ifdef WANT_RESTRICT_STATS
216 struct http_data *cookie = io_getcookie( sock ); 263 struct http_data *cookie = io_getcookie(sock);
217 264
218 if( !cookie || !accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_STAT ) ) 265 if (!cookie || !accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_STAT))
219 HTTPERROR_403_IP; 266 HTTPERROR_403_IP;
220#endif 267#endif
221 268
222 while( scanon ) { 269 while (scanon) {
223 switch( scan_find_keywords( keywords_main, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) { 270 switch (scan_find_keywords(keywords_main, &read_ptr, SCAN_SEARCHPATH_PARAM)) {
224 case -2: scanon = 0; break; /* TERMINATOR */ 271 case -2:
225 case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ 272 scanon = 0;
226 case -3: scan_urlencoded_skipvalue( &read_ptr ); break; 273 break; /* TERMINATOR */
227 case 1: /* matched "mode" */ 274 case -1:
228 if( ( mode = scan_find_keywords( keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; 275 HTTPERROR_400_PARAM; /* PARSE ERROR */
276 case -3:
277 scan_urlencoded_skipvalue(&read_ptr);
278 break;
279 case 1: /* matched "mode" */
280 if ((mode = scan_find_keywords(keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0)
281 HTTPERROR_400_PARAM;
229 break; 282 break;
230 case 2: /* matched "format" */ 283 case 2: /* matched "format" */
231 if( ( format = scan_find_keywords( keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; 284 if ((format = scan_find_keywords(keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0)
285 HTTPERROR_400_PARAM;
232 break; 286 break;
233 case 3: HTTPERROR_400_PARAM; /* If the stats URL was mistakenly added as announce URL, return a 400 */ 287 case 3:
288 HTTPERROR_400_PARAM; /* If the stats URL was mistakenly added as announce URL, return a 400 */
234 } 289 }
235 } 290 }
236 291
237#ifdef WANT_FULLSCRAPE 292#ifdef WANT_FULLSCRAPE
238 if( mode == TASK_FULLSCRAPE_TRACKERSTATE ) { 293 if (mode == TASK_FULLSCRAPE_TRACKERSTATE) {
239 format = mode; mode = TASK_STATS_TPB; 294 format = mode;
295 mode = TASK_STATS_TPB;
240 } 296 }
241 297
242 if( mode == TASK_STATS_TPB ) { 298 if (mode == TASK_STATS_TPB) {
243 struct http_data* cookie = io_getcookie( sock ); 299 struct http_data *cookie = io_getcookie(sock);
244 tai6464 t; 300 tai6464 t;
245#ifdef WANT_COMPRESSION_GZIP 301#ifdef WANT_COMPRESSION_GZIP
246 ws->request[ws->request_size] = 0; 302 ws->request[ws->request_size] = 0;
247#ifdef WANT_COMPRESSION_GZIP_ALWAYS 303#ifndef WANT_COMPRESSION_GZIP_ALWAYS
248 if( strstr( read_ptr - 1, "gzip" ) ) { 304 if (strstr(read_ptr - 1, "gzip")) {
249#endif 305#endif
250 cookie->flag |= STRUCT_HTTP_FLAG_GZIP; 306 cookie->flag |= STRUCT_HTTP_FLAG_GZIP;
251 format |= TASK_FLAG_GZIP; 307 format |= TASK_FLAG_GZIP;
252#ifdef WANT_COMPRESSION_GZIP_ALWAYS 308#ifndef WANT_COMPRESSION_GZIP_ALWAYS
253 } 309 }
254#endif 310#endif
255#endif 311#endif
256 /* Pass this task to the worker thread */ 312 /* Pass this task to the worker thread */
257 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK; 313 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED;
258 314
259 /* Clients waiting for us should not easily timeout */ 315 /* Clients waiting for us should not easily timeout */
260 taia_uint( &t, 0 ); io_timeout( sock, t ); 316 taia_uint(&t, 0);
261 fullscrape_deliver( sock, format ); 317 io_timeout(sock, t);
262 io_dontwantread( sock ); 318 fullscrape_deliver(sock, format);
319 io_dontwantread(sock);
263 return ws->reply_size = -2; 320 return ws->reply_size = -2;
264 } 321 }
265#endif 322#endif
266 323
267 /* default format for now */ 324 /* default format for now */
268 if( ( mode & TASK_CLASS_MASK ) == TASK_STATS ) { 325 if ((mode & TASK_CLASS_MASK) == TASK_STATS) {
269 tai6464 t; 326 tai6464 t;
270 /* Complex stats also include expensive memory debugging tools */ 327 /* Complex stats also include expensive memory debugging tools */
271 taia_uint( &t, 0 ); io_timeout( sock, t ); 328 taia_uint(&t, 0);
272 stats_deliver( sock, mode ); 329 io_timeout(sock, t);
330 stats_deliver(sock, mode);
273 return ws->reply_size = -2; 331 return ws->reply_size = -2;
274 } 332 }
275 333
276 /* Simple stats can be answerred immediately */ 334 /* Simple stats can be answerred immediately */
277 return ws->reply_size = return_stats_for_tracker( ws->reply, mode, 0 ); 335 return ws->reply_size = return_stats_for_tracker(ws->reply, mode, 0);
278} 336}
279 337
280#ifdef WANT_MODEST_FULLSCRAPES 338#ifdef WANT_MODEST_FULLSCRAPES
281static pthread_mutex_t g_modest_fullscrape_mutex = PTHREAD_MUTEX_INITIALIZER; 339static pthread_mutex_t g_modest_fullscrape_mutex = PTHREAD_MUTEX_INITIALIZER;
282static ot_vector g_modest_fullscrape_timeouts; 340static ot_vector g_modest_fullscrape_timeouts;
283typedef struct { ot_ip6 ip; ot_time last_fullscrape; } ot_scrape_log; 341typedef struct {
342 ot_ip6 ip;
343 ot_time last_fullscrape;
344} ot_scrape_log;
284#endif 345#endif
285 346
286#ifdef WANT_FULLSCRAPE 347#ifdef WANT_FULLSCRAPE
287static ssize_t http_handle_fullscrape( const int64 sock, struct ot_workstruct *ws ) { 348static ssize_t http_handle_fullscrape(const int64 sock, struct ot_workstruct *ws) {
288 struct http_data* cookie = io_getcookie( sock ); 349 struct http_data *cookie = io_getcookie(sock);
289 int format = 0; 350 int format = 0;
290 tai6464 t; 351 tai6464 t;
291 352
292#ifdef WANT_MODEST_FULLSCRAPES 353#ifdef WANT_MODEST_FULLSCRAPES
293 { 354 {
294 ot_scrape_log this_peer, *new_peer; 355 ot_scrape_log this_peer, *new_peer;
295 int exactmatch; 356 int exactmatch;
296 memcpy( this_peer.ip, cookie->ip, sizeof(ot_ip6)); 357 memcpy(this_peer.ip, cookie->ip, sizeof(ot_ip6));
297 this_peer.last_fullscrape = g_now_seconds; 358 this_peer.last_fullscrape = g_now_seconds;
298 pthread_mutex_lock(&g_modest_fullscrape_mutex); 359 pthread_mutex_lock(&g_modest_fullscrape_mutex);
299 new_peer = vector_find_or_insert( &g_modest_fullscrape_timeouts, &this_peer, sizeof(ot_scrape_log), sizeof(ot_ip6), &exactmatch ); 360 new_peer = vector_find_or_insert(&g_modest_fullscrape_timeouts, &this_peer, sizeof(ot_scrape_log), sizeof(ot_ip6), &exactmatch);
300 if( !new_peer ) { 361 if (!new_peer) {
301 pthread_mutex_unlock(&g_modest_fullscrape_mutex); 362 pthread_mutex_unlock(&g_modest_fullscrape_mutex);
302 HTTPERROR_500; 363 HTTPERROR_500;
303 } 364 }
304 if( exactmatch && ( this_peer.last_fullscrape - new_peer->last_fullscrape ) < OT_MODEST_PEER_TIMEOUT ) { 365 if (exactmatch && (this_peer.last_fullscrape - new_peer->last_fullscrape) < OT_MODEST_PEER_TIMEOUT) {
305 pthread_mutex_unlock(&g_modest_fullscrape_mutex); 366 pthread_mutex_unlock(&g_modest_fullscrape_mutex);
306 HTTPERROR_402_NOTMODEST; 367 HTTPERROR_402_NOTMODEST;
307 } 368 }
308 memcpy( new_peer, &this_peer, sizeof(ot_scrape_log)); 369 memcpy(new_peer, &this_peer, sizeof(ot_scrape_log));
309 pthread_mutex_unlock(&g_modest_fullscrape_mutex); 370 pthread_mutex_unlock(&g_modest_fullscrape_mutex);
310 } 371 }
311#endif 372#endif
312 373
374
375#if defined(WANT_COMPRESSION_GZIP) || defined(WANT_COMPRESSION_ZSTD)
376 ws->request[ws->request_size - 1] = 0;
313#ifdef WANT_COMPRESSION_GZIP 377#ifdef WANT_COMPRESSION_GZIP
314 ws->request[ws->request_size-1] = 0; 378 if (strstr(ws->request, "gzip")) {
315 if( strstr( ws->request, "gzip" ) ) {
316 cookie->flag |= STRUCT_HTTP_FLAG_GZIP; 379 cookie->flag |= STRUCT_HTTP_FLAG_GZIP;
317 format = TASK_FLAG_GZIP; 380 format |= TASK_FLAG_GZIP;
318 stats_issue_event( EVENT_FULLSCRAPE_REQUEST_GZIP, 0, (uintptr_t)cookie->ip ); 381 }
319 } else 382#endif
383#ifdef WANT_COMPRESSION_ZSTD
384 if (strstr(ws->request, "zstd")) {
385 cookie->flag |= STRUCT_HTTP_FLAG_ZSTD;
386 format |= TASK_FLAG_ZSTD;
387 }
388#endif
389
390#if defined(WANT_COMPRESSION_ZSTD) && defined(WANT_COMPRESSION_ZSTD_ALWAYS)
391 cookie->flag |= STRUCT_HTTP_FLAG_ZSTD;
392 format |= TASK_FLAG_ZSTD;
320#endif 393#endif
321 stats_issue_event( EVENT_FULLSCRAPE_REQUEST, 0, (uintptr_t)cookie->ip ); 394
395#if defined(WANT_COMPRESSION_GZIP) && defined(WANT_COMPRESSION_GZIP_ALWAYS)
396 cookie->flag |= STRUCT_HTTP_FLAG_GZIP;
397 format |= TASK_FLAG_GZIP;
398#endif
399#endif
400
401 stats_issue_event(EVENT_FULLSCRAPE_REQUEST, 0, (uintptr_t)cookie->ip);
322 402
323#ifdef _DEBUG_HTTPERROR 403#ifdef _DEBUG_HTTPERROR
324 fprintf( stderr, "%s", ws->debugbuf ); 404 fprintf(stderr, "%s", ws->debugbuf);
325#endif 405#endif
326 406
327 /* Pass this task to the worker thread */ 407 /* Pass this task to the worker thread */
328 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK; 408 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED;
329 /* Clients waiting for us should not easily timeout */ 409 /* Clients waiting for us should not easily timeout */
330 taia_uint( &t, 0 ); io_timeout( sock, t ); 410 taia_uint(&t, 0);
331 fullscrape_deliver( sock, TASK_FULLSCRAPE | format ); 411 io_timeout(sock, t);
332 io_dontwantread( sock ); 412 fullscrape_deliver(sock, TASK_FULLSCRAPE | format);
413 io_dontwantread(sock);
333 return ws->reply_size = -2; 414 return ws->reply_size = -2;
334} 415}
335#endif 416#endif
336 417
337static ssize_t http_handle_scrape( const int64 sock, struct ot_workstruct *ws, char *read_ptr ) { 418static ssize_t http_handle_scrape(const int64 sock, struct ot_workstruct *ws, char *read_ptr) {
338 static const ot_keywords keywords_scrape[] = { { "info_hash", 1 }, { NULL, -3 } }; 419 static const ot_keywords keywords_scrape[] = {{"info_hash", 1}, {NULL, -3}};
339 420
340 ot_hash * multiscrape_buf = (ot_hash*)ws->request; 421 ot_hash *multiscrape_buf = (ot_hash *)ws->request;
341 int scanon = 1, numwant = 0; 422 int scanon = 1, numwant = 0;
342 423
343 /* This is to hack around stupid clients that send "scrape ?info_hash" */ 424 /* This is to hack around stupid clients that send "scrape ?info_hash" */
344 if( read_ptr[-1] != '?' ) { 425 if (read_ptr[-1] != '?') {
345 while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr; 426 while ((*read_ptr != '?') && (*read_ptr != '\n'))
346 if( *read_ptr == '\n' ) HTTPERROR_400_PARAM; 427 ++read_ptr;
428 if (*read_ptr == '\n')
429 HTTPERROR_400_PARAM;
347 ++read_ptr; 430 ++read_ptr;
348 } 431 }
349 432
350 while( scanon ) { 433 while (scanon) {
351 switch( scan_find_keywords( keywords_scrape, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) { 434 switch (scan_find_keywords(keywords_scrape, &read_ptr, SCAN_SEARCHPATH_PARAM)) {
352 case -2: scanon = 0; break; /* TERMINATOR */ 435 case -2:
353 default: HTTPERROR_400_PARAM; /* PARSE ERROR */ 436 scanon = 0;
354 case -3: scan_urlencoded_skipvalue( &read_ptr ); break; 437 break; /* TERMINATOR */
355 case 1: /* matched "info_hash" */ 438 default:
439 HTTPERROR_400_PARAM; /* PARSE ERROR */
440 case -3:
441 scan_urlencoded_skipvalue(&read_ptr);
442 break;
443 case 1: /* matched "info_hash" */
356 /* ignore this, when we have less than 20 bytes */ 444 /* ignore this, when we have less than 20 bytes */
357 if( scan_urlencoded_query( &read_ptr, (char*)(multiscrape_buf + numwant++), SCAN_SEARCHPATH_VALUE ) != (ssize_t)sizeof(ot_hash) ) 445 if (scan_urlencoded_query(&read_ptr, (char *)(multiscrape_buf + numwant++), SCAN_SEARCHPATH_VALUE) != (ssize_t)sizeof(ot_hash))
358 HTTPERROR_400_PARAM; 446 HTTPERROR_400_PARAM;
359 break; 447 break;
360 } 448 }
361 } 449 }
362 450
363 /* No info_hash found? Inform user */ 451 /* No info_hash found? Inform user */
364 if( !numwant ) HTTPERROR_400_PARAM; 452 if (!numwant)
453 HTTPERROR_400_PARAM;
365 454
366 /* Limit number of hashes to process */ 455 /* Limit number of hashes to process */
367 if( numwant > OT_MAXMULTISCRAPE_COUNT ) 456 if (numwant > OT_MAXMULTISCRAPE_COUNT)
368 numwant = OT_MAXMULTISCRAPE_COUNT; 457 numwant = OT_MAXMULTISCRAPE_COUNT;
369 458
370 /* Enough for http header + whole scrape string */ 459 /* Enough for http header + whole scrape string */
371 ws->reply_size = return_tcp_scrape_for_torrent( multiscrape_buf, numwant, ws->reply ); 460 ws->reply_size = return_tcp_scrape_for_torrent((const ot_hash *)multiscrape_buf, numwant, ws->reply);
372 stats_issue_event( EVENT_SCRAPE, FLAG_TCP, ws->reply_size ); 461 stats_issue_event(EVENT_SCRAPE, FLAG_TCP, ws->reply_size);
373 return ws->reply_size; 462 return ws->reply_size;
374} 463}
375 464
376#ifdef WANT_LOG_NUMWANT 465#ifdef WANT_LOG_NUMWANT
377 unsigned long long numwants[201]; 466unsigned long long numwants[201];
378#endif 467#endif
379 468
380#if defined( WANT_KEEPALIVE ) || defined( WANT_IP_FROM_PROXY ) 469#if defined(WANT_KEEPALIVE) || defined(WANT_IP_FROM_PROXY)
381static char* http_header( char *data, size_t byte_count, char *header ) { 470static char *http_header(char *data, size_t byte_count, char *header) {
382 size_t i; 471 size_t i;
383 long sl = strlen( header ); 472 long sl = strlen(header);
384 for( i = 0; i + sl + 2 < byte_count; ++i ) { 473 for (i = 0; i + sl + 2 < byte_count; ++i) {
385 if( data[i] != '\n' || data[ i + sl + 1] != ':' ) continue; 474 if (data[i] != '\n' || data[i + sl + 1] != ':')
386 if( !case_equalb( data + i + 1, sl, header ) ) continue; 475 continue;
476 if (!case_equalb(data + i + 1, sl, header))
477 continue;
387 data += i + sl + 2; 478 data += i + sl + 2;
388 while( *data == ' ' || *data == '\t' ) ++data; 479 while (*data == ' ' || *data == '\t')
480 ++data;
389 return data; 481 return data;
390 } 482 }
391 return 0; 483 return 0;
392} 484}
393#endif 485#endif
394 486
395static ot_keywords keywords_announce[] = { { "port", 1 }, { "left", 2 }, { "event", 3 }, { "numwant", 4 }, { "compact", 5 }, { "compact6", 5 }, { "info_hash", 6 }, 487static ot_keywords keywords_announce[] = {{"port", 1}, {"left", 2}, {"event", 3}, {"numwant", 4}, {"compact", 5}, {"compact6", 5}, {"info_hash", 6},
396#ifdef WANT_IP_FROM_QUERY_STRING 488#ifdef WANT_IP_FROM_QUERY_STRING
397{ "ip", 7 }, 489 {"ip", 7},
398#endif 490#endif
399#ifdef WANT_FULLLOG_NETWORKS 491#ifdef WANT_FULLLOG_NETWORKS
400{ "lognet", 8 }, 492 {"lognet", 8},
401#endif 493#endif
402{ "peer_id", 9 }, 494 {"peer_id", 9}, {NULL, -3}};
403{ NULL, -3 } }; 495static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}};
404static ot_keywords keywords_announce_event[] = { { "completed", 1 }, { "stopped", 2 }, { NULL, -3 } }; 496static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct *ws, char *read_ptr) {
405static ssize_t http_handle_announce( const int64 sock, struct ot_workstruct *ws, char *read_ptr ) {
406 int numwant, tmp, scanon; 497 int numwant, tmp, scanon;
407 unsigned short port = 0; 498 unsigned short port = 0;
408 char *write_ptr; 499 char *write_ptr;
409 ssize_t len; 500 ssize_t len;
410 struct http_data *cookie = io_getcookie( sock ); 501 struct http_data *cookie = io_getcookie(sock);
411 502
412 /* This is to hack around stupid clients that send "announce ?info_hash" */ 503 /* This is to hack around stupid clients that send "announce ?info_hash" */
413 if( read_ptr[-1] != '?' ) { 504 if (read_ptr[-1] != '?') {
414 while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr; 505 while ((*read_ptr != '?') && (*read_ptr != '\n'))
415 if( *read_ptr == '\n' ) HTTPERROR_400_PARAM; 506 ++read_ptr;
507 if (*read_ptr == '\n')
508 HTTPERROR_400_PARAM;
416 ++read_ptr; 509 ++read_ptr;
417 } 510 }
418 511
419#ifdef WANT_IP_FROM_PROXY 512#ifdef WANT_IP_FROM_PROXY
420 if( accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_PROXY ) ) { 513 if (accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_PROXY)) {
421 ot_ip6 proxied_ip; 514 ot_ip6 proxied_ip;
422 char *fwd = http_header( ws->request, ws->header_size, "x-forwarded-for" ); 515 char *fwd = http_header(ws->request, ws->header_size, "x-forwarded-for");
423 if( fwd && scan_ip6( fwd, proxied_ip ) ) 516 if (fwd && scan_ip6(fwd, proxied_ip)) {
424 OT_SETIP( &ws->peer, proxied_ip ); 517 OT_SETIP(ws->peer, proxied_ip);
425 else 518 } else
426 OT_SETIP( &ws->peer, cookie->ip ); 519 OT_SETIP(ws->peer, cookie->ip);
427 } else 520 } else
428#endif 521#endif
429 OT_SETIP( &ws->peer, cookie->ip ); 522 OT_SETIP(ws->peer, cookie->ip);
430 523
431 ws->peer_id = NULL; 524 ws->peer_id = NULL;
432 ws->hash = NULL; 525 ws->hash = NULL;
433 526
434 OT_SETPORT( &ws->peer, &port ); 527 OT_SETPORT(ws->peer, &port);
435 OT_PEERFLAG( &ws->peer ) = 0; 528 OT_PEERFLAG(ws->peer) = 0;
436 numwant = 50; 529 numwant = 50;
437 scanon = 1; 530 scanon = 1;
438 531
439 while( scanon ) { 532 while (scanon) {
440 switch( scan_find_keywords(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) { 533 switch (scan_find_keywords(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM)) {
441 case -2: scanon = 0; break; /* TERMINATOR */ 534 case -2:
442 case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ 535 scanon = 0;
443 case -3: scan_urlencoded_skipvalue( &read_ptr ); break; 536 break; /* TERMINATOR */
537 case -1:
538 HTTPERROR_400_PARAM; /* PARSE ERROR */
539 case -3:
540 scan_urlencoded_skipvalue(&read_ptr);
541 break;
444 case 1: /* matched "port" */ 542 case 1: /* matched "port" */
445 len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); 543 len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE);
446 if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) || ( tmp > 0xffff ) ) HTTPERROR_400_PARAM; 544 if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp) || (tmp > 0xffff))
447 port = htons( tmp ); OT_SETPORT( &ws->peer, &port ); 545 HTTPERROR_400_PARAM;
546 port = htons(tmp);
547 OT_SETPORT(&ws->peer, &port);
448 break; 548 break;
449 case 2: /* matched "left" */ 549 case 2: /* matched "left" */
450 if( ( len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; 550 if ((len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0)
451 if( scan_fixed_int( write_ptr, len, &tmp ) ) tmp = 0; 551 HTTPERROR_400_PARAM;
452 if( !tmp ) OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_SEEDING; 552 if (scan_fixed_int(write_ptr, len, &tmp))
553 tmp = 0;
554 if (!tmp)
555 OT_PEERFLAG(&ws->peer) |= PEER_FLAG_SEEDING;
453 break; 556 break;
454 case 3: /* matched "event" */ 557 case 3: /* matched "event" */
455 switch( scan_find_keywords( keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) { 558 switch (scan_find_keywords(keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE)) {
456 case -1: HTTPERROR_400_PARAM; 559 case -1:
457 case 1: /* matched "completed" */ 560 HTTPERROR_400_PARAM;
458 OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_COMPLETED; 561 case 1: /* matched "completed" */
459 break; 562 OT_PEERFLAG(&ws->peer) |= PEER_FLAG_COMPLETED;
460 case 2: /* matched "stopped" */ 563 break;
461 OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_STOPPED; 564 case 2: /* matched "stopped" */
462 break; 565 OT_PEERFLAG(&ws->peer) |= PEER_FLAG_STOPPED;
463 default: 566 break;
464 break; 567 default:
568 break;
465 } 569 }
466 break; 570 break;
467 case 4: /* matched "numwant" */ 571 case 4: /* matched "numwant" */
468 len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); 572 len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE);
469 if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &numwant ) ) HTTPERROR_400_PARAM; 573 if ((len <= 0) || scan_fixed_int(write_ptr, len, &numwant))
470 if( numwant < 0 ) numwant = 50; 574 HTTPERROR_400_PARAM;
471 if( numwant > 200 ) numwant = 200; 575 if (numwant < 0)
576 numwant = 50;
577 if (numwant > 200)
578 numwant = 200;
472 break; 579 break;
473 case 5: /* matched "compact" */ 580 case 5: /* matched "compact" */
474 len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); 581 len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE);
475 if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) ) HTTPERROR_400_PARAM; 582 if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp))
476 if( !tmp ) HTTPERROR_400_COMPACT; 583 HTTPERROR_400_PARAM;
584 if (!tmp)
585 HTTPERROR_400_COMPACT;
477 break; 586 break;
478 case 6: /* matched "info_hash" */ 587 case 6: /* matched "info_hash" */
479 if( ws->hash ) HTTPERROR_400_DOUBLEHASH; 588 if (ws->hash)
589 HTTPERROR_400_DOUBLEHASH;
480 /* ignore this, when we have less than 20 bytes */ 590 /* ignore this, when we have less than 20 bytes */
481 if( scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM; 591 if (scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20)
482 ws->hash = (ot_hash*)write_ptr; 592 HTTPERROR_400_PARAM;
593 ws->hash = (ot_hash *)write_ptr;
483 break; 594 break;
484#ifdef WANT_IP_FROM_QUERY_STRING 595#ifdef WANT_IP_FROM_QUERY_STRING
485 case 7: /* matched "ip" */ 596 case 7: /* matched "ip" */
486 { 597 {
487 char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply+16; 598 char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16;
488 len = scan_urlencoded_query( &read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE ); 599 len = scan_urlencoded_query(&read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE);
489 tmp_buf2[len] = 0; 600 tmp_buf2[len] = 0;
490 if( ( len <= 0 ) || !scan_ip6( tmp_buf2, tmp_buf1 ) ) HTTPERROR_400_PARAM; 601 if ((len <= 0) || !scan_ip6(tmp_buf2, tmp_buf1))
491 OT_SETIP( &ws->peer, tmp_buf1 ); 602 HTTPERROR_400_PARAM;
492 } 603 OT_SETIP(&ws->peer, tmp_buf1);
493 break; 604 } break;
494#endif 605#endif
495#ifdef WANT_FULLLOG_NETWORKS 606#ifdef WANT_FULLLOG_NETWORKS
496 case 8: /* matched "lognet" */ 607 case 8: /* matched "lognet" */
497 { 608 {
498 //if( accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_STAT ) ) { 609 // if( accesslist_is_blessed( cookie->ip, OT_PERMISSION_MAY_STAT ) ) {
499 char *tmp_buf = ws->reply; 610 char *tmp_buf = ws->reply;
500 ot_net net; 611 ot_net net;
501 signed short parsed, bits; 612 signed short parsed, bits;
502 613
503 len = scan_urlencoded_query( &read_ptr, tmp_buf, SCAN_SEARCHPATH_VALUE ); 614 len = scan_urlencoded_query(&read_ptr, tmp_buf, SCAN_SEARCHPATH_VALUE);
504 tmp_buf[len] = 0; 615 tmp_buf[len] = 0;
505 if( len <= 0 ) HTTPERROR_400_PARAM; 616 if (len <= 0)
506 if( *tmp_buf == '-' ) { 617 HTTPERROR_400_PARAM;
507 loglist_reset( ); 618 if (*tmp_buf == '-') {
508 return ws->reply_size = sprintf( ws->reply, "Successfully removed.\n" ); 619 loglist_reset();
509 } 620 return ws->reply_size = sprintf(ws->reply, "Successfully removed.\n");
510 parsed = scan_ip6( tmp_buf, net.address );
511 if( !parsed ) HTTPERROR_400_PARAM;
512 if( tmp_buf[parsed++] != '/' )
513 bits = 128;
514 else {
515 parsed = scan_short( tmp_buf + parsed, &bits );
516 if( !parsed ) HTTPERROR_400_PARAM;
517 if( ip6_isv4mapped( net.address ) )
518 bits += 96;
519 }
520 net.bits = bits;
521 loglist_add_network( &net );
522 return ws->reply_size = sprintf( ws->reply, "Successfully added.\n" );
523 //}
524 } 621 }
525 break; 622 parsed = scan_ip6(tmp_buf, net.address);
623 if (!parsed)
624 HTTPERROR_400_PARAM;
625 if (tmp_buf[parsed++] != '/')
626 bits = 128;
627 else {
628 parsed = scan_short(tmp_buf + parsed, &bits);
629 if (!parsed)
630 HTTPERROR_400_PARAM;
631 if (ip6_isv4mapped(net.address))
632 bits += 96;
633 }
634 net.bits = bits;
635 loglist_add_network(&net);
636 return ws->reply_size = sprintf(ws->reply, "Successfully added.\n");
637 //}
638 } break;
526#endif 639#endif
527 case 9: /* matched "peer_id" */ 640 case 9: /* matched "peer_id" */
528 /* ignore this, when we have less than 20 bytes */ 641 /* ignore this, when we have less than 20 bytes */
529 if( scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM; 642 if (scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20)
530 ws->peer_id = write_ptr; 643 HTTPERROR_400_PARAM;
531 break; 644 ws->peer_id = write_ptr;
645 break;
532 } 646 }
533 } 647 }
534 648
@@ -541,100 +655,107 @@ static ssize_t http_handle_announce( const int64 sock, struct ot_workstruct *ws,
541 */ 655 */
542 656
543 /* Scanned whole query string */ 657 /* Scanned whole query string */
544 if( !ws->hash ) 658 if (!ws->hash)
545 return ws->reply_size = sprintf( ws->reply, "d14:failure reason80:Your client forgot to send your torrent's info_hash. Please upgrade your client.e" ); 659 return ws->reply_size = sprintf(ws->reply, "d14:failure reason80:Your client forgot to send your torrent's info_hash. Please upgrade your client.e");
546 660
547 if( OT_PEERFLAG( &ws->peer ) & PEER_FLAG_STOPPED ) 661 if (OT_PEERFLAG(&ws->peer) & PEER_FLAG_STOPPED)
548 ws->reply_size = remove_peer_from_torrent( FLAG_TCP, ws ); 662 ws->reply_size = remove_peer_from_torrent(FLAG_TCP, ws);
549 else 663 else
550 ws->reply_size = add_peer_to_torrent_and_return_peers( FLAG_TCP, ws, numwant ); 664 ws->reply_size = add_peer_to_torrent_and_return_peers(FLAG_TCP, ws, numwant);
551 665
552 stats_issue_event( EVENT_ANNOUNCE, FLAG_TCP, ws->reply_size); 666 stats_issue_event(EVENT_ANNOUNCE, FLAG_TCP, ws->reply_size);
553 return ws->reply_size; 667 return ws->reply_size;
554} 668}
555 669
556ssize_t http_handle_request( const int64 sock, struct ot_workstruct *ws ) { 670ssize_t http_handle_request(const int64 sock, struct ot_workstruct *ws) {
557 ssize_t reply_off, len; 671 ssize_t reply_off, len;
558 char *read_ptr = ws->request, *write_ptr; 672 char *read_ptr = ws->request, *write_ptr;
559 673
560#ifdef WANT_FULLLOG_NETWORKS 674#ifdef WANT_FULLLOG_NETWORKS
561 struct http_data *cookie = io_getcookie( sock ); 675 struct http_data *cookie = io_getcookie(sock);
562 if( loglist_check_address( cookie->ip ) ) { 676 if (loglist_check_address(cookie->ip)) {
563 ot_log *log = malloc( sizeof( ot_log ) ); 677 ot_log *log = malloc(sizeof(ot_log));
564 if( log ) { 678 if (log) {
565 log->size = ws->request_size; 679 log->size = ws->request_size;
566 log->data = malloc( ws->request_size ); 680 log->data = malloc(ws->request_size);
567 log->next = 0; 681 log->next = 0;
568 log->time = g_now_seconds; 682 log->time = g_now_seconds;
569 memcpy( log->ip, cookie->ip, sizeof(ot_ip6)); 683 memcpy(log->ip, cookie->ip, sizeof(ot_ip6));
570 if( log->data ) { 684 if (log->data) {
571 memcpy( log->data, ws->request, ws->request_size ); 685 memcpy(log->data, ws->request, ws->request_size);
572 if( !g_logchain_first ) 686 if (!g_logchain_first)
573 g_logchain_first = g_logchain_last = log; 687 g_logchain_first = g_logchain_last = log;
574 else { 688 else {
575 g_logchain_last->next = log; 689 g_logchain_last->next = log;
576 g_logchain_last = log; 690 g_logchain_last = log;
577 } 691 }
578 } else 692 } else
579 free( log ); 693 free(log);
580 } 694 }
581 } 695 }
582#endif 696#endif
583 697
584#ifdef _DEBUG_HTTPERROR 698#ifdef _DEBUG_HTTPERROR
585 reply_off = ws->request_size; 699 reply_off = ws->request_size;
586 if( ws->request_size >= G_DEBUGBUF_SIZE ) 700 if (ws->request_size >= G_DEBUGBUF_SIZE)
587 reply_off = G_DEBUGBUF_SIZE - 1; 701 reply_off = G_DEBUGBUF_SIZE - 1;
588 memcpy( ws->debugbuf, ws->request, reply_off ); 702 memcpy(ws->debugbuf, ws->request, reply_off);
589 ws->debugbuf[ reply_off ] = 0; 703 ws->debugbuf[reply_off] = 0;
590#endif 704#endif
591 705
592 /* Tell subroutines where to put reply data */ 706 /* Tell subroutines where to put reply data */
593 ws->reply = ws->outbuf + SUCCESS_HTTP_HEADER_LENGTH; 707 ws->reply = ws->outbuf + SUCCESS_HTTP_HEADER_LENGTH;
594 708
595 /* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */ 709 /* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */
596 if( memcmp( read_ptr, "GET /", 5) ) HTTPERROR_400; 710 if (memcmp(read_ptr, "GET /", 5))
711 HTTPERROR_400;
597 712
598 /* Skip leading '/' */ 713 /* Skip leading '/' */
599 for( read_ptr+=4; *read_ptr == '/'; ++read_ptr); 714 for (read_ptr += 4; *read_ptr == '/'; ++read_ptr)
715 ;
600 716
601 /* Try to parse the request. 717 /* Try to parse the request.
602 In reality we abandoned requiring the url to be correct. This now 718 In reality we abandoned requiring the url to be correct. This now
603 only decodes url encoded characters, we check for announces and 719 only decodes url encoded characters, we check for announces and
604 scrapes by looking for "a*" or "sc" */ 720 scrapes by looking for "a*" or "sc" */
605 len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_PATH ); 721 len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_PATH);
606 722
607 /* If parsing returned an error, leave with not found */ 723 /* If parsing returned an error, leave with not found */
608 if( g_redirecturl && ( len == -2 ) ) HTTPERROR_302; 724 if (g_redirecturl && (len == -2))
609 if( len <= 0 ) HTTPERROR_404; 725 HTTPERROR_302;
726 if (len <= 0)
727 HTTPERROR_404;
610 728
611 /* This is the hardcore match for announce*/ 729 /* This is the hardcore match for announce*/
612 if( ( *write_ptr == 'a' ) || ( *write_ptr == '?' ) ) 730 if ((*write_ptr == 'a') || (*write_ptr == '?'))
613 http_handle_announce( sock, ws, read_ptr ); 731 http_handle_announce(sock, ws, read_ptr);
614#ifdef WANT_FULLSCRAPE 732#ifdef WANT_FULLSCRAPE
615 else if( !memcmp( write_ptr, "scrape HTTP/", 12 ) ) 733 else if (!memcmp(write_ptr, "scrape HTTP/", 12))
616 http_handle_fullscrape( sock, ws ); 734 http_handle_fullscrape(sock, ws);
617#endif 735#endif
618 /* This is the hardcore match for scrape */ 736 /* This is the hardcore match for scrape */
619 else if( !memcmp( write_ptr, "sc", 2 ) ) 737 else if (!memcmp(write_ptr, "sc", 2))
620 http_handle_scrape( sock, ws, read_ptr ); 738 http_handle_scrape(sock, ws, read_ptr);
621 /* All the rest is matched the standard way */ 739 /* All the rest is matched the standard way */
622 else if( len == g_stats_path_len && !memcmp( write_ptr, g_stats_path, len ) ) 740 else if (len == g_stats_path_len && !memcmp(write_ptr, g_stats_path, len))
623 http_handle_stats( sock, ws, read_ptr ); 741 http_handle_stats(sock, ws, read_ptr);
624 else 742 else
625 HTTPERROR_404; 743 HTTPERROR_404;
626 744
627 /* Find out if the client wants to keep this connection alive */ 745 /* Find out if the client wants to keep this connection alive */
628 ws->keep_alive = 0; 746 ws->keep_alive = 0;
629#ifdef WANT_KEEPALIVE 747#ifdef WANT_KEEPALIVE
630 read_ptr=http_header( ws->request, ws->header_size, "connection"); 748 read_ptr = http_header(ws->request, ws->header_size, "connection");
631 if( read_ptr && ( *read_ptr == 'K' || *read_ptr == 'k' ) ) ws->keep_alive = 1; 749 if (read_ptr && (*read_ptr == 'K' || *read_ptr == 'k'))
750 ws->keep_alive = 1;
632#endif 751#endif
633 752
634 /* If routines handled sending themselves, just return */ 753 /* If routines handled sending themselves, just return */
635 if( ws->reply_size == -2 ) return 0; 754 if (ws->reply_size == -2)
755 return 0;
636 /* If routine failed, let http error take over */ 756 /* If routine failed, let http error take over */
637 if( ws->reply_size <= 0 ) HTTPERROR_500; 757 if (ws->reply_size <= 0)
758 HTTPERROR_500;
638 759
639 /* This one is rather ugly, so I take you step by step through it. 760 /* This one is rather ugly, so I take you step by step through it.
640 761
@@ -643,18 +764,16 @@ ssize_t http_handle_request( const int64 sock, struct ot_workstruct *ws ) {
643 plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for its expansion and calculate 764 plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for its expansion and calculate
644 the space NOT needed to expand in reply_off 765 the space NOT needed to expand in reply_off
645 */ 766 */
646 reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf( ws->outbuf, 0, "%zd", ws->reply_size ); 767 reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf(ws->outbuf, 0, "%zd", ws->reply_size);
647 ws->reply = ws->outbuf + reply_off; 768 ws->reply = ws->outbuf + reply_off;
648 769
649 /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete 770 /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete
650 packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */ 771 packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */
651 ws->reply_size += 1 + sprintf( ws->reply, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", ws->reply_size ); 772 ws->reply_size += 1 + sprintf(ws->reply, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", ws->reply_size);
652 773
653 /* 3. Finally we join both blocks neatly */ 774 /* 3. Finally we join both blocks neatly */
654 ws->outbuf[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n'; 775 ws->outbuf[SUCCESS_HTTP_HEADER_LENGTH - 1] = '\n';
655 776
656 http_senddata( sock, ws ); 777 http_senddata(sock, ws);
657 return ws->reply_size; 778 return ws->reply_size;
658} 779}
659
660const char *g_version_http_c = "$Source$: $Revision$\n";