summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile46
-rw-r--r--opentracker.c29
-rw-r--r--ot_clean.h2
-rw-r--r--ot_http.h8
-rw-r--r--ot_iovec.c3
-rw-r--r--ot_livesync.c42
-rw-r--r--ot_livesync.h2
-rw-r--r--ot_socket.c256
-rw-r--r--ot_socket.h45
-rw-r--r--ot_stats.c8
-rw-r--r--ot_udp.c20
-rw-r--r--ot_udp.h2
-rw-r--r--ot_vector.h5
-rw-r--r--proxy.c65
-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
20 files changed, 721 insertions, 148 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 af336aa..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"
@@ -254,10 +254,10 @@ static void handle_accept(const int64_t serversocket) {
254 struct http_data *cookie; 254 struct http_data *cookie;
255 int64_t 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);
@@ -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_t 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_http.h b/ot_http.h
index 004d710..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,
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 8b2dee6..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_t g_socket_in = -1;
44
45/* For incoming packets */ 39/* For incoming packets */
46static int64_t 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 6eca479..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);
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 7945f4e..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
diff --git a/ot_udp.c b/ot_udp.c
index 8e056db..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;
@@ -75,15 +72,16 @@ int 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_t 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_t 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_t 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_t 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 }
diff --git a/ot_udp.h b/ot_udp.h
index c670175..87d99a9 100644
--- a/ot_udp.h
+++ b/ot_udp.h
@@ -8,6 +8,8 @@
8 8
9#include <stdint.h> 9#include <stdint.h>
10 10
11#include "trackerlogic.h"
12
11void udp_init(int64_t sock, unsigned int worker_count); 13void udp_init(int64_t sock, unsigned int worker_count);
12void udp_kickoff(void); 14void udp_kickoff(void);
13int handle_udp6(int64_t serversocket, struct ot_workstruct *ws); 15int handle_udp6(int64_t serversocket, struct ot_workstruct *ws);
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 c2dc417..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_t 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_t 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) {
@@ -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_t 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
@@ -311,9 +281,9 @@ static void handle_reconnects(void) {
311static void handle_accept(int64_t serversocket) { 281static void handle_accept(int64_t serversocket) {
312 int64_t 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
@@ -436,7 +406,7 @@ static void handle_write(int64_t 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);
@@ -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_t 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