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