mirror of
https://github.com/savoirfairelinux/opendht.git
synced 2026-11-11 22:07:10 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c60c6b8af2 | ||
|
|
9f7e4aaa96 | ||
|
|
0bfd3cd9fc | ||
|
|
442e5501d8 | ||
|
|
0203bd8318 | ||
|
|
cd07d68bef | ||
|
|
88f3870f20 | ||
|
|
d9d2c44e0f | ||
|
|
f427f36d9c | ||
|
|
a1c881d914 | ||
|
|
fbc489863f | ||
|
|
504a3638d9 | ||
|
|
a85abccd62 | ||
|
|
afce0fb0b4 | ||
|
|
99f0334229 | ||
|
|
55ac11bf5c | ||
|
|
e028c1be8c | ||
|
|
3e8d9835d4 | ||
|
|
18df23bd50 | ||
|
|
6b8c3ea581 | ||
|
|
7645f284e4 | ||
|
|
2cec478dfc | ||
|
|
54f07e449c | ||
|
|
09308321f8 | ||
|
|
e4b333b672 | ||
|
|
69f382449d | ||
|
|
47d2f41930 | ||
|
|
d6b124eec0 | ||
|
|
0330ee6683 | ||
|
|
1b854b8a09 | ||
|
|
4f6c561540 | ||
|
|
79ffe663f6 | ||
|
|
a14a97e3a2 | ||
|
|
bddc34d47b | ||
|
|
de6ea1d632 | ||
|
|
1d2fd80fad | ||
|
|
f7df3413e1 | ||
|
|
4cb67002e3 | ||
|
|
197a9a6bcd | ||
|
|
8641de139d | ||
|
|
adcb61bbf9 | ||
|
|
70709c9a68 | ||
|
|
bf930de9e4 | ||
|
|
bb4e716fc7 | ||
|
|
54fd6cf64f | ||
|
|
d905a02b32 | ||
|
|
d12f9271b8 | ||
|
|
3639d651a2 | ||
|
|
07824391a2 | ||
|
|
c855f495d0 | ||
|
|
cc5c148fc1 | ||
|
|
949751f632 | ||
|
|
6dfc4e4ba1 | ||
|
|
db8d62e17b | ||
|
|
7226775987 | ||
|
|
9c264e1cad | ||
|
|
deee57b6dd | ||
|
|
95f0b27eae | ||
|
|
b6f98a8488 | ||
|
|
73acc27a11 | ||
|
|
d46e302b3f | ||
|
|
6f57e03b48 | ||
|
|
a5e39768e4 | ||
|
|
6f6fdba8dd | ||
|
|
e5ef0a8900 | ||
|
|
b6f21933ab | ||
|
|
e4b8b0259d | ||
|
|
8134d6201a | ||
|
|
db4cd7dcc1 | ||
|
|
9d6107c01c | ||
|
|
de46d55b13 | ||
|
|
75afd9d9dc | ||
|
|
99c56d13bb | ||
|
|
95fa29d2ec | ||
|
|
f3d9d8414d | ||
|
|
0c9d715d6d | ||
|
|
cfff247e06 | ||
|
|
04edc70fee | ||
|
|
fdc4bdee68 | ||
|
|
69bf7afe31 | ||
|
|
7b3ee44175 | ||
|
|
122c30cce8 | ||
|
|
957e05319f | ||
|
|
58f83ab3c6 | ||
|
|
6151ee8c0b | ||
|
|
c08b6a7f5d | ||
|
|
0139e7a2d4 | ||
|
|
416de4b4f8 | ||
|
|
b45f32e8f8 | ||
|
|
cda5b0afa2 | ||
|
|
f66a57b033 | ||
|
|
72a688cd14 | ||
|
|
e882efe89e | ||
|
|
7b49ed873c | ||
|
|
3fec6e7636 | ||
|
|
67ac7a4d86 | ||
|
|
615534fa0f | ||
|
|
4250336951 | ||
|
|
932cacbda3 | ||
|
|
3b254663ca | ||
|
|
18f74693e9 | ||
|
|
e0cc973aa4 | ||
|
|
c866d46798 | ||
|
|
c762e4e43f | ||
|
|
3eed83c22d | ||
|
|
234f47a018 | ||
|
|
615463e103 | ||
|
|
d50585188d | ||
|
|
dd247d15ba | ||
|
|
4a34045c0f | ||
|
|
edfdb39970 | ||
|
|
89551496c7 | ||
|
|
becb878f5f | ||
|
|
a94f99dcd2 | ||
|
|
8de9584b72 | ||
|
|
303a3c2c5a | ||
|
|
e6dcc3226d | ||
|
|
ab71e5a59e | ||
|
|
bf9561c4af | ||
|
|
c139fc9791 | ||
|
|
31ddf3cc27 | ||
|
|
1bbdc3e673 | ||
|
|
60ecb95cf6 | ||
|
|
075ea59244 | ||
|
|
2f98f1f987 | ||
|
|
2b22fe9055 | ||
|
|
e144460869 | ||
|
|
6b54c42fdb | ||
|
|
57587b6cd8 | ||
|
|
0fd642f92d | ||
|
|
91310c83a7 | ||
|
|
daf2f8edaf | ||
|
|
a957cdba86 | ||
|
|
5a9a8dc67a | ||
|
|
5fe0dad574 | ||
|
|
a051b391a8 | ||
|
|
ec27ff6c49 | ||
|
|
0e1042124e | ||
|
|
f7e7101abc | ||
|
|
26957ee632 | ||
|
|
04fa07edfe | ||
|
|
6cf763dfa4 | ||
|
|
c41e42e58c | ||
|
|
1b238083d3 | ||
|
|
0784270c2e | ||
|
|
e675bb62fd | ||
|
|
43ac0e14e0 | ||
|
|
f4d494cb3d | ||
|
|
b3dfbf13d8 | ||
|
|
93538e89c6 | ||
|
|
947e845dcd | ||
|
|
6b49de7e4b | ||
|
|
f6191fa2a5 | ||
|
|
b167887d51 | ||
|
|
77769898ea | ||
|
|
42189e3cf7 | ||
|
|
73f8000e49 | ||
|
|
298e334482 | ||
|
|
bb982b8cf9 | ||
|
|
80145c37bb | ||
|
|
bc8503a21e | ||
|
|
2947dbfe5f | ||
|
|
bf9ddfc72f | ||
|
|
c848a7e70b | ||
|
|
e7c5b0e36f | ||
|
|
41282c99f9 | ||
|
|
e25f6f131a | ||
|
|
e69f0fd01e | ||
|
|
30c9534bc5 | ||
|
|
6d56fb7510 | ||
|
|
e46d03b440 | ||
|
|
8255596171 | ||
|
|
baae3ba701 | ||
|
|
6c43be2a03 | ||
|
|
ee4c8a19d6 | ||
|
|
e8cf9dd25f | ||
|
|
df21c4524c | ||
|
|
ccec6c2f67 | ||
|
|
5ab450491c | ||
|
|
3b0cce8c25 | ||
|
|
d6b6f30275 | ||
|
|
c9f18c8349 | ||
|
|
df3186ad95 | ||
|
|
58956549b8 | ||
|
|
74ed041791 | ||
|
|
312f883387 | ||
|
|
fad4093183 | ||
|
|
5c005e097e | ||
|
|
69f23e49c4 | ||
|
|
f53976dc2f | ||
|
|
1d80a77e55 | ||
|
|
fb8734c448 | ||
|
|
090fe75b7f | ||
|
|
173ca3d923 | ||
|
|
f5c875a19d | ||
|
|
6780013f50 | ||
|
|
2f21a6e35a | ||
|
|
9e9e364048 | ||
|
|
2c3296542b | ||
|
|
72988cca28 | ||
|
|
a105513750 | ||
|
|
bece03eab8 | ||
|
|
0fdb6979dd | ||
|
|
a77c1d9538 | ||
|
|
4a7e1aba15 | ||
|
|
a4263b9b54 | ||
|
|
e26ed26e05 | ||
|
|
526b11f973 | ||
|
|
627af36085 | ||
|
|
a22c84d013 | ||
|
|
3707f6294e | ||
|
|
94cb17ef63 | ||
|
|
9bb08fa194 | ||
|
|
425cd58ea6 | ||
|
|
43eb062ac5 | ||
|
|
29d878fad7 | ||
|
|
a3a95a8f59 | ||
|
|
550721e233 | ||
|
|
fa11d60672 | ||
|
|
6f8c681b26 | ||
|
|
718e445370 | ||
|
|
5e469cba0f | ||
|
|
a05479e2dc | ||
|
|
fb630e29b9 | ||
|
|
3a0b09816b | ||
|
|
8ab6b5b06e | ||
|
|
9ca1c45ae7 | ||
|
|
b9a6b1001c | ||
|
|
798adefcde | ||
|
|
72afb61079 | ||
|
|
c27f0f8fdc | ||
|
|
8d8b082ae2 | ||
|
|
5377a48074 | ||
|
|
ca8998e62b | ||
|
|
62359d9011 | ||
|
|
3022c4b378 | ||
|
|
de8b441dae | ||
|
|
5c4997d07e | ||
|
|
49b1daec38 | ||
|
|
1eba919dbd | ||
|
|
3cd5338890 | ||
|
|
2fc81ad2dd | ||
|
|
63a49896bf | ||
|
|
80755e5f15 | ||
|
|
ba25b8aeba | ||
|
|
279589fa78 | ||
|
|
1c1c99974a | ||
|
|
3be70cd90d |
No files matched your search
@@ -0,0 +1,99 @@
|
||||
Language: Cpp
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignArrayOfStructures: Left
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignConsecutiveMacros: true
|
||||
AlignEscapedNewlines: DontAlign
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLambdasOnASingleLine: Inline
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterReturnType: TopLevelDefinitions
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BraceWrapping:
|
||||
AfterClass: true
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: true
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: true
|
||||
AfterUnion: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: false
|
||||
SplitEmptyNamespace: false
|
||||
BreakBeforeBinaryOperators: All
|
||||
BreakBeforeBraces: Custom
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 120
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- forever # avoids { wrapped to next line
|
||||
- foreach
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IndentCaseLabels: false
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MacroBlockBegin: ""
|
||||
MacroBlockEnd: ""
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBlockIndentWidth: 4
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 150
|
||||
PenaltyBreakBeforeFirstCallParameter: 300
|
||||
PenaltyBreakComment: 500
|
||||
PenaltyBreakFirstLessLess: 400
|
||||
PenaltyBreakString: 600
|
||||
PenaltyExcessCharacter: 50
|
||||
PenaltyReturnTypeOnItsOwnLine: 300
|
||||
PointerAlignment: Left
|
||||
ReflowComments: true
|
||||
SortIncludes: false
|
||||
SortUsingDeclarations: false
|
||||
SpaceAfterCStyleCast: true
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: c++17
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
@@ -4,9 +4,12 @@
|
||||
"dockerfile": "../docker/DockerfileDepsLlvm",
|
||||
},
|
||||
"runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"],
|
||||
"settings": {},
|
||||
"extensions": [
|
||||
"ms-vscode.cpptools"
|
||||
],
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"ms-vscode.cpptools"
|
||||
]
|
||||
}
|
||||
},
|
||||
"forwardPorts": [4222],
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/docker"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "vcpkg"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "devcontainers"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "/rust"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
@@ -7,6 +7,7 @@ env:
|
||||
UBUNTU_FULL_DEPS: "libncurses5-dev libreadline-dev nettle-dev libasio-dev libgnutls28-dev libuv1-dev python3-pip python3-dev python3-setuptools libcppunit-dev libjsoncpp-dev autotools-dev autoconf libfmt-dev libmsgpack-dev libargon2-dev"
|
||||
UBUNTU_MESON_DEPS: "ninja-build libncurses5-dev libreadline-dev nettle-dev libasio-dev libgnutls28-dev libuv1-dev python3-dev python3-setuptools python3-pip libcppunit-dev libjsoncpp-dev libfmt-dev libmsgpack-dev libargon2-dev"
|
||||
UBUNTU_MINIMAL_DEPS: "libncurses5-dev libreadline-dev nettle-dev libfmt-dev libgnutls28-dev libcppunit-dev libmsgpack-dev libargon2-dev"
|
||||
DEBIAN12_DEPS: "git autotools-dev autoconf libtool m4 automake pkg-config wget cmake libncurses5-dev libreadline-dev nettle-dev libasio-dev libgnutls28-dev libuv1-dev python3-pip python3-dev python3-setuptools libcppunit-dev libjsoncpp-dev libfmt-dev libmsgpack-dev libargon2-dev"
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
@@ -15,7 +16,7 @@ jobs:
|
||||
name: Ubuntu/GCC Autotools build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
@@ -38,6 +39,15 @@ jobs:
|
||||
run: |
|
||||
sudo mkdir -p /usr/local/include/nonstd
|
||||
sudo wget "${EXPECTED_LITE_URL}" -O /usr/local/include/nonstd/expected.hpp
|
||||
|
||||
- name: Install simdutf
|
||||
run: |
|
||||
wget https://github.com/simdutf/simdutf/archive/refs/tags/v7.7.0.tar.gz -O simdutf.tar.gz
|
||||
tar -xzf simdutf.tar.gz
|
||||
mv simdutf-7.7.0 simdutf
|
||||
mkdir simdutf/build && cd simdutf/build
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DSIMDUTF_CXX_STANDARD=17 -DSIMDUTF_TOOLS=Off -DSIMDUTF_TESTS=Off .. && make -j2 && sudo make install
|
||||
cd ../../ && rm -rf simdutf
|
||||
|
||||
- name: Build restinio dependency
|
||||
run: |
|
||||
@@ -65,7 +75,7 @@ jobs:
|
||||
name: Ubuntu/GCC Meson build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
@@ -86,7 +96,7 @@ jobs:
|
||||
name: Ubuntu/GCC minimal build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Install Minimal Dependencies
|
||||
run: |
|
||||
@@ -107,13 +117,72 @@ jobs:
|
||||
run: cd build && make
|
||||
|
||||
- name: Run Unit tests
|
||||
run: cd build && ./opendht_unit_tests
|
||||
run: ctest --output-on-failure --test-dir build
|
||||
|
||||
build-debian12:
|
||||
name: Debian 12/GCC build
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
container:
|
||||
image: debian:12
|
||||
volumes:
|
||||
- .:/workspace
|
||||
options: --workdir /workspace
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y $DEBIAN12_DEPS
|
||||
|
||||
- name: Build llhttp dependency
|
||||
run: |
|
||||
mkdir llhttp
|
||||
wget "https://github.com/nodejs/llhttp/archive/refs/tags/release/${LLHTTP_VERSION}.tar.gz" -O llhttp.tar.gz
|
||||
tar -xzf llhttp.tar.gz -C llhttp --strip-components=1
|
||||
cd llhttp && cmake -DCMAKE_INSTALL_PREFIX=/usr . && make -j2 && make install
|
||||
cd ../ && rm -rf llhttp*
|
||||
|
||||
- name: Install expected-lite header
|
||||
run: |
|
||||
mkdir -p /usr/local/include/nonstd
|
||||
wget "${EXPECTED_LITE_URL}" -O /usr/local/include/nonstd/expected.hpp
|
||||
|
||||
- name: Build restinio dependency
|
||||
run: |
|
||||
mkdir restinio && cd restinio
|
||||
wget "https://github.com/Stiffstream/restinio/releases/download/v${RESTINIO_VERSION}/restinio-${RESTINIO_VERSION}.tar.bz2"
|
||||
tar -xjf restinio-${RESTINIO_VERSION}.tar.bz2
|
||||
cd restinio-${RESTINIO_VERSION}/dev
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr/local \
|
||||
-DRESTINIO_TEST=Off -DRESTINIO_SAMPLE=Off -DRESTINIO_BENCHMARK=Off \
|
||||
-DRESTINIO_WITH_SOBJECTIZER=Off \
|
||||
-DRESTINIO_DEP_STANDALONE_ASIO=system -DRESTINIO_DEP_LLHTTP=system \
|
||||
-DRESTINIO_DEP_FMT=system -DRESTINIO_DEP_EXPECTED_LITE=system .
|
||||
make install
|
||||
cd ../../ && rm -rf restinio*
|
||||
|
||||
- name: Configure project with CMake
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Debug \
|
||||
-DOPENDHT_C=On -DBUILD_TESTING=On \
|
||||
-DOPENDHT_PEER_DISCOVERY=On -DOPENDHT_PYTHON=Off \
|
||||
-DOPENDHT_TOOLS=On -DOPENDHT_PROXY_SERVER=On \
|
||||
-DOPENDHT_PROXY_CLIENT=On
|
||||
|
||||
- name: Build
|
||||
run: cd build && make
|
||||
|
||||
build-macos:
|
||||
name: macOS/Clang build
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Install Homebrew Dependencies
|
||||
run: brew install msgpack-cxx asio gnutls nettle readline fmt jsoncpp argon2 openssl cppunit
|
||||
@@ -146,6 +215,7 @@ jobs:
|
||||
export CPPFLAGS="-I/usr/local/opt/openssl@3/include"
|
||||
export PKG_CONFIG_PATH="/usr/local/opt/openssl@3/lib/pkgconfig"
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local \
|
||||
-DOPENDHT_DOWNLOAD_DEPS=ON \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DOPENDHT_C=On -DBUILD_TESTING=On \
|
||||
-DOPENDHT_PEER_DISCOVERY=On -DOPENDHT_PYTHON=Off \
|
||||
|
||||
@@ -6,7 +6,7 @@ jobs:
|
||||
name: Clang Static Analysis
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: deps
|
||||
run: |
|
||||
sudo apt update && \
|
||||
@@ -37,6 +37,6 @@ jobs:
|
||||
- name: cmake
|
||||
run: |
|
||||
mkdir build && cd build && \
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug -DOPENDHT_C=On -DOPENDHT_PEER_DISCOVERY=On -DOPENDHT_PYTHON=Off -DOPENDHT_TOOLS=On -DOPENDHT_PROXY_SERVER=On -DOPENDHT_PROXY_CLIENT=On -DOPENDHT_PUSH_NOTIFICATIONS=On
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug -DOPENDHT_C=On -DOPENDHT_DOWNLOAD_DEPS=ON -DOPENDHT_PEER_DISCOVERY=On -DOPENDHT_PYTHON=Off -DOPENDHT_TOOLS=On -DOPENDHT_PROXY_SERVER=On -DOPENDHT_PROXY_CLIENT=On -DOPENDHT_PUSH_NOTIFICATIONS=On
|
||||
- name: scan-build
|
||||
run: cd build && scan-build --status-bugs make
|
||||
@@ -22,18 +22,20 @@ jobs:
|
||||
include:
|
||||
- language: c-cpp
|
||||
build-mode: autobuild
|
||||
- language: actions
|
||||
build-mode: none
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@v4
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
@@ -0,0 +1,108 @@
|
||||
name: Python Package
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build_wheels:
|
||||
name: Build wheels on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
# Run locally with: pipx run cibuildwheel --platform linux python
|
||||
- name: Build wheels
|
||||
uses: pypa/cibuildwheel@v3.3.0
|
||||
with:
|
||||
package-dir: python
|
||||
|
||||
- uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: opendht-wheels-${{ matrix.os }}
|
||||
path: ./wheelhouse/*.whl
|
||||
|
||||
build_wheels_windows:
|
||||
name: Build wheels on Windows
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
VCPKG_VERSION: "2025.07.25"
|
||||
VCPKG_INSTALLATION_ROOT: "C:\\vcpkg"
|
||||
VCPKG_DISABLE_METRICS: "1"
|
||||
VCPKG_DEFAULT_TRIPLET: x64-windows-release
|
||||
VCPKG_INSTALLED_DIR: ${{ github.workspace }}\vcpkg_installed
|
||||
VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}\vcpkg_binary_cache
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Cache vcpkg installed packages
|
||||
id: cache-vcpkg
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
${{ env.VCPKG_INSTALLED_DIR }}
|
||||
${{ env.VCPKG_DEFAULT_BINARY_CACHE }}
|
||||
key: ${{ runner.os }}-vcpkg-${{ env.VCPKG_VERSION }}-${{ env.VCPKG_DEFAULT_TRIPLET }}-${{ hashFiles('**/vcpkg.json', '**/vcpkg-configuration.json') }}-${{ hashFiles('.github/workflows/windows-build.yml') }}
|
||||
|
||||
- name: Setup vcpkg and install dependencies
|
||||
if: steps.cache-vcpkg.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
New-Item -Path "${{ env.VCPKG_INSTALLED_DIR }}" -ItemType Directory -Force
|
||||
New-Item -Path "${{ env.VCPKG_DEFAULT_BINARY_CACHE }}" -ItemType Directory -Force
|
||||
vcpkg install `
|
||||
--recurse `
|
||||
--clean-after-build `
|
||||
--triplet=${{ env.VCPKG_DEFAULT_TRIPLET }} `
|
||||
--x-install-root=${{ env.VCPKG_INSTALLED_DIR }}
|
||||
shell: pwsh
|
||||
|
||||
- name: Build wheels
|
||||
run: |
|
||||
pip install cython delvewheel build
|
||||
cmake -S . -B build `
|
||||
-DCMAKE_BUILD_TYPE=Release `
|
||||
-DVCPKG_TARGET_TRIPLET=${{ env.VCPKG_DEFAULT_TRIPLET }} `
|
||||
-DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_INSTALLATION_ROOT }}\scripts\buildsystems\vcpkg.cmake `
|
||||
-DVCPKG_INSTALLED_DIR=${{ env.VCPKG_INSTALLED_DIR }} `
|
||||
-DBUILD_TESTING=OFF `
|
||||
-DOPENDHT_C=OFF `
|
||||
-DOPENDHT_TOOLS=OFF `
|
||||
-DOPENDHT_PYTHON=ON `
|
||||
-DOPENDHT_DOCUMENTATION=OFF `
|
||||
-DOPENDHT_PROXY_CLIENT=ON `
|
||||
-DOPENDHT_PROXY_SERVER=ON
|
||||
cd build
|
||||
pipx run cibuildwheel python
|
||||
shell: pwsh
|
||||
|
||||
- uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: opendht-wheels-win_amd64
|
||||
path: ./build/wheelhouse/*.whl
|
||||
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_wheels, build_wheels_windows]
|
||||
steps:
|
||||
- uses: actions/download-artifact@v6
|
||||
with:
|
||||
pattern: opendht-wheels-*
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
|
||||
- name: Publish package
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_DEPS }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: docker/DockerfileDeps
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: docker/Dockerfile
|
||||
@@ -92,7 +92,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
@@ -108,7 +108,7 @@ jobs:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_DEPS_LLVM }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: docker/DockerfileDepsLlvm
|
||||
@@ -125,7 +125,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
@@ -142,7 +142,7 @@ jobs:
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LLVM }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: docker/DockerfileLlvm
|
||||
@@ -159,7 +159,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
@@ -176,7 +176,7 @@ jobs:
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME_DHTNODE }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: docker/DockerfileDhtnode
|
||||
@@ -193,7 +193,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
@@ -210,7 +210,7 @@ jobs:
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME_ALPINE_DEPS }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: docker/DockerfileDepsAlpine
|
||||
@@ -227,7 +227,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
@@ -244,7 +244,7 @@ jobs:
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME_ALPINE }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: docker/DockerfileAlpine
|
||||
@@ -256,20 +256,50 @@ jobs:
|
||||
name: Build Windows MSI Installer
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
VCPKG_VERSION: "2025.04.09"
|
||||
VCPKG_VERSION: "2025.07.25"
|
||||
VCPKG_INSTALLATION_ROOT: "C:\\vcpkg"
|
||||
VCPKG_DISABLE_METRICS: "1"
|
||||
VCPKG_DEFAULT_TRIPLET: x64-windows
|
||||
VCPKG_ROOT: "C:\\vcpkg"
|
||||
VCPKG_INSTALLED_DIR: ${{ github.workspace }}\vcpkg_installed
|
||||
VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}\vcpkg_binary_cache
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up MSVC developer environment
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
|
||||
- name: Install WIX using .net tools
|
||||
run: |
|
||||
dotnet tool install --global wix --version 6.0.1
|
||||
wix extension add --global WixToolset.UI.wixext/6.0.1
|
||||
shell: pwsh
|
||||
|
||||
- name: Cache vcpkg installed packages
|
||||
id: cache-vcpkg
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
${{ env.VCPKG_INSTALLED_DIR }}
|
||||
${{ env.VCPKG_DEFAULT_BINARY_CACHE }}
|
||||
key: ${{ runner.os }}-vcpkg-${{ env.VCPKG_VERSION }}-x64-shared-${{ hashFiles('**/vcpkg.json', '**/vcpkg-configuration.json') }}-${{ hashFiles('.github/workflows/windows-build.yml') }}
|
||||
|
||||
- name: Setup vcpkg and install dependencies
|
||||
if: steps.cache-vcpkg.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
New-Item -Path "${{ env.VCPKG_INSTALLED_DIR }}" -ItemType Directory -Force
|
||||
New-Item -Path "${{ env.VCPKG_DEFAULT_BINARY_CACHE }}" -ItemType Directory -Force
|
||||
vcpkg install `
|
||||
--recurse `
|
||||
--clean-after-build `
|
||||
--triplet=x64-windows `
|
||||
--x-install-root=${{ env.VCPKG_INSTALLED_DIR }} `
|
||||
--x-feature=test
|
||||
shell: pwsh
|
||||
|
||||
- name: Configure CMake for MSI
|
||||
shell: pwsh
|
||||
@@ -277,22 +307,30 @@ jobs:
|
||||
mkdir build -ErrorAction SilentlyContinue
|
||||
cmake -B build `
|
||||
-S . `
|
||||
-DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_ROOT }}/scripts/buildsystems/vcpkg.cmake `
|
||||
-DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_INSTALLATION_ROOT }}/scripts/buildsystems/vcpkg.cmake `
|
||||
-DVCPKG_TARGET_TRIPLET=x64-windows `
|
||||
-DVCPKG_INSTALLED_DIR=${{ env.VCPKG_INSTALLED_DIR }} `
|
||||
-DVCPKG_MANIFEST_FEATURES=test `
|
||||
-DCMAKE_BUILD_TYPE=Release `
|
||||
-DOPENDHT_C=On `
|
||||
-DOPENDHT_TOOLS=On `
|
||||
-DOPENDHT_PYTHON=Off `
|
||||
-DOPENDHT_DOCUMENTATION=OFF `
|
||||
-DOPENDHT_PEER_DISCOVERY=On `
|
||||
-DOPENDHT_PROXY_SERVER=On `
|
||||
-DOPENDHT_PROXY_CLIENT=On `
|
||||
-DOPENDHT_HTTP=On `
|
||||
-DBUILD_TESTING=Off `
|
||||
-DOPENDHT_CPACK=On `
|
||||
-A x64
|
||||
|
||||
- name: Build with MSVC
|
||||
shell: pwsh
|
||||
run: |
|
||||
cmake --build build --config Release --parallel $([System.Environment]::ProcessorCount)
|
||||
# Copy vcpkg installed libraries to the build directory
|
||||
Copy-Item -Path "${{ env.VCPKG_INSTALLED_DIR }}\x64-windows\lib\*.lib" -Destination "build" -Force
|
||||
Copy-Item -Path "${{ env.VCPKG_INSTALLED_DIR }}\x64-windows\bin\*.dll" -Destination "build" -Force
|
||||
|
||||
- name: Package MSI with CPack
|
||||
shell: pwsh
|
||||
@@ -312,66 +350,115 @@ jobs:
|
||||
}
|
||||
|
||||
- name: Upload MSI Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: opendht-windows-msi
|
||||
path: build/${{ steps.cpack_msi.outputs.msi_path }} # Path to the MSI in the build directory
|
||||
retention-days: 7
|
||||
|
||||
build-python-wheels:
|
||||
name: Build Python Wheels
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/savoirfairelinux/opendht/opendht-deps:latest
|
||||
build-windows-dhtnode:
|
||||
name: Build Windows dhtnode
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
VCPKG_VERSION: "2025.07.25"
|
||||
VCPKG_INSTALLATION_ROOT: "C:\\vcpkg"
|
||||
VCPKG_DISABLE_METRICS: "1"
|
||||
VCPKG_DEFAULT_TRIPLET: x64-windows-static-release
|
||||
VCPKG_INSTALLED_DIR: ${{ github.workspace }}\vcpkg_installed
|
||||
VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}\vcpkg_binary_cache
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: cmake (for Python wheel)
|
||||
run: |
|
||||
mkdir build_python && cd build_python && \
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DOPENDHT_STATIC=Off \
|
||||
-DOPENDHT_PYTHON=On \
|
||||
-DOPENDHT_C=Off \
|
||||
-DOPENDHT_PEER_DISCOVERY=On \
|
||||
-DOPENDHT_TOOLS=Off \
|
||||
-DOPENDHT_PROXY_SERVER=On \
|
||||
-DOPENDHT_PROXY_CLIENT=On
|
||||
|
||||
- name: build (for Python wheel)
|
||||
run: cd build_python && make dist
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
name: opendht-wheels-linux
|
||||
path: build_python/python/dist/*.whl
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up MSVC developer environment
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
|
||||
- name: Cache vcpkg static installed packages
|
||||
id: cache-vcpkg-static
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
${{ env.VCPKG_INSTALLED_DIR }}
|
||||
${{ env.VCPKG_DEFAULT_BINARY_CACHE }}
|
||||
key: ${{ runner.os }}-vcpkg-${{ env.VCPKG_VERSION }}-x64-static-${{ hashFiles('**/vcpkg.json', '**/vcpkg-configuration.json') }}-${{ hashFiles('.github/workflows/windows-build.yml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-vcpkg-${{ env.VCPKG_VERSION }}-x64-static-${{ hashFiles('**/vcpkg.json', '**/vcpkg-configuration.json') }}
|
||||
${{ runner.os }}-vcpkg-${{ env.VCPKG_VERSION }}-x64-static-
|
||||
|
||||
- name: Setup vcpkg and install static dependencies
|
||||
if: steps.cache-vcpkg-static.outputs.cache-hit != 'true'
|
||||
shell: pwsh
|
||||
run: |
|
||||
New-Item -Path "${{ env.VCPKG_INSTALLED_DIR }}" -ItemType Directory -Force
|
||||
New-Item -Path "${{ env.VCPKG_DEFAULT_BINARY_CACHE }}" -ItemType Directory -Force
|
||||
vcpkg install `
|
||||
--recurse `
|
||||
--clean-after-build `
|
||||
--triplet=x64-windows-static-release `
|
||||
--x-install-root=${{ env.VCPKG_INSTALLED_DIR }} `
|
||||
--x-feature=test
|
||||
|
||||
- name: Configure CMake for Static Build
|
||||
shell: pwsh
|
||||
run: |
|
||||
mkdir build_static -ErrorAction SilentlyContinue
|
||||
cmake -B build_static `
|
||||
-S . `
|
||||
-DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_INSTALLATION_ROOT }}/scripts/buildsystems/vcpkg.cmake `
|
||||
-DVCPKG_TARGET_TRIPLET=x64-windows-static-release `
|
||||
-DVCPKG_INSTALLED_DIR=${{ env.VCPKG_INSTALLED_DIR }} `
|
||||
-DVCPKG_MANIFEST_FEATURES=test `
|
||||
-DCMAKE_BUILD_TYPE=Release `
|
||||
-DBUILD_SHARED_LIBS=OFF `
|
||||
-DOPENDHT_PROXY_CLIENT=ON `
|
||||
-DOPENDHT_PROXY_SERVER=ON `
|
||||
-DOPENDHT_C=ON `
|
||||
-DOPENDHT_TOOLS=ON `
|
||||
-DOPENDHT_DOCUMENTATION=OFF `
|
||||
-DOPENDHT_PYTHON=OFF `
|
||||
-DOPENDHT_PEER_DISCOVERY=ON `
|
||||
-DOPENDHT_HTTP=ON `
|
||||
-A x64
|
||||
|
||||
- name: Build Static dhtnode
|
||||
shell: pwsh
|
||||
run: |
|
||||
cmake --build build_static --config Release --parallel $([System.Environment]::ProcessorCount)
|
||||
|
||||
- name: Upload Static dhtnode Artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: opendht-windows-dhtnode
|
||||
path: |
|
||||
build_static/**/*dhtnode.exe
|
||||
retention-days: 7
|
||||
|
||||
create-github-release:
|
||||
name: Create GitHub Release
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-windows-msi, build-python-wheels]
|
||||
needs: [build-windows-msi, build-windows-dhtnode]
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Download Windows MSI artifact
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
name: opendht-windows-msi
|
||||
path: downloaded_artifacts/windows_msi
|
||||
|
||||
- name: Download Linux Wheels artifact
|
||||
uses: actions/download-artifact@v4
|
||||
- name: Download Static dhtnode artifact
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
name: opendht-wheels-linux
|
||||
path: downloaded_artifacts/linux_wheels
|
||||
name: opendht-windows-dhtnode
|
||||
path: downloaded_artifacts/static_dhtnode
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
@@ -379,5 +466,5 @@ jobs:
|
||||
with:
|
||||
draft: true
|
||||
files: |
|
||||
downloaded_artifacts/linux_wheels/*.whl
|
||||
downloaded_artifacts/windows_msi/*.msi
|
||||
downloaded_artifacts/static_dhtnode/**/*dhtnode.exe
|
||||
@@ -1,4 +1,6 @@
|
||||
name: Windows Build
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -7,10 +9,11 @@ on:
|
||||
branches: [ main, master ]
|
||||
|
||||
env:
|
||||
VCPKG_VERSION: "2025.04.09"
|
||||
VCPKG_VERSION: "2025.07.25"
|
||||
VCPKG_INSTALLATION_ROOT: "C:\\vcpkg"
|
||||
VCPKG_DISABLE_METRICS: "1"
|
||||
VCPKG_INSTALLED_DIR: ${{ github.workspace }}\vcpkg_installed
|
||||
VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}\vcpkg_binary_cache
|
||||
|
||||
jobs:
|
||||
prepare-dependencies:
|
||||
@@ -24,7 +27,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up MSVC
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
@@ -33,22 +36,26 @@ jobs:
|
||||
id: generate_cache_key
|
||||
shell: pwsh
|
||||
run: |
|
||||
$key_value = "${{ runner.os }}-vcpkg-${{ env.VCPKG_VERSION }}-${{ matrix.arch }}-${{ hashFiles('**/vcpkg.json', '**/vcpkg-configuration.json') }}-${{ hashFiles('.github/workflow/windows-build.yml') }}"
|
||||
$key_value = "${{ runner.os }}-vcpkg-${{ env.VCPKG_VERSION }}-${{ matrix.arch }}-shared-${{ hashFiles('**/vcpkg.json', '**/vcpkg-configuration.json') }}-${{ hashFiles('.github/workflows/windows-build.yml') }}"
|
||||
echo "key=$key_value" >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: Cache vcpkg installed packages
|
||||
id: cache-vcpkg
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.VCPKG_INSTALLED_DIR }}
|
||||
path: |
|
||||
${{ env.VCPKG_INSTALLED_DIR }}
|
||||
${{ env.VCPKG_DEFAULT_BINARY_CACHE }}
|
||||
key: ${{ steps.generate_cache_key.outputs.key }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-vcpkg-${{ env.VCPKG_VERSION }}-${{ matrix.arch }}-${{ hashFiles('**/vcpkg.json', '**/vcpkg-configuration.json') }}
|
||||
${{ runner.os }}-vcpkg-${{ env.VCPKG_VERSION }}-${{ matrix.arch }}-
|
||||
${{ runner.os }}-vcpkg-${{ env.VCPKG_VERSION }}-${{ matrix.arch }}-shared-${{ hashFiles('**/vcpkg.json', '**/vcpkg-configuration.json') }}
|
||||
${{ runner.os }}-vcpkg-${{ env.VCPKG_VERSION }}-${{ matrix.arch }}-shared-
|
||||
|
||||
- name: Setup vcpkg and install dependencies
|
||||
if: steps.cache-vcpkg.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
New-Item -Path "${{ env.VCPKG_INSTALLED_DIR }}" -ItemType Directory -Force
|
||||
New-Item -Path "${{ env.VCPKG_DEFAULT_BINARY_CACHE }}" -ItemType Directory -Force
|
||||
vcpkg install `
|
||||
--recurse `
|
||||
--clean-after-build `
|
||||
@@ -57,6 +64,54 @@ jobs:
|
||||
--x-feature=test
|
||||
shell: pwsh
|
||||
|
||||
prepare-static-dependencies:
|
||||
name: Prepare Windows Static Dependencies
|
||||
runs-on: windows-latest
|
||||
outputs:
|
||||
vcpkg_static_cache_key: ${{ steps.generate_static_cache_key.outputs.key }}
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x64]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up MSVC
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
|
||||
- name: Generate Cache Key for Static Libs
|
||||
id: generate_static_cache_key
|
||||
shell: pwsh
|
||||
run: |
|
||||
$key_value = "${{ runner.os }}-vcpkg-${{ env.VCPKG_VERSION }}-${{ matrix.arch }}-static-${{ hashFiles('**/vcpkg.json', '**/vcpkg-configuration.json') }}-${{ hashFiles('.github/workflows/windows-build.yml') }}"
|
||||
echo "key=$key_value" >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: Cache vcpkg static installed packages
|
||||
id: cache-vcpkg-static
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
${{ env.VCPKG_INSTALLED_DIR }}
|
||||
${{ env.VCPKG_DEFAULT_BINARY_CACHE }}
|
||||
key: ${{ steps.generate_static_cache_key.outputs.key }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-vcpkg-${{ env.VCPKG_VERSION }}-${{ matrix.arch }}-static-${{ hashFiles('**/vcpkg.json', '**/vcpkg-configuration.json') }}
|
||||
${{ runner.os }}-vcpkg-${{ env.VCPKG_VERSION }}-${{ matrix.arch }}-static-
|
||||
|
||||
- name: Setup vcpkg and install static dependencies
|
||||
if: steps.cache-vcpkg-static.outputs.cache-hit != 'true'
|
||||
shell: pwsh
|
||||
run: |
|
||||
New-Item -Path "${{ env.VCPKG_INSTALLED_DIR }}" -ItemType Directory -Force
|
||||
New-Item -Path "${{ env.VCPKG_DEFAULT_BINARY_CACHE }}" -ItemType Directory -Force
|
||||
vcpkg install `
|
||||
--recurse `
|
||||
--clean-after-build `
|
||||
--triplet=${{ matrix.arch }}-windows-static-release `
|
||||
--x-install-root=${{ env.VCPKG_INSTALLED_DIR }} `
|
||||
--x-feature=test
|
||||
|
||||
build-windows:
|
||||
name: Windows MSVC Build
|
||||
runs-on: windows-latest
|
||||
@@ -69,7 +124,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up MSVC
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
@@ -78,11 +133,13 @@ jobs:
|
||||
id: cache-vcpkg-restore
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: ${{ env.VCPKG_INSTALLED_DIR }}
|
||||
path: |
|
||||
${{ env.VCPKG_INSTALLED_DIR }}
|
||||
${{ env.VCPKG_DEFAULT_BINARY_CACHE }}
|
||||
key: ${{ needs.prepare-dependencies.outputs.vcpkg_cache_key }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-vcpkg-${{ env.VCPKG_VERSION }}-${{ matrix.arch }}-${{ hashFiles('**/vcpkg.json', '**/vcpkg-configuration.json') }}
|
||||
${{ runner.os }}-vcpkg-${{ env.VCPKG_VERSION }}-${{ matrix.arch }}-
|
||||
${{ runner.os }}-vcpkg-${{ env.VCPKG_VERSION }}-${{ matrix.arch }}-shared-${{ hashFiles('**/vcpkg.json', '**/vcpkg-configuration.json') }}
|
||||
${{ runner.os }}-vcpkg-${{ env.VCPKG_VERSION }}-${{ matrix.arch }}-shared-
|
||||
|
||||
- name: Configure CMake
|
||||
shell: pwsh
|
||||
@@ -101,6 +158,7 @@ jobs:
|
||||
-DOPENDHT_C=ON `
|
||||
-DOPENDHT_TOOLS=ON `
|
||||
-DOPENDHT_PYTHON=OFF `
|
||||
-DOPENDHT_DOCUMENTATION=OFF `
|
||||
-DOPENDHT_PEER_DISCOVERY=ON `
|
||||
-DOPENDHT_HTTP=ON `
|
||||
-A ${{ matrix.arch }}
|
||||
@@ -120,7 +178,7 @@ jobs:
|
||||
|
||||
- name: Upload build artifacts
|
||||
if: matrix.build_type == 'Release'
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: opendht-windows-${{ matrix.arch }}-${{ matrix.build_type }}
|
||||
path: |
|
||||
@@ -129,6 +187,77 @@ jobs:
|
||||
build/**/*.lib
|
||||
retention-days: 7
|
||||
|
||||
build-windows-static:
|
||||
name: Windows Static MSVC Build
|
||||
runs-on: windows-latest
|
||||
needs: prepare-static-dependencies
|
||||
strategy:
|
||||
matrix:
|
||||
build_type: [Release]
|
||||
arch: [x64]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up MSVC
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
|
||||
- name: Restore vcpkg static installed packages
|
||||
id: cache-vcpkg-static-restore
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: |
|
||||
${{ env.VCPKG_INSTALLED_DIR }}
|
||||
${{ env.VCPKG_DEFAULT_BINARY_CACHE }}
|
||||
key: ${{ needs.prepare-static-dependencies.outputs.vcpkg_static_cache_key }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-vcpkg-${{ env.VCPKG_VERSION }}-${{ matrix.arch }}-static-${{ hashFiles('**/vcpkg.json', '**/vcpkg-configuration.json') }}
|
||||
${{ runner.os }}-vcpkg-${{ env.VCPKG_VERSION }}-${{ matrix.arch }}-static-
|
||||
|
||||
- name: Configure CMake for Static Build
|
||||
shell: pwsh
|
||||
run: |
|
||||
mkdir build -ErrorAction SilentlyContinue
|
||||
cmake -B build `
|
||||
-S . `
|
||||
-DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_INSTALLATION_ROOT }}/scripts/buildsystems/vcpkg.cmake `
|
||||
-DVCPKG_TARGET_TRIPLET=${{ matrix.arch }}-windows-static-release `
|
||||
-DVCPKG_INSTALLED_DIR=${{ env.VCPKG_INSTALLED_DIR }} `
|
||||
-DVCPKG_MANIFEST_FEATURES=test `
|
||||
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} `
|
||||
-DBUILD_SHARED_LIBS=OFF `
|
||||
-DBUILD_TESTING=ON `
|
||||
-DOPENDHT_PROXY_CLIENT=ON `
|
||||
-DOPENDHT_PROXY_SERVER=ON `
|
||||
-DOPENDHT_C=ON `
|
||||
-DOPENDHT_TOOLS=ON `
|
||||
-DOPENDHT_PYTHON=OFF `
|
||||
-DOPENDHT_DOCUMENTATION=OFF `
|
||||
-DOPENDHT_PEER_DISCOVERY=ON `
|
||||
-DOPENDHT_HTTP=ON `
|
||||
-A ${{ matrix.arch }}
|
||||
|
||||
- name: Build Static
|
||||
shell: pwsh
|
||||
run: |
|
||||
cmake --build build --config ${{ matrix.build_type }} --parallel 4
|
||||
|
||||
- name: Test Static Build
|
||||
shell: pwsh
|
||||
run: |
|
||||
cd build
|
||||
ctest --build-config ${{ matrix.build_type }} --output-on-failure --parallel 4
|
||||
|
||||
- name: Upload static build artifacts
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: opendht-windows-static-${{ matrix.arch }}-${{ matrix.build_type }}
|
||||
path: |
|
||||
build/**/*.exe
|
||||
build/**/*.lib
|
||||
retention-days: 7
|
||||
|
||||
build-windows-mingw:
|
||||
name: Windows MinGW Build
|
||||
runs-on: windows-latest
|
||||
@@ -140,7 +269,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup MinGW
|
||||
uses: msys2/setup-msys2@v2
|
||||
@@ -227,6 +356,7 @@ jobs:
|
||||
-DOPENDHT_C=ON \
|
||||
-DOPENDHT_TOOLS=ON \
|
||||
-DOPENDHT_PYTHON=OFF \
|
||||
-DOPENDHT_DOCUMENTATION=OFF \
|
||||
-DOPENDHT_PEER_DISCOVERY=ON \
|
||||
-DOPENDHT_HTTP=ON
|
||||
cmake --build . --parallel 4
|
||||
@@ -239,7 +369,7 @@ jobs:
|
||||
|
||||
- name: Upload MinGW artifacts
|
||||
if: matrix.build_type == 'Release'
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: opendht-windows-mingw-${{ matrix.build_type }}
|
||||
path: |
|
||||
|
||||
+8
-3
@@ -45,9 +45,11 @@ Makefile.in
|
||||
*~
|
||||
*.pc
|
||||
|
||||
# KDevelop
|
||||
.kdev4/
|
||||
*.kdev4
|
||||
# CMake
|
||||
CMakeCache.txt
|
||||
CMakeFiles/
|
||||
cmake_install.cmake
|
||||
*ConfigVersion.cmake
|
||||
|
||||
# VSCode
|
||||
.vscode/*
|
||||
@@ -67,6 +69,9 @@ language.settings.xml
|
||||
*.stamp
|
||||
python/opendht.cpp
|
||||
python/setup.py
|
||||
python/dist/
|
||||
wheelhouse/
|
||||
*.egg-info/
|
||||
|
||||
# Doxygen
|
||||
doc/Doxyfile
|
||||
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"C_Cpp.default.compileCommands": "${workspaceFolder}/build/compile_commands.json",
|
||||
"C_Cpp.default.cppStandard": "c++17",
|
||||
"C_Cpp.default.cStandard": "c11",
|
||||
"C_Cpp.autoAddFileAssociations": false,
|
||||
"cmake.configureOnOpen": false,
|
||||
"cmake.configureOnEdit": false,
|
||||
"editor.formatOnSave": true,
|
||||
"files.eol": "\n",
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
# Build the library
|
||||
|
||||
The build is currently tested on GNU/Linux, macOS, and Windows.
|
||||
|
||||
## Unix-like systems (Linux, macOS, MSYS2/MinGW, etc.)
|
||||
|
||||
## Install dependencies
|
||||
|
||||
### Debian 13
|
||||
|
||||
```sh
|
||||
# Install dependencies
|
||||
sudo apt install pkg-config libncurses5-dev libreadline-dev nettle-dev libgnutls28-dev libargon2-dev libmsgpack-dev libssl-dev libfmt-dev libjsoncpp-dev libhttp-parser-dev libasio-dev libmsgpack-cxx-dev
|
||||
|
||||
# Install Python binding dependencies
|
||||
sudo apt-get install cython3 python3-dev python3-setuptools python3-build
|
||||
```
|
||||
|
||||
Optionally, install RESTinio to enable the OpenDHT proxy client and server:
|
||||
```sh
|
||||
mkdir /usr/include/nonstd
|
||||
wget https://raw.githubusercontent.com/martinmoene/expected-lite/master/include/nonstd/expected.hpp -O /usr/include/nonstd/expected.hpp
|
||||
|
||||
wget https://github.com/Stiffstream/restinio/releases/download/v.0.7.3/restinio-0.7.3.tar.bz2
|
||||
tar -xjf restinio-0.7.3.tar.bz2
|
||||
cd restinio-0.7.3/dev
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr -DRESTINIO_TEST=Off -DRESTINIO_SAMPLE=Off -DRESTINIO_BENCHMARK=Off -DRESTINIO_WITH_SOBJECTIZER=Off -DRESTINIO_DEP_STANDALONE_ASIO=system -DRESTINIO_DEP_LLHTTP=system -DRESTINIO_DEP_FMT=system -DRESTINIO_DEP_EXPECTED_LITE=system .
|
||||
make -j2 && make install
|
||||
```
|
||||
|
||||
To run openDHT on a different computer from the build computer, the following runtime dependencies are required:
|
||||
```sh
|
||||
sudo apt install libargon2-1 libjsoncpp26
|
||||
```
|
||||
|
||||
### Ubuntu 24.04 and Debian 12
|
||||
|
||||
```sh
|
||||
# Install dependencies
|
||||
sudo apt install pkg-config libncurses5-dev libreadline-dev nettle-dev libgnutls28-dev libargon2-0-dev libmsgpack-dev libssl-dev libfmt-dev libjsoncpp-dev libhttp-parser-dev libasio-dev
|
||||
|
||||
# Install Python binding dependencies
|
||||
sudo apt-get install cython3 python3-dev python3-setuptools python3-build
|
||||
```
|
||||
|
||||
Optionally, install RESTinio to enable the OpenDHT proxy client and server:
|
||||
```sh
|
||||
mkdir /usr/include/nonstd
|
||||
wget https://raw.githubusercontent.com/martinmoene/expected-lite/master/include/nonstd/expected.hpp -O /usr/include/nonstd/expected.hpp
|
||||
|
||||
wget https://github.com/Stiffstream/restinio/releases/download/v.0.7.3/restinio-0.7.3.tar.bz2
|
||||
tar -xjf restinio-0.7.3.tar.bz2
|
||||
cd restinio-0.7.3/dev
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr -DRESTINIO_TEST=Off -DRESTINIO_SAMPLE=Off -DRESTINIO_BENCHMARK=Off -DRESTINIO_WITH_SOBJECTIZER=Off -DRESTINIO_DEP_STANDALONE_ASIO=system -DRESTINIO_DEP_LLHTTP=system -DRESTINIO_DEP_FMT=system -DRESTINIO_DEP_EXPECTED_LITE=system .
|
||||
make -j2 && make install
|
||||
```
|
||||
|
||||
### Fedora
|
||||
|
||||
```sh
|
||||
# Install GnuTLS, Readline and msgpack-c
|
||||
sudo dnf install readline-devel gnutls-devel msgpack-devel asio-devel libargon2-devel fmt-devel
|
||||
# Install python binding dependencies
|
||||
sudo dnf install python3-Cython python3-devel redhat-rpm-config
|
||||
```
|
||||
|
||||
### macOS
|
||||
|
||||
```sh
|
||||
brew install gnutls msgpack-cxx argon2 asio readline jsoncpp fmt
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
Using CMake:
|
||||
|
||||
```sh
|
||||
# Clone the repository
|
||||
git clone https://github.com/savoirfairelinux/opendht.git
|
||||
|
||||
# Build and install
|
||||
cd opendht
|
||||
mkdir build && cd build
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
|
||||
make -j$(nproc)
|
||||
sudo make install
|
||||
```
|
||||
|
||||
#### Example Configurations
|
||||
|
||||
Minimal build (libraries only):
|
||||
```sh
|
||||
cmake -DOPENDHT_TOOLS=OFF ..
|
||||
```
|
||||
|
||||
Full-featured build with proxy support, Python, and C bindings:
|
||||
```sh
|
||||
cmake -DOPENDHT_PYTHON=ON \
|
||||
-DOPENDHT_C=ON \
|
||||
-DOPENDHT_PROXY_SERVER=ON \
|
||||
-DOPENDHT_PROXY_SERVER_IDENTITY=ON \
|
||||
-DOPENDHT_PROXY_CLIENT=ON \
|
||||
-DOPENDHT_PUSH_NOTIFICATIONS=ON \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr ..
|
||||
```
|
||||
|
||||
## Windows/MSVC
|
||||
|
||||
Building on Windows with MSVC is supported with VCPKG and CMake.
|
||||
|
||||
### Build with VCPKG in manifest mode
|
||||
|
||||
1. Install [VCPKG](https://github.com/microsoft/vcpkg) and required dependencies:
|
||||
```ps1
|
||||
git clone https://github.com/microsoft/vcpkg.git
|
||||
cd vcpkg; .\bootstrap-vcpkg.bat
|
||||
```
|
||||
|
||||
2. Use CMake preset for vcpkg:
|
||||
```ps1
|
||||
cd opendht
|
||||
cmake --preset=vcpkg_manifest .
|
||||
cmake --build .
|
||||
```
|
||||
+528
-312
File diff suppressed because it is too large.
Load diff
+104
-19
@@ -12,7 +12,34 @@
|
||||
"generator": "Unix Makefiles",
|
||||
"inherits": "default",
|
||||
"cacheVariables": {
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "unix-make-full",
|
||||
"description": "Unix Makefiles (Full build)",
|
||||
"inherits": "unix-make",
|
||||
"cacheVariables": {
|
||||
"OPENDHT_C": true,
|
||||
"OPENDHT_PROXY_CLIENT": true,
|
||||
"OPENDHT_PROXY_SERVER": true,
|
||||
"OPENDHT_PUSH_NOTIFICATIONS": true,
|
||||
"OPENDHT_PYTHON": true,
|
||||
"OPENDHT_INDEX": true,
|
||||
"BUILD_TESTING": true,
|
||||
"OPENDHT_EXAMPLES": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "unix-make-release",
|
||||
"description": "Unix Makefiles (Release build)",
|
||||
"inherits": "unix-make-full",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"BUILD_SHARED_LIBS": true,
|
||||
"BUILD_TESTING": false,
|
||||
"OPENDHT_SANITIZE": false,
|
||||
"OPENDHT_EXAMPLES": false
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -20,14 +47,27 @@
|
||||
"generator": "Ninja",
|
||||
"inherits": "default",
|
||||
"cacheVariables": {
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ninja-release",
|
||||
"inherits": "ninja",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"BUILD_SHARED_LIBS": true,
|
||||
"BUILD_TESTING": false,
|
||||
"OPENDHT_SANITIZE": false,
|
||||
"OPENDHT_CPACK": true,
|
||||
"OPENDHT_C": true,
|
||||
"OPENDHT_PROXY_CLIENT": true,
|
||||
"OPENDHT_PROXY_SERVER": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "msvc",
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"inherits": "default",
|
||||
"binaryDir": "${sourceDir}/build-vs",
|
||||
"cacheVariables": {
|
||||
"BUILD_TESTING": true,
|
||||
"VCPKG_MANIFEST_FEATURES": "test",
|
||||
@@ -41,17 +81,25 @@
|
||||
"inherits": "msvc",
|
||||
"cacheVariables": {
|
||||
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
|
||||
"BUILD_SHARED_LIBS": "On",
|
||||
"BUILD_SHARED_LIBS": true,
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"VCPKG_MANIFEST_MODE": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "vcpkg_manifest_static",
|
||||
"inherits": "vcpkg_manifest",
|
||||
"cacheVariables": {
|
||||
"BUILD_SHARED_LIBS": false,
|
||||
"VCPKG_TARGET_TRIPLET": "x64-windows-static"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "vcpkg_local",
|
||||
"inherits": "msvc",
|
||||
"cacheVariables": {
|
||||
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
|
||||
"BUILD_SHARED_LIBS": "On",
|
||||
"BUILD_SHARED_LIBS": true,
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"BUILD_TESTING": true,
|
||||
"VCPKG_MANIFEST_MODE": false
|
||||
@@ -71,25 +119,23 @@
|
||||
},
|
||||
{
|
||||
"name": "vcpkg_release_full",
|
||||
"inherits": "vcpkg_manifest",
|
||||
"inherits": "vcpkg_release",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"VCPKG_TARGET_TRIPLET": "x64-windows-release",
|
||||
"VCPKG_OVERLAY_TRIPLETS": "x64-windows-release;x64-windows-rel;x64-windows",
|
||||
"BUILD_TESTING": false,
|
||||
"OPENDHT_PROXY_CLIENT": true,
|
||||
"OPENDHT_PROXY_SERVER": true,
|
||||
"OPENDHT_CPACK": true
|
||||
"OPENDHT_C": true,
|
||||
"OPENDHT_PYTHON": true,
|
||||
"OPENDHT_CPACK": true,
|
||||
"OPENDHT_DOCUMENTATION": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "vcpkg_release_static",
|
||||
"inherits": "vcpkg_manifest",
|
||||
"inherits": "vcpkg_manifest_static",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"BUILD_SHARED_LIBS": "Off",
|
||||
"VCPKG_TARGET_TRIPLET": "x64-windows-static-release",
|
||||
"VCPKG_OVERLAY_TRIPLETS": "x64-windows-static-release;x64-windows-static-rel;x64-windows-static",
|
||||
"BUILD_TESTING": false,
|
||||
"OPENDHT_CPACK": true,
|
||||
"OPENDHT_C": false
|
||||
@@ -97,16 +143,16 @@
|
||||
},
|
||||
{
|
||||
"name": "vcpkg_release_static_full",
|
||||
"inherits": "vcpkg_manifest",
|
||||
"inherits": "vcpkg_manifest_static",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"BUILD_SHARED_LIBS": "Off",
|
||||
"VCPKG_TARGET_TRIPLET": "x64-windows-static-release",
|
||||
"VCPKG_OVERLAY_TRIPLETS": "x64-windows-static-release;x64-windows-static-rel;x64-windows-static",
|
||||
"BUILD_TESTING": false,
|
||||
"OPENDHT_PROXY_CLIENT": true,
|
||||
"OPENDHT_PROXY_SERVER": true,
|
||||
"OPENDHT_CPACK": true
|
||||
"OPENDHT_C": true,
|
||||
"OPENDHT_CPACK": true,
|
||||
"OPENDHT_DOCUMENTATION": false
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -139,7 +185,7 @@
|
||||
"inherits": "vcpkg_local",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"BUILD_SHARED_LIBS": "Off",
|
||||
"BUILD_SHARED_LIBS": false,
|
||||
"VCPKG_TARGET_TRIPLET": "x64-windows-static-release",
|
||||
"VCPKG_OVERLAY_TRIPLETS": "x64-windows-static-release;x64-windows-static-rel;x64-windows-static",
|
||||
"BUILD_TESTING": false,
|
||||
@@ -152,7 +198,7 @@
|
||||
"inherits": "vcpkg_local",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"BUILD_SHARED_LIBS": "Off",
|
||||
"BUILD_SHARED_LIBS": false,
|
||||
"VCPKG_TARGET_TRIPLET": "x64-windows-static-release",
|
||||
"VCPKG_OVERLAY_TRIPLETS": "x64-windows-static-release;x64-windows-static-rel;x64-windows-static",
|
||||
"BUILD_TESTING": false,
|
||||
@@ -161,5 +207,44 @@
|
||||
"OPENDHT_CPACK": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"configurePreset": "default",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"name": "unix-make",
|
||||
"inherits": "default",
|
||||
"configurePreset": "unix-make"
|
||||
},
|
||||
{
|
||||
"name": "ninja",
|
||||
"inherits": "default",
|
||||
"configurePreset": "ninja"
|
||||
},
|
||||
{
|
||||
"name": "msvc",
|
||||
"inherits": "default",
|
||||
"configurePreset": "msvc"
|
||||
},
|
||||
{
|
||||
"name": "vcpkg-dev",
|
||||
"inherits": "default",
|
||||
"configurePreset": "vcpkg_manifest"
|
||||
},
|
||||
{
|
||||
"name": "vcpkg-release-static",
|
||||
"inherits": "default",
|
||||
"configurePreset": "vcpkg_release_static_full",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "vcpkg-release",
|
||||
"inherits": "default",
|
||||
"configurePreset": "vcpkg_release_full",
|
||||
"configuration": "Release"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
<a id="user-content-opendht-" class="anchor" href="/savoirfairelinux/opendht/blob/master/README.md#opendht-" aria-hidden="true"></a>OpenDHT
|
||||
</h1>
|
||||
|
||||

|
||||
|
||||
A lightweight C++17 Distributed Hash Table implementation.
|
||||
|
||||
OpenDHT provides an easy to use distributed in-memory data store.
|
||||
@@ -23,12 +25,12 @@ See the wiki: <https://github.com/savoirfairelinux/opendht/wiki>
|
||||
|
||||
#### How to run a node
|
||||
|
||||
You can help contributing to the public network by running a stable node with a public IP address.
|
||||
You can help contribute to the public network by running a stable node with a public IP address.
|
||||
https://github.com/savoirfairelinux/opendht/wiki/Running-a-node-with-dhtnode
|
||||
|
||||
#### How-to build and install
|
||||
|
||||
Build instructions: <https://github.com/savoirfairelinux/opendht/wiki/Build-the-library>
|
||||
Build instructions: see [BUILD.md](BUILD.md)
|
||||
|
||||
## Examples
|
||||
### C++ example
|
||||
@@ -73,14 +75,22 @@ int main()
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Python 3 example
|
||||
|
||||
Install on Linux, macOS and Windows with:
|
||||
```sh
|
||||
pip install opendht
|
||||
```
|
||||
|
||||
Using the simple, blocking API:
|
||||
```python
|
||||
import opendht as dht
|
||||
|
||||
node = dht.DhtRunner()
|
||||
node.run()
|
||||
|
||||
# Join the network through any running node,
|
||||
# Join the DHT network through any running node,
|
||||
# here using a known bootstrap node.
|
||||
node.bootstrap("bootstrap.jami.net", "4222")
|
||||
|
||||
@@ -88,8 +98,45 @@ node.bootstrap("bootstrap.jami.net", "4222")
|
||||
node.put(dht.InfoHash.get("unique_key"), dht.Value(b'some binary data'))
|
||||
|
||||
results = node.get(dht.InfoHash.get("unique_key"))
|
||||
for r in results:
|
||||
print(r)
|
||||
for value in results:
|
||||
print(value)
|
||||
```
|
||||
|
||||
Or using asyncio:
|
||||
```python
|
||||
import asyncio
|
||||
import opendht.aio as dht
|
||||
|
||||
async def dht_async_demo(key_str: str):
|
||||
# Start a new node using an async context manager.
|
||||
# It is also possible to call run()/await shutdown() manually.
|
||||
async with dht.DhtRunner(
|
||||
bootstrap=(("bootstrap.jami.net", "4222"),)
|
||||
) as node:
|
||||
# compute key hash
|
||||
key = dht.InfoHash.get(key_str)
|
||||
|
||||
# put data, waiting for completion
|
||||
await node.put(key, dht.Value(b'tata data'))
|
||||
|
||||
# get all values at key
|
||||
results = await node.getAll(key)
|
||||
for value in results:
|
||||
print(value)
|
||||
|
||||
# same operation, but stream values as they come from the network
|
||||
with node.get(key) as results:
|
||||
async for value in results:
|
||||
print(value)
|
||||
|
||||
# listen for change of values at key
|
||||
with node.listen(key) as values:
|
||||
async for value, expired in values:
|
||||
print(value)
|
||||
if value.data == b'tata data':
|
||||
break
|
||||
|
||||
asyncio.run(dht_async_demo("unique_key"))
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
@@ -100,7 +147,7 @@ for r in results:
|
||||
- (optional) restinio used for the REST API.
|
||||
- (optional) llhttp used for the REST API.
|
||||
- (optional) jsoncpp 1.7.4-3+, used for the REST API.
|
||||
- Build tested with GCC 7+ (GNU/Linux, Windows with MinGW), Clang/LLVM (GNU/Linux, Android, macOS, iOS).
|
||||
- Build tested with GCC 9+ (GNU/Linux, Windows with MinGW), Clang/LLVM (GNU/Linux, Android, macOS, iOS).
|
||||
- Build tested with Microsoft Visual Studio 2019, 2022
|
||||
|
||||
## Contact
|
||||
@@ -118,4 +165,3 @@ See COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html for the full GPLv3 l
|
||||
|
||||
## Acknowledgements
|
||||
This project was originally based on https://github.com/jech/dht by Juliusz Chroboczek.
|
||||
It is independent from another (now extinct) project called OpenDHT (Sean Rhea. Ph.D. Thesis, 2005).
|
||||
@@ -268,6 +268,11 @@ dht_publickey* dht_certificate_get_publickey(const dht_certificate* c) {
|
||||
return reinterpret_cast<dht_publickey*>(new PubkeySp(cert->getSharedPublicKey()));
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
// Function with internal C++ linkage
|
||||
#endif
|
||||
|
||||
// dht::crypto::Identity
|
||||
inline dht::crypto::Identity dht_identity_from_c(const dht_identity* cid) {
|
||||
dht::crypto::Identity id {};
|
||||
@@ -285,6 +290,10 @@ inline dht_identity dht_identity_to_c(const dht::crypto::Identity& id) {
|
||||
return cid;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
OPENDHT_C_PUBLIC dht_identity dht_identity_generate(const char* common_name, const dht_identity* ca) {
|
||||
return dht_identity_to_c(dht::crypto::generateIdentity(common_name, dht_identity_from_c(ca)));
|
||||
}
|
||||
|
||||
+3
-3
@@ -135,7 +135,7 @@ typedef struct dht_op_token dht_op_token;
|
||||
OPENDHT_C_PUBLIC void dht_op_token_delete(dht_op_token* token);
|
||||
|
||||
// config
|
||||
struct OPENDHT_PUBLIC dht_node_config {
|
||||
struct OPENDHT_C_PUBLIC dht_node_config {
|
||||
dht_infohash node_id;
|
||||
uint32_t network;
|
||||
bool is_bootstrap;
|
||||
@@ -144,13 +144,13 @@ struct OPENDHT_PUBLIC dht_node_config {
|
||||
};
|
||||
typedef struct dht_node_config dht_node_config;
|
||||
|
||||
struct OPENDHT_PUBLIC dht_secure_config {
|
||||
struct OPENDHT_C_PUBLIC dht_secure_config {
|
||||
dht_node_config node_config;
|
||||
dht_identity id;
|
||||
};
|
||||
typedef struct dht_secure_config dht_secure_config;
|
||||
|
||||
struct OPENDHT_PUBLIC dht_runner_config {
|
||||
struct OPENDHT_C_PUBLIC dht_runner_config {
|
||||
dht_secure_config dht_config;
|
||||
bool threaded;
|
||||
const char* proxy_server;
|
||||
|
||||
@@ -57,7 +57,7 @@ elseif(LLVM_COMPILER_IS_GCC_COMPATIBLE OR CMAKE_CXX_COMPILER_ID MATCHES "XL")
|
||||
message(FATAL_ERROR "Host compiler must support std::atomic!")
|
||||
endif()
|
||||
else()
|
||||
#message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.")
|
||||
#message(FATAL_ERROR "Host compiler appears to require libatomic, but unable to find it.")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
@@ -78,7 +78,7 @@ elseif(LLVM_COMPILER_IS_GCC_COMPATIBLE OR CMAKE_CXX_COMPILER_ID MATCHES "XL")
|
||||
message(FATAL_ERROR "Host compiler must support 64-bit std::atomic!")
|
||||
endif()
|
||||
else()
|
||||
#message(FATAL_ERROR "Host compiler appears to require libatomic for 64-bit operations, but cannot find it.")
|
||||
#message(FATAL_ERROR "Host compiler appears to require libatomic for 64-bit operations, but unable to find it.")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
@@ -123,4 +123,4 @@ int main() {
|
||||
|
||||
if( NOT LLVM_HAS_ATOMICS )
|
||||
message(STATUS "Warning: LLVM will be built thread-unsafe because atomic builtins are missing")
|
||||
endif()
|
||||
endif()
|
||||
@@ -0,0 +1,21 @@
|
||||
# Windows utility functions and macros
|
||||
|
||||
# Determine Windows NT version for compatibility
|
||||
# https://stackoverflow.com/a/40217291/9560245
|
||||
macro(get_WIN32_WINNT version)
|
||||
if(CMAKE_SYSTEM_VERSION)
|
||||
set(ver ${CMAKE_SYSTEM_VERSION})
|
||||
string(REGEX MATCH "^([0-9]+).([0-9])" ver ${ver})
|
||||
string(REGEX MATCH "^([0-9]+)" verMajor ${ver})
|
||||
# Check for Windows 10, b/c we'll need to convert to hex 'A'.
|
||||
if("${verMajor}" MATCHES "10")
|
||||
set(verMajor "A")
|
||||
string(REGEX REPLACE "^([0-9]+)" ${verMajor} ver ${ver})
|
||||
endif()
|
||||
# Remove all remaining '.' characters.
|
||||
string(REPLACE "." "" ver ${ver})
|
||||
# Prepend each digit with a zero.
|
||||
string(REGEX REPLACE "([0-9A-Z])" "0\\1" ver ${ver})
|
||||
set(${version} "0x${ver}")
|
||||
endif()
|
||||
endmacro()
|
||||
+38
-23
@@ -1,11 +1,12 @@
|
||||
dnl define macros
|
||||
m4_define([opendht_major_version], 3)
|
||||
m4_define([opendht_minor_version], 4)
|
||||
m4_define([opendht_patch_version], 0)
|
||||
m4_define([opendht_minor_version], 6)
|
||||
m4_define([opendht_patch_version], 2)
|
||||
m4_define([opendht_version],
|
||||
[opendht_major_version.opendht_minor_version.opendht_patch_version])
|
||||
|
||||
AC_INIT(opendht, [opendht_version])
|
||||
AC_MSG_WARN([The Autotools build system for OpenDHT is deprecated and will be removed in a future release. Please use CMake or Meson instead.])
|
||||
AC_CONFIG_AUX_DIR(ac)
|
||||
AM_INIT_AUTOMAKE([foreign subdir-objects])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
@@ -56,21 +57,25 @@ dnl Check for Doxygen
|
||||
AC_ARG_ENABLE([doc], AS_HELP_STRING([--enable-doc], [Enable documentation generation (doxygen)]))
|
||||
AS_IF([test "x$enable_doc" = "xyes"], [
|
||||
AC_CHECK_PROGS([DOXYGEN], [doxygen])
|
||||
AS_IF([test -z "$DOXYGEN"], [AC_MSG_WARN([Doxygen not found - continuing without Doxygen support])])
|
||||
AS_IF([test -z "$DOXYGEN"],
|
||||
[AC_MSG_ERROR([Doxygen is required to generate the documentation.])])
|
||||
])
|
||||
AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"])
|
||||
|
||||
dnl Check for Python
|
||||
AC_ARG_ENABLE([python], [AS_HELP_STRING([--disable-python], [Disble python binding])])
|
||||
AS_IF([test "x$enable_python" != "xno"], [
|
||||
AM_PATH_PYTHON([3.3],, [:])
|
||||
AS_IF([test -n "$PYTHON"],[
|
||||
echo 'import Cython' | $PYTHON
|
||||
AS_IF([test $? == 0],[CYTHON=yes],[AC_MSG_WARN([Cython not found - continuing without python support])])
|
||||
AC_CHECK_PROGS([PIP], [pip3])
|
||||
AS_IF([test -z "$PIP"],[AC_MSG_WARN([pip not found - continuing without python uninstall support])])
|
||||
])
|
||||
])
|
||||
AC_ARG_ENABLE([python], [AS_HELP_STRING([--disable-python], [Disable python binding])])
|
||||
AS_IF([test "x$enable_python" = "xyes"], [
|
||||
AM_COND_IF([ENABLE_INDEXATION], [
|
||||
AM_PATH_PYTHON([3.3],, [:])
|
||||
AS_IF([test -n "$PYTHON"],[
|
||||
echo 'import Cython' | $PYTHON
|
||||
AS_IF([test $? == 0],
|
||||
[CYTHON=yes],
|
||||
[AC_MSG_ERROR([Cython not found, but required for Python support.])])
|
||||
AC_CHECK_PROGS([PIP], [pip3])
|
||||
AS_IF([test -z "$PIP"],
|
||||
[AC_MSG_WARN([pip not found - continuing without python uninstall support])])])
|
||||
], [AC_MSG_ERROR([Python support requires DHT indexation to be turned on.])])])
|
||||
AM_CONDITIONAL([USE_CYTHON], [test -n "$CYTHON"])
|
||||
AM_CONDITIONAL([HAVE_PIP], [test -n "$PIP"])
|
||||
|
||||
@@ -83,6 +88,7 @@ case "${host_os}" in
|
||||
;;
|
||||
linux*)
|
||||
SYS=linux
|
||||
CPPFLAGS="$CPPFLAGS -D_POSIX_C_SOURCE=200809L"
|
||||
;;
|
||||
darwin*)
|
||||
SYS=darwin
|
||||
@@ -136,25 +142,27 @@ AM_CONDITIONAL(PROXY_CLIENT_OR_SERVER, test x$proxy_client == xyes || test x$pro
|
||||
PKG_CHECK_MODULES([Nettle], [nettle >= 2.4])
|
||||
PKG_CHECK_MODULES([GnuTLS], [gnutls >= 3.3])
|
||||
AC_CHECK_HEADERS([msgpack.hpp], [], [
|
||||
PKG_CHECK_MODULES([MsgPack], [msgpack >= 1.2])
|
||||
PKG_CHECK_MODULES([MsgPack], [msgpack-cxx >= 1.2])
|
||||
])
|
||||
PKG_CHECK_MODULES([Argon2], [libargon2])
|
||||
AC_SUBST(argon2_lib, [", libargon2"])
|
||||
|
||||
AC_ARG_WITH([jsoncpp], AS_HELP_STRING([--without-jsoncpp], [Build without JsonCpp support]))
|
||||
AS_IF([test "x$with_jsoncpp" != "xno"],
|
||||
[PKG_CHECK_MODULES([JsonCpp], [jsoncpp >= 1.7.2], [have_jsoncpp=yes], [have_jsoncpp=no])],
|
||||
[PKG_CHECK_MODULES([JsonCpp], [jsoncpp >= 1.7.2], [have_jsoncpp=yes], [have_jsoncpp=no])
|
||||
PKG_CHECK_MODULES([simdutf], [simdutf >= 6], [have_simdutf=yes], [have_simdutf=no])],
|
||||
[have_jsoncpp=no])
|
||||
AS_IF([test "x$have_jsoncpp" = "xyes"], [
|
||||
AC_MSG_NOTICE([Using JsonCpp])
|
||||
CPPFLAGS="${CPPFLAGS} -DOPENDHT_JSONCPP"
|
||||
AC_SUBST(jsoncpp_lib, [", jsoncpp"])
|
||||
AC_SUBST(simdutf_lib, [", simdutf"])
|
||||
], [
|
||||
AC_MSG_NOTICE([Not using JsonCpp])
|
||||
AM_COND_IF(PROXY_CLIENT_OR_SERVER, AC_MSG_ERROR(["JsonCpp is required for proxy/push notification support"]))
|
||||
])
|
||||
|
||||
AC_ARG_WITH([openssl], AS_HELP_STRING([--without-openssl], [Build with OpenSSL support]))
|
||||
AC_ARG_WITH([openssl], AS_HELP_STRING([--without-openssl], [Build without OpenSSL support]))
|
||||
AS_IF([test "x$with_openssl" != "xno"],
|
||||
[PKG_CHECK_MODULES([OpenSSL], [openssl >= 1.1], [have_openssl=yes], [have_openssl=no])],
|
||||
[have_openssl=no])
|
||||
@@ -175,20 +183,26 @@ AS_IF([test "x$have_fmt" = "xyes"], [
|
||||
AM_COND_IF([PROXY_CLIENT_OR_SERVER], [
|
||||
AC_CHECK_HEADERS([asio.hpp],, AC_MSG_ERROR([Missing Asio headers files]))
|
||||
CXXFLAGS="${CXXFLAGS} -DASIO_STANDALONE"
|
||||
# llhttp has no pkgconfig, instead we check with:
|
||||
AC_CHECK_LIB(llhttp, exit,, AC_MSG_ERROR([Missing llhttp library files]))
|
||||
AC_CHECK_HEADERS([llhttp.h], [llhttp_headers=yes; break;])
|
||||
AC_SUBST(http_lib, ["-lllhttp"])
|
||||
AS_IF([test "x$llhttp_headers" != "xyes"], AC_MSG_ERROR([Missing llhttp headers files]))
|
||||
PKG_CHECK_MODULES([Llhttp], [libllhttp])
|
||||
llhttp_lib=", libllhttp"
|
||||
])
|
||||
AC_SUBST(llhttp_lib)
|
||||
|
||||
AC_ARG_WITH([liburing], AS_HELP_STRING([--with-liburing], [Build with io_uring support(Linux only)]),[], [with_liburing=no])
|
||||
AS_IF([test "x$with_liburing" != "xno"], [
|
||||
CXX_FLAGS="${CXXFLAGS} -DASIO_HAS_IO_URING -DASIO_DISABLE_EPOLL"
|
||||
PKG_CHECK_MODULES([Liburing], [liburing])
|
||||
iouring_lib=", liburing"
|
||||
])
|
||||
AC_SUBST(iouring_lib)
|
||||
|
||||
CXXFLAGS="${CXXFLAGS} -DMSGPACK_NO_BOOST -DMSGPACK_DISABLE_LEGACY_NIL -DMSGPACK_DISABLE_LEGACY_CONVERT"
|
||||
|
||||
AC_ARG_ENABLE([tools], AS_HELP_STRING([--disable-tools],[Disable tools (CLI DHT node)]),,build_tools=yes)
|
||||
AM_CONDITIONAL(ENABLE_TOOLS, test x$build_tools == xyes)
|
||||
AM_COND_IF([ENABLE_TOOLS], [
|
||||
AC_CHECK_HEADERS([readline/readline.h readline/history.h], [], [
|
||||
AC_MSG_ERROR([unable to find readline.h])
|
||||
AC_CHECK_HEADERS([readline/readline.h readline/history.h], [],
|
||||
[AC_MSG_ERROR([Unable to find readline.h. Readline is required for the tools.])
|
||||
])
|
||||
])
|
||||
|
||||
@@ -213,4 +227,5 @@ AC_CONFIG_FILES([Makefile
|
||||
doc/Makefile
|
||||
doc/Doxyfile
|
||||
opendht.pc])
|
||||
AC_MSG_WARN([The Autotools build system for OpenDHT is deprecated and will be removed in a future release. Please use CMake or Meson instead.])
|
||||
AC_OUTPUT
|
||||
+1
-1
@@ -12,5 +12,5 @@ if (OPENDHT_DOCUMENTATION)
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Generating API documentation with Doxygen"
|
||||
VERBATIM)
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION share/doc/opendht)
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION ${CMAKE_INSTALL_DOCDIR}/opendht)
|
||||
endif()
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
FROM ghcr.io/savoirfairelinux/opendht/opendht-deps:latest
|
||||
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
|
||||
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
|
||||
LABEL org.opencontainers.image.source=https://github.com/savoirfairelinux/opendht
|
||||
|
||||
COPY . opendht
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
FROM ghcr.io/savoirfairelinux/opendht/opendht-deps-alpine:latest AS build
|
||||
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
|
||||
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
|
||||
LABEL org.opencontainers.image.source=https://github.com/savoirfairelinux/opendht
|
||||
|
||||
COPY . opendht
|
||||
|
||||
RUN mkdir /install
|
||||
ENV DESTDIR /install
|
||||
ENV DESTDIR=/install
|
||||
|
||||
RUN cd opendht && mkdir build && cd build \
|
||||
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr \
|
||||
@@ -19,7 +19,7 @@ RUN cd opendht && mkdir build && cd build \
|
||||
-DOPENDHT_SYSTEMD=On \
|
||||
&& make -j8 && make install
|
||||
|
||||
FROM alpine:3.19 AS install
|
||||
FROM alpine:3.23 AS install
|
||||
COPY --from=build /install /
|
||||
RUN apk add --no-cache \
|
||||
libstdc++ \
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
FROM opendht-alpine-edge-deps:latest AS build
|
||||
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
|
||||
LABEL org.opencontainers.image.source=https://github.com/savoirfairelinux/opendht
|
||||
|
||||
COPY . opendht
|
||||
|
||||
RUN mkdir /install
|
||||
ENV DESTDIR=/install
|
||||
|
||||
RUN cd opendht && mkdir build && cd build \
|
||||
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DOPENDHT_DOWNLOAD_DEPS=Off \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=On \
|
||||
-DOPENDHT_C=On \
|
||||
-DOPENDHT_PEER_DISCOVERY=On \
|
||||
-DOPENDHT_PYTHON=On \
|
||||
-DOPENDHT_TOOLS=On \
|
||||
-DOPENDHT_PROXY_SERVER=On \
|
||||
-DOPENDHT_PROXY_CLIENT=On \
|
||||
-DOPENDHT_SYSTEMD=On \
|
||||
&& make -j8 && make install
|
||||
|
||||
FROM alpine:edge AS install
|
||||
COPY --from=build /install /
|
||||
RUN apk add --no-cache \
|
||||
libstdc++ \
|
||||
gnutls \
|
||||
nettle \
|
||||
openssl \
|
||||
argon2-dev \
|
||||
jsoncpp \
|
||||
llhttp \
|
||||
fmt \
|
||||
readline \
|
||||
ncurses \
|
||||
gdb
|
||||
#CMD ["dhtnode", "-b", "bootstrap.jami.net", "-p", "4222", "--proxyserver", "8080"]
|
||||
EXPOSE 4222/udp
|
||||
EXPOSE 8080/tcp
|
||||
CMD ["/bin/sh"]
|
||||
@@ -1,17 +0,0 @@
|
||||
FROM ghcr.io/savoirfairelinux/opendht/opendht-deps-bionic:latest
|
||||
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
|
||||
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
|
||||
|
||||
COPY . opendht
|
||||
|
||||
RUN cd opendht && mkdir build && cd build \
|
||||
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=On \
|
||||
-DOPENDHT_C=On \
|
||||
-DOPENDHT_PEER_DISCOVERY=On \
|
||||
-DOPENDHT_PYTHON=On \
|
||||
-DOPENDHT_TOOLS=On \
|
||||
-DOPENDHT_PROXY_SERVER=On \
|
||||
-DOPENDHT_PROXY_CLIENT=On \
|
||||
&& make -j8 && make install \
|
||||
&& cd ../.. && rm -rf opendht
|
||||
@@ -1,6 +1,6 @@
|
||||
FROM ubuntu:22.04
|
||||
FROM ubuntu:24.04
|
||||
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
|
||||
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
|
||||
LABEL org.opencontainers.image.source=https://github.com/savoirfairelinux/opendht
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
dialog apt-utils \
|
||||
@@ -8,16 +8,14 @@ RUN apt-get update && apt-get install -y \
|
||||
&& echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
build-essential pkg-config cmake git wget \
|
||||
build-essential pkg-config cmake git wget meson \
|
||||
libtool autotools-dev autoconf \
|
||||
python3-pip python3-dev python3-setuptools python3-build python3-virtualenv \
|
||||
python3-dev python3-setuptools python3-build python3-virtualenv cython3 \
|
||||
libncurses5-dev libreadline-dev nettle-dev libcppunit-dev \
|
||||
libgnutls28-dev libuv1-dev libjsoncpp-dev libargon2-dev \
|
||||
libssl-dev libfmt-dev libasio-dev libmsgpack-dev \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/* /var/cache/apt/*
|
||||
|
||||
RUN pip3 install meson Cython
|
||||
|
||||
RUN mkdir /usr/include/nonstd \
|
||||
&& wget https://raw.githubusercontent.com/martinmoene/expected-lite/master/include/nonstd/expected.hpp \
|
||||
-O /usr/include/nonstd/expected.hpp
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
FROM alpine:3.19
|
||||
FROM alpine:3.23
|
||||
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
|
||||
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
|
||||
LABEL org.opencontainers.image.source=https://github.com/savoirfairelinux/opendht
|
||||
|
||||
RUN apk add --no-cache \
|
||||
build-base cmake ninja git wget \
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
FROM alpine:edge
|
||||
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
|
||||
LABEL org.opencontainers.image.source=https://github.com/savoirfairelinux/opendht
|
||||
|
||||
RUN apk add --no-cache \
|
||||
build-base cmake ninja git wget \
|
||||
cython python3-dev py3-setuptools \
|
||||
ncurses-dev readline-dev nettle-dev \
|
||||
cppunit-dev gnutls-dev jsoncpp-dev \
|
||||
argon2-dev openssl-dev fmt-dev \
|
||||
llhttp-dev asio-dev msgpack-cxx-dev \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
RUN mkdir /usr/include/nonstd \
|
||||
&& wget https://raw.githubusercontent.com/martinmoene/expected-lite/master/include/nonstd/expected.hpp \
|
||||
-O /usr/include/nonstd/expected.hpp
|
||||
|
||||
RUN echo "*** Downloading RESTinio ***" \
|
||||
&& mkdir restinio && cd restinio \
|
||||
&& wget https://github.com/Stiffstream/restinio/releases/download/v.0.7.3/restinio-0.7.3.tar.bz2 \
|
||||
&& ls -l && tar -xjf restinio-0.7.3.tar.bz2 \
|
||||
&& cd restinio-0.7.3/dev \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DRESTINIO_TEST=Off -DRESTINIO_SAMPLE=Off -DRESTINIO_BENCHMARK=Off \
|
||||
-DRESTINIO_WITH_SOBJECTIZER=Off -DRESTINIO_DEP_STANDALONE_ASIO=system -DRESTINIO_DEP_LLHTTP=system \
|
||||
-DRESTINIO_DEP_FMT=system -DRESTINIO_DEP_EXPECTED_LITE=system . \
|
||||
&& make -j2 && make install \
|
||||
&& cd ../../ && rm -rf restinio*
|
||||
@@ -1,36 +0,0 @@
|
||||
FROM ubuntu:18.04
|
||||
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
|
||||
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
|
||||
|
||||
RUN echo "APT::Acquire::Retries \"3\";" > /etc/apt/apt.conf.d/80-retries
|
||||
RUN apt-get update && apt-get install -y \
|
||||
apt-transport-https build-essential pkg-config git wget libncurses5-dev libreadline-dev nettle-dev \
|
||||
libgnutls28-dev libuv1-dev python3-dev python3-setuptools libcppunit-dev libjsoncpp-dev \
|
||||
libargon2-0-dev \
|
||||
autotools-dev autoconf libfmt-dev libmsgpack-dev libssl-dev python3-pip \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/* /var/cache/apt/*
|
||||
|
||||
RUN pip3 install --upgrade cmake meson Cython
|
||||
|
||||
# libasio-dev (1.10) is too old
|
||||
RUN echo "** Building a recent version of asio ***" \
|
||||
&& wget https://github.com/aberaud/asio/archive/a7d66ef4017d8f1b7f2cef1bb4ba8e23b0961571.tar.gz \
|
||||
&& tar -xvf a7d66ef4017d8f1b7f2cef1bb4ba8e23b0961571.tar.gz && cd asio-a7d66ef4017d8f1b7f2cef1bb4ba8e23b0961571/asio \
|
||||
&& ./autogen.sh && ./configure --prefix=/usr --without-boost --disable-examples --disable-tests \
|
||||
&& make install \
|
||||
&& cd ../../ && rm -rf asio*
|
||||
|
||||
RUN mkdir /usr/include/nonstd \
|
||||
&& wget https://raw.githubusercontent.com/martinmoene/expected-lite/master/include/nonstd/expected.hpp \
|
||||
-O /usr/include/nonstd/expected.hpp
|
||||
|
||||
RUN echo "*** Downloading RESTinio ***" \
|
||||
&& mkdir restinio && cd restinio \
|
||||
&& wget https://github.com/Stiffstream/restinio/releases/download/v.0.7.3/restinio-0.7.3.tar.bz2 \
|
||||
&& ls -l && tar -xjf restinio-0.7.3.tar.bz2 \
|
||||
&& cd restinio-0.7.3/dev \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DRESTINIO_TEST=Off -DRESTINIO_SAMPLE=Off -DRESTINIO_BENCHMARK=Off \
|
||||
-DRESTINIO_WITH_SOBJECTIZER=Off -DRESTINIO_DEP_STANDALONE_ASIO=system -DRESTINIO_DEP_LLHTTP=system \
|
||||
-DRESTINIO_DEP_FMT=system -DRESTINIO_DEP_EXPECTED_LITE=system . \
|
||||
&& make -j2 && make install \
|
||||
&& cd ../../ && rm -rf restinio*
|
||||
@@ -1,6 +1,6 @@
|
||||
FROM ubuntu:20.04
|
||||
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
|
||||
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
|
||||
LABEL org.opencontainers.image.source=https://github.com/savoirfairelinux/opendht
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
dialog apt-utils \
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
FROM ubuntu:24.04
|
||||
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
|
||||
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
|
||||
LABEL org.opencontainers.image.source=https://github.com/savoirfairelinux/opendht
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
dialog apt-utils \
|
||||
@@ -19,8 +19,8 @@ RUN apt-get update \
|
||||
|
||||
RUN pip3 install Cython --break-system-packages
|
||||
|
||||
ENV CC cc
|
||||
ENV CXX c++
|
||||
ENV CC=cc
|
||||
ENV CXX=c++
|
||||
|
||||
RUN mkdir /usr/include/nonstd \
|
||||
&& wget https://raw.githubusercontent.com/martinmoene/expected-lite/master/include/nonstd/expected.hpp \
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
FROM ghcr.io/savoirfairelinux/opendht/opendht-deps-bionic:latest
|
||||
FROM ghcr.io/savoirfairelinux/opendht/opendht-deps-focal:latest
|
||||
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
|
||||
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
|
||||
LABEL org.opencontainers.image.source=https://github.com/savoirfairelinux/opendht
|
||||
|
||||
COPY . opendht
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
FROM ghcr.io/savoirfairelinux/opendht/opendht-deps-llvm:latest
|
||||
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
|
||||
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
|
||||
LABEL org.opencontainers.image.source=https://github.com/savoirfairelinux/opendht
|
||||
|
||||
COPY . opendht
|
||||
|
||||
|
||||
@@ -189,7 +189,9 @@ using ShutdownCallback = std::function<void()>;
|
||||
using IdentityAnnouncedCb = std::function<void(bool)>;
|
||||
using PublicAddressChangedCb = std::function<void(std::vector<SockAddr>)>;
|
||||
|
||||
using CertificateStoreQuery = std::function<std::vector<std::shared_ptr<crypto::Certificate>>(const InfoHash& pk_id)>;
|
||||
using CertificateStoreQueryLegacy = std::function<std::vector<std::shared_ptr<crypto::Certificate>>(const InfoHash& pk_id)>;
|
||||
using CertificateStoreQuery = std::function<std::vector<std::shared_ptr<crypto::Certificate>>(const PkId& pk_id)>;
|
||||
|
||||
using DoneCallback = std::function<void(bool success, const std::vector<std::shared_ptr<Node>>& nodes)>;
|
||||
using DoneCallbackSimple = std::function<void(bool success)>;
|
||||
|
||||
|
||||
+192
-90
@@ -1,7 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2025 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Vsevolod Ivanov <vsevolod.ivanov@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -47,10 +45,13 @@ namespace dht {
|
||||
*/
|
||||
namespace crypto {
|
||||
|
||||
class OPENDHT_PUBLIC CryptoException : public std::runtime_error {
|
||||
class OPENDHT_PUBLIC CryptoException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit CryptoException(const std::string& str) : std::runtime_error(str) {};
|
||||
explicit CryptoException(const char* str) : std::runtime_error(str) {};
|
||||
explicit CryptoException(const std::string& str)
|
||||
: std::runtime_error(str) {};
|
||||
explicit CryptoException(const char* str)
|
||||
: std::runtime_error(str) {};
|
||||
CryptoException(const CryptoException& e) noexcept = default;
|
||||
CryptoException& operator=(const CryptoException&) noexcept = default;
|
||||
};
|
||||
@@ -58,10 +59,13 @@ public:
|
||||
/**
|
||||
* Exception thrown when a decryption error happened.
|
||||
*/
|
||||
class OPENDHT_PUBLIC DecryptError : public CryptoException {
|
||||
class OPENDHT_PUBLIC DecryptError : public CryptoException
|
||||
{
|
||||
public:
|
||||
explicit DecryptError(const std::string& str) : CryptoException(str) {};
|
||||
explicit DecryptError(const char* str) : CryptoException(str) {};
|
||||
explicit DecryptError(const std::string& str)
|
||||
: CryptoException(str) {};
|
||||
explicit DecryptError(const char* str)
|
||||
: CryptoException(str) {};
|
||||
DecryptError(const DecryptError& e) noexcept = default;
|
||||
DecryptError& operator=(const DecryptError&) noexcept = default;
|
||||
};
|
||||
@@ -82,29 +86,35 @@ struct OPENDHT_PUBLIC PublicKey
|
||||
/**
|
||||
* Takes ownership of an existing gnutls_pubkey.
|
||||
*/
|
||||
PublicKey(gnutls_pubkey_t k) : pk(k) {}
|
||||
PublicKey(gnutls_pubkey_t k)
|
||||
: pk(k)
|
||||
{}
|
||||
|
||||
/** Import public key from serialized data */
|
||||
PublicKey(const uint8_t* dat, size_t dat_size);
|
||||
PublicKey(const Blob& pk) : PublicKey(pk.data(), pk.size()) {}
|
||||
PublicKey(std::string_view pk) : PublicKey((const uint8_t*)pk.data(), pk.size()) {}
|
||||
PublicKey(PublicKey&& o) noexcept : pk(o.pk) { o.pk = nullptr; }
|
||||
PublicKey(const Blob& pk)
|
||||
: PublicKey(pk.data(), pk.size())
|
||||
{}
|
||||
PublicKey(std::string_view pk)
|
||||
: PublicKey((const uint8_t*) pk.data(), pk.size())
|
||||
{}
|
||||
PublicKey(PublicKey&& o) noexcept
|
||||
: pk(o.pk)
|
||||
{
|
||||
o.pk = nullptr;
|
||||
}
|
||||
|
||||
~PublicKey();
|
||||
explicit operator bool() const { return pk; }
|
||||
bool operator ==(const PublicKey& o) const {
|
||||
return pk == o.pk || getLongId() == o.getLongId();
|
||||
}
|
||||
bool operator !=(const PublicKey& o) const {
|
||||
return !(*this == o);
|
||||
}
|
||||
bool operator==(const PublicKey& o) const { return pk == o.pk || getLongId() == o.getLongId(); }
|
||||
bool operator!=(const PublicKey& o) const { return !(*this == o); }
|
||||
|
||||
PublicKey& operator=(PublicKey&& o) noexcept;
|
||||
|
||||
/**
|
||||
* Get public key fingerprint
|
||||
*/
|
||||
const InfoHash& getId() const;
|
||||
const InfoHash& getId() const;
|
||||
|
||||
/**
|
||||
* Get public key long fingerprint
|
||||
@@ -112,30 +122,27 @@ struct OPENDHT_PUBLIC PublicKey
|
||||
const PkId& getLongId() const;
|
||||
|
||||
bool checkSignature(const uint8_t* data, size_t data_len, const uint8_t* signature, size_t signature_len) const;
|
||||
inline bool checkSignature(const Blob& data, const Blob& signature) const {
|
||||
inline bool checkSignature(const Blob& data, const Blob& signature) const
|
||||
{
|
||||
return checkSignature(data.data(), data.size(), signature.data(), signature.size());
|
||||
}
|
||||
|
||||
Blob encrypt(const uint8_t* data, size_t data_len) const;
|
||||
inline Blob encrypt(const Blob& data) const {
|
||||
return encrypt(data.data(), data.size());
|
||||
}
|
||||
inline Blob encrypt(std::string_view data) const {
|
||||
return encrypt((const uint8_t*)data.data(), data.size());
|
||||
}
|
||||
inline Blob encrypt(const Blob& data) const { return encrypt(data.data(), data.size()); }
|
||||
inline Blob encrypt(std::string_view data) const { return encrypt((const uint8_t*) data.data(), data.size()); }
|
||||
void pack(Blob& b) const;
|
||||
int pack(uint8_t* out, size_t* out_len) const;
|
||||
void unpack(const uint8_t* dat, size_t dat_size);
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
template <typename Packer>
|
||||
template<typename Packer>
|
||||
void msgpack_pack(Packer& p) const
|
||||
{
|
||||
Blob b;
|
||||
pack(b);
|
||||
p.pack_bin(b.size());
|
||||
p.pack_bin_body((const char*)b.data(), b.size());
|
||||
p.pack_bin_body((const char*) b.data(), b.size());
|
||||
}
|
||||
|
||||
void msgpack_unpack(const msgpack::object& o);
|
||||
@@ -143,6 +150,7 @@ struct OPENDHT_PUBLIC PublicKey
|
||||
gnutls_digest_algorithm_t getPreferredDigest() const;
|
||||
|
||||
gnutls_pubkey_t pk {nullptr};
|
||||
|
||||
private:
|
||||
mutable InfoHash cachedId_ {};
|
||||
mutable PkId cachedLongId_ {};
|
||||
@@ -160,7 +168,7 @@ private:
|
||||
struct OPENDHT_PUBLIC PrivateKey
|
||||
{
|
||||
PrivateKey();
|
||||
//PrivateKey(gnutls_privkey_t k) : key(k) {}
|
||||
// PrivateKey(gnutls_privkey_t k) : key(k) {}
|
||||
|
||||
/**
|
||||
* Takes ownership of an existing gnutls_x509_privkey.
|
||||
@@ -171,8 +179,12 @@ struct OPENDHT_PUBLIC PrivateKey
|
||||
PrivateKey& operator=(PrivateKey&& o) noexcept;
|
||||
|
||||
PrivateKey(const uint8_t* src, size_t src_size, const char* password = nullptr);
|
||||
PrivateKey(const Blob& src, const std::string& password = {}) : PrivateKey(src.data(), src.size(), password.c_str()) {}
|
||||
PrivateKey(std::string_view src, const std::string& password = {}) : PrivateKey((const uint8_t*)src.data(), src.size(), password.c_str()) {}
|
||||
PrivateKey(const Blob& src, const std::string& password = {})
|
||||
: PrivateKey(src.data(), src.size(), password.c_str())
|
||||
{}
|
||||
PrivateKey(std::string_view src, const std::string& password = {})
|
||||
: PrivateKey((const uint8_t*) src.data(), src.size(), password.c_str())
|
||||
{}
|
||||
|
||||
~PrivateKey();
|
||||
explicit operator bool() const { return key; }
|
||||
@@ -188,7 +200,7 @@ struct OPENDHT_PUBLIC PrivateKey
|
||||
* @returns the signature data.
|
||||
*/
|
||||
Blob sign(const uint8_t* data, size_t data_len) const;
|
||||
inline Blob sign(std::string_view dat) const { return sign((const uint8_t*)dat.data(), dat.size()); }
|
||||
inline Blob sign(std::string_view dat) const { return sign((const uint8_t*) dat.data(), dat.size()); }
|
||||
inline Blob sign(const Blob& dat) const { return sign(dat.data(), dat.size()); }
|
||||
|
||||
/**
|
||||
@@ -204,12 +216,14 @@ struct OPENDHT_PUBLIC PrivateKey
|
||||
* @param key_length : size of the modulus in bits
|
||||
* Minimim value: 2048
|
||||
* Recommended values: 4096, 8192
|
||||
* @param algo : the public key algorithm to use, GNUTLS_PK_RSA or GNUTLS_PK_RSA_OAEP
|
||||
*/
|
||||
static PrivateKey generate(unsigned key_length = 4096);
|
||||
static PrivateKey generate(unsigned key_length = 4096, gnutls_pk_algorithm_t algo = GNUTLS_PK_RSA);
|
||||
static PrivateKey generateEC();
|
||||
|
||||
gnutls_privkey_t key {};
|
||||
gnutls_x509_privkey_t x509_key {};
|
||||
|
||||
private:
|
||||
PrivateKey(const PrivateKey&) = delete;
|
||||
PrivateKey& operator=(const PrivateKey&) = delete;
|
||||
@@ -224,28 +238,39 @@ class OPENDHT_PUBLIC RevocationList
|
||||
using clock = std::chrono::system_clock;
|
||||
using time_point = clock::time_point;
|
||||
using duration = clock::duration;
|
||||
|
||||
public:
|
||||
RevocationList();
|
||||
RevocationList(const Blob& b);
|
||||
RevocationList(RevocationList&& o) noexcept : crl(o.crl) { o.crl = nullptr; }
|
||||
RevocationList(RevocationList&& o) noexcept
|
||||
: crl(o.crl)
|
||||
{
|
||||
o.crl = nullptr;
|
||||
}
|
||||
~RevocationList();
|
||||
|
||||
RevocationList& operator=(RevocationList&& o) { crl = o.crl; o.crl = nullptr; return *this; }
|
||||
RevocationList& operator=(RevocationList&& o)
|
||||
{
|
||||
crl = o.crl;
|
||||
o.crl = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void pack(Blob& b) const;
|
||||
void unpack(const uint8_t* dat, size_t dat_size);
|
||||
Blob getPacked() const {
|
||||
Blob getPacked() const
|
||||
{
|
||||
Blob b;
|
||||
pack(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
template <typename Packer>
|
||||
template<typename Packer>
|
||||
void msgpack_pack(Packer& p) const
|
||||
{
|
||||
Blob b = getPacked();
|
||||
p.pack_bin(b.size());
|
||||
p.pack_bin_body((const char*)b.data(), b.size());
|
||||
p.pack_bin_body((const char*) b.data(), b.size());
|
||||
}
|
||||
|
||||
void msgpack_unpack(const msgpack::object& o);
|
||||
@@ -280,7 +305,8 @@ public:
|
||||
time_point getNextUpdateTime() const;
|
||||
|
||||
gnutls_x509_crl_t get() { return crl; }
|
||||
gnutls_x509_crl_t getCopy() const {
|
||||
gnutls_x509_crl_t getCopy() const
|
||||
{
|
||||
if (not crl)
|
||||
return nullptr;
|
||||
auto copy = RevocationList(getPacked());
|
||||
@@ -297,14 +323,21 @@ private:
|
||||
|
||||
enum class NameType { UNKNOWN = 0, RFC822, DNS, URI, IP };
|
||||
|
||||
class OPENDHT_PUBLIC CertificateRequest {
|
||||
class OPENDHT_PUBLIC CertificateRequest
|
||||
{
|
||||
public:
|
||||
CertificateRequest();
|
||||
CertificateRequest(const uint8_t* data, size_t size);
|
||||
CertificateRequest(std::string_view src) : CertificateRequest((const uint8_t*)src.data(), src.size()) {}
|
||||
CertificateRequest(const Blob& data) : CertificateRequest(data.data(), data.size()) {}
|
||||
CertificateRequest(std::string_view src)
|
||||
: CertificateRequest((const uint8_t*) src.data(), src.size())
|
||||
{}
|
||||
CertificateRequest(const Blob& data)
|
||||
: CertificateRequest(data.data(), data.size())
|
||||
{}
|
||||
|
||||
CertificateRequest(CertificateRequest&& o) noexcept : request(std::move(o.request)) {
|
||||
CertificateRequest(CertificateRequest&& o) noexcept
|
||||
: request(std::move(o.request))
|
||||
{
|
||||
o.request = nullptr;
|
||||
}
|
||||
CertificateRequest& operator=(CertificateRequest&& o) noexcept;
|
||||
@@ -326,6 +359,7 @@ public:
|
||||
std::string toString() const;
|
||||
|
||||
gnutls_x509_crq_t get() const { return request; }
|
||||
|
||||
private:
|
||||
CertificateRequest(const CertificateRequest& o) = delete;
|
||||
CertificateRequest& operator=(const CertificateRequest& o) = delete;
|
||||
@@ -335,9 +369,13 @@ private:
|
||||
class OPENDHT_PUBLIC OcspRequest
|
||||
{
|
||||
public:
|
||||
OcspRequest(gnutls_ocsp_req_t r) : request(r) {}
|
||||
OcspRequest(gnutls_ocsp_req_t r)
|
||||
: request(r)
|
||||
{}
|
||||
OcspRequest(const uint8_t* dat_ptr, size_t dat_size);
|
||||
OcspRequest(std::string_view dat): OcspRequest((const uint8_t*)dat.data(), dat.size()) {}
|
||||
OcspRequest(std::string_view dat)
|
||||
: OcspRequest((const uint8_t*) dat.data(), dat.size())
|
||||
{}
|
||||
~OcspRequest();
|
||||
|
||||
/*
|
||||
@@ -347,6 +385,7 @@ public:
|
||||
|
||||
Blob pack() const;
|
||||
Blob getNonce() const;
|
||||
|
||||
private:
|
||||
gnutls_ocsp_req_t request;
|
||||
};
|
||||
@@ -355,7 +394,9 @@ class OPENDHT_PUBLIC OcspResponse
|
||||
{
|
||||
public:
|
||||
OcspResponse(const uint8_t* dat_ptr, size_t dat_size);
|
||||
OcspResponse(std::string_view response) : OcspResponse((const uint8_t*)response.data(), response.size()) {}
|
||||
OcspResponse(std::string_view response)
|
||||
: OcspResponse((const uint8_t*) response.data(), response.size())
|
||||
{}
|
||||
~OcspResponse();
|
||||
|
||||
Blob pack() const;
|
||||
@@ -382,36 +423,46 @@ private:
|
||||
gnutls_ocsp_resp_t response;
|
||||
};
|
||||
|
||||
struct OPENDHT_PUBLIC Certificate {
|
||||
struct OPENDHT_PUBLIC Certificate
|
||||
{
|
||||
Certificate() noexcept {}
|
||||
|
||||
/**
|
||||
* Take ownership of existing gnutls structure
|
||||
*/
|
||||
Certificate(gnutls_x509_crt_t crt) noexcept : cert(crt) {}
|
||||
Certificate(gnutls_x509_crt_t crt) noexcept
|
||||
: cert(crt)
|
||||
{}
|
||||
|
||||
Certificate(Certificate&& o) noexcept
|
||||
: cert(o.cert)
|
||||
, issuer(std::move(o.issuer))
|
||||
, publicKey_(std::move(o.publicKey_))
|
||||
{ o.cert = nullptr; };
|
||||
{
|
||||
o.cert = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Import certificate (PEM or DER) or certificate chain (PEM),
|
||||
* ordered from subject to issuer
|
||||
*/
|
||||
Certificate(const Blob& crt);
|
||||
Certificate(const uint8_t* dat, size_t dat_size) : cert(nullptr) {
|
||||
Certificate(const uint8_t* dat, size_t dat_size)
|
||||
: cert(nullptr)
|
||||
{
|
||||
unpack(dat, dat_size);
|
||||
}
|
||||
Certificate(std::string_view pem) : Certificate((const uint8_t*)pem.data(), pem.size()) {}
|
||||
Certificate(std::string_view pem)
|
||||
: Certificate((const uint8_t*) pem.data(), pem.size())
|
||||
{}
|
||||
|
||||
/**
|
||||
* Import certificate chain (PEM or DER),
|
||||
* ordered from subject to issuer
|
||||
*/
|
||||
template<typename Iterator>
|
||||
Certificate(const Iterator& begin, const Iterator& end) {
|
||||
Certificate(const Iterator& begin, const Iterator& end)
|
||||
{
|
||||
unpack(begin, end);
|
||||
}
|
||||
|
||||
@@ -420,7 +471,8 @@ struct OPENDHT_PUBLIC Certificate {
|
||||
* ordered from subject to issuer
|
||||
*/
|
||||
template<typename Iterator>
|
||||
Certificate(const std::vector<std::pair<Iterator, Iterator>>& certs) {
|
||||
Certificate(const std::vector<std::pair<Iterator, Iterator>>& certs)
|
||||
{
|
||||
unpack(certs);
|
||||
}
|
||||
|
||||
@@ -429,7 +481,8 @@ struct OPENDHT_PUBLIC Certificate {
|
||||
|
||||
void pack(Blob& b) const;
|
||||
void unpack(const uint8_t* dat, size_t dat_size);
|
||||
Blob getPacked() const {
|
||||
Blob getPacked() const
|
||||
{
|
||||
Blob b;
|
||||
pack(b);
|
||||
return b;
|
||||
@@ -478,7 +531,7 @@ struct OPENDHT_PUBLIC Certificate {
|
||||
for (auto li = certs.rbegin(); li != certs.rend(); ++li) {
|
||||
Certificate tmp_crt;
|
||||
gnutls_x509_crt_init(&tmp_crt.cert);
|
||||
const gnutls_datum_t crt_dt {(uint8_t*)&(*li->first), (unsigned)(li->second-li->first)};
|
||||
const gnutls_datum_t crt_dt {(uint8_t*) &(*li->first), (unsigned) (li->second - li->first)};
|
||||
int err = gnutls_x509_crt_import(tmp_crt.cert, &crt_dt, GNUTLS_X509_FMT_PEM);
|
||||
if (err != GNUTLS_E_SUCCESS)
|
||||
err = gnutls_x509_crt_import(tmp_crt.cert, &crt_dt, GNUTLS_X509_FMT_DER);
|
||||
@@ -490,13 +543,13 @@ struct OPENDHT_PUBLIC Certificate {
|
||||
*this = tmp_issuer ? std::move(*tmp_issuer) : Certificate();
|
||||
}
|
||||
|
||||
template <typename Packer>
|
||||
template<typename Packer>
|
||||
void msgpack_pack(Packer& p) const
|
||||
{
|
||||
Blob b;
|
||||
pack(b);
|
||||
p.pack_bin(b.size());
|
||||
p.pack_bin_body((const char*)b.data(), b.size());
|
||||
p.pack_bin_body((const char*) b.data(), b.size());
|
||||
}
|
||||
|
||||
void msgpack_unpack(const msgpack::object& o);
|
||||
@@ -512,7 +565,7 @@ struct OPENDHT_PUBLIC Certificate {
|
||||
|
||||
Blob getSerialNumber() const;
|
||||
|
||||
/** Read certificate full DN as described in RFC4514 */
|
||||
/** Read certificate full DN as described in RFC4514 */
|
||||
std::string getDN() const;
|
||||
|
||||
/** Read certificate Common Name (CN) */
|
||||
@@ -567,10 +620,15 @@ struct OPENDHT_PUBLIC Certificate {
|
||||
void addRevocationList(RevocationList&&);
|
||||
void addRevocationList(std::shared_ptr<RevocationList>);
|
||||
|
||||
static Certificate generate(const PrivateKey& key, const std::string& name = "dhtnode", const Identity& ca = {}, bool is_ca = false, int64_t validity = 0);
|
||||
static Certificate generate(const PrivateKey& key,
|
||||
const std::string& name = "dhtnode",
|
||||
const Identity& ca = {},
|
||||
bool is_ca = false,
|
||||
int64_t validity = 0);
|
||||
static Certificate generate(const CertificateRequest& request, const Identity& ca, int64_t validity = 0);
|
||||
|
||||
gnutls_x509_crt_t getCopy() const {
|
||||
gnutls_x509_crt_t getCopy() const
|
||||
{
|
||||
if (not cert)
|
||||
return nullptr;
|
||||
auto copy = Certificate(getPacked());
|
||||
@@ -579,8 +637,7 @@ struct OPENDHT_PUBLIC Certificate {
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<gnutls_x509_crt_t>
|
||||
getChain(bool copy = false) const
|
||||
std::vector<gnutls_x509_crt_t> getChain(bool copy = false) const
|
||||
{
|
||||
if (not cert)
|
||||
return {};
|
||||
@@ -590,11 +647,8 @@ struct OPENDHT_PUBLIC Certificate {
|
||||
return crts;
|
||||
}
|
||||
|
||||
std::pair<
|
||||
std::vector<gnutls_x509_crt_t>,
|
||||
std::vector<gnutls_x509_crl_t>
|
||||
>
|
||||
getChainWithRevocations(bool copy = false) const
|
||||
std::pair<std::vector<gnutls_x509_crt_t>, std::vector<gnutls_x509_crl_t>> getChainWithRevocations(
|
||||
bool copy = false) const
|
||||
{
|
||||
if (not cert)
|
||||
return {};
|
||||
@@ -627,6 +681,7 @@ struct OPENDHT_PUBLIC Certificate {
|
||||
gnutls_x509_crt_t cert {nullptr};
|
||||
std::shared_ptr<Certificate> issuer {};
|
||||
std::shared_ptr<OcspResponse> ocspResponse;
|
||||
|
||||
private:
|
||||
Certificate(const Certificate&) = delete;
|
||||
Certificate& operator=(const Certificate&) = delete;
|
||||
@@ -635,8 +690,10 @@ private:
|
||||
mutable std::atomic_bool idCached_ {false};
|
||||
mutable std::atomic_bool longIdCached_ {false};
|
||||
|
||||
struct crlNumberCmp {
|
||||
bool operator() (const std::shared_ptr<RevocationList>& lhs, const std::shared_ptr<RevocationList>& rhs) const {
|
||||
struct crlNumberCmp
|
||||
{
|
||||
bool operator()(const std::shared_ptr<RevocationList>& lhs, const std::shared_ptr<RevocationList>& rhs) const
|
||||
{
|
||||
return lhs->getNumber() < rhs->getNumber();
|
||||
}
|
||||
};
|
||||
@@ -649,18 +706,21 @@ private:
|
||||
|
||||
struct OPENDHT_PUBLIC TrustList
|
||||
{
|
||||
struct VerifyResult {
|
||||
struct VerifyResult
|
||||
{
|
||||
int ret;
|
||||
unsigned result;
|
||||
bool hasError() const { return ret < 0; }
|
||||
bool isValid() const { return !hasError() and !(result & GNUTLS_CERT_INVALID); }
|
||||
explicit operator bool() const { return isValid(); }
|
||||
OPENDHT_PUBLIC std::string toString() const;
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const VerifyResult& h);
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<<(std::ostream& s, const VerifyResult& h);
|
||||
};
|
||||
|
||||
TrustList();
|
||||
TrustList(TrustList&& o) noexcept : trust(std::move(o.trust)) {
|
||||
TrustList(TrustList&& o) noexcept
|
||||
: trust(std::move(o.trust))
|
||||
{
|
||||
o.trust = nullptr;
|
||||
}
|
||||
TrustList& operator=(TrustList&& o) noexcept;
|
||||
@@ -684,13 +744,15 @@ private:
|
||||
* @param key_length stength of the generated private key (bits).
|
||||
*/
|
||||
OPENDHT_PUBLIC Identity generateIdentity(const std::string& name, const Identity& ca, unsigned key_length, bool is_ca);
|
||||
OPENDHT_PUBLIC Identity generateIdentity(const std::string& name = "dhtnode", const Identity& ca = {}, unsigned key_length = 4096);
|
||||
OPENDHT_PUBLIC Identity generateIdentity(const std::string& name = "dhtnode",
|
||||
const Identity& ca = {},
|
||||
unsigned key_length = 4096);
|
||||
|
||||
OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name, const Identity& ca, bool is_ca);
|
||||
OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name = "dhtnode", const Identity& ca = {});
|
||||
|
||||
OPENDHT_PUBLIC void saveIdentity(const Identity& id, const std::string& path, const std::string& privkey_password = {});
|
||||
OPENDHT_PUBLIC Identity loadIdentity(const std::string &path,const std::string &privkey_password = {});
|
||||
OPENDHT_PUBLIC Identity loadIdentity(const std::string& path, const std::string& privkey_password = {});
|
||||
|
||||
/**
|
||||
* Performs SHA512, SHA256 or SHA1, depending on hash_length.
|
||||
@@ -700,7 +762,7 @@ OPENDHT_PUBLIC Identity loadIdentity(const std::string &path,const std::string &
|
||||
* will use SHA256 for hash_length up to 32 bytes,
|
||||
* will use SHA512 for hash_length of 33 bytes and more.
|
||||
*/
|
||||
OPENDHT_PUBLIC Blob hash(const Blob& data, size_t hash_length = 512/8);
|
||||
OPENDHT_PUBLIC Blob hash(const Blob& data, size_t hash_length = 512 / 8);
|
||||
|
||||
OPENDHT_PUBLIC void hash(const uint8_t* data, size_t data_length, uint8_t* hash, size_t hash_length);
|
||||
|
||||
@@ -711,20 +773,23 @@ OPENDHT_PUBLIC void hash(const uint8_t* data, size_t data_length, uint8_t* hash,
|
||||
* that can be transmitted in clear, and will be generated if
|
||||
* not provided (32 bytes).
|
||||
*/
|
||||
OPENDHT_PUBLIC Blob stretchKey(std::string_view password, Blob& salt, size_t key_length = 512/8);
|
||||
OPENDHT_PUBLIC Blob stretchKey(std::string_view password, Blob& salt, size_t key_length = 512 / 8);
|
||||
|
||||
/**
|
||||
* AES-GCM encryption. Key must be 128, 192 or 256 bits long (16, 24 or 32 bytes).
|
||||
*/
|
||||
OPENDHT_PUBLIC Blob aesEncrypt(const uint8_t* data, size_t data_length, const Blob& key);
|
||||
OPENDHT_PUBLIC inline Blob aesEncrypt(const Blob& data, const Blob& key) {
|
||||
OPENDHT_PUBLIC inline Blob
|
||||
aesEncrypt(const Blob& data, const Blob& key)
|
||||
{
|
||||
return aesEncrypt(data.data(), data.size(), key);
|
||||
}
|
||||
/**
|
||||
* AES-GCM encryption with argon2 key derivation.
|
||||
* This function uses `stretchKey` to generate an AES key from the password and a random salt.
|
||||
* The result is a bundle including the salt that can be decrypted with `aesDecrypt(data, password)`.
|
||||
* If needed, the salt or encrypted data can be individually extracted from the bundle with `aesGetSalt` and `aesGetEncrypted`.
|
||||
* If needed, the salt or encrypted data can be individually extracted from the bundle with `aesGetSalt` and
|
||||
* `aesGetEncrypted`.
|
||||
* @param data: data to encrypt
|
||||
* @param password: password to encrypt the data with
|
||||
* @param salt: optional salt to use for key derivation. If not provided, a random salt will be generated.
|
||||
@@ -735,28 +800,47 @@ OPENDHT_PUBLIC Blob aesEncrypt(const Blob& data, std::string_view password, cons
|
||||
* AES-GCM decryption.
|
||||
*/
|
||||
OPENDHT_PUBLIC Blob aesDecrypt(const uint8_t* data, size_t data_length, const Blob& key);
|
||||
OPENDHT_PUBLIC inline Blob aesDecrypt(const Blob& data, const Blob& key) { return aesDecrypt(data.data(), data.size(), key); }
|
||||
OPENDHT_PUBLIC inline Blob aesDecrypt(std::string_view data, const Blob& key) { return aesDecrypt((uint8_t*)data.data(), data.size(), key); }
|
||||
OPENDHT_PUBLIC inline Blob
|
||||
aesDecrypt(const Blob& data, const Blob& key)
|
||||
{
|
||||
return aesDecrypt(data.data(), data.size(), key);
|
||||
}
|
||||
OPENDHT_PUBLIC inline Blob
|
||||
aesDecrypt(std::string_view data, const Blob& key)
|
||||
{
|
||||
return aesDecrypt((uint8_t*) data.data(), data.size(), key);
|
||||
}
|
||||
|
||||
OPENDHT_PUBLIC Blob aesDecrypt(const uint8_t* data, size_t data_length, std::string_view password);
|
||||
OPENDHT_PUBLIC inline Blob aesDecrypt(const Blob& data, std::string_view password) { return aesDecrypt(data.data(), data.size(), password); }
|
||||
OPENDHT_PUBLIC inline Blob aesDecrypt(std::string_view data, std::string_view password) { return aesDecrypt((uint8_t*)data.data(), data.size(), password); }
|
||||
OPENDHT_PUBLIC inline Blob
|
||||
aesDecrypt(const Blob& data, std::string_view password)
|
||||
{
|
||||
return aesDecrypt(data.data(), data.size(), password);
|
||||
}
|
||||
OPENDHT_PUBLIC inline Blob
|
||||
aesDecrypt(std::string_view data, std::string_view password)
|
||||
{
|
||||
return aesDecrypt((uint8_t*) data.data(), data.size(), password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get raw AES key from password and salt stored with the encrypted data.
|
||||
*/
|
||||
OPENDHT_PUBLIC Blob aesGetKey(const uint8_t* data, size_t data_length, std::string_view password);
|
||||
OPENDHT_PUBLIC Blob inline aesGetKey(const Blob& data, std::string_view password) {
|
||||
OPENDHT_PUBLIC Blob inline aesGetKey(const Blob& data, std::string_view password)
|
||||
{
|
||||
return aesGetKey(data.data(), data.size(), password);
|
||||
}
|
||||
/** Get the salt part of data password-encrypted with `aesEncrypt(data, password)` */
|
||||
OPENDHT_PUBLIC Blob aesGetSalt(const uint8_t* data, size_t data_length);
|
||||
OPENDHT_PUBLIC Blob inline aesGetSalt(const Blob& data) {
|
||||
OPENDHT_PUBLIC Blob inline aesGetSalt(const Blob& data)
|
||||
{
|
||||
return aesGetSalt(data.data(), data.size());
|
||||
}
|
||||
/** Get the encrypted data (ciphertext) part of data password-encrypted with `aesEncrypt(data, password)` */
|
||||
OPENDHT_PUBLIC std::string_view aesGetEncrypted(const uint8_t* data, size_t data_length);
|
||||
OPENDHT_PUBLIC std::string_view inline aesGetEncrypted(const Blob& data) {
|
||||
OPENDHT_PUBLIC std::string_view inline aesGetEncrypted(const Blob& data)
|
||||
{
|
||||
return aesGetEncrypted(data.data(), data.size());
|
||||
}
|
||||
|
||||
@@ -766,12 +850,30 @@ OPENDHT_PUBLIC std::string_view inline aesGetEncrypted(const Blob& data) {
|
||||
* Can be obtained from an existing bundle with `aesGetSalt`.
|
||||
**/
|
||||
OPENDHT_PUBLIC Blob aesBuildEncrypted(const uint8_t* encryptedData, size_t data_length, const Blob& salt);
|
||||
OPENDHT_PUBLIC Blob inline aesBuildEncrypted(const Blob& encryptedData, const Blob& salt) {
|
||||
OPENDHT_PUBLIC Blob inline aesBuildEncrypted(const Blob& encryptedData, const Blob& salt)
|
||||
{
|
||||
return aesBuildEncrypted(encryptedData.data(), encryptedData.size(), salt);
|
||||
}
|
||||
OPENDHT_PUBLIC Blob inline aesBuildEncrypted(std::string_view encryptedData, const Blob& salt) {
|
||||
return aesBuildEncrypted((const uint8_t*)encryptedData.data(), encryptedData.size(), salt);
|
||||
OPENDHT_PUBLIC Blob inline aesBuildEncrypted(std::string_view encryptedData, const Blob& salt)
|
||||
{
|
||||
return aesBuildEncrypted((const uint8_t*) encryptedData.data(), encryptedData.size(), salt);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Encrypts a payload for WebPush (RFC 8291).
|
||||
* @param p256dh: The user's P-256 public key (uncompressed, 65 bytes).
|
||||
* @param auth: The user's authentication secret (16 bytes).
|
||||
* @param payload: The data to encrypt.
|
||||
* @param privKey: (Optional) The sender's private key (for testing).
|
||||
* @param salt: (Optional) The salt to use (for testing).
|
||||
* @return The encrypted body (including salt, record size, keyid).
|
||||
*/
|
||||
OPENDHT_PUBLIC Blob webPushEncrypt(const Blob& p256dh,
|
||||
const Blob& auth,
|
||||
const uint8_t* payload,
|
||||
size_t payload_length,
|
||||
const PrivateKey& privKey = {},
|
||||
const Blob& salt = {});
|
||||
|
||||
} // namespace crypto
|
||||
} // namespace dht
|
||||
@@ -69,6 +69,8 @@ public:
|
||||
if (v.owner) {
|
||||
owner = v.owner;
|
||||
from = v.owner->getId();
|
||||
} else {
|
||||
throw std::runtime_error("Value has no owner");
|
||||
}
|
||||
BaseClass::unpackValue(v);
|
||||
}
|
||||
@@ -89,6 +91,9 @@ public:
|
||||
|
||||
public:
|
||||
virtual void unpackValue(const Value& v) override {
|
||||
if (!v.recipient) {
|
||||
throw std::runtime_error("Value has no recipient");
|
||||
}
|
||||
to = v.recipient;
|
||||
BaseClass::unpackValue(v);
|
||||
}
|
||||
@@ -103,9 +108,6 @@ public:
|
||||
dht::InfoHash to;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class OPENDHT_PUBLIC ImMessage : public SignedValue<ImMessage>
|
||||
{
|
||||
private:
|
||||
|
||||
@@ -124,7 +124,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct ServerStats {
|
||||
struct OPENDHT_PUBLIC ServerStats {
|
||||
/** Current number of listen operations */
|
||||
size_t listenCount {0};
|
||||
/** Current number of permanent put operations (hash used) */
|
||||
@@ -149,44 +149,12 @@ public:
|
||||
/** Node Info **/
|
||||
std::shared_ptr<NodeInfo> nodeInfo {};
|
||||
|
||||
std::string toString() const {
|
||||
auto ret = fmt::format("Listens: {}, Puts: {}, PushListeners: {}\n"
|
||||
"Push requests in the last {}: [Android: {}], [iOS: {}], [Unified: {}]\n"
|
||||
"Requests: {} per second.",
|
||||
listenCount, putCount, pushListenersCount,
|
||||
print_duration(lastUpdated - serverStartTime),
|
||||
androidPush.toString(), iosPush.toString(), unifiedPush.toString(),
|
||||
requestRate);
|
||||
if (nodeInfo) {
|
||||
auto& ipv4 = nodeInfo->ipv4;
|
||||
if (ipv4.table_depth > 1)
|
||||
ret += fmt::format("IPv4 Network estimation: {}\n", ipv4.getNetworkSizeEstimation());
|
||||
auto& ipv6 = nodeInfo->ipv6;
|
||||
if (ipv6.table_depth > 1)
|
||||
ret += fmt::format("IPv6 Network estimation: {}\n", ipv6.getNetworkSizeEstimation());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
std::string toString() const;
|
||||
|
||||
/**
|
||||
* Build a json object from a NodeStats
|
||||
*/
|
||||
Json::Value toJson() const {
|
||||
Json::Value result;
|
||||
result["listenCount"] = static_cast<Json::UInt64>(listenCount);
|
||||
result["putCount"] = static_cast<Json::UInt64>(putCount);
|
||||
result["totalPermanentPuts"] = static_cast<Json::UInt64>(totalPermanentPuts);
|
||||
result["pushListenersCount"] = static_cast<Json::UInt64>(pushListenersCount);
|
||||
result["serverStartTime"] = static_cast<Json::LargestInt>(to_time_t(serverStartTime));
|
||||
result["lastUpdated"] = static_cast<Json::LargestInt>(to_time_t(lastUpdated));
|
||||
result["androidPush"] = androidPush.toJson();
|
||||
result["iosPush"] = iosPush.toJson();
|
||||
result["unifiedPush"] = unifiedPush.toJson();
|
||||
result["requestRate"] = requestRate;
|
||||
if (nodeInfo)
|
||||
result["nodeInfo"] = nodeInfo->toJson();
|
||||
return result;
|
||||
}
|
||||
Json::Value toJson() const;
|
||||
};
|
||||
|
||||
std::shared_ptr<ServerStats> stats() const { return stats_; }
|
||||
|
||||
@@ -77,7 +77,8 @@ public:
|
||||
std::unique_ptr<net::DatagramSocket> sock;
|
||||
std::shared_ptr<PeerDiscovery> peerDiscovery {};
|
||||
StatusCallback statusChangedCallback {};
|
||||
CertificateStoreQuery certificateStore {};
|
||||
CertificateStoreQueryLegacy certificateStore {};
|
||||
CertificateStoreQuery certificateStorePkId {};
|
||||
IdentityAnnouncedCb identityAnnouncedCb {};
|
||||
PublicAddressChangedCb publicAddressChangedCb {};
|
||||
std::unique_ptr<std::mt19937_64> rng {};
|
||||
@@ -325,7 +326,7 @@ public:
|
||||
/**
|
||||
* Get the public key fingerprint if an identity is used with this node, 0 otherwise.
|
||||
*/
|
||||
InfoHash getId() const;
|
||||
[[deprecated("Use getPublicKey()->getLongId() instead")]] InfoHash getId() const;
|
||||
std::shared_ptr<crypto::PublicKey> getPublicKey() const;
|
||||
|
||||
/**
|
||||
@@ -365,6 +366,7 @@ public:
|
||||
void setLogFilter(const InfoHash& f = {});
|
||||
|
||||
void registerType(const ValueType& type);
|
||||
void registerInsecureType(const ValueType& type);
|
||||
|
||||
void importValues(const std::vector<ValuesExport>& values);
|
||||
|
||||
@@ -393,6 +395,7 @@ public:
|
||||
void findCertificate(PkId hash, std::function<void(const std::shared_ptr<crypto::Certificate>&)>);
|
||||
|
||||
void registerCertificate(const std::shared_ptr<crypto::Certificate>& cert);
|
||||
void setLocalCertificateStore(CertificateStoreQueryLegacy&& query_method);
|
||||
void setLocalCertificateStore(CertificateStoreQuery&& query_method);
|
||||
|
||||
/**
|
||||
@@ -438,9 +441,11 @@ public:
|
||||
void shutdown(ShutdownCallback cb = {}, bool stop = false);
|
||||
|
||||
/**
|
||||
* Quit and wait for all threads to terminate.
|
||||
* No callbacks will be called after this method returns.
|
||||
* All internal state will be lost. The DHT can then be run again with @run().
|
||||
* Stop the node and wait for all threads to terminate.
|
||||
* The node is reset to its default state. The DHT can then be run again with @run().
|
||||
* No DHT callbacks will run after this method returns.
|
||||
* Calling this method is optional. It allows to ensure the destructor won't block.
|
||||
* This method is unable to be called from a DHT callback.
|
||||
*/
|
||||
void join();
|
||||
|
||||
|
||||
+64
-58
@@ -25,8 +25,8 @@
|
||||
// some libraries may try to redefine snprintf
|
||||
// but restinio will use it in std namespace
|
||||
#ifdef _MSC_VER
|
||||
# undef snprintf
|
||||
# define snprintf snprintf
|
||||
#undef snprintf
|
||||
#define snprintf snprintf
|
||||
#endif
|
||||
|
||||
#include <asio/ip/tcp.hpp>
|
||||
@@ -47,7 +47,7 @@ namespace restinio {
|
||||
namespace impl {
|
||||
class tls_socket_t;
|
||||
}
|
||||
}
|
||||
} // namespace restinio
|
||||
|
||||
namespace dht {
|
||||
namespace log {
|
||||
@@ -62,8 +62,7 @@ namespace http {
|
||||
|
||||
using HandlerCb = std::function<void(const asio::error_code& ec)>;
|
||||
using BytesHandlerCb = std::function<void(const asio::error_code& ec, size_t bytes)>;
|
||||
using ConnectHandlerCb = std::function<void(const asio::error_code& ec,
|
||||
const asio::ip::tcp::endpoint& endpoint)>;
|
||||
using ConnectHandlerCb = std::function<void(const asio::error_code& ec, const asio::ip::tcp::endpoint& endpoint)>;
|
||||
|
||||
using ssl_socket_t = restinio::impl::tls_socket_t;
|
||||
using socket_t = asio::ip::tcp::socket;
|
||||
@@ -75,11 +74,13 @@ public:
|
||||
Url(std::string_view url);
|
||||
std::string url;
|
||||
std::string protocol {"http"};
|
||||
std::string host;
|
||||
std::string service;
|
||||
std::string_view user;
|
||||
std::string_view password;
|
||||
std::string_view host;
|
||||
std::string_view service;
|
||||
std::string target;
|
||||
std::string query;
|
||||
std::string fragment;
|
||||
std::string_view query;
|
||||
std::string_view fragment;
|
||||
|
||||
std::string toString() const;
|
||||
};
|
||||
@@ -88,11 +89,13 @@ class OPENDHT_PUBLIC Connection : public std::enable_shared_from_this<Connection
|
||||
{
|
||||
public:
|
||||
Connection(asio::io_context& ctx, const bool ssl = true, std::shared_ptr<log::Logger> l = {});
|
||||
Connection(asio::io_context& ctx, std::shared_ptr<dht::crypto::Certificate> server_ca,
|
||||
const dht::crypto::Identity& identity, std::shared_ptr<log::Logger> l = {});
|
||||
Connection(asio::io_context& ctx,
|
||||
std::shared_ptr<dht::crypto::Certificate> server_ca,
|
||||
const dht::crypto::Identity& identity,
|
||||
std::shared_ptr<log::Logger> l = {});
|
||||
~Connection();
|
||||
|
||||
inline unsigned int id() const { return id_; };
|
||||
inline unsigned int id() const { return id_; };
|
||||
bool is_open() const;
|
||||
bool is_ssl() const;
|
||||
void checkOcsp(bool check = true) { checkOcsp_ = check; }
|
||||
@@ -122,10 +125,10 @@ public:
|
||||
void close();
|
||||
|
||||
private:
|
||||
|
||||
template<typename T>
|
||||
T wrapCallback(T cb) const {
|
||||
return [t=shared_from_this(),cb=std::move(cb)](auto ...params) {
|
||||
T wrapCallback(T cb) const
|
||||
{
|
||||
return [t = shared_from_this(), cb = std::move(cb)](auto... params) {
|
||||
cb(params...);
|
||||
};
|
||||
}
|
||||
@@ -159,35 +162,38 @@ private:
|
||||
class OPENDHT_PUBLIC Resolver
|
||||
{
|
||||
public:
|
||||
using ResolverCb = std::function<void(const asio::error_code& ec,
|
||||
const std::vector<asio::ip::tcp::endpoint>& endpoints)>;
|
||||
using ResolverCb
|
||||
= std::function<void(const asio::error_code& ec, const std::vector<asio::ip::tcp::endpoint>& endpoints)>;
|
||||
|
||||
Resolver(asio::io_context& ctx, const std::string& url, std::shared_ptr<log::Logger> logger = {});
|
||||
Resolver(asio::io_context& ctx, const std::string& host, const std::string& service,
|
||||
const bool ssl = false, std::shared_ptr<log::Logger> logger = {});
|
||||
Resolver(asio::io_context& ctx,
|
||||
std::string_view host,
|
||||
std::string_view service,
|
||||
const bool ssl = false,
|
||||
std::shared_ptr<log::Logger> logger = {});
|
||||
|
||||
// use already resolved endpoints with classes using this resolver
|
||||
Resolver(asio::io_context& ctx, std::vector<asio::ip::tcp::endpoint> endpoints,
|
||||
const bool ssl = false, std::shared_ptr<log::Logger> logger = {});
|
||||
Resolver(asio::io_context& ctx, const std::string& url, std::vector<asio::ip::tcp::endpoint> endpoints,
|
||||
std::shared_ptr<log::Logger> logger = {});
|
||||
Resolver(asio::io_context& ctx,
|
||||
std::vector<asio::ip::tcp::endpoint> endpoints,
|
||||
const bool ssl = false,
|
||||
std::shared_ptr<log::Logger> logger = {});
|
||||
Resolver(asio::io_context& ctx,
|
||||
const std::string& url,
|
||||
std::vector<asio::ip::tcp::endpoint> endpoints,
|
||||
std::shared_ptr<log::Logger> logger = {});
|
||||
|
||||
~Resolver();
|
||||
|
||||
inline const Url& get_url() const {
|
||||
return url_;
|
||||
}
|
||||
inline const Url& get_url() const { return url_; }
|
||||
|
||||
void add_callback(ResolverCb cb, sa_family_t family = AF_UNSPEC);
|
||||
|
||||
std::shared_ptr<log::Logger> getLogger() const {
|
||||
return logger_;
|
||||
}
|
||||
std::shared_ptr<log::Logger> getLogger() const { return logger_; }
|
||||
|
||||
void cancel();
|
||||
|
||||
private:
|
||||
void resolve(const std::string& host, const std::string& service);
|
||||
void resolve(std::string_view host, std::string_view service);
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
@@ -217,13 +223,7 @@ struct Response
|
||||
class OPENDHT_PUBLIC Request : public std::enable_shared_from_this<Request>
|
||||
{
|
||||
public:
|
||||
enum class State {
|
||||
CREATED,
|
||||
SENDING,
|
||||
HEADER_RECEIVED,
|
||||
RECEIVING,
|
||||
DONE
|
||||
};
|
||||
enum class State { CREATED, SENDING, HEADER_RECEIVED, RECEIVING, DONE };
|
||||
using OnStatusCb = std::function<void(unsigned status_code)>;
|
||||
using OnDataCb = std::function<void(const char* at, size_t length)>;
|
||||
using OnStateChangeCb = std::function<void(State state, const Response& response)>;
|
||||
@@ -231,46 +231,51 @@ public:
|
||||
using OnDoneCb = std::function<void(const Response& response)>;
|
||||
|
||||
// resolves implicitly
|
||||
Request(asio::io_context& ctx, const std::string& url, const Json::Value& json, OnJsonCb jsoncb,
|
||||
std::shared_ptr<log::Logger> logger = {});
|
||||
Request(asio::io_context& ctx, const std::string& url, OnJsonCb jsoncb,
|
||||
Request(asio::io_context& ctx,
|
||||
const std::string& url,
|
||||
const Json::Value& json,
|
||||
OnJsonCb jsoncb,
|
||||
std::shared_ptr<log::Logger> logger = {});
|
||||
Request(asio::io_context& ctx, const std::string& url, OnJsonCb jsoncb, std::shared_ptr<log::Logger> logger = {});
|
||||
|
||||
Request(asio::io_context& ctx, const std::string& url, std::shared_ptr<log::Logger> logger = {});
|
||||
Request(asio::io_context& ctx, const std::string& host, const std::string& service,
|
||||
const bool ssl = false, std::shared_ptr<log::Logger> logger = {});
|
||||
Request(asio::io_context& ctx,
|
||||
std::string_view host,
|
||||
std::string_view service,
|
||||
const bool ssl = false,
|
||||
std::shared_ptr<log::Logger> logger = {});
|
||||
Request(asio::io_context& ctx, const std::string& url, OnDoneCb onDone, std::shared_ptr<log::Logger> logger = {});
|
||||
|
||||
// user defined resolver
|
||||
Request(asio::io_context& ctx, std::shared_ptr<Resolver> resolver, sa_family_t family = AF_UNSPEC);
|
||||
Request(asio::io_context& ctx, std::shared_ptr<Resolver> resolver, const std::string& target, sa_family_t family = AF_UNSPEC);
|
||||
Request(asio::io_context& ctx,
|
||||
std::shared_ptr<Resolver> resolver,
|
||||
const std::string& target,
|
||||
sa_family_t family = AF_UNSPEC);
|
||||
|
||||
// user defined resolved endpoints
|
||||
Request(asio::io_context& ctx, std::vector<asio::ip::tcp::endpoint>&& endpoints,
|
||||
const bool ssl = false, std::shared_ptr<log::Logger> logger = {});
|
||||
Request(asio::io_context& ctx,
|
||||
std::vector<asio::ip::tcp::endpoint>&& endpoints,
|
||||
const bool ssl = false,
|
||||
std::shared_ptr<log::Logger> logger = {});
|
||||
|
||||
~Request();
|
||||
|
||||
inline unsigned int id() const { return id_; };
|
||||
inline unsigned int id() const { return id_; };
|
||||
void set_connection(std::shared_ptr<Connection> connection);
|
||||
std::shared_ptr<Connection> get_connection() const;
|
||||
inline const Url& get_url() const {
|
||||
return resolver_->get_url();
|
||||
};
|
||||
inline const Url& get_url() const { return resolver_->get_url(); };
|
||||
|
||||
void timeout(const std::chrono::seconds& timeout, HandlerCb cb = {}) {
|
||||
void timeout(const std::chrono::seconds& timeout, HandlerCb cb = {})
|
||||
{
|
||||
timeout_ = timeout;
|
||||
timeoutCb_ = cb;
|
||||
}
|
||||
|
||||
/** The previous request in case of redirect following */
|
||||
std::shared_ptr<Request> getPrevious() const {
|
||||
return prev_.lock();
|
||||
}
|
||||
std::shared_ptr<Request> getPrevious() const { return prev_.lock(); }
|
||||
|
||||
inline std::string& to_string() {
|
||||
return request_;
|
||||
}
|
||||
inline std::string& to_string() { return request_; }
|
||||
|
||||
void set_certificate_authority(std::shared_ptr<dht::crypto::Certificate> certificate);
|
||||
void set_identity(const dht::crypto::Identity& identity);
|
||||
@@ -285,6 +290,7 @@ public:
|
||||
void set_header_field(restinio::http_field_t field, std::string value);
|
||||
void set_connection_type(restinio::http_connection_header_t connection);
|
||||
void set_body(std::string body);
|
||||
void set_body(const uint8_t* data, size_t length);
|
||||
void set_auth(const std::string& username, const std::string& password);
|
||||
|
||||
void add_on_status_callback(OnStatusCb cb);
|
||||
@@ -306,7 +312,8 @@ public:
|
||||
private:
|
||||
using OnCompleteCb = std::function<void()>;
|
||||
|
||||
struct Callbacks {
|
||||
struct Callbacks
|
||||
{
|
||||
OnStatusCb on_status;
|
||||
OnDataCb on_header_field;
|
||||
OnDataCb on_header_value;
|
||||
@@ -381,4 +388,3 @@ private:
|
||||
|
||||
} // namespace http
|
||||
} // namespace dht
|
||||
|
||||
@@ -266,7 +266,7 @@ private:
|
||||
|
||||
using Value = std::pair<InfoHash, dht::Value::Id>;
|
||||
struct OPENDHT_PUBLIC IndexEntry : public dht::Value::Serializable<IndexEntry> {
|
||||
static const ValueType TYPE;
|
||||
static const ValueType& TYPE;
|
||||
|
||||
virtual void unpackValue(const dht::Value& v) {
|
||||
Serializable<IndexEntry>::unpackValue(v);
|
||||
|
||||
+59
-41
@@ -30,89 +30,107 @@
|
||||
namespace dht {
|
||||
namespace log {
|
||||
|
||||
enum class LogLevel {
|
||||
debug, warning, error
|
||||
};
|
||||
enum class LogLevel { debug, warning, error };
|
||||
|
||||
using LogMethod = std::function<void(LogLevel, std::string&&)>;
|
||||
|
||||
struct OPENDHT_PUBLIC Logger {
|
||||
struct OPENDHT_PUBLIC Logger
|
||||
{
|
||||
Logger() = delete;
|
||||
Logger(LogMethod&& l)
|
||||
: logger(std::move(l)) {
|
||||
if (!logger)
|
||||
throw std::invalid_argument{"logger and loggerf must be set"};
|
||||
}
|
||||
void setFilter(const InfoHash& f) {
|
||||
: logger(std::move(l))
|
||||
{
|
||||
if (!logger)
|
||||
throw std::invalid_argument {"logger and loggerf must be set"};
|
||||
}
|
||||
void setFilter(const InfoHash& f)
|
||||
{
|
||||
filter_ = f;
|
||||
filterEnable_ = static_cast<bool>(filter_);
|
||||
}
|
||||
inline void log0(LogLevel level, fmt::string_view format, fmt::printf_args args) const {
|
||||
inline void log0(LogLevel level, fmt::string_view format, fmt::printf_args args) const
|
||||
{
|
||||
if (not filterEnable_)
|
||||
logger(level, fmt::vsprintf(format, args));
|
||||
}
|
||||
inline void log1(LogLevel level, const InfoHash& f, fmt::string_view format, fmt::printf_args args) const {
|
||||
inline void log1(LogLevel level, const InfoHash& f, fmt::string_view format, fmt::printf_args args) const
|
||||
{
|
||||
if (not filterEnable_ or f == filter_)
|
||||
logger(level, fmt::vsprintf(format, args));
|
||||
}
|
||||
inline void log2(LogLevel level, const InfoHash& f1, const InfoHash& f2, fmt::string_view format, fmt::printf_args args) const {
|
||||
inline void log2(
|
||||
LogLevel level, const InfoHash& f1, const InfoHash& f2, fmt::string_view format, fmt::printf_args args) const
|
||||
{
|
||||
if (not filterEnable_ or f1 == filter_ or f2 == filter_)
|
||||
logger(level, fmt::vsprintf(format, args));
|
||||
}
|
||||
template<typename S, typename... Args>
|
||||
inline void debug(S&& format, Args&&... args) const {
|
||||
logger(LogLevel::debug, fmt::format(format, args...));
|
||||
template<typename... Args>
|
||||
inline void debug(fmt::format_string<Args...> format, Args&&... args) const
|
||||
{
|
||||
logger(LogLevel::debug, fmt::format(format, std::forward<Args>(args)...));
|
||||
}
|
||||
template<typename S, typename... Args>
|
||||
inline void warn(S&& format, Args&&... args) const {
|
||||
logger(LogLevel::warning, fmt::format(format, args...));
|
||||
template<typename... Args>
|
||||
inline void warn(fmt::format_string<Args...> format, Args&&... args) const
|
||||
{
|
||||
logger(LogLevel::warning, fmt::format(format, std::forward<Args>(args)...));
|
||||
}
|
||||
template<typename S, typename... Args>
|
||||
inline void error(S&& format, Args&&... args) const {
|
||||
logger(LogLevel::error, fmt::format(format, args...));
|
||||
template<typename... Args>
|
||||
inline void error(fmt::format_string<Args...> format, Args&&... args) const
|
||||
{
|
||||
logger(LogLevel::error, fmt::format(format, std::forward<Args>(args)...));
|
||||
}
|
||||
template <typename... T>
|
||||
inline void d(fmt::format_string<T...> format, T&&... args) const {
|
||||
template<typename... T>
|
||||
inline void d(fmt::format_string<T...> format, T&&... args) const
|
||||
{
|
||||
log0(LogLevel::debug, format, fmt::make_printf_args(args...));
|
||||
}
|
||||
template <typename... T>
|
||||
inline void d(const InfoHash& f, fmt::format_string<T...> format, T&&... args) const {
|
||||
template<typename... T>
|
||||
inline void d(const InfoHash& f, fmt::format_string<T...> format, T&&... args) const
|
||||
{
|
||||
log1(LogLevel::debug, f, format, fmt::make_printf_args(args...));
|
||||
}
|
||||
template <typename... T>
|
||||
inline void d(const InfoHash& f1, const InfoHash& f2, fmt::format_string<T...> format, T&&... args) const {
|
||||
template<typename... T>
|
||||
inline void d(const InfoHash& f1, const InfoHash& f2, fmt::format_string<T...> format, T&&... args) const
|
||||
{
|
||||
log2(LogLevel::debug, f1, f2, format, fmt::make_printf_args(args...));
|
||||
}
|
||||
template <typename... T>
|
||||
inline void w(fmt::format_string<T...> format, T&&... args) const {
|
||||
template<typename... T>
|
||||
inline void w(fmt::format_string<T...> format, T&&... args) const
|
||||
{
|
||||
log0(LogLevel::warning, format, fmt::make_printf_args(args...));
|
||||
}
|
||||
template <typename... T>
|
||||
inline void w(const InfoHash& f, fmt::format_string<T...> format, T&&... args) const {
|
||||
template<typename... T>
|
||||
inline void w(const InfoHash& f, fmt::format_string<T...> format, T&&... args) const
|
||||
{
|
||||
log1(LogLevel::warning, f, format, fmt::make_printf_args(args...));
|
||||
}
|
||||
template <typename... T>
|
||||
inline void w(const InfoHash& f1, const InfoHash& f2, fmt::format_string<T...> format, T&&... args) const {
|
||||
template<typename... T>
|
||||
inline void w(const InfoHash& f1, const InfoHash& f2, fmt::format_string<T...> format, T&&... args) const
|
||||
{
|
||||
log2(LogLevel::warning, f1, f2, format, fmt::make_printf_args(args...));
|
||||
}
|
||||
template <typename... T>
|
||||
inline void e(fmt::format_string<T...> format, T&&... args) const {
|
||||
template<typename... T>
|
||||
inline void e(fmt::format_string<T...> format, T&&... args) const
|
||||
{
|
||||
log0(LogLevel::error, format, fmt::make_printf_args(args...));
|
||||
}
|
||||
template <typename... T>
|
||||
inline void e(const InfoHash& f, fmt::format_string<T...> format, T&&... args) const {
|
||||
template<typename... T>
|
||||
inline void e(const InfoHash& f, fmt::format_string<T...> format, T&&... args) const
|
||||
{
|
||||
log1(LogLevel::error, f, format, fmt::make_printf_args(args...));
|
||||
}
|
||||
template <typename... T>
|
||||
inline void e(const InfoHash& f1, const InfoHash& f2, fmt::format_string<T...> format, T&&... args) const {
|
||||
template<typename... T>
|
||||
inline void e(const InfoHash& f1, const InfoHash& f2, fmt::format_string<T...> format, T&&... args) const
|
||||
{
|
||||
log2(LogLevel::error, f1, f2, format, fmt::make_printf_args(args...));
|
||||
}
|
||||
|
||||
private:
|
||||
LogMethod logger = {};
|
||||
bool filterEnable_ {false};
|
||||
InfoHash filter_ {};
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace log
|
||||
using Logger = log::Logger;
|
||||
}
|
||||
} // namespace dht
|
||||
@@ -59,18 +59,18 @@ class DhtProtocolException : public DhtException {
|
||||
public:
|
||||
// sent to another peer (http-like).
|
||||
static const constexpr uint16_t NON_AUTHORITATIVE_INFORMATION {203}; /* incomplete request packet. */
|
||||
static const constexpr uint16_t UNAUTHORIZED {401}; /* wrong tokens. */
|
||||
static const constexpr uint16_t UNAUTHORIZED {401}; /* incorrect tokens. */
|
||||
static const constexpr uint16_t NOT_FOUND {404}; /* storage not found */
|
||||
// for internal use (custom).
|
||||
static const constexpr uint16_t INVALID_TID_SIZE {421}; /* id was truncated. */
|
||||
static const constexpr uint16_t UNKNOWN_TID {422}; /* unknown tid */
|
||||
static const constexpr uint16_t WRONG_NODE_INFO_BUF_LEN {423}; /* node info length is wrong */
|
||||
static const constexpr uint16_t WRONG_NODE_INFO_BUF_LEN {423}; /* node info length is incorrect */
|
||||
|
||||
static const std::string GET_NO_INFOHASH; /* received "get" request with no infohash */
|
||||
static const std::string LISTEN_NO_INFOHASH; /* got "listen" request without infohash */
|
||||
static const std::string LISTEN_WRONG_TOKEN; /* wrong token in "listen" request */
|
||||
static const std::string LISTEN_WRONG_TOKEN; /* incorrect token in "listen" request */
|
||||
static const std::string PUT_NO_INFOHASH; /* no infohash in "put" request */
|
||||
static const std::string PUT_WRONG_TOKEN; /* got "put" request with wrong token */
|
||||
static const std::string PUT_WRONG_TOKEN; /* got "put" request with incorrect token */
|
||||
static const std::string STORAGE_NOT_FOUND; /* got access request for an unknown storage */
|
||||
static const std::string PUT_INVALID_ID; /* invalid id in "put" request */
|
||||
|
||||
@@ -345,11 +345,11 @@ public:
|
||||
* @param previous The previous request "listen" sent to this node.
|
||||
* @param socket **UNUSED** The socket for further response.
|
||||
*
|
||||
* For backward compatibility purpose, sendListen has to
|
||||
* handle creation of the socket. Therefor, you cannot
|
||||
* use openSocket yourself. TODO: Once we don't support
|
||||
* the old "listen" negociation, sendListen shall not
|
||||
* create the socket itself.
|
||||
* For backward compatibility purposes, sendListen has to
|
||||
* handle creation of the socket. Therefore, you are unable to
|
||||
* use openSocket yourself.
|
||||
* TODO: Once we don't support the old "listen" negotiation,
|
||||
* sendListen shall not create the socket itself.
|
||||
*
|
||||
* @param on_done Request callback when the request is completed.
|
||||
* @param on_expired Request callback when the request expires.
|
||||
|
||||
@@ -52,14 +52,16 @@ bool setNonblocking(int fd, bool nonblocking = true);
|
||||
#ifdef _WIN32
|
||||
void udpPipe(int fds[2]);
|
||||
#endif
|
||||
struct ReceivedPacket {
|
||||
struct ReceivedPacket
|
||||
{
|
||||
Blob data;
|
||||
SockAddr from;
|
||||
time_point received;
|
||||
};
|
||||
using PacketList = std::list<ReceivedPacket>;
|
||||
|
||||
class OPENDHT_PUBLIC DatagramSocket {
|
||||
class OPENDHT_PUBLIC DatagramSocket
|
||||
{
|
||||
public:
|
||||
/** A function that takes a list of new received packets and
|
||||
* optionally returns consumed packets for recycling.
|
||||
@@ -69,7 +71,8 @@ public:
|
||||
|
||||
virtual int sendTo(const SockAddr& dest, const uint8_t* data, size_t size, bool replied) = 0;
|
||||
|
||||
inline void setOnReceive(OnReceive&& cb) {
|
||||
inline void setOnReceive(OnReceive&& cb)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
rx_callback = std::move(cb);
|
||||
}
|
||||
@@ -77,11 +80,13 @@ public:
|
||||
virtual bool hasIPv4() const = 0;
|
||||
virtual bool hasIPv6() const = 0;
|
||||
|
||||
SockAddr getBound(sa_family_t family = AF_UNSPEC) const {
|
||||
SockAddr getBound(sa_family_t family = AF_UNSPEC) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
return getBoundRef(family);
|
||||
}
|
||||
in_port_t getPort(sa_family_t family = AF_UNSPEC) const {
|
||||
in_port_t getPort(sa_family_t family = AF_UNSPEC) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
return getBoundRef(family).getPort();
|
||||
}
|
||||
@@ -89,14 +94,16 @@ public:
|
||||
virtual const SockAddr& getBoundRef(sa_family_t family = AF_UNSPEC) const = 0;
|
||||
|
||||
/** Virtual resolver mothod allows to implement custom resolver */
|
||||
virtual std::vector<SockAddr> resolve(const std::string& host, const std::string& service = {}) {
|
||||
virtual std::vector<SockAddr> resolve(const std::string& host, const std::string& service = {})
|
||||
{
|
||||
return SockAddr::resolve(host, service);
|
||||
}
|
||||
|
||||
virtual void stop() = 0;
|
||||
protected:
|
||||
|
||||
PacketList getNewPacket() {
|
||||
protected:
|
||||
PacketList getNewPacket()
|
||||
{
|
||||
PacketList pkts;
|
||||
if (toRecycle_.empty()) {
|
||||
pkts.emplace_back();
|
||||
@@ -108,7 +115,8 @@ protected:
|
||||
return pkts;
|
||||
}
|
||||
|
||||
inline void onReceived(PacketList&& packets) {
|
||||
inline void onReceived(PacketList&& packets)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
if (rx_callback) {
|
||||
auto r = rx_callback(std::move(packets));
|
||||
@@ -116,14 +124,17 @@ protected:
|
||||
toRecycle_.splice(toRecycle_.end(), std::move(r));
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
mutable std::mutex lock;
|
||||
|
||||
private:
|
||||
OnReceive rx_callback;
|
||||
PacketList toRecycle_;
|
||||
};
|
||||
|
||||
class OPENDHT_PUBLIC UdpSocket : public DatagramSocket {
|
||||
class OPENDHT_PUBLIC UdpSocket : public DatagramSocket
|
||||
{
|
||||
public:
|
||||
UdpSocket(in_port_t port, const std::shared_ptr<Logger>& l = {});
|
||||
UdpSocket(const SockAddr& bind4, const SockAddr& bind6, const std::shared_ptr<Logger>& l = {});
|
||||
@@ -131,20 +142,24 @@ public:
|
||||
|
||||
int sendTo(const SockAddr& dest, const uint8_t* data, size_t size, bool replied) override;
|
||||
|
||||
const SockAddr& getBoundRef(sa_family_t family = AF_UNSPEC) const override {
|
||||
const SockAddr& getBoundRef(sa_family_t family = AF_UNSPEC) const override
|
||||
{
|
||||
return (family == AF_INET6) ? bound6 : bound4;
|
||||
}
|
||||
|
||||
bool hasIPv4() const override {
|
||||
bool hasIPv4() const override
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
return s4 != -1;
|
||||
}
|
||||
bool hasIPv6() const override {
|
||||
bool hasIPv6() const override
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
return s6 != -1;
|
||||
}
|
||||
|
||||
void stop() override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Logger> logger;
|
||||
int s4 {-1};
|
||||
@@ -157,5 +172,5 @@ private:
|
||||
void openSockets(const SockAddr& bind4, const SockAddr& bind6);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
} // namespace net
|
||||
} // namespace dht
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
|
||||
/**
|
||||
* "Secure" get(), that will check the signature of signed data, and decrypt encrypted data.
|
||||
* If the signature can't be checked, or if the data can't be decrypted, it is not returned.
|
||||
* If the signature is unable to be checked, or if the data is unable to be decrypted, it is not returned.
|
||||
* Public, non-signed & non-encrypted data is retransmitted as-is.
|
||||
*/
|
||||
void get(const InfoHash& id, GetCallback cb, DoneCallback donecb={}, Value::Filter&& = {}, Where&& w = {}) override;
|
||||
@@ -157,6 +157,9 @@ public:
|
||||
* The search key used is the public key ID, so there may be multiple certificates retured, signed with
|
||||
* the same private key.
|
||||
*/
|
||||
void setLocalCertificateStore(CertificateStoreQueryLegacy&& query_method) {
|
||||
localQueryMethodLegacy_ = std::move(query_method);
|
||||
}
|
||||
void setLocalCertificateStore(CertificateStoreQuery&& query_method) {
|
||||
localQueryMethod_ = std::move(query_method);
|
||||
}
|
||||
@@ -379,6 +382,7 @@ private:
|
||||
|
||||
// method to query the local certificate store
|
||||
CertificateStoreQuery localQueryMethod_ {};
|
||||
CertificateStoreQueryLegacy localQueryMethodLegacy_ {};
|
||||
|
||||
// our certificate cache
|
||||
std::map<InfoHash, Sp<crypto::Certificate>> nodesCertificates_ {};
|
||||
|
||||
+79
-40
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "def.h"
|
||||
|
||||
#include <type_traits>
|
||||
#include <msgpack.hpp>
|
||||
#include <fmt/format.h>
|
||||
|
||||
@@ -27,6 +28,7 @@
|
||||
#include <random>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string_view>
|
||||
|
||||
#include <cstdarg>
|
||||
|
||||
@@ -47,32 +49,49 @@ OPENDHT_PUBLIC const char* version();
|
||||
template<class T>
|
||||
using Sp = std::shared_ptr<T>;
|
||||
|
||||
template <typename Key, typename Item, typename Condition>
|
||||
void erase_if(std::map<Key, Item>& map, const Condition& condition)
|
||||
template<typename Key, typename Item, typename Condition>
|
||||
void
|
||||
erase_if(std::map<Key, Item>& map, const Condition& condition)
|
||||
{
|
||||
for (auto it = map.begin(); it != map.end(); ) {
|
||||
for (auto it = map.begin(); it != map.end();) {
|
||||
if (condition(*it)) {
|
||||
it = map.erase(it);
|
||||
} else { ++it; }
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
std::string
|
||||
concat(Args&&... args)
|
||||
{
|
||||
static_assert((std::is_constructible_v<std::string_view, Args&&> && ...));
|
||||
std::string s;
|
||||
s.reserve((std::string_view {args}.size() + ...));
|
||||
(s.append(std::forward<Args>(args)), ...);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split "[host]:port" or "host:port" to pair<"host", "port">.
|
||||
*/
|
||||
OPENDHT_PUBLIC std::pair<std::string, std::string>
|
||||
splitPort(std::string_view s);
|
||||
OPENDHT_PUBLIC std::pair<std::string_view, std::string_view> splitPort(std::string_view s);
|
||||
|
||||
class OPENDHT_PUBLIC DhtException : public std::runtime_error {
|
||||
class OPENDHT_PUBLIC DhtException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
DhtException(const std::string &str = "") :
|
||||
std::runtime_error("DhtException occurred: " + str) {}
|
||||
DhtException(const std::string& str = "")
|
||||
: std::runtime_error("DhtException occurred: " + str)
|
||||
{}
|
||||
};
|
||||
|
||||
class OPENDHT_PUBLIC SocketException : public DhtException {
|
||||
class OPENDHT_PUBLIC SocketException : public DhtException
|
||||
{
|
||||
public:
|
||||
SocketException(int err) :
|
||||
DhtException(strerror(err)) {}
|
||||
SocketException(int err)
|
||||
: DhtException(strerror(err))
|
||||
{}
|
||||
};
|
||||
|
||||
// Time related definitions and utility functions
|
||||
@@ -85,47 +104,61 @@ using duration = clock::duration;
|
||||
time_point from_time_t(std::time_t t);
|
||||
std::time_t to_time_t(time_point t);
|
||||
|
||||
template <class DT>
|
||||
template<class DT>
|
||||
static std::string
|
||||
print_duration(DT d) {
|
||||
print_duration(DT d)
|
||||
{
|
||||
if (d < std::chrono::seconds(0)) {
|
||||
return "-" + print_duration(-d);
|
||||
} else if (d < std::chrono::milliseconds(1)) {
|
||||
return fmt::format("{:.3g} us", std::chrono::duration_cast<std::chrono::duration<double, std::micro>>(d).count());
|
||||
return fmt::format("{:.3g} us",
|
||||
std::chrono::duration_cast<std::chrono::duration<double, std::micro>>(d).count());
|
||||
} else if (d < std::chrono::seconds(1)) {
|
||||
return fmt::format("{:.3g} ms", std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(d).count());
|
||||
return fmt::format("{:.3g} ms",
|
||||
std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(d).count());
|
||||
} else if (d < std::chrono::minutes(1)) {
|
||||
return fmt::format("{:.3g} s", std::chrono::duration_cast<std::chrono::duration<double>>(d).count());
|
||||
} else if (d < std::chrono::hours(1)) {
|
||||
return fmt::format("{:.3g} min", std::chrono::duration_cast<std::chrono::duration<double, std::ratio<60>>>(d).count());
|
||||
return fmt::format("{:.3g} min",
|
||||
std::chrono::duration_cast<std::chrono::duration<double, std::ratio<60>>>(d).count());
|
||||
} else if (d < std::chrono::hours(72)) {
|
||||
return fmt::format("{:.3g} h", std::chrono::duration_cast<std::chrono::duration<double, std::ratio<3600>>>(d).count());
|
||||
return fmt::format("{:.3g} h",
|
||||
std::chrono::duration_cast<std::chrono::duration<double, std::ratio<3600>>>(d).count());
|
||||
} else {
|
||||
return fmt::format("{:.3g} days", std::chrono::duration_cast<std::chrono::duration<double, std::ratio<86400>>>(d).count());
|
||||
return fmt::format("{:.3g} days",
|
||||
std::chrono::duration_cast<std::chrono::duration<double, std::ratio<86400>>>(d).count());
|
||||
}
|
||||
}
|
||||
|
||||
template <class TimePoint>
|
||||
template<class TimePoint>
|
||||
static std::string
|
||||
print_time_relative(TimePoint now, TimePoint d) {
|
||||
if (d == TimePoint::min()) return "never";
|
||||
if (d == now) return "now";
|
||||
return (d > now) ? std::string("in ") + print_duration(d - now)
|
||||
: print_duration(now - d) + std::string(" ago");
|
||||
print_time_relative(TimePoint now, TimePoint d)
|
||||
{
|
||||
if (d == TimePoint::min())
|
||||
return "never";
|
||||
if (d == now)
|
||||
return "now";
|
||||
return (d > now) ? std::string("in ") + print_duration(d - now) : print_duration(now - d) + std::string(" ago");
|
||||
}
|
||||
|
||||
template <typename Duration = duration>
|
||||
class uniform_duration_distribution : public std::uniform_int_distribution<typename Duration::rep> {
|
||||
template<typename Duration = duration>
|
||||
class uniform_duration_distribution : public std::uniform_int_distribution<typename Duration::rep>
|
||||
{
|
||||
using Base = std::uniform_int_distribution<typename Duration::rep>;
|
||||
using param_type = typename Base::param_type;
|
||||
|
||||
public:
|
||||
uniform_duration_distribution(Duration min, Duration max) : Base(min.count(), max.count()) {}
|
||||
template <class Generator>
|
||||
Duration operator()(Generator && g) {
|
||||
uniform_duration_distribution(Duration min, Duration max)
|
||||
: Base(min.count(), max.count())
|
||||
{}
|
||||
template<class Generator>
|
||||
Duration operator()(Generator&& g)
|
||||
{
|
||||
return Duration(Base::operator()(g));
|
||||
}
|
||||
template< class Generator >
|
||||
Duration operator()( Generator && g, const param_type& params ) {
|
||||
template<class Generator>
|
||||
Duration operator()(Generator&& g, const param_type& params)
|
||||
{
|
||||
return Duration(Base::operator()(g, params));
|
||||
}
|
||||
};
|
||||
@@ -142,19 +175,21 @@ using Blob = std::vector<uint8_t>;
|
||||
*/
|
||||
OPENDHT_PUBLIC Blob unpackBlob(const msgpack::object& o);
|
||||
|
||||
template <typename Type>
|
||||
template<typename Type>
|
||||
Blob
|
||||
packMsg(const Type& t) {
|
||||
packMsg(const Type& t)
|
||||
{
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack(t);
|
||||
return {buffer.data(), buffer.data()+buffer.size()};
|
||||
return {buffer.data(), buffer.data() + buffer.size()};
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
template<typename Type>
|
||||
Type
|
||||
unpackMsg(Blob b) {
|
||||
msgpack::unpacked msg_res = msgpack::unpack((const char*)b.data(), b.size());
|
||||
unpackMsg(Blob b)
|
||||
{
|
||||
msgpack::unpacked msg_res = msgpack::unpack((const char*) b.data(), b.size());
|
||||
return msg_res.get().as<Type>();
|
||||
}
|
||||
|
||||
@@ -162,10 +197,14 @@ msgpack::unpacked unpackMsg(Blob b);
|
||||
|
||||
msgpack::object* findMapValue(const msgpack::object& map, const char* key, size_t length);
|
||||
|
||||
inline msgpack::object* findMapValue(const msgpack::object& map, const char* key) {
|
||||
inline msgpack::object*
|
||||
findMapValue(const msgpack::object& map, const char* key)
|
||||
{
|
||||
return findMapValue(map, key, strlen(key));
|
||||
}
|
||||
inline msgpack::object* findMapValue(const msgpack::object& map, std::string_view key) {
|
||||
inline msgpack::object*
|
||||
findMapValue(const msgpack::object& map, std::string_view key)
|
||||
{
|
||||
return findMapValue(map, key.data(), key.size());
|
||||
}
|
||||
|
||||
|
||||
@@ -90,18 +90,19 @@ using StorePolicy = std::function<bool(InfoHash key, std::shared_ptr<Value>& val
|
||||
using EditPolicy = std::function<bool(InfoHash key, const std::shared_ptr<Value>& old_val, std::shared_ptr<Value>& new_val, const InfoHash& from, const SockAddr& addr)>;
|
||||
|
||||
static constexpr const size_t MAX_VALUE_SIZE {1024 * 64};
|
||||
static constexpr const duration DEFAULT_VALUE_EXPIRATION {std::chrono::minutes(10)};
|
||||
|
||||
struct ValueType {
|
||||
typedef uint16_t Id;
|
||||
|
||||
static bool DEFAULT_STORE_POLICY(InfoHash, const std::shared_ptr<Value>& v, const InfoHash&, const SockAddr&);
|
||||
static bool DEFAULT_EDIT_POLICY(InfoHash, const std::shared_ptr<Value>&, std::shared_ptr<Value>&, const InfoHash&, const SockAddr&) {
|
||||
OPENDHT_PUBLIC static bool DEFAULT_STORE_POLICY(InfoHash, const std::shared_ptr<Value>& v, const InfoHash&, const SockAddr&);
|
||||
static inline bool DEFAULT_EDIT_POLICY(InfoHash, const std::shared_ptr<Value>&, std::shared_ptr<Value>&, const InfoHash&, const SockAddr&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ValueType () {}
|
||||
|
||||
ValueType (Id id, std::string name, duration e = std::chrono::minutes(10))
|
||||
ValueType (Id id, std::string name, duration e = DEFAULT_VALUE_EXPIRATION)
|
||||
: id(id), name(name), expiration(e) {}
|
||||
|
||||
ValueType (Id id, std::string name, duration e, StorePolicy sp, EditPolicy ep = DEFAULT_EDIT_POLICY)
|
||||
@@ -119,7 +120,7 @@ struct ValueType {
|
||||
|
||||
Id id {0};
|
||||
std::string name {};
|
||||
duration expiration {std::chrono::minutes(10)};
|
||||
duration expiration {DEFAULT_VALUE_EXPIRATION};
|
||||
StorePolicy storePolicy {DEFAULT_STORE_POLICY};
|
||||
EditPolicy editPolicy {DEFAULT_EDIT_POLICY};
|
||||
};
|
||||
|
||||
+200
-119
@@ -1,29 +1,56 @@
|
||||
project('opendht', 'c', 'cpp', 'cython',
|
||||
version: '3.4.0',
|
||||
default_options: [
|
||||
'cpp_std=c++17',
|
||||
'warning_level=3'
|
||||
])
|
||||
project(
|
||||
'opendht',
|
||||
'c',
|
||||
'cpp',
|
||||
'cython',
|
||||
version: '3.6.2',
|
||||
meson_version: '>=1.1.0',
|
||||
default_options: ['cpp_std=c++17', 'warning_level=3'],
|
||||
)
|
||||
|
||||
gnutls = dependency('gnutls')
|
||||
nettle = dependency('nettle')
|
||||
msgpack = dependency('msgpack-cxx', required : false)
|
||||
msgpack = dependency('msgpack-cxx', required: false)
|
||||
argon2 = dependency('libargon2')
|
||||
fmt = dependency('fmt')
|
||||
openssl = dependency('openssl', required: get_option('proxy_client'))
|
||||
jsoncpp = dependency('jsoncpp', required: get_option('proxy_client'))
|
||||
fmt = dependency('fmt')
|
||||
llhttp = dependency('llhttp', 'libllhttp', required: get_option('proxy_client'))
|
||||
io_uring = dependency('liburing', required: false)
|
||||
simdutf = dependency('simdutf', required: get_option('proxy_client'))
|
||||
io_uring = host_machine.system() == 'linux' ? dependency('liburing', required: false) : disabler()
|
||||
|
||||
deps = [fmt, gnutls, nettle, msgpack, argon2, openssl, jsoncpp, llhttp, io_uring]
|
||||
deps = [
|
||||
fmt,
|
||||
gnutls,
|
||||
nettle,
|
||||
msgpack,
|
||||
argon2,
|
||||
openssl,
|
||||
jsoncpp,
|
||||
simdutf,
|
||||
llhttp,
|
||||
]
|
||||
|
||||
if host_machine.system() == 'linux' and io_uring.found()
|
||||
deps += io_uring
|
||||
endif
|
||||
conf_data = configuration_data()
|
||||
|
||||
add_project_arguments('-DMSGPACK_NO_BOOST', '-DASIO_STANDALONE', language : 'cpp')
|
||||
add_project_arguments('-DMSGPACK_NO_BOOST', '-DASIO_STANDALONE', language: 'cpp')
|
||||
if io_uring.found()
|
||||
add_project_arguments('-DASIO_HAS_IO_URING', '-DASIO_DISABLE_EPOLL', language : 'cpp')
|
||||
add_project_arguments('-DASIO_HAS_IO_URING', '-DASIO_DISABLE_EPOLL', language: 'cpp')
|
||||
conf_data.set('iouring_lib', ', liburing')
|
||||
endif
|
||||
add_project_arguments(['-Wno-return-type','-Wno-deprecated','-Wnon-virtual-dtor','-pedantic-errors','-fvisibility=hidden'], language : 'cpp')
|
||||
add_project_arguments(
|
||||
[
|
||||
'-Wno-return-type',
|
||||
'-Wno-deprecated',
|
||||
'-Wnon-virtual-dtor',
|
||||
'-pedantic-errors',
|
||||
'-fvisibility=hidden',
|
||||
],
|
||||
language: 'cpp',
|
||||
)
|
||||
|
||||
opendht_inc = include_directories('include/opendht')
|
||||
opendht_interface_inc = include_directories('include', is_system: true)
|
||||
@@ -48,17 +75,17 @@ opendht_src = [
|
||||
|
||||
if get_option('indexation').enabled()
|
||||
opendht_src += 'src/indexation/pht.cpp'
|
||||
add_project_arguments('-DOPENDHT_INDEXATION', language : 'cpp')
|
||||
add_project_arguments('-DOPENDHT_INDEXATION', language: 'cpp')
|
||||
endif
|
||||
if jsoncpp.found()
|
||||
opendht_src += ['src/base64.cpp']
|
||||
add_project_arguments('-DOPENDHT_JSONCPP', language : 'cpp')
|
||||
add_project_arguments('-DOPENDHT_JSONCPP', language: 'cpp')
|
||||
conf_data.set('jsoncpp_lib', ', jsoncpp')
|
||||
endif
|
||||
if llhttp.found()
|
||||
opendht_src += ['src/http.cpp', 'src/compat/os_cert.cpp']
|
||||
if host_machine.system() == 'darwin'
|
||||
deps+=dependency('appleframeworks', modules : ['CoreFoundation', 'Security'])
|
||||
deps += dependency('appleframeworks', modules: ['CoreFoundation', 'Security'])
|
||||
endif
|
||||
conf_data.set('http_lib', '-lllhttp')
|
||||
endif
|
||||
@@ -67,25 +94,27 @@ if openssl.found()
|
||||
endif
|
||||
if get_option('proxy_client').enabled()
|
||||
opendht_src += ['src/dht_proxy_client.cpp']
|
||||
add_project_arguments('-DOPENDHT_PROXY_CLIENT', language : 'cpp')
|
||||
add_project_arguments('-DOPENDHT_PROXY_CLIENT', language: 'cpp')
|
||||
endif
|
||||
if get_option('proxy_server').enabled()
|
||||
opendht_src += 'src/dht_proxy_server.cpp'
|
||||
add_project_arguments('-DOPENDHT_PROXY_SERVER', language : 'cpp')
|
||||
add_project_arguments('-DOPENDHT_PROXY_SERVER', language: 'cpp')
|
||||
endif
|
||||
if get_option('push_notifications').enabled()
|
||||
add_project_arguments('-DOPENDHT_PUSH_NOTIFICATIONS', language : 'cpp')
|
||||
add_project_arguments('-DOPENDHT_PUSH_NOTIFICATIONS', language: 'cpp')
|
||||
endif
|
||||
if get_option('peer_discovery').enabled()
|
||||
opendht_src += 'src/peer_discovery.cpp'
|
||||
add_project_arguments('-DOPENDHT_PEER_DISCOVERY', language : 'cpp')
|
||||
add_project_arguments('-DOPENDHT_PEER_DISCOVERY', language: 'cpp')
|
||||
endif
|
||||
opendht = library('opendht',
|
||||
opendht = library(
|
||||
'opendht',
|
||||
opendht_src,
|
||||
include_directories : opendht_inc,
|
||||
dependencies : deps,
|
||||
cpp_args : ['-DOPENDHT_BUILD', '-Dopendht_EXPORTS'],
|
||||
install : true)
|
||||
include_directories: opendht_inc,
|
||||
dependencies: deps,
|
||||
cpp_args: ['-DOPENDHT_BUILD', '-Dopendht_EXPORTS'],
|
||||
install: true,
|
||||
)
|
||||
|
||||
conf_data.set('VERSION', meson.project_version())
|
||||
conf_data.set('prefix', get_option('prefix'))
|
||||
@@ -93,74 +122,99 @@ conf_data.set('exec_prefix', get_option('prefix'))
|
||||
conf_data.set('libdir', join_paths(get_option('prefix'), get_option('libdir')))
|
||||
conf_data.set('includedir', join_paths(get_option('prefix'), get_option('includedir')))
|
||||
conf_data.set('argon2_lib', ', libargon2')
|
||||
conf_data.set('simdutf_lib', ', simdutf')
|
||||
|
||||
configure_file(input : 'opendht.pc.in',
|
||||
output : 'opendht.pc',
|
||||
configuration : conf_data,
|
||||
install: true,
|
||||
install_dir: join_paths(get_option('prefix'), get_option('libdir'), 'pkgconfig'))
|
||||
configure_file(
|
||||
input: 'opendht.pc.in',
|
||||
output: 'opendht.pc',
|
||||
configuration: conf_data,
|
||||
install: true,
|
||||
install_dir: join_paths(get_option('prefix'), get_option('libdir'), 'pkgconfig'),
|
||||
)
|
||||
|
||||
readline = meson.get_compiler('c').find_library('readline', required: get_option('tools'))
|
||||
|
||||
if get_option('c').enabled()
|
||||
opendht_c = library('opendht-c',
|
||||
['c/opendht.cpp', 'c/opendht_c.h'],
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [msgpack, jsoncpp, fmt],
|
||||
cpp_args : ['-DOPENDHT_C_BUILD', '-Dopendht_c_EXPORTS'],
|
||||
install : true)
|
||||
|
||||
configure_file(input : 'opendht-c.pc.in',
|
||||
output : 'opendht-c.pc',
|
||||
configuration : conf_data,
|
||||
install: true,
|
||||
install_dir: join_paths(get_option('prefix'), get_option('libdir'), 'pkgconfig'))
|
||||
opendht_c = library(
|
||||
'opendht-c',
|
||||
['c/opendht.cpp', 'c/opendht_c.h'],
|
||||
include_directories: opendht_interface_inc,
|
||||
link_with: opendht,
|
||||
dependencies: [msgpack, jsoncpp, fmt],
|
||||
cpp_args: ['-DOPENDHT_C_BUILD', '-Dopendht_c_EXPORTS'],
|
||||
install: true,
|
||||
)
|
||||
|
||||
configure_file(
|
||||
input: 'opendht-c.pc.in',
|
||||
output: 'opendht-c.pc',
|
||||
configuration: conf_data,
|
||||
install: true,
|
||||
install_dir: join_paths(get_option('prefix'), get_option('libdir'), 'pkgconfig'),
|
||||
)
|
||||
|
||||
if get_option('tools').enabled()
|
||||
dhtcnode = executable('dhtcnode', 'tools/dhtcnode.c',
|
||||
include_directories : ['c', opendht_interface_inc],
|
||||
link_with : opendht_c,
|
||||
dependencies : [readline],
|
||||
install : true)
|
||||
dhtcnode = executable(
|
||||
'dhtcnode',
|
||||
'tools/dhtcnode.c',
|
||||
include_directories: ['c', opendht_interface_inc],
|
||||
link_with: opendht_c,
|
||||
dependencies: [readline],
|
||||
install: true,
|
||||
)
|
||||
endif
|
||||
endif
|
||||
|
||||
if get_option('python').require(get_option('indexation').enabled(), error_message: 'python requires indexation').allowed()
|
||||
if get_option('python').require(
|
||||
get_option('indexation').enabled(),
|
||||
error_message: 'python requires indexation',
|
||||
).allowed()
|
||||
py = import('python').find_installation()
|
||||
dep_py = py.dependency()
|
||||
py.extension_module(
|
||||
'opendht',
|
||||
'python/opendht.pyx',
|
||||
include_directories : opendht_interface_inc,
|
||||
override_options : ['cython_language=cpp'],
|
||||
link_with : [opendht],
|
||||
dependencies : [msgpack, jsoncpp, fmt, dep_py],
|
||||
install : true
|
||||
include_directories: opendht_interface_inc,
|
||||
override_options: ['cython_language=cpp'],
|
||||
link_with: [opendht],
|
||||
dependencies: [msgpack, jsoncpp, fmt, dep_py],
|
||||
install: true,
|
||||
)
|
||||
endif
|
||||
|
||||
if get_option('tools').enabled()
|
||||
dhtnode = executable('dhtnode', 'tools/dhtnode.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [readline, jsoncpp, msgpack, fmt, openssl],
|
||||
install : true)
|
||||
dhtchat = executable('dhtchat', 'tools/dhtchat.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [readline, jsoncpp, msgpack, fmt, openssl],
|
||||
install : true)
|
||||
dhtscanner = executable('dhtscanner', 'tools/dhtscanner.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [readline, jsoncpp, msgpack, fmt, openssl],
|
||||
install : true)
|
||||
dhtnode = executable(
|
||||
'dhtnode',
|
||||
'tools/dhtnode.cpp',
|
||||
include_directories: opendht_interface_inc,
|
||||
link_with: opendht,
|
||||
dependencies: [readline, jsoncpp, msgpack, fmt, openssl],
|
||||
install: true,
|
||||
)
|
||||
dhtchat = executable(
|
||||
'dhtchat',
|
||||
'tools/dhtchat.cpp',
|
||||
include_directories: opendht_interface_inc,
|
||||
link_with: opendht,
|
||||
dependencies: [readline, jsoncpp, msgpack, fmt, openssl],
|
||||
install: true,
|
||||
)
|
||||
dhtscanner = executable(
|
||||
'dhtscanner',
|
||||
'tools/dhtscanner.cpp',
|
||||
include_directories: opendht_interface_inc,
|
||||
link_with: opendht,
|
||||
dependencies: [readline, jsoncpp, msgpack, fmt, openssl],
|
||||
install: true,
|
||||
)
|
||||
if llhttp.found()
|
||||
durl = executable('durl', 'tools/durl.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [msgpack, openssl])
|
||||
durl = executable(
|
||||
'durl',
|
||||
'tools/durl.cpp',
|
||||
include_directories: opendht_interface_inc,
|
||||
link_with: opendht,
|
||||
dependencies: [msgpack, openssl],
|
||||
)
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -168,74 +222,101 @@ endif
|
||||
if get_option('tests').enabled()
|
||||
cppunit = dependency('cppunit')
|
||||
|
||||
test_infohash = executable('test_infohash',
|
||||
'tests/infohashtester.cpp', 'tests/tests_runner.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [cppunit, jsoncpp, fmt, openssl, msgpack])
|
||||
test_infohash = executable(
|
||||
'test_infohash',
|
||||
'tests/infohashtester.cpp',
|
||||
'tests/tests_runner.cpp',
|
||||
include_directories: opendht_interface_inc,
|
||||
link_with: opendht,
|
||||
dependencies: [cppunit, jsoncpp, fmt, openssl, msgpack],
|
||||
)
|
||||
test('Infohash', test_infohash)
|
||||
|
||||
test_value = executable('test_value',
|
||||
'tests/valuetester.cpp', 'tests/tests_runner.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [cppunit, jsoncpp, fmt, openssl, msgpack])
|
||||
test_value = executable(
|
||||
'test_value',
|
||||
'tests/valuetester.cpp',
|
||||
'tests/tests_runner.cpp',
|
||||
include_directories: opendht_interface_inc,
|
||||
link_with: opendht,
|
||||
dependencies: [cppunit, jsoncpp, fmt, openssl, msgpack],
|
||||
)
|
||||
test('Value', test_value)
|
||||
|
||||
test_crypto = executable('test_crypto',
|
||||
'tests/cryptotester.cpp', 'tests/tests_runner.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [cppunit, jsoncpp, fmt, openssl, msgpack])
|
||||
test_crypto = executable(
|
||||
'test_crypto',
|
||||
'tests/cryptotester.cpp',
|
||||
'tests/tests_runner.cpp',
|
||||
include_directories: opendht_interface_inc,
|
||||
link_with: opendht,
|
||||
dependencies: [cppunit, jsoncpp, fmt, openssl, msgpack, gnutls],
|
||||
)
|
||||
test('Crypto', test_crypto)
|
||||
|
||||
test_dhtrunner = executable('test_dhtrunner',
|
||||
'tests/dhtrunnertester.cpp', 'tests/tests_runner.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [cppunit, jsoncpp, fmt, openssl, msgpack])
|
||||
test_dhtrunner = executable(
|
||||
'test_dhtrunner',
|
||||
'tests/dhtrunnertester.cpp',
|
||||
'tests/tests_runner.cpp',
|
||||
include_directories: opendht_interface_inc,
|
||||
link_with: opendht,
|
||||
dependencies: [cppunit, jsoncpp, fmt, openssl, msgpack],
|
||||
)
|
||||
test('DhtRunner', test_dhtrunner)
|
||||
|
||||
test_threadpool = executable('test_threadpool',
|
||||
'tests/threadpooltester.cpp', 'tests/tests_runner.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [cppunit, jsoncpp, fmt, openssl, msgpack])
|
||||
test_threadpool = executable(
|
||||
'test_threadpool',
|
||||
'tests/threadpooltester.cpp',
|
||||
'tests/tests_runner.cpp',
|
||||
include_directories: opendht_interface_inc,
|
||||
link_with: opendht,
|
||||
dependencies: [cppunit, jsoncpp, fmt, openssl, msgpack],
|
||||
)
|
||||
test('ThreadPool', test_threadpool)
|
||||
|
||||
if get_option('proxy_client').enabled() or get_option('proxy_server').enabled()
|
||||
test_http = executable('test_http',
|
||||
'tests/httptester.cpp', 'tests/tests_runner.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [cppunit, jsoncpp, fmt, openssl, msgpack])
|
||||
test_http = executable(
|
||||
'test_http',
|
||||
'tests/httptester.cpp',
|
||||
'tests/tests_runner.cpp',
|
||||
include_directories: opendht_interface_inc,
|
||||
link_with: opendht,
|
||||
dependencies: [cppunit, jsoncpp, fmt, openssl, msgpack],
|
||||
)
|
||||
test('Http', test_http)
|
||||
endif
|
||||
|
||||
if get_option('peer_discovery').enabled()
|
||||
test_peerdiscovery = executable('test_peerdiscovery',
|
||||
'tests/peerdiscoverytester.cpp', 'tests/tests_runner.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [cppunit, jsoncpp, fmt, openssl, msgpack])
|
||||
test_peerdiscovery = executable(
|
||||
'test_peerdiscovery',
|
||||
'tests/peerdiscoverytester.cpp',
|
||||
'tests/tests_runner.cpp',
|
||||
include_directories: opendht_interface_inc,
|
||||
link_with: opendht,
|
||||
dependencies: [cppunit, jsoncpp, fmt, openssl, msgpack],
|
||||
)
|
||||
test('PeerDiscovery', test_peerdiscovery)
|
||||
endif
|
||||
|
||||
if get_option('proxy_server').enabled()
|
||||
test_proxy = executable('test_dhtproxy',
|
||||
'tests/dhtproxytester.cpp', 'tests/tests_runner.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [cppunit, jsoncpp, fmt, openssl, msgpack])
|
||||
test_proxy = executable(
|
||||
'test_dhtproxy',
|
||||
'tests/dhtproxytester.cpp',
|
||||
'tests/tests_runner.cpp',
|
||||
include_directories: opendht_interface_inc,
|
||||
link_with: opendht,
|
||||
dependencies: [cppunit, jsoncpp, fmt, openssl, msgpack],
|
||||
)
|
||||
test('DhtProxy', test_proxy)
|
||||
|
||||
if get_option('long_tests').enabled()
|
||||
test_proxystress = executable('test_dhtproxystress',
|
||||
'tests/dhtproxy_stress_tester.cpp', 'tests/tests_runner.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [cppunit, jsoncpp, fmt, openssl, msgpack])
|
||||
test_proxystress = executable(
|
||||
'test_dhtproxystress',
|
||||
'tests/dhtproxy_stress_tester.cpp',
|
||||
'tests/tests_runner.cpp',
|
||||
include_directories: opendht_interface_inc,
|
||||
link_with: opendht,
|
||||
dependencies: [cppunit, jsoncpp, fmt, openssl, msgpack],
|
||||
)
|
||||
test('DhtProxy Stress', test_proxystress)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
+2
-2
@@ -5,8 +5,8 @@ includedir=@includedir@
|
||||
Name: OpenDHT
|
||||
Description: C++17 Distributed Hash Table library
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lopendht
|
||||
Libs: -L${libdir} -lopendht -lfmt
|
||||
Libs.private: @http_lib@ -pthread
|
||||
Requires: gnutls >= 3.3@jsoncpp_lib@@openssl_lib@
|
||||
Requires.private: nettle >= 2.4@argon2_lib@@iouring_lib@
|
||||
Requires.private: nettle >= 2.4@argon2_lib@@llhttp_lib@@iouring_lib@@simdutf_lib@
|
||||
Cflags: -I${includedir}
|
||||
+39
-9
@@ -1,19 +1,49 @@
|
||||
|
||||
set(CURRENT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(CURRENT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
configure_file(setup.py.in setup.py)
|
||||
configure_file(pyproject.toml pyproject.toml COPYONLY)
|
||||
configure_file(README.md README.md COPYONLY)
|
||||
|
||||
add_custom_target(python ALL
|
||||
COMMAND python3 setup.py build
|
||||
DEPENDS opendht opendht_cpp.pxd opendht.pyx pyproject.toml)
|
||||
find_package(Python3 REQUIRED COMPONENTS Interpreter)
|
||||
|
||||
add_custom_target(dist
|
||||
COMMAND python3 -m build
|
||||
DEPENDS opendht opendht_cpp.pxd opendht.pyx pyproject.toml)
|
||||
add_custom_target(python
|
||||
COMMAND ${Python3_EXECUTABLE} -m build "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
DEPENDS opendht opendht_cpp.pxd opendht.pyx pyproject.toml
|
||||
${CURRENT_SOURCE_DIR}/opendht/__init__.py
|
||||
${CURRENT_SOURCE_DIR}/opendht/aio.py)
|
||||
|
||||
install(CODE [[
|
||||
if(NOT Python3_EXECUTABLE)
|
||||
find_package(Python3 COMPONENTS Interpreter)
|
||||
endif()
|
||||
if(NOT Python3_EXECUTABLE)
|
||||
message(WARNING "Skipping Python package install: Python interpreter not found.")
|
||||
else()
|
||||
execute_process(
|
||||
COMMAND ${Python3_EXECUTABLE} -m pip --version
|
||||
OUTPUT_QUIET
|
||||
ERROR_QUIET
|
||||
RESULT_VARIABLE _pip_check)
|
||||
if(NOT _pip_check EQUAL 0)
|
||||
message(WARNING "Skipping Python package install: pip not available for ${Python3_EXECUTABLE}.")
|
||||
else()
|
||||
execute_process(
|
||||
COMMAND ${Python3_EXECUTABLE} -m pip install --no-deps --break-system-packages .
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/python
|
||||
OUTPUT_VARIABLE _pip_output
|
||||
ERROR_VARIABLE _pip_error
|
||||
RESULT_VARIABLE _pip_result)
|
||||
|
||||
install(CODE "execute_process(COMMAND python3 setup.py install --root=\$ENV{DESTDIR}/ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})")
|
||||
if(_pip_result EQUAL 0)
|
||||
message(STATUS "Successfully installed Python package")
|
||||
message(STATUS "Output: ${_pip_output}")
|
||||
else()
|
||||
message(WARNING "pip install failed (code: ${_pip_result})\nOutput: ${_pip_output}\nError: ${_pip_error}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
]])
|
||||
if (OPENDHT_TOOLS)
|
||||
install(PROGRAMS tools/dhtcluster.py DESTINATION ${CMAKE_INSTALL_BINDIR} RENAME dhtcluster)
|
||||
endif()
|
||||
endif()
|
||||
+3
-3
@@ -8,7 +8,7 @@ noinst_HEADERS = \
|
||||
PYTHON_INSTALL_RECORD = $(builddir)/install_record.txt
|
||||
|
||||
pybuild.stamp:
|
||||
LDFLAGS="-L$(top_srcdir)/src/.libs" $(PYTHON) setup.py build_ext --inplace
|
||||
LDFLAGS="-L$(top_srcdir)/src/.libs" $(PYTHON) -m build .
|
||||
echo stamp > pybuild.stamp
|
||||
|
||||
CLEANFILES = pybuild.stamp
|
||||
@@ -18,7 +18,8 @@ clean-local:
|
||||
rm -rf $(builddir)/build $(builddir)/*.so $(PYTHON_INSTALL_RECORD)
|
||||
|
||||
install-exec-local:
|
||||
$(PYTHON) setup.py install --root=$(DESTDIR)/ --record $(PYTHON_INSTALL_RECORD)
|
||||
$(PYTHON) -m pip install --no-deps --no-build-isolation . \
|
||||
--root=$(DESTDIR)/ --prefix=$(prefix)
|
||||
rm -rf $(builddir)/build
|
||||
|
||||
if HAVE_PIP
|
||||
@@ -27,4 +28,3 @@ uninstall-local:
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
# OpenDHT
|
||||
|
||||
OpenDHT is a lightweight Distributed Hash Table implementation providing an easy to use distributed in-memory data store.
|
||||
|
||||
## Key Features
|
||||
|
||||
* Lightweight and scalable, designed for large networks and small devices
|
||||
* High resilience to network disruption
|
||||
* Public key cryptography layer providing optional data signature and encryption
|
||||
* IPv4 and IPv6 support
|
||||
* Python 3 bindings
|
||||
* REST API with optional HTTP client+server with push notification support
|
||||
|
||||
Every node in the network can read and write values to the store. Values are distributed over the network with redundancy.
|
||||
|
||||
## Installation
|
||||
|
||||
Install on Linux, macOS and Windows with:
|
||||
```sh
|
||||
pip install opendht
|
||||
```
|
||||
|
||||
## Quick Example
|
||||
|
||||
Using the simple, blocking API:
|
||||
```python
|
||||
import opendht as dht
|
||||
|
||||
node = dht.DhtRunner()
|
||||
node.run()
|
||||
|
||||
# Join the DHT network through any running node,
|
||||
# here using a known bootstrap node.
|
||||
node.bootstrap("bootstrap.jami.net", "4222")
|
||||
|
||||
# blocking call (provide callback arguments to make the call non-blocking)
|
||||
node.put(dht.InfoHash.get("unique_key"), dht.Value(b'some binary data'))
|
||||
|
||||
results = node.get(dht.InfoHash.get("unique_key"))
|
||||
for value in results:
|
||||
print(value)
|
||||
```
|
||||
|
||||
Or using asyncio:
|
||||
```python
|
||||
import asyncio
|
||||
import opendht.aio as dht
|
||||
|
||||
async def dht_async_demo(key_str: str):
|
||||
# Start a new node using an async context manager.
|
||||
# It is also possible to call run()/await shutdown() manually.
|
||||
async with dht.DhtRunner(
|
||||
bootstrap=(("bootstrap.jami.net", "4222"),)
|
||||
) as node:
|
||||
# compute key hash
|
||||
key = dht.InfoHash.get(key_str)
|
||||
|
||||
# put data, waiting for completion
|
||||
await node.put(key, dht.Value(b'tata data'))
|
||||
|
||||
# get all values at key
|
||||
results = await node.getAll(key)
|
||||
for value in results:
|
||||
print(value)
|
||||
|
||||
# same operation, but stream values as they come from the network
|
||||
with node.get(key) as results:
|
||||
async for value in results:
|
||||
print(value)
|
||||
|
||||
# listen for change of values at key
|
||||
with node.listen(key) as values:
|
||||
async for value, expired in values:
|
||||
print(value)
|
||||
if value.data == b'tata data':
|
||||
break
|
||||
|
||||
asyncio.run(dht_async_demo("unique_key"))
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
For more information, examples and documentation, or to repport issues, visit: https://opendht.net
|
||||
|
||||
## License
|
||||
|
||||
Copyright (C) 2014-2025 Savoir-faire Linux Inc.
|
||||
|
||||
OpenDHT is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
|
||||
+250
-116
@@ -5,27 +5,20 @@
|
||||
# distutils: libraries = opendht gnutls
|
||||
# cython: language_level=3
|
||||
#
|
||||
# Copyright (c) 2015-2023 Savoir-faire Linux Inc.
|
||||
# Author(s): Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
|
||||
# Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
# Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
# Copyright (C) 2014-2025 Savoir-faire Linux Inc.
|
||||
#
|
||||
# This wrapper is written for Cython 0.22
|
||||
#
|
||||
# This file is part of OpenDHT Python Wrapper.
|
||||
#
|
||||
# OpenDHT Python Wrapper is free software: you can redistribute it and/or modify
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# OpenDHT Python Wrapper is distributed in the hope that it will be useful,
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with OpenDHT Python Wrapper. If not, see <https://www.gnu.org/licenses/>.
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
from libcpp.map cimport map as map
|
||||
from libcpp cimport bool
|
||||
@@ -33,7 +26,6 @@ from libcpp.utility cimport pair
|
||||
from libcpp.string cimport string
|
||||
from libcpp.memory cimport shared_ptr
|
||||
|
||||
from cython.parallel import parallel, prange
|
||||
from cython.operator cimport dereference as deref, preincrement as inc, predecrement as dec
|
||||
from cpython cimport ref
|
||||
from datetime import timedelta
|
||||
@@ -42,6 +34,23 @@ cimport opendht_cpp as cpp
|
||||
|
||||
import threading
|
||||
|
||||
cdef inline void _initialize_gnutls():
|
||||
cdef int err = cpp.gnutls_global_init()
|
||||
if err != 0:
|
||||
print(f"Failed to initialize GnuTLS: {cpp.gnutls_strerror(err).decode()}")
|
||||
else:
|
||||
import atexit
|
||||
atexit.register(_deinitialize_gnutls)
|
||||
|
||||
cdef inline _deinitialize_gnutls():
|
||||
cpp.gnutls_global_deinit()
|
||||
|
||||
# Initialize GnuTLS when this module is imported
|
||||
_initialize_gnutls()
|
||||
|
||||
def version() -> str:
|
||||
return cpp.version().decode()
|
||||
|
||||
cdef inline void lookup_callback(cpp.vector[cpp.shared_ptr[cpp.IndexValue]]* values, cpp.Prefix* p, void *user_data) noexcept with gil:
|
||||
cbs = <object>user_data
|
||||
if 'lookup' in cbs and cbs['lookup']:
|
||||
@@ -98,6 +107,12 @@ cdef class _WithID(object):
|
||||
def __str__(self):
|
||||
return self.getId().toString().decode()
|
||||
|
||||
cdef class _WithPk(object):
|
||||
def __repr__(self):
|
||||
return "<%s '%s'>" % (self.__class__.__name__, str(self))
|
||||
def __str__(self):
|
||||
return self.getLongId().toString().decode()
|
||||
|
||||
cdef class InfoHash(_WithID):
|
||||
cdef cpp.InfoHash _infohash
|
||||
def __cinit__(self, bytes str=b''):
|
||||
@@ -123,19 +138,57 @@ cdef class InfoHash(_WithID):
|
||||
def toFloat(InfoHash self):
|
||||
return self._infohash.toFloat()
|
||||
@staticmethod
|
||||
def commonBits(InfoHash a, InfoHash b):
|
||||
def commonBits(InfoHash a, InfoHash b) -> int:
|
||||
return cpp.InfoHash.commonBits(a._infohash, b._infohash)
|
||||
@staticmethod
|
||||
def get(str key):
|
||||
def get(str key) -> InfoHash:
|
||||
h = InfoHash()
|
||||
h._infohash = cpp.InfoHash.get(key.encode())
|
||||
return h
|
||||
@staticmethod
|
||||
def getRandom():
|
||||
def getRandom() -> InfoHash:
|
||||
h = InfoHash()
|
||||
h._infohash = cpp.InfoHash.getRandom()
|
||||
return h
|
||||
|
||||
cdef class PkId(_WithPk):
|
||||
cdef cpp.PkId _pkid
|
||||
def __cinit__(self, bytes str=b''):
|
||||
self._pkid = cpp.PkId(str) if str else cpp.PkId()
|
||||
def __bool__(PkId self):
|
||||
return <bool>self._pkid
|
||||
def __richcmp__(PkId self, PkId other, int op):
|
||||
if op == 0:
|
||||
return self._pkid < other._pkid
|
||||
if op == 1:
|
||||
return self._pkid < other._pkid or self._pkid == other._pkid
|
||||
if op == 2:
|
||||
return self._pkid == other._pkid
|
||||
return NotImplemented
|
||||
def getBit(PkId self, bit):
|
||||
return self._pkid.getBit(bit)
|
||||
def setBit(PkId self, bit, b):
|
||||
self._pkid.setBit(bit, b)
|
||||
def getLongId(PkId self):
|
||||
return self
|
||||
def toString(PkId self):
|
||||
return self._pkid.toString()
|
||||
def toFloat(PkId self) -> float:
|
||||
return self._pkid.toFloat()
|
||||
@staticmethod
|
||||
def commonBits(PkId a, PkId b) -> int:
|
||||
return cpp.PkId.commonBits(a._pkid, b._pkid)
|
||||
@staticmethod
|
||||
def get(str key) -> PkId:
|
||||
h = PkId()
|
||||
h._pkid = cpp.PkId.get(key.encode())
|
||||
return h
|
||||
@staticmethod
|
||||
def getRandom() -> PkId:
|
||||
h = PkId()
|
||||
h._pkid = cpp.PkId.getRandom()
|
||||
return h
|
||||
|
||||
cdef class SockAddr(object):
|
||||
cdef cpp.SockAddr _addr
|
||||
def toString(SockAddr self):
|
||||
@@ -225,7 +278,7 @@ cdef class Where(object):
|
||||
self._where = cpp.Where()
|
||||
def __str__(self):
|
||||
return self._where.toString().decode()
|
||||
def isSatisfiedBy(self, Where where):
|
||||
def isSatisfiedBy(self, Where where) -> bool:
|
||||
return self._where.isSatisfiedBy(where._where)
|
||||
def id(self, cpp.uint64_t id):
|
||||
self._where.id(id)
|
||||
@@ -247,39 +300,44 @@ cdef class Value(object):
|
||||
cdef shared_ptr[cpp.Value] _value
|
||||
def __init__(self, bytes val=b'', cpp.uint16_t id=0):
|
||||
self._value.reset(new cpp.Value(id, val, len(val)))
|
||||
|
||||
def __str__(self):
|
||||
return self._value.get().toString().decode()
|
||||
property owner:
|
||||
def __get__(self):
|
||||
h = InfoHash()
|
||||
h._infohash = self._value.get().owner.get().getId()
|
||||
return h
|
||||
property recipient:
|
||||
def __get__(self):
|
||||
h = InfoHash()
|
||||
h._infohash = self._value.get().recipient
|
||||
return h
|
||||
def __set__(self, InfoHash h):
|
||||
self._value.get().recipient = h._infohash
|
||||
property data:
|
||||
def __get__(self):
|
||||
return string(<char*>self._value.get().data.data(), self._value.get().data.size())
|
||||
def __set__(self, bytes value):
|
||||
self._value.get().data = value
|
||||
property user_type:
|
||||
def __get__(self):
|
||||
return self._value.get().user_type.decode()
|
||||
def __set__(self, str t):
|
||||
self._value.get().user_type = t.encode()
|
||||
property id:
|
||||
def __get__(self):
|
||||
return self._value.get().id
|
||||
def __set__(self, cpp.uint64_t value):
|
||||
self._value.get().id = value
|
||||
property size:
|
||||
def __get__(self):
|
||||
return self._value.get().size()
|
||||
@property
|
||||
def owner(self) -> PublicKey:
|
||||
h = PublicKey()
|
||||
h._key = self._value.get().owner
|
||||
return h
|
||||
@property
|
||||
def recipient(self) -> InfoHash:
|
||||
h = InfoHash()
|
||||
h._infohash = self._value.get().recipient
|
||||
return h
|
||||
@recipient.setter
|
||||
def recipient(self, InfoHash h):
|
||||
self._value.get().recipient = h._infohash
|
||||
@property
|
||||
def data(self) -> bytes:
|
||||
cdef char* data_ptr = <char*>self._value.get().data.data()
|
||||
cdef int data_size = self._value.get().data.size()
|
||||
return data_ptr[:data_size]
|
||||
@data.setter
|
||||
def data(self, bytes value):
|
||||
self._value.get().data = value
|
||||
@property
|
||||
def user_type(self) -> str:
|
||||
return self._value.get().user_type.decode()
|
||||
@user_type.setter
|
||||
def user_type(self, str t):
|
||||
self._value.get().user_type = t.encode()
|
||||
@property
|
||||
def id(self):
|
||||
return self._value.get().id
|
||||
@id.setter
|
||||
def id(self, cpp.uint64_t value):
|
||||
self._value.get().id = value
|
||||
@property
|
||||
def size(self) -> int:
|
||||
return self._value.get().size()
|
||||
|
||||
cdef class ValueType(object):
|
||||
cdef cpp.ValueType * _value
|
||||
@@ -334,17 +392,31 @@ cdef class NodeSet(object):
|
||||
def __iter__(self):
|
||||
return NodeSetIter(self)
|
||||
|
||||
cdef class PrivateKey(_WithID):
|
||||
cdef class PrivateKey(_WithPk):
|
||||
cdef shared_ptr[cpp.PrivateKey] _key
|
||||
def getId(self):
|
||||
|
||||
def getId(self) -> InfoHash:
|
||||
if not self._key:
|
||||
raise ValueError("PrivateKey is not initialized")
|
||||
h = InfoHash()
|
||||
h._infohash = self._key.get().getSharedPublicKey().get().getId()
|
||||
return h
|
||||
def getPublicKey(self):
|
||||
|
||||
def getLongId(self) -> PkId:
|
||||
if not self._key:
|
||||
raise ValueError("PrivateKey is not initialized")
|
||||
id = PkId()
|
||||
id._pkid = self._key.get().getSharedPublicKey().get().getLongId()
|
||||
return id
|
||||
|
||||
def getPublicKey(self) -> PublicKey:
|
||||
if not self._key:
|
||||
raise ValueError("PrivateKey is not initialized")
|
||||
pk = PublicKey()
|
||||
pk._key = self._key.get().getSharedPublicKey()
|
||||
return pk
|
||||
def decrypt(self, bytes dat):
|
||||
|
||||
def decrypt(self, bytes dat) -> bytes:
|
||||
cdef size_t d_len = len(dat)
|
||||
cdef cpp.uint8_t* d_ptr = <cpp.uint8_t*>dat
|
||||
cdef cpp.Blob indat
|
||||
@@ -353,26 +425,49 @@ cdef class PrivateKey(_WithID):
|
||||
cdef char* decrypted_c_str = <char *>decrypted.data()
|
||||
cdef Py_ssize_t length = decrypted.size()
|
||||
return decrypted_c_str[:length]
|
||||
def __str__(self):
|
||||
return self.getId().toString().decode()
|
||||
|
||||
def sign(self, bytes dat) -> bytes:
|
||||
if not self._key:
|
||||
raise ValueError("PrivateKey is not initialized")
|
||||
cdef size_t d_len = len(dat)
|
||||
cdef cpp.uint8_t* d_ptr = <cpp.uint8_t*>dat
|
||||
cdef cpp.Blob indat
|
||||
indat.assign(d_ptr, <cpp.uint8_t*>(d_ptr + d_len))
|
||||
cdef cpp.Blob signature = self._key.get().sign(indat)
|
||||
cdef char* signature_c_str = <char *>signature.data()
|
||||
cdef Py_ssize_t length = signature.size()
|
||||
return signature_c_str[:length]
|
||||
|
||||
@staticmethod
|
||||
def generate():
|
||||
def generate() -> PrivateKey:
|
||||
k = PrivateKey()
|
||||
k._key = cpp.make_shared[cpp.PrivateKey](cpp.PrivateKey.generate())
|
||||
return k
|
||||
@staticmethod
|
||||
def generateEC():
|
||||
def generateEC() -> PrivateKey:
|
||||
k = PrivateKey()
|
||||
k._key = cpp.make_shared[cpp.PrivateKey](cpp.PrivateKey.generateEC())
|
||||
return k
|
||||
|
||||
cdef class PublicKey(_WithID):
|
||||
cdef class PublicKey(_WithPk):
|
||||
cdef cpp.shared_ptr[cpp.PublicKey] _key
|
||||
def getId(self):
|
||||
def getId(self) -> InfoHash:
|
||||
if not self._key:
|
||||
raise ValueError("PublicKey is not initialized")
|
||||
h = InfoHash()
|
||||
h._infohash = self._key.get().getId()
|
||||
return h
|
||||
def encrypt(self, bytes dat):
|
||||
|
||||
def getLongId(self) -> PkId:
|
||||
if not self._key:
|
||||
raise ValueError("PublicKey is not initialized")
|
||||
id = PkId()
|
||||
id._pkid = self._key.get().getLongId()
|
||||
return id
|
||||
|
||||
def encrypt(self, bytes dat) -> bytes:
|
||||
if not self._key:
|
||||
raise ValueError("PublicKey is not initialized")
|
||||
cdef size_t d_len = len(dat)
|
||||
cdef cpp.uint8_t* d_ptr = <cpp.uint8_t*>dat
|
||||
cdef cpp.Blob indat
|
||||
@@ -382,29 +477,53 @@ cdef class PublicKey(_WithID):
|
||||
cdef Py_ssize_t length = encrypted.size()
|
||||
return encrypted_c_str[:length]
|
||||
|
||||
cdef class Certificate(_WithID):
|
||||
def checkSignature(self, bytes dat, bytes signature) -> bool:
|
||||
if not self._key:
|
||||
raise ValueError("PublicKey is not initialized")
|
||||
cdef size_t d_len = len(dat)
|
||||
cdef cpp.uint8_t* d_ptr = <cpp.uint8_t*>dat
|
||||
cdef cpp.Blob indat
|
||||
indat.assign(d_ptr, <cpp.uint8_t*>(d_ptr + d_len))
|
||||
cdef size_t s_len = len(signature)
|
||||
cdef cpp.uint8_t* s_ptr = <cpp.uint8_t*>signature
|
||||
cdef cpp.Blob sig
|
||||
sig.assign(s_ptr, <cpp.uint8_t*>(s_ptr + s_len))
|
||||
return self._key.get().checkSignature(indat, sig)
|
||||
|
||||
cdef class Certificate(_WithPk):
|
||||
cdef shared_ptr[cpp.Certificate] _cert
|
||||
def __init__(self, bytes dat = None):
|
||||
if dat:
|
||||
self._cert = cpp.make_shared[cpp.Certificate](<cpp.string>dat)
|
||||
def getId(self):
|
||||
def getId(self) -> InfoHash:
|
||||
if not self._cert:
|
||||
raise ValueError("Certificate is not initialized")
|
||||
h = InfoHash()
|
||||
if self._cert:
|
||||
h._infohash = self._cert.get().getId()
|
||||
h._infohash = self._cert.get().getId()
|
||||
return h
|
||||
def toString(self):
|
||||
def getLongId(self) -> PkId:
|
||||
if not self._cert:
|
||||
raise ValueError("Certificate is not initialized")
|
||||
id = PkId()
|
||||
id._pkid = self._cert.get().getLongId()
|
||||
return id
|
||||
def toString(self) -> str:
|
||||
if not self._cert:
|
||||
return ""
|
||||
return self._cert.get().toString().decode()
|
||||
def getName(self):
|
||||
return self._cert.get().getName()
|
||||
def getName(self) -> str:
|
||||
return self._cert.get().getName().decode()
|
||||
def revoke(self, PrivateKey k, Certificate c):
|
||||
self._cert.get().revoke(deref(k._key.get()), deref(c._cert.get()));
|
||||
def __bytes__(self):
|
||||
return self._cert.get().toString() if self._cert else b''
|
||||
property issuer:
|
||||
def __get__(self):
|
||||
return self._cert.get().toString().encode() if self._cert else b''
|
||||
@property
|
||||
def issuer(self) -> Certificate|None:
|
||||
if self._cert and self._cert.get().issuer:
|
||||
c = Certificate()
|
||||
c._cert = self._cert.get().issuer
|
||||
return c;
|
||||
return c
|
||||
return None
|
||||
@staticmethod
|
||||
def generate(PrivateKey k, str name, Identity i = Identity(), bool is_ca = False):
|
||||
c = Certificate()
|
||||
@@ -446,21 +565,27 @@ cdef class Identity(object):
|
||||
i = Identity()
|
||||
i._id = cpp.generateIdentity(name.encode(), ca._id, bits)
|
||||
return i
|
||||
property publickey:
|
||||
def __get__(self):
|
||||
k = PublicKey()
|
||||
k._key = self._id.first.get().getSharedPublicKey()
|
||||
return k
|
||||
property certificate:
|
||||
def __get__(self):
|
||||
c = Certificate()
|
||||
c._cert = self._id.second
|
||||
return c
|
||||
property key:
|
||||
def __get__(self):
|
||||
k = PrivateKey()
|
||||
k._key = self._id.first
|
||||
return k
|
||||
@property
|
||||
def publickey(self) -> PublicKey|None:
|
||||
if not self._id.first:
|
||||
return None
|
||||
k = PublicKey()
|
||||
k._key = self._id.first.get().getSharedPublicKey()
|
||||
return k
|
||||
@property
|
||||
def certificate(self) -> Certificate|None:
|
||||
if not self._id.second:
|
||||
return None
|
||||
c = Certificate()
|
||||
c._cert = self._id.second
|
||||
return c
|
||||
@property
|
||||
def key(self) -> PrivateKey|None:
|
||||
if not self._id.first:
|
||||
return None
|
||||
k = PrivateKey()
|
||||
k._key = self._id.first
|
||||
return k
|
||||
|
||||
def aesEncrypt(bytes data, str password) -> bytes :
|
||||
cdef size_t d_len = len(data)
|
||||
@@ -519,13 +644,22 @@ cdef class DhtRunner(_WithID):
|
||||
cdef cpp.shared_ptr[cpp.DhtRunner] thisptr
|
||||
def __cinit__(self):
|
||||
self.thisptr.reset(new cpp.DhtRunner())
|
||||
def getId(self):
|
||||
def getId(self) -> InfoHash:
|
||||
h = InfoHash()
|
||||
if self.thisptr:
|
||||
h._infohash = self.thisptr.get().getId()
|
||||
return h
|
||||
def getNodeId(self):
|
||||
return self.thisptr.get().getNodeId().toString()
|
||||
def getPublicKey(self) -> PublicKey:
|
||||
if not self.thisptr:
|
||||
raise ValueError("DhtRunner is not initialized")
|
||||
pk = PublicKey()
|
||||
pk._key = self.thisptr.get().getPublicKey()
|
||||
return pk
|
||||
def getNodeId(self) -> InfoHash:
|
||||
h = InfoHash()
|
||||
if self.thisptr:
|
||||
h._infohash = self.thisptr.get().getNodeId()
|
||||
return h
|
||||
def ping(self, SockAddr addr, done_cb=None):
|
||||
if done_cb:
|
||||
cb_obj = {'done':done_cb}
|
||||
@@ -681,11 +815,33 @@ cdef class DhtRunner(_WithID):
|
||||
lock.wait()
|
||||
return ok
|
||||
|
||||
def putEncrypted(self, InfoHash key, InfoHash to, Value val, done_cb=None, bool permanent=False):
|
||||
def putEncrypted(self, InfoHash key, Value val, to, done_cb=None, bool permanent=False):
|
||||
if to is None:
|
||||
raise ValueError("'to' must be provided")
|
||||
if done_cb:
|
||||
cb_obj = {'done':done_cb}
|
||||
ref.Py_INCREF(cb_obj)
|
||||
self.thisptr.get().putEncrypted(key._infohash, to._infohash, val._value, cpp.bindDoneCb(done_callback, <void*>cb_obj), permanent)
|
||||
try:
|
||||
if isinstance(to, PublicKey):
|
||||
self.thisptr.get().putEncrypted(
|
||||
key._infohash, (<PublicKey>to)._key, val._value,
|
||||
cpp.bindDoneCb(done_callback, <void*>cb_obj), permanent
|
||||
)
|
||||
elif isinstance(to, PkId):
|
||||
self.thisptr.get().putEncrypted(
|
||||
key._infohash, (<PkId>to)._pkid, val._value,
|
||||
cpp.bindDoneCb(done_callback, <void*>cb_obj), permanent
|
||||
)
|
||||
elif isinstance(to, InfoHash):
|
||||
self.thisptr.get().putEncrypted(
|
||||
key._infohash, (<InfoHash>to)._infohash, val._value,
|
||||
cpp.bindDoneCb(done_callback, <void*>cb_obj), permanent
|
||||
)
|
||||
else:
|
||||
raise TypeError("'to' must be PublicKey, PkId, or InfoHash")
|
||||
except:
|
||||
ref.Py_DECREF(cb_obj)
|
||||
raise
|
||||
else:
|
||||
lock = threading.Condition()
|
||||
pending = 0
|
||||
@@ -698,29 +854,7 @@ cdef class DhtRunner(_WithID):
|
||||
lock.notify()
|
||||
with lock:
|
||||
pending += 1
|
||||
self.putEncrypted(key, to, val, done_cb=tmp_done, permanent=permanent)
|
||||
while pending > 0:
|
||||
lock.wait()
|
||||
return ok
|
||||
|
||||
def putEncrypted(self, InfoHash key, PublicKey to, Value val, done_cb=None, bool permanent=False):
|
||||
if done_cb:
|
||||
cb_obj = {'done':done_cb}
|
||||
ref.Py_INCREF(cb_obj)
|
||||
self.thisptr.get().putEncrypted(key._infohash, to._key, val._value, cpp.bindDoneCb(done_callback, <void*>cb_obj), permanent)
|
||||
else:
|
||||
lock = threading.Condition()
|
||||
pending = 0
|
||||
ok = False
|
||||
def tmp_done(ok_ret, nodes):
|
||||
nonlocal pending, ok, lock
|
||||
with lock:
|
||||
ok = ok_ret
|
||||
pending -= 1
|
||||
lock.notify()
|
||||
with lock:
|
||||
pending += 1
|
||||
self.putEncrypted(key, to, val, done_cb=tmp_done, permanent=permanent)
|
||||
self.putEncrypted(key, val, to=to, done_cb=tmp_done, permanent=permanent)
|
||||
while pending > 0:
|
||||
lock.wait()
|
||||
return ok
|
||||
@@ -763,9 +897,9 @@ cdef class Pht(object):
|
||||
for kk, size in key_spec.items():
|
||||
cpp_key_spec[bytes(kk, 'utf-8')] = size
|
||||
self.thisptr = new cpp.Pht(name, cpp_key_spec, dht.thisptr)
|
||||
property MAX_NODE_ENTRY_COUNT:
|
||||
def __get__(self):
|
||||
return cpp.PHT_MAX_NODE_ENTRY_COUNT
|
||||
@property
|
||||
def MAX_NODE_ENTRY_COUNT(self):
|
||||
return cpp.PHT_MAX_NODE_ENTRY_COUNT
|
||||
def lookup(self, key, lookup_cb=None, done_cb=None):
|
||||
"""Query the Index with a specified key.
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
"""
|
||||
Python package facade for OpenDHT.
|
||||
|
||||
This package re-exports the Cython-built core bindings from ``opendht._core``
|
||||
to preserve the public API (e.g., ``opendht.DhtRunner``), and also exposes a
|
||||
pure-Python asyncio-friendly wrapper under ``opendht.aio``.
|
||||
"""
|
||||
|
||||
from ._core import * # re-export core Cython bindings
|
||||
|
||||
__version__ = version()
|
||||
__all__ = [name for name in dir() if not name.startswith("_")]
|
||||
@@ -0,0 +1,293 @@
|
||||
# Copyright (C) 2014-2025 Savoir-faire Linux Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from typing import Any, Callable, Iterable, List, Optional, Tuple
|
||||
|
||||
from . import _core as _dht
|
||||
|
||||
# Re-export core types for convenience
|
||||
from ._core import InfoHash, Value, ValueType, Where, SockAddr, PublicKey, PkId, DhtConfig, Identity
|
||||
|
||||
|
||||
class _Listener:
|
||||
"""Async iterator around DHT listen callback.
|
||||
|
||||
Yields tuples (Value, expired: bool).
|
||||
"""
|
||||
|
||||
__slots__ = ("_runner", "_token", "_loop", "_queue", "_closed")
|
||||
|
||||
def __init__(self, runner: DhtRunner, key: InfoHash) -> None:
|
||||
self._runner = runner
|
||||
self._loop = runner._loop
|
||||
self._queue: asyncio.Queue[Tuple[Value, bool]] = asyncio.Queue()
|
||||
self._closed = False
|
||||
|
||||
def _on_value(v: Value, expired: bool) -> bool:
|
||||
if self._closed:
|
||||
return False
|
||||
self._loop.call_soon_threadsafe(self._queue.put_nowait, (v, expired))
|
||||
return True # continue listening
|
||||
|
||||
self._token = self._runner._dht.listen(key, _on_value)
|
||||
|
||||
def __enter__(self) -> _Listener:
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc, tb) -> None:
|
||||
self.cancel()
|
||||
|
||||
def __aiter__(self) -> _Listener:
|
||||
return self
|
||||
|
||||
async def __anext__(self) -> Tuple[Value, bool]:
|
||||
if self._closed:
|
||||
raise StopAsyncIteration
|
||||
item = await self._queue.get()
|
||||
if item is None:
|
||||
self._closed = True
|
||||
raise StopAsyncIteration
|
||||
return item
|
||||
|
||||
def cancel(self) -> None:
|
||||
if self._closed:
|
||||
return
|
||||
self._closed = True
|
||||
try:
|
||||
self._runner._dht.cancelListen(self._token)
|
||||
finally:
|
||||
# Unblock any pending __anext__
|
||||
self._queue.put_nowait(None)
|
||||
|
||||
|
||||
class _GetStream:
|
||||
"""Async iterator around DHT get callback.
|
||||
|
||||
Yields Value instances as they are found, then ends.
|
||||
Raises on failure.
|
||||
"""
|
||||
__slots__ = ("_runner", "_loop", "_queue", "_closed", "_error")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
runner: DhtRunner,
|
||||
key: InfoHash,
|
||||
where: Optional[Where],
|
||||
filter: Optional[Callable[[Value], bool]],
|
||||
) -> None:
|
||||
self._runner = runner
|
||||
self._loop = runner._loop
|
||||
self._queue: asyncio.Queue[Optional[Value]] = asyncio.Queue()
|
||||
self._closed = False
|
||||
self._error: Optional[BaseException] = None
|
||||
|
||||
def _on_value(v: Value) -> bool:
|
||||
# Called on DHT thread
|
||||
if self._closed:
|
||||
return False
|
||||
self._loop.call_soon_threadsafe(self._queue.put_nowait, v)
|
||||
return True
|
||||
|
||||
def _on_done(ok: bool, _nodes: Iterable[Any]) -> None:
|
||||
# Called on DHT thread
|
||||
if self._closed:
|
||||
return
|
||||
if not ok:
|
||||
self._error = RuntimeError("DHT get failed")
|
||||
self._loop.call_soon_threadsafe(self._queue.put_nowait, None)
|
||||
|
||||
self._runner._dht.get(key, _on_value, _on_done, filter, where)
|
||||
|
||||
def __enter__(self) -> _GetStream:
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc, tb) -> None:
|
||||
self.cancel()
|
||||
|
||||
def __aiter__(self) -> _GetStream:
|
||||
return self
|
||||
|
||||
async def __anext__(self) -> Value:
|
||||
if self._closed:
|
||||
raise StopAsyncIteration
|
||||
item = await self._queue.get()
|
||||
if item is None:
|
||||
self._closed = True
|
||||
if self._error is not None:
|
||||
raise self._error
|
||||
raise StopAsyncIteration
|
||||
return item
|
||||
|
||||
def cancel(self) -> None:
|
||||
if self._closed:
|
||||
return
|
||||
self._closed = True
|
||||
# Unblock any pending __anext__
|
||||
self._queue.put_nowait(None)
|
||||
|
||||
|
||||
class DhtRunner:
|
||||
"""Async wrapper around opendht.DhtRunner.
|
||||
"""
|
||||
|
||||
__slots__ = ("_dht", "_loop", "_config", "_bootstrap")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
loop: Optional[asyncio.AbstractEventLoop] = None,
|
||||
core: Optional[_dht.DhtRunner] = None,
|
||||
config: Optional[DhtConfig] = None,
|
||||
bootstrap: Optional[Iterable[Tuple[str, Optional[str]]]] = None,
|
||||
) -> None:
|
||||
try:
|
||||
self._loop = loop or asyncio.get_running_loop()
|
||||
except RuntimeError:
|
||||
self._loop = asyncio.get_event_loop()
|
||||
self._dht = core or _dht.DhtRunner()
|
||||
self._config = config
|
||||
self._bootstrap = list(bootstrap) if bootstrap is not None else None
|
||||
|
||||
def run(
|
||||
self,
|
||||
id: Optional[Identity] = None,
|
||||
is_bootstrap: bool = False,
|
||||
port: int = 0,
|
||||
ipv4: str = "",
|
||||
ipv6: str = "",
|
||||
config: Optional[DhtConfig] = None,
|
||||
) -> None:
|
||||
cfg = config or self._config or DhtConfig()
|
||||
self._dht.run(id, is_bootstrap, port, ipv4, ipv6, cfg)
|
||||
if self._bootstrap:
|
||||
for host, port_str in self._bootstrap:
|
||||
self._dht.bootstrap(host, port_str)
|
||||
|
||||
def bootstrap(self, host: str, port: Optional[str] = None) -> None:
|
||||
self._dht.bootstrap(host, port)
|
||||
|
||||
async def ping(self, addr: SockAddr) -> bool:
|
||||
fut: asyncio.Future[bool] = self._loop.create_future()
|
||||
self._dht.ping(addr, lambda ok: self._loop.call_soon_threadsafe(fut.set_result, bool(ok)))
|
||||
return await fut
|
||||
|
||||
def get(
|
||||
self,
|
||||
key: InfoHash,
|
||||
*,
|
||||
where: Optional[Where] = None,
|
||||
filter: Optional[Callable[[Value], bool]] = None,
|
||||
):
|
||||
# Return a streaming iterator of values; it ends on completion and raises on failure.
|
||||
return _GetStream(self, key, where, filter)
|
||||
|
||||
async def getAll(
|
||||
self,
|
||||
key: InfoHash,
|
||||
*,
|
||||
where: Optional[Where] = None,
|
||||
filter: Optional[Callable[[Value], bool]] = None,
|
||||
) -> List[Value]:
|
||||
"""Collect all values for a key and return them as a list."""
|
||||
results: List[Value] = []
|
||||
async for v in self.get(key, where=where, filter=filter):
|
||||
results.append(v)
|
||||
return results
|
||||
|
||||
async def getFirst(
|
||||
self,
|
||||
key: InfoHash,
|
||||
*,
|
||||
where: Optional[Where] = None,
|
||||
predicate: Optional[Callable[[Value], Any]] = None,
|
||||
filter: Optional[Callable[[Value], bool]] = None,
|
||||
) -> Optional[Value]:
|
||||
"""Return the first value matching an (optionally async) predicate, or None."""
|
||||
with self.get(key, where=where, filter=filter) as stream:
|
||||
async for value in stream:
|
||||
if predicate is None:
|
||||
return value
|
||||
result = predicate(value)
|
||||
if asyncio.iscoroutine(result):
|
||||
result = await result
|
||||
if result:
|
||||
return value
|
||||
return None
|
||||
|
||||
async def put(self, key: InfoHash, val: Value, *, permanent: bool = False) -> bool:
|
||||
fut: asyncio.Future[bool] = self._loop.create_future()
|
||||
|
||||
def _done(ok: bool, _nodes: Iterable[Any]) -> None:
|
||||
self._loop.call_soon_threadsafe(fut.set_result, bool(ok))
|
||||
|
||||
self._dht.put(key, val, _done, permanent)
|
||||
return await fut
|
||||
|
||||
async def putSigned(self, key: InfoHash, val: Value, *, permanent: bool = False) -> bool:
|
||||
fut: asyncio.Future[bool] = self._loop.create_future()
|
||||
|
||||
def _done(ok: bool, _nodes: Iterable[Any]) -> None:
|
||||
self._loop.call_soon_threadsafe(fut.set_result, bool(ok))
|
||||
|
||||
self._dht.putSigned(key, val, _done, permanent)
|
||||
return await fut
|
||||
|
||||
async def putEncrypted(
|
||||
self,
|
||||
key: InfoHash,
|
||||
to: InfoHash | PublicKey | PkId,
|
||||
val: Value,
|
||||
*,
|
||||
permanent: bool = False,
|
||||
) -> bool:
|
||||
fut: asyncio.Future[bool] = self._loop.create_future()
|
||||
|
||||
def _done(ok: bool, _nodes: Iterable[Any]) -> None:
|
||||
self._loop.call_soon_threadsafe(fut.set_result, bool(ok))
|
||||
|
||||
self._dht.putEncrypted(key, val, to, _done, permanent)
|
||||
return await fut
|
||||
|
||||
def listen(self, key: InfoHash) -> _Listener:
|
||||
return _Listener(self, key)
|
||||
|
||||
async def cancelListen(self, listener: _Listener) -> None:
|
||||
await listener.cancel()
|
||||
|
||||
async def shutdown(self) -> None:
|
||||
fut: asyncio.Future[None] = self._loop.create_future()
|
||||
self._dht.shutdown(lambda: self._loop.call_soon_threadsafe(fut.set_result, None))
|
||||
await fut
|
||||
|
||||
async def join(self) -> None:
|
||||
await asyncio.to_thread(self._dht.join)
|
||||
|
||||
async def __aenter__(self):
|
||||
if not self.isRunning():
|
||||
self.run()
|
||||
return self
|
||||
|
||||
async def __aexit__(self, exc_type, exc, tb):
|
||||
if self.isRunning():
|
||||
try:
|
||||
await self.shutdown()
|
||||
finally:
|
||||
await self.join()
|
||||
return False
|
||||
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
return getattr(self._dht, name)
|
||||
+39
-10
@@ -1,31 +1,29 @@
|
||||
# Copyright (c) 2015-2023 Savoir-faire Linux Inc.
|
||||
# Author(s): Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
# Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
# Copyright (C) 2014-2025 Savoir-faire Linux Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; If not, see <https://www.gnu.org/licenses/>.
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
from libc.stdint cimport *
|
||||
from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t
|
||||
from libcpp cimport bool, nullptr_t, nullptr
|
||||
from libcpp.string cimport string
|
||||
from libcpp.vector cimport vector
|
||||
from libcpp.utility cimport pair
|
||||
from libcpp.map cimport map
|
||||
from libcpp.memory cimport shared_ptr, make_shared
|
||||
from libc.string cimport const_char, const_uchar
|
||||
from libc.string cimport const_char
|
||||
|
||||
ctypedef uint16_t in_port_t
|
||||
ctypedef unsigned short int sa_family_t;
|
||||
ctypedef unsigned short int sa_family_t
|
||||
|
||||
cdef extern from "<chrono>" namespace "std::chrono" nogil:
|
||||
cdef cppclass duration[ulong]:
|
||||
@@ -51,6 +49,7 @@ cdef extern from "<future>" namespace "std" nogil:
|
||||
shared_future[T] share()
|
||||
|
||||
cdef extern from "opendht/utils.h" namespace "dht":
|
||||
const char* version()
|
||||
cdef cppclass time_point:
|
||||
@staticmethod
|
||||
time_point min() const
|
||||
@@ -74,6 +73,24 @@ cdef extern from "opendht/infohash.h" namespace "dht":
|
||||
bool operator==(InfoHash) const
|
||||
bool operator<(InfoHash) const
|
||||
bool operator bool() const
|
||||
|
||||
cdef cppclass PkId:
|
||||
PkId() except +
|
||||
PkId(string s) except +
|
||||
string toString() const
|
||||
bool getBit(unsigned bit) const
|
||||
void setBit(unsigned bit, bool b)
|
||||
double toFloat() const
|
||||
@staticmethod
|
||||
unsigned commonBits(PkId a, PkId b)
|
||||
@staticmethod
|
||||
PkId get(string s)
|
||||
@staticmethod
|
||||
PkId getRandom()
|
||||
bool operator==(PkId) const
|
||||
bool operator<(PkId) const
|
||||
bool operator bool() const
|
||||
|
||||
|
||||
cdef extern from "opendht/sockaddr.h" namespace "dht":
|
||||
cdef cppclass SockAddr:
|
||||
@@ -101,6 +118,7 @@ cdef extern from "opendht/crypto.h" namespace "dht::crypto":
|
||||
PrivateKey()
|
||||
shared_ptr[PublicKey] getSharedPublicKey() const
|
||||
Blob decrypt(Blob data) const
|
||||
Blob sign(Blob data) const
|
||||
@staticmethod
|
||||
PrivateKey generate()
|
||||
@staticmethod
|
||||
@@ -109,12 +127,16 @@ cdef extern from "opendht/crypto.h" namespace "dht::crypto":
|
||||
cdef cppclass PublicKey:
|
||||
PublicKey()
|
||||
InfoHash getId() const
|
||||
PkId getLongId() const
|
||||
Blob encrypt(Blob data) const
|
||||
bool checkSignature(Blob data, Blob signature) const
|
||||
bool operator==(PublicKey) const
|
||||
|
||||
cdef cppclass Certificate:
|
||||
Certificate()
|
||||
Certificate(string pem)
|
||||
InfoHash getId() const
|
||||
PkId getLongId() const
|
||||
string toString() const
|
||||
string getName() const
|
||||
void revoke(PrivateKey key, Certificate cert)
|
||||
@@ -255,6 +277,7 @@ cdef extern from "opendht/dhtrunner.h" namespace "dht":
|
||||
SockAddr bind6
|
||||
|
||||
InfoHash getId() const
|
||||
shared_ptr[PublicKey] getPublicKey() const
|
||||
InfoHash getNodeId() const
|
||||
void bootstrap(const_char*, const_char*)
|
||||
void bootstrap(const SockAddr&, DoneCallbackSimple done_cb)
|
||||
@@ -272,6 +295,7 @@ cdef extern from "opendht/dhtrunner.h" namespace "dht":
|
||||
void put(InfoHash key, shared_ptr[Value] val, DoneCallback done_cb, time_point created, bool permanent)
|
||||
void putSigned(InfoHash key, shared_ptr[Value] val, DoneCallback done_cb, bool permanent)
|
||||
void putEncrypted(InfoHash key, InfoHash to, shared_ptr[Value] val, DoneCallback done_cb, bool permanent)
|
||||
void putEncrypted(InfoHash key, PkId to, shared_ptr[Value] val, DoneCallback done_cb, bool permanent)
|
||||
void putEncrypted(InfoHash key, shared_ptr[PublicKey] to, shared_ptr[Value] val, DoneCallback done_cb, bool permanent)
|
||||
void cancelPut(InfoHash key, shared_ptr[Value] val)
|
||||
ListenToken listen(InfoHash key, ValueCallback get_cb)
|
||||
@@ -303,3 +327,8 @@ cdef extern from "opendht/indexation/pht.h" namespace "dht::indexation":
|
||||
void insert(IndexKey k, IndexValue v, DoneCallbackSimple cb)
|
||||
@staticmethod
|
||||
LookupCallback bindLookupCb(LookupCallbackRaw cb, void *user_data)
|
||||
|
||||
cdef extern from "gnutls/gnutls.h" nogil:
|
||||
int gnutls_global_init()
|
||||
void gnutls_global_deinit()
|
||||
const char* gnutls_strerror(int error)
|
||||
@@ -1,3 +1,48 @@
|
||||
[build-system]
|
||||
requires = ["setuptools", "wheel", "Cython"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.cibuildwheel]
|
||||
build = [
|
||||
'cp3{9,10,11,12,13}-manylinux_x86_64',
|
||||
'cp3{10,11,12,13}-manylinux_aarch64',
|
||||
'cp3{11,12,13}-macosx_x86_64',
|
||||
'cp3{11,12,13}-macosx_arm64',
|
||||
'cp3{11,12,13}-win_amd64',
|
||||
]
|
||||
|
||||
[tool.cibuildwheel.linux]
|
||||
manylinux-x86_64-image = 'manylinux_2_34'
|
||||
manylinux-aarch64-image = 'manylinux_2_34'
|
||||
before-all = [
|
||||
'dnf install -y epel-release',
|
||||
'dnf install -y gnutls-devel msgpack-devel readline-devel libargon2-devel asio-devel fmt-devel',
|
||||
]
|
||||
before-build = [
|
||||
'pip install cython',
|
||||
'cmake -DOPENDHT_PYTHON=ON -DOPENDHT_TOOLS=OFF -DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=Release .',
|
||||
'cmake --build .',
|
||||
]
|
||||
# Point auditwheel to libopendht.so.* in the current dir when repairing
|
||||
repair-wheel-command = 'LD_LIBRARY_PATH=$PWD auditwheel repair -w {dest_dir} {wheel}'
|
||||
|
||||
[tool.cibuildwheel.macos]
|
||||
environment = { MACOSX_DEPLOYMENT_TARGET = "15.0" }
|
||||
before-all = [
|
||||
'brew update',
|
||||
'brew install gnutls msgpack-cxx readline argon2 asio fmt',
|
||||
]
|
||||
before-build = [
|
||||
'pip install cython delocate',
|
||||
'cmake -DOPENDHT_PYTHON=ON -DOPENDHT_TOOLS=OFF -DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=Release .',
|
||||
'cmake --build .',
|
||||
]
|
||||
# Bundle dependent dylibs into the wheel; ensure local libopendht.dylib is found
|
||||
repair-wheel-command = 'DYLD_LIBRARY_PATH=$PWD delocate-wheel -w {dest_dir} {wheel}'
|
||||
|
||||
[tool.cibuildwheel.windows]
|
||||
# Configure first, then from 'build' directory run: with pipx run cibuildwheel python
|
||||
before-build = [
|
||||
'cmake --build . --config Release',
|
||||
]
|
||||
repair-wheel-command = "delvewheel repair -w {dest_dir} {wheel} --add-path ./Release --add-path ./vcpkg_installed/x64-windows-release/bin"
|
||||
+58
-36
@@ -1,55 +1,77 @@
|
||||
# Copyright (C) 2014-2025 Savoir-faire Linux Inc.
|
||||
# Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
|
||||
# Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
#
|
||||
# A Python3 wrapper to access to OpenDHT API
|
||||
# This wrapper is written for Cython 0.22
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This file is part of OpenDHT Python Wrapper.
|
||||
#
|
||||
# OpenDHT Python Wrapper is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# OpenDHT Python Wrapper is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with OpenDHT Python Wrapper. If not, see <https://www.gnu.org/licenses/>.
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
from setuptools import setup, Extension
|
||||
from Cython.Build import cythonize
|
||||
from Cython.Distutils import build_ext
|
||||
from setuptools import setup, Extension, find_packages
|
||||
from setuptools.command.build_ext import build_ext as _build_ext
|
||||
import os
|
||||
|
||||
with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'README.md'), 'r', encoding='utf-8') as f:
|
||||
long_description = f.read()
|
||||
|
||||
extra_compile_args = {
|
||||
'msvc': ['/std:c++17', '/EHsc', '/utf-8'],
|
||||
'unix': ['-std=c++17'],
|
||||
}
|
||||
|
||||
extra_link_args = {
|
||||
'msvc': ['wsock32.lib', 'ws2_32.lib'],
|
||||
'unix': ['-std=c++17']
|
||||
}
|
||||
extra_libraries = {
|
||||
'msvc': ['opendht_import', 'gnutls'],
|
||||
'unix': ['opendht']
|
||||
}
|
||||
|
||||
class build_ext(_build_ext):
|
||||
def build_extensions(self):
|
||||
compile_args = extra_compile_args.get(self.compiler.compiler_type, [])
|
||||
link_args = extra_link_args.get(self.compiler.compiler_type, [])
|
||||
libraries = extra_libraries.get(self.compiler.compiler_type, ['opendht'])
|
||||
for ext in self.extensions:
|
||||
ext.extra_compile_args.extend(compile_args)
|
||||
ext.extra_link_args.extend(link_args)
|
||||
ext.libraries = libraries
|
||||
super().build_extensions()
|
||||
|
||||
setup(name="opendht",
|
||||
version="@PACKAGE_VERSION@",
|
||||
description="Python wrapper for OpenDHT",
|
||||
description="OpenDHT: A lightweight Distributed Hash Table",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
url='https://github.com/savoirfairelinux/opendht',
|
||||
author="Adrien Béraud, Guillaume Roguez, Simon Désaulniers",
|
||||
license="GPLv3",
|
||||
author="Adrien Beraud <adrien.beraud@savoirfairelinux.com>",
|
||||
license="GPL-3.0-or-later",
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Intended Audience :: Developers',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||
'Topic :: System :: Distributed Computing',
|
||||
'Topic :: System :: Networking'
|
||||
'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
|
||||
'Topic :: System :: Networking',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Programming Language :: Python :: 3.9',
|
||||
],
|
||||
cmdclass = { 'build_ext' : build_ext },
|
||||
ext_modules = cythonize(Extension(
|
||||
"opendht",
|
||||
cmdclass=dict(build_ext=build_ext),
|
||||
packages=find_packages(where='@CURRENT_SOURCE_DIR@'),
|
||||
package_dir={"": "@CURRENT_SOURCE_DIR@"},
|
||||
ext_modules=[Extension(
|
||||
"opendht._core",
|
||||
["@CURRENT_SOURCE_DIR@/opendht.pyx"],
|
||||
include_dirs = ['@PROJECT_SOURCE_DIR@/include'],
|
||||
language="c++",
|
||||
extra_compile_args=["-std=c++17"],
|
||||
extra_link_args=["-std=c++17"],
|
||||
libraries=["opendht"],
|
||||
library_dirs = ['@CURRENT_BINARY_DIR@', '@PROJECT_BINARY_DIR@']
|
||||
))
|
||||
extra_compile_args=["-DMSGPACK_NO_BOOST", "-DMSGPACK_DISABLE_LEGACY_NIL", "-DMSGPACK_DISABLE_LEGACY_CONVERT", "-DOPENDHT_INDEXATION", "-Dopendht_EXPORTS"],
|
||||
libraries=["opendht", "gnutls"],
|
||||
include_dirs = ['@PROJECT_SOURCE_DIR@/include'@OPENDHT_PYTHON_INCLUDE_DIRS@],
|
||||
library_dirs = ['@CURRENT_BINARY_DIR@', '@PROJECT_BINARY_DIR@'@OPENDHT_PYTHON_LIBRARY_DIRS@]
|
||||
)]
|
||||
)
|
||||
+46
-48
@@ -15,64 +15,62 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import opendht as dht
|
||||
import time
|
||||
import asyncio
|
||||
import time
|
||||
|
||||
config = dht.DhtConfig()
|
||||
config.setRateLimit(-1, -1)
|
||||
from opendht import aio as dht
|
||||
|
||||
ping_node = dht.DhtRunner()
|
||||
ping_node.run(config=config)
|
||||
#ping_node.enableLogging()
|
||||
#ping_node.bootstrap("bootstrap.jami.net", "4222")
|
||||
pong_node = dht.DhtRunner()
|
||||
pong_node.run(config=config)
|
||||
#pong_node.enableLogging()
|
||||
pong_node.ping(ping_node.getBound())
|
||||
|
||||
loc_ping = dht.InfoHash.get("toto99")
|
||||
loc_pong = dht.InfoHash.get(str(loc_ping))
|
||||
async def main():
|
||||
config = dht.DhtConfig()
|
||||
config.setRateLimit(-1, -1)
|
||||
|
||||
net = []
|
||||
for i in range(1,10):
|
||||
node = dht.DhtRunner()
|
||||
node.run(config=config)
|
||||
node.ping(ping_node.getBound())
|
||||
net.append(node)
|
||||
ping_node = dht.DhtRunner()
|
||||
pong_node = dht.DhtRunner()
|
||||
# ping_node.enableLogging()
|
||||
# pong_node.enableLogging()
|
||||
# ping_node.bootstrap("bootstrap.jami.net", "4222")
|
||||
ping_node.run(config=config)
|
||||
pong_node.run(config=config)
|
||||
await pong_node.ping(ping_node.getBound())
|
||||
|
||||
i = 0
|
||||
MAX = 2048
|
||||
net = [dht.DhtRunner() for _ in range(1, 10)]
|
||||
for n in net:
|
||||
n.run(config=config)
|
||||
n.ping(ping_node.getBound())
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
MAX = 2048
|
||||
counter = 0
|
||||
loc_ping = dht.InfoHash.get(f"ping-{ping_node.getNodeId()}")
|
||||
loc_pong = dht.InfoHash.get(str(loc_ping))
|
||||
|
||||
def done(h, ok):
|
||||
pass
|
||||
#print(h, "over", ok)
|
||||
async def ponger(listener_node: dht.DhtRunner, listen_key, responder_node: dht.DhtRunner, respond_key):
|
||||
nonlocal counter
|
||||
with listener_node.listen(listen_key) as listener:
|
||||
async for value, expired in listener:
|
||||
if not expired:
|
||||
await responder_node.put(respond_key, dht.Value(b"hey"))
|
||||
counter += 1
|
||||
if counter >= MAX:
|
||||
break
|
||||
|
||||
def ping(node, h):
|
||||
global i
|
||||
i += 1
|
||||
if i < MAX:
|
||||
node.put(h, dht.Value(b"hey"), lambda ok, nodes: done(node.getNodeId().decode(), ok))
|
||||
else:
|
||||
loop.stop()
|
||||
start = time.time()
|
||||
await asyncio.gather(
|
||||
ponger(ping_node, loc_ping, pong_node, loc_pong),
|
||||
ponger(pong_node, loc_pong, ping_node, loc_ping),
|
||||
pong_node.put(loc_ping, dht.Value(b"hey"))
|
||||
)
|
||||
duration = time.time() - start
|
||||
|
||||
def pong(node, h):
|
||||
#print(node.getNodeId().decode(), "got ping", h, i)
|
||||
loop.call_soon_threadsafe(ping, node, h)
|
||||
return True
|
||||
await asyncio.gather(
|
||||
ping_node.shutdown(),
|
||||
pong_node.shutdown(),
|
||||
*(n.shutdown() for n in net),
|
||||
)
|
||||
|
||||
t1 = time.time()
|
||||
print(MAX, "ping-pong done, took", duration, "s")
|
||||
print(1000 * duration / MAX, "ms per rt", MAX / duration, "rt per s")
|
||||
|
||||
ping_node.listen(loc_ping, lambda v, e: pong(pong_node, loc_pong) if not e else True)
|
||||
pong_node.listen(loc_pong, lambda v, e: pong(ping_node, loc_ping) if not e else True)
|
||||
|
||||
ping(pong_node, loc_ping)
|
||||
|
||||
loop.run_forever()
|
||||
|
||||
t2 = time.time()
|
||||
|
||||
print(MAX, "ping-pong done, took", t2 - t1, "s")
|
||||
print(1000 * (t2 - t1)/MAX, "ms per rt", MAX/(t2 - t1), "rt per s")
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -1,19 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2023 Savoir-faire Linux Inc.
|
||||
# Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
# Copyright (C) 2014-2025 Savoir-faire Linux Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import time, sys, os
|
||||
from pprint import pprint
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
lib_LTLIBRARIES = libopendht.la
|
||||
|
||||
libopendht_la_CPPFLAGS = @CPPFLAGS@ -I$(top_srcdir)/include/opendht @Argon2_CFLAGS@ @JsonCpp_CFLAGS@ @MsgPack_CFLAGS@ @OpenSSL_CFLAGS@ @Fmt_CFLAGS@
|
||||
libopendht_la_LIBADD = @Argon2_LIBS@ @JsonCpp_LIBS@ @GnuTLS_LIBS@ @Nettle_LIBS@ @OpenSSL_LIBS@ @Fmt_LIBS@
|
||||
libopendht_la_CPPFLAGS = @CPPFLAGS@ -I$(top_srcdir)/include/opendht @Argon2_CFLAGS@ @JsonCpp_CFLAGS@ @MsgPack_CFLAGS@ @OpenSSL_CFLAGS@ @Fmt_CFLAGS@ @Llhttp_CFLAGS@ @Liburing_CFLAGS@ @simdutf_CFLAGS@
|
||||
libopendht_la_LIBADD = @Argon2_LIBS@ @JsonCpp_LIBS@ @GnuTLS_LIBS@ @Nettle_LIBS@ @OpenSSL_LIBS@ @Fmt_LIBS@ @Llhttp_LIBS@ @Liburing_LIBS@ @simdutf_LIBS@
|
||||
libopendht_la_LDFLAGS = @LDFLAGS@ -version-number @OPENDHT_MAJOR_VERSION@:@OPENDHT_MINOR_VERSION@:@OPENDHT_PATCH_VERSION@
|
||||
libopendht_la_SOURCES = \
|
||||
dht.cpp \
|
||||
|
||||
+21
-111
@@ -16,128 +16,38 @@
|
||||
*/
|
||||
|
||||
#include "base64.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <simdutf.h>
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
|
||||
/* Mainly based on the following stackoverflow question:
|
||||
* http://stackoverflow.com/questions/342409/how-do-i-base64-encode-decode-in-c
|
||||
*/
|
||||
static const char encoding_table[] = {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
|
||||
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
|
||||
'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
|
||||
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
|
||||
's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2',
|
||||
'3', '4', '5', '6', '7', '8', '9', '+', '/'
|
||||
};
|
||||
|
||||
static const size_t mod_table[] = { 0, 2, 1 };
|
||||
|
||||
char *base64_encode(const uint8_t *input, size_t input_length,
|
||||
char *output, size_t *output_length)
|
||||
{
|
||||
size_t i, j;
|
||||
size_t out_sz = *output_length;
|
||||
*output_length = 4 * ((input_length + 2) / 3);
|
||||
if (out_sz < *output_length || output == nullptr)
|
||||
return nullptr;
|
||||
|
||||
for (i = 0, j = 0; i < input_length; ) {
|
||||
uint8_t octet_a = i < input_length ? input[i++] : 0;
|
||||
uint8_t octet_b = i < input_length ? input[i++] : 0;
|
||||
uint8_t octet_c = i < input_length ? input[i++] : 0;
|
||||
|
||||
uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
|
||||
|
||||
output[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
|
||||
output[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
|
||||
output[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
|
||||
output[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
|
||||
}
|
||||
|
||||
for (i = 0; i < mod_table[input_length % 3]; i++)
|
||||
output[*output_length - 1 - i] = '=';
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
uint8_t *base64_decode(const char *input, size_t input_length,
|
||||
uint8_t *output, size_t *output_length)
|
||||
{
|
||||
size_t i, j;
|
||||
uint8_t decoding_table[256];
|
||||
|
||||
uint8_t c;
|
||||
for (c = 0; c < 64; c++)
|
||||
decoding_table[static_cast<int>(encoding_table[c])] = c;
|
||||
|
||||
if (input_length % 4 != 0 || input_length < 2)
|
||||
return nullptr;
|
||||
|
||||
size_t out_sz = *output_length;
|
||||
*output_length = input_length / 4 * 3;
|
||||
if (input[input_length - 1] == '=')
|
||||
(*output_length)--;
|
||||
if (input[input_length - 2] == '=')
|
||||
(*output_length)--;
|
||||
|
||||
if (out_sz < *output_length || output == nullptr)
|
||||
return nullptr;
|
||||
|
||||
for (i = 0, j = 0; i < input_length;) {
|
||||
uint8_t sextet_a = input[i] == '=' ? 0 & i++
|
||||
: decoding_table[static_cast<int>(input[i++])];
|
||||
uint8_t sextet_b = input[i] == '=' ? 0 & i++
|
||||
: decoding_table[static_cast<int>(input[i++])];
|
||||
uint8_t sextet_c = input[i] == '=' ? 0 & i++
|
||||
: decoding_table[static_cast<int>(input[i++])];
|
||||
uint8_t sextet_d = input[i] == '=' ? 0 & i++
|
||||
: decoding_table[static_cast<int>(input[i++])];
|
||||
|
||||
uint32_t triple = (sextet_a << 3 * 6) +
|
||||
(sextet_b << 2 * 6) +
|
||||
(sextet_c << 1 * 6) +
|
||||
(sextet_d << 0 * 6);
|
||||
|
||||
if (j < *output_length)
|
||||
output[j++] = (triple >> 2 * 8) & 0xFF;
|
||||
if (j < *output_length)
|
||||
output[j++] = (triple >> 1 * 8) & 0xFF;
|
||||
if (j < *output_length)
|
||||
output[j++] = (triple >> 0 * 8) & 0xFF;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string
|
||||
base64_encode(const std::vector<uint8_t>::const_iterator begin,
|
||||
const std::vector<uint8_t>::const_iterator end)
|
||||
{
|
||||
size_t output_length = 4 * ((std::distance(begin, end) + 2) / 3);
|
||||
std::string out;
|
||||
out.resize(output_length);
|
||||
base64_encode(&(*begin), std::distance(begin, end),
|
||||
&(*out.begin()), &output_length);
|
||||
out.resize(output_length);
|
||||
return out;
|
||||
}
|
||||
namespace dht {
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
std::string
|
||||
base64_encode(const std::vector<unsigned char>& str)
|
||||
{
|
||||
return base64_encode(str.cbegin(), str.cend());
|
||||
std::string buffer(simdutf::base64_length_from_binary(str.size()), '\0');
|
||||
simdutf::binary_to_base64((const char*) str.data(), str.size(), buffer.data());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::vector<unsigned char>
|
||||
base64_decode(const std::string& str)
|
||||
base64_decode(std::string_view str)
|
||||
{
|
||||
size_t output_length = str.length() / 4 * 3 + 2;
|
||||
std::vector<uint8_t> output;
|
||||
output.resize(output_length);
|
||||
base64_decode(str.data(), str.size(), output.data(), &output_length);
|
||||
output.resize(output_length);
|
||||
return output;
|
||||
std::vector<unsigned char> buffer(simdutf::maximal_binary_length_from_base64(str.data(), str.size()));
|
||||
simdutf::result r = simdutf::base64_to_binary(str.data(), str.size(), (char*) buffer.data());
|
||||
if (r.error) {
|
||||
throw std::invalid_argument(concat("Invalid base64 input: "sv, simdutf::error_to_string(r.error)));
|
||||
} else {
|
||||
buffer.resize(r.count);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
} // namespace dht
|
||||
+5
-1
@@ -20,6 +20,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace dht {
|
||||
|
||||
/**
|
||||
* Encode a buffer in base64.
|
||||
*
|
||||
@@ -33,4 +35,6 @@ std::string base64_encode(const std::vector<unsigned char>& str);
|
||||
* @param str the input buffer
|
||||
* @return a base64-decoded buffer
|
||||
*/
|
||||
std::vector<unsigned char> base64_decode(const std::string& str);
|
||||
std::vector<unsigned char> base64_decode(std::string_view str);
|
||||
|
||||
} // namespace dht
|
||||
@@ -51,6 +51,4 @@
|
||||
typedef int mode_t;
|
||||
#include <BaseTsd.h>
|
||||
|
||||
#define usleep(x) Sleep((x) / 1000)
|
||||
|
||||
#endif
|
||||
+585
-241
File diff suppressed because it is too large.
Load diff
+295
-257
File diff suppressed because it is too large.
Load diff
+451
-278
File diff suppressed because it is too large.
Load diff
+193
-142
@@ -40,18 +40,20 @@ using namespace std::literals;
|
||||
|
||||
constexpr auto PEER_DISCOVERY_DHT_SERVICE = "dht"sv;
|
||||
|
||||
struct NodeInsertionPack {
|
||||
struct NodeInsertionPack
|
||||
{
|
||||
dht::InfoHash nodeId;
|
||||
in_port_t port;
|
||||
dht::NetId net;
|
||||
MSGPACK_DEFINE(nodeId, port, net)
|
||||
};
|
||||
|
||||
DhtRunner::DhtRunner() : dht_()
|
||||
DhtRunner::DhtRunner()
|
||||
: dht_()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSADATA wsd;
|
||||
if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
|
||||
throw DhtException("Can't initialize Winsock2");
|
||||
#endif
|
||||
}
|
||||
@@ -95,7 +97,8 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
auto expected = State::Idle;
|
||||
if (not running.compare_exchange_strong(expected, State::Running)) {
|
||||
if (context.logger)
|
||||
context.logger->w("[runner %p] Node is already running. Call join() first before calling run() again.", fmt::ptr(this));
|
||||
context.logger->w("[runner %p] Node is already running. Call join() first before calling run() again.",
|
||||
fmt::ptr(this));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -104,7 +107,9 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
auto local6 = config.bind6;
|
||||
if (not local4 and not local6) {
|
||||
if (context.logger)
|
||||
context.logger->w("[runner %p] No address to bind specified in the configuration, using default addresses", fmt::ptr(this));
|
||||
context.logger
|
||||
->w("[runner %p] No address to bind specified in the configuration, using default addresses",
|
||||
fmt::ptr(this));
|
||||
local4.setFamily(AF_INET);
|
||||
local6.setFamily(AF_INET6);
|
||||
}
|
||||
@@ -118,14 +123,18 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
if (inConfig >> port) {
|
||||
if (local4.getPort() == 0) {
|
||||
if (context.logger)
|
||||
context.logger->d("[runner %p] Using IPv4 port %hu from saved configuration", fmt::ptr(this), port);
|
||||
context.logger->d("[runner %p] Using IPv4 port %hu from saved configuration",
|
||||
fmt::ptr(this),
|
||||
port);
|
||||
local4.setPort(port);
|
||||
}
|
||||
}
|
||||
if (inConfig >> port) {
|
||||
if (local6.getPort() == 0) {
|
||||
if (context.logger)
|
||||
context.logger->d("[runner %p] Using IPv6 port %hu from saved configuration", fmt::ptr(this), port);
|
||||
context.logger->d("[runner %p] Using IPv6 port %hu from saved configuration",
|
||||
fmt::ptr(this),
|
||||
port);
|
||||
local6.setPort(port);
|
||||
}
|
||||
}
|
||||
@@ -146,7 +155,7 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
if (not context.sock) {
|
||||
context.sock.reset(new net::UdpSocket(local4, local6, context.logger));
|
||||
}
|
||||
context.sock->setOnReceive([&] (net::PacketList&& pkts) {
|
||||
context.sock->setOnReceive([&](net::PacketList&& pkts) {
|
||||
net::PacketList ret;
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(sock_mtx);
|
||||
@@ -169,12 +178,18 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
outConfig << context.sock->getBoundRef(AF_INET).getPort() << std::endl;
|
||||
outConfig << context.sock->getBoundRef(AF_INET6).getPort() << std::endl;
|
||||
}
|
||||
auto dht = std::make_unique<Dht>(std::move(context.sock), SecureDht::getConfig(config.dht_config), context.logger, std::move(context.rng));
|
||||
dht_ = std::make_unique<SecureDht>(std::move(dht), config.dht_config, std::move(context.identityAnnouncedCb), context.logger);
|
||||
auto dht = std::make_unique<Dht>(std::move(context.sock),
|
||||
SecureDht::getConfig(config.dht_config),
|
||||
context.logger,
|
||||
std::move(context.rng));
|
||||
dht_ = std::make_unique<SecureDht>(std::move(dht),
|
||||
config.dht_config,
|
||||
std::move(context.identityAnnouncedCb),
|
||||
context.logger);
|
||||
} else {
|
||||
enableProxy(true);
|
||||
}
|
||||
} catch(const std::exception& e) {
|
||||
} catch (const std::exception& e) {
|
||||
config_ = {};
|
||||
identityAnnouncedCb_ = {};
|
||||
dht_.reset();
|
||||
@@ -188,6 +203,9 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
if (context.certificateStore) {
|
||||
dht_->setLocalCertificateStore(std::move(context.certificateStore));
|
||||
}
|
||||
if (context.certificateStorePkId) {
|
||||
dht_->setLocalCertificateStore(std::move(context.certificateStorePkId));
|
||||
}
|
||||
if (context.publicAddressChangedCb) {
|
||||
dht_->setOnPublicAddressChanged(std::move(context.publicAddressChangedCb));
|
||||
}
|
||||
@@ -212,7 +230,8 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
if (not pending_ops_prio.empty())
|
||||
return true;
|
||||
auto s = getStatus();
|
||||
if (not pending_ops.empty() and (s == NodeStatus::Connected or s == NodeStatus::Disconnected or running == State::Stopping))
|
||||
if (not pending_ops.empty()
|
||||
and (s == NodeStatus::Connected or s == NodeStatus::Disconnected or running == State::Stopping))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -227,9 +246,8 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
if (config.proxy_server.empty()) {
|
||||
if (config.peer_discovery or config.peer_publish) {
|
||||
#ifdef OPENDHT_PEER_DISCOVERY
|
||||
peerDiscovery_ = context.peerDiscovery ?
|
||||
std::move(context.peerDiscovery) :
|
||||
std::make_shared<PeerDiscovery>();
|
||||
peerDiscovery_ = context.peerDiscovery ? std::move(context.peerDiscovery)
|
||||
: std::make_shared<PeerDiscovery>();
|
||||
#else
|
||||
std::cerr << "Peer discovery requested but OpenDHT built without peer discovery support." << std::endl;
|
||||
#endif
|
||||
@@ -238,12 +256,13 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
#ifdef OPENDHT_PEER_DISCOVERY
|
||||
auto netId = config.dht_config.node_config.network;
|
||||
if (config.peer_discovery) {
|
||||
peerDiscovery_->startDiscovery<NodeInsertionPack>(PEER_DISCOVERY_DHT_SERVICE, [this, netId](NodeInsertionPack&& v, SockAddr&& addr){
|
||||
addr.setPort(v.port);
|
||||
if (v.nodeId != dht_->getNodeId() && netId == v.net){
|
||||
bootstrap(v.nodeId, addr);
|
||||
}
|
||||
});
|
||||
peerDiscovery_->startDiscovery<NodeInsertionPack>(PEER_DISCOVERY_DHT_SERVICE,
|
||||
[this, netId](NodeInsertionPack&& v, SockAddr&& addr) {
|
||||
addr.setPort(v.port);
|
||||
if (v.nodeId != dht_->getNodeId() && netId == v.net) {
|
||||
bootstrap(v.nodeId, addr);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (config.peer_publish) {
|
||||
msgpack::sbuffer sbuf_node;
|
||||
@@ -270,11 +289,9 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
statusCbs.emplace_back([this](NodeStatus status4, NodeStatus status6) {
|
||||
if (status4 == NodeStatus::Disconnected && status6 == NodeStatus::Disconnected) {
|
||||
peerDiscovery_->connectivityChanged();
|
||||
}
|
||||
else if (status4 != NodeStatus::Connected || status6 != NodeStatus::Connected) {
|
||||
} else if (status4 != NodeStatus::Connected || status6 != NodeStatus::Connected) {
|
||||
peerDiscovery_->stopConnectivityChanged();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
#endif
|
||||
@@ -282,15 +299,15 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::shutdown(ShutdownCallback cb, bool stop) {
|
||||
DhtRunner::shutdown(ShutdownCallback cb, bool stop)
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(storage_mtx);
|
||||
auto expected = State::Running;
|
||||
if (not running.compare_exchange_strong(expected, State::Stopping)) {
|
||||
if (expected == State::Stopping and ongoing_ops) {
|
||||
if (cb)
|
||||
shutdownCallbacks_.emplace_back(std::move(cb));
|
||||
}
|
||||
else if (cb) {
|
||||
} else if (cb) {
|
||||
lck.unlock();
|
||||
cb();
|
||||
}
|
||||
@@ -301,7 +318,9 @@ DhtRunner::shutdown(ShutdownCallback cb, bool stop) {
|
||||
ongoing_ops++;
|
||||
shutdownCallbacks_.emplace_back(std::move(cb));
|
||||
pending_ops.emplace([=](SecureDht&) mutable {
|
||||
auto onShutdown = [this]{ opEnded(); };
|
||||
auto onShutdown = [this] {
|
||||
opEnded();
|
||||
};
|
||||
if (dht_)
|
||||
dht_->shutdown(onShutdown, stop);
|
||||
else
|
||||
@@ -311,29 +330,35 @@ DhtRunner::shutdown(ShutdownCallback cb, bool stop) {
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::opEnded() {
|
||||
DhtRunner::opEnded()
|
||||
{
|
||||
if (--ongoing_ops == 0)
|
||||
checkShutdown();
|
||||
}
|
||||
|
||||
DoneCallback
|
||||
DhtRunner::bindOpDoneCallback(DoneCallback&& cb) {
|
||||
return [this, cb = std::move(cb)](bool ok, const std::vector<std::shared_ptr<Node>>& nodes){
|
||||
if (cb) cb(ok, nodes);
|
||||
DhtRunner::bindOpDoneCallback(DoneCallback&& cb)
|
||||
{
|
||||
return [this, cb = std::move(cb)](bool ok, const std::vector<std::shared_ptr<Node>>& nodes) {
|
||||
if (cb)
|
||||
cb(ok, nodes);
|
||||
opEnded();
|
||||
};
|
||||
}
|
||||
|
||||
DoneCallbackSimple
|
||||
DhtRunner::bindOpDoneCallback(DoneCallbackSimple&& cb) {
|
||||
return [this, cb = std::move(cb)](bool ok){
|
||||
if (cb) cb(ok);
|
||||
DhtRunner::bindOpDoneCallback(DoneCallbackSimple&& cb)
|
||||
{
|
||||
return [this, cb = std::move(cb)](bool ok) {
|
||||
if (cb)
|
||||
cb(ok);
|
||||
opEnded();
|
||||
};
|
||||
}
|
||||
|
||||
bool
|
||||
DhtRunner::checkShutdown() {
|
||||
DhtRunner::checkShutdown()
|
||||
{
|
||||
decltype(shutdownCallbacks_) cbs;
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
@@ -342,7 +367,8 @@ DhtRunner::checkShutdown() {
|
||||
cbs = std::move(shutdownCallbacks_);
|
||||
}
|
||||
for (auto& cb : cbs)
|
||||
if (cb) cb();
|
||||
if (cb)
|
||||
cb();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -387,16 +413,18 @@ DhtRunner::join()
|
||||
}
|
||||
|
||||
SockAddr
|
||||
DhtRunner::getBound(sa_family_t af) const {
|
||||
DhtRunner::getBound(sa_family_t af) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
if (dht_)
|
||||
if (auto sock = dht_->getSocket())
|
||||
return sock->getBound(af);
|
||||
return SockAddr{};
|
||||
return SockAddr {};
|
||||
}
|
||||
|
||||
in_port_t
|
||||
DhtRunner::getBoundPort(sa_family_t af) const {
|
||||
DhtRunner::getBoundPort(sa_family_t af) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
if (dht_)
|
||||
if (auto sock = dht_->getSocket())
|
||||
@@ -414,24 +442,24 @@ DhtRunner::dumpTables() const
|
||||
InfoHash
|
||||
DhtRunner::getId() const
|
||||
{
|
||||
return dht_ ? dht_->getId() : InfoHash{};
|
||||
return dht_ ? dht_->getId() : InfoHash {};
|
||||
}
|
||||
|
||||
std::shared_ptr<crypto::PublicKey>
|
||||
DhtRunner::getPublicKey() const
|
||||
{
|
||||
return dht_ ? dht_->getPublicKey() : std::shared_ptr<crypto::PublicKey>{};
|
||||
return dht_ ? dht_->getPublicKey() : std::shared_ptr<crypto::PublicKey> {};
|
||||
}
|
||||
|
||||
InfoHash
|
||||
DhtRunner::getNodeId() const
|
||||
{
|
||||
return dht_ ? dht_->getNodeId() : InfoHash{};
|
||||
return dht_ ? dht_->getNodeId() : InfoHash {};
|
||||
}
|
||||
|
||||
|
||||
std::pair<size_t, size_t>
|
||||
DhtRunner::getStoreSize() const {
|
||||
DhtRunner::getStoreSize() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
if (!dht_)
|
||||
return {};
|
||||
@@ -439,7 +467,8 @@ DhtRunner::getStoreSize() const {
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::setStorageLimit(size_t limit) {
|
||||
DhtRunner::setStorageLimit(size_t limit)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
if (!dht_)
|
||||
throw std::runtime_error("dht is not running");
|
||||
@@ -447,7 +476,8 @@ DhtRunner::setStorageLimit(size_t limit) {
|
||||
}
|
||||
|
||||
std::vector<NodeExport>
|
||||
DhtRunner::exportNodes() const {
|
||||
DhtRunner::exportNodes() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
if (!dht_)
|
||||
return {};
|
||||
@@ -455,7 +485,8 @@ DhtRunner::exportNodes() const {
|
||||
}
|
||||
|
||||
std::vector<ValuesExport>
|
||||
DhtRunner::exportValues() const {
|
||||
DhtRunner::exportValues() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
if (!dht_)
|
||||
return {};
|
||||
@@ -463,7 +494,8 @@ DhtRunner::exportValues() const {
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::setLogger(const Sp<Logger>& logger) {
|
||||
DhtRunner::setLogger(const Sp<Logger>& logger)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
logger_ = logger;
|
||||
if (dht_)
|
||||
@@ -471,26 +503,40 @@ DhtRunner::setLogger(const Sp<Logger>& logger) {
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::setLogFilter(const InfoHash& f) {
|
||||
DhtRunner::setLogFilter(const InfoHash& f)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
if (dht_)
|
||||
dht_->setLogFilter(f);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::registerType(const ValueType& type) {
|
||||
DhtRunner::registerType(const ValueType& type)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
dht_->registerType(type);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::importValues(const std::vector<ValuesExport>& values) {
|
||||
DhtRunner::registerInsecureType(const ValueType& type)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
dht_->registerInsecureType(type);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::importValues(const std::vector<ValuesExport>& values)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
dht_->importValues(values);
|
||||
}
|
||||
|
||||
unsigned
|
||||
DhtRunner::getNodesStats(sa_family_t af, unsigned *good_return, unsigned *dubious_return, unsigned *cached_return, unsigned *incoming_return) const
|
||||
DhtRunner::getNodesStats(sa_family_t af,
|
||||
unsigned* good_return,
|
||||
unsigned* dubious_return,
|
||||
unsigned* cached_return,
|
||||
unsigned* incoming_return) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
const auto stats = dht_->getNodesStats(af);
|
||||
@@ -513,7 +559,8 @@ DhtRunner::getNodesStats(sa_family_t af) const
|
||||
}
|
||||
|
||||
NodeInfo
|
||||
DhtRunner::getNodeInfo() const {
|
||||
DhtRunner::getNodeInfo() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
NodeInfo info {};
|
||||
if (dht_) {
|
||||
@@ -535,7 +582,7 @@ DhtRunner::getNodeInfo(std::function<void(std::shared_ptr<NodeInfo>)> cb)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
ongoing_ops++;
|
||||
pending_ops_prio.emplace([cb = std::move(cb), this](SecureDht& dht){
|
||||
pending_ops_prio.emplace([cb = std::move(cb), this](SecureDht& dht) {
|
||||
auto sinfo = std::make_shared<NodeInfo>();
|
||||
auto& info = *sinfo;
|
||||
info.id = dht.getId();
|
||||
@@ -613,7 +660,7 @@ DhtRunner::getPublicAddress(std::function<void(std::vector<SockAddr>&&)> cb, sa_
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
ongoing_ops++;
|
||||
pending_ops_prio.emplace([cb = std::move(cb), this, af](SecureDht& dht){
|
||||
pending_ops_prio.emplace([cb = std::move(cb), this, af](SecureDht& dht) {
|
||||
cb(dht.getPublicAddress(af));
|
||||
opEnded();
|
||||
});
|
||||
@@ -621,13 +668,23 @@ DhtRunner::getPublicAddress(std::function<void(std::vector<SockAddr>&&)> cb, sa_
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::registerCertificate(const std::shared_ptr<crypto::Certificate>& cert) {
|
||||
DhtRunner::registerCertificate(const std::shared_ptr<crypto::Certificate>& cert)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
dht_->registerCertificate(cert);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::setLocalCertificateStore(CertificateStoreQuery&& query_method) {
|
||||
DhtRunner::setLocalCertificateStore(CertificateStoreQueryLegacy&& query_method)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
if (dht_)
|
||||
dht_->setLocalCertificateStore(std::forward<CertificateStoreQueryLegacy>(query_method));
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::setLocalCertificateStore(CertificateStoreQuery&& query_method)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
if (dht_)
|
||||
dht_->setLocalCertificateStore(std::forward<CertificateStoreQuery>(query_method));
|
||||
@@ -643,8 +700,10 @@ DhtRunner::loop_()
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
auto s = getStatus();
|
||||
ops = (pending_ops_prio.empty() && (s == NodeStatus::Connected or s == NodeStatus::Disconnected or running == State::Stopping)) ?
|
||||
std::move(pending_ops) : std::move(pending_ops_prio);
|
||||
ops = (pending_ops_prio.empty()
|
||||
&& (s == NodeStatus::Connected or s == NodeStatus::Disconnected or running == State::Stopping))
|
||||
? std::move(pending_ops)
|
||||
: std::move(pending_ops_prio);
|
||||
}
|
||||
while (not ops.empty()) {
|
||||
ops.front()(*dht_);
|
||||
@@ -703,7 +762,7 @@ DhtRunner::loop_()
|
||||
if (nstatus4 != status4 || nstatus6 != status6) {
|
||||
status4 = nstatus4;
|
||||
status6 = nstatus6;
|
||||
for (auto& cb : statusCbs){
|
||||
for (auto& cb : statusCbs) {
|
||||
cb(status4, status6);
|
||||
}
|
||||
}
|
||||
@@ -717,7 +776,8 @@ DhtRunner::get(InfoHash hash, GetCallback vcb, DoneCallback dcb, Value::Filter f
|
||||
std::unique_lock<std::mutex> lck(storage_mtx);
|
||||
if (running != State::Running) {
|
||||
lck.unlock();
|
||||
if (dcb) dcb(false, {});
|
||||
if (dcb)
|
||||
dcb(false, {});
|
||||
return;
|
||||
}
|
||||
ongoing_ops++;
|
||||
@@ -733,11 +793,13 @@ DhtRunner::get(const std::string& key, GetCallback vcb, DoneCallbackSimple dcb,
|
||||
get(InfoHash::get(key), std::move(vcb), std::move(dcb), std::move(f), std::move(w));
|
||||
}
|
||||
void
|
||||
DhtRunner::query(const InfoHash& hash, QueryCallback cb, DoneCallback done_cb, Query q) {
|
||||
DhtRunner::query(const InfoHash& hash, QueryCallback cb, DoneCallback done_cb, Query q)
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(storage_mtx);
|
||||
if (running != State::Running) {
|
||||
lck.unlock();
|
||||
if (done_cb) done_cb(false, {});
|
||||
if (done_cb)
|
||||
done_cb(false, {});
|
||||
return;
|
||||
}
|
||||
ongoing_ops++;
|
||||
@@ -804,14 +866,12 @@ DhtRunner::put(InfoHash hash, Value&& value, DoneCallback cb, time_point created
|
||||
std::unique_lock<std::mutex> lck(storage_mtx);
|
||||
if (running != State::Running) {
|
||||
lck.unlock();
|
||||
if (cb) cb(false, {});
|
||||
if (cb)
|
||||
cb(false, {});
|
||||
return;
|
||||
}
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([=,
|
||||
cb = std::move(cb),
|
||||
sv = std::make_shared<Value>(std::move(value))
|
||||
] (SecureDht& dht) mutable {
|
||||
pending_ops.emplace([=, cb = std::move(cb), sv = std::make_shared<Value>(std::move(value))](SecureDht& dht) mutable {
|
||||
dht.put(hash, sv, bindOpDoneCallback(std::move(cb)), created, permanent);
|
||||
});
|
||||
cv.notify_all();
|
||||
@@ -823,7 +883,8 @@ DhtRunner::put(InfoHash hash, std::shared_ptr<Value> value, DoneCallback cb, tim
|
||||
std::unique_lock<std::mutex> lck(storage_mtx);
|
||||
if (running != State::Running) {
|
||||
lck.unlock();
|
||||
if (cb) cb(false, {});
|
||||
if (cb)
|
||||
cb(false, {});
|
||||
return;
|
||||
}
|
||||
ongoing_ops++;
|
||||
@@ -843,9 +904,7 @@ void
|
||||
DhtRunner::cancelPut(const InfoHash& h, Value::Id id)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.cancelPut(h, id);
|
||||
});
|
||||
pending_ops.emplace([=](SecureDht& dht) { dht.cancelPut(h, id); });
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
@@ -853,9 +912,7 @@ void
|
||||
DhtRunner::cancelPut(const InfoHash& h, const std::shared_ptr<Value>& value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.cancelPut(h, value->id);
|
||||
});
|
||||
pending_ops.emplace([=](SecureDht& dht) { dht.cancelPut(h, value->id); });
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
@@ -865,14 +922,12 @@ DhtRunner::putSigned(InfoHash hash, std::shared_ptr<Value> value, DoneCallback c
|
||||
std::unique_lock<std::mutex> lck(storage_mtx);
|
||||
if (running != State::Running) {
|
||||
lck.unlock();
|
||||
if (cb) cb(false, {});
|
||||
if (cb)
|
||||
cb(false, {});
|
||||
return;
|
||||
}
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([=,
|
||||
cb = std::move(cb),
|
||||
value = std::move(value)
|
||||
](SecureDht& dht) mutable {
|
||||
pending_ops.emplace([=, cb = std::move(cb), value = std::move(value)](SecureDht& dht) mutable {
|
||||
dht.putSigned(hash, value, bindOpDoneCallback(std::move(cb)), permanent);
|
||||
});
|
||||
cv.notify_all();
|
||||
@@ -896,14 +951,12 @@ DhtRunner::putEncrypted(InfoHash hash, const PkId& to, std::shared_ptr<Value> va
|
||||
std::unique_lock<std::mutex> lck(storage_mtx);
|
||||
if (running != State::Running) {
|
||||
lck.unlock();
|
||||
if (cb) cb(false, {});
|
||||
if (cb)
|
||||
cb(false, {});
|
||||
return;
|
||||
}
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([=,
|
||||
cb = std::move(cb),
|
||||
value = std::move(value)
|
||||
] (SecureDht& dht) mutable {
|
||||
pending_ops.emplace([=, cb = std::move(cb), value = std::move(value)](SecureDht& dht) mutable {
|
||||
dht.putEncrypted(hash, to, value, bindOpDoneCallback(std::move(cb)), permanent);
|
||||
});
|
||||
cv.notify_all();
|
||||
@@ -915,14 +968,12 @@ DhtRunner::putEncrypted(InfoHash hash, InfoHash to, std::shared_ptr<Value> value
|
||||
std::unique_lock<std::mutex> lck(storage_mtx);
|
||||
if (running != State::Running) {
|
||||
lck.unlock();
|
||||
if (cb) cb(false, {});
|
||||
if (cb)
|
||||
cb(false, {});
|
||||
return;
|
||||
}
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([=,
|
||||
cb = std::move(cb),
|
||||
value = std::move(value)
|
||||
] (SecureDht& dht) mutable {
|
||||
pending_ops.emplace([=, cb = std::move(cb), value = std::move(value)](SecureDht& dht) mutable {
|
||||
dht.putEncrypted(hash, to, value, bindOpDoneCallback(std::move(cb)), permanent);
|
||||
});
|
||||
cv.notify_all();
|
||||
@@ -941,26 +992,29 @@ DhtRunner::putEncrypted(const std::string& key, InfoHash to, Value&& value, Done
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::putEncrypted(InfoHash hash, const std::shared_ptr<crypto::PublicKey>& to, std::shared_ptr<Value> value, DoneCallback cb, bool permanent)
|
||||
DhtRunner::putEncrypted(InfoHash hash,
|
||||
const std::shared_ptr<crypto::PublicKey>& to,
|
||||
std::shared_ptr<Value> value,
|
||||
DoneCallback cb,
|
||||
bool permanent)
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(storage_mtx);
|
||||
if (running != State::Running) {
|
||||
lck.unlock();
|
||||
if (cb) cb(false, {});
|
||||
if (cb)
|
||||
cb(false, {});
|
||||
return;
|
||||
}
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([=,
|
||||
cb = std::move(cb),
|
||||
value = std::move(value)
|
||||
] (SecureDht& dht) mutable {
|
||||
pending_ops.emplace([=, cb = std::move(cb), value = std::move(value)](SecureDht& dht) mutable {
|
||||
dht.putEncrypted(hash, *to, value, bindOpDoneCallback(std::move(cb)), permanent);
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::putEncrypted(InfoHash hash, const std::shared_ptr<crypto::PublicKey>& to, Value&& value, DoneCallback cb, bool permanent)
|
||||
DhtRunner::putEncrypted(
|
||||
InfoHash hash, const std::shared_ptr<crypto::PublicKey>& to, Value&& value, DoneCallback cb, bool permanent)
|
||||
{
|
||||
putEncrypted(hash, to, std::make_shared<Value>(std::move(value)), std::move(cb), permanent);
|
||||
}
|
||||
@@ -969,9 +1023,7 @@ void
|
||||
DhtRunner::bootstrap(const std::string& host, const std::string& service)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([host, service] (SecureDht& dht) mutable {
|
||||
dht.addBootstrap(host, service);
|
||||
});
|
||||
pending_ops_prio.emplace([host, service](SecureDht& dht) mutable { dht.addBootstrap(host, service); });
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
@@ -979,9 +1031,9 @@ void
|
||||
DhtRunner::bootstrap(const std::string& hostService)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([host_service = splitPort(hostService)] (SecureDht& dht) mutable {
|
||||
dht.addBootstrap(host_service.first, host_service.second);
|
||||
});
|
||||
auto [h, s] = splitPort(hostService);
|
||||
pending_ops_prio.emplace(
|
||||
[host = std::string(h), service = std::string(s)](SecureDht& dht) { dht.addBootstrap(host, service); });
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
@@ -989,9 +1041,7 @@ void
|
||||
DhtRunner::clearBootstrap()
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([] (SecureDht& dht) mutable {
|
||||
dht.clearBootstrap();
|
||||
});
|
||||
pending_ops_prio.emplace([](SecureDht& dht) mutable { dht.clearBootstrap(); });
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
@@ -1004,15 +1054,12 @@ DhtRunner::bootstrap(std::vector<SockAddr> nodes, DoneCallbackSimple cb)
|
||||
}
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
ongoing_ops++;
|
||||
pending_ops_prio.emplace([
|
||||
cb = bindOpDoneCallback(std::move(cb)),
|
||||
nodes = std::move(nodes)
|
||||
] (SecureDht& dht) mutable {
|
||||
pending_ops_prio.emplace([cb = bindOpDoneCallback(std::move(cb)), nodes = std::move(nodes)](SecureDht& dht) mutable {
|
||||
auto rem = cb ? std::make_shared<std::pair<size_t, bool>>(nodes.size(), false) : nullptr;
|
||||
for (auto& node : nodes) {
|
||||
if (node.getPort() == 0)
|
||||
node.setPort(net::DHT_DEFAULT_PORT);
|
||||
dht.pingNode(std::move(node), [rem,cb](bool ok) {
|
||||
dht.pingNode(std::move(node), [rem, cb](bool ok) {
|
||||
auto& r = *rem;
|
||||
r.first--;
|
||||
r.second |= ok;
|
||||
@@ -1031,7 +1078,8 @@ DhtRunner::bootstrap(SockAddr addr, DoneCallbackSimple cb)
|
||||
std::unique_lock<std::mutex> lck(storage_mtx);
|
||||
if (running != State::Running) {
|
||||
lck.unlock();
|
||||
if (cb) cb(false);
|
||||
if (cb)
|
||||
cb(false);
|
||||
return;
|
||||
}
|
||||
ongoing_ops++;
|
||||
@@ -1047,9 +1095,7 @@ DhtRunner::bootstrap(const InfoHash& id, const SockAddr& address)
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
if (running != State::Running)
|
||||
return;
|
||||
pending_ops_prio.emplace([id, address](SecureDht& dht) mutable {
|
||||
dht.insertNode(id, address);
|
||||
});
|
||||
pending_ops_prio.emplace([id, address](SecureDht& dht) mutable { dht.insertNode(id, address); });
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
@@ -1081,7 +1127,8 @@ DhtRunner::connectivityChanged()
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::findCertificate(InfoHash hash, std::function<void(const Sp<crypto::Certificate>&)> cb) {
|
||||
DhtRunner::findCertificate(InfoHash hash, std::function<void(const Sp<crypto::Certificate>&)> cb)
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(storage_mtx);
|
||||
if (running != State::Running) {
|
||||
lck.unlock();
|
||||
@@ -1089,8 +1136,8 @@ DhtRunner::findCertificate(InfoHash hash, std::function<void(const Sp<crypto::Ce
|
||||
return;
|
||||
}
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([this, hash, cb = std::move(cb)] (SecureDht& dht) {
|
||||
dht.findCertificate(hash, [this, cb = std::move(cb)](const Sp<crypto::Certificate>& crt){
|
||||
pending_ops.emplace([this, hash, cb = std::move(cb)](SecureDht& dht) {
|
||||
dht.findCertificate(hash, [this, cb = std::move(cb)](const Sp<crypto::Certificate>& crt) {
|
||||
cb(crt);
|
||||
opEnded();
|
||||
});
|
||||
@@ -1098,7 +1145,8 @@ DhtRunner::findCertificate(InfoHash hash, std::function<void(const Sp<crypto::Ce
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void DhtRunner::findCertificate(PkId hash, std::function<void(const std::shared_ptr<crypto::Certificate>&)> cb)
|
||||
void
|
||||
DhtRunner::findCertificate(PkId hash, std::function<void(const std::shared_ptr<crypto::Certificate>&)> cb)
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(storage_mtx);
|
||||
if (running != State::Running) {
|
||||
@@ -1106,8 +1154,8 @@ void DhtRunner::findCertificate(PkId hash, std::function<void(const std::shared_
|
||||
return;
|
||||
}
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([this, hash, cb = std::move(cb)] (SecureDht& dht) {
|
||||
dht.findCertificate(hash, [this, cb = std::move(cb)](const Sp<crypto::Certificate>& crt){
|
||||
pending_ops.emplace([this, hash, cb = std::move(cb)](SecureDht& dht) {
|
||||
dht.findCertificate(hash, [this, cb = std::move(cb)](const Sp<crypto::Certificate>& crt) {
|
||||
cb(crt);
|
||||
opEnded();
|
||||
});
|
||||
@@ -1149,22 +1197,22 @@ DhtRunner::enableProxy(bool proxify)
|
||||
if (proxify) {
|
||||
// Init the proxy client
|
||||
auto dht_via_proxy = std::make_unique<DhtProxyClient>(
|
||||
config_.server_ca,
|
||||
config_.client_identity,
|
||||
[this]{
|
||||
if (config_.threaded) {
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht&) mutable {});
|
||||
cv.notify_all();
|
||||
}
|
||||
},
|
||||
config_.proxy_server,
|
||||
config_.proxy_user_agent,
|
||||
config_.push_node_id,
|
||||
config_.push_token,
|
||||
config_.push_topic,
|
||||
config_.push_platform,
|
||||
logger_);
|
||||
config_.server_ca,
|
||||
config_.client_identity,
|
||||
[this] {
|
||||
if (config_.threaded) {
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht&) mutable {});
|
||||
cv.notify_all();
|
||||
}
|
||||
},
|
||||
config_.proxy_server,
|
||||
config_.proxy_user_agent,
|
||||
config_.push_node_id,
|
||||
config_.push_token,
|
||||
config_.push_topic,
|
||||
config_.push_platform,
|
||||
logger_);
|
||||
dht_ = std::make_unique<SecureDht>(std::move(dht_via_proxy), config_.dht_config, identityAnnouncedCb_, logger_);
|
||||
// and use it
|
||||
use_proxy = proxify;
|
||||
@@ -1189,7 +1237,8 @@ DhtRunner::forwardAllMessages(bool forward)
|
||||
* Updates the push notification device token
|
||||
*/
|
||||
void
|
||||
DhtRunner::setPushNotificationToken(const std::string& token) {
|
||||
DhtRunner::setPushNotificationToken(const std::string& token)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
#if defined(OPENDHT_PROXY_CLIENT) && defined(OPENDHT_PUSH_NOTIFICATIONS)
|
||||
config_.push_token = token;
|
||||
@@ -1201,7 +1250,8 @@ DhtRunner::setPushNotificationToken(const std::string& token) {
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::setPushNotificationTopic(const std::string& topic) {
|
||||
DhtRunner::setPushNotificationTopic(const std::string& topic)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
#if defined(OPENDHT_PROXY_CLIENT) && defined(OPENDHT_PUSH_NOTIFICATIONS)
|
||||
config_.push_topic = topic;
|
||||
@@ -1213,7 +1263,8 @@ DhtRunner::setPushNotificationTopic(const std::string& topic) {
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::setPushNotificationPlatform(const std::string& platform) {
|
||||
DhtRunner::setPushNotificationPlatform(const std::string& platform)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
#if defined(OPENDHT_PROXY_CLIENT) && defined(OPENDHT_PUSH_NOTIFICATIONS)
|
||||
config_.push_platform = platform;
|
||||
@@ -1246,4 +1297,4 @@ DhtRunner::pushNotificationReceived([[maybe_unused]] const std::map<std::string,
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace dht
|
||||
+511
-338
File diff suppressed because it is too large.
Load diff
@@ -145,7 +145,7 @@ int Pht::Cache::lookup(const Prefix& p) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
const ValueType IndexEntry::TYPE = ValueType::USER_DATA;
|
||||
const ValueType& IndexEntry::TYPE = ValueType::USER_DATA;
|
||||
constexpr std::chrono::minutes Pht::Cache::NODE_EXPIRE_TIME;
|
||||
|
||||
void Pht::lookupStep(Prefix p, std::shared_ptr<int> lo, std::shared_ptr<int> hi,
|
||||
|
||||
+23
-45
@@ -33,10 +33,10 @@ using namespace std::chrono_literals;
|
||||
|
||||
const std::string DhtProtocolException::GET_NO_INFOHASH {"Get_values with no info_hash"};
|
||||
const std::string DhtProtocolException::LISTEN_NO_INFOHASH {"Listen with no info_hash"};
|
||||
const std::string DhtProtocolException::LISTEN_WRONG_TOKEN {"Listen with wrong token"};
|
||||
const std::string DhtProtocolException::LISTEN_WRONG_TOKEN {"Listen with incorrect token"};
|
||||
const std::string DhtProtocolException::PUT_NO_INFOHASH {"Put with no info_hash"};
|
||||
const std::string DhtProtocolException::PUT_WRONG_TOKEN {"Put with wrong token"};
|
||||
const std::string DhtProtocolException::PUT_INVALID_ID {"Put with invalid id"};
|
||||
const std::string DhtProtocolException::PUT_WRONG_TOKEN {"Put with incorrect token"};
|
||||
const std::string DhtProtocolException::PUT_INVALID_ID {"Put with invalid ID"};
|
||||
const std::string DhtProtocolException::STORAGE_NOT_FOUND {"Access operation for unknown storage"};
|
||||
|
||||
constexpr std::chrono::seconds NetworkEngine::UDP_REPLY_TIME;
|
||||
@@ -133,7 +133,7 @@ NetworkEngine::tellListener(const Sp<Node>& node, Tid socket_id, const InfoHash&
|
||||
}
|
||||
} catch (const std::overflow_error& e) {
|
||||
if (logger_)
|
||||
logger_->e("Can't send value: buffer not large enough !");
|
||||
logger_->e("Unable to send value: buffer not large enough.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,13 +288,14 @@ NetworkEngine::requestStep(Sp<Request> sreq)
|
||||
auto& node = *req.node;
|
||||
if (req.isExpired(now)) {
|
||||
// if (logger_)
|
||||
// logger_->d(node.id, "[node %s] expired !", node.toString().c_str());
|
||||
// logger_->d(node.id, "[node %s] expired.", node.toString().c_str());
|
||||
node.setExpired();
|
||||
if (not node.id)
|
||||
requests.erase(req.tid);
|
||||
return;
|
||||
} else if (req.attempt_count == 1) {
|
||||
req.on_expired(req, false);
|
||||
if (req.on_expired)
|
||||
req.on_expired(req, false);
|
||||
}
|
||||
|
||||
auto err = send(node.getAddr(), (char*)req.msg.data(), req.msg.size(), node.getReplyTime() < now - UDP_REPLY_TIME);
|
||||
@@ -429,7 +430,7 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, SockAddr f)
|
||||
msg->msgpack_unpack(msg_res.get());
|
||||
} catch (const std::exception& e) {
|
||||
if (logger_)
|
||||
logger_->w("Can't parse message of size %lu: %s", buflen, e.what());
|
||||
logger_->w("Unable to parse message of size %lu: %s", buflen, e.what());
|
||||
// if (logger_)
|
||||
// logger_->DBG.logPrintable(buf, buflen);
|
||||
return;
|
||||
@@ -449,7 +450,7 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, SockAddr f)
|
||||
if (pmsg_it == partial_messages.end()) {
|
||||
if (logIncoming_)
|
||||
if (logger_)
|
||||
logger_->d("Can't find partial message");
|
||||
logger_->d("Unable to find partial message");
|
||||
rateLimit(from);
|
||||
return;
|
||||
}
|
||||
@@ -523,7 +524,7 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
|
||||
if (msg->type == MessageType::ValueUpdate) {
|
||||
auto rsocket = node->getSocket(msg->tid);
|
||||
if (not rsocket)
|
||||
throw DhtProtocolException {DhtProtocolException::UNKNOWN_TID, "Can't find socket", msg->id};
|
||||
throw DhtProtocolException {DhtProtocolException::UNKNOWN_TID, "Unable to find socket", msg->id};
|
||||
node->received(now, {});
|
||||
onNewNode(node, 2);
|
||||
try {
|
||||
@@ -531,7 +532,7 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
|
||||
rsocket->on_receive(node, std::move(*msg));
|
||||
} catch (const DhtProtocolException& e) {
|
||||
if (logger_)
|
||||
logger_->w("Can't deserialize nodes %s", e.what());
|
||||
logger_->w("Unable to deserialize nodes %s", e.what());
|
||||
}
|
||||
}
|
||||
else if (msg->type == MessageType::Error or msg->type == MessageType::Reply) {
|
||||
@@ -550,7 +551,7 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
|
||||
if (not node->isClient())
|
||||
onNewNode(node, 1);
|
||||
if (logger_)
|
||||
logger_->d(node->id, "[node %s] can't find transaction with id %u", node->toString().c_str(), msg->tid);
|
||||
logger_->d(node->id, "[node %s] Unable to find transaction with id %u", node->toString().c_str(), msg->tid);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -600,7 +601,7 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
|
||||
r.setDone(std::move(*msg));
|
||||
} catch (const DhtProtocolException& e) {
|
||||
if (logger_)
|
||||
logger_->w("Can't deserialize nodes %s", e.what());
|
||||
logger_->w("Unable to deserialize nodes %s", e.what());
|
||||
}
|
||||
break;
|
||||
} else { /* request socket data */
|
||||
@@ -609,7 +610,7 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
|
||||
rsocket->on_receive(node, std::move(*msg));
|
||||
} catch (const DhtProtocolException& e) {
|
||||
if (logger_)
|
||||
logger_->w("Can't deserialize nodes %s", e.what());
|
||||
logger_->w("Unable to deserialize nodes %s", e.what());
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -694,7 +695,7 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
|
||||
}
|
||||
} catch (const std::overflow_error& e) {
|
||||
if (logger_)
|
||||
logger_->e("Can't send value: buffer not large enough !");
|
||||
logger_->e("Unable to send value: buffer not large enough.");
|
||||
} catch (const DhtProtocolException& e) {
|
||||
sendError(from, msg->tid, e.getCode(), e.getMsg().c_str(), true);
|
||||
}
|
||||
@@ -744,16 +745,12 @@ NetworkEngine::sendPing(const Sp<Node>& node, RequestCb&& on_done, RequestExpire
|
||||
Blob(buffer.data(), buffer.data() + buffer.size()),
|
||||
[=](const Request& req_status, ParsedMessage&&) {
|
||||
if (logger_)
|
||||
logger_->d(req_status.node->id, "[node %s] got pong !", req_status.node->toString().c_str());
|
||||
logger_->d(req_status.node->id, "[node %s] got pong.", req_status.node->toString().c_str());
|
||||
if (on_done) {
|
||||
on_done(req_status, {});
|
||||
}
|
||||
},
|
||||
[=](const Request& req_status, bool done) { /* on expired */
|
||||
if (on_expired) {
|
||||
on_expired(req_status, done);
|
||||
}
|
||||
}
|
||||
std::move(on_expired)
|
||||
);
|
||||
sendRequest(req);
|
||||
++out_stats.ping;
|
||||
@@ -819,11 +816,7 @@ NetworkEngine::sendFindNode(const Sp<Node>& n, const InfoHash& target, want_t wa
|
||||
on_done(req_status, {std::forward<ParsedMessage>(msg)});
|
||||
}
|
||||
},
|
||||
[=](const Request& req_status, bool done) { /* on expired */
|
||||
if (on_expired) {
|
||||
on_expired(req_status, done);
|
||||
}
|
||||
}
|
||||
std::move(on_expired)
|
||||
);
|
||||
sendRequest(req);
|
||||
++out_stats.find;
|
||||
@@ -875,11 +868,7 @@ NetworkEngine::sendGetValues(const Sp<Node>& n, const InfoHash& info_hash, const
|
||||
on_done(req_status, {std::forward<ParsedMessage>(msg)});
|
||||
}
|
||||
},
|
||||
[=](const Request& req_status, bool done) { /* on expired */
|
||||
if (on_expired) {
|
||||
on_expired(req_status, done);
|
||||
}
|
||||
}
|
||||
std::move(on_expired)
|
||||
);
|
||||
sendRequest(req);
|
||||
++out_stats.get;
|
||||
@@ -1157,10 +1146,7 @@ NetworkEngine::sendListen(const Sp<Node>& n,
|
||||
if (on_done)
|
||||
on_done(req_status, {std::forward<ParsedMessage>(msg)});
|
||||
},
|
||||
[=](const Request& req_status, bool done) { /* on expired */
|
||||
if (on_expired)
|
||||
on_expired(req_status, done);
|
||||
}
|
||||
std::move(on_expired)
|
||||
);
|
||||
sendRequest(req);
|
||||
++out_stats.listen;
|
||||
@@ -1240,11 +1226,7 @@ NetworkEngine::sendAnnounceValue(const Sp<Node>& n,
|
||||
}
|
||||
}
|
||||
},
|
||||
[=](const Request& req_status, bool done) { /* on expired */
|
||||
if (on_expired) {
|
||||
on_expired(req_status, done);
|
||||
}
|
||||
}
|
||||
std::move(on_expired)
|
||||
);
|
||||
req->parts = std::move(v);
|
||||
sendRequest(req);
|
||||
@@ -1372,12 +1354,8 @@ NetworkEngine::sendRefreshValue(const Sp<Node>& n,
|
||||
}
|
||||
}
|
||||
},
|
||||
on_error,
|
||||
[=](const Request& req_status, bool done) { /* on expired */
|
||||
if (on_expired) {
|
||||
on_expired(req_status, done);
|
||||
}
|
||||
}
|
||||
std::move(on_error),
|
||||
std::move(on_expired)
|
||||
);
|
||||
sendRequest(req);
|
||||
++out_stats.refresh;
|
||||
|
||||
+42
-30
@@ -23,8 +23,8 @@
|
||||
#include <io.h>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#define close(x) closesocket(x)
|
||||
#define write(s, b, f) send(s, b, (int)strlen(b), 0)
|
||||
#define close(x) closesocket(x)
|
||||
#define write(s, b, f) send(s, b, (int) strlen(b), 0)
|
||||
#define _poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
@@ -49,10 +49,10 @@ bindSocket(const SockAddr& addr, SockAddr& bound)
|
||||
throw DhtException(std::string("Can't open socket: ") + strerror(sock));
|
||||
int set = 1;
|
||||
#ifdef SO_NOSIGPIPE
|
||||
setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, (const char*)&set, sizeof(set));
|
||||
setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, (const char*) &set, sizeof(set));
|
||||
#endif
|
||||
if (is_ipv6)
|
||||
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&set, sizeof(set));
|
||||
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*) &set, sizeof(set));
|
||||
net::setNonblocking(sock);
|
||||
int rc = bind(sock, addr.get(), addr.getLength());
|
||||
if (rc < 0) {
|
||||
@@ -62,7 +62,7 @@ bindSocket(const SockAddr& addr, SockAddr& bound)
|
||||
}
|
||||
sockaddr_storage ss;
|
||||
socklen_t ss_len = sizeof(ss);
|
||||
getsockname(sock, (sockaddr*)&ss, &ss_len);
|
||||
getsockname(sock, (sockaddr*) &ss, &ss_len);
|
||||
bound = {ss, ss_len};
|
||||
return sock;
|
||||
}
|
||||
@@ -84,7 +84,8 @@ setNonblocking(int fd, bool nonblocking)
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
void udpPipe(int fds[2])
|
||||
void
|
||||
udpPipe(int fds[2])
|
||||
{
|
||||
int lst = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (lst < 0)
|
||||
@@ -97,11 +98,12 @@ void udpPipe(int fds[2])
|
||||
inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
inaddr.sin_port = 0;
|
||||
int yes = 1;
|
||||
setsockopt(lst, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes));
|
||||
int rc = bind(lst, (sockaddr*)&inaddr, sizeof(inaddr));
|
||||
setsockopt(lst, SOL_SOCKET, SO_REUSEADDR, (char*) &yes, sizeof(yes));
|
||||
int rc = bind(lst, (sockaddr*) &inaddr, sizeof(inaddr));
|
||||
if (rc < 0) {
|
||||
close(lst);
|
||||
throw DhtException("Can't bind socket on " + print_addr((sockaddr*)&inaddr, sizeof(inaddr)) + " " + strerror(rc));
|
||||
throw DhtException("Can't bind socket on " + print_addr((sockaddr*) &inaddr, sizeof(inaddr)) + " "
|
||||
+ strerror(rc));
|
||||
}
|
||||
socklen_t len = sizeof(addr);
|
||||
getsockname(lst, &addr, &len);
|
||||
@@ -111,7 +113,9 @@ void udpPipe(int fds[2])
|
||||
}
|
||||
#endif
|
||||
|
||||
UdpSocket::UdpSocket(in_port_t port, const std::shared_ptr<Logger>& l) : logger(l) {
|
||||
UdpSocket::UdpSocket(in_port_t port, const std::shared_ptr<Logger>& l)
|
||||
: logger(l)
|
||||
{
|
||||
SockAddr bind4;
|
||||
bind4.setFamily(AF_INET);
|
||||
bind4.setPort(port);
|
||||
@@ -122,28 +126,37 @@ UdpSocket::UdpSocket(in_port_t port, const std::shared_ptr<Logger>& l) : logger(
|
||||
openSockets(bind4, bind6);
|
||||
}
|
||||
|
||||
UdpSocket::UdpSocket(const SockAddr& bind4, const SockAddr& bind6, const std::shared_ptr<Logger>& l) : logger(l)
|
||||
UdpSocket::UdpSocket(const SockAddr& bind4, const SockAddr& bind6, const std::shared_ptr<Logger>& l)
|
||||
: logger(l)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
openSockets(bind4, bind6);
|
||||
}
|
||||
|
||||
UdpSocket::~UdpSocket() {
|
||||
UdpSocket::~UdpSocket()
|
||||
{
|
||||
stop();
|
||||
if (rcv_thread.joinable())
|
||||
rcv_thread.join();
|
||||
}
|
||||
|
||||
int
|
||||
UdpSocket::sendTo(const SockAddr& dest, const uint8_t* data, size_t size, bool replied) {
|
||||
UdpSocket::sendTo(const SockAddr& dest, const uint8_t* data, size_t size, [[maybe_unused]] bool replied)
|
||||
{
|
||||
if (not dest)
|
||||
return EFAULT;
|
||||
|
||||
int s;
|
||||
switch (dest.getFamily()) {
|
||||
case AF_INET: s = s4; break;
|
||||
case AF_INET6: s = s6; break;
|
||||
default: s = -1; break;
|
||||
case AF_INET:
|
||||
s = s4;
|
||||
break;
|
||||
case AF_INET6:
|
||||
s = s6;
|
||||
break;
|
||||
default:
|
||||
s = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (s < 0)
|
||||
@@ -153,14 +166,12 @@ UdpSocket::sendTo(const SockAddr& dest, const uint8_t* data, size_t size, bool r
|
||||
#ifdef MSG_CONFIRM
|
||||
if (replied)
|
||||
flags |= MSG_CONFIRM;
|
||||
#else
|
||||
(void) replied;
|
||||
#endif
|
||||
#ifdef MSG_NOSIGNAL
|
||||
flags |= MSG_NOSIGNAL;
|
||||
#endif
|
||||
|
||||
if (sendto(s, (const char*)data, size, flags, dest.get(), dest.getLength()) == -1) {
|
||||
if (sendto(s, (const char*) data, size, flags, dest.get(), dest.getLength()) == -1) {
|
||||
int err = errno;
|
||||
if (logger)
|
||||
logger->d("Can't send message to %s: %s", dest.toString().c_str(), strerror(err));
|
||||
@@ -239,7 +250,7 @@ UdpSocket::openSockets(const SockAddr& bind4, const SockAddr& bind6)
|
||||
}
|
||||
|
||||
running = true;
|
||||
rcv_thread = std::thread([this, stop_readfd, ls4=s4, ls6=s6]() mutable {
|
||||
rcv_thread = std::thread([this, stop_readfd, ls4 = s4, ls6 = s6]() mutable {
|
||||
struct pollfd fds[NUM_FDS];
|
||||
for (int i = 0; i < NUM_FDS; i++)
|
||||
fds[i].events = POLLIN;
|
||||
@@ -270,23 +281,22 @@ UdpSocket::openSockets(const SockAddr& bind4, const SockAddr& bind6)
|
||||
socklen_t from_len = sizeof(from);
|
||||
|
||||
if (fds[stop_readfd_index].revents & POLLIN) {
|
||||
if (recv(stop_readfd, (char*)buf.data(), buf.size(), 0) < 0) {
|
||||
if (recv(stop_readfd, (char*) buf.data(), buf.size(), 0) < 0) {
|
||||
if (logger)
|
||||
logger->e("Got stop packet error: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (fds[ls4_index].revents & POLLIN)
|
||||
rc = recvfrom(ls4, (char*)buf.data(), buf.size(), 0, (sockaddr*)&from, &from_len);
|
||||
} else if (fds[ls4_index].revents & POLLIN)
|
||||
rc = recvfrom(ls4, (char*) buf.data(), buf.size(), 0, (sockaddr*) &from, &from_len);
|
||||
else if (fds[ls6_index].revents & POLLIN)
|
||||
rc = recvfrom(ls6, (char*)buf.data(), buf.size(), 0, (sockaddr*)&from, &from_len);
|
||||
rc = recvfrom(ls6, (char*) buf.data(), buf.size(), 0, (sockaddr*) &from, &from_len);
|
||||
else
|
||||
continue;
|
||||
|
||||
if (rc > 0) {
|
||||
auto pkts = getNewPacket();
|
||||
auto& pkt = pkts.front();
|
||||
pkt.data.insert(pkt.data.end(), buf.begin(), buf.begin()+rc);
|
||||
pkt.data.insert(pkt.data.end(), buf.begin(), buf.begin() + rc);
|
||||
pkt.from = {from, from_len};
|
||||
pkt.received = clock::now();
|
||||
onReceived(std::move(pkts));
|
||||
@@ -295,10 +305,12 @@ UdpSocket::openSockets(const SockAddr& bind4, const SockAddr& bind6)
|
||||
logger->e("Error receiving packet: %s", strerror(errno));
|
||||
int err = errno;
|
||||
if (err == EPIPE || err == ENOTCONN || err == ECONNRESET) {
|
||||
if (not running) break;
|
||||
if (not running)
|
||||
break;
|
||||
std::unique_lock<std::mutex> lk(lock, std::try_to_lock);
|
||||
if (lk.owns_lock()) {
|
||||
if (not running) break;
|
||||
if (not running)
|
||||
break;
|
||||
if (ls4 >= 0) {
|
||||
close(ls4);
|
||||
try {
|
||||
@@ -363,5 +375,5 @@ UdpSocket::stop()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} // namespace net
|
||||
} // namespace dht
|
||||
@@ -115,13 +115,13 @@ PeerDiscovery::DomainPeerDiscovery::DomainPeerDiscovery(asio::ip::udp domain, in
|
||||
sockFd_.bind({domain, port});
|
||||
} catch(const std::exception& e){
|
||||
if (logger_)
|
||||
logger_->error("Can't start peer discovery using android workaround: {}", e.what());
|
||||
logger_->error("Unable to start peer discovery using android workaround: {}", e.what());
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (logger_)
|
||||
logger_->error("Can't start peer discovery for {}: {}",
|
||||
logger_->error("Unable to start peer discovery for {}: {}",
|
||||
domain.family() == AF_INET ? "IPv4" : "IPv6", e.what());
|
||||
}
|
||||
}
|
||||
@@ -322,9 +322,9 @@ PeerDiscovery::DomainPeerDiscovery::reDiscover()
|
||||
|
||||
sockFd_.set_option(asio::ip::multicast::join_group(sockAddrSend_.address()), ec);
|
||||
if (ec and logger_)
|
||||
logger_->w("can't multicast on %s: %s",
|
||||
sockAddrSend_.address().to_string().c_str(),
|
||||
ec.message().c_str());
|
||||
logger_->w("Unable to multicast on %s: %s",
|
||||
sockAddrSend_.address().to_string().c_str(),
|
||||
ec.message().c_str());
|
||||
query(sockAddrSend_);
|
||||
}
|
||||
|
||||
@@ -382,13 +382,13 @@ PeerDiscovery::PeerDiscovery(in_port_t port, Sp<asio::io_context> ioContext, Sp<
|
||||
peerDiscovery4_.reset(new DomainPeerDiscovery(asio::ip::udp::v4(), port, ioContext, logger));
|
||||
} catch(const std::exception& e){
|
||||
if (logger)
|
||||
logger->e("[peerdiscovery] can't start IPv4: %s", e.what());
|
||||
logger->e("[peerdiscovery] Unable to start IPv4: %s", e.what());
|
||||
}
|
||||
try {
|
||||
peerDiscovery6_.reset(new DomainPeerDiscovery(asio::ip::udp::v6(), port, ioContext, logger));
|
||||
} catch(const std::exception& e) {
|
||||
if (logger)
|
||||
logger->e("[peerdiscovery] can't start IPv6: %s", e.what());
|
||||
logger->e("[peerdiscovery] Unable to start IPv6: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -89,7 +89,8 @@ struct Request {
|
||||
void setExpired() {
|
||||
if (pending()) {
|
||||
state_ = Request::State::EXPIRED;
|
||||
on_expired(*this, true);
|
||||
if (on_expired)
|
||||
on_expired(*this, true);
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
+33
-14
@@ -53,25 +53,30 @@ SecureDht::SecureDht(std::unique_ptr<DhtInterface> dht, SecureDht::Config conf,
|
||||
auto certLongId = certificate_->getLongId();
|
||||
if (key_ and (certId != key_->getPublicKey().getId() or certLongId != key_->getPublicKey().getLongId()))
|
||||
throw DhtException("SecureDht: provided certificate doesn't match private key.");
|
||||
dht_->addOnConnectedCallback([this, certId, certLongId, cb=std::move(iacb)]{
|
||||
|
||||
auto cb = iacb ? [
|
||||
this,
|
||||
completedState = std::make_shared<std::pair<unsigned,bool>>(2, true),
|
||||
iacb = std::move(iacb)
|
||||
](bool ok) {
|
||||
if (logger_)
|
||||
logger_->d("SecureDht: certificate announcement %s", ok ? "succeeded" : "failed");
|
||||
completedState->second = completedState->second and ok;
|
||||
if (--completedState->first == 0)
|
||||
iacb(completedState->second);
|
||||
} : iacb;
|
||||
|
||||
dht_->addOnConnectedCallback([this, certId, certLongId, cb=std::move(cb)]{
|
||||
dht_->put(certId, Value {
|
||||
CERTIFICATE_TYPE,
|
||||
*certificate_,
|
||||
1
|
||||
}, [this, certId, cb=std::move(cb)](bool ok) {
|
||||
if (cb) cb(ok);
|
||||
if (logger_)
|
||||
logger_->d(certId, "SecureDht: certificate announcement %s", ok ? "succeeded" : "failed");
|
||||
}, {}, true);
|
||||
}, cb, {}, true);
|
||||
dht_->put(InfoHash::get(certLongId), Value {
|
||||
CERTIFICATE_TYPE,
|
||||
*certificate_,
|
||||
1
|
||||
}, [this, cb=std::move(cb)](bool ok) {
|
||||
if (cb) cb(ok);
|
||||
if (logger_)
|
||||
logger_->debug("SecureDht: certificate announcement {}", ok ? "succeeded" : "failed");
|
||||
}, {}, true);
|
||||
}, cb, {}, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -93,7 +98,7 @@ SecureDht::secureType(ValueType&& type)
|
||||
return type.editPolicy(id, o, n, nid, a);
|
||||
if (*o->owner != *n->owner or not n->isSigned()) {
|
||||
if (logger_)
|
||||
logger_->w("Edition forbidden: not signed or wrong owner.");
|
||||
logger_->w("Edition forbidden: not signed or incorrect owner.");
|
||||
return false;
|
||||
}
|
||||
if (not n->checkSignature()) {
|
||||
@@ -228,12 +233,13 @@ SecureDht::findCertificate(const InfoHash& node, const std::function<void(const
|
||||
cb(b);
|
||||
return;
|
||||
}
|
||||
if (localQueryMethod_) {
|
||||
auto res = localQueryMethod_(node);
|
||||
if (localQueryMethodLegacy_) {
|
||||
auto res = localQueryMethodLegacy_(node);
|
||||
if (not res.empty()) {
|
||||
if (logger_)
|
||||
logger_->d("Registering certificate from local store for %s", node.to_c_str());
|
||||
nodesCertificates_.emplace(node, res.front());
|
||||
nodesCertificatesLong_.emplace(res.front()->getLongId(), res.front());
|
||||
if (cb)
|
||||
cb(res.front());
|
||||
return;
|
||||
@@ -296,6 +302,19 @@ SecureDht::findCertificate(const PkId& node, const std::function<void(const Sp<c
|
||||
return;
|
||||
}
|
||||
|
||||
if (localQueryMethod_) {
|
||||
auto res = localQueryMethod_(node);
|
||||
if (not res.empty()) {
|
||||
if (logger_)
|
||||
logger_->debug("Registering certificate from local store for {}", node.to_c_str());
|
||||
nodesCertificates_.emplace(res.front()->getId(), res.front());
|
||||
nodesCertificatesLong_.emplace(node, res.front());
|
||||
if (cb)
|
||||
cb(res.front());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto found = std::make_shared<bool>(false);
|
||||
dht_->get(InfoHash::get(node), [cb,node,found,this](const std::vector<Sp<Value>>& vals) {
|
||||
for (const auto& v : vals) {
|
||||
|
||||
+2
-2
@@ -46,7 +46,7 @@ public:
|
||||
} else
|
||||
++rit;
|
||||
}
|
||||
// printf("StorageBucket::erase can't find value %s %016" PRIx64 "\n", id.to_c_str(), value.id);
|
||||
// printf("StorageBucket::erase unable to find value %s %016" PRIx64 "\n", id.to_c_str(), value.id);
|
||||
}
|
||||
void refresh(const InfoHash& id, const Value& value, time_point old_expiration, time_point expiration) {
|
||||
auto range = storedValues_.equal_range(old_expiration);
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
} else
|
||||
++rit;
|
||||
}
|
||||
// printf("StorageBucket::refresh can't find value %s %016" PRIx64 "\n", id.to_c_str(), value.id);
|
||||
// printf("StorageBucket::refresh unable to find value %s %016" PRIx64 "\n", id.to_c_str(), value.id);
|
||||
insert(id, value, expiration);
|
||||
}
|
||||
size_t size() const { return totalSize_; }
|
||||
|
||||
+53
-30
@@ -20,6 +20,13 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && (_WIN32_WINNT < 0x0502) && (NTDDI_VERSION >= NTDDI_VISTA)
|
||||
#define _WIN32_WINNT _WIN32_WINNT_WIN10
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <io.h>
|
||||
#endif // inet_pton() workaround for MSVC in Windows 10/11
|
||||
|
||||
#include "utils.h"
|
||||
#include "sockaddr.h"
|
||||
#include "default_types.h"
|
||||
@@ -35,30 +42,35 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
const char* version() {
|
||||
const char*
|
||||
version()
|
||||
{
|
||||
return PACKAGE_VERSION;
|
||||
}
|
||||
|
||||
static constexpr std::array<uint8_t, 12> MAPPED_IPV4_PREFIX {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}};
|
||||
static constexpr std::array<uint8_t, 12> MAPPED_IPV4_PREFIX {
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}
|
||||
};
|
||||
|
||||
std::pair<std::string, std::string>
|
||||
splitPort(std::string_view s) {
|
||||
std::pair<std::string_view, std::string_view>
|
||||
splitPort(std::string_view s)
|
||||
{
|
||||
if (s.empty())
|
||||
return {};
|
||||
if (s[0] == '[') {
|
||||
std::size_t closure = s.find_first_of(']');
|
||||
std::size_t found = s.find_last_of(':');
|
||||
if (closure == std::string_view::npos)
|
||||
return {std::string(s), ""};
|
||||
return {s, ""};
|
||||
if (found == std::string_view::npos or found < closure)
|
||||
return {std::string(s.substr(1,closure-1)), ""};
|
||||
return {std::string(s.substr(1,closure-1)), std::string(s.substr(found+1))};
|
||||
return {s.substr(1, closure - 1), ""};
|
||||
return {s.substr(1, closure - 1), s.substr(found + 1)};
|
||||
}
|
||||
std::size_t found = s.find_last_of(':');
|
||||
std::size_t first = s.find_first_of(':');
|
||||
if (found == std::string_view::npos or found != first)
|
||||
return {std::string(s), ""};
|
||||
return {std::string(s.substr(0,found)), std::string(s.substr(found+1))};
|
||||
return {s, ""};
|
||||
return {s.substr(0, found), s.substr(found + 1)};
|
||||
}
|
||||
|
||||
std::vector<SockAddr>
|
||||
@@ -73,7 +85,7 @@ SockAddr::resolve(const std::string& host, const std::string& service)
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
addrinfo* info = nullptr;
|
||||
int rc = getaddrinfo(host.c_str(), service.empty() ? nullptr : service.c_str(), &hints, &info);
|
||||
if(rc != 0)
|
||||
if (rc != 0)
|
||||
throw std::invalid_argument(std::string("Error: `") + host + ":" + service + "`: " + gai_strerror(rc));
|
||||
|
||||
for (addrinfo* infop = info; infop; infop = infop->ai_next)
|
||||
@@ -101,11 +113,13 @@ SockAddr::setAddress(const char* address)
|
||||
throw std::runtime_error(std::string("Can't parse IP address: ") + strerror(errno));
|
||||
}
|
||||
|
||||
void print_addr(std::ostream& out, const sockaddr* sa, socklen_t slen)
|
||||
void
|
||||
print_addr(std::ostream& out, const sockaddr* sa, socklen_t slen)
|
||||
{
|
||||
char hbuf[NI_MAXHOST];
|
||||
char sbuf[NI_MAXSERV];
|
||||
if (sa and slen and !getnameinfo(sa, slen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV)) {
|
||||
if (sa and slen
|
||||
and !getnameinfo(sa, slen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV)) {
|
||||
if (sa->sa_family == AF_INET6)
|
||||
out << '[' << hbuf << ']';
|
||||
else
|
||||
@@ -127,7 +141,7 @@ print_addr(const sockaddr* sa, socklen_t slen)
|
||||
std::string
|
||||
print_addr(const sockaddr_storage& ss, socklen_t sslen)
|
||||
{
|
||||
return print_addr((const sockaddr*)&ss, sslen);
|
||||
return print_addr((const sockaddr*) &ss, sslen);
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -149,7 +163,7 @@ SockAddr::isLoopback() const
|
||||
switch (getFamily()) {
|
||||
case AF_INET: {
|
||||
auto addr_host = ntohl(getIPv4().sin_addr.s_addr);
|
||||
uint8_t b1 = (uint8_t)(addr_host >> 24);
|
||||
uint8_t b1 = (uint8_t) (addr_host >> 24);
|
||||
return b1 == 127;
|
||||
}
|
||||
case AF_INET6:
|
||||
@@ -169,8 +183,8 @@ SockAddr::isPrivate() const
|
||||
case AF_INET: {
|
||||
auto addr_host = ntohl(getIPv4().sin_addr.s_addr);
|
||||
uint8_t b1, b2;
|
||||
b1 = (uint8_t)(addr_host >> 24);
|
||||
b2 = (uint8_t)((addr_host >> 16) & 0x0ff);
|
||||
b1 = (uint8_t) (addr_host >> 24);
|
||||
b2 = (uint8_t) ((addr_host >> 16) & 0x0ff);
|
||||
// 10.x.y.z
|
||||
if (b1 == 10)
|
||||
return true;
|
||||
@@ -233,11 +247,15 @@ SockAddr::getMappedIPv6()
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool operator==(const SockAddr& a, const SockAddr& b) {
|
||||
bool
|
||||
operator==(const SockAddr& a, const SockAddr& b)
|
||||
{
|
||||
return a.equals(b);
|
||||
}
|
||||
|
||||
time_point from_time_t(std::time_t t) {
|
||||
time_point
|
||||
from_time_t(std::time_t t)
|
||||
{
|
||||
auto dt = system_clock::from_time_t(t) - system_clock::now();
|
||||
auto now = clock::now();
|
||||
if (dt > system_clock::duration(0) and now > time_point::max() - dt)
|
||||
@@ -247,7 +265,9 @@ time_point from_time_t(std::time_t t) {
|
||||
return now + dt;
|
||||
}
|
||||
|
||||
std::time_t to_time_t(time_point t) {
|
||||
std::time_t
|
||||
to_time_t(time_point t)
|
||||
{
|
||||
auto dt = t - clock::now();
|
||||
auto now = system_clock::now();
|
||||
if (dt > duration(0) and now >= system_clock::time_point::max() - dt)
|
||||
@@ -258,15 +278,16 @@ std::time_t to_time_t(time_point t) {
|
||||
}
|
||||
|
||||
Blob
|
||||
unpackBlob(const msgpack::object& o) {
|
||||
unpackBlob(const msgpack::object& o)
|
||||
{
|
||||
switch (o.type) {
|
||||
case msgpack::type::BIN:
|
||||
return {o.via.bin.ptr, o.via.bin.ptr+o.via.bin.size};
|
||||
return {o.via.bin.ptr, o.via.bin.ptr + o.via.bin.size};
|
||||
case msgpack::type::STR:
|
||||
return {o.via.str.ptr, o.via.str.ptr+o.via.str.size};
|
||||
return {o.via.str.ptr, o.via.str.ptr + o.via.str.size};
|
||||
case msgpack::type::ARRAY: {
|
||||
Blob ret(o.via.array.size);
|
||||
std::transform(o.via.array.ptr, o.via.array.ptr+o.via.array.size, ret.begin(), [](const msgpack::object& b) {
|
||||
std::transform(o.via.array.ptr, o.via.array.ptr + o.via.array.size, ret.begin(), [](const msgpack::object& b) {
|
||||
return b.as<uint8_t>();
|
||||
});
|
||||
return ret;
|
||||
@@ -277,21 +298,23 @@ unpackBlob(const msgpack::object& o) {
|
||||
}
|
||||
|
||||
msgpack::unpacked
|
||||
unpackMsg(Blob b) {
|
||||
return msgpack::unpack((const char*)b.data(), b.size());
|
||||
unpackMsg(Blob b)
|
||||
{
|
||||
return msgpack::unpack((const char*) b.data(), b.size());
|
||||
}
|
||||
|
||||
msgpack::object*
|
||||
findMapValue(const msgpack::object& map, const char* key, size_t key_length) {
|
||||
if (map.type != msgpack::type::MAP) throw msgpack::type_error();
|
||||
findMapValue(const msgpack::object& map, const char* key, size_t key_length)
|
||||
{
|
||||
if (map.type != msgpack::type::MAP)
|
||||
throw msgpack::type_error();
|
||||
for (unsigned i = 0; i < map.via.map.size; i++) {
|
||||
auto& o = map.via.map.ptr[i];
|
||||
if (o.key.type == msgpack::type::STR
|
||||
&& key_length == o.key.via.str.size
|
||||
if (o.key.type == msgpack::type::STR && key_length == o.key.via.str.size
|
||||
&& std::strncmp(o.key.via.str.ptr, key, o.key.via.str.size) == 0)
|
||||
return &o.val;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace dht
|
||||
+72
-50
@@ -30,19 +30,22 @@
|
||||
#include "base64.h"
|
||||
#endif
|
||||
|
||||
|
||||
namespace dht {
|
||||
|
||||
const std::string Query::QUERY_PARSE_ERROR {"Error parsing query."};
|
||||
|
||||
Value::Filter bindFilterRaw(FilterRaw raw_filter, void* user_data) {
|
||||
if (not raw_filter) return {};
|
||||
Value::Filter
|
||||
bindFilterRaw(FilterRaw raw_filter, void* user_data)
|
||||
{
|
||||
if (not raw_filter)
|
||||
return {};
|
||||
return [=](const Value& value) {
|
||||
return raw_filter(value, user_data);
|
||||
};
|
||||
}
|
||||
|
||||
std::ostream& operator<< (std::ostream& s, const Value& v)
|
||||
std::ostream&
|
||||
operator<<(std::ostream& s, const Value& v)
|
||||
{
|
||||
auto flags(s.flags());
|
||||
s << "Value[id:" << std::hex << v.id << std::dec << ' ';
|
||||
@@ -73,7 +76,7 @@ std::ostream& operator<< (std::ostream& s, const Value& v)
|
||||
s << "data(" << v.user_type << "):";
|
||||
if (v.user_type == "text/plain") {
|
||||
s << '"';
|
||||
s.write((const char*)v.data.data(), v.data.size());
|
||||
s.write(reinterpret_cast<const char*>(v.data.data()), v.data.size());
|
||||
s << '"';
|
||||
} else if (v.data.size() < 1024) {
|
||||
s << toHex(v.data.data(), v.data.size());
|
||||
@@ -240,7 +243,8 @@ Value::toJson() const
|
||||
}
|
||||
|
||||
uint64_t
|
||||
unpackId(const Json::Value& json, const std::string& key) {
|
||||
unpackId(const Json::Value& json, const std::string& key)
|
||||
{
|
||||
uint64_t ret = 0;
|
||||
try {
|
||||
const auto& t = json[key];
|
||||
@@ -249,7 +253,8 @@ unpackId(const Json::Value& json, const std::string& key) {
|
||||
} else {
|
||||
ret = t.asLargestUInt();
|
||||
}
|
||||
} catch (...) {}
|
||||
} catch (...) {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
@@ -297,7 +302,7 @@ Value::decrypt(const crypto::PrivateKey& key)
|
||||
decrypted = true;
|
||||
if (isEncrypted()) {
|
||||
auto decryptedBlob = key.decrypt(cypher);
|
||||
auto msg = msgpack::unpack((const char*)decryptedBlob.data(), decryptedBlob.size());
|
||||
auto msg = msgpack::unpack((const char*) decryptedBlob.data(), decryptedBlob.size());
|
||||
auto v = std::make_unique<Value>(id);
|
||||
v->msgpack_unpack_body(msg.get());
|
||||
if (v->recipient != key.getPublicKey().getId())
|
||||
@@ -355,13 +360,13 @@ FieldValueIndex::FieldValueIndex(const Value& v, const Select& s)
|
||||
{
|
||||
if (not s.empty()) {
|
||||
auto selection = s.getSelection();
|
||||
std::transform(selection.begin(), selection.end(), std::inserter(index, index.end()),
|
||||
[](const std::set<Value::Field>::value_type& f) {
|
||||
return std::make_pair(f, FieldValue {});
|
||||
});
|
||||
std::transform(selection.begin(),
|
||||
selection.end(),
|
||||
std::inserter(index, index.end()),
|
||||
[](const std::set<Value::Field>::value_type& f) { return std::make_pair(f, FieldValue {}); });
|
||||
} else {
|
||||
index.clear();
|
||||
for (size_t f = 1 ; f < static_cast<int>(Value::Field::COUNT) ; ++f)
|
||||
for (size_t f = 1; f < static_cast<int>(Value::Field::COUNT); ++f)
|
||||
index[static_cast<Value::Field>(f)] = {};
|
||||
}
|
||||
for (const auto& fvp : index) {
|
||||
@@ -374,13 +379,15 @@ FieldValueIndex::FieldValueIndex(const Value& v, const Select& s)
|
||||
index[f] = {f, v.type};
|
||||
break;
|
||||
case Value::Field::OwnerPk:
|
||||
index[f] = {f, v.owner ? v.owner->getId() : InfoHash() };
|
||||
index[f] = {f, v.owner ? v.owner->getId() : InfoHash()};
|
||||
break;
|
||||
case Value::Field::SeqNum:
|
||||
index[f] = {f, v.seq};
|
||||
break;
|
||||
case Value::Field::UserType:
|
||||
index[f] = {f, Blob {v.user_type.begin(), v.user_type.end()}};
|
||||
index[f] = {
|
||||
f, Blob {v.user_type.begin(), v.user_type.end()}
|
||||
};
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -388,7 +395,9 @@ FieldValueIndex::FieldValueIndex(const Value& v, const Select& s)
|
||||
}
|
||||
}
|
||||
|
||||
bool FieldValueIndex::containedIn(const FieldValueIndex& other) const {
|
||||
bool
|
||||
FieldValueIndex::containedIn(const FieldValueIndex& other) const
|
||||
{
|
||||
if (index.size() > other.index.size())
|
||||
return false;
|
||||
for (const auto& field : index) {
|
||||
@@ -399,7 +408,9 @@ bool FieldValueIndex::containedIn(const FieldValueIndex& other) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const FieldValueIndex& fvi) {
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const FieldValueIndex& fvi)
|
||||
{
|
||||
os << "Index[";
|
||||
for (auto v = fvi.index.begin(); v != fvi.index.end(); ++v) {
|
||||
switch (v->first) {
|
||||
@@ -438,7 +449,7 @@ FieldValueIndex::msgpack_unpack_fields(const std::set<Value::Field>& fields, con
|
||||
|
||||
unsigned j = 0;
|
||||
for (const auto& field : fields) {
|
||||
auto& field_value = o.via.array.ptr[offset+(j++)];
|
||||
auto& field_value = o.via.array.ptr[offset + (j++)];
|
||||
switch (field) {
|
||||
case Value::Field::Id:
|
||||
case Value::Field::ValueType:
|
||||
@@ -457,13 +468,16 @@ FieldValueIndex::msgpack_unpack_fields(const std::set<Value::Field>& fields, con
|
||||
}
|
||||
}
|
||||
|
||||
void trim_str(std::string& str) {
|
||||
void
|
||||
trim_str(std::string& str)
|
||||
{
|
||||
auto first = std::min(str.size(), str.find_first_not_of(' '));
|
||||
auto last = std::min(str.size(), str.find_last_not_of(' '));
|
||||
str = str.substr(first, last - first + 1);
|
||||
}
|
||||
|
||||
Select::Select(std::string_view q_str) {
|
||||
Select::Select(std::string_view q_str)
|
||||
{
|
||||
std::istringstream q_iss {std::string(q_str)};
|
||||
std::string token {};
|
||||
q_iss >> token;
|
||||
@@ -488,7 +502,8 @@ Select::Select(std::string_view q_str) {
|
||||
}
|
||||
}
|
||||
|
||||
Where::Where(std::string_view q_str) {
|
||||
Where::Where(std::string_view q_str)
|
||||
{
|
||||
std::istringstream q_iss {std::string(q_str)};
|
||||
std::string token {};
|
||||
q_iss >> token;
|
||||
@@ -509,11 +524,9 @@ Where::Where(std::string_view q_str) {
|
||||
std::string s {};
|
||||
std::istringstream convert {value_str};
|
||||
convert >> v;
|
||||
if (not convert
|
||||
and value_str.size() > 1
|
||||
and value_str[0] == '"'
|
||||
and value_str[value_str.size()-1] == '"')
|
||||
s = value_str.substr(1, value_str.size()-2);
|
||||
if (not convert and value_str.size() > 1 and value_str[0] == '"'
|
||||
and value_str[value_str.size() - 1] == '"')
|
||||
s = value_str.substr(1, value_str.size() - 2);
|
||||
else
|
||||
s = value_str;
|
||||
if (field_str == VALUE_KEY_ID)
|
||||
@@ -527,7 +540,8 @@ Where::Where(std::string_view q_str) {
|
||||
else if (field_str == "user_type")
|
||||
userType(s);
|
||||
else
|
||||
throw std::invalid_argument(Query::QUERY_PARSE_ERROR + " (WHERE) wrong token near: " + field_str);
|
||||
throw std::invalid_argument(Query::QUERY_PARSE_ERROR
|
||||
+ " (WHERE) incorrect token near: " + field_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -536,24 +550,25 @@ Where::Where(std::string_view q_str) {
|
||||
void
|
||||
Query::msgpack_unpack(const msgpack::object& o)
|
||||
{
|
||||
if (o.type != msgpack::type::MAP)
|
||||
throw msgpack::type_error();
|
||||
if (o.type != msgpack::type::MAP)
|
||||
throw msgpack::type_error();
|
||||
|
||||
auto rfilters = findMapValue(o, "w"sv); /* unpacking filters */
|
||||
if (rfilters)
|
||||
auto rfilters = findMapValue(o, "w"sv); /* unpacking filters */
|
||||
if (rfilters)
|
||||
where.msgpack_unpack(*rfilters);
|
||||
else
|
||||
throw msgpack::type_error();
|
||||
else
|
||||
throw msgpack::type_error();
|
||||
|
||||
auto rfield_selector = findMapValue(o, "s"sv); /* unpacking field selectors */
|
||||
if (rfield_selector)
|
||||
auto rfield_selector = findMapValue(o, "s"sv); /* unpacking field selectors */
|
||||
if (rfield_selector)
|
||||
select.msgpack_unpack(*rfield_selector);
|
||||
else
|
||||
throw msgpack::type_error();
|
||||
else
|
||||
throw msgpack::type_error();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool subset(const std::vector<T>& fds, const std::vector<T>& qfds)
|
||||
template<typename T>
|
||||
bool
|
||||
subset(const std::vector<T>& fds, const std::vector<T>& qfds)
|
||||
{
|
||||
for (const auto& fd : fds) {
|
||||
if (std::find_if(qfds.begin(), qfds.end(), [&fd](const T& _vfd) { return fd == _vfd; }) == qfds.end())
|
||||
@@ -562,22 +577,28 @@ bool subset(const std::vector<T>& fds, const std::vector<T>& qfds)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Select::isSatisfiedBy(const Select& os) const {
|
||||
bool
|
||||
Select::isSatisfiedBy(const Select& os) const
|
||||
{
|
||||
/* empty, means all values are selected. */
|
||||
return fieldSelection_.empty() ?
|
||||
os.fieldSelection_.empty() :
|
||||
subset(fieldSelection_, os.fieldSelection_);
|
||||
return fieldSelection_.empty() ? os.fieldSelection_.empty() : subset(fieldSelection_, os.fieldSelection_);
|
||||
}
|
||||
|
||||
bool Where::isSatisfiedBy(const Where& ow) const {
|
||||
bool
|
||||
Where::isSatisfiedBy(const Where& ow) const
|
||||
{
|
||||
return subset(ow.filters_, filters_);
|
||||
}
|
||||
|
||||
bool Query::isSatisfiedBy(const Query& q) const {
|
||||
bool
|
||||
Query::isSatisfiedBy(const Query& q) const
|
||||
{
|
||||
return none or (where.isSatisfiedBy(q.where) and select.isSatisfiedBy(q.select));
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& s, const dht::Select& select) {
|
||||
std::ostream&
|
||||
operator<<(std::ostream& s, const dht::Select& select)
|
||||
{
|
||||
s << "SELECT ";
|
||||
if (select.fieldSelection_.empty())
|
||||
s << '*';
|
||||
@@ -608,10 +629,12 @@ std::ostream& operator<<(std::ostream& s, const dht::Select& select) {
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& s, const dht::Where& where) {
|
||||
std::ostream&
|
||||
operator<<(std::ostream& s, const dht::Where& where)
|
||||
{
|
||||
if (not where.filters_.empty()) {
|
||||
s << "WHERE ";
|
||||
for (auto f = where.filters_.begin() ; f != where.filters_.end() ; ++f) {
|
||||
for (auto f = where.filters_.begin(); f != where.filters_.end(); ++f) {
|
||||
switch (f->getField()) {
|
||||
case Value::Field::Id:
|
||||
s << VALUE_KEY_ID << '=' << f->getInt();
|
||||
@@ -639,5 +662,4 @@ std::ostream& operator<<(std::ostream& s, const dht::Where& where) {
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} // namespace dht
|
||||
+233
-27
@@ -26,12 +26,12 @@ namespace test {
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(CryptoTester);
|
||||
|
||||
void
|
||||
CryptoTester::setUp() {
|
||||
|
||||
}
|
||||
CryptoTester::setUp()
|
||||
{}
|
||||
|
||||
void
|
||||
CryptoTester::testSignatureEncryption() {
|
||||
CryptoTester::testSignatureEncryption()
|
||||
{
|
||||
auto key = dht::crypto::PrivateKey::generate();
|
||||
const auto& public_key = key.getPublicKey();
|
||||
|
||||
@@ -140,40 +140,47 @@ CryptoTester::testCertificateRequest()
|
||||
CPPUNIT_ASSERT_MESSAGE(v.toString(), v);
|
||||
}
|
||||
|
||||
void CryptoTester::testCertificateSerialNumber()
|
||||
void
|
||||
CryptoTester::testCertificateSerialNumber()
|
||||
{
|
||||
static const std::string cert_pem = "-----BEGIN CERTIFICATE-----"
|
||||
"MIICDjCCAZSgAwIBAgIIS90uAKp+u/swCgYIKoZIzj0EAwMwTDEQMA4GA1UEAxMH"
|
||||
"ZGh0bm9kZTE4MDYGCgmSJomT8ixkAQETKDBlNDQxZTA4YWJmYTQzYTc3ZTVjZDBm"
|
||||
"Y2QzMzAzMTc4MjYxMTk0MzIwHhcNMTkxMTA3MDA0MTMwWhcNMjkxMTA0MDA0MTMw"
|
||||
"WjBMMRAwDgYDVQQDEwdkaHRub2RlMTgwNgYKCZImiZPyLGQBARMoMGU0NDFlMDhh"
|
||||
"YmZhNDNhNzdlNWNkMGZjZDMzMDMxNzgyNjExOTQzMjB2MBAGByqGSM49AgEGBSuB"
|
||||
"BAAiA2IABCZ7sBp0Pu+b5yIifoNXchU9crv9won0in++COWynvM4GCLF2Gk6QGhh"
|
||||
"YLDxNGsyQjGR7z5AGibvYhNLU0JA4RbmxYWHw4g3wBrPA1jm9hGZ8y5Y8R97d0Hl"
|
||||
"VpyreEMjRKNDMEEwHQYDVR0OBBYEFA5EHgir+kOnflzQ/NMwMXgmEZQyMA8GA1Ud"
|
||||
"EwEB/wQFMAMBAf8wDwYDVR0PAQH/BAUDAwcGADAKBggqhkjOPQQDAwNoADBlAjEA"
|
||||
"kKF/6WReNytwSrJ8JSTToc7xWS5SvAa23Fnla4mywZUxUFS4VSxCMQTjQCknO3iZ"
|
||||
"AjBgxXyXYqn0d7vz7S6oAY5TdaD5YFT5MD2c1MAAp8pxQSwdPa9k0ZSoGIEn31Z0"
|
||||
"GxU="
|
||||
"-----END CERTIFICATE-----";
|
||||
"MIICDjCCAZSgAwIBAgIIS90uAKp+u/swCgYIKoZIzj0EAwMwTDEQMA4GA1UEAxMH"
|
||||
"ZGh0bm9kZTE4MDYGCgmSJomT8ixkAQETKDBlNDQxZTA4YWJmYTQzYTc3ZTVjZDBm"
|
||||
"Y2QzMzAzMTc4MjYxMTk0MzIwHhcNMTkxMTA3MDA0MTMwWhcNMjkxMTA0MDA0MTMw"
|
||||
"WjBMMRAwDgYDVQQDEwdkaHRub2RlMTgwNgYKCZImiZPyLGQBARMoMGU0NDFlMDhh"
|
||||
"YmZhNDNhNzdlNWNkMGZjZDMzMDMxNzgyNjExOTQzMjB2MBAGByqGSM49AgEGBSuB"
|
||||
"BAAiA2IABCZ7sBp0Pu+b5yIifoNXchU9crv9won0in++COWynvM4GCLF2Gk6QGhh"
|
||||
"YLDxNGsyQjGR7z5AGibvYhNLU0JA4RbmxYWHw4g3wBrPA1jm9hGZ8y5Y8R97d0Hl"
|
||||
"VpyreEMjRKNDMEEwHQYDVR0OBBYEFA5EHgir+kOnflzQ/NMwMXgmEZQyMA8GA1Ud"
|
||||
"EwEB/wQFMAMBAf8wDwYDVR0PAQH/BAUDAwcGADAKBggqhkjOPQQDAwNoADBlAjEA"
|
||||
"kKF/6WReNytwSrJ8JSTToc7xWS5SvAa23Fnla4mywZUxUFS4VSxCMQTjQCknO3iZ"
|
||||
"AjBgxXyXYqn0d7vz7S6oAY5TdaD5YFT5MD2c1MAAp8pxQSwdPa9k0ZSoGIEn31Z0"
|
||||
"GxU="
|
||||
"-----END CERTIFICATE-----";
|
||||
/*
|
||||
* $ openssl x509 -in cert.pem -noout -serial
|
||||
* serial=4BDD2E00AA7EBBFB
|
||||
*/
|
||||
static constexpr std::array<uint8_t, 8> SERIAL {{0x4b,0xdd,0x2e,0x00,0xaa,0x7e,0xbb,0xfb}};
|
||||
static constexpr std::array<uint8_t, 8> SERIAL {
|
||||
{0x4b, 0xdd, 0x2e, 0x00, 0xaa, 0x7e, 0xbb, 0xfb}
|
||||
};
|
||||
auto serial = dht::crypto::Certificate(cert_pem).getSerialNumber();
|
||||
CPPUNIT_ASSERT(std::equal(SERIAL.begin(), SERIAL.end(), serial.begin(), serial.end()));
|
||||
}
|
||||
|
||||
void CryptoTester::testOcsp() {
|
||||
void
|
||||
CryptoTester::testOcsp()
|
||||
{
|
||||
auto ca = dht::crypto::generateIdentity("Test CA");
|
||||
auto device = dht::crypto::generateIdentity("Test Device", ca);
|
||||
auto ocspRequest = device.second->generateOcspRequest(ca.second->cert);
|
||||
auto req = dht::crypto::OcspRequest((const uint8_t*)ocspRequest.first.data(), ocspRequest.first.size());
|
||||
auto req = dht::crypto::OcspRequest((const uint8_t*) ocspRequest.first.data(), ocspRequest.first.size());
|
||||
CPPUNIT_ASSERT(ocspRequest.second == req.getNonce());
|
||||
}
|
||||
|
||||
void CryptoTester::testAesEncryption() {
|
||||
void
|
||||
CryptoTester::testAesEncryption()
|
||||
{
|
||||
auto password = "this is a password 123414!@#%@#$?" + std::to_string(rand());
|
||||
|
||||
std::vector<uint8_t> data1 {5, 10};
|
||||
@@ -214,8 +221,8 @@ void CryptoTester::testAesEncryption() {
|
||||
CPPUNIT_ASSERT(encrypted11 == encrypted1);
|
||||
CPPUNIT_ASSERT(encrypted22 == encrypted2);
|
||||
|
||||
auto key12 = dht::crypto::stretchKey(password, salt1, 256/8);
|
||||
auto key22 = dht::crypto::stretchKey(password, salt2, 256/8);
|
||||
auto key12 = dht::crypto::stretchKey(password, salt1, 256 / 8);
|
||||
auto key22 = dht::crypto::stretchKey(password, salt2, 256 / 8);
|
||||
|
||||
CPPUNIT_ASSERT(key1 == key12);
|
||||
CPPUNIT_ASSERT(key2 == key22);
|
||||
@@ -239,7 +246,9 @@ void CryptoTester::testAesEncryption() {
|
||||
CPPUNIT_ASSERT(data2 == decrypted2);
|
||||
}
|
||||
|
||||
void CryptoTester::testAesEncryptionWithMultipleKeySizes() {
|
||||
void
|
||||
CryptoTester::testAesEncryptionWithMultipleKeySizes()
|
||||
{
|
||||
auto data = std::vector<uint8_t>(rand(), rand());
|
||||
|
||||
// Valid key sizes
|
||||
@@ -261,7 +270,204 @@ void CryptoTester::testAesEncryptionWithMultipleKeySizes() {
|
||||
}
|
||||
|
||||
void
|
||||
CryptoTester::tearDown() {
|
||||
CryptoTester::testOaep()
|
||||
{
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030804 && !defined(_WIN32)
|
||||
auto data = std::vector<uint8_t>(446, 10);
|
||||
auto ca = dht::crypto::generateIdentity("n1");
|
||||
|
||||
auto key = dht::crypto::PrivateKey::generate(4096, GNUTLS_PK_RSA_OAEP);
|
||||
|
||||
auto pk = key.getSharedPublicKey();
|
||||
auto encrypted = pk->encrypt(data);
|
||||
|
||||
auto decrypted = key.decrypt(encrypted);
|
||||
CPPUNIT_ASSERT(data == decrypted);
|
||||
|
||||
auto crt = dht::crypto::Certificate::generate(key, "dhtnode", ca);
|
||||
auto pk2 = crt.getSharedPublicKey();
|
||||
|
||||
auto encrypted2 = pk2->encrypt(data);
|
||||
auto decrypted2 = key.decrypt(encrypted2);
|
||||
CPPUNIT_ASSERT(data == decrypted2);
|
||||
|
||||
auto exported = crt.getPacked();
|
||||
dht::crypto::Certificate imported(exported);
|
||||
auto pk3 = imported.getSharedPublicKey();
|
||||
auto encrypted3 = pk3->encrypt(data);
|
||||
auto decrypted3 = key.decrypt(encrypted3);
|
||||
CPPUNIT_ASSERT(data == decrypted3);
|
||||
|
||||
auto exportedPk = pk->toString();
|
||||
dht::crypto::PublicKey importedPk(exportedPk);
|
||||
auto encrypted4 = importedPk.encrypt(data);
|
||||
auto decrypted4 = key.decrypt(encrypted4);
|
||||
CPPUNIT_ASSERT(data == decrypted4);
|
||||
|
||||
auto exportedKey = key.serialize();
|
||||
dht::crypto::PrivateKey importedKey(exportedKey);
|
||||
auto decrypted5 = importedKey.decrypt(encrypted);
|
||||
CPPUNIT_ASSERT(data == decrypted5);
|
||||
#else
|
||||
std::cerr << "Skipping OAEP test, not supported by the current GnuTLS version" << std::endl;
|
||||
#endif
|
||||
}
|
||||
} // namespace test
|
||||
|
||||
void
|
||||
CryptoTester::testWebPushEncryption()
|
||||
{
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030802
|
||||
// 1. Generate Receiver P-256 Key Pair
|
||||
gnutls_privkey_t receiver_key;
|
||||
CPPUNIT_ASSERT_EQUAL(GNUTLS_E_SUCCESS, gnutls_privkey_init(&receiver_key));
|
||||
CPPUNIT_ASSERT_EQUAL(GNUTLS_E_SUCCESS,
|
||||
gnutls_privkey_generate(receiver_key,
|
||||
GNUTLS_PK_ECC,
|
||||
GNUTLS_CURVE_TO_BITS(GNUTLS_ECC_CURVE_SECP256R1),
|
||||
0));
|
||||
|
||||
gnutls_datum_t x, y, k;
|
||||
CPPUNIT_ASSERT_EQUAL(GNUTLS_E_SUCCESS, gnutls_privkey_export_ecc_raw(receiver_key, NULL, &x, &y, &k));
|
||||
|
||||
// Construct receiver public key (uncompressed)
|
||||
std::vector<uint8_t> p256dh(65);
|
||||
p256dh[0] = 0x04;
|
||||
|
||||
auto copy_coord = [](uint8_t* dest, const gnutls_datum_t& src) {
|
||||
if (src.size > 32 && (src.size != 33 || src.data[0] != 0))
|
||||
throw std::runtime_error("Invalid key coordinate size: " + std::to_string(src.size));
|
||||
size_t offset = src.size > 32 ? 1 : 0;
|
||||
size_t len = src.size - offset;
|
||||
std::memset(dest, 0, 32 - len);
|
||||
std::memcpy(dest + (32 - len), src.data + offset, len);
|
||||
};
|
||||
|
||||
copy_coord(p256dh.data() + 1, x);
|
||||
copy_coord(p256dh.data() + 33, y);
|
||||
|
||||
gnutls_free(x.data);
|
||||
gnutls_free(y.data);
|
||||
gnutls_free(k.data);
|
||||
gnutls_privkey_deinit(receiver_key);
|
||||
|
||||
// 2. Generate Auth Secret
|
||||
std::vector<uint8_t> auth(16, 'a'); // Dummy auth
|
||||
|
||||
// 3. Payload
|
||||
std::string payloadStr = "{\"test\":\"data\"}";
|
||||
std::vector<uint8_t> payload(payloadStr.begin(), payloadStr.end());
|
||||
|
||||
std::vector<uint8_t> longPayload(3 * 4096 + 17, 'b');
|
||||
|
||||
// 4. Encrypt
|
||||
auto encrypted = dht::crypto::webPushEncrypt(p256dh, auth, payload.data(), payload.size());
|
||||
auto encryptedLong = dht::crypto::webPushEncrypt(p256dh, auth, longPayload.data(), longPayload.size());
|
||||
|
||||
// 5. Verify Structure
|
||||
// Salt (16) + RS (4) + IDLEN (1) + Key (65) + Ciphertext (payload + 1 + 16)
|
||||
size_t expectedSize = 16 + 4 + 1 + 65 + payload.size() + 1 + 16;
|
||||
CPPUNIT_ASSERT_EQUAL(expectedSize, encrypted.size());
|
||||
|
||||
expectedSize = 16 + 4 + 1 + 65 + longPayload.size() + 1 + 16;
|
||||
CPPUNIT_ASSERT_EQUAL(expectedSize, encryptedLong.size());
|
||||
|
||||
// Check IDLEN
|
||||
CPPUNIT_ASSERT_EQUAL((uint8_t) 65, encrypted[20]);
|
||||
CPPUNIT_ASSERT_EQUAL((uint8_t) 65, encryptedLong[20]);
|
||||
|
||||
// Check Key Header
|
||||
CPPUNIT_ASSERT_EQUAL((uint8_t) 0x04, encrypted[21]);
|
||||
CPPUNIT_ASSERT_EQUAL((uint8_t) 0x04, encryptedLong[21]);
|
||||
#else
|
||||
std::cerr << "Skipping Web Push Encryption test, not supported by the current GnuTLS version" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
CryptoTester::testWebPushRFC8291()
|
||||
{
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030802
|
||||
// RFC 8291 Example
|
||||
// https://www.rfc-editor.org/rfc/rfc8291.html#section-5
|
||||
|
||||
// Plaintext
|
||||
std::string plaintextStr = "When I grow up, I want to be a watermelon";
|
||||
dht::Blob payload(plaintextStr.begin(), plaintextStr.end());
|
||||
|
||||
// Receiver (UA) Public Key (P-256 uncompressed)
|
||||
// Base64URL: BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4
|
||||
dht::Blob uaPub = {0x04, 0x25, 0x71, 0xb2, 0xbe, 0xcd, 0xfd, 0xe3, 0x60, 0x55, 0x1a, 0xaf, 0x1e,
|
||||
0xd0, 0xf4, 0xcd, 0x36, 0x6c, 0x11, 0xce, 0xbe, 0x55, 0x5f, 0x89, 0xbc, 0xb7,
|
||||
0xb1, 0x86, 0xa5, 0x33, 0x39, 0x17, 0x31, 0x68, 0xec, 0xe2, 0xeb, 0xe0, 0x18,
|
||||
0x59, 0x7b, 0xd3, 0x04, 0x79, 0xb8, 0x6e, 0x3c, 0x8f, 0x8e, 0xce, 0xd5, 0x77,
|
||||
0xca, 0x59, 0x18, 0x7e, 0x92, 0x46, 0x99, 0x0d, 0xb6, 0x82, 0x00, 0x8b, 0x0e};
|
||||
|
||||
// Auth Secret
|
||||
// BTBZMqHH6r4Tts7J_aSIgg
|
||||
dht::Blob auth = {0x05, 0x30, 0x59, 0x32, 0xa1, 0xc7, 0xea, 0xbe, 0x13, 0xb6, 0xce, 0xc9, 0xfd, 0xa4, 0x88, 0x82};
|
||||
|
||||
// Salt
|
||||
// DGv6ra1nlYgDCS1FRnbzlw
|
||||
dht::Blob salt = {0x0c, 0x6b, 0xfa, 0xad, 0xad, 0x67, 0x95, 0x88, 0x03, 0x09, 0x2d, 0x45, 0x46, 0x76, 0xf3, 0x97};
|
||||
|
||||
// AS Private Key (Sender)
|
||||
// yfWPiYE-n46HLnH0KqZOF1fJJU3MYrct3AELtAQ-oRw
|
||||
dht::Blob asPrivRaw = {0xc9, 0xf5, 0x8f, 0x89, 0x81, 0x3e, 0x9f, 0x8e, 0x87, 0x2e, 0x71,
|
||||
0xf4, 0x2a, 0xa6, 0x4e, 0x17, 0x57, 0xc9, 0x25, 0x4d, 0xcc, 0x62,
|
||||
0xb7, 0x2d, 0xdc, 0x01, 0x0b, 0xb4, 0x04, 0x3e, 0xa1, 0x1c};
|
||||
|
||||
// AS Public Key (Sender) - for verification
|
||||
// BP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A8
|
||||
dht::Blob asPubRaw = {0x04, 0xfe, 0x33, 0xf4, 0xab, 0x0d, 0xea, 0x71, 0x91, 0x4d, 0xb5, 0x58, 0x23,
|
||||
0xf7, 0x3b, 0x54, 0x94, 0x8f, 0x41, 0x30, 0x6d, 0x92, 0x07, 0x32, 0xdb, 0xb9,
|
||||
0xa5, 0x9a, 0x53, 0x28, 0x64, 0x82, 0x20, 0x0e, 0x59, 0x7a, 0x7b, 0x7b, 0xc2,
|
||||
0x60, 0xba, 0x1c, 0x22, 0x79, 0x98, 0x58, 0x09, 0x92, 0xe9, 0x39, 0x73, 0x00,
|
||||
0x2f, 0x30, 0x12, 0xa2, 0x8a, 0xe8, 0xf0, 0x6b, 0xbb, 0x78, 0xe5, 0xec, 0x0f};
|
||||
|
||||
gnutls_privkey_t asPrivKeyGnutls;
|
||||
gnutls_privkey_init(&asPrivKeyGnutls);
|
||||
gnutls_datum_t k = {(unsigned char*) asPrivRaw.data(), (unsigned int) asPrivRaw.size()};
|
||||
gnutls_datum_t x = {(unsigned char*) asPubRaw.data() + 1, 32};
|
||||
gnutls_datum_t y = {(unsigned char*) asPubRaw.data() + 33, 32};
|
||||
|
||||
int ret = gnutls_privkey_import_ecc_raw(asPrivKeyGnutls, GNUTLS_ECC_CURVE_SECP256R1, &x, &y, &k);
|
||||
CPPUNIT_ASSERT_EQUAL(0, ret);
|
||||
|
||||
dht::crypto::PrivateKey asPrivKey;
|
||||
asPrivKey.key = asPrivKeyGnutls;
|
||||
|
||||
// Expected Ciphertext (from RFC)
|
||||
// 8pfeW0KbunFT06SuDKoJH9Ql87S1QUrdirN6GcG7sFz1y1sqLgVi1VhjVkHsUoEsbI_0LpXMuGvnzQ
|
||||
dht::Blob expectedCiphertext = {0xf2, 0x97, 0xde, 0x5b, 0x42, 0x9b, 0xba, 0x71, 0x53, 0xd3, 0xa4, 0xae,
|
||||
0x0c, 0xaa, 0x09, 0x1f, 0xd4, 0x25, 0xf3, 0xb4, 0xb5, 0x41, 0x4a, 0xdd,
|
||||
0x8a, 0xb3, 0x7a, 0x19, 0xc1, 0xbb, 0xb0, 0x5c, 0xf5, 0xcb, 0x5b, 0x2a,
|
||||
0x2e, 0x05, 0x62, 0xd5, 0x58, 0x63, 0x56, 0x41, 0xec, 0x52, 0x81, 0x2c,
|
||||
0x6c, 0x8f, 0xf4, 0x2e, 0x95, 0xcc, 0xb8, 0x6b, 0xe7, 0xcd};
|
||||
|
||||
// Perform Encryption
|
||||
auto output = dht::crypto::webPushEncrypt(uaPub, auth, payload.data(), payload.size(), asPrivKey, salt);
|
||||
|
||||
// Verify Output
|
||||
size_t headerSize = 16 + 4 + 1 + 65;
|
||||
CPPUNIT_ASSERT(output.size() == headerSize + expectedCiphertext.size());
|
||||
|
||||
// Check Salt
|
||||
dht::Blob outSalt(output.begin(), output.begin() + 16);
|
||||
CPPUNIT_ASSERT(outSalt == salt);
|
||||
|
||||
// Check Ciphertext
|
||||
dht::Blob outCiphertext(output.begin() + headerSize, output.end());
|
||||
CPPUNIT_ASSERT(outCiphertext == expectedCiphertext);
|
||||
|
||||
// Check AS Public Key in header
|
||||
dht::Blob outASPub(output.begin() + 16 + 4 + 1, output.begin() + 16 + 4 + 1 + 65);
|
||||
CPPUNIT_ASSERT(outASPub == asPubRaw);
|
||||
#else
|
||||
std::cerr << "Skipping Web Push RFC8291 test, not supported by the current GnuTLS version" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
CryptoTester::tearDown()
|
||||
{}
|
||||
} // namespace test
|
||||
+11
-3
@@ -26,7 +26,8 @@
|
||||
|
||||
namespace test {
|
||||
|
||||
class CryptoTester : public CppUnit::TestFixture {
|
||||
class CryptoTester : public CppUnit::TestFixture
|
||||
{
|
||||
CPPUNIT_TEST_SUITE(CryptoTester);
|
||||
CPPUNIT_TEST(testSignatureEncryption);
|
||||
CPPUNIT_TEST(testCertificateRevocation);
|
||||
@@ -35,9 +36,12 @@ class CryptoTester : public CppUnit::TestFixture {
|
||||
CPPUNIT_TEST(testOcsp);
|
||||
CPPUNIT_TEST(testAesEncryption);
|
||||
CPPUNIT_TEST(testAesEncryptionWithMultipleKeySizes);
|
||||
CPPUNIT_TEST(testOaep);
|
||||
CPPUNIT_TEST(testWebPushEncryption);
|
||||
CPPUNIT_TEST(testWebPushRFC8291);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
public:
|
||||
/**
|
||||
* Method automatically called before each test by CppUnit
|
||||
*/
|
||||
@@ -71,6 +75,10 @@ class CryptoTester : public CppUnit::TestFixture {
|
||||
*/
|
||||
void testAesEncryption();
|
||||
void testAesEncryptionWithMultipleKeySizes();
|
||||
|
||||
void testOaep();
|
||||
void testWebPushEncryption();
|
||||
void testWebPushRFC8291();
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace test
|
||||
+48
-33
@@ -52,13 +52,13 @@ DhtRunnerTester::tearDown() {
|
||||
std::condition_variable cv;
|
||||
std::mutex cv_m;
|
||||
auto shutdown = [&]{
|
||||
std::lock_guard<std::mutex> lk(cv_m);
|
||||
std::lock_guard lk(cv_m);
|
||||
done++;
|
||||
cv.notify_all();
|
||||
};
|
||||
node1.shutdown(shutdown);
|
||||
node2.shutdown(shutdown);
|
||||
std::unique_lock<std::mutex> lk(cv_m);
|
||||
std::unique_lock lk(cv_m);
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]{ return done == 2u; }));
|
||||
node1.join();
|
||||
node2.join();
|
||||
@@ -207,13 +207,13 @@ DhtRunnerTester::testListen() {
|
||||
|
||||
for (unsigned i=0; i<N; i++) {
|
||||
node2.put(a, dht::Value("v1"), [&](bool ok) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
std::lock_guard lock(mutex);
|
||||
putCount++;
|
||||
if (ok) putOkCount1++;
|
||||
cv.notify_all();
|
||||
});
|
||||
node2.put(b, dht::Value("v2"), [&](bool ok) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
std::lock_guard lock(mutex);
|
||||
putCount++;
|
||||
if (ok) putOkCount2++;
|
||||
cv.notify_all();
|
||||
@@ -221,7 +221,7 @@ DhtRunnerTester::testListen() {
|
||||
auto bigVal = std::make_shared<dht::Value>();
|
||||
bigVal->data = mtu;
|
||||
node2.put(c, std::move(bigVal), [&](bool ok) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
std::lock_guard lock(mutex);
|
||||
putCount++;
|
||||
if (ok) putOkCount3++;
|
||||
cv.notify_all();
|
||||
@@ -229,7 +229,7 @@ DhtRunnerTester::testListen() {
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
std::unique_lock lk(mutex);
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]{ return putCount == N * 3u; }));
|
||||
CPPUNIT_ASSERT_EQUAL(N, putOkCount1);
|
||||
CPPUNIT_ASSERT_EQUAL(N, putOkCount2);
|
||||
@@ -264,6 +264,7 @@ void
|
||||
DhtRunnerTester::testIdOps() {
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
unsigned identityCount(0);
|
||||
unsigned valueCount(0);
|
||||
unsigned valueCountEdit(0);
|
||||
|
||||
@@ -275,8 +276,8 @@ DhtRunnerTester::testIdOps() {
|
||||
dht::DhtRunner::Context context2;
|
||||
context2.identityAnnouncedCb = [&](bool ok) {
|
||||
CPPUNIT_ASSERT(ok);
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
valueCount++;
|
||||
std::lock_guard lk(mutex);
|
||||
identityCount++;
|
||||
cv.notify_all();
|
||||
};
|
||||
|
||||
@@ -288,27 +289,28 @@ DhtRunnerTester::testIdOps() {
|
||||
node2.bootstrap(bound);
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&]{ return valueCount == 1; }));
|
||||
std::unique_lock lk(mutex);
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&]{ return identityCount == 1; }));
|
||||
}
|
||||
|
||||
node1.findCertificate(node2.getId(), [&](const std::shared_ptr<dht::crypto::Certificate>& crt){
|
||||
CPPUNIT_ASSERT(crt);
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
std::lock_guard lk(mutex);
|
||||
valueCount++;
|
||||
cv.notify_all();
|
||||
});
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&]{ return valueCount == 2; }));
|
||||
std::unique_lock lk(mutex);
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&]{ return valueCount == 1u; }));
|
||||
CPPUNIT_ASSERT_EQUAL(1u, identityCount);
|
||||
}
|
||||
|
||||
dht::DhtRunner::Context context1;
|
||||
context1.identityAnnouncedCb = [&](bool ok) {
|
||||
CPPUNIT_ASSERT(ok);
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
valueCount++;
|
||||
std::lock_guard lk(mutex);
|
||||
identityCount++;
|
||||
cv.notify_all();
|
||||
};
|
||||
|
||||
@@ -323,21 +325,21 @@ DhtRunnerTester::testIdOps() {
|
||||
auto key = dht::InfoHash::get("key");
|
||||
node1.putEncrypted(key, node2.getId(), dht::Value("yo"), [&](bool ok){
|
||||
CPPUNIT_ASSERT(ok);
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
std::lock_guard lk(mutex);
|
||||
valueCount++;
|
||||
cv.notify_all();
|
||||
});
|
||||
|
||||
node1.putEncrypted(key, node2.getPublicKey(), dht::Value("yo"), [&](bool ok){
|
||||
node1.putEncrypted(key, node2.getPublicKey(), std::make_shared<dht::Value>("yo"), [&](bool ok){
|
||||
CPPUNIT_ASSERT(ok);
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
std::lock_guard lk(mutex);
|
||||
valueCount++;
|
||||
cv.notify_all();
|
||||
});
|
||||
|
||||
node2.listen<std::string>(key, [&](std::string&& value){
|
||||
CPPUNIT_ASSERT_EQUAL("yo"s, value);
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
std::lock_guard lk(mutex);
|
||||
valueCount++;
|
||||
cv.notify_all();
|
||||
return true;
|
||||
@@ -347,7 +349,7 @@ DhtRunnerTester::testIdOps() {
|
||||
auto editValue = std::make_shared<dht::Value>("v1");
|
||||
node1.putSigned(key2, editValue, [&](bool ok){
|
||||
CPPUNIT_ASSERT(ok);
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
std::lock_guard lk(mutex);
|
||||
valueCountEdit++;
|
||||
cv.notify_all();
|
||||
});
|
||||
@@ -359,16 +361,29 @@ DhtRunnerTester::testIdOps() {
|
||||
CPPUNIT_ASSERT_EQUAL("v2"s, dht::unpackMsg<std::string>(v->data));
|
||||
CPPUNIT_ASSERT_EQUAL(v->owner->getLongId(), node1.getPublicKey()->getLongId());
|
||||
}
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
std::lock_guard lk(mutex);
|
||||
valueCountEdit += values.size();
|
||||
cv.notify_all();
|
||||
return true;
|
||||
});
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&]{ return valueCount == 7; }));
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&]{ return valueCountEdit == 2; }));
|
||||
std::unique_lock lk(mutex);
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&]{ return identityCount == 2u; }));
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&]{ return valueCount == 5u; }));
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&]{ return valueCountEdit == 2u; }));
|
||||
}
|
||||
|
||||
node2.findCertificate(node1.getPublicKey()->getLongId(), [&](const std::shared_ptr<dht::crypto::Certificate>& crt){
|
||||
CPPUNIT_ASSERT(crt);
|
||||
std::lock_guard lk(mutex);
|
||||
valueCount++;
|
||||
cv.notify_all();
|
||||
});
|
||||
|
||||
{
|
||||
std::unique_lock lk(mutex);
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&]{ return valueCount == 6u; }));
|
||||
}
|
||||
|
||||
// editValue->data = dht::packMsg("v2");
|
||||
@@ -376,12 +391,12 @@ DhtRunnerTester::testIdOps() {
|
||||
editValue->data = dht::packMsg("v2");
|
||||
node1.putSigned(key2, editValue, [&](bool ok){
|
||||
CPPUNIT_ASSERT(ok);
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
std::lock_guard lk(mutex);
|
||||
valueCountEdit++;
|
||||
cv.notify_all();
|
||||
});
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&]{ return valueCountEdit == 4; }));
|
||||
std::unique_lock lk(mutex);
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&]{ return valueCountEdit == 4u; }));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -399,14 +414,14 @@ DhtRunnerTester::testListenLotOfBytes() {
|
||||
|
||||
for (unsigned i=0; i<N; i++) {
|
||||
node2.put(foo, data, [&](bool ok) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
std::lock_guard lock(mutex);
|
||||
putCount++;
|
||||
if (ok) putOkCount++;
|
||||
cv.notify_all();
|
||||
});
|
||||
}
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
std::unique_lock lk(mutex);
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]{ return putCount == N; }));
|
||||
}
|
||||
|
||||
@@ -427,7 +442,7 @@ DhtRunnerTester::testListenLotOfBytes() {
|
||||
});
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
std::unique_lock lk(mutex);
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]{ return valueCount == N; }));
|
||||
}
|
||||
|
||||
@@ -447,7 +462,7 @@ DhtRunnerTester::testMultithread() {
|
||||
for (unsigned i=0; i<N; i++) {
|
||||
dht::ThreadPool::computation().run([&]{
|
||||
node2.put(dht::InfoHash::get("123" + std::to_string(i)), "hehe", [&](bool ok) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
std::lock_guard lock(mutex);
|
||||
putCount++;
|
||||
if (ok) putOkCount++;
|
||||
cv.notify_all();
|
||||
@@ -455,14 +470,14 @@ DhtRunnerTester::testMultithread() {
|
||||
node2.get(dht::InfoHash::get("123" + std::to_string(N-i-1)), [](const std::shared_ptr<dht::Value>&){
|
||||
return true;
|
||||
}, [&](bool ok) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
std::lock_guard lock(mutex);
|
||||
putCount++;
|
||||
if (ok) putOkCount++;
|
||||
cv.notify_all();
|
||||
});
|
||||
});
|
||||
}
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
std::unique_lock lk(mutex);
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]{ return putCount == 2*N; }));
|
||||
CPPUNIT_ASSERT_EQUAL(2*N, putOkCount);
|
||||
|
||||
|
||||
+171
-39
@@ -31,12 +31,13 @@ namespace test {
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(HttpTester);
|
||||
|
||||
void
|
||||
HttpTester::setUp() {
|
||||
HttpTester::setUp()
|
||||
{
|
||||
nodePeer = std::make_shared<dht::DhtRunner>();
|
||||
nodePeer->run(0);
|
||||
|
||||
auto nodeProxy = std::make_shared<dht::DhtRunner>();
|
||||
nodeProxy->run(0, /*identity*/{}, /*threaded*/true);
|
||||
nodeProxy->run(0, /*identity*/ {}, /*threaded*/ true);
|
||||
auto bound = nodePeer->getBound();
|
||||
if (bound.isUnspecified())
|
||||
bound.setLoopback();
|
||||
@@ -52,17 +53,19 @@ HttpTester::setUp() {
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::tearDown() {
|
||||
HttpTester::tearDown()
|
||||
{
|
||||
serverProxy.reset();
|
||||
nodePeer->join();
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::test_parse_url() {
|
||||
HttpTester::test_parse_url()
|
||||
{
|
||||
// Arrange
|
||||
std::string url = "http://google.com/";
|
||||
// Act
|
||||
dht::http::Url parsed (url);
|
||||
dht::http::Url parsed(url);
|
||||
// Assert
|
||||
CPPUNIT_ASSERT(parsed.url == url);
|
||||
CPPUNIT_ASSERT(parsed.protocol == "http");
|
||||
@@ -71,11 +74,12 @@ HttpTester::test_parse_url() {
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::test_parse_https_url_no_service() {
|
||||
HttpTester::test_parse_https_url_no_service()
|
||||
{
|
||||
// Arrange
|
||||
std::string url = "https://jami.net/";
|
||||
// Act
|
||||
dht::http::Url parsed (url);
|
||||
dht::http::Url parsed(url);
|
||||
// Assert
|
||||
CPPUNIT_ASSERT(parsed.url == url);
|
||||
CPPUNIT_ASSERT(parsed.protocol == "https");
|
||||
@@ -84,11 +88,12 @@ HttpTester::test_parse_https_url_no_service() {
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::test_parse_url_no_prefix_no_target() {
|
||||
HttpTester::test_parse_url_no_prefix_no_target()
|
||||
{
|
||||
// Arrange
|
||||
std::string url = "google.com";
|
||||
// Act
|
||||
dht::http::Url parsed (url);
|
||||
dht::http::Url parsed(url);
|
||||
// Assert
|
||||
CPPUNIT_ASSERT(parsed.url == url);
|
||||
CPPUNIT_ASSERT(parsed.protocol == "http");
|
||||
@@ -97,11 +102,12 @@ HttpTester::test_parse_url_no_prefix_no_target() {
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::test_parse_url_target() {
|
||||
HttpTester::test_parse_url_target()
|
||||
{
|
||||
// Arrange
|
||||
std::string url = "https://www.google.com:666/going/under";
|
||||
// Act
|
||||
dht::http::Url parsed (url);
|
||||
dht::http::Url parsed(url);
|
||||
// Assert
|
||||
CPPUNIT_ASSERT(parsed.url == url);
|
||||
CPPUNIT_ASSERT(parsed.protocol == "https");
|
||||
@@ -111,11 +117,12 @@ HttpTester::test_parse_url_target() {
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::test_parse_url_query() {
|
||||
HttpTester::test_parse_url_query()
|
||||
{
|
||||
// Arrange
|
||||
std::string url = "http://google.com/?key=1";
|
||||
// Act
|
||||
dht::http::Url parsed (url);
|
||||
dht::http::Url parsed(url);
|
||||
// Assert
|
||||
CPPUNIT_ASSERT(parsed.url == url);
|
||||
CPPUNIT_ASSERT(parsed.protocol == "http");
|
||||
@@ -125,11 +132,12 @@ HttpTester::test_parse_url_query() {
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::test_parse_url_fragment() {
|
||||
HttpTester::test_parse_url_fragment()
|
||||
{
|
||||
// Arrange
|
||||
std::string url = "http://google.com/?key=1#some-important-id";
|
||||
// Act
|
||||
dht::http::Url parsed (url);
|
||||
dht::http::Url parsed(url);
|
||||
// Assert
|
||||
CPPUNIT_ASSERT(parsed.url == url);
|
||||
CPPUNIT_ASSERT(parsed.protocol == "http");
|
||||
@@ -140,11 +148,12 @@ HttpTester::test_parse_url_fragment() {
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::test_parse_url_ipv4() {
|
||||
HttpTester::test_parse_url_ipv4()
|
||||
{
|
||||
// Arrange
|
||||
std::string url = "http://172.217.13.132/";
|
||||
// Act
|
||||
dht::http::Url parsed (url);
|
||||
dht::http::Url parsed(url);
|
||||
// Assert
|
||||
CPPUNIT_ASSERT(parsed.url == url);
|
||||
CPPUNIT_ASSERT(parsed.protocol == "http");
|
||||
@@ -153,11 +162,12 @@ HttpTester::test_parse_url_ipv4() {
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::test_parse_url_no_prefix_no_target_ipv4() {
|
||||
HttpTester::test_parse_url_no_prefix_no_target_ipv4()
|
||||
{
|
||||
// Arrange
|
||||
std::string url = "172.217.13.132";
|
||||
// Act
|
||||
dht::http::Url parsed (url);
|
||||
dht::http::Url parsed(url);
|
||||
// Assert
|
||||
CPPUNIT_ASSERT(parsed.url == url);
|
||||
CPPUNIT_ASSERT(parsed.protocol == "http");
|
||||
@@ -165,11 +175,12 @@ HttpTester::test_parse_url_no_prefix_no_target_ipv4() {
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::test_parse_url_target_ipv4() {
|
||||
HttpTester::test_parse_url_target_ipv4()
|
||||
{
|
||||
// Arrange
|
||||
std::string url = "https://172.217.13.132:666/going/under";
|
||||
// Act
|
||||
dht::http::Url parsed (url);
|
||||
dht::http::Url parsed(url);
|
||||
// Assert
|
||||
CPPUNIT_ASSERT(parsed.url == url);
|
||||
CPPUNIT_ASSERT(parsed.protocol == "https");
|
||||
@@ -179,11 +190,12 @@ HttpTester::test_parse_url_target_ipv4() {
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::test_parse_url_ipv6() {
|
||||
HttpTester::test_parse_url_ipv6()
|
||||
{
|
||||
// Arrange
|
||||
std::string url = "http://[2607:f8b0:4006:804::2004]/";
|
||||
// Act
|
||||
dht::http::Url parsed (url);
|
||||
dht::http::Url parsed(url);
|
||||
// Assert
|
||||
CPPUNIT_ASSERT(parsed.url == url);
|
||||
CPPUNIT_ASSERT(parsed.protocol == "http");
|
||||
@@ -192,11 +204,12 @@ HttpTester::test_parse_url_ipv6() {
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::test_parse_url_no_prefix_no_target_ipv6() {
|
||||
HttpTester::test_parse_url_no_prefix_no_target_ipv6()
|
||||
{
|
||||
// Arrange
|
||||
std::string url = "2607:f8b0:4006:804::2004";
|
||||
// Act
|
||||
dht::http::Url parsed (url);
|
||||
dht::http::Url parsed(url);
|
||||
// Assert
|
||||
CPPUNIT_ASSERT(parsed.url == url);
|
||||
CPPUNIT_ASSERT(parsed.protocol == "http");
|
||||
@@ -204,11 +217,12 @@ HttpTester::test_parse_url_no_prefix_no_target_ipv6() {
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::test_parse_url_target_ipv6() {
|
||||
HttpTester::test_parse_url_target_ipv6()
|
||||
{
|
||||
// Arrange
|
||||
std::string url = "https://[2607:f8b0:4006:804::2004]:666/going/under";
|
||||
// Act
|
||||
dht::http::Url parsed (url);
|
||||
dht::http::Url parsed(url);
|
||||
// Assert
|
||||
CPPUNIT_ASSERT(parsed.url == url);
|
||||
CPPUNIT_ASSERT(parsed.protocol == "https");
|
||||
@@ -218,7 +232,125 @@ HttpTester::test_parse_url_target_ipv6() {
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::test_send_json() {
|
||||
HttpTester::test_parse_url_user_pass()
|
||||
{
|
||||
// Arrange
|
||||
std::string url = "http://user:pass@example.com/";
|
||||
// Act
|
||||
dht::http::Url parsed(url);
|
||||
// Assert
|
||||
CPPUNIT_ASSERT_EQUAL(url, parsed.toString());
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("http"), parsed.protocol);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string_view("user"), parsed.user);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string_view("pass"), parsed.password);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string_view("example.com"), parsed.host);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("/"), parsed.target);
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::test_parse_url_user_only()
|
||||
{
|
||||
// Arrange
|
||||
std::string url = "http://user@example.com/";
|
||||
// Act
|
||||
dht::http::Url parsed(url);
|
||||
// Assert
|
||||
CPPUNIT_ASSERT_EQUAL(url, parsed.toString());
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("http"), parsed.protocol);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string_view("user"), parsed.user);
|
||||
CPPUNIT_ASSERT(parsed.password.empty());
|
||||
CPPUNIT_ASSERT_EQUAL(std::string_view("example.com"), parsed.host);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("/"), parsed.target);
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::test_parse_url_ipv6_brackets()
|
||||
{
|
||||
// Arrange
|
||||
std::string url = "http://[2001:db8::1]/";
|
||||
// Act
|
||||
dht::http::Url parsed(url);
|
||||
// Assert
|
||||
CPPUNIT_ASSERT_EQUAL(url, parsed.toString());
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("http"), parsed.protocol);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string_view("2001:db8::1"), parsed.host);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("/"), parsed.target);
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::test_parse_url_ipv6_brackets_port()
|
||||
{
|
||||
// Arrange
|
||||
std::string url = "http://[2001:db8::1]:8080/";
|
||||
// Act
|
||||
dht::http::Url parsed(url);
|
||||
// Assert
|
||||
CPPUNIT_ASSERT_EQUAL(url, parsed.toString());
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("http"), parsed.protocol);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string_view("2001:db8::1"), parsed.host);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string_view("8080"), parsed.service);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("/"), parsed.target);
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::test_parse_url_mixed_case_protocol()
|
||||
{
|
||||
// Arrange
|
||||
std::string url = "HtTp://example.com/";
|
||||
// Act
|
||||
dht::http::Url parsed(url);
|
||||
// Assert
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("http://example.com/"), parsed.toString());
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("http"), parsed.protocol);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string_view("example.com"), parsed.host);
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::test_parse_url_complex_path_query_fragment()
|
||||
{
|
||||
// Arrange
|
||||
std::string url = "https://example.com/path/to/resource?query=param&q2=v2#fragment";
|
||||
// Act
|
||||
dht::http::Url parsed(url);
|
||||
// Assert
|
||||
CPPUNIT_ASSERT_EQUAL(url, parsed.toString());
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("https"), parsed.protocol);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string_view("example.com"), parsed.host);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("/path/to/resource?query=param&q2=v2"), parsed.target);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string_view("query=param&q2=v2"), parsed.query);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string_view("#fragment"), parsed.fragment);
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::test_parse_url_empty()
|
||||
{
|
||||
// Arrange
|
||||
std::string url = "";
|
||||
// Act
|
||||
dht::http::Url parsed(url);
|
||||
// Assert
|
||||
CPPUNIT_ASSERT(parsed.url.empty());
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("http"), parsed.protocol); // Default
|
||||
CPPUNIT_ASSERT(parsed.host.empty());
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::test_parse_url_just_slash()
|
||||
{
|
||||
// Arrange
|
||||
std::string url = "/";
|
||||
// Act
|
||||
dht::http::Url parsed(url);
|
||||
// Assert
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("http:///"), parsed.toString());
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("http"), parsed.protocol);
|
||||
CPPUNIT_ASSERT(parsed.host.empty());
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("/"), parsed.target);
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::test_send_json()
|
||||
{
|
||||
// Arrange
|
||||
std::condition_variable cv;
|
||||
std::mutex cv_m;
|
||||
@@ -231,19 +363,19 @@ HttpTester::test_send_json() {
|
||||
|
||||
// Act
|
||||
auto request = std::make_shared<dht::http::Request>(serverProxy->io_context(),
|
||||
"http://127.0.0.1:8080/key/key",
|
||||
json,
|
||||
[&](Json::Value value, const dht::http::Response& response) {
|
||||
std::lock_guard<std::mutex> lk(cv_m);
|
||||
resp_val = std::move(value);
|
||||
status = response.status_code;
|
||||
done = true;
|
||||
cv.notify_all();
|
||||
});
|
||||
"http://127.0.0.1:8080/key/key",
|
||||
json,
|
||||
[&](Json::Value value, const dht::http::Response& response) {
|
||||
std::lock_guard<std::mutex> lk(cv_m);
|
||||
resp_val = std::move(value);
|
||||
status = response.status_code;
|
||||
done = true;
|
||||
cv.notify_all();
|
||||
});
|
||||
request->send();
|
||||
|
||||
// Assert
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; }));
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(10), [&] { return done; }));
|
||||
CPPUNIT_ASSERT_EQUAL(200u, status);
|
||||
CPPUNIT_ASSERT_EQUAL(json["data"].asString(), resp_val["data"].asString());
|
||||
done = false;
|
||||
@@ -292,4 +424,4 @@ HttpTester::test_send_json() {
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace test
|
||||
@@ -47,6 +47,14 @@ class HttpTester : public CppUnit::TestFixture {
|
||||
CPPUNIT_TEST(test_parse_url_ipv6);
|
||||
CPPUNIT_TEST(test_parse_url_no_prefix_no_target_ipv6);
|
||||
CPPUNIT_TEST(test_parse_url_target_ipv6);
|
||||
CPPUNIT_TEST(test_parse_url_user_pass);
|
||||
CPPUNIT_TEST(test_parse_url_user_only);
|
||||
CPPUNIT_TEST(test_parse_url_ipv6_brackets);
|
||||
CPPUNIT_TEST(test_parse_url_ipv6_brackets_port);
|
||||
CPPUNIT_TEST(test_parse_url_mixed_case_protocol);
|
||||
CPPUNIT_TEST(test_parse_url_complex_path_query_fragment);
|
||||
CPPUNIT_TEST(test_parse_url_empty);
|
||||
CPPUNIT_TEST(test_parse_url_just_slash);
|
||||
// send
|
||||
CPPUNIT_TEST(test_send_json);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
@@ -82,6 +90,14 @@ class HttpTester : public CppUnit::TestFixture {
|
||||
void test_parse_url_ipv6();
|
||||
void test_parse_url_no_prefix_no_target_ipv6();
|
||||
void test_parse_url_target_ipv6();
|
||||
void test_parse_url_user_pass();
|
||||
void test_parse_url_user_only();
|
||||
void test_parse_url_ipv6_brackets();
|
||||
void test_parse_url_ipv6_brackets_port();
|
||||
void test_parse_url_mixed_case_protocol();
|
||||
void test_parse_url_complex_path_query_fragment();
|
||||
void test_parse_url_empty();
|
||||
void test_parse_url_just_slash();
|
||||
/**
|
||||
* Test send(json)
|
||||
*/
|
||||
|
||||
+15
-13
@@ -9,21 +9,19 @@ function (configure_tool name extra_files)
|
||||
add_dependencies(${name} opendht)
|
||||
if (MSVC)
|
||||
target_sources(${name} PRIVATE ${MSC_COMPAT_SOURCES})
|
||||
target_link_libraries (${name} PUBLIC opendht wsock32 ws2_32)
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
set_target_properties(${name} PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
endif()
|
||||
target_link_libraries (${name} PUBLIC opendht)
|
||||
target_include_directories (${name} PRIVATE SYSTEM ${MSC_COMPAT_DIR} ${MSVC_COMPAT_DIR})
|
||||
else()
|
||||
target_link_libraries (${name} LINK_PUBLIC opendht ${readline_LIBRARIES})
|
||||
target_link_libraries (${name} LINK_PUBLIC opendht ${READLINE_LIBRARIES})
|
||||
target_include_directories (${name} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif ()
|
||||
endfunction ()
|
||||
|
||||
configure_tool (dhtnode tools_common.h)
|
||||
configure_tool (dhtscanner tools_common.h)
|
||||
configure_tool (dhtchat tools_common.h)
|
||||
if (OPENDHT_EXAMPLES)
|
||||
configure_tool (dhtscanner tools_common.h)
|
||||
configure_tool (dhtchat tools_common.h)
|
||||
endif ()
|
||||
if (NOT MSVC)
|
||||
configure_tool (perftest tools_common.h)
|
||||
endif ()
|
||||
@@ -34,17 +32,15 @@ endif ()
|
||||
if (OPENDHT_C)
|
||||
add_executable (dhtcnode dhtcnode.c)
|
||||
add_dependencies(dhtcnode opendht-c)
|
||||
target_link_libraries (dhtcnode PUBLIC opendht-c ${readline_LIBRARIES})
|
||||
target_link_libraries (dhtcnode PUBLIC opendht-c)
|
||||
target_include_directories (dhtcnode SYSTEM PRIVATE ${PROJECT_SOURCE_DIR}/c)
|
||||
if (WIN32)
|
||||
target_link_libraries (dhtcnode PUBLIC ws2_32)
|
||||
endif()
|
||||
if (MSVC)
|
||||
target_sources(dhtcnode PRIVATE ${MSC_COMPAT_SOURCES})
|
||||
target_link_libraries(dhtcnode PRIVATE unofficial::readline-win32::readline)
|
||||
target_include_directories (dhtcnode SYSTEM PRIVATE ${MSC_COMPAT_DIR} ${MSVC_COMPAT_DIR})
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
set_target_properties(dhtcnode PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
endif()
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
@@ -52,9 +48,15 @@ if (NOT DEFINED CMAKE_INSTALL_BINDIR)
|
||||
set(CMAKE_INSTALL_BINDIR bin)
|
||||
endif ()
|
||||
|
||||
install (TARGETS dhtnode dhtscanner dhtchat RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
install (TARGETS dhtnode RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
if (OPENDHT_EXAMPLES)
|
||||
install (TARGETS dhtscanner dhtchat RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endif ()
|
||||
if(MSVC)
|
||||
install(FILES $<TARGET_PDB_FILE:dhtnode> $<TARGET_PDB_FILE:dhtscanner> $<TARGET_PDB_FILE:dhtchat> CONFIGURATIONS "Debug" "RelWithDebInfo" DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
install(FILES $<TARGET_PDB_FILE:dhtnode> CONFIGURATIONS "Debug" "RelWithDebInfo" DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
if (OPENDHT_EXAMPLES)
|
||||
install(FILES $<TARGET_PDB_FILE:dhtscanner> $<TARGET_PDB_FILE:dhtchat> CONFIGURATIONS "Debug" "RelWithDebInfo" DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (OPENDHT_SYSTEMD)
|
||||
|
||||
+8
-1
@@ -24,6 +24,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdatomic.h>
|
||||
#include <inttypes.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include "wingetopt.h"
|
||||
@@ -33,6 +34,7 @@
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
@@ -296,7 +298,12 @@ int main(int argc, char **argv)
|
||||
|
||||
// Wait until shutdown callback is called
|
||||
while (!atomic_load(&ctx.stop)) {
|
||||
usleep(10000);
|
||||
#ifdef _WIN32
|
||||
Sleep(10); // 10ms
|
||||
#else
|
||||
struct timespec ts = {0, 10000000}; // 10ms
|
||||
nanosleep(&ts, NULL);
|
||||
#endif
|
||||
}
|
||||
dht_runner_delete(runner);
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
+12
-12
@@ -34,21 +34,21 @@
|
||||
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
#if !defined(WIN32) && !defined(_WIN32) && !defined(__WIN32__) && !defined(__NT__)
|
||||
#pragma message "Building on UNIX-like platform, linking system getopt"
|
||||
#include <getopt.h>
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
#endif
|
||||
#ifdef _MSC_VER
|
||||
#pragma message("Building on WIN32 platform using MSVC, linking wingetopt")
|
||||
#include "wingetopt.h"
|
||||
#endif
|
||||
#if defined(__MINGW32__) || defined (__MINGW64__)
|
||||
#pragma message "Building on MinGW, linking system-wide getopt"
|
||||
#ifndef _WIN32
|
||||
#include <getopt.h>
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
#else
|
||||
#ifdef _MSC_VER
|
||||
#pragma message("Building on WIN32 platform using MSVC, linking wingetopt")
|
||||
#include "wingetopt.h"
|
||||
#endif
|
||||
#if defined(__MINGW32__) || defined (__MINGW64__)
|
||||
#pragma message "Building on MinGW, linking system-wide getopt"
|
||||
#include <getopt.h>
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__) || defined (__MINGW64__)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"default-registry": {
|
||||
"kind": "git",
|
||||
"baseline": "654410ee8e11f0610c11a4a49a8827a84be5e187",
|
||||
"baseline": "dd3097e305afa53f7b4312371f62058d2e665320",
|
||||
"repository": "https://github.com/microsoft/vcpkg"
|
||||
},
|
||||
"registries": [
|
||||
|
||||
+13
-9
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"name": "opendht",
|
||||
"version-string": "3.4.0",
|
||||
"version-string": "3.6.2",
|
||||
"port-version": 1,
|
||||
"description": "OpenDHT is a distributed hash table (DHT) implementation that provides a decentralized and fault-tolerant way to store and retrieve data across a network of nodes.",
|
||||
"description": "OpenDHT: A lightweight C++17 Distributed Hash Table library.",
|
||||
"homepage": "https://opendht.net",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": [
|
||||
{
|
||||
"name" : "vcpkg-cmake",
|
||||
"host" : true
|
||||
"name": "vcpkg-cmake",
|
||||
"host": true
|
||||
},
|
||||
{
|
||||
"name" : "vcpkg-cmake-config",
|
||||
"host" : true
|
||||
"name": "vcpkg-cmake-config",
|
||||
"host": true
|
||||
},
|
||||
"fmt",
|
||||
"nettle",
|
||||
@@ -20,9 +20,13 @@
|
||||
"msgpack",
|
||||
"jsoncpp",
|
||||
"asio",
|
||||
"argon2"
|
||||
"argon2",
|
||||
"simdutf"
|
||||
],
|
||||
"default-features": [
|
||||
"proxy",
|
||||
"tools"
|
||||
],
|
||||
"default-features": ["proxy", "tools"],
|
||||
"features": {
|
||||
"proxy": {
|
||||
"description": "DHT Proxy",
|
||||
@@ -45,4 +49,4 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user