mirror of
https://github.com/savoirfairelinux/opendht.git
synced 2026-11-08 20:37:23 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf73feb056 | ||
|
|
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()
|
||||
+37
-22
@@ -1,11 +1,12 @@
|
||||
dnl define macros
|
||||
m4_define([opendht_major_version], 3)
|
||||
m4_define([opendht_minor_version], 4)
|
||||
m4_define([opendht_minor_version], 6)
|
||||
m4_define([opendht_patch_version], 0)
|
||||
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.22 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.22
|
||||
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)>;
|
||||
|
||||
|
||||
@@ -204,8 +204,9 @@ 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 {};
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -393,6 +394,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 +440,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();
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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_ {};
|
||||
|
||||
+68
-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,38 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, std::string> 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 +93,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 +164,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 +186,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};
|
||||
};
|
||||
|
||||
+197
-119
@@ -1,29 +1,53 @@
|
||||
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.0',
|
||||
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,
|
||||
io_uring,
|
||||
]
|
||||
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 +72,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 +91,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 +119,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 +219,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],
|
||||
)
|
||||
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 \
|
||||
|
||||
+15
-112
@@ -17,127 +17,30 @@
|
||||
|
||||
#include "base64.h"
|
||||
|
||||
#include <simdutf.h>
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
// Decoding error, return an empty vector
|
||||
return std::vector<unsigned char>();
|
||||
} else {
|
||||
buffer.resize(r.count); // resize the buffer according to actual number of bytes
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
@@ -51,6 +51,4 @@
|
||||
typedef int mode_t;
|
||||
#include <BaseTsd.h>
|
||||
|
||||
#define usleep(x) Sleep((x) / 1000)
|
||||
|
||||
#endif
|
||||
+151
-72
@@ -79,6 +79,31 @@ bool aesKeySizeGood(size_t key_size)
|
||||
return false;
|
||||
}
|
||||
|
||||
class Spki {
|
||||
public:
|
||||
Spki() {
|
||||
gnutls_x509_spki_init(&spki);
|
||||
}
|
||||
Spki(gnutls_pubkey_t pubkey): Spki() {
|
||||
auto err = gnutls_pubkey_get_spki(pubkey, spki, 0);
|
||||
if (err < 0) {
|
||||
gnutls_x509_spki_deinit(spki);
|
||||
throw CryptoException(fmt::format("Unable to get SPKI from public key: {}", gnutls_strerror(err)));
|
||||
}
|
||||
}
|
||||
|
||||
~Spki() {
|
||||
gnutls_x509_spki_deinit(spki);
|
||||
}
|
||||
|
||||
inline gnutls_x509_spki_t& get() {
|
||||
return spki;
|
||||
}
|
||||
|
||||
private:
|
||||
gnutls_x509_spki_t spki;
|
||||
};
|
||||
|
||||
#ifndef GCM_DIGEST_SIZE
|
||||
#define GCM_DIGEST_SIZE GCM_BLOCK_SIZE
|
||||
#endif
|
||||
@@ -86,7 +111,7 @@ bool aesKeySizeGood(size_t key_size)
|
||||
Blob aesEncrypt(const uint8_t* data, size_t data_length, const Blob& key)
|
||||
{
|
||||
if (not aesKeySizeGood(key.size()))
|
||||
throw DecryptError("Wrong key size");
|
||||
throw DecryptError("Incorrect key size");
|
||||
|
||||
Blob ret(data_length + GCM_IV_SIZE + GCM_DIGEST_SIZE);
|
||||
{
|
||||
@@ -127,10 +152,10 @@ Blob aesEncrypt(const Blob& data, std::string_view password, const Blob& salt)
|
||||
Blob aesDecrypt(const uint8_t* data, size_t data_length, const Blob& key)
|
||||
{
|
||||
if (not aesKeySizeGood(key.size()))
|
||||
throw DecryptError("Wrong key size");
|
||||
throw DecryptError("Incorrect key size");
|
||||
|
||||
if (data_length <= GCM_IV_SIZE + GCM_DIGEST_SIZE)
|
||||
throw DecryptError("Wrong data size");
|
||||
throw DecryptError("Incorrect data size");
|
||||
|
||||
std::array<uint8_t, GCM_DIGEST_SIZE> digest;
|
||||
|
||||
@@ -158,7 +183,7 @@ Blob aesDecrypt(const uint8_t* data, size_t data_length, const Blob& key)
|
||||
}
|
||||
|
||||
if (not std::equal(digest.begin(), digest.end(), data + data_length - GCM_DIGEST_SIZE)) {
|
||||
throw DecryptError("Can't decrypt data");
|
||||
throw DecryptError("Unable to decrypt data");
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -175,14 +200,14 @@ Blob aesDecrypt(const uint8_t* data, size_t data_length, std::string_view passwo
|
||||
Blob aesGetSalt(const uint8_t* data, size_t data_length)
|
||||
{
|
||||
if (data_length <= PASSWORD_SALT_LENGTH)
|
||||
throw DecryptError("Wrong data size");
|
||||
throw DecryptError("Incorrect data size");
|
||||
return Blob {data, data+PASSWORD_SALT_LENGTH};
|
||||
}
|
||||
|
||||
std::string_view aesGetEncrypted(const uint8_t* data, size_t data_length)
|
||||
{
|
||||
if (data_length <= PASSWORD_SALT_LENGTH)
|
||||
throw DecryptError("Wrong data size");
|
||||
throw DecryptError("Incorrect data size");
|
||||
return std::string_view((const char*)(data+PASSWORD_SALT_LENGTH), data_length - PASSWORD_SALT_LENGTH);
|
||||
}
|
||||
|
||||
@@ -212,7 +237,7 @@ Blob stretchKey(std::string_view password, Blob& salt, size_t key_length)
|
||||
res.resize(32);
|
||||
auto ret = argon2i_hash_raw(16, 64*1024, 1, password.data(), password.size(), salt.data(), salt.size(), res.data(), res.size());
|
||||
if (ret != ARGON2_OK)
|
||||
throw CryptoException("Can't compute argon2i !");
|
||||
throw CryptoException("Unable to compute Argon2i.");
|
||||
return hash(res, key_length);
|
||||
}
|
||||
|
||||
@@ -224,7 +249,7 @@ Blob hash(const Blob& data, size_t hash_len)
|
||||
res.resize(res_size);
|
||||
const gnutls_datum_t gdat {(uint8_t*)data.data(), (unsigned)data.size()};
|
||||
if (auto err = gnutls_fingerprint(algo, &gdat, res.data(), &res_size))
|
||||
throw CryptoException(std::string("Can't compute hash: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to compute hash: ") + gnutls_strerror(err));
|
||||
res.resize(std::min(hash_len, res_size));
|
||||
return res;
|
||||
}
|
||||
@@ -235,7 +260,7 @@ void hash(const uint8_t* data, size_t data_length, uint8_t* hash, size_t hash_le
|
||||
size_t res_size = hash_length;
|
||||
const gnutls_datum_t gdat {(uint8_t*)data, (unsigned)data_length};
|
||||
if (auto err = gnutls_fingerprint(algo, &gdat, hash, &res_size))
|
||||
throw CryptoException(std::string("Can't compute hash: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to compute hash: ") + gnutls_strerror(err));
|
||||
}
|
||||
|
||||
PrivateKey::PrivateKey()
|
||||
@@ -246,7 +271,7 @@ PrivateKey::PrivateKey(gnutls_x509_privkey_t k) : x509_key(k)
|
||||
gnutls_privkey_init(&key);
|
||||
if (gnutls_privkey_import_x509(key, k, GNUTLS_PRIVKEY_IMPORT_COPY) != GNUTLS_E_SUCCESS) {
|
||||
key = nullptr;
|
||||
throw CryptoException("Can't load generic private key !");
|
||||
throw CryptoException("Unable to load generic private key.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +279,7 @@ PrivateKey::PrivateKey(const uint8_t* src, size_t src_size, const char* password
|
||||
{
|
||||
int err = gnutls_x509_privkey_init(&x509_key);
|
||||
if (err != GNUTLS_E_SUCCESS)
|
||||
throw CryptoException("Can't initialize private key !");
|
||||
throw CryptoException("Unable to initialize private key.");
|
||||
|
||||
const gnutls_datum_t dt {(uint8_t*)src, static_cast<unsigned>(src_size)};
|
||||
int flags = (password_ptr == NULL || strlen(password_ptr)==0) ? GNUTLS_PKCS_PLAIN
|
||||
@@ -267,16 +292,16 @@ PrivateKey::PrivateKey(const uint8_t* src, size_t src_size, const char* password
|
||||
if (err_der != GNUTLS_E_SUCCESS) {
|
||||
gnutls_x509_privkey_deinit(x509_key);
|
||||
if (err == GNUTLS_E_DECRYPTION_FAILED or err_der == GNUTLS_E_DECRYPTION_FAILED)
|
||||
throw DecryptError("Can't decrypt private key");
|
||||
throw DecryptError("Unable to decrypt private key");
|
||||
else
|
||||
throw CryptoException(std::string("Can't load private key: PEM: ") + gnutls_strerror(err)
|
||||
throw CryptoException(std::string("Unable to load private key: PEM: ") + gnutls_strerror(err)
|
||||
+ " DER: " + gnutls_strerror(err_der));
|
||||
}
|
||||
}
|
||||
|
||||
gnutls_privkey_init(&key);
|
||||
if (gnutls_privkey_import_x509(key, x509_key, GNUTLS_PRIVKEY_IMPORT_COPY) != GNUTLS_E_SUCCESS) {
|
||||
throw CryptoException("Can't load generic private key !");
|
||||
throw CryptoException("Unable to load generic private key.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,13 +343,13 @@ Blob
|
||||
PrivateKey::sign(const uint8_t* data, size_t data_length) const
|
||||
{
|
||||
if (!key)
|
||||
throw CryptoException("Can't sign data: no private key set !");
|
||||
throw CryptoException("Unable to sign data: no private key set.");
|
||||
if (std::numeric_limits<unsigned>::max() < data_length)
|
||||
throw CryptoException("Can't sign data: too large !");
|
||||
throw CryptoException("Unable to sign data: too large.");
|
||||
gnutls_datum_t sig {nullptr, 0};
|
||||
const gnutls_datum_t dat {(unsigned char*)data, (unsigned)data_length};
|
||||
if (gnutls_privkey_sign_data(key, GNUTLS_DIG_SHA512, 0, &dat, &sig) != GNUTLS_E_SUCCESS)
|
||||
throw CryptoException("Can't sign data !");
|
||||
throw CryptoException("Unable to sign data.");
|
||||
Blob ret(sig.data, sig.data+sig.size);
|
||||
gnutls_free(sig.data);
|
||||
return ret;
|
||||
@@ -337,7 +362,7 @@ PrivateKey::decryptBloc(const uint8_t* src, size_t src_size) const
|
||||
gnutls_datum_t out {nullptr, 0};
|
||||
int err = gnutls_privkey_decrypt_data(key, 0, &dat, &out);
|
||||
if (err != GNUTLS_E_SUCCESS)
|
||||
throw DecryptError(std::string("Can't decrypt data: ") + gnutls_strerror(err));
|
||||
throw DecryptError(std::string("Unable to decrypt data: ") + gnutls_strerror(err));
|
||||
Blob ret {out.data, out.data+out.size};
|
||||
gnutls_free(out.data);
|
||||
return ret;
|
||||
@@ -347,13 +372,17 @@ Blob
|
||||
PrivateKey::decrypt(const uint8_t* cypher, size_t cypher_len) const
|
||||
{
|
||||
if (!key)
|
||||
throw CryptoException("Can't decrypt data without private key !");
|
||||
throw CryptoException("Unable to decrypt data without private key.");
|
||||
|
||||
unsigned key_len = 0;
|
||||
int err = gnutls_privkey_get_pk_algorithm(key, &key_len);
|
||||
if (err < 0)
|
||||
throw CryptoException("Can't read public key length !");
|
||||
if (err != GNUTLS_PK_RSA)
|
||||
int algo = gnutls_privkey_get_pk_algorithm(key, &key_len);
|
||||
if (algo < 0)
|
||||
throw CryptoException("Unable to read public key length.");
|
||||
if (algo != GNUTLS_PK_RSA
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030804
|
||||
&& algo != GNUTLS_PK_RSA_OAEP
|
||||
#endif
|
||||
)
|
||||
throw CryptoException("Must be an RSA key");
|
||||
|
||||
unsigned cypher_block_sz = key_len / 8;
|
||||
@@ -405,7 +434,7 @@ PrivateKey::getSharedPublicKey() const
|
||||
if (not publicKey_) {
|
||||
auto pk = std::make_shared<PublicKey>();
|
||||
if (auto err = gnutls_pubkey_import_privkey(pk->pk, key, GNUTLS_KEY_KEY_CERT_SIGN | GNUTLS_KEY_CRL_SIGN, 0))
|
||||
throw CryptoException(std::string("Can't retreive public key: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to retreive public key: ") + gnutls_strerror(err));
|
||||
publicKey_ = std::move(pk);
|
||||
}
|
||||
return publicKey_;
|
||||
@@ -414,7 +443,7 @@ PrivateKey::getSharedPublicKey() const
|
||||
PublicKey::PublicKey()
|
||||
{
|
||||
if (auto err = gnutls_pubkey_init(&pk))
|
||||
throw CryptoException(std::string("Can't initialize public key: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to initialize public key: ") + gnutls_strerror(err));
|
||||
}
|
||||
|
||||
PublicKey::PublicKey(const uint8_t* dat, size_t dat_size) : PublicKey()
|
||||
@@ -515,7 +544,7 @@ PublicKey::encryptBloc(const uint8_t* src, size_t src_size, uint8_t* dst, size_t
|
||||
gnutls_datum_t encrypted {nullptr, 0};
|
||||
auto err = gnutls_pubkey_encrypt_data(pk, 0, &key_dat, &encrypted);
|
||||
if (err != GNUTLS_E_SUCCESS)
|
||||
throw CryptoException(std::string("Can't encrypt data: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to encrypt data: ") + gnutls_strerror(err));
|
||||
if (encrypted.size != dst_size)
|
||||
throw CryptoException("Unexpected cypherblock size");
|
||||
std::copy_n(encrypted.data, encrypted.size, dst);
|
||||
@@ -526,18 +555,37 @@ Blob
|
||||
PublicKey::encrypt(const uint8_t* data, size_t data_len) const
|
||||
{
|
||||
if (!pk)
|
||||
throw CryptoException("Can't read public key !");
|
||||
throw CryptoException("Unable to read public key.");
|
||||
|
||||
unsigned key_len = 0;
|
||||
int err = gnutls_pubkey_get_pk_algorithm(pk, &key_len);
|
||||
if (err < 0)
|
||||
throw CryptoException("Can't read public key length !");
|
||||
if (err != GNUTLS_PK_RSA)
|
||||
int algo = gnutls_pubkey_get_pk_algorithm(pk, &key_len);
|
||||
if (algo < 0)
|
||||
throw CryptoException("Unable to read public key length.");
|
||||
unsigned max_block_sz = 0;
|
||||
|
||||
if (algo == GNUTLS_PK_RSA) {
|
||||
max_block_sz = key_len / 8 - 11;
|
||||
}
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030804
|
||||
else if (algo == GNUTLS_PK_RSA_OAEP) {
|
||||
Spki spki(pk);
|
||||
gnutls_digest_algorithm_t dig;
|
||||
gnutls_datum_t label = {nullptr, 0};
|
||||
if (gnutls_x509_spki_get_rsa_oaep_params(spki.get(), &dig, &label) < 0) {
|
||||
throw CryptoException("Unable to get OAEP params");
|
||||
}
|
||||
size_t hash_size = gnutlsHashSize(dig);
|
||||
if (hash_size == 0) {
|
||||
throw CryptoException("Invalid digest algorithm for OAEP: GNUTLS_DIG_UNKNOWN");
|
||||
}
|
||||
max_block_sz = key_len / 8 - 2 * hash_size - 2 - label.size;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
throw CryptoException("Must be an RSA key");
|
||||
|
||||
const unsigned max_block_sz = key_len / 8 - 11;
|
||||
}
|
||||
|
||||
const unsigned cypher_block_sz = key_len / 8;
|
||||
|
||||
/* Use plain RSA if the data is small enough */
|
||||
if (data_len <= max_block_sz) {
|
||||
Blob ret(cypher_block_sz);
|
||||
@@ -574,9 +622,9 @@ PublicKey::getId() const
|
||||
InfoHash id;
|
||||
size_t sz = id.size();
|
||||
if (auto err = gnutls_pubkey_get_key_id(pk, 0, id.data(), &sz))
|
||||
throw CryptoException(std::string("Can't get public key ID: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to get public key ID: ") + gnutls_strerror(err));
|
||||
if (sz != id.size())
|
||||
throw CryptoException("Can't get public key ID: wrong output length.");
|
||||
throw CryptoException("Unable to get public key ID: incorrect output length.");
|
||||
cachedId_ = id;
|
||||
idCached_.store(true);
|
||||
}
|
||||
@@ -590,9 +638,9 @@ PublicKey::getLongId() const
|
||||
PkId h;
|
||||
size_t sz = h.size();
|
||||
if (auto err = gnutls_pubkey_get_key_id(pk, GNUTLS_KEYID_USE_SHA256, h.data(), &sz))
|
||||
throw CryptoException(std::string("Can't get 256 bits public key ID: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to get 256-bit public key ID: ") + gnutls_strerror(err));
|
||||
if (sz != h.size())
|
||||
throw CryptoException("Can't get 256 bits public key ID: wrong output length.");
|
||||
throw CryptoException("Unable to get 256-bit public key ID: incorrect output length.");
|
||||
cachedLongId_ = h;
|
||||
longIdCached_.store(true);
|
||||
}
|
||||
@@ -661,14 +709,14 @@ readDN(gnutls_x509_crq_t request, const char* oid)
|
||||
CertificateRequest::CertificateRequest()
|
||||
{
|
||||
if (auto err = gnutls_x509_crq_init(&request))
|
||||
throw CryptoException(std::string("Can't initialize certificate request: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to initialize certificate request: ") + gnutls_strerror(err));
|
||||
}
|
||||
|
||||
CertificateRequest::CertificateRequest(const uint8_t* data, size_t size) : CertificateRequest()
|
||||
{
|
||||
const gnutls_datum_t dat {(uint8_t*)data, (unsigned)size};
|
||||
if (auto err = gnutls_x509_crq_import(request, &dat, GNUTLS_X509_FMT_PEM))
|
||||
throw CryptoException(std::string("Can't import certificate request: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to import certificate request: ") + gnutls_strerror(err));
|
||||
}
|
||||
|
||||
CertificateRequest::~CertificateRequest()
|
||||
@@ -727,14 +775,14 @@ CertificateRequest::sign(const PrivateKey& key, const std::string& password)
|
||||
gnutls_x509_crq_set_challenge_password(request, password.c_str());
|
||||
|
||||
if (auto err = gnutls_x509_crq_set_key(request, key.x509_key))
|
||||
throw CryptoException(std::string("Can't set certificate request key: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to set certificate request key: ") + gnutls_strerror(err));
|
||||
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030601
|
||||
if (auto err = gnutls_x509_crq_privkey_sign(request, key.key, key.getPublicKey().getPreferredDigest(), 0))
|
||||
throw CryptoException(std::string("Can't sign certificate request: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to sign certificate request: ") + gnutls_strerror(err));
|
||||
#else
|
||||
if (auto err = gnutls_x509_crq_privkey_sign(request, key.key, GNUTLS_DIG_UNKNOWN, 0))
|
||||
throw CryptoException(std::string("Can't sign certificate request: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to sign certificate request: ") + gnutls_strerror(err));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -749,7 +797,7 @@ CertificateRequest::pack() const
|
||||
{
|
||||
gnutls_datum_t dat {nullptr, 0};
|
||||
if (auto err = gnutls_x509_crq_export2(request, GNUTLS_X509_FMT_PEM, &dat))
|
||||
throw CryptoException(std::string("Can't export certificate request: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to export certificate request: ") + gnutls_strerror(err));
|
||||
Blob ret(dat.data, dat.data + dat.size);
|
||||
gnutls_free(dat.data);
|
||||
return ret;
|
||||
@@ -760,7 +808,7 @@ CertificateRequest::toString() const
|
||||
{
|
||||
gnutls_datum_t dat {nullptr, 0};
|
||||
if (auto err = gnutls_x509_crq_export2(request, GNUTLS_X509_FMT_PEM, &dat))
|
||||
throw CryptoException(std::string("Can't export certificate request: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to export certificate request: ") + gnutls_strerror(err));
|
||||
std::string ret(dat.data, dat.data + dat.size);
|
||||
gnutls_free(dat.data);
|
||||
return ret;
|
||||
@@ -891,7 +939,7 @@ Certificate::getSharedPublicKey() const
|
||||
if (not publicKey_) {
|
||||
auto pk = std::make_shared<PublicKey>();
|
||||
if (auto err = gnutls_pubkey_import_x509(pk->pk, cert, 0))
|
||||
throw CryptoException(std::string("Can't get certificate public key: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to get certificate public key: ") + gnutls_strerror(err));
|
||||
publicKey_ = std::move(pk);
|
||||
}
|
||||
return publicKey_;
|
||||
@@ -904,9 +952,9 @@ Certificate::getId() const
|
||||
InfoHash id;
|
||||
size_t sz = id.size();
|
||||
if (auto err = gnutls_x509_crt_get_key_id(cert, 0, id.data(), &sz))
|
||||
throw CryptoException(std::string("Can't get certificate public key ID: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to get certificate public key ID: ") + gnutls_strerror(err));
|
||||
if (sz != id.size())
|
||||
throw CryptoException("Can't get certificate public key ID: wrong output length.");
|
||||
throw CryptoException("Unable to get certificate public key ID: incorrect output length.");
|
||||
cachedId_ = id;
|
||||
idCached_.store(true);
|
||||
}
|
||||
@@ -920,9 +968,9 @@ Certificate::getLongId() const
|
||||
PkId id;
|
||||
size_t sz = id.size();
|
||||
if (auto err = gnutls_x509_crt_get_key_id(cert, GNUTLS_KEYID_USE_SHA256, id.data(), &sz))
|
||||
throw CryptoException(std::string("Can't get certificate 256 bits public key ID: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to get certificate 256-bit public key ID: ") + gnutls_strerror(err));
|
||||
if (sz != id.size())
|
||||
throw CryptoException("Can't get certificate 256 bits public key ID: wrong output length.");
|
||||
throw CryptoException("Unable to get certificate 256-bit public key ID: incorrect output length.");
|
||||
cachedLongId_ = id;
|
||||
longIdCached_.store(true);
|
||||
}
|
||||
@@ -1129,15 +1177,46 @@ Certificate::generateOcspRequest(gnutls_x509_crt_t& issuer)
|
||||
// PrivateKey
|
||||
|
||||
PrivateKey
|
||||
PrivateKey::generate(unsigned key_length)
|
||||
PrivateKey::generate(unsigned key_length, gnutls_pk_algorithm_t algo)
|
||||
{
|
||||
gnutls_x509_privkey_t key;
|
||||
if (gnutls_x509_privkey_init(&key) != GNUTLS_E_SUCCESS)
|
||||
throw CryptoException("Can't initialize private key.");
|
||||
int err = gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, key_length, 0);
|
||||
if (err != GNUTLS_E_SUCCESS) {
|
||||
throw CryptoException("Unable to initialize private key.");
|
||||
if (algo == GNUTLS_PK_RSA) {
|
||||
int err = gnutls_x509_privkey_generate(key, algo, key_length, 0);
|
||||
if (err != GNUTLS_E_SUCCESS) {
|
||||
gnutls_x509_privkey_deinit(key);
|
||||
throw CryptoException(std::string("Unable to generate RSA key pair: ") + gnutls_strerror(err));
|
||||
}
|
||||
}
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030804
|
||||
else if (algo == GNUTLS_PK_RSA_OAEP) {
|
||||
int err;
|
||||
Spki spki;
|
||||
err = gnutls_x509_spki_set_rsa_oaep_params(spki.get(), GNUTLS_DIG_SHA256, nullptr);
|
||||
if (err != GNUTLS_E_SUCCESS) {
|
||||
gnutls_x509_privkey_deinit(key);
|
||||
throw CryptoException(std::string("Unable to set RSA-OAEP params: ") + gnutls_strerror(err));
|
||||
}
|
||||
/*gnutls_keygen_data_st params;
|
||||
params.type = GNUTLS_KEYGEN_SPKI;
|
||||
params.data = (uint8_t*)&spki.get();
|
||||
params.size = spki.size();*/
|
||||
err = gnutls_x509_privkey_generate2(key, algo, key_length, 0, nullptr, 0);
|
||||
if (err != GNUTLS_E_SUCCESS) {
|
||||
gnutls_x509_privkey_deinit(key);
|
||||
throw CryptoException(std::string("Unable to generate RSA key pair: ") + gnutls_strerror(err));
|
||||
}
|
||||
err = gnutls_x509_privkey_set_spki(key, spki.get(), 0);
|
||||
if (err != GNUTLS_E_SUCCESS) {
|
||||
gnutls_x509_privkey_deinit(key);
|
||||
throw CryptoException(std::string("Unable to set SPKI: ") + gnutls_strerror(err));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
gnutls_x509_privkey_deinit(key);
|
||||
throw CryptoException(std::string("Can't generate RSA key pair: ") + gnutls_strerror(err));
|
||||
throw CryptoException("Only RSA and RSA-OAEP keys are supported");
|
||||
}
|
||||
return PrivateKey{key};
|
||||
}
|
||||
@@ -1147,11 +1226,11 @@ PrivateKey::generateEC()
|
||||
{
|
||||
gnutls_x509_privkey_t key;
|
||||
if (gnutls_x509_privkey_init(&key) != GNUTLS_E_SUCCESS)
|
||||
throw CryptoException("Can't initialize private key.");
|
||||
throw CryptoException("Unable to initialize private key.");
|
||||
int err = gnutls_x509_privkey_generate(key, GNUTLS_PK_EC, gnutls_sec_param_to_pk_bits(GNUTLS_PK_EC, GNUTLS_SEC_PARAM_ULTRA), 0);
|
||||
if (err != GNUTLS_E_SUCCESS) {
|
||||
gnutls_x509_privkey_deinit(key);
|
||||
throw CryptoException(std::string("Can't generate EC key pair: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to generate EC key pair: ") + gnutls_strerror(err));
|
||||
}
|
||||
return PrivateKey{key};
|
||||
}
|
||||
@@ -1328,20 +1407,20 @@ Certificate::generate(const CertificateRequest& request, const Identity& ca, int
|
||||
{
|
||||
gnutls_x509_crt_t cert;
|
||||
if (auto err = gnutls_x509_crt_init(&cert))
|
||||
throw CryptoException(std::string("Can't initialize certificate: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to initialize certificate: ") + gnutls_strerror(err));
|
||||
Certificate ret {cert};
|
||||
if (auto err = gnutls_x509_crt_set_crq(cert, request.get()))
|
||||
throw CryptoException(std::string("Can't initialize certificate: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to initialize certificate: ") + gnutls_strerror(err));
|
||||
|
||||
if (auto err = gnutls_x509_crt_set_version(cert, 3)) {
|
||||
throw CryptoException(std::string("Can't set certificate version: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to set certificate version: ") + gnutls_strerror(err));
|
||||
}
|
||||
|
||||
setValidityPeriod(cert, validity <= 0 ? 10 * 365 * 24 * 60 * 60 : validity);
|
||||
setRandomSerial(cert);
|
||||
|
||||
if (auto err = gnutls_x509_crt_privkey_sign(cert, ca.second->cert, ca.first->key, ca.second->getPreferredDigest(), 0)) {
|
||||
throw CryptoException(std::string("Can't sign certificate: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to sign certificate: ") + gnutls_strerror(err));
|
||||
}
|
||||
ret.issuer = ca.second;
|
||||
|
||||
@@ -1628,7 +1707,7 @@ RevocationList::pack(Blob& b) const
|
||||
{
|
||||
gnutls_datum_t gdat {nullptr, 0};
|
||||
if (auto err = gnutls_x509_crl_export2(crl, GNUTLS_X509_FMT_DER, &gdat)) {
|
||||
throw CryptoException(std::string("Can't export CRL: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to export CRL: ") + gnutls_strerror(err));
|
||||
}
|
||||
b.insert(b.end(), gdat.data, gdat.data + gdat.size);
|
||||
gnutls_free(gdat.data);
|
||||
@@ -1638,11 +1717,11 @@ void
|
||||
RevocationList::unpack(const uint8_t* dat, size_t dat_size)
|
||||
{
|
||||
if (std::numeric_limits<unsigned>::max() < dat_size)
|
||||
throw CryptoException("Can't load CRL: too large!");
|
||||
throw CryptoException("Unable to load CRL: too large.");
|
||||
const gnutls_datum_t gdat {(uint8_t*)dat, (unsigned)dat_size};
|
||||
if (auto err_pem = gnutls_x509_crl_import(crl, &gdat, GNUTLS_X509_FMT_PEM))
|
||||
if (auto err_der = gnutls_x509_crl_import(crl, &gdat, GNUTLS_X509_FMT_DER)) {
|
||||
throw CryptoException(std::string("Can't load CRL: PEM: ") + gnutls_strerror(err_pem)
|
||||
throw CryptoException(std::string("Unable to load CRL: PEM: ") + gnutls_strerror(err_pem)
|
||||
+ " DER: " + gnutls_strerror(err_der));
|
||||
}
|
||||
}
|
||||
@@ -1667,7 +1746,7 @@ RevocationList::isRevoked(const Certificate& crt) const
|
||||
{
|
||||
auto ret = gnutls_x509_crt_check_revocation(crt.cert, &crl, 1);
|
||||
if (ret < 0)
|
||||
throw CryptoException(std::string("Can't check certificate revocation status: ") + gnutls_strerror(ret));
|
||||
throw CryptoException(std::string("Unable to check certificate revocation status: ") + gnutls_strerror(ret));
|
||||
return ret != 0;
|
||||
}
|
||||
|
||||
@@ -1677,7 +1756,7 @@ RevocationList::revoke(const Certificate& crt, std::chrono::system_clock::time_p
|
||||
if (t == time_point::min())
|
||||
t = clock::now();
|
||||
if (auto err = gnutls_x509_crl_set_crt(crl, crt.cert, std::chrono::system_clock::to_time_t(t)))
|
||||
throw CryptoException(std::string("Can't revoke certificate: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to revoke certificate: ") + gnutls_strerror(err));
|
||||
}
|
||||
|
||||
static std::string
|
||||
@@ -1753,13 +1832,13 @@ void
|
||||
RevocationList::sign(const PrivateKey& key, const Certificate& ca, duration validity)
|
||||
{
|
||||
if (auto err = gnutls_x509_crl_set_version(crl, 2))
|
||||
throw CryptoException(std::string("Can't set CRL version: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to set CRL version: ") + gnutls_strerror(err));
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto next_update = (validity == duration{}) ? ca.getExpiration() : now + validity;
|
||||
if (auto err = gnutls_x509_crl_set_this_update(crl, std::chrono::system_clock::to_time_t(now)))
|
||||
throw CryptoException(std::string("Can't set CRL update time: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to set CRL update time: ") + gnutls_strerror(err));
|
||||
if (auto err = gnutls_x509_crl_set_next_update(crl, std::chrono::system_clock::to_time_t(next_update)))
|
||||
throw CryptoException(std::string("Can't set CRL next update time: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to set CRL next update time: ") + gnutls_strerror(err));
|
||||
uint64_t number {0};
|
||||
size_t number_sz {sizeof(number)};
|
||||
unsigned critical {0};
|
||||
@@ -1772,9 +1851,9 @@ RevocationList::sign(const PrivateKey& key, const Certificate& ca, duration vali
|
||||
} else
|
||||
number = endian(endian(number) + 1);
|
||||
if (auto err = gnutls_x509_crl_set_number(crl, &number, sizeof(number)))
|
||||
throw CryptoException(std::string("Can't set CRL update time: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to set CRL update time: ") + gnutls_strerror(err));
|
||||
if (auto err = gnutls_x509_crl_sign2(crl, ca.cert, key.x509_key, GNUTLS_DIG_SHA512, 0))
|
||||
throw CryptoException(std::string("Can't sign certificate revocation list: ") + gnutls_strerror(err));
|
||||
throw CryptoException(std::string("Unable to sign certificate revocation list: ") + gnutls_strerror(err));
|
||||
// to be able to actually use the CRL we need to serialize/deserialize it
|
||||
auto packed = getPacked();
|
||||
unpack(packed.data(), packed.size());
|
||||
@@ -1786,7 +1865,7 @@ RevocationList::isSignedBy(const Certificate& issuer) const
|
||||
unsigned result {0};
|
||||
auto err = gnutls_x509_crl_verify(crl, &issuer.cert, 1, 0, &result);
|
||||
if (err < 0) {
|
||||
//std::cout << "Can't verify CRL: " << err << " " << result << " " << gnutls_strerror(err) << std::endl;
|
||||
//std::cout << "Unable to verify CRL: " << err << " " << result << " " << gnutls_strerror(err) << std::endl;
|
||||
return false;
|
||||
}
|
||||
return result == 0;
|
||||
|
||||
+763
-512
File diff suppressed because it is too large.
Load diff
@@ -208,7 +208,8 @@ DhtProxyClient::stop()
|
||||
std::lock_guard<std::mutex> l(resolverLock_);
|
||||
resolver = std::move(resolver_);
|
||||
}
|
||||
resolver->cancel();
|
||||
if (resolver)
|
||||
resolver->cancel();
|
||||
cancelAllListeners();
|
||||
if (infoState_)
|
||||
infoState_->cancel = true;
|
||||
|
||||
@@ -100,6 +100,47 @@ private:
|
||||
std::shared_ptr<Logger> m_logger;
|
||||
};
|
||||
|
||||
|
||||
std::string
|
||||
DhtProxyServer::ServerStats::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;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
DhtProxyServer::ServerStats::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;
|
||||
}
|
||||
|
||||
restinio::request_handling_status_t
|
||||
DhtProxyServer::serverError(restinio::request_t& request) {
|
||||
auto response = initHttpResponse(request.create_response(restinio::status_internal_server_error()));
|
||||
|
||||
+11
-1
@@ -188,6 +188,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));
|
||||
}
|
||||
@@ -627,7 +630,14 @@ DhtRunner::registerCertificate(const std::shared_ptr<crypto::Certificate>& 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));
|
||||
|
||||
+2
-2
@@ -881,7 +881,7 @@ Request::Request(asio::io_context& ctx, const std::string& url, const Json::Valu
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
if (!reader->parse(response.body.data(), response.body.data() + response.body.size(), &json, &err) and logger_)
|
||||
logger_->error("[http:request:{:d}] can't parse response to json: {:s}", id_, err);
|
||||
logger_->error("[http:request:{:d}] Unable to parse response to JSON: {:s}", id_, err);
|
||||
}
|
||||
if (jsoncb)
|
||||
jsoncb(std::move(json), response);
|
||||
@@ -903,7 +903,7 @@ Request::Request(asio::io_context& ctx, const std::string& url, OnJsonCb jsoncb,
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
if (!reader->parse(response.body.data(), response.body.data() + response.body.size(), &json, &err) and logger_)
|
||||
logger_->error("[http:request:{:d}] can't parse response to json: {:s}", id_, err);
|
||||
logger_->error("[http:request:{:d}] Unable to parse response to JSON: {:s}", id_, err);
|
||||
}
|
||||
if (jsoncb)
|
||||
jsoncb(std::move(json), response);
|
||||
|
||||
@@ -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,
|
||||
|
||||
+14
-14
@@ -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,7 +288,7 @@ 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);
|
||||
@@ -429,7 +429,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 +449,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 +523,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 +531,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 +550,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 +600,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 +609,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 +694,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,7 +744,7 @@ 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, {});
|
||||
}
|
||||
|
||||
+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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+147
-112
@@ -36,10 +36,18 @@ extern "C" {
|
||||
|
||||
namespace dht {
|
||||
|
||||
SecureDht::SecureDht(std::unique_ptr<DhtInterface> dht, SecureDht::Config conf, IdentityAnnouncedCb iacb, const std::shared_ptr<Logger>& l)
|
||||
: DhtInterface(l), dht_(std::move(dht)), key_(conf.id.first), certificate_(conf.id.second), enableCache_(conf.cert_cache_all)
|
||||
SecureDht::SecureDht(std::unique_ptr<DhtInterface> dht,
|
||||
SecureDht::Config conf,
|
||||
IdentityAnnouncedCb iacb,
|
||||
const std::shared_ptr<Logger>& l)
|
||||
: DhtInterface(l)
|
||||
, dht_(std::move(dht))
|
||||
, key_(conf.id.first)
|
||||
, certificate_(conf.id.second)
|
||||
, enableCache_(conf.cert_cache_all)
|
||||
{
|
||||
if (!dht_) return;
|
||||
if (!dht_)
|
||||
return;
|
||||
for (const auto& type : DEFAULT_TYPES)
|
||||
registerType(type);
|
||||
|
||||
@@ -53,30 +61,28 @@ 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)]{
|
||||
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);
|
||||
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);
|
||||
|
||||
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}, cb, time_point::max(), true);
|
||||
dht_->put(InfoHash::get(certLongId), Value {CERTIFICATE_TYPE, *certificate_, 1}, cb, time_point::max(), true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
SecureDht::~SecureDht(){
|
||||
SecureDht::~SecureDht()
|
||||
{
|
||||
dht_.reset();
|
||||
}
|
||||
|
||||
@@ -88,12 +94,13 @@ SecureDht::secureType(ValueType&& type)
|
||||
return v->checkSignature();
|
||||
return type.storePolicy(id, v, nid, a);
|
||||
};
|
||||
type.editPolicy = [this,type](InfoHash id, const Sp<Value>& o, Sp<Value>& n, const InfoHash& nid, const SockAddr& a) {
|
||||
type.editPolicy = [this,
|
||||
type](InfoHash id, const Sp<Value>& o, Sp<Value>& n, const InfoHash& nid, const SockAddr& a) {
|
||||
if (not o->isSigned())
|
||||
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()) {
|
||||
@@ -109,8 +116,7 @@ SecureDht::secureType(ValueType&& type)
|
||||
logger_->w("Edition forbidden: sequence number must be increasing.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (n->seq < o->seq)
|
||||
} else if (n->seq < o->seq)
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
@@ -228,12 +234,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;
|
||||
@@ -241,22 +248,26 @@ SecureDht::findCertificate(const InfoHash& node, const std::function<void(const
|
||||
}
|
||||
|
||||
auto found = std::make_shared<bool>(false);
|
||||
dht_->get(node, [cb,node,found,this](const std::vector<Sp<Value>>& vals) {
|
||||
for (const auto& v : vals) {
|
||||
if (auto cert = registerCertificate(node, v->data)) {
|
||||
*found = true;
|
||||
if (logger_)
|
||||
logger_->d(node, "Found certificate for %s", node.to_c_str());
|
||||
if (cb)
|
||||
cb(cert);
|
||||
return false;
|
||||
dht_->get(
|
||||
node,
|
||||
[cb, node, found, this](const std::vector<Sp<Value>>& vals) {
|
||||
for (const auto& v : vals) {
|
||||
if (auto cert = registerCertificate(node, v->data)) {
|
||||
*found = true;
|
||||
if (logger_)
|
||||
logger_->d(node, "Found certificate for %s", node.to_c_str());
|
||||
if (cb)
|
||||
cb(cert);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return !*found;
|
||||
}, [cb,found](bool) {
|
||||
if (!*found and cb)
|
||||
cb(nullptr);
|
||||
}, Value::TypeFilter(CERTIFICATE_TYPE));
|
||||
return !*found;
|
||||
},
|
||||
[cb, found](bool) {
|
||||
if (!*found and cb)
|
||||
cb(nullptr);
|
||||
},
|
||||
Value::TypeFilter(CERTIFICATE_TYPE));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -276,11 +287,13 @@ SecureDht::findPublicKey(const InfoHash& node, const std::function<void(const Sp
|
||||
if (*pk) {
|
||||
nodesPubKeys_[pk->getId()] = pk;
|
||||
nodesPubKeysLong_[pk->getLongId()] = pk;
|
||||
if (cb) cb(pk);
|
||||
if (cb)
|
||||
cb(pk);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (cb) cb(nullptr);
|
||||
if (cb)
|
||||
cb(nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -296,23 +309,40 @@ SecureDht::findCertificate(const PkId& node, const std::function<void(const Sp<c
|
||||
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) {
|
||||
if (auto cert = registerCertificate(node, v->data)) {
|
||||
*found = true;
|
||||
if (logger_)
|
||||
logger_->debug("Found certificate for {}", node.to_c_str());
|
||||
if (cb)
|
||||
cb(cert);
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
return !*found;
|
||||
}, [cb,found](bool) {
|
||||
if (!*found and cb)
|
||||
cb(nullptr);
|
||||
}, Value::TypeFilter(CERTIFICATE_TYPE));
|
||||
}
|
||||
|
||||
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) {
|
||||
if (auto cert = registerCertificate(node, v->data)) {
|
||||
*found = true;
|
||||
if (logger_)
|
||||
logger_->debug("Found certificate for {}", node.to_c_str());
|
||||
if (cb)
|
||||
cb(cert);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !*found;
|
||||
},
|
||||
[cb, found](bool) {
|
||||
if (!*found and cb)
|
||||
cb(nullptr);
|
||||
},
|
||||
Value::TypeFilter(CERTIFICATE_TYPE));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -332,11 +362,13 @@ SecureDht::findPublicKey(const PkId& node, const std::function<void(const Sp<cry
|
||||
if (*pk) {
|
||||
nodesPubKeys_[pk->getId()] = pk;
|
||||
nodesPubKeysLong_[pk->getLongId()] = pk;
|
||||
if (cb) cb(pk);
|
||||
if (cb)
|
||||
cb(pk);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (cb) cb(nullptr);
|
||||
if (cb)
|
||||
cb(nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -404,7 +436,6 @@ SecureDht::getCallbackFilter(const ValueCallback& cb, Value::Filter&& filter)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
GetCallback
|
||||
SecureDht::getCallbackFilter(const GetCallback& cb, Value::Filter&& filter)
|
||||
{
|
||||
@@ -435,7 +466,6 @@ SecureDht::listen(const InfoHash& id, ValueCallback cb, Value::Filter f, Where w
|
||||
return dht_->listen(id, getCallbackFilter(cb, std::forward<Value::Filter>(f)), {}, std::forward<Where>(w));
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
SecureDht::listen(const InfoHash& id, GetCallback cb, Value::Filter f, Where w)
|
||||
{
|
||||
@@ -445,7 +475,7 @@ SecureDht::listen(const InfoHash& id, GetCallback cb, Value::Filter f, Where w)
|
||||
void
|
||||
SecureDht::putSigned(const InfoHash& hash, Sp<Value> val, DoneCallback callback, bool permanent)
|
||||
{
|
||||
if (not key_ or not hash or not val) {
|
||||
if (not key_ or not hash or not val) {
|
||||
if (callback)
|
||||
callback(false, {});
|
||||
return;
|
||||
@@ -463,8 +493,9 @@ SecureDht::putSigned(const InfoHash& hash, Sp<Value> val, DoneCallback callback,
|
||||
}
|
||||
|
||||
// Check if data already exists on the dht
|
||||
get(hash,
|
||||
[val,this] (const std::vector<Sp<Value>>& vals) {
|
||||
get(
|
||||
hash,
|
||||
[val, this](const std::vector<Sp<Value>>& vals) {
|
||||
if (logger_)
|
||||
logger_->d("Found online previous value being announced.");
|
||||
for (const auto& v : vals) {
|
||||
@@ -479,73 +510,77 @@ SecureDht::putSigned(const InfoHash& hash, Sp<Value> val, DoneCallback callback,
|
||||
}
|
||||
return true;
|
||||
},
|
||||
[hash,val,this,callback,permanent] (bool /* ok */) {
|
||||
[hash, val, this, callback, permanent](bool /* ok */) {
|
||||
sign(*val);
|
||||
dht_->put(hash, val, callback, time_point::max(), permanent);
|
||||
},
|
||||
Value::IdFilter(val->id),
|
||||
Where().id(val->id)
|
||||
);
|
||||
Where().id(val->id));
|
||||
}
|
||||
|
||||
void
|
||||
SecureDht::putEncrypted(const InfoHash& hash, const InfoHash& to, Sp<Value> val, DoneCallback callback, bool permanent)
|
||||
{
|
||||
if (not key_) {
|
||||
if (not key_) {
|
||||
if (callback)
|
||||
callback(false, {});
|
||||
return;
|
||||
}
|
||||
findPublicKey(to, [this, hash, val = std::move(val), callback = std::move(callback), permanent](const Sp<crypto::PublicKey>& pk) {
|
||||
if(!pk || !*pk) {
|
||||
if (callback)
|
||||
callback(false, {});
|
||||
return;
|
||||
}
|
||||
if (logger_)
|
||||
logger_->w("Encrypting data for PK: %s", pk->getId().toString().c_str());
|
||||
try {
|
||||
dht_->put(hash, encrypt(*val, *pk), callback, time_point::max(), permanent);
|
||||
} catch (const std::exception& e) {
|
||||
if (logger_)
|
||||
logger_->e("Error putting encrypted data: %s", e.what());
|
||||
if (callback)
|
||||
callback(false, {});
|
||||
}
|
||||
});
|
||||
findPublicKey(to,
|
||||
[this, hash, val = std::move(val), callback = std::move(callback), permanent](
|
||||
const Sp<crypto::PublicKey>& pk) {
|
||||
if (!pk || !*pk) {
|
||||
if (callback)
|
||||
callback(false, {});
|
||||
return;
|
||||
}
|
||||
if (logger_)
|
||||
logger_->w("Encrypting data for PK: %s", pk->getId().toString().c_str());
|
||||
try {
|
||||
dht_->put(hash, encrypt(*val, *pk), callback, time_point::max(), permanent);
|
||||
} catch (const std::exception& e) {
|
||||
if (logger_)
|
||||
logger_->e("Error putting encrypted data: %s", e.what());
|
||||
if (callback)
|
||||
callback(false, {});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
SecureDht::putEncrypted(const InfoHash& hash, const PkId& to, Sp<Value> val, DoneCallback callback, bool permanent)
|
||||
{
|
||||
if (not key_) {
|
||||
if (not key_) {
|
||||
if (callback)
|
||||
callback(false, {});
|
||||
return;
|
||||
}
|
||||
findPublicKey(to, [this, hash, val = std::move(val), callback = std::move(callback), permanent](const Sp<crypto::PublicKey>& pk) {
|
||||
if(!pk || !*pk) {
|
||||
if (callback)
|
||||
callback(false, {});
|
||||
return;
|
||||
}
|
||||
if (logger_)
|
||||
logger_->warn("Encrypting data for PK: {}", pk->getLongId());
|
||||
try {
|
||||
dht_->put(hash, encrypt(*val, *pk), callback, time_point::max(), permanent);
|
||||
} catch (const std::exception& e) {
|
||||
if (logger_)
|
||||
logger_->e("Error putting encrypted data: %s", e.what());
|
||||
if (callback)
|
||||
callback(false, {});
|
||||
}
|
||||
});
|
||||
findPublicKey(to,
|
||||
[this, hash, val = std::move(val), callback = std::move(callback), permanent](
|
||||
const Sp<crypto::PublicKey>& pk) {
|
||||
if (!pk || !*pk) {
|
||||
if (callback)
|
||||
callback(false, {});
|
||||
return;
|
||||
}
|
||||
if (logger_)
|
||||
logger_->warn("Encrypting data for PK: {}", pk->getLongId());
|
||||
try {
|
||||
dht_->put(hash, encrypt(*val, *pk), callback, time_point::max(), permanent);
|
||||
} catch (const std::exception& e) {
|
||||
if (logger_)
|
||||
logger_->e("Error putting encrypted data: %s", e.what());
|
||||
if (callback)
|
||||
callback(false, {});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
SecureDht::putEncrypted(const InfoHash& hash, const crypto::PublicKey& pk, Sp<Value> val, DoneCallback callback, bool permanent)
|
||||
SecureDht::putEncrypted(
|
||||
const InfoHash& hash, const crypto::PublicKey& pk, Sp<Value> val, DoneCallback callback, bool permanent)
|
||||
{
|
||||
if (not key_) {
|
||||
if (not key_) {
|
||||
if (callback)
|
||||
callback(false, {});
|
||||
return;
|
||||
@@ -583,7 +618,7 @@ SecureDht::decrypt(const Value& v)
|
||||
auto decrypted = key_->decrypt(v.cypher);
|
||||
|
||||
Value ret {v.id};
|
||||
auto msg = msgpack::unpack((const char*)decrypted.data(), decrypted.size());
|
||||
auto msg = msgpack::unpack((const char*) decrypted.data(), decrypted.size());
|
||||
ret.msgpack_unpack_body(msg.get());
|
||||
|
||||
if (ret.recipient != getId())
|
||||
@@ -594,4 +629,4 @@ SecureDht::decrypt(const Value& v)
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace dht
|
||||
+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_; }
|
||||
|
||||
@@ -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"
|
||||
|
||||
+2
-2
@@ -73,7 +73,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());
|
||||
@@ -527,7 +527,7 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,6 +260,48 @@ void CryptoTester::testAesEncryptionWithMultipleKeySizes() {
|
||||
}
|
||||
}
|
||||
|
||||
void 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
|
||||
}
|
||||
|
||||
void
|
||||
CryptoTester::tearDown() {
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ class CryptoTester : public CppUnit::TestFixture {
|
||||
CPPUNIT_TEST(testOcsp);
|
||||
CPPUNIT_TEST(testAesEncryption);
|
||||
CPPUNIT_TEST(testAesEncryptionWithMultipleKeySizes);
|
||||
CPPUNIT_TEST(testOaep);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@@ -71,6 +72,8 @@ class CryptoTester : public CppUnit::TestFixture {
|
||||
*/
|
||||
void testAesEncryption();
|
||||
void testAesEncryptionWithMultipleKeySizes();
|
||||
|
||||
void testOaep();
|
||||
};
|
||||
|
||||
} // 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);
|
||||
|
||||
|
||||
+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.0",
|
||||
"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