summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile10
-rw-r--r--opentracker.c16
-rw-r--r--ot_accesslist.c30
-rw-r--r--ot_accesslist.h2
-rw-r--r--ot_hex.c20
-rw-r--r--ot_hex.h4
-rw-r--r--ot_http.c10
-rw-r--r--ot_ip.c201
-rw-r--r--ot_ip.h33
-rw-r--r--ot_livesync.c10
-rw-r--r--ot_stats.c13
-rw-r--r--ot_udp.c4
-rw-r--r--ot_vector.c2
-rw-r--r--proxy.c10
-rw-r--r--tests/runner.c1
-rw-r--r--tests/t_ip.c217
-rw-r--r--tests/test.h1
-rw-r--r--trackerlogic.c12
18 files changed, 531 insertions, 65 deletions
diff --git a/Makefile b/Makefile
index f35d1af..27d635d 100644
--- a/Makefile
+++ b/Makefile
@@ -68,9 +68,9 @@ CFLAGS+=-I$(LIBOWFAT_HEADERS) -DGIT_VERSION=$(GIT_VERSION) -Wall -pipe -pthread
68LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread 68LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread
69 69
70BINARY =opentracker 70BINARY =opentracker
71HEADERS=trackerlogic.h scan_urlencoded_query.h ot_buf.h ot_hex.h ot_scan.h ot_mutex.h ot_random.h ot_stats.h ot_vector.h ot_clean.h ot_udp.h ot_iovec.h ot_fullscrape.h ot_accesslist.h ot_http.h ot_livesync.h ot_rijndael.h 71HEADERS=trackerlogic.h scan_urlencoded_query.h ot_buf.h ot_hex.h ot_ip.h ot_scan.h ot_mutex.h ot_random.h ot_stats.h ot_vector.h ot_clean.h ot_udp.h ot_iovec.h ot_fullscrape.h ot_accesslist.h ot_http.h ot_livesync.h ot_rijndael.h
72SOURCES=opentracker.c trackerlogic.c scan_urlencoded_query.c ot_buf.c ot_hex.c ot_scan.c ot_mutex.c ot_random.c ot_stats.c ot_vector.c ot_clean.c ot_udp.c ot_iovec.c ot_fullscrape.c ot_accesslist.c ot_http.c ot_livesync.c ot_rijndael.c 72SOURCES=opentracker.c trackerlogic.c scan_urlencoded_query.c ot_buf.c ot_hex.c ot_ip.c ot_scan.c ot_mutex.c ot_random.c ot_stats.c ot_vector.c ot_clean.c ot_udp.c ot_iovec.c ot_fullscrape.c ot_accesslist.c ot_http.c ot_livesync.c ot_rijndael.c
73SOURCES_proxy=proxy.c ot_vector.c ot_mutex.c ot_random.c ot_scan.c 73SOURCES_proxy=proxy.c ot_vector.c ot_mutex.c ot_random.c ot_scan.c ot_ip.c
74 74
75OBJECTS = $(SOURCES:%.c=%.o) 75OBJECTS = $(SOURCES:%.c=%.o)
76OBJECTS_debug = $(SOURCES:%.c=%.debug.o) 76OBJECTS_debug = $(SOURCES:%.c=%.debug.o)
@@ -105,8 +105,8 @@ proxy.debug: $(OBJECTS_proxy_debug) $(HEADERS)
105# has to. tests/stubs.c supplies the few symbols those modules reference but 105# has to. tests/stubs.c supplies the few symbols those modules reference but
106# the tests never reach. 106# the tests never reach.
107TESTBIN=tests/runner 107TESTBIN=tests/runner
108TEST_SOURCES=tests/runner.c tests/test.c tests/stubs.c tests/t_scan_urlencoded_query.c tests/t_iovec.c tests/t_vector.c tests/t_random.c tests/t_hex.c tests/t_accesslist.c tests/t_scan.c tests/t_buf.c 108TEST_SOURCES=tests/runner.c tests/test.c tests/stubs.c tests/t_scan_urlencoded_query.c tests/t_iovec.c tests/t_vector.c tests/t_random.c tests/t_hex.c tests/t_accesslist.c tests/t_scan.c tests/t_buf.c tests/t_ip.c
109TEST_UNITS=scan_urlencoded_query.c ot_buf.c ot_iovec.c ot_vector.c ot_random.c ot_hex.c ot_scan.c ot_accesslist.c 109TEST_UNITS=scan_urlencoded_query.c ot_buf.c ot_ip.c ot_iovec.c ot_vector.c ot_random.c ot_hex.c ot_scan.c ot_accesslist.c
110TEST_HEADERS=tests/test.h $(HEADERS) 110TEST_HEADERS=tests/test.h $(HEADERS)
111# Tests reach code that sits behind feature flags, so the units under test are 111# Tests reach code that sits behind feature flags, so the units under test are
112# built with those features enabled. A test binary is not a production binary. 112# built with those features enabled. A test binary is not a production binary.
diff --git a/opentracker.c b/opentracker.c
index 354e837..8a39bde 100644
--- a/opentracker.c
+++ b/opentracker.c
@@ -24,17 +24,17 @@
24/* Libowfat */ 24/* Libowfat */
25#include "io.h" 25#include "io.h"
26#include "iob.h" 26#include "iob.h"
27#include "ip6.h"
28#include "socket.h" 27#include "socket.h"
29 28
30/* Opentracker */ 29/* Opentracker */
31#include "ot_accesslist.h" 30#include "ot_accesslist.h"
32#include "ot_hex.h" 31#include "ot_hex.h"
33#include "ot_http.h" 32#include "ot_http.h"
33#include "ot_ip.h"
34#include "ot_livesync.h" 34#include "ot_livesync.h"
35#include "ot_mutex.h" 35#include "ot_mutex.h"
36#include "ot_scan.h"
37#include "ot_random.h" 36#include "ot_random.h"
37#include "ot_scan.h"
38#include "ot_stats.h" 38#include "ot_stats.h"
39#include "ot_udp.h" 39#include "ot_udp.h"
40#include "trackerlogic.h" 40#include "trackerlogic.h"
@@ -346,7 +346,7 @@ static int64_t ot_try_bind(ot_ip6 ip, uint16_t port, PROTO_FLAG proto) {
346 char *protos[] = {"TCP", "UDP", "UDP mcast"}; 346 char *protos[] = {"TCP", "UDP", "UDP mcast"};
347 char _debug[512]; 347 char _debug[512];
348 int off = snprintf(_debug, sizeof(_debug), "Binding socket type %s to address [", protos[proto]); 348 int off = snprintf(_debug, sizeof(_debug), "Binding socket type %s to address [", protos[proto]);
349 off += fmt_ip6c(_debug + off, ip); 349 off += ip_format(_debug + off, ip);
350 snprintf(_debug + off, sizeof(_debug) - off, "]:%d...", port); 350 snprintf(_debug + off, sizeof(_debug) - off, "]:%d...", port);
351 fputs(_debug, stderr); 351 fputs(_debug, stderr);
352 } 352 }
@@ -394,14 +394,14 @@ static int scan_ip6_port(const char *src, ot_ip6 ip, uint16_t *port) {
394 ++s; 394 ++s;
395 if (*s == '[') 395 if (*s == '[')
396 ++s, ++bracket; /* for v6 style notation */ 396 ++s, ++bracket; /* for v6 style notation */
397 if (!(off = scan_ip6(s, ip))) 397 if (!(off = ip_scan(s, ip)))
398 return 0; 398 return 0;
399 s += off; 399 s += off;
400 if (bracket && *s == ']') 400 if (bracket && *s == ']')
401 ++s; 401 ++s;
402 if (*s == 0 || isspace(*s)) 402 if (*s == 0 || isspace(*s))
403 return s - src; 403 return s - src;
404 if (!ip6_isv4mapped(ip)) { 404 if (!ip_is_v4mapped(ip)) {
405 if (*s != ':' && *s != '.') 405 if (*s != ':' && *s != '.')
406 return 0; 406 return 0;
407 if (!bracket && *(s) == ':') 407 if (!bracket && *(s) == ':')
@@ -423,7 +423,7 @@ static int scan_ip6_net(const char *src, ot_net *net) {
423 int off; 423 int off;
424 while (isspace(*s)) 424 while (isspace(*s))
425 ++s; 425 ++s;
426 if (!(off = scan_ip6(s, net->address))) 426 if (!(off = ip_scan(s, net->address)))
427 return 0; 427 return 0;
428 s += off; 428 s += off;
429 if (*s != '/') 429 if (*s != '/')
@@ -433,7 +433,7 @@ static int scan_ip6_net(const char *src, ot_net *net) {
433 if (!(off = scan_udec(s, &value, 128))) 433 if (!(off = scan_udec(s, &value, 128)))
434 return 0; 434 return 0;
435 net->bits = (int)value; 435 net->bits = (int)value;
436 if (ip6_isv4mapped(net->address)) 436 if (ip_is_v4mapped(net->address))
437 net->bits += 96; 437 net->bits += 96;
438 if (net->bits > 128) 438 if (net->bits > 128)
439 return 0; 439 return 0;
@@ -761,7 +761,7 @@ int main(int argc, char **argv) {
761 scanon = 0; 761 scanon = 0;
762 break; 762 break;
763 case 'i': 763 case 'i':
764 if (!scan_ip6(optarg, serverip)) { 764 if (!ip_scan(optarg, serverip)) {
765 usage(argv[0]); 765 usage(argv[0]);
766 exit(1); 766 exit(1);
767 } 767 }
diff --git a/ot_accesslist.c b/ot_accesslist.c
index a199fa4..2465ada 100644
--- a/ot_accesslist.c
+++ b/ot_accesslist.c
@@ -22,11 +22,11 @@
22#endif 22#endif
23 23
24/* Libowfat */ 24/* Libowfat */
25#include "ip6.h"
26 25
27/* Opentracker */ 26/* Opentracker */
28#include "ot_accesslist.h" 27#include "ot_accesslist.h"
29#include "ot_hex.h" 28#include "ot_hex.h"
29#include "ot_ip.h"
30#include "ot_vector.h" 30#include "ot_vector.h"
31#include "trackerlogic.h" 31#include "trackerlogic.h"
32 32
@@ -81,7 +81,7 @@ static ot_accesslist *accesslist_make(ot_accesslist *next, size_t size) {
81 if (accesslist_new) { 81 if (accesslist_new) {
82 accesslist_new->list = size ? malloc(sizeof(ot_hash) * size) : NULL; 82 accesslist_new->list = size ? malloc(sizeof(ot_hash) * size) : NULL;
83 accesslist_new->size = size; 83 accesslist_new->size = size;
84 accesslist_new->base = g_now_minutes; 84 accesslist_new->base = 0;
85 accesslist_new->next = next; 85 accesslist_new->next = next;
86 if (size && !accesslist_new->list) { 86 if (size && !accesslist_new->list) {
87 free(accesslist_new); 87 free(accesslist_new);
@@ -192,16 +192,24 @@ static void accesslist_readfile(void) {
192 192
193 /* Now exchange the accesslist vector in the least race condition prone way */ 193 /* Now exchange the accesslist vector in the least race condition prone way */
194 pthread_mutex_lock(&g_accesslist_mutex); 194 pthread_mutex_lock(&g_accesslist_mutex);
195
196 /* Mark old accesslist as "needs to live another five minutes" */
197 if (g_accesslist)
198 g_accesslist->base = g_now_minutes;
195 accesslist_new->next = g_accesslist; 199 accesslist_new->next = g_accesslist;
196 g_accesslist = accesslist_new; /* Only now set a new list */ 200 g_accesslist = accesslist_new; /* Only now set a new list */
197 201
198#ifdef WANT_DYNAMIC_ACCESSLIST 202#ifdef WANT_DYNAMIC_ACCESSLIST
199 /* If we have dynamic accesslists, reloading a new one will always void the add/delete lists. 203 /* If we have dynamic accesslists, reloading a new one will always void the add/delete lists.
200 Insert empty ones at the list head */ 204 Insert empty ones at the list head */
201 if (g_accesslist_add && (accesslist_new = accesslist_make(g_accesslist_add, 0)) != NULL) 205 if (g_accesslist_add && (accesslist_new = accesslist_make(g_accesslist_add, 0)) != NULL) {
206 g_accesslist_add->base = g_now_minutes;
202 g_accesslist_add = accesslist_new; 207 g_accesslist_add = accesslist_new;
203 if (g_accesslist_delete && (accesslist_new = accesslist_make(g_accesslist_delete, 0)) != NULL) 208 }
209 if (g_accesslist_delete && (accesslist_new = accesslist_make(g_accesslist_delete, 0)) != NULL) {
210 g_accesslist_delete->base = g_now_minutes;
204 g_accesslist_delete = accesslist_new; 211 g_accesslist_delete = accesslist_new;
212 }
205#endif 213#endif
206 214
207 accesslist_clean(g_accesslist); 215 accesslist_clean(g_accesslist);
@@ -209,7 +217,7 @@ static void accesslist_readfile(void) {
209 pthread_mutex_unlock(&g_accesslist_mutex); 217 pthread_mutex_unlock(&g_accesslist_mutex);
210} 218}
211 219
212int accesslist_hashisvalid(ot_hash hash) { 220int accesslist_hashisvalid(const ot_hash hash) {
213 /* Get working copy of current access list */ 221 /* Get working copy of current access list */
214 ot_accesslist *accesslist = g_accesslist; 222 ot_accesslist *accesslist = g_accesslist;
215#ifdef WANT_DYNAMIC_ACCESSLIST 223#ifdef WANT_DYNAMIC_ACCESSLIST
@@ -315,7 +323,8 @@ static void *accesslist_adddel_worker(char *fifoname, ot_accesslist *_Atomic
315 ot_accesslist *accesslist_new = accesslist_make(*removing_from, (*removing_from)->size - 1); 323 ot_accesslist *accesslist_new = accesslist_make(*removing_from, (*removing_from)->size - 1);
316 if (accesslist_new) { 324 if (accesslist_new) {
317 memcpy(accesslist_new->list, (*removing_from)->list, sizeof(ot_hash) * off); 325 memcpy(accesslist_new->list, (*removing_from)->list, sizeof(ot_hash) * off);
318 memcpy(accesslist_new->list + off, (*removing_from)->list + off + 1, (*removing_from)->size - off - 1); 326 memcpy(accesslist_new->list + off, (*removing_from)->list + off + 1, sizeof(ot_hash) * ((*removing_from)->size - off - 1));
327 (*removing_from)->base = g_now_minutes;
319 *removing_from = accesslist_new; 328 *removing_from = accesslist_new;
320 } 329 }
321 } 330 }
@@ -330,7 +339,7 @@ static void *accesslist_adddel_worker(char *fifoname, ot_accesslist *_Atomic
330 } 339 }
331 } else { 340 } else {
332 int exactmatch = 0; 341 int exactmatch = 0;
333 ot_hash *insert_point = binary_search(info_hash, (*adding_to)->list, (*adding_to)->size, OT_HASH_COMPARE_SIZE, sizeof(ot_hash), &exactmatch); 342 ot_hash *insert_point = binary_search(info_hash, (*adding_to)->list, (*adding_to)->size, sizeof(ot_hash), OT_HASH_COMPARE_SIZE, &exactmatch);
334 343
335 /* Only if the info hash is not in the adding_to list, create a new head with that entry */ 344 /* Only if the info hash is not in the adding_to list, create a new head with that entry */
336 if (!exactmatch) { 345 if (!exactmatch) {
@@ -339,7 +348,8 @@ static void *accesslist_adddel_worker(char *fifoname, ot_accesslist *_Atomic
339 if (accesslist_new) { 348 if (accesslist_new) {
340 memcpy(accesslist_new->list, (*adding_to)->list, sizeof(ot_hash) * off); 349 memcpy(accesslist_new->list, (*adding_to)->list, sizeof(ot_hash) * off);
341 memcpy(accesslist_new->list + off, info_hash, sizeof(info_hash)); 350 memcpy(accesslist_new->list + off, info_hash, sizeof(info_hash));
342 memcpy(accesslist_new->list + off + 1, (*adding_to)->list + off, (*adding_to)->size - off); 351 memcpy(accesslist_new->list + off + 1, (*adding_to)->list + off, sizeof(ot_hash) * ((*adding_to)->size - off));
352 (*adding_to)->base = g_now_minutes;
343 *adding_to = accesslist_new; 353 *adding_to = accesslist_new;
344 } 354 }
345 } 355 }
@@ -546,10 +556,10 @@ int accesslist_bless_net(ot_net *net, ot_permissions permissions) {
546 { 556 {
547 char _debug[512]; 557 char _debug[512];
548 int off = snprintf(_debug, sizeof(_debug), "Blessing ip net "); 558 int off = snprintf(_debug, sizeof(_debug), "Blessing ip net ");
549 off += fmt_ip6c(_debug + off, net->address); 559 off += ip_format(_debug + off, net->address);
550 if (net->bits < 128) { 560 if (net->bits < 128) {
551 _debug[off++] = '/'; 561 _debug[off++] = '/';
552 if (ip6_isv4mapped(net->address)) 562 if (ip_is_v4mapped(net->address))
553 off += snprintf(_debug + off, sizeof(_debug) - off, "%d", net->bits - 96); 563 off += snprintf(_debug + off, sizeof(_debug) - off, "%d", net->bits - 96);
554 else 564 else
555 off += snprintf(_debug + off, sizeof(_debug) - off, "%d", net->bits); 565 off += snprintf(_debug + off, sizeof(_debug) - off, "%d", net->bits);
diff --git a/ot_accesslist.h b/ot_accesslist.h
index b3e979d..30aaebd 100644
--- a/ot_accesslist.h
+++ b/ot_accesslist.h
@@ -16,7 +16,7 @@
16#define WANT_ACCESSLIST 16#define WANT_ACCESSLIST
17void accesslist_init(void); 17void accesslist_init(void);
18void accesslist_deinit(void); 18void accesslist_deinit(void);
19int accesslist_hashisvalid(ot_hash hash); 19int accesslist_hashisvalid(const ot_hash hash);
20void accesslist_cleanup(void); 20void accesslist_cleanup(void);
21 21
22/* Scan buf for up to capacity info_hashes in hexadecimal notation, one per 22/* Scan buf for up to capacity info_hashes in hexadecimal notation, one per
diff --git a/ot_hex.c b/ot_hex.c
index 17dd1ef..9581951 100644
--- a/ot_hex.c
+++ b/ot_hex.c
@@ -10,16 +10,18 @@
10/* Opentracker */ 10/* Opentracker */
11#include "ot_hex.h" 11#include "ot_hex.h"
12 12
13static int fromhex(unsigned char c) { 13int hex_value(int c) {
14 if (c >= '0' && c <= '9') 14 unsigned char u = (unsigned char)c;
15 return c - '0'; 15
16 c |= 0x20; /* fold upper case onto lower, digits are already handled */ 16 if (u >= '0' && u <= '9')
17 if (c >= 'a' && c <= 'f') 17 return u - '0';
18 return c - 'a' + 10; 18 u |= 0x20; /* fold upper case onto lower, digits are already handled */
19 if (u >= 'a' && u <= 'f')
20 return u - 'a' + 10;
19 return -1; 21 return -1;
20} 22}
21 23
22int hex_isdigit(int c) { return fromhex((unsigned char)c) >= 0; } 24int hex_isdigit(int c) { return hex_value(c) >= 0; }
23 25
24char *hex_fromhash(char *dest, const uint8_t *hash) { 26char *hex_fromhash(char *dest, const uint8_t *hash) {
25 static const char digits[] = "0123456789ABCDEF"; 27 static const char digits[] = "0123456789ABCDEF";
@@ -43,9 +45,9 @@ int hex_tohash(ot_hash hash, const char *src) {
43 45
44 /* Tested one character at a time so that a string ending early stops us 46 /* Tested one character at a time so that a string ending early stops us
45 at its terminator rather than one byte past it */ 47 at its terminator rather than one byte past it */
46 if ((high = fromhex((unsigned char)src[2 * i])) < 0) 48 if ((high = hex_value((unsigned char)src[2 * i])) < 0)
47 return 0; 49 return 0;
48 if ((low = fromhex((unsigned char)src[2 * i + 1])) < 0) 50 if ((low = hex_value((unsigned char)src[2 * i + 1])) < 0)
49 return 0; 51 return 0;
50 52
51 decoded[i] = (uint8_t)((high << 4) | low); 53 decoded[i] = (uint8_t)((high << 4) | low);
diff --git a/ot_hex.h b/ot_hex.h
index cb2e86f..3b45b61 100644
--- a/ot_hex.h
+++ b/ot_hex.h
@@ -22,4 +22,8 @@ int hex_tohash(ot_hash hash, const char *src);
22/* Non-zero if c is a hexadecimal digit, in either case. */ 22/* Non-zero if c is a hexadecimal digit, in either case. */
23int hex_isdigit(int c); 23int hex_isdigit(int c);
24 24
25/* The value of a hexadecimal digit, or -1 if c is not one. Shared with the
26 address scanner so that both agree on what a digit is. */
27int hex_value(int c);
28
25#endif 29#endif
diff --git a/ot_http.c b/ot_http.c
index 85bfcb6..dcf634f 100644
--- a/ot_http.c
+++ b/ot_http.c
@@ -16,13 +16,13 @@
16 16
17/* Libowfat */ 17/* Libowfat */
18#include "iob.h" 18#include "iob.h"
19#include "ip6.h"
20 19
21/* Opentracker */ 20/* Opentracker */
22#include "ot_accesslist.h" 21#include "ot_accesslist.h"
23#include "ot_fullscrape.h" 22#include "ot_fullscrape.h"
24#include "ot_http.h" 23#include "ot_http.h"
25#include "ot_iovec.h" 24#include "ot_iovec.h"
25#include "ot_ip.h"
26#include "ot_mutex.h" 26#include "ot_mutex.h"
27#include "ot_scan.h" 27#include "ot_scan.h"
28#include "ot_stats.h" 28#include "ot_stats.h"
@@ -525,7 +525,7 @@ static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct *
525 if (accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_PROXY)) { 525 if (accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_PROXY)) {
526 ot_ip6 proxied_ip; 526 ot_ip6 proxied_ip;
527 char *fwd = http_header(ws->request, ws->header_size, "x-forwarded-for"); 527 char *fwd = http_header(ws->request, ws->header_size, "x-forwarded-for");
528 if (fwd && scan_ip6(fwd, proxied_ip)) { 528 if (fwd && ip_scan(fwd, proxied_ip)) {
529 OT_SETIP(ws->peer, proxied_ip); 529 OT_SETIP(ws->peer, proxied_ip);
530 } else 530 } else
531 OT_SETIP(ws->peer, cookie->ip); 531 OT_SETIP(ws->peer, cookie->ip);
@@ -611,7 +611,7 @@ static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct *
611 char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16; 611 char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16;
612 len = scan_urlencoded_query(&read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE); 612 len = scan_urlencoded_query(&read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE);
613 tmp_buf2[len] = 0; 613 tmp_buf2[len] = 0;
614 if ((len <= 0) || !scan_ip6(tmp_buf2, tmp_buf1)) 614 if ((len <= 0) || !ip_scan(tmp_buf2, tmp_buf1))
615 HTTPERROR_400_PARAM; 615 HTTPERROR_400_PARAM;
616 OT_SETIP(&ws->peer, tmp_buf1); 616 OT_SETIP(&ws->peer, tmp_buf1);
617 } break; 617 } break;
@@ -633,7 +633,7 @@ static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct *
633 loglist_reset(); 633 loglist_reset();
634 return ws->reply_size = sprintf(ws->reply, "Successfully removed.\n"); 634 return ws->reply_size = sprintf(ws->reply, "Successfully removed.\n");
635 } 635 }
636 parsed = scan_ip6(tmp_buf, net.address); 636 parsed = ip_scan(tmp_buf, net.address);
637 if (!parsed) 637 if (!parsed)
638 HTTPERROR_400_PARAM; 638 HTTPERROR_400_PARAM;
639 if (tmp_buf[parsed++] != '/') 639 if (tmp_buf[parsed++] != '/')
@@ -643,7 +643,7 @@ static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct *
643 if (!parsed) 643 if (!parsed)
644 HTTPERROR_400_PARAM; 644 HTTPERROR_400_PARAM;
645 bits = (signed short)value; 645 bits = (signed short)value;
646 if (ip6_isv4mapped(net.address)) 646 if (ip_is_v4mapped(net.address))
647 bits += 96; 647 bits += 96;
648 } 648 }
649 net.bits = bits; 649 net.bits = bits;
diff --git a/ot_ip.c b/ot_ip.c
new file mode 100644
index 0000000..8cde7d2
--- /dev/null
+++ b/ot_ip.c
@@ -0,0 +1,201 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <stdint.h>
8#include <string.h>
9
10/* Opentracker */
11#include "ot_hex.h"
12#include "ot_ip.h"
13
14const char ip_v4mapped_prefix[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff};
15
16/* The dotted quad is only ever reached through the v4 mapped form, so both
17 halves of it stay in here */
18
19static size_t ip4_format(char *dest, const uint8_t *ip) {
20 char *d = dest;
21 int i;
22
23 for (i = 0; i < 4; ++i) {
24 unsigned int octet = ip[i];
25
26 if (i)
27 *d++ = '.';
28 if (octet >= 100)
29 *d++ = (char)('0' + octet / 100);
30 if (octet >= 10)
31 *d++ = (char)('0' + (octet / 10) % 10);
32 *d++ = (char)('0' + octet % 10);
33 }
34
35 return (size_t)(d - dest);
36}
37
38static size_t ip4_scan(const char *src, uint8_t ip[4]) {
39 uint8_t octets[4];
40 const char *s = src;
41 int i;
42
43 for (i = 0; i < 4; ++i) {
44 unsigned int value = 0;
45 int digits = 0;
46
47 if (i && *s++ != '.')
48 return 0;
49
50 /* Leading zeros are tolerated, a value beyond one octet is not */
51 while (*s >= '0' && *s <= '9' && digits < 4) {
52 value = value * 10 + (unsigned int)(*s++ - '0');
53 ++digits;
54 }
55 if (!digits || value > 255)
56 return 0;
57
58 octets[i] = (uint8_t)value;
59 }
60
61 memcpy(ip, octets, sizeof(octets));
62 return (size_t)(s - src);
63}
64
65static unsigned int group_at(const char *ip, int n) { return ((unsigned int)(uint8_t)ip[2 * n] << 8) | (uint8_t)ip[2 * n + 1]; }
66
67size_t ip_format(char *dest, const ot_ip6 ip) {
68 static const char hexdigit[] = "0123456789abcdef";
69 char *d = dest;
70 int best_start = 0, best_len = 0, i = 0;
71
72 if (ip_is_v4mapped(ip))
73 return ip4_format(dest, (const uint8_t *)ip + sizeof(ip_v4mapped_prefix));
74
75 /* RFC 5952 4.2: the longest run of zero groups becomes "::", never a run
76 of one, and the first of two equal runs wins */
77 while (i < 8) {
78 int j = i;
79
80 if (group_at(ip, i)) {
81 ++i;
82 continue;
83 }
84 while (j < 8 && !group_at(ip, j))
85 ++j;
86 if (j - i > best_len) {
87 best_len = j - i;
88 best_start = i;
89 }
90 i = j;
91 }
92 if (best_len < 2)
93 best_len = 0;
94
95 for (i = 0; i < 8; ++i) {
96 unsigned int group;
97 int shift = 12;
98
99 if (best_len && i == best_start) {
100 *d++ = ':';
101 if (i + best_len == 8)
102 *d++ = ':'; /* nothing follows, so close the run */
103 i += best_len - 1;
104 continue;
105 }
106
107 if (i)
108 *d++ = ':';
109
110 group = group_at(ip, i);
111 while (shift && !(group >> shift))
112 shift -= 4; /* RFC 5952 4.1: no leading zeros */
113 for (;; shift -= 4) {
114 *d++ = hexdigit[(group >> shift) & 15];
115 if (!shift)
116 break;
117 }
118 }
119
120 return (size_t)(d - dest);
121}
122
123size_t ip_scan(const char *src, ot_ip6 ip) {
124 uint8_t prefix[16], suffix[16];
125 size_t prefixlen = 0, suffixlen = 0;
126 const char *s = src;
127 int have_gap = 0;
128 int need_colon = 0;
129
130 /* A plain IPv4 address is an address too, stored v4 mapped */
131 {
132 size_t consumed = ip4_scan(src, prefix);
133 if (consumed) {
134 memcpy(ip, ip_v4mapped_prefix, sizeof(ip_v4mapped_prefix));
135 memcpy(ip + sizeof(ip_v4mapped_prefix), prefix, 4);
136 return consumed;
137 }
138 }
139
140 for (;;) {
141 unsigned int value = 0;
142 int digits = 0, nibble;
143 uint8_t *into = have_gap ? suffix : prefix;
144 size_t *fill = have_gap ? &suffixlen : &prefixlen;
145
146 /* RFC 4291 2.2: one run of zero groups may be written "::" */
147 if (s[0] == ':' && s[1] == ':') {
148 if (have_gap)
149 return 0;
150 have_gap = 1;
151 need_colon = 0;
152 s += 2;
153 continue;
154 }
155
156 if (need_colon) {
157 if (*s != ':')
158 break;
159 ++s;
160 }
161
162 /* RFC 4291 2.2 form 3: the final two groups may be a dotted quad */
163 if (prefixlen + suffixlen + 4 <= 16) {
164 size_t consumed = ip4_scan(s, into + *fill);
165 if (consumed) {
166 *fill += 4;
167 s += consumed;
168 break;
169 }
170 }
171
172 while (digits < 4 && (nibble = hex_value(*s)) >= 0) {
173 value = value * 16 + (unsigned int)nibble;
174 ++digits;
175 ++s;
176 }
177 if (!digits) {
178 if (need_colon)
179 --s; /* leave the separator we ate for the caller */
180 break;
181 }
182
183 if (prefixlen + suffixlen + 2 > 16)
184 return 0;
185
186 into[(*fill)++] = (uint8_t)(value >> 8);
187 into[(*fill)++] = (uint8_t)value;
188 need_colon = 1;
189 }
190
191 if (!prefixlen && !suffixlen && !have_gap)
192 return 0;
193 if (!have_gap && prefixlen + suffixlen != 16)
194 return 0; /* without "::" every group has to be spelled out */
195
196 memset(ip, 0, 16);
197 memcpy(ip, prefix, prefixlen);
198 memcpy(ip + 16 - suffixlen, suffix, suffixlen);
199
200 return (size_t)(s - src);
201}
diff --git a/ot_ip.h b/ot_ip.h
new file mode 100644
index 0000000..ac8964d
--- /dev/null
+++ b/ot_ip.h
@@ -0,0 +1,33 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6#ifndef OT_IP_H__
7#define OT_IP_H__
8
9#include <stddef.h>
10#include <string.h>
11
12#include "trackerlogic.h"
13
14/* opentracker holds every address as sixteen bytes, writing IPv4 as the v4
15 mapped form of RFC 4291 section 2.5.5.2. */
16extern const char ip_v4mapped_prefix[12];
17
18static inline int ip_is_v4mapped(const void *ip) { return !memcmp(ip, ip_v4mapped_prefix, sizeof(ip_v4mapped_prefix)); }
19
20/* Read an address and return how many characters it took, or zero if src
21 does not start with one. A plain IPv4 address is accepted and stored in
22 its v4 mapped form.
23
24 The count lets the caller carry on where the address ended, which is how
25 "address:port" and "address/bits" are read. */
26size_t ip_scan(const char *src, ot_ip6 ip);
27
28/* Write an address and return how many characters it took. Nothing is
29 terminated, so a caller may keep appending. A v4 mapped address is written
30 as the plain dotted quad rather than "::ffff:1.2.3.4". */
31size_t ip_format(char *dest, const ot_ip6 ip);
32
33#endif
diff --git a/ot_livesync.c b/ot_livesync.c
index c4dc6e5..f6bb28b 100644
--- a/ot_livesync.c
+++ b/ot_livesync.c
@@ -13,11 +13,11 @@
13#include <unistd.h> 13#include <unistd.h>
14 14
15/* Libowfat */ 15/* Libowfat */
16#include "ip6.h"
17#include "socket.h" 16#include "socket.h"
18 17
19/* Opentracker */ 18/* Opentracker */
20#include "ot_accesslist.h" 19#include "ot_accesslist.h"
20#include "ot_ip.h"
21#include "ot_livesync.h" 21#include "ot_livesync.h"
22#include "ot_mutex.h" 22#include "ot_mutex.h"
23#include "ot_stats.h" 23#include "ot_stats.h"
@@ -92,7 +92,7 @@ void livesync_bind_mcast(ot_ip6 ip, uint16_t port) {
92 char tmpip[4] = {0, 0, 0, 0}; 92 char tmpip[4] = {0, 0, 0, 0};
93 char *v4ip; 93 char *v4ip;
94 94
95 if (!ip6_isv4mapped(ip)) 95 if (!ip_is_v4mapped(ip))
96 exerr("v6 mcast support not yet available."); 96 exerr("v6 mcast support not yet available.");
97 v4ip = ip + 12; 97 v4ip = ip + 12;
98 98
@@ -140,14 +140,14 @@ static void livesync_handle_peersync(struct ot_workstruct *ws, size_t peer_size)
140 140
141 /* Prepare peer record for v4mapped expansion */ 141 /* Prepare peer record for v4mapped expansion */
142 if (peer_size == OT_PEER_SIZE4) 142 if (peer_size == OT_PEER_SIZE4)
143 memcpy(&ws->peer, V4mappedprefix, sizeof(V4mappedprefix)); 143 memcpy(&ws->peer, ip_v4mapped_prefix, sizeof(ip_v4mapped_prefix));
144 144
145 /* Now basic sanity checks have been done on the live sync packet 145 /* Now basic sanity checks have been done on the live sync packet
146 We might add more testing and logging. */ 146 We might add more testing and logging. */
147 while ((ssize_t)(off + sizeof(ot_hash) + peer_size) <= ws->request_size) { 147 while ((ssize_t)(off + sizeof(ot_hash) + peer_size) <= ws->request_size) {
148 char *peer_ip = ws->request + off + sizeof(ot_hash); 148 char *peer_ip = ws->request + off + sizeof(ot_hash);
149 if (peer_size == OT_PEER_SIZE4) 149 if (peer_size == OT_PEER_SIZE4)
150 memcpy(&ws->peer[0] + sizeof(V4mappedprefix), peer_ip, peer_size); 150 memcpy(&ws->peer[0] + sizeof(ip_v4mapped_prefix), peer_ip, peer_size);
151 else 151 else
152 memcpy(&ws->peer, peer_ip, peer_size); 152 memcpy(&ws->peer, peer_ip, peer_size);
153 ws->hash = (ot_hash *)(ws->request + off); 153 ws->hash = (ot_hash *)(ws->request + off);
@@ -215,7 +215,7 @@ static void *livesync_worker(void *args) {
215 ws.inbuf = ws.request = malloc(LIVESYNC_INCOMING_BUFFSIZE); 215 ws.inbuf = ws.request = malloc(LIVESYNC_INCOMING_BUFFSIZE);
216 ws.outbuf = ws.reply = 0; 216 ws.outbuf = ws.reply = 0;
217 217
218 memcpy(in_ip, V4mappedprefix, sizeof(V4mappedprefix)); 218 memcpy(in_ip, ip_v4mapped_prefix, sizeof(ip_v4mapped_prefix));
219 219
220 while (1) { 220 while (1) {
221 uint32_t packet_type; 221 uint32_t packet_type;
diff --git a/ot_stats.c b/ot_stats.c
index d43f29c..62c59c9 100644
--- a/ot_stats.c
+++ b/ot_stats.c
@@ -18,13 +18,12 @@
18 18
19/* Libowfat */ 19/* Libowfat */
20#include "io.h" 20#include "io.h"
21#include "ip4.h"
22#include "ip6.h"
23 21
24/* Opentracker */ 22/* Opentracker */
25#include "ot_accesslist.h" 23#include "ot_accesslist.h"
26#include "ot_hex.h" 24#include "ot_hex.h"
27#include "ot_iovec.h" 25#include "ot_iovec.h"
26#include "ot_ip.h"
28#include "ot_mutex.h" 27#include "ot_mutex.h"
29#include "ot_stats.h" 28#include "ot_stats.h"
30#include "trackerlogic.h" 29#include "trackerlogic.h"
@@ -206,7 +205,7 @@ static size_t stats_return_busy_networks(char *reply, stats_network_node *tree,
206 if (scores[i - 1]) { 205 if (scores[i - 1]) {
207 r += sprintf(r, "%08zd: ", scores[i - 1]); 206 r += sprintf(r, "%08zd: ", scores[i - 1]);
208 // #ifdef WANT_V6 207 // #ifdef WANT_V6
209 r += fmt_ip6c(r, networks[i - 1]); 208 r += ip_format(r, networks[i - 1]);
210#if 0 209#if 0
211 // XXX 210 // XXX
212 r += fmt_ip4( r, networks[i - 1]); 211 r += fmt_ip4( r, networks[i - 1]);
@@ -470,7 +469,7 @@ static void stats_return_fulllog(size_t *iovec_entries, struct iovec **iovector,
470 re = r + 32 * OT_STATS_TMPSIZE; 469 re = r + 32 * OT_STATS_TMPSIZE;
471 } 470 }
472 r += sprintf(r, "%08ld: ", loglist->time); 471 r += sprintf(r, "%08ld: ", loglist->time);
473 r += fmt_ip6c(r, loglist->ip); 472 r += ip_format(r, loglist->ip);
474 *r++ = '\n'; 473 *r++ = '\n';
475 memcpy(r, loglist->data, loglist->size); 474 memcpy(r, loglist->data, loglist->size);
476 r += loglist->size; 475 r += loglist->size;
@@ -663,7 +662,7 @@ void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_
663 *peerid_hex = 0; 662 *peerid_hex = 0;
664 } 663 }
665 664
666 ip_readable[fmt_ip6c(ip_readable, (char *)&ws->peer)] = 0; 665 ip_readable[ip_format(ip_readable, (char *)&ws->peer)] = 0;
667#if 0 666#if 0
668 /* XXX */ 667 /* XXX */
669 ip_readable[ fmt_ip4( ip_readable, (char*)&ws->peer ) ] = 0; 668 ip_readable[ fmt_ip4( ip_readable, (char*)&ws->peer ) ] = 0;
@@ -687,7 +686,7 @@ void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_
687 ot_ip6 *ip = (ot_ip6 *)event_data; /* ugly hack to transfer ip to stats */ 686 ot_ip6 *ip = (ot_ip6 *)event_data; /* ugly hack to transfer ip to stats */
688 char _debug[512]; 687 char _debug[512];
689 int off = snprintf(_debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(g_now_seconds - ot_start_time) / 60); 688 int off = snprintf(_debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(g_now_seconds - ot_start_time) / 60);
690 off += fmt_ip6c(_debug + off, *ip); 689 off += ip_format(_debug + off, *ip);
691 off += snprintf(_debug + off, sizeof(_debug) - off, " - FULL SCRAPE\n"); 690 off += snprintf(_debug + off, sizeof(_debug) - off, " - FULL SCRAPE\n");
692 (void)write(2, _debug, off); 691 (void)write(2, _debug, off);
693 ot_full_scrape_request_count++; 692 ot_full_scrape_request_count++;
@@ -696,7 +695,7 @@ void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_
696 ot_ip6 *ip = (ot_ip6 *)event_data; /* ugly hack to transfer ip to stats */ 695 ot_ip6 *ip = (ot_ip6 *)event_data; /* ugly hack to transfer ip to stats */
697 char _debug[512]; 696 char _debug[512];
698 int off = snprintf(_debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(g_now_seconds - ot_start_time) / 60); 697 int off = snprintf(_debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(g_now_seconds - ot_start_time) / 60);
699 off += fmt_ip6c(_debug + off, *ip); 698 off += ip_format(_debug + off, *ip);
700 off += snprintf(_debug + off, sizeof(_debug) - off, " - FULL SCRAPE\n"); 699 off += snprintf(_debug + off, sizeof(_debug) - off, " - FULL SCRAPE\n");
701 (void)write(2, _debug, off); 700 (void)write(2, _debug, off);
702 ot_full_scrape_request_count++; 701 ot_full_scrape_request_count++;
diff --git a/ot_udp.c b/ot_udp.c
index 82aa8bd..d17f3ff 100644
--- a/ot_udp.c
+++ b/ot_udp.c
@@ -12,10 +12,10 @@
12 12
13/* Libowfat */ 13/* Libowfat */
14#include "io.h" 14#include "io.h"
15#include "ip6.h"
16#include "socket.h" 15#include "socket.h"
17 16
18/* Opentracker */ 17/* Opentracker */
18#include "ot_ip.h"
19#include "ot_random.h" 19#include "ot_random.h"
20#include "ot_rijndael.h" 20#include "ot_rijndael.h"
21#include "ot_stats.h" 21#include "ot_stats.h"
@@ -176,7 +176,7 @@ int handle_udp6(int64 serversocket, struct ot_workstruct *ws) {
176 } else { 176 } else {
177 /* Limit amount of peers to OT_MAX_PEERS_UDP */ 177 /* Limit amount of peers to OT_MAX_PEERS_UDP */
178 uint32_t numwant = ntohl(inpacket[92 / 4]); 178 uint32_t numwant = ntohl(inpacket[92 / 4]);
179 size_t max_peers = ip6_isv4mapped(remoteip) ? OT_MAX_PEERS_UDP4 : OT_MAX_PEERS_UDP6; 179 size_t max_peers = ip_is_v4mapped(remoteip) ? OT_MAX_PEERS_UDP4 : OT_MAX_PEERS_UDP6;
180 if (numwant > max_peers) 180 if (numwant > max_peers)
181 numwant = max_peers; 181 numwant = max_peers;
182 182
diff --git a/ot_vector.c b/ot_vector.c
index e3c3d3f..c4cd171 100644
--- a/ot_vector.c
+++ b/ot_vector.c
@@ -156,8 +156,6 @@ void vector_remove_torrent(ot_vector *vector, ot_torrent *match) {
156 if (!vector->size) 156 if (!vector->size)
157 return; 157 return;
158 158
159 /* If this is being called after a unsuccessful malloc() for peer_list
160 in add_peer_to_torrent, match->peer_list actually might be NULL */
161 free_peerlist(match->peer_list6); 159 free_peerlist(match->peer_list6);
162 free_peerlist(match->peer_list4); 160 free_peerlist(match->peer_list4);
163 161
diff --git a/proxy.c b/proxy.c
index 19a084a..784e2a1 100644
--- a/proxy.c
+++ b/proxy.c
@@ -19,13 +19,13 @@
19/* Libowfat */ 19/* Libowfat */
20#include "io.h" 20#include "io.h"
21#include "iob.h" 21#include "iob.h"
22#include "ip6.h"
23#include "socket.h" 22#include "socket.h"
24 23
25/* Opentracker */ 24/* Opentracker */
25#include "ot_ip.h"
26#include "ot_mutex.h" 26#include "ot_mutex.h"
27#include "ot_scan.h"
28#include "ot_random.h" 27#include "ot_random.h"
28#include "ot_scan.h"
29#include "ot_stats.h" 29#include "ot_stats.h"
30#include "ot_vector.h" 30#include "ot_vector.h"
31#include "trackerlogic.h" 31#include "trackerlogic.h"
@@ -92,7 +92,7 @@ void livesync_bind_mcast(ot_ip6 ip, uint16_t port) {
92 char tmpip[4] = {0, 0, 0, 0}; 92 char tmpip[4] = {0, 0, 0, 0};
93 char *v4ip; 93 char *v4ip;
94 94
95 if (!ip6_isv4mapped(ip)) 95 if (!ip_is_v4mapped(ip))
96 exerr("v6 mcast support not yet available."); 96 exerr("v6 mcast support not yet available.");
97 v4ip = ip + 12; 97 v4ip = ip + 12;
98 98
@@ -539,14 +539,14 @@ static int scan_ip6_port(const char *src, ot_ip6 ip, uint16_t *port) {
539 ++s; 539 ++s;
540 if (*s == '[') 540 if (*s == '[')
541 ++s, ++bracket; /* for v6 style notation */ 541 ++s, ++bracket; /* for v6 style notation */
542 if (!(off = scan_ip6(s, ip))) 542 if (!(off = ip_scan(s, ip)))
543 return 0; 543 return 0;
544 s += off; 544 s += off;
545 if (*s == 0 || isspace(*s)) 545 if (*s == 0 || isspace(*s))
546 return s - src; 546 return s - src;
547 if (*s == ']' && bracket) 547 if (*s == ']' && bracket)
548 ++s; 548 ++s;
549 if (!ip6_isv4mapped(ip)) { 549 if (!ip_is_v4mapped(ip)) {
550 if ((bracket && *(s) != ':') || (*(s) != '.')) 550 if ((bracket && *(s) != ':') || (*(s) != '.'))
551 return 0; 551 return 0;
552 s++; 552 s++;
diff --git a/tests/runner.c b/tests/runner.c
index 25456bc..10b6625 100644
--- a/tests/runner.c
+++ b/tests/runner.c
@@ -21,6 +21,7 @@ static const struct module modules[] = {{"scan_urlencoded_query", test_scan_urle
21 {"ot_hex", test_hex}, 21 {"ot_hex", test_hex},
22 {"ot_scan", test_scan}, 22 {"ot_scan", test_scan},
23 {"ot_buf", test_buf}, 23 {"ot_buf", test_buf},
24 {"ot_ip", test_ip},
24 {"ot_accesslist", test_accesslist}, 25 {"ot_accesslist", test_accesslist},
25 {NULL, NULL}}; 26 {NULL, NULL}};
26 27
diff --git a/tests/t_ip.c b/tests/t_ip.c
new file mode 100644
index 0000000..2fe3a72
--- /dev/null
+++ b/tests/t_ip.c
@@ -0,0 +1,217 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <stdint.h>
8#include <string.h>
9
10/* Opentracker */
11#include "ot_ip.h"
12#include "test.h"
13
14static char text[64];
15
16static const char *formatted(const char *ip) {
17 text[ip_format(text, ip)] = 0;
18 return text;
19}
20
21/* ------------------------------------------------------------------ IPv4 */
22
23/* ------------------------------------------------------- RFC 5952 output */
24
25static void case_format_rfc5952(void) {
26 test_case("4.1 leading zeros in a group are suppressed");
27 CHECK_STR(formatted("\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"), "2001:db8::1");
28
29 test_case("4.2.1 the longest run of zero groups becomes ::");
30 CHECK_STR(formatted("\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01"), "2001:db8::2:1");
31
32 test_case("4.2.2 a single zero group is never shortened");
33 CHECK_STR(formatted("\x20\x01\x0d\xb8\x00\x00\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01"), "2001:db8:0:1:1:1:1:1");
34
35 test_case("4.2.1 a longer run wins over an earlier shorter one");
36 CHECK_STR(formatted("\x20\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01"), "2001:0:0:1::1");
37
38 test_case("4.2.3 of two runs of equal length the first is shortened");
39 CHECK_STR(formatted("\x20\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x01"), "2001::1:0:0:1:1");
40
41 test_case("4.3 hexadecimal digits are lower case");
42 CHECK_STR(formatted("\xab\xcd\xef\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"), "abcd:ef01::1");
43
44 test_case("the unspecified and loopback addresses");
45 CHECK_STR(formatted("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), "::");
46 CHECK_STR(formatted("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"), "::1");
47
48 test_case("a full address with no zero group");
49 CHECK_STR(formatted("\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x66\x66\x77\x77\x88\x88"), "1111:2222:3333:4444:5555:6666:7777:8888");
50}
51
52static void case_format_trailing_zero_group(void) {
53 /* libowfat's formatter looked two bytes past the address to decide this
54 one, so a peer address ending in a zero group came out as "::" whenever
55 the bytes that happened to follow were not themselves zero. In
56 ot_stats.c those bytes are the peer's port. */
57 const char ip[] = "\x20\x01\x0d\xb8\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x00";
58
59 test_case("a single trailing zero group is written out, not shortened");
60 CHECK_STR(formatted(ip), "2001:db8:1:2:3:4:5:0");
61
62 test_case("and nothing beyond the sixteen bytes is consulted");
63 {
64 char padded[20];
65 memcpy(padded, ip, 16);
66 memset(padded + 16, 0xff, 4); /* a port and flags, as a peer record has */
67 CHECK_STR(formatted(padded), "2001:db8:1:2:3:4:5:0");
68 memset(padded + 16, 0x00, 4);
69 CHECK_STR(formatted(padded), "2001:db8:1:2:3:4:5:0");
70 }
71}
72
73static void case_v4mapped(void) {
74 const char mapped[] = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x0a\x00\x00\x01";
75 const char unmapped[] = "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01";
76
77 test_case("a v4 mapped address is recognised");
78 CHECK(ip_is_v4mapped(mapped));
79 CHECK_INT(ip_is_v4mapped(unmapped), 0);
80
81 /* The tracker has only ever shown the plain quad, so that is the one
82 form the formatter produces; "::ffff:10.0.0.1" is not written anywhere */
83 test_case("a mapped address is written as the plain dotted quad");
84 CHECK_STR(formatted(mapped), "10.0.0.1");
85 CHECK_STR(formatted("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00"), "0.0.0.0");
86 CHECK_STR(formatted("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff"), "255.255.255.255");
87
88 test_case("anything else follows RFC 5952");
89 CHECK_STR(formatted(unmapped), "2001:db8::1");
90}
91
92/* -------------------------------------------------------- RFC 4291 input */
93
94static void case_scan_forms(void) {
95 char ip[16];
96
97 test_case("2.2 form 1, every group spelled out");
98 CHECK_INT(ip_scan("1111:2222:3333:4444:5555:6666:7777:8888", ip), 39);
99 CHECK_MEM(ip, "\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x66\x66\x77\x77\x88\x88", 16);
100
101 test_case("2.2 form 2, zero groups abbreviated");
102 CHECK_INT(ip_scan("2001:db8::1", ip), 11);
103 CHECK_MEM(ip, "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16);
104 CHECK_INT(ip_scan("::1", ip), 3);
105 CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16);
106 CHECK_INT(ip_scan("::", ip), 2);
107 CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16);
108 CHECK_INT(ip_scan("1::", ip), 3);
109 CHECK_MEM(ip, "\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16);
110
111 test_case("2.2 form 3, a dotted quad in the last two groups");
112 CHECK_INT(ip_scan("::ffff:1.2.3.4", ip), 14);
113 CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x01\x02\x03\x04", 16);
114 CHECK_INT(ip_scan("1:2:3:4:5:6:1.2.3.4", ip), 19);
115 CHECK_MEM(ip, "\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x01\x02\x03\x04", 16);
116
117 test_case("upper case input is accepted");
118 CHECK_INT(ip_scan("2001:DB8::1", ip), 11);
119 CHECK_MEM(ip, "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16);
120}
121
122static void case_scan_accepts_bare_ipv4(void) {
123 char ip[16];
124
125 /* Every address opentracker holds is sixteen bytes, so an operator writing
126 a plain IPv4 address in the config has to land as the v4 mapped form */
127 test_case("a bare IPv4 address is stored v4 mapped");
128 CHECK_INT(ip_scan("10.0.0.1", ip), 8);
129 CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x0a\x00\x00\x01", 16);
130 CHECK(ip_is_v4mapped(ip));
131}
132
133static void case_scan_reports_length(void) {
134 char ip[16];
135
136 /* The config parser continues from where the address ended, which is how
137 "address:port" and "address/bits" are read */
138 test_case("scanning stops at the character after the address");
139 CHECK_INT(ip_scan("10.0.0.1:6969", ip), 8);
140 CHECK_INT(ip_scan("2001:db8::1/64", ip), 11);
141 CHECK_INT(ip_scan("::1]", ip), 3);
142 CHECK_INT(ip_scan("1:2:3:4:5:6:7:8 trailing", ip), 15);
143}
144
145static void case_scan_rejects(void) {
146 char ip[16];
147
148 test_case("an empty or junk string is refused");
149 CHECK_INT(ip_scan("", ip), 0);
150 CHECK_INT(ip_scan("x", ip), 0);
151 CHECK_INT(ip_scan(":", ip), 0);
152 CHECK_INT(ip_scan(":1:2", ip), 0);
153
154 test_case("too few groups without :: are refused");
155 CHECK_INT(ip_scan("1:2:3:4:5:6:7", ip), 0);
156 CHECK_INT(ip_scan("2001:db8:1", ip), 0);
157
158 /* libowfat accepted a prefix of each of these and carried on. Taking only
159 part of a malformed address is worse than refusing it, especially where
160 the text comes from a config file or an X-Forwarded-For header. */
161 test_case("a second :: is refused rather than truncated");
162 CHECK_INT(ip_scan("1::2::3", ip), 0);
163 CHECK_INT(ip_scan("::1::", ip), 0);
164 CHECK_INT(ip_scan("::::", ip), 0);
165
166 test_case("more than eight groups is refused");
167 CHECK_INT(ip_scan("1:2:3:4:5:6:7:8:9", ip), 0);
168
169 test_case("a group of more than four digits is refused, not truncated");
170 CHECK_INT(ip_scan("12345::1", ip), 0);
171 CHECK_INT(ip_scan("fffff::1", ip), 0);
172}
173
174static void case_round_trip(void) {
175 /* Whatever the formatter writes, the scanner has to read back */
176 static const char *const samples[] = {"::",
177 "::1",
178 "1::",
179 "2001:db8::1",
180 "2001:db8:1:2:3:4:5:0",
181 "1111:2222:3333:4444:5555:6666:7777:8888",
182 "2001::1:0:0:1",
183 "abcd:ef01::1",
184 NULL};
185 char ip[16], again[16];
186 size_t i;
187
188 test_case("every formatted address scans back to the same bytes");
189 for (i = 0; samples[i]; ++i) {
190 size_t n = ip_scan(samples[i], ip);
191 if (n != strlen(samples[i])) {
192 test_failf(__FILE__, __LINE__, "\"%s\" consumed %zu of %zu", samples[i], n, strlen(samples[i]));
193 return;
194 }
195 text[ip_format(text, ip)] = 0;
196 if (strcmp(text, samples[i])) {
197 test_failf(__FILE__, __LINE__, "\"%s\" formatted back as \"%s\"", samples[i], text);
198 return;
199 }
200 if (!ip_scan(text, again) || memcmp(ip, again, 16)) {
201 test_failf(__FILE__, __LINE__, "\"%s\" did not survive a second pass", samples[i]);
202 return;
203 }
204 }
205 ++test_checks;
206}
207
208void test_ip(void) {
209 case_format_rfc5952();
210 case_format_trailing_zero_group();
211 case_v4mapped();
212 case_scan_forms();
213 case_scan_accepts_bare_ipv4();
214 case_scan_reports_length();
215 case_scan_rejects();
216 case_round_trip();
217}
diff --git a/tests/test.h b/tests/test.h
index 676ca33..7449b73 100644
--- a/tests/test.h
+++ b/tests/test.h
@@ -84,5 +84,6 @@ void test_hex(void);
84void test_accesslist(void); 84void test_accesslist(void);
85void test_scan(void); 85void test_scan(void);
86void test_buf(void); 86void test_buf(void);
87void test_ip(void);
87 88
88#endif 89#endif
diff --git a/trackerlogic.c b/trackerlogic.c
index d8a2f9b..db1d787 100644
--- a/trackerlogic.c
+++ b/trackerlogic.c
@@ -13,26 +13,26 @@
13/* Libowfat */ 13/* Libowfat */
14#include "io.h" 14#include "io.h"
15#include "iob.h" 15#include "iob.h"
16#include "ip6.h"
17 16
18/* Opentracker */ 17/* Opentracker */
19#include "ot_accesslist.h" 18#include "ot_accesslist.h"
20#include "ot_clean.h" 19#include "ot_clean.h"
21#include "ot_fullscrape.h" 20#include "ot_fullscrape.h"
22#include "ot_http.h" 21#include "ot_http.h"
22#include "ot_ip.h"
23#include "ot_livesync.h" 23#include "ot_livesync.h"
24#include "ot_mutex.h" 24#include "ot_mutex.h"
25#include "ot_random.h" 25#include "ot_random.h"
26#include "ot_stats.h" 26#include "ot_stats.h"
27#include "ot_vector.h"
28#include "ot_udp.h" 27#include "ot_udp.h"
28#include "ot_vector.h"
29#include "trackerlogic.h" 29#include "trackerlogic.h"
30 30
31/* Forward declaration */ 31/* Forward declaration */
32size_t return_peers_for_torrent(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto); 32size_t return_peers_for_torrent(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto);
33 33
34void free_peerlist(ot_peerlist *peer_list) { 34void free_peerlist(ot_peerlist *peer_list) {
35 if (peer_list->peers.data) { 35 if (peer_list && peer_list->peers.data) {
36 if (OT_PEERLIST_HASBUCKETS(peer_list)) 36 if (OT_PEERLIST_HASBUCKETS(peer_list))
37 vector_clean_list((ot_vector *)peer_list->peers.data, peer_list->peers.size); 37 vector_clean_list((ot_vector *)peer_list->peers.data, peer_list->peers.size);
38 else 38 else
@@ -511,7 +511,7 @@ void iterate_all_torrents(int (*for_each)(ot_torrent *torrent, uintptr_t data),
511 511
512ot_peer *peer_from_peer6(ot_peer6 *peer, size_t *peer_size) { 512ot_peer *peer_from_peer6(ot_peer6 *peer, size_t *peer_size) {
513 ot_ip6 *ip = (ot_ip6 *)peer; 513 ot_ip6 *ip = (ot_ip6 *)peer;
514 if (!ip6_isv4mapped(ip)) { 514 if (!ip_is_v4mapped(ip)) {
515 *peer_size = OT_PEER_SIZE6; 515 *peer_size = OT_PEER_SIZE6;
516 return (ot_peer *)peer; 516 return (ot_peer *)peer;
517 } 517 }
@@ -521,7 +521,7 @@ ot_peer *peer_from_peer6(ot_peer6 *peer, size_t *peer_size) {
521 521
522size_t peer_size_from_peer6(ot_peer6 *peer) { 522size_t peer_size_from_peer6(ot_peer6 *peer) {
523 ot_ip6 *ip = (ot_ip6 *)peer; 523 ot_ip6 *ip = (ot_ip6 *)peer;
524 if (!ip6_isv4mapped(ip)) 524 if (!ip_is_v4mapped(ip))
525 return OT_PEER_SIZE6; 525 return OT_PEER_SIZE6;
526 return OT_PEER_SIZE4; 526 return OT_PEER_SIZE4;
527} 527}