Compare commits

..
117 Commits
Author SHA1 Message Date
Adrien Beraud cf73feb056 dht: use max date for permanent puts 2025-11-28 13:41:29 -05:00
Adrien Béraud e028c1be8c update version to 3.6.0 2025-11-26 14:03:56 -05:00
Adrien Béraud 3e8d9835d4 utils: various fixes to build with c++23 2025-11-26 14:03:56 -05:00
Adrien Béraud 18df23bd50 build/cmake: support c++ other than 17 2025-11-26 14:03:56 -05:00
Adrien Béraud 6b8c3ea581 base64: use simdutf 2025-11-26 14:01:45 -05:00
dependabot[bot] 7645f284e4 build(deps): bump pypa/cibuildwheel from 3.2.1 to 3.3.0
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 3.2.1 to 3.3.0.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v3.2.1...v3.3.0)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-version: 3.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-25 10:54:55 -05:00
dependabot[bot] 2cec478dfc build(deps): bump actions/checkout from 5 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-25 10:54:00 -05:00
Adrien Béraud 54f07e449c pkgconfig: link with fmt 2025-11-19 11:20:40 -05:00
Adrien Béraud 09308321f8 network_utils: cleanup 2025-11-19 11:20:02 -05:00
Adrien Béraud e4b333b672 add clang-format 2025-11-19 11:20:02 -05:00
Adrien Béraud 69f382449d add .vscode settings 2025-11-19 11:19:55 -05:00
Adrien Beraud 47d2f41930 proxy client: check for null resolver 2025-11-17 12:56:36 -05:00
ovari d6b124eec0 Update BUILD.md 2025-11-16 17:35:19 -05:00
ovari 0330ee6683 Update BUILD.md 2025-11-16 17:35:19 -05:00
Jean-Charles de Longueville 1b854b8a09 Update BUILD.md from reviewer's comments 2025-11-16 16:50:04 -05:00
Jean-Charles de Longueville 4f6c561540 Update BUILD.md: typo 2025-11-16 16:50:04 -05:00
Jean-Charles de Longueville 79ffe663f6 Update BUILD.md: add debian 13 trixie instructions 2025-11-16 16:50:04 -05:00
dependabot[bot] a14a97e3a2 build(deps): bump actions/download-artifact from 5 to 6
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-28 10:20:24 -04:00
dependabot[bot] bddc34d47b build(deps): bump actions/upload-artifact from 4 to 5
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-28 10:19:46 -04:00
kestrel de6ea1d632 Add support for io_uring for Autotools builds.
* configure.ac (liburing): Add --with-liburing option, tests for liburing.
* src/Makefile.am (libopendht_la_CPPFLAGS, libopendht_la_LIBS): Add relevant compiler and linker flags.
2025-10-15 15:13:19 -04:00
kestrel 1d2fd80fad Use pkg-config to test for llhttp for Autotools builds.
* configure.ac (llhttp_lib): Replaces existing tests with PKG_CHECK_MODULES.
* src/Makefile.am (libopendht_la_CPPFLAGS, libopendht_la_LIBS): Add relevant compiler and linker flags.
2025-10-15 15:13:19 -04:00
SASANO Takayoshi f7df3413e1 CMakeLists.txt: revise libreadline check on OpenBSD 2025-10-15 15:12:17 -04:00
SASANO Takayoshi 4cb67002e3 CMakeLists.txt: fix -lreadline not found error 2025-10-15 15:12:17 -04:00
SASANO Takayoshi 197a9a6bcd CMakeLists.txt: fix 'install TARGETS RUNTIME_DEPENDENCY_SET is not supported' 2025-10-15 15:12:17 -04:00
SASANO Takayoshi 8641de139d CMakeLists.txt: avoid libreadline check on OpenBSD 2025-10-15 15:12:17 -04:00
dependabot[bot] adcb61bbf9 build(deps): bump pypa/cibuildwheel from 3.2.0 to 3.2.1
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 3.2.0 to 3.2.1.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v3.2.0...v3.2.1)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-version: 3.2.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-15 15:11:09 -04:00
dependabot[bot] 70709c9a68 build(deps): bump github/codeql-action from 3 to 4
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-15 15:10:42 -04:00
kestrel bf930de9e4 configure.ac: Fix llhttplib substituion.
* configure.ac (@http_lib@): Rename to llhttp_lib, replace the substituted value for a pkg-config module name.
2025-10-13 14:35:28 -04:00
Adrien Beraud bb4e716fc7 README: add pypi badge 2025-10-10 13:59:44 -04:00
Adrien Beraud 54fd6cf64f securedht: avoid calling identity announced callback multiple times 2025-10-10 13:59:44 -04:00
dependabot[bot] d905a02b32 build(deps): bump pypa/cibuildwheel from 3.1.4 to 3.2.0
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 3.1.4 to 3.2.0.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v3.1.4...v3.2.0)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-version: 3.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-10 11:11:56 -04:00
Adrien Béraud d12f9271b8 pht: use const reference for IndexEntry::TYPE
Prevents static initialization order issue
2025-09-22 14:41:22 -04:00
Adrien Béraud 3639d651a2 docker: add alpine edge container 2025-09-22 14:41:22 -04:00
Adrien Béraud 07824391a2 update version to 3.5.5 2025-09-22 14:41:22 -04:00
Adrien Béraud c855f495d0 ci/release: remove reference to removed artifact 2025-09-19 00:59:28 -04:00
Adrien Béraud cc5c148fc1 python: repair python installation with cmake 2025-09-19 00:41:18 -04:00
Adrien Béraud 949751f632 crypto: disable Oaep test for win32 2025-09-18 23:39:38 -04:00
Adrien Béraud 6dfc4e4ba1 ci: don't build with documentation on Windows/MSVC 2025-09-18 23:19:35 -04:00
Adrien Béraud db8d62e17b ci: don't build with documentation on Windows/MSVC 2025-09-18 23:02:22 -04:00
Adrien Béraud 7226775987 crypto: disable Oaep test for win32 2025-09-18 22:26:50 -04:00
Adrien Béraud 9c264e1cad python: add debug information for python install 2025-09-18 22:19:41 -04:00
Adrien Béraud deee57b6dd ci/github: call ctest directly 2025-09-14 18:27:19 -04:00
Adrien Béraud 95f0b27eae cmake: build tests as separate executables 2025-09-14 17:55:59 -04:00
ovari b6f98a8488 Update crypto.cpp 2025-09-14 13:38:35 -04:00
ovari 73acc27a11 Update CheckAtomic.cmake 2025-09-14 13:38:35 -04:00
ovari d46e302b3f Update network_engine.h 2025-09-14 13:38:35 -04:00
ovari 6f57e03b48 Update network_engine.h 2025-09-14 13:38:35 -04:00
ovari a5e39768e4 Update securedht.cpp 2025-09-14 13:38:35 -04:00
ovari 6f6fdba8dd Update value.cpp 2025-09-14 13:38:35 -04:00
ovari e5ef0a8900 Update dhtrunner.h 2025-09-14 13:38:35 -04:00
ovari b6f21933ab Update securedht.h 2025-09-14 13:38:35 -04:00
ovari e4b8b0259d Update http.cpp 2025-09-14 13:38:35 -04:00
ovari 8134d6201a Update network_engine.cpp 2025-09-14 13:38:35 -04:00
ovari db4cd7dcc1 Update peer_discovery.cpp 2025-09-14 13:38:35 -04:00
ovari 9d6107c01c Update storage.h 2025-09-14 13:38:35 -04:00
ovari de46d55b13 Update crypto.cpp
“ !” → “.”
bits → bit
can’t → unable to
wrong → incorrect
2025-09-14 13:38:35 -04:00
Adrien Béraud 75afd9d9dc ci/msvc: build without documentation 2025-09-14 13:36:45 -04:00
Adrien Beraud 99c56d13bb crypto: fix build on GnuTLS 3.8.3 and bellow 2025-09-08 16:38:25 -04:00
Adrien Béraud 95fa29d2ec securedht: add CertificateStoreQuery by PkId 2025-09-08 00:14:44 -04:00
Adrien Béraud f3d9d8414d crypto: add test for RSA OAEP 2025-09-07 23:45:40 -04:00
Adrien Béraud 0c9d715d6d crypto: allow to generate RSA OAEP private keys 2025-09-07 23:44:47 -04:00
Adrien Béraud cfff247e06 crypto: support RSA OAEP encryption and decryption 2025-09-07 22:15:00 -04:00
Adrien Béraud 04edc70fee dhtrunner: update documentation for join() 2025-09-06 17:14:10 -04:00
Adrien Béraud fdc4bdee68 ci: install cmake on Debian 12 2025-09-06 16:35:34 -04:00
Adrien Béraud 69bf7afe31 BUILD.md: add pkg-config and support for Debian 12 2025-09-06 16:33:48 -04:00
Adrien Béraud 7b3ee44175 ci: add proxy dependencies for Debian 12 2025-09-06 16:33:21 -04:00
Adrien Béraud 122c30cce8 ci: add Debian 12 2025-09-06 16:08:03 -04:00
Adrien Béraud 957e05319f BUILD.md: cleanup 2025-09-05 00:35:46 -04:00
Adrien Béraud 58f83ab3c6 build/cmake: python cleanup 2025-09-05 00:35:28 -04:00
Adrien Béraud 6151ee8c0b pyproject: remove unneeded install command 2025-09-04 23:23:27 -04:00
Adrien Béraud c08b6a7f5d add support for version 2025-09-04 23:22:34 -04:00
Adrien Béraud 0139e7a2d4 update README.md 2025-09-04 23:22:10 -04:00
Adrien Béraud 416de4b4f8 update version to 3.5.4 2025-09-02 23:52:56 -04:00
Adrien Béraud b45f32e8f8 README: update for new Python API 2025-09-02 23:51:36 -04:00
Adrien Béraud cda5b0afa2 python: update pingpong example for asyncio 2025-09-02 23:48:43 -04:00
Adrien Béraud f66a57b033 python: cleanup asynio layer 2025-09-02 23:48:34 -04:00
Adrien Béraud 72a688cd14 ci/release-package: remove deprecated python wheels build 2025-09-02 23:07:48 -04:00
Adrien Béraud e882efe89e build/cmake: use VCPKG_INSTALLED_DIR for python 2025-09-02 21:49:30 -04:00
Adrien Beraud 7b49ed873c build/python: install build for ci job 2025-09-02 19:49:02 -04:00
Adrien Béraud 3fec6e7636 pht: don't export IndexEntry::ValueType 2025-09-02 09:22:40 -04:00
Adrien Béraud 67ac7a4d86 python: update pingpong example for asyncio 2025-09-02 01:57:09 -04:00
Adrien Béraud 615534fa0f python: add initial asyncio implementation 2025-09-02 01:45:29 -04:00
Adrien Béraud 4250336951 .gitignore: add cmake files 2025-09-02 01:45:29 -04:00
Adrien Béraud 932cacbda3 python: use single to argument for putEncrypted 2025-09-02 01:40:28 -04:00
Adrien Béraud 3b254663ca build/python: install delvewheel from ci job 2025-09-02 01:37:59 -04:00
Adrien Béraud 18f74693e9 ci/github: merge windows python wheels 2025-09-01 21:06:11 -04:00
Adrien Béraud e0cc973aa4 build/cmake: enable python in full vcpkg preset 2025-09-01 20:17:42 -04:00
Adrien Béraud c866d46798 build/cmake: filter more windows dlls 2025-09-01 20:17:42 -04:00
Adrien Béraud c762e4e43f build/cmake: remove python from all taregts 2025-09-01 20:17:42 -04:00
Adrien Béraud 3eed83c22d ci/github: build windows python wheel 2025-09-01 20:17:42 -04:00
Adrien Béraud 234f47a018 python/setup.py: use appropriate flags for windows build 2025-09-01 20:17:42 -04:00
Adrien Béraud 615463e103 ci/python: add support for windows/msvc wheel build 2025-09-01 20:17:42 -04:00
Adrien Béraud d50585188d utils: add missing header 2025-09-01 19:42:41 -04:00
Adrien Béraud dd247d15ba python: call gnutls_global_init/gnutls_global_deinit on module load/unload 2025-09-01 19:40:20 -04:00
Adrien Béraud 4a34045c0f python/setup.py: add appropriate build definitions 2025-09-01 19:33:18 -04:00
Adrien Béraud edfdb39970 vcpkg: update description 2025-09-01 19:27:59 -04:00
Adrien Béraud 89551496c7 python: update readme 2025-08-31 00:21:46 -04:00
Adrien Beraud becb878f5f python: add bindings for sign, checkSignature 2025-08-31 00:04:00 -04:00
Adrien Beraud a94f99dcd2 python: add annotations and checks 2025-08-30 23:52:40 -04:00
Adrien Beraud 8de9584b72 dhtrunner: mark getId() deprecated 2025-08-30 22:20:32 -04:00
Adrien Beraud 303a3c2c5a python: unify putEncrypted 2025-08-30 22:18:49 -04:00
Adrien Beraud e6dcc3226d python: cleanup 2025-08-30 19:54:33 -04:00
Adrien Beraud ab71e5a59e python: add support for putEncrypted with PkId 2025-08-30 19:30:20 -04:00
Adrien Beraud bf9561c4af python: add binding for getLongId, getPublicKey 2025-08-30 19:27:57 -04:00
Adrien Beraud c139fc9791 python: add binding for PkId 2025-08-30 19:13:59 -04:00
dependabot[bot] 31ddf3cc27 build(deps): bump actions/download-artifact from 4 to 5
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-25 10:50:51 -04:00
Adrien Béraud 1bbdc3e673 add BUILD.md 2025-08-24 20:05:55 -04:00
Adrien Béraud 60ecb95cf6 build/python: copy README.md to build dir 2025-08-24 16:01:29 -04:00
Adrien Béraud 075ea59244 build/cmake: fix including jsoncpp on linux 2025-08-24 15:52:39 -04:00
Adrien Béraud 2f98f1f987 ci/pipy: build for Python 13 2025-08-24 11:22:49 -04:00
Adrien Béraud 2b22fe9055 setup.py: update metadata 2025-08-24 11:22:32 -04:00
Adrien BéraudandCopilot Autofix powered by AI e144460869 Potential fix for code scanning alert no. 17: Workflow does not contain permissions
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-08-24 01:47:50 -04:00
Adrien Béraud 6b54c42fdb build python with appropriate flags for public dependencies 2025-08-24 01:47:50 -04:00
Adrien Béraud 57587b6cd8 build/cmake: improve msgpack lookup logic 2025-08-24 01:47:50 -04:00
Adrien Béraud 0fd642f92d ci: add python-wheels.yml 2025-08-24 01:47:50 -04:00
Adrien Béraud 91310c83a7 setup.py.in: remove unneeded cythonize 2025-08-24 01:47:50 -04:00
Adrien Béraud daf2f8edaf value: add OPENDHT_PUBLIC to default store policy 2025-08-24 01:25:47 -04:00
55 changed files with 3228 additions and 1459 deletions

No files matched your search

+99
View File
@@ -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
+74 -5
View File
@@ -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@v5
- 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@v5
- 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@v5
- 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@v5
- uses: actions/checkout@v6
- name: Install Homebrew Dependencies
run: brew install msgpack-cxx asio gnutls nettle readline fmt jsoncpp argon2 openssl cppunit
+1 -1
View File
@@ -6,7 +6,7 @@ jobs:
name: Clang Static Analysis
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: deps
run: |
sudo apt update && \
+3 -3
View File
@@ -27,15 +27,15 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v5
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}}"
+108
View File
@@ -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 }}
+17 -54
View File
@@ -25,7 +25,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Log in to the Container registry
uses: docker/login-action@v3
@@ -58,7 +58,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Log in to the Container registry
uses: docker/login-action@v3
@@ -92,7 +92,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Log in to the Container registry
uses: docker/login-action@v3
@@ -125,7 +125,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Log in to the Container registry
uses: docker/login-action@v3
@@ -159,7 +159,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Log in to the Container registry
uses: docker/login-action@v3
@@ -193,7 +193,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Log in to the Container registry
uses: docker/login-action@v3
@@ -227,7 +227,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Log in to the Container registry
uses: docker/login-action@v3
@@ -266,7 +266,7 @@ jobs:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
submodules: recursive
@@ -315,6 +315,7 @@ jobs:
-DOPENDHT_C=On `
-DOPENDHT_TOOLS=On `
-DOPENDHT_PYTHON=Off `
-DOPENDHT_DOCUMENTATION=OFF `
-DOPENDHT_PEER_DISCOVERY=On `
-DOPENDHT_PROXY_SERVER=On `
-DOPENDHT_PROXY_CLIENT=On `
@@ -349,7 +350,7 @@ 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
@@ -369,7 +370,7 @@ jobs:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
submodules: recursive
@@ -417,6 +418,7 @@ jobs:
-DOPENDHT_PROXY_SERVER=ON `
-DOPENDHT_C=ON `
-DOPENDHT_TOOLS=ON `
-DOPENDHT_DOCUMENTATION=OFF `
-DOPENDHT_PYTHON=OFF `
-DOPENDHT_PEER_DISCOVERY=ON `
-DOPENDHT_HTTP=ON `
@@ -428,80 +430,41 @@ jobs:
cmake --build build_static --config Release --parallel $([System.Environment]::ProcessorCount)
- name: Upload Static dhtnode Artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: opendht-windows-dhtnode
path: |
build_static/**/*dhtnode.exe
retention-days: 7
build-python-wheels:
name: Build Python Wheels
runs-on: ubuntu-latest
container: ghcr.io/savoirfairelinux/opendht/opendht-deps:latest
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: cmake (for Python wheel)
run: |
mkdir build_python && cd build_python && \
cmake .. -DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=On \
-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
with:
name: opendht-wheels-linux
path: build_python/python/dist/*.whl
retention-days: 7
create-github-release:
name: Create GitHub Release
runs-on: ubuntu-latest
needs: [build-windows-msi, build-python-wheels, build-windows-dhtnode]
needs: [build-windows-msi, build-windows-dhtnode]
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Download Windows MSI artifact
uses: actions/download-artifact@v5
uses: actions/download-artifact@v6
with:
name: opendht-windows-msi
path: downloaded_artifacts/windows_msi
- name: Download Static dhtnode artifact
uses: actions/download-artifact@v5
uses: actions/download-artifact@v6
with:
name: opendht-windows-dhtnode
path: downloaded_artifacts/static_dhtnode
- name: Download Linux Wheels artifact
uses: actions/download-artifact@v5
with:
name: opendht-wheels-linux
path: downloaded_artifacts/linux_wheels
- name: Create Release
id: create_release
uses: softprops/action-gh-release@v2
with:
draft: true
files: |
downloaded_artifacts/linux_wheels/*.whl
downloaded_artifacts/windows_msi/*.msi
downloaded_artifacts/static_dhtnode/**/*dhtnode.exe
+11 -8
View File
@@ -27,7 +27,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Set up MSVC
uses: microsoft/setup-msbuild@v2
@@ -75,7 +75,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Set up MSVC
uses: microsoft/setup-msbuild@v2
@@ -124,7 +124,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Set up MSVC
uses: microsoft/setup-msbuild@v2
@@ -158,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 }}
@@ -177,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: |
@@ -197,7 +198,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Set up MSVC
uses: microsoft/setup-msbuild@v2
@@ -232,6 +233,7 @@ jobs:
-DOPENDHT_C=ON `
-DOPENDHT_TOOLS=ON `
-DOPENDHT_PYTHON=OFF `
-DOPENDHT_DOCUMENTATION=OFF `
-DOPENDHT_PEER_DISCOVERY=ON `
-DOPENDHT_HTTP=ON `
-A ${{ matrix.arch }}
@@ -248,7 +250,7 @@ jobs:
ctest --build-config ${{ matrix.build_type }} --output-on-failure --parallel 4
- name: Upload static build artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: opendht-windows-static-${{ matrix.arch }}-${{ matrix.build_type }}
path: |
@@ -267,7 +269,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Setup MinGW
uses: msys2/setup-msys2@v2
@@ -354,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
@@ -366,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
View File
@@ -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
+10
View File
@@ -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",
}
+124
View File
@@ -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 .
```
+151 -51
View File
@@ -1,16 +1,16 @@
cmake_minimum_required (VERSION 3.21..3.31)
if(POLICY CMP0091)
cmake_policy(SET CMP0091 NEW)
cmake_policy(SET CMP0091 NEW)
endif()
if(POLICY CMP0135)
cmake_policy(SET CMP0135 NEW)
cmake_policy(SET CMP0135 NEW)
endif()
if(NOT CMAKE_VERSION VERSION_LESS 3.30 AND NOT POLICY CMP0167)
cmake_policy(SET CMP0167 OLD)
if(POLICY CMP0167)
cmake_policy(SET CMP0167 NEW)
endif()
set (opendht_VERSION_MAJOR 3)
set (opendht_VERSION_MINOR 5.1)
set (opendht_VERSION_MINOR 6.0)
set (opendht_VERSION ${opendht_VERSION_MAJOR}.${opendht_VERSION_MINOR})
set (PACKAGE_VERSION ${opendht_VERSION})
set (VERSION "${opendht_VERSION}")
@@ -59,7 +59,9 @@ if (DOXYGEN_FOUND)
option (OPENDHT_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" ${DOXYGEN_FOUND})
endif ()
# Build flags
set (CMAKE_CXX_STANDARD 17)
if(NOT DEFINED CMAKE_CXX_STANDARD)
set (CMAKE_CXX_STANDARD 17)
endif()
set (CMAKE_CXX_STANDARD_REQUIRED on)
if(MSVC AND NOT BUILD_SHARED_LIBS AND NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
@@ -123,26 +125,54 @@ if (NOT MSVC)
# jsoncpp
pkg_search_module (Jsoncpp IMPORTED_TARGET jsoncpp)
if(Jsoncpp_FOUND)
if(APPLE)
set(JSONCPP_LIBRARIES PkgConfig::Jsoncpp)
else()
set(JSONCPP_LIBRARIES ${Jsoncpp_LIBRARIES})
endif()
set(JSONCPP_LIBRARIES PkgConfig::Jsoncpp)
set(JSONCPP_INCLUDE_DIRS ${Jsoncpp_INCLUDE_DIRS})
set(JSONCPP_FOUND ${Jsoncpp_FOUND})
include_directories(${JSONCPP_INCLUDE_DIRS})
pkg_search_module (simdutf IMPORTED_TARGET simdutf)
if (NOT simdutf_FOUND)
message(STATUS "simdutf not found with pkgconfig")
if (OPENDHT_DOWNLOAD_DEPS)
message(STATUS "Fallback mode specified, downloading simdutf")
set(SIMDUTF_TESTS OFF CACHE INTERNAL "Disable tests for simdutf downloader")
set(SIMDUTF_TOOLS OFF CACHE INTERNAL "Disable tools for simdutf downloader")
set(SIMDUTF_CXX_STANDARD 17 CACHE INTERNAL "Set C++ standard for simdutf downloader")
FetchContent_Declare(simdutf
GIT_REPOSITORY "https://github.com/simdutf/simdutf.git"
GIT_TAG "v7.5.0"
)
FetchContent_MakeAvailable(simdutf)
set(SIMDUTF_LIBRARIES simdutf::simdutf)
else()
message(SEND_ERROR "simdutf not found and the fallback downloader is not set! Install simdutf or use OPENDHT_DOWNLOAD_DEPS option")
endif()
else()
set(SIMDUTF_LIBRARIES PkgConfig::simdutf)
set(simdutf_lib ", ${simdutf_MODULE_NAME}")
endif()
endif()
# libfmt
pkg_search_module (fmt REQUIRED IMPORTED_TARGET fmt)
# libreadline
if (OPENDHT_TOOLS AND NOT APPLE)
pkg_search_module(readline REQUIRED IMPORTED_TARGET readline)
message(STATUS "libreadline: version ${readline_VERSION}")
set(READLINE_VERSION ${readline_VERSION})
if(READLINE_VERSION VERSION_LESS 6)
message(SEND_ERROR "libreadline: required version 6.0 or later")
if (OPENDHT_TOOLS AND NOT APPLE)
if("${CMAKE_SYSTEM_NAME}" MATCHES "OpenBSD")
# use OpenBSD-readline (not GNU)
find_library(readline NAMES readline PATHS ${CMAKE_LIBRARY_PATH})
if("${readline}" MATCHES "readline-NOTFOUND")
message(SEND_ERROR "libreadline: not found")
else()
set(READLINE_LIBRARIES readline)
endif()
else()
pkg_search_module(readline REQUIRED IMPORTED_TARGET readline)
message(STATUS "libreadline: version ${readline_VERSION}")
set(READLINE_VERSION ${readline_VERSION})
if(READLINE_VERSION VERSION_LESS 6)
message(SEND_ERROR "libreadline: required version 6.0 or later")
endif()
set(READLINE_LIBRARIES ${readline_LIBRARIES})
link_directories(${readline_LIBRARY_DIRS})
endif()
set(READLINE_LIBRARIES ${readline_LIBRARIES})
endif ()
# ASIO
if (OPENDHT_HTTP OR OPENDHT_PEER_DISCOVERY)
@@ -212,11 +242,11 @@ if (NOT MSVC)
# msgpack
check_include_file_cxx(msgpack.hpp HAVE_MSGPACKCXX)
if (NOT HAVE_MSGPACKCXX)
find_package(msgpack QUIET CONFIG NAMES msgpack msgpackc-cxx)
find_package(msgpack CONFIG NAMES msgpack-cxx)
if (NOT msgpack_FOUND)
find_package(msgpack QUIET CONFIG NAMES msgpack-cxx)
find_package(msgpack CONFIG NAMES msgpack msgpackc-cxx)
if (msgpack_FOUND)
set(MSGPACK_TARGET "msgpack-cxx")
set(MSGPACK_TARGET "msgpackc-cxx")
else()
if(OPENDHT_DOWNLOAD_DEPS)
# Workaround for msgpack-cxx
@@ -227,14 +257,14 @@ if (NOT MSVC)
)
set(MSGPACK_USE_BOOST OFF CACHE INTERNAL "Workaround for Boost")
FetchContent_MakeAvailable(msgpack-c)
find_package(msgpack-c CONFIG REQUIRED)
include_directories(${MSGPACK_INCLUDEDIR} ${msgpack-c_SOURCE_DIR}/include)
find_package(msgpack-cxx CONFIG REQUIRED)
set(MSGPACK_TARGET msgpack-cxx)
else()
message(SEND_ERROR "msgpack not found and the fallback downloader is not set! Please install msgpack or use OPENDHT_DOWNLOAD_DEPS option")
endif()
endif ()
else()
set(MSGPACK_TARGET "msgpackc-cxx")
set(MSGPACK_TARGET "msgpack-cxx")
endif()
endif()
# llhttp
@@ -293,10 +323,7 @@ else () # MSVC
include_directories(src/compat/msvc)
include_directories(src/compat/win32)
find_package(argon2 CONFIG QUIET)
if (NOT argon2_FOUND)
find_package(unofficial-argon2 CONFIG REQUIRED)
endif()
find_package(unofficial-argon2 CONFIG REQUIRED)
include_directories(${ARGON2_INCLUDEDIR})
set(argon2_lib ", libargon2")
@@ -310,6 +337,9 @@ else () # MSVC
find_package(jsoncpp CONFIG REQUIRED)
set(JSONCPP_FOUND TRUE)
include_directories(${JSONCPP_INCLUDE_DIRS})
find_package(simdutf CONFIG REQUIRED)
include_directories(${SIMDUTF_INCLUDE_DIRS})
find_package(fmt CONFIG REQUIRED)
include_directories(${FMT_INCLUDEDIR})
@@ -560,13 +590,16 @@ if (MSVC)
tasn1_lib
PUBLIC
${GNUTLS_LIBRARIES}
msgpack-cxx
JsonCpp::JsonCpp
fmt::fmt
simdutf::simdutf
wsock32
ws2_32
)
if (OPENDHT_TOOLS)
target_link_libraries(opendht PUBLIC unofficial::readline-win32::readline)
endif()
target_link_libraries(opendht PUBLIC msgpack-cxx JsonCpp::JsonCpp fmt::fmt)
if (OPENDHT_HTTP)
target_link_libraries(opendht
PUBLIC restinio::restinio
@@ -587,6 +620,7 @@ else()
PRIVATE
PkgConfig::argon2
PkgConfig::Nettle
${SIMDUTF_LIBRARIES}
PUBLIC
${CMAKE_THREAD_LIBS_INIT}
PkgConfig::GnuTLS
@@ -661,12 +695,21 @@ if (BUILD_SHARED_LIBS)
target_compile_definitions(opendht PUBLIC opendht_EXPORTS)
endif ()
if(("${CMAKE_SYSTEM_NAME}" MATCHES "OpenBSD") OR
("${CMAKE_SYSTEM_NAME}" MATCHES "NetBSD") OR
("${CMAKE_SYSTEM_NAME}" MATCHES "DragonFly"))
install (TARGETS opendht
DESTINATION ${CMAKE_INSTALL_LIBDIR}
EXPORT opendht
)
else()
install (TARGETS opendht
DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME_DEPENDENCY_SET opendht_rdeps
EXPORT opendht
)
endif()
install (TARGETS opendht
DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME_DEPENDENCY_SET opendht_rdeps
EXPORT opendht
)
if (WIN32)
install(RUNTIME_DEPENDENCY_SET opendht_rdeps
PRE_EXCLUDE_REGEXES
@@ -697,6 +740,8 @@ if (WIN32)
"msvcp.*\\.dll"
"wpax.*\\.dll"
"azure.*\\.dll"
"lsasrv\\.dll"
"samsrv\\.dll"
)
endif()
@@ -733,6 +778,53 @@ endif ()
add_subdirectory(doc)
if (OPENDHT_PYTHON)
if (JSONCPP_FOUND)
list (APPEND OPENDHT_PYTHON_INCLUDE_DIRS_LIST ${JSONCPP_INCLUDE_DIRS})
#if (OPENDHT_USE_PKGCONFIG)
list (APPEND OPENDHT_PYTHON_LIBRARY_DIRS_LIST ${Jsoncpp_LIBRARY_DIRS})
#endif()
endif()
if (NOT HAVE_MSGPACKCXX)
get_target_property(MSGPACK_INCLUDEDIR msgpack-cxx INTERFACE_INCLUDE_DIRECTORIES)
get_target_property(MSGPACK_LIBRARY_DIRS msgpack-cxx INTERFACE_LIBRARY_DIRS)
list(APPEND OPENDHT_PYTHON_INCLUDE_DIRS_LIST ${MSGPACK_INCLUDEDIR})
if (MSGPACK_LIBRARY_DIRS)
list(APPEND OPENDHT_PYTHON_LIBRARY_DIRS_LIST ${MSGPACK_LIBRARY_DIRS})
endif()
endif()
# Add the directory where the OpenDHT library is built
if(WIN32)
# Append actual configuration-specific build directories instead of an unevaluated $<CONFIG> generator expression
if(CMAKE_CONFIGURATION_TYPES)
foreach(cfg ${CMAKE_CONFIGURATION_TYPES})
list(APPEND OPENDHT_PYTHON_LIBRARY_DIRS_LIST ${CMAKE_CURRENT_BINARY_DIR}/${cfg})
endforeach()
elseif(CMAKE_BUILD_TYPE)
list(APPEND OPENDHT_PYTHON_LIBRARY_DIRS_LIST ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE})
else()
list(APPEND OPENDHT_PYTHON_LIBRARY_DIRS_LIST ${CMAKE_CURRENT_BINARY_DIR})
endif()
# if using MSVC with vcpkg, add the vcpkg installed directory
if (MSVC AND VCPKG_TARGET_TRIPLET)
if (VCPKG_INSTALLED_DIR)
list(APPEND OPENDHT_PYTHON_LIBRARY_DIRS_LIST "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib")
else()
list(APPEND OPENDHT_PYTHON_LIBRARY_DIRS_LIST "${CMAKE_CURRENT_BINARY_DIR}/vcpkg_installed/${VCPKG_TARGET_TRIPLET}/lib")
endif()
endif()
endif()
if (OPENDHT_PYTHON_INCLUDE_DIRS_LIST)
list(REMOVE_DUPLICATES OPENDHT_PYTHON_INCLUDE_DIRS_LIST)
string(JOIN "\", \"" OPENDHT_PYTHON_INCLUDE_DIRS ${OPENDHT_PYTHON_INCLUDE_DIRS_LIST})
set(OPENDHT_PYTHON_INCLUDE_DIRS ", \"${OPENDHT_PYTHON_INCLUDE_DIRS}\"")
endif()
if (OPENDHT_PYTHON_LIBRARY_DIRS_LIST)
list(REMOVE_DUPLICATES OPENDHT_PYTHON_LIBRARY_DIRS_LIST)
string(JOIN "\", \"" OPENDHT_PYTHON_LIBRARY_DIRS ${OPENDHT_PYTHON_LIBRARY_DIRS_LIST})
set(OPENDHT_PYTHON_LIBRARY_DIRS ", \"${OPENDHT_PYTHON_LIBRARY_DIRS}\"")
endif()
add_subdirectory(python)
endif ()
@@ -796,23 +888,31 @@ if (BUILD_TESTING)
)
endif()
endif()
add_executable(opendht_unit_tests
tests/tests_runner.cpp
${test_FILES}
)
target_link_libraries(opendht_unit_tests PRIVATE
opendht
${CMAKE_THREAD_LIBS_INIT}
${cppunit_LIBRARIES}
)
if (OPENDHT_PROXY_OPENSSL)
if (WIN32 AND MINGW) # MSYS2
target_link_libraries(opendht_unit_tests PRIVATE ${OPENSSL_LIBRARIES})
elseif(NOT MSVC) # Clang on UNIX-pkgconfig
target_link_libraries(opendht_unit_tests PRIVATE PkgConfig::OPENSSL)
# Create one test executable per test implementation (.cpp) so they can be run independently.
# We keep using the common tests_runner.cpp (CppUnit registry based) and compile it with each test file.
foreach(test_src ${test_FILES})
if (test_src MATCHES ".*\\.cpp$")
get_filename_component(test_name ${test_src} NAME_WE) # e.g. infohashtester
set(target_name ${test_name})
add_executable(${target_name}
tests/tests_runner.cpp
${test_src}
)
target_link_libraries(${target_name} PRIVATE
opendht
${CMAKE_THREAD_LIBS_INIT}
${cppunit_LIBRARIES}
)
if (OPENDHT_PROXY_OPENSSL)
if (WIN32 AND MINGW) # MSYS2
target_link_libraries(${target_name} PRIVATE ${OPENSSL_LIBRARIES})
elseif(NOT MSVC) # Clang on UNIX-pkgconfig
target_link_libraries(${target_name} PRIVATE PkgConfig::OPENSSL)
endif()
endif()
add_test(NAME ${test_name} COMMAND ${test_name})
endif()
endif()
add_test(TEST opendht_unit_tests)
endforeach()
endif()
if (OPENDHT_CPACK)
+12 -2
View File
@@ -125,7 +125,9 @@
"OPENDHT_PROXY_CLIENT": true,
"OPENDHT_PROXY_SERVER": true,
"OPENDHT_C": true,
"OPENDHT_CPACK": true
"OPENDHT_PYTHON": true,
"OPENDHT_CPACK": true,
"OPENDHT_DOCUMENTATION": false
}
},
{
@@ -148,7 +150,9 @@
"BUILD_TESTING": false,
"OPENDHT_PROXY_CLIENT": true,
"OPENDHT_PROXY_SERVER": true,
"OPENDHT_CPACK": true
"OPENDHT_C": true,
"OPENDHT_CPACK": true,
"OPENDHT_DOCUMENTATION": false
}
},
{
@@ -235,6 +239,12 @@
"inherits": "default",
"configurePreset": "vcpkg_release_static_full",
"configuration": "Release"
},
{
"name": "vcpkg-release",
"inherits": "default",
"configurePreset": "vcpkg_release_full",
"configuration": "Release"
}
]
}
+53 -7
View File
@@ -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>
![PyPI - Version](https://img.shields.io/pypi/v/opendht?style=flat)
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).
+3 -3
View File
@@ -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()
+19 -9
View File
@@ -1,11 +1,12 @@
dnl define macros
m4_define([opendht_major_version], 3)
m4_define([opendht_minor_version], 5)
m4_define([opendht_patch_version], 1)
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])
@@ -141,19 +142,21 @@ 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"]))
@@ -180,12 +183,18 @@ 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"
@@ -218,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
+41
View File
@@ -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"]
+27
View File
@@ -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*
+3 -1
View File
@@ -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)>;
+2 -1
View File
@@ -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 {};
+9 -5
View File
@@ -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();
+1 -1
View File
@@ -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
View File
@@ -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
+9 -9
View File
@@ -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.
+5 -1
View File
@@ -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
View File
@@ -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());
}
+5 -4
View File
@@ -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};
};
+195 -118
View File
@@ -1,30 +1,53 @@
project('opendht', 'c', 'cpp', 'cython',
version: '3.5.1',
project(
'opendht',
'c',
'cpp',
'cython',
version: '3.6.0',
meson_version: '>=1.1.0',
default_options: [
'cpp_std=c++17',
'warning_level=3'
])
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'))
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)
@@ -49,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
@@ -68,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'))
@@ -94,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
@@ -169,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
View File
@@ -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@@llhttp_lib@@iouring_lib@
Requires.private: nettle >= 2.4@argon2_lib@@llhttp_lib@@iouring_lib@@simdutf_lib@
Cflags: -I${includedir}
+34 -10
View File
@@ -3,21 +3,45 @@ 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)
find_package(Python3 REQUIRED COMPONENTS Interpreter)
add_custom_target(python
COMMAND ${Python3_EXECUTABLE} -m build .
DEPENDS opendht opendht_cpp.pxd opendht.pyx pyproject.toml)
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 [[
execute_process(
COMMAND "${Python3_EXECUTABLE}" -m pip install --no-deps .
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
RESULT_VARIABLE _pip_result
OUTPUT_FILE "toto" )
if(NOT _pip_result EQUAL 0)
message(FATAL_ERROR "pip install failed with code ${_pip_result}")
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)
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)
+89
View File
@@ -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.
+245 -104
View File
@@ -26,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
@@ -35,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']:
@@ -91,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''):
@@ -116,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):
@@ -218,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)
@@ -240,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
@@ -327,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
@@ -346,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
@@ -375,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()
@@ -439,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)
@@ -512,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}
@@ -674,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
@@ -691,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
@@ -756,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.
+12
View File
@@ -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("_")]
+293
View File
@@ -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)
+34 -3
View File
@@ -13,17 +13,17 @@
# 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 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]:
@@ -49,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
@@ -72,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:
@@ -99,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
@@ -107,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)
@@ -253,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)
@@ -270,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)
@@ -301,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)
+45
View File
@@ -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"
+46 -15
View File
@@ -13,15 +13,45 @@
# 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",
author="Adrien Beraud <adrien.beraud@savoirfairelinux.com>",
license="GPL-3.0-or-later",
classifiers=[
'Development Status :: 5 - Production/Stable',
@@ -30,17 +60,18 @@ setup(name="opendht",
'Topic :: System :: Distributed Computing',
'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"],
language="c++",
extra_compile_args=["-std=c++17"],
extra_link_args=["-std=c++17"],
libraries=["opendht"],
include_dirs = ['@PROJECT_SOURCE_DIR@/include'],
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
View File
@@ -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())
+2 -2
View File
@@ -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
View File
@@ -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;
}
+151 -72
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+2 -1
View File
@@ -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;
+11 -1
View File
@@ -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
View File
@@ -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);
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
+7 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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_; }
+1 -1
View File
@@ -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);
}
}
}
+42
View File
@@ -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() {
+3
View File
@@ -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
+47 -32
View File
@@ -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(), 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);
+13 -9
View File
@@ -1,18 +1,18 @@
{
"name": "opendht",
"version-string": "3.5.1",
"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 @@
]
}
}
}
}