diff options
| -rw-r--r-- | Makefile | 22 | ||||
| -rw-r--r-- | Makefile.arc4random | 3 | ||||
| -rw-r--r-- | Makefile.gzip | 4 | ||||
| -rw-r--r-- | Makefile.zstd | 3 | ||||
| -rw-r--r-- | man1/opentracker.1 | 130 | ||||
| -rw-r--r-- | man4/opentracker.conf.4 | 86 | ||||
| -rw-r--r-- | opentracker.conf.sample | 5 | ||||
| -rw-r--r-- | ot_fullscrape.c | 150 | ||||
| -rw-r--r-- | ot_http.c | 57 | ||||
| -rw-r--r-- | ot_http.h | 5 | ||||
| -rw-r--r-- | ot_mutex.h | 3 | ||||
| -rw-r--r-- | ot_stats.c | 26 | ||||
| -rw-r--r-- | ot_stats.h | 1 | ||||
| -rw-r--r-- | ot_vector.c | 9 |
14 files changed, 463 insertions, 41 deletions
| @@ -18,15 +18,24 @@ LIBOWFAT_LIBRARY=$(PREFIX)/libowfat | |||
| 18 | BINDIR?=$(PREFIX)/bin | 18 | BINDIR?=$(PREFIX)/bin |
| 19 | STRIP?=strip | 19 | STRIP?=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 |
| 28 | FEATURES+=-DWANT_COMPRESSION_GZIP | 28 | |
| 29 | FEATURES+=-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 | ||
| 32 | include 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 | |||
| 55 | OPTS_production=-O3 | 64 | OPTS_production=-O3 |
| 56 | 65 | ||
| 57 | CFLAGS+=-I$(LIBOWFAT_HEADERS) -DGIT_VERSION=$(GIT_VERSION) -Wall -pipe -pthread -Wextra #-ansi -pedantic | 66 | CFLAGS+=-I$(LIBOWFAT_HEADERS) -DGIT_VERSION=$(GIT_VERSION) -Wall -pipe -pthread -Wextra #-ansi -pedantic |
| 58 | LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread -lz | 67 | LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread |
| 59 | #LDFLAGS+=-lbsd | ||
| 60 | 68 | ||
| 61 | BINARY =opentracker | 69 | BINARY =opentracker |
| 62 | HEADERS=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 | 70 | HEADERS=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 @@ | |||
| 1 | FEATURES+=-DWANT_ARC4RANDOM | ||
| 2 | LDFLAGS+=-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 @@ | |||
| 1 | FEATURES+=-DWANT_COMPRESSION_GZIP | ||
| 2 | #FEATURES+=-DWANT_COMPRESSION_GZIP_ALWAYS | ||
| 3 | |||
| 4 | LDFLAGS+=-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 @@ | |||
| 1 | FEATURES+=-DWANT_COMPRESSION_ZSTD | ||
| 2 | #FEATURES+=-DWANT_COMPRESSION_ZSTD_ALWAYS | ||
| 3 | LDFLAGS+=-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 | ||
| 20 | is a bittorrent tracker that implements announce and scrape actions over the | ||
| 21 | UDP and the plain http protocol, aiming for minimal resource usage. | ||
| 22 | .Pp | ||
| 23 | When invoked with parameters, it binds to TCP and UDP port 6969 on all | ||
| 24 | interfaces. The recommended way to configure opentracker is by providing a | ||
| 25 | config file using the | ||
| 26 | .Op Fl f Ar config | ||
| 27 | option. See | ||
| 28 | .Xr opentracker.conf 4 | ||
| 29 | for details. | ||
| 30 | .Pp | ||
| 31 | .Sh OPTIONS | ||
| 32 | The following options are available: | ||
| 33 | .Bl -tag -width -indent=8 | ||
| 34 | .It Fl f Ar config | ||
| 35 | Parse a config file with a list of options. Consecutive command options | ||
| 36 | will override options from the config file. See | ||
| 37 | .Xr opentracker.conf 4 | ||
| 38 | for details. | ||
| 39 | .It Fl i Ar ip-select | ||
| 40 | Select an ip address that will be used with the next | ||
| 41 | .Op Fl p | ||
| 42 | or | ||
| 43 | .Op Fl P | ||
| 44 | command to actually bind to this address. Setting this option without any bind | ||
| 45 | options in the config file or | ||
| 46 | .Op Fl p | ||
| 47 | or | ||
| 48 | .Op Fl P | ||
| 49 | commands will limit opentracker to only bind to this address. | ||
| 50 | .It Fl p Ar port-bind-tcp | ||
| 51 | Bind to the TCP port on the last preceding ip address set with the | ||
| 52 | .Op Fl i ip-select | ||
| 53 | option or to all available addresses if none has been set. Can be given multiple | ||
| 54 | times. | ||
| 55 | .It Fl P Ar port-bind-udp | ||
| 56 | Bind to the UDP port on the last preceding ip address set with the | ||
| 57 | .Op Fl i ip-select | ||
| 58 | option or to all available addresses if none has been set. Can be given multiple | ||
| 59 | times. | ||
| 60 | .It Fl A Ar blessed-ip | ||
| 61 | Set an ip address in IPv4 or IPv6 or a net in CIDR notation to bless the network | ||
| 62 | for access to restricted resources. | ||
| 63 | .It Fl r Ar redirect-url | ||
| 64 | Set the URL that | ||
| 65 | .Nm | ||
| 66 | will redirect users to when the / address is requested via HTTP. | ||
| 67 | .It Fl d Ar chdir | ||
| 68 | Sets the directory | ||
| 69 | .Nm | ||
| 70 | will | ||
| 71 | .Xr chroot 2 | ||
| 72 | to if ran as root or | ||
| 73 | .Xr chdir 2 | ||
| 74 | to if ran as unprivileged user. Note that any accesslist files need to be | ||
| 75 | relative to and within that directory. | ||
| 76 | .It Fl u Ar user | ||
| 77 | User to run | ||
| 78 | .Nm | ||
| 79 | under after all operations that need privileges have finished. | ||
| 80 | .It Fl w Ar accesslist | Fl b Ar accesslist | ||
| 81 | If | ||
| 82 | .Nm | ||
| 83 | has been compiled with the | ||
| 84 | .B WANT_ACCESSLIST_BLACK | ||
| 85 | or | ||
| 86 | .Br WANT_ACCESSLIST_WHITE | ||
| 87 | options, this option sets the location of the accesslist. | ||
| 88 | .El | ||
| 89 | .Sh EXAMPLES | ||
| 90 | Start | ||
| 91 | .Nm | ||
| 92 | bound on UDP and TCP ports 6969 on IPv6 localhost. | ||
| 93 | .Dl # ./opentracker -i ::1 -p 6969 -P 6969 | ||
| 94 | .Pp | ||
| 95 | Start | ||
| 96 | .Nm | ||
| 97 | bound on UDP port 6868 and TCP port 6868 on IPv4 localhost and allow | ||
| 98 | privileged access from the network 192.168/16 while redirecting | ||
| 99 | HTTP clients accessing the root directory, which is not covered by the | ||
| 100 | bittorrent 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/ | ||
| 102 | The announce URLs are http://192.168.0.4:6868/announce and | ||
| 103 | udp://192.168.0.4:6868/announce respectively. | ||
| 104 | .Sh FILES | ||
| 105 | .Bl -tag -width indent | ||
| 106 | .It Pa opentracker.conf | ||
| 107 | The | ||
| 108 | .Nm | ||
| 109 | config file. | ||
| 110 | .El | ||
| 111 | .Sh SEE ALSO | ||
| 112 | .Xr opentracker.conf 4 | ||
| 113 | .Pp | ||
| 114 | opentracker documentation | ||
| 115 | .Lk https://erdgeist.org/arts/software/opentracker | ||
| 116 | .Pp | ||
| 117 | Bittorrent 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 | ||
| 123 | This software is released under the Beerware License: | ||
| 124 | .Pp | ||
| 125 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software | ||
| 126 | and associated documentation files (the "Software"), to deal in the Software with the following | ||
| 127 | terms and conditions: | ||
| 128 | .Pp | ||
| 129 | If you meet the author(s) someday, and you think this software is worth it, you can buy them | ||
| 130 | a 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 | ||
| 10 | The | ||
| 11 | .Nm | ||
| 12 | configuration file specifies various options for configuring the behavior of the opentracker program. | ||
| 13 | .Pp | ||
| 14 | Lines starting with '#' are comments and are ignored. Options are specified as 'keyword value' pairs. | ||
| 15 | .Pp | ||
| 16 | The following options are available: | ||
| 17 | .Pp | ||
| 18 | .Bl -tag -width ".It access.proxy" -compact | ||
| 19 | .It listen.tcp_udp Ar address | ||
| 20 | Specifies 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 | ||
| 23 | Specifies the address opentracker will listen on for TCP connections. Can be added more than once. | ||
| 24 | .Pp | ||
| 25 | .It listen.udp Ar address | ||
| 26 | Specifies the address opentracker will listen on for UDP connections. Can be added more than once. | ||
| 27 | .Pp | ||
| 28 | .It listen.udp.workers Ar threads | ||
| 29 | Specifies how many threads will be spawned to handle UDP connections. Defaults to 4. | ||
| 30 | .Pp | ||
| 31 | .It access.whitelist Ar path/to/whitelist | ||
| 32 | Specifies 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 | ||
| 35 | Specifies 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 | ||
| 38 | Specifies 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 | ||
| 41 | Specifies 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 | ||
| 44 | Specifies the IP address or network in CIDR notation allowed to fetch stats from opentracker. | ||
| 45 | .Pp | ||
| 46 | .It access.stats_path Ar path | ||
| 47 | Specifies 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 | ||
| 50 | Specifies 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 | ||
| 53 | Specifies 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 | ||
| 56 | Specifies 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 | ||
| 59 | Specifies the admin IP address for old-style (HTTP-based) asynchronous tracker syncing. | ||
| 60 | .Pp | ||
| 61 | .It tracker.rootdir Ar path | ||
| 62 | Specifies 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 | ||
| 65 | Specifies the user opentracker will setuid to after binding to potentially privileged ports. | ||
| 66 | .Pp | ||
| 67 | .It tracker.redirect_url Ar URL | ||
| 68 | Specifies the URL opentracker will redirect to in response to a "GET / HTTP" request. | ||
| 69 | .El | ||
| 70 | .Sh EXAMPLES | ||
| 71 | To 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 | ||
| 74 | To specify the address opentracker will listen on for TCP connections: | ||
| 75 | .Dl listen.tcp 0.0.0.0 | ||
| 76 | .Pp | ||
| 77 | To 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.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 |
| 41 | static void fullscrape_make_gzip(int taskid, ot_tasktype mode); | 45 | static void fullscrape_make_gzip(int taskid, ot_tasktype mode); |
| 42 | #endif | 46 | #endif |
| 47 | #ifdef WANT_COMPRESSION_ZSTD | ||
| 48 | static 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 | |||
| 330 | static 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 |
| @@ -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> |
| @@ -32,7 +32,7 @@ | |||
| 32 | #include "trackerlogic.h" | 32 | #include "trackerlogic.h" |
| 33 | 33 | ||
| 34 | #ifdef WANT_NO_AUTO_FREE | 34 | #ifdef WANT_NO_AUTO_FREE |
| 35 | #define OT_IOB_INIT(B) bzero(B, sizeof(io_batch)) | 35 | #define OT_IOB_INIT(B) (bzero(B, sizeof(io_batch)), 0) |
| 36 | #else | 36 | #else |
| 37 | #define OT_IOB_INIT(B) iob_init_autofree(B, 0) | 37 | #define OT_IOB_INIT(B) iob_init_autofree(B, 0) |
| 38 | #endif | 38 | #endif |
| @@ -88,7 +88,15 @@ static void http_senddata(const int64 sock, struct ot_workstruct *ws) { | |||
| 88 | memcpy(outbuf, ws->reply + written_size, ws->reply_size - written_size); | 88 | memcpy(outbuf, ws->reply + written_size, ws->reply_size - written_size); |
| 89 | if (!cookie->batch) { | 89 | if (!cookie->batch) { |
| 90 | cookie->batch = malloc(sizeof(io_batch)); | 90 | cookie->batch = malloc(sizeof(io_batch)); |
| 91 | OT_IOB_INIT(cookie->batch); | 91 | if (!cookie->batch || OT_IOB_INIT(cookie->batch) == -1) { |
| 92 | free(cookie->batch); | ||
| 93 | free(outbuf); | ||
| 94 | array_reset(&cookie->request); | ||
| 95 | free(cookie); | ||
| 96 | io_close(sock); | ||
| 97 | return; | ||
| 98 | } | ||
| 99 | |||
| 92 | cookie->batches = 1; | 100 | cookie->batches = 1; |
| 93 | } | 101 | } |
| 94 | 102 | ||
| @@ -159,7 +167,9 @@ ssize_t http_sendiovecdata(const int64 sock, struct ot_workstruct *ws, int iovec | |||
| 159 | 167 | ||
| 160 | if (iovec_entries) { | 168 | if (iovec_entries) { |
| 161 | 169 | ||
| 162 | if (cookie->flag & STRUCT_HTTP_FLAG_GZIP) | 170 | if (cookie->flag & STRUCT_HTTP_FLAG_ZSTD) |
| 171 | encoding = "Content-Encoding: zstd\r\n"; | ||
| 172 | else if (cookie->flag & STRUCT_HTTP_FLAG_GZIP) | ||
| 163 | encoding = "Content-Encoding: gzip\r\n"; | 173 | encoding = "Content-Encoding: gzip\r\n"; |
| 164 | else if (cookie->flag & STRUCT_HTTP_FLAG_BZIP2) | 174 | else if (cookie->flag & STRUCT_HTTP_FLAG_BZIP2) |
| 165 | encoding = "Content-Encoding: bzip2\r\n"; | 175 | encoding = "Content-Encoding: bzip2\r\n"; |
| @@ -181,12 +191,12 @@ ssize_t http_sendiovecdata(const int64 sock, struct ot_workstruct *ws, int iovec | |||
| 181 | 191 | ||
| 182 | if (!cookie->batch) { | 192 | if (!cookie->batch) { |
| 183 | cookie->batch = malloc(sizeof(io_batch)); | 193 | cookie->batch = malloc(sizeof(io_batch)); |
| 184 | if (!cookie->batch) { | 194 | if (!cookie->batch || OT_IOB_INIT(cookie->batch) == -1) { |
| 195 | free(cookie->batch); | ||
| 185 | free(header); | 196 | free(header); |
| 186 | iovec_free(&iovec_entries, &iovector); | 197 | iovec_free(&iovec_entries, &iovector); |
| 187 | HTTPERROR_500; | 198 | HTTPERROR_500; |
| 188 | } | 199 | } |
| 189 | OT_IOB_INIT(cookie->batch); | ||
| 190 | cookie->batches = 1; | 200 | cookie->batches = 1; |
| 191 | } | 201 | } |
| 192 | current = cookie->batch + cookie->batches - 1; | 202 | current = cookie->batch + cookie->batches - 1; |
| @@ -199,8 +209,8 @@ ssize_t http_sendiovecdata(const int64 sock, struct ot_workstruct *ws, int iovec | |||
| 199 | io_batch *new_batch = realloc(cookie->batch, (cookie->batches + 1) * sizeof(io_batch)); | 209 | io_batch *new_batch = realloc(cookie->batch, (cookie->batches + 1) * sizeof(io_batch)); |
| 200 | if (new_batch) { | 210 | if (new_batch) { |
| 201 | cookie->batch = new_batch; | 211 | cookie->batch = new_batch; |
| 202 | current = cookie->batch + cookie->batches++; | 212 | if (OT_IOB_INIT(current) != -1) |
| 203 | OT_IOB_INIT(current); | 213 | current = cookie->batch + cookie->batches++; |
| 204 | } | 214 | } |
| 205 | } | 215 | } |
| 206 | iob_addbuf_free(current, iovector[i].iov_base, iovector[i].iov_len); | 216 | iob_addbuf_free(current, iovector[i].iov_base, iovector[i].iov_len); |
| @@ -369,19 +379,34 @@ static ssize_t http_handle_fullscrape(const int64 sock, struct ot_workstruct *ws | |||
| 369 | } | 379 | } |
| 370 | #endif | 380 | #endif |
| 371 | 381 | ||
| 372 | #ifdef WANT_COMPRESSION_GZIP | 382 | |
| 383 | #if defined(WANT_COMPRESSION_GZIP) || defined(WANT_COMPRESSION_ZSTD) | ||
| 373 | ws->request[ws->request_size - 1] = 0; | 384 | ws->request[ws->request_size - 1] = 0; |
| 374 | #ifndef WANT_COMPRESSION_GZIP_ALWAYS | 385 | #ifdef WANT_COMPRESSION_GZIP |
| 375 | if (strstr(ws->request, "gzip")) { | 386 | if (strstr(ws->request, "gzip")) { |
| 376 | #endif | ||
| 377 | cookie->flag |= STRUCT_HTTP_FLAG_GZIP; | 387 | cookie->flag |= STRUCT_HTTP_FLAG_GZIP; |
| 378 | format = TASK_FLAG_GZIP; | 388 | format |= TASK_FLAG_GZIP; |
| 379 | stats_issue_event(EVENT_FULLSCRAPE_REQUEST_GZIP, 0, (uintptr_t)cookie->ip); | 389 | } |
| 380 | #ifndef WANT_COMPRESSION_GZIP_ALWAYS | ||
| 381 | } else | ||
| 382 | #endif | 390 | #endif |
| 391 | #ifdef WANT_COMPRESSION_ZSTD | ||
| 392 | if (strstr(ws->request, "zstd")) { | ||
| 393 | cookie->flag |= STRUCT_HTTP_FLAG_ZSTD; | ||
| 394 | format |= TASK_FLAG_ZSTD; | ||
| 395 | } | ||
| 396 | #endif | ||
| 397 | |||
| 398 | #if defined(WANT_COMPRESSION_ZSTD) && defined(WANT_COMPRESSION_ZSTD_ALWAYS) | ||
| 399 | cookie->flag |= STRUCT_HTTP_FLAG_ZSTD; | ||
| 400 | format |= TASK_FLAG_ZSTD; | ||
| 383 | #endif | 401 | #endif |
| 384 | stats_issue_event(EVENT_FULLSCRAPE_REQUEST, 0, (uintptr_t)cookie->ip); | 402 | |
| 403 | #if defined(WANT_COMPRESSION_GZIP) && defined(WANT_COMPRESSION_GZIP_ALWAYS) | ||
| 404 | cookie->flag |= STRUCT_HTTP_FLAG_GZIP; | ||
| 405 | format |= TASK_FLAG_GZIP; | ||
| 406 | #endif | ||
| 407 | #endif | ||
| 408 | |||
| 409 | stats_issue_event(EVENT_FULLSCRAPE_REQUEST, 0, (uintptr_t)cookie->ip); | ||
| 385 | 410 | ||
| 386 | #ifdef _DEBUG_HTTPERROR | 411 | #ifdef _DEBUG_HTTPERROR |
| 387 | fprintf(stderr, "%s", ws->debugbuf); | 412 | fprintf(stderr, "%s", ws->debugbuf); |
| @@ -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 | ||
| 17 | struct http_data { | 18 | struct http_data { |
| @@ -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, |
| @@ -320,7 +320,7 @@ typedef struct { | |||
| 320 | /* Fetches stats from tracker */ | 320 | /* Fetches stats from tracker */ |
| 321 | size_t stats_top_txt(char *reply, int amount) { | 321 | size_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 | } |
| @@ -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, |
diff --git a/ot_vector.c b/ot_vector.c index 2bc07b5..2acfbef 100644 --- a/ot_vector.c +++ b/ot_vector.c | |||
| @@ -67,12 +67,15 @@ void *vector_find_or_insert(ot_vector *vector, void *key, size_t member_size, si | |||
| 67 | return match; | 67 | return match; |
| 68 | 68 | ||
| 69 | if (vector->size + 1 > vector->space) { | 69 | if (vector->size + 1 > vector->space) { |
| 70 | size_t new_space = vector->space ? OT_VECTOR_GROW_RATIO * vector->space : OT_VECTOR_MIN_MEMBERS; | 70 | 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); | 71 | ptrdiff_t match_off = match - (uint8_t *)vector->data; |
| 72 | uint8_t *new_data = realloc(vector->data, new_space * member_size); | ||
| 73 | |||
| 72 | if (!new_data) | 74 | if (!new_data) |
| 73 | return NULL; | 75 | return NULL; |
| 76 | |||
| 74 | /* Adjust pointer if it moved by realloc */ | 77 | /* Adjust pointer if it moved by realloc */ |
| 75 | match = new_data + (match - (uint8_t *)vector->data); | 78 | match = new_data + match_off; |
| 76 | 79 | ||
| 77 | vector->data = new_data; | 80 | vector->data = new_data; |
| 78 | vector->space = new_space; | 81 | vector->space = new_space; |
