summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile10
-rw-r--r--opentracker.c32
-rw-r--r--ot_accesslist.c34
-rw-r--r--ot_accesslist.h6
-rw-r--r--ot_config.c2
-rw-r--r--ot_fdstate.c188
-rw-r--r--ot_fdstate.h57
-rw-r--r--ot_fullscrape.c14
-rw-r--r--ot_hex.c4
-rw-r--r--ot_hex.h4
-rw-r--r--ot_http.c75
-rw-r--r--ot_iovec.c10
-rw-r--r--ot_iovec.h6
-rw-r--r--ot_stats.c22
-rw-r--r--ot_sync.c6
-rw-r--r--ot_urlencoded.c (renamed from scan_urlencoded_query.c)16
-rw-r--r--ot_urlencoded.h (renamed from scan_urlencoded_query.h)8
-rw-r--r--ot_vector.c10
-rw-r--r--ot_vector.h2
-rw-r--r--proxy.c17
-rw-r--r--tests/runner.c3
-rw-r--r--tests/t_fdstate.c224
-rw-r--r--tests/t_hex.c28
-rw-r--r--tests/t_iovec.c20
-rw-r--r--tests/t_urlencoded.c (renamed from tests/t_scan_urlencoded_query.c)66
-rw-r--r--tests/t_vector.c16
-rw-r--r--tests/test.h3
-rw-r--r--trackerlogic.c6
28 files changed, 677 insertions, 212 deletions
diff --git a/Makefile b/Makefile
index c7105d4..d9b8690 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_batch.h ot_buf.h ot_hex.h ot_ip.h ot_config.h ot_scan.h ot_socket.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 ot_urlencoded.h ot_batch.h ot_buf.h ot_fdstate.h ot_hex.h ot_ip.h ot_config.h ot_scan.h ot_socket.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_batch.c ot_buf.c ot_hex.c ot_ip.c ot_config.c ot_scan.c ot_socket.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 ot_urlencoded.c ot_batch.c ot_buf.c ot_fdstate.c ot_hex.c ot_ip.c ot_config.c ot_scan.c ot_socket.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_batch.c ot_vector.c ot_mutex.c ot_random.c ot_scan.c ot_ip.c ot_socket.c 73SOURCES_proxy=proxy.c ot_batch.c ot_fdstate.c ot_vector.c ot_mutex.c ot_random.c ot_scan.c ot_ip.c ot_socket.c
74 74
75OBJECTS = $(SOURCES:%.c=%.o) 75OBJECTS = $(SOURCES:%.c=%.o)
76OBJECTS_debug = $(SOURCES:%.c=%.debug.o) 76OBJECTS_debug = $(SOURCES:%.c=%.debug.o)
@@ -119,8 +119,8 @@ $(OBJECTS_proxy_debug): $(HEADERS)
119# has to. tests/stubs.c supplies the few symbols those modules reference but 119# has to. tests/stubs.c supplies the few symbols those modules reference but
120# the tests never reach. 120# the tests never reach.
121TESTBIN=tests/runner 121TESTBIN=tests/runner
122TEST_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 tests/t_socket.c tests/t_mutex.c tests/t_batch.c 122TEST_SOURCES=tests/runner.c tests/test.c tests/stubs.c tests/t_urlencoded.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 tests/t_socket.c tests/t_mutex.c tests/t_batch.c tests/t_fdstate.c
123TEST_UNITS=scan_urlencoded_query.c ot_batch.c ot_buf.c ot_ip.c ot_socket.c ot_iovec.c ot_vector.c ot_random.c ot_hex.c ot_scan.c ot_accesslist.c ot_mutex.c 123TEST_UNITS=ot_urlencoded.c ot_batch.c ot_fdstate.c ot_buf.c ot_ip.c ot_socket.c ot_iovec.c ot_vector.c ot_random.c ot_hex.c ot_scan.c ot_accesslist.c ot_mutex.c
124TEST_HEADERS=tests/test.h $(HEADERS) 124TEST_HEADERS=tests/test.h $(HEADERS)
125# Tests reach code that sits behind feature flags, so the units under test are 125# Tests reach code that sits behind feature flags, so the units under test are
126# built with those features enabled. A test binary is not a production binary. 126# built with those features enabled. A test binary is not a production binary.
diff --git a/opentracker.c b/opentracker.c
index 50fff29..f0f6404 100644
--- a/opentracker.c
+++ b/opentracker.c
@@ -34,6 +34,7 @@
34#include "ot_mutex.h" 34#include "ot_mutex.h"
35#include "ot_random.h" 35#include "ot_random.h"
36#include "ot_scan.h" 36#include "ot_scan.h"
37#include "ot_fdstate.h"
37#include "ot_socket.h" 38#include "ot_socket.h"
38#include "ot_stats.h" 39#include "ot_stats.h"
39#include "ot_udp.h" 40#include "ot_udp.h"
@@ -117,7 +118,7 @@ static ssize_t header_complete(char *request, ssize_t byte_count) {
117} 118}
118 119
119static void handle_dead(const int64_t sock) { 120static void handle_dead(const int64_t sock) {
120 struct http_data *cookie = io_getcookie(sock); 121 struct http_data *cookie = fdstate_cookie(sock);
121 if (cookie) { 122 if (cookie) {
122 if (cookie->batch) 123 if (cookie->batch)
123 batch_reset(cookie->batch); 124 batch_reset(cookie->batch);
@@ -127,11 +128,13 @@ static void handle_dead(const int64_t sock) {
127 mutex_workqueue_canceltask(sock); 128 mutex_workqueue_canceltask(sock);
128 free(cookie); 129 free(cookie);
129 } 130 }
131 /* The generation has to move before the kernel can reissue the number */
132 fdstate_close(sock);
130 io_close(sock); 133 io_close(sock);
131} 134}
132 135
133static void handle_read(const int64_t sock, struct ot_workstruct *ws) { 136static void handle_read(const int64_t sock, struct ot_workstruct *ws) {
134 struct http_data *cookie = io_getcookie(sock); 137 struct http_data *cookie = fdstate_cookie(sock);
135 ssize_t byte_count = io_tryread(sock, ws->inbuf, G_INBUF_SIZE); 138 ssize_t byte_count = io_tryread(sock, ws->inbuf, G_INBUF_SIZE);
136 139
137 if (byte_count == 0 || byte_count == -3) { 140 if (byte_count == 0 || byte_count == -3) {
@@ -175,7 +178,7 @@ static void handle_read(const int64_t sock, struct ot_workstruct *ws) {
175} 178}
176 179
177static void handle_write(const int64_t sock) { 180static void handle_write(const int64_t sock) {
178 struct http_data *cookie = io_getcookie(sock); 181 struct http_data *cookie = fdstate_cookie(sock);
179 int chunked = 0; 182 int chunked = 0;
180 183
181 if (cookie) { 184 if (cookie) {
@@ -204,7 +207,6 @@ static void handle_accept(const int64_t serversocket) {
204 int64_t sock; 207 int64_t sock;
205 ot_ip6 ip; 208 ot_ip6 ip;
206 uint16_t port; 209 uint16_t port;
207 tai6464 t;
208 210
209 while ((sock = socket_accept(serversocket, ip, &port)) != -1) { 211 while ((sock = socket_accept(serversocket, ip, &port)) != -1) {
210 212
@@ -218,16 +220,11 @@ static void handle_accept(const int64_t serversocket) {
218 memset(cookie, 0, sizeof(struct http_data)); 220 memset(cookie, 0, sizeof(struct http_data));
219 memcpy(cookie->ip, ip, sizeof(ot_ip6)); 221 memcpy(cookie->ip, ip, sizeof(ot_ip6));
220 222
221 io_setcookie(sock, cookie); 223 fdstate_open(sock, cookie, g_now_seconds + OT_CLIENT_TIMEOUT);
222 io_wantread(sock); 224 io_wantread(sock);
223 225
224 stats_issue_event(EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ip); 226 stats_issue_event(EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ip);
225 227
226 /* That breaks taia encapsulation. But there is no way to take system
227 time this often in FreeBSD and libowfat does not allow to set unix time */
228 taia_uint(&t, 0); /* Clear t */
229 tai_unix(&(t.sec), (g_now_seconds + OT_CLIENT_TIMEOUT));
230 io_timeout(sock, t);
231 } 228 }
232 io_eagain(serversocket); 229 io_eagain(serversocket);
233} 230}
@@ -259,7 +256,7 @@ static void *server_mainloop(void *args) {
259 io_wait(); 256 io_wait();
260 257
261 while ((sock = io_canread()) != -1) { 258 while ((sock = io_canread()) != -1) {
262 const void *cookie = io_getcookie(sock); 259 const void *cookie = fdstate_cookie(sock);
263 if ((intptr_t)cookie == FLAG_TCP) 260 if ((intptr_t)cookie == FLAG_TCP)
264 handle_accept(sock); 261 handle_accept(sock);
265 else if ((intptr_t)cookie == FLAG_UDP) 262 else if ((intptr_t)cookie == FLAG_UDP)
@@ -277,7 +274,9 @@ static void *server_mainloop(void *args) {
277 handle_write(sock); 274 handle_write(sock);
278 275
279 if (g_now_seconds > next_timeout_check) { 276 if (g_now_seconds > next_timeout_check) {
280 while ((sock = io_timeouted()) != -1) 277 int cursor = 0;
278
279 while ((sock = fdstate_next_expired(g_now_seconds, &cursor)) != -1)
281 handle_dead(sock); 280 handle_dead(sock);
282 next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL; 281 next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL;
283 } 282 }
@@ -323,7 +322,7 @@ static void listen_bind_one(ot_listen *entry) {
323 if (!io_fd(entry->sock)) 322 if (!io_fd(entry->sock))
324 panic("io_fd"); 323 panic("io_fd");
325 324
326 io_setcookie(entry->sock, (void *)entry->proto); 325 fdstate_open(entry->sock, (void *)entry->proto, 0);
327 io_wantread(entry->sock); 326 io_wantread(entry->sock);
328 327
329#ifdef _DEBUG 328#ifdef _DEBUG
@@ -376,7 +375,7 @@ void load_state(const char *const state_filename) {
376 while (fgets(inbuf, sizeof(inbuf), state_filehandle)) { 375 while (fgets(inbuf, sizeof(inbuf), state_filehandle)) {
377 size_t i; 376 size_t i;
378 377
379 if (!hex_tohash(infohash, inbuf)) 378 if (!hex_to_hash(infohash, inbuf))
380 continue; 379 continue;
381 i = 2 * sizeof(ot_hash); 380 i = 2 * sizeof(ot_hash);
382 381
@@ -452,6 +451,8 @@ int main(int argc, char **argv) {
452 pthread_t thread_id; /* time cacher */ 451 pthread_t thread_id; /* time cacher */
453 452
454 random_init(); 453 random_init();
454 if (fdstate_init() == -1)
455 panic("fdstate_init");
455 config_parse(argc, argv); 456 config_parse(argc, argv);
456 457
457 listen_bind_all(); 458 listen_bind_all();
@@ -477,7 +478,7 @@ int main(int argc, char **argv) {
477 panic("selfpipe io_fd failed: "); 478 panic("selfpipe io_fd failed: ");
478 if (!io_fd(g_self_pipe[1])) 479 if (!io_fd(g_self_pipe[1]))
479 panic("selfpipe io_fd failed: "); 480 panic("selfpipe io_fd failed: ");
480 io_setcookie(g_self_pipe[0], (void *)FLAG_SELFPIPE); 481 fdstate_open(g_self_pipe[0], (void *)FLAG_SELFPIPE, 0);
481 io_wantread(g_self_pipe[0]); 482 io_wantread(g_self_pipe[0]);
482 483
483 /* Init all sub systems. This call may fail with an exit() */ 484 /* Init all sub systems. This call may fail with an exit() */
@@ -498,5 +499,6 @@ int main(int argc, char **argv) {
498 499
499 server_mainloop(0); 500 server_mainloop(0);
500 501
502 fdstate_deinit();
501 return 0; 503 return 0;
502} 504}
diff --git a/ot_accesslist.c b/ot_accesslist.c
index d7e88ee..79a11a5 100644
--- a/ot_accesslist.c
+++ b/ot_accesslist.c
@@ -110,7 +110,7 @@ size_t accesslist_parse_hashes(ot_hash *dest, size_t capacity, const char *buf,
110 size_t count = 0; 110 size_t count = 0;
111 111
112 while (count < capacity && end - offs >= 40) { 112 while (count < capacity && end - offs >= 40) {
113 if (hex_tohash(dest[count], offs)) { 113 if (hex_to_hash(dest[count], offs)) {
114 offs += 40; 114 offs += 40;
115 115
116 if (offs == end || !hex_isdigit((unsigned char)*offs)) 116 if (offs == end || !hex_isdigit((unsigned char)*offs))
@@ -308,7 +308,7 @@ static void *accesslist_adddel_worker(const char *fifoname, ot_accesslist *_
308 if (linelen < 41) 308 if (linelen < 41)
309 continue; 309 continue;
310 310
311 if (!hex_tohash(info_hash, line)) 311 if (!hex_to_hash(info_hash, line))
312 continue; 312 continue;
313 313
314 /* From now on we modify g_accesslist_add and g_accesslist_delete, so prevent the 314 /* From now on we modify g_accesslist_add and g_accesslist_delete, so prevent the
@@ -339,7 +339,7 @@ static void *accesslist_adddel_worker(const char *fifoname, ot_accesslist *_
339 } 339 }
340 } else { 340 } else {
341 int exactmatch = 0; 341 int exactmatch = 0;
342 ot_hash *insert_point = binary_search(info_hash, (*adding_to)->list, (*adding_to)->size, sizeof(ot_hash), OT_HASH_COMPARE_SIZE, &exactmatch); 342 ot_hash *insert_point = vector_binary_search(info_hash, (*adding_to)->list, (*adding_to)->size, sizeof(ot_hash), OT_HASH_COMPARE_SIZE, &exactmatch);
343 343
344 /* 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 */
345 if (!exactmatch) { 345 if (!exactmatch) {
@@ -416,7 +416,7 @@ void accesslist_cleanup(void) {
416} 416}
417#endif 417#endif
418 418
419int address_in_net(const ot_ip6 address, const ot_net *net) { 419int accesslist_address_in_net(const ot_ip6 address, const ot_net *net) {
420 int bits = net->bits, checkbits = (0x7f00 >> (bits & 7)); 420 int bits = net->bits, checkbits = (0x7f00 >> (bits & 7));
421 int result = memcmp(address, &net->address, bits >> 3); 421 int result = memcmp(address, &net->address, bits >> 3);
422 if (!result && (bits & 7)) 422 if (!result && (bits & 7))
@@ -424,7 +424,7 @@ int address_in_net(const ot_ip6 address, const ot_net *net) {
424 return result == 0; 424 return result == 0;
425} 425}
426 426
427void *set_value_for_net(const ot_net *net, ot_vector *vector, const void *value, const size_t member_size) { 427void *accesslist_set_value_for_net(const ot_net *net, ot_vector *vector, const void *value, const size_t member_size) {
428 size_t i; 428 size_t i;
429 int exactmatch; 429 int exactmatch;
430 430
@@ -435,7 +435,7 @@ void *set_value_for_net(const ot_net *net, ot_vector *vector, const void *value,
435 /* Check each net in vector for overlap */ 435 /* Check each net in vector for overlap */
436 uint8_t *member = ((uint8_t *)vector->data); 436 uint8_t *member = ((uint8_t *)vector->data);
437 for (i = 0; i < vector->size; ++i) { 437 for (i = 0; i < vector->size; ++i) {
438 if (address_in_net(*(ot_ip6 *)member, net) || address_in_net(net->address, (ot_net *)member)) 438 if (accesslist_address_in_net(*(ot_ip6 *)member, net) || accesslist_address_in_net(net->address, (ot_net *)member))
439 return 0; 439 return 0;
440 member += member_size; 440 member += member_size;
441 } 441 }
@@ -451,16 +451,16 @@ void *set_value_for_net(const ot_net *net, ot_vector *vector, const void *value,
451 451
452/* Takes a vector filled with { ot_net net, uint8_t[x] value }; 452/* Takes a vector filled with { ot_net net, uint8_t[x] value };
453 Returns value associated with the net, or NULL if not found */ 453 Returns value associated with the net, or NULL if not found */
454void *get_value_for_net(const ot_ip6 address, const ot_vector *vector, const size_t member_size) { 454void *accesslist_get_value_for_net(const ot_ip6 address, const ot_vector *vector, const size_t member_size) {
455 int exactmatch; 455 int exactmatch;
456 /* This binary search will return a pointer to the first non-containing network... */ 456 /* This binary search will return a pointer to the first non-containing network... */
457 ot_net *net = binary_search(address, vector->data, vector->size, member_size, sizeof(ot_ip6), &exactmatch); 457 ot_net *net = vector_binary_search(address, vector->data, vector->size, member_size, sizeof(ot_ip6), &exactmatch);
458 if (!net) 458 if (!net)
459 return NULL; 459 return NULL;
460 /* ... so we'll need to move back one step unless we've exactly hit the first address in network */ 460 /* ... so we'll need to move back one step unless we've exactly hit the first address in network */
461 if (!exactmatch && ((void *)net > vector->data)) 461 if (!exactmatch && ((void *)net > vector->data))
462 --net; 462 --net;
463 if (!address_in_net(address, net)) 463 if (!accesslist_address_in_net(address, net))
464 return NULL; 464 return NULL;
465 return (void *)net; 465 return (void *)net;
466} 466}
@@ -472,7 +472,7 @@ static pthread_mutex_t g_lognets_list_mutex = PTHREAD_MUTEX_INITIALIZER;
472 472
473void loglist_add_network(const ot_net *net) { 473void loglist_add_network(const ot_net *net) {
474 pthread_mutex_lock(&g_lognets_list_mutex); 474 pthread_mutex_lock(&g_lognets_list_mutex);
475 set_value_for_net(net, &g_lognets_list, NULL, sizeof(ot_net)); 475 accesslist_set_value_for_net(net, &g_lognets_list, NULL, sizeof(ot_net));
476 pthread_mutex_unlock(&g_lognets_list_mutex); 476 pthread_mutex_unlock(&g_lognets_list_mutex);
477} 477}
478 478
@@ -487,7 +487,7 @@ void loglist_reset(void) {
487int loglist_check_address(const ot_ip6 address) { 487int loglist_check_address(const ot_ip6 address) {
488 int result; 488 int result;
489 pthread_mutex_lock(&g_lognets_list_mutex); 489 pthread_mutex_lock(&g_lognets_list_mutex);
490 result = (NULL != get_value_for_net(address, &g_lognets_list, sizeof(ot_net))); 490 result = (NULL != accesslist_get_value_for_net(address, &g_lognets_list, sizeof(ot_net)));
491 pthread_mutex_unlock(&g_lognets_list_mutex); 491 pthread_mutex_unlock(&g_lognets_list_mutex);
492 return result; 492 return result;
493} 493}
@@ -508,17 +508,17 @@ int proxylist_add_network(const ot_net *proxy, const ot_net *net) {
508 pthread_mutex_lock(&g_proxies_list_mutex); 508 pthread_mutex_lock(&g_proxies_list_mutex);
509 509
510 /* If we have a direct hit, use and extend the vector there */ 510 /* If we have a direct hit, use and extend the vector there */
511 map = binary_search(proxy, g_proxies_list.data, g_proxies_list.size, sizeof(ot_proxymap), sizeof(ot_net), &exactmatch); 511 map = vector_binary_search(proxy, g_proxies_list.data, g_proxies_list.size, sizeof(ot_proxymap), sizeof(ot_net), &exactmatch);
512 512
513 if (!map || !exactmatch) { 513 if (!map || !exactmatch) {
514 /* else see, if we've got overlapping networks 514 /* else see, if we've got overlapping networks
515 and get a new empty vector if not */ 515 and get a new empty vector if not */
516 ot_vector empty; 516 ot_vector empty;
517 memset(&empty, 0, sizeof(ot_vector)); 517 memset(&empty, 0, sizeof(ot_vector));
518 map = set_value_for_net(proxy, &g_proxies_list, &empty, sizeof(ot_proxymap)); 518 map = accesslist_set_value_for_net(proxy, &g_proxies_list, &empty, sizeof(ot_proxymap));
519 } 519 }
520 520
521 if (map && set_value_for_net(net, &map->networks, NULL, sizeof(ot_net))) 521 if (map && accesslist_set_value_for_net(net, &map->networks, NULL, sizeof(ot_net)))
522 result = 1; 522 result = 1;
523 523
524 pthread_mutex_unlock(&g_proxies_list_mutex); 524 pthread_mutex_unlock(&g_proxies_list_mutex);
@@ -531,8 +531,8 @@ int proxylist_check_proxy(const ot_ip6 proxy, const ot_ip6 address) {
531 531
532 pthread_mutex_lock(&g_proxies_list_mutex); 532 pthread_mutex_lock(&g_proxies_list_mutex);
533 533
534 if ((map = get_value_for_net(proxy, &g_proxies_list, sizeof(ot_proxymap)))) 534 if ((map = accesslist_get_value_for_net(proxy, &g_proxies_list, sizeof(ot_proxymap))))
535 if (!address || get_value_for_net(address, &map->networks, sizeof(ot_net))) 535 if (!address || accesslist_get_value_for_net(address, &map->networks, sizeof(ot_net)))
536 result = 1; 536 result = 1;
537 537
538 pthread_mutex_unlock(&g_proxies_list_mutex); 538 pthread_mutex_unlock(&g_proxies_list_mutex);
@@ -587,7 +587,7 @@ int accesslist_bless_net(ot_net *net, ot_permissions permissions) {
587int accesslist_is_blessed(ot_ip6 ip, ot_permissions permissions) { 587int accesslist_is_blessed(ot_ip6 ip, ot_permissions permissions) {
588 size_t i; 588 size_t i;
589 for (i = 0; i < g_admin_nets_count; ++i) 589 for (i = 0; i < g_admin_nets_count; ++i)
590 if (address_in_net(ip, g_admin_nets + i) && (g_admin_nets_permissions[i] & permissions)) 590 if (accesslist_address_in_net(ip, g_admin_nets + i) && (g_admin_nets_permissions[i] & permissions))
591 return 1; 591 return 1;
592 return 0; 592 return 0;
593} 593}
diff --git a/ot_accesslist.h b/ot_accesslist.h
index 9dfdd95..0a8bf4c 100644
--- a/ot_accesslist.h
+++ b/ot_accesslist.h
@@ -40,7 +40,7 @@ extern const char *g_accesslist_pipe_delete;
40#endif 40#endif
41 41
42/* Test if an address is subset of an ot_net, return value is considered a bool */ 42/* Test if an address is subset of an ot_net, return value is considered a bool */
43int address_in_net(const ot_ip6 address, const ot_net *net); 43int accesslist_address_in_net(const ot_ip6 address, const ot_net *net);
44 44
45/* Store a value into a vector of struct { ot_net net, uint8_t[x] value } member; 45/* Store a value into a vector of struct { ot_net net, uint8_t[x] value } member;
46 returns NULL 46 returns NULL
@@ -51,13 +51,13 @@ int address_in_net(const ot_ip6 address, const ot_net *net);
51 returns pointer to new member in vector for success 51 returns pointer to new member in vector for success
52 member_size can be sizeof(ot_net) to reduce the lookup to a boolean mapping 52 member_size can be sizeof(ot_net) to reduce the lookup to a boolean mapping
53*/ 53*/
54void *set_value_for_net(const ot_net *net, ot_vector *vector, const void *value, const size_t member_size); 54void *accesslist_set_value_for_net(const ot_net *net, ot_vector *vector, const void *value, const size_t member_size);
55 55
56/* Takes a vector filled with struct { ot_net net, uint8_t[x] value } member; 56/* Takes a vector filled with struct { ot_net net, uint8_t[x] value } member;
57 Returns pointer to _member_ associated with the net, or NULL if not found 57 Returns pointer to _member_ associated with the net, or NULL if not found
58 member_size can be sizeof(ot_net) to reduce the lookup to a boolean mapping 58 member_size can be sizeof(ot_net) to reduce the lookup to a boolean mapping
59*/ 59*/
60void *get_value_for_net(const ot_ip6 address, const ot_vector *vector, const size_t member_size); 60void *accesslist_get_value_for_net(const ot_ip6 address, const ot_vector *vector, const size_t member_size);
61 61
62#ifdef WANT_IP_FROM_PROXY 62#ifdef WANT_IP_FROM_PROXY
63int proxylist_add_network(const ot_net *proxy, const ot_net *net); 63int proxylist_add_network(const ot_net *proxy, const ot_net *net);
diff --git a/ot_config.c b/ot_config.c
index e9666f9..a5fc06e 100644
--- a/ot_config.c
+++ b/ot_config.c
@@ -264,7 +264,7 @@ static config_kind parse_config_key(char **p) {
264 return CONFIG_UNKNOWN; 264 return CONFIG_UNKNOWN;
265} 265}
266 266
267int parse_configfile(char *config_filename) { 267static int parse_configfile(char *config_filename) {
268 FILE *config_file_handle; 268 FILE *config_file_handle;
269 char inbuf[512]; 269 char inbuf[512];
270 ot_ip6 tmpip; 270 ot_ip6 tmpip;
diff --git a/ot_fdstate.c b/ot_fdstate.c
new file mode 100644
index 0000000..7f73d3d
--- /dev/null
+++ b/ot_fdstate.c
@@ -0,0 +1,188 @@
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 <stdatomic.h>
8#include <stdint.h>
9#include <stdlib.h>
10#include <sys/mman.h>
11#include <sys/resource.h>
12#include <sys/types.h>
13
14/* Opentracker */
15#include "ot_fdstate.h"
16
17enum { FD_FREE, FD_IDLE, FD_BUSY, FD_CLOSING };
18
19/* Generation and state share one word so that taking the state is also a
20 check that the generation has not moved */
21#define FD_GEN(w) ((uint32_t)((w) >> 32))
22#define FD_ST(w) ((uint32_t)(((w) >> 24) & 0xff))
23#define FD_WORD(g, s) (((uint64_t)(g) << 32) | ((uint64_t)(s) << 24))
24#define FD_SET_ST(w, s) (((w) & ~(uint64_t)0xff000000) | ((uint64_t)(s) << 24))
25
26typedef struct {
27 _Atomic uint64_t state;
28 _Atomic(void *) cookie;
29 _Atomic ot_time expiry;
30} ot_fdslot;
31
32/* A descriptor the kernel can hand out is below the limit it was handed out
33 under, but the limit can be raised after we sized the table, so an out of
34 range descriptor is rejected */
35#define FD_SLOTS_MAX (1u << 20)
36
37static ot_fdslot *g_slots;
38static size_t g_slot_count;
39/* Descriptors are handed out lowest first, so a sweep only has to walk as
40 far as the highest one ever seen rather than the whole table. */
41static _Atomic size_t g_slot_highest;
42
43int fdstate_init(void) {
44 struct rlimit limit;
45 size_t count = FD_SLOTS_MAX;
46
47 if (getrlimit(RLIMIT_NOFILE, &limit) == 0 && limit.rlim_cur != RLIM_INFINITY && limit.rlim_cur < FD_SLOTS_MAX)
48 count = (size_t)limit.rlim_cur;
49
50 g_slots = mmap(NULL, count * sizeof(ot_fdslot), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
51 if (g_slots == MAP_FAILED) {
52 g_slots = NULL;
53 return -1;
54 }
55
56 g_slot_count = count;
57 atomic_store(&g_slot_highest, 0);
58 return 0;
59}
60
61void fdstate_deinit(void) {
62 if (g_slots)
63 munmap(g_slots, g_slot_count * sizeof(ot_fdslot));
64 g_slots = NULL;
65 g_slot_count = 0;
66 atomic_store(&g_slot_highest, 0);
67}
68
69size_t fdstate_slots(void) { return g_slot_count; }
70
71static ot_fdslot *fdstate_slot(int fd) {
72 if (fd < 0 || (size_t)fd >= g_slot_count)
73 return NULL;
74 return g_slots + fd;
75}
76
77uint32_t fdstate_open(int fd, void *cookie, ot_time expiry) {
78 ot_fdslot *slot = fdstate_slot(fd);
79 uint64_t word;
80
81 if (!slot)
82 return 0;
83
84 {
85 size_t highest = atomic_load_explicit(&g_slot_highest, memory_order_relaxed);
86
87 while ((size_t)fd > highest &&
88 !atomic_compare_exchange_weak_explicit(&g_slot_highest, &highest, (size_t)fd, memory_order_relaxed,
89 memory_order_relaxed))
90 ;
91 }
92
93 word = atomic_load_explicit(&slot->state, memory_order_relaxed);
94 atomic_store_explicit(&slot->cookie, cookie, memory_order_relaxed);
95 atomic_store_explicit(&slot->expiry, expiry, memory_order_relaxed);
96 /* Publishes the two above to whoever claims the slot next */
97 atomic_store_explicit(&slot->state, FD_WORD(FD_GEN(word), FD_IDLE), memory_order_release);
98
99 return FD_GEN(word);
100}
101
102int fdstate_claim(int fd, uint32_t generation) {
103 ot_fdslot *slot = fdstate_slot(fd);
104 uint64_t word;
105
106 if (!slot)
107 return 0;
108
109 word = atomic_load_explicit(&slot->state, memory_order_acquire);
110 for (;;) {
111 if (FD_GEN(word) != generation || FD_ST(word) != FD_IDLE)
112 return 0;
113 if (atomic_compare_exchange_weak_explicit(&slot->state, &word, FD_SET_ST(word, FD_BUSY), memory_order_acquire,
114 memory_order_relaxed))
115 return 1;
116 /* word now holds what was really there, so the test above runs again */
117 }
118}
119
120void fdstate_release(int fd) {
121 ot_fdslot *slot = fdstate_slot(fd);
122 uint64_t word;
123
124 if (!slot)
125 return;
126
127 word = atomic_load_explicit(&slot->state, memory_order_relaxed);
128 atomic_store_explicit(&slot->state, FD_SET_ST(word, FD_IDLE), memory_order_release);
129}
130
131int fdstate_claim_expired(int fd, ot_time now) {
132 ot_fdslot *slot = fdstate_slot(fd);
133 uint64_t word;
134 ot_time expiry;
135
136 if (!slot)
137 return 0;
138
139 /* Reading before asking for the slot keeps the common case, a descriptor
140 that has not expired, off the bus entirely */
141 word = atomic_load_explicit(&slot->state, memory_order_relaxed);
142 if (FD_ST(word) != FD_IDLE)
143 return 0;
144 expiry = atomic_load_explicit(&slot->expiry, memory_order_relaxed);
145 if (!expiry || expiry > now)
146 return 0;
147
148 /* Nothing can move the expiry while the slot sits at FD_IDLE, so winning
149 the slot is the last word */
150 return atomic_compare_exchange_strong_explicit(&slot->state, &word, FD_SET_ST(word, FD_CLOSING), memory_order_acquire,
151 memory_order_relaxed);
152}
153
154void fdstate_close(int fd) {
155 ot_fdslot *slot = fdstate_slot(fd);
156 uint64_t word;
157
158 if (!slot)
159 return;
160
161 word = atomic_load_explicit(&slot->state, memory_order_relaxed);
162 atomic_store_explicit(&slot->cookie, NULL, memory_order_relaxed);
163 atomic_store_explicit(&slot->expiry, 0, memory_order_relaxed);
164 atomic_store_explicit(&slot->state, FD_WORD(FD_GEN(word) + 1, FD_FREE), memory_order_release);
165}
166
167int fdstate_next_expired(ot_time now, int *cursor) {
168 size_t highest = atomic_load_explicit(&g_slot_highest, memory_order_relaxed);
169
170 for (; (size_t)*cursor <= highest; ++*cursor)
171 if (fdstate_claim_expired(*cursor, now))
172 return (*cursor)++;
173
174 return -1;
175}
176
177void *fdstate_cookie(int fd) {
178 ot_fdslot *slot = fdstate_slot(fd);
179
180 return slot ? atomic_load_explicit(&slot->cookie, memory_order_relaxed) : NULL;
181}
182
183void fdstate_set_expiry(int fd, ot_time expiry) {
184 ot_fdslot *slot = fdstate_slot(fd);
185
186 if (slot)
187 atomic_store_explicit(&slot->expiry, expiry, memory_order_relaxed);
188}
diff --git a/ot_fdstate.h b/ot_fdstate.h
new file mode 100644
index 0000000..88f685d
--- /dev/null
+++ b/ot_fdstate.h
@@ -0,0 +1,57 @@
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_FDSTATE_H__
7#define OT_FDSTATE_H__
8
9#include <stdint.h>
10
11#include "trackerlogic.h"
12
13/* What the tracker knows about a file descriptor: who owns it, when it
14 should be given up on, and whether anyone is working on it right now.
15
16 A descriptor number says nothing about identity, since the kernel hands
17 the same number out again as soon as it is closed. Every slot therefore
18 carries a generation that is bumped when the descriptor is given up, and
19 every caller that was told about a descriptor has to say which generation
20 it was told about. A caller holding a stale generation is turned away
21 instead of being handed somebody else's connection.
22
23 The table is sized once from RLIMIT_NOFILE and never moves, so looking a
24 descriptor up needs no synchronisation at all. */
25
26int fdstate_init(void);
27void fdstate_deinit(void);
28size_t fdstate_slots(void);
29
30/* Take a freshly accepted descriptor into the table and return the
31 generation to quote when asking for it back. expiry of 0 never expires. */
32uint32_t fdstate_open(int fd, void *cookie, ot_time expiry);
33
34/* Ask for exclusive use. Returns 0 if the descriptor has been recycled since
35 generation was handed out, or if somebody else is using it. */
36int fdstate_claim(int fd, uint32_t generation);
37void fdstate_release(int fd);
38
39/* The cleaner's side of the same thing: takes the descriptor only if nobody
40 is working on it and it really has expired. Never waits. */
41int fdstate_claim_expired(int fd, ot_time now);
42
43/* Give the descriptor up. The generation is bumped here, which has to happen
44 before close(2) so that the kernel cannot reissue the number while events
45 quoting the old generation are still in flight. */
46void fdstate_close(int fd);
47
48/* Walks the table for descriptors that have expired, handing back one at a
49 time until it returns -1. The cursor belongs to the caller, so several
50 sweepers can run without sharing anything. */
51int fdstate_next_expired(ot_time now, int *cursor);
52
53/* Both only valid while the caller holds the descriptor. */
54void *fdstate_cookie(int fd);
55void fdstate_set_expiry(int fd, ot_time expiry);
56
57#endif
diff --git a/ot_fullscrape.c b/ot_fullscrape.c
index eade79d..0dee5c2 100644
--- a/ot_fullscrape.c
+++ b/ot_fullscrape.c
@@ -25,7 +25,7 @@
25#include "ot_fullscrape.h" 25#include "ot_fullscrape.h"
26#include "ot_iovec.h" 26#include "ot_iovec.h"
27#include "ot_mutex.h" 27#include "ot_mutex.h"
28#include "scan_urlencoded_query.h" 28#include "ot_urlencoded.h"
29#include "trackerlogic.h" 29#include "trackerlogic.h"
30 30
31/* Fetch full scrape info for all torrents 31/* Fetch full scrape info for all torrents
@@ -105,12 +105,12 @@ static char * fullscrape_write_one( ot_tasktype mode, char *r, ot_torrent *torre
105 105
106 break; 106 break;
107 case TASK_FULLSCRAPE_TPB_ASCII: 107 case TASK_FULLSCRAPE_TPB_ASCII:
108 hex_fromhash(r, *hash); 108 hex_from_hash(r, *hash);
109 r += 2 * sizeof(ot_hash); 109 r += 2 * sizeof(ot_hash);
110 r += sprintf(r, ":%zd:%zd\n", seed_count, peer_count - seed_count); 110 r += sprintf(r, ":%zd:%zd\n", seed_count, peer_count - seed_count);
111 break; 111 break;
112 case TASK_FULLSCRAPE_TPB_ASCII_PLUS: 112 case TASK_FULLSCRAPE_TPB_ASCII_PLUS:
113 hex_fromhash(r, *hash); 113 hex_from_hash(r, *hash);
114 r += 2 * sizeof(ot_hash); 114 r += 2 * sizeof(ot_hash);
115 r += sprintf(r, ":%zd:%zd:%zd\n", seed_count, peer_count - seed_count, down_count); 115 r += sprintf(r, ":%zd:%zd:%zd\n", seed_count, peer_count - seed_count, down_count);
116 break; 116 break;
@@ -122,11 +122,11 @@ static char * fullscrape_write_one( ot_tasktype mode, char *r, ot_torrent *torre
122 r += 8; 122 r += 8;
123 break; 123 break;
124 case TASK_FULLSCRAPE_TPB_URLENCODED: 124 case TASK_FULLSCRAPE_TPB_URLENCODED:
125 r += fmt_urlencoded(r, *hash, sizeof(ot_hash)); 125 r += urlencoded_format(r, *hash, sizeof(ot_hash));
126 r += sprintf(r, ":%zd:%zd\n", seed_count, peer_count - seed_count); 126 r += sprintf(r, ":%zd:%zd\n", seed_count, peer_count - seed_count);
127 break; 127 break;
128 case TASK_FULLSCRAPE_TRACKERSTATE: 128 case TASK_FULLSCRAPE_TRACKERSTATE:
129 hex_fromhash(r, *hash); 129 hex_from_hash(r, *hash);
130 r += 2 * sizeof(ot_hash); 130 r += 2 * sizeof(ot_hash);
131 r += sprintf(r, ":%zd:%zd\n", torrent->peer_list6->base, down_count); 131 r += sprintf(r, ":%zd:%zd\n", torrent->peer_list6->base, down_count);
132 break; 132 break;
@@ -296,7 +296,7 @@ static void fullscrape_make_gzip(int taskid, ot_tasktype mode) {
296 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE); 296 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE);
297 297
298 if (!iovector.iov_base) { 298 if (!iovector.iov_base) {
299 fprintf(stderr, "Problem with iovec_fix_increase_or_free\n"); 299 fprintf(stderr, "Problem with iovec_next_chunk\n");
300 deflateEnd(&strm); 300 deflateEnd(&strm);
301 return; 301 return;
302 } 302 }
@@ -422,7 +422,7 @@ static void fullscrape_make_zstd(int taskid, ot_tasktype mode) {
422 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE); 422 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE);
423 423
424 if (!iovector.iov_base) { 424 if (!iovector.iov_base) {
425 fprintf(stderr, "Problem with iovec_fix_increase_or_free\n"); 425 fprintf(stderr, "Problem with iovec_next_chunk\n");
426 ZSTD_freeCCtx(zstream); 426 ZSTD_freeCCtx(zstream);
427 return; 427 return;
428 } 428 }
diff --git a/ot_hex.c b/ot_hex.c
index 9581951..2bf69c4 100644
--- a/ot_hex.c
+++ b/ot_hex.c
@@ -23,7 +23,7 @@ int hex_value(int c) {
23 23
24int hex_isdigit(int c) { return hex_value(c) >= 0; } 24int hex_isdigit(int c) { return hex_value(c) >= 0; }
25 25
26char *hex_fromhash(char *dest, const uint8_t *hash) { 26char *hex_from_hash(char *dest, const uint8_t *hash) {
27 static const char digits[] = "0123456789ABCDEF"; 27 static const char digits[] = "0123456789ABCDEF";
28 char *d = dest; 28 char *d = dest;
29 size_t i; 29 size_t i;
@@ -36,7 +36,7 @@ char *hex_fromhash(char *dest, const uint8_t *hash) {
36 return dest; 36 return dest;
37} 37}
38 38
39int hex_tohash(ot_hash hash, const char *src) { 39int hex_to_hash(ot_hash hash, const char *src) {
40 uint8_t decoded[sizeof(ot_hash)]; 40 uint8_t decoded[sizeof(ot_hash)];
41 size_t i; 41 size_t i;
42 42
diff --git a/ot_hex.h b/ot_hex.h
index 3b45b61..28eddf5 100644
--- a/ot_hex.h
+++ b/ot_hex.h
@@ -12,12 +12,12 @@
12 12
13/* Write hash as forty upper case hex digits followed by a terminating zero. 13/* Write hash as forty upper case hex digits followed by a terminating zero.
14 dest must have room for forty one bytes. Returns dest. */ 14 dest must have room for forty one bytes. Returns dest. */
15char *hex_fromhash(char *dest, const uint8_t *hash); 15char *hex_from_hash(char *dest, const uint8_t *hash);
16 16
17/* Read forty hex digits from src into hash. Returns non-zero on success. 17/* Read forty hex digits from src into hash. Returns non-zero on success.
18 On failure hash is left untouched and no byte of src beyond the offending 18 On failure hash is left untouched and no byte of src beyond the offending
19 character is read, so src need not be forty characters long. */ 19 character is read, so src need not be forty characters long. */
20int hex_tohash(ot_hash hash, const char *src); 20int hex_to_hash(ot_hash hash, const char *src);
21 21
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);
diff --git a/ot_http.c b/ot_http.c
index 4675efb..6c3332c 100644
--- a/ot_http.c
+++ b/ot_http.c
@@ -25,8 +25,9 @@
25#include "ot_ip.h" 25#include "ot_ip.h"
26#include "ot_mutex.h" 26#include "ot_mutex.h"
27#include "ot_scan.h" 27#include "ot_scan.h"
28#include "ot_fdstate.h"
28#include "ot_stats.h" 29#include "ot_stats.h"
29#include "scan_urlencoded_query.h" 30#include "ot_urlencoded.h"
30#include "trackerlogic.h" 31#include "trackerlogic.h"
31 32
32#define OT_MAXMULTISCRAPE_COUNT 64 33#define OT_MAXMULTISCRAPE_COUNT 64
@@ -38,7 +39,7 @@ ssize_t g_stats_path_len;
38enum { SUCCESS_HTTP_HEADER_LENGTH = 80, SUCCESS_HTTP_SIZE_OFF = 17 }; 39enum { SUCCESS_HTTP_HEADER_LENGTH = 80, SUCCESS_HTTP_SIZE_OFF = 17 };
39 40
40static void http_senddata(const int64_t sock, struct ot_workstruct *ws) { 41static void http_senddata(const int64_t sock, struct ot_workstruct *ws) {
41 struct http_data *cookie = io_getcookie(sock); 42 struct http_data *cookie = fdstate_cookie(sock);
42 ssize_t written_size; 43 ssize_t written_size;
43 44
44 if (!cookie) { 45 if (!cookie) {
@@ -72,7 +73,6 @@ static void http_senddata(const int64_t sock, struct ot_workstruct *ws) {
72 73
73 if (written_size < ws->reply_size) { 74 if (written_size < ws->reply_size) {
74 char *outbuf; 75 char *outbuf;
75 tai6464 t;
76 76
77 if (!(outbuf = malloc(ws->reply_size - written_size))) { 77 if (!(outbuf = malloc(ws->reply_size - written_size))) {
78 buf_reset(&cookie->request); 78 buf_reset(&cookie->request);
@@ -97,8 +97,7 @@ static void http_senddata(const int64_t sock, struct ot_workstruct *ws) {
97 97
98 /* writeable short data sockets just have a tcp timeout */ 98 /* writeable short data sockets just have a tcp timeout */
99 if (!ws->keep_alive) { 99 if (!ws->keep_alive) {
100 taia_uint(&t, 0); 100 fdstate_set_expiry(sock, 0);
101 io_timeout(sock, t);
102 io_dontwantread(sock); 101 io_dontwantread(sock);
103 } 102 }
104 io_wantwrite(sock); 103 io_wantwrite(sock);
@@ -135,11 +134,10 @@ ssize_t http_issue_error(const int64_t sock, struct ot_workstruct *ws, int code)
135} 134}
136 135
137ssize_t http_sendiovecdata(const int64_t sock, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial) { 136ssize_t http_sendiovecdata(const int64_t sock, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial) {
138 struct http_data *cookie = io_getcookie(sock); 137 struct http_data *cookie = fdstate_cookie(sock);
139 char *header; 138 char *header;
140 const char *encoding = ""; 139 const char *encoding = "";
141 size_t header_size, size = iovec_length(&iovec_entries, (const struct iovec **)&iovector); 140 size_t header_size, size = iovec_length(&iovec_entries, (const struct iovec **)&iovector);
142 tai6464 t;
143 141
144 /* No cookie? Bad socket. Leave. */ 142 /* No cookie? Bad socket. Leave. */
145 if (!cookie) { 143 if (!cookie) {
@@ -209,10 +207,7 @@ ssize_t http_sendiovecdata(const int64_t sock, struct ot_workstruct *ws, size_t
209 cookie->flag &= ~STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER; 207 cookie->flag &= ~STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER;
210 } 208 }
211 209
212 /* writeable sockets timeout after 10 minutes */ 210 fdstate_set_expiry(sock, g_now_seconds + OT_CLIENT_TIMEOUT_SEND);
213 taia_now(&t);
214 taia_addsec(&t, &t, OT_CLIENT_TIMEOUT_SEND);
215 io_timeout(sock, t);
216 io_wantwrite(sock); 211 io_wantwrite(sock);
217 return 0; 212 return 0;
218} 213}
@@ -248,7 +243,7 @@ static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, c
248 {"url", TASK_FULLSCRAPE_TPB_URLENCODED}, {"txt", TASK_FULLSCRAPE_TPB_ASCII}, 243 {"url", TASK_FULLSCRAPE_TPB_URLENCODED}, {"txt", TASK_FULLSCRAPE_TPB_ASCII},
249 {"txtp", TASK_FULLSCRAPE_TPB_ASCII_PLUS}, {NULL, -3}}; 244 {"txtp", TASK_FULLSCRAPE_TPB_ASCII_PLUS}, {NULL, -3}};
250 245
251 struct http_data *cookie = io_getcookie(sock); 246 struct http_data *cookie = fdstate_cookie(sock);
252 int mode = TASK_STATS_PEERS, scanon = 1, format = 0; 247 int mode = TASK_STATS_PEERS, scanon = 1, format = 0;
253 248
254 /* A socket without one is a dead socket, which is what http_sendiovecdata 249 /* A socket without one is a dead socket, which is what http_sendiovecdata
@@ -262,21 +257,21 @@ static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, c
262#endif 257#endif
263 258
264 while (scanon) { 259 while (scanon) {
265 switch (scan_find_keywords(keywords_main, &read_ptr, SCAN_SEARCHPATH_PARAM)) { 260 switch (urlencoded_match_keyword(keywords_main, &read_ptr, SCAN_SEARCHPATH_PARAM)) {
266 case -2: 261 case -2:
267 scanon = 0; 262 scanon = 0;
268 break; /* TERMINATOR */ 263 break; /* TERMINATOR */
269 case -1: 264 case -1:
270 HTTPERROR_400_PARAM; /* PARSE ERROR */ 265 HTTPERROR_400_PARAM; /* PARSE ERROR */
271 case -3: 266 case -3:
272 scan_urlencoded_skipvalue(&read_ptr); 267 urlencoded_skip_value(&read_ptr);
273 break; 268 break;
274 case 1: /* matched "mode" */ 269 case 1: /* matched "mode" */
275 if ((mode = scan_find_keywords(keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) 270 if ((mode = urlencoded_match_keyword(keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0)
276 HTTPERROR_400_PARAM; 271 HTTPERROR_400_PARAM;
277 break; 272 break;
278 case 2: /* matched "format" */ 273 case 2: /* matched "format" */
279 if ((format = scan_find_keywords(keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) 274 if ((format = urlencoded_match_keyword(keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0)
280 HTTPERROR_400_PARAM; 275 HTTPERROR_400_PARAM;
281 break; 276 break;
282 case 3: 277 case 3:
@@ -291,7 +286,6 @@ static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, c
291 } 286 }
292 287
293 if (mode == TASK_STATS_TPB) { 288 if (mode == TASK_STATS_TPB) {
294 tai6464 t;
295#ifdef WANT_COMPRESSION_GZIP 289#ifdef WANT_COMPRESSION_GZIP
296 ws->request[ws->request_size - 1] = 0; 290 ws->request[ws->request_size - 1] = 0;
297#ifndef WANT_COMPRESSION_GZIP_ALWAYS 291#ifndef WANT_COMPRESSION_GZIP_ALWAYS
@@ -307,8 +301,7 @@ static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, c
307 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED; 301 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED;
308 302
309 /* Clients waiting for us should not easily timeout */ 303 /* Clients waiting for us should not easily timeout */
310 taia_uint(&t, 0); 304 fdstate_set_expiry(sock, 0);
311 io_timeout(sock, t);
312 fullscrape_deliver(sock, format); 305 fullscrape_deliver(sock, format);
313 io_dontwantread(sock); 306 io_dontwantread(sock);
314 return ws->reply_size = -2; 307 return ws->reply_size = -2;
@@ -317,13 +310,11 @@ static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, c
317 310
318 /* default format for now */ 311 /* default format for now */
319 if ((mode & TASK_CLASS_MASK) == TASK_STATS) { 312 if ((mode & TASK_CLASS_MASK) == TASK_STATS) {
320 tai6464 t;
321 313
322 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK; 314 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK;
323 315
324 /* Complex stats also include expensive memory debugging tools */ 316 /* Complex stats also include expensive memory debugging tools */
325 taia_uint(&t, 0); 317 fdstate_set_expiry(sock, 0);
326 io_timeout(sock, t);
327 stats_deliver(sock, mode); 318 stats_deliver(sock, mode);
328 return ws->reply_size = -2; 319 return ws->reply_size = -2;
329 } 320 }
@@ -343,9 +334,8 @@ typedef struct {
343 334
344#ifdef WANT_FULLSCRAPE 335#ifdef WANT_FULLSCRAPE
345static ssize_t http_handle_fullscrape(const int64_t sock, struct ot_workstruct *ws) { 336static ssize_t http_handle_fullscrape(const int64_t sock, struct ot_workstruct *ws) {
346 struct http_data *cookie = io_getcookie(sock); 337 struct http_data *cookie = fdstate_cookie(sock);
347 int format = 0; 338 int format = 0;
348 tai6464 t;
349 339
350#ifdef WANT_MODEST_FULLSCRAPES 340#ifdef WANT_MODEST_FULLSCRAPES
351 { 341 {
@@ -404,8 +394,7 @@ static ssize_t http_handle_fullscrape(const int64_t sock, struct ot_workstruct *
404 /* Pass this task to the worker thread */ 394 /* Pass this task to the worker thread */
405 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED; 395 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED;
406 /* Clients waiting for us should not easily timeout */ 396 /* Clients waiting for us should not easily timeout */
407 taia_uint(&t, 0); 397 fdstate_set_expiry(sock, 0);
408 io_timeout(sock, t);
409 fullscrape_deliver(sock, format); 398 fullscrape_deliver(sock, format);
410 io_dontwantread(sock); 399 io_dontwantread(sock);
411 return ws->reply_size = -2; 400 return ws->reply_size = -2;
@@ -429,18 +418,18 @@ static ssize_t http_handle_scrape(const int64_t sock, struct ot_workstruct *ws,
429 } 418 }
430 419
431 while (scanon) { 420 while (scanon) {
432 switch (scan_find_keywords(keywords_scrape, &read_ptr, SCAN_SEARCHPATH_PARAM)) { 421 switch (urlencoded_match_keyword(keywords_scrape, &read_ptr, SCAN_SEARCHPATH_PARAM)) {
433 case -2: 422 case -2:
434 scanon = 0; 423 scanon = 0;
435 break; /* TERMINATOR */ 424 break; /* TERMINATOR */
436 default: 425 default:
437 HTTPERROR_400_PARAM; /* PARSE ERROR */ 426 HTTPERROR_400_PARAM; /* PARSE ERROR */
438 case -3: 427 case -3:
439 scan_urlencoded_skipvalue(&read_ptr); 428 urlencoded_skip_value(&read_ptr);
440 break; 429 break;
441 case 1: /* matched "info_hash" */ 430 case 1: /* matched "info_hash" */
442 /* ignore this, when we have less than 20 bytes */ 431 /* ignore this, when we have less than 20 bytes */
443 if (scan_urlencoded_query(&read_ptr, (char *)(multiscrape_buf + numwant++), SCAN_SEARCHPATH_VALUE) != (ssize_t)sizeof(ot_hash)) 432 if (urlencoded_parse(&read_ptr, (char *)(multiscrape_buf + numwant++), SCAN_SEARCHPATH_VALUE) != (ssize_t)sizeof(ot_hash))
444 HTTPERROR_400_PARAM; 433 HTTPERROR_400_PARAM;
445 break; 434 break;
446 } 435 }
@@ -497,7 +486,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
497 unsigned short port = 0; 486 unsigned short port = 0;
498 char *write_ptr; 487 char *write_ptr;
499 ssize_t len; 488 ssize_t len;
500 struct http_data *cookie = io_getcookie(sock); 489 struct http_data *cookie = fdstate_cookie(sock);
501 490
502 /* This is to hack around stupid clients that send "announce ?info_hash" */ 491 /* This is to hack around stupid clients that send "announce ?info_hash" */
503 if (read_ptr[-1] != '?') { 492 if (read_ptr[-1] != '?') {
@@ -529,24 +518,24 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
529 scanon = 1; 518 scanon = 1;
530 519
531 while (scanon) { 520 while (scanon) {
532 switch (scan_find_keywords(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM)) { 521 switch (urlencoded_match_keyword(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM)) {
533 case -2: 522 case -2:
534 scanon = 0; 523 scanon = 0;
535 break; /* TERMINATOR */ 524 break; /* TERMINATOR */
536 case -1: 525 case -1:
537 HTTPERROR_400_PARAM; /* PARSE ERROR */ 526 HTTPERROR_400_PARAM; /* PARSE ERROR */
538 case -3: 527 case -3:
539 scan_urlencoded_skipvalue(&read_ptr); 528 urlencoded_skip_value(&read_ptr);
540 break; 529 break;
541 case 1: /* matched "port" */ 530 case 1: /* matched "port" */
542 len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); 531 len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE);
543 if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp) || (tmp > 0xffff)) 532 if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp) || (tmp > 0xffff))
544 HTTPERROR_400_PARAM; 533 HTTPERROR_400_PARAM;
545 port = htons(tmp); 534 port = htons(tmp);
546 OT_SETPORT(&ws->peer, &port); 535 OT_SETPORT(&ws->peer, &port);
547 break; 536 break;
548 case 2: /* matched "left" */ 537 case 2: /* matched "left" */
549 if ((len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) 538 if ((len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0)
550 HTTPERROR_400_PARAM; 539 HTTPERROR_400_PARAM;
551 if (scan_fixed_int(write_ptr, len, &tmp)) 540 if (scan_fixed_int(write_ptr, len, &tmp))
552 tmp = 0; 541 tmp = 0;
@@ -554,7 +543,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
554 OT_PEERFLAG(&ws->peer) |= PEER_FLAG_SEEDING; 543 OT_PEERFLAG(&ws->peer) |= PEER_FLAG_SEEDING;
555 break; 544 break;
556 case 3: /* matched "event" */ 545 case 3: /* matched "event" */
557 switch (scan_find_keywords(keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE)) { 546 switch (urlencoded_match_keyword(keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE)) {
558 case -1: 547 case -1:
559 HTTPERROR_400_PARAM; 548 HTTPERROR_400_PARAM;
560 case 1: /* matched "completed" */ 549 case 1: /* matched "completed" */
@@ -568,7 +557,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
568 } 557 }
569 break; 558 break;
570 case 4: /* matched "numwant" */ 559 case 4: /* matched "numwant" */
571 len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); 560 len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE);
572 if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp)) 561 if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp))
573 HTTPERROR_400_PARAM; 562 HTTPERROR_400_PARAM;
574 if (tmp < 0) 563 if (tmp < 0)
@@ -578,7 +567,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
578 numwant = tmp; 567 numwant = tmp;
579 break; 568 break;
580 case 5: /* matched "compact" */ 569 case 5: /* matched "compact" */
581 len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); 570 len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE);
582 if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp)) 571 if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp))
583 HTTPERROR_400_PARAM; 572 HTTPERROR_400_PARAM;
584 if (!tmp) 573 if (!tmp)
@@ -588,7 +577,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
588 if (ws->hash) 577 if (ws->hash)
589 HTTPERROR_400_DOUBLEHASH; 578 HTTPERROR_400_DOUBLEHASH;
590 /* ignore this, when we have less than 20 bytes */ 579 /* ignore this, when we have less than 20 bytes */
591 if (scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20) 580 if (urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20)
592 HTTPERROR_400_PARAM; 581 HTTPERROR_400_PARAM;
593 ws->hash = (ot_hash *)write_ptr; 582 ws->hash = (ot_hash *)write_ptr;
594 break; 583 break;
@@ -596,7 +585,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
596 case 7: /* matched "ip" */ 585 case 7: /* matched "ip" */
597 { 586 {
598 char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16; 587 char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16;
599 len = scan_urlencoded_query(&read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE); 588 len = urlencoded_parse(&read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE);
600 tmp_buf2[len] = 0; 589 tmp_buf2[len] = 0;
601 if ((len <= 0) || !ip_scan(tmp_buf2, tmp_buf1)) 590 if ((len <= 0) || !ip_scan(tmp_buf2, tmp_buf1))
602 HTTPERROR_400_PARAM; 591 HTTPERROR_400_PARAM;
@@ -612,7 +601,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
612 uint64_t value; 601 uint64_t value;
613 signed short parsed, bits; 602 signed short parsed, bits;
614 603
615 len = scan_urlencoded_query(&read_ptr, tmp_buf, SCAN_SEARCHPATH_VALUE); 604 len = urlencoded_parse(&read_ptr, tmp_buf, SCAN_SEARCHPATH_VALUE);
616 tmp_buf[len] = 0; 605 tmp_buf[len] = 0;
617 if (len <= 0) 606 if (len <= 0)
618 HTTPERROR_400_PARAM; 607 HTTPERROR_400_PARAM;
@@ -641,7 +630,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
641#endif 630#endif
642 case 9: /* matched "peer_id" */ 631 case 9: /* matched "peer_id" */
643 /* ignore this, when we have less than 20 bytes */ 632 /* ignore this, when we have less than 20 bytes */
644 if (scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20) 633 if (urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20)
645 HTTPERROR_400_PARAM; 634 HTTPERROR_400_PARAM;
646 ws->peer_id = write_ptr; 635 ws->peer_id = write_ptr;
647 break; 636 break;
@@ -674,7 +663,7 @@ ssize_t http_handle_request(const int64_t sock, struct ot_workstruct *ws) {
674 char *read_ptr = ws->request, *write_ptr; 663 char *read_ptr = ws->request, *write_ptr;
675 664
676#ifdef WANT_FULLLOG_NETWORKS 665#ifdef WANT_FULLLOG_NETWORKS
677 struct http_data *cookie = io_getcookie(sock); 666 struct http_data *cookie = fdstate_cookie(sock);
678 if (loglist_check_address(cookie->ip)) { 667 if (loglist_check_address(cookie->ip)) {
679 ot_log *log = malloc(sizeof(ot_log)); 668 ot_log *log = malloc(sizeof(ot_log));
680 if (log) { 669 if (log) {
@@ -720,7 +709,7 @@ ssize_t http_handle_request(const int64_t sock, struct ot_workstruct *ws) {
720 In reality we abandoned requiring the url to be correct. This now 709 In reality we abandoned requiring the url to be correct. This now
721 only decodes url encoded characters, we check for announces and 710 only decodes url encoded characters, we check for announces and
722 scrapes by looking for "a*" or "sc" */ 711 scrapes by looking for "a*" or "sc" */
723 len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_PATH); 712 len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_PATH);
724 713
725 /* If parsing returned an error, leave with not found */ 714 /* If parsing returned an error, leave with not found */
726 if (g_redirecturl && (len == -2)) 715 if (g_redirecturl && (len == -2))
diff --git a/ot_iovec.c b/ot_iovec.c
index cd5b9f9..2ee455c 100644
--- a/ot_iovec.c
+++ b/ot_iovec.c
@@ -12,7 +12,7 @@
12/* Opentracker */ 12/* Opentracker */
13#include "ot_iovec.h" 13#include "ot_iovec.h"
14 14
15void *iovec_increase(size_t *iovec_entries, struct iovec **iovector, size_t new_alloc) { 15void *iovec_new_chunk(size_t *iovec_entries, struct iovec **iovector, size_t new_alloc) {
16 void *new_data; 16 void *new_data;
17 size_t new_entries = 1 + *iovec_entries; 17 size_t new_entries = 1 + *iovec_entries;
18 struct iovec *new_vec = realloc(*iovector, new_entries * sizeof(struct iovec)); 18 struct iovec *new_vec = realloc(*iovector, new_entries * sizeof(struct iovec));
@@ -62,7 +62,7 @@ void iovec_free(size_t *iovec_entries, struct iovec **iovector) {
62 *iovec_entries = 0; 62 *iovec_entries = 0;
63} 63}
64 64
65void iovec_fixlast(size_t *iovec_entries, struct iovec **iovector, void *last_ptr) { 65void iovec_trim_last(size_t *iovec_entries, struct iovec **iovector, void *last_ptr) {
66 if (*iovec_entries) { 66 if (*iovec_entries) {
67 char *base = (char *)((*iovector)[*iovec_entries - 1]).iov_base; 67 char *base = (char *)((*iovector)[*iovec_entries - 1]).iov_base;
68 size_t new_alloc = ((char *)last_ptr) - base; 68 size_t new_alloc = ((char *)last_ptr) - base;
@@ -72,12 +72,12 @@ void iovec_fixlast(size_t *iovec_entries, struct iovec **iovector, void *last_pt
72 } 72 }
73} 73}
74 74
75void *iovec_fix_increase_or_free(size_t *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc) { 75void *iovec_next_chunk(size_t *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc) {
76 void *new_data; 76 void *new_data;
77 77
78 iovec_fixlast(iovec_entries, iovector, last_ptr); 78 iovec_trim_last(iovec_entries, iovector, last_ptr);
79 79
80 if (!(new_data = iovec_increase(iovec_entries, iovector, new_alloc))) 80 if (!(new_data = iovec_new_chunk(iovec_entries, iovector, new_alloc)))
81 iovec_free(iovec_entries, iovector); 81 iovec_free(iovec_entries, iovector);
82 82
83 return new_data; 83 return new_data;
diff --git a/ot_iovec.h b/ot_iovec.h
index eb4365b..6f5a1d6 100644
--- a/ot_iovec.h
+++ b/ot_iovec.h
@@ -8,13 +8,13 @@
8 8
9#include <sys/uio.h> 9#include <sys/uio.h>
10 10
11void *iovec_increase(size_t *iovec_entries, struct iovec **iovector, size_t new_alloc); 11void *iovec_new_chunk(size_t *iovec_entries, struct iovec **iovector, size_t new_alloc);
12void *iovec_append(size_t *iovec_entries, struct iovec **iovector, struct iovec *append_iovector); 12void *iovec_append(size_t *iovec_entries, struct iovec **iovector, struct iovec *append_iovector);
13void iovec_fixlast(size_t *iovec_entries, struct iovec **iovector, void *last_ptr); 13void iovec_trim_last(size_t *iovec_entries, struct iovec **iovector, void *last_ptr);
14void iovec_free(size_t *iovec_entries, struct iovec **iovector); 14void iovec_free(size_t *iovec_entries, struct iovec **iovector);
15 15
16size_t iovec_length(const size_t *iovec_entries, const struct iovec **iovector); 16size_t iovec_length(const size_t *iovec_entries, const struct iovec **iovector);
17 17
18void *iovec_fix_increase_or_free(size_t *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc); 18void *iovec_next_chunk(size_t *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc);
19 19
20#endif 20#endif
diff --git a/ot_stats.c b/ot_stats.c
index 6589aef..ceeb89d 100644
--- a/ot_stats.c
+++ b/ot_stats.c
@@ -345,15 +345,15 @@ size_t stats_top_txt(char *reply, size_t amount) {
345 r += sprintf(r, "Top %zd torrents by peers:\n", amount); 345 r += sprintf(r, "Top %zd torrents by peers:\n", amount);
346 for (idx = 0; idx < amount; ++idx) 346 for (idx = 0; idx < amount; ++idx)
347 if (top100c[idx].val) 347 if (top100c[idx].val)
348 r += sprintf(r, "\t%zd\t%s\n", top100c[idx].val, hex_fromhash(hex_out, top100c[idx].hash)); 348 r += sprintf(r, "\t%zd\t%s\n", top100c[idx].val, hex_from_hash(hex_out, top100c[idx].hash));
349 r += sprintf(r, "Top %zd torrents by seeds:\n", amount); 349 r += sprintf(r, "Top %zd torrents by seeds:\n", amount);
350 for (idx = 0; idx < amount; ++idx) 350 for (idx = 0; idx < amount; ++idx)
351 if (top100s[idx].val) 351 if (top100s[idx].val)
352 r += sprintf(r, "\t%zd\t%s\n", top100s[idx].val, hex_fromhash(hex_out, top100s[idx].hash)); 352 r += sprintf(r, "\t%zd\t%s\n", top100s[idx].val, hex_from_hash(hex_out, top100s[idx].hash));
353 r += sprintf(r, "Top %zd torrents by leechers:\n", amount); 353 r += sprintf(r, "Top %zd torrents by leechers:\n", amount);
354 for (idx = 0; idx < amount; ++idx) 354 for (idx = 0; idx < amount; ++idx)
355 if (top100l[idx].val) 355 if (top100l[idx].val)
356 r += sprintf(r, "\t%zd\t%s\n", top100l[idx].val, hex_fromhash(hex_out, top100l[idx].hash)); 356 r += sprintf(r, "\t%zd\t%s\n", top100l[idx].val, hex_from_hash(hex_out, top100l[idx].hash));
357 357
358 return r - reply; 358 return r - reply;
359} 359}
@@ -459,7 +459,7 @@ static void stats_return_fulllog(size_t *iovec_entries, struct iovec **iovector,
459 459
460 while (loglist) { 460 while (loglist) {
461 if (r + (loglist->size + 64) >= re) { 461 if (r + (loglist->size + 64) >= re) {
462 r = iovec_fix_increase_or_free(iovec_entries, iovector, r, 32 * OT_STATS_TMPSIZE); 462 r = iovec_next_chunk(iovec_entries, iovector, r, 32 * OT_STATS_TMPSIZE);
463 if (!r) 463 if (!r)
464 return; 464 return;
465 re = r + 32 * OT_STATS_TMPSIZE; 465 re = r + 32 * OT_STATS_TMPSIZE;
@@ -479,7 +479,7 @@ static void stats_return_fulllog(size_t *iovec_entries, struct iovec **iovector,
479 free(loglist); 479 free(loglist);
480 loglist = llnext; 480 loglist = llnext;
481 } 481 }
482 iovec_fixlast(iovec_entries, iovector, r); 482 iovec_trim_last(iovec_entries, iovector, r);
483} 483}
484#endif 484#endif
485 485
@@ -570,7 +570,7 @@ static void stats_make(size_t *iovec_entries, struct iovec **iovector, ot_taskty
570 570
571 *iovec_entries = 0; 571 *iovec_entries = 0;
572 *iovector = NULL; 572 *iovector = NULL;
573 if (!(r = iovec_increase(iovec_entries, iovector, OT_STATS_TMPSIZE))) 573 if (!(r = iovec_new_chunk(iovec_entries, iovector, OT_STATS_TMPSIZE)))
574 return; 574 return;
575 575
576 switch (mode & TASK_TASK_MASK) { 576 switch (mode & TASK_TASK_MASK) {
@@ -587,13 +587,13 @@ static void stats_make(size_t *iovec_entries, struct iovec **iovector, ot_taskty
587 r += stats_top_txt(r, 10); 587 r += stats_top_txt(r, 10);
588 break; 588 break;
589 case TASK_STATS_TOP100: 589 case TASK_STATS_TOP100:
590 r = iovec_fix_increase_or_free(iovec_entries, iovector, r, 4 * OT_STATS_TMPSIZE); 590 r = iovec_next_chunk(iovec_entries, iovector, r, 4 * OT_STATS_TMPSIZE);
591 if (!r) 591 if (!r)
592 return; 592 return;
593 r += stats_top_txt(r, 100); 593 r += stats_top_txt(r, 100);
594 break; 594 break;
595 case TASK_STATS_EVERYTHING: 595 case TASK_STATS_EVERYTHING:
596 r = iovec_fix_increase_or_free(iovec_entries, iovector, r, OT_STATS_TMPSIZE + 64 * OT_PEER_TIMEOUT); 596 r = iovec_next_chunk(iovec_entries, iovector, r, OT_STATS_TMPSIZE + 64 * OT_PEER_TIMEOUT);
597 if (!r) 597 if (!r)
598 return; 598 return;
599 r += stats_return_everything(r); 599 r += stats_return_everything(r);
@@ -612,7 +612,7 @@ static void stats_make(size_t *iovec_entries, struct iovec **iovector, ot_taskty
612 iovec_free(iovec_entries, iovector); 612 iovec_free(iovec_entries, iovector);
613 return; 613 return;
614 } 614 }
615 iovec_fixlast(iovec_entries, iovector, r); 615 iovec_trim_last(iovec_entries, iovector, r);
616} 616}
617 617
618void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_data) { 618void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_data) {
@@ -651,9 +651,9 @@ void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_
651 localtime_r(&ttt, &time_now); 651 localtime_r(&ttt, &time_now);
652 strftime(timestring, sizeof(timestring), "%FT%T%z", &time_now); 652 strftime(timestring, sizeof(timestring), "%FT%T%z", &time_now);
653 653
654 hex_fromhash(hash_hex, *ws->hash); 654 hex_from_hash(hash_hex, *ws->hash);
655 if (ws->peer_id) 655 if (ws->peer_id)
656 hex_fromhash(peerid_hex, (uint8_t *)ws->peer_id); 656 hex_from_hash(peerid_hex, (uint8_t *)ws->peer_id);
657 else { 657 else {
658 *peerid_hex = 0; 658 *peerid_hex = 0;
659 } 659 }
diff --git a/ot_sync.c b/ot_sync.c
index dc71ddd..5527c94 100644
--- a/ot_sync.c
+++ b/ot_sync.c
@@ -77,7 +77,7 @@ static void sync_make(size_t *iovec_entries, struct iovec **iovector) {
77 /* Setup return vector... */ 77 /* Setup return vector... */
78 *iovec_entries = 0; 78 *iovec_entries = 0;
79 *iovector = NULL; 79 *iovector = NULL;
80 if (!(r = iovec_increase(iovec_entries, iovector, OT_SYNC_CHUNK_SIZE))) 80 if (!(r = iovec_new_chunk(iovec_entries, iovector, OT_SYNC_CHUNK_SIZE)))
81 return; 81 return;
82 82
83 /* ... and pointer to end of current output buffer. 83 /* ... and pointer to end of current output buffer.
@@ -105,7 +105,7 @@ static void sync_make(size_t *iovec_entries, struct iovec **iovector) {
105 105
106 /* Allocate a fresh output buffer at the end of our buffers list 106 /* Allocate a fresh output buffer at the end of our buffers list
107 release bucket and return, if that fails */ 107 release bucket and return, if that fails */
108 if (!(r = iovec_fix_increase_or_free(iovec_entries, iovector, r, OT_SYNC_CHUNK_SIZE))) 108 if (!(r = iovec_next_chunk(iovec_entries, iovector, r, OT_SYNC_CHUNK_SIZE)))
109 return mutex_bucket_unlock(bucket); 109 return mutex_bucket_unlock(bucket);
110 110
111 /* Adjust new end of output buffer */ 111 /* Adjust new end of output buffer */
@@ -131,7 +131,7 @@ static void sync_make(size_t *iovec_entries, struct iovec **iovector) {
131 *r++ = 'e'; 131 *r++ = 'e';
132 132
133 /* Release unused memory in current output buffer */ 133 /* Release unused memory in current output buffer */
134 iovec_fixlast(iovec_entries, iovector, r); 134 iovec_trim_last(iovec_entries, iovector, r);
135} 135}
136 136
137/* This is the entry point into this worker thread 137/* This is the entry point into this worker thread
diff --git a/scan_urlencoded_query.c b/ot_urlencoded.c
index 4825a4a..1ca6e81 100644
--- a/scan_urlencoded_query.c
+++ b/ot_urlencoded.c
@@ -4,7 +4,7 @@
4 $id$ */ 4 $id$ */
5 5
6/* Opentracker */ 6/* Opentracker */
7#include "scan_urlencoded_query.h" 7#include "ot_urlencoded.h"
8 8
9/* System */ 9/* System */
10#include <string.h> 10#include <string.h>
@@ -53,7 +53,7 @@ static unsigned char fromhex(unsigned char x) {
53} 53}
54 54
55/* Skip the value of a param=value pair */ 55/* Skip the value of a param=value pair */
56void scan_urlencoded_skipvalue(char **string) { 56void urlencoded_skip_value(char **string) {
57 const unsigned char *s = *(const unsigned char **)string; 57 const unsigned char *s = *(const unsigned char **)string;
58 unsigned char f; 58 unsigned char f;
59 59
@@ -63,16 +63,16 @@ void scan_urlencoded_skipvalue(char **string) {
63 ; 63 ;
64 64
65 /* If we stopped at a hard terminator like \0 or \n, make the 65 /* If we stopped at a hard terminator like \0 or \n, make the
66 next scan_urlencoded_query encounter it again */ 66 next urlencoded_parse encounter it again */
67 if (f & SCAN_SEARCHPATH_TERMINATOR) 67 if (f & SCAN_SEARCHPATH_TERMINATOR)
68 --s; 68 --s;
69 69
70 *string = (char *)s; 70 *string = (char *)s;
71} 71}
72 72
73int scan_find_keywords(const ot_keywords *keywords, char **string, SCAN_SEARCHPATH_FLAG flags) { 73int urlencoded_match_keyword(const ot_keywords *keywords, char **string, SCAN_SEARCHPATH_FLAG flags) {
74 char *deststring = *string; 74 char *deststring = *string;
75 ssize_t match_length = scan_urlencoded_query(string, deststring, flags); 75 ssize_t match_length = urlencoded_parse(string, deststring, flags);
76 76
77 if (match_length < 0) 77 if (match_length < 0)
78 return match_length; 78 return match_length;
@@ -88,7 +88,7 @@ int scan_find_keywords(const ot_keywords *keywords, char **string, SCAN_SEARCHPA
88 return -3; 88 return -3;
89} 89}
90 90
91ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags) { 91ssize_t urlencoded_parse(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags) {
92 const unsigned char *s = *(const unsigned char **)string; 92 const unsigned char *s = *(const unsigned char **)string;
93 unsigned char *d = (unsigned char *)deststring; 93 unsigned char *d = (unsigned char *)deststring;
94 unsigned char b, c; 94 unsigned char b, c;
@@ -121,7 +121,7 @@ ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_F
121 if (d == (unsigned char *)deststring) 121 if (d == (unsigned char *)deststring)
122 return -2; 122 return -2;
123 123
124 /* Else make the next call to scan_urlencoded_param encounter it again */ 124 /* Else make the next call to urlencoded_parse encounter it again */
125 --s; 125 --s;
126 break; 126 break;
127 case '?': 127 case '?':
@@ -160,7 +160,7 @@ ssize_t scan_fixed_int(char *data, size_t len, int *tmp) {
160 return len; 160 return len;
161} 161}
162 162
163size_t fmt_urlencoded(char *dest, const uint8_t *src, size_t len) { 163size_t urlencoded_format(char *dest, const uint8_t *src, size_t len) {
164 static const char safe[] = "$/.=~-_"; 164 static const char safe[] = "$/.=~-_";
165 static const char hexdigit[] = "0123456789abcdef"; 165 static const char hexdigit[] = "0123456789abcdef";
166 size_t written = 0, i; 166 size_t written = 0, i;
diff --git a/scan_urlencoded_query.h b/ot_urlencoded.h
index f71752d..8147370 100644
--- a/scan_urlencoded_query.h
+++ b/ot_urlencoded.h
@@ -30,7 +30,7 @@ typedef enum {
30 or -1 for parse error 30 or -1 for parse error
31 or -2 for terminator found 31 or -2 for terminator found
32*/ 32*/
33ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags); 33ssize_t urlencoded_parse(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags);
34 34
35/* string in: pointer to source 35/* string in: pointer to source
36 out: pointer to next scan position 36 out: pointer to next scan position
@@ -40,12 +40,12 @@ ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_F
40 or -2 for terminator found 40 or -2 for terminator found
41 or -3 for no keyword matched 41 or -3 for no keyword matched
42 */ 42 */
43int scan_find_keywords(const ot_keywords *keywords, char **string, SCAN_SEARCHPATH_FLAG flags); 43int urlencoded_match_keyword(const ot_keywords *keywords, char **string, SCAN_SEARCHPATH_FLAG flags);
44 44
45/* string in: pointer to value of a param=value pair to skip 45/* string in: pointer to value of a param=value pair to skip
46 out: pointer to next scan position on return 46 out: pointer to next scan position on return
47*/ 47*/
48void scan_urlencoded_skipvalue(char **string); 48void urlencoded_skip_value(char **string);
49 49
50/* data pointer to len chars of string 50/* data pointer to len chars of string
51 len length of chars in data to parse 51 len length of chars in data to parse
@@ -56,6 +56,6 @@ ssize_t scan_fixed_int(char *data, size_t len, int *number);
56 56
57/* The other direction: percent encode len bytes from src into dest and return 57/* The other direction: percent encode len bytes from src into dest and return
58 how many characters were written. dest needs room for three times len. */ 58 how many characters were written. dest needs room for three times len. */
59size_t fmt_urlencoded(char *dest, const uint8_t *src, size_t len); 59size_t urlencoded_format(char *dest, const uint8_t *src, size_t len);
60 60
61#endif 61#endif
diff --git a/ot_vector.c b/ot_vector.c
index 7f18246..924d468 100644
--- a/ot_vector.c
+++ b/ot_vector.c
@@ -19,7 +19,7 @@ static int vector_compare_peer4(const void *peer1, const void *peer2) { return m
19 no exact match is found. In that case it sets exactmatch 0 and gives 19 no exact match is found. In that case it sets exactmatch 0 and gives
20 calling functions the chance to insert data 20 calling functions the chance to insert data
21*/ 21*/
22void *binary_search(const void *const key, const void *base, const size_t member_count, const size_t member_size, size_t compare_size, int *exactmatch) { 22void *vector_binary_search(const void *const key, const void *base, const size_t member_count, const size_t member_size, size_t compare_size, int *exactmatch) {
23 size_t interval = member_count; 23 size_t interval = member_count;
24 24
25 while (interval) { 25 while (interval) {
@@ -50,13 +50,13 @@ static uint8_t vector_hash_peer(ot_peer const *peer, size_t compare_size, size_t
50 50
51/* This is the generic insert operation for our vector type. 51/* This is the generic insert operation for our vector type.
52 It tries to locate the object at "key" with size "member_size" by comparing its first "compare_size" bytes with 52 It tries to locate the object at "key" with size "member_size" by comparing its first "compare_size" bytes with
53 those of objects in vector. Our special "binary_search" function does that and either returns the match or a 53 those of objects in vector. Our special "vector_binary_search" function does that and either returns the match or a
54 pointer to where the object is to be inserted. vector_find_or_insert makes space for the object and copies it, 54 pointer to where the object is to be inserted. vector_find_or_insert makes space for the object and copies it,
55 if it wasn't found in vector. Caller needs to check the passed "exactmatch" variable to see, whether an insert 55 if it wasn't found in vector. Caller needs to check the passed "exactmatch" variable to see, whether an insert
56 took place. If resizing the vector failed, NULL is returned, else the pointer to the object in vector. 56 took place. If resizing the vector failed, NULL is returned, else the pointer to the object in vector.
57*/ 57*/
58void *vector_find_or_insert(ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch) { 58void *vector_find_or_insert(ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch) {
59 uint8_t *match = binary_search(key, vector->data, vector->size, member_size, compare_size, exactmatch); 59 uint8_t *match = vector_binary_search(key, vector->data, vector->size, member_size, compare_size, exactmatch);
60 60
61 if (*exactmatch) 61 if (*exactmatch)
62 return match; 62 return match;
@@ -89,7 +89,7 @@ ot_peer *vector_find_or_insert_peer(ot_vector *vector, ot_peer const *peer, size
89 /* If space is zero but size is set, we're dealing with a list of vector->size buckets */ 89 /* If space is zero but size is set, we're dealing with a list of vector->size buckets */
90 if (vector->space < vector->size) 90 if (vector->space < vector->size)
91 vector = ((ot_vector *)vector->data) + vector_hash_peer(peer, compare_size, vector->size); 91 vector = ((ot_vector *)vector->data) + vector_hash_peer(peer, compare_size, vector->size);
92 match = binary_search(peer, vector->data, vector->size, peer_size, compare_size, exactmatch); 92 match = vector_binary_search(peer, vector->data, vector->size, peer_size, compare_size, exactmatch);
93 93
94 if (*exactmatch) 94 if (*exactmatch)
95 return match; 95 return match;
@@ -137,7 +137,7 @@ int vector_remove_peer(ot_vector *vector, ot_peer const *peer, size_t peer_size)
137 vector = ((ot_vector *)vector->data) + vector_hash_peer(peer, compare_size, vector->size); 137 vector = ((ot_vector *)vector->data) + vector_hash_peer(peer, compare_size, vector->size);
138 138
139 end = ((ot_peer *)vector->data) + peer_size * vector->size; 139 end = ((ot_peer *)vector->data) + peer_size * vector->size;
140 match = (ot_peer *)binary_search(peer, vector->data, vector->size, peer_size, compare_size, &exactmatch); 140 match = (ot_peer *)vector_binary_search(peer, vector->data, vector->size, peer_size, compare_size, &exactmatch);
141 if (!exactmatch) 141 if (!exactmatch)
142 return 0; 142 return 0;
143 143
diff --git a/ot_vector.h b/ot_vector.h
index fbc75f4..d553956 100644
--- a/ot_vector.h
+++ b/ot_vector.h
@@ -26,7 +26,7 @@ typedef struct {
26/* trackerlogic.h needs ot_vector, break circular dependency here */ 26/* trackerlogic.h needs ot_vector, break circular dependency here */
27#include "trackerlogic.h" 27#include "trackerlogic.h"
28 28
29void *binary_search(const void *const key, const void *base, const size_t member_count, const size_t member_size, size_t compare_size, int *exactmatch); 29void *vector_binary_search(const void *const key, const void *base, const size_t member_count, const size_t member_size, size_t compare_size, int *exactmatch);
30void *vector_find_or_insert(ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch); 30void *vector_find_or_insert(ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch);
31ot_peer *vector_find_or_insert_peer(ot_vector *vector, ot_peer const *peer, size_t peer_size, int *exactmatch); 31ot_peer *vector_find_or_insert_peer(ot_vector *vector, ot_peer const *peer, size_t peer_size, int *exactmatch);
32 32
diff --git a/proxy.c b/proxy.c
index 63b4c4f..0cac20c 100644
--- a/proxy.c
+++ b/proxy.c
@@ -19,6 +19,7 @@
19 19
20/* Opentracker */ 20/* Opentracker */
21#include "ot_batch.h" 21#include "ot_batch.h"
22#include "ot_fdstate.h"
22#include "ot_ip.h" 23#include "ot_ip.h"
23#include "ot_mutex.h" 24#include "ot_mutex.h"
24#include "ot_random.h" 25#include "ot_random.h"
@@ -148,7 +149,7 @@ size_t add_peer_to_torrent_proxy(ot_hash hash, ot_peer *peer, size_t peer_size)
148size_t remove_peer_from_torrent_proxy(ot_hash hash, ot_peer *peer, size_t peer_size) { 149size_t remove_peer_from_torrent_proxy(ot_hash hash, ot_peer *peer, size_t peer_size) {
149 int exactmatch; 150 int exactmatch;
150 ot_vector *torrents_list = mutex_bucket_lock_by_hash(hash); 151 ot_vector *torrents_list = mutex_bucket_lock_by_hash(hash);
151 ot_torrent *torrent = binary_search(hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch); 152 ot_torrent *torrent = vector_binary_search(hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch);
152 153
153 if (exactmatch) { 154 if (exactmatch) {
154 ot_peerlist *peer_list = peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4; 155 ot_peerlist *peer_list = peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4;
@@ -267,7 +268,7 @@ static void handle_reconnects(void) {
267 continue; /* No socket for you */ 268 continue; /* No socket for you */
268 io_fd(newfd); 269 io_fd(newfd);
269 io_wantwrite(newfd); /* So we will be informed when it is connected */ 270 io_wantwrite(newfd); /* So we will be informed when it is connected */
270 io_setcookie(newfd, g_connections + i); 271 fdstate_open(newfd, g_connections + i, 0);
271 272
272 /* Prepare connection info block */ 273 /* Prepare connection info block */
273 reset_info_block(g_connections + i); 274 reset_info_block(g_connections + i);
@@ -310,7 +311,7 @@ static void handle_accept(int64_t serversocket) {
310 g_connections[i].port = port; 311 g_connections[i].port = port;
311 g_connections[i].fd = newfd; 312 g_connections[i].fd = newfd;
312 313
313 io_setcookie(newfd, g_connections + i); 314 fdstate_open(newfd, g_connections + i, 0);
314 315
315 /* We expect the connecting side to begin with its tracker_id */ 316 /* We expect the connecting side to begin with its tracker_id */
316 io_wantread(newfd); 317 io_wantread(newfd);
@@ -325,7 +326,7 @@ static void handle_read(int64_t peersocket) {
325 int i; 326 int i;
326 int64_t datalen; 327 int64_t datalen;
327 uint32_t tracker_id; 328 uint32_t tracker_id;
328 proxy_peer *peer = io_getcookie(peersocket); 329 proxy_peer *peer = fdstate_cookie(peersocket);
329 330
330 if (!peer) { 331 if (!peer) {
331 /* Can't happen ;) */ 332 /* Can't happen ;) */
@@ -391,7 +392,7 @@ static void handle_read(int64_t peersocket) {
391 392
392/* Can write new sync data to the stream */ 393/* Can write new sync data to the stream */
393static void handle_write(int64_t peersocket) { 394static void handle_write(int64_t peersocket) {
394 proxy_peer *peer = io_getcookie(peersocket); 395 proxy_peer *peer = fdstate_cookie(peersocket);
395 396
396 if (!peer) { 397 if (!peer) {
397 /* Can't happen ;) */ 398 /* Can't happen ;) */
@@ -464,7 +465,7 @@ static void server_mainloop() {
464 465
465 /* Loop over readable sockets */ 466 /* Loop over readable sockets */
466 while ((sock = io_canread()) != -1) { 467 while ((sock = io_canread()) != -1) {
467 const void *cookie = io_getcookie(sock); 468 const void *cookie = fdstate_cookie(sock);
468 if ((uintptr_t)cookie == FLAG_SERVERSOCKET) 469 if ((uintptr_t)cookie == FLAG_SERVERSOCKET)
469 handle_accept(sock); 470 handle_accept(sock);
470 else 471 else
@@ -493,7 +494,7 @@ static int64_t ot_try_bind(ot_ip6 ip, uint16_t port) {
493 if (!io_fd(sock)) 494 if (!io_fd(sock))
494 panic("io_fd"); 495 panic("io_fd");
495 496
496 io_setcookie(sock, (void *)FLAG_SERVERSOCKET); 497 fdstate_open(sock, (void *)FLAG_SERVERSOCKET, 0);
497 io_wantread(sock); 498 io_wantread(sock);
498 return sock; 499 return sock;
499} 500}
@@ -535,6 +536,8 @@ int main(int argc, char **argv) {
535 int scanon = 1, lbound = 0, sbound = 0; 536 int scanon = 1, lbound = 0, sbound = 0;
536 537
537 random_init(); 538 random_init();
539 if (fdstate_init() == -1)
540 panic("fdstate_init");
538 g_tracker_id = random_uint32(); 541 g_tracker_id = random_uint32();
539 542
540 while (scanon) { 543 while (scanon) {
diff --git a/tests/runner.c b/tests/runner.c
index 148b928..9e5c99f 100644
--- a/tests/runner.c
+++ b/tests/runner.c
@@ -14,7 +14,7 @@ struct module {
14 void (*run)(void); 14 void (*run)(void);
15}; 15};
16 16
17static const struct module modules[] = {{"scan_urlencoded_query", test_scan_urlencoded_query}, 17static const struct module modules[] = {{"ot_urlencoded", test_urlencoded},
18 {"ot_iovec", test_iovec}, 18 {"ot_iovec", test_iovec},
19 {"ot_vector", test_vector}, 19 {"ot_vector", test_vector},
20 {"ot_random", test_random}, 20 {"ot_random", test_random},
@@ -25,6 +25,7 @@ static const struct module modules[] = {{"scan_urlencoded_query", test_scan_urle
25 {"ot_socket", test_socket}, 25 {"ot_socket", test_socket},
26 {"ot_mutex", test_mutex}, 26 {"ot_mutex", test_mutex},
27 {"ot_batch", test_batch}, 27 {"ot_batch", test_batch},
28 {"ot_fdstate", test_fdstate},
28 {"ot_accesslist", test_accesslist}, 29 {"ot_accesslist", test_accesslist},
29 {NULL, NULL}}; 30 {NULL, NULL}};
30 31
diff --git a/tests/t_fdstate.c b/tests/t_fdstate.c
new file mode 100644
index 0000000..bf8724a
--- /dev/null
+++ b/tests/t_fdstate.c
@@ -0,0 +1,224 @@
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 <pthread.h>
8#include <stdatomic.h>
9#include <stdint.h>
10#include <stdlib.h>
11#include <string.h>
12
13/* Opentracker */
14#include "ot_fdstate.h"
15#include "test.h"
16
17/* ------------------------------------------------------- one thread first */
18
19static void case_open_claim_release(void) {
20 char cookie[4];
21 uint32_t generation;
22
23 test_case("a descriptor taken into the table can be claimed and gives back its cookie");
24 generation = fdstate_open(7, cookie, 0);
25 CHECK_INT(fdstate_claim(7, generation), 1);
26 CHECK_PTR(fdstate_cookie(7), cookie);
27 fdstate_release(7);
28
29 test_case("and can be claimed again once released");
30 CHECK_INT(fdstate_claim(7, generation), 1);
31 fdstate_release(7);
32 fdstate_close(7);
33}
34
35static void case_one_holder_at_a_time(void) {
36 uint32_t generation = fdstate_open(8, NULL, 0);
37
38 test_case("a descriptor somebody holds cannot be claimed again");
39 CHECK_INT(fdstate_claim(8, generation), 1);
40 CHECK_INT(fdstate_claim(8, generation), 0);
41 fdstate_release(8);
42 CHECK_INT(fdstate_claim(8, generation), 1);
43 fdstate_release(8);
44 fdstate_close(8);
45}
46
47static void case_stale_generation_is_refused(void) {
48 char first[4], second[4];
49 uint32_t old, new;
50
51 test_case("a caller quoting the generation of a closed descriptor is turned away");
52 old = fdstate_open(9, first, 0);
53 fdstate_close(9);
54 new = fdstate_open(9, second, 0);
55 CHECK(old != new);
56 CHECK_INT(fdstate_claim(9, old), 0);
57
58 test_case("while the one quoting the new generation gets the new cookie");
59 CHECK_INT(fdstate_claim(9, new), 1);
60 CHECK_PTR(fdstate_cookie(9), second);
61 fdstate_release(9);
62 fdstate_close(9);
63}
64
65static void case_expiry(void) {
66 uint32_t generation = fdstate_open(10, NULL, 100);
67
68 test_case("a descriptor past its expiry is collected");
69 CHECK_INT(fdstate_claim_expired(10, 99), 0);
70 CHECK_INT(fdstate_claim_expired(10, 100), 1);
71 fdstate_close(10);
72
73 test_case("one that never expires is never collected");
74 generation = fdstate_open(10, NULL, 0);
75 CHECK_INT(fdstate_claim_expired(10, 1 << 30), 0);
76
77 test_case("and one somebody is working on is left alone");
78 fdstate_set_expiry(10, 1);
79 CHECK_INT(fdstate_claim(10, generation), 1);
80 CHECK_INT(fdstate_claim_expired(10, 1 << 30), 0);
81 fdstate_release(10);
82 CHECK_INT(fdstate_claim_expired(10, 1 << 30), 1);
83 fdstate_close(10);
84}
85
86static void case_out_of_range(void) {
87 test_case("a descriptor outside the table is refused rather than trusted");
88 CHECK_INT(fdstate_claim(-1, 0), 0);
89 CHECK_INT(fdstate_claim((int)fdstate_slots(), 0), 0);
90 CHECK_INT(fdstate_claim_expired(-1, 0), 0);
91 CHECK_PTR(fdstate_cookie(-1), NULL);
92}
93
94/* --------------------------------------------------------- then in anger */
95
96/* The race this is about only exists while a descriptor is being recycled,
97 so recycling is driven by work done rather than by a clock: every slot is
98 given up after RECYCLE_AFTER claims, whatever the machine's speed. */
99
100#define STRESS_SLOTS 64
101#define RECYCLE_AFTER 32
102#define STRESS_CLAIMS 200000
103#define STRESS_GIVEUP (STRESS_CLAIMS * 100)
104
105static _Atomic long stress_claims, stress_recycles, stress_violations, stress_attempts;
106static _Atomic int stress_lives[STRESS_SLOTS];
107static _Atomic int stress_live[STRESS_SLOTS];
108
109/* Generation and cookie are published in one word so a worker can never read
110 a cookie that belongs to a different generation than the one it claimed
111 with. Each connection gets a cookie nobody else ever gets. */
112static _Atomic uint64_t stress_conn[STRESS_SLOTS];
113static _Atomic uint64_t stress_next_cookie;
114
115#define STRESS_CONN(cookie, gen) (((uint64_t)(cookie) << 32) | (uint32_t)(gen))
116#define STRESS_CONN_COOKIE(w) ((void *)(uintptr_t)((w) >> 32))
117#define STRESS_CONN_GEN(w) ((uint32_t)(w))
118
119static int stress_running(void) {
120 return atomic_load(&stress_claims) < STRESS_CLAIMS && atomic_load(&stress_attempts) < STRESS_GIVEUP;
121}
122
123static void *stress_worker(void *arg) {
124 unsigned seed = (unsigned)(uintptr_t)arg;
125
126 while (stress_running()) {
127 int fd = (int)(rand_r(&seed) % STRESS_SLOTS);
128 uint64_t conn = atomic_load(&stress_conn[fd]);
129
130 atomic_fetch_add(&stress_attempts, 1);
131 if (!fdstate_claim(fd, STRESS_CONN_GEN(conn)))
132 continue;
133
134 if (fdstate_cookie(fd) != STRESS_CONN_COOKIE(conn))
135 atomic_fetch_add(&stress_violations, 1);
136 if (atomic_fetch_sub(&stress_lives[fd], 1) <= 1)
137 fdstate_set_expiry(fd, 1); /* retired by its holder, as a handler would */
138 if (fdstate_cookie(fd) != STRESS_CONN_COOKIE(conn))
139 atomic_fetch_add(&stress_violations, 1);
140
141 fdstate_release(fd);
142 atomic_fetch_add(&stress_claims, 1);
143 }
144 return NULL;
145}
146
147static void *stress_keeper(void *arg) {
148 (void)arg;
149 while (stress_running()) {
150 int fd;
151
152 for (fd = 0; fd < STRESS_SLOTS; ++fd) {
153 if (atomic_load(&stress_live[fd])) {
154 if (!fdstate_claim_expired(fd, 1))
155 continue; /* a worker has it, come back */
156 fdstate_close(fd);
157 atomic_store(&stress_live[fd], 0);
158 atomic_fetch_add(&stress_recycles, 1);
159 }
160 {
161 void *cookie = (void *)(uintptr_t)(atomic_fetch_add(&stress_next_cookie, 1) + 1);
162 uint32_t gen = fdstate_open(fd, cookie, 0);
163
164 atomic_store(&stress_lives[fd], RECYCLE_AFTER);
165 atomic_store(&stress_conn[fd], STRESS_CONN((uintptr_t)cookie, gen));
166 atomic_store(&stress_live[fd], 1);
167 }
168 }
169 }
170 return NULL;
171}
172
173static void case_recycling_under_load(void) {
174 pthread_t worker[4], keeper;
175 size_t i;
176
177 test_case("no worker is ever handed a connection from another generation");
178 atomic_store(&stress_claims, 0);
179 atomic_store(&stress_recycles, 0);
180 atomic_store(&stress_violations, 0);
181 atomic_store(&stress_attempts, 0);
182 atomic_store(&stress_next_cookie, 0);
183 /* Every slot is opened and published before any worker runs: a worker only
184 ever learns of a descriptor from whoever registered it, and never
185 invents a generation of its own. */
186 for (i = 0; i < STRESS_SLOTS; ++i) {
187 void *cookie = (void *)(uintptr_t)(atomic_fetch_add(&stress_next_cookie, 1) + 1);
188 uint32_t gen = fdstate_open((int)i, cookie, 0);
189
190 atomic_store(&stress_lives[i], RECYCLE_AFTER);
191 atomic_store(&stress_conn[i], STRESS_CONN((uintptr_t)cookie, gen));
192 atomic_store(&stress_live[i], 1);
193 }
194
195 pthread_create(&keeper, NULL, stress_keeper, NULL);
196 for (i = 0; i < sizeof(worker) / sizeof(worker[0]); ++i)
197 pthread_create(&worker[i], NULL, stress_worker, (void *)(uintptr_t)(i + 1));
198 for (i = 0; i < sizeof(worker) / sizeof(worker[0]); ++i)
199 pthread_join(worker[i], NULL);
200 pthread_join(keeper, NULL);
201
202 CHECK_INT(atomic_load(&stress_violations), 0);
203
204 test_case("and the run actually recycled descriptors, or it proved nothing");
205 CHECK(atomic_load(&stress_claims) >= STRESS_CLAIMS);
206 CHECK(atomic_load(&stress_recycles) > STRESS_CLAIMS / (RECYCLE_AFTER * 4));
207
208 for (i = 0; i < STRESS_SLOTS; ++i)
209 fdstate_close((int)i);
210}
211
212void test_fdstate(void) {
213 CHECK_INT(fdstate_init(), 0);
214 CHECK(fdstate_slots() > STRESS_SLOTS);
215
216 case_open_claim_release();
217 case_one_holder_at_a_time();
218 case_stale_generation_is_refused();
219 case_expiry();
220 case_out_of_range();
221 case_recycling_under_load();
222
223 fdstate_deinit();
224}
diff --git a/tests/t_hex.c b/tests/t_hex.c
index 7cce8ac..a761737 100644
--- a/tests/t_hex.c
+++ b/tests/t_hex.c
@@ -18,7 +18,7 @@ static void case_encode(void) {
18 18
19 test_case("a hash encodes to forty upper case digits and a terminator"); 19 test_case("a hash encodes to forty upper case digits and a terminator");
20 memset(dest, 0x7f, sizeof(dest)); 20 memset(dest, 0x7f, sizeof(dest));
21 CHECK_PTR(hex_fromhash(dest, sample), dest); 21 CHECK_PTR(hex_from_hash(dest, sample), dest);
22 CHECK_STR(dest, "000123456789ABCDEFFF10203040506070809" "0A0"); 22 CHECK_STR(dest, "000123456789ABCDEFFF10203040506070809" "0A0");
23 CHECK_INT(strlen(dest), 40); 23 CHECK_INT(strlen(dest), 40);
24 CHECK_INT((unsigned char)dest[41], 0x7f); 24 CHECK_INT((unsigned char)dest[41], 0x7f);
@@ -28,7 +28,7 @@ static void case_decode(void) {
28 ot_hash hash; 28 ot_hash hash;
29 29
30 test_case("forty digits decode back to the hash"); 30 test_case("forty digits decode back to the hash");
31 CHECK(hex_tohash(hash, "000123456789ABCDEFFF10203040506070809" "0A0")); 31 CHECK(hex_to_hash(hash, "000123456789ABCDEFFF10203040506070809" "0A0"));
32 CHECK_MEM(hash, sample, sizeof(sample)); 32 CHECK_MEM(hash, sample, sizeof(sample));
33} 33}
34 34
@@ -41,8 +41,8 @@ static void case_round_trip(void) {
41 for (i = 0; i < 256; ++i) { 41 for (i = 0; i < 256; ++i) {
42 uint8_t probe[20]; 42 uint8_t probe[20];
43 memset(probe, (uint8_t)i, sizeof(probe)); 43 memset(probe, (uint8_t)i, sizeof(probe));
44 hex_fromhash(text, probe); 44 hex_from_hash(text, probe);
45 if (!hex_tohash(hash, text) || memcmp(hash, probe, sizeof(probe))) { 45 if (!hex_to_hash(hash, text) || memcmp(hash, probe, sizeof(probe))) {
46 test_failf(__FILE__, __LINE__, "round trip failed for byte value %zu", i); 46 test_failf(__FILE__, __LINE__, "round trip failed for byte value %zu", i);
47 return; 47 return;
48 } 48 }
@@ -54,8 +54,8 @@ static void case_decode_is_case_insensitive(void) {
54 ot_hash upper, lower; 54 ot_hash upper, lower;
55 55
56 test_case("lower case digits decode the same as upper case"); 56 test_case("lower case digits decode the same as upper case");
57 CHECK(hex_tohash(upper, "ABCDEF0123456789ABCDEF0123456789ABCDEF01")); 57 CHECK(hex_to_hash(upper, "ABCDEF0123456789ABCDEF0123456789ABCDEF01"));
58 CHECK(hex_tohash(lower, "abcdef0123456789abcdef0123456789abcdef01")); 58 CHECK(hex_to_hash(lower, "abcdef0123456789abcdef0123456789abcdef01"));
59 CHECK_MEM(lower, upper, sizeof(ot_hash)); 59 CHECK_MEM(lower, upper, sizeof(ot_hash));
60} 60}
61 61
@@ -63,18 +63,18 @@ static void case_rejects_non_digits(void) {
63 ot_hash hash; 63 ot_hash hash;
64 64
65 test_case("a non hex character anywhere rejects the whole string"); 65 test_case("a non hex character anywhere rejects the whole string");
66 CHECK_INT(hex_tohash(hash, "g00123456789ABCDEFFF102030405060708090A0"), 0); 66 CHECK_INT(hex_to_hash(hash, "g00123456789ABCDEFFF102030405060708090A0"), 0);
67 CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF102030405060708090Ag"), 0); 67 CHECK_INT(hex_to_hash(hash, "000123456789ABCDEFFF102030405060708090Ag"), 0);
68 CHECK_INT(hex_tohash(hash, "000123456789ABCDEF:F102030405060708090A"), 0); 68 CHECK_INT(hex_to_hash(hash, "000123456789ABCDEF:F102030405060708090A"), 0);
69} 69}
70 70
71static void case_rejects_short_input(void) { 71static void case_rejects_short_input(void) {
72 ot_hash hash; 72 ot_hash hash;
73 73
74 test_case("a string shorter than forty digits is rejected"); 74 test_case("a string shorter than forty digits is rejected");
75 CHECK_INT(hex_tohash(hash, ""), 0); 75 CHECK_INT(hex_to_hash(hash, ""), 0);
76 CHECK_INT(hex_tohash(hash, "00"), 0); 76 CHECK_INT(hex_to_hash(hash, "00"), 0);
77 CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF102030405060708090A"), 0); 77 CHECK_INT(hex_to_hash(hash, "000123456789ABCDEFFF102030405060708090A"), 0);
78} 78}
79 79
80static void case_leaves_hash_untouched_on_failure(void) { 80static void case_leaves_hash_untouched_on_failure(void) {
@@ -82,7 +82,7 @@ static void case_leaves_hash_untouched_on_failure(void) {
82 82
83 test_case("a rejected string leaves the destination untouched"); 83 test_case("a rejected string leaves the destination untouched");
84 memset(hash, 0x5a, sizeof(hash)); 84 memset(hash, 0x5a, sizeof(hash));
85 CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF10203040506070809XX0"), 0); 85 CHECK_INT(hex_to_hash(hash, "000123456789ABCDEFFF10203040506070809XX0"), 0);
86 CHECK_MEM(hash, "\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a", sizeof(hash)); 86 CHECK_MEM(hash, "\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a", sizeof(hash));
87} 87}
88 88
@@ -96,7 +96,7 @@ static void case_stops_at_the_terminator(void) {
96 memcpy(guarded, "0012", 4); 96 memcpy(guarded, "0012", 4);
97 guarded[4] = 0; 97 guarded[4] = 0;
98 memset(guarded + 5, 'f', 3); 98 memset(guarded + 5, 'f', 3);
99 CHECK_INT(hex_tohash(hash, guarded), 0); 99 CHECK_INT(hex_to_hash(hash, guarded), 0);
100} 100}
101 101
102static void case_isdigit(void) { 102static void case_isdigit(void) {
diff --git a/tests/t_iovec.c b/tests/t_iovec.c
index 89f3fda..2553141 100644
--- a/tests/t_iovec.c
+++ b/tests/t_iovec.c
@@ -18,13 +18,13 @@ static void case_increase_and_length(void) {
18 void *first, *second; 18 void *first, *second;
19 19
20 test_case("increase appends an entry and length is their sum"); 20 test_case("increase appends an entry and length is their sum");
21 first = iovec_increase(&entries, &vector, 100); 21 first = iovec_new_chunk(&entries, &vector, 100);
22 CHECK(first != NULL); 22 CHECK(first != NULL);
23 CHECK_INT(entries, 1); 23 CHECK_INT(entries, 1);
24 CHECK_INT(vector[0].iov_len, 100); 24 CHECK_INT(vector[0].iov_len, 100);
25 CHECK_PTR(vector[0].iov_base, first); 25 CHECK_PTR(vector[0].iov_base, first);
26 26
27 second = iovec_increase(&entries, &vector, 50); 27 second = iovec_new_chunk(&entries, &vector, 50);
28 CHECK(second != NULL); 28 CHECK(second != NULL);
29 CHECK_INT(entries, 2); 29 CHECK_INT(entries, 2);
30 CHECK_INT(vector[1].iov_len, 50); 30 CHECK_INT(vector[1].iov_len, 50);
@@ -38,8 +38,8 @@ static void case_free_resets(void) {
38 struct iovec *vector = NULL; 38 struct iovec *vector = NULL;
39 39
40 test_case("free releases the entries and resets the vector"); 40 test_case("free releases the entries and resets the vector");
41 CHECK(iovec_increase(&entries, &vector, 10) != NULL); 41 CHECK(iovec_new_chunk(&entries, &vector, 10) != NULL);
42 CHECK(iovec_increase(&entries, &vector, 10) != NULL); 42 CHECK(iovec_new_chunk(&entries, &vector, 10) != NULL);
43 iovec_free(&entries, &vector); 43 iovec_free(&entries, &vector);
44 CHECK_INT(entries, 0); 44 CHECK_INT(entries, 0);
45 CHECK_PTR(vector, NULL); 45 CHECK_PTR(vector, NULL);
@@ -52,12 +52,12 @@ static void case_fixlast_shrinks(void) {
52 char *block; 52 char *block;
53 53
54 test_case("fixlast trims the last entry to the bytes actually used"); 54 test_case("fixlast trims the last entry to the bytes actually used");
55 CHECK(iovec_increase(&entries, &vector, 64) != NULL); 55 CHECK(iovec_new_chunk(&entries, &vector, 64) != NULL);
56 block = iovec_increase(&entries, &vector, 1000); 56 block = iovec_new_chunk(&entries, &vector, 1000);
57 CHECK(block != NULL); 57 CHECK(block != NULL);
58 memset(block, 'x', 20); 58 memset(block, 'x', 20);
59 59
60 iovec_fixlast(&entries, &vector, block + 20); 60 iovec_trim_last(&entries, &vector, block + 20);
61 CHECK_INT(entries, 2); 61 CHECK_INT(entries, 2);
62 CHECK_INT(vector[1].iov_len, 20); 62 CHECK_INT(vector[1].iov_len, 20);
63 CHECK_INT(iovec_length(&entries, (const struct iovec **)&vector), 84); 63 CHECK_INT(iovec_length(&entries, (const struct iovec **)&vector), 84);
@@ -71,7 +71,7 @@ static void case_fixlast_on_empty(void) {
71 struct iovec *vector = NULL; 71 struct iovec *vector = NULL;
72 72
73 test_case("fixlast on an empty vector does nothing"); 73 test_case("fixlast on an empty vector does nothing");
74 iovec_fixlast(&entries, &vector, NULL); 74 iovec_trim_last(&entries, &vector, NULL);
75 CHECK_INT(entries, 0); 75 CHECK_INT(entries, 0);
76 CHECK_PTR(vector, NULL); 76 CHECK_PTR(vector, NULL);
77} 77}
@@ -103,9 +103,9 @@ static void case_fix_increase(void) {
103 char *block; 103 char *block;
104 104
105 test_case("fix_increase trims the previous entry and adds a new one"); 105 test_case("fix_increase trims the previous entry and adds a new one");
106 block = iovec_increase(&entries, &vector, 500); 106 block = iovec_new_chunk(&entries, &vector, 500);
107 CHECK(block != NULL); 107 CHECK(block != NULL);
108 block = iovec_fix_increase_or_free(&entries, &vector, block + 30, 200); 108 block = iovec_next_chunk(&entries, &vector, block + 30, 200);
109 CHECK(block != NULL); 109 CHECK(block != NULL);
110 CHECK_INT(entries, 2); 110 CHECK_INT(entries, 2);
111 CHECK_INT(vector[0].iov_len, 30); 111 CHECK_INT(vector[0].iov_len, 30);
diff --git a/tests/t_scan_urlencoded_query.c b/tests/t_urlencoded.c
index f660ea3..1239e6b 100644
--- a/tests/t_scan_urlencoded_query.c
+++ b/tests/t_urlencoded.c
@@ -7,7 +7,7 @@
7#include <string.h> 7#include <string.h>
8 8
9/* Opentracker */ 9/* Opentracker */
10#include "scan_urlencoded_query.h" 10#include "ot_urlencoded.h"
11#include "test.h" 11#include "test.h"
12 12
13/* The scanner returns the number of bytes written to the destination, or 13/* The scanner returns the number of bytes written to the destination, or
@@ -23,7 +23,7 @@ static void case_path(void) {
23 ssize_t n; 23 ssize_t n;
24 24
25 test_case("path stops at the question mark"); 25 test_case("path stops at the question mark");
26 n = scan_urlencoded_query(&p, dest, SCAN_PATH); 26 n = urlencoded_parse(&p, dest, SCAN_PATH);
27 CHECK_INT(n, 9); 27 CHECK_INT(n, 9);
28 dest[n] = 0; 28 dest[n] = 0;
29 CHECK_STR(dest, "/announce"); 29 CHECK_STR(dest, "/announce");
@@ -37,22 +37,22 @@ static void case_param_and_value(void) {
37 ssize_t n; 37 ssize_t n;
38 38
39 test_case("parameter stops at '=', value stops at '&'"); 39 test_case("parameter stops at '=', value stops at '&'");
40 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_PARAM); 40 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_PARAM);
41 CHECK_INT(n, 9); 41 CHECK_INT(n, 9);
42 dest[n] = 0; 42 dest[n] = 0;
43 CHECK_STR(dest, "info_hash"); 43 CHECK_STR(dest, "info_hash");
44 44
45 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); 45 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE);
46 CHECK_INT(n, 3); 46 CHECK_INT(n, 3);
47 dest[n] = 0; 47 dest[n] = 0;
48 CHECK_STR(dest, "abc"); 48 CHECK_STR(dest, "abc");
49 49
50 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_PARAM); 50 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_PARAM);
51 CHECK_INT(n, 4); 51 CHECK_INT(n, 4);
52 dest[n] = 0; 52 dest[n] = 0;
53 CHECK_STR(dest, "port"); 53 CHECK_STR(dest, "port");
54 54
55 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); 55 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE);
56 CHECK_INT(n, 2); 56 CHECK_INT(n, 2);
57 dest[n] = 0; 57 dest[n] = 0;
58 CHECK_STR(dest, "80"); 58 CHECK_STR(dest, "80");
@@ -65,7 +65,7 @@ static void case_percent_decoding(void) {
65 ssize_t n; 65 ssize_t n;
66 66
67 test_case("percent escapes decode"); 67 test_case("percent escapes decode");
68 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); 68 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE);
69 CHECK_INT(n, 3); 69 CHECK_INT(n, 3);
70 CHECK_MEM(dest, "ABC", 3); 70 CHECK_MEM(dest, "ABC", 3);
71} 71}
@@ -79,7 +79,7 @@ static void case_binary_info_hash(void) {
79 ssize_t n; 79 ssize_t n;
80 80
81 test_case("twenty byte binary value survives decoding"); 81 test_case("twenty byte binary value survives decoding");
82 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); 82 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE);
83 CHECK_INT(n, 20); 83 CHECK_INT(n, 20);
84 CHECK_MEM(dest, want, 20); 84 CHECK_MEM(dest, want, 20);
85} 85}
@@ -90,7 +90,7 @@ static void case_in_place(void) {
90 ssize_t n; 90 ssize_t n;
91 91
92 test_case("decoding in place over the source is safe"); 92 test_case("decoding in place over the source is safe");
93 n = scan_urlencoded_query(&p, query, SCAN_SEARCHPATH_VALUE); 93 n = urlencoded_parse(&p, query, SCAN_SEARCHPATH_VALUE);
94 CHECK_INT(n, 2); 94 CHECK_INT(n, 2);
95 CHECK_MEM(query, "AB", 2); 95 CHECK_MEM(query, "AB", 2);
96} 96}
@@ -102,7 +102,7 @@ static void case_bad_escape(void) {
102 ssize_t n; 102 ssize_t n;
103 103
104 test_case("a non hexadecimal escape is a parse error"); 104 test_case("a non hexadecimal escape is a parse error");
105 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); 105 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE);
106 CHECK_INT(n, -1); 106 CHECK_INT(n, -1);
107} 107}
108 108
@@ -113,7 +113,7 @@ static void case_truncated_escape(void) {
113 ssize_t n; 113 ssize_t n;
114 114
115 test_case("an escape cut short by the terminator is a parse error"); 115 test_case("an escape cut short by the terminator is a parse error");
116 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); 116 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE);
117 CHECK_INT(n, -1); 117 CHECK_INT(n, -1);
118} 118}
119 119
@@ -125,11 +125,11 @@ static void case_terminator(void) {
125 ssize_t n; 125 ssize_t n;
126 126
127 test_case("a hard terminator before any output reports end of input"); 127 test_case("a hard terminator before any output reports end of input");
128 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_PARAM); 128 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_PARAM);
129 CHECK_INT(n, -2); 129 CHECK_INT(n, -2);
130 130
131 p = space; 131 p = space;
132 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_PARAM); 132 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_PARAM);
133 CHECK_INT(n, -2); 133 CHECK_INT(n, -2);
134} 134}
135 135
@@ -140,10 +140,10 @@ static void case_terminator_after_output(void) {
140 ssize_t n; 140 ssize_t n;
141 141
142 test_case("a hard terminator after output is left for the next call"); 142 test_case("a hard terminator after output is left for the next call");
143 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); 143 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE);
144 CHECK_INT(n, 3); 144 CHECK_INT(n, 3);
145 CHECK_INT(*p, 0); 145 CHECK_INT(*p, 0);
146 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); 146 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE);
147 CHECK_INT(n, -2); 147 CHECK_INT(n, -2);
148} 148}
149 149
@@ -159,14 +159,14 @@ static void case_separators_are_contextual(void) {
159 ssize_t n; 159 ssize_t n;
160 160
161 test_case("a question mark is literal inside a value"); 161 test_case("a question mark is literal inside a value");
162 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); 162 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE);
163 CHECK_INT(n, 3); 163 CHECK_INT(n, 3);
164 dest[n] = 0; 164 dest[n] = 0;
165 CHECK_STR(dest, "a?b"); 165 CHECK_STR(dest, "a?b");
166 166
167 test_case("an equals sign is literal inside a value"); 167 test_case("an equals sign is literal inside a value");
168 p = equals; 168 p = equals;
169 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); 169 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE);
170 CHECK_INT(n, 3); 170 CHECK_INT(n, 3);
171 dest[n] = 0; 171 dest[n] = 0;
172 CHECK_STR(dest, "a=b"); 172 CHECK_STR(dest, "a=b");
@@ -179,7 +179,7 @@ static void case_ampersand_in_path(void) {
179 ssize_t n; 179 ssize_t n;
180 180
181 test_case("an ampersand in a path is a parse error"); 181 test_case("an ampersand in a path is a parse error");
182 n = scan_urlencoded_query(&p, dest, SCAN_PATH); 182 n = urlencoded_parse(&p, dest, SCAN_PATH);
183 CHECK_INT(n, -1); 183 CHECK_INT(n, -1);
184} 184}
185 185
@@ -190,7 +190,7 @@ static void case_raw_high_byte(void) {
190 ssize_t n; 190 ssize_t n;
191 191
192 test_case("an undecoded byte above ASCII is a parse error"); 192 test_case("an undecoded byte above ASCII is a parse error");
193 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); 193 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE);
194 CHECK_INT(n, -1); 194 CHECK_INT(n, -1);
195} 195}
196 196
@@ -200,12 +200,12 @@ static void case_skipvalue(void) {
200 char *p = query; 200 char *p = query;
201 201
202 test_case("skipvalue steps over the value and its separator"); 202 test_case("skipvalue steps over the value and its separator");
203 scan_urlencoded_skipvalue(&p); 203 urlencoded_skip_value(&p);
204 CHECK_STR(p, "next=1"); 204 CHECK_STR(p, "next=1");
205 205
206 test_case("skipvalue leaves a hard terminator in place"); 206 test_case("skipvalue leaves a hard terminator in place");
207 p = bare; 207 p = bare;
208 scan_urlencoded_skipvalue(&p); 208 urlencoded_skip_value(&p);
209 CHECK_INT(*p, 0); 209 CHECK_INT(*p, 0);
210} 210}
211 211
@@ -217,15 +217,15 @@ static void case_find_keywords(void) {
217 char *p = match; 217 char *p = match;
218 218
219 test_case("a known keyword returns its value"); 219 test_case("a known keyword returns its value");
220 CHECK_INT(scan_find_keywords(keywords, &p, SCAN_SEARCHPATH_PARAM), 2); 220 CHECK_INT(urlencoded_match_keyword(keywords, &p, SCAN_SEARCHPATH_PARAM), 2);
221 221
222 test_case("an unknown keyword returns -3"); 222 test_case("an unknown keyword returns -3");
223 p = nomatch; 223 p = nomatch;
224 CHECK_INT(scan_find_keywords(keywords, &p, SCAN_SEARCHPATH_PARAM), -3); 224 CHECK_INT(urlencoded_match_keyword(keywords, &p, SCAN_SEARCHPATH_PARAM), -3);
225 225
226 test_case("a prefix of a keyword does not match it"); 226 test_case("a prefix of a keyword does not match it");
227 p = prefix; 227 p = prefix;
228 CHECK_INT(scan_find_keywords(keywords, &p, SCAN_SEARCHPATH_PARAM), -3); 228 CHECK_INT(urlencoded_match_keyword(keywords, &p, SCAN_SEARCHPATH_PARAM), -3);
229} 229}
230 230
231static void case_fixed_int(void) { 231static void case_fixed_int(void) {
@@ -265,7 +265,7 @@ static void case_fmt_passes_safe_bytes(void) {
265 size_t n; 265 size_t n;
266 266
267 test_case("alphanumerics and the safe punctuation pass through"); 267 test_case("alphanumerics and the safe punctuation pass through");
268 n = fmt_urlencoded(dest, src, 11); 268 n = urlencoded_format(dest, src, 11);
269 dest[n] = 0; 269 dest[n] = 0;
270 CHECK_INT(n, 11); 270 CHECK_INT(n, 11);
271 CHECK_STR(dest, "aZ09$-./=_~"); 271 CHECK_STR(dest, "aZ09$-./=_~");
@@ -277,7 +277,7 @@ static void case_fmt_escapes_the_rest(void) {
277 size_t n; 277 size_t n;
278 278
279 test_case("everything else becomes a percent escape"); 279 test_case("everything else becomes a percent escape");
280 n = fmt_urlencoded(dest, src, sizeof(src)); 280 n = urlencoded_format(dest, src, sizeof(src));
281 dest[n] = 0; 281 dest[n] = 0;
282 CHECK_INT(n, 18); 282 CHECK_INT(n, 18);
283 CHECK_STR(dest, "%20%25%26%2b%3a%3f"); 283 CHECK_STR(dest, "%20%25%26%2b%3a%3f");
@@ -289,7 +289,7 @@ static void case_fmt_hex_is_lower_case(void) {
289 size_t n; 289 size_t n;
290 290
291 test_case("escapes use lower case hex digits"); 291 test_case("escapes use lower case hex digits");
292 n = fmt_urlencoded(dest, src, sizeof(src)); 292 n = urlencoded_format(dest, src, sizeof(src));
293 dest[n] = 0; 293 dest[n] = 0;
294 CHECK_INT(n, 9); 294 CHECK_INT(n, 9);
295 CHECK_STR(dest, "%00%ab%ff"); 295 CHECK_STR(dest, "%00%ab%ff");
@@ -300,7 +300,7 @@ static void case_fmt_zero_length(void) {
300 300
301 test_case("a zero length input writes nothing"); 301 test_case("a zero length input writes nothing");
302 memset(dest, 0x5a, sizeof(dest)); 302 memset(dest, 0x5a, sizeof(dest));
303 CHECK_INT(fmt_urlencoded(dest, (const uint8_t *)"abc", 0), 0); 303 CHECK_INT(urlencoded_format(dest, (const uint8_t *)"abc", 0), 0);
304 CHECK_MEM(dest, "\x5a\x5a\x5a\x5a", 4); 304 CHECK_MEM(dest, "\x5a\x5a\x5a\x5a", 4);
305} 305}
306 306
@@ -312,7 +312,7 @@ static void case_fmt_worst_case_fits(void) {
312 test_case("a hash of entirely unsafe bytes needs three times its length"); 312 test_case("a hash of entirely unsafe bytes needs three times its length");
313 for (i = 0; i < sizeof(src); ++i) 313 for (i = 0; i < sizeof(src); ++i)
314 src[i] = 0xff; 314 src[i] = 0xff;
315 n = fmt_urlencoded(dest, src, sizeof(src)); 315 n = urlencoded_format(dest, src, sizeof(src));
316 dest[n] = 0; 316 dest[n] = 0;
317 CHECK_INT(n, 60); 317 CHECK_INT(n, 60);
318 for (i = 0; i < 20; ++i) 318 for (i = 0; i < 20; ++i)
@@ -328,7 +328,7 @@ static void case_fmt_decoder_disagrees_on_dollar(void) {
328 encoder passes through unescaped, '$' is the only one the decoder then 328 encoder passes through unescaped, '$' is the only one the decoder then
329 refuses, and it refuses the whole string rather than stopping short. 329 refuses, and it refuses the whole string rather than stopping short.
330 Output of the urlencoded full scrape therefore does not round trip 330 Output of the urlencoded full scrape therefore does not round trip
331 through scan_urlencoded_query when a hash contains 0x24. Harmless today 331 through urlencoded_parse when a hash contains 0x24. Harmless today
332 because only third party tooling reads that output. */ 332 because only third party tooling reads that output. */
333 const uint8_t src[] = {'a', '$', 'b'}; 333 const uint8_t src[] = {'a', '$', 'b'};
334 char dest[16]; 334 char dest[16];
@@ -338,15 +338,15 @@ static void case_fmt_decoder_disagrees_on_dollar(void) {
338 ssize_t got; 338 ssize_t got;
339 339
340 test_case("the encoder passes '$' but the decoder rejects it"); 340 test_case("the encoder passes '$' but the decoder rejects it");
341 n = fmt_urlencoded(dest, src, sizeof(src)); 341 n = urlencoded_format(dest, src, sizeof(src));
342 dest[n] = 0; 342 dest[n] = 0;
343 CHECK_STR(dest, "a$b"); 343 CHECK_STR(dest, "a$b");
344 344
345 got = scan_urlencoded_query(&p, scanned, SCAN_SEARCHPATH_VALUE); 345 got = urlencoded_parse(&p, scanned, SCAN_SEARCHPATH_VALUE);
346 CHECK_INT(got, -1); 346 CHECK_INT(got, -1);
347} 347}
348 348
349void test_scan_urlencoded_query(void) { 349void test_urlencoded(void) {
350 case_path(); 350 case_path();
351 case_param_and_value(); 351 case_param_and_value();
352 case_percent_decoding(); 352 case_percent_decoding();
diff --git a/tests/t_vector.c b/tests/t_vector.c
index a8186b2..541a6da 100644
--- a/tests/t_vector.c
+++ b/tests/t_vector.c
@@ -13,7 +13,7 @@
13#include "trackerlogic.h" 13#include "trackerlogic.h"
14#include "test.h" 14#include "test.h"
15 15
16/* binary_search returns the matching member when there is one, and otherwise 16/* vector_binary_search returns the matching member when there is one, and otherwise
17 the position at which the key would have to be inserted to keep the vector 17 the position at which the key would have to be inserted to keep the vector
18 sorted. exactmatch distinguishes the two. */ 18 sorted. exactmatch distinguishes the two. */
19 19
@@ -25,7 +25,7 @@ static void case_search_finds_member(void) {
25 void *found; 25 void *found;
26 26
27 test_case("an existing key is found"); 27 test_case("an existing key is found");
28 found = binary_search(key, sorted, 5, 4, 4, &exactmatch); 28 found = vector_binary_search(key, sorted, 5, 4, 4, &exactmatch);
29 CHECK(exactmatch != 0); 29 CHECK(exactmatch != 0);
30 CHECK_PTR(found, sorted[2]); 30 CHECK_PTR(found, sorted[2]);
31} 31}
@@ -36,9 +36,9 @@ static void case_search_finds_first_and_last(void) {
36 int exactmatch; 36 int exactmatch;
37 37
38 test_case("the first and last members are found"); 38 test_case("the first and last members are found");
39 CHECK_PTR(binary_search(first, sorted, 5, 4, 4, &exactmatch), sorted[0]); 39 CHECK_PTR(vector_binary_search(first, sorted, 5, 4, 4, &exactmatch), sorted[0]);
40 CHECK(exactmatch != 0); 40 CHECK(exactmatch != 0);
41 CHECK_PTR(binary_search(last, sorted, 5, 4, 4, &exactmatch), sorted[4]); 41 CHECK_PTR(vector_binary_search(last, sorted, 5, 4, 4, &exactmatch), sorted[4]);
42 CHECK(exactmatch != 0); 42 CHECK(exactmatch != 0);
43} 43}
44 44
@@ -49,11 +49,11 @@ static void case_search_insert_position(void) {
49 void *slot; 49 void *slot;
50 50
51 test_case("a missing key yields its insert position"); 51 test_case("a missing key yields its insert position");
52 slot = binary_search(middle, sorted, 5, 4, 4, &exactmatch); 52 slot = vector_binary_search(middle, sorted, 5, 4, 4, &exactmatch);
53 CHECK_INT(exactmatch, 0); 53 CHECK_INT(exactmatch, 0);
54 CHECK_PTR(slot, sorted[2]); 54 CHECK_PTR(slot, sorted[2]);
55 55
56 slot = binary_search(before, sorted, 5, 4, 4, &exactmatch); 56 slot = vector_binary_search(before, sorted, 5, 4, 4, &exactmatch);
57 CHECK_INT(exactmatch, 0); 57 CHECK_INT(exactmatch, 0);
58 CHECK_PTR(slot, sorted[0]); 58 CHECK_PTR(slot, sorted[0]);
59} 59}
@@ -64,7 +64,7 @@ static void case_search_past_the_end(void) {
64 void *slot; 64 void *slot;
65 65
66 test_case("a key above every member yields the position past the end"); 66 test_case("a key above every member yields the position past the end");
67 slot = binary_search(beyond, sorted, 5, 4, 4, &exactmatch); 67 slot = vector_binary_search(beyond, sorted, 5, 4, 4, &exactmatch);
68 CHECK_INT(exactmatch, 0); 68 CHECK_INT(exactmatch, 0);
69 CHECK_PTR(slot, sorted[5]); 69 CHECK_PTR(slot, sorted[5]);
70} 70}
@@ -74,7 +74,7 @@ static void case_search_empty(void) {
74 void *base = (void *)sorted; 74 void *base = (void *)sorted;
75 75
76 test_case("searching an empty vector reports no match at the base"); 76 test_case("searching an empty vector reports no match at the base");
77 CHECK_PTR(binary_search(sorted[0], base, 0, 4, 4, &exactmatch), base); 77 CHECK_PTR(vector_binary_search(sorted[0], base, 0, 4, 4, &exactmatch), base);
78 CHECK_INT(exactmatch, 0); 78 CHECK_INT(exactmatch, 0);
79} 79}
80 80
diff --git a/tests/test.h b/tests/test.h
index fd574c2..cebd08a 100644
--- a/tests/test.h
+++ b/tests/test.h
@@ -76,7 +76,7 @@ extern unsigned long test_failures;
76 } while (0) 76 } while (0)
77 77
78/* One entry point per module under test */ 78/* One entry point per module under test */
79void test_scan_urlencoded_query(void); 79void test_urlencoded(void);
80void test_iovec(void); 80void test_iovec(void);
81void test_vector(void); 81void test_vector(void);
82void test_random(void); 82void test_random(void);
@@ -88,5 +88,6 @@ void test_ip(void);
88void test_socket(void); 88void test_socket(void);
89void test_mutex(void); 89void test_mutex(void);
90void test_batch(void); 90void test_batch(void);
91void test_fdstate(void);
91 92
92#endif 93#endif
diff --git a/trackerlogic.c b/trackerlogic.c
index 74fea6d..9705bd6 100644
--- a/trackerlogic.c
+++ b/trackerlogic.c
@@ -381,7 +381,7 @@ size_t return_peers_for_torrent(struct ot_workstruct *ws, ot_torrent *torrent, s
381size_t return_udp_scrape_for_torrent(ot_hash const hash, char *reply) { 381size_t return_udp_scrape_for_torrent(ot_hash const hash, char *reply) {
382 int exactmatch, delta_torrentcount = 0; 382 int exactmatch, delta_torrentcount = 0;
383 ot_vector *torrents_list = mutex_bucket_lock_by_hash(hash); 383 ot_vector *torrents_list = mutex_bucket_lock_by_hash(hash);
384 ot_torrent *torrent = binary_search(hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch); 384 ot_torrent *torrent = vector_binary_search(hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch);
385 385
386 if (!exactmatch) { 386 if (!exactmatch) {
387 memset(reply, 0, 12); 387 memset(reply, 0, 12);
@@ -414,7 +414,7 @@ size_t return_tcp_scrape_for_torrent(ot_hash const *hash_list, size_t amount, ch
414 int delta_torrentcount = 0; 414 int delta_torrentcount = 0;
415 ot_hash const *hash = hash_list + i; 415 ot_hash const *hash = hash_list + i;
416 ot_vector *torrents_list = mutex_bucket_lock_by_hash(*hash); 416 ot_vector *torrents_list = mutex_bucket_lock_by_hash(*hash);
417 ot_torrent *torrent = binary_search(hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch); 417 ot_torrent *torrent = vector_binary_search(hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch);
418 418
419 if (exactmatch) { 419 if (exactmatch) {
420 if (clean_single_torrent(torrent)) { 420 if (clean_single_torrent(torrent)) {
@@ -443,7 +443,7 @@ static ot_peerlist dummy_list;
443size_t remove_peer_from_torrent(PROTO_FLAG proto, struct ot_workstruct *ws) { 443size_t remove_peer_from_torrent(PROTO_FLAG proto, struct ot_workstruct *ws) {
444 int exactmatch; 444 int exactmatch;
445 ot_vector *torrents_list = mutex_bucket_lock_by_hash(*ws->hash); 445 ot_vector *torrents_list = mutex_bucket_lock_by_hash(*ws->hash);
446 ot_torrent *torrent = binary_search(ws->hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch); 446 ot_torrent *torrent = vector_binary_search(ws->hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch);
447 ot_peerlist *peer_list = &dummy_list; 447 ot_peerlist *peer_list = &dummy_list;
448 size_t peer_size; /* initialized in next line */ 448 size_t peer_size; /* initialized in next line */
449 ot_peer const *peer_src = peer_from_peer6(&ws->peer, &peer_size); 449 ot_peer const *peer_src = peer_from_peer6(&ws->peer, &peer_size);