summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile10
-rw-r--r--opentracker.c54
-rw-r--r--ot_accesslist.c30
-rw-r--r--ot_accesslist.h2
-rw-r--r--ot_buf.c41
-rw-r--r--ot_buf.h27
-rw-r--r--ot_fullscrape.c2
-rw-r--r--ot_fullscrape.h2
-rw-r--r--ot_hex.c20
-rw-r--r--ot_hex.h4
-rw-r--r--ot_http.c81
-rw-r--r--ot_http.h10
-rw-r--r--ot_ip.c201
-rw-r--r--ot_ip.h33
-rw-r--r--ot_livesync.c14
-rw-r--r--ot_livesync.h2
-rw-r--r--ot_mutex.c13
-rw-r--r--ot_mutex.h14
-rw-r--r--ot_stats.c15
-rw-r--r--ot_stats.h2
-rw-r--r--ot_sync.c2
-rw-r--r--ot_sync.h4
-rw-r--r--ot_udp.c15
-rw-r--r--ot_udp.h6
-rw-r--r--ot_vector.c2
-rw-r--r--proxy.c36
-rw-r--r--tests/runner.c2
-rw-r--r--tests/t_buf.c205
-rw-r--r--tests/t_ip.c217
-rw-r--r--tests/test.h2
-rw-r--r--trackerlogic.c12
31 files changed, 925 insertions, 155 deletions
diff --git a/Makefile b/Makefile
index 591ddb0..27d635d 100644
--- a/Makefile
+++ b/Makefile
@@ -68,9 +68,9 @@ CFLAGS+=-I$(LIBOWFAT_HEADERS) -DGIT_VERSION=$(GIT_VERSION) -Wall -pipe -pthread
68LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread 68LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread
69 69
70BINARY =opentracker 70BINARY =opentracker
71HEADERS=trackerlogic.h scan_urlencoded_query.h ot_hex.h ot_scan.h ot_mutex.h ot_random.h ot_stats.h ot_vector.h ot_clean.h ot_udp.h ot_iovec.h ot_fullscrape.h ot_accesslist.h ot_http.h ot_livesync.h ot_rijndael.h 71HEADERS=trackerlogic.h scan_urlencoded_query.h ot_buf.h ot_hex.h ot_ip.h ot_scan.h ot_mutex.h ot_random.h ot_stats.h ot_vector.h ot_clean.h ot_udp.h ot_iovec.h ot_fullscrape.h ot_accesslist.h ot_http.h ot_livesync.h ot_rijndael.h
72SOURCES=opentracker.c trackerlogic.c scan_urlencoded_query.c ot_hex.c ot_scan.c ot_mutex.c ot_random.c ot_stats.c ot_vector.c ot_clean.c ot_udp.c ot_iovec.c ot_fullscrape.c ot_accesslist.c ot_http.c ot_livesync.c ot_rijndael.c 72SOURCES=opentracker.c trackerlogic.c scan_urlencoded_query.c ot_buf.c ot_hex.c ot_ip.c ot_scan.c ot_mutex.c ot_random.c ot_stats.c ot_vector.c ot_clean.c ot_udp.c ot_iovec.c ot_fullscrape.c ot_accesslist.c ot_http.c ot_livesync.c ot_rijndael.c
73SOURCES_proxy=proxy.c ot_vector.c ot_mutex.c ot_random.c ot_scan.c 73SOURCES_proxy=proxy.c ot_vector.c ot_mutex.c ot_random.c ot_scan.c ot_ip.c
74 74
75OBJECTS = $(SOURCES:%.c=%.o) 75OBJECTS = $(SOURCES:%.c=%.o)
76OBJECTS_debug = $(SOURCES:%.c=%.debug.o) 76OBJECTS_debug = $(SOURCES:%.c=%.debug.o)
@@ -105,8 +105,8 @@ proxy.debug: $(OBJECTS_proxy_debug) $(HEADERS)
105# has to. tests/stubs.c supplies the few symbols those modules reference but 105# has to. tests/stubs.c supplies the few symbols those modules reference but
106# the tests never reach. 106# the tests never reach.
107TESTBIN=tests/runner 107TESTBIN=tests/runner
108TEST_SOURCES=tests/runner.c tests/test.c tests/stubs.c tests/t_scan_urlencoded_query.c tests/t_iovec.c tests/t_vector.c tests/t_random.c tests/t_hex.c tests/t_accesslist.c tests/t_scan.c 108TEST_SOURCES=tests/runner.c tests/test.c tests/stubs.c tests/t_scan_urlencoded_query.c tests/t_iovec.c tests/t_vector.c tests/t_random.c tests/t_hex.c tests/t_accesslist.c tests/t_scan.c tests/t_buf.c tests/t_ip.c
109TEST_UNITS=scan_urlencoded_query.c ot_iovec.c ot_vector.c ot_random.c ot_hex.c ot_scan.c ot_accesslist.c 109TEST_UNITS=scan_urlencoded_query.c ot_buf.c ot_ip.c ot_iovec.c ot_vector.c ot_random.c ot_hex.c ot_scan.c ot_accesslist.c
110TEST_HEADERS=tests/test.h $(HEADERS) 110TEST_HEADERS=tests/test.h $(HEADERS)
111# Tests reach code that sits behind feature flags, so the units under test are 111# Tests reach code that sits behind feature flags, so the units under test are
112# built with those features enabled. A test binary is not a production binary. 112# built with those features enabled. A test binary is not a production binary.
diff --git a/opentracker.c b/opentracker.c
index bcc075c..af336aa 100644
--- a/opentracker.c
+++ b/opentracker.c
@@ -24,17 +24,17 @@
24/* Libowfat */ 24/* Libowfat */
25#include "io.h" 25#include "io.h"
26#include "iob.h" 26#include "iob.h"
27#include "ip6.h"
28#include "socket.h" 27#include "socket.h"
29 28
30/* Opentracker */ 29/* Opentracker */
31#include "ot_accesslist.h" 30#include "ot_accesslist.h"
32#include "ot_hex.h" 31#include "ot_hex.h"
33#include "ot_http.h" 32#include "ot_http.h"
33#include "ot_ip.h"
34#include "ot_livesync.h" 34#include "ot_livesync.h"
35#include "ot_mutex.h" 35#include "ot_mutex.h"
36#include "ot_scan.h"
37#include "ot_random.h" 36#include "ot_random.h"
37#include "ot_scan.h"
38#include "ot_stats.h" 38#include "ot_stats.h"
39#include "ot_udp.h" 39#include "ot_udp.h"
40#include "trackerlogic.h" 40#include "trackerlogic.h"
@@ -156,14 +156,14 @@ static ssize_t header_complete(char *request, ssize_t byte_count) {
156 return 0; 156 return 0;
157} 157}
158 158
159static void handle_dead(const int64 sock) { 159static void handle_dead(const int64_t sock) {
160 struct http_data *cookie = io_getcookie(sock); 160 struct http_data *cookie = io_getcookie(sock);
161 if (cookie) { 161 if (cookie) {
162 size_t i; 162 size_t i;
163 for (i = 0; i < cookie->batches; ++i) 163 for (i = 0; i < cookie->batches; ++i)
164 iob_reset(cookie->batch + i); 164 iob_reset(cookie->batch + i);
165 free(cookie->batch); 165 free(cookie->batch);
166 array_reset(&cookie->request); 166 buf_reset(&cookie->request);
167 if (cookie->flag & (STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER)) 167 if (cookie->flag & (STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER))
168 mutex_workqueue_canceltask(sock); 168 mutex_workqueue_canceltask(sock);
169 free(cookie); 169 free(cookie);
@@ -171,7 +171,7 @@ static void handle_dead(const int64 sock) {
171 io_close(sock); 171 io_close(sock);
172} 172}
173 173
174static void handle_read(const int64 sock, struct ot_workstruct *ws) { 174static void handle_read(const int64_t sock, struct ot_workstruct *ws) {
175 struct http_data *cookie = io_getcookie(sock); 175 struct http_data *cookie = io_getcookie(sock);
176 ssize_t byte_count = io_tryread(sock, ws->inbuf, G_INBUF_SIZE); 176 ssize_t byte_count = io_tryread(sock, ws->inbuf, G_INBUF_SIZE);
177 177
@@ -183,26 +183,30 @@ static void handle_read(const int64 sock, struct ot_workstruct *ws) {
183 if (byte_count == -1) 183 if (byte_count == -1)
184 return; 184 return;
185 185
186 ws->request = NULL;
187 ws->request_size = 0;
188 ws->header_size = 0;
189 ws->keep_alive = 0;
190
186 /* If we get the whole request in one packet, handle it without copying */ 191 /* If we get the whole request in one packet, handle it without copying */
187 if (!array_start(&cookie->request)) { 192 if (!cookie->request.data) {
188 if ((ws->header_size = header_complete(ws->inbuf, byte_count))) { 193 if ((ws->header_size = header_complete(ws->inbuf, byte_count))) {
189 ws->request = ws->inbuf; 194 ws->request = ws->inbuf;
190 ws->request_size = byte_count; 195 ws->request_size = byte_count;
191 http_handle_request(sock, ws); 196 http_handle_request(sock, ws);
192 } else 197 } else if (buf_cat(&cookie->request, ws->inbuf, (size_t)byte_count))
193 array_catb(&cookie->request, ws->inbuf, (size_t)byte_count); 198 handle_dead(sock);
194 return; 199 return;
195 } 200 }
196 201
197 array_catb(&cookie->request, ws->inbuf, byte_count); 202 if (buf_cat(&cookie->request, ws->inbuf, (size_t)byte_count) || cookie->request.size > G_INBUF_SIZE) {
198 if (array_failed(&cookie->request) || array_bytes(&cookie->request) > 8192) {
199 http_issue_error(sock, ws, CODE_HTTPERROR_500); 203 http_issue_error(sock, ws, CODE_HTTPERROR_500);
200 return; 204 return;
201 } 205 }
202 206
203 while ((ws->header_size = header_complete(array_start(&cookie->request), array_bytes(&cookie->request)))) { 207 while ((ws->header_size = header_complete(cookie->request.data, (ssize_t)cookie->request.size))) {
204 ws->request = array_start(&cookie->request); 208 ws->request = cookie->request.data;
205 ws->request_size = array_bytes(&cookie->request); 209 ws->request_size = (ssize_t)cookie->request.size;
206 http_handle_request(sock, ws); 210 http_handle_request(sock, ws);
207#ifdef WANT_KEEPALIVE 211#ifdef WANT_KEEPALIVE
208 if (!ws->keep_alive) 212 if (!ws->keep_alive)
@@ -211,7 +215,7 @@ static void handle_read(const int64 sock, struct ot_workstruct *ws) {
211 } 215 }
212} 216}
213 217
214static void handle_write(const int64 sock) { 218static void handle_write(const int64_t sock) {
215 struct http_data *cookie = io_getcookie(sock); 219 struct http_data *cookie = io_getcookie(sock);
216 size_t i; 220 size_t i;
217 int chunked = 0; 221 int chunked = 0;
@@ -223,7 +227,7 @@ static void handle_write(const int64 sock) {
223 227
224 for (i = 0; i < cookie->batches; ++i) { 228 for (i = 0; i < cookie->batches; ++i) {
225 if (cookie->batch[i].bytesleft) { 229 if (cookie->batch[i].bytesleft) {
226 int64 res = iob_send(sock, cookie->batch + i); 230 int64_t res = iob_send(sock, cookie->batch + i);
227 231
228 if (res == -3) { 232 if (res == -3) {
229 handle_dead(sock); 233 handle_dead(sock);
@@ -246,9 +250,9 @@ static void handle_write(const int64 sock) {
246 handle_dead(sock); 250 handle_dead(sock);
247} 251}
248 252
249static void handle_accept(const int64 serversocket) { 253static void handle_accept(const int64_t serversocket) {
250 struct http_data *cookie; 254 struct http_data *cookie;
251 int64 sock; 255 int64_t sock;
252 ot_ip6 ip; 256 ot_ip6 ip;
253 uint16 port; 257 uint16 port;
254 tai6464 t; 258 tai6464 t;
@@ -301,7 +305,7 @@ static void *server_mainloop(void *args) {
301 random_bytes(ws.rand48_state, sizeof(ws.rand48_state)); 305 random_bytes(ws.rand48_state, sizeof(ws.rand48_state));
302 306
303 for (;;) { 307 for (;;) {
304 int64 sock; 308 int64_t sock;
305 309
306 io_wait(); 310 io_wait();
307 311
@@ -335,14 +339,14 @@ static void *server_mainloop(void *args) {
335} 339}
336 340
337static int64_t ot_try_bind(ot_ip6 ip, uint16_t port, PROTO_FLAG proto) { 341static int64_t ot_try_bind(ot_ip6 ip, uint16_t port, PROTO_FLAG proto) {
338 int64 sock = proto == FLAG_TCP ? socket_tcp6() : socket_udp6(); 342 int64_t sock = proto == FLAG_TCP ? socket_tcp6() : socket_udp6();
339 343
340#ifdef _DEBUG 344#ifdef _DEBUG
341 { 345 {
342 char *protos[] = {"TCP", "UDP", "UDP mcast"}; 346 char *protos[] = {"TCP", "UDP", "UDP mcast"};
343 char _debug[512]; 347 char _debug[512];
344 int off = snprintf(_debug, sizeof(_debug), "Binding socket type %s to address [", protos[proto]); 348 int off = snprintf(_debug, sizeof(_debug), "Binding socket type %s to address [", protos[proto]);
345 off += fmt_ip6c(_debug + off, ip); 349 off += ip_format(_debug + off, ip);
346 snprintf(_debug + off, sizeof(_debug) - off, "]:%d...", port); 350 snprintf(_debug + off, sizeof(_debug) - off, "]:%d...", port);
347 fputs(_debug, stderr); 351 fputs(_debug, stderr);
348 } 352 }
@@ -390,14 +394,14 @@ static int scan_ip6_port(const char *src, ot_ip6 ip, uint16_t *port) {
390 ++s; 394 ++s;
391 if (*s == '[') 395 if (*s == '[')
392 ++s, ++bracket; /* for v6 style notation */ 396 ++s, ++bracket; /* for v6 style notation */
393 if (!(off = scan_ip6(s, ip))) 397 if (!(off = ip_scan(s, ip)))
394 return 0; 398 return 0;
395 s += off; 399 s += off;
396 if (bracket && *s == ']') 400 if (bracket && *s == ']')
397 ++s; 401 ++s;
398 if (*s == 0 || isspace(*s)) 402 if (*s == 0 || isspace(*s))
399 return s - src; 403 return s - src;
400 if (!ip6_isv4mapped(ip)) { 404 if (!ip_is_v4mapped(ip)) {
401 if (*s != ':' && *s != '.') 405 if (*s != ':' && *s != '.')
402 return 0; 406 return 0;
403 if (!bracket && *(s) == ':') 407 if (!bracket && *(s) == ':')
@@ -419,7 +423,7 @@ static int scan_ip6_net(const char *src, ot_net *net) {
419 int off; 423 int off;
420 while (isspace(*s)) 424 while (isspace(*s))
421 ++s; 425 ++s;
422 if (!(off = scan_ip6(s, net->address))) 426 if (!(off = ip_scan(s, net->address)))
423 return 0; 427 return 0;
424 s += off; 428 s += off;
425 if (*s != '/') 429 if (*s != '/')
@@ -429,7 +433,7 @@ static int scan_ip6_net(const char *src, ot_net *net) {
429 if (!(off = scan_udec(s, &value, 128))) 433 if (!(off = scan_udec(s, &value, 128)))
430 return 0; 434 return 0;
431 net->bits = (int)value; 435 net->bits = (int)value;
432 if (ip6_isv4mapped(net->address)) 436 if (ip_is_v4mapped(net->address))
433 net->bits += 96; 437 net->bits += 96;
434 if (net->bits > 128) 438 if (net->bits > 128)
435 return 0; 439 return 0;
@@ -757,7 +761,7 @@ int main(int argc, char **argv) {
757 scanon = 0; 761 scanon = 0;
758 break; 762 break;
759 case 'i': 763 case 'i':
760 if (!scan_ip6(optarg, serverip)) { 764 if (!ip_scan(optarg, serverip)) {
761 usage(argv[0]); 765 usage(argv[0]);
762 exit(1); 766 exit(1);
763 } 767 }
diff --git a/ot_accesslist.c b/ot_accesslist.c
index a199fa4..2465ada 100644
--- a/ot_accesslist.c
+++ b/ot_accesslist.c
@@ -22,11 +22,11 @@
22#endif 22#endif
23 23
24/* Libowfat */ 24/* Libowfat */
25#include "ip6.h"
26 25
27/* Opentracker */ 26/* Opentracker */
28#include "ot_accesslist.h" 27#include "ot_accesslist.h"
29#include "ot_hex.h" 28#include "ot_hex.h"
29#include "ot_ip.h"
30#include "ot_vector.h" 30#include "ot_vector.h"
31#include "trackerlogic.h" 31#include "trackerlogic.h"
32 32
@@ -81,7 +81,7 @@ static ot_accesslist *accesslist_make(ot_accesslist *next, size_t size) {
81 if (accesslist_new) { 81 if (accesslist_new) {
82 accesslist_new->list = size ? malloc(sizeof(ot_hash) * size) : NULL; 82 accesslist_new->list = size ? malloc(sizeof(ot_hash) * size) : NULL;
83 accesslist_new->size = size; 83 accesslist_new->size = size;
84 accesslist_new->base = g_now_minutes; 84 accesslist_new->base = 0;
85 accesslist_new->next = next; 85 accesslist_new->next = next;
86 if (size && !accesslist_new->list) { 86 if (size && !accesslist_new->list) {
87 free(accesslist_new); 87 free(accesslist_new);
@@ -192,16 +192,24 @@ static void accesslist_readfile(void) {
192 192
193 /* Now exchange the accesslist vector in the least race condition prone way */ 193 /* Now exchange the accesslist vector in the least race condition prone way */
194 pthread_mutex_lock(&g_accesslist_mutex); 194 pthread_mutex_lock(&g_accesslist_mutex);
195
196 /* Mark old accesslist as "needs to live another five minutes" */
197 if (g_accesslist)
198 g_accesslist->base = g_now_minutes;
195 accesslist_new->next = g_accesslist; 199 accesslist_new->next = g_accesslist;
196 g_accesslist = accesslist_new; /* Only now set a new list */ 200 g_accesslist = accesslist_new; /* Only now set a new list */
197 201
198#ifdef WANT_DYNAMIC_ACCESSLIST 202#ifdef WANT_DYNAMIC_ACCESSLIST
199 /* If we have dynamic accesslists, reloading a new one will always void the add/delete lists. 203 /* If we have dynamic accesslists, reloading a new one will always void the add/delete lists.
200 Insert empty ones at the list head */ 204 Insert empty ones at the list head */
201 if (g_accesslist_add && (accesslist_new = accesslist_make(g_accesslist_add, 0)) != NULL) 205 if (g_accesslist_add && (accesslist_new = accesslist_make(g_accesslist_add, 0)) != NULL) {
206 g_accesslist_add->base = g_now_minutes;
202 g_accesslist_add = accesslist_new; 207 g_accesslist_add = accesslist_new;
203 if (g_accesslist_delete && (accesslist_new = accesslist_make(g_accesslist_delete, 0)) != NULL) 208 }
209 if (g_accesslist_delete && (accesslist_new = accesslist_make(g_accesslist_delete, 0)) != NULL) {
210 g_accesslist_delete->base = g_now_minutes;
204 g_accesslist_delete = accesslist_new; 211 g_accesslist_delete = accesslist_new;
212 }
205#endif 213#endif
206 214
207 accesslist_clean(g_accesslist); 215 accesslist_clean(g_accesslist);
@@ -209,7 +217,7 @@ static void accesslist_readfile(void) {
209 pthread_mutex_unlock(&g_accesslist_mutex); 217 pthread_mutex_unlock(&g_accesslist_mutex);
210} 218}
211 219
212int accesslist_hashisvalid(ot_hash hash) { 220int accesslist_hashisvalid(const ot_hash hash) {
213 /* Get working copy of current access list */ 221 /* Get working copy of current access list */
214 ot_accesslist *accesslist = g_accesslist; 222 ot_accesslist *accesslist = g_accesslist;
215#ifdef WANT_DYNAMIC_ACCESSLIST 223#ifdef WANT_DYNAMIC_ACCESSLIST
@@ -315,7 +323,8 @@ static void *accesslist_adddel_worker(char *fifoname, ot_accesslist *_Atomic
315 ot_accesslist *accesslist_new = accesslist_make(*removing_from, (*removing_from)->size - 1); 323 ot_accesslist *accesslist_new = accesslist_make(*removing_from, (*removing_from)->size - 1);
316 if (accesslist_new) { 324 if (accesslist_new) {
317 memcpy(accesslist_new->list, (*removing_from)->list, sizeof(ot_hash) * off); 325 memcpy(accesslist_new->list, (*removing_from)->list, sizeof(ot_hash) * off);
318 memcpy(accesslist_new->list + off, (*removing_from)->list + off + 1, (*removing_from)->size - off - 1); 326 memcpy(accesslist_new->list + off, (*removing_from)->list + off + 1, sizeof(ot_hash) * ((*removing_from)->size - off - 1));
327 (*removing_from)->base = g_now_minutes;
319 *removing_from = accesslist_new; 328 *removing_from = accesslist_new;
320 } 329 }
321 } 330 }
@@ -330,7 +339,7 @@ static void *accesslist_adddel_worker(char *fifoname, ot_accesslist *_Atomic
330 } 339 }
331 } else { 340 } else {
332 int exactmatch = 0; 341 int exactmatch = 0;
333 ot_hash *insert_point = binary_search(info_hash, (*adding_to)->list, (*adding_to)->size, OT_HASH_COMPARE_SIZE, sizeof(ot_hash), &exactmatch); 342 ot_hash *insert_point = binary_search(info_hash, (*adding_to)->list, (*adding_to)->size, sizeof(ot_hash), OT_HASH_COMPARE_SIZE, &exactmatch);
334 343
335 /* Only if the info hash is not in the adding_to list, create a new head with that entry */ 344 /* Only if the info hash is not in the adding_to list, create a new head with that entry */
336 if (!exactmatch) { 345 if (!exactmatch) {
@@ -339,7 +348,8 @@ static void *accesslist_adddel_worker(char *fifoname, ot_accesslist *_Atomic
339 if (accesslist_new) { 348 if (accesslist_new) {
340 memcpy(accesslist_new->list, (*adding_to)->list, sizeof(ot_hash) * off); 349 memcpy(accesslist_new->list, (*adding_to)->list, sizeof(ot_hash) * off);
341 memcpy(accesslist_new->list + off, info_hash, sizeof(info_hash)); 350 memcpy(accesslist_new->list + off, info_hash, sizeof(info_hash));
342 memcpy(accesslist_new->list + off + 1, (*adding_to)->list + off, (*adding_to)->size - off); 351 memcpy(accesslist_new->list + off + 1, (*adding_to)->list + off, sizeof(ot_hash) * ((*adding_to)->size - off));
352 (*adding_to)->base = g_now_minutes;
343 *adding_to = accesslist_new; 353 *adding_to = accesslist_new;
344 } 354 }
345 } 355 }
@@ -546,10 +556,10 @@ int accesslist_bless_net(ot_net *net, ot_permissions permissions) {
546 { 556 {
547 char _debug[512]; 557 char _debug[512];
548 int off = snprintf(_debug, sizeof(_debug), "Blessing ip net "); 558 int off = snprintf(_debug, sizeof(_debug), "Blessing ip net ");
549 off += fmt_ip6c(_debug + off, net->address); 559 off += ip_format(_debug + off, net->address);
550 if (net->bits < 128) { 560 if (net->bits < 128) {
551 _debug[off++] = '/'; 561 _debug[off++] = '/';
552 if (ip6_isv4mapped(net->address)) 562 if (ip_is_v4mapped(net->address))
553 off += snprintf(_debug + off, sizeof(_debug) - off, "%d", net->bits - 96); 563 off += snprintf(_debug + off, sizeof(_debug) - off, "%d", net->bits - 96);
554 else 564 else
555 off += snprintf(_debug + off, sizeof(_debug) - off, "%d", net->bits); 565 off += snprintf(_debug + off, sizeof(_debug) - off, "%d", net->bits);
diff --git a/ot_accesslist.h b/ot_accesslist.h
index b3e979d..30aaebd 100644
--- a/ot_accesslist.h
+++ b/ot_accesslist.h
@@ -16,7 +16,7 @@
16#define WANT_ACCESSLIST 16#define WANT_ACCESSLIST
17void accesslist_init(void); 17void accesslist_init(void);
18void accesslist_deinit(void); 18void accesslist_deinit(void);
19int accesslist_hashisvalid(ot_hash hash); 19int accesslist_hashisvalid(const ot_hash hash);
20void accesslist_cleanup(void); 20void accesslist_cleanup(void);
21 21
22/* Scan buf for up to capacity info_hashes in hexadecimal notation, one per 22/* Scan buf for up to capacity info_hashes in hexadecimal notation, one per
diff --git a/ot_buf.c b/ot_buf.c
new file mode 100644
index 0000000..eb00edf
--- /dev/null
+++ b/ot_buf.c
@@ -0,0 +1,41 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <stdlib.h>
8#include <string.h>
9
10/* Opentracker */
11#include "ot_buf.h"
12
13int buf_cat(ot_buf *buf, const void *src, size_t len) {
14 char *data;
15
16 if (!len)
17 return 0;
18
19 if (buf->size + len < buf->size)
20 return -1;
21
22 if (!(data = realloc(buf->data, buf->size + len)))
23 return -1;
24
25 buf->data = data;
26 memcpy(data + buf->size, src, len);
27 buf->size += len;
28
29 return 0;
30}
31
32void buf_truncate(ot_buf *buf, size_t len) {
33 if (len < buf->size)
34 buf->size = len;
35}
36
37void buf_reset(ot_buf *buf) {
38 free(buf->data);
39 buf->data = NULL;
40 buf->size = 0;
41}
diff --git a/ot_buf.h b/ot_buf.h
new file mode 100644
index 0000000..219a069
--- /dev/null
+++ b/ot_buf.h
@@ -0,0 +1,27 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6#ifndef OT_BUF_H__
7#define OT_BUF_H__
8
9#include <stddef.h>
10
11/* A growable byte buffer, holding an HTTP request that did not arrive in a
12 single packet. Requests that arrive whole are served straight from the
13 worker's input buffer and never come near this. */
14typedef struct {
15 char *data;
16 size_t size;
17} ot_buf;
18
19/* Append len bytes from src. Returns 0, or -1 if the memory could not be
20 had, in which case the buffer keeps whatever it already held. */
21int buf_cat(ot_buf *buf, const void *src, size_t len);
22
23void buf_truncate(ot_buf *buf, size_t len);
24
25void buf_reset(ot_buf *buf);
26
27#endif
diff --git a/ot_fullscrape.c b/ot_fullscrape.c
index 3d0e2f1..9dcbe68 100644
--- a/ot_fullscrape.c
+++ b/ot_fullscrape.c
@@ -82,7 +82,7 @@ void fullscrape_deinit(void) {
82 pthread_cancel( thread_id ); 82 pthread_cancel( thread_id );
83} 83}
84 84
85void fullscrape_deliver( int64 sock, ot_tasktype tasktype ) { 85void fullscrape_deliver( int64_t sock, ot_tasktype tasktype ) {
86 mutex_workqueue_pushtask( sock, tasktype ); 86 mutex_workqueue_pushtask( sock, tasktype );
87} 87}
88 88
diff --git a/ot_fullscrape.h b/ot_fullscrape.h
index 843e039..0e7690c 100644
--- a/ot_fullscrape.h
+++ b/ot_fullscrape.h
@@ -12,7 +12,7 @@
12 12
13void fullscrape_init(void); 13void fullscrape_init(void);
14void fullscrape_deinit(void); 14void fullscrape_deinit(void);
15void fullscrape_deliver(int64 sock, ot_tasktype tasktype); 15void fullscrape_deliver(int64_t sock, ot_tasktype tasktype);
16 16
17#else 17#else
18 18
diff --git a/ot_hex.c b/ot_hex.c
index 17dd1ef..9581951 100644
--- a/ot_hex.c
+++ b/ot_hex.c
@@ -10,16 +10,18 @@
10/* Opentracker */ 10/* Opentracker */
11#include "ot_hex.h" 11#include "ot_hex.h"
12 12
13static int fromhex(unsigned char c) { 13int hex_value(int c) {
14 if (c >= '0' && c <= '9') 14 unsigned char u = (unsigned char)c;
15 return c - '0'; 15
16 c |= 0x20; /* fold upper case onto lower, digits are already handled */ 16 if (u >= '0' && u <= '9')
17 if (c >= 'a' && c <= 'f') 17 return u - '0';
18 return c - 'a' + 10; 18 u |= 0x20; /* fold upper case onto lower, digits are already handled */
19 if (u >= 'a' && u <= 'f')
20 return u - 'a' + 10;
19 return -1; 21 return -1;
20} 22}
21 23
22int hex_isdigit(int c) { return fromhex((unsigned char)c) >= 0; } 24int hex_isdigit(int c) { return hex_value(c) >= 0; }
23 25
24char *hex_fromhash(char *dest, const uint8_t *hash) { 26char *hex_fromhash(char *dest, const uint8_t *hash) {
25 static const char digits[] = "0123456789ABCDEF"; 27 static const char digits[] = "0123456789ABCDEF";
@@ -43,9 +45,9 @@ int hex_tohash(ot_hash hash, const char *src) {
43 45
44 /* Tested one character at a time so that a string ending early stops us 46 /* Tested one character at a time so that a string ending early stops us
45 at its terminator rather than one byte past it */ 47 at its terminator rather than one byte past it */
46 if ((high = fromhex((unsigned char)src[2 * i])) < 0) 48 if ((high = hex_value((unsigned char)src[2 * i])) < 0)
47 return 0; 49 return 0;
48 if ((low = fromhex((unsigned char)src[2 * i + 1])) < 0) 50 if ((low = hex_value((unsigned char)src[2 * i + 1])) < 0)
49 return 0; 51 return 0;
50 52
51 decoded[i] = (uint8_t)((high << 4) | low); 53 decoded[i] = (uint8_t)((high << 4) | low);
diff --git a/ot_hex.h b/ot_hex.h
index cb2e86f..3b45b61 100644
--- a/ot_hex.h
+++ b/ot_hex.h
@@ -22,4 +22,8 @@ int hex_tohash(ot_hash hash, const char *src);
22/* Non-zero if c is a hexadecimal digit, in either case. */ 22/* Non-zero if c is a hexadecimal digit, in either case. */
23int hex_isdigit(int c); 23int hex_isdigit(int c);
24 24
25/* The value of a hexadecimal digit, or -1 if c is not one. Shared with the
26 address scanner so that both agree on what a digit is. */
27int hex_value(int c);
28
25#endif 29#endif
diff --git a/ot_http.c b/ot_http.c
index 626547b..46ca5a9 100644
--- a/ot_http.c
+++ b/ot_http.c
@@ -15,15 +15,14 @@
15#include <unistd.h> 15#include <unistd.h>
16 16
17/* Libowfat */ 17/* Libowfat */
18#include "array.h"
19#include "iob.h" 18#include "iob.h"
20#include "ip6.h"
21 19
22/* Opentracker */ 20/* Opentracker */
23#include "ot_accesslist.h" 21#include "ot_accesslist.h"
24#include "ot_fullscrape.h" 22#include "ot_fullscrape.h"
25#include "ot_http.h" 23#include "ot_http.h"
26#include "ot_iovec.h" 24#include "ot_iovec.h"
25#include "ot_ip.h"
27#include "ot_mutex.h" 26#include "ot_mutex.h"
28#include "ot_scan.h" 27#include "ot_scan.h"
29#include "ot_stats.h" 28#include "ot_stats.h"
@@ -45,7 +44,7 @@ ssize_t g_stats_path_len;
45 44
46enum { SUCCESS_HTTP_HEADER_LENGTH = 80, SUCCESS_HTTP_SIZE_OFF = 17 }; 45enum { SUCCESS_HTTP_HEADER_LENGTH = 80, SUCCESS_HTTP_SIZE_OFF = 17 };
47 46
48static void http_senddata(const int64 sock, struct ot_workstruct *ws) { 47static void http_senddata(const int64_t sock, struct ot_workstruct *ws) {
49 struct http_data *cookie = io_getcookie(sock); 48 struct http_data *cookie = io_getcookie(sock);
50 ssize_t written_size; 49 ssize_t written_size;
51 50
@@ -54,20 +53,25 @@ static void http_senddata(const int64 sock, struct ot_workstruct *ws) {
54 return; 53 return;
55 } 54 }
56 55
57 /* whoever sends data is not interested in its input-array */ 56 /* whoever sends data is not interested in its input buffer */
58 if (ws->keep_alive && ws->header_size != ws->request_size) { 57 if (ws->keep_alive && ws->header_size != ws->request_size) {
59 size_t rest = ws->request_size - ws->header_size; 58 size_t rest = ws->request_size - ws->header_size;
60 if (array_start(&cookie->request)) { 59 if (cookie->request.data) {
61 memmove(array_start(&cookie->request), ws->request + ws->header_size, rest); 60 memmove(cookie->request.data, ws->request + ws->header_size, rest);
62 array_truncate(&cookie->request, 1, rest); 61 buf_truncate(&cookie->request, rest);
63 } else 62 } else if (buf_cat(&cookie->request, ws->request + ws->header_size, rest)) {
64 array_catb(&cookie->request, ws->request + ws->header_size, rest); 63 /* Without room for what the client pipelined behind this request, answer
64 this one and let the connection close, rather than keeping it open
65 around a request we have silently dropped */
66 buf_reset(&cookie->request);
67 ws->keep_alive = 0;
68 }
65 } else 69 } else
66 array_reset(&cookie->request); 70 buf_reset(&cookie->request);
67 71
68 written_size = write(sock, ws->reply, ws->reply_size); 72 written_size = write(sock, ws->reply, ws->reply_size);
69 if ((written_size < 0) || ((written_size == ws->reply_size) && !ws->keep_alive)) { 73 if ((written_size < 0) || ((written_size == ws->reply_size) && !ws->keep_alive)) {
70 array_reset(&cookie->request); 74 buf_reset(&cookie->request);
71 free(cookie); 75 free(cookie);
72 io_close(sock); 76 io_close(sock);
73 return; 77 return;
@@ -78,7 +82,7 @@ static void http_senddata(const int64 sock, struct ot_workstruct *ws) {
78 tai6464 t; 82 tai6464 t;
79 83
80 if (!(outbuf = malloc(ws->reply_size - written_size))) { 84 if (!(outbuf = malloc(ws->reply_size - written_size))) {
81 array_reset(&cookie->request); 85 buf_reset(&cookie->request);
82 free(cookie); 86 free(cookie);
83 io_close(sock); 87 io_close(sock);
84 return; 88 return;
@@ -90,7 +94,7 @@ static void http_senddata(const int64 sock, struct ot_workstruct *ws) {
90 if (!cookie->batch || OT_IOB_INIT(cookie->batch) == -1) { 94 if (!cookie->batch || OT_IOB_INIT(cookie->batch) == -1) {
91 free(cookie->batch); 95 free(cookie->batch);
92 free(outbuf); 96 free(outbuf);
93 array_reset(&cookie->request); 97 buf_reset(&cookie->request);
94 free(cookie); 98 free(cookie);
95 io_close(sock); 99 io_close(sock);
96 return; 100 return;
@@ -120,7 +124,7 @@ static void http_senddata(const int64 sock, struct ot_workstruct *ws) {
120#define HTTPERROR_403_IP return http_issue_error(sock, ws, CODE_HTTPERROR_403_IP) 124#define HTTPERROR_403_IP return http_issue_error(sock, ws, CODE_HTTPERROR_403_IP)
121#define HTTPERROR_404 return http_issue_error(sock, ws, CODE_HTTPERROR_404) 125#define HTTPERROR_404 return http_issue_error(sock, ws, CODE_HTTPERROR_404)
122#define HTTPERROR_500 return http_issue_error(sock, ws, CODE_HTTPERROR_500) 126#define HTTPERROR_500 return http_issue_error(sock, ws, CODE_HTTPERROR_500)
123ssize_t http_issue_error(const int64 sock, struct ot_workstruct *ws, int code) { 127ssize_t http_issue_error(const int64_t sock, struct ot_workstruct *ws, int code) {
124 char *error_code[] = {"302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", "402 Payment Required", 128 char *error_code[] = {"302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", "402 Payment Required",
125 "403 Not Modest", "403 Access Denied", "404 Not Found", "500 Internal Server Error"}; 129 "403 Not Modest", "403 Access Denied", "404 Not Found", "500 Internal Server Error"};
126 char *title = error_code[code]; 130 char *title = error_code[code];
@@ -140,7 +144,7 @@ ssize_t http_issue_error(const int64 sock, struct ot_workstruct *ws, int code) {
140 return ws->reply_size = -2; 144 return ws->reply_size = -2;
141} 145}
142 146
143ssize_t http_sendiovecdata(const int64 sock, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial) { 147ssize_t http_sendiovecdata(const int64_t sock, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial) {
144 struct http_data *cookie = io_getcookie(sock); 148 struct http_data *cookie = io_getcookie(sock);
145 io_batch *current; 149 io_batch *current;
146 char *header; 150 char *header;
@@ -154,8 +158,15 @@ ssize_t http_sendiovecdata(const int64 sock, struct ot_workstruct *ws, size_t io
154 HTTPERROR_500; 158 HTTPERROR_500;
155 } 159 }
156 160
161 /* Descriptor numbers get recycled, so a result can only belong to this
162 connection if this connection is in fact waiting for one. */
163 if (!(cookie->flag & (STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER))) {
164 iovec_free(&iovec_entries, &iovector);
165 return 0;
166 }
167
157 /* If this socket collected request in a buffer, free it now */ 168 /* If this socket collected request in a buffer, free it now */
158 array_reset(&cookie->request); 169 buf_reset(&cookie->request);
159 170
160 /* If we came here, wait for the answer is over */ 171 /* If we came here, wait for the answer is over */
161 if (cookie->flag & STRUCT_HTTP_FLAG_WAITINGFORTASK) { 172 if (cookie->flag & STRUCT_HTTP_FLAG_WAITINGFORTASK) {
@@ -232,7 +243,7 @@ ssize_t http_sendiovecdata(const int64 sock, struct ot_workstruct *ws, size_t io
232 return 0; 243 return 0;
233} 244}
234 245
235static ssize_t http_handle_stats(const int64 sock, struct ot_workstruct *ws, char *read_ptr) { 246static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) {
236 static const ot_keywords keywords_main[] = {{"mode", 1}, {"format", 2}, {"info_hash", 3}, {NULL, -3}}; 247 static const ot_keywords keywords_main[] = {{"mode", 1}, {"format", 2}, {"info_hash", 3}, {NULL, -3}};
237 static const ot_keywords keywords_mode[] = {{"peer", TASK_STATS_PEERS}, 248 static const ot_keywords keywords_mode[] = {{"peer", TASK_STATS_PEERS},
238 {"conn", TASK_STATS_CONNS}, 249 {"conn", TASK_STATS_CONNS},
@@ -263,12 +274,16 @@ static ssize_t http_handle_stats(const int64 sock, struct ot_workstruct *ws, cha
263 {"url", TASK_FULLSCRAPE_TPB_URLENCODED}, {"txt", TASK_FULLSCRAPE_TPB_ASCII}, 274 {"url", TASK_FULLSCRAPE_TPB_URLENCODED}, {"txt", TASK_FULLSCRAPE_TPB_ASCII},
264 {"txtp", TASK_FULLSCRAPE_TPB_ASCII_PLUS}, {NULL, -3}}; 275 {"txtp", TASK_FULLSCRAPE_TPB_ASCII_PLUS}, {NULL, -3}};
265 276
266 int mode = TASK_STATS_PEERS, scanon = 1, format = 0;
267
268#ifdef WANT_RESTRICT_STATS
269 struct http_data *cookie = io_getcookie(sock); 277 struct http_data *cookie = io_getcookie(sock);
278 int mode = TASK_STATS_PEERS, scanon = 1, format = 0;
279
280 /* A socket without one is a dead socket, which is what http_sendiovecdata
281 already answers 500 to. */
282 if (!cookie)
283 HTTPERROR_500;
270 284
271 if (!cookie || !accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_STAT)) 285#ifdef WANT_RESTRICT_STATS
286 if (!accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_STAT))
272 HTTPERROR_403_IP; 287 HTTPERROR_403_IP;
273#endif 288#endif
274 289
@@ -302,10 +317,9 @@ static ssize_t http_handle_stats(const int64 sock, struct ot_workstruct *ws, cha
302 } 317 }
303 318
304 if (mode == TASK_STATS_TPB) { 319 if (mode == TASK_STATS_TPB) {
305 struct http_data *cookie = io_getcookie(sock); 320 tai6464 t;
306 tai6464 t;
307#ifdef WANT_COMPRESSION_GZIP 321#ifdef WANT_COMPRESSION_GZIP
308 ws->request[ws->request_size] = 0; 322 ws->request[ws->request_size - 1] = 0;
309#ifndef WANT_COMPRESSION_GZIP_ALWAYS 323#ifndef WANT_COMPRESSION_GZIP_ALWAYS
310 if (strstr(read_ptr - 1, "gzip")) { 324 if (strstr(read_ptr - 1, "gzip")) {
311#endif 325#endif
@@ -330,6 +344,9 @@ static ssize_t http_handle_stats(const int64 sock, struct ot_workstruct *ws, cha
330 /* default format for now */ 344 /* default format for now */
331 if ((mode & TASK_CLASS_MASK) == TASK_STATS) { 345 if ((mode & TASK_CLASS_MASK) == TASK_STATS) {
332 tai6464 t; 346 tai6464 t;
347
348 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK;
349
333 /* Complex stats also include expensive memory debugging tools */ 350 /* Complex stats also include expensive memory debugging tools */
334 taia_uint(&t, 0); 351 taia_uint(&t, 0);
335 io_timeout(sock, t); 352 io_timeout(sock, t);
@@ -351,7 +368,7 @@ typedef struct {
351#endif 368#endif
352 369
353#ifdef WANT_FULLSCRAPE 370#ifdef WANT_FULLSCRAPE
354static ssize_t http_handle_fullscrape(const int64 sock, struct ot_workstruct *ws) { 371static ssize_t http_handle_fullscrape(const int64_t sock, struct ot_workstruct *ws) {
355 struct http_data *cookie = io_getcookie(sock); 372 struct http_data *cookie = io_getcookie(sock);
356 int format = 0; 373 int format = 0;
357 tai6464 t; 374 tai6464 t;
@@ -421,7 +438,7 @@ static ssize_t http_handle_fullscrape(const int64 sock, struct ot_workstruct *ws
421} 438}
422#endif 439#endif
423 440
424static ssize_t http_handle_scrape(const int64 sock, struct ot_workstruct *ws, char *read_ptr) { 441static ssize_t http_handle_scrape(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) {
425 static const ot_keywords keywords_scrape[] = {{"info_hash", 1}, {NULL, -3}}; 442 static const ot_keywords keywords_scrape[] = {{"info_hash", 1}, {NULL, -3}};
426 443
427 ot_hash *multiscrape_buf = (ot_hash *)ws->request; 444 ot_hash *multiscrape_buf = (ot_hash *)ws->request;
@@ -500,7 +517,7 @@ static ot_keywords keywords_announce[] = {{"port", 1}, {"left", 2}, {"event",
500#endif 517#endif
501 {"peer_id", 9}, {NULL, -3}}; 518 {"peer_id", 9}, {NULL, -3}};
502static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}}; 519static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}};
503static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct *ws, char *read_ptr) { 520static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) {
504 size_t numwant; 521 size_t numwant;
505 int tmp, scanon; 522 int tmp, scanon;
506 unsigned short port = 0; 523 unsigned short port = 0;
@@ -521,7 +538,7 @@ static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct *
521 if (accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_PROXY)) { 538 if (accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_PROXY)) {
522 ot_ip6 proxied_ip; 539 ot_ip6 proxied_ip;
523 char *fwd = http_header(ws->request, ws->header_size, "x-forwarded-for"); 540 char *fwd = http_header(ws->request, ws->header_size, "x-forwarded-for");
524 if (fwd && scan_ip6(fwd, proxied_ip)) { 541 if (fwd && ip_scan(fwd, proxied_ip)) {
525 OT_SETIP(ws->peer, proxied_ip); 542 OT_SETIP(ws->peer, proxied_ip);
526 } else 543 } else
527 OT_SETIP(ws->peer, cookie->ip); 544 OT_SETIP(ws->peer, cookie->ip);
@@ -607,7 +624,7 @@ static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct *
607 char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16; 624 char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16;
608 len = scan_urlencoded_query(&read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE); 625 len = scan_urlencoded_query(&read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE);
609 tmp_buf2[len] = 0; 626 tmp_buf2[len] = 0;
610 if ((len <= 0) || !scan_ip6(tmp_buf2, tmp_buf1)) 627 if ((len <= 0) || !ip_scan(tmp_buf2, tmp_buf1))
611 HTTPERROR_400_PARAM; 628 HTTPERROR_400_PARAM;
612 OT_SETIP(&ws->peer, tmp_buf1); 629 OT_SETIP(&ws->peer, tmp_buf1);
613 } break; 630 } break;
@@ -629,7 +646,7 @@ static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct *
629 loglist_reset(); 646 loglist_reset();
630 return ws->reply_size = sprintf(ws->reply, "Successfully removed.\n"); 647 return ws->reply_size = sprintf(ws->reply, "Successfully removed.\n");
631 } 648 }
632 parsed = scan_ip6(tmp_buf, net.address); 649 parsed = ip_scan(tmp_buf, net.address);
633 if (!parsed) 650 if (!parsed)
634 HTTPERROR_400_PARAM; 651 HTTPERROR_400_PARAM;
635 if (tmp_buf[parsed++] != '/') 652 if (tmp_buf[parsed++] != '/')
@@ -639,7 +656,7 @@ static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct *
639 if (!parsed) 656 if (!parsed)
640 HTTPERROR_400_PARAM; 657 HTTPERROR_400_PARAM;
641 bits = (signed short)value; 658 bits = (signed short)value;
642 if (ip6_isv4mapped(net.address)) 659 if (ip_is_v4mapped(net.address))
643 bits += 96; 660 bits += 96;
644 } 661 }
645 net.bits = bits; 662 net.bits = bits;
@@ -678,7 +695,7 @@ static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct *
678 return ws->reply_size; 695 return ws->reply_size;
679} 696}
680 697
681ssize_t http_handle_request(const int64 sock, struct ot_workstruct *ws) { 698ssize_t http_handle_request(const int64_t sock, struct ot_workstruct *ws) {
682 ssize_t reply_off, len; 699 ssize_t reply_off, len;
683 char *read_ptr = ws->request, *write_ptr; 700 char *read_ptr = ws->request, *write_ptr;
684 701
diff --git a/ot_http.h b/ot_http.h
index 8980682..004d710 100644
--- a/ot_http.h
+++ b/ot_http.h
@@ -6,6 +6,8 @@
6#ifndef OT_HTTP_H__ 6#ifndef OT_HTTP_H__
7#define OT_HTTP_H__ 7#define OT_HTTP_H__
8 8
9#include "ot_buf.h"
10
9typedef enum { 11typedef enum {
10 STRUCT_HTTP_FLAG_WAITINGFORTASK = 1, 12 STRUCT_HTTP_FLAG_WAITINGFORTASK = 1,
11 STRUCT_HTTP_FLAG_GZIP = 2, 13 STRUCT_HTTP_FLAG_GZIP = 2,
@@ -16,16 +18,16 @@ typedef enum {
16} STRUCT_HTTP_FLAG; 18} STRUCT_HTTP_FLAG;
17 19
18struct http_data { 20struct http_data {
19 array request; 21 ot_buf request;
20 io_batch *batch; 22 io_batch *batch;
21 size_t batches; 23 size_t batches;
22 ot_ip6 ip; 24 ot_ip6 ip;
23 STRUCT_HTTP_FLAG flag; 25 STRUCT_HTTP_FLAG flag;
24}; 26};
25 27
26ssize_t http_handle_request(const int64 s, struct ot_workstruct *ws); 28ssize_t http_handle_request(const int64_t s, struct ot_workstruct *ws);
27ssize_t http_sendiovecdata(const int64 s, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial); 29ssize_t http_sendiovecdata(const int64_t s, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial);
28ssize_t http_issue_error(const int64 s, struct ot_workstruct *ws, int code); 30ssize_t http_issue_error(const int64_t s, struct ot_workstruct *ws, int code);
29 31
30extern char *g_stats_path; 32extern char *g_stats_path;
31extern ssize_t g_stats_path_len; 33extern ssize_t g_stats_path_len;
diff --git a/ot_ip.c b/ot_ip.c
new file mode 100644
index 0000000..8cde7d2
--- /dev/null
+++ b/ot_ip.c
@@ -0,0 +1,201 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <stdint.h>
8#include <string.h>
9
10/* Opentracker */
11#include "ot_hex.h"
12#include "ot_ip.h"
13
14const char ip_v4mapped_prefix[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff};
15
16/* The dotted quad is only ever reached through the v4 mapped form, so both
17 halves of it stay in here */
18
19static size_t ip4_format(char *dest, const uint8_t *ip) {
20 char *d = dest;
21 int i;
22
23 for (i = 0; i < 4; ++i) {
24 unsigned int octet = ip[i];
25
26 if (i)
27 *d++ = '.';
28 if (octet >= 100)
29 *d++ = (char)('0' + octet / 100);
30 if (octet >= 10)
31 *d++ = (char)('0' + (octet / 10) % 10);
32 *d++ = (char)('0' + octet % 10);
33 }
34
35 return (size_t)(d - dest);
36}
37
38static size_t ip4_scan(const char *src, uint8_t ip[4]) {
39 uint8_t octets[4];
40 const char *s = src;
41 int i;
42
43 for (i = 0; i < 4; ++i) {
44 unsigned int value = 0;
45 int digits = 0;
46
47 if (i && *s++ != '.')
48 return 0;
49
50 /* Leading zeros are tolerated, a value beyond one octet is not */
51 while (*s >= '0' && *s <= '9' && digits < 4) {
52 value = value * 10 + (unsigned int)(*s++ - '0');
53 ++digits;
54 }
55 if (!digits || value > 255)
56 return 0;
57
58 octets[i] = (uint8_t)value;
59 }
60
61 memcpy(ip, octets, sizeof(octets));
62 return (size_t)(s - src);
63}
64
65static unsigned int group_at(const char *ip, int n) { return ((unsigned int)(uint8_t)ip[2 * n] << 8) | (uint8_t)ip[2 * n + 1]; }
66
67size_t ip_format(char *dest, const ot_ip6 ip) {
68 static const char hexdigit[] = "0123456789abcdef";
69 char *d = dest;
70 int best_start = 0, best_len = 0, i = 0;
71
72 if (ip_is_v4mapped(ip))
73 return ip4_format(dest, (const uint8_t *)ip + sizeof(ip_v4mapped_prefix));
74
75 /* RFC 5952 4.2: the longest run of zero groups becomes "::", never a run
76 of one, and the first of two equal runs wins */
77 while (i < 8) {
78 int j = i;
79
80 if (group_at(ip, i)) {
81 ++i;
82 continue;
83 }
84 while (j < 8 && !group_at(ip, j))
85 ++j;
86 if (j - i > best_len) {
87 best_len = j - i;
88 best_start = i;
89 }
90 i = j;
91 }
92 if (best_len < 2)
93 best_len = 0;
94
95 for (i = 0; i < 8; ++i) {
96 unsigned int group;
97 int shift = 12;
98
99 if (best_len && i == best_start) {
100 *d++ = ':';
101 if (i + best_len == 8)
102 *d++ = ':'; /* nothing follows, so close the run */
103 i += best_len - 1;
104 continue;
105 }
106
107 if (i)
108 *d++ = ':';
109
110 group = group_at(ip, i);
111 while (shift && !(group >> shift))
112 shift -= 4; /* RFC 5952 4.1: no leading zeros */
113 for (;; shift -= 4) {
114 *d++ = hexdigit[(group >> shift) & 15];
115 if (!shift)
116 break;
117 }
118 }
119
120 return (size_t)(d - dest);
121}
122
123size_t ip_scan(const char *src, ot_ip6 ip) {
124 uint8_t prefix[16], suffix[16];
125 size_t prefixlen = 0, suffixlen = 0;
126 const char *s = src;
127 int have_gap = 0;
128 int need_colon = 0;
129
130 /* A plain IPv4 address is an address too, stored v4 mapped */
131 {
132 size_t consumed = ip4_scan(src, prefix);
133 if (consumed) {
134 memcpy(ip, ip_v4mapped_prefix, sizeof(ip_v4mapped_prefix));
135 memcpy(ip + sizeof(ip_v4mapped_prefix), prefix, 4);
136 return consumed;
137 }
138 }
139
140 for (;;) {
141 unsigned int value = 0;
142 int digits = 0, nibble;
143 uint8_t *into = have_gap ? suffix : prefix;
144 size_t *fill = have_gap ? &suffixlen : &prefixlen;
145
146 /* RFC 4291 2.2: one run of zero groups may be written "::" */
147 if (s[0] == ':' && s[1] == ':') {
148 if (have_gap)
149 return 0;
150 have_gap = 1;
151 need_colon = 0;
152 s += 2;
153 continue;
154 }
155
156 if (need_colon) {
157 if (*s != ':')
158 break;
159 ++s;
160 }
161
162 /* RFC 4291 2.2 form 3: the final two groups may be a dotted quad */
163 if (prefixlen + suffixlen + 4 <= 16) {
164 size_t consumed = ip4_scan(s, into + *fill);
165 if (consumed) {
166 *fill += 4;
167 s += consumed;
168 break;
169 }
170 }
171
172 while (digits < 4 && (nibble = hex_value(*s)) >= 0) {
173 value = value * 16 + (unsigned int)nibble;
174 ++digits;
175 ++s;
176 }
177 if (!digits) {
178 if (need_colon)
179 --s; /* leave the separator we ate for the caller */
180 break;
181 }
182
183 if (prefixlen + suffixlen + 2 > 16)
184 return 0;
185
186 into[(*fill)++] = (uint8_t)(value >> 8);
187 into[(*fill)++] = (uint8_t)value;
188 need_colon = 1;
189 }
190
191 if (!prefixlen && !suffixlen && !have_gap)
192 return 0;
193 if (!have_gap && prefixlen + suffixlen != 16)
194 return 0; /* without "::" every group has to be spelled out */
195
196 memset(ip, 0, 16);
197 memcpy(ip, prefix, prefixlen);
198 memcpy(ip + 16 - suffixlen, suffix, suffixlen);
199
200 return (size_t)(s - src);
201}
diff --git a/ot_ip.h b/ot_ip.h
new file mode 100644
index 0000000..ac8964d
--- /dev/null
+++ b/ot_ip.h
@@ -0,0 +1,33 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6#ifndef OT_IP_H__
7#define OT_IP_H__
8
9#include <stddef.h>
10#include <string.h>
11
12#include "trackerlogic.h"
13
14/* opentracker holds every address as sixteen bytes, writing IPv4 as the v4
15 mapped form of RFC 4291 section 2.5.5.2. */
16extern const char ip_v4mapped_prefix[12];
17
18static inline int ip_is_v4mapped(const void *ip) { return !memcmp(ip, ip_v4mapped_prefix, sizeof(ip_v4mapped_prefix)); }
19
20/* Read an address and return how many characters it took, or zero if src
21 does not start with one. A plain IPv4 address is accepted and stored in
22 its v4 mapped form.
23
24 The count lets the caller carry on where the address ended, which is how
25 "address:port" and "address/bits" are read. */
26size_t ip_scan(const char *src, ot_ip6 ip);
27
28/* Write an address and return how many characters it took. Nothing is
29 terminated, so a caller may keep appending. A v4 mapped address is written
30 as the plain dotted quad rather than "::ffff:1.2.3.4". */
31size_t ip_format(char *dest, const ot_ip6 ip);
32
33#endif
diff --git a/ot_livesync.c b/ot_livesync.c
index c4dc6e5..8b2dee6 100644
--- a/ot_livesync.c
+++ b/ot_livesync.c
@@ -13,11 +13,11 @@
13#include <unistd.h> 13#include <unistd.h>
14 14
15/* Libowfat */ 15/* Libowfat */
16#include "ip6.h"
17#include "socket.h" 16#include "socket.h"
18 17
19/* Opentracker */ 18/* Opentracker */
20#include "ot_accesslist.h" 19#include "ot_accesslist.h"
20#include "ot_ip.h"
21#include "ot_livesync.h" 21#include "ot_livesync.h"
22#include "ot_mutex.h" 22#include "ot_mutex.h"
23#include "ot_stats.h" 23#include "ot_stats.h"
@@ -40,10 +40,10 @@ enum { OT_SYNC_PEER4, OT_SYNC_PEER6 };
40static void *livesync_worker(void *args); 40static void *livesync_worker(void *args);
41 41
42/* For outgoing packets */ 42/* For outgoing packets */
43static int64 g_socket_in = -1; 43static int64_t g_socket_in = -1;
44 44
45/* For incoming packets */ 45/* For incoming packets */
46static int64 g_socket_out = -1; 46static int64_t g_socket_out = -1;
47 47
48static pthread_mutex_t g_outbuf_mutex = PTHREAD_MUTEX_INITIALIZER; 48static pthread_mutex_t g_outbuf_mutex = PTHREAD_MUTEX_INITIALIZER;
49typedef struct { 49typedef struct {
@@ -92,7 +92,7 @@ void livesync_bind_mcast(ot_ip6 ip, uint16_t port) {
92 char tmpip[4] = {0, 0, 0, 0}; 92 char tmpip[4] = {0, 0, 0, 0};
93 char *v4ip; 93 char *v4ip;
94 94
95 if (!ip6_isv4mapped(ip)) 95 if (!ip_is_v4mapped(ip))
96 exerr("v6 mcast support not yet available."); 96 exerr("v6 mcast support not yet available.");
97 v4ip = ip + 12; 97 v4ip = ip + 12;
98 98
@@ -140,14 +140,14 @@ static void livesync_handle_peersync(struct ot_workstruct *ws, size_t peer_size)
140 140
141 /* Prepare peer record for v4mapped expansion */ 141 /* Prepare peer record for v4mapped expansion */
142 if (peer_size == OT_PEER_SIZE4) 142 if (peer_size == OT_PEER_SIZE4)
143 memcpy(&ws->peer, V4mappedprefix, sizeof(V4mappedprefix)); 143 memcpy(&ws->peer, ip_v4mapped_prefix, sizeof(ip_v4mapped_prefix));
144 144
145 /* Now basic sanity checks have been done on the live sync packet 145 /* Now basic sanity checks have been done on the live sync packet
146 We might add more testing and logging. */ 146 We might add more testing and logging. */
147 while ((ssize_t)(off + sizeof(ot_hash) + peer_size) <= ws->request_size) { 147 while ((ssize_t)(off + sizeof(ot_hash) + peer_size) <= ws->request_size) {
148 char *peer_ip = ws->request + off + sizeof(ot_hash); 148 char *peer_ip = ws->request + off + sizeof(ot_hash);
149 if (peer_size == OT_PEER_SIZE4) 149 if (peer_size == OT_PEER_SIZE4)
150 memcpy(&ws->peer[0] + sizeof(V4mappedprefix), peer_ip, peer_size); 150 memcpy(&ws->peer[0] + sizeof(ip_v4mapped_prefix), peer_ip, peer_size);
151 else 151 else
152 memcpy(&ws->peer, peer_ip, peer_size); 152 memcpy(&ws->peer, peer_ip, peer_size);
153 ws->hash = (ot_hash *)(ws->request + off); 153 ws->hash = (ot_hash *)(ws->request + off);
@@ -215,7 +215,7 @@ static void *livesync_worker(void *args) {
215 ws.inbuf = ws.request = malloc(LIVESYNC_INCOMING_BUFFSIZE); 215 ws.inbuf = ws.request = malloc(LIVESYNC_INCOMING_BUFFSIZE);
216 ws.outbuf = ws.reply = 0; 216 ws.outbuf = ws.reply = 0;
217 217
218 memcpy(in_ip, V4mappedprefix, sizeof(V4mappedprefix)); 218 memcpy(in_ip, ip_v4mapped_prefix, sizeof(ip_v4mapped_prefix));
219 219
220 while (1) { 220 while (1) {
221 uint32_t packet_type; 221 uint32_t packet_type;
diff --git a/ot_livesync.h b/ot_livesync.h
index ed710f0..6eca479 100644
--- a/ot_livesync.h
+++ b/ot_livesync.h
@@ -62,7 +62,7 @@ void livesync_tell(struct ot_workstruct *ws);
62void livesync_ticker(void); 62void livesync_ticker(void);
63 63
64/* Handle an incoming live sync packet */ 64/* Handle an incoming live sync packet */
65void handle_livesync(const int64 sock); 65void handle_livesync(const int64_t sock);
66 66
67#else 67#else
68 68
diff --git a/ot_mutex.c b/ot_mutex.c
index 9aa476d..2a8cbf5 100644
--- a/ot_mutex.c
+++ b/ot_mutex.c
@@ -62,7 +62,7 @@ size_t mutex_get_torrent_count(void) { return g_torrent_count; }
62struct ot_task { 62struct ot_task {
63 ot_taskid taskid; 63 ot_taskid taskid;
64 ot_tasktype tasktype; 64 ot_tasktype tasktype;
65 int64 sock; 65 int64_t sock;
66 size_t iovec_entries; 66 size_t iovec_entries;
67 struct iovec *iovec; 67 struct iovec *iovec;
68 struct ot_task *next; 68 struct ot_task *next;
@@ -73,7 +73,7 @@ static struct ot_task *tasklist;
73static pthread_mutex_t tasklist_mutex; 73static pthread_mutex_t tasklist_mutex;
74static pthread_cond_t tasklist_being_filled; 74static pthread_cond_t tasklist_being_filled;
75 75
76int mutex_workqueue_pushtask(int64 sock, ot_tasktype tasktype) { 76int mutex_workqueue_pushtask(int64_t sock, ot_tasktype tasktype) {
77 struct ot_task **tmptask, *task; 77 struct ot_task **tmptask, *task;
78 78
79 task = malloc(sizeof(struct ot_task)); 79 task = malloc(sizeof(struct ot_task));
@@ -102,7 +102,7 @@ int mutex_workqueue_pushtask(int64 sock, ot_tasktype tasktype) {
102 return 0; 102 return 0;
103} 103}
104 104
105void mutex_workqueue_canceltask(int64 sock) { 105void mutex_workqueue_canceltask(int64_t sock) {
106 struct ot_task **task; 106 struct ot_task **task;
107 107
108 /* Want exclusive access to tasklist */ 108 /* Want exclusive access to tasklist */
@@ -114,9 +114,10 @@ void mutex_workqueue_canceltask(int64 sock) {
114 struct ot_task *ptask = *task; 114 struct ot_task *ptask = *task;
115 size_t i; 115 size_t i;
116 116
117 /* Free task's iovec */ 117 /* Free task's iovec, the buffers it points at and the vector itself */
118 for (i = 0; i < (*task)->iovec_entries; ++i) 118 for (i = 0; i < (*task)->iovec_entries; ++i)
119 free(iovec[i].iov_base); 119 free(iovec[i].iov_base);
120 free(iovec);
120 121
121 *task = (*task)->next; 122 *task = (*task)->next;
122 free(ptask); 123 free(ptask);
@@ -226,9 +227,9 @@ int mutex_workqueue_pushchunked(ot_taskid taskid, struct iovec *iovec) {
226 return task ? 0 : -1; 227 return task ? 0 : -1;
227} 228}
228 229
229int64 mutex_workqueue_popresult(size_t *iovec_entries, struct iovec **iovec, int *is_partial) { 230int64_t mutex_workqueue_popresult(size_t *iovec_entries, struct iovec **iovec, int *is_partial) {
230 struct ot_task **task; 231 struct ot_task **task;
231 int64 sock = -1; 232 int64_t sock = -1;
232 233
233 *is_partial = 0; 234 *is_partial = 0;
234 235
diff --git a/ot_mutex.h b/ot_mutex.h
index ab1cf59..b5af96e 100644
--- a/ot_mutex.h
+++ b/ot_mutex.h
@@ -69,12 +69,12 @@ typedef enum {
69 69
70typedef unsigned long ot_taskid; 70typedef unsigned long ot_taskid;
71 71
72int mutex_workqueue_pushtask(int64 sock, ot_tasktype tasktype); 72int mutex_workqueue_pushtask(int64_t sock, ot_tasktype tasktype);
73void mutex_workqueue_canceltask(int64 sock); 73void mutex_workqueue_canceltask(int64_t sock);
74void mutex_workqueue_pushsuccess(ot_taskid taskid); 74void mutex_workqueue_pushsuccess(ot_taskid taskid);
75ot_taskid mutex_workqueue_poptask(ot_tasktype *tasktype); 75ot_taskid mutex_workqueue_poptask(ot_tasktype *tasktype);
76int mutex_workqueue_pushresult(ot_taskid taskid, size_t iovec_entries, struct iovec *iovector); 76int mutex_workqueue_pushresult(ot_taskid taskid, size_t iovec_entries, struct iovec *iovector);
77int mutex_workqueue_pushchunked(ot_taskid taskid, struct iovec *iovec); 77int mutex_workqueue_pushchunked(ot_taskid taskid, struct iovec *iovec);
78int64 mutex_workqueue_popresult(size_t *iovec_entries, struct iovec **iovector, int *is_partial); 78int64_t mutex_workqueue_popresult(size_t *iovec_entries, struct iovec **iovector, int *is_partial);
79 79
80#endif 80#endif
diff --git a/ot_stats.c b/ot_stats.c
index d43f29c..7945f4e 100644
--- a/ot_stats.c
+++ b/ot_stats.c
@@ -18,13 +18,12 @@
18 18
19/* Libowfat */ 19/* Libowfat */
20#include "io.h" 20#include "io.h"
21#include "ip4.h"
22#include "ip6.h"
23 21
24/* Opentracker */ 22/* Opentracker */
25#include "ot_accesslist.h" 23#include "ot_accesslist.h"
26#include "ot_hex.h" 24#include "ot_hex.h"
27#include "ot_iovec.h" 25#include "ot_iovec.h"
26#include "ot_ip.h"
28#include "ot_mutex.h" 27#include "ot_mutex.h"
29#include "ot_stats.h" 28#include "ot_stats.h"
30#include "trackerlogic.h" 29#include "trackerlogic.h"
@@ -206,7 +205,7 @@ static size_t stats_return_busy_networks(char *reply, stats_network_node *tree,
206 if (scores[i - 1]) { 205 if (scores[i - 1]) {
207 r += sprintf(r, "%08zd: ", scores[i - 1]); 206 r += sprintf(r, "%08zd: ", scores[i - 1]);
208 // #ifdef WANT_V6 207 // #ifdef WANT_V6
209 r += fmt_ip6c(r, networks[i - 1]); 208 r += ip_format(r, networks[i - 1]);
210#if 0 209#if 0
211 // XXX 210 // XXX
212 r += fmt_ip4( r, networks[i - 1]); 211 r += fmt_ip4( r, networks[i - 1]);
@@ -470,7 +469,7 @@ static void stats_return_fulllog(size_t *iovec_entries, struct iovec **iovector,
470 re = r + 32 * OT_STATS_TMPSIZE; 469 re = r + 32 * OT_STATS_TMPSIZE;
471 } 470 }
472 r += sprintf(r, "%08ld: ", loglist->time); 471 r += sprintf(r, "%08ld: ", loglist->time);
473 r += fmt_ip6c(r, loglist->ip); 472 r += ip_format(r, loglist->ip);
474 *r++ = '\n'; 473 *r++ = '\n';
475 memcpy(r, loglist->data, loglist->size); 474 memcpy(r, loglist->data, loglist->size);
476 r += loglist->size; 475 r += loglist->size;
@@ -663,7 +662,7 @@ void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_
663 *peerid_hex = 0; 662 *peerid_hex = 0;
664 } 663 }
665 664
666 ip_readable[fmt_ip6c(ip_readable, (char *)&ws->peer)] = 0; 665 ip_readable[ip_format(ip_readable, (char *)&ws->peer)] = 0;
667#if 0 666#if 0
668 /* XXX */ 667 /* XXX */
669 ip_readable[ fmt_ip4( ip_readable, (char*)&ws->peer ) ] = 0; 668 ip_readable[ fmt_ip4( ip_readable, (char*)&ws->peer ) ] = 0;
@@ -687,7 +686,7 @@ void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_
687 ot_ip6 *ip = (ot_ip6 *)event_data; /* ugly hack to transfer ip to stats */ 686 ot_ip6 *ip = (ot_ip6 *)event_data; /* ugly hack to transfer ip to stats */
688 char _debug[512]; 687 char _debug[512];
689 int off = snprintf(_debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(g_now_seconds - ot_start_time) / 60); 688 int off = snprintf(_debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(g_now_seconds - ot_start_time) / 60);
690 off += fmt_ip6c(_debug + off, *ip); 689 off += ip_format(_debug + off, *ip);
691 off += snprintf(_debug + off, sizeof(_debug) - off, " - FULL SCRAPE\n"); 690 off += snprintf(_debug + off, sizeof(_debug) - off, " - FULL SCRAPE\n");
692 (void)write(2, _debug, off); 691 (void)write(2, _debug, off);
693 ot_full_scrape_request_count++; 692 ot_full_scrape_request_count++;
@@ -696,7 +695,7 @@ void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_
696 ot_ip6 *ip = (ot_ip6 *)event_data; /* ugly hack to transfer ip to stats */ 695 ot_ip6 *ip = (ot_ip6 *)event_data; /* ugly hack to transfer ip to stats */
697 char _debug[512]; 696 char _debug[512];
698 int off = snprintf(_debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(g_now_seconds - ot_start_time) / 60); 697 int off = snprintf(_debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(g_now_seconds - ot_start_time) / 60);
699 off += fmt_ip6c(_debug + off, *ip); 698 off += ip_format(_debug + off, *ip);
700 off += snprintf(_debug + off, sizeof(_debug) - off, " - FULL SCRAPE\n"); 699 off += snprintf(_debug + off, sizeof(_debug) - off, " - FULL SCRAPE\n");
701 (void)write(2, _debug, off); 700 (void)write(2, _debug, off);
702 ot_full_scrape_request_count++; 701 ot_full_scrape_request_count++;
@@ -751,7 +750,7 @@ static void *stats_worker(void *args) {
751 return NULL; 750 return NULL;
752} 751}
753 752
754void stats_deliver(int64 sock, int tasktype) { mutex_workqueue_pushtask(sock, tasktype); } 753void stats_deliver(int64_t sock, int tasktype) { mutex_workqueue_pushtask(sock, tasktype); }
755 754
756static pthread_t thread_id; 755static pthread_t thread_id;
757void stats_init(void) { 756void stats_init(void) {
diff --git a/ot_stats.h b/ot_stats.h
index 8ed2b1e..203831c 100644
--- a/ot_stats.h
+++ b/ot_stats.h
@@ -42,7 +42,7 @@ enum {
42}; 42};
43 43
44void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_data); 44void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_data);
45void stats_deliver(int64 sock, int tasktype); 45void stats_deliver(int64_t sock, int tasktype);
46void stats_cleanup(void); 46void stats_cleanup(void);
47size_t return_stats_for_tracker(char *reply, int mode, int format); 47size_t return_stats_for_tracker(char *reply, int mode, int format);
48size_t stats_return_tracker_version(char *reply); 48size_t stats_return_tracker_version(char *reply);
diff --git a/ot_sync.c b/ot_sync.c
index fa6bb8b..dc71ddd 100644
--- a/ot_sync.c
+++ b/ot_sync.c
@@ -163,7 +163,7 @@ void sync_deinit( ) {
163 pthread_cancel( thread_id ); 163 pthread_cancel( thread_id );
164} 164}
165 165
166void sync_deliver( int64 socket ) { 166void sync_deliver( int64_t socket ) {
167 mutex_workqueue_pushtask( socket, TASK_SYNC_OUT ); 167 mutex_workqueue_pushtask( socket, TASK_SYNC_OUT );
168} 168}
169 169
diff --git a/ot_sync.h b/ot_sync.h
index 0fe0739..b6964fa 100644
--- a/ot_sync.h
+++ b/ot_sync.h
@@ -6,12 +6,14 @@
6#ifndef OT_SYNC_H__ 6#ifndef OT_SYNC_H__
7#define OT_SYNC_H__ 7#define OT_SYNC_H__
8 8
9#include <stdint.h>
10
9#ifdef WANT_SYNC_BATCH 11#ifdef WANT_SYNC_BATCH
10enum { SYNC_IN, SYNC_OUT }; 12enum { SYNC_IN, SYNC_OUT };
11 13
12void sync_init(); 14void sync_init();
13void sync_deinit(); 15void sync_deinit();
14void sync_deliver(int64 socket); 16void sync_deliver(int64_t socket);
15 17
16int add_changeset_to_tracker(uint8_t *data, size_t len); 18int add_changeset_to_tracker(uint8_t *data, size_t len);
17#else 19#else
diff --git a/ot_udp.c b/ot_udp.c
index 82aa8bd..02467e1 100644
--- a/ot_udp.c
+++ b/ot_udp.c
@@ -12,10 +12,10 @@
12 12
13/* Libowfat */ 13/* Libowfat */
14#include "io.h" 14#include "io.h"
15#include "ip6.h"
16#include "socket.h" 15#include "socket.h"
17 16
18/* Opentracker */ 17/* Opentracker */
18#include "ot_ip.h"
19#include "ot_random.h" 19#include "ot_random.h"
20#include "ot_rijndael.h" 20#include "ot_rijndael.h"
21#include "ot_stats.h" 21#include "ot_stats.h"
@@ -71,7 +71,7 @@ static void udp_make_connectionid(uint32_t connid[2], const ot_ip6 remoteip, int
71} 71}
72 72
73/* UDP implementation according to http://xbtt.sourceforge.net/udp_tracker_protocol.html */ 73/* UDP implementation according to http://xbtt.sourceforge.net/udp_tracker_protocol.html */
74int handle_udp6(int64 serversocket, struct ot_workstruct *ws) { 74int handle_udp6(int64_t serversocket, struct ot_workstruct *ws) {
75 ot_ip6 remoteip; 75 ot_ip6 remoteip;
76 uint32_t *inpacket = (uint32_t *)ws->inbuf; 76 uint32_t *inpacket = (uint32_t *)ws->inbuf;
77 uint32_t *outpacket = (uint32_t *)ws->outbuf; 77 uint32_t *outpacket = (uint32_t *)ws->outbuf;
@@ -81,9 +81,10 @@ int handle_udp6(int64 serversocket, struct ot_workstruct *ws) {
81 uint16_t port, remoteport; 81 uint16_t port, remoteport;
82 size_t byte_count, scrape_count; 82 size_t byte_count, scrape_count;
83 83
84 byte_count = socket_recv6(serversocket, ws->inbuf, G_INBUF_SIZE, remoteip, &remoteport, &scopeid); 84 ssize_t result = socket_recv6(serversocket, ws->inbuf, G_INBUF_SIZE, remoteip, &remoteport, &scopeid);
85 if (!byte_count) 85 if (result <= 0)
86 return 0; 86 return 0;
87 byte_count = (size_t)result;
87 88
88 stats_issue_event(EVENT_ACCEPT, FLAG_UDP, (uintptr_t)remoteip); 89 stats_issue_event(EVENT_ACCEPT, FLAG_UDP, (uintptr_t)remoteip);
89 stats_issue_event(EVENT_READ, FLAG_UDP, byte_count); 90 stats_issue_event(EVENT_READ, FLAG_UDP, byte_count);
@@ -176,7 +177,7 @@ int handle_udp6(int64 serversocket, struct ot_workstruct *ws) {
176 } else { 177 } else {
177 /* Limit amount of peers to OT_MAX_PEERS_UDP */ 178 /* Limit amount of peers to OT_MAX_PEERS_UDP */
178 uint32_t numwant = ntohl(inpacket[92 / 4]); 179 uint32_t numwant = ntohl(inpacket[92 / 4]);
179 size_t max_peers = ip6_isv4mapped(remoteip) ? OT_MAX_PEERS_UDP4 : OT_MAX_PEERS_UDP6; 180 size_t max_peers = ip_is_v4mapped(remoteip) ? OT_MAX_PEERS_UDP4 : OT_MAX_PEERS_UDP6;
180 if (numwant > max_peers) 181 if (numwant > max_peers)
181 numwant = max_peers; 182 numwant = max_peers;
182 183
@@ -203,7 +204,7 @@ int handle_udp6(int64 serversocket, struct ot_workstruct *ws) {
203} 204}
204 205
205static void *udp_worker(void *args) { 206static void *udp_worker(void *args) {
206 int64 sock = (int64)args; 207 int64_t sock = (int64_t)args;
207 struct ot_workstruct ws; 208 struct ot_workstruct ws;
208 memset(&ws, 0, sizeof(ws)); 209 memset(&ws, 0, sizeof(ws));
209 210
@@ -229,7 +230,7 @@ static void *udp_worker(void *args) {
229 return NULL; 230 return NULL;
230} 231}
231 232
232void udp_init(int64 sock, unsigned int worker_count) { 233void udp_init(int64_t sock, unsigned int worker_count) {
233 pthread_t thread_id; 234 pthread_t thread_id;
234 if (!g_rijndael_round_key[0]) 235 if (!g_rijndael_round_key[0])
235 udp_generate_rijndael_round_key(); 236 udp_generate_rijndael_round_key();
diff --git a/ot_udp.h b/ot_udp.h
index db48696..c670175 100644
--- a/ot_udp.h
+++ b/ot_udp.h
@@ -6,8 +6,10 @@
6#ifndef OT_UDP_H__ 6#ifndef OT_UDP_H__
7#define OT_UDP_H__ 7#define OT_UDP_H__
8 8
9void udp_init(int64 sock, unsigned int worker_count); 9#include <stdint.h>
10
11void udp_init(int64_t sock, unsigned int worker_count);
10void udp_kickoff(void); 12void udp_kickoff(void);
11int handle_udp6(int64 serversocket, struct ot_workstruct *ws); 13int handle_udp6(int64_t serversocket, struct ot_workstruct *ws);
12 14
13#endif 15#endif
diff --git a/ot_vector.c b/ot_vector.c
index e3c3d3f..c4cd171 100644
--- a/ot_vector.c
+++ b/ot_vector.c
@@ -156,8 +156,6 @@ void vector_remove_torrent(ot_vector *vector, ot_torrent *match) {
156 if (!vector->size) 156 if (!vector->size)
157 return; 157 return;
158 158
159 /* If this is being called after a unsuccessful malloc() for peer_list
160 in add_peer_to_torrent, match->peer_list actually might be NULL */
161 free_peerlist(match->peer_list6); 159 free_peerlist(match->peer_list6);
162 free_peerlist(match->peer_list4); 160 free_peerlist(match->peer_list4);
163 161
diff --git a/proxy.c b/proxy.c
index 19a084a..c2dc417 100644
--- a/proxy.c
+++ b/proxy.c
@@ -19,13 +19,13 @@
19/* Libowfat */ 19/* Libowfat */
20#include "io.h" 20#include "io.h"
21#include "iob.h" 21#include "iob.h"
22#include "ip6.h"
23#include "socket.h" 22#include "socket.h"
24 23
25/* Opentracker */ 24/* Opentracker */
25#include "ot_ip.h"
26#include "ot_mutex.h" 26#include "ot_mutex.h"
27#include "ot_scan.h"
28#include "ot_random.h" 27#include "ot_random.h"
28#include "ot_scan.h"
29#include "ot_stats.h" 29#include "ot_stats.h"
30#include "ot_vector.h" 30#include "ot_vector.h"
31#include "trackerlogic.h" 31#include "trackerlogic.h"
@@ -63,11 +63,11 @@ enum { OT_SYNC_PEER4, OT_SYNC_PEER6 };
63enum { FLAG_SERVERSOCKET = 1 }; 63enum { FLAG_SERVERSOCKET = 1 };
64 64
65/* For incoming packets */ 65/* For incoming packets */
66static int64 g_socket_in = -1; 66static int64_t g_socket_in = -1;
67static uint8_t g_inbuffer[LIVESYNC_INCOMING_BUFFSIZE]; 67static uint8_t g_inbuffer[LIVESYNC_INCOMING_BUFFSIZE];
68 68
69/* For outgoing packets */ 69/* For outgoing packets */
70static int64 g_socket_out = -1; 70static int64_t g_socket_out = -1;
71static uint8_t g_peerbuffer_start[LIVESYNC_OUTGOING_BUFFSIZE_PEERS]; 71static uint8_t g_peerbuffer_start[LIVESYNC_OUTGOING_BUFFSIZE_PEERS];
72static uint8_t *g_peerbuffer_pos; 72static uint8_t *g_peerbuffer_pos;
73static uint8_t *g_peerbuffer_highwater = g_peerbuffer_start + LIVESYNC_OUTGOING_BUFFSIZE_PEERS - LIVESYNC_OUTGOING_WATERMARK_PEERS; 73static uint8_t *g_peerbuffer_highwater = g_peerbuffer_start + LIVESYNC_OUTGOING_BUFFSIZE_PEERS - LIVESYNC_OUTGOING_WATERMARK_PEERS;
@@ -92,7 +92,7 @@ void livesync_bind_mcast(ot_ip6 ip, uint16_t port) {
92 char tmpip[4] = {0, 0, 0, 0}; 92 char tmpip[4] = {0, 0, 0, 0};
93 char *v4ip; 93 char *v4ip;
94 94
95 if (!ip6_isv4mapped(ip)) 95 if (!ip_is_v4mapped(ip))
96 exerr("v6 mcast support not yet available."); 96 exerr("v6 mcast support not yet available.");
97 v4ip = ip + 12; 97 v4ip = ip + 12;
98 98
@@ -249,7 +249,7 @@ typedef struct {
249 uint8_t indata[8192 * 16]; /* Any data not processed yet */ 249 uint8_t indata[8192 * 16]; /* Any data not processed yet */
250 size_t indata_length; /* Length of unprocessed data */ 250 size_t indata_length; /* Length of unprocessed data */
251 uint32_t tracker_id; /* How the other end greeted */ 251 uint32_t tracker_id; /* How the other end greeted */
252 int64 fd; /* A file handle, if connected, <= 0 is disconnected (0 initially, -1 else) */ 252 int64_t fd; /* A file handle, if connected, <= 0 is disconnected (0 initially, -1 else) */
253 io_batch outdata; /* The iobatch containing our sync data */ 253 io_batch outdata; /* The iobatch containing our sync data */
254 254
255 size_t packet_tcount; /* Number of unprocessed torrents in packet we currently receive */ 255 size_t packet_tcount; /* Number of unprocessed torrents in packet we currently receive */
@@ -283,7 +283,7 @@ static void handle_reconnects(void) {
283 size_t i; 283 size_t i;
284 for (i = 0; i < g_connection_count; ++i) 284 for (i = 0; i < g_connection_count; ++i)
285 if (PROXYPEER_NEEDSCONNECT(g_connections[i].state)) { 285 if (PROXYPEER_NEEDSCONNECT(g_connections[i].state)) {
286 int64 newfd = socket_tcp6(); 286 int64_t newfd = socket_tcp6();
287 fprintf(stderr, "(Re)connecting to peer..."); 287 fprintf(stderr, "(Re)connecting to peer...");
288 if (newfd < 0) 288 if (newfd < 0)
289 continue; /* No socket for you */ 289 continue; /* No socket for you */
@@ -308,10 +308,10 @@ static void handle_reconnects(void) {
308} 308}
309 309
310/* Handle incoming connection requests, check against whitelist */ 310/* Handle incoming connection requests, check against whitelist */
311static void handle_accept(int64 serversocket) { 311static void handle_accept(int64_t serversocket) {
312 int64 newfd; 312 int64_t newfd;
313 ot_ip6 ip; 313 ot_ip6 ip;
314 uint16 port; 314 uint16 port;
315 315
316 while ((newfd = socket_accept6(serversocket, ip, &port, NULL)) != -1) { 316 while ((newfd = socket_accept6(serversocket, ip, &port, NULL)) != -1) {
317 317
@@ -351,9 +351,9 @@ static void handle_accept(int64 serversocket) {
351} 351}
352 352
353/* New sync data on the stream */ 353/* New sync data on the stream */
354static void handle_read(int64 peersocket) { 354static void handle_read(int64_t peersocket) {
355 int i; 355 int i;
356 int64 datalen; 356 int64_t datalen;
357 uint32_t tracker_id; 357 uint32_t tracker_id;
358 proxy_peer *peer = io_getcookie(peersocket); 358 proxy_peer *peer = io_getcookie(peersocket);
359 359
@@ -420,7 +420,7 @@ static void handle_read(int64 peersocket) {
420} 420}
421 421
422/* Can write new sync data to the stream */ 422/* Can write new sync data to the stream */
423static void handle_write(int64 peersocket) { 423static void handle_write(int64_t peersocket) {
424 proxy_peer *peer = io_getcookie(peersocket); 424 proxy_peer *peer = io_getcookie(peersocket);
425 425
426 if (!peer) { 426 if (!peer) {
@@ -472,7 +472,7 @@ static void handle_write(int64 peersocket) {
472} 472}
473 473
474static void server_mainloop() { 474static void server_mainloop() {
475 int64 sock; 475 int64_t sock;
476 uint32_t packet_type; 476 uint32_t packet_type;
477 477
478 /* inlined livesync_init() */ 478 /* inlined livesync_init() */
@@ -515,7 +515,7 @@ static void panic(const char *routine) {
515} 515}
516 516
517static int64_t ot_try_bind(ot_ip6 ip, uint16_t port) { 517static int64_t ot_try_bind(ot_ip6 ip, uint16_t port) {
518 int64 sock = socket_tcp6(); 518 int64_t sock = socket_tcp6();
519 519
520 if (socket_bind6_reuse(sock, ip, port, 0) == -1) 520 if (socket_bind6_reuse(sock, ip, port, 0) == -1)
521 panic("socket_bind6_reuse"); 521 panic("socket_bind6_reuse");
@@ -539,14 +539,14 @@ static int scan_ip6_port(const char *src, ot_ip6 ip, uint16_t *port) {
539 ++s; 539 ++s;
540 if (*s == '[') 540 if (*s == '[')
541 ++s, ++bracket; /* for v6 style notation */ 541 ++s, ++bracket; /* for v6 style notation */
542 if (!(off = scan_ip6(s, ip))) 542 if (!(off = ip_scan(s, ip)))
543 return 0; 543 return 0;
544 s += off; 544 s += off;
545 if (*s == 0 || isspace(*s)) 545 if (*s == 0 || isspace(*s))
546 return s - src; 546 return s - src;
547 if (*s == ']' && bracket) 547 if (*s == ']' && bracket)
548 ++s; 548 ++s;
549 if (!ip6_isv4mapped(ip)) { 549 if (!ip_is_v4mapped(ip)) {
550 if ((bracket && *(s) != ':') || (*(s) != '.')) 550 if ((bracket && *(s) != ':') || (*(s) != '.'))
551 return 0; 551 return 0;
552 s++; 552 s++;
diff --git a/tests/runner.c b/tests/runner.c
index 370b92b..10b6625 100644
--- a/tests/runner.c
+++ b/tests/runner.c
@@ -20,6 +20,8 @@ static const struct module modules[] = {{"scan_urlencoded_query", test_scan_urle
20 {"ot_random", test_random}, 20 {"ot_random", test_random},
21 {"ot_hex", test_hex}, 21 {"ot_hex", test_hex},
22 {"ot_scan", test_scan}, 22 {"ot_scan", test_scan},
23 {"ot_buf", test_buf},
24 {"ot_ip", test_ip},
23 {"ot_accesslist", test_accesslist}, 25 {"ot_accesslist", test_accesslist},
24 {NULL, NULL}}; 26 {NULL, NULL}};
25 27
diff --git a/tests/t_buf.c b/tests/t_buf.c
new file mode 100644
index 0000000..dcfb0bb
--- /dev/null
+++ b/tests/t_buf.c
@@ -0,0 +1,205 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <stdint.h>
8#include <string.h>
9
10/* Opentracker */
11#include "ot_buf.h"
12#include "test.h"
13
14static void case_starts_empty(void) {
15 ot_buf buf = {NULL, 0};
16
17 test_case("a fresh buffer holds nothing and has no data pointer");
18 CHECK_PTR(buf.data, NULL);
19 CHECK_INT(buf.size, 0);
20}
21
22static void case_cat_appends(void) {
23 ot_buf buf = {NULL, 0};
24
25 test_case("appends concatenate in order and report success");
26 CHECK_INT(buf_cat(&buf, "GET /ann", 8), 0);
27 CHECK(buf.data != NULL);
28 CHECK_INT(buf.size, 8);
29 CHECK_INT(buf_cat(&buf, "ounce", 5), 0);
30 CHECK_INT(buf.size, 13);
31 CHECK_MEM(buf.data, "GET /announce", 13);
32 buf_reset(&buf);
33}
34
35static void case_cat_of_nothing_allocates_nothing(void) {
36 ot_buf buf = {NULL, 0};
37
38 /* handle_read tells an untouched buffer from a partial request by the data
39 pointer, so a zero length append must not quietly allocate one */
40 test_case("appending zero bytes leaves the buffer untouched");
41 CHECK_INT(buf_cat(&buf, "ignored", 0), 0);
42 CHECK_PTR(buf.data, NULL);
43 CHECK_INT(buf.size, 0);
44 buf_reset(&buf);
45}
46
47static void case_grows_across_many_appends(void) {
48 ot_buf buf = {NULL, 0};
49 char chunk[100];
50 size_t i;
51
52 test_case("content survives growing one packet at a time");
53 memset(chunk, 'x', sizeof(chunk));
54 for (i = 0; i < 100; ++i)
55 if (buf_cat(&buf, chunk, sizeof(chunk))) {
56 test_failf(__FILE__, __LINE__, "append %zu was refused", i);
57 buf_reset(&buf);
58 return;
59 }
60 ++test_checks;
61 CHECK_INT(buf.size, 10000);
62 for (i = 0; i < 10000; ++i)
63 if (buf.data[i] != 'x') {
64 test_failf(__FILE__, __LINE__, "byte %zu was not preserved across growth", i);
65 buf_reset(&buf);
66 return;
67 }
68 ++test_checks;
69 buf_reset(&buf);
70}
71
72static void case_grows_one_byte_at_a_time(void) {
73 ot_buf buf = {NULL, 0};
74 size_t i;
75
76 /* The shape a trickling client produces. Nothing here is optimised for it,
77 so the point is only that it stays correct. */
78 test_case("a byte at a time accumulates correctly");
79 for (i = 0; i < 2048; ++i) {
80 char c = (char)('a' + (i % 26));
81 if (buf_cat(&buf, &c, 1)) {
82 test_failf(__FILE__, __LINE__, "append %zu was refused", i);
83 buf_reset(&buf);
84 return;
85 }
86 }
87 ++test_checks;
88 CHECK_INT(buf.size, 2048);
89 for (i = 0; i < 2048; ++i)
90 if (buf.data[i] != (char)('a' + (i % 26))) {
91 test_failf(__FILE__, __LINE__, "byte %zu differs", i);
92 buf_reset(&buf);
93 return;
94 }
95 ++test_checks;
96 buf_reset(&buf);
97}
98
99static void case_truncate_shrinks_only(void) {
100 ot_buf buf = {NULL, 0};
101
102 test_case("truncate drops the tail but never grows");
103 buf_cat(&buf, "0123456789", 10);
104 buf_truncate(&buf, 4);
105 CHECK_INT(buf.size, 4);
106 CHECK_MEM(buf.data, "0123", 4);
107
108 buf_truncate(&buf, 9);
109 CHECK_INT(buf.size, 4);
110 buf_truncate(&buf, 4);
111 CHECK_INT(buf.size, 4);
112
113 buf_truncate(&buf, 0);
114 CHECK_INT(buf.size, 0);
115 buf_reset(&buf);
116}
117
118static void case_append_after_truncate(void) {
119 ot_buf buf = {NULL, 0};
120
121 test_case("appending after a truncate overwrites the dropped tail");
122 buf_cat(&buf, "keepdrop", 8);
123 buf_truncate(&buf, 4);
124 CHECK_INT(buf_cat(&buf, "MORE", 4), 0);
125 CHECK_INT(buf.size, 8);
126 CHECK_MEM(buf.data, "keepMORE", 8);
127 buf_reset(&buf);
128}
129
130static void case_reset_clears_everything(void) {
131 ot_buf buf = {NULL, 0};
132
133 test_case("reset releases the buffer and restores the initial state");
134 buf_cat(&buf, "something", 9);
135 buf_reset(&buf);
136 CHECK_PTR(buf.data, NULL);
137 CHECK_INT(buf.size, 0);
138}
139
140static void case_reset_of_empty_is_safe(void) {
141 ot_buf buf = {NULL, 0};
142
143 test_case("resetting a buffer that was never used is harmless");
144 buf_reset(&buf);
145 buf_reset(&buf);
146 CHECK_PTR(buf.data, NULL);
147 CHECK_INT(buf.size, 0);
148}
149
150static void case_refused_append_is_harmless(void) {
151 ot_buf buf = {NULL, 0};
152
153 /* A length that would wrap the size is rejected before src is read and
154 before anything is allocated, so a short string is safe to pass here.
155 A refused append must not destroy the partial request already collected:
156 the caller answers and closes rather than losing both. */
157 test_case("a length that would wrap the size is refused");
158 CHECK_INT(buf_cat(&buf, "already here", 12), 0);
159 CHECK_INT(buf_cat(&buf, "x", (size_t)-1), -1);
160
161 test_case("and earlier content survives it");
162 CHECK_INT(buf.size, 12);
163 CHECK_MEM(buf.data, "already here", 12);
164
165 test_case("and the buffer still works afterwards");
166 CHECK_INT(buf_cat(&buf, "!", 1), 0);
167 CHECK_INT(buf.size, 13);
168 CHECK_MEM(buf.data, "already here!", 13);
169 buf_reset(&buf);
170}
171
172static void case_accumulates_a_split_request(void) {
173 /* What handle_read actually does: collect a request that arrived in
174 pieces, then hand over the whole of it */
175 ot_buf buf = {NULL, 0};
176 const char *pieces[] = {"GET /announce?info_hash=", "0123456789abcdefghij", " HTTP/1.0\r\n\r\n"};
177 size_t i;
178
179 test_case("a request split over several reads is reassembled");
180 for (i = 0; i < 3; ++i)
181 if (buf_cat(&buf, pieces[i], strlen(pieces[i]))) {
182 test_failf(__FILE__, __LINE__, "piece %zu was refused", i);
183 buf_reset(&buf);
184 return;
185 }
186 ++test_checks;
187
188 CHECK_INT(buf.size, 24 + 20 + 13);
189 CHECK_MEM(buf.data, "GET /announce?info_hash=0123456789abcdefghij HTTP/1.0\r\n\r\n", 57);
190 buf_reset(&buf);
191}
192
193void test_buf(void) {
194 case_starts_empty();
195 case_cat_appends();
196 case_cat_of_nothing_allocates_nothing();
197 case_grows_across_many_appends();
198 case_grows_one_byte_at_a_time();
199 case_truncate_shrinks_only();
200 case_append_after_truncate();
201 case_reset_clears_everything();
202 case_reset_of_empty_is_safe();
203 case_refused_append_is_harmless();
204 case_accumulates_a_split_request();
205}
diff --git a/tests/t_ip.c b/tests/t_ip.c
new file mode 100644
index 0000000..2fe3a72
--- /dev/null
+++ b/tests/t_ip.c
@@ -0,0 +1,217 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <stdint.h>
8#include <string.h>
9
10/* Opentracker */
11#include "ot_ip.h"
12#include "test.h"
13
14static char text[64];
15
16static const char *formatted(const char *ip) {
17 text[ip_format(text, ip)] = 0;
18 return text;
19}
20
21/* ------------------------------------------------------------------ IPv4 */
22
23/* ------------------------------------------------------- RFC 5952 output */
24
25static void case_format_rfc5952(void) {
26 test_case("4.1 leading zeros in a group are suppressed");
27 CHECK_STR(formatted("\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"), "2001:db8::1");
28
29 test_case("4.2.1 the longest run of zero groups becomes ::");
30 CHECK_STR(formatted("\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01"), "2001:db8::2:1");
31
32 test_case("4.2.2 a single zero group is never shortened");
33 CHECK_STR(formatted("\x20\x01\x0d\xb8\x00\x00\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01"), "2001:db8:0:1:1:1:1:1");
34
35 test_case("4.2.1 a longer run wins over an earlier shorter one");
36 CHECK_STR(formatted("\x20\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01"), "2001:0:0:1::1");
37
38 test_case("4.2.3 of two runs of equal length the first is shortened");
39 CHECK_STR(formatted("\x20\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x01"), "2001::1:0:0:1:1");
40
41 test_case("4.3 hexadecimal digits are lower case");
42 CHECK_STR(formatted("\xab\xcd\xef\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"), "abcd:ef01::1");
43
44 test_case("the unspecified and loopback addresses");
45 CHECK_STR(formatted("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), "::");
46 CHECK_STR(formatted("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"), "::1");
47
48 test_case("a full address with no zero group");
49 CHECK_STR(formatted("\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x66\x66\x77\x77\x88\x88"), "1111:2222:3333:4444:5555:6666:7777:8888");
50}
51
52static void case_format_trailing_zero_group(void) {
53 /* libowfat's formatter looked two bytes past the address to decide this
54 one, so a peer address ending in a zero group came out as "::" whenever
55 the bytes that happened to follow were not themselves zero. In
56 ot_stats.c those bytes are the peer's port. */
57 const char ip[] = "\x20\x01\x0d\xb8\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x00";
58
59 test_case("a single trailing zero group is written out, not shortened");
60 CHECK_STR(formatted(ip), "2001:db8:1:2:3:4:5:0");
61
62 test_case("and nothing beyond the sixteen bytes is consulted");
63 {
64 char padded[20];
65 memcpy(padded, ip, 16);
66 memset(padded + 16, 0xff, 4); /* a port and flags, as a peer record has */
67 CHECK_STR(formatted(padded), "2001:db8:1:2:3:4:5:0");
68 memset(padded + 16, 0x00, 4);
69 CHECK_STR(formatted(padded), "2001:db8:1:2:3:4:5:0");
70 }
71}
72
73static void case_v4mapped(void) {
74 const char mapped[] = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x0a\x00\x00\x01";
75 const char unmapped[] = "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01";
76
77 test_case("a v4 mapped address is recognised");
78 CHECK(ip_is_v4mapped(mapped));
79 CHECK_INT(ip_is_v4mapped(unmapped), 0);
80
81 /* The tracker has only ever shown the plain quad, so that is the one
82 form the formatter produces; "::ffff:10.0.0.1" is not written anywhere */
83 test_case("a mapped address is written as the plain dotted quad");
84 CHECK_STR(formatted(mapped), "10.0.0.1");
85 CHECK_STR(formatted("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00"), "0.0.0.0");
86 CHECK_STR(formatted("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff"), "255.255.255.255");
87
88 test_case("anything else follows RFC 5952");
89 CHECK_STR(formatted(unmapped), "2001:db8::1");
90}
91
92/* -------------------------------------------------------- RFC 4291 input */
93
94static void case_scan_forms(void) {
95 char ip[16];
96
97 test_case("2.2 form 1, every group spelled out");
98 CHECK_INT(ip_scan("1111:2222:3333:4444:5555:6666:7777:8888", ip), 39);
99 CHECK_MEM(ip, "\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x66\x66\x77\x77\x88\x88", 16);
100
101 test_case("2.2 form 2, zero groups abbreviated");
102 CHECK_INT(ip_scan("2001:db8::1", ip), 11);
103 CHECK_MEM(ip, "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16);
104 CHECK_INT(ip_scan("::1", ip), 3);
105 CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16);
106 CHECK_INT(ip_scan("::", ip), 2);
107 CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16);
108 CHECK_INT(ip_scan("1::", ip), 3);
109 CHECK_MEM(ip, "\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16);
110
111 test_case("2.2 form 3, a dotted quad in the last two groups");
112 CHECK_INT(ip_scan("::ffff:1.2.3.4", ip), 14);
113 CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x01\x02\x03\x04", 16);
114 CHECK_INT(ip_scan("1:2:3:4:5:6:1.2.3.4", ip), 19);
115 CHECK_MEM(ip, "\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x01\x02\x03\x04", 16);
116
117 test_case("upper case input is accepted");
118 CHECK_INT(ip_scan("2001:DB8::1", ip), 11);
119 CHECK_MEM(ip, "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16);
120}
121
122static void case_scan_accepts_bare_ipv4(void) {
123 char ip[16];
124
125 /* Every address opentracker holds is sixteen bytes, so an operator writing
126 a plain IPv4 address in the config has to land as the v4 mapped form */
127 test_case("a bare IPv4 address is stored v4 mapped");
128 CHECK_INT(ip_scan("10.0.0.1", ip), 8);
129 CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x0a\x00\x00\x01", 16);
130 CHECK(ip_is_v4mapped(ip));
131}
132
133static void case_scan_reports_length(void) {
134 char ip[16];
135
136 /* The config parser continues from where the address ended, which is how
137 "address:port" and "address/bits" are read */
138 test_case("scanning stops at the character after the address");
139 CHECK_INT(ip_scan("10.0.0.1:6969", ip), 8);
140 CHECK_INT(ip_scan("2001:db8::1/64", ip), 11);
141 CHECK_INT(ip_scan("::1]", ip), 3);
142 CHECK_INT(ip_scan("1:2:3:4:5:6:7:8 trailing", ip), 15);
143}
144
145static void case_scan_rejects(void) {
146 char ip[16];
147
148 test_case("an empty or junk string is refused");
149 CHECK_INT(ip_scan("", ip), 0);
150 CHECK_INT(ip_scan("x", ip), 0);
151 CHECK_INT(ip_scan(":", ip), 0);
152 CHECK_INT(ip_scan(":1:2", ip), 0);
153
154 test_case("too few groups without :: are refused");
155 CHECK_INT(ip_scan("1:2:3:4:5:6:7", ip), 0);
156 CHECK_INT(ip_scan("2001:db8:1", ip), 0);
157
158 /* libowfat accepted a prefix of each of these and carried on. Taking only
159 part of a malformed address is worse than refusing it, especially where
160 the text comes from a config file or an X-Forwarded-For header. */
161 test_case("a second :: is refused rather than truncated");
162 CHECK_INT(ip_scan("1::2::3", ip), 0);
163 CHECK_INT(ip_scan("::1::", ip), 0);
164 CHECK_INT(ip_scan("::::", ip), 0);
165
166 test_case("more than eight groups is refused");
167 CHECK_INT(ip_scan("1:2:3:4:5:6:7:8:9", ip), 0);
168
169 test_case("a group of more than four digits is refused, not truncated");
170 CHECK_INT(ip_scan("12345::1", ip), 0);
171 CHECK_INT(ip_scan("fffff::1", ip), 0);
172}
173
174static void case_round_trip(void) {
175 /* Whatever the formatter writes, the scanner has to read back */
176 static const char *const samples[] = {"::",
177 "::1",
178 "1::",
179 "2001:db8::1",
180 "2001:db8:1:2:3:4:5:0",
181 "1111:2222:3333:4444:5555:6666:7777:8888",
182 "2001::1:0:0:1",
183 "abcd:ef01::1",
184 NULL};
185 char ip[16], again[16];
186 size_t i;
187
188 test_case("every formatted address scans back to the same bytes");
189 for (i = 0; samples[i]; ++i) {
190 size_t n = ip_scan(samples[i], ip);
191 if (n != strlen(samples[i])) {
192 test_failf(__FILE__, __LINE__, "\"%s\" consumed %zu of %zu", samples[i], n, strlen(samples[i]));
193 return;
194 }
195 text[ip_format(text, ip)] = 0;
196 if (strcmp(text, samples[i])) {
197 test_failf(__FILE__, __LINE__, "\"%s\" formatted back as \"%s\"", samples[i], text);
198 return;
199 }
200 if (!ip_scan(text, again) || memcmp(ip, again, 16)) {
201 test_failf(__FILE__, __LINE__, "\"%s\" did not survive a second pass", samples[i]);
202 return;
203 }
204 }
205 ++test_checks;
206}
207
208void test_ip(void) {
209 case_format_rfc5952();
210 case_format_trailing_zero_group();
211 case_v4mapped();
212 case_scan_forms();
213 case_scan_accepts_bare_ipv4();
214 case_scan_reports_length();
215 case_scan_rejects();
216 case_round_trip();
217}
diff --git a/tests/test.h b/tests/test.h
index f0f2360..7449b73 100644
--- a/tests/test.h
+++ b/tests/test.h
@@ -83,5 +83,7 @@ void test_random(void);
83void test_hex(void); 83void test_hex(void);
84void test_accesslist(void); 84void test_accesslist(void);
85void test_scan(void); 85void test_scan(void);
86void test_buf(void);
87void test_ip(void);
86 88
87#endif 89#endif
diff --git a/trackerlogic.c b/trackerlogic.c
index d8a2f9b..db1d787 100644
--- a/trackerlogic.c
+++ b/trackerlogic.c
@@ -13,26 +13,26 @@
13/* Libowfat */ 13/* Libowfat */
14#include "io.h" 14#include "io.h"
15#include "iob.h" 15#include "iob.h"
16#include "ip6.h"
17 16
18/* Opentracker */ 17/* Opentracker */
19#include "ot_accesslist.h" 18#include "ot_accesslist.h"
20#include "ot_clean.h" 19#include "ot_clean.h"
21#include "ot_fullscrape.h" 20#include "ot_fullscrape.h"
22#include "ot_http.h" 21#include "ot_http.h"
22#include "ot_ip.h"
23#include "ot_livesync.h" 23#include "ot_livesync.h"
24#include "ot_mutex.h" 24#include "ot_mutex.h"
25#include "ot_random.h" 25#include "ot_random.h"
26#include "ot_stats.h" 26#include "ot_stats.h"
27#include "ot_vector.h"
28#include "ot_udp.h" 27#include "ot_udp.h"
28#include "ot_vector.h"
29#include "trackerlogic.h" 29#include "trackerlogic.h"
30 30
31/* Forward declaration */ 31/* Forward declaration */
32size_t return_peers_for_torrent(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto); 32size_t return_peers_for_torrent(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto);
33 33
34void free_peerlist(ot_peerlist *peer_list) { 34void free_peerlist(ot_peerlist *peer_list) {
35 if (peer_list->peers.data) { 35 if (peer_list && peer_list->peers.data) {
36 if (OT_PEERLIST_HASBUCKETS(peer_list)) 36 if (OT_PEERLIST_HASBUCKETS(peer_list))
37 vector_clean_list((ot_vector *)peer_list->peers.data, peer_list->peers.size); 37 vector_clean_list((ot_vector *)peer_list->peers.data, peer_list->peers.size);
38 else 38 else
@@ -511,7 +511,7 @@ void iterate_all_torrents(int (*for_each)(ot_torrent *torrent, uintptr_t data),
511 511
512ot_peer *peer_from_peer6(ot_peer6 *peer, size_t *peer_size) { 512ot_peer *peer_from_peer6(ot_peer6 *peer, size_t *peer_size) {
513 ot_ip6 *ip = (ot_ip6 *)peer; 513 ot_ip6 *ip = (ot_ip6 *)peer;
514 if (!ip6_isv4mapped(ip)) { 514 if (!ip_is_v4mapped(ip)) {
515 *peer_size = OT_PEER_SIZE6; 515 *peer_size = OT_PEER_SIZE6;
516 return (ot_peer *)peer; 516 return (ot_peer *)peer;
517 } 517 }
@@ -521,7 +521,7 @@ ot_peer *peer_from_peer6(ot_peer6 *peer, size_t *peer_size) {
521 521
522size_t peer_size_from_peer6(ot_peer6 *peer) { 522size_t peer_size_from_peer6(ot_peer6 *peer) {
523 ot_ip6 *ip = (ot_ip6 *)peer; 523 ot_ip6 *ip = (ot_ip6 *)peer;
524 if (!ip6_isv4mapped(ip)) 524 if (!ip_is_v4mapped(ip))
525 return OT_PEER_SIZE6; 525 return OT_PEER_SIZE6;
526 return OT_PEER_SIZE4; 526 return OT_PEER_SIZE4;
527} 527}