summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format246
-rw-r--r--.gitignore2
-rw-r--r--Makefile72
-rw-r--r--Makefile.gzip4
-rw-r--r--Makefile.zstd3
-rw-r--r--man1/opentracker.1130
-rw-r--r--man4/opentracker.conf.486
-rw-r--r--opentracker.c1029
-rw-r--r--opentracker.conf.sample52
-rw-r--r--ot_accesslist.c568
-rw-r--r--ot_accesslist.h56
-rw-r--r--ot_buf.c41
-rw-r--r--ot_buf.h27
-rw-r--r--ot_clean.c137
-rw-r--r--ot_clean.h10
-rw-r--r--ot_fullscrape.c471
-rw-r--r--ot_fullscrape.h8
-rw-r--r--ot_hex.c58
-rw-r--r--ot_hex.h29
-rw-r--r--ot_http.c864
-rw-r--r--ot_http.h19
-rw-r--r--ot_iovec.c78
-rw-r--r--ot_iovec.h11
-rw-r--r--ot_ip.c201
-rw-r--r--ot_ip.h33
-rw-r--r--ot_livesync.c227
-rw-r--r--ot_livesync.h20
-rw-r--r--ot_mutex.c322
-rw-r--r--ot_mutex.h103
-rw-r--r--ot_random.c53
-rw-r--r--ot_random.h19
-rw-r--r--ot_rijndael.c2
-rw-r--r--ot_scan.c44
-rw-r--r--ot_scan.h21
-rw-r--r--ot_stats.c982
-rw-r--r--ot_stats.h24
-rw-r--r--ot_sync.c122
-rw-r--r--ot_sync.h10
-rw-r--r--ot_udp.c293
-rw-r--r--ot_udp.h7
-rw-r--r--ot_vector.c255
-rw-r--r--ot_vector.h24
-rw-r--r--proxy.c876
-rw-r--r--scan_urlencoded_query.c128
-rw-r--r--scan_urlencoded_query.h14
-rw-r--r--tests/runner.c40
-rw-r--r--tests/stubs.c20
-rw-r--r--tests/t_accesslist.c122
-rw-r--r--tests/t_buf.c205
-rw-r--r--tests/t_hex.c128
-rw-r--r--tests/t_iovec.c124
-rw-r--r--tests/t_ip.c217
-rw-r--r--tests/t_random.c114
-rw-r--r--tests/t_scan.c162
-rw-r--r--tests/t_scan_urlencoded_query.c381
-rw-r--r--tests/t_vector.c161
-rw-r--r--tests/test.c58
-rw-r--r--tests/test.h89
-rw-r--r--tests/testsuite2.sh22
-rw-r--r--trackerlogic.c651
-rw-r--r--trackerlogic.h175
61 files changed, 7257 insertions, 3193 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..cf3c715
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,246 @@
1---
2Language: Cpp
3# BasedOnStyle: LLVM
4AccessModifierOffset: -2
5AlignAfterOpenBracket: Align
6AlignArrayOfStructures: None
7AlignConsecutiveAssignments:
8 Enabled: true
9 AcrossEmptyLines: true
10 AcrossComments: true
11 AlignCompound: true
12 AlignFunctionPointers: false
13 PadOperators: true
14AlignConsecutiveBitFields:
15 Enabled: false
16 AcrossEmptyLines: true
17 AcrossComments: true
18 AlignCompound: false
19 AlignFunctionPointers: false
20 PadOperators: false
21AlignConsecutiveDeclarations:
22 Enabled: true
23 AcrossEmptyLines: true
24 AcrossComments: true
25 AlignCompound: true
26 AlignFunctionPointers: false
27 PadOperators: true
28AlignConsecutiveMacros:
29 Enabled: true
30 AcrossEmptyLines: true
31 AcrossComments: true
32 AlignCompound: true
33 AlignFunctionPointers: false
34 PadOperators: false
35AlignConsecutiveShortCaseStatements:
36 Enabled: true
37 AcrossEmptyLines: true
38 AcrossComments: true
39 AlignCaseColons: false
40AlignEscapedNewlines: Right
41AlignOperands: Align
42AlignTrailingComments:
43 Kind: Always
44 OverEmptyLines: 0
45AllowAllArgumentsOnNextLine: true
46AllowAllParametersOfDeclarationOnNextLine: true
47AllowBreakBeforeNoexceptSpecifier: Never
48AllowShortBlocksOnASingleLine: Never
49AllowShortCaseLabelsOnASingleLine: false
50AllowShortCompoundRequirementOnASingleLine: true
51AllowShortEnumsOnASingleLine: true
52AllowShortFunctionsOnASingleLine: All
53AllowShortIfStatementsOnASingleLine: Never
54AllowShortLambdasOnASingleLine: All
55AllowShortLoopsOnASingleLine: false
56AlwaysBreakAfterDefinitionReturnType: None
57AlwaysBreakAfterReturnType: None
58AlwaysBreakBeforeMultilineStrings: false
59AlwaysBreakTemplateDeclarations: MultiLine
60AttributeMacros:
61 - __capability
62BinPackArguments: true
63BinPackParameters: true
64BitFieldColonSpacing: Both
65BraceWrapping:
66 AfterCaseLabel: false
67 AfterClass: false
68 AfterControlStatement: Never
69 AfterEnum: false
70 AfterExternBlock: false
71 AfterFunction: false
72 AfterNamespace: false
73 AfterObjCDeclaration: false
74 AfterStruct: false
75 AfterUnion: false
76 BeforeCatch: false
77 BeforeElse: false
78 BeforeLambdaBody: false
79 BeforeWhile: false
80 IndentBraces: false
81 SplitEmptyFunction: true
82 SplitEmptyRecord: true
83 SplitEmptyNamespace: true
84BreakAdjacentStringLiterals: true
85BreakAfterAttributes: Leave
86BreakAfterJavaFieldAnnotations: false
87BreakArrays: true
88BreakBeforeBinaryOperators: None
89BreakBeforeConceptDeclarations: Always
90BreakBeforeBraces: Attach
91BreakBeforeInlineASMColon: OnlyMultiline
92BreakBeforeTernaryOperators: true
93BreakConstructorInitializers: BeforeColon
94BreakInheritanceList: BeforeColon
95BreakStringLiterals: false
96ColumnLimit: 160
97CommentPragmas: '^ IWYU pragma:'
98CompactNamespaces: false
99ConstructorInitializerIndentWidth: 4
100ContinuationIndentWidth: 4
101Cpp11BracedListStyle: true
102DerivePointerAlignment: false
103DisableFormat: false
104EmptyLineAfterAccessModifier: Never
105EmptyLineBeforeAccessModifier: LogicalBlock
106ExperimentalAutoDetectBinPacking: false
107FixNamespaceComments: true
108ForEachMacros:
109 - foreach
110 - Q_FOREACH
111 - BOOST_FOREACH
112IfMacros:
113 - KJ_IF_MAYBE
114IncludeBlocks: Preserve
115IncludeCategories:
116 - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
117 Priority: 2
118 SortPriority: 0
119 CaseSensitive: false
120 - Regex: '^(<|"(gtest|gmock|isl|json)/)'
121 Priority: 3
122 SortPriority: 0
123 CaseSensitive: false
124 - Regex: '.*'
125 Priority: 1
126 SortPriority: 0
127 CaseSensitive: false
128IncludeIsMainRegex: '(Test)?$'
129IncludeIsMainSourceRegex: ''
130IndentAccessModifiers: false
131IndentCaseBlocks: false
132IndentCaseLabels: false
133IndentExternBlock: AfterExternBlock
134IndentGotoLabels: true
135IndentPPDirectives: None
136IndentRequiresClause: true
137IndentWidth: 2
138IndentWrappedFunctionNames: false
139InsertBraces: false
140InsertNewlineAtEOF: false
141InsertTrailingCommas: None
142IntegerLiteralSeparator:
143 Binary: 0
144 BinaryMinDigits: 0
145 Decimal: 0
146 DecimalMinDigits: 0
147 Hex: 0
148 HexMinDigits: 0
149JavaScriptQuotes: Leave
150JavaScriptWrapImports: true
151KeepEmptyLinesAtTheStartOfBlocks: true
152KeepEmptyLinesAtEOF: false
153LambdaBodyIndentation: Signature
154LineEnding: DeriveLF
155MacroBlockBegin: ''
156MacroBlockEnd: ''
157MaxEmptyLinesToKeep: 1
158NamespaceIndentation: None
159ObjCBinPackProtocolList: Auto
160ObjCBlockIndentWidth: 2
161ObjCBreakBeforeNestedBlockParam: true
162ObjCSpaceAfterProperty: false
163ObjCSpaceBeforeProtocolList: true
164PackConstructorInitializers: BinPack
165PenaltyBreakAssignment: 2
166PenaltyBreakBeforeFirstCallParameter: 19
167PenaltyBreakComment: 300
168PenaltyBreakFirstLessLess: 120
169PenaltyBreakOpenParenthesis: 0
170PenaltyBreakScopeResolution: 500
171PenaltyBreakString: 1000
172PenaltyBreakTemplateDeclaration: 10
173PenaltyExcessCharacter: 1000000
174PenaltyIndentedWhitespace: 0
175PenaltyReturnTypeOnItsOwnLine: 60
176PointerAlignment: Right
177PPIndentWidth: -1
178QualifierAlignment: Leave
179ReferenceAlignment: Pointer
180ReflowComments: true
181RemoveBracesLLVM: false
182RemoveParentheses: Leave
183RemoveSemicolon: false
184RequiresClausePosition: OwnLine
185RequiresExpressionIndentation: OuterScope
186SeparateDefinitionBlocks: Leave
187ShortNamespaceLines: 1
188SkipMacroDefinitionBody: false
189SortIncludes: CaseSensitive
190SortJavaStaticImport: Before
191SortUsingDeclarations: LexicographicNumeric
192SpaceAfterCStyleCast: false
193SpaceAfterLogicalNot: false
194SpaceAfterTemplateKeyword: true
195SpaceAroundPointerQualifiers: Default
196SpaceBeforeAssignmentOperators: true
197SpaceBeforeCaseColon: false
198SpaceBeforeCpp11BracedList: false
199SpaceBeforeCtorInitializerColon: true
200SpaceBeforeInheritanceColon: true
201SpaceBeforeJsonColon: false
202SpaceBeforeParens: ControlStatements
203SpaceBeforeParensOptions:
204 AfterControlStatements: true
205 AfterForeachMacros: true
206 AfterFunctionDefinitionName: false
207 AfterFunctionDeclarationName: false
208 AfterIfMacros: true
209 AfterOverloadedOperator: false
210 AfterPlacementOperator: true
211 AfterRequiresInClause: false
212 AfterRequiresInExpression: false
213 BeforeNonEmptyParentheses: false
214SpaceBeforeRangeBasedForLoopColon: true
215SpaceBeforeSquareBrackets: false
216SpaceInEmptyBlock: false
217SpacesBeforeTrailingComments: 1
218SpacesInAngles: Never
219SpacesInContainerLiterals: true
220SpacesInLineCommentPrefix:
221 Minimum: 1
222 Maximum: -1
223SpacesInParens: Never
224SpacesInParensOptions:
225 InCStyleCasts: false
226 InConditionalStatements: false
227 InEmptyParentheses: false
228 Other: false
229SpacesInSquareBrackets: false
230Standard: Latest
231StatementAttributeLikeMacros:
232 - Q_EMIT
233StatementMacros:
234 - Q_UNUSED
235 - QT_REQUIRE_VERSION
236TabWidth: 8
237UseTab: Never
238VerilogBreakBetweenInstancePorts: true
239WhitespaceSensitiveMacros:
240 - BOOST_PP_STRINGIZE
241 - CF_SWIFT_NAME
242 - NS_SWIFT_NAME
243 - PP_STRINGIZE
244 - STRINGIZE
245...
246
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8b4bec1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
1*.o
2tests/runner
diff --git a/Makefile b/Makefile
index c8366ca..27d635d 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,5 @@
1# $Id$ 1# $Id$
2 2
3CC?=gcc
4
5# Linux flavour 3# Linux flavour
6# PREFIX?=/opt/diet 4# PREFIX?=/opt/diet
7# LIBOWFAT_HEADERS=$(PREFIX)/include 5# LIBOWFAT_HEADERS=$(PREFIX)/include
@@ -18,16 +16,26 @@ LIBOWFAT_HEADERS=$(PREFIX)/libowfat
18LIBOWFAT_LIBRARY=$(PREFIX)/libowfat 16LIBOWFAT_LIBRARY=$(PREFIX)/libowfat
19 17
20BINDIR?=$(PREFIX)/bin 18BINDIR?=$(PREFIX)/bin
19STRIP?=strip
21 20
22#FEATURES+=-DWANT_V6 21#FEATURES+=-DWANT_V4_ONLY
23
24#FEATURES+=-DWANT_ACCESSLIST_BLACK 22#FEATURES+=-DWANT_ACCESSLIST_BLACK
25#FEATURES+=-DWANT_ACCESSLIST_WHITE 23#FEATURES+=-DWANT_ACCESSLIST_WHITE
24#FEATURES+=-DWANT_DYNAMIC_ACCESSLIST
26 25
27#FEATURES+=-DWANT_SYNC_LIVE 26#FEATURES+=-DWANT_SYNC_LIVE
28#FEATURES+=-DWANT_IP_FROM_QUERY_STRING 27#FEATURES+=-DWANT_IP_FROM_QUERY_STRING
29#FEATURES+=-DWANT_COMPRESSION_GZIP 28
30#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
32include Makefile.gzip
33
34# If you want zstd support to be compiled in, uncomment the next include.
35# You can further modify the behaviour by setting DWANT_COMPRESSION_ZSTD_ALWAYS
36# in Makefile.zstd
37#include Makefile.zstd
38
31#FEATURES+=-DWANT_LOG_NETWORKS 39#FEATURES+=-DWANT_LOG_NETWORKS
32#FEATURES+=-DWANT_RESTRICT_STATS 40#FEATURES+=-DWANT_RESTRICT_STATS
33#FEATURES+=-DWANT_IP_FROM_PROXY 41#FEATURES+=-DWANT_IP_FROM_PROXY
@@ -39,23 +47,30 @@ BINDIR?=$(PREFIX)/bin
39#FEATURES+=-DWANT_DEV_RANDOM 47#FEATURES+=-DWANT_DEV_RANDOM
40FEATURES+=-DWANT_FULLSCRAPE 48FEATURES+=-DWANT_FULLSCRAPE
41 49
42# Is enabled on BSD systems by default in trackerlogic.h 50# You need libowfat version 0.34 to allow for automatic release of chunks during
43# on Linux systems you will need -lbds 51# full scrape transfer, if you rely on an older versions, enable this flag
44#FEATURES+=-DWANT_ARC4RANDOM 52#FEATURES+=-DWANT_NO_AUTO_FREE
53
54# opentracker takes its randomness from arc4random, which is part of libc on
55# the BSDs, on macOS and on glibc 2.36 and newer. On an older glibc or on musl
56# it comes from libbsd instead, so uncomment the next include to link it.
57#LDFLAGS+=-lbsd
45 58
46#FEATURES+=-D_DEBUG_HTTPERROR 59#FEATURES+=-D_DEBUG_HTTPERROR
60#FEATURES+=-D_DEBUG_RANDOMTORRENTS
61
62GIT_VERSION=`sh -c 'command -v git >/dev/null && test -d .git && git rev-parse HEAD || echo _git_or_commit_not_found_'`
47 63
48OPTS_debug=-D_DEBUG -g -ggdb # -pg -fprofile-arcs -ftest-coverage 64OPTS_debug=-D_DEBUG -g -ggdb # -pg -fprofile-arcs -ftest-coverage
49OPTS_production=-O3 65OPTS_production=-O3
50 66
51CFLAGS+=-I$(LIBOWFAT_HEADERS) -Wall -pipe -Wextra #-ansi -pedantic 67CFLAGS+=-I$(LIBOWFAT_HEADERS) -DGIT_VERSION=$(GIT_VERSION) -Wall -pipe -pthread -Wextra #-ansi -pedantic
52LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread -lpthread -lz 68LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread
53#LDFLAGS+=-lbsd
54 69
55BINARY =opentracker 70BINARY =opentracker
56HEADERS=trackerlogic.h scan_urlencoded_query.h ot_mutex.h ot_stats.h ot_vector.h ot_clean.h ot_udp.h ot_iovec.h ot_fullscrape.h ot_accesslist.h ot_http.h ot_livesync.h ot_rijndael.h 71HEADERS=trackerlogic.h scan_urlencoded_query.h ot_buf.h ot_hex.h ot_ip.h ot_scan.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
57SOURCES=opentracker.c trackerlogic.c scan_urlencoded_query.c ot_mutex.c ot_stats.c ot_vector.c ot_clean.c ot_udp.c ot_iovec.c ot_fullscrape.c ot_accesslist.c ot_http.c ot_livesync.c ot_rijndael.c 72SOURCES=opentracker.c trackerlogic.c scan_urlencoded_query.c ot_buf.c ot_hex.c ot_ip.c ot_scan.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
58SOURCES_proxy=proxy.c ot_vector.c ot_mutex.c 73SOURCES_proxy=proxy.c ot_vector.c ot_mutex.c ot_random.c ot_scan.c ot_ip.c
59 74
60OBJECTS = $(SOURCES:%.c=%.o) 75OBJECTS = $(SOURCES:%.c=%.o)
61OBJECTS_debug = $(SOURCES:%.c=%.debug.o) 76OBJECTS_debug = $(SOURCES:%.c=%.debug.o)
@@ -71,7 +86,7 @@ CFLAGS_debug = $(CFLAGS) $(OPTS_debug) $(FEATURES)
71 86
72$(BINARY): $(OBJECTS) $(HEADERS) 87$(BINARY): $(OBJECTS) $(HEADERS)
73 $(CC) -o $@ $(OBJECTS) $(LDFLAGS) 88 $(CC) -o $@ $(OBJECTS) $(LDFLAGS)
74 strip $@ 89 $(STRIP) $@
75$(BINARY).debug: $(OBJECTS_debug) $(HEADERS) 90$(BINARY).debug: $(OBJECTS_debug) $(HEADERS)
76 $(CC) -o $@ $(OBJECTS_debug) $(LDFLAGS) 91 $(CC) -o $@ $(OBJECTS_debug) $(LDFLAGS)
77proxy: $(OBJECTS_proxy) $(HEADERS) 92proxy: $(OBJECTS_proxy) $(HEADERS)
@@ -85,8 +100,29 @@ proxy.debug: $(OBJECTS_proxy_debug) $(HEADERS)
85.c.o : $(HEADERS) 100.c.o : $(HEADERS)
86 $(CC) -c -o $@ $(CFLAGS_production) $< 101 $(CC) -c -o $@ $(CFLAGS_production) $<
87 102
103# Unit tests. The modules under test are compiled straight into the test
104# binary, so a test may reach a static function by including the source if it
105# has to. tests/stubs.c supplies the few symbols those modules reference but
106# the tests never reach.
107TESTBIN=tests/runner
108TEST_SOURCES=tests/runner.c tests/test.c tests/stubs.c tests/t_scan_urlencoded_query.c tests/t_iovec.c tests/t_vector.c tests/t_random.c tests/t_hex.c tests/t_accesslist.c tests/t_scan.c tests/t_buf.c tests/t_ip.c
109TEST_UNITS=scan_urlencoded_query.c ot_buf.c ot_ip.c ot_iovec.c ot_vector.c ot_random.c ot_hex.c ot_scan.c ot_accesslist.c
110TEST_HEADERS=tests/test.h $(HEADERS)
111# Tests reach code that sits behind feature flags, so the units under test are
112# built with those features enabled. A test binary is not a production binary.
113FEATURES_test=$(FEATURES) -DWANT_ACCESSLIST_BLACK
114CFLAGS_test = $(CFLAGS) -I. -g -O1 $(FEATURES_test)
115
116$(TESTBIN): $(TEST_SOURCES) $(TEST_UNITS) $(TEST_HEADERS)
117 $(CC) -o $@ $(CFLAGS_test) $(TEST_SOURCES) $(TEST_UNITS)
118
119check: $(TESTBIN)
120 ./$(TESTBIN)
121
122.PHONY: all check clean install
123
88clean: 124clean:
89 rm -rf opentracker opentracker.debug *.o *~ 125 rm -rf opentracker opentracker.debug $(TESTBIN) *.o *~
90 126
91install: 127install:
92 install -m 755 opentracker $(BINDIR) 128 install -m 755 opentracker $(DESTDIR)$(BINDIR)
diff --git a/Makefile.gzip b/Makefile.gzip
new file mode 100644
index 0000000..70d6d62
--- /dev/null
+++ b/Makefile.gzip
@@ -0,0 +1,4 @@
1FEATURES+=-DWANT_COMPRESSION_GZIP
2#FEATURES+=-DWANT_COMPRESSION_GZIP_ALWAYS
3
4LDFLAGS+=-lz
diff --git a/Makefile.zstd b/Makefile.zstd
new file mode 100644
index 0000000..2bb56be
--- /dev/null
+++ b/Makefile.zstd
@@ -0,0 +1,3 @@
1FEATURES+=-DWANT_COMPRESSION_ZSTD
2#FEATURES+=-DWANT_COMPRESSION_ZSTD_ALWAYS
3LDFLAGS+=-lzstd
diff --git a/man1/opentracker.1 b/man1/opentracker.1
new file mode 100644
index 0000000..0103ebe
--- /dev/null
+++ b/man1/opentracker.1
@@ -0,0 +1,130 @@
1.Dd 15/4/2024
2.Dt opentracker 1
3.Os Unix
4.Sh opentracker
5.Nm opentracker
6.Nd a free and open bittorrent tracker
7.Sh SYNOPSIS
8.Nm
9.Op Fl f Ar config
10.Op Fl i Ar ip-select
11.Op Fl p Ar port-bind-tcp
12.Op Fl P Ar port-bind-udp
13.Op Fl A Ar blessed-ip
14.Op Fl r Ar redirect-url
15.Op Fl d Ar chdir
16.Op Fl u Ar user
17.Op Fl w| Fl b accesslist
18.Sh DESCRIPTION
19.Nm
20is a bittorrent tracker that implements announce and scrape actions over the
21UDP and the plain http protocol, aiming for minimal resource usage.
22.Pp
23When invoked with parameters, it binds to TCP and UDP port 6969 on all
24interfaces. The recommended way to configure opentracker is by providing a
25config file using the
26.Op Fl f Ar config
27option. See
28.Xr opentracker.conf 4
29for details.
30.Pp
31.Sh OPTIONS
32The following options are available:
33.Bl -tag -width -indent=8
34.It Fl f Ar config
35Parse a config file with a list of options. Consecutive command options
36will override options from the config file. See
37.Xr opentracker.conf 4
38for details.
39.It Fl i Ar ip-select
40Select an ip address that will be used with the next
41.Op Fl p
42or
43.Op Fl P
44command to actually bind to this address. Setting this option without any bind
45options in the config file or
46.Op Fl p
47or
48.Op Fl P
49commands will limit opentracker to only bind to this address.
50.It Fl p Ar port-bind-tcp
51Bind to the TCP port on the last preceding ip address set with the
52.Op Fl i ip-select
53option or to all available addresses if none has been set. Can be given multiple
54times.
55.It Fl P Ar port-bind-udp
56Bind to the UDP port on the last preceding ip address set with the
57.Op Fl i ip-select
58option or to all available addresses if none has been set. Can be given multiple
59times.
60.It Fl A Ar blessed-ip
61Set an ip address in IPv4 or IPv6 or a net in CIDR notation to bless the network
62for access to restricted resources.
63.It Fl r Ar redirect-url
64Set the URL that
65.Nm
66will redirect users to when the / address is requested via HTTP.
67.It Fl d Ar chdir
68Sets the directory
69.Nm
70will
71.Xr chroot 2
72to if ran as root or
73.Xr chdir 2
74to if ran as unprivileged user. Note that any accesslist files need to be
75relative to and within that directory.
76.It Fl u Ar user
77User to run
78.Nm
79under after all operations that need privileges have finished.
80.It Fl w Ar accesslist | Fl b Ar accesslist
81If
82.Nm
83has been compiled with the
84.B WANT_ACCESSLIST_BLACK
85or
86.Br WANT_ACCESSLIST_WHITE
87options, this option sets the location of the accesslist.
88.El
89.Sh EXAMPLES
90Start
91.Nm
92bound on UDP and TCP ports 6969 on IPv6 localhost.
93.Dl # ./opentracker -i ::1 -p 6969 -P 6969
94.Pp
95Start
96.Nm
97bound on UDP port 6868 and TCP port 6868 on IPv4 localhost and allow
98privileged access from the network 192.168/16 while redirecting
99HTTP clients accessing the root directory, which is not covered by the
100bittorrent tracker protocol, to https://my-trackersite.com/.
101.Dl # ./opentracker -i 192.168.0.4 -p 6868 -P 6969 -A 192.168/16 -r https://my-trackersite.com/
102The announce URLs are http://192.168.0.4:6868/announce and
103udp://192.168.0.4:6868/announce respectively.
104.Sh FILES
105.Bl -tag -width indent
106.It Pa opentracker.conf
107The
108.Nm
109config file.
110.El
111.Sh SEE ALSO
112.Xr opentracker.conf 4
113.Pp
114opentracker documentation
115.Lk https://erdgeist.org/arts/software/opentracker
116.Pp
117Bittorrent tracker protocol
118.Lk http://www.bittorrent.org/beps/bep_0015.html
119.Sh AUTHOR
120.An Dirk Engling
121.Aq Mt erdgeist@erdgeist.org .
122.Sh LICENSE
123This software is released under the Beerware License:
124.Pp
125Permission is hereby granted, free of charge, to any person obtaining a copy of this software
126and associated documentation files (the "Software"), to deal in the Software with the following
127terms and conditions:
128.Pp
129If you meet the author(s) someday, and you think this software is worth it, you can buy them
130a beer in return.
diff --git a/man4/opentracker.conf.4 b/man4/opentracker.conf.4
new file mode 100644
index 0000000..2bc1389
--- /dev/null
+++ b/man4/opentracker.conf.4
@@ -0,0 +1,86 @@
1.Dd 2024-04-18
2.Dt opentracker.conf 5
3.Os Unix
4.Sh NAME
5.Nm opentracker.conf
6.Nd configuration file for opentracker
7.Sh SYNOPSIS
8.Nm
9.Sh DESCRIPTION
10The
11.Nm
12configuration file specifies various options for configuring the behavior of the opentracker program.
13.Pp
14Lines starting with '#' are comments and are ignored. Options are specified as 'keyword value' pairs.
15.Pp
16The following options are available:
17.Pp
18.Bl -tag -width ".It access.proxy" -compact
19.It listen.tcp_udp Ar address
20Specifies an address opentracker will listen on for both TCP and UDP connections. If none are specified, opentracker listens on 0.0.0.0:6969 by default. Can be added more than once.
21.Pp
22.It listen.tcp Ar address
23Specifies the address opentracker will listen on for TCP connections. Can be added more than once.
24.Pp
25.It listen.udp Ar address
26Specifies the address opentracker will listen on for UDP connections. Can be added more than once.
27.Pp
28.It listen.udp.workers Ar threads
29Specifies how many threads will be spawned to handle UDP connections. Defaults to 4.
30.Pp
31.It access.whitelist Ar path/to/whitelist
32Specifies the path to the whitelist file containing all torrent hashes that opentracker will serve. Use this option if opentracker runs in a non-open mode.
33.Pp
34.It access.blacklist Ar path/to/blacklist
35Specifies the path to the blacklist file containing all torrent hashes that opentracker will not serve. Use this option if opentracker was compiled to allow blacklisting.
36.Pp
37.It access.fifo_add Ar path/to/adder.fifo
38Specifies the path to the FIFO (named pipe) used for dynamic changesets to accesslists. Info hashes written to this FIFO will be added to the main accesslist file.
39.Pp
40.It access.fifo_delete Ar path/to/deleter.fifo
41Specifies the path to the FIFO (named pipe) used for dynamic changesets to accesslists. Info hashes written to this FIFO will be removed from the main accesslist file.
42.Pp
43.It access.stats Ar ip_address_or_network
44Specifies the IP address or network in CIDR notation allowed to fetch stats from opentracker.
45.Pp
46.It access.stats_path Ar path
47Specifies the path to the stats location. You can configure opentracker to appear anywhere on your tracker. Defaults to /stats.
48.Pp
49.It access.proxy Ar ip_address_or_network
50Specifies the IP address or network of the reverse proxies. Opentracker will take the X-Forwarded-For address instead of the source IP address. Can be added more than once.
51.Pp
52.It livesync.cluster.listen Ar ip_address:port
53Specifies the IP address and port opentracker will listen on for incoming live sync packets to keep a cluster of opentrackers synchronized.
54.Pp
55.It livesync.cluster.node_ip Ar ip_address
56Specifies one trusted IP address for sync between trackers running in a cluster. Can be added more than once.
57.Pp
58.It batchsync.cluster.admin_ip Ar ip_address
59Specifies the admin IP address for old-style (HTTP-based) asynchronous tracker syncing.
60.Pp
61.It tracker.rootdir Ar path
62Specifies the directory opentracker will chroot/chdir to. All black/white list files must be located in this directory.
63.Pp
64.It tracker.user Ar username
65Specifies the user opentracker will setuid to after binding to potentially privileged ports.
66.Pp
67.It tracker.redirect_url Ar URL
68Specifies the URL opentracker will redirect to in response to a "GET / HTTP" request.
69.El
70.Sh EXAMPLES
71To specify the address opentracker will listen on for both TCP and UDP connections:
72.Dl listen.tcp_udp 0.0.0.0:6969
73.Pp
74To specify the address opentracker will listen on for TCP connections:
75.Dl listen.tcp 0.0.0.0
76.Pp
77To specify the address opentracker will listen on for UDP connections:
78.Dl listen.udp 0.0.0.0:6969
79.Pp
80.Sh SEE ALSO
81.Xr opentracker 1
82.Pp
83.Sh AUTHOR
84.An Dirk Engling
85.Aq Mt erdgeist@erdgeist.org
86.Pp
diff --git a/opentracker.c b/opentracker.c
index 32b3364..af336aa 100644
--- a/opentracker.c
+++ b/opentracker.c
@@ -5,59 +5,61 @@
5 $Id$ */ 5 $Id$ */
6 6
7/* System */ 7/* System */
8#include <ctype.h>
9#include <errno.h>
10#include <limits.h>
11#include <pthread.h>
12#include <pwd.h>
13#include <signal.h>
14#include <stdint.h>
15#include <stdio.h>
8#include <stdlib.h> 16#include <stdlib.h>
9#include <string.h> 17#include <string.h>
10#include <arpa/inet.h>
11#include <sys/socket.h> 18#include <sys/socket.h>
12#include <unistd.h> 19#include <unistd.h>
13#include <errno.h>
14#include <signal.h>
15#include <stdio.h>
16#include <pwd.h>
17#include <ctype.h>
18#include <pthread.h>
19#ifdef WANT_SYSLOGS 20#ifdef WANT_SYSLOGS
20#include <syslog.h> 21#include <syslog.h>
21#endif 22#endif
22 23
23/* Libowfat */ 24/* Libowfat */
24#include "socket.h"
25#include "io.h" 25#include "io.h"
26#include "iob.h" 26#include "iob.h"
27#include "byte.h" 27#include "socket.h"
28#include "scan.h"
29#include "ip6.h"
30 28
31/* Opentracker */ 29/* Opentracker */
32#include "trackerlogic.h"
33#include "ot_mutex.h"
34#include "ot_http.h"
35#include "ot_udp.h"
36#include "ot_accesslist.h" 30#include "ot_accesslist.h"
37#include "ot_stats.h" 31#include "ot_hex.h"
32#include "ot_http.h"
33#include "ot_ip.h"
38#include "ot_livesync.h" 34#include "ot_livesync.h"
35#include "ot_mutex.h"
36#include "ot_random.h"
37#include "ot_scan.h"
38#include "ot_stats.h"
39#include "ot_udp.h"
40#include "trackerlogic.h"
39 41
40/* Globals */ 42/* Globals */
41time_t g_now_seconds; 43time_t g_now_seconds;
42char * g_redirecturl; 44char *g_redirecturl;
43uint32_t g_tracker_id; 45uint32_t g_tracker_id;
44volatile int g_opentracker_running = 1; 46volatile int g_opentracker_running = 1;
45int g_self_pipe[2]; 47int g_self_pipe[2];
46 48
47static char * g_serverdir; 49static char *g_serverdir;
48static char * g_serveruser; 50static char *g_serveruser;
49static unsigned int g_udp_workers; 51static unsigned int g_udp_workers;
50 52
51static void panic( const char *routing ) __attribute__ ((noreturn)); 53static void panic(const char *routine) __attribute__((noreturn));
52static void panic( const char *routine ) { 54static void panic(const char *routine) {
53 fprintf( stderr, "%s: %s\n", routine, strerror(errno) ); 55 fprintf(stderr, "%s: %s\n", routine, strerror(errno));
54 exit( 111 ); 56 exit(111);
55} 57}
56 58
57static void signal_handler( int s ) { 59static void signal_handler(int s) {
58 if( s == SIGINT ) { 60 if (s == SIGINT) {
59 /* Any new interrupt signal quits the application */ 61 /* Any new interrupt signal quits the application */
60 signal( SIGINT, SIG_DFL); 62 signal(SIGINT, SIG_DFL);
61 63
62 /* Tell all other threads to not acquire any new lock on a bucket 64 /* Tell all other threads to not acquire any new lock on a bucket
63 but cancel their operations and return */ 65 but cancel their operations and return */
@@ -69,511 +71,651 @@ static void signal_handler( int s ) {
69 closelog(); 71 closelog();
70#endif 72#endif
71 73
72 exit( 0 ); 74 exit(0);
73 } else if( s == SIGALRM ) {
74 /* Maintain our copy of the clock. time() on BSDs is very expensive. */
75 g_now_seconds = time(NULL);
76 alarm(5);
77 } 75 }
78} 76}
79 77
80static void defaul_signal_handlers( void ) { 78static void defaul_signal_handlers(void) {
81 sigset_t signal_mask; 79 sigset_t signal_mask;
82 sigemptyset(&signal_mask); 80 sigemptyset(&signal_mask);
83 sigaddset (&signal_mask, SIGPIPE); 81 sigaddset(&signal_mask, SIGPIPE);
84 sigaddset (&signal_mask, SIGHUP); 82 sigaddset(&signal_mask, SIGHUP);
85 sigaddset (&signal_mask, SIGINT); 83 sigaddset(&signal_mask, SIGINT);
86 sigaddset (&signal_mask, SIGALRM); 84 sigaddset(&signal_mask, SIGALRM);
87 pthread_sigmask (SIG_BLOCK, &signal_mask, NULL); 85 pthread_sigmask(SIG_BLOCK, &signal_mask, NULL);
88} 86}
89 87
90static void install_signal_handlers( void ) { 88static void install_signal_handlers(void) {
91 struct sigaction sa; 89 struct sigaction sa;
92 sigset_t signal_mask; 90 sigset_t signal_mask;
93 sigemptyset(&signal_mask); 91 sigemptyset(&signal_mask);
94 92
95 sa.sa_handler = signal_handler; 93 sa.sa_handler = signal_handler;
96 sigemptyset(&sa.sa_mask); 94 sigemptyset(&sa.sa_mask);
97 sa.sa_flags = SA_RESTART; 95 sa.sa_flags = SA_RESTART;
98 if ((sigaction(SIGINT, &sa, NULL) == -1) || (sigaction(SIGALRM, &sa, NULL) == -1) ) 96 if ((sigaction(SIGINT, &sa, NULL) == -1) || (sigaction(SIGALRM, &sa, NULL) == -1))
99 panic( "install_signal_handlers" ); 97 panic("install_signal_handlers");
100 98
101 sigaddset (&signal_mask, SIGINT); 99 sigaddset(&signal_mask, SIGINT);
102 sigaddset (&signal_mask, SIGALRM); 100 pthread_sigmask(SIG_UNBLOCK, &signal_mask, NULL);
103 pthread_sigmask (SIG_UNBLOCK, &signal_mask, NULL);
104} 101}
105 102
106static void usage( char *name ) { 103static void usage(char *name) {
107 fprintf( stderr, "Usage: %s [-i ip] [-p port] [-P port] [-r redirect] [-d dir] [-u user] [-A ip] [-f config] [-s livesyncport]" 104 fprintf(stderr,
105 "Usage: %s [-i ip] [-p port] [-P port] [-r redirect] [-d dir] [-u user] [-A ip[/bits]] [-f config] [-s livesyncport]"
108#ifdef WANT_ACCESSLIST_BLACK 106#ifdef WANT_ACCESSLIST_BLACK
109 " [-b blacklistfile]" 107 " [-b blacklistfile]"
110#elif defined ( WANT_ACCESSLIST_WHITE ) 108#elif defined(WANT_ACCESSLIST_WHITE)
111 " [-w whitelistfile]" 109 " [-w whitelistfile]"
112#endif 110#endif
113 "\n", name ); 111 "\n",
112 name);
114} 113}
115 114
116#define HELPLINE(opt,desc) fprintf(stderr, "\t%-10s%s\n",opt,desc) 115#define HELPLINE(opt, desc) fprintf(stderr, "\t%-10s%s\n", opt, desc)
117static void help( char *name ) { 116static void help(char *name) {
118 usage( name ); 117 usage(name);
119 118
120 HELPLINE("-f config","include and execute the config file"); 119 HELPLINE("-f config", "include and execute the config file");
121 HELPLINE("-i ip","specify ip to bind to (default: *, you may specify more than one)"); 120 HELPLINE("-i ip", "specify ip to bind to with next -[pP] (default: any, overrides preceeding ones)");
122 HELPLINE("-p port","specify tcp port to bind to (default: 6969, you may specify more than one)"); 121 HELPLINE("-p port", "do bind to tcp port (default: 6969, you may specify more than one)");
123 HELPLINE("-P port","specify udp port to bind to (default: 6969, you may specify more than one)"); 122 HELPLINE("-P port", "do bind to udp port (default: 6969, you may specify more than one)");
124 HELPLINE("-r redirecturl","specify url where / should be redirected to (default none)"); 123 HELPLINE("-r redirecturl", "specify url where / should be redirected to (default none)");
125 HELPLINE("-d dir","specify directory to try to chroot to (default: \".\")"); 124 HELPLINE("-d dir", "specify directory to try to chroot to (default: \".\")");
126 HELPLINE("-u user","specify user under whose priviliges opentracker should run (default: \"nobody\")"); 125 HELPLINE("-u user", "specify user under whose privileges opentracker should run (default: \"nobody\")");
127 HELPLINE("-A ip","bless an ip address as admin address (e.g. to allow syncs from this address)"); 126 HELPLINE("-A ip[/bits]", "bless an ip address or net as admin address (e.g. to allow syncs from this address)");
128#ifdef WANT_ACCESSLIST_BLACK 127#ifdef WANT_ACCESSLIST_BLACK
129 HELPLINE("-b file","specify blacklist file."); 128 HELPLINE("-b file", "specify blacklist file.");
130#elif defined( WANT_ACCESSLIST_WHITE ) 129#elif defined(WANT_ACCESSLIST_WHITE)
131 HELPLINE("-w file","specify whitelist file."); 130 HELPLINE("-w file", "specify whitelist file.");
132#endif 131#endif
133 132
134 fprintf( stderr, "\nExample: ./opentracker -i 127.0.0.1 -p 6969 -P 6969 -f ./opentracker.conf -i 10.1.1.23 -p 2710 -p 80\n" ); 133 fprintf(stderr, "\nExample: ./opentracker -i 127.0.0.1 -p 6969 -P 6969 -f ./opentracker.conf -i 10.1.1.23 -p 2710 -p 80\n");
134 fprintf(stderr, " Here -i 127.0.0.1 selects the ip address for the next -p 6969 and -P 6969.\n");
135 fprintf(stderr, " If no port is bound from config file or command line, the last address given\n");
136 fprintf(stderr, " (or ::1 if none is set) will be used on port 6969.\n");
135} 137}
136#undef HELPLINE 138#undef HELPLINE
137 139
138static size_t header_complete( char * request, ssize_t byte_count ) { 140static ssize_t header_complete(char *request, ssize_t byte_count) {
139 int i = 0, state = 0; 141 ssize_t i = 0, state = 0;
140 142
141 for( i=1; i < byte_count; i+=2 ) 143 for (i = 1; i < byte_count; i += 2)
142 if( request[i] <= 13 ) { 144 if (request[i] <= 13) {
143 i--; 145 i--;
144 for( state = 0 ; i < byte_count; ++i ) { 146 for (state = 0; i < byte_count; ++i) {
145 char c = request[i]; 147 char c = request[i];
146 if( c == '\r' || c == '\n' ) 148 if (c == '\r' || c == '\n')
147 state = ( state >> 2 ) | ( ( c << 6 ) & 0xc0 ); 149 state = (state >> 2) | ((c << 6) & 0xc0);
148 else 150 else
149 break; 151 break;
150 if( state >= 0xa0 || state == 0x99 ) return i + 1; 152 if (state >= 0xa0 || state == 0x99)
153 return i + 1;
151 } 154 }
152 } 155 }
153 return 0; 156 return 0;
154} 157}
155 158
156static void handle_dead( const int64 sock ) { 159static void handle_dead(const int64_t sock) {
157 struct http_data* cookie=io_getcookie( sock ); 160 struct http_data *cookie = io_getcookie(sock);
158 if( cookie ) { 161 if (cookie) {
159 size_t i; 162 size_t i;
160 for ( i = 0; i < cookie->batches; ++i) 163 for (i = 0; i < cookie->batches; ++i)
161 iob_reset( cookie->batch + i ); 164 iob_reset(cookie->batch + i);
162 free( cookie->batch ); 165 free(cookie->batch);
163 array_reset( &cookie->request ); 166 buf_reset(&cookie->request);
164 if( cookie->flag & STRUCT_HTTP_FLAG_WAITINGFORTASK ) 167 if (cookie->flag & (STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER))
165 mutex_workqueue_canceltask( sock ); 168 mutex_workqueue_canceltask(sock);
166 free( cookie ); 169 free(cookie);
167 } 170 }
168 io_close( sock ); 171 io_close(sock);
169} 172}
170 173
171static void handle_read( const int64 sock, struct ot_workstruct *ws ) { 174static void handle_read(const int64_t sock, struct ot_workstruct *ws) {
172 struct http_data* cookie = io_getcookie( sock ); 175 struct http_data *cookie = io_getcookie(sock);
173 ssize_t byte_count = io_tryread( sock, ws->inbuf, G_INBUF_SIZE ); 176 ssize_t byte_count = io_tryread(sock, ws->inbuf, G_INBUF_SIZE);
174 177
175 if( byte_count == 0 || byte_count == -3 ) { 178 if (byte_count == 0 || byte_count == -3) {
176 handle_dead( sock ); 179 handle_dead(sock);
177 return; 180 return;
178 } 181 }
179 182
183 if (byte_count == -1)
184 return;
185
186 ws->request = NULL;
187 ws->request_size = 0;
188 ws->header_size = 0;
189 ws->keep_alive = 0;
190
180 /* If we get the whole request in one packet, handle it without copying */ 191 /* If we get the whole request in one packet, handle it without copying */
181 if( !array_start( &cookie->request ) ) { 192 if (!cookie->request.data) {
182 if( ( ws->header_size = header_complete( ws->inbuf, byte_count ) ) ) { 193 if ((ws->header_size = header_complete(ws->inbuf, byte_count))) {
183 ws->request = ws->inbuf; 194 ws->request = ws->inbuf;
184 ws->request_size = byte_count; 195 ws->request_size = byte_count;
185 http_handle_request( sock, ws ); 196 http_handle_request(sock, ws);
186 } else 197 } else if (buf_cat(&cookie->request, ws->inbuf, (size_t)byte_count))
187 array_catb( &cookie->request, ws->inbuf, byte_count ); 198 handle_dead(sock);
188 return; 199 return;
189 } 200 }
190 201
191 array_catb( &cookie->request, ws->inbuf, byte_count ); 202 if (buf_cat(&cookie->request, ws->inbuf, (size_t)byte_count) || cookie->request.size > G_INBUF_SIZE) {
192 if( array_failed( &cookie->request ) || array_bytes( &cookie->request ) > 8192 ) { 203 http_issue_error(sock, ws, CODE_HTTPERROR_500);
193 http_issue_error( sock, ws, CODE_HTTPERROR_500 );
194 return; 204 return;
195 } 205 }
196 206
197 while( ( ws->header_size = header_complete( array_start( &cookie->request ), array_bytes( &cookie->request ) ) ) ) { 207 while ((ws->header_size = header_complete(cookie->request.data, (ssize_t)cookie->request.size))) {
198 ws->request = array_start( &cookie->request ); 208 ws->request = cookie->request.data;
199 ws->request_size = array_bytes( &cookie->request ); 209 ws->request_size = (ssize_t)cookie->request.size;
200 http_handle_request( sock, ws ); 210 http_handle_request(sock, ws);
201#ifdef WANT_KEEPALIVE 211#ifdef WANT_KEEPALIVE
202 if( !ws->keep_alive ) 212 if (!ws->keep_alive)
203#endif 213#endif
204 return; 214 return;
205 } 215 }
206} 216}
207 217
208static void handle_write( const int64 sock ) { 218static void handle_write(const int64_t sock) {
209 struct http_data* cookie=io_getcookie( sock ); 219 struct http_data *cookie = io_getcookie(sock);
210 size_t i; 220 size_t i;
221 int chunked = 0;
211 222
212 /* Look for the first io_batch still containing bytes to write */ 223 /* Look for the first io_batch still containing bytes to write */
213 if( cookie ) 224 if (cookie) {
214 for( i = 0; i < cookie->batches; ++i ) 225 if (cookie->flag & STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER)
215 if( cookie->batch[i].bytesleft ) { 226 chunked = 1;
216 int64 res = iob_send( sock, cookie->batch + i );
217 227
218 if( res == -3 ) 228 for (i = 0; i < cookie->batches; ++i) {
219 break; 229 if (cookie->batch[i].bytesleft) {
230 int64_t res = iob_send(sock, cookie->batch + i);
231
232 if (res == -3) {
233 handle_dead(sock);
234 return;
235 }
220 236
221 if( !cookie->batch[i].bytesleft ) 237 if (!cookie->batch[i].bytesleft)
222 continue; 238 continue;
223 239
224 if( res == -1 || res > 0 || i < cookie->batches - 1 ) 240 if (res == -1 || res > 0 || i < cookie->batches - 1)
225 return; 241 return;
226 } 242 }
243 }
244 }
227 245
228 handle_dead( sock ); 246 /* In a chunked transfer after all batches accumulated have been sent, wait for the next one */
247 if (chunked)
248 io_dontwantwrite(sock);
249 else
250 handle_dead(sock);
229} 251}
230 252
231static void handle_accept( const int64 serversocket ) { 253static void handle_accept(const int64_t serversocket) {
232 struct http_data *cookie; 254 struct http_data *cookie;
233 int64 sock; 255 int64_t sock;
234 ot_ip6 ip; 256 ot_ip6 ip;
235 uint16 port; 257 uint16 port;
236 tai6464 t; 258 tai6464 t;
237 259
238 while( ( sock = socket_accept6( serversocket, ip, &port, NULL ) ) != -1 ) { 260 while ((sock = socket_accept6(serversocket, ip, &port, NULL)) != -1) {
239 261
240 /* Put fd into a non-blocking mode */ 262 /* Put fd into a non-blocking mode */
241 io_nonblock( sock ); 263 io_nonblock(sock);
242 264
243 if( !io_fd( sock ) || 265 if (!io_fd(sock) || !(cookie = (struct http_data *)malloc(sizeof(struct http_data)))) {
244 !( cookie = (struct http_data*)malloc( sizeof(struct http_data) ) ) ) { 266 io_close(sock);
245 io_close( sock );
246 continue; 267 continue;
247 } 268 }
248 memset(cookie, 0, sizeof( struct http_data ) ); 269 memset(cookie, 0, sizeof(struct http_data));
249 memcpy(cookie->ip,ip,sizeof(ot_ip6)); 270 memcpy(cookie->ip, ip, sizeof(ot_ip6));
250 271
251 io_setcookie( sock, cookie ); 272 io_setcookie(sock, cookie);
252 io_wantread( sock ); 273 io_wantread(sock);
253 274
254 stats_issue_event( EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ip); 275 stats_issue_event(EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ip);
255 276
256 /* That breaks taia encapsulation. But there is no way to take system 277 /* That breaks taia encapsulation. But there is no way to take system
257 time this often in FreeBSD and libowfat does not allow to set unix time */ 278 time this often in FreeBSD and libowfat does not allow to set unix time */
258 taia_uint( &t, 0 ); /* Clear t */ 279 taia_uint(&t, 0); /* Clear t */
259 tai_unix( &(t.sec), (g_now_seconds + OT_CLIENT_TIMEOUT) ); 280 tai_unix(&(t.sec), (g_now_seconds + OT_CLIENT_TIMEOUT));
260 io_timeout( sock, t ); 281 io_timeout(sock, t);
261 } 282 }
262 io_eagain(serversocket); 283 io_eagain(serversocket);
263} 284}
264 285
265static void * server_mainloop( void * args ) { 286static void *server_mainloop(void *args) {
266 struct ot_workstruct ws; 287 struct ot_workstruct ws;
267 time_t next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL; 288 time_t next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL;
268 struct iovec *iovector; 289 struct iovec *iovector;
269 int iovec_entries; 290 size_t iovec_entries;
291 int is_partial;
270 292
271 (void)args; 293 (void)args;
272 294
273 /* Initialize our "thread local storage" */ 295 /* Initialize our "thread local storage" */
274 ws.inbuf = malloc( G_INBUF_SIZE ); 296 ws.inbuf = malloc(G_INBUF_SIZE);
275 ws.outbuf = malloc( G_OUTBUF_SIZE ); 297 ws.outbuf = malloc(G_OUTBUF_SIZE);
276#ifdef _DEBUG_HTTPERROR 298#ifdef _DEBUG_HTTPERROR
277 ws.debugbuf= malloc( G_DEBUGBUF_SIZE ); 299 ws.debugbuf = malloc(G_DEBUGBUF_SIZE);
278#endif 300#endif
279 301
280 if( !ws.inbuf || !ws.outbuf ) 302 if (!ws.inbuf || !ws.outbuf)
281 panic( "Initializing worker failed" ); 303 panic("Initializing worker failed");
282 304
283#ifdef WANT_ARC4RANDOM 305 random_bytes(ws.rand48_state, sizeof(ws.rand48_state));
284 arc4random_buf(&ws.rand48_state[0], 3 * sizeof(uint16_t));
285#else
286 ws.rand48_state[0] = (uint16_t)random();
287 ws.rand48_state[1] = (uint16_t)random();
288 ws.rand48_state[2] = (uint16_t)random();
289#endif
290 306
291 for( ; ; ) { 307 for (;;) {
292 int64 sock; 308 int64_t sock;
293 309
294 io_wait(); 310 io_wait();
295 311
296 while( ( sock = io_canread( ) ) != -1 ) { 312 while ((sock = io_canread()) != -1) {
297 const void *cookie = io_getcookie( sock ); 313 const void *cookie = io_getcookie(sock);
298 if( (intptr_t)cookie == FLAG_TCP ) 314 if ((intptr_t)cookie == FLAG_TCP)
299 handle_accept( sock ); 315 handle_accept(sock);
300 else if( (intptr_t)cookie == FLAG_UDP ) 316 else if ((intptr_t)cookie == FLAG_UDP)
301 handle_udp6( sock, &ws ); 317 handle_udp6(sock, &ws);
302 else if( (intptr_t)cookie == FLAG_SELFPIPE ) 318 else if ((intptr_t)cookie == FLAG_SELFPIPE)
303 io_tryread( sock, ws.inbuf, G_INBUF_SIZE ); 319 io_tryread(sock, ws.inbuf, G_INBUF_SIZE);
304 else 320 else
305 handle_read( sock, &ws ); 321 handle_read(sock, &ws);
306 } 322 }
307 323
308 while( ( sock = mutex_workqueue_popresult( &iovec_entries, &iovector ) ) != -1 ) 324 while ((sock = mutex_workqueue_popresult(&iovec_entries, &iovector, &is_partial)) != -1)
309 http_sendiovecdata( sock, &ws, iovec_entries, iovector ); 325 http_sendiovecdata(sock, &ws, iovec_entries, iovector, is_partial);
310 326
311 while( ( sock = io_canwrite( ) ) != -1 ) 327 while ((sock = io_canwrite()) != -1)
312 handle_write( sock ); 328 handle_write(sock);
313 329
314 if( g_now_seconds > next_timeout_check ) { 330 if (g_now_seconds > next_timeout_check) {
315 while( ( sock = io_timeouted() ) != -1 ) 331 while ((sock = io_timeouted()) != -1)
316 handle_dead( sock ); 332 handle_dead(sock);
317 next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL; 333 next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL;
318 } 334 }
319 335
320 livesync_ticker(); 336 livesync_ticker();
321
322 /* Enforce setting the clock */
323 signal_handler( SIGALRM );
324 } 337 }
325 return 0; 338 return 0;
326} 339}
327 340
328static int64_t ot_try_bind( ot_ip6 ip, uint16_t port, PROTO_FLAG proto ) { 341static int64_t ot_try_bind(ot_ip6 ip, uint16_t port, PROTO_FLAG proto) {
329 int64 sock = proto == FLAG_TCP ? socket_tcp6( ) : socket_udp6( ); 342 int64_t sock = proto == FLAG_TCP ? socket_tcp6() : socket_udp6();
330
331#ifndef WANT_V6
332 if( !ip6_isv4mapped(ip) ) {
333 exerr( "V4 Tracker is V4 only!" );
334 }
335#else
336 if( ip6_isv4mapped(ip) ) {
337 exerr( "V6 Tracker is V6 only!" );
338 }
339#endif
340 343
341#ifdef _DEBUG 344#ifdef _DEBUG
342 { 345 {
343 char *protos[] = {"TCP","UDP","UDP mcast"}; 346 char *protos[] = {"TCP", "UDP", "UDP mcast"};
344 char _debug[512]; 347 char _debug[512];
345 int off = snprintf( _debug, sizeof(_debug), "Binding socket type %s to address [", protos[proto] ); 348 int off = snprintf(_debug, sizeof(_debug), "Binding socket type %s to address [", protos[proto]);
346 off += fmt_ip6c( _debug+off, ip); 349 off += ip_format(_debug + off, ip);
347 snprintf( _debug + off, sizeof(_debug)-off, "]:%d...", port); 350 snprintf(_debug + off, sizeof(_debug) - off, "]:%d...", port);
348 fputs( _debug, stderr ); 351 fputs(_debug, stderr);
349 } 352 }
350#endif 353#endif
351 354
352 if( socket_bind6_reuse( sock, ip, port, 0 ) == -1 ) 355 if (socket_bind6_reuse(sock, ip, port, 0) == -1)
353 panic( "socket_bind6_reuse" ); 356 panic("socket_bind6_reuse");
354 357
355 if( ( proto == FLAG_TCP ) && ( socket_listen( sock, SOMAXCONN) == -1 ) ) 358 if ((proto == FLAG_TCP) && (socket_listen(sock, SOMAXCONN) == -1))
356 panic( "socket_listen" ); 359 panic("socket_listen");
357 360
358 if( !io_fd( sock ) ) 361 if (!io_fd(sock))
359 panic( "io_fd" ); 362 panic("io_fd");
360 363
361 io_setcookie( sock, (void*)proto ); 364 io_setcookie(sock, (void *)proto);
362 365
363 if( (proto == FLAG_UDP) && g_udp_workers ) { 366 if ((proto == FLAG_UDP) && g_udp_workers) {
364 io_block( sock ); 367 io_block(sock);
365 udp_init( sock, g_udp_workers ); 368 udp_init(sock, g_udp_workers);
366 } else 369 } else
367 io_wantread( sock ); 370 io_wantread(sock);
368 371
369#ifdef _DEBUG 372#ifdef _DEBUG
370 fputs( " success.\n", stderr); 373 fputs(" success.\n", stderr);
371#endif 374#endif
372 375
373 return sock; 376 return sock;
374} 377}
375 378
376char * set_config_option( char **option, char *value ) { 379char *set_config_option(char **option, char *value) {
377#ifdef _DEBUG 380#ifdef _DEBUG
378 fprintf( stderr, "Setting config option: %s\n", value ); 381 fprintf(stderr, "Setting config option: %s\n", value);
379#endif 382#endif
380 while( isspace(*value) ) ++value; 383 while (isspace(*value))
381 free( *option ); 384 ++value;
382 return *option = strdup( value ); 385 free(*option);
386 return *option = strdup(value);
383} 387}
384 388
385static int scan_ip6_port( const char *src, ot_ip6 ip, uint16 *port ) { 389static int scan_ip6_port(const char *src, ot_ip6 ip, uint16_t *port) {
386 const char *s = src; 390 const char *s = src;
387 int off, bracket = 0; 391 uint64_t value;
388 while( isspace(*s) ) ++s; 392 int off, bracket = 0;
389 if( *s == '[' ) ++s, ++bracket; /* for v6 style notation */ 393 while (isspace(*s))
390 if( !(off = scan_ip6( s, ip ) ) ) 394 ++s;
395 if (*s == '[')
396 ++s, ++bracket; /* for v6 style notation */
397 if (!(off = ip_scan(s, ip)))
391 return 0; 398 return 0;
392 s += off; 399 s += off;
393 if( bracket && *s == ']' ) ++s; 400 if (bracket && *s == ']')
394 if( *s == 0 || isspace(*s)) return s-src; 401 ++s;
395 if( !ip6_isv4mapped(ip)){ 402 if (*s == 0 || isspace(*s))
396 if( *s != ':' && *s != '.' ) return 0; 403 return s - src;
397 if( !bracket && *(s) == ':' ) return 0; 404 if (!ip_is_v4mapped(ip)) {
405 if (*s != ':' && *s != '.')
406 return 0;
407 if (!bracket && *(s) == ':')
408 return 0;
398 s++; 409 s++;
399 } else { 410 } else {
400 if( *(s++) != ':' ) return 0; 411 if (*(s++) != ':')
412 return 0;
401 } 413 }
402 if( !(off = scan_ushort (s, port ) ) ) 414 if (!(off = scan_udec(s, &value, 65535)))
403 return 0; 415 return 0;
404 return off+s-src; 416 *port = (uint16_t)value;
417 return off + s - src;
405} 418}
406 419
407int parse_configfile( char * config_filename ) { 420static int scan_ip6_net(const char *src, ot_net *net) {
408 FILE * accesslist_filehandle; 421 const char *s = src;
409 char inbuf[512]; 422 uint64_t value;
410 ot_ip6 tmpip; 423 int off;
411 int bound = 0; 424 while (isspace(*s))
425 ++s;
426 if (!(off = ip_scan(s, net->address)))
427 return 0;
428 s += off;
429 if (*s != '/')
430 net->bits = 128;
431 else {
432 s++;
433 if (!(off = scan_udec(s, &value, 128)))
434 return 0;
435 net->bits = (int)value;
436 if (ip_is_v4mapped(net->address))
437 net->bits += 96;
438 if (net->bits > 128)
439 return 0;
440 s += off;
441 }
442 return off + s - src;
443}
444
445typedef enum {
446 CONFIG_UNKNOWN,
447 CONFIG_TRACKER_ROOTDIR,
448 CONFIG_TRACKER_USER,
449 CONFIG_LISTEN_TCP_UDP,
450 CONFIG_LISTEN_TCP,
451 CONFIG_LISTEN_UDP,
452 CONFIG_LISTEN_UDP_WORKERS,
453 CONFIG_ACCESS_WHITELIST,
454 CONFIG_ACCESS_BLACKLIST,
455 CONFIG_ACCESS_FIFO_ADD,
456 CONFIG_ACCESS_FIFO_DELETE,
457 CONFIG_ACCESS_STATS,
458 CONFIG_ACCESS_STATS_PATH,
459 CONFIG_ACCESS_PROXY,
460 CONFIG_TRACKER_REDIRECT_URL,
461 CONFIG_LIVESYNC_CLUSTER_NODE_IP,
462 CONFIG_LIVESYNC_CLUSTER_LISTEN,
463} config_kind;
412 464
413 accesslist_filehandle = fopen( config_filename, "r" ); 465typedef struct {
466 const char *name;
467 size_t len;
468 config_kind kind;
469} config_key;
414 470
415 if( accesslist_filehandle == NULL ) { 471#define CONFIG_KEY(s, k) { s, sizeof(s) - 1, k }
416 fprintf( stderr, "Warning: Can't open config file: %s.", config_filename ); 472
473static const config_key config_keys[] = {
474 CONFIG_KEY("tracker.rootdir", CONFIG_TRACKER_ROOTDIR),
475 CONFIG_KEY("tracker.user", CONFIG_TRACKER_USER),
476 CONFIG_KEY("listen.tcp_udp", CONFIG_LISTEN_TCP_UDP),
477 CONFIG_KEY("listen.tcp", CONFIG_LISTEN_TCP),
478 CONFIG_KEY("listen.udp", CONFIG_LISTEN_UDP),
479 CONFIG_KEY("listen.udp.workers", CONFIG_LISTEN_UDP_WORKERS),
480#ifdef WANT_ACCESSLIST_WHITE
481 CONFIG_KEY("access.whitelist", CONFIG_ACCESS_WHITELIST),
482#elif defined(WANT_ACCESSLIST_BLACK)
483 CONFIG_KEY("access.blacklist", CONFIG_ACCESS_BLACKLIST),
484#endif
485#ifdef WANT_DYNAMIC_ACCESSLIST
486 CONFIG_KEY("access.fifo_add", CONFIG_ACCESS_FIFO_ADD),
487 CONFIG_KEY("access.fifo_delete", CONFIG_ACCESS_FIFO_DELETE),
488#endif
489#ifdef WANT_RESTRICT_STATS
490 CONFIG_KEY("access.stats", CONFIG_ACCESS_STATS),
491#endif
492 CONFIG_KEY("access.stats_path", CONFIG_ACCESS_STATS_PATH),
493#ifdef WANT_IP_FROM_PROXY
494 CONFIG_KEY("access.proxy", CONFIG_ACCESS_PROXY),
495#endif
496 CONFIG_KEY("tracker.redirect_url", CONFIG_TRACKER_REDIRECT_URL),
497#ifdef WANT_SYNC_LIVE
498 CONFIG_KEY("livesync.cluster.node_ip", CONFIG_LIVESYNC_CLUSTER_NODE_IP),
499 CONFIG_KEY("livesync.cluster.listen", CONFIG_LIVESYNC_CLUSTER_LISTEN),
500#endif
501};
502static const size_t config_entry_count = sizeof(config_keys) / sizeof(config_keys[0]);
503
504static config_kind parse_config_key(char **p) {
505 size_t i;
506 for (i = 0; i < config_entry_count; ++i) {
507 const config_key *key = &config_keys[i];
508 if (strncmp(*p, key->name, key->len) == 0 && isspace((unsigned char)(*p)[key->len])) {
509 *p += key->len + 1;
510 /* Skip white spaces */
511 while (isspace((unsigned char)**p))
512 ++(*p);
513 return key->kind;
514 }
515 }
516 return CONFIG_UNKNOWN;
517}
518
519int parse_configfile(char *config_filename) {
520 FILE *config_file_handle;
521 char inbuf[512];
522 ot_ip6 tmpip;
523#if defined(WANT_RESTRICT_STATS) || defined(WANT_IP_FROM_PROXY) || defined(WANT_SYNC_LIVE)
524 ot_net tmpnet;
525#endif
526 int bound = 0;
527
528 config_file_handle = fopen(config_filename, "r");
529
530 if (config_file_handle == NULL) {
531 fprintf(stderr, "Warning: Can't open config file: %s.", config_filename);
417 return 0; 532 return 0;
418 } 533 }
419 534
420 while( fgets( inbuf, sizeof(inbuf), accesslist_filehandle ) ) { 535 while (fgets(inbuf, sizeof(inbuf), config_file_handle)) {
421 char *p = inbuf; 536 char *p = inbuf, *end;
422 size_t strl; 537 uint64_t value;
538 uint16_t tmpport = 6969;
423 539
424 /* Skip white spaces */ 540 /* Skip white spaces */
425 while(isspace(*p)) ++p; 541 while (isspace((unsigned char)*p))
542 ++p;
426 543
427 /* Ignore comments and empty lines */ 544 /* Ignore comments and empty lines */
428 if((*p=='#')||(*p=='\n')||(*p==0)) continue; 545 if ((*p == '#') || (*p == '\n') || (*p == 0))
546 continue;
429 547
430 /* consume trailing new lines and spaces */ 548 /* consume trailing new lines and spaces */
431 strl = strlen(p); 549 end = p + strlen(p);
432 while( strl && isspace(p[strl-1])) 550 while (end > p && isspace((unsigned char)end[-1]))
433 p[--strl] = 0; 551 *--end = 0;
434 552
435 /* Scan for commands */ 553 /* Scan for commands */
436 if(!byte_diff(p,15,"tracker.rootdir" ) && isspace(p[15])) { 554 switch (parse_config_key(&p)) {
437 set_config_option( &g_serverdir, p+16 ); 555 case CONFIG_TRACKER_ROOTDIR:
438 } else if(!byte_diff(p,12,"tracker.user" ) && isspace(p[12])) { 556 set_config_option(&g_serverdir, p);
439 set_config_option( &g_serveruser, p+13 ); 557 break;
440 } else if(!byte_diff(p,14,"listen.tcp_udp" ) && isspace(p[14])) { 558 case CONFIG_TRACKER_USER:
441 uint16_t tmpport = 6969; 559 set_config_option(&g_serveruser, p);
442 if( !scan_ip6_port( p+15, tmpip, &tmpport )) goto parse_error; 560 break;
443 ot_try_bind( tmpip, tmpport, FLAG_TCP ); ++bound; 561 case CONFIG_LISTEN_TCP_UDP:
444 ot_try_bind( tmpip, tmpport, FLAG_UDP ); ++bound; 562 if (!scan_ip6_port(p, tmpip, &tmpport))
445 } else if(!byte_diff(p,10,"listen.tcp" ) && isspace(p[10])) { 563 goto parse_error;
446 uint16_t tmpport = 6969; 564 ot_try_bind(tmpip, tmpport, FLAG_TCP);
447 if( !scan_ip6_port( p+11, tmpip, &tmpport )) goto parse_error; 565 ++bound;
448 ot_try_bind( tmpip, tmpport, FLAG_TCP ); 566 ot_try_bind(tmpip, tmpport, FLAG_UDP);
449 ++bound; 567 ++bound;
450 } else if(!byte_diff(p, 10, "listen.udp" ) && isspace(p[10])) { 568 break;
451 uint16_t tmpport = 6969; 569 case CONFIG_LISTEN_TCP:
452 if( !scan_ip6_port( p+11, tmpip, &tmpport )) goto parse_error; 570 if (!scan_ip6_port(p, tmpip, &tmpport))
453 ot_try_bind( tmpip, tmpport, FLAG_UDP ); 571 goto parse_error;
454 ++bound; 572 ot_try_bind(tmpip, tmpport, FLAG_TCP);
455 } else if(!byte_diff(p,18,"listen.udp.workers" ) && isspace(p[18])) { 573 ++bound;
456 char *value = p + 18; 574 break;
457 while( isspace(*value) ) ++value; 575 case CONFIG_LISTEN_UDP:
458 scan_uint( value, &g_udp_workers ); 576 if (!scan_ip6_port(p, tmpip, &tmpport))
577 goto parse_error;
578 ot_try_bind(tmpip, tmpport, FLAG_UDP);
579 ++bound;
580 break;
581 case CONFIG_LISTEN_UDP_WORKERS:
582 if (scan_udec(p, &value, UINT_MAX))
583 g_udp_workers = (unsigned int)value;
584 break;
459#ifdef WANT_ACCESSLIST_WHITE 585#ifdef WANT_ACCESSLIST_WHITE
460 } else if(!byte_diff(p, 16, "access.whitelist" ) && isspace(p[16])) { 586 case CONFIG_ACCESS_WHITELIST:
461 set_config_option( &g_accesslist_filename, p+17 ); 587 set_config_option(&g_accesslist_filename, p);
462#elif defined( WANT_ACCESSLIST_BLACK ) 588 break;
463 } else if(!byte_diff(p, 16, "access.blacklist" ) && isspace(p[16])) { 589#elif defined(WANT_ACCESSLIST_BLACK)
464 set_config_option( &g_accesslist_filename, p+17 ); 590 case CONFIG_ACCESS_BLACKLIST:
591 set_config_option(&g_accesslist_filename, p);
592 break;
593#endif
594#ifdef WANT_DYNAMIC_ACCESSLIST
595 case CONFIG_ACCESS_FIFO_ADD:
596 set_config_option(&g_accesslist_pipe_add, p);
597 break;
598 case CONFIG_ACCESS_FIFO_DELETE:
599 set_config_option(&g_accesslist_pipe_delete, p);
600 break;
465#endif 601#endif
466#ifdef WANT_RESTRICT_STATS 602#ifdef WANT_RESTRICT_STATS
467 } else if(!byte_diff(p, 12, "access.stats" ) && isspace(p[12])) { 603 case CONFIG_ACCESS_STATS:
468 if( !scan_ip6( p+13, tmpip )) goto parse_error; 604 if (!scan_ip6_net(p, &tmpnet))
469 accesslist_blessip( tmpip, OT_PERMISSION_MAY_STAT ); 605 goto parse_error;
606 accesslist_bless_net(&tmpnet, OT_PERMISSION_MAY_STAT);
607 break;
470#endif 608#endif
471 } else if(!byte_diff(p, 17, "access.stats_path" ) && isspace(p[17])) { 609 case CONFIG_ACCESS_STATS_PATH:
472 set_config_option( &g_stats_path, p+18 ); 610 set_config_option(&g_stats_path, p);
611 break;
473#ifdef WANT_IP_FROM_PROXY 612#ifdef WANT_IP_FROM_PROXY
474 } else if(!byte_diff(p, 12, "access.proxy" ) && isspace(p[12])) { 613 case CONFIG_ACCESS_PROXY:
475 if( !scan_ip6( p+13, tmpip )) goto parse_error; 614 if (!scan_ip6_net(p, &tmpnet))
476 accesslist_blessip( tmpip, OT_PERMISSION_MAY_PROXY ); 615 goto parse_error;
616 accesslist_bless_net(&tmpnet, OT_PERMISSION_MAY_PROXY);
617 break;
477#endif 618#endif
478 } else if(!byte_diff(p, 20, "tracker.redirect_url" ) && isspace(p[20])) { 619 case CONFIG_TRACKER_REDIRECT_URL:
479 set_config_option( &g_redirecturl, p+21 ); 620 set_config_option(&g_redirecturl, p);
621 break;
480#ifdef WANT_SYNC_LIVE 622#ifdef WANT_SYNC_LIVE
481 } else if(!byte_diff(p, 24, "livesync.cluster.node_ip" ) && isspace(p[24])) { 623 case CONFIG_LIVESYNC_CLUSTER_NODE_IP:
482 if( !scan_ip6( p+25, tmpip )) goto parse_error; 624 if (!scan_ip6_net(p, &tmpnet))
483 accesslist_blessip( tmpip, OT_PERMISSION_MAY_LIVESYNC ); 625 goto parse_error;
484 } else if(!byte_diff(p, 23, "livesync.cluster.listen" ) && isspace(p[23])) { 626 accesslist_bless_net(&tmpnet, OT_PERMISSION_MAY_LIVESYNC);
485 uint16_t tmpport = LIVESYNC_PORT; 627 break;
486 if( !scan_ip6_port( p+24, tmpip, &tmpport )) goto parse_error; 628 case CONFIG_LIVESYNC_CLUSTER_LISTEN:
487 livesync_bind_mcast( tmpip, tmpport ); 629 tmpport = LIVESYNC_PORT;
630 if (!scan_ip6_port(p, tmpip, &tmpport))
631 goto parse_error;
632 livesync_bind_mcast(tmpip, tmpport);
633 break;
488#endif 634#endif
489 } else 635 default:
490 fprintf( stderr, "Unhandled line in config file: %s\n", inbuf ); 636 fprintf(stderr, "Unhandled line in config file: %s\n", inbuf);
491 continue; 637 break;
492 parse_error: 638 parse_error:
493 fprintf( stderr, "Parse error in config file: %s\n", inbuf); 639 fprintf(stderr, "Parse error in config file: %s\n", inbuf);
640 }
494 } 641 }
495 fclose( accesslist_filehandle ); 642 fclose(config_file_handle);
496 return bound; 643 return bound;
497} 644}
498 645
499void load_state(const char * const state_filename ) { 646void load_state(const char *const state_filename) {
500 FILE * state_filehandle; 647 FILE *state_filehandle;
501 char inbuf[512]; 648 char inbuf[512];
502 ot_hash infohash; 649 ot_hash infohash;
503 unsigned long long base, downcount; 650 uint64_t base, downcount;
504 int consumed; 651 int consumed;
505 652
506 state_filehandle = fopen( state_filename, "r" ); 653 state_filehandle = fopen(state_filename, "r");
507 654
508 if( state_filehandle == NULL ) { 655 if (state_filehandle == NULL) {
509 fprintf( stderr, "Warning: Can't open config file: %s.", state_filename ); 656 fprintf(stderr, "Warning: Can't open config file: %s.", state_filename);
510 return; 657 return;
511 } 658 }
512 659
513 /* We do ignore anything that is not of the form "^[:xdigit:]:\d+:\d+" */ 660 /* We do ignore anything that is not of the form "^[:xdigit:]:\d+:\d+" */
514 while( fgets( inbuf, sizeof(inbuf), state_filehandle ) ) { 661 while (fgets(inbuf, sizeof(inbuf), state_filehandle)) {
515 int i; 662 size_t i;
516 for( i=0; i<(int)sizeof(ot_hash); ++i ) {
517 int eger = 16 * scan_fromhex( inbuf[ 2*i ] ) + scan_fromhex( inbuf[ 1 + 2*i ] );
518 if( eger < 0 )
519 continue;
520 infohash[i] = eger;
521 }
522 663
523 if( i != (int)sizeof(ot_hash) ) continue; 664 if (!hex_tohash(infohash, inbuf))
524 i *= 2; 665 continue;
666 i = 2 * sizeof(ot_hash);
525 667
526 if( inbuf[ i++ ] != ':' || !( consumed = scan_ulonglong( inbuf+i, &base ) ) ) continue; 668 if (inbuf[i++] != ':' || !(consumed = scan_udec(inbuf + i, &base, UINT64_MAX)))
669 continue;
527 i += consumed; 670 i += consumed;
528 if( inbuf[ i++ ] != ':' || !( consumed = scan_ulonglong( inbuf+i, &downcount ) ) ) continue; 671 if (inbuf[i++] != ':' || !(consumed = scan_udec(inbuf + i, &downcount, UINT64_MAX)))
529 add_torrent_from_saved_state( infohash, base, downcount ); 672 continue;
673 add_torrent_from_saved_state(infohash, base, downcount);
530 } 674 }
531 675
532 fclose( state_filehandle ); 676 fclose(state_filehandle);
533} 677}
534 678
535int drop_privileges ( const char * const serveruser, const char * const serverdir ) { 679int drop_privileges(const char *const serveruser, const char *const serverdir) {
536 struct passwd *pws = NULL; 680 struct passwd *pws = NULL;
537 681
538#ifdef _DEBUG 682#ifdef _DEBUG
539 if( !geteuid() ) 683 if (!geteuid())
540 fprintf( stderr, "Dropping to user %s.\n", serveruser ); 684 fprintf(stderr, "Dropping to user %s.\n", serveruser);
541 if( serverdir ) 685 if (serverdir)
542 fprintf( stderr, "ch%s'ing to directory %s.\n", geteuid() ? "dir" : "root", serverdir ); 686 fprintf(stderr, "ch%s'ing to directory %s.\n", geteuid() ? "dir" : "root", serverdir);
543#endif 687#endif
544 688
545 /* Grab pws entry before chrooting */ 689 /* Grab pws entry before chrooting */
546 pws = getpwnam( serveruser ); 690 pws = getpwnam(serveruser);
547 endpwent(); 691 endpwent();
548 692
549 if( geteuid() == 0 ) { 693 if (geteuid() == 0) {
550 /* Running as root: chroot and drop privileges */ 694 /* Running as root: chroot and drop privileges */
551 if( serverdir && chroot( serverdir ) ) { 695 if (serverdir && chroot(serverdir)) {
552 fprintf( stderr, "Could not chroot to %s, because: %s\n", serverdir, strerror(errno) ); 696 fprintf(stderr, "Could not chroot to %s, because: %s\n", serverdir, strerror(errno));
553 return -1; 697 return -1;
554 } 698 }
555 699
556 if(chdir("/")) 700 if (chdir("/"))
557 panic("chdir() failed after chrooting: "); 701 panic("chdir() failed after chrooting: ");
558 702
559 /* If we can't find server user, revert to nobody's default uid */ 703 /* If we can't find server user, revert to nobody's default uid */
560 if( !pws ) { 704 if (!pws) {
561 fprintf( stderr, "Warning: Could not get password entry for %s. Reverting to uid -2.\n", serveruser ); 705 fprintf(stderr, "Warning: Could not get password entry for %s. Reverting to uid -2.\n", serveruser);
562 setegid( (gid_t)-2 ); setgid( (gid_t)-2 ); 706 if (setegid((gid_t)-2) || setgid((gid_t)-2) || setuid((uid_t)-2) || seteuid((uid_t)-2))
563 setuid( (uid_t)-2 ); seteuid( (uid_t)-2 ); 707 panic("Could not set uid to value -2");
564 } 708 } else {
565 else { 709 if (setegid(pws->pw_gid) || setgid(pws->pw_gid) || setuid(pws->pw_uid) || seteuid(pws->pw_uid))
566 setegid( pws->pw_gid ); setgid( pws->pw_gid ); 710 panic("Could not set uid to specified value");
567 setuid( pws->pw_uid ); seteuid( pws->pw_uid );
568 } 711 }
569 712
570 if( geteuid() == 0 || getegid() == 0 ) 713 if (geteuid() == 0 || getegid() == 0)
571 panic("Still running with root privileges?!"); 714 panic("Still running with root privileges?!");
572 } 715 } else {
573 else {
574 /* Normal user, just chdir() */ 716 /* Normal user, just chdir() */
575 if( serverdir && chdir( serverdir ) ) { 717 if (serverdir && chdir(serverdir)) {
576 fprintf( stderr, "Could not chroot to %s, because: %s\n", serverdir, strerror(errno) ); 718 fprintf(stderr, "Could not chroot to %s, because: %s\n", serverdir, strerror(errno));
577 return -1; 719 return -1;
578 } 720 }
579 } 721 }
@@ -581,118 +723,169 @@ int drop_privileges ( const char * const serveruser, const char * const serverdi
581 return 0; 723 return 0;
582} 724}
583 725
584int main( int argc, char **argv ) { 726/* Maintain our copy of the clock. time() on BSDs is very expensive. */
585 ot_ip6 serverip, tmpip; 727static void *time_caching_worker(void *args) {
586 int bound = 0, scanon = 1; 728 (void)args;
587 uint16_t tmpport; 729 while (1) {
588 char * statefile = 0; 730 g_now_seconds = time(NULL);
731 sleep(5);
732 }
733 return NULL;
734}
735
736int main(int argc, char **argv) {
737 ot_ip6 serverip;
738 ot_net tmpnet;
739 int bound = 0, scanon = 1;
740 uint64_t value;
741 char *statefile = 0;
742 pthread_t thread_id; /* time cacher */
589 743
590 memset( serverip, 0, sizeof(ot_ip6) ); 744 memset(serverip, 0, sizeof(ot_ip6));
591#ifndef WANT_V6 745#ifdef WANT_V4_ONLY
592 serverip[10]=serverip[11]=-1; 746 serverip[10] = serverip[11] = -1;
593 noipv6=1;
594#endif 747#endif
595 748
596#ifdef WANT_DEV_RANDOM 749 random_init();
597 srandomdev();
598#else
599 srandom( time(NULL) );
600#endif
601 750
602 while( scanon ) { 751 while (scanon) {
603 switch( getopt( argc, argv, ":i:p:A:P:d:u:r:s:f:l:v" 752 switch (getopt(argc, argv,
753 ":i:p:A:P:d:u:r:s:f:l:v"
604#ifdef WANT_ACCESSLIST_BLACK 754#ifdef WANT_ACCESSLIST_BLACK
605"b:" 755 "b:"
606#elif defined( WANT_ACCESSLIST_WHITE ) 756#elif defined(WANT_ACCESSLIST_WHITE)
607"w:" 757 "w:"
608#endif 758#endif
609 "h" ) ) { 759 "h")) {
610 case -1 : scanon = 0; break; 760 case -1:
611 case 'i': 761 scanon = 0;
612 if( !scan_ip6( optarg, serverip )) { usage( argv[0] ); exit( 1 ); } 762 break;
613 break; 763 case 'i':
764 if (!ip_scan(optarg, serverip)) {
765 usage(argv[0]);
766 exit(1);
767 }
768 break;
614#ifdef WANT_ACCESSLIST_BLACK 769#ifdef WANT_ACCESSLIST_BLACK
615 case 'b': set_config_option( &g_accesslist_filename, optarg); break; 770 case 'b':
616#elif defined( WANT_ACCESSLIST_WHITE ) 771 set_config_option(&g_accesslist_filename, optarg);
617 case 'w': set_config_option( &g_accesslist_filename, optarg); break; 772 break;
773#elif defined(WANT_ACCESSLIST_WHITE)
774 case 'w':
775 set_config_option(&g_accesslist_filename, optarg);
776 break;
618#endif 777#endif
619 case 'p': 778 case 'p':
620 if( !scan_ushort( optarg, &tmpport)) { usage( argv[0] ); exit( 1 ); } 779 if (!scan_udec(optarg, &value, 65535)) {
621 ot_try_bind( serverip, tmpport, FLAG_TCP ); bound++; break; 780 usage(argv[0]);
622 case 'P': 781 exit(1);
623 if( !scan_ushort( optarg, &tmpport)) { usage( argv[0] ); exit( 1 ); } 782 }
624 ot_try_bind( serverip, tmpport, FLAG_UDP ); bound++; break; 783 ot_try_bind(serverip, (uint16_t)value, FLAG_TCP);
784 bound++;
785 break;
786 case 'P':
787 if (!scan_udec(optarg, &value, 65535)) {
788 usage(argv[0]);
789 exit(1);
790 }
791 ot_try_bind(serverip, (uint16_t)value, FLAG_UDP);
792 bound++;
793 break;
625#ifdef WANT_SYNC_LIVE 794#ifdef WANT_SYNC_LIVE
626 case 's': 795 case 's':
627 if( !scan_ushort( optarg, &tmpport)) { usage( argv[0] ); exit( 1 ); } 796 if (!scan_udec(optarg, &value, 65535)) {
628 livesync_bind_mcast( serverip, tmpport); break; 797 usage(argv[0]);
798 exit(1);
799 }
800 livesync_bind_mcast(serverip, (uint16_t)value);
801 break;
629#endif 802#endif
630 case 'd': set_config_option( &g_serverdir, optarg ); break; 803 case 'd':
631 case 'u': set_config_option( &g_serveruser, optarg ); break; 804 set_config_option(&g_serverdir, optarg);
632 case 'r': set_config_option( &g_redirecturl, optarg ); break; 805 break;
633 case 'l': statefile = optarg; break; 806 case 'u':
634 case 'A': 807 set_config_option(&g_serveruser, optarg);
635 if( !scan_ip6( optarg, tmpip )) { usage( argv[0] ); exit( 1 ); } 808 break;
636 accesslist_blessip( tmpip, 0xffff ); /* Allow everything for now */ 809 case 'r':
637 break; 810 set_config_option(&g_redirecturl, optarg);
638 case 'f': bound += parse_configfile( optarg ); break; 811 break;
639 case 'h': help( argv[0] ); exit( 0 ); 812 case 'l':
640 case 'v': { 813 statefile = optarg;
641 char buffer[8192]; 814 break;
642 stats_return_tracker_version( buffer ); 815 case 'A':
643 fputs( buffer, stderr ); 816 if (!scan_ip6_net(optarg, &tmpnet)) {
644 exit( 0 ); 817 usage(argv[0]);
818 exit(1);
645 } 819 }
646 default: 820 accesslist_bless_net(&tmpnet, 0xffff); /* Allow everything for now */
647 case '?': usage( argv[0] ); exit( 1 ); 821 break;
822 case 'f':
823 bound += parse_configfile(optarg);
824 break;
825 case 'h':
826 help(argv[0]);
827 exit(0);
828 case 'v': {
829 char buffer[8192];
830 stats_return_tracker_version(buffer);
831 fputs(buffer, stderr);
832 exit(0);
833 }
834 default:
835 case '?':
836 usage(argv[0]);
837 exit(1);
648 } 838 }
649 } 839 }
650 840
651 /* Bind to our default tcp/udp ports */ 841 /* Bind to our default tcp/udp ports */
652 if( !bound) { 842 if (!bound) {
653 ot_try_bind( serverip, 6969, FLAG_TCP ); 843 ot_try_bind(serverip, 6969, FLAG_TCP);
654 ot_try_bind( serverip, 6969, FLAG_UDP ); 844 ot_try_bind(serverip, 6969, FLAG_UDP);
655 } 845 }
656 846
847 defaul_signal_handlers();
848
657#ifdef WANT_SYSLOGS 849#ifdef WANT_SYSLOGS
658 openlog( "opentracker", 0, LOG_USER ); 850 openlog("opentracker", 0, LOG_USER);
659 setlogmask(LOG_UPTO(LOG_INFO)); 851 setlogmask(LOG_UPTO(LOG_INFO));
660#endif 852#endif
661 853
662 if( drop_privileges( g_serveruser ? g_serveruser : "nobody", g_serverdir ) == -1 ) 854 if (drop_privileges(g_serveruser ? g_serveruser : "nobody", g_serverdir) == -1)
663 panic( "drop_privileges failed, exiting. Last error"); 855 panic("drop_privileges failed, exiting. Last error");
664 856
665 g_now_seconds = time( NULL ); 857 g_now_seconds = time(NULL);
858 pthread_create(&thread_id, NULL, time_caching_worker, NULL);
666 859
667 /* Create our self pipe which allows us to interrupt mainloops 860 /* Create our self pipe which allows us to interrupt mainloops
668 io_wait in case some data is available to send out */ 861 io_wait in case some data is available to send out */
669 if( pipe( g_self_pipe ) == -1 ) 862 if (pipe(g_self_pipe) == -1)
670 panic( "selfpipe failed: " ); 863 panic("selfpipe failed: ");
671 if( !io_fd( g_self_pipe[0] ) ) 864 if (!io_fd(g_self_pipe[0]))
672 panic( "selfpipe io_fd failed: " ); 865 panic("selfpipe io_fd failed: ");
673 if( !io_fd( g_self_pipe[1] ) ) 866 if (!io_fd(g_self_pipe[1]))
674 panic( "selfpipe io_fd failed: " ); 867 panic("selfpipe io_fd failed: ");
675 io_setcookie( g_self_pipe[0], (void*)FLAG_SELFPIPE ); 868 io_setcookie(g_self_pipe[0], (void *)FLAG_SELFPIPE);
676 io_wantread( g_self_pipe[0] ); 869 io_wantread(g_self_pipe[0]);
677 870
678 defaul_signal_handlers( );
679 /* Init all sub systems. This call may fail with an exit() */ 871 /* Init all sub systems. This call may fail with an exit() */
680 trackerlogic_init( ); 872 trackerlogic_init();
681 873
682 if( statefile ) 874#ifdef _DEBUG_RANDOMTORRENTS
683 load_state( statefile ); 875 fprintf(stderr, "DEBUG: Generating %d random peers on random torrents. This may take a while. (Setting RANDOMTORRENTS in trackerlogic.h)\n", RANDOMTORRENTS);
876 trackerlogic_add_random_torrents(RANDOMTORRENTS);
877 fprintf(stderr, "... done.\n");
878#endif
684 879
685 install_signal_handlers( ); 880 if (statefile)
881 load_state(statefile);
686 882
687 if( !g_udp_workers ) 883 install_signal_handlers();
688 udp_init( -1, 0 );
689 884
690 /* Kick off our initial clock setting alarm */ 885 if (!g_udp_workers)
691 alarm(5); 886 udp_init(-1, 0);
692 887
693 server_mainloop( 0 ); 888 server_mainloop(0);
694 889
695 return 0; 890 return 0;
696} 891}
697
698const char *g_version_opentracker_c = "$Source$: $Revision$\n";
diff --git a/opentracker.conf.sample b/opentracker.conf.sample
index db45122..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.
@@ -44,17 +44,65 @@
44# listing, so choose one of those options at compile time. File format 44# listing, so choose one of those options at compile time. File format
45# is straight forward: "<hex info hash>\n<hex info hash>\n..." 45# is straight forward: "<hex info hash>\n<hex info hash>\n..."
46# 46#
47# IIa) You can enable dynamic changesets to accesslists by enabling
48# WANT_DYNAMIC_ACCESSLIST.
49#
50# The suggested way to work with dynamic changeset lists is to keep a
51# main accesslist file that is loaded when opentracker (re)starts and
52# reloaded infrequently (hourly or daily).
53#
54# All changes to the accesslist (e.g. from a web frontend) should be
55# both appended to or removed from that file and sent to opentracker. By
56# keeping dynamic changeset lists, you can avoid reloading huge
57# accesslists whenever just a single entry is added or removed.
58#
59# Any info_hash (format see above) written to the fifo_add file will be
60# kept on a dynamic add-changeset, removed from the dynamic
61# delete-changeset and treated as if it was in the main accesslist file.
62# The semantic of the respective dynamic changeset depends on whether
63# WANT_ACCESSLIST_WHITE or WANT_ACCESSLIST_BLACK is enabled.
64#
65# access.fifo_add /var/run/opentracker/adder.fifo
66#
67# Any info_hash (format see above) written to the fifo_delete file will
68# be kept on a dynamic delete-changeset, removed from the dynamic
69# add-changeset and treated as if it was not in the main accesslist
70# file.
71#
72# access.fifo_delete /var/run/opentracker/deleter.fifo
73#
74# If you reload the accesslist by sending SIGHUP to the tracker process,
75# the dynamic lists are flushed, as opentracker assumes thoses lists are
76# merged into the main accesslist.
77#
78# NOTE: While you can have multiple writers sending lines to the fifos,
79# any writes larger than PIPE_BUF (see your limits.h, minimally 512
80# bytes but usually 4096) may be interleaved with data sent by other
81# writers. This can lead to unparsable lines of info_hashes.
82#
83# IIb)
47# 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
48# WANT_RESTRICT_STATS option in Makefile and bless the ip addresses 85# WANT_RESTRICT_STATS option in Makefile and bless the ip addresses
49# allowed to fetch stats here. 86# or network allowed to fetch stats here.
50# 87#
51# access.stats 192.168.0.23 88# access.stats 192.168.0.23
89# access.stats 10.1.1.23
52# 90#
53# 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
54# 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
55# appear anywhere on your tracker. 93# appear anywhere on your tracker.
56# 94#
57# access.stats_path stats 95# access.stats_path stats
96#
97# II
98# If opentracker lives behind one or multiple reverse proxies,
99# every http connection appears to come from these proxies. In order to
100# take the X-Forwarded-For address instead, compile opentracker with the
101# WANT_IP_FROM_PROXY option and set your proxy addresses or networkss here.
102#
103# access.proxy 10.0.1.23
104# access.proxy 192.0.0.0/8
105#
58 106
59# III) Live sync uses udp multicast packets to keep a cluster of opentrackers 107# III) Live sync uses udp multicast packets to keep a cluster of opentrackers
60# synchronized. This option tells opentracker which port to listen for 108# synchronized. This option tells opentracker which port to listen for
diff --git a/ot_accesslist.c b/ot_accesslist.c
index 3154e7f..2465ada 100644
--- a/ot_accesslist.c
+++ b/ot_accesslist.c
@@ -5,121 +5,240 @@
5 5
6/* System */ 6/* System */
7#include <pthread.h> 7#include <pthread.h>
8#include <signal.h>
9#include <stdio.h>
8#include <stdlib.h> 10#include <stdlib.h>
9#include <string.h> 11#include <string.h>
10#include <stdio.h>
11#include <signal.h>
12#include <unistd.h> 12#include <unistd.h>
13#if defined(WANT_ACCESSLIST_BLACK) || defined(WANT_ACCESSLIST_WHITE)
14#include <fcntl.h>
15#include <stdint.h>
16#include <sys/mman.h>
17#include <sys/stat.h>
18#include <sys/types.h>
19#endif
20#ifdef WANT_DYNAMIC_ACCESSLIST
21#include <errno.h>
22#endif
13 23
14/* Libowfat */ 24/* Libowfat */
15#include "byte.h"
16#include "scan.h"
17#include "ip6.h"
18#include "mmap.h"
19 25
20/* Opentracker */ 26/* Opentracker */
21#include "trackerlogic.h"
22#include "ot_accesslist.h" 27#include "ot_accesslist.h"
28#include "ot_hex.h"
29#include "ot_ip.h"
23#include "ot_vector.h" 30#include "ot_vector.h"
31#include "trackerlogic.h"
24 32
25/* GLOBAL VARIABLES */ 33/* GLOBAL VARIABLES */
26#ifdef WANT_ACCESSLIST 34#ifdef WANT_ACCESSLIST
27 char *g_accesslist_filename; 35char *g_accesslist_filename = NULL;
36#ifdef WANT_DYNAMIC_ACCESSLIST
37char *g_accesslist_pipe_add = NULL;
38char *g_accesslist_pipe_delete = NULL;
39#endif
28static pthread_mutex_t g_accesslist_mutex; 40static pthread_mutex_t g_accesslist_mutex;
29 41
30typedef struct { 42/* Accesslists are lock free linked lists. We can not make them locking, because every announce
31 ot_hash *list; 43 would try to acquire the mutex, making it the most contested mutex in the whole of opentracker,
32 size_t size; 44 basically creating a central performance choke point.
33} ot_accesslist;
34ot_accesslist * g_accesslist = NULL;
35ot_accesslist * g_accesslist_old = NULL;
36 45
37static int vector_compare_hash(const void *hash1, const void *hash2 ) { 46 The idea is that updating the list heads happens under the g_accesslist_mutex guard and is
38 return memcmp( hash1, hash2, OT_HASH_COMPARE_SIZE ); 47 done atomically, while consumers might potentially still hold pointers deeper inside the list.
39}
40 48
41/* Read initial access list */ 49 Consumers (for now only via accesslist_hashisvalid) will always fetch the list head pointer
42static void accesslist_readfile( void ) { 50 that is guaranteed to live for at least five minutes. This should be many orders of magnitudes
43 ot_accesslist * accesslist_new = malloc(sizeof(ot_accesslist)); 51 more than how long it will be needed by the bsearch done on the list. */
44 ot_hash *info_hash; 52struct ot_accesslist;
45 const char *map, *map_end, *read_offs; 53typedef struct ot_accesslist ot_accesslist;
46 size_t maplen; 54struct ot_accesslist {
55 ot_hash *list;
56 size_t size;
57 ot_time base;
58 ot_accesslist *next;
59};
60static ot_accesslist *_Atomic g_accesslist = NULL;
61#ifdef WANT_DYNAMIC_ACCESSLIST
62static ot_accesslist *_Atomic g_accesslist_add = NULL;
63static ot_accesslist *_Atomic g_accesslist_delete = NULL;
64#endif
47 65
48 if( ( map = mmap_read( g_accesslist_filename, &maplen ) ) == NULL ) { 66/* Helpers to work on access lists */
49 char *wd = getcwd( NULL, 0 ); 67static int vector_compare_hash(const void *hash1, const void *hash2) { return memcmp(hash1, hash2, OT_HASH_COMPARE_SIZE); }
50 fprintf( stderr, "Warning: Can't open accesslist file: %s (but will try to create it later, if necessary and possible).\nPWD: %s\n", g_accesslist_filename, wd ); 68
51 free( wd ); 69static ot_accesslist *accesslist_free(ot_accesslist *accesslist) {
52 return; 70 while (accesslist) {
71 ot_accesslist *this_accesslist = accesslist;
72 accesslist = this_accesslist->next;
73 free(this_accesslist->list);
74 free(this_accesslist);
53 } 75 }
76 return NULL;
77}
54 78
55 /* You need at least 41 bytes to pass an info_hash, make enough room 79static ot_accesslist *accesslist_make(ot_accesslist *next, size_t size) {
56 for the maximum amount of them */ 80 ot_accesslist *accesslist_new = malloc(sizeof(ot_accesslist));
57 accesslist_new->size = 0; 81 if (accesslist_new) {
58 info_hash = accesslist_new->list = malloc( ( maplen / 41 ) * 20 ); 82 accesslist_new->list = size ? malloc(sizeof(ot_hash) * size) : NULL;
59 if( !accesslist_new->list ) { 83 accesslist_new->size = size;
60 fprintf( stderr, "Warning: Not enough memory to allocate %zd bytes for accesslist buffer. May succeed later.\n", ( maplen / 41 ) * 20 ); 84 accesslist_new->base = 0;
61 mmap_unmap( map, maplen); 85 accesslist_new->next = next;
62 free(accesslist_new); 86 if (size && !accesslist_new->list) {
63 return; 87 free(accesslist_new);
88 accesslist_new = NULL;
89 }
64 } 90 }
91 return accesslist_new;
92}
65 93
66 /* No use to scan if there's not enough room for another full info_hash */ 94/* This must be called with g_accesslist_mutex held.
67 map_end = map + maplen - 40; 95 This will never delete head, because that might still be in use. */
68 read_offs = map; 96static void accesslist_clean(ot_accesslist *accesslist) {
97 while (accesslist && accesslist->next) {
98 if (accesslist->next->base + 5 < g_now_minutes)
99 accesslist->next = accesslist_free(accesslist->next);
100 accesslist = accesslist->next;
101 }
102}
69 103
70 /* We do ignore anything that is not of the form "^[:xdigit:]{40}[^:xdigit:].*" */ 104/* A hash needs forty bytes, and all but a final one are followed by at least
71 while( read_offs <= map_end ) { 105 one separator, so n hashes need at least 41n - 1 bytes. (len + 1) / 41 is
72 int i; 106 therefore an exact upper bound on what a buffer of len bytes can hold. */
73 for( i=0; i<(int)sizeof(ot_hash); ++i ) { 107size_t accesslist_parse_hashes(ot_hash *dest, size_t capacity, const char *buf, size_t len) {
74 int eger1 = scan_fromhex( read_offs[ 2*i ] ); 108 const char *end = buf + len;
75 int eger2 = scan_fromhex( read_offs[ 1 + 2*i ] ); 109 const char *offs = buf;
76 if( eger1 < 0 || eger2 < 0 ) 110 size_t count = 0;
77 break;
78 (*info_hash)[i] = eger1 * 16 + eger2;
79 }
80 111
81 if( i == sizeof(ot_hash) ) { 112 while (count < capacity && end - offs >= 40) {
82 read_offs += 40; 113 if (hex_tohash(dest[count], offs)) {
114 offs += 40;
83 115
84 /* Append accesslist to accesslist vector */ 116 if (offs == end || !hex_isdigit((unsigned char)*offs))
85 if( read_offs == map_end || scan_fromhex( *read_offs ) < 0 ) 117 ++count;
86 ++info_hash;
87 } 118 }
88 119
89 /* Find start of next line */ 120 /* Find start of next line */
90 while( read_offs <= map_end && *(read_offs++) != '\n' ); 121 while (offs < end && *(offs++) != '\n') {}
122 }
123
124 return count;
125}
126
127/* Map a file read only. Returns 0 and sets map and maplen on success, -1 if
128 the file cannot be opened or mapped. */
129static int accesslist_map(const char *filename, const char **map, size_t *maplen) {
130 struct stat st;
131 void *mapped;
132 int fd = open(filename, O_RDONLY);
133
134 if (fd < 0)
135 return -1;
136
137 if (fstat(fd, &st) < 0 || st.st_size < 0 || (uintmax_t)st.st_size > (uintmax_t)SIZE_MAX) {
138 close(fd);
139 return -1;
140 }
141
142 if (st.st_size == 0) {
143 close(fd);
144 *map = NULL;
145 *maplen = 0;
146 return 0;
147 }
148
149 mapped = mmap(NULL, (size_t)st.st_size, PROT_READ, MAP_SHARED, fd, 0);
150 close(fd);
151
152 if (mapped == MAP_FAILED)
153 return -1;
154
155 *map = mapped;
156 *maplen = (size_t)st.st_size;
157 return 0;
158}
159
160static void accesslist_readfile(void) {
161 ot_accesslist *accesslist_new;
162 const char *map;
163 size_t maplen, capacity, found;
164
165 if (accesslist_map(g_accesslist_filename, &map, &maplen) < 0) {
166 char *wd = getcwd(NULL, 0);
167 fprintf(stderr, "Warning: Can't open accesslist file: %s (but will try to create it later, if necessary and possible).\nPWD: %s\n", g_accesslist_filename, wd);
168 free(wd);
169 return;
170 }
171
172 /* Make room for the most info_hashes this file could possibly hold */
173 capacity = (maplen + 1) / 41;
174 accesslist_new = accesslist_make(g_accesslist, capacity);
175 if (!accesslist_new) {
176 fprintf(stderr, "Warning: Not enough memory to allocate %zd bytes for accesslist buffer. May succeed later.\n", capacity * sizeof(ot_hash));
177 if (map)
178 munmap((void *)map, maplen);
179 return;
91 } 180 }
181
182 found = maplen ? accesslist_parse_hashes(accesslist_new->list, capacity, map, maplen) : 0;
92#ifdef _DEBUG 183#ifdef _DEBUG
93 fprintf( stderr, "Added %zd info_hashes to accesslist\n", (size_t)(info_hash - accesslist_new->list) ); 184 fprintf(stderr, "Added %zd info_hashes to accesslist\n", found);
94#endif 185#endif
95 186
96 mmap_unmap( map, maplen); 187 if (map)
188 munmap((void *)map, maplen);
97 189
98 qsort( accesslist_new->list, info_hash - accesslist_new->list, sizeof( *info_hash ), vector_compare_hash ); 190 qsort(accesslist_new->list, found, sizeof(ot_hash), vector_compare_hash);
99 accesslist_new->size = info_hash - accesslist_new->list; 191 accesslist_new->size = found;
100 192
101 /* Now exchange the accesslist vector in the least race condition prone way */ 193 /* Now exchange the accesslist vector in the least race condition prone way */
102 pthread_mutex_lock(&g_accesslist_mutex); 194 pthread_mutex_lock(&g_accesslist_mutex);
103 195
104 if (g_accesslist_old) { 196 /* Mark old accesslist as "needs to live another five minutes" */
105 free(g_accesslist_old->list); 197 if (g_accesslist)
106 free(g_accesslist_old); 198 g_accesslist->base = g_now_minutes;
199 accesslist_new->next = g_accesslist;
200 g_accesslist = accesslist_new; /* Only now set a new list */
201
202#ifdef WANT_DYNAMIC_ACCESSLIST
203 /* If we have dynamic accesslists, reloading a new one will always void the add/delete lists.
204 Insert empty ones at the list head */
205 if (g_accesslist_add && (accesslist_new = accesslist_make(g_accesslist_add, 0)) != NULL) {
206 g_accesslist_add->base = g_now_minutes;
207 g_accesslist_add = accesslist_new;
208 }
209 if (g_accesslist_delete && (accesslist_new = accesslist_make(g_accesslist_delete, 0)) != NULL) {
210 g_accesslist_delete->base = g_now_minutes;
211 g_accesslist_delete = accesslist_new;
107 } 212 }
213#endif
108 214
109 g_accesslist_old = g_accesslist; /* Keep a copy for later free */ 215 accesslist_clean(g_accesslist);
110 g_accesslist = accesslist_new; /* Only now set a new list */
111 216
112 pthread_mutex_unlock(&g_accesslist_mutex); 217 pthread_mutex_unlock(&g_accesslist_mutex);
113} 218}
114 219
115int accesslist_hashisvalid( ot_hash hash ) { 220int accesslist_hashisvalid(const ot_hash hash) {
116 /* Get working copy of current access list */ 221 /* Get working copy of current access list */
117 ot_accesslist * accesslist = g_accesslist; 222 ot_accesslist *accesslist = g_accesslist;
118 223#ifdef WANT_DYNAMIC_ACCESSLIST
119 void * exactmatch = NULL; 224 ot_accesslist *accesslist_add, *accesslist_delete;
225#endif
226 void *exactmatch = NULL;
120 227
121 if (accesslist) 228 if (accesslist)
122 exactmatch = bsearch( hash, accesslist->list, accesslist->size, OT_HASH_COMPARE_SIZE, vector_compare_hash ); 229 exactmatch = bsearch(hash, accesslist->list, accesslist->size, OT_HASH_COMPARE_SIZE, vector_compare_hash);
230
231#ifdef WANT_DYNAMIC_ACCESSLIST
232 /* If we had no match on the main list, scan the list of dynamically added hashes */
233 accesslist_add = g_accesslist_add;
234 if ((exactmatch == NULL) && accesslist_add)
235 exactmatch = bsearch(hash, accesslist_add->list, accesslist_add->size, OT_HASH_COMPARE_SIZE, vector_compare_hash);
236
237 /* If we found a matching hash on the main list, scan the list of dynamically deleted hashes */
238 accesslist_delete = g_accesslist_delete;
239 if ((exactmatch != NULL) && accesslist_delete && bsearch(hash, accesslist_delete->list, accesslist_delete->size, OT_HASH_COMPARE_SIZE, vector_compare_hash))
240 exactmatch = NULL;
241#endif
123 242
124#ifdef WANT_ACCESSLIST_BLACK 243#ifdef WANT_ACCESSLIST_BLACK
125 return exactmatch == NULL; 244 return exactmatch == NULL;
@@ -128,79 +247,203 @@ int accesslist_hashisvalid( ot_hash hash ) {
128#endif 247#endif
129} 248}
130 249
131static void * accesslist_worker( void * args ) { 250static void *accesslist_worker(void *args) {
132 int sig; 251 int sig;
133 sigset_t signal_mask; 252 sigset_t signal_mask;
134 253
135 sigemptyset(&signal_mask); 254 sigemptyset(&signal_mask);
136 sigaddset(&signal_mask, SIGHUP); 255 sigaddset(&signal_mask, SIGHUP);
137 256
138 (void)args; 257 (void)args;
139 258
140 while( 1 ) { 259 while (1) {
260 if (!g_opentracker_running)
261 return NULL;
141 262
142 /* Initial attempt to read accesslist */ 263 /* Initial attempt to read accesslist */
143 accesslist_readfile( ); 264 accesslist_readfile();
144 265
145 /* Wait for signals */ 266 /* Wait for signals */
146 while( sigwait (&signal_mask, &sig) != 0 && sig != SIGHUP ); 267 while (sigwait(&signal_mask, &sig) != 0 && sig != SIGHUP)
268 ;
269 }
270 return NULL;
271}
272
273#ifdef WANT_DYNAMIC_ACCESSLIST
274static pthread_t thread_adder_id, thread_deleter_id;
275static void *accesslist_adddel_worker(char *fifoname, ot_accesslist *_Atomic *adding_to, ot_accesslist *_Atomic *removing_from) {
276 struct stat st;
277
278 if (!stat(fifoname, &st)) {
279 if (!S_ISFIFO(st.st_mode)) {
280 fprintf(stderr, "Error when starting dynamic accesslists: Found Non-FIFO file at %s.\nPlease remove it and restart opentracker.\n", fifoname);
281 return NULL;
282 }
283 } else {
284 int error = mkfifo(fifoname, 0755);
285 if (error && error != EEXIST) {
286 fprintf(stderr, "Error when starting dynamic accesslists: Couldn't create FIFO at %s, error: %s\n", fifoname, strerror(errno));
287 return NULL;
288 }
289 }
290
291 while (g_opentracker_running) {
292 FILE *fifo = fopen(fifoname, "r");
293 char *line = NULL;
294 size_t linecap = 0;
295 ssize_t linelen;
296
297 if (!fifo) {
298 fprintf(stderr, "Error when reading dynamic accesslists: Couldn't open FIFO at %s, error: %s\n", fifoname, strerror(errno));
299 return NULL;
300 }
301
302 while ((linelen = getline(&line, &linecap, fifo)) > 0) {
303 ot_hash info_hash;
304
305 printf("Got line %*s", (int)linelen, line);
306 /* We do ignore anything that is not of the form "^[:xdigit:]{40}[^:xdigit:].*"
307 If there's not enough characters for an info_hash in the line, skip it. */
308 if (linelen < 41)
309 continue;
310
311 if (!hex_tohash(info_hash, line))
312 continue;
313
314 /* From now on we modify g_accesslist_add and g_accesslist_delete, so prevent the
315 other worker threads from doing the same */
316 pthread_mutex_lock(&g_accesslist_mutex);
317
318 /* If the info hash is in the removing_from list, create a new head without that entry */
319 if (*removing_from && (*removing_from)->list) {
320 ot_hash *exactmatch = bsearch(info_hash, (*removing_from)->list, (*removing_from)->size, OT_HASH_COMPARE_SIZE, vector_compare_hash);
321 if (exactmatch) {
322 ptrdiff_t off = exactmatch - (*removing_from)->list;
323 ot_accesslist *accesslist_new = accesslist_make(*removing_from, (*removing_from)->size - 1);
324 if (accesslist_new) {
325 memcpy(accesslist_new->list, (*removing_from)->list, sizeof(ot_hash) * off);
326 memcpy(accesslist_new->list + off, (*removing_from)->list + off + 1, sizeof(ot_hash) * ((*removing_from)->size - off - 1));
327 (*removing_from)->base = g_now_minutes;
328 *removing_from = accesslist_new;
329 }
330 }
331 }
332
333 /* Simple case: there's no adding_to list yet, create one with one member */
334 if (!*adding_to) {
335 ot_accesslist *accesslist_new = accesslist_make(NULL, 1);
336 if (accesslist_new) {
337 memcpy(accesslist_new->list, info_hash, sizeof(ot_hash));
338 *adding_to = accesslist_new;
339 }
340 } else {
341 int exactmatch = 0;
342 ot_hash *insert_point = binary_search(info_hash, (*adding_to)->list, (*adding_to)->size, sizeof(ot_hash), OT_HASH_COMPARE_SIZE, &exactmatch);
343
344 /* Only if the info hash is not in the adding_to list, create a new head with that entry */
345 if (!exactmatch) {
346 ot_accesslist *accesslist_new = accesslist_make(*adding_to, (*adding_to)->size + 1);
347 ptrdiff_t off = insert_point - (*adding_to)->list;
348 if (accesslist_new) {
349 memcpy(accesslist_new->list, (*adding_to)->list, sizeof(ot_hash) * off);
350 memcpy(accesslist_new->list + off, info_hash, sizeof(info_hash));
351 memcpy(accesslist_new->list + off + 1, (*adding_to)->list + off, sizeof(ot_hash) * ((*adding_to)->size - off));
352 (*adding_to)->base = g_now_minutes;
353 *adding_to = accesslist_new;
354 }
355 }
356 }
357
358 pthread_mutex_unlock(&g_accesslist_mutex);
359 }
360
361 fclose(fifo);
147 } 362 }
148 return NULL; 363 return NULL;
149} 364}
150 365
366static void *accesslist_adder_worker(void *args) {
367 (void)args;
368 return accesslist_adddel_worker(g_accesslist_pipe_add, &g_accesslist_add, &g_accesslist_delete);
369}
370static void *accesslist_deleter_worker(void *args) {
371 (void)args;
372 return accesslist_adddel_worker(g_accesslist_pipe_delete, &g_accesslist_delete, &g_accesslist_add);
373}
374#endif
375
151static pthread_t thread_id; 376static pthread_t thread_id;
152void accesslist_init( ) { 377void accesslist_init(void) {
153 pthread_mutex_init(&g_accesslist_mutex, NULL); 378 pthread_mutex_init(&g_accesslist_mutex, NULL);
154 pthread_create( &thread_id, NULL, accesslist_worker, NULL ); 379 pthread_create(&thread_id, NULL, accesslist_worker, NULL);
380#ifdef WANT_DYNAMIC_ACCESSLIST
381 if (g_accesslist_pipe_add)
382 pthread_create(&thread_adder_id, NULL, accesslist_adder_worker, NULL);
383 if (g_accesslist_pipe_delete)
384 pthread_create(&thread_deleter_id, NULL, accesslist_deleter_worker, NULL);
385#endif
155} 386}
156 387
157void accesslist_deinit( void ) { 388void accesslist_deinit(void) {
158 pthread_cancel( thread_id ); 389 /* Wake up sleeping worker */
390 pthread_kill(thread_id, SIGHUP);
391
392 pthread_mutex_lock(&g_accesslist_mutex);
393
394 g_accesslist = accesslist_free(g_accesslist);
395
396#ifdef WANT_DYNAMIC_ACCESSLIST
397 g_accesslist_add = accesslist_free(g_accesslist_add);
398 g_accesslist_delete = accesslist_free(g_accesslist_delete);
399#endif
400
401 pthread_mutex_unlock(&g_accesslist_mutex);
402 pthread_cancel(thread_id);
159 pthread_mutex_destroy(&g_accesslist_mutex); 403 pthread_mutex_destroy(&g_accesslist_mutex);
404}
160 405
161 if (g_accesslist_old) { 406void accesslist_cleanup(void) {
162 free(g_accesslist_old->list); 407 pthread_mutex_lock(&g_accesslist_mutex);
163 free(g_accesslist_old);
164 g_accesslist_old = 0;
165 }
166 408
167 if (g_accesslist) { 409 accesslist_clean(g_accesslist);
168 free(g_accesslist->list); 410#if WANT_DYNAMIC_ACCESSLIST
169 free(g_accesslist); 411 accesslist_clean(g_accesslist_add);
170 g_accesslist = 0; 412 accesslist_clean(g_accesslist_delete);
171 } 413#endif
414
415 pthread_mutex_unlock(&g_accesslist_mutex);
172} 416}
173#endif 417#endif
174 418
175int address_in_net( const ot_ip6 address, const ot_net *net ) { 419int address_in_net(const ot_ip6 address, const ot_net *net) {
176 int bits = net->bits; 420 int bits = net->bits, checkbits = (0x7f00 >> (bits & 7));
177 int result = memcmp( address, &net->address, bits >> 3 ); 421 int result = memcmp(address, &net->address, bits >> 3);
178 if( !result && ( bits & 7 ) ) 422 if (!result && (bits & 7))
179 result = ( ( 0x7f00 >> ( bits & 7 ) ) & address[bits>>3] ) - net->address[bits>>3]; 423 result = (checkbits & address[bits >> 3]) - (checkbits & net->address[bits >> 3]);
180 return result == 0; 424 return result == 0;
181} 425}
182 426
183void *set_value_for_net( const ot_net *net, ot_vector *vector, const void *value, const size_t member_size ) { 427void *set_value_for_net(const ot_net *net, ot_vector *vector, const void *value, const size_t member_size) {
184 size_t i; 428 size_t i;
185 int exactmatch; 429 int exactmatch;
186 430
187 /* Caller must have a concept of ot_net in it's member */ 431 /* Caller must have a concept of ot_net in it's member */
188 if( member_size < sizeof(ot_net) ) 432 if (member_size < sizeof(ot_net))
189 return 0; 433 return 0;
190 434
191 /* Check each net in vector for overlap */ 435 /* Check each net in vector for overlap */
192 uint8_t *member = ((uint8_t*)vector->data); 436 uint8_t *member = ((uint8_t *)vector->data);
193 for( i=0; i<vector->size; ++i ) { 437 for (i = 0; i < vector->size; ++i) {
194 if( address_in_net( *(ot_ip6*)member, net ) || 438 if (address_in_net(*(ot_ip6 *)member, net) || address_in_net(net->address, (ot_net *)member))
195 address_in_net( net->address, (ot_net*)member ) )
196 return 0; 439 return 0;
197 member += member_size; 440 member += member_size;
198 } 441 }
199 442
200 member = vector_find_or_insert( vector, (void*)net, member_size, sizeof(ot_net), &exactmatch ); 443 member = vector_find_or_insert(vector, (void *)net, member_size, sizeof(ot_net), &exactmatch);
201 if( member ) { 444 if (member) {
202 memcpy( member, net, sizeof(ot_net)); 445 memcpy(member, net, sizeof(ot_net));
203 memcpy( member + sizeof(ot_net), value, member_size - sizeof(ot_net)); 446 memcpy(member + sizeof(ot_net), value, member_size - sizeof(ot_net));
204 } 447 }
205 448
206 return member; 449 return member;
@@ -208,43 +451,43 @@ void *set_value_for_net( const ot_net *net, ot_vector *vector, const void *value
208 451
209/* Takes a vector filled with { ot_net net, uint8_t[x] value }; 452/* Takes a vector filled with { ot_net net, uint8_t[x] value };
210 Returns value associated with the net, or NULL if not found */ 453 Returns value associated with the net, or NULL if not found */
211void *get_value_for_net( const ot_ip6 address, const ot_vector *vector, const size_t member_size ) { 454void *get_value_for_net(const ot_ip6 address, const ot_vector *vector, const size_t member_size) {
212 int exactmatch; 455 int exactmatch;
213 /* This binary search will return a pointer to the first non-containing network... */ 456 /* This binary search will return a pointer to the first non-containing network... */
214 ot_net *net = binary_search( address, vector->data, vector->size, member_size, sizeof(ot_ip6), &exactmatch ); 457 ot_net *net = binary_search(address, vector->data, vector->size, member_size, sizeof(ot_ip6), &exactmatch);
215 if( !net ) 458 if (!net)
216 return NULL; 459 return NULL;
217 /* ... so we'll need to move back one step unless we've exactly hit the first address in network */ 460 /* ... so we'll need to move back one step unless we've exactly hit the first address in network */
218 if( !exactmatch && ( (void*)net > vector->data ) ) 461 if (!exactmatch && ((void *)net > vector->data))
219 --net; 462 --net;
220 if( !address_in_net( address, net ) ) 463 if (!address_in_net(address, net))
221 return NULL; 464 return NULL;
222 return (void*)net; 465 return (void *)net;
223} 466}
224 467
225#ifdef WANT_FULLLOG_NETWORKS 468#ifdef WANT_FULLLOG_NETWORKS
226static ot_vector g_lognets_list; 469static ot_vector g_lognets_list;
227ot_log *g_logchain_first, *g_logchain_last; 470ot_log *g_logchain_first, *g_logchain_last;
228
229static pthread_mutex_t g_lognets_list_mutex = PTHREAD_MUTEX_INITIALIZER; 471static pthread_mutex_t g_lognets_list_mutex = PTHREAD_MUTEX_INITIALIZER;
230void loglist_add_network( const ot_net *net ) { 472
473void loglist_add_network(const ot_net *net) {
231 pthread_mutex_lock(&g_lognets_list_mutex); 474 pthread_mutex_lock(&g_lognets_list_mutex);
232 set_value_for_net( net, &g_lognets_list, NULL, sizeof(ot_net)); 475 set_value_for_net(net, &g_lognets_list, NULL, sizeof(ot_net));
233 pthread_mutex_unlock(&g_lognets_list_mutex); 476 pthread_mutex_unlock(&g_lognets_list_mutex);
234} 477}
235 478
236void loglist_reset( ) { 479void loglist_reset(void) {
237 pthread_mutex_lock(&g_lognets_list_mutex); 480 pthread_mutex_lock(&g_lognets_list_mutex);
238 free( g_lognets_list.data ); 481 free(g_lognets_list.data);
239 g_lognets_list.data = 0; 482 g_lognets_list.data = 0;
240 g_lognets_list.size = g_lognets_list.space = 0; 483 g_lognets_list.size = g_lognets_list.space = 0;
241 pthread_mutex_unlock(&g_lognets_list_mutex); 484 pthread_mutex_unlock(&g_lognets_list_mutex);
242} 485}
243 486
244int loglist_check_address( const ot_ip6 address ) { 487int loglist_check_address(const ot_ip6 address) {
245 int result; 488 int result;
246 pthread_mutex_lock(&g_lognets_list_mutex); 489 pthread_mutex_lock(&g_lognets_list_mutex);
247 result = ( NULL != get_value_for_net( address, &g_lognets_list, sizeof(ot_net)) ); 490 result = (NULL != get_value_for_net(address, &g_lognets_list, sizeof(ot_net)));
248 pthread_mutex_unlock(&g_lognets_list_mutex); 491 pthread_mutex_unlock(&g_lognets_list_mutex);
249 return result; 492 return result;
250} 493}
@@ -252,44 +495,44 @@ int loglist_check_address( const ot_ip6 address ) {
252 495
253#ifdef WANT_IP_FROM_PROXY 496#ifdef WANT_IP_FROM_PROXY
254typedef struct { 497typedef struct {
255 ot_net *proxy; 498 ot_net *proxy;
256 ot_vector networks; 499 ot_vector networks;
257} ot_proxymap; 500} ot_proxymap;
258 501
259static ot_vector g_proxies_list; 502static ot_vector g_proxies_list;
260static pthread_mutex_t g_proxies_list_mutex = PTHREAD_MUTEX_INITIALIZER; 503static pthread_mutex_t g_proxies_list_mutex = PTHREAD_MUTEX_INITIALIZER;
261 504
262int proxylist_add_network( const ot_net *proxy, const ot_net *net ) { 505int proxylist_add_network(const ot_net *proxy, const ot_net *net) {
263 ot_proxymap *map; 506 ot_proxymap *map;
264 int exactmatch, result = 1; 507 int exactmatch, result = 1;
265 pthread_mutex_lock(&g_proxies_list_mutex); 508 pthread_mutex_lock(&g_proxies_list_mutex);
266 509
267 /* If we have a direct hit, use and extend the vector there */ 510 /* If we have a direct hit, use and extend the vector there */
268 map = binary_search( proxy, g_proxies_list.data, g_proxies_list.size, sizeof(ot_proxymap), sizeof(ot_net), &exactmatch ); 511 map = binary_search(proxy, g_proxies_list.data, g_proxies_list.size, sizeof(ot_proxymap), sizeof(ot_net), &exactmatch);
269 512
270 if( !map || !exactmatch ) { 513 if (!map || !exactmatch) {
271 /* else see, if we've got overlapping networks 514 /* else see, if we've got overlapping networks
272 and get a new empty vector if not */ 515 and get a new empty vector if not */
273 ot_vector empty; 516 ot_vector empty;
274 memset( &empty, 0, sizeof( ot_vector ) ); 517 memset(&empty, 0, sizeof(ot_vector));
275 map = set_value_for_net( proxy, &g_proxies_list, &empty, sizeof(ot_proxymap)); 518 map = set_value_for_net(proxy, &g_proxies_list, &empty, sizeof(ot_proxymap));
276 } 519 }
277 520
278 if( map && set_value_for_net( net, &map->networks, NULL, sizeof(ot_net) ) ) 521 if (map && set_value_for_net(net, &map->networks, NULL, sizeof(ot_net)))
279 result = 1; 522 result = 1;
280 523
281 pthread_mutex_unlock(&g_proxies_list_mutex); 524 pthread_mutex_unlock(&g_proxies_list_mutex);
282 return result; 525 return result;
283} 526}
284 527
285int proxylist_check_proxy( const ot_ip6 proxy, const ot_ip6 address ) { 528int proxylist_check_proxy(const ot_ip6 proxy, const ot_ip6 address) {
286 int result = 0; 529 int result = 0;
287 ot_proxymap *map; 530 ot_proxymap *map;
288 531
289 pthread_mutex_lock(&g_proxies_list_mutex); 532 pthread_mutex_lock(&g_proxies_list_mutex);
290 533
291 if( ( map = get_value_for_net( proxy, &g_proxies_list, sizeof(ot_proxymap) ) ) ) 534 if ((map = get_value_for_net(proxy, &g_proxies_list, sizeof(ot_proxymap))))
292 if( !address || get_value_for_net( address, &map->networks, sizeof(ot_net) ) ) 535 if (!address || get_value_for_net(address, &map->networks, sizeof(ot_net)))
293 result = 1; 536 result = 1;
294 537
295 pthread_mutex_unlock(&g_proxies_list_mutex); 538 pthread_mutex_unlock(&g_proxies_list_mutex);
@@ -298,42 +541,53 @@ int proxylist_check_proxy( const ot_ip6 proxy, const ot_ip6 address ) {
298 541
299#endif 542#endif
300 543
301static ot_ip6 g_adminip_addresses[OT_ADMINIP_MAX]; 544static ot_net g_admin_nets[OT_ADMINIP_MAX];
302static ot_permissions g_adminip_permissions[OT_ADMINIP_MAX]; 545static ot_permissions g_admin_nets_permissions[OT_ADMINIP_MAX];
303static unsigned int g_adminip_count = 0; 546static size_t g_admin_nets_count = 0;
304 547
305int accesslist_blessip( ot_ip6 ip, ot_permissions permissions ) { 548int accesslist_bless_net(ot_net *net, ot_permissions permissions) {
306 if( g_adminip_count >= OT_ADMINIP_MAX ) 549 if (g_admin_nets_count >= OT_ADMINIP_MAX)
307 return -1; 550 return -1;
308 551
309 memcpy(g_adminip_addresses + g_adminip_count,ip,sizeof(ot_ip6)); 552 memcpy(g_admin_nets + g_admin_nets_count, net, sizeof(ot_net));
310 g_adminip_permissions[ g_adminip_count++ ] = permissions; 553 g_admin_nets_permissions[g_admin_nets_count++] = permissions;
311 554
312#ifdef _DEBUG 555#ifdef _DEBUG
313 { 556 {
314 char _debug[512]; 557 char _debug[512];
315 int off = snprintf( _debug, sizeof(_debug), "Blessing ip address " ); 558 int off = snprintf(_debug, sizeof(_debug), "Blessing ip net ");
316 off += fmt_ip6c(_debug+off, ip ); 559 off += ip_format(_debug + off, net->address);
560 if (net->bits < 128) {
561 _debug[off++] = '/';
562 if (ip_is_v4mapped(net->address))
563 off += snprintf(_debug + off, sizeof(_debug) - off, "%d", net->bits - 96);
564 else
565 off += snprintf(_debug + off, sizeof(_debug) - off, "%d", net->bits);
566 }
317 567
318 if( permissions & OT_PERMISSION_MAY_STAT ) off += snprintf( _debug+off, 512-off, " may_fetch_stats" ); 568 if (permissions & OT_PERMISSION_MAY_STAT)
319 if( permissions & OT_PERMISSION_MAY_LIVESYNC ) off += snprintf( _debug+off, 512-off, " may_sync_live" ); 569 off += snprintf(_debug + off, 512 - off, " may_fetch_stats");
320 if( permissions & OT_PERMISSION_MAY_FULLSCRAPE ) off += snprintf( _debug+off, 512-off, " may_fetch_fullscrapes" ); 570 if (permissions & OT_PERMISSION_MAY_LIVESYNC)
321 if( permissions & OT_PERMISSION_MAY_PROXY ) off += snprintf( _debug+off, 512-off, " may_proxy" ); 571 off += snprintf(_debug + off, 512 - off, " may_sync_live");
322 if( !permissions ) off += snprintf( _debug+off, sizeof(_debug)-off, " nothing\n" ); 572 if (permissions & OT_PERMISSION_MAY_FULLSCRAPE)
573 off += snprintf(_debug + off, 512 - off, " may_fetch_fullscrapes");
574 if (permissions & OT_PERMISSION_MAY_PROXY)
575 off += snprintf(_debug + off, 512 - off, " may_proxy");
576 if (!permissions)
577 off += snprintf(_debug + off, sizeof(_debug) - off, " nothing");
323 _debug[off++] = '.'; 578 _debug[off++] = '.';
324 write( 2, _debug, off ); 579 _debug[off++] = '\n';
580 (void)write(2, _debug, off);
325 } 581 }
326#endif 582#endif
327 583
328 return 0; 584 return 0;
329} 585}
330 586
331int accesslist_isblessed( ot_ip6 ip, ot_permissions permissions ) { 587int accesslist_is_blessed(ot_ip6 ip, ot_permissions permissions) {
332 unsigned int i; 588 size_t i;
333 for( i=0; i<g_adminip_count; ++i ) 589 for (i = 0; i < g_admin_nets_count; ++i)
334 if( !memcmp( g_adminip_addresses + i, ip, sizeof(ot_ip6)) && ( g_adminip_permissions[ i ] & permissions ) ) 590 if (address_in_net(ip, g_admin_nets + i) && (g_admin_nets_permissions[i] & permissions))
335 return 1; 591 return 1;
336 return 0; 592 return 0;
337} 593}
338
339const char *g_version_accesslist_c = "$Source$: $Revision$\n";
diff --git a/ot_accesslist.h b/ot_accesslist.h
index b38b91a..30aaebd 100644
--- a/ot_accesslist.h
+++ b/ot_accesslist.h
@@ -6,26 +6,41 @@
6#ifndef OT_ACCESSLIST_H__ 6#ifndef OT_ACCESSLIST_H__
7#define OT_ACCESSLIST_H__ 7#define OT_ACCESSLIST_H__
8 8
9#if defined ( WANT_ACCESSLIST_BLACK ) && defined (WANT_ACCESSLIST_WHITE ) 9#include "trackerlogic.h"
10# error WANT_ACCESSLIST_BLACK and WANT_ACCESSLIST_WHITE are exclusive. 10
11#if defined(WANT_ACCESSLIST_BLACK) && defined(WANT_ACCESSLIST_WHITE)
12#error WANT_ACCESSLIST_BLACK and WANT_ACCESSLIST_WHITE are exclusive.
11#endif 13#endif
12 14
13#if defined ( WANT_ACCESSLIST_BLACK ) || defined (WANT_ACCESSLIST_WHITE ) 15#if defined(WANT_ACCESSLIST_BLACK) || defined(WANT_ACCESSLIST_WHITE)
14#define WANT_ACCESSLIST 16#define WANT_ACCESSLIST
15void accesslist_init( ); 17void accesslist_init(void);
16void accesslist_deinit( ); 18void accesslist_deinit(void);
17int accesslist_hashisvalid( ot_hash hash ); 19int accesslist_hashisvalid(const ot_hash hash);
20void accesslist_cleanup(void);
21
22/* Scan buf for up to capacity info_hashes in hexadecimal notation, one per
23 line, and return how many were written to dest */
24size_t accesslist_parse_hashes(ot_hash *dest, size_t capacity, const char *buf, size_t len);
18 25
19extern char *g_accesslist_filename; 26extern char *g_accesslist_filename;
27#ifdef WANT_DYNAMIC_ACCESSLIST
28extern char *g_accesslist_pipe_add;
29extern char *g_accesslist_pipe_delete;
30#endif
20 31
21#else 32#else
22#define accesslist_init( accesslist_filename ) 33#ifdef WANT_DYNAMIC_ACCESSLIST
23#define accesslist_deinit( ) 34#error WANT_DYNAMIC_ACCESSLIST needs either WANT_ACCESSLIST_BLACK or WANT_ACCESSLIST_WHITE
24#define accesslist_hashisvalid( hash ) 1 35#endif
36
37#define accesslist_init(accesslist_filename)
38#define accesslist_deinit()
39#define accesslist_hashisvalid(hash) 1
25#endif 40#endif
26 41
27/* Test if an address is subset of an ot_net, return value is considered a bool */ 42/* Test if an address is subset of an ot_net, return value is considered a bool */
28int address_in_net( const ot_ip6 address, const ot_net *net ); 43int address_in_net(const ot_ip6 address, const ot_net *net);
29 44
30/* Store a value into a vector of struct { ot_net net, uint8_t[x] value } member; 45/* Store a value into a vector of struct { ot_net net, uint8_t[x] value } member;
31 returns NULL 46 returns NULL
@@ -36,18 +51,17 @@ int address_in_net( const ot_ip6 address, const ot_net *net );
36 returns pointer to new member in vector for success 51 returns pointer to new member in vector for success
37 member_size can be sizeof(ot_net) to reduce the lookup to a boolean mapping 52 member_size can be sizeof(ot_net) to reduce the lookup to a boolean mapping
38*/ 53*/
39void *set_value_for_net( const ot_net *net, ot_vector *vector, const void *value, const size_t member_size ); 54void *set_value_for_net(const ot_net *net, ot_vector *vector, const void *value, const size_t member_size);
40 55
41/* Takes a vector filled with struct { ot_net net, uint8_t[x] value } member; 56/* Takes a vector filled with struct { ot_net net, uint8_t[x] value } member;
42 Returns pointer to _member_ associated with the net, or NULL if not found 57 Returns pointer to _member_ associated with the net, or NULL if not found
43 member_size can be sizeof(ot_net) to reduce the lookup to a boolean mapping 58 member_size can be sizeof(ot_net) to reduce the lookup to a boolean mapping
44*/ 59*/
45void *get_value_for_net( const ot_ip6 address, const ot_vector *vector, const size_t member_size ); 60void *get_value_for_net(const ot_ip6 address, const ot_vector *vector, const size_t member_size);
46
47 61
48#ifdef WANT_IP_FROM_PROXY 62#ifdef WANT_IP_FROM_PROXY
49int proxylist_add_network( const ot_net *proxy, const ot_net *net ); 63int proxylist_add_network(const ot_net *proxy, const ot_net *net);
50int proxylist_check_network( const ot_ip6 *proxy, const ot_ip6 address /* can be NULL to only check proxy */ ); 64int proxylist_check_network(const ot_ip6 *proxy, const ot_ip6 address /* can be NULL to only check proxy */);
51#endif 65#endif
52 66
53#ifdef WANT_FULLLOG_NETWORKS 67#ifdef WANT_FULLLOG_NETWORKS
@@ -61,10 +75,10 @@ struct ot_log {
61}; 75};
62extern ot_log *g_logchain_first, *g_logchain_last; 76extern ot_log *g_logchain_first, *g_logchain_last;
63 77
64void loglist_add_network( const ot_net *net ); 78void loglist_add_network(const ot_net *net);
65void loglist_reset( ); 79void loglist_reset(void);
66int loglist_check_address( const ot_ip6 address ); 80int loglist_check_address(const ot_ip6 address);
67#endif 81#endif
68 82
69typedef enum { 83typedef enum {
70 OT_PERMISSION_MAY_FULLSCRAPE = 0x1, 84 OT_PERMISSION_MAY_FULLSCRAPE = 0x1,
@@ -73,7 +87,7 @@ typedef enum {
73 OT_PERMISSION_MAY_PROXY = 0x8 87 OT_PERMISSION_MAY_PROXY = 0x8
74} ot_permissions; 88} ot_permissions;
75 89
76int accesslist_blessip( ot_ip6 ip, ot_permissions permissions ); 90int accesslist_bless_net(ot_net *net, ot_permissions permissions);
77int accesslist_isblessed( ot_ip6 ip, ot_permissions permissions ); 91int accesslist_is_blessed(ot_ip6 ip, ot_permissions permissions);
78 92
79#endif 93#endif
diff --git a/ot_buf.c b/ot_buf.c
new file mode 100644
index 0000000..eb00edf
--- /dev/null
+++ b/ot_buf.c
@@ -0,0 +1,41 @@
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_buf.h"
12
13int buf_cat(ot_buf *buf, const void *src, size_t len) {
14 char *data;
15
16 if (!len)
17 return 0;
18
19 if (buf->size + len < buf->size)
20 return -1;
21
22 if (!(data = realloc(buf->data, buf->size + len)))
23 return -1;
24
25 buf->data = data;
26 memcpy(data + buf->size, src, len);
27 buf->size += len;
28
29 return 0;
30}
31
32void buf_truncate(ot_buf *buf, size_t len) {
33 if (len < buf->size)
34 buf->size = len;
35}
36
37void buf_reset(ot_buf *buf) {
38 free(buf->data);
39 buf->data = NULL;
40 buf->size = 0;
41}
diff --git a/ot_buf.h b/ot_buf.h
new file mode 100644
index 0000000..219a069
--- /dev/null
+++ b/ot_buf.h
@@ -0,0 +1,27 @@
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_BUF_H__
7#define OT_BUF_H__
8
9#include <stddef.h>
10
11/* A growable byte buffer, holding an HTTP request that did not arrive in a
12 single packet. Requests that arrive whole are served straight from the
13 worker's input buffer and never come near this. */
14typedef struct {
15 char *data;
16 size_t size;
17} ot_buf;
18
19/* Append len bytes from src. Returns 0, or -1 if the memory could not be
20 had, in which case the buffer keeps whatever it already held. */
21int buf_cat(ot_buf *buf, const void *src, size_t len);
22
23void buf_truncate(ot_buf *buf, size_t len);
24
25void buf_reset(ot_buf *buf);
26
27#endif
diff --git a/ot_clean.c b/ot_clean.c
index 4c03416..825290d 100644
--- a/ot_clean.c
+++ b/ot_clean.c
@@ -5,89 +5,91 @@
5 5
6/* System */ 6/* System */
7#include <pthread.h> 7#include <pthread.h>
8#include <unistd.h>
9#include <string.h> 8#include <string.h>
9#include <unistd.h>
10 10
11/* Libowfat */ 11/* Libowfat */
12#include "io.h" 12#include "io.h"
13 13
14/* Opentracker */ 14/* Opentracker */
15#include "trackerlogic.h" 15#include "ot_accesslist.h"
16#include "ot_mutex.h"
17#include "ot_vector.h"
18#include "ot_clean.h" 16#include "ot_clean.h"
17#include "ot_mutex.h"
19#include "ot_stats.h" 18#include "ot_stats.h"
19#include "ot_vector.h"
20#include "trackerlogic.h"
20 21
21/* Returns amount of removed peers */ 22/* Returns amount of removed peers */
22static ssize_t clean_single_bucket( ot_peer *peers, size_t peer_count, time_t timedout, int *removed_seeders ) { 23static ssize_t clean_single_bucket(ot_peer *peers, size_t peer_count, size_t peer_size, time_t timedout, size_t *removed_seeders) {
23 ot_peer *last_peer = peers + peer_count, *insert_point; 24 ot_peer *last_peer = peers + peer_count * peer_size, *insert_point;
24 time_t timediff;
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 */
27 while( peers < last_peer ) { 27 while (peers < last_peer) {
28 if( ( timediff = timedout + OT_PEERTIME( peers ) ) >= OT_PEER_TIMEOUT ) 28 time_t timediff = timedout + OT_PEERTIME(peers, peer_size);
29 if (timediff >= OT_PEER_TIMEOUT)
29 break; 30 break;
30 OT_PEERTIME( peers++ ) = timediff; 31 OT_PEERTIME(peers, peer_size) = timediff;
32 peers += peer_size;
31 } 33 }
32 34
33 /* If we at least remove one peer, we have to copy */ 35 /* If we at least remove one peer, we have to copy */
34 insert_point = peers; 36 for (insert_point = peers; peers < last_peer; peers += peer_size) {
35 while( peers < last_peer ) 37 time_t timediff = timedout + OT_PEERTIME(peers, peer_size);
36 if( ( timediff = timedout + OT_PEERTIME( peers ) ) < OT_PEER_TIMEOUT ) {
37 OT_PEERTIME( peers ) = timediff;
38 memcpy( insert_point++, peers++, sizeof(ot_peer));
39 } else
40 if( OT_PEERFLAG( peers++ ) & PEER_FLAG_SEEDING )
41 (*removed_seeders)++;
42 38
43 return peers - insert_point; 39 if (timediff < OT_PEER_TIMEOUT) {
40 OT_PEERTIME(peers, peer_size) = timediff;
41 memcpy(insert_point, peers, peer_size);
42 insert_point += peer_size;
43 } else if (OT_PEERFLAG_D(peers, peer_size) & PEER_FLAG_SEEDING)
44 (*removed_seeders)++;
45 }
46
47 return (peers - insert_point) / peer_size;
44} 48}
45 49
46/* Clean a single torrent 50int clean_single_peer_list(ot_peerlist *peer_list, size_t peer_size) {
47 return 1 if torrent timed out 51 ot_vector *peer_vector = &peer_list->peers;
48*/ 52 time_t timedout = (time_t)(g_now_minutes - peer_list->base);
49int clean_single_torrent( ot_torrent *torrent ) { 53 size_t num_buckets = 1, removed_seeders = 0;
50 ot_peerlist *peer_list = torrent->peer_list;
51 ot_vector *bucket_list = &peer_list->peers;
52 time_t timedout = (time_t)( g_now_minutes - peer_list->base );
53 int 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)
57 return 0; 57 return 0;
58 58
59 /* Torrent has idled out */ 59 /* Torrent has idled out */
60 if( timedout > OT_TORRENT_TIMEOUT ) 60 if (timedout > OT_TORRENT_TIMEOUT)
61 return 1; 61 return 1;
62 62
63 /* Nothing to be cleaned here? Test if torrent is worth keeping */ 63 /* Nothing to be cleaned here? Test if torrent is worth keeping */
64 if( timedout > OT_PEER_TIMEOUT ) { 64 if (timedout > OT_PEER_TIMEOUT) {
65 if( !peer_list->peer_count ) 65 if (!peer_list->peer_count)
66 return peer_list->down_count ? 0 : 1; 66 return peer_list->down_count ? 0 : 1;
67 timedout = OT_PEER_TIMEOUT; 67 timedout = OT_PEER_TIMEOUT;
68 } 68 }
69 69
70 if( OT_PEERLIST_HASBUCKETS( peer_list ) ) { 70 if (OT_PEERLIST_HASBUCKETS(peer_list)) {
71 num_buckets = bucket_list->size; 71 num_buckets = peer_vector->size;
72 bucket_list = (ot_vector *)bucket_list->data; 72 peer_vector = (ot_vector *)peer_vector->data;
73 } 73 }
74 74
75 while( num_buckets-- ) { 75 while (num_buckets--) {
76 size_t removed_peers = clean_single_bucket( bucket_list->data, bucket_list->size, timedout, &removed_seeders ); 76 size_t removed_peers = clean_single_bucket(peer_vector->data, peer_vector->size, peer_size, timedout, &removed_seeders);
77 peer_list->peer_count -= removed_peers; 77 peer_list->peer_count -= removed_peers;
78 bucket_list->size -= removed_peers; 78 peer_vector->size -= removed_peers;
79 if( bucket_list->size < removed_peers ) 79 if (removed_peers)
80 vector_fixup_peers( bucket_list ); 80 vector_fixup_peers(peer_vector, peer_size);
81 ++bucket_list; 81
82 /* Skip to next bucket, a vector containing peers */
83 ++peer_vector;
82 } 84 }
83 85
84 peer_list->seed_count -= removed_seeders; 86 peer_list->seed_count -= removed_seeders;
85 87
86 /* See, if we need to convert a torrent from simple vector to bucket list */ 88 /* See if we need to convert a torrent from simple vector to bucket list */
87 if( ( peer_list->peer_count > OT_PEER_BUCKET_MINCOUNT ) || OT_PEERLIST_HASBUCKETS(peer_list) ) 89 if ((peer_list->peer_count > OT_PEER_BUCKET_MINCOUNT) || OT_PEERLIST_HASBUCKETS(peer_list))
88 vector_redistribute_buckets( peer_list ); 90 vector_redistribute_buckets(peer_list, peer_size);
89 91
90 if( peer_list->peer_count ) 92 if (peer_list->peer_count)
91 peer_list->base = g_now_minutes; 93 peer_list->base = g_now_minutes;
92 else { 94 else {
93 /* When we got here, the last time that torrent 95 /* When we got here, the last time that torrent
@@ -95,45 +97,48 @@ int clean_single_torrent( ot_torrent *torrent ) {
95 peer_list->base = g_now_minutes - OT_PEER_TIMEOUT; 97 peer_list->base = g_now_minutes - OT_PEER_TIMEOUT;
96 } 98 }
97 return 0; 99 return 0;
100}
98 101
102/* Clean a single torrent
103 return 1 if torrent timed out
104*/
105int clean_single_torrent(ot_torrent *torrent) {
106 return clean_single_peer_list(torrent->peer_list6, OT_PEER_SIZE6) * clean_single_peer_list(torrent->peer_list4, OT_PEER_SIZE4);
99} 107}
100 108
101/* Clean up all peers in current bucket, remove timedout pools and 109/* Clean up all peers in current bucket, remove timedout pools and
102 torrents */ 110 torrents */
103static void * clean_worker( void * args ) { 111static void *clean_worker(void *args) {
104 (void) args; 112 (void)args;
105 while( 1 ) { 113 while (1) {
106 int bucket = OT_BUCKET_COUNT; 114 size_t bucket = OT_BUCKET_COUNT;
107 while( bucket-- ) { 115 while (bucket--) {
108 ot_vector *torrents_list = mutex_bucket_lock( bucket ); 116 ot_vector *torrents_list = mutex_bucket_lock(bucket);
109 size_t toffs; 117 size_t toffs;
110 int delta_torrentcount = 0; 118 int delta_torrentcount = 0;
111 119
112 for( toffs=0; toffs<torrents_list->size; ++toffs ) { 120 for (toffs = 0; toffs < torrents_list->size; ++toffs) {
113 ot_torrent *torrent = ((ot_torrent*)(torrents_list->data)) + toffs; 121 ot_torrent *torrent = ((ot_torrent *)(torrents_list->data)) + toffs;
114 if( clean_single_torrent( torrent ) ) { 122 if (clean_single_torrent(torrent)) {
115 vector_remove_torrent( torrents_list, torrent ); 123 vector_remove_torrent(torrents_list, torrent);
116 --delta_torrentcount; 124 --delta_torrentcount;
117 --toffs; 125 --toffs;
118 } 126 }
119 } 127 }
120 mutex_bucket_unlock( bucket, delta_torrentcount ); 128 mutex_bucket_unlock(bucket, delta_torrentcount);
121 if( !g_opentracker_running ) 129 if (!g_opentracker_running)
122 return NULL; 130 return NULL;
123 usleep( OT_CLEAN_SLEEP ); 131 usleep(OT_CLEAN_SLEEP);
124 } 132 }
125 stats_cleanup(); 133 stats_cleanup();
134#ifdef WANT_ACCESSLIST
135 accesslist_cleanup();
136#endif
126 } 137 }
127 return NULL; 138 return NULL;
128} 139}
129 140
130static pthread_t thread_id; 141static pthread_t thread_id;
131void clean_init( void ) { 142void clean_init(void) { pthread_create(&thread_id, NULL, clean_worker, NULL); }
132 pthread_create( &thread_id, NULL, clean_worker, NULL );
133}
134
135void clean_deinit( void ) {
136 pthread_cancel( thread_id );
137}
138 143
139const char *g_version_clean_c = "$Source$: $Revision$\n"; 144void clean_deinit(void) { pthread_cancel(thread_id); }
diff --git a/ot_clean.h b/ot_clean.h
index 956770a..e8bcdc1 100644
--- a/ot_clean.h
+++ b/ot_clean.h
@@ -7,13 +7,13 @@
7#define OT_CLEAN_H__ 7#define OT_CLEAN_H__
8 8
9/* The amount of time a clean cycle should take */ 9/* The amount of time a clean cycle should take */
10#define OT_CLEAN_INTERVAL_MINUTES 2 10#define OT_CLEAN_INTERVAL_MINUTES 2
11 11
12/* So after each bucket wait 1 / OT_BUCKET_COUNT intervals */ 12/* So after each bucket wait 1 / OT_BUCKET_COUNT intervals */
13#define OT_CLEAN_SLEEP ( ( ( OT_CLEAN_INTERVAL_MINUTES ) * 60 * 1000000 ) / ( OT_BUCKET_COUNT ) ) 13#define OT_CLEAN_SLEEP (((OT_CLEAN_INTERVAL_MINUTES) * 60 * 1000000) / (OT_BUCKET_COUNT))
14 14
15void clean_init( void ); 15void clean_init(void);
16void clean_deinit( void ); 16void clean_deinit(void);
17int clean_single_torrent( ot_torrent *torrent ); 17int clean_single_torrent(ot_torrent *torrent);
18 18
19#endif 19#endif
diff --git a/ot_fullscrape.c b/ot_fullscrape.c
index 5d115dc..9dcbe68 100644
--- a/ot_fullscrape.c
+++ b/ot_fullscrape.c
@@ -6,152 +6,175 @@
6#ifdef WANT_FULLSCRAPE 6#ifdef WANT_FULLSCRAPE
7 7
8/* System */ 8/* System */
9#include <sys/param.h> 9#include <arpa/inet.h>
10#include <pthread.h>
10#include <stdio.h> 11#include <stdio.h>
11#include <string.h> 12#include <string.h>
12#include <pthread.h>
13#include <arpa/inet.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
17 19
18/* Libowfat */ 20/* Libowfat */
19#include "byte.h"
20#include "io.h" 21#include "io.h"
21#include "textcode.h"
22 22
23/* Opentracker */ 23/* Opentracker */
24#include "trackerlogic.h" 24#include "ot_hex.h"
25#include "ot_mutex.h"
26#include "ot_iovec.h"
27#include "ot_fullscrape.h" 25#include "ot_fullscrape.h"
26#include "ot_iovec.h"
27#include "ot_mutex.h"
28#include "scan_urlencoded_query.h"
29#include "trackerlogic.h"
28 30
29/* Fetch full scrape info for all torrents 31/* Fetch full scrape info for all torrents
30 Full scrapes usually are huge and one does not want to 32 Full scrapes usually are huge and one does not want to
31 allocate more memory. So lets get them in 512k units 33 allocate more memory. So lets get them in 512k units
32*/ 34*/
33#define OT_SCRAPE_CHUNK_SIZE (1024*1024) 35#define OT_SCRAPE_CHUNK_SIZE (1024 * 1024)
34 36
35/* "d8:completei%zde10:downloadedi%zde10:incompletei%zdee" */ 37/* "d8:completei%zde10:downloadedi%zde10:incompletei%zdee" */
36#define OT_SCRAPE_MAXENTRYLEN 256 38#define OT_SCRAPE_MAXENTRYLEN 256
37 39
38/* Forward declaration */ 40/* Forward declaration */
39static void fullscrape_make( int *iovec_entries, struct iovec **iovector, ot_tasktype mode ); 41static void fullscrape_make(int taskid, ot_tasktype mode);
40#ifdef WANT_COMPRESSION_GZIP 42#ifdef WANT_COMPRESSION_GZIP
41static void fullscrape_make_gzip( int *iovec_entries, struct iovec **iovector, ot_tasktype mode ); 43static void fullscrape_make_gzip(int taskid, ot_tasktype mode);
44#endif
45#ifdef WANT_COMPRESSION_ZSTD
46static void fullscrape_make_zstd(int taskid, ot_tasktype mode);
42#endif 47#endif
43 48
44/* Converter function from memory to human readable hex strings 49/* Converter function from memory to human readable hex strings
45 XXX - Duplicated from ot_stats. Needs fix. */ 50 XXX - Duplicated from ot_stats. Needs fix. */
46static char*to_hex(char*d,uint8_t*s){char*m="0123456789ABCDEF";char *t=d;char*e=d+40;while(d<e){*d++=m[*s>>4];*d++=m[*s++&15];}*d=0;return t;}
47
48/* This is the entry point into this worker thread 51/* This is the entry point into this worker thread
49 It grabs tasks from mutex_tasklist and delivers results back 52 It grabs tasks from mutex_tasklist and delivers results back
50*/ 53*/
51static void * fullscrape_worker( void * args ) { 54static void *fullscrape_worker(void *args) {
52 int iovec_entries; 55 (void)args;
53 struct iovec *iovector;
54
55 (void) args;
56 56
57 while( g_opentracker_running ) { 57 while (g_opentracker_running) {
58 ot_tasktype tasktype = TASK_FULLSCRAPE; 58 ot_tasktype tasktype = TASK_FULLSCRAPE;
59 ot_taskid taskid = mutex_workqueue_poptask( &tasktype ); 59 ot_taskid taskid = mutex_workqueue_poptask(&tasktype);
60#ifdef WANT_COMPRESSION_ZSTD
61 if (tasktype & TASK_FLAG_ZSTD)
62 fullscrape_make_zstd(taskid, tasktype);
63 else
64#endif
60#ifdef WANT_COMPRESSION_GZIP 65#ifdef WANT_COMPRESSION_GZIP
61 if (tasktype & TASK_FLAG_GZIP) 66 if (tasktype & TASK_FLAG_GZIP)
62 fullscrape_make_gzip( &iovec_entries, &iovector, tasktype ); 67 fullscrape_make_gzip(taskid, tasktype);
63 else 68 else
64#endif 69#endif
65 fullscrape_make( &iovec_entries, &iovector, tasktype ); 70 fullscrape_make(taskid, tasktype);
66 if( mutex_workqueue_pushresult( taskid, iovec_entries, iovector ) ) 71 mutex_workqueue_pushchunked(taskid, NULL);
67 iovec_free( &iovec_entries, &iovector );
68 } 72 }
69 return NULL; 73 return NULL;
70} 74}
71 75
72static pthread_t thread_id; 76static pthread_t thread_id;
73void fullscrape_init( ) { 77void fullscrape_init(void) {
74 pthread_create( &thread_id, NULL, fullscrape_worker, NULL ); 78 pthread_create( &thread_id, NULL, fullscrape_worker, NULL );
75} 79}
76 80
77void fullscrape_deinit( ) { 81void fullscrape_deinit(void) {
78 pthread_cancel( thread_id ); 82 pthread_cancel( thread_id );
79} 83}
80 84
81void fullscrape_deliver( int64 sock, ot_tasktype tasktype ) { 85void fullscrape_deliver( int64_t sock, ot_tasktype tasktype ) {
82 mutex_workqueue_pushtask( sock, tasktype ); 86 mutex_workqueue_pushtask( sock, tasktype );
83} 87}
84 88
85static char * fullscrape_write_one( ot_tasktype mode, char *r, ot_peerlist *peer_list, ot_hash *hash ) { 89static char * fullscrape_write_one( ot_tasktype mode, char *r, ot_torrent *torrent, ot_hash *hash ) {
86 switch( mode & TASK_TASK_MASK ) { 90 size_t seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count;
87 case TASK_FULLSCRAPE: 91 size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count;
88 default: 92 size_t down_count = torrent->peer_list6->down_count + torrent->peer_list4->down_count;
89 /* push hash as bencoded string */
90 *r++='2'; *r++='0'; *r++=':';
91 memcpy( r, hash, sizeof(ot_hash) ); r += sizeof(ot_hash);
92 /* push rest of the scrape string */
93 r += sprintf( r, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", peer_list->seed_count, peer_list->down_count, peer_list->peer_count-peer_list->seed_count );
94 93
95 break; 94 switch (mode & TASK_TASK_MASK) {
96 case TASK_FULLSCRAPE_TPB_ASCII: 95 case TASK_FULLSCRAPE:
97 to_hex( r, *hash ); r+= 2 * sizeof(ot_hash); 96 default:
98 r += sprintf( r, ":%zd:%zd\n", peer_list->seed_count, peer_list->peer_count-peer_list->seed_count ); 97 /* push hash as bencoded string */
99 break; 98 *r++ = '2';
100 case TASK_FULLSCRAPE_TPB_ASCII_PLUS: 99 *r++ = '0';
101 to_hex( r, *hash ); r+= 2 * sizeof(ot_hash); 100 *r++ = ':';
102 r += sprintf( r, ":%zd:%zd:%zd\n", peer_list->seed_count, peer_list->peer_count-peer_list->seed_count, peer_list->down_count ); 101 memcpy(r, hash, sizeof(ot_hash));
103 break; 102 r += sizeof(ot_hash);
104 case TASK_FULLSCRAPE_TPB_BINARY: 103 /* push rest of the scrape string */
105 memcpy( r, *hash, sizeof(ot_hash) ); r += sizeof(ot_hash); 104 r += sprintf(r, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", seed_count, down_count, peer_count - seed_count);
106 *(uint32_t*)(r+0) = htonl( (uint32_t) peer_list->seed_count ); 105
107 *(uint32_t*)(r+4) = htonl( (uint32_t)( peer_list->peer_count-peer_list->seed_count) ); 106 break;
108 r+=8; 107 case TASK_FULLSCRAPE_TPB_ASCII:
109 break; 108 hex_fromhash(r, *hash);
110 case TASK_FULLSCRAPE_TPB_URLENCODED: 109 r += 2 * sizeof(ot_hash);
111 r += fmt_urlencoded( r, (char *)*hash, 20 ); 110 r += sprintf(r, ":%zd:%zd\n", seed_count, peer_count - seed_count);
112 r += sprintf( r, ":%zd:%zd\n", peer_list->seed_count, peer_list->peer_count-peer_list->seed_count ); 111 break;
113 break; 112 case TASK_FULLSCRAPE_TPB_ASCII_PLUS:
114 case TASK_FULLSCRAPE_TRACKERSTATE: 113 hex_fromhash(r, *hash);
115 to_hex( r, *hash ); r+= 2 * sizeof(ot_hash); 114 r += 2 * sizeof(ot_hash);
116 r += sprintf( r, ":%zd:%zd\n", peer_list->base, peer_list->down_count ); 115 r += sprintf(r, ":%zd:%zd:%zd\n", seed_count, peer_count - seed_count, down_count);
117 break; 116 break;
118 } 117 case TASK_FULLSCRAPE_TPB_BINARY:
119 return r; 118 memcpy(r, *hash, sizeof(ot_hash));
119 r += sizeof(ot_hash);
120 *(uint32_t *)(r + 0) = htonl((uint32_t)seed_count);
121 *(uint32_t *)(r + 4) = htonl((uint32_t)(peer_count - seed_count));
122 r += 8;
123 break;
124 case TASK_FULLSCRAPE_TPB_URLENCODED:
125 r += fmt_urlencoded(r, *hash, sizeof(ot_hash));
126 r += sprintf(r, ":%zd:%zd\n", seed_count, peer_count - seed_count);
127 break;
128 case TASK_FULLSCRAPE_TRACKERSTATE:
129 hex_fromhash(r, *hash);
130 r += 2 * sizeof(ot_hash);
131 r += sprintf(r, ":%zd:%zd\n", torrent->peer_list6->base, down_count);
132 break;
133 }
134 return r;
120} 135}
121 136
122static void fullscrape_make( int *iovec_entries, struct iovec **iovector, ot_tasktype mode ) { 137static void fullscrape_make(int taskid, ot_tasktype mode) {
123 int bucket; 138 size_t bucket;
124 char *r, *re; 139 char *r, *re;
140 struct iovec iovector = {NULL, 0};
125 141
126 /* Setup return vector... */ 142 /* Setup return vector... */
127 *iovec_entries = 0; 143 r = iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE);
128 *iovector = NULL; 144 if (!r)
129 r = iovec_increase( iovec_entries, iovector, OT_SCRAPE_CHUNK_SIZE );
130 if( !r )
131 return; 145 return;
132 146
133 /* re points to low watermark */ 147 /* re points to low watermark */
134 re = r + OT_SCRAPE_CHUNK_SIZE - OT_SCRAPE_MAXENTRYLEN; 148 re = r + OT_SCRAPE_CHUNK_SIZE - OT_SCRAPE_MAXENTRYLEN;
135 149
136 if( ( mode & TASK_TASK_MASK ) == TASK_FULLSCRAPE ) 150 if ((mode & TASK_TASK_MASK) == TASK_FULLSCRAPE)
137 r += sprintf( r, "d5:filesd" ); 151 r += sprintf(r, "d5:filesd");
138 152
139 /* For each bucket... */ 153 /* For each bucket... */
140 for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) { 154 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
141 /* Get exclusive access to that bucket */ 155 /* Get exclusive access to that bucket */
142 ot_vector *torrents_list = mutex_bucket_lock( bucket ); 156 ot_vector *torrents_list = mutex_bucket_lock(bucket);
143 ot_torrent *torrents = (ot_torrent*)(torrents_list->data); 157 ot_torrent *torrents = (ot_torrent *)(torrents_list->data);
144 size_t i; 158 size_t i;
145 159
146 /* For each torrent in this bucket.. */ 160 /* For each torrent in this bucket.. */
147 for( i=0; i<torrents_list->size; ++i ) { 161 for (i = 0; i < torrents_list->size; ++i) {
148 r = fullscrape_write_one( mode, r, torrents[i].peer_list, &torrents[i].hash ); 162 r = fullscrape_write_one(mode, r, torrents + i, &torrents[i].hash);
149 163
150 if( r > re) { 164 if (r > re) {
151 /* Allocate a fresh output buffer at the end of our buffers list */ 165 iovector.iov_len = r - (char *)iovector.iov_base;
152 r = iovec_fix_increase_or_free( iovec_entries, iovector, r, OT_SCRAPE_CHUNK_SIZE ); 166
153 if( !r ) 167 if (mutex_workqueue_pushchunked(taskid, &iovector)) {
154 return mutex_bucket_unlock( bucket, 0 ); 168 free(iovector.iov_base);
169 mutex_bucket_unlock(bucket, 0);
170 return;
171 }
172 /* Allocate a fresh output buffer */
173 r = iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE);
174 if (!r) {
175 mutex_bucket_unlock(bucket, 0);
176 return;
177 }
155 178
156 /* re points to low watermark */ 179 /* re points to low watermark */
157 re = r + OT_SCRAPE_CHUNK_SIZE - OT_SCRAPE_MAXENTRYLEN; 180 re = r + OT_SCRAPE_CHUNK_SIZE - OT_SCRAPE_MAXENTRYLEN;
@@ -159,125 +182,267 @@ static void fullscrape_make( int *iovec_entries, struct iovec **iovector, ot_tas
159 } 182 }
160 183
161 /* All torrents done: release lock on current bucket */ 184 /* All torrents done: release lock on current bucket */
162 mutex_bucket_unlock( bucket, 0 ); 185 mutex_bucket_unlock(bucket, 0);
163 186
164 /* Parent thread died? */ 187 /* Parent thread died? */
165 if( !g_opentracker_running ) 188 if (!g_opentracker_running)
166 return; 189 return;
167 } 190 }
168 191
169 if( ( mode & TASK_TASK_MASK ) == TASK_FULLSCRAPE ) 192 if ((mode & TASK_TASK_MASK) == TASK_FULLSCRAPE)
170 r += sprintf( r, "ee" ); 193 r += sprintf(r, "ee");
171 194
172 /* Release unused memory in current output buffer */ 195 /* Send rest of data */
173 iovec_fixlast( iovec_entries, iovector, r ); 196 iovector.iov_len = r - (char *)iovector.iov_base;
197 if (mutex_workqueue_pushchunked(taskid, &iovector))
198 free(iovector.iov_base);
174} 199}
175 200
176#ifdef WANT_COMPRESSION_GZIP 201#ifdef WANT_COMPRESSION_GZIP
177 202
178static void fullscrape_make_gzip( int *iovec_entries, struct iovec **iovector, ot_tasktype mode ) { 203static void fullscrape_make_gzip(int taskid, ot_tasktype mode) {
179 int bucket; 204 size_t bucket;
180 char *r; 205 char *r;
181 int zres; 206 struct iovec iovector = {NULL, 0};
182 z_stream strm; 207 int zres;
183 208 z_stream strm;
184 /* Setup return vector... */ 209 /* Setup return vector... */
185 *iovec_entries = 0; 210 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE);
186 *iovector = NULL; 211 if (!iovector.iov_base)
187 r = iovec_increase( iovec_entries, iovector, OT_SCRAPE_CHUNK_SIZE );
188 if( !r )
189 return; 212 return;
190 213
191 byte_zero( &strm, sizeof(strm) ); 214 memset(&strm, 0, sizeof(strm));
192 strm.next_out = (uint8_t*)r; 215 strm.next_out = (uint8_t *)iovector.iov_base;
193 strm.avail_out = OT_SCRAPE_CHUNK_SIZE; 216 strm.avail_out = OT_SCRAPE_CHUNK_SIZE;
194 if( deflateInit2(&strm,7,Z_DEFLATED,31,9,Z_DEFAULT_STRATEGY) != Z_OK ) 217 if (deflateInit2(&strm, 7, Z_DEFLATED, 31, 9, Z_DEFAULT_STRATEGY) != Z_OK)
195 fprintf( stderr, "not ok.\n" ); 218 fprintf(stderr, "not ok.\n");
196 219
197 if( ( mode & TASK_TASK_MASK ) == TASK_FULLSCRAPE ) { 220 if ((mode & TASK_TASK_MASK) == TASK_FULLSCRAPE) {
198 strm.next_in = (uint8_t*)"d5:filesd"; 221 strm.next_in = (uint8_t *)"d5:filesd";
199 strm.avail_in = strlen("d5:filesd"); 222 strm.avail_in = strlen("d5:filesd");
200 zres = deflate( &strm, Z_NO_FLUSH ); 223 zres = deflate(&strm, Z_NO_FLUSH);
201 } 224 }
202 225
203 /* For each bucket... */ 226 /* For each bucket... */
204 for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) { 227 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
205 /* Get exclusive access to that bucket */ 228 /* Get exclusive access to that bucket */
206 ot_vector *torrents_list = mutex_bucket_lock( bucket ); 229 ot_vector *torrents_list = mutex_bucket_lock(bucket);
207 ot_torrent *torrents = (ot_torrent*)(torrents_list->data); 230 ot_torrent *torrents = (ot_torrent *)(torrents_list->data);
208 size_t i; 231 size_t i;
209 232
210 /* For each torrent in this bucket.. */ 233 /* For each torrent in this bucket.. */
211 for( i=0; i<torrents_list->size; ++i ) { 234 for (i = 0; i < torrents_list->size; ++i) {
212 char compress_buffer[OT_SCRAPE_MAXENTRYLEN]; 235 char compress_buffer[OT_SCRAPE_MAXENTRYLEN];
213 r = fullscrape_write_one( mode, compress_buffer, torrents[i].peer_list, &torrents[i].hash ); 236 r = fullscrape_write_one(mode, compress_buffer, torrents + i, &torrents[i].hash);
214 strm.next_in = (uint8_t*)compress_buffer; 237 strm.next_in = (uint8_t *)compress_buffer;
215 strm.avail_in = r - compress_buffer; 238 strm.avail_in = r - compress_buffer;
216 zres = deflate( &strm, Z_NO_FLUSH ); 239 zres = deflate(&strm, Z_NO_FLUSH);
217 if( ( zres < Z_OK ) && ( zres != Z_BUF_ERROR ) ) 240 if ((zres < Z_OK) && (zres != Z_BUF_ERROR))
218 fprintf( stderr, "deflate() failed while in fullscrape_make().\n" ); 241 fprintf(stderr, "deflate() failed while in fullscrape_make().\n");
219 242
220 /* Check if there still is enough buffer left */ 243 /* Check if there still is enough buffer left */
221 while( !strm.avail_out ) { 244 while (!strm.avail_out) {
222 /* Allocate a fresh output buffer at the end of our buffers list */ 245 iovector.iov_len = (char *)strm.next_out - (char *)iovector.iov_base;
223 r = iovec_increase( iovec_entries, iovector, OT_SCRAPE_CHUNK_SIZE ); 246
224 if( !r ) { 247 if (mutex_workqueue_pushchunked(taskid, &iovector)) {
225 fprintf( stderr, "Problem with iovec_fix_increase_or_free\n" ); 248 free(iovector.iov_base);
226 iovec_free( iovec_entries, iovector ); 249 mutex_bucket_unlock(bucket, 0);
250 return;
251 }
252 /* Allocate a fresh output buffer */
253 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE);
254 if (!iovector.iov_base) {
255 fprintf(stderr, "Out of memory trying to claim ouput buffer\n");
227 deflateEnd(&strm); 256 deflateEnd(&strm);
228 return mutex_bucket_unlock( bucket, 0 ); 257 mutex_bucket_unlock(bucket, 0);
258 return;
229 } 259 }
230 strm.next_out = (uint8_t*)r; 260 strm.next_out = (uint8_t *)iovector.iov_base;
231 strm.avail_out = OT_SCRAPE_CHUNK_SIZE; 261 strm.avail_out = OT_SCRAPE_CHUNK_SIZE;
232 zres = deflate( &strm, Z_NO_FLUSH ); 262 zres = deflate(&strm, Z_NO_FLUSH);
233 if( ( zres < Z_OK ) && ( zres != Z_BUF_ERROR ) ) 263 if ((zres < Z_OK) && (zres != Z_BUF_ERROR))
234 fprintf( stderr, "deflate() failed while in fullscrape_make().\n" ); 264 fprintf(stderr, "deflate() failed while in fullscrape_make().\n");
235 } 265 }
236 } 266 }
237 267
238 /* All torrents done: release lock on current bucket */ 268 /* All torrents done: release lock on current bucket */
239 mutex_bucket_unlock( bucket, 0 ); 269 mutex_bucket_unlock(bucket, 0);
240 270
241 /* Parent thread died? */ 271 /* Parent thread died? */
242 if( !g_opentracker_running ) 272 if (!g_opentracker_running) {
273 deflateEnd(&strm);
243 return; 274 return;
275 }
244 } 276 }
245 277
246 if( ( mode & TASK_TASK_MASK ) == TASK_FULLSCRAPE ) { 278 if ((mode & TASK_TASK_MASK) == TASK_FULLSCRAPE) {
247 strm.next_in = (uint8_t*)"ee"; 279 strm.next_in = (uint8_t *)"ee";
248 strm.avail_in = strlen("ee"); 280 strm.avail_in = strlen("ee");
249 } 281 }
250 282
251 if( deflate( &strm, Z_FINISH ) < Z_OK ) 283 if (deflate(&strm, Z_FINISH) < Z_OK)
252 fprintf( stderr, "deflate() failed while in fullscrape_make()'s endgame.\n" ); 284 fprintf(stderr, "deflate() failed while in fullscrape_make()'s endgame.\n");
253 285
254 if( !strm.avail_out ) { 286 iovector.iov_len = (char *)strm.next_out - (char *)iovector.iov_base;
255 unsigned int pending; 287 if (mutex_workqueue_pushchunked(taskid, &iovector)) {
256 int bits; 288 free(iovector.iov_base);
257 deflatePending( &strm, &pending, &bits); 289 deflateEnd(&strm);
258 pending += ( bits ? 1 : 0 ); 290 return;
259
260 /* Allocate a fresh output buffer at the end of our buffers list */
261 r = iovec_fix_increase_or_free( iovec_entries, iovector, strm.next_out, pending );
262 if( !r ) {
263 fprintf( stderr, "Problem with iovec_fix_increase_or_free\n" );
264 deflateEnd(&strm);
265 return mutex_bucket_unlock( bucket, 0 );
266 }
267 strm.next_out = (uint8_t*)r;
268 strm.avail_out = pending;
269 if( deflate( &strm, Z_FINISH ) < Z_OK )
270 fprintf( stderr, "deflate() failed while in fullscrape_make()'s endgame.\n" );
271 } 291 }
272 292
273 /* Release unused memory in current output buffer */ 293 /* Check if there's a last batch of data in the zlib buffer */
274 iovec_fixlast( iovec_entries, iovector, strm.next_out ); 294 if (!strm.avail_out) {
295 /* Allocate a fresh output buffer */
296 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE);
297
298 if (!iovector.iov_base) {
299 fprintf(stderr, "Problem with iovec_fix_increase_or_free\n");
300 deflateEnd(&strm);
301 return;
302 }
303 strm.next_out = iovector.iov_base;
304 strm.avail_out = OT_SCRAPE_CHUNK_SIZE;
305 if (deflate(&strm, Z_FINISH) < Z_OK)
306 fprintf(stderr, "deflate() failed while in fullscrape_make()'s endgame.\n");
307
308 /* Only pass the new buffer if there actually was some data left in the buffer */
309 iovector.iov_len = (char *)strm.next_out - (char *)iovector.iov_base;
310 if (!iovector.iov_len || mutex_workqueue_pushchunked(taskid, &iovector))
311 free(iovector.iov_base);
312 }
275 313
276 deflateEnd(&strm); 314 deflateEnd(&strm);
277} 315}
278/* WANT_COMPRESSION_GZIP */ 316/* WANT_COMPRESSION_GZIP */
279#endif 317#endif
280 318
319#ifdef WANT_COMPRESSION_ZSTD
320
321static void fullscrape_make_zstd(int taskid, ot_tasktype mode) {
322 int bucket;
323 char *r;
324 struct iovec iovector = {NULL, 0};
325 ZSTD_CCtx *zstream = ZSTD_createCCtx();
326 ZSTD_inBuffer inbuf;
327 ZSTD_outBuffer outbuf;
328 size_t more_bytes;
329
330 if (!zstream)
331 return;
332
333 /* Setup return vector... */
334 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE);
335 if (!iovector.iov_base) {
336 ZSTD_freeCCtx(zstream);
337 return;
338 }
339
340 /* Working with a compression level 6 is half as fast as level 3, but
341 seems to be the last reasonable bump that's worth extra cpu */
342 ZSTD_CCtx_setParameter(zstream, ZSTD_c_compressionLevel, 6);
343
344 outbuf.dst = iovector.iov_base;
345 outbuf.size = OT_SCRAPE_CHUNK_SIZE;
346 outbuf.pos = 0;
347
348 if ((mode & TASK_TASK_MASK) == TASK_FULLSCRAPE) {
349 inbuf.src = (const void *)"d5:filesd";
350 inbuf.size = strlen("d5:filesd");
351 inbuf.pos = 0;
352 ZSTD_compressStream2(zstream, &outbuf, &inbuf, ZSTD_e_continue);
353 }
354
355 /* For each bucket... */
356 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
357 /* Get exclusive access to that bucket */
358 ot_vector *torrents_list = mutex_bucket_lock(bucket);
359 ot_torrent *torrents = (ot_torrent *)(torrents_list->data);
360 size_t i;
361
362 /* For each torrent in this bucket.. */
363 for (i = 0; i < torrents_list->size; ++i) {
364 char compress_buffer[OT_SCRAPE_MAXENTRYLEN];
365 r = fullscrape_write_one(mode, compress_buffer, torrents + i, &torrents[i].hash);
366 inbuf.src = compress_buffer;
367 inbuf.size = r - compress_buffer;
368 inbuf.pos = 0;
369 ZSTD_compressStream2(zstream, &outbuf, &inbuf, ZSTD_e_continue);
370
371 /* Check if there still is enough buffer left */
372 while (outbuf.pos + OT_SCRAPE_MAXENTRYLEN > outbuf.size) {
373 iovector.iov_len = outbuf.size;
374
375 if (mutex_workqueue_pushchunked(taskid, &iovector)) {
376 free(iovector.iov_base);
377 ZSTD_freeCCtx(zstream);
378 return mutex_bucket_unlock(bucket, 0);
379 }
380 /* Allocate a fresh output buffer */
381 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE);
382 if (!iovector.iov_base) {
383 fprintf(stderr, "Out of memory trying to claim ouput buffer\n");
384 ZSTD_freeCCtx(zstream);
385 return mutex_bucket_unlock(bucket, 0);
386 }
387
388 outbuf.dst = iovector.iov_base;
389 outbuf.size = OT_SCRAPE_CHUNK_SIZE;
390 outbuf.pos = 0;
391
392 ZSTD_compressStream2(zstream, &outbuf, &inbuf, ZSTD_e_continue);
393 }
394 }
395
396 /* All torrents done: release lock on current bucket */
397 mutex_bucket_unlock(bucket, 0);
398
399 /* Parent thread died? */
400 if (!g_opentracker_running)
401 return;
402 }
403
404 if ((mode & TASK_TASK_MASK) == TASK_FULLSCRAPE) {
405 inbuf.src = (const void *)"ee";
406 inbuf.size = strlen("ee");
407 inbuf.pos = 0;
408 }
409
410 more_bytes = ZSTD_compressStream2(zstream, &outbuf, &inbuf, ZSTD_e_end);
411
412 iovector.iov_len = outbuf.pos;
413 if (mutex_workqueue_pushchunked(taskid, &iovector)) {
414 free(iovector.iov_base);
415 ZSTD_freeCCtx(zstream);
416 return;
417 }
418
419 /* Check if there's a last batch of data in the zlib buffer */
420 if (more_bytes) {
421 /* Allocate a fresh output buffer */
422 iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE);
423
424 if (!iovector.iov_base) {
425 fprintf(stderr, "Problem with iovec_fix_increase_or_free\n");
426 ZSTD_freeCCtx(zstream);
427 return;
428 }
429
430 outbuf.dst = iovector.iov_base;
431 outbuf.size = OT_SCRAPE_CHUNK_SIZE;
432 outbuf.pos = 0;
433
434 ZSTD_compressStream2(zstream, &outbuf, &inbuf, ZSTD_e_end);
435
436 /* Only pass the new buffer if there actually was some data left in the buffer */
437 iovector.iov_len = outbuf.pos;
438 if (!iovector.iov_len || mutex_workqueue_pushchunked(taskid, &iovector))
439 free(iovector.iov_base);
440 }
441
442 ZSTD_freeCCtx(zstream);
443}
444/* WANT_COMPRESSION_ZSTD */
445#endif
446
281/* WANT_FULLSCRAPE */ 447/* WANT_FULLSCRAPE */
282#endif 448#endif
283const char *g_version_fullscrape_c = "$Source$: $Revision$\n";
diff --git a/ot_fullscrape.h b/ot_fullscrape.h
index 0f920ec..0e7690c 100644
--- a/ot_fullscrape.h
+++ b/ot_fullscrape.h
@@ -8,9 +8,11 @@
8 8
9#ifdef WANT_FULLSCRAPE 9#ifdef WANT_FULLSCRAPE
10 10
11void fullscrape_init( ); 11#include "ot_mutex.h"
12void fullscrape_deinit( ); 12
13void fullscrape_deliver( int64 sock, ot_tasktype tasktype ); 13void fullscrape_init(void);
14void fullscrape_deinit(void);
15void fullscrape_deliver(int64_t sock, ot_tasktype tasktype);
14 16
15#else 17#else
16 18
diff --git a/ot_hex.c b/ot_hex.c
new file mode 100644
index 0000000..9581951
--- /dev/null
+++ b/ot_hex.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 <stdint.h>
8#include <string.h>
9
10/* Opentracker */
11#include "ot_hex.h"
12
13int hex_value(int c) {
14 unsigned char u = (unsigned char)c;
15
16 if (u >= '0' && u <= '9')
17 return u - '0';
18 u |= 0x20; /* fold upper case onto lower, digits are already handled */
19 if (u >= 'a' && u <= 'f')
20 return u - 'a' + 10;
21 return -1;
22}
23
24int hex_isdigit(int c) { return hex_value(c) >= 0; }
25
26char *hex_fromhash(char *dest, const uint8_t *hash) {
27 static const char digits[] = "0123456789ABCDEF";
28 char *d = dest;
29 size_t i;
30
31 for (i = 0; i < sizeof(ot_hash); ++i) {
32 *d++ = digits[hash[i] >> 4];
33 *d++ = digits[hash[i] & 15];
34 }
35 *d = 0;
36 return dest;
37}
38
39int hex_tohash(ot_hash hash, const char *src) {
40 uint8_t decoded[sizeof(ot_hash)];
41 size_t i;
42
43 for (i = 0; i < sizeof(ot_hash); ++i) {
44 int high, low;
45
46 /* Tested one character at a time so that a string ending early stops us
47 at its terminator rather than one byte past it */
48 if ((high = hex_value((unsigned char)src[2 * i])) < 0)
49 return 0;
50 if ((low = hex_value((unsigned char)src[2 * i + 1])) < 0)
51 return 0;
52
53 decoded[i] = (uint8_t)((high << 4) | low);
54 }
55
56 memcpy(hash, decoded, sizeof(decoded));
57 return 1;
58}
diff --git a/ot_hex.h b/ot_hex.h
new file mode 100644
index 0000000..3b45b61
--- /dev/null
+++ b/ot_hex.h
@@ -0,0 +1,29 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6#ifndef OT_HEX_H__
7#define OT_HEX_H__
8
9#include <stdint.h>
10
11#include "trackerlogic.h"
12
13/* Write hash as forty upper case hex digits followed by a terminating zero.
14 dest must have room for forty one bytes. Returns dest. */
15char *hex_fromhash(char *dest, const uint8_t *hash);
16
17/* Read forty hex digits from src into hash. Returns non-zero on success.
18 On failure hash is left untouched and no byte of src beyond the offending
19 character is read, so src need not be forty characters long. */
20int hex_tohash(ot_hash hash, const char *src);
21
22/* Non-zero if c is a hexadecimal digit, in either case. */
23int hex_isdigit(int c);
24
25/* The value of a hexadecimal digit, or -1 if c is not one. Shared with the
26 address scanner so that both agree on what a digit is. */
27int hex_value(int c);
28
29#endif
diff --git a/ot_http.c b/ot_http.c
index 2e16b58..46ca5a9 100644
--- a/ot_http.c
+++ b/ot_http.c
@@ -4,530 +4,673 @@
4 $id$ */ 4 $id$ */
5 5
6/* System */ 6/* System */
7#include <sys/types.h> 7#define _GNU_SOURCE
8#include <arpa/inet.h> 8#include <arpa/inet.h>
9#include <stdlib.h> 9#include <pthread.h>
10#include <stdio.h> 10#include <stdio.h>
11#include <stdlib.h>
11#include <string.h> 12#include <string.h>
13#include <strings.h>
14#include <sys/types.h>
12#include <unistd.h> 15#include <unistd.h>
13#include <pthread.h>
14 16
15/* Libowfat */ 17/* Libowfat */
16#include "byte.h"
17#include "array.h"
18#include "iob.h" 18#include "iob.h"
19#include "ip6.h"
20#include "scan.h"
21#include "case.h"
22 19
23/* Opentracker */ 20/* Opentracker */
24#include "trackerlogic.h" 21#include "ot_accesslist.h"
25#include "ot_mutex.h" 22#include "ot_fullscrape.h"
26#include "ot_http.h" 23#include "ot_http.h"
27#include "ot_iovec.h" 24#include "ot_iovec.h"
28#include "scan_urlencoded_query.h" 25#include "ot_ip.h"
29#include "ot_fullscrape.h" 26#include "ot_mutex.h"
27#include "ot_scan.h"
30#include "ot_stats.h" 28#include "ot_stats.h"
31#include "ot_accesslist.h" 29#include "scan_urlencoded_query.h"
30#include "trackerlogic.h"
31
32#ifdef WANT_NO_AUTO_FREE
33#define OT_IOB_INIT(B) (bzero(B, sizeof(io_batch)), 0)
34#else
35#define OT_IOB_INIT(B) iob_init_autofree(B, 0)
36#endif
32 37
33#define OT_MAXMULTISCRAPE_COUNT 64 38#define OT_MAXMULTISCRAPE_COUNT 64
34#define OT_BATCH_LIMIT (1024*1024*16) 39#define OT_BATCH_LIMIT (1024 * 1024 * 16)
35extern char *g_redirecturl; 40extern char *g_redirecturl;
36 41
37char *g_stats_path; 42char *g_stats_path;
38ssize_t g_stats_path_len; 43ssize_t g_stats_path_len;
39 44
40enum { 45enum { SUCCESS_HTTP_HEADER_LENGTH = 80, SUCCESS_HTTP_SIZE_OFF = 17 };
41 SUCCESS_HTTP_HEADER_LENGTH = 80,
42 SUCCESS_HTTP_HEADER_LENGTH_CONTENT_ENCODING = 32,
43 SUCCESS_HTTP_SIZE_OFF = 17 };
44 46
45static void http_senddata( const int64 sock, struct ot_workstruct *ws ) { 47static void http_senddata(const int64_t sock, struct ot_workstruct *ws) {
46 struct http_data *cookie = io_getcookie( sock ); 48 struct http_data *cookie = io_getcookie(sock);
47 ssize_t written_size; 49 ssize_t written_size;
48 50
49 if( !cookie ) { io_close(sock); return; } 51 if (!cookie) {
52 io_close(sock);
53 return;
54 }
50 55
51 /* whoever sends data is not interested in its input-array */ 56 /* whoever sends data is not interested in its input buffer */
52 if( ws->keep_alive && ws->header_size != ws->request_size ) { 57 if (ws->keep_alive && ws->header_size != ws->request_size) {
53 size_t rest = ws->request_size - ws->header_size; 58 size_t rest = ws->request_size - ws->header_size;
54 if( array_start(&cookie->request) ) { 59 if (cookie->request.data) {
55 memmove( array_start(&cookie->request), ws->request + ws->header_size, rest ); 60 memmove(cookie->request.data, ws->request + ws->header_size, rest);
56 array_truncate( &cookie->request, 1, rest ); 61 buf_truncate(&cookie->request, rest);
57 } else 62 } else if (buf_cat(&cookie->request, ws->request + ws->header_size, rest)) {
58 array_catb(&cookie->request, ws->request + ws->header_size, rest ); 63 /* Without room for what the client pipelined behind this request, answer
64 this one and let the connection close, rather than keeping it open
65 around a request we have silently dropped */
66 buf_reset(&cookie->request);
67 ws->keep_alive = 0;
68 }
59 } else 69 } else
60 array_reset( &cookie->request ); 70 buf_reset(&cookie->request);
61 71
62 written_size = write( sock, ws->reply, ws->reply_size ); 72 written_size = write(sock, ws->reply, ws->reply_size);
63 if( ( written_size < 0 ) || ( ( written_size == ws->reply_size ) && !ws->keep_alive ) ) { 73 if ((written_size < 0) || ((written_size == ws->reply_size) && !ws->keep_alive)) {
64 array_reset( &cookie->request ); 74 buf_reset(&cookie->request);
65 free( cookie ); io_close( sock ); return; 75 free(cookie);
76 io_close(sock);
77 return;
66 } 78 }
67 79
68 if( written_size < ws->reply_size ) { 80 if (written_size < ws->reply_size) {
69 char * outbuf; 81 char *outbuf;
70 tai6464 t; 82 tai6464 t;
71 83
72 if( !( outbuf = malloc( ws->reply_size - written_size ) ) ) { 84 if (!(outbuf = malloc(ws->reply_size - written_size))) {
73 array_reset( &cookie->request ); 85 buf_reset(&cookie->request);
74 free(cookie); io_close( sock ); 86 free(cookie);
87 io_close(sock);
75 return; 88 return;
76 } 89 }
77 90
78 memcpy( outbuf, ws->reply + written_size, ws->reply_size - written_size ); 91 memcpy(outbuf, ws->reply + written_size, ws->reply_size - written_size);
79 if ( !cookie->batch ) { 92 if (!cookie->batch) {
80 cookie->batch = malloc( sizeof(io_batch) ); 93 cookie->batch = malloc(sizeof(io_batch));
81 memset( cookie->batch, 0, sizeof(io_batch) ); 94 if (!cookie->batch || OT_IOB_INIT(cookie->batch) == -1) {
82 cookie->batches = 1; 95 free(cookie->batch);
96 free(outbuf);
97 buf_reset(&cookie->request);
98 free(cookie);
99 io_close(sock);
100 return;
101 }
102
103 cookie->batches = 1;
83 } 104 }
84 105
85 iob_addbuf_free( cookie->batch, outbuf, ws->reply_size - written_size ); 106 iob_addbuf_free(cookie->batch, outbuf, ws->reply_size - written_size);
86 107
87 /* writeable short data sockets just have a tcp timeout */ 108 /* writeable short data sockets just have a tcp timeout */
88 if( !ws->keep_alive ) { 109 if (!ws->keep_alive) {
89 taia_uint( &t, 0 ); io_timeout( sock, t ); 110 taia_uint(&t, 0);
90 io_dontwantread( sock ); 111 io_timeout(sock, t);
112 io_dontwantread(sock);
91 } 113 }
92 io_wantwrite( sock ); 114 io_wantwrite(sock);
93 } 115 }
94} 116}
95 117
96#define HTTPERROR_302 return http_issue_error( sock, ws, CODE_HTTPERROR_302 ) 118#define HTTPERROR_302 return http_issue_error(sock, ws, CODE_HTTPERROR_302)
97#define HTTPERROR_400 return http_issue_error( sock, ws, CODE_HTTPERROR_400 ) 119#define HTTPERROR_400 return http_issue_error(sock, ws, CODE_HTTPERROR_400)
98#define HTTPERROR_400_PARAM return http_issue_error( sock, ws, CODE_HTTPERROR_400_PARAM ) 120#define HTTPERROR_400_PARAM return http_issue_error(sock, ws, CODE_HTTPERROR_400_PARAM)
99#define HTTPERROR_400_COMPACT return http_issue_error( sock, ws, CODE_HTTPERROR_400_COMPACT ) 121#define HTTPERROR_400_COMPACT return http_issue_error(sock, ws, CODE_HTTPERROR_400_COMPACT)
100#define HTTPERROR_400_DOUBLEHASH return http_issue_error( sock, ws, CODE_HTTPERROR_400_PARAM ) 122#define HTTPERROR_400_DOUBLEHASH return http_issue_error(sock, ws, CODE_HTTPERROR_400_PARAM)
101#define HTTPERROR_402_NOTMODEST return http_issue_error( sock, ws, CODE_HTTPERROR_402_NOTMODEST ) 123#define HTTPERROR_402_NOTMODEST return http_issue_error(sock, ws, CODE_HTTPERROR_402_NOTMODEST)
102#define HTTPERROR_403_IP return http_issue_error( sock, ws, CODE_HTTPERROR_403_IP ) 124#define HTTPERROR_403_IP return http_issue_error(sock, ws, CODE_HTTPERROR_403_IP)
103#define HTTPERROR_404 return http_issue_error( sock, ws, CODE_HTTPERROR_404 ) 125#define HTTPERROR_404 return http_issue_error(sock, ws, CODE_HTTPERROR_404)
104#define HTTPERROR_500 return http_issue_error( sock, ws, CODE_HTTPERROR_500 ) 126#define HTTPERROR_500 return http_issue_error(sock, ws, CODE_HTTPERROR_500)
105ssize_t http_issue_error( const int64 sock, struct ot_workstruct *ws, int code ) { 127ssize_t http_issue_error(const int64_t sock, struct ot_workstruct *ws, int code) {
106 char *error_code[] = { "302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", "402 Payment Required", 128 char *error_code[] = {"302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", "402 Payment Required",
107 "403 Not Modest", "403 Access Denied", "404 Not Found", "500 Internal Server Error" }; 129 "403 Not Modest", "403 Access Denied", "404 Not Found", "500 Internal Server Error"};
108 char *title = error_code[code]; 130 char *title = error_code[code];
109 131
110 ws->reply = ws->outbuf; 132 ws->reply = ws->outbuf;
111 if( code == CODE_HTTPERROR_302 ) 133 if (code == CODE_HTTPERROR_302)
112 ws->reply_size = snprintf( ws->reply, G_OUTBUF_SIZE, "HTTP/1.0 302 Found\r\nContent-Length: 0\r\nLocation: %s\r\n\r\n", g_redirecturl ); 134 ws->reply_size = snprintf(ws->reply, G_OUTBUF_SIZE, "HTTP/1.0 302 Found\r\nContent-Length: 0\r\nLocation: %s\r\n\r\n", g_redirecturl);
113 else 135 else
114 ws->reply_size = snprintf( ws->reply, G_OUTBUF_SIZE, "HTTP/1.0 %s\r\nContent-Type: text/html\r\nContent-Length: %zd\r\n\r\n<title>%s</title>\n", title, strlen(title)+16-4,title+4); 136 ws->reply_size = snprintf(ws->reply, G_OUTBUF_SIZE, "HTTP/1.0 %s\r\nContent-Type: text/html\r\nContent-Length: %zd\r\n\r\n<title>%s</title>\n", title,
137 strlen(title) + 16 - 4, title + 4);
115 138
116#ifdef _DEBUG_HTTPERROR 139#ifdef _DEBUG_HTTPERROR
117 fprintf( stderr, "DEBUG: invalid request was: %s\n", ws->debugbuf ); 140 fprintf(stderr, "DEBUG: invalid request was: %s\n", ws->debugbuf);
118#endif 141#endif
119 stats_issue_event( EVENT_FAILED, FLAG_TCP, code ); 142 stats_issue_event(EVENT_FAILED, FLAG_TCP, code);
120 http_senddata( sock, ws ); 143 http_senddata(sock, ws);
121 return ws->reply_size = -2; 144 return ws->reply_size = -2;
122} 145}
123 146
124ssize_t http_sendiovecdata( const int64 sock, struct ot_workstruct *ws, int iovec_entries, struct iovec *iovector ) { 147ssize_t http_sendiovecdata(const int64_t sock, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial) {
125 struct http_data *cookie = io_getcookie( sock ); 148 struct http_data *cookie = io_getcookie(sock);
126 char *header; 149 io_batch *current;
127 int i; 150 char *header;
128 size_t header_size, size = iovec_length( &iovec_entries, (const struct iovec **)&iovector ); 151 const char *encoding = "";
129 tai6464 t; 152 size_t i, header_size, size = iovec_length(&iovec_entries, (const struct iovec **)&iovector);
153 tai6464 t;
130 154
131 /* No cookie? Bad socket. Leave. */ 155 /* No cookie? Bad socket. Leave. */
132 if( !cookie ) { 156 if (!cookie) {
133 iovec_free( &iovec_entries, &iovector ); 157 iovec_free(&iovec_entries, &iovector);
134 HTTPERROR_500; 158 HTTPERROR_500;
135 } 159 }
136 160
161 /* Descriptor numbers get recycled, so a result can only belong to this
162 connection if this connection is in fact waiting for one. */
163 if (!(cookie->flag & (STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER))) {
164 iovec_free(&iovec_entries, &iovector);
165 return 0;
166 }
167
137 /* If this socket collected request in a buffer, free it now */ 168 /* If this socket collected request in a buffer, free it now */
138 array_reset( &cookie->request ); 169 buf_reset(&cookie->request);
139 170
140 /* If we came here, wait for the answer is over */ 171 /* If we came here, wait for the answer is over */
141 cookie->flag &= ~STRUCT_HTTP_FLAG_WAITINGFORTASK; 172 if (cookie->flag & STRUCT_HTTP_FLAG_WAITINGFORTASK) {
142 173 io_dontwantread(sock);
143 /* Our answers never are 0 vectors. Return an error. */ 174 cookie->flag &= ~STRUCT_HTTP_FLAG_WAITINGFORTASK;
144 if( !iovec_entries ) {
145 HTTPERROR_500;
146 } 175 }
147 176
148 /* Prepare space for http header */ 177 if (iovec_entries) {
149 header = malloc( SUCCESS_HTTP_HEADER_LENGTH + SUCCESS_HTTP_HEADER_LENGTH_CONTENT_ENCODING );
150 if( !header ) {
151 iovec_free( &iovec_entries, &iovector );
152 HTTPERROR_500;
153 }
154 178
155 if( cookie->flag & STRUCT_HTTP_FLAG_GZIP ) 179 if (cookie->flag & STRUCT_HTTP_FLAG_ZSTD)
156 header_size = sprintf( header, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Encoding: gzip\r\nContent-Length: %zd\r\n\r\n", size ); 180 encoding = "Content-Encoding: zstd\r\n";
157 else if( cookie->flag & STRUCT_HTTP_FLAG_BZIP2 ) 181 else if (cookie->flag & STRUCT_HTTP_FLAG_GZIP)
158 header_size = sprintf( header, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Encoding: bzip2\r\nContent-Length: %zd\r\n\r\n", size ); 182 encoding = "Content-Encoding: gzip\r\n";
159 else 183 else if (cookie->flag & STRUCT_HTTP_FLAG_BZIP2)
160 header_size = sprintf( header, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r\n", size ); 184 encoding = "Content-Encoding: bzip2\r\n";
161 185
162 if (!cookie->batch ) { 186 if (!(cookie->flag & STRUCT_HTTP_FLAG_CHUNKED))
163 cookie->batch = malloc( sizeof(io_batch) ); 187 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);
164 memset( cookie->batch, 0, sizeof(io_batch) ); 188 else {
165 cookie->batches = 1; 189 if (!(cookie->flag & STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER)) {
166 } 190 header_size =
167 iob_addbuf_free( cookie->batch, header, header_size ); 191 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);
192 cookie->flag |= STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER;
193 } else
194 header_size = asprintf(&header, "%zx\r\n", size);
195 }
196 if (!header) {
197 iovec_free(&iovec_entries, &iovector);
198 HTTPERROR_500;
199 }
168 200
169 /* Split huge iovectors into separate io_batches */ 201 if (!cookie->batch) {
170 for( i=0; i<iovec_entries; ++i ) { 202 cookie->batch = malloc(sizeof(io_batch));
171 io_batch *current = cookie->batch + cookie->batches - 1; 203 if (!cookie->batch || OT_IOB_INIT(cookie->batch) == -1) {
204 free(cookie->batch);
205 free(header);
206 iovec_free(&iovec_entries, &iovector);
207 HTTPERROR_500;
208 }
209 cookie->batches = 1;
210 }
211 current = cookie->batch + cookie->batches - 1;
212 iob_addbuf_free(current, header, header_size);
172 213
173 /* If the current batch's limit is reached, try to reallocate a new batch to work on */ 214 /* Split huge iovectors into separate io_batches */
174 if( current->bytesleft > OT_BATCH_LIMIT ) { 215 for (i = 0; i < (size_t)iovec_entries; ++i) {
175 io_batch * new_batch = realloc( current, (cookie->batches + 1) * sizeof(io_batch) ); 216 /* If the current batch's limit is reached, try to reallocate a new batch to work on */
176 if( new_batch ) { 217 if (current->bytesleft > OT_BATCH_LIMIT) {
177 cookie->batches++; 218 io_batch *new_batch = realloc(cookie->batch, (cookie->batches + 1) * sizeof(io_batch));
178 current = cookie->batch = new_batch; 219 if (new_batch) {
179 memset( current, 0, sizeof(io_batch) ); 220 cookie->batch = new_batch;
221 if (OT_IOB_INIT(current) != -1)
222 current = cookie->batch + cookie->batches++;
180 } 223 }
224 }
225 iob_addbuf_free(current, iovector[i].iov_base, iovector[i].iov_len);
181 } 226 }
227 free(iovector);
228 if (cookie->flag & STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER)
229 iob_addbuf(current, "\r\n", 2);
230 }
182 231
183 iob_addbuf_free( current, iovector[i].iov_base, iovector[i].iov_len ); 232 if ((cookie->flag & STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER) && cookie->batch && !is_partial) {
233 current = cookie->batch + cookie->batches - 1;
234 iob_addbuf(current, "0\r\n\r\n", 5);
235 cookie->flag &= ~STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER;
184 } 236 }
185 free( iovector );
186 237
187 /* writeable sockets timeout after 10 minutes */ 238 /* writeable sockets timeout after 10 minutes */
188 taia_now( &t ); taia_addsec( &t, &t, OT_CLIENT_TIMEOUT_SEND ); 239 taia_now(&t);
189 io_timeout( sock, t ); 240 taia_addsec(&t, &t, OT_CLIENT_TIMEOUT_SEND);
190 io_dontwantread( sock ); 241 io_timeout(sock, t);
191 io_wantwrite( sock ); 242 io_wantwrite(sock);
192 return 0; 243 return 0;
193} 244}
194 245
195static ssize_t http_handle_stats( const int64 sock, struct ot_workstruct *ws, char *read_ptr ) { 246static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) {
196static const ot_keywords keywords_main[] = 247 static const ot_keywords keywords_main[] = {{"mode", 1}, {"format", 2}, {"info_hash", 3}, {NULL, -3}};
197 { { "mode", 1 }, {"format", 2 }, { NULL, -3 } }; 248 static const ot_keywords keywords_mode[] = {{"peer", TASK_STATS_PEERS},
198static const ot_keywords keywords_mode[] = 249 {"conn", TASK_STATS_CONNS},
199 { { "peer", TASK_STATS_PEERS }, { "conn", TASK_STATS_CONNS }, { "scrp", TASK_STATS_SCRAPE }, { "udp4", TASK_STATS_UDP }, { "tcp4", TASK_STATS_TCP }, 250 {"scrp", TASK_STATS_SCRAPE},
200 { "busy", TASK_STATS_BUSY_NETWORKS }, { "torr", TASK_STATS_TORRENTS }, { "fscr", TASK_STATS_FULLSCRAPE }, 251 {"udp4", TASK_STATS_UDP},
201 { "s24s", TASK_STATS_SLASH24S }, { "tpbs", TASK_STATS_TPB }, { "herr", TASK_STATS_HTTPERRORS }, { "completed", TASK_STATS_COMPLETED }, 252 {"tcp4", TASK_STATS_TCP},
202 { "top100", TASK_STATS_TOP100 }, { "top10", TASK_STATS_TOP10 }, { "renew", TASK_STATS_RENEW }, { "syncs", TASK_STATS_SYNCS }, { "version", TASK_STATS_VERSION }, 253 {"busy", TASK_STATS_BUSY_NETWORKS},
203 { "everything", TASK_STATS_EVERYTHING }, { "statedump", TASK_FULLSCRAPE_TRACKERSTATE }, { "fulllog", TASK_STATS_FULLLOG }, 254 {"torr", TASK_STATS_TORRENTS},
204 { "woodpeckers", TASK_STATS_WOODPECKERS}, 255 {"fscr", TASK_STATS_FULLSCRAPE},
256 {"s24s", TASK_STATS_SLASH24S},
257 {"tpbs", TASK_STATS_TPB},
258 {"herr", TASK_STATS_HTTPERRORS},
259 {"completed", TASK_STATS_COMPLETED},
260 {"top100", TASK_STATS_TOP100},
261 {"top10", TASK_STATS_TOP10},
262 {"renew", TASK_STATS_RENEW},
263 {"syncs", TASK_STATS_SYNCS},
264 {"version", TASK_STATS_VERSION},
265 {"everything", TASK_STATS_EVERYTHING},
266 {"statedump", TASK_FULLSCRAPE_TRACKERSTATE},
267 {"fulllog", TASK_STATS_FULLLOG},
268 {"woodpeckers", TASK_STATS_WOODPECKERS},
205#ifdef WANT_LOG_NUMWANT 269#ifdef WANT_LOG_NUMWANT
206 { "numwants", TASK_STATS_NUMWANTS}, 270 {"numwants", TASK_STATS_NUMWANTS},
207#endif 271#endif
208 { NULL, -3 } }; 272 {NULL, -3}};
209static const ot_keywords keywords_format[] = 273 static const ot_keywords keywords_format[] = {{"bin", TASK_FULLSCRAPE_TPB_BINARY}, {"ben", TASK_FULLSCRAPE},
210 { { "bin", TASK_FULLSCRAPE_TPB_BINARY }, { "ben", TASK_FULLSCRAPE }, { "url", TASK_FULLSCRAPE_TPB_URLENCODED }, 274 {"url", TASK_FULLSCRAPE_TPB_URLENCODED}, {"txt", TASK_FULLSCRAPE_TPB_ASCII},
211 { "txt", TASK_FULLSCRAPE_TPB_ASCII }, { "txtp", TASK_FULLSCRAPE_TPB_ASCII_PLUS }, { NULL, -3 } }; 275 {"txtp", TASK_FULLSCRAPE_TPB_ASCII_PLUS}, {NULL, -3}};
212 276
213 int mode = TASK_STATS_PEERS, scanon = 1, format = 0; 277 struct http_data *cookie = io_getcookie(sock);
278 int mode = TASK_STATS_PEERS, scanon = 1, format = 0;
214 279
215#ifdef WANT_RESTRICT_STATS 280 /* A socket without one is a dead socket, which is what http_sendiovecdata
216 struct http_data *cookie = io_getcookie( sock ); 281 already answers 500 to. */
282 if (!cookie)
283 HTTPERROR_500;
217 284
218 if( !cookie || !accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_STAT ) ) 285#ifdef WANT_RESTRICT_STATS
286 if (!accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_STAT))
219 HTTPERROR_403_IP; 287 HTTPERROR_403_IP;
220#endif 288#endif
221 289
222 while( scanon ) { 290 while (scanon) {
223 switch( scan_find_keywords( keywords_main, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) { 291 switch (scan_find_keywords(keywords_main, &read_ptr, SCAN_SEARCHPATH_PARAM)) {
224 case -2: scanon = 0; break; /* TERMINATOR */ 292 case -2:
225 case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ 293 scanon = 0;
226 case -3: scan_urlencoded_skipvalue( &read_ptr ); break; 294 break; /* TERMINATOR */
227 case 1: /* matched "mode" */ 295 case -1:
228 if( ( mode = scan_find_keywords( keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; 296 HTTPERROR_400_PARAM; /* PARSE ERROR */
297 case -3:
298 scan_urlencoded_skipvalue(&read_ptr);
299 break;
300 case 1: /* matched "mode" */
301 if ((mode = scan_find_keywords(keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0)
302 HTTPERROR_400_PARAM;
229 break; 303 break;
230 case 2: /* matched "format" */ 304 case 2: /* matched "format" */
231 if( ( format = scan_find_keywords( keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; 305 if ((format = scan_find_keywords(keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0)
306 HTTPERROR_400_PARAM;
232 break; 307 break;
308 case 3:
309 HTTPERROR_400_PARAM; /* If the stats URL was mistakenly added as announce URL, return a 400 */
233 } 310 }
234 } 311 }
235 312
236#ifdef WANT_FULLSCRAPE 313#ifdef WANT_FULLSCRAPE
237 if( mode == TASK_FULLSCRAPE_TRACKERSTATE ) { 314 if (mode == TASK_FULLSCRAPE_TRACKERSTATE) {
238 format = mode; mode = TASK_STATS_TPB; 315 format = mode;
316 mode = TASK_STATS_TPB;
239 } 317 }
240 318
241 if( mode == TASK_STATS_TPB ) { 319 if (mode == TASK_STATS_TPB) {
242 struct http_data* cookie = io_getcookie( sock );
243 tai6464 t; 320 tai6464 t;
244#ifdef WANT_COMPRESSION_GZIP 321#ifdef WANT_COMPRESSION_GZIP
245 ws->request[ws->request_size] = 0; 322 ws->request[ws->request_size - 1] = 0;
246#ifdef WANT_COMPRESSION_GZIP_ALWAYS 323#ifndef WANT_COMPRESSION_GZIP_ALWAYS
247 if( strstr( read_ptr - 1, "gzip" ) ) { 324 if (strstr(read_ptr - 1, "gzip")) {
248#endif 325#endif
249 cookie->flag |= STRUCT_HTTP_FLAG_GZIP; 326 cookie->flag |= STRUCT_HTTP_FLAG_GZIP;
250 format |= TASK_FLAG_GZIP; 327 format |= TASK_FLAG_GZIP;
251#ifdef WANT_COMPRESSION_GZIP_ALWAYS 328#ifndef WANT_COMPRESSION_GZIP_ALWAYS
252 } 329 }
253#endif 330#endif
254#endif 331#endif
255 /* Pass this task to the worker thread */ 332 /* Pass this task to the worker thread */
256 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK; 333 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED;
257 334
258 /* Clients waiting for us should not easily timeout */ 335 /* Clients waiting for us should not easily timeout */
259 taia_uint( &t, 0 ); io_timeout( sock, t ); 336 taia_uint(&t, 0);
260 fullscrape_deliver( sock, format ); 337 io_timeout(sock, t);
261 io_dontwantread( sock ); 338 fullscrape_deliver(sock, format);
339 io_dontwantread(sock);
262 return ws->reply_size = -2; 340 return ws->reply_size = -2;
263 } 341 }
264#endif 342#endif
265 343
266 /* default format for now */ 344 /* default format for now */
267 if( ( mode & TASK_CLASS_MASK ) == TASK_STATS ) { 345 if ((mode & TASK_CLASS_MASK) == TASK_STATS) {
268 tai6464 t; 346 tai6464 t;
347
348 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK;
349
269 /* Complex stats also include expensive memory debugging tools */ 350 /* Complex stats also include expensive memory debugging tools */
270 taia_uint( &t, 0 ); io_timeout( sock, t ); 351 taia_uint(&t, 0);
271 stats_deliver( sock, mode ); 352 io_timeout(sock, t);
353 stats_deliver(sock, mode);
272 return ws->reply_size = -2; 354 return ws->reply_size = -2;
273 } 355 }
274 356
275 /* Simple stats can be answerred immediately */ 357 /* Simple stats can be answerred immediately */
276 return ws->reply_size = return_stats_for_tracker( ws->reply, mode, 0 ); 358 return ws->reply_size = return_stats_for_tracker(ws->reply, mode, 0);
277} 359}
278 360
279#ifdef WANT_MODEST_FULLSCRAPES 361#ifdef WANT_MODEST_FULLSCRAPES
280static pthread_mutex_t g_modest_fullscrape_mutex = PTHREAD_MUTEX_INITIALIZER; 362static pthread_mutex_t g_modest_fullscrape_mutex = PTHREAD_MUTEX_INITIALIZER;
281static ot_vector g_modest_fullscrape_timeouts; 363static ot_vector g_modest_fullscrape_timeouts;
282typedef struct { ot_ip6 ip; ot_time last_fullscrape; } ot_scrape_log; 364typedef struct {
365 ot_ip6 ip;
366 ot_time last_fullscrape;
367} ot_scrape_log;
283#endif 368#endif
284 369
285#ifdef WANT_FULLSCRAPE 370#ifdef WANT_FULLSCRAPE
286static ssize_t http_handle_fullscrape( const int64 sock, struct ot_workstruct *ws ) { 371static ssize_t http_handle_fullscrape(const int64_t sock, struct ot_workstruct *ws) {
287 struct http_data* cookie = io_getcookie( sock ); 372 struct http_data *cookie = io_getcookie(sock);
288 int format = 0; 373 int format = 0;
289 tai6464 t; 374 tai6464 t;
290 375
291#ifdef WANT_MODEST_FULLSCRAPES 376#ifdef WANT_MODEST_FULLSCRAPES
292 { 377 {
293 ot_scrape_log this_peer, *new_peer; 378 ot_scrape_log this_peer, *new_peer;
294 int exactmatch; 379 int exactmatch;
295 memcpy( this_peer.ip, cookie->ip, sizeof(ot_ip6)); 380 memcpy(this_peer.ip, cookie->ip, sizeof(ot_ip6));
296 this_peer.last_fullscrape = g_now_seconds; 381 this_peer.last_fullscrape = g_now_seconds;
297 pthread_mutex_lock(&g_modest_fullscrape_mutex); 382 pthread_mutex_lock(&g_modest_fullscrape_mutex);
298 new_peer = vector_find_or_insert( &g_modest_fullscrape_timeouts, &this_peer, sizeof(ot_scrape_log), sizeof(ot_ip6), &exactmatch ); 383 new_peer = vector_find_or_insert(&g_modest_fullscrape_timeouts, &this_peer, sizeof(ot_scrape_log), sizeof(ot_ip6), &exactmatch);
299 if( !new_peer ) { 384 if (!new_peer) {
300 pthread_mutex_unlock(&g_modest_fullscrape_mutex); 385 pthread_mutex_unlock(&g_modest_fullscrape_mutex);
301 HTTPERROR_500; 386 HTTPERROR_500;
302 } 387 }
303 if( exactmatch && ( this_peer.last_fullscrape - new_peer->last_fullscrape ) < OT_MODEST_PEER_TIMEOUT ) { 388 if (exactmatch && (this_peer.last_fullscrape - new_peer->last_fullscrape) < OT_MODEST_PEER_TIMEOUT) {
304 pthread_mutex_unlock(&g_modest_fullscrape_mutex); 389 pthread_mutex_unlock(&g_modest_fullscrape_mutex);
305 HTTPERROR_402_NOTMODEST; 390 HTTPERROR_402_NOTMODEST;
306 } 391 }
307 memcpy( new_peer, &this_peer, sizeof(ot_scrape_log)); 392 memcpy(new_peer, &this_peer, sizeof(ot_scrape_log));
308 pthread_mutex_unlock(&g_modest_fullscrape_mutex); 393 pthread_mutex_unlock(&g_modest_fullscrape_mutex);
309 } 394 }
310#endif 395#endif
311 396
397
398#if defined(WANT_COMPRESSION_GZIP) || defined(WANT_COMPRESSION_ZSTD)
399 ws->request[ws->request_size - 1] = 0;
312#ifdef WANT_COMPRESSION_GZIP 400#ifdef WANT_COMPRESSION_GZIP
313 ws->request[ws->request_size-1] = 0; 401 if (strstr(ws->request, "gzip")) {
314 if( strstr( ws->request, "gzip" ) ) {
315 cookie->flag |= STRUCT_HTTP_FLAG_GZIP; 402 cookie->flag |= STRUCT_HTTP_FLAG_GZIP;
316 format = TASK_FLAG_GZIP; 403 format |= TASK_FLAG_GZIP;
317 stats_issue_event( EVENT_FULLSCRAPE_REQUEST_GZIP, 0, (uintptr_t)cookie->ip ); 404 }
318 } else 405#endif
406#ifdef WANT_COMPRESSION_ZSTD
407 if (strstr(ws->request, "zstd")) {
408 cookie->flag |= STRUCT_HTTP_FLAG_ZSTD;
409 format |= TASK_FLAG_ZSTD;
410 }
411#endif
412
413#if defined(WANT_COMPRESSION_ZSTD) && defined(WANT_COMPRESSION_ZSTD_ALWAYS)
414 cookie->flag |= STRUCT_HTTP_FLAG_ZSTD;
415 format |= TASK_FLAG_ZSTD;
416#endif
417
418#if defined(WANT_COMPRESSION_GZIP) && defined(WANT_COMPRESSION_GZIP_ALWAYS)
419 cookie->flag |= STRUCT_HTTP_FLAG_GZIP;
420 format |= TASK_FLAG_GZIP;
319#endif 421#endif
320 stats_issue_event( EVENT_FULLSCRAPE_REQUEST, 0, (uintptr_t)cookie->ip ); 422#endif
423
424 stats_issue_event(EVENT_FULLSCRAPE_REQUEST, 0, (uintptr_t)cookie->ip);
321 425
322#ifdef _DEBUG_HTTPERROR 426#ifdef _DEBUG_HTTPERROR
323 fprintf( stderr, "%s", ws->debugbuf ); 427 fprintf(stderr, "%s", ws->debugbuf);
324#endif 428#endif
325 429
326 /* Pass this task to the worker thread */ 430 /* Pass this task to the worker thread */
327 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK; 431 cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED;
328 /* Clients waiting for us should not easily timeout */ 432 /* Clients waiting for us should not easily timeout */
329 taia_uint( &t, 0 ); io_timeout( sock, t ); 433 taia_uint(&t, 0);
330 fullscrape_deliver( sock, TASK_FULLSCRAPE | format ); 434 io_timeout(sock, t);
331 io_dontwantread( sock ); 435 fullscrape_deliver(sock, TASK_FULLSCRAPE | format);
436 io_dontwantread(sock);
332 return ws->reply_size = -2; 437 return ws->reply_size = -2;
333} 438}
334#endif 439#endif
335 440
336static ssize_t http_handle_scrape( const int64 sock, struct ot_workstruct *ws, char *read_ptr ) { 441static ssize_t http_handle_scrape(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) {
337 static const ot_keywords keywords_scrape[] = { { "info_hash", 1 }, { NULL, -3 } }; 442 static const ot_keywords keywords_scrape[] = {{"info_hash", 1}, {NULL, -3}};
338 443
339 ot_hash * multiscrape_buf = (ot_hash*)ws->request; 444 ot_hash *multiscrape_buf = (ot_hash *)ws->request;
340 int scanon = 1, numwant = 0; 445 int scanon = 1;
446 size_t numwant = 0;
341 447
342 /* This is to hack around stupid clients that send "scrape ?info_hash" */ 448 /* This is to hack around stupid clients that send "scrape ?info_hash" */
343 if( read_ptr[-1] != '?' ) { 449 if (read_ptr[-1] != '?') {
344 while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr; 450 while ((*read_ptr != '?') && (*read_ptr != '\n'))
345 if( *read_ptr == '\n' ) HTTPERROR_400_PARAM; 451 ++read_ptr;
452 if (*read_ptr == '\n')
453 HTTPERROR_400_PARAM;
346 ++read_ptr; 454 ++read_ptr;
347 } 455 }
348 456
349 while( scanon ) { 457 while (scanon) {
350 switch( scan_find_keywords( keywords_scrape, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) { 458 switch (scan_find_keywords(keywords_scrape, &read_ptr, SCAN_SEARCHPATH_PARAM)) {
351 case -2: scanon = 0; break; /* TERMINATOR */ 459 case -2:
352 default: HTTPERROR_400_PARAM; /* PARSE ERROR */ 460 scanon = 0;
353 case -3: scan_urlencoded_skipvalue( &read_ptr ); break; 461 break; /* TERMINATOR */
354 case 1: /* matched "info_hash" */ 462 default:
463 HTTPERROR_400_PARAM; /* PARSE ERROR */
464 case -3:
465 scan_urlencoded_skipvalue(&read_ptr);
466 break;
467 case 1: /* matched "info_hash" */
355 /* ignore this, when we have less than 20 bytes */ 468 /* ignore this, when we have less than 20 bytes */
356 if( scan_urlencoded_query( &read_ptr, (char*)(multiscrape_buf + numwant++), SCAN_SEARCHPATH_VALUE ) != (ssize_t)sizeof(ot_hash) ) 469 if (scan_urlencoded_query(&read_ptr, (char *)(multiscrape_buf + numwant++), SCAN_SEARCHPATH_VALUE) != (ssize_t)sizeof(ot_hash))
357 HTTPERROR_400_PARAM; 470 HTTPERROR_400_PARAM;
358 break; 471 break;
359 } 472 }
360 } 473 }
361 474
362 /* No info_hash found? Inform user */ 475 /* No info_hash found? Inform user */
363 if( !numwant ) HTTPERROR_400_PARAM; 476 if (!numwant)
477 HTTPERROR_400_PARAM;
364 478
365 /* Limit number of hashes to process */ 479 /* Limit number of hashes to process */
366 if( numwant > OT_MAXMULTISCRAPE_COUNT ) 480 if (numwant > OT_MAXMULTISCRAPE_COUNT)
367 numwant = OT_MAXMULTISCRAPE_COUNT; 481 numwant = OT_MAXMULTISCRAPE_COUNT;
368 482
369 /* Enough for http header + whole scrape string */ 483 /* Enough for http header + whole scrape string */
370 ws->reply_size = return_tcp_scrape_for_torrent( multiscrape_buf, numwant, ws->reply ); 484 ws->reply_size = return_tcp_scrape_for_torrent((const ot_hash *)multiscrape_buf, numwant, ws->reply);
371 stats_issue_event( EVENT_SCRAPE, FLAG_TCP, ws->reply_size ); 485 stats_issue_event(EVENT_SCRAPE, FLAG_TCP, ws->reply_size);
372 return ws->reply_size; 486 return ws->reply_size;
373} 487}
374 488
375#ifdef WANT_LOG_NUMWANT 489#ifdef WANT_LOG_NUMWANT
376 unsigned long long numwants[201]; 490unsigned long long numwants[201];
377#endif 491#endif
378 492
379#if defined( WANT_KEEPALIVE ) || defined( WANT_IP_FROM_PROXY ) 493#if defined(WANT_KEEPALIVE) || defined(WANT_IP_FROM_PROXY)
380static char* http_header( char *data, size_t byte_count, char *header ) { 494static char *http_header(char *data, size_t byte_count, char *header) {
381 size_t i; 495 size_t i;
382 long sl = strlen( header ); 496 long sl = strlen(header);
383 for( i = 0; i + sl + 2 < byte_count; ++i ) { 497 for (i = 0; i + sl + 2 < byte_count; ++i) {
384 if( data[i] != '\n' || data[ i + sl + 1] != ':' ) continue; 498 if (data[i] != '\n' || data[i + sl + 1] != ':')
385 if( !case_equalb( data + i + 1, sl, header ) ) continue; 499 continue;
500 if (strncasecmp(data + i + 1, header, sl))
501 continue;
386 data += i + sl + 2; 502 data += i + sl + 2;
387 while( *data == ' ' || *data == '\t' ) ++data; 503 while (*data == ' ' || *data == '\t')
504 ++data;
388 return data; 505 return data;
389 } 506 }
390 return 0; 507 return 0;
391} 508}
392#endif 509#endif
393 510
394static ot_keywords keywords_announce[] = { { "port", 1 }, { "left", 2 }, { "event", 3 }, { "numwant", 4 }, { "compact", 5 }, { "compact6", 5 }, { "info_hash", 6 }, 511static ot_keywords keywords_announce[] = {{"port", 1}, {"left", 2}, {"event", 3}, {"numwant", 4}, {"compact", 5}, {"compact6", 5}, {"info_hash", 6},
395#ifdef WANT_IP_FROM_QUERY_STRING 512#ifdef WANT_IP_FROM_QUERY_STRING
396{ "ip", 7 }, 513 {"ip", 7},
397#endif 514#endif
398#ifdef WANT_FULLLOG_NETWORKS 515#ifdef WANT_FULLLOG_NETWORKS
399{ "lognet", 8 }, 516 {"lognet", 8},
400#endif 517#endif
401{ "peer_id", 9 }, 518 {"peer_id", 9}, {NULL, -3}};
402{ NULL, -3 } }; 519static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}};
403static ot_keywords keywords_announce_event[] = { { "completed", 1 }, { "stopped", 2 }, { NULL, -3 } }; 520static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) {
404static ssize_t http_handle_announce( const int64 sock, struct ot_workstruct *ws, char *read_ptr ) { 521 size_t numwant;
405 int numwant, tmp, scanon; 522 int tmp, scanon;
406 unsigned short port = 0; 523 unsigned short port = 0;
407 char *write_ptr; 524 char *write_ptr;
408 ssize_t len; 525 ssize_t len;
409 struct http_data *cookie = io_getcookie( sock ); 526 struct http_data *cookie = io_getcookie(sock);
410 527
411 /* This is to hack around stupid clients that send "announce ?info_hash" */ 528 /* This is to hack around stupid clients that send "announce ?info_hash" */
412 if( read_ptr[-1] != '?' ) { 529 if (read_ptr[-1] != '?') {
413 while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr; 530 while ((*read_ptr != '?') && (*read_ptr != '\n'))
414 if( *read_ptr == '\n' ) HTTPERROR_400_PARAM; 531 ++read_ptr;
532 if (*read_ptr == '\n')
533 HTTPERROR_400_PARAM;
415 ++read_ptr; 534 ++read_ptr;
416 } 535 }
417 536
418#ifdef WANT_IP_FROM_PROXY 537#ifdef WANT_IP_FROM_PROXY
419 if( accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_PROXY ) ) { 538 if (accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_PROXY)) {
420 ot_ip6 proxied_ip; 539 ot_ip6 proxied_ip;
421 char *fwd = http_header( ws->request, ws->header_size, "x-forwarded-for" ); 540 char *fwd = http_header(ws->request, ws->header_size, "x-forwarded-for");
422 if( fwd && scan_ip6( fwd, proxied_ip ) ) 541 if (fwd && ip_scan(fwd, proxied_ip)) {
423 OT_SETIP( &ws->peer, proxied_ip ); 542 OT_SETIP(ws->peer, proxied_ip);
424 else 543 } else
425 OT_SETIP( &ws->peer, cookie->ip ); 544 OT_SETIP(ws->peer, cookie->ip);
426 } else 545 } else
427#endif 546#endif
428 OT_SETIP( &ws->peer, cookie->ip ); 547 OT_SETIP(ws->peer, cookie->ip);
429 548
430 ws->peer_id = NULL; 549 ws->peer_id = NULL;
431 ws->hash = NULL; 550 ws->hash = NULL;
432 551
433 OT_SETPORT( &ws->peer, &port ); 552 OT_SETPORT(ws->peer, &port);
434 OT_PEERFLAG( &ws->peer ) = 0; 553 OT_PEERFLAG(ws->peer) = 0;
435 numwant = 50; 554 numwant = 50;
436 scanon = 1; 555 scanon = 1;
437 556
438 while( scanon ) { 557 while (scanon) {
439 switch( scan_find_keywords(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) { 558 switch (scan_find_keywords(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM)) {
440 case -2: scanon = 0; break; /* TERMINATOR */ 559 case -2:
441 case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ 560 scanon = 0;
442 case -3: scan_urlencoded_skipvalue( &read_ptr ); break; 561 break; /* TERMINATOR */
562 case -1:
563 HTTPERROR_400_PARAM; /* PARSE ERROR */
564 case -3:
565 scan_urlencoded_skipvalue(&read_ptr);
566 break;
443 case 1: /* matched "port" */ 567 case 1: /* matched "port" */
444 len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); 568 len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE);
445 if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) || ( tmp > 0xffff ) ) HTTPERROR_400_PARAM; 569 if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp) || (tmp > 0xffff))
446 port = htons( tmp ); OT_SETPORT( &ws->peer, &port ); 570 HTTPERROR_400_PARAM;
571 port = htons(tmp);
572 OT_SETPORT(&ws->peer, &port);
447 break; 573 break;
448 case 2: /* matched "left" */ 574 case 2: /* matched "left" */
449 if( ( len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; 575 if ((len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0)
450 if( scan_fixed_int( write_ptr, len, &tmp ) ) tmp = 0; 576 HTTPERROR_400_PARAM;
451 if( !tmp ) OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_SEEDING; 577 if (scan_fixed_int(write_ptr, len, &tmp))
578 tmp = 0;
579 if (!tmp)
580 OT_PEERFLAG(&ws->peer) |= PEER_FLAG_SEEDING;
452 break; 581 break;
453 case 3: /* matched "event" */ 582 case 3: /* matched "event" */
454 switch( scan_find_keywords( keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) { 583 switch (scan_find_keywords(keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE)) {
455 case -1: HTTPERROR_400_PARAM; 584 case -1:
456 case 1: /* matched "completed" */ 585 HTTPERROR_400_PARAM;
457 OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_COMPLETED; 586 case 1: /* matched "completed" */
458 break; 587 OT_PEERFLAG(&ws->peer) |= PEER_FLAG_COMPLETED;
459 case 2: /* matched "stopped" */ 588 break;
460 OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_STOPPED; 589 case 2: /* matched "stopped" */
461 break; 590 OT_PEERFLAG(&ws->peer) |= PEER_FLAG_STOPPED;
462 default: 591 break;
463 break; 592 default:
593 break;
464 } 594 }
465 break; 595 break;
466 case 4: /* matched "numwant" */ 596 case 4: /* matched "numwant" */
467 len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); 597 len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE);
468 if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &numwant ) ) HTTPERROR_400_PARAM; 598 if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp))
469 if( numwant < 0 ) numwant = 50; 599 HTTPERROR_400_PARAM;
470 if( numwant > 200 ) numwant = 200; 600 if (tmp < 0)
601 tmp = 50;
602 if (tmp > 200)
603 tmp = 200;
604 numwant = tmp;
471 break; 605 break;
472 case 5: /* matched "compact" */ 606 case 5: /* matched "compact" */
473 len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); 607 len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE);
474 if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) ) HTTPERROR_400_PARAM; 608 if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp))
475 if( !tmp ) HTTPERROR_400_COMPACT; 609 HTTPERROR_400_PARAM;
610 if (!tmp)
611 HTTPERROR_400_COMPACT;
476 break; 612 break;
477 case 6: /* matched "info_hash" */ 613 case 6: /* matched "info_hash" */
478 if( ws->hash ) HTTPERROR_400_DOUBLEHASH; 614 if (ws->hash)
615 HTTPERROR_400_DOUBLEHASH;
479 /* ignore this, when we have less than 20 bytes */ 616 /* ignore this, when we have less than 20 bytes */
480 if( scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM; 617 if (scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20)
481 ws->hash = (ot_hash*)write_ptr; 618 HTTPERROR_400_PARAM;
619 ws->hash = (ot_hash *)write_ptr;
482 break; 620 break;
483#ifdef WANT_IP_FROM_QUERY_STRING 621#ifdef WANT_IP_FROM_QUERY_STRING
484 case 7: /* matched "ip" */ 622 case 7: /* matched "ip" */
485 { 623 {
486 char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply+16; 624 char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16;
487 len = scan_urlencoded_query( &read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE ); 625 len = scan_urlencoded_query(&read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE);
488 tmp_buf2[len] = 0; 626 tmp_buf2[len] = 0;
489 if( ( len <= 0 ) || !scan_ip6( tmp_buf2, tmp_buf1 ) ) HTTPERROR_400_PARAM; 627 if ((len <= 0) || !ip_scan(tmp_buf2, tmp_buf1))
490 OT_SETIP( &ws->peer, tmp_buf1 ); 628 HTTPERROR_400_PARAM;
491 } 629 OT_SETIP(&ws->peer, tmp_buf1);
492 break; 630 } break;
493#endif 631#endif
494#ifdef WANT_FULLLOG_NETWORKS 632#ifdef WANT_FULLLOG_NETWORKS
495 case 8: /* matched "lognet" */ 633 case 8: /* matched "lognet" */
496 { 634 {
497 //if( accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_STAT ) ) { 635 // if( accesslist_is_blessed( cookie->ip, OT_PERMISSION_MAY_STAT ) ) {
498 char *tmp_buf = ws->reply; 636 char *tmp_buf = ws->reply;
499 ot_net net; 637 ot_net net;
500 signed short parsed, bits; 638 uint64_t value;
639 signed short parsed, bits;
501 640
502 len = scan_urlencoded_query( &read_ptr, tmp_buf, SCAN_SEARCHPATH_VALUE ); 641 len = scan_urlencoded_query(&read_ptr, tmp_buf, SCAN_SEARCHPATH_VALUE);
503 tmp_buf[len] = 0; 642 tmp_buf[len] = 0;
504 if( len <= 0 ) HTTPERROR_400_PARAM; 643 if (len <= 0)
505 if( *tmp_buf == '-' ) { 644 HTTPERROR_400_PARAM;
506 loglist_reset( ); 645 if (*tmp_buf == '-') {
507 return ws->reply_size = sprintf( ws->reply, "Successfully removed.\n" ); 646 loglist_reset();
508 } 647 return ws->reply_size = sprintf(ws->reply, "Successfully removed.\n");
509 parsed = scan_ip6( tmp_buf, net.address );
510 if( !parsed ) HTTPERROR_400_PARAM;
511 if( tmp_buf[parsed++] != '/' )
512 bits = 128;
513 else {
514 parsed = scan_short( tmp_buf + parsed, &bits );
515 if( !parsed ) HTTPERROR_400_PARAM;
516 if( ip6_isv4mapped( net.address ) )
517 bits += 96;
518 }
519 net.bits = bits;
520 loglist_add_network( &net );
521 return ws->reply_size = sprintf( ws->reply, "Successfully added.\n" );
522 //}
523 } 648 }
524 break; 649 parsed = ip_scan(tmp_buf, net.address);
650 if (!parsed)
651 HTTPERROR_400_PARAM;
652 if (tmp_buf[parsed++] != '/')
653 bits = 128;
654 else {
655 parsed = scan_udec(tmp_buf + parsed, &value, 128);
656 if (!parsed)
657 HTTPERROR_400_PARAM;
658 bits = (signed short)value;
659 if (ip_is_v4mapped(net.address))
660 bits += 96;
661 }
662 net.bits = bits;
663 loglist_add_network(&net);
664 return ws->reply_size = sprintf(ws->reply, "Successfully added.\n");
665 //}
666 } break;
525#endif 667#endif
526 case 9: /* matched "peer_id" */ 668 case 9: /* matched "peer_id" */
527 /* ignore this, when we have less than 20 bytes */ 669 /* ignore this, when we have less than 20 bytes */
528 if( scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM; 670 if (scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20)
529 ws->peer_id = write_ptr; 671 HTTPERROR_400_PARAM;
530 break; 672 ws->peer_id = write_ptr;
673 break;
531 } 674 }
532 } 675 }
533 676
@@ -540,100 +683,107 @@ static ssize_t http_handle_announce( const int64 sock, struct ot_workstruct *ws,
540 */ 683 */
541 684
542 /* Scanned whole query string */ 685 /* Scanned whole query string */
543 if( !ws->hash ) 686 if (!ws->hash)
544 return ws->reply_size = sprintf( ws->reply, "d14:failure reason80:Your client forgot to send your torrent's info_hash. Please upgrade your client.e" ); 687 return ws->reply_size = sprintf(ws->reply, "d14:failure reason80:Your client forgot to send your torrent's info_hash. Please upgrade your client.e");
545 688
546 if( OT_PEERFLAG( &ws->peer ) & PEER_FLAG_STOPPED ) 689 if (OT_PEERFLAG(&ws->peer) & PEER_FLAG_STOPPED)
547 ws->reply_size = remove_peer_from_torrent( FLAG_TCP, ws ); 690 ws->reply_size = remove_peer_from_torrent(FLAG_TCP, ws);
548 else 691 else
549 ws->reply_size = add_peer_to_torrent_and_return_peers( FLAG_TCP, ws, numwant ); 692 ws->reply_size = add_peer_to_torrent_and_return_peers(FLAG_TCP, ws, numwant);
550 693
551 stats_issue_event( EVENT_ANNOUNCE, FLAG_TCP, ws->reply_size); 694 stats_issue_event(EVENT_ANNOUNCE, FLAG_TCP, ws->reply_size);
552 return ws->reply_size; 695 return ws->reply_size;
553} 696}
554 697
555ssize_t http_handle_request( const int64 sock, struct ot_workstruct *ws ) { 698ssize_t http_handle_request(const int64_t sock, struct ot_workstruct *ws) {
556 ssize_t reply_off, len; 699 ssize_t reply_off, len;
557 char *read_ptr = ws->request, *write_ptr; 700 char *read_ptr = ws->request, *write_ptr;
558 701
559#ifdef WANT_FULLLOG_NETWORKS 702#ifdef WANT_FULLLOG_NETWORKS
560 struct http_data *cookie = io_getcookie( sock ); 703 struct http_data *cookie = io_getcookie(sock);
561 if( loglist_check_address( cookie->ip ) ) { 704 if (loglist_check_address(cookie->ip)) {
562 ot_log *log = malloc( sizeof( ot_log ) ); 705 ot_log *log = malloc(sizeof(ot_log));
563 if( log ) { 706 if (log) {
564 log->size = ws->request_size; 707 log->size = ws->request_size;
565 log->data = malloc( ws->request_size ); 708 log->data = malloc(ws->request_size);
566 log->next = 0; 709 log->next = 0;
567 log->time = g_now_seconds; 710 log->time = g_now_seconds;
568 memcpy( log->ip, cookie->ip, sizeof(ot_ip6)); 711 memcpy(log->ip, cookie->ip, sizeof(ot_ip6));
569 if( log->data ) { 712 if (log->data) {
570 memcpy( log->data, ws->request, ws->request_size ); 713 memcpy(log->data, ws->request, ws->request_size);
571 if( !g_logchain_first ) 714 if (!g_logchain_first)
572 g_logchain_first = g_logchain_last = log; 715 g_logchain_first = g_logchain_last = log;
573 else { 716 else {
574 g_logchain_last->next = log; 717 g_logchain_last->next = log;
575 g_logchain_last = log; 718 g_logchain_last = log;
576 } 719 }
577 } else 720 } else
578 free( log ); 721 free(log);
579 } 722 }
580 } 723 }
581#endif 724#endif
582 725
583#ifdef _DEBUG_HTTPERROR 726#ifdef _DEBUG_HTTPERROR
584 reply_off = ws->request_size; 727 reply_off = ws->request_size;
585 if( ws->request_size >= G_DEBUGBUF_SIZE ) 728 if (ws->request_size >= G_DEBUGBUF_SIZE)
586 reply_off = G_DEBUGBUF_SIZE - 1; 729 reply_off = G_DEBUGBUF_SIZE - 1;
587 memcpy( ws->debugbuf, ws->request, reply_off ); 730 memcpy(ws->debugbuf, ws->request, reply_off);
588 ws->debugbuf[ reply_off ] = 0; 731 ws->debugbuf[reply_off] = 0;
589#endif 732#endif
590 733
591 /* Tell subroutines where to put reply data */ 734 /* Tell subroutines where to put reply data */
592 ws->reply = ws->outbuf + SUCCESS_HTTP_HEADER_LENGTH; 735 ws->reply = ws->outbuf + SUCCESS_HTTP_HEADER_LENGTH;
593 736
594 /* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */ 737 /* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */
595 if( memcmp( read_ptr, "GET /", 5) ) HTTPERROR_400; 738 if (memcmp(read_ptr, "GET /", 5))
739 HTTPERROR_400;
596 740
597 /* Skip leading '/' */ 741 /* Skip leading '/' */
598 for( read_ptr+=4; *read_ptr == '/'; ++read_ptr); 742 for (read_ptr += 4; *read_ptr == '/'; ++read_ptr)
743 ;
599 744
600 /* Try to parse the request. 745 /* Try to parse the request.
601 In reality we abandoned requiring the url to be correct. This now 746 In reality we abandoned requiring the url to be correct. This now
602 only decodes url encoded characters, we check for announces and 747 only decodes url encoded characters, we check for announces and
603 scrapes by looking for "a*" or "sc" */ 748 scrapes by looking for "a*" or "sc" */
604 len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_PATH ); 749 len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_PATH);
605 750
606 /* If parsing returned an error, leave with not found */ 751 /* If parsing returned an error, leave with not found */
607 if( g_redirecturl && ( len == -2 ) ) HTTPERROR_302; 752 if (g_redirecturl && (len == -2))
608 if( len <= 0 ) HTTPERROR_404; 753 HTTPERROR_302;
754 if (len <= 0)
755 HTTPERROR_404;
609 756
610 /* This is the hardcore match for announce*/ 757 /* This is the hardcore match for announce*/
611 if( ( *write_ptr == 'a' ) || ( *write_ptr == '?' ) ) 758 if ((*write_ptr == 'a') || (*write_ptr == '?'))
612 http_handle_announce( sock, ws, read_ptr ); 759 http_handle_announce(sock, ws, read_ptr);
613#ifdef WANT_FULLSCRAPE 760#ifdef WANT_FULLSCRAPE
614 else if( !memcmp( write_ptr, "scrape HTTP/", 12 ) ) 761 else if (!memcmp(write_ptr, "scrape HTTP/", 12))
615 http_handle_fullscrape( sock, ws ); 762 http_handle_fullscrape(sock, ws);
616#endif 763#endif
617 /* This is the hardcore match for scrape */ 764 /* This is the hardcore match for scrape */
618 else if( !memcmp( write_ptr, "sc", 2 ) ) 765 else if (!memcmp(write_ptr, "sc", 2))
619 http_handle_scrape( sock, ws, read_ptr ); 766 http_handle_scrape(sock, ws, read_ptr);
620 /* All the rest is matched the standard way */ 767 /* All the rest is matched the standard way */
621 else if( len == g_stats_path_len && !memcmp( write_ptr, g_stats_path, len ) ) 768 else if (len == g_stats_path_len && !memcmp(write_ptr, g_stats_path, len))
622 http_handle_stats( sock, ws, read_ptr ); 769 http_handle_stats(sock, ws, read_ptr);
623 else 770 else
624 HTTPERROR_404; 771 HTTPERROR_404;
625 772
626 /* Find out if the client wants to keep this connection alive */ 773 /* Find out if the client wants to keep this connection alive */
627 ws->keep_alive = 0; 774 ws->keep_alive = 0;
628#ifdef WANT_KEEPALIVE 775#ifdef WANT_KEEPALIVE
629 read_ptr=http_header( ws->request, ws->header_size, "connection"); 776 read_ptr = http_header(ws->request, ws->header_size, "connection");
630 if( read_ptr && ( *read_ptr == 'K' || *read_ptr == 'k' ) ) ws->keep_alive = 1; 777 if (read_ptr && (*read_ptr == 'K' || *read_ptr == 'k'))
778 ws->keep_alive = 1;
631#endif 779#endif
632 780
633 /* If routines handled sending themselves, just return */ 781 /* If routines handled sending themselves, just return */
634 if( ws->reply_size == -2 ) return 0; 782 if (ws->reply_size == -2)
783 return 0;
635 /* If routine failed, let http error take over */ 784 /* If routine failed, let http error take over */
636 if( ws->reply_size <= 0 ) HTTPERROR_500; 785 if (ws->reply_size <= 0)
786 HTTPERROR_500;
637 787
638 /* This one is rather ugly, so I take you step by step through it. 788 /* This one is rather ugly, so I take you step by step through it.
639 789
@@ -642,18 +792,16 @@ ssize_t http_handle_request( const int64 sock, struct ot_workstruct *ws ) {
642 plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for its expansion and calculate 792 plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for its expansion and calculate
643 the space NOT needed to expand in reply_off 793 the space NOT needed to expand in reply_off
644 */ 794 */
645 reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf( ws->outbuf, 0, "%zd", ws->reply_size ); 795 reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf(ws->outbuf, 0, "%zd", ws->reply_size);
646 ws->reply = ws->outbuf + reply_off; 796 ws->reply = ws->outbuf + reply_off;
647 797
648 /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete 798 /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete
649 packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */ 799 packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */
650 ws->reply_size += 1 + sprintf( ws->reply, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", ws->reply_size ); 800 ws->reply_size += 1 + sprintf(ws->reply, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", ws->reply_size);
651 801
652 /* 3. Finally we join both blocks neatly */ 802 /* 3. Finally we join both blocks neatly */
653 ws->outbuf[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n'; 803 ws->outbuf[SUCCESS_HTTP_HEADER_LENGTH - 1] = '\n';
654 804
655 http_senddata( sock, ws ); 805 http_senddata(sock, ws);
656 return ws->reply_size; 806 return ws->reply_size;
657} 807}
658
659const char *g_version_http_c = "$Source$: $Revision$\n";
diff --git a/ot_http.h b/ot_http.h
index 40161d8..004d710 100644
--- a/ot_http.h
+++ b/ot_http.h
@@ -6,23 +6,28 @@
6#ifndef OT_HTTP_H__ 6#ifndef OT_HTTP_H__
7#define OT_HTTP_H__ 7#define OT_HTTP_H__
8 8
9#include "ot_buf.h"
10
9typedef enum { 11typedef enum {
10 STRUCT_HTTP_FLAG_WAITINGFORTASK = 1, 12 STRUCT_HTTP_FLAG_WAITINGFORTASK = 1,
11 STRUCT_HTTP_FLAG_GZIP = 2, 13 STRUCT_HTTP_FLAG_GZIP = 2,
12 STRUCT_HTTP_FLAG_BZIP2 = 4 14 STRUCT_HTTP_FLAG_BZIP2 = 4,
15 STRUCT_HTTP_FLAG_ZSTD = 8,
16 STRUCT_HTTP_FLAG_CHUNKED = 16,
17 STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER = 32
13} STRUCT_HTTP_FLAG; 18} STRUCT_HTTP_FLAG;
14 19
15struct http_data { 20struct http_data {
16 array request; 21 ot_buf request;
17 io_batch *batch; 22 io_batch *batch;
18 size_t batches; 23 size_t batches;
19 ot_ip6 ip; 24 ot_ip6 ip;
20 STRUCT_HTTP_FLAG flag; 25 STRUCT_HTTP_FLAG flag;
21}; 26};
22 27
23ssize_t http_handle_request( const int64 s, struct ot_workstruct *ws ); 28ssize_t http_handle_request(const int64_t s, struct ot_workstruct *ws);
24ssize_t http_sendiovecdata( const int64 s, struct ot_workstruct *ws, int iovec_entries, struct iovec *iovector ); 29ssize_t http_sendiovecdata(const int64_t s, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial);
25ssize_t http_issue_error( const int64 s, struct ot_workstruct *ws, int code ); 30ssize_t http_issue_error(const int64_t s, struct ot_workstruct *ws, int code);
26 31
27extern char *g_stats_path; 32extern char *g_stats_path;
28extern ssize_t g_stats_path_len; 33extern ssize_t g_stats_path_len;
diff --git a/ot_iovec.c b/ot_iovec.c
index fec3912..bab19e3 100644
--- a/ot_iovec.c
+++ b/ot_iovec.c
@@ -4,9 +4,7 @@
4 $id$ */ 4 $id$ */
5 5
6/* System */ 6/* System */
7#include <sys/types.h>
8#include <stdlib.h> 7#include <stdlib.h>
9#include <unistd.h>
10#include <sys/uio.h> 8#include <sys/uio.h>
11 9
12/* Libowfat */ 10/* Libowfat */
@@ -14,63 +12,79 @@
14/* Opentracker */ 12/* Opentracker */
15#include "ot_iovec.h" 13#include "ot_iovec.h"
16 14
17void *iovec_increase( int *iovec_entries, struct iovec **iovector, size_t new_alloc ) { 15void *iovec_increase(size_t *iovec_entries, struct iovec **iovector, size_t new_alloc) {
18 void *new_data; 16 void *new_data;
19 int new_entries = 1 + *iovec_entries; 17 size_t new_entries = 1 + *iovec_entries;
20 struct iovec *new_vec = realloc( *iovector, new_entries * sizeof( struct iovec ) ); 18 struct iovec *new_vec = realloc(*iovector, new_entries * sizeof(struct iovec));
21 19
22 if( !new_vec ) 20 if (!new_vec)
23 return NULL; 21 return NULL;
24 22
25 /* Only allocate after we have a place to store the pointer */ 23 /* Only allocate after we have a place to store the pointer */
26 new_data = malloc( new_alloc ); 24 new_data = malloc(new_alloc);
27 if( !new_data ) 25 if (!new_data)
28 return NULL; 26 return NULL;
29 27
30 new_vec[new_entries - 1].iov_base = new_data; 28 new_vec[new_entries - 1].iov_base = new_data;
31 new_vec[new_entries - 1].iov_len = new_alloc; 29 new_vec[new_entries - 1].iov_len = new_alloc;
32 30
33 *iovector = new_vec; 31 *iovector = new_vec;
34 ++*iovec_entries; 32 ++*iovec_entries;
35 return new_data; 33 return new_data;
36} 34}
37 35
38void iovec_free( int *iovec_entries, struct iovec **iovector ) { 36void *iovec_append(size_t *iovec_entries, struct iovec **iovector, struct iovec *append_iovector) {
39 int i; 37 size_t new_entries = *iovec_entries + 1;
40 for( i=0; i<*iovec_entries; ++i ) 38 struct iovec *new_vec = realloc(*iovector, new_entries * sizeof(struct iovec));
41 free( ((*iovector)[i]).iov_base ); 39 if (!new_vec)
42 *iovector = NULL; 40 return NULL;
41
42 /* Take over data from appended iovec */
43 new_vec[*iovec_entries].iov_base = append_iovector->iov_base;
44 new_vec[*iovec_entries].iov_len = append_iovector->iov_len;
45
46 append_iovector->iov_base = NULL;
47 append_iovector->iov_len = 0;
48
49 *iovector = new_vec;
50 *iovec_entries = new_entries;
51
52 return new_vec;
53}
54
55void iovec_free(size_t *iovec_entries, struct iovec **iovector) {
56 size_t i;
57 for (i = 0; i < *iovec_entries; ++i)
58 free(((*iovector)[i]).iov_base);
59 free(*iovector);
60 *iovector = NULL;
43 *iovec_entries = 0; 61 *iovec_entries = 0;
44} 62}
45 63
46void iovec_fixlast( int *iovec_entries, struct iovec **iovector, void *last_ptr ) { 64void iovec_fixlast(size_t *iovec_entries, struct iovec **iovector, void *last_ptr) {
47 if( *iovec_entries ) { 65 if (*iovec_entries) {
48 char * base = (char*)((*iovector)[ *iovec_entries - 1 ]).iov_base; 66 char *base = (char *)((*iovector)[*iovec_entries - 1]).iov_base;
49 size_t new_alloc = ((char*)last_ptr) - base; 67 size_t new_alloc = ((char *)last_ptr) - base;
50 68
51 ((*iovector)[*iovec_entries - 1 ]).iov_base = realloc( base, new_alloc ); 69 ((*iovector)[*iovec_entries - 1]).iov_base = realloc(base, new_alloc);
52 ((*iovector)[*iovec_entries - 1 ]).iov_len = new_alloc; 70 ((*iovector)[*iovec_entries - 1]).iov_len = new_alloc;
53 } 71 }
54} 72}
55 73
56void *iovec_fix_increase_or_free( int *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc ) { 74void *iovec_fix_increase_or_free(size_t *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc) {
57 void *new_data; 75 void *new_data;
58 76
59 iovec_fixlast( iovec_entries, iovector, last_ptr ); 77 iovec_fixlast(iovec_entries, iovector, last_ptr);
60 78
61 if( !( new_data = iovec_increase( iovec_entries, iovector, new_alloc ) ) ) 79 if (!(new_data = iovec_increase(iovec_entries, iovector, new_alloc)))
62 iovec_free( iovec_entries, iovector ); 80 iovec_free(iovec_entries, iovector);
63 81
64 return new_data; 82 return new_data;
65} 83}
66 84
67 85size_t iovec_length(const size_t *iovec_entries, const struct iovec **iovector) {
68size_t iovec_length( const int *iovec_entries, const struct iovec **iovector ) { 86 size_t i, length = 0;
69 size_t length = 0; 87 for (i = 0; i < *iovec_entries; ++i)
70 int i;
71 for( i=0; i<*iovec_entries; ++i )
72 length += ((*iovector)[i]).iov_len; 88 length += ((*iovector)[i]).iov_len;
73 return length; 89 return length;
74} 90}
75
76const char *g_version_iovec_c = "$Source$: $Revision$\n";
diff --git a/ot_iovec.h b/ot_iovec.h
index e48008e..eb4365b 100644
--- a/ot_iovec.h
+++ b/ot_iovec.h
@@ -8,12 +8,13 @@
8 8
9#include <sys/uio.h> 9#include <sys/uio.h>
10 10
11void *iovec_increase( int *iovec_entries, struct iovec **iovector, size_t new_alloc ); 11void *iovec_increase(size_t *iovec_entries, struct iovec **iovector, size_t new_alloc);
12void iovec_fixlast( int *iovec_entries, struct iovec **iovector, void *last_ptr ); 12void *iovec_append(size_t *iovec_entries, struct iovec **iovector, struct iovec *append_iovector);
13void iovec_free( int *iovec_entries, struct iovec **iovector ); 13void iovec_fixlast(size_t *iovec_entries, struct iovec **iovector, void *last_ptr);
14void iovec_free(size_t *iovec_entries, struct iovec **iovector);
14 15
15size_t iovec_length( const int *iovec_entries, const struct iovec **iovector ); 16size_t iovec_length(const size_t *iovec_entries, const struct iovec **iovector);
16 17
17void *iovec_fix_increase_or_free( int *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc ); 18void *iovec_fix_increase_or_free(size_t *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc);
18 19
19#endif 20#endif
diff --git a/ot_ip.c b/ot_ip.c
new file mode 100644
index 0000000..8cde7d2
--- /dev/null
+++ b/ot_ip.c
@@ -0,0 +1,201 @@
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 "ot_ip.h"
13
14const char ip_v4mapped_prefix[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff};
15
16/* The dotted quad is only ever reached through the v4 mapped form, so both
17 halves of it stay in here */
18
19static size_t ip4_format(char *dest, const uint8_t *ip) {
20 char *d = dest;
21 int i;
22
23 for (i = 0; i < 4; ++i) {
24 unsigned int octet = ip[i];
25
26 if (i)
27 *d++ = '.';
28 if (octet >= 100)
29 *d++ = (char)('0' + octet / 100);
30 if (octet >= 10)
31 *d++ = (char)('0' + (octet / 10) % 10);
32 *d++ = (char)('0' + octet % 10);
33 }
34
35 return (size_t)(d - dest);
36}
37
38static size_t ip4_scan(const char *src, uint8_t ip[4]) {
39 uint8_t octets[4];
40 const char *s = src;
41 int i;
42
43 for (i = 0; i < 4; ++i) {
44 unsigned int value = 0;
45 int digits = 0;
46
47 if (i && *s++ != '.')
48 return 0;
49
50 /* Leading zeros are tolerated, a value beyond one octet is not */
51 while (*s >= '0' && *s <= '9' && digits < 4) {
52 value = value * 10 + (unsigned int)(*s++ - '0');
53 ++digits;
54 }
55 if (!digits || value > 255)
56 return 0;
57
58 octets[i] = (uint8_t)value;
59 }
60
61 memcpy(ip, octets, sizeof(octets));
62 return (size_t)(s - src);
63}
64
65static unsigned int group_at(const char *ip, int n) { return ((unsigned int)(uint8_t)ip[2 * n] << 8) | (uint8_t)ip[2 * n + 1]; }
66
67size_t ip_format(char *dest, const ot_ip6 ip) {
68 static const char hexdigit[] = "0123456789abcdef";
69 char *d = dest;
70 int best_start = 0, best_len = 0, i = 0;
71
72 if (ip_is_v4mapped(ip))
73 return ip4_format(dest, (const uint8_t *)ip + sizeof(ip_v4mapped_prefix));
74
75 /* RFC 5952 4.2: the longest run of zero groups becomes "::", never a run
76 of one, and the first of two equal runs wins */
77 while (i < 8) {
78 int j = i;
79
80 if (group_at(ip, i)) {
81 ++i;
82 continue;
83 }
84 while (j < 8 && !group_at(ip, j))
85 ++j;
86 if (j - i > best_len) {
87 best_len = j - i;
88 best_start = i;
89 }
90 i = j;
91 }
92 if (best_len < 2)
93 best_len = 0;
94
95 for (i = 0; i < 8; ++i) {
96 unsigned int group;
97 int shift = 12;
98
99 if (best_len && i == best_start) {
100 *d++ = ':';
101 if (i + best_len == 8)
102 *d++ = ':'; /* nothing follows, so close the run */
103 i += best_len - 1;
104 continue;
105 }
106
107 if (i)
108 *d++ = ':';
109
110 group = group_at(ip, i);
111 while (shift && !(group >> shift))
112 shift -= 4; /* RFC 5952 4.1: no leading zeros */
113 for (;; shift -= 4) {
114 *d++ = hexdigit[(group >> shift) & 15];
115 if (!shift)
116 break;
117 }
118 }
119
120 return (size_t)(d - dest);
121}
122
123size_t ip_scan(const char *src, ot_ip6 ip) {
124 uint8_t prefix[16], suffix[16];
125 size_t prefixlen = 0, suffixlen = 0;
126 const char *s = src;
127 int have_gap = 0;
128 int need_colon = 0;
129
130 /* A plain IPv4 address is an address too, stored v4 mapped */
131 {
132 size_t consumed = ip4_scan(src, prefix);
133 if (consumed) {
134 memcpy(ip, ip_v4mapped_prefix, sizeof(ip_v4mapped_prefix));
135 memcpy(ip + sizeof(ip_v4mapped_prefix), prefix, 4);
136 return consumed;
137 }
138 }
139
140 for (;;) {
141 unsigned int value = 0;
142 int digits = 0, nibble;
143 uint8_t *into = have_gap ? suffix : prefix;
144 size_t *fill = have_gap ? &suffixlen : &prefixlen;
145
146 /* RFC 4291 2.2: one run of zero groups may be written "::" */
147 if (s[0] == ':' && s[1] == ':') {
148 if (have_gap)
149 return 0;
150 have_gap = 1;
151 need_colon = 0;
152 s += 2;
153 continue;
154 }
155
156 if (need_colon) {
157 if (*s != ':')
158 break;
159 ++s;
160 }
161
162 /* RFC 4291 2.2 form 3: the final two groups may be a dotted quad */
163 if (prefixlen + suffixlen + 4 <= 16) {
164 size_t consumed = ip4_scan(s, into + *fill);
165 if (consumed) {
166 *fill += 4;
167 s += consumed;
168 break;
169 }
170 }
171
172 while (digits < 4 && (nibble = hex_value(*s)) >= 0) {
173 value = value * 16 + (unsigned int)nibble;
174 ++digits;
175 ++s;
176 }
177 if (!digits) {
178 if (need_colon)
179 --s; /* leave the separator we ate for the caller */
180 break;
181 }
182
183 if (prefixlen + suffixlen + 2 > 16)
184 return 0;
185
186 into[(*fill)++] = (uint8_t)(value >> 8);
187 into[(*fill)++] = (uint8_t)value;
188 need_colon = 1;
189 }
190
191 if (!prefixlen && !suffixlen && !have_gap)
192 return 0;
193 if (!have_gap && prefixlen + suffixlen != 16)
194 return 0; /* without "::" every group has to be spelled out */
195
196 memset(ip, 0, 16);
197 memcpy(ip, prefix, prefixlen);
198 memcpy(ip + 16 - suffixlen, suffix, suffixlen);
199
200 return (size_t)(s - src);
201}
diff --git a/ot_ip.h b/ot_ip.h
new file mode 100644
index 0000000..ac8964d
--- /dev/null
+++ b/ot_ip.h
@@ -0,0 +1,33 @@
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_IP_H__
7#define OT_IP_H__
8
9#include <stddef.h>
10#include <string.h>
11
12#include "trackerlogic.h"
13
14/* opentracker holds every address as sixteen bytes, writing IPv4 as the v4
15 mapped form of RFC 4291 section 2.5.5.2. */
16extern const char ip_v4mapped_prefix[12];
17
18static inline int ip_is_v4mapped(const void *ip) { return !memcmp(ip, ip_v4mapped_prefix, sizeof(ip_v4mapped_prefix)); }
19
20/* Read an address and return how many characters it took, or zero if src
21 does not start with one. A plain IPv4 address is accepted and stored in
22 its v4 mapped form.
23
24 The count lets the caller carry on where the address ended, which is how
25 "address:port" and "address/bits" are read. */
26size_t ip_scan(const char *src, ot_ip6 ip);
27
28/* Write an address and return how many characters it took. Nothing is
29 terminated, so a caller may keep appending. A v4 mapped address is written
30 as the plain dotted quad rather than "::ffff:1.2.3.4". */
31size_t ip_format(char *dest, const ot_ip6 ip);
32
33#endif
diff --git a/ot_livesync.c b/ot_livesync.c
index cded0f7..8b2dee6 100644
--- a/ot_livesync.c
+++ b/ot_livesync.c
@@ -4,204 +4,242 @@
4 $id$ */ 4 $id$ */
5 5
6/* System */ 6/* System */
7#include <sys/types.h> 7#include <arpa/inet.h>
8#include <sys/uio.h> 8#include <fcntl.h>
9#include <string.h>
10#include <pthread.h> 9#include <pthread.h>
11#include <unistd.h>
12#include <stdlib.h> 10#include <stdlib.h>
11#include <string.h>
12#include <sys/types.h>
13#include <unistd.h>
13 14
14/* Libowfat */ 15/* Libowfat */
15#include "socket.h" 16#include "socket.h"
16#include "ndelay.h"
17#include "byte.h"
18#include "ip6.h"
19 17
20/* Opentracker */ 18/* Opentracker */
21#include "trackerlogic.h"
22#include "ot_livesync.h"
23#include "ot_accesslist.h" 19#include "ot_accesslist.h"
24#include "ot_stats.h" 20#include "ot_ip.h"
21#include "ot_livesync.h"
25#include "ot_mutex.h" 22#include "ot_mutex.h"
23#include "ot_stats.h"
24#include "trackerlogic.h"
26 25
27#ifdef WANT_SYNC_LIVE 26#ifdef WANT_SYNC_LIVE
28 27
29char groupip_1[4] = { 224,0,23,5 }; 28char groupip_1[4] = {224, 0, 23, 5};
30 29
31#define LIVESYNC_INCOMING_BUFFSIZE (256*256) 30#define LIVESYNC_INCOMING_BUFFSIZE (256 * 256)
32 31
33#define LIVESYNC_OUTGOING_BUFFSIZE_PEERS 1480 32#define LIVESYNC_OUTGOING_BUFFSIZE_PEERS 1480
34#define LIVESYNC_OUTGOING_WATERMARK_PEERS (sizeof(ot_peer)+sizeof(ot_hash)) 33#define LIVESYNC_OUTGOING_WATERMARK_PEERS (sizeof(ot_peer) + sizeof(ot_hash))
35 34
36#define LIVESYNC_MAXDELAY 15 /* seconds */ 35#define LIVESYNC_MAXDELAY 15 /* seconds */
37 36
38enum { OT_SYNC_PEER }; 37enum { OT_SYNC_PEER4, OT_SYNC_PEER6 };
39 38
40/* Forward declaration */ 39/* Forward declaration */
41static void * livesync_worker( void * args ); 40static void *livesync_worker(void *args);
42 41
43/* For outgoing packets */ 42/* For outgoing packets */
44static int64 g_socket_in = -1; 43static int64_t g_socket_in = -1;
45 44
46/* For incoming packets */ 45/* For incoming packets */
47static int64 g_socket_out = -1; 46static int64_t g_socket_out = -1;
48 47
49static pthread_mutex_t g_outbuf_mutex = PTHREAD_MUTEX_INITIALIZER; 48static pthread_mutex_t g_outbuf_mutex = PTHREAD_MUTEX_INITIALIZER;
50char g_outbuf[LIVESYNC_OUTGOING_BUFFSIZE_PEERS]; 49typedef struct {
51static size_t g_outbuf_data; 50 uint8_t data[LIVESYNC_OUTGOING_BUFFSIZE_PEERS];
52static ot_time g_next_packet_time; 51 size_t fill;
52 ot_time next_packet_time;
53} sync_buffer;
54
55static sync_buffer g_v6_buf;
56static sync_buffer g_v4_buf;
53 57
54static pthread_t thread_id; 58static pthread_t thread_id;
55void livesync_init( ) { 59void livesync_init(void) {
60 uint32_t packet_type6 = htonl(OT_SYNC_PEER6);
61 uint32_t packet_type4 = htonl(OT_SYNC_PEER4);
56 62
57 if( g_socket_in == -1 ) 63 if (g_socket_in == -1)
58 exerr( "No socket address for live sync specified." ); 64 exerr("No socket address for live sync specified.");
59 65
60 /* Prepare outgoing peers buffer */ 66 /* Prepare outgoing peers buffer */
61 memcpy( g_outbuf, &g_tracker_id, sizeof( g_tracker_id ) ); 67 memcpy(g_v6_buf.data, &g_tracker_id, sizeof(g_tracker_id));
62 uint32_pack_big( g_outbuf + sizeof( g_tracker_id ), OT_SYNC_PEER); 68 memcpy(g_v4_buf.data, &g_tracker_id, sizeof(g_tracker_id));
63 g_outbuf_data = sizeof( g_tracker_id ) + sizeof( uint32_t );
64 69
65 g_next_packet_time = g_now_seconds + LIVESYNC_MAXDELAY; 70 memcpy(g_v6_buf.data + sizeof(g_tracker_id), &packet_type6, sizeof(packet_type6));
71 memcpy(g_v4_buf.data + sizeof(g_tracker_id), &packet_type4, sizeof(packet_type4));
66 72
67 pthread_create( &thread_id, NULL, livesync_worker, NULL ); 73 g_v6_buf.fill = sizeof(g_tracker_id) + sizeof(uint32_t);
74 g_v4_buf.fill = sizeof(g_tracker_id) + sizeof(uint32_t);
75
76 g_v6_buf.next_packet_time = g_now_seconds + LIVESYNC_MAXDELAY;
77 g_v4_buf.next_packet_time = g_now_seconds + LIVESYNC_MAXDELAY;
78
79 pthread_create(&thread_id, NULL, livesync_worker, NULL);
68} 80}
69 81
70void livesync_deinit() { 82void livesync_deinit(void) {
71 if( g_socket_in != -1 ) 83 if (g_socket_in != -1)
72 close( g_socket_in ); 84 close(g_socket_in);
73 if( g_socket_out != -1 ) 85 if (g_socket_out != -1)
74 close( g_socket_out ); 86 close(g_socket_out);
75 87
76 pthread_cancel( thread_id ); 88 pthread_cancel(thread_id);
77} 89}
78 90
79void livesync_bind_mcast( ot_ip6 ip, uint16_t port) { 91void livesync_bind_mcast(ot_ip6 ip, uint16_t port) {
80 char tmpip[4] = {0,0,0,0}; 92 char tmpip[4] = {0, 0, 0, 0};
81 char *v4ip; 93 char *v4ip;
82 94
83 if( !ip6_isv4mapped(ip)) 95 if (!ip_is_v4mapped(ip))
84 exerr("v6 mcast support not yet available."); 96 exerr("v6 mcast support not yet available.");
85 v4ip = ip+12; 97 v4ip = ip + 12;
86 98
87 if( g_socket_in != -1 ) 99 if (g_socket_in != -1)
88 exerr("Error: Livesync listen ip specified twice."); 100 exerr("Error: Livesync listen ip specified twice.");
89 101
90 if( ( g_socket_in = socket_udp4( )) < 0) 102 if ((g_socket_in = socket_udp4()) < 0)
91 exerr("Error: Cant create live sync incoming socket." ); 103 exerr("Error: Cant create live sync incoming socket.");
92 ndelay_off(g_socket_in);
93 104
94 if( socket_bind4_reuse( g_socket_in, tmpip, port ) == -1 ) 105 fcntl(g_socket_in, F_SETFL, fcntl(g_socket_in, F_GETFL, 0) & ~O_NONBLOCK);
95 exerr("Error: Cant bind live sync incoming socket." );
96 106
97 if( socket_mcjoin4( g_socket_in, groupip_1, v4ip ) ) 107 if (socket_bind4_reuse(g_socket_in, tmpip, port) == -1)
108 exerr("Error: Cant bind live sync incoming socket.");
109
110 if (socket_mcjoin4(g_socket_in, groupip_1, v4ip))
98 exerr("Error: Cant make live sync incoming socket join mcast group."); 111 exerr("Error: Cant make live sync incoming socket join mcast group.");
99 112
100 if( ( g_socket_out = socket_udp4()) < 0) 113 if ((g_socket_out = socket_udp4()) < 0)
101 exerr("Error: Cant create live sync outgoing socket." ); 114 exerr("Error: Cant create live sync outgoing socket.");
102 if( socket_bind4_reuse( g_socket_out, v4ip, port ) == -1 ) 115 if (socket_bind4_reuse(g_socket_out, v4ip, port) == -1)
103 exerr("Error: Cant bind live sync outgoing socket." ); 116 exerr("Error: Cant bind live sync outgoing socket.");
104 117
105 socket_mcttl4(g_socket_out, 1); 118 socket_mcttl4(g_socket_out, 1);
106 socket_mcloop4(g_socket_out, 0); 119 socket_mcloop4(g_socket_out, 0);
107} 120}
108 121
109/* Caller MUST hold g_outbuf_mutex. Returns with g_outbuf_mutex unlocked */ 122/* Caller MUST hold g_outbuf_mutex. Returns with g_outbuf_mutex unlocked */
110static void livesync_issue_peersync( ) { 123static void livesync_issue_peersync(sync_buffer *buf) {
111 char mycopy[LIVESYNC_OUTGOING_BUFFSIZE_PEERS]; 124 char mycopy[LIVESYNC_OUTGOING_BUFFSIZE_PEERS];
112 size_t data = g_outbuf_data; 125 size_t fill = buf->fill;
113 126
114 memcpy( mycopy, g_outbuf, data ); 127 memcpy(mycopy, buf->data, fill);
115 g_outbuf_data = sizeof( g_tracker_id ) + sizeof( uint32_t ); 128 buf->fill = sizeof(g_tracker_id) + sizeof(uint32_t);
116 g_next_packet_time = g_now_seconds + LIVESYNC_MAXDELAY; 129 buf->next_packet_time = g_now_seconds + LIVESYNC_MAXDELAY;
117 130
118 /* From now this thread has a local copy of the buffer and 131 /* From now this thread has a local copy of the buffer and
119 has modified the protected element */ 132 has modified the protected element */
120 pthread_mutex_unlock(&g_outbuf_mutex); 133 pthread_mutex_unlock(&g_outbuf_mutex);
121 134
122 socket_send4(g_socket_out, mycopy, data, groupip_1, LIVESYNC_PORT); 135 socket_send4(g_socket_out, mycopy, fill, groupip_1, LIVESYNC_PORT);
123} 136}
124 137
125static void livesync_handle_peersync( struct ot_workstruct *ws ) { 138static void livesync_handle_peersync(struct ot_workstruct *ws, size_t peer_size) {
126 int off = sizeof( g_tracker_id ) + sizeof( uint32_t ); 139 size_t off = sizeof(g_tracker_id) + sizeof(uint32_t);
140
141 /* Prepare peer record for v4mapped expansion */
142 if (peer_size == OT_PEER_SIZE4)
143 memcpy(&ws->peer, ip_v4mapped_prefix, sizeof(ip_v4mapped_prefix));
127 144
128 /* Now basic sanity checks have been done on the live sync packet 145 /* Now basic sanity checks have been done on the live sync packet
129 We might add more testing and logging. */ 146 We might add more testing and logging. */
130 while( off + (ssize_t)sizeof( ot_hash ) + (ssize_t)sizeof( ot_peer ) <= ws->request_size ) { 147 while ((ssize_t)(off + sizeof(ot_hash) + peer_size) <= ws->request_size) {
131 memcpy( &ws->peer, ws->request + off + sizeof(ot_hash), sizeof( ot_peer ) ); 148 char *peer_ip = ws->request + off + sizeof(ot_hash);
132 ws->hash = (ot_hash*)(ws->request + off); 149 if (peer_size == OT_PEER_SIZE4)
150 memcpy(&ws->peer[0] + sizeof(ip_v4mapped_prefix), peer_ip, peer_size);
151 else
152 memcpy(&ws->peer, peer_ip, peer_size);
153 ws->hash = (ot_hash *)(ws->request + off);
133 154
134 if( !g_opentracker_running ) return; 155 if (!g_opentracker_running)
156 return;
135 157
136 if( OT_PEERFLAG(&ws->peer) & PEER_FLAG_STOPPED ) 158 if (OT_PEERFLAG(ws->peer) & PEER_FLAG_STOPPED)
137 remove_peer_from_torrent( FLAG_MCA, ws ); 159 remove_peer_from_torrent(FLAG_MCA, ws);
138 else 160 else
139 add_peer_to_torrent_and_return_peers( FLAG_MCA, ws, /* amount = */ 0 ); 161 add_peer_to_torrent_and_return_peers(FLAG_MCA, ws, /* amount = */ 0);
140 162
141 off += sizeof( ot_hash ) + sizeof( ot_peer ); 163 off += sizeof(ot_hash) + peer_size;
142 } 164 }
143 165
144 stats_issue_event(EVENT_SYNC, 0, 166 stats_issue_event(EVENT_SYNC, 0, (ws->request_size - sizeof(g_tracker_id) - sizeof(uint32_t)) / ((ssize_t)sizeof(ot_hash) + peer_size));
145 (ws->request_size - sizeof( g_tracker_id ) - sizeof( uint32_t ) ) /
146 ((ssize_t)sizeof( ot_hash ) + (ssize_t)sizeof( ot_peer )));
147} 167}
148 168
149/* Tickle the live sync module from time to time, so no events get 169/* Tickle the live sync module from time to time, so no events get
150 stuck when there's not enough traffic to fill udp packets fast 170 stuck when there's not enough traffic to fill udp packets fast
151 enough */ 171 enough */
152void livesync_ticker( ) { 172void livesync_ticker(void) {
153 /* livesync_issue_peersync sets g_next_packet_time */ 173 /* livesync_issue_peersync sets g_next_packet_time */
154 pthread_mutex_lock(&g_outbuf_mutex); 174 pthread_mutex_lock(&g_outbuf_mutex);
155 if( g_now_seconds > g_next_packet_time && 175 if (g_now_seconds > g_v6_buf.next_packet_time && g_v6_buf.fill > sizeof(g_tracker_id) + sizeof(uint32_t))
156 g_outbuf_data > sizeof( g_tracker_id ) + sizeof( uint32_t ) ) 176 livesync_issue_peersync(&g_v6_buf);
157 livesync_issue_peersync(); 177 else
178 pthread_mutex_unlock(&g_outbuf_mutex);
179
180 pthread_mutex_lock(&g_outbuf_mutex);
181 if (g_now_seconds > g_v4_buf.next_packet_time && g_v4_buf.fill > sizeof(g_tracker_id) + sizeof(uint32_t))
182 livesync_issue_peersync(&g_v4_buf);
158 else 183 else
159 pthread_mutex_unlock(&g_outbuf_mutex); 184 pthread_mutex_unlock(&g_outbuf_mutex);
160} 185}
161 186
162/* Inform live sync about whats going on. */ 187/* Inform live sync about whats going on. */
163void livesync_tell( struct ot_workstruct *ws ) { 188void livesync_tell(struct ot_workstruct *ws) {
189 size_t peer_size; /* initialized in next line */
190 ot_peer *peer_src = peer_from_peer6(&ws->peer, &peer_size);
191 sync_buffer *dest_buf = peer_size == OT_PEER_SIZE6 ? &g_v6_buf : &g_v4_buf;
192
164 pthread_mutex_lock(&g_outbuf_mutex); 193 pthread_mutex_lock(&g_outbuf_mutex);
165 194
166 memcpy( g_outbuf + g_outbuf_data, ws->hash, sizeof(ot_hash) ); 195 memcpy(dest_buf->data + dest_buf->fill, ws->hash, sizeof(ot_hash));
167 memcpy( g_outbuf + g_outbuf_data + sizeof(ot_hash), &ws->peer, sizeof(ot_peer) ); 196 dest_buf->fill += sizeof(ot_hash);
168 197
169 g_outbuf_data += sizeof(ot_hash) + sizeof(ot_peer); 198 memcpy(dest_buf->data + dest_buf->fill, peer_src, peer_size);
199 dest_buf->fill += peer_size;
170 200
171 if( g_outbuf_data >= LIVESYNC_OUTGOING_BUFFSIZE_PEERS - LIVESYNC_OUTGOING_WATERMARK_PEERS ) 201 if (dest_buf->fill >= LIVESYNC_OUTGOING_BUFFSIZE_PEERS - LIVESYNC_OUTGOING_WATERMARK_PEERS)
172 livesync_issue_peersync(); 202 livesync_issue_peersync(dest_buf);
173 else 203 else
174 pthread_mutex_unlock(&g_outbuf_mutex); 204 pthread_mutex_unlock(&g_outbuf_mutex);
175} 205}
176 206
177static void * livesync_worker( void * args ) { 207static void *livesync_worker(void *args) {
178 struct ot_workstruct ws; 208 struct ot_workstruct ws;
179 ot_ip6 in_ip; uint16_t in_port; 209 ot_ip6 in_ip;
210 uint16_t in_port;
180 211
181 (void)args; 212 (void)args;
182 213
183 /* Initialize our "thread local storage" */ 214 /* Initialize our "thread local storage" */
184 ws.inbuf = ws.request = malloc( LIVESYNC_INCOMING_BUFFSIZE ); 215 ws.inbuf = ws.request = malloc(LIVESYNC_INCOMING_BUFFSIZE);
185 ws.outbuf = ws.reply = 0; 216 ws.outbuf = ws.reply = 0;
186 217
187 memcpy( in_ip, V4mappedprefix, sizeof( V4mappedprefix ) ); 218 memcpy(in_ip, ip_v4mapped_prefix, sizeof(ip_v4mapped_prefix));
188 219
189 while( 1 ) { 220 while (1) {
190 ws.request_size = socket_recv4(g_socket_in, (char*)ws.inbuf, LIVESYNC_INCOMING_BUFFSIZE, 12+(char*)in_ip, &in_port); 221 uint32_t packet_type;
222
223 ws.request_size = socket_recv4(g_socket_in, (char *)ws.inbuf, LIVESYNC_INCOMING_BUFFSIZE, 12 + (char *)in_ip, &in_port);
191 224
192 /* Expect at least tracker id and packet type */ 225 /* Expect at least tracker id and packet type */
193 if( ws.request_size <= (ssize_t)(sizeof( g_tracker_id ) + sizeof( uint32_t )) ) 226 if (ws.request_size <= (ssize_t)(sizeof(g_tracker_id) + sizeof(uint32_t)))
194 continue; 227 continue;
195 if( !accesslist_isblessed(in_ip, OT_PERMISSION_MAY_LIVESYNC)) 228 if (!accesslist_is_blessed(in_ip, OT_PERMISSION_MAY_LIVESYNC))
196 continue; 229 continue;
197 if( !memcmp( ws.inbuf, &g_tracker_id, sizeof( g_tracker_id ) ) ) { 230 if (!memcmp(ws.inbuf, &g_tracker_id, sizeof(g_tracker_id))) {
198 /* TODO: log packet coming from ourselves */ 231 /* TODO: log packet coming from ourselves */
199 continue; 232 continue;
200 } 233 }
201 234
202 switch( uint32_read_big( sizeof( g_tracker_id ) + (char *)ws.inbuf ) ) { 235 memcpy(&packet_type, ws.inbuf + sizeof(g_tracker_id), sizeof(packet_type));
203 case OT_SYNC_PEER: 236
204 livesync_handle_peersync( &ws ); 237 switch (ntohl(packet_type)) {
238 case OT_SYNC_PEER6:
239 livesync_handle_peersync(&ws, OT_PEER_SIZE6);
240 break;
241 case OT_SYNC_PEER4:
242 livesync_handle_peersync(&ws, OT_PEER_SIZE4);
205 break; 243 break;
206 default: 244 default:
207 break; 245 break;
@@ -213,4 +251,3 @@ static void * livesync_worker( void * args ) {
213} 251}
214 252
215#endif 253#endif
216const char *g_version_livesync_c = "$Source$: $Revision$\n";
diff --git a/ot_livesync.h b/ot_livesync.h
index d7490e5..6eca479 100644
--- a/ot_livesync.h
+++ b/ot_livesync.h
@@ -28,35 +28,41 @@
28 Each tracker instance accumulates announce requests until its buffer is 28 Each tracker instance accumulates announce requests until its buffer is
29 full or a timeout is reached. Then it broadcasts its live sync packer: 29 full or a timeout is reached. Then it broadcasts its live sync packer:
30 30
31 packet type SYNC_LIVE 31 packet type SYNC_LIVE4
32 [ 0x0008 0x14 info_hash 32 [ 0x0008 0x14 info_hash
33 0x001c 0x04 peer's ipv4 address 33 0x001c 0x04 peer's ipv4 address
34 0x0020 0x02 peer's port 34 0x0020 0x02 peer's port
35 0x0024 0x02 peer flags v1 ( SEEDING = 0x80, COMPLETE = 0x40, STOPPED = 0x20 ) 35 0x0024 0x02 peer flags v1 ( SEEDING = 0x80, COMPLETE = 0x40, STOPPED = 0x20 )
36 ]* 36 ]*
37 37
38 packet type SYNC_LIVE6
39 [ 0x0008 0x14 info_hash
40 0x001c 0x10 peer's ipv6 address
41 0x002c 0x02 peer's port
42 0x002e 0x02 peer flags v1 ( SEEDING = 0x80, COMPLETE = 0x40, STOPPED = 0x20 )
43 ]*
38*/ 44*/
39 45
40#ifdef WANT_SYNC_LIVE 46#ifdef WANT_SYNC_LIVE
41 47
42#define LIVESYNC_PORT 9696 48#define LIVESYNC_PORT 9696
43 49
44void livesync_init(); 50void livesync_init(void);
45void livesync_deinit(); 51void livesync_deinit(void);
46 52
47/* Join multicast group for listening and create sending socket */ 53/* Join multicast group for listening and create sending socket */
48void livesync_bind_mcast( char *ip, uint16_t port ); 54void livesync_bind_mcast(char *ip, uint16_t port);
49 55
50/* Inform live sync about whats going on. */ 56/* Inform live sync about whats going on. */
51void livesync_tell( struct ot_workstruct *ws ); 57void livesync_tell(struct ot_workstruct *ws);
52 58
53/* 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
54 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
55 enough */ 61 enough */
56void livesync_ticker( ); 62void livesync_ticker(void);
57 63
58/* Handle an incoming live sync packet */ 64/* Handle an incoming live sync packet */
59void handle_livesync( const int64 sock ); 65void handle_livesync(const int64_t sock);
60 66
61#else 67#else
62 68
diff --git a/ot_mutex.c b/ot_mutex.c
index f46e3e2..2a8cbf5 100644
--- a/ot_mutex.c
+++ b/ot_mutex.c
@@ -4,92 +4,81 @@
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 "trackerlogic.h" 18#include "ot_iovec.h"
20#include "ot_mutex.h" 19#include "ot_mutex.h"
21#include "ot_stats.h" 20#include "ot_stats.h"
21#include "trackerlogic.h"
22 22
23/* #define MTX_DBG( STRING ) fprintf( stderr, STRING ) */ 23/* #define MTX_DBG( STRING ) fprintf( stderr, STRING ) */
24#define MTX_DBG( STRING ) 24#define MTX_DBG(STRING)
25 25
26/* Our global all torrents list */ 26/* Our global all torrents list */
27static ot_vector all_torrents[OT_BUCKET_COUNT]; 27static ot_vector all_torrents[OT_BUCKET_COUNT];
28static pthread_mutex_t bucket_mutex[OT_BUCKET_COUNT]; 28static pthread_mutex_t bucket_mutex[OT_BUCKET_COUNT];
29static size_t g_torrent_count; 29static size_t g_torrent_count;
30 30
31/* Self pipe from opentracker.c */ 31/* Self pipe from opentracker.c */
32extern int g_self_pipe[2]; 32extern int g_self_pipe[2];
33 33
34ot_vector *mutex_bucket_lock( int bucket ) { 34ot_vector *mutex_bucket_lock(int bucket) {
35 pthread_mutex_lock(bucket_mutex + bucket ); 35 pthread_mutex_lock(bucket_mutex + bucket);
36 return all_torrents + bucket; 36 return all_torrents + bucket;
37} 37}
38 38
39ot_vector *mutex_bucket_lock_by_hash( ot_hash hash ) { 39/* Torrents are spread over buckets by the first four bytes of their info_hash,
40 return mutex_bucket_lock( uint32_read_big( (char*)hash ) >> OT_BUCKET_COUNT_SHIFT ); 40 which may sit at any alignment, hence the memcpy */
41static int bucket_from_hash(ot_hash const hash) {
42 uint32_t bucket;
43 memcpy(&bucket, hash, sizeof(bucket));
44 return ntohl(bucket) >> OT_BUCKET_COUNT_SHIFT;
41} 45}
42 46
43void mutex_bucket_unlock( int bucket, int delta_torrentcount ) { 47ot_vector *mutex_bucket_lock_by_hash(ot_hash const hash) { return mutex_bucket_lock(bucket_from_hash(hash)); }
48
49void mutex_bucket_unlock(int bucket, int delta_torrentcount) {
44 pthread_mutex_unlock(bucket_mutex + bucket); 50 pthread_mutex_unlock(bucket_mutex + bucket);
45 g_torrent_count += delta_torrentcount; 51 g_torrent_count += delta_torrentcount;
46} 52}
47 53
48void mutex_bucket_unlock_by_hash( ot_hash hash, int delta_torrentcount ) { 54void mutex_bucket_unlock_by_hash(ot_hash const hash, int delta_torrentcount) {
49 mutex_bucket_unlock( uint32_read_big( (char*)hash ) >> OT_BUCKET_COUNT_SHIFT, delta_torrentcount ); 55 mutex_bucket_unlock(bucket_from_hash(hash), delta_torrentcount);
50} 56}
51 57
52size_t mutex_get_torrent_count( ) { 58size_t mutex_get_torrent_count(void) { return g_torrent_count; }
53 return g_torrent_count;
54}
55 59
56/* TaskQueue Magic */ 60/* TaskQueue Magic */
57 61
58struct ot_task { 62struct ot_task {
59 ot_taskid taskid; 63 ot_taskid taskid;
60 ot_tasktype tasktype; 64 ot_tasktype tasktype;
61 int64 sock; 65 int64_t sock;
62 int iovec_entries; 66 size_t iovec_entries;
63 struct iovec *iovec; 67 struct iovec *iovec;
64 struct ot_task *next; 68 struct ot_task *next;
65}; 69};
66 70
67static ot_taskid next_free_taskid = 1; 71static ot_taskid next_free_taskid = 1;
68static struct ot_task *tasklist; 72static struct ot_task *tasklist;
69static pthread_mutex_t tasklist_mutex; 73static pthread_mutex_t tasklist_mutex;
70static pthread_cond_t tasklist_being_filled; 74static pthread_cond_t tasklist_being_filled;
71
72int mutex_workqueue_pushtask( int64 sock, ot_tasktype tasktype ) {
73 struct ot_task ** tmptask, * task;
74 75
75 /* Want exclusive access to tasklist */ 76int mutex_workqueue_pushtask(int64_t sock, ot_tasktype tasktype) {
76 MTX_DBG( "pushtask locks.\n" ); 77 struct ot_task **tmptask, *task;
77 pthread_mutex_lock( &tasklist_mutex );
78 MTX_DBG( "pushtask locked.\n" );
79 78
80 task = malloc(sizeof( struct ot_task)); 79 task = malloc(sizeof(struct ot_task));
81 if( !task ) { 80 if (!task)
82 MTX_DBG( "pushtask fail unlocks.\n" );
83 pthread_mutex_unlock( &tasklist_mutex );
84 MTX_DBG( "pushtask fail unlocked.\n" );
85 return -1; 81 return -1;
86 }
87
88 /* Skip to end of list */
89 tmptask = &tasklist;
90 while( *tmptask )
91 tmptask = &(*tmptask)->next;
92 *tmptask = task;
93 82
94 task->taskid = 0; 83 task->taskid = 0;
95 task->tasktype = tasktype; 84 task->tasktype = tasktype;
@@ -98,183 +87,194 @@ int mutex_workqueue_pushtask( int64 sock, ot_tasktype tasktype ) {
98 task->iovec = NULL; 87 task->iovec = NULL;
99 task->next = 0; 88 task->next = 0;
100 89
90 /* Want exclusive access to tasklist */
91 pthread_mutex_lock(&tasklist_mutex);
92
93 /* Skip to end of list */
94 tmptask = &tasklist;
95 while (*tmptask)
96 tmptask = &(*tmptask)->next;
97 *tmptask = task;
98
101 /* Inform waiting workers and release lock */ 99 /* Inform waiting workers and release lock */
102 MTX_DBG( "pushtask broadcasts.\n" ); 100 pthread_cond_broadcast(&tasklist_being_filled);
103 pthread_cond_broadcast( &tasklist_being_filled ); 101 pthread_mutex_unlock(&tasklist_mutex);
104 MTX_DBG( "pushtask broadcasted, mutex unlocks.\n" );
105 pthread_mutex_unlock( &tasklist_mutex );
106 MTX_DBG( "pushtask end mutex unlocked.\n" );
107 return 0; 102 return 0;
108} 103}
109 104
110void mutex_workqueue_canceltask( int64 sock ) { 105void mutex_workqueue_canceltask(int64_t sock) {
111 struct ot_task ** task; 106 struct ot_task **task;
112 107
113 /* Want exclusive access to tasklist */ 108 /* Want exclusive access to tasklist */
114 MTX_DBG( "canceltask locks.\n" ); 109 pthread_mutex_lock(&tasklist_mutex);
115 pthread_mutex_lock( &tasklist_mutex );
116 MTX_DBG( "canceltask locked.\n" );
117
118 task = &tasklist;
119 while( *task && ( (*task)->sock != sock ) )
120 *task = (*task)->next;
121 110
122 if( *task && ( (*task)->sock == sock ) ) { 111 for (task = &tasklist; *task; task = &((*task)->next))
123 struct iovec *iovec = (*task)->iovec; 112 if ((*task)->sock == sock) {
124 struct ot_task *ptask = *task; 113 struct iovec *iovec = (*task)->iovec;
125 int i; 114 struct ot_task *ptask = *task;
115 size_t i;
126 116
127 /* Free task's iovec */ 117 /* Free task's iovec, the buffers it points at and the vector itself */
128 for( i=0; i<(*task)->iovec_entries; ++i ) 118 for (i = 0; i < (*task)->iovec_entries; ++i)
129 free( iovec[i].iov_base ); 119 free(iovec[i].iov_base);
120 free(iovec);
130 121
131 *task = (*task)->next; 122 *task = (*task)->next;
132 free( ptask ); 123 free(ptask);
133 } 124 break;
125 }
134 126
135 /* Release lock */ 127 /* Release lock */
136 MTX_DBG( "canceltask unlocks.\n" ); 128 pthread_mutex_unlock(&tasklist_mutex);
137 pthread_mutex_unlock( &tasklist_mutex );
138 MTX_DBG( "canceltask unlocked.\n" );
139} 129}
140 130
141ot_taskid mutex_workqueue_poptask( ot_tasktype *tasktype ) { 131ot_taskid mutex_workqueue_poptask(ot_tasktype *tasktype) {
142 struct ot_task * task; 132 struct ot_task *task;
143 ot_taskid taskid = 0; 133 ot_taskid taskid = 0;
144 134
145 /* Want exclusive access to tasklist */ 135 /* Want exclusive access to tasklist */
146 MTX_DBG( "poptask mutex locks.\n" ); 136 pthread_mutex_lock(&tasklist_mutex);
147 pthread_mutex_lock( &tasklist_mutex );
148 MTX_DBG( "poptask mutex locked.\n" );
149 137
150 while( !taskid ) { 138 while (!taskid) {
151 /* Skip to the first unassigned task this worker wants to do */ 139 /* Skip to the first unassigned task this worker wants to do */
152 task = tasklist; 140 for (task = tasklist; task; task = task->next)
153 while( task && ( ( ( TASK_CLASS_MASK & task->tasktype ) != *tasktype ) || task->taskid ) ) 141 if (!task->taskid && (TASK_CLASS_MASK & task->tasktype) == *tasktype) {
154 task = task->next; 142 /* If we found an outstanding task, assign a taskid to it
143 and leave the loop */
144 task->taskid = taskid = ++next_free_taskid;
145 *tasktype = task->tasktype;
146 break;
147 }
155 148
156 /* If we found an outstanding task, assign a taskid to it 149 /* Wait until the next task is being fed */
157 and leave the loop */ 150 if (!taskid)
158 if( task ) { 151 pthread_cond_wait(&tasklist_being_filled, &tasklist_mutex);
159 task->taskid = taskid = ++next_free_taskid;
160 *tasktype = task->tasktype;
161 } else {
162 /* Wait until the next task is being fed */
163 MTX_DBG( "poptask cond waits.\n" );
164 pthread_cond_wait( &tasklist_being_filled, &tasklist_mutex );
165 MTX_DBG( "poptask cond waited.\n" );
166 }
167 } 152 }
168 153
169 /* Release lock */ 154 /* Release lock */
170 MTX_DBG( "poptask end mutex unlocks.\n" ); 155 pthread_mutex_unlock(&tasklist_mutex);
171 pthread_mutex_unlock( &tasklist_mutex );
172 MTX_DBG( "poptask end mutex unlocked.\n" );
173 156
174 return taskid; 157 return taskid;
175} 158}
176 159
177void mutex_workqueue_pushsuccess( ot_taskid taskid ) { 160void mutex_workqueue_pushsuccess(ot_taskid taskid) {
178 struct ot_task ** task; 161 struct ot_task **task;
179 162
180 /* Want exclusive access to tasklist */ 163 /* Want exclusive access to tasklist */
181 MTX_DBG( "pushsuccess locks.\n" ); 164 pthread_mutex_lock(&tasklist_mutex);
182 pthread_mutex_lock( &tasklist_mutex );
183 MTX_DBG( "pushsuccess locked.\n" );
184
185 task = &tasklist;
186 while( *task && ( (*task)->taskid != taskid ) )
187 *task = (*task)->next;
188 165
189 if( *task && ( (*task)->taskid == taskid ) ) { 166 for (task = &tasklist; *task; task = &((*task)->next))
190 struct ot_task *ptask = *task; 167 if ((*task)->taskid == taskid) {
191 *task = (*task)->next; 168 struct ot_task *ptask = *task;
192 free( ptask ); 169 *task = (*task)->next;
193 } 170 free(ptask);
171 break;
172 }
194 173
195 /* Release lock */ 174 /* Release lock */
196 MTX_DBG( "pushsuccess unlocks.\n" ); 175 pthread_mutex_unlock(&tasklist_mutex);
197 pthread_mutex_unlock( &tasklist_mutex );
198 MTX_DBG( "pushsuccess unlocked.\n" );
199} 176}
200 177
201int mutex_workqueue_pushresult( ot_taskid taskid, int iovec_entries, struct iovec *iovec ) { 178int mutex_workqueue_pushresult(ot_taskid taskid, size_t iovec_entries, struct iovec *iovec) {
202 struct ot_task * task; 179 struct ot_task *task;
203 const char byte = 'o'; 180 const char byte = 'o';
204 181
205 /* Want exclusive access to tasklist */ 182 /* Want exclusive access to tasklist */
206 MTX_DBG( "pushresult locks.\n" ); 183 pthread_mutex_lock(&tasklist_mutex);
207 pthread_mutex_lock( &tasklist_mutex );
208 MTX_DBG( "pushresult locked.\n" );
209
210 task = tasklist;
211 while( task && ( task->taskid != taskid ) )
212 task = task->next;
213 184
214 if( task ) { 185 for (task = tasklist; task; task = task->next)
215 task->iovec_entries = iovec_entries; 186 if (task->taskid == taskid) {
216 task->iovec = iovec; 187 task->iovec_entries = iovec_entries;
217 task->tasktype = TASK_DONE; 188 task->iovec = iovec;
218 } 189 task->tasktype = TASK_DONE;
190 break;
191 }
219 192
220 /* Release lock */ 193 /* Release lock */
221 MTX_DBG( "pushresult unlocks.\n" ); 194 pthread_mutex_unlock(&tasklist_mutex);
222 pthread_mutex_unlock( &tasklist_mutex );
223 MTX_DBG( "pushresult unlocked.\n" );
224 195
225 io_trywrite( g_self_pipe[1], &byte, 1 ); 196 io_trywrite(g_self_pipe[1], &byte, 1);
226 197
227 /* Indicate whether the worker has to throw away results */ 198 /* Indicate whether the worker has to throw away results */
228 return task ? 0 : -1; 199 return task ? 0 : -1;
229} 200}
230 201
231int64 mutex_workqueue_popresult( int *iovec_entries, struct iovec ** iovec ) { 202int mutex_workqueue_pushchunked(ot_taskid taskid, struct iovec *iovec) {
232 struct ot_task ** task; 203 struct ot_task *task;
233 int64 sock = -1; 204 const char byte = 'o';
234 205
235 /* Want exclusive access to tasklist */ 206 /* Want exclusive access to tasklist */
236 MTX_DBG( "popresult locks.\n" ); 207 pthread_mutex_lock(&tasklist_mutex);
237 pthread_mutex_lock( &tasklist_mutex );
238 MTX_DBG( "popresult locked.\n" );
239 208
240 task = &tasklist; 209 for (task = tasklist; task; task = task->next)
241 while( *task && ( (*task)->tasktype != TASK_DONE ) ) 210 if (task->taskid == taskid) {
242 task = &(*task)->next; 211 if (iovec) {
212 if (iovec_append(&task->iovec_entries, &task->iovec, iovec))
213 task->tasktype = TASK_DONE_PARTIAL;
214 else
215 task = NULL;
216 } else
217 task->tasktype = TASK_DONE;
218 break;
219 }
243 220
244 if( *task && ( (*task)->tasktype == TASK_DONE ) ) { 221 /* Release lock */
245 struct ot_task *ptask = *task; 222 pthread_mutex_unlock(&tasklist_mutex);
246 223
247 *iovec_entries = (*task)->iovec_entries; 224 io_trywrite(g_self_pipe[1], &byte, 1);
248 *iovec = (*task)->iovec;
249 sock = (*task)->sock;
250 225
251 *task = (*task)->next; 226 /* Indicate whether the worker has to throw away results */
252 free( ptask ); 227 return task ? 0 : -1;
253 } 228}
229
230int64_t mutex_workqueue_popresult(size_t *iovec_entries, struct iovec **iovec, int *is_partial) {
231 struct ot_task **task;
232 int64_t sock = -1;
233
234 *is_partial = 0;
235
236 /* Want exclusive access to tasklist */
237 pthread_mutex_lock(&tasklist_mutex);
238
239 for (task = &tasklist; *task; task = &((*task)->next))
240 if (((*task)->tasktype & TASK_CLASS_MASK) == TASK_DONE) {
241 struct ot_task *ptask = *task;
242 *iovec_entries = ptask->iovec_entries;
243 *iovec = ptask->iovec;
244 sock = ptask->sock;
245
246 if ((*task)->tasktype == TASK_DONE) {
247 *task = ptask->next;
248 free(ptask);
249 } else {
250 ptask->iovec_entries = 0;
251 ptask->iovec = NULL;
252 *is_partial = 1;
253 /* Prevent task from showing up immediately again unless new data was added */
254 (*task)->tasktype = TASK_FULLSCRAPE;
255 }
256 break;
257 }
254 258
255 /* Release lock */ 259 /* Release lock */
256 MTX_DBG( "popresult unlocks.\n" ); 260 pthread_mutex_unlock(&tasklist_mutex);
257 pthread_mutex_unlock( &tasklist_mutex );
258 MTX_DBG( "popresult unlocked.\n" );
259 return sock; 261 return sock;
260} 262}
261 263
262void mutex_init( ) { 264void mutex_init(void) {
263 int i; 265 size_t i;
264 pthread_mutex_init(&tasklist_mutex, NULL); 266 pthread_mutex_init(&tasklist_mutex, NULL);
265 pthread_cond_init (&tasklist_being_filled, NULL); 267 pthread_cond_init(&tasklist_being_filled, NULL);
266 for (i=0; i < OT_BUCKET_COUNT; ++i) 268 for (i = 0; i < OT_BUCKET_COUNT; ++i)
267 pthread_mutex_init(bucket_mutex + i, NULL); 269 pthread_mutex_init(bucket_mutex + i, NULL);
268 byte_zero( all_torrents, sizeof( all_torrents ) ); 270 memset(all_torrents, 0, sizeof(all_torrents));
269} 271}
270 272
271void mutex_deinit( ) { 273void mutex_deinit(void) {
272 int i; 274 size_t i;
273 for (i=0; i < OT_BUCKET_COUNT; ++i) 275 for (i = 0; i < OT_BUCKET_COUNT; ++i)
274 pthread_mutex_destroy(bucket_mutex + i); 276 pthread_mutex_destroy(bucket_mutex + i);
275 pthread_mutex_destroy(&tasklist_mutex); 277 pthread_mutex_destroy(&tasklist_mutex);
276 pthread_cond_destroy(&tasklist_being_filled); 278 pthread_cond_destroy(&tasklist_being_filled);
277 byte_zero( all_torrents, sizeof( all_torrents ) ); 279 memset(all_torrents, 0, sizeof(all_torrents));
278} 280}
279
280const char *g_version_mutex_c = "$Source$: $Revision$\n";
diff --git a/ot_mutex.h b/ot_mutex.h
index bd07009..b5af96e 100644
--- a/ot_mutex.h
+++ b/ot_mutex.h
@@ -7,69 +7,74 @@
7#define OT_MUTEX_H__ 7#define OT_MUTEX_H__
8 8
9#include <sys/uio.h> 9#include <sys/uio.h>
10#include "trackerlogic.h"
10 11
11void mutex_init( ); 12void mutex_init(void);
12void mutex_deinit( ); 13void mutex_deinit(void);
13 14
14ot_vector *mutex_bucket_lock( int bucket ); 15ot_vector *mutex_bucket_lock(int bucket);
15ot_vector *mutex_bucket_lock_by_hash( ot_hash hash ); 16ot_vector *mutex_bucket_lock_by_hash(ot_hash const hash);
16 17
17void mutex_bucket_unlock( int bucket, int delta_torrentcount ); 18void mutex_bucket_unlock(int bucket, int delta_torrentcount);
18void mutex_bucket_unlock_by_hash( ot_hash hash, int delta_torrentcount ); 19void mutex_bucket_unlock_by_hash(ot_hash const hash, int delta_torrentcount);
19 20
20size_t mutex_get_torrent_count(); 21size_t mutex_get_torrent_count(void);
21 22
22typedef enum { 23typedef enum {
23 TASK_STATS_CONNS = 0x0001, 24 TASK_STATS_CONNS = 0x0001,
24 TASK_STATS_TCP = 0x0002, 25 TASK_STATS_TCP = 0x0002,
25 TASK_STATS_UDP = 0x0003, 26 TASK_STATS_UDP = 0x0003,
26 TASK_STATS_SCRAPE = 0x0004, 27 TASK_STATS_SCRAPE = 0x0004,
27 TASK_STATS_FULLSCRAPE = 0x0005, 28 TASK_STATS_FULLSCRAPE = 0x0005,
28 TASK_STATS_TPB = 0x0006, 29 TASK_STATS_TPB = 0x0006,
29 TASK_STATS_HTTPERRORS = 0x0007, 30 TASK_STATS_HTTPERRORS = 0x0007,
30 TASK_STATS_VERSION = 0x0008, 31 TASK_STATS_VERSION = 0x0008,
31 TASK_STATS_BUSY_NETWORKS = 0x0009, 32 TASK_STATS_BUSY_NETWORKS = 0x0009,
32 TASK_STATS_RENEW = 0x000a, 33 TASK_STATS_RENEW = 0x000a,
33 TASK_STATS_SYNCS = 0x000b, 34 TASK_STATS_SYNCS = 0x000b,
34 TASK_STATS_COMPLETED = 0x000c, 35 TASK_STATS_COMPLETED = 0x000c,
35 TASK_STATS_NUMWANTS = 0x000d, 36 TASK_STATS_NUMWANTS = 0x000d,
36 37
37 TASK_STATS = 0x0100, /* Mask */ 38 TASK_STATS = 0x0100, /* Mask */
38 TASK_STATS_TORRENTS = 0x0101, 39 TASK_STATS_TORRENTS = 0x0101,
39 TASK_STATS_PEERS = 0x0102, 40 TASK_STATS_PEERS = 0x0102,
40 TASK_STATS_SLASH24S = 0x0103, 41 TASK_STATS_SLASH24S = 0x0103,
41 TASK_STATS_TOP10 = 0x0104, 42 TASK_STATS_TOP10 = 0x0104,
42 TASK_STATS_TOP100 = 0x0105, 43 TASK_STATS_TOP100 = 0x0105,
43 TASK_STATS_EVERYTHING = 0x0106, 44 TASK_STATS_EVERYTHING = 0x0106,
44 TASK_STATS_FULLLOG = 0x0107, 45 TASK_STATS_FULLLOG = 0x0107,
45 TASK_STATS_WOODPECKERS = 0x0108, 46 TASK_STATS_WOODPECKERS = 0x0108,
46
47 TASK_FULLSCRAPE = 0x0200, /* Default mode */
48 TASK_FULLSCRAPE_TPB_BINARY = 0x0201,
49 TASK_FULLSCRAPE_TPB_ASCII = 0x0202,
50 TASK_FULLSCRAPE_TPB_ASCII_PLUS = 0x0203,
51 TASK_FULLSCRAPE_TPB_URLENCODED = 0x0204,
52 TASK_FULLSCRAPE_TRACKERSTATE = 0x0205,
53 47
54 TASK_DMEM = 0x0300, 48 TASK_FULLSCRAPE = 0x0200, /* Default mode */
49 TASK_FULLSCRAPE_TPB_BINARY = 0x0201,
50 TASK_FULLSCRAPE_TPB_ASCII = 0x0202,
51 TASK_FULLSCRAPE_TPB_ASCII_PLUS = 0x0203,
52 TASK_FULLSCRAPE_TPB_URLENCODED = 0x0204,
53 TASK_FULLSCRAPE_TRACKERSTATE = 0x0205,
55 54
56 TASK_DONE = 0x0f00, 55 TASK_DMEM = 0x0300,
57 56
58 TASK_FLAG_GZIP = 0x1000, 57 TASK_DONE = 0x0f00,
59 TASK_FLAG_BZIP2 = 0x2000, 58 TASK_DONE_PARTIAL = 0x0f01,
60 59
61 TASK_TASK_MASK = 0x0fff, 60 TASK_FLAG_GZIP = 0x1000,
62 TASK_CLASS_MASK = 0x0f00, 61 TASK_FLAG_BZIP2 = 0x2000,
63 TASK_FLAGS_MASK = 0xf000 62 TASK_FLAG_ZSTD = 0x4000,
63 TASK_FLAG_CHUNKED = 0x8000,
64
65 TASK_TASK_MASK = 0x0fff,
66 TASK_CLASS_MASK = 0x0f00,
67 TASK_FLAGS_MASK = 0xf000
64} ot_tasktype; 68} ot_tasktype;
65 69
66typedef unsigned long ot_taskid; 70typedef unsigned long ot_taskid;
67 71
68int mutex_workqueue_pushtask( int64 sock, ot_tasktype tasktype ); 72int mutex_workqueue_pushtask(int64_t sock, ot_tasktype tasktype);
69void mutex_workqueue_canceltask( int64 sock ); 73void mutex_workqueue_canceltask(int64_t sock);
70void mutex_workqueue_pushsuccess( ot_taskid taskid ); 74void mutex_workqueue_pushsuccess(ot_taskid taskid);
71ot_taskid mutex_workqueue_poptask( ot_tasktype *tasktype ); 75ot_taskid mutex_workqueue_poptask(ot_tasktype *tasktype);
72int mutex_workqueue_pushresult( ot_taskid taskid, int iovec_entries, struct iovec *iovector ); 76int mutex_workqueue_pushresult(ot_taskid taskid, size_t iovec_entries, struct iovec *iovector);
73int64 mutex_workqueue_popresult( int *iovec_entries, struct iovec ** iovector ); 77int mutex_workqueue_pushchunked(ot_taskid taskid, struct iovec *iovec);
78int64_t mutex_workqueue_popresult(size_t *iovec_entries, struct iovec **iovector, int *is_partial);
74 79
75#endif 80#endif
diff --git a/ot_random.c b/ot_random.c
new file mode 100644
index 0000000..3fae876
--- /dev/null
+++ b/ot_random.c
@@ -0,0 +1,53 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <stdint.h>
8#include <stdlib.h>
9
10/* Opentracker */
11#include "ot_random.h"
12
13#ifdef WANT_INSECURE_RANDOM_FALLBACK
14
15#warning WANT_INSECURE_RANDOM_FALLBACK builds without arc4random. UDP connection ids become predictable and announces can be forged. Do not ship this.
16
17#include <time.h>
18
19void random_init(void) {
20#ifdef WANT_DEV_RANDOM
21 srandomdev();
22#else
23 srandom(time(NULL));
24#endif
25}
26
27/* random() yields 31 bits, so one draw can never set the top bit. Combine
28 two of them rather than hand out a value that is quietly half as wide as
29 its type claims. */
30uint32_t random_uint32(void) { return ((uint32_t)random() << 16) ^ (uint32_t)random(); }
31
32void random_bytes(void *buf, size_t len) {
33 uint8_t *out = buf;
34 /* random() leaves the top bit clear and its lowest bits are the weakest,
35 so take a byte from the middle of each draw */
36 while (len--)
37 *out++ = (uint8_t)(random() >> 8);
38}
39
40#else
41
42#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__DragonFly__) && !defined(__APPLE__) && \
43 !(defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 36)))
44#include <bsd/stdlib.h>
45#endif
46
47void random_init(void) {}
48
49uint32_t random_uint32(void) { return arc4random(); }
50
51void random_bytes(void *buf, size_t len) { arc4random_buf(buf, len); }
52
53#endif
diff --git a/ot_random.h b/ot_random.h
new file mode 100644
index 0000000..5200dbc
--- /dev/null
+++ b/ot_random.h
@@ -0,0 +1,19 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6#ifndef OT_RANDOM_H__
7#define OT_RANDOM_H__
8
9#include <stddef.h>
10#include <stdint.h>
11
12/* Seed whatever generator the build settled on. A no-op when backed by
13 arc4random, which seeds itself from the kernel. */
14void random_init(void);
15
16uint32_t random_uint32(void);
17void random_bytes(void *buf, size_t len);
18
19#endif
diff --git a/ot_rijndael.c b/ot_rijndael.c
index f468e2f..3f36bde 100644
--- a/ot_rijndael.c
+++ b/ot_rijndael.c
@@ -486,5 +486,3 @@ void rijndaelEncrypt128(const uint32_t rk[44], const uint8_t pt[16], uint8_t ct[
486 rk[43]; 486 rk[43];
487 PUTU32(ct + 12, s3); 487 PUTU32(ct + 12, s3);
488} 488}
489
490const char *g_version_rijndael_c = "$Source$: $Revision$\n";
diff --git a/ot_scan.c b/ot_scan.c
new file mode 100644
index 0000000..a96f82b
--- /dev/null
+++ b/ot_scan.c
@@ -0,0 +1,44 @@
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 <errno.h>
8#include <stdint.h>
9#include <stdlib.h>
10
11/* Opentracker */
12#include "ot_scan.h"
13
14size_t scan_udec(const char *src, uint64_t *dest, uint64_t max) {
15 unsigned long long value;
16 char *end;
17
18 if (*src < '0' || *src > '9')
19 return 0;
20
21 errno = 0;
22 value = strtoull(src, &end, 10);
23
24 if (errno == ERANGE || value > (unsigned long long)max)
25 return 0;
26
27 *dest = (uint64_t)value;
28 return (size_t)(end - src);
29}
30
31size_t scan_udec_bencode(const char *src, uint64_t *dest, uint64_t max) {
32 uint64_t value;
33 size_t consumed = scan_udec(src, &value, max);
34
35 /* bencode writes no leading zeros, so anything longer than a single digit
36 must not begin with one. */
37 if (consumed > 1 && *src == '0')
38 return 0;
39
40 if (consumed)
41 *dest = value;
42
43 return consumed;
44}
diff --git a/ot_scan.h b/ot_scan.h
new file mode 100644
index 0000000..860a088
--- /dev/null
+++ b/ot_scan.h
@@ -0,0 +1,21 @@
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_SCAN_H__
7#define OT_SCAN_H__
8
9#include <stddef.h>
10#include <stdint.h>
11
12/* Read an unsigned decimal number from src into dest and return how many
13 characters were consumed. Returns zero and leaves dest untouched when src
14 does not begin with a digit, or when the value would exceed max. */
15size_t scan_udec(const char *src, uint64_t *dest, uint64_t max);
16
17/* As scan_udec, but following the bencode integer grammar, which permits a
18 leading zero on zero alone. */
19size_t scan_udec_bencode(const char *src, uint64_t *dest, uint64_t max);
20
21#endif
diff --git a/ot_stats.c b/ot_stats.c
index 83cd058..7945f4e 100644
--- a/ot_stats.c
+++ b/ot_stats.c
@@ -4,82 +4,81 @@
4 $id$ */ 4 $id$ */
5 5
6/* System */ 6/* System */
7#include <inttypes.h>
8#include <pthread.h>
9#include <stdio.h>
7#include <stdlib.h> 10#include <stdlib.h>
8#include <arpa/inet.h> 11#include <string.h>
9#include <sys/types.h> 12#include <sys/types.h>
10#include <sys/uio.h> 13#include <sys/uio.h>
11#include <sys/mman.h>
12#include <stdio.h>
13#include <string.h>
14#include <pthread.h>
15#include <unistd.h> 14#include <unistd.h>
16#include <inttypes.h>
17#ifdef WANT_SYSLOGS 15#ifdef WANT_SYSLOGS
18#include <syslog.h> 16#include <syslog.h>
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"
25#include "ip6.h"
26 21
27/* Opentracker */ 22/* Opentracker */
28#include "trackerlogic.h" 23#include "ot_accesslist.h"
29#include "ot_mutex.h" 24#include "ot_hex.h"
30#include "ot_iovec.h" 25#include "ot_iovec.h"
26#include "ot_ip.h"
27#include "ot_mutex.h"
31#include "ot_stats.h" 28#include "ot_stats.h"
32#include "ot_accesslist.h" 29#include "trackerlogic.h"
33 30
34#ifndef NO_FULLSCRAPE_LOGGING 31#ifndef NO_FULLSCRAPE_LOGGING
35#define LOG_TO_STDERR( ... ) fprintf( stderr, __VA_ARGS__ ) 32#define LOG_TO_STDERR(...) fprintf(stderr, __VA_ARGS__)
36#else 33#else
37#define LOG_TO_STDERR( ... ) 34#define LOG_TO_STDERR(...)
38#endif 35#endif
39 36
40/* Forward declaration */ 37/* Forward declaration */
41static void stats_make( int *iovec_entries, struct iovec **iovector, ot_tasktype mode ); 38static void stats_make(size_t *iovec_entries, struct iovec **iovector, ot_tasktype mode);
42#define OT_STATS_TMPSIZE 8192 39#define OT_STATS_TMPSIZE 8192
43 40
44/* Clumsy counters... to be rethought */ 41/* Clumsy counters... to be rethought */
45static unsigned long long ot_overall_tcp_connections = 0; 42static unsigned long long ot_overall_tcp_connections;
46static unsigned long long ot_overall_udp_connections = 0; 43static unsigned long long ot_overall_udp_connections;
47static unsigned long long ot_overall_tcp_successfulannounces = 0; 44static unsigned long long ot_overall_tcp_successfulannounces;
48static unsigned long long ot_overall_udp_successfulannounces = 0; 45static unsigned long long ot_overall_udp_successfulannounces;
49static unsigned long long ot_overall_tcp_successfulscrapes = 0; 46static unsigned long long ot_overall_tcp_successfulscrapes;
50static unsigned long long ot_overall_udp_successfulscrapes = 0; 47static unsigned long long ot_overall_udp_successfulscrapes;
51static unsigned long long ot_overall_udp_connectionidmissmatches = 0; 48static unsigned long long ot_overall_udp_connectionidmissmatches;
52static unsigned long long ot_overall_tcp_connects = 0; 49static unsigned long long ot_overall_tcp_connects;
53static unsigned long long ot_overall_udp_connects = 0; 50static unsigned long long ot_overall_udp_connects;
54static unsigned long long ot_overall_completed = 0; 51static unsigned long long ot_overall_completed;
55static unsigned long long ot_full_scrape_count = 0; 52static unsigned long long ot_full_scrape_count;
56static unsigned long long ot_full_scrape_request_count = 0; 53static unsigned long long ot_full_scrape_request_count;
57static unsigned long long ot_full_scrape_size = 0; 54static unsigned long long ot_full_scrape_size;
58static unsigned long long ot_failed_request_counts[CODE_HTTPERROR_COUNT]; 55static unsigned long long ot_failed_request_counts[CODE_HTTPERROR_COUNT];
59static char * ot_failed_request_names[] = { "302 Redirect", "400 Parse Error", "400 Invalid Parameter", "400 Invalid Parameter (compact=0)", "400 Not Modest", "402 Payment Required", "403 Access Denied", "404 Not found", "500 Internal Server Error" }; 56static char *ot_failed_request_names[] = {
57 "302 Redirect", "400 Parse Error", "400 Invalid Parameter", "400 Invalid Parameter (compact=0)", "400 Not Modest",
58 "402 Payment Required", "403 Access Denied", "404 Not found", "500 Internal Server Error"};
60static unsigned long long ot_renewed[OT_PEER_TIMEOUT]; 59static unsigned long long ot_renewed[OT_PEER_TIMEOUT];
61static unsigned long long ot_overall_sync_count; 60static unsigned long long ot_overall_sync_count;
62static unsigned long long ot_overall_stall_count; 61static unsigned long long ot_overall_stall_count;
63 62
64static time_t ot_start_time; 63static time_t ot_start_time;
65 64
66#define STATS_NETWORK_NODE_BITWIDTH 4 65#define STATS_NETWORK_NODE_BITWIDTH 4
67#define STATS_NETWORK_NODE_COUNT (1<<STATS_NETWORK_NODE_BITWIDTH) 66#define STATS_NETWORK_NODE_COUNT (1 << STATS_NETWORK_NODE_BITWIDTH)
68 67
69#define __BYTE(P,D) (((uint8_t*)P)[D/8]) 68#define __BYTE(P, D) (((uint8_t *)P)[D / 8])
70#define __MSK (STATS_NETWORK_NODE_COUNT-1) 69#define __MSK (STATS_NETWORK_NODE_COUNT - 1)
71#define __SHFT(D) ((D^STATS_NETWORK_NODE_BITWIDTH)&STATS_NETWORK_NODE_BITWIDTH) 70#define __SHFT(D) ((D ^ STATS_NETWORK_NODE_BITWIDTH) & STATS_NETWORK_NODE_BITWIDTH)
72 71
73#define __LDR(P,D) ((__BYTE((P),(D))>>__SHFT((D)))&__MSK) 72#define __LDR(P, D) ((__BYTE((P), (D)) >> __SHFT((D))) & __MSK)
74#define __STR(P,D,V) __BYTE((P),(D))=(__BYTE((P),(D))&~(__MSK<<__SHFT((D))))|((V)<<__SHFT((D))) 73#define __STR(P, D, V) __BYTE((P), (D)) = (__BYTE((P), (D)) & ~(__MSK << __SHFT((D)))) | ((V) << __SHFT((D)))
75 74
76#ifdef WANT_V6 75#if 0
77#define STATS_NETWORK_NODE_MAXDEPTH (68-STATS_NETWORK_NODE_BITWIDTH) 76// XXX
78#define STATS_NETWORK_NODE_LIMIT (48-STATS_NETWORK_NODE_BITWIDTH) 77#define STATS_NETWORK_NODE_MAXDEPTH (68 - STATS_NETWORK_NODE_BITWIDTH)
79#else 78#define STATS_NETWORK_NODE_LIMIT (48 - STATS_NETWORK_NODE_BITWIDTH)
80#define STATS_NETWORK_NODE_MAXDEPTH (28-STATS_NETWORK_NODE_BITWIDTH)
81#define STATS_NETWORK_NODE_LIMIT (24-STATS_NETWORK_NODE_BITWIDTH)
82#endif 79#endif
80#define STATS_NETWORK_NODE_MAXDEPTH (28 - STATS_NETWORK_NODE_BITWIDTH)
81#define STATS_NETWORK_NODE_LIMIT (24 - STATS_NETWORK_NODE_BITWIDTH)
83 82
84typedef union stats_network_node stats_network_node; 83typedef union stats_network_node stats_network_node;
85union stats_network_node { 84union stats_network_node {
@@ -91,121 +90,125 @@ union stats_network_node {
91static stats_network_node *stats_network_counters_root; 90static stats_network_node *stats_network_counters_root;
92#endif 91#endif
93 92
94static int stat_increase_network_count( stats_network_node **pnode, int depth, uintptr_t ip ) { 93static int stat_increase_network_count(stats_network_node **pnode, int depth, uintptr_t ip) {
95 int foo = __LDR(ip,depth); 94 int foo = __LDR(ip, depth);
96 stats_network_node *node; 95 stats_network_node *node;
97 96
98 if( !*pnode ) { 97 if (!*pnode) {
99 *pnode = malloc( sizeof( stats_network_node ) ); 98 *pnode = malloc(sizeof(stats_network_node));
100 if( !*pnode ) 99 if (!*pnode)
101 return -1; 100 return -1;
102 memset( *pnode, 0, sizeof( stats_network_node ) ); 101 memset(*pnode, 0, sizeof(stats_network_node));
103 } 102 }
104 node = *pnode; 103 node = *pnode;
105 104
106 if( depth < STATS_NETWORK_NODE_MAXDEPTH ) 105 if (depth < STATS_NETWORK_NODE_MAXDEPTH)
107 return stat_increase_network_count( node->children + foo, depth+STATS_NETWORK_NODE_BITWIDTH, ip ); 106 return stat_increase_network_count(node->children + foo, depth + STATS_NETWORK_NODE_BITWIDTH, ip);
108 107
109 node->counters[ foo ]++; 108 node->counters[foo]++;
110 return 0; 109 return 0;
111} 110}
112 111
113static int stats_shift_down_network_count( stats_network_node **node, int depth, int shift ) { 112static int stats_shift_down_network_count(stats_network_node **node, int depth, int shift) {
114 int i, rest = 0; 113 size_t i, rest = 0;
115 114
116 if( !*node ) 115 if (!*node)
117 return 0; 116 return 0;
118 117
119 for( i=0; i<STATS_NETWORK_NODE_COUNT; ++i ) 118 for (i = 0; i < STATS_NETWORK_NODE_COUNT; ++i)
120 if( depth < STATS_NETWORK_NODE_MAXDEPTH ) 119 if (depth < STATS_NETWORK_NODE_MAXDEPTH)
121 rest += stats_shift_down_network_count( (*node)->children + i, depth+STATS_NETWORK_NODE_BITWIDTH, shift ); 120 rest += stats_shift_down_network_count((*node)->children + i, depth + STATS_NETWORK_NODE_BITWIDTH, shift);
122 else 121 else
123 rest += (*node)->counters[i] >>= shift; 122 rest += (*node)->counters[i] >>= shift;
124 123
125 if( !rest ) { 124 if (!rest) {
126 free( *node ); 125 free(*node);
127 *node = NULL; 126 *node = NULL;
128 } 127 }
129 128
130 return rest; 129 return rest;
131} 130}
132 131
133static size_t stats_get_highscore_networks( stats_network_node *node, int depth, ot_ip6 node_value, size_t *scores, ot_ip6 *networks, int network_count, int limit ) { 132static 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,
134 size_t score = 0; 133 int limit) {
135 int i; 134 size_t i, score = 0;
136 135
137 if( !node ) return 0; 136 if (!node)
137 return 0;
138 138
139 if( depth < limit ) { 139 if (depth < limit) {
140 for( i=0; i<STATS_NETWORK_NODE_COUNT; ++i ) 140 for (i = 0; i < STATS_NETWORK_NODE_COUNT; ++i)
141 if( node->children[i] ) { 141 if (node->children[i]) {
142 __STR(node_value,depth,i); 142 __STR(node_value, depth, i);
143 score += stats_get_highscore_networks( node->children[i], depth+STATS_NETWORK_NODE_BITWIDTH, node_value, scores, networks, network_count, limit ); 143 score += stats_get_highscore_networks(node->children[i], depth + STATS_NETWORK_NODE_BITWIDTH, node_value, scores, networks, network_count, limit);
144 } 144 }
145 return score; 145 return score;
146 } 146 }
147 147
148 if( depth > limit && depth < STATS_NETWORK_NODE_MAXDEPTH ) { 148 if (depth > limit && depth < STATS_NETWORK_NODE_MAXDEPTH) {
149 for( i=0; i<STATS_NETWORK_NODE_COUNT; ++i ) 149 for (i = 0; i < STATS_NETWORK_NODE_COUNT; ++i)
150 if( node->children[i] ) 150 if (node->children[i])
151 score += stats_get_highscore_networks( node->children[i], depth+STATS_NETWORK_NODE_BITWIDTH, node_value, scores, networks, network_count, limit ); 151 score += stats_get_highscore_networks(node->children[i], depth + STATS_NETWORK_NODE_BITWIDTH, node_value, scores, networks, network_count, limit);
152 return score; 152 return score;
153 } 153 }
154 154
155 if( depth > limit && depth == STATS_NETWORK_NODE_MAXDEPTH ) { 155 if (depth > limit && depth == STATS_NETWORK_NODE_MAXDEPTH) {
156 for( i=0; i<STATS_NETWORK_NODE_COUNT; ++i ) 156 for (i = 0; i < STATS_NETWORK_NODE_COUNT; ++i)
157 score += node->counters[i]; 157 score += node->counters[i];
158 return score; 158 return score;
159 } 159 }
160 160
161 /* if( depth == limit ) */ 161 /* if( depth == limit ) */
162 for( i=0; i<STATS_NETWORK_NODE_COUNT; ++i ) { 162 for (i = 0; i < STATS_NETWORK_NODE_COUNT; ++i) {
163 int j=1; 163 int j = 1;
164 size_t node_score; 164 size_t node_score;
165 165
166 if( depth == STATS_NETWORK_NODE_MAXDEPTH ) 166 if (depth == STATS_NETWORK_NODE_MAXDEPTH)
167 node_score = node->counters[i]; 167 node_score = node->counters[i];
168 else 168 else
169 node_score = stats_get_highscore_networks( node->children[i], depth+STATS_NETWORK_NODE_BITWIDTH, node_value, scores, networks, network_count, limit ); 169 node_score = stats_get_highscore_networks(node->children[i], depth + STATS_NETWORK_NODE_BITWIDTH, node_value, scores, networks, network_count, limit);
170 170
171 score += node_score; 171 score += node_score;
172 172
173 if( node_score <= scores[0] ) continue; 173 if (node_score <= scores[0])
174 continue;
174 175
175 __STR(node_value,depth,i); 176 __STR(node_value, depth, i);
176 while( j < network_count && node_score > scores[j] ) ++j; 177 while (j < network_count && node_score > scores[j])
178 ++j;
177 --j; 179 --j;
178 180
179 memcpy( scores, scores + 1, j * sizeof( *scores ) ); 181 memcpy(scores, scores + 1, j * sizeof(*scores));
180 memcpy( networks, networks + 1, j * sizeof( *networks ) ); 182 memcpy(networks, networks + 1, j * sizeof(*networks));
181 scores[ j ] = node_score; 183 scores[j] = node_score;
182 memcpy( networks + j, node_value, sizeof( *networks ) ); 184 memcpy(networks + j, node_value, sizeof(*networks));
183 } 185 }
184 186
185 return score; 187 return score;
186} 188}
187 189
188static size_t stats_return_busy_networks( char * reply, stats_network_node *tree, int amount, int limit ) { 190static size_t stats_return_busy_networks(char *reply, stats_network_node *tree, size_t amount, int limit) {
189 ot_ip6 networks[amount]; 191 ot_ip6 networks[amount];
190 ot_ip6 node_value; 192 ot_ip6 node_value;
191 size_t scores[amount]; 193 size_t scores[amount];
192 int i; 194 size_t i;
193 char * r = reply; 195 char *r = reply;
194 196
195 memset( scores, 0, sizeof( scores ) ); 197 memset(scores, 0, sizeof(scores));
196 memset( networks, 0, sizeof( networks ) ); 198 memset(networks, 0, sizeof(networks));
197 memset( node_value, 0, sizeof( node_value ) ); 199 memset(node_value, 0, sizeof(node_value));
198 200
199 stats_get_highscore_networks( tree, 0, node_value, scores, networks, amount, limit ); 201 stats_get_highscore_networks(tree, 0, node_value, scores, networks, amount, limit);
200 202
201 r += sprintf( r, "Networks, limit /%d:\n", limit+STATS_NETWORK_NODE_BITWIDTH ); 203 r += sprintf(r, "Networks, limit /%d:\n", limit + STATS_NETWORK_NODE_BITWIDTH);
202 for( i=amount-1; i>=0; --i) { 204 for (i = amount; i > 0; --i) {
203 if( scores[i] ) { 205 if (scores[i - 1]) {
204 r += sprintf( r, "%08zd: ", scores[i] ); 206 r += sprintf(r, "%08zd: ", scores[i - 1]);
205#ifdef WANT_V6 207 // #ifdef WANT_V6
206 r += fmt_ip6c( r, networks[i] ); 208 r += ip_format(r, networks[i - 1]);
207#else 209#if 0
208 r += fmt_ip4( r, networks[i]); 210 // XXX
211 r += fmt_ip4( r, networks[i - 1]);
209#endif 212#endif
210 *r++ = '\n'; 213 *r++ = '\n';
211 } 214 }
@@ -215,64 +218,65 @@ static size_t stats_return_busy_networks( char * reply, stats_network_node *tree
215 return r - reply; 218 return r - reply;
216} 219}
217 220
218static size_t stats_slash24s_txt( char *reply, size_t amount ) { 221static size_t stats_slash24s_txt(char *reply, size_t amount) {
219 stats_network_node *slash24s_network_counters_root = NULL; 222 stats_network_node *slash24s_network_counters_root = NULL;
220 char *r=reply; 223 char *r = reply;
221 int bucket; 224 size_t i, bucket, peer_size = OT_PEER_SIZE4;
222 size_t i;
223 225
224 for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) { 226 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
225 ot_vector *torrents_list = mutex_bucket_lock( bucket ); 227 ot_vector *torrents_list = mutex_bucket_lock(bucket);
226 for( i=0; i<torrents_list->size; ++i ) { 228 for (i = 0; i < torrents_list->size; ++i) {
227 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[i] ).peer_list; 229 ot_peerlist *peer_list = (((ot_torrent *)(torrents_list->data))[i]).peer_list4;
228 ot_vector *bucket_list = &peer_list->peers; 230 ot_vector *bucket_list = &peer_list->peers;
229 int num_buckets = 1; 231 int num_buckets = 1;
230 232
231 if( OT_PEERLIST_HASBUCKETS( peer_list ) ) { 233 if (OT_PEERLIST_HASBUCKETS(peer_list)) {
232 num_buckets = bucket_list->size; 234 num_buckets = bucket_list->size;
233 bucket_list = (ot_vector *)bucket_list->data; 235 bucket_list = (ot_vector *)bucket_list->data;
234 } 236 }
235 237
236 while( num_buckets-- ) { 238 while (num_buckets--) {
237 ot_peer *peers = (ot_peer*)bucket_list->data; 239 ot_peer *peers = (ot_peer *)bucket_list->data;
238 size_t numpeers = bucket_list->size; 240 size_t numpeers = bucket_list->size;
239 while( numpeers-- ) 241 while (numpeers--) {
240 if( stat_increase_network_count( &slash24s_network_counters_root, 0, (uintptr_t)(peers++) ) ) 242 if (stat_increase_network_count(&slash24s_network_counters_root, 0, (uintptr_t)(peers)))
241 goto bailout_unlock; 243 goto bailout_unlock;
244 peers += peer_size;
245 }
242 ++bucket_list; 246 ++bucket_list;
243 } 247 }
244 } 248 }
245 mutex_bucket_unlock( bucket, 0 ); 249 mutex_bucket_unlock(bucket, 0);
246 if( !g_opentracker_running ) 250 if (!g_opentracker_running)
247 goto bailout_error; 251 goto bailout_error;
248 } 252 }
249 253
250 /* The tree is built. Now analyze */ 254 /* The tree is built. Now analyze */
251 r += stats_return_busy_networks( r, slash24s_network_counters_root, amount, STATS_NETWORK_NODE_MAXDEPTH ); 255 r += stats_return_busy_networks(r, slash24s_network_counters_root, amount, STATS_NETWORK_NODE_MAXDEPTH);
252 r += stats_return_busy_networks( r, slash24s_network_counters_root, amount, STATS_NETWORK_NODE_LIMIT ); 256 r += stats_return_busy_networks(r, slash24s_network_counters_root, amount, STATS_NETWORK_NODE_LIMIT);
253 goto success; 257 goto success;
254 258
255bailout_unlock: 259bailout_unlock:
256 mutex_bucket_unlock( bucket, 0 ); 260 mutex_bucket_unlock(bucket, 0);
257bailout_error: 261bailout_error:
258 r = reply; 262 r = reply;
259success: 263success:
260 stats_shift_down_network_count( &slash24s_network_counters_root, 0, sizeof(int)*8-1 ); 264 stats_shift_down_network_count(&slash24s_network_counters_root, 0, sizeof(int) * 8 - 1);
261 265
262 return r-reply; 266 return r - reply;
263} 267}
264 268
265#ifdef WANT_SPOT_WOODPECKER 269#ifdef WANT_SPOT_WOODPECKER
266static stats_network_node *stats_woodpeckers_tree; 270static stats_network_node *stats_woodpeckers_tree;
267static pthread_mutex_t g_woodpeckers_mutex = PTHREAD_MUTEX_INITIALIZER; 271static pthread_mutex_t g_woodpeckers_mutex = PTHREAD_MUTEX_INITIALIZER;
268 272
269static size_t stats_return_woodpeckers( char * reply, int amount ) { 273static size_t stats_return_woodpeckers(char *reply, int amount) {
270 char * r = reply; 274 char *r = reply;
271 275
272 pthread_mutex_lock( &g_woodpeckers_mutex ); 276 pthread_mutex_lock(&g_woodpeckers_mutex);
273 r += stats_return_busy_networks( r, stats_woodpeckers_tree, amount, STATS_NETWORK_NODE_MAXDEPTH ); 277 r += stats_return_busy_networks(r, stats_woodpeckers_tree, amount, STATS_NETWORK_NODE_MAXDEPTH);
274 pthread_mutex_unlock( &g_woodpeckers_mutex ); 278 pthread_mutex_unlock(&g_woodpeckers_mutex);
275 return r-reply; 279 return r - reply;
276} 280}
277#endif 281#endif
278 282
@@ -282,492 +286,478 @@ typedef struct {
282 unsigned long long seed_count; 286 unsigned long long seed_count;
283} torrent_stats; 287} torrent_stats;
284 288
285static int torrent_statter( ot_torrent *torrent, uintptr_t data ) { 289static int torrent_statter(ot_torrent *torrent, uintptr_t data) {
286 torrent_stats *stats = (torrent_stats*)data; 290 torrent_stats *stats = (torrent_stats *)data;
287 stats->torrent_count++; 291 stats->torrent_count++;
288 stats->peer_count += torrent->peer_list->peer_count; 292 stats->peer_count += torrent->peer_list6->peer_count + torrent->peer_list4->peer_count;
289 stats->seed_count += torrent->peer_list->seed_count; 293 stats->seed_count += torrent->peer_list6->seed_count + torrent->peer_list4->seed_count;
290 return 0; 294 return 0;
291} 295}
292 296
293/* Converter function from memory to human readable hex strings */ 297/* Converter function from memory to human readable hex strings */
294static char*to_hex(char*d,uint8_t*s){char*m="0123456789ABCDEF";char *t=d;char*e=d+40;while(d<e){*d++=m[*s>>4];*d++=m[*s++&15];}*d=0;return t;} 298typedef struct {
299 size_t val;
300 ot_hash hash;
301} ot_record;
302
303static inline void stats_insert_into_highscore(size_t val, ot_record* list, size_t amount, ot_hash* hash) {
304 size_t i;
305 for (i = 0; (i < amount) && (list[i].val > val); ++i)
306 {}
307 /* if we score better than a record in the list, we need to make room in the high
308 score array by moving lower scoring entries up */
309 if (i < amount) {
310 memmove(list + i + 1, list + i, (amount - i - 1) * sizeof(ot_record));
311
312 memcpy(&list[i].hash, hash, sizeof(ot_hash));
313 list[i].val = val;
314 }
315}
295 316
296typedef struct { size_t val; ot_torrent * torrent; } ot_record;
297 317
298/* Fetches stats from tracker */ 318/* Fetches stats from tracker */
299size_t stats_top_txt( char * reply, int amount ) { 319size_t stats_top_txt(char *reply, size_t amount) {
300 size_t j; 320 size_t j, idx, bucket;
301 ot_record top100s[100], top100c[100]; 321 ot_record top100s[100], top100c[100], top100l[100];
302 char *r = reply, hex_out[42]; 322 char *r = reply, hex_out[42];
303 int idx, bucket;
304 323
305 if( amount > 100 ) 324 if (amount > 100)
306 amount = 100; 325 amount = 100;
307 326
308 byte_zero( top100s, sizeof( top100s ) ); 327 memset(top100s, 0, sizeof(top100s));
309 byte_zero( top100c, sizeof( top100c ) ); 328 memset(top100c, 0, sizeof(top100c));
329 memset(top100l, 0, sizeof(top100l));
310 330
311 for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) { 331 /* Iterate over complete torrent list */
312 ot_vector *torrents_list = mutex_bucket_lock( bucket ); 332 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
313 for( j=0; j<torrents_list->size; ++j ) { 333 ot_vector *torrents_list = mutex_bucket_lock(bucket);
314 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list; 334 for (j = 0; j < torrents_list->size; ++j) {
315 int idx = amount - 1; while( (idx >= 0) && ( peer_list->peer_count > top100c[idx].val ) ) --idx; 335 ot_torrent *torrent = (ot_torrent *)(torrents_list->data) + j;
316 if ( idx++ != amount - 1 ) { 336 size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count;
317 memmove( top100c + idx + 1, top100c + idx, ( amount - 1 - idx ) * sizeof( ot_record ) ); 337 size_t seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count;
318 top100c[idx].val = peer_list->peer_count; 338 size_t leech_count = peer_count - seed_count;
319 top100c[idx].torrent = (ot_torrent*)(torrents_list->data) + j; 339
320 } 340 stats_insert_into_highscore(peer_count, top100c, amount, &torrent->hash);
321 idx = amount - 1; while( (idx >= 0) && ( peer_list->seed_count > top100s[idx].val ) ) --idx; 341 stats_insert_into_highscore(seed_count, top100s, amount, &torrent->hash);
322 if ( idx++ != amount - 1 ) { 342 stats_insert_into_highscore(leech_count, top100l, amount, &torrent->hash);
323 memmove( top100s + idx + 1, top100s + idx, ( amount - 1 - idx ) * sizeof( ot_record ) );
324 top100s[idx].val = peer_list->seed_count;
325 top100s[idx].torrent = (ot_torrent*)(torrents_list->data) + j;
326 }
327 } 343 }
328 mutex_bucket_unlock( bucket, 0 ); 344 mutex_bucket_unlock(bucket, 0);
329 if( !g_opentracker_running ) 345 if (!g_opentracker_running)
330 return 0; 346 return 0;
331 } 347 }
332 348
333 r += sprintf( r, "Top %d torrents by peers:\n", amount ); 349 r += sprintf(r, "Top %zd torrents by peers:\n", amount);
334 for( idx=0; idx<amount; ++idx ) 350 for (idx = 0; idx < amount; ++idx)
335 if( top100c[idx].torrent ) 351 if (top100c[idx].val)
336 r += sprintf( r, "\t%zd\t%s\n", top100c[idx].val, to_hex( hex_out, top100c[idx].torrent->hash) ); 352 r += sprintf(r, "\t%zd\t%s\n", top100c[idx].val, hex_fromhash(hex_out, top100c[idx].hash));
337 r += sprintf( r, "Top %d torrents by seeds:\n", amount ); 353 r += sprintf(r, "Top %zd torrents by seeds:\n", amount);
338 for( idx=0; idx<amount; ++idx ) 354 for (idx = 0; idx < amount; ++idx)
339 if( top100s[idx].torrent ) 355 if (top100s[idx].val)
340 r += sprintf( r, "\t%zd\t%s\n", top100s[idx].val, to_hex( hex_out, top100s[idx].torrent->hash) ); 356 r += sprintf(r, "\t%zd\t%s\n", top100s[idx].val, hex_fromhash(hex_out, top100s[idx].hash));
357 r += sprintf(r, "Top %zd torrents by leechers:\n", amount);
358 for (idx = 0; idx < amount; ++idx)
359 if (top100l[idx].val)
360 r += sprintf(r, "\t%zd\t%s\n", top100l[idx].val, hex_fromhash(hex_out, top100l[idx].hash));
341 361
342 return r - reply; 362 return r - reply;
343} 363}
344 364
345static unsigned long events_per_time( unsigned long long events, time_t t ) { 365static unsigned long events_per_time(unsigned long long events, time_t t) { return events / ((unsigned int)t ? (unsigned int)t : 1); }
346 return events / ( (unsigned int)t ? (unsigned int)t : 1 );
347}
348 366
349static size_t stats_connections_mrtg( char * reply ) { 367static size_t stats_connections_mrtg(char *reply) {
350 ot_time t = time( NULL ) - ot_start_time; 368 ot_time t = time(NULL) - ot_start_time;
351 return sprintf( reply, 369 return sprintf(reply, "%llu\n%llu\n%i seconds (%i hours)\nopentracker connections, %lu conns/s :: %lu success/s.",
352 "%llu\n%llu\n%i seconds (%i hours)\nopentracker connections, %lu conns/s :: %lu success/s.", 370 ot_overall_tcp_connections + ot_overall_udp_connections,
353 ot_overall_tcp_connections+ot_overall_udp_connections, 371 ot_overall_tcp_successfulannounces + ot_overall_udp_successfulannounces + ot_overall_udp_connects, (int)t, (int)(t / 3600),
354 ot_overall_tcp_successfulannounces+ot_overall_udp_successfulannounces+ot_overall_udp_connects, 372 events_per_time(ot_overall_tcp_connections + ot_overall_udp_connections, t),
355 (int)t, 373 events_per_time(ot_overall_tcp_successfulannounces + ot_overall_udp_successfulannounces + ot_overall_udp_connects, t));
356 (int)(t / 3600),
357 events_per_time( ot_overall_tcp_connections+ot_overall_udp_connections, t ),
358 events_per_time( ot_overall_tcp_successfulannounces+ot_overall_udp_successfulannounces+ot_overall_udp_connects, t )
359 );
360} 374}
361 375
362static size_t stats_udpconnections_mrtg( char * reply ) { 376static size_t stats_udpconnections_mrtg(char *reply) {
363 ot_time t = time( NULL ) - ot_start_time; 377 ot_time t = time(NULL) - ot_start_time;
364 return sprintf( reply, 378 return sprintf(reply, "%llu\n%llu\n%i seconds (%i hours)\nopentracker udp4 stats, %lu conns/s :: %lu success/s.", ot_overall_udp_connections,
365 "%llu\n%llu\n%i seconds (%i hours)\nopentracker udp4 stats, %lu conns/s :: %lu success/s.", 379 ot_overall_udp_successfulannounces + ot_overall_udp_connects, (int)t, (int)(t / 3600), events_per_time(ot_overall_udp_connections, t),
366 ot_overall_udp_connections, 380 events_per_time(ot_overall_udp_successfulannounces + ot_overall_udp_connects, t));
367 ot_overall_udp_successfulannounces+ot_overall_udp_connects,
368 (int)t,
369 (int)(t / 3600),
370 events_per_time( ot_overall_udp_connections, t ),
371 events_per_time( ot_overall_udp_successfulannounces+ot_overall_udp_connects, t )
372 );
373} 381}
374 382
375static size_t stats_tcpconnections_mrtg( char * reply ) { 383static size_t stats_tcpconnections_mrtg(char *reply) {
376 time_t t = time( NULL ) - ot_start_time; 384 time_t t = time(NULL) - ot_start_time;
377 return sprintf( reply, 385 return sprintf(reply, "%llu\n%llu\n%i seconds (%i hours)\nopentracker tcp4 stats, %lu conns/s :: %lu success/s.", ot_overall_tcp_connections,
378 "%llu\n%llu\n%i seconds (%i hours)\nopentracker tcp4 stats, %lu conns/s :: %lu success/s.", 386 ot_overall_tcp_successfulannounces, (int)t, (int)(t / 3600), events_per_time(ot_overall_tcp_connections, t),
379 ot_overall_tcp_connections, 387 events_per_time(ot_overall_tcp_successfulannounces, t));
380 ot_overall_tcp_successfulannounces,
381 (int)t,
382 (int)(t / 3600),
383 events_per_time( ot_overall_tcp_connections, t ),
384 events_per_time( ot_overall_tcp_successfulannounces, t )
385 );
386} 388}
387 389
388static size_t stats_scrape_mrtg( char * reply ) { 390static size_t stats_scrape_mrtg(char *reply) {
389 time_t t = time( NULL ) - ot_start_time; 391 time_t t = time(NULL) - ot_start_time;
390 return sprintf( reply, 392 return sprintf(reply, "%llu\n%llu\n%i seconds (%i hours)\nopentracker scrape stats, %lu scrape/s (tcp and udp)", ot_overall_tcp_successfulscrapes,
391 "%llu\n%llu\n%i seconds (%i hours)\nopentracker scrape stats, %lu scrape/s (tcp and udp)", 393 ot_overall_udp_successfulscrapes, (int)t, (int)(t / 3600),
392 ot_overall_tcp_successfulscrapes, 394 events_per_time((ot_overall_tcp_successfulscrapes + ot_overall_udp_successfulscrapes), t));
393 ot_overall_udp_successfulscrapes,
394 (int)t,
395 (int)(t / 3600),
396 events_per_time( (ot_overall_tcp_successfulscrapes+ot_overall_udp_successfulscrapes), t )
397 );
398} 395}
399 396
400static size_t stats_fullscrapes_mrtg( char * reply ) { 397static size_t stats_fullscrapes_mrtg(char *reply) {
401 ot_time t = time( NULL ) - ot_start_time; 398 ot_time t = time(NULL) - ot_start_time;
402 return sprintf( reply, 399 return sprintf(reply, "%llu\n%llu\n%i seconds (%i hours)\nopentracker full scrape stats, %lu conns/s :: %lu bytes/s.", ot_full_scrape_count * 1000,
403 "%llu\n%llu\n%i seconds (%i hours)\nopentracker full scrape stats, %lu conns/s :: %lu bytes/s.", 400 ot_full_scrape_size, (int)t, (int)(t / 3600), events_per_time(ot_full_scrape_count, t), events_per_time(ot_full_scrape_size, t));
404 ot_full_scrape_count * 1000,
405 ot_full_scrape_size,
406 (int)t,
407 (int)(t / 3600),
408 events_per_time( ot_full_scrape_count, t ),
409 events_per_time( ot_full_scrape_size, t )
410 );
411} 401}
412 402
413static size_t stats_peers_mrtg( char * reply ) { 403static size_t stats_peers_mrtg(char *reply) {
414 torrent_stats stats = {0,0,0}; 404 torrent_stats stats = {0, 0, 0};
415 405
416 iterate_all_torrents( torrent_statter, (uintptr_t)&stats ); 406 iterate_all_torrents(torrent_statter, (uintptr_t)&stats);
417 407
418 return sprintf( reply, "%llu\n%llu\nopentracker serving %llu torrents\nopentracker", 408 return sprintf(reply, "%llu\n%llu\nopentracker serving %llu torrents\nopentracker", stats.peer_count, stats.seed_count, stats.torrent_count);
419 stats.peer_count,
420 stats.seed_count,
421 stats.torrent_count
422 );
423} 409}
424 410
425static size_t stats_torrents_mrtg( char * reply ) 411static size_t stats_torrents_mrtg(char *reply) {
426{
427 size_t torrent_count = mutex_get_torrent_count(); 412 size_t torrent_count = mutex_get_torrent_count();
428 413
429 return sprintf( reply, "%zd\n%zd\nopentracker serving %zd torrents\nopentracker", 414 return sprintf(reply, "%zd\n%zd\nopentracker serving %zd torrents\nopentracker", torrent_count, (size_t)0, torrent_count);
430 torrent_count,
431 (size_t)0,
432 torrent_count
433 );
434} 415}
435 416
436static size_t stats_httperrors_txt ( char * reply ) { 417static size_t stats_httperrors_txt(char *reply) {
437 return sprintf( reply, "302 RED %llu\n400 ... %llu\n400 PAR %llu\n400 COM %llu\n403 IP %llu\n404 INV %llu\n500 SRV %llu\n", 418 return sprintf(reply, "302 RED %llu\n400 ... %llu\n400 PAR %llu\n400 COM %llu\n403 IP %llu\n404 INV %llu\n500 SRV %llu\n", ot_failed_request_counts[0],
438 ot_failed_request_counts[0], ot_failed_request_counts[1], ot_failed_request_counts[2], 419 ot_failed_request_counts[1], ot_failed_request_counts[2], ot_failed_request_counts[3], ot_failed_request_counts[4],
439 ot_failed_request_counts[3], ot_failed_request_counts[4], ot_failed_request_counts[5], 420 ot_failed_request_counts[5], ot_failed_request_counts[6]);
440 ot_failed_request_counts[6] );
441} 421}
442 422
443static size_t stats_return_renew_bucket( char * reply ) { 423static size_t stats_return_renew_bucket(char *reply) {
444 char *r = reply; 424 char *r = reply;
445 int i; 425 int i;
446 426
447 for( i=0; i<OT_PEER_TIMEOUT; ++i ) 427 for (i = 0; i < OT_PEER_TIMEOUT; ++i)
448 r+=sprintf(r,"%02i %llu\n", i, ot_renewed[i] ); 428 r += sprintf(r, "%02i %llu\n", i, ot_renewed[i]);
449 return r - reply; 429 return r - reply;
450} 430}
451 431
452static size_t stats_return_sync_mrtg( char * reply ) { 432static size_t stats_return_sync_mrtg(char *reply) {
453 ot_time t = time( NULL ) - ot_start_time; 433 ot_time t = time(NULL) - ot_start_time;
454 return sprintf( reply, 434 return sprintf(reply, "%llu\n%llu\n%i seconds (%i hours)\nopentracker connections, %lu conns/s :: %lu success/s.", ot_overall_sync_count, 0LL, (int)t,
455 "%llu\n%llu\n%i seconds (%i hours)\nopentracker connections, %lu conns/s :: %lu success/s.", 435 (int)(t / 3600), events_per_time(ot_overall_tcp_connections + ot_overall_udp_connections, t),
456 ot_overall_sync_count, 436 events_per_time(ot_overall_tcp_successfulannounces + ot_overall_udp_successfulannounces + ot_overall_udp_connects, t));
457 0LL,
458 (int)t,
459 (int)(t / 3600),
460 events_per_time( ot_overall_tcp_connections+ot_overall_udp_connections, t ),
461 events_per_time( ot_overall_tcp_successfulannounces+ot_overall_udp_successfulannounces+ot_overall_udp_connects, t )
462 );
463} 437}
464 438
465static size_t stats_return_completed_mrtg( char * reply ) { 439static size_t stats_return_completed_mrtg(char *reply) {
466 ot_time t = time( NULL ) - ot_start_time; 440 ot_time t = time(NULL) - ot_start_time;
467 441
468 return sprintf( reply, 442 return sprintf(reply, "%llu\n%llu\n%i seconds (%i hours)\nopentracker, %lu completed/h.", ot_overall_completed, 0LL, (int)t, (int)(t / 3600),
469 "%llu\n%llu\n%i seconds (%i hours)\nopentracker, %lu completed/h.", 443 events_per_time(ot_overall_completed, t / 3600));
470 ot_overall_completed,
471 0LL,
472 (int)t,
473 (int)(t / 3600),
474 events_per_time( ot_overall_completed, t / 3600 )
475 );
476} 444}
477 445
478#ifdef WANT_LOG_NUMWANT 446#ifdef WANT_LOG_NUMWANT
479extern unsigned long long numwants[201]; 447extern unsigned long long numwants[201];
480static size_t stats_return_numwants( char * reply ) { 448static size_t stats_return_numwants(char *reply) {
481 char * r = reply; 449 char *r = reply;
482 int i; 450 int i;
483 for( i=0; i<=200; ++i ) 451 for (i = 0; i <= 200; ++i)
484 r += sprintf( r, "%03d => %lld\n", i, numwants[i] ); 452 r += sprintf(r, "%03d => %lld\n", i, numwants[i]);
485 return r-reply; 453 return r - reply;
486} 454}
487#endif 455#endif
488 456
489#ifdef WANT_FULLLOG_NETWORKS 457#ifdef WANT_FULLLOG_NETWORKS
490static void stats_return_fulllog( int *iovec_entries, struct iovec **iovector, char *r ) { 458static void stats_return_fulllog(size_t *iovec_entries, struct iovec **iovector, char *r) {
491 ot_log *loglist = g_logchain_first, *llnext; 459 ot_log *loglist = g_logchain_first, *llnext;
492 char * re = r + OT_STATS_TMPSIZE; 460 char *re = r + OT_STATS_TMPSIZE;
493 461
494 g_logchain_first = g_logchain_last = 0; 462 g_logchain_first = g_logchain_last = 0;
495 463
496 while( loglist ) { 464 while (loglist) {
497 if( r + ( loglist->size + 64 ) >= re ) { 465 if (r + (loglist->size + 64) >= re) {
498 r = iovec_fix_increase_or_free( iovec_entries, iovector, r, 32 * OT_STATS_TMPSIZE ); 466 r = iovec_fix_increase_or_free(iovec_entries, iovector, r, 32 * OT_STATS_TMPSIZE);
499 if( !r ) return; 467 if (!r)
468 return;
500 re = r + 32 * OT_STATS_TMPSIZE; 469 re = r + 32 * OT_STATS_TMPSIZE;
501 } 470 }
502 r += sprintf( r, "%08ld: ", loglist->time ); 471 r += sprintf(r, "%08ld: ", loglist->time);
503 r += fmt_ip6c( r, loglist->ip ); 472 r += ip_format(r, loglist->ip);
504 *r++ = '\n'; 473 *r++ = '\n';
505 memcpy( r, loglist->data, loglist->size ); 474 memcpy(r, loglist->data, loglist->size);
506 r += loglist->size; 475 r += loglist->size;
507 *r++ = '\n'; 476 *r++ = '\n';
508 *r++ = '*'; 477 *r++ = '*';
509 *r++ = '\n'; 478 *r++ = '\n';
510 *r++ = '\n'; 479 *r++ = '\n';
511 480
512 llnext = loglist->next; 481 llnext = loglist->next;
513 free( loglist->data ); 482 free(loglist->data);
514 free( loglist ); 483 free(loglist);
515 loglist = llnext; 484 loglist = llnext;
516 } 485 }
517 iovec_fixlast( iovec_entries, iovector, r ); 486 iovec_fixlast(iovec_entries, iovector, r);
518} 487}
519#endif 488#endif
520 489
521static size_t stats_return_everything( char * reply ) { 490static size_t stats_return_everything(char *reply) {
522 torrent_stats stats = {0,0,0}; 491 torrent_stats stats = {0, 0, 0};
523 int i; 492 int i;
524 char * r = reply; 493 char *r = reply;
525 494
526 iterate_all_torrents( torrent_statter, (uintptr_t)&stats ); 495 iterate_all_torrents(torrent_statter, (uintptr_t)&stats);
527 496
528 r += sprintf( r, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" ); 497 r += sprintf(r, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
529 r += sprintf( r, "<stats>\n" ); 498 r += sprintf(r, "<stats>\n");
530 r += sprintf( r, " <tracker_id>%" PRIu32 "</tracker_id>\n", g_tracker_id ); 499 r += sprintf(r, " <tracker_id>%" PRIu32 "</tracker_id>\n", g_tracker_id);
531 r += sprintf( r, " <version>\n" ); r += stats_return_tracker_version( r ); r += sprintf( r, " </version>\n" ); 500 r += sprintf(r, " <version>\n");
532 r += sprintf( r, " <uptime>%llu</uptime>\n", (unsigned long long)(time( NULL ) - ot_start_time) ); 501 r += stats_return_tracker_version(r);
533 r += sprintf( r, " <torrents>\n" ); 502 r += sprintf(r, " </version>\n");
534 r += sprintf( r, " <count_mutex>%zd</count_mutex>\n", mutex_get_torrent_count() ); 503 r += sprintf(r, " <uptime>%llu</uptime>\n", (unsigned long long)(time(NULL) - ot_start_time));
535 r += sprintf( r, " <count_iterator>%llu</count_iterator>\n", stats.torrent_count ); 504 r += sprintf(r, " <torrents>\n");
536 r += sprintf( r, " </torrents>\n" ); 505 r += sprintf(r, " <count_mutex>%zd</count_mutex>\n", mutex_get_torrent_count());
537 r += sprintf( r, " <peers>\n <count>%llu</count>\n </peers>\n", stats.peer_count ); 506 r += sprintf(r, " <count_iterator>%llu</count_iterator>\n", stats.torrent_count);
538 r += sprintf( r, " <seeds>\n <count>%llu</count>\n </seeds>\n", stats.seed_count ); 507 r += sprintf(r, " </torrents>\n");
539 r += sprintf( r, " <completed>\n <count>%llu</count>\n </completed>\n", ot_overall_completed ); 508 r += sprintf(r, " <peers>\n <count>%llu</count>\n </peers>\n", stats.peer_count);
540 r += sprintf( r, " <connections>\n" ); 509 r += sprintf(r, " <seeds>\n <count>%llu</count>\n </seeds>\n", stats.seed_count);
541 r += sprintf( r, " <tcp>\n <accept>%llu</accept>\n <announce>%llu</announce>\n <scrape>%llu</scrape>\n </tcp>\n", ot_overall_tcp_connections, ot_overall_tcp_successfulannounces, ot_overall_tcp_successfulscrapes ); 510 r += sprintf(r, " <completed>\n <count>%llu</count>\n </completed>\n", ot_overall_completed);
542 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", ot_overall_udp_connections, ot_overall_udp_connects, ot_overall_udp_successfulannounces, ot_overall_udp_successfulscrapes, ot_overall_udp_connectionidmissmatches ); 511 r += sprintf(r, " <connections>\n");
543 r += sprintf( r, " <livesync>\n <count>%llu</count>\n </livesync>\n", ot_overall_sync_count ); 512 r += sprintf(r, " <tcp>\n <accept>%llu</accept>\n <announce>%llu</announce>\n <scrape>%llu</scrape>\n </tcp>\n",
544 r += sprintf( r, " </connections>\n" ); 513 ot_overall_tcp_connections, ot_overall_tcp_successfulannounces, ot_overall_tcp_successfulscrapes);
545 r += sprintf( r, " <debug>\n" ); 514 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",
546 r += sprintf( r, " <renew>\n" ); 515 ot_overall_udp_connections, ot_overall_udp_connects, ot_overall_udp_successfulannounces, ot_overall_udp_successfulscrapes,
547 for( i=0; i<OT_PEER_TIMEOUT; ++i ) 516 ot_overall_udp_connectionidmissmatches);
548 r += sprintf( r, " <count interval=\"%02i\">%llu</count>\n", i, ot_renewed[i] ); 517 r += sprintf(r, " <livesync>\n <count>%llu</count>\n </livesync>\n", ot_overall_sync_count);
549 r += sprintf( r, " </renew>\n" ); 518 r += sprintf(r, " </connections>\n");
550 r += sprintf( r, " <http_error>\n" ); 519 r += sprintf(r, " <debug>\n");
551 for( i=0; i<CODE_HTTPERROR_COUNT; ++i ) 520 r += sprintf(r, " <renew>\n");
552 r += sprintf( r, " <count code=\"%s\">%llu</count>\n", ot_failed_request_names[i], ot_failed_request_counts[i] ); 521 for (i = 0; i < OT_PEER_TIMEOUT; ++i)
553 r += sprintf( r, " </http_error>\n" ); 522 r += sprintf(r, " <count interval=\"%02i\">%llu</count>\n", i, ot_renewed[i]);
554 r += sprintf( r, " <mutex_stall>\n <count>%llu</count>\n </mutex_stall>\n", ot_overall_stall_count ); 523 r += sprintf(r, " </renew>\n");
555 r += sprintf( r, " </debug>\n" ); 524 r += sprintf(r, " <http_error>\n");
556 r += sprintf( r, "</stats>" ); 525 for (i = 0; i < CODE_HTTPERROR_COUNT; ++i)
526 r += sprintf(r, " <count code=\"%s\">%llu</count>\n", ot_failed_request_names[i], ot_failed_request_counts[i]);
527 r += sprintf(r, " </http_error>\n");
528 r += sprintf(r, " <mutex_stall>\n <count>%llu</count>\n </mutex_stall>\n", ot_overall_stall_count);
529 r += sprintf(r, " </debug>\n");
530 r += sprintf(r, "</stats>");
557 return r - reply; 531 return r - reply;
558} 532}
559 533
560extern const char 534size_t stats_return_tracker_version(char *reply) {
561*g_version_opentracker_c, *g_version_accesslist_c, *g_version_clean_c, *g_version_fullscrape_c, *g_version_http_c, 535#define QUOTE(name) #name
562*g_version_iovec_c, *g_version_mutex_c, *g_version_stats_c, *g_version_udp_c, *g_version_vector_c, 536#define SQUOTE(name) QUOTE(name)
563*g_version_scan_urlencoded_query_c, *g_version_trackerlogic_c, *g_version_livesync_c, *g_version_rijndael_c; 537 return sprintf(reply, "https://erdgeist.org/gitweb/opentracker/commit/?id=" SQUOTE(GIT_VERSION) "\n");
564
565size_t stats_return_tracker_version( char *reply ) {
566 return sprintf( reply, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
567 g_version_opentracker_c, g_version_accesslist_c, g_version_clean_c, g_version_fullscrape_c, g_version_http_c,
568 g_version_iovec_c, g_version_mutex_c, g_version_stats_c, g_version_udp_c, g_version_vector_c,
569 g_version_scan_urlencoded_query_c, g_version_trackerlogic_c, g_version_livesync_c, g_version_rijndael_c );
570} 538}
571 539
572size_t return_stats_for_tracker( char *reply, int mode, int format ) { 540size_t return_stats_for_tracker(char *reply, int mode, int format) {
573 (void) format; 541 (void)format;
574 switch( mode & TASK_TASK_MASK ) { 542 switch (mode & TASK_TASK_MASK) {
575 case TASK_STATS_CONNS: 543 case TASK_STATS_CONNS:
576 return stats_connections_mrtg( reply ); 544 return stats_connections_mrtg(reply);
577 case TASK_STATS_SCRAPE: 545 case TASK_STATS_SCRAPE:
578 return stats_scrape_mrtg( reply ); 546 return stats_scrape_mrtg(reply);
579 case TASK_STATS_UDP: 547 case TASK_STATS_UDP:
580 return stats_udpconnections_mrtg( reply ); 548 return stats_udpconnections_mrtg(reply);
581 case TASK_STATS_TCP: 549 case TASK_STATS_TCP:
582 return stats_tcpconnections_mrtg( reply ); 550 return stats_tcpconnections_mrtg(reply);
583 case TASK_STATS_FULLSCRAPE: 551 case TASK_STATS_FULLSCRAPE:
584 return stats_fullscrapes_mrtg( reply ); 552 return stats_fullscrapes_mrtg(reply);
585 case TASK_STATS_COMPLETED: 553 case TASK_STATS_COMPLETED:
586 return stats_return_completed_mrtg( reply ); 554 return stats_return_completed_mrtg(reply);
587 case TASK_STATS_HTTPERRORS: 555 case TASK_STATS_HTTPERRORS:
588 return stats_httperrors_txt( reply ); 556 return stats_httperrors_txt(reply);
589 case TASK_STATS_VERSION: 557 case TASK_STATS_VERSION:
590 return stats_return_tracker_version( reply ); 558 return stats_return_tracker_version(reply);
591 case TASK_STATS_RENEW: 559 case TASK_STATS_RENEW:
592 return stats_return_renew_bucket( reply ); 560 return stats_return_renew_bucket(reply);
593 case TASK_STATS_SYNCS: 561 case TASK_STATS_SYNCS:
594 return stats_return_sync_mrtg( reply ); 562 return stats_return_sync_mrtg(reply);
595#ifdef WANT_LOG_NUMWANT 563#ifdef WANT_LOG_NUMWANT
596 case TASK_STATS_NUMWANTS: 564 case TASK_STATS_NUMWANTS:
597 return stats_return_numwants( reply ); 565 return stats_return_numwants(reply);
598#endif 566#endif
599 default: 567 default:
600 return 0; 568 return 0;
601 } 569 }
602} 570}
603 571
604static void stats_make( int *iovec_entries, struct iovec **iovector, ot_tasktype mode ) { 572static void stats_make(size_t *iovec_entries, struct iovec **iovector, ot_tasktype mode) {
605 char *r; 573 char *r;
606 574
607 *iovec_entries = 0; 575 *iovec_entries = 0;
608 *iovector = NULL; 576 *iovector = NULL;
609 if( !( r = iovec_increase( iovec_entries, iovector, OT_STATS_TMPSIZE ) ) ) 577 if (!(r = iovec_increase(iovec_entries, iovector, OT_STATS_TMPSIZE)))
610 return; 578 return;
611 579
612 switch( mode & TASK_TASK_MASK ) { 580 switch (mode & TASK_TASK_MASK) {
613 case TASK_STATS_TORRENTS: r += stats_torrents_mrtg( r ); break; 581 case TASK_STATS_TORRENTS:
614 case TASK_STATS_PEERS: r += stats_peers_mrtg( r ); break; 582 r += stats_torrents_mrtg(r);
615 case TASK_STATS_SLASH24S: r += stats_slash24s_txt( r, 128 ); break; 583 break;
616 case TASK_STATS_TOP10: r += stats_top_txt( r, 10 ); break; 584 case TASK_STATS_PEERS:
617 case TASK_STATS_TOP100: 585 r += stats_peers_mrtg(r);
618 r = iovec_fix_increase_or_free( iovec_entries, iovector, r, 4 * OT_STATS_TMPSIZE ); 586 break;
619 if( !r ) return; 587 case TASK_STATS_SLASH24S:
620 r += stats_top_txt( r, 100 ); break; 588 r += stats_slash24s_txt(r, 128);
621 case TASK_STATS_EVERYTHING: r += stats_return_everything( r ); break; 589 break;
590 case TASK_STATS_TOP10:
591 r += stats_top_txt(r, 10);
592 break;
593 case TASK_STATS_TOP100:
594 r = iovec_fix_increase_or_free(iovec_entries, iovector, r, 4 * OT_STATS_TMPSIZE);
595 if (!r)
596 return;
597 r += stats_top_txt(r, 100);
598 break;
599 case TASK_STATS_EVERYTHING:
600 r = iovec_fix_increase_or_free(iovec_entries, iovector, r, OT_STATS_TMPSIZE + 64 * OT_PEER_TIMEOUT);
601 if (!r)
602 return;
603 r += stats_return_everything(r);
604 break;
622#ifdef WANT_SPOT_WOODPECKER 605#ifdef WANT_SPOT_WOODPECKER
623 case TASK_STATS_WOODPECKERS: r += stats_return_woodpeckers( r, 128 ); break; 606 case TASK_STATS_WOODPECKERS:
607 r += stats_return_woodpeckers(r, 128);
608 break;
624#endif 609#endif
625#ifdef WANT_FULLLOG_NETWORKS 610#ifdef WANT_FULLLOG_NETWORKS
626 case TASK_STATS_FULLLOG: stats_return_fulllog( iovec_entries, iovector, r ); 611 case TASK_STATS_FULLLOG:
627 return; 612 stats_return_fulllog(iovec_entries, iovector, r);
613 return;
628#endif 614#endif
629 default: 615 default:
630 iovec_free(iovec_entries, iovector); 616 iovec_free(iovec_entries, iovector);
631 return; 617 return;
632 } 618 }
633 iovec_fixlast( iovec_entries, iovector, r ); 619 iovec_fixlast(iovec_entries, iovector, r);
634} 620}
635 621
636void stats_issue_event( ot_status_event event, PROTO_FLAG proto, uintptr_t event_data ) { 622void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_data) {
637 switch( event ) { 623 switch (event) {
638 case EVENT_ACCEPT: 624 case EVENT_ACCEPT:
639 if( proto == FLAG_TCP ) ot_overall_tcp_connections++; else ot_overall_udp_connections++; 625 if (proto == FLAG_TCP)
626 ot_overall_tcp_connections++;
627 else
628 ot_overall_udp_connections++;
640#ifdef WANT_LOG_NETWORKS 629#ifdef WANT_LOG_NETWORKS
641 stat_increase_network_count( &stats_network_counters_root, 0, event_data ); 630 stat_increase_network_count(&stats_network_counters_root, 0, event_data);
642#endif 631#endif
643 break; 632 break;
644 case EVENT_ANNOUNCE: 633 case EVENT_ANNOUNCE:
645 if( proto == FLAG_TCP ) ot_overall_tcp_successfulannounces++; else ot_overall_udp_successfulannounces++; 634 if (proto == FLAG_TCP)
646 break; 635 ot_overall_tcp_successfulannounces++;
647 case EVENT_CONNECT: 636 else
648 if( proto == FLAG_TCP ) ot_overall_tcp_connects++; else ot_overall_udp_connects++; 637 ot_overall_udp_successfulannounces++;
649 break; 638 break;
650 case EVENT_COMPLETED: 639 case EVENT_CONNECT:
640 if (proto == FLAG_TCP)
641 ot_overall_tcp_connects++;
642 else
643 ot_overall_udp_connects++;
644 break;
645 case EVENT_COMPLETED:
651#ifdef WANT_SYSLOGS 646#ifdef WANT_SYSLOGS
652 if( event_data) { 647 if (event_data) {
653 struct ot_workstruct *ws = (struct ot_workstruct *)event_data; 648 struct ot_workstruct *ws = (struct ot_workstruct *)event_data;
654 char timestring[64]; 649 char timestring[64];
655 char hash_hex[42], peerid_hex[42], ip_readable[64]; 650 char hash_hex[42], peerid_hex[42], ip_readable[64];
656 struct tm time_now; 651 struct tm time_now;
657 time_t ttt; 652 time_t ttt;
658 653
659 time( &ttt ); 654 time(&ttt);
660 localtime_r( &ttt, &time_now ); 655 localtime_r(&ttt, &time_now);
661 strftime( timestring, sizeof( timestring ), "%FT%T%z", &time_now ); 656 strftime(timestring, sizeof(timestring), "%FT%T%z", &time_now);
662 657
663 to_hex( hash_hex, *ws->hash ); 658 hex_fromhash(hash_hex, *ws->hash);
664 if( ws->peer_id ) 659 if (ws->peer_id)
665 to_hex( peerid_hex, (uint8_t*)ws->peer_id ); 660 hex_fromhash(peerid_hex, (uint8_t *)ws->peer_id);
666 else { 661 else {
667 *peerid_hex=0; 662 *peerid_hex = 0;
668 } 663 }
669 664
670#ifdef WANT_V6 665 ip_readable[ip_format(ip_readable, (char *)&ws->peer)] = 0;
671 ip_readable[ fmt_ip6c( ip_readable, (char*)&ws->peer ) ] = 0; 666#if 0
672#else 667 /* XXX */
673 ip_readable[ fmt_ip4( ip_readable, (char*)&ws->peer ) ] = 0; 668 ip_readable[ fmt_ip4( ip_readable, (char*)&ws->peer ) ] = 0;
674#endif 669#endif
675 syslog( LOG_INFO, "time=%s event=completed info_hash=%s peer_id=%s ip=%s", timestring, hash_hex, peerid_hex, ip_readable ); 670 syslog(LOG_INFO, "time=%s event=completed info_hash=%s peer_id=%s ip=%s", timestring, hash_hex, peerid_hex, ip_readable);
676 }
677#endif
678 ot_overall_completed++;
679 break;
680 case EVENT_SCRAPE:
681 if( proto == FLAG_TCP ) ot_overall_tcp_successfulscrapes++; else ot_overall_udp_successfulscrapes++;
682 break;
683 case EVENT_FULLSCRAPE:
684 ot_full_scrape_count++;
685 ot_full_scrape_size += event_data;
686 break;
687 case EVENT_FULLSCRAPE_REQUEST:
688 {
689 ot_ip6 *ip = (ot_ip6*)event_data; /* ugly hack to transfer ip to stats */
690 char _debug[512];
691 int off = snprintf( _debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(g_now_seconds - ot_start_time)/60 );
692 off += fmt_ip6c( _debug+off, *ip );
693 off += snprintf( _debug+off, sizeof(_debug)-off, " - FULL SCRAPE\n" );
694 write( 2, _debug, off );
695 ot_full_scrape_request_count++;
696 } 671 }
697 break; 672#endif
698 case EVENT_FULLSCRAPE_REQUEST_GZIP: 673 ot_overall_completed++;
699 { 674 break;
700 ot_ip6 *ip = (ot_ip6*)event_data; /* ugly hack to transfer ip to stats */ 675 case EVENT_SCRAPE:
701 char _debug[512]; 676 if (proto == FLAG_TCP)
702 int off = snprintf( _debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(g_now_seconds - ot_start_time)/60 ); 677 ot_overall_tcp_successfulscrapes++;
703 off += fmt_ip6c(_debug+off, *ip ); 678 else
704 off += snprintf( _debug+off, sizeof(_debug)-off, " - FULL SCRAPE\n" ); 679 ot_overall_udp_successfulscrapes++;
705 write( 2, _debug, off ); 680 break;
706 ot_full_scrape_request_count++; 681 case EVENT_FULLSCRAPE:
707 } 682 ot_full_scrape_count++;
708 break; 683 ot_full_scrape_size += event_data;
709 case EVENT_FAILED: 684 break;
710 ot_failed_request_counts[event_data]++; 685 case EVENT_FULLSCRAPE_REQUEST: {
711 break; 686 ot_ip6 *ip = (ot_ip6 *)event_data; /* ugly hack to transfer ip to stats */
712 case EVENT_RENEW: 687 char _debug[512];
713 ot_renewed[event_data]++; 688 int off = snprintf(_debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(g_now_seconds - ot_start_time) / 60);
714 break; 689 off += ip_format(_debug + off, *ip);
715 case EVENT_SYNC: 690 off += snprintf(_debug + off, sizeof(_debug) - off, " - FULL SCRAPE\n");
716 ot_overall_sync_count+=event_data; 691 (void)write(2, _debug, off);
717 break; 692 ot_full_scrape_request_count++;
718 case EVENT_BUCKET_LOCKED: 693 } break;
719 ot_overall_stall_count++; 694 case EVENT_FULLSCRAPE_REQUEST_GZIP: {
720 break; 695 ot_ip6 *ip = (ot_ip6 *)event_data; /* ugly hack to transfer ip to stats */
696 char _debug[512];
697 int off = snprintf(_debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(g_now_seconds - ot_start_time) / 60);
698 off += ip_format(_debug + off, *ip);
699 off += snprintf(_debug + off, sizeof(_debug) - off, " - FULL SCRAPE\n");
700 (void)write(2, _debug, off);
701 ot_full_scrape_request_count++;
702 } break;
703 case EVENT_FAILED:
704 ot_failed_request_counts[event_data]++;
705 break;
706 case EVENT_RENEW:
707 ot_renewed[event_data]++;
708 break;
709 case EVENT_SYNC:
710 ot_overall_sync_count += event_data;
711 break;
712 case EVENT_BUCKET_LOCKED:
713 ot_overall_stall_count++;
714 break;
721#ifdef WANT_SPOT_WOODPECKER 715#ifdef WANT_SPOT_WOODPECKER
722 case EVENT_WOODPECKER: 716 case EVENT_WOODPECKER:
723 pthread_mutex_lock( &g_woodpeckers_mutex ); 717 pthread_mutex_lock(&g_woodpeckers_mutex);
724 stat_increase_network_count( &stats_woodpeckers_tree, 0, event_data ); 718 stat_increase_network_count(&stats_woodpeckers_tree, 0, event_data);
725 pthread_mutex_unlock( &g_woodpeckers_mutex ); 719 pthread_mutex_unlock(&g_woodpeckers_mutex);
726 break; 720 break;
727#endif 721#endif
728 case EVENT_CONNID_MISSMATCH: 722 case EVENT_CONNID_MISSMATCH:
729 ++ot_overall_udp_connectionidmissmatches; 723 ++ot_overall_udp_connectionidmissmatches;
730 default: 724 default:
731 break; 725 break;
732 } 726 }
733} 727}
734 728
735void stats_cleanup() { 729void stats_cleanup(void) {
736#ifdef WANT_SPOT_WOODPECKER 730#ifdef WANT_SPOT_WOODPECKER
737 pthread_mutex_lock( &g_woodpeckers_mutex ); 731 pthread_mutex_lock(&g_woodpeckers_mutex);
738 stats_shift_down_network_count( &stats_woodpeckers_tree, 0, 1 ); 732 stats_shift_down_network_count(&stats_woodpeckers_tree, 0, 1);
739 pthread_mutex_unlock( &g_woodpeckers_mutex ); 733 pthread_mutex_unlock(&g_woodpeckers_mutex);
740#endif 734#endif
741} 735}
742 736
743static void * stats_worker( void * args ) { 737static void *stats_worker(void *args) {
744 int iovec_entries; 738 size_t iovec_entries;
745 struct iovec *iovector; 739 struct iovec *iovector;
746 740
747 (void) args; 741 (void)args;
748 742
749 while( 1 ) { 743 while (1) {
750 ot_tasktype tasktype = TASK_STATS; 744 ot_tasktype tasktype = TASK_STATS;
751 ot_taskid taskid = mutex_workqueue_poptask( &tasktype ); 745 ot_taskid taskid = mutex_workqueue_poptask(&tasktype);
752 stats_make( &iovec_entries, &iovector, tasktype ); 746 stats_make(&iovec_entries, &iovector, tasktype);
753 if( mutex_workqueue_pushresult( taskid, iovec_entries, iovector ) ) 747 if (mutex_workqueue_pushresult(taskid, iovec_entries, iovector))
754 iovec_free( &iovec_entries, &iovector ); 748 iovec_free(&iovec_entries, &iovector);
755 } 749 }
756 return NULL; 750 return NULL;
757} 751}
758 752
759void stats_deliver( int64 sock, int tasktype ) { 753void stats_deliver(int64_t sock, int tasktype) { mutex_workqueue_pushtask(sock, tasktype); }
760 mutex_workqueue_pushtask( sock, tasktype );
761}
762 754
763static pthread_t thread_id; 755static pthread_t thread_id;
764void stats_init( ) { 756void stats_init(void) {
765 ot_start_time = g_now_seconds; 757 ot_start_time = g_now_seconds;
766 pthread_create( &thread_id, NULL, stats_worker, NULL ); 758 pthread_create(&thread_id, NULL, stats_worker, NULL);
767} 759}
768 760
769void stats_deinit( ) { 761void stats_deinit(void) {
770 pthread_cancel( thread_id ); 762 pthread_cancel(thread_id);
771} 763}
772
773const char *g_version_stats_c = "$Source$: $Revision$\n";
diff --git a/ot_stats.h b/ot_stats.h
index 6a2515b..203831c 100644
--- a/ot_stats.h
+++ b/ot_stats.h
@@ -6,10 +6,12 @@
6#ifndef OT_STATS_H__ 6#ifndef OT_STATS_H__
7#define OT_STATS_H__ 7#define OT_STATS_H__
8 8
9#include "trackerlogic.h"
10
9typedef enum { 11typedef enum {
10 EVENT_ACCEPT, 12 EVENT_ACCEPT,
11 EVENT_READ, 13 EVENT_READ,
12 EVENT_CONNECT, /* UDP only */ 14 EVENT_CONNECT, /* UDP only */
13 EVENT_ANNOUNCE, 15 EVENT_ANNOUNCE,
14 EVENT_COMPLETED, 16 EVENT_COMPLETED,
15 EVENT_RENEW, 17 EVENT_RENEW,
@@ -17,7 +19,8 @@ typedef enum {
17 EVENT_SCRAPE, 19 EVENT_SCRAPE,
18 EVENT_FULLSCRAPE_REQUEST, 20 EVENT_FULLSCRAPE_REQUEST,
19 EVENT_FULLSCRAPE_REQUEST_GZIP, 21 EVENT_FULLSCRAPE_REQUEST_GZIP,
20 EVENT_FULLSCRAPE, /* TCP only */ 22 EVENT_FULLSCRAPE_REQUEST_ZSTD,
23 EVENT_FULLSCRAPE, /* TCP only */
21 EVENT_FAILED, 24 EVENT_FAILED,
22 EVENT_BUCKET_LOCKED, 25 EVENT_BUCKET_LOCKED,
23 EVENT_WOODPECKER, 26 EVENT_WOODPECKER,
@@ -38,15 +41,12 @@ enum {
38 CODE_HTTPERROR_COUNT 41 CODE_HTTPERROR_COUNT
39}; 42};
40 43
41void stats_issue_event( ot_status_event event, PROTO_FLAG proto, uintptr_t event_data ); 44void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_data);
42void stats_deliver( int64 sock, int tasktype ); 45void stats_deliver(int64_t sock, int tasktype);
43void stats_cleanup(); 46void stats_cleanup(void);
44size_t return_stats_for_tracker( char *reply, int mode, int format ); 47size_t return_stats_for_tracker(char *reply, int mode, int format);
45size_t stats_return_tracker_version( char *reply ); 48size_t stats_return_tracker_version(char *reply);
46void stats_init( ); 49void stats_init(void);
47void stats_deinit( ); 50void stats_deinit(void);
48
49extern const char *g_version_rijndael_c;
50extern const char *g_version_livesync_c;
51 51
52#endif 52#endif
diff --git a/ot_sync.c b/ot_sync.c
index cd66a46..dc71ddd 100644
--- a/ot_sync.c
+++ b/ot_sync.c
@@ -4,64 +4,64 @@
4 $id$ */ 4 $id$ */
5 5
6/* System */ 6/* System */
7#include <sys/types.h> 7#include <pthread.h>
8#include <sys/mman.h>
9#include <sys/uio.h>
10#include <stdio.h> 8#include <stdio.h>
11#include <string.h> 9#include <string.h>
12#include <pthread.h> 10#include <sys/types.h>
11#include <sys/uio.h>
13 12
14/* Libowfat */ 13/* Libowfat */
15#include "scan.h"
16#include "byte.h"
17#include "io.h" 14#include "io.h"
18 15
19/* Opentracker */ 16/* Opentracker */
20#include "trackerlogic.h" 17#include "ot_iovec.h"
21#include "ot_mutex.h" 18#include "ot_mutex.h"
22#include "ot_sync.h" 19#include "ot_scan.h"
23#include "ot_stats.h" 20#include "ot_stats.h"
24#include "ot_iovec.h" 21#include "ot_sync.h"
22#include "trackerlogic.h"
25 23
26#ifdef WANT_SYNC_BATCH 24#ifdef WANT_SYNC_BATCH
27 25
28#define OT_SYNC_CHUNK_SIZE (512*1024) 26#define OT_SYNC_CHUNK_SIZE (512 * 1024)
29 27
30/* Import Changeset from an external authority 28/* Import Changeset from an external authority
31 format: d4:syncd[..]ee 29 format: d4:syncd[..]ee
32 [..]: ( 20:01234567890abcdefghij16:XXXXYYYY )+ 30 [..]: ( 20:01234567890abcdefghij16:XXXXYYYY )+
33*/ 31*/
34int add_changeset_to_tracker( uint8_t *data, size_t len ) { 32int add_changeset_to_tracker(uint8_t *data, size_t len) {
35 ot_hash *hash; 33 ot_hash *hash;
36 uint8_t *end = data + len; 34 uint8_t *end = data + len;
37 unsigned long peer_count; 35 uint64_t 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" ) ) return -1; 39 if (!memcmp(data, "d4:syncd", 8))
40 return -1;
42 data += 8; 41 data += 8;
43 42
44 while( 1 ) { 43 while (1) {
45 if( byte_diff( data, 3, "20:" ) ) { 44 if (!memcmp(data, "20:", 3)) {
46 if( byte_diff( data, 2, "ee" ) ) 45 if (!memcmp(data, "ee", 2))
47 return -1; 46 return -1;
48 return 0; 47 return 0;
49 } 48 }
50 data += 3; 49 data += 3;
51 hash = (ot_hash*)data; 50 hash = (ot_hash *)data;
52 data += sizeof( ot_hash ); 51 data += sizeof(ot_hash);
53 52
54 /* Scan string length indicator */ 53 /* Scan string length indicator, strictly: bencode writes no leading zeros */
55 data += ( len = scan_ulong( (char*)data, &peer_count ) ); 54 data += (len = scan_udec_bencode((char *)data, &peer_count, (uint64_t)(end - data)));
56 55
57 /* If no long was scanned, it is not divisible by 8, it is not 56 /* If no long was scanned, it is not divisible by 8, it is not
58 followed by a colon or claims to need to much memory, we fail */ 57 followed by a colon or claims to need to much memory, we fail */
59 if( !len || !peer_count || ( peer_count & 7 ) || ( *data++ != ':' ) || ( data + peer_count > end ) ) 58 if (!len || !peer_count || (peer_count & 7) || (*data++ != ':') || (data + peer_count > end))
60 return -1; 59 return -1;
61 60
62 while( peer_count > 0 ) { 61 while (peer_count > 0) {
63 add_peer_to_torrent( hash, (ot_peer*)data, 1 ); 62 add_peer_to_torrent(hash, (ot_peer *)data, 1);
64 data += 8; peer_count -= 8; 63 data += 8;
64 peer_count -= 8;
65 } 65 }
66 } 66 }
67 return 0; 67 return 0;
@@ -70,80 +70,86 @@ int add_changeset_to_tracker( uint8_t *data, size_t len ) {
70/* Proposed output format 70/* Proposed output format
71 d4:syncd20:<info_hash>8*N:(xxxxyyyy)*Nee 71 d4:syncd20:<info_hash>8*N:(xxxxyyyy)*Nee
72*/ 72*/
73static void sync_make( int *iovec_entries, struct iovec **iovector ) { 73static void sync_make(size_t *iovec_entries, struct iovec **iovector) {
74 int bucket; 74 size_t bucket;
75 char *r, *re; 75 char *r, *re;
76 76
77 /* Setup return vector... */ 77 /* Setup return vector... */
78 *iovec_entries = 0; 78 *iovec_entries = 0;
79 *iovector = NULL; 79 *iovector = NULL;
80 if( !( r = iovec_increase( iovec_entries, iovector, OT_SYNC_CHUNK_SIZE ) ) ) 80 if (!(r = iovec_increase(iovec_entries, iovector, OT_SYNC_CHUNK_SIZE)))
81 return; 81 return;
82 82
83 /* ... and pointer to end of current output buffer. 83 /* ... and pointer to end of current output buffer.
84 This works as a low watermark */ 84 This works as a low watermark */
85 re = r + OT_SYNC_CHUNK_SIZE; 85 re = r + OT_SYNC_CHUNK_SIZE;
86 86
87 memmove( r, "d4:syncd", 8 ); r += 8; 87 memmove(r, "d4:syncd", 8);
88 r += 8;
88 89
89 /* For each bucket... */ 90 /* For each bucket... */
90 for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) { 91 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
91 /* Get exclusive access to that bucket */ 92 /* Get exclusive access to that bucket */
92 ot_vector *torrents_list = mutex_bucket_lock( bucket ); 93 ot_vector *torrents_list = mutex_bucket_lock(bucket);
93 size_t tor_offset; 94 size_t tor_offset;
94 95
95 /* For each torrent in this bucket.. */ 96 /* For each torrent in this bucket.. */
96 for( tor_offset=0; tor_offset<torrents_list->size; ++tor_offset ) { 97 for (tor_offset = 0; tor_offset < torrents_list->size; ++tor_offset) {
97 /* Address torrents members */ 98 /* Address torrents members */
98 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[tor_offset] ).peer_list; 99 ot_peerlist *peer_list = (((ot_torrent *)(torrents_list->data))[tor_offset]).peer_list;
99 ot_hash *hash =&( ((ot_torrent*)(torrents_list->data))[tor_offset] ).hash; 100 ot_hash *hash = &(((ot_torrent *)(torrents_list->data))[tor_offset]).hash;
100 const size_t byte_count = sizeof(ot_peer) * peer_list->changeset.size; 101 const size_t byte_count = sizeof(ot_peer) * peer_list->changeset.size;
101 102
102 /* If we reached our low watermark in buffer... */ 103 /* If we reached our low watermark in buffer... */
103 if( re - r <= (ssize_t)(/* strlen( "20:" ) == */ 3 + sizeof( ot_hash ) + /* strlen_max( "%zd" ) == */ 12 + byte_count ) ) { 104 if (re - r <= (ssize_t)(/* strlen( "20:" ) == */ 3 + sizeof(ot_hash) + /* strlen_max( "%zd" ) == */ 12 + byte_count)) {
104 105
105 /* Allocate a fresh output buffer at the end of our buffers list 106 /* Allocate a fresh output buffer at the end of our buffers list
106 release bucket and return, if that fails */ 107 release bucket and return, if that fails */
107 if( !( r = iovec_fix_increase_or_free( iovec_entries, iovector, r, OT_SYNC_CHUNK_SIZE ) ) ) 108 if (!(r = iovec_fix_increase_or_free(iovec_entries, iovector, r, OT_SYNC_CHUNK_SIZE)))
108 return mutex_bucket_unlock( bucket ); 109 return mutex_bucket_unlock(bucket);
109 110
110 /* Adjust new end of output buffer */ 111 /* Adjust new end of output buffer */
111 re = r + OT_SYNC_CHUNK_SIZE; 112 re = r + OT_SYNC_CHUNK_SIZE;
112 } 113 }
113 114
114 *r++ = '2'; *r++ = '0'; *r++ = ':'; 115 *r++ = '2';
115 memmove( r, hash, sizeof( ot_hash ) ); r += sizeof( ot_hash ); 116 *r++ = '0';
116 r += sprintf( r, "%zd:", byte_count ); 117 *r++ = ':';
117 memmove( r, peer_list->changeset.data, byte_count ); r += byte_count; 118 memmove(r, hash, sizeof(ot_hash));
119 r += sizeof(ot_hash);
120 r += sprintf(r, "%zd:", byte_count);
121 memmove(r, peer_list->changeset.data, byte_count);
122 r += byte_count;
118 } 123 }
119 124
120 /* All torrents done: release lock on currenct bucket */ 125 /* All torrents done: release lock on currenct bucket */
121 mutex_bucket_unlock( bucket ); 126 mutex_bucket_unlock(bucket);
122 } 127 }
123 128
124 /* Close bencoded sync dictionary */ 129 /* Close bencoded sync dictionary */
125 *r++='e'; *r++='e'; 130 *r++ = 'e';
131 *r++ = 'e';
126 132
127 /* Release unused memory in current output buffer */ 133 /* Release unused memory in current output buffer */
128 iovec_fixlast( iovec_entries, iovector, r ); 134 iovec_fixlast(iovec_entries, iovector, r);
129} 135}
130 136
131/* This is the entry point into this worker thread 137/* This is the entry point into this worker thread
132 It grabs tasks from mutex_tasklist and delivers results back 138 It grabs tasks from mutex_tasklist and delivers results back
133*/ 139*/
134static void * sync_worker( void * args) { 140static void *sync_worker(void *args) {
135 int iovec_entries; 141 size_t iovec_entries;
136 struct iovec *iovector; 142 struct iovec *iovector;
137 143
138 args = args; 144 args = args;
139 145
140 while( 1 ) { 146 while (1) {
141 ot_tasktype tasktype = TASK_SYNC_OUT; 147 ot_tasktype tasktype = TASK_SYNC_OUT;
142 ot_taskid taskid = mutex_workqueue_poptask( &tasktype ); 148 ot_taskid taskid = mutex_workqueue_poptask(&tasktype);
143 sync_make( &iovec_entries, &iovector ); 149 sync_make(&iovec_entries, &iovector);
144 stats_issue_event( EVENT_SYNC_OUT, FLAG_TCP, iovec_length( &iovec_entries, &iovector) ); 150 stats_issue_event(EVENT_SYNC_OUT, FLAG_TCP, iovec_length(&iovec_entries, &iovector));
145 if( mutex_workqueue_pushresult( taskid, iovec_entries, iovector ) ) 151 if (mutex_workqueue_pushresult(taskid, iovec_entries, iovector))
146 iovec_free( &iovec_entries, &iovector ); 152 iovec_free(&iovec_entries, &iovector);
147 } 153 }
148 return NULL; 154 return NULL;
149} 155}
@@ -157,10 +163,8 @@ void sync_deinit( ) {
157 pthread_cancel( thread_id ); 163 pthread_cancel( thread_id );
158} 164}
159 165
160void sync_deliver( int64 socket ) { 166void sync_deliver( int64_t socket ) {
161 mutex_workqueue_pushtask( socket, TASK_SYNC_OUT ); 167 mutex_workqueue_pushtask( socket, TASK_SYNC_OUT );
162} 168}
163 169
164#endif 170#endif
165
166const char *g_version_sync_c = "$Source$: $Revision$\n";
diff --git a/ot_sync.h b/ot_sync.h
index 441010d..b6964fa 100644
--- a/ot_sync.h
+++ b/ot_sync.h
@@ -6,14 +6,16 @@
6#ifndef OT_SYNC_H__ 6#ifndef OT_SYNC_H__
7#define OT_SYNC_H__ 7#define OT_SYNC_H__
8 8
9#include <stdint.h>
10
9#ifdef WANT_SYNC_BATCH 11#ifdef WANT_SYNC_BATCH
10enum { SYNC_IN, SYNC_OUT }; 12enum { SYNC_IN, SYNC_OUT };
11 13
12void sync_init( ); 14void sync_init();
13void sync_deinit( ); 15void sync_deinit();
14void sync_deliver( int64 socket ); 16void sync_deliver(int64_t socket);
15 17
16int add_changeset_to_tracker( uint8_t *data, size_t len ); 18int add_changeset_to_tracker(uint8_t *data, size_t len);
17#else 19#else
18 20
19#define sync_init() 21#define sync_init()
diff --git a/ot_udp.c b/ot_udp.c
index edbaca8..8e056db 100644
--- a/ot_udp.c
+++ b/ot_udp.c
@@ -4,224 +4,245 @@
4 $id$ */ 4 $id$ */
5 5
6/* System */ 6/* System */
7#include <stdlib.h>
8#include <pthread.h>
9#include <string.h>
10#include <arpa/inet.h> 7#include <arpa/inet.h>
8#include <pthread.h>
11#include <stdio.h> 9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12 12
13/* Libowfat */ 13/* Libowfat */
14#include "socket.h"
15#include "io.h" 14#include "io.h"
15#include "socket.h"
16 16
17/* Opentracker */ 17/* Opentracker */
18#include "trackerlogic.h" 18#include "ot_ip.h"
19#include "ot_udp.h" 19#include "ot_random.h"
20#include "ot_stats.h"
21#include "ot_rijndael.h" 20#include "ot_rijndael.h"
21#include "ot_stats.h"
22#include "ot_udp.h"
23#include "trackerlogic.h"
22 24
23#if 0 25#if 0
24static const uint8_t g_static_connid[8] = { 0x23, 0x42, 0x05, 0x17, 0xde, 0x41, 0x50, 0xff }; 26static const uint8_t g_static_connid[8] = { 0x23, 0x42, 0x05, 0x17, 0xde, 0x41, 0x50, 0xff };
25#endif 27#endif
26static uint32_t g_rijndael_round_key[44] = {0}; 28static uint32_t g_rijndael_round_key[44] = {0};
27static uint32_t g_key_of_the_hour[2] = {0}; 29static volatile uint32_t g_key_of_the_hour[2] = {0};
28static ot_time g_hour_of_the_key; 30static volatile ot_time g_hour_of_the_key;
29 31
30static void udp_generate_rijndael_round_key() { 32static pthread_mutex_t udp_start_mutex = PTHREAD_MUTEX_INITIALIZER;
33static pthread_cond_t udp_start_cond = PTHREAD_COND_INITIALIZER;
34static int g_opentracker_started = 0;
35
36static void udp_generate_rijndael_round_key(void) {
31 uint32_t key[16]; 37 uint32_t key[16];
32#ifdef WANT_ARC4RANDOM 38 random_bytes(key, sizeof(key));
33 arc4random_buf(&key[0], sizeof(key)); 39 rijndaelKeySetupEnc128(g_rijndael_round_key, (uint8_t *)key);
34#else
35 key[0] = random();
36 key[1] = random();
37 key[2] = random();
38 key[3] = random();
39#endif
40 rijndaelKeySetupEnc128( g_rijndael_round_key, (uint8_t*)key );
41 40
42#ifdef WANT_ARC4RANDOM 41 g_key_of_the_hour[0] = random_uint32();
43 g_key_of_the_hour[0] = arc4random();
44#else
45 g_key_of_the_hour[0] = random();
46#endif
47 g_hour_of_the_key = g_now_minutes; 42 g_hour_of_the_key = g_now_minutes;
48} 43}
49 44
50/* Generate current and previous connection id for ip */ 45/* Generate current and previous connection id for ip */
51static void udp_make_connectionid( uint32_t connid[2], const ot_ip6 remoteip, int age ) { 46static void udp_make_connectionid(uint32_t connid[2], const ot_ip6 remoteip, int age) {
52 uint32_t plain[4], crypt[4]; 47 uint32_t plain[4], crypt[4];
53 int i; 48 int i;
54 if( g_now_minutes + 60 > g_hour_of_the_key ) { 49 uint32_t current_key_of_the_hour;
55 g_hour_of_the_key = g_now_minutes; 50
56 g_key_of_the_hour[1] = g_key_of_the_hour[0]; 51 if (g_now_minutes - g_hour_of_the_key >= 60) {
57#ifdef WANT_ARC4RANDOM 52 uint32_t old_key_of_the_hour = g_key_of_the_hour[0];
58 g_key_of_the_hour[0] = arc4random(); 53 uint32_t new_key_of_the_hour = random_uint32();
59#else 54 /* If in the meantime another thread has performed
60 g_key_of_the_hour[0] = random(); 55 key rotation, do not overwrite their results */
61#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 }
62 } 62 }
63 63
64 memcpy( plain, remoteip, sizeof( plain ) ); 64 memcpy(plain, remoteip, sizeof(plain));
65 for( i=0; i<4; ++i ) plain[i] ^= g_key_of_the_hour[age]; 65 current_key_of_the_hour = g_key_of_the_hour[age];
66 rijndaelEncrypt128( g_rijndael_round_key, (uint8_t*)remoteip, (uint8_t*)crypt ); 66 for (i = 0; i < 4; ++i)
67 plain[i] ^= current_key_of_the_hour;
68 rijndaelEncrypt128(g_rijndael_round_key, (uint8_t *)plain, (uint8_t *)crypt);
67 connid[0] = crypt[0] ^ crypt[1]; 69 connid[0] = crypt[0] ^ crypt[1];
68 connid[1] = crypt[2] ^ crypt[3]; 70 connid[1] = crypt[2] ^ crypt[3];
69} 71}
70 72
71/* UDP implementation according to http://xbtt.sourceforge.net/udp_tracker_protocol.html */ 73/* UDP implementation according to http://xbtt.sourceforge.net/udp_tracker_protocol.html */
72int handle_udp6( int64 serversocket, struct ot_workstruct *ws ) { 74int handle_udp6(int64_t serversocket, struct ot_workstruct *ws) {
73 ot_ip6 remoteip; 75 ot_ip6 remoteip;
74 uint32_t *inpacket = (uint32_t*)ws->inbuf; 76 uint32_t *inpacket = (uint32_t *)ws->inbuf;
75 uint32_t *outpacket = (uint32_t*)ws->outbuf; 77 uint32_t *outpacket = (uint32_t *)ws->outbuf;
76 uint32_t numwant, left, event, scopeid; 78 uint32_t left, event, scopeid;
77 uint32_t connid[2]; 79 uint32_t connid[2];
78 uint32_t action; 80 uint32_t action;
79 uint16_t port, remoteport; 81 uint16_t port, remoteport;
80 size_t byte_count, scrape_count; 82 size_t byte_count, scrape_count;
81 83
82 byte_count = socket_recv6( serversocket, ws->inbuf, G_INBUF_SIZE, remoteip, &remoteport, &scopeid ); 84 byte_count = socket_recv6(serversocket, ws->inbuf, G_INBUF_SIZE, remoteip, &remoteport, &scopeid);
83 if( !byte_count ) return 0; 85 if (!byte_count)
86 return 0;
84 87
85 stats_issue_event( EVENT_ACCEPT, FLAG_UDP, (uintptr_t)remoteip ); 88 stats_issue_event(EVENT_ACCEPT, FLAG_UDP, (uintptr_t)remoteip);
86 stats_issue_event( EVENT_READ, FLAG_UDP, byte_count ); 89 stats_issue_event(EVENT_READ, FLAG_UDP, byte_count);
87 90
88 /* Minimum udp tracker packet size, also catches error */ 91 /* Minimum udp tracker packet size, also catches error */
89 if( byte_count < 16 ) 92 if (byte_count < 16)
90 return 1; 93 return 1;
91 94
92 /* Get action to take. Ignore error messages and broken packets */ 95 /* Get action to take. Ignore error messages and broken packets */
93 action = ntohl( inpacket[2] ); 96 action = ntohl(inpacket[2]);
94 if( action > 2 ) 97 if (action > 2)
95 return 1; 98 return 1;
96 99
97 /* Generate the connection id we give out and expect to and from 100 /* Generate the connection id we give out and expect to and from
98 the requesting ip address, this prevents udp spoofing */ 101 the requesting ip address, this prevents udp spoofing */
99 udp_make_connectionid( connid, remoteip, 0 ); 102 udp_make_connectionid(connid, remoteip, 0);
100 103
101 /* Initialise hash pointer */ 104 /* Initialise hash pointer */
102 ws->hash = NULL; 105 ws->hash = NULL;
103 ws->peer_id = NULL; 106 ws->peer_id = NULL;
104 107
105 /* If action is not 0 (connect), then we expect the derived 108 /* If action is not 0 (connect), then we expect the derived
106 connection id in first 64 bit */ 109 connection id in first 64 bit */
107 if( ( action > 0 ) && ( inpacket[0] != connid[0] || inpacket[1] != connid[1] ) ) { 110 if ((action > 0) && (inpacket[0] != connid[0] || inpacket[1] != connid[1])) {
108 /* If connection id does not match, try the one that was 111 /* If connection id does not match, try the one that was
109 valid in the previous hour. Only if this also does not 112 valid in the previous hour. Only if this also does not
110 match, return an error packet */ 113 match, return an error packet */
111 udp_make_connectionid( connid, remoteip, 1 ); 114 udp_make_connectionid(connid, remoteip, 1);
112 if( inpacket[0] != connid[0] || inpacket[1] != connid[1] ) { 115 if (inpacket[0] != connid[0] || inpacket[1] != connid[1]) {
113 const size_t s = sizeof( "Connection ID missmatch." ); 116 const size_t s = sizeof("Connection ID missmatch.");
114 outpacket[0] = htonl( 3 ); outpacket[1] = inpacket[3]; 117 outpacket[0] = htonl(3);
115 memcpy( &outpacket[2], "Connection ID missmatch.", s ); 118 outpacket[1] = inpacket[3];
116 socket_send6( serversocket, ws->outbuf, 8 + s, remoteip, remoteport, 0 ); 119 memcpy(&outpacket[2], "Connection ID missmatch.", s);
117 stats_issue_event( EVENT_CONNID_MISSMATCH, FLAG_UDP, 8 + s ); 120 socket_send6(serversocket, ws->outbuf, 8 + s, remoteip, remoteport, 0);
121 stats_issue_event(EVENT_CONNID_MISSMATCH, FLAG_UDP, 8 + s);
118 return 1; 122 return 1;
119 } 123 }
120 } 124 }
121 125
122 switch( action ) { 126 switch (action) {
123 case 0: /* This is a connect action */ 127 case 0: /* This is a connect action */
124 /* look for udp bittorrent magic id */ 128 /* look for udp bittorrent magic id */
125 if( (ntohl(inpacket[0]) != 0x00000417) || (ntohl(inpacket[1]) != 0x27101980) ) 129 if ((ntohl(inpacket[0]) != 0x00000417) || (ntohl(inpacket[1]) != 0x27101980))
126 return 1; 130 return 1;
127 131
128 outpacket[0] = 0; 132 outpacket[0] = 0;
129 outpacket[1] = inpacket[3]; 133 outpacket[1] = inpacket[3];
130 outpacket[2] = connid[0]; 134 outpacket[2] = connid[0];
131 outpacket[3] = connid[1]; 135 outpacket[3] = connid[1];
132 136
133 socket_send6( serversocket, ws->outbuf, 16, remoteip, remoteport, 0 ); 137 socket_send6(serversocket, ws->outbuf, 16, remoteip, remoteport, 0);
134 stats_issue_event( EVENT_CONNECT, FLAG_UDP, 16 ); 138 stats_issue_event(EVENT_CONNECT, FLAG_UDP, 16);
135 break; 139 break;
136 case 1: /* This is an announce action */ 140 case 1: /* This is an announce action */
137 /* Minimum udp announce packet size */ 141 /* Minimum udp announce packet size */
138 if( byte_count < 98 ) 142 if (byte_count < 98)
139 return 1; 143 return 1;
140 144
141 /* We do only want to know, if it is zero */ 145 /* We do only want to know, if it is zero */
142 left = inpacket[64/4] | inpacket[68/4]; 146 left = inpacket[64 / 4] | inpacket[68 / 4];
143 147
144 /* Limit amount of peers to 200 */ 148 event = ntohl(inpacket[80 / 4]);
145 numwant = ntohl( inpacket[92/4] ); 149 port = *(uint16_t *)(((char *)inpacket) + 96);
146 if (numwant > 200) numwant = 200; 150 ws->hash = (ot_hash *)(((char *)inpacket) + 16);
147 151
148 event = ntohl( inpacket[80/4] ); 152 OT_SETIP(ws->peer, remoteip);
149 port = *(uint16_t*)( ((char*)inpacket) + 96 ); 153 OT_SETPORT(ws->peer, &port);
150 ws->hash = (ot_hash*)( ((char*)inpacket) + 16 ); 154 OT_PEERFLAG(ws->peer) = 0;
151 155
152 OT_SETIP( &ws->peer, remoteip ); 156 switch (event) {
153 OT_SETPORT( &ws->peer, &port ); 157 case 1:
154 OT_PEERFLAG( &ws->peer ) = 0; 158 OT_PEERFLAG(ws->peer) |= PEER_FLAG_COMPLETED;
159 break;
160 case 3:
161 OT_PEERFLAG(ws->peer) |= PEER_FLAG_STOPPED;
162 break;
163 default:
164 break;
165 }
155 166
156 switch( event ) { 167 if (!left)
157 case 1: OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_COMPLETED; break; 168 OT_PEERFLAG(ws->peer) |= PEER_FLAG_SEEDING;
158 case 3: OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_STOPPED; break;
159 default: break;
160 }
161 169
162 if( !left ) 170 outpacket[0] = htonl(1); /* announce action */
163 OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_SEEDING; 171 outpacket[1] = inpacket[12 / 4];
164 172
165 outpacket[0] = htonl( 1 ); /* announce action */ 173 if (OT_PEERFLAG(ws->peer) & PEER_FLAG_STOPPED) { /* Peer is gone. */
166 outpacket[1] = inpacket[12/4]; 174 ws->reply = ws->outbuf;
175 ws->reply_size = remove_peer_from_torrent(FLAG_UDP, ws);
176 } else {
177 /* Limit amount of peers to OT_MAX_PEERS_UDP */
178 uint32_t numwant = ntohl(inpacket[92 / 4]);
179 size_t max_peers = ip_is_v4mapped(remoteip) ? OT_MAX_PEERS_UDP4 : OT_MAX_PEERS_UDP6;
180 if (numwant > max_peers)
181 numwant = max_peers;
167 182
168 if( OT_PEERFLAG( &ws->peer ) & PEER_FLAG_STOPPED ) { /* Peer is gone. */ 183 ws->reply = ws->outbuf + 8;
169 ws->reply = ws->outbuf; 184 ws->reply_size = 8 + add_peer_to_torrent_and_return_peers(FLAG_UDP, ws, numwant);
170 ws->reply_size = remove_peer_from_torrent( FLAG_UDP, ws ); 185 }
171 } else {
172 ws->reply = ws->outbuf + 8;
173 ws->reply_size = 8 + add_peer_to_torrent_and_return_peers( FLAG_UDP, ws, numwant );
174 }
175 186
176 socket_send6( serversocket, ws->outbuf, ws->reply_size, remoteip, remoteport, 0 ); 187 socket_send6(serversocket, ws->outbuf, ws->reply_size, remoteip, remoteport, 0);
177 stats_issue_event( EVENT_ANNOUNCE, FLAG_UDP, ws->reply_size ); 188 stats_issue_event(EVENT_ANNOUNCE, FLAG_UDP, ws->reply_size);
178 break; 189 break;
179 190
180 case 2: /* This is a scrape action */ 191 case 2: /* This is a scrape action */
181 outpacket[0] = htonl( 2 ); /* scrape action */ 192 outpacket[0] = htonl(2); /* scrape action */
182 outpacket[1] = inpacket[12/4]; 193 outpacket[1] = inpacket[12 / 4];
183 194
184 for( scrape_count = 0; ( scrape_count * 20 < byte_count - 16) && ( scrape_count <= 74 ); scrape_count++ ) 195 for (scrape_count = 0; (scrape_count * 20 < byte_count - 16) && (scrape_count <= 74); scrape_count++)
185 return_udp_scrape_for_torrent( *(ot_hash*)( ((char*)inpacket) + 16 + 20 * scrape_count ), ((char*)outpacket) + 8 + 12 * scrape_count ); 196 return_udp_scrape_for_torrent(*(ot_hash *)(((char *)inpacket) + 16 + 20 * scrape_count), ((char *)outpacket) + 8 + 12 * scrape_count);
186 197
187 socket_send6( serversocket, ws->outbuf, 8 + 12 * scrape_count, remoteip, remoteport, 0 ); 198 socket_send6(serversocket, ws->outbuf, 8 + 12 * scrape_count, remoteip, remoteport, 0);
188 stats_issue_event( EVENT_SCRAPE, FLAG_UDP, scrape_count ); 199 stats_issue_event(EVENT_SCRAPE, FLAG_UDP, scrape_count);
189 break; 200 break;
190 } 201 }
191 return 1; 202 return 1;
192} 203}
193 204
194static void* udp_worker( void * args ) { 205static void *udp_worker(void *args) {
195 int64 sock = (int64)args; 206 int64_t sock = (int64_t)args;
196 struct ot_workstruct ws; 207 struct ot_workstruct ws;
197 memset( &ws, 0, sizeof(ws) ); 208 memset(&ws, 0, sizeof(ws));
198 209
199 ws.inbuf=malloc(G_INBUF_SIZE); 210 ws.inbuf = malloc(G_INBUF_SIZE);
200 ws.outbuf=malloc(G_OUTBUF_SIZE); 211 ws.outbuf = malloc(G_OUTBUF_SIZE);
201#ifdef _DEBUG_HTTPERROR 212#ifdef _DEBUG_HTTPERROR
202 ws.debugbuf=malloc(G_DEBUGBUF_SIZE); 213 ws.debugbuf = malloc(G_DEBUGBUF_SIZE);
203#endif 214#endif
204 215
205 while( g_opentracker_running ) 216 pthread_mutex_lock(&udp_start_mutex);
206 handle_udp6( sock, &ws ); 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);
207 220
208 free( ws.inbuf ); 221 while (g_opentracker_running)
209 free( ws.outbuf ); 222 handle_udp6(sock, &ws);
210#ifdef _DEBUG_HTTPERROR 223
211 free( ws.debugbuf ); 224 free(ws.inbuf);
225 free(ws.outbuf);
226#ifdef _DEBUG_HTTPERROR
227 free(ws.debugbuf);
212#endif 228#endif
213 return NULL; 229 return NULL;
214} 230}
215 231
216void udp_init( int64 sock, unsigned int worker_count ) { 232void udp_init(int64_t sock, unsigned int worker_count) {
217 pthread_t thread_id; 233 pthread_t thread_id;
218 if( !g_rijndael_round_key[0] ) 234 if (!g_rijndael_round_key[0])
219 udp_generate_rijndael_round_key(); 235 udp_generate_rijndael_round_key();
220#ifdef _DEBUG 236#ifdef _DEBUG
221 fprintf( stderr, " installing %d workers on udp socket %ld\n", worker_count, (unsigned long)sock ); 237 fprintf(stderr, " installing %d workers on udp socket %ld\n", worker_count, (unsigned long)sock);
222#endif 238#endif
223 while( worker_count-- ) 239 while (worker_count--)
224 pthread_create( &thread_id, NULL, udp_worker, (void *)sock ); 240 pthread_create(&thread_id, NULL, udp_worker, (void *)sock);
225} 241}
226 242
227const char *g_version_udp_c = "$Source$: $Revision$\n"; 243void udp_kickoff(void) {
244 pthread_mutex_lock(&udp_start_mutex);
245 g_opentracker_started = 1;
246 pthread_cond_broadcast(&udp_start_cond);
247 pthread_mutex_unlock(&udp_start_mutex);
248}
diff --git a/ot_udp.h b/ot_udp.h
index 974c727..c670175 100644
--- a/ot_udp.h
+++ b/ot_udp.h
@@ -6,7 +6,10 @@
6#ifndef OT_UDP_H__ 6#ifndef OT_UDP_H__
7#define OT_UDP_H__ 7#define OT_UDP_H__
8 8
9void udp_init( int64 sock, unsigned int worker_count ); 9#include <stdint.h>
10int handle_udp6( int64 serversocket, struct ot_workstruct *ws ); 10
11void udp_init(int64_t sock, unsigned int worker_count);
12void udp_kickoff(void);
13int handle_udp6(int64_t serversocket, struct ot_workstruct *ws);
11 14
12#endif 15#endif
diff --git a/ot_vector.c b/ot_vector.c
index 2a632b2..c4cd171 100644
--- a/ot_vector.c
+++ b/ot_vector.c
@@ -4,39 +4,33 @@
4 $id$ */ 4 $id$ */
5 5
6/* System */ 6/* System */
7#include <stddef.h>
8#include <stdint.h>
7#include <stdlib.h> 9#include <stdlib.h>
8#include <string.h> 10#include <string.h>
9#include <strings.h> 11#include <strings.h>
10#include <stdint.h>
11 12
12/* Opentracker */ 13/* Opentracker */
13#include "trackerlogic.h" 14#include "trackerlogic.h"
14#include "ot_vector.h"
15 15
16/* Libowfat */ 16static int vector_compare_peer6(const void *peer1, const void *peer2) { return memcmp(peer1, peer2, OT_PEER_COMPARE_SIZE6); }
17#include "uint32.h" 17static int vector_compare_peer4(const void *peer1, const void *peer2) { return memcmp(peer1, peer2, OT_PEER_COMPARE_SIZE4); }
18#include "uint16.h"
19
20static int vector_compare_peer(const void *peer1, const void *peer2 ) {
21 return memcmp( peer1, peer2, OT_PEER_COMPARE_SIZE );
22}
23 18
24/* This function gives us a binary search that returns a pointer, even if 19/* This function gives us a binary search that returns a pointer, even if
25 no exact match is found. In that case it sets exactmatch 0 and gives 20 no exact match is found. In that case it sets exactmatch 0 and gives
26 calling functions the chance to insert data 21 calling functions the chance to insert data
27*/ 22*/
28void *binary_search( const void * const key, const void * base, const size_t member_count, const size_t member_size, 23void *binary_search(const void *const key, const void *base, const size_t member_count, const size_t member_size, size_t compare_size, int *exactmatch) {
29 size_t compare_size, int *exactmatch ) {
30 size_t interval = member_count; 24 size_t interval = member_count;
31 25
32 while( interval ) { 26 while (interval) {
33 uint8_t *lookat = ((uint8_t*)base) + member_size * ( interval / 2 ); 27 uint8_t *lookat = ((uint8_t *)base) + member_size * (interval / 2);
34 int cmp = memcmp( lookat, key, compare_size ); 28 int cmp = memcmp(lookat, key, compare_size);
35 if(cmp == 0 ) { 29 if (cmp == 0) {
36 base = lookat; 30 base = lookat;
37 break; 31 break;
38 } 32 }
39 if(cmp < 0) { 33 if (cmp < 0) {
40 base = lookat + member_size; 34 base = lookat + member_size;
41 interval--; 35 interval--;
42 } 36 }
@@ -44,13 +38,14 @@ void *binary_search( const void * const key, const void * base, const size_t mem
44 } 38 }
45 39
46 *exactmatch = interval; 40 *exactmatch = interval;
47 return (void*)base; 41 return (void *)base;
48} 42}
49 43
50static uint8_t vector_hash_peer( ot_peer *peer, int bucket_count ) { 44static uint8_t vector_hash_peer(ot_peer const *peer, size_t compare_size, size_t bucket_count) {
51 unsigned int hash = 5381, i = OT_PEER_COMPARE_SIZE; 45 unsigned int hash = 5381;
52 uint8_t *p = (uint8_t*)peer; 46 uint8_t *p = (uint8_t *)peer;
53 while( i-- ) hash += (hash<<5) + *(p++); 47 while (compare_size--)
48 hash += (hash << 5) + *(p++);
54 return hash % bucket_count; 49 return hash % bucket_count;
55} 50}
56 51
@@ -61,48 +56,65 @@ static uint8_t vector_hash_peer( ot_peer *peer, int bucket_count ) {
61 if it wasn't found in vector. Caller needs to check the passed "exactmatch" variable to see, whether an insert 56 if it wasn't found in vector. Caller needs to check the passed "exactmatch" variable to see, whether an insert
62 took place. If resizing the vector failed, NULL is returned, else the pointer to the object in vector. 57 took place. If resizing the vector failed, NULL is returned, else the pointer to the object in vector.
63*/ 58*/
64void *vector_find_or_insert( ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch ) { 59void *vector_find_or_insert(ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch) {
65 uint8_t *match = binary_search( key, vector->data, vector->size, member_size, compare_size, exactmatch ); 60 uint8_t *match = binary_search(key, vector->data, vector->size, member_size, compare_size, exactmatch);
66 61
67 if( *exactmatch ) return match; 62 if (*exactmatch)
63 return match;
64
65 if (vector->size + 1 > vector->space) {
66 size_t new_space = vector->space ? OT_VECTOR_GROW_RATIO * vector->space : OT_VECTOR_MIN_MEMBERS;
67 ptrdiff_t match_off = match - (uint8_t *)vector->data;
68 uint8_t *new_data = realloc(vector->data, new_space * member_size);
69
70 if (!new_data)
71 return NULL;
68 72
69 if( vector->size + 1 > vector->space ) {
70 size_t new_space = vector->space ? OT_VECTOR_GROW_RATIO * vector->space : OT_VECTOR_MIN_MEMBERS;
71 uint8_t *new_data = realloc( vector->data, new_space * member_size );
72 if( !new_data ) return NULL;
73 /* Adjust pointer if it moved by realloc */ 73 /* Adjust pointer if it moved by realloc */
74 match = new_data + (match - (uint8_t*)vector->data); 74 match = new_data + match_off;
75 75
76 vector->data = new_data; 76 vector->data = new_data;
77 vector->space = new_space; 77 vector->space = new_space;
78 } 78 }
79 memmove( match + member_size, match, ((uint8_t*)vector->data) + member_size * vector->size - match ); 79 memmove(match + member_size, match, ((uint8_t *)vector->data) + member_size * vector->size - match);
80 80
81 vector->size++; 81 vector->size++;
82 return match; 82 return match;
83} 83}
84 84
85ot_peer *vector_find_or_insert_peer( ot_vector *vector, ot_peer *peer, int *exactmatch ) { 85ot_peer *vector_find_or_insert_peer(ot_vector *vector, ot_peer const *peer, size_t peer_size, int *exactmatch) {
86 ot_peer *match; 86 ot_peer *match, *end;
87 const size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
88 size_t match_to_end;
87 89
88 /* If space is zero but size is set, we're dealing with a list of vector->size buckets */ 90 /* If space is zero but size is set, we're dealing with a list of vector->size buckets */
89 if( vector->space < vector->size ) 91 if (vector->space < vector->size)
90 vector = ((ot_vector*)vector->data) + vector_hash_peer(peer, vector->size ); 92 vector = ((ot_vector *)vector->data) + vector_hash_peer(peer, compare_size, vector->size);
91 match = (ot_peer*)binary_search( peer, vector->data, vector->size, sizeof(ot_peer), OT_PEER_COMPARE_SIZE, exactmatch ); 93 match = binary_search(peer, vector->data, vector->size, peer_size, compare_size, exactmatch);
94
95 if (*exactmatch)
96 return match;
92 97
93 if( *exactmatch ) return match; 98 /* This is the amount of bytes that needs to be pushed backwards by peer_size bytes to make room for new peer */
99 end = (ot_peer *)vector->data + vector->size * peer_size;
100 match_to_end = end - match;
94 101
95 if( vector->size + 1 > vector->space ) { 102 if (vector->size + 1 > vector->space) {
96 size_t new_space = vector->space ? OT_VECTOR_GROW_RATIO * vector->space : OT_VECTOR_MIN_MEMBERS; 103 ptrdiff_t offset = match - (ot_peer *)vector->data;
97 ot_peer *new_data = realloc( vector->data, new_space * sizeof(ot_peer) ); 104 size_t new_space = vector->space ? OT_VECTOR_GROW_RATIO * vector->space : OT_VECTOR_MIN_MEMBERS;
98 if( !new_data ) return NULL; 105 ot_peer *new_data = realloc(vector->data, new_space * peer_size);
106
107 if (!new_data)
108 return NULL;
99 /* Adjust pointer if it moved by realloc */ 109 /* Adjust pointer if it moved by realloc */
100 match = new_data + (match - (ot_peer*)vector->data); 110 match = new_data + offset;
101 111
102 vector->data = new_data; 112 vector->data = new_data;
103 vector->space = new_space; 113 vector->space = new_space;
104 } 114 }
105 memmove( match + 1, match, sizeof(ot_peer) * ( ((ot_peer*)vector->data) + vector->size - match ) ); 115
116 /* Here we're guaranteed to have enough space in vector to move the block of peers after insertion point */
117 memmove(match + peer_size, match, match_to_end);
106 118
107 vector->size++; 119 vector->size++;
108 return match; 120 return match;
@@ -113,126 +125,136 @@ ot_peer *vector_find_or_insert_peer( ot_vector *vector, ot_peer *peer, int *exac
113 1 if a non-seeding peer was removed 125 1 if a non-seeding peer was removed
114 2 if a seeding peer was removed 126 2 if a seeding peer was removed
115*/ 127*/
116int vector_remove_peer( ot_vector *vector, ot_peer *peer ) { 128int vector_remove_peer(ot_vector *vector, ot_peer const *peer, size_t peer_size) {
117 int exactmatch; 129 int exactmatch, was_seeder;
118 ot_peer *match, *end; 130 ot_peer *match, *end;
131 size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
119 132
120 if( !vector->size ) return 0; 133 if (!vector->size)
134 return 0;
121 135
122 /* If space is zero but size is set, we're dealing with a list of vector->size buckets */ 136 /* If space is zero but size is set, we're dealing with a list of vector->size buckets */
123 if( vector->space < vector->size ) 137 if (vector->space < vector->size)
124 vector = ((ot_vector*)vector->data) + vector_hash_peer(peer, vector->size ); 138 vector = ((ot_vector *)vector->data) + vector_hash_peer(peer, compare_size, vector->size);
125 139
126 end = ((ot_peer*)vector->data) + vector->size; 140 end = ((ot_peer *)vector->data) + peer_size * vector->size;
127 match = (ot_peer*)binary_search( peer, vector->data, vector->size, sizeof(ot_peer), OT_PEER_COMPARE_SIZE, &exactmatch ); 141 match = (ot_peer *)binary_search(peer, vector->data, vector->size, peer_size, compare_size, &exactmatch);
128 if( !exactmatch ) return 0; 142 if (!exactmatch)
143 return 0;
129 144
130 exactmatch = ( OT_PEERFLAG( match ) & PEER_FLAG_SEEDING ) ? 2 : 1; 145 was_seeder = (OT_PEERFLAG_D(match, peer_size) & PEER_FLAG_SEEDING) ? 2 : 1;
131 memmove( match, match + 1, sizeof(ot_peer) * ( end - match - 1 ) ); 146 memmove(match, match + peer_size, end - match - peer_size);
132 147
133 vector->size--; 148 vector->size--;
134 vector_fixup_peers( vector ); 149 vector_fixup_peers(vector, peer_size);
135 return exactmatch; 150 return was_seeder;
136} 151}
137 152
138void vector_remove_torrent( ot_vector *vector, ot_torrent *match ) { 153void vector_remove_torrent(ot_vector *vector, ot_torrent *match) {
139 ot_torrent *end = ((ot_torrent*)vector->data) + vector->size; 154 ot_torrent *end = ((ot_torrent *)vector->data) + vector->size;
140 155
141 if( !vector->size ) return; 156 if (!vector->size)
157 return;
142 158
143 /* If this is being called after a unsuccessful malloc() for peer_list 159 free_peerlist(match->peer_list6);
144 in add_peer_to_torrent, match->peer_list actually might be NULL */ 160 free_peerlist(match->peer_list4);
145 if( match->peer_list) free_peerlist( match->peer_list );
146 161
147 memmove( match, match + 1, sizeof(ot_torrent) * ( end - match - 1 ) ); 162 memmove(match, match + 1, sizeof(ot_torrent) * (end - match - 1));
148 if( ( --vector->size * OT_VECTOR_SHRINK_THRESH < vector->space ) && ( vector->space >= OT_VECTOR_SHRINK_RATIO * OT_VECTOR_MIN_MEMBERS ) ) { 163 if ((--vector->size * OT_VECTOR_SHRINK_THRESH < vector->space) && (vector->space >= OT_VECTOR_SHRINK_RATIO * OT_VECTOR_MIN_MEMBERS)) {
149 vector->space /= OT_VECTOR_SHRINK_RATIO; 164 vector->space /= OT_VECTOR_SHRINK_RATIO;
150 vector->data = realloc( vector->data, vector->space * sizeof( ot_torrent ) ); 165 vector->data = realloc(vector->data, vector->space * sizeof(ot_torrent));
151 } 166 }
152} 167}
153 168
154void vector_clean_list( ot_vector * vector, int num_buckets ) { 169void vector_clean_list(ot_vector *vector, size_t num_buckets) {
155 while( num_buckets-- ) 170 while (num_buckets--)
156 free( vector[num_buckets].data ); 171 free(vector[num_buckets].data);
157 free( vector ); 172 free(vector);
158 return; 173 return;
159} 174}
160 175
161void vector_redistribute_buckets( ot_peerlist * peer_list ) { 176void vector_redistribute_buckets(ot_peerlist *peer_list, size_t peer_size) {
162 int tmp, bucket, bucket_size_new, num_buckets_new, num_buckets_old = 1; 177 size_t tmp, bucket, bucket_size_new, num_buckets_new, num_buckets_old = 1;
163 ot_vector * bucket_list_new, * bucket_list_old = &peer_list->peers; 178 ot_vector *bucket_list_new, *bucket_list_old = &peer_list->peers;
179 int (*sort_func)(const void *, const void *) = peer_size == OT_PEER_SIZE6 ? &vector_compare_peer6 : &vector_compare_peer4;
164 180
165 if( OT_PEERLIST_HASBUCKETS( peer_list ) ) { 181 if (OT_PEERLIST_HASBUCKETS(peer_list)) {
166 num_buckets_old = peer_list->peers.size; 182 num_buckets_old = peer_list->peers.size;
167 bucket_list_old = peer_list->peers.data; 183 bucket_list_old = peer_list->peers.data;
168 } 184 }
169 185
170 if( peer_list->peer_count < 255 ) 186 if (peer_list->peer_count < 255)
171 num_buckets_new = 1; 187 num_buckets_new = 1;
172 else if( peer_list->peer_count > 8192 ) 188 else if (peer_list->peer_count > 8192)
173 num_buckets_new = 64; 189 num_buckets_new = 64;
174 else if( peer_list->peer_count >= 512 && peer_list->peer_count < 4096 ) 190 else if (peer_list->peer_count >= 512 && peer_list->peer_count < 4096)
175 num_buckets_new = 16; 191 num_buckets_new = 16;
176 else if( peer_list->peer_count < 512 && num_buckets_old <= 16 ) 192 else if (peer_list->peer_count < 512 && num_buckets_old <= 16)
177 num_buckets_new = num_buckets_old; 193 num_buckets_new = num_buckets_old;
178 else if( peer_list->peer_count < 512 ) 194 else if (peer_list->peer_count < 512)
179 num_buckets_new = 1; 195 num_buckets_new = 1;
180 else if( peer_list->peer_count < 8192 && num_buckets_old > 1 ) 196 else if (peer_list->peer_count < 8192 && num_buckets_old > 1)
181 num_buckets_new = num_buckets_old; 197 num_buckets_new = num_buckets_old;
182 else 198 else
183 num_buckets_new = 16; 199 num_buckets_new = 16;
184 200
185 if( num_buckets_new == num_buckets_old ) 201 if (num_buckets_new == num_buckets_old)
186 return; 202 return;
187 203
188 /* Assume near perfect distribution */ 204 /* Assume near perfect distribution */
189 bucket_list_new = malloc( num_buckets_new * sizeof( ot_vector ) ); 205 bucket_list_new = malloc(num_buckets_new * sizeof(ot_vector));
190 if( !bucket_list_new) return; 206 if (!bucket_list_new)
191 bzero( bucket_list_new, num_buckets_new * sizeof( ot_vector ) ); 207 return;
208 bzero(bucket_list_new, num_buckets_new * sizeof(ot_vector));
192 209
193 tmp = peer_list->peer_count / num_buckets_new; 210 tmp = peer_list->peer_count / num_buckets_new;
194 bucket_size_new = OT_VECTOR_MIN_MEMBERS; 211 bucket_size_new = OT_VECTOR_MIN_MEMBERS;
195 while( bucket_size_new < tmp) 212 while (bucket_size_new < tmp)
196 bucket_size_new *= OT_VECTOR_GROW_RATIO; 213 bucket_size_new *= OT_VECTOR_GROW_RATIO;
197 214
198 /* preallocate vectors to hold all peers */ 215 /* preallocate vectors to hold all peers */
199 for( bucket=0; bucket<num_buckets_new; ++bucket ) { 216 for (bucket = 0; bucket < num_buckets_new; ++bucket) {
200 bucket_list_new[bucket].space = bucket_size_new; 217 bucket_list_new[bucket].space = bucket_size_new;
201 bucket_list_new[bucket].data = malloc( bucket_size_new * sizeof(ot_peer) ); 218 bucket_list_new[bucket].data = malloc(bucket_size_new * peer_size);
202 if( !bucket_list_new[bucket].data ) 219 if (!bucket_list_new[bucket].data) {
203 return vector_clean_list( bucket_list_new, num_buckets_new ); 220 vector_clean_list(bucket_list_new, num_buckets_new);
221 return;
222 }
204 } 223 }
205 224
206 /* Now sort them into the correct bucket */ 225 /* Now sort them into the correct bucket */
207 for( bucket=0; bucket<num_buckets_old; ++bucket ) { 226 for (bucket = 0; bucket < num_buckets_old; ++bucket) {
208 ot_peer * peers_old = bucket_list_old[bucket].data, * peers_new; 227 ot_peer *peers_old = bucket_list_old[bucket].data;
209 int peer_count_old = bucket_list_old[bucket].size; 228 size_t peer_count_old = bucket_list_old[bucket].size;
210 while( peer_count_old-- ) { 229 while (peer_count_old--) {
211 ot_vector * bucket_dest = bucket_list_new; 230 ot_vector *bucket_dest = bucket_list_new;
212 if( num_buckets_new > 1 ) 231 if (num_buckets_new > 1)
213 bucket_dest += vector_hash_peer(peers_old, num_buckets_new); 232 bucket_dest += vector_hash_peer(peers_old, OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size), num_buckets_new);
214 if( bucket_dest->size + 1 > bucket_dest->space ) { 233 if (bucket_dest->size + 1 > bucket_dest->space) {
215 void * tmp = realloc( bucket_dest->data, sizeof(ot_peer) * OT_VECTOR_GROW_RATIO * bucket_dest->space ); 234 void *tmp = realloc(bucket_dest->data, peer_size * OT_VECTOR_GROW_RATIO * bucket_dest->space);
216 if( !tmp ) return vector_clean_list( bucket_list_new, num_buckets_new ); 235 if (!tmp) {
236 vector_clean_list(bucket_list_new, num_buckets_new);
237 return;
238 }
217 bucket_dest->data = tmp; 239 bucket_dest->data = tmp;
218 bucket_dest->space *= OT_VECTOR_GROW_RATIO; 240 bucket_dest->space *= OT_VECTOR_GROW_RATIO;
219 } 241 }
220 peers_new = (ot_peer*)bucket_dest->data; 242 memcpy((ot_peer *)bucket_dest->data + peer_size * bucket_dest->size++, peers_old, peer_size);
221 memcpy(peers_new + bucket_dest->size++, peers_old++, sizeof(ot_peer)); 243 peers_old += peer_size;
222 } 244 }
223 } 245 }
224 246
225 /* Now sort each bucket to later allow bsearch */ 247 /* Now sort each bucket to later allow bsearch */
226 for( bucket=0; bucket<num_buckets_new; ++bucket ) 248 for (bucket = 0; bucket < num_buckets_new; ++bucket)
227 qsort( bucket_list_new[bucket].data, bucket_list_new[bucket].size, sizeof( ot_peer ), vector_compare_peer ); 249 qsort(bucket_list_new[bucket].data, bucket_list_new[bucket].size, peer_size, sort_func);
228 250
229 /* Everything worked fine. Now link new bucket_list to peer_list */ 251 /* Everything worked fine. Now link new bucket_list to peer_list */
230 if( OT_PEERLIST_HASBUCKETS( peer_list) ) 252 if (OT_PEERLIST_HASBUCKETS(peer_list))
231 vector_clean_list( (ot_vector*)peer_list->peers.data, peer_list->peers.size ); 253 vector_clean_list((ot_vector *)peer_list->peers.data, peer_list->peers.size);
232 else 254 else
233 free( peer_list->peers.data ); 255 free(peer_list->peers.data);
234 256
235 if( num_buckets_new > 1 ) { 257 if (num_buckets_new > 1) {
236 peer_list->peers.data = bucket_list_new; 258 peer_list->peers.data = bucket_list_new;
237 peer_list->peers.size = num_buckets_new; 259 peer_list->peers.size = num_buckets_new;
238 peer_list->peers.space = 0; /* Magic marker for "is list of buckets" */ 260 peer_list->peers.space = 0; /* Magic marker for "is list of buckets" */
@@ -240,27 +262,24 @@ void vector_redistribute_buckets( ot_peerlist * peer_list ) {
240 peer_list->peers.data = bucket_list_new->data; 262 peer_list->peers.data = bucket_list_new->data;
241 peer_list->peers.size = bucket_list_new->size; 263 peer_list->peers.size = bucket_list_new->size;
242 peer_list->peers.space = bucket_list_new->space; 264 peer_list->peers.space = bucket_list_new->space;
243 free( bucket_list_new ); 265 free(bucket_list_new);
244 } 266 }
245} 267}
246 268
247void vector_fixup_peers( ot_vector * vector ) { 269void vector_fixup_peers(ot_vector *vector, size_t peer_size) {
248 int need_fix = 0; 270 int need_fix = 0;
249 271
250 if( !vector->size ) { 272 if (!vector->size) {
251 free( vector->data ); 273 free(vector->data);
252 vector->data = NULL; 274 vector->data = NULL;
253 vector->space = 0; 275 vector->space = 0;
254 return; 276 return;
255 } 277 }
256 278
257 while( ( vector->size * OT_VECTOR_SHRINK_THRESH < vector->space ) && 279 while ((vector->size * OT_VECTOR_SHRINK_THRESH < vector->space) && (vector->space >= OT_VECTOR_SHRINK_RATIO * OT_VECTOR_MIN_MEMBERS)) {
258 ( vector->space >= OT_VECTOR_SHRINK_RATIO * OT_VECTOR_MIN_MEMBERS ) ) {
259 vector->space /= OT_VECTOR_SHRINK_RATIO; 280 vector->space /= OT_VECTOR_SHRINK_RATIO;
260 need_fix++; 281 need_fix++;
261 } 282 }
262 if( need_fix ) 283 if (need_fix)
263 vector->data = realloc( vector->data, vector->space * sizeof( ot_peer ) ); 284 vector->data = realloc(vector->data, vector->space * peer_size);
264} 285}
265
266const char *g_version_vector_c = "$Source$: $Revision$\n";
diff --git a/ot_vector.h b/ot_vector.h
index f7f87aa..2618363 100644
--- a/ot_vector.h
+++ b/ot_vector.h
@@ -16,19 +16,21 @@
16#define OT_PEER_BUCKET_MAXCOUNT 256 16#define OT_PEER_BUCKET_MAXCOUNT 256
17 17
18typedef struct { 18typedef struct {
19 void *data; 19 void *data;
20 size_t size; 20 size_t size;
21 size_t space; 21 size_t space;
22} ot_vector; 22} ot_vector;
23 23
24void *binary_search( const void * const key, const void * base, const size_t member_count, const size_t member_size, 24void *binary_search(const void *const key, const void *base, const size_t member_count, const size_t member_size, size_t compare_size, int *exactmatch);
25 size_t compare_size, int *exactmatch ); 25void *vector_find_or_insert(ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch);
26void *vector_find_or_insert( ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch ); 26ot_peer *vector_find_or_insert_peer(ot_vector *vector, ot_peer const *peer, size_t peer_size, int *exactmatch);
27ot_peer *vector_find_or_insert_peer( ot_vector *vector, ot_peer *peer, int *exactmatch );
28 27
29int vector_remove_peer( ot_vector *vector, ot_peer *peer ); 28int vector_remove_peer(ot_vector *vector, ot_peer const *peer, size_t peer_size);
30void vector_remove_torrent( ot_vector *vector, ot_torrent *match ); 29void vector_remove_torrent(ot_vector *vector, ot_torrent *match);
31void vector_redistribute_buckets( ot_peerlist * peer_list ); 30
32void vector_fixup_peers( ot_vector * vector ); 31/* For ot_clean.c */
32void vector_redistribute_buckets(ot_peerlist *peer_list, size_t peer_size);
33void vector_fixup_peers(ot_vector *vector, size_t peer_size);
34void vector_clean_list(ot_vector *vector, size_t num_buckets);
33 35
34#endif 36#endif
diff --git a/proxy.c b/proxy.c
index 640958a..c2dc417 100644
--- a/proxy.c
+++ b/proxy.c
@@ -4,33 +4,31 @@
4 $Id$ */ 4 $Id$ */
5 5
6/* System */ 6/* System */
7#include <arpa/inet.h>
8#include <ctype.h>
9#include <errno.h>
10#include <fcntl.h>
11#include <pthread.h>
7#include <stdint.h> 12#include <stdint.h>
13#include <stdio.h>
8#include <stdlib.h> 14#include <stdlib.h>
9#include <string.h> 15#include <string.h>
10#include <arpa/inet.h>
11#include <sys/socket.h> 16#include <sys/socket.h>
12#include <unistd.h> 17#include <unistd.h>
13#include <errno.h>
14#include <signal.h>
15#include <stdio.h>
16#include <pwd.h>
17#include <ctype.h>
18#include <pthread.h>
19 18
20/* Libowfat */ 19/* Libowfat */
21#include "socket.h"
22#include "io.h" 20#include "io.h"
23#include "iob.h" 21#include "iob.h"
24#include "byte.h" 22#include "socket.h"
25#include "scan.h"
26#include "ip6.h"
27#include "ndelay.h"
28 23
29/* Opentracker */ 24/* Opentracker */
30#include "trackerlogic.h" 25#include "ot_ip.h"
31#include "ot_vector.h"
32#include "ot_mutex.h" 26#include "ot_mutex.h"
27#include "ot_random.h"
28#include "ot_scan.h"
33#include "ot_stats.h" 29#include "ot_stats.h"
30#include "ot_vector.h"
31#include "trackerlogic.h"
34 32
35#ifndef WANT_SYNC_LIVE 33#ifndef WANT_SYNC_LIVE
36#define WANT_SYNC_LIVE 34#define WANT_SYNC_LIVE
@@ -40,180 +38,189 @@
40ot_ip6 g_serverip; 38ot_ip6 g_serverip;
41uint16_t g_serverport = 9009; 39uint16_t g_serverport = 9009;
42uint32_t g_tracker_id; 40uint32_t g_tracker_id;
43char groupip_1[4] = { 224,0,23,5 }; 41char groupip_1[4] = {224, 0, 23, 5};
44int g_self_pipe[2]; 42int g_self_pipe[2];
45 43
46/* If you have more than 10 peers, don't use this proxy 44/* If you have more than 10 peers, don't use this proxy
47 Use 20 slots for 10 peers to have room for 10 incoming connection slots 45 Use 20 slots for 10 peers to have room for 10 incoming connection slots
48 */ 46 */
49#define MAX_PEERS 20 47#define MAX_PEERS 20
50 48
51#define LIVESYNC_INCOMING_BUFFSIZE (256*256) 49#define LIVESYNC_INCOMING_BUFFSIZE (256 * 256)
52#define STREAMSYNC_OUTGOING_BUFFSIZE (256*256) 50#define STREAMSYNC_OUTGOING_BUFFSIZE (256 * 256)
53 51
54#define LIVESYNC_OUTGOING_BUFFSIZE_PEERS 1480 52#define LIVESYNC_OUTGOING_BUFFSIZE_PEERS 1480
55#define LIVESYNC_OUTGOING_WATERMARK_PEERS (sizeof(ot_peer)+sizeof(ot_hash)) 53#define LIVESYNC_OUTGOING_WATERMARK_PEERS (sizeof(ot_peer) + sizeof(ot_hash))
56#define LIVESYNC_MAXDELAY 15 /* seconds */ 54#define LIVESYNC_MAXDELAY 15 /* seconds */
57 55
58/* The amount of time a complete sync cycle should take */ 56/* The amount of time a complete sync cycle should take */
59#define OT_SYNC_INTERVAL_MINUTES 2 57#define OT_SYNC_INTERVAL_MINUTES 2
60 58
61/* So after each bucket wait 1 / OT_BUCKET_COUNT intervals */ 59/* So after each bucket wait 1 / OT_BUCKET_COUNT intervals */
62#define OT_SYNC_SLEEP ( ( ( OT_SYNC_INTERVAL_MINUTES ) * 60 * 1000000 ) / ( OT_BUCKET_COUNT ) ) 60#define OT_SYNC_SLEEP (((OT_SYNC_INTERVAL_MINUTES) * 60 * 1000000) / (OT_BUCKET_COUNT))
63 61
64enum { OT_SYNC_PEER }; 62enum { OT_SYNC_PEER4, OT_SYNC_PEER6 };
65enum { FLAG_SERVERSOCKET = 1 }; 63enum { FLAG_SERVERSOCKET = 1 };
66 64
67/* For incoming packets */ 65/* For incoming packets */
68static int64 g_socket_in = -1; 66static int64_t g_socket_in = -1;
69static uint8_t g_inbuffer[LIVESYNC_INCOMING_BUFFSIZE]; 67static uint8_t g_inbuffer[LIVESYNC_INCOMING_BUFFSIZE];
70 68
71/* For outgoing packets */ 69/* For outgoing packets */
72static int64 g_socket_out = -1; 70static int64_t g_socket_out = -1;
73static uint8_t g_peerbuffer_start[LIVESYNC_OUTGOING_BUFFSIZE_PEERS]; 71static uint8_t g_peerbuffer_start[LIVESYNC_OUTGOING_BUFFSIZE_PEERS];
74static uint8_t *g_peerbuffer_pos; 72static uint8_t *g_peerbuffer_pos;
75static uint8_t *g_peerbuffer_highwater = g_peerbuffer_start + LIVESYNC_OUTGOING_BUFFSIZE_PEERS - LIVESYNC_OUTGOING_WATERMARK_PEERS; 73static uint8_t *g_peerbuffer_highwater = g_peerbuffer_start + LIVESYNC_OUTGOING_BUFFSIZE_PEERS - LIVESYNC_OUTGOING_WATERMARK_PEERS;
76static ot_time g_next_packet_time; 74static ot_time g_next_packet_time;
77 75
78static void * livesync_worker( void * args ); 76static void *livesync_worker(void *args);
79static void * streamsync_worker( void * args ); 77static void *streamsync_worker(void *args);
80static void livesync_proxytell( uint8_t prefix, uint8_t *info_hash, uint8_t *peer ); 78static void livesync_proxytell(uint8_t prefix, uint8_t *info_hash, uint8_t *peer);
81 79
82void exerr( char * message ) { 80void exerr(char *message) {
83 fprintf( stderr, "%s\n", message ); 81 fprintf(stderr, "%s\n", message);
84 exit( 111 ); 82 exit(111);
85} 83}
86 84
87void stats_issue_event( ot_status_event event, PROTO_FLAG proto, uintptr_t event_data ) { 85void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_data) {
88 (void) event; 86 (void)event;
89 (void) proto; 87 (void)proto;
90 (void) event_data; 88 (void)event_data;
91} 89}
92 90
93void livesync_bind_mcast( ot_ip6 ip, uint16_t port) { 91void livesync_bind_mcast(ot_ip6 ip, uint16_t port) {
94 char tmpip[4] = {0,0,0,0}; 92 char tmpip[4] = {0, 0, 0, 0};
95 char *v4ip; 93 char *v4ip;
96 94
97 if( !ip6_isv4mapped(ip)) 95 if (!ip_is_v4mapped(ip))
98 exerr("v6 mcast support not yet available."); 96 exerr("v6 mcast support not yet available.");
99 v4ip = ip+12; 97 v4ip = ip + 12;
100 98
101 if( g_socket_in != -1 ) 99 if (g_socket_in != -1)
102 exerr("Error: Livesync listen ip specified twice."); 100 exerr("Error: Livesync listen ip specified twice.");
103 101
104 if( ( g_socket_in = socket_udp4( )) < 0) 102 if ((g_socket_in = socket_udp4()) < 0)
105 exerr("Error: Cant create live sync incoming socket." ); 103 exerr("Error: Cant create live sync incoming socket.");
106 ndelay_off(g_socket_in); 104
105 fcntl(g_socket_in, F_SETFL, fcntl(g_socket_in, F_GETFL, 0) & ~O_NONBLOCK);
107 106
108 if( socket_bind4_reuse( g_socket_in, tmpip, port ) == -1 ) 107 if (socket_bind4_reuse(g_socket_in, tmpip, port) == -1)
109 exerr("Error: Cant bind live sync incoming socket." ); 108 exerr("Error: Cant bind live sync incoming socket.");
110 109
111 if( socket_mcjoin4( g_socket_in, groupip_1, v4ip ) ) 110 if (socket_mcjoin4(g_socket_in, groupip_1, v4ip))
112 exerr("Error: Cant make live sync incoming socket join mcast group."); 111 exerr("Error: Cant make live sync incoming socket join mcast group.");
113 112
114 if( ( g_socket_out = socket_udp4()) < 0) 113 if ((g_socket_out = socket_udp4()) < 0)
115 exerr("Error: Cant create live sync outgoing socket." ); 114 exerr("Error: Cant create live sync outgoing socket.");
116 if( socket_bind4_reuse( g_socket_out, v4ip, port ) == -1 ) 115 if (socket_bind4_reuse(g_socket_out, v4ip, port) == -1)
117 exerr("Error: Cant bind live sync outgoing socket." ); 116 exerr("Error: Cant bind live sync outgoing socket.");
118 117
119 socket_mcttl4(g_socket_out, 1); 118 socket_mcttl4(g_socket_out, 1);
120 socket_mcloop4(g_socket_out, 1); 119 socket_mcloop4(g_socket_out, 1);
121} 120}
122 121
123size_t add_peer_to_torrent_proxy( ot_hash hash, ot_peer *peer ) { 122size_t add_peer_to_torrent_proxy(ot_hash hash, ot_peer *peer, size_t peer_size) {
124 int exactmatch; 123 int exactmatch;
125 ot_torrent *torrent; 124 ot_torrent *torrent;
126 ot_peer *peer_dest; 125 ot_peerlist *peer_list;
127 ot_vector *torrents_list = mutex_bucket_lock_by_hash( hash ); 126 ot_peer *peer_dest;
127 ot_vector *torrents_list = mutex_bucket_lock_by_hash(hash);
128 size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
128 129
129 torrent = vector_find_or_insert( torrents_list, (void*)hash, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 130 torrent = vector_find_or_insert(torrents_list, (void *)hash, sizeof(ot_torrent), compare_size, &exactmatch);
130 if( !torrent ) 131 if (!torrent)
131 return -1; 132 return -1;
132 133
133 if( !exactmatch ) { 134 if (!exactmatch) {
134 /* Create a new torrent entry, then */ 135 /* Create a new torrent entry, then */
135 memcpy( torrent->hash, hash, sizeof(ot_hash) ); 136 memcpy(torrent->hash, hash, sizeof(ot_hash));
136 137
137 if( !( torrent->peer_list = malloc( sizeof (ot_peerlist) ) ) ) { 138 if (!(torrent->peer_list6 = malloc(sizeof(ot_peerlist))) || !(torrent->peer_list4 = malloc(sizeof(ot_peerlist)))) {
138 vector_remove_torrent( torrents_list, torrent ); 139 vector_remove_torrent(torrents_list, torrent);
139 mutex_bucket_unlock_by_hash( hash, 0 ); 140 mutex_bucket_unlock_by_hash(hash, 0);
140 return -1; 141 return -1;
141 } 142 }
142 143
143 byte_zero( torrent->peer_list, sizeof( ot_peerlist ) ); 144 memset(torrent->peer_list6, 0, sizeof(ot_peerlist));
145 memset(torrent->peer_list4, 0, sizeof(ot_peerlist));
144 } 146 }
145 147
148 peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4;
149
146 /* Check for peer in torrent */ 150 /* Check for peer in torrent */
147 peer_dest = vector_find_or_insert_peer( &(torrent->peer_list->peers), peer, &exactmatch ); 151 peer_dest = vector_find_or_insert_peer(&(peer_list->peers), peer, peer_size, &exactmatch);
148 if( !peer_dest ) { 152 if (!peer_dest) {
149 mutex_bucket_unlock_by_hash( hash, 0 ); 153 mutex_bucket_unlock_by_hash(hash, 0);
150 return -1; 154 return -1;
151 } 155 }
152 /* Tell peer that it's fresh */ 156 /* Tell peer that it's fresh */
153 OT_PEERTIME( peer ) = 0; 157 OT_PEERTIME(peer, peer_size) = 0;
154 158
155 /* If we hadn't had a match create peer there */ 159 /* If we hadn't had a match create peer there */
156 if( !exactmatch ) { 160 if (!exactmatch) {
157 torrent->peer_list->peer_count++; 161 peer_list->peer_count++;
158 if( OT_PEERFLAG(peer) & PEER_FLAG_SEEDING ) 162 if (OT_PEERFLAG_D(peer, peer_size) & PEER_FLAG_SEEDING)
159 torrent->peer_list->seed_count++; 163 peer_list->seed_count++;
160 } 164 }
161 memcpy( peer_dest, peer, sizeof(ot_peer) ); 165 memcpy(peer_dest, peer, peer_size);
162 mutex_bucket_unlock_by_hash( hash, 0 ); 166 mutex_bucket_unlock_by_hash(hash, 0);
163 return 0; 167 return 0;
164} 168}
165 169
166size_t remove_peer_from_torrent_proxy( ot_hash hash, ot_peer *peer ) { 170size_t remove_peer_from_torrent_proxy(ot_hash hash, ot_peer *peer, size_t peer_size) {
167 int exactmatch; 171 int exactmatch;
168 ot_vector *torrents_list = mutex_bucket_lock_by_hash( hash ); 172 ot_vector *torrents_list = mutex_bucket_lock_by_hash(hash);
169 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 173 ot_torrent *torrent = binary_search(hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch);
170 174
171 if( exactmatch ) { 175 if (exactmatch) {
172 ot_peerlist *peer_list = torrent->peer_list; 176 ot_peerlist *peer_list = peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4;
173 switch( vector_remove_peer( &peer_list->peers, peer ) ) { 177 switch (vector_remove_peer(&peer_list->peers, peer, peer_size)) {
174 case 2: peer_list->seed_count--; /* Fall throughs intended */ 178 case 2:
175 case 1: peer_list->peer_count--; /* Fall throughs intended */ 179 peer_list->seed_count--; /* Intentional fallthrough */
176 default: break; 180 case 1:
181 peer_list->peer_count--; /* Intentional fallthrough */
182 default:
183 break;
177 } 184 }
178 } 185 }
179 186
180 mutex_bucket_unlock_by_hash( hash, 0 ); 187 mutex_bucket_unlock_by_hash(hash, 0);
181 return 0; 188 return 0;
182} 189}
183 190
184void free_peerlist( ot_peerlist *peer_list ) { 191void free_peerlist(ot_peerlist *peer_list) {
185 if( peer_list->peers.data ) { 192 if (peer_list->peers.data) {
186 if( OT_PEERLIST_HASBUCKETS( peer_list ) ) { 193 if (OT_PEERLIST_HASBUCKETS(peer_list)) {
187 ot_vector *bucket_list = (ot_vector*)(peer_list->peers.data); 194 ot_vector *bucket_list = (ot_vector *)(peer_list->peers.data);
188 195
189 while( peer_list->peers.size-- ) 196 while (peer_list->peers.size--)
190 free( bucket_list++->data ); 197 free(bucket_list++->data);
191 } 198 }
192 free( peer_list->peers.data ); 199 free(peer_list->peers.data);
193 } 200 }
194 free( peer_list ); 201 free(peer_list);
195} 202}
196 203
197static void livesync_handle_peersync( ssize_t datalen ) { 204static void livesync_handle_peersync(ssize_t datalen, size_t peer_size) {
198 int off = sizeof( g_tracker_id ) + sizeof( uint32_t ); 205 int off = sizeof(g_tracker_id) + sizeof(uint32_t);
199 206
200 fprintf( stderr, "." ); 207 fprintf(stderr, ".");
201 208
202 while( off + (ssize_t)sizeof( ot_hash ) + (ssize_t)sizeof( ot_peer ) <= datalen ) { 209 while ((ssize_t)(off + sizeof(ot_hash) + peer_size) <= datalen) {
203 ot_peer *peer = (ot_peer*)(g_inbuffer + off + sizeof(ot_hash)); 210 ot_peer *peer = (ot_peer *)(g_inbuffer + off + sizeof(ot_hash));
204 ot_hash *hash = (ot_hash*)(g_inbuffer + off); 211 ot_hash *hash = (ot_hash *)(g_inbuffer + off);
205 212
206 if( OT_PEERFLAG(peer) & PEER_FLAG_STOPPED ) 213 if (OT_PEERFLAG_D(peer, peer_size) & PEER_FLAG_STOPPED)
207 remove_peer_from_torrent_proxy( *hash, peer ); 214 remove_peer_from_torrent_proxy(*hash, peer, peer_size);
208 else 215 else
209 add_peer_to_torrent_proxy( *hash, peer ); 216 add_peer_to_torrent_proxy(*hash, peer, peer_size);
210 217
211 off += sizeof( ot_hash ) + sizeof( ot_peer ); 218 off += sizeof(ot_hash) + peer_size;
212 } 219 }
213} 220}
214 221
215int usage( char *self ) { 222int usage(char *self) {
216 fprintf( stderr, "Usage: %s -L <livesync_iface_ip> -l <listenip>:<listenport> -c <connectip>:<connectport>\n", self ); 223 fprintf(stderr, "Usage: %s -L <livesync_iface_ip> -l <listenip>:<listenport> -c <connectip>:<connectport>\n", self);
217 return 0; 224 return 0;
218} 225}
219 226
@@ -228,115 +235,115 @@ enum {
228 FLAG_MASK = 0x07 235 FLAG_MASK = 0x07
229}; 236};
230 237
231#define PROXYPEER_NEEDSCONNECT(flag) ((flag)==FLAG_OUTGOING) 238#define PROXYPEER_NEEDSCONNECT(flag) ((flag) == FLAG_OUTGOING)
232#define PROXYPEER_ISCONNECTED(flag) (((flag)&FLAG_MASK)==FLAG_CONNECTED) 239#define PROXYPEER_ISCONNECTED(flag) (((flag) & FLAG_MASK) == FLAG_CONNECTED)
233#define PROXYPEER_SETDISCONNECTED(flag) (flag)=(((flag)&FLAG_OUTGOING)|FLAG_DISCONNECTED) 240#define PROXYPEER_SETDISCONNECTED(flag) (flag) = (((flag) & FLAG_OUTGOING) | FLAG_DISCONNECTED)
234#define PROXYPEER_SETCONNECTING(flag) (flag)=(((flag)&FLAG_OUTGOING)|FLAG_CONNECTING) 241#define PROXYPEER_SETCONNECTING(flag) (flag) = (((flag) & FLAG_OUTGOING) | FLAG_CONNECTING)
235#define PROXYPEER_SETWAITTRACKERID(flag) (flag)=(((flag)&FLAG_OUTGOING)|FLAG_WAITTRACKERID) 242#define PROXYPEER_SETWAITTRACKERID(flag) (flag) = (((flag) & FLAG_OUTGOING) | FLAG_WAITTRACKERID)
236#define PROXYPEER_SETCONNECTED(flag) (flag)=(((flag)&FLAG_OUTGOING)|FLAG_CONNECTED) 243#define PROXYPEER_SETCONNECTED(flag) (flag) = (((flag) & FLAG_OUTGOING) | FLAG_CONNECTED)
237 244
238typedef struct { 245typedef struct {
239 int state; /* Whether we want to connect, how far our handshake is, etc. */ 246 int state; /* Whether we want to connect, how far our handshake is, etc. */
240 ot_ip6 ip; /* The peer to connect to */ 247 ot_ip6 ip; /* The peer to connect to */
241 uint16_t port; /* The peers port */ 248 uint16_t port; /* The peers port */
242 uint8_t indata[8192*16]; /* Any data not processed yet */ 249 uint8_t indata[8192 * 16]; /* Any data not processed yet */
243 size_t indata_length; /* Length of unprocessed data */ 250 size_t indata_length; /* Length of unprocessed data */
244 uint32_t tracker_id; /* How the other end greeted */ 251 uint32_t tracker_id; /* How the other end greeted */
245 int64 fd; /* A file handle, if connected, <= 0 is disconnected (0 initially, -1 else) */ 252 int64_t fd; /* A file handle, if connected, <= 0 is disconnected (0 initially, -1 else) */
246 io_batch outdata; /* The iobatch containing our sync data */ 253 io_batch outdata; /* The iobatch containing our sync data */
247 254
248 size_t packet_tcount; /* Number of unprocessed torrents in packet we currently receive */ 255 size_t packet_tcount; /* Number of unprocessed torrents in packet we currently receive */
249 uint8_t packet_tprefix; /* Prefix byte for all torrents in current packet */ 256 uint8_t packet_tprefix; /* Prefix byte for all torrents in current packet */
250 uint8_t packet_type; /* Type of current packet */ 257 uint8_t packet_type; /* Type of current packet */
251 uint32_t packet_tid; /* Tracker id for current packet */ 258 uint32_t packet_tid; /* Tracker id for current packet */
252 259
253} proxy_peer; 260} proxy_peer;
254static void process_indata( proxy_peer * peer ); 261static void process_indata(proxy_peer *peer);
255 262
256void reset_info_block( proxy_peer * peer ) { 263void reset_info_block(proxy_peer *peer) {
257 peer->indata_length = 0; 264 peer->indata_length = 0;
258 peer->tracker_id = 0; 265 peer->tracker_id = 0;
259 peer->fd = -1; 266 peer->fd = -1;
260 peer->packet_tcount = 0; 267 peer->packet_tcount = 0;
261 iob_reset( &peer->outdata ); 268 iob_reset(&peer->outdata);
262 PROXYPEER_SETDISCONNECTED( peer->state ); 269 PROXYPEER_SETDISCONNECTED(peer->state);
263} 270}
264 271
265/* Number of connections to peers 272/* Number of connections to peers
266 * If a peer's IP is set, we try to reconnect, when the connection drops 273 * If a peer's IP is set, we try to reconnect, when the connection drops
267 * If we already have a connected tracker_id in our records for an _incoming_ connection, drop it 274 * If we already have a connected tracker_id in our records for an _incoming_ connection, drop it
268 * Multiple connections to/from the same ip are okay, if tracker_id doesn't match 275 * Multiple connections to/from the same ip are okay, if tracker_id doesn't match
269 * Reconnect attempts occur only twice a minute 276 * Reconnect attempts occur only twice a minute
270*/ 277 */
271static int g_connection_count; 278static size_t g_connection_count;
272static ot_time g_connection_reconn; 279static ot_time g_connection_reconn;
273static proxy_peer g_connections[MAX_PEERS]; 280static proxy_peer g_connections[MAX_PEERS];
274 281
275static void handle_reconnects( void ) { 282static void handle_reconnects(void) {
276 int i; 283 size_t i;
277 for( i=0; i<g_connection_count; ++i ) 284 for (i = 0; i < g_connection_count; ++i)
278 if( PROXYPEER_NEEDSCONNECT( g_connections[i].state ) ) { 285 if (PROXYPEER_NEEDSCONNECT(g_connections[i].state)) {
279 int64 newfd = socket_tcp6( ); 286 int64_t newfd = socket_tcp6();
280 fprintf( stderr, "(Re)connecting to peer..." ); 287 fprintf(stderr, "(Re)connecting to peer...");
281 if( newfd < 0 ) continue; /* No socket for you */ 288 if (newfd < 0)
289 continue; /* No socket for you */
282 io_fd(newfd); 290 io_fd(newfd);
283 if( socket_bind6_reuse(newfd,g_serverip,g_serverport,0) ) { 291 if (socket_bind6_reuse(newfd, g_serverip, g_serverport, 0)) {
284 io_close( newfd ); 292 io_close(newfd);
285 continue; 293 continue;
286 } 294 }
287 if( socket_connect6(newfd,g_connections[i].ip,g_connections[i].port,0) == -1 && 295 if (socket_connect6(newfd, g_connections[i].ip, g_connections[i].port, 0) == -1 && errno != EINPROGRESS && errno != EWOULDBLOCK) {
288 errno != EINPROGRESS && errno != EWOULDBLOCK ) {
289 close(newfd); 296 close(newfd);
290 continue; 297 continue;
291 } 298 }
292 io_wantwrite(newfd); /* So we will be informed when it is connected */ 299 io_wantwrite(newfd); /* So we will be informed when it is connected */
293 io_setcookie(newfd,g_connections+i); 300 io_setcookie(newfd, g_connections + i);
294 301
295 /* Prepare connection info block */ 302 /* Prepare connection info block */
296 reset_info_block( g_connections+i ); 303 reset_info_block(g_connections + i);
297 g_connections[i].fd = newfd; 304 g_connections[i].fd = newfd;
298 PROXYPEER_SETCONNECTING( g_connections[i].state ); 305 PROXYPEER_SETCONNECTING(g_connections[i].state);
299 } 306 }
300 g_connection_reconn = time(NULL) + 30; 307 g_connection_reconn = time(NULL) + 30;
301} 308}
302 309
303/* Handle incoming connection requests, check against whitelist */ 310/* Handle incoming connection requests, check against whitelist */
304static void handle_accept( int64 serversocket ) { 311static void handle_accept(int64_t serversocket) {
305 int64 newfd; 312 int64_t newfd;
306 ot_ip6 ip; 313 ot_ip6 ip;
307 uint16 port; 314 uint16 port;
308 315
309 while( ( newfd = socket_accept6( serversocket, ip, &port, NULL ) ) != -1 ) { 316 while ((newfd = socket_accept6(serversocket, ip, &port, NULL)) != -1) {
310 317
311 /* XXX some access control */ 318 /* XXX some access control */
312 319
313 /* Put fd into a non-blocking mode */ 320 /* Put fd into a non-blocking mode */
314 io_nonblock( newfd ); 321 io_nonblock(newfd);
315 322
316 if( !io_fd( newfd ) ) 323 if (!io_fd(newfd))
317 io_close( newfd ); 324 io_close(newfd);
318 else { 325 else {
319 /* Find a new home for our incoming connection */ 326 /* Find a new home for our incoming connection */
320 int i; 327 int i;
321 for( i=0; i<MAX_PEERS; ++i ) 328 for (i = 0; i < MAX_PEERS; ++i)
322 if( g_connections[i].state == FLAG_DISCONNECTED ) 329 if (g_connections[i].state == FLAG_DISCONNECTED)
323 break; 330 break;
324 if( i == MAX_PEERS ) { 331 if (i == MAX_PEERS) {
325 fprintf( stderr, "No room for incoming connection." ); 332 fprintf(stderr, "No room for incoming connection.");
326 close( newfd ); 333 close(newfd);
327 continue; 334 continue;
328 } 335 }
329 336
330 /* Prepare connection info block */ 337 /* Prepare connection info block */
331 reset_info_block( g_connections+i ); 338 reset_info_block(g_connections + i);
332 PROXYPEER_SETCONNECTING( g_connections[i].state ); 339 PROXYPEER_SETCONNECTING(g_connections[i].state);
333 g_connections[i].port = port; 340 g_connections[i].port = port;
334 g_connections[i].fd = newfd; 341 g_connections[i].fd = newfd;
335 342
336 io_setcookie( newfd, g_connections + i ); 343 io_setcookie(newfd, g_connections + i);
337 344
338 /* We expect the connecting side to begin with its tracker_id */ 345 /* We expect the connecting side to begin with its tracker_id */
339 io_wantread( newfd ); 346 io_wantread(newfd);
340 } 347 }
341 } 348 }
342 349
@@ -344,117 +351,116 @@ static void handle_accept( int64 serversocket ) {
344} 351}
345 352
346/* New sync data on the stream */ 353/* New sync data on the stream */
347static void handle_read( int64 peersocket ) { 354static void handle_read(int64_t peersocket) {
348 int i; 355 int i;
349 int64 datalen; 356 int64_t datalen;
350 uint32_t tracker_id; 357 uint32_t tracker_id;
351 proxy_peer *peer = io_getcookie( peersocket ); 358 proxy_peer *peer = io_getcookie(peersocket);
352 359
353 if( !peer ) { 360 if (!peer) {
354 /* Can't happen ;) */ 361 /* Can't happen ;) */
355 io_close( peersocket ); 362 io_close(peersocket);
356 return; 363 return;
357 } 364 }
358 switch( peer->state & FLAG_MASK ) { 365 switch (peer->state & FLAG_MASK) {
359 case FLAG_DISCONNECTED: 366 case FLAG_DISCONNECTED:
360 io_close( peersocket ); 367 io_close(peersocket);
361 break; /* Shouldnt happen */ 368 break; /* Shouldnt happen */
362 case FLAG_CONNECTING: 369 case FLAG_CONNECTING:
363 case FLAG_WAITTRACKERID: 370 case FLAG_WAITTRACKERID:
364 /* We want at least the first four bytes to come at once, to avoid keeping extra states (for now) 371 /* We want at least the first four bytes to come at once, to avoid keeping extra states (for now)
365 This also catches 0 bytes reads == EOF and negative values, denoting connection errors */ 372 This also catches 0 bytes reads == EOF and negative values, denoting connection errors */
366 if( io_tryread( peersocket, (void*)&tracker_id, sizeof( tracker_id ) ) != sizeof( tracker_id ) ) 373 if (io_tryread(peersocket, (void *)&tracker_id, sizeof(tracker_id)) != sizeof(tracker_id))
367 goto close_socket; 374 goto close_socket;
368 375
369 /* See, if we already have a connection to that peer */ 376 /* See, if we already have a connection to that peer */
370 for( i=0; i<MAX_PEERS; ++i ) 377 for (i = 0; i < MAX_PEERS; ++i)
371 if( ( g_connections[i].state & FLAG_MASK ) == FLAG_CONNECTED && 378 if ((g_connections[i].state & FLAG_MASK) == FLAG_CONNECTED && g_connections[i].tracker_id == tracker_id) {
372 g_connections[i].tracker_id == tracker_id ) { 379 fprintf(stderr, "Peer already connected. Closing connection.\n");
373 fprintf( stderr, "Peer already connected. Closing connection.\n" );
374 goto close_socket; 380 goto close_socket;
375 } 381 }
376 382
377 /* Also no need for soliloquy */ 383 /* Also no need for soliloquy */
378 if( tracker_id == g_tracker_id ) 384 if (tracker_id == g_tracker_id)
379 goto close_socket; 385 goto close_socket;
380 386
381 /* The new connection is good, send our tracker_id on incoming connections */ 387 /* The new connection is good, send our tracker_id on incoming connections */
382 if( peer->state == FLAG_CONNECTING ) 388 if (peer->state == FLAG_CONNECTING)
383 if( io_trywrite( peersocket, (void*)&g_tracker_id, sizeof( g_tracker_id ) ) != sizeof( g_tracker_id ) ) 389 if (io_trywrite(peersocket, (void *)&g_tracker_id, sizeof(g_tracker_id)) != sizeof(g_tracker_id))
384 goto close_socket; 390 goto close_socket;
385 391
386 peer->tracker_id = tracker_id; 392 peer->tracker_id = tracker_id;
387 PROXYPEER_SETCONNECTED( peer->state ); 393 PROXYPEER_SETCONNECTED(peer->state);
388 394
389 if( peer->state & FLAG_OUTGOING ) 395 if (peer->state & FLAG_OUTGOING)
390 fprintf( stderr, "succeeded.\n" ); 396 fprintf(stderr, "succeeded.\n");
391 else 397 else
392 fprintf( stderr, "Incoming connection successful.\n" ); 398 fprintf(stderr, "Incoming connection successful.\n");
393 399
394 break; 400 break;
395close_socket: 401 close_socket:
396 fprintf( stderr, "Handshake incomplete, closing socket\n" ); 402 fprintf(stderr, "Handshake incomplete, closing socket\n");
397 io_close( peersocket ); 403 io_close(peersocket);
398 reset_info_block( peer ); 404 reset_info_block(peer);
399 break; 405 break;
400 case FLAG_CONNECTED: 406 case FLAG_CONNECTED:
401 /* Here we acutally expect data from peer 407 /* Here we acutally expect data from peer
402 indata_length should be less than 20+256*7 bytes, for incomplete torrent entries */ 408 indata_length should be less than 20+256*7 bytes, for incomplete torrent entries */
403 datalen = io_tryread( peersocket, (void*)(peer->indata + peer->indata_length), sizeof( peer->indata ) - peer->indata_length ); 409 datalen = io_tryread(peersocket, (void *)(peer->indata + peer->indata_length), sizeof(peer->indata) - peer->indata_length);
404 if( !datalen || datalen < -1 ) { 410 if (!datalen || datalen < -1) {
405 fprintf( stderr, "Connection closed by remote peer.\n" ); 411 fprintf(stderr, "Connection closed by remote peer.\n");
406 io_close( peersocket ); 412 io_close(peersocket);
407 reset_info_block( peer ); 413 reset_info_block(peer);
408 } else if( datalen > 0 ) { 414 } else if (datalen > 0) {
409 peer->indata_length += datalen; 415 peer->indata_length += datalen;
410 process_indata( peer ); 416 process_indata(peer);
411 } 417 }
412 break; 418 break;
413 } 419 }
414} 420}
415 421
416/* Can write new sync data to the stream */ 422/* Can write new sync data to the stream */
417static void handle_write( int64 peersocket ) { 423static void handle_write(int64_t peersocket) {
418 proxy_peer *peer = io_getcookie( peersocket ); 424 proxy_peer *peer = io_getcookie(peersocket);
419 425
420 if( !peer ) { 426 if (!peer) {
421 /* Can't happen ;) */ 427 /* Can't happen ;) */
422 io_close( peersocket ); 428 io_close(peersocket);
423 return; 429 return;
424 } 430 }
425 431
426 switch( peer->state & FLAG_MASK ) { 432 switch (peer->state & FLAG_MASK) {
427 case FLAG_DISCONNECTED: 433 case FLAG_DISCONNECTED:
428 default: /* Should not happen */ 434 default: /* Should not happen */
429 io_close( peersocket ); 435 io_close(peersocket);
430 break; 436 break;
431 case FLAG_CONNECTING: 437 case FLAG_CONNECTING:
432 /* Ensure that the connection is established and handle connection error */ 438 /* Ensure that the connection is established and handle connection error */
433 if( peer->state & FLAG_OUTGOING && !socket_connected( peersocket ) ) { 439 if (peer->state & FLAG_OUTGOING && !socket_connected(peersocket)) {
434 fprintf( stderr, "failed\n" ); 440 fprintf(stderr, "failed\n");
435 reset_info_block( peer ); 441 reset_info_block(peer);
436 io_close( peersocket ); 442 io_close(peersocket);
437 break; 443 break;
438 } 444 }
439 445
440 if( io_trywrite( peersocket, (void*)&g_tracker_id, sizeof( g_tracker_id ) ) == sizeof( g_tracker_id ) ) { 446 if (io_trywrite(peersocket, (void *)&g_tracker_id, sizeof(g_tracker_id)) == sizeof(g_tracker_id)) {
441 PROXYPEER_SETWAITTRACKERID( peer->state ); 447 PROXYPEER_SETWAITTRACKERID(peer->state);
442 io_dontwantwrite( peersocket ); 448 io_dontwantwrite(peersocket);
443 io_wantread( peersocket ); 449 io_wantread(peersocket);
444 } else { 450 } else {
445 fprintf( stderr, "Handshake incomplete, closing socket\n" ); 451 fprintf(stderr, "Handshake incomplete, closing socket\n");
446 io_close( peersocket ); 452 io_close(peersocket);
447 reset_info_block( peer ); 453 reset_info_block(peer);
448 } 454 }
449 break; 455 break;
450 case FLAG_CONNECTED: 456 case FLAG_CONNECTED:
451 switch( iob_send( peersocket, &peer->outdata ) ) { 457 switch (iob_send(peersocket, &peer->outdata)) {
452 case 0: /* all data sent */ 458 case 0: /* all data sent */
453 io_dontwantwrite( peersocket ); 459 io_dontwantwrite(peersocket);
454 break; 460 break;
455 case -3: /* an error occured */ 461 case -3: /* an error occured */
456 io_close( peersocket ); 462 io_close(peersocket);
457 reset_info_block( peer ); 463 reset_info_block(peer);
458 break; 464 break;
459 default: /* Normal operation or eagain */ 465 default: /* Normal operation or eagain */
460 break; 466 break;
@@ -466,293 +472,327 @@ static void handle_write( int64 peersocket ) {
466} 472}
467 473
468static void server_mainloop() { 474static void server_mainloop() {
469 int64 sock; 475 int64_t sock;
476 uint32_t packet_type;
470 477
471 /* inlined livesync_init() */ 478 /* inlined livesync_init() */
472 memset( g_peerbuffer_start, 0, sizeof( g_peerbuffer_start ) ); 479 memset(g_peerbuffer_start, 0, sizeof(g_peerbuffer_start));
473 g_peerbuffer_pos = g_peerbuffer_start; 480 g_peerbuffer_pos = g_peerbuffer_start;
474 memcpy( g_peerbuffer_pos, &g_tracker_id, sizeof( g_tracker_id ) ); 481 memcpy(g_peerbuffer_pos, &g_tracker_id, sizeof(g_tracker_id));
475 uint32_pack_big( (char*)g_peerbuffer_pos + sizeof( g_tracker_id ), OT_SYNC_PEER); 482 packet_type = htonl(OT_SYNC_PEER);
476 g_peerbuffer_pos += sizeof( g_tracker_id ) + sizeof( uint32_t); 483 memcpy(g_peerbuffer_pos + sizeof(g_tracker_id), &packet_type, sizeof(packet_type));
477 g_next_packet_time = time(NULL) + LIVESYNC_MAXDELAY; 484 g_peerbuffer_pos += sizeof(g_tracker_id) + sizeof(uint32_t);
485 g_next_packet_time = time(NULL) + LIVESYNC_MAXDELAY;
478 486
479 while(1) { 487 while (1) {
480 /* See, if we need to connect to anyone */ 488 /* See if we need to connect to anyone */
481 if( time(NULL) > g_connection_reconn ) 489 if (time(NULL) > g_connection_reconn)
482 handle_reconnects( ); 490 handle_reconnects();
483 491
484 /* Wait for io events until next approx reconn check time */ 492 /* Wait for io events until next approx reconn check time */
485 io_waituntil2( 30*1000 ); 493 io_waituntil2(30 * 1000);
486 494
487 /* Loop over readable sockets */ 495 /* Loop over readable sockets */
488 while( ( sock = io_canread( ) ) != -1 ) { 496 while ((sock = io_canread()) != -1) {
489 const void *cookie = io_getcookie( sock ); 497 const void *cookie = io_getcookie(sock);
490 if( (uintptr_t)cookie == FLAG_SERVERSOCKET ) 498 if ((uintptr_t)cookie == FLAG_SERVERSOCKET)
491 handle_accept( sock ); 499 handle_accept(sock);
492 else 500 else
493 handle_read( sock ); 501 handle_read(sock);
494 } 502 }
495 503
496 /* Loop over writable sockets */ 504 /* Loop over writable sockets */
497 while( ( sock = io_canwrite( ) ) != -1 ) 505 while ((sock = io_canwrite()) != -1)
498 handle_write( sock ); 506 handle_write(sock);
499 507
500 livesync_ticker( ); 508 livesync_ticker();
501 } 509 }
502} 510}
503 511
504static void panic( const char *routine ) { 512static void panic(const char *routine) {
505 fprintf( stderr, "%s: %s\n", routine, strerror(errno) ); 513 fprintf(stderr, "%s: %s\n", routine, strerror(errno));
506 exit( 111 ); 514 exit(111);
507} 515}
508 516
509static int64_t ot_try_bind( ot_ip6 ip, uint16_t port ) { 517static int64_t ot_try_bind(ot_ip6 ip, uint16_t port) {
510 int64 sock = socket_tcp6( ); 518 int64_t sock = socket_tcp6();
511 519
512 if( socket_bind6_reuse( sock, ip, port, 0 ) == -1 ) 520 if (socket_bind6_reuse(sock, ip, port, 0) == -1)
513 panic( "socket_bind6_reuse" ); 521 panic("socket_bind6_reuse");
514 522
515 if( socket_listen( sock, SOMAXCONN) == -1 ) 523 if (socket_listen(sock, SOMAXCONN) == -1)
516 panic( "socket_listen" ); 524 panic("socket_listen");
517 525
518 if( !io_fd( sock ) ) 526 if (!io_fd(sock))
519 panic( "io_fd" ); 527 panic("io_fd");
520 528
521 io_setcookie( sock, (void*)FLAG_SERVERSOCKET ); 529 io_setcookie(sock, (void *)FLAG_SERVERSOCKET);
522 io_wantread( sock ); 530 io_wantread(sock);
523 return sock; 531 return sock;
524} 532}
525 533
526 534static int scan_ip6_port(const char *src, ot_ip6 ip, uint16_t *port) {
527static int scan_ip6_port( const char *src, ot_ip6 ip, uint16 *port ) {
528 const char *s = src; 535 const char *s = src;
529 int off, bracket = 0; 536 uint64_t value;
530 while( isspace(*s) ) ++s; 537 int off, bracket = 0;
531 if( *s == '[' ) ++s, ++bracket; /* for v6 style notation */ 538 while (isspace(*s))
532 if( !(off = scan_ip6( s, ip ) ) ) 539 ++s;
540 if (*s == '[')
541 ++s, ++bracket; /* for v6 style notation */
542 if (!(off = ip_scan(s, ip)))
533 return 0; 543 return 0;
534 s += off; 544 s += off;
535 if( *s == 0 || isspace(*s)) return s-src; 545 if (*s == 0 || isspace(*s))
536 if( *s == ']' && bracket ) ++s; 546 return s - src;
537 if( !ip6_isv4mapped(ip)){ 547 if (*s == ']' && bracket)
538 if( ( bracket && *(s) != ':' ) || ( *(s) != '.' ) ) return 0; 548 ++s;
549 if (!ip_is_v4mapped(ip)) {
550 if ((bracket && *(s) != ':') || (*(s) != '.'))
551 return 0;
539 s++; 552 s++;
540 } else { 553 } else {
541 if( *(s++) != ':' ) return 0; 554 if (*(s++) != ':')
555 return 0;
542 } 556 }
543 if( !(off = scan_ushort (s, port ) ) ) 557 if (!(off = scan_udec(s, &value, 65535)))
544 return 0; 558 return 0;
545 return off+s-src; 559 *port = (uint16_t)value;
560 return off + s - src;
546} 561}
547 562
548int main( int argc, char **argv ) { 563int main(int argc, char **argv) {
549 static pthread_t sync_in_thread_id; 564 static pthread_t sync_in_thread_id;
550 static pthread_t sync_out_thread_id; 565 static pthread_t sync_out_thread_id;
551 ot_ip6 serverip; 566 ot_ip6 serverip;
552 uint16_t tmpport; 567 uint16_t tmpport;
553 int scanon = 1, lbound = 0, sbound = 0; 568 int scanon = 1, lbound = 0, sbound = 0;
554 569
555 srandom( time(NULL) ); 570 random_init();
556#ifdef WANT_ARC4RANDOM 571 g_tracker_id = random_uint32();
557 g_tracker_id = arc4random();
558#else
559 g_tracker_id = random();
560#endif
561 noipv6=1;
562 572
563 while( scanon ) { 573 while (scanon) {
564 switch( getopt( argc, argv, ":l:c:L:h" ) ) { 574 switch (getopt(argc, argv, ":l:c:L:h")) {
565 case -1: scanon = 0; break; 575 case -1:
576 scanon = 0;
577 break;
566 case 'l': 578 case 'l':
567 tmpport = 0; 579 tmpport = 0;
568 if( !scan_ip6_port( optarg, serverip, &tmpport ) || !tmpport ) { usage( argv[0] ); exit( 1 ); } 580 if (!scan_ip6_port(optarg, serverip, &tmpport) || !tmpport) {
569 ot_try_bind( serverip, tmpport ); 581 usage(argv[0]);
582 exit(1);
583 }
584 ot_try_bind(serverip, tmpport);
570 ++sbound; 585 ++sbound;
571 break; 586 break;
572 case 'c': 587 case 'c':
573 if( g_connection_count > MAX_PEERS / 2 ) exerr( "Connection limit exceeded.\n" ); 588 if (g_connection_count > MAX_PEERS / 2)
589 exerr("Connection limit exceeded.\n");
574 tmpport = 0; 590 tmpport = 0;
575 if( !scan_ip6_port( optarg, 591 if (!scan_ip6_port(optarg, g_connections[g_connection_count].ip, &g_connections[g_connection_count].port) || !g_connections[g_connection_count].port) {
576 g_connections[g_connection_count].ip, 592 usage(argv[0]);
577 &g_connections[g_connection_count].port ) || 593 exit(1);
578 !g_connections[g_connection_count].port ) { usage( argv[0] ); exit( 1 ); } 594 }
579 g_connections[g_connection_count++].state = FLAG_OUTGOING; 595 g_connections[g_connection_count++].state = FLAG_OUTGOING;
580 break; 596 break;
581 case 'L': 597 case 'L':
582 tmpport = 9696; 598 tmpport = 9696;
583 if( !scan_ip6_port( optarg, serverip, &tmpport ) || !tmpport ) { usage( argv[0] ); exit( 1 ); } 599 if (!scan_ip6_port(optarg, serverip, &tmpport) || !tmpport) {
584 livesync_bind_mcast( serverip, tmpport); ++lbound; break; 600 usage(argv[0]);
601 exit(1);
602 }
603 livesync_bind_mcast(serverip, tmpport);
604 ++lbound;
605 break;
585 default: 606 default:
586 case '?': usage( argv[0] ); exit( 1 ); 607 case '?':
608 usage(argv[0]);
609 exit(1);
587 } 610 }
588 } 611 }
589 612
590 if( !lbound ) exerr( "No livesync port bound." ); 613 if (!lbound)
591 if( !g_connection_count && !sbound ) exerr( "No streamsync port bound." ); 614 exerr("No livesync port bound.");
592 pthread_create( &sync_in_thread_id, NULL, livesync_worker, NULL ); 615 if (!g_connection_count && !sbound)
593 pthread_create( &sync_out_thread_id, NULL, streamsync_worker, NULL ); 616 exerr("No streamsync port bound.");
617 pthread_create(&sync_in_thread_id, NULL, livesync_worker, NULL);
618 pthread_create(&sync_out_thread_id, NULL, streamsync_worker, NULL);
594 619
595 server_mainloop(); 620 server_mainloop();
596 return 0; 621 return 0;
597} 622}
598 623
599static void * streamsync_worker( void * args ) { 624static void *streamsync_worker(void *args) {
600 (void)args; 625 (void)args;
601 while( 1 ) { 626 while (1) {
602 int bucket; 627 size_t bucket;
603 /* For each bucket... */ 628 /* For each bucket... */
604 for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) { 629 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
605 /* Get exclusive access to that bucket */ 630 /* Get exclusive access to that bucket */
606 ot_vector *torrents_list = mutex_bucket_lock( bucket ); 631 ot_vector *torrents_list = mutex_bucket_lock(bucket);
607 size_t tor_offset, count_def = 0, count_one = 0, count_two = 0, count_peers = 0; 632 size_t tor_offset, count_def = 0, count_one = 0, count_two = 0, count_peers = 0;
608 size_t mem, mem_a = 0, mem_b = 0; 633 size_t mem, mem_a = 0, mem_b = 0;
609 uint8_t *ptr = 0, *ptr_a, *ptr_b, *ptr_c; 634 uint8_t *ptr = 0, *ptr_a, *ptr_b, *ptr_c;
610 635
611 if( !torrents_list->size ) goto unlock_continue; 636 if (!torrents_list->size)
637 goto unlock_continue;
612 638
613 /* For each torrent in this bucket.. */ 639 /* For each torrent in this bucket.. */
614 for( tor_offset=0; tor_offset<torrents_list->size; ++tor_offset ) { 640 for (tor_offset = 0; tor_offset < torrents_list->size; ++tor_offset) {
615 /* Address torrents members */ 641 /* Address torrents members */
616 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[tor_offset] ).peer_list; 642 ot_peerlist *peer_list = (((ot_torrent *)(torrents_list->data))[tor_offset]).peer_list;
617 switch( peer_list->peer_count ) { 643 switch (peer_list->peer_count) {
618 case 2: count_two++; break; 644 case 2:
619 case 1: count_one++; break; 645 count_two++;
620 case 0: break; 646 break;
621 default: count_def++; 647 case 1:
622 count_peers += peer_list->peer_count; 648 count_one++;
649 break;
650 case 0:
651 break;
652 default:
653 count_def++;
654 count_peers += peer_list->peer_count;
623 } 655 }
624 } 656 }
625 657
626 /* Maximal memory requirement: max 3 blocks, max torrents * 20 + max peers * 7 */ 658 /* Maximal memory requirement: max 3 blocks, max torrents * 20 + max peers * 7 */
627 mem = 3 * ( 1 + 1 + 2 ) + ( count_one + count_two ) * ( 19 + 1 ) + count_def * ( 19 + 8 ) + 659 mem = 3 * (1 + 1 + 2) + (count_one + count_two) * (19 + 1) + count_def * (19 + 8) + (count_one + 2 * count_two + count_peers) * 7;
628 ( count_one + 2 * count_two + count_peers ) * 7;
629 660
630 fprintf( stderr, "Mem: %zd\n", mem ); 661 fprintf(stderr, "Mem: %zd\n", mem);
631 662
632 ptr = ptr_a = ptr_b = ptr_c = malloc( mem ); 663 ptr = ptr_a = ptr_b = ptr_c = malloc(mem);
633 if( !ptr ) goto unlock_continue; 664 if (!ptr)
665 goto unlock_continue;
634 666
635 if( count_one > 4 || !count_def ) { 667 if (count_one > 4 || !count_def) {
636 mem_a = 1 + 1 + 2 + count_one * ( 19 + 7 ); 668 mem_a = 1 + 1 + 2 + count_one * (19 + 7);
637 ptr_b += mem_a; ptr_c += mem_a; 669 ptr_b += mem_a;
638 ptr_a[0] = 1; /* Offset 0: packet type 1 */ 670 ptr_c += mem_a;
639 ptr_a[1] = (bucket << 8) >> OT_BUCKET_COUNT_BITS; /* Offset 1: the shared prefix */ 671 ptr_a[0] = 1; /* Offset 0: packet type 1 */
640 ptr_a[2] = count_one >> 8; 672 ptr_a[1] = (bucket << 8) >> OT_BUCKET_COUNT_BITS; /* Offset 1: the shared prefix */
641 ptr_a[3] = count_one & 255; 673 ptr_a[2] = count_one >> 8;
642 ptr_a += 4; 674 ptr_a[3] = count_one & 255;
675 ptr_a += 4;
643 } else 676 } else
644 count_def += count_one; 677 count_def += count_one;
645 678
646 if( count_two > 4 || !count_def ) { 679 if (count_two > 4 || !count_def) {
647 mem_b = 1 + 1 + 2 + count_two * ( 19 + 14 ); 680 mem_b = 1 + 1 + 2 + count_two * (19 + 14);
648 ptr_c += mem_b; 681 ptr_c += mem_b;
649 ptr_b[0] = 2; /* Offset 0: packet type 2 */ 682 ptr_b[0] = 2; /* Offset 0: packet type 2 */
650 ptr_b[1] = (bucket << 8) >> OT_BUCKET_COUNT_BITS; /* Offset 1: the shared prefix */ 683 ptr_b[1] = (bucket << 8) >> OT_BUCKET_COUNT_BITS; /* Offset 1: the shared prefix */
651 ptr_b[2] = count_two >> 8; 684 ptr_b[2] = count_two >> 8;
652 ptr_b[3] = count_two & 255; 685 ptr_b[3] = count_two & 255;
653 ptr_b += 4; 686 ptr_b += 4;
654 } else 687 } else
655 count_def += count_two; 688 count_def += count_two;
656 689
657 if( count_def ) { 690 if (count_def) {
658 ptr_c[0] = 0; /* Offset 0: packet type 0 */ 691 ptr_c[0] = 0; /* Offset 0: packet type 0 */
659 ptr_c[1] = (bucket << 8) >> OT_BUCKET_COUNT_BITS; /* Offset 1: the shared prefix */ 692 ptr_c[1] = (bucket << 8) >> OT_BUCKET_COUNT_BITS; /* Offset 1: the shared prefix */
660 ptr_c[2] = count_def >> 8; 693 ptr_c[2] = count_def >> 8;
661 ptr_c[3] = count_def & 255; 694 ptr_c[3] = count_def & 255;
662 ptr_c += 4; 695 ptr_c += 4;
663 } 696 }
664 697
665 /* For each torrent in this bucket.. */ 698 /* For each torrent in this bucket.. */
666 for( tor_offset=0; tor_offset<torrents_list->size; ++tor_offset ) { 699 for (tor_offset = 0; tor_offset < torrents_list->size; ++tor_offset) {
667 /* Address torrents members */ 700 /* Address torrents members */
668 ot_torrent *torrent = ((ot_torrent*)(torrents_list->data)) + tor_offset; 701 ot_torrent *torrent = ((ot_torrent *)(torrents_list->data)) + tor_offset;
669 ot_peerlist *peer_list = torrent->peer_list; 702 ot_peerlist *peer_list = torrent->peer_list;
670 ot_peer *peers = (ot_peer*)(peer_list->peers.data); 703 ot_peer *peers = (ot_peer *)(peer_list->peers.data);
671 uint8_t **dst; 704 uint8_t **dst;
672 705
673 /* Determine destination slot */ 706 /* Determine destination slot */
674 count_peers = peer_list->peer_count; 707 count_peers = peer_list->peer_count;
675 switch( count_peers ) { 708 switch (count_peers) {
676 case 0: continue; 709 case 0:
677 case 1: dst = mem_a ? &ptr_a : &ptr_c; break; 710 continue;
678 case 2: dst = mem_b ? &ptr_b : &ptr_c; break; 711 case 1:
679 default: dst = &ptr_c; break; 712 dst = mem_a ? &ptr_a : &ptr_c;
713 break;
714 case 2:
715 dst = mem_b ? &ptr_b : &ptr_c;
716 break;
717 default:
718 dst = &ptr_c;
719 break;
680 } 720 }
681 721
682 /* Copy tail of info_hash, advance pointer */ 722 /* Copy tail of info_hash, advance pointer */
683 memcpy( *dst, ((uint8_t*)torrent->hash) + 1, sizeof( ot_hash ) - 1); 723 memcpy(*dst, ((uint8_t *)torrent->hash) + 1, sizeof(ot_hash) - 1);
684 *dst += sizeof( ot_hash ) - 1; 724 *dst += sizeof(ot_hash) - 1;
685 725
686 /* Encode peer count */ 726 /* Encode peer count */
687 if( dst == &ptr_c ) 727 if (dst == &ptr_c)
688 while( count_peers ) { 728 while (count_peers) {
689 if( count_peers <= 0x7f ) 729 if (count_peers <= 0x7f)
690 *(*dst)++ = count_peers; 730 *(*dst)++ = count_peers;
691 else 731 else
692 *(*dst)++ = 0x80 | ( count_peers & 0x7f ); 732 *(*dst)++ = 0x80 | (count_peers & 0x7f);
693 count_peers >>= 7; 733 count_peers >>= 7;
694 } 734 }
695 735
696 /* Copy peers */ 736 /* Copy peers */
697 count_peers = peer_list->peer_count; 737 count_peers = peer_list->peer_count;
698 while( count_peers-- ) { 738 while (count_peers--) {
699 memcpy( *dst, peers++, OT_IP_SIZE + 3 ); 739 memcpy(*dst, peers++, OT_IP_SIZE + 3);
700 *dst += OT_IP_SIZE + 3; 740 *dst += OT_IP_SIZE + 3;
701 } 741 }
702 free_peerlist(peer_list); 742 free_peerlist(peer_list);
703 } 743 }
704 744
705 free( torrents_list->data ); 745 free(torrents_list->data);
706 memset( torrents_list, 0, sizeof(*torrents_list ) ); 746 memset(torrents_list, 0, sizeof(*torrents_list));
707unlock_continue: 747 unlock_continue:
708 mutex_bucket_unlock( bucket, 0 ); 748 mutex_bucket_unlock(bucket, 0);
709 749
710 if( ptr ) { 750 if (ptr) {
711 int i; 751 int i;
712 752
713 if( ptr_b > ptr_c ) ptr_c = ptr_b; 753 if (ptr_b > ptr_c)
714 if( ptr_a > ptr_c ) ptr_c = ptr_a; 754 ptr_c = ptr_b;
755 if (ptr_a > ptr_c)
756 ptr_c = ptr_a;
715 mem = ptr_c - ptr; 757 mem = ptr_c - ptr;
716 758
717 for( i=0; i < MAX_PEERS; ++i ) { 759 for (i = 0; i < MAX_PEERS; ++i) {
718 if( PROXYPEER_ISCONNECTED(g_connections[i].state) ) { 760 if (PROXYPEER_ISCONNECTED(g_connections[i].state)) {
719 void *tmp = malloc( mem ); 761 void *tmp = malloc(mem);
720 if( tmp ) { 762 if (tmp) {
721 memcpy( tmp, ptr, mem ); 763 memcpy(tmp, ptr, mem);
722 iob_addbuf_free( &g_connections[i].outdata, tmp, mem ); 764 iob_addbuf_free(&g_connections[i].outdata, tmp, mem);
723 io_wantwrite( g_connections[i].fd ); 765 io_wantwrite(g_connections[i].fd);
724 } 766 }
725 } 767 }
726 } 768 }
727 769
728 free( ptr ); 770 free(ptr);
729 } 771 }
730 usleep( OT_SYNC_SLEEP ); 772 usleep(OT_SYNC_SLEEP);
731 } 773 }
732 } 774 }
733 return 0; 775 return 0;
734} 776}
735 777
736static void livesync_issue_peersync( ) { 778static void livesync_issue_peersync() {
737 socket_send4(g_socket_out, (char*)g_peerbuffer_start, g_peerbuffer_pos - g_peerbuffer_start, 779 socket_send4(g_socket_out, (char *)g_peerbuffer_start, g_peerbuffer_pos - g_peerbuffer_start, groupip_1, LIVESYNC_PORT);
738 groupip_1, LIVESYNC_PORT); 780 g_peerbuffer_pos = g_peerbuffer_start + sizeof(g_tracker_id) + sizeof(uint32_t);
739 g_peerbuffer_pos = g_peerbuffer_start + sizeof( g_tracker_id ) + sizeof( uint32_t );
740 g_next_packet_time = time(NULL) + LIVESYNC_MAXDELAY; 781 g_next_packet_time = time(NULL) + LIVESYNC_MAXDELAY;
741} 782}
742 783
743void livesync_ticker( ) { 784void livesync_ticker() {
744 /* livesync_issue_peersync sets g_next_packet_time */ 785 /* livesync_issue_peersync sets g_next_packet_time */
745 if( time(NULL) > g_next_packet_time && 786 if (time(NULL) > g_next_packet_time && g_peerbuffer_pos > g_peerbuffer_start + sizeof(g_tracker_id))
746 g_peerbuffer_pos > g_peerbuffer_start + sizeof( g_tracker_id ) )
747 livesync_issue_peersync(); 787 livesync_issue_peersync();
748} 788}
749 789
750static void livesync_proxytell( uint8_t prefix, uint8_t *info_hash, uint8_t *peer ) { 790static void livesync_proxytell(uint8_t prefix, uint8_t *info_hash, uint8_t *peer) {
751// unsigned int i; 791 // unsigned int i;
752 792
753 *g_peerbuffer_pos = prefix; 793 *g_peerbuffer_pos = prefix;
754 memcpy( g_peerbuffer_pos + 1, info_hash, sizeof(ot_hash) - 1 ); 794 memcpy(g_peerbuffer_pos + 1, info_hash, sizeof(ot_hash) - 1);
755 memcpy( g_peerbuffer_pos + sizeof(ot_hash), peer, sizeof(ot_peer) - 1 ); 795 memcpy(g_peerbuffer_pos + sizeof(ot_hash), peer, sizeof(ot_peer) - 1);
756 796
757#if 0 797#if 0
758 /* Dump info_hash */ 798 /* Dump info_hash */
@@ -767,80 +807,90 @@ static void livesync_proxytell( uint8_t prefix, uint8_t *info_hash, uint8_t *pee
767#endif 807#endif
768 g_peerbuffer_pos += sizeof(ot_peer); 808 g_peerbuffer_pos += sizeof(ot_peer);
769 809
770 if( g_peerbuffer_pos >= g_peerbuffer_highwater ) 810 if (g_peerbuffer_pos >= g_peerbuffer_highwater)
771 livesync_issue_peersync(); 811 livesync_issue_peersync();
772} 812}
773 813
774static void process_indata( proxy_peer * peer ) { 814static void process_indata(proxy_peer *peer) {
775 size_t consumed, peers; 815 size_t consumed, peers;
776 uint8_t *data = peer->indata, *hash; 816 uint8_t *data = peer->indata, *hash;
777 uint8_t *dataend = data + peer->indata_length; 817 uint8_t *dataend = data + peer->indata_length;
778 818
779 while( 1 ) { 819 while (1) {
780 /* If we're not inside of a packet, make a new one */ 820 /* If we're not inside of a packet, make a new one */
781 if( !peer->packet_tcount ) { 821 if (!peer->packet_tcount) {
782 /* Ensure the header is complete or postpone processing */ 822 /* Ensure the header is complete or postpone processing */
783 if( data + 4 > dataend ) break; 823 if (data + 4 > dataend)
784 peer->packet_type = data[0]; 824 break;
785 peer->packet_tprefix = data[1]; 825 peer->packet_type = data[0];
786 peer->packet_tcount = data[2] * 256 + data[3]; 826 peer->packet_tprefix = data[1];
787 data += 4; 827 peer->packet_tcount = data[2] * 256 + data[3];
788printf( "type: %hhu, prefix: %02X, torrentcount: %zd\n", peer->packet_type, peer->packet_tprefix, peer->packet_tcount ); 828 data += 4;
829 printf("type: %hhu, prefix: %02X, torrentcount: %zd\n", peer->packet_type, peer->packet_tprefix, peer->packet_tcount);
789 } 830 }
790 831
791 /* Ensure size for a minimal torrent block */ 832 /* Ensure size for a minimal torrent block */
792 if( data + sizeof(ot_hash) + OT_IP_SIZE + 3 > dataend ) break; 833 if (data + sizeof(ot_hash) + OT_IP_SIZE + 3 > dataend)
834 break;
793 835
794 /* Advance pointer to peer count or peers */ 836 /* Advance pointer to peer count or peers */
795 hash = data; 837 hash = data;
796 data += sizeof(ot_hash) - 1; 838 data += sizeof(ot_hash) - 1;
797 839
798 /* Type 0 has peer count encoded before each peers */ 840 /* Type 0 has peer count encoded before each peers */
799 peers = peer->packet_type; 841 peers = peer->packet_type;
800 if( !peers ) { 842 if (!peers) {
801 int shift = 0; 843 int shift = 0;
802 do peers |= ( 0x7f & *data ) << ( 7 * shift ); 844 do
803 while ( *(data++) & 0x80 && shift++ < 6 ); 845 peers |= (0x7f & *data) << (7 * shift);
846 while (*(data++) & 0x80 && shift++ < 6);
804 } 847 }
805#if 0 848#if 0
806printf( "peers: %zd\n", peers ); 849printf( "peers: %zd\n", peers );
807#endif 850#endif
808 /* Ensure enough data being read to hold all peers */ 851 /* Ensure enough data being read to hold all peers */
809 if( data + (OT_IP_SIZE + 3) * peers > dataend ) { 852 if (data + (OT_IP_SIZE + 3) * peers > dataend) {
810 data = hash; 853 data = hash;
811 break; 854 break;
812 } 855 }
813 while( peers-- ) { 856 while (peers--) {
814 livesync_proxytell( peer->packet_tprefix, hash, data ); 857 livesync_proxytell(peer->packet_tprefix, hash, data);
815 data += OT_IP_SIZE + 3; 858 data += OT_IP_SIZE + 3;
816 } 859 }
817 --peer->packet_tcount; 860 --peer->packet_tcount;
818 } 861 }
819 862
820 consumed = data - peer->indata; 863 consumed = data - peer->indata;
821 memmove( peer->indata, data, peer->indata_length - consumed ); 864 memmove(peer->indata, data, peer->indata_length - consumed);
822 peer->indata_length -= consumed; 865 peer->indata_length -= consumed;
823} 866}
824 867
825static void * livesync_worker( void * args ) { 868static void *livesync_worker(void *args) {
826 (void)args; 869 (void)args;
827 while( 1 ) { 870 while (1) {
828 ot_ip6 in_ip; uint16_t in_port; 871 ot_ip6 in_ip;
829 size_t datalen = socket_recv4(g_socket_in, (char*)g_inbuffer, LIVESYNC_INCOMING_BUFFSIZE, 12+(char*)in_ip, &in_port); 872 uint16_t in_port;
873 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);
830 875
831 /* Expect at least tracker id and packet type */ 876 /* Expect at least tracker id and packet type */
832 if( datalen <= (ssize_t)(sizeof( g_tracker_id ) + sizeof( uint32_t )) ) 877 if (datalen <= (ssize_t)(sizeof(g_tracker_id) + sizeof(uint32_t)))
833 continue; 878 continue;
834 if( !memcmp( g_inbuffer, &g_tracker_id, sizeof( g_tracker_id ) ) ) { 879 if (!memcmp(g_inbuffer, &g_tracker_id, sizeof(g_tracker_id))) {
835 /* drop packet coming from ourselves */ 880 /* drop packet coming from ourselves */
836 continue; 881 continue;
837 } 882 }
838 switch( uint32_read_big( sizeof( g_tracker_id ) + (char*)g_inbuffer ) ) { 883 memcpy(&packet_type, g_inbuffer + sizeof(g_tracker_id), sizeof(packet_type));
839 case OT_SYNC_PEER: 884
840 livesync_handle_peersync( datalen ); 885 switch (ntohl(packet_type)) {
886 case OT_SYNC_PEER4:
887 livesync_handle_peersync(datalen, OT_PEER_SIZE4);
888 break;
889 case OT_SYNC_PEER6:
890 livesync_handle_peersync(datalen, OT_PEER_SIZE6);
841 break; 891 break;
842 default: 892 default:
843 // fprintf( stderr, "Received an unknown live sync packet type %u.\n", uint32_read_big( sizeof( g_tracker_id ) + (char*)g_inbuffer ) ); 893 // fprintf( stderr, "Received an unknown live sync packet type %u.\n", ntohl( packet_type ) );
844 break; 894 break;
845 } 895 }
846 } 896 }
diff --git a/scan_urlencoded_query.c b/scan_urlencoded_query.c
index a4f89c2..cf0c5b1 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
@@ -23,7 +20,7 @@
23*/ 20*/
24 21
25/* This matrix holds for each ascii character the information, 22/* This matrix holds for each ascii character the information,
26 whether it is a non-terminating character for on of the three 23 whether it is a non-terminating character for one of the three
27 scan states we are in, that is 'path', 'param' and 'value' from 24 scan states we are in, that is 'path', 'param' and 'value' from
28 /path?param=value&param=value, it is encoded in bit 0, 1 and 2 25 /path?param=value&param=value, it is encoded in bit 0, 1 and 2
29 respectively 26 respectively
@@ -45,37 +42,45 @@ static const unsigned char is_unreserved[256] = {
45 42
46/* Do a fast nibble to hex representation conversion */ 43/* Do a fast nibble to hex representation conversion */
47static unsigned char fromhex(unsigned char x) { 44static unsigned char fromhex(unsigned char x) {
48 x-='0'; if( x<=9) return x; 45 x -= '0';
49 x&=~0x20; x-='A'-'0'; 46 if (x <= 9)
50 if( x<6 ) return x+10; 47 return x;
48 x &= ~0x20;
49 x -= 'A' - '0';
50 if (x < 6)
51 return x + 10;
51 return 0xff; 52 return 0xff;
52} 53}
53 54
54/* Skip the value of a param=value pair */ 55/* Skip the value of a param=value pair */
55void scan_urlencoded_skipvalue( char **string ) { 56void scan_urlencoded_skipvalue(char **string) {
56 const unsigned char* s=*(const unsigned char**) string; 57 const unsigned char *s = *(const unsigned char **)string;
57 unsigned char f; 58 unsigned char f;
58 59
59 /* Since we are asked to skip the 'value', we assume to stop at 60 /* Since we are asked to skip the 'value', we assume to stop at
60 terminators for a 'value' string position */ 61 terminators for a 'value' string position */
61 while( ( f = is_unreserved[ *s++ ] ) & SCAN_SEARCHPATH_VALUE ); 62 while ((f = is_unreserved[*s++]) & SCAN_SEARCHPATH_VALUE)
63 ;
62 64
63 /* If we stopped at a hard terminator like \0 or \n, make the 65 /* If we stopped at a hard terminator like \0 or \n, make the
64 next scan_urlencoded_query encounter it again */ 66 next scan_urlencoded_query encounter it again */
65 if( f & SCAN_SEARCHPATH_TERMINATOR ) --s; 67 if (f & SCAN_SEARCHPATH_TERMINATOR)
68 --s;
66 69
67 *string = (char*)s; 70 *string = (char *)s;
68} 71}
69 72
70int scan_find_keywords( const ot_keywords * keywords, char **string, SCAN_SEARCHPATH_FLAG flags) { 73int scan_find_keywords(const ot_keywords *keywords, char **string, SCAN_SEARCHPATH_FLAG flags) {
71 char *deststring = *string; 74 char *deststring = *string;
72 ssize_t match_length = scan_urlencoded_query(string, deststring, flags ); 75 ssize_t match_length = scan_urlencoded_query(string, deststring, flags);
73 76
74 if( match_length < 0 ) return match_length; 77 if (match_length < 0)
75 if( match_length == 0 ) return -3; 78 return match_length;
79 if (match_length == 0)
80 return -3;
76 81
77 while( keywords->key ) { 82 while (keywords->key) {
78 if( !strncmp( keywords->key, deststring, match_length ) && !keywords->key[match_length] ) 83 if (!strncmp(keywords->key, deststring, match_length) && !keywords->key[match_length])
79 return keywords->value; 84 return keywords->value;
80 keywords++; 85 keywords++;
81 } 86 }
@@ -84,60 +89,99 @@ int scan_find_keywords( const ot_keywords * keywords, char **string, SCAN_SEARCH
84} 89}
85 90
86ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags) { 91ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags) {
87 const unsigned char* s=*(const unsigned char**) string; 92 const unsigned char *s = *(const unsigned char **)string;
88 unsigned char *d = (unsigned char*)deststring; 93 unsigned char *d = (unsigned char *)deststring;
89 unsigned char b, c; 94 unsigned char b, c;
90 95
91 /* This is the main decoding loop. 96 /* This is the main decoding loop.
92 'flag' determines, which characters are non-terminating in current context 97 'flag' determines, which characters are non-terminating in current context
93 (ie. stop at '=' and '&' if scanning for a 'param'; stop at '?' if scanning for the path ) 98 (ie. stop at '=' and '&' if scanning for a 'param'; stop at '?' if scanning for the path )
94 */ 99 */
95 while( is_unreserved[ c = *s++ ] & flags ) { 100 while (is_unreserved[c = *s++] & flags) {
96 101
97 /* When encountering an url escaped character, try to decode */ 102 /* When encountering an url escaped character, try to decode */
98 if( c=='%') { 103 if (c == '%') {
99 if( ( b = fromhex(*s++) ) == 0xff ) return -1; 104 if ((b = fromhex(*s++)) == 0xff)
100 if( ( c = fromhex(*s++) ) == 0xff ) return -1; 105 return -1;
101 c|=(b<<4); 106 if ((c = fromhex(*s++)) == 0xff)
107 return -1;
108 c |= (b << 4);
102 } 109 }
103 110
104 /* Write (possibly decoded) character to output */ 111 /* Write (possibly decoded) character to output */
105 *d++ = c; 112 *d++ = c;
106 } 113 }
107 114
108 switch( c ) { 115 switch (c) {
109 case 0: case '\r': case '\n': case ' ': 116 case 0:
117 case '\r':
118 case '\n':
119 case ' ':
110 /* If we started scanning on a hard terminator, indicate we've finished */ 120 /* If we started scanning on a hard terminator, indicate we've finished */
111 if( d == (unsigned char*)deststring ) return -2; 121 if (d == (unsigned char *)deststring)
122 return -2;
112 123
113 /* Else make the next call to scan_urlencoded_param encounter it again */ 124 /* Else make the next call to scan_urlencoded_param encounter it again */
114 --s; 125 --s;
115 break; 126 break;
116 case '?': 127 case '?':
117 if( flags != SCAN_PATH ) return -1; 128 if (flags != SCAN_PATH)
129 return -1;
118 break; 130 break;
119 case '=': 131 case '=':
120 if( flags != SCAN_SEARCHPATH_PARAM ) return -1; 132 if (flags != SCAN_SEARCHPATH_PARAM)
133 return -1;
121 break; 134 break;
122 case '&': 135 case '&':
123 if( flags == SCAN_PATH ) return -1; 136 if (flags == SCAN_PATH)
124 if( flags == SCAN_SEARCHPATH_PARAM ) --s; 137 return -1;
138 if (flags == SCAN_SEARCHPATH_PARAM)
139 --s;
125 break; 140 break;
126 default: 141 default:
127 return -1; 142 return -1;
128 } 143 }
129 144
130 *string = (char *)s; 145 *string = (char *)s;
131 return d - (unsigned char*)deststring; 146 return d - (unsigned char *)deststring;
132} 147}
133 148
134ssize_t scan_fixed_int( char *data, size_t len, int *tmp ) { 149ssize_t scan_fixed_int(char *data, size_t len, int *tmp) {
135 int minus = 0; 150 int minus = 0;
136 *tmp = 0; 151 *tmp = 0;
137 if( *data == '-' ) --len, ++data, ++minus; 152 if (*data == '-')
138 while( (len > 0) && (*data >= '0') && (*data <= '9') ) { --len; *tmp = 10**tmp + *data++-'0'; } 153 --len, ++data, ++minus;
139 if( minus ) *tmp = -*tmp; 154 while ((len > 0) && (*data >= '0') && (*data <= '9')) {
155 --len;
156 *tmp = 10 * *tmp + *data++ - '0';
157 }
158 if (minus)
159 *tmp = -*tmp;
140 return len; 160 return len;
141} 161}
142 162
143const char *g_version_scan_urlencoded_query_c = "$Source$: $Revision$\n"; 163size_t fmt_urlencoded(char *dest, const uint8_t *src, size_t len) {
164 static const char safe[] = "$/.=~-_";
165 static const char hexdigit[] = "0123456789abcdef";
166 size_t written = 0, i;
167
168 for (i = 0; i < len; ++i) {
169 uint8_t c = src[i];
170
171 /* strchr would find the terminator, so a zero byte never counts as safe */
172 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c && strchr(safe, c))) {
173 if (dest)
174 dest[written] = (char)c;
175 ++written;
176 } else {
177 if (dest) {
178 dest[written] = '%';
179 dest[written + 1] = hexdigit[c >> 4];
180 dest[written + 2] = hexdigit[c & 15];
181 }
182 written += 3;
183 }
184 }
185
186 return written;
187}
diff --git a/scan_urlencoded_query.h b/scan_urlencoded_query.h
index 06b91f5..6f12c03 100644
--- a/scan_urlencoded_query.h
+++ b/scan_urlencoded_query.h
@@ -6,6 +6,8 @@
6#ifndef SCAN_URLENCODED_QUERY_H__ 6#ifndef SCAN_URLENCODED_QUERY_H__
7#define SCAN_URLENCODED_QUERY_H__ 7#define SCAN_URLENCODED_QUERY_H__
8 8
9#include <stddef.h>
10#include <stdint.h>
9#include <sys/types.h> 11#include <sys/types.h>
10 12
11typedef struct { 13typedef struct {
@@ -38,18 +40,24 @@ ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_F
38 or -2 for terminator found 40 or -2 for terminator found
39 or -3 for no keyword matched 41 or -3 for no keyword matched
40 */ 42 */
41int scan_find_keywords( const ot_keywords * keywords, char **string, SCAN_SEARCHPATH_FLAG flags); 43int scan_find_keywords(const ot_keywords *keywords, char **string, SCAN_SEARCHPATH_FLAG flags);
42 44
43/* string in: pointer to value of a param=value pair to skip 45/* string in: pointer to value of a param=value pair to skip
44 out: pointer to next scan position on return 46 out: pointer to next scan position on return
45*/ 47*/
46void scan_urlencoded_skipvalue( char **string ); 48void scan_urlencoded_skipvalue(char **string);
47 49
48/* data pointer to len chars of string 50/* data pointer to len chars of string
49 len length of chars in data to parse 51 len length of chars in data to parse
50 number number to receive result 52 number number to receive result
51 returns number of bytes not parsed, mostly !=0 means fail 53 returns number of bytes not parsed, mostly !=0 means fail
52 */ 54 */
53ssize_t scan_fixed_int( char *data, size_t len, int *number ); 55ssize_t scan_fixed_int(char *data, size_t len, int *number);
56
57/* The other direction: percent encode len bytes from src into dest and return
58 how many characters were written, or how many would be needed when dest is
59 NULL. dest needs room for three times len.
60 */
61size_t fmt_urlencoded(char *dest, const uint8_t *src, size_t len);
54 62
55#endif 63#endif
diff --git a/tests/runner.c b/tests/runner.c
new file mode 100644
index 0000000..10b6625
--- /dev/null
+++ b/tests/runner.c
@@ -0,0 +1,40 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <string.h>
8
9/* Opentracker */
10#include "test.h"
11
12struct module {
13 const char *name;
14 void (*run)(void);
15};
16
17static const struct module modules[] = {{"scan_urlencoded_query", test_scan_urlencoded_query},
18 {"ot_iovec", test_iovec},
19 {"ot_vector", test_vector},
20 {"ot_random", test_random},
21 {"ot_hex", test_hex},
22 {"ot_scan", test_scan},
23 {"ot_buf", test_buf},
24 {"ot_ip", test_ip},
25 {"ot_accesslist", test_accesslist},
26 {NULL, NULL}};
27
28/* Run every module, or only those whose name contains argv[1] */
29int main(int argc, char **argv) {
30 const struct module *m;
31
32 for (m = modules; m->name; ++m) {
33 if (argc > 1 && !strstr(m->name, argv[1]))
34 continue;
35 test_module(m->name);
36 m->run();
37 }
38
39 return test_summary();
40}
diff --git a/tests/stubs.c b/tests/stubs.c
new file mode 100644
index 0000000..ce93071
--- /dev/null
+++ b/tests/stubs.c
@@ -0,0 +1,20 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* Stubs for symbols the modules under test reference but never reach along
7 the paths the tests exercise. If a test ever does reach one, the missing
8 behaviour should be supplied here rather than the test weakened. */
9
10/* Opentracker */
11#include "trackerlogic.h"
12
13/* ot_vector.c calls this from vector_clean_list, which no test drives yet. */
14void free_peerlist(ot_peerlist *peer_list) { (void)peer_list; }
15
16/* ot_accesslist.c is built with WANT_ACCESSLIST_BLACK so that the hash parser
17 is reachable. These two belong to accesslist_readfile and the reload worker,
18 neither of which any test drives. */
19time_t g_now_seconds;
20volatile int g_opentracker_running;
diff --git a/tests/t_accesslist.c b/tests/t_accesslist.c
new file mode 100644
index 0000000..9925acd
--- /dev/null
+++ b/tests/t_accesslist.c
@@ -0,0 +1,122 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <stdlib.h>
8#include <string.h>
9
10/* Opentracker */
11#include "ot_accesslist.h"
12#include "test.h"
13
14#define A "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
15#define B "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
16#define C "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
17
18static ot_hash dest[8];
19
20/* The parser is handed a mapped file, so it must never look at a byte beyond
21 the length it was given. These cases place a hex digit immediately after
22 that length: a parser that reads one byte too far sees it, decides the hash
23 runs on, and silently drops the last entry. */
24
25static void case_empty(void) {
26 test_case("an empty buffer yields nothing");
27 CHECK_INT(accesslist_parse_hashes(dest, 8, "", 0), 0);
28}
29
30static void case_single_with_newline(void) {
31 const char buf[] = A "\n";
32
33 test_case("one hash followed by a newline");
34 CHECK_INT(accesslist_parse_hashes(dest, 8, buf, 41), 1);
35}
36
37static void case_single_without_newline(void) {
38 const char buf[] = A "A";
39
40 test_case("one hash ending the buffer, with a hex digit just past the end");
41 CHECK_INT(accesslist_parse_hashes(dest, 8, buf, 40), 1);
42}
43
44static void case_several_last_without_newline(void) {
45 const char buf[] = A "\n" B "\n" C "C";
46
47 test_case("a final hash without a trailing newline still counts");
48 CHECK_INT(accesslist_parse_hashes(dest, 8, buf, 3 * 40 + 2), 3);
49}
50
51static void case_run_on_hash_is_rejected(void) {
52 const char buf[] = A "A\n";
53
54 test_case("forty one hex digits are not a hash");
55 CHECK_INT(accesslist_parse_hashes(dest, 8, buf, 42), 0);
56}
57
58static void case_junk_lines_skipped(void) {
59 const char buf[] = "# a comment\n" A "\nnot a hash\n" B "\n";
60
61 test_case("lines that are not hashes are ignored");
62 CHECK_INT(accesslist_parse_hashes(dest, 8, buf, sizeof(buf) - 1), 2);
63}
64
65static void case_trailing_junk_on_line(void) {
66 const char buf[] = A " some comment\n" B "\n";
67
68 test_case("a hash followed by other text on the same line still counts");
69 CHECK_INT(accesslist_parse_hashes(dest, 8, buf, sizeof(buf) - 1), 2);
70}
71
72static void case_capacity_is_respected(void) {
73 const char buf[] = A "\n" B "\n" C "\n";
74
75 test_case("no more than capacity hashes are written");
76 CHECK_INT(accesslist_parse_hashes(dest, 2, buf, sizeof(buf) - 1), 2);
77}
78
79static void case_zero_capacity_never_writes(void) {
80 const char buf[] = A "\n";
81
82 test_case("a zero capacity tolerates a null destination");
83 CHECK_INT(accesslist_parse_hashes(NULL, 0, buf, 41), 0);
84}
85
86static void case_decoded_value(void) {
87 const char buf[] = "000102030405060708090A0B0C0D0E0F10111213\n";
88 const uint8_t want[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
89
90 test_case("the hash is decoded, not merely counted");
91 CHECK_INT(accesslist_parse_hashes(dest, 8, buf, 41), 1);
92 CHECK_MEM(dest[0], want, sizeof(want));
93}
94
95static void case_capacity_bound_holds(void) {
96 /* (len + 1) / 41 is what accesslist_readfile allocates. Pack a buffer as
97 densely as the parser allows and check the bound is not exceeded. */
98 char buf[41 * 4];
99 size_t len, capacity, found;
100
101 test_case("the capacity formula bounds what the parser can produce");
102 memcpy(buf, A "\n" B "\n" C "\n" A, 4 * 40 + 3);
103 len = 4 * 40 + 3;
104 capacity = (len + 1) / 41;
105 found = accesslist_parse_hashes(dest, 8, buf, len);
106 CHECK_INT(found, 4);
107 CHECK(found <= capacity);
108}
109
110void test_accesslist(void) {
111 case_empty();
112 case_single_with_newline();
113 case_single_without_newline();
114 case_several_last_without_newline();
115 case_run_on_hash_is_rejected();
116 case_junk_lines_skipped();
117 case_trailing_junk_on_line();
118 case_capacity_is_respected();
119 case_zero_capacity_never_writes();
120 case_decoded_value();
121 case_capacity_bound_holds();
122}
diff --git a/tests/t_buf.c b/tests/t_buf.c
new file mode 100644
index 0000000..dcfb0bb
--- /dev/null
+++ b/tests/t_buf.c
@@ -0,0 +1,205 @@
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_buf.h"
12#include "test.h"
13
14static void case_starts_empty(void) {
15 ot_buf buf = {NULL, 0};
16
17 test_case("a fresh buffer holds nothing and has no data pointer");
18 CHECK_PTR(buf.data, NULL);
19 CHECK_INT(buf.size, 0);
20}
21
22static void case_cat_appends(void) {
23 ot_buf buf = {NULL, 0};
24
25 test_case("appends concatenate in order and report success");
26 CHECK_INT(buf_cat(&buf, "GET /ann", 8), 0);
27 CHECK(buf.data != NULL);
28 CHECK_INT(buf.size, 8);
29 CHECK_INT(buf_cat(&buf, "ounce", 5), 0);
30 CHECK_INT(buf.size, 13);
31 CHECK_MEM(buf.data, "GET /announce", 13);
32 buf_reset(&buf);
33}
34
35static void case_cat_of_nothing_allocates_nothing(void) {
36 ot_buf buf = {NULL, 0};
37
38 /* handle_read tells an untouched buffer from a partial request by the data
39 pointer, so a zero length append must not quietly allocate one */
40 test_case("appending zero bytes leaves the buffer untouched");
41 CHECK_INT(buf_cat(&buf, "ignored", 0), 0);
42 CHECK_PTR(buf.data, NULL);
43 CHECK_INT(buf.size, 0);
44 buf_reset(&buf);
45}
46
47static void case_grows_across_many_appends(void) {
48 ot_buf buf = {NULL, 0};
49 char chunk[100];
50 size_t i;
51
52 test_case("content survives growing one packet at a time");
53 memset(chunk, 'x', sizeof(chunk));
54 for (i = 0; i < 100; ++i)
55 if (buf_cat(&buf, chunk, sizeof(chunk))) {
56 test_failf(__FILE__, __LINE__, "append %zu was refused", i);
57 buf_reset(&buf);
58 return;
59 }
60 ++test_checks;
61 CHECK_INT(buf.size, 10000);
62 for (i = 0; i < 10000; ++i)
63 if (buf.data[i] != 'x') {
64 test_failf(__FILE__, __LINE__, "byte %zu was not preserved across growth", i);
65 buf_reset(&buf);
66 return;
67 }
68 ++test_checks;
69 buf_reset(&buf);
70}
71
72static void case_grows_one_byte_at_a_time(void) {
73 ot_buf buf = {NULL, 0};
74 size_t i;
75
76 /* The shape a trickling client produces. Nothing here is optimised for it,
77 so the point is only that it stays correct. */
78 test_case("a byte at a time accumulates correctly");
79 for (i = 0; i < 2048; ++i) {
80 char c = (char)('a' + (i % 26));
81 if (buf_cat(&buf, &c, 1)) {
82 test_failf(__FILE__, __LINE__, "append %zu was refused", i);
83 buf_reset(&buf);
84 return;
85 }
86 }
87 ++test_checks;
88 CHECK_INT(buf.size, 2048);
89 for (i = 0; i < 2048; ++i)
90 if (buf.data[i] != (char)('a' + (i % 26))) {
91 test_failf(__FILE__, __LINE__, "byte %zu differs", i);
92 buf_reset(&buf);
93 return;
94 }
95 ++test_checks;
96 buf_reset(&buf);
97}
98
99static void case_truncate_shrinks_only(void) {
100 ot_buf buf = {NULL, 0};
101
102 test_case("truncate drops the tail but never grows");
103 buf_cat(&buf, "0123456789", 10);
104 buf_truncate(&buf, 4);
105 CHECK_INT(buf.size, 4);
106 CHECK_MEM(buf.data, "0123", 4);
107
108 buf_truncate(&buf, 9);
109 CHECK_INT(buf.size, 4);
110 buf_truncate(&buf, 4);
111 CHECK_INT(buf.size, 4);
112
113 buf_truncate(&buf, 0);
114 CHECK_INT(buf.size, 0);
115 buf_reset(&buf);
116}
117
118static void case_append_after_truncate(void) {
119 ot_buf buf = {NULL, 0};
120
121 test_case("appending after a truncate overwrites the dropped tail");
122 buf_cat(&buf, "keepdrop", 8);
123 buf_truncate(&buf, 4);
124 CHECK_INT(buf_cat(&buf, "MORE", 4), 0);
125 CHECK_INT(buf.size, 8);
126 CHECK_MEM(buf.data, "keepMORE", 8);
127 buf_reset(&buf);
128}
129
130static void case_reset_clears_everything(void) {
131 ot_buf buf = {NULL, 0};
132
133 test_case("reset releases the buffer and restores the initial state");
134 buf_cat(&buf, "something", 9);
135 buf_reset(&buf);
136 CHECK_PTR(buf.data, NULL);
137 CHECK_INT(buf.size, 0);
138}
139
140static void case_reset_of_empty_is_safe(void) {
141 ot_buf buf = {NULL, 0};
142
143 test_case("resetting a buffer that was never used is harmless");
144 buf_reset(&buf);
145 buf_reset(&buf);
146 CHECK_PTR(buf.data, NULL);
147 CHECK_INT(buf.size, 0);
148}
149
150static void case_refused_append_is_harmless(void) {
151 ot_buf buf = {NULL, 0};
152
153 /* A length that would wrap the size is rejected before src is read and
154 before anything is allocated, so a short string is safe to pass here.
155 A refused append must not destroy the partial request already collected:
156 the caller answers and closes rather than losing both. */
157 test_case("a length that would wrap the size is refused");
158 CHECK_INT(buf_cat(&buf, "already here", 12), 0);
159 CHECK_INT(buf_cat(&buf, "x", (size_t)-1), -1);
160
161 test_case("and earlier content survives it");
162 CHECK_INT(buf.size, 12);
163 CHECK_MEM(buf.data, "already here", 12);
164
165 test_case("and the buffer still works afterwards");
166 CHECK_INT(buf_cat(&buf, "!", 1), 0);
167 CHECK_INT(buf.size, 13);
168 CHECK_MEM(buf.data, "already here!", 13);
169 buf_reset(&buf);
170}
171
172static void case_accumulates_a_split_request(void) {
173 /* What handle_read actually does: collect a request that arrived in
174 pieces, then hand over the whole of it */
175 ot_buf buf = {NULL, 0};
176 const char *pieces[] = {"GET /announce?info_hash=", "0123456789abcdefghij", " HTTP/1.0\r\n\r\n"};
177 size_t i;
178
179 test_case("a request split over several reads is reassembled");
180 for (i = 0; i < 3; ++i)
181 if (buf_cat(&buf, pieces[i], strlen(pieces[i]))) {
182 test_failf(__FILE__, __LINE__, "piece %zu was refused", i);
183 buf_reset(&buf);
184 return;
185 }
186 ++test_checks;
187
188 CHECK_INT(buf.size, 24 + 20 + 13);
189 CHECK_MEM(buf.data, "GET /announce?info_hash=0123456789abcdefghij HTTP/1.0\r\n\r\n", 57);
190 buf_reset(&buf);
191}
192
193void test_buf(void) {
194 case_starts_empty();
195 case_cat_appends();
196 case_cat_of_nothing_allocates_nothing();
197 case_grows_across_many_appends();
198 case_grows_one_byte_at_a_time();
199 case_truncate_shrinks_only();
200 case_append_after_truncate();
201 case_reset_clears_everything();
202 case_reset_of_empty_is_safe();
203 case_refused_append_is_harmless();
204 case_accumulates_a_split_request();
205}
diff --git a/tests/t_hex.c b/tests/t_hex.c
new file mode 100644
index 0000000..7cce8ac
--- /dev/null
+++ b/tests/t_hex.c
@@ -0,0 +1,128 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <stdint.h>
8#include <string.h>
9
10/* Opentracker */
11#include "ot_hex.h"
12#include "test.h"
13
14static const uint8_t sample[20] = {0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xff, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0};
15
16static void case_encode(void) {
17 char dest[64];
18
19 test_case("a hash encodes to forty upper case digits and a terminator");
20 memset(dest, 0x7f, sizeof(dest));
21 CHECK_PTR(hex_fromhash(dest, sample), dest);
22 CHECK_STR(dest, "000123456789ABCDEFFF10203040506070809" "0A0");
23 CHECK_INT(strlen(dest), 40);
24 CHECK_INT((unsigned char)dest[41], 0x7f);
25}
26
27static void case_decode(void) {
28 ot_hash hash;
29
30 test_case("forty digits decode back to the hash");
31 CHECK(hex_tohash(hash, "000123456789ABCDEFFF10203040506070809" "0A0"));
32 CHECK_MEM(hash, sample, sizeof(sample));
33}
34
35static void case_round_trip(void) {
36 ot_hash hash;
37 char text[64];
38 size_t i;
39
40 test_case("encode and decode are inverses over every byte value");
41 for (i = 0; i < 256; ++i) {
42 uint8_t probe[20];
43 memset(probe, (uint8_t)i, sizeof(probe));
44 hex_fromhash(text, probe);
45 if (!hex_tohash(hash, text) || memcmp(hash, probe, sizeof(probe))) {
46 test_failf(__FILE__, __LINE__, "round trip failed for byte value %zu", i);
47 return;
48 }
49 }
50 ++test_checks;
51}
52
53static void case_decode_is_case_insensitive(void) {
54 ot_hash upper, lower;
55
56 test_case("lower case digits decode the same as upper case");
57 CHECK(hex_tohash(upper, "ABCDEF0123456789ABCDEF0123456789ABCDEF01"));
58 CHECK(hex_tohash(lower, "abcdef0123456789abcdef0123456789abcdef01"));
59 CHECK_MEM(lower, upper, sizeof(ot_hash));
60}
61
62static void case_rejects_non_digits(void) {
63 ot_hash hash;
64
65 test_case("a non hex character anywhere rejects the whole string");
66 CHECK_INT(hex_tohash(hash, "g00123456789ABCDEFFF102030405060708090A0"), 0);
67 CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF102030405060708090Ag"), 0);
68 CHECK_INT(hex_tohash(hash, "000123456789ABCDEF:F102030405060708090A"), 0);
69}
70
71static void case_rejects_short_input(void) {
72 ot_hash hash;
73
74 test_case("a string shorter than forty digits is rejected");
75 CHECK_INT(hex_tohash(hash, ""), 0);
76 CHECK_INT(hex_tohash(hash, "00"), 0);
77 CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF102030405060708090A"), 0);
78}
79
80static void case_leaves_hash_untouched_on_failure(void) {
81 ot_hash hash;
82
83 test_case("a rejected string leaves the destination untouched");
84 memset(hash, 0x5a, sizeof(hash));
85 CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF10203040506070809XX0"), 0);
86 CHECK_MEM(hash, "\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a", sizeof(hash));
87}
88
89static void case_stops_at_the_terminator(void) {
90 /* The saved state parser hands this whole buffers that may end early. The
91 decoder must not read past the zero that stopped it. */
92 char guarded[8];
93 ot_hash hash;
94
95 test_case("decoding stops at a terminator rather than running past it");
96 memcpy(guarded, "0012", 4);
97 guarded[4] = 0;
98 memset(guarded + 5, 'f', 3);
99 CHECK_INT(hex_tohash(hash, guarded), 0);
100}
101
102static void case_isdigit(void) {
103 test_case("hex_isdigit accepts exactly the hex digits");
104 CHECK(hex_isdigit('0'));
105 CHECK(hex_isdigit('9'));
106 CHECK(hex_isdigit('a'));
107 CHECK(hex_isdigit('f'));
108 CHECK(hex_isdigit('A'));
109 CHECK(hex_isdigit('F'));
110 CHECK_INT(hex_isdigit('g'), 0);
111 CHECK_INT(hex_isdigit('G'), 0);
112 CHECK_INT(hex_isdigit(':'), 0);
113 CHECK_INT(hex_isdigit('\n'), 0);
114 CHECK_INT(hex_isdigit(0), 0);
115 CHECK_INT(hex_isdigit(0x80), 0);
116}
117
118void test_hex(void) {
119 case_encode();
120 case_decode();
121 case_round_trip();
122 case_decode_is_case_insensitive();
123 case_rejects_non_digits();
124 case_rejects_short_input();
125 case_leaves_hash_untouched_on_failure();
126 case_stops_at_the_terminator();
127 case_isdigit();
128}
diff --git a/tests/t_iovec.c b/tests/t_iovec.c
new file mode 100644
index 0000000..89f3fda
--- /dev/null
+++ b/tests/t_iovec.c
@@ -0,0 +1,124 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <stdlib.h>
8#include <string.h>
9#include <sys/uio.h>
10
11/* Opentracker */
12#include "ot_iovec.h"
13#include "test.h"
14
15static void case_increase_and_length(void) {
16 size_t entries = 0;
17 struct iovec *vector = NULL;
18 void *first, *second;
19
20 test_case("increase appends an entry and length is their sum");
21 first = iovec_increase(&entries, &vector, 100);
22 CHECK(first != NULL);
23 CHECK_INT(entries, 1);
24 CHECK_INT(vector[0].iov_len, 100);
25 CHECK_PTR(vector[0].iov_base, first);
26
27 second = iovec_increase(&entries, &vector, 50);
28 CHECK(second != NULL);
29 CHECK_INT(entries, 2);
30 CHECK_INT(vector[1].iov_len, 50);
31 CHECK_INT(iovec_length(&entries, (const struct iovec **)&vector), 150);
32
33 iovec_free(&entries, &vector);
34}
35
36static void case_free_resets(void) {
37 size_t entries = 0;
38 struct iovec *vector = NULL;
39
40 test_case("free releases the entries and resets the vector");
41 CHECK(iovec_increase(&entries, &vector, 10) != NULL);
42 CHECK(iovec_increase(&entries, &vector, 10) != NULL);
43 iovec_free(&entries, &vector);
44 CHECK_INT(entries, 0);
45 CHECK_PTR(vector, NULL);
46 CHECK_INT(iovec_length(&entries, (const struct iovec **)&vector), 0);
47}
48
49static void case_fixlast_shrinks(void) {
50 size_t entries = 0;
51 struct iovec *vector = NULL;
52 char *block;
53
54 test_case("fixlast trims the last entry to the bytes actually used");
55 CHECK(iovec_increase(&entries, &vector, 64) != NULL);
56 block = iovec_increase(&entries, &vector, 1000);
57 CHECK(block != NULL);
58 memset(block, 'x', 20);
59
60 iovec_fixlast(&entries, &vector, block + 20);
61 CHECK_INT(entries, 2);
62 CHECK_INT(vector[1].iov_len, 20);
63 CHECK_INT(iovec_length(&entries, (const struct iovec **)&vector), 84);
64 CHECK_MEM(vector[1].iov_base, "xxxxxxxxxxxxxxxxxxxx", 20);
65
66 iovec_free(&entries, &vector);
67}
68
69static void case_fixlast_on_empty(void) {
70 size_t entries = 0;
71 struct iovec *vector = NULL;
72
73 test_case("fixlast on an empty vector does nothing");
74 iovec_fixlast(&entries, &vector, NULL);
75 CHECK_INT(entries, 0);
76 CHECK_PTR(vector, NULL);
77}
78
79static void case_append_takes_ownership(void) {
80 size_t entries = 0;
81 struct iovec *vector = NULL;
82 struct iovec source;
83
84 test_case("append moves the buffer and clears the source");
85 source.iov_base = malloc(10);
86 CHECK(source.iov_base != NULL);
87 memcpy(source.iov_base, "0123456789", 10);
88 source.iov_len = 10;
89
90 CHECK(iovec_append(&entries, &vector, &source) != NULL);
91 CHECK_INT(entries, 1);
92 CHECK_INT(vector[0].iov_len, 10);
93 CHECK_MEM(vector[0].iov_base, "0123456789", 10);
94 CHECK_PTR(source.iov_base, NULL);
95 CHECK_INT(source.iov_len, 0);
96
97 iovec_free(&entries, &vector);
98}
99
100static void case_fix_increase(void) {
101 size_t entries = 0;
102 struct iovec *vector = NULL;
103 char *block;
104
105 test_case("fix_increase trims the previous entry and adds a new one");
106 block = iovec_increase(&entries, &vector, 500);
107 CHECK(block != NULL);
108 block = iovec_fix_increase_or_free(&entries, &vector, block + 30, 200);
109 CHECK(block != NULL);
110 CHECK_INT(entries, 2);
111 CHECK_INT(vector[0].iov_len, 30);
112 CHECK_INT(vector[1].iov_len, 200);
113
114 iovec_free(&entries, &vector);
115}
116
117void test_iovec(void) {
118 case_increase_and_length();
119 case_free_resets();
120 case_fixlast_shrinks();
121 case_fixlast_on_empty();
122 case_append_takes_ownership();
123 case_fix_increase();
124}
diff --git a/tests/t_ip.c b/tests/t_ip.c
new file mode 100644
index 0000000..2fe3a72
--- /dev/null
+++ b/tests/t_ip.c
@@ -0,0 +1,217 @@
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_ip.h"
12#include "test.h"
13
14static char text[64];
15
16static const char *formatted(const char *ip) {
17 text[ip_format(text, ip)] = 0;
18 return text;
19}
20
21/* ------------------------------------------------------------------ IPv4 */
22
23/* ------------------------------------------------------- RFC 5952 output */
24
25static void case_format_rfc5952(void) {
26 test_case("4.1 leading zeros in a group are suppressed");
27 CHECK_STR(formatted("\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"), "2001:db8::1");
28
29 test_case("4.2.1 the longest run of zero groups becomes ::");
30 CHECK_STR(formatted("\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01"), "2001:db8::2:1");
31
32 test_case("4.2.2 a single zero group is never shortened");
33 CHECK_STR(formatted("\x20\x01\x0d\xb8\x00\x00\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01"), "2001:db8:0:1:1:1:1:1");
34
35 test_case("4.2.1 a longer run wins over an earlier shorter one");
36 CHECK_STR(formatted("\x20\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01"), "2001:0:0:1::1");
37
38 test_case("4.2.3 of two runs of equal length the first is shortened");
39 CHECK_STR(formatted("\x20\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x01"), "2001::1:0:0:1:1");
40
41 test_case("4.3 hexadecimal digits are lower case");
42 CHECK_STR(formatted("\xab\xcd\xef\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"), "abcd:ef01::1");
43
44 test_case("the unspecified and loopback addresses");
45 CHECK_STR(formatted("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), "::");
46 CHECK_STR(formatted("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"), "::1");
47
48 test_case("a full address with no zero group");
49 CHECK_STR(formatted("\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x66\x66\x77\x77\x88\x88"), "1111:2222:3333:4444:5555:6666:7777:8888");
50}
51
52static void case_format_trailing_zero_group(void) {
53 /* libowfat's formatter looked two bytes past the address to decide this
54 one, so a peer address ending in a zero group came out as "::" whenever
55 the bytes that happened to follow were not themselves zero. In
56 ot_stats.c those bytes are the peer's port. */
57 const char ip[] = "\x20\x01\x0d\xb8\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x00";
58
59 test_case("a single trailing zero group is written out, not shortened");
60 CHECK_STR(formatted(ip), "2001:db8:1:2:3:4:5:0");
61
62 test_case("and nothing beyond the sixteen bytes is consulted");
63 {
64 char padded[20];
65 memcpy(padded, ip, 16);
66 memset(padded + 16, 0xff, 4); /* a port and flags, as a peer record has */
67 CHECK_STR(formatted(padded), "2001:db8:1:2:3:4:5:0");
68 memset(padded + 16, 0x00, 4);
69 CHECK_STR(formatted(padded), "2001:db8:1:2:3:4:5:0");
70 }
71}
72
73static void case_v4mapped(void) {
74 const char mapped[] = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x0a\x00\x00\x01";
75 const char unmapped[] = "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01";
76
77 test_case("a v4 mapped address is recognised");
78 CHECK(ip_is_v4mapped(mapped));
79 CHECK_INT(ip_is_v4mapped(unmapped), 0);
80
81 /* The tracker has only ever shown the plain quad, so that is the one
82 form the formatter produces; "::ffff:10.0.0.1" is not written anywhere */
83 test_case("a mapped address is written as the plain dotted quad");
84 CHECK_STR(formatted(mapped), "10.0.0.1");
85 CHECK_STR(formatted("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00"), "0.0.0.0");
86 CHECK_STR(formatted("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff"), "255.255.255.255");
87
88 test_case("anything else follows RFC 5952");
89 CHECK_STR(formatted(unmapped), "2001:db8::1");
90}
91
92/* -------------------------------------------------------- RFC 4291 input */
93
94static void case_scan_forms(void) {
95 char ip[16];
96
97 test_case("2.2 form 1, every group spelled out");
98 CHECK_INT(ip_scan("1111:2222:3333:4444:5555:6666:7777:8888", ip), 39);
99 CHECK_MEM(ip, "\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x66\x66\x77\x77\x88\x88", 16);
100
101 test_case("2.2 form 2, zero groups abbreviated");
102 CHECK_INT(ip_scan("2001:db8::1", ip), 11);
103 CHECK_MEM(ip, "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16);
104 CHECK_INT(ip_scan("::1", ip), 3);
105 CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16);
106 CHECK_INT(ip_scan("::", ip), 2);
107 CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16);
108 CHECK_INT(ip_scan("1::", ip), 3);
109 CHECK_MEM(ip, "\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16);
110
111 test_case("2.2 form 3, a dotted quad in the last two groups");
112 CHECK_INT(ip_scan("::ffff:1.2.3.4", ip), 14);
113 CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x01\x02\x03\x04", 16);
114 CHECK_INT(ip_scan("1:2:3:4:5:6:1.2.3.4", ip), 19);
115 CHECK_MEM(ip, "\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x01\x02\x03\x04", 16);
116
117 test_case("upper case input is accepted");
118 CHECK_INT(ip_scan("2001:DB8::1", ip), 11);
119 CHECK_MEM(ip, "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16);
120}
121
122static void case_scan_accepts_bare_ipv4(void) {
123 char ip[16];
124
125 /* Every address opentracker holds is sixteen bytes, so an operator writing
126 a plain IPv4 address in the config has to land as the v4 mapped form */
127 test_case("a bare IPv4 address is stored v4 mapped");
128 CHECK_INT(ip_scan("10.0.0.1", ip), 8);
129 CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x0a\x00\x00\x01", 16);
130 CHECK(ip_is_v4mapped(ip));
131}
132
133static void case_scan_reports_length(void) {
134 char ip[16];
135
136 /* The config parser continues from where the address ended, which is how
137 "address:port" and "address/bits" are read */
138 test_case("scanning stops at the character after the address");
139 CHECK_INT(ip_scan("10.0.0.1:6969", ip), 8);
140 CHECK_INT(ip_scan("2001:db8::1/64", ip), 11);
141 CHECK_INT(ip_scan("::1]", ip), 3);
142 CHECK_INT(ip_scan("1:2:3:4:5:6:7:8 trailing", ip), 15);
143}
144
145static void case_scan_rejects(void) {
146 char ip[16];
147
148 test_case("an empty or junk string is refused");
149 CHECK_INT(ip_scan("", ip), 0);
150 CHECK_INT(ip_scan("x", ip), 0);
151 CHECK_INT(ip_scan(":", ip), 0);
152 CHECK_INT(ip_scan(":1:2", ip), 0);
153
154 test_case("too few groups without :: are refused");
155 CHECK_INT(ip_scan("1:2:3:4:5:6:7", ip), 0);
156 CHECK_INT(ip_scan("2001:db8:1", ip), 0);
157
158 /* libowfat accepted a prefix of each of these and carried on. Taking only
159 part of a malformed address is worse than refusing it, especially where
160 the text comes from a config file or an X-Forwarded-For header. */
161 test_case("a second :: is refused rather than truncated");
162 CHECK_INT(ip_scan("1::2::3", ip), 0);
163 CHECK_INT(ip_scan("::1::", ip), 0);
164 CHECK_INT(ip_scan("::::", ip), 0);
165
166 test_case("more than eight groups is refused");
167 CHECK_INT(ip_scan("1:2:3:4:5:6:7:8:9", ip), 0);
168
169 test_case("a group of more than four digits is refused, not truncated");
170 CHECK_INT(ip_scan("12345::1", ip), 0);
171 CHECK_INT(ip_scan("fffff::1", ip), 0);
172}
173
174static void case_round_trip(void) {
175 /* Whatever the formatter writes, the scanner has to read back */
176 static const char *const samples[] = {"::",
177 "::1",
178 "1::",
179 "2001:db8::1",
180 "2001:db8:1:2:3:4:5:0",
181 "1111:2222:3333:4444:5555:6666:7777:8888",
182 "2001::1:0:0:1",
183 "abcd:ef01::1",
184 NULL};
185 char ip[16], again[16];
186 size_t i;
187
188 test_case("every formatted address scans back to the same bytes");
189 for (i = 0; samples[i]; ++i) {
190 size_t n = ip_scan(samples[i], ip);
191 if (n != strlen(samples[i])) {
192 test_failf(__FILE__, __LINE__, "\"%s\" consumed %zu of %zu", samples[i], n, strlen(samples[i]));
193 return;
194 }
195 text[ip_format(text, ip)] = 0;
196 if (strcmp(text, samples[i])) {
197 test_failf(__FILE__, __LINE__, "\"%s\" formatted back as \"%s\"", samples[i], text);
198 return;
199 }
200 if (!ip_scan(text, again) || memcmp(ip, again, 16)) {
201 test_failf(__FILE__, __LINE__, "\"%s\" did not survive a second pass", samples[i]);
202 return;
203 }
204 }
205 ++test_checks;
206}
207
208void test_ip(void) {
209 case_format_rfc5952();
210 case_format_trailing_zero_group();
211 case_v4mapped();
212 case_scan_forms();
213 case_scan_accepts_bare_ipv4();
214 case_scan_reports_length();
215 case_scan_rejects();
216 case_round_trip();
217}
diff --git a/tests/t_random.c b/tests/t_random.c
new file mode 100644
index 0000000..67dedf6
--- /dev/null
+++ b/tests/t_random.c
@@ -0,0 +1,114 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <stdint.h>
8#include <string.h>
9
10/* Opentracker */
11#include "ot_random.h"
12#include "test.h"
13
14/* Nothing here can test that the bytes are unpredictable, which is a property
15 of the generator the build selected. What these cases do pin is that the
16 wrapper hands the whole buffer to it, writes nothing beyond the length it
17 was given, and is actually wired to something that varies. The first of
18 those matters: the fallback this module replaced filled only the first
19 sixteen bytes of the sixty-four byte key buffer in ot_udp.c. */
20
21#define DRAWS 32
22#define BUFLEN 64
23
24static void case_every_byte_varies(void) {
25 unsigned char first[BUFLEN], draw[BUFLEN];
26 unsigned char varied[BUFLEN];
27 size_t i, n;
28
29 test_case("every byte of the buffer comes from the generator");
30 memset(varied, 0, sizeof(varied));
31 random_bytes(first, sizeof(first));
32
33 for (n = 0; n < DRAWS; ++n) {
34 random_bytes(draw, sizeof(draw));
35 for (i = 0; i < BUFLEN; ++i)
36 if (draw[i] != first[i])
37 varied[i] = 1;
38 }
39
40 /* A byte position that never moved across 32 draws is not being written.
41 A false alarm needs all 32 draws to agree by chance, around 256^-31. */
42 for (i = 0; i < BUFLEN; ++i)
43 if (!varied[i]) {
44 test_failf(__FILE__, __LINE__, "byte %zu never changed over %d draws", i, DRAWS);
45 return;
46 }
47 ++test_checks;
48}
49
50static void case_respects_length(void) {
51 unsigned char buf[32];
52 size_t i;
53
54 test_case("nothing is written past the requested length");
55 memset(buf, 0xA5, sizeof(buf));
56 random_bytes(buf, 8);
57 for (i = 8; i < sizeof(buf); ++i)
58 if (buf[i] != 0xA5) {
59 test_failf(__FILE__, __LINE__, "byte %zu past the end was overwritten", i);
60 return;
61 }
62 ++test_checks;
63}
64
65static void case_zero_length(void) {
66 unsigned char buf[4];
67
68 test_case("a zero length request writes nothing");
69 memset(buf, 0x5A, sizeof(buf));
70 random_bytes(buf, 0);
71 CHECK_MEM(buf, "\x5a\x5a\x5a\x5a", 4);
72}
73
74static void case_successive_draws_differ(void) {
75 unsigned char a[32], b[32];
76
77 test_case("two draws differ");
78 random_bytes(a, sizeof(a));
79 random_bytes(b, sizeof(b));
80 CHECK(memcmp(a, b, sizeof(a)) != 0);
81}
82
83static void case_uint32_varies(void) {
84 uint32_t first = random_uint32();
85 int differs = 0;
86 size_t n;
87
88 test_case("random_uint32 does not return a constant");
89 for (n = 0; n < DRAWS; ++n)
90 if (random_uint32() != first)
91 differs = 1;
92 CHECK(differs);
93}
94
95static void case_uint32_uses_the_top_bit(void) {
96 uint32_t seen = 0;
97 size_t n;
98
99 test_case("random_uint32 spans the full 32 bits");
100 /* random() never sets bit 31, so a build that returned it unmixed would
101 fail this. Over 64 draws the chance of a false alarm is 2^-64. */
102 for (n = 0; n < 64; ++n)
103 seen |= random_uint32();
104 CHECK_INT(seen & 0x80000000u, 0x80000000u);
105}
106
107void test_random(void) {
108 case_every_byte_varies();
109 case_respects_length();
110 case_zero_length();
111 case_successive_draws_differ();
112 case_uint32_varies();
113 case_uint32_uses_the_top_bit();
114}
diff --git a/tests/t_scan.c b/tests/t_scan.c
new file mode 100644
index 0000000..89f1590
--- /dev/null
+++ b/tests/t_scan.c
@@ -0,0 +1,162 @@
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_scan.h"
12#include "test.h"
13
14static void case_plain_numbers(void) {
15 uint64_t value;
16
17 test_case("a decimal number is read and its length reported");
18 CHECK_INT(scan_udec("0", &value, 65535), 1);
19 CHECK_INT(value, 0);
20 CHECK_INT(scan_udec("7", &value, 65535), 1);
21 CHECK_INT(value, 7);
22 CHECK_INT(scan_udec("6969", &value, 65535), 4);
23 CHECK_INT(value, 6969);
24 CHECK_INT(scan_udec("65535", &value, 65535), 5);
25 CHECK_INT(value, 65535);
26}
27
28static void case_stops_at_non_digit(void) {
29 uint64_t value;
30
31 test_case("scanning stops at the first character that is not a digit");
32 CHECK_INT(scan_udec("6969:rest", &value, 65535), 4);
33 CHECK_INT(value, 6969);
34 CHECK_INT(scan_udec("128/8", &value, 65535), 3);
35 CHECK_INT(value, 128);
36 CHECK_INT(scan_udec("42\n", &value, 65535), 2);
37 CHECK_INT(value, 42);
38}
39
40static void case_rejects_whitespace(void) {
41 uint64_t value = 0xdead;
42
43 test_case("leading whitespace is not skipped");
44 CHECK_INT(scan_udec(" 1", &value, 65535), 0);
45 CHECK_INT(scan_udec("\t1", &value, 65535), 0);
46 CHECK_INT(scan_udec("\n1", &value, 65535), 0);
47 CHECK_INT(value, 0xdead);
48}
49
50static void case_rejects_sign(void) {
51 uint64_t value = 0xdead;
52
53 test_case("a sign is not accepted");
54 CHECK_INT(scan_udec("+1", &value, 65535), 0);
55 CHECK_INT(scan_udec("-1", &value, 65535), 0);
56 CHECK_INT(scan_udec("-0", &value, 65535), 0);
57 CHECK_INT(value, 0xdead);
58}
59
60static void case_rejects_empty_and_junk(void) {
61 uint64_t value = 0xdead;
62
63 test_case("a string with no leading digit yields nothing");
64 CHECK_INT(scan_udec("", &value, 65535), 0);
65 CHECK_INT(scan_udec("x", &value, 65535), 0);
66 CHECK_INT(scan_udec(":80", &value, 65535), 0);
67 CHECK_INT(value, 0xdead);
68}
69
70static void case_no_alternative_bases(void) {
71 uint64_t value;
72
73 test_case("hexadecimal and octal notations are read as plain decimal");
74 CHECK_INT(scan_udec("0x10", &value, 65535), 1);
75 CHECK_INT(value, 0);
76 CHECK_INT(scan_udec("010", &value, 65535), 3);
77 CHECK_INT(value, 10);
78}
79
80static void case_refuses_out_of_range(void) {
81 uint64_t value = 0xdead;
82
83 test_case("a value above max is refused, not truncated");
84 CHECK_INT(scan_udec("65536", &value, 65535), 0);
85 CHECK_INT(scan_udec("99999", &value, 65535), 0);
86 CHECK_INT(scan_udec("129", &value, 128), 0);
87 CHECK_INT(value, 0xdead);
88
89 test_case("the boundary itself is accepted");
90 CHECK_INT(scan_udec("128", &value, 128), 3);
91 CHECK_INT(value, 128);
92}
93
94static void case_sixty_four_bit_range(void) {
95 uint64_t value;
96
97 test_case("the full unsigned 64 bit range is available");
98 CHECK_INT(scan_udec("18446744073709551615", &value, UINT64_MAX), 20);
99 CHECK(value == UINT64_MAX);
100
101 test_case("a number too large for 64 bits is refused");
102 CHECK_INT(scan_udec("18446744073709551616", &value, UINT64_MAX), 0);
103 CHECK_INT(scan_udec("99999999999999999999999999", &value, UINT64_MAX), 0);
104}
105
106static void case_leading_zeros_allowed(void) {
107 uint64_t value;
108
109 test_case("leading zeros are accepted outside bencode");
110 CHECK_INT(scan_udec("007", &value, 65535), 3);
111 CHECK_INT(value, 7);
112 CHECK_INT(scan_udec("0000", &value, 65535), 4);
113 CHECK_INT(value, 0);
114}
115
116static void case_bencode_rejects_leading_zeros(void) {
117 uint64_t value = 0xdead;
118
119 test_case("bencode refuses a leading zero on anything but zero");
120 CHECK_INT(scan_udec_bencode("007", &value, 65535), 0);
121 CHECK_INT(scan_udec_bencode("0000", &value, 65535), 0);
122 CHECK_INT(scan_udec_bencode("01", &value, 65535), 0);
123 CHECK_INT(value, 0xdead);
124}
125
126static void case_bencode_accepts_canonical(void) {
127 uint64_t value;
128
129 test_case("bencode accepts a canonically written number");
130 CHECK_INT(scan_udec_bencode("0", &value, 65535), 1);
131 CHECK_INT(value, 0);
132 CHECK_INT(scan_udec_bencode("0:", &value, 65535), 1);
133 CHECK_INT(value, 0);
134 CHECK_INT(scan_udec_bencode("160:", &value, 65535), 3);
135 CHECK_INT(value, 160);
136}
137
138static void case_bencode_inherits_the_rest(void) {
139 uint64_t value = 0xdead;
140
141 test_case("bencode is no laxer than the plain scanner");
142 CHECK_INT(scan_udec_bencode(" 1", &value, 65535), 0);
143 CHECK_INT(scan_udec_bencode("-1", &value, 65535), 0);
144 CHECK_INT(scan_udec_bencode("+1", &value, 65535), 0);
145 CHECK_INT(scan_udec_bencode("65536", &value, 65535), 0);
146 CHECK_INT(value, 0xdead);
147}
148
149void test_scan(void) {
150 case_plain_numbers();
151 case_stops_at_non_digit();
152 case_rejects_whitespace();
153 case_rejects_sign();
154 case_rejects_empty_and_junk();
155 case_no_alternative_bases();
156 case_refuses_out_of_range();
157 case_sixty_four_bit_range();
158 case_leading_zeros_allowed();
159 case_bencode_rejects_leading_zeros();
160 case_bencode_accepts_canonical();
161 case_bencode_inherits_the_rest();
162}
diff --git a/tests/t_scan_urlencoded_query.c b/tests/t_scan_urlencoded_query.c
new file mode 100644
index 0000000..6fa34e0
--- /dev/null
+++ b/tests/t_scan_urlencoded_query.c
@@ -0,0 +1,381 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <string.h>
8
9/* Opentracker */
10#include "scan_urlencoded_query.h"
11#include "test.h"
12
13/* The scanner returns the number of bytes written to the destination, or
14 -1 for a parse error and -2 for a hard terminator encountered before any
15 output was produced. It advances the source pointer past the separator it
16 stopped on, except for hard terminators and the '&' ending a parameter,
17 which it leaves for the next call to see. */
18
19static void case_path(void) {
20 char query[] = "/announce?info_hash=A";
21 char dest[64];
22 char *p = query;
23 ssize_t n;
24
25 test_case("path stops at the question mark");
26 n = scan_urlencoded_query(&p, dest, SCAN_PATH);
27 CHECK_INT(n, 9);
28 dest[n] = 0;
29 CHECK_STR(dest, "/announce");
30 CHECK_INT(*p, 'i');
31}
32
33static void case_param_and_value(void) {
34 char query[] = "info_hash=abc&port=80";
35 char dest[64];
36 char *p = query;
37 ssize_t n;
38
39 test_case("parameter stops at '=', value stops at '&'");
40 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_PARAM);
41 CHECK_INT(n, 9);
42 dest[n] = 0;
43 CHECK_STR(dest, "info_hash");
44
45 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
46 CHECK_INT(n, 3);
47 dest[n] = 0;
48 CHECK_STR(dest, "abc");
49
50 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_PARAM);
51 CHECK_INT(n, 4);
52 dest[n] = 0;
53 CHECK_STR(dest, "port");
54
55 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
56 CHECK_INT(n, 2);
57 dest[n] = 0;
58 CHECK_STR(dest, "80");
59}
60
61static void case_percent_decoding(void) {
62 char query[] = "%41%42%43&x=1";
63 char dest[64];
64 char *p = query;
65 ssize_t n;
66
67 test_case("percent escapes decode");
68 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
69 CHECK_INT(n, 3);
70 CHECK_MEM(dest, "ABC", 3);
71}
72
73static void case_binary_info_hash(void) {
74 /* The twenty byte info_hash is what the announce path really decodes */
75 char query[] = "%00%01%02%ff%fe%7f%80ABCDEFGHIJKLM&x=1";
76 unsigned char want[] = {0x00, 0x01, 0x02, 0xff, 0xfe, 0x7f, 0x80, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M'};
77 char dest[64];
78 char *p = query;
79 ssize_t n;
80
81 test_case("twenty byte binary value survives decoding");
82 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
83 CHECK_INT(n, 20);
84 CHECK_MEM(dest, want, 20);
85}
86
87static void case_in_place(void) {
88 char query[] = "%41%42&x=1";
89 char *p = query;
90 ssize_t n;
91
92 test_case("decoding in place over the source is safe");
93 n = scan_urlencoded_query(&p, query, SCAN_SEARCHPATH_VALUE);
94 CHECK_INT(n, 2);
95 CHECK_MEM(query, "AB", 2);
96}
97
98static void case_bad_escape(void) {
99 char query[] = "%zz";
100 char dest[64];
101 char *p = query;
102 ssize_t n;
103
104 test_case("a non hexadecimal escape is a parse error");
105 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
106 CHECK_INT(n, -1);
107}
108
109static void case_truncated_escape(void) {
110 char query[] = "%4";
111 char dest[64];
112 char *p = query;
113 ssize_t n;
114
115 test_case("an escape cut short by the terminator is a parse error");
116 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
117 CHECK_INT(n, -1);
118}
119
120static void case_terminator(void) {
121 char empty[] = "";
122 char space[] = " rest";
123 char dest[64];
124 char *p = empty;
125 ssize_t n;
126
127 test_case("a hard terminator before any output reports end of input");
128 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_PARAM);
129 CHECK_INT(n, -2);
130
131 p = space;
132 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_PARAM);
133 CHECK_INT(n, -2);
134}
135
136static void case_terminator_after_output(void) {
137 char query[] = "abc";
138 char dest[64];
139 char *p = query;
140 ssize_t n;
141
142 test_case("a hard terminator after output is left for the next call");
143 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
144 CHECK_INT(n, 3);
145 CHECK_INT(*p, 0);
146 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
147 CHECK_INT(n, -2);
148}
149
150/* The character matrix makes '?' terminate a path only, and '=' terminate a
151 path or a parameter only. Both are ordinary characters inside a value, so a
152 value is free to contain a nested query string. */
153
154static void case_separators_are_contextual(void) {
155 char question[] = "a?b&x=1";
156 char equals[] = "a=b&x=1";
157 char dest[64];
158 char *p = question;
159 ssize_t n;
160
161 test_case("a question mark is literal inside a value");
162 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
163 CHECK_INT(n, 3);
164 dest[n] = 0;
165 CHECK_STR(dest, "a?b");
166
167 test_case("an equals sign is literal inside a value");
168 p = equals;
169 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
170 CHECK_INT(n, 3);
171 dest[n] = 0;
172 CHECK_STR(dest, "a=b");
173}
174
175static void case_ampersand_in_path(void) {
176 char query[] = "/path&x";
177 char dest[64];
178 char *p = query;
179 ssize_t n;
180
181 test_case("an ampersand in a path is a parse error");
182 n = scan_urlencoded_query(&p, dest, SCAN_PATH);
183 CHECK_INT(n, -1);
184}
185
186static void case_raw_high_byte(void) {
187 char query[] = {'a', 'b', (char)0x80, 0};
188 char dest[64];
189 char *p = query;
190 ssize_t n;
191
192 test_case("an undecoded byte above ASCII is a parse error");
193 n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE);
194 CHECK_INT(n, -1);
195}
196
197static void case_skipvalue(void) {
198 char query[] = "abc&next=1";
199 char bare[] = "abc";
200 char *p = query;
201
202 test_case("skipvalue steps over the value and its separator");
203 scan_urlencoded_skipvalue(&p);
204 CHECK_STR(p, "next=1");
205
206 test_case("skipvalue leaves a hard terminator in place");
207 p = bare;
208 scan_urlencoded_skipvalue(&p);
209 CHECK_INT(*p, 0);
210}
211
212static void case_find_keywords(void) {
213 static const ot_keywords keywords[] = {{"port", 1}, {"left", 2}, {"compact", 3}, {NULL, -3}};
214 char match[] = "left=0";
215 char nomatch[] = "bogus=1";
216 char prefix[] = "por=1";
217 char *p = match;
218
219 test_case("a known keyword returns its value");
220 CHECK_INT(scan_find_keywords(keywords, &p, SCAN_SEARCHPATH_PARAM), 2);
221
222 test_case("an unknown keyword returns -3");
223 p = nomatch;
224 CHECK_INT(scan_find_keywords(keywords, &p, SCAN_SEARCHPATH_PARAM), -3);
225
226 test_case("a prefix of a keyword does not match it");
227 p = prefix;
228 CHECK_INT(scan_find_keywords(keywords, &p, SCAN_SEARCHPATH_PARAM), -3);
229}
230
231static void case_fixed_int(void) {
232 char positive[] = "123";
233 char negative[] = "-45";
234 char trailing[] = "12a";
235 int value;
236
237 test_case("scan_fixed_int returns the count of unparsed bytes");
238 CHECK_INT(scan_fixed_int(positive, 3, &value), 0);
239 CHECK_INT(value, 123);
240
241 CHECK_INT(scan_fixed_int(negative, 3, &value), 0);
242 CHECK_INT(value, -45);
243
244 CHECK_INT(scan_fixed_int(trailing, 3, &value), 1);
245 CHECK_INT(value, 12);
246}
247
248static void case_fixed_int_bounded(void) {
249 char digits[] = "12345";
250 int value;
251
252 test_case("scan_fixed_int honours the length it is given");
253 CHECK_INT(scan_fixed_int(digits, 3, &value), 0);
254 CHECK_INT(value, 123);
255}
256
257
258/* The encoding direction. Which bytes survive unescaped is fixed by what
259 existing parsers of the urlencoded full scrape expect, so these cases pin
260 the set rather than describe a preference. */
261
262static void case_fmt_passes_safe_bytes(void) {
263 const uint8_t src[] = "aZ09$-./=_~";
264 char dest[64];
265 size_t n;
266
267 test_case("alphanumerics and the safe punctuation pass through");
268 n = fmt_urlencoded(dest, src, 11);
269 dest[n] = 0;
270 CHECK_INT(n, 11);
271 CHECK_STR(dest, "aZ09$-./=_~");
272}
273
274static void case_fmt_escapes_the_rest(void) {
275 const uint8_t src[] = {' ', '%', '&', '+', ':', '?'};
276 char dest[64];
277 size_t n;
278
279 test_case("everything else becomes a percent escape");
280 n = fmt_urlencoded(dest, src, sizeof(src));
281 dest[n] = 0;
282 CHECK_INT(n, 18);
283 CHECK_STR(dest, "%20%25%26%2b%3a%3f");
284}
285
286static void case_fmt_hex_is_lower_case(void) {
287 const uint8_t src[] = {0x00, 0xab, 0xff};
288 char dest[64];
289 size_t n;
290
291 test_case("escapes use lower case hex digits");
292 n = fmt_urlencoded(dest, src, sizeof(src));
293 dest[n] = 0;
294 CHECK_INT(n, 9);
295 CHECK_STR(dest, "%00%ab%ff");
296}
297
298static void case_fmt_length_only(void) {
299 const uint8_t src[] = {'a', 0xff, 'b'};
300
301 test_case("a null destination reports the length without writing");
302 CHECK_INT(fmt_urlencoded(NULL, src, sizeof(src)), 5);
303 CHECK_INT(fmt_urlencoded(NULL, src, 0), 0);
304}
305
306static void case_fmt_zero_length(void) {
307 char dest[4];
308
309 test_case("a zero length input writes nothing");
310 memset(dest, 0x5a, sizeof(dest));
311 CHECK_INT(fmt_urlencoded(dest, (const uint8_t *)"abc", 0), 0);
312 CHECK_MEM(dest, "\x5a\x5a\x5a\x5a", 4);
313}
314
315static void case_fmt_worst_case_fits(void) {
316 uint8_t src[20];
317 char dest[61];
318 size_t i, n;
319
320 test_case("a hash of entirely unsafe bytes needs three times its length");
321 for (i = 0; i < sizeof(src); ++i)
322 src[i] = 0xff;
323 n = fmt_urlencoded(dest, src, sizeof(src));
324 dest[n] = 0;
325 CHECK_INT(n, 60);
326 for (i = 0; i < 20; ++i)
327 if (memcmp(dest + 3 * i, "%ff", 3)) {
328 test_failf(__FILE__, __LINE__, "escape %zu is wrong", i);
329 return;
330 }
331 ++test_checks;
332}
333
334static void case_fmt_decoder_disagrees_on_dollar(void) {
335 /* Documenting a known asymmetry rather than a wish. Of the bytes the
336 encoder passes through unescaped, '$' is the only one the decoder then
337 refuses, and it refuses the whole string rather than stopping short.
338 Output of the urlencoded full scrape therefore does not round trip
339 through scan_urlencoded_query when a hash contains 0x24. Harmless today
340 because only third party tooling reads that output. */
341 const uint8_t src[] = {'a', '$', 'b'};
342 char dest[16];
343 char scanned[16];
344 char *p = dest;
345 size_t n;
346 ssize_t got;
347
348 test_case("the encoder passes '$' but the decoder rejects it");
349 n = fmt_urlencoded(dest, src, sizeof(src));
350 dest[n] = 0;
351 CHECK_STR(dest, "a$b");
352
353 got = scan_urlencoded_query(&p, scanned, SCAN_SEARCHPATH_VALUE);
354 CHECK_INT(got, -1);
355}
356
357void test_scan_urlencoded_query(void) {
358 case_path();
359 case_param_and_value();
360 case_percent_decoding();
361 case_binary_info_hash();
362 case_in_place();
363 case_bad_escape();
364 case_truncated_escape();
365 case_terminator();
366 case_terminator_after_output();
367 case_separators_are_contextual();
368 case_ampersand_in_path();
369 case_raw_high_byte();
370 case_skipvalue();
371 case_find_keywords();
372 case_fixed_int();
373 case_fixed_int_bounded();
374 case_fmt_passes_safe_bytes();
375 case_fmt_escapes_the_rest();
376 case_fmt_hex_is_lower_case();
377 case_fmt_length_only();
378 case_fmt_zero_length();
379 case_fmt_worst_case_fits();
380 case_fmt_decoder_disagrees_on_dollar();
381}
diff --git a/tests/t_vector.c b/tests/t_vector.c
new file mode 100644
index 0000000..fee336b
--- /dev/null
+++ b/tests/t_vector.c
@@ -0,0 +1,161 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <stdlib.h>
8#include <string.h>
9
10/* Opentracker */
11#include "trackerlogic.h"
12#include "test.h"
13
14/* binary_search returns the matching member when there is one, and otherwise
15 the position at which the key would have to be inserted to keep the vector
16 sorted. exactmatch distinguishes the two. */
17
18static const unsigned char sorted[5][4] = {{0, 0, 0, 1}, {0, 0, 0, 3}, {0, 0, 0, 5}, {0, 0, 0, 7}, {0, 0, 0, 9}};
19
20static void case_search_finds_member(void) {
21 const unsigned char key[4] = {0, 0, 0, 5};
22 int exactmatch;
23 void *found;
24
25 test_case("an existing key is found");
26 found = binary_search(key, sorted, 5, 4, 4, &exactmatch);
27 CHECK(exactmatch != 0);
28 CHECK_PTR(found, sorted[2]);
29}
30
31static void case_search_finds_first_and_last(void) {
32 const unsigned char first[4] = {0, 0, 0, 1};
33 const unsigned char last[4] = {0, 0, 0, 9};
34 int exactmatch;
35
36 test_case("the first and last members are found");
37 CHECK_PTR(binary_search(first, sorted, 5, 4, 4, &exactmatch), sorted[0]);
38 CHECK(exactmatch != 0);
39 CHECK_PTR(binary_search(last, sorted, 5, 4, 4, &exactmatch), sorted[4]);
40 CHECK(exactmatch != 0);
41}
42
43static void case_search_insert_position(void) {
44 const unsigned char middle[4] = {0, 0, 0, 4};
45 const unsigned char before[4] = {0, 0, 0, 0};
46 int exactmatch;
47 void *slot;
48
49 test_case("a missing key yields its insert position");
50 slot = binary_search(middle, sorted, 5, 4, 4, &exactmatch);
51 CHECK_INT(exactmatch, 0);
52 CHECK_PTR(slot, sorted[2]);
53
54 slot = binary_search(before, sorted, 5, 4, 4, &exactmatch);
55 CHECK_INT(exactmatch, 0);
56 CHECK_PTR(slot, sorted[0]);
57}
58
59static void case_search_past_the_end(void) {
60 const unsigned char beyond[4] = {0, 0, 0, 0xff};
61 int exactmatch;
62 void *slot;
63
64 test_case("a key above every member yields the position past the end");
65 slot = binary_search(beyond, sorted, 5, 4, 4, &exactmatch);
66 CHECK_INT(exactmatch, 0);
67 CHECK_PTR(slot, sorted[5]);
68}
69
70static void case_search_empty(void) {
71 int exactmatch;
72 void *base = (void *)sorted;
73
74 test_case("searching an empty vector reports no match at the base");
75 CHECK_PTR(binary_search(sorted[0], base, 0, 4, 4, &exactmatch), base);
76 CHECK_INT(exactmatch, 0);
77}
78
79static void case_insert_keeps_order(void) {
80 ot_vector vector = {NULL, 0, 0};
81 const unsigned char five[4] = {0, 0, 0, 5};
82 const unsigned char one[4] = {0, 0, 0, 1};
83 const unsigned char nine[4] = {0, 0, 0, 9};
84 int exactmatch;
85 void *slot;
86
87 test_case("inserts land in sorted order and grow the vector");
88 slot = vector_find_or_insert(&vector, (void *)five, 4, 4, &exactmatch);
89 CHECK(slot != NULL);
90 CHECK_INT(exactmatch, 0);
91 memcpy(slot, five, 4);
92 CHECK_INT(vector.size, 1);
93 CHECK_INT(vector.space, OT_VECTOR_MIN_MEMBERS);
94
95 slot = vector_find_or_insert(&vector, (void *)one, 4, 4, &exactmatch);
96 CHECK(slot != NULL);
97 CHECK_INT(exactmatch, 0);
98 memcpy(slot, one, 4);
99 CHECK_INT(vector.size, 2);
100
101 slot = vector_find_or_insert(&vector, (void *)nine, 4, 4, &exactmatch);
102 CHECK(slot != NULL);
103 CHECK_INT(exactmatch, 0);
104 memcpy(slot, nine, 4);
105 CHECK_INT(vector.size, 3);
106 CHECK_INT(vector.space, OT_VECTOR_MIN_MEMBERS * OT_VECTOR_GROW_RATIO);
107
108 CHECK_MEM(vector.data, "\0\0\0\1\0\0\0\5\0\0\0\11", 12);
109
110 free(vector.data);
111}
112
113static void case_insert_finds_existing(void) {
114 ot_vector vector = {NULL, 0, 0};
115 const unsigned char five[4] = {0, 0, 0, 5};
116 int exactmatch;
117 void *slot, *again;
118
119 test_case("a second insert of the same key returns the existing member");
120 slot = vector_find_or_insert(&vector, (void *)five, 4, 4, &exactmatch);
121 CHECK(slot != NULL);
122 memcpy(slot, five, 4);
123
124 again = vector_find_or_insert(&vector, (void *)five, 4, 4, &exactmatch);
125 CHECK(exactmatch != 0);
126 CHECK_PTR(again, vector.data);
127 CHECK_INT(vector.size, 1);
128
129 free(vector.data);
130}
131
132static void case_insert_compare_prefix(void) {
133 ot_vector vector = {NULL, 0, 0};
134 unsigned char first[6] = {1, 2, 3, 0xaa, 0xaa, 0xaa};
135 unsigned char second[6] = {1, 2, 3, 0xbb, 0xbb, 0xbb};
136 int exactmatch;
137 void *slot;
138
139 test_case("only the compare prefix decides identity");
140 slot = vector_find_or_insert(&vector, first, 6, 3, &exactmatch);
141 CHECK(slot != NULL);
142 memcpy(slot, first, 6);
143
144 slot = vector_find_or_insert(&vector, second, 6, 3, &exactmatch);
145 CHECK(exactmatch != 0);
146 CHECK_INT(vector.size, 1);
147 CHECK_MEM(slot, first, 6);
148
149 free(vector.data);
150}
151
152void test_vector(void) {
153 case_search_finds_member();
154 case_search_finds_first_and_last();
155 case_search_insert_position();
156 case_search_past_the_end();
157 case_search_empty();
158 case_insert_keeps_order();
159 case_insert_finds_existing();
160 case_insert_compare_prefix();
161}
diff --git a/tests/test.c b/tests/test.c
new file mode 100644
index 0000000..4686369
--- /dev/null
+++ b/tests/test.c
@@ -0,0 +1,58 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <stdarg.h>
8#include <stdio.h>
9
10/* Opentracker */
11#include "test.h"
12
13unsigned long test_checks;
14unsigned long test_failures;
15
16static const char *current_module = "";
17static const char *current_case = "";
18
19void test_module(const char *name) {
20 current_module = name;
21 current_case = "";
22 printf("%s\n", name);
23}
24
25void test_case(const char *name) { current_case = name; }
26
27static void test_where(const char *file, int line) {
28 ++test_failures;
29 printf(" FAIL %s/%s (%s:%d)\n ", current_module, current_case, file, line);
30}
31
32void test_failf(const char *file, int line, const char *fmt, ...) {
33 va_list ap;
34 test_where(file, line);
35 va_start(ap, fmt);
36 vprintf(fmt, ap);
37 va_end(ap);
38 printf("\n");
39}
40
41void test_failmem(const char *file, int line, const char *what, const void *got, const void *want, size_t len) {
42 const unsigned char *g = got, *w = want;
43 size_t i;
44
45 test_where(file, line);
46 printf("%s: %zu bytes differ\n got ", what, len);
47 for (i = 0; i < len; ++i)
48 printf("%02x", g[i]);
49 printf("\n want ");
50 for (i = 0; i < len; ++i)
51 printf("%02x", w[i]);
52 printf("\n");
53}
54
55int test_summary(void) {
56 printf("\n%lu checks, %lu failures\n", test_checks, test_failures);
57 return test_failures != 0;
58}
diff --git a/tests/test.h b/tests/test.h
new file mode 100644
index 0000000..7449b73
--- /dev/null
+++ b/tests/test.h
@@ -0,0 +1,89 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6#ifndef OT_TEST_H__
7#define OT_TEST_H__
8
9#include <stddef.h>
10#include <string.h>
11
12/* Minimal scaffolding for opentracker's unit tests. No external dependencies.
13
14 Tests are grouped into modules, one per source file under test. A module is
15 a list of small case functions, each pinning one behaviour. A failing CHECK
16 reports file, line and the offending values, then returns from the case it
17 is in, so a single run reports as many independent problems as it can.
18 Because the macros return, they may only be used in functions returning
19 void. */
20
21void test_module(const char *name);
22void test_case(const char *name);
23void test_failf(const char *file, int line, const char *fmt, ...);
24void test_failmem(const char *file, int line, const char *what, const void *got, const void *want, size_t len);
25int test_summary(void);
26
27extern unsigned long test_checks;
28extern unsigned long test_failures;
29
30#define CHECK(cond) \
31 do { \
32 ++test_checks; \
33 if (!(cond)) { \
34 test_failf(__FILE__, __LINE__, "expected %s", #cond); \
35 return; \
36 } \
37 } while (0)
38
39#define CHECK_INT(got, want) \
40 do { \
41 long long got_ = (long long)(got), want_ = (long long)(want); \
42 ++test_checks; \
43 if (got_ != want_) { \
44 test_failf(__FILE__, __LINE__, "%s: got %lld, want %lld", #got, got_, want_); \
45 return; \
46 } \
47 } while (0)
48
49#define CHECK_PTR(got, want) \
50 do { \
51 const void *got_ = (const void *)(got), *want_ = (const void *)(want); \
52 ++test_checks; \
53 if (got_ != want_) { \
54 test_failf(__FILE__, __LINE__, "%s: got %p, want %p", #got, got_, want_); \
55 return; \
56 } \
57 } while (0)
58
59#define CHECK_MEM(got, want, len) \
60 do { \
61 ++test_checks; \
62 if (memcmp((got), (want), (len))) { \
63 test_failmem(__FILE__, __LINE__, #got, (got), (want), (len)); \
64 return; \
65 } \
66 } while (0)
67
68#define CHECK_STR(got, want) \
69 do { \
70 const char *got_ = (got), *want_ = (want); \
71 ++test_checks; \
72 if (strcmp(got_, want_)) { \
73 test_failf(__FILE__, __LINE__, "%s: got \"%s\", want \"%s\"", #got, got_, want_); \
74 return; \
75 } \
76 } while (0)
77
78/* One entry point per module under test */
79void test_scan_urlencoded_query(void);
80void test_iovec(void);
81void test_vector(void);
82void test_random(void);
83void test_hex(void);
84void test_accesslist(void);
85void test_scan(void);
86void test_buf(void);
87void test_ip(void);
88
89#endif
diff --git a/tests/testsuite2.sh b/tests/testsuite2.sh
index c9a5a6a..da5181b 100644
--- a/tests/testsuite2.sh
+++ b/tests/testsuite2.sh
@@ -2,13 +2,21 @@
2 2
3while true; do 3while true; do
4 request_string="GET /announce?info_hash=012345678901234567\ 4 request_string="GET /announce?info_hash=012345678901234567\
5%$(printf %02X $(( $RANDOM & 0xff )) )\ 5$(printf %02X $(( $RANDOM & 0xff )) )\
6%$(printf %02X $(( $RANDOM & 0xff )) )\ 6&ip=$(( $RANDOM & 0xff )).17.13.15&port=$(( $RANDOM & 0xff )) HTTP/1.0"
7&ip=$(( $RANDOM & 0xff )).17.13.15&port=$(( $RANDOM & 0xff )) HTTP/1.0\n"
8 7
9 echo $request_string 8# echo $request_string
10 echo 9# echo
11 echo $request_string | nc 23.23.23.237 6969 >/dev/null 10 printf "%s\n\n" "$request_string" | nc 84.200.61.9 6969 | hexdump -C
12 echo 11
12 request_string="GET /announce?info_hash=012345678901234567\
13$(printf %02X $(( $RANDOM & 0xff )) )\
14&ip=2001:1608:6:27::$(( $RANDOM & 0xff ))&port=$(( $RANDOM & 0xff )) HTTP/1.0"
15 printf "%s\n\n" "$request_string" | nc 2001:1608:6:27::9 6969 | hexdump -C
16 printf "%s\n\n" "$request_string"
17
18 request_string="GET /scrape?info_hash=012345678901234567\
19$(printf %02X $(( $RANDOM & 0xff )) ) HTTP/1.0"
20 printf "%s\n\n" "$request_string" | nc 2001:1608:6:27::9 6969 | hexdump -C
13 21
14done 22done
diff --git a/trackerlogic.c b/trackerlogic.c
index ea4b489..db1d787 100644
--- a/trackerlogic.c
+++ b/trackerlogic.c
@@ -4,454 +4,599 @@
4 $id$ */ 4 $id$ */
5 5
6/* System */ 6/* System */
7#include <stdlib.h>
8#include <string.h>
9#include <stdio.h>
10#include <arpa/inet.h> 7#include <arpa/inet.h>
11#include <unistd.h>
12#include <errno.h>
13#include <stdint.h> 8#include <stdint.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
14 12
15/* Libowfat */ 13/* Libowfat */
16#include "byte.h"
17#include "io.h" 14#include "io.h"
18#include "iob.h" 15#include "iob.h"
19#include "array.h"
20 16
21/* Opentracker */ 17/* Opentracker */
22#include "trackerlogic.h"
23#include "ot_mutex.h"
24#include "ot_stats.h"
25#include "ot_clean.h"
26#include "ot_http.h"
27#include "ot_accesslist.h" 18#include "ot_accesslist.h"
19#include "ot_clean.h"
28#include "ot_fullscrape.h" 20#include "ot_fullscrape.h"
21#include "ot_http.h"
22#include "ot_ip.h"
29#include "ot_livesync.h" 23#include "ot_livesync.h"
24#include "ot_mutex.h"
25#include "ot_random.h"
26#include "ot_stats.h"
27#include "ot_udp.h"
28#include "ot_vector.h"
29#include "trackerlogic.h"
30 30
31/* Forward declaration */ 31/* Forward declaration */
32size_t return_peers_for_torrent( struct ot_workstruct * ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto ); 32size_t return_peers_for_torrent(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto);
33
34void free_peerlist( ot_peerlist *peer_list ) {
35 if( peer_list->peers.data ) {
36 if( OT_PEERLIST_HASBUCKETS( peer_list ) ) {
37 ot_vector *bucket_list = (ot_vector*)(peer_list->peers.data);
38 33
39 while( peer_list->peers.size-- ) 34void free_peerlist(ot_peerlist *peer_list) {
40 free( bucket_list++->data ); 35 if (peer_list && peer_list->peers.data) {
41 } 36 if (OT_PEERLIST_HASBUCKETS(peer_list))
42 free( peer_list->peers.data ); 37 vector_clean_list((ot_vector *)peer_list->peers.data, peer_list->peers.size);
38 else
39 free(peer_list->peers.data);
43 } 40 }
44 free( peer_list ); 41 free(peer_list);
45} 42}
46 43
47void add_torrent_from_saved_state( ot_hash hash, ot_time base, size_t down_count ) { 44void add_torrent_from_saved_state(ot_hash const hash, ot_time base, size_t down_count) {
48 int exactmatch; 45 int exactmatch;
49 ot_torrent *torrent; 46 ot_torrent *torrent;
50 ot_vector *torrents_list = mutex_bucket_lock_by_hash( hash ); 47 ot_vector *torrents_list = mutex_bucket_lock_by_hash(hash);
51 48
52 if( !accesslist_hashisvalid( hash ) ) 49 if (!accesslist_hashisvalid(hash)) {
53 return mutex_bucket_unlock_by_hash( hash, 0 ); 50 mutex_bucket_unlock_by_hash(hash, 0);
51 return;
52 }
54 53
55 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);
56 if( !torrent || exactmatch ) 55 if (!torrent || exactmatch) {
57 return mutex_bucket_unlock_by_hash( hash, 0 ); 56 mutex_bucket_unlock_by_hash(hash, 0);
57 return;
58 }
58 59
59 /* Create a new torrent entry, then */ 60 /* Create a new torrent entry, then */
60 memcpy( torrent->hash, hash, sizeof(ot_hash) ); 61 memset(torrent, 0, sizeof(ot_torrent));
62 memcpy(torrent->hash, hash, sizeof(ot_hash));
61 63
62 if( !( torrent->peer_list = 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_list, sizeof( ot_peerlist ) ); 70 memset(torrent->peer_list6, 0, sizeof(ot_peerlist));
68 torrent->peer_list->base = base; 71 memset(torrent->peer_list4, 0, sizeof(ot_peerlist));
69 torrent->peer_list->down_count = down_count; 72 torrent->peer_list6->base = base;
73 torrent->peer_list4->base = base;
74 torrent->peer_list6->down_count = down_count;
75 torrent->peer_list4->down_count = down_count;
70 76
71 return mutex_bucket_unlock_by_hash( hash, 1 ); 77 mutex_bucket_unlock_by_hash(hash, 1);
72} 78}
73 79
74size_t add_peer_to_torrent_and_return_peers( PROTO_FLAG proto, struct ot_workstruct *ws, size_t amount ) { 80size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstruct *ws, size_t amount) {
75 int exactmatch, delta_torrentcount = 0; 81 int exactmatch, delta_torrentcount = 0;
76 ot_torrent *torrent; 82 ot_torrent *torrent;
77 ot_peer *peer_dest; 83 ot_peer *peer_dest;
78 ot_vector *torrents_list = mutex_bucket_lock_by_hash( *ws->hash ); 84 ot_vector *torrents_list = mutex_bucket_lock_by_hash(*ws->hash);
85 ot_peerlist *peer_list;
86 size_t peer_size; /* initialized in next line */
87 ot_peer const *peer_src = peer_from_peer6(&ws->peer, &peer_size);
79 88
80 if( !accesslist_hashisvalid( *ws->hash ) ) { 89 if (!accesslist_hashisvalid(*ws->hash)) {
81 mutex_bucket_unlock_by_hash( *ws->hash, 0 ); 90 mutex_bucket_unlock_by_hash(*ws->hash, 0);
82 if( proto == FLAG_TCP ) { 91 if (proto == FLAG_TCP) {
83 const char invalid_hash[] = "d14:failure reason63:Requested download is not authorized for use with this tracker.e"; 92 const char invalid_hash[] = "d14:failure reason63:Requested download is not authorized for use with this tracker.e";
84 memcpy( ws->reply, invalid_hash, strlen( invalid_hash ) ); 93 memcpy(ws->reply, invalid_hash, strlen(invalid_hash));
85 return strlen( invalid_hash ); 94 return strlen(invalid_hash);
86 } 95 }
87 return 0; 96 return 0;
88 } 97 }
89 98
90 torrent = vector_find_or_insert( torrents_list, (void*)ws->hash, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 99 torrent = vector_find_or_insert(torrents_list, (void *)ws->hash, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch);
91 if( !torrent ) { 100 if (!torrent) {
92 mutex_bucket_unlock_by_hash( *ws->hash, 0 ); 101 mutex_bucket_unlock_by_hash(*ws->hash, 0);
93 return 0; 102 return 0;
94 } 103 }
95 104
96 if( !exactmatch ) { 105 if (!exactmatch) {
97 /* Create a new torrent entry, then */ 106 /* Create a new torrent entry, then */
98 memcpy( torrent->hash, *ws->hash, sizeof(ot_hash) ); 107 memset(torrent, 0, sizeof(ot_torrent));
108 memcpy(torrent->hash, *ws->hash, sizeof(ot_hash));
99 109
100 if( !( torrent->peer_list = malloc( sizeof (ot_peerlist) ) ) ) { 110 if (!(torrent->peer_list6 = malloc(sizeof(ot_peerlist))) || !(torrent->peer_list4 = malloc(sizeof(ot_peerlist)))) {
101 vector_remove_torrent( torrents_list, torrent ); 111 vector_remove_torrent(torrents_list, torrent);
102 mutex_bucket_unlock_by_hash( *ws->hash, 0 ); 112 mutex_bucket_unlock_by_hash(*ws->hash, 0);
103 return 0; 113 return 0;
104 } 114 }
105 115
106 byte_zero( torrent->peer_list, sizeof( ot_peerlist ) ); 116 memset(torrent->peer_list6, 0, sizeof(ot_peerlist));
117 memset(torrent->peer_list4, 0, sizeof(ot_peerlist));
107 delta_torrentcount = 1; 118 delta_torrentcount = 1;
108 } else 119 } else
109 clean_single_torrent( torrent ); 120 clean_single_torrent(torrent);
110 121
111 torrent->peer_list->base = g_now_minutes; 122 torrent->peer_list6->base = g_now_minutes;
123 torrent->peer_list4->base = g_now_minutes;
124
125 peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4;
112 126
113 /* Check for peer in torrent */ 127 /* Check for peer in torrent */
114 peer_dest = vector_find_or_insert_peer( &(torrent->peer_list->peers), &ws->peer, &exactmatch ); 128 peer_dest = vector_find_or_insert_peer(&(peer_list->peers), peer_src, peer_size, &exactmatch);
115 if( !peer_dest ) { 129 if (!peer_dest) {
116 mutex_bucket_unlock_by_hash( *ws->hash, delta_torrentcount ); 130 mutex_bucket_unlock_by_hash(*ws->hash, delta_torrentcount);
117 return 0; 131 return 0;
118 } 132 }
119 133
120 /* Tell peer that it's fresh */ 134 /* Tell peer that it's fresh */
121 OT_PEERTIME( &ws->peer ) = 0; 135 OT_PEERTIME(ws->peer, OT_PEER_SIZE6) = 0;
122 136
123 /* Sanitize flags: Whoever claims to have completed download, must be a seeder */ 137 /* Sanitize flags: Whoever claims to have completed download, must be a seeder */
124 if( ( OT_PEERFLAG( &ws->peer ) & ( PEER_FLAG_COMPLETED | PEER_FLAG_SEEDING ) ) == PEER_FLAG_COMPLETED ) 138 if ((OT_PEERFLAG(ws->peer) & (PEER_FLAG_COMPLETED | PEER_FLAG_SEEDING)) == PEER_FLAG_COMPLETED)
125 OT_PEERFLAG( &ws->peer ) ^= PEER_FLAG_COMPLETED; 139 OT_PEERFLAG(ws->peer) ^= PEER_FLAG_COMPLETED;
126 140
127 /* If we hadn't had a match create peer there */ 141 /* If we hadn't had a match create peer there */
128 if( !exactmatch ) { 142 if (!exactmatch) {
129 143
130#ifdef WANT_SYNC_LIVE 144#ifdef WANT_SYNC_LIVE
131 if( proto == FLAG_MCA ) 145 if (proto == FLAG_MCA)
132 OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_FROM_SYNC; 146 OT_PEERFLAG(ws->peer) |= PEER_FLAG_FROM_SYNC;
133 else 147 else
134 livesync_tell( ws ); 148 livesync_tell(ws);
135#endif 149#endif
136 150
137 torrent->peer_list->peer_count++; 151 peer_list->peer_count++;
138 if( OT_PEERFLAG(&ws->peer) & PEER_FLAG_COMPLETED ) { 152 if (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED) {
139 torrent->peer_list->down_count++; 153 peer_list->down_count++;
140 stats_issue_event( EVENT_COMPLETED, 0, (uintptr_t)ws ); 154 stats_issue_event(EVENT_COMPLETED, 0, (uintptr_t)ws);
141 } 155 }
142 if( OT_PEERFLAG(&ws->peer) & PEER_FLAG_SEEDING ) 156 if (OT_PEERFLAG(ws->peer) & PEER_FLAG_SEEDING)
143 torrent->peer_list->seed_count++; 157 peer_list->seed_count++;
144 158
145 } else { 159 } else {
146 stats_issue_event( EVENT_RENEW, 0, OT_PEERTIME( peer_dest ) ); 160 stats_issue_event(EVENT_RENEW, 0, OT_PEERTIME(peer_dest, peer_size));
147#ifdef WANT_SPOT_WOODPECKER 161#ifdef WANT_SPOT_WOODPECKER
148 if( ( OT_PEERTIME(peer_dest) > 0 ) && ( OT_PEERTIME(peer_dest) < 20 ) ) 162 if ((OT_PEERTIME(peer_dest, peer_size) > 0) && (OT_PEERTIME(peer_dest, peer_size) < 20))
149 stats_issue_event( EVENT_WOODPECKER, 0, (uintptr_t)&ws->peer ); 163 stats_issue_event(EVENT_WOODPECKER, 0, (uintptr_t)&ws->peer);
150#endif 164#endif
151#ifdef WANT_SYNC_LIVE 165#ifdef WANT_SYNC_LIVE
152 /* Won't live sync peers that come back too fast. Only exception: 166 /* Won't live sync peers that come back too fast. Only exception:
153 fresh "completed" reports */ 167 fresh "completed" reports */
154 if( proto != FLAG_MCA ) { 168 if (proto != FLAG_MCA) {
155 if( OT_PEERTIME( peer_dest ) > OT_CLIENT_SYNC_RENEW_BOUNDARY || 169 if (OT_PEERTIME(peer_dest, peer_size) > OT_CLIENT_SYNC_RENEW_BOUNDARY ||
156 ( !(OT_PEERFLAG(peer_dest) & PEER_FLAG_COMPLETED ) && (OT_PEERFLAG(&ws->peer) & PEER_FLAG_COMPLETED ) ) ) 170 (!(OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED) && (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED)))
157 livesync_tell( ws ); 171 livesync_tell(ws);
158 } 172 }
159#endif 173#endif
160 174
161 if( (OT_PEERFLAG(peer_dest) & PEER_FLAG_SEEDING ) && !(OT_PEERFLAG(&ws->peer) & PEER_FLAG_SEEDING ) ) 175 if ((OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_SEEDING) && !(OT_PEERFLAG(ws->peer) & PEER_FLAG_SEEDING))
162 torrent->peer_list->seed_count--; 176 peer_list->seed_count--;
163 if( !(OT_PEERFLAG(peer_dest) & PEER_FLAG_SEEDING ) && (OT_PEERFLAG(&ws->peer) & PEER_FLAG_SEEDING ) ) 177 if (!(OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_SEEDING) && (OT_PEERFLAG(ws->peer) & PEER_FLAG_SEEDING))
164 torrent->peer_list->seed_count++; 178 peer_list->seed_count++;
165 if( !(OT_PEERFLAG(peer_dest) & PEER_FLAG_COMPLETED ) && (OT_PEERFLAG(&ws->peer) & PEER_FLAG_COMPLETED ) ) { 179 if (!(OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED) && (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED)) {
166 torrent->peer_list->down_count++; 180 peer_list->down_count++;
167 stats_issue_event( EVENT_COMPLETED, 0, (uintptr_t)ws ); 181 stats_issue_event(EVENT_COMPLETED, 0, (uintptr_t)ws);
168 } 182 }
169 if( OT_PEERFLAG(peer_dest) & PEER_FLAG_COMPLETED ) 183 if (OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED)
170 OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_COMPLETED; 184 OT_PEERFLAG(ws->peer) |= PEER_FLAG_COMPLETED;
171 } 185 }
172 186
173 memcpy( peer_dest, &ws->peer, sizeof(ot_peer) ); 187 memcpy(peer_dest, peer_src, peer_size);
174#ifdef WANT_SYNC 188#ifdef WANT_SYNC
175 if( proto == FLAG_MCA ) { 189 if (proto == FLAG_MCA) {
176 mutex_bucket_unlock_by_hash( *ws->hash, delta_torrentcount ); 190 mutex_bucket_unlock_by_hash(*ws->hash, delta_torrentcount);
177 return 0; 191 return 0;
178 } 192 }
179#endif 193#endif
180 194
181 ws->reply_size = return_peers_for_torrent( ws, torrent, amount, ws->reply, proto ); 195 ws->reply_size = return_peers_for_torrent(ws, torrent, amount, ws->reply, proto);
182 mutex_bucket_unlock_by_hash( *ws->hash, delta_torrentcount ); 196 mutex_bucket_unlock_by_hash(*ws->hash, delta_torrentcount);
183 return ws->reply_size; 197 return ws->reply_size;
184} 198}
185 199
186static size_t return_peers_all( ot_peerlist *peer_list, char *reply ) { 200static size_t return_peers_all(ot_peerlist *peer_list, size_t peer_size, char *reply) {
187 unsigned int bucket, num_buckets = 1; 201 size_t bucket, num_buckets = 1;
188 ot_vector * bucket_list = &peer_list->peers; 202 ot_vector *bucket_list = &peer_list->peers;
189 size_t result = OT_PEER_COMPARE_SIZE * peer_list->peer_count; 203 size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
190 char * r_end = reply + result; 204 size_t result = compare_size * peer_list->peer_count;
205 char *r_end = reply + result;
191 206
192 if( OT_PEERLIST_HASBUCKETS(peer_list) ) { 207 if (OT_PEERLIST_HASBUCKETS(peer_list)) {
193 num_buckets = bucket_list->size; 208 num_buckets = bucket_list->size;
194 bucket_list = (ot_vector *)bucket_list->data; 209 bucket_list = (ot_vector *)bucket_list->data;
195 } 210 }
196 211
197 for( bucket = 0; bucket<num_buckets; ++bucket ) { 212 for (bucket = 0; bucket < num_buckets; ++bucket) {
198 ot_peer * peers = (ot_peer*)bucket_list[bucket].data; 213 ot_peer *peers = bucket_list[bucket].data;
199 size_t peer_count = bucket_list[bucket].size; 214 size_t peer_count = bucket_list[bucket].size;
200 while( peer_count-- ) { 215 while (peer_count--) {
201 if( OT_PEERFLAG(peers) & PEER_FLAG_SEEDING ) { 216 if (OT_PEERFLAG_D(peers, peer_size) & PEER_FLAG_SEEDING) {
202 r_end-=OT_PEER_COMPARE_SIZE; 217 r_end -= compare_size;
203 memcpy(r_end,peers++,OT_PEER_COMPARE_SIZE); 218 memcpy(r_end, peers, compare_size);
204 } else { 219 } else {
205 memcpy(reply,peers++,OT_PEER_COMPARE_SIZE); 220 memcpy(reply, peers, compare_size);
206 reply+=OT_PEER_COMPARE_SIZE; 221 reply += compare_size;
207 } 222 }
223 peers += peer_size;
208 } 224 }
209 } 225 }
210 return result; 226 return result;
211} 227}
212 228
213static size_t return_peers_selection( struct ot_workstruct *ws, ot_peerlist *peer_list, size_t amount, char *reply ) { 229static size_t return_peers_selection(struct ot_workstruct *ws, ot_peerlist *peer_list, size_t peer_size, size_t amount, char *reply) {
214 unsigned int bucket_offset, bucket_index = 0, num_buckets = 1; 230 size_t bucket_offset, bucket_index = 0, num_buckets = 1;
215 ot_vector * bucket_list = &peer_list->peers; 231 ot_vector *bucket_list = &peer_list->peers;
216 unsigned int shifted_pc = peer_list->peer_count; 232 unsigned int shifted_pc = peer_list->peer_count;
217 unsigned int shifted_step = 0; 233 unsigned int shifted_step = 0;
218 unsigned int shift = 0; 234 unsigned int shift = 0;
219 size_t result = OT_PEER_COMPARE_SIZE * amount; 235 size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
220 char * r_end = reply + result; 236 size_t result = compare_size * amount;
237 char *r_end = reply + result;
221 238
222 if( OT_PEERLIST_HASBUCKETS(peer_list) ) { 239 if (OT_PEERLIST_HASBUCKETS(peer_list)) {
223 num_buckets = bucket_list->size; 240 num_buckets = bucket_list->size;
224 bucket_list = (ot_vector *)bucket_list->data; 241 bucket_list = (ot_vector *)bucket_list->data;
225 } 242 }
226 243
227 /* Make fixpoint arithmetic as exact as possible */ 244 /* Make fixpoint arithmetic as exact as possible */
228#define MAXPRECBIT (1<<(8*sizeof(int)-3)) 245#define MAXPRECBIT (1 << (8 * sizeof(int) - 3))
229 while( !(shifted_pc & MAXPRECBIT ) ) { shifted_pc <<= 1; shift++; } 246 while (!(shifted_pc & MAXPRECBIT)) {
230 shifted_step = shifted_pc/amount; 247 shifted_pc <<= 1;
248 shift++;
249 }
250 shifted_step = shifted_pc / amount;
231#undef MAXPRECBIT 251#undef MAXPRECBIT
232 252
233 /* Initialize somewhere in the middle of peers so that 253 /* Initialize somewhere in the middle of peers so that
234 fixpoint's aliasing doesn't alway miss the same peers */ 254 fixpoint's aliasing doesn't alway miss the same peers */
235 bucket_offset = nrand48(ws->rand48_state) % peer_list->peer_count; 255 bucket_offset = nrand48(ws->rand48_state) % peer_list->peer_count;
236 256
237 while( amount-- ) { 257 while (amount--) {
238 ot_peer * peer; 258 ot_peer *peer;
239 259
240 /* This is the aliased, non shifted range, next value may fall into */ 260 /* This is the aliased, non shifted range, next value may fall into */
241 unsigned int diff = ( ( ( amount + 1 ) * shifted_step ) >> shift ) - 261 unsigned int diff = (((amount + 1) * shifted_step) >> shift) - ((amount * shifted_step) >> shift);
242 ( ( amount * shifted_step ) >> shift ); 262 bucket_offset += 1 + nrand48(ws->rand48_state) % diff;
243 bucket_offset += 1 + nrand48(ws->rand48_state) % diff;
244 263
245 while( bucket_offset >= bucket_list[bucket_index].size ) { 264 while (bucket_offset >= bucket_list[bucket_index].size) {
246 bucket_offset -= bucket_list[bucket_index].size; 265 bucket_offset -= bucket_list[bucket_index].size;
247 bucket_index = ( bucket_index + 1 ) % num_buckets; 266 bucket_index = (bucket_index + 1) % num_buckets;
248 } 267 }
249 peer = ((ot_peer*)bucket_list[bucket_index].data) + bucket_offset; 268 peer = (ot_peer*)bucket_list[bucket_index].data + peer_size * bucket_offset;
250 if( OT_PEERFLAG(peer) & PEER_FLAG_SEEDING ) { 269 if (OT_PEERFLAG_D(peer, peer_size) & PEER_FLAG_SEEDING) {
251 r_end-=OT_PEER_COMPARE_SIZE; 270 r_end -= compare_size;
252 memcpy(r_end,peer,OT_PEER_COMPARE_SIZE); 271 memcpy(r_end, peer, compare_size);
253 } else { 272 } else {
254 memcpy(reply,peer,OT_PEER_COMPARE_SIZE); 273 memcpy(reply, peer, compare_size);
255 reply+=OT_PEER_COMPARE_SIZE; 274 reply += compare_size;
256 } 275 }
257 } 276 }
258 return result; 277 return result;
259} 278}
260 279
261/* Compiles a list of random peers for a torrent 280static size_t return_peers_for_torrent_udp(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply) {
262 * reply must have enough space to hold 92+6*amount bytes 281 char *r = reply;
263 * does not yet check not to return self 282 size_t peer_size = peer_size_from_peer6(&ws->peer);
264*/ 283 ot_peerlist *peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4;
265size_t return_peers_for_torrent( struct ot_workstruct * ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto ) { 284 size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count;
266 ot_peerlist *peer_list = torrent->peer_list; 285 size_t seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count;
267 char *r = reply;
268 286
269 if( amount > peer_list->peer_count ) 287 if (amount > peer_list->peer_count)
270 amount = peer_list->peer_count; 288 amount = peer_list->peer_count;
271 289
272 if( proto == FLAG_TCP ) { 290 *(uint32_t *)(r + 0) = htonl(OT_CLIENT_REQUEST_INTERVAL_RANDOM);
273 int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM; 291 *(uint32_t *)(r + 4) = htonl(peer_count - seed_count);
274 r += sprintf( r, "d8:completei%zde10:downloadedi%zde10:incompletei%zde8:intervali%ie12:min intervali%ie" PEERS_BENCODED "%zd:", peer_list->seed_count, peer_list->down_count, peer_list->peer_count-peer_list->seed_count, erval, erval/2, OT_PEER_COMPARE_SIZE*amount ); 292 *(uint32_t *)(r + 8) = htonl(seed_count);
275 } else { 293 r += 12;
276 *(uint32_t*)(r+0) = htonl( OT_CLIENT_REQUEST_INTERVAL_RANDOM ); 294
277 *(uint32_t*)(r+4) = htonl( peer_list->peer_count - peer_list->seed_count ); 295 if (amount) {
278 *(uint32_t*)(r+8) = htonl( peer_list->seed_count ); 296 if (amount == peer_list->peer_count)
279 r += 12; 297 r += return_peers_all(peer_list, peer_size, r);
298 else
299 r += return_peers_selection(ws, peer_list, peer_size, amount, r);
300 }
301 return r - reply;
302}
303
304static size_t return_peers_for_torrent_tcp(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply) {
305 char *r = reply;
306 int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM;
307 size_t seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count;
308 size_t down_count = torrent->peer_list6->down_count + torrent->peer_list4->down_count;
309 size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count - seed_count;
310
311 /* Simple case: amount of peers in both lists is less than requested, here we return all results */
312 size_t amount_v4 = torrent->peer_list4->peer_count;
313 size_t amount_v6 = torrent->peer_list6->peer_count;
314
315 /* Complex case: both lists have more than enough entries and we need to split between v4 and v6 clients */
316 if (amount_v4 + amount_v6 > amount) {
317 size_t amount_left, percent_v6 = 0, percent_v4 = 0, left_v6, left_v4;
318 const size_t SCALE = 1024;
319
320 /* If possible, fill at least a quarter of peer from each family */
321 if (amount / 4 <= amount_v4)
322 amount_v4 = amount / 4;
323 if (amount / 4 <= amount_v6)
324 amount_v6 = amount / 4;
325
326 /* Fill the rest according to which family's pool provides more peers */
327 amount_left = amount - (amount_v4 + amount_v6);
328
329 left_v4 = torrent->peer_list4->peer_count - amount_v4;
330 left_v6 = torrent->peer_list6->peer_count - amount_v6;
331
332 if (left_v4 + left_v6) {
333 percent_v4 = (SCALE * left_v4) / (left_v4 + left_v6);
334 percent_v6 = (SCALE * left_v6) / (left_v4 + left_v6);
335 }
336
337 amount_v4 += (amount_left * percent_v4) / SCALE;
338 amount_v6 += (amount_left * percent_v6) / SCALE;
339
340 /* Integer division rounding can leave out a peer */
341 if (amount_v4 + amount_v6 < amount && amount_v6 < torrent->peer_list6->peer_count)
342 ++amount_v6;
343 if (amount_v4 + amount_v6 < amount && amount_v4 < torrent->peer_list4->peer_count)
344 ++amount_v4;
280 } 345 }
281 346
282 if( amount ) { 347 r +=
283 if( amount == peer_list->peer_count ) 348 sprintf(r, "d8:completei%zde10:downloadedi%zde10:incompletei%zde8:intervali%ie12:min intervali%ie", seed_count, down_count, peer_count, erval, erval / 2);
284 r += return_peers_all( peer_list, r ); 349
350 if (amount_v4) {
351 r += sprintf(r, PEERS_BENCODED4 "%zd:", OT_PEER_COMPARE_SIZE4 * amount_v4);
352 if (amount_v4 == torrent->peer_list4->peer_count)
353 r += return_peers_all(torrent->peer_list4, OT_PEER_SIZE4, r);
354 else
355 r += return_peers_selection(ws, torrent->peer_list4, OT_PEER_SIZE4, amount_v4, r);
356 }
357
358 if (amount_v6) {
359 r += sprintf(r, PEERS_BENCODED6 "%zd:", OT_PEER_COMPARE_SIZE6 * amount_v6);
360 if (amount_v6 == torrent->peer_list6->peer_count)
361 r += return_peers_all(torrent->peer_list6, OT_PEER_SIZE6, r);
285 else 362 else
286 r += return_peers_selection( ws, peer_list, amount, r ); 363 r += return_peers_selection(ws, torrent->peer_list6, OT_PEER_SIZE6, amount_v6, r);
287 } 364 }
288 365
289 if( proto == FLAG_TCP ) 366 *r++ = 'e';
290 *r++ = 'e';
291 367
292 return r - reply; 368 return r - reply;
293} 369}
294 370
371/* Compiles a list of random peers for a torrent
372 * Reply must have enough space to hold:
373 * 92 + 6 * amount bytes for TCP/IPv4
374 * 92 + 18 * amount bytes for TCP/IPv6
375 * 12 + 6 * amount bytes for UDP/IPv4
376 * 12 + 18 * amount bytes for UDP/IPv6
377 * Does not yet check not to return self
378 */
379size_t return_peers_for_torrent(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto) {
380 return proto == FLAG_TCP ? return_peers_for_torrent_tcp(ws, torrent, amount, reply) : return_peers_for_torrent_udp(ws, torrent, amount, reply);
381}
382
295/* Fetches scrape info for a specific torrent */ 383/* Fetches scrape info for a specific torrent */
296size_t return_udp_scrape_for_torrent( ot_hash hash, char *reply ) { 384size_t return_udp_scrape_for_torrent(ot_hash const hash, char *reply) {
297 int exactmatch, delta_torrentcount = 0; 385 int exactmatch, delta_torrentcount = 0;
298 ot_vector *torrents_list = mutex_bucket_lock_by_hash( hash ); 386 ot_vector *torrents_list = mutex_bucket_lock_by_hash(hash);
299 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 387 ot_torrent *torrent = binary_search(hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch);
300 388
301 if( !exactmatch ) { 389 if (!exactmatch) {
302 memset( reply, 0, 12); 390 memset(reply, 0, 12);
303 } else { 391 } else {
304 uint32_t *r = (uint32_t*) reply; 392 uint32_t *r = (uint32_t *)reply;
305 393
306 if( clean_single_torrent( torrent ) ) { 394 if (clean_single_torrent(torrent)) {
307 vector_remove_torrent( torrents_list, torrent ); 395 vector_remove_torrent(torrents_list, torrent);
308 memset( reply, 0, 12); 396 memset(reply, 0, 12);
309 delta_torrentcount = -1; 397 delta_torrentcount = -1;
310 } else { 398 } else {
311 r[0] = htonl( torrent->peer_list->seed_count ); 399 r[0] = htonl(torrent->peer_list6->seed_count + torrent->peer_list4->seed_count);
312 r[1] = htonl( torrent->peer_list->down_count ); 400 r[1] = htonl(torrent->peer_list6->down_count + torrent->peer_list4->down_count);
313 r[2] = htonl( torrent->peer_list->peer_count-torrent->peer_list->seed_count ); 401 r[2] = htonl(torrent->peer_list6->peer_count + torrent->peer_list4->peer_count - torrent->peer_list6->seed_count - torrent->peer_list4->seed_count);
314 } 402 }
315 } 403 }
316 mutex_bucket_unlock_by_hash( hash, delta_torrentcount ); 404 mutex_bucket_unlock_by_hash(hash, delta_torrentcount);
317 return 12; 405 return 12;
318} 406}
319 407
320/* Fetches scrape info for a specific torrent */ 408/* Fetches scrape info for a specific torrent */
321size_t return_tcp_scrape_for_torrent( ot_hash *hash_list, int amount, char *reply ) { 409size_t return_tcp_scrape_for_torrent(ot_hash const *hash_list, size_t amount, char *reply) {
322 char *r = reply; 410 char *r = reply;
323 int exactmatch, i; 411 int exactmatch;
412 size_t i;
324 413
325 r += sprintf( r, "d5:filesd" ); 414 r += sprintf(r, "d5:filesd");
326 415
327 for( i=0; i<amount; ++i ) { 416 for (i = 0; i < amount; ++i) {
328 int delta_torrentcount = 0; 417 int delta_torrentcount = 0;
329 ot_hash *hash = hash_list + i; 418 ot_hash const *hash = hash_list + i;
330 ot_vector *torrents_list = mutex_bucket_lock_by_hash( *hash ); 419 ot_vector *torrents_list = mutex_bucket_lock_by_hash(*hash);
331 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 420 ot_torrent *torrent = binary_search(hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch);
332 421
333 if( exactmatch ) { 422 if (exactmatch) {
334 if( clean_single_torrent( torrent ) ) { 423 if (clean_single_torrent(torrent)) {
335 vector_remove_torrent( torrents_list, torrent ); 424 vector_remove_torrent(torrents_list, torrent);
336 delta_torrentcount = -1; 425 delta_torrentcount = -1;
337 } else { 426 } else {
338 *r++='2';*r++='0';*r++=':'; 427 *r++ = '2';
339 memcpy( r, hash, sizeof(ot_hash) ); r+=sizeof(ot_hash); 428 *r++ = '0';
340 r += sprintf( r, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", 429 *r++ = ':';
341 torrent->peer_list->seed_count, torrent->peer_list->down_count, torrent->peer_list->peer_count-torrent->peer_list->seed_count ); 430 memcpy(r, hash, sizeof(ot_hash));
431 r += sizeof(ot_hash);
432 r += sprintf(r, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", torrent->peer_list6->seed_count + torrent->peer_list4->seed_count,
433 torrent->peer_list6->down_count + torrent->peer_list4->down_count,
434 torrent->peer_list6->peer_count + torrent->peer_list4->peer_count - torrent->peer_list6->seed_count - torrent->peer_list4->seed_count);
342 } 435 }
343 } 436 }
344 mutex_bucket_unlock_by_hash( *hash, delta_torrentcount ); 437 mutex_bucket_unlock_by_hash(*hash, delta_torrentcount);
345 } 438 }
346 439
347 *r++ = 'e'; *r++ = 'e'; 440 *r++ = 'e';
441 *r++ = 'e';
348 return r - reply; 442 return r - reply;
349} 443}
350 444
351static ot_peerlist dummy_list; 445static ot_peerlist dummy_list;
352size_t remove_peer_from_torrent( PROTO_FLAG proto, struct ot_workstruct *ws ) { 446size_t remove_peer_from_torrent(PROTO_FLAG proto, struct ot_workstruct *ws) {
353 int exactmatch; 447 int exactmatch;
354 ot_vector *torrents_list = mutex_bucket_lock_by_hash( *ws->hash ); 448 ot_vector *torrents_list = mutex_bucket_lock_by_hash(*ws->hash);
355 ot_torrent *torrent = binary_search( ws->hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 449 ot_torrent *torrent = binary_search(ws->hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch);
356 ot_peerlist *peer_list = &dummy_list; 450 ot_peerlist *peer_list = &dummy_list;
451 size_t peer_size; /* initialized in next line */
452 ot_peer const *peer_src = peer_from_peer6(&ws->peer, &peer_size);
453 size_t peer_count = 0, seed_count = 0;
357 454
358#ifdef WANT_SYNC_LIVE 455#ifdef WANT_SYNC_LIVE
359 if( proto != FLAG_MCA ) { 456 if (proto != FLAG_MCA) {
360 OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_STOPPED; 457 OT_PEERFLAG(ws->peer) |= PEER_FLAG_STOPPED;
361 livesync_tell( ws ); 458 livesync_tell(ws);
362 } 459 }
363#endif 460#endif
364 461
365 if( exactmatch ) { 462 if (exactmatch) {
366 peer_list = torrent->peer_list; 463 peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4;
367 switch( vector_remove_peer( &peer_list->peers, &ws->peer ) ) { 464 switch (vector_remove_peer(&peer_list->peers, peer_src, peer_size)) {
368 case 2: peer_list->seed_count--; /* Fall throughs intended */ 465 case 2:
369 case 1: peer_list->peer_count--; /* Fall throughs intended */ 466 peer_list->seed_count--; /* Intentional fallthrough */
370 default: break; 467 case 1:
468 peer_list->peer_count--; /* Intentional fallthrough */
469 default:
470 break;
371 } 471 }
472
473 peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count;
474 seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count;
372 } 475 }
373 476
374 if( proto == FLAG_TCP ) { 477 if (proto == FLAG_TCP) {
375 int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM; 478 int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM;
376 ws->reply_size = sprintf( ws->reply, "d8:completei%zde10:incompletei%zde8:intervali%ie12:min intervali%ie" PEERS_BENCODED "0:e", peer_list->seed_count, peer_list->peer_count - peer_list->seed_count, erval, erval / 2 ); 479 ws->reply_size = sprintf(ws->reply, "d8:completei%zde10:incompletei%zde8:intervali%ie12:min intervali%ie%s0:e", seed_count, peer_count - seed_count, erval,
480 erval / 2, peer_size == OT_PEER_SIZE6 ? PEERS_BENCODED6 : PEERS_BENCODED4);
377 } 481 }
378 482
379 /* Handle UDP reply */ 483 /* Handle UDP reply */
380 if( proto == FLAG_UDP ) { 484 if (proto == FLAG_UDP) {
381 ((uint32_t*)ws->reply)[2] = htonl( OT_CLIENT_REQUEST_INTERVAL_RANDOM ); 485 ((uint32_t *)ws->reply)[2] = htonl(OT_CLIENT_REQUEST_INTERVAL_RANDOM);
382 ((uint32_t*)ws->reply)[3] = htonl( peer_list->peer_count - peer_list->seed_count ); 486 ((uint32_t *)ws->reply)[3] = htonl(peer_count - seed_count);
383 ((uint32_t*)ws->reply)[4] = htonl( peer_list->seed_count); 487 ((uint32_t *)ws->reply)[4] = htonl(seed_count);
384 ws->reply_size = 20; 488 ws->reply_size = 20;
385 } 489 }
386 490
387 mutex_bucket_unlock_by_hash( *ws->hash, 0 ); 491 mutex_bucket_unlock_by_hash(*ws->hash, 0);
388 return ws->reply_size; 492 return ws->reply_size;
389} 493}
390 494
391void iterate_all_torrents( int (*for_each)( ot_torrent* torrent, uintptr_t data ), uintptr_t data ) { 495void iterate_all_torrents(int (*for_each)(ot_torrent *torrent, uintptr_t data), uintptr_t data) {
392 int bucket; 496 size_t bucket, j;
393 size_t j;
394 497
395 for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) { 498 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
396 ot_vector *torrents_list = mutex_bucket_lock( bucket ); 499 ot_vector *torrents_list = mutex_bucket_lock(bucket);
397 ot_torrent *torrents = (ot_torrent*)(torrents_list->data); 500 ot_torrent *torrents = (ot_torrent *)(torrents_list->data);
398 501
399 for( j=0; j<torrents_list->size; ++j ) 502 for (j = 0; j < torrents_list->size; ++j)
400 if( for_each( torrents + j, data ) ) 503 if (for_each(torrents + j, data))
401 break; 504 break;
402 505
403 mutex_bucket_unlock( bucket, 0 ); 506 mutex_bucket_unlock(bucket, 0);
404 if( !g_opentracker_running ) return; 507 if (!g_opentracker_running)
508 return;
509 }
510}
511
512ot_peer *peer_from_peer6(ot_peer6 *peer, size_t *peer_size) {
513 ot_ip6 *ip = (ot_ip6 *)peer;
514 if (!ip_is_v4mapped(ip)) {
515 *peer_size = OT_PEER_SIZE6;
516 return (ot_peer *)peer;
517 }
518 *peer_size = OT_PEER_SIZE4;
519 return (ot_peer *)(((uint8_t *)peer) + 12);
520}
521
522size_t peer_size_from_peer6(ot_peer6 *peer) {
523 ot_ip6 *ip = (ot_ip6 *)peer;
524 if (!ip_is_v4mapped(ip))
525 return OT_PEER_SIZE6;
526 return OT_PEER_SIZE4;
527}
528
529#ifdef _DEBUG_RANDOMTORRENTS
530void trackerlogic_add_random_torrents(size_t amount) {
531 struct ot_workstruct ws;
532 memset(&ws, 0, sizeof(ws));
533
534 ws.inbuf = malloc(G_INBUF_SIZE);
535 ws.outbuf = malloc(G_OUTBUF_SIZE);
536 ws.reply = ws.outbuf;
537 ws.hash = (ot_hash *)ws.inbuf;
538
539 while (amount--) {
540 random_bytes(ws.hash, sizeof(ot_hash));
541 random_bytes(&ws.peer, sizeof(ws.peer));
542
543 OT_PEERFLAG(ws.peer) &= PEER_FLAG_SEEDING | PEER_FLAG_COMPLETED | PEER_FLAG_STOPPED;
544
545 add_peer_to_torrent_and_return_peers(FLAG_TCP, &ws, 1);
405 } 546 }
547
548 free(ws.inbuf);
549 free(ws.outbuf);
406} 550}
551#endif
407 552
408void exerr( char * message ) { 553void exerr(char *message) {
409 fprintf( stderr, "%s\n", message ); 554 fprintf(stderr, "%s\n", message);
410 exit( 111 ); 555 exit(111);
411} 556}
412 557
413void trackerlogic_init( ) { 558void trackerlogic_init(void) {
414 g_tracker_id = random(); 559 g_tracker_id = random_uint32();
415 560
416 if( !g_stats_path ) 561 if (!g_stats_path)
417 g_stats_path = "stats"; 562 g_stats_path = "stats";
418 g_stats_path_len = strlen( g_stats_path ); 563 g_stats_path_len = strlen(g_stats_path);
419 564
420 /* Initialise background worker threads */ 565 /* Initialise background worker threads */
421 mutex_init( ); 566 mutex_init();
422 clean_init( ); 567 clean_init();
423 fullscrape_init( ); 568 fullscrape_init();
424 accesslist_init( ); 569 accesslist_init();
425 livesync_init( ); 570 livesync_init();
426 stats_init( ); 571 stats_init();
572 udp_kickoff();
427} 573}
428 574
429void trackerlogic_deinit( void ) { 575void trackerlogic_deinit(void) {
430 int bucket, delta_torrentcount = 0; 576 int delta_torrentcount = 0;
431 size_t j; 577 size_t bucket, j;
432 578
433 /* Free all torrents... */ 579 /* Free all torrents... */
434 for(bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) { 580 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
435 ot_vector *torrents_list = mutex_bucket_lock( bucket ); 581 ot_vector *torrents_list = mutex_bucket_lock(bucket);
436 if( torrents_list->size ) { 582 if (torrents_list->size) {
437 for( j=0; j<torrents_list->size; ++j ) { 583 for (j = 0; j < torrents_list->size; ++j) {
438 ot_torrent *torrent = ((ot_torrent*)(torrents_list->data)) + j; 584 ot_torrent *torrent = ((ot_torrent *)(torrents_list->data)) + j;
439 free_peerlist( torrent->peer_list ); 585 free_peerlist(torrent->peer_list6);
586 free_peerlist(torrent->peer_list4);
440 delta_torrentcount -= 1; 587 delta_torrentcount -= 1;
441 } 588 }
442 free( torrents_list->data ); 589 free(torrents_list->data);
443 } 590 }
444 mutex_bucket_unlock( bucket, delta_torrentcount ); 591 mutex_bucket_unlock(bucket, delta_torrentcount);
445 } 592 }
446 593
447 /* Deinitialise background worker threads */ 594 /* Deinitialise background worker threads */
448 stats_deinit( ); 595 stats_deinit();
449 livesync_deinit( ); 596 livesync_deinit();
450 accesslist_deinit( ); 597 accesslist_deinit();
451 fullscrape_deinit( ); 598 fullscrape_deinit();
452 clean_deinit( ); 599 clean_deinit();
453 /* Release mutexes */ 600 /* Release mutexes */
454 mutex_deinit( ); 601 mutex_deinit();
455} 602}
456
457const char *g_version_trackerlogic_c = "$Source$: $Revision$\n";
diff --git a/trackerlogic.h b/trackerlogic.h
index 87b9138..aa10a54 100644
--- a/trackerlogic.h
+++ b/trackerlogic.h
@@ -6,134 +6,148 @@
6#ifndef OT_TRACKERLOGIC_H__ 6#ifndef OT_TRACKERLOGIC_H__
7#define OT_TRACKERLOGIC_H__ 7#define OT_TRACKERLOGIC_H__
8 8
9#include <sys/types.h>
10#include <sys/time.h>
11#include <time.h>
12#include <stdint.h> 9#include <stdint.h>
13#include <stdlib.h> 10#include <stdlib.h>
14 11#include <sys/time.h>
15#if defined(__linux__) && defined(WANT_ARC4RANDOM) 12#include <sys/types.h>
16#include <bsd/stdlib.h> 13#include <time.h>
17#endif
18#ifdef __FreeBSD__
19#define WANT_ARC4RANDOM
20#endif
21 14
22typedef uint8_t ot_hash[20]; 15typedef uint8_t ot_hash[20];
23typedef time_t ot_time; 16typedef time_t ot_time;
24typedef char ot_ip6[16]; 17typedef char ot_ip6[16];
25typedef struct { ot_ip6 address; int bits; } 18typedef struct {
26 ot_net; 19 ot_ip6 address;
27#ifdef WANT_V6 20 int bits;
28#define OT_IP_SIZE 16 21} ot_net;
29#define PEERS_BENCODED "6:peers6" 22/* List of peers should fit in a single UDP packet (around 1200 bytes) */
30#else 23#define OT_MAX_PEERS_UDP6 66
31#define OT_IP_SIZE 4 24#define OT_MAX_PEERS_UDP4 200
32#define PEERS_BENCODED "5:peers" 25
33#endif 26#define OT_IP_SIZE6 16
27#define OT_IP_SIZE4 4
28#define OT_PORT_SIZE 2
29#define OT_FLAG_SIZE 1
30#define OT_TIME_SIZE 1
34 31
35/* Some tracker behaviour tunable */ 32/* Some tracker behaviour tunable */
36#define OT_CLIENT_TIMEOUT 30 33#define OT_CLIENT_TIMEOUT 30
37#define OT_CLIENT_TIMEOUT_CHECKINTERVAL 10 34#define OT_CLIENT_TIMEOUT_CHECKINTERVAL 10
38#define OT_CLIENT_TIMEOUT_SEND (60*15) 35#define OT_CLIENT_TIMEOUT_SEND (60 * 15)
39#define OT_CLIENT_REQUEST_INTERVAL (60*30) 36#define OT_CLIENT_REQUEST_INTERVAL (60 * 30)
40#define OT_CLIENT_REQUEST_VARIATION (60*6) 37#define OT_CLIENT_REQUEST_VARIATION (60 * 6)
41 38
42#define OT_TORRENT_TIMEOUT_HOURS 24 39#define OT_TORRENT_TIMEOUT_HOURS 24
43#define OT_TORRENT_TIMEOUT (60*OT_TORRENT_TIMEOUT_HOURS) 40#define OT_TORRENT_TIMEOUT (60 * OT_TORRENT_TIMEOUT_HOURS)
44 41
45#define OT_CLIENT_REQUEST_INTERVAL_RANDOM ( OT_CLIENT_REQUEST_INTERVAL - OT_CLIENT_REQUEST_VARIATION/2 + (int)( nrand48(ws->rand48_state) % OT_CLIENT_REQUEST_VARIATION ) ) 42#define OT_CLIENT_REQUEST_INTERVAL_RANDOM \
43 (OT_CLIENT_REQUEST_INTERVAL - OT_CLIENT_REQUEST_VARIATION / 2 + (int)(nrand48(ws->rand48_state) % OT_CLIENT_REQUEST_VARIATION))
46 44
47/* If WANT_MODEST_FULLSCRAPES is on, ip addresses may not 45/* If WANT_MODEST_FULLSCRAPES is on, ip addresses may not
48 fullscrape more frequently than this amount in seconds */ 46 fullscrape more frequently than this amount in seconds */
49#define OT_MODEST_PEER_TIMEOUT (60*5) 47#define OT_MODEST_PEER_TIMEOUT (60 * 5)
50 48
51/* If peers come back before 10 minutes, don't live sync them */ 49/* If peers come back before 10 minutes, don't live sync them */
52#define OT_CLIENT_SYNC_RENEW_BOUNDARY 10 50#define OT_CLIENT_SYNC_RENEW_BOUNDARY 10
53 51
54/* Number of tracker admin ip addresses allowed */ 52/* Number of tracker admin ip addresses allowed */
55#define OT_ADMINIP_MAX 64 53#define OT_ADMINIP_MAX 64
56#define OT_MAX_THREADS 64 54#define OT_MAX_THREADS 64
57 55
58#define OT_PEER_TIMEOUT 45 56/* Number of minutes after announce before peer is removed */
57#define OT_PEER_TIMEOUT 45
59 58
60/* We maintain a list of 1024 pointers to sorted list of ot_torrent structs 59/* We maintain a list of 1024 pointers to sorted list of ot_torrent structs
61 Sort key is, of course, its hash */ 60 Sort key is, of course, its hash */
62#define OT_BUCKET_COUNT_BITS 10 61#define OT_BUCKET_COUNT_BITS 10
63 62
64#define OT_BUCKET_COUNT (1<<OT_BUCKET_COUNT_BITS) 63#define OT_BUCKET_COUNT (1 << OT_BUCKET_COUNT_BITS)
65#define OT_BUCKET_COUNT_SHIFT (32-OT_BUCKET_COUNT_BITS) 64#define OT_BUCKET_COUNT_SHIFT (32 - OT_BUCKET_COUNT_BITS)
65
66/* if _DEBUG_RANDOMTORRENTS is set, this is the amount of torrents to create
67 on startup */
68#define RANDOMTORRENTS (1024 * 1024 * 1)
66 69
67/* From opentracker.c */ 70/* From opentracker.c */
68extern time_t g_now_seconds; 71extern time_t g_now_seconds;
69extern volatile int g_opentracker_running; 72extern volatile int g_opentracker_running;
70#define g_now_minutes (g_now_seconds/60) 73#define g_now_minutes (g_now_seconds / 60)
71 74
72extern uint32_t g_tracker_id; 75extern uint32_t g_tracker_id;
73typedef enum { FLAG_TCP, FLAG_UDP, FLAG_MCA, FLAG_SELFPIPE } PROTO_FLAG; 76typedef enum { FLAG_TCP, FLAG_UDP, FLAG_MCA, FLAG_SELFPIPE } PROTO_FLAG;
74 77
75typedef struct { 78#define OT_PEER_COMPARE_SIZE6 ((OT_IP_SIZE6) + (OT_PORT_SIZE))
76 uint8_t data[OT_IP_SIZE+2+2]; 79#define OT_PEER_COMPARE_SIZE4 ((OT_IP_SIZE4) + (OT_PORT_SIZE))
77} ot_peer; 80#define OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(PEER_SIZE) ((PEER_SIZE) - (OT_TIME_SIZE) - (OT_FLAG_SIZE))
81
82#define OT_PEER_SIZE6 ((OT_TIME_SIZE) + (OT_FLAG_SIZE) + (OT_PEER_COMPARE_SIZE6))
83#define OT_PEER_SIZE4 ((OT_TIME_SIZE) + (OT_FLAG_SIZE) + (OT_PEER_COMPARE_SIZE4))
84
85typedef uint8_t ot_peer; /* Generic pointer to a v6 or v4 peer */
86typedef uint8_t ot_peer6[OT_PEER_SIZE6];
87typedef uint8_t ot_peer4[OT_PEER_SIZE4];
78static const uint8_t PEER_FLAG_SEEDING = 0x80; 88static const uint8_t PEER_FLAG_SEEDING = 0x80;
79static const uint8_t PEER_FLAG_COMPLETED = 0x40; 89static const uint8_t PEER_FLAG_COMPLETED = 0x40;
80static const uint8_t PEER_FLAG_STOPPED = 0x20; 90static const uint8_t PEER_FLAG_STOPPED = 0x20;
81static const uint8_t PEER_FLAG_FROM_SYNC = 0x10; 91static const uint8_t PEER_FLAG_FROM_SYNC = 0x10;
82static const uint8_t PEER_FLAG_LEECHING = 0x00; 92static const uint8_t PEER_FLAG_LEECHING = 0x00;
83 93
84#ifdef WANT_V6 94/* Takes an ot_peer6 and returns the proper pointer to the peer and sets peer_size */
85#define OT_SETIP(peer,ip) memcpy((peer),(ip),(OT_IP_SIZE)) 95ot_peer *peer_from_peer6(ot_peer6 *peer, size_t *peer_size);
86#else 96size_t peer_size_from_peer6(ot_peer6 *peer);
87#define OT_SETIP(peer,ip) memcpy((peer),(((uint8_t*)ip)+12),(OT_IP_SIZE)) 97
88#endif 98/* New style */
89#define OT_SETPORT(peer,port) memcpy(((uint8_t*)(peer))+(OT_IP_SIZE),(port),2) 99#define OT_SETIP(peer, ip) memcpy((uint8_t *)(peer), (ip), OT_IP_SIZE6)
90#define OT_PEERFLAG(peer) (((uint8_t*)(peer))[(OT_IP_SIZE)+2]) 100#define OT_SETPORT(peer, port) memcpy(((uint8_t *)(peer)) + (OT_IP_SIZE6), (port), 2)
91#define OT_PEERTIME(peer) (((uint8_t*)(peer))[(OT_IP_SIZE)+3]) 101#define OT_PEERFLAG(peer) (((uint8_t *)(peer))[(OT_IP_SIZE6) + 2])
102#define OT_PEERFLAG_D(peer, peersize) (((uint8_t *)(peer))[(peersize) - 2])
103#define OT_PEERTIME(peer, peersize) (((uint8_t *)(peer))[(peersize) - 1])
92 104
93#define OT_HASH_COMPARE_SIZE (sizeof(ot_hash)) 105#define PEERS_BENCODED6 "6:peers6"
94#define OT_PEER_COMPARE_SIZE ((OT_IP_SIZE)+2) 106#define PEERS_BENCODED4 "5:peers"
107
108#define OT_HASH_COMPARE_SIZE (sizeof(ot_hash))
95 109
96struct ot_peerlist; 110struct ot_peerlist;
97typedef struct ot_peerlist ot_peerlist; 111typedef struct ot_peerlist ot_peerlist;
98typedef struct { 112typedef struct {
99 ot_hash hash; 113 ot_hash hash;
100 ot_peerlist *peer_list; 114 ot_peerlist *peer_list6;
115 ot_peerlist *peer_list4;
101} ot_torrent; 116} ot_torrent;
102 117
103#include "ot_vector.h" 118#include "ot_vector.h"
104 119
105struct ot_peerlist { 120struct ot_peerlist {
106 ot_time base; 121 ot_time base;
107 size_t seed_count; 122 size_t seed_count;
108 size_t peer_count; 123 size_t peer_count;
109 size_t down_count; 124 size_t down_count;
110/* normal peers vector or 125 /* normal peers vector or
111 pointer to ot_vector[32] buckets if data != NULL and space == 0 126 pointer to ot_vector[32] buckets if data != NULL and space == 0
112*/ 127 */
113 ot_vector peers; 128 ot_vector peers;
114}; 129};
115#define OT_PEERLIST_HASBUCKETS(peer_list) ((peer_list)->peers.size > (peer_list)->peers.space) 130#define OT_PEERLIST_HASBUCKETS(peer_list) ((peer_list)->peers.size > (peer_list)->peers.space)
116 131
117struct ot_workstruct { 132struct ot_workstruct {
118 /* Thread specific, static */ 133 /* Thread specific, static */
119 char *inbuf; 134 char *inbuf;
120#define G_INBUF_SIZE 8192 135#define G_INBUF_SIZE 8192
121 char *outbuf; 136 char *outbuf;
122#define G_OUTBUF_SIZE 8192 137#define G_OUTBUF_SIZE 8192
123#ifdef _DEBUG_HTTPERROR 138#ifdef _DEBUG_HTTPERROR
124 char *debugbuf; 139 char *debugbuf;
125#define G_DEBUGBUF_SIZE 8192 140#define G_DEBUGBUF_SIZE 8192
126#endif 141#endif
127 142
128 /* The peer currently in the working */ 143 /* The peer currently in the working */
129 ot_peer peer; 144 ot_peer6 peer; /* Can fit v6 and v4 peers */
130 145
131 /* Pointers into the request buffer */ 146 /* Pointers into the request buffer */
132 ot_hash *hash; 147 ot_hash *hash;
133 char *peer_id; 148 char *peer_id;
134 149
135 /* HTTP specific, non static */ 150 /* HTTP specific, non static */
136 int keep_alive;
137 char *request; 151 char *request;
138 ssize_t request_size; 152 ssize_t request_size;
139 ssize_t header_size; 153 ssize_t header_size;
@@ -143,6 +157,8 @@ struct ot_workstruct {
143 /* Entropy state for rand48 function so that threads don't need to acquire mutexes for 157 /* Entropy state for rand48 function so that threads don't need to acquire mutexes for
144 global random() or arc4random() state, which causes heavy load on linuxes */ 158 global random() or arc4random() state, which causes heavy load on linuxes */
145 uint16_t rand48_state[3]; 159 uint16_t rand48_state[3];
160
161 int keep_alive;
146}; 162};
147 163
148/* 164/*
@@ -154,31 +170,34 @@ struct ot_workstruct {
154#endif 170#endif
155 171
156#ifdef WANT_SYNC 172#ifdef WANT_SYNC
157#define WANT_SYNC_PARAM( param ) , param 173#define WANT_SYNC_PARAM(param) , param
158#else 174#else
159#define WANT_SYNC_PARAM( param ) 175#define WANT_SYNC_PARAM(param)
160#endif 176#endif
161 177
162#ifdef WANT_LOG_NETWORKS 178#ifdef WANT_LOG_NETWORKS
163#error Live logging networks disabled at the moment. 179#error Live logging networks disabled at the moment.
164#endif 180#endif
165 181
166void trackerlogic_init( ); 182void trackerlogic_init(void);
167void trackerlogic_deinit( void ); 183void trackerlogic_deinit(void);
168void exerr( char * message ); 184void exerr(char *message);
169 185
170/* add_peer_to_torrent does only release the torrent bucket if from_sync is set, 186/* add_peer_to_torrent does only release the torrent bucket if from_sync is set,
171 otherwise it is released in return_peers_for_torrent */ 187 otherwise it is released in return_peers_for_torrent */
172size_t add_peer_to_torrent_and_return_peers( PROTO_FLAG proto, struct ot_workstruct *ws, size_t amount ); 188size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstruct *ws, size_t amount);
173size_t remove_peer_from_torrent( PROTO_FLAG proto, struct ot_workstruct *ws ); 189size_t remove_peer_from_torrent(PROTO_FLAG proto, struct ot_workstruct *ws);
174size_t return_tcp_scrape_for_torrent( ot_hash *hash, int amount, char *reply ); 190size_t return_tcp_scrape_for_torrent(ot_hash const *hash_list, size_t amount, char *reply);
175size_t return_udp_scrape_for_torrent( ot_hash hash, char *reply ); 191size_t return_udp_scrape_for_torrent(ot_hash const hash, char *reply);
176void add_torrent_from_saved_state( ot_hash hash, ot_time base, size_t down_count ); 192void add_torrent_from_saved_state(ot_hash const hash, ot_time base, size_t down_count);
193#ifdef _DEBUG_RANDOMTORRENTS
194void trackerlogic_add_random_torrents(size_t amount);
195#endif
177 196
178/* torrent iterator */ 197/* torrent iterator */
179void iterate_all_torrents( int (*for_each)( ot_torrent* torrent, uintptr_t data ), uintptr_t data ); 198void iterate_all_torrents(int (*for_each)(ot_torrent *torrent, uintptr_t data), uintptr_t data);
180 199
181/* Helper, before it moves to its own object */ 200/* Helper, before it moves to its own object */
182void free_peerlist( ot_peerlist *peer_list ); 201void free_peerlist(ot_peerlist *peer_list);
183 202
184#endif 203#endif