summaryrefslogtreecommitdiff
path: root/ot_http.c
diff options
context:
space:
mode:
Diffstat (limited to 'ot_http.c')
-rw-r--r--ot_http.c836
1 files changed, 478 insertions, 358 deletions
diff --git a/ot_http.c b/ot_http.c
index 6869687..af3f210 100644
--- a/ot_http.c
+++ b/ot_http.c
@@ -4,530 +4,645 @@
4 $id$ */ 4 $id$ */
5 5
6/* System */ 6/* System */
7#include <sys/types.h>
8#include <arpa/inet.h> 7#include <arpa/inet.h>
9#include <stdlib.h> 8#include <pthread.h>
9#define _GNU_SOURCE
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; 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_munmap( 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 }, { 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;
287 case 3:
288 HTTPERROR_400_PARAM; /* If the stats URL was mistakenly added as announce URL, return a 400 */
233 } 289 }
234 } 290 }
235 291
236#ifdef WANT_FULLSCRAPE 292#ifdef WANT_FULLSCRAPE
237 if( mode == TASK_FULLSCRAPE_TRACKERSTATE ) { 293 if (mode == TASK_FULLSCRAPE_TRACKERSTATE) {
238 format = mode; mode = TASK_STATS_TPB; 294 format = mode;
295 mode = TASK_STATS_TPB;
239 } 296 }
240 297
241 if( mode == TASK_STATS_TPB ) { 298 if (mode == TASK_STATS_TPB) {
242 struct http_data* cookie = io_getcookie( sock ); 299 struct http_data *cookie = io_getcookie(sock);
243 tai6464 t; 300 tai6464 t;
244#ifdef WANT_COMPRESSION_GZIP 301#ifdef WANT_COMPRESSION_GZIP
245 ws->request[ws->request_size] = 0; 302 ws->request[ws->request_size] = 0;
246#ifdef WANT_COMPRESSION_GZIP_ALWAYS 303#ifndef WANT_COMPRESSION_GZIP_ALWAYS
247 if( strstr( read_ptr - 1, "gzip" ) ) { 304 if (strstr(read_ptr - 1, "gzip")) {
248#endif 305#endif
249 cookie->flag |= STRUCT_HTTP_FLAG_GZIP; 306 cookie->flag |= STRUCT_HTTP_FLAG_GZIP;
250 format |= TASK_FLAG_GZIP; 307 format |= TASK_FLAG_GZIP;
251#ifdef WANT_COMPRESSION_GZIP_ALWAYS 308#ifndef WANT_COMPRESSION_GZIP_ALWAYS
252 } 309 }
253#endif 310#endif
254#endif 311#endif
255 /* Pass this task to the worker thread */ 312 /* Pass this task to the worker thread */
256 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK; 313 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED;
257 314
258 /* Clients waiting for us should not easily timeout */ 315 /* Clients waiting for us should not easily timeout */
259 taia_uint( &t, 0 ); io_timeout( sock, t ); 316 taia_uint(&t, 0);
260 fullscrape_deliver( sock, format ); 317 io_timeout(sock, t);
261 io_dontwantread( sock ); 318 fullscrape_deliver(sock, format);
319 io_dontwantread(sock);
262 return ws->reply_size = -2; 320 return ws->reply_size = -2;
263 } 321 }
264#endif 322#endif
265 323
266 /* default format for now */ 324 /* default format for now */
267 if( ( mode & TASK_CLASS_MASK ) == TASK_STATS ) { 325 if ((mode & TASK_CLASS_MASK) == TASK_STATS) {
268 tai6464 t; 326 tai6464 t;
269 /* Complex stats also include expensive memory debugging tools */ 327 /* Complex stats also include expensive memory debugging tools */
270 taia_uint( &t, 0 ); io_timeout( sock, t ); 328 taia_uint(&t, 0);
271 stats_deliver( sock, mode ); 329 io_timeout(sock, t);
330 stats_deliver(sock, mode);
272 return ws->reply_size = -2; 331 return ws->reply_size = -2;
273 } 332 }
274 333
275 /* Simple stats can be answerred immediately */ 334 /* Simple stats can be answerred immediately */
276 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);
277} 336}
278 337
279#ifdef WANT_MODEST_FULLSCRAPES 338#ifdef WANT_MODEST_FULLSCRAPES
280static pthread_mutex_t g_modest_fullscrape_mutex = PTHREAD_MUTEX_INITIALIZER; 339static pthread_mutex_t g_modest_fullscrape_mutex = PTHREAD_MUTEX_INITIALIZER;
281static ot_vector g_modest_fullscrape_timeouts; 340static ot_vector g_modest_fullscrape_timeouts;
282typedef 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;
283#endif 345#endif
284 346
285#ifdef WANT_FULLSCRAPE 347#ifdef WANT_FULLSCRAPE
286static 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) {
287 struct http_data* cookie = io_getcookie( sock ); 349 struct http_data *cookie = io_getcookie(sock);
288 int format = 0; 350 int format = 0;
289 tai6464 t; 351 tai6464 t;
290 352
291#ifdef WANT_MODEST_FULLSCRAPES 353#ifdef WANT_MODEST_FULLSCRAPES
292 { 354 {
293 ot_scrape_log this_peer, *new_peer; 355 ot_scrape_log this_peer, *new_peer;
294 int exactmatch; 356 int exactmatch;
295 memcpy( this_peer.ip, cookie->ip, sizeof(ot_ip6)); 357 memcpy(this_peer.ip, cookie->ip, sizeof(ot_ip6));
296 this_peer.last_fullscrape = g_now_seconds; 358 this_peer.last_fullscrape = g_now_seconds;
297 pthread_mutex_lock(&g_modest_fullscrape_mutex); 359 pthread_mutex_lock(&g_modest_fullscrape_mutex);
298 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);
299 if( !new_peer ) { 361 if (!new_peer) {
300 pthread_mutex_unlock(&g_modest_fullscrape_mutex); 362 pthread_mutex_unlock(&g_modest_fullscrape_mutex);
301 HTTPERROR_500; 363 HTTPERROR_500;
302 } 364 }
303 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) {
304 pthread_mutex_unlock(&g_modest_fullscrape_mutex); 366 pthread_mutex_unlock(&g_modest_fullscrape_mutex);
305 HTTPERROR_402_NOTMODEST; 367 HTTPERROR_402_NOTMODEST;
306 } 368 }
307 memcpy( new_peer, &this_peer, sizeof(ot_scrape_log)); 369 memcpy(new_peer, &this_peer, sizeof(ot_scrape_log));
308 pthread_mutex_unlock(&g_modest_fullscrape_mutex); 370 pthread_mutex_unlock(&g_modest_fullscrape_mutex);
309 } 371 }
310#endif 372#endif
311 373
374
375#if defined(WANT_COMPRESSION_GZIP) || defined(WANT_COMPRESSION_ZSTD)
376 ws->request[ws->request_size - 1] = 0;
312#ifdef WANT_COMPRESSION_GZIP 377#ifdef WANT_COMPRESSION_GZIP
313 ws->request[ws->request_size-1] = 0; 378 if (strstr(ws->request, "gzip")) {
314 if( strstr( ws->request, "gzip" ) ) {
315 cookie->flag |= STRUCT_HTTP_FLAG_GZIP; 379 cookie->flag |= STRUCT_HTTP_FLAG_GZIP;
316 format = TASK_FLAG_GZIP; 380 format |= TASK_FLAG_GZIP;
317 stats_issue_event( EVENT_FULLSCRAPE_REQUEST_GZIP, 0, (uintptr_t)cookie->ip ); 381 }
318 } 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;
319#endif 393#endif
320 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);
321 402
322#ifdef _DEBUG_HTTPERROR 403#ifdef _DEBUG_HTTPERROR
323 fprintf( stderr, "%s", ws->debugbuf ); 404 fprintf(stderr, "%s", ws->debugbuf);
324#endif 405#endif
325 406
326 /* Pass this task to the worker thread */ 407 /* Pass this task to the worker thread */
327 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK; 408 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED;
328 /* Clients waiting for us should not easily timeout */ 409 /* Clients waiting for us should not easily timeout */
329 taia_uint( &t, 0 ); io_timeout( sock, t ); 410 taia_uint(&t, 0);
330 fullscrape_deliver( sock, TASK_FULLSCRAPE | format ); 411 io_timeout(sock, t);
331 io_dontwantread( sock ); 412 fullscrape_deliver(sock, TASK_FULLSCRAPE | format);
413 io_dontwantread(sock);
332 return ws->reply_size = -2; 414 return ws->reply_size = -2;
333} 415}
334#endif 416#endif
335 417
336static 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) {
337 static const ot_keywords keywords_scrape[] = { { "info_hash", 1 }, { NULL, -3 } }; 419 static const ot_keywords keywords_scrape[] = {{"info_hash", 1}, {NULL, -3}};
338 420
339 ot_hash * multiscrape_buf = (ot_hash*)ws->request; 421 ot_hash *multiscrape_buf = (ot_hash *)ws->request;
340 int scanon = 1, numwant = 0; 422 int scanon = 1, numwant = 0;
341 423
342 /* 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" */
343 if( read_ptr[-1] != '?' ) { 425 if (read_ptr[-1] != '?') {
344 while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr; 426 while ((*read_ptr != '?') && (*read_ptr != '\n'))
345 if( *read_ptr == '\n' ) HTTPERROR_400_PARAM; 427 ++read_ptr;
428 if (*read_ptr == '\n')
429 HTTPERROR_400_PARAM;
346 ++read_ptr; 430 ++read_ptr;
347 } 431 }
348 432
349 while( scanon ) { 433 while (scanon) {
350 switch( scan_find_keywords( keywords_scrape, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) { 434 switch (scan_find_keywords(keywords_scrape, &read_ptr, SCAN_SEARCHPATH_PARAM)) {
351 case -2: scanon = 0; break; /* TERMINATOR */ 435 case -2:
352 default: HTTPERROR_400_PARAM; /* PARSE ERROR */ 436 scanon = 0;
353 case -3: scan_urlencoded_skipvalue( &read_ptr ); break; 437 break; /* TERMINATOR */
354 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" */
355 /* ignore this, when we have less than 20 bytes */ 444 /* ignore this, when we have less than 20 bytes */
356 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))
357 HTTPERROR_400_PARAM; 446 HTTPERROR_400_PARAM;
358 break; 447 break;
359 } 448 }
360 } 449 }
361 450
362 /* No info_hash found? Inform user */ 451 /* No info_hash found? Inform user */
363 if( !numwant ) HTTPERROR_400_PARAM; 452 if (!numwant)
453 HTTPERROR_400_PARAM;
364 454
365 /* Limit number of hashes to process */ 455 /* Limit number of hashes to process */
366 if( numwant > OT_MAXMULTISCRAPE_COUNT ) 456 if (numwant > OT_MAXMULTISCRAPE_COUNT)
367 numwant = OT_MAXMULTISCRAPE_COUNT; 457 numwant = OT_MAXMULTISCRAPE_COUNT;
368 458
369 /* Enough for http header + whole scrape string */ 459 /* Enough for http header + whole scrape string */
370 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);
371 stats_issue_event( EVENT_SCRAPE, FLAG_TCP, ws->reply_size ); 461 stats_issue_event(EVENT_SCRAPE, FLAG_TCP, ws->reply_size);
372 return ws->reply_size; 462 return ws->reply_size;
373} 463}
374 464
375#ifdef WANT_LOG_NUMWANT 465#ifdef WANT_LOG_NUMWANT
376 unsigned long long numwants[201]; 466unsigned long long numwants[201];
377#endif 467#endif
378 468
379#if defined( WANT_KEEPALIVE ) || defined( WANT_IP_FROM_PROXY ) 469#if defined(WANT_KEEPALIVE) || defined(WANT_IP_FROM_PROXY)
380static char* http_header( char *data, size_t byte_count, char *header ) { 470static char *http_header(char *data, size_t byte_count, char *header) {
381 size_t i; 471 size_t i;
382 long sl = strlen( header ); 472 long sl = strlen(header);
383 for( i = 0; i + sl + 2 < byte_count; ++i ) { 473 for (i = 0; i + sl + 2 < byte_count; ++i) {
384 if( data[i] != '\n' || data[ i + sl + 1] != ':' ) continue; 474 if (data[i] != '\n' || data[i + sl + 1] != ':')
385 if( !case_equalb( data + i + 1, sl, header ) ) continue; 475 continue;
476 if (!case_equalb(data + i + 1, sl, header))
477 continue;
386 data += i + sl + 2; 478 data += i + sl + 2;
387 while( *data == ' ' || *data == '\t' ) ++data; 479 while (*data == ' ' || *data == '\t')
480 ++data;
388 return data; 481 return data;
389 } 482 }
390 return 0; 483 return 0;
391} 484}
392#endif 485#endif
393 486
394static 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},
395#ifdef WANT_IP_FROM_QUERY_STRING 488#ifdef WANT_IP_FROM_QUERY_STRING
396{ "ip", 7 }, 489 {"ip", 7},
397#endif 490#endif
398#ifdef WANT_FULLLOG_NETWORKS 491#ifdef WANT_FULLLOG_NETWORKS
399{ "lognet", 8 }, 492 {"lognet", 8},
400#endif 493#endif
401{ "peer_id", 9 }, 494 {"peer_id", 9}, {NULL, -3}};
402{ NULL, -3 } }; 495static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}};
403static 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) {
404static ssize_t http_handle_announce( const int64 sock, struct ot_workstruct *ws, char *read_ptr ) {
405 int numwant, tmp, scanon; 497 int numwant, tmp, scanon;
406 unsigned short port = 0; 498 unsigned short port = 0;
407 char *write_ptr; 499 char *write_ptr;
408 ssize_t len; 500 ssize_t len;
409 struct http_data *cookie = io_getcookie( sock ); 501 struct http_data *cookie = io_getcookie(sock);
410 502
411 /* 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" */
412 if( read_ptr[-1] != '?' ) { 504 if (read_ptr[-1] != '?') {
413 while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr; 505 while ((*read_ptr != '?') && (*read_ptr != '\n'))
414 if( *read_ptr == '\n' ) HTTPERROR_400_PARAM; 506 ++read_ptr;
507 if (*read_ptr == '\n')
508 HTTPERROR_400_PARAM;
415 ++read_ptr; 509 ++read_ptr;
416 } 510 }
417 511
418#ifdef WANT_IP_FROM_PROXY 512#ifdef WANT_IP_FROM_PROXY
419 if( accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_PROXY ) ) { 513 if (accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_PROXY)) {
420 ot_ip6 proxied_ip; 514 ot_ip6 proxied_ip;
421 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");
422 if( fwd && scan_ip6( fwd, proxied_ip ) ) 516 if (fwd && scan_ip6(fwd, proxied_ip)) {
423 OT_SETIP( &ws->peer, proxied_ip ); 517 OT_SETIP(ws->peer, proxied_ip);
424 else 518 } else
425 OT_SETIP( &ws->peer, cookie->ip ); 519 OT_SETIP(ws->peer, cookie->ip);
426 } else 520 } else
427#endif 521#endif
428 OT_SETIP( &ws->peer, cookie->ip ); 522 OT_SETIP(ws->peer, cookie->ip);
429 523
430 ws->peer_id = NULL; 524 ws->peer_id = NULL;
431 ws->hash = NULL; 525 ws->hash = NULL;
432 526
433 OT_SETPORT( &ws->peer, &port ); 527 OT_SETPORT(ws->peer, &port);
434 OT_PEERFLAG( &ws->peer ) = 0; 528 OT_PEERFLAG(ws->peer) = 0;
435 numwant = 50; 529 numwant = 50;
436 scanon = 1; 530 scanon = 1;
437 531
438 while( scanon ) { 532 while (scanon) {
439 switch( scan_find_keywords(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) { 533 switch (scan_find_keywords(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM)) {
440 case -2: scanon = 0; break; /* TERMINATOR */ 534 case -2:
441 case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ 535 scanon = 0;
442 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;
443 case 1: /* matched "port" */ 542 case 1: /* matched "port" */
444 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);
445 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))
446 port = htons( tmp ); OT_SETPORT( &ws->peer, &port ); 545 HTTPERROR_400_PARAM;
546 port = htons(tmp);
547 OT_SETPORT(&ws->peer, &port);
447 break; 548 break;
448 case 2: /* matched "left" */ 549 case 2: /* matched "left" */
449 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)
450 if( scan_fixed_int( write_ptr, len, &tmp ) ) tmp = 0; 551 HTTPERROR_400_PARAM;
451 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;
452 break; 556 break;
453 case 3: /* matched "event" */ 557 case 3: /* matched "event" */
454 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)) {
455 case -1: HTTPERROR_400_PARAM; 559 case -1:
456 case 1: /* matched "completed" */ 560 HTTPERROR_400_PARAM;
457 OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_COMPLETED; 561 case 1: /* matched "completed" */
458 break; 562 OT_PEERFLAG(&ws->peer) |= PEER_FLAG_COMPLETED;
459 case 2: /* matched "stopped" */ 563 break;
460 OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_STOPPED; 564 case 2: /* matched "stopped" */
461 break; 565 OT_PEERFLAG(&ws->peer) |= PEER_FLAG_STOPPED;
462 default: 566 break;
463 break; 567 default:
568 break;
464 } 569 }
465 break; 570 break;
466 case 4: /* matched "numwant" */ 571 case 4: /* matched "numwant" */
467 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);
468 if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &numwant ) ) HTTPERROR_400_PARAM; 573 if ((len <= 0) || scan_fixed_int(write_ptr, len, &numwant))
469 if( numwant < 0 ) numwant = 50; 574 HTTPERROR_400_PARAM;
470 if( numwant > 200 ) numwant = 200; 575 if (numwant < 0)
576 numwant = 50;
577 if (numwant > 200)
578 numwant = 200;
471 break; 579 break;
472 case 5: /* matched "compact" */ 580 case 5: /* matched "compact" */
473 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);
474 if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) ) HTTPERROR_400_PARAM; 582 if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp))
475 if( !tmp ) HTTPERROR_400_COMPACT; 583 HTTPERROR_400_PARAM;
584 if (!tmp)
585 HTTPERROR_400_COMPACT;
476 break; 586 break;
477 case 6: /* matched "info_hash" */ 587 case 6: /* matched "info_hash" */
478 if( ws->hash ) HTTPERROR_400_DOUBLEHASH; 588 if (ws->hash)
589 HTTPERROR_400_DOUBLEHASH;
479 /* ignore this, when we have less than 20 bytes */ 590 /* ignore this, when we have less than 20 bytes */
480 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)
481 ws->hash = (ot_hash*)write_ptr; 592 HTTPERROR_400_PARAM;
593 ws->hash = (ot_hash *)write_ptr;
482 break; 594 break;
483#ifdef WANT_IP_FROM_QUERY_STRING 595#ifdef WANT_IP_FROM_QUERY_STRING
484 case 7: /* matched "ip" */ 596 case 7: /* matched "ip" */
485 { 597 {
486 char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply+16; 598 char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16;
487 len = scan_urlencoded_query( &read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE ); 599 len = scan_urlencoded_query(&read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE);
488 tmp_buf2[len] = 0; 600 tmp_buf2[len] = 0;
489 if( ( len <= 0 ) || !scan_ip6( tmp_buf2, tmp_buf1 ) ) HTTPERROR_400_PARAM; 601 if ((len <= 0) || !scan_ip6(tmp_buf2, tmp_buf1))
490 OT_SETIP( &ws->peer, tmp_buf1 ); 602 HTTPERROR_400_PARAM;
491 } 603 OT_SETIP(&ws->peer, tmp_buf1);
492 break; 604 } break;
493#endif 605#endif
494#ifdef WANT_FULLLOG_NETWORKS 606#ifdef WANT_FULLLOG_NETWORKS
495 case 8: /* matched "lognet" */ 607 case 8: /* matched "lognet" */
496 { 608 {
497 //if( accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_STAT ) ) { 609 // if( accesslist_is_blessed( cookie->ip, OT_PERMISSION_MAY_STAT ) ) {
498 char *tmp_buf = ws->reply; 610 char *tmp_buf = ws->reply;
499 ot_net net; 611 ot_net net;
500 signed short parsed, bits; 612 signed short parsed, bits;
501 613
502 len = scan_urlencoded_query( &read_ptr, tmp_buf, SCAN_SEARCHPATH_VALUE ); 614 len = scan_urlencoded_query(&read_ptr, tmp_buf, SCAN_SEARCHPATH_VALUE);
503 tmp_buf[len] = 0; 615 tmp_buf[len] = 0;
504 if( len <= 0 ) HTTPERROR_400_PARAM; 616 if (len <= 0)
505 if( *tmp_buf == '-' ) { 617 HTTPERROR_400_PARAM;
506 loglist_reset( ); 618 if (*tmp_buf == '-') {
507 return ws->reply_size = sprintf( ws->reply, "Successfully removed.\n" ); 619 loglist_reset();
508 } 620 return ws->reply_size = sprintf(ws->reply, "Successfully removed.\n");
509 parsed = scan_ip6( tmp_buf, net.address );
510 if( !parsed ) HTTPERROR_400_PARAM;
511 if( tmp_buf[parsed++] != '/' )
512 bits = 128;
513 else {
514 parsed = scan_short( tmp_buf + parsed, &bits );
515 if( !parsed ) HTTPERROR_400_PARAM;
516 if( ip6_isv4mapped( net.address ) )
517 bits += 96;
518 }
519 net.bits = bits;
520 loglist_add_network( &net );
521 return ws->reply_size = sprintf( ws->reply, "Successfully added.\n" );
522 //}
523 } 621 }
524 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;
525#endif 639#endif
526 case 9: /* matched "peer_id" */ 640 case 9: /* matched "peer_id" */
527 /* ignore this, when we have less than 20 bytes */ 641 /* ignore this, when we have less than 20 bytes */
528 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)
529 ws->peer_id = write_ptr; 643 HTTPERROR_400_PARAM;
530 break; 644 ws->peer_id = write_ptr;
645 break;
531 } 646 }
532 } 647 }
533 648
@@ -540,100 +655,107 @@ static ssize_t http_handle_announce( const int64 sock, struct ot_workstruct *ws,
540 */ 655 */
541 656
542 /* Scanned whole query string */ 657 /* Scanned whole query string */
543 if( !ws->hash ) 658 if (!ws->hash)
544 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");
545 660
546 if( OT_PEERFLAG( &ws->peer ) & PEER_FLAG_STOPPED ) 661 if (OT_PEERFLAG(&ws->peer) & PEER_FLAG_STOPPED)
547 ws->reply_size = remove_peer_from_torrent( FLAG_TCP, ws ); 662 ws->reply_size = remove_peer_from_torrent(FLAG_TCP, ws);
548 else 663 else
549 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);
550 665
551 stats_issue_event( EVENT_ANNOUNCE, FLAG_TCP, ws->reply_size); 666 stats_issue_event(EVENT_ANNOUNCE, FLAG_TCP, ws->reply_size);
552 return ws->reply_size; 667 return ws->reply_size;
553} 668}
554 669
555ssize_t http_handle_request( const int64 sock, struct ot_workstruct *ws ) { 670ssize_t http_handle_request(const int64 sock, struct ot_workstruct *ws) {
556 ssize_t reply_off, len; 671 ssize_t reply_off, len;
557 char *read_ptr = ws->request, *write_ptr; 672 char *read_ptr = ws->request, *write_ptr;
558 673
559#ifdef WANT_FULLLOG_NETWORKS 674#ifdef WANT_FULLLOG_NETWORKS
560 struct http_data *cookie = io_getcookie( sock ); 675 struct http_data *cookie = io_getcookie(sock);
561 if( loglist_check_address( cookie->ip ) ) { 676 if (loglist_check_address(cookie->ip)) {
562 ot_log *log = malloc( sizeof( ot_log ) ); 677 ot_log *log = malloc(sizeof(ot_log));
563 if( log ) { 678 if (log) {
564 log->size = ws->request_size; 679 log->size = ws->request_size;
565 log->data = malloc( ws->request_size ); 680 log->data = malloc(ws->request_size);
566 log->next = 0; 681 log->next = 0;
567 log->time = g_now_seconds; 682 log->time = g_now_seconds;
568 memcpy( log->ip, cookie->ip, sizeof(ot_ip6)); 683 memcpy(log->ip, cookie->ip, sizeof(ot_ip6));
569 if( log->data ) { 684 if (log->data) {
570 memcpy( log->data, ws->request, ws->request_size ); 685 memcpy(log->data, ws->request, ws->request_size);
571 if( !g_logchain_first ) 686 if (!g_logchain_first)
572 g_logchain_first = g_logchain_last = log; 687 g_logchain_first = g_logchain_last = log;
573 else { 688 else {
574 g_logchain_last->next = log; 689 g_logchain_last->next = log;
575 g_logchain_last = log; 690 g_logchain_last = log;
576 } 691 }
577 } else 692 } else
578 free( log ); 693 free(log);
579 } 694 }
580 } 695 }
581#endif 696#endif
582 697
583#ifdef _DEBUG_HTTPERROR 698#ifdef _DEBUG_HTTPERROR
584 reply_off = ws->request_size; 699 reply_off = ws->request_size;
585 if( ws->request_size >= G_DEBUGBUF_SIZE ) 700 if (ws->request_size >= G_DEBUGBUF_SIZE)
586 reply_off = G_DEBUGBUF_SIZE - 1; 701 reply_off = G_DEBUGBUF_SIZE - 1;
587 memcpy( ws->debugbuf, ws->request, reply_off ); 702 memcpy(ws->debugbuf, ws->request, reply_off);
588 ws->debugbuf[ reply_off ] = 0; 703 ws->debugbuf[reply_off] = 0;
589#endif 704#endif
590 705
591 /* Tell subroutines where to put reply data */ 706 /* Tell subroutines where to put reply data */
592 ws->reply = ws->outbuf + SUCCESS_HTTP_HEADER_LENGTH; 707 ws->reply = ws->outbuf + SUCCESS_HTTP_HEADER_LENGTH;
593 708
594 /* 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 */
595 if( memcmp( read_ptr, "GET /", 5) ) HTTPERROR_400; 710 if (memcmp(read_ptr, "GET /", 5))
711 HTTPERROR_400;
596 712
597 /* Skip leading '/' */ 713 /* Skip leading '/' */
598 for( read_ptr+=4; *read_ptr == '/'; ++read_ptr); 714 for (read_ptr += 4; *read_ptr == '/'; ++read_ptr)
715 ;
599 716
600 /* Try to parse the request. 717 /* Try to parse the request.
601 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
602 only decodes url encoded characters, we check for announces and 719 only decodes url encoded characters, we check for announces and
603 scrapes by looking for "a*" or "sc" */ 720 scrapes by looking for "a*" or "sc" */
604 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);
605 722
606 /* If parsing returned an error, leave with not found */ 723 /* If parsing returned an error, leave with not found */
607 if( g_redirecturl && ( len == -2 ) ) HTTPERROR_302; 724 if (g_redirecturl && (len == -2))
608 if( len <= 0 ) HTTPERROR_404; 725 HTTPERROR_302;
726 if (len <= 0)
727 HTTPERROR_404;
609 728
610 /* This is the hardcore match for announce*/ 729 /* This is the hardcore match for announce*/
611 if( ( *write_ptr == 'a' ) || ( *write_ptr == '?' ) ) 730 if ((*write_ptr == 'a') || (*write_ptr == '?'))
612 http_handle_announce( sock, ws, read_ptr ); 731 http_handle_announce(sock, ws, read_ptr);
613#ifdef WANT_FULLSCRAPE 732#ifdef WANT_FULLSCRAPE
614 else if( !memcmp( write_ptr, "scrape HTTP/", 12 ) ) 733 else if (!memcmp(write_ptr, "scrape HTTP/", 12))
615 http_handle_fullscrape( sock, ws ); 734 http_handle_fullscrape(sock, ws);
616#endif 735#endif
617 /* This is the hardcore match for scrape */ 736 /* This is the hardcore match for scrape */
618 else if( !memcmp( write_ptr, "sc", 2 ) ) 737 else if (!memcmp(write_ptr, "sc", 2))
619 http_handle_scrape( sock, ws, read_ptr ); 738 http_handle_scrape(sock, ws, read_ptr);
620 /* All the rest is matched the standard way */ 739 /* All the rest is matched the standard way */
621 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))
622 http_handle_stats( sock, ws, read_ptr ); 741 http_handle_stats(sock, ws, read_ptr);
623 else 742 else
624 HTTPERROR_404; 743 HTTPERROR_404;
625 744
626 /* Find out if the client wants to keep this connection alive */ 745 /* Find out if the client wants to keep this connection alive */
627 ws->keep_alive = 0; 746 ws->keep_alive = 0;
628#ifdef WANT_KEEPALIVE 747#ifdef WANT_KEEPALIVE
629 read_ptr=http_header( ws->request, ws->header_size, "connection"); 748 read_ptr = http_header(ws->request, ws->header_size, "connection");
630 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;
631#endif 751#endif
632 752
633 /* If routines handled sending themselves, just return */ 753 /* If routines handled sending themselves, just return */
634 if( ws->reply_size == -2 ) return 0; 754 if (ws->reply_size == -2)
755 return 0;
635 /* If routine failed, let http error take over */ 756 /* If routine failed, let http error take over */
636 if( ws->reply_size <= 0 ) HTTPERROR_500; 757 if (ws->reply_size <= 0)
758 HTTPERROR_500;
637 759
638 /* 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.
639 761
@@ -642,18 +764,16 @@ ssize_t http_handle_request( const int64 sock, struct ot_workstruct *ws ) {
642 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
643 the space NOT needed to expand in reply_off 765 the space NOT needed to expand in reply_off
644 */ 766 */
645 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);
646 ws->reply = ws->outbuf + reply_off; 768 ws->reply = ws->outbuf + reply_off;
647 769
648 /* 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
649 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 */
650 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);
651 773
652 /* 3. Finally we join both blocks neatly */ 774 /* 3. Finally we join both blocks neatly */
653 ws->outbuf[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n'; 775 ws->outbuf[SUCCESS_HTTP_HEADER_LENGTH - 1] = '\n';
654 776
655 http_senddata( sock, ws ); 777 http_senddata(sock, ws);
656 return ws->reply_size; 778 return ws->reply_size;
657} 779}
658
659const char *g_version_http_c = "$Source$: $Revision$\n";