summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile58
-rw-r--r--Makefile.gzip4
-rw-r--r--Makefile.zstd3
-rw-r--r--man1/opentracker.1130
-rw-r--r--man4/opentracker.conf.486
-rw-r--r--opentracker.c273
-rw-r--r--opentracker.conf.sample5
-rw-r--r--ot_accesslist.c98
-rw-r--r--ot_accesslist.h6
-rw-r--r--ot_clean.c8
-rw-r--r--ot_fullscrape.c199
-rw-r--r--ot_fullscrape.h4
-rw-r--r--ot_hex.c56
-rw-r--r--ot_hex.h25
-rw-r--r--ot_http.c90
-rw-r--r--ot_http.h7
-rw-r--r--ot_iovec.c26
-rw-r--r--ot_iovec.h12
-rw-r--r--ot_livesync.c32
-rw-r--r--ot_livesync.h6
-rw-r--r--ot_mutex.c41
-rw-r--r--ot_mutex.h7
-rw-r--r--ot_random.c53
-rw-r--r--ot_random.h19
-rw-r--r--ot_rijndael.c2
-rw-r--r--ot_stats.c149
-rw-r--r--ot_stats.h4
-rw-r--r--ot_sync.c20
-rw-r--r--ot_udp.c64
-rw-r--r--ot_udp.h1
-rw-r--r--ot_vector.c35
-rw-r--r--ot_vector.h2
-rw-r--r--proxy.c35
-rw-r--r--scan_urlencoded_query.c5
-rw-r--r--tests/runner.c37
-rw-r--r--tests/stubs.c34
-rw-r--r--tests/t_accesslist.c122
-rw-r--r--tests/t_hex.c128
-rw-r--r--tests/t_iovec.c124
-rw-r--r--tests/t_random.c114
-rw-r--r--tests/t_scan_urlencoded_query.c274
-rw-r--r--tests/t_vector.c161
-rw-r--r--tests/test.c58
-rw-r--r--tests/test.h86
-rw-r--r--trackerlogic.c64
-rw-r--r--trackerlogic.h9
47 files changed, 2264 insertions, 514 deletions
diff --git a/.gitignore b/.gitignore
index 874c63c..8b4bec1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,2 @@
1*.o 1*.o
2 2tests/runner
diff --git a/Makefile b/Makefile
index 6f9a82b..e96269f 100644
--- a/Makefile
+++ b/Makefile
@@ -18,15 +18,24 @@ LIBOWFAT_LIBRARY=$(PREFIX)/libowfat
18BINDIR?=$(PREFIX)/bin 18BINDIR?=$(PREFIX)/bin
19STRIP?=strip 19STRIP?=strip
20 20
21#FEATURES+=-DWAND_V4_ONLY 21#FEATURES+=-DWANT_V4_ONLY
22#FEATURES+=-DWANT_ACCESSLIST_BLACK 22#FEATURES+=-DWANT_ACCESSLIST_BLACK
23#FEATURES+=-DWANT_ACCESSLIST_WHITE 23#FEATURES+=-DWANT_ACCESSLIST_WHITE
24#FEATURES+=-DWANT_DYNAMIC_ACCESSLIST 24#FEATURES+=-DWANT_DYNAMIC_ACCESSLIST
25 25
26#FEATURES+=-DWANT_SYNC_LIVE 26#FEATURES+=-DWANT_SYNC_LIVE
27#FEATURES+=-DWANT_IP_FROM_QUERY_STRING 27#FEATURES+=-DWANT_IP_FROM_QUERY_STRING
28FEATURES+=-DWANT_COMPRESSION_GZIP 28
29FEATURES+=-DWANT_COMPRESSION_GZIP_ALWAYS 29# If you want gzip support to be compiled in, uncomment the next include.
30# You can further modify the behaviour by setting DWANT_COMPRESSION_GZIP_ALWAYS
31# in Makefile.gzip
32include Makefile.gzip
33
34# If you want zstd support to be compiled in, uncomment the next include.
35# You can further modify the behaviour by setting DWANT_COMPRESSION_ZSTD_ALWAYS
36# in Makefile.zstd
37#include Makefile.zstd
38
30#FEATURES+=-DWANT_LOG_NETWORKS 39#FEATURES+=-DWANT_LOG_NETWORKS
31#FEATURES+=-DWANT_RESTRICT_STATS 40#FEATURES+=-DWANT_RESTRICT_STATS
32#FEATURES+=-DWANT_IP_FROM_PROXY 41#FEATURES+=-DWANT_IP_FROM_PROXY
@@ -42,24 +51,26 @@ FEATURES+=-DWANT_FULLSCRAPE
42# full scrape transfer, if you rely on an older versions, enable this flag 51# full scrape transfer, if you rely on an older versions, enable this flag
43#FEATURES+=-DWANT_NO_AUTO_FREE 52#FEATURES+=-DWANT_NO_AUTO_FREE
44 53
45# Is enabled on BSD systems by default in trackerlogic.h 54# opentracker takes its randomness from arc4random, which is part of libc on
46# on Linux systems you will need -lbds 55# the BSDs, on macOS and on glibc 2.36 and newer. On an older glibc or on musl
47#FEATURES+=-DWANT_ARC4RANDOM 56# it comes from libbsd instead, so uncomment the next include to link it.
57#LDFLAGS+=-lbsd
48 58
49#FEATURES+=-D_DEBUG_HTTPERROR 59#FEATURES+=-D_DEBUG_HTTPERROR
50#FEATURES+=-D_DEBUG_RANDOMTORRENTS 60#FEATURES+=-D_DEBUG_RANDOMTORRENTS
51 61
62GIT_VERSION=`sh -c 'command -v git >/dev/null && test -d .git && git rev-parse HEAD || echo _git_or_commit_not_found_'`
63
52OPTS_debug=-D_DEBUG -g -ggdb # -pg -fprofile-arcs -ftest-coverage 64OPTS_debug=-D_DEBUG -g -ggdb # -pg -fprofile-arcs -ftest-coverage
53OPTS_production=-O3 65OPTS_production=-O3
54 66
55CFLAGS+=-I$(LIBOWFAT_HEADERS) -Wall -pipe -pthread -Wextra #-ansi -pedantic 67CFLAGS+=-I$(LIBOWFAT_HEADERS) -DGIT_VERSION=$(GIT_VERSION) -Wall -pipe -pthread -Wextra #-ansi -pedantic
56LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread -lz 68LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread
57#LDFLAGS+=-lbsd
58 69
59BINARY =opentracker 70BINARY =opentracker
60HEADERS=trackerlogic.h scan_urlencoded_query.h ot_mutex.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_hex.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
61SOURCES=opentracker.c trackerlogic.c scan_urlencoded_query.c ot_mutex.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_hex.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
62SOURCES_proxy=proxy.c ot_vector.c ot_mutex.c 73SOURCES_proxy=proxy.c ot_vector.c ot_mutex.c ot_random.c
63 74
64OBJECTS = $(SOURCES:%.c=%.o) 75OBJECTS = $(SOURCES:%.c=%.o)
65OBJECTS_debug = $(SOURCES:%.c=%.debug.o) 76OBJECTS_debug = $(SOURCES:%.c=%.debug.o)
@@ -89,8 +100,29 @@ proxy.debug: $(OBJECTS_proxy_debug) $(HEADERS)
89.c.o : $(HEADERS) 100.c.o : $(HEADERS)
90 $(CC) -c -o $@ $(CFLAGS_production) $< 101 $(CC) -c -o $@ $(CFLAGS_production) $<
91 102
103# Unit tests. The modules under test are compiled straight into the test
104# binary, so a test may reach a static function by including the source if it
105# has to. tests/stubs.c supplies the few symbols those modules reference but
106# the tests never reach.
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
109TEST_UNITS=scan_urlencoded_query.c ot_iovec.c ot_vector.c ot_random.c ot_hex.c ot_accesslist.c
110TEST_HEADERS=tests/test.h $(HEADERS)
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.
113FEATURES_test=$(FEATURES) -DWANT_ACCESSLIST_BLACK
114CFLAGS_test = $(CFLAGS) -I. -g -O1 $(FEATURES_test)
115
116$(TESTBIN): $(TEST_SOURCES) $(TEST_UNITS) $(TEST_HEADERS)
117 $(CC) -o $@ $(CFLAGS_test) $(TEST_SOURCES) $(TEST_UNITS)
118
119check: $(TESTBIN)
120 ./$(TESTBIN)
121
122.PHONY: all check clean install
123
92clean: 124clean:
93 rm -rf opentracker opentracker.debug *.o *~ 125 rm -rf opentracker opentracker.debug $(TESTBIN) *.o *~
94 126
95install: 127install:
96 install -m 755 opentracker $(DESTDIR)$(BINDIR) 128 install -m 755 opentracker $(DESTDIR)$(BINDIR)
diff --git a/Makefile.gzip b/Makefile.gzip
new file mode 100644
index 0000000..70d6d62
--- /dev/null
+++ b/Makefile.gzip
@@ -0,0 +1,4 @@
1FEATURES+=-DWANT_COMPRESSION_GZIP
2#FEATURES+=-DWANT_COMPRESSION_GZIP_ALWAYS
3
4LDFLAGS+=-lz
diff --git a/Makefile.zstd b/Makefile.zstd
new file mode 100644
index 0000000..2bb56be
--- /dev/null
+++ b/Makefile.zstd
@@ -0,0 +1,3 @@
1FEATURES+=-DWANT_COMPRESSION_ZSTD
2#FEATURES+=-DWANT_COMPRESSION_ZSTD_ALWAYS
3LDFLAGS+=-lzstd
diff --git a/man1/opentracker.1 b/man1/opentracker.1
new file mode 100644
index 0000000..0103ebe
--- /dev/null
+++ b/man1/opentracker.1
@@ -0,0 +1,130 @@
1.Dd 15/4/2024
2.Dt opentracker 1
3.Os Unix
4.Sh opentracker
5.Nm opentracker
6.Nd a free and open bittorrent tracker
7.Sh SYNOPSIS
8.Nm
9.Op Fl f Ar config
10.Op Fl i Ar ip-select
11.Op Fl p Ar port-bind-tcp
12.Op Fl P Ar port-bind-udp
13.Op Fl A Ar blessed-ip
14.Op Fl r Ar redirect-url
15.Op Fl d Ar chdir
16.Op Fl u Ar user
17.Op Fl w| Fl b accesslist
18.Sh DESCRIPTION
19.Nm
20is a bittorrent tracker that implements announce and scrape actions over the
21UDP and the plain http protocol, aiming for minimal resource usage.
22.Pp
23When invoked with parameters, it binds to TCP and UDP port 6969 on all
24interfaces. The recommended way to configure opentracker is by providing a
25config file using the
26.Op Fl f Ar config
27option. See
28.Xr opentracker.conf 4
29for details.
30.Pp
31.Sh OPTIONS
32The following options are available:
33.Bl -tag -width -indent=8
34.It Fl f Ar config
35Parse a config file with a list of options. Consecutive command options
36will override options from the config file. See
37.Xr opentracker.conf 4
38for details.
39.It Fl i Ar ip-select
40Select an ip address that will be used with the next
41.Op Fl p
42or
43.Op Fl P
44command to actually bind to this address. Setting this option without any bind
45options in the config file or
46.Op Fl p
47or
48.Op Fl P
49commands will limit opentracker to only bind to this address.
50.It Fl p Ar port-bind-tcp
51Bind to the TCP port on the last preceding ip address set with the
52.Op Fl i ip-select
53option or to all available addresses if none has been set. Can be given multiple
54times.
55.It Fl P Ar port-bind-udp
56Bind to the UDP port on the last preceding ip address set with the
57.Op Fl i ip-select
58option or to all available addresses if none has been set. Can be given multiple
59times.
60.It Fl A Ar blessed-ip
61Set an ip address in IPv4 or IPv6 or a net in CIDR notation to bless the network
62for access to restricted resources.
63.It Fl r Ar redirect-url
64Set the URL that
65.Nm
66will redirect users to when the / address is requested via HTTP.
67.It Fl d Ar chdir
68Sets the directory
69.Nm
70will
71.Xr chroot 2
72to if ran as root or
73.Xr chdir 2
74to if ran as unprivileged user. Note that any accesslist files need to be
75relative to and within that directory.
76.It Fl u Ar user
77User to run
78.Nm
79under after all operations that need privileges have finished.
80.It Fl w Ar accesslist | Fl b Ar accesslist
81If
82.Nm
83has been compiled with the
84.B WANT_ACCESSLIST_BLACK
85or
86.Br WANT_ACCESSLIST_WHITE
87options, this option sets the location of the accesslist.
88.El
89.Sh EXAMPLES
90Start
91.Nm
92bound on UDP and TCP ports 6969 on IPv6 localhost.
93.Dl # ./opentracker -i ::1 -p 6969 -P 6969
94.Pp
95Start
96.Nm
97bound on UDP port 6868 and TCP port 6868 on IPv4 localhost and allow
98privileged access from the network 192.168/16 while redirecting
99HTTP clients accessing the root directory, which is not covered by the
100bittorrent tracker protocol, to https://my-trackersite.com/.
101.Dl # ./opentracker -i 192.168.0.4 -p 6868 -P 6969 -A 192.168/16 -r https://my-trackersite.com/
102The announce URLs are http://192.168.0.4:6868/announce and
103udp://192.168.0.4:6868/announce respectively.
104.Sh FILES
105.Bl -tag -width indent
106.It Pa opentracker.conf
107The
108.Nm
109config file.
110.El
111.Sh SEE ALSO
112.Xr opentracker.conf 4
113.Pp
114opentracker documentation
115.Lk https://erdgeist.org/arts/software/opentracker
116.Pp
117Bittorrent tracker protocol
118.Lk http://www.bittorrent.org/beps/bep_0015.html
119.Sh AUTHOR
120.An Dirk Engling
121.Aq Mt erdgeist@erdgeist.org .
122.Sh LICENSE
123This software is released under the Beerware License:
124.Pp
125Permission is hereby granted, free of charge, to any person obtaining a copy of this software
126and associated documentation files (the "Software"), to deal in the Software with the following
127terms and conditions:
128.Pp
129If you meet the author(s) someday, and you think this software is worth it, you can buy them
130a beer in return.
diff --git a/man4/opentracker.conf.4 b/man4/opentracker.conf.4
new file mode 100644
index 0000000..2bc1389
--- /dev/null
+++ b/man4/opentracker.conf.4
@@ -0,0 +1,86 @@
1.Dd 2024-04-18
2.Dt opentracker.conf 5
3.Os Unix
4.Sh NAME
5.Nm opentracker.conf
6.Nd configuration file for opentracker
7.Sh SYNOPSIS
8.Nm
9.Sh DESCRIPTION
10The
11.Nm
12configuration file specifies various options for configuring the behavior of the opentracker program.
13.Pp
14Lines starting with '#' are comments and are ignored. Options are specified as 'keyword value' pairs.
15.Pp
16The following options are available:
17.Pp
18.Bl -tag -width ".It access.proxy" -compact
19.It listen.tcp_udp Ar address
20Specifies an address opentracker will listen on for both TCP and UDP connections. If none are specified, opentracker listens on 0.0.0.0:6969 by default. Can be added more than once.
21.Pp
22.It listen.tcp Ar address
23Specifies the address opentracker will listen on for TCP connections. Can be added more than once.
24.Pp
25.It listen.udp Ar address
26Specifies the address opentracker will listen on for UDP connections. Can be added more than once.
27.Pp
28.It listen.udp.workers Ar threads
29Specifies how many threads will be spawned to handle UDP connections. Defaults to 4.
30.Pp
31.It access.whitelist Ar path/to/whitelist
32Specifies the path to the whitelist file containing all torrent hashes that opentracker will serve. Use this option if opentracker runs in a non-open mode.
33.Pp
34.It access.blacklist Ar path/to/blacklist
35Specifies the path to the blacklist file containing all torrent hashes that opentracker will not serve. Use this option if opentracker was compiled to allow blacklisting.
36.Pp
37.It access.fifo_add Ar path/to/adder.fifo
38Specifies the path to the FIFO (named pipe) used for dynamic changesets to accesslists. Info hashes written to this FIFO will be added to the main accesslist file.
39.Pp
40.It access.fifo_delete Ar path/to/deleter.fifo
41Specifies the path to the FIFO (named pipe) used for dynamic changesets to accesslists. Info hashes written to this FIFO will be removed from the main accesslist file.
42.Pp
43.It access.stats Ar ip_address_or_network
44Specifies the IP address or network in CIDR notation allowed to fetch stats from opentracker.
45.Pp
46.It access.stats_path Ar path
47Specifies the path to the stats location. You can configure opentracker to appear anywhere on your tracker. Defaults to /stats.
48.Pp
49.It access.proxy Ar ip_address_or_network
50Specifies the IP address or network of the reverse proxies. Opentracker will take the X-Forwarded-For address instead of the source IP address. Can be added more than once.
51.Pp
52.It livesync.cluster.listen Ar ip_address:port
53Specifies the IP address and port opentracker will listen on for incoming live sync packets to keep a cluster of opentrackers synchronized.
54.Pp
55.It livesync.cluster.node_ip Ar ip_address
56Specifies one trusted IP address for sync between trackers running in a cluster. Can be added more than once.
57.Pp
58.It batchsync.cluster.admin_ip Ar ip_address
59Specifies the admin IP address for old-style (HTTP-based) asynchronous tracker syncing.
60.Pp
61.It tracker.rootdir Ar path
62Specifies the directory opentracker will chroot/chdir to. All black/white list files must be located in this directory.
63.Pp
64.It tracker.user Ar username
65Specifies the user opentracker will setuid to after binding to potentially privileged ports.
66.Pp
67.It tracker.redirect_url Ar URL
68Specifies the URL opentracker will redirect to in response to a "GET / HTTP" request.
69.El
70.Sh EXAMPLES
71To specify the address opentracker will listen on for both TCP and UDP connections:
72.Dl listen.tcp_udp 0.0.0.0:6969
73.Pp
74To specify the address opentracker will listen on for TCP connections:
75.Dl listen.tcp 0.0.0.0
76.Pp
77To specify the address opentracker will listen on for UDP connections:
78.Dl listen.udp 0.0.0.0:6969
79.Pp
80.Sh SEE ALSO
81.Xr opentracker 1
82.Pp
83.Sh AUTHOR
84.An Dirk Engling
85.Aq Mt erdgeist@erdgeist.org
86.Pp
diff --git a/opentracker.c b/opentracker.c
index 01cb501..f218589 100644
--- a/opentracker.c
+++ b/opentracker.c
@@ -5,7 +5,6 @@
5 $Id$ */ 5 $Id$ */
6 6
7/* System */ 7/* System */
8#include <arpa/inet.h>
9#include <ctype.h> 8#include <ctype.h>
10#include <errno.h> 9#include <errno.h>
11#include <pthread.h> 10#include <pthread.h>
@@ -21,7 +20,6 @@
21#endif 20#endif
22 21
23/* Libowfat */ 22/* Libowfat */
24#include "byte.h"
25#include "io.h" 23#include "io.h"
26#include "iob.h" 24#include "iob.h"
27#include "ip6.h" 25#include "ip6.h"
@@ -30,9 +28,11 @@
30 28
31/* Opentracker */ 29/* Opentracker */
32#include "ot_accesslist.h" 30#include "ot_accesslist.h"
31#include "ot_hex.h"
33#include "ot_http.h" 32#include "ot_http.h"
34#include "ot_livesync.h" 33#include "ot_livesync.h"
35#include "ot_mutex.h" 34#include "ot_mutex.h"
35#include "ot_random.h"
36#include "ot_stats.h" 36#include "ot_stats.h"
37#include "ot_udp.h" 37#include "ot_udp.h"
38#include "trackerlogic.h" 38#include "trackerlogic.h"
@@ -281,7 +281,8 @@ static void *server_mainloop(void *args) {
281 struct ot_workstruct ws; 281 struct ot_workstruct ws;
282 time_t next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL; 282 time_t next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL;
283 struct iovec *iovector; 283 struct iovec *iovector;
284 int iovec_entries, is_partial; 284 size_t iovec_entries;
285 int is_partial;
285 286
286 (void)args; 287 (void)args;
287 288
@@ -295,13 +296,7 @@ static void *server_mainloop(void *args) {
295 if (!ws.inbuf || !ws.outbuf) 296 if (!ws.inbuf || !ws.outbuf)
296 panic("Initializing worker failed"); 297 panic("Initializing worker failed");
297 298
298#ifdef WANT_ARC4RANDOM 299 random_bytes(ws.rand48_state, sizeof(ws.rand48_state));
299 arc4random_buf(&ws.rand48_state[0], 3 * sizeof(uint16_t));
300#else
301 ws.rand48_state[0] = (uint16_t)random();
302 ws.rand48_state[1] = (uint16_t)random();
303 ws.rand48_state[2] = (uint16_t)random();
304#endif
305 300
306 for (;;) { 301 for (;;) {
307 int64 sock; 302 int64 sock;
@@ -437,28 +432,102 @@ static int scan_ip6_net(const char *src, ot_net *net) {
437 return off + s - src; 432 return off + s - src;
438} 433}
439 434
435typedef enum {
436 CONFIG_UNKNOWN,
437 CONFIG_TRACKER_ROOTDIR,
438 CONFIG_TRACKER_USER,
439 CONFIG_LISTEN_TCP_UDP,
440 CONFIG_LISTEN_TCP,
441 CONFIG_LISTEN_UDP,
442 CONFIG_LISTEN_UDP_WORKERS,
443 CONFIG_ACCESS_WHITELIST,
444 CONFIG_ACCESS_BLACKLIST,
445 CONFIG_ACCESS_FIFO_ADD,
446 CONFIG_ACCESS_FIFO_DELETE,
447 CONFIG_ACCESS_STATS,
448 CONFIG_ACCESS_STATS_PATH,
449 CONFIG_ACCESS_PROXY,
450 CONFIG_TRACKER_REDIRECT_URL,
451 CONFIG_LIVESYNC_CLUSTER_NODE_IP,
452 CONFIG_LIVESYNC_CLUSTER_LISTEN,
453} config_kind;
454
455typedef struct {
456 const char *name;
457 size_t len;
458 config_kind kind;
459} config_key;
460
461#define CONFIG_KEY(s, k) { s, sizeof(s) - 1, k }
462
463static const config_key config_keys[] = {
464 CONFIG_KEY("tracker.rootdir", CONFIG_TRACKER_ROOTDIR),
465 CONFIG_KEY("tracker.user", CONFIG_TRACKER_USER),
466 CONFIG_KEY("listen.tcp_udp", CONFIG_LISTEN_TCP_UDP),
467 CONFIG_KEY("listen.tcp", CONFIG_LISTEN_TCP),
468 CONFIG_KEY("listen.udp", CONFIG_LISTEN_UDP),
469 CONFIG_KEY("listen.udp.workers", CONFIG_LISTEN_UDP_WORKERS),
470#ifdef WANT_ACCESSLIST_WHITE
471 CONFIG_KEY("access.whitelist", CONFIG_ACCESS_WHITELIST),
472#elif defined(WANT_ACCESSLIST_BLACK)
473 CONFIG_KEY("access.blacklist", CONFIG_ACCESS_BLACKLIST),
474#endif
475#ifdef WANT_DYNAMIC_ACCESSLIST
476 CONFIG_KEY("access.fifo_add", CONFIG_ACCESS_FIFO_ADD),
477 CONFIG_KEY("access.fifo_delete", CONFIG_ACCESS_FIFO_DELETE),
478#endif
479#ifdef WANT_RESTRICT_STATS
480 CONFIG_KEY("access.stats", CONFIG_ACCESS_STATS),
481#endif
482 CONFIG_KEY("access.stats_path", CONFIG_ACCESS_STATS_PATH),
483#ifdef WANT_IP_FROM_PROXY
484 CONFIG_KEY("access.proxy", CONFIG_ACCESS_PROXY),
485#endif
486 CONFIG_KEY("tracker.redirect_url", CONFIG_TRACKER_REDIRECT_URL),
487#ifdef WANT_SYNC_LIVE
488 CONFIG_KEY("livesync.cluster.node_ip", CONFIG_LIVESYNC_CLUSTER_NODE_IP),
489 CONFIG_KEY("livesync.cluster.listen", CONFIG_LIVESYNC_CLUSTER_LISTEN),
490#endif
491};
492static const size_t config_entry_count = sizeof(config_keys) / sizeof(config_keys[0]);
493
494static config_kind parse_config_key(char **p) {
495 size_t i;
496 for (i = 0; i < config_entry_count; ++i) {
497 const config_key *key = &config_keys[i];
498 if (strncmp(*p, key->name, key->len) == 0 && isspace((unsigned char)(*p)[key->len])) {
499 *p += key->len + 1;
500 /* Skip white spaces */
501 while (isspace((unsigned char)**p))
502 ++(*p);
503 return key->kind;
504 }
505 }
506 return CONFIG_UNKNOWN;
507}
508
440int parse_configfile(char *config_filename) { 509int parse_configfile(char *config_filename) {
441 FILE *accesslist_filehandle; 510 FILE *config_file_handle;
442 char inbuf[512]; 511 char inbuf[512];
443 ot_ip6 tmpip; 512 ot_ip6 tmpip;
444#if defined(WANT_RESTRICT_STATS) || defined(WANT_IP_FROM_PROXY) || defined(WANT_SYNC_LIVE) 513#if defined(WANT_RESTRICT_STATS) || defined(WANT_IP_FROM_PROXY) || defined(WANT_SYNC_LIVE)
445 ot_net tmpnet; 514 ot_net tmpnet;
446#endif 515#endif
447 int bound = 0; 516 int bound = 0;
448 517
449 accesslist_filehandle = fopen(config_filename, "r"); 518 config_file_handle = fopen(config_filename, "r");
450 519
451 if (accesslist_filehandle == NULL) { 520 if (config_file_handle == NULL) {
452 fprintf(stderr, "Warning: Can't open config file: %s.", config_filename); 521 fprintf(stderr, "Warning: Can't open config file: %s.", config_filename);
453 return 0; 522 return 0;
454 } 523 }
455 524
456 while (fgets(inbuf, sizeof(inbuf), accesslist_filehandle)) { 525 while (fgets(inbuf, sizeof(inbuf), config_file_handle)) {
457 char *p = inbuf; 526 char *p = inbuf, *end;
458 size_t strl; 527 uint16_t tmpport = 6969;
459 528
460 /* Skip white spaces */ 529 /* Skip white spaces */
461 while (isspace(*p)) 530 while (isspace((unsigned char)*p))
462 ++p; 531 ++p;
463 532
464 /* Ignore comments and empty lines */ 533 /* Ignore comments and empty lines */
@@ -466,87 +535,98 @@ int parse_configfile(char *config_filename) {
466 continue; 535 continue;
467 536
468 /* consume trailing new lines and spaces */ 537 /* consume trailing new lines and spaces */
469 strl = strlen(p); 538 end = p + strlen(p);
470 while (strl && isspace(p[strl - 1])) 539 while (end > p && isspace((unsigned char)end[-1]))
471 p[--strl] = 0; 540 *--end = 0;
472 541
473 /* Scan for commands */ 542 /* Scan for commands */
474 if (!byte_diff(p, 15, "tracker.rootdir") && isspace(p[15])) { 543 switch (parse_config_key(&p)) {
475 set_config_option(&g_serverdir, p + 16); 544 case CONFIG_TRACKER_ROOTDIR:
476 } else if (!byte_diff(p, 12, "tracker.user") && isspace(p[12])) { 545 set_config_option(&g_serverdir, p);
477 set_config_option(&g_serveruser, p + 13); 546 break;
478 } else if (!byte_diff(p, 14, "listen.tcp_udp") && isspace(p[14])) { 547 case CONFIG_TRACKER_USER:
479 uint16_t tmpport = 6969; 548 set_config_option(&g_serveruser, p);
480 if (!scan_ip6_port(p + 15, tmpip, &tmpport)) 549 break;
481 goto parse_error; 550 case CONFIG_LISTEN_TCP_UDP:
482 ot_try_bind(tmpip, tmpport, FLAG_TCP); 551 if (!scan_ip6_port(p, tmpip, &tmpport))
483 ++bound; 552 goto parse_error;
484 ot_try_bind(tmpip, tmpport, FLAG_UDP); 553 ot_try_bind(tmpip, tmpport, FLAG_TCP);
485 ++bound; 554 ++bound;
486 } else if (!byte_diff(p, 10, "listen.tcp") && isspace(p[10])) { 555 ot_try_bind(tmpip, tmpport, FLAG_UDP);
487 uint16_t tmpport = 6969; 556 ++bound;
488 if (!scan_ip6_port(p + 11, tmpip, &tmpport)) 557 break;
489 goto parse_error; 558 case CONFIG_LISTEN_TCP:
490 ot_try_bind(tmpip, tmpport, FLAG_TCP); 559 if (!scan_ip6_port(p, tmpip, &tmpport))
491 ++bound; 560 goto parse_error;
492 } else if (!byte_diff(p, 10, "listen.udp") && isspace(p[10])) { 561 ot_try_bind(tmpip, tmpport, FLAG_TCP);
493 uint16_t tmpport = 6969; 562 ++bound;
494 if (!scan_ip6_port(p + 11, tmpip, &tmpport)) 563 break;
495 goto parse_error; 564 case CONFIG_LISTEN_UDP:
496 ot_try_bind(tmpip, tmpport, FLAG_UDP); 565 if (!scan_ip6_port(p, tmpip, &tmpport))
497 ++bound; 566 goto parse_error;
498 } else if (!byte_diff(p, 18, "listen.udp.workers") && isspace(p[18])) { 567 ot_try_bind(tmpip, tmpport, FLAG_UDP);
499 char *value = p + 18; 568 ++bound;
500 while (isspace(*value)) 569 case CONFIG_LISTEN_UDP_WORKERS:
501 ++value; 570 scan_uint(p, &g_udp_workers);
502 scan_uint(value, &g_udp_workers); 571 break;
503#ifdef WANT_ACCESSLIST_WHITE 572#ifdef WANT_ACCESSLIST_WHITE
504 } else if (!byte_diff(p, 16, "access.whitelist") && isspace(p[16])) { 573 case CONFIG_ACCESS_WHITELIST:
505 set_config_option(&g_accesslist_filename, p + 17); 574 set_config_option(&g_accesslist_filename, p);
575 break;
506#elif defined(WANT_ACCESSLIST_BLACK) 576#elif defined(WANT_ACCESSLIST_BLACK)
507 } else if (!byte_diff(p, 16, "access.blacklist") && isspace(p[16])) { 577 case CONFIG_ACCESS_BLACKLIST:
508 set_config_option(&g_accesslist_filename, p + 17); 578 set_config_option(&g_accesslist_filename, p);
579 break;
509#endif 580#endif
510#ifdef WANT_DYNAMIC_ACCESSLIST 581#ifdef WANT_DYNAMIC_ACCESSLIST
511 } else if (!byte_diff(p, 15, "access.fifo_add") && isspace(p[15])) { 582 case CONFIG_ACCESS_FIFO_ADD:
512 set_config_option(&g_accesslist_pipe_add, p + 16); 583 set_config_option(&g_accesslist_pipe_add, p);
513 } else if (!byte_diff(p, 18, "access.fifo_delete") && isspace(p[18])) { 584 break;
514 set_config_option(&g_accesslist_pipe_delete, p + 19); 585 case CONFIG_ACCESS_FIFO_DELETE:
586 set_config_option(&g_accesslist_pipe_delete, p);
587 break;
515#endif 588#endif
516#ifdef WANT_RESTRICT_STATS 589#ifdef WANT_RESTRICT_STATS
517 } else if (!byte_diff(p, 12, "access.stats") && isspace(p[12])) { 590 case CONFIG_ACCESS_STATS:
518 if (!scan_ip6_net(p + 13, &tmpnet)) 591 if (!scan_ip6_net(p, &tmpnet))
519 goto parse_error; 592 goto parse_error;
520 accesslist_bless_net(&tmpnet, OT_PERMISSION_MAY_STAT); 593 accesslist_bless_net(&tmpnet, OT_PERMISSION_MAY_STAT);
594 break;
521#endif 595#endif
522 } else if (!byte_diff(p, 17, "access.stats_path") && isspace(p[17])) { 596 case CONFIG_ACCESS_STATS_PATH:
523 set_config_option(&g_stats_path, p + 18); 597 set_config_option(&g_stats_path, p);
598 break;
524#ifdef WANT_IP_FROM_PROXY 599#ifdef WANT_IP_FROM_PROXY
525 } else if (!byte_diff(p, 12, "access.proxy") && isspace(p[12])) { 600 case CONFIG_ACCESS_PROXY:
526 if (!scan_ip6_net(p + 13, &tmpnet)) 601 if (!scan_ip6_net(p, &tmpnet))
527 goto parse_error; 602 goto parse_error;
528 accesslist_bless_net(&tmpnet, OT_PERMISSION_MAY_PROXY); 603 accesslist_bless_net(&tmpnet, OT_PERMISSION_MAY_PROXY);
604 break;
529#endif 605#endif
530 } else if (!byte_diff(p, 20, "tracker.redirect_url") && isspace(p[20])) { 606 case CONFIG_TRACKER_REDIRECT_URL:
531 set_config_option(&g_redirecturl, p + 21); 607 set_config_option(&g_redirecturl, p);
608 break;
532#ifdef WANT_SYNC_LIVE 609#ifdef WANT_SYNC_LIVE
533 } else if (!byte_diff(p, 24, "livesync.cluster.node_ip") && isspace(p[24])) { 610 case CONFIG_LIVESYNC_CLUSTER_NODE_IP:
534 if (!scan_ip6_net(p + 25, &tmpnet)) 611 if (!scan_ip6_net(p, &tmpnet))
535 goto parse_error; 612 goto parse_error;
536 accesslist_bless_net(&tmpnet, OT_PERMISSION_MAY_LIVESYNC); 613 accesslist_bless_net(&tmpnet, OT_PERMISSION_MAY_LIVESYNC);
537 } else if (!byte_diff(p, 23, "livesync.cluster.listen") && isspace(p[23])) { 614 break;
538 uint16_t tmpport = LIVESYNC_PORT; 615 case CONFIG_LIVESYNC_CLUSTER_LISTEN:
539 if (!scan_ip6_port(p + 24, tmpip, &tmpport)) 616 tmpport = LIVESYNC_PORT;
540 goto parse_error; 617 if (!scan_ip6_port(p, tmpip, &tmpport))
541 livesync_bind_mcast(tmpip, tmpport); 618 goto parse_error;
619 livesync_bind_mcast(tmpip, tmpport);
620 break;
542#endif 621#endif
543 } else 622 default:
544 fprintf(stderr, "Unhandled line in config file: %s\n", inbuf); 623 fprintf(stderr, "Unhandled line in config file: %s\n", inbuf);
545 continue; 624 break;
546 parse_error: 625 parse_error:
547 fprintf(stderr, "Parse error in config file: %s\n", inbuf); 626 fprintf(stderr, "Parse error in config file: %s\n", inbuf);
627 }
548 } 628 }
549 fclose(accesslist_filehandle); 629 fclose(config_file_handle);
550 return bound; 630 return bound;
551} 631}
552 632
@@ -566,17 +646,11 @@ void load_state(const char *const state_filename) {
566 646
567 /* We do ignore anything that is not of the form "^[:xdigit:]:\d+:\d+" */ 647 /* We do ignore anything that is not of the form "^[:xdigit:]:\d+:\d+" */
568 while (fgets(inbuf, sizeof(inbuf), state_filehandle)) { 648 while (fgets(inbuf, sizeof(inbuf), state_filehandle)) {
569 int i; 649 size_t i;
570 for (i = 0; i < (int)sizeof(ot_hash); ++i) {
571 int eger = 16 * scan_fromhex(inbuf[2 * i]) + scan_fromhex(inbuf[1 + 2 * i]);
572 if (eger < 0)
573 continue;
574 infohash[i] = eger;
575 }
576 650
577 if (i != (int)sizeof(ot_hash)) 651 if (!hex_tohash(infohash, inbuf))
578 continue; 652 continue;
579 i *= 2; 653 i = 2 * sizeof(ot_hash);
580 654
581 if (inbuf[i++] != ':' || !(consumed = scan_ulonglong(inbuf + i, &base))) 655 if (inbuf[i++] != ':' || !(consumed = scan_ulonglong(inbuf + i, &base)))
582 continue; 656 continue;
@@ -659,11 +733,7 @@ int main(int argc, char **argv) {
659 serverip[10] = serverip[11] = -1; 733 serverip[10] = serverip[11] = -1;
660#endif 734#endif
661 735
662#ifdef WANT_DEV_RANDOM 736 random_init();
663 srandomdev();
664#else
665 srandom(time(NULL));
666#endif
667 737
668 while (scanon) { 738 while (scanon) {
669 switch (getopt(argc, argv, 739 switch (getopt(argc, argv,
@@ -761,6 +831,8 @@ int main(int argc, char **argv) {
761 ot_try_bind(serverip, 6969, FLAG_UDP); 831 ot_try_bind(serverip, 6969, FLAG_UDP);
762 } 832 }
763 833
834 defaul_signal_handlers();
835
764#ifdef WANT_SYSLOGS 836#ifdef WANT_SYSLOGS
765 openlog("opentracker", 0, LOG_USER); 837 openlog("opentracker", 0, LOG_USER);
766 setlogmask(LOG_UPTO(LOG_INFO)); 838 setlogmask(LOG_UPTO(LOG_INFO));
@@ -783,7 +855,6 @@ int main(int argc, char **argv) {
783 io_setcookie(g_self_pipe[0], (void *)FLAG_SELFPIPE); 855 io_setcookie(g_self_pipe[0], (void *)FLAG_SELFPIPE);
784 io_wantread(g_self_pipe[0]); 856 io_wantread(g_self_pipe[0]);
785 857
786 defaul_signal_handlers();
787 /* Init all sub systems. This call may fail with an exit() */ 858 /* Init all sub systems. This call may fail with an exit() */
788 trackerlogic_init(); 859 trackerlogic_init();
789 860
@@ -805,5 +876,3 @@ int main(int argc, char **argv) {
805 876
806 return 0; 877 return 0;
807} 878}
808
809const char *g_version_opentracker_c = "$Source$: $Revision$\n";
diff --git a/opentracker.conf.sample b/opentracker.conf.sample
index f8bef43..054e405 100644
--- a/opentracker.conf.sample
+++ b/opentracker.conf.sample
@@ -2,7 +2,7 @@
2# 2#
3 3
4# I) Address opentracker will listen on, using both, tcp AND udp family 4# I) Address opentracker will listen on, using both, tcp AND udp family
5# (note, that port 6969 is implicite if ommitted). 5# (note, that port 6969 is implicit if omitted).
6# 6#
7# If no listen option is given (here or on the command line), opentracker 7# If no listen option is given (here or on the command line), opentracker
8# listens on 0.0.0.0:6969 tcp and udp. 8# listens on 0.0.0.0:6969 tcp and udp.
@@ -83,9 +83,10 @@
83# IIb) 83# IIb)
84# If you do not want to grant anyone access to your stats, enable the 84# If you do not want to grant anyone access to your stats, enable the
85# WANT_RESTRICT_STATS option in Makefile and bless the ip addresses 85# WANT_RESTRICT_STATS option in Makefile and bless the ip addresses
86# allowed to fetch stats here. 86# or network allowed to fetch stats here.
87# 87#
88# access.stats 192.168.0.23 88# access.stats 192.168.0.23
89# access.stats 10.1.1.23
89# 90#
90# There is another way of hiding your stats. You can obfuscate the path 91# There is another way of hiding your stats. You can obfuscate the path
91# to them. Normally it is located at /stats but you can configure it to 92# to them. Normally it is located at /stats but you can configure it to
diff --git a/ot_accesslist.c b/ot_accesslist.c
index c26e14a..9b0024c 100644
--- a/ot_accesslist.c
+++ b/ot_accesslist.c
@@ -17,14 +17,13 @@
17#endif 17#endif
18 18
19/* Libowfat */ 19/* Libowfat */
20#include "byte.h"
21#include "fmt.h" 20#include "fmt.h"
22#include "ip6.h" 21#include "ip6.h"
23#include "mmap.h" 22#include "mmap.h"
24#include "scan.h"
25 23
26/* Opentracker */ 24/* Opentracker */
27#include "ot_accesslist.h" 25#include "ot_accesslist.h"
26#include "ot_hex.h"
28#include "ot_vector.h" 27#include "ot_vector.h"
29#include "trackerlogic.h" 28#include "trackerlogic.h"
30 29
@@ -99,12 +98,33 @@ static void accesslist_clean(ot_accesslist *accesslist) {
99 } 98 }
100} 99}
101 100
102/* Read initial access list */ 101/* A hash needs forty bytes, and all but a final one are followed by at least
102 one separator, so n hashes need at least 41n - 1 bytes. (len + 1) / 41 is
103 therefore an exact upper bound on what a buffer of len bytes can hold. */
104size_t accesslist_parse_hashes(ot_hash *dest, size_t capacity, const char *buf, size_t len) {
105 const char *end = buf + len;
106 const char *offs = buf;
107 size_t count = 0;
108
109 while (count < capacity && end - offs >= 40) {
110 if (hex_tohash(dest[count], offs)) {
111 offs += 40;
112
113 if (offs == end || !hex_isdigit((unsigned char)*offs))
114 ++count;
115 }
116
117 /* Find start of next line */
118 while (offs < end && *(offs++) != '\n') {}
119 }
120
121 return count;
122}
123
103static void accesslist_readfile(void) { 124static void accesslist_readfile(void) {
104 ot_accesslist *accesslist_new; 125 ot_accesslist *accesslist_new;
105 ot_hash *info_hash; 126 const char *map;
106 const char *map, *map_end, *read_offs; 127 size_t maplen, capacity, found;
107 size_t maplen;
108 128
109 if ((map = mmap_read(g_accesslist_filename, &maplen)) == NULL) { 129 if ((map = mmap_read(g_accesslist_filename, &maplen)) == NULL) {
110 char *wd = getcwd(NULL, 0); 130 char *wd = getcwd(NULL, 0);
@@ -113,51 +133,24 @@ static void accesslist_readfile(void) {
113 return; 133 return;
114 } 134 }
115 135
116 /* You need at least 41 bytes to pass an info_hash, make enough room 136 /* Make room for the most info_hashes this file could possibly hold */
117 for the maximum amount of them */ 137 capacity = (maplen + 1) / 41;
118 accesslist_new = accesslist_make(g_accesslist, maplen / 41); 138 accesslist_new = accesslist_make(g_accesslist, capacity);
119 if (!accesslist_new) { 139 if (!accesslist_new) {
120 fprintf(stderr, "Warning: Not enough memory to allocate %zd bytes for accesslist buffer. May succeed later.\n", (maplen / 41) * 20); 140 fprintf(stderr, "Warning: Not enough memory to allocate %zd bytes for accesslist buffer. May succeed later.\n", capacity * sizeof(ot_hash));
121 mmap_unmap(map, maplen); 141 mmap_unmap(map, maplen);
122 return; 142 return;
123 } 143 }
124 info_hash = accesslist_new->list;
125 144
126 /* No use to scan if there's not enough room for another full info_hash */ 145 found = accesslist_parse_hashes(accesslist_new->list, capacity, map, maplen);
127 map_end = map + maplen - 40;
128 read_offs = map;
129
130 /* We do ignore anything that is not of the form "^[:xdigit:]{40}[^:xdigit:].*" */
131 while (read_offs <= map_end) {
132 int i;
133 for (i = 0; i < (int)sizeof(ot_hash); ++i) {
134 int eger1 = scan_fromhex((unsigned char)read_offs[2 * i]);
135 int eger2 = scan_fromhex((unsigned char)read_offs[1 + 2 * i]);
136 if (eger1 < 0 || eger2 < 0)
137 break;
138 (*info_hash)[i] = (uint8_t)(eger1 * 16 + eger2);
139 }
140
141 if (i == sizeof(ot_hash)) {
142 read_offs += 40;
143
144 /* Append accesslist to accesslist vector */
145 if (read_offs == map_end || scan_fromhex((unsigned char)*read_offs) < 0)
146 ++info_hash;
147 }
148
149 /* Find start of next line */
150 while (read_offs <= map_end && *(read_offs++) != '\n')
151 ;
152 }
153#ifdef _DEBUG 146#ifdef _DEBUG
154 fprintf(stderr, "Added %zd info_hashes to accesslist\n", (size_t)(info_hash - accesslist_new->list)); 147 fprintf(stderr, "Added %zd info_hashes to accesslist\n", found);
155#endif 148#endif
156 149
157 mmap_unmap(map, maplen); 150 mmap_unmap(map, maplen);
158 151
159 qsort(accesslist_new->list, info_hash - accesslist_new->list, sizeof(*info_hash), vector_compare_hash); 152 qsort(accesslist_new->list, found, sizeof(ot_hash), vector_compare_hash);
160 accesslist_new->size = info_hash - accesslist_new->list; 153 accesslist_new->size = found;
161 154
162 /* Now exchange the accesslist vector in the least race condition prone way */ 155 /* Now exchange the accesslist vector in the least race condition prone way */
163 pthread_mutex_lock(&g_accesslist_mutex); 156 pthread_mutex_lock(&g_accesslist_mutex);
@@ -197,7 +190,7 @@ int accesslist_hashisvalid(ot_hash hash) {
197 190
198 /* If we found a matching hash on the main list, scan the list of dynamically deleted hashes */ 191 /* If we found a matching hash on the main list, scan the list of dynamically deleted hashes */
199 accesslist_delete = g_accesslist_delete; 192 accesslist_delete = g_accesslist_delete;
200 if ((exactmatch != NULL) && accesslist_delete && bsearch(hash, accesslist_add->list, accesslist_add->size, OT_HASH_COMPARE_SIZE, vector_compare_hash)) 193 if ((exactmatch != NULL) && accesslist_delete && bsearch(hash, accesslist_delete->list, accesslist_delete->size, OT_HASH_COMPARE_SIZE, vector_compare_hash))
201 exactmatch = NULL; 194 exactmatch = NULL;
202#endif 195#endif
203 196
@@ -262,7 +255,6 @@ static void *accesslist_adddel_worker(char *fifoname, ot_accesslist *_Atomic
262 255
263 while ((linelen = getline(&line, &linecap, fifo)) > 0) { 256 while ((linelen = getline(&line, &linecap, fifo)) > 0) {
264 ot_hash info_hash; 257 ot_hash info_hash;
265 int i;
266 258
267 printf("Got line %*s", (int)linelen, line); 259 printf("Got line %*s", (int)linelen, line);
268 /* We do ignore anything that is not of the form "^[:xdigit:]{40}[^:xdigit:].*" 260 /* We do ignore anything that is not of the form "^[:xdigit:]{40}[^:xdigit:].*"
@@ -270,15 +262,7 @@ static void *accesslist_adddel_worker(char *fifoname, ot_accesslist *_Atomic
270 if (linelen < 41) 262 if (linelen < 41)
271 continue; 263 continue;
272 264
273 for (i = 0; i < (int)sizeof(ot_hash); ++i) { 265 if (!hex_tohash(info_hash, line))
274 int eger1 = scan_fromhex((unsigned char)line[2 * i]);
275 int eger2 = scan_fromhex((unsigned char)line[1 + 2 * i]);
276 if (eger1 < 0 || eger2 < 0)
277 break;
278 ((uint8_t *)info_hash)[i] = (uint8_t)(eger1 * 16 + eger2);
279 }
280 printf("parsed info_hash %20s\n", info_hash);
281 if (i != sizeof(ot_hash))
282 continue; 266 continue;
283 267
284 /* From now on we modify g_accesslist_add and g_accesslist_delete, so prevent the 268 /* From now on we modify g_accesslist_add and g_accesslist_delete, so prevent the
@@ -342,7 +326,7 @@ static void *accesslist_deleter_worker(void *args) {
342#endif 326#endif
343 327
344static pthread_t thread_id; 328static pthread_t thread_id;
345void accesslist_init() { 329void accesslist_init(void) {
346 pthread_mutex_init(&g_accesslist_mutex, NULL); 330 pthread_mutex_init(&g_accesslist_mutex, NULL);
347 pthread_create(&thread_id, NULL, accesslist_worker, NULL); 331 pthread_create(&thread_id, NULL, accesslist_worker, NULL);
348#ifdef WANT_DYNAMIC_ACCESSLIST 332#ifdef WANT_DYNAMIC_ACCESSLIST
@@ -444,7 +428,7 @@ void loglist_add_network(const ot_net *net) {
444 pthread_mutex_unlock(&g_lognets_list_mutex); 428 pthread_mutex_unlock(&g_lognets_list_mutex);
445} 429}
446 430
447void loglist_reset() { 431void loglist_reset(void) {
448 pthread_mutex_lock(&g_lognets_list_mutex); 432 pthread_mutex_lock(&g_lognets_list_mutex);
449 free(g_lognets_list.data); 433 free(g_lognets_list.data);
450 g_lognets_list.data = 0; 434 g_lognets_list.data = 0;
@@ -511,7 +495,7 @@ int proxylist_check_proxy(const ot_ip6 proxy, const ot_ip6 address) {
511 495
512static ot_net g_admin_nets[OT_ADMINIP_MAX]; 496static ot_net g_admin_nets[OT_ADMINIP_MAX];
513static ot_permissions g_admin_nets_permissions[OT_ADMINIP_MAX]; 497static ot_permissions g_admin_nets_permissions[OT_ADMINIP_MAX];
514static unsigned int g_admin_nets_count = 0; 498static size_t g_admin_nets_count = 0;
515 499
516int accesslist_bless_net(ot_net *net, ot_permissions permissions) { 500int accesslist_bless_net(ot_net *net, ot_permissions permissions) {
517 if (g_admin_nets_count >= OT_ADMINIP_MAX) 501 if (g_admin_nets_count >= OT_ADMINIP_MAX)
@@ -553,11 +537,9 @@ int accesslist_bless_net(ot_net *net, ot_permissions permissions) {
553} 537}
554 538
555int accesslist_is_blessed(ot_ip6 ip, ot_permissions permissions) { 539int accesslist_is_blessed(ot_ip6 ip, ot_permissions permissions) {
556 unsigned int i; 540 size_t i;
557 for (i = 0; i < g_admin_nets_count; ++i) 541 for (i = 0; i < g_admin_nets_count; ++i)
558 if (address_in_net(ip, g_admin_nets + i) && (g_admin_nets_permissions[i] & permissions)) 542 if (address_in_net(ip, g_admin_nets + i) && (g_admin_nets_permissions[i] & permissions))
559 return 1; 543 return 1;
560 return 0; 544 return 0;
561} 545}
562
563const char *g_version_accesslist_c = "$Source$: $Revision$\n";
diff --git a/ot_accesslist.h b/ot_accesslist.h
index 0a7488e..b3e979d 100644
--- a/ot_accesslist.h
+++ b/ot_accesslist.h
@@ -19,6 +19,10 @@ void accesslist_deinit(void);
19int accesslist_hashisvalid(ot_hash hash); 19int accesslist_hashisvalid(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
23 line, and return how many were written to dest */
24size_t accesslist_parse_hashes(ot_hash *dest, size_t capacity, const char *buf, size_t len);
25
22extern char *g_accesslist_filename; 26extern char *g_accesslist_filename;
23#ifdef WANT_DYNAMIC_ACCESSLIST 27#ifdef WANT_DYNAMIC_ACCESSLIST
24extern char *g_accesslist_pipe_add; 28extern char *g_accesslist_pipe_add;
@@ -72,7 +76,7 @@ struct ot_log {
72extern ot_log *g_logchain_first, *g_logchain_last; 76extern ot_log *g_logchain_first, *g_logchain_last;
73 77
74void loglist_add_network(const ot_net *net); 78void loglist_add_network(const ot_net *net);
75void loglist_reset(); 79void loglist_reset(void);
76int loglist_check_address(const ot_ip6 address); 80int loglist_check_address(const ot_ip6 address);
77#endif 81#endif
78 82
diff --git a/ot_clean.c b/ot_clean.c
index 2506cc1..825290d 100644
--- a/ot_clean.c
+++ b/ot_clean.c
@@ -20,7 +20,7 @@
20#include "trackerlogic.h" 20#include "trackerlogic.h"
21 21
22/* Returns amount of removed peers */ 22/* Returns amount of removed peers */
23static ssize_t clean_single_bucket(ot_peer *peers, size_t peer_count, size_t peer_size, time_t timedout, int *removed_seeders) { 23static ssize_t clean_single_bucket(ot_peer *peers, size_t peer_count, size_t peer_size, time_t timedout, size_t *removed_seeders) {
24 ot_peer *last_peer = peers + peer_count * peer_size, *insert_point; 24 ot_peer *last_peer = peers + peer_count * peer_size, *insert_point;
25 25
26 /* Two scan modes: unless there is one peer removed, just increase ot_peertime */ 26 /* Two scan modes: unless there is one peer removed, just increase ot_peertime */
@@ -50,7 +50,7 @@ static ssize_t clean_single_bucket(ot_peer *peers, size_t peer_count, size_t pee
50int clean_single_peer_list(ot_peerlist *peer_list, size_t peer_size) { 50int clean_single_peer_list(ot_peerlist *peer_list, size_t peer_size) {
51 ot_vector *peer_vector = &peer_list->peers; 51 ot_vector *peer_vector = &peer_list->peers;
52 time_t timedout = (time_t)(g_now_minutes - peer_list->base); 52 time_t timedout = (time_t)(g_now_minutes - peer_list->base);
53 int num_buckets = 1, removed_seeders = 0; 53 size_t num_buckets = 1, removed_seeders = 0;
54 54
55 /* No need to clean empty torrent */ 55 /* No need to clean empty torrent */
56 if (!timedout) 56 if (!timedout)
@@ -111,7 +111,7 @@ int clean_single_torrent(ot_torrent *torrent) {
111static void *clean_worker(void *args) { 111static void *clean_worker(void *args) {
112 (void)args; 112 (void)args;
113 while (1) { 113 while (1) {
114 int bucket = OT_BUCKET_COUNT; 114 size_t bucket = OT_BUCKET_COUNT;
115 while (bucket--) { 115 while (bucket--) {
116 ot_vector *torrents_list = mutex_bucket_lock(bucket); 116 ot_vector *torrents_list = mutex_bucket_lock(bucket);
117 size_t toffs; 117 size_t toffs;
@@ -142,5 +142,3 @@ static pthread_t thread_id;
142void clean_init(void) { pthread_create(&thread_id, NULL, clean_worker, NULL); } 142void clean_init(void) { pthread_create(&thread_id, NULL, clean_worker, NULL); }
143 143
144void clean_deinit(void) { pthread_cancel(thread_id); } 144void clean_deinit(void) { pthread_cancel(thread_id); }
145
146const char *g_version_clean_c = "$Source$: $Revision$\n";
diff --git a/ot_fullscrape.c b/ot_fullscrape.c
index d299ed9..08970a6 100644
--- a/ot_fullscrape.c
+++ b/ot_fullscrape.c
@@ -10,17 +10,20 @@
10#include <pthread.h> 10#include <pthread.h>
11#include <stdio.h> 11#include <stdio.h>
12#include <string.h> 12#include <string.h>
13#include <sys/param.h>
14#ifdef WANT_COMPRESSION_GZIP 13#ifdef WANT_COMPRESSION_GZIP
15#include <zlib.h> 14#include <zlib.h>
16#endif 15#endif
16#ifdef WANT_COMPRESSION_ZSTD
17#include <zstd.h>
18#endif
19
17 20
18/* Libowfat */ 21/* Libowfat */
19#include "byte.h"
20#include "io.h" 22#include "io.h"
21#include "textcode.h" 23#include "textcode.h"
22 24
23/* Opentracker */ 25/* Opentracker */
26#include "ot_hex.h"
24#include "ot_fullscrape.h" 27#include "ot_fullscrape.h"
25#include "ot_iovec.h" 28#include "ot_iovec.h"
26#include "ot_mutex.h" 29#include "ot_mutex.h"
@@ -40,21 +43,12 @@ static void fullscrape_make(int taskid, ot_tasktype mode);
40#ifdef WANT_COMPRESSION_GZIP 43#ifdef WANT_COMPRESSION_GZIP
41static void fullscrape_make_gzip(int taskid, ot_tasktype mode); 44static void fullscrape_make_gzip(int taskid, ot_tasktype mode);
42#endif 45#endif
46#ifdef WANT_COMPRESSION_ZSTD
47static void fullscrape_make_zstd(int taskid, ot_tasktype mode);
48#endif
43 49
44/* Converter function from memory to human readable hex strings 50/* Converter function from memory to human readable hex strings
45 XXX - Duplicated from ot_stats. Needs fix. */ 51 XXX - Duplicated from ot_stats. Needs fix. */
46static char *to_hex(char *d, uint8_t *s) {
47 char *m = "0123456789ABCDEF";
48 char *t = d;
49 char *e = d + 40;
50 while (d < e) {
51 *d++ = m[*s >> 4];
52 *d++ = m[*s++ & 15];
53 }
54 *d = 0;
55 return t;
56}
57
58/* This is the entry point into this worker thread 52/* This is the entry point into this worker thread
59 It grabs tasks from mutex_tasklist and delivers results back 53 It grabs tasks from mutex_tasklist and delivers results back
60*/ 54*/
@@ -64,6 +58,11 @@ static void *fullscrape_worker(void *args) {
64 while (g_opentracker_running) { 58 while (g_opentracker_running) {
65 ot_tasktype tasktype = TASK_FULLSCRAPE; 59 ot_tasktype tasktype = TASK_FULLSCRAPE;
66 ot_taskid taskid = mutex_workqueue_poptask(&tasktype); 60 ot_taskid taskid = mutex_workqueue_poptask(&tasktype);
61#ifdef WANT_COMPRESSION_ZSTD
62 if (tasktype & TASK_FLAG_ZSTD)
63 fullscrape_make_zstd(taskid, tasktype);
64 else
65#endif
67#ifdef WANT_COMPRESSION_GZIP 66#ifdef WANT_COMPRESSION_GZIP
68 if (tasktype & TASK_FLAG_GZIP) 67 if (tasktype & TASK_FLAG_GZIP)
69 fullscrape_make_gzip(taskid, tasktype); 68 fullscrape_make_gzip(taskid, tasktype);
@@ -76,11 +75,11 @@ static void *fullscrape_worker(void *args) {
76} 75}
77 76
78static pthread_t thread_id; 77static pthread_t thread_id;
79void fullscrape_init( ) { 78void fullscrape_init(void) {
80 pthread_create( &thread_id, NULL, fullscrape_worker, NULL ); 79 pthread_create( &thread_id, NULL, fullscrape_worker, NULL );
81} 80}
82 81
83void fullscrape_deinit( ) { 82void fullscrape_deinit(void) {
84 pthread_cancel( thread_id ); 83 pthread_cancel( thread_id );
85} 84}
86 85
@@ -107,12 +106,12 @@ static char * fullscrape_write_one( ot_tasktype mode, char *r, ot_torrent *torre
107 106
108 break; 107 break;
109 case TASK_FULLSCRAPE_TPB_ASCII: 108 case TASK_FULLSCRAPE_TPB_ASCII:
110 to_hex(r, *hash); 109 hex_fromhash(r, *hash);
111 r += 2 * sizeof(ot_hash); 110 r += 2 * sizeof(ot_hash);
112 r += sprintf(r, ":%zd:%zd\n", seed_count, peer_count - seed_count); 111 r += sprintf(r, ":%zd:%zd\n", seed_count, peer_count - seed_count);
113 break; 112 break;
114 case TASK_FULLSCRAPE_TPB_ASCII_PLUS: 113 case TASK_FULLSCRAPE_TPB_ASCII_PLUS:
115 to_hex(r, *hash); 114 hex_fromhash(r, *hash);
116 r += 2 * sizeof(ot_hash); 115 r += 2 * sizeof(ot_hash);
117 r += sprintf(r, ":%zd:%zd:%zd\n", seed_count, peer_count - seed_count, down_count); 116 r += sprintf(r, ":%zd:%zd:%zd\n", seed_count, peer_count - seed_count, down_count);
118 break; 117 break;
@@ -128,7 +127,7 @@ static char * fullscrape_write_one( ot_tasktype mode, char *r, ot_torrent *torre
128 r += sprintf(r, ":%zd:%zd\n", seed_count, peer_count - seed_count); 127 r += sprintf(r, ":%zd:%zd\n", seed_count, peer_count - seed_count);
129 break; 128 break;
130 case TASK_FULLSCRAPE_TRACKERSTATE: 129 case TASK_FULLSCRAPE_TRACKERSTATE:
131 to_hex(r, *hash); 130 hex_fromhash(r, *hash);
132 r += 2 * sizeof(ot_hash); 131 r += 2 * sizeof(ot_hash);
133 r += sprintf(r, ":%zd:%zd\n", torrent->peer_list6->base, down_count); 132 r += sprintf(r, ":%zd:%zd\n", torrent->peer_list6->base, down_count);
134 break; 133 break;
@@ -137,8 +136,8 @@ static char * fullscrape_write_one( ot_tasktype mode, char *r, ot_torrent *torre
137} 136}
138 137
139static void fullscrape_make(int taskid, ot_tasktype mode) { 138static void fullscrape_make(int taskid, ot_tasktype mode) {
140 int bucket; 139 size_t bucket;
141 char *r, *re; 140 char *r, *re;
142 struct iovec iovector = {NULL, 0}; 141 struct iovec iovector = {NULL, 0};
143 142
144 /* Setup return vector... */ 143 /* Setup return vector... */
@@ -168,12 +167,15 @@ static void fullscrape_make(int taskid, ot_tasktype mode) {
168 167
169 if (mutex_workqueue_pushchunked(taskid, &iovector)) { 168 if (mutex_workqueue_pushchunked(taskid, &iovector)) {
170 free(iovector.iov_base); 169 free(iovector.iov_base);
171 return mutex_bucket_unlock(bucket, 0); 170 mutex_bucket_unlock(bucket, 0);
171 return;
172 } 172 }
173 /* Allocate a fresh output buffer */ 173 /* Allocate a fresh output buffer */
174 r = iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE); 174 r = iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE);
175 if (!r) 175 if (!r) {
176 return mutex_bucket_unlock(bucket, 0); 176 mutex_bucket_unlock(bucket, 0);
177 return;
178 }
177 179
178 /* re points to low watermark */ 180 /* re points to low watermark */
179 re = r + OT_SCRAPE_CHUNK_SIZE - OT_SCRAPE_MAXENTRYLEN; 181 re = r + OT_SCRAPE_CHUNK_SIZE - OT_SCRAPE_MAXENTRYLEN;
@@ -200,18 +202,17 @@ static void fullscrape_make(int taskid, ot_tasktype mode) {
200#ifdef WANT_COMPRESSION_GZIP 202#ifdef WANT_COMPRESSION_GZIP
201 203
202static void fullscrape_make_gzip(int taskid, ot_tasktype mode) { 204static void fullscrape_make_gzip(int taskid, ot_tasktype mode) {
203 int bucket; 205 size_t bucket;
204 char *r; 206 char *r;
205 struct iovec iovector = {NULL, 0}; 207 struct iovec iovector = {NULL, 0};
206 int zres; 208 int zres;
207 z_stream strm; 209 z_stream strm;
208 fprintf(stderr, "GZIP path\n");
209 /* Setup return vector... */ 210 /* Setup return vector... */
210 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE); 211 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE);
211 if (!iovector.iov_base) 212 if (!iovector.iov_base)
212 return; 213 return;
213 214
214 byte_zero(&strm, sizeof(strm)); 215 memset(&strm, 0, sizeof(strm));
215 strm.next_out = (uint8_t *)iovector.iov_base; 216 strm.next_out = (uint8_t *)iovector.iov_base;
216 strm.avail_out = OT_SCRAPE_CHUNK_SIZE; 217 strm.avail_out = OT_SCRAPE_CHUNK_SIZE;
217 if (deflateInit2(&strm, 7, Z_DEFLATED, 31, 9, Z_DEFAULT_STRATEGY) != Z_OK) 218 if (deflateInit2(&strm, 7, Z_DEFLATED, 31, 9, Z_DEFAULT_STRATEGY) != Z_OK)
@@ -246,14 +247,16 @@ static void fullscrape_make_gzip(int taskid, ot_tasktype mode) {
246 247
247 if (mutex_workqueue_pushchunked(taskid, &iovector)) { 248 if (mutex_workqueue_pushchunked(taskid, &iovector)) {
248 free(iovector.iov_base); 249 free(iovector.iov_base);
249 return mutex_bucket_unlock(bucket, 0); 250 mutex_bucket_unlock(bucket, 0);
251 return;
250 } 252 }
251 /* Allocate a fresh output buffer */ 253 /* Allocate a fresh output buffer */
252 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE); 254 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE);
253 if (!iovector.iov_base) { 255 if (!iovector.iov_base) {
254 fprintf(stderr, "Out of memory trying to claim ouput buffer\n"); 256 fprintf(stderr, "Out of memory trying to claim ouput buffer\n");
255 deflateEnd(&strm); 257 deflateEnd(&strm);
256 return mutex_bucket_unlock(bucket, 0); 258 mutex_bucket_unlock(bucket, 0);
259 return;
257 } 260 }
258 strm.next_out = (uint8_t *)iovector.iov_base; 261 strm.next_out = (uint8_t *)iovector.iov_base;
259 strm.avail_out = OT_SCRAPE_CHUNK_SIZE; 262 strm.avail_out = OT_SCRAPE_CHUNK_SIZE;
@@ -267,8 +270,10 @@ static void fullscrape_make_gzip(int taskid, ot_tasktype mode) {
267 mutex_bucket_unlock(bucket, 0); 270 mutex_bucket_unlock(bucket, 0);
268 271
269 /* Parent thread died? */ 272 /* Parent thread died? */
270 if (!g_opentracker_running) 273 if (!g_opentracker_running) {
274 deflateEnd(&strm);
271 return; 275 return;
276 }
272 } 277 }
273 278
274 if ((mode & TASK_TASK_MASK) == TASK_FULLSCRAPE) { 279 if ((mode & TASK_TASK_MASK) == TASK_FULLSCRAPE) {
@@ -282,7 +287,8 @@ static void fullscrape_make_gzip(int taskid, ot_tasktype mode) {
282 iovector.iov_len = (char *)strm.next_out - (char *)iovector.iov_base; 287 iovector.iov_len = (char *)strm.next_out - (char *)iovector.iov_base;
283 if (mutex_workqueue_pushchunked(taskid, &iovector)) { 288 if (mutex_workqueue_pushchunked(taskid, &iovector)) {
284 free(iovector.iov_base); 289 free(iovector.iov_base);
285 return mutex_bucket_unlock(bucket, 0); 290 deflateEnd(&strm);
291 return;
286 } 292 }
287 293
288 /* Check if there's a last batch of data in the zlib buffer */ 294 /* Check if there's a last batch of data in the zlib buffer */
@@ -293,7 +299,7 @@ static void fullscrape_make_gzip(int taskid, ot_tasktype mode) {
293 if (!iovector.iov_base) { 299 if (!iovector.iov_base) {
294 fprintf(stderr, "Problem with iovec_fix_increase_or_free\n"); 300 fprintf(stderr, "Problem with iovec_fix_increase_or_free\n");
295 deflateEnd(&strm); 301 deflateEnd(&strm);
296 return mutex_bucket_unlock(bucket, 0); 302 return;
297 } 303 }
298 strm.next_out = iovector.iov_base; 304 strm.next_out = iovector.iov_base;
299 strm.avail_out = OT_SCRAPE_CHUNK_SIZE; 305 strm.avail_out = OT_SCRAPE_CHUNK_SIZE;
@@ -311,6 +317,133 @@ static void fullscrape_make_gzip(int taskid, ot_tasktype mode) {
311/* WANT_COMPRESSION_GZIP */ 317/* WANT_COMPRESSION_GZIP */
312#endif 318#endif
313 319
320#ifdef WANT_COMPRESSION_ZSTD
321
322static void fullscrape_make_zstd(int taskid, ot_tasktype mode) {
323 int bucket;
324 char *r;
325 struct iovec iovector = {NULL, 0};
326 ZSTD_CCtx *zstream = ZSTD_createCCtx();
327 ZSTD_inBuffer inbuf;
328 ZSTD_outBuffer outbuf;
329 size_t more_bytes;
330
331 if (!zstream)
332 return;
333
334 /* Setup return vector... */
335 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE);
336 if (!iovector.iov_base) {
337 ZSTD_freeCCtx(zstream);
338 return;
339 }
340
341 /* Working with a compression level 6 is half as fast as level 3, but
342 seems to be the last reasonable bump that's worth extra cpu */
343 ZSTD_CCtx_setParameter(zstream, ZSTD_c_compressionLevel, 6);
344
345 outbuf.dst = iovector.iov_base;
346 outbuf.size = OT_SCRAPE_CHUNK_SIZE;
347 outbuf.pos = 0;
348
349 if ((mode & TASK_TASK_MASK) == TASK_FULLSCRAPE) {
350 inbuf.src = (const void *)"d5:filesd";
351 inbuf.size = strlen("d5:filesd");
352 inbuf.pos = 0;
353 ZSTD_compressStream2(zstream, &outbuf, &inbuf, ZSTD_e_continue);
354 }
355
356 /* For each bucket... */
357 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
358 /* Get exclusive access to that bucket */
359 ot_vector *torrents_list = mutex_bucket_lock(bucket);
360 ot_torrent *torrents = (ot_torrent *)(torrents_list->data);
361 size_t i;
362
363 /* For each torrent in this bucket.. */
364 for (i = 0; i < torrents_list->size; ++i) {
365 char compress_buffer[OT_SCRAPE_MAXENTRYLEN];
366 r = fullscrape_write_one(mode, compress_buffer, torrents + i, &torrents[i].hash);
367 inbuf.src = compress_buffer;
368 inbuf.size = r - compress_buffer;
369 inbuf.pos = 0;
370 ZSTD_compressStream2(zstream, &outbuf, &inbuf, ZSTD_e_continue);
371
372 /* Check if there still is enough buffer left */
373 while (outbuf.pos + OT_SCRAPE_MAXENTRYLEN > outbuf.size) {
374 iovector.iov_len = outbuf.size;
375
376 if (mutex_workqueue_pushchunked(taskid, &iovector)) {
377 free(iovector.iov_base);
378 ZSTD_freeCCtx(zstream);
379 return mutex_bucket_unlock(bucket, 0);
380 }
381 /* Allocate a fresh output buffer */
382 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE);
383 if (!iovector.iov_base) {
384 fprintf(stderr, "Out of memory trying to claim ouput buffer\n");
385 ZSTD_freeCCtx(zstream);
386 return mutex_bucket_unlock(bucket, 0);
387 }
388
389 outbuf.dst = iovector.iov_base;
390 outbuf.size = OT_SCRAPE_CHUNK_SIZE;
391 outbuf.pos = 0;
392
393 ZSTD_compressStream2(zstream, &outbuf, &inbuf, ZSTD_e_continue);
394 }
395 }
396
397 /* All torrents done: release lock on current bucket */
398 mutex_bucket_unlock(bucket, 0);
399
400 /* Parent thread died? */
401 if (!g_opentracker_running)
402 return;
403 }
404
405 if ((mode & TASK_TASK_MASK) == TASK_FULLSCRAPE) {
406 inbuf.src = (const void *)"ee";
407 inbuf.size = strlen("ee");
408 inbuf.pos = 0;
409 }
410
411 more_bytes = ZSTD_compressStream2(zstream, &outbuf, &inbuf, ZSTD_e_end);
412
413 iovector.iov_len = outbuf.pos;
414 if (mutex_workqueue_pushchunked(taskid, &iovector)) {
415 free(iovector.iov_base);
416 ZSTD_freeCCtx(zstream);
417 return;
418 }
419
420 /* Check if there's a last batch of data in the zlib buffer */
421 if (more_bytes) {
422 /* Allocate a fresh output buffer */
423 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE);
424
425 if (!iovector.iov_base) {
426 fprintf(stderr, "Problem with iovec_fix_increase_or_free\n");
427 ZSTD_freeCCtx(zstream);
428 return;
429 }
430
431 outbuf.dst = iovector.iov_base;
432 outbuf.size = OT_SCRAPE_CHUNK_SIZE;
433 outbuf.pos = 0;
434
435 ZSTD_compressStream2(zstream, &outbuf, &inbuf, ZSTD_e_end);
436
437 /* Only pass the new buffer if there actually was some data left in the buffer */
438 iovector.iov_len = outbuf.pos;
439 if (!iovector.iov_len || mutex_workqueue_pushchunked(taskid, &iovector))
440 free(iovector.iov_base);
441 }
442
443 ZSTD_freeCCtx(zstream);
444}
445/* WANT_COMPRESSION_ZSTD */
446#endif
447
314/* WANT_FULLSCRAPE */ 448/* WANT_FULLSCRAPE */
315#endif 449#endif
316const char *g_version_fullscrape_c = "$Source$: $Revision$\n";
diff --git a/ot_fullscrape.h b/ot_fullscrape.h
index bbb2a3f..843e039 100644
--- a/ot_fullscrape.h
+++ b/ot_fullscrape.h
@@ -10,8 +10,8 @@
10 10
11#include "ot_mutex.h" 11#include "ot_mutex.h"
12 12
13void fullscrape_init(); 13void fullscrape_init(void);
14void fullscrape_deinit(); 14void fullscrape_deinit(void);
15void fullscrape_deliver(int64 sock, ot_tasktype tasktype); 15void fullscrape_deliver(int64 sock, ot_tasktype tasktype);
16 16
17#else 17#else
diff --git a/ot_hex.c b/ot_hex.c
new file mode 100644
index 0000000..17dd1ef
--- /dev/null
+++ b/ot_hex.c
@@ -0,0 +1,56 @@
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
13static int fromhex(unsigned char c) {
14 if (c >= '0' && c <= '9')
15 return c - '0';
16 c |= 0x20; /* fold upper case onto lower, digits are already handled */
17 if (c >= 'a' && c <= 'f')
18 return c - 'a' + 10;
19 return -1;
20}
21
22int hex_isdigit(int c) { return fromhex((unsigned char)c) >= 0; }
23
24char *hex_fromhash(char *dest, const uint8_t *hash) {
25 static const char digits[] = "0123456789ABCDEF";
26 char *d = dest;
27 size_t i;
28
29 for (i = 0; i < sizeof(ot_hash); ++i) {
30 *d++ = digits[hash[i] >> 4];
31 *d++ = digits[hash[i] & 15];
32 }
33 *d = 0;
34 return dest;
35}
36
37int hex_tohash(ot_hash hash, const char *src) {
38 uint8_t decoded[sizeof(ot_hash)];
39 size_t i;
40
41 for (i = 0; i < sizeof(ot_hash); ++i) {
42 int high, low;
43
44 /* Tested one character at a time so that a string ending early stops us
45 at its terminator rather than one byte past it */
46 if ((high = fromhex((unsigned char)src[2 * i])) < 0)
47 return 0;
48 if ((low = fromhex((unsigned char)src[2 * i + 1])) < 0)
49 return 0;
50
51 decoded[i] = (uint8_t)((high << 4) | low);
52 }
53
54 memcpy(hash, decoded, sizeof(decoded));
55 return 1;
56}
diff --git a/ot_hex.h b/ot_hex.h
new file mode 100644
index 0000000..cb2e86f
--- /dev/null
+++ b/ot_hex.h
@@ -0,0 +1,25 @@
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_HEX_H__
7#define OT_HEX_H__
8
9#include <stdint.h>
10
11#include "trackerlogic.h"
12
13/* Write hash as forty upper case hex digits followed by a terminating zero.
14 dest must have room for forty one bytes. Returns dest. */
15char *hex_fromhash(char *dest, const uint8_t *hash);
16
17/* Read forty hex digits from src into hash. Returns non-zero on success.
18 On failure hash is left untouched and no byte of src beyond the offending
19 character is read, so src need not be forty characters long. */
20int hex_tohash(ot_hash hash, const char *src);
21
22/* Non-zero if c is a hexadecimal digit, in either case. */
23int hex_isdigit(int c);
24
25#endif
diff --git a/ot_http.c b/ot_http.c
index d825426..45dacdc 100644
--- a/ot_http.c
+++ b/ot_http.c
@@ -4,9 +4,9 @@
4 $id$ */ 4 $id$ */
5 5
6/* System */ 6/* System */
7#define _GNU_SOURCE
7#include <arpa/inet.h> 8#include <arpa/inet.h>
8#include <pthread.h> 9#include <pthread.h>
9#define _GNU_SOURCE
10#include <stdio.h> 10#include <stdio.h>
11#include <stdlib.h> 11#include <stdlib.h>
12#include <string.h> 12#include <string.h>
@@ -15,7 +15,6 @@
15 15
16/* Libowfat */ 16/* Libowfat */
17#include "array.h" 17#include "array.h"
18#include "byte.h"
19#include "case.h" 18#include "case.h"
20#include "iob.h" 19#include "iob.h"
21#include "ip6.h" 20#include "ip6.h"
@@ -32,7 +31,7 @@
32#include "trackerlogic.h" 31#include "trackerlogic.h"
33 32
34#ifdef WANT_NO_AUTO_FREE 33#ifdef WANT_NO_AUTO_FREE
35#define OT_IOB_INIT(B) bzero(B, sizeof(io_batch)) 34#define OT_IOB_INIT(B) (bzero(B, sizeof(io_batch)), 0)
36#else 35#else
37#define OT_IOB_INIT(B) iob_init_autofree(B, 0) 36#define OT_IOB_INIT(B) iob_init_autofree(B, 0)
38#endif 37#endif
@@ -88,7 +87,15 @@ static void http_senddata(const int64 sock, struct ot_workstruct *ws) {
88 memcpy(outbuf, ws->reply + written_size, ws->reply_size - written_size); 87 memcpy(outbuf, ws->reply + written_size, ws->reply_size - written_size);
89 if (!cookie->batch) { 88 if (!cookie->batch) {
90 cookie->batch = malloc(sizeof(io_batch)); 89 cookie->batch = malloc(sizeof(io_batch));
91 OT_IOB_INIT(cookie->batch); 90 if (!cookie->batch || OT_IOB_INIT(cookie->batch) == -1) {
91 free(cookie->batch);
92 free(outbuf);
93 array_reset(&cookie->request);
94 free(cookie);
95 io_close(sock);
96 return;
97 }
98
92 cookie->batches = 1; 99 cookie->batches = 1;
93 } 100 }
94 101
@@ -133,13 +140,12 @@ ssize_t http_issue_error(const int64 sock, struct ot_workstruct *ws, int code) {
133 return ws->reply_size = -2; 140 return ws->reply_size = -2;
134} 141}
135 142
136ssize_t http_sendiovecdata(const int64 sock, struct ot_workstruct *ws, int iovec_entries, struct iovec *iovector, int is_partial) { 143ssize_t http_sendiovecdata(const int64 sock, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial) {
137 struct http_data *cookie = io_getcookie(sock); 144 struct http_data *cookie = io_getcookie(sock);
138 io_batch *current; 145 io_batch *current;
139 char *header; 146 char *header;
140 const char *encoding = ""; 147 const char *encoding = "";
141 int i; 148 size_t i, header_size, size = iovec_length(&iovec_entries, (const struct iovec **)&iovector);
142 size_t header_size, size = iovec_length(&iovec_entries, (const struct iovec **)&iovector);
143 tai6464 t; 149 tai6464 t;
144 150
145 /* No cookie? Bad socket. Leave. */ 151 /* No cookie? Bad socket. Leave. */
@@ -159,17 +165,19 @@ ssize_t http_sendiovecdata(const int64 sock, struct ot_workstruct *ws, int iovec
159 165
160 if (iovec_entries) { 166 if (iovec_entries) {
161 167
162 if (cookie->flag & STRUCT_HTTP_FLAG_GZIP) 168 if (cookie->flag & STRUCT_HTTP_FLAG_ZSTD)
169 encoding = "Content-Encoding: zstd\r\n";
170 else if (cookie->flag & STRUCT_HTTP_FLAG_GZIP)
163 encoding = "Content-Encoding: gzip\r\n"; 171 encoding = "Content-Encoding: gzip\r\n";
164 else if (cookie->flag & STRUCT_HTTP_FLAG_BZIP2) 172 else if (cookie->flag & STRUCT_HTTP_FLAG_BZIP2)
165 encoding = "Content-Encoding: bzip2\r\n"; 173 encoding = "Content-Encoding: bzip2\r\n";
166 174
167 if (!(cookie->flag & STRUCT_HTTP_FLAG_CHUNKED)) 175 if (!(cookie->flag & STRUCT_HTTP_FLAG_CHUNKED))
168 header_size = asprintf(&header, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n%sContent-Length: %zd\r\n\r\n", encoding, size); 176 header_size = asprintf(&header, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n%sContent-Length: %zd\r\n\r\n", encoding, size);
169 else { 177 else {
170 if (!(cookie->flag & STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER)) { 178 if (!(cookie->flag & STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER)) {
171 header_size = 179 header_size =
172 asprintf(&header, "HTTP/1.0 200 OK\r\nContent-Type: application/octet-stream\r\n%sTransfer-Encoding: chunked\r\n\r\n%zx\r\n", encoding, size); 180 asprintf(&header, "HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\n%sTransfer-Encoding: chunked\r\n\r\n%zx\r\n", encoding, size);
173 cookie->flag |= STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER; 181 cookie->flag |= STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER;
174 } else 182 } else
175 header_size = asprintf(&header, "%zx\r\n", size); 183 header_size = asprintf(&header, "%zx\r\n", size);
@@ -181,26 +189,26 @@ ssize_t http_sendiovecdata(const int64 sock, struct ot_workstruct *ws, int iovec
181 189
182 if (!cookie->batch) { 190 if (!cookie->batch) {
183 cookie->batch = malloc(sizeof(io_batch)); 191 cookie->batch = malloc(sizeof(io_batch));
184 if (!cookie->batch) { 192 if (!cookie->batch || OT_IOB_INIT(cookie->batch) == -1) {
193 free(cookie->batch);
185 free(header); 194 free(header);
186 iovec_free(&iovec_entries, &iovector); 195 iovec_free(&iovec_entries, &iovector);
187 HTTPERROR_500; 196 HTTPERROR_500;
188 } 197 }
189 OT_IOB_INIT(cookie->batch);
190 cookie->batches = 1; 198 cookie->batches = 1;
191 } 199 }
192 current = cookie->batch + cookie->batches - 1; 200 current = cookie->batch + cookie->batches - 1;
193 iob_addbuf_free(current, header, header_size); 201 iob_addbuf_free(current, header, header_size);
194 202
195 /* Split huge iovectors into separate io_batches */ 203 /* Split huge iovectors into separate io_batches */
196 for (i = 0; i < iovec_entries; ++i) { 204 for (i = 0; i < (size_t)iovec_entries; ++i) {
197 /* If the current batch's limit is reached, try to reallocate a new batch to work on */ 205 /* If the current batch's limit is reached, try to reallocate a new batch to work on */
198 if (current->bytesleft > OT_BATCH_LIMIT) { 206 if (current->bytesleft > OT_BATCH_LIMIT) {
199 io_batch *new_batch = realloc(cookie->batch, (cookie->batches + 1) * sizeof(io_batch)); 207 io_batch *new_batch = realloc(cookie->batch, (cookie->batches + 1) * sizeof(io_batch));
200 if (new_batch) { 208 if (new_batch) {
201 cookie->batch = new_batch; 209 cookie->batch = new_batch;
202 current = cookie->batch + cookie->batches++; 210 if (OT_IOB_INIT(current) != -1)
203 OT_IOB_INIT(current); 211 current = cookie->batch + cookie->batches++;
204 } 212 }
205 } 213 }
206 iob_addbuf_free(current, iovector[i].iov_base, iovector[i].iov_len); 214 iob_addbuf_free(current, iovector[i].iov_base, iovector[i].iov_len);
@@ -369,19 +377,34 @@ static ssize_t http_handle_fullscrape(const int64 sock, struct ot_workstruct *ws
369 } 377 }
370#endif 378#endif
371 379
372#ifdef WANT_COMPRESSION_GZIP 380
381#if defined(WANT_COMPRESSION_GZIP) || defined(WANT_COMPRESSION_ZSTD)
373 ws->request[ws->request_size - 1] = 0; 382 ws->request[ws->request_size - 1] = 0;
374#ifndef WANT_COMPRESSION_GZIP_ALWAYS 383#ifdef WANT_COMPRESSION_GZIP
375 if (strstr(ws->request, "gzip")) { 384 if (strstr(ws->request, "gzip")) {
376#endif
377 cookie->flag |= STRUCT_HTTP_FLAG_GZIP; 385 cookie->flag |= STRUCT_HTTP_FLAG_GZIP;
378 format = TASK_FLAG_GZIP; 386 format |= TASK_FLAG_GZIP;
379 stats_issue_event(EVENT_FULLSCRAPE_REQUEST_GZIP, 0, (uintptr_t)cookie->ip); 387 }
380#ifndef WANT_COMPRESSION_GZIP_ALWAYS 388#endif
381 } else 389#ifdef WANT_COMPRESSION_ZSTD
390 if (strstr(ws->request, "zstd")) {
391 cookie->flag |= STRUCT_HTTP_FLAG_ZSTD;
392 format |= TASK_FLAG_ZSTD;
393 }
394#endif
395
396#if defined(WANT_COMPRESSION_ZSTD) && defined(WANT_COMPRESSION_ZSTD_ALWAYS)
397 cookie->flag |= STRUCT_HTTP_FLAG_ZSTD;
398 format |= TASK_FLAG_ZSTD;
399#endif
400
401#if defined(WANT_COMPRESSION_GZIP) && defined(WANT_COMPRESSION_GZIP_ALWAYS)
402 cookie->flag |= STRUCT_HTTP_FLAG_GZIP;
403 format |= TASK_FLAG_GZIP;
382#endif 404#endif
383#endif 405#endif
384 stats_issue_event(EVENT_FULLSCRAPE_REQUEST, 0, (uintptr_t)cookie->ip); 406
407 stats_issue_event(EVENT_FULLSCRAPE_REQUEST, 0, (uintptr_t)cookie->ip);
385 408
386#ifdef _DEBUG_HTTPERROR 409#ifdef _DEBUG_HTTPERROR
387 fprintf(stderr, "%s", ws->debugbuf); 410 fprintf(stderr, "%s", ws->debugbuf);
@@ -401,8 +424,9 @@ static ssize_t http_handle_fullscrape(const int64 sock, struct ot_workstruct *ws
401static ssize_t http_handle_scrape(const int64 sock, struct ot_workstruct *ws, char *read_ptr) { 424static ssize_t http_handle_scrape(const int64 sock, struct ot_workstruct *ws, char *read_ptr) {
402 static const ot_keywords keywords_scrape[] = {{"info_hash", 1}, {NULL, -3}}; 425 static const ot_keywords keywords_scrape[] = {{"info_hash", 1}, {NULL, -3}};
403 426
404 ot_hash *multiscrape_buf = (ot_hash *)ws->request; 427 ot_hash *multiscrape_buf = (ot_hash *)ws->request;
405 int scanon = 1, numwant = 0; 428 int scanon = 1;
429 size_t numwant = 0;
406 430
407 /* This is to hack around stupid clients that send "scrape ?info_hash" */ 431 /* This is to hack around stupid clients that send "scrape ?info_hash" */
408 if (read_ptr[-1] != '?') { 432 if (read_ptr[-1] != '?') {
@@ -477,7 +501,8 @@ static ot_keywords keywords_announce[] = {{"port", 1}, {"left", 2}, {"event",
477 {"peer_id", 9}, {NULL, -3}}; 501 {"peer_id", 9}, {NULL, -3}};
478static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}}; 502static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}};
479static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct *ws, char *read_ptr) { 503static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct *ws, char *read_ptr) {
480 int numwant, tmp, scanon; 504 size_t numwant;
505 int tmp, scanon;
481 unsigned short port = 0; 506 unsigned short port = 0;
482 char *write_ptr; 507 char *write_ptr;
483 ssize_t len; 508 ssize_t len;
@@ -553,12 +578,13 @@ static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct *
553 break; 578 break;
554 case 4: /* matched "numwant" */ 579 case 4: /* matched "numwant" */
555 len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); 580 len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE);
556 if ((len <= 0) || scan_fixed_int(write_ptr, len, &numwant)) 581 if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp))
557 HTTPERROR_400_PARAM; 582 HTTPERROR_400_PARAM;
558 if (numwant < 0) 583 if (tmp < 0)
559 numwant = 50; 584 tmp = 50;
560 if (numwant > 200) 585 if (tmp > 200)
561 numwant = 200; 586 tmp = 200;
587 numwant = tmp;
562 break; 588 break;
563 case 5: /* matched "compact" */ 589 case 5: /* matched "compact" */
564 len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); 590 len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE);
@@ -760,5 +786,3 @@ ssize_t http_handle_request(const int64 sock, struct ot_workstruct *ws) {
760 http_senddata(sock, ws); 786 http_senddata(sock, ws);
761 return ws->reply_size; 787 return ws->reply_size;
762} 788}
763
764const char *g_version_http_c = "$Source$: $Revision$\n";
diff --git a/ot_http.h b/ot_http.h
index fecb4eb..8980682 100644
--- a/ot_http.h
+++ b/ot_http.h
@@ -10,8 +10,9 @@ typedef enum {
10 STRUCT_HTTP_FLAG_WAITINGFORTASK = 1, 10 STRUCT_HTTP_FLAG_WAITINGFORTASK = 1,
11 STRUCT_HTTP_FLAG_GZIP = 2, 11 STRUCT_HTTP_FLAG_GZIP = 2,
12 STRUCT_HTTP_FLAG_BZIP2 = 4, 12 STRUCT_HTTP_FLAG_BZIP2 = 4,
13 STRUCT_HTTP_FLAG_CHUNKED = 8, 13 STRUCT_HTTP_FLAG_ZSTD = 8,
14 STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER = 16 14 STRUCT_HTTP_FLAG_CHUNKED = 16,
15 STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER = 32
15} STRUCT_HTTP_FLAG; 16} STRUCT_HTTP_FLAG;
16 17
17struct http_data { 18struct http_data {
@@ -23,7 +24,7 @@ struct http_data {
23}; 24};
24 25
25ssize_t http_handle_request(const int64 s, struct ot_workstruct *ws); 26ssize_t http_handle_request(const int64 s, struct ot_workstruct *ws);
26ssize_t http_sendiovecdata(const int64 s, struct ot_workstruct *ws, int iovec_entries, struct iovec *iovector, int is_partial); 27ssize_t http_sendiovecdata(const int64 s, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial);
27ssize_t http_issue_error(const int64 s, struct ot_workstruct *ws, int code); 28ssize_t http_issue_error(const int64 s, struct ot_workstruct *ws, int code);
28 29
29extern char *g_stats_path; 30extern char *g_stats_path;
diff --git a/ot_iovec.c b/ot_iovec.c
index 9fb30cc..bab19e3 100644
--- a/ot_iovec.c
+++ b/ot_iovec.c
@@ -5,18 +5,16 @@
5 5
6/* System */ 6/* System */
7#include <stdlib.h> 7#include <stdlib.h>
8#include <sys/types.h>
9#include <sys/uio.h> 8#include <sys/uio.h>
10#include <unistd.h>
11 9
12/* Libowfat */ 10/* Libowfat */
13 11
14/* Opentracker */ 12/* Opentracker */
15#include "ot_iovec.h" 13#include "ot_iovec.h"
16 14
17void *iovec_increase(int *iovec_entries, struct iovec **iovector, size_t new_alloc) { 15void *iovec_increase(size_t *iovec_entries, struct iovec **iovector, size_t new_alloc) {
18 void *new_data; 16 void *new_data;
19 int new_entries = 1 + *iovec_entries; 17 size_t new_entries = 1 + *iovec_entries;
20 struct iovec *new_vec = realloc(*iovector, new_entries * sizeof(struct iovec)); 18 struct iovec *new_vec = realloc(*iovector, new_entries * sizeof(struct iovec));
21 19
22 if (!new_vec) 20 if (!new_vec)
@@ -35,8 +33,8 @@ void *iovec_increase(int *iovec_entries, struct iovec **iovector, size_t new_all
35 return new_data; 33 return new_data;
36} 34}
37 35
38void *iovec_append(int *iovec_entries, struct iovec **iovector, struct iovec *append_iovector) { 36void *iovec_append(size_t *iovec_entries, struct iovec **iovector, struct iovec *append_iovector) {
39 int new_entries = *iovec_entries + 1; 37 size_t new_entries = *iovec_entries + 1;
40 struct iovec *new_vec = realloc(*iovector, new_entries * sizeof(struct iovec)); 38 struct iovec *new_vec = realloc(*iovector, new_entries * sizeof(struct iovec));
41 if (!new_vec) 39 if (!new_vec)
42 return NULL; 40 return NULL;
@@ -54,15 +52,16 @@ void *iovec_append(int *iovec_entries, struct iovec **iovector, struct iovec *ap
54 return new_vec; 52 return new_vec;
55} 53}
56 54
57void iovec_free(int *iovec_entries, struct iovec **iovector) { 55void iovec_free(size_t *iovec_entries, struct iovec **iovector) {
58 int i; 56 size_t i;
59 for (i = 0; i < *iovec_entries; ++i) 57 for (i = 0; i < *iovec_entries; ++i)
60 free(((*iovector)[i]).iov_base); 58 free(((*iovector)[i]).iov_base);
59 free(*iovector);
61 *iovector = NULL; 60 *iovector = NULL;
62 *iovec_entries = 0; 61 *iovec_entries = 0;
63} 62}
64 63
65void iovec_fixlast(int *iovec_entries, struct iovec **iovector, void *last_ptr) { 64void iovec_fixlast(size_t *iovec_entries, struct iovec **iovector, void *last_ptr) {
66 if (*iovec_entries) { 65 if (*iovec_entries) {
67 char *base = (char *)((*iovector)[*iovec_entries - 1]).iov_base; 66 char *base = (char *)((*iovector)[*iovec_entries - 1]).iov_base;
68 size_t new_alloc = ((char *)last_ptr) - base; 67 size_t new_alloc = ((char *)last_ptr) - base;
@@ -72,7 +71,7 @@ void iovec_fixlast(int *iovec_entries, struct iovec **iovector, void *last_ptr)
72 } 71 }
73} 72}
74 73
75void *iovec_fix_increase_or_free(int *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc) { 74void *iovec_fix_increase_or_free(size_t *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc) {
76 void *new_data; 75 void *new_data;
77 76
78 iovec_fixlast(iovec_entries, iovector, last_ptr); 77 iovec_fixlast(iovec_entries, iovector, last_ptr);
@@ -83,12 +82,9 @@ void *iovec_fix_increase_or_free(int *iovec_entries, struct iovec **iovector, vo
83 return new_data; 82 return new_data;
84} 83}
85 84
86size_t iovec_length(const int *iovec_entries, const struct iovec **iovector) { 85size_t iovec_length(const size_t *iovec_entries, const struct iovec **iovector) {
87 size_t length = 0; 86 size_t i, length = 0;
88 int i;
89 for (i = 0; i < *iovec_entries; ++i) 87 for (i = 0; i < *iovec_entries; ++i)
90 length += ((*iovector)[i]).iov_len; 88 length += ((*iovector)[i]).iov_len;
91 return length; 89 return length;
92} 90}
93
94const char *g_version_iovec_c = "$Source$: $Revision$\n";
diff --git a/ot_iovec.h b/ot_iovec.h
index 4317ab7..eb4365b 100644
--- a/ot_iovec.h
+++ b/ot_iovec.h
@@ -8,13 +8,13 @@
8 8
9#include <sys/uio.h> 9#include <sys/uio.h>
10 10
11void *iovec_increase(int *iovec_entries, struct iovec **iovector, size_t new_alloc); 11void *iovec_increase(size_t *iovec_entries, struct iovec **iovector, size_t new_alloc);
12void *iovec_append(int *iovec_entries, struct iovec **iovector, struct iovec *append_iovector); 12void *iovec_append(size_t *iovec_entries, struct iovec **iovector, struct iovec *append_iovector);
13void iovec_fixlast(int *iovec_entries, struct iovec **iovector, void *last_ptr); 13void iovec_fixlast(size_t *iovec_entries, struct iovec **iovector, void *last_ptr);
14void iovec_free(int *iovec_entries, struct iovec **iovector); 14void iovec_free(size_t *iovec_entries, struct iovec **iovector);
15 15
16size_t iovec_length(const int *iovec_entries, const struct iovec **iovector); 16size_t iovec_length(const size_t *iovec_entries, const struct iovec **iovector);
17 17
18void *iovec_fix_increase_or_free(int *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc); 18void *iovec_fix_increase_or_free(size_t *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc);
19 19
20#endif 20#endif
diff --git a/ot_livesync.c b/ot_livesync.c
index 246317b..b876ef8 100644
--- a/ot_livesync.c
+++ b/ot_livesync.c
@@ -4,15 +4,14 @@
4 $id$ */ 4 $id$ */
5 5
6/* System */ 6/* System */
7#include <arpa/inet.h>
7#include <pthread.h> 8#include <pthread.h>
8#include <stdlib.h> 9#include <stdlib.h>
9#include <string.h> 10#include <string.h>
10#include <sys/types.h> 11#include <sys/types.h>
11#include <sys/uio.h>
12#include <unistd.h> 12#include <unistd.h>
13 13
14/* Libowfat */ 14/* Libowfat */
15#include "byte.h"
16#include "ip6.h" 15#include "ip6.h"
17#include "ndelay.h" 16#include "ndelay.h"
18#include "socket.h" 17#include "socket.h"
@@ -57,7 +56,9 @@ static sync_buffer g_v6_buf;
57static sync_buffer g_v4_buf; 56static sync_buffer g_v4_buf;
58 57
59static pthread_t thread_id; 58static pthread_t thread_id;
60void livesync_init() { 59void livesync_init(void) {
60 uint32_t packet_type6 = htonl(OT_SYNC_PEER6);
61 uint32_t packet_type4 = htonl(OT_SYNC_PEER4);
61 62
62 if (g_socket_in == -1) 63 if (g_socket_in == -1)
63 exerr("No socket address for live sync specified."); 64 exerr("No socket address for live sync specified.");
@@ -66,8 +67,8 @@ void livesync_init() {
66 memcpy(g_v6_buf.data, &g_tracker_id, sizeof(g_tracker_id)); 67 memcpy(g_v6_buf.data, &g_tracker_id, sizeof(g_tracker_id));
67 memcpy(g_v4_buf.data, &g_tracker_id, sizeof(g_tracker_id)); 68 memcpy(g_v4_buf.data, &g_tracker_id, sizeof(g_tracker_id));
68 69
69 uint32_pack_big((char *)g_v6_buf.data + sizeof(g_tracker_id), OT_SYNC_PEER6); 70 memcpy(g_v6_buf.data + sizeof(g_tracker_id), &packet_type6, sizeof(packet_type6));
70 uint32_pack_big((char *)g_v4_buf.data + sizeof(g_tracker_id), OT_SYNC_PEER4); 71 memcpy(g_v4_buf.data + sizeof(g_tracker_id), &packet_type4, sizeof(packet_type4));
71 72
72 g_v6_buf.fill = sizeof(g_tracker_id) + sizeof(uint32_t); 73 g_v6_buf.fill = sizeof(g_tracker_id) + sizeof(uint32_t);
73 g_v4_buf.fill = sizeof(g_tracker_id) + sizeof(uint32_t); 74 g_v4_buf.fill = sizeof(g_tracker_id) + sizeof(uint32_t);
@@ -78,7 +79,7 @@ void livesync_init() {
78 pthread_create(&thread_id, NULL, livesync_worker, NULL); 79 pthread_create(&thread_id, NULL, livesync_worker, NULL);
79} 80}
80 81
81void livesync_deinit() { 82void livesync_deinit(void) {
82 if (g_socket_in != -1) 83 if (g_socket_in != -1)
83 close(g_socket_in); 84 close(g_socket_in);
84 if (g_socket_out != -1) 85 if (g_socket_out != -1)
@@ -136,10 +137,18 @@ static void livesync_issue_peersync(sync_buffer *buf) {
136static void livesync_handle_peersync(struct ot_workstruct *ws, size_t peer_size) { 137static void livesync_handle_peersync(struct ot_workstruct *ws, size_t peer_size) {
137 size_t off = sizeof(g_tracker_id) + sizeof(uint32_t); 138 size_t off = sizeof(g_tracker_id) + sizeof(uint32_t);
138 139
140 /* Prepare peer record for v4mapped expansion */
141 if (peer_size == OT_PEER_SIZE4)
142 memcpy(&ws->peer, V4mappedprefix, sizeof(V4mappedprefix));
143
139 /* Now basic sanity checks have been done on the live sync packet 144 /* Now basic sanity checks have been done on the live sync packet
140 We might add more testing and logging. */ 145 We might add more testing and logging. */
141 while ((ssize_t)(off + sizeof(ot_hash) + peer_size) <= ws->request_size) { 146 while ((ssize_t)(off + sizeof(ot_hash) + peer_size) <= ws->request_size) {
142 memcpy(&ws->peer, ws->request + off + sizeof(ot_hash), peer_size); 147 char *peer_ip = ws->request + off + sizeof(ot_hash);
148 if (peer_size == OT_PEER_SIZE4)
149 memcpy(&ws->peer[0] + sizeof(V4mappedprefix), peer_ip, peer_size);
150 else
151 memcpy(&ws->peer, peer_ip, peer_size);
143 ws->hash = (ot_hash *)(ws->request + off); 152 ws->hash = (ot_hash *)(ws->request + off);
144 153
145 if (!g_opentracker_running) 154 if (!g_opentracker_running)
@@ -159,7 +168,7 @@ static void livesync_handle_peersync(struct ot_workstruct *ws, size_t peer_size)
159/* Tickle the live sync module from time to time, so no events get 168/* Tickle the live sync module from time to time, so no events get
160 stuck when there's not enough traffic to fill udp packets fast 169 stuck when there's not enough traffic to fill udp packets fast
161 enough */ 170 enough */
162void livesync_ticker() { 171void livesync_ticker(void) {
163 /* livesync_issue_peersync sets g_next_packet_time */ 172 /* livesync_issue_peersync sets g_next_packet_time */
164 pthread_mutex_lock(&g_outbuf_mutex); 173 pthread_mutex_lock(&g_outbuf_mutex);
165 if (g_now_seconds > g_v6_buf.next_packet_time && g_v6_buf.fill > sizeof(g_tracker_id) + sizeof(uint32_t)) 174 if (g_now_seconds > g_v6_buf.next_packet_time && g_v6_buf.fill > sizeof(g_tracker_id) + sizeof(uint32_t))
@@ -208,6 +217,8 @@ static void *livesync_worker(void *args) {
208 memcpy(in_ip, V4mappedprefix, sizeof(V4mappedprefix)); 217 memcpy(in_ip, V4mappedprefix, sizeof(V4mappedprefix));
209 218
210 while (1) { 219 while (1) {
220 uint32_t packet_type;
221
211 ws.request_size = socket_recv4(g_socket_in, (char *)ws.inbuf, LIVESYNC_INCOMING_BUFFSIZE, 12 + (char *)in_ip, &in_port); 222 ws.request_size = socket_recv4(g_socket_in, (char *)ws.inbuf, LIVESYNC_INCOMING_BUFFSIZE, 12 + (char *)in_ip, &in_port);
212 223
213 /* Expect at least tracker id and packet type */ 224 /* Expect at least tracker id and packet type */
@@ -220,7 +231,9 @@ static void *livesync_worker(void *args) {
220 continue; 231 continue;
221 } 232 }
222 233
223 switch (uint32_read_big(sizeof(g_tracker_id) + (char *)ws.inbuf)) { 234 memcpy(&packet_type, ws.inbuf + sizeof(g_tracker_id), sizeof(packet_type));
235
236 switch (ntohl(packet_type)) {
224 case OT_SYNC_PEER6: 237 case OT_SYNC_PEER6:
225 livesync_handle_peersync(&ws, OT_PEER_SIZE6); 238 livesync_handle_peersync(&ws, OT_PEER_SIZE6);
226 break; 239 break;
@@ -237,4 +250,3 @@ static void *livesync_worker(void *args) {
237} 250}
238 251
239#endif 252#endif
240const char *g_version_livesync_c = "$Source$: $Revision$\n";
diff --git a/ot_livesync.h b/ot_livesync.h
index cb28774..ed710f0 100644
--- a/ot_livesync.h
+++ b/ot_livesync.h
@@ -47,8 +47,8 @@
47 47
48#define LIVESYNC_PORT 9696 48#define LIVESYNC_PORT 9696
49 49
50void livesync_init(); 50void livesync_init(void);
51void livesync_deinit(); 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(char *ip, uint16_t port);
@@ -59,7 +59,7 @@ void livesync_tell(struct ot_workstruct *ws);
59/* Tickle the live sync module from time to time, so no events get 59/* Tickle the live sync module from time to time, so no events get
60 stuck when there's not enough traffic to fill udp packets fast 60 stuck when there's not enough traffic to fill udp packets fast
61 enough */ 61 enough */
62void livesync_ticker(); 62void livesync_ticker(void);
63 63
64/* Handle an incoming live sync packet */ 64/* Handle an incoming live sync packet */
65void handle_livesync(const int64 sock); 65void handle_livesync(const int64 sock);
diff --git a/ot_mutex.c b/ot_mutex.c
index 1aa2783..9aa476d 100644
--- a/ot_mutex.c
+++ b/ot_mutex.c
@@ -4,16 +4,15 @@
4 $id$ */ 4 $id$ */
5 5
6/* System */ 6/* System */
7#include <arpa/inet.h>
7#include <pthread.h> 8#include <pthread.h>
8#include <stdio.h> 9#include <stdio.h>
9#include <stdlib.h> 10#include <stdlib.h>
10#include <sys/mman.h> 11#include <string.h>
11#include <sys/uio.h> 12#include <sys/uio.h>
12 13
13/* Libowfat */ 14/* Libowfat */
14#include "byte.h"
15#include "io.h" 15#include "io.h"
16#include "uint32.h"
17 16
18/* Opentracker */ 17/* Opentracker */
19#include "ot_iovec.h" 18#include "ot_iovec.h"
@@ -37,7 +36,15 @@ ot_vector *mutex_bucket_lock(int bucket) {
37 return all_torrents + bucket; 36 return all_torrents + bucket;
38} 37}
39 38
40ot_vector *mutex_bucket_lock_by_hash(ot_hash const hash) { return mutex_bucket_lock(uint32_read_big((const char *)hash) >> OT_BUCKET_COUNT_SHIFT); } 39/* Torrents are spread over buckets by the first four bytes of their info_hash,
40 which may sit at any alignment, hence the memcpy */
41static int bucket_from_hash(ot_hash const hash) {
42 uint32_t bucket;
43 memcpy(&bucket, hash, sizeof(bucket));
44 return ntohl(bucket) >> OT_BUCKET_COUNT_SHIFT;
45}
46
47ot_vector *mutex_bucket_lock_by_hash(ot_hash const hash) { return mutex_bucket_lock(bucket_from_hash(hash)); }
41 48
42void mutex_bucket_unlock(int bucket, int delta_torrentcount) { 49void mutex_bucket_unlock(int bucket, int delta_torrentcount) {
43 pthread_mutex_unlock(bucket_mutex + bucket); 50 pthread_mutex_unlock(bucket_mutex + bucket);
@@ -45,10 +52,10 @@ void mutex_bucket_unlock(int bucket, int delta_torrentcount) {
45} 52}
46 53
47void mutex_bucket_unlock_by_hash(ot_hash const hash, int delta_torrentcount) { 54void mutex_bucket_unlock_by_hash(ot_hash const hash, int delta_torrentcount) {
48 mutex_bucket_unlock(uint32_read_big((char *)hash) >> OT_BUCKET_COUNT_SHIFT, delta_torrentcount); 55 mutex_bucket_unlock(bucket_from_hash(hash), delta_torrentcount);
49} 56}
50 57
51size_t mutex_get_torrent_count() { return g_torrent_count; } 58size_t mutex_get_torrent_count(void) { return g_torrent_count; }
52 59
53/* TaskQueue Magic */ 60/* TaskQueue Magic */
54 61
@@ -56,7 +63,7 @@ struct ot_task {
56 ot_taskid taskid; 63 ot_taskid taskid;
57 ot_tasktype tasktype; 64 ot_tasktype tasktype;
58 int64 sock; 65 int64 sock;
59 int iovec_entries; 66 size_t iovec_entries;
60 struct iovec *iovec; 67 struct iovec *iovec;
61 struct ot_task *next; 68 struct ot_task *next;
62}; 69};
@@ -105,7 +112,7 @@ void mutex_workqueue_canceltask(int64 sock) {
105 if ((*task)->sock == sock) { 112 if ((*task)->sock == sock) {
106 struct iovec *iovec = (*task)->iovec; 113 struct iovec *iovec = (*task)->iovec;
107 struct ot_task *ptask = *task; 114 struct ot_task *ptask = *task;
108 int i; 115 size_t i;
109 116
110 /* Free task's iovec */ 117 /* Free task's iovec */
111 for (i = 0; i < (*task)->iovec_entries; ++i) 118 for (i = 0; i < (*task)->iovec_entries; ++i)
@@ -167,7 +174,7 @@ void mutex_workqueue_pushsuccess(ot_taskid taskid) {
167 pthread_mutex_unlock(&tasklist_mutex); 174 pthread_mutex_unlock(&tasklist_mutex);
168} 175}
169 176
170int mutex_workqueue_pushresult(ot_taskid taskid, int iovec_entries, struct iovec *iovec) { 177int mutex_workqueue_pushresult(ot_taskid taskid, size_t iovec_entries, struct iovec *iovec) {
171 struct ot_task *task; 178 struct ot_task *task;
172 const char byte = 'o'; 179 const char byte = 'o';
173 180
@@ -219,7 +226,7 @@ int mutex_workqueue_pushchunked(ot_taskid taskid, struct iovec *iovec) {
219 return task ? 0 : -1; 226 return task ? 0 : -1;
220} 227}
221 228
222int64 mutex_workqueue_popresult(int *iovec_entries, struct iovec **iovec, int *is_partial) { 229int64 mutex_workqueue_popresult(size_t *iovec_entries, struct iovec **iovec, int *is_partial) {
223 struct ot_task **task; 230 struct ot_task **task;
224 int64 sock = -1; 231 int64 sock = -1;
225 232
@@ -253,22 +260,20 @@ int64 mutex_workqueue_popresult(int *iovec_entries, struct iovec **iovec, int *i
253 return sock; 260 return sock;
254} 261}
255 262
256void mutex_init() { 263void mutex_init(void) {
257 int i; 264 size_t i;
258 pthread_mutex_init(&tasklist_mutex, NULL); 265 pthread_mutex_init(&tasklist_mutex, NULL);
259 pthread_cond_init(&tasklist_being_filled, NULL); 266 pthread_cond_init(&tasklist_being_filled, NULL);
260 for (i = 0; i < OT_BUCKET_COUNT; ++i) 267 for (i = 0; i < OT_BUCKET_COUNT; ++i)
261 pthread_mutex_init(bucket_mutex + i, NULL); 268 pthread_mutex_init(bucket_mutex + i, NULL);
262 byte_zero(all_torrents, sizeof(all_torrents)); 269 memset(all_torrents, 0, sizeof(all_torrents));
263} 270}
264 271
265void mutex_deinit() { 272void mutex_deinit(void) {
266 int i; 273 size_t i;
267 for (i = 0; i < OT_BUCKET_COUNT; ++i) 274 for (i = 0; i < OT_BUCKET_COUNT; ++i)
268 pthread_mutex_destroy(bucket_mutex + i); 275 pthread_mutex_destroy(bucket_mutex + i);
269 pthread_mutex_destroy(&tasklist_mutex); 276 pthread_mutex_destroy(&tasklist_mutex);
270 pthread_cond_destroy(&tasklist_being_filled); 277 pthread_cond_destroy(&tasklist_being_filled);
271 byte_zero(all_torrents, sizeof(all_torrents)); 278 memset(all_torrents, 0, sizeof(all_torrents));
272} 279}
273
274const char *g_version_mutex_c = "$Source$: $Revision$\n";
diff --git a/ot_mutex.h b/ot_mutex.h
index 66b627f..ab1cf59 100644
--- a/ot_mutex.h
+++ b/ot_mutex.h
@@ -59,7 +59,8 @@ typedef enum {
59 59
60 TASK_FLAG_GZIP = 0x1000, 60 TASK_FLAG_GZIP = 0x1000,
61 TASK_FLAG_BZIP2 = 0x2000, 61 TASK_FLAG_BZIP2 = 0x2000,
62 TASK_FLAG_CHUNKED = 0x4000, 62 TASK_FLAG_ZSTD = 0x4000,
63 TASK_FLAG_CHUNKED = 0x8000,
63 64
64 TASK_TASK_MASK = 0x0fff, 65 TASK_TASK_MASK = 0x0fff,
65 TASK_CLASS_MASK = 0x0f00, 66 TASK_CLASS_MASK = 0x0f00,
@@ -72,8 +73,8 @@ int mutex_workqueue_pushtask(int64 sock, ot_tasktype tasktype)
72void mutex_workqueue_canceltask(int64 sock); 73void mutex_workqueue_canceltask(int64 sock);
73void mutex_workqueue_pushsuccess(ot_taskid taskid); 74void mutex_workqueue_pushsuccess(ot_taskid taskid);
74ot_taskid mutex_workqueue_poptask(ot_tasktype *tasktype); 75ot_taskid mutex_workqueue_poptask(ot_tasktype *tasktype);
75int mutex_workqueue_pushresult(ot_taskid taskid, int iovec_entries, struct iovec *iovector); 76int mutex_workqueue_pushresult(ot_taskid taskid, size_t iovec_entries, struct iovec *iovector);
76int mutex_workqueue_pushchunked(ot_taskid taskid, struct iovec *iovec); 77int mutex_workqueue_pushchunked(ot_taskid taskid, struct iovec *iovec);
77int64 mutex_workqueue_popresult(int *iovec_entries, struct iovec **iovector, int *is_partial); 78int64 mutex_workqueue_popresult(size_t *iovec_entries, struct iovec **iovector, int *is_partial);
78 79
79#endif 80#endif
diff --git a/ot_random.c b/ot_random.c
new file mode 100644
index 0000000..3fae876
--- /dev/null
+++ b/ot_random.c
@@ -0,0 +1,53 @@
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 <stdlib.h>
9
10/* Opentracker */
11#include "ot_random.h"
12
13#ifdef WANT_INSECURE_RANDOM_FALLBACK
14
15#warning WANT_INSECURE_RANDOM_FALLBACK builds without arc4random. UDP connection ids become predictable and announces can be forged. Do not ship this.
16
17#include <time.h>
18
19void random_init(void) {
20#ifdef WANT_DEV_RANDOM
21 srandomdev();
22#else
23 srandom(time(NULL));
24#endif
25}
26
27/* random() yields 31 bits, so one draw can never set the top bit. Combine
28 two of them rather than hand out a value that is quietly half as wide as
29 its type claims. */
30uint32_t random_uint32(void) { return ((uint32_t)random() << 16) ^ (uint32_t)random(); }
31
32void random_bytes(void *buf, size_t len) {
33 uint8_t *out = buf;
34 /* random() leaves the top bit clear and its lowest bits are the weakest,
35 so take a byte from the middle of each draw */
36 while (len--)
37 *out++ = (uint8_t)(random() >> 8);
38}
39
40#else
41
42#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__DragonFly__) && !defined(__APPLE__) && \
43 !(defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 36)))
44#include <bsd/stdlib.h>
45#endif
46
47void random_init(void) {}
48
49uint32_t random_uint32(void) { return arc4random(); }
50
51void random_bytes(void *buf, size_t len) { arc4random_buf(buf, len); }
52
53#endif
diff --git a/ot_random.h b/ot_random.h
new file mode 100644
index 0000000..5200dbc
--- /dev/null
+++ b/ot_random.h
@@ -0,0 +1,19 @@
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_RANDOM_H__
7#define OT_RANDOM_H__
8
9#include <stddef.h>
10#include <stdint.h>
11
12/* Seed whatever generator the build settled on. A no-op when backed by
13 arc4random, which seeds itself from the kernel. */
14void random_init(void);
15
16uint32_t random_uint32(void);
17void random_bytes(void *buf, size_t len);
18
19#endif
diff --git a/ot_rijndael.c b/ot_rijndael.c
index f468e2f..3f36bde 100644
--- a/ot_rijndael.c
+++ b/ot_rijndael.c
@@ -486,5 +486,3 @@ void rijndaelEncrypt128(const uint32_t rk[44], const uint8_t pt[16], uint8_t ct[
486 rk[43]; 486 rk[43];
487 PUTU32(ct + 12, s3); 487 PUTU32(ct + 12, s3);
488} 488}
489
490const char *g_version_rijndael_c = "$Source$: $Revision$\n";
diff --git a/ot_stats.c b/ot_stats.c
index fa456c3..d43f29c 100644
--- a/ot_stats.c
+++ b/ot_stats.c
@@ -4,13 +4,11 @@
4 $id$ */ 4 $id$ */
5 5
6/* System */ 6/* System */
7#include <arpa/inet.h>
8#include <inttypes.h> 7#include <inttypes.h>
9#include <pthread.h> 8#include <pthread.h>
10#include <stdio.h> 9#include <stdio.h>
11#include <stdlib.h> 10#include <stdlib.h>
12#include <string.h> 11#include <string.h>
13#include <sys/mman.h>
14#include <sys/types.h> 12#include <sys/types.h>
15#include <sys/uio.h> 13#include <sys/uio.h>
16#include <unistd.h> 14#include <unistd.h>
@@ -19,13 +17,13 @@
19#endif 17#endif
20 18
21/* Libowfat */ 19/* Libowfat */
22#include "byte.h"
23#include "io.h" 20#include "io.h"
24#include "ip4.h" 21#include "ip4.h"
25#include "ip6.h" 22#include "ip6.h"
26 23
27/* Opentracker */ 24/* Opentracker */
28#include "ot_accesslist.h" 25#include "ot_accesslist.h"
26#include "ot_hex.h"
29#include "ot_iovec.h" 27#include "ot_iovec.h"
30#include "ot_mutex.h" 28#include "ot_mutex.h"
31#include "ot_stats.h" 29#include "ot_stats.h"
@@ -38,7 +36,7 @@
38#endif 36#endif
39 37
40/* Forward declaration */ 38/* Forward declaration */
41static void stats_make(int *iovec_entries, struct iovec **iovector, ot_tasktype mode); 39static void stats_make(size_t *iovec_entries, struct iovec **iovector, ot_tasktype mode);
42#define OT_STATS_TMPSIZE 8192 40#define OT_STATS_TMPSIZE 8192
43 41
44/* Clumsy counters... to be rethought */ 42/* Clumsy counters... to be rethought */
@@ -113,7 +111,7 @@ static int stat_increase_network_count(stats_network_node **pnode, int depth, ui
113} 111}
114 112
115static int stats_shift_down_network_count(stats_network_node **node, int depth, int shift) { 113static int stats_shift_down_network_count(stats_network_node **node, int depth, int shift) {
116 int i, rest = 0; 114 size_t i, rest = 0;
117 115
118 if (!*node) 116 if (!*node)
119 return 0; 117 return 0;
@@ -134,8 +132,7 @@ static int stats_shift_down_network_count(stats_network_node **node, int depth,
134 132
135static size_t stats_get_highscore_networks(stats_network_node *node, int depth, ot_ip6 node_value, size_t *scores, ot_ip6 *networks, int network_count, 133static size_t stats_get_highscore_networks(stats_network_node *node, int depth, ot_ip6 node_value, size_t *scores, ot_ip6 *networks, int network_count,
136 int limit) { 134 int limit) {
137 size_t score = 0; 135 size_t i, score = 0;
138 int i;
139 136
140 if (!node) 137 if (!node)
141 return 0; 138 return 0;
@@ -191,11 +188,11 @@ static size_t stats_get_highscore_networks(stats_network_node *node, int depth,
191 return score; 188 return score;
192} 189}
193 190
194static size_t stats_return_busy_networks(char *reply, stats_network_node *tree, int amount, int limit) { 191static size_t stats_return_busy_networks(char *reply, stats_network_node *tree, size_t amount, int limit) {
195 ot_ip6 networks[amount]; 192 ot_ip6 networks[amount];
196 ot_ip6 node_value; 193 ot_ip6 node_value;
197 size_t scores[amount]; 194 size_t scores[amount];
198 int i; 195 size_t i;
199 char *r = reply; 196 char *r = reply;
200 197
201 memset(scores, 0, sizeof(scores)); 198 memset(scores, 0, sizeof(scores));
@@ -205,14 +202,14 @@ static size_t stats_return_busy_networks(char *reply, stats_network_node *tree,
205 stats_get_highscore_networks(tree, 0, node_value, scores, networks, amount, limit); 202 stats_get_highscore_networks(tree, 0, node_value, scores, networks, amount, limit);
206 203
207 r += sprintf(r, "Networks, limit /%d:\n", limit + STATS_NETWORK_NODE_BITWIDTH); 204 r += sprintf(r, "Networks, limit /%d:\n", limit + STATS_NETWORK_NODE_BITWIDTH);
208 for (i = amount - 1; i >= 0; --i) { 205 for (i = amount; i > 0; --i) {
209 if (scores[i]) { 206 if (scores[i - 1]) {
210 r += sprintf(r, "%08zd: ", scores[i]); 207 r += sprintf(r, "%08zd: ", scores[i - 1]);
211 // #ifdef WANT_V6 208 // #ifdef WANT_V6
212 r += fmt_ip6c(r, networks[i]); 209 r += fmt_ip6c(r, networks[i - 1]);
213#if 0 210#if 0
214 // XXX 211 // XXX
215 r += fmt_ip4( r, networks[i]); 212 r += fmt_ip4( r, networks[i - 1]);
216#endif 213#endif
217 *r++ = '\n'; 214 *r++ = '\n';
218 } 215 }
@@ -225,8 +222,7 @@ static size_t stats_return_busy_networks(char *reply, stats_network_node *tree,
225static size_t stats_slash24s_txt(char *reply, size_t amount) { 222static size_t stats_slash24s_txt(char *reply, size_t amount) {
226 stats_network_node *slash24s_network_counters_root = NULL; 223 stats_network_node *slash24s_network_counters_root = NULL;
227 char *r = reply; 224 char *r = reply;
228 int bucket; 225 size_t i, bucket, peer_size = OT_PEER_SIZE4;
229 size_t i, peer_size = OT_PEER_SIZE4;
230 226
231 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) { 227 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
232 ot_vector *torrents_list = mutex_bucket_lock(bucket); 228 ot_vector *torrents_list = mutex_bucket_lock(bucket);
@@ -300,72 +296,69 @@ static int torrent_statter(ot_torrent *torrent, uintptr_t data) {
300} 296}
301 297
302/* Converter function from memory to human readable hex strings */ 298/* Converter function from memory to human readable hex strings */
303static char *to_hex(char *d, uint8_t *s) {
304 char *m = "0123456789ABCDEF";
305 char *t = d;
306 char *e = d + 40;
307 while (d < e) {
308 *d++ = m[*s >> 4];
309 *d++ = m[*s++ & 15];
310 }
311 *d = 0;
312 return t;
313}
314
315typedef struct { 299typedef struct {
316 size_t val; 300 size_t val;
317 ot_hash hash; 301 ot_hash hash;
318} ot_record; 302} ot_record;
319 303
304static inline void stats_insert_into_highscore(size_t val, ot_record* list, size_t amount, ot_hash* hash) {
305 size_t i;
306 for (i = 0; (i < amount) && (list[i].val > val); ++i)
307 {}
308 /* if we score better than a record in the list, we need to make room in the high
309 score array by moving lower scoring entries up */
310 if (i < amount) {
311 memmove(list + i + 1, list + i, (amount - i - 1) * sizeof(ot_record));
312
313 memcpy(&list[i].hash, hash, sizeof(ot_hash));
314 list[i].val = val;
315 }
316}
317
318
320/* Fetches stats from tracker */ 319/* Fetches stats from tracker */
321size_t stats_top_txt(char *reply, int amount) { 320size_t stats_top_txt(char *reply, size_t amount) {
322 size_t j; 321 size_t j, idx, bucket;
323 ot_record top100s[100], top100c[100]; 322 ot_record top100s[100], top100c[100], top100l[100];
324 char *r = reply, hex_out[42]; 323 char *r = reply, hex_out[42];
325 int idx, bucket;
326 324
327 if (amount > 100) 325 if (amount > 100)
328 amount = 100; 326 amount = 100;
329 327
330 byte_zero(top100s, sizeof(top100s)); 328 memset(top100s, 0, sizeof(top100s));
331 byte_zero(top100c, sizeof(top100c)); 329 memset(top100c, 0, sizeof(top100c));
330 memset(top100l, 0, sizeof(top100l));
332 331
332 /* Iterate over complete torrent list */
333 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) { 333 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
334 ot_vector *torrents_list = mutex_bucket_lock(bucket); 334 ot_vector *torrents_list = mutex_bucket_lock(bucket);
335 for (j = 0; j < torrents_list->size; ++j) { 335 for (j = 0; j < torrents_list->size; ++j) {
336 ot_torrent *torrent = (ot_torrent *)(torrents_list->data) + j; 336 ot_torrent *torrent = (ot_torrent *)(torrents_list->data) + j;
337 size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count; 337 size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count;
338 size_t seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count; 338 size_t seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count;
339 idx = amount - 1; 339 size_t leech_count = peer_count - seed_count;
340 while ((idx >= 0) && (peer_count > top100c[idx].val)) 340
341 --idx; 341 stats_insert_into_highscore(peer_count, top100c, amount, &torrent->hash);
342 if (idx++ != amount - 1) { 342 stats_insert_into_highscore(seed_count, top100s, amount, &torrent->hash);
343 memmove(top100c + idx + 1, top100c + idx, (amount - 1 - idx) * sizeof(ot_record)); 343 stats_insert_into_highscore(leech_count, top100l, amount, &torrent->hash);
344 memcpy(&top100c[idx].hash, &torrent->hash, sizeof(ot_hash));
345 top100c[idx].val = peer_count;
346 }
347 idx = amount - 1;
348 while ((idx >= 0) && (seed_count > top100s[idx].val))
349 --idx;
350 if (idx++ != amount - 1) {
351 memmove(top100s + idx + 1, top100s + idx, (amount - 1 - idx) * sizeof(ot_record));
352 memcpy(&top100s[idx].hash, &torrent->hash, sizeof(ot_hash));
353 top100s[idx].val = seed_count;
354 }
355 } 344 }
356 mutex_bucket_unlock(bucket, 0); 345 mutex_bucket_unlock(bucket, 0);
357 if (!g_opentracker_running) 346 if (!g_opentracker_running)
358 return 0; 347 return 0;
359 } 348 }
360 349
361 r += sprintf(r, "Top %d torrents by peers:\n", amount); 350 r += sprintf(r, "Top %zd torrents by peers:\n", amount);
362 for (idx = 0; idx < amount; ++idx) 351 for (idx = 0; idx < amount; ++idx)
363 if (top100c[idx].val) 352 if (top100c[idx].val)
364 r += sprintf(r, "\t%zd\t%s\n", top100c[idx].val, to_hex(hex_out, top100c[idx].hash)); 353 r += sprintf(r, "\t%zd\t%s\n", top100c[idx].val, hex_fromhash(hex_out, top100c[idx].hash));
365 r += sprintf(r, "Top %d torrents by seeds:\n", amount); 354 r += sprintf(r, "Top %zd torrents by seeds:\n", amount);
366 for (idx = 0; idx < amount; ++idx) 355 for (idx = 0; idx < amount; ++idx)
367 if (top100s[idx].val) 356 if (top100s[idx].val)
368 r += sprintf(r, "\t%zd\t%s\n", top100s[idx].val, to_hex(hex_out, top100s[idx].hash)); 357 r += sprintf(r, "\t%zd\t%s\n", top100s[idx].val, hex_fromhash(hex_out, top100s[idx].hash));
358 r += sprintf(r, "Top %zd torrents by leechers:\n", amount);
359 for (idx = 0; idx < amount; ++idx)
360 if (top100l[idx].val)
361 r += sprintf(r, "\t%zd\t%s\n", top100l[idx].val, hex_fromhash(hex_out, top100l[idx].hash));
369 362
370 return r - reply; 363 return r - reply;
371} 364}
@@ -463,7 +456,7 @@ static size_t stats_return_numwants(char *reply) {
463#endif 456#endif
464 457
465#ifdef WANT_FULLLOG_NETWORKS 458#ifdef WANT_FULLLOG_NETWORKS
466static void stats_return_fulllog(int *iovec_entries, struct iovec **iovector, char *r) { 459static void stats_return_fulllog(size_t *iovec_entries, struct iovec **iovector, char *r) {
467 ot_log *loglist = g_logchain_first, *llnext; 460 ot_log *loglist = g_logchain_first, *llnext;
468 char *re = r + OT_STATS_TMPSIZE; 461 char *re = r + OT_STATS_TMPSIZE;
469 462
@@ -476,9 +469,9 @@ static void stats_return_fulllog(int *iovec_entries, struct iovec **iovector, ch
476 return; 469 return;
477 re = r + 32 * OT_STATS_TMPSIZE; 470 re = r + 32 * OT_STATS_TMPSIZE;
478 } 471 }
479 r += sprintf(r, "%08ld: ", loglist->time); 472 r += sprintf(r, "%08ld: ", loglist->time);
480 r += fmt_ip6c(r, loglist->ip); 473 r += fmt_ip6c(r, loglist->ip);
481 *r++ = '\n'; 474 *r++ = '\n';
482 memcpy(r, loglist->data, loglist->size); 475 memcpy(r, loglist->data, loglist->size);
483 r += loglist->size; 476 r += loglist->size;
484 *r++ = '\n'; 477 *r++ = '\n';
@@ -518,10 +511,8 @@ static size_t stats_return_everything(char *reply) {
518 r += sprintf(r, " <completed>\n <count>%llu</count>\n </completed>\n", ot_overall_completed); 511 r += sprintf(r, " <completed>\n <count>%llu</count>\n </completed>\n", ot_overall_completed);
519 r += sprintf(r, " <connections>\n"); 512 r += sprintf(r, " <connections>\n");
520 r += sprintf(r, " <tcp>\n <accept>%llu</accept>\n <announce>%llu</announce>\n <scrape>%llu</scrape>\n </tcp>\n", 513 r += sprintf(r, " <tcp>\n <accept>%llu</accept>\n <announce>%llu</announce>\n <scrape>%llu</scrape>\n </tcp>\n",
521 ot_overall_tcp_connections, ot_overall_tcp_successfulannounces, ot_overall_tcp_successfulscrapes); 514 ot_overall_tcp_connections, ot_overall_tcp_successfulannounces, ot_overall_tcp_successfulscrapes);
522 r += sprintf( 515 r += sprintf(r, " <udp>\n <overall>%llu</overall>\n <connect>%llu</connect>\n <announce>%llu</announce>\n <scrape>%llu</scrape>\n <missmatch>%llu</missmatch>\n </udp>\n",
523 r,
524 " <udp>\n <overall>%llu</overall>\n <connect>%llu</connect>\n <announce>%llu</announce>\n <scrape>%llu</scrape>\n <missmatch>%llu</missmatch>\n </udp>\n",
525 ot_overall_udp_connections, ot_overall_udp_connects, ot_overall_udp_successfulannounces, ot_overall_udp_successfulscrapes, 516 ot_overall_udp_connections, ot_overall_udp_connects, ot_overall_udp_successfulannounces, ot_overall_udp_successfulscrapes,
526 ot_overall_udp_connectionidmissmatches); 517 ot_overall_udp_connectionidmissmatches);
527 r += sprintf(r, " <livesync>\n <count>%llu</count>\n </livesync>\n", ot_overall_sync_count); 518 r += sprintf(r, " <livesync>\n <count>%llu</count>\n </livesync>\n", ot_overall_sync_count);
@@ -541,14 +532,10 @@ static size_t stats_return_everything(char *reply) {
541 return r - reply; 532 return r - reply;
542} 533}
543 534
544extern const char *g_version_opentracker_c, *g_version_accesslist_c, *g_version_clean_c, *g_version_fullscrape_c, *g_version_http_c, *g_version_iovec_c,
545 *g_version_mutex_c, *g_version_stats_c, *g_version_udp_c, *g_version_vector_c, *g_version_scan_urlencoded_query_c, *g_version_trackerlogic_c,
546 *g_version_livesync_c, *g_version_rijndael_c;
547
548size_t stats_return_tracker_version(char *reply) { 535size_t stats_return_tracker_version(char *reply) {
549 return sprintf(reply, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s", g_version_opentracker_c, g_version_accesslist_c, g_version_clean_c, g_version_fullscrape_c, 536#define QUOTE(name) #name
550 g_version_http_c, g_version_iovec_c, g_version_mutex_c, g_version_stats_c, g_version_udp_c, g_version_vector_c, 537#define SQUOTE(name) QUOTE(name)
551 g_version_scan_urlencoded_query_c, g_version_trackerlogic_c, g_version_livesync_c, g_version_rijndael_c); 538 return sprintf(reply, "https://erdgeist.org/gitweb/opentracker/commit/?id=" SQUOTE(GIT_VERSION) "\n");
552} 539}
553 540
554size_t return_stats_for_tracker(char *reply, int mode, int format) { 541size_t return_stats_for_tracker(char *reply, int mode, int format) {
@@ -583,7 +570,7 @@ size_t return_stats_for_tracker(char *reply, int mode, int format) {
583 } 570 }
584} 571}
585 572
586static void stats_make(int *iovec_entries, struct iovec **iovector, ot_tasktype mode) { 573static void stats_make(size_t *iovec_entries, struct iovec **iovector, ot_tasktype mode) {
587 char *r; 574 char *r;
588 575
589 *iovec_entries = 0; 576 *iovec_entries = 0;
@@ -669,9 +656,9 @@ void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_
669 localtime_r(&ttt, &time_now); 656 localtime_r(&ttt, &time_now);
670 strftime(timestring, sizeof(timestring), "%FT%T%z", &time_now); 657 strftime(timestring, sizeof(timestring), "%FT%T%z", &time_now);
671 658
672 to_hex(hash_hex, *ws->hash); 659 hex_fromhash(hash_hex, *ws->hash);
673 if (ws->peer_id) 660 if (ws->peer_id)
674 to_hex(peerid_hex, (uint8_t *)ws->peer_id); 661 hex_fromhash(peerid_hex, (uint8_t *)ws->peer_id);
675 else { 662 else {
676 *peerid_hex = 0; 663 *peerid_hex = 0;
677 } 664 }
@@ -740,7 +727,7 @@ void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_
740 } 727 }
741} 728}
742 729
743void stats_cleanup() { 730void stats_cleanup(void) {
744#ifdef WANT_SPOT_WOODPECKER 731#ifdef WANT_SPOT_WOODPECKER
745 pthread_mutex_lock(&g_woodpeckers_mutex); 732 pthread_mutex_lock(&g_woodpeckers_mutex);
746 stats_shift_down_network_count(&stats_woodpeckers_tree, 0, 1); 733 stats_shift_down_network_count(&stats_woodpeckers_tree, 0, 1);
@@ -749,7 +736,7 @@ void stats_cleanup() {
749} 736}
750 737
751static void *stats_worker(void *args) { 738static void *stats_worker(void *args) {
752 int iovec_entries; 739 size_t iovec_entries;
753 struct iovec *iovector; 740 struct iovec *iovector;
754 741
755 (void)args; 742 (void)args;
@@ -767,13 +754,11 @@ static void *stats_worker(void *args) {
767void stats_deliver(int64 sock, int tasktype) { mutex_workqueue_pushtask(sock, tasktype); } 754void stats_deliver(int64 sock, int tasktype) { mutex_workqueue_pushtask(sock, tasktype); }
768 755
769static pthread_t thread_id; 756static pthread_t thread_id;
770void stats_init() { 757void stats_init(void) {
771 ot_start_time = g_now_seconds; 758 ot_start_time = g_now_seconds;
772 pthread_create(&thread_id, NULL, stats_worker, NULL); 759 pthread_create(&thread_id, NULL, stats_worker, NULL);
773} 760}
774 761
775void stats_deinit() { 762void stats_deinit(void) {
776 pthread_cancel(thread_id); 763 pthread_cancel(thread_id);
777} 764}
778
779const char *g_version_stats_c = "$Source$: $Revision$\n";
diff --git a/ot_stats.h b/ot_stats.h
index a354c19..8ed2b1e 100644
--- a/ot_stats.h
+++ b/ot_stats.h
@@ -19,6 +19,7 @@ typedef enum {
19 EVENT_SCRAPE, 19 EVENT_SCRAPE,
20 EVENT_FULLSCRAPE_REQUEST, 20 EVENT_FULLSCRAPE_REQUEST,
21 EVENT_FULLSCRAPE_REQUEST_GZIP, 21 EVENT_FULLSCRAPE_REQUEST_GZIP,
22 EVENT_FULLSCRAPE_REQUEST_ZSTD,
22 EVENT_FULLSCRAPE, /* TCP only */ 23 EVENT_FULLSCRAPE, /* TCP only */
23 EVENT_FAILED, 24 EVENT_FAILED,
24 EVENT_BUCKET_LOCKED, 25 EVENT_BUCKET_LOCKED,
@@ -48,7 +49,4 @@ size_t stats_return_tracker_version(char *reply);
48void stats_init(void); 49void stats_init(void);
49void stats_deinit(void); 50void stats_deinit(void);
50 51
51extern const char *g_version_rijndael_c;
52extern const char *g_version_livesync_c;
53
54#endif 52#endif
diff --git a/ot_sync.c b/ot_sync.c
index 259d4de..d3d30fa 100644
--- a/ot_sync.c
+++ b/ot_sync.c
@@ -7,12 +7,10 @@
7#include <pthread.h> 7#include <pthread.h>
8#include <stdio.h> 8#include <stdio.h>
9#include <string.h> 9#include <string.h>
10#include <sys/mman.h>
11#include <sys/types.h> 10#include <sys/types.h>
12#include <sys/uio.h> 11#include <sys/uio.h>
13 12
14/* Libowfat */ 13/* Libowfat */
15#include "byte.h"
16#include "io.h" 14#include "io.h"
17#include "scan.h" 15#include "scan.h"
18 16
@@ -36,15 +34,15 @@ int add_changeset_to_tracker(uint8_t *data, size_t len) {
36 uint8_t *end = data + len; 34 uint8_t *end = data + len;
37 unsigned long peer_count; 35 unsigned long peer_count;
38 36
39 /* We do know, that the string is \n terminated, so it cant 37 /* We do know, that the string is \n terminated, so it can't
40 overflow */ 38 overflow */
41 if (byte_diff(data, 8, "d4:syncd")) 39 if (!memcmp(data, "d4:syncd", 8))
42 return -1; 40 return -1;
43 data += 8; 41 data += 8;
44 42
45 while (1) { 43 while (1) {
46 if (byte_diff(data, 3, "20:")) { 44 if (!memcmp(data, "20:", 3)) {
47 if (byte_diff(data, 2, "ee")) 45 if (!memcmp(data, "ee", 2))
48 return -1; 46 return -1;
49 return 0; 47 return 0;
50 } 48 }
@@ -72,9 +70,9 @@ int add_changeset_to_tracker(uint8_t *data, size_t len) {
72/* Proposed output format 70/* Proposed output format
73 d4:syncd20:<info_hash>8*N:(xxxxyyyy)*Nee 71 d4:syncd20:<info_hash>8*N:(xxxxyyyy)*Nee
74*/ 72*/
75static void sync_make(int *iovec_entries, struct iovec **iovector) { 73static void sync_make(size_t *iovec_entries, struct iovec **iovector) {
76 int bucket; 74 size_t bucket;
77 char *r, *re; 75 char *r, *re;
78 76
79 /* Setup return vector... */ 77 /* Setup return vector... */
80 *iovec_entries = 0; 78 *iovec_entries = 0;
@@ -140,7 +138,7 @@ static void sync_make(int *iovec_entries, struct iovec **iovector) {
140 It grabs tasks from mutex_tasklist and delivers results back 138 It grabs tasks from mutex_tasklist and delivers results back
141*/ 139*/
142static void *sync_worker(void *args) { 140static void *sync_worker(void *args) {
143 int iovec_entries; 141 size_t iovec_entries;
144 struct iovec *iovector; 142 struct iovec *iovector;
145 143
146 args = args; 144 args = args;
@@ -170,5 +168,3 @@ void sync_deliver( int64 socket ) {
170} 168}
171 169
172#endif 170#endif
173
174const char *g_version_sync_c = "$Source$: $Revision$\n";
diff --git a/ot_udp.c b/ot_udp.c
index 912c7e4..82aa8bd 100644
--- a/ot_udp.c
+++ b/ot_udp.c
@@ -16,6 +16,7 @@
16#include "socket.h" 16#include "socket.h"
17 17
18/* Opentracker */ 18/* Opentracker */
19#include "ot_random.h"
19#include "ot_rijndael.h" 20#include "ot_rijndael.h"
20#include "ot_stats.h" 21#include "ot_stats.h"
21#include "ot_udp.h" 22#include "ot_udp.h"
@@ -25,26 +26,19 @@
25static const uint8_t g_static_connid[8] = { 0x23, 0x42, 0x05, 0x17, 0xde, 0x41, 0x50, 0xff }; 26static const uint8_t g_static_connid[8] = { 0x23, 0x42, 0x05, 0x17, 0xde, 0x41, 0x50, 0xff };
26#endif 27#endif
27static uint32_t g_rijndael_round_key[44] = {0}; 28static uint32_t g_rijndael_round_key[44] = {0};
28static uint32_t g_key_of_the_hour[2] = {0}; 29static volatile uint32_t g_key_of_the_hour[2] = {0};
29static ot_time g_hour_of_the_key; 30static volatile ot_time g_hour_of_the_key;
30 31
31static void udp_generate_rijndael_round_key() { 32static pthread_mutex_t udp_start_mutex = PTHREAD_MUTEX_INITIALIZER;
33static pthread_cond_t udp_start_cond = PTHREAD_COND_INITIALIZER;
34static int g_opentracker_started = 0;
35
36static void udp_generate_rijndael_round_key(void) {
32 uint32_t key[16]; 37 uint32_t key[16];
33#ifdef WANT_ARC4RANDOM 38 random_bytes(key, sizeof(key));
34 arc4random_buf(&key[0], sizeof(key));
35#else
36 key[0] = random();
37 key[1] = random();
38 key[2] = random();
39 key[3] = random();
40#endif
41 rijndaelKeySetupEnc128(g_rijndael_round_key, (uint8_t *)key); 39 rijndaelKeySetupEnc128(g_rijndael_round_key, (uint8_t *)key);
42 40
43#ifdef WANT_ARC4RANDOM 41 g_key_of_the_hour[0] = random_uint32();
44 g_key_of_the_hour[0] = arc4random();
45#else
46 g_key_of_the_hour[0] = random();
47#endif
48 g_hour_of_the_key = g_now_minutes; 42 g_hour_of_the_key = g_now_minutes;
49} 43}
50 44
@@ -52,20 +46,26 @@ static void udp_generate_rijndael_round_key() {
52static void udp_make_connectionid(uint32_t connid[2], const ot_ip6 remoteip, int age) { 46static void udp_make_connectionid(uint32_t connid[2], const ot_ip6 remoteip, int age) {
53 uint32_t plain[4], crypt[4]; 47 uint32_t plain[4], crypt[4];
54 int i; 48 int i;
55 if (g_now_minutes + 60 > g_hour_of_the_key) { 49 uint32_t current_key_of_the_hour;
56 g_hour_of_the_key = g_now_minutes; 50
57 g_key_of_the_hour[1] = g_key_of_the_hour[0]; 51 if (g_now_minutes - g_hour_of_the_key >= 60) {
58#ifdef WANT_ARC4RANDOM 52 uint32_t old_key_of_the_hour = g_key_of_the_hour[0];
59 g_key_of_the_hour[0] = arc4random(); 53 uint32_t new_key_of_the_hour = random_uint32();
60#else 54 /* If in the meantime another thread has performed
61 g_key_of_the_hour[0] = random(); 55 key rotation, do not overwrite their results */
62#endif 56 if (g_now_minutes - g_hour_of_the_key >= 60) {
57 /* Upgrade en bloc */
58 g_hour_of_the_key = g_now_minutes;
59 g_key_of_the_hour[0] = new_key_of_the_hour;
60 g_key_of_the_hour[1] = old_key_of_the_hour;
61 }
63 } 62 }
64 63
65 memcpy(plain, remoteip, sizeof(plain)); 64 memcpy(plain, remoteip, sizeof(plain));
65 current_key_of_the_hour = g_key_of_the_hour[age];
66 for (i = 0; i < 4; ++i) 66 for (i = 0; i < 4; ++i)
67 plain[i] ^= g_key_of_the_hour[age]; 67 plain[i] ^= current_key_of_the_hour;
68 rijndaelEncrypt128(g_rijndael_round_key, (uint8_t *)remoteip, (uint8_t *)crypt); 68 rijndaelEncrypt128(g_rijndael_round_key, (uint8_t *)plain, (uint8_t *)crypt);
69 connid[0] = crypt[0] ^ crypt[1]; 69 connid[0] = crypt[0] ^ crypt[1];
70 connid[1] = crypt[2] ^ crypt[3]; 70 connid[1] = crypt[2] ^ crypt[3];
71} 71}
@@ -213,6 +213,11 @@ static void *udp_worker(void *args) {
213 ws.debugbuf = malloc(G_DEBUGBUF_SIZE); 213 ws.debugbuf = malloc(G_DEBUGBUF_SIZE);
214#endif 214#endif
215 215
216 pthread_mutex_lock(&udp_start_mutex);
217 while(g_opentracker_running && !g_opentracker_started)
218 pthread_cond_wait(&udp_start_cond, &udp_start_mutex);
219 pthread_mutex_unlock(&udp_start_mutex);
220
216 while (g_opentracker_running) 221 while (g_opentracker_running)
217 handle_udp6(sock, &ws); 222 handle_udp6(sock, &ws);
218 223
@@ -235,4 +240,9 @@ void udp_init(int64 sock, unsigned int worker_count) {
235 pthread_create(&thread_id, NULL, udp_worker, (void *)sock); 240 pthread_create(&thread_id, NULL, udp_worker, (void *)sock);
236} 241}
237 242
238const char *g_version_udp_c = "$Source$: $Revision$\n"; 243void udp_kickoff(void) {
244 pthread_mutex_lock(&udp_start_mutex);
245 g_opentracker_started = 1;
246 pthread_cond_broadcast(&udp_start_cond);
247 pthread_mutex_unlock(&udp_start_mutex);
248}
diff --git a/ot_udp.h b/ot_udp.h
index a71a3d9..db48696 100644
--- a/ot_udp.h
+++ b/ot_udp.h
@@ -7,6 +7,7 @@
7#define OT_UDP_H__ 7#define OT_UDP_H__
8 8
9void udp_init(int64 sock, unsigned int worker_count); 9void udp_init(int64 sock, unsigned int worker_count);
10void udp_kickoff(void);
10int handle_udp6(int64 serversocket, struct ot_workstruct *ws); 11int handle_udp6(int64 serversocket, struct ot_workstruct *ws);
11 12
12#endif 13#endif
diff --git a/ot_vector.c b/ot_vector.c
index 744306f..e3c3d3f 100644
--- a/ot_vector.c
+++ b/ot_vector.c
@@ -13,10 +13,6 @@
13/* Opentracker */ 13/* Opentracker */
14#include "trackerlogic.h" 14#include "trackerlogic.h"
15 15
16/* Libowfat */
17#include "uint16.h"
18#include "uint32.h"
19
20static int vector_compare_peer6(const void *peer1, const void *peer2) { return memcmp(peer1, peer2, OT_PEER_COMPARE_SIZE6); } 16static int vector_compare_peer6(const void *peer1, const void *peer2) { return memcmp(peer1, peer2, OT_PEER_COMPARE_SIZE6); }
21static int vector_compare_peer4(const void *peer1, const void *peer2) { return memcmp(peer1, peer2, OT_PEER_COMPARE_SIZE4); } 17static int vector_compare_peer4(const void *peer1, const void *peer2) { return memcmp(peer1, peer2, OT_PEER_COMPARE_SIZE4); }
22 18
@@ -45,7 +41,7 @@ void *binary_search(const void *const key, const void *base, const size_t member
45 return (void *)base; 41 return (void *)base;
46} 42}
47 43
48static uint8_t vector_hash_peer(ot_peer const *peer, size_t compare_size, int bucket_count) { 44static uint8_t vector_hash_peer(ot_peer const *peer, size_t compare_size, size_t bucket_count) {
49 unsigned int hash = 5381; 45 unsigned int hash = 5381;
50 uint8_t *p = (uint8_t *)peer; 46 uint8_t *p = (uint8_t *)peer;
51 while (compare_size--) 47 while (compare_size--)
@@ -67,12 +63,15 @@ void *vector_find_or_insert(ot_vector *vector, void *key, size_t member_size, si
67 return match; 63 return match;
68 64
69 if (vector->size + 1 > vector->space) { 65 if (vector->size + 1 > vector->space) {
70 size_t new_space = vector->space ? OT_VECTOR_GROW_RATIO * vector->space : OT_VECTOR_MIN_MEMBERS; 66 size_t new_space = vector->space ? OT_VECTOR_GROW_RATIO * vector->space : OT_VECTOR_MIN_MEMBERS;
71 uint8_t *new_data = realloc(vector->data, new_space * member_size); 67 ptrdiff_t match_off = match - (uint8_t *)vector->data;
68 uint8_t *new_data = realloc(vector->data, new_space * member_size);
69
72 if (!new_data) 70 if (!new_data)
73 return NULL; 71 return NULL;
72
74 /* Adjust pointer if it moved by realloc */ 73 /* Adjust pointer if it moved by realloc */
75 match = new_data + (match - (uint8_t *)vector->data); 74 match = new_data + match_off;
76 75
77 vector->data = new_data; 76 vector->data = new_data;
78 vector->space = new_space; 77 vector->space = new_space;
@@ -169,7 +168,7 @@ void vector_remove_torrent(ot_vector *vector, ot_torrent *match) {
169 } 168 }
170} 169}
171 170
172void vector_clean_list(ot_vector *vector, int num_buckets) { 171void vector_clean_list(ot_vector *vector, size_t num_buckets) {
173 while (num_buckets--) 172 while (num_buckets--)
174 free(vector[num_buckets].data); 173 free(vector[num_buckets].data);
175 free(vector); 174 free(vector);
@@ -177,7 +176,7 @@ void vector_clean_list(ot_vector *vector, int num_buckets) {
177} 176}
178 177
179void vector_redistribute_buckets(ot_peerlist *peer_list, size_t peer_size) { 178void vector_redistribute_buckets(ot_peerlist *peer_list, size_t peer_size) {
180 int tmp, bucket, bucket_size_new, num_buckets_new, num_buckets_old = 1; 179 size_t tmp, bucket, bucket_size_new, num_buckets_new, num_buckets_old = 1;
181 ot_vector *bucket_list_new, *bucket_list_old = &peer_list->peers; 180 ot_vector *bucket_list_new, *bucket_list_old = &peer_list->peers;
182 int (*sort_func)(const void *, const void *) = peer_size == OT_PEER_SIZE6 ? &vector_compare_peer6 : &vector_compare_peer4; 181 int (*sort_func)(const void *, const void *) = peer_size == OT_PEER_SIZE6 ? &vector_compare_peer6 : &vector_compare_peer4;
183 182
@@ -219,22 +218,26 @@ void vector_redistribute_buckets(ot_peerlist *peer_list, size_t peer_size) {
219 for (bucket = 0; bucket < num_buckets_new; ++bucket) { 218 for (bucket = 0; bucket < num_buckets_new; ++bucket) {
220 bucket_list_new[bucket].space = bucket_size_new; 219 bucket_list_new[bucket].space = bucket_size_new;
221 bucket_list_new[bucket].data = malloc(bucket_size_new * peer_size); 220 bucket_list_new[bucket].data = malloc(bucket_size_new * peer_size);
222 if (!bucket_list_new[bucket].data) 221 if (!bucket_list_new[bucket].data) {
223 return vector_clean_list(bucket_list_new, num_buckets_new); 222 vector_clean_list(bucket_list_new, num_buckets_new);
223 return;
224 }
224 } 225 }
225 226
226 /* Now sort them into the correct bucket */ 227 /* Now sort them into the correct bucket */
227 for (bucket = 0; bucket < num_buckets_old; ++bucket) { 228 for (bucket = 0; bucket < num_buckets_old; ++bucket) {
228 ot_peer *peers_old = bucket_list_old[bucket].data; 229 ot_peer *peers_old = bucket_list_old[bucket].data;
229 int peer_count_old = bucket_list_old[bucket].size; 230 size_t peer_count_old = bucket_list_old[bucket].size;
230 while (peer_count_old--) { 231 while (peer_count_old--) {
231 ot_vector *bucket_dest = bucket_list_new; 232 ot_vector *bucket_dest = bucket_list_new;
232 if (num_buckets_new > 1) 233 if (num_buckets_new > 1)
233 bucket_dest += vector_hash_peer(peers_old, OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size), num_buckets_new); 234 bucket_dest += vector_hash_peer(peers_old, OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size), num_buckets_new);
234 if (bucket_dest->size + 1 > bucket_dest->space) { 235 if (bucket_dest->size + 1 > bucket_dest->space) {
235 void *tmp = realloc(bucket_dest->data, peer_size * OT_VECTOR_GROW_RATIO * bucket_dest->space); 236 void *tmp = realloc(bucket_dest->data, peer_size * OT_VECTOR_GROW_RATIO * bucket_dest->space);
236 if (!tmp) 237 if (!tmp) {
237 return vector_clean_list(bucket_list_new, num_buckets_new); 238 vector_clean_list(bucket_list_new, num_buckets_new);
239 return;
240 }
238 bucket_dest->data = tmp; 241 bucket_dest->data = tmp;
239 bucket_dest->space *= OT_VECTOR_GROW_RATIO; 242 bucket_dest->space *= OT_VECTOR_GROW_RATIO;
240 } 243 }
@@ -282,5 +285,3 @@ void vector_fixup_peers(ot_vector *vector, size_t peer_size) {
282 if (need_fix) 285 if (need_fix)
283 vector->data = realloc(vector->data, vector->space * peer_size); 286 vector->data = realloc(vector->data, vector->space * peer_size);
284} 287}
285
286const char *g_version_vector_c = "$Source$: $Revision$\n";
diff --git a/ot_vector.h b/ot_vector.h
index 8d41452..2618363 100644
--- a/ot_vector.h
+++ b/ot_vector.h
@@ -31,6 +31,6 @@ void vector_remove_torrent(ot_vector *vector, ot_torrent *match);
31/* For ot_clean.c */ 31/* For ot_clean.c */
32void vector_redistribute_buckets(ot_peerlist *peer_list, size_t peer_size); 32void vector_redistribute_buckets(ot_peerlist *peer_list, size_t peer_size);
33void vector_fixup_peers(ot_vector *vector, size_t peer_size); 33void vector_fixup_peers(ot_vector *vector, size_t peer_size);
34void vector_clean_list(ot_vector *vector, int num_buckets); 34void vector_clean_list(ot_vector *vector, size_t num_buckets);
35 35
36#endif 36#endif
diff --git a/proxy.c b/proxy.c
index 9946240..d728e5b 100644
--- a/proxy.c
+++ b/proxy.c
@@ -8,8 +8,6 @@
8#include <ctype.h> 8#include <ctype.h>
9#include <errno.h> 9#include <errno.h>
10#include <pthread.h> 10#include <pthread.h>
11#include <pwd.h>
12#include <signal.h>
13#include <stdint.h> 11#include <stdint.h>
14#include <stdio.h> 12#include <stdio.h>
15#include <stdlib.h> 13#include <stdlib.h>
@@ -18,7 +16,6 @@
18#include <unistd.h> 16#include <unistd.h>
19 17
20/* Libowfat */ 18/* Libowfat */
21#include "byte.h"
22#include "io.h" 19#include "io.h"
23#include "iob.h" 20#include "iob.h"
24#include "ip6.h" 21#include "ip6.h"
@@ -28,6 +25,7 @@
28 25
29/* Opentracker */ 26/* Opentracker */
30#include "ot_mutex.h" 27#include "ot_mutex.h"
28#include "ot_random.h"
31#include "ot_stats.h" 29#include "ot_stats.h"
32#include "ot_vector.h" 30#include "ot_vector.h"
33#include "trackerlogic.h" 31#include "trackerlogic.h"
@@ -142,8 +140,8 @@ size_t add_peer_to_torrent_proxy(ot_hash hash, ot_peer *peer, size_t peer_size)
142 return -1; 140 return -1;
143 } 141 }
144 142
145 byte_zero(torrent->peer_list6, sizeof(ot_peerlist)); 143 memset(torrent->peer_list6, 0, sizeof(ot_peerlist));
146 byte_zero(torrent->peer_list4, sizeof(ot_peerlist)); 144 memset(torrent->peer_list4, 0, sizeof(ot_peerlist));
147 } 145 }
148 146
149 peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4; 147 peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4;
@@ -276,12 +274,12 @@ void reset_info_block(proxy_peer *peer) {
276 * Multiple connections to/from the same ip are okay, if tracker_id doesn't match 274 * Multiple connections to/from the same ip are okay, if tracker_id doesn't match
277 * Reconnect attempts occur only twice a minute 275 * Reconnect attempts occur only twice a minute
278 */ 276 */
279static int g_connection_count; 277static size_t g_connection_count;
280static ot_time g_connection_reconn; 278static ot_time g_connection_reconn;
281static proxy_peer g_connections[MAX_PEERS]; 279static proxy_peer g_connections[MAX_PEERS];
282 280
283static void handle_reconnects(void) { 281static void handle_reconnects(void) {
284 int i; 282 size_t i;
285 for (i = 0; i < g_connection_count; ++i) 283 for (i = 0; i < g_connection_count; ++i)
286 if (PROXYPEER_NEEDSCONNECT(g_connections[i].state)) { 284 if (PROXYPEER_NEEDSCONNECT(g_connections[i].state)) {
287 int64 newfd = socket_tcp6(); 285 int64 newfd = socket_tcp6();
@@ -473,13 +471,15 @@ static void handle_write(int64 peersocket) {
473} 471}
474 472
475static void server_mainloop() { 473static void server_mainloop() {
476 int64 sock; 474 int64 sock;
475 uint32_t packet_type;
477 476
478 /* inlined livesync_init() */ 477 /* inlined livesync_init() */
479 memset(g_peerbuffer_start, 0, sizeof(g_peerbuffer_start)); 478 memset(g_peerbuffer_start, 0, sizeof(g_peerbuffer_start));
480 g_peerbuffer_pos = g_peerbuffer_start; 479 g_peerbuffer_pos = g_peerbuffer_start;
481 memcpy(g_peerbuffer_pos, &g_tracker_id, sizeof(g_tracker_id)); 480 memcpy(g_peerbuffer_pos, &g_tracker_id, sizeof(g_tracker_id));
482 uint32_pack_big((char *)g_peerbuffer_pos + sizeof(g_tracker_id), OT_SYNC_PEER); 481 packet_type = htonl(OT_SYNC_PEER);
482 memcpy(g_peerbuffer_pos + sizeof(g_tracker_id), &packet_type, sizeof(packet_type));
483 g_peerbuffer_pos += sizeof(g_tracker_id) + sizeof(uint32_t); 483 g_peerbuffer_pos += sizeof(g_tracker_id) + sizeof(uint32_t);
484 g_next_packet_time = time(NULL) + LIVESYNC_MAXDELAY; 484 g_next_packet_time = time(NULL) + LIVESYNC_MAXDELAY;
485 485
@@ -564,12 +564,8 @@ int main(int argc, char **argv) {
564 uint16_t tmpport; 564 uint16_t tmpport;
565 int scanon = 1, lbound = 0, sbound = 0; 565 int scanon = 1, lbound = 0, sbound = 0;
566 566
567 srandom(time(NULL)); 567 random_init();
568#ifdef WANT_ARC4RANDOM 568 g_tracker_id = random_uint32();
569 g_tracker_id = arc4random();
570#else
571 g_tracker_id = random();
572#endif
573 569
574 while (scanon) { 570 while (scanon) {
575 switch (getopt(argc, argv, ":l:c:L:h")) { 571 switch (getopt(argc, argv, ":l:c:L:h")) {
@@ -625,7 +621,7 @@ int main(int argc, char **argv) {
625static void *streamsync_worker(void *args) { 621static void *streamsync_worker(void *args) {
626 (void)args; 622 (void)args;
627 while (1) { 623 while (1) {
628 int bucket; 624 size_t bucket;
629 /* For each bucket... */ 625 /* For each bucket... */
630 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) { 626 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
631 /* Get exclusive access to that bucket */ 627 /* Get exclusive access to that bucket */
@@ -871,6 +867,7 @@ static void *livesync_worker(void *args) {
871 while (1) { 867 while (1) {
872 ot_ip6 in_ip; 868 ot_ip6 in_ip;
873 uint16_t in_port; 869 uint16_t in_port;
870 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); 871 size_t datalen = socket_recv4(g_socket_in, (char *)g_inbuffer, LIVESYNC_INCOMING_BUFFSIZE, 12 + (char *)in_ip, &in_port);
875 872
876 /* Expect at least tracker id and packet type */ 873 /* Expect at least tracker id and packet type */
@@ -880,7 +877,9 @@ static void *livesync_worker(void *args) {
880 /* drop packet coming from ourselves */ 877 /* drop packet coming from ourselves */
881 continue; 878 continue;
882 } 879 }
883 switch (uint32_read_big((char *)g_inbuffer + sizeof(g_tracker_id))) { 880 memcpy(&packet_type, g_inbuffer + sizeof(g_tracker_id), sizeof(packet_type));
881
882 switch (ntohl(packet_type)) {
884 case OT_SYNC_PEER4: 883 case OT_SYNC_PEER4:
885 livesync_handle_peersync(datalen, OT_PEER_SIZE4); 884 livesync_handle_peersync(datalen, OT_PEER_SIZE4);
886 break; 885 break;
@@ -888,7 +887,7 @@ static void *livesync_worker(void *args) {
888 livesync_handle_peersync(datalen, OT_PEER_SIZE6); 887 livesync_handle_peersync(datalen, OT_PEER_SIZE6);
889 break; 888 break;
890 default: 889 default:
891 // fprintf( stderr, "Received an unknown live sync packet type %u.\n", uint32_read_big( sizeof( g_tracker_id ) + (char*)g_inbuffer ) ); 890 // fprintf( stderr, "Received an unknown live sync packet type %u.\n", ntohl( packet_type ) );
892 break; 891 break;
893 } 892 }
894 } 893 }
diff --git a/scan_urlencoded_query.c b/scan_urlencoded_query.c
index 2d3599d..50aced3 100644
--- a/scan_urlencoded_query.c
+++ b/scan_urlencoded_query.c
@@ -6,9 +6,6 @@
6/* Opentracker */ 6/* Opentracker */
7#include "scan_urlencoded_query.h" 7#include "scan_urlencoded_query.h"
8 8
9/* Libwofat */
10#include "scan.h"
11
12/* System */ 9/* System */
13#include <string.h> 10#include <string.h>
14 11
@@ -162,5 +159,3 @@ ssize_t scan_fixed_int(char *data, size_t len, int *tmp) {
162 *tmp = -*tmp; 159 *tmp = -*tmp;
163 return len; 160 return len;
164} 161}
165
166const char *g_version_scan_urlencoded_query_c = "$Source$: $Revision$\n";
diff --git a/tests/runner.c b/tests/runner.c
new file mode 100644
index 0000000..bf4fbde
--- /dev/null
+++ b/tests/runner.c
@@ -0,0 +1,37 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <string.h>
8
9/* Opentracker */
10#include "test.h"
11
12struct module {
13 const char *name;
14 void (*run)(void);
15};
16
17static const struct module modules[] = {{"scan_urlencoded_query", test_scan_urlencoded_query},
18 {"ot_iovec", test_iovec},
19 {"ot_vector", test_vector},
20 {"ot_random", test_random},
21 {"ot_hex", test_hex},
22 {"ot_accesslist", test_accesslist},
23 {NULL, NULL}};
24
25/* Run every module, or only those whose name contains argv[1] */
26int main(int argc, char **argv) {
27 const struct module *m;
28
29 for (m = modules; m->name; ++m) {
30 if (argc > 1 && !strstr(m->name, argv[1]))
31 continue;
32 test_module(m->name);
33 m->run();
34 }
35
36 return test_summary();
37}
diff --git a/tests/stubs.c b/tests/stubs.c
new file mode 100644
index 0000000..febdd7b
--- /dev/null
+++ b/tests/stubs.c
@@ -0,0 +1,34 @@
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/* Stubs for symbols the modules under test reference but never reach along
7 the paths the tests exercise. If a test ever does reach one, the missing
8 behaviour should be supplied here rather than the test weakened. */
9
10/* Opentracker */
11#include "trackerlogic.h"
12
13/* ot_vector.c calls this from vector_clean_list, which no test drives yet. */
14void free_peerlist(ot_peerlist *peer_list) { (void)peer_list; }
15
16/* ot_accesslist.c is built with WANT_ACCESSLIST_BLACK so that the hash parser
17 is reachable. These four belong to accesslist_readfile and the reload
18 worker, neither of which any test drives. */
19time_t g_now_seconds;
20volatile int g_opentracker_running;
21
22const char *mmap_read(const char *filename, size_t *filesize);
23const char *mmap_read(const char *filename, size_t *filesize) {
24 (void)filename;
25 *filesize = 0;
26 return NULL;
27}
28
29int mmap_unmap(const char *mapped, size_t maplen);
30int mmap_unmap(const char *mapped, size_t maplen) {
31 (void)mapped;
32 (void)maplen;
33 return 0;
34}
diff --git a/tests/t_accesslist.c b/tests/t_accesslist.c
new file mode 100644
index 0000000..9925acd
--- /dev/null
+++ b/tests/t_accesslist.c
@@ -0,0 +1,122 @@
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 <stdlib.h>
8#include <string.h>
9
10/* Opentracker */
11#include "ot_accesslist.h"
12#include "test.h"
13
14#define A "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
15#define B "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
16#define C "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
17
18static ot_hash dest[8];
19
20/* The parser is handed a mapped file, so it must never look at a byte beyond
21 the length it was given. These cases place a hex digit immediately after
22 that length: a parser that reads one byte too far sees it, decides the hash
23 runs on, and silently drops the last entry. */
24
25static void case_empty(void) {
26 test_case("an empty buffer yields nothing");
27 CHECK_INT(accesslist_parse_hashes(dest, 8, "", 0), 0);
28}
29
30static void case_single_with_newline(void) {
31 const char buf[] = A "\n";
32
33 test_case("one hash followed by a newline");
34 CHECK_INT(accesslist_parse_hashes(dest, 8, buf, 41), 1);
35}
36
37static void case_single_without_newline(void) {
38 const char buf[] = A "A";
39
40 test_case("one hash ending the buffer, with a hex digit just past the end");
41 CHECK_INT(accesslist_parse_hashes(dest, 8, buf, 40), 1);
42}
43
44static void case_several_last_without_newline(void) {
45 const char buf[] = A "\n" B "\n" C "C";
46
47 test_case("a final hash without a trailing newline still counts");
48 CHECK_INT(accesslist_parse_hashes(dest, 8, buf, 3 * 40 + 2), 3);
49}
50
51static void case_run_on_hash_is_rejected(void) {
52 const char buf[] = A "A\n";
53
54 test_case("forty one hex digits are not a hash");
55 CHECK_INT(accesslist_parse_hashes(dest, 8, buf, 42), 0);
56}
57
58static void case_junk_lines_skipped(void) {
59 const char buf[] = "# a comment\n" A "\nnot a hash\n" B "\n";
60
61 test_case("lines that are not hashes are ignored");
62 CHECK_INT(accesslist_parse_hashes(dest, 8, buf, sizeof(buf) - 1), 2);
63}
64
65static void case_trailing_junk_on_line(void) {
66 const char buf[] = A " some comment\n" B "\n";
67
68 test_case("a hash followed by other text on the same line still counts");
69 CHECK_INT(accesslist_parse_hashes(dest, 8, buf, sizeof(buf) - 1), 2);
70}
71
72static void case_capacity_is_respected(void) {
73 const char buf[] = A "\n" B "\n" C "\n";
74
75 test_case("no more than capacity hashes are written");
76 CHECK_INT(accesslist_parse_hashes(dest, 2, buf, sizeof(buf) - 1), 2);
77}
78
79static void case_zero_capacity_never_writes(void) {
80 const char buf[] = A "\n";
81
82 test_case("a zero capacity tolerates a null destination");
83 CHECK_INT(accesslist_parse_hashes(NULL, 0, buf, 41), 0);
84}
85
86static void case_decoded_value(void) {
87 const char buf[] = "000102030405060708090A0B0C0D0E0F10111213\n";
88 const uint8_t want[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
89
90 test_case("the hash is decoded, not merely counted");
91 CHECK_INT(accesslist_parse_hashes(dest, 8, buf, 41), 1);
92 CHECK_MEM(dest[0], want, sizeof(want));
93}
94
95static void case_capacity_bound_holds(void) {
96 /* (len + 1) / 41 is what accesslist_readfile allocates. Pack a buffer as
97 densely as the parser allows and check the bound is not exceeded. */
98 char buf[41 * 4];
99 size_t len, capacity, found;
100
101 test_case("the capacity formula bounds what the parser can produce");
102 memcpy(buf, A "\n" B "\n" C "\n" A, 4 * 40 + 3);
103 len = 4 * 40 + 3;
104 capacity = (len + 1) / 41;
105 found = accesslist_parse_hashes(dest, 8, buf, len);
106 CHECK_INT(found, 4);
107 CHECK(found <= capacity);
108}
109
110void test_accesslist(void) {
111 case_empty();
112 case_single_with_newline();
113 case_single_without_newline();
114 case_several_last_without_newline();
115 case_run_on_hash_is_rejected();
116 case_junk_lines_skipped();
117 case_trailing_junk_on_line();
118 case_capacity_is_respected();
119 case_zero_capacity_never_writes();
120 case_decoded_value();
121 case_capacity_bound_holds();
122}
diff --git a/tests/t_hex.c b/tests/t_hex.c
new file mode 100644
index 0000000..7cce8ac
--- /dev/null
+++ b/tests/t_hex.c
@@ -0,0 +1,128 @@
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 "test.h"
13
14static const uint8_t sample[20] = {0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xff, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0};
15
16static void case_encode(void) {
17 char dest[64];
18
19 test_case("a hash encodes to forty upper case digits and a terminator");
20 memset(dest, 0x7f, sizeof(dest));
21 CHECK_PTR(hex_fromhash(dest, sample), dest);
22 CHECK_STR(dest, "000123456789ABCDEFFF10203040506070809" "0A0");
23 CHECK_INT(strlen(dest), 40);
24 CHECK_INT((unsigned char)dest[41], 0x7f);
25}
26
27static void case_decode(void) {
28 ot_hash hash;
29
30 test_case("forty digits decode back to the hash");
31 CHECK(hex_tohash(hash, "000123456789ABCDEFFF10203040506070809" "0A0"));
32 CHECK_MEM(hash, sample, sizeof(sample));
33}
34
35static void case_round_trip(void) {
36 ot_hash hash;
37 char text[64];
38 size_t i;
39
40 test_case("encode and decode are inverses over every byte value");
41 for (i = 0; i < 256; ++i) {
42 uint8_t probe[20];
43 memset(probe, (uint8_t)i, sizeof(probe));
44 hex_fromhash(text, probe);
45 if (!hex_tohash(hash, text) || memcmp(hash, probe, sizeof(probe))) {
46 test_failf(__FILE__, __LINE__, "round trip failed for byte value %zu", i);
47 return;
48 }
49 }
50 ++test_checks;
51}
52
53static void case_decode_is_case_insensitive(void) {
54 ot_hash upper, lower;
55
56 test_case("lower case digits decode the same as upper case");
57 CHECK(hex_tohash(upper, "ABCDEF0123456789ABCDEF0123456789ABCDEF01"));
58 CHECK(hex_tohash(lower, "abcdef0123456789abcdef0123456789abcdef01"));
59 CHECK_MEM(lower, upper, sizeof(ot_hash));
60}
61
62static void case_rejects_non_digits(void) {
63 ot_hash hash;
64
65 test_case("a non hex character anywhere rejects the whole string");
66 CHECK_INT(hex_tohash(hash, "g00123456789ABCDEFFF102030405060708090A0"), 0);
67 CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF102030405060708090Ag"), 0);
68 CHECK_INT(hex_tohash(hash, "000123456789ABCDEF:F102030405060708090A"), 0);
69}
70
71static void case_rejects_short_input(void) {
72 ot_hash hash;
73
74 test_case("a string shorter than forty digits is rejected");
75 CHECK_INT(hex_tohash(hash, ""), 0);
76 CHECK_INT(hex_tohash(hash, "00"), 0);
77 CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF102030405060708090A"), 0);
78}
79
80static void case_leaves_hash_untouched_on_failure(void) {
81 ot_hash hash;
82
83 test_case("a rejected string leaves the destination untouched");
84 memset(hash, 0x5a, sizeof(hash));
85 CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF10203040506070809XX0"), 0);
86 CHECK_MEM(hash, "\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a", sizeof(hash));
87}
88
89static void case_stops_at_the_terminator(void) {
90 /* The saved state parser hands this whole buffers that may end early. The
91 decoder must not read past the zero that stopped it. */
92 char guarded[8];
93 ot_hash hash;
94
95 test_case("decoding stops at a terminator rather than running past it");
96 memcpy(guarded, "0012", 4);
97 guarded[4] = 0;
98 memset(guarded + 5, 'f', 3);
99 CHECK_INT(hex_tohash(hash, guarded), 0);
100}
101
102static void case_isdigit(void) {
103 test_case("hex_isdigit accepts exactly the hex digits");
104 CHECK(hex_isdigit('0'));
105 CHECK(hex_isdigit('9'));
106 CHECK(hex_isdigit('a'));
107 CHECK(hex_isdigit('f'));
108 CHECK(hex_isdigit('A'));
109 CHECK(hex_isdigit('F'));
110 CHECK_INT(hex_isdigit('g'), 0);
111 CHECK_INT(hex_isdigit('G'), 0);
112 CHECK_INT(hex_isdigit(':'), 0);
113 CHECK_INT(hex_isdigit('\n'), 0);
114 CHECK_INT(hex_isdigit(0), 0);
115 CHECK_INT(hex_isdigit(0x80), 0);
116}
117
118void test_hex(void) {
119 case_encode();
120 case_decode();
121 case_round_trip();
122 case_decode_is_case_insensitive();
123 case_rejects_non_digits();
124 case_rejects_short_input();
125 case_leaves_hash_untouched_on_failure();
126 case_stops_at_the_terminator();
127 case_isdigit();
128}
diff --git a/tests/t_iovec.c b/tests/t_iovec.c
new file mode 100644
index 0000000..89f3fda
--- /dev/null
+++ b/tests/t_iovec.c
@@ -0,0 +1,124 @@
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 <stdlib.h>
8#include <string.h>
9#include <sys/uio.h>
10
11/* Opentracker */
12#include "ot_iovec.h"
13#include "test.h"
14
15static void case_increase_and_length(void) {
16 size_t entries = 0;
17 struct iovec *vector = NULL;
18 void *first, *second;
19
20 test_case("increase appends an entry and length is their sum");
21 first = iovec_increase(&entries, &vector, 100);
22 CHECK(first != NULL);
23 CHECK_INT(entries, 1);
24 CHECK_INT(vector[0].iov_len, 100);
25 CHECK_PTR(vector[0].iov_base, first);
26
27 second = iovec_increase(&entries, &vector, 50);
28 CHECK(second != NULL);
29 CHECK_INT(entries, 2);
30 CHECK_INT(vector[1].iov_len, 50);
31 CHECK_INT(iovec_length(&entries, (const struct iovec **)&vector), 150);
32
33 iovec_free(&entries, &vector);
34}
35
36static void case_free_resets(void) {
37 size_t entries = 0;
38 struct iovec *vector = NULL;
39
40 test_case("free releases the entries and resets the vector");
41 CHECK(iovec_increase(&entries, &vector, 10) != NULL);
42 CHECK(iovec_increase(&entries, &vector, 10) != NULL);
43 iovec_free(&entries, &vector);
44 CHECK_INT(entries, 0);
45 CHECK_PTR(vector, NULL);
46 CHECK_INT(iovec_length(&entries, (const struct iovec **)&vector), 0);
47}
48
49static void case_fixlast_shrinks(void) {
50 size_t entries = 0;
51 struct iovec *vector = NULL;
52 char *block;
53
54 test_case("fixlast trims the last entry to the bytes actually used");
55 CHECK(iovec_increase(&entries, &vector, 64) != NULL);
56 block = iovec_increase(&entries, &vector, 1000);
57 CHECK(block != NULL);
58 memset(block, 'x', 20);
59
60 iovec_fixlast(&entries, &vector, block + 20);
61 CHECK_INT(entries, 2);
62 CHECK_INT(vector[1].iov_len, 20);
63 CHECK_INT(iovec_length(&entries, (const struct iovec **)&vector), 84);
64 CHECK_MEM(vector[1].iov_base, "xxxxxxxxxxxxxxxxxxxx", 20);
65
66 iovec_free(&entries, &vector);
67}
68
69static void case_fixlast_on_empty(void) {
70 size_t entries = 0;
71 struct iovec *vector = NULL;
72
73 test_case("fixlast on an empty vector does nothing");
74 iovec_fixlast(&entries, &vector, NULL);
75 CHECK_INT(entries, 0);
76 CHECK_PTR(vector, NULL);
77}
78
79static void case_append_takes_ownership(void) {
80 size_t entries = 0;
81 struct iovec *vector = NULL;
82 struct iovec source;
83
84 test_case("append moves the buffer and clears the source");
85 source.iov_base = malloc(10);
86 CHECK(source.iov_base != NULL);
87 memcpy(source.iov_base, "0123456789", 10);
88 source.iov_len = 10;
89
90 CHECK(iovec_append(&entries, &vector, &source) != NULL);
91 CHECK_INT(entries, 1);
92 CHECK_INT(vector[0].iov_len, 10);
93 CHECK_MEM(vector[0].iov_base, "0123456789", 10);
94 CHECK_PTR(source.iov_base, NULL);
95 CHECK_INT(source.iov_len, 0);
96
97 iovec_free(&entries, &vector);
98}
99
100static void case_fix_increase(void) {
101 size_t entries = 0;
102 struct iovec *vector = NULL;
103 char *block;
104
105 test_case("fix_increase trims the previous entry and adds a new one");
106 block = iovec_increase(&entries, &vector, 500);
107 CHECK(block != NULL);
108 block = iovec_fix_increase_or_free(&entries, &vector, block + 30, 200);
109 CHECK(block != NULL);
110 CHECK_INT(entries, 2);
111 CHECK_INT(vector[0].iov_len, 30);
112 CHECK_INT(vector[1].iov_len, 200);
113
114 iovec_free(&entries, &vector);
115}
116
117void test_iovec(void) {
118 case_increase_and_length();
119 case_free_resets();
120 case_fixlast_shrinks();
121 case_fixlast_on_empty();
122 case_append_takes_ownership();
123 case_fix_increase();
124}
diff --git a/tests/t_random.c b/tests/t_random.c
new file mode 100644
index 0000000..67dedf6
--- /dev/null
+++ b/tests/t_random.c
@@ -0,0 +1,114 @@
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_random.h"
12#include "test.h"
13
14/* Nothing here can test that the bytes are unpredictable, which is a property
15 of the generator the build selected. What these cases do pin is that the
16 wrapper hands the whole buffer to it, writes nothing beyond the length it
17 was given, and is actually wired to something that varies. The first of
18 those matters: the fallback this module replaced filled only the first
19 sixteen bytes of the sixty-four byte key buffer in ot_udp.c. */
20
21#define DRAWS 32
22#define BUFLEN 64
23
24static void case_every_byte_varies(void) {
25 unsigned char first[BUFLEN], draw[BUFLEN];
26 unsigned char varied[BUFLEN];
27 size_t i, n;
28
29 test_case("every byte of the buffer comes from the generator");
30 memset(varied, 0, sizeof(varied));
31 random_bytes(first, sizeof(first));
32
33 for (n = 0; n < DRAWS; ++n) {
34 random_bytes(draw, sizeof(draw));
35 for (i = 0; i < BUFLEN; ++i)
36 if (draw[i] != first[i])
37 varied[i] = 1;
38 }
39
40 /* A byte position that never moved across 32 draws is not being written.
41 A false alarm needs all 32 draws to agree by chance, around 256^-31. */
42 for (i = 0; i < BUFLEN; ++i)
43 if (!varied[i]) {
44 test_failf(__FILE__, __LINE__, "byte %zu never changed over %d draws", i, DRAWS);
45 return;
46 }
47 ++test_checks;
48}
49
50static void case_respects_length(void) {
51 unsigned char buf[32];
52 size_t i;
53
54 test_case("nothing is written past the requested length");
55 memset(buf, 0xA5, sizeof(buf));
56 random_bytes(buf, 8);
57 for (i = 8; i < sizeof(buf); ++i)
58 if (buf[i] != 0xA5) {
59 test_failf(__FILE__, __LINE__, "byte %zu past the end was overwritten", i);
60 return;
61 }
62 ++test_checks;
63}
64
65static void case_zero_length(void) {
66 unsigned char buf[4];
67
68 test_case("a zero length request writes nothing");
69 memset(buf, 0x5A, sizeof(buf));
70 random_bytes(buf, 0);
71 CHECK_MEM(buf, "\x5a\x5a\x5a\x5a", 4);
72}
73
74static void case_successive_draws_differ(void) {
75 unsigned char a[32], b[32];
76
77 test_case("two draws differ");
78 random_bytes(a, sizeof(a));
79 random_bytes(b, sizeof(b));
80 CHECK(memcmp(a, b, sizeof(a)) != 0);
81}
82
83static void case_uint32_varies(void) {
84 uint32_t first = random_uint32();
85 int differs = 0;
86 size_t n;
87
88 test_case("random_uint32 does not return a constant");
89 for (n = 0; n < DRAWS; ++n)
90 if (random_uint32() != first)
91 differs = 1;
92 CHECK(differs);
93}
94
95static void case_uint32_uses_the_top_bit(void) {
96 uint32_t seen = 0;
97 size_t n;
98
99 test_case("random_uint32 spans the full 32 bits");
100 /* random() never sets bit 31, so a build that returned it unmixed would
101 fail this. Over 64 draws the chance of a false alarm is 2^-64. */
102 for (n = 0; n < 64; ++n)
103 seen |= random_uint32();
104 CHECK_INT(seen & 0x80000000u, 0x80000000u);
105}
106
107void test_random(void) {
108 case_every_byte_varies();
109 case_respects_length();
110 case_zero_length();
111 case_successive_draws_differ();
112 case_uint32_varies();
113 case_uint32_uses_the_top_bit();
114}
diff --git a/tests/t_scan_urlencoded_query.c b/tests/t_scan_urlencoded_query.c
new file mode 100644
index 0000000..4c1ef23
--- /dev/null
+++ b/tests/t_scan_urlencoded_query.c
@@ -0,0 +1,274 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <string.h>
8
9/* Opentracker */
10#include "scan_urlencoded_query.h"
11#include "test.h"
12
13/* The scanner returns the number of bytes written to the destination, or
14 -1 for a parse error and -2 for a hard terminator encountered before any
15 output was produced. It advances the source pointer past the separator it
16 stopped on, except for hard terminators and the '&' ending a parameter,
17 which it leaves for the next call to see. */
18
19static void case_path(void) {
20 char query[] = "/announce?info_hash=A";
21 char dest[64];
22 char *p = query;
23 ssize_t n;
24
25 test_case("path stops at the question mark");
26 n = scan_urlencoded_query(&p, dest, SCAN_PATH);
27 CHECK_INT(n, 9);
28 dest[n] = 0;
29 CHECK_STR(dest, "/announce");
30 CHECK_INT(*p, 'i');
31}
32
33static void case_param_and_value(void) {
34 char query[] = "info_hash=abc&port=80";
35 char dest[64];
36 char *p = query;
37 ssize_t n;
38
39 test_case("parameter stops at '=', value stops at '&'");
40 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_PARAM);
41 CHECK_INT(n, 9);
42 dest[n] = 0;
43 CHECK_STR(dest, "info_hash");
44
45 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
46 CHECK_INT(n, 3);
47 dest[n] = 0;
48 CHECK_STR(dest, "abc");
49
50 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_PARAM);
51 CHECK_INT(n, 4);
52 dest[n] = 0;
53 CHECK_STR(dest, "port");
54
55 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
56 CHECK_INT(n, 2);
57 dest[n] = 0;
58 CHECK_STR(dest, "80");
59}
60
61static void case_percent_decoding(void) {
62 char query[] = "%41%42%43&x=1";
63 char dest[64];
64 char *p = query;
65 ssize_t n;
66
67 test_case("percent escapes decode");
68 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
69 CHECK_INT(n, 3);
70 CHECK_MEM(dest, "ABC", 3);
71}
72
73static void case_binary_info_hash(void) {
74 /* The twenty byte info_hash is what the announce path really decodes */
75 char query[] = "%00%01%02%ff%fe%7f%80ABCDEFGHIJKLM&x=1";
76 unsigned char want[] = {0x00, 0x01, 0x02, 0xff, 0xfe, 0x7f, 0x80, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M'};
77 char dest[64];
78 char *p = query;
79 ssize_t n;
80
81 test_case("twenty byte binary value survives decoding");
82 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
83 CHECK_INT(n, 20);
84 CHECK_MEM(dest, want, 20);
85}
86
87static void case_in_place(void) {
88 char query[] = "%41%42&x=1";
89 char *p = query;
90 ssize_t n;
91
92 test_case("decoding in place over the source is safe");
93 n = scan_urlencoded_query(&p, query, SCAN_SEARCHPATH_VALUE);
94 CHECK_INT(n, 2);
95 CHECK_MEM(query, "AB", 2);
96}
97
98static void case_bad_escape(void) {
99 char query[] = "%zz";
100 char dest[64];
101 char *p = query;
102 ssize_t n;
103
104 test_case("a non hexadecimal escape is a parse error");
105 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
106 CHECK_INT(n, -1);
107}
108
109static void case_truncated_escape(void) {
110 char query[] = "%4";
111 char dest[64];
112 char *p = query;
113 ssize_t n;
114
115 test_case("an escape cut short by the terminator is a parse error");
116 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
117 CHECK_INT(n, -1);
118}
119
120static void case_terminator(void) {
121 char empty[] = "";
122 char space[] = " rest";
123 char dest[64];
124 char *p = empty;
125 ssize_t n;
126
127 test_case("a hard terminator before any output reports end of input");
128 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_PARAM);
129 CHECK_INT(n, -2);
130
131 p = space;
132 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_PARAM);
133 CHECK_INT(n, -2);
134}
135
136static void case_terminator_after_output(void) {
137 char query[] = "abc";
138 char dest[64];
139 char *p = query;
140 ssize_t n;
141
142 test_case("a hard terminator after output is left for the next call");
143 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
144 CHECK_INT(n, 3);
145 CHECK_INT(*p, 0);
146 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
147 CHECK_INT(n, -2);
148}
149
150/* The character matrix makes '?' terminate a path only, and '=' terminate a
151 path or a parameter only. Both are ordinary characters inside a value, so a
152 value is free to contain a nested query string. */
153
154static void case_separators_are_contextual(void) {
155 char question[] = "a?b&x=1";
156 char equals[] = "a=b&x=1";
157 char dest[64];
158 char *p = question;
159 ssize_t n;
160
161 test_case("a question mark is literal inside a value");
162 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
163 CHECK_INT(n, 3);
164 dest[n] = 0;
165 CHECK_STR(dest, "a?b");
166
167 test_case("an equals sign is literal inside a value");
168 p = equals;
169 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
170 CHECK_INT(n, 3);
171 dest[n] = 0;
172 CHECK_STR(dest, "a=b");
173}
174
175static void case_ampersand_in_path(void) {
176 char query[] = "/path&x";
177 char dest[64];
178 char *p = query;
179 ssize_t n;
180
181 test_case("an ampersand in a path is a parse error");
182 n = scan_urlencoded_query(&p, dest, SCAN_PATH);
183 CHECK_INT(n, -1);
184}
185
186static void case_raw_high_byte(void) {
187 char query[] = {'a', 'b', (char)0x80, 0};
188 char dest[64];
189 char *p = query;
190 ssize_t n;
191
192 test_case("an undecoded byte above ASCII is a parse error");
193 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
194 CHECK_INT(n, -1);
195}
196
197static void case_skipvalue(void) {
198 char query[] = "abc&next=1";
199 char bare[] = "abc";
200 char *p = query;
201
202 test_case("skipvalue steps over the value and its separator");
203 scan_urlencoded_skipvalue(&p);
204 CHECK_STR(p, "next=1");
205
206 test_case("skipvalue leaves a hard terminator in place");
207 p = bare;
208 scan_urlencoded_skipvalue(&p);
209 CHECK_INT(*p, 0);
210}
211
212static void case_find_keywords(void) {
213 static const ot_keywords keywords[] = {{"port", 1}, {"left", 2}, {"compact", 3}, {NULL, -3}};
214 char match[] = "left=0";
215 char nomatch[] = "bogus=1";
216 char prefix[] = "por=1";
217 char *p = match;
218
219 test_case("a known keyword returns its value");
220 CHECK_INT(scan_find_keywords(keywords, &p, SCAN_SEARCHPATH_PARAM), 2);
221
222 test_case("an unknown keyword returns -3");
223 p = nomatch;
224 CHECK_INT(scan_find_keywords(keywords, &p, SCAN_SEARCHPATH_PARAM), -3);
225
226 test_case("a prefix of a keyword does not match it");
227 p = prefix;
228 CHECK_INT(scan_find_keywords(keywords, &p, SCAN_SEARCHPATH_PARAM), -3);
229}
230
231static void case_fixed_int(void) {
232 char positive[] = "123";
233 char negative[] = "-45";
234 char trailing[] = "12a";
235 int value;
236
237 test_case("scan_fixed_int returns the count of unparsed bytes");
238 CHECK_INT(scan_fixed_int(positive, 3, &value), 0);
239 CHECK_INT(value, 123);
240
241 CHECK_INT(scan_fixed_int(negative, 3, &value), 0);
242 CHECK_INT(value, -45);
243
244 CHECK_INT(scan_fixed_int(trailing, 3, &value), 1);
245 CHECK_INT(value, 12);
246}
247
248static void case_fixed_int_bounded(void) {
249 char digits[] = "12345";
250 int value;
251
252 test_case("scan_fixed_int honours the length it is given");
253 CHECK_INT(scan_fixed_int(digits, 3, &value), 0);
254 CHECK_INT(value, 123);
255}
256
257void test_scan_urlencoded_query(void) {
258 case_path();
259 case_param_and_value();
260 case_percent_decoding();
261 case_binary_info_hash();
262 case_in_place();
263 case_bad_escape();
264 case_truncated_escape();
265 case_terminator();
266 case_terminator_after_output();
267 case_separators_are_contextual();
268 case_ampersand_in_path();
269 case_raw_high_byte();
270 case_skipvalue();
271 case_find_keywords();
272 case_fixed_int();
273 case_fixed_int_bounded();
274}
diff --git a/tests/t_vector.c b/tests/t_vector.c
new file mode 100644
index 0000000..fee336b
--- /dev/null
+++ b/tests/t_vector.c
@@ -0,0 +1,161 @@
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 <stdlib.h>
8#include <string.h>
9
10/* Opentracker */
11#include "trackerlogic.h"
12#include "test.h"
13
14/* binary_search returns the matching member when there is one, and otherwise
15 the position at which the key would have to be inserted to keep the vector
16 sorted. exactmatch distinguishes the two. */
17
18static const unsigned char sorted[5][4] = {{0, 0, 0, 1}, {0, 0, 0, 3}, {0, 0, 0, 5}, {0, 0, 0, 7}, {0, 0, 0, 9}};
19
20static void case_search_finds_member(void) {
21 const unsigned char key[4] = {0, 0, 0, 5};
22 int exactmatch;
23 void *found;
24
25 test_case("an existing key is found");
26 found = binary_search(key, sorted, 5, 4, 4, &exactmatch);
27 CHECK(exactmatch != 0);
28 CHECK_PTR(found, sorted[2]);
29}
30
31static void case_search_finds_first_and_last(void) {
32 const unsigned char first[4] = {0, 0, 0, 1};
33 const unsigned char last[4] = {0, 0, 0, 9};
34 int exactmatch;
35
36 test_case("the first and last members are found");
37 CHECK_PTR(binary_search(first, sorted, 5, 4, 4, &exactmatch), sorted[0]);
38 CHECK(exactmatch != 0);
39 CHECK_PTR(binary_search(last, sorted, 5, 4, 4, &exactmatch), sorted[4]);
40 CHECK(exactmatch != 0);
41}
42
43static void case_search_insert_position(void) {
44 const unsigned char middle[4] = {0, 0, 0, 4};
45 const unsigned char before[4] = {0, 0, 0, 0};
46 int exactmatch;
47 void *slot;
48
49 test_case("a missing key yields its insert position");
50 slot = binary_search(middle, sorted, 5, 4, 4, &exactmatch);
51 CHECK_INT(exactmatch, 0);
52 CHECK_PTR(slot, sorted[2]);
53
54 slot = binary_search(before, sorted, 5, 4, 4, &exactmatch);
55 CHECK_INT(exactmatch, 0);
56 CHECK_PTR(slot, sorted[0]);
57}
58
59static void case_search_past_the_end(void) {
60 const unsigned char beyond[4] = {0, 0, 0, 0xff};
61 int exactmatch;
62 void *slot;
63
64 test_case("a key above every member yields the position past the end");
65 slot = binary_search(beyond, sorted, 5, 4, 4, &exactmatch);
66 CHECK_INT(exactmatch, 0);
67 CHECK_PTR(slot, sorted[5]);
68}
69
70static void case_search_empty(void) {
71 int exactmatch;
72 void *base = (void *)sorted;
73
74 test_case("searching an empty vector reports no match at the base");
75 CHECK_PTR(binary_search(sorted[0], base, 0, 4, 4, &exactmatch), base);
76 CHECK_INT(exactmatch, 0);
77}
78
79static void case_insert_keeps_order(void) {
80 ot_vector vector = {NULL, 0, 0};
81 const unsigned char five[4] = {0, 0, 0, 5};
82 const unsigned char one[4] = {0, 0, 0, 1};
83 const unsigned char nine[4] = {0, 0, 0, 9};
84 int exactmatch;
85 void *slot;
86
87 test_case("inserts land in sorted order and grow the vector");
88 slot = vector_find_or_insert(&vector, (void *)five, 4, 4, &exactmatch);
89 CHECK(slot != NULL);
90 CHECK_INT(exactmatch, 0);
91 memcpy(slot, five, 4);
92 CHECK_INT(vector.size, 1);
93 CHECK_INT(vector.space, OT_VECTOR_MIN_MEMBERS);
94
95 slot = vector_find_or_insert(&vector, (void *)one, 4, 4, &exactmatch);
96 CHECK(slot != NULL);
97 CHECK_INT(exactmatch, 0);
98 memcpy(slot, one, 4);
99 CHECK_INT(vector.size, 2);
100
101 slot = vector_find_or_insert(&vector, (void *)nine, 4, 4, &exactmatch);
102 CHECK(slot != NULL);
103 CHECK_INT(exactmatch, 0);
104 memcpy(slot, nine, 4);
105 CHECK_INT(vector.size, 3);
106 CHECK_INT(vector.space, OT_VECTOR_MIN_MEMBERS * OT_VECTOR_GROW_RATIO);
107
108 CHECK_MEM(vector.data, "\0\0\0\1\0\0\0\5\0\0\0\11", 12);
109
110 free(vector.data);
111}
112
113static void case_insert_finds_existing(void) {
114 ot_vector vector = {NULL, 0, 0};
115 const unsigned char five[4] = {0, 0, 0, 5};
116 int exactmatch;
117 void *slot, *again;
118
119 test_case("a second insert of the same key returns the existing member");
120 slot = vector_find_or_insert(&vector, (void *)five, 4, 4, &exactmatch);
121 CHECK(slot != NULL);
122 memcpy(slot, five, 4);
123
124 again = vector_find_or_insert(&vector, (void *)five, 4, 4, &exactmatch);
125 CHECK(exactmatch != 0);
126 CHECK_PTR(again, vector.data);
127 CHECK_INT(vector.size, 1);
128
129 free(vector.data);
130}
131
132static void case_insert_compare_prefix(void) {
133 ot_vector vector = {NULL, 0, 0};
134 unsigned char first[6] = {1, 2, 3, 0xaa, 0xaa, 0xaa};
135 unsigned char second[6] = {1, 2, 3, 0xbb, 0xbb, 0xbb};
136 int exactmatch;
137 void *slot;
138
139 test_case("only the compare prefix decides identity");
140 slot = vector_find_or_insert(&vector, first, 6, 3, &exactmatch);
141 CHECK(slot != NULL);
142 memcpy(slot, first, 6);
143
144 slot = vector_find_or_insert(&vector, second, 6, 3, &exactmatch);
145 CHECK(exactmatch != 0);
146 CHECK_INT(vector.size, 1);
147 CHECK_MEM(slot, first, 6);
148
149 free(vector.data);
150}
151
152void test_vector(void) {
153 case_search_finds_member();
154 case_search_finds_first_and_last();
155 case_search_insert_position();
156 case_search_past_the_end();
157 case_search_empty();
158 case_insert_keeps_order();
159 case_insert_finds_existing();
160 case_insert_compare_prefix();
161}
diff --git a/tests/test.c b/tests/test.c
new file mode 100644
index 0000000..4686369
--- /dev/null
+++ b/tests/test.c
@@ -0,0 +1,58 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <stdarg.h>
8#include <stdio.h>
9
10/* Opentracker */
11#include "test.h"
12
13unsigned long test_checks;
14unsigned long test_failures;
15
16static const char *current_module = "";
17static const char *current_case = "";
18
19void test_module(const char *name) {
20 current_module = name;
21 current_case = "";
22 printf("%s\n", name);
23}
24
25void test_case(const char *name) { current_case = name; }
26
27static void test_where(const char *file, int line) {
28 ++test_failures;
29 printf(" FAIL %s/%s (%s:%d)\n ", current_module, current_case, file, line);
30}
31
32void test_failf(const char *file, int line, const char *fmt, ...) {
33 va_list ap;
34 test_where(file, line);
35 va_start(ap, fmt);
36 vprintf(fmt, ap);
37 va_end(ap);
38 printf("\n");
39}
40
41void test_failmem(const char *file, int line, const char *what, const void *got, const void *want, size_t len) {
42 const unsigned char *g = got, *w = want;
43 size_t i;
44
45 test_where(file, line);
46 printf("%s: %zu bytes differ\n got ", what, len);
47 for (i = 0; i < len; ++i)
48 printf("%02x", g[i]);
49 printf("\n want ");
50 for (i = 0; i < len; ++i)
51 printf("%02x", w[i]);
52 printf("\n");
53}
54
55int test_summary(void) {
56 printf("\n%lu checks, %lu failures\n", test_checks, test_failures);
57 return test_failures != 0;
58}
diff --git a/tests/test.h b/tests/test.h
new file mode 100644
index 0000000..8be624b
--- /dev/null
+++ b/tests/test.h
@@ -0,0 +1,86 @@
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_TEST_H__
7#define OT_TEST_H__
8
9#include <stddef.h>
10#include <string.h>
11
12/* Minimal scaffolding for opentracker's unit tests. No external dependencies.
13
14 Tests are grouped into modules, one per source file under test. A module is
15 a list of small case functions, each pinning one behaviour. A failing CHECK
16 reports file, line and the offending values, then returns from the case it
17 is in, so a single run reports as many independent problems as it can.
18 Because the macros return, they may only be used in functions returning
19 void. */
20
21void test_module(const char *name);
22void test_case(const char *name);
23void test_failf(const char *file, int line, const char *fmt, ...);
24void test_failmem(const char *file, int line, const char *what, const void *got, const void *want, size_t len);
25int test_summary(void);
26
27extern unsigned long test_checks;
28extern unsigned long test_failures;
29
30#define CHECK(cond) \
31 do { \
32 ++test_checks; \
33 if (!(cond)) { \
34 test_failf(__FILE__, __LINE__, "expected %s", #cond); \
35 return; \
36 } \
37 } while (0)
38
39#define CHECK_INT(got, want) \
40 do { \
41 long long got_ = (long long)(got), want_ = (long long)(want); \
42 ++test_checks; \
43 if (got_ != want_) { \
44 test_failf(__FILE__, __LINE__, "%s: got %lld, want %lld", #got, got_, want_); \
45 return; \
46 } \
47 } while (0)
48
49#define CHECK_PTR(got, want) \
50 do { \
51 const void *got_ = (const void *)(got), *want_ = (const void *)(want); \
52 ++test_checks; \
53 if (got_ != want_) { \
54 test_failf(__FILE__, __LINE__, "%s: got %p, want %p", #got, got_, want_); \
55 return; \
56 } \
57 } while (0)
58
59#define CHECK_MEM(got, want, len) \
60 do { \
61 ++test_checks; \
62 if (memcmp((got), (want), (len))) { \
63 test_failmem(__FILE__, __LINE__, #got, (got), (want), (len)); \
64 return; \
65 } \
66 } while (0)
67
68#define CHECK_STR(got, want) \
69 do { \
70 const char *got_ = (got), *want_ = (want); \
71 ++test_checks; \
72 if (strcmp(got_, want_)) { \
73 test_failf(__FILE__, __LINE__, "%s: got \"%s\", want \"%s\"", #got, got_, want_); \
74 return; \
75 } \
76 } while (0)
77
78/* One entry point per module under test */
79void test_scan_urlencoded_query(void);
80void test_iovec(void);
81void test_vector(void);
82void test_random(void);
83void test_hex(void);
84void test_accesslist(void);
85
86#endif
diff --git a/trackerlogic.c b/trackerlogic.c
index 6cc239e..d8a2f9b 100644
--- a/trackerlogic.c
+++ b/trackerlogic.c
@@ -5,16 +5,12 @@
5 5
6/* System */ 6/* System */
7#include <arpa/inet.h> 7#include <arpa/inet.h>
8#include <errno.h>
9#include <stdint.h> 8#include <stdint.h>
10#include <stdio.h> 9#include <stdio.h>
11#include <stdlib.h> 10#include <stdlib.h>
12#include <string.h> 11#include <string.h>
13#include <unistd.h>
14 12
15/* Libowfat */ 13/* Libowfat */
16#include "array.h"
17#include "byte.h"
18#include "io.h" 14#include "io.h"
19#include "iob.h" 15#include "iob.h"
20#include "ip6.h" 16#include "ip6.h"
@@ -26,8 +22,10 @@
26#include "ot_http.h" 22#include "ot_http.h"
27#include "ot_livesync.h" 23#include "ot_livesync.h"
28#include "ot_mutex.h" 24#include "ot_mutex.h"
25#include "ot_random.h"
29#include "ot_stats.h" 26#include "ot_stats.h"
30#include "ot_vector.h" 27#include "ot_vector.h"
28#include "ot_udp.h"
31#include "trackerlogic.h" 29#include "trackerlogic.h"
32 30
33/* Forward declaration */ 31/* Forward declaration */
@@ -48,30 +46,35 @@ void add_torrent_from_saved_state(ot_hash const hash, ot_time base, size_t down_
48 ot_torrent *torrent; 46 ot_torrent *torrent;
49 ot_vector *torrents_list = mutex_bucket_lock_by_hash(hash); 47 ot_vector *torrents_list = mutex_bucket_lock_by_hash(hash);
50 48
51 if (!accesslist_hashisvalid(hash)) 49 if (!accesslist_hashisvalid(hash)) {
52 return mutex_bucket_unlock_by_hash(hash, 0); 50 mutex_bucket_unlock_by_hash(hash, 0);
51 return;
52 }
53 53
54 torrent = vector_find_or_insert(torrents_list, (void *)hash, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch); 54 torrent = vector_find_or_insert(torrents_list, (void *)hash, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch);
55 if (!torrent || exactmatch) 55 if (!torrent || exactmatch) {
56 return mutex_bucket_unlock_by_hash(hash, 0); 56 mutex_bucket_unlock_by_hash(hash, 0);
57 return;
58 }
57 59
58 /* Create a new torrent entry, then */ 60 /* Create a new torrent entry, then */
59 byte_zero(torrent, sizeof(ot_torrent)); 61 memset(torrent, 0, sizeof(ot_torrent));
60 memcpy(torrent->hash, hash, sizeof(ot_hash)); 62 memcpy(torrent->hash, hash, sizeof(ot_hash));
61 63
62 if (!(torrent->peer_list6 = malloc(sizeof(ot_peerlist))) || !(torrent->peer_list4 = malloc(sizeof(ot_peerlist)))) { 64 if (!(torrent->peer_list6 = malloc(sizeof(ot_peerlist))) || !(torrent->peer_list4 = malloc(sizeof(ot_peerlist)))) {
63 vector_remove_torrent(torrents_list, torrent); 65 vector_remove_torrent(torrents_list, torrent);
64 return mutex_bucket_unlock_by_hash(hash, 0); 66 mutex_bucket_unlock_by_hash(hash, 0);
67 return;
65 } 68 }
66 69
67 byte_zero(torrent->peer_list6, sizeof(ot_peerlist)); 70 memset(torrent->peer_list6, 0, sizeof(ot_peerlist));
68 byte_zero(torrent->peer_list4, sizeof(ot_peerlist)); 71 memset(torrent->peer_list4, 0, sizeof(ot_peerlist));
69 torrent->peer_list6->base = base; 72 torrent->peer_list6->base = base;
70 torrent->peer_list4->base = base; 73 torrent->peer_list4->base = base;
71 torrent->peer_list6->down_count = down_count; 74 torrent->peer_list6->down_count = down_count;
72 torrent->peer_list4->down_count = down_count; 75 torrent->peer_list4->down_count = down_count;
73 76
74 return mutex_bucket_unlock_by_hash(hash, 1); 77 mutex_bucket_unlock_by_hash(hash, 1);
75} 78}
76 79
77size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstruct *ws, size_t amount) { 80size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstruct *ws, size_t amount) {
@@ -101,7 +104,7 @@ size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstru
101 104
102 if (!exactmatch) { 105 if (!exactmatch) {
103 /* Create a new torrent entry, then */ 106 /* Create a new torrent entry, then */
104 byte_zero(torrent, sizeof(ot_torrent)); 107 memset(torrent, 0, sizeof(ot_torrent));
105 memcpy(torrent->hash, *ws->hash, sizeof(ot_hash)); 108 memcpy(torrent->hash, *ws->hash, sizeof(ot_hash));
106 109
107 if (!(torrent->peer_list6 = malloc(sizeof(ot_peerlist))) || !(torrent->peer_list4 = malloc(sizeof(ot_peerlist)))) { 110 if (!(torrent->peer_list6 = malloc(sizeof(ot_peerlist))) || !(torrent->peer_list4 = malloc(sizeof(ot_peerlist)))) {
@@ -110,8 +113,8 @@ size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstru
110 return 0; 113 return 0;
111 } 114 }
112 115
113 byte_zero(torrent->peer_list6, sizeof(ot_peerlist)); 116 memset(torrent->peer_list6, 0, sizeof(ot_peerlist));
114 byte_zero(torrent->peer_list4, sizeof(ot_peerlist)); 117 memset(torrent->peer_list4, 0, sizeof(ot_peerlist));
115 delta_torrentcount = 1; 118 delta_torrentcount = 1;
116 } else 119 } else
117 clean_single_torrent(torrent); 120 clean_single_torrent(torrent);
@@ -195,7 +198,7 @@ size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstru
195} 198}
196 199
197static size_t return_peers_all(ot_peerlist *peer_list, size_t peer_size, char *reply) { 200static size_t return_peers_all(ot_peerlist *peer_list, size_t peer_size, char *reply) {
198 unsigned int bucket, num_buckets = 1; 201 size_t bucket, num_buckets = 1;
199 ot_vector *bucket_list = &peer_list->peers; 202 ot_vector *bucket_list = &peer_list->peers;
200 size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size); 203 size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
201 size_t result = compare_size * peer_list->peer_count; 204 size_t result = compare_size * peer_list->peer_count;
@@ -224,7 +227,7 @@ static size_t return_peers_all(ot_peerlist *peer_list, size_t peer_size, char *r
224} 227}
225 228
226static size_t return_peers_selection(struct ot_workstruct *ws, ot_peerlist *peer_list, size_t peer_size, size_t amount, char *reply) { 229static size_t return_peers_selection(struct ot_workstruct *ws, ot_peerlist *peer_list, size_t peer_size, size_t amount, char *reply) {
227 unsigned int bucket_offset, bucket_index = 0, num_buckets = 1; 230 size_t bucket_offset, bucket_index = 0, num_buckets = 1;
228 ot_vector *bucket_list = &peer_list->peers; 231 ot_vector *bucket_list = &peer_list->peers;
229 unsigned int shifted_pc = peer_list->peer_count; 232 unsigned int shifted_pc = peer_list->peer_count;
230 unsigned int shifted_step = 0; 233 unsigned int shifted_step = 0;
@@ -262,7 +265,7 @@ static size_t return_peers_selection(struct ot_workstruct *ws, ot_peerlist *peer
262 bucket_offset -= bucket_list[bucket_index].size; 265 bucket_offset -= bucket_list[bucket_index].size;
263 bucket_index = (bucket_index + 1) % num_buckets; 266 bucket_index = (bucket_index + 1) % num_buckets;
264 } 267 }
265 peer = bucket_list[bucket_index].data + peer_size * bucket_offset; 268 peer = (ot_peer*)bucket_list[bucket_index].data + peer_size * bucket_offset;
266 if (OT_PEERFLAG_D(peer, peer_size) & PEER_FLAG_SEEDING) { 269 if (OT_PEERFLAG_D(peer, peer_size) & PEER_FLAG_SEEDING) {
267 r_end -= compare_size; 270 r_end -= compare_size;
268 memcpy(r_end, peer, compare_size); 271 memcpy(r_end, peer, compare_size);
@@ -403,9 +406,10 @@ size_t return_udp_scrape_for_torrent(ot_hash const hash, char *reply) {
403} 406}
404 407
405/* Fetches scrape info for a specific torrent */ 408/* Fetches scrape info for a specific torrent */
406size_t return_tcp_scrape_for_torrent(ot_hash const *hash_list, int amount, char *reply) { 409size_t return_tcp_scrape_for_torrent(ot_hash const *hash_list, size_t amount, char *reply) {
407 char *r = reply; 410 char *r = reply;
408 int exactmatch, i; 411 int exactmatch;
412 size_t i;
409 413
410 r += sprintf(r, "d5:filesd"); 414 r += sprintf(r, "d5:filesd");
411 415
@@ -489,8 +493,7 @@ size_t remove_peer_from_torrent(PROTO_FLAG proto, struct ot_workstruct *ws) {
489} 493}
490 494
491void iterate_all_torrents(int (*for_each)(ot_torrent *torrent, uintptr_t data), uintptr_t data) { 495void iterate_all_torrents(int (*for_each)(ot_torrent *torrent, uintptr_t data), uintptr_t data) {
492 int bucket; 496 size_t bucket, j;
493 size_t j;
494 497
495 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) { 498 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
496 ot_vector *torrents_list = mutex_bucket_lock(bucket); 499 ot_vector *torrents_list = mutex_bucket_lock(bucket);
@@ -534,8 +537,8 @@ void trackerlogic_add_random_torrents(size_t amount) {
534 ws.hash = (ot_hash *)ws.inbuf; 537 ws.hash = (ot_hash *)ws.inbuf;
535 538
536 while (amount--) { 539 while (amount--) {
537 arc4random_buf(ws.hash, sizeof(ot_hash)); 540 random_bytes(ws.hash, sizeof(ot_hash));
538 arc4random_buf(&ws.peer, sizeof(ws.peer)); 541 random_bytes(&ws.peer, sizeof(ws.peer));
539 542
540 OT_PEERFLAG(ws.peer) &= PEER_FLAG_SEEDING | PEER_FLAG_COMPLETED | PEER_FLAG_STOPPED; 543 OT_PEERFLAG(ws.peer) &= PEER_FLAG_SEEDING | PEER_FLAG_COMPLETED | PEER_FLAG_STOPPED;
541 544
@@ -552,8 +555,8 @@ void exerr(char *message) {
552 exit(111); 555 exit(111);
553} 556}
554 557
555void trackerlogic_init() { 558void trackerlogic_init(void) {
556 g_tracker_id = random(); 559 g_tracker_id = random_uint32();
557 560
558 if (!g_stats_path) 561 if (!g_stats_path)
559 g_stats_path = "stats"; 562 g_stats_path = "stats";
@@ -566,11 +569,12 @@ void trackerlogic_init() {
566 accesslist_init(); 569 accesslist_init();
567 livesync_init(); 570 livesync_init();
568 stats_init(); 571 stats_init();
572 udp_kickoff();
569} 573}
570 574
571void trackerlogic_deinit(void) { 575void trackerlogic_deinit(void) {
572 int bucket, delta_torrentcount = 0; 576 int delta_torrentcount = 0;
573 size_t j; 577 size_t bucket, j;
574 578
575 /* Free all torrents... */ 579 /* Free all torrents... */
576 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) { 580 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
@@ -596,5 +600,3 @@ void trackerlogic_deinit(void) {
596 /* Release mutexes */ 600 /* Release mutexes */
597 mutex_deinit(); 601 mutex_deinit();
598} 602}
599
600const char *g_version_trackerlogic_c = "$Source$: $Revision$\n";
diff --git a/trackerlogic.h b/trackerlogic.h
index 022184d..aa10a54 100644
--- a/trackerlogic.h
+++ b/trackerlogic.h
@@ -12,13 +12,6 @@
12#include <sys/types.h> 12#include <sys/types.h>
13#include <time.h> 13#include <time.h>
14 14
15#if defined(__linux__) && defined(WANT_ARC4RANDOM)
16#include <bsd/stdlib.h>
17#endif
18#ifdef __FreeBSD__
19#define WANT_ARC4RANDOM
20#endif
21
22typedef uint8_t ot_hash[20]; 15typedef uint8_t ot_hash[20];
23typedef time_t ot_time; 16typedef time_t ot_time;
24typedef char ot_ip6[16]; 17typedef char ot_ip6[16];
@@ -194,7 +187,7 @@ void exerr(char *message);
194 otherwise it is released in return_peers_for_torrent */ 187 otherwise it is released in return_peers_for_torrent */
195size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstruct *ws, size_t amount); 188size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstruct *ws, size_t amount);
196size_t remove_peer_from_torrent(PROTO_FLAG proto, struct ot_workstruct *ws); 189size_t remove_peer_from_torrent(PROTO_FLAG proto, struct ot_workstruct *ws);
197size_t return_tcp_scrape_for_torrent(ot_hash const *hash_list, int amount, char *reply); 190size_t return_tcp_scrape_for_torrent(ot_hash const *hash_list, size_t amount, char *reply);
198size_t return_udp_scrape_for_torrent(ot_hash const hash, char *reply); 191size_t return_udp_scrape_for_torrent(ot_hash const hash, char *reply);
199void add_torrent_from_saved_state(ot_hash const hash, ot_time base, size_t down_count); 192void add_torrent_from_saved_state(ot_hash const hash, ot_time base, size_t down_count);
200#ifdef _DEBUG_RANDOMTORRENTS 193#ifdef _DEBUG_RANDOMTORRENTS