summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile22
-rw-r--r--Makefile.arc4random3
-rw-r--r--Makefile.gzip4
-rw-r--r--Makefile.zstd3
-rw-r--r--man1/opentracker.1142
-rw-r--r--man4/opentracker.conf.486
-rw-r--r--opentracker.conf.sample5
-rw-r--r--ot_fullscrape.c150
-rw-r--r--ot_http.c37
-rw-r--r--ot_http.h5
-rw-r--r--ot_mutex.h3
-rw-r--r--ot_stats.c26
-rw-r--r--ot_stats.h1
13 files changed, 455 insertions, 32 deletions
diff --git a/Makefile b/Makefile
index a224845..706b742 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
@@ -43,8 +52,8 @@ FEATURES+=-DWANT_FULLSCRAPE
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# Is enabled on BSD systems by default in trackerlogic.h
46# on Linux systems you will need -lbds 55# on Linux systems the include Makefile adds -lbsd
47#FEATURES+=-DWANT_ARC4RANDOM 56#include Makefile.arc4random
48 57
49#FEATURES+=-D_DEBUG_HTTPERROR 58#FEATURES+=-D_DEBUG_HTTPERROR
50#FEATURES+=-D_DEBUG_RANDOMTORRENTS 59#FEATURES+=-D_DEBUG_RANDOMTORRENTS
@@ -55,8 +64,7 @@ OPTS_debug=-D_DEBUG -g -ggdb # -pg -fprofile-arcs -ftest-coverage
55OPTS_production=-O3 64OPTS_production=-O3
56 65
57CFLAGS+=-I$(LIBOWFAT_HEADERS) -DGIT_VERSION=$(GIT_VERSION) -Wall -pipe -pthread -Wextra #-ansi -pedantic 66CFLAGS+=-I$(LIBOWFAT_HEADERS) -DGIT_VERSION=$(GIT_VERSION) -Wall -pipe -pthread -Wextra #-ansi -pedantic
58LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread -lz 67LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread
59#LDFLAGS+=-lbsd
60 68
61BINARY =opentracker 69BINARY =opentracker
62HEADERS=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 70HEADERS=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
diff --git a/Makefile.arc4random b/Makefile.arc4random
new file mode 100644
index 0000000..1488408
--- /dev/null
+++ b/Makefile.arc4random
@@ -0,0 +1,3 @@
1FEATURES+=-DWANT_ARC4RANDOM
2LDFLAGS+=-lbsd
3
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..85ded7b
--- /dev/null
+++ b/man1/opentracker.1
@@ -0,0 +1,142 @@
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
23
24When invoked with parameters, it binds to TCP and UDP port 6969 on all
25interfaces. The recommended way to configure opentracker is by providing a
26config file using the
27.Op Fl f Ar config
28option. See
29.Xr opentracker.conf 4
30for details.
31.Pp
32
33.Sh OPTIONS
34The following options are available:
35
36.Bl -tag -width -indent=8
37.It Fl f Ar config
38Parse a config file with a list of options. Consecutive command options
39will override options from the config file. See
40.Xr opentracker.conf 4
41for details.
42
43.It Fl i Ar ip-select
44Select an ip address that will be used with the next
45.Op Fl p
46or
47.Op Fl P
48command to actually bind to this address. Setting this option without any bind
49options in the config file or
50.Op Fl p
51or
52.Op Fl P
53commands will limit opentracker to only bind to this address.
54.It Fl p Ar port-bind-tcp
55Bind to the TCP port on the last preceding ip address set with the
56.Op Fl i ip-select
57option or to all available addresses if none has been set. Can be given multiple
58times.
59.It Fl P Ar port-bind-udp
60Bind to the UDP port on the last preceding ip address set with the
61.Op Fl i ip-select
62option or to all available addresses if none has been set. Can be given multiple
63times.
64.It Fl A Ar blessed-ip
65Set an ip address in IPv4 or IPv6 or a net in CIDR notation to bless the network
66for access to restricted resources.
67.It Fl r Ar redirect-url
68Set the URL that
69.Nm
70will redirect users to when the / address is requested via HTTP.
71.It Fl d Ar chdir
72Sets the directory
73.Nm
74will
75.Xr chroot 2
76to if ran as root or
77.Xr chdir 2
78to if ran as unprivileged user. Note that any accesslist files need to be
79relative to and within that directory.
80.It Fl u Ar user
81User to run
82.Nm
83under after all operations that need privileges have finished.
84.It Fl w Ar accesslist | Fl b Ar accesslist
85If
86.Nm
87has been compiled with the
88.B WANT_ACCESSLIST_BLACK
89or
90.Br WANT_ACCESSLIST_WHITE
91options, this option sets the location of the accesslist.
92.El
93
94.Sh EXAMPLES
95
96Start
97.Nm
98bound on UDP and TCP ports 6969 on IPv6 localhost.
99
100.Dl # ./opentracker -i ::1 -p 6969 -P 6969
101
102.Pp
103Start
104.Nm
105bound on UDP port 6868 and TCP port 6868 on IPv4 localhost and allow
106privileged access from the network 192.168/16 while redirecting
107HTTP clients accessing the root directory, which is not covered by the
108bittorrent tracker protocol, to https://my-trackersite.com/.
109
110.Dl # ./opentracker -i 192.168.0.4 -p 6868 -P 6969 -A 192.168/16 -r https://my-trackersite.com/
111
112The announce URLs are http://192.168.0.4:6868/announce and
113udp://192.168.0.4:6868/announce respectively.
114
115.Sh FILES
116.Bl -tag -width indent
117.It Pa opentracker.conf
118The
119.Nm
120config file.
121.El
122.Sh SEE ALSO
123.Xr opentracker.conf 4
124.Pp
125opentracker documentation
126.Lk https://erdgeist.org/arts/software/opentracker
127.Pp
128Bittorrent tracker protocol
129.Lk http://www.bittorrent.org/beps/bep_0015.html
130.Sh
131.Sh AUTHOR
132.An Dirk Engling
133.Aq Mt erdgeist@erdgeist.org .
134.Sh LICENSE
135This software is released under the Beerware License:
136
137Permission is hereby granted, free of charge, to any person obtaining a copy of this software
138and associated documentation files (the "Software"), to deal in the Software with the following
139terms and conditions:
140
141If you meet the author(s) someday, and you think this software is worth it, you can buy them
142a beer in return.
diff --git a/man4/opentracker.conf.4 b/man4/opentracker.conf.4
new file mode 100644
index 0000000..b4f5f51
--- /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
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
22.It listen.tcp Ar address
23Specifies the address opentracker will listen on for TCP connections. Can be added more than once.
24
25.It listen.udp Ar address
26Specifies the address opentracker will listen on for UDP connections. Can be added more than once.
27
28.It listen.udp.workers Ar threads
29Specifies how many threads will be spawned to handle UDP connections. Defaults to 4.
30
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
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
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
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
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
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
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
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
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
58.It batchsync.cluster.admin_ip Ar ip_address
59Specifies the admin IP address for old-style (HTTP-based) asynchronous tracker syncing.
60
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
64.It tracker.user Ar username
65Specifies the user opentracker will setuid to after binding to potentially privileged ports.
66
67.It tracker.redirect_url Ar URL
68Specifies the URL opentracker will redirect to in response to a "GET / HTTP" request.
69
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
80.Sh SEE ALSO
81.Xr opentracker 1
82
83.Sh AUTHOR
84.An Dirk Engling
85.Aq Mt erdgeist@erdgeist.org
86
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_fullscrape.c b/ot_fullscrape.c
index aed2ad9..6fd6d1c 100644
--- a/ot_fullscrape.c
+++ b/ot_fullscrape.c
@@ -14,6 +14,10 @@
14#ifdef WANT_COMPRESSION_GZIP 14#ifdef WANT_COMPRESSION_GZIP
15#include <zlib.h> 15#include <zlib.h>
16#endif 16#endif
17#ifdef WANT_COMPRESSION_ZSTD
18#include <zstd.h>
19#endif
20
17 21
18/* Libowfat */ 22/* Libowfat */
19#include "byte.h" 23#include "byte.h"
@@ -40,6 +44,9 @@ static void fullscrape_make(int taskid, ot_tasktype mode);
40#ifdef WANT_COMPRESSION_GZIP 44#ifdef WANT_COMPRESSION_GZIP
41static void fullscrape_make_gzip(int taskid, ot_tasktype mode); 45static void fullscrape_make_gzip(int taskid, ot_tasktype mode);
42#endif 46#endif
47#ifdef WANT_COMPRESSION_ZSTD
48static void fullscrape_make_zstd(int taskid, ot_tasktype mode);
49#endif
43 50
44/* Converter function from memory to human readable hex strings 51/* Converter function from memory to human readable hex strings
45 XXX - Duplicated from ot_stats. Needs fix. */ 52 XXX - Duplicated from ot_stats. Needs fix. */
@@ -64,6 +71,11 @@ static void *fullscrape_worker(void *args) {
64 while (g_opentracker_running) { 71 while (g_opentracker_running) {
65 ot_tasktype tasktype = TASK_FULLSCRAPE; 72 ot_tasktype tasktype = TASK_FULLSCRAPE;
66 ot_taskid taskid = mutex_workqueue_poptask(&tasktype); 73 ot_taskid taskid = mutex_workqueue_poptask(&tasktype);
74#ifdef WANT_COMPRESSION_ZSTD
75 if (tasktype & TASK_FLAG_ZSTD)
76 fullscrape_make_zstd(taskid, tasktype);
77 else
78#endif
67#ifdef WANT_COMPRESSION_GZIP 79#ifdef WANT_COMPRESSION_GZIP
68 if (tasktype & TASK_FLAG_GZIP) 80 if (tasktype & TASK_FLAG_GZIP)
69 fullscrape_make_gzip(taskid, tasktype); 81 fullscrape_make_gzip(taskid, tasktype);
@@ -205,7 +217,6 @@ static void fullscrape_make_gzip(int taskid, ot_tasktype mode) {
205 struct iovec iovector = {NULL, 0}; 217 struct iovec iovector = {NULL, 0};
206 int zres; 218 int zres;
207 z_stream strm; 219 z_stream strm;
208 fprintf(stderr, "GZIP path\n");
209 /* Setup return vector... */ 220 /* Setup return vector... */
210 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE); 221 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE);
211 if (!iovector.iov_base) 222 if (!iovector.iov_base)
@@ -267,8 +278,10 @@ static void fullscrape_make_gzip(int taskid, ot_tasktype mode) {
267 mutex_bucket_unlock(bucket, 0); 278 mutex_bucket_unlock(bucket, 0);
268 279
269 /* Parent thread died? */ 280 /* Parent thread died? */
270 if (!g_opentracker_running) 281 if (!g_opentracker_running) {
282 deflateEnd(&strm);
271 return; 283 return;
284 }
272 } 285 }
273 286
274 if ((mode & TASK_TASK_MASK) == TASK_FULLSCRAPE) { 287 if ((mode & TASK_TASK_MASK) == TASK_FULLSCRAPE) {
@@ -282,7 +295,8 @@ static void fullscrape_make_gzip(int taskid, ot_tasktype mode) {
282 iovector.iov_len = (char *)strm.next_out - (char *)iovector.iov_base; 295 iovector.iov_len = (char *)strm.next_out - (char *)iovector.iov_base;
283 if (mutex_workqueue_pushchunked(taskid, &iovector)) { 296 if (mutex_workqueue_pushchunked(taskid, &iovector)) {
284 free(iovector.iov_base); 297 free(iovector.iov_base);
285 return mutex_bucket_unlock(bucket, 0); 298 deflateEnd(&strm);
299 return;
286 } 300 }
287 301
288 /* Check if there's a last batch of data in the zlib buffer */ 302 /* Check if there's a last batch of data in the zlib buffer */
@@ -293,7 +307,7 @@ static void fullscrape_make_gzip(int taskid, ot_tasktype mode) {
293 if (!iovector.iov_base) { 307 if (!iovector.iov_base) {
294 fprintf(stderr, "Problem with iovec_fix_increase_or_free\n"); 308 fprintf(stderr, "Problem with iovec_fix_increase_or_free\n");
295 deflateEnd(&strm); 309 deflateEnd(&strm);
296 return mutex_bucket_unlock(bucket, 0); 310 return;
297 } 311 }
298 strm.next_out = iovector.iov_base; 312 strm.next_out = iovector.iov_base;
299 strm.avail_out = OT_SCRAPE_CHUNK_SIZE; 313 strm.avail_out = OT_SCRAPE_CHUNK_SIZE;
@@ -311,5 +325,133 @@ static void fullscrape_make_gzip(int taskid, ot_tasktype mode) {
311/* WANT_COMPRESSION_GZIP */ 325/* WANT_COMPRESSION_GZIP */
312#endif 326#endif
313 327
328#ifdef WANT_COMPRESSION_ZSTD
329
330static void fullscrape_make_zstd(int taskid, ot_tasktype mode) {
331 int bucket;
332 char *r;
333 struct iovec iovector = {NULL, 0};
334 ZSTD_CCtx *zstream = ZSTD_createCCtx();
335 ZSTD_inBuffer inbuf;
336 ZSTD_outBuffer outbuf;
337 size_t more_bytes;
338
339 if (!zstream)
340 return;
341
342 /* Setup return vector... */
343 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE);
344 if (!iovector.iov_base) {
345 ZSTD_freeCCtx(zstream);
346 return;
347 }
348
349 /* Working with a compression level 6 is half as fast as level 3, but
350 seems to be the last reasonable bump that's worth extra cpu */
351 ZSTD_CCtx_setParameter(zstream, ZSTD_c_compressionLevel, 6);
352
353 outbuf.dst = iovector.iov_base;
354 outbuf.size = OT_SCRAPE_CHUNK_SIZE;
355 outbuf.pos = 0;
356
357 if ((mode & TASK_TASK_MASK) == TASK_FULLSCRAPE) {
358 inbuf.src = (const void *)"d5:filesd";
359 inbuf.size = strlen("d5:filesd");
360 inbuf.pos = 0;
361 ZSTD_compressStream2(zstream, &outbuf, &inbuf, ZSTD_e_continue);
362 }
363
364 /* For each bucket... */
365 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
366 /* Get exclusive access to that bucket */
367 ot_vector *torrents_list = mutex_bucket_lock(bucket);
368 ot_torrent *torrents = (ot_torrent *)(torrents_list->data);
369 size_t i;
370
371 /* For each torrent in this bucket.. */
372 for (i = 0; i < torrents_list->size; ++i) {
373 char compress_buffer[OT_SCRAPE_MAXENTRYLEN];
374 r = fullscrape_write_one(mode, compress_buffer, torrents + i, &torrents[i].hash);
375 inbuf.src = compress_buffer;
376 inbuf.size = r - compress_buffer;
377 inbuf.pos = 0;
378 ZSTD_compressStream2(zstream, &outbuf, &inbuf, ZSTD_e_continue);
379
380 /* Check if there still is enough buffer left */
381 while (outbuf.pos + OT_SCRAPE_MAXENTRYLEN > outbuf.size) {
382 iovector.iov_len = outbuf.size;
383
384 if (mutex_workqueue_pushchunked(taskid, &iovector)) {
385 free(iovector.iov_base);
386 ZSTD_freeCCtx(zstream);
387 return mutex_bucket_unlock(bucket, 0);
388 }
389 /* Allocate a fresh output buffer */
390 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE);
391 if (!iovector.iov_base) {
392 fprintf(stderr, "Out of memory trying to claim ouput buffer\n");
393 ZSTD_freeCCtx(zstream);
394 return mutex_bucket_unlock(bucket, 0);
395 }
396
397 outbuf.dst = iovector.iov_base;
398 outbuf.size = OT_SCRAPE_CHUNK_SIZE;
399 outbuf.pos = 0;
400
401 ZSTD_compressStream2(zstream, &outbuf, &inbuf, ZSTD_e_continue);
402 }
403 }
404
405 /* All torrents done: release lock on current bucket */
406 mutex_bucket_unlock(bucket, 0);
407
408 /* Parent thread died? */
409 if (!g_opentracker_running)
410 return;
411 }
412
413 if ((mode & TASK_TASK_MASK) == TASK_FULLSCRAPE) {
414 inbuf.src = (const void *)"ee";
415 inbuf.size = strlen("ee");
416 inbuf.pos = 0;
417 }
418
419 more_bytes = ZSTD_compressStream2(zstream, &outbuf, &inbuf, ZSTD_e_end);
420
421 iovector.iov_len = outbuf.pos;
422 if (mutex_workqueue_pushchunked(taskid, &iovector)) {
423 free(iovector.iov_base);
424 ZSTD_freeCCtx(zstream);
425 return;
426 }
427
428 /* Check if there's a last batch of data in the zlib buffer */
429 if (more_bytes) {
430 /* Allocate a fresh output buffer */
431 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE);
432
433 if (!iovector.iov_base) {
434 fprintf(stderr, "Problem with iovec_fix_increase_or_free\n");
435 ZSTD_freeCCtx(zstream);
436 return;
437 }
438
439 outbuf.dst = iovector.iov_base;
440 outbuf.size = OT_SCRAPE_CHUNK_SIZE;
441 outbuf.pos = 0;
442
443 ZSTD_compressStream2(zstream, &outbuf, &inbuf, ZSTD_e_end);
444
445 /* Only pass the new buffer if there actually was some data left in the buffer */
446 iovector.iov_len = outbuf.pos;
447 if (!iovector.iov_len || mutex_workqueue_pushchunked(taskid, &iovector))
448 free(iovector.iov_base);
449 }
450
451 ZSTD_freeCCtx(zstream);
452}
453/* WANT_COMPRESSION_ZSTD */
454#endif
455
314/* WANT_FULLSCRAPE */ 456/* WANT_FULLSCRAPE */
315#endif 457#endif
diff --git a/ot_http.c b/ot_http.c
index cd2dfc1..5c622e2 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>
@@ -159,7 +159,9 @@ ssize_t http_sendiovecdata(const int64 sock, struct ot_workstruct *ws, int iovec
159 159
160 if (iovec_entries) { 160 if (iovec_entries) {
161 161
162 if (cookie->flag & STRUCT_HTTP_FLAG_GZIP) 162 if (cookie->flag & STRUCT_HTTP_FLAG_ZSTD)
163 encoding = "Content-Encoding: zstd\r\n";
164 else if (cookie->flag & STRUCT_HTTP_FLAG_GZIP)
163 encoding = "Content-Encoding: gzip\r\n"; 165 encoding = "Content-Encoding: gzip\r\n";
164 else if (cookie->flag & STRUCT_HTTP_FLAG_BZIP2) 166 else if (cookie->flag & STRUCT_HTTP_FLAG_BZIP2)
165 encoding = "Content-Encoding: bzip2\r\n"; 167 encoding = "Content-Encoding: bzip2\r\n";
@@ -369,19 +371,34 @@ static ssize_t http_handle_fullscrape(const int64 sock, struct ot_workstruct *ws
369 } 371 }
370#endif 372#endif
371 373
372#ifdef WANT_COMPRESSION_GZIP 374
375#if defined(WANT_COMPRESSION_GZIP) || defined(WANT_COMPRESSION_ZSTD)
373 ws->request[ws->request_size - 1] = 0; 376 ws->request[ws->request_size - 1] = 0;
374#ifndef WANT_COMPRESSION_GZIP_ALWAYS 377#ifdef WANT_COMPRESSION_GZIP
375 if (strstr(ws->request, "gzip")) { 378 if (strstr(ws->request, "gzip")) {
376#endif
377 cookie->flag |= STRUCT_HTTP_FLAG_GZIP; 379 cookie->flag |= STRUCT_HTTP_FLAG_GZIP;
378 format = TASK_FLAG_GZIP; 380 format |= TASK_FLAG_GZIP;
379 stats_issue_event(EVENT_FULLSCRAPE_REQUEST_GZIP, 0, (uintptr_t)cookie->ip); 381 }
380#ifndef WANT_COMPRESSION_GZIP_ALWAYS 382#endif
381 } else 383#ifdef WANT_COMPRESSION_ZSTD
384 if (strstr(ws->request, "zstd")) {
385 cookie->flag |= STRUCT_HTTP_FLAG_ZSTD;
386 format |= TASK_FLAG_ZSTD;
387 }
388#endif
389
390#if defined(WANT_COMPRESSION_ZSTD) && defined(WANT_COMPRESSION_ZSTD_ALWAYS)
391 cookie->flag |= STRUCT_HTTP_FLAG_ZSTD;
392 format |= TASK_FLAG_ZSTD;
393#endif
394
395#if defined(WANT_COMPRESSION_GZIP) && defined(WANT_COMPRESSION_GZIP_ALWAYS)
396 cookie->flag |= STRUCT_HTTP_FLAG_GZIP;
397 format |= TASK_FLAG_GZIP;
382#endif 398#endif
383#endif 399#endif
384 stats_issue_event(EVENT_FULLSCRAPE_REQUEST, 0, (uintptr_t)cookie->ip); 400
401 stats_issue_event(EVENT_FULLSCRAPE_REQUEST, 0, (uintptr_t)cookie->ip);
385 402
386#ifdef _DEBUG_HTTPERROR 403#ifdef _DEBUG_HTTPERROR
387 fprintf(stderr, "%s", ws->debugbuf); 404 fprintf(stderr, "%s", ws->debugbuf);
diff --git a/ot_http.h b/ot_http.h
index fecb4eb..b5ae9ff 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 {
diff --git a/ot_mutex.h b/ot_mutex.h
index 66b627f..cdfabc9 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,
diff --git a/ot_stats.c b/ot_stats.c
index 158884f..b2eaec9 100644
--- a/ot_stats.c
+++ b/ot_stats.c
@@ -320,7 +320,7 @@ typedef struct {
320/* Fetches stats from tracker */ 320/* Fetches stats from tracker */
321size_t stats_top_txt(char *reply, int amount) { 321size_t stats_top_txt(char *reply, int amount) {
322 size_t j; 322 size_t j;
323 ot_record top100s[100], top100c[100]; 323 ot_record top100s[100], top100c[100], top100l[100];
324 char *r = reply, hex_out[42]; 324 char *r = reply, hex_out[42];
325 int idx, bucket; 325 int idx, bucket;
326 326
@@ -329,14 +329,16 @@ size_t stats_top_txt(char *reply, int amount) {
329 329
330 byte_zero(top100s, sizeof(top100s)); 330 byte_zero(top100s, sizeof(top100s));
331 byte_zero(top100c, sizeof(top100c)); 331 byte_zero(top100c, sizeof(top100c));
332 byte_zero(top100l, sizeof(top100l));
332 333
333 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) { 334 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
334 ot_vector *torrents_list = mutex_bucket_lock(bucket); 335 ot_vector *torrents_list = mutex_bucket_lock(bucket);
335 for (j = 0; j < torrents_list->size; ++j) { 336 for (j = 0; j < torrents_list->size; ++j) {
336 ot_torrent *torrent = (ot_torrent *)(torrents_list->data) + j; 337 ot_torrent *torrent = (ot_torrent *)(torrents_list->data) + j;
337 size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count; 338 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; 339 size_t seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count;
339 idx = amount - 1; 340 size_t leech_count = peer_count - seed_count;
341 idx = amount - 1;
340 while ((idx >= 0) && (peer_count > top100c[idx].val)) 342 while ((idx >= 0) && (peer_count > top100c[idx].val))
341 --idx; 343 --idx;
342 if (idx++ != amount - 1) { 344 if (idx++ != amount - 1) {
@@ -352,6 +354,14 @@ size_t stats_top_txt(char *reply, int amount) {
352 memcpy(&top100s[idx].hash, &torrent->hash, sizeof(ot_hash)); 354 memcpy(&top100s[idx].hash, &torrent->hash, sizeof(ot_hash));
353 top100s[idx].val = seed_count; 355 top100s[idx].val = seed_count;
354 } 356 }
357 idx = amount - 1;
358 while ((idx >= 0) && (leech_count > top100l[idx].val))
359 --idx;
360 if (idx++ != amount - 1) {
361 memmove(top100l + idx + 1, top100l + idx, (amount - 1 - idx) * sizeof(ot_record));
362 memcpy(&top100l[idx].hash, &torrent->hash, sizeof(ot_hash));
363 top100l[idx].val = leech_count;
364 }
355 } 365 }
356 mutex_bucket_unlock(bucket, 0); 366 mutex_bucket_unlock(bucket, 0);
357 if (!g_opentracker_running) 367 if (!g_opentracker_running)
@@ -366,6 +376,10 @@ size_t stats_top_txt(char *reply, int amount) {
366 for (idx = 0; idx < amount; ++idx) 376 for (idx = 0; idx < amount; ++idx)
367 if (top100s[idx].val) 377 if (top100s[idx].val)
368 r += sprintf(r, "\t%zd\t%s\n", top100s[idx].val, to_hex(hex_out, top100s[idx].hash)); 378 r += sprintf(r, "\t%zd\t%s\n", top100s[idx].val, to_hex(hex_out, top100s[idx].hash));
379 r += sprintf(r, "Top %d torrents by leechers:\n", amount);
380 for (idx = 0; idx < amount; ++idx)
381 if (top100l[idx].val)
382 r += sprintf(r, "\t%zd\t%s\n", top100l[idx].val, to_hex(hex_out, top100l[idx].hash));
369 383
370 return r - reply; 384 return r - reply;
371} 385}
diff --git a/ot_stats.h b/ot_stats.h
index 4f75049..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,