summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile46
-rw-r--r--opentracker.c43
-rw-r--r--ot_clean.h2
-rw-r--r--ot_fullscrape.c2
-rw-r--r--ot_fullscrape.h2
-rw-r--r--ot_http.c41
-rw-r--r--ot_http.h14
-rw-r--r--ot_iovec.c3
-rw-r--r--ot_livesync.c42
-rw-r--r--ot_livesync.h4
-rw-r--r--ot_mutex.c13
-rw-r--r--ot_mutex.h14
-rw-r--r--ot_socket.c256
-rw-r--r--ot_socket.h45
-rw-r--r--ot_stats.c10
-rw-r--r--ot_stats.h2
-rw-r--r--ot_sync.c2
-rw-r--r--ot_sync.h4
-rw-r--r--ot_udp.c26
-rw-r--r--ot_udp.h8
-rw-r--r--ot_vector.c2
-rw-r--r--ot_vector.h5
-rw-r--r--proxy.c81
-rw-r--r--scan_urlencoded_query.c14
-rw-r--r--scan_urlencoded_query.h4
-rw-r--r--tests/runner.c1
-rw-r--r--tests/t_scan_urlencoded_query.c9
-rw-r--r--tests/t_socket.c307
-rw-r--r--tests/test.h1
-rw-r--r--trackerlogic.c4
30 files changed, 798 insertions, 209 deletions
diff --git a/Makefile b/Makefile
index 27d635d..9a09e7b 100644
--- a/Makefile
+++ b/Makefile
@@ -68,36 +68,50 @@ CFLAGS+=-I$(LIBOWFAT_HEADERS) -DGIT_VERSION=$(GIT_VERSION) -Wall -pipe -pthread
68LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread 68LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread
69 69
70BINARY =opentracker 70BINARY =opentracker
71HEADERS=trackerlogic.h scan_urlencoded_query.h ot_buf.h ot_hex.h ot_ip.h ot_scan.h ot_mutex.h ot_random.h ot_stats.h ot_vector.h ot_clean.h ot_udp.h ot_iovec.h ot_fullscrape.h ot_accesslist.h ot_http.h ot_livesync.h ot_rijndael.h 71HEADERS=trackerlogic.h scan_urlencoded_query.h ot_buf.h ot_hex.h ot_ip.h ot_scan.h ot_socket.h ot_mutex.h ot_random.h ot_stats.h ot_vector.h ot_clean.h ot_udp.h ot_iovec.h ot_fullscrape.h ot_accesslist.h ot_http.h ot_livesync.h ot_rijndael.h
72SOURCES=opentracker.c trackerlogic.c scan_urlencoded_query.c ot_buf.c ot_hex.c ot_ip.c ot_scan.c ot_mutex.c ot_random.c ot_stats.c ot_vector.c ot_clean.c ot_udp.c ot_iovec.c ot_fullscrape.c ot_accesslist.c ot_http.c ot_livesync.c ot_rijndael.c 72SOURCES=opentracker.c trackerlogic.c scan_urlencoded_query.c ot_buf.c ot_hex.c ot_ip.c ot_scan.c ot_socket.c ot_mutex.c ot_random.c ot_stats.c ot_vector.c ot_clean.c ot_udp.c ot_iovec.c ot_fullscrape.c ot_accesslist.c ot_http.c ot_livesync.c ot_rijndael.c
73SOURCES_proxy=proxy.c ot_vector.c ot_mutex.c ot_random.c ot_scan.c ot_ip.c 73SOURCES_proxy=proxy.c ot_vector.c ot_mutex.c ot_random.c ot_scan.c ot_ip.c ot_socket.c
74 74
75OBJECTS = $(SOURCES:%.c=%.o) 75OBJECTS = $(SOURCES:%.c=%.o)
76OBJECTS_debug = $(SOURCES:%.c=%.debug.o) 76OBJECTS_debug = $(SOURCES:%.c=%.debug.o)
77OBJECTS_proxy = $(SOURCES_proxy:%.c=%.o) 77OBJECTS_proxy = $(SOURCES_proxy:%.c=%.proxy.o)
78OBJECTS_proxy_debug = $(SOURCES_proxy:%.c=%.debug.o) 78OBJECTS_proxy_debug = $(SOURCES_proxy:%.c=%.proxy.debug.o)
79 79
80.SUFFIXES: .debug.o .o .c 80.SUFFIXES: .proxy.debug.o .proxy.o .debug.o .o .c
81 81
82all: $(BINARY) $(BINARY).debug 82all: $(BINARY) $(BINARY).debug
83 83
84CFLAGS_production = $(CFLAGS) $(OPTS_production) $(FEATURES) 84CFLAGS_production = $(CFLAGS) $(OPTS_production) $(FEATURES)
85CFLAGS_debug = $(CFLAGS) $(OPTS_debug) $(FEATURES) 85CFLAGS_debug = $(CFLAGS) $(OPTS_debug) $(FEATURES)
86 86
87$(BINARY): $(OBJECTS) $(HEADERS) 87CFLAGS_proxy = $(CFLAGS_production) -DWANT_SYNC_LIVE
88CFLAGS_proxy_debug = $(CFLAGS_debug) -DWANT_SYNC_LIVE
89
90$(BINARY): $(OBJECTS)
88 $(CC) -o $@ $(OBJECTS) $(LDFLAGS) 91 $(CC) -o $@ $(OBJECTS) $(LDFLAGS)
89 $(STRIP) $@ 92 $(STRIP) $@
90$(BINARY).debug: $(OBJECTS_debug) $(HEADERS) 93$(BINARY).debug: $(OBJECTS_debug)
91 $(CC) -o $@ $(OBJECTS_debug) $(LDFLAGS) 94 $(CC) -o $@ $(OBJECTS_debug) $(LDFLAGS)
92proxy: $(OBJECTS_proxy) $(HEADERS) 95proxy: $(OBJECTS_proxy)
93 $(CC) -o $@ $(OBJECTS_proxy) $(CFLAGS_production) $(LDFLAGS) 96 $(CC) -o $@ $(OBJECTS_proxy) $(CFLAGS_production) $(LDFLAGS)
94proxy.debug: $(OBJECTS_proxy_debug) $(HEADERS) 97proxy.debug: $(OBJECTS_proxy_debug)
95 $(CC) -o $@ $(OBJECTS_proxy_debug) $(LDFLAGS) 98 $(CC) -o $@ $(OBJECTS_proxy_debug) $(LDFLAGS)
96 99
97.c.debug.o : $(HEADERS) 100$(OBJECTS): $(HEADERS)
101$(OBJECTS_debug): $(HEADERS)
102$(OBJECTS_proxy): $(HEADERS)
103$(OBJECTS_proxy_debug): $(HEADERS)
104
105.c.proxy.debug.o :
106 $(CC) -c -o $@ $(CFLAGS_proxy_debug) $(<:.proxy.debug.o=.c)
107
108.c.proxy.o :
109 $(CC) -c -o $@ $(CFLAGS_proxy) $(<:.proxy.o=.c)
110
111.c.debug.o :
98 $(CC) -c -o $@ $(CFLAGS_debug) $(<:.debug.o=.c) 112 $(CC) -c -o $@ $(CFLAGS_debug) $(<:.debug.o=.c)
99 113
100.c.o : $(HEADERS) 114.c.o :
101 $(CC) -c -o $@ $(CFLAGS_production) $< 115 $(CC) -c -o $@ $(CFLAGS_production) $<
102 116
103# Unit tests. The modules under test are compiled straight into the test 117# Unit tests. The modules under test are compiled straight into the test
@@ -105,12 +119,12 @@ proxy.debug: $(OBJECTS_proxy_debug) $(HEADERS)
105# has to. tests/stubs.c supplies the few symbols those modules reference but 119# has to. tests/stubs.c supplies the few symbols those modules reference but
106# the tests never reach. 120# the tests never reach.
107TESTBIN=tests/runner 121TESTBIN=tests/runner
108TEST_SOURCES=tests/runner.c tests/test.c tests/stubs.c tests/t_scan_urlencoded_query.c tests/t_iovec.c tests/t_vector.c tests/t_random.c tests/t_hex.c tests/t_accesslist.c tests/t_scan.c tests/t_buf.c tests/t_ip.c 122TEST_SOURCES=tests/runner.c tests/test.c tests/stubs.c tests/t_scan_urlencoded_query.c tests/t_iovec.c tests/t_vector.c tests/t_random.c tests/t_hex.c tests/t_accesslist.c tests/t_scan.c tests/t_buf.c tests/t_ip.c tests/t_socket.c
109TEST_UNITS=scan_urlencoded_query.c ot_buf.c ot_ip.c ot_iovec.c ot_vector.c ot_random.c ot_hex.c ot_scan.c ot_accesslist.c 123TEST_UNITS=scan_urlencoded_query.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
110TEST_HEADERS=tests/test.h $(HEADERS) 124TEST_HEADERS=tests/test.h $(HEADERS)
111# Tests reach code that sits behind feature flags, so the units under test are 125# Tests reach code that sits behind feature flags, so the units under test are
112# built with those features enabled. A test binary is not a production binary. 126# built with those features enabled. A test binary is not a production binary.
113FEATURES_test=$(FEATURES) -DWANT_ACCESSLIST_BLACK 127FEATURES_test=$(FEATURES) -DWANT_ACCESSLIST_BLACK -DWANT_SYNC_LIVE
114CFLAGS_test = $(CFLAGS) -I. -g -O1 $(FEATURES_test) 128CFLAGS_test = $(CFLAGS) -I. -g -O1 $(FEATURES_test)
115 129
116$(TESTBIN): $(TEST_SOURCES) $(TEST_UNITS) $(TEST_HEADERS) 130$(TESTBIN): $(TEST_SOURCES) $(TEST_UNITS) $(TEST_HEADERS)
@@ -122,7 +136,7 @@ check: $(TESTBIN)
122.PHONY: all check clean install 136.PHONY: all check clean install
123 137
124clean: 138clean:
125 rm -rf opentracker opentracker.debug $(TESTBIN) *.o *~ 139 rm -rf opentracker opentracker.debug proxy proxy.debug $(TESTBIN) *.o *~
126 140
127install: 141install:
128 install -m 755 opentracker $(DESTDIR)$(BINDIR) 142 install -m 755 opentracker $(DESTDIR)$(BINDIR)
diff --git a/opentracker.c b/opentracker.c
index 8a39bde..5b197d1 100644
--- a/opentracker.c
+++ b/opentracker.c
@@ -24,7 +24,6 @@
24/* Libowfat */ 24/* Libowfat */
25#include "io.h" 25#include "io.h"
26#include "iob.h" 26#include "iob.h"
27#include "socket.h"
28 27
29/* Opentracker */ 28/* Opentracker */
30#include "ot_accesslist.h" 29#include "ot_accesslist.h"
@@ -35,6 +34,7 @@
35#include "ot_mutex.h" 34#include "ot_mutex.h"
36#include "ot_random.h" 35#include "ot_random.h"
37#include "ot_scan.h" 36#include "ot_scan.h"
37#include "ot_socket.h"
38#include "ot_stats.h" 38#include "ot_stats.h"
39#include "ot_udp.h" 39#include "ot_udp.h"
40#include "trackerlogic.h" 40#include "trackerlogic.h"
@@ -156,7 +156,7 @@ static ssize_t header_complete(char *request, ssize_t byte_count) {
156 return 0; 156 return 0;
157} 157}
158 158
159static void handle_dead(const int64 sock) { 159static void handle_dead(const int64_t sock) {
160 struct http_data *cookie = io_getcookie(sock); 160 struct http_data *cookie = io_getcookie(sock);
161 if (cookie) { 161 if (cookie) {
162 size_t i; 162 size_t i;
@@ -171,7 +171,7 @@ static void handle_dead(const int64 sock) {
171 io_close(sock); 171 io_close(sock);
172} 172}
173 173
174static void handle_read(const int64 sock, struct ot_workstruct *ws) { 174static void handle_read(const int64_t sock, struct ot_workstruct *ws) {
175 struct http_data *cookie = io_getcookie(sock); 175 struct http_data *cookie = io_getcookie(sock);
176 ssize_t byte_count = io_tryread(sock, ws->inbuf, G_INBUF_SIZE); 176 ssize_t byte_count = io_tryread(sock, ws->inbuf, G_INBUF_SIZE);
177 177
@@ -215,7 +215,7 @@ static void handle_read(const int64 sock, struct ot_workstruct *ws) {
215 } 215 }
216} 216}
217 217
218static void handle_write(const int64 sock) { 218static void handle_write(const int64_t sock) {
219 struct http_data *cookie = io_getcookie(sock); 219 struct http_data *cookie = io_getcookie(sock);
220 size_t i; 220 size_t i;
221 int chunked = 0; 221 int chunked = 0;
@@ -227,7 +227,7 @@ static void handle_write(const int64 sock) {
227 227
228 for (i = 0; i < cookie->batches; ++i) { 228 for (i = 0; i < cookie->batches; ++i) {
229 if (cookie->batch[i].bytesleft) { 229 if (cookie->batch[i].bytesleft) {
230 int64 res = iob_send(sock, cookie->batch + i); 230 int64_t res = iob_send(sock, cookie->batch + i);
231 231
232 if (res == -3) { 232 if (res == -3) {
233 handle_dead(sock); 233 handle_dead(sock);
@@ -250,14 +250,14 @@ static void handle_write(const int64 sock) {
250 handle_dead(sock); 250 handle_dead(sock);
251} 251}
252 252
253static void handle_accept(const int64 serversocket) { 253static void handle_accept(const int64_t serversocket) {
254 struct http_data *cookie; 254 struct http_data *cookie;
255 int64 sock; 255 int64_t sock;
256 ot_ip6 ip; 256 ot_ip6 ip;
257 uint16 port; 257 uint16_t port;
258 tai6464 t; 258 tai6464 t;
259 259
260 while ((sock = socket_accept6(serversocket, ip, &port, NULL)) != -1) { 260 while ((sock = socket_accept(serversocket, ip, &port)) != -1) {
261 261
262 /* Put fd into a non-blocking mode */ 262 /* Put fd into a non-blocking mode */
263 io_nonblock(sock); 263 io_nonblock(sock);
@@ -305,7 +305,7 @@ static void *server_mainloop(void *args) {
305 random_bytes(ws.rand48_state, sizeof(ws.rand48_state)); 305 random_bytes(ws.rand48_state, sizeof(ws.rand48_state));
306 306
307 for (;;) { 307 for (;;) {
308 int64 sock; 308 int64_t sock;
309 309
310 io_wait(); 310 io_wait();
311 311
@@ -339,7 +339,7 @@ static void *server_mainloop(void *args) {
339} 339}
340 340
341static int64_t ot_try_bind(ot_ip6 ip, uint16_t port, PROTO_FLAG proto) { 341static int64_t ot_try_bind(ot_ip6 ip, uint16_t port, PROTO_FLAG proto) {
342 int64 sock = proto == FLAG_TCP ? socket_tcp6() : socket_udp6(); 342 int64_t sock;
343 343
344#ifdef _DEBUG 344#ifdef _DEBUG
345 { 345 {
@@ -352,11 +352,13 @@ static int64_t ot_try_bind(ot_ip6 ip, uint16_t port, PROTO_FLAG proto) {
352 } 352 }
353#endif 353#endif
354 354
355 if (socket_bind6_reuse(sock, ip, port, 0) == -1) 355 if (proto == FLAG_TCP) {
356 panic("socket_bind6_reuse"); 356 if ((sock = socket_tcp_listen(ip, port)) == -1)
357 357 panic("socket_tcp_listen");
358 if ((proto == FLAG_TCP) && (socket_listen(sock, SOMAXCONN) == -1)) 358 } else {
359 panic("socket_listen"); 359 if ((sock = socket_udp_bind(ip, port)) == -1)
360 panic("socket_udp_bind");
361 }
360 362
361 if (!io_fd(sock)) 363 if (!io_fd(sock))
362 panic("io_fd"); 364 panic("io_fd");
@@ -450,6 +452,7 @@ typedef enum {
450 CONFIG_LISTEN_TCP, 452 CONFIG_LISTEN_TCP,
451 CONFIG_LISTEN_UDP, 453 CONFIG_LISTEN_UDP,
452 CONFIG_LISTEN_UDP_WORKERS, 454 CONFIG_LISTEN_UDP_WORKERS,
455 CONFIG_LISTEN_REUSEPORT,
453 CONFIG_ACCESS_WHITELIST, 456 CONFIG_ACCESS_WHITELIST,
454 CONFIG_ACCESS_BLACKLIST, 457 CONFIG_ACCESS_BLACKLIST,
455 CONFIG_ACCESS_FIFO_ADD, 458 CONFIG_ACCESS_FIFO_ADD,
@@ -477,6 +480,7 @@ static const config_key config_keys[] = {
477 CONFIG_KEY("listen.tcp", CONFIG_LISTEN_TCP), 480 CONFIG_KEY("listen.tcp", CONFIG_LISTEN_TCP),
478 CONFIG_KEY("listen.udp", CONFIG_LISTEN_UDP), 481 CONFIG_KEY("listen.udp", CONFIG_LISTEN_UDP),
479 CONFIG_KEY("listen.udp.workers", CONFIG_LISTEN_UDP_WORKERS), 482 CONFIG_KEY("listen.udp.workers", CONFIG_LISTEN_UDP_WORKERS),
483 CONFIG_KEY("listen.reuseport", CONFIG_LISTEN_REUSEPORT),
480#ifdef WANT_ACCESSLIST_WHITE 484#ifdef WANT_ACCESSLIST_WHITE
481 CONFIG_KEY("access.whitelist", CONFIG_ACCESS_WHITELIST), 485 CONFIG_KEY("access.whitelist", CONFIG_ACCESS_WHITELIST),
482#elif defined(WANT_ACCESSLIST_BLACK) 486#elif defined(WANT_ACCESSLIST_BLACK)
@@ -582,6 +586,13 @@ int parse_configfile(char *config_filename) {
582 if (scan_udec(p, &value, UINT_MAX)) 586 if (scan_udec(p, &value, UINT_MAX))
583 g_udp_workers = (unsigned int)value; 587 g_udp_workers = (unsigned int)value;
584 break; 588 break;
589 case CONFIG_LISTEN_REUSEPORT:
590 /* Like listen.udp.workers this is read when a socket is bound, so it
591 has to come before the listen lines it should apply to. */
592 if (!scan_udec(p, &value, 1))
593 goto parse_error;
594 g_socket_reuseport = (int)value;
595 break;
585#ifdef WANT_ACCESSLIST_WHITE 596#ifdef WANT_ACCESSLIST_WHITE
586 case CONFIG_ACCESS_WHITELIST: 597 case CONFIG_ACCESS_WHITELIST:
587 set_config_option(&g_accesslist_filename, p); 598 set_config_option(&g_accesslist_filename, p);
diff --git a/ot_clean.h b/ot_clean.h
index e8bcdc1..1484d82 100644
--- a/ot_clean.h
+++ b/ot_clean.h
@@ -6,6 +6,8 @@
6#ifndef OT_CLEAN_H__ 6#ifndef OT_CLEAN_H__
7#define OT_CLEAN_H__ 7#define OT_CLEAN_H__
8 8
9#include "trackerlogic.h"
10
9/* The amount of time a clean cycle should take */ 11/* The amount of time a clean cycle should take */
10#define OT_CLEAN_INTERVAL_MINUTES 2 12#define OT_CLEAN_INTERVAL_MINUTES 2
11 13
diff --git a/ot_fullscrape.c b/ot_fullscrape.c
index 3d0e2f1..9dcbe68 100644
--- a/ot_fullscrape.c
+++ b/ot_fullscrape.c
@@ -82,7 +82,7 @@ void fullscrape_deinit(void) {
82 pthread_cancel( thread_id ); 82 pthread_cancel( thread_id );
83} 83}
84 84
85void fullscrape_deliver( int64 sock, ot_tasktype tasktype ) { 85void fullscrape_deliver( int64_t sock, ot_tasktype tasktype ) {
86 mutex_workqueue_pushtask( sock, tasktype ); 86 mutex_workqueue_pushtask( sock, tasktype );
87} 87}
88 88
diff --git a/ot_fullscrape.h b/ot_fullscrape.h
index 843e039..0e7690c 100644
--- a/ot_fullscrape.h
+++ b/ot_fullscrape.h
@@ -12,7 +12,7 @@
12 12
13void fullscrape_init(void); 13void fullscrape_init(void);
14void fullscrape_deinit(void); 14void fullscrape_deinit(void);
15void fullscrape_deliver(int64 sock, ot_tasktype tasktype); 15void fullscrape_deliver(int64_t sock, ot_tasktype tasktype);
16 16
17#else 17#else
18 18
diff --git a/ot_http.c b/ot_http.c
index dcf634f..46ca5a9 100644
--- a/ot_http.c
+++ b/ot_http.c
@@ -44,7 +44,7 @@ ssize_t g_stats_path_len;
44 44
45enum { SUCCESS_HTTP_HEADER_LENGTH = 80, SUCCESS_HTTP_SIZE_OFF = 17 }; 45enum { SUCCESS_HTTP_HEADER_LENGTH = 80, SUCCESS_HTTP_SIZE_OFF = 17 };
46 46
47static void http_senddata(const int64 sock, struct ot_workstruct *ws) { 47static void http_senddata(const int64_t sock, struct ot_workstruct *ws) {
48 struct http_data *cookie = io_getcookie(sock); 48 struct http_data *cookie = io_getcookie(sock);
49 ssize_t written_size; 49 ssize_t written_size;
50 50
@@ -124,7 +124,7 @@ static void http_senddata(const int64 sock, struct ot_workstruct *ws) {
124#define HTTPERROR_403_IP return http_issue_error(sock, ws, CODE_HTTPERROR_403_IP) 124#define HTTPERROR_403_IP return http_issue_error(sock, ws, CODE_HTTPERROR_403_IP)
125#define HTTPERROR_404 return http_issue_error(sock, ws, CODE_HTTPERROR_404) 125#define HTTPERROR_404 return http_issue_error(sock, ws, CODE_HTTPERROR_404)
126#define HTTPERROR_500 return http_issue_error(sock, ws, CODE_HTTPERROR_500) 126#define HTTPERROR_500 return http_issue_error(sock, ws, CODE_HTTPERROR_500)
127ssize_t http_issue_error(const int64 sock, struct ot_workstruct *ws, int code) { 127ssize_t http_issue_error(const int64_t sock, struct ot_workstruct *ws, int code) {
128 char *error_code[] = {"302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", "402 Payment Required", 128 char *error_code[] = {"302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", "402 Payment Required",
129 "403 Not Modest", "403 Access Denied", "404 Not Found", "500 Internal Server Error"}; 129 "403 Not Modest", "403 Access Denied", "404 Not Found", "500 Internal Server Error"};
130 char *title = error_code[code]; 130 char *title = error_code[code];
@@ -144,7 +144,7 @@ ssize_t http_issue_error(const int64 sock, struct ot_workstruct *ws, int code) {
144 return ws->reply_size = -2; 144 return ws->reply_size = -2;
145} 145}
146 146
147ssize_t http_sendiovecdata(const int64 sock, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial) { 147ssize_t http_sendiovecdata(const int64_t sock, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial) {
148 struct http_data *cookie = io_getcookie(sock); 148 struct http_data *cookie = io_getcookie(sock);
149 io_batch *current; 149 io_batch *current;
150 char *header; 150 char *header;
@@ -158,6 +158,13 @@ ssize_t http_sendiovecdata(const int64 sock, struct ot_workstruct *ws, size_t io
158 HTTPERROR_500; 158 HTTPERROR_500;
159 } 159 }
160 160
161 /* Descriptor numbers get recycled, so a result can only belong to this
162 connection if this connection is in fact waiting for one. */
163 if (!(cookie->flag & (STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER))) {
164 iovec_free(&iovec_entries, &iovector);
165 return 0;
166 }
167
161 /* If this socket collected request in a buffer, free it now */ 168 /* If this socket collected request in a buffer, free it now */
162 buf_reset(&cookie->request); 169 buf_reset(&cookie->request);
163 170
@@ -236,7 +243,7 @@ ssize_t http_sendiovecdata(const int64 sock, struct ot_workstruct *ws, size_t io
236 return 0; 243 return 0;
237} 244}
238 245
239static ssize_t http_handle_stats(const int64 sock, struct ot_workstruct *ws, char *read_ptr) { 246static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) {
240 static const ot_keywords keywords_main[] = {{"mode", 1}, {"format", 2}, {"info_hash", 3}, {NULL, -3}}; 247 static const ot_keywords keywords_main[] = {{"mode", 1}, {"format", 2}, {"info_hash", 3}, {NULL, -3}};
241 static const ot_keywords keywords_mode[] = {{"peer", TASK_STATS_PEERS}, 248 static const ot_keywords keywords_mode[] = {{"peer", TASK_STATS_PEERS},
242 {"conn", TASK_STATS_CONNS}, 249 {"conn", TASK_STATS_CONNS},
@@ -267,12 +274,16 @@ static ssize_t http_handle_stats(const int64 sock, struct ot_workstruct *ws, cha
267 {"url", TASK_FULLSCRAPE_TPB_URLENCODED}, {"txt", TASK_FULLSCRAPE_TPB_ASCII}, 274 {"url", TASK_FULLSCRAPE_TPB_URLENCODED}, {"txt", TASK_FULLSCRAPE_TPB_ASCII},
268 {"txtp", TASK_FULLSCRAPE_TPB_ASCII_PLUS}, {NULL, -3}}; 275 {"txtp", TASK_FULLSCRAPE_TPB_ASCII_PLUS}, {NULL, -3}};
269 276
270 int mode = TASK_STATS_PEERS, scanon = 1, format = 0;
271
272#ifdef WANT_RESTRICT_STATS
273 struct http_data *cookie = io_getcookie(sock); 277 struct http_data *cookie = io_getcookie(sock);
278 int mode = TASK_STATS_PEERS, scanon = 1, format = 0;
274 279
275 if (!cookie || !accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_STAT)) 280 /* A socket without one is a dead socket, which is what http_sendiovecdata
281 already answers 500 to. */
282 if (!cookie)
283 HTTPERROR_500;
284
285#ifdef WANT_RESTRICT_STATS
286 if (!accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_STAT))
276 HTTPERROR_403_IP; 287 HTTPERROR_403_IP;
277#endif 288#endif
278 289
@@ -306,8 +317,7 @@ static ssize_t http_handle_stats(const int64 sock, struct ot_workstruct *ws, cha
306 } 317 }
307 318
308 if (mode == TASK_STATS_TPB) { 319 if (mode == TASK_STATS_TPB) {
309 struct http_data *cookie = io_getcookie(sock); 320 tai6464 t;
310 tai6464 t;
311#ifdef WANT_COMPRESSION_GZIP 321#ifdef WANT_COMPRESSION_GZIP
312 ws->request[ws->request_size - 1] = 0; 322 ws->request[ws->request_size - 1] = 0;
313#ifndef WANT_COMPRESSION_GZIP_ALWAYS 323#ifndef WANT_COMPRESSION_GZIP_ALWAYS
@@ -334,6 +344,9 @@ static ssize_t http_handle_stats(const int64 sock, struct ot_workstruct *ws, cha
334 /* default format for now */ 344 /* default format for now */
335 if ((mode & TASK_CLASS_MASK) == TASK_STATS) { 345 if ((mode & TASK_CLASS_MASK) == TASK_STATS) {
336 tai6464 t; 346 tai6464 t;
347
348 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK;
349
337 /* Complex stats also include expensive memory debugging tools */ 350 /* Complex stats also include expensive memory debugging tools */
338 taia_uint(&t, 0); 351 taia_uint(&t, 0);
339 io_timeout(sock, t); 352 io_timeout(sock, t);
@@ -355,7 +368,7 @@ typedef struct {
355#endif 368#endif
356 369
357#ifdef WANT_FULLSCRAPE 370#ifdef WANT_FULLSCRAPE
358static ssize_t http_handle_fullscrape(const int64 sock, struct ot_workstruct *ws) { 371static ssize_t http_handle_fullscrape(const int64_t sock, struct ot_workstruct *ws) {
359 struct http_data *cookie = io_getcookie(sock); 372 struct http_data *cookie = io_getcookie(sock);
360 int format = 0; 373 int format = 0;
361 tai6464 t; 374 tai6464 t;
@@ -425,7 +438,7 @@ static ssize_t http_handle_fullscrape(const int64 sock, struct ot_workstruct *ws
425} 438}
426#endif 439#endif
427 440
428static ssize_t http_handle_scrape(const int64 sock, struct ot_workstruct *ws, char *read_ptr) { 441static ssize_t http_handle_scrape(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) {
429 static const ot_keywords keywords_scrape[] = {{"info_hash", 1}, {NULL, -3}}; 442 static const ot_keywords keywords_scrape[] = {{"info_hash", 1}, {NULL, -3}};
430 443
431 ot_hash *multiscrape_buf = (ot_hash *)ws->request; 444 ot_hash *multiscrape_buf = (ot_hash *)ws->request;
@@ -504,7 +517,7 @@ static ot_keywords keywords_announce[] = {{"port", 1}, {"left", 2}, {"event",
504#endif 517#endif
505 {"peer_id", 9}, {NULL, -3}}; 518 {"peer_id", 9}, {NULL, -3}};
506static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}}; 519static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}};
507static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct *ws, char *read_ptr) { 520static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) {
508 size_t numwant; 521 size_t numwant;
509 int tmp, scanon; 522 int tmp, scanon;
510 unsigned short port = 0; 523 unsigned short port = 0;
@@ -682,7 +695,7 @@ static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct *
682 return ws->reply_size; 695 return ws->reply_size;
683} 696}
684 697
685ssize_t http_handle_request(const int64 sock, struct ot_workstruct *ws) { 698ssize_t http_handle_request(const int64_t sock, struct ot_workstruct *ws) {
686 ssize_t reply_off, len; 699 ssize_t reply_off, len;
687 char *read_ptr = ws->request, *write_ptr; 700 char *read_ptr = ws->request, *write_ptr;
688 701
diff --git a/ot_http.h b/ot_http.h
index e53accc..965820f 100644
--- a/ot_http.h
+++ b/ot_http.h
@@ -6,7 +6,15 @@
6#ifndef OT_HTTP_H__ 6#ifndef OT_HTTP_H__
7#define OT_HTTP_H__ 7#define OT_HTTP_H__
8 8
9#include <stdint.h>
10#include <sys/types.h>
11#include <sys/uio.h>
12
13/* Libowfat */
14#include "iob.h"
15
9#include "ot_buf.h" 16#include "ot_buf.h"
17#include "trackerlogic.h"
10 18
11typedef enum { 19typedef enum {
12 STRUCT_HTTP_FLAG_WAITINGFORTASK = 1, 20 STRUCT_HTTP_FLAG_WAITINGFORTASK = 1,
@@ -25,9 +33,9 @@ struct http_data {
25 STRUCT_HTTP_FLAG flag; 33 STRUCT_HTTP_FLAG flag;
26}; 34};
27 35
28ssize_t http_handle_request(const int64 s, struct ot_workstruct *ws); 36ssize_t http_handle_request(const int64_t s, struct ot_workstruct *ws);
29ssize_t http_sendiovecdata(const int64 s, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial); 37ssize_t http_sendiovecdata(const int64_t s, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial);
30ssize_t http_issue_error(const int64 s, struct ot_workstruct *ws, int code); 38ssize_t http_issue_error(const int64_t s, struct ot_workstruct *ws, int code);
31 39
32extern char *g_stats_path; 40extern char *g_stats_path;
33extern ssize_t g_stats_path_len; 41extern ssize_t g_stats_path_len;
diff --git a/ot_iovec.c b/ot_iovec.c
index bab19e3..cd5b9f9 100644
--- a/ot_iovec.c
+++ b/ot_iovec.c
@@ -20,6 +20,8 @@ void *iovec_increase(size_t *iovec_entries, struct iovec **iovector, size_t new_
20 if (!new_vec) 20 if (!new_vec)
21 return NULL; 21 return NULL;
22 22
23 *iovector = new_vec;
24
23 /* Only allocate after we have a place to store the pointer */ 25 /* Only allocate after we have a place to store the pointer */
24 new_data = malloc(new_alloc); 26 new_data = malloc(new_alloc);
25 if (!new_data) 27 if (!new_data)
@@ -28,7 +30,6 @@ void *iovec_increase(size_t *iovec_entries, struct iovec **iovector, size_t new_
28 new_vec[new_entries - 1].iov_base = new_data; 30 new_vec[new_entries - 1].iov_base = new_data;
29 new_vec[new_entries - 1].iov_len = new_alloc; 31 new_vec[new_entries - 1].iov_len = new_alloc;
30 32
31 *iovector = new_vec;
32 ++*iovec_entries; 33 ++*iovec_entries;
33 return new_data; 34 return new_data;
34} 35}
diff --git a/ot_livesync.c b/ot_livesync.c
index f6bb28b..5a63f3c 100644
--- a/ot_livesync.c
+++ b/ot_livesync.c
@@ -5,19 +5,16 @@
5 5
6/* System */ 6/* System */
7#include <arpa/inet.h> 7#include <arpa/inet.h>
8#include <fcntl.h>
9#include <pthread.h> 8#include <pthread.h>
10#include <stdlib.h> 9#include <stdlib.h>
11#include <string.h> 10#include <string.h>
12#include <sys/types.h> 11#include <sys/types.h>
13#include <unistd.h> 12#include <unistd.h>
14 13
15/* Libowfat */
16#include "socket.h"
17
18/* Opentracker */ 14/* Opentracker */
19#include "ot_accesslist.h" 15#include "ot_accesslist.h"
20#include "ot_ip.h" 16#include "ot_ip.h"
17#include "ot_socket.h"
21#include "ot_livesync.h" 18#include "ot_livesync.h"
22#include "ot_mutex.h" 19#include "ot_mutex.h"
23#include "ot_stats.h" 20#include "ot_stats.h"
@@ -25,7 +22,7 @@
25 22
26#ifdef WANT_SYNC_LIVE 23#ifdef WANT_SYNC_LIVE
27 24
28char groupip_1[4] = {224, 0, 23, 5}; 25ot_ip6 groupip_1 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 224, 0, 23, 5};
29 26
30#define LIVESYNC_INCOMING_BUFFSIZE (256 * 256) 27#define LIVESYNC_INCOMING_BUFFSIZE (256 * 256)
31 28
@@ -39,11 +36,11 @@ enum { OT_SYNC_PEER4, OT_SYNC_PEER6 };
39/* Forward declaration */ 36/* Forward declaration */
40static void *livesync_worker(void *args); 37static void *livesync_worker(void *args);
41 38
42/* For outgoing packets */
43static int64 g_socket_in = -1;
44
45/* For incoming packets */ 39/* For incoming packets */
46static int64 g_socket_out = -1; 40static int g_socket_in = -1;
41
42/* For outgoing packets */
43static int g_socket_out = -1;
47 44
48static pthread_mutex_t g_outbuf_mutex = PTHREAD_MUTEX_INITIALIZER; 45static pthread_mutex_t g_outbuf_mutex = PTHREAD_MUTEX_INITIALIZER;
49typedef struct { 46typedef struct {
@@ -89,34 +86,17 @@ void livesync_deinit(void) {
89} 86}
90 87
91void livesync_bind_mcast(ot_ip6 ip, uint16_t port) { 88void livesync_bind_mcast(ot_ip6 ip, uint16_t port) {
92 char tmpip[4] = {0, 0, 0, 0};
93 char *v4ip;
94
95 if (!ip_is_v4mapped(ip)) 89 if (!ip_is_v4mapped(ip))
96 exerr("v6 mcast support not yet available."); 90 exerr("v6 mcast support not yet available.");
97 v4ip = ip + 12;
98 91
99 if (g_socket_in != -1) 92 if (g_socket_in != -1)
100 exerr("Error: Livesync listen ip specified twice."); 93 exerr("Error: Livesync listen ip specified twice.");
101 94
102 if ((g_socket_in = socket_udp4()) < 0) 95 if ((g_socket_in = socket_mcast_join(groupip_1, ip, port)) == -1)
103 exerr("Error: Cant create live sync incoming socket.");
104
105 fcntl(g_socket_in, F_SETFL, fcntl(g_socket_in, F_GETFL, 0) & ~O_NONBLOCK);
106
107 if (socket_bind4_reuse(g_socket_in, tmpip, port) == -1)
108 exerr("Error: Cant bind live sync incoming socket."); 96 exerr("Error: Cant bind live sync incoming socket.");
109 97
110 if (socket_mcjoin4(g_socket_in, groupip_1, v4ip)) 98 if ((g_socket_out = socket_mcast_bind(ip, port, 0)) == -1)
111 exerr("Error: Cant make live sync incoming socket join mcast group.");
112
113 if ((g_socket_out = socket_udp4()) < 0)
114 exerr("Error: Cant create live sync outgoing socket.");
115 if (socket_bind4_reuse(g_socket_out, v4ip, port) == -1)
116 exerr("Error: Cant bind live sync outgoing socket."); 99 exerr("Error: Cant bind live sync outgoing socket.");
117
118 socket_mcttl4(g_socket_out, 1);
119 socket_mcloop4(g_socket_out, 0);
120} 100}
121 101
122/* Caller MUST hold g_outbuf_mutex. Returns with g_outbuf_mutex unlocked */ 102/* Caller MUST hold g_outbuf_mutex. Returns with g_outbuf_mutex unlocked */
@@ -132,7 +112,7 @@ static void livesync_issue_peersync(sync_buffer *buf) {
132 has modified the protected element */ 112 has modified the protected element */
133 pthread_mutex_unlock(&g_outbuf_mutex); 113 pthread_mutex_unlock(&g_outbuf_mutex);
134 114
135 socket_send4(g_socket_out, mycopy, fill, groupip_1, LIVESYNC_PORT); 115 socket_mcast_send(g_socket_out, mycopy, fill, groupip_1, LIVESYNC_PORT);
136} 116}
137 117
138static void livesync_handle_peersync(struct ot_workstruct *ws, size_t peer_size) { 118static void livesync_handle_peersync(struct ot_workstruct *ws, size_t peer_size) {
@@ -215,12 +195,10 @@ static void *livesync_worker(void *args) {
215 ws.inbuf = ws.request = malloc(LIVESYNC_INCOMING_BUFFSIZE); 195 ws.inbuf = ws.request = malloc(LIVESYNC_INCOMING_BUFFSIZE);
216 ws.outbuf = ws.reply = 0; 196 ws.outbuf = ws.reply = 0;
217 197
218 memcpy(in_ip, ip_v4mapped_prefix, sizeof(ip_v4mapped_prefix));
219
220 while (1) { 198 while (1) {
221 uint32_t packet_type; 199 uint32_t packet_type;
222 200
223 ws.request_size = socket_recv4(g_socket_in, (char *)ws.inbuf, LIVESYNC_INCOMING_BUFFSIZE, 12 + (char *)in_ip, &in_port); 201 ws.request_size = socket_recv(g_socket_in, ws.inbuf, LIVESYNC_INCOMING_BUFFSIZE, in_ip, &in_port);
224 202
225 /* Expect at least tracker id and packet type */ 203 /* Expect at least tracker id and packet type */
226 if (ws.request_size <= (ssize_t)(sizeof(g_tracker_id) + sizeof(uint32_t))) 204 if (ws.request_size <= (ssize_t)(sizeof(g_tracker_id) + sizeof(uint32_t)))
diff --git a/ot_livesync.h b/ot_livesync.h
index ed710f0..5b9fa0e 100644
--- a/ot_livesync.h
+++ b/ot_livesync.h
@@ -51,7 +51,7 @@ void livesync_init(void);
51void livesync_deinit(void); 51void livesync_deinit(void);
52 52
53/* Join multicast group for listening and create sending socket */ 53/* Join multicast group for listening and create sending socket */
54void livesync_bind_mcast(char *ip, uint16_t port); 54void livesync_bind_mcast(ot_ip6 ip, uint16_t port);
55 55
56/* Inform live sync about whats going on. */ 56/* Inform live sync about whats going on. */
57void livesync_tell(struct ot_workstruct *ws); 57void livesync_tell(struct ot_workstruct *ws);
@@ -62,7 +62,7 @@ void livesync_tell(struct ot_workstruct *ws);
62void livesync_ticker(void); 62void livesync_ticker(void);
63 63
64/* Handle an incoming live sync packet */ 64/* Handle an incoming live sync packet */
65void handle_livesync(const int64 sock); 65void handle_livesync(const int64_t sock);
66 66
67#else 67#else
68 68
diff --git a/ot_mutex.c b/ot_mutex.c
index 9aa476d..2a8cbf5 100644
--- a/ot_mutex.c
+++ b/ot_mutex.c
@@ -62,7 +62,7 @@ size_t mutex_get_torrent_count(void) { return g_torrent_count; }
62struct ot_task { 62struct ot_task {
63 ot_taskid taskid; 63 ot_taskid taskid;
64 ot_tasktype tasktype; 64 ot_tasktype tasktype;
65 int64 sock; 65 int64_t sock;
66 size_t iovec_entries; 66 size_t iovec_entries;
67 struct iovec *iovec; 67 struct iovec *iovec;
68 struct ot_task *next; 68 struct ot_task *next;
@@ -73,7 +73,7 @@ static struct ot_task *tasklist;
73static pthread_mutex_t tasklist_mutex; 73static pthread_mutex_t tasklist_mutex;
74static pthread_cond_t tasklist_being_filled; 74static pthread_cond_t tasklist_being_filled;
75 75
76int mutex_workqueue_pushtask(int64 sock, ot_tasktype tasktype) { 76int mutex_workqueue_pushtask(int64_t sock, ot_tasktype tasktype) {
77 struct ot_task **tmptask, *task; 77 struct ot_task **tmptask, *task;
78 78
79 task = malloc(sizeof(struct ot_task)); 79 task = malloc(sizeof(struct ot_task));
@@ -102,7 +102,7 @@ int mutex_workqueue_pushtask(int64 sock, ot_tasktype tasktype) {
102 return 0; 102 return 0;
103} 103}
104 104
105void mutex_workqueue_canceltask(int64 sock) { 105void mutex_workqueue_canceltask(int64_t sock) {
106 struct ot_task **task; 106 struct ot_task **task;
107 107
108 /* Want exclusive access to tasklist */ 108 /* Want exclusive access to tasklist */
@@ -114,9 +114,10 @@ void mutex_workqueue_canceltask(int64 sock) {
114 struct ot_task *ptask = *task; 114 struct ot_task *ptask = *task;
115 size_t i; 115 size_t i;
116 116
117 /* Free task's iovec */ 117 /* Free task's iovec, the buffers it points at and the vector itself */
118 for (i = 0; i < (*task)->iovec_entries; ++i) 118 for (i = 0; i < (*task)->iovec_entries; ++i)
119 free(iovec[i].iov_base); 119 free(iovec[i].iov_base);
120 free(iovec);
120 121
121 *task = (*task)->next; 122 *task = (*task)->next;
122 free(ptask); 123 free(ptask);
@@ -226,9 +227,9 @@ int mutex_workqueue_pushchunked(ot_taskid taskid, struct iovec *iovec) {
226 return task ? 0 : -1; 227 return task ? 0 : -1;
227} 228}
228 229
229int64 mutex_workqueue_popresult(size_t *iovec_entries, struct iovec **iovec, int *is_partial) { 230int64_t mutex_workqueue_popresult(size_t *iovec_entries, struct iovec **iovec, int *is_partial) {
230 struct ot_task **task; 231 struct ot_task **task;
231 int64 sock = -1; 232 int64_t sock = -1;
232 233
233 *is_partial = 0; 234 *is_partial = 0;
234 235
diff --git a/ot_mutex.h b/ot_mutex.h
index ab1cf59..b5af96e 100644
--- a/ot_mutex.h
+++ b/ot_mutex.h
@@ -69,12 +69,12 @@ typedef enum {
69 69
70typedef unsigned long ot_taskid; 70typedef unsigned long ot_taskid;
71 71
72int mutex_workqueue_pushtask(int64 sock, ot_tasktype tasktype); 72int mutex_workqueue_pushtask(int64_t sock, ot_tasktype tasktype);
73void mutex_workqueue_canceltask(int64 sock); 73void mutex_workqueue_canceltask(int64_t sock);
74void mutex_workqueue_pushsuccess(ot_taskid taskid); 74void mutex_workqueue_pushsuccess(ot_taskid taskid);
75ot_taskid mutex_workqueue_poptask(ot_tasktype *tasktype); 75ot_taskid mutex_workqueue_poptask(ot_tasktype *tasktype);
76int mutex_workqueue_pushresult(ot_taskid taskid, size_t iovec_entries, struct iovec *iovector); 76int mutex_workqueue_pushresult(ot_taskid taskid, size_t iovec_entries, struct iovec *iovector);
77int mutex_workqueue_pushchunked(ot_taskid taskid, struct iovec *iovec); 77int mutex_workqueue_pushchunked(ot_taskid taskid, struct iovec *iovec);
78int64 mutex_workqueue_popresult(size_t *iovec_entries, struct iovec **iovector, int *is_partial); 78int64_t mutex_workqueue_popresult(size_t *iovec_entries, struct iovec **iovector, int *is_partial);
79 79
80#endif 80#endif
diff --git a/ot_socket.c b/ot_socket.c
new file mode 100644
index 0000000..e26ad52
--- /dev/null
+++ b/ot_socket.c
@@ -0,0 +1,256 @@
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 <fcntl.h>
9#include <netinet/in.h>
10#include <string.h>
11#include <sys/socket.h>
12#include <unistd.h>
13
14/* Opentracker */
15#include "ot_ip.h"
16#include "ot_socket.h"
17
18int g_socket_reuseport;
19
20static void sockaddr_from_ip(struct sockaddr_in6 *sa, const ot_ip6 ip, uint16_t port) {
21 memset(sa, 0, sizeof(*sa));
22 sa->sin6_family = AF_INET6;
23 sa->sin6_port = htons(port);
24 memcpy(&sa->sin6_addr, ip, sizeof(ot_ip6));
25}
26
27/* Linux reports an IPv4 peer on a dual stack socket as a v4 mapped
28 sockaddr_in6, the BSDs and macOS hand back a plain sockaddr_in instead, so
29 both forms have to be read. */
30static void ip_from_sockaddr(ot_ip6 ip, uint16_t *port, const struct sockaddr_storage *ss) {
31 if (ss->ss_family == AF_INET) {
32 const struct sockaddr_in *sa = (const struct sockaddr_in *)ss;
33
34 memcpy(ip, ip_v4mapped_prefix, sizeof(ip_v4mapped_prefix));
35 memcpy(ip + sizeof(ip_v4mapped_prefix), &sa->sin_addr, 4);
36 *port = ntohs(sa->sin_port);
37 } else {
38 const struct sockaddr_in6 *sa = (const struct sockaddr_in6 *)ss;
39
40 memcpy(ip, &sa->sin6_addr, sizeof(ot_ip6));
41 *port = ntohs(sa->sin6_port);
42 }
43}
44
45static int set_nonblocking(int s) {
46 int flags = fcntl(s, F_GETFL, 0);
47
48 if (flags == -1)
49 return -1;
50 return fcntl(s, F_SETFL, flags | O_NONBLOCK);
51}
52
53/* Close without letting the failure we are about to report be overwritten */
54static int close_and_fail(int s) {
55 int saved = errno;
56 close(s);
57 errno = saved;
58 return -1;
59}
60
61static int socket_v6(int type) {
62 int s = socket(PF_INET6, type, 0);
63 int zero = 0;
64
65 if (s == -1)
66 return -1;
67
68 /* A tracker that answered only one family would be half a tracker */
69 if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) == -1)
70 return close_and_fail(s);
71
72 if (set_nonblocking(s) == -1)
73 return close_and_fail(s);
74
75 return s;
76}
77
78static int socket_bind_v6(int s, const ot_ip6 ip, uint16_t port) {
79 struct sockaddr_in6 sa;
80 int one = 1;
81
82 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
83#ifdef SO_REUSEPORT
84 if (g_socket_reuseport)
85 setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
86#endif
87
88 sockaddr_from_ip(&sa, ip, port);
89 return bind(s, (struct sockaddr *)&sa, sizeof(sa));
90}
91
92int socket_tcp_listen(const ot_ip6 ip, uint16_t port) {
93 int s = socket_v6(SOCK_STREAM);
94
95 if (s == -1)
96 return -1;
97 if (socket_bind_v6(s, ip, port) == -1 || listen(s, SOMAXCONN) == -1)
98 return close_and_fail(s);
99
100 return s;
101}
102
103int socket_udp_bind(const ot_ip6 ip, uint16_t port) {
104 int s = socket_v6(SOCK_DGRAM);
105
106 if (s == -1)
107 return -1;
108 if (socket_bind_v6(s, ip, port) == -1)
109 return close_and_fail(s);
110
111 return s;
112}
113
114int socket_tcp_connect(const ot_ip6 srcip, uint16_t srcport, const ot_ip6 dstip, uint16_t dstport) {
115 struct sockaddr_in6 sa;
116 int s = socket_v6(SOCK_STREAM);
117
118 if (s == -1)
119 return -1;
120 if (socket_bind_v6(s, srcip, srcport) == -1)
121 return close_and_fail(s);
122
123 sockaddr_from_ip(&sa, dstip, dstport);
124 if (connect(s, (struct sockaddr *)&sa, sizeof(sa)) == -1 && errno != EINPROGRESS && errno != EWOULDBLOCK)
125 return close_and_fail(s);
126
127 return s;
128}
129
130int socket_is_connected(int s) {
131 int error = 0;
132 socklen_t len = sizeof(error);
133
134 if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) == -1)
135 return 0;
136 if (error) {
137 errno = error;
138 return 0;
139 }
140 return 1;
141}
142
143int socket_accept(int s, ot_ip6 ip, uint16_t *port) {
144 struct sockaddr_storage ss;
145 socklen_t len = sizeof(ss);
146 int fd;
147
148 memset(&ss, 0, sizeof(ss));
149 if ((fd = accept(s, (struct sockaddr *)&ss, &len)) == -1)
150 return -1;
151
152 /* Linux hands over a blocking socket whatever the listening socket was, the
153 BSDs and macOS inherit the listener's flags, so say which one we want. */
154 if (set_nonblocking(fd) == -1)
155 return close_and_fail(fd);
156
157 ip_from_sockaddr(ip, port, &ss);
158 return fd;
159}
160
161ssize_t socket_recv(int s, void *buf, size_t len, ot_ip6 ip, uint16_t *port) {
162 struct sockaddr_storage ss;
163 socklen_t sslen = sizeof(ss);
164 ssize_t received;
165
166 memset(&ss, 0, sizeof(ss));
167 received = recvfrom(s, buf, len, 0, (struct sockaddr *)&ss, &sslen);
168 if (received == -1)
169 return -1;
170
171 ip_from_sockaddr(ip, port, &ss);
172 return received;
173}
174
175ssize_t socket_send(int s, const void *buf, size_t len, const ot_ip6 ip, uint16_t port) {
176 struct sockaddr_in6 sa;
177
178 sockaddr_from_ip(&sa, ip, port);
179 return sendto(s, buf, len, 0, (struct sockaddr *)&sa, sizeof(sa));
180}
181
182#ifdef WANT_SYNC_LIVE
183static void sockaddr4_from_ip(struct sockaddr_in *sa, const ot_ip6 ip, uint16_t port) {
184 memset(sa, 0, sizeof(*sa));
185 sa->sin_family = AF_INET;
186 sa->sin_port = htons(port);
187 memcpy(&sa->sin_addr, ip + sizeof(ip_v4mapped_prefix), 4);
188}
189
190static int socket_v4_bind(const ot_ip6 ip, uint16_t port) {
191 struct sockaddr_in sa;
192 int s, one = 1;
193
194 if ((s = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
195 return -1;
196
197 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
198#ifdef SO_REUSEPORT
199 if (g_socket_reuseport)
200 setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
201#endif
202
203 sockaddr4_from_ip(&sa, ip, port);
204 if (bind(s, (struct sockaddr *)&sa, sizeof(sa)) == -1)
205 return close_and_fail(s);
206
207 return s;
208}
209
210int socket_mcast_join(const ot_ip6 group, const ot_ip6 interface, uint16_t port) {
211 struct ip_mreq mreq;
212 ot_ip6 any;
213 int s;
214
215 /* The group is what is listened to, so the socket takes the wildcard and
216 the join names the interface to receive it on */
217 memcpy(any, ip_v4mapped_prefix, sizeof(ip_v4mapped_prefix));
218 memset(any + sizeof(ip_v4mapped_prefix), 0, 4);
219
220 if ((s = socket_v4_bind(any, port)) == -1)
221 return -1;
222
223 memset(&mreq, 0, sizeof(mreq));
224 memcpy(&mreq.imr_multiaddr, group + sizeof(ip_v4mapped_prefix), 4);
225 memcpy(&mreq.imr_interface, interface + sizeof(ip_v4mapped_prefix), 4);
226 if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1)
227 return close_and_fail(s);
228
229 return s;
230}
231
232int socket_mcast_bind(const ot_ip6 interface, uint16_t port, int loopback) {
233 /* The BSDs and macOS want a byte for the IPv4 multicast options, Linux
234 accepts either that or an int. */
235 unsigned char ttl = 1, loop = (unsigned char)(loopback != 0);
236 int s = socket_v4_bind(interface, port);
237
238 if (s == -1)
239 return -1;
240 if (set_nonblocking(s) == -1)
241 return close_and_fail(s);
242
243 if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) == -1 ||
244 setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) == -1)
245 return close_and_fail(s);
246
247 return s;
248}
249
250ssize_t socket_mcast_send(int s, const void *buf, size_t len, const ot_ip6 group, uint16_t port) {
251 struct sockaddr_in sa;
252
253 sockaddr4_from_ip(&sa, group, port);
254 return sendto(s, buf, len, 0, (struct sockaddr *)&sa, sizeof(sa));
255}
256#endif
diff --git a/ot_socket.h b/ot_socket.h
new file mode 100644
index 0000000..20fd839
--- /dev/null
+++ b/ot_socket.h
@@ -0,0 +1,45 @@
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_SOCKET_H__
7#define OT_SOCKET_H__
8
9#include <stdint.h>
10#include <sys/types.h>
11
12#include "trackerlogic.h"
13
14/* Every socket opentracker opens is an AF_INET6 socket with IPV6_V6ONLY
15 cleared, so one descriptor serves both families and an IPv4 peer arrives
16 as the v4 mapped address */
17
18/* Ask the kernel to let several processes bind the same address */
19extern int g_socket_reuseport;
20
21/* Create, bind and, for TCP, listen. The returned socket is non blocking
22 Returns -1 on failure with errno set */
23int socket_tcp_listen(const ot_ip6 ip, uint16_t port);
24int socket_udp_bind(const ot_ip6 ip, uint16_t port);
25
26int socket_tcp_connect(const ot_ip6 srcip, uint16_t srcport, const ot_ip6 dstip, uint16_t dstport);
27int socket_is_connected(int s);
28int socket_accept(int s, ot_ip6 ip, uint16_t *port);
29
30ssize_t socket_recv(int s, void *buf, size_t len, ot_ip6 ip, uint16_t *port);
31ssize_t socket_send(int s, const void *buf, size_t len, const ot_ip6 ip, uint16_t port);
32
33#ifdef WANT_SYNC_LIVE
34/*
35 socket_mcast_join takes the wildcard address and names the interface in the
36 join, and hands back a blocking socket, since the thread that reads it does
37 nothing else. socket_mcast_bind hands back a non blocking one, because the
38 thread that sends from it is also serving clients. Incoming datagrams are
39 read with socket_recv */
40int socket_mcast_join(const ot_ip6 group, const ot_ip6 interface, uint16_t port);
41int socket_mcast_bind(const ot_ip6 interface, uint16_t port, int loopback);
42ssize_t socket_mcast_send(int s, const void *buf, size_t len, const ot_ip6 group, uint16_t port);
43#endif
44
45#endif
diff --git a/ot_stats.c b/ot_stats.c
index 62c59c9..f938e91 100644
--- a/ot_stats.c
+++ b/ot_stats.c
@@ -206,10 +206,6 @@ static size_t stats_return_busy_networks(char *reply, stats_network_node *tree,
206 r += sprintf(r, "%08zd: ", scores[i - 1]); 206 r += sprintf(r, "%08zd: ", scores[i - 1]);
207 // #ifdef WANT_V6 207 // #ifdef WANT_V6
208 r += ip_format(r, networks[i - 1]); 208 r += ip_format(r, networks[i - 1]);
209#if 0
210 // XXX
211 r += fmt_ip4( r, networks[i - 1]);
212#endif
213 *r++ = '\n'; 209 *r++ = '\n';
214 } 210 }
215 } 211 }
@@ -663,10 +659,6 @@ void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_
663 } 659 }
664 660
665 ip_readable[ip_format(ip_readable, (char *)&ws->peer)] = 0; 661 ip_readable[ip_format(ip_readable, (char *)&ws->peer)] = 0;
666#if 0
667 /* XXX */
668 ip_readable[ fmt_ip4( ip_readable, (char*)&ws->peer ) ] = 0;
669#endif
670 syslog(LOG_INFO, "time=%s event=completed info_hash=%s peer_id=%s ip=%s", timestring, hash_hex, peerid_hex, ip_readable); 662 syslog(LOG_INFO, "time=%s event=completed info_hash=%s peer_id=%s ip=%s", timestring, hash_hex, peerid_hex, ip_readable);
671 } 663 }
672#endif 664#endif
@@ -750,7 +742,7 @@ static void *stats_worker(void *args) {
750 return NULL; 742 return NULL;
751} 743}
752 744
753void stats_deliver(int64 sock, int tasktype) { mutex_workqueue_pushtask(sock, tasktype); } 745void stats_deliver(int64_t sock, int tasktype) { mutex_workqueue_pushtask(sock, tasktype); }
754 746
755static pthread_t thread_id; 747static pthread_t thread_id;
756void stats_init(void) { 748void stats_init(void) {
diff --git a/ot_stats.h b/ot_stats.h
index 8ed2b1e..203831c 100644
--- a/ot_stats.h
+++ b/ot_stats.h
@@ -42,7 +42,7 @@ enum {
42}; 42};
43 43
44void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_data); 44void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_data);
45void stats_deliver(int64 sock, int tasktype); 45void stats_deliver(int64_t sock, int tasktype);
46void stats_cleanup(void); 46void stats_cleanup(void);
47size_t return_stats_for_tracker(char *reply, int mode, int format); 47size_t return_stats_for_tracker(char *reply, int mode, int format);
48size_t stats_return_tracker_version(char *reply); 48size_t stats_return_tracker_version(char *reply);
diff --git a/ot_sync.c b/ot_sync.c
index fa6bb8b..dc71ddd 100644
--- a/ot_sync.c
+++ b/ot_sync.c
@@ -163,7 +163,7 @@ void sync_deinit( ) {
163 pthread_cancel( thread_id ); 163 pthread_cancel( thread_id );
164} 164}
165 165
166void sync_deliver( int64 socket ) { 166void sync_deliver( int64_t socket ) {
167 mutex_workqueue_pushtask( socket, TASK_SYNC_OUT ); 167 mutex_workqueue_pushtask( socket, TASK_SYNC_OUT );
168} 168}
169 169
diff --git a/ot_sync.h b/ot_sync.h
index 0fe0739..b6964fa 100644
--- a/ot_sync.h
+++ b/ot_sync.h
@@ -6,12 +6,14 @@
6#ifndef OT_SYNC_H__ 6#ifndef OT_SYNC_H__
7#define OT_SYNC_H__ 7#define OT_SYNC_H__
8 8
9#include <stdint.h>
10
9#ifdef WANT_SYNC_BATCH 11#ifdef WANT_SYNC_BATCH
10enum { SYNC_IN, SYNC_OUT }; 12enum { SYNC_IN, SYNC_OUT };
11 13
12void sync_init(); 14void sync_init();
13void sync_deinit(); 15void sync_deinit();
14void sync_deliver(int64 socket); 16void sync_deliver(int64_t socket);
15 17
16int add_changeset_to_tracker(uint8_t *data, size_t len); 18int add_changeset_to_tracker(uint8_t *data, size_t len);
17#else 19#else
diff --git a/ot_udp.c b/ot_udp.c
index d17f3ff..dc8267c 100644
--- a/ot_udp.c
+++ b/ot_udp.c
@@ -12,19 +12,16 @@
12 12
13/* Libowfat */ 13/* Libowfat */
14#include "io.h" 14#include "io.h"
15#include "socket.h"
16 15
17/* Opentracker */ 16/* Opentracker */
18#include "ot_ip.h" 17#include "ot_ip.h"
19#include "ot_random.h" 18#include "ot_random.h"
20#include "ot_rijndael.h" 19#include "ot_rijndael.h"
20#include "ot_socket.h"
21#include "ot_stats.h" 21#include "ot_stats.h"
22#include "ot_udp.h" 22#include "ot_udp.h"
23#include "trackerlogic.h" 23#include "trackerlogic.h"
24 24
25#if 0
26static const uint8_t g_static_connid[8] = { 0x23, 0x42, 0x05, 0x17, 0xde, 0x41, 0x50, 0xff };
27#endif
28static uint32_t g_rijndael_round_key[44] = {0}; 25static uint32_t g_rijndael_round_key[44] = {0};
29static volatile uint32_t g_key_of_the_hour[2] = {0}; 26static volatile uint32_t g_key_of_the_hour[2] = {0};
30static volatile ot_time g_hour_of_the_key; 27static volatile ot_time g_hour_of_the_key;
@@ -71,19 +68,20 @@ static void udp_make_connectionid(uint32_t connid[2], const ot_ip6 remoteip, int
71} 68}
72 69
73/* UDP implementation according to http://xbtt.sourceforge.net/udp_tracker_protocol.html */ 70/* UDP implementation according to http://xbtt.sourceforge.net/udp_tracker_protocol.html */
74int handle_udp6(int64 serversocket, struct ot_workstruct *ws) { 71int handle_udp6(int64_t serversocket, struct ot_workstruct *ws) {
75 ot_ip6 remoteip; 72 ot_ip6 remoteip;
76 uint32_t *inpacket = (uint32_t *)ws->inbuf; 73 uint32_t *inpacket = (uint32_t *)ws->inbuf;
77 uint32_t *outpacket = (uint32_t *)ws->outbuf; 74 uint32_t *outpacket = (uint32_t *)ws->outbuf;
78 uint32_t left, event, scopeid; 75 uint32_t left, event;
79 uint32_t connid[2]; 76 uint32_t connid[2];
80 uint32_t action; 77 uint32_t action;
81 uint16_t port, remoteport; 78 uint16_t port, remoteport;
82 size_t byte_count, scrape_count; 79 size_t byte_count, scrape_count;
83 80
84 byte_count = socket_recv6(serversocket, ws->inbuf, G_INBUF_SIZE, remoteip, &remoteport, &scopeid); 81 ssize_t result = socket_recv(serversocket, ws->inbuf, G_INBUF_SIZE, remoteip, &remoteport);
85 if (!byte_count) 82 if (result <= 0)
86 return 0; 83 return 0;
84 byte_count = (size_t)result;
87 85
88 stats_issue_event(EVENT_ACCEPT, FLAG_UDP, (uintptr_t)remoteip); 86 stats_issue_event(EVENT_ACCEPT, FLAG_UDP, (uintptr_t)remoteip);
89 stats_issue_event(EVENT_READ, FLAG_UDP, byte_count); 87 stats_issue_event(EVENT_READ, FLAG_UDP, byte_count);
@@ -117,7 +115,7 @@ int handle_udp6(int64 serversocket, struct ot_workstruct *ws) {
117 outpacket[0] = htonl(3); 115 outpacket[0] = htonl(3);
118 outpacket[1] = inpacket[3]; 116 outpacket[1] = inpacket[3];
119 memcpy(&outpacket[2], "Connection ID missmatch.", s); 117 memcpy(&outpacket[2], "Connection ID missmatch.", s);
120 socket_send6(serversocket, ws->outbuf, 8 + s, remoteip, remoteport, 0); 118 socket_send(serversocket, ws->outbuf, 8 + s, remoteip, remoteport);
121 stats_issue_event(EVENT_CONNID_MISSMATCH, FLAG_UDP, 8 + s); 119 stats_issue_event(EVENT_CONNID_MISSMATCH, FLAG_UDP, 8 + s);
122 return 1; 120 return 1;
123 } 121 }
@@ -134,7 +132,7 @@ int handle_udp6(int64 serversocket, struct ot_workstruct *ws) {
134 outpacket[2] = connid[0]; 132 outpacket[2] = connid[0];
135 outpacket[3] = connid[1]; 133 outpacket[3] = connid[1];
136 134
137 socket_send6(serversocket, ws->outbuf, 16, remoteip, remoteport, 0); 135 socket_send(serversocket, ws->outbuf, 16, remoteip, remoteport);
138 stats_issue_event(EVENT_CONNECT, FLAG_UDP, 16); 136 stats_issue_event(EVENT_CONNECT, FLAG_UDP, 16);
139 break; 137 break;
140 case 1: /* This is an announce action */ 138 case 1: /* This is an announce action */
@@ -184,7 +182,7 @@ int handle_udp6(int64 serversocket, struct ot_workstruct *ws) {
184 ws->reply_size = 8 + add_peer_to_torrent_and_return_peers(FLAG_UDP, ws, numwant); 182 ws->reply_size = 8 + add_peer_to_torrent_and_return_peers(FLAG_UDP, ws, numwant);
185 } 183 }
186 184
187 socket_send6(serversocket, ws->outbuf, ws->reply_size, remoteip, remoteport, 0); 185 socket_send(serversocket, ws->outbuf, ws->reply_size, remoteip, remoteport);
188 stats_issue_event(EVENT_ANNOUNCE, FLAG_UDP, ws->reply_size); 186 stats_issue_event(EVENT_ANNOUNCE, FLAG_UDP, ws->reply_size);
189 break; 187 break;
190 188
@@ -195,7 +193,7 @@ int handle_udp6(int64 serversocket, struct ot_workstruct *ws) {
195 for (scrape_count = 0; (scrape_count * 20 < byte_count - 16) && (scrape_count <= 74); scrape_count++) 193 for (scrape_count = 0; (scrape_count * 20 < byte_count - 16) && (scrape_count <= 74); scrape_count++)
196 return_udp_scrape_for_torrent(*(ot_hash *)(((char *)inpacket) + 16 + 20 * scrape_count), ((char *)outpacket) + 8 + 12 * scrape_count); 194 return_udp_scrape_for_torrent(*(ot_hash *)(((char *)inpacket) + 16 + 20 * scrape_count), ((char *)outpacket) + 8 + 12 * scrape_count);
197 195
198 socket_send6(serversocket, ws->outbuf, 8 + 12 * scrape_count, remoteip, remoteport, 0); 196 socket_send(serversocket, ws->outbuf, 8 + 12 * scrape_count, remoteip, remoteport);
199 stats_issue_event(EVENT_SCRAPE, FLAG_UDP, scrape_count); 197 stats_issue_event(EVENT_SCRAPE, FLAG_UDP, scrape_count);
200 break; 198 break;
201 } 199 }
@@ -203,7 +201,7 @@ int handle_udp6(int64 serversocket, struct ot_workstruct *ws) {
203} 201}
204 202
205static void *udp_worker(void *args) { 203static void *udp_worker(void *args) {
206 int64 sock = (int64)args; 204 int64_t sock = (int64_t)args;
207 struct ot_workstruct ws; 205 struct ot_workstruct ws;
208 memset(&ws, 0, sizeof(ws)); 206 memset(&ws, 0, sizeof(ws));
209 207
@@ -229,7 +227,7 @@ static void *udp_worker(void *args) {
229 return NULL; 227 return NULL;
230} 228}
231 229
232void udp_init(int64 sock, unsigned int worker_count) { 230void udp_init(int64_t sock, unsigned int worker_count) {
233 pthread_t thread_id; 231 pthread_t thread_id;
234 if (!g_rijndael_round_key[0]) 232 if (!g_rijndael_round_key[0])
235 udp_generate_rijndael_round_key(); 233 udp_generate_rijndael_round_key();
diff --git a/ot_udp.h b/ot_udp.h
index db48696..87d99a9 100644
--- a/ot_udp.h
+++ b/ot_udp.h
@@ -6,8 +6,12 @@
6#ifndef OT_UDP_H__ 6#ifndef OT_UDP_H__
7#define OT_UDP_H__ 7#define OT_UDP_H__
8 8
9void udp_init(int64 sock, unsigned int worker_count); 9#include <stdint.h>
10
11#include "trackerlogic.h"
12
13void udp_init(int64_t sock, unsigned int worker_count);
10void udp_kickoff(void); 14void udp_kickoff(void);
11int handle_udp6(int64 serversocket, struct ot_workstruct *ws); 15int handle_udp6(int64_t serversocket, struct ot_workstruct *ws);
12 16
13#endif 17#endif
diff --git a/ot_vector.c b/ot_vector.c
index e3c3d3f..c4cd171 100644
--- a/ot_vector.c
+++ b/ot_vector.c
@@ -156,8 +156,6 @@ void vector_remove_torrent(ot_vector *vector, ot_torrent *match) {
156 if (!vector->size) 156 if (!vector->size)
157 return; 157 return;
158 158
159 /* If this is being called after a unsuccessful malloc() for peer_list
160 in add_peer_to_torrent, match->peer_list actually might be NULL */
161 free_peerlist(match->peer_list6); 159 free_peerlist(match->peer_list6);
162 free_peerlist(match->peer_list4); 160 free_peerlist(match->peer_list4);
163 161
diff --git a/ot_vector.h b/ot_vector.h
index 2618363..fbc75f4 100644
--- a/ot_vector.h
+++ b/ot_vector.h
@@ -6,6 +6,8 @@
6#ifndef OT_VECTOR_H__ 6#ifndef OT_VECTOR_H__
7#define OT_VECTOR_H__ 7#define OT_VECTOR_H__
8 8
9#include <stddef.h>
10
9/* These defines control vectors behaviour */ 11/* These defines control vectors behaviour */
10#define OT_VECTOR_MIN_MEMBERS 2 12#define OT_VECTOR_MIN_MEMBERS 2
11#define OT_VECTOR_GROW_RATIO 2 13#define OT_VECTOR_GROW_RATIO 2
@@ -21,6 +23,9 @@ typedef struct {
21 size_t space; 23 size_t space;
22} ot_vector; 24} ot_vector;
23 25
26/* trackerlogic.h needs ot_vector, break circular dependency here */
27#include "trackerlogic.h"
28
24void *binary_search(const void *const key, const void *base, const size_t member_count, const size_t member_size, size_t compare_size, int *exactmatch); 29void *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);
25void *vector_find_or_insert(ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch); 30void *vector_find_or_insert(ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch);
26ot_peer *vector_find_or_insert_peer(ot_vector *vector, ot_peer const *peer, size_t peer_size, int *exactmatch); 31ot_peer *vector_find_or_insert_peer(ot_vector *vector, ot_peer const *peer, size_t peer_size, int *exactmatch);
diff --git a/proxy.c b/proxy.c
index 784e2a1..16b8286 100644
--- a/proxy.c
+++ b/proxy.c
@@ -7,38 +7,33 @@
7#include <arpa/inet.h> 7#include <arpa/inet.h>
8#include <ctype.h> 8#include <ctype.h>
9#include <errno.h> 9#include <errno.h>
10#include <fcntl.h>
11#include <pthread.h> 10#include <pthread.h>
12#include <stdint.h> 11#include <stdint.h>
13#include <stdio.h> 12#include <stdio.h>
14#include <stdlib.h> 13#include <stdlib.h>
15#include <string.h> 14#include <string.h>
16#include <sys/socket.h>
17#include <unistd.h> 15#include <unistd.h>
18 16
19/* Libowfat */ 17/* Libowfat */
20#include "io.h" 18#include "io.h"
21#include "iob.h" 19#include "iob.h"
22#include "socket.h"
23 20
24/* Opentracker */ 21/* Opentracker */
25#include "ot_ip.h" 22#include "ot_ip.h"
26#include "ot_mutex.h" 23#include "ot_mutex.h"
27#include "ot_random.h" 24#include "ot_random.h"
28#include "ot_scan.h" 25#include "ot_scan.h"
26#include "ot_socket.h"
29#include "ot_stats.h" 27#include "ot_stats.h"
30#include "ot_vector.h" 28#include "ot_vector.h"
31#include "trackerlogic.h" 29#include "trackerlogic.h"
32 30
33#ifndef WANT_SYNC_LIVE
34#define WANT_SYNC_LIVE
35#endif
36#include "ot_livesync.h" 31#include "ot_livesync.h"
37 32
38ot_ip6 g_serverip; 33ot_ip6 g_serverip;
39uint16_t g_serverport = 9009; 34uint16_t g_serverport = 9009;
40uint32_t g_tracker_id; 35uint32_t g_tracker_id;
41char groupip_1[4] = {224, 0, 23, 5}; 36ot_ip6 groupip_1 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 224, 0, 23, 5};
42int g_self_pipe[2]; 37int g_self_pipe[2];
43 38
44/* If you have more than 10 peers, don't use this proxy 39/* If you have more than 10 peers, don't use this proxy
@@ -63,11 +58,11 @@ enum { OT_SYNC_PEER4, OT_SYNC_PEER6 };
63enum { FLAG_SERVERSOCKET = 1 }; 58enum { FLAG_SERVERSOCKET = 1 };
64 59
65/* For incoming packets */ 60/* For incoming packets */
66static int64 g_socket_in = -1; 61static int g_socket_in = -1;
67static uint8_t g_inbuffer[LIVESYNC_INCOMING_BUFFSIZE]; 62static uint8_t g_inbuffer[LIVESYNC_INCOMING_BUFFSIZE];
68 63
69/* For outgoing packets */ 64/* For outgoing packets */
70static int64 g_socket_out = -1; 65static int g_socket_out = -1;
71static uint8_t g_peerbuffer_start[LIVESYNC_OUTGOING_BUFFSIZE_PEERS]; 66static uint8_t g_peerbuffer_start[LIVESYNC_OUTGOING_BUFFSIZE_PEERS];
72static uint8_t *g_peerbuffer_pos; 67static uint8_t *g_peerbuffer_pos;
73static uint8_t *g_peerbuffer_highwater = g_peerbuffer_start + LIVESYNC_OUTGOING_BUFFSIZE_PEERS - LIVESYNC_OUTGOING_WATERMARK_PEERS; 68static uint8_t *g_peerbuffer_highwater = g_peerbuffer_start + LIVESYNC_OUTGOING_BUFFSIZE_PEERS - LIVESYNC_OUTGOING_WATERMARK_PEERS;
@@ -89,34 +84,17 @@ void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_
89} 84}
90 85
91void livesync_bind_mcast(ot_ip6 ip, uint16_t port) { 86void livesync_bind_mcast(ot_ip6 ip, uint16_t port) {
92 char tmpip[4] = {0, 0, 0, 0};
93 char *v4ip;
94
95 if (!ip_is_v4mapped(ip)) 87 if (!ip_is_v4mapped(ip))
96 exerr("v6 mcast support not yet available."); 88 exerr("v6 mcast support not yet available.");
97 v4ip = ip + 12;
98 89
99 if (g_socket_in != -1) 90 if (g_socket_in != -1)
100 exerr("Error: Livesync listen ip specified twice."); 91 exerr("Error: Livesync listen ip specified twice.");
101 92
102 if ((g_socket_in = socket_udp4()) < 0) 93 if ((g_socket_in = socket_mcast_join(groupip_1, ip, port)) == -1)
103 exerr("Error: Cant create live sync incoming socket.");
104
105 fcntl(g_socket_in, F_SETFL, fcntl(g_socket_in, F_GETFL, 0) & ~O_NONBLOCK);
106
107 if (socket_bind4_reuse(g_socket_in, tmpip, port) == -1)
108 exerr("Error: Cant bind live sync incoming socket."); 94 exerr("Error: Cant bind live sync incoming socket.");
109 95
110 if (socket_mcjoin4(g_socket_in, groupip_1, v4ip)) 96 if ((g_socket_out = socket_mcast_bind(ip, port, 1)) == -1)
111 exerr("Error: Cant make live sync incoming socket join mcast group.");
112
113 if ((g_socket_out = socket_udp4()) < 0)
114 exerr("Error: Cant create live sync outgoing socket.");
115 if (socket_bind4_reuse(g_socket_out, v4ip, port) == -1)
116 exerr("Error: Cant bind live sync outgoing socket."); 97 exerr("Error: Cant bind live sync outgoing socket.");
117
118 socket_mcttl4(g_socket_out, 1);
119 socket_mcloop4(g_socket_out, 1);
120} 98}
121 99
122size_t add_peer_to_torrent_proxy(ot_hash hash, ot_peer *peer, size_t peer_size) { 100size_t add_peer_to_torrent_proxy(ot_hash hash, ot_peer *peer, size_t peer_size) {
@@ -249,7 +227,7 @@ typedef struct {
249 uint8_t indata[8192 * 16]; /* Any data not processed yet */ 227 uint8_t indata[8192 * 16]; /* Any data not processed yet */
250 size_t indata_length; /* Length of unprocessed data */ 228 size_t indata_length; /* Length of unprocessed data */
251 uint32_t tracker_id; /* How the other end greeted */ 229 uint32_t tracker_id; /* How the other end greeted */
252 int64 fd; /* A file handle, if connected, <= 0 is disconnected (0 initially, -1 else) */ 230 int64_t fd; /* A file handle, if connected, <= 0 is disconnected (0 initially, -1 else) */
253 io_batch outdata; /* The iobatch containing our sync data */ 231 io_batch outdata; /* The iobatch containing our sync data */
254 232
255 size_t packet_tcount; /* Number of unprocessed torrents in packet we currently receive */ 233 size_t packet_tcount; /* Number of unprocessed torrents in packet we currently receive */
@@ -283,19 +261,11 @@ static void handle_reconnects(void) {
283 size_t i; 261 size_t i;
284 for (i = 0; i < g_connection_count; ++i) 262 for (i = 0; i < g_connection_count; ++i)
285 if (PROXYPEER_NEEDSCONNECT(g_connections[i].state)) { 263 if (PROXYPEER_NEEDSCONNECT(g_connections[i].state)) {
286 int64 newfd = socket_tcp6(); 264 int64_t newfd = socket_tcp_connect(g_serverip, g_serverport, g_connections[i].ip, g_connections[i].port);
287 fprintf(stderr, "(Re)connecting to peer..."); 265 fprintf(stderr, "(Re)connecting to peer...");
288 if (newfd < 0) 266 if (newfd == -1)
289 continue; /* No socket for you */ 267 continue; /* No socket for you */
290 io_fd(newfd); 268 io_fd(newfd);
291 if (socket_bind6_reuse(newfd, g_serverip, g_serverport, 0)) {
292 io_close(newfd);
293 continue;
294 }
295 if (socket_connect6(newfd, g_connections[i].ip, g_connections[i].port, 0) == -1 && errno != EINPROGRESS && errno != EWOULDBLOCK) {
296 close(newfd);
297 continue;
298 }
299 io_wantwrite(newfd); /* So we will be informed when it is connected */ 269 io_wantwrite(newfd); /* So we will be informed when it is connected */
300 io_setcookie(newfd, g_connections + i); 270 io_setcookie(newfd, g_connections + i);
301 271
@@ -308,12 +278,12 @@ static void handle_reconnects(void) {
308} 278}
309 279
310/* Handle incoming connection requests, check against whitelist */ 280/* Handle incoming connection requests, check against whitelist */
311static void handle_accept(int64 serversocket) { 281static void handle_accept(int64_t serversocket) {
312 int64 newfd; 282 int64_t newfd;
313 ot_ip6 ip; 283 ot_ip6 ip;
314 uint16 port; 284 uint16_t port;
315 285
316 while ((newfd = socket_accept6(serversocket, ip, &port, NULL)) != -1) { 286 while ((newfd = socket_accept(serversocket, ip, &port)) != -1) {
317 287
318 /* XXX some access control */ 288 /* XXX some access control */
319 289
@@ -351,9 +321,9 @@ static void handle_accept(int64 serversocket) {
351} 321}
352 322
353/* New sync data on the stream */ 323/* New sync data on the stream */
354static void handle_read(int64 peersocket) { 324static void handle_read(int64_t peersocket) {
355 int i; 325 int i;
356 int64 datalen; 326 int64_t datalen;
357 uint32_t tracker_id; 327 uint32_t tracker_id;
358 proxy_peer *peer = io_getcookie(peersocket); 328 proxy_peer *peer = io_getcookie(peersocket);
359 329
@@ -420,7 +390,7 @@ static void handle_read(int64 peersocket) {
420} 390}
421 391
422/* Can write new sync data to the stream */ 392/* Can write new sync data to the stream */
423static void handle_write(int64 peersocket) { 393static void handle_write(int64_t peersocket) {
424 proxy_peer *peer = io_getcookie(peersocket); 394 proxy_peer *peer = io_getcookie(peersocket);
425 395
426 if (!peer) { 396 if (!peer) {
@@ -436,7 +406,7 @@ static void handle_write(int64 peersocket) {
436 break; 406 break;
437 case FLAG_CONNECTING: 407 case FLAG_CONNECTING:
438 /* Ensure that the connection is established and handle connection error */ 408 /* Ensure that the connection is established and handle connection error */
439 if (peer->state & FLAG_OUTGOING && !socket_connected(peersocket)) { 409 if (peer->state & FLAG_OUTGOING && !socket_is_connected(peersocket)) {
440 fprintf(stderr, "failed\n"); 410 fprintf(stderr, "failed\n");
441 reset_info_block(peer); 411 reset_info_block(peer);
442 io_close(peersocket); 412 io_close(peersocket);
@@ -472,7 +442,7 @@ static void handle_write(int64 peersocket) {
472} 442}
473 443
474static void server_mainloop() { 444static void server_mainloop() {
475 int64 sock; 445 int64_t sock;
476 uint32_t packet_type; 446 uint32_t packet_type;
477 447
478 /* inlined livesync_init() */ 448 /* inlined livesync_init() */
@@ -515,13 +485,10 @@ static void panic(const char *routine) {
515} 485}
516 486
517static int64_t ot_try_bind(ot_ip6 ip, uint16_t port) { 487static int64_t ot_try_bind(ot_ip6 ip, uint16_t port) {
518 int64 sock = socket_tcp6(); 488 int64_t sock = socket_tcp_listen(ip, port);
519
520 if (socket_bind6_reuse(sock, ip, port, 0) == -1)
521 panic("socket_bind6_reuse");
522 489
523 if (socket_listen(sock, SOMAXCONN) == -1) 490 if (sock == -1)
524 panic("socket_listen"); 491 panic("socket_tcp_listen");
525 492
526 if (!io_fd(sock)) 493 if (!io_fd(sock))
527 panic("io_fd"); 494 panic("io_fd");
@@ -776,7 +743,7 @@ static void *streamsync_worker(void *args) {
776} 743}
777 744
778static void livesync_issue_peersync() { 745static void livesync_issue_peersync() {
779 socket_send4(g_socket_out, (char *)g_peerbuffer_start, g_peerbuffer_pos - g_peerbuffer_start, groupip_1, LIVESYNC_PORT); 746 socket_mcast_send(g_socket_out, g_peerbuffer_start, (size_t)(g_peerbuffer_pos - g_peerbuffer_start), groupip_1, LIVESYNC_PORT);
780 g_peerbuffer_pos = g_peerbuffer_start + sizeof(g_tracker_id) + sizeof(uint32_t); 747 g_peerbuffer_pos = g_peerbuffer_start + sizeof(g_tracker_id) + sizeof(uint32_t);
781 g_next_packet_time = time(NULL) + LIVESYNC_MAXDELAY; 748 g_next_packet_time = time(NULL) + LIVESYNC_MAXDELAY;
782} 749}
@@ -871,7 +838,7 @@ static void *livesync_worker(void *args) {
871 ot_ip6 in_ip; 838 ot_ip6 in_ip;
872 uint16_t in_port; 839 uint16_t in_port;
873 uint32_t packet_type; 840 uint32_t packet_type;
874 size_t datalen = socket_recv4(g_socket_in, (char *)g_inbuffer, LIVESYNC_INCOMING_BUFFSIZE, 12 + (char *)in_ip, &in_port); 841 ssize_t datalen = socket_recv(g_socket_in, g_inbuffer, LIVESYNC_INCOMING_BUFFSIZE, in_ip, &in_port);
875 842
876 /* Expect at least tracker id and packet type */ 843 /* Expect at least tracker id and packet type */
877 if (datalen <= (ssize_t)(sizeof(g_tracker_id) + sizeof(uint32_t))) 844 if (datalen <= (ssize_t)(sizeof(g_tracker_id) + sizeof(uint32_t)))
diff --git a/scan_urlencoded_query.c b/scan_urlencoded_query.c
index cf0c5b1..4825a4a 100644
--- a/scan_urlencoded_query.c
+++ b/scan_urlencoded_query.c
@@ -170,16 +170,12 @@ size_t fmt_urlencoded(char *dest, const uint8_t *src, size_t len) {
170 170
171 /* strchr would find the terminator, so a zero byte never counts as safe */ 171 /* strchr would find the terminator, so a zero byte never counts as safe */
172 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c && strchr(safe, c))) { 172 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c && strchr(safe, c))) {
173 if (dest) 173 dest[written++] = (char)c;
174 dest[written] = (char)c;
175 ++written;
176 } else { 174 } else {
177 if (dest) { 175 dest[written] = '%';
178 dest[written] = '%'; 176 dest[written + 1] = hexdigit[c >> 4];
179 dest[written + 1] = hexdigit[c >> 4]; 177 dest[written + 2] = hexdigit[c & 15];
180 dest[written + 2] = hexdigit[c & 15]; 178 written += 3;
181 }
182 written += 3;
183 } 179 }
184 } 180 }
185 181
diff --git a/scan_urlencoded_query.h b/scan_urlencoded_query.h
index 6f12c03..d0929cf 100644
--- a/scan_urlencoded_query.h
+++ b/scan_urlencoded_query.h
@@ -55,9 +55,7 @@ void scan_urlencoded_skipvalue(char **string);
55ssize_t scan_fixed_int(char *data, size_t len, int *number); 55ssize_t scan_fixed_int(char *data, size_t len, int *number);
56 56
57/* The other direction: percent encode len bytes from src into dest and return 57/* The other direction: percent encode len bytes from src into dest and return
58 how many characters were written, or how many would be needed when dest is 58 how many characters were written. dest needs room for three times len. */
59 NULL. dest needs room for three times len.
60 */
61size_t fmt_urlencoded(char *dest, const uint8_t *src, size_t len); 59size_t fmt_urlencoded(char *dest, const uint8_t *src, size_t len);
62 60
63#endif 61#endif
diff --git a/tests/runner.c b/tests/runner.c
index 10b6625..db62246 100644
--- a/tests/runner.c
+++ b/tests/runner.c
@@ -22,6 +22,7 @@ static const struct module modules[] = {{"scan_urlencoded_query", test_scan_urle
22 {"ot_scan", test_scan}, 22 {"ot_scan", test_scan},
23 {"ot_buf", test_buf}, 23 {"ot_buf", test_buf},
24 {"ot_ip", test_ip}, 24 {"ot_ip", test_ip},
25 {"ot_socket", test_socket},
25 {"ot_accesslist", test_accesslist}, 26 {"ot_accesslist", test_accesslist},
26 {NULL, NULL}}; 27 {NULL, NULL}};
27 28
diff --git a/tests/t_scan_urlencoded_query.c b/tests/t_scan_urlencoded_query.c
index 6fa34e0..f660ea3 100644
--- a/tests/t_scan_urlencoded_query.c
+++ b/tests/t_scan_urlencoded_query.c
@@ -295,14 +295,6 @@ static void case_fmt_hex_is_lower_case(void) {
295 CHECK_STR(dest, "%00%ab%ff"); 295 CHECK_STR(dest, "%00%ab%ff");
296} 296}
297 297
298static void case_fmt_length_only(void) {
299 const uint8_t src[] = {'a', 0xff, 'b'};
300
301 test_case("a null destination reports the length without writing");
302 CHECK_INT(fmt_urlencoded(NULL, src, sizeof(src)), 5);
303 CHECK_INT(fmt_urlencoded(NULL, src, 0), 0);
304}
305
306static void case_fmt_zero_length(void) { 298static void case_fmt_zero_length(void) {
307 char dest[4]; 299 char dest[4];
308 300
@@ -374,7 +366,6 @@ void test_scan_urlencoded_query(void) {
374 case_fmt_passes_safe_bytes(); 366 case_fmt_passes_safe_bytes();
375 case_fmt_escapes_the_rest(); 367 case_fmt_escapes_the_rest();
376 case_fmt_hex_is_lower_case(); 368 case_fmt_hex_is_lower_case();
377 case_fmt_length_only();
378 case_fmt_zero_length(); 369 case_fmt_zero_length();
379 case_fmt_worst_case_fits(); 370 case_fmt_worst_case_fits();
380 case_fmt_decoder_disagrees_on_dollar(); 371 case_fmt_decoder_disagrees_on_dollar();
diff --git a/tests/t_socket.c b/tests/t_socket.c
new file mode 100644
index 0000000..bbc9e5e
--- /dev/null
+++ b/tests/t_socket.c
@@ -0,0 +1,307 @@
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 <fcntl.h>
9#include <netinet/in.h>
10#include <poll.h>
11#include <string.h>
12#include <sys/socket.h>
13#include <unistd.h>
14
15/* Opentracker */
16#include "ot_ip.h"
17#include "ot_socket.h"
18#include "test.h"
19
20/* These cases talk to the loopback interface rather than to a mock, because
21 what is worth pinning down here is the behaviour of the socket options the
22 rest of the tracker relies on, and only the kernel can report that. */
23
24static const char ip_any[16] = {0};
25static const char ip_loopback[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
26static const char ip_v4_loopback[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 127, 0, 0, 1};
27/* RFC 3849 reserves 2001:db8::/32 for documentation, so no host carries it */
28static const char ip_unassigned[16] = {0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
29
30/* What a socket ended up bound to, which is not always what was asked for: a
31 FreeBSD jail, for one, rewrites a loopback bind to the address it was given.
32 A datagram a socket sends to itself must come back carrying this, so the
33 cases below compare against it rather than against the address they passed
34 to the bind. */
35static void bound_address(int s, char ip[16], uint16_t *port) {
36 struct sockaddr_storage ss;
37 socklen_t len = sizeof(ss);
38
39 memset(&ss, 0, sizeof(ss));
40 memset(ip, 0, 16);
41 *port = 0;
42 if (getsockname(s, (struct sockaddr *)&ss, &len) == -1)
43 return;
44
45 /* Spelled out here rather than borrowed from ot_socket.c, so that a wrong
46 idea about how a kernel reports an address cannot hide in both at once */
47 if (ss.ss_family == AF_INET) {
48 const struct sockaddr_in *sa = (const struct sockaddr_in *)&ss;
49
50 memcpy(ip, ip_v4mapped_prefix, sizeof(ip_v4mapped_prefix));
51 memcpy(ip + sizeof(ip_v4mapped_prefix), &sa->sin_addr, 4);
52 *port = ntohs(sa->sin_port);
53 } else {
54 const struct sockaddr_in6 *sa = (const struct sockaddr_in6 *)&ss;
55
56 memcpy(ip, &sa->sin6_addr, 16);
57 *port = ntohs(sa->sin6_port);
58 }
59}
60
61static int is_nonblocking(int s) {
62 int flags = fcntl(s, F_GETFL, 0);
63 return flags != -1 && (flags & O_NONBLOCK) != 0;
64}
65
66static int sock_flag(int s, int level, int option) {
67 int value = -1;
68 socklen_t len = sizeof(value);
69
70 if (getsockopt(s, level, option, &value, &len) == -1)
71 return -1;
72 return value != 0;
73}
74
75/* Wait for one event, so a test never blocks forever on a lost packet */
76static int wait_for(int s, short events) {
77 struct pollfd pfd;
78
79 pfd.fd = s;
80 pfd.events = events;
81 pfd.revents = 0;
82 return poll(&pfd, 1, 2000) == 1;
83}
84
85/* ------------------------------------------------------------ invariants */
86
87/* Asked for the wildcard address, because a socket bound to one specific
88 IPv6 address cannot carry IPv4 traffic anyway and Linux turns IPV6_V6ONLY
89 back on in that case. The tracker's own default is the wildcard. */
90static void case_socket_options(void) {
91 int udp, tcp;
92
93 test_case("a bound udp socket is non blocking and serves both families");
94 udp = socket_udp_bind(ip_any, 0);
95 CHECK(udp != -1);
96 CHECK_INT(is_nonblocking(udp), 1);
97 CHECK_INT(sock_flag(udp, IPPROTO_IPV6, IPV6_V6ONLY), 0);
98 close(udp);
99
100 test_case("a listening tcp socket is non blocking and serves both families");
101 tcp = socket_tcp_listen(ip_any, 0);
102 CHECK(tcp != -1);
103 CHECK_INT(is_nonblocking(tcp), 1);
104 CHECK_INT(sock_flag(tcp, IPPROTO_IPV6, IPV6_V6ONLY), 0);
105 close(tcp);
106}
107
108#ifdef SO_REUSEPORT
109static void case_reuseport_is_opt_in(void) {
110 int s;
111
112 test_case("reuseport stays off unless the operator asks for it");
113 g_socket_reuseport = 0;
114 s = socket_udp_bind(ip_loopback, 0);
115 CHECK(s != -1);
116 CHECK_INT(sock_flag(s, SOL_SOCKET, SO_REUSEPORT), 0);
117 close(s);
118
119 test_case("reuseport reaches the socket when it is asked for");
120 g_socket_reuseport = 1;
121 s = socket_udp_bind(ip_loopback, 0);
122 CHECK(s != -1);
123 CHECK_INT(sock_flag(s, SOL_SOCKET, SO_REUSEPORT), 1);
124 close(s);
125
126 g_socket_reuseport = 0;
127}
128#endif
129
130/* ------------------------------------------------------------------- udp */
131
132static void case_udp_roundtrip(void) {
133 char buf[32], ip[16], own_ip[16];
134 uint16_t port = 0, own_port = 0;
135 int s;
136
137 test_case("a datagram comes back with the sender's address and port");
138 s = socket_udp_bind(ip_loopback, 0);
139 CHECK(s != -1);
140 bound_address(s, own_ip, &own_port);
141 CHECK(own_port != 0);
142 CHECK_INT(socket_send(s, "opentracker", 11, own_ip, own_port), 11);
143 CHECK_INT(wait_for(s, POLLIN), 1);
144 CHECK_INT(socket_recv(s, buf, sizeof(buf), ip, &port), 11);
145 CHECK_MEM(buf, "opentracker", 11);
146 CHECK_MEM(ip, own_ip, 16);
147 CHECK_INT(port, own_port);
148 close(s);
149}
150
151/* An IPv4 peer must arrive in the v4 mapped form opentracker stores, however
152 the kernel chose to report it. Linux fills a v4 mapped sockaddr_in6 here
153 and the BSDs fill a plain sockaddr_in, so this is the case that tells the
154 two apart. */
155static void case_udp_v4mapped(void) {
156 char buf[32], ip[16], receiver_ip[16], sender_ip[16];
157 uint16_t port = 0, receiver_port = 0, sender_port = 0;
158 int receiver, sender;
159
160 test_case("an IPv4 sender is reported in its v4 mapped form");
161 receiver = socket_udp_bind(ip_v4_loopback, 0);
162 CHECK(receiver != -1);
163 sender = socket_udp_bind(ip_v4_loopback, 0);
164 CHECK(sender != -1);
165 bound_address(receiver, receiver_ip, &receiver_port);
166 bound_address(sender, sender_ip, &sender_port);
167 CHECK_INT(ip_is_v4mapped(sender_ip), 1);
168
169 CHECK_INT(socket_send(sender, "v4", 2, receiver_ip, receiver_port), 2);
170 CHECK_INT(wait_for(receiver, POLLIN), 1);
171 CHECK_INT(socket_recv(receiver, buf, sizeof(buf), ip, &port), 2);
172 CHECK_INT(ip_is_v4mapped(ip), 1);
173 CHECK_MEM(ip, sender_ip, 16);
174 CHECK_INT(port, sender_port);
175 close(sender);
176 close(receiver);
177}
178
179/* ------------------------------------------------------------------- tcp */
180
181static void case_tcp_roundtrip(void) {
182 char ip[16], listener_ip[16], client_ip[16];
183 uint16_t port = 0, listener_port = 0, client_port = 0;
184 int listener, client, accepted;
185
186 test_case("a connection is accepted and carries the caller's address");
187 listener = socket_tcp_listen(ip_loopback, 0);
188 CHECK(listener != -1);
189 bound_address(listener, listener_ip, &listener_port);
190 CHECK(listener_port != 0);
191
192 client = socket_tcp_connect(ip_loopback, 0, listener_ip, listener_port);
193 CHECK(client != -1);
194 CHECK_INT(wait_for(client, POLLOUT), 1);
195 CHECK_INT(socket_is_connected(client), 1);
196 bound_address(client, client_ip, &client_port);
197
198 CHECK_INT(wait_for(listener, POLLIN), 1);
199 accepted = socket_accept(listener, ip, &port);
200 CHECK(accepted != -1);
201 CHECK_MEM(ip, client_ip, 16);
202 CHECK_INT(port, client_port);
203
204 test_case("the accepted socket is handed over non blocking");
205 CHECK_INT(is_nonblocking(accepted), 1);
206
207 close(accepted);
208 close(client);
209 close(listener);
210}
211
212static void case_connect_refused(void) {
213 char listener_ip[16];
214 int listener, client;
215 uint16_t port;
216
217 test_case("a refused connection is reported by socket_is_connected");
218 /* Take a port, then drop it, so nothing is listening on it any more */
219 listener = socket_tcp_listen(ip_loopback, 0);
220 CHECK(listener != -1);
221 bound_address(listener, listener_ip, &port);
222 close(listener);
223
224 client = socket_tcp_connect(ip_loopback, 0, listener_ip, port);
225 CHECK(client != -1);
226 CHECK_INT(wait_for(client, POLLOUT), 1);
227 CHECK_INT(socket_is_connected(client), 0);
228 CHECK_INT(errno, ECONNREFUSED);
229 close(client);
230}
231
232/* --------------------------------------------------------- failed binds */
233
234/* The lowest free descriptor is handed out first, so a number that stays put
235 across a failing call shows the call did not keep a socket behind. */
236static int next_descriptor(void) {
237 int fd = dup(0);
238
239 if (fd != -1)
240 close(fd);
241 return fd;
242}
243
244static void case_bind_failure(void) {
245 int before;
246
247 test_case("binding an address no interface carries fails");
248 before = next_descriptor();
249 CHECK(before != -1);
250 CHECK_INT(socket_tcp_listen(ip_unassigned, 0), -1);
251 CHECK_INT(socket_udp_bind(ip_unassigned, 0), -1);
252
253 test_case("a failed bind leaves no socket behind");
254 CHECK_INT(next_descriptor(), before);
255}
256
257#ifdef WANT_SYNC_LIVE
258/* Reading an IPv4 multicast option back: Linux answers with an int, the BSDs
259 with a single byte, so ask how much was written. */
260static int mcast_option(int s, int option) {
261 int value = 0;
262 socklen_t len = sizeof(value);
263
264 if (getsockopt(s, IPPROTO_IP, option, &value, &len) == -1)
265 return -1;
266 if (len == 1)
267 return *(unsigned char *)&value;
268 return value;
269}
270
271static void case_mcast_sender(void) {
272 int s;
273
274 test_case("the sending socket keeps its traffic on the local network");
275 s = socket_mcast_bind(ip_v4_loopback, 0, 0);
276 CHECK(s != -1);
277 CHECK_INT(mcast_option(s, IP_MULTICAST_TTL), 1);
278
279 test_case("and does not hear itself unless it is asked to");
280 CHECK_INT(mcast_option(s, IP_MULTICAST_LOOP), 0);
281
282 test_case("whoever sends from it is serving clients too, so it cannot block");
283 CHECK_INT(is_nonblocking(s), 1);
284 close(s);
285
286 test_case("the proxy asks to hear itself and gets it");
287 s = socket_mcast_bind(ip_v4_loopback, 0, 1);
288 CHECK(s != -1);
289 CHECK_INT(mcast_option(s, IP_MULTICAST_LOOP), 1);
290 close(s);
291}
292#endif
293
294void test_socket(void) {
295 case_socket_options();
296#ifdef SO_REUSEPORT
297 case_reuseport_is_opt_in();
298#endif
299 case_udp_roundtrip();
300 case_udp_v4mapped();
301 case_tcp_roundtrip();
302 case_connect_refused();
303 case_bind_failure();
304#ifdef WANT_SYNC_LIVE
305 case_mcast_sender();
306#endif
307}
diff --git a/tests/test.h b/tests/test.h
index 7449b73..d79c4d2 100644
--- a/tests/test.h
+++ b/tests/test.h
@@ -85,5 +85,6 @@ void test_accesslist(void);
85void test_scan(void); 85void test_scan(void);
86void test_buf(void); 86void test_buf(void);
87void test_ip(void); 87void test_ip(void);
88void test_socket(void);
88 89
89#endif 90#endif
diff --git a/trackerlogic.c b/trackerlogic.c
index f5e3dc0..db1d787 100644
--- a/trackerlogic.c
+++ b/trackerlogic.c
@@ -31,8 +31,8 @@
31/* Forward declaration */ 31/* Forward declaration */
32size_t return_peers_for_torrent(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto); 32size_t return_peers_for_torrent(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto);
33 33
34void free_peerlist(ot_peerlist *peer_list) { 34void free_peerlist(ot_peerlist *peer_list) {
35 if (peer_list->peers.data) { 35 if (peer_list && peer_list->peers.data) {
36 if (OT_PEERLIST_HASBUCKETS(peer_list)) 36 if (OT_PEERLIST_HASBUCKETS(peer_list))
37 vector_clean_list((ot_vector *)peer_list->peers.data, peer_list->peers.size); 37 vector_clean_list((ot_vector *)peer_list->peers.data, peer_list->peers.size);
38 else 38 else