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