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