diff options
| -rw-r--r-- | .clang-format | 246 | ||||
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Makefile | 72 | ||||
| -rw-r--r-- | Makefile.gzip | 4 | ||||
| -rw-r--r-- | Makefile.zstd | 3 | ||||
| -rw-r--r-- | man1/opentracker.1 | 130 | ||||
| -rw-r--r-- | man4/opentracker.conf.4 | 86 | ||||
| -rw-r--r-- | opentracker.c | 1029 | ||||
| -rw-r--r-- | opentracker.conf.sample | 52 | ||||
| -rw-r--r-- | ot_accesslist.c | 568 | ||||
| -rw-r--r-- | ot_accesslist.h | 56 | ||||
| -rw-r--r-- | ot_buf.c | 41 | ||||
| -rw-r--r-- | ot_buf.h | 27 | ||||
| -rw-r--r-- | ot_clean.c | 137 | ||||
| -rw-r--r-- | ot_clean.h | 10 | ||||
| -rw-r--r-- | ot_fullscrape.c | 471 | ||||
| -rw-r--r-- | ot_fullscrape.h | 8 | ||||
| -rw-r--r-- | ot_hex.c | 58 | ||||
| -rw-r--r-- | ot_hex.h | 29 | ||||
| -rw-r--r-- | ot_http.c | 864 | ||||
| -rw-r--r-- | ot_http.h | 19 | ||||
| -rw-r--r-- | ot_iovec.c | 78 | ||||
| -rw-r--r-- | ot_iovec.h | 11 | ||||
| -rw-r--r-- | ot_ip.c | 201 | ||||
| -rw-r--r-- | ot_ip.h | 33 | ||||
| -rw-r--r-- | ot_livesync.c | 227 | ||||
| -rw-r--r-- | ot_livesync.h | 20 | ||||
| -rw-r--r-- | ot_mutex.c | 322 | ||||
| -rw-r--r-- | ot_mutex.h | 103 | ||||
| -rw-r--r-- | ot_random.c | 53 | ||||
| -rw-r--r-- | ot_random.h | 19 | ||||
| -rw-r--r-- | ot_rijndael.c | 2 | ||||
| -rw-r--r-- | ot_scan.c | 44 | ||||
| -rw-r--r-- | ot_scan.h | 21 | ||||
| -rw-r--r-- | ot_stats.c | 982 | ||||
| -rw-r--r-- | ot_stats.h | 24 | ||||
| -rw-r--r-- | ot_sync.c | 122 | ||||
| -rw-r--r-- | ot_sync.h | 10 | ||||
| -rw-r--r-- | ot_udp.c | 293 | ||||
| -rw-r--r-- | ot_udp.h | 7 | ||||
| -rw-r--r-- | ot_vector.c | 255 | ||||
| -rw-r--r-- | ot_vector.h | 24 | ||||
| -rw-r--r-- | proxy.c | 876 | ||||
| -rw-r--r-- | scan_urlencoded_query.c | 128 | ||||
| -rw-r--r-- | scan_urlencoded_query.h | 14 | ||||
| -rw-r--r-- | tests/runner.c | 40 | ||||
| -rw-r--r-- | tests/stubs.c | 20 | ||||
| -rw-r--r-- | tests/t_accesslist.c | 122 | ||||
| -rw-r--r-- | tests/t_buf.c | 205 | ||||
| -rw-r--r-- | tests/t_hex.c | 128 | ||||
| -rw-r--r-- | tests/t_iovec.c | 124 | ||||
| -rw-r--r-- | tests/t_ip.c | 217 | ||||
| -rw-r--r-- | tests/t_random.c | 114 | ||||
| -rw-r--r-- | tests/t_scan.c | 162 | ||||
| -rw-r--r-- | tests/t_scan_urlencoded_query.c | 381 | ||||
| -rw-r--r-- | tests/t_vector.c | 161 | ||||
| -rw-r--r-- | tests/test.c | 58 | ||||
| -rw-r--r-- | tests/test.h | 89 | ||||
| -rw-r--r-- | tests/testsuite2.sh | 22 | ||||
| -rw-r--r-- | trackerlogic.c | 651 | ||||
| -rw-r--r-- | trackerlogic.h | 175 |
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 | --- | ||
| 2 | Language: Cpp | ||
| 3 | # BasedOnStyle: LLVM | ||
| 4 | AccessModifierOffset: -2 | ||
| 5 | AlignAfterOpenBracket: Align | ||
| 6 | AlignArrayOfStructures: None | ||
| 7 | AlignConsecutiveAssignments: | ||
| 8 | Enabled: true | ||
| 9 | AcrossEmptyLines: true | ||
| 10 | AcrossComments: true | ||
| 11 | AlignCompound: true | ||
| 12 | AlignFunctionPointers: false | ||
| 13 | PadOperators: true | ||
| 14 | AlignConsecutiveBitFields: | ||
| 15 | Enabled: false | ||
| 16 | AcrossEmptyLines: true | ||
| 17 | AcrossComments: true | ||
| 18 | AlignCompound: false | ||
| 19 | AlignFunctionPointers: false | ||
| 20 | PadOperators: false | ||
| 21 | AlignConsecutiveDeclarations: | ||
| 22 | Enabled: true | ||
| 23 | AcrossEmptyLines: true | ||
| 24 | AcrossComments: true | ||
| 25 | AlignCompound: true | ||
| 26 | AlignFunctionPointers: false | ||
| 27 | PadOperators: true | ||
| 28 | AlignConsecutiveMacros: | ||
| 29 | Enabled: true | ||
| 30 | AcrossEmptyLines: true | ||
| 31 | AcrossComments: true | ||
| 32 | AlignCompound: true | ||
| 33 | AlignFunctionPointers: false | ||
| 34 | PadOperators: false | ||
| 35 | AlignConsecutiveShortCaseStatements: | ||
| 36 | Enabled: true | ||
| 37 | AcrossEmptyLines: true | ||
| 38 | AcrossComments: true | ||
| 39 | AlignCaseColons: false | ||
| 40 | AlignEscapedNewlines: Right | ||
| 41 | AlignOperands: Align | ||
| 42 | AlignTrailingComments: | ||
| 43 | Kind: Always | ||
| 44 | OverEmptyLines: 0 | ||
| 45 | AllowAllArgumentsOnNextLine: true | ||
| 46 | AllowAllParametersOfDeclarationOnNextLine: true | ||
| 47 | AllowBreakBeforeNoexceptSpecifier: Never | ||
| 48 | AllowShortBlocksOnASingleLine: Never | ||
| 49 | AllowShortCaseLabelsOnASingleLine: false | ||
| 50 | AllowShortCompoundRequirementOnASingleLine: true | ||
| 51 | AllowShortEnumsOnASingleLine: true | ||
| 52 | AllowShortFunctionsOnASingleLine: All | ||
| 53 | AllowShortIfStatementsOnASingleLine: Never | ||
| 54 | AllowShortLambdasOnASingleLine: All | ||
| 55 | AllowShortLoopsOnASingleLine: false | ||
| 56 | AlwaysBreakAfterDefinitionReturnType: None | ||
| 57 | AlwaysBreakAfterReturnType: None | ||
| 58 | AlwaysBreakBeforeMultilineStrings: false | ||
| 59 | AlwaysBreakTemplateDeclarations: MultiLine | ||
| 60 | AttributeMacros: | ||
| 61 | - __capability | ||
| 62 | BinPackArguments: true | ||
| 63 | BinPackParameters: true | ||
| 64 | BitFieldColonSpacing: Both | ||
| 65 | BraceWrapping: | ||
| 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 | ||
| 84 | BreakAdjacentStringLiterals: true | ||
| 85 | BreakAfterAttributes: Leave | ||
| 86 | BreakAfterJavaFieldAnnotations: false | ||
| 87 | BreakArrays: true | ||
| 88 | BreakBeforeBinaryOperators: None | ||
| 89 | BreakBeforeConceptDeclarations: Always | ||
| 90 | BreakBeforeBraces: Attach | ||
| 91 | BreakBeforeInlineASMColon: OnlyMultiline | ||
| 92 | BreakBeforeTernaryOperators: true | ||
| 93 | BreakConstructorInitializers: BeforeColon | ||
| 94 | BreakInheritanceList: BeforeColon | ||
| 95 | BreakStringLiterals: false | ||
| 96 | ColumnLimit: 160 | ||
| 97 | CommentPragmas: '^ IWYU pragma:' | ||
| 98 | CompactNamespaces: false | ||
| 99 | ConstructorInitializerIndentWidth: 4 | ||
| 100 | ContinuationIndentWidth: 4 | ||
| 101 | Cpp11BracedListStyle: true | ||
| 102 | DerivePointerAlignment: false | ||
| 103 | DisableFormat: false | ||
| 104 | EmptyLineAfterAccessModifier: Never | ||
| 105 | EmptyLineBeforeAccessModifier: LogicalBlock | ||
| 106 | ExperimentalAutoDetectBinPacking: false | ||
| 107 | FixNamespaceComments: true | ||
| 108 | ForEachMacros: | ||
| 109 | - foreach | ||
| 110 | - Q_FOREACH | ||
| 111 | - BOOST_FOREACH | ||
| 112 | IfMacros: | ||
| 113 | - KJ_IF_MAYBE | ||
| 114 | IncludeBlocks: Preserve | ||
| 115 | IncludeCategories: | ||
| 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 | ||
| 128 | IncludeIsMainRegex: '(Test)?$' | ||
| 129 | IncludeIsMainSourceRegex: '' | ||
| 130 | IndentAccessModifiers: false | ||
| 131 | IndentCaseBlocks: false | ||
| 132 | IndentCaseLabels: false | ||
| 133 | IndentExternBlock: AfterExternBlock | ||
| 134 | IndentGotoLabels: true | ||
| 135 | IndentPPDirectives: None | ||
| 136 | IndentRequiresClause: true | ||
| 137 | IndentWidth: 2 | ||
| 138 | IndentWrappedFunctionNames: false | ||
| 139 | InsertBraces: false | ||
| 140 | InsertNewlineAtEOF: false | ||
| 141 | InsertTrailingCommas: None | ||
| 142 | IntegerLiteralSeparator: | ||
| 143 | Binary: 0 | ||
| 144 | BinaryMinDigits: 0 | ||
| 145 | Decimal: 0 | ||
| 146 | DecimalMinDigits: 0 | ||
| 147 | Hex: 0 | ||
| 148 | HexMinDigits: 0 | ||
| 149 | JavaScriptQuotes: Leave | ||
| 150 | JavaScriptWrapImports: true | ||
| 151 | KeepEmptyLinesAtTheStartOfBlocks: true | ||
| 152 | KeepEmptyLinesAtEOF: false | ||
| 153 | LambdaBodyIndentation: Signature | ||
| 154 | LineEnding: DeriveLF | ||
| 155 | MacroBlockBegin: '' | ||
| 156 | MacroBlockEnd: '' | ||
| 157 | MaxEmptyLinesToKeep: 1 | ||
| 158 | NamespaceIndentation: None | ||
| 159 | ObjCBinPackProtocolList: Auto | ||
| 160 | ObjCBlockIndentWidth: 2 | ||
| 161 | ObjCBreakBeforeNestedBlockParam: true | ||
| 162 | ObjCSpaceAfterProperty: false | ||
| 163 | ObjCSpaceBeforeProtocolList: true | ||
| 164 | PackConstructorInitializers: BinPack | ||
| 165 | PenaltyBreakAssignment: 2 | ||
| 166 | PenaltyBreakBeforeFirstCallParameter: 19 | ||
| 167 | PenaltyBreakComment: 300 | ||
| 168 | PenaltyBreakFirstLessLess: 120 | ||
| 169 | PenaltyBreakOpenParenthesis: 0 | ||
| 170 | PenaltyBreakScopeResolution: 500 | ||
| 171 | PenaltyBreakString: 1000 | ||
| 172 | PenaltyBreakTemplateDeclaration: 10 | ||
| 173 | PenaltyExcessCharacter: 1000000 | ||
| 174 | PenaltyIndentedWhitespace: 0 | ||
| 175 | PenaltyReturnTypeOnItsOwnLine: 60 | ||
| 176 | PointerAlignment: Right | ||
| 177 | PPIndentWidth: -1 | ||
| 178 | QualifierAlignment: Leave | ||
| 179 | ReferenceAlignment: Pointer | ||
| 180 | ReflowComments: true | ||
| 181 | RemoveBracesLLVM: false | ||
| 182 | RemoveParentheses: Leave | ||
| 183 | RemoveSemicolon: false | ||
| 184 | RequiresClausePosition: OwnLine | ||
| 185 | RequiresExpressionIndentation: OuterScope | ||
| 186 | SeparateDefinitionBlocks: Leave | ||
| 187 | ShortNamespaceLines: 1 | ||
| 188 | SkipMacroDefinitionBody: false | ||
| 189 | SortIncludes: CaseSensitive | ||
| 190 | SortJavaStaticImport: Before | ||
| 191 | SortUsingDeclarations: LexicographicNumeric | ||
| 192 | SpaceAfterCStyleCast: false | ||
| 193 | SpaceAfterLogicalNot: false | ||
| 194 | SpaceAfterTemplateKeyword: true | ||
| 195 | SpaceAroundPointerQualifiers: Default | ||
| 196 | SpaceBeforeAssignmentOperators: true | ||
| 197 | SpaceBeforeCaseColon: false | ||
| 198 | SpaceBeforeCpp11BracedList: false | ||
| 199 | SpaceBeforeCtorInitializerColon: true | ||
| 200 | SpaceBeforeInheritanceColon: true | ||
| 201 | SpaceBeforeJsonColon: false | ||
| 202 | SpaceBeforeParens: ControlStatements | ||
| 203 | SpaceBeforeParensOptions: | ||
| 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 | ||
| 214 | SpaceBeforeRangeBasedForLoopColon: true | ||
| 215 | SpaceBeforeSquareBrackets: false | ||
| 216 | SpaceInEmptyBlock: false | ||
| 217 | SpacesBeforeTrailingComments: 1 | ||
| 218 | SpacesInAngles: Never | ||
| 219 | SpacesInContainerLiterals: true | ||
| 220 | SpacesInLineCommentPrefix: | ||
| 221 | Minimum: 1 | ||
| 222 | Maximum: -1 | ||
| 223 | SpacesInParens: Never | ||
| 224 | SpacesInParensOptions: | ||
| 225 | InCStyleCasts: false | ||
| 226 | InConditionalStatements: false | ||
| 227 | InEmptyParentheses: false | ||
| 228 | Other: false | ||
| 229 | SpacesInSquareBrackets: false | ||
| 230 | Standard: Latest | ||
| 231 | StatementAttributeLikeMacros: | ||
| 232 | - Q_EMIT | ||
| 233 | StatementMacros: | ||
| 234 | - Q_UNUSED | ||
| 235 | - QT_REQUIRE_VERSION | ||
| 236 | TabWidth: 8 | ||
| 237 | UseTab: Never | ||
| 238 | VerilogBreakBetweenInstancePorts: true | ||
| 239 | WhitespaceSensitiveMacros: | ||
| 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 | ||
| 2 | tests/runner | ||
| @@ -1,7 +1,5 @@ | |||
| 1 | # $Id$ | 1 | # $Id$ |
| 2 | 2 | ||
| 3 | CC?=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 | |||
| 18 | LIBOWFAT_LIBRARY=$(PREFIX)/libowfat | 16 | LIBOWFAT_LIBRARY=$(PREFIX)/libowfat |
| 19 | 17 | ||
| 20 | BINDIR?=$(PREFIX)/bin | 18 | BINDIR?=$(PREFIX)/bin |
| 19 | STRIP?=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 | ||
| 32 | include Makefile.gzip | ||
| 33 | |||
| 34 | # If you want zstd support to be compiled in, uncomment the next include. | ||
| 35 | # You can further modify the behaviour by setting DWANT_COMPRESSION_ZSTD_ALWAYS | ||
| 36 | # in Makefile.zstd | ||
| 37 | #include Makefile.zstd | ||
| 38 | |||
| 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 |
| 40 | FEATURES+=-DWANT_FULLSCRAPE | 48 | FEATURES+=-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 | |||
| 62 | GIT_VERSION=`sh -c 'command -v git >/dev/null && test -d .git && git rev-parse HEAD || echo _git_or_commit_not_found_'` | ||
| 47 | 63 | ||
| 48 | OPTS_debug=-D_DEBUG -g -ggdb # -pg -fprofile-arcs -ftest-coverage | 64 | OPTS_debug=-D_DEBUG -g -ggdb # -pg -fprofile-arcs -ftest-coverage |
| 49 | OPTS_production=-O3 | 65 | OPTS_production=-O3 |
| 50 | 66 | ||
| 51 | CFLAGS+=-I$(LIBOWFAT_HEADERS) -Wall -pipe -Wextra #-ansi -pedantic | 67 | CFLAGS+=-I$(LIBOWFAT_HEADERS) -DGIT_VERSION=$(GIT_VERSION) -Wall -pipe -pthread -Wextra #-ansi -pedantic |
| 52 | LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread -lpthread -lz | 68 | LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread |
| 53 | #LDFLAGS+=-lbsd | ||
| 54 | 69 | ||
| 55 | BINARY =opentracker | 70 | BINARY =opentracker |
| 56 | HEADERS=trackerlogic.h scan_urlencoded_query.h ot_mutex.h ot_stats.h ot_vector.h ot_clean.h ot_udp.h ot_iovec.h ot_fullscrape.h ot_accesslist.h ot_http.h ot_livesync.h ot_rijndael.h | 71 | HEADERS=trackerlogic.h scan_urlencoded_query.h ot_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 |
| 57 | SOURCES=opentracker.c trackerlogic.c scan_urlencoded_query.c ot_mutex.c ot_stats.c ot_vector.c ot_clean.c ot_udp.c ot_iovec.c ot_fullscrape.c ot_accesslist.c ot_http.c ot_livesync.c ot_rijndael.c | 72 | SOURCES=opentracker.c trackerlogic.c scan_urlencoded_query.c ot_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 |
| 58 | SOURCES_proxy=proxy.c ot_vector.c ot_mutex.c | 73 | SOURCES_proxy=proxy.c ot_vector.c ot_mutex.c ot_random.c ot_scan.c ot_ip.c |
| 59 | 74 | ||
| 60 | OBJECTS = $(SOURCES:%.c=%.o) | 75 | OBJECTS = $(SOURCES:%.c=%.o) |
| 61 | OBJECTS_debug = $(SOURCES:%.c=%.debug.o) | 76 | OBJECTS_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) |
| 77 | proxy: $(OBJECTS_proxy) $(HEADERS) | 92 | proxy: $(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. | ||
| 107 | TESTBIN=tests/runner | ||
| 108 | TEST_SOURCES=tests/runner.c tests/test.c tests/stubs.c tests/t_scan_urlencoded_query.c tests/t_iovec.c tests/t_vector.c tests/t_random.c tests/t_hex.c tests/t_accesslist.c tests/t_scan.c tests/t_buf.c tests/t_ip.c | ||
| 109 | TEST_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 | ||
| 110 | TEST_HEADERS=tests/test.h $(HEADERS) | ||
| 111 | # Tests reach code that sits behind feature flags, so the units under test are | ||
| 112 | # built with those features enabled. A test binary is not a production binary. | ||
| 113 | FEATURES_test=$(FEATURES) -DWANT_ACCESSLIST_BLACK | ||
| 114 | CFLAGS_test = $(CFLAGS) -I. -g -O1 $(FEATURES_test) | ||
| 115 | |||
| 116 | $(TESTBIN): $(TEST_SOURCES) $(TEST_UNITS) $(TEST_HEADERS) | ||
| 117 | $(CC) -o $@ $(CFLAGS_test) $(TEST_SOURCES) $(TEST_UNITS) | ||
| 118 | |||
| 119 | check: $(TESTBIN) | ||
| 120 | ./$(TESTBIN) | ||
| 121 | |||
| 122 | .PHONY: all check clean install | ||
| 123 | |||
| 88 | clean: | 124 | clean: |
| 89 | rm -rf opentracker opentracker.debug *.o *~ | 125 | rm -rf opentracker opentracker.debug $(TESTBIN) *.o *~ |
| 90 | 126 | ||
| 91 | install: | 127 | install: |
| 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 @@ | |||
| 1 | FEATURES+=-DWANT_COMPRESSION_GZIP | ||
| 2 | #FEATURES+=-DWANT_COMPRESSION_GZIP_ALWAYS | ||
| 3 | |||
| 4 | LDFLAGS+=-lz | ||
diff --git a/Makefile.zstd b/Makefile.zstd new file mode 100644 index 0000000..2bb56be --- /dev/null +++ b/Makefile.zstd | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | FEATURES+=-DWANT_COMPRESSION_ZSTD | ||
| 2 | #FEATURES+=-DWANT_COMPRESSION_ZSTD_ALWAYS | ||
| 3 | LDFLAGS+=-lzstd | ||
diff --git a/man1/opentracker.1 b/man1/opentracker.1 new file mode 100644 index 0000000..0103ebe --- /dev/null +++ b/man1/opentracker.1 | |||
| @@ -0,0 +1,130 @@ | |||
| 1 | .Dd 15/4/2024 | ||
| 2 | .Dt opentracker 1 | ||
| 3 | .Os Unix | ||
| 4 | .Sh opentracker | ||
| 5 | .Nm opentracker | ||
| 6 | .Nd a free and open bittorrent tracker | ||
| 7 | .Sh SYNOPSIS | ||
| 8 | .Nm | ||
| 9 | .Op Fl f Ar config | ||
| 10 | .Op Fl i Ar ip-select | ||
| 11 | .Op Fl p Ar port-bind-tcp | ||
| 12 | .Op Fl P Ar port-bind-udp | ||
| 13 | .Op Fl A Ar blessed-ip | ||
| 14 | .Op Fl r Ar redirect-url | ||
| 15 | .Op Fl d Ar chdir | ||
| 16 | .Op Fl u Ar user | ||
| 17 | .Op Fl w| Fl b accesslist | ||
| 18 | .Sh DESCRIPTION | ||
| 19 | .Nm | ||
| 20 | is a bittorrent tracker that implements announce and scrape actions over the | ||
| 21 | UDP and the plain http protocol, aiming for minimal resource usage. | ||
| 22 | .Pp | ||
| 23 | When invoked with parameters, it binds to TCP and UDP port 6969 on all | ||
| 24 | interfaces. The recommended way to configure opentracker is by providing a | ||
| 25 | config file using the | ||
| 26 | .Op Fl f Ar config | ||
| 27 | option. See | ||
| 28 | .Xr opentracker.conf 4 | ||
| 29 | for details. | ||
| 30 | .Pp | ||
| 31 | .Sh OPTIONS | ||
| 32 | The following options are available: | ||
| 33 | .Bl -tag -width -indent=8 | ||
| 34 | .It Fl f Ar config | ||
| 35 | Parse a config file with a list of options. Consecutive command options | ||
| 36 | will override options from the config file. See | ||
| 37 | .Xr opentracker.conf 4 | ||
| 38 | for details. | ||
| 39 | .It Fl i Ar ip-select | ||
| 40 | Select an ip address that will be used with the next | ||
| 41 | .Op Fl p | ||
| 42 | or | ||
| 43 | .Op Fl P | ||
| 44 | command to actually bind to this address. Setting this option without any bind | ||
| 45 | options in the config file or | ||
| 46 | .Op Fl p | ||
| 47 | or | ||
| 48 | .Op Fl P | ||
| 49 | commands will limit opentracker to only bind to this address. | ||
| 50 | .It Fl p Ar port-bind-tcp | ||
| 51 | Bind to the TCP port on the last preceding ip address set with the | ||
| 52 | .Op Fl i ip-select | ||
| 53 | option or to all available addresses if none has been set. Can be given multiple | ||
| 54 | times. | ||
| 55 | .It Fl P Ar port-bind-udp | ||
| 56 | Bind to the UDP port on the last preceding ip address set with the | ||
| 57 | .Op Fl i ip-select | ||
| 58 | option or to all available addresses if none has been set. Can be given multiple | ||
| 59 | times. | ||
| 60 | .It Fl A Ar blessed-ip | ||
| 61 | Set an ip address in IPv4 or IPv6 or a net in CIDR notation to bless the network | ||
| 62 | for access to restricted resources. | ||
| 63 | .It Fl r Ar redirect-url | ||
| 64 | Set the URL that | ||
| 65 | .Nm | ||
| 66 | will redirect users to when the / address is requested via HTTP. | ||
| 67 | .It Fl d Ar chdir | ||
| 68 | Sets the directory | ||
| 69 | .Nm | ||
| 70 | will | ||
| 71 | .Xr chroot 2 | ||
| 72 | to if ran as root or | ||
| 73 | .Xr chdir 2 | ||
| 74 | to if ran as unprivileged user. Note that any accesslist files need to be | ||
| 75 | relative to and within that directory. | ||
| 76 | .It Fl u Ar user | ||
| 77 | User to run | ||
| 78 | .Nm | ||
| 79 | under after all operations that need privileges have finished. | ||
| 80 | .It Fl w Ar accesslist | Fl b Ar accesslist | ||
| 81 | If | ||
| 82 | .Nm | ||
| 83 | has been compiled with the | ||
| 84 | .B WANT_ACCESSLIST_BLACK | ||
| 85 | or | ||
| 86 | .Br WANT_ACCESSLIST_WHITE | ||
| 87 | options, this option sets the location of the accesslist. | ||
| 88 | .El | ||
| 89 | .Sh EXAMPLES | ||
| 90 | Start | ||
| 91 | .Nm | ||
| 92 | bound on UDP and TCP ports 6969 on IPv6 localhost. | ||
| 93 | .Dl # ./opentracker -i ::1 -p 6969 -P 6969 | ||
| 94 | .Pp | ||
| 95 | Start | ||
| 96 | .Nm | ||
| 97 | bound on UDP port 6868 and TCP port 6868 on IPv4 localhost and allow | ||
| 98 | privileged access from the network 192.168/16 while redirecting | ||
| 99 | HTTP clients accessing the root directory, which is not covered by the | ||
| 100 | bittorrent tracker protocol, to https://my-trackersite.com/. | ||
| 101 | .Dl # ./opentracker -i 192.168.0.4 -p 6868 -P 6969 -A 192.168/16 -r https://my-trackersite.com/ | ||
| 102 | The announce URLs are http://192.168.0.4:6868/announce and | ||
| 103 | udp://192.168.0.4:6868/announce respectively. | ||
| 104 | .Sh FILES | ||
| 105 | .Bl -tag -width indent | ||
| 106 | .It Pa opentracker.conf | ||
| 107 | The | ||
| 108 | .Nm | ||
| 109 | config file. | ||
| 110 | .El | ||
| 111 | .Sh SEE ALSO | ||
| 112 | .Xr opentracker.conf 4 | ||
| 113 | .Pp | ||
| 114 | opentracker documentation | ||
| 115 | .Lk https://erdgeist.org/arts/software/opentracker | ||
| 116 | .Pp | ||
| 117 | Bittorrent tracker protocol | ||
| 118 | .Lk http://www.bittorrent.org/beps/bep_0015.html | ||
| 119 | .Sh AUTHOR | ||
| 120 | .An Dirk Engling | ||
| 121 | .Aq Mt erdgeist@erdgeist.org . | ||
| 122 | .Sh LICENSE | ||
| 123 | This software is released under the Beerware License: | ||
| 124 | .Pp | ||
| 125 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software | ||
| 126 | and associated documentation files (the "Software"), to deal in the Software with the following | ||
| 127 | terms and conditions: | ||
| 128 | .Pp | ||
| 129 | If you meet the author(s) someday, and you think this software is worth it, you can buy them | ||
| 130 | a beer in return. | ||
diff --git a/man4/opentracker.conf.4 b/man4/opentracker.conf.4 new file mode 100644 index 0000000..2bc1389 --- /dev/null +++ b/man4/opentracker.conf.4 | |||
| @@ -0,0 +1,86 @@ | |||
| 1 | .Dd 2024-04-18 | ||
| 2 | .Dt opentracker.conf 5 | ||
| 3 | .Os Unix | ||
| 4 | .Sh NAME | ||
| 5 | .Nm opentracker.conf | ||
| 6 | .Nd configuration file for opentracker | ||
| 7 | .Sh SYNOPSIS | ||
| 8 | .Nm | ||
| 9 | .Sh DESCRIPTION | ||
| 10 | The | ||
| 11 | .Nm | ||
| 12 | configuration file specifies various options for configuring the behavior of the opentracker program. | ||
| 13 | .Pp | ||
| 14 | Lines starting with '#' are comments and are ignored. Options are specified as 'keyword value' pairs. | ||
| 15 | .Pp | ||
| 16 | The following options are available: | ||
| 17 | .Pp | ||
| 18 | .Bl -tag -width ".It access.proxy" -compact | ||
| 19 | .It listen.tcp_udp Ar address | ||
| 20 | Specifies an address opentracker will listen on for both TCP and UDP connections. If none are specified, opentracker listens on 0.0.0.0:6969 by default. Can be added more than once. | ||
| 21 | .Pp | ||
| 22 | .It listen.tcp Ar address | ||
| 23 | Specifies the address opentracker will listen on for TCP connections. Can be added more than once. | ||
| 24 | .Pp | ||
| 25 | .It listen.udp Ar address | ||
| 26 | Specifies the address opentracker will listen on for UDP connections. Can be added more than once. | ||
| 27 | .Pp | ||
| 28 | .It listen.udp.workers Ar threads | ||
| 29 | Specifies how many threads will be spawned to handle UDP connections. Defaults to 4. | ||
| 30 | .Pp | ||
| 31 | .It access.whitelist Ar path/to/whitelist | ||
| 32 | Specifies the path to the whitelist file containing all torrent hashes that opentracker will serve. Use this option if opentracker runs in a non-open mode. | ||
| 33 | .Pp | ||
| 34 | .It access.blacklist Ar path/to/blacklist | ||
| 35 | Specifies the path to the blacklist file containing all torrent hashes that opentracker will not serve. Use this option if opentracker was compiled to allow blacklisting. | ||
| 36 | .Pp | ||
| 37 | .It access.fifo_add Ar path/to/adder.fifo | ||
| 38 | Specifies the path to the FIFO (named pipe) used for dynamic changesets to accesslists. Info hashes written to this FIFO will be added to the main accesslist file. | ||
| 39 | .Pp | ||
| 40 | .It access.fifo_delete Ar path/to/deleter.fifo | ||
| 41 | Specifies the path to the FIFO (named pipe) used for dynamic changesets to accesslists. Info hashes written to this FIFO will be removed from the main accesslist file. | ||
| 42 | .Pp | ||
| 43 | .It access.stats Ar ip_address_or_network | ||
| 44 | Specifies the IP address or network in CIDR notation allowed to fetch stats from opentracker. | ||
| 45 | .Pp | ||
| 46 | .It access.stats_path Ar path | ||
| 47 | Specifies the path to the stats location. You can configure opentracker to appear anywhere on your tracker. Defaults to /stats. | ||
| 48 | .Pp | ||
| 49 | .It access.proxy Ar ip_address_or_network | ||
| 50 | Specifies the IP address or network of the reverse proxies. Opentracker will take the X-Forwarded-For address instead of the source IP address. Can be added more than once. | ||
| 51 | .Pp | ||
| 52 | .It livesync.cluster.listen Ar ip_address:port | ||
| 53 | Specifies the IP address and port opentracker will listen on for incoming live sync packets to keep a cluster of opentrackers synchronized. | ||
| 54 | .Pp | ||
| 55 | .It livesync.cluster.node_ip Ar ip_address | ||
| 56 | Specifies one trusted IP address for sync between trackers running in a cluster. Can be added more than once. | ||
| 57 | .Pp | ||
| 58 | .It batchsync.cluster.admin_ip Ar ip_address | ||
| 59 | Specifies the admin IP address for old-style (HTTP-based) asynchronous tracker syncing. | ||
| 60 | .Pp | ||
| 61 | .It tracker.rootdir Ar path | ||
| 62 | Specifies the directory opentracker will chroot/chdir to. All black/white list files must be located in this directory. | ||
| 63 | .Pp | ||
| 64 | .It tracker.user Ar username | ||
| 65 | Specifies the user opentracker will setuid to after binding to potentially privileged ports. | ||
| 66 | .Pp | ||
| 67 | .It tracker.redirect_url Ar URL | ||
| 68 | Specifies the URL opentracker will redirect to in response to a "GET / HTTP" request. | ||
| 69 | .El | ||
| 70 | .Sh EXAMPLES | ||
| 71 | To specify the address opentracker will listen on for both TCP and UDP connections: | ||
| 72 | .Dl listen.tcp_udp 0.0.0.0:6969 | ||
| 73 | .Pp | ||
| 74 | To specify the address opentracker will listen on for TCP connections: | ||
| 75 | .Dl listen.tcp 0.0.0.0 | ||
| 76 | .Pp | ||
| 77 | To specify the address opentracker will listen on for UDP connections: | ||
| 78 | .Dl listen.udp 0.0.0.0:6969 | ||
| 79 | .Pp | ||
| 80 | .Sh SEE ALSO | ||
| 81 | .Xr opentracker 1 | ||
| 82 | .Pp | ||
| 83 | .Sh AUTHOR | ||
| 84 | .An Dirk Engling | ||
| 85 | .Aq Mt erdgeist@erdgeist.org | ||
| 86 | .Pp | ||
diff --git a/opentracker.c b/opentracker.c index 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 */ |
| 41 | time_t g_now_seconds; | 43 | time_t g_now_seconds; |
| 42 | char * g_redirecturl; | 44 | char *g_redirecturl; |
| 43 | uint32_t g_tracker_id; | 45 | uint32_t g_tracker_id; |
| 44 | volatile int g_opentracker_running = 1; | 46 | volatile int g_opentracker_running = 1; |
| 45 | int g_self_pipe[2]; | 47 | int g_self_pipe[2]; |
| 46 | 48 | ||
| 47 | static char * g_serverdir; | 49 | static char *g_serverdir; |
| 48 | static char * g_serveruser; | 50 | static char *g_serveruser; |
| 49 | static unsigned int g_udp_workers; | 51 | static unsigned int g_udp_workers; |
| 50 | 52 | ||
| 51 | static void panic( const char *routing ) __attribute__ ((noreturn)); | 53 | static void panic(const char *routine) __attribute__((noreturn)); |
| 52 | static void panic( const char *routine ) { | 54 | static 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 | ||
| 57 | static void signal_handler( int s ) { | 59 | static 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 | ||
| 80 | static void defaul_signal_handlers( void ) { | 78 | static 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 | ||
| 90 | static void install_signal_handlers( void ) { | 88 | static 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 | ||
| 106 | static void usage( char *name ) { | 103 | static 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) |
| 117 | static void help( char *name ) { | 116 | static 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 | ||
| 138 | static size_t header_complete( char * request, ssize_t byte_count ) { | 140 | static 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 | ||
| 156 | static void handle_dead( const int64 sock ) { | 159 | static 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 | ||
| 171 | static void handle_read( const int64 sock, struct ot_workstruct *ws ) { | 174 | static 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 | ||
| 208 | static void handle_write( const int64 sock ) { | 218 | static 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 | ||
| 231 | static void handle_accept( const int64 serversocket ) { | 253 | static 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 | ||
| 265 | static void * server_mainloop( void * args ) { | 286 | static 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 | ||
| 328 | static int64_t ot_try_bind( ot_ip6 ip, uint16_t port, PROTO_FLAG proto ) { | 341 | static 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 | ||
| 376 | char * set_config_option( char **option, char *value ) { | 379 | char *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 | ||
| 385 | static int scan_ip6_port( const char *src, ot_ip6 ip, uint16 *port ) { | 389 | static 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 | ||
| 407 | int parse_configfile( char * config_filename ) { | 420 | static 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 | |||
| 445 | typedef 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" ); | 465 | typedef 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 | |
| 473 | static 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 | }; | ||
| 502 | static const size_t config_entry_count = sizeof(config_keys) / sizeof(config_keys[0]); | ||
| 503 | |||
| 504 | static 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 | |||
| 519 | int 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 | ||
| 499 | void load_state(const char * const state_filename ) { | 646 | void 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 | ||
| 535 | int drop_privileges ( const char * const serveruser, const char * const serverdir ) { | 679 | int 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 | ||
| 584 | int main( int argc, char **argv ) { | 726 | /* Maintain our copy of the clock. time() on BSDs is very expensive. */ |
| 585 | ot_ip6 serverip, tmpip; | 727 | static 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 | |||
| 736 | int 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 | |||
| 698 | const 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; | 35 | char *g_accesslist_filename = NULL; |
| 36 | #ifdef WANT_DYNAMIC_ACCESSLIST | ||
| 37 | char *g_accesslist_pipe_add = NULL; | ||
| 38 | char *g_accesslist_pipe_delete = NULL; | ||
| 39 | #endif | ||
| 28 | static pthread_mutex_t g_accesslist_mutex; | 40 | static pthread_mutex_t g_accesslist_mutex; |
| 29 | 41 | ||
| 30 | typedef 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; | ||
| 34 | ot_accesslist * g_accesslist = NULL; | ||
| 35 | ot_accesslist * g_accesslist_old = NULL; | ||
| 36 | 45 | ||
| 37 | static 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 |
| 42 | static 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; | 52 | struct ot_accesslist; |
| 45 | const char *map, *map_end, *read_offs; | 53 | typedef struct ot_accesslist ot_accesslist; |
| 46 | size_t maplen; | 54 | struct ot_accesslist { |
| 55 | ot_hash *list; | ||
| 56 | size_t size; | ||
| 57 | ot_time base; | ||
| 58 | ot_accesslist *next; | ||
| 59 | }; | ||
| 60 | static ot_accesslist *_Atomic g_accesslist = NULL; | ||
| 61 | #ifdef WANT_DYNAMIC_ACCESSLIST | ||
| 62 | static ot_accesslist *_Atomic g_accesslist_add = NULL; | ||
| 63 | static 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 ); | 67 | static 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 ); | 69 | static 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 | 79 | static 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; | 96 | static 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 ) { | 107 | size_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. */ | ||
| 129 | static 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 | |||
| 160 | static 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 | ||
| 115 | int accesslist_hashisvalid( ot_hash hash ) { | 220 | int 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 | ||
| 131 | static void * accesslist_worker( void * args ) { | 250 | static 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 | ||
| 274 | static pthread_t thread_adder_id, thread_deleter_id; | ||
| 275 | static 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 | ||
| 366 | static 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 | } | ||
| 370 | static 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 | |||
| 151 | static pthread_t thread_id; | 376 | static pthread_t thread_id; |
| 152 | void accesslist_init( ) { | 377 | void 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 | ||
| 157 | void accesslist_deinit( void ) { | 388 | void 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) { | 406 | void 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 | ||
| 175 | int address_in_net( const ot_ip6 address, const ot_net *net ) { | 419 | int 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 | ||
| 183 | void *set_value_for_net( const ot_net *net, ot_vector *vector, const void *value, const size_t member_size ) { | 427 | void *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 */ |
| 211 | void *get_value_for_net( const ot_ip6 address, const ot_vector *vector, const size_t member_size ) { | 454 | void *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 |
| 226 | static ot_vector g_lognets_list; | 469 | static ot_vector g_lognets_list; |
| 227 | ot_log *g_logchain_first, *g_logchain_last; | 470 | ot_log *g_logchain_first, *g_logchain_last; |
| 228 | |||
| 229 | static pthread_mutex_t g_lognets_list_mutex = PTHREAD_MUTEX_INITIALIZER; | 471 | static pthread_mutex_t g_lognets_list_mutex = PTHREAD_MUTEX_INITIALIZER; |
| 230 | void loglist_add_network( const ot_net *net ) { | 472 | |
| 473 | void 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 | ||
| 236 | void loglist_reset( ) { | 479 | void 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 | ||
| 244 | int loglist_check_address( const ot_ip6 address ) { | 487 | int 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 |
| 254 | typedef struct { | 497 | typedef 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 | ||
| 259 | static ot_vector g_proxies_list; | 502 | static ot_vector g_proxies_list; |
| 260 | static pthread_mutex_t g_proxies_list_mutex = PTHREAD_MUTEX_INITIALIZER; | 503 | static pthread_mutex_t g_proxies_list_mutex = PTHREAD_MUTEX_INITIALIZER; |
| 261 | 504 | ||
| 262 | int proxylist_add_network( const ot_net *proxy, const ot_net *net ) { | 505 | int 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 | ||
| 285 | int proxylist_check_proxy( const ot_ip6 proxy, const ot_ip6 address ) { | 528 | int 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 | ||
| 301 | static ot_ip6 g_adminip_addresses[OT_ADMINIP_MAX]; | 544 | static ot_net g_admin_nets[OT_ADMINIP_MAX]; |
| 302 | static ot_permissions g_adminip_permissions[OT_ADMINIP_MAX]; | 545 | static ot_permissions g_admin_nets_permissions[OT_ADMINIP_MAX]; |
| 303 | static unsigned int g_adminip_count = 0; | 546 | static size_t g_admin_nets_count = 0; |
| 304 | 547 | ||
| 305 | int accesslist_blessip( ot_ip6 ip, ot_permissions permissions ) { | 548 | int 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 | ||
| 331 | int accesslist_isblessed( ot_ip6 ip, ot_permissions permissions ) { | 587 | int 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 | |||
| 339 | const 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 |
| 15 | void accesslist_init( ); | 17 | void accesslist_init(void); |
| 16 | void accesslist_deinit( ); | 18 | void accesslist_deinit(void); |
| 17 | int accesslist_hashisvalid( ot_hash hash ); | 19 | int accesslist_hashisvalid(const ot_hash hash); |
| 20 | void 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 */ | ||
| 24 | size_t accesslist_parse_hashes(ot_hash *dest, size_t capacity, const char *buf, size_t len); | ||
| 18 | 25 | ||
| 19 | extern char *g_accesslist_filename; | 26 | extern char *g_accesslist_filename; |
| 27 | #ifdef WANT_DYNAMIC_ACCESSLIST | ||
| 28 | extern char *g_accesslist_pipe_add; | ||
| 29 | extern 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 */ |
| 28 | int address_in_net( const ot_ip6 address, const ot_net *net ); | 43 | int 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 | */ |
| 39 | void *set_value_for_net( const ot_net *net, ot_vector *vector, const void *value, const size_t member_size ); | 54 | void *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 | */ |
| 45 | void *get_value_for_net( const ot_ip6 address, const ot_vector *vector, const size_t member_size ); | 60 | void *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 |
| 49 | int proxylist_add_network( const ot_net *proxy, const ot_net *net ); | 63 | int proxylist_add_network(const ot_net *proxy, const ot_net *net); |
| 50 | int proxylist_check_network( const ot_ip6 *proxy, const ot_ip6 address /* can be NULL to only check proxy */ ); | 64 | int 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 | }; |
| 62 | extern ot_log *g_logchain_first, *g_logchain_last; | 76 | extern ot_log *g_logchain_first, *g_logchain_last; |
| 63 | 77 | ||
| 64 | void loglist_add_network( const ot_net *net ); | 78 | void loglist_add_network(const ot_net *net); |
| 65 | void loglist_reset( ); | 79 | void loglist_reset(void); |
| 66 | int loglist_check_address( const ot_ip6 address ); | 80 | int loglist_check_address(const ot_ip6 address); |
| 67 | #endif | 81 | #endif |
| 68 | 82 | ||
| 69 | typedef enum { | 83 | typedef 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 | ||
| 76 | int accesslist_blessip( ot_ip6 ip, ot_permissions permissions ); | 90 | int accesslist_bless_net(ot_net *net, ot_permissions permissions); |
| 77 | int accesslist_isblessed( ot_ip6 ip, ot_permissions permissions ); | 91 | int 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 | |||
| 13 | int 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 | |||
| 32 | void buf_truncate(ot_buf *buf, size_t len) { | ||
| 33 | if (len < buf->size) | ||
| 34 | buf->size = len; | ||
| 35 | } | ||
| 36 | |||
| 37 | void 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. */ | ||
| 14 | typedef 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. */ | ||
| 21 | int buf_cat(ot_buf *buf, const void *src, size_t len); | ||
| 22 | |||
| 23 | void buf_truncate(ot_buf *buf, size_t len); | ||
| 24 | |||
| 25 | void buf_reset(ot_buf *buf); | ||
| 26 | |||
| 27 | #endif | ||
| @@ -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 */ |
| 22 | static ssize_t clean_single_bucket( ot_peer *peers, size_t peer_count, time_t timedout, int *removed_seeders ) { | 23 | static ssize_t clean_single_bucket(ot_peer *peers, size_t peer_count, size_t peer_size, time_t timedout, size_t *removed_seeders) { |
| 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 | 50 | int 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); |
| 49 | int 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 | */ | ||
| 105 | int 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 */ |
| 103 | static void * clean_worker( void * args ) { | 111 | static 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 | ||
| 130 | static pthread_t thread_id; | 141 | static pthread_t thread_id; |
| 131 | void clean_init( void ) { | 142 | void clean_init(void) { pthread_create(&thread_id, NULL, clean_worker, NULL); } |
| 132 | pthread_create( &thread_id, NULL, clean_worker, NULL ); | ||
| 133 | } | ||
| 134 | |||
| 135 | void clean_deinit( void ) { | ||
| 136 | pthread_cancel( thread_id ); | ||
| 137 | } | ||
| 138 | 143 | ||
| 139 | const char *g_version_clean_c = "$Source$: $Revision$\n"; | 144 | void clean_deinit(void) { pthread_cancel(thread_id); } |
| @@ -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 | ||
| 15 | void clean_init( void ); | 15 | void clean_init(void); |
| 16 | void clean_deinit( void ); | 16 | void clean_deinit(void); |
| 17 | int clean_single_torrent( ot_torrent *torrent ); | 17 | int 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 */ |
| 39 | static void fullscrape_make( int *iovec_entries, struct iovec **iovector, ot_tasktype mode ); | 41 | static void fullscrape_make(int taskid, ot_tasktype mode); |
| 40 | #ifdef WANT_COMPRESSION_GZIP | 42 | #ifdef WANT_COMPRESSION_GZIP |
| 41 | static void fullscrape_make_gzip( int *iovec_entries, struct iovec **iovector, ot_tasktype mode ); | 43 | static void fullscrape_make_gzip(int taskid, ot_tasktype mode); |
| 44 | #endif | ||
| 45 | #ifdef WANT_COMPRESSION_ZSTD | ||
| 46 | static 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. */ |
| 46 | static 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 | */ |
| 51 | static void * fullscrape_worker( void * args ) { | 54 | static 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 | ||
| 72 | static pthread_t thread_id; | 76 | static pthread_t thread_id; |
| 73 | void fullscrape_init( ) { | 77 | void 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 | ||
| 77 | void fullscrape_deinit( ) { | 81 | void fullscrape_deinit(void) { |
| 78 | pthread_cancel( thread_id ); | 82 | pthread_cancel( thread_id ); |
| 79 | } | 83 | } |
| 80 | 84 | ||
| 81 | void fullscrape_deliver( int64 sock, ot_tasktype tasktype ) { | 85 | void fullscrape_deliver( int64_t sock, ot_tasktype tasktype ) { |
| 82 | mutex_workqueue_pushtask( sock, tasktype ); | 86 | mutex_workqueue_pushtask( sock, tasktype ); |
| 83 | } | 87 | } |
| 84 | 88 | ||
| 85 | static char * fullscrape_write_one( ot_tasktype mode, char *r, ot_peerlist *peer_list, ot_hash *hash ) { | 89 | static 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 | ||
| 122 | static void fullscrape_make( int *iovec_entries, struct iovec **iovector, ot_tasktype mode ) { | 137 | static 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 | ||
| 178 | static void fullscrape_make_gzip( int *iovec_entries, struct iovec **iovector, ot_tasktype mode ) { | 203 | static 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 | |||
| 321 | static 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 |
| 283 | const 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 | ||
| 11 | void fullscrape_init( ); | 11 | #include "ot_mutex.h" |
| 12 | void fullscrape_deinit( ); | 12 | |
| 13 | void fullscrape_deliver( int64 sock, ot_tasktype tasktype ); | 13 | void fullscrape_init(void); |
| 14 | void fullscrape_deinit(void); | ||
| 15 | void 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 | |||
| 13 | int 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 | |||
| 24 | int hex_isdigit(int c) { return hex_value(c) >= 0; } | ||
| 25 | |||
| 26 | char *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 | |||
| 39 | int 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. */ | ||
| 15 | char *hex_fromhash(char *dest, const uint8_t *hash); | ||
| 16 | |||
| 17 | /* Read forty hex digits from src into hash. Returns non-zero on success. | ||
| 18 | On failure hash is left untouched and no byte of src beyond the offending | ||
| 19 | character is read, so src need not be forty characters long. */ | ||
| 20 | int hex_tohash(ot_hash hash, const char *src); | ||
| 21 | |||
| 22 | /* Non-zero if c is a hexadecimal digit, in either case. */ | ||
| 23 | int hex_isdigit(int c); | ||
| 24 | |||
| 25 | /* 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. */ | ||
| 27 | int hex_value(int c); | ||
| 28 | |||
| 29 | #endif | ||
| @@ -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) |
| 35 | extern char *g_redirecturl; | 40 | extern char *g_redirecturl; |
| 36 | 41 | ||
| 37 | char *g_stats_path; | 42 | char *g_stats_path; |
| 38 | ssize_t g_stats_path_len; | 43 | ssize_t g_stats_path_len; |
| 39 | 44 | ||
| 40 | enum { | 45 | enum { 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 | ||
| 45 | static void http_senddata( const int64 sock, struct ot_workstruct *ws ) { | 47 | static 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) |
| 105 | ssize_t http_issue_error( const int64 sock, struct ot_workstruct *ws, int code ) { | 127 | ssize_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 | ||
| 124 | ssize_t http_sendiovecdata( const int64 sock, struct ot_workstruct *ws, int iovec_entries, struct iovec *iovector ) { | 147 | ssize_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 | ||
| 195 | static ssize_t http_handle_stats( const int64 sock, struct ot_workstruct *ws, char *read_ptr ) { | 246 | static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) { |
| 196 | static 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}, |
| 198 | static 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}}; |
| 209 | static 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 |
| 280 | static pthread_mutex_t g_modest_fullscrape_mutex = PTHREAD_MUTEX_INITIALIZER; | 362 | static pthread_mutex_t g_modest_fullscrape_mutex = PTHREAD_MUTEX_INITIALIZER; |
| 281 | static ot_vector g_modest_fullscrape_timeouts; | 363 | static ot_vector g_modest_fullscrape_timeouts; |
| 282 | typedef struct { ot_ip6 ip; ot_time last_fullscrape; } ot_scrape_log; | 364 | typedef 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 |
| 286 | static ssize_t http_handle_fullscrape( const int64 sock, struct ot_workstruct *ws ) { | 371 | static 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 | ||
| 336 | static ssize_t http_handle_scrape( const int64 sock, struct ot_workstruct *ws, char *read_ptr ) { | 441 | static 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]; | 490 | unsigned 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) |
| 380 | static char* http_header( char *data, size_t byte_count, char *header ) { | 494 | static 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 | ||
| 394 | static ot_keywords keywords_announce[] = { { "port", 1 }, { "left", 2 }, { "event", 3 }, { "numwant", 4 }, { "compact", 5 }, { "compact6", 5 }, { "info_hash", 6 }, | 511 | static 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 } }; | 519 | static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}}; |
| 403 | static ot_keywords keywords_announce_event[] = { { "completed", 1 }, { "stopped", 2 }, { NULL, -3 } }; | 520 | static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) { |
| 404 | static 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 | ||
| 555 | ssize_t http_handle_request( const int64 sock, struct ot_workstruct *ws ) { | 698 | ssize_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 | |||
| 659 | const char *g_version_http_c = "$Source$: $Revision$\n"; | ||
| @@ -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 | |||
| 9 | typedef enum { | 11 | typedef 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 | ||
| 15 | struct http_data { | 20 | struct 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 | ||
| 23 | ssize_t http_handle_request( const int64 s, struct ot_workstruct *ws ); | 28 | ssize_t http_handle_request(const int64_t s, struct ot_workstruct *ws); |
| 24 | ssize_t http_sendiovecdata( const int64 s, struct ot_workstruct *ws, int iovec_entries, struct iovec *iovector ); | 29 | ssize_t http_sendiovecdata(const int64_t s, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial); |
| 25 | ssize_t http_issue_error( const int64 s, struct ot_workstruct *ws, int code ); | 30 | ssize_t http_issue_error(const int64_t s, struct ot_workstruct *ws, int code); |
| 26 | 31 | ||
| 27 | extern char *g_stats_path; | 32 | extern char *g_stats_path; |
| 28 | extern ssize_t g_stats_path_len; | 33 | extern ssize_t g_stats_path_len; |
| @@ -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 | ||
| 17 | void *iovec_increase( int *iovec_entries, struct iovec **iovector, size_t new_alloc ) { | 15 | void *iovec_increase(size_t *iovec_entries, struct iovec **iovector, size_t new_alloc) { |
| 18 | void *new_data; | 16 | void *new_data; |
| 19 | int new_entries = 1 + *iovec_entries; | 17 | size_t new_entries = 1 + *iovec_entries; |
| 20 | struct iovec *new_vec = realloc( *iovector, new_entries * sizeof( struct iovec ) ); | 18 | struct iovec *new_vec = realloc(*iovector, new_entries * sizeof(struct iovec)); |
| 21 | 19 | ||
| 22 | if( !new_vec ) | 20 | if (!new_vec) |
| 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 | ||
| 38 | void iovec_free( int *iovec_entries, struct iovec **iovector ) { | 36 | void *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 | |||
| 55 | void 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 | ||
| 46 | void iovec_fixlast( int *iovec_entries, struct iovec **iovector, void *last_ptr ) { | 64 | void iovec_fixlast(size_t *iovec_entries, struct iovec **iovector, void *last_ptr) { |
| 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 | ||
| 56 | void *iovec_fix_increase_or_free( int *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc ) { | 74 | void *iovec_fix_increase_or_free(size_t *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc) { |
| 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 | 85 | size_t iovec_length(const size_t *iovec_entries, const struct iovec **iovector) { | |
| 68 | size_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 | |||
| 76 | const char *g_version_iovec_c = "$Source$: $Revision$\n"; | ||
| @@ -8,12 +8,13 @@ | |||
| 8 | 8 | ||
| 9 | #include <sys/uio.h> | 9 | #include <sys/uio.h> |
| 10 | 10 | ||
| 11 | void *iovec_increase( int *iovec_entries, struct iovec **iovector, size_t new_alloc ); | 11 | void *iovec_increase(size_t *iovec_entries, struct iovec **iovector, size_t new_alloc); |
| 12 | void iovec_fixlast( int *iovec_entries, struct iovec **iovector, void *last_ptr ); | 12 | void *iovec_append(size_t *iovec_entries, struct iovec **iovector, struct iovec *append_iovector); |
| 13 | void iovec_free( int *iovec_entries, struct iovec **iovector ); | 13 | void iovec_fixlast(size_t *iovec_entries, struct iovec **iovector, void *last_ptr); |
| 14 | void iovec_free(size_t *iovec_entries, struct iovec **iovector); | ||
| 14 | 15 | ||
| 15 | size_t iovec_length( const int *iovec_entries, const struct iovec **iovector ); | 16 | size_t iovec_length(const size_t *iovec_entries, const struct iovec **iovector); |
| 16 | 17 | ||
| 17 | void *iovec_fix_increase_or_free( int *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc ); | 18 | void *iovec_fix_increase_or_free(size_t *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc); |
| 18 | 19 | ||
| 19 | #endif | 20 | #endif |
| @@ -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 | |||
| 14 | const 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 | |||
| 19 | static 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 | |||
| 38 | static 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 | |||
| 65 | static 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 | |||
| 67 | size_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 | |||
| 123 | size_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 | } | ||
| @@ -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. */ | ||
| 16 | extern const char ip_v4mapped_prefix[12]; | ||
| 17 | |||
| 18 | static 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. */ | ||
| 26 | size_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". */ | ||
| 31 | size_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 | ||
| 29 | char groupip_1[4] = { 224,0,23,5 }; | 28 | char 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 | ||
| 38 | enum { OT_SYNC_PEER }; | 37 | enum { OT_SYNC_PEER4, OT_SYNC_PEER6 }; |
| 39 | 38 | ||
| 40 | /* Forward declaration */ | 39 | /* Forward declaration */ |
| 41 | static void * livesync_worker( void * args ); | 40 | static void *livesync_worker(void *args); |
| 42 | 41 | ||
| 43 | /* For outgoing packets */ | 42 | /* For outgoing packets */ |
| 44 | static int64 g_socket_in = -1; | 43 | static int64_t g_socket_in = -1; |
| 45 | 44 | ||
| 46 | /* For incoming packets */ | 45 | /* For incoming packets */ |
| 47 | static int64 g_socket_out = -1; | 46 | static int64_t g_socket_out = -1; |
| 48 | 47 | ||
| 49 | static pthread_mutex_t g_outbuf_mutex = PTHREAD_MUTEX_INITIALIZER; | 48 | static pthread_mutex_t g_outbuf_mutex = PTHREAD_MUTEX_INITIALIZER; |
| 50 | char g_outbuf[LIVESYNC_OUTGOING_BUFFSIZE_PEERS]; | 49 | typedef struct { |
| 51 | static size_t g_outbuf_data; | 50 | uint8_t data[LIVESYNC_OUTGOING_BUFFSIZE_PEERS]; |
| 52 | static ot_time g_next_packet_time; | 51 | size_t fill; |
| 52 | ot_time next_packet_time; | ||
| 53 | } sync_buffer; | ||
| 54 | |||
| 55 | static sync_buffer g_v6_buf; | ||
| 56 | static sync_buffer g_v4_buf; | ||
| 53 | 57 | ||
| 54 | static pthread_t thread_id; | 58 | static pthread_t thread_id; |
| 55 | void livesync_init( ) { | 59 | void livesync_init(void) { |
| 60 | uint32_t packet_type6 = htonl(OT_SYNC_PEER6); | ||
| 61 | uint32_t packet_type4 = htonl(OT_SYNC_PEER4); | ||
| 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 | ||
| 70 | void livesync_deinit() { | 82 | void 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 | ||
| 79 | void livesync_bind_mcast( ot_ip6 ip, uint16_t port) { | 91 | void 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 */ |
| 110 | static void livesync_issue_peersync( ) { | 123 | static 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 | ||
| 125 | static void livesync_handle_peersync( struct ot_workstruct *ws ) { | 138 | static 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 */ |
| 152 | void livesync_ticker( ) { | 172 | void 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. */ |
| 163 | void livesync_tell( struct ot_workstruct *ws ) { | 188 | void 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 | ||
| 177 | static void * livesync_worker( void * args ) { | 207 | static 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 |
| 216 | const 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 | ||
| 44 | void livesync_init(); | 50 | void livesync_init(void); |
| 45 | void livesync_deinit(); | 51 | void 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 */ |
| 48 | void livesync_bind_mcast( char *ip, uint16_t port ); | 54 | void 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. */ |
| 51 | void livesync_tell( struct ot_workstruct *ws ); | 57 | void 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 */ |
| 56 | void livesync_ticker( ); | 62 | void livesync_ticker(void); |
| 57 | 63 | ||
| 58 | /* Handle an incoming live sync packet */ | 64 | /* Handle an incoming live sync packet */ |
| 59 | void handle_livesync( const int64 sock ); | 65 | void handle_livesync(const int64_t sock); |
| 60 | 66 | ||
| 61 | #else | 67 | #else |
| 62 | 68 | ||
| @@ -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 */ |
| 27 | static ot_vector all_torrents[OT_BUCKET_COUNT]; | 27 | static ot_vector all_torrents[OT_BUCKET_COUNT]; |
| 28 | static pthread_mutex_t bucket_mutex[OT_BUCKET_COUNT]; | 28 | static pthread_mutex_t bucket_mutex[OT_BUCKET_COUNT]; |
| 29 | static size_t g_torrent_count; | 29 | static size_t g_torrent_count; |
| 30 | 30 | ||
| 31 | /* Self pipe from opentracker.c */ | 31 | /* Self pipe from opentracker.c */ |
| 32 | extern int g_self_pipe[2]; | 32 | extern int g_self_pipe[2]; |
| 33 | 33 | ||
| 34 | ot_vector *mutex_bucket_lock( int bucket ) { | 34 | ot_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 | ||
| 39 | ot_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 */ |
| 41 | static int bucket_from_hash(ot_hash const hash) { | ||
| 42 | uint32_t bucket; | ||
| 43 | memcpy(&bucket, hash, sizeof(bucket)); | ||
| 44 | return ntohl(bucket) >> OT_BUCKET_COUNT_SHIFT; | ||
| 41 | } | 45 | } |
| 42 | 46 | ||
| 43 | void mutex_bucket_unlock( int bucket, int delta_torrentcount ) { | 47 | ot_vector *mutex_bucket_lock_by_hash(ot_hash const hash) { return mutex_bucket_lock(bucket_from_hash(hash)); } |
| 48 | |||
| 49 | void 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 | ||
| 48 | void mutex_bucket_unlock_by_hash( ot_hash hash, int delta_torrentcount ) { | 54 | void 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 | ||
| 52 | size_t mutex_get_torrent_count( ) { | 58 | size_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 | ||
| 58 | struct ot_task { | 62 | struct 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 | ||
| 67 | static ot_taskid next_free_taskid = 1; | 71 | static ot_taskid next_free_taskid = 1; |
| 68 | static struct ot_task *tasklist; | 72 | static struct ot_task *tasklist; |
| 69 | static pthread_mutex_t tasklist_mutex; | 73 | static pthread_mutex_t tasklist_mutex; |
| 70 | static pthread_cond_t tasklist_being_filled; | 74 | static pthread_cond_t tasklist_being_filled; |
| 71 | |||
| 72 | int mutex_workqueue_pushtask( int64 sock, ot_tasktype tasktype ) { | ||
| 73 | struct ot_task ** tmptask, * task; | ||
| 74 | 75 | ||
| 75 | /* Want exclusive access to tasklist */ | 76 | int 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 | ||
| 110 | void mutex_workqueue_canceltask( int64 sock ) { | 105 | void 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 | ||
| 141 | ot_taskid mutex_workqueue_poptask( ot_tasktype *tasktype ) { | 131 | ot_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 | ||
| 177 | void mutex_workqueue_pushsuccess( ot_taskid taskid ) { | 160 | void 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 | ||
| 201 | int mutex_workqueue_pushresult( ot_taskid taskid, int iovec_entries, struct iovec *iovec ) { | 178 | int 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 | ||
| 231 | int64 mutex_workqueue_popresult( int *iovec_entries, struct iovec ** iovec ) { | 202 | int 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 | |||
| 230 | int64_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 | ||
| 262 | void mutex_init( ) { | 264 | void 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 | ||
| 271 | void mutex_deinit( ) { | 273 | void 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 | |||
| 280 | const char *g_version_mutex_c = "$Source$: $Revision$\n"; | ||
| @@ -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 | ||
| 11 | void mutex_init( ); | 12 | void mutex_init(void); |
| 12 | void mutex_deinit( ); | 13 | void mutex_deinit(void); |
| 13 | 14 | ||
| 14 | ot_vector *mutex_bucket_lock( int bucket ); | 15 | ot_vector *mutex_bucket_lock(int bucket); |
| 15 | ot_vector *mutex_bucket_lock_by_hash( ot_hash hash ); | 16 | ot_vector *mutex_bucket_lock_by_hash(ot_hash const hash); |
| 16 | 17 | ||
| 17 | void mutex_bucket_unlock( int bucket, int delta_torrentcount ); | 18 | void mutex_bucket_unlock(int bucket, int delta_torrentcount); |
| 18 | void mutex_bucket_unlock_by_hash( ot_hash hash, int delta_torrentcount ); | 19 | void mutex_bucket_unlock_by_hash(ot_hash const hash, int delta_torrentcount); |
| 19 | 20 | ||
| 20 | size_t mutex_get_torrent_count(); | 21 | size_t mutex_get_torrent_count(void); |
| 21 | 22 | ||
| 22 | typedef enum { | 23 | typedef 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 | ||
| 66 | typedef unsigned long ot_taskid; | 70 | typedef unsigned long ot_taskid; |
| 67 | 71 | ||
| 68 | int mutex_workqueue_pushtask( int64 sock, ot_tasktype tasktype ); | 72 | int mutex_workqueue_pushtask(int64_t sock, ot_tasktype tasktype); |
| 69 | void mutex_workqueue_canceltask( int64 sock ); | 73 | void mutex_workqueue_canceltask(int64_t sock); |
| 70 | void mutex_workqueue_pushsuccess( ot_taskid taskid ); | 74 | void mutex_workqueue_pushsuccess(ot_taskid taskid); |
| 71 | ot_taskid mutex_workqueue_poptask( ot_tasktype *tasktype ); | 75 | ot_taskid mutex_workqueue_poptask(ot_tasktype *tasktype); |
| 72 | int mutex_workqueue_pushresult( ot_taskid taskid, int iovec_entries, struct iovec *iovector ); | 76 | int mutex_workqueue_pushresult(ot_taskid taskid, size_t iovec_entries, struct iovec *iovector); |
| 73 | int64 mutex_workqueue_popresult( int *iovec_entries, struct iovec ** iovector ); | 77 | int mutex_workqueue_pushchunked(ot_taskid taskid, struct iovec *iovec); |
| 78 | int64_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 | |||
| 19 | void random_init(void) { | ||
| 20 | #ifdef WANT_DEV_RANDOM | ||
| 21 | srandomdev(); | ||
| 22 | #else | ||
| 23 | srandom(time(NULL)); | ||
| 24 | #endif | ||
| 25 | } | ||
| 26 | |||
| 27 | /* random() yields 31 bits, so one draw can never set the top bit. Combine | ||
| 28 | two of them rather than hand out a value that is quietly half as wide as | ||
| 29 | its type claims. */ | ||
| 30 | uint32_t random_uint32(void) { return ((uint32_t)random() << 16) ^ (uint32_t)random(); } | ||
| 31 | |||
| 32 | void random_bytes(void *buf, size_t len) { | ||
| 33 | uint8_t *out = buf; | ||
| 34 | /* random() leaves the top bit clear and its lowest bits are the weakest, | ||
| 35 | so take a byte from the middle of each draw */ | ||
| 36 | while (len--) | ||
| 37 | *out++ = (uint8_t)(random() >> 8); | ||
| 38 | } | ||
| 39 | |||
| 40 | #else | ||
| 41 | |||
| 42 | #if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__DragonFly__) && !defined(__APPLE__) && \ | ||
| 43 | !(defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 36))) | ||
| 44 | #include <bsd/stdlib.h> | ||
| 45 | #endif | ||
| 46 | |||
| 47 | void random_init(void) {} | ||
| 48 | |||
| 49 | uint32_t random_uint32(void) { return arc4random(); } | ||
| 50 | |||
| 51 | void random_bytes(void *buf, size_t len) { arc4random_buf(buf, len); } | ||
| 52 | |||
| 53 | #endif | ||
diff --git a/ot_random.h b/ot_random.h new file mode 100644 index 0000000..5200dbc --- /dev/null +++ b/ot_random.h | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | /* This software was written by Dirk Engling <erdgeist@erdgeist.org> | ||
| 2 | It is considered beerware. Prost. Skol. Cheers or whatever. | ||
| 3 | |||
| 4 | $id$ */ | ||
| 5 | |||
| 6 | #ifndef OT_RANDOM_H__ | ||
| 7 | #define OT_RANDOM_H__ | ||
| 8 | |||
| 9 | #include <stddef.h> | ||
| 10 | #include <stdint.h> | ||
| 11 | |||
| 12 | /* Seed whatever generator the build settled on. A no-op when backed by | ||
| 13 | arc4random, which seeds itself from the kernel. */ | ||
| 14 | void random_init(void); | ||
| 15 | |||
| 16 | uint32_t random_uint32(void); | ||
| 17 | void random_bytes(void *buf, size_t len); | ||
| 18 | |||
| 19 | #endif | ||
diff --git a/ot_rijndael.c b/ot_rijndael.c index f468e2f..3f36bde 100644 --- a/ot_rijndael.c +++ b/ot_rijndael.c | |||
| @@ -486,5 +486,3 @@ void rijndaelEncrypt128(const uint32_t rk[44], const uint8_t pt[16], uint8_t ct[ | |||
| 486 | rk[43]; | 486 | rk[43]; |
| 487 | PUTU32(ct + 12, s3); | 487 | PUTU32(ct + 12, s3); |
| 488 | } | 488 | } |
| 489 | |||
| 490 | const char *g_version_rijndael_c = "$Source$: $Revision$\n"; | ||
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 | |||
| 14 | size_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 | |||
| 31 | size_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. */ | ||
| 15 | size_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. */ | ||
| 19 | size_t scan_udec_bencode(const char *src, uint64_t *dest, uint64_t max); | ||
| 20 | |||
| 21 | #endif | ||
| @@ -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 */ |
| 41 | static void stats_make( int *iovec_entries, struct iovec **iovector, ot_tasktype mode ); | 38 | static 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 */ |
| 45 | static unsigned long long ot_overall_tcp_connections = 0; | 42 | static unsigned long long ot_overall_tcp_connections; |
| 46 | static unsigned long long ot_overall_udp_connections = 0; | 43 | static unsigned long long ot_overall_udp_connections; |
| 47 | static unsigned long long ot_overall_tcp_successfulannounces = 0; | 44 | static unsigned long long ot_overall_tcp_successfulannounces; |
| 48 | static unsigned long long ot_overall_udp_successfulannounces = 0; | 45 | static unsigned long long ot_overall_udp_successfulannounces; |
| 49 | static unsigned long long ot_overall_tcp_successfulscrapes = 0; | 46 | static unsigned long long ot_overall_tcp_successfulscrapes; |
| 50 | static unsigned long long ot_overall_udp_successfulscrapes = 0; | 47 | static unsigned long long ot_overall_udp_successfulscrapes; |
| 51 | static unsigned long long ot_overall_udp_connectionidmissmatches = 0; | 48 | static unsigned long long ot_overall_udp_connectionidmissmatches; |
| 52 | static unsigned long long ot_overall_tcp_connects = 0; | 49 | static unsigned long long ot_overall_tcp_connects; |
| 53 | static unsigned long long ot_overall_udp_connects = 0; | 50 | static unsigned long long ot_overall_udp_connects; |
| 54 | static unsigned long long ot_overall_completed = 0; | 51 | static unsigned long long ot_overall_completed; |
| 55 | static unsigned long long ot_full_scrape_count = 0; | 52 | static unsigned long long ot_full_scrape_count; |
| 56 | static unsigned long long ot_full_scrape_request_count = 0; | 53 | static unsigned long long ot_full_scrape_request_count; |
| 57 | static unsigned long long ot_full_scrape_size = 0; | 54 | static unsigned long long ot_full_scrape_size; |
| 58 | static unsigned long long ot_failed_request_counts[CODE_HTTPERROR_COUNT]; | 55 | static unsigned long long ot_failed_request_counts[CODE_HTTPERROR_COUNT]; |
| 59 | static 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" }; | 56 | static 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"}; | ||
| 60 | static unsigned long long ot_renewed[OT_PEER_TIMEOUT]; | 59 | static unsigned long long ot_renewed[OT_PEER_TIMEOUT]; |
| 61 | static unsigned long long ot_overall_sync_count; | 60 | static unsigned long long ot_overall_sync_count; |
| 62 | static unsigned long long ot_overall_stall_count; | 61 | static unsigned long long ot_overall_stall_count; |
| 63 | 62 | ||
| 64 | static time_t ot_start_time; | 63 | static 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 | ||
| 84 | typedef union stats_network_node stats_network_node; | 83 | typedef union stats_network_node stats_network_node; |
| 85 | union stats_network_node { | 84 | union stats_network_node { |
| @@ -91,121 +90,125 @@ union stats_network_node { | |||
| 91 | static stats_network_node *stats_network_counters_root; | 90 | static stats_network_node *stats_network_counters_root; |
| 92 | #endif | 91 | #endif |
| 93 | 92 | ||
| 94 | static int stat_increase_network_count( stats_network_node **pnode, int depth, uintptr_t ip ) { | 93 | static 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 | ||
| 113 | static int stats_shift_down_network_count( stats_network_node **node, int depth, int shift ) { | 112 | static 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 | ||
| 133 | static size_t stats_get_highscore_networks( stats_network_node *node, int depth, ot_ip6 node_value, size_t *scores, ot_ip6 *networks, int network_count, int limit ) { | 132 | static size_t stats_get_highscore_networks(stats_network_node *node, int depth, ot_ip6 node_value, size_t *scores, ot_ip6 *networks, int network_count, |
| 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 | ||
| 188 | static size_t stats_return_busy_networks( char * reply, stats_network_node *tree, int amount, int limit ) { | 190 | static 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 | ||
| 218 | static size_t stats_slash24s_txt( char *reply, size_t amount ) { | 221 | static 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 | ||
| 255 | bailout_unlock: | 259 | bailout_unlock: |
| 256 | mutex_bucket_unlock( bucket, 0 ); | 260 | mutex_bucket_unlock(bucket, 0); |
| 257 | bailout_error: | 261 | bailout_error: |
| 258 | r = reply; | 262 | r = reply; |
| 259 | success: | 263 | success: |
| 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 |
| 266 | static stats_network_node *stats_woodpeckers_tree; | 270 | static stats_network_node *stats_woodpeckers_tree; |
| 267 | static pthread_mutex_t g_woodpeckers_mutex = PTHREAD_MUTEX_INITIALIZER; | 271 | static pthread_mutex_t g_woodpeckers_mutex = PTHREAD_MUTEX_INITIALIZER; |
| 268 | 272 | ||
| 269 | static size_t stats_return_woodpeckers( char * reply, int amount ) { | 273 | static 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 | ||
| 285 | static int torrent_statter( ot_torrent *torrent, uintptr_t data ) { | 289 | static 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 */ |
| 294 | static 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;} | 298 | typedef struct { |
| 299 | size_t val; | ||
| 300 | ot_hash hash; | ||
| 301 | } ot_record; | ||
| 302 | |||
| 303 | static 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 | ||
| 296 | typedef struct { size_t val; ot_torrent * torrent; } ot_record; | ||
| 297 | 317 | ||
| 298 | /* Fetches stats from tracker */ | 318 | /* Fetches stats from tracker */ |
| 299 | size_t stats_top_txt( char * reply, int amount ) { | 319 | size_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 | ||
| 345 | static unsigned long events_per_time( unsigned long long events, time_t t ) { | 365 | static 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 | ||
| 349 | static size_t stats_connections_mrtg( char * reply ) { | 367 | static 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 | ||
| 362 | static size_t stats_udpconnections_mrtg( char * reply ) { | 376 | static 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 | ||
| 375 | static size_t stats_tcpconnections_mrtg( char * reply ) { | 383 | static 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 | ||
| 388 | static size_t stats_scrape_mrtg( char * reply ) { | 390 | static 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 | ||
| 400 | static size_t stats_fullscrapes_mrtg( char * reply ) { | 397 | static 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 | ||
| 413 | static size_t stats_peers_mrtg( char * reply ) { | 403 | static 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 | ||
| 425 | static size_t stats_torrents_mrtg( char * reply ) | 411 | static 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 | ||
| 436 | static size_t stats_httperrors_txt ( char * reply ) { | 417 | static 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 | ||
| 443 | static size_t stats_return_renew_bucket( char * reply ) { | 423 | static 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 | ||
| 452 | static size_t stats_return_sync_mrtg( char * reply ) { | 432 | static 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 | ||
| 465 | static size_t stats_return_completed_mrtg( char * reply ) { | 439 | static 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 |
| 479 | extern unsigned long long numwants[201]; | 447 | extern unsigned long long numwants[201]; |
| 480 | static size_t stats_return_numwants( char * reply ) { | 448 | static 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 |
| 490 | static void stats_return_fulllog( int *iovec_entries, struct iovec **iovector, char *r ) { | 458 | static 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 | ||
| 521 | static size_t stats_return_everything( char * reply ) { | 490 | static 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 | ||
| 560 | extern const char | 534 | size_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 | |||
| 565 | size_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 | ||
| 572 | size_t return_stats_for_tracker( char *reply, int mode, int format ) { | 540 | size_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 | ||
| 604 | static void stats_make( int *iovec_entries, struct iovec **iovector, ot_tasktype mode ) { | 572 | static 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 | ||
| 636 | void stats_issue_event( ot_status_event event, PROTO_FLAG proto, uintptr_t event_data ) { | 622 | void 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 | ||
| 735 | void stats_cleanup() { | 729 | void 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 | ||
| 743 | static void * stats_worker( void * args ) { | 737 | static 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 | ||
| 759 | void stats_deliver( int64 sock, int tasktype ) { | 753 | void stats_deliver(int64_t sock, int tasktype) { mutex_workqueue_pushtask(sock, tasktype); } |
| 760 | mutex_workqueue_pushtask( sock, tasktype ); | ||
| 761 | } | ||
| 762 | 754 | ||
| 763 | static pthread_t thread_id; | 755 | static pthread_t thread_id; |
| 764 | void stats_init( ) { | 756 | void 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 | ||
| 769 | void stats_deinit( ) { | 761 | void stats_deinit(void) { |
| 770 | pthread_cancel( thread_id ); | 762 | pthread_cancel(thread_id); |
| 771 | } | 763 | } |
| 772 | |||
| 773 | const char *g_version_stats_c = "$Source$: $Revision$\n"; | ||
| @@ -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 | |||
| 9 | typedef enum { | 11 | typedef 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 | ||
| 41 | void stats_issue_event( ot_status_event event, PROTO_FLAG proto, uintptr_t event_data ); | 44 | void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_data); |
| 42 | void stats_deliver( int64 sock, int tasktype ); | 45 | void stats_deliver(int64_t sock, int tasktype); |
| 43 | void stats_cleanup(); | 46 | void stats_cleanup(void); |
| 44 | size_t return_stats_for_tracker( char *reply, int mode, int format ); | 47 | size_t return_stats_for_tracker(char *reply, int mode, int format); |
| 45 | size_t stats_return_tracker_version( char *reply ); | 48 | size_t stats_return_tracker_version(char *reply); |
| 46 | void stats_init( ); | 49 | void stats_init(void); |
| 47 | void stats_deinit( ); | 50 | void stats_deinit(void); |
| 48 | |||
| 49 | extern const char *g_version_rijndael_c; | ||
| 50 | extern const char *g_version_livesync_c; | ||
| 51 | 51 | ||
| 52 | #endif | 52 | #endif |
| @@ -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 | */ |
| 34 | int add_changeset_to_tracker( uint8_t *data, size_t len ) { | 32 | int 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 | */ |
| 73 | static void sync_make( int *iovec_entries, struct iovec **iovector ) { | 73 | static 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 | */ |
| 134 | static void * sync_worker( void * args) { | 140 | static 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 | ||
| 160 | void sync_deliver( int64 socket ) { | 166 | void 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 | |||
| 166 | const char *g_version_sync_c = "$Source$: $Revision$\n"; | ||
| @@ -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 |
| 10 | enum { SYNC_IN, SYNC_OUT }; | 12 | enum { SYNC_IN, SYNC_OUT }; |
| 11 | 13 | ||
| 12 | void sync_init( ); | 14 | void sync_init(); |
| 13 | void sync_deinit( ); | 15 | void sync_deinit(); |
| 14 | void sync_deliver( int64 socket ); | 16 | void sync_deliver(int64_t socket); |
| 15 | 17 | ||
| 16 | int add_changeset_to_tracker( uint8_t *data, size_t len ); | 18 | int add_changeset_to_tracker(uint8_t *data, size_t len); |
| 17 | #else | 19 | #else |
| 18 | 20 | ||
| 19 | #define sync_init() | 21 | #define sync_init() |
| @@ -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 |
| 24 | static const uint8_t g_static_connid[8] = { 0x23, 0x42, 0x05, 0x17, 0xde, 0x41, 0x50, 0xff }; | 26 | static const uint8_t g_static_connid[8] = { 0x23, 0x42, 0x05, 0x17, 0xde, 0x41, 0x50, 0xff }; |
| 25 | #endif | 27 | #endif |
| 26 | static uint32_t g_rijndael_round_key[44] = {0}; | 28 | static uint32_t g_rijndael_round_key[44] = {0}; |
| 27 | static uint32_t g_key_of_the_hour[2] = {0}; | 29 | static volatile uint32_t g_key_of_the_hour[2] = {0}; |
| 28 | static ot_time g_hour_of_the_key; | 30 | static volatile ot_time g_hour_of_the_key; |
| 29 | 31 | ||
| 30 | static void udp_generate_rijndael_round_key() { | 32 | static pthread_mutex_t udp_start_mutex = PTHREAD_MUTEX_INITIALIZER; |
| 33 | static pthread_cond_t udp_start_cond = PTHREAD_COND_INITIALIZER; | ||
| 34 | static int g_opentracker_started = 0; | ||
| 35 | |||
| 36 | static void udp_generate_rijndael_round_key(void) { | ||
| 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 */ |
| 51 | static void udp_make_connectionid( uint32_t connid[2], const ot_ip6 remoteip, int age ) { | 46 | static void udp_make_connectionid(uint32_t connid[2], const ot_ip6 remoteip, int age) { |
| 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 */ |
| 72 | int handle_udp6( int64 serversocket, struct ot_workstruct *ws ) { | 74 | int 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 | ||
| 194 | static void* udp_worker( void * args ) { | 205 | static 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 | ||
| 216 | void udp_init( int64 sock, unsigned int worker_count ) { | 232 | void 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 | ||
| 227 | const char *g_version_udp_c = "$Source$: $Revision$\n"; | 243 | void udp_kickoff(void) { |
| 244 | pthread_mutex_lock(&udp_start_mutex); | ||
| 245 | g_opentracker_started = 1; | ||
| 246 | pthread_cond_broadcast(&udp_start_cond); | ||
| 247 | pthread_mutex_unlock(&udp_start_mutex); | ||
| 248 | } | ||
| @@ -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 | ||
| 9 | void udp_init( int64 sock, unsigned int worker_count ); | 9 | #include <stdint.h> |
| 10 | int handle_udp6( int64 serversocket, struct ot_workstruct *ws ); | 10 | |
| 11 | void udp_init(int64_t sock, unsigned int worker_count); | ||
| 12 | void udp_kickoff(void); | ||
| 13 | int 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 */ | 16 | static int vector_compare_peer6(const void *peer1, const void *peer2) { return memcmp(peer1, peer2, OT_PEER_COMPARE_SIZE6); } |
| 17 | #include "uint32.h" | 17 | static int vector_compare_peer4(const void *peer1, const void *peer2) { return memcmp(peer1, peer2, OT_PEER_COMPARE_SIZE4); } |
| 18 | #include "uint16.h" | ||
| 19 | |||
| 20 | static 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 | */ |
| 28 | void *binary_search( const void * const key, const void * base, const size_t member_count, const size_t member_size, | 23 | void *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 | ||
| 50 | static uint8_t vector_hash_peer( ot_peer *peer, int bucket_count ) { | 44 | static 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 | */ |
| 64 | void *vector_find_or_insert( ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch ) { | 59 | void *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 | ||
| 85 | ot_peer *vector_find_or_insert_peer( ot_vector *vector, ot_peer *peer, int *exactmatch ) { | 85 | ot_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 | */ |
| 116 | int vector_remove_peer( ot_vector *vector, ot_peer *peer ) { | 128 | int 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 | ||
| 138 | void vector_remove_torrent( ot_vector *vector, ot_torrent *match ) { | 153 | void 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 | ||
| 154 | void vector_clean_list( ot_vector * vector, int num_buckets ) { | 169 | void 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 | ||
| 161 | void vector_redistribute_buckets( ot_peerlist * peer_list ) { | 176 | void 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 | ||
| 247 | void vector_fixup_peers( ot_vector * vector ) { | 269 | void 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 | |||
| 266 | const 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 | ||
| 18 | typedef struct { | 18 | typedef 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 | ||
| 24 | void *binary_search( const void * const key, const void * base, const size_t member_count, const size_t member_size, | 24 | void *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 ); | 25 | void *vector_find_or_insert(ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch); |
| 26 | void *vector_find_or_insert( ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch ); | 26 | ot_peer *vector_find_or_insert_peer(ot_vector *vector, ot_peer const *peer, size_t peer_size, int *exactmatch); |
| 27 | ot_peer *vector_find_or_insert_peer( ot_vector *vector, ot_peer *peer, int *exactmatch ); | ||
| 28 | 27 | ||
| 29 | int vector_remove_peer( ot_vector *vector, ot_peer *peer ); | 28 | int vector_remove_peer(ot_vector *vector, ot_peer const *peer, size_t peer_size); |
| 30 | void vector_remove_torrent( ot_vector *vector, ot_torrent *match ); | 29 | void vector_remove_torrent(ot_vector *vector, ot_torrent *match); |
| 31 | void vector_redistribute_buckets( ot_peerlist * peer_list ); | 30 | |
| 32 | void vector_fixup_peers( ot_vector * vector ); | 31 | /* For ot_clean.c */ |
| 32 | void vector_redistribute_buckets(ot_peerlist *peer_list, size_t peer_size); | ||
| 33 | void vector_fixup_peers(ot_vector *vector, size_t peer_size); | ||
| 34 | void vector_clean_list(ot_vector *vector, size_t num_buckets); | ||
| 33 | 35 | ||
| 34 | #endif | 36 | #endif |
| @@ -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 @@ | |||
| 40 | ot_ip6 g_serverip; | 38 | ot_ip6 g_serverip; |
| 41 | uint16_t g_serverport = 9009; | 39 | uint16_t g_serverport = 9009; |
| 42 | uint32_t g_tracker_id; | 40 | uint32_t g_tracker_id; |
| 43 | char groupip_1[4] = { 224,0,23,5 }; | 41 | char groupip_1[4] = {224, 0, 23, 5}; |
| 44 | int g_self_pipe[2]; | 42 | int 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 | ||
| 64 | enum { OT_SYNC_PEER }; | 62 | enum { OT_SYNC_PEER4, OT_SYNC_PEER6 }; |
| 65 | enum { FLAG_SERVERSOCKET = 1 }; | 63 | enum { FLAG_SERVERSOCKET = 1 }; |
| 66 | 64 | ||
| 67 | /* For incoming packets */ | 65 | /* For incoming packets */ |
| 68 | static int64 g_socket_in = -1; | 66 | static int64_t g_socket_in = -1; |
| 69 | static uint8_t g_inbuffer[LIVESYNC_INCOMING_BUFFSIZE]; | 67 | static uint8_t g_inbuffer[LIVESYNC_INCOMING_BUFFSIZE]; |
| 70 | 68 | ||
| 71 | /* For outgoing packets */ | 69 | /* For outgoing packets */ |
| 72 | static int64 g_socket_out = -1; | 70 | static int64_t g_socket_out = -1; |
| 73 | static uint8_t g_peerbuffer_start[LIVESYNC_OUTGOING_BUFFSIZE_PEERS]; | 71 | static uint8_t g_peerbuffer_start[LIVESYNC_OUTGOING_BUFFSIZE_PEERS]; |
| 74 | static uint8_t *g_peerbuffer_pos; | 72 | static uint8_t *g_peerbuffer_pos; |
| 75 | static uint8_t *g_peerbuffer_highwater = g_peerbuffer_start + LIVESYNC_OUTGOING_BUFFSIZE_PEERS - LIVESYNC_OUTGOING_WATERMARK_PEERS; | 73 | static uint8_t *g_peerbuffer_highwater = g_peerbuffer_start + LIVESYNC_OUTGOING_BUFFSIZE_PEERS - LIVESYNC_OUTGOING_WATERMARK_PEERS; |
| 76 | static ot_time g_next_packet_time; | 74 | static ot_time g_next_packet_time; |
| 77 | 75 | ||
| 78 | static void * livesync_worker( void * args ); | 76 | static void *livesync_worker(void *args); |
| 79 | static void * streamsync_worker( void * args ); | 77 | static void *streamsync_worker(void *args); |
| 80 | static void livesync_proxytell( uint8_t prefix, uint8_t *info_hash, uint8_t *peer ); | 78 | static void livesync_proxytell(uint8_t prefix, uint8_t *info_hash, uint8_t *peer); |
| 81 | 79 | ||
| 82 | void exerr( char * message ) { | 80 | void 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 | ||
| 87 | void stats_issue_event( ot_status_event event, PROTO_FLAG proto, uintptr_t event_data ) { | 85 | void 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 | ||
| 93 | void livesync_bind_mcast( ot_ip6 ip, uint16_t port) { | 91 | void 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 | ||
| 123 | size_t add_peer_to_torrent_proxy( ot_hash hash, ot_peer *peer ) { | 122 | size_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 | ||
| 166 | size_t remove_peer_from_torrent_proxy( ot_hash hash, ot_peer *peer ) { | 170 | size_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 | ||
| 184 | void free_peerlist( ot_peerlist *peer_list ) { | 191 | void 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 | ||
| 197 | static void livesync_handle_peersync( ssize_t datalen ) { | 204 | static 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 | ||
| 215 | int usage( char *self ) { | 222 | int 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 | ||
| 238 | typedef struct { | 245 | typedef 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; |
| 254 | static void process_indata( proxy_peer * peer ); | 261 | static void process_indata(proxy_peer *peer); |
| 255 | 262 | ||
| 256 | void reset_info_block( proxy_peer * peer ) { | 263 | void 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 | */ |
| 271 | static int g_connection_count; | 278 | static size_t g_connection_count; |
| 272 | static ot_time g_connection_reconn; | 279 | static ot_time g_connection_reconn; |
| 273 | static proxy_peer g_connections[MAX_PEERS]; | 280 | static proxy_peer g_connections[MAX_PEERS]; |
| 274 | 281 | ||
| 275 | static void handle_reconnects( void ) { | 282 | static 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 */ |
| 304 | static void handle_accept( int64 serversocket ) { | 311 | static 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 */ |
| 347 | static void handle_read( int64 peersocket ) { | 354 | static 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; |
| 395 | close_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 */ |
| 417 | static void handle_write( int64 peersocket ) { | 423 | static 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 | ||
| 468 | static void server_mainloop() { | 474 | static 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 | ||
| 504 | static void panic( const char *routine ) { | 512 | static 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 | ||
| 509 | static int64_t ot_try_bind( ot_ip6 ip, uint16_t port ) { | 517 | static 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 | 534 | static int scan_ip6_port(const char *src, ot_ip6 ip, uint16_t *port) { | |
| 527 | static 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 | ||
| 548 | int main( int argc, char **argv ) { | 563 | int 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 | ||
| 599 | static void * streamsync_worker( void * args ) { | 624 | static 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)); |
| 707 | unlock_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 | ||
| 736 | static void livesync_issue_peersync( ) { | 778 | static 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 | ||
| 743 | void livesync_ticker( ) { | 784 | void 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 | ||
| 750 | static void livesync_proxytell( uint8_t prefix, uint8_t *info_hash, uint8_t *peer ) { | 790 | static 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 | ||
| 774 | static void process_indata( proxy_peer * peer ) { | 814 | static 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]; |
| 788 | printf( "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 |
| 806 | printf( "peers: %zd\n", peers ); | 849 | printf( "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 | ||
| 825 | static void * livesync_worker( void * args ) { | 868 | static 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¶m=value, it is encoded in bit 0, 1 and 2 | 25 | /path?param=value¶m=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 */ |
| 47 | static unsigned char fromhex(unsigned char x) { | 44 | static 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 */ |
| 55 | void scan_urlencoded_skipvalue( char **string ) { | 56 | void 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 | ||
| 70 | int scan_find_keywords( const ot_keywords * keywords, char **string, SCAN_SEARCHPATH_FLAG flags) { | 73 | int 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 | ||
| 86 | ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags) { | 91 | ssize_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 | ||
| 134 | ssize_t scan_fixed_int( char *data, size_t len, int *tmp ) { | 149 | ssize_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 | ||
| 143 | const char *g_version_scan_urlencoded_query_c = "$Source$: $Revision$\n"; | 163 | size_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 | ||
| 11 | typedef struct { | 13 | typedef 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 | */ |
| 41 | int scan_find_keywords( const ot_keywords * keywords, char **string, SCAN_SEARCHPATH_FLAG flags); | 43 | int 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 | */ |
| 46 | void scan_urlencoded_skipvalue( char **string ); | 48 | void 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 | */ |
| 53 | ssize_t scan_fixed_int( char *data, size_t len, int *number ); | 55 | ssize_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 | */ | ||
| 61 | size_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 | |||
| 12 | struct module { | ||
| 13 | const char *name; | ||
| 14 | void (*run)(void); | ||
| 15 | }; | ||
| 16 | |||
| 17 | static const struct module modules[] = {{"scan_urlencoded_query", test_scan_urlencoded_query}, | ||
| 18 | {"ot_iovec", test_iovec}, | ||
| 19 | {"ot_vector", test_vector}, | ||
| 20 | {"ot_random", test_random}, | ||
| 21 | {"ot_hex", test_hex}, | ||
| 22 | {"ot_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] */ | ||
| 29 | int 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. */ | ||
| 14 | void free_peerlist(ot_peerlist *peer_list) { (void)peer_list; } | ||
| 15 | |||
| 16 | /* ot_accesslist.c is built with WANT_ACCESSLIST_BLACK so that the hash parser | ||
| 17 | is reachable. These two belong to accesslist_readfile and the reload worker, | ||
| 18 | neither of which any test drives. */ | ||
| 19 | time_t g_now_seconds; | ||
| 20 | volatile 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 | |||
| 18 | static ot_hash dest[8]; | ||
| 19 | |||
| 20 | /* The parser is handed a mapped file, so it must never look at a byte beyond | ||
| 21 | the length it was given. These cases place a hex digit immediately after | ||
| 22 | that length: a parser that reads one byte too far sees it, decides the hash | ||
| 23 | runs on, and silently drops the last entry. */ | ||
| 24 | |||
| 25 | static void case_empty(void) { | ||
| 26 | test_case("an empty buffer yields nothing"); | ||
| 27 | CHECK_INT(accesslist_parse_hashes(dest, 8, "", 0), 0); | ||
| 28 | } | ||
| 29 | |||
| 30 | static void case_single_with_newline(void) { | ||
| 31 | const char buf[] = A "\n"; | ||
| 32 | |||
| 33 | test_case("one hash followed by a newline"); | ||
| 34 | CHECK_INT(accesslist_parse_hashes(dest, 8, buf, 41), 1); | ||
| 35 | } | ||
| 36 | |||
| 37 | static void case_single_without_newline(void) { | ||
| 38 | const char buf[] = A "A"; | ||
| 39 | |||
| 40 | test_case("one hash ending the buffer, with a hex digit just past the end"); | ||
| 41 | CHECK_INT(accesslist_parse_hashes(dest, 8, buf, 40), 1); | ||
| 42 | } | ||
| 43 | |||
| 44 | static void case_several_last_without_newline(void) { | ||
| 45 | const char buf[] = A "\n" B "\n" C "C"; | ||
| 46 | |||
| 47 | test_case("a final hash without a trailing newline still counts"); | ||
| 48 | CHECK_INT(accesslist_parse_hashes(dest, 8, buf, 3 * 40 + 2), 3); | ||
| 49 | } | ||
| 50 | |||
| 51 | static void case_run_on_hash_is_rejected(void) { | ||
| 52 | const char buf[] = A "A\n"; | ||
| 53 | |||
| 54 | test_case("forty one hex digits are not a hash"); | ||
| 55 | CHECK_INT(accesslist_parse_hashes(dest, 8, buf, 42), 0); | ||
| 56 | } | ||
| 57 | |||
| 58 | static void case_junk_lines_skipped(void) { | ||
| 59 | const char buf[] = "# a comment\n" A "\nnot a hash\n" B "\n"; | ||
| 60 | |||
| 61 | test_case("lines that are not hashes are ignored"); | ||
| 62 | CHECK_INT(accesslist_parse_hashes(dest, 8, buf, sizeof(buf) - 1), 2); | ||
| 63 | } | ||
| 64 | |||
| 65 | static void case_trailing_junk_on_line(void) { | ||
| 66 | const char buf[] = A " some comment\n" B "\n"; | ||
| 67 | |||
| 68 | test_case("a hash followed by other text on the same line still counts"); | ||
| 69 | CHECK_INT(accesslist_parse_hashes(dest, 8, buf, sizeof(buf) - 1), 2); | ||
| 70 | } | ||
| 71 | |||
| 72 | static void case_capacity_is_respected(void) { | ||
| 73 | const char buf[] = A "\n" B "\n" C "\n"; | ||
| 74 | |||
| 75 | test_case("no more than capacity hashes are written"); | ||
| 76 | CHECK_INT(accesslist_parse_hashes(dest, 2, buf, sizeof(buf) - 1), 2); | ||
| 77 | } | ||
| 78 | |||
| 79 | static void case_zero_capacity_never_writes(void) { | ||
| 80 | const char buf[] = A "\n"; | ||
| 81 | |||
| 82 | test_case("a zero capacity tolerates a null destination"); | ||
| 83 | CHECK_INT(accesslist_parse_hashes(NULL, 0, buf, 41), 0); | ||
| 84 | } | ||
| 85 | |||
| 86 | static void case_decoded_value(void) { | ||
| 87 | const char buf[] = "000102030405060708090A0B0C0D0E0F10111213\n"; | ||
| 88 | const uint8_t want[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; | ||
| 89 | |||
| 90 | test_case("the hash is decoded, not merely counted"); | ||
| 91 | CHECK_INT(accesslist_parse_hashes(dest, 8, buf, 41), 1); | ||
| 92 | CHECK_MEM(dest[0], want, sizeof(want)); | ||
| 93 | } | ||
| 94 | |||
| 95 | static void case_capacity_bound_holds(void) { | ||
| 96 | /* (len + 1) / 41 is what accesslist_readfile allocates. Pack a buffer as | ||
| 97 | densely as the parser allows and check the bound is not exceeded. */ | ||
| 98 | char buf[41 * 4]; | ||
| 99 | size_t len, capacity, found; | ||
| 100 | |||
| 101 | test_case("the capacity formula bounds what the parser can produce"); | ||
| 102 | memcpy(buf, A "\n" B "\n" C "\n" A, 4 * 40 + 3); | ||
| 103 | len = 4 * 40 + 3; | ||
| 104 | capacity = (len + 1) / 41; | ||
| 105 | found = accesslist_parse_hashes(dest, 8, buf, len); | ||
| 106 | CHECK_INT(found, 4); | ||
| 107 | CHECK(found <= capacity); | ||
| 108 | } | ||
| 109 | |||
| 110 | void test_accesslist(void) { | ||
| 111 | case_empty(); | ||
| 112 | case_single_with_newline(); | ||
| 113 | case_single_without_newline(); | ||
| 114 | case_several_last_without_newline(); | ||
| 115 | case_run_on_hash_is_rejected(); | ||
| 116 | case_junk_lines_skipped(); | ||
| 117 | case_trailing_junk_on_line(); | ||
| 118 | case_capacity_is_respected(); | ||
| 119 | case_zero_capacity_never_writes(); | ||
| 120 | case_decoded_value(); | ||
| 121 | case_capacity_bound_holds(); | ||
| 122 | } | ||
diff --git a/tests/t_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 | |||
| 14 | static 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 | |||
| 22 | static 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 | |||
| 35 | static 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 | |||
| 47 | static 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 | |||
| 72 | static 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 | |||
| 99 | static 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 | |||
| 118 | static 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 | |||
| 130 | static 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 | |||
| 140 | static 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 | |||
| 150 | static 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 | |||
| 172 | static 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 | |||
| 193 | void 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 | |||
| 14 | static const uint8_t sample[20] = {0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xff, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0}; | ||
| 15 | |||
| 16 | static void case_encode(void) { | ||
| 17 | char dest[64]; | ||
| 18 | |||
| 19 | test_case("a hash encodes to forty upper case digits and a terminator"); | ||
| 20 | memset(dest, 0x7f, sizeof(dest)); | ||
| 21 | CHECK_PTR(hex_fromhash(dest, sample), dest); | ||
| 22 | CHECK_STR(dest, "000123456789ABCDEFFF10203040506070809" "0A0"); | ||
| 23 | CHECK_INT(strlen(dest), 40); | ||
| 24 | CHECK_INT((unsigned char)dest[41], 0x7f); | ||
| 25 | } | ||
| 26 | |||
| 27 | static void case_decode(void) { | ||
| 28 | ot_hash hash; | ||
| 29 | |||
| 30 | test_case("forty digits decode back to the hash"); | ||
| 31 | CHECK(hex_tohash(hash, "000123456789ABCDEFFF10203040506070809" "0A0")); | ||
| 32 | CHECK_MEM(hash, sample, sizeof(sample)); | ||
| 33 | } | ||
| 34 | |||
| 35 | static void case_round_trip(void) { | ||
| 36 | ot_hash hash; | ||
| 37 | char text[64]; | ||
| 38 | size_t i; | ||
| 39 | |||
| 40 | test_case("encode and decode are inverses over every byte value"); | ||
| 41 | for (i = 0; i < 256; ++i) { | ||
| 42 | uint8_t probe[20]; | ||
| 43 | memset(probe, (uint8_t)i, sizeof(probe)); | ||
| 44 | hex_fromhash(text, probe); | ||
| 45 | if (!hex_tohash(hash, text) || memcmp(hash, probe, sizeof(probe))) { | ||
| 46 | test_failf(__FILE__, __LINE__, "round trip failed for byte value %zu", i); | ||
| 47 | return; | ||
| 48 | } | ||
| 49 | } | ||
| 50 | ++test_checks; | ||
| 51 | } | ||
| 52 | |||
| 53 | static void case_decode_is_case_insensitive(void) { | ||
| 54 | ot_hash upper, lower; | ||
| 55 | |||
| 56 | test_case("lower case digits decode the same as upper case"); | ||
| 57 | CHECK(hex_tohash(upper, "ABCDEF0123456789ABCDEF0123456789ABCDEF01")); | ||
| 58 | CHECK(hex_tohash(lower, "abcdef0123456789abcdef0123456789abcdef01")); | ||
| 59 | CHECK_MEM(lower, upper, sizeof(ot_hash)); | ||
| 60 | } | ||
| 61 | |||
| 62 | static void case_rejects_non_digits(void) { | ||
| 63 | ot_hash hash; | ||
| 64 | |||
| 65 | test_case("a non hex character anywhere rejects the whole string"); | ||
| 66 | CHECK_INT(hex_tohash(hash, "g00123456789ABCDEFFF102030405060708090A0"), 0); | ||
| 67 | CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF102030405060708090Ag"), 0); | ||
| 68 | CHECK_INT(hex_tohash(hash, "000123456789ABCDEF:F102030405060708090A"), 0); | ||
| 69 | } | ||
| 70 | |||
| 71 | static void case_rejects_short_input(void) { | ||
| 72 | ot_hash hash; | ||
| 73 | |||
| 74 | test_case("a string shorter than forty digits is rejected"); | ||
| 75 | CHECK_INT(hex_tohash(hash, ""), 0); | ||
| 76 | CHECK_INT(hex_tohash(hash, "00"), 0); | ||
| 77 | CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF102030405060708090A"), 0); | ||
| 78 | } | ||
| 79 | |||
| 80 | static void case_leaves_hash_untouched_on_failure(void) { | ||
| 81 | ot_hash hash; | ||
| 82 | |||
| 83 | test_case("a rejected string leaves the destination untouched"); | ||
| 84 | memset(hash, 0x5a, sizeof(hash)); | ||
| 85 | CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF10203040506070809XX0"), 0); | ||
| 86 | CHECK_MEM(hash, "\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a", sizeof(hash)); | ||
| 87 | } | ||
| 88 | |||
| 89 | static void case_stops_at_the_terminator(void) { | ||
| 90 | /* The saved state parser hands this whole buffers that may end early. The | ||
| 91 | decoder must not read past the zero that stopped it. */ | ||
| 92 | char guarded[8]; | ||
| 93 | ot_hash hash; | ||
| 94 | |||
| 95 | test_case("decoding stops at a terminator rather than running past it"); | ||
| 96 | memcpy(guarded, "0012", 4); | ||
| 97 | guarded[4] = 0; | ||
| 98 | memset(guarded + 5, 'f', 3); | ||
| 99 | CHECK_INT(hex_tohash(hash, guarded), 0); | ||
| 100 | } | ||
| 101 | |||
| 102 | static void case_isdigit(void) { | ||
| 103 | test_case("hex_isdigit accepts exactly the hex digits"); | ||
| 104 | CHECK(hex_isdigit('0')); | ||
| 105 | CHECK(hex_isdigit('9')); | ||
| 106 | CHECK(hex_isdigit('a')); | ||
| 107 | CHECK(hex_isdigit('f')); | ||
| 108 | CHECK(hex_isdigit('A')); | ||
| 109 | CHECK(hex_isdigit('F')); | ||
| 110 | CHECK_INT(hex_isdigit('g'), 0); | ||
| 111 | CHECK_INT(hex_isdigit('G'), 0); | ||
| 112 | CHECK_INT(hex_isdigit(':'), 0); | ||
| 113 | CHECK_INT(hex_isdigit('\n'), 0); | ||
| 114 | CHECK_INT(hex_isdigit(0), 0); | ||
| 115 | CHECK_INT(hex_isdigit(0x80), 0); | ||
| 116 | } | ||
| 117 | |||
| 118 | void test_hex(void) { | ||
| 119 | case_encode(); | ||
| 120 | case_decode(); | ||
| 121 | case_round_trip(); | ||
| 122 | case_decode_is_case_insensitive(); | ||
| 123 | case_rejects_non_digits(); | ||
| 124 | case_rejects_short_input(); | ||
| 125 | case_leaves_hash_untouched_on_failure(); | ||
| 126 | case_stops_at_the_terminator(); | ||
| 127 | case_isdigit(); | ||
| 128 | } | ||
diff --git a/tests/t_iovec.c b/tests/t_iovec.c new file mode 100644 index 0000000..89f3fda --- /dev/null +++ b/tests/t_iovec.c | |||
| @@ -0,0 +1,124 @@ | |||
| 1 | /* This software was written by Dirk Engling <erdgeist@erdgeist.org> | ||
| 2 | It is considered beerware. Prost. Skol. Cheers or whatever. | ||
| 3 | |||
| 4 | $id$ */ | ||
| 5 | |||
| 6 | /* System */ | ||
| 7 | #include <stdlib.h> | ||
| 8 | #include <string.h> | ||
| 9 | #include <sys/uio.h> | ||
| 10 | |||
| 11 | /* Opentracker */ | ||
| 12 | #include "ot_iovec.h" | ||
| 13 | #include "test.h" | ||
| 14 | |||
| 15 | static void case_increase_and_length(void) { | ||
| 16 | size_t entries = 0; | ||
| 17 | struct iovec *vector = NULL; | ||
| 18 | void *first, *second; | ||
| 19 | |||
| 20 | test_case("increase appends an entry and length is their sum"); | ||
| 21 | first = iovec_increase(&entries, &vector, 100); | ||
| 22 | CHECK(first != NULL); | ||
| 23 | CHECK_INT(entries, 1); | ||
| 24 | CHECK_INT(vector[0].iov_len, 100); | ||
| 25 | CHECK_PTR(vector[0].iov_base, first); | ||
| 26 | |||
| 27 | second = iovec_increase(&entries, &vector, 50); | ||
| 28 | CHECK(second != NULL); | ||
| 29 | CHECK_INT(entries, 2); | ||
| 30 | CHECK_INT(vector[1].iov_len, 50); | ||
| 31 | CHECK_INT(iovec_length(&entries, (const struct iovec **)&vector), 150); | ||
| 32 | |||
| 33 | iovec_free(&entries, &vector); | ||
| 34 | } | ||
| 35 | |||
| 36 | static void case_free_resets(void) { | ||
| 37 | size_t entries = 0; | ||
| 38 | struct iovec *vector = NULL; | ||
| 39 | |||
| 40 | test_case("free releases the entries and resets the vector"); | ||
| 41 | CHECK(iovec_increase(&entries, &vector, 10) != NULL); | ||
| 42 | CHECK(iovec_increase(&entries, &vector, 10) != NULL); | ||
| 43 | iovec_free(&entries, &vector); | ||
| 44 | CHECK_INT(entries, 0); | ||
| 45 | CHECK_PTR(vector, NULL); | ||
| 46 | CHECK_INT(iovec_length(&entries, (const struct iovec **)&vector), 0); | ||
| 47 | } | ||
| 48 | |||
| 49 | static void case_fixlast_shrinks(void) { | ||
| 50 | size_t entries = 0; | ||
| 51 | struct iovec *vector = NULL; | ||
| 52 | char *block; | ||
| 53 | |||
| 54 | test_case("fixlast trims the last entry to the bytes actually used"); | ||
| 55 | CHECK(iovec_increase(&entries, &vector, 64) != NULL); | ||
| 56 | block = iovec_increase(&entries, &vector, 1000); | ||
| 57 | CHECK(block != NULL); | ||
| 58 | memset(block, 'x', 20); | ||
| 59 | |||
| 60 | iovec_fixlast(&entries, &vector, block + 20); | ||
| 61 | CHECK_INT(entries, 2); | ||
| 62 | CHECK_INT(vector[1].iov_len, 20); | ||
| 63 | CHECK_INT(iovec_length(&entries, (const struct iovec **)&vector), 84); | ||
| 64 | CHECK_MEM(vector[1].iov_base, "xxxxxxxxxxxxxxxxxxxx", 20); | ||
| 65 | |||
| 66 | iovec_free(&entries, &vector); | ||
| 67 | } | ||
| 68 | |||
| 69 | static void case_fixlast_on_empty(void) { | ||
| 70 | size_t entries = 0; | ||
| 71 | struct iovec *vector = NULL; | ||
| 72 | |||
| 73 | test_case("fixlast on an empty vector does nothing"); | ||
| 74 | iovec_fixlast(&entries, &vector, NULL); | ||
| 75 | CHECK_INT(entries, 0); | ||
| 76 | CHECK_PTR(vector, NULL); | ||
| 77 | } | ||
| 78 | |||
| 79 | static void case_append_takes_ownership(void) { | ||
| 80 | size_t entries = 0; | ||
| 81 | struct iovec *vector = NULL; | ||
| 82 | struct iovec source; | ||
| 83 | |||
| 84 | test_case("append moves the buffer and clears the source"); | ||
| 85 | source.iov_base = malloc(10); | ||
| 86 | CHECK(source.iov_base != NULL); | ||
| 87 | memcpy(source.iov_base, "0123456789", 10); | ||
| 88 | source.iov_len = 10; | ||
| 89 | |||
| 90 | CHECK(iovec_append(&entries, &vector, &source) != NULL); | ||
| 91 | CHECK_INT(entries, 1); | ||
| 92 | CHECK_INT(vector[0].iov_len, 10); | ||
| 93 | CHECK_MEM(vector[0].iov_base, "0123456789", 10); | ||
| 94 | CHECK_PTR(source.iov_base, NULL); | ||
| 95 | CHECK_INT(source.iov_len, 0); | ||
| 96 | |||
| 97 | iovec_free(&entries, &vector); | ||
| 98 | } | ||
| 99 | |||
| 100 | static void case_fix_increase(void) { | ||
| 101 | size_t entries = 0; | ||
| 102 | struct iovec *vector = NULL; | ||
| 103 | char *block; | ||
| 104 | |||
| 105 | test_case("fix_increase trims the previous entry and adds a new one"); | ||
| 106 | block = iovec_increase(&entries, &vector, 500); | ||
| 107 | CHECK(block != NULL); | ||
| 108 | block = iovec_fix_increase_or_free(&entries, &vector, block + 30, 200); | ||
| 109 | CHECK(block != NULL); | ||
| 110 | CHECK_INT(entries, 2); | ||
| 111 | CHECK_INT(vector[0].iov_len, 30); | ||
| 112 | CHECK_INT(vector[1].iov_len, 200); | ||
| 113 | |||
| 114 | iovec_free(&entries, &vector); | ||
| 115 | } | ||
| 116 | |||
| 117 | void test_iovec(void) { | ||
| 118 | case_increase_and_length(); | ||
| 119 | case_free_resets(); | ||
| 120 | case_fixlast_shrinks(); | ||
| 121 | case_fixlast_on_empty(); | ||
| 122 | case_append_takes_ownership(); | ||
| 123 | case_fix_increase(); | ||
| 124 | } | ||
diff --git a/tests/t_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 | |||
| 14 | static char text[64]; | ||
| 15 | |||
| 16 | static 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 | |||
| 25 | static 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 | |||
| 52 | static 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 | |||
| 73 | static 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 | |||
| 94 | static 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 | |||
| 122 | static 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 | |||
| 133 | static 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 | |||
| 145 | static 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 | |||
| 174 | static 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 | |||
| 208 | void 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 | |||
| 24 | static void case_every_byte_varies(void) { | ||
| 25 | unsigned char first[BUFLEN], draw[BUFLEN]; | ||
| 26 | unsigned char varied[BUFLEN]; | ||
| 27 | size_t i, n; | ||
| 28 | |||
| 29 | test_case("every byte of the buffer comes from the generator"); | ||
| 30 | memset(varied, 0, sizeof(varied)); | ||
| 31 | random_bytes(first, sizeof(first)); | ||
| 32 | |||
| 33 | for (n = 0; n < DRAWS; ++n) { | ||
| 34 | random_bytes(draw, sizeof(draw)); | ||
| 35 | for (i = 0; i < BUFLEN; ++i) | ||
| 36 | if (draw[i] != first[i]) | ||
| 37 | varied[i] = 1; | ||
| 38 | } | ||
| 39 | |||
| 40 | /* A byte position that never moved across 32 draws is not being written. | ||
| 41 | A false alarm needs all 32 draws to agree by chance, around 256^-31. */ | ||
| 42 | for (i = 0; i < BUFLEN; ++i) | ||
| 43 | if (!varied[i]) { | ||
| 44 | test_failf(__FILE__, __LINE__, "byte %zu never changed over %d draws", i, DRAWS); | ||
| 45 | return; | ||
| 46 | } | ||
| 47 | ++test_checks; | ||
| 48 | } | ||
| 49 | |||
| 50 | static void case_respects_length(void) { | ||
| 51 | unsigned char buf[32]; | ||
| 52 | size_t i; | ||
| 53 | |||
| 54 | test_case("nothing is written past the requested length"); | ||
| 55 | memset(buf, 0xA5, sizeof(buf)); | ||
| 56 | random_bytes(buf, 8); | ||
| 57 | for (i = 8; i < sizeof(buf); ++i) | ||
| 58 | if (buf[i] != 0xA5) { | ||
| 59 | test_failf(__FILE__, __LINE__, "byte %zu past the end was overwritten", i); | ||
| 60 | return; | ||
| 61 | } | ||
| 62 | ++test_checks; | ||
| 63 | } | ||
| 64 | |||
| 65 | static void case_zero_length(void) { | ||
| 66 | unsigned char buf[4]; | ||
| 67 | |||
| 68 | test_case("a zero length request writes nothing"); | ||
| 69 | memset(buf, 0x5A, sizeof(buf)); | ||
| 70 | random_bytes(buf, 0); | ||
| 71 | CHECK_MEM(buf, "\x5a\x5a\x5a\x5a", 4); | ||
| 72 | } | ||
| 73 | |||
| 74 | static void case_successive_draws_differ(void) { | ||
| 75 | unsigned char a[32], b[32]; | ||
| 76 | |||
| 77 | test_case("two draws differ"); | ||
| 78 | random_bytes(a, sizeof(a)); | ||
| 79 | random_bytes(b, sizeof(b)); | ||
| 80 | CHECK(memcmp(a, b, sizeof(a)) != 0); | ||
| 81 | } | ||
| 82 | |||
| 83 | static void case_uint32_varies(void) { | ||
| 84 | uint32_t first = random_uint32(); | ||
| 85 | int differs = 0; | ||
| 86 | size_t n; | ||
| 87 | |||
| 88 | test_case("random_uint32 does not return a constant"); | ||
| 89 | for (n = 0; n < DRAWS; ++n) | ||
| 90 | if (random_uint32() != first) | ||
| 91 | differs = 1; | ||
| 92 | CHECK(differs); | ||
| 93 | } | ||
| 94 | |||
| 95 | static void case_uint32_uses_the_top_bit(void) { | ||
| 96 | uint32_t seen = 0; | ||
| 97 | size_t n; | ||
| 98 | |||
| 99 | test_case("random_uint32 spans the full 32 bits"); | ||
| 100 | /* random() never sets bit 31, so a build that returned it unmixed would | ||
| 101 | fail this. Over 64 draws the chance of a false alarm is 2^-64. */ | ||
| 102 | for (n = 0; n < 64; ++n) | ||
| 103 | seen |= random_uint32(); | ||
| 104 | CHECK_INT(seen & 0x80000000u, 0x80000000u); | ||
| 105 | } | ||
| 106 | |||
| 107 | void test_random(void) { | ||
| 108 | case_every_byte_varies(); | ||
| 109 | case_respects_length(); | ||
| 110 | case_zero_length(); | ||
| 111 | case_successive_draws_differ(); | ||
| 112 | case_uint32_varies(); | ||
| 113 | case_uint32_uses_the_top_bit(); | ||
| 114 | } | ||
diff --git a/tests/t_scan.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 | |||
| 14 | static 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 | |||
| 28 | static 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 | |||
| 40 | static 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 | |||
| 50 | static 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 | |||
| 60 | static 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 | |||
| 70 | static 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 | |||
| 80 | static 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 | |||
| 94 | static 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 | |||
| 106 | static 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 | |||
| 116 | static 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 | |||
| 126 | static 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 | |||
| 138 | static 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 | |||
| 149 | void 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 | |||
| 19 | static void case_path(void) { | ||
| 20 | char query[] = "/announce?info_hash=A"; | ||
| 21 | char dest[64]; | ||
| 22 | char *p = query; | ||
| 23 | ssize_t n; | ||
| 24 | |||
| 25 | test_case("path stops at the question mark"); | ||
| 26 | n = scan_urlencoded_query(&p, dest, SCAN_PATH); | ||
| 27 | CHECK_INT(n, 9); | ||
| 28 | dest[n] = 0; | ||
| 29 | CHECK_STR(dest, "/announce"); | ||
| 30 | CHECK_INT(*p, 'i'); | ||
| 31 | } | ||
| 32 | |||
| 33 | static void case_param_and_value(void) { | ||
| 34 | char query[] = "info_hash=abc&port=80"; | ||
| 35 | char dest[64]; | ||
| 36 | char *p = query; | ||
| 37 | ssize_t n; | ||
| 38 | |||
| 39 | test_case("parameter stops at '=', value stops at '&'"); | ||
| 40 | n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_PARAM); | ||
| 41 | CHECK_INT(n, 9); | ||
| 42 | dest[n] = 0; | ||
| 43 | CHECK_STR(dest, "info_hash"); | ||
| 44 | |||
| 45 | n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); | ||
| 46 | CHECK_INT(n, 3); | ||
| 47 | dest[n] = 0; | ||
| 48 | CHECK_STR(dest, "abc"); | ||
| 49 | |||
| 50 | n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_PARAM); | ||
| 51 | CHECK_INT(n, 4); | ||
| 52 | dest[n] = 0; | ||
| 53 | CHECK_STR(dest, "port"); | ||
| 54 | |||
| 55 | n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); | ||
| 56 | CHECK_INT(n, 2); | ||
| 57 | dest[n] = 0; | ||
| 58 | CHECK_STR(dest, "80"); | ||
| 59 | } | ||
| 60 | |||
| 61 | static void case_percent_decoding(void) { | ||
| 62 | char query[] = "%41%42%43&x=1"; | ||
| 63 | char dest[64]; | ||
| 64 | char *p = query; | ||
| 65 | ssize_t n; | ||
| 66 | |||
| 67 | test_case("percent escapes decode"); | ||
| 68 | n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); | ||
| 69 | CHECK_INT(n, 3); | ||
| 70 | CHECK_MEM(dest, "ABC", 3); | ||
| 71 | } | ||
| 72 | |||
| 73 | static void case_binary_info_hash(void) { | ||
| 74 | /* The twenty byte info_hash is what the announce path really decodes */ | ||
| 75 | char query[] = "%00%01%02%ff%fe%7f%80ABCDEFGHIJKLM&x=1"; | ||
| 76 | unsigned char want[] = {0x00, 0x01, 0x02, 0xff, 0xfe, 0x7f, 0x80, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M'}; | ||
| 77 | char dest[64]; | ||
| 78 | char *p = query; | ||
| 79 | ssize_t n; | ||
| 80 | |||
| 81 | test_case("twenty byte binary value survives decoding"); | ||
| 82 | n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); | ||
| 83 | CHECK_INT(n, 20); | ||
| 84 | CHECK_MEM(dest, want, 20); | ||
| 85 | } | ||
| 86 | |||
| 87 | static void case_in_place(void) { | ||
| 88 | char query[] = "%41%42&x=1"; | ||
| 89 | char *p = query; | ||
| 90 | ssize_t n; | ||
| 91 | |||
| 92 | test_case("decoding in place over the source is safe"); | ||
| 93 | n = scan_urlencoded_query(&p, query, SCAN_SEARCHPATH_VALUE); | ||
| 94 | CHECK_INT(n, 2); | ||
| 95 | CHECK_MEM(query, "AB", 2); | ||
| 96 | } | ||
| 97 | |||
| 98 | static void case_bad_escape(void) { | ||
| 99 | char query[] = "%zz"; | ||
| 100 | char dest[64]; | ||
| 101 | char *p = query; | ||
| 102 | ssize_t n; | ||
| 103 | |||
| 104 | test_case("a non hexadecimal escape is a parse error"); | ||
| 105 | n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); | ||
| 106 | CHECK_INT(n, -1); | ||
| 107 | } | ||
| 108 | |||
| 109 | static void case_truncated_escape(void) { | ||
| 110 | char query[] = "%4"; | ||
| 111 | char dest[64]; | ||
| 112 | char *p = query; | ||
| 113 | ssize_t n; | ||
| 114 | |||
| 115 | test_case("an escape cut short by the terminator is a parse error"); | ||
| 116 | n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); | ||
| 117 | CHECK_INT(n, -1); | ||
| 118 | } | ||
| 119 | |||
| 120 | static void case_terminator(void) { | ||
| 121 | char empty[] = ""; | ||
| 122 | char space[] = " rest"; | ||
| 123 | char dest[64]; | ||
| 124 | char *p = empty; | ||
| 125 | ssize_t n; | ||
| 126 | |||
| 127 | test_case("a hard terminator before any output reports end of input"); | ||
| 128 | n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_PARAM); | ||
| 129 | CHECK_INT(n, -2); | ||
| 130 | |||
| 131 | p = space; | ||
| 132 | n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_PARAM); | ||
| 133 | CHECK_INT(n, -2); | ||
| 134 | } | ||
| 135 | |||
| 136 | static void case_terminator_after_output(void) { | ||
| 137 | char query[] = "abc"; | ||
| 138 | char dest[64]; | ||
| 139 | char *p = query; | ||
| 140 | ssize_t n; | ||
| 141 | |||
| 142 | test_case("a hard terminator after output is left for the next call"); | ||
| 143 | n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); | ||
| 144 | CHECK_INT(n, 3); | ||
| 145 | CHECK_INT(*p, 0); | ||
| 146 | n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); | ||
| 147 | CHECK_INT(n, -2); | ||
| 148 | } | ||
| 149 | |||
| 150 | /* The character matrix makes '?' terminate a path only, and '=' terminate a | ||
| 151 | path or a parameter only. Both are ordinary characters inside a value, so a | ||
| 152 | value is free to contain a nested query string. */ | ||
| 153 | |||
| 154 | static void case_separators_are_contextual(void) { | ||
| 155 | char question[] = "a?b&x=1"; | ||
| 156 | char equals[] = "a=b&x=1"; | ||
| 157 | char dest[64]; | ||
| 158 | char *p = question; | ||
| 159 | ssize_t n; | ||
| 160 | |||
| 161 | test_case("a question mark is literal inside a value"); | ||
| 162 | n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); | ||
| 163 | CHECK_INT(n, 3); | ||
| 164 | dest[n] = 0; | ||
| 165 | CHECK_STR(dest, "a?b"); | ||
| 166 | |||
| 167 | test_case("an equals sign is literal inside a value"); | ||
| 168 | p = equals; | ||
| 169 | n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); | ||
| 170 | CHECK_INT(n, 3); | ||
| 171 | dest[n] = 0; | ||
| 172 | CHECK_STR(dest, "a=b"); | ||
| 173 | } | ||
| 174 | |||
| 175 | static void case_ampersand_in_path(void) { | ||
| 176 | char query[] = "/path&x"; | ||
| 177 | char dest[64]; | ||
| 178 | char *p = query; | ||
| 179 | ssize_t n; | ||
| 180 | |||
| 181 | test_case("an ampersand in a path is a parse error"); | ||
| 182 | n = scan_urlencoded_query(&p, dest, SCAN_PATH); | ||
| 183 | CHECK_INT(n, -1); | ||
| 184 | } | ||
| 185 | |||
| 186 | static void case_raw_high_byte(void) { | ||
| 187 | char query[] = {'a', 'b', (char)0x80, 0}; | ||
| 188 | char dest[64]; | ||
| 189 | char *p = query; | ||
| 190 | ssize_t n; | ||
| 191 | |||
| 192 | test_case("an undecoded byte above ASCII is a parse error"); | ||
| 193 | n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); | ||
| 194 | CHECK_INT(n, -1); | ||
| 195 | } | ||
| 196 | |||
| 197 | static void case_skipvalue(void) { | ||
| 198 | char query[] = "abc&next=1"; | ||
| 199 | char bare[] = "abc"; | ||
| 200 | char *p = query; | ||
| 201 | |||
| 202 | test_case("skipvalue steps over the value and its separator"); | ||
| 203 | scan_urlencoded_skipvalue(&p); | ||
| 204 | CHECK_STR(p, "next=1"); | ||
| 205 | |||
| 206 | test_case("skipvalue leaves a hard terminator in place"); | ||
| 207 | p = bare; | ||
| 208 | scan_urlencoded_skipvalue(&p); | ||
| 209 | CHECK_INT(*p, 0); | ||
| 210 | } | ||
| 211 | |||
| 212 | static void case_find_keywords(void) { | ||
| 213 | static const ot_keywords keywords[] = {{"port", 1}, {"left", 2}, {"compact", 3}, {NULL, -3}}; | ||
| 214 | char match[] = "left=0"; | ||
| 215 | char nomatch[] = "bogus=1"; | ||
| 216 | char prefix[] = "por=1"; | ||
| 217 | char *p = match; | ||
| 218 | |||
| 219 | test_case("a known keyword returns its value"); | ||
| 220 | CHECK_INT(scan_find_keywords(keywords, &p, SCAN_SEARCHPATH_PARAM), 2); | ||
| 221 | |||
| 222 | test_case("an unknown keyword returns -3"); | ||
| 223 | p = nomatch; | ||
| 224 | CHECK_INT(scan_find_keywords(keywords, &p, SCAN_SEARCHPATH_PARAM), -3); | ||
| 225 | |||
| 226 | test_case("a prefix of a keyword does not match it"); | ||
| 227 | p = prefix; | ||
| 228 | CHECK_INT(scan_find_keywords(keywords, &p, SCAN_SEARCHPATH_PARAM), -3); | ||
| 229 | } | ||
| 230 | |||
| 231 | static void case_fixed_int(void) { | ||
| 232 | char positive[] = "123"; | ||
| 233 | char negative[] = "-45"; | ||
| 234 | char trailing[] = "12a"; | ||
| 235 | int value; | ||
| 236 | |||
| 237 | test_case("scan_fixed_int returns the count of unparsed bytes"); | ||
| 238 | CHECK_INT(scan_fixed_int(positive, 3, &value), 0); | ||
| 239 | CHECK_INT(value, 123); | ||
| 240 | |||
| 241 | CHECK_INT(scan_fixed_int(negative, 3, &value), 0); | ||
| 242 | CHECK_INT(value, -45); | ||
| 243 | |||
| 244 | CHECK_INT(scan_fixed_int(trailing, 3, &value), 1); | ||
| 245 | CHECK_INT(value, 12); | ||
| 246 | } | ||
| 247 | |||
| 248 | static void case_fixed_int_bounded(void) { | ||
| 249 | char digits[] = "12345"; | ||
| 250 | int value; | ||
| 251 | |||
| 252 | test_case("scan_fixed_int honours the length it is given"); | ||
| 253 | CHECK_INT(scan_fixed_int(digits, 3, &value), 0); | ||
| 254 | CHECK_INT(value, 123); | ||
| 255 | } | ||
| 256 | |||
| 257 | |||
| 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 | |||
| 262 | static 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 | |||
| 274 | static 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 | |||
| 286 | static 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 | |||
| 298 | static 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 | |||
| 306 | static 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 | |||
| 315 | static 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 | |||
| 334 | static 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 | |||
| 357 | void 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 | |||
| 18 | static const unsigned char sorted[5][4] = {{0, 0, 0, 1}, {0, 0, 0, 3}, {0, 0, 0, 5}, {0, 0, 0, 7}, {0, 0, 0, 9}}; | ||
| 19 | |||
| 20 | static void case_search_finds_member(void) { | ||
| 21 | const unsigned char key[4] = {0, 0, 0, 5}; | ||
| 22 | int exactmatch; | ||
| 23 | void *found; | ||
| 24 | |||
| 25 | test_case("an existing key is found"); | ||
| 26 | found = binary_search(key, sorted, 5, 4, 4, &exactmatch); | ||
| 27 | CHECK(exactmatch != 0); | ||
| 28 | CHECK_PTR(found, sorted[2]); | ||
| 29 | } | ||
| 30 | |||
| 31 | static void case_search_finds_first_and_last(void) { | ||
| 32 | const unsigned char first[4] = {0, 0, 0, 1}; | ||
| 33 | const unsigned char last[4] = {0, 0, 0, 9}; | ||
| 34 | int exactmatch; | ||
| 35 | |||
| 36 | test_case("the first and last members are found"); | ||
| 37 | CHECK_PTR(binary_search(first, sorted, 5, 4, 4, &exactmatch), sorted[0]); | ||
| 38 | CHECK(exactmatch != 0); | ||
| 39 | CHECK_PTR(binary_search(last, sorted, 5, 4, 4, &exactmatch), sorted[4]); | ||
| 40 | CHECK(exactmatch != 0); | ||
| 41 | } | ||
| 42 | |||
| 43 | static void case_search_insert_position(void) { | ||
| 44 | const unsigned char middle[4] = {0, 0, 0, 4}; | ||
| 45 | const unsigned char before[4] = {0, 0, 0, 0}; | ||
| 46 | int exactmatch; | ||
| 47 | void *slot; | ||
| 48 | |||
| 49 | test_case("a missing key yields its insert position"); | ||
| 50 | slot = binary_search(middle, sorted, 5, 4, 4, &exactmatch); | ||
| 51 | CHECK_INT(exactmatch, 0); | ||
| 52 | CHECK_PTR(slot, sorted[2]); | ||
| 53 | |||
| 54 | slot = binary_search(before, sorted, 5, 4, 4, &exactmatch); | ||
| 55 | CHECK_INT(exactmatch, 0); | ||
| 56 | CHECK_PTR(slot, sorted[0]); | ||
| 57 | } | ||
| 58 | |||
| 59 | static void case_search_past_the_end(void) { | ||
| 60 | const unsigned char beyond[4] = {0, 0, 0, 0xff}; | ||
| 61 | int exactmatch; | ||
| 62 | void *slot; | ||
| 63 | |||
| 64 | test_case("a key above every member yields the position past the end"); | ||
| 65 | slot = binary_search(beyond, sorted, 5, 4, 4, &exactmatch); | ||
| 66 | CHECK_INT(exactmatch, 0); | ||
| 67 | CHECK_PTR(slot, sorted[5]); | ||
| 68 | } | ||
| 69 | |||
| 70 | static void case_search_empty(void) { | ||
| 71 | int exactmatch; | ||
| 72 | void *base = (void *)sorted; | ||
| 73 | |||
| 74 | test_case("searching an empty vector reports no match at the base"); | ||
| 75 | CHECK_PTR(binary_search(sorted[0], base, 0, 4, 4, &exactmatch), base); | ||
| 76 | CHECK_INT(exactmatch, 0); | ||
| 77 | } | ||
| 78 | |||
| 79 | static void case_insert_keeps_order(void) { | ||
| 80 | ot_vector vector = {NULL, 0, 0}; | ||
| 81 | const unsigned char five[4] = {0, 0, 0, 5}; | ||
| 82 | const unsigned char one[4] = {0, 0, 0, 1}; | ||
| 83 | const unsigned char nine[4] = {0, 0, 0, 9}; | ||
| 84 | int exactmatch; | ||
| 85 | void *slot; | ||
| 86 | |||
| 87 | test_case("inserts land in sorted order and grow the vector"); | ||
| 88 | slot = vector_find_or_insert(&vector, (void *)five, 4, 4, &exactmatch); | ||
| 89 | CHECK(slot != NULL); | ||
| 90 | CHECK_INT(exactmatch, 0); | ||
| 91 | memcpy(slot, five, 4); | ||
| 92 | CHECK_INT(vector.size, 1); | ||
| 93 | CHECK_INT(vector.space, OT_VECTOR_MIN_MEMBERS); | ||
| 94 | |||
| 95 | slot = vector_find_or_insert(&vector, (void *)one, 4, 4, &exactmatch); | ||
| 96 | CHECK(slot != NULL); | ||
| 97 | CHECK_INT(exactmatch, 0); | ||
| 98 | memcpy(slot, one, 4); | ||
| 99 | CHECK_INT(vector.size, 2); | ||
| 100 | |||
| 101 | slot = vector_find_or_insert(&vector, (void *)nine, 4, 4, &exactmatch); | ||
| 102 | CHECK(slot != NULL); | ||
| 103 | CHECK_INT(exactmatch, 0); | ||
| 104 | memcpy(slot, nine, 4); | ||
| 105 | CHECK_INT(vector.size, 3); | ||
| 106 | CHECK_INT(vector.space, OT_VECTOR_MIN_MEMBERS * OT_VECTOR_GROW_RATIO); | ||
| 107 | |||
| 108 | CHECK_MEM(vector.data, "\0\0\0\1\0\0\0\5\0\0\0\11", 12); | ||
| 109 | |||
| 110 | free(vector.data); | ||
| 111 | } | ||
| 112 | |||
| 113 | static void case_insert_finds_existing(void) { | ||
| 114 | ot_vector vector = {NULL, 0, 0}; | ||
| 115 | const unsigned char five[4] = {0, 0, 0, 5}; | ||
| 116 | int exactmatch; | ||
| 117 | void *slot, *again; | ||
| 118 | |||
| 119 | test_case("a second insert of the same key returns the existing member"); | ||
| 120 | slot = vector_find_or_insert(&vector, (void *)five, 4, 4, &exactmatch); | ||
| 121 | CHECK(slot != NULL); | ||
| 122 | memcpy(slot, five, 4); | ||
| 123 | |||
| 124 | again = vector_find_or_insert(&vector, (void *)five, 4, 4, &exactmatch); | ||
| 125 | CHECK(exactmatch != 0); | ||
| 126 | CHECK_PTR(again, vector.data); | ||
| 127 | CHECK_INT(vector.size, 1); | ||
| 128 | |||
| 129 | free(vector.data); | ||
| 130 | } | ||
| 131 | |||
| 132 | static void case_insert_compare_prefix(void) { | ||
| 133 | ot_vector vector = {NULL, 0, 0}; | ||
| 134 | unsigned char first[6] = {1, 2, 3, 0xaa, 0xaa, 0xaa}; | ||
| 135 | unsigned char second[6] = {1, 2, 3, 0xbb, 0xbb, 0xbb}; | ||
| 136 | int exactmatch; | ||
| 137 | void *slot; | ||
| 138 | |||
| 139 | test_case("only the compare prefix decides identity"); | ||
| 140 | slot = vector_find_or_insert(&vector, first, 6, 3, &exactmatch); | ||
| 141 | CHECK(slot != NULL); | ||
| 142 | memcpy(slot, first, 6); | ||
| 143 | |||
| 144 | slot = vector_find_or_insert(&vector, second, 6, 3, &exactmatch); | ||
| 145 | CHECK(exactmatch != 0); | ||
| 146 | CHECK_INT(vector.size, 1); | ||
| 147 | CHECK_MEM(slot, first, 6); | ||
| 148 | |||
| 149 | free(vector.data); | ||
| 150 | } | ||
| 151 | |||
| 152 | void test_vector(void) { | ||
| 153 | case_search_finds_member(); | ||
| 154 | case_search_finds_first_and_last(); | ||
| 155 | case_search_insert_position(); | ||
| 156 | case_search_past_the_end(); | ||
| 157 | case_search_empty(); | ||
| 158 | case_insert_keeps_order(); | ||
| 159 | case_insert_finds_existing(); | ||
| 160 | case_insert_compare_prefix(); | ||
| 161 | } | ||
diff --git a/tests/test.c b/tests/test.c new file mode 100644 index 0000000..4686369 --- /dev/null +++ b/tests/test.c | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | /* This software was written by Dirk Engling <erdgeist@erdgeist.org> | ||
| 2 | It is considered beerware. Prost. Skol. Cheers or whatever. | ||
| 3 | |||
| 4 | $id$ */ | ||
| 5 | |||
| 6 | /* System */ | ||
| 7 | #include <stdarg.h> | ||
| 8 | #include <stdio.h> | ||
| 9 | |||
| 10 | /* Opentracker */ | ||
| 11 | #include "test.h" | ||
| 12 | |||
| 13 | unsigned long test_checks; | ||
| 14 | unsigned long test_failures; | ||
| 15 | |||
| 16 | static const char *current_module = ""; | ||
| 17 | static const char *current_case = ""; | ||
| 18 | |||
| 19 | void test_module(const char *name) { | ||
| 20 | current_module = name; | ||
| 21 | current_case = ""; | ||
| 22 | printf("%s\n", name); | ||
| 23 | } | ||
| 24 | |||
| 25 | void test_case(const char *name) { current_case = name; } | ||
| 26 | |||
| 27 | static void test_where(const char *file, int line) { | ||
| 28 | ++test_failures; | ||
| 29 | printf(" FAIL %s/%s (%s:%d)\n ", current_module, current_case, file, line); | ||
| 30 | } | ||
| 31 | |||
| 32 | void test_failf(const char *file, int line, const char *fmt, ...) { | ||
| 33 | va_list ap; | ||
| 34 | test_where(file, line); | ||
| 35 | va_start(ap, fmt); | ||
| 36 | vprintf(fmt, ap); | ||
| 37 | va_end(ap); | ||
| 38 | printf("\n"); | ||
| 39 | } | ||
| 40 | |||
| 41 | void test_failmem(const char *file, int line, const char *what, const void *got, const void *want, size_t len) { | ||
| 42 | const unsigned char *g = got, *w = want; | ||
| 43 | size_t i; | ||
| 44 | |||
| 45 | test_where(file, line); | ||
| 46 | printf("%s: %zu bytes differ\n got ", what, len); | ||
| 47 | for (i = 0; i < len; ++i) | ||
| 48 | printf("%02x", g[i]); | ||
| 49 | printf("\n want "); | ||
| 50 | for (i = 0; i < len; ++i) | ||
| 51 | printf("%02x", w[i]); | ||
| 52 | printf("\n"); | ||
| 53 | } | ||
| 54 | |||
| 55 | int test_summary(void) { | ||
| 56 | printf("\n%lu checks, %lu failures\n", test_checks, test_failures); | ||
| 57 | return test_failures != 0; | ||
| 58 | } | ||
diff --git a/tests/test.h b/tests/test.h new file mode 100644 index 0000000..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 | |||
| 21 | void test_module(const char *name); | ||
| 22 | void test_case(const char *name); | ||
| 23 | void test_failf(const char *file, int line, const char *fmt, ...); | ||
| 24 | void test_failmem(const char *file, int line, const char *what, const void *got, const void *want, size_t len); | ||
| 25 | int test_summary(void); | ||
| 26 | |||
| 27 | extern unsigned long test_checks; | ||
| 28 | extern unsigned long test_failures; | ||
| 29 | |||
| 30 | #define CHECK(cond) \ | ||
| 31 | do { \ | ||
| 32 | ++test_checks; \ | ||
| 33 | if (!(cond)) { \ | ||
| 34 | test_failf(__FILE__, __LINE__, "expected %s", #cond); \ | ||
| 35 | return; \ | ||
| 36 | } \ | ||
| 37 | } while (0) | ||
| 38 | |||
| 39 | #define CHECK_INT(got, want) \ | ||
| 40 | do { \ | ||
| 41 | long long got_ = (long long)(got), want_ = (long long)(want); \ | ||
| 42 | ++test_checks; \ | ||
| 43 | if (got_ != want_) { \ | ||
| 44 | test_failf(__FILE__, __LINE__, "%s: got %lld, want %lld", #got, got_, want_); \ | ||
| 45 | return; \ | ||
| 46 | } \ | ||
| 47 | } while (0) | ||
| 48 | |||
| 49 | #define CHECK_PTR(got, want) \ | ||
| 50 | do { \ | ||
| 51 | const void *got_ = (const void *)(got), *want_ = (const void *)(want); \ | ||
| 52 | ++test_checks; \ | ||
| 53 | if (got_ != want_) { \ | ||
| 54 | test_failf(__FILE__, __LINE__, "%s: got %p, want %p", #got, got_, want_); \ | ||
| 55 | return; \ | ||
| 56 | } \ | ||
| 57 | } while (0) | ||
| 58 | |||
| 59 | #define CHECK_MEM(got, want, len) \ | ||
| 60 | do { \ | ||
| 61 | ++test_checks; \ | ||
| 62 | if (memcmp((got), (want), (len))) { \ | ||
| 63 | test_failmem(__FILE__, __LINE__, #got, (got), (want), (len)); \ | ||
| 64 | return; \ | ||
| 65 | } \ | ||
| 66 | } while (0) | ||
| 67 | |||
| 68 | #define CHECK_STR(got, want) \ | ||
| 69 | do { \ | ||
| 70 | const char *got_ = (got), *want_ = (want); \ | ||
| 71 | ++test_checks; \ | ||
| 72 | if (strcmp(got_, want_)) { \ | ||
| 73 | test_failf(__FILE__, __LINE__, "%s: got \"%s\", want \"%s\"", #got, got_, want_); \ | ||
| 74 | return; \ | ||
| 75 | } \ | ||
| 76 | } while (0) | ||
| 77 | |||
| 78 | /* One entry point per module under test */ | ||
| 79 | void test_scan_urlencoded_query(void); | ||
| 80 | void test_iovec(void); | ||
| 81 | void test_vector(void); | ||
| 82 | void test_random(void); | ||
| 83 | void test_hex(void); | ||
| 84 | void test_accesslist(void); | ||
| 85 | void test_scan(void); | ||
| 86 | void test_buf(void); | ||
| 87 | void 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 | ||
| 3 | while true; do | 3 | while 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 | ||
| 14 | done | 22 | done |
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 */ |
| 32 | size_t return_peers_for_torrent( struct ot_workstruct * ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto ); | 32 | size_t return_peers_for_torrent(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto); |
| 33 | |||
| 34 | void 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-- ) | 34 | void 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 | ||
| 47 | void add_torrent_from_saved_state( ot_hash hash, ot_time base, size_t down_count ) { | 44 | void 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 | ||
| 74 | size_t add_peer_to_torrent_and_return_peers( PROTO_FLAG proto, struct ot_workstruct *ws, size_t amount ) { | 80 | size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstruct *ws, size_t amount) { |
| 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 | ||
| 186 | static size_t return_peers_all( ot_peerlist *peer_list, char *reply ) { | 200 | static 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 | ||
| 213 | static size_t return_peers_selection( struct ot_workstruct *ws, ot_peerlist *peer_list, size_t amount, char *reply ) { | 229 | static size_t return_peers_selection(struct ot_workstruct *ws, ot_peerlist *peer_list, size_t peer_size, size_t amount, char *reply) { |
| 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 | 280 | static 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; |
| 265 | size_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 | |||
| 304 | static 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 | */ | ||
| 379 | size_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 */ |
| 296 | size_t return_udp_scrape_for_torrent( ot_hash hash, char *reply ) { | 384 | size_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 */ |
| 321 | size_t return_tcp_scrape_for_torrent( ot_hash *hash_list, int amount, char *reply ) { | 409 | size_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 | ||
| 351 | static ot_peerlist dummy_list; | 445 | static ot_peerlist dummy_list; |
| 352 | size_t remove_peer_from_torrent( PROTO_FLAG proto, struct ot_workstruct *ws ) { | 446 | size_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 | ||
| 391 | void iterate_all_torrents( int (*for_each)( ot_torrent* torrent, uintptr_t data ), uintptr_t data ) { | 495 | void iterate_all_torrents(int (*for_each)(ot_torrent *torrent, uintptr_t data), uintptr_t data) { |
| 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 | |||
| 512 | ot_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 | |||
| 522 | size_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 | ||
| 530 | void 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 | ||
| 408 | void exerr( char * message ) { | 553 | void 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 | ||
| 413 | void trackerlogic_init( ) { | 558 | void 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 | ||
| 429 | void trackerlogic_deinit( void ) { | 575 | void 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 | |||
| 457 | const 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 | ||
| 22 | typedef uint8_t ot_hash[20]; | 15 | typedef uint8_t ot_hash[20]; |
| 23 | typedef time_t ot_time; | 16 | typedef time_t ot_time; |
| 24 | typedef char ot_ip6[16]; | 17 | typedef char ot_ip6[16]; |
| 25 | typedef struct { ot_ip6 address; int bits; } | 18 | typedef 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 */ |
| 68 | extern time_t g_now_seconds; | 71 | extern time_t g_now_seconds; |
| 69 | extern volatile int g_opentracker_running; | 72 | extern 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 | ||
| 72 | extern uint32_t g_tracker_id; | 75 | extern uint32_t g_tracker_id; |
| 73 | typedef enum { FLAG_TCP, FLAG_UDP, FLAG_MCA, FLAG_SELFPIPE } PROTO_FLAG; | 76 | typedef enum { FLAG_TCP, FLAG_UDP, FLAG_MCA, FLAG_SELFPIPE } PROTO_FLAG; |
| 74 | 77 | ||
| 75 | typedef 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 | |||
| 85 | typedef uint8_t ot_peer; /* Generic pointer to a v6 or v4 peer */ | ||
| 86 | typedef uint8_t ot_peer6[OT_PEER_SIZE6]; | ||
| 87 | typedef uint8_t ot_peer4[OT_PEER_SIZE4]; | ||
| 78 | static const uint8_t PEER_FLAG_SEEDING = 0x80; | 88 | static const uint8_t PEER_FLAG_SEEDING = 0x80; |
| 79 | static const uint8_t PEER_FLAG_COMPLETED = 0x40; | 89 | static const uint8_t PEER_FLAG_COMPLETED = 0x40; |
| 80 | static const uint8_t PEER_FLAG_STOPPED = 0x20; | 90 | static const uint8_t PEER_FLAG_STOPPED = 0x20; |
| 81 | static const uint8_t PEER_FLAG_FROM_SYNC = 0x10; | 91 | static const uint8_t PEER_FLAG_FROM_SYNC = 0x10; |
| 82 | static const uint8_t PEER_FLAG_LEECHING = 0x00; | 92 | static 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)) | 95 | ot_peer *peer_from_peer6(ot_peer6 *peer, size_t *peer_size); |
| 86 | #else | 96 | size_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 | ||
| 96 | struct ot_peerlist; | 110 | struct ot_peerlist; |
| 97 | typedef struct ot_peerlist ot_peerlist; | 111 | typedef struct ot_peerlist ot_peerlist; |
| 98 | typedef struct { | 112 | typedef 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 | ||
| 105 | struct ot_peerlist { | 120 | struct 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 | ||
| 117 | struct ot_workstruct { | 132 | struct 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 | ||
| 166 | void trackerlogic_init( ); | 182 | void trackerlogic_init(void); |
| 167 | void trackerlogic_deinit( void ); | 183 | void trackerlogic_deinit(void); |
| 168 | void exerr( char * message ); | 184 | void 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 */ |
| 172 | size_t add_peer_to_torrent_and_return_peers( PROTO_FLAG proto, struct ot_workstruct *ws, size_t amount ); | 188 | size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstruct *ws, size_t amount); |
| 173 | size_t remove_peer_from_torrent( PROTO_FLAG proto, struct ot_workstruct *ws ); | 189 | size_t remove_peer_from_torrent(PROTO_FLAG proto, struct ot_workstruct *ws); |
| 174 | size_t return_tcp_scrape_for_torrent( ot_hash *hash, int amount, char *reply ); | 190 | size_t return_tcp_scrape_for_torrent(ot_hash const *hash_list, size_t amount, char *reply); |
| 175 | size_t return_udp_scrape_for_torrent( ot_hash hash, char *reply ); | 191 | size_t return_udp_scrape_for_torrent(ot_hash const hash, char *reply); |
| 176 | void add_torrent_from_saved_state( ot_hash hash, ot_time base, size_t down_count ); | 192 | void add_torrent_from_saved_state(ot_hash const hash, ot_time base, size_t down_count); |
| 193 | #ifdef _DEBUG_RANDOMTORRENTS | ||
| 194 | void trackerlogic_add_random_torrents(size_t amount); | ||
| 195 | #endif | ||
| 177 | 196 | ||
| 178 | /* torrent iterator */ | 197 | /* torrent iterator */ |
| 179 | void iterate_all_torrents( int (*for_each)( ot_torrent* torrent, uintptr_t data ), uintptr_t data ); | 198 | void 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 */ |
| 182 | void free_peerlist( ot_peerlist *peer_list ); | 201 | void free_peerlist(ot_peerlist *peer_list); |
| 183 | 202 | ||
| 184 | #endif | 203 | #endif |
