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