diff options
| -rw-r--r-- | Makefile | 50 | ||||
| -rw-r--r-- | opentracker.c | 123 | ||||
| -rw-r--r-- | ot_accesslist.c | 36 | ||||
| -rw-r--r-- | ot_accesslist.h | 6 | ||||
| -rw-r--r-- | ot_clean.c | 3 | ||||
| -rw-r--r-- | ot_config.c | 31 | ||||
| -rw-r--r-- | ot_event.c | 332 | ||||
| -rw-r--r-- | ot_event.h | 39 | ||||
| -rw-r--r-- | ot_fdstate.c | 194 | ||||
| -rw-r--r-- | ot_fdstate.h | 61 | ||||
| -rw-r--r-- | ot_fmt.c | 42 | ||||
| -rw-r--r-- | ot_fmt.h | 21 | ||||
| -rw-r--r-- | ot_fullscrape.c | 55 | ||||
| -rw-r--r-- | ot_hex.c | 4 | ||||
| -rw-r--r-- | ot_hex.h | 4 | ||||
| -rw-r--r-- | ot_http.c | 177 | ||||
| -rw-r--r-- | ot_http.h | 6 | ||||
| -rw-r--r-- | ot_iovec.c | 12 | ||||
| -rw-r--r-- | ot_iovec.h | 6 | ||||
| -rw-r--r-- | ot_ip.c | 2 | ||||
| -rw-r--r-- | ot_ip.h | 2 | ||||
| -rw-r--r-- | ot_livesync.h | 1 | ||||
| -rw-r--r-- | ot_mutex.c | 9 | ||||
| -rw-r--r-- | ot_scan.c | 29 | ||||
| -rw-r--r-- | ot_scan.h | 12 | ||||
| -rw-r--r-- | ot_socket.c | 6 | ||||
| -rw-r--r-- | ot_socket.h | 1 | ||||
| -rw-r--r-- | ot_stats.c | 325 | ||||
| -rw-r--r-- | ot_stats.h | 4 | ||||
| -rw-r--r-- | ot_sync.c | 24 | ||||
| -rw-r--r-- | ot_udp.c | 3 | ||||
| -rw-r--r-- | ot_urlencoded.c (renamed from scan_urlencoded_query.c) | 29 | ||||
| -rw-r--r-- | ot_urlencoded.h (renamed from scan_urlencoded_query.h) | 14 | ||||
| -rw-r--r-- | ot_vector.c | 57 | ||||
| -rw-r--r-- | ot_vector.h | 2 | ||||
| -rw-r--r-- | proxy.c | 21 | ||||
| -rw-r--r-- | tests/integration/.gitignore | 2 | ||||
| -rw-r--r-- | tests/integration/harness.py | 166 | ||||
| -rw-r--r-- | tests/integration/test_announce.py | 129 | ||||
| -rw-r--r-- | tests/integration/test_dualstack.py | 89 | ||||
| -rw-r--r-- | tests/runner.c | 5 | ||||
| -rw-r--r-- | tests/t_event.c | 222 | ||||
| -rw-r--r-- | tests/t_fdstate.c | 224 | ||||
| -rw-r--r-- | tests/t_fmt.c | 108 | ||||
| -rw-r--r-- | tests/t_hex.c | 28 | ||||
| -rw-r--r-- | tests/t_iovec.c | 20 | ||||
| -rw-r--r-- | tests/t_ip.c | 54 | ||||
| -rw-r--r-- | tests/t_scan.c | 124 | ||||
| -rw-r--r-- | tests/t_urlencoded.c (renamed from tests/t_scan_urlencoded_query.c) | 95 | ||||
| -rw-r--r-- | tests/t_vector.c | 62 | ||||
| -rw-r--r-- | tests/test.h | 5 | ||||
| -rw-r--r-- | trackerlogic.c | 61 | ||||
| -rw-r--r-- | trackerlogic.h | 2 |
53 files changed, 2456 insertions, 683 deletions
| @@ -47,10 +47,6 @@ include Makefile.gzip | |||
| 47 | #FEATURES+=-DWANT_DEV_RANDOM | 47 | #FEATURES+=-DWANT_DEV_RANDOM |
| 48 | FEATURES+=-DWANT_FULLSCRAPE | 48 | FEATURES+=-DWANT_FULLSCRAPE |
| 49 | 49 | ||
| 50 | # You need libowfat version 0.34 to allow for automatic release of chunks during | ||
| 51 | # full scrape transfer, if you rely on an older versions, enable this flag | ||
| 52 | #FEATURES+=-DWANT_NO_AUTO_FREE | ||
| 53 | |||
| 54 | # opentracker takes its randomness from arc4random, which is part of libc on | 50 | # opentracker takes its randomness from arc4random, which is part of libc on |
| 55 | # the BSDs, on macOS and on glibc 2.36 and newer. On an older glibc or on musl | 51 | # the BSDs, on macOS and on glibc 2.36 and newer. On an older glibc or on musl |
| 56 | # it comes from libbsd instead, so uncomment the next include to link it. | 52 | # it comes from libbsd instead, so uncomment the next include to link it. |
| @@ -64,13 +60,18 @@ GIT_VERSION=`sh -c 'command -v git >/dev/null && test -d .git && git rev-parse H | |||
| 64 | OPTS_debug=-D_DEBUG -g -ggdb # -pg -fprofile-arcs -ftest-coverage | 60 | OPTS_debug=-D_DEBUG -g -ggdb # -pg -fprofile-arcs -ftest-coverage |
| 65 | OPTS_production=-O3 | 61 | OPTS_production=-O3 |
| 66 | 62 | ||
| 67 | CFLAGS+=-I$(LIBOWFAT_HEADERS) -DGIT_VERSION=$(GIT_VERSION) -Wall -pipe -pthread -Wextra -Wwrite-strings #-ansi -pedantic | 63 | CFLAGS+=-DGIT_VERSION=$(GIT_VERSION) -Wall -pipe -pthread -Wextra -Wwrite-strings #-ansi -pedantic |
| 68 | LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread | 64 | LDFLAGS+=-pthread |
| 65 | |||
| 66 | # Only the proxy, which has not been ported since the tracker went dual stack, | ||
| 67 | # still wants libowfat. | ||
| 68 | CFLAGS_libowfat=-I$(LIBOWFAT_HEADERS) | ||
| 69 | LDFLAGS_libowfat=-L$(LIBOWFAT_LIBRARY) -lowfat | ||
| 69 | 70 | ||
| 70 | BINARY =opentracker | 71 | BINARY =opentracker |
| 71 | HEADERS=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 | 72 | HEADERS=trackerlogic.h ot_urlencoded.h ot_batch.h ot_buf.h ot_event.h ot_fdstate.h ot_fmt.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 |
| 72 | SOURCES=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 | 73 | SOURCES=opentracker.c trackerlogic.c ot_urlencoded.c ot_batch.c ot_buf.c ot_event.c ot_fdstate.c ot_fmt.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 |
| 73 | SOURCES_proxy=proxy.c ot_batch.c ot_vector.c ot_mutex.c ot_random.c ot_scan.c ot_ip.c ot_socket.c | 74 | SOURCES_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 | 75 | ||
| 75 | OBJECTS = $(SOURCES:%.c=%.o) | 76 | OBJECTS = $(SOURCES:%.c=%.o) |
| 76 | OBJECTS_debug = $(SOURCES:%.c=%.debug.o) | 77 | OBJECTS_debug = $(SOURCES:%.c=%.debug.o) |
| @@ -84,8 +85,8 @@ all: $(BINARY) $(BINARY).debug | |||
| 84 | CFLAGS_production = $(CFLAGS) $(OPTS_production) $(FEATURES) | 85 | CFLAGS_production = $(CFLAGS) $(OPTS_production) $(FEATURES) |
| 85 | CFLAGS_debug = $(CFLAGS) $(OPTS_debug) $(FEATURES) | 86 | CFLAGS_debug = $(CFLAGS) $(OPTS_debug) $(FEATURES) |
| 86 | 87 | ||
| 87 | CFLAGS_proxy = $(CFLAGS_production) -DWANT_SYNC_LIVE | 88 | CFLAGS_proxy = $(CFLAGS_production) $(CFLAGS_libowfat) -DWANT_SYNC_LIVE |
| 88 | CFLAGS_proxy_debug = $(CFLAGS_debug) -DWANT_SYNC_LIVE | 89 | CFLAGS_proxy_debug = $(CFLAGS_debug) $(CFLAGS_libowfat) -DWANT_SYNC_LIVE |
| 89 | 90 | ||
| 90 | $(BINARY): $(OBJECTS) | 91 | $(BINARY): $(OBJECTS) |
| 91 | $(CC) -o $@ $(OBJECTS) $(LDFLAGS) | 92 | $(CC) -o $@ $(OBJECTS) $(LDFLAGS) |
| @@ -93,9 +94,9 @@ $(BINARY): $(OBJECTS) | |||
| 93 | $(BINARY).debug: $(OBJECTS_debug) | 94 | $(BINARY).debug: $(OBJECTS_debug) |
| 94 | $(CC) -o $@ $(OBJECTS_debug) $(LDFLAGS) | 95 | $(CC) -o $@ $(OBJECTS_debug) $(LDFLAGS) |
| 95 | proxy: $(OBJECTS_proxy) | 96 | proxy: $(OBJECTS_proxy) |
| 96 | $(CC) -o $@ $(OBJECTS_proxy) $(CFLAGS_production) $(LDFLAGS) | 97 | $(CC) -o $@ $(OBJECTS_proxy) $(CFLAGS_production) $(LDFLAGS) $(LDFLAGS_libowfat) |
| 97 | proxy.debug: $(OBJECTS_proxy_debug) | 98 | proxy.debug: $(OBJECTS_proxy_debug) |
| 98 | $(CC) -o $@ $(OBJECTS_proxy_debug) $(LDFLAGS) | 99 | $(CC) -o $@ $(OBJECTS_proxy_debug) $(LDFLAGS) $(LDFLAGS_libowfat) |
| 99 | 100 | ||
| 100 | $(OBJECTS): $(HEADERS) | 101 | $(OBJECTS): $(HEADERS) |
| 101 | $(OBJECTS_debug): $(HEADERS) | 102 | $(OBJECTS_debug): $(HEADERS) |
| @@ -119,8 +120,8 @@ $(OBJECTS_proxy_debug): $(HEADERS) | |||
| 119 | # has to. tests/stubs.c supplies the few symbols those modules reference but | 120 | # has to. tests/stubs.c supplies the few symbols those modules reference but |
| 120 | # the tests never reach. | 121 | # the tests never reach. |
| 121 | TESTBIN=tests/runner | 122 | TESTBIN=tests/runner |
| 122 | TEST_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 | 123 | TEST_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 tests/t_event.c tests/t_fmt.c |
| 123 | TEST_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 | 124 | TEST_UNITS=ot_urlencoded.c ot_fmt.c ot_event.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 |
| 124 | TEST_HEADERS=tests/test.h $(HEADERS) | 125 | TEST_HEADERS=tests/test.h $(HEADERS) |
| 125 | # Tests reach code that sits behind feature flags, so the units under test are | 126 | # 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. | 127 | # built with those features enabled. A test binary is not a production binary. |
| @@ -133,10 +134,25 @@ $(TESTBIN): $(TEST_SOURCES) $(TEST_UNITS) $(TEST_HEADERS) | |||
| 133 | check: $(TESTBIN) $(BINARY) | 134 | check: $(TESTBIN) $(BINARY) |
| 134 | ./$(TESTBIN) | 135 | ./$(TESTBIN) |
| 135 | 136 | ||
| 136 | .PHONY: all check clean install | 137 | # Black box tests: these drive the real binary over the wire, so they cover |
| 138 | # the event loop and the parsers working together, which the unit tests | ||
| 139 | # cannot reach. Built with the sanitizers on, so the tracker reports its own | ||
| 140 | # memory errors and the harness turns those into failures. | ||
| 141 | PYTHON?=python3 | ||
| 142 | FEATURES_integration=$(FEATURES) -DWANT_IP_FROM_QUERY_STRING | ||
| 143 | CFLAGS_integration=$(CFLAGS) $(OPTS_debug) $(FEATURES_integration) -fsanitize=address,undefined -fno-omit-frame-pointer | ||
| 144 | LDFLAGS_integration=$(LDFLAGS) -fsanitize=address,undefined | ||
| 145 | |||
| 146 | opentracker.integration: $(SOURCES) $(HEADERS) | ||
| 147 | $(CC) -o $@ $(CFLAGS_integration) $(SOURCES) $(LDFLAGS_integration) | ||
| 148 | |||
| 149 | check-integration: opentracker.integration | ||
| 150 | OPENTRACKER=./opentracker.integration $(PYTHON) -m unittest discover -s tests/integration | ||
| 151 | |||
| 152 | .PHONY: all check check-integration clean install | ||
| 137 | 153 | ||
| 138 | clean: | 154 | clean: |
| 139 | rm -rf opentracker opentracker.debug proxy proxy.debug $(TESTBIN) *.o *~ | 155 | rm -rf opentracker opentracker.debug opentracker.integration proxy proxy.debug $(TESTBIN) *.o *~ |
| 140 | 156 | ||
| 141 | install: | 157 | install: |
| 142 | install -m 755 opentracker $(DESTDIR)$(BINDIR) | 158 | install -m 755 opentracker $(DESTDIR)$(BINDIR) |
diff --git a/opentracker.c b/opentracker.c index 50fff29..21eeb42 100644 --- a/opentracker.c +++ b/opentracker.c | |||
| @@ -21,9 +21,6 @@ | |||
| 21 | #include <syslog.h> | 21 | #include <syslog.h> |
| 22 | #endif | 22 | #endif |
| 23 | 23 | ||
| 24 | /* Libowfat */ | ||
| 25 | #include "io.h" | ||
| 26 | |||
| 27 | /* Opentracker */ | 24 | /* Opentracker */ |
| 28 | #include "ot_accesslist.h" | 25 | #include "ot_accesslist.h" |
| 29 | #include "ot_config.h" | 26 | #include "ot_config.h" |
| @@ -34,6 +31,8 @@ | |||
| 34 | #include "ot_mutex.h" | 31 | #include "ot_mutex.h" |
| 35 | #include "ot_random.h" | 32 | #include "ot_random.h" |
| 36 | #include "ot_scan.h" | 33 | #include "ot_scan.h" |
| 34 | #include "ot_event.h" | ||
| 35 | #include "ot_fdstate.h" | ||
| 37 | #include "ot_socket.h" | 36 | #include "ot_socket.h" |
| 38 | #include "ot_stats.h" | 37 | #include "ot_stats.h" |
| 39 | #include "ot_udp.h" | 38 | #include "ot_udp.h" |
| @@ -117,7 +116,7 @@ static ssize_t header_complete(char *request, ssize_t byte_count) { | |||
| 117 | } | 116 | } |
| 118 | 117 | ||
| 119 | static void handle_dead(const int64_t sock) { | 118 | static void handle_dead(const int64_t sock) { |
| 120 | struct http_data *cookie = io_getcookie(sock); | 119 | struct http_data *cookie = fdstate_cookie(sock); |
| 121 | if (cookie) { | 120 | if (cookie) { |
| 122 | if (cookie->batch) | 121 | if (cookie->batch) |
| 123 | batch_reset(cookie->batch); | 122 | batch_reset(cookie->batch); |
| @@ -127,14 +126,17 @@ static void handle_dead(const int64_t sock) { | |||
| 127 | mutex_workqueue_canceltask(sock); | 126 | mutex_workqueue_canceltask(sock); |
| 128 | free(cookie); | 127 | free(cookie); |
| 129 | } | 128 | } |
| 130 | io_close(sock); | 129 | /* The generation has to move before the kernel can reissue the number */ |
| 130 | fdstate_close(sock); | ||
| 131 | event_del((int)sock); | ||
| 132 | close((int)sock); | ||
| 131 | } | 133 | } |
| 132 | 134 | ||
| 133 | static void handle_read(const int64_t sock, struct ot_workstruct *ws) { | 135 | static void handle_read(const int64_t sock, struct ot_workstruct *ws) { |
| 134 | struct http_data *cookie = io_getcookie(sock); | 136 | struct http_data *cookie = fdstate_cookie(sock); |
| 135 | ssize_t byte_count = io_tryread(sock, ws->inbuf, G_INBUF_SIZE); | 137 | ssize_t byte_count = read((int)sock, ws->inbuf, G_INBUF_SIZE); |
| 136 | 138 | ||
| 137 | if (byte_count == 0 || byte_count == -3) { | 139 | if (byte_count == 0 || (byte_count == -1 && errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)) { |
| 138 | handle_dead(sock); | 140 | handle_dead(sock); |
| 139 | return; | 141 | return; |
| 140 | } | 142 | } |
| @@ -175,7 +177,7 @@ static void handle_read(const int64_t sock, struct ot_workstruct *ws) { | |||
| 175 | } | 177 | } |
| 176 | 178 | ||
| 177 | static void handle_write(const int64_t sock) { | 179 | static void handle_write(const int64_t sock) { |
| 178 | struct http_data *cookie = io_getcookie(sock); | 180 | struct http_data *cookie = fdstate_cookie(sock); |
| 179 | int chunked = 0; | 181 | int chunked = 0; |
| 180 | 182 | ||
| 181 | if (cookie) { | 183 | if (cookie) { |
| @@ -194,7 +196,7 @@ static void handle_write(const int64_t sock) { | |||
| 194 | 196 | ||
| 195 | /* In a chunked transfer, wait for the next piece once everything queued has gone out */ | 197 | /* In a chunked transfer, wait for the next piece once everything queued has gone out */ |
| 196 | if (chunked) | 198 | if (chunked) |
| 197 | io_dontwantwrite(sock); | 199 | http_interest_drop(sock, cookie, EVENT_OUT); |
| 198 | else | 200 | else |
| 199 | handle_dead(sock); | 201 | handle_dead(sock); |
| 200 | } | 202 | } |
| @@ -204,34 +206,33 @@ static void handle_accept(const int64_t serversocket) { | |||
| 204 | int64_t sock; | 206 | int64_t sock; |
| 205 | ot_ip6 ip; | 207 | ot_ip6 ip; |
| 206 | uint16_t port; | 208 | uint16_t port; |
| 207 | tai6464 t; | ||
| 208 | 209 | ||
| 209 | while ((sock = socket_accept(serversocket, ip, &port)) != -1) { | 210 | while ((sock = socket_accept(serversocket, ip, &port)) != -1) { |
| 210 | 211 | ||
| 211 | /* Put fd into a non-blocking mode */ | 212 | socket_set_blocking((int)sock, 0); |
| 212 | io_nonblock(sock); | ||
| 213 | 213 | ||
| 214 | if (!io_fd(sock) || !(cookie = (struct http_data *)malloc(sizeof(struct http_data)))) { | 214 | if (!(cookie = (struct http_data *)malloc(sizeof(struct http_data)))) { |
| 215 | io_close(sock); | 215 | close((int)sock); |
| 216 | continue; | 216 | continue; |
| 217 | } | 217 | } |
| 218 | memset(cookie, 0, sizeof(struct http_data)); | 218 | memset(cookie, 0, sizeof(struct http_data)); |
| 219 | memcpy(cookie->ip, ip, sizeof(ot_ip6)); | 219 | memcpy(cookie->ip, ip, sizeof(ot_ip6)); |
| 220 | cookie->interest = EVENT_IN; | ||
| 220 | 221 | ||
| 221 | io_setcookie(sock, cookie); | 222 | if (event_add((int)sock, EVENT_IN, fdstate_open(sock, cookie, g_now_seconds + OT_CLIENT_TIMEOUT)) == -1) { |
| 222 | io_wantread(sock); | 223 | fdstate_close(sock); |
| 224 | free(cookie); | ||
| 225 | close((int)sock); | ||
| 226 | continue; | ||
| 227 | } | ||
| 223 | 228 | ||
| 224 | stats_issue_event(EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ip); | 229 | stats_issue_event(EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ip); |
| 225 | |||
| 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 | } | 230 | } |
| 232 | io_eagain(serversocket); | ||
| 233 | } | 231 | } |
| 234 | 232 | ||
| 233 | /* How many ready descriptors one wait hands back at a time */ | ||
| 234 | #define OT_EVENT_BATCH 512 | ||
| 235 | |||
| 235 | static void *server_mainloop(void *args) { | 236 | static void *server_mainloop(void *args) { |
| 236 | struct ot_workstruct ws; | 237 | struct ot_workstruct ws; |
| 237 | time_t next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL; | 238 | time_t next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL; |
| @@ -254,30 +255,46 @@ static void *server_mainloop(void *args) { | |||
| 254 | random_bytes(ws.rand48_state, sizeof(ws.rand48_state)); | 255 | random_bytes(ws.rand48_state, sizeof(ws.rand48_state)); |
| 255 | 256 | ||
| 256 | for (;;) { | 257 | for (;;) { |
| 257 | int64_t sock; | 258 | ot_event ready[OT_EVENT_BATCH]; |
| 259 | int64_t sock; | ||
| 260 | int count, i; | ||
| 261 | |||
| 262 | count = event_wait(ready, OT_EVENT_BATCH, OT_CLIENT_TIMEOUT_CHECKINTERVAL * 1000); | ||
| 258 | 263 | ||
| 259 | io_wait(); | 264 | for (i = 0; i < count; ++i) { |
| 265 | const void *cookie; | ||
| 266 | |||
| 267 | sock = ready[i].fd; | ||
| 268 | /* A descriptor closed earlier in this same batch may already have been | ||
| 269 | handed out again by accept, so an event is only ours if the | ||
| 270 | generation it was registered under is still the one in place */ | ||
| 271 | if (fdstate_generation((int)sock) != ready[i].generation) | ||
| 272 | continue; | ||
| 273 | cookie = fdstate_cookie((int)sock); | ||
| 260 | 274 | ||
| 261 | while ((sock = io_canread()) != -1) { | ||
| 262 | const void *cookie = io_getcookie(sock); | ||
| 263 | if ((intptr_t)cookie == FLAG_TCP) | 275 | if ((intptr_t)cookie == FLAG_TCP) |
| 264 | handle_accept(sock); | 276 | handle_accept(sock); |
| 265 | else if ((intptr_t)cookie == FLAG_UDP) | 277 | else if ((intptr_t)cookie == FLAG_UDP) |
| 266 | handle_udp6(sock, &ws); | 278 | handle_udp6(sock, &ws); |
| 267 | else if ((intptr_t)cookie == FLAG_SELFPIPE) | 279 | else if ((intptr_t)cookie == FLAG_SELFPIPE) |
| 268 | io_tryread(sock, ws.inbuf, G_INBUF_SIZE); | 280 | while (read((int)sock, ws.inbuf, G_INBUF_SIZE) > 0) |
| 269 | else | 281 | ; |
| 270 | handle_read(sock, &ws); | 282 | else { |
| 283 | if (ready[i].what & EVENT_IN) | ||
| 284 | handle_read(sock, &ws); | ||
| 285 | /* The read may have closed it, so look again before writing */ | ||
| 286 | if ((ready[i].what & EVENT_OUT) && fdstate_generation((int)sock) == ready[i].generation) | ||
| 287 | handle_write(sock); | ||
| 288 | } | ||
| 271 | } | 289 | } |
| 272 | 290 | ||
| 273 | while ((sock = mutex_workqueue_popresult(&iovec_entries, &iovector, &is_partial)) != -1) | 291 | while ((sock = mutex_workqueue_popresult(&iovec_entries, &iovector, &is_partial)) != -1) |
| 274 | http_sendiovecdata(sock, &ws, iovec_entries, iovector, is_partial); | 292 | http_sendiovecdata(sock, &ws, iovec_entries, iovector, is_partial); |
| 275 | 293 | ||
| 276 | while ((sock = io_canwrite()) != -1) | ||
| 277 | handle_write(sock); | ||
| 278 | |||
| 279 | if (g_now_seconds > next_timeout_check) { | 294 | if (g_now_seconds > next_timeout_check) { |
| 280 | while ((sock = io_timeouted()) != -1) | 295 | int cursor = 0; |
| 296 | |||
| 297 | while ((sock = fdstate_next_expired(g_now_seconds, &cursor)) != -1) | ||
| 281 | handle_dead(sock); | 298 | handle_dead(sock); |
| 282 | next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL; | 299 | next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL; |
| 283 | } | 300 | } |
| @@ -320,11 +337,8 @@ static void listen_bind_one(ot_listen *entry) { | |||
| 320 | panic("socket_udp_bind"); | 337 | panic("socket_udp_bind"); |
| 321 | } | 338 | } |
| 322 | 339 | ||
| 323 | if (!io_fd(entry->sock)) | 340 | if (event_add((int)entry->sock, EVENT_IN, fdstate_open(entry->sock, (void *)entry->proto, 0)) == -1) |
| 324 | panic("io_fd"); | 341 | panic("event_add"); |
| 325 | |||
| 326 | io_setcookie(entry->sock, (void *)entry->proto); | ||
| 327 | io_wantread(entry->sock); | ||
| 328 | 342 | ||
| 329 | #ifdef _DEBUG | 343 | #ifdef _DEBUG |
| 330 | fputs(" success.\n", stderr); | 344 | fputs(" success.\n", stderr); |
| @@ -353,7 +367,8 @@ static void listen_start_udp_workers(void) { | |||
| 353 | if (entry->proto != FLAG_UDP || entry->workers <= 0) | 367 | if (entry->proto != FLAG_UDP || entry->workers <= 0) |
| 354 | continue; | 368 | continue; |
| 355 | 369 | ||
| 356 | io_block(entry->sock); | 370 | socket_set_blocking((int)entry->sock, 1); |
| 371 | event_del((int)entry->sock); | ||
| 357 | udp_workers_install(entry->sock, (unsigned int)entry->workers); | 372 | udp_workers_install(entry->sock, (unsigned int)entry->workers); |
| 358 | } | 373 | } |
| 359 | } | 374 | } |
| @@ -376,14 +391,14 @@ void load_state(const char *const state_filename) { | |||
| 376 | while (fgets(inbuf, sizeof(inbuf), state_filehandle)) { | 391 | while (fgets(inbuf, sizeof(inbuf), state_filehandle)) { |
| 377 | size_t i; | 392 | size_t i; |
| 378 | 393 | ||
| 379 | if (!hex_tohash(infohash, inbuf)) | 394 | if (!hex_to_hash(infohash, inbuf)) |
| 380 | continue; | 395 | continue; |
| 381 | i = 2 * sizeof(ot_hash); | 396 | i = 2 * sizeof(ot_hash); |
| 382 | 397 | ||
| 383 | if (inbuf[i++] != ':' || !(consumed = scan_udec(inbuf + i, &base, UINT64_MAX))) | 398 | if (inbuf[i++] != ':' || !(consumed = scan_uint(inbuf + i, &base, UINT64_MAX))) |
| 384 | continue; | 399 | continue; |
| 385 | i += consumed; | 400 | i += consumed; |
| 386 | if (inbuf[i++] != ':' || !(consumed = scan_udec(inbuf + i, &downcount, UINT64_MAX))) | 401 | if (inbuf[i++] != ':' || !(consumed = scan_uint(inbuf + i, &downcount, UINT64_MAX))) |
| 387 | continue; | 402 | continue; |
| 388 | add_torrent_from_saved_state(infohash, base, downcount); | 403 | add_torrent_from_saved_state(infohash, base, downcount); |
| 389 | } | 404 | } |
| @@ -452,6 +467,10 @@ int main(int argc, char **argv) { | |||
| 452 | pthread_t thread_id; /* time cacher */ | 467 | pthread_t thread_id; /* time cacher */ |
| 453 | 468 | ||
| 454 | random_init(); | 469 | random_init(); |
| 470 | if (fdstate_init() == -1) | ||
| 471 | panic("fdstate_init"); | ||
| 472 | if (event_init() == -1) | ||
| 473 | panic("event_init"); | ||
| 455 | config_parse(argc, argv); | 474 | config_parse(argc, argv); |
| 456 | 475 | ||
| 457 | listen_bind_all(); | 476 | listen_bind_all(); |
| @@ -470,15 +489,15 @@ int main(int argc, char **argv) { | |||
| 470 | pthread_create(&thread_id, NULL, time_caching_worker, NULL); | 489 | pthread_create(&thread_id, NULL, time_caching_worker, NULL); |
| 471 | 490 | ||
| 472 | /* Create our self pipe which allows us to interrupt mainloops | 491 | /* Create our self pipe which allows us to interrupt mainloops |
| 473 | io_wait in case some data is available to send out */ | 492 | wait in case some data is available to send out */ |
| 474 | if (pipe(g_self_pipe) == -1) | 493 | if (pipe(g_self_pipe) == -1) |
| 475 | panic("selfpipe failed: "); | 494 | panic("selfpipe failed: "); |
| 476 | if (!io_fd(g_self_pipe[0])) | 495 | /* Both ends have to be non blocking: the loop drains the read end until it |
| 477 | panic("selfpipe io_fd failed: "); | 496 | is empty, and a worker nudging a full pipe has already made its point */ |
| 478 | if (!io_fd(g_self_pipe[1])) | 497 | socket_set_blocking(g_self_pipe[0], 0); |
| 479 | panic("selfpipe io_fd failed: "); | 498 | socket_set_blocking(g_self_pipe[1], 0); |
| 480 | io_setcookie(g_self_pipe[0], (void *)FLAG_SELFPIPE); | 499 | if (event_add(g_self_pipe[0], EVENT_IN, fdstate_open(g_self_pipe[0], (void *)FLAG_SELFPIPE, 0)) == -1) |
| 481 | io_wantread(g_self_pipe[0]); | 500 | panic("selfpipe event_add"); |
| 482 | 501 | ||
| 483 | /* Init all sub systems. This call may fail with an exit() */ | 502 | /* Init all sub systems. This call may fail with an exit() */ |
| 484 | trackerlogic_init(); | 503 | trackerlogic_init(); |
| @@ -498,5 +517,7 @@ int main(int argc, char **argv) { | |||
| 498 | 517 | ||
| 499 | server_mainloop(0); | 518 | server_mainloop(0); |
| 500 | 519 | ||
| 520 | event_deinit(); | ||
| 521 | fdstate_deinit(); | ||
| 501 | return 0; | 522 | return 0; |
| 502 | } | 523 | } |
diff --git a/ot_accesslist.c b/ot_accesslist.c index d7e88ee..724978e 100644 --- a/ot_accesslist.c +++ b/ot_accesslist.c | |||
| @@ -21,8 +21,6 @@ | |||
| 21 | #include <errno.h> | 21 | #include <errno.h> |
| 22 | #endif | 22 | #endif |
| 23 | 23 | ||
| 24 | /* Libowfat */ | ||
| 25 | |||
| 26 | /* Opentracker */ | 24 | /* Opentracker */ |
| 27 | #include "ot_accesslist.h" | 25 | #include "ot_accesslist.h" |
| 28 | #include "ot_hex.h" | 26 | #include "ot_hex.h" |
| @@ -110,7 +108,7 @@ size_t accesslist_parse_hashes(ot_hash *dest, size_t capacity, const char *buf, | |||
| 110 | size_t count = 0; | 108 | size_t count = 0; |
| 111 | 109 | ||
| 112 | while (count < capacity && end - offs >= 40) { | 110 | while (count < capacity && end - offs >= 40) { |
| 113 | if (hex_tohash(dest[count], offs)) { | 111 | if (hex_to_hash(dest[count], offs)) { |
| 114 | offs += 40; | 112 | offs += 40; |
| 115 | 113 | ||
| 116 | if (offs == end || !hex_isdigit((unsigned char)*offs)) | 114 | if (offs == end || !hex_isdigit((unsigned char)*offs)) |
| @@ -308,7 +306,7 @@ static void *accesslist_adddel_worker(const char *fifoname, ot_accesslist *_ | |||
| 308 | if (linelen < 41) | 306 | if (linelen < 41) |
| 309 | continue; | 307 | continue; |
| 310 | 308 | ||
| 311 | if (!hex_tohash(info_hash, line)) | 309 | if (!hex_to_hash(info_hash, line)) |
| 312 | continue; | 310 | continue; |
| 313 | 311 | ||
| 314 | /* From now on we modify g_accesslist_add and g_accesslist_delete, so prevent the | 312 | /* From now on we modify g_accesslist_add and g_accesslist_delete, so prevent the |
| @@ -339,7 +337,7 @@ static void *accesslist_adddel_worker(const char *fifoname, ot_accesslist *_ | |||
| 339 | } | 337 | } |
| 340 | } else { | 338 | } else { |
| 341 | int exactmatch = 0; | 339 | 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); | 340 | ot_hash *insert_point = vector_binary_search(info_hash, (*adding_to)->list, (*adding_to)->size, sizeof(ot_hash), OT_HASH_COMPARE_SIZE, &exactmatch); |
| 343 | 341 | ||
| 344 | /* Only if the info hash is not in the adding_to list, create a new head with that entry */ | 342 | /* Only if the info hash is not in the adding_to list, create a new head with that entry */ |
| 345 | if (!exactmatch) { | 343 | if (!exactmatch) { |
| @@ -416,7 +414,7 @@ void accesslist_cleanup(void) { | |||
| 416 | } | 414 | } |
| 417 | #endif | 415 | #endif |
| 418 | 416 | ||
| 419 | int address_in_net(const ot_ip6 address, const ot_net *net) { | 417 | int accesslist_address_in_net(const ot_ip6 address, const ot_net *net) { |
| 420 | int bits = net->bits, checkbits = (0x7f00 >> (bits & 7)); | 418 | int bits = net->bits, checkbits = (0x7f00 >> (bits & 7)); |
| 421 | int result = memcmp(address, &net->address, bits >> 3); | 419 | int result = memcmp(address, &net->address, bits >> 3); |
| 422 | if (!result && (bits & 7)) | 420 | if (!result && (bits & 7)) |
| @@ -424,7 +422,7 @@ int address_in_net(const ot_ip6 address, const ot_net *net) { | |||
| 424 | return result == 0; | 422 | return result == 0; |
| 425 | } | 423 | } |
| 426 | 424 | ||
| 427 | void *set_value_for_net(const ot_net *net, ot_vector *vector, const void *value, const size_t member_size) { | 425 | void *accesslist_set_value_for_net(const ot_net *net, ot_vector *vector, const void *value, const size_t member_size) { |
| 428 | size_t i; | 426 | size_t i; |
| 429 | int exactmatch; | 427 | int exactmatch; |
| 430 | 428 | ||
| @@ -435,7 +433,7 @@ void *set_value_for_net(const ot_net *net, ot_vector *vector, const void *value, | |||
| 435 | /* Check each net in vector for overlap */ | 433 | /* Check each net in vector for overlap */ |
| 436 | uint8_t *member = ((uint8_t *)vector->data); | 434 | uint8_t *member = ((uint8_t *)vector->data); |
| 437 | for (i = 0; i < vector->size; ++i) { | 435 | for (i = 0; i < vector->size; ++i) { |
| 438 | if (address_in_net(*(ot_ip6 *)member, net) || address_in_net(net->address, (ot_net *)member)) | 436 | if (accesslist_address_in_net(*(ot_ip6 *)member, net) || accesslist_address_in_net(net->address, (ot_net *)member)) |
| 439 | return 0; | 437 | return 0; |
| 440 | member += member_size; | 438 | member += member_size; |
| 441 | } | 439 | } |
| @@ -451,16 +449,16 @@ void *set_value_for_net(const ot_net *net, ot_vector *vector, const void *value, | |||
| 451 | 449 | ||
| 452 | /* Takes a vector filled with { ot_net net, uint8_t[x] value }; | 450 | /* Takes a vector filled with { ot_net net, uint8_t[x] value }; |
| 453 | Returns value associated with the net, or NULL if not found */ | 451 | Returns value associated with the net, or NULL if not found */ |
| 454 | void *get_value_for_net(const ot_ip6 address, const ot_vector *vector, const size_t member_size) { | 452 | void *accesslist_get_value_for_net(const ot_ip6 address, const ot_vector *vector, const size_t member_size) { |
| 455 | int exactmatch; | 453 | int exactmatch; |
| 456 | /* This binary search will return a pointer to the first non-containing network... */ | 454 | /* 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); | 455 | ot_net *net = vector_binary_search(address, vector->data, vector->size, member_size, sizeof(ot_ip6), &exactmatch); |
| 458 | if (!net) | 456 | if (!net) |
| 459 | return NULL; | 457 | return NULL; |
| 460 | /* ... so we'll need to move back one step unless we've exactly hit the first address in network */ | 458 | /* ... 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)) | 459 | if (!exactmatch && ((void *)net > vector->data)) |
| 462 | --net; | 460 | --net; |
| 463 | if (!address_in_net(address, net)) | 461 | if (!accesslist_address_in_net(address, net)) |
| 464 | return NULL; | 462 | return NULL; |
| 465 | return (void *)net; | 463 | return (void *)net; |
| 466 | } | 464 | } |
| @@ -472,7 +470,7 @@ static pthread_mutex_t g_lognets_list_mutex = PTHREAD_MUTEX_INITIALIZER; | |||
| 472 | 470 | ||
| 473 | void loglist_add_network(const ot_net *net) { | 471 | void loglist_add_network(const ot_net *net) { |
| 474 | pthread_mutex_lock(&g_lognets_list_mutex); | 472 | pthread_mutex_lock(&g_lognets_list_mutex); |
| 475 | set_value_for_net(net, &g_lognets_list, NULL, sizeof(ot_net)); | 473 | accesslist_set_value_for_net(net, &g_lognets_list, NULL, sizeof(ot_net)); |
| 476 | pthread_mutex_unlock(&g_lognets_list_mutex); | 474 | pthread_mutex_unlock(&g_lognets_list_mutex); |
| 477 | } | 475 | } |
| 478 | 476 | ||
| @@ -487,7 +485,7 @@ void loglist_reset(void) { | |||
| 487 | int loglist_check_address(const ot_ip6 address) { | 485 | int loglist_check_address(const ot_ip6 address) { |
| 488 | int result; | 486 | int result; |
| 489 | pthread_mutex_lock(&g_lognets_list_mutex); | 487 | pthread_mutex_lock(&g_lognets_list_mutex); |
| 490 | result = (NULL != get_value_for_net(address, &g_lognets_list, sizeof(ot_net))); | 488 | result = (NULL != accesslist_get_value_for_net(address, &g_lognets_list, sizeof(ot_net))); |
| 491 | pthread_mutex_unlock(&g_lognets_list_mutex); | 489 | pthread_mutex_unlock(&g_lognets_list_mutex); |
| 492 | return result; | 490 | return result; |
| 493 | } | 491 | } |
| @@ -508,17 +506,17 @@ int proxylist_add_network(const ot_net *proxy, const ot_net *net) { | |||
| 508 | pthread_mutex_lock(&g_proxies_list_mutex); | 506 | pthread_mutex_lock(&g_proxies_list_mutex); |
| 509 | 507 | ||
| 510 | /* If we have a direct hit, use and extend the vector there */ | 508 | /* 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); | 509 | map = vector_binary_search(proxy, g_proxies_list.data, g_proxies_list.size, sizeof(ot_proxymap), sizeof(ot_net), &exactmatch); |
| 512 | 510 | ||
| 513 | if (!map || !exactmatch) { | 511 | if (!map || !exactmatch) { |
| 514 | /* else see, if we've got overlapping networks | 512 | /* else see, if we've got overlapping networks |
| 515 | and get a new empty vector if not */ | 513 | and get a new empty vector if not */ |
| 516 | ot_vector empty; | 514 | ot_vector empty; |
| 517 | memset(&empty, 0, sizeof(ot_vector)); | 515 | memset(&empty, 0, sizeof(ot_vector)); |
| 518 | map = set_value_for_net(proxy, &g_proxies_list, &empty, sizeof(ot_proxymap)); | 516 | map = accesslist_set_value_for_net(proxy, &g_proxies_list, &empty, sizeof(ot_proxymap)); |
| 519 | } | 517 | } |
| 520 | 518 | ||
| 521 | if (map && set_value_for_net(net, &map->networks, NULL, sizeof(ot_net))) | 519 | if (map && accesslist_set_value_for_net(net, &map->networks, NULL, sizeof(ot_net))) |
| 522 | result = 1; | 520 | result = 1; |
| 523 | 521 | ||
| 524 | pthread_mutex_unlock(&g_proxies_list_mutex); | 522 | pthread_mutex_unlock(&g_proxies_list_mutex); |
| @@ -531,8 +529,8 @@ int proxylist_check_proxy(const ot_ip6 proxy, const ot_ip6 address) { | |||
| 531 | 529 | ||
| 532 | pthread_mutex_lock(&g_proxies_list_mutex); | 530 | pthread_mutex_lock(&g_proxies_list_mutex); |
| 533 | 531 | ||
| 534 | if ((map = get_value_for_net(proxy, &g_proxies_list, sizeof(ot_proxymap)))) | 532 | 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))) | 533 | if (!address || accesslist_get_value_for_net(address, &map->networks, sizeof(ot_net))) |
| 536 | result = 1; | 534 | result = 1; |
| 537 | 535 | ||
| 538 | pthread_mutex_unlock(&g_proxies_list_mutex); | 536 | pthread_mutex_unlock(&g_proxies_list_mutex); |
| @@ -587,7 +585,7 @@ int accesslist_bless_net(ot_net *net, ot_permissions permissions) { | |||
| 587 | int accesslist_is_blessed(ot_ip6 ip, ot_permissions permissions) { | 585 | int accesslist_is_blessed(ot_ip6 ip, ot_permissions permissions) { |
| 588 | size_t i; | 586 | size_t i; |
| 589 | for (i = 0; i < g_admin_nets_count; ++i) | 587 | 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)) | 588 | if (accesslist_address_in_net(ip, g_admin_nets + i) && (g_admin_nets_permissions[i] & permissions)) |
| 591 | return 1; | 589 | return 1; |
| 592 | return 0; | 590 | return 0; |
| 593 | } | 591 | } |
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 */ |
| 43 | int address_in_net(const ot_ip6 address, const ot_net *net); | 43 | int 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 | */ |
| 54 | void *set_value_for_net(const ot_net *net, ot_vector *vector, const void *value, const size_t member_size); | 54 | void *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 | */ |
| 60 | void *get_value_for_net(const ot_ip6 address, const ot_vector *vector, const size_t member_size); | 60 | void *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 |
| 63 | int proxylist_add_network(const ot_net *proxy, const ot_net *net); | 63 | int proxylist_add_network(const ot_net *proxy, const ot_net *net); |
| @@ -8,9 +8,6 @@ | |||
| 8 | #include <string.h> | 8 | #include <string.h> |
| 9 | #include <unistd.h> | 9 | #include <unistd.h> |
| 10 | 10 | ||
| 11 | /* Libowfat */ | ||
| 12 | #include "io.h" | ||
| 13 | |||
| 14 | /* Opentracker */ | 11 | /* Opentracker */ |
| 15 | #include "ot_accesslist.h" | 12 | #include "ot_accesslist.h" |
| 16 | #include "ot_clean.h" | 13 | #include "ot_clean.h" |
diff --git a/ot_config.c b/ot_config.c index e9666f9..ee6bc12 100644 --- a/ot_config.c +++ b/ot_config.c | |||
| @@ -107,7 +107,7 @@ static int listen_scan_options(char *p, int *shared, int *workers) { | |||
| 107 | *shared = 1; | 107 | *shared = 1; |
| 108 | break; | 108 | break; |
| 109 | case LISTEN_OPTION_WORKERS: { | 109 | case LISTEN_OPTION_WORKERS: { |
| 110 | size_t off = scan_udec(p, &value, UINT_MAX); | 110 | size_t off = scan_uint(p, &value, UINT_MAX); |
| 111 | 111 | ||
| 112 | if (!off) | 112 | if (!off) |
| 113 | return 0; | 113 | return 0; |
| @@ -140,7 +140,7 @@ static int scan_ip6_port(const char *src, ot_ip6 ip, uint16_t *port) { | |||
| 140 | ++s; | 140 | ++s; |
| 141 | if (*s == '[') | 141 | if (*s == '[') |
| 142 | ++s, ++bracket; /* for v6 style notation */ | 142 | ++s, ++bracket; /* for v6 style notation */ |
| 143 | if (!(off = ip_scan(s, ip))) | 143 | if (!(off = ip_parse(s, ip))) |
| 144 | return 0; | 144 | return 0; |
| 145 | s += off; | 145 | s += off; |
| 146 | if (bracket && *s == ']') | 146 | if (bracket && *s == ']') |
| @@ -157,7 +157,7 @@ static int scan_ip6_port(const char *src, ot_ip6 ip, uint16_t *port) { | |||
| 157 | if (*(s++) != ':') | 157 | if (*(s++) != ':') |
| 158 | return 0; | 158 | return 0; |
| 159 | } | 159 | } |
| 160 | if (!(off = scan_udec(s, &value, 65535))) | 160 | if (!(off = scan_uint(s, &value, 65535))) |
| 161 | return 0; | 161 | return 0; |
| 162 | *port = (uint16_t)value; | 162 | *port = (uint16_t)value; |
| 163 | return off + s - src; | 163 | return off + s - src; |
| @@ -169,14 +169,14 @@ static int scan_ip6_net(const char *src, ot_net *net) { | |||
| 169 | int off; | 169 | int off; |
| 170 | while (isspace(*s)) | 170 | while (isspace(*s)) |
| 171 | ++s; | 171 | ++s; |
| 172 | if (!(off = ip_scan(s, net->address))) | 172 | if (!(off = ip_parse(s, net->address))) |
| 173 | return 0; | 173 | return 0; |
| 174 | s += off; | 174 | s += off; |
| 175 | if (*s != '/') | 175 | if (*s != '/') |
| 176 | net->bits = 128; | 176 | net->bits = 128; |
| 177 | else { | 177 | else { |
| 178 | s++; | 178 | s++; |
| 179 | if (!(off = scan_udec(s, &value, 128))) | 179 | if (!(off = scan_uint(s, &value, 128))) |
| 180 | return 0; | 180 | return 0; |
| 181 | net->bits = (int)value; | 181 | net->bits = (int)value; |
| 182 | if (ip_is_v4mapped(net->address)) | 182 | if (ip_is_v4mapped(net->address)) |
| @@ -264,7 +264,7 @@ static config_kind parse_config_key(char **p) { | |||
| 264 | return CONFIG_UNKNOWN; | 264 | return CONFIG_UNKNOWN; |
| 265 | } | 265 | } |
| 266 | 266 | ||
| 267 | int parse_configfile(char *config_filename) { | 267 | static 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; |
| @@ -324,12 +324,12 @@ int parse_configfile(char *config_filename) { | |||
| 324 | break; | 324 | break; |
| 325 | } | 325 | } |
| 326 | case CONFIG_LISTEN_UDP_WORKERS: | 326 | case CONFIG_LISTEN_UDP_WORKERS: |
| 327 | if (!scan_udec(p, &value, UINT_MAX)) | 327 | if (!scan_uint(p, &value, UINT_MAX)) |
| 328 | goto parse_error; | 328 | goto parse_error; |
| 329 | g_listen_workers = (int)value; | 329 | g_listen_workers = (int)value; |
| 330 | break; | 330 | break; |
| 331 | case CONFIG_LISTEN_SHARED: | 331 | case CONFIG_LISTEN_SHARED: |
| 332 | if (!scan_udec(p, &value, 1)) | 332 | if (!scan_uint(p, &value, 1)) |
| 333 | goto parse_error; | 333 | goto parse_error; |
| 334 | g_listen_shared = (int)value; | 334 | g_listen_shared = (int)value; |
| 335 | break; | 335 | break; |
| @@ -479,7 +479,7 @@ void config_parse(int argc, char **argv) { | |||
| 479 | scanon = 0; | 479 | scanon = 0; |
| 480 | break; | 480 | break; |
| 481 | case 'i': | 481 | case 'i': |
| 482 | if (!ip_scan(optarg, serverip)) { | 482 | if (!ip_parse(optarg, serverip)) { |
| 483 | usage(argv[0]); | 483 | usage(argv[0]); |
| 484 | exit(1); | 484 | exit(1); |
| 485 | } | 485 | } |
| @@ -494,7 +494,7 @@ void config_parse(int argc, char **argv) { | |||
| 494 | break; | 494 | break; |
| 495 | #endif | 495 | #endif |
| 496 | case 'p': | 496 | case 'p': |
| 497 | if (!scan_udec(optarg, &value, 65535)) { | 497 | if (!scan_uint(optarg, &value, 65535)) { |
| 498 | usage(argv[0]); | 498 | usage(argv[0]); |
| 499 | exit(1); | 499 | exit(1); |
| 500 | } | 500 | } |
| @@ -502,7 +502,7 @@ void config_parse(int argc, char **argv) { | |||
| 502 | bound++; | 502 | bound++; |
| 503 | break; | 503 | break; |
| 504 | case 'P': | 504 | case 'P': |
| 505 | if (!scan_udec(optarg, &value, 65535)) { | 505 | if (!scan_uint(optarg, &value, 65535)) { |
| 506 | usage(argv[0]); | 506 | usage(argv[0]); |
| 507 | exit(1); | 507 | exit(1); |
| 508 | } | 508 | } |
| @@ -511,7 +511,7 @@ void config_parse(int argc, char **argv) { | |||
| 511 | break; | 511 | break; |
| 512 | #ifdef WANT_SYNC_LIVE | 512 | #ifdef WANT_SYNC_LIVE |
| 513 | case 's': | 513 | case 's': |
| 514 | if (!scan_udec(optarg, &value, 65535)) { | 514 | if (!scan_uint(optarg, &value, 65535)) { |
| 515 | usage(argv[0]); | 515 | usage(argv[0]); |
| 516 | exit(1); | 516 | exit(1); |
| 517 | } | 517 | } |
| @@ -544,8 +544,11 @@ void config_parse(int argc, char **argv) { | |||
| 544 | help(argv[0]); | 544 | help(argv[0]); |
| 545 | exit(0); | 545 | exit(0); |
| 546 | case 'v': { | 546 | case 'v': { |
| 547 | char buffer[8192]; | 547 | char buffer[8192]; |
| 548 | stats_return_tracker_version(buffer); | 548 | char *cursor = buffer; |
| 549 | |||
| 550 | stats_return_tracker_version(&cursor, buffer + sizeof(buffer) - 1); | ||
| 551 | *cursor = 0; | ||
| 549 | fputs(buffer, stderr); | 552 | fputs(buffer, stderr); |
| 550 | exit(0); | 553 | exit(0); |
| 551 | } | 554 | } |
diff --git a/ot_event.c b/ot_event.c new file mode 100644 index 0000000..ce0feef --- /dev/null +++ b/ot_event.c | |||
| @@ -0,0 +1,332 @@ | |||
| 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 <errno.h> | ||
| 8 | #include <stdlib.h> | ||
| 9 | #include <string.h> | ||
| 10 | #include <unistd.h> | ||
| 11 | |||
| 12 | /* Opentracker */ | ||
| 13 | #include "ot_event.h" | ||
| 14 | |||
| 15 | /* One caller waits, so the scratch the mechanism needs is kept here rather | ||
| 16 | than allocated per call. */ | ||
| 17 | |||
| 18 | /* poll works everywhere and is what the BSDs get until kqueue is written. | ||
| 19 | Defining OT_EVENT_FORCE_POLL picks it on a platform that would otherwise | ||
| 20 | use something better, which is how both are exercised by the tests. */ | ||
| 21 | #if defined(OT_EVENT_FORCE_POLL) | ||
| 22 | #define OT_EVENT_POLL | ||
| 23 | #elif defined(OT_EVENT_FORCE_KQUEUE) | ||
| 24 | #define OT_EVENT_KQUEUE | ||
| 25 | #elif defined(__linux__) | ||
| 26 | #define OT_EVENT_EPOLL | ||
| 27 | #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__APPLE__) | ||
| 28 | #define OT_EVENT_KQUEUE | ||
| 29 | #else | ||
| 30 | #define OT_EVENT_POLL | ||
| 31 | #endif | ||
| 32 | |||
| 33 | #ifdef OT_EVENT_EPOLL | ||
| 34 | /* ------------------------------------------------------------- epoll */ | ||
| 35 | |||
| 36 | #include <sys/epoll.h> | ||
| 37 | |||
| 38 | static int g_epoll = -1; | ||
| 39 | static struct epoll_event *g_ready; | ||
| 40 | static int g_ready_space; | ||
| 41 | |||
| 42 | const char *event_mechanism(void) { return "epoll"; } | ||
| 43 | |||
| 44 | int event_init(void) { | ||
| 45 | g_ready = NULL; | ||
| 46 | g_ready_space = 0; | ||
| 47 | return (g_epoll = epoll_create1(EPOLL_CLOEXEC)) == -1 ? -1 : 0; | ||
| 48 | } | ||
| 49 | |||
| 50 | void event_deinit(void) { | ||
| 51 | if (g_epoll != -1) | ||
| 52 | close(g_epoll); | ||
| 53 | free(g_ready); | ||
| 54 | g_epoll = -1; | ||
| 55 | g_ready = NULL; | ||
| 56 | g_ready_space = 0; | ||
| 57 | } | ||
| 58 | |||
| 59 | static int event_ctl(int op, int fd, int what, uint32_t generation) { | ||
| 60 | struct epoll_event ev; | ||
| 61 | |||
| 62 | memset(&ev, 0, sizeof(ev)); | ||
| 63 | ev.events = (what & EVENT_IN ? (uint32_t)EPOLLIN : 0) | (what & EVENT_OUT ? (uint32_t)EPOLLOUT : 0); | ||
| 64 | /* The generation rides along inside the kernel, so an event says which | ||
| 65 | connection it belongs to without anybody looking it up */ | ||
| 66 | ev.data.u64 = ((uint64_t)generation << 32) | (uint32_t)fd; | ||
| 67 | return epoll_ctl(g_epoll, op, fd, &ev); | ||
| 68 | } | ||
| 69 | |||
| 70 | int event_add(int fd, int what, uint32_t generation) { return event_ctl(EPOLL_CTL_ADD, fd, what, generation); } | ||
| 71 | int event_modify(int fd, int what, uint32_t generation) { return event_ctl(EPOLL_CTL_MOD, fd, what, generation); } | ||
| 72 | int event_del(int fd) { return epoll_ctl(g_epoll, EPOLL_CTL_DEL, fd, NULL); } | ||
| 73 | |||
| 74 | int event_wait(ot_event *out, int max, int timeout_ms) { | ||
| 75 | int count, i; | ||
| 76 | |||
| 77 | if (max > g_ready_space) { | ||
| 78 | struct epoll_event *grown = realloc(g_ready, (size_t)max * sizeof(struct epoll_event)); | ||
| 79 | |||
| 80 | if (!grown) | ||
| 81 | return -1; | ||
| 82 | g_ready = grown; | ||
| 83 | g_ready_space = max; | ||
| 84 | } | ||
| 85 | |||
| 86 | while ((count = epoll_wait(g_epoll, g_ready, max, timeout_ms)) == -1 && errno == EINTR) | ||
| 87 | ; | ||
| 88 | for (i = 0; i < count; ++i) { | ||
| 89 | uint32_t events = g_ready[i].events; | ||
| 90 | |||
| 91 | out[i].fd = (int)(uint32_t)g_ready[i].data.u64; | ||
| 92 | out[i].generation = (uint32_t)(g_ready[i].data.u64 >> 32); | ||
| 93 | /* A hangup or an error is reported as both, so whichever side the caller | ||
| 94 | was waiting on gets to notice */ | ||
| 95 | out[i].what = (events & (EPOLLIN | EPOLLHUP | EPOLLERR) ? EVENT_IN : 0) | | ||
| 96 | (events & (EPOLLOUT | EPOLLHUP | EPOLLERR) ? EVENT_OUT : 0); | ||
| 97 | } | ||
| 98 | return count; | ||
| 99 | } | ||
| 100 | |||
| 101 | #endif /* OT_EVENT_EPOLL */ | ||
| 102 | |||
| 103 | #ifdef OT_EVENT_KQUEUE | ||
| 104 | /* ------------------------------------------------------------ kqueue */ | ||
| 105 | |||
| 106 | #include <sys/event.h> | ||
| 107 | #include <sys/time.h> | ||
| 108 | #include <sys/types.h> | ||
| 109 | |||
| 110 | static int g_kqueue = -1; | ||
| 111 | static struct kevent *g_ready; | ||
| 112 | static int g_ready_space; | ||
| 113 | |||
| 114 | const char *event_mechanism(void) { return "kqueue"; } | ||
| 115 | |||
| 116 | int event_init(void) { | ||
| 117 | g_ready = NULL; | ||
| 118 | g_ready_space = 0; | ||
| 119 | return (g_kqueue = kqueue()) == -1 ? -1 : 0; | ||
| 120 | } | ||
| 121 | |||
| 122 | void event_deinit(void) { | ||
| 123 | if (g_kqueue != -1) | ||
| 124 | close(g_kqueue); | ||
| 125 | free(g_ready); | ||
| 126 | g_kqueue = -1; | ||
| 127 | g_ready = NULL; | ||
| 128 | g_ready_space = 0; | ||
| 129 | } | ||
| 130 | |||
| 131 | /* kqueue keeps one registration per filter, so reading and writing are two | ||
| 132 | entries. A change of interest is an add of the one wanted and a delete | ||
| 133 | of the one not. The generation travels in udata, which the kernel passes | ||
| 134 | back untouched. */ | ||
| 135 | static int event_change(int fd, int what, uint32_t generation, int deleting) { | ||
| 136 | struct kevent change[2], result[2]; | ||
| 137 | void *udata = (void *)(uintptr_t)(((uint64_t)generation << 32) | (uint32_t)fd); | ||
| 138 | int succeeded = 0, count, i; | ||
| 139 | |||
| 140 | EV_SET(&change[0], (uintptr_t)fd, EVFILT_READ, | ||
| 141 | EV_RECEIPT | ((deleting || !(what & EVENT_IN)) ? EV_DELETE : EV_ADD), 0, 0, udata); | ||
| 142 | EV_SET(&change[1], (uintptr_t)fd, EVFILT_WRITE, | ||
| 143 | EV_RECEIPT | ((deleting || !(what & EVENT_OUT)) ? EV_DELETE : EV_ADD), 0, 0, udata); | ||
| 144 | |||
| 145 | if ((count = kevent(g_kqueue, change, 2, result, 2, NULL)) == -1) | ||
| 146 | return -1; | ||
| 147 | |||
| 148 | for (i = 0; i < count && i < 2; ++i) { | ||
| 149 | if (!result[i].data) { | ||
| 150 | ++succeeded; | ||
| 151 | continue; | ||
| 152 | } | ||
| 153 | /* Dropping interest we never registered is not a failure */ | ||
| 154 | if ((change[i].flags & EV_DELETE) && result[i].data == ENOENT) | ||
| 155 | continue; | ||
| 156 | errno = (int)result[i].data; | ||
| 157 | return -1; | ||
| 158 | } | ||
| 159 | |||
| 160 | /* Nothing at all was registered or removed, so the descriptor is unknown */ | ||
| 161 | if (!succeeded) { | ||
| 162 | errno = ENOENT; | ||
| 163 | return -1; | ||
| 164 | } | ||
| 165 | return 0; | ||
| 166 | } | ||
| 167 | |||
| 168 | int event_add(int fd, int what, uint32_t generation) { return event_change(fd, what, generation, 0); } | ||
| 169 | int event_modify(int fd, int what, uint32_t generation) { return event_change(fd, what, generation, 0); } | ||
| 170 | int event_del(int fd) { return event_change(fd, 0, 0, 1); } | ||
| 171 | |||
| 172 | int event_wait(ot_event *out, int max, int timeout_ms) { | ||
| 173 | struct timespec timeout; | ||
| 174 | struct timespec *deadline = NULL; | ||
| 175 | int count, i; | ||
| 176 | |||
| 177 | if (max > g_ready_space) { | ||
| 178 | struct kevent *grown = realloc(g_ready, (size_t)max * sizeof(struct kevent)); | ||
| 179 | |||
| 180 | if (!grown) | ||
| 181 | return -1; | ||
| 182 | g_ready = grown; | ||
| 183 | g_ready_space = max; | ||
| 184 | } | ||
| 185 | |||
| 186 | if (timeout_ms >= 0) { | ||
| 187 | timeout.tv_sec = timeout_ms / 1000; | ||
| 188 | timeout.tv_nsec = (long)(timeout_ms % 1000) * 1000000; | ||
| 189 | deadline = &timeout; | ||
| 190 | } | ||
| 191 | |||
| 192 | while ((count = kevent(g_kqueue, NULL, 0, g_ready, max, deadline)) == -1 && errno == EINTR) | ||
| 193 | ; | ||
| 194 | for (i = 0; i < count; ++i) { | ||
| 195 | uint64_t udata = (uint64_t)(uintptr_t)g_ready[i].udata; | ||
| 196 | int eof = (g_ready[i].flags & (EV_EOF | EV_ERROR)) != 0; | ||
| 197 | |||
| 198 | out[i].fd = (int)(uint32_t)udata; | ||
| 199 | out[i].generation = (uint32_t)(udata >> 32); | ||
| 200 | out[i].what = ((g_ready[i].filter == EVFILT_READ || eof) ? EVENT_IN : 0) | | ||
| 201 | ((g_ready[i].filter == EVFILT_WRITE || eof) ? EVENT_OUT : 0); | ||
| 202 | } | ||
| 203 | return count; | ||
| 204 | } | ||
| 205 | |||
| 206 | #endif /* OT_EVENT_KQUEUE */ | ||
| 207 | |||
| 208 | #ifdef OT_EVENT_POLL | ||
| 209 | /* -------------------------------------------------------------- poll */ | ||
| 210 | |||
| 211 | #include <poll.h> | ||
| 212 | |||
| 213 | /* poll wants its descriptors packed, so the registered ones live in one | ||
| 214 | array and a second maps a descriptor number to its place in it. */ | ||
| 215 | static struct pollfd *g_pfds; | ||
| 216 | static uint32_t *g_generation; | ||
| 217 | static int *g_where; | ||
| 218 | static int g_count, g_space, g_where_space; | ||
| 219 | |||
| 220 | const char *event_mechanism(void) { return "poll"; } | ||
| 221 | |||
| 222 | int event_init(void) { | ||
| 223 | g_pfds = NULL, g_generation = NULL, g_where = NULL; | ||
| 224 | g_count = g_space = g_where_space = 0; | ||
| 225 | return 0; | ||
| 226 | } | ||
| 227 | |||
| 228 | void event_deinit(void) { | ||
| 229 | free(g_pfds), free(g_generation), free(g_where); | ||
| 230 | g_pfds = NULL, g_generation = NULL, g_where = NULL; | ||
| 231 | g_count = g_space = g_where_space = 0; | ||
| 232 | } | ||
| 233 | |||
| 234 | static short event_to_poll(int what) { | ||
| 235 | return (short)((what & EVENT_IN ? POLLIN : 0) | (what & EVENT_OUT ? POLLOUT : 0)); | ||
| 236 | } | ||
| 237 | |||
| 238 | static int event_room_for(int fd) { | ||
| 239 | if (fd >= g_where_space) { | ||
| 240 | int space = g_where_space ? g_where_space : 64; | ||
| 241 | int *grown; | ||
| 242 | |||
| 243 | while (space <= fd) | ||
| 244 | space *= 2; | ||
| 245 | if (!(grown = realloc(g_where, (size_t)space * sizeof(int)))) | ||
| 246 | return -1; | ||
| 247 | while (g_where_space < space) | ||
| 248 | grown[g_where_space++] = -1; | ||
| 249 | g_where = grown; | ||
| 250 | } | ||
| 251 | |||
| 252 | if (g_count == g_space) { | ||
| 253 | int space = g_space ? g_space * 2 : 64; | ||
| 254 | struct pollfd *pfds = realloc(g_pfds, (size_t)space * sizeof(struct pollfd)); | ||
| 255 | uint32_t *gens; | ||
| 256 | |||
| 257 | if (!pfds) | ||
| 258 | return -1; | ||
| 259 | g_pfds = pfds; | ||
| 260 | if (!(gens = realloc(g_generation, (size_t)space * sizeof(uint32_t)))) | ||
| 261 | return -1; | ||
| 262 | g_generation = gens; | ||
| 263 | g_space = space; | ||
| 264 | } | ||
| 265 | return 0; | ||
| 266 | } | ||
| 267 | |||
| 268 | int event_add(int fd, int what, uint32_t generation) { | ||
| 269 | if (fd < 0 || event_room_for(fd) == -1) | ||
| 270 | return -1; | ||
| 271 | if (g_where[fd] != -1) { | ||
| 272 | errno = EEXIST; | ||
| 273 | return -1; | ||
| 274 | } | ||
| 275 | |||
| 276 | g_pfds[g_count].fd = fd; | ||
| 277 | g_pfds[g_count].events = event_to_poll(what); | ||
| 278 | g_pfds[g_count].revents = 0; | ||
| 279 | g_generation[g_count] = generation; | ||
| 280 | g_where[fd] = g_count++; | ||
| 281 | return 0; | ||
| 282 | } | ||
| 283 | |||
| 284 | int event_modify(int fd, int what, uint32_t generation) { | ||
| 285 | if (fd < 0 || fd >= g_where_space || g_where[fd] == -1) { | ||
| 286 | errno = ENOENT; | ||
| 287 | return -1; | ||
| 288 | } | ||
| 289 | g_pfds[g_where[fd]].events = event_to_poll(what); | ||
| 290 | g_generation[g_where[fd]] = generation; | ||
| 291 | return 0; | ||
| 292 | } | ||
| 293 | |||
| 294 | int event_del(int fd) { | ||
| 295 | int at; | ||
| 296 | |||
| 297 | if (fd < 0 || fd >= g_where_space || (at = g_where[fd]) == -1) { | ||
| 298 | errno = ENOENT; | ||
| 299 | return -1; | ||
| 300 | } | ||
| 301 | |||
| 302 | /* The last one moves into the gap, so the array stays packed */ | ||
| 303 | g_pfds[at] = g_pfds[--g_count]; | ||
| 304 | g_generation[at] = g_generation[g_count]; | ||
| 305 | g_where[g_pfds[at].fd] = at; | ||
| 306 | g_where[fd] = -1; | ||
| 307 | return 0; | ||
| 308 | } | ||
| 309 | |||
| 310 | int event_wait(ot_event *out, int max, int timeout_ms) { | ||
| 311 | int ready, found = 0, i; | ||
| 312 | |||
| 313 | while ((ready = poll(g_pfds, (nfds_t)g_count, timeout_ms)) == -1 && errno == EINTR) | ||
| 314 | ; | ||
| 315 | if (ready <= 0) | ||
| 316 | return ready; | ||
| 317 | |||
| 318 | for (i = 0; i < g_count && found < max; ++i) { | ||
| 319 | short revents = g_pfds[i].revents; | ||
| 320 | |||
| 321 | if (!revents) | ||
| 322 | continue; | ||
| 323 | out[found].fd = g_pfds[i].fd; | ||
| 324 | out[found].generation = g_generation[i]; | ||
| 325 | out[found].what = (revents & (POLLIN | POLLHUP | POLLERR | POLLNVAL) ? EVENT_IN : 0) | | ||
| 326 | (revents & (POLLOUT | POLLHUP | POLLERR | POLLNVAL) ? EVENT_OUT : 0); | ||
| 327 | ++found; | ||
| 328 | } | ||
| 329 | return found; | ||
| 330 | } | ||
| 331 | |||
| 332 | #endif /* OT_EVENT_POLL */ | ||
diff --git a/ot_event.h b/ot_event.h new file mode 100644 index 0000000..6023134 --- /dev/null +++ b/ot_event.h | |||
| @@ -0,0 +1,39 @@ | |||
| 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_EVENT_H__ | ||
| 7 | #define OT_EVENT_H__ | ||
| 8 | |||
| 9 | #include <stdint.h> | ||
| 10 | |||
| 11 | /* Which descriptors have become readable or writable, asked of whichever | ||
| 12 | mechanism the kernel offers. The generation a descriptor was registered | ||
| 13 | with is handed back with every event, so a caller can tell an event for | ||
| 14 | the connection it is holding from one for a connection that has since been | ||
| 15 | closed and had its number reused. */ | ||
| 16 | |||
| 17 | enum { EVENT_IN = 1, EVENT_OUT = 2 }; | ||
| 18 | |||
| 19 | typedef struct { | ||
| 20 | int fd; | ||
| 21 | uint32_t generation; | ||
| 22 | int what; | ||
| 23 | } ot_event; | ||
| 24 | |||
| 25 | int event_init(void); | ||
| 26 | void event_deinit(void); | ||
| 27 | |||
| 28 | int event_add(int fd, int what, uint32_t generation); | ||
| 29 | int event_modify(int fd, int what, uint32_t generation); | ||
| 30 | int event_del(int fd); | ||
| 31 | |||
| 32 | /* Waits at most timeout_ms, or indefinitely when that is negative, and fills | ||
| 33 | out with up to max ready descriptors. Returns how many, or -1. */ | ||
| 34 | int event_wait(ot_event *out, int max, int timeout_ms); | ||
| 35 | |||
| 36 | /* Which mechanism was compiled in, for the version string */ | ||
| 37 | const char *event_mechanism(void); | ||
| 38 | |||
| 39 | #endif | ||
diff --git a/ot_fdstate.c b/ot_fdstate.c new file mode 100644 index 0000000..537bcb0 --- /dev/null +++ b/ot_fdstate.c | |||
| @@ -0,0 +1,194 @@ | |||
| 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 | |||
| 17 | enum { 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 | |||
| 26 | typedef 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 | |||
| 37 | static ot_fdslot *g_slots; | ||
| 38 | static 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. */ | ||
| 41 | static _Atomic size_t g_slot_highest; | ||
| 42 | |||
| 43 | int 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 | |||
| 61 | void 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 | |||
| 69 | size_t fdstate_slots(void) { return g_slot_count; } | ||
| 70 | |||
| 71 | static 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 | |||
| 77 | uint32_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 | |||
| 102 | uint32_t fdstate_generation(int fd) { | ||
| 103 | ot_fdslot *slot = fdstate_slot(fd); | ||
| 104 | |||
| 105 | return slot ? FD_GEN(atomic_load_explicit(&slot->state, memory_order_relaxed)) : 0; | ||
| 106 | } | ||
| 107 | |||
| 108 | int fdstate_claim(int fd, uint32_t generation) { | ||
| 109 | ot_fdslot *slot = fdstate_slot(fd); | ||
| 110 | uint64_t word; | ||
| 111 | |||
| 112 | if (!slot) | ||
| 113 | return 0; | ||
| 114 | |||
| 115 | word = atomic_load_explicit(&slot->state, memory_order_acquire); | ||
| 116 | for (;;) { | ||
| 117 | if (FD_GEN(word) != generation || FD_ST(word) != FD_IDLE) | ||
| 118 | return 0; | ||
| 119 | if (atomic_compare_exchange_weak_explicit(&slot->state, &word, FD_SET_ST(word, FD_BUSY), memory_order_acquire, | ||
| 120 | memory_order_relaxed)) | ||
| 121 | return 1; | ||
| 122 | /* word now holds what was really there, so the test above runs again */ | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | void fdstate_release(int fd) { | ||
| 127 | ot_fdslot *slot = fdstate_slot(fd); | ||
| 128 | uint64_t word; | ||
| 129 | |||
| 130 | if (!slot) | ||
| 131 | return; | ||
| 132 | |||
| 133 | word = atomic_load_explicit(&slot->state, memory_order_relaxed); | ||
| 134 | atomic_store_explicit(&slot->state, FD_SET_ST(word, FD_IDLE), memory_order_release); | ||
| 135 | } | ||
| 136 | |||
| 137 | int fdstate_claim_expired(int fd, ot_time now) { | ||
| 138 | ot_fdslot *slot = fdstate_slot(fd); | ||
| 139 | uint64_t word; | ||
| 140 | ot_time expiry; | ||
| 141 | |||
| 142 | if (!slot) | ||
| 143 | return 0; | ||
| 144 | |||
| 145 | /* Reading before asking for the slot keeps the common case, a descriptor | ||
| 146 | that has not expired, off the bus entirely */ | ||
| 147 | word = atomic_load_explicit(&slot->state, memory_order_relaxed); | ||
| 148 | if (FD_ST(word) != FD_IDLE) | ||
| 149 | return 0; | ||
| 150 | expiry = atomic_load_explicit(&slot->expiry, memory_order_relaxed); | ||
| 151 | if (!expiry || expiry > now) | ||
| 152 | return 0; | ||
| 153 | |||
| 154 | /* Nothing can move the expiry while the slot sits at FD_IDLE, so winning | ||
| 155 | the slot is the last word */ | ||
| 156 | return atomic_compare_exchange_strong_explicit(&slot->state, &word, FD_SET_ST(word, FD_CLOSING), memory_order_acquire, | ||
| 157 | memory_order_relaxed); | ||
| 158 | } | ||
| 159 | |||
| 160 | void fdstate_close(int fd) { | ||
| 161 | ot_fdslot *slot = fdstate_slot(fd); | ||
| 162 | uint64_t word; | ||
| 163 | |||
| 164 | if (!slot) | ||
| 165 | return; | ||
| 166 | |||
| 167 | word = atomic_load_explicit(&slot->state, memory_order_relaxed); | ||
| 168 | atomic_store_explicit(&slot->cookie, NULL, memory_order_relaxed); | ||
| 169 | atomic_store_explicit(&slot->expiry, 0, memory_order_relaxed); | ||
| 170 | atomic_store_explicit(&slot->state, FD_WORD(FD_GEN(word) + 1, FD_FREE), memory_order_release); | ||
| 171 | } | ||
| 172 | |||
| 173 | int fdstate_next_expired(ot_time now, int *cursor) { | ||
| 174 | size_t highest = atomic_load_explicit(&g_slot_highest, memory_order_relaxed); | ||
| 175 | |||
| 176 | for (; (size_t)*cursor <= highest; ++*cursor) | ||
| 177 | if (fdstate_claim_expired(*cursor, now)) | ||
| 178 | return (*cursor)++; | ||
| 179 | |||
| 180 | return -1; | ||
| 181 | } | ||
| 182 | |||
| 183 | void *fdstate_cookie(int fd) { | ||
| 184 | ot_fdslot *slot = fdstate_slot(fd); | ||
| 185 | |||
| 186 | return slot ? atomic_load_explicit(&slot->cookie, memory_order_relaxed) : NULL; | ||
| 187 | } | ||
| 188 | |||
| 189 | void fdstate_set_expiry(int fd, ot_time expiry) { | ||
| 190 | ot_fdslot *slot = fdstate_slot(fd); | ||
| 191 | |||
| 192 | if (slot) | ||
| 193 | atomic_store_explicit(&slot->expiry, expiry, memory_order_relaxed); | ||
| 194 | } | ||
diff --git a/ot_fdstate.h b/ot_fdstate.h new file mode 100644 index 0000000..c09f018 --- /dev/null +++ b/ot_fdstate.h | |||
| @@ -0,0 +1,61 @@ | |||
| 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 | |||
| 26 | int fdstate_init(void); | ||
| 27 | void fdstate_deinit(void); | ||
| 28 | size_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. */ | ||
| 32 | uint32_t fdstate_open(int fd, void *cookie, ot_time expiry); | ||
| 33 | |||
| 34 | /* Which generation a descriptor currently carries, to check an event that | ||
| 35 | was handed out earlier against the connection living there now. */ | ||
| 36 | uint32_t fdstate_generation(int fd); | ||
| 37 | |||
| 38 | /* Ask for exclusive use. Returns 0 if the descriptor has been recycled since | ||
| 39 | generation was handed out, or if somebody else is using it. */ | ||
| 40 | int fdstate_claim(int fd, uint32_t generation); | ||
| 41 | void fdstate_release(int fd); | ||
| 42 | |||
| 43 | /* The cleaner's side of the same thing: takes the descriptor only if nobody | ||
| 44 | is working on it and it really has expired. Never waits. */ | ||
| 45 | int fdstate_claim_expired(int fd, ot_time now); | ||
| 46 | |||
| 47 | /* Give the descriptor up. The generation is bumped here, which has to happen | ||
| 48 | before close(2) so that the kernel cannot reissue the number while events | ||
| 49 | quoting the old generation are still in flight. */ | ||
| 50 | void fdstate_close(int fd); | ||
| 51 | |||
| 52 | /* Walks the table for descriptors that have expired, handing back one at a | ||
| 53 | time until it returns -1. The cursor belongs to the caller, so several | ||
| 54 | sweepers can run without sharing anything. */ | ||
| 55 | int fdstate_next_expired(ot_time now, int *cursor); | ||
| 56 | |||
| 57 | /* Both only valid while the caller holds the descriptor. */ | ||
| 58 | void *fdstate_cookie(int fd); | ||
| 59 | void fdstate_set_expiry(int fd, ot_time expiry); | ||
| 60 | |||
| 61 | #endif | ||
diff --git a/ot_fmt.c b/ot_fmt.c new file mode 100644 index 0000000..9cab25f --- /dev/null +++ b/ot_fmt.c | |||
| @@ -0,0 +1,42 @@ | |||
| 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 <stdarg.h> | ||
| 8 | #include <stdio.h> | ||
| 9 | #include <string.h> | ||
| 10 | |||
| 11 | /* Opentracker */ | ||
| 12 | #include "ot_fmt.h" | ||
| 13 | |||
| 14 | void fmt_append(char **cursor, const char *end, const char *format, ...) { | ||
| 15 | va_list arguments; | ||
| 16 | size_t room; | ||
| 17 | int written; | ||
| 18 | |||
| 19 | if (*cursor >= end) { | ||
| 20 | *cursor = (char *)end; | ||
| 21 | return; | ||
| 22 | } | ||
| 23 | |||
| 24 | room = (size_t)(end - *cursor); | ||
| 25 | va_start(arguments, format); | ||
| 26 | written = vsnprintf(*cursor, room, format, arguments); | ||
| 27 | va_end(arguments); | ||
| 28 | |||
| 29 | if (written < 0) | ||
| 30 | return; | ||
| 31 | *cursor = (size_t)written >= room ? (char *)end : *cursor + written; | ||
| 32 | } | ||
| 33 | |||
| 34 | void fmt_bytes(char **cursor, const char *end, const void *bytes, size_t length) { | ||
| 35 | if (*cursor >= end || (size_t)(end - *cursor) < length) { | ||
| 36 | *cursor = (char *)end; | ||
| 37 | return; | ||
| 38 | } | ||
| 39 | |||
| 40 | memcpy(*cursor, bytes, length); | ||
| 41 | *cursor += length; | ||
| 42 | } | ||
diff --git a/ot_fmt.h b/ot_fmt.h new file mode 100644 index 0000000..851b2d8 --- /dev/null +++ b/ot_fmt.h | |||
| @@ -0,0 +1,21 @@ | |||
| 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_FMT_H__ | ||
| 7 | #define OT_FMT_H__ | ||
| 8 | |||
| 9 | #include <stddef.h> | ||
| 10 | |||
| 11 | /* Appending to a cursor that walks a fixed buffer. Both take the cursor by | ||
| 12 | reference and advance it, so a reply is written as a run of statements and | ||
| 13 | no caller does pointer arithmetic of its own. */ | ||
| 14 | |||
| 15 | void fmt_append(char **cursor, const char *end, const char *format, ...) __attribute__((format(printf, 3, 4))); | ||
| 16 | |||
| 17 | /* Append raw bytes for the binary stretches of a bencoded reply. | ||
| 18 | Either everything is appended or nothing at all */ | ||
| 19 | void fmt_bytes(char **cursor, const char *end, const void *bytes, size_t length); | ||
| 20 | |||
| 21 | #endif | ||
diff --git a/ot_fullscrape.c b/ot_fullscrape.c index eade79d..8518bd2 100644 --- a/ot_fullscrape.c +++ b/ot_fullscrape.c | |||
| @@ -17,15 +17,13 @@ | |||
| 17 | #include <zstd.h> | 17 | #include <zstd.h> |
| 18 | #endif | 18 | #endif |
| 19 | 19 | ||
| 20 | /* Libowfat */ | ||
| 21 | #include "io.h" | ||
| 22 | |||
| 23 | /* Opentracker */ | 20 | /* Opentracker */ |
| 21 | #include "ot_fmt.h" | ||
| 24 | #include "ot_hex.h" | 22 | #include "ot_hex.h" |
| 25 | #include "ot_fullscrape.h" | 23 | #include "ot_fullscrape.h" |
| 26 | #include "ot_iovec.h" | 24 | #include "ot_iovec.h" |
| 27 | #include "ot_mutex.h" | 25 | #include "ot_mutex.h" |
| 28 | #include "scan_urlencoded_query.h" | 26 | #include "ot_urlencoded.h" |
| 29 | #include "trackerlogic.h" | 27 | #include "trackerlogic.h" |
| 30 | 28 | ||
| 31 | /* Fetch full scrape info for all torrents | 29 | /* Fetch full scrape info for all torrents |
| @@ -86,10 +84,15 @@ void fullscrape_deliver( int64_t sock, ot_tasktype tasktype ) { | |||
| 86 | mutex_workqueue_pushtask( sock, TASK_FULLSCRAPE | tasktype ); | 84 | mutex_workqueue_pushtask( sock, TASK_FULLSCRAPE | tasktype ); |
| 87 | } | 85 | } |
| 88 | 86 | ||
| 89 | static char * fullscrape_write_one( ot_tasktype mode, char *r, ot_torrent *torrent, ot_hash *hash ) { | 87 | static void fullscrape_write_one( ot_tasktype mode, char **cursor, ot_torrent *torrent, ot_hash *hash ) { |
| 90 | size_t seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count; | 88 | /* Callers guarantee this much room: the chunk writer keeps its low |
| 91 | size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count; | 89 | watermark that far from the end, the compressors hand over a buffer of |
| 92 | size_t down_count = torrent->peer_list6->down_count + torrent->peer_list4->down_count; | 90 | exactly that size */ |
| 91 | char *r = *cursor; | ||
| 92 | const char *r_end = r + OT_SCRAPE_MAXENTRYLEN; | ||
| 93 | size_t seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count; | ||
| 94 | size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count; | ||
| 95 | size_t down_count = torrent->peer_list6->down_count + torrent->peer_list4->down_count; | ||
| 93 | 96 | ||
| 94 | switch (mode & TASK_TASK_MASK) { | 97 | switch (mode & TASK_TASK_MASK) { |
| 95 | case TASK_FULLSCRAPE: | 98 | case TASK_FULLSCRAPE: |
| @@ -101,18 +104,18 @@ static char * fullscrape_write_one( ot_tasktype mode, char *r, ot_torrent *torre | |||
| 101 | memcpy(r, hash, sizeof(ot_hash)); | 104 | memcpy(r, hash, sizeof(ot_hash)); |
| 102 | r += sizeof(ot_hash); | 105 | r += sizeof(ot_hash); |
| 103 | /* push rest of the scrape string */ | 106 | /* push rest of the scrape string */ |
| 104 | r += sprintf(r, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", seed_count, down_count, peer_count - seed_count); | 107 | fmt_append(&r, r_end, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", seed_count, down_count, peer_count - seed_count); |
| 105 | 108 | ||
| 106 | break; | 109 | break; |
| 107 | case TASK_FULLSCRAPE_TPB_ASCII: | 110 | case TASK_FULLSCRAPE_TPB_ASCII: |
| 108 | hex_fromhash(r, *hash); | 111 | hex_from_hash(r, *hash); |
| 109 | r += 2 * sizeof(ot_hash); | 112 | r += 2 * sizeof(ot_hash); |
| 110 | r += sprintf(r, ":%zd:%zd\n", seed_count, peer_count - seed_count); | 113 | fmt_append(&r, r_end, ":%zd:%zd\n", seed_count, peer_count - seed_count); |
| 111 | break; | 114 | break; |
| 112 | case TASK_FULLSCRAPE_TPB_ASCII_PLUS: | 115 | case TASK_FULLSCRAPE_TPB_ASCII_PLUS: |
| 113 | hex_fromhash(r, *hash); | 116 | hex_from_hash(r, *hash); |
| 114 | r += 2 * sizeof(ot_hash); | 117 | r += 2 * sizeof(ot_hash); |
| 115 | r += sprintf(r, ":%zd:%zd:%zd\n", seed_count, peer_count - seed_count, down_count); | 118 | fmt_append(&r, r_end, ":%zd:%zd:%zd\n", seed_count, peer_count - seed_count, down_count); |
| 116 | break; | 119 | break; |
| 117 | case TASK_FULLSCRAPE_TPB_BINARY: | 120 | case TASK_FULLSCRAPE_TPB_BINARY: |
| 118 | memcpy(r, *hash, sizeof(ot_hash)); | 121 | memcpy(r, *hash, sizeof(ot_hash)); |
| @@ -122,16 +125,16 @@ static char * fullscrape_write_one( ot_tasktype mode, char *r, ot_torrent *torre | |||
| 122 | r += 8; | 125 | r += 8; |
| 123 | break; | 126 | break; |
| 124 | case TASK_FULLSCRAPE_TPB_URLENCODED: | 127 | case TASK_FULLSCRAPE_TPB_URLENCODED: |
| 125 | r += fmt_urlencoded(r, *hash, sizeof(ot_hash)); | 128 | r += urlencoded_format(r, *hash, sizeof(ot_hash)); |
| 126 | r += sprintf(r, ":%zd:%zd\n", seed_count, peer_count - seed_count); | 129 | fmt_append(&r, r_end, ":%zd:%zd\n", seed_count, peer_count - seed_count); |
| 127 | break; | 130 | break; |
| 128 | case TASK_FULLSCRAPE_TRACKERSTATE: | 131 | case TASK_FULLSCRAPE_TRACKERSTATE: |
| 129 | hex_fromhash(r, *hash); | 132 | hex_from_hash(r, *hash); |
| 130 | r += 2 * sizeof(ot_hash); | 133 | r += 2 * sizeof(ot_hash); |
| 131 | r += sprintf(r, ":%zd:%zd\n", torrent->peer_list6->base, down_count); | 134 | fmt_append(&r, r_end, ":%zd:%zd\n", torrent->peer_list6->base, down_count); |
| 132 | break; | 135 | break; |
| 133 | } | 136 | } |
| 134 | return r; | 137 | *cursor = r; |
| 135 | } | 138 | } |
| 136 | 139 | ||
| 137 | static void fullscrape_make(int taskid, ot_tasktype mode) { | 140 | static void fullscrape_make(int taskid, ot_tasktype mode) { |
| @@ -148,7 +151,7 @@ static void fullscrape_make(int taskid, ot_tasktype mode) { | |||
| 148 | re = r + OT_SCRAPE_CHUNK_SIZE - OT_SCRAPE_MAXENTRYLEN; | 151 | re = r + OT_SCRAPE_CHUNK_SIZE - OT_SCRAPE_MAXENTRYLEN; |
| 149 | 152 | ||
| 150 | if ((mode & TASK_TASK_MASK) == TASK_FULLSCRAPE) | 153 | if ((mode & TASK_TASK_MASK) == TASK_FULLSCRAPE) |
| 151 | r += sprintf(r, "d5:filesd"); | 154 | fmt_append(&r, (char *)iovector.iov_base + OT_SCRAPE_CHUNK_SIZE, "d5:filesd"); |
| 152 | 155 | ||
| 153 | /* For each bucket... */ | 156 | /* For each bucket... */ |
| 154 | for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) { | 157 | for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) { |
| @@ -159,7 +162,7 @@ static void fullscrape_make(int taskid, ot_tasktype mode) { | |||
| 159 | 162 | ||
| 160 | /* For each torrent in this bucket.. */ | 163 | /* For each torrent in this bucket.. */ |
| 161 | for (i = 0; i < torrents_list->size; ++i) { | 164 | for (i = 0; i < torrents_list->size; ++i) { |
| 162 | r = fullscrape_write_one(mode, r, torrents + i, &torrents[i].hash); | 165 | fullscrape_write_one(mode, &r, torrents + i, &torrents[i].hash); |
| 163 | 166 | ||
| 164 | if (r > re) { | 167 | if (r > re) { |
| 165 | iovector.iov_len = r - (char *)iovector.iov_base; | 168 | iovector.iov_len = r - (char *)iovector.iov_base; |
| @@ -190,7 +193,7 @@ static void fullscrape_make(int taskid, ot_tasktype mode) { | |||
| 190 | } | 193 | } |
| 191 | 194 | ||
| 192 | if ((mode & TASK_TASK_MASK) == TASK_FULLSCRAPE) | 195 | if ((mode & TASK_TASK_MASK) == TASK_FULLSCRAPE) |
| 193 | r += sprintf(r, "ee"); | 196 | fmt_append(&r, (char *)iovector.iov_base + OT_SCRAPE_CHUNK_SIZE, "ee"); |
| 194 | 197 | ||
| 195 | /* Send rest of data */ | 198 | /* Send rest of data */ |
| 196 | iovector.iov_len = r - (char *)iovector.iov_base; | 199 | iovector.iov_len = r - (char *)iovector.iov_base; |
| @@ -233,7 +236,8 @@ static void fullscrape_make_gzip(int taskid, ot_tasktype mode) { | |||
| 233 | /* For each torrent in this bucket.. */ | 236 | /* For each torrent in this bucket.. */ |
| 234 | for (i = 0; i < torrents_list->size; ++i) { | 237 | for (i = 0; i < torrents_list->size; ++i) { |
| 235 | char compress_buffer[OT_SCRAPE_MAXENTRYLEN]; | 238 | char compress_buffer[OT_SCRAPE_MAXENTRYLEN]; |
| 236 | r = fullscrape_write_one(mode, compress_buffer, torrents + i, &torrents[i].hash); | 239 | r = compress_buffer; |
| 240 | fullscrape_write_one(mode, &r, torrents + i, &torrents[i].hash); | ||
| 237 | strm.next_in = (uint8_t *)compress_buffer; | 241 | strm.next_in = (uint8_t *)compress_buffer; |
| 238 | strm.avail_in = r - compress_buffer; | 242 | strm.avail_in = r - compress_buffer; |
| 239 | zres = deflate(&strm, Z_NO_FLUSH); | 243 | zres = deflate(&strm, Z_NO_FLUSH); |
| @@ -296,7 +300,7 @@ static void fullscrape_make_gzip(int taskid, ot_tasktype mode) { | |||
| 296 | iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE); | 300 | iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE); |
| 297 | 301 | ||
| 298 | if (!iovector.iov_base) { | 302 | if (!iovector.iov_base) { |
| 299 | fprintf(stderr, "Problem with iovec_fix_increase_or_free\n"); | 303 | fprintf(stderr, "Problem with iovec_next_chunk\n"); |
| 300 | deflateEnd(&strm); | 304 | deflateEnd(&strm); |
| 301 | return; | 305 | return; |
| 302 | } | 306 | } |
| @@ -362,7 +366,8 @@ static void fullscrape_make_zstd(int taskid, ot_tasktype mode) { | |||
| 362 | /* For each torrent in this bucket.. */ | 366 | /* For each torrent in this bucket.. */ |
| 363 | for (i = 0; i < torrents_list->size; ++i) { | 367 | for (i = 0; i < torrents_list->size; ++i) { |
| 364 | char compress_buffer[OT_SCRAPE_MAXENTRYLEN]; | 368 | char compress_buffer[OT_SCRAPE_MAXENTRYLEN]; |
| 365 | r = fullscrape_write_one(mode, compress_buffer, torrents + i, &torrents[i].hash); | 369 | r = compress_buffer; |
| 370 | fullscrape_write_one(mode, &r, torrents + i, &torrents[i].hash); | ||
| 366 | inbuf.src = compress_buffer; | 371 | inbuf.src = compress_buffer; |
| 367 | inbuf.size = r - compress_buffer; | 372 | inbuf.size = r - compress_buffer; |
| 368 | inbuf.pos = 0; | 373 | inbuf.pos = 0; |
| @@ -422,7 +427,7 @@ static void fullscrape_make_zstd(int taskid, ot_tasktype mode) { | |||
| 422 | iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE); | 427 | iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE); |
| 423 | 428 | ||
| 424 | if (!iovector.iov_base) { | 429 | if (!iovector.iov_base) { |
| 425 | fprintf(stderr, "Problem with iovec_fix_increase_or_free\n"); | 430 | fprintf(stderr, "Problem with iovec_next_chunk\n"); |
| 426 | ZSTD_freeCCtx(zstream); | 431 | ZSTD_freeCCtx(zstream); |
| 427 | return; | 432 | return; |
| 428 | } | 433 | } |
| @@ -23,7 +23,7 @@ int hex_value(int c) { | |||
| 23 | 23 | ||
| 24 | int hex_isdigit(int c) { return hex_value(c) >= 0; } | 24 | int hex_isdigit(int c) { return hex_value(c) >= 0; } |
| 25 | 25 | ||
| 26 | char *hex_fromhash(char *dest, const uint8_t *hash) { | 26 | char *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 | ||
| 39 | int hex_tohash(ot_hash hash, const char *src) { | 39 | int 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 | ||
| @@ -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. */ |
| 15 | char *hex_fromhash(char *dest, const uint8_t *hash); | 15 | char *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. */ |
| 20 | int hex_tohash(ot_hash hash, const char *src); | 20 | int 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. */ |
| 23 | int hex_isdigit(int c); | 23 | int hex_isdigit(int c); |
| @@ -14,9 +14,6 @@ | |||
| 14 | #include <sys/types.h> | 14 | #include <sys/types.h> |
| 15 | #include <unistd.h> | 15 | #include <unistd.h> |
| 16 | 16 | ||
| 17 | /* Libowfat */ | ||
| 18 | #include "io.h" | ||
| 19 | |||
| 20 | /* Opentracker */ | 17 | /* Opentracker */ |
| 21 | #include "ot_accesslist.h" | 18 | #include "ot_accesslist.h" |
| 22 | #include "ot_fullscrape.h" | 19 | #include "ot_fullscrape.h" |
| @@ -25,8 +22,11 @@ | |||
| 25 | #include "ot_ip.h" | 22 | #include "ot_ip.h" |
| 26 | #include "ot_mutex.h" | 23 | #include "ot_mutex.h" |
| 27 | #include "ot_scan.h" | 24 | #include "ot_scan.h" |
| 25 | #include "ot_event.h" | ||
| 26 | #include "ot_fdstate.h" | ||
| 27 | #include "ot_fmt.h" | ||
| 28 | #include "ot_stats.h" | 28 | #include "ot_stats.h" |
| 29 | #include "scan_urlencoded_query.h" | 29 | #include "ot_urlencoded.h" |
| 30 | #include "trackerlogic.h" | 30 | #include "trackerlogic.h" |
| 31 | 31 | ||
| 32 | #define OT_MAXMULTISCRAPE_COUNT 64 | 32 | #define OT_MAXMULTISCRAPE_COUNT 64 |
| @@ -38,11 +38,11 @@ ssize_t g_stats_path_len; | |||
| 38 | enum { SUCCESS_HTTP_HEADER_LENGTH = 80, SUCCESS_HTTP_SIZE_OFF = 17 }; | 38 | enum { SUCCESS_HTTP_HEADER_LENGTH = 80, SUCCESS_HTTP_SIZE_OFF = 17 }; |
| 39 | 39 | ||
| 40 | static void http_senddata(const int64_t sock, struct ot_workstruct *ws) { | 40 | static void http_senddata(const int64_t sock, struct ot_workstruct *ws) { |
| 41 | struct http_data *cookie = io_getcookie(sock); | 41 | struct http_data *cookie = fdstate_cookie(sock); |
| 42 | ssize_t written_size; | 42 | ssize_t written_size; |
| 43 | 43 | ||
| 44 | if (!cookie) { | 44 | if (!cookie) { |
| 45 | io_close(sock); | 45 | event_del((int)sock), close((int)sock); |
| 46 | return; | 46 | return; |
| 47 | } | 47 | } |
| 48 | 48 | ||
| @@ -66,18 +66,17 @@ static void http_senddata(const int64_t sock, struct ot_workstruct *ws) { | |||
| 66 | if ((written_size < 0) || ((written_size == ws->reply_size) && !ws->keep_alive)) { | 66 | if ((written_size < 0) || ((written_size == ws->reply_size) && !ws->keep_alive)) { |
| 67 | buf_reset(&cookie->request); | 67 | buf_reset(&cookie->request); |
| 68 | free(cookie); | 68 | free(cookie); |
| 69 | io_close(sock); | 69 | event_del((int)sock), close((int)sock); |
| 70 | return; | 70 | return; |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | if (written_size < ws->reply_size) { | 73 | if (written_size < ws->reply_size) { |
| 74 | char *outbuf; | 74 | char *outbuf; |
| 75 | tai6464 t; | ||
| 76 | 75 | ||
| 77 | if (!(outbuf = malloc(ws->reply_size - written_size))) { | 76 | if (!(outbuf = malloc(ws->reply_size - written_size))) { |
| 78 | buf_reset(&cookie->request); | 77 | buf_reset(&cookie->request); |
| 79 | free(cookie); | 78 | free(cookie); |
| 80 | io_close(sock); | 79 | event_del((int)sock), close((int)sock); |
| 81 | return; | 80 | return; |
| 82 | } | 81 | } |
| 83 | 82 | ||
| @@ -88,7 +87,7 @@ static void http_senddata(const int64_t sock, struct ot_workstruct *ws) { | |||
| 88 | free(outbuf); | 87 | free(outbuf); |
| 89 | buf_reset(&cookie->request); | 88 | buf_reset(&cookie->request); |
| 90 | free(cookie); | 89 | free(cookie); |
| 91 | io_close(sock); | 90 | event_del((int)sock), close((int)sock); |
| 92 | return; | 91 | return; |
| 93 | } | 92 | } |
| 94 | } | 93 | } |
| @@ -97,11 +96,10 @@ static void http_senddata(const int64_t sock, struct ot_workstruct *ws) { | |||
| 97 | 96 | ||
| 98 | /* writeable short data sockets just have a tcp timeout */ | 97 | /* writeable short data sockets just have a tcp timeout */ |
| 99 | if (!ws->keep_alive) { | 98 | if (!ws->keep_alive) { |
| 100 | taia_uint(&t, 0); | 99 | fdstate_set_expiry(sock, 0); |
| 101 | io_timeout(sock, t); | 100 | http_interest_drop(sock, cookie, EVENT_IN); |
| 102 | io_dontwantread(sock); | ||
| 103 | } | 101 | } |
| 104 | io_wantwrite(sock); | 102 | http_interest_add(sock, cookie, EVENT_OUT); |
| 105 | } | 103 | } |
| 106 | } | 104 | } |
| 107 | 105 | ||
| @@ -114,6 +112,21 @@ static void http_senddata(const int64_t sock, struct ot_workstruct *ws) { | |||
| 114 | #define HTTPERROR_403_IP return http_issue_error(sock, ws, CODE_HTTPERROR_403_IP) | 112 | #define HTTPERROR_403_IP return http_issue_error(sock, ws, CODE_HTTPERROR_403_IP) |
| 115 | #define HTTPERROR_404 return http_issue_error(sock, ws, CODE_HTTPERROR_404) | 113 | #define HTTPERROR_404 return http_issue_error(sock, ws, CODE_HTTPERROR_404) |
| 116 | #define HTTPERROR_500 return http_issue_error(sock, ws, CODE_HTTPERROR_500) | 114 | #define HTTPERROR_500 return http_issue_error(sock, ws, CODE_HTTPERROR_500) |
| 115 | static void http_interest(const int64_t sock, struct http_data *cookie, int what) { | ||
| 116 | if (cookie->interest == what) | ||
| 117 | return; | ||
| 118 | cookie->interest = what; | ||
| 119 | event_modify((int)sock, what, fdstate_generation((int)sock)); | ||
| 120 | } | ||
| 121 | |||
| 122 | void http_interest_add(const int64_t sock, struct http_data *cookie, int what) { | ||
| 123 | http_interest(sock, cookie, cookie->interest | what); | ||
| 124 | } | ||
| 125 | |||
| 126 | void http_interest_drop(const int64_t sock, struct http_data *cookie, int what) { | ||
| 127 | http_interest(sock, cookie, cookie->interest & ~what); | ||
| 128 | } | ||
| 129 | |||
| 117 | ssize_t http_issue_error(const int64_t sock, struct ot_workstruct *ws, int code) { | 130 | ssize_t http_issue_error(const int64_t sock, struct ot_workstruct *ws, int code) { |
| 118 | const char *error_code[] = {"302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", "402 Payment Required", | 131 | const char *error_code[] = {"302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", "402 Payment Required", |
| 119 | "403 Not Modest", "403 Access Denied", "404 Not Found", "500 Internal Server Error"}; | 132 | "403 Not Modest", "403 Access Denied", "404 Not Found", "500 Internal Server Error"}; |
| @@ -135,11 +148,10 @@ ssize_t http_issue_error(const int64_t sock, struct ot_workstruct *ws, int code) | |||
| 135 | } | 148 | } |
| 136 | 149 | ||
| 137 | ssize_t http_sendiovecdata(const int64_t sock, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial) { | 150 | ssize_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); | 151 | struct http_data *cookie = fdstate_cookie(sock); |
| 139 | char *header; | 152 | char *header; |
| 140 | const char *encoding = ""; | 153 | const char *encoding = ""; |
| 141 | size_t header_size, size = iovec_length(&iovec_entries, (const struct iovec **)&iovector); | 154 | size_t header_size, size = iovec_length(&iovec_entries, (const struct iovec **)&iovector); |
| 142 | tai6464 t; | ||
| 143 | 155 | ||
| 144 | /* No cookie? Bad socket. Leave. */ | 156 | /* No cookie? Bad socket. Leave. */ |
| 145 | if (!cookie) { | 157 | if (!cookie) { |
| @@ -159,7 +171,7 @@ ssize_t http_sendiovecdata(const int64_t sock, struct ot_workstruct *ws, size_t | |||
| 159 | 171 | ||
| 160 | /* If we came here, wait for the answer is over */ | 172 | /* If we came here, wait for the answer is over */ |
| 161 | if (cookie->flag & STRUCT_HTTP_FLAG_WAITINGFORTASK) { | 173 | if (cookie->flag & STRUCT_HTTP_FLAG_WAITINGFORTASK) { |
| 162 | io_dontwantread(sock); | 174 | http_interest_drop(sock, cookie, EVENT_IN); |
| 163 | cookie->flag &= ~STRUCT_HTTP_FLAG_WAITINGFORTASK; | 175 | cookie->flag &= ~STRUCT_HTTP_FLAG_WAITINGFORTASK; |
| 164 | } | 176 | } |
| 165 | 177 | ||
| @@ -209,11 +221,8 @@ ssize_t http_sendiovecdata(const int64_t sock, struct ot_workstruct *ws, size_t | |||
| 209 | cookie->flag &= ~STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER; | 221 | cookie->flag &= ~STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER; |
| 210 | } | 222 | } |
| 211 | 223 | ||
| 212 | /* writeable sockets timeout after 10 minutes */ | 224 | fdstate_set_expiry(sock, g_now_seconds + OT_CLIENT_TIMEOUT_SEND); |
| 213 | taia_now(&t); | 225 | http_interest_add(sock, cookie, EVENT_OUT); |
| 214 | taia_addsec(&t, &t, OT_CLIENT_TIMEOUT_SEND); | ||
| 215 | io_timeout(sock, t); | ||
| 216 | io_wantwrite(sock); | ||
| 217 | return 0; | 226 | return 0; |
| 218 | } | 227 | } |
| 219 | 228 | ||
| @@ -248,7 +257,8 @@ 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}, | 257 | {"url", TASK_FULLSCRAPE_TPB_URLENCODED}, {"txt", TASK_FULLSCRAPE_TPB_ASCII}, |
| 249 | {"txtp", TASK_FULLSCRAPE_TPB_ASCII_PLUS}, {NULL, -3}}; | 258 | {"txtp", TASK_FULLSCRAPE_TPB_ASCII_PLUS}, {NULL, -3}}; |
| 250 | 259 | ||
| 251 | struct http_data *cookie = io_getcookie(sock); | 260 | struct http_data *cookie = fdstate_cookie(sock); |
| 261 | char *reply; | ||
| 252 | int mode = TASK_STATS_PEERS, scanon = 1, format = 0; | 262 | int mode = TASK_STATS_PEERS, scanon = 1, format = 0; |
| 253 | 263 | ||
| 254 | /* A socket without one is a dead socket, which is what http_sendiovecdata | 264 | /* A socket without one is a dead socket, which is what http_sendiovecdata |
| @@ -262,21 +272,21 @@ static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, c | |||
| 262 | #endif | 272 | #endif |
| 263 | 273 | ||
| 264 | while (scanon) { | 274 | while (scanon) { |
| 265 | switch (scan_find_keywords(keywords_main, &read_ptr, SCAN_SEARCHPATH_PARAM)) { | 275 | switch (urlencoded_match_keyword(keywords_main, &read_ptr, SCAN_SEARCHPATH_PARAM)) { |
| 266 | case -2: | 276 | case -2: |
| 267 | scanon = 0; | 277 | scanon = 0; |
| 268 | break; /* TERMINATOR */ | 278 | break; /* TERMINATOR */ |
| 269 | case -1: | 279 | case -1: |
| 270 | HTTPERROR_400_PARAM; /* PARSE ERROR */ | 280 | HTTPERROR_400_PARAM; /* PARSE ERROR */ |
| 271 | case -3: | 281 | case -3: |
| 272 | scan_urlencoded_skipvalue(&read_ptr); | 282 | urlencoded_skip_value(&read_ptr); |
| 273 | break; | 283 | break; |
| 274 | case 1: /* matched "mode" */ | 284 | case 1: /* matched "mode" */ |
| 275 | if ((mode = scan_find_keywords(keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) | 285 | if ((mode = urlencoded_match_keyword(keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) |
| 276 | HTTPERROR_400_PARAM; | 286 | HTTPERROR_400_PARAM; |
| 277 | break; | 287 | break; |
| 278 | case 2: /* matched "format" */ | 288 | case 2: /* matched "format" */ |
| 279 | if ((format = scan_find_keywords(keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) | 289 | if ((format = urlencoded_match_keyword(keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) |
| 280 | HTTPERROR_400_PARAM; | 290 | HTTPERROR_400_PARAM; |
| 281 | break; | 291 | break; |
| 282 | case 3: | 292 | case 3: |
| @@ -291,7 +301,6 @@ static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, c | |||
| 291 | } | 301 | } |
| 292 | 302 | ||
| 293 | if (mode == TASK_STATS_TPB) { | 303 | if (mode == TASK_STATS_TPB) { |
| 294 | tai6464 t; | ||
| 295 | #ifdef WANT_COMPRESSION_GZIP | 304 | #ifdef WANT_COMPRESSION_GZIP |
| 296 | ws->request[ws->request_size - 1] = 0; | 305 | ws->request[ws->request_size - 1] = 0; |
| 297 | #ifndef WANT_COMPRESSION_GZIP_ALWAYS | 306 | #ifndef WANT_COMPRESSION_GZIP_ALWAYS |
| @@ -307,29 +316,28 @@ 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; | 316 | cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED; |
| 308 | 317 | ||
| 309 | /* Clients waiting for us should not easily timeout */ | 318 | /* Clients waiting for us should not easily timeout */ |
| 310 | taia_uint(&t, 0); | 319 | fdstate_set_expiry(sock, 0); |
| 311 | io_timeout(sock, t); | ||
| 312 | fullscrape_deliver(sock, format); | 320 | fullscrape_deliver(sock, format); |
| 313 | io_dontwantread(sock); | 321 | http_interest_drop(sock, cookie, EVENT_IN); |
| 314 | return ws->reply_size = -2; | 322 | return ws->reply_size = -2; |
| 315 | } | 323 | } |
| 316 | #endif | 324 | #endif |
| 317 | 325 | ||
| 318 | /* default format for now */ | 326 | /* default format for now */ |
| 319 | if ((mode & TASK_CLASS_MASK) == TASK_STATS) { | 327 | if ((mode & TASK_CLASS_MASK) == TASK_STATS) { |
| 320 | tai6464 t; | ||
| 321 | 328 | ||
| 322 | cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK; | 329 | cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK; |
| 323 | 330 | ||
| 324 | /* Complex stats also include expensive memory debugging tools */ | 331 | /* Complex stats also include expensive memory debugging tools */ |
| 325 | taia_uint(&t, 0); | 332 | fdstate_set_expiry(sock, 0); |
| 326 | io_timeout(sock, t); | ||
| 327 | stats_deliver(sock, mode); | 333 | stats_deliver(sock, mode); |
| 328 | return ws->reply_size = -2; | 334 | return ws->reply_size = -2; |
| 329 | } | 335 | } |
| 330 | 336 | ||
| 331 | /* Simple stats can be answerred immediately */ | 337 | /* Simple stats can be answerred immediately */ |
| 332 | return ws->reply_size = return_stats_for_tracker(ws->reply, mode, 0); | 338 | reply = ws->reply; |
| 339 | return_stats_for_tracker(&reply, ws->outbuf + G_OUTBUF_SIZE, mode, 0); | ||
| 340 | return ws->reply_size = reply - ws->reply; | ||
| 333 | } | 341 | } |
| 334 | 342 | ||
| 335 | #ifdef WANT_MODEST_FULLSCRAPES | 343 | #ifdef WANT_MODEST_FULLSCRAPES |
| @@ -343,9 +351,8 @@ typedef struct { | |||
| 343 | 351 | ||
| 344 | #ifdef WANT_FULLSCRAPE | 352 | #ifdef WANT_FULLSCRAPE |
| 345 | static ssize_t http_handle_fullscrape(const int64_t sock, struct ot_workstruct *ws) { | 353 | static ssize_t http_handle_fullscrape(const int64_t sock, struct ot_workstruct *ws) { |
| 346 | struct http_data *cookie = io_getcookie(sock); | 354 | struct http_data *cookie = fdstate_cookie(sock); |
| 347 | int format = 0; | 355 | int format = 0; |
| 348 | tai6464 t; | ||
| 349 | 356 | ||
| 350 | #ifdef WANT_MODEST_FULLSCRAPES | 357 | #ifdef WANT_MODEST_FULLSCRAPES |
| 351 | { | 358 | { |
| @@ -404,10 +411,9 @@ static ssize_t http_handle_fullscrape(const int64_t sock, struct ot_workstruct * | |||
| 404 | /* Pass this task to the worker thread */ | 411 | /* Pass this task to the worker thread */ |
| 405 | cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED; | 412 | cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED; |
| 406 | /* Clients waiting for us should not easily timeout */ | 413 | /* Clients waiting for us should not easily timeout */ |
| 407 | taia_uint(&t, 0); | 414 | fdstate_set_expiry(sock, 0); |
| 408 | io_timeout(sock, t); | ||
| 409 | fullscrape_deliver(sock, format); | 415 | fullscrape_deliver(sock, format); |
| 410 | io_dontwantread(sock); | 416 | http_interest_drop(sock, cookie, EVENT_IN); |
| 411 | return ws->reply_size = -2; | 417 | return ws->reply_size = -2; |
| 412 | } | 418 | } |
| 413 | #endif | 419 | #endif |
| @@ -415,9 +421,10 @@ static ssize_t http_handle_fullscrape(const int64_t sock, struct ot_workstruct * | |||
| 415 | static ssize_t http_handle_scrape(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) { | 421 | static ssize_t http_handle_scrape(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) { |
| 416 | static const ot_keywords keywords_scrape[] = {{"info_hash", 1}, {NULL, -3}}; | 422 | static const ot_keywords keywords_scrape[] = {{"info_hash", 1}, {NULL, -3}}; |
| 417 | 423 | ||
| 418 | ot_hash *multiscrape_buf = (ot_hash *)ws->request; | 424 | ot_hash *multiscrape_buf = (ot_hash *)ws->request; |
| 419 | int scanon = 1; | 425 | char *reply = ws->reply; |
| 420 | size_t numwant = 0; | 426 | int scanon = 1; |
| 427 | size_t numwant = 0; | ||
| 421 | 428 | ||
| 422 | /* This is to hack around stupid clients that send "scrape ?info_hash" */ | 429 | /* This is to hack around stupid clients that send "scrape ?info_hash" */ |
| 423 | if (read_ptr[-1] != '?') { | 430 | if (read_ptr[-1] != '?') { |
| @@ -429,18 +436,18 @@ static ssize_t http_handle_scrape(const int64_t sock, struct ot_workstruct *ws, | |||
| 429 | } | 436 | } |
| 430 | 437 | ||
| 431 | while (scanon) { | 438 | while (scanon) { |
| 432 | switch (scan_find_keywords(keywords_scrape, &read_ptr, SCAN_SEARCHPATH_PARAM)) { | 439 | switch (urlencoded_match_keyword(keywords_scrape, &read_ptr, SCAN_SEARCHPATH_PARAM)) { |
| 433 | case -2: | 440 | case -2: |
| 434 | scanon = 0; | 441 | scanon = 0; |
| 435 | break; /* TERMINATOR */ | 442 | break; /* TERMINATOR */ |
| 436 | default: | 443 | default: |
| 437 | HTTPERROR_400_PARAM; /* PARSE ERROR */ | 444 | HTTPERROR_400_PARAM; /* PARSE ERROR */ |
| 438 | case -3: | 445 | case -3: |
| 439 | scan_urlencoded_skipvalue(&read_ptr); | 446 | urlencoded_skip_value(&read_ptr); |
| 440 | break; | 447 | break; |
| 441 | case 1: /* matched "info_hash" */ | 448 | case 1: /* matched "info_hash" */ |
| 442 | /* ignore this, when we have less than 20 bytes */ | 449 | /* 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)) | 450 | if (urlencoded_parse(&read_ptr, (char *)(multiscrape_buf + numwant++), SCAN_SEARCHPATH_VALUE) != (ssize_t)sizeof(ot_hash)) |
| 444 | HTTPERROR_400_PARAM; | 451 | HTTPERROR_400_PARAM; |
| 445 | break; | 452 | break; |
| 446 | } | 453 | } |
| @@ -455,7 +462,8 @@ static ssize_t http_handle_scrape(const int64_t sock, struct ot_workstruct *ws, | |||
| 455 | numwant = OT_MAXMULTISCRAPE_COUNT; | 462 | numwant = OT_MAXMULTISCRAPE_COUNT; |
| 456 | 463 | ||
| 457 | /* Enough for http header + whole scrape string */ | 464 | /* Enough for http header + whole scrape string */ |
| 458 | ws->reply_size = return_tcp_scrape_for_torrent((const ot_hash *)multiscrape_buf, numwant, ws->reply); | 465 | return_tcp_scrape_for_torrent((const ot_hash *)multiscrape_buf, numwant, &reply, ws->outbuf + G_OUTBUF_SIZE); |
| 466 | ws->reply_size = reply - ws->reply; | ||
| 459 | stats_issue_event(EVENT_SCRAPE, FLAG_TCP, ws->reply_size); | 467 | stats_issue_event(EVENT_SCRAPE, FLAG_TCP, ws->reply_size); |
| 460 | return ws->reply_size; | 468 | return ws->reply_size; |
| 461 | } | 469 | } |
| @@ -491,13 +499,24 @@ static ot_keywords keywords_announce[] = {{"port", 1}, {"left", 2}, {"event", | |||
| 491 | #endif | 499 | #endif |
| 492 | {"peer_id", 9}, {NULL, -3}}; | 500 | {"peer_id", 9}, {NULL, -3}}; |
| 493 | static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}}; | 501 | static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}}; |
| 502 | |||
| 503 | /* For the replies that are one fixed string and nothing else, where the only | ||
| 504 | thing the caller wants back is how long it turned out */ | ||
| 505 | static ssize_t reply_literal(struct ot_workstruct *ws, const char *text) { | ||
| 506 | char *reply = ws->reply; | ||
| 507 | |||
| 508 | fmt_bytes(&reply, ws->outbuf + G_OUTBUF_SIZE, text, strlen(text)); | ||
| 509 | return reply - ws->reply; | ||
| 510 | } | ||
| 511 | |||
| 494 | static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) { | 512 | static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) { |
| 495 | size_t numwant; | 513 | size_t numwant; |
| 496 | int tmp, scanon; | 514 | int64_t tmp; |
| 515 | int scanon; | ||
| 497 | unsigned short port = 0; | 516 | unsigned short port = 0; |
| 498 | char *write_ptr; | 517 | char *write_ptr; |
| 499 | ssize_t len; | 518 | ssize_t len; |
| 500 | struct http_data *cookie = io_getcookie(sock); | 519 | struct http_data *cookie = fdstate_cookie(sock); |
| 501 | 520 | ||
| 502 | /* This is to hack around stupid clients that send "announce ?info_hash" */ | 521 | /* This is to hack around stupid clients that send "announce ?info_hash" */ |
| 503 | if (read_ptr[-1] != '?') { | 522 | if (read_ptr[-1] != '?') { |
| @@ -512,7 +531,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct | |||
| 512 | if (accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_PROXY)) { | 531 | if (accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_PROXY)) { |
| 513 | ot_ip6 proxied_ip; | 532 | ot_ip6 proxied_ip; |
| 514 | char *fwd = http_header(ws->request, ws->header_size, "x-forwarded-for"); | 533 | char *fwd = http_header(ws->request, ws->header_size, "x-forwarded-for"); |
| 515 | if (fwd && ip_scan(fwd, proxied_ip)) { | 534 | if (fwd && ip_parse(fwd, proxied_ip)) { |
| 516 | OT_SETIP(ws->peer, proxied_ip); | 535 | OT_SETIP(ws->peer, proxied_ip); |
| 517 | } else | 536 | } else |
| 518 | OT_SETIP(ws->peer, cookie->ip); | 537 | OT_SETIP(ws->peer, cookie->ip); |
| @@ -529,32 +548,32 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct | |||
| 529 | scanon = 1; | 548 | scanon = 1; |
| 530 | 549 | ||
| 531 | while (scanon) { | 550 | while (scanon) { |
| 532 | switch (scan_find_keywords(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM)) { | 551 | switch (urlencoded_match_keyword(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM)) { |
| 533 | case -2: | 552 | case -2: |
| 534 | scanon = 0; | 553 | scanon = 0; |
| 535 | break; /* TERMINATOR */ | 554 | break; /* TERMINATOR */ |
| 536 | case -1: | 555 | case -1: |
| 537 | HTTPERROR_400_PARAM; /* PARSE ERROR */ | 556 | HTTPERROR_400_PARAM; /* PARSE ERROR */ |
| 538 | case -3: | 557 | case -3: |
| 539 | scan_urlencoded_skipvalue(&read_ptr); | 558 | urlencoded_skip_value(&read_ptr); |
| 540 | break; | 559 | break; |
| 541 | case 1: /* matched "port" */ | 560 | case 1: /* matched "port" */ |
| 542 | len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); | 561 | 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)) | 562 | if ((len <= 0) || scan_int(write_ptr, len, &tmp) || (tmp > 0xffff)) |
| 544 | HTTPERROR_400_PARAM; | 563 | HTTPERROR_400_PARAM; |
| 545 | port = htons(tmp); | 564 | port = htons((uint16_t)tmp); |
| 546 | OT_SETPORT(&ws->peer, &port); | 565 | OT_SETPORT(&ws->peer, &port); |
| 547 | break; | 566 | break; |
| 548 | case 2: /* matched "left" */ | 567 | case 2: /* matched "left" */ |
| 549 | if ((len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) | 568 | if ((len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) |
| 550 | HTTPERROR_400_PARAM; | 569 | HTTPERROR_400_PARAM; |
| 551 | if (scan_fixed_int(write_ptr, len, &tmp)) | 570 | if (scan_int(write_ptr, len, &tmp)) |
| 552 | tmp = 0; | 571 | tmp = 0; |
| 553 | if (!tmp) | 572 | if (!tmp) |
| 554 | OT_PEERFLAG(&ws->peer) |= PEER_FLAG_SEEDING; | 573 | OT_PEERFLAG(&ws->peer) |= PEER_FLAG_SEEDING; |
| 555 | break; | 574 | break; |
| 556 | case 3: /* matched "event" */ | 575 | case 3: /* matched "event" */ |
| 557 | switch (scan_find_keywords(keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE)) { | 576 | switch (urlencoded_match_keyword(keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE)) { |
| 558 | case -1: | 577 | case -1: |
| 559 | HTTPERROR_400_PARAM; | 578 | HTTPERROR_400_PARAM; |
| 560 | case 1: /* matched "completed" */ | 579 | case 1: /* matched "completed" */ |
| @@ -568,18 +587,18 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct | |||
| 568 | } | 587 | } |
| 569 | break; | 588 | break; |
| 570 | case 4: /* matched "numwant" */ | 589 | case 4: /* matched "numwant" */ |
| 571 | len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); | 590 | len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); |
| 572 | if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp)) | 591 | if ((len <= 0) || scan_int(write_ptr, len, &tmp)) |
| 573 | HTTPERROR_400_PARAM; | 592 | HTTPERROR_400_PARAM; |
| 574 | if (tmp < 0) | 593 | if (tmp < 0) |
| 575 | tmp = 50; | 594 | tmp = 50; |
| 576 | if (tmp > 200) | 595 | if (tmp > 200) |
| 577 | tmp = 200; | 596 | tmp = 200; |
| 578 | numwant = tmp; | 597 | numwant = (size_t)tmp; |
| 579 | break; | 598 | break; |
| 580 | case 5: /* matched "compact" */ | 599 | case 5: /* matched "compact" */ |
| 581 | len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); | 600 | len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); |
| 582 | if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp)) | 601 | if ((len <= 0) || scan_int(write_ptr, len, &tmp)) |
| 583 | HTTPERROR_400_PARAM; | 602 | HTTPERROR_400_PARAM; |
| 584 | if (!tmp) | 603 | if (!tmp) |
| 585 | HTTPERROR_400_COMPACT; | 604 | HTTPERROR_400_COMPACT; |
| @@ -588,7 +607,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct | |||
| 588 | if (ws->hash) | 607 | if (ws->hash) |
| 589 | HTTPERROR_400_DOUBLEHASH; | 608 | HTTPERROR_400_DOUBLEHASH; |
| 590 | /* ignore this, when we have less than 20 bytes */ | 609 | /* ignore this, when we have less than 20 bytes */ |
| 591 | if (scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20) | 610 | if (urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20) |
| 592 | HTTPERROR_400_PARAM; | 611 | HTTPERROR_400_PARAM; |
| 593 | ws->hash = (ot_hash *)write_ptr; | 612 | ws->hash = (ot_hash *)write_ptr; |
| 594 | break; | 613 | break; |
| @@ -596,9 +615,9 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct | |||
| 596 | case 7: /* matched "ip" */ | 615 | case 7: /* matched "ip" */ |
| 597 | { | 616 | { |
| 598 | char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16; | 617 | char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16; |
| 599 | len = scan_urlencoded_query(&read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE); | 618 | len = urlencoded_parse(&read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE); |
| 600 | tmp_buf2[len] = 0; | 619 | tmp_buf2[len] = 0; |
| 601 | if ((len <= 0) || !ip_scan(tmp_buf2, tmp_buf1)) | 620 | if ((len <= 0) || ip_parse(tmp_buf2, tmp_buf1) != (size_t)len) |
| 602 | HTTPERROR_400_PARAM; | 621 | HTTPERROR_400_PARAM; |
| 603 | OT_SETIP(&ws->peer, tmp_buf1); | 622 | OT_SETIP(&ws->peer, tmp_buf1); |
| 604 | } break; | 623 | } break; |
| @@ -612,21 +631,21 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct | |||
| 612 | uint64_t value; | 631 | uint64_t value; |
| 613 | signed short parsed, bits; | 632 | signed short parsed, bits; |
| 614 | 633 | ||
| 615 | len = scan_urlencoded_query(&read_ptr, tmp_buf, SCAN_SEARCHPATH_VALUE); | 634 | len = urlencoded_parse(&read_ptr, tmp_buf, SCAN_SEARCHPATH_VALUE); |
| 616 | tmp_buf[len] = 0; | 635 | tmp_buf[len] = 0; |
| 617 | if (len <= 0) | 636 | if (len <= 0) |
| 618 | HTTPERROR_400_PARAM; | 637 | HTTPERROR_400_PARAM; |
| 619 | if (*tmp_buf == '-') { | 638 | if (*tmp_buf == '-') { |
| 620 | loglist_reset(); | 639 | loglist_reset(); |
| 621 | return ws->reply_size = sprintf(ws->reply, "Successfully removed.\n"); | 640 | return ws->reply_size = reply_literal(ws, "Successfully removed.\n"); |
| 622 | } | 641 | } |
| 623 | parsed = ip_scan(tmp_buf, net.address); | 642 | parsed = ip_parse(tmp_buf, net.address); |
| 624 | if (!parsed) | 643 | if (!parsed) |
| 625 | HTTPERROR_400_PARAM; | 644 | HTTPERROR_400_PARAM; |
| 626 | if (tmp_buf[parsed++] != '/') | 645 | if (tmp_buf[parsed++] != '/') |
| 627 | bits = 128; | 646 | bits = 128; |
| 628 | else { | 647 | else { |
| 629 | parsed = scan_udec(tmp_buf + parsed, &value, 128); | 648 | parsed = scan_uint(tmp_buf + parsed, &value, 128); |
| 630 | if (!parsed) | 649 | if (!parsed) |
| 631 | HTTPERROR_400_PARAM; | 650 | HTTPERROR_400_PARAM; |
| 632 | bits = (signed short)value; | 651 | bits = (signed short)value; |
| @@ -635,13 +654,13 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct | |||
| 635 | } | 654 | } |
| 636 | net.bits = bits; | 655 | net.bits = bits; |
| 637 | loglist_add_network(&net); | 656 | loglist_add_network(&net); |
| 638 | return ws->reply_size = sprintf(ws->reply, "Successfully added.\n"); | 657 | return ws->reply_size = reply_literal(ws, "Successfully added.\n"); |
| 639 | //} | 658 | //} |
| 640 | } break; | 659 | } break; |
| 641 | #endif | 660 | #endif |
| 642 | case 9: /* matched "peer_id" */ | 661 | case 9: /* matched "peer_id" */ |
| 643 | /* ignore this, when we have less than 20 bytes */ | 662 | /* ignore this, when we have less than 20 bytes */ |
| 644 | if (scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20) | 663 | if (urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20) |
| 645 | HTTPERROR_400_PARAM; | 664 | HTTPERROR_400_PARAM; |
| 646 | ws->peer_id = write_ptr; | 665 | ws->peer_id = write_ptr; |
| 647 | break; | 666 | break; |
| @@ -658,7 +677,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct | |||
| 658 | 677 | ||
| 659 | /* Scanned whole query string */ | 678 | /* Scanned whole query string */ |
| 660 | if (!ws->hash) | 679 | if (!ws->hash) |
| 661 | return ws->reply_size = sprintf(ws->reply, "d14:failure reason80:Your client forgot to send your torrent's info_hash. Please upgrade your client.e"); | 680 | return ws->reply_size = reply_literal(ws, "d14:failure reason80:Your client forgot to send your torrent's info_hash. Please upgrade your client.e"); |
| 662 | 681 | ||
| 663 | if (OT_PEERFLAG(&ws->peer) & PEER_FLAG_STOPPED) | 682 | if (OT_PEERFLAG(&ws->peer) & PEER_FLAG_STOPPED) |
| 664 | ws->reply_size = remove_peer_from_torrent(FLAG_TCP, ws); | 683 | ws->reply_size = remove_peer_from_torrent(FLAG_TCP, ws); |
| @@ -674,7 +693,7 @@ ssize_t http_handle_request(const int64_t sock, struct ot_workstruct *ws) { | |||
| 674 | char *read_ptr = ws->request, *write_ptr; | 693 | char *read_ptr = ws->request, *write_ptr; |
| 675 | 694 | ||
| 676 | #ifdef WANT_FULLLOG_NETWORKS | 695 | #ifdef WANT_FULLLOG_NETWORKS |
| 677 | struct http_data *cookie = io_getcookie(sock); | 696 | struct http_data *cookie = fdstate_cookie(sock); |
| 678 | if (loglist_check_address(cookie->ip)) { | 697 | if (loglist_check_address(cookie->ip)) { |
| 679 | ot_log *log = malloc(sizeof(ot_log)); | 698 | ot_log *log = malloc(sizeof(ot_log)); |
| 680 | if (log) { | 699 | if (log) { |
| @@ -720,7 +739,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 | 739 | In reality we abandoned requiring the url to be correct. This now |
| 721 | only decodes url encoded characters, we check for announces and | 740 | only decodes url encoded characters, we check for announces and |
| 722 | scrapes by looking for "a*" or "sc" */ | 741 | scrapes by looking for "a*" or "sc" */ |
| 723 | len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_PATH); | 742 | len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_PATH); |
| 724 | 743 | ||
| 725 | /* If parsing returned an error, leave with not found */ | 744 | /* If parsing returned an error, leave with not found */ |
| 726 | if (g_redirecturl && (len == -2)) | 745 | if (g_redirecturl && (len == -2)) |
| @@ -769,9 +788,11 @@ ssize_t http_handle_request(const int64_t sock, struct ot_workstruct *ws) { | |||
| 769 | reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf(ws->outbuf, 0, "%zd", ws->reply_size); | 788 | reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf(ws->outbuf, 0, "%zd", ws->reply_size); |
| 770 | ws->reply = ws->outbuf + reply_off; | 789 | ws->reply = ws->outbuf + reply_off; |
| 771 | 790 | ||
| 772 | /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete | 791 | /* 2. Now we write the header so that its terminating '\0' lands exactly one byte before the content starts. The bound is the |
| 773 | packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */ | 792 | space step 1 reserved, so the '\0' falls where step 3 expects it. Complete packet size is increased by size of header plus |
| 774 | ws->reply_size += 1 + sprintf(ws->reply, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", ws->reply_size); | 793 | one byte '\n', we will copy over '\0' in next step */ |
| 794 | ws->reply_size += 1 + snprintf(ws->reply, (size_t)(SUCCESS_HTTP_HEADER_LENGTH - reply_off), | ||
| 795 | "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", ws->reply_size); | ||
| 775 | 796 | ||
| 776 | /* 3. Finally we join both blocks neatly */ | 797 | /* 3. Finally we join both blocks neatly */ |
| 777 | ws->outbuf[SUCCESS_HTTP_HEADER_LENGTH - 1] = '\n'; | 798 | ws->outbuf[SUCCESS_HTTP_HEADER_LENGTH - 1] = '\n'; |
| @@ -11,6 +11,7 @@ | |||
| 11 | #include <sys/uio.h> | 11 | #include <sys/uio.h> |
| 12 | 12 | ||
| 13 | #include "ot_batch.h" | 13 | #include "ot_batch.h" |
| 14 | #include "ot_event.h" | ||
| 14 | #include "ot_buf.h" | 15 | #include "ot_buf.h" |
| 15 | #include "trackerlogic.h" | 16 | #include "trackerlogic.h" |
| 16 | 17 | ||
| @@ -25,11 +26,16 @@ typedef enum { | |||
| 25 | 26 | ||
| 26 | struct http_data { | 27 | struct http_data { |
| 27 | ot_buf request; | 28 | ot_buf request; |
| 29 | int interest; /* EVENT_IN and EVENT_OUT as asked for */ | ||
| 28 | ot_batch *batch; | 30 | ot_batch *batch; |
| 29 | ot_ip6 ip; | 31 | ot_ip6 ip; |
| 30 | STRUCT_HTTP_FLAG flag; | 32 | STRUCT_HTTP_FLAG flag; |
| 31 | }; | 33 | }; |
| 32 | 34 | ||
| 35 | /* Add to or drop from what this connection is waiting to be told about */ | ||
| 36 | void http_interest_add(const int64_t s, struct http_data *cookie, int what); | ||
| 37 | void http_interest_drop(const int64_t s, struct http_data *cookie, int what); | ||
| 38 | |||
| 33 | ssize_t http_handle_request(const int64_t s, struct ot_workstruct *ws); | 39 | ssize_t http_handle_request(const int64_t s, struct ot_workstruct *ws); |
| 34 | ssize_t http_sendiovecdata(const int64_t s, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial); | 40 | ssize_t http_sendiovecdata(const int64_t s, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial); |
| 35 | ssize_t http_issue_error(const int64_t s, struct ot_workstruct *ws, int code); | 41 | ssize_t http_issue_error(const int64_t s, struct ot_workstruct *ws, int code); |
| @@ -7,12 +7,10 @@ | |||
| 7 | #include <stdlib.h> | 7 | #include <stdlib.h> |
| 8 | #include <sys/uio.h> | 8 | #include <sys/uio.h> |
| 9 | 9 | ||
| 10 | /* Libowfat */ | ||
| 11 | |||
| 12 | /* Opentracker */ | 10 | /* Opentracker */ |
| 13 | #include "ot_iovec.h" | 11 | #include "ot_iovec.h" |
| 14 | 12 | ||
| 15 | void *iovec_increase(size_t *iovec_entries, struct iovec **iovector, size_t new_alloc) { | 13 | void *iovec_new_chunk(size_t *iovec_entries, struct iovec **iovector, size_t new_alloc) { |
| 16 | void *new_data; | 14 | void *new_data; |
| 17 | size_t new_entries = 1 + *iovec_entries; | 15 | size_t new_entries = 1 + *iovec_entries; |
| 18 | struct iovec *new_vec = realloc(*iovector, new_entries * sizeof(struct iovec)); | 16 | struct iovec *new_vec = realloc(*iovector, new_entries * sizeof(struct iovec)); |
| @@ -62,7 +60,7 @@ void iovec_free(size_t *iovec_entries, struct iovec **iovector) { | |||
| 62 | *iovec_entries = 0; | 60 | *iovec_entries = 0; |
| 63 | } | 61 | } |
| 64 | 62 | ||
| 65 | void iovec_fixlast(size_t *iovec_entries, struct iovec **iovector, void *last_ptr) { | 63 | void iovec_trim_last(size_t *iovec_entries, struct iovec **iovector, void *last_ptr) { |
| 66 | if (*iovec_entries) { | 64 | if (*iovec_entries) { |
| 67 | char *base = (char *)((*iovector)[*iovec_entries - 1]).iov_base; | 65 | char *base = (char *)((*iovector)[*iovec_entries - 1]).iov_base; |
| 68 | size_t new_alloc = ((char *)last_ptr) - base; | 66 | size_t new_alloc = ((char *)last_ptr) - base; |
| @@ -72,12 +70,12 @@ void iovec_fixlast(size_t *iovec_entries, struct iovec **iovector, void *last_pt | |||
| 72 | } | 70 | } |
| 73 | } | 71 | } |
| 74 | 72 | ||
| 75 | void *iovec_fix_increase_or_free(size_t *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc) { | 73 | void *iovec_next_chunk(size_t *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc) { |
| 76 | void *new_data; | 74 | void *new_data; |
| 77 | 75 | ||
| 78 | iovec_fixlast(iovec_entries, iovector, last_ptr); | 76 | iovec_trim_last(iovec_entries, iovector, last_ptr); |
| 79 | 77 | ||
| 80 | if (!(new_data = iovec_increase(iovec_entries, iovector, new_alloc))) | 78 | if (!(new_data = iovec_new_chunk(iovec_entries, iovector, new_alloc))) |
| 81 | iovec_free(iovec_entries, iovector); | 79 | iovec_free(iovec_entries, iovector); |
| 82 | 80 | ||
| 83 | return new_data; | 81 | return new_data; |
| @@ -8,13 +8,13 @@ | |||
| 8 | 8 | ||
| 9 | #include <sys/uio.h> | 9 | #include <sys/uio.h> |
| 10 | 10 | ||
| 11 | void *iovec_increase(size_t *iovec_entries, struct iovec **iovector, size_t new_alloc); | 11 | void *iovec_new_chunk(size_t *iovec_entries, struct iovec **iovector, size_t new_alloc); |
| 12 | void *iovec_append(size_t *iovec_entries, struct iovec **iovector, struct iovec *append_iovector); | 12 | void *iovec_append(size_t *iovec_entries, struct iovec **iovector, struct iovec *append_iovector); |
| 13 | void iovec_fixlast(size_t *iovec_entries, struct iovec **iovector, void *last_ptr); | 13 | void iovec_trim_last(size_t *iovec_entries, struct iovec **iovector, void *last_ptr); |
| 14 | void iovec_free(size_t *iovec_entries, struct iovec **iovector); | 14 | void iovec_free(size_t *iovec_entries, struct iovec **iovector); |
| 15 | 15 | ||
| 16 | size_t iovec_length(const size_t *iovec_entries, const struct iovec **iovector); | 16 | size_t iovec_length(const size_t *iovec_entries, const struct iovec **iovector); |
| 17 | 17 | ||
| 18 | void *iovec_fix_increase_or_free(size_t *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc); | 18 | void *iovec_next_chunk(size_t *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc); |
| 19 | 19 | ||
| 20 | #endif | 20 | #endif |
| @@ -120,7 +120,7 @@ size_t ip_format(char *dest, const ot_ip6 ip) { | |||
| 120 | return (size_t)(d - dest); | 120 | return (size_t)(d - dest); |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | size_t ip_scan(const char *src, ot_ip6 ip) { | 123 | size_t ip_parse(const char *src, ot_ip6 ip) { |
| 124 | uint8_t prefix[16], suffix[16]; | 124 | uint8_t prefix[16], suffix[16]; |
| 125 | size_t prefixlen = 0, suffixlen = 0; | 125 | size_t prefixlen = 0, suffixlen = 0; |
| 126 | const char *s = src; | 126 | const char *s = src; |
| @@ -23,7 +23,7 @@ static inline int ip_is_v4mapped(const void *ip) { return !memcmp(ip, ip_v4mappe | |||
| 23 | 23 | ||
| 24 | The count lets the caller carry on where the address ended, which is how | 24 | The count lets the caller carry on where the address ended, which is how |
| 25 | "address:port" and "address/bits" are read. */ | 25 | "address:port" and "address/bits" are read. */ |
| 26 | size_t ip_scan(const char *src, ot_ip6 ip); | 26 | size_t ip_parse(const char *src, ot_ip6 ip); |
| 27 | 27 | ||
| 28 | /* Write an address and return how many characters it took. Nothing is | 28 | /* Write an address and return how many characters it took. Nothing is |
| 29 | terminated, so a caller may keep appending. A v4 mapped address is written | 29 | terminated, so a caller may keep appending. A v4 mapped address is written |
diff --git a/ot_livesync.h b/ot_livesync.h index 5b9fa0e..4725023 100644 --- a/ot_livesync.h +++ b/ot_livesync.h | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | #ifndef OT_LIVESYNC_H__ | 6 | #ifndef OT_LIVESYNC_H__ |
| 7 | #define OT_LIVESYNC_H__ | 7 | #define OT_LIVESYNC_H__ |
| 8 | 8 | ||
| 9 | #include "io.h" | ||
| 10 | #include "trackerlogic.h" | 9 | #include "trackerlogic.h" |
| 11 | 10 | ||
| 12 | /* | 11 | /* |
| @@ -8,11 +8,10 @@ | |||
| 8 | #include <pthread.h> | 8 | #include <pthread.h> |
| 9 | #include <stdio.h> | 9 | #include <stdio.h> |
| 10 | #include <stdlib.h> | 10 | #include <stdlib.h> |
| 11 | #include <unistd.h> | ||
| 11 | #include <string.h> | 12 | #include <string.h> |
| 12 | #include <sys/uio.h> | 13 | #include <sys/uio.h> |
| 13 | 14 | ||
| 14 | /* Libowfat */ | ||
| 15 | #include "io.h" | ||
| 16 | 15 | ||
| 17 | /* Opentracker */ | 16 | /* Opentracker */ |
| 18 | #include "ot_iovec.h" | 17 | #include "ot_iovec.h" |
| @@ -193,7 +192,8 @@ int mutex_workqueue_pushresult(ot_taskid taskid, size_t iovec_entries, struct io | |||
| 193 | /* Release lock */ | 192 | /* Release lock */ |
| 194 | pthread_mutex_unlock(&tasklist_mutex); | 193 | pthread_mutex_unlock(&tasklist_mutex); |
| 195 | 194 | ||
| 196 | io_trywrite(g_self_pipe[1], &byte, 1); | 195 | /* A full pipe has already done this nudge's job, so a failure is fine */ |
| 196 | (void)!write(g_self_pipe[1], &byte, 1); | ||
| 197 | 197 | ||
| 198 | /* Indicate whether the worker has to throw away results */ | 198 | /* Indicate whether the worker has to throw away results */ |
| 199 | return task ? 0 : -1; | 199 | return task ? 0 : -1; |
| @@ -221,7 +221,8 @@ int mutex_workqueue_pushchunked(ot_taskid taskid, struct iovec *iovec) { | |||
| 221 | /* Release lock */ | 221 | /* Release lock */ |
| 222 | pthread_mutex_unlock(&tasklist_mutex); | 222 | pthread_mutex_unlock(&tasklist_mutex); |
| 223 | 223 | ||
| 224 | io_trywrite(g_self_pipe[1], &byte, 1); | 224 | /* A full pipe has already done this nudge's job, so a failure is fine */ |
| 225 | (void)!write(g_self_pipe[1], &byte, 1); | ||
| 225 | 226 | ||
| 226 | /* Indicate whether the worker has to throw away results */ | 227 | /* Indicate whether the worker has to throw away results */ |
| 227 | return task ? 0 : -1; | 228 | return task ? 0 : -1; |
| @@ -11,7 +11,7 @@ | |||
| 11 | /* Opentracker */ | 11 | /* Opentracker */ |
| 12 | #include "ot_scan.h" | 12 | #include "ot_scan.h" |
| 13 | 13 | ||
| 14 | size_t scan_udec(const char *src, uint64_t *dest, uint64_t max) { | 14 | size_t scan_uint(const char *src, uint64_t *dest, uint64_t max) { |
| 15 | unsigned long long value; | 15 | unsigned long long value; |
| 16 | char *end; | 16 | char *end; |
| 17 | 17 | ||
| @@ -28,9 +28,9 @@ size_t scan_udec(const char *src, uint64_t *dest, uint64_t max) { | |||
| 28 | return (size_t)(end - src); | 28 | return (size_t)(end - src); |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | size_t scan_udec_bencode(const char *src, uint64_t *dest, uint64_t max) { | 31 | size_t scan_uint_bencode(const char *src, uint64_t *dest, uint64_t max) { |
| 32 | uint64_t value; | 32 | uint64_t value; |
| 33 | size_t consumed = scan_udec(src, &value, max); | 33 | size_t consumed = scan_uint(src, &value, max); |
| 34 | 34 | ||
| 35 | /* bencode writes no leading zeros, so anything longer than a single digit | 35 | /* bencode writes no leading zeros, so anything longer than a single digit |
| 36 | must not begin with one. */ | 36 | must not begin with one. */ |
| @@ -42,3 +42,26 @@ size_t scan_udec_bencode(const char *src, uint64_t *dest, uint64_t max) { | |||
| 42 | 42 | ||
| 43 | return consumed; | 43 | return consumed; |
| 44 | } | 44 | } |
| 45 | |||
| 46 | int scan_int(const char *src, size_t len, int64_t *dest) { | ||
| 47 | int64_t value = 0; | ||
| 48 | int negate = 0; | ||
| 49 | |||
| 50 | if (len && *src == '-') | ||
| 51 | --len, ++src, negate = 1; | ||
| 52 | if (!len) | ||
| 53 | return -1; | ||
| 54 | |||
| 55 | while (len--) { | ||
| 56 | if (*src < '0' || *src > '9') | ||
| 57 | return -1; | ||
| 58 | /* A twenty character field of nines does not fit, and silently wrapping | ||
| 59 | would magically heal a nonsense announce */ | ||
| 60 | if (value > (INT64_MAX - (*src - '0')) / 10) | ||
| 61 | return -1; | ||
| 62 | value = 10 * value + (*src++ - '0'); | ||
| 63 | } | ||
| 64 | |||
| 65 | *dest = negate ? -value : value; | ||
| 66 | return 0; | ||
| 67 | } | ||
| @@ -7,15 +7,21 @@ | |||
| 7 | #define OT_SCAN_H__ | 7 | #define OT_SCAN_H__ |
| 8 | 8 | ||
| 9 | #include <stddef.h> | 9 | #include <stddef.h> |
| 10 | #include <stddef.h> | ||
| 10 | #include <stdint.h> | 11 | #include <stdint.h> |
| 11 | 12 | ||
| 12 | /* Read an unsigned decimal number from src into dest and return how many | 13 | /* Read an unsigned decimal number from src into dest and return how many |
| 13 | characters were consumed. Returns zero and leaves dest untouched when src | 14 | characters were consumed. Returns zero and leaves dest untouched when src |
| 14 | does not begin with a digit, or when the value would exceed max. */ | 15 | does not begin with a digit, or when the value would exceed max. */ |
| 15 | size_t scan_udec(const char *src, uint64_t *dest, uint64_t max); | 16 | size_t scan_uint(const char *src, uint64_t *dest, uint64_t max); |
| 16 | 17 | ||
| 17 | /* As scan_udec, but following the bencode integer grammar, which permits a | 18 | /* As scan_uint, but following the bencode integer grammar, which permits a |
| 18 | leading zero on zero alone. */ | 19 | leading zero on zero alone. */ |
| 19 | size_t scan_udec_bencode(const char *src, uint64_t *dest, uint64_t max); | 20 | size_t scan_uint_bencode(const char *src, uint64_t *dest, uint64_t max); |
| 21 | |||
| 22 | /* Read a signed decimal number that has to take up exactly len characters, | ||
| 23 | which is how a value lifted out of a query string arrives. Returns 0, or | ||
| 24 | -1 leaving dest untouched. */ | ||
| 25 | int scan_int(const char *src, size_t len, int64_t *dest); | ||
| 20 | 26 | ||
| 21 | #endif | 27 | #endif |
diff --git a/ot_socket.c b/ot_socket.c index 84020ca..576087f 100644 --- a/ot_socket.c +++ b/ot_socket.c | |||
| @@ -40,14 +40,16 @@ static void ip_from_sockaddr(ot_ip6 ip, uint16_t *port, const struct sockaddr_st | |||
| 40 | } | 40 | } |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | static int set_nonblocking(int s) { | 43 | int socket_set_blocking(int s, int blocking) { |
| 44 | int flags = fcntl(s, F_GETFL, 0); | 44 | int flags = fcntl(s, F_GETFL, 0); |
| 45 | 45 | ||
| 46 | if (flags == -1) | 46 | if (flags == -1) |
| 47 | return -1; | 47 | return -1; |
| 48 | return fcntl(s, F_SETFL, flags | O_NONBLOCK); | 48 | return fcntl(s, F_SETFL, blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK)); |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | static int set_nonblocking(int s) { return socket_set_blocking(s, 0); } | ||
| 52 | |||
| 51 | /* Close without letting the failure we are about to report be overwritten */ | 53 | /* Close without letting the failure we are about to report be overwritten */ |
| 52 | static int close_and_fail(int s) { | 54 | static int close_and_fail(int s) { |
| 53 | int saved = errno; | 55 | int saved = errno; |
diff --git a/ot_socket.h b/ot_socket.h index 21d04b1..aa4e2d2 100644 --- a/ot_socket.h +++ b/ot_socket.h | |||
| @@ -27,6 +27,7 @@ int socket_udp_bind(const ot_ip6 ip, uint16_t port, socket_sharing sharing); | |||
| 27 | int socket_tcp_connect(const ot_ip6 srcip, uint16_t srcport, const ot_ip6 dstip, uint16_t dstport); | 27 | int socket_tcp_connect(const ot_ip6 srcip, uint16_t srcport, const ot_ip6 dstip, uint16_t dstport); |
| 28 | int socket_is_connected(int s); | 28 | int socket_is_connected(int s); |
| 29 | int socket_accept(int s, ot_ip6 ip, uint16_t *port); | 29 | int socket_accept(int s, ot_ip6 ip, uint16_t *port); |
| 30 | int socket_set_blocking(int s, int blocking); | ||
| 30 | 31 | ||
| 31 | ssize_t socket_recv(int s, void *buf, size_t len, ot_ip6 ip, uint16_t *port); | 32 | ssize_t socket_recv(int s, void *buf, size_t len, ot_ip6 ip, uint16_t *port); |
| 32 | ssize_t socket_send(int s, const void *buf, size_t len, const ot_ip6 ip, uint16_t port); | 33 | ssize_t socket_send(int s, const void *buf, size_t len, const ot_ip6 ip, uint16_t port); |
| @@ -16,15 +16,13 @@ | |||
| 16 | #include <syslog.h> | 16 | #include <syslog.h> |
| 17 | #endif | 17 | #endif |
| 18 | 18 | ||
| 19 | /* Libowfat */ | ||
| 20 | #include "io.h" | ||
| 21 | |||
| 22 | /* Opentracker */ | 19 | /* Opentracker */ |
| 23 | #include "ot_accesslist.h" | 20 | #include "ot_accesslist.h" |
| 24 | #include "ot_hex.h" | 21 | #include "ot_hex.h" |
| 25 | #include "ot_iovec.h" | 22 | #include "ot_iovec.h" |
| 26 | #include "ot_ip.h" | 23 | #include "ot_ip.h" |
| 27 | #include "ot_mutex.h" | 24 | #include "ot_mutex.h" |
| 25 | #include "ot_fmt.h" | ||
| 28 | #include "ot_stats.h" | 26 | #include "ot_stats.h" |
| 29 | #include "trackerlogic.h" | 27 | #include "trackerlogic.h" |
| 30 | 28 | ||
| @@ -178,8 +176,8 @@ static size_t stats_get_highscore_networks(stats_network_node *node, int depth, | |||
| 178 | ++j; | 176 | ++j; |
| 179 | --j; | 177 | --j; |
| 180 | 178 | ||
| 181 | memcpy(scores, scores + 1, j * sizeof(*scores)); | 179 | memmove(scores, scores + 1, j * sizeof(*scores)); |
| 182 | memcpy(networks, networks + 1, j * sizeof(*networks)); | 180 | memmove(networks, networks + 1, j * sizeof(*networks)); |
| 183 | scores[j] = node_score; | 181 | scores[j] = node_score; |
| 184 | memcpy(networks + j, node_value, sizeof(*networks)); | 182 | memcpy(networks + j, node_value, sizeof(*networks)); |
| 185 | } | 183 | } |
| @@ -187,12 +185,11 @@ static size_t stats_get_highscore_networks(stats_network_node *node, int depth, | |||
| 187 | return score; | 185 | return score; |
| 188 | } | 186 | } |
| 189 | 187 | ||
| 190 | static size_t stats_return_busy_networks(char *reply, stats_network_node *tree, size_t amount, int limit) { | 188 | static void stats_return_busy_networks(char **reply, const char *reply_end, stats_network_node *tree, size_t amount, int limit) { |
| 191 | ot_ip6 networks[amount]; | 189 | ot_ip6 networks[amount]; |
| 192 | ot_ip6 node_value; | 190 | ot_ip6 node_value; |
| 193 | size_t scores[amount]; | 191 | size_t scores[amount]; |
| 194 | size_t i; | 192 | size_t i; |
| 195 | char *r = reply; | ||
| 196 | 193 | ||
| 197 | memset(scores, 0, sizeof(scores)); | 194 | memset(scores, 0, sizeof(scores)); |
| 198 | memset(networks, 0, sizeof(networks)); | 195 | memset(networks, 0, sizeof(networks)); |
| @@ -200,24 +197,21 @@ static size_t stats_return_busy_networks(char *reply, stats_network_node *tree, | |||
| 200 | 197 | ||
| 201 | stats_get_highscore_networks(tree, 0, node_value, scores, networks, amount, limit); | 198 | stats_get_highscore_networks(tree, 0, node_value, scores, networks, amount, limit); |
| 202 | 199 | ||
| 203 | r += sprintf(r, "Networks, limit /%d:\n", limit + STATS_NETWORK_NODE_BITWIDTH); | 200 | fmt_append(reply, reply_end, "Networks, limit /%d:\n", limit + STATS_NETWORK_NODE_BITWIDTH); |
| 204 | for (i = amount; i > 0; --i) { | 201 | for (i = amount; i > 0; --i) { |
| 205 | if (scores[i - 1]) { | 202 | if (scores[i - 1]) { |
| 206 | r += sprintf(r, "%08zd: ", scores[i - 1]); | 203 | char ip_readable[64]; |
| 207 | // #ifdef WANT_V6 | 204 | |
| 208 | r += ip_format(r, networks[i - 1]); | 205 | ip_readable[ip_format(ip_readable, networks[i - 1])] = 0; |
| 209 | *r++ = '\n'; | 206 | fmt_append(reply, reply_end, "%08zd: %s\n", scores[i - 1], ip_readable); |
| 210 | } | 207 | } |
| 211 | } | 208 | } |
| 212 | *r++ = '\n'; | 209 | fmt_append(reply, reply_end, "\n"); |
| 213 | |||
| 214 | return r - reply; | ||
| 215 | } | 210 | } |
| 216 | 211 | ||
| 217 | static size_t stats_slash24s_txt(char *reply, size_t amount) { | 212 | static void stats_slash24s_txt(char **reply, const char *reply_end, size_t amount) { |
| 218 | stats_network_node *slash24s_network_counters_root = NULL; | 213 | stats_network_node *slash24s_network_counters_root = NULL; |
| 219 | char *r = reply; | 214 | size_t i, bucket, peer_size = OT_PEER_SIZE4; |
| 220 | size_t i, bucket, peer_size = OT_PEER_SIZE4; | ||
| 221 | 215 | ||
| 222 | for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) { | 216 | for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) { |
| 223 | ot_vector *torrents_list = mutex_bucket_lock(bucket); | 217 | ot_vector *torrents_list = mutex_bucket_lock(bucket); |
| @@ -244,35 +238,28 @@ static size_t stats_slash24s_txt(char *reply, size_t amount) { | |||
| 244 | } | 238 | } |
| 245 | mutex_bucket_unlock(bucket, 0); | 239 | mutex_bucket_unlock(bucket, 0); |
| 246 | if (!g_opentracker_running) | 240 | if (!g_opentracker_running) |
| 247 | goto bailout_error; | 241 | goto done; |
| 248 | } | 242 | } |
| 249 | 243 | ||
| 250 | /* The tree is built. Now analyze */ | 244 | /* The tree is built. Now analyze */ |
| 251 | r += stats_return_busy_networks(r, slash24s_network_counters_root, amount, STATS_NETWORK_NODE_MAXDEPTH); | 245 | stats_return_busy_networks(reply, reply_end, slash24s_network_counters_root, amount, STATS_NETWORK_NODE_MAXDEPTH); |
| 252 | r += stats_return_busy_networks(r, slash24s_network_counters_root, amount, STATS_NETWORK_NODE_LIMIT); | 246 | stats_return_busy_networks(reply, reply_end, slash24s_network_counters_root, amount, STATS_NETWORK_NODE_LIMIT); |
| 253 | goto success; | 247 | goto done; |
| 254 | 248 | ||
| 255 | bailout_unlock: | 249 | bailout_unlock: |
| 256 | mutex_bucket_unlock(bucket, 0); | 250 | mutex_bucket_unlock(bucket, 0); |
| 257 | bailout_error: | 251 | done: |
| 258 | r = reply; | ||
| 259 | success: | ||
| 260 | stats_shift_down_network_count(&slash24s_network_counters_root, 0, sizeof(int) * 8 - 1); | 252 | stats_shift_down_network_count(&slash24s_network_counters_root, 0, sizeof(int) * 8 - 1); |
| 261 | |||
| 262 | return r - reply; | ||
| 263 | } | 253 | } |
| 264 | 254 | ||
| 265 | #ifdef WANT_SPOT_WOODPECKER | 255 | #ifdef WANT_SPOT_WOODPECKER |
| 266 | static stats_network_node *stats_woodpeckers_tree; | 256 | static stats_network_node *stats_woodpeckers_tree; |
| 267 | static pthread_mutex_t g_woodpeckers_mutex = PTHREAD_MUTEX_INITIALIZER; | 257 | static pthread_mutex_t g_woodpeckers_mutex = PTHREAD_MUTEX_INITIALIZER; |
| 268 | 258 | ||
| 269 | static size_t stats_return_woodpeckers(char *reply, int amount) { | 259 | static void stats_return_woodpeckers(char **reply, const char *reply_end, int amount) { |
| 270 | char *r = reply; | ||
| 271 | |||
| 272 | pthread_mutex_lock(&g_woodpeckers_mutex); | 260 | pthread_mutex_lock(&g_woodpeckers_mutex); |
| 273 | r += stats_return_busy_networks(r, stats_woodpeckers_tree, amount, STATS_NETWORK_NODE_MAXDEPTH); | 261 | stats_return_busy_networks(reply, reply_end, stats_woodpeckers_tree, amount, STATS_NETWORK_NODE_MAXDEPTH); |
| 274 | pthread_mutex_unlock(&g_woodpeckers_mutex); | 262 | pthread_mutex_unlock(&g_woodpeckers_mutex); |
| 275 | return r - reply; | ||
| 276 | } | 263 | } |
| 277 | #endif | 264 | #endif |
| 278 | 265 | ||
| @@ -312,10 +299,10 @@ static inline void stats_insert_into_highscore(size_t val, ot_record* list, size | |||
| 312 | 299 | ||
| 313 | 300 | ||
| 314 | /* Fetches stats from tracker */ | 301 | /* Fetches stats from tracker */ |
| 315 | size_t stats_top_txt(char *reply, size_t amount) { | 302 | void stats_top_txt(char **reply, const char *reply_end, size_t amount) { |
| 316 | size_t j, idx, bucket; | 303 | size_t j, idx, bucket; |
| 317 | ot_record top100s[100], top100c[100], top100l[100]; | 304 | ot_record top100s[100], top100c[100], top100l[100]; |
| 318 | char *r = reply, hex_out[42]; | 305 | char hex_out[42]; |
| 319 | 306 | ||
| 320 | if (amount > 100) | 307 | if (amount > 100) |
| 321 | amount = 100; | 308 | amount = 100; |
| @@ -326,7 +313,7 @@ size_t stats_top_txt(char *reply, size_t amount) { | |||
| 326 | 313 | ||
| 327 | /* Iterate over complete torrent list */ | 314 | /* Iterate over complete torrent list */ |
| 328 | for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) { | 315 | for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) { |
| 329 | ot_vector *torrents_list = mutex_bucket_lock(bucket); | 316 | ot_vector *torrents_list = mutex_bucket_lock(bucket); |
| 330 | for (j = 0; j < torrents_list->size; ++j) { | 317 | for (j = 0; j < torrents_list->size; ++j) { |
| 331 | ot_torrent *torrent = (ot_torrent *)(torrents_list->data) + j; | 318 | ot_torrent *torrent = (ot_torrent *)(torrents_list->data) + j; |
| 332 | size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count; | 319 | size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count; |
| @@ -339,114 +326,110 @@ size_t stats_top_txt(char *reply, size_t amount) { | |||
| 339 | } | 326 | } |
| 340 | mutex_bucket_unlock(bucket, 0); | 327 | mutex_bucket_unlock(bucket, 0); |
| 341 | if (!g_opentracker_running) | 328 | if (!g_opentracker_running) |
| 342 | return 0; | 329 | return; |
| 343 | } | 330 | } |
| 344 | 331 | ||
| 345 | r += sprintf(r, "Top %zd torrents by peers:\n", amount); | 332 | fmt_append(reply, reply_end, "Top %zd torrents by peers:\n", amount); |
| 346 | for (idx = 0; idx < amount; ++idx) | 333 | for (idx = 0; idx < amount; ++idx) |
| 347 | if (top100c[idx].val) | 334 | if (top100c[idx].val) |
| 348 | r += sprintf(r, "\t%zd\t%s\n", top100c[idx].val, hex_fromhash(hex_out, top100c[idx].hash)); | 335 | fmt_append(reply, reply_end, "\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); | 336 | fmt_append(reply, reply_end, "Top %zd torrents by seeds:\n", amount); |
| 350 | for (idx = 0; idx < amount; ++idx) | 337 | for (idx = 0; idx < amount; ++idx) |
| 351 | if (top100s[idx].val) | 338 | if (top100s[idx].val) |
| 352 | r += sprintf(r, "\t%zd\t%s\n", top100s[idx].val, hex_fromhash(hex_out, top100s[idx].hash)); | 339 | fmt_append(reply, reply_end, "\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); | 340 | fmt_append(reply, reply_end, "Top %zd torrents by leechers:\n", amount); |
| 354 | for (idx = 0; idx < amount; ++idx) | 341 | for (idx = 0; idx < amount; ++idx) |
| 355 | if (top100l[idx].val) | 342 | if (top100l[idx].val) |
| 356 | r += sprintf(r, "\t%zd\t%s\n", top100l[idx].val, hex_fromhash(hex_out, top100l[idx].hash)); | 343 | fmt_append(reply, reply_end, "\t%zd\t%s\n", top100l[idx].val, hex_from_hash(hex_out, top100l[idx].hash)); |
| 357 | |||
| 358 | return r - reply; | ||
| 359 | } | 344 | } |
| 360 | 345 | ||
| 361 | static unsigned long events_per_time(unsigned long long events, time_t t) { return events / ((unsigned int)t ? (unsigned int)t : 1); } | 346 | static unsigned long events_per_time(unsigned long long events, time_t t) { return events / ((unsigned int)t ? (unsigned int)t : 1); } |
| 362 | 347 | ||
| 363 | static size_t stats_connections_mrtg(char *reply) { | 348 | static void stats_connections_mrtg(char **reply, const char *reply_end) { |
| 364 | ot_time t = time(NULL) - ot_start_time; | 349 | ot_time t = time(NULL) - ot_start_time; |
| 365 | return sprintf(reply, "%llu\n%llu\n%i seconds (%i hours)\nopentracker connections, %lu conns/s :: %lu success/s.", | 350 | fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker connections, %lu conns/s :: %lu success/s.", |
| 366 | ot_overall_tcp_connections + ot_overall_udp_connections, | 351 | ot_overall_tcp_connections + ot_overall_udp_connections, |
| 367 | ot_overall_tcp_successfulannounces + ot_overall_udp_successfulannounces + ot_overall_udp_connects, (int)t, (int)(t / 3600), | 352 | ot_overall_tcp_successfulannounces + ot_overall_udp_successfulannounces + ot_overall_udp_connects, (int)t, (int)(t / 3600), |
| 368 | events_per_time(ot_overall_tcp_connections + ot_overall_udp_connections, t), | 353 | events_per_time(ot_overall_tcp_connections + ot_overall_udp_connections, t), |
| 369 | events_per_time(ot_overall_tcp_successfulannounces + ot_overall_udp_successfulannounces + ot_overall_udp_connects, t)); | 354 | events_per_time(ot_overall_tcp_successfulannounces + ot_overall_udp_successfulannounces + ot_overall_udp_connects, t)); |
| 370 | } | 355 | } |
| 371 | 356 | ||
| 372 | static size_t stats_udpconnections_mrtg(char *reply) { | 357 | static void stats_udpconnections_mrtg(char **reply, const char *reply_end) { |
| 373 | ot_time t = time(NULL) - ot_start_time; | 358 | ot_time t = time(NULL) - ot_start_time; |
| 374 | return sprintf(reply, "%llu\n%llu\n%i seconds (%i hours)\nopentracker udp4 stats, %lu conns/s :: %lu success/s.", ot_overall_udp_connections, | 359 | fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker udp4 stats, %lu conns/s :: %lu success/s.", ot_overall_udp_connections, |
| 375 | ot_overall_udp_successfulannounces + ot_overall_udp_connects, (int)t, (int)(t / 3600), events_per_time(ot_overall_udp_connections, t), | 360 | ot_overall_udp_successfulannounces + ot_overall_udp_connects, (int)t, (int)(t / 3600), events_per_time(ot_overall_udp_connections, t), |
| 376 | events_per_time(ot_overall_udp_successfulannounces + ot_overall_udp_connects, t)); | 361 | events_per_time(ot_overall_udp_successfulannounces + ot_overall_udp_connects, t)); |
| 377 | } | 362 | } |
| 378 | 363 | ||
| 379 | static size_t stats_tcpconnections_mrtg(char *reply) { | 364 | static void stats_tcpconnections_mrtg(char **reply, const char *reply_end) { |
| 380 | time_t t = time(NULL) - ot_start_time; | 365 | time_t t = time(NULL) - ot_start_time; |
| 381 | return sprintf(reply, "%llu\n%llu\n%i seconds (%i hours)\nopentracker tcp4 stats, %lu conns/s :: %lu success/s.", ot_overall_tcp_connections, | 366 | fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker tcp4 stats, %lu conns/s :: %lu success/s.", ot_overall_tcp_connections, |
| 382 | ot_overall_tcp_successfulannounces, (int)t, (int)(t / 3600), events_per_time(ot_overall_tcp_connections, t), | 367 | ot_overall_tcp_successfulannounces, (int)t, (int)(t / 3600), events_per_time(ot_overall_tcp_connections, t), |
| 383 | events_per_time(ot_overall_tcp_successfulannounces, t)); | 368 | events_per_time(ot_overall_tcp_successfulannounces, t)); |
| 384 | } | 369 | } |
| 385 | 370 | ||
| 386 | static size_t stats_scrape_mrtg(char *reply) { | 371 | static void stats_scrape_mrtg(char **reply, const char *reply_end) { |
| 387 | time_t t = time(NULL) - ot_start_time; | 372 | time_t t = time(NULL) - ot_start_time; |
| 388 | return sprintf(reply, "%llu\n%llu\n%i seconds (%i hours)\nopentracker scrape stats, %lu scrape/s (tcp and udp)", ot_overall_tcp_successfulscrapes, | 373 | fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker scrape stats, %lu scrape/s (tcp and udp)", |
| 389 | ot_overall_udp_successfulscrapes, (int)t, (int)(t / 3600), | 374 | ot_overall_tcp_successfulscrapes, ot_overall_udp_successfulscrapes, (int)t, (int)(t / 3600), |
| 390 | events_per_time((ot_overall_tcp_successfulscrapes + ot_overall_udp_successfulscrapes), t)); | 375 | events_per_time((ot_overall_tcp_successfulscrapes + ot_overall_udp_successfulscrapes), t)); |
| 391 | } | 376 | } |
| 392 | 377 | ||
| 393 | static size_t stats_fullscrapes_mrtg(char *reply) { | 378 | static void stats_fullscrapes_mrtg(char **reply, const char *reply_end) { |
| 394 | ot_time t = time(NULL) - ot_start_time; | 379 | ot_time t = time(NULL) - ot_start_time; |
| 395 | return sprintf(reply, "%llu\n%llu\n%i seconds (%i hours)\nopentracker full scrape stats, %lu conns/s :: %lu bytes/s.", ot_full_scrape_count * 1000, | 380 | fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker full scrape stats, %lu conns/s :: %lu bytes/s.", |
| 396 | ot_full_scrape_size, (int)t, (int)(t / 3600), events_per_time(ot_full_scrape_count, t), events_per_time(ot_full_scrape_size, t)); | 381 | ot_full_scrape_count * 1000, ot_full_scrape_size, (int)t, (int)(t / 3600), events_per_time(ot_full_scrape_count, t), |
| 382 | events_per_time(ot_full_scrape_size, t)); | ||
| 397 | } | 383 | } |
| 398 | 384 | ||
| 399 | static size_t stats_peers_mrtg(char *reply) { | 385 | static void stats_peers_mrtg(char **reply, const char *reply_end) { |
| 400 | torrent_stats stats = {0, 0, 0}; | 386 | torrent_stats stats = {0, 0, 0}; |
| 401 | 387 | ||
| 402 | iterate_all_torrents(torrent_statter, (uintptr_t)&stats); | 388 | iterate_all_torrents(torrent_statter, (uintptr_t)&stats); |
| 403 | 389 | ||
| 404 | return sprintf(reply, "%llu\n%llu\nopentracker serving %llu torrents\nopentracker", stats.peer_count, stats.seed_count, stats.torrent_count); | 390 | fmt_append(reply, reply_end, "%llu\n%llu\nopentracker serving %llu torrents\nopentracker", stats.peer_count, stats.seed_count, |
| 391 | stats.torrent_count); | ||
| 405 | } | 392 | } |
| 406 | 393 | ||
| 407 | static size_t stats_torrents_mrtg(char *reply) { | 394 | static void stats_torrents_mrtg(char **reply, const char *reply_end) { |
| 408 | size_t torrent_count = mutex_get_torrent_count(); | 395 | size_t torrent_count = mutex_get_torrent_count(); |
| 409 | 396 | ||
| 410 | return sprintf(reply, "%zd\n%zd\nopentracker serving %zd torrents\nopentracker", torrent_count, (size_t)0, torrent_count); | 397 | fmt_append(reply, reply_end, "%zd\n%zd\nopentracker serving %zd torrents\nopentracker", torrent_count, (size_t)0, torrent_count); |
| 411 | } | 398 | } |
| 412 | 399 | ||
| 413 | static size_t stats_httperrors_txt(char *reply) { | 400 | static void stats_httperrors_txt(char **reply, const char *reply_end) { |
| 414 | return sprintf(reply, "302 RED %llu\n400 ... %llu\n400 PAR %llu\n400 COM %llu\n403 IP %llu\n404 INV %llu\n500 SRV %llu\n", ot_failed_request_counts[0], | 401 | fmt_append(reply, reply_end, "302 RED %llu\n400 ... %llu\n400 PAR %llu\n400 COM %llu\n403 IP %llu\n404 INV %llu\n500 SRV %llu\n", |
| 415 | ot_failed_request_counts[1], ot_failed_request_counts[2], ot_failed_request_counts[3], ot_failed_request_counts[4], | 402 | ot_failed_request_counts[0], ot_failed_request_counts[1], ot_failed_request_counts[2], ot_failed_request_counts[3], |
| 416 | ot_failed_request_counts[5], ot_failed_request_counts[6]); | 403 | ot_failed_request_counts[4], ot_failed_request_counts[5], ot_failed_request_counts[6]); |
| 417 | } | 404 | } |
| 418 | 405 | ||
| 419 | static size_t stats_return_renew_bucket(char *reply) { | 406 | static void stats_return_renew_bucket(char **reply, const char *reply_end) { |
| 420 | char *r = reply; | 407 | int i; |
| 421 | int i; | ||
| 422 | 408 | ||
| 423 | for (i = 0; i < OT_PEER_TIMEOUT; ++i) | 409 | for (i = 0; i < OT_PEER_TIMEOUT; ++i) |
| 424 | r += sprintf(r, "%02i %llu\n", i, ot_renewed[i]); | 410 | fmt_append(reply, reply_end, "%02i %llu\n", i, ot_renewed[i]); |
| 425 | return r - reply; | ||
| 426 | } | 411 | } |
| 427 | 412 | ||
| 428 | static size_t stats_return_sync_mrtg(char *reply) { | 413 | static void stats_return_sync_mrtg(char **reply, const char *reply_end) { |
| 429 | ot_time t = time(NULL) - ot_start_time; | 414 | ot_time t = time(NULL) - ot_start_time; |
| 430 | return sprintf(reply, "%llu\n%llu\n%i seconds (%i hours)\nopentracker connections, %lu conns/s :: %lu success/s.", ot_overall_sync_count, 0LL, (int)t, | 415 | fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker connections, %lu conns/s :: %lu success/s.", ot_overall_sync_count, 0LL, |
| 431 | (int)(t / 3600), events_per_time(ot_overall_tcp_connections + ot_overall_udp_connections, t), | 416 | (int)t, (int)(t / 3600), events_per_time(ot_overall_tcp_connections + ot_overall_udp_connections, t), |
| 432 | events_per_time(ot_overall_tcp_successfulannounces + ot_overall_udp_successfulannounces + ot_overall_udp_connects, t)); | 417 | events_per_time(ot_overall_tcp_successfulannounces + ot_overall_udp_successfulannounces + ot_overall_udp_connects, t)); |
| 433 | } | 418 | } |
| 434 | 419 | ||
| 435 | static size_t stats_return_completed_mrtg(char *reply) { | 420 | static void stats_return_completed_mrtg(char **reply, const char *reply_end) { |
| 436 | ot_time t = time(NULL) - ot_start_time; | 421 | ot_time t = time(NULL) - ot_start_time; |
| 437 | 422 | ||
| 438 | return sprintf(reply, "%llu\n%llu\n%i seconds (%i hours)\nopentracker, %lu completed/h.", ot_overall_completed, 0LL, (int)t, (int)(t / 3600), | 423 | fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker, %lu completed/h.", ot_overall_completed, 0LL, (int)t, (int)(t / 3600), |
| 439 | events_per_time(ot_overall_completed, t / 3600)); | 424 | events_per_time(ot_overall_completed, t / 3600)); |
| 440 | } | 425 | } |
| 441 | 426 | ||
| 442 | #ifdef WANT_LOG_NUMWANT | 427 | #ifdef WANT_LOG_NUMWANT |
| 443 | extern unsigned long long numwants[201]; | 428 | extern unsigned long long numwants[201]; |
| 444 | static size_t stats_return_numwants(char *reply) { | 429 | static void stats_return_numwants(char **reply, const char *reply_end) { |
| 445 | char *r = reply; | 430 | int i; |
| 446 | int i; | ||
| 447 | for (i = 0; i <= 200; ++i) | 431 | for (i = 0; i <= 200; ++i) |
| 448 | r += sprintf(r, "%03d => %lld\n", i, numwants[i]); | 432 | fmt_append(reply, reply_end, "%03d => %lld\n", i, numwants[i]); |
| 449 | return r - reply; | ||
| 450 | } | 433 | } |
| 451 | #endif | 434 | #endif |
| 452 | 435 | ||
| @@ -459,148 +442,158 @@ static void stats_return_fulllog(size_t *iovec_entries, struct iovec **iovector, | |||
| 459 | 442 | ||
| 460 | while (loglist) { | 443 | while (loglist) { |
| 461 | if (r + (loglist->size + 64) >= re) { | 444 | if (r + (loglist->size + 64) >= re) { |
| 462 | r = iovec_fix_increase_or_free(iovec_entries, iovector, r, 32 * OT_STATS_TMPSIZE); | 445 | r = iovec_next_chunk(iovec_entries, iovector, r, 32 * OT_STATS_TMPSIZE); |
| 463 | if (!r) | 446 | if (!r) |
| 464 | return; | 447 | return; |
| 465 | re = r + 32 * OT_STATS_TMPSIZE; | 448 | re = r + 32 * OT_STATS_TMPSIZE; |
| 466 | } | 449 | } |
| 467 | r += sprintf(r, "%08ld: ", loglist->time); | 450 | char ip_readable[64]; |
| 468 | r += ip_format(r, loglist->ip); | 451 | |
| 469 | *r++ = '\n'; | 452 | ip_readable[ip_format(ip_readable, loglist->ip)] = 0; |
| 470 | memcpy(r, loglist->data, loglist->size); | 453 | fmt_append(&r, re, "%08ld: %s\n", loglist->time, ip_readable); |
| 471 | r += loglist->size; | 454 | fmt_bytes(&r, re, loglist->data, loglist->size); |
| 472 | *r++ = '\n'; | 455 | fmt_append(&r, re, "\n*\n\n"); |
| 473 | *r++ = '*'; | ||
| 474 | *r++ = '\n'; | ||
| 475 | *r++ = '\n'; | ||
| 476 | 456 | ||
| 477 | llnext = loglist->next; | 457 | llnext = loglist->next; |
| 478 | free(loglist->data); | 458 | free(loglist->data); |
| 479 | free(loglist); | 459 | free(loglist); |
| 480 | loglist = llnext; | 460 | loglist = llnext; |
| 481 | } | 461 | } |
| 482 | iovec_fixlast(iovec_entries, iovector, r); | 462 | iovec_trim_last(iovec_entries, iovector, r); |
| 483 | } | 463 | } |
| 484 | #endif | 464 | #endif |
| 485 | 465 | ||
| 486 | static size_t stats_return_everything(char *reply) { | 466 | static void stats_return_everything(char **reply, const char *reply_end) { |
| 487 | torrent_stats stats = {0, 0, 0}; | 467 | torrent_stats stats = {0, 0, 0}; |
| 488 | int i; | 468 | int i; |
| 489 | char *r = reply; | ||
| 490 | 469 | ||
| 491 | iterate_all_torrents(torrent_statter, (uintptr_t)&stats); | 470 | iterate_all_torrents(torrent_statter, (uintptr_t)&stats); |
| 492 | 471 | ||
| 493 | r += sprintf(r, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); | 472 | fmt_append(reply, reply_end, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); |
| 494 | r += sprintf(r, "<stats>\n"); | 473 | fmt_append(reply, reply_end, "<stats>\n"); |
| 495 | r += sprintf(r, " <tracker_id>%" PRIu32 "</tracker_id>\n", g_tracker_id); | 474 | fmt_append(reply, reply_end, " <tracker_id>%" PRIu32 "</tracker_id>\n", g_tracker_id); |
| 496 | r += sprintf(r, " <version>\n"); | 475 | fmt_append(reply, reply_end, " <version>\n"); |
| 497 | r += stats_return_tracker_version(r); | 476 | stats_return_tracker_version(reply, reply_end); |
| 498 | r += sprintf(r, " </version>\n"); | 477 | fmt_append(reply, reply_end, " </version>\n"); |
| 499 | r += sprintf(r, " <uptime>%llu</uptime>\n", (unsigned long long)(time(NULL) - ot_start_time)); | 478 | fmt_append(reply, reply_end, " <uptime>%llu</uptime>\n", (unsigned long long)(time(NULL) - ot_start_time)); |
| 500 | r += sprintf(r, " <torrents>\n"); | 479 | fmt_append(reply, reply_end, " <torrents>\n"); |
| 501 | r += sprintf(r, " <count_mutex>%zd</count_mutex>\n", mutex_get_torrent_count()); | 480 | fmt_append(reply, reply_end, " <count_mutex>%zd</count_mutex>\n", mutex_get_torrent_count()); |
| 502 | r += sprintf(r, " <count_iterator>%llu</count_iterator>\n", stats.torrent_count); | 481 | fmt_append(reply, reply_end, " <count_iterator>%llu</count_iterator>\n", stats.torrent_count); |
| 503 | r += sprintf(r, " </torrents>\n"); | 482 | fmt_append(reply, reply_end, " </torrents>\n"); |
| 504 | r += sprintf(r, " <peers>\n <count>%llu</count>\n </peers>\n", stats.peer_count); | 483 | fmt_append(reply, reply_end, " <peers>\n <count>%llu</count>\n </peers>\n", stats.peer_count); |
| 505 | r += sprintf(r, " <seeds>\n <count>%llu</count>\n </seeds>\n", stats.seed_count); | 484 | fmt_append(reply, reply_end, " <seeds>\n <count>%llu</count>\n </seeds>\n", stats.seed_count); |
| 506 | r += sprintf(r, " <completed>\n <count>%llu</count>\n </completed>\n", ot_overall_completed); | 485 | fmt_append(reply, reply_end, " <completed>\n <count>%llu</count>\n </completed>\n", ot_overall_completed); |
| 507 | r += sprintf(r, " <connections>\n"); | 486 | fmt_append(reply, reply_end, " <connections>\n"); |
| 508 | r += sprintf(r, " <tcp>\n <accept>%llu</accept>\n <announce>%llu</announce>\n <scrape>%llu</scrape>\n </tcp>\n", | 487 | fmt_append(reply, reply_end, " <tcp>\n <accept>%llu</accept>\n <announce>%llu</announce>\n <scrape>%llu</scrape>\n </tcp>\n", |
| 509 | ot_overall_tcp_connections, ot_overall_tcp_successfulannounces, ot_overall_tcp_successfulscrapes); | 488 | ot_overall_tcp_connections, ot_overall_tcp_successfulannounces, ot_overall_tcp_successfulscrapes); |
| 510 | r += sprintf(r, " <udp>\n <overall>%llu</overall>\n <connect>%llu</connect>\n <announce>%llu</announce>\n <scrape>%llu</scrape>\n <missmatch>%llu</missmatch>\n </udp>\n", | 489 | fmt_append(reply, reply_end, " <udp>\n <overall>%llu</overall>\n <connect>%llu</connect>\n <announce>%llu</announce>\n <scrape>%llu</scrape>\n <missmatch>%llu</missmatch>\n </udp>\n", |
| 511 | ot_overall_udp_connections, ot_overall_udp_connects, ot_overall_udp_successfulannounces, ot_overall_udp_successfulscrapes, | 490 | ot_overall_udp_connections, ot_overall_udp_connects, ot_overall_udp_successfulannounces, ot_overall_udp_successfulscrapes, |
| 512 | ot_overall_udp_connectionidmissmatches); | 491 | ot_overall_udp_connectionidmissmatches); |
| 513 | r += sprintf(r, " <livesync>\n <count>%llu</count>\n </livesync>\n", ot_overall_sync_count); | 492 | fmt_append(reply, reply_end, " <livesync>\n <count>%llu</count>\n </livesync>\n", ot_overall_sync_count); |
| 514 | r += sprintf(r, " </connections>\n"); | 493 | fmt_append(reply, reply_end, " </connections>\n"); |
| 515 | r += sprintf(r, " <debug>\n"); | 494 | fmt_append(reply, reply_end, " <debug>\n"); |
| 516 | r += sprintf(r, " <renew>\n"); | 495 | fmt_append(reply, reply_end, " <renew>\n"); |
| 517 | for (i = 0; i < OT_PEER_TIMEOUT; ++i) | 496 | for (i = 0; i < OT_PEER_TIMEOUT; ++i) |
| 518 | r += sprintf(r, " <count interval=\"%02i\">%llu</count>\n", i, ot_renewed[i]); | 497 | fmt_append(reply, reply_end, " <count interval=\"%02i\">%llu</count>\n", i, ot_renewed[i]); |
| 519 | r += sprintf(r, " </renew>\n"); | 498 | fmt_append(reply, reply_end, " </renew>\n"); |
| 520 | r += sprintf(r, " <http_error>\n"); | 499 | fmt_append(reply, reply_end, " <http_error>\n"); |
| 521 | for (i = 0; i < CODE_HTTPERROR_COUNT; ++i) | 500 | for (i = 0; i < CODE_HTTPERROR_COUNT; ++i) |
| 522 | r += sprintf(r, " <count code=\"%s\">%llu</count>\n", ot_failed_request_names[i], ot_failed_request_counts[i]); | 501 | fmt_append(reply, reply_end, " <count code=\"%s\">%llu</count>\n", ot_failed_request_names[i], ot_failed_request_counts[i]); |
| 523 | r += sprintf(r, " </http_error>\n"); | 502 | fmt_append(reply, reply_end, " </http_error>\n"); |
| 524 | r += sprintf(r, " <mutex_stall>\n <count>%llu</count>\n </mutex_stall>\n", ot_overall_stall_count); | 503 | fmt_append(reply, reply_end, " <mutex_stall>\n <count>%llu</count>\n </mutex_stall>\n", ot_overall_stall_count); |
| 525 | r += sprintf(r, " </debug>\n"); | 504 | fmt_append(reply, reply_end, " </debug>\n"); |
| 526 | r += sprintf(r, "</stats>"); | 505 | fmt_append(reply, reply_end, "</stats>"); |
| 527 | return r - reply; | ||
| 528 | } | 506 | } |
| 529 | 507 | ||
| 530 | size_t stats_return_tracker_version(char *reply) { | 508 | void stats_return_tracker_version(char **reply, const char *reply_end) { |
| 531 | #define QUOTE(name) #name | 509 | #define QUOTE(name) #name |
| 532 | #define SQUOTE(name) QUOTE(name) | 510 | #define SQUOTE(name) QUOTE(name) |
| 533 | return sprintf(reply, "https://erdgeist.org/gitweb/opentracker/commit/?id=" SQUOTE(GIT_VERSION) "\n"); | 511 | fmt_append(reply, reply_end, "https://erdgeist.org/gitweb/opentracker/commit/?id=" SQUOTE(GIT_VERSION) "\n"); |
| 534 | } | 512 | } |
| 535 | 513 | ||
| 536 | size_t return_stats_for_tracker(char *reply, int mode, int format) { | 514 | void return_stats_for_tracker(char **reply, const char *reply_end, int mode, int format) { |
| 537 | (void)format; | 515 | (void)format; |
| 538 | switch (mode & TASK_TASK_MASK) { | 516 | switch (mode & TASK_TASK_MASK) { |
| 539 | case TASK_STATS_CONNS: | 517 | case TASK_STATS_CONNS: |
| 540 | return stats_connections_mrtg(reply); | 518 | stats_connections_mrtg(reply, reply_end); |
| 519 | break; | ||
| 541 | case TASK_STATS_SCRAPE: | 520 | case TASK_STATS_SCRAPE: |
| 542 | return stats_scrape_mrtg(reply); | 521 | stats_scrape_mrtg(reply, reply_end); |
| 522 | break; | ||
| 543 | case TASK_STATS_UDP: | 523 | case TASK_STATS_UDP: |
| 544 | return stats_udpconnections_mrtg(reply); | 524 | stats_udpconnections_mrtg(reply, reply_end); |
| 525 | break; | ||
| 545 | case TASK_STATS_TCP: | 526 | case TASK_STATS_TCP: |
| 546 | return stats_tcpconnections_mrtg(reply); | 527 | stats_tcpconnections_mrtg(reply, reply_end); |
| 528 | break; | ||
| 547 | case TASK_STATS_FULLSCRAPE: | 529 | case TASK_STATS_FULLSCRAPE: |
| 548 | return stats_fullscrapes_mrtg(reply); | 530 | stats_fullscrapes_mrtg(reply, reply_end); |
| 531 | break; | ||
| 549 | case TASK_STATS_COMPLETED: | 532 | case TASK_STATS_COMPLETED: |
| 550 | return stats_return_completed_mrtg(reply); | 533 | stats_return_completed_mrtg(reply, reply_end); |
| 534 | break; | ||
| 551 | case TASK_STATS_HTTPERRORS: | 535 | case TASK_STATS_HTTPERRORS: |
| 552 | return stats_httperrors_txt(reply); | 536 | stats_httperrors_txt(reply, reply_end); |
| 537 | break; | ||
| 553 | case TASK_STATS_VERSION: | 538 | case TASK_STATS_VERSION: |
| 554 | return stats_return_tracker_version(reply); | 539 | stats_return_tracker_version(reply, reply_end); |
| 540 | break; | ||
| 555 | case TASK_STATS_RENEW: | 541 | case TASK_STATS_RENEW: |
| 556 | return stats_return_renew_bucket(reply); | 542 | stats_return_renew_bucket(reply, reply_end); |
| 543 | break; | ||
| 557 | case TASK_STATS_SYNCS: | 544 | case TASK_STATS_SYNCS: |
| 558 | return stats_return_sync_mrtg(reply); | 545 | stats_return_sync_mrtg(reply, reply_end); |
| 546 | break; | ||
| 559 | #ifdef WANT_LOG_NUMWANT | 547 | #ifdef WANT_LOG_NUMWANT |
| 560 | case TASK_STATS_NUMWANTS: | 548 | case TASK_STATS_NUMWANTS: |
| 561 | return stats_return_numwants(reply); | 549 | stats_return_numwants(reply, reply_end); |
| 550 | break; | ||
| 562 | #endif | 551 | #endif |
| 563 | default: | 552 | default: |
| 564 | return 0; | 553 | break; |
| 565 | } | 554 | } |
| 566 | } | 555 | } |
| 567 | 556 | ||
| 568 | static void stats_make(size_t *iovec_entries, struct iovec **iovector, ot_tasktype mode) { | 557 | static void stats_make(size_t *iovec_entries, struct iovec **iovector, ot_tasktype mode) { |
| 569 | char *r; | 558 | char *r; |
| 559 | const char *r_end; | ||
| 570 | 560 | ||
| 571 | *iovec_entries = 0; | 561 | *iovec_entries = 0; |
| 572 | *iovector = NULL; | 562 | *iovector = NULL; |
| 573 | if (!(r = iovec_increase(iovec_entries, iovector, OT_STATS_TMPSIZE))) | 563 | if (!(r = iovec_new_chunk(iovec_entries, iovector, OT_STATS_TMPSIZE))) |
| 574 | return; | 564 | return; |
| 565 | r_end = r + OT_STATS_TMPSIZE; | ||
| 575 | 566 | ||
| 576 | switch (mode & TASK_TASK_MASK) { | 567 | switch (mode & TASK_TASK_MASK) { |
| 577 | case TASK_STATS_TORRENTS: | 568 | case TASK_STATS_TORRENTS: |
| 578 | r += stats_torrents_mrtg(r); | 569 | stats_torrents_mrtg(&r, r_end); |
| 579 | break; | 570 | break; |
| 580 | case TASK_STATS_PEERS: | 571 | case TASK_STATS_PEERS: |
| 581 | r += stats_peers_mrtg(r); | 572 | stats_peers_mrtg(&r, r_end); |
| 582 | break; | 573 | break; |
| 583 | case TASK_STATS_SLASH24S: | 574 | case TASK_STATS_SLASH24S: |
| 584 | r += stats_slash24s_txt(r, 128); | 575 | stats_slash24s_txt(&r, r_end, 128); |
| 585 | break; | 576 | break; |
| 586 | case TASK_STATS_TOP10: | 577 | case TASK_STATS_TOP10: |
| 587 | r += stats_top_txt(r, 10); | 578 | stats_top_txt(&r, r_end, 10); |
| 588 | break; | 579 | break; |
| 589 | case TASK_STATS_TOP100: | 580 | case TASK_STATS_TOP100: |
| 590 | r = iovec_fix_increase_or_free(iovec_entries, iovector, r, 4 * OT_STATS_TMPSIZE); | 581 | r = iovec_next_chunk(iovec_entries, iovector, r, 4 * OT_STATS_TMPSIZE); |
| 591 | if (!r) | 582 | if (!r) |
| 592 | return; | 583 | return; |
| 593 | r += stats_top_txt(r, 100); | 584 | r_end = r + 4 * OT_STATS_TMPSIZE; |
| 585 | stats_top_txt(&r, r_end, 100); | ||
| 594 | break; | 586 | break; |
| 595 | case TASK_STATS_EVERYTHING: | 587 | case TASK_STATS_EVERYTHING: |
| 596 | r = iovec_fix_increase_or_free(iovec_entries, iovector, r, OT_STATS_TMPSIZE + 64 * OT_PEER_TIMEOUT); | 588 | r = iovec_next_chunk(iovec_entries, iovector, r, OT_STATS_TMPSIZE + 64 * OT_PEER_TIMEOUT); |
| 597 | if (!r) | 589 | if (!r) |
| 598 | return; | 590 | return; |
| 599 | r += stats_return_everything(r); | 591 | r_end = r + OT_STATS_TMPSIZE + 64 * OT_PEER_TIMEOUT; |
| 592 | stats_return_everything(&r, r_end); | ||
| 600 | break; | 593 | break; |
| 601 | #ifdef WANT_SPOT_WOODPECKER | 594 | #ifdef WANT_SPOT_WOODPECKER |
| 602 | case TASK_STATS_WOODPECKERS: | 595 | case TASK_STATS_WOODPECKERS: |
| 603 | r += stats_return_woodpeckers(r, 128); | 596 | stats_return_woodpeckers(&r, r_end, 128); |
| 604 | break; | 597 | break; |
| 605 | #endif | 598 | #endif |
| 606 | #ifdef WANT_FULLLOG_NETWORKS | 599 | #ifdef WANT_FULLLOG_NETWORKS |
| @@ -612,7 +605,7 @@ static void stats_make(size_t *iovec_entries, struct iovec **iovector, ot_taskty | |||
| 612 | iovec_free(iovec_entries, iovector); | 605 | iovec_free(iovec_entries, iovector); |
| 613 | return; | 606 | return; |
| 614 | } | 607 | } |
| 615 | iovec_fixlast(iovec_entries, iovector, r); | 608 | iovec_trim_last(iovec_entries, iovector, r); |
| 616 | } | 609 | } |
| 617 | 610 | ||
| 618 | void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_data) { | 611 | void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_data) { |
| @@ -651,9 +644,9 @@ void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_ | |||
| 651 | localtime_r(&ttt, &time_now); | 644 | localtime_r(&ttt, &time_now); |
| 652 | strftime(timestring, sizeof(timestring), "%FT%T%z", &time_now); | 645 | strftime(timestring, sizeof(timestring), "%FT%T%z", &time_now); |
| 653 | 646 | ||
| 654 | hex_fromhash(hash_hex, *ws->hash); | 647 | hex_from_hash(hash_hex, *ws->hash); |
| 655 | if (ws->peer_id) | 648 | if (ws->peer_id) |
| 656 | hex_fromhash(peerid_hex, (uint8_t *)ws->peer_id); | 649 | hex_from_hash(peerid_hex, (uint8_t *)ws->peer_id); |
| 657 | else { | 650 | else { |
| 658 | *peerid_hex = 0; | 651 | *peerid_hex = 0; |
| 659 | } | 652 | } |
| @@ -44,8 +44,8 @@ enum { | |||
| 44 | void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_data); | 44 | void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_data); |
| 45 | void stats_deliver(int64_t sock, int tasktype); | 45 | void stats_deliver(int64_t sock, int tasktype); |
| 46 | void stats_cleanup(void); | 46 | void stats_cleanup(void); |
| 47 | size_t return_stats_for_tracker(char *reply, int mode, int format); | 47 | void return_stats_for_tracker(char **reply, const char *reply_end, int mode, int format); |
| 48 | size_t stats_return_tracker_version(char *reply); | 48 | void stats_return_tracker_version(char **reply, const char *reply_end); |
| 49 | void stats_init(void); | 49 | void stats_init(void); |
| 50 | void stats_deinit(void); | 50 | void stats_deinit(void); |
| 51 | 51 | ||
| @@ -10,10 +10,8 @@ | |||
| 10 | #include <sys/types.h> | 10 | #include <sys/types.h> |
| 11 | #include <sys/uio.h> | 11 | #include <sys/uio.h> |
| 12 | 12 | ||
| 13 | /* Libowfat */ | ||
| 14 | #include "io.h" | ||
| 15 | |||
| 16 | /* Opentracker */ | 13 | /* Opentracker */ |
| 14 | #include "ot_fmt.h" | ||
| 17 | #include "ot_iovec.h" | 15 | #include "ot_iovec.h" |
| 18 | #include "ot_mutex.h" | 16 | #include "ot_mutex.h" |
| 19 | #include "ot_scan.h" | 17 | #include "ot_scan.h" |
| @@ -51,7 +49,7 @@ int add_changeset_to_tracker(uint8_t *data, size_t len) { | |||
| 51 | data += sizeof(ot_hash); | 49 | data += sizeof(ot_hash); |
| 52 | 50 | ||
| 53 | /* Scan string length indicator, strictly: bencode writes no leading zeros */ | 51 | /* Scan string length indicator, strictly: bencode writes no leading zeros */ |
| 54 | data += (len = scan_udec_bencode((char *)data, &peer_count, (uint64_t)(end - data))); | 52 | data += (len = scan_uint_bencode((char *)data, &peer_count, (uint64_t)(end - data))); |
| 55 | 53 | ||
| 56 | /* If no long was scanned, it is not divisible by 8, it is not | 54 | /* If no long was scanned, it is not divisible by 8, it is not |
| 57 | followed by a colon or claims to need to much memory, we fail */ | 55 | followed by a colon or claims to need to much memory, we fail */ |
| @@ -77,7 +75,7 @@ static void sync_make(size_t *iovec_entries, struct iovec **iovector) { | |||
| 77 | /* Setup return vector... */ | 75 | /* Setup return vector... */ |
| 78 | *iovec_entries = 0; | 76 | *iovec_entries = 0; |
| 79 | *iovector = NULL; | 77 | *iovector = NULL; |
| 80 | if (!(r = iovec_increase(iovec_entries, iovector, OT_SYNC_CHUNK_SIZE))) | 78 | if (!(r = iovec_new_chunk(iovec_entries, iovector, OT_SYNC_CHUNK_SIZE))) |
| 81 | return; | 79 | return; |
| 82 | 80 | ||
| 83 | /* ... and pointer to end of current output buffer. | 81 | /* ... and pointer to end of current output buffer. |
| @@ -105,21 +103,17 @@ static void sync_make(size_t *iovec_entries, struct iovec **iovector) { | |||
| 105 | 103 | ||
| 106 | /* Allocate a fresh output buffer at the end of our buffers list | 104 | /* Allocate a fresh output buffer at the end of our buffers list |
| 107 | release bucket and return, if that fails */ | 105 | release bucket and return, if that fails */ |
| 108 | if (!(r = iovec_fix_increase_or_free(iovec_entries, iovector, r, OT_SYNC_CHUNK_SIZE))) | 106 | if (!(r = iovec_next_chunk(iovec_entries, iovector, r, OT_SYNC_CHUNK_SIZE))) |
| 109 | return mutex_bucket_unlock(bucket); | 107 | return mutex_bucket_unlock(bucket); |
| 110 | 108 | ||
| 111 | /* Adjust new end of output buffer */ | 109 | /* Adjust new end of output buffer */ |
| 112 | re = r + OT_SYNC_CHUNK_SIZE; | 110 | re = r + OT_SYNC_CHUNK_SIZE; |
| 113 | } | 111 | } |
| 114 | 112 | ||
| 115 | *r++ = '2'; | 113 | fmt_append(&r, re, "20:"); |
| 116 | *r++ = '0'; | 114 | fmt_bytes(&r, re, hash, sizeof(ot_hash)); |
| 117 | *r++ = ':'; | 115 | fmt_append(&r, re, "%zd:", byte_count); |
| 118 | memmove(r, hash, sizeof(ot_hash)); | 116 | fmt_bytes(&r, re, peer_list->changeset.data, byte_count); |
| 119 | r += sizeof(ot_hash); | ||
| 120 | r += sprintf(r, "%zd:", byte_count); | ||
| 121 | memmove(r, peer_list->changeset.data, byte_count); | ||
| 122 | r += byte_count; | ||
| 123 | } | 117 | } |
| 124 | 118 | ||
| 125 | /* All torrents done: release lock on currenct bucket */ | 119 | /* All torrents done: release lock on currenct bucket */ |
| @@ -131,7 +125,7 @@ static void sync_make(size_t *iovec_entries, struct iovec **iovector) { | |||
| 131 | *r++ = 'e'; | 125 | *r++ = 'e'; |
| 132 | 126 | ||
| 133 | /* Release unused memory in current output buffer */ | 127 | /* Release unused memory in current output buffer */ |
| 134 | iovec_fixlast(iovec_entries, iovector, r); | 128 | iovec_trim_last(iovec_entries, iovector, r); |
| 135 | } | 129 | } |
| 136 | 130 | ||
| 137 | /* This is the entry point into this worker thread | 131 | /* This is the entry point into this worker thread |
| @@ -10,9 +10,6 @@ | |||
| 10 | #include <stdlib.h> | 10 | #include <stdlib.h> |
| 11 | #include <string.h> | 11 | #include <string.h> |
| 12 | 12 | ||
| 13 | /* Libowfat */ | ||
| 14 | #include "io.h" | ||
| 15 | |||
| 16 | /* Opentracker */ | 13 | /* Opentracker */ |
| 17 | #include "ot_ip.h" | 14 | #include "ot_ip.h" |
| 18 | #include "ot_random.h" | 15 | #include "ot_random.h" |
diff --git a/scan_urlencoded_query.c b/ot_urlencoded.c index 4825a4a..17f12a4 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 */ |
| 56 | void scan_urlencoded_skipvalue(char **string) { | 56 | void 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 | ||
| 73 | int scan_find_keywords(const ot_keywords *keywords, char **string, SCAN_SEARCHPATH_FLAG flags) { | 73 | int 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 | ||
| 91 | ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags) { | 91 | ssize_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 '?': |
| @@ -146,21 +146,8 @@ ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_F | |||
| 146 | return d - (unsigned char *)deststring; | 146 | return d - (unsigned char *)deststring; |
| 147 | } | 147 | } |
| 148 | 148 | ||
| 149 | ssize_t scan_fixed_int(char *data, size_t len, int *tmp) { | ||
| 150 | int minus = 0; | ||
| 151 | *tmp = 0; | ||
| 152 | if (*data == '-') | ||
| 153 | --len, ++data, ++minus; | ||
| 154 | while ((len > 0) && (*data >= '0') && (*data <= '9')) { | ||
| 155 | --len; | ||
| 156 | *tmp = 10 * *tmp + *data++ - '0'; | ||
| 157 | } | ||
| 158 | if (minus) | ||
| 159 | *tmp = -*tmp; | ||
| 160 | return len; | ||
| 161 | } | ||
| 162 | 149 | ||
| 163 | size_t fmt_urlencoded(char *dest, const uint8_t *src, size_t len) { | 150 | size_t urlencoded_format(char *dest, const uint8_t *src, size_t len) { |
| 164 | static const char safe[] = "$/.=~-_"; | 151 | static const char safe[] = "$/.=~-_"; |
| 165 | static const char hexdigit[] = "0123456789abcdef"; | 152 | static const char hexdigit[] = "0123456789abcdef"; |
| 166 | size_t written = 0, i; | 153 | size_t written = 0, i; |
diff --git a/scan_urlencoded_query.h b/ot_urlencoded.h index f71752d..b6f1208 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 | */ |
| 33 | ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags); | 33 | ssize_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,22 +40,16 @@ 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 | */ |
| 43 | int scan_find_keywords(const ot_keywords *keywords, char **string, SCAN_SEARCHPATH_FLAG flags); | 43 | int 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 | */ |
| 48 | void scan_urlencoded_skipvalue(char **string); | 48 | void urlencoded_skip_value(char **string); |
| 49 | 49 | ||
| 50 | /* data pointer to len chars of string | ||
| 51 | len length of chars in data to parse | ||
| 52 | number number to receive result | ||
| 53 | returns number of bytes not parsed, mostly !=0 means fail | ||
| 54 | */ | ||
| 55 | ssize_t scan_fixed_int(char *data, size_t len, int *number); | ||
| 56 | 50 | ||
| 57 | /* The other direction: percent encode len bytes from src into dest and return | 51 | /* 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. */ | 52 | how many characters were written. dest needs room for three times len. */ |
| 59 | size_t fmt_urlencoded(char *dest, const uint8_t *src, size_t len); | 53 | size_t urlencoded_format(char *dest, const uint8_t *src, size_t len); |
| 60 | 54 | ||
| 61 | #endif | 55 | #endif |
diff --git a/ot_vector.c b/ot_vector.c index 7f18246..a56d55b 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 | */ |
| 22 | void *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) { | 22 | void *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,70 +50,75 @@ 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 | */ |
| 58 | void *vector_find_or_insert(ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch) { | 58 | void *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 | size_t match_offset, bytes_after; | ||
| 60 | 61 | ||
| 61 | if (*exactmatch) | 62 | if (*exactmatch) |
| 62 | return match; | 63 | return match; |
| 63 | 64 | ||
| 65 | /* Hold the insertion point as an offset rather than a pointer: it survives | ||
| 66 | the realloc below, and an empty vector has no data pointer to offset from | ||
| 67 | in the first place */ | ||
| 68 | match_offset = vector->size ? (size_t)(match - (uint8_t *)vector->data) : 0; | ||
| 69 | bytes_after = member_size * vector->size - match_offset; | ||
| 70 | |||
| 64 | if (vector->size + 1 > vector->space) { | 71 | if (vector->size + 1 > vector->space) { |
| 65 | size_t new_space = vector->space ? OT_VECTOR_GROW_RATIO * vector->space : OT_VECTOR_MIN_MEMBERS; | 72 | size_t new_space = vector->space ? OT_VECTOR_GROW_RATIO * vector->space : OT_VECTOR_MIN_MEMBERS; |
| 66 | ptrdiff_t match_off = match - (uint8_t *)vector->data; | 73 | uint8_t *new_data = realloc(vector->data, new_space * member_size); |
| 67 | uint8_t *new_data = realloc(vector->data, new_space * member_size); | ||
| 68 | 74 | ||
| 69 | if (!new_data) | 75 | if (!new_data) |
| 70 | return NULL; | 76 | return NULL; |
| 71 | 77 | ||
| 72 | /* Adjust pointer if it moved by realloc */ | ||
| 73 | match = new_data + match_off; | ||
| 74 | |||
| 75 | vector->data = new_data; | 78 | vector->data = new_data; |
| 76 | vector->space = new_space; | 79 | vector->space = new_space; |
| 77 | } | 80 | } |
| 78 | memmove(match + member_size, match, ((uint8_t *)vector->data) + member_size * vector->size - match); | 81 | |
| 82 | match = (uint8_t *)vector->data + match_offset; | ||
| 83 | memmove(match + member_size, match, bytes_after); | ||
| 79 | 84 | ||
| 80 | vector->size++; | 85 | vector->size++; |
| 81 | return match; | 86 | return match; |
| 82 | } | 87 | } |
| 83 | 88 | ||
| 84 | ot_peer *vector_find_or_insert_peer(ot_vector *vector, ot_peer const *peer, size_t peer_size, int *exactmatch) { | 89 | ot_peer *vector_find_or_insert_peer(ot_vector *vector, ot_peer const *peer, size_t peer_size, int *exactmatch) { |
| 85 | ot_peer *match, *end; | 90 | ot_peer *match; |
| 86 | const size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size); | 91 | const size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size); |
| 87 | size_t match_to_end; | 92 | size_t match_offset, bytes_after; |
| 88 | 93 | ||
| 89 | /* If space is zero but size is set, we're dealing with a list of vector->size buckets */ | 94 | /* If space is zero but size is set, we're dealing with a list of vector->size buckets */ |
| 90 | if (vector->space < vector->size) | 95 | if (vector->space < vector->size) |
| 91 | vector = ((ot_vector *)vector->data) + vector_hash_peer(peer, compare_size, vector->size); | 96 | 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); | 97 | match = vector_binary_search(peer, vector->data, vector->size, peer_size, compare_size, exactmatch); |
| 93 | 98 | ||
| 94 | if (*exactmatch) | 99 | if (*exactmatch) |
| 95 | return match; | 100 | return match; |
| 96 | 101 | ||
| 97 | /* This is the amount of bytes that needs to be pushed backwards by peer_size bytes to make room for new peer */ | 102 | /* Hold the insertion point as an offset rather than a pointer: it survives |
| 98 | end = (ot_peer *)vector->data + vector->size * peer_size; | 103 | the realloc below, and an empty vector has no data pointer to offset from |
| 99 | match_to_end = end - match; | 104 | in the first place. bytes_after is what has to move up by peer_size to |
| 105 | make room for the new peer */ | ||
| 106 | match_offset = vector->size ? (size_t)(match - (ot_peer *)vector->data) : 0; | ||
| 107 | bytes_after = vector->size * peer_size - match_offset; | ||
| 100 | 108 | ||
| 101 | if (vector->size + 1 > vector->space) { | 109 | if (vector->size + 1 > vector->space) { |
| 102 | ptrdiff_t offset = match - (ot_peer *)vector->data; | 110 | size_t new_space = vector->space ? OT_VECTOR_GROW_RATIO * vector->space : OT_VECTOR_MIN_MEMBERS; |
| 103 | size_t new_space = vector->space ? OT_VECTOR_GROW_RATIO * vector->space : OT_VECTOR_MIN_MEMBERS; | 111 | ot_peer *new_data = realloc(vector->data, new_space * peer_size); |
| 104 | ot_peer *new_data = realloc(vector->data, new_space * peer_size); | ||
| 105 | 112 | ||
| 106 | if (!new_data) | 113 | if (!new_data) |
| 107 | return NULL; | 114 | return NULL; |
| 108 | /* Adjust pointer if it moved by realloc */ | ||
| 109 | match = new_data + offset; | ||
| 110 | 115 | ||
| 111 | vector->data = new_data; | 116 | vector->data = new_data; |
| 112 | vector->space = new_space; | 117 | vector->space = new_space; |
| 113 | } | 118 | } |
| 114 | 119 | ||
| 115 | /* Here we're guaranteed to have enough space in vector to move the block of peers after insertion point */ | 120 | match = (ot_peer *)vector->data + match_offset; |
| 116 | memmove(match + peer_size, match, match_to_end); | 121 | memmove(match + peer_size, match, bytes_after); |
| 117 | 122 | ||
| 118 | vector->size++; | 123 | vector->size++; |
| 119 | return match; | 124 | return match; |
| @@ -137,7 +142,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); | 142 | vector = ((ot_vector *)vector->data) + vector_hash_peer(peer, compare_size, vector->size); |
| 138 | 143 | ||
| 139 | end = ((ot_peer *)vector->data) + peer_size * vector->size; | 144 | 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); | 145 | match = (ot_peer *)vector_binary_search(peer, vector->data, vector->size, peer_size, compare_size, &exactmatch); |
| 141 | if (!exactmatch) | 146 | if (!exactmatch) |
| 142 | return 0; | 147 | return 0; |
| 143 | 148 | ||
| @@ -150,11 +155,13 @@ int vector_remove_peer(ot_vector *vector, ot_peer const *peer, size_t peer_size) | |||
| 150 | } | 155 | } |
| 151 | 156 | ||
| 152 | void vector_remove_torrent(ot_vector *vector, ot_torrent *match) { | 157 | void vector_remove_torrent(ot_vector *vector, ot_torrent *match) { |
| 153 | ot_torrent *end = ((ot_torrent *)vector->data) + vector->size; | 158 | ot_torrent *end; |
| 154 | 159 | ||
| 155 | if (!vector->size) | 160 | if (!vector->size) |
| 156 | return; | 161 | return; |
| 157 | 162 | ||
| 163 | end = ((ot_torrent *)vector->data) + vector->size; | ||
| 164 | |||
| 158 | free_peerlist(match->peer_list6); | 165 | free_peerlist(match->peer_list6); |
| 159 | free_peerlist(match->peer_list4); | 166 | free_peerlist(match->peer_list4); |
| 160 | 167 | ||
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 | ||
| 29 | void *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); | 29 | void *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); |
| 30 | void *vector_find_or_insert(ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch); | 30 | void *vector_find_or_insert(ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch); |
| 31 | ot_peer *vector_find_or_insert_peer(ot_vector *vector, ot_peer const *peer, size_t peer_size, int *exactmatch); | 31 | ot_peer *vector_find_or_insert_peer(ot_vector *vector, ot_peer const *peer, size_t peer_size, int *exactmatch); |
| 32 | 32 | ||
| @@ -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) | |||
| 148 | size_t remove_peer_from_torrent_proxy(ot_hash hash, ot_peer *peer, size_t peer_size) { | 149 | size_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 */ |
| 393 | static void handle_write(int64_t peersocket) { | 394 | static 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 | } |
| @@ -506,7 +507,7 @@ static int scan_ip6_port(const char *src, ot_ip6 ip, uint16_t *port) { | |||
| 506 | ++s; | 507 | ++s; |
| 507 | if (*s == '[') | 508 | if (*s == '[') |
| 508 | ++s, ++bracket; /* for v6 style notation */ | 509 | ++s, ++bracket; /* for v6 style notation */ |
| 509 | if (!(off = ip_scan(s, ip))) | 510 | if (!(off = ip_parse(s, ip))) |
| 510 | return 0; | 511 | return 0; |
| 511 | s += off; | 512 | s += off; |
| 512 | if (*s == 0 || isspace(*s)) | 513 | if (*s == 0 || isspace(*s)) |
| @@ -521,7 +522,7 @@ static int scan_ip6_port(const char *src, ot_ip6 ip, uint16_t *port) { | |||
| 521 | if (*(s++) != ':') | 522 | if (*(s++) != ':') |
| 522 | return 0; | 523 | return 0; |
| 523 | } | 524 | } |
| 524 | if (!(off = scan_udec(s, &value, 65535))) | 525 | if (!(off = scan_uint(s, &value, 65535))) |
| 525 | return 0; | 526 | return 0; |
| 526 | *port = (uint16_t)value; | 527 | *port = (uint16_t)value; |
| 527 | return off + s - src; | 528 | return off + s - src; |
| @@ -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/integration/.gitignore b/tests/integration/.gitignore new file mode 100644 index 0000000..7a60b85 --- /dev/null +++ b/tests/integration/.gitignore | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | __pycache__/ | ||
| 2 | *.pyc | ||
diff --git a/tests/integration/harness.py b/tests/integration/harness.py new file mode 100644 index 0000000..c8da1e2 --- /dev/null +++ b/tests/integration/harness.py | |||
| @@ -0,0 +1,166 @@ | |||
| 1 | """Starts a real opentracker and talks to it over the wire. | ||
| 2 | |||
| 3 | Everything here drives the binary rather than the code, so these tests cover | ||
| 4 | what the unit suite cannot reach: the event loop, the HTTP parser and the | ||
| 5 | torrent store working together. Run against a sanitizer build and the tracker | ||
| 6 | reports its own memory errors, which the teardown turns into test failures. | ||
| 7 | """ | ||
| 8 | |||
| 9 | import os | ||
| 10 | import socket | ||
| 11 | import struct | ||
| 12 | import subprocess | ||
| 13 | import sys | ||
| 14 | import tempfile | ||
| 15 | import time | ||
| 16 | import unittest | ||
| 17 | import urllib.error | ||
| 18 | import urllib.parse | ||
| 19 | import urllib.request | ||
| 20 | |||
| 21 | TRACKER = os.environ.get("OPENTRACKER", os.path.join(os.path.dirname(__file__), "..", "..", "opentracker.debug")) | ||
| 22 | |||
| 23 | |||
| 24 | def bdecode(data): | ||
| 25 | """Enough bencode for what a tracker answers.""" | ||
| 26 | |||
| 27 | def value(i): | ||
| 28 | if data[i : i + 1] == b"d": | ||
| 29 | i, out = i + 1, {} | ||
| 30 | while data[i : i + 1] != b"e": | ||
| 31 | i, key = value(i) | ||
| 32 | i, out[key] = value(i) | ||
| 33 | return i + 1, out | ||
| 34 | if data[i : i + 1] == b"l": | ||
| 35 | i, out = i + 1, [] | ||
| 36 | while data[i : i + 1] != b"e": | ||
| 37 | i, item = value(i) | ||
| 38 | out.append(item) | ||
| 39 | return i + 1, out | ||
| 40 | if data[i : i + 1] == b"i": | ||
| 41 | end = data.index(b"e", i) | ||
| 42 | return end + 1, int(data[i + 1 : end]) | ||
| 43 | colon = data.index(b":", i) | ||
| 44 | length = int(data[i:colon]) | ||
| 45 | return colon + 1 + length, data[colon + 1 : colon + 1 + length] | ||
| 46 | |||
| 47 | return value(0)[1] | ||
| 48 | |||
| 49 | |||
| 50 | def free_port(): | ||
| 51 | probe = socket.socket() | ||
| 52 | probe.bind(("127.0.0.1", 0)) | ||
| 53 | port = probe.getsockname()[1] | ||
| 54 | probe.close() | ||
| 55 | return port | ||
| 56 | |||
| 57 | |||
| 58 | class Tracker: | ||
| 59 | """One tracker process, listening on loopback.""" | ||
| 60 | |||
| 61 | def __init__(self, extra_config=""): | ||
| 62 | self.port = free_port() | ||
| 63 | self.dir = tempfile.mkdtemp(prefix="ot-integration-") | ||
| 64 | self.config = os.path.join(self.dir, "opentracker.conf") | ||
| 65 | with open(self.config, "w") as handle: | ||
| 66 | handle.write("listen.tcp 127.0.0.1:%d\n" % self.port) | ||
| 67 | handle.write("listen.udp 127.0.0.1:%d\n" % self.port) | ||
| 68 | handle.write(extra_config) | ||
| 69 | self.log = open(os.path.join(self.dir, "tracker.log"), "w+b") | ||
| 70 | environment = dict(os.environ, ASAN_OPTIONS="detect_leaks=0:abort_on_error=0") | ||
| 71 | self.process = subprocess.Popen([TRACKER, "-f", self.config], stdout=self.log, stderr=self.log, env=environment) | ||
| 72 | self._wait_for_port() | ||
| 73 | |||
| 74 | def _wait_for_port(self): | ||
| 75 | for _ in range(200): | ||
| 76 | if self.process.poll() is not None: | ||
| 77 | raise AssertionError("tracker exited at once:\n" + self.output()) | ||
| 78 | try: | ||
| 79 | probe = socket.create_connection(("127.0.0.1", self.port), timeout=0.2) | ||
| 80 | probe.close() | ||
| 81 | return | ||
| 82 | except OSError: | ||
| 83 | time.sleep(0.05) | ||
| 84 | raise AssertionError("tracker never listened on %d:\n%s" % (self.port, self.output())) | ||
| 85 | |||
| 86 | def output(self): | ||
| 87 | self.log.flush() | ||
| 88 | self.log.seek(0) | ||
| 89 | return self.log.read().decode("utf-8", "replace") | ||
| 90 | |||
| 91 | def stop(self): | ||
| 92 | """Returns anything the tracker complained about on the way out.""" | ||
| 93 | if self.process.poll() is None: | ||
| 94 | self.process.kill() | ||
| 95 | self.process.wait() | ||
| 96 | complaints = [ | ||
| 97 | line | ||
| 98 | for line in self.output().splitlines() | ||
| 99 | if "Sanitizer" in line or "runtime error" in line or line.startswith("ERROR") | ||
| 100 | ] | ||
| 101 | self.log.close() | ||
| 102 | return complaints | ||
| 103 | |||
| 104 | # ----------------------------------------------------------- over http | ||
| 105 | |||
| 106 | def get(self, path, timeout=10): | ||
| 107 | return urllib.request.urlopen("http://127.0.0.1:%d%s" % (self.port, path), timeout=timeout).read() | ||
| 108 | |||
| 109 | def announce(self, info_hash, peer_id=b"-OT0001-aaaaaaaaaaaa", port=6881, left=0, **extra): | ||
| 110 | query = { | ||
| 111 | "info_hash": urllib.parse.quote_from_bytes(info_hash, safe=""), | ||
| 112 | "peer_id": urllib.parse.quote_from_bytes(peer_id, safe=""), | ||
| 113 | "port": port, | ||
| 114 | "left": left, | ||
| 115 | } | ||
| 116 | query.update(extra) | ||
| 117 | return bdecode(self.get("/announce?" + "&".join("%s=%s" % item for item in query.items()))) | ||
| 118 | |||
| 119 | def scrape(self, info_hash): | ||
| 120 | return bdecode(self.get("/scrape?info_hash=" + urllib.parse.quote_from_bytes(info_hash, safe=""))) | ||
| 121 | |||
| 122 | def stats(self, mode): | ||
| 123 | return self.get("/stats?mode=" + mode) | ||
| 124 | |||
| 125 | def torrent_count(self): | ||
| 126 | return int(self.stats("torr").split()[0]) | ||
| 127 | |||
| 128 | # ------------------------------------------------------------ over udp | ||
| 129 | |||
| 130 | def udp_connect(self): | ||
| 131 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | ||
| 132 | sock.settimeout(5) | ||
| 133 | sock.sendto(struct.pack(">QII", 0x41727101980, 0, 1), ("127.0.0.1", self.port)) | ||
| 134 | answer = sock.recv(2048) | ||
| 135 | action, _, connection_id = struct.unpack(">IIQ", answer[:16]) | ||
| 136 | assert action == 0, "udp connect answered action %d" % action | ||
| 137 | return sock, connection_id | ||
| 138 | |||
| 139 | def udp_announce(self, sock, connection_id, info_hash, left=0, event=0, port=6881, numwant=-1): | ||
| 140 | sock.sendto( | ||
| 141 | struct.pack( | ||
| 142 | ">QII20s20sQQQIIIiH", connection_id, 1, 2, info_hash, b"p" * 20, 0, left, 0, event, 0, 0, numwant, port | ||
| 143 | ), | ||
| 144 | ("127.0.0.1", self.port), | ||
| 145 | ) | ||
| 146 | answer = sock.recv(2048) | ||
| 147 | action, _, interval, leechers, seeders = struct.unpack(">IIIII", answer[:20]) | ||
| 148 | assert action == 1, "udp announce answered action %d" % action | ||
| 149 | return {"interval": interval, "leechers": leechers, "seeders": seeders, "peers": answer[20:]} | ||
| 150 | |||
| 151 | |||
| 152 | class TrackerTestCase(unittest.TestCase): | ||
| 153 | """Gives every test its own tracker and fails it on any sanitizer output.""" | ||
| 154 | |||
| 155 | config = "" | ||
| 156 | |||
| 157 | def setUp(self): | ||
| 158 | self.tracker = Tracker(self.config) | ||
| 159 | |||
| 160 | def tearDown(self): | ||
| 161 | complaints = self.tracker.stop() | ||
| 162 | self.assertEqual(complaints, [], "the tracker reported:\n" + "\n".join(complaints)) | ||
| 163 | |||
| 164 | def hash_for(self, name): | ||
| 165 | """A distinct info hash per test, so tests cannot leak into each other.""" | ||
| 166 | return (name.encode() + b"\0" * 20)[:20] | ||
diff --git a/tests/integration/test_announce.py b/tests/integration/test_announce.py new file mode 100644 index 0000000..ffbc07b --- /dev/null +++ b/tests/integration/test_announce.py | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | """What a peer's announce does to the swarm it joins.""" | ||
| 2 | |||
| 3 | import os | ||
| 4 | import socket | ||
| 5 | import sys | ||
| 6 | import unittest | ||
| 7 | |||
| 8 | sys.path.insert(0, os.path.dirname(__file__)) | ||
| 9 | from harness import TrackerTestCase | ||
| 10 | |||
| 11 | |||
| 12 | def peers_of(answer, key=b"peers"): | ||
| 13 | """Compact peer list as a list of (address, port).""" | ||
| 14 | blob, out = answer.get(key, b""), [] | ||
| 15 | size = 6 if key == b"peers" else 18 | ||
| 16 | for offset in range(0, len(blob), size): | ||
| 17 | entry = blob[offset : offset + size] | ||
| 18 | address = socket.inet_ntop(socket.AF_INET if size == 6 else socket.AF_INET6, entry[:-2]) | ||
| 19 | out.append((address, entry[-2] << 8 | entry[-1])) | ||
| 20 | return out | ||
| 21 | |||
| 22 | |||
| 23 | class AnnounceLifetime(TrackerTestCase): | ||
| 24 | config = "" | ||
| 25 | |||
| 26 | def test_a_leecher_is_counted_as_incomplete(self): | ||
| 27 | info_hash = self.hash_for("leecher") | ||
| 28 | answer = self.tracker.announce(info_hash, left=100) | ||
| 29 | self.assertEqual(answer[b"complete"], 0) | ||
| 30 | self.assertEqual(answer[b"incomplete"], 1) | ||
| 31 | |||
| 32 | def test_no_bytes_left_means_seeding(self): | ||
| 33 | info_hash = self.hash_for("seeder") | ||
| 34 | answer = self.tracker.announce(info_hash, left=0) | ||
| 35 | self.assertEqual(answer[b"complete"], 1) | ||
| 36 | self.assertEqual(answer[b"incomplete"], 0) | ||
| 37 | |||
| 38 | def test_a_peer_that_finishes_moves_from_one_column_to_the_other(self): | ||
| 39 | info_hash = self.hash_for("finishes") | ||
| 40 | peer_id = b"-OT0001-finisher0001" | ||
| 41 | self.tracker.announce(info_hash, peer_id=peer_id, left=100) | ||
| 42 | self.assertEqual(self.tracker.announce(info_hash, peer_id=peer_id, left=100)[b"incomplete"], 1) | ||
| 43 | |||
| 44 | answer = self.tracker.announce(info_hash, peer_id=peer_id, left=0, event="completed") | ||
| 45 | self.assertEqual(answer[b"complete"], 1, "the peer should now count as a seeder") | ||
| 46 | self.assertEqual(answer[b"incomplete"], 0, "and no longer as a leecher") | ||
| 47 | |||
| 48 | def test_stopped_removes_the_peer(self): | ||
| 49 | info_hash = self.hash_for("stopped") | ||
| 50 | peer_id = b"-OT0001-leaves000001" | ||
| 51 | self.tracker.announce(info_hash, peer_id=peer_id, left=100) | ||
| 52 | self.assertEqual(self.tracker.scrape(info_hash)[b"files"][info_hash][b"incomplete"], 1) | ||
| 53 | |||
| 54 | self.tracker.announce(info_hash, peer_id=peer_id, left=100, event="stopped") | ||
| 55 | self.assertEqual( | ||
| 56 | self.tracker.scrape(info_hash)[b"files"][info_hash][b"incomplete"], 0, "the peer should be gone" | ||
| 57 | ) | ||
| 58 | |||
| 59 | def test_the_same_peer_announcing_twice_is_still_one_peer(self): | ||
| 60 | info_hash = self.hash_for("twice") | ||
| 61 | peer_id = b"-OT0001-repeats00001" | ||
| 62 | for _ in range(5): | ||
| 63 | answer = self.tracker.announce(info_hash, peer_id=peer_id, left=100) | ||
| 64 | self.assertEqual(answer[b"incomplete"], 1, "re-announcing must not duplicate the peer") | ||
| 65 | |||
| 66 | def test_a_peer_is_its_address_and_port_not_its_peer_id(self): | ||
| 67 | """Two announces from one address and port are one peer, whatever | ||
| 68 | peer_id they carry, because that pair is the whole identity.""" | ||
| 69 | info_hash = self.hash_for("identity") | ||
| 70 | self.tracker.announce(info_hash, peer_id=b"-OT0001-a00000000001", port=6881, left=100) | ||
| 71 | answer = self.tracker.announce(info_hash, peer_id=b"-OT0001-b00000000002", port=6881, left=100) | ||
| 72 | self.assertEqual(answer[b"incomplete"], 1, "same address and port, so still one peer") | ||
| 73 | |||
| 74 | answer = self.tracker.announce(info_hash, peer_id=b"-OT0001-c00000000003", port=6882, left=100) | ||
| 75 | self.assertEqual(answer[b"incomplete"], 2, "a different port makes a different peer") | ||
| 76 | |||
| 77 | def test_a_downloaded_count_is_kept(self): | ||
| 78 | info_hash = self.hash_for("downloaded") | ||
| 79 | self.tracker.announce(info_hash, port=7001, left=0, event="completed") | ||
| 80 | self.tracker.announce(info_hash, port=7002, left=0, event="completed") | ||
| 81 | self.assertEqual(self.tracker.scrape(info_hash)[b"files"][info_hash][b"downloaded"], 2) | ||
| 82 | |||
| 83 | def test_a_peer_is_given_the_whole_swarm_including_itself(self): | ||
| 84 | """opentracker does not filter the announcing peer out of its own | ||
| 85 | answer. Clients are expected to ignore themselves.""" | ||
| 86 | info_hash = self.hash_for("sees") | ||
| 87 | self.tracker.announce(info_hash, port=1111, left=100) | ||
| 88 | answer = self.tracker.announce(info_hash, port=2222, left=100) | ||
| 89 | |||
| 90 | ports = sorted(port for _, port in peers_of(answer)) | ||
| 91 | self.assertEqual(ports, [1111, 2222]) | ||
| 92 | |||
| 93 | def test_an_unknown_torrent_scrapes_as_empty(self): | ||
| 94 | answer = self.tracker.scrape(self.hash_for("never-seen")) | ||
| 95 | self.assertEqual(answer[b"files"], {}, "a torrent nobody announced has no entry") | ||
| 96 | |||
| 97 | |||
| 98 | class AnnounceRejects(TrackerTestCase): | ||
| 99 | def assert_rejected(self, query): | ||
| 100 | import urllib.error | ||
| 101 | |||
| 102 | with self.assertRaises(urllib.error.HTTPError) as caught: | ||
| 103 | self.tracker.get("/announce?" + query) | ||
| 104 | self.assertEqual(caught.exception.code, 400) | ||
| 105 | |||
| 106 | def test_a_short_info_hash_is_rejected(self): | ||
| 107 | self.assert_rejected("info_hash=tooshort&peer_id=" + "b" * 20 + "&port=6881&left=0") | ||
| 108 | |||
| 109 | def test_a_missing_info_hash_answers_a_failure_reason(self): | ||
| 110 | """Not a 400: the tracker answers 200 with a bencoded complaint, so | ||
| 111 | that a client shows the message to its user.""" | ||
| 112 | from harness import bdecode | ||
| 113 | |||
| 114 | answer = bdecode(self.tracker.get("/announce?peer_id=" + "b" * 20 + "&port=6881&left=0")) | ||
| 115 | self.assertIn(b"failure reason", answer) | ||
| 116 | self.assertIn(b"info_hash", answer[b"failure reason"]) | ||
| 117 | |||
| 118 | def test_a_port_out_of_range_is_rejected(self): | ||
| 119 | self.assert_rejected("info_hash=" + "a" * 20 + "&peer_id=" + "b" * 20 + "&port=65536&left=0") | ||
| 120 | |||
| 121 | def test_a_port_that_is_not_a_number_is_rejected(self): | ||
| 122 | self.assert_rejected("info_hash=" + "a" * 20 + "&peer_id=" + "b" * 20 + "&port=abc&left=0") | ||
| 123 | |||
| 124 | def test_two_info_hashes_are_rejected(self): | ||
| 125 | self.assert_rejected("info_hash=" + "a" * 20 + "&info_hash=" + "c" * 20 + "&peer_id=" + "b" * 20 + "&port=6881&left=0") | ||
| 126 | |||
| 127 | |||
| 128 | if __name__ == "__main__": | ||
| 129 | unittest.main() | ||
diff --git a/tests/integration/test_dualstack.py b/tests/integration/test_dualstack.py new file mode 100644 index 0000000..cd5ba7d --- /dev/null +++ b/tests/integration/test_dualstack.py | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | """A tracker serving both families from one socket. | ||
| 2 | |||
| 3 | These need WANT_IP_FROM_QUERY_STRING so a peer's address can be dictated | ||
| 4 | rather than being whatever loopback the test happened to come from. | ||
| 5 | """ | ||
| 6 | |||
| 7 | import os | ||
| 8 | import socket | ||
| 9 | import sys | ||
| 10 | import unittest | ||
| 11 | |||
| 12 | sys.path.insert(0, os.path.dirname(__file__)) | ||
| 13 | from harness import TrackerTestCase | ||
| 14 | |||
| 15 | |||
| 16 | def addresses(answer, key): | ||
| 17 | blob, out = answer.get(key, b""), [] | ||
| 18 | size = 6 if key == b"peers" else 18 | ||
| 19 | for offset in range(0, len(blob), size): | ||
| 20 | entry = blob[offset : offset + size] | ||
| 21 | out.append(socket.inet_ntop(socket.AF_INET if size == 6 else socket.AF_INET6, entry[:-2])) | ||
| 22 | return out | ||
| 23 | |||
| 24 | |||
| 25 | class DualStack(TrackerTestCase): | ||
| 26 | def test_an_ipv4_peer_lands_in_the_v4_list(self): | ||
| 27 | info_hash = self.hash_for("v4only") | ||
| 28 | self.tracker.announce(info_hash, ip="10.1.2.3", port=5001, left=100) | ||
| 29 | answer = self.tracker.announce(info_hash, ip="10.1.2.4", port=5002, left=100) | ||
| 30 | |||
| 31 | self.assertEqual(sorted(addresses(answer, b"peers")), ["10.1.2.3", "10.1.2.4"]) | ||
| 32 | self.assertEqual(addresses(answer, b"peers6"), [], "no v6 peer was announced") | ||
| 33 | |||
| 34 | def test_an_ipv6_peer_lands_in_the_v6_list(self): | ||
| 35 | info_hash = self.hash_for("v6only") | ||
| 36 | self.tracker.announce(info_hash, ip="2001:db8::1", port=5001, left=100) | ||
| 37 | answer = self.tracker.announce(info_hash, ip="2001:db8::2", port=5002, left=100) | ||
| 38 | |||
| 39 | self.assertEqual(sorted(addresses(answer, b"peers6")), ["2001:db8::1", "2001:db8::2"]) | ||
| 40 | self.assertEqual(addresses(answer, b"peers"), [], "no v4 peer was announced") | ||
| 41 | |||
| 42 | def test_the_two_families_are_kept_apart_but_both_returned(self): | ||
| 43 | info_hash = self.hash_for("both") | ||
| 44 | self.tracker.announce(info_hash, ip="10.9.9.9", port=6001, left=100) | ||
| 45 | answer = self.tracker.announce(info_hash, ip="2001:db8::9", port=6002, left=100) | ||
| 46 | |||
| 47 | self.assertEqual(addresses(answer, b"peers"), ["10.9.9.9"]) | ||
| 48 | self.assertEqual(addresses(answer, b"peers6"), ["2001:db8::9"]) | ||
| 49 | |||
| 50 | def test_a_v4_mapped_address_is_the_same_peer_as_the_plain_one(self): | ||
| 51 | info_hash = self.hash_for("v4mapped") | ||
| 52 | self.tracker.announce(info_hash, ip="10.5.5.5", port=7001, left=100) | ||
| 53 | answer = self.tracker.announce(info_hash, ip="::ffff:10.5.5.5", port=7001, left=100) | ||
| 54 | |||
| 55 | self.assertEqual(answer[b"incomplete"], 1, "the same address written two ways is one peer") | ||
| 56 | self.assertEqual(addresses(answer, b"peers"), ["10.5.5.5"], "and it is held as v4") | ||
| 57 | |||
| 58 | def test_the_two_families_count_into_one_swarm(self): | ||
| 59 | info_hash = self.hash_for("counts") | ||
| 60 | self.tracker.announce(info_hash, ip="10.4.4.4", port=8001, left=0) | ||
| 61 | answer = self.tracker.announce(info_hash, ip="2001:db8::4", port=8002, left=100) | ||
| 62 | |||
| 63 | self.assertEqual(answer[b"complete"], 1) | ||
| 64 | self.assertEqual(answer[b"incomplete"], 1) | ||
| 65 | self.assertEqual(self.tracker.scrape(info_hash)[b"files"][info_hash][b"complete"], 1) | ||
| 66 | |||
| 67 | def test_stopped_removes_only_the_family_it_came_from(self): | ||
| 68 | info_hash = self.hash_for("stopfamily") | ||
| 69 | self.tracker.announce(info_hash, ip="10.3.3.3", port=9001, left=100) | ||
| 70 | self.tracker.announce(info_hash, ip="2001:db8::3", port=9002, left=100) | ||
| 71 | self.tracker.announce(info_hash, ip="10.3.3.3", port=9001, left=100, event="stopped") | ||
| 72 | |||
| 73 | answer = self.tracker.announce(info_hash, ip="2001:db8::33", port=9003, left=100) | ||
| 74 | self.assertEqual(addresses(answer, b"peers"), [], "the v4 peer left") | ||
| 75 | self.assertIn("2001:db8::3", addresses(answer, b"peers6"), "the v6 one stayed") | ||
| 76 | |||
| 77 | def test_a_malformed_address_is_rejected(self): | ||
| 78 | import urllib.error | ||
| 79 | |||
| 80 | # The whole field has to be the address: a prefix that happens to | ||
| 81 | # parse would quietly record the peer somewhere else | ||
| 82 | for bad in ("999.1.2.3", "nonsense", "2001:db8:::1", "10.1.2.3.4", "10.1.2.3junk", "2001:db8::1%eth0"): | ||
| 83 | with self.assertRaises(urllib.error.HTTPError, msg="ip=%s should be refused" % bad) as caught: | ||
| 84 | self.tracker.announce(self.hash_for("badip"), ip=bad, port=6881, left=0) | ||
| 85 | self.assertEqual(caught.exception.code, 400) | ||
| 86 | |||
| 87 | |||
| 88 | if __name__ == "__main__": | ||
| 89 | unittest.main() | ||
diff --git a/tests/runner.c b/tests/runner.c index 148b928..a92315e 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 | ||
| 17 | static const struct module modules[] = {{"scan_urlencoded_query", test_scan_urlencoded_query}, | 17 | static 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,9 @@ 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}, | ||
| 29 | {"ot_event", test_event}, | ||
| 30 | {"ot_fmt", test_fmt}, | ||
| 28 | {"ot_accesslist", test_accesslist}, | 31 | {"ot_accesslist", test_accesslist}, |
| 29 | {NULL, NULL}}; | 32 | {NULL, NULL}}; |
| 30 | 33 | ||
diff --git a/tests/t_event.c b/tests/t_event.c new file mode 100644 index 0000000..5509f19 --- /dev/null +++ b/tests/t_event.c | |||
| @@ -0,0 +1,222 @@ | |||
| 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 <string.h> | ||
| 8 | #include <sys/socket.h> | ||
| 9 | #include <sys/time.h> | ||
| 10 | #include <unistd.h> | ||
| 11 | |||
| 12 | /* Opentracker */ | ||
| 13 | #include "ot_event.h" | ||
| 14 | #include "test.h" | ||
| 15 | |||
| 16 | /* Against real socket pairs: only the kernel can say whether a descriptor is | ||
| 17 | ready, so there is nothing useful to mock here. */ | ||
| 18 | |||
| 19 | #define EVENTS 16 | ||
| 20 | |||
| 21 | static int sv[2]; | ||
| 22 | static ot_event ready[EVENTS]; | ||
| 23 | |||
| 24 | static void pair_open(void) { | ||
| 25 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) | ||
| 26 | sv[0] = sv[1] = -1; | ||
| 27 | } | ||
| 28 | |||
| 29 | static void pair_close(void) { | ||
| 30 | if (sv[0] != -1) | ||
| 31 | close(sv[0]); | ||
| 32 | if (sv[1] != -1) | ||
| 33 | close(sv[1]); | ||
| 34 | sv[0] = sv[1] = -1; | ||
| 35 | } | ||
| 36 | |||
| 37 | static int wait_for(int fd, int what, int timeout_ms, uint32_t *generation) { | ||
| 38 | int count = event_wait(ready, EVENTS, timeout_ms), i; | ||
| 39 | |||
| 40 | for (i = 0; i < count; ++i) | ||
| 41 | if (ready[i].fd == fd && (ready[i].what & what)) { | ||
| 42 | if (generation) | ||
| 43 | *generation = ready[i].generation; | ||
| 44 | return 1; | ||
| 45 | } | ||
| 46 | return 0; | ||
| 47 | } | ||
| 48 | |||
| 49 | static unsigned long elapsed_ms(struct timeval *since) { | ||
| 50 | struct timeval now; | ||
| 51 | |||
| 52 | gettimeofday(&now, NULL); | ||
| 53 | return (unsigned long)((now.tv_sec - since->tv_sec) * 1000 + (now.tv_usec - since->tv_usec) / 1000); | ||
| 54 | } | ||
| 55 | |||
| 56 | static void case_readable(void) { | ||
| 57 | uint32_t generation = 0; | ||
| 58 | |||
| 59 | test_case("a descriptor with something to read is reported, with its generation"); | ||
| 60 | pair_open(); | ||
| 61 | CHECK(sv[0] != -1); | ||
| 62 | CHECK_INT(event_add(sv[0], EVENT_IN, 4711), 0); | ||
| 63 | CHECK_INT(write(sv[1], "x", 1), 1); | ||
| 64 | CHECK_INT(wait_for(sv[0], EVENT_IN, 1000, &generation), 1); | ||
| 65 | CHECK_INT(generation, 4711); | ||
| 66 | |||
| 67 | test_case("and keeps being reported until it is read"); | ||
| 68 | CHECK_INT(wait_for(sv[0], EVENT_IN, 1000, NULL), 1); | ||
| 69 | { | ||
| 70 | char scratch[4]; | ||
| 71 | CHECK_INT(read(sv[0], scratch, sizeof(scratch)), 1); | ||
| 72 | } | ||
| 73 | CHECK_INT(wait_for(sv[0], EVENT_IN, 50, NULL), 0); | ||
| 74 | |||
| 75 | CHECK_INT(event_del(sv[0]), 0); | ||
| 76 | pair_close(); | ||
| 77 | } | ||
| 78 | |||
| 79 | static void case_writable(void) { | ||
| 80 | test_case("a descriptor with room to write is reported"); | ||
| 81 | pair_open(); | ||
| 82 | CHECK(sv[0] != -1); | ||
| 83 | CHECK_INT(event_add(sv[0], EVENT_OUT, 1), 0); | ||
| 84 | CHECK_INT(wait_for(sv[0], EVENT_OUT, 1000, NULL), 1); | ||
| 85 | |||
| 86 | test_case("and is not, once we say we no longer care"); | ||
| 87 | CHECK_INT(event_modify(sv[0], EVENT_IN, 1), 0); | ||
| 88 | CHECK_INT(wait_for(sv[0], EVENT_OUT, 50, NULL), 0); | ||
| 89 | |||
| 90 | CHECK_INT(event_del(sv[0]), 0); | ||
| 91 | pair_close(); | ||
| 92 | } | ||
| 93 | |||
| 94 | static void case_interest_is_separate(void) { | ||
| 95 | test_case("asking only about writing does not report reading"); | ||
| 96 | pair_open(); | ||
| 97 | CHECK(sv[0] != -1); | ||
| 98 | CHECK_INT(event_add(sv[0], EVENT_OUT, 1), 0); | ||
| 99 | CHECK_INT(write(sv[1], "x", 1), 1); | ||
| 100 | CHECK_INT(wait_for(sv[0], EVENT_IN, 50, NULL), 0); | ||
| 101 | |||
| 102 | test_case("until we ask about it"); | ||
| 103 | CHECK_INT(event_modify(sv[0], EVENT_IN | EVENT_OUT, 1), 0); | ||
| 104 | CHECK_INT(wait_for(sv[0], EVENT_IN, 1000, NULL), 1); | ||
| 105 | |||
| 106 | CHECK_INT(event_del(sv[0]), 0); | ||
| 107 | pair_close(); | ||
| 108 | } | ||
| 109 | |||
| 110 | static void case_removed_is_silent(void) { | ||
| 111 | test_case("a removed descriptor is not reported again"); | ||
| 112 | pair_open(); | ||
| 113 | CHECK(sv[0] != -1); | ||
| 114 | CHECK_INT(event_add(sv[0], EVENT_IN, 1), 0); | ||
| 115 | CHECK_INT(write(sv[1], "x", 1), 1); | ||
| 116 | CHECK_INT(wait_for(sv[0], EVENT_IN, 1000, NULL), 1); | ||
| 117 | CHECK_INT(event_del(sv[0]), 0); | ||
| 118 | CHECK_INT(wait_for(sv[0], EVENT_IN, 50, NULL), 0); | ||
| 119 | pair_close(); | ||
| 120 | } | ||
| 121 | |||
| 122 | static void case_generations_do_not_mix(void) { | ||
| 123 | int other[2]; | ||
| 124 | uint32_t generation = 0; | ||
| 125 | |||
| 126 | test_case("two descriptors carry their own generations"); | ||
| 127 | pair_open(); | ||
| 128 | CHECK(sv[0] != -1); | ||
| 129 | CHECK_INT(socketpair(AF_UNIX, SOCK_STREAM, 0, other), 0); | ||
| 130 | CHECK_INT(event_add(sv[0], EVENT_IN, 100), 0); | ||
| 131 | CHECK_INT(event_add(other[0], EVENT_IN, 200), 0); | ||
| 132 | CHECK_INT(write(sv[1], "x", 1), 1); | ||
| 133 | CHECK_INT(write(other[1], "y", 1), 1); | ||
| 134 | |||
| 135 | CHECK_INT(wait_for(sv[0], EVENT_IN, 1000, &generation), 1); | ||
| 136 | CHECK_INT(generation, 100); | ||
| 137 | CHECK_INT(wait_for(other[0], EVENT_IN, 1000, &generation), 1); | ||
| 138 | CHECK_INT(generation, 200); | ||
| 139 | |||
| 140 | test_case("and re-registering one changes only its own"); | ||
| 141 | CHECK_INT(event_modify(sv[0], EVENT_IN, 101), 0); | ||
| 142 | CHECK_INT(wait_for(sv[0], EVENT_IN, 1000, &generation), 1); | ||
| 143 | CHECK_INT(generation, 101); | ||
| 144 | CHECK_INT(wait_for(other[0], EVENT_IN, 1000, &generation), 1); | ||
| 145 | CHECK_INT(generation, 200); | ||
| 146 | |||
| 147 | event_del(sv[0]); | ||
| 148 | event_del(other[0]); | ||
| 149 | close(other[0]); | ||
| 150 | close(other[1]); | ||
| 151 | pair_close(); | ||
| 152 | } | ||
| 153 | |||
| 154 | /* Removing one of several has to leave the rest addressable: a backend that | ||
| 155 | keeps its descriptors packed moves the last one into the gap, and has to | ||
| 156 | remember where it went. */ | ||
| 157 | static void case_delete_from_the_middle(void) { | ||
| 158 | int pair[3][2]; | ||
| 159 | uint32_t generation = 0; | ||
| 160 | int i; | ||
| 161 | |||
| 162 | test_case("deleting one of three leaves the others reported correctly"); | ||
| 163 | for (i = 0; i < 3; ++i) { | ||
| 164 | CHECK_INT(socketpair(AF_UNIX, SOCK_STREAM, 0, pair[i]), 0); | ||
| 165 | CHECK_INT(event_add(pair[i][0], EVENT_IN, (uint32_t)(10 + i)), 0); | ||
| 166 | } | ||
| 167 | CHECK_INT(event_del(pair[1][0]), 0); | ||
| 168 | |||
| 169 | for (i = 0; i < 3; i += 2) { | ||
| 170 | CHECK_INT(write(pair[i][1], "x", 1), 1); | ||
| 171 | CHECK_INT(wait_for(pair[i][0], EVENT_IN, 1000, &generation), 1); | ||
| 172 | CHECK_INT(generation, 10 + i); | ||
| 173 | } | ||
| 174 | |||
| 175 | test_case("and the survivors can still be changed and removed"); | ||
| 176 | CHECK_INT(event_modify(pair[2][0], EVENT_IN, 99), 0); | ||
| 177 | CHECK_INT(wait_for(pair[2][0], EVENT_IN, 1000, &generation), 1); | ||
| 178 | CHECK_INT(generation, 99); | ||
| 179 | CHECK_INT(event_del(pair[0][0]), 0); | ||
| 180 | CHECK_INT(event_del(pair[2][0]), 0); | ||
| 181 | CHECK_INT(wait_for(pair[2][0], EVENT_IN, 50, NULL), 0); | ||
| 182 | |||
| 183 | for (i = 0; i < 3; ++i) | ||
| 184 | close(pair[i][0]), close(pair[i][1]); | ||
| 185 | } | ||
| 186 | |||
| 187 | static void case_timeout(void) { | ||
| 188 | struct timeval started; | ||
| 189 | |||
| 190 | test_case("waiting on nothing comes back empty when the time is up"); | ||
| 191 | gettimeofday(&started, NULL); | ||
| 192 | CHECK_INT(event_wait(ready, EVENTS, 200), 0); | ||
| 193 | { | ||
| 194 | unsigned long took = elapsed_ms(&started); | ||
| 195 | /* Scheduling makes the upper bound generous. The point is that it waited | ||
| 196 | rather than returning at once, and that it came back at all */ | ||
| 197 | CHECK(took >= 150); | ||
| 198 | CHECK(took < 5000); | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 202 | static void case_unknown_descriptor(void) { | ||
| 203 | test_case("changing or removing one that was never added is refused"); | ||
| 204 | CHECK_INT(event_modify(4096, EVENT_IN, 1), -1); | ||
| 205 | CHECK_INT(event_del(4096), -1); | ||
| 206 | } | ||
| 207 | |||
| 208 | void test_event(void) { | ||
| 209 | CHECK_INT(event_init(), 0); | ||
| 210 | CHECK(event_mechanism() != NULL); | ||
| 211 | |||
| 212 | case_readable(); | ||
| 213 | case_writable(); | ||
| 214 | case_interest_is_separate(); | ||
| 215 | case_removed_is_silent(); | ||
| 216 | case_generations_do_not_mix(); | ||
| 217 | case_delete_from_the_middle(); | ||
| 218 | case_timeout(); | ||
| 219 | case_unknown_descriptor(); | ||
| 220 | |||
| 221 | event_deinit(); | ||
| 222 | } | ||
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 | |||
| 19 | static 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 | |||
| 35 | static 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 | |||
| 47 | static 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 | |||
| 65 | static 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 | |||
| 86 | static 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 | |||
| 105 | static _Atomic long stress_claims, stress_recycles, stress_violations, stress_attempts; | ||
| 106 | static _Atomic int stress_lives[STRESS_SLOTS]; | ||
| 107 | static _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. */ | ||
| 112 | static _Atomic uint64_t stress_conn[STRESS_SLOTS]; | ||
| 113 | static _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 | |||
| 119 | static int stress_running(void) { | ||
| 120 | return atomic_load(&stress_claims) < STRESS_CLAIMS && atomic_load(&stress_attempts) < STRESS_GIVEUP; | ||
| 121 | } | ||
| 122 | |||
| 123 | static 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 | |||
| 147 | static 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 | |||
| 173 | static 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 | |||
| 212 | void 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_fmt.c b/tests/t_fmt.c new file mode 100644 index 0000000..bc23ed5 --- /dev/null +++ b/tests/t_fmt.c | |||
| @@ -0,0 +1,108 @@ | |||
| 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 <string.h> | ||
| 8 | |||
| 9 | /* Opentracker */ | ||
| 10 | #include "ot_fmt.h" | ||
| 11 | #include "test.h" | ||
| 12 | |||
| 13 | static void case_appends_in_sequence(void) { | ||
| 14 | char buffer[64]; | ||
| 15 | char *r = buffer, *re = buffer + sizeof(buffer); | ||
| 16 | |||
| 17 | test_case("appends follow each other and advance the cursor"); | ||
| 18 | fmt_append(&r, re, "d8:completei%zde", (size_t)7); | ||
| 19 | fmt_append(&r, re, "ee"); | ||
| 20 | *r = 0; | ||
| 21 | CHECK_STR(buffer, "d8:completei7eee"); | ||
| 22 | CHECK_INT(r - buffer, 16); | ||
| 23 | } | ||
| 24 | |||
| 25 | static void case_truncation_stops_at_the_end(void) { | ||
| 26 | char buffer[16]; | ||
| 27 | char *r = buffer, *re = buffer + 8; /* pretend the buffer ends early */ | ||
| 28 | |||
| 29 | memset(buffer, '#', sizeof(buffer)); | ||
| 30 | test_case("a value too long for the room left stops at the end"); | ||
| 31 | fmt_append(&r, re, "%s", "0123456789"); | ||
| 32 | CHECK_PTR(r, re); | ||
| 33 | |||
| 34 | test_case("and writes nothing past it"); | ||
| 35 | CHECK_INT(buffer[8], '#'); | ||
| 36 | CHECK_INT(buffer[15], '#'); | ||
| 37 | } | ||
| 38 | |||
| 39 | static void case_a_full_buffer_takes_nothing_more(void) { | ||
| 40 | char buffer[16]; | ||
| 41 | char *r = buffer + 8, *re = buffer + 8; | ||
| 42 | |||
| 43 | memset(buffer, '#', sizeof(buffer)); | ||
| 44 | test_case("appending to a cursor already at the end changes nothing"); | ||
| 45 | fmt_append(&r, re, "more"); | ||
| 46 | CHECK_PTR(r, re); | ||
| 47 | CHECK_INT(buffer[8], '#'); | ||
| 48 | } | ||
| 49 | |||
| 50 | static void case_an_append_that_just_fits(void) { | ||
| 51 | char buffer[8]; | ||
| 52 | char *r = buffer, *re = buffer + 3; | ||
| 53 | |||
| 54 | memset(buffer, '#', sizeof(buffer)); | ||
| 55 | test_case("three characters into three bytes of room still truncate"); | ||
| 56 | fmt_append(&r, re, "abc"); | ||
| 57 | CHECK_PTR(r, re); | ||
| 58 | CHECK_INT(buffer[0], 'a'); | ||
| 59 | CHECK_INT(buffer[1], 'b'); | ||
| 60 | |||
| 61 | test_case("because snprintf spends the last byte on its terminator"); | ||
| 62 | CHECK_INT(buffer[2], 0); | ||
| 63 | CHECK_INT(buffer[3], '#'); | ||
| 64 | } | ||
| 65 | |||
| 66 | static void case_bytes_are_all_or_nothing(void) { | ||
| 67 | char buffer[16]; | ||
| 68 | char *r = buffer, *re = buffer + 8; | ||
| 69 | |||
| 70 | memset(buffer, '#', sizeof(buffer)); | ||
| 71 | test_case("a run of bytes is written when it fits"); | ||
| 72 | fmt_bytes(&r, re, "\0\1\2\3", 4); | ||
| 73 | CHECK_PTR(r, buffer + 4); | ||
| 74 | CHECK_MEM(buffer, "\0\1\2\3", 4); | ||
| 75 | |||
| 76 | test_case("a run that would not fit is refused whole"); | ||
| 77 | fmt_bytes(&r, re, "abcde", 5); | ||
| 78 | CHECK_PTR(r, re); | ||
| 79 | CHECK_INT(buffer[4], '#'); | ||
| 80 | |||
| 81 | test_case("a run of exactly the room left is written"); | ||
| 82 | r = buffer + 4; | ||
| 83 | fmt_bytes(&r, re, "wxyz", 4); | ||
| 84 | CHECK_PTR(r, re); | ||
| 85 | CHECK_MEM(buffer + 4, "wxyz", 4); | ||
| 86 | CHECK_INT(buffer[8], '#'); | ||
| 87 | } | ||
| 88 | |||
| 89 | static void case_bytes_do_not_terminate(void) { | ||
| 90 | char buffer[8]; | ||
| 91 | char *r = buffer, *re = buffer + 4; | ||
| 92 | |||
| 93 | memset(buffer, '#', sizeof(buffer)); | ||
| 94 | test_case("unlike an append, a run of bytes may fill the room exactly"); | ||
| 95 | fmt_bytes(&r, re, "abcd", 4); | ||
| 96 | CHECK_PTR(r, re); | ||
| 97 | CHECK_MEM(buffer, "abcd", 4); | ||
| 98 | CHECK_INT(buffer[4], '#'); | ||
| 99 | } | ||
| 100 | |||
| 101 | void test_fmt(void) { | ||
| 102 | case_appends_in_sequence(); | ||
| 103 | case_truncation_stops_at_the_end(); | ||
| 104 | case_a_full_buffer_takes_nothing_more(); | ||
| 105 | case_an_append_that_just_fits(); | ||
| 106 | case_bytes_are_all_or_nothing(); | ||
| 107 | case_bytes_do_not_terminate(); | ||
| 108 | } | ||
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 | ||
| 71 | static void case_rejects_short_input(void) { | 71 | static 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 | ||
| 80 | static void case_leaves_hash_untouched_on_failure(void) { | 80 | static 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 | ||
| 102 | static void case_isdigit(void) { | 102 | static 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_ip.c b/tests/t_ip.c index 2fe3a72..8d71679 100644 --- a/tests/t_ip.c +++ b/tests/t_ip.c | |||
| @@ -95,27 +95,27 @@ static void case_scan_forms(void) { | |||
| 95 | char ip[16]; | 95 | char ip[16]; |
| 96 | 96 | ||
| 97 | test_case("2.2 form 1, every group spelled out"); | 97 | test_case("2.2 form 1, every group spelled out"); |
| 98 | CHECK_INT(ip_scan("1111:2222:3333:4444:5555:6666:7777:8888", ip), 39); | 98 | CHECK_INT(ip_parse("1111:2222:3333:4444:5555:6666:7777:8888", ip), 39); |
| 99 | CHECK_MEM(ip, "\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x66\x66\x77\x77\x88\x88", 16); | 99 | CHECK_MEM(ip, "\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x66\x66\x77\x77\x88\x88", 16); |
| 100 | 100 | ||
| 101 | test_case("2.2 form 2, zero groups abbreviated"); | 101 | test_case("2.2 form 2, zero groups abbreviated"); |
| 102 | CHECK_INT(ip_scan("2001:db8::1", ip), 11); | 102 | CHECK_INT(ip_parse("2001:db8::1", ip), 11); |
| 103 | CHECK_MEM(ip, "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16); | 103 | CHECK_MEM(ip, "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16); |
| 104 | CHECK_INT(ip_scan("::1", ip), 3); | 104 | CHECK_INT(ip_parse("::1", ip), 3); |
| 105 | CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16); | 105 | CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16); |
| 106 | CHECK_INT(ip_scan("::", ip), 2); | 106 | CHECK_INT(ip_parse("::", ip), 2); |
| 107 | CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); | 107 | CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); |
| 108 | CHECK_INT(ip_scan("1::", ip), 3); | 108 | CHECK_INT(ip_parse("1::", ip), 3); |
| 109 | CHECK_MEM(ip, "\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); | 109 | CHECK_MEM(ip, "\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); |
| 110 | 110 | ||
| 111 | test_case("2.2 form 3, a dotted quad in the last two groups"); | 111 | test_case("2.2 form 3, a dotted quad in the last two groups"); |
| 112 | CHECK_INT(ip_scan("::ffff:1.2.3.4", ip), 14); | 112 | CHECK_INT(ip_parse("::ffff:1.2.3.4", ip), 14); |
| 113 | CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x01\x02\x03\x04", 16); | 113 | CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x01\x02\x03\x04", 16); |
| 114 | CHECK_INT(ip_scan("1:2:3:4:5:6:1.2.3.4", ip), 19); | 114 | CHECK_INT(ip_parse("1:2:3:4:5:6:1.2.3.4", ip), 19); |
| 115 | CHECK_MEM(ip, "\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x01\x02\x03\x04", 16); | 115 | CHECK_MEM(ip, "\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x01\x02\x03\x04", 16); |
| 116 | 116 | ||
| 117 | test_case("upper case input is accepted"); | 117 | test_case("upper case input is accepted"); |
| 118 | CHECK_INT(ip_scan("2001:DB8::1", ip), 11); | 118 | CHECK_INT(ip_parse("2001:DB8::1", ip), 11); |
| 119 | CHECK_MEM(ip, "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16); | 119 | CHECK_MEM(ip, "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16); |
| 120 | } | 120 | } |
| 121 | 121 | ||
| @@ -125,7 +125,7 @@ static void case_scan_accepts_bare_ipv4(void) { | |||
| 125 | /* Every address opentracker holds is sixteen bytes, so an operator writing | 125 | /* Every address opentracker holds is sixteen bytes, so an operator writing |
| 126 | a plain IPv4 address in the config has to land as the v4 mapped form */ | 126 | a plain IPv4 address in the config has to land as the v4 mapped form */ |
| 127 | test_case("a bare IPv4 address is stored v4 mapped"); | 127 | test_case("a bare IPv4 address is stored v4 mapped"); |
| 128 | CHECK_INT(ip_scan("10.0.0.1", ip), 8); | 128 | CHECK_INT(ip_parse("10.0.0.1", ip), 8); |
| 129 | CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x0a\x00\x00\x01", 16); | 129 | CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x0a\x00\x00\x01", 16); |
| 130 | CHECK(ip_is_v4mapped(ip)); | 130 | CHECK(ip_is_v4mapped(ip)); |
| 131 | } | 131 | } |
| @@ -136,39 +136,39 @@ static void case_scan_reports_length(void) { | |||
| 136 | /* The config parser continues from where the address ended, which is how | 136 | /* The config parser continues from where the address ended, which is how |
| 137 | "address:port" and "address/bits" are read */ | 137 | "address:port" and "address/bits" are read */ |
| 138 | test_case("scanning stops at the character after the address"); | 138 | test_case("scanning stops at the character after the address"); |
| 139 | CHECK_INT(ip_scan("10.0.0.1:6969", ip), 8); | 139 | CHECK_INT(ip_parse("10.0.0.1:6969", ip), 8); |
| 140 | CHECK_INT(ip_scan("2001:db8::1/64", ip), 11); | 140 | CHECK_INT(ip_parse("2001:db8::1/64", ip), 11); |
| 141 | CHECK_INT(ip_scan("::1]", ip), 3); | 141 | CHECK_INT(ip_parse("::1]", ip), 3); |
| 142 | CHECK_INT(ip_scan("1:2:3:4:5:6:7:8 trailing", ip), 15); | 142 | CHECK_INT(ip_parse("1:2:3:4:5:6:7:8 trailing", ip), 15); |
| 143 | } | 143 | } |
| 144 | 144 | ||
| 145 | static void case_scan_rejects(void) { | 145 | static void case_scan_rejects(void) { |
| 146 | char ip[16]; | 146 | char ip[16]; |
| 147 | 147 | ||
| 148 | test_case("an empty or junk string is refused"); | 148 | test_case("an empty or junk string is refused"); |
| 149 | CHECK_INT(ip_scan("", ip), 0); | 149 | CHECK_INT(ip_parse("", ip), 0); |
| 150 | CHECK_INT(ip_scan("x", ip), 0); | 150 | CHECK_INT(ip_parse("x", ip), 0); |
| 151 | CHECK_INT(ip_scan(":", ip), 0); | 151 | CHECK_INT(ip_parse(":", ip), 0); |
| 152 | CHECK_INT(ip_scan(":1:2", ip), 0); | 152 | CHECK_INT(ip_parse(":1:2", ip), 0); |
| 153 | 153 | ||
| 154 | test_case("too few groups without :: are refused"); | 154 | test_case("too few groups without :: are refused"); |
| 155 | CHECK_INT(ip_scan("1:2:3:4:5:6:7", ip), 0); | 155 | CHECK_INT(ip_parse("1:2:3:4:5:6:7", ip), 0); |
| 156 | CHECK_INT(ip_scan("2001:db8:1", ip), 0); | 156 | CHECK_INT(ip_parse("2001:db8:1", ip), 0); |
| 157 | 157 | ||
| 158 | /* libowfat accepted a prefix of each of these and carried on. Taking only | 158 | /* libowfat accepted a prefix of each of these and carried on. Taking only |
| 159 | part of a malformed address is worse than refusing it, especially where | 159 | part of a malformed address is worse than refusing it, especially where |
| 160 | the text comes from a config file or an X-Forwarded-For header. */ | 160 | the text comes from a config file or an X-Forwarded-For header. */ |
| 161 | test_case("a second :: is refused rather than truncated"); | 161 | test_case("a second :: is refused rather than truncated"); |
| 162 | CHECK_INT(ip_scan("1::2::3", ip), 0); | 162 | CHECK_INT(ip_parse("1::2::3", ip), 0); |
| 163 | CHECK_INT(ip_scan("::1::", ip), 0); | 163 | CHECK_INT(ip_parse("::1::", ip), 0); |
| 164 | CHECK_INT(ip_scan("::::", ip), 0); | 164 | CHECK_INT(ip_parse("::::", ip), 0); |
| 165 | 165 | ||
| 166 | test_case("more than eight groups is refused"); | 166 | test_case("more than eight groups is refused"); |
| 167 | CHECK_INT(ip_scan("1:2:3:4:5:6:7:8:9", ip), 0); | 167 | CHECK_INT(ip_parse("1:2:3:4:5:6:7:8:9", ip), 0); |
| 168 | 168 | ||
| 169 | test_case("a group of more than four digits is refused, not truncated"); | 169 | test_case("a group of more than four digits is refused, not truncated"); |
| 170 | CHECK_INT(ip_scan("12345::1", ip), 0); | 170 | CHECK_INT(ip_parse("12345::1", ip), 0); |
| 171 | CHECK_INT(ip_scan("fffff::1", ip), 0); | 171 | CHECK_INT(ip_parse("fffff::1", ip), 0); |
| 172 | } | 172 | } |
| 173 | 173 | ||
| 174 | static void case_round_trip(void) { | 174 | static void case_round_trip(void) { |
| @@ -187,7 +187,7 @@ static void case_round_trip(void) { | |||
| 187 | 187 | ||
| 188 | test_case("every formatted address scans back to the same bytes"); | 188 | test_case("every formatted address scans back to the same bytes"); |
| 189 | for (i = 0; samples[i]; ++i) { | 189 | for (i = 0; samples[i]; ++i) { |
| 190 | size_t n = ip_scan(samples[i], ip); | 190 | size_t n = ip_parse(samples[i], ip); |
| 191 | if (n != strlen(samples[i])) { | 191 | if (n != strlen(samples[i])) { |
| 192 | test_failf(__FILE__, __LINE__, "\"%s\" consumed %zu of %zu", samples[i], n, strlen(samples[i])); | 192 | test_failf(__FILE__, __LINE__, "\"%s\" consumed %zu of %zu", samples[i], n, strlen(samples[i])); |
| 193 | return; | 193 | return; |
| @@ -197,7 +197,7 @@ static void case_round_trip(void) { | |||
| 197 | test_failf(__FILE__, __LINE__, "\"%s\" formatted back as \"%s\"", samples[i], text); | 197 | test_failf(__FILE__, __LINE__, "\"%s\" formatted back as \"%s\"", samples[i], text); |
| 198 | return; | 198 | return; |
| 199 | } | 199 | } |
| 200 | if (!ip_scan(text, again) || memcmp(ip, again, 16)) { | 200 | if (!ip_parse(text, again) || memcmp(ip, again, 16)) { |
| 201 | test_failf(__FILE__, __LINE__, "\"%s\" did not survive a second pass", samples[i]); | 201 | test_failf(__FILE__, __LINE__, "\"%s\" did not survive a second pass", samples[i]); |
| 202 | return; | 202 | return; |
| 203 | } | 203 | } |
diff --git a/tests/t_scan.c b/tests/t_scan.c index 89f1590..aab4413 100644 --- a/tests/t_scan.c +++ b/tests/t_scan.c | |||
| @@ -15,13 +15,13 @@ static void case_plain_numbers(void) { | |||
| 15 | uint64_t value; | 15 | uint64_t value; |
| 16 | 16 | ||
| 17 | test_case("a decimal number is read and its length reported"); | 17 | test_case("a decimal number is read and its length reported"); |
| 18 | CHECK_INT(scan_udec("0", &value, 65535), 1); | 18 | CHECK_INT(scan_uint("0", &value, 65535), 1); |
| 19 | CHECK_INT(value, 0); | 19 | CHECK_INT(value, 0); |
| 20 | CHECK_INT(scan_udec("7", &value, 65535), 1); | 20 | CHECK_INT(scan_uint("7", &value, 65535), 1); |
| 21 | CHECK_INT(value, 7); | 21 | CHECK_INT(value, 7); |
| 22 | CHECK_INT(scan_udec("6969", &value, 65535), 4); | 22 | CHECK_INT(scan_uint("6969", &value, 65535), 4); |
| 23 | CHECK_INT(value, 6969); | 23 | CHECK_INT(value, 6969); |
| 24 | CHECK_INT(scan_udec("65535", &value, 65535), 5); | 24 | CHECK_INT(scan_uint("65535", &value, 65535), 5); |
| 25 | CHECK_INT(value, 65535); | 25 | CHECK_INT(value, 65535); |
| 26 | } | 26 | } |
| 27 | 27 | ||
| @@ -29,11 +29,11 @@ static void case_stops_at_non_digit(void) { | |||
| 29 | uint64_t value; | 29 | uint64_t value; |
| 30 | 30 | ||
| 31 | test_case("scanning stops at the first character that is not a digit"); | 31 | test_case("scanning stops at the first character that is not a digit"); |
| 32 | CHECK_INT(scan_udec("6969:rest", &value, 65535), 4); | 32 | CHECK_INT(scan_uint("6969:rest", &value, 65535), 4); |
| 33 | CHECK_INT(value, 6969); | 33 | CHECK_INT(value, 6969); |
| 34 | CHECK_INT(scan_udec("128/8", &value, 65535), 3); | 34 | CHECK_INT(scan_uint("128/8", &value, 65535), 3); |
| 35 | CHECK_INT(value, 128); | 35 | CHECK_INT(value, 128); |
| 36 | CHECK_INT(scan_udec("42\n", &value, 65535), 2); | 36 | CHECK_INT(scan_uint("42\n", &value, 65535), 2); |
| 37 | CHECK_INT(value, 42); | 37 | CHECK_INT(value, 42); |
| 38 | } | 38 | } |
| 39 | 39 | ||
| @@ -41,9 +41,9 @@ static void case_rejects_whitespace(void) { | |||
| 41 | uint64_t value = 0xdead; | 41 | uint64_t value = 0xdead; |
| 42 | 42 | ||
| 43 | test_case("leading whitespace is not skipped"); | 43 | test_case("leading whitespace is not skipped"); |
| 44 | CHECK_INT(scan_udec(" 1", &value, 65535), 0); | 44 | CHECK_INT(scan_uint(" 1", &value, 65535), 0); |
| 45 | CHECK_INT(scan_udec("\t1", &value, 65535), 0); | 45 | CHECK_INT(scan_uint("\t1", &value, 65535), 0); |
| 46 | CHECK_INT(scan_udec("\n1", &value, 65535), 0); | 46 | CHECK_INT(scan_uint("\n1", &value, 65535), 0); |
| 47 | CHECK_INT(value, 0xdead); | 47 | CHECK_INT(value, 0xdead); |
| 48 | } | 48 | } |
| 49 | 49 | ||
| @@ -51,9 +51,9 @@ static void case_rejects_sign(void) { | |||
| 51 | uint64_t value = 0xdead; | 51 | uint64_t value = 0xdead; |
| 52 | 52 | ||
| 53 | test_case("a sign is not accepted"); | 53 | test_case("a sign is not accepted"); |
| 54 | CHECK_INT(scan_udec("+1", &value, 65535), 0); | 54 | CHECK_INT(scan_uint("+1", &value, 65535), 0); |
| 55 | CHECK_INT(scan_udec("-1", &value, 65535), 0); | 55 | CHECK_INT(scan_uint("-1", &value, 65535), 0); |
| 56 | CHECK_INT(scan_udec("-0", &value, 65535), 0); | 56 | CHECK_INT(scan_uint("-0", &value, 65535), 0); |
| 57 | CHECK_INT(value, 0xdead); | 57 | CHECK_INT(value, 0xdead); |
| 58 | } | 58 | } |
| 59 | 59 | ||
| @@ -61,9 +61,9 @@ static void case_rejects_empty_and_junk(void) { | |||
| 61 | uint64_t value = 0xdead; | 61 | uint64_t value = 0xdead; |
| 62 | 62 | ||
| 63 | test_case("a string with no leading digit yields nothing"); | 63 | test_case("a string with no leading digit yields nothing"); |
| 64 | CHECK_INT(scan_udec("", &value, 65535), 0); | 64 | CHECK_INT(scan_uint("", &value, 65535), 0); |
| 65 | CHECK_INT(scan_udec("x", &value, 65535), 0); | 65 | CHECK_INT(scan_uint("x", &value, 65535), 0); |
| 66 | CHECK_INT(scan_udec(":80", &value, 65535), 0); | 66 | CHECK_INT(scan_uint(":80", &value, 65535), 0); |
| 67 | CHECK_INT(value, 0xdead); | 67 | CHECK_INT(value, 0xdead); |
| 68 | } | 68 | } |
| 69 | 69 | ||
| @@ -71,9 +71,9 @@ static void case_no_alternative_bases(void) { | |||
| 71 | uint64_t value; | 71 | uint64_t value; |
| 72 | 72 | ||
| 73 | test_case("hexadecimal and octal notations are read as plain decimal"); | 73 | test_case("hexadecimal and octal notations are read as plain decimal"); |
| 74 | CHECK_INT(scan_udec("0x10", &value, 65535), 1); | 74 | CHECK_INT(scan_uint("0x10", &value, 65535), 1); |
| 75 | CHECK_INT(value, 0); | 75 | CHECK_INT(value, 0); |
| 76 | CHECK_INT(scan_udec("010", &value, 65535), 3); | 76 | CHECK_INT(scan_uint("010", &value, 65535), 3); |
| 77 | CHECK_INT(value, 10); | 77 | CHECK_INT(value, 10); |
| 78 | } | 78 | } |
| 79 | 79 | ||
| @@ -81,13 +81,13 @@ static void case_refuses_out_of_range(void) { | |||
| 81 | uint64_t value = 0xdead; | 81 | uint64_t value = 0xdead; |
| 82 | 82 | ||
| 83 | test_case("a value above max is refused, not truncated"); | 83 | test_case("a value above max is refused, not truncated"); |
| 84 | CHECK_INT(scan_udec("65536", &value, 65535), 0); | 84 | CHECK_INT(scan_uint("65536", &value, 65535), 0); |
| 85 | CHECK_INT(scan_udec("99999", &value, 65535), 0); | 85 | CHECK_INT(scan_uint("99999", &value, 65535), 0); |
| 86 | CHECK_INT(scan_udec("129", &value, 128), 0); | 86 | CHECK_INT(scan_uint("129", &value, 128), 0); |
| 87 | CHECK_INT(value, 0xdead); | 87 | CHECK_INT(value, 0xdead); |
| 88 | 88 | ||
| 89 | test_case("the boundary itself is accepted"); | 89 | test_case("the boundary itself is accepted"); |
| 90 | CHECK_INT(scan_udec("128", &value, 128), 3); | 90 | CHECK_INT(scan_uint("128", &value, 128), 3); |
| 91 | CHECK_INT(value, 128); | 91 | CHECK_INT(value, 128); |
| 92 | } | 92 | } |
| 93 | 93 | ||
| @@ -95,21 +95,21 @@ static void case_sixty_four_bit_range(void) { | |||
| 95 | uint64_t value; | 95 | uint64_t value; |
| 96 | 96 | ||
| 97 | test_case("the full unsigned 64 bit range is available"); | 97 | test_case("the full unsigned 64 bit range is available"); |
| 98 | CHECK_INT(scan_udec("18446744073709551615", &value, UINT64_MAX), 20); | 98 | CHECK_INT(scan_uint("18446744073709551615", &value, UINT64_MAX), 20); |
| 99 | CHECK(value == UINT64_MAX); | 99 | CHECK(value == UINT64_MAX); |
| 100 | 100 | ||
| 101 | test_case("a number too large for 64 bits is refused"); | 101 | test_case("a number too large for 64 bits is refused"); |
| 102 | CHECK_INT(scan_udec("18446744073709551616", &value, UINT64_MAX), 0); | 102 | CHECK_INT(scan_uint("18446744073709551616", &value, UINT64_MAX), 0); |
| 103 | CHECK_INT(scan_udec("99999999999999999999999999", &value, UINT64_MAX), 0); | 103 | CHECK_INT(scan_uint("99999999999999999999999999", &value, UINT64_MAX), 0); |
| 104 | } | 104 | } |
| 105 | 105 | ||
| 106 | static void case_leading_zeros_allowed(void) { | 106 | static void case_leading_zeros_allowed(void) { |
| 107 | uint64_t value; | 107 | uint64_t value; |
| 108 | 108 | ||
| 109 | test_case("leading zeros are accepted outside bencode"); | 109 | test_case("leading zeros are accepted outside bencode"); |
| 110 | CHECK_INT(scan_udec("007", &value, 65535), 3); | 110 | CHECK_INT(scan_uint("007", &value, 65535), 3); |
| 111 | CHECK_INT(value, 7); | 111 | CHECK_INT(value, 7); |
| 112 | CHECK_INT(scan_udec("0000", &value, 65535), 4); | 112 | CHECK_INT(scan_uint("0000", &value, 65535), 4); |
| 113 | CHECK_INT(value, 0); | 113 | CHECK_INT(value, 0); |
| 114 | } | 114 | } |
| 115 | 115 | ||
| @@ -117,9 +117,9 @@ static void case_bencode_rejects_leading_zeros(void) { | |||
| 117 | uint64_t value = 0xdead; | 117 | uint64_t value = 0xdead; |
| 118 | 118 | ||
| 119 | test_case("bencode refuses a leading zero on anything but zero"); | 119 | test_case("bencode refuses a leading zero on anything but zero"); |
| 120 | CHECK_INT(scan_udec_bencode("007", &value, 65535), 0); | 120 | CHECK_INT(scan_uint_bencode("007", &value, 65535), 0); |
| 121 | CHECK_INT(scan_udec_bencode("0000", &value, 65535), 0); | 121 | CHECK_INT(scan_uint_bencode("0000", &value, 65535), 0); |
| 122 | CHECK_INT(scan_udec_bencode("01", &value, 65535), 0); | 122 | CHECK_INT(scan_uint_bencode("01", &value, 65535), 0); |
| 123 | CHECK_INT(value, 0xdead); | 123 | CHECK_INT(value, 0xdead); |
| 124 | } | 124 | } |
| 125 | 125 | ||
| @@ -127,11 +127,11 @@ static void case_bencode_accepts_canonical(void) { | |||
| 127 | uint64_t value; | 127 | uint64_t value; |
| 128 | 128 | ||
| 129 | test_case("bencode accepts a canonically written number"); | 129 | test_case("bencode accepts a canonically written number"); |
| 130 | CHECK_INT(scan_udec_bencode("0", &value, 65535), 1); | 130 | CHECK_INT(scan_uint_bencode("0", &value, 65535), 1); |
| 131 | CHECK_INT(value, 0); | 131 | CHECK_INT(value, 0); |
| 132 | CHECK_INT(scan_udec_bencode("0:", &value, 65535), 1); | 132 | CHECK_INT(scan_uint_bencode("0:", &value, 65535), 1); |
| 133 | CHECK_INT(value, 0); | 133 | CHECK_INT(value, 0); |
| 134 | CHECK_INT(scan_udec_bencode("160:", &value, 65535), 3); | 134 | CHECK_INT(scan_uint_bencode("160:", &value, 65535), 3); |
| 135 | CHECK_INT(value, 160); | 135 | CHECK_INT(value, 160); |
| 136 | } | 136 | } |
| 137 | 137 | ||
| @@ -139,14 +139,64 @@ static void case_bencode_inherits_the_rest(void) { | |||
| 139 | uint64_t value = 0xdead; | 139 | uint64_t value = 0xdead; |
| 140 | 140 | ||
| 141 | test_case("bencode is no laxer than the plain scanner"); | 141 | test_case("bencode is no laxer than the plain scanner"); |
| 142 | CHECK_INT(scan_udec_bencode(" 1", &value, 65535), 0); | 142 | CHECK_INT(scan_uint_bencode(" 1", &value, 65535), 0); |
| 143 | CHECK_INT(scan_udec_bencode("-1", &value, 65535), 0); | 143 | CHECK_INT(scan_uint_bencode("-1", &value, 65535), 0); |
| 144 | CHECK_INT(scan_udec_bencode("+1", &value, 65535), 0); | 144 | CHECK_INT(scan_uint_bencode("+1", &value, 65535), 0); |
| 145 | CHECK_INT(scan_udec_bencode("65536", &value, 65535), 0); | 145 | CHECK_INT(scan_uint_bencode("65536", &value, 65535), 0); |
| 146 | CHECK_INT(value, 0xdead); | 146 | CHECK_INT(value, 0xdead); |
| 147 | } | 147 | } |
| 148 | 148 | ||
| 149 | /* ------------------------------------------- fixed width signed integers */ | ||
| 150 | |||
| 151 | /* A value lifted out of a query string arrives as a pointer and a length | ||
| 152 | with nothing terminating it, and the whole of it has to be the number. */ | ||
| 153 | |||
| 154 | static void case_int_whole_field(void) { | ||
| 155 | int64_t value = 0; | ||
| 156 | |||
| 157 | test_case("the whole field is read, sign and all"); | ||
| 158 | CHECK_INT(scan_int("123", 3, &value), 0); | ||
| 159 | CHECK_INT(value, 123); | ||
| 160 | CHECK_INT(scan_int("-45", 3, &value), 0); | ||
| 161 | CHECK_INT(value, -45); | ||
| 162 | |||
| 163 | test_case("and only as far as the length given"); | ||
| 164 | CHECK_INT(scan_int("12345", 3, &value), 0); | ||
| 165 | CHECK_INT(value, 123); | ||
| 166 | } | ||
| 167 | |||
| 168 | static void case_int_rejects_the_rest(void) { | ||
| 169 | int64_t value = 4711; | ||
| 170 | |||
| 171 | test_case("anything that is not all digits is refused"); | ||
| 172 | CHECK_INT(scan_int("12a", 3, &value), -1); | ||
| 173 | CHECK_INT(scan_int("", 0, &value), -1); | ||
| 174 | CHECK_INT(scan_int("-", 1, &value), -1); | ||
| 175 | CHECK_INT(scan_int(" 12", 3, &value), -1); | ||
| 176 | CHECK_INT(scan_int("+12", 3, &value), -1); | ||
| 177 | |||
| 178 | test_case("and a refused field leaves the number alone"); | ||
| 179 | CHECK_INT(value, 4711); | ||
| 180 | } | ||
| 181 | |||
| 182 | static void case_int_overflow(void) { | ||
| 183 | int64_t value = 0; | ||
| 184 | |||
| 185 | test_case("a field too long to fit is refused rather than wrapped"); | ||
| 186 | CHECK_INT(scan_int("9223372036854775807", 19, &value), 0); | ||
| 187 | CHECK_INT(value, INT64_MAX); | ||
| 188 | CHECK_INT(scan_int("9223372036854775808", 19, &value), -1); | ||
| 189 | CHECK_INT(scan_int("99999999999999999999", 20, &value), -1); | ||
| 190 | |||
| 191 | test_case("including the negative side"); | ||
| 192 | CHECK_INT(scan_int("-9223372036854775807", 20, &value), 0); | ||
| 193 | CHECK_INT(value, -INT64_MAX); | ||
| 194 | } | ||
| 195 | |||
| 149 | void test_scan(void) { | 196 | void test_scan(void) { |
| 197 | case_int_whole_field(); | ||
| 198 | case_int_rejects_the_rest(); | ||
| 199 | case_int_overflow(); | ||
| 150 | case_plain_numbers(); | 200 | case_plain_numbers(); |
| 151 | case_stops_at_non_digit(); | 201 | case_stops_at_non_digit(); |
| 152 | case_rejects_whitespace(); | 202 | case_rejects_whitespace(); |
diff --git a/tests/t_scan_urlencoded_query.c b/tests/t_urlencoded.c index f660ea3..6e0ee31 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,44 +217,17 @@ 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 | ||
| 231 | static void case_fixed_int(void) { | ||
| 232 | char positive[] = "123"; | ||
| 233 | char negative[] = "-45"; | ||
| 234 | char trailing[] = "12a"; | ||
| 235 | int value; | ||
| 236 | |||
| 237 | test_case("scan_fixed_int returns the count of unparsed bytes"); | ||
| 238 | CHECK_INT(scan_fixed_int(positive, 3, &value), 0); | ||
| 239 | CHECK_INT(value, 123); | ||
| 240 | |||
| 241 | CHECK_INT(scan_fixed_int(negative, 3, &value), 0); | ||
| 242 | CHECK_INT(value, -45); | ||
| 243 | |||
| 244 | CHECK_INT(scan_fixed_int(trailing, 3, &value), 1); | ||
| 245 | CHECK_INT(value, 12); | ||
| 246 | } | ||
| 247 | |||
| 248 | static void case_fixed_int_bounded(void) { | ||
| 249 | char digits[] = "12345"; | ||
| 250 | int value; | ||
| 251 | |||
| 252 | test_case("scan_fixed_int honours the length it is given"); | ||
| 253 | CHECK_INT(scan_fixed_int(digits, 3, &value), 0); | ||
| 254 | CHECK_INT(value, 123); | ||
| 255 | } | ||
| 256 | |||
| 257 | |||
| 258 | /* The encoding direction. Which bytes survive unescaped is fixed by what | 231 | /* The encoding direction. Which bytes survive unescaped is fixed by what |
| 259 | existing parsers of the urlencoded full scrape expect, so these cases pin | 232 | existing parsers of the urlencoded full scrape expect, so these cases pin |
| 260 | the set rather than describe a preference. */ | 233 | the set rather than describe a preference. */ |
| @@ -265,7 +238,7 @@ static void case_fmt_passes_safe_bytes(void) { | |||
| 265 | size_t n; | 238 | size_t n; |
| 266 | 239 | ||
| 267 | test_case("alphanumerics and the safe punctuation pass through"); | 240 | test_case("alphanumerics and the safe punctuation pass through"); |
| 268 | n = fmt_urlencoded(dest, src, 11); | 241 | n = urlencoded_format(dest, src, 11); |
| 269 | dest[n] = 0; | 242 | dest[n] = 0; |
| 270 | CHECK_INT(n, 11); | 243 | CHECK_INT(n, 11); |
| 271 | CHECK_STR(dest, "aZ09$-./=_~"); | 244 | CHECK_STR(dest, "aZ09$-./=_~"); |
| @@ -277,7 +250,7 @@ static void case_fmt_escapes_the_rest(void) { | |||
| 277 | size_t n; | 250 | size_t n; |
| 278 | 251 | ||
| 279 | test_case("everything else becomes a percent escape"); | 252 | test_case("everything else becomes a percent escape"); |
| 280 | n = fmt_urlencoded(dest, src, sizeof(src)); | 253 | n = urlencoded_format(dest, src, sizeof(src)); |
| 281 | dest[n] = 0; | 254 | dest[n] = 0; |
| 282 | CHECK_INT(n, 18); | 255 | CHECK_INT(n, 18); |
| 283 | CHECK_STR(dest, "%20%25%26%2b%3a%3f"); | 256 | CHECK_STR(dest, "%20%25%26%2b%3a%3f"); |
| @@ -289,7 +262,7 @@ static void case_fmt_hex_is_lower_case(void) { | |||
| 289 | size_t n; | 262 | size_t n; |
| 290 | 263 | ||
| 291 | test_case("escapes use lower case hex digits"); | 264 | test_case("escapes use lower case hex digits"); |
| 292 | n = fmt_urlencoded(dest, src, sizeof(src)); | 265 | n = urlencoded_format(dest, src, sizeof(src)); |
| 293 | dest[n] = 0; | 266 | dest[n] = 0; |
| 294 | CHECK_INT(n, 9); | 267 | CHECK_INT(n, 9); |
| 295 | CHECK_STR(dest, "%00%ab%ff"); | 268 | CHECK_STR(dest, "%00%ab%ff"); |
| @@ -300,7 +273,7 @@ static void case_fmt_zero_length(void) { | |||
| 300 | 273 | ||
| 301 | test_case("a zero length input writes nothing"); | 274 | test_case("a zero length input writes nothing"); |
| 302 | memset(dest, 0x5a, sizeof(dest)); | 275 | memset(dest, 0x5a, sizeof(dest)); |
| 303 | CHECK_INT(fmt_urlencoded(dest, (const uint8_t *)"abc", 0), 0); | 276 | CHECK_INT(urlencoded_format(dest, (const uint8_t *)"abc", 0), 0); |
| 304 | CHECK_MEM(dest, "\x5a\x5a\x5a\x5a", 4); | 277 | CHECK_MEM(dest, "\x5a\x5a\x5a\x5a", 4); |
| 305 | } | 278 | } |
| 306 | 279 | ||
| @@ -312,7 +285,7 @@ static void case_fmt_worst_case_fits(void) { | |||
| 312 | test_case("a hash of entirely unsafe bytes needs three times its length"); | 285 | test_case("a hash of entirely unsafe bytes needs three times its length"); |
| 313 | for (i = 0; i < sizeof(src); ++i) | 286 | for (i = 0; i < sizeof(src); ++i) |
| 314 | src[i] = 0xff; | 287 | src[i] = 0xff; |
| 315 | n = fmt_urlencoded(dest, src, sizeof(src)); | 288 | n = urlencoded_format(dest, src, sizeof(src)); |
| 316 | dest[n] = 0; | 289 | dest[n] = 0; |
| 317 | CHECK_INT(n, 60); | 290 | CHECK_INT(n, 60); |
| 318 | for (i = 0; i < 20; ++i) | 291 | for (i = 0; i < 20; ++i) |
| @@ -328,7 +301,7 @@ static void case_fmt_decoder_disagrees_on_dollar(void) { | |||
| 328 | encoder passes through unescaped, '$' is the only one the decoder then | 301 | encoder passes through unescaped, '$' is the only one the decoder then |
| 329 | refuses, and it refuses the whole string rather than stopping short. | 302 | refuses, and it refuses the whole string rather than stopping short. |
| 330 | Output of the urlencoded full scrape therefore does not round trip | 303 | Output of the urlencoded full scrape therefore does not round trip |
| 331 | through scan_urlencoded_query when a hash contains 0x24. Harmless today | 304 | through urlencoded_parse when a hash contains 0x24. Harmless today |
| 332 | because only third party tooling reads that output. */ | 305 | because only third party tooling reads that output. */ |
| 333 | const uint8_t src[] = {'a', '$', 'b'}; | 306 | const uint8_t src[] = {'a', '$', 'b'}; |
| 334 | char dest[16]; | 307 | char dest[16]; |
| @@ -338,15 +311,15 @@ static void case_fmt_decoder_disagrees_on_dollar(void) { | |||
| 338 | ssize_t got; | 311 | ssize_t got; |
| 339 | 312 | ||
| 340 | test_case("the encoder passes '$' but the decoder rejects it"); | 313 | test_case("the encoder passes '$' but the decoder rejects it"); |
| 341 | n = fmt_urlencoded(dest, src, sizeof(src)); | 314 | n = urlencoded_format(dest, src, sizeof(src)); |
| 342 | dest[n] = 0; | 315 | dest[n] = 0; |
| 343 | CHECK_STR(dest, "a$b"); | 316 | CHECK_STR(dest, "a$b"); |
| 344 | 317 | ||
| 345 | got = scan_urlencoded_query(&p, scanned, SCAN_SEARCHPATH_VALUE); | 318 | got = urlencoded_parse(&p, scanned, SCAN_SEARCHPATH_VALUE); |
| 346 | CHECK_INT(got, -1); | 319 | CHECK_INT(got, -1); |
| 347 | } | 320 | } |
| 348 | 321 | ||
| 349 | void test_scan_urlencoded_query(void) { | 322 | void test_urlencoded(void) { |
| 350 | case_path(); | 323 | case_path(); |
| 351 | case_param_and_value(); | 324 | case_param_and_value(); |
| 352 | case_percent_decoding(); | 325 | case_percent_decoding(); |
| @@ -361,8 +334,6 @@ void test_scan_urlencoded_query(void) { | |||
| 361 | case_raw_high_byte(); | 334 | case_raw_high_byte(); |
| 362 | case_skipvalue(); | 335 | case_skipvalue(); |
| 363 | case_find_keywords(); | 336 | case_find_keywords(); |
| 364 | case_fixed_int(); | ||
| 365 | case_fixed_int_bounded(); | ||
| 366 | case_fmt_passes_safe_bytes(); | 337 | case_fmt_passes_safe_bytes(); |
| 367 | case_fmt_escapes_the_rest(); | 338 | case_fmt_escapes_the_rest(); |
| 368 | case_fmt_hex_is_lower_case(); | 339 | case_fmt_hex_is_lower_case(); |
diff --git a/tests/t_vector.c b/tests/t_vector.c index a8186b2..6e048e1 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 | ||
| @@ -112,6 +112,51 @@ static void case_insert_keeps_order(void) { | |||
| 112 | free(vector.data); | 112 | free(vector.data); |
| 113 | } | 113 | } |
| 114 | 114 | ||
| 115 | /* The peer variant is reached with an empty vector on every first announce | ||
| 116 | for a torrent, and again whenever a swarm empties and refills, because | ||
| 117 | vector_fixup_peers drops the allocation and nulls the data pointer. */ | ||
| 118 | |||
| 119 | static void case_insert_peer_into_empty_vector(void) { | ||
| 120 | ot_vector vector = {NULL, 0, 0}; | ||
| 121 | ot_peer6 low, high; | ||
| 122 | int exactmatch; | ||
| 123 | ot_peer *slot; | ||
| 124 | |||
| 125 | memset(low, 0x11, sizeof(low)); | ||
| 126 | memset(high, 0x99, sizeof(high)); | ||
| 127 | |||
| 128 | test_case("the first peer goes into a vector that has no allocation yet"); | ||
| 129 | slot = vector_find_or_insert_peer(&vector, low, OT_PEER_SIZE6, &exactmatch); | ||
| 130 | CHECK(slot != NULL); | ||
| 131 | CHECK_INT(exactmatch, 0); | ||
| 132 | memcpy(slot, low, OT_PEER_SIZE6); | ||
| 133 | CHECK_INT(vector.size, 1); | ||
| 134 | CHECK_INT(vector.space, OT_VECTOR_MIN_MEMBERS); | ||
| 135 | CHECK_PTR(slot, vector.data); | ||
| 136 | |||
| 137 | test_case("a second peer keeps the order"); | ||
| 138 | slot = vector_find_or_insert_peer(&vector, high, OT_PEER_SIZE6, &exactmatch); | ||
| 139 | CHECK(slot != NULL); | ||
| 140 | CHECK_INT(exactmatch, 0); | ||
| 141 | memcpy(slot, high, OT_PEER_SIZE6); | ||
| 142 | CHECK_INT(vector.size, 2); | ||
| 143 | CHECK_MEM((char *)vector.data, low, OT_PEER_COMPARE_SIZE6); | ||
| 144 | CHECK_MEM((char *)vector.data + OT_PEER_SIZE6, high, OT_PEER_COMPARE_SIZE6); | ||
| 145 | |||
| 146 | test_case("and emptying the vector hands the allocation back"); | ||
| 147 | CHECK(vector_remove_peer(&vector, low, OT_PEER_SIZE6) != 0); | ||
| 148 | CHECK(vector_remove_peer(&vector, high, OT_PEER_SIZE6) != 0); | ||
| 149 | CHECK_INT(vector.size, 0); | ||
| 150 | CHECK_PTR(vector.data, NULL); | ||
| 151 | |||
| 152 | test_case("so the next peer starts from no allocation again"); | ||
| 153 | slot = vector_find_or_insert_peer(&vector, low, OT_PEER_SIZE6, &exactmatch); | ||
| 154 | CHECK(slot != NULL); | ||
| 155 | CHECK_INT(vector.size, 1); | ||
| 156 | |||
| 157 | free(vector.data); | ||
| 158 | } | ||
| 159 | |||
| 115 | static void case_insert_finds_existing(void) { | 160 | static void case_insert_finds_existing(void) { |
| 116 | ot_vector vector = {NULL, 0, 0}; | 161 | ot_vector vector = {NULL, 0, 0}; |
| 117 | const unsigned char five[4] = {0, 0, 0, 5}; | 162 | const unsigned char five[4] = {0, 0, 0, 5}; |
| @@ -257,6 +302,7 @@ void test_vector(void) { | |||
| 257 | case_search_past_the_end(); | 302 | case_search_past_the_end(); |
| 258 | case_search_empty(); | 303 | case_search_empty(); |
| 259 | case_insert_keeps_order(); | 304 | case_insert_keeps_order(); |
| 305 | case_insert_peer_into_empty_vector(); | ||
| 260 | case_insert_finds_existing(); | 306 | case_insert_finds_existing(); |
| 261 | case_insert_compare_prefix(); | 307 | case_insert_compare_prefix(); |
| 262 | case_redistribute_holds_every_peer(); | 308 | case_redistribute_holds_every_peer(); |
diff --git a/tests/test.h b/tests/test.h index fd574c2..0eddc82 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 */ |
| 79 | void test_scan_urlencoded_query(void); | 79 | void test_urlencoded(void); |
| 80 | void test_iovec(void); | 80 | void test_iovec(void); |
| 81 | void test_vector(void); | 81 | void test_vector(void); |
| 82 | void test_random(void); | 82 | void test_random(void); |
| @@ -88,5 +88,8 @@ void test_ip(void); | |||
| 88 | void test_socket(void); | 88 | void test_socket(void); |
| 89 | void test_mutex(void); | 89 | void test_mutex(void); |
| 90 | void test_batch(void); | 90 | void test_batch(void); |
| 91 | void test_fdstate(void); | ||
| 92 | void test_event(void); | ||
| 93 | void test_fmt(void); | ||
| 91 | 94 | ||
| 92 | #endif | 95 | #endif |
diff --git a/trackerlogic.c b/trackerlogic.c index 74fea6d..1f38f07 100644 --- a/trackerlogic.c +++ b/trackerlogic.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | /* Opentracker */ | 14 | /* Opentracker */ |
| 15 | #include "ot_accesslist.h" | 15 | #include "ot_accesslist.h" |
| 16 | #include "ot_clean.h" | 16 | #include "ot_clean.h" |
| 17 | #include "ot_fmt.h" | ||
| 17 | #include "ot_fullscrape.h" | 18 | #include "ot_fullscrape.h" |
| 18 | #include "ot_http.h" | 19 | #include "ot_http.h" |
| 19 | #include "ot_ip.h" | 20 | #include "ot_ip.h" |
| @@ -299,11 +300,12 @@ static size_t return_peers_for_torrent_udp(struct ot_workstruct *ws, ot_torrent | |||
| 299 | } | 300 | } |
| 300 | 301 | ||
| 301 | static size_t return_peers_for_torrent_tcp(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply) { | 302 | static size_t return_peers_for_torrent_tcp(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply) { |
| 302 | char *r = reply; | 303 | char *r = reply; |
| 303 | int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM; | 304 | const char *r_end = ws->outbuf + G_OUTBUF_SIZE; |
| 304 | size_t seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count; | 305 | int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM; |
| 305 | size_t down_count = torrent->peer_list6->down_count + torrent->peer_list4->down_count; | 306 | size_t seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count; |
| 306 | size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count - seed_count; | 307 | size_t down_count = torrent->peer_list6->down_count + torrent->peer_list4->down_count; |
| 308 | size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count - seed_count; | ||
| 307 | 309 | ||
| 308 | /* Simple case: amount of peers in both lists is less than requested, here we return all results */ | 310 | /* Simple case: amount of peers in both lists is less than requested, here we return all results */ |
| 309 | size_t amount_v4 = torrent->peer_list4->peer_count; | 311 | size_t amount_v4 = torrent->peer_list4->peer_count; |
| @@ -341,11 +343,11 @@ static size_t return_peers_for_torrent_tcp(struct ot_workstruct *ws, ot_torrent | |||
| 341 | ++amount_v4; | 343 | ++amount_v4; |
| 342 | } | 344 | } |
| 343 | 345 | ||
| 344 | r += | 346 | fmt_append(&r, r_end, "d8:completei%zde10:downloadedi%zde10:incompletei%zde8:intervali%ie12:min intervali%ie", seed_count, down_count, peer_count, erval, |
| 345 | sprintf(r, "d8:completei%zde10:downloadedi%zde10:incompletei%zde8:intervali%ie12:min intervali%ie", seed_count, down_count, peer_count, erval, erval / 2); | 347 | erval / 2); |
| 346 | 348 | ||
| 347 | if (amount_v4) { | 349 | if (amount_v4) { |
| 348 | r += sprintf(r, PEERS_BENCODED4 "%zd:", OT_PEER_COMPARE_SIZE4 * amount_v4); | 350 | fmt_append(&r, r_end, PEERS_BENCODED4 "%zd:", OT_PEER_COMPARE_SIZE4 * amount_v4); |
| 349 | if (amount_v4 == torrent->peer_list4->peer_count) | 351 | if (amount_v4 == torrent->peer_list4->peer_count) |
| 350 | r += return_peers_all(torrent->peer_list4, OT_PEER_SIZE4, r); | 352 | r += return_peers_all(torrent->peer_list4, OT_PEER_SIZE4, r); |
| 351 | else | 353 | else |
| @@ -353,14 +355,14 @@ static size_t return_peers_for_torrent_tcp(struct ot_workstruct *ws, ot_torrent | |||
| 353 | } | 355 | } |
| 354 | 356 | ||
| 355 | if (amount_v6) { | 357 | if (amount_v6) { |
| 356 | r += sprintf(r, PEERS_BENCODED6 "%zd:", OT_PEER_COMPARE_SIZE6 * amount_v6); | 358 | fmt_append(&r, r_end, PEERS_BENCODED6 "%zd:", OT_PEER_COMPARE_SIZE6 * amount_v6); |
| 357 | if (amount_v6 == torrent->peer_list6->peer_count) | 359 | if (amount_v6 == torrent->peer_list6->peer_count) |
| 358 | r += return_peers_all(torrent->peer_list6, OT_PEER_SIZE6, r); | 360 | r += return_peers_all(torrent->peer_list6, OT_PEER_SIZE6, r); |
| 359 | else | 361 | else |
| 360 | r += return_peers_selection(ws, torrent->peer_list6, OT_PEER_SIZE6, amount_v6, r); | 362 | r += return_peers_selection(ws, torrent->peer_list6, OT_PEER_SIZE6, amount_v6, r); |
| 361 | } | 363 | } |
| 362 | 364 | ||
| 363 | *r++ = 'e'; | 365 | fmt_append(&r, r_end, "e"); |
| 364 | 366 | ||
| 365 | return r - reply; | 367 | return r - reply; |
| 366 | } | 368 | } |
| @@ -381,7 +383,7 @@ size_t return_peers_for_torrent(struct ot_workstruct *ws, ot_torrent *torrent, s | |||
| 381 | size_t return_udp_scrape_for_torrent(ot_hash const hash, char *reply) { | 383 | size_t return_udp_scrape_for_torrent(ot_hash const hash, char *reply) { |
| 382 | int exactmatch, delta_torrentcount = 0; | 384 | int exactmatch, delta_torrentcount = 0; |
| 383 | ot_vector *torrents_list = mutex_bucket_lock_by_hash(hash); | 385 | 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); | 386 | ot_torrent *torrent = vector_binary_search(hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch); |
| 385 | 387 | ||
| 386 | if (!exactmatch) { | 388 | if (!exactmatch) { |
| 387 | memset(reply, 0, 12); | 389 | memset(reply, 0, 12); |
| @@ -403,47 +405,41 @@ size_t return_udp_scrape_for_torrent(ot_hash const hash, char *reply) { | |||
| 403 | } | 405 | } |
| 404 | 406 | ||
| 405 | /* Fetches scrape info for a specific torrent */ | 407 | /* Fetches scrape info for a specific torrent */ |
| 406 | size_t return_tcp_scrape_for_torrent(ot_hash const *hash_list, size_t amount, char *reply) { | 408 | void return_tcp_scrape_for_torrent(ot_hash const *hash_list, size_t amount, char **reply, const char *reply_end) { |
| 407 | char *r = reply; | 409 | int exactmatch; |
| 408 | int exactmatch; | ||
| 409 | size_t i; | 410 | size_t i; |
| 410 | 411 | ||
| 411 | r += sprintf(r, "d5:filesd"); | 412 | fmt_append(reply, reply_end, "d5:filesd"); |
| 412 | 413 | ||
| 413 | for (i = 0; i < amount; ++i) { | 414 | for (i = 0; i < amount; ++i) { |
| 414 | int delta_torrentcount = 0; | 415 | int delta_torrentcount = 0; |
| 415 | ot_hash const *hash = hash_list + i; | 416 | ot_hash const *hash = hash_list + i; |
| 416 | ot_vector *torrents_list = mutex_bucket_lock_by_hash(*hash); | 417 | 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); | 418 | ot_torrent *torrent = vector_binary_search(hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch); |
| 418 | 419 | ||
| 419 | if (exactmatch) { | 420 | if (exactmatch) { |
| 420 | if (clean_single_torrent(torrent)) { | 421 | if (clean_single_torrent(torrent)) { |
| 421 | vector_remove_torrent(torrents_list, torrent); | 422 | vector_remove_torrent(torrents_list, torrent); |
| 422 | delta_torrentcount = -1; | 423 | delta_torrentcount = -1; |
| 423 | } else { | 424 | } else { |
| 424 | *r++ = '2'; | 425 | fmt_append(reply, reply_end, "20:"); |
| 425 | *r++ = '0'; | 426 | fmt_bytes(reply, reply_end, hash, sizeof(ot_hash)); |
| 426 | *r++ = ':'; | 427 | fmt_append(reply, reply_end, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", |
| 427 | memcpy(r, hash, sizeof(ot_hash)); | 428 | torrent->peer_list6->seed_count + torrent->peer_list4->seed_count, torrent->peer_list6->down_count + torrent->peer_list4->down_count, |
| 428 | r += sizeof(ot_hash); | 429 | torrent->peer_list6->peer_count + torrent->peer_list4->peer_count - torrent->peer_list6->seed_count - torrent->peer_list4->seed_count); |
| 429 | r += sprintf(r, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", torrent->peer_list6->seed_count + torrent->peer_list4->seed_count, | ||
| 430 | torrent->peer_list6->down_count + torrent->peer_list4->down_count, | ||
| 431 | torrent->peer_list6->peer_count + torrent->peer_list4->peer_count - torrent->peer_list6->seed_count - torrent->peer_list4->seed_count); | ||
| 432 | } | 430 | } |
| 433 | } | 431 | } |
| 434 | mutex_bucket_unlock_by_hash(*hash, delta_torrentcount); | 432 | mutex_bucket_unlock_by_hash(*hash, delta_torrentcount); |
| 435 | } | 433 | } |
| 436 | 434 | ||
| 437 | *r++ = 'e'; | 435 | fmt_append(reply, reply_end, "ee"); |
| 438 | *r++ = 'e'; | ||
| 439 | return r - reply; | ||
| 440 | } | 436 | } |
| 441 | 437 | ||
| 442 | static ot_peerlist dummy_list; | 438 | static ot_peerlist dummy_list; |
| 443 | size_t remove_peer_from_torrent(PROTO_FLAG proto, struct ot_workstruct *ws) { | 439 | size_t remove_peer_from_torrent(PROTO_FLAG proto, struct ot_workstruct *ws) { |
| 444 | int exactmatch; | 440 | int exactmatch; |
| 445 | ot_vector *torrents_list = mutex_bucket_lock_by_hash(*ws->hash); | 441 | 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); | 442 | 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; | 443 | ot_peerlist *peer_list = &dummy_list; |
| 448 | size_t peer_size; /* initialized in next line */ | 444 | size_t peer_size; /* initialized in next line */ |
| 449 | ot_peer const *peer_src = peer_from_peer6(&ws->peer, &peer_size); | 445 | ot_peer const *peer_src = peer_from_peer6(&ws->peer, &peer_size); |
| @@ -472,9 +468,12 @@ size_t remove_peer_from_torrent(PROTO_FLAG proto, struct ot_workstruct *ws) { | |||
| 472 | } | 468 | } |
| 473 | 469 | ||
| 474 | if (proto == FLAG_TCP) { | 470 | if (proto == FLAG_TCP) { |
| 475 | int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM; | 471 | int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM; |
| 476 | ws->reply_size = sprintf(ws->reply, "d8:completei%zde10:incompletei%zde8:intervali%ie12:min intervali%ie%s0:e", seed_count, peer_count - seed_count, erval, | 472 | char *r = ws->reply; |
| 477 | erval / 2, peer_size == OT_PEER_SIZE6 ? PEERS_BENCODED6 : PEERS_BENCODED4); | 473 | |
| 474 | fmt_append(&r, ws->outbuf + G_OUTBUF_SIZE, "d8:completei%zde10:incompletei%zde8:intervali%ie12:min intervali%ie%s0:e", seed_count, peer_count - seed_count, | ||
| 475 | erval, erval / 2, peer_size == OT_PEER_SIZE6 ? PEERS_BENCODED6 : PEERS_BENCODED4); | ||
| 476 | ws->reply_size = r - ws->reply; | ||
| 478 | } | 477 | } |
| 479 | 478 | ||
| 480 | /* Handle UDP reply */ | 479 | /* Handle UDP reply */ |
diff --git a/trackerlogic.h b/trackerlogic.h index 42018c3..63abf78 100644 --- a/trackerlogic.h +++ b/trackerlogic.h | |||
| @@ -187,7 +187,7 @@ void exerr(const char *message); | |||
| 187 | otherwise it is released in return_peers_for_torrent */ | 187 | otherwise it is released in return_peers_for_torrent */ |
| 188 | size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstruct *ws, size_t amount); | 188 | size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstruct *ws, size_t amount); |
| 189 | size_t remove_peer_from_torrent(PROTO_FLAG proto, struct ot_workstruct *ws); | 189 | size_t remove_peer_from_torrent(PROTO_FLAG proto, struct ot_workstruct *ws); |
| 190 | size_t return_tcp_scrape_for_torrent(ot_hash const *hash_list, size_t amount, char *reply); | 190 | void return_tcp_scrape_for_torrent(ot_hash const *hash_list, size_t amount, char **reply, const char *reply_end); |
| 191 | size_t return_udp_scrape_for_torrent(ot_hash const hash, char *reply); | 191 | size_t return_udp_scrape_for_torrent(ot_hash const hash, char *reply); |
| 192 | void add_torrent_from_saved_state(ot_hash const hash, ot_time base, size_t down_count); | 192 | void add_torrent_from_saved_state(ot_hash const hash, ot_time base, size_t down_count); |
| 193 | #ifdef _DEBUG_RANDOMTORRENTS | 193 | #ifdef _DEBUG_RANDOMTORRENTS |
