summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile94
-rw-r--r--man1/opentracker.14
-rw-r--r--man4/opentracker.conf.43
-rw-r--r--opentracker.conf.sample6
-rw-r--r--ot_accesslist.c28
-rw-r--r--ot_accesslist.h18
-rw-r--r--ot_config.c2
-rw-r--r--ot_http.c69
-rw-r--r--ot_stats.c146
-rw-r--r--ot_stats.h1
-rw-r--r--ot_sync.c165
-rw-r--r--ot_sync.h26
-rw-r--r--ot_workqueue.h2
-rw-r--r--proxy.c4
-rw-r--r--tests/integration/test_stats.py25
-rw-r--r--tests/integration/test_version.py36
-rw-r--r--tests/runner.c1
-rw-r--r--tests/stubs.c33
-rw-r--r--tests/t_trackerlogic.c243
-rw-r--r--tests/test.h1
-rw-r--r--trackerlogic.h4
22 files changed, 493 insertions, 421 deletions
diff --git a/.gitignore b/.gitignore
index 3e469ff..df288e0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,6 @@ proxy.debug
9tests/runner 9tests/runner
10tests/runner.poll 10tests/runner.poll
11tests/runner.sanitize 11tests/runner.sanitize
12.buildstamp
13.tarball-version
14opentracker-*.tar.gz
diff --git a/Makefile b/Makefile
index 0e5e985..a665890 100644
--- a/Makefile
+++ b/Makefile
@@ -1,20 +1,6 @@
1# $Id$ 1# $Id$
2 2
3# Linux flavour
4# PREFIX?=/opt/diet
5# LIBOWFAT_HEADERS=$(PREFIX)/include
6# LIBOWFAT_LIBRARY=$(PREFIX)/lib
7
8# BSD flavour
9# PREFIX?=/usr/local
10# LIBOWFAT_HEADERS=$(PREFIX)/include/libowfat
11# LIBOWFAT_LIBRARY=$(PREFIX)/lib
12
13# Debug flavour
14PREFIX?=.. 3PREFIX?=..
15LIBOWFAT_HEADERS=$(PREFIX)/libowfat
16LIBOWFAT_LIBRARY=$(PREFIX)/libowfat
17
18BINDIR?=$(PREFIX)/bin 4BINDIR?=$(PREFIX)/bin
19STRIP?=strip 5STRIP?=strip
20 6
@@ -36,10 +22,8 @@ include Makefile.gzip
36# in Makefile.zstd 22# in Makefile.zstd
37#include Makefile.zstd 23#include Makefile.zstd
38 24
39#FEATURES+=-DWANT_LOG_NETWORKS
40#FEATURES+=-DWANT_RESTRICT_STATS 25#FEATURES+=-DWANT_RESTRICT_STATS
41#FEATURES+=-DWANT_IP_FROM_PROXY 26#FEATURES+=-DWANT_IP_FROM_PROXY
42#FEATURES+=-DWANT_FULLLOG_NETWORKS
43#FEATURES+=-DWANT_LOG_NUMWANT 27#FEATURES+=-DWANT_LOG_NUMWANT
44#FEATURES+=-DWANT_MODEST_FULLSCRAPES 28#FEATURES+=-DWANT_MODEST_FULLSCRAPES
45#FEATURES+=-DWANT_SPOT_WOODPECKER 29#FEATURES+=-DWANT_SPOT_WOODPECKER
@@ -55,62 +39,73 @@ FEATURES+=-DWANT_FULLSCRAPE
55#FEATURES+=-D_DEBUG_HTTPERROR 39#FEATURES+=-D_DEBUG_HTTPERROR
56#FEATURES+=-D_DEBUG_RANDOMTORRENTS 40#FEATURES+=-D_DEBUG_RANDOMTORRENTS
57 41
58GIT_VERSION=`sh -c 'command -v git >/dev/null && test -d .git && git rev-parse HEAD || echo _git_or_commit_not_found_'` 42VERSION=1.0.0
43TARBALL_VERSION=test -r .tarball-version && read v < .tarball-version && echo "$$v"
44HAVE_GIT=command -v git >/dev/null && test -d .git
45GIT_VERSION=`sh -c '$(TARBALL_VERSION) || { $(HAVE_GIT) && git describe --tags --match "v*" --always --dirty 2>/dev/null; }'`
46OT_TAG=`sh -c '$(TARBALL_VERSION) || { $(HAVE_GIT) && git describe --tags --match "v*" --exact-match 2>/dev/null; }'`
59 47
60OPTS_debug=-D_DEBUG -g -ggdb # -pg -fprofile-arcs -ftest-coverage 48OPTS_debug=-D_DEBUG -g -ggdb # -pg -fprofile-arcs -ftest-coverage
61OPTS_production=-O3 49OPTS_production=-O3
62 50
63CFLAGS+=-DGIT_VERSION=$(GIT_VERSION) -Wall -pipe -pthread -Wextra -Wwrite-strings #-ansi -pedantic 51CFLAGS+=-DOT_VERSION=\"$(VERSION)\" -DGIT_VERSION=\"$(GIT_VERSION)\" -DOT_TAG=\"$(OT_TAG)\" -Wall -pipe -pthread -Wextra -Wwrite-strings #-ansi -pedantic
64LDFLAGS+=-pthread 52LDFLAGS+=-pthread
65 53
66# Only the proxy, which has not been ported since the tracker went dual stack, 54# Record feature flags as a dep to allow rebuild when they change
67# still wants libowfat. 55BUILDSTAMP=.buildstamp
68CFLAGS_libowfat=-I$(LIBOWFAT_HEADERS) 56BUILDDEPS=Makefile $(BUILDSTAMP)
69LDFLAGS_libowfat=-L$(LIBOWFAT_LIBRARY) -lowfat 57
58$(BUILDSTAMP): FORCE
59 @echo "$(CFLAGS) $(FEATURES) $(LDFLAGS)" | cmp -s - $@ 2>/dev/null || echo "$(CFLAGS) $(FEATURES) $(LDFLAGS)" > $@
60
61FORCE:
70 62
71BINARY =opentracker 63BINARY =opentracker
72HEADERS=trackerlogic.h ot_urlencoded.h ot_batch.h ot_buf.h ot_event.h ot_fdstate.h ot_fmt.h ot_hex.h ot_ip.h ot_config.h ot_scan.h ot_socket.h ot_tcp.h ot_worker.h ot_workqueue.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 64HEADERS=trackerlogic.h ot_urlencoded.h ot_batch.h ot_buf.h ot_event.h ot_fdstate.h ot_fmt.h ot_hex.h ot_ip.h ot_config.h ot_scan.h ot_socket.h ot_tcp.h ot_worker.h ot_workqueue.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
73SOURCES=opentracker.c trackerlogic.c ot_urlencoded.c ot_batch.c ot_buf.c ot_event.c ot_fdstate.c ot_fmt.c ot_hex.c ot_ip.c ot_config.c ot_scan.c ot_socket.c ot_tcp.c ot_worker.c ot_workqueue.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 65SOURCES=opentracker.c trackerlogic.c ot_urlencoded.c ot_batch.c ot_buf.c ot_event.c ot_fdstate.c ot_fmt.c ot_hex.c ot_ip.c ot_config.c ot_scan.c ot_socket.c ot_tcp.c ot_worker.c ot_workqueue.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
74SOURCES_proxy=proxy.c ot_batch.c ot_fdstate.c ot_vector.c ot_mutex.c ot_random.c ot_scan.c ot_ip.c ot_socket.c
75 66
76all: $(BINARY) $(BINARY).debug 67all: $(BINARY) $(BINARY).debug
77 68
78CFLAGS_production = $(CFLAGS) $(OPTS_production) $(FEATURES) 69CFLAGS_production = $(CFLAGS) $(OPTS_production) $(FEATURES)
79CFLAGS_debug = $(CFLAGS) $(OPTS_debug) $(FEATURES) 70CFLAGS_debug = $(CFLAGS) $(OPTS_debug) $(FEATURES)
80 71
81CFLAGS_proxy = $(CFLAGS_production) $(CFLAGS_libowfat) -DWANT_SYNC_LIVE 72$(BINARY): $(SOURCES) $(HEADERS) $(BUILDDEPS)
82CFLAGS_proxy_debug = $(CFLAGS_debug) $(CFLAGS_libowfat) -DWANT_SYNC_LIVE
83
84$(BINARY): $(SOURCES) $(HEADERS)
85 $(CC) -o $@ $(CFLAGS_production) $(SOURCES) $(LDFLAGS) 73 $(CC) -o $@ $(CFLAGS_production) $(SOURCES) $(LDFLAGS)
86 $(STRIP) $@ 74 $(STRIP) $@
87$(BINARY).debug: $(SOURCES) $(HEADERS) 75$(BINARY).debug: $(SOURCES) $(HEADERS) $(BUILDDEPS)
88 $(CC) -o $@ $(CFLAGS_debug) $(SOURCES) $(LDFLAGS) 76 $(CC) -o $@ $(CFLAGS_debug) $(SOURCES) $(LDFLAGS)
89proxy: $(SOURCES_proxy) $(HEADERS) 77
90 $(CC) -o $@ $(CFLAGS_proxy) $(SOURCES_proxy) $(LDFLAGS) $(LDFLAGS_libowfat) 78# The proxy is out of service
91proxy.debug: $(SOURCES_proxy) $(HEADERS) 79#
92 $(CC) -o $@ $(CFLAGS_proxy_debug) $(SOURCES_proxy) $(LDFLAGS) $(LDFLAGS_libowfat) 80# SOURCES_proxy=proxy.c ot_batch.c ot_fdstate.c ot_vector.c ot_mutex.c ot_random.c ot_scan.c ot_ip.c ot_socket.c
81# CFLAGS_proxy = $(CFLAGS_production) -DWANT_SYNC_LIVE
82# CFLAGS_proxy_debug = $(CFLAGS_debug) -DWANT_SYNC_LIVE
83#
84# proxy: $(SOURCES_proxy) $(HEADERS) $(BUILDDEPS)
85# $(CC) -o $@ $(CFLAGS_proxy) $(SOURCES_proxy) $(LDFLAGS)
86# proxy.debug: $(SOURCES_proxy) $(HEADERS) $(BUILDDEPS)
87# $(CC) -o $@ $(CFLAGS_proxy_debug) $(SOURCES_proxy) $(LDFLAGS)
93 88
94# Unit tests. The modules under test are compiled straight into the test 89# Unit tests. The modules under test are compiled straight into the test
95# binary, so a test may reach a static function by including the source if it 90# binary, so a test may reach a static function by including the source if it
96# has to. tests/stubs.c supplies the few symbols those modules reference but 91# has to. tests/stubs.c supplies the few symbols those modules reference but
97# the tests never reach. 92# the tests never reach.
98TESTBIN=tests/runner 93TESTBIN=tests/runner
99TEST_SOURCES=tests/runner.c tests/test.c tests/stubs.c tests/t_urlencoded.c tests/t_iovec.c tests/t_vector.c tests/t_random.c tests/t_hex.c tests/t_accesslist.c tests/t_scan.c tests/t_buf.c tests/t_ip.c tests/t_socket.c tests/t_mutex.c tests/t_workqueue.c tests/t_batch.c tests/t_fdstate.c tests/t_event.c tests/t_fmt.c 94TEST_SOURCES=tests/runner.c tests/test.c tests/stubs.c tests/t_urlencoded.c tests/t_iovec.c tests/t_vector.c tests/t_random.c tests/t_hex.c tests/t_accesslist.c tests/t_scan.c tests/t_buf.c tests/t_ip.c tests/t_socket.c tests/t_mutex.c tests/t_trackerlogic.c tests/t_workqueue.c tests/t_batch.c tests/t_fdstate.c tests/t_event.c tests/t_fmt.c
100TEST_UNITS=ot_urlencoded.c ot_fmt.c ot_event.c ot_batch.c ot_fdstate.c ot_buf.c ot_ip.c ot_socket.c ot_iovec.c ot_vector.c ot_random.c ot_hex.c ot_scan.c ot_accesslist.c ot_mutex.c ot_workqueue.c 95TEST_UNITS=ot_urlencoded.c ot_fmt.c ot_event.c ot_batch.c ot_fdstate.c ot_buf.c ot_ip.c ot_socket.c ot_iovec.c ot_vector.c ot_random.c ot_hex.c ot_scan.c ot_accesslist.c ot_mutex.c ot_workqueue.c trackerlogic.c
101TEST_HEADERS=tests/test.h $(HEADERS) 96TEST_HEADERS=tests/test.h $(HEADERS)
102# Tests reach code that sits behind feature flags, so the units under test are 97# Tests reach code that sits behind feature flags, so the units under test are
103# built with those features enabled. A test binary is not a production binary. 98# built with those features enabled. A test binary is not a production binary.
104FEATURES_test=$(FEATURES) -DWANT_ACCESSLIST_BLACK -DWANT_SYNC_LIVE 99FEATURES_test=$(FEATURES) -DWANT_ACCESSLIST_BLACK -DWANT_SYNC_LIVE
105CFLAGS_test = $(CFLAGS) -I. -g -O1 $(FEATURES_test) 100CFLAGS_test = $(CFLAGS) -I. -g -O1 $(FEATURES_test)
106 101
107$(TESTBIN): $(TEST_SOURCES) $(TEST_UNITS) $(TEST_HEADERS) 102$(TESTBIN): $(TEST_SOURCES) $(TEST_UNITS) $(TEST_HEADERS) $(BUILDDEPS)
108 $(CC) -o $@ $(CFLAGS_test) $(TEST_SOURCES) $(TEST_UNITS) 103 $(CC) -o $@ $(CFLAGS_test) $(TEST_SOURCES) $(TEST_UNITS)
109 104
110check: $(TESTBIN) $(BINARY) 105check: $(TESTBIN) $(BINARY)
111 ./$(TESTBIN) 106 ./$(TESTBIN)
112 107
113$(TESTBIN).poll: $(TEST_SOURCES) $(TEST_UNITS) $(TEST_HEADERS) 108$(TESTBIN).poll: $(TEST_SOURCES) $(TEST_UNITS) $(TEST_HEADERS) $(BUILDDEPS)
114 $(CC) -o $@ $(CFLAGS_test) -DOT_EVENT_FORCE_POLL $(TEST_SOURCES) $(TEST_UNITS) 109 $(CC) -o $@ $(CFLAGS_test) -DOT_EVENT_FORCE_POLL $(TEST_SOURCES) $(TEST_UNITS)
115 110
116check-poll: $(TESTBIN).poll 111check-poll: $(TESTBIN).poll
@@ -123,7 +118,7 @@ CC_sanitize?=clang
123CFLAGS_sanitize=$(CFLAGS_test) -fsanitize=address,undefined -fno-omit-frame-pointer 118CFLAGS_sanitize=$(CFLAGS_test) -fsanitize=address,undefined -fno-omit-frame-pointer
124LDFLAGS_sanitize=$(LDFLAGS) -fsanitize=address,undefined 119LDFLAGS_sanitize=$(LDFLAGS) -fsanitize=address,undefined
125 120
126$(TESTBIN).sanitize: $(TEST_SOURCES) $(TEST_UNITS) $(TEST_HEADERS) 121$(TESTBIN).sanitize: $(TEST_SOURCES) $(TEST_UNITS) $(TEST_HEADERS) $(BUILDDEPS)
127 $(CC_sanitize) -o $@ $(CFLAGS_sanitize) $(TEST_SOURCES) $(TEST_UNITS) $(LDFLAGS_sanitize) 122 $(CC_sanitize) -o $@ $(CFLAGS_sanitize) $(TEST_SOURCES) $(TEST_UNITS) $(LDFLAGS_sanitize)
128 123
129check-sanitize: $(TESTBIN).sanitize 124check-sanitize: $(TESTBIN).sanitize
@@ -140,7 +135,7 @@ FEATURES_integration=$(FEATURES) -DWANT_IP_FROM_QUERY_STRING -DWANT_ACCESSLIST_B
140CFLAGS_integration=$(CFLAGS) $(OPTS_debug) $(FEATURES_integration) -fsanitize=address,undefined -fno-omit-frame-pointer 135CFLAGS_integration=$(CFLAGS) $(OPTS_debug) $(FEATURES_integration) -fsanitize=address,undefined -fno-omit-frame-pointer
141LDFLAGS_integration=$(LDFLAGS) -fsanitize=address,undefined 136LDFLAGS_integration=$(LDFLAGS) -fsanitize=address,undefined
142 137
143opentracker.integration: $(SOURCES) $(HEADERS) 138opentracker.integration: $(SOURCES) $(HEADERS) $(BUILDDEPS)
144 $(CC) -o $@ $(CFLAGS_integration) $(SOURCES) $(LDFLAGS_integration) 139 $(CC) -o $@ $(CFLAGS_integration) $(SOURCES) $(LDFLAGS_integration)
145 140
146check-integration: opentracker.integration 141check-integration: opentracker.integration
@@ -150,7 +145,7 @@ check-integration: opentracker.integration
150CFLAGS_tsan=$(CFLAGS) $(OPTS_debug) $(FEATURES_integration) -fsanitize=thread -fno-omit-frame-pointer 145CFLAGS_tsan=$(CFLAGS) $(OPTS_debug) $(FEATURES_integration) -fsanitize=thread -fno-omit-frame-pointer
151LDFLAGS_tsan=$(LDFLAGS) -fsanitize=thread 146LDFLAGS_tsan=$(LDFLAGS) -fsanitize=thread
152 147
153opentracker.tsan: $(SOURCES) $(HEADERS) 148opentracker.tsan: $(SOURCES) $(HEADERS) $(BUILDDEPS)
154 $(CC) -o $@ $(CFLAGS_tsan) $(SOURCES) $(LDFLAGS_tsan) 149 $(CC) -o $@ $(CFLAGS_tsan) $(SOURCES) $(LDFLAGS_tsan)
155 150
156check-tsan: opentracker.tsan 151check-tsan: opentracker.tsan
@@ -164,12 +159,27 @@ check-all:
164 $(MAKE) check-integration 159 $(MAKE) check-integration
165 $(MAKE) check-tsan 160 $(MAKE) check-tsan
166 161
167.PHONY: all check check-poll check-sanitize check-integration check-tsan check-all clean install 162.PHONY: all FORCE dist check check-poll check-sanitize check-integration check-tsan check-all clean install
168 163
169BINARIES=opentracker opentracker.debug opentracker.integration opentracker.tsan proxy proxy.debug $(TESTBIN) $(TESTBIN).sanitize $(TESTBIN).poll 164BINARIES=opentracker opentracker.debug opentracker.integration opentracker.tsan $(TESTBIN) $(TESTBIN).sanitize $(TESTBIN).poll
170 165
171clean: 166clean:
172 rm -rf $(BINARIES) $(BINARIES:=.dSYM) *.o *~ 167 rm -rf $(BINARIES) $(BINARIES:=.dSYM) $(BUILDSTAMP) *.o *~
173 168
174install: 169install:
175 install -m 755 opentracker $(DESTDIR)$(BINDIR) 170 install -m 755 opentracker $(DESTDIR)$(BINDIR)
171
172# Roll a release: tag it v<something>, then make dist. The commit is written
173# into the tarball, so a build from it reports the same version this checkout
174# would, without a git to ask. The tarball is HEAD by construction, so the
175# working tree it was rolled from does not enter into the version.
176DISTNAME=opentracker-$(VERSION)
177
178dist:
179 @test "`git describe --tags --match 'v*' --exact-match 2>/dev/null`" = "v$(VERSION)" || \
180 { echo "HEAD must carry the tag v$(VERSION). Tag the release, or correct VERSION."; exit 1; }
181 rm -rf $(DISTNAME) $(DISTNAME).tar.gz
182 git archive --format=tar --prefix=$(DISTNAME)/ HEAD | tar -xf -
183 git describe --tags --match "v*" --always > $(DISTNAME)/.tarball-version
184 tar -czf $(DISTNAME).tar.gz $(DISTNAME)
185 rm -rf $(DISTNAME)
diff --git a/man1/opentracker.1 b/man1/opentracker.1
index 0103ebe..489a54c 100644
--- a/man1/opentracker.1
+++ b/man1/opentracker.1
@@ -15,6 +15,7 @@
15.Op Fl d Ar chdir 15.Op Fl d Ar chdir
16.Op Fl u Ar user 16.Op Fl u Ar user
17.Op Fl w| Fl b accesslist 17.Op Fl w| Fl b accesslist
18.Op Fl v
18.Sh DESCRIPTION 19.Sh DESCRIPTION
19.Nm 20.Nm
20is a bittorrent tracker that implements announce and scrape actions over the 21is a bittorrent tracker that implements announce and scrape actions over the
@@ -85,6 +86,9 @@ has been compiled with the
85or 86or
86.Br WANT_ACCESSLIST_WHITE 87.Br WANT_ACCESSLIST_WHITE
87options, this option sets the location of the accesslist. 88options, this option sets the location of the accesslist.
89.It Fl v
90Report the version, the event mechanism in use and the feature flags this
91binary was built with.
88.El 92.El
89.Sh EXAMPLES 93.Sh EXAMPLES
90Start 94Start
diff --git a/man4/opentracker.conf.4 b/man4/opentracker.conf.4
index 1bd1c7a..239ab88 100644
--- a/man4/opentracker.conf.4
+++ b/man4/opentracker.conf.4
@@ -84,9 +84,6 @@ Specifies the IP address and port opentracker will listen on for incoming live s
84.It livesync.cluster.node_ip Ar ip_address 84.It livesync.cluster.node_ip Ar ip_address
85Specifies one trusted IP address for sync between trackers running in a cluster. Can be added more than once. 85Specifies one trusted IP address for sync between trackers running in a cluster. Can be added more than once.
86.Pp 86.Pp
87.It batchsync.cluster.admin_ip Ar ip_address
88Specifies the admin IP address for old-style (HTTP-based) asynchronous tracker syncing.
89.Pp
90.It tracker.rootdir Ar path 87.It tracker.rootdir Ar path
91Specifies the directory opentracker will chroot/chdir to. All black/white list files must be located in this directory. 88Specifies the directory opentracker will chroot/chdir to. All black/white list files must be located in this directory.
92.Pp 89.Pp
diff --git a/opentracker.conf.sample b/opentracker.conf.sample
index f075703..491c88a 100644
--- a/opentracker.conf.sample
+++ b/opentracker.conf.sample
@@ -154,12 +154,6 @@
154# livesync.cluster.node_ip 192.168.0.4 154# livesync.cluster.node_ip 192.168.0.4
155# livesync.cluster.node_ip 192.168.0.5 155# livesync.cluster.node_ip 192.168.0.5
156# livesync.cluster.node_ip 192.168.0.6 156# livesync.cluster.node_ip 192.168.0.6
157#
158# This is the admin ip address for old style (HTTP based) asynchronus
159# tracker syncing.
160#
161# batchsync.cluster.admin_ip 10.1.1.1
162#
163 157
164# V) Control privilege drop behaviour. 158# V) Control privilege drop behaviour.
165# Put in the directory opentracker will chroot/chdir to. All black/white 159# Put in the directory opentracker will chroot/chdir to. All black/white
diff --git a/ot_accesslist.c b/ot_accesslist.c
index df084b8..945ddcf 100644
--- a/ot_accesslist.c
+++ b/ot_accesslist.c
@@ -503,34 +503,6 @@ void *accesslist_get_value_for_net(const ot_ip6 address, const ot_vector *vector
503 return (void *)net; 503 return (void *)net;
504} 504}
505 505
506#ifdef WANT_FULLLOG_NETWORKS
507static ot_vector g_lognets_list;
508ot_log *g_logchain_first, *g_logchain_last;
509static pthread_mutex_t g_lognets_list_mutex = PTHREAD_MUTEX_INITIALIZER;
510
511void loglist_add_network(const ot_net *net) {
512 pthread_mutex_lock(&g_lognets_list_mutex);
513 accesslist_set_value_for_net(net, &g_lognets_list, NULL, sizeof(ot_net));
514 pthread_mutex_unlock(&g_lognets_list_mutex);
515}
516
517void loglist_reset(void) {
518 pthread_mutex_lock(&g_lognets_list_mutex);
519 free(g_lognets_list.data);
520 g_lognets_list.data = 0;
521 g_lognets_list.size = g_lognets_list.space = 0;
522 pthread_mutex_unlock(&g_lognets_list_mutex);
523}
524
525int loglist_check_address(const ot_ip6 address) {
526 int result;
527 pthread_mutex_lock(&g_lognets_list_mutex);
528 result = (NULL != accesslist_get_value_for_net(address, &g_lognets_list, sizeof(ot_net)));
529 pthread_mutex_unlock(&g_lognets_list_mutex);
530 return result;
531}
532#endif
533
534#ifdef WANT_IP_FROM_PROXY 506#ifdef WANT_IP_FROM_PROXY
535typedef struct { 507typedef struct {
536 ot_net *proxy; 508 ot_net *proxy;
diff --git a/ot_accesslist.h b/ot_accesslist.h
index 0a8bf4c..ca0e126 100644
--- a/ot_accesslist.h
+++ b/ot_accesslist.h
@@ -14,6 +14,8 @@
14 14
15#if defined(WANT_ACCESSLIST_BLACK) || defined(WANT_ACCESSLIST_WHITE) 15#if defined(WANT_ACCESSLIST_BLACK) || defined(WANT_ACCESSLIST_WHITE)
16#define WANT_ACCESSLIST 16#define WANT_ACCESSLIST
17/* Starts a thread that rereads the list when sent SIGHUP, and is stopped by
18 being sent one, so the caller blocks SIGHUP process wide beforehand */
17void accesslist_init(void); 19void accesslist_init(void);
18void accesslist_deinit(void); 20void accesslist_deinit(void);
19int accesslist_hashisvalid(const ot_hash hash); 21int accesslist_hashisvalid(const ot_hash hash);
@@ -64,22 +66,6 @@ int proxylist_add_network(const ot_net *proxy, const ot_net *net);
64int proxylist_check_network(const ot_ip6 *proxy, const ot_ip6 address /* can be NULL to only check proxy */); 66int proxylist_check_network(const ot_ip6 *proxy, const ot_ip6 address /* can be NULL to only check proxy */);
65#endif 67#endif
66 68
67#ifdef WANT_FULLLOG_NETWORKS
68typedef struct ot_log ot_log;
69struct ot_log {
70 ot_ip6 ip;
71 uint8_t *data;
72 size_t size;
73 ot_time time;
74 ot_log *next;
75};
76extern ot_log *g_logchain_first, *g_logchain_last;
77
78void loglist_add_network(const ot_net *net);
79void loglist_reset(void);
80int loglist_check_address(const ot_ip6 address);
81#endif
82
83typedef enum { 69typedef enum {
84 OT_PERMISSION_MAY_FULLSCRAPE = 0x1, 70 OT_PERMISSION_MAY_FULLSCRAPE = 0x1,
85 OT_PERMISSION_MAY_STAT = 0x2, 71 OT_PERMISSION_MAY_STAT = 0x2,
diff --git a/ot_config.c b/ot_config.c
index 272acf1..5da6642 100644
--- a/ot_config.c
+++ b/ot_config.c
@@ -432,6 +432,7 @@ static void help(char *name) {
432 HELPLINE("-d dir", "specify directory to try to chroot to (default: \".\")"); 432 HELPLINE("-d dir", "specify directory to try to chroot to (default: \".\")");
433 HELPLINE("-u user", "specify user under whose privileges opentracker should run (default: \"nobody\")"); 433 HELPLINE("-u user", "specify user under whose privileges opentracker should run (default: \"nobody\")");
434 HELPLINE("-A ip[/bits]", "bless an ip address or net as admin address (e.g. to allow syncs from this address)"); 434 HELPLINE("-A ip[/bits]", "bless an ip address or net as admin address (e.g. to allow syncs from this address)");
435 HELPLINE("-v", "report version, event mechanism and the feature flags opentracker was built with");
435#ifdef WANT_ACCESSLIST_BLACK 436#ifdef WANT_ACCESSLIST_BLACK
436 HELPLINE("-b file", "specify blacklist file."); 437 HELPLINE("-b file", "specify blacklist file.");
437#elif defined(WANT_ACCESSLIST_WHITE) 438#elif defined(WANT_ACCESSLIST_WHITE)
@@ -566,6 +567,7 @@ void config_parse(int argc, char **argv) {
566 char *cursor = buffer; 567 char *cursor = buffer;
567 568
568 stats_return_tracker_version(&cursor, buffer + sizeof(buffer) - 1); 569 stats_return_tracker_version(&cursor, buffer + sizeof(buffer) - 1);
570 stats_return_tracker_features(&cursor, buffer + sizeof(buffer) - 1);
569 *cursor = 0; 571 *cursor = 0;
570 fputs(buffer, stderr); 572 fputs(buffer, stderr);
571 exit(0); 573 exit(0);
diff --git a/ot_http.c b/ot_http.c
index b0e1de3..b243ff6 100644
--- a/ot_http.c
+++ b/ot_http.c
@@ -289,10 +289,8 @@ static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, c
289 {"top10", TASK_STATS_TOP10}, 289 {"top10", TASK_STATS_TOP10},
290 {"renew", TASK_STATS_RENEW}, 290 {"renew", TASK_STATS_RENEW},
291 {"syncs", TASK_STATS_SYNCS}, 291 {"syncs", TASK_STATS_SYNCS},
292 {"version", TASK_STATS_VERSION},
293 {"everything", TASK_STATS_EVERYTHING}, 292 {"everything", TASK_STATS_EVERYTHING},
294 {"statedump", TASK_FULLSCRAPE_TRACKERSTATE}, 293 {"statedump", TASK_FULLSCRAPE_TRACKERSTATE},
295 {"fulllog", TASK_STATS_FULLLOG},
296 {"woodpeckers", TASK_STATS_WOODPECKERS}, 294 {"woodpeckers", TASK_STATS_WOODPECKERS},
297#ifdef WANT_LOG_NUMWANT 295#ifdef WANT_LOG_NUMWANT
298 {"numwants", TASK_STATS_NUMWANTS}, 296 {"numwants", TASK_STATS_NUMWANTS},
@@ -559,10 +557,7 @@ static ot_keywords keywords_announce[] = {{"port", 1}, {"left", 2}, {"event",
559#ifdef WANT_IP_FROM_QUERY_STRING 557#ifdef WANT_IP_FROM_QUERY_STRING
560 {"ip", 7}, 558 {"ip", 7},
561#endif 559#endif
562#ifdef WANT_FULLLOG_NETWORKS 560 {"peer_id", 8}, {NULL, -3}};
563 {"lognet", 8},
564#endif
565 {"peer_id", 9}, {NULL, -3}};
566static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}}; 561static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}};
567 562
568/* For the replies that are one fixed string and nothing else, where the only 563/* For the replies that are one fixed string and nothing else, where the only
@@ -687,43 +682,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
687 OT_SETIP(&ws->peer, tmp_buf1); 682 OT_SETIP(&ws->peer, tmp_buf1);
688 } break; 683 } break;
689#endif 684#endif
690#ifdef WANT_FULLLOG_NETWORKS 685 case 8: /* matched "peer_id" */
691 case 8: /* matched "lognet" */
692 {
693 // if( accesslist_is_blessed( cookie->ip, OT_PERMISSION_MAY_STAT ) ) {
694 char *tmp_buf = ws->reply;
695 ot_net net;
696 uint64_t value;
697 signed short parsed, bits;
698
699 len = urlencoded_parse(&read_ptr, tmp_buf, SCAN_SEARCHPATH_VALUE);
700 tmp_buf[len] = 0;
701 if (len <= 0)
702 HTTPERROR_400_PARAM;
703 if (*tmp_buf == '-') {
704 loglist_reset();
705 return ws->reply_size = reply_literal(ws, "Successfully removed.\n");
706 }
707 parsed = ip_parse(tmp_buf, net.address);
708 if (!parsed)
709 HTTPERROR_400_PARAM;
710 if (tmp_buf[parsed++] != '/')
711 bits = 128;
712 else {
713 parsed = scan_uint(tmp_buf + parsed, &value, 128);
714 if (!parsed)
715 HTTPERROR_400_PARAM;
716 bits = (signed short)value;
717 if (ip_is_v4mapped(net.address))
718 bits += 96;
719 }
720 net.bits = bits;
721 loglist_add_network(&net);
722 return ws->reply_size = reply_literal(ws, "Successfully added.\n");
723 //}
724 } break;
725#endif
726 case 9: /* matched "peer_id" */
727 /* ignore this, when we have less than 20 bytes */ 686 /* ignore this, when we have less than 20 bytes */
728 if (urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20) 687 if (urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20)
729 HTTPERROR_400_PARAM; 688 HTTPERROR_400_PARAM;
@@ -757,30 +716,6 @@ ssize_t http_handle_request(const int64_t sock, struct ot_workstruct *ws) {
757 ssize_t reply_off, len; 716 ssize_t reply_off, len;
758 char *read_ptr = ws->request, *write_ptr; 717 char *read_ptr = ws->request, *write_ptr;
759 718
760#ifdef WANT_FULLLOG_NETWORKS
761 struct http_data *cookie = fdstate_cookie(sock);
762 if (loglist_check_address(cookie->ip)) {
763 ot_log *log = malloc(sizeof(ot_log));
764 if (log) {
765 log->size = ws->request_size;
766 log->data = malloc(ws->request_size);
767 log->next = 0;
768 log->time = time_now_seconds();
769 memcpy(log->ip, cookie->ip, sizeof(ot_ip6));
770 if (log->data) {
771 memcpy(log->data, ws->request, ws->request_size);
772 if (!g_logchain_first)
773 g_logchain_first = g_logchain_last = log;
774 else {
775 g_logchain_last->next = log;
776 g_logchain_last = log;
777 }
778 } else
779 free(log);
780 }
781 }
782#endif
783
784#ifdef _DEBUG_HTTPERROR 719#ifdef _DEBUG_HTTPERROR
785 reply_off = ws->request_size; 720 reply_off = ws->request_size;
786 if (ws->request_size >= G_DEBUGBUF_SIZE) 721 if (ws->request_size >= G_DEBUGBUF_SIZE)
diff --git a/ot_stats.c b/ot_stats.c
index d38355a..a2eb084 100644
--- a/ot_stats.c
+++ b/ot_stats.c
@@ -4,7 +4,6 @@
4 $id$ */ 4 $id$ */
5 5
6/* System */ 6/* System */
7#include <inttypes.h>
8#include <pthread.h> 7#include <pthread.h>
9#include <stdatomic.h> 8#include <stdatomic.h>
10#include <stdio.h> 9#include <stdio.h>
@@ -19,6 +18,7 @@
19 18
20/* Opentracker */ 19/* Opentracker */
21#include "ot_accesslist.h" 20#include "ot_accesslist.h"
21#include "ot_event.h"
22#include "ot_hex.h" 22#include "ot_hex.h"
23#include "ot_iovec.h" 23#include "ot_iovec.h"
24#include "ot_ip.h" 24#include "ot_ip.h"
@@ -117,10 +117,6 @@ union stats_network_node {
117 stats_network_node *children[STATS_NETWORK_NODE_COUNT]; 117 stats_network_node *children[STATS_NETWORK_NODE_COUNT];
118}; 118};
119 119
120#ifdef WANT_LOG_NETWORKS
121static stats_network_node *stats_network_counters_root;
122#endif
123
124static int stat_increase_network_count(stats_network_node **pnode, int depth, uintptr_t ip) { 120static int stat_increase_network_count(stats_network_node **pnode, int depth, uintptr_t ip) {
125 int foo = __LDR(ip, depth); 121 int foo = __LDR(ip, depth);
126 stats_network_node *node; 122 stats_network_node *node;
@@ -504,36 +500,6 @@ static void stats_return_numwants(char **reply, const char *reply_en
504} 500}
505#endif 501#endif
506 502
507#ifdef WANT_FULLLOG_NETWORKS
508static void stats_return_fulllog(size_t *iovec_entries, struct iovec **iovector, char *r) {
509 ot_log *loglist = g_logchain_first, *llnext;
510 char *re = r + OT_STATS_TMPSIZE;
511
512 g_logchain_first = g_logchain_last = 0;
513
514 while (loglist) {
515 if (r + (loglist->size + 64) >= re) {
516 r = iovec_next_chunk(iovec_entries, iovector, r, 32 * OT_STATS_TMPSIZE);
517 if (!r)
518 return;
519 re = r + 32 * OT_STATS_TMPSIZE;
520 }
521 char ip_readable[64];
522
523 ip_readable[ip_format(ip_readable, loglist->ip)] = 0;
524 fmt_append(&r, re, "%08ld: %s\n", loglist->time, ip_readable);
525 fmt_bytes(&r, re, loglist->data, loglist->size);
526 fmt_append(&r, re, "\n*\n\n");
527
528 llnext = loglist->next;
529 free(loglist->data);
530 free(loglist);
531 loglist = llnext;
532 }
533 iovec_trim_last(iovec_entries, iovector, r);
534}
535#endif
536
537static void stats_return_everything(char **reply, const char *reply_end) { 503static void stats_return_everything(char **reply, const char *reply_end) {
538 unsigned long long total[OT_COUNTER_COUNT]; 504 unsigned long long total[OT_COUNTER_COUNT];
539 505
@@ -546,10 +512,6 @@ static void stats_return_everything(char **reply, const char *reply_end) {
546 512
547 fmt_append(reply, reply_end, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); 513 fmt_append(reply, reply_end, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
548 fmt_append(reply, reply_end, "<stats>\n"); 514 fmt_append(reply, reply_end, "<stats>\n");
549 fmt_append(reply, reply_end, " <tracker_id>%" PRIu32 "</tracker_id>\n", g_tracker_id);
550 fmt_append(reply, reply_end, " <version>\n");
551 stats_return_tracker_version(reply, reply_end);
552 fmt_append(reply, reply_end, " </version>\n");
553 fmt_append(reply, reply_end, " <uptime>%llu</uptime>\n", (unsigned long long)(time(NULL) - ot_start_time)); 515 fmt_append(reply, reply_end, " <uptime>%llu</uptime>\n", (unsigned long long)(time(NULL) - ot_start_time));
554 fmt_append(reply, reply_end, " <torrents>\n"); 516 fmt_append(reply, reply_end, " <torrents>\n");
555 fmt_append(reply, reply_end, " <count_mutex>%zd</count_mutex>\n", mutex_get_torrent_count()); 517 fmt_append(reply, reply_end, " <count_mutex>%zd</count_mutex>\n", mutex_get_torrent_count());
@@ -580,10 +542,99 @@ static void stats_return_everything(char **reply, const char *reply_end) {
580 fmt_append(reply, reply_end, "</stats>"); 542 fmt_append(reply, reply_end, "</stats>");
581} 543}
582 544
545/* The names as the Makefile spells them, so that what a binary reports maps
546 straight back onto the FEATURES lines it was built from */
547static const char *const build_features[] = {
548#ifdef WANT_V4_ONLY
549 "WANT_V4_ONLY",
550#endif
551#ifdef WANT_ACCESSLIST_BLACK
552 "WANT_ACCESSLIST_BLACK",
553#endif
554#ifdef WANT_ACCESSLIST_WHITE
555 "WANT_ACCESSLIST_WHITE",
556#endif
557#ifdef WANT_DYNAMIC_ACCESSLIST
558 "WANT_DYNAMIC_ACCESSLIST",
559#endif
560#ifdef WANT_SYNC_LIVE
561 "WANT_SYNC_LIVE",
562#endif
563#ifdef WANT_IP_FROM_QUERY_STRING
564 "WANT_IP_FROM_QUERY_STRING",
565#endif
566#ifdef WANT_IP_FROM_PROXY
567 "WANT_IP_FROM_PROXY",
568#endif
569#ifdef WANT_COMPRESSION_GZIP
570 "WANT_COMPRESSION_GZIP",
571#endif
572#ifdef WANT_COMPRESSION_GZIP_ALWAYS
573 "WANT_COMPRESSION_GZIP_ALWAYS",
574#endif
575#ifdef WANT_COMPRESSION_ZSTD
576 "WANT_COMPRESSION_ZSTD",
577#endif
578#ifdef WANT_COMPRESSION_ZSTD_ALWAYS
579 "WANT_COMPRESSION_ZSTD_ALWAYS",
580#endif
581#ifdef WANT_FULLSCRAPE
582 "WANT_FULLSCRAPE",
583#endif
584#ifdef WANT_MODEST_FULLSCRAPES
585 "WANT_MODEST_FULLSCRAPES",
586#endif
587#ifdef WANT_KEEPALIVE
588 "WANT_KEEPALIVE",
589#endif
590#ifdef WANT_RESTRICT_STATS
591 "WANT_RESTRICT_STATS",
592#endif
593#ifdef WANT_LOG_NUMWANT
594 "WANT_LOG_NUMWANT",
595#endif
596#ifdef WANT_SPOT_WOODPECKER
597 "WANT_SPOT_WOODPECKER",
598#endif
599#ifdef WANT_SYSLOGS
600 "WANT_SYSLOGS",
601#endif
602#ifdef WANT_DEV_RANDOM
603 "WANT_DEV_RANDOM",
604#endif
605#ifdef WANT_INSECURE_RANDOM_FALLBACK
606 "WANT_INSECURE_RANDOM_FALLBACK",
607#endif
608#ifdef _DEBUG
609 "_DEBUG",
610#endif
611#ifdef _DEBUG_HTTPERROR
612 "_DEBUG_HTTPERROR",
613#endif
614#ifdef _DEBUG_RANDOMTORRENTS
615 "_DEBUG_RANDOMTORRENTS",
616#endif
617 NULL};
618
619void stats_return_tracker_features(char **reply, const char *reply_end) {
620 size_t i;
621
622 fmt_append(reply, reply_end, "events: %s\n", event_mechanism());
623 fmt_append(reply, reply_end, "features:");
624 for (i = 0; build_features[i]; ++i)
625 fmt_append(reply, reply_end, " %s", build_features[i]);
626 if (!build_features[0])
627 fmt_append(reply, reply_end, " none");
628 fmt_append(reply, reply_end, "\n");
629}
630
583void stats_return_tracker_version(char **reply, const char *reply_end) { 631void stats_return_tracker_version(char **reply, const char *reply_end) {
584#define QUOTE(name) #name 632 fmt_append(reply, reply_end, "opentracker %s", OT_VERSION);
585#define SQUOTE(name) QUOTE(name) 633 if (*GIT_VERSION && strcmp(GIT_VERSION, OT_TAG))
586 fmt_append(reply, reply_end, "https://erdgeist.org/gitweb/opentracker/commit/?id=" SQUOTE(GIT_VERSION) "\n"); 634 fmt_append(reply, reply_end, " (%s)", GIT_VERSION);
635 fmt_append(reply, reply_end, "\n");
636 if (*OT_TAG)
637 fmt_append(reply, reply_end, "https://erdgeist.org/gitweb/opentracker/tag/?h=%s\n", OT_TAG);
587} 638}
588 639
589void return_stats_for_tracker(char **reply, const char *reply_end, int mode, int format) { 640void return_stats_for_tracker(char **reply, const char *reply_end, int mode, int format) {
@@ -610,9 +661,6 @@ void return_stats_for_tracker(char **reply, const char *reply_end, int mode, int
610 case TASK_STATS_HTTPERRORS: 661 case TASK_STATS_HTTPERRORS:
611 stats_httperrors_txt(reply, reply_end); 662 stats_httperrors_txt(reply, reply_end);
612 break; 663 break;
613 case TASK_STATS_VERSION:
614 stats_return_tracker_version(reply, reply_end);
615 break;
616 case TASK_STATS_RENEW: 664 case TASK_STATS_RENEW:
617 stats_return_renew_bucket(reply, reply_end); 665 stats_return_renew_bucket(reply, reply_end);
618 break; 666 break;
@@ -671,11 +719,6 @@ static void stats_make(size_t *iovec_entries, struct iovec **iovector, ot_taskty
671 stats_return_woodpeckers(&r, r_end, 128); 719 stats_return_woodpeckers(&r, r_end, 128);
672 break; 720 break;
673#endif 721#endif
674#ifdef WANT_FULLLOG_NETWORKS
675 case TASK_STATS_FULLLOG:
676 stats_return_fulllog(iovec_entries, iovector, r);
677 return;
678#endif
679 default: 722 default:
680 iovec_free(iovec_entries, iovector); 723 iovec_free(iovec_entries, iovector);
681 return; 724 return;
@@ -693,9 +736,6 @@ void stats_issue_event(struct ot_workstruct *ws, ot_status_event event, PROTO_FL
693 COUNT(OT_COUNTER_TCP_CONNECTIONS, 1); 736 COUNT(OT_COUNTER_TCP_CONNECTIONS, 1);
694 else 737 else
695 COUNT(OT_COUNTER_UDP_CONNECTIONS, 1); 738 COUNT(OT_COUNTER_UDP_CONNECTIONS, 1);
696#ifdef WANT_LOG_NETWORKS
697 stat_increase_network_count(&stats_network_counters_root, 0, event_data);
698#endif
699 break; 739 break;
700 case EVENT_ANNOUNCE: 740 case EVENT_ANNOUNCE:
701 if (proto == FLAG_TCP) 741 if (proto == FLAG_TCP)
diff --git a/ot_stats.h b/ot_stats.h
index f484765..f9a9d87 100644
--- a/ot_stats.h
+++ b/ot_stats.h
@@ -35,6 +35,7 @@ void stats_deliver(int64_t sock, int tasktype);
35void stats_cleanup(void); 35void stats_cleanup(void);
36void return_stats_for_tracker(char **reply, const char *reply_end, int mode, int format); 36void return_stats_for_tracker(char **reply, const char *reply_end, int mode, int format);
37void stats_return_tracker_version(char **reply, const char *reply_end); 37void stats_return_tracker_version(char **reply, const char *reply_end);
38void stats_return_tracker_features(char **reply, const char *reply_end);
38void stats_init(void); 39void stats_init(void);
39void stats_deinit(void); 40void stats_deinit(void);
40 41
diff --git a/ot_sync.c b/ot_sync.c
deleted file mode 100644
index c19cf36..0000000
--- a/ot_sync.c
+++ /dev/null
@@ -1,165 +0,0 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <pthread.h>
8#include <stdio.h>
9#include <string.h>
10#include <sys/types.h>
11#include <sys/uio.h>
12
13/* Opentracker */
14#include "ot_fmt.h"
15#include "ot_iovec.h"
16#include "ot_mutex.h"
17#include "ot_workqueue.h"
18#include "ot_scan.h"
19#include "ot_stats.h"
20#include "ot_sync.h"
21#include "trackerlogic.h"
22
23#ifdef WANT_SYNC_BATCH
24
25#define OT_SYNC_CHUNK_SIZE (512 * 1024)
26
27/* Import Changeset from an external authority
28 format: d4:syncd[..]ee
29 [..]: ( 20:01234567890abcdefghij16:XXXXYYYY )+
30*/
31int add_changeset_to_tracker(uint8_t *data, size_t len) {
32 ot_hash *hash;
33 uint8_t *end = data + len;
34 uint64_t peer_count;
35
36 /* We do know, that the string is \n terminated, so it can't
37 overflow */
38 if (!memcmp(data, "d4:syncd", 8))
39 return -1;
40 data += 8;
41
42 while (1) {
43 if (!memcmp(data, "20:", 3)) {
44 if (!memcmp(data, "ee", 2))
45 return -1;
46 return 0;
47 }
48 data += 3;
49 hash = (ot_hash *)data;
50 data += sizeof(ot_hash);
51
52 /* Scan string length indicator, strictly: bencode writes no leading zeros */
53 data += (len = scan_uint_bencode((char *)data, &peer_count, (uint64_t)(end - data)));
54
55 /* If no long was scanned, it is not divisible by 8, it is not
56 followed by a colon or claims to need to much memory, we fail */
57 if (!len || !peer_count || (peer_count & 7) || (*data++ != ':') || (data + peer_count > end))
58 return -1;
59
60 while (peer_count > 0) {
61 add_peer_to_torrent(hash, (ot_peer *)data, 1);
62 data += 8;
63 peer_count -= 8;
64 }
65 }
66 return 0;
67}
68
69/* Proposed output format
70 d4:syncd20:<info_hash>8*N:(xxxxyyyy)*Nee
71*/
72static void sync_make(size_t *iovec_entries, struct iovec **iovector) {
73 size_t bucket;
74 char *r, *re;
75
76 /* Setup return vector... */
77 *iovec_entries = 0;
78 *iovector = NULL;
79 if (!(r = iovec_new_chunk(iovec_entries, iovector, OT_SYNC_CHUNK_SIZE)))
80 return;
81
82 /* ... and pointer to end of current output buffer.
83 This works as a low watermark */
84 re = r + OT_SYNC_CHUNK_SIZE;
85
86 memmove(r, "d4:syncd", 8);
87 r += 8;
88
89 /* For each bucket... */
90 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
91 /* Get exclusive access to that bucket */
92 ot_vector *torrents_list = mutex_bucket_lock(bucket);
93 size_t tor_offset;
94
95 /* For each torrent in this bucket.. */
96 for (tor_offset = 0; tor_offset < torrents_list->size; ++tor_offset) {
97 /* Address torrents members */
98 ot_peerlist *peer_list = (((ot_torrent *)(torrents_list->data))[tor_offset]).peer_list;
99 ot_hash *hash = &(((ot_torrent *)(torrents_list->data))[tor_offset]).hash;
100 const size_t byte_count = sizeof(ot_peer) * peer_list->changeset.size;
101
102 /* If we reached our low watermark in buffer... */
103 if (re - r <= (ssize_t)(/* strlen( "20:" ) == */ 3 + sizeof(ot_hash) + /* strlen_max( "%zd" ) == */ 12 + byte_count)) {
104
105 /* Allocate a fresh output buffer at the end of our buffers list
106 release bucket and return, if that fails */
107 if (!(r = iovec_next_chunk(iovec_entries, iovector, r, OT_SYNC_CHUNK_SIZE)))
108 return mutex_bucket_unlock(bucket);
109
110 /* Adjust new end of output buffer */
111 re = r + OT_SYNC_CHUNK_SIZE;
112 }
113
114 fmt_append(&r, re, "20:");
115 fmt_bytes(&r, re, hash, sizeof(ot_hash));
116 fmt_append(&r, re, "%zd:", byte_count);
117 fmt_bytes(&r, re, peer_list->changeset.data, byte_count);
118 }
119
120 /* All torrents done: release lock on currenct bucket */
121 mutex_bucket_unlock(bucket);
122 }
123
124 /* Close bencoded sync dictionary */
125 *r++ = 'e';
126 *r++ = 'e';
127
128 /* Release unused memory in current output buffer */
129 iovec_trim_last(iovec_entries, iovector, r);
130}
131
132/* This is the entry point into this worker thread
133 It grabs tasks from mutex_tasklist and delivers results back
134*/
135static void *sync_worker(void *args) {
136 size_t iovec_entries;
137 struct iovec *iovector;
138
139 args = args;
140
141 while (1) {
142 ot_tasktype tasktype = TASK_SYNC_OUT;
143 ot_taskid taskid = workqueue_poptask(&tasktype);
144 sync_make(&iovec_entries, &iovector);
145 stats_issue_event(ws, EVENT_SYNC_OUT, FLAG_TCP, iovec_length(&iovec_entries, &iovector));
146 if (workqueue_pushresult(taskid, iovec_entries, iovector))
147 iovec_free(&iovec_entries, &iovector);
148 }
149 return NULL;
150}
151
152static pthread_t thread_id;
153void sync_init( ) {
154 pthread_create( &thread_id, NULL, sync_worker, NULL );
155}
156
157void sync_deinit( ) {
158 pthread_cancel( thread_id );
159}
160
161void sync_deliver( int64_t socket ) {
162 workqueue_pushtask( socket, TASK_SYNC_OUT );
163}
164
165#endif
diff --git a/ot_sync.h b/ot_sync.h
deleted file mode 100644
index b6964fa..0000000
--- a/ot_sync.h
+++ /dev/null
@@ -1,26 +0,0 @@
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_SYNC_H__
7#define OT_SYNC_H__
8
9#include <stdint.h>
10
11#ifdef WANT_SYNC_BATCH
12enum { SYNC_IN, SYNC_OUT };
13
14void sync_init();
15void sync_deinit();
16void sync_deliver(int64_t socket);
17
18int add_changeset_to_tracker(uint8_t *data, size_t len);
19#else
20
21#define sync_init()
22#define sync_deinit()
23
24#endif
25
26#endif
diff --git a/ot_workqueue.h b/ot_workqueue.h
index f99a7b5..4acd6fd 100644
--- a/ot_workqueue.h
+++ b/ot_workqueue.h
@@ -23,7 +23,6 @@ typedef enum {
23 TASK_STATS_FULLSCRAPE = 0x0005, 23 TASK_STATS_FULLSCRAPE = 0x0005,
24 TASK_STATS_TPB = 0x0006, 24 TASK_STATS_TPB = 0x0006,
25 TASK_STATS_HTTPERRORS = 0x0007, 25 TASK_STATS_HTTPERRORS = 0x0007,
26 TASK_STATS_VERSION = 0x0008,
27 TASK_STATS_RENEW = 0x000a, 26 TASK_STATS_RENEW = 0x000a,
28 TASK_STATS_SYNCS = 0x000b, 27 TASK_STATS_SYNCS = 0x000b,
29 TASK_STATS_COMPLETED = 0x000c, 28 TASK_STATS_COMPLETED = 0x000c,
@@ -36,7 +35,6 @@ typedef enum {
36 TASK_STATS_TOP10 = 0x0104, 35 TASK_STATS_TOP10 = 0x0104,
37 TASK_STATS_TOP100 = 0x0105, 36 TASK_STATS_TOP100 = 0x0105,
38 TASK_STATS_EVERYTHING = 0x0106, 37 TASK_STATS_EVERYTHING = 0x0106,
39 TASK_STATS_FULLLOG = 0x0107,
40 TASK_STATS_WOODPECKERS = 0x0108, 38 TASK_STATS_WOODPECKERS = 0x0108,
41 39
42 TASK_FULLSCRAPE = 0x0200, /* Default mode */ 40 TASK_FULLSCRAPE = 0x0200, /* Default mode */
diff --git a/proxy.c b/proxy.c
index b06e256..f6e90a3 100644
--- a/proxy.c
+++ b/proxy.c
@@ -3,6 +3,10 @@
3 3
4 $Id$ */ 4 $Id$ */
5 5
6/* This relays sync traffic between trackers, and between multicast domains
7 that have no route to each other, so that no tracker has to reach out to
8 the outside world by itself. Defunc. */
9
6/* System */ 10/* System */
7#include <arpa/inet.h> 11#include <arpa/inet.h>
8#include <ctype.h> 12#include <ctype.h>
diff --git a/tests/integration/test_stats.py b/tests/integration/test_stats.py
index 820b917..088d8a1 100644
--- a/tests/integration/test_stats.py
+++ b/tests/integration/test_stats.py
@@ -15,10 +15,10 @@ import xml.etree.ElementTree
15from harness import TrackerTestCase 15from harness import TrackerTestCase
16 16
17# The modes a default build answers. "tpbs" and "statedump" are full scrapes 17# The modes a default build answers. "tpbs" and "statedump" are full scrapes
18# and belong to test_fullscrape; "fulllog" and "woodpeckers" sit behind 18# and belong to test_fullscrape; "woodpeckers" sits behind a
19# feature flags this build does not set. 19# feature flag this build does not set.
20MRTG_MODES = ["peer", "conn", "scrp", "udp4", "tcp4", "torr", "fscr", "completed", "syncs"] 20MRTG_MODES = ["peer", "conn", "scrp", "udp4", "tcp4", "torr", "fscr", "completed", "syncs"]
21TEXT_MODES = ["herr", "s24s", "top10", "top100", "renew", "version"] 21TEXT_MODES = ["herr", "s24s", "top10", "top100", "renew"]
22 22
23 23
24class StatsShape(TrackerTestCase): 24class StatsShape(TrackerTestCase):
@@ -55,9 +55,22 @@ class StatsShape(TrackerTestCase):
55 self.assertIsNotNone(root.find("torrents/count_mutex")) 55 self.assertIsNotNone(root.find("torrents/count_mutex"))
56 self.assertIsNotNone(root.find("connections/tcp/announce")) 56 self.assertIsNotNone(root.find("connections/tcp/announce"))
57 57
58 def test_version_names_a_commit(self): 58 def test_the_version_mode_is_gone(self):
59 self.assertIn(b"opentracker", self.tracker.stats("version").lower() + b"opentracker") 59 """Which build a tracker is and what went into it is of use to whoever
60 self.assertRegex(self.tracker.stats("version"), rb"https?://") 60 attacks it and to nobody else on the far end of a socket. It is
61 reported by -v, to whoever already has the binary."""
62 with self.assertRaises(urllib.error.HTTPError) as caught:
63 self.tracker.stats("version")
64 self.assertEqual(caught.exception.code, 400, "an unknown mode, like any other")
65
66 def test_everything_does_not_name_the_build(self):
67 self.assertNotIn(b"opentracker 1", self.tracker.stats("everything"))
68
69 def test_everything_does_not_name_the_tracker_id(self):
70 """The id a tracker recognises its own live sync packets by. Knowing
71 it is of no use to a client and of some use to whoever would like a
72 tracker to discard what it is told."""
73 self.assertNotIn(b"tracker_id", self.tracker.stats("everything"))
61 74
62 75
63class StatsCount(TrackerTestCase): 76class StatsCount(TrackerTestCase):
diff --git a/tests/integration/test_version.py b/tests/integration/test_version.py
new file mode 100644
index 0000000..66f570d
--- /dev/null
+++ b/tests/integration/test_version.py
@@ -0,0 +1,36 @@
1"""The -v report, which is where a build says what went into it.
2
3Driving the binary is the only way to see this: the feature list is a table
4of #ifdefs, so it is only true for the flags the binary itself was built with.
5"""
6
7import subprocess
8import unittest
9
10from harness import TRACKER
11
12
13class VersionFlag(unittest.TestCase):
14 def report(self):
15 # -v writes to stderr and exits before it binds anything
16 done = subprocess.run([TRACKER, "-v"], capture_output=True, timeout=30)
17 self.assertEqual(done.returncode, 0, done.stderr)
18 return done.stderr.decode()
19
20 def test_first_line_names_the_release(self):
21 # The commit in brackets is there in a checkout and in a release
22 # tarball, and absent in a build that had neither to ask.
23 first = self.report().splitlines()[0]
24 self.assertRegex(first, r"^opentracker \d+\.\d+\.\d+( \(\S+\))?$")
25
26 def test_names_the_event_mechanism(self):
27 self.assertRegex(self.report(), r"(?m)^events: (epoll|kqueue|poll)$")
28
29 def test_lists_the_features_this_build_has(self):
30 # The Makefile builds this binary with these and not with those, so a
31 # feature table that stops following the build fails here.
32 report = self.report()
33 for want in ("WANT_KEEPALIVE", "WANT_ACCESSLIST_BLACK", "WANT_DYNAMIC_ACCESSLIST"):
34 self.assertIn(want, report)
35 for unwanted in ("WANT_SPOT_WOODPECKER", "WANT_V4_ONLY", "WANT_RESTRICT_STATS"):
36 self.assertNotIn(unwanted, report)
diff --git a/tests/runner.c b/tests/runner.c
index dcb009c..97c6d51 100644
--- a/tests/runner.c
+++ b/tests/runner.c
@@ -28,6 +28,7 @@ static const struct module modules[] = {{"ot_urlencoded", test_urlencoded},
28 {"ot_ip", test_ip}, 28 {"ot_ip", test_ip},
29 {"ot_socket", test_socket}, 29 {"ot_socket", test_socket},
30 {"ot_mutex", test_mutex}, 30 {"ot_mutex", test_mutex},
31 {"trackerlogic", test_trackerlogic},
31 {"ot_workqueue", test_workqueue}, 32 {"ot_workqueue", test_workqueue},
32 {"ot_batch", test_batch}, 33 {"ot_batch", test_batch},
33 {"ot_fdstate", test_fdstate}, 34 {"ot_fdstate", test_fdstate},
diff --git a/tests/stubs.c b/tests/stubs.c
index a8e6962..5999e2d 100644
--- a/tests/stubs.c
+++ b/tests/stubs.c
@@ -8,11 +8,9 @@
8 behaviour should be supplied here rather than the test weakened. */ 8 behaviour should be supplied here rather than the test weakened. */
9 9
10/* Opentracker */ 10/* Opentracker */
11#include "ot_stats.h"
11#include "trackerlogic.h" 12#include "trackerlogic.h"
12 13
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 14/* ot_accesslist.c is built with WANT_ACCESSLIST_BLACK so that the hash parser
17 is reachable. These two belong to accesslist_readfile and the reload worker, 15 is reachable. These two belong to accesslist_readfile and the reload worker,
18 neither of which any test drives. */ 16 neither of which any test drives. */
@@ -26,3 +24,32 @@ long io_trywrite(long d, const char *buf, long len) {
26 (void)buf; 24 (void)buf;
27 return len; 25 return len;
28} 26}
27
28/* trackerlogic.c is under test, and starting it up starts every module it
29 leans on. None of them is driven by these tests, so they are answered here
30 rather than linked in and left to run threads of their own. */
31void clean_init(void) {}
32void clean_deinit(void) {}
33int clean_single_torrent(ot_torrent *torrent) {
34 (void)torrent;
35 return 0;
36}
37void fullscrape_init(void) {}
38void fullscrape_deinit(void) {}
39void livesync_init(void) {}
40void livesync_deinit(void) {}
41void livesync_tell(struct ot_workstruct *ws) { (void)ws; }
42void stats_init(void) {}
43void stats_deinit(void) {}
44void stats_issue_event(struct ot_workstruct *ws, ot_status_event event, PROTO_FLAG proto, uintptr_t event_data) {
45 (void)ws;
46 (void)event;
47 (void)proto;
48 (void)event_data;
49}
50void stats_counters_register(struct ot_workstruct *ws) { (void)ws; }
51void stats_counters_retire(struct ot_workstruct *ws) { (void)ws; }
52
53const char *g_stats_path;
54ssize_t g_stats_path_len;
55uint32_t g_tracker_id;
diff --git a/tests/t_trackerlogic.c b/tests/t_trackerlogic.c
new file mode 100644
index 0000000..d3d8755
--- /dev/null
+++ b/tests/t_trackerlogic.c
@@ -0,0 +1,243 @@
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 <arpa/inet.h>
8#include <signal.h>
9#include <pthread.h>
10#include <string.h>
11
12/* Opentracker */
13#include "ot_mutex.h"
14#include "trackerlogic.h"
15#include "test.h"
16
17/* The swarm itself: what an announce does to a torrent, and what a scrape
18 says about it afterwards. The counts are read back through the udp scrape
19 because that is the one reply the tracker builds as plain numbers. */
20
21static struct ot_workstruct ws;
22
23static void hash_named(ot_hash hash, const char *name) {
24 memset(hash, 0, sizeof(ot_hash));
25 memcpy(hash, name, strlen(name) < sizeof(ot_hash) ? strlen(name) : sizeof(ot_hash));
26}
27
28/* An IPv4 peer arrives as the v4 mapped form the tracker stores */
29static void peer_v4(ot_peer6 peer, uint8_t last, uint16_t port) {
30 ot_ip6 ip = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 10, 0, 0, 0};
31 uint16_t net_port = htons(port);
32
33 ip[15] = last;
34 memset(peer, 0, sizeof(ot_peer6));
35 OT_SETIP(peer, ip);
36 OT_SETPORT(peer, &net_port);
37 OT_PEERFLAG(peer) = 0;
38}
39
40static void peer_v6(ot_peer6 peer, uint8_t last, uint16_t port) {
41 ot_ip6 ip = {0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
42 uint16_t net_port = htons(port);
43
44 ip[15] = last;
45 memset(peer, 0, sizeof(ot_peer6));
46 OT_SETIP(peer, ip);
47 OT_SETPORT(peer, &net_port);
48 OT_PEERFLAG(peer) = 0;
49}
50
51static void announce(ot_hash *hash, ot_peer6 peer) {
52 ws.hash = hash;
53 memcpy(ws.peer, peer, sizeof(ot_peer6));
54 ws.reply = ws.outbuf;
55 add_peer_to_torrent_and_return_peers(FLAG_TCP, &ws, 50);
56}
57
58/* seeders, downloads completed, leechers */
59static void scrape(ot_hash hash, uint32_t counts[3]) {
60 char reply[12];
61
62 return_udp_scrape_for_torrent(hash, reply);
63 counts[0] = ntohl(((uint32_t *)reply)[0]);
64 counts[1] = ntohl(((uint32_t *)reply)[1]);
65 counts[2] = ntohl(((uint32_t *)reply)[2]);
66}
67
68static void case_an_announce_makes_a_torrent(void) {
69 ot_hash hash;
70 ot_peer6 peer;
71 uint32_t counts[3];
72 size_t before = mutex_get_torrent_count();
73
74 test_case("the first announce for a hash creates its torrent");
75 hash_named(hash, "first-announce");
76 peer_v4(peer, 1, 6881);
77 announce(&hash, peer);
78
79 CHECK_INT((int)(mutex_get_torrent_count() - before), 1);
80 scrape(hash, counts);
81 CHECK_INT((int)counts[2], 1); /* one leecher */
82 CHECK_INT((int)counts[0], 0);
83}
84
85static void case_the_same_peer_twice_is_one_peer(void) {
86 ot_hash hash;
87 ot_peer6 peer;
88 uint32_t counts[3];
89
90 test_case("announcing twice renews a peer rather than adding another");
91 hash_named(hash, "renewing");
92 peer_v4(peer, 2, 6881);
93 announce(&hash, peer);
94 announce(&hash, peer);
95
96 scrape(hash, counts);
97 CHECK_INT((int)counts[2], 1);
98}
99
100static void case_a_port_makes_a_different_peer(void) {
101 ot_hash hash;
102 ot_peer6 peer;
103 uint32_t counts[3];
104
105 test_case("the same address on another port is another peer");
106 hash_named(hash, "two-ports");
107 peer_v4(peer, 3, 6881);
108 announce(&hash, peer);
109 peer_v4(peer, 3, 6882);
110 announce(&hash, peer);
111
112 scrape(hash, counts);
113 CHECK_INT((int)counts[2], 2);
114}
115
116static void case_seeding_is_counted_apart(void) {
117 ot_hash hash;
118 ot_peer6 peer;
119 uint32_t counts[3];
120
121 test_case("a seeder counts as complete, a leecher as incomplete");
122 hash_named(hash, "seed-and-leech");
123 peer_v4(peer, 4, 6881);
124 OT_PEERFLAG(peer) |= PEER_FLAG_SEEDING;
125 announce(&hash, peer);
126 peer_v4(peer, 5, 6881);
127 announce(&hash, peer);
128
129 scrape(hash, counts);
130 CHECK_INT((int)counts[0], 1);
131 CHECK_INT((int)counts[2], 1);
132}
133
134static void case_completion_without_seeding_is_not_believed(void) {
135 ot_hash hash;
136 ot_peer6 peer;
137 uint32_t counts[3];
138
139 test_case("a peer that claims to have completed but is still leeching is not counted");
140 hash_named(hash, "claiming");
141 peer_v4(peer, 6, 6881);
142 OT_PEERFLAG(peer) |= PEER_FLAG_COMPLETED;
143 announce(&hash, peer);
144
145 scrape(hash, counts);
146 CHECK_INT((int)counts[1], 0);
147 CHECK_INT((int)counts[2], 1); /* still a leecher */
148}
149
150static void case_completing_is_counted_once(void) {
151 ot_hash hash;
152 ot_peer6 peer;
153 uint32_t counts[3];
154
155 test_case("a seeder reporting completion is counted once, however often it says so");
156 hash_named(hash, "completing");
157 peer_v4(peer, 7, 6881);
158 OT_PEERFLAG(peer) |= PEER_FLAG_COMPLETED | PEER_FLAG_SEEDING;
159 announce(&hash, peer);
160 announce(&hash, peer);
161 announce(&hash, peer);
162
163 scrape(hash, counts);
164 CHECK_INT((int)counts[1], 1);
165 CHECK_INT((int)counts[0], 1);
166}
167
168static void case_a_peer_can_leave(void) {
169 ot_hash hash;
170 ot_peer6 peer;
171 uint32_t counts[3];
172
173 test_case("a peer that stops is gone from the swarm");
174 hash_named(hash, "leaving");
175 peer_v4(peer, 7, 6881);
176 announce(&hash, peer);
177 peer_v4(peer, 8, 6881);
178 announce(&hash, peer);
179 scrape(hash, counts);
180 CHECK_INT((int)counts[2], 2);
181
182 ws.hash = &hash;
183 memcpy(ws.peer, peer, sizeof(ot_peer6));
184 remove_peer_from_torrent(FLAG_TCP, &ws);
185
186 scrape(hash, counts);
187 CHECK_INT((int)counts[2], 1);
188}
189
190static void case_the_families_are_kept_apart(void) {
191 ot_hash hash;
192 ot_peer6 peer;
193 uint32_t counts[3];
194
195 test_case("a v4 and a v6 peer of one swarm are both counted");
196 hash_named(hash, "dual-stack");
197 peer_v4(peer, 9, 6881);
198 announce(&hash, peer);
199 peer_v6(peer, 9, 6881);
200 announce(&hash, peer);
201
202 scrape(hash, counts);
203 CHECK_INT((int)counts[2], 2);
204}
205
206static void case_a_hash_nobody_announced(void) {
207 ot_hash hash;
208 uint32_t counts[3];
209
210 test_case("a hash nobody has announced scrapes as nothing at all");
211 hash_named(hash, "never-seen");
212 scrape(hash, counts);
213 CHECK_INT((int)counts[0], 0);
214 CHECK_INT((int)counts[1], 0);
215 CHECK_INT((int)counts[2], 0);
216}
217
218void test_trackerlogic(void) {
219 sigset_t signal_mask;
220
221 /* accesslist_init starts a thread that waits for SIGHUP and is stopped by
222 being sent one, so the signal has to be blocked here as the tracker
223 blocks it, or stopping the tracker logic ends this process instead */
224 sigemptyset(&signal_mask);
225 sigaddset(&signal_mask, SIGHUP);
226 pthread_sigmask(SIG_BLOCK, &signal_mask, NULL);
227
228 trackerlogic_init();
229 CHECK_INT(workstruct_init(&ws), 0);
230
231 case_an_announce_makes_a_torrent();
232 case_the_same_peer_twice_is_one_peer();
233 case_a_port_makes_a_different_peer();
234 case_seeding_is_counted_apart();
235 case_completion_without_seeding_is_not_believed();
236 case_completing_is_counted_once();
237 case_a_peer_can_leave();
238 case_the_families_are_kept_apart();
239 case_a_hash_nobody_announced();
240
241 workstruct_deinit(&ws);
242 trackerlogic_deinit();
243}
diff --git a/tests/test.h b/tests/test.h
index 7582dab..d7296cc 100644
--- a/tests/test.h
+++ b/tests/test.h
@@ -87,6 +87,7 @@ void test_buf(void);
87void test_ip(void); 87void test_ip(void);
88void test_socket(void); 88void test_socket(void);
89void test_mutex(void); 89void test_mutex(void);
90void test_trackerlogic(void);
90void test_workqueue(void); 91void test_workqueue(void);
91void test_batch(void); 92void test_batch(void);
92void test_fdstate(void); 93void test_fdstate(void);
diff --git a/trackerlogic.h b/trackerlogic.h
index 53edabd..39afece 100644
--- a/trackerlogic.h
+++ b/trackerlogic.h
@@ -256,10 +256,6 @@ void workstruct_deinit(struct ot_workstruct *ws);
256#define WANT_SYNC_PARAM(param) 256#define WANT_SYNC_PARAM(param)
257#endif 257#endif
258 258
259#ifdef WANT_LOG_NETWORKS
260#error Live logging networks disabled at the moment.
261#endif
262
263void trackerlogic_init(void); 259void trackerlogic_init(void);
264void trackerlogic_deinit(void); 260void trackerlogic_deinit(void);
265void exerr(const char *message); 261void exerr(const char *message);