Compare commits

..
2 Commits
97 changed files with 2727 additions and 3792 deletions

No files matched your search

+26 -52
View File
@@ -4,79 +4,53 @@ on: [push, pull_request]
jobs:
build-ubuntu:
name: Ubuntu/GCC Autotools build
name: Ubuntu/GCC build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Dependencies
run: |
sudo apt install libncurses5-dev libreadline-dev nettle-dev libasio-dev \
libgnutls28-dev libuv1-dev cython3 python3-dev python3-setuptools libcppunit-dev libjsoncpp-dev \
autotools-dev autoconf libfmt-dev libhttp-parser-dev libmsgpack-dev libargon2-0-dev
- name: Configure
run: |
./autogen.sh
./configure
- name: Build
run: make
build-ubuntu-meson:
name: Ubuntu/GCC Meson build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Dependencies
run: |
sudo apt install meson ninja-build libncurses5-dev libreadline-dev nettle-dev libasio-dev \
libgnutls28-dev libuv1-dev cython3 python3-dev python3-setuptools libcppunit-dev libjsoncpp-dev \
libfmt-dev libhttp-parser-dev libmsgpack-dev libargon2-0-dev
- name: Configure
run: meson setup build .
- name: Build
run: cd build && ninja
build-ubuntu-minimal:
name: Ubuntu/GCC minimal build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Dependencies
- name: deps
run: |
sudo apt install libncurses5-dev libreadline-dev nettle-dev \
libgnutls28-dev libcppunit-dev libmsgpack-dev libargon2-0-dev
- name: Configure
libgnutls28-dev libuv1-dev cython3 python3-dev python3-setuptools libcppunit-dev libjsoncpp-dev \
autotools-dev autoconf libfmt-dev libhttp-parser-dev libmsgpack-dev libargon2-0-dev
- name: autogen
run: ./autogen.sh
- name: asio
run: |
mkdir build && cd build
cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Debug \
-DOPENDHT_C=Off -DOPENDHT_TESTS=On -DOPENDHT_PEER_DISCOVERY=Off -DOPENDHT_PYTHON=Off \
-DOPENDHT_TOOLS=On -DOPENDHT_PROXY_SERVER=Off -DOPENDHT_PROXY_CLIENT=Off
- name: Build
run: cd build && make
- name: Unit tests
run: cd build && ./opendht_unit_tests
wget https://github.com/aberaud/asio/archive/b2b7a1c166390459e1c169c8ae9ef3234b361e3f.tar.gz \
&& tar -xvf b2b7a1c166390459e1c169c8ae9ef3234b361e3f.tar.gz && cd asio-b2b7a1c166390459e1c169c8ae9ef3234b361e3f/asio \
&& ./autogen.sh && ./configure --prefix=/usr --without-boost --disable-examples --disable-tests \
&& sudo make install \
&& cd ../../ && rm -rf asio*
- name: configure
run: ./configure
- name: make
run: make
- name: make check
run: make check
build-macos:
name: macOS/Clang build
runs-on: macos-11
steps:
- uses: actions/checkout@v3
- name: Dependencies
- name: deps
run: |
brew install msgpack-cxx asio gnutls nettle readline fmt jsoncpp argon2 openssl http-parser cppunit
- name: restinio
run: |
mkdir restinio && cd restinio
wget https://github.com/aberaud/restinio/archive/bbaa034dbcc7555ce67df0f8a1475591a7441733.tar.gz
ls -l && tar -xzf bbaa034dbcc7555ce67df0f8a1475591a7441733.tar.gz
cd restinio-bbaa034dbcc7555ce67df0f8a1475591a7441733/dev
wget https://github.com/aberaud/restinio/archive/e0a261dd8488246a3cb8bbb3ea781ea5139c3c94.tar.gz
ls -l && tar -xzf e0a261dd8488246a3cb8bbb3ea781ea5139c3c94.tar.gz
cd restinio-e0a261dd8488246a3cb8bbb3ea781ea5139c3c94/dev
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DRESTINIO_TEST=OFF -DRESTINIO_SAMPLE=OFF \
-DRESTINIO_INSTALL_SAMPLES=OFF -DRESTINIO_BENCH=OFF -DRESTINIO_INSTALL_BENCHES=OFF \
-DRESTINIO_FIND_DEPS=ON -DRESTINIO_ALLOW_SOBJECTIZER=Off -DRESTINIO_USE_BOOST_ASIO=none .
make -j8 && sudo make install
cd ../../.. && rm -rf restinio
- name: Configure
- name: cmake
run: |
mkdir build && cd build
export PATH="/opt/homebrew/opt/openssl@3/bin:$PATH"
@@ -85,8 +59,8 @@ jobs:
export PKG_CONFIG_PATH="/usr/local/opt/openssl@3/lib/pkgconfig"
cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Debug \
-DOPENDHT_C=On -DOPENDHT_TESTS=On -DOPENDHT_PEER_DISCOVERY=On -DOPENDHT_PYTHON=Off \
-DOPENDHT_TOOLS=On -DOPENDHT_PROXY_SERVER=On -DOPENDHT_PROXY_CLIENT=On -DOPENDHT_PUSH_NOTIFICATIONS=On
-DOPENDHT_TOOLS=On -DOPENDHT_PROXY_SERVER=On -DOPENDHT_PROXY_CLIENT=On
- name: Build
- name: make
run: cd build && make
+6 -6
View File
@@ -1,6 +1,6 @@
name: Clang Static Analysis
on: [push, pull_request]
on:
push
jobs:
clang-analyzer:
name: Clang Static Analysis
@@ -18,9 +18,9 @@ jobs:
- name: restinio
run: |
mkdir restinio && cd restinio \
&& wget https://github.com/aberaud/restinio/archive/bbaa034dbcc7555ce67df0f8a1475591a7441733.tar.gz \
&& ls -l && tar -xzf bbaa034dbcc7555ce67df0f8a1475591a7441733.tar.gz \
&& cd restinio-bbaa034dbcc7555ce67df0f8a1475591a7441733/dev \
&& wget https://github.com/aberaud/restinio/archive/e0a261dd8488246a3cb8bbb3ea781ea5139c3c94.tar.gz \
&& ls -l && tar -xzf e0a261dd8488246a3cb8bbb3ea781ea5139c3c94.tar.gz \
&& cd restinio-e0a261dd8488246a3cb8bbb3ea781ea5139c3c94/dev \
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DRESTINIO_TEST=OFF -DRESTINIO_SAMPLE=OFF \
-DRESTINIO_INSTALL_SAMPLES=OFF -DRESTINIO_BENCH=OFF -DRESTINIO_INSTALL_BENCHES=OFF \
-DRESTINIO_FIND_DEPS=ON -DRESTINIO_ALLOW_SOBJECTIZER=Off -DRESTINIO_USE_BOOST_ASIO=none . \
@@ -30,6 +30,6 @@ jobs:
- name: cmake
run: |
mkdir build && cd build && \
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug -DOPENDHT_C=On -DOPENDHT_PEER_DISCOVERY=On -DOPENDHT_PYTHON=Off -DOPENDHT_TOOLS=On -DOPENDHT_PROXY_SERVER=On -DOPENDHT_PROXY_CLIENT=On -DOPENDHT_PUSH_NOTIFICATIONS=On
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug -DOPENDHT_C=On -DOPENDHT_PEER_DISCOVERY=On -DOPENDHT_PYTHON=Off -DOPENDHT_TOOLS=On -DOPENDHT_PROXY_SERVER=On -DOPENDHT_PROXY_CLIENT=On
- name: scan-build
run: cd build && scan-build --status-bugs make
-53
View File
@@ -1,53 +0,0 @@
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "master" ]
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
container:
image: ghcr.io/savoirfairelinux/opendht/opendht-deps:latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'cpp' ]
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
-293
View File
@@ -1,293 +0,0 @@
name: Release actions
on:
push:
tags:
- 'v*'
env:
REGISTRY: ghcr.io
IMAGE_NAME_DEPS: ${{ github.repository }}/opendht-deps
IMAGE_NAME: ${{ github.repository }}/opendht
IMAGE_NAME_DEPS_LLVM: ${{ github.repository }}/opendht-deps-llvm
IMAGE_NAME_LLVM: ${{ github.repository }}/opendht-llvm
IMAGE_NAME_DHTNODE: ${{ github.repository }}/dhtnode
IMAGE_NAME_ALPINE: ${{ github.repository }}/opendht-alpine
IMAGE_NAME_ALPINE_DEPS: ${{ github.repository }}/opendht-deps-alpine
jobs:
build-and-push-deps-image:
name: Dependency Docker image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_DEPS }}
- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
context: .
file: docker/DockerfileDeps
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-and-push-image:
name: OpenDHT Docker image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
context: .
file: docker/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-and-push-deps-image-llvm:
name: Dependency Docker image (LLVM)
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_DEPS_LLVM }}
- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
context: .
file: docker/DockerfileDepsLlvm
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-and-push-image-llvm:
name: OpenDHT Docker image (LLVM)
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LLVM }}
- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
context: .
file: docker/DockerfileLlvm
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-and-push-image-dhtnode:
name: dhtnode Docker image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME_DHTNODE }}
- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
context: .
file: docker/DockerfileDhtnode
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-and-push-image-alpine-deps:
name: Alpine Deps Docker image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME_ALPINE_DEPS }}
- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
context: .
file: docker/DockerfileDepsAlpine
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-and-push-image-alpine:
name: Alpine Docker image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME_ALPINE }}
- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
context: .
file: docker/DockerfileAlpine
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-python-wheel:
name: Release and build Python Wheel package
runs-on: ubuntu-latest
container: ghcr.io/savoirfairelinux/opendht/opendht-deps:latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: cmake
run: |
mkdir build && cd build && \
cmake .. -DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_BUILD_TYPE=Release \
-DOPENDHT_STATIC=Off \
-DOPENDHT_PYTHON=On \
-DOPENDHT_C=Off \
-DOPENDHT_PEER_DISCOVERY=On \
-DOPENDHT_TOOLS=Off \
-DOPENDHT_PROXY_SERVER=On \
-DOPENDHT_PROXY_CLIENT=On
- name: build
run: cd build && make dist
- uses: actions/upload-artifact@v3
with:
name: opendht-wheels-linux
path: build/python/dist/*.whl
- name: Create Release
id: create_release
uses: softprops/action-gh-release@v1
with:
draft: true
files: build/python/dist/*.whl
-1
View File
@@ -76,5 +76,4 @@ doc/Doxyfile
# build dir
build
build_dev
.DS_Store
+8 -8
View File
@@ -5,9 +5,9 @@
"name": "dhtnode",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build_dev/tools/dhtnode",
"program": "${workspaceFolder}/build/tools/dhtnode",
"args": ["-v", "-p", "4222"],
"cwd": "${workspaceFolder}/build_dev",
"cwd": "${workspaceFolder}/build",
"environment": [],
"setupCommands": [
{
@@ -30,8 +30,8 @@
"name": "tests",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build_dev/opendht_unit_tests",
"cwd": "${workspaceFolder}/build_dev",
"program": "${workspaceFolder}/build/opendht_unit_tests",
"cwd": "${workspaceFolder}/build",
"environment": [],
"setupCommands": [
{
@@ -41,14 +41,14 @@
}
],
"preLaunchTask": "build",
"linux": {
"linux": {
"MIMode": "gdb",
"externalConsole": false,
},
"osx": {
},
"osx": {
"MIMode": "lldb",
"externalConsole": true,
},
},
}
]
}
-9
View File
@@ -1,9 +0,0 @@
{
"cmake.configureSettings": {
"OPENDHT_SANITIZE": true,
"OPENDHT_PROXY_CLIENT": true,
"OPENDHT_PROXY_SERVER": true,
"OPENDHT_C": true,
"OPENDHT_DOCUMENTATION": false
}
}
+3 -5
View File
@@ -5,7 +5,7 @@
"label": "build",
"type": "shell",
"options": {
"cwd": "${workspaceRoot}/build_dev"
"cwd": "${workspaceRoot}/build"
},
"command": "make",
"args": ["-j4"],
@@ -15,14 +15,12 @@
"label": "cmake",
"type": "shell",
"options": {
"cwd": "${workspaceRoot}/build_dev"
"cwd": "${workspaceRoot}/build"
},
"command": "cmake",
"args": [
"${workspaceRoot}",
"-DCMAKE_EXPORT_COMPILE_COMMANDS=On",
"-DCMAKE_BUILD_TYPE=Debug",
"-DOPENDHT_SANITIZE=On",
"-DOPENDHT_PROXY_CLIENT=On",
"-DOPENDHT_PROXY_SERVER=On",
"-DOPENDHT_TESTS=On",
@@ -39,7 +37,7 @@
"command": "mkdir",
"args": [
"-p",
"${workspaceRoot}/build_dev"
"${workspaceRoot}/build"
]
},
]
+117 -84
View File
@@ -1,4 +1,10 @@
cmake_minimum_required (VERSION 3.10..3.20)
cmake_minimum_required (VERSION 3.10)
if(POLICY CMP0065)
cmake_policy(SET CMP0065 NEW)
endif()
if(POLICY CMP0069)
cmake_policy(SET CMP0069 NEW)
endif()
if(POLICY CMP0073)
cmake_policy(SET CMP0073 NEW)
endif()
@@ -9,20 +15,18 @@ project (opendht)
include(CMakePackageConfigHelpers)
include(CMakeDependentOption)
include(CheckIncludeFileCXX)
include(FindPkgConfig)
include(cmake/CheckAtomic.cmake)
include(CTest)
set (opendht_VERSION_MAJOR 2)
set (opendht_VERSION_MINOR 5.5)
set (opendht_VERSION_MINOR 4.0)
set (opendht_VERSION ${opendht_VERSION_MAJOR}.${opendht_VERSION_MINOR})
set (PACKAGE_VERSION ${opendht_VERSION})
set (VERSION "${opendht_VERSION}")
# Options
option (BUILD_SHARED_LIBS "Build shared library" ON)
CMAKE_DEPENDENT_OPTION (OPENDHT_STATIC "Build static library" OFF BUILD_SHARED_LIBS ON)
option (OPENDHT_STATIC "Build static library" ON)
option (OPENDHT_SHARED "Build shared library" ON)
option (OPENDHT_LOG "Build with logs" ON)
option (OPENDHT_PYTHON "Build Python bindings" OFF)
option (OPENDHT_TOOLS "Build DHT tools" ON)
@@ -37,16 +41,13 @@ option (OPENDHT_PROXY_OPENSSL "Build DHT proxy with OpenSSL" ON)
CMAKE_DEPENDENT_OPTION(OPENDHT_HTTP "Build embedded http(s) client" OFF "NOT OPENDHT_PROXY_SERVER;NOT OPENDHT_PROXY_CLIENT" ON)
option (OPENDHT_PEER_DISCOVERY "Enable multicast peer discovery" ON)
option (OPENDHT_INDEX "Build DHT indexation feature" OFF)
option (OPENDHT_TESTS "Add unit tests executable" OFF)
option (OPENDHT_TESTS_NETWORK "Enable unit tests that require network access" ON)
option (OPENDHT_C "Build C bindings" OFF)
find_package(Doxygen)
option (OPENDHT_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" ${DOXYGEN_FOUND})
# Build flags
set (CMAKE_CXX_STANDARD 17)
set (CMAKE_CXX_STANDARD_REQUIRED on)
# Dependencies
if (NOT HAVE_CXX_ATOMICS_WITHOUT_LIB)
link_libraries (atomic)
@@ -57,34 +58,25 @@ if (NOT MSVC)
set (THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package (Threads)
find_package (PkgConfig REQUIRED)
pkg_search_module (GnuTLS REQUIRED IMPORTED_TARGET gnutls)
pkg_search_module (Nettle REQUIRED IMPORTED_TARGET nettle)
check_include_file_cxx(msgpack.hpp HAVE_MSGPACKCXX)
if (NOT HAVE_MSGPACKCXX)
find_package(msgpack QUIET CONFIG NAMES msgpack msgpackc-cxx)
if (NOT msgpack_FOUND)
find_package(msgpack REQUIRED CONFIG NAMES msgpack-cxx)
set(MSGPACK_TARGET "msgpack-cxx")
else()
set(MSGPACK_TARGET "msgpackc-cxx")
endif()
endif()
find_package (GnuTLS 3.3 REQUIRED)
pkg_search_module (Nettle REQUIRED nettle)
find_package (msgpack REQUIRED)
if (OPENDHT_TOOLS)
find_package (Readline 6 REQUIRED)
endif ()
pkg_search_module(argon2 REQUIRED IMPORTED_TARGET libargon2)
set(argon2_lib ", libargon2")
pkg_search_module(Jsoncpp IMPORTED_TARGET jsoncpp)
pkg_search_module(argon2 REQUIRED libargon2)
pkg_search_module(Jsoncpp jsoncpp)
if (Jsoncpp_FOUND)
add_definitions(-DOPENDHT_JSONCPP)
set(jsoncpp_lib ", jsoncpp")
list (APPEND opendht_SOURCES
src/base64.h
src/base64.cpp
)
endif()
find_path(ASIO_INCLUDE_DIR asio.hpp REQUIRED)
if (OPENDHT_HTTP OR OPENDHT_PEER_DISCOVERY)
find_path(ASIO_INCLUDE_DIR asio.hpp REQUIRED)
endif ()
if (OPENDHT_HTTP)
find_package(Restinio REQUIRED)
@@ -92,16 +84,16 @@ if (NOT MSVC)
add_library(fmt SHARED IMPORTED)
find_library(HTTP_PARSER_LIBRARY http_parser)
add_library(http_parser SHARED IMPORTED)
set(http_parser_lib "-lhttp_parser")
if (NOT Jsoncpp_FOUND)
message(SEND_ERROR "Jsoncpp is required for DHT proxy support")
endif()
if (OPENDHT_PROXY_OPENSSL)
# https://cmake.org/cmake/help/latest/module/FindOpenSSL.html
pkg_search_module(OPENSSL REQUIRED IMPORTED_TARGET openssl)
pkg_search_module(OPENSSL REQUIRED openssl)
if (OPENSSL_FOUND)
message(STATUS "Found OpenSSL ${OPENSSL_VERSION} ${OPENSSL_INCLUDE_DIRS}")
set(openssl_lib ", openssl")
include_directories(SYSTEM ${OPENSSL_INCLUDE_DIRS})
link_directories (${OPENSSL_LIBRARY_DIRS})
else ()
message(SEND_ERROR "OpenSSL is required for DHT proxy as specified")
endif()
@@ -135,8 +127,12 @@ if (OPENDHT_HTTP OR OPENDHT_PEER_DISCOVERY)
add_definitions(-DASIO_STANDALONE)
endif()
# Build flags
set (CMAKE_CXX_STANDARD 17)
set (CMAKE_CXX_STANDARD_REQUIRED on)
if (NOT MSVC)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-return-type -Wno-deprecated -Wall -Wextra -Wnon-virtual-dtor -pedantic-errors -fvisibility=hidden")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-return-type -Wall -Wextra -Wnon-virtual-dtor -pedantic-errors -fvisibility=hidden")
if (OPENDHT_SANITIZE)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fstack-protector-strong")
endif ()
@@ -152,6 +148,9 @@ else ()
endif ()
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMSGPACK_NO_BOOST -DMSGPACK_DISABLE_LEGACY_NIL -DMSGPACK_DISABLE_LEGACY_CONVERT")
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif ()
add_definitions(-DPACKAGE_VERSION="${opendht_VERSION}")
if (OPENDHT_LOG)
add_definitions(-DOPENDHT_LOG=true)
@@ -159,12 +158,27 @@ else ()
add_definitions(-DOPENDHT_LOG=false)
endif()
if (MSGPACK_INCLUDE_DIRS)
include_directories (SYSTEM "${MSGPACK_INCLUDE_DIRS}")
endif ()
if (GNUTLS_INCLUDE_DIRS)
include_directories (SYSTEM "${GNUTLS_INCLUDE_DIRS}")
endif ()
if (Nettle_INCLUDE_DIRS)
include_directories (SYSTEM "${Nettle_INCLUDE_DIRS}")
endif ()
if (ASIO_INCLUDE_DIR)
include_directories (SYSTEM "${ASIO_INCLUDE_DIR}")
endif ()
if (Restinio_INCLUDE_DIR)
include_directories (SYSTEM "${Restinio_INCLUDE_DIR}")
endif ()
if (Jsoncpp_INCLUDE_DIRS)
include_directories (SYSTEM "${Jsoncpp_INCLUDE_DIRS}")
endif ()
link_directories (${Nettle_LIBRARY_DIRS})
link_directories (${Jsoncpp_LIBRARY_DIRS})
link_directories (${argon2_LIBRARY_DIRS})
include_directories (
./
include/
@@ -176,8 +190,8 @@ include_directories (
include (GNUInstallDirs)
set (prefix ${CMAKE_INSTALL_PREFIX})
set (exec_prefix "\${prefix}")
set (libdir "${CMAKE_INSTALL_FULL_LIBDIR}")
set (includedir "${CMAKE_INSTALL_FULL_INCLUDEDIR}")
set (libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
set (includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
set (bindir "${CMAKE_INSTALL_FULL_BINDIR}")
set (sysconfdir "${CMAKE_INSTALL_FULL_SYSCONFDIR}")
set (top_srcdir "${CMAKE_CURRENT_SOURCE_DIR}")
@@ -185,6 +199,7 @@ set (top_srcdir "${CMAKE_CURRENT_SOURCE_DIR}")
# Sources
list (APPEND opendht_SOURCES
src/utils.cpp
src/infohash.cpp
src/crypto.cpp
src/default_types.cpp
src/node.cpp
@@ -208,12 +223,12 @@ list (APPEND opendht_SOURCES
src/log.cpp
src/network_utils.cpp
src/thread_pool.cpp
src/udp_socket.cpp
)
list (APPEND opendht_HEADERS
include/opendht/def.h
include/opendht/utils.h
include/opendht/sockaddr.h
include/opendht/rng.h
include/opendht/crypto.h
include/opendht/infohash.h
@@ -232,7 +247,7 @@ list (APPEND opendht_HEADERS
include/opendht/log.h
include/opendht/log_enable.h
include/opendht/thread_pool.h
include/opendht/udp_socket.h
include/opendht/network_utils.h
include/opendht.h
)
@@ -296,8 +311,20 @@ if (MSVC)
endif ()
# Targets
if (MSVC)
if (OPENDHT_STATIC)
if (OPENDHT_STATIC)
if (NOT MSVC)
add_library (opendht-static STATIC
${opendht_SOURCES}
${opendht_HEADERS}
)
set_target_properties (opendht-static PROPERTIES OUTPUT_NAME "opendht")
target_include_directories(opendht-static SYSTEM PRIVATE ${argon2_INCLUDE_DIRS})
target_link_libraries(opendht-static
PRIVATE ${argon2_LIBRARIES}
PUBLIC ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_STATIC_LIBRARIES}
${Jsoncpp_STATIC_LIBRARIES} ${FMT_LIBRARY} ${HTTP_PARSER_LIBRARY}
${OPENSSL_STATIC_LIBRARIES})
else ()
if (OPENDHT_TOOLS)
function (add_obj_lib name libfile)
add_library(${name} OBJECT IMPORTED)
@@ -336,63 +363,56 @@ if (MSVC)
${PROJECT_SOURCE_DIR}/../openssl/libcrypto.lib
)
endif ()
add_library (opendht-static STATIC
${opendht_SOURCES}
${opendht_HEADERS}
${obj_libs}
)
target_link_libraries(opendht-static PUBLIC ${Win32_STATIC_LIBRARIES} ${Win32_IMPORT_LIBRARIES})
set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4006")
set_target_properties (opendht-static PROPERTIES OUTPUT_NAME "libopendht")
endif()
install (TARGETS opendht-static DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht)
endif ()
add_library (opendht
${opendht_SOURCES}
${opendht_HEADERS}
${obj_libs}
)
set_target_properties (opendht PROPERTIES OUTPUT_NAME "opendht")
if (NOT HAVE_MSGPACKCXX)
target_link_libraries(opendht PUBLIC ${MSGPACK_TARGET})
endif()
if (APPLE)
target_link_libraries(opendht PRIVATE SYSTEM "-framework CoreFoundation" "-framework Security")
endif()
if (MSVC)
if (OPENDHT_STATIC)
target_link_libraries(opendht PUBLIC ${Win32_STATIC_LIBRARIES} ${Win32_IMPORT_LIBRARIES})
set_target_properties (opendht PROPERTIES OUTPUT_NAME "libopendht")
endif()
else()
target_link_libraries(opendht
PRIVATE
PkgConfig::argon2
PkgConfig::Nettle
${HTTP_PARSER_LIBRARY}
PUBLIC
${CMAKE_THREAD_LIBS_INIT}
PkgConfig::GnuTLS
${FMT_LIBRARY}
if (OPENDHT_SHARED)
add_library (opendht SHARED
${opendht_SOURCES}
${opendht_HEADERS}
)
if (Jsoncpp_FOUND)
target_link_libraries(opendht PUBLIC PkgConfig::Jsoncpp)
endif()
if (OPENDHT_PROXY_OPENSSL)
target_link_libraries(opendht PUBLIC PkgConfig::OPENSSL)
endif()
endif()
if (BUILD_SHARED_LIBS)
set_target_properties (opendht PROPERTIES IMPORT_SUFFIX "_import.lib")
set_target_properties (opendht PROPERTIES SOVERSION ${opendht_VERSION_MAJOR} VERSION ${opendht_VERSION})
target_compile_definitions(opendht PRIVATE OPENDHT_BUILD)
target_include_directories(opendht SYSTEM PRIVATE ${argon2_INCLUDE_DIRS})
if (APPLE)
target_link_libraries(opendht
PUBLIC ${CMAKE_THREAD_LIBS_INIT} ${OPENSSL_LIBRARIES}
PRIVATE ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES}
${Jsoncpp_LIBRARIES}
${FMT_LIBRARY} ${HTTP_PARSER_LIBRARY} ${argon2_LIBRARIES}
SYSTEM "-framework CoreFoundation" "-framework Security")
else ()
target_link_libraries(opendht
PUBLIC ${CMAKE_THREAD_LIBS_INIT} ${OPENSSL_LIBRARIES}
PRIVATE ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES}
${Jsoncpp_LIBRARIES}
${FMT_LIBRARY} ${HTTP_PARSER_LIBRARY} ${argon2_LIBRARIES})
endif ()
install (TARGETS opendht DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht)
endif ()
install (TARGETS opendht DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht)
if (OPENDHT_C)
add_library (opendht-c
add_library (opendht-c SHARED
c/opendht.cpp
c/opendht_c.h
)
target_compile_definitions(opendht-c PRIVATE OPENDHT_C_BUILD)
target_link_libraries(opendht-c PRIVATE opendht)
set_target_properties (opendht-c PROPERTIES SOVERSION ${opendht_VERSION_MAJOR} VERSION ${opendht_VERSION})
install (TARGETS opendht-c DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht-c)
if (OPENDHT_SHARED)
target_link_libraries(opendht-c opendht)
else ()
target_link_libraries(opendht-c opendht-static)
endif ()
# PkgConfig module
configure_file (
@@ -400,6 +420,7 @@ if (OPENDHT_C)
opendht-c.pc
@ONLY
)
install (TARGETS opendht-c DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht-c)
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/opendht-c.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
install (FILES c/opendht_c.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include/opendht)
endif ()
@@ -432,8 +453,8 @@ install (EXPORT opendht DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/opendht FILE o
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/opendhtConfigVersion.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/opendht)
# Unit tests
if (BUILD_TESTING AND NOT MSVC)
pkg_search_module(Cppunit REQUIRED IMPORTED_TARGET cppunit)
if (OPENDHT_TESTS)
pkg_search_module(Cppunit REQUIRED cppunit)
# unit testing
list (APPEND test_FILES
tests/infohashtester.h
@@ -467,10 +488,22 @@ if (BUILD_TESTING AND NOT MSVC)
tests/tests_runner.cpp
${test_FILES}
)
target_link_libraries(opendht_unit_tests PRIVATE
opendht
target_include_directories(opendht_unit_tests SYSTEM PRIVATE ${Cppunit_INCLUDE_DIR})
target_link_directories(opendht_unit_tests PRIVATE ${Cppunit_LIBRARY_DIRS})
if (OPENDHT_SHARED)
target_link_libraries(opendht_unit_tests opendht)
else ()
target_link_libraries(opendht_unit_tests opendht-static)
endif ()
target_link_libraries(opendht_unit_tests
${CMAKE_THREAD_LIBS_INIT}
PkgConfig::Cppunit
${Cppunit_LIBRARIES}
${GNUTLS_LIBRARIES}
${Jsoncpp_LIBRARIES}
)
if (OPENDHT_PROXY_OPENSSL)
target_link_libraries(opendht_unit_tests ${OPENSSL_LIBRARIES})
endif()
enable_testing()
add_test(TEST opendht_unit_tests)
endif()
-4
View File
@@ -4,10 +4,6 @@ SUBDIRS =
SUBDIRS += src
if ENABLE_C
SUBDIRS += c
endif
if ENABLE_TOOLS
SUBDIRS += tools
endif
+8
View File
@@ -25,6 +25,11 @@ See the wiki: <https://github.com/savoirfairelinux/opendht/wiki>
Build instructions: <https://github.com/savoirfairelinux/opendht/wiki/Build-the-library>
#### How-to build a simple client app
```bash
g++ main.cpp -std=c++17 -lopendht
```
## Examples
### C++ example
The `tools` directory includes simple example programs :
@@ -112,3 +117,6 @@ 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 project called OpenDHT (Sean Rhea. Ph.D. Thesis, 2005), now extinct.
## Donations
We gratefully accept Bitcoin donations to support OpenDHT development at: `bitcoin:3EykSd1An888efq4Bq3KaV3hJ3JQ4FPnwm`.
-15
View File
@@ -1,15 +0,0 @@
lib_LTLIBRARIES = libopendht-c.la
noinst_HEADERS = opendht_c.h
AM_CPPFLAGS = -DOPENDHT_C_BUILD -isystem @top_srcdir@/include @JsonCpp_CFLAGS@ @MsgPack_CFLAGS@
if OPENDHT_SHARED
AM_CPPFLAGS += -Dopendht_c_EXPORTS
endif
libopendht_c_la_LIBADD = $(LIBOBJS) ../src/.libs/libopendht.la
libopendht_c_la_SOURCES = \
opendht.cpp
nobase_include_HEADERS = \
opendht_c.h
+17 -65
View File
@@ -17,9 +17,7 @@
*/
#include "opendht_c.h"
#include <opendht.h>
#include <opendht/log.h>
#include "opendht.h"
using ValueSp = std::shared_ptr<dht::Value>;
using PrivkeySp = std::shared_ptr<dht::crypto::PrivateKey>;
@@ -31,12 +29,6 @@ extern "C" {
#endif
#include <errno.h>
#include <cstdlib>
const char* dht_version()
{
return dht::version();
}
// dht::InfoHash
@@ -77,11 +69,7 @@ bool dht_infohash_is_zero(const dht_infohash* h) {
}
void dht_infohash_from_hex(dht_infohash* h, const char* dat) {
*h = dht_infohash_to_c(dht::InfoHash(std::string_view(dat, HASH_LEN*2)));
}
void dht_infohash_from_hex_null(dht_infohash* h, const char* dat) {
*h = dht_infohash_to_c(dht::InfoHash(std::string_view(dat)));
*h = dht_infohash_to_c(dht::InfoHash(std::string(dat, HASH_LEN*2)));
}
const char* dht_pkid_print(const dht_pkid* h) {
@@ -262,7 +250,7 @@ dht_pkid dht_certificate_get_long_id(const dht_certificate* c) {
dht_publickey* dht_certificate_get_publickey(const dht_certificate* c) {
const auto& cert = *reinterpret_cast<const CertSp*>(c);
return reinterpret_cast<dht_publickey*>(new PubkeySp(cert->getSharedPublicKey()));
return reinterpret_cast<dht_publickey*>(new PubkeySp(std::make_shared<dht::crypto::PublicKey>(cert->getPublicKey())));
}
// dht::crypto::Identity
@@ -343,69 +331,23 @@ int dht_runner_run_config(dht_runner* r, in_port_t port, const dht_runner_config
config.proxy_server = conf->proxy_server ? std::string(conf->proxy_server) : std::string{};
config.push_node_id = conf->push_node_id ? std::string(conf->push_node_id) : std::string{};
config.push_token = conf->push_token ? std::string(conf->push_token) : std::string{};
config.push_topic = conf->push_topic ? std::string(conf->push_topic) : std::string{};
config.push_platform = conf->push_platform ? std::string(conf->push_platform) : std::string{};
config.peer_discovery = conf->peer_discovery;
config.peer_publish = conf->peer_publish;
dht::DhtRunner::Context context;
if (conf->log) {
context.logger = dht::log::getStdLogger();
}
runner->run(port, config, std::move(context));
runner->run(port, config);
} catch(...) {
return ENOTCONN;
}
return 0;
}
asio::ip::udp::endpoint sockaddr_to_endpoint(struct sockaddr* addr, socklen_t addr_len) {
if (addr->sa_family == AF_INET) {
auto addr_in = reinterpret_cast<struct sockaddr_in*>(addr);
asio::ip::address_v4 address(ntohl(addr_in->sin_addr.s_addr));
unsigned short port = ntohs(addr_in->sin_port);
return asio::ip::udp::endpoint(address, port);
} else if (addr->sa_family == AF_INET6) {
auto addr_in6 = reinterpret_cast<struct sockaddr_in6*>(addr);
asio::ip::address_v6::bytes_type bytes;
std::memcpy(bytes.data(), &addr_in6->sin6_addr, 16);
asio::ip::address_v6 address(bytes, addr_in6->sin6_scope_id);
unsigned short port = ntohs(addr_in6->sin6_port);
return asio::ip::udp::endpoint(address, port);
} else {
throw std::runtime_error("Invalid sockaddr family");
}
}
struct sockaddr* endpoint_to_sockaddr(const asio::ip::udp::endpoint& endpoint, socklen_t* addr_len = nullptr) {
const struct sockaddr* addr_src = endpoint.data();
auto len = endpoint.size();
struct sockaddr* addr_copy = reinterpret_cast<struct sockaddr*>(std::malloc(len));
std::memcpy(addr_copy, addr_src, len);
if (addr_len)
*addr_len = len;
return addr_copy;
}
struct sockaddr** endpoints_to_sockaddrs(const std::vector<asio::ip::udp::endpoint>& endpoints) {
size_t num_endpoints = endpoints.size();
struct sockaddr** sockaddrs = reinterpret_cast<struct sockaddr**>(std::malloc((num_endpoints + 1) * sizeof(struct sockaddr*)));
for (size_t i = 0; i < num_endpoints; ++i) {
sockaddrs[i] = endpoint_to_sockaddr(endpoints[i]);
}
sockaddrs[num_endpoints] = nullptr; // Null-terminate the array
return sockaddrs;
}
void dht_runner_ping(dht_runner* r, struct sockaddr* addr, socklen_t addr_len, dht_done_cb done_cb, void* cb_user_data) {
auto runner = reinterpret_cast<dht::DhtRunner*>(r);
if (done_cb) {
runner->bootstrap(sockaddr_to_endpoint(addr, addr_len), [done_cb, cb_user_data](bool ok){
runner->bootstrap(dht::SockAddr(addr, addr_len), [done_cb, cb_user_data](bool ok){
done_cb(ok, cb_user_data);
});
} else {
runner->bootstrap(sockaddr_to_endpoint(addr, addr_len));
runner->bootstrap(dht::SockAddr(addr, addr_len));
}
}
@@ -545,7 +487,17 @@ struct sockaddr** dht_runner_get_public_address(const dht_runner* r) {
auto addrs = const_cast<dht::DhtRunner*>(runner)->getPublicAddress();
if (addrs.empty())
return nullptr;
return endpoints_to_sockaddrs(addrs);
auto ret = (struct sockaddr**)malloc(sizeof(struct sockaddr*) * (addrs.size() + 1));
for (size_t i=0; i<addrs.size(); i++) {
if (const auto& addr = addrs[i]) {
ret[i] = (struct sockaddr*)malloc(addr.getLength());
memcpy((struct sockaddr*)ret[i], addr.get(), addr.getLength());
} else {
ret[i] = nullptr;
}
}
ret[addrs.size()] = nullptr;
return ret;
}
#ifdef __cplusplus
+1 -7
View File
@@ -22,7 +22,7 @@
extern "C" {
#endif
#include <opendht/def.h>
#include "def.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdbool.h>
@@ -36,8 +36,6 @@ struct OPENDHT_C_PUBLIC dht_data_view {
};
typedef struct dht_data_view dht_data_view;
OPENDHT_C_PUBLIC const char* dht_version(void);
// dht::Blob
struct OPENDHT_C_PUBLIC dht_blob;
typedef struct dht_blob dht_blob;
@@ -50,7 +48,6 @@ typedef struct dht_infohash dht_infohash;
OPENDHT_C_PUBLIC void dht_infohash_zero(dht_infohash* h);
OPENDHT_C_PUBLIC void dht_infohash_random(dht_infohash* h);
OPENDHT_C_PUBLIC void dht_infohash_from_hex(dht_infohash* h, const char* dat);
OPENDHT_C_PUBLIC void dht_infohash_from_hex_null(dht_infohash* h, const char* dat);
OPENDHT_C_PUBLIC void dht_infohash_get(dht_infohash* h, const uint8_t* dat, size_t dat_size);
OPENDHT_C_PUBLIC void dht_infohash_get_from_string(dht_infohash* h, const char* str);
OPENDHT_C_PUBLIC const char* dht_infohash_print(const dht_infohash* h);
@@ -146,13 +143,10 @@ struct OPENDHT_PUBLIC dht_runner_config {
const char* proxy_server;
const char* push_node_id;
const char* push_token;
const char* push_topic;
const char* push_platform;
bool peer_discovery;
bool peer_publish;
dht_certificate* server_ca;
dht_identity client_identity;
bool log;
};
typedef struct dht_runner_config dht_runner_config;
OPENDHT_C_PUBLIC void dht_runner_config_default(dht_runner_config* config);
+18 -31
View File
@@ -1,7 +1,7 @@
dnl define macros
m4_define([opendht_major_version], 2)
m4_define([opendht_minor_version], 5)
m4_define([opendht_patch_version], 5)
m4_define([opendht_minor_version], 4)
m4_define([opendht_patch_version], 0)
m4_define([opendht_version],
[opendht_major_version.opendht_minor_version.opendht_patch_version])
@@ -18,33 +18,18 @@ AC_SUBST(OPENDHT_PATCH_VERSION, opendht_patch_version)
AC_ARG_ENABLE([debug], AS_HELP_STRING([--enable-debug], [Build in debug mode, adds stricter warnings, disables optimization]))
AS_IF([test "x$enable_debug" = "xyes"],
[CXXFLAGS="${CXXFLAGS} -g -Og -Wno-return-type -Wall -Wextra -Wnon-virtual-dtor -Wno-deprecated -pedantic-errors"],
[CXXFLAGS="${CXXFLAGS} -O3 -Wno-deprecated -pedantic-errors -fvisibility=hidden"])
[CXXFLAGS="${CXXFLAGS} -g -Wno-return-type -Wall -Wextra -Wnon-virtual-dtor -O0 -pedantic-errors"],
[CXXFLAGS="${CXXFLAGS} -O3 -pedantic-errors"])
CPPFLAGS+=" -DOPENDHT_BUILD"
AM_CONDITIONAL([OPENDHT_SHARED], [test "x$enable_shared" != xno])
AM_COND_IF(OPENDHT_SHARED, [
CPPFLAGS+=" -Dopendht_EXPORTS"
])
AM_PROG_AR
LT_INIT()
LT_LANG(C++)
AC_LANG(C++)
AC_PROG_CXX
AX_CXX_COMPILE_STDCXX(17,[noext],[mandatory])
AM_PROG_AR
dnl Check for logs
AC_ARG_ENABLE([logs], [AS_HELP_STRING([--disable-logs], [Disable DHT logs])])
AS_IF([test "x$enable_logs" != "xno"], [], [
AC_DEFINE([OPENDHT_LOG], [false], [Define if DHT logs are disabled])
])
dnl Check for C binding
AC_ARG_ENABLE([c], [AS_HELP_STRING([--disable-c], [Disable DHT C binding])])
AM_CONDITIONAL(ENABLE_C, test x$enable_c != "xno")
AM_COND_IF(ENABLE_C, [
AC_DEFINE([OPENDHT_C], [], [Define if DHT C biding is enabled])
AS_IF([test "x$enable_logs" != "xno"], [
AC_DEFINE([OPENDHT_LOG], [true], [Define if DHT logs are enabled])
], [
AC_DEFINE([OPENDHT_LOG], [false], [Define if DHT logs are enabled])
])
dnl Check for indexation
@@ -114,6 +99,11 @@ AM_CONDITIONAL(WIN32, [test "x$SYS" = "xmingw32"])
AS_IF([test "x$SYS" = "xandroid"],
[], [LDFLAGS="${LDFLAGS} -lpthread"])
LT_INIT()
LT_LANG(C++)
AX_CXX_COMPILE_STDCXX(17,[noext],[mandatory])
PKG_PROG_PKG_CONFIG()
AC_ARG_ENABLE([proxy_server], AS_HELP_STRING([--enable-proxy-server], [Enable proxy server ability]), proxy_server=yes, proxy_server=no)
@@ -132,8 +122,8 @@ AC_ARG_ENABLE([proxy_client], AS_HELP_STRING([--enable-proxy-client], [Enable pr
AM_CONDITIONAL(ENABLE_PROXY_CLIENT, test x$proxy_client == xyes)
AC_ARG_ENABLE([tests], AS_HELP_STRING([--enable-tests], [Enable tests]), build_tests=yes, build_tests=no)
AM_CONDITIONAL(ENABLE_TESTS, test "x$build_tests" == xyes)
AS_IF([test "x$build_tests" == xyes], [
AM_CONDITIONAL(ENABLE_TESTS, test x$build_tests == xyes)
AM_COND_IF([ENABLE_TESTS], [
PKG_CHECK_MODULES([CppUnit], [cppunit >= 1.12])
])
@@ -141,9 +131,7 @@ 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 >= 1.2])
PKG_CHECK_MODULES([Argon2], [libargon2])
AC_SUBST(argon2_lib, [", libargon2"])
@@ -172,7 +160,7 @@ AS_IF([test "x$have_openssl" = "xyes"], [
])
AM_COND_IF([PROXY_CLIENT_OR_SERVER], [
AC_CHECK_HEADERS([asio.hpp],, AC_MSG_ERROR([Missing Asio headers files]))
AC_CHECK_HEADERS([asio.hpp], exit,, AC_MSG_ERROR([Missing Asio headers files]))
CXXFLAGS="${CXXFLAGS} -DASIO_STANDALONE"
PKG_CHECK_MODULES([Fmt], [fmt >= 5.3.0], [have_fmt=yes], [have_fmt=no])
AS_IF([test "x$have_fmt" = "xyes"], [
@@ -210,7 +198,6 @@ AC_SUBST(PROJECT_BINARY_DIR, "../src/.libs")
AC_CONFIG_FILES([Makefile
src/Makefile
c/Makefile
tools/Makefile
python/Makefile
python/setup.py
+2 -4
View File
@@ -2,9 +2,8 @@ FROM ghcr.io/savoirfairelinux/opendht/opendht-deps:latest
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
COPY . opendht
RUN cd opendht && mkdir build && cd build \
RUN git clone https://github.com/savoirfairelinux/opendht.git \
&& cd opendht && mkdir build && cd build \
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=On \
-DOPENDHT_C=On \
@@ -13,6 +12,5 @@ RUN cd opendht && mkdir build && cd build \
-DOPENDHT_TOOLS=On \
-DOPENDHT_PROXY_SERVER=On \
-DOPENDHT_PROXY_CLIENT=On \
-DOPENDHT_SYSTEMD=On \
&& make -j8 && make install \
&& cd ../.. && rm -rf opendht
-37
View File
@@ -1,37 +0,0 @@
FROM ghcr.io/savoirfairelinux/opendht/opendht-deps-alpine:latest AS build
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
COPY . opendht
RUN mkdir /install
ENV DESTDIR /install
RUN cd opendht && mkdir build && cd build \
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=On \
-DOPENDHT_C=On \
-DOPENDHT_PEER_DISCOVERY=On \
-DOPENDHT_PYTHON=On \
-DOPENDHT_TOOLS=On \
-DOPENDHT_PROXY_SERVER=On \
-DOPENDHT_PROXY_CLIENT=On \
-DOPENDHT_SYSTEMD=On \
&& make -j8 && make install
FROM alpine:3.17 AS install
COPY --from=build /install /
RUN apk add --no-cache \
libstdc++ \
gnutls \
nettle \
openssl \
argon2-dev \
jsoncpp \
fmt \
http-parser \
readline \
ncurses
CMD ["dhtnode", "-b", "bootstrap.jami.net", "-p", "4222", "--proxyserver", "8080"]
EXPOSE 4222/udp
EXPOSE 8080/tcp
+2 -3
View File
@@ -2,9 +2,8 @@ FROM ghcr.io/savoirfairelinux/opendht/opendht-deps-bionic:latest
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
COPY . opendht
RUN cd opendht && mkdir build && cd build \
RUN git clone https://github.com/savoirfairelinux/opendht.git \
&& cd opendht && mkdir build && cd build \
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=On \
-DOPENDHT_C=On \
+6 -6
View File
@@ -1,4 +1,4 @@
FROM ubuntu:22.04
FROM ubuntu:20.04
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
@@ -9,8 +9,8 @@ RUN apt-get update && apt-get install -y \
RUN apt-get update && apt-get install -y \
build-essential pkg-config cmake git wget \
libtool autotools-dev autoconf \
cython3 python3-dev python3-setuptools python3-build python3-virtualenv \
autotools-dev autoconf \
cython3 python3-dev python3-setuptools \
libncurses5-dev libreadline-dev nettle-dev libcppunit-dev \
libgnutls28-dev libuv1-dev libjsoncpp-dev libargon2-dev \
libssl-dev libfmt-dev libhttp-parser-dev libasio-dev libmsgpack-dev \
@@ -18,9 +18,9 @@ RUN apt-get update && apt-get install -y \
RUN echo "*** Downloading RESTinio ***" \
&& mkdir restinio && cd restinio \
&& wget https://github.com/aberaud/restinio/archive/bbaa034dbcc7555ce67df0f8a1475591a7441733.tar.gz \
&& ls -l && tar -xzf bbaa034dbcc7555ce67df0f8a1475591a7441733.tar.gz \
&& cd restinio-bbaa034dbcc7555ce67df0f8a1475591a7441733/dev \
&& wget https://github.com/aberaud/restinio/archive/e0a261dd8488246a3cb8bbb3ea781ea5139c3c94.tar.gz \
&& ls -l && tar -xzf e0a261dd8488246a3cb8bbb3ea781ea5139c3c94.tar.gz \
&& cd restinio-e0a261dd8488246a3cb8bbb3ea781ea5139c3c94/dev \
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DRESTINIO_TEST=OFF -DRESTINIO_SAMPLE=OFF \
-DRESTINIO_INSTALL_SAMPLES=OFF -DRESTINIO_BENCH=OFF -DRESTINIO_INSTALL_BENCHES=OFF \
-DRESTINIO_FIND_DEPS=ON -DRESTINIO_ALLOW_SOBJECTIZER=Off -DRESTINIO_USE_BOOST_ASIO=none . \
-23
View File
@@ -1,23 +0,0 @@
FROM alpine:3.17
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 \
http-parser-dev asio-dev msgpack-cxx-dev \
&& rm -rf /var/cache/apk/*
RUN echo "*** Downloading RESTinio ***" \
&& mkdir restinio && cd restinio \
&& wget https://github.com/aberaud/restinio/archive/bbaa034dbcc7555ce67df0f8a1475591a7441733.tar.gz \
&& tar -xzf bbaa034dbcc7555ce67df0f8a1475591a7441733.tar.gz \
&& cd restinio-bbaa034dbcc7555ce67df0f8a1475591a7441733/dev \
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DRESTINIO_TEST=OFF -DRESTINIO_SAMPLE=OFF \
-DRESTINIO_INSTALL_SAMPLES=OFF -DRESTINIO_BENCH=OFF -DRESTINIO_INSTALL_BENCHES=OFF \
-DRESTINIO_FIND_DEPS=ON -DRESTINIO_ALLOW_SOBJECTIZER=Off -DRESTINIO_USE_BOOST_ASIO=none . \
&& make -j8 && make install \
&& cd ../../.. && rm -rf restinio
-28
View File
@@ -1,28 +0,0 @@
FROM ubuntu:20.04
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
RUN apt-get update && apt-get install -y \
dialog apt-utils \
&& apt-get clean \
&& echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
RUN apt-get update && apt-get install -y \
build-essential pkg-config cmake git wget \
libtool autotools-dev autoconf \
cython3 python3-dev python3-setuptools \
libncurses5-dev libreadline-dev nettle-dev libcppunit-dev \
libgnutls28-dev libuv1-dev libjsoncpp-dev libargon2-dev \
libssl-dev libfmt-dev libhttp-parser-dev libasio-dev libmsgpack-dev \
&& apt-get clean && rm -rf /var/lib/apt/lists/* /var/cache/apt/*
RUN echo "*** Downloading RESTinio ***" \
&& mkdir restinio && cd restinio \
&& wget https://github.com/aberaud/restinio/archive/e0a261dd8488246a3cb8bbb3ea781ea5139c3c94.tar.gz \
&& ls -l && tar -xzf e0a261dd8488246a3cb8bbb3ea781ea5139c3c94.tar.gz \
&& cd restinio-e0a261dd8488246a3cb8bbb3ea781ea5139c3c94/dev \
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DRESTINIO_TEST=OFF -DRESTINIO_SAMPLE=OFF \
-DRESTINIO_INSTALL_SAMPLES=OFF -DRESTINIO_BENCH=OFF -DRESTINIO_INSTALL_BENCHES=OFF \
-DRESTINIO_FIND_DEPS=ON -DRESTINIO_ALLOW_SOBJECTIZER=Off -DRESTINIO_USE_BOOST_ASIO=none . \
&& make -j8 && make install \
&& cd ../../.. && rm -rf restinio
+6 -7
View File
@@ -1,4 +1,4 @@
FROM ubuntu:22.04
FROM ubuntu:20.04
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
@@ -8,10 +8,9 @@ RUN apt-get update && apt-get install -y \
&& echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
RUN apt-get update \
&& apt-get install -y llvm llvm-dev lldb clang gdb make cmake pkg-config \
libtool git wget libncurses5-dev libreadline-dev \
&& apt-get install -y llvm llvm-dev lldb clang gdb make cmake pkg-config git wget libncurses5-dev libreadline-dev \
nettle-dev libgnutls28-dev libuv1-dev libmsgpack-dev libjsoncpp-dev cython3 python3-dev \
python3-setuptools libcppunit-dev python3-pip python3-build python3-virtualenv \
python3-setuptools libcppunit-dev python3-pip \
autotools-dev autoconf libssl-dev libargon2-dev \
libfmt-dev libhttp-parser-dev libasio-dev \
&& apt-get remove -y gcc g++ && apt-get autoremove -y \
@@ -22,9 +21,9 @@ ENV CXX c++
RUN echo "*** Downloading RESTinio ***" \
&& mkdir restinio && cd restinio \
&& wget https://github.com/aberaud/restinio/archive/bbaa034dbcc7555ce67df0f8a1475591a7441733.tar.gz \
&& ls -l && tar -xzf bbaa034dbcc7555ce67df0f8a1475591a7441733.tar.gz \
&& cd restinio-bbaa034dbcc7555ce67df0f8a1475591a7441733/dev \
&& wget https://github.com/aberaud/restinio/archive/e0a261dd8488246a3cb8bbb3ea781ea5139c3c94.tar.gz \
&& ls -l && tar -xzf e0a261dd8488246a3cb8bbb3ea781ea5139c3c94.tar.gz \
&& cd restinio-e0a261dd8488246a3cb8bbb3ea781ea5139c3c94/dev \
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DRESTINIO_TEST=OFF -DRESTINIO_SAMPLE=OFF \
-DRESTINIO_INSTALL_SAMPLES=OFF -DRESTINIO_BENCH=OFF -DRESTINIO_INSTALL_BENCHES=OFF \
-DRESTINIO_FIND_DEPS=ON -DRESTINIO_ALLOW_SOBJECTIZER=Off -DRESTINIO_USE_BOOST_ASIO=none . \
-4
View File
@@ -1,4 +0,0 @@
FROM ghcr.io/savoirfairelinux/opendht/opendht:latest
CMD ["dhtnode", "-b", "bootstrap.jami.net", "-p", "4222", "--proxyserver", "8080"]
EXPOSE 4222/udp
EXPOSE 8080/tcp
-17
View File
@@ -1,17 +0,0 @@
FROM ghcr.io/savoirfairelinux/opendht/opendht-deps-bionic:latest
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
COPY . opendht
RUN cd opendht && mkdir build && cd build \
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=On \
-DOPENDHT_C=On \
-DOPENDHT_PEER_DISCOVERY=On \
-DOPENDHT_PYTHON=On \
-DOPENDHT_TOOLS=On \
-DOPENDHT_PROXY_SERVER=On \
-DOPENDHT_PROXY_CLIENT=On \
&& make -j8 && make install \
&& cd ../.. && rm -rf opendht
+2 -4
View File
@@ -2,9 +2,8 @@ FROM ghcr.io/savoirfairelinux/opendht/opendht-deps-llvm:latest
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
COPY . opendht
RUN cd opendht && mkdir build && cd build \
RUN git clone https://github.com/savoirfairelinux/opendht.git \
&& cd opendht && mkdir build && cd build \
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=On \
-DOPENDHT_C=On \
@@ -13,6 +12,5 @@ RUN cd opendht && mkdir build && cd build \
-DOPENDHT_TOOLS=On \
-DOPENDHT_PROXY_SERVER=On \
-DOPENDHT_PROXY_CLIENT=On \
-DOPENDHT_SYSTEMD=On \
&& make -j8 && make install \
&& cd ../.. && rm -rf opendht
-18
View File
@@ -44,18 +44,6 @@ enum class NodeStatus {
Connecting, // 1+ nodes
Connected // 1+ good nodes
};
struct OPENDHT_PUBLIC DhtNodeStatus {
NodeStatus ipv4;
NodeStatus ipv6;
inline NodeStatus get() const {
if (ipv4 == NodeStatus::Connected or ipv6 == NodeStatus::Connected)
return NodeStatus::Connected;
if (ipv4 == NodeStatus::Connecting or ipv6 == NodeStatus::Connecting)
return NodeStatus::Connecting;
return NodeStatus::Disconnected;
}
};
using StatusCallback = std::function<void(DhtNodeStatus)>;
inline constexpr const char*
statusToStr(NodeStatus status) {
@@ -91,12 +79,9 @@ struct OPENDHT_PUBLIC NodeStats {
struct OPENDHT_PUBLIC NodeInfo {
InfoHash id;
InfoHash node_id;
PkId node_long_id;
NodeStats ipv4 {};
NodeStats ipv6 {};
size_t ongoing_ops {0};
size_t storage_values {0};
size_t storage_size {0};
in_port_t bound4 {0};
in_port_t bound6 {0};
@@ -147,9 +132,6 @@ struct OPENDHT_PUBLIC Config {
/* If non-0, overrides the default maximum store size. -1 means no limit. */
ssize_t max_store_size {0};
/* If non-0, overrides the default maximum store key count. -1 means no limit. */
ssize_t max_store_keys {0};
/**
* Use appropriate bahavior for a public IP, stable node:
* - No connectivity change triggered when a search fails
+15 -40
View File
@@ -32,9 +32,6 @@ extern "C" {
#include <vector>
#include <memory>
#include <atomic>
#include <mutex>
#include <string_view>
#ifdef _WIN32
#include <iso646.h>
@@ -87,13 +84,12 @@ struct OPENDHT_PUBLIC PublicKey
/** Import public key from serialized data */
PublicKey(const uint8_t* dat, size_t dat_size);
PublicKey(const Blob& pk) : PublicKey(pk.data(), pk.size()) {}
PublicKey(std::string_view pk) : PublicKey((const uint8_t*)pk.data(), pk.size()) {}
PublicKey(PublicKey&& o) noexcept : pk(o.pk) { o.pk = nullptr; }
PublicKey(PublicKey&& o) noexcept : pk(o.pk) { o.pk = nullptr; };
~PublicKey();
explicit operator bool() const { return pk; }
bool operator ==(const PublicKey& o) const {
return pk == o.pk || getLongId() == o.getLongId();
return pk == o.pk || getId() == o.getId();
}
bool operator !=(const PublicKey& o) const {
return !(*this == o);
@@ -104,12 +100,12 @@ struct OPENDHT_PUBLIC PublicKey
/**
* Get public key fingerprint
*/
const InfoHash& getId() const;
InfoHash getId() const;
/**
* Get public key long fingerprint
*/
const PkId& getLongId() const;
PkId getLongId() const;
bool checkSignature(const uint8_t* data, size_t data_len, const uint8_t* signature, size_t signature_len) const;
inline bool checkSignature(const Blob& data, const Blob& signature) const {
@@ -120,9 +116,7 @@ struct OPENDHT_PUBLIC PublicKey
inline Blob encrypt(const Blob& data) const {
return encrypt(data.data(), data.size());
}
inline Blob encrypt(std::string_view data) const {
return encrypt((const uint8_t*)data.data(), data.size());
}
void pack(Blob& b) const;
int pack(uint8_t* out, size_t* out_len) const;
void unpack(const uint8_t* dat, size_t dat_size);
@@ -144,11 +138,6 @@ struct OPENDHT_PUBLIC PublicKey
gnutls_pubkey_t pk {nullptr};
private:
mutable InfoHash cachedId_ {};
mutable PkId cachedLongId_ {};
mutable std::atomic_bool idCached_ {false};
mutable std::atomic_bool longIdCached_ {false};
PublicKey(const PublicKey&) = delete;
PublicKey& operator=(const PublicKey&) = delete;
void encryptBloc(const uint8_t* src, size_t src_size, uint8_t* dst, size_t dst_size) const;
@@ -171,9 +160,7 @@ struct OPENDHT_PUBLIC PrivateKey
PrivateKey& operator=(PrivateKey&& o) noexcept;
PrivateKey(const uint8_t* src, size_t src_size, const char* password = nullptr);
PrivateKey(const Blob& src, const std::string& password = {}) : PrivateKey(src.data(), src.size(), password.c_str()) {}
PrivateKey(std::string_view src, const std::string& password = {}) : PrivateKey((const uint8_t*)src.data(), src.size(), password.c_str()) {}
PrivateKey(const Blob& src, const std::string& password = {}) : PrivateKey(src.data(), src.size(), password.data()) {}
~PrivateKey();
explicit operator bool() const { return key; }
@@ -188,7 +175,6 @@ struct OPENDHT_PUBLIC PrivateKey
* @returns the signature data.
*/
Blob sign(const uint8_t* data, size_t data_len) const;
inline Blob sign(std::string_view dat) const { return sign((const uint8_t*)dat.data(), dat.size()); }
inline Blob sign(const Blob& dat) const { return sign(dat.data(), dat.size()); }
/**
@@ -215,7 +201,6 @@ private:
PrivateKey& operator=(const PrivateKey&) = delete;
Blob decryptBloc(const uint8_t* src, size_t src_size) const;
mutable std::mutex publicKeyMutex_ {};
mutable std::shared_ptr<PublicKey> publicKey_ {};
};
@@ -301,7 +286,6 @@ class OPENDHT_PUBLIC CertificateRequest {
public:
CertificateRequest();
CertificateRequest(const uint8_t* data, size_t size);
CertificateRequest(std::string_view src) : CertificateRequest((const uint8_t*)src.data(), src.size()) {}
CertificateRequest(const Blob& data) : CertificateRequest(data.data(), data.size()) {}
CertificateRequest(CertificateRequest&& o) noexcept : request(std::move(o.request)) {
@@ -335,9 +319,8 @@ private:
class OPENDHT_PUBLIC OcspRequest
{
public:
OcspRequest(gnutls_ocsp_req_t r) : request(r) {}
OcspRequest(gnutls_ocsp_req_t r) : request(r) {};
OcspRequest(const uint8_t* dat_ptr, size_t dat_size);
OcspRequest(std::string_view dat): OcspRequest((const uint8_t*)dat.data(), dat.size()) {}
~OcspRequest();
/*
@@ -355,7 +338,7 @@ class OPENDHT_PUBLIC OcspResponse
{
public:
OcspResponse(const uint8_t* dat_ptr, size_t dat_size);
OcspResponse(std::string_view response) : OcspResponse((const uint8_t*)response.data(), response.size()) {}
OcspResponse(const std::string& response) : OcspResponse((const uint8_t*)response.data(), response.size()) {};
~OcspResponse();
Blob pack() const;
@@ -390,21 +373,19 @@ struct OPENDHT_PUBLIC Certificate {
*/
Certificate(gnutls_x509_crt_t crt) noexcept : cert(crt) {}
Certificate(Certificate&& o) noexcept
: cert(o.cert)
, issuer(std::move(o.issuer))
, publicKey_(std::move(o.publicKey_))
{ o.cert = nullptr; };
Certificate(Certificate&& o) noexcept : cert(o.cert), issuer(std::move(o.issuer)) { o.cert = nullptr; };
/**
* Import certificate (PEM or DER) or certificate chain (PEM),
* ordered from subject to issuer
*/
Certificate(const Blob& crt);
Certificate(const std::string& pem) : cert(nullptr) {
unpack((const uint8_t*)pem.data(), pem.size());
}
Certificate(const uint8_t* dat, size_t dat_size) : cert(nullptr) {
unpack(dat, dat_size);
}
Certificate(std::string_view pem) : Certificate((const uint8_t*)pem.data(), pem.size()) {}
/**
* Import certificate chain (PEM or DER),
@@ -502,13 +483,12 @@ struct OPENDHT_PUBLIC Certificate {
void msgpack_unpack(const msgpack::object& o);
explicit operator bool() const { return cert; }
const PublicKey& getPublicKey() const;
const std::shared_ptr<PublicKey>& getSharedPublicKey() const;
PublicKey getPublicKey() const;
/** Same as getPublicKey().getId() */
const InfoHash& getId() const;
InfoHash getId() const;
/** Same as getPublicKey().getLongId() */
const PkId& getLongId() const;
PkId getLongId() const;
Blob getSerialNumber() const;
@@ -626,8 +606,6 @@ private:
Certificate& operator=(const Certificate&) = delete;
mutable InfoHash cachedId_ {};
mutable PkId cachedLongId_ {};
mutable std::atomic_bool idCached_ {false};
mutable std::atomic_bool longIdCached_ {false};
struct crlNumberCmp {
bool operator() (const std::shared_ptr<RevocationList>& lhs, const std::shared_ptr<RevocationList>& rhs) const {
@@ -636,9 +614,6 @@ private:
};
std::set<std::shared_ptr<RevocationList>, crlNumberCmp> revocation_lists;
mutable std::mutex publicKeyMutex_ {};
mutable std::shared_ptr<PublicKey> publicKey_ {};
};
struct OPENDHT_PUBLIC TrustList
+9 -13
View File
@@ -19,8 +19,7 @@
#pragma once
#include "value.h"
#include <asio/ip/udp.hpp>
#include "sockaddr.h"
namespace dht {
enum class ImStatus : uint8_t {
@@ -216,10 +215,9 @@ private:
public:
static const ValueType TYPE;
IpServiceAnnouncement(sa_family_t family = AF_UNSPEC, in_port_t p = 0)
: addr() {
addr.address(family == AF_INET ? asio::ip::address(asio::ip::address_v4::any()) : asio::ip::address(asio::ip::address_v6::any()));
addr.port(p);
IpServiceAnnouncement(sa_family_t family = AF_UNSPEC, in_port_t p = 0) {
addr.setFamily(family);
addr.setPort(p);
}
IpServiceAnnouncement(const SockAddr& sa) : addr(sa) {}
@@ -231,25 +229,23 @@ public:
template <typename Packer>
void msgpack_pack(Packer& pk) const
{
pk.pack_bin(addr.size());
pk.pack_bin_body((const char*)addr.data(), addr.size());
pk.pack_bin(addr.getLength());
pk.pack_bin_body((const char*)addr.get(), addr.getLength());
}
virtual void msgpack_unpack(const msgpack::object& o)
{
//{(sockaddr*)o.via.bin.ptr, (socklen_t)o.via.bin.size};
//auto p =
if (o.type == msgpack::type::BIN)
addr = *reinterpret_cast<const asio::ip::udp::endpoint*>((sockaddr*)o.via.bin.ptr);
addr = {(sockaddr*)o.via.bin.ptr, (socklen_t)o.via.bin.size};
else
throw msgpack::type_error();
}
in_port_t getPort() const {
return addr.port();
return addr.getPort();
}
void setPort(in_port_t p) {
addr.port(p);
addr.setPort(p);
}
const SockAddr& getPeerAddr() const {
+18 -37
View File
@@ -29,8 +29,6 @@
#include "callbacks.h"
#include "dht_interface.h"
#include <asio/strand.hpp>
#include <string>
#include <array>
#include <vector>
@@ -46,7 +44,6 @@ namespace dht {
namespace net {
struct Request;
class UdpSocket;
} /* namespace net */
struct Storage;
@@ -68,7 +65,10 @@ public:
* Initialise the Dht with two open sockets (for IPv4 and IP6)
* and an ID for the node.
*/
Dht(std::shared_ptr<net::strand> strand, std::unique_ptr<net::DatagramSocket>&& sock, const Config& config, const Sp<Logger>& l = {});
Dht(std::unique_ptr<net::DatagramSocket>&& sock, const Config& config, const Sp<Logger>& l = {});
Dht(std::unique_ptr<net::DatagramSocket>&& sock, const Config& config, const Logger& l = {})
: Dht(std::move(sock), config, std::make_shared<Logger>(l)) {}
virtual ~Dht();
@@ -89,14 +89,8 @@ public:
NodeStatus getStatus() const override {
return std::max(getStatus(AF_INET), getStatus(AF_INET6));
}
void addOnConnectedCallback(std::function<void()> cb) {
onConnectCallbacks_.emplace(std::move(cb));
}
void addOnStateChangeCallback(StatusCallback cb) {
onStateChangeCallbacks_.emplace_back(std::move(cb));
}
const net::DatagramSocket* getSocket() const override { return network_engine.getSocket(); };
net::DatagramSocket* getSocket() const override { return network_engine.getSocket(); };
/**
* Performs final operations before quitting.
@@ -134,15 +128,15 @@ public:
*/
void insertNode(const InfoHash& id, const SockAddr&) override;
void insertNode(const NodeExport& n) override {
insertNode(n.id, n.addr);
insertNode(n.id, SockAddr(n.ss, n.sslen));
}
void pingNode(SockAddr, DoneCallbackSimple&& cb={}) override;
/*time_point periodic(const uint8_t *buf, size_t buflen, SockAddr, const time_point& now) override;
time_point periodic(const uint8_t *buf, size_t buflen, SockAddr, const time_point& now) override;
time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen, const time_point& now) override {
return periodic(buf, buflen, SockAddr(from, fromlen), now);
}*/
}
/**
* Get a value by searching on all available protocols (IPv4, IPv6),
@@ -308,9 +302,6 @@ public:
void setStorageLimit(size_t limit = DEFAULT_STORAGE_LIMIT) override {
max_store_size = limit;
}
size_t getStorageLimit() const override {
return max_store_size;
}
/**
* Returns the total memory usage of stored values and the number
@@ -349,7 +340,7 @@ private:
static constexpr unsigned LISTEN_NODES {4};
/* The maximum number of hashes we're willing to track. */
static constexpr unsigned MAX_HASHES {1024 * 1024 * 1024};
static constexpr unsigned MAX_HASHES {1024 * 1024};
/* The maximum number of searches we keep data about. */
static constexpr unsigned MAX_SEARCHES {1024 * 1024};
@@ -385,7 +376,6 @@ private:
uint64_t secret {};
uint64_t oldsecret {};
asio::steady_timer rotateRecretsJob;
// registred types
TypeStore types;
@@ -406,10 +396,10 @@ private:
std::vector<std::pair<std::string,std::string>> bootstrap_nodes {};
std::chrono::steady_clock::duration bootstrap_period {BOOTSTRAP_PERIOD};
asio::steady_timer bootstrapJob;
Sp<Scheduler::Job> bootstrapJob {};
std::map<InfoHash, Storage> store;
std::map<SockAddr, StorageBucket, ipCmp> store_quota;
std::map<SockAddr, StorageBucket, SockAddr::ipCmp> store_quota;
size_t total_values {0};
size_t total_store_size {0};
size_t max_store_keys {MAX_HASHES};
@@ -425,18 +415,15 @@ private:
// timing
asio::steady_timer nextNodesConfirmation;
asio::steady_timer nextStorageMaintenance;
asio::steady_timer expirationJob;
asio::steady_timer statusCheckJob;
Scheduler scheduler;
Sp<Scheduler::Job> nextNodesConfirmation {};
Sp<Scheduler::Job> nextStorageMaintenance {};
net::NetworkEngine network_engine;
using ReportedAddr = std::pair<unsigned, SockAddr>;
std::vector<ReportedAddr> reported_addr;
std::string persistPath;
std::vector<StatusCallback> onStateChangeCallbacks_ {};
std::queue<std::function<void()>> onConnectCallbacks_ {};
// are we a bootstrap node ?
// note: Any running node can be used as a bootstrap node.
@@ -459,14 +446,14 @@ private:
// Storage
void storageAddListener(const InfoHash& id, const Sp<Node>& node, size_t tid, Query&& = {}, int version = 0);
bool storageStore(const InfoHash& id, const Sp<Value>& value, time_point created, const SockAddr* sa = nullptr, bool permanent = false);
bool storageStore(const InfoHash& id, const Sp<Value>& value, time_point created, const SockAddr& sa = {}, bool permanent = false);
bool storageErase(const InfoHash& id, Value::Id vid);
bool storageRefresh(const InfoHash& id, Value::Id vid);
void expireStore();
void expireStorage(InfoHash h);
void expireStore(decltype(store)::iterator);
void storageRemoved(const InfoHash& id, Storage& st, const std::vector<Sp<Value>>& values, size_t totalSize);
void storageChanged(const InfoHash& id, Storage& st, const Sp<Value>&, bool newValue);
void storageChanged(const InfoHash& id, Storage& st, ValueStorage&, bool newValue);
std::string printStorageLog(const decltype(store)::value_type&) const;
/**
@@ -503,8 +490,7 @@ private:
void onNewNode(const Sp<Node>& node, int confirm);
const Sp<Node> findNode(const InfoHash& id, sa_family_t af) const;
bool trySearchInsert(const Sp<Node>& node);
void scheduleStatusCheck();
// Searches
inline SearchMap& searches(sa_family_t af) { return dht(af).searches; }
inline const SearchMap& searches(sa_family_t af) const { return dht(af).searches; }
@@ -514,7 +500,6 @@ private:
* infohash (id), using the specified IP version (IPv4 or IPv6).
*/
Sp<Search> search(const InfoHash& id, sa_family_t af, GetCallback = {}, QueryCallback = {}, DoneCallback = {}, Value::Filter = {}, const Sp<Query>& q = {});
void scheduleNodeConfirmation(const time_point& step);
void announce(const InfoHash& id, sa_family_t af, Sp<Value> value, DoneCallback callback, time_point created=time_point::max(), bool permanent = false);
size_t listenTo(const InfoHash& id, sa_family_t af, ValueCallback cb, Value::Filter f = {}, const Sp<Query>& q = {});
@@ -532,13 +517,9 @@ private:
void confirmNodes();
void expire();
void onStateChanged();
void onConnected();
void onDisconnected();
inline const time_point& time() const { return network_engine.time(); }
inline asio::io_context& context() const { return network_engine.context(); }
/**
* Generic function to execute when a 'get' request has completed.
*
+9 -10
View File
@@ -20,15 +20,13 @@
#include "infohash.h"
#include "log_enable.h"
#include "node_export.h"
#include "callbacks.h"
#include <queue>
namespace dht {
namespace net {
class DatagramSocket;
class DatagramSocket;
}
class OPENDHT_PUBLIC DhtInterface {
@@ -50,10 +48,11 @@ public:
virtual NodeStatus getStatus(sa_family_t af) const = 0;
virtual NodeStatus getStatus() const = 0;
virtual void addOnConnectedCallback(std::function<void()> cb) = 0;
virtual void addOnStateChangeCallback(StatusCallback cb) = 0;
void addOnConnectedCallback(std::function<void()> cb) {
onConnectCallbacks_.emplace(std::move(cb));
}
virtual const net::DatagramSocket* getSocket() const { return {}; };
virtual net::DatagramSocket* getSocket() const { return {}; };
/**
* Get the ID of the DHT node.
@@ -92,6 +91,9 @@ public:
virtual void pingNode(SockAddr, DoneCallbackSimple&& cb={}) = 0;
virtual time_point periodic(const uint8_t *buf, size_t buflen, SockAddr, const time_point& now) = 0;
virtual time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen, const time_point& now) = 0;
/**
* Get a value by searching on all available protocols (IPv4, IPv6),
* and call the provided get callback when values are found at key.
@@ -219,7 +221,6 @@ public:
* Set the in-memory storage limit in bytes
*/
virtual void setStorageLimit(size_t limit = DEFAULT_STORAGE_LIMIT) = 0;
virtual size_t getStorageLimit() const = 0;
/**
* Returns the total memory usage of stored values and the number
@@ -263,9 +264,6 @@ public:
virtual void setPushNotificationToken(const std::string&) {};
virtual void setPushNotificationTopic(const std::string&) {};
virtual void setPushNotificationPlatform(const std::string&) {};
/**
* Call linked callback with a push notification
* @param notification to process
@@ -274,6 +272,7 @@ public:
protected:
std::shared_ptr<Logger> logger_ {};
std::queue<std::function<void()>> onConnectCallbacks_ {};
};
} // namespace dht
+15 -52
View File
@@ -54,7 +54,7 @@ public:
explicit DhtProxyClient(
std::shared_ptr<crypto::Certificate> serverCA, crypto::Identity clientIdentity,
std::shared_ptr<asio::io_context::strand> strand, const std::string& serverHost,
std::function<void()> loopSignal, const std::string& serverHost,
const std::string& pushClientId = "", std::shared_ptr<Logger> logger = {});
void setHeaderFields(http::Request& request);
@@ -67,22 +67,6 @@ public:
#endif
}
virtual void setPushNotificationTopic(const std::string& topic) override {
#ifdef OPENDHT_PUSH_NOTIFICATIONS
notificationTopic_ = topic;
#else
(void) topic;
#endif
}
virtual void setPushNotificationPlatform(const std::string& platform) override {
#ifdef OPENDHT_PUSH_NOTIFICATIONS
platform_ = platform;
#else
(void) platform;
#endif
}
virtual ~DhtProxyClient();
/**
@@ -97,13 +81,6 @@ public:
NodeStatus getStatus() const override {
return std::max(getStatus(AF_INET), getStatus(AF_INET6));
}
void addOnConnectedCallback(std::function<void()> cb) {
onConnectCallbacks_.emplace(std::move(cb));
}
void addOnStateChangeCallback(StatusCallback cb) {
onStateChangeCallbacks_.emplace_back(std::move(cb));
}
void onStateChanged();
/**
* Performs final operations before quitting.
@@ -220,6 +197,11 @@ public:
*/
void pushNotificationReceived(const std::map<std::string, std::string>& notification) override;
time_point periodic(const uint8_t*, size_t, SockAddr, const time_point& now) override;
time_point periodic(const uint8_t* buf, size_t buflen, const sockaddr* from, socklen_t fromlen, const time_point& now) override {
return periodic(buf, buflen, SockAddr(from, fromlen), now);
}
/**
* Similar to Dht::get, but sends a Query to filter data remotely.
* @param key the key for which to query data for.
@@ -281,12 +263,12 @@ public:
void dumpTables() const override {}
std::vector<unsigned> getNodeMessageStats(bool) override { return {}; }
void setStorageLimit(size_t) override {}
virtual size_t getStorageLimit() const override { return 0; }
void connectivityChanged(sa_family_t) override {
getProxyInfos();
}
void connectivityChanged() override {
getProxyInfos();
loopSignal_();
}
private:
@@ -353,13 +335,9 @@ private:
NodeStatus statusIpv6_ {NodeStatus::Disconnected};
NodeStats stats4_ {};
NodeStats stats6_ {};
SockAddr localAddrv4_;
SockAddr localAddrv6_;
SockAddr publicAddressV4_;
SockAddr publicAddressV6_;
std::atomic_bool launchConnectedCbs_ {false};
std::vector<StatusCallback> onStateChangeCallbacks_ {};
std::queue<std::function<void()>> onConnectCallbacks_ {};
InfoHash myid {};
@@ -370,11 +348,7 @@ private:
* ASIO I/O Context for sockets in httpClient_
* Note: Each context is used in one thread only
*/
//asio::io_context httpContext_;
//std::shared_ptr<asio::io_context> httpContext_;
std::shared_ptr<asio::io_context::strand> strand_;
asio::io_context& context() const { return strand_->context(); }
mutable std::mutex resolverLock_;
asio::io_context httpContext_;
std::shared_ptr<http::Resolver> resolver_;
mutable std::mutex requestLock_;
@@ -393,6 +367,12 @@ private:
size_t listenerToken_ {0};
std::map<InfoHash, ProxySearch> searches_;
/**
* Callbacks should be executed in the main thread.
*/
std::mutex lockCallbacks_;
std::vector<std::function<void()>> callbacks_;
Sp<InfoState> infoState_;
/**
@@ -419,24 +399,7 @@ private:
*/
std::string deviceKey_ {};
/**
* Notification topic for ios notifications.
*/
std::string notificationTopic_ {};
/**
* Platform for push notifications (supported android, ios, unifiedpush)
*/
std::string platform_
#ifdef __ANDROID__
{"android"};
#else
#ifdef __APPLE__
{"ios"};
#else
{};
#endif
#endif
const std::function<void()> loopSignal_;
#ifdef OPENDHT_PUSH_NOTIFICATIONS
std::string fillBody(bool resubscribe);
+8 -25
View File
@@ -25,6 +25,7 @@
#include "infohash.h"
#include "proxy.h"
#include "scheduler.h"
#include "sockaddr.h"
#include "value.h"
#include "http.h"
@@ -39,8 +40,7 @@ namespace dht {
enum class PushType {
None = 0,
Android,
iOS,
UnifiedPush
iOS
};
}
MSGPACK_ADD_ENUM(dht::PushType)
@@ -61,13 +61,11 @@ class DhtRunner;
using RestRouter = restinio::router::express_router_t<>;
using RequestStatus = restinio::request_handling_status_t;
struct OPENDHT_PUBLIC ProxyServerConfig {
std::string address {};
struct ProxyServerConfig {
in_port_t port {8000};
std::string pushServer {};
std::string persistStatePath {};
dht::crypto::Identity identity {};
std::string bundleId {};
};
/**
@@ -88,7 +86,6 @@ public:
const ProxyServerConfig& config = {},
const std::shared_ptr<dht::Logger>& logger = {});
void stop();
virtual ~DhtProxyServer();
DhtProxyServer(const DhtProxyServer& other) = delete;
@@ -276,11 +273,6 @@ private:
restinio::router::route_params_t params);
#ifdef OPENDHT_PUSH_NOTIFICATIONS
PushType getTypeFromString(const std::string& type);
std::string getDefaultTopic(PushType type);
RequestStatus pingPush(restinio::request_handle_t request,
restinio::router::route_params_t /*params*/);
/**
* Subscribe to push notifications for an iOS or Android device.
* Method: SUBSCRIBE "/{InfoHash: .*}"
@@ -308,7 +300,7 @@ private:
* @param key of the device
* @param json, the content to send
*/
void sendPushNotification(const std::string& key, Json::Value&& json, PushType type, bool highPriority, const std::string& topic);
void sendPushNotification(const std::string& key, Json::Value&& json, PushType type, bool highPriority);
/**
* Send push notification with an expire timeout.
@@ -316,10 +308,9 @@ private:
* @param pushToken
* @param json
* @param type
* @param topic
*/
void handleNotifyPushListenExpire(const asio::error_code &ec, const std::string pushToken,
std::function<Json::Value()> json, PushType type, const std::string& topic);
std::function<Json::Value()> json, PushType type);
/**
* Remove a push listener between a client and a hash
@@ -391,12 +382,11 @@ private:
std::unique_ptr<asio::steady_timer> expireNotifyTimer;
Sp<Value> value;
PushType type;
std::string topic;
template <typename Packer>
void msgpack_pack(Packer& p) const
{
p.pack_map(2 + (sessionCtx ? 1 : 0) + (clientId.empty() ? 0 : 1) + (type == PushType::None ? 0 : 2) + (topic.empty() ? 0 : 1));
p.pack_map(2 + (sessionCtx ? 1 : 0) + (clientId.empty() ? 0 : 1) + (type == PushType::None ? 0 : 2));
p.pack("value"); p.pack(value);
p.pack("exp"); p.pack(to_time_t(expiration));
if (not clientId.empty()) {
@@ -410,9 +400,6 @@ private:
p.pack("t"); p.pack(type);
p.pack("token"); p.pack(pushToken);
}
if (not topic.empty()) {
p.pack("top"); p.pack(topic);
}
}
void msgpack_unpack(const msgpack::object& o);
@@ -428,7 +415,6 @@ private:
mutable std::atomic<time_point> lastStatsReset_ {time_point::min()};
std::string pushServer_;
std::string bundleId_;
#ifdef OPENDHT_PUSH_NOTIFICATIONS
struct Listener {
@@ -439,12 +425,11 @@ private:
std::unique_ptr<asio::steady_timer> expireTimer;
std::unique_ptr<asio::steady_timer> expireNotifyTimer;
PushType type;
std::string topic;
template <typename Packer>
void msgpack_pack(Packer& p) const
{
p.pack_map(3 + (sessionCtx ? 1 : 0) + (topic.empty() ? 0 : 1));
p.pack_map(sessionCtx ? 4 : 3);
p.pack("cid"); p.pack(clientId);
p.pack("exp"); p.pack(to_time_t(expiration));
if (sessionCtx) {
@@ -452,9 +437,6 @@ private:
p.pack("sid"); p.pack(sessionCtx->sessionId);
}
p.pack("t"); p.pack(type);
if (!topic.empty()) {
p.pack("top"); p.pack(topic);
}
}
void msgpack_unpack(const msgpack::object& o);
@@ -464,6 +446,7 @@ private:
MSGPACK_DEFINE_ARRAY(listeners)
};
std::map<std::string, PushListener> pushListeners_;
proxy::ListenToken tokenPushNotif_ {0};
#endif //OPENDHT_PUSH_NOTIFICATIONS
};
+53 -31
View File
@@ -24,10 +24,9 @@
#include "infohash.h"
#include "value.h"
#include "callbacks.h"
#include "sockaddr.h"
#include "log_enable.h"
#include "node_export.h"
#include "network_utils.h"
#include "udp_socket.h"
#include <thread>
#include <mutex>
@@ -52,15 +51,16 @@ struct SecureDhtConfig;
* thread that will update the DHT when appropriate.
*/
class OPENDHT_PUBLIC DhtRunner {
public:
using StatusCallback = std::function<void(NodeStatus, NodeStatus)>;
struct Config {
SecureDhtConfig dht_config {};
bool threaded {true};
std::string proxy_server {};
std::string push_node_id {};
std::string push_token {};
std::string push_topic {};
std::string push_platform {};
bool peer_discovery {false};
bool peer_publish {false};
std::shared_ptr<dht::crypto::Certificate> server_ca;
@@ -71,7 +71,6 @@ public:
struct Context {
std::shared_ptr<Logger> logger {};
std::unique_ptr<net::DatagramSocket> sock;
std::shared_ptr<asio::io_context> ioContext {};
std::shared_ptr<PeerDiscovery> peerDiscovery {};
StatusCallback statusChangedCallback {};
CertificateStoreQuery certificateStore {};
@@ -274,13 +273,13 @@ public:
* Usefull to restart a node and get things running fast without putting load on the network.
*/
void bootstrap(std::vector<SockAddr> nodes, DoneCallbackSimple cb={});
void bootstrap(SockAddr addr, DoneCallbackSimple cb={});
void bootstrap(const SockAddr& addr, DoneCallbackSimple cb={});
/**
* Insert known nodes to the routing table, without necessarly ping them.
* Usefull to restart a node and get things running fast without putting load on the network.
*/
void bootstrap(std::vector<NodeExport> nodes);
void bootstrap(const std::vector<NodeExport>& nodes);
/**
* Add host:service to bootstrap nodes, and ping this node.
@@ -336,7 +335,6 @@ public:
std::pair<size_t, size_t> getStoreSize() const;
void getStorageLimit() const;
void setStorageLimit(size_t limit = DEFAULT_STORAGE_LIMIT);
std::vector<NodeExport> exportNodes() const;
@@ -373,8 +371,8 @@ public:
std::string getRoutingTablesLog(sa_family_t af) const;
std::string getSearchesLog(sa_family_t af = AF_UNSPEC) const;
std::string getSearchLog(const InfoHash&, sa_family_t af = AF_UNSPEC) const;
std::vector<SockAddr> getPublicAddress(sa_family_t af = AF_UNSPEC) const;
std::vector<std::string> getPublicAddressStr(sa_family_t af = AF_UNSPEC) const;
std::vector<SockAddr> getPublicAddress(sa_family_t af = AF_UNSPEC);
std::vector<std::string> getPublicAddressStr(sa_family_t af = AF_UNSPEC);
void getPublicAddress(std::function<void(std::vector<SockAddr>&&)>, sa_family_t af = AF_UNSPEC);
// securedht methods
@@ -405,6 +403,20 @@ public:
void run(const Config& config, Context&& context);
void setOnStatusChanged(StatusCallback&& cb) {
statusCb = std::move(cb);
}
/**
* In non-threaded mode, the user should call this method
* regularly and everytime a new packet is received.
* @return the next op
*/
time_point loop() {
std::lock_guard<std::mutex> lck(dht_mtx);
return loop_();
}
/**
* Gracefuly disconnect from network.
*/
@@ -424,6 +436,7 @@ public:
/**
* Start or stop the proxy
* @param proxify if we want to use the proxy
* @param deviceKey non empty to enable push notifications
*/
void enableProxy(bool proxify);
@@ -434,16 +447,6 @@ public:
*/
void setPushNotificationToken(const std::string& token);
/**
* Sets the push notification topic
*/
void setPushNotificationTopic(const std::string& topic);
/**
* Sets the push notification platform
*/
void setPushNotificationPlatform(const std::string& platform);
/**
* Insert a push notification to process for OpenDHT
*/
@@ -459,23 +462,23 @@ private:
Stopping
};
time_point loop_();
NodeStatus getStatus() const {
return std::max(status4, status6);
}
bool checkShutdown();
void opEnded();
DoneCallback bindOpDoneCallback(DoneCallback&& cb);
DoneCallbackSimple bindOpDoneCallback(DoneCallbackSimple&& cb);
inline void post(std::function<void()>&& op, bool prio = false) {
ioContext_->post(asio::bind_executor(*strand_, std::move(op)));
}
void postOp(std::function<void(SecureDht&)>&& op, bool prio = false);
std::shared_ptr<asio::io_context> ioContext_;
std::shared_ptr<asio::io_context::strand> strand_;
/** DHT instance */
/** Local DHT instance */
std::unique_ptr<SecureDht> dht_;
/** Proxy client instance */
std::unique_ptr<SecureDht> dht_via_proxy_;
/** true if we are currently using a proxy */
std::atomic_bool use_proxy {false};
@@ -487,11 +490,26 @@ private:
* reset dht clients
*/
void resetDht();
/**
* @return the current active DHT
*/
SecureDht* activeDht() const;
/**
* Store current listeners and translates global tokens for each client.
*/
struct Listener;
std::map<size_t, Listener> listeners_;
size_t listener_token_ {1};
mutable std::mutex dht_mtx {};
std::thread dht_thread {};
std::condition_variable cv {};
/*std::queue<std::function<void(SecureDht&)>> pending_ops_prio {};*/
std::mutex sock_mtx {};
net::PacketList rcv {};
decltype(rcv) rcv_free {};
std::queue<std::function<void(SecureDht&)>> pending_ops_prio {};
std::queue<std::function<void(SecureDht&)>> pending_ops {};
std::mutex storage_mtx {};
@@ -499,6 +517,10 @@ private:
std::atomic_size_t ongoing_ops {0};
std::vector<ShutdownCallback> shutdownCallbacks_;
NodeStatus status4 {NodeStatus::Disconnected},
status6 {NodeStatus::Disconnected};
StatusCallback statusCb {nullptr};
/** PeerDiscovery Parameters */
std::shared_ptr<PeerDiscovery> peerDiscovery_;
+2 -15
View File
@@ -114,16 +114,13 @@ public:
void async_read(size_t bytes, BytesHandlerCb cb);
void async_read_some(size_t bytes, BytesHandlerCb cb);
const asio::ip::address& local_address() const;
void timeout(const std::chrono::seconds& timeout, HandlerCb cb = {});
void timeout(const std::chrono::seconds timeout, HandlerCb cb = {});
void close();
private:
template<typename T>
T wrapCallback(T cb) const {
T wrapCallabck(T cb) const {
return [t=shared_from_this(),cb=std::move(cb)](auto ...params) {
cb(params...);
};
@@ -145,8 +142,6 @@ private:
asio::streambuf read_buf_;
std::istream istream_;
asio::ip::address local_address_;
std::unique_ptr<asio::steady_timer> timeout_timer_;
std::shared_ptr<dht::Logger> logger_;
bool checkOcsp_ {false};
@@ -266,11 +261,6 @@ public:
return resolver_->get_url();
};
void timeout(const std::chrono::seconds& timeout, HandlerCb cb = {}) {
timeout_ = timeout;
timeoutCb_ = cb;
}
/** The previous request in case of redirect following */
std::shared_ptr<Request> getPrevious() const {
return prev_.lock();
@@ -380,9 +370,6 @@ private:
std::weak_ptr<Request> prev_;
unsigned num_redirect {0};
bool follow_redirect {true};
HandlerCb timeoutCb_ {};
std::chrono::seconds timeout_ {0};
};
} // namespace http
+41 -11
View File
@@ -22,7 +22,19 @@
#include "rng.h"
#include <msgpack.hpp>
#include <asio/ip/udp.hpp>
#ifndef _WIN32
#include <netinet/in.h>
#include <netdb.h>
#ifdef __ANDROID__
typedef uint16_t in_port_t;
#endif
#else
#include <iso646.h>
#include <ws2tcpip.h>
typedef uint16_t sa_family_t;
typedef uint16_t in_port_t;
#endif
#include <iostream>
#include <iomanip>
@@ -32,13 +44,11 @@
#include <algorithm>
#include <stdexcept>
#include <sstream>
#include <cstring>
#include <cstddef>
namespace dht {
using byte = uint8_t;
namespace crypto {
@@ -93,7 +103,13 @@ public:
static constexpr inline Hash zero() noexcept { return Hash{}; }
bool operator==(const Hash& h) const {
return data_ == h.data_;
auto a = reinterpret_cast<const uint32_t*>(data_.data());
auto b = reinterpret_cast<const uint32_t*>(h.data_.data());
constexpr unsigned n = N / sizeof(uint32_t);
for (unsigned i=0; i < n; i++)
if (a[i] != b[i])
return false;
return true;
}
bool operator!=(const Hash& h) const { return !(*this == h); }
@@ -218,11 +234,6 @@ public:
return get(data.data(), data.size());
}
template <size_t H>
static Hash get(const Hash<H>& o) {
return get(o.data(), o.size());
}
/**
* Computes the hash from a given data buffer of size data_len.
*/
@@ -244,8 +255,6 @@ public:
template <size_t M>
OPENDHT_PUBLIC friend std::istream& operator>> (std::istream& s, Hash<M>& h);
/** Returns view to thread-allocated memory, only valid until the next call to this function. */
std::string_view to_view() const { return std::string_view(to_c_str(), N*2); }
const char* to_c_str() const;
std::string toString() const;
@@ -382,4 +391,25 @@ Hash<N>::toString() const
return std::string(to_c_str(), N*2);
}
struct OPENDHT_PUBLIC NodeExport {
InfoHash id;
sockaddr_storage ss;
socklen_t sslen;
template <typename Packer>
void msgpack_pack(Packer& pk) const
{
pk.pack_map(2);
pk.pack(std::string("id"));
pk.pack(id);
pk.pack(std::string("addr"));
pk.pack_bin(sslen);
pk.pack_bin_body((char*)&ss, sslen);
}
void msgpack_unpack(msgpack::object o);
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const NodeExport& h);
};
}
+29 -47
View File
@@ -19,19 +19,16 @@
#pragma once
#include "network_utils.h"
#include "node_cache.h"
#include "value.h"
#include "infohash.h"
#include "node.h"
#include "scheduler.h"
#include "utils.h"
#include "rng.h"
#include "rate_limiter.h"
#include "log_enable.h"
#include "udp_socket.h"
#include <asio/strand.hpp>
#include <asio/ip/udp.hpp>
#include "network_utils.h"
#include <vector>
#include <string>
@@ -47,6 +44,10 @@ struct Request;
struct Socket;
struct TransId;
#ifndef MSG_CONFIRM
#define MSG_CONFIRM 0
#endif
struct NetworkConfig {
NetId network {0};
ssize_t max_req_per_sec {0};
@@ -216,10 +217,10 @@ public:
NetworkEngine(
InfoHash& myid,
NetworkConfig config,
const Sp<strand>& strand,
std::unique_ptr<DatagramSocket>&& sock,
const Sp<Logger>& log,
std::mt19937_64& rd,
Scheduler& scheduler,
decltype(NetworkEngine::onError)&& onError,
decltype(NetworkEngine::onNewNode)&& onNewNode,
decltype(NetworkEngine::onReportedAddr)&& onReportedAddr,
@@ -232,9 +233,7 @@ public:
~NetworkEngine();
asio::io_context& context() const { return strand_->context(); }
const net::DatagramSocket* getSocket() const { return dht_socket.get(); };
net::DatagramSocket* getSocket() const { return dht_socket.get(); };
void clear();
@@ -253,12 +252,12 @@ public:
* @param values The values to send.
* @param version If version = 1, a request will be used to answer to the listener
*/
void tellListener(const Sp<Node>& n, Tid socket_id, const InfoHash& hash, want_t want, const Blob& ntoken,
void tellListener(Sp<Node> n, Tid socket_id, const InfoHash& hash, want_t want, const Blob& ntoken,
std::vector<Sp<Node>>&& nodes, std::vector<Sp<Node>>&& nodes6,
std::vector<Sp<Value>>&& values, const Query& q, int version);
void tellListenerRefreshed(const Sp<Node>& n, Tid socket_id, const InfoHash& hash, const Blob& ntoken, const std::vector<Value::Id>& values, int version);
void tellListenerExpired(const Sp<Node>& n, Tid socket_id, const InfoHash& hash, const Blob& ntoken, const std::vector<Value::Id>& values, int version);
void tellListenerRefreshed(Sp<Node> n, Tid socket_id, const InfoHash& hash, const Blob& ntoken, const std::vector<Value::Id>& values, int version);
void tellListenerExpired(Sp<Node> n, Tid socket_id, const InfoHash& hash, const Blob& ntoken, const std::vector<Value::Id>& values, int version);
bool isRunning(sa_family_t af) const;
inline want_t want () const { return dht_socket->hasIPv4() and dht_socket->hasIPv6() ? (WANT4 | WANT6) : -1; }
@@ -279,7 +278,7 @@ public:
* @return the request with information concerning its success.
*/
Sp<Request>
sendPing(const Sp<Node>& n, RequestCb&& on_done, RequestExpiredCb&& on_expired);
sendPing(Sp<Node> n, RequestCb&& on_done, RequestExpiredCb&& on_expired);
/**
* Send a "ping" request to a given node.
@@ -310,7 +309,7 @@ public:
*
* @return the request with information concerning its success.
*/
Sp<Request> sendFindNode(const Sp<Node>& n,
Sp<Request> sendFindNode(Sp<Node> n,
const InfoHash& hash,
want_t want = -1,
RequestCb&& on_done = {},
@@ -329,7 +328,7 @@ public:
*
* @return the request with information concerning its success.
*/
Sp<Request> sendGetValues(const Sp<Node>& n,
Sp<Request> sendGetValues(Sp<Node> n,
const InfoHash& hash,
const Query& query,
want_t want,
@@ -358,7 +357,7 @@ public:
*
* @return the request with information concerning its success.
*/
Sp<Request> sendListen(const Sp<Node>& n,
Sp<Request> sendListen(Sp<Node> n,
const InfoHash& hash,
const Query& query,
const Blob& token,
@@ -378,7 +377,7 @@ public:
*
* @return the request with information concerning its success.
*/
Sp<Request> sendAnnounceValue(const Sp<Node>& n,
Sp<Request> sendAnnounceValue(Sp<Node> n,
const InfoHash& hash,
const Sp<Value>& v,
time_point created,
@@ -398,7 +397,7 @@ public:
*
* @return the request with information concerning its success.
*/
Sp<Request> sendRefreshValue(const Sp<Node>& n,
Sp<Request> sendRefreshValue(Sp<Node> n,
const InfoHash& hash,
const Value::Id& vid,
const Blob& token,
@@ -417,19 +416,12 @@ public:
*
* @return the request with information concerning its success.
*/
void sendUpdateValues(const Sp<Node>& n,
Sp<Request> sendUpdateValues(Sp<Node> n,
const InfoHash& infohash,
std::vector<Sp<Value>>&& values,
const std::vector<Sp<Value>>& values,
time_point created,
const Blob& token,
size_t sid);
Sp<Request> sendUpdateValues(const Sp<Node>& n,
const InfoHash& infohash,
std::vector<Sp<Value>>::iterator begin,
std::vector<Sp<Value>>::iterator end,
time_point created,
const Blob& token,
size_t sid);
const size_t& sid);
/**
* Parses a message and calls appropriate callbacks.
@@ -442,8 +434,8 @@ public:
*/
void processMessage(const uint8_t *buf, size_t buflen, SockAddr addr);
Sp<Node> insertNode(const InfoHash& id, const SockAddr& addr) {
auto n = cache.getNode(id, addr, time(), 0);
Sp<Node> insertNode(const InfoHash& id, const SockAddr& addr) {
auto n = cache.getNode(id, addr, scheduler.time(), 0);
onNewNode(n, 0);
return n;
}
@@ -475,12 +467,6 @@ public:
size_t getPartialCount() const {
return partial_messages.size();
}
/**
* Accessors for the common time reference used for synchronizing
* operations.
*/
inline const time_point& time() const { return now; }
inline const time_point& syncTime() { return (now = clock::now()); }
private:
@@ -506,7 +492,6 @@ private:
static constexpr size_t MTU {1280};
static constexpr size_t MAX_PACKET_VALUE_SIZE {600};
static constexpr size_t MAX_MESSAGE_VALUE_SIZE {16 * MTU};
static const std::string my_v;
@@ -537,13 +522,10 @@ private:
// basic wrapper for socket sendto function
asio::error_code send(const SockAddr& addr, const char *buf, size_t len, bool confirmed = false);
int send(const SockAddr& addr, const char *buf, size_t len, bool confirmed = false);
void sendValueParts(Tid tid, const std::vector<Blob>& svals, const SockAddr& addr);
std::vector<Blob> packValueHeader(msgpack::sbuffer&, std::vector<Sp<Value>>::const_iterator, std::vector<Sp<Value>>::const_iterator) const;
std::vector<Blob> packValueHeader(msgpack::sbuffer& buf, const std::vector<Sp<Value>>& values) const {
return packValueHeader(buf, values.begin(), values.end());
}
std::vector<Blob> packValueHeader(msgpack::sbuffer&, const std::vector<Sp<Value>>&);
void maintainRxBuffer(Tid tid);
/*************
@@ -580,10 +562,9 @@ private:
void deserializeNodes(ParsedMessage& msg, const SockAddr& from);
/* DHT info */
Sp<strand> strand_;
const InfoHash& myid;
const NetworkConfig config {};
std::unique_ptr<DatagramSocket> dht_socket;
const std::unique_ptr<DatagramSocket> dht_socket;
Sp<Logger> logger_;
std::mt19937_64& rd;
@@ -591,7 +572,7 @@ private:
// global limiting should be triggered by at least 8 different IPs
using IpLimiter = RateLimiter;
using IpLimiterMap = std::map<SockAddr, IpLimiter, ipCmp>;
using IpLimiterMap = std::map<SockAddr, IpLimiter, SockAddr::ipCmp>;
IpLimiterMap address_rate_limiter;
RateLimiter rate_limiter;
ssize_t limiter_maintenance {0};
@@ -602,9 +583,10 @@ private:
MessageStats in_stats {}, out_stats {};
std::set<SockAddr> blacklist {};
bool logIncoming_ {false};
time_point now {clock::now()};
Scheduler& scheduler;
bool logIncoming_ {false};
};
} /* namespace net */
+130 -8
View File
@@ -19,21 +19,143 @@
#include "def.h"
#include <asio.hpp>
#include <chrono>
#include <cstdint>
#include "sockaddr.h"
#include "utils.h"
#include "log_enable.h"
#ifdef _WIN32
#include <ws2tcpip.h>
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#endif
#include <functional>
#include <thread>
#include <atomic>
#include <mutex>
#include <list>
#include <memory>
#include <vector>
namespace dht {
namespace net {
using asio::ip::udp;
//using time_point = std::chrono::high_resolution_clock::time_point;
static const constexpr in_port_t DHT_DEFAULT_PORT = 4222;
static const constexpr size_t RX_QUEUE_MAX_SIZE = 1024 * 16;
static const constexpr std::chrono::milliseconds RX_QUEUE_MAX_DELAY(650);
int bindSocket(const SockAddr& addr, SockAddr& bound);
bool setNonblocking(int fd, bool nonblocking = true);
#ifdef _WIN32
void udpPipe(int fds[2]);
#endif
struct ReceivedPacket {
Blob data;
SockAddr from;
time_point received;
};
using PacketList = std::list<ReceivedPacket>;
class OPENDHT_PUBLIC DatagramSocket {
public:
/** A function that takes a list of new received packets and
* optionally returns consumed packets for recycling.
**/
using OnReceive = std::function<PacketList(PacketList&& packets)>;
virtual ~DatagramSocket() {};
virtual int sendTo(const SockAddr& dest, const uint8_t* data, size_t size, bool replied) = 0;
inline void setOnReceive(OnReceive&& cb) {
std::lock_guard<std::mutex> lk(lock);
rx_callback = std::move(cb);
}
virtual bool hasIPv4() const = 0;
virtual bool hasIPv6() const = 0;
SockAddr getBound(sa_family_t family = AF_UNSPEC) const {
std::lock_guard<std::mutex> lk(lock);
return getBoundRef(family);
}
in_port_t getPort(sa_family_t family = AF_UNSPEC) const {
std::lock_guard<std::mutex> lk(lock);
return getBoundRef(family).getPort();
}
virtual const SockAddr& getBoundRef(sa_family_t family = AF_UNSPEC) const = 0;
/** Virtual resolver mothod allows to implement custom resolver */
virtual std::vector<SockAddr> resolve(const std::string& host, const std::string& service = {}) {
return SockAddr::resolve(host, service);
}
virtual void stop() = 0;
protected:
PacketList getNewPacket() {
PacketList pkts;
if (toRecycle_.empty()) {
pkts.emplace_back();
} else {
auto begIt = toRecycle_.begin();
auto begItNext = std::next(begIt);
pkts.splice(pkts.end(), toRecycle_, begIt, begItNext);
}
return pkts;
}
inline void onReceived(PacketList&& packets) {
std::lock_guard<std::mutex> lk(lock);
if (rx_callback) {
auto r = rx_callback(std::move(packets));
if (not r.empty() and toRecycle_.size() < RX_QUEUE_MAX_SIZE)
toRecycle_.splice(toRecycle_.end(), std::move(r));
}
}
protected:
mutable std::mutex lock;
private:
OnReceive rx_callback;
PacketList toRecycle_;
};
class OPENDHT_PUBLIC UdpSocket : public DatagramSocket {
public:
UdpSocket(in_port_t port, const std::shared_ptr<Logger>& l = {});
UdpSocket(const SockAddr& bind4, const SockAddr& bind6, const std::shared_ptr<Logger>& l = {});
~UdpSocket();
int sendTo(const SockAddr& dest, const uint8_t* data, size_t size, bool replied) override;
const SockAddr& getBoundRef(sa_family_t family = AF_UNSPEC) const override {
return (family == AF_INET6) ? bound6 : bound4;
}
bool hasIPv4() const override {
std::lock_guard<std::mutex> lk(lock);
return s4 != -1;
}
bool hasIPv6() const override {
std::lock_guard<std::mutex> lk(lock);
return s6 != -1;
}
void stop() override;
private:
std::shared_ptr<Logger> logger;
int s4 {-1};
int s6 {-1};
int stopfd {-1};
SockAddr bound4, bound6;
std::thread rcv_thread {};
std::atomic_bool running {false};
void openSockets(const SockAddr& bind4, const SockAddr& bind6);
};
}
}
+10 -6
View File
@@ -21,7 +21,7 @@
#include "infohash.h" // includes socket structures
#include "utils.h"
#include "node_export.h"
#include "sockaddr.h"
#include <list>
#include <map>
@@ -49,13 +49,16 @@ struct Node {
Node(const InfoHash& id, const SockAddr& addr, std::mt19937_64& rd, bool client=false);
Node(const InfoHash& id, SockAddr&& addr, std::mt19937_64& rd, bool client=false);
//Node(const InfoHash& id, const sockaddr* sa, socklen_t salen, std::mt19937_64& rd)
// : Node(id, SockAddr(sa, salen), rd) {}
Node(const InfoHash& id, const sockaddr* sa, socklen_t salen, std::mt19937_64& rd)
: Node(id, SockAddr(sa, salen), rd) {}
const InfoHash& getId() const {
InfoHash getId() const {
return id;
}
const SockAddr& getAddr() const { return addr; }
std::string getAddrStr() const {
return addr.toString();
}
bool isClient() const { return is_client; }
bool isIncoming() { return time > reply_time; }
@@ -89,10 +92,11 @@ struct Node {
NodeExport exportNode() const {
NodeExport ne;
ne.id = id;
ne.addr = addr;
ne.sslen = addr.getLength();
std::memcpy(&ne.ss, addr.get(), ne.sslen);
return ne;
}
sa_family_t getFamily() const { return addr.protocol().family(); }
sa_family_t getFamily() const { return addr.getFamily(); }
void update(const SockAddr&);
-50
View File
@@ -1,50 +0,0 @@
/*
* Copyright (C) 2014-2023 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/>.
*/
#pragma once
#include "def.h"
#include "infohash.h"
#include <asio/ip/udp.hpp>
#include <string_view>
#include <string_view>
namespace dht {
using namespace std::literals;
struct OPENDHT_PUBLIC NodeExport {
InfoHash id;
asio::ip::udp::endpoint addr;
template <typename Packer>
void msgpack_pack(Packer& pk) const
{
pk.pack_map(2);
pk.pack("id"sv);
pk.pack(id);
pk.pack("addr"sv);
pk.pack_bin(addr.size());
pk.pack_bin_body((const char*)addr.data(), addr.size());
}
void msgpack_unpack(msgpack::object o);
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const NodeExport& h);
};
}
+2 -2
View File
@@ -20,8 +20,8 @@
#pragma once
#include "def.h"
#include "sockaddr.h"
#include "infohash.h"
#include "utils.h"
#include "log_enable.h"
#include <thread>
@@ -36,7 +36,7 @@ class OPENDHT_PUBLIC PeerDiscovery
{
public:
static constexpr in_port_t DEFAULT_PORT = 8888;
using ServiceDiscoveredCallback = std::function<void(msgpack::object&&, asio::ip::udp::endpoint)>;
using ServiceDiscoveredCallback = std::function<void(msgpack::object&&, SockAddr&&)>;
PeerDiscovery(in_port_t port = DEFAULT_PORT, std::shared_ptr<asio::io_context> ioContext = {}, std::shared_ptr<Logger> logger = {});
~PeerDiscovery();
+8 -12
View File
@@ -173,10 +173,6 @@ public:
void setStorageLimit(size_t limit = DEFAULT_STORAGE_LIMIT) override {
dht_->setStorageLimit(limit);
}
size_t getStorageLimit() const override {
return dht_->getStorageLimit();
}
std::vector<NodeExport> exportNodes() const override {
return dht_->exportNodes();
}
@@ -204,6 +200,12 @@ public:
std::vector<SockAddr> getPublicAddress(sa_family_t family = 0) override {
return dht_->getPublicAddress(family);
}
time_point periodic(const uint8_t *buf, size_t buflen, SockAddr sa, const time_point& now) override {
return dht_->periodic(buf, buflen, std::move(sa), now);
}
time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen, const time_point& now) override {
return dht_->periodic(buf, buflen, from, fromlen, now);
}
NodeStatus updateStatus(sa_family_t af) override {
return dht_->updateStatus(af);
}
@@ -213,13 +215,7 @@ public:
NodeStatus getStatus() const override {
return dht_->getStatus();
}
void addOnConnectedCallback(std::function<void()> cb) override {
dht_->addOnConnectedCallback(std::move(cb));
}
void addOnStateChangeCallback(StatusCallback cb) override {
dht_->addOnStateChangeCallback(std::move(cb));
}
const net::DatagramSocket* getSocket() const override {
net::DatagramSocket* getSocket() const override {
return dht_->getSocket();
};
bool isRunning(sa_family_t af = 0) const override {
@@ -378,7 +374,7 @@ const ValueType CERTIFICATE_TYPE = {
try {
crypto::Certificate crt(v->data);
// TODO check certificate signature
return crt.getPublicKey().getId() == id || InfoHash::get(crt.getPublicKey().getLongId()) == id;
return crt.getPublicKey().getId() == id;
} catch (const std::exception& e) {}
return false;
},
+302
View File
@@ -0,0 +1,302 @@
/*
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* 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/>.
*/
#pragma once
#include "def.h"
#ifndef _WIN32
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifdef __ANDROID__
typedef uint16_t in_port_t;
#endif
#else
#include <iso646.h>
#include <stdint.h>
#include <winsock2.h>
#include <ws2def.h>
#include <ws2tcpip.h>
typedef uint16_t sa_family_t;
typedef uint16_t in_port_t;
#endif
#include <string>
#include <memory>
#include <vector>
#include <stdexcept>
#include <stdlib.h>
#include <cstring>
#include <cstddef>
namespace dht {
OPENDHT_PUBLIC std::string print_addr(const sockaddr* sa, socklen_t slen);
OPENDHT_PUBLIC std::string print_addr(const sockaddr_storage& ss, socklen_t sslen);
/**
* A Socket Address (sockaddr*), with abstraction for IPv4, IPv6 address families.
*/
class OPENDHT_PUBLIC SockAddr {
public:
SockAddr() {}
SockAddr(const SockAddr& o) {
set(o.get(), o.getLength());
}
SockAddr(SockAddr&& o) noexcept : addr(std::move(o.addr)), len(o.len) {
o.len = 0;
}
/**
* Build from existing address.
*/
SockAddr(const sockaddr* sa, socklen_t length) {
if (length > static_cast<socklen_t>(sizeof(sockaddr_storage)))
throw std::runtime_error("Socket address length is too large");
set(sa, length);
}
SockAddr(const sockaddr* sa) {
socklen_t len = 0;
if (sa) {
if (sa->sa_family == AF_INET)
len = sizeof(sockaddr_in);
else if(sa->sa_family == AF_INET6)
len = sizeof(sockaddr_in6);
else
throw std::runtime_error("Unknown address family");
}
set(sa, len);
}
/**
* Build from an existing sockaddr_storage structure.
*/
SockAddr(const sockaddr_storage& ss, socklen_t len) : SockAddr((const sockaddr*)&ss, len) {}
static std::vector<SockAddr> resolve(const std::string& host, const std::string& service = {});
bool operator<(const SockAddr& o) const {
if (len != o.len)
return len < o.len;
return std::memcmp((const uint8_t*)get(), (const uint8_t*)o.get(), len) < 0;
}
bool equals(const SockAddr& o) const {
return len == o.len
&& std::memcmp((const uint8_t*)get(), (const uint8_t*)o.get(), len) == 0;
}
SockAddr& operator=(const SockAddr& o) {
set(o.get(), o.getLength());
return *this;
}
SockAddr& operator=(SockAddr&& o) {
len = o.len;
o.len = 0;
addr = std::move(o.addr);
return *this;
}
std::string toString() const {
return print_addr(get(), getLength());
}
/**
* Returns the address family or AF_UNSPEC if the address is not set.
*/
sa_family_t getFamily() const { return len ? addr->sa_family : AF_UNSPEC; }
/**
* Resize the managed structure to the appropriate size (if needed),
* in which case the sockaddr structure is cleared to zero,
* and set the address family field (sa_family).
*/
void setFamily(sa_family_t af) {
socklen_t new_length;
switch(af) {
case AF_INET:
new_length = sizeof(sockaddr_in);
break;
case AF_INET6:
new_length = sizeof(sockaddr_in6);
break;
default:
new_length = 0;
}
if (new_length != len) {
len = new_length;
if (len) addr.reset((sockaddr*)::calloc(len, 1));
else addr.reset();
}
if (len)
addr->sa_family = af;
}
/**
* Set Network Interface to any
*/
void setAny() {
auto family = getFamily();
switch(family) {
case AF_INET:
getIPv4().sin_addr.s_addr = htonl(INADDR_ANY);
break;
case AF_INET6:
getIPv6().sin6_addr = in6addr_any;
break;
}
}
/**
* Retreive the port (in host byte order) or 0 if the address is not
* of a supported family.
*/
in_port_t getPort() const {
switch(getFamily()) {
case AF_INET:
return ntohs(getIPv4().sin_port);
case AF_INET6:
return ntohs(getIPv6().sin6_port);
default:
return 0;
}
}
/**
* Set the port. The address must be of a supported family.
* @param p The port in host byte order.
*/
void setPort(in_port_t p) {
switch(getFamily()) {
case AF_INET:
getIPv4().sin_port = htons(p);
break;
case AF_INET6:
getIPv6().sin6_port = htons(p);
break;
}
}
/**
* Set the address part of the socket address from a numeric IP address (string representation).
* Family must be already set. Throws in case of parse failue.
*/
void setAddress(const char* address);
/**
* Returns the accessible byte length at the pointer returned by #get().
* If zero, #get() returns null.
*/
socklen_t getLength() const { return len; }
/**
* An address is defined to be true if its length is not zero.
*/
explicit operator bool() const noexcept {
return len;
}
/**
* Returns the address to the managed sockaddr structure.
* The accessible length is returned by #getLength().
*/
const sockaddr* get() const { return addr.get(); }
/**
* Returns the address to the managed sockaddr structure.
* The accessible length is returned by #getLength().
*/
sockaddr* get() { return addr.get(); }
inline const sockaddr_in& getIPv4() const {
return *reinterpret_cast<const sockaddr_in*>(get());
}
inline const sockaddr_in6& getIPv6() const {
return *reinterpret_cast<const sockaddr_in6*>(get());
}
inline sockaddr_in& getIPv4() {
return *reinterpret_cast<sockaddr_in*>(get());
}
inline sockaddr_in6& getIPv6() {
return *reinterpret_cast<sockaddr_in6*>(get());
}
/**
* Return true if address is a loopback IP address.
*/
bool isLoopback() const;
/**
* Return true if address is not a public IP address.
*/
bool isPrivate() const;
bool isUnspecified() const;
bool isMappedIPv4() const;
SockAddr getMappedIPv4();
SockAddr getMappedIPv6();
/**
* A comparator to classify IP addresses, only considering the
* first 64 bits in IPv6.
*/
struct ipCmp {
bool operator()(const SockAddr& a, const SockAddr& b) const {
if (a.len != b.len)
return a.len < b.len;
socklen_t start, len;
switch(a.getFamily()) {
case AF_INET:
start = offsetof(sockaddr_in, sin_addr);
len = sizeof(in_addr);
break;
case AF_INET6:
start = offsetof(sockaddr_in6, sin6_addr);
// don't consider more than 64 bits (IPv6)
len = 8;
break;
default:
start = 0;
len = a.len;
break;
}
return std::memcmp((uint8_t*)a.get()+start,
(uint8_t*)b.get()+start, len) < 0;
}
};
private:
struct free_delete { void operator()(void* p) { ::free(p); } };
std::unique_ptr<sockaddr, free_delete> addr {};
socklen_t len {0};
void set(const sockaddr* sa, socklen_t length) {
if (len != length) {
len = length;
if (len) addr.reset((sockaddr*)::malloc(len));
else addr.reset();
}
if (len)
std::memcpy((uint8_t*)get(), (const uint8_t*)sa, len);
}
};
OPENDHT_PUBLIC bool operator==(const SockAddr& a, const SockAddr& b);
}
-106
View File
@@ -1,106 +0,0 @@
#pragma once
#include <asio.hpp>
#include <chrono>
#include <cstdint>
#include <functional>
#include <memory>
#include <vector>
namespace dht {
namespace net {
using asio::ip::udp;
using strand = asio::io_context::strand;
struct ReceivedPacket {
std::vector<uint8_t> data;
udp::endpoint from;
//time_point received;
};
class DatagramSocket {
public:
/** A function that takes a list of new received packets and
* optionally returns consumed packets for recycling.
**/
//using OnReceive = std::function<PacketList(PacketList&& packets)>;
using ReceiveCallback = std::function<void(const ReceivedPacket&)>;
virtual ~DatagramSocket() {};
virtual asio::error_code sendTo(const uint8_t* data, size_t size, const udp::endpoint& dest) = 0;
virtual void setOnReceive(const ReceiveCallback& cb) = 0;/* {
//std::lock_guard<std::mutex> lk(lock);
//rx_callback = std::move(cb);
}*/
virtual bool hasIPv4() const = 0;
virtual bool hasIPv6() const = 0;
virtual udp::endpoint getBound(sa_family_t family = AF_UNSPEC) const = 0;
in_port_t getPort(sa_family_t family = AF_UNSPEC) const {
//std::lock_guard<std::mutex> lk(lock);
return getBound(family).port();
}
//virtual const udp::endpoint& getBoundRef(sa_family_t family = AF_UNSPEC) const = 0;
virtual void stop() = 0;
/*protected:
PacketList getNewPacket() {
PacketList pkts;
if (toRecycle_.empty()) {
pkts.emplace_back();
} else {
auto begIt = toRecycle_.begin();
auto begItNext = std::next(begIt);
pkts.splice(pkts.end(), toRecycle_, begIt, begItNext);
}
return pkts;
}
inline void onReceived(PacketList&& packets) {
std::lock_guard<std::mutex> lk(lock);
if (rx_callback) {
auto r = rx_callback(std::move(packets));
if (not r.empty() and toRecycle_.size() < RX_QUEUE_MAX_SIZE)
toRecycle_.splice(toRecycle_.end(), std::move(r));
}
}
protected:
mutable std::mutex lock;*/
private:
//ReceiveCallback rx_callback;
//PacketList toRecycle_;
};
class UdpSocket : public DatagramSocket {
public:
UdpSocket(std::shared_ptr<strand> strand, const udp::endpoint& ipv4_endpoint, const udp::endpoint& ipv6_endpoint);
void setOnReceive(const ReceiveCallback& callback);
void start_receive();
void stop();
void sendToAsync(std::vector<uint8_t> data, const udp::endpoint& to);
asio::error_code sendTo(const uint8_t* buf, size_t len, const udp::endpoint& to);
bool hasIPv4() const;
bool hasIPv6() const;
udp::endpoint getBound(sa_family_t af) const;
private:
class SocketHandler;
//std::shared_ptr<strand> strand_;
std::shared_ptr<SocketHandler> ipv4_handler_;
std::shared_ptr<SocketHandler> ipv6_handler_;
};
} // namespace net
} // namespace dht
-39
View File
@@ -21,8 +21,6 @@
#include "def.h"
#include <msgpack.hpp>
#include <asio/ip/address.hpp>
#include <asio/ip/udp.hpp>
#include <chrono>
#include <random>
@@ -64,11 +62,6 @@ void erase_if(std::map<Key, Item>& map, const Condition& condition)
OPENDHT_PUBLIC std::pair<std::string, std::string>
splitPort(const std::string& s);
inline
std::string print_addr(const asio::ip::udp::endpoint& endpoint) {
return asio::ip::detail::endpoint(endpoint.address(), endpoint.port()).to_string();
}
class OPENDHT_PUBLIC DhtException : public std::runtime_error {
public:
DhtException(const std::string &str = "") :
@@ -189,36 +182,4 @@ inline msgpack::object* findMapValue(const msgpack::object& map, std::string_vie
return findMapValue(map, key.data(), key.size());
}
using SockAddr = asio::ip::udp::endpoint;
/**
* A comparator to classify IP addresses, only considering the
* first 64 bits in IPv6.
*/
struct ipCmp {
bool operator()(const SockAddr& a, const SockAddr& b) const {
return a < b;
/*if (a.len != b.len)
return a.len < b.len;
socklen_t start, len;
switch(a.getFamily()) {
case AF_INET:
start = offsetof(sockaddr_in, sin_addr);
len = sizeof(in_addr);
break;
case AF_INET6:
start = offsetof(sockaddr_in6, sin6_addr);
// don't consider more than 64 bits (IPv6)
len = 8;
break;
default:
start = 0;
len = a.len;
break;
}
return std::memcmp((uint8_t*)a.get()+start,
(uint8_t*)b.get()+start, len) < 0;*/
}
};
} // namespace dht
+62 -62
View File
@@ -22,6 +22,7 @@
#include "infohash.h"
#include "crypto.h"
#include "utils.h"
#include "sockaddr.h"
#include <msgpack.hpp>
@@ -170,29 +171,29 @@ struct OPENDHT_PUBLIC Value
template<typename Functor>
Filter(Functor f) : std::function<bool(const Value&)>::function(f) {}
inline Filter chain(Filter&& f2) {
Filter chain(Filter&& f2) {
auto f1 = *this;
return chain(std::move(f1), std::move(f2));
}
inline Filter chainOr(Filter&& f2) {
Filter chainOr(Filter&& f2) {
auto f1 = *this;
return chainOr(std::move(f1), std::move(f2));
}
static inline Filter chain(Filter&& f1, Filter&& f2) {
static Filter chain(Filter&& f1, Filter&& f2) {
if (not f1) return std::move(f2);
if (not f2) return std::move(f1);
return [f1 = std::move(f1), f2 = std::move(f2)](const Value& v) {
return f1(v) and f2(v);
};
}
static inline Filter chain(const Filter& f1, const Filter& f2) {
static Filter chain(const Filter& f1, const Filter& f2) {
if (not f1) return f2;
if (not f2) return f1;
return [f1,f2](const Value& v) {
return f1(v) and f2(v);
};
}
static inline Filter chainAll(std::vector<Filter>&& set) {
static Filter chainAll(std::vector<Filter>&& set) {
if (set.empty()) return {};
return [set = std::move(set)](const Value& v) {
for (const auto& f : set)
@@ -201,18 +202,18 @@ struct OPENDHT_PUBLIC Value
return true;
};
}
static inline Filter chain(std::initializer_list<Filter> l) {
static Filter chain(std::initializer_list<Filter> l) {
return chainAll(std::vector<Filter>(l.begin(), l.end()));
}
static inline Filter chainOr(Filter&& f1, Filter&& f2) {
static Filter chainOr(Filter&& f1, Filter&& f2) {
if (not f1 or not f2) return {};
return [f1=std::move(f1),f2=std::move(f2)](const Value& v) {
return f1(v) or f2(v);
};
}
static inline Filter notFilter(Filter&& f) {
static Filter notFilter(Filter&& f) {
if (not f) return [](const Value&) { return false; };
return [f = std::move(f)](const Value& v) { return not f(v); };
return [f](const Value& v) { return not f(v); };
}
std::vector<Sp<Value>> filter(const std::vector<Sp<Value>>& values) {
if (not (*this))
@@ -227,50 +228,50 @@ struct OPENDHT_PUBLIC Value
/* Sneaky functions disguised in classes */
static inline Filter AllFilter() {
static const Filter AllFilter() {
return {};
}
static inline Filter TypeFilter(const ValueType& t) {
static Filter TypeFilter(const ValueType& t) {
return [tid = t.id](const Value& v) {
return v.type == tid;
};
}
static inline Filter TypeFilter(const ValueType::Id& tid) {
static Filter TypeFilter(const ValueType::Id& tid) {
return [tid](const Value& v) {
return v.type == tid;
};
}
static inline Filter IdFilter(const Id id) {
static Filter IdFilter(const Id id) {
return [id](const Value& v) {
return v.id == id;
};
}
static inline Filter RecipientFilter(const InfoHash& r) {
static Filter RecipientFilter(const InfoHash& r) {
return [r](const Value& v) {
return v.recipient == r;
};
}
static inline Filter OwnerFilter(const crypto::PublicKey& pk) {
static Filter OwnerFilter(const crypto::PublicKey& pk) {
return OwnerFilter(pk.getId());
}
static inline Filter OwnerFilter(const InfoHash& pkh) {
static Filter OwnerFilter(const InfoHash& pkh) {
return [pkh](const Value& v) {
return v.owner and v.owner->getId() == pkh;
};
}
static inline Filter SeqNumFilter(uint16_t seq_no) {
static Filter SeqNumFilter(uint16_t seq_no) {
return [seq_no](const Value& v) {
return v.seq == seq_no;
};
}
static inline Filter UserTypeFilter(std::string ut) {
static Filter UserTypeFilter(std::string ut) {
return [ut = std::move(ut)](const Value& v) {
return v.user_type == ut;
};
@@ -342,10 +343,10 @@ struct OPENDHT_PUBLIC Value
return unpack<T>(*this);
}
inline bool isEncrypted() const {
bool isEncrypted() const {
return not cypher.empty();
}
inline bool isSigned() const {
bool isSigned() const {
return owner and not signature.empty();
}
@@ -354,24 +355,37 @@ struct OPENDHT_PUBLIC Value
* Afterward, checkSignature() will return true and owner will
* be set to the corresponding public key.
*/
void sign(const crypto::PrivateKey& key);
void sign(const crypto::PrivateKey& key) {
if (isEncrypted())
throw DhtException("Can't sign encrypted data.");
owner = key.getSharedPublicKey();
signature = key.sign(getToSign());
}
/**
* Check that the value is signed and that the signature matches.
* If true, the owner field will contain the signer public key.
*/
inline bool checkSignature() const {
bool checkSignature() const {
return isSigned() and owner->checkSignature(getToSign(), signature);
}
inline std::shared_ptr<crypto::PublicKey> getOwner() const {
return owner;
std::shared_ptr<crypto::PublicKey> getOwner() const {
return std::static_pointer_cast<crypto::PublicKey>(owner);
}
/**
* Sign the value with from and returns the encrypted version for to.
*/
Value encrypt(const crypto::PrivateKey& from, const crypto::PublicKey& to);
Value encrypt(const crypto::PrivateKey& from, const crypto::PublicKey& to) {
if (isEncrypted())
throw DhtException("Data is already encrypted.");
setRecipient(to.getId());
sign(from);
Value nv {id};
nv.setCypher(to.encrypt(getToEncrypt()));
return nv;
}
Value() {}
@@ -426,7 +440,7 @@ struct OPENDHT_PUBLIC Value
/**
* Returns true if value contents are equals (not considering the value ID)
*/
inline bool contentEquals(const Value& o) const {
inline bool contentEquals(const Value& o) {
return isEncrypted() ? cypher == o.cypher :
((owner == o.owner || (owner and o.owner and *owner == *o.owner))
&& type == o.type
@@ -435,25 +449,22 @@ struct OPENDHT_PUBLIC Value
&& signature == o.signature);
}
inline bool operator== (const Value& o) const {
inline bool operator== (const Value& o) {
return id == o.id and contentEquals(o);
}
inline bool operator!= (const Value& o) const {
return !(*this == o);
}
inline void setRecipient(const InfoHash& r) {
void setRecipient(const InfoHash& r) {
recipient = r;
}
inline void setCypher(Blob&& c) {
void setCypher(Blob&& c) {
cypher = std::move(c);
}
/**
* Pack part of the data to be signed (must always be done the same way)
*/
inline Blob getToSign() const {
Blob getToSign() const {
msgpack::sbuffer buffer;
msgpack::packer<msgpack::sbuffer> pk(&buffer);
msgpack_pack_to_sign(pk);
@@ -463,7 +474,7 @@ struct OPENDHT_PUBLIC Value
/**
* Pack part of the data to be encrypted
*/
inline Blob getToEncrypt() const {
Blob getToEncrypt() const {
msgpack::sbuffer buffer;
msgpack::packer<msgpack::sbuffer> pk(&buffer);
msgpack_pack_to_encrypt(pk);
@@ -473,8 +484,8 @@ struct OPENDHT_PUBLIC Value
/** print value for debugging */
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const Value& v);
inline std::string toString() const {
std::ostringstream ss;
std::string toString() const {
std::stringstream ss;
ss << *this;
return ss.str();
}
@@ -627,16 +638,8 @@ struct OPENDHT_PUBLIC Value
*/
unsigned priority {0};
inline bool isSignatureChecked() const {
return signatureChecked;
}
inline bool isDecrypted() const {
return decrypted;
}
bool checkSignature();
Sp<Value> decrypt(const crypto::PrivateKey& key);
private:
friend class SecureDht;
/* Cache for crypto ops */
bool signatureChecked {false};
bool signatureValid {false};
@@ -773,7 +776,7 @@ struct OPENDHT_PUBLIC Select
}
std::string toString() const {
std::ostringstream ss;
std::stringstream ss;
ss << *this;
return ss.str();
}
@@ -806,11 +809,11 @@ struct OPENDHT_PUBLIC Where
*
* @return the resulting Where instance.
*/
Where&& id(Value::Id id) {
Where& id(Value::Id id) {
FieldValue fv {Value::Field::Id, id};
if (std::find(filters_.begin(), filters_.end(), fv) == filters_.end())
filters_.emplace_back(std::move(fv));
return std::move(*this);
return *this;
}
/**
@@ -820,11 +823,11 @@ struct OPENDHT_PUBLIC Where
*
* @return the resulting Where instance.
*/
Where&& valueType(ValueType::Id type) {
Where& valueType(ValueType::Id type) {
FieldValue fv {Value::Field::ValueType, type};
if (std::find(filters_.begin(), filters_.end(), fv) == filters_.end())
filters_.emplace_back(std::move(fv));
return std::move(*this);
return *this;
}
/**
@@ -834,11 +837,11 @@ struct OPENDHT_PUBLIC Where
*
* @return the resulting Where instance.
*/
Where&& owner(InfoHash owner_pk_hash) {
Where& owner(InfoHash owner_pk_hash) {
FieldValue fv {Value::Field::OwnerPk, owner_pk_hash};
if (std::find(filters_.begin(), filters_.end(), fv) == filters_.end())
filters_.emplace_back(std::move(fv));
return std::move(*this);
return *this;
}
/**
@@ -848,11 +851,11 @@ struct OPENDHT_PUBLIC Where
*
* @return the resulting Where instance.
*/
Where&& seq(uint16_t seq_no) {
Where& seq(uint16_t seq_no) {
FieldValue fv {Value::Field::SeqNum, seq_no};
if (std::find(filters_.begin(), filters_.end(), fv) == filters_.end())
filters_.emplace_back(std::move(fv));
return std::move(*this);
return *this;
}
/**
@@ -862,11 +865,11 @@ struct OPENDHT_PUBLIC Where
*
* @return the resulting Where instance.
*/
Where&& userType(std::string_view user_type) {
Where& userType(std::string_view user_type) {
FieldValue fv {Value::Field::UserType, Blob {user_type.begin(), user_type.end()}};
if (std::find(filters_.begin(), filters_.end(), fv) == filters_.end())
filters_.emplace_back(std::move(fv));
return std::move(*this);
return *this;
}
/**
@@ -875,10 +878,7 @@ struct OPENDHT_PUBLIC Where
* @return the resulting Value::Filter.
*/
Value::Filter getFilter() const {
if (filters_.empty())
return {};
if (filters_.size() == 1)
return filters_[0].getLocalFilter();
if (filters_.empty()) return {};
std::vector<Value::Filter> fset;
fset.reserve(filters_.size());
for (const auto& f : filters_) {
@@ -896,7 +896,7 @@ struct OPENDHT_PUBLIC Where
}
std::string toString() const {
std::ostringstream ss;
std::stringstream ss;
ss << *this;
return ss.str();
}
@@ -962,7 +962,7 @@ struct OPENDHT_PUBLIC Query
void msgpack_unpack(const msgpack::object& o);
std::string toString() const {
std::ostringstream ss;
std::stringstream ss;
ss << *this;
return ss.str();
}
-88
View File
@@ -1,88 +0,0 @@
project('opendht', 'c', 'cpp',
version: '2.5.5',
default_options: [
'cpp_std=c++17',
'warning_level=3'
])
gnutls = dependency('gnutls')
nettle = dependency('nettle')
msgpack = dependency('msgpack-cxx', required : false)
argon2 = dependency('libargon2')
openssl = dependency('openssl', required: get_option('proxy_client'))
jsoncpp = dependency('jsoncpp', required: get_option('proxy_client'))
dirs=[]
if host_machine.system() == 'freebsd'
dirs+='/usr/local/lib'
elif host_machine.system() == 'darwin'
dirs+='/opt/homebrew/lib'
endif
http_parser = meson.get_compiler('c').find_library('http_parser', dirs: dirs, required: get_option('proxy_client'))
deps = [gnutls, nettle, msgpack, argon2, openssl, jsoncpp, http_parser]
add_project_arguments('-DMSGPACK_NO_BOOST', 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)
opendht_src = [
'src/utils.cpp',
'src/crypto.cpp',
'src/default_types.cpp',
'src/node.cpp',
'src/value.cpp',
'src/dht.cpp',
'src/callbacks.cpp',
'src/routing_table.cpp',
'src/node_cache.cpp',
'src/network_engine.cpp',
'src/securedht.cpp',
'src/dhtrunner.cpp',
'src/log.cpp',
'src/op_cache.cpp',
'src/network_utils.cpp'
]
if jsoncpp.found()
opendht_src += ['src/base64.cpp']
add_project_arguments('-DOPENDHT_JSONCPP', language : 'cpp')
endif
if http_parser.found()
opendht_src += ['src/http.cpp', 'src/compat/os_cert.cpp']
if host_machine.system() == 'darwin'
deps+=dependency('appleframeworks', modules : ['CoreFoundation', 'Security'])
endif
endif
if get_option('proxy_client').enabled()
opendht_src += ['src/dht_proxy_client.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')
endif
if get_option('peer_discovery').enabled()
opendht_src += 'src/peer_discovery.cpp'
add_project_arguments('-DOPENDHT_PEER_DISCOVERY', language : 'cpp')
endif
opendht = shared_library('opendht',
opendht_src,
include_directories : opendht_inc,
dependencies : deps,
cpp_args : ['-DOPENHT_BUILD', '-Dopendht_EXPORTS'],
install : true)
readline = meson.get_compiler('c').find_library('readline', required: get_option('tools'))
if get_option('tools').enabled()
dhtnode = executable('dhtnode', 'tools/dhtnode.cpp',
include_directories : opendht_interface_inc,
link_with : opendht,
dependencies : [readline, msgpack],
install : true)
if http_parser.found()
durl = executable('durl', 'tools/durl.cpp',
include_directories : opendht_interface_inc,
link_with : opendht,
dependencies : [msgpack, openssl])
endif
endif
-4
View File
@@ -1,4 +0,0 @@
option('proxy_client', type : 'feature', value : 'disabled')
option('proxy_server', type : 'feature', value : 'disabled')
option('peer_discovery', type : 'feature', value : 'enabled')
option('tools', type : 'feature', value : 'enabled')
+2 -3
View File
@@ -3,10 +3,9 @@ exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: OpenDHT
Description: C++17 Distributed Hash Table library
Description: C++14 Distributed Hash Table library
Version: @VERSION@
Libs: -L${libdir} -lopendht
Libs.private: @http_parser_lib@ -pthread
Requires: gnutls >= 3.3@jsoncpp_lib@@openssl_lib@
Requires.private: nettle >= 2.4@argon2_lib@
Requires.private: gnutls >= 3.3, nettle >= 2.4@argon2_lib@@jsoncpp_lib@@openssl_lib@
Cflags: -I${includedir}
+1 -6
View File
@@ -3,15 +3,10 @@ set(CURRENT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(CURRENT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
configure_file(setup.py.in setup.py)
configure_file(pyproject.toml pyproject.toml COPYONLY)
add_custom_target(python ALL
COMMAND python3 setup.py build
DEPENDS opendht opendht_cpp.pxd opendht.pyx pyproject.toml)
add_custom_target(dist
COMMAND python3 -m build
DEPENDS opendht opendht_cpp.pxd opendht.pyx pyproject.toml)
DEPENDS opendht opendht_cpp.pxd opendht.pyx)
install(CODE "execute_process(COMMAND python3 setup.py install --root=\$ENV{DESTDIR}/ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})")
if (OPENDHT_TOOLS)
+1 -2
View File
@@ -2,8 +2,7 @@ if USE_CYTHON
noinst_HEADERS = \
opendht.pyx \
opendht_cpp.pxd \
pyproject.toml
opendht_cpp.pxd
PYTHON_INSTALL_RECORD = $(builddir)/install_record.txt
-3
View File
@@ -1,3 +0,0 @@
[build-system]
requires = ["setuptools", "wheel", "Cython"]
build-backend = "setuptools.build_meta"
+1 -1
View File
@@ -20,7 +20,7 @@ help).
## Python dependencies
- pyroute2 >=0.6.9
- pyroute2 >=0.3.14
- matplotlib
- GeoIP (used by `scanner.py` for drawing map of the world)
- ipaddress
+1 -1
View File
@@ -30,7 +30,7 @@ import traceback
import ipaddress
import netifaces
import numpy as np
from pyroute2 import NSPopen
from pyroute2.netns.process.proxy import NSPopen
import msgpack
from opendht import *
+88 -234
View File
@@ -15,253 +15,107 @@
#
# You should have received a copy of the GNU General Public License
# along with this program; If not, see <http://www.gnu.org/licenses/>.
import argparse
import os
import subprocess
from pyroute2 import NDB, NSPopen
import argparse, subprocess
from pyroute2 import IPDB, NetNS
from pyroute2.netns.process.proxy import NSPopen
def int_range(mini, maxi):
def check_ifnum(arg):
try:
ret = int(arg)
except ValueError:
raise argparse.ArgumentTypeError('must be an integer')
if ret > maxi or ret < mini:
raise argparse.ArgumentTypeError(
f'must be {mini} <= int <= {maxi}'
)
return ret
return check_ifnum
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Creates a virtual network topology for testing'
)
parser.add_argument(
'-i', '--ifname', help='interface name', default='ethdht'
)
parser.add_argument(
'-n',
'--ifnum',
type=int_range(1, 245),
help='number of isolated interfaces to create',
default=1,
)
parser.add_argument(
'-r',
'--remove',
help='remove instead of adding network interfaces',
action='store_true',
)
parser.add_argument(
'-l',
'--loss',
help='simulated packet loss (percent)',
type=int,
default=0,
)
parser.add_argument(
'-d', '--delay', help='simulated latency (ms)', type=int, default=0
)
parser.add_argument(
'-4', '--ipv4', help='Enable IPv4', action='store_true'
)
parser.add_argument(
'-6', '--ipv6', help='Enable IPv6', action='store_true'
)
parser.add_argument(
'-b',
'--debug',
help='Turn on debug logging and dump topology databases',
action='store_true',
)
parser.add_argument(
'-v',
'--verbose',
help='Turn on verbose output on netns and interfaces operations',
action='store_true',
)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Creates a virtual network topology for testing')
parser.add_argument('-i', '--ifname', help='interface name', default='ethdht')
parser.add_argument('-n', '--ifnum', type=int, help='number of isolated interfaces to create', default=1)
parser.add_argument('-r', '--remove', help='remove instead of adding network interfaces', action="store_true")
parser.add_argument('-l', '--loss', help='simulated packet loss (percent)', type=int, default=0)
parser.add_argument('-d', '--delay', help='simulated latency (ms)', type=int, default=0)
parser.add_argument('-4', '--ipv4', help='Enable IPv4', action="store_true")
parser.add_argument('-6', '--ipv6', help='Enable IPv6', action="store_true")
args = parser.parse_args()
local_addr4 = '10.0.42.'
local_addr6 = '2001:db9::'
bripv4 = f'{local_addr4}1/24'
bripv6 = f'{local_addr6}1/64'
bridge_name = f'br{args.ifname}'
tap_name = f'tap{args.ifname}'
veth_names = []
namespaces = []
ipv4addrs = []
ipv6addrs = []
for ifn in range(args.ifnum):
namespaces.append(f'node{ifn}')
veth_names.append(f'{args.ifname}{ifn}')
ipv4addrs.append(f'{local_addr4}{ifn+8}/24' if args.ipv4 else None)
ipv6addrs.append(f'{local_addr6}{ifn+8}/64' if args.ipv6 else None)
brige_name = 'br'+args.ifname
with NDB(log='debug' if args.debug else None) as ndb:
ip = None
try:
ip = IPDB()
if args.remove:
# cleanup interfaces in the main namespace
for iface in veth_names + [bridge_name] + [tap_name]:
if iface in ndb.interfaces:
ndb.interfaces[iface].remove().commit()
if args.verbose:
print(f'link: del main/{iface}')
# cleanup namespaces
for nsname in namespaces:
try:
ndb.netns[nsname].remove().commit()
if args.verbose:
print(f'netns: del {nsname}')
except KeyError:
pass
else:
# create ports
for veth, nsname, ipv4addr, ipv6addr in zip(
veth_names, namespaces, ipv4addrs, ipv6addrs
):
# create a network namespace and launch NDB for it
#
# another possible solution could be simply to attach
# the namespace to the main NDB instance, but it can
# take a lot of memory in case of many interfaces, thus
# launch and discard netns NDB instances
netns = NDB(
log='debug' if args.debug else None,
sources=[
{
'target': 'localhost',
'netns': nsname,
'kind': 'netns',
}
],
)
if args.verbose:
print(f'netns: add {nsname}')
# create the port and push the peer into the namespace
(
ndb.interfaces.create(
**{
'ifname': veth,
'kind': 'veth',
'state': 'up',
'peer': {'ifname': veth, 'net_ns_fd': nsname},
}
).commit()
)
if args.verbose:
print(f'link: add main/{veth} <-> {nsname}/{veth}')
# bring up namespace's loopback
(
netns.interfaces.wait(ifname='lo', timeout=3)
.set('state', 'up')
.commit()
)
if args.verbose:
print(f'link: set {nsname}/lo')
# bring up the peer
with netns.interfaces.wait(ifname=veth, timeout=3) as i:
i.set('state', 'up')
if args.ipv4:
i.add_ip(ipv4addr)
if args.ipv6:
i.add_ip(ipv6addr)
if args.verbose:
print(f'link: set {nsname}/{veth}, {ipv4addr}, {ipv6addr}')
# disconnect the namespace NDB agent, not removing the NS
if args.debug:
fname = f'{nsname}-ndb.db'
print(f'dump: netns topology database {fname}')
netns.schema.backup(fname)
# cleanup interfaces
for ifn in range(args.ifnum):
iface = args.ifname+str(ifn)
if iface in ip.interfaces:
with ip.interfaces[iface] as i:
i.remove()
if 'tap'+args.ifname in ip.interfaces:
with ip.interfaces['tap'+args.ifname] as i:
i.remove()
if brige_name in ip.interfaces:
with ip.interfaces[brige_name] as i:
i.remove()
for ifn in range(args.ifnum):
netns = NetNS('node'+str(ifn))
netns.close()
# set up the emulation QDisc
nsp = NSPopen(
nsname,
[
'tc',
'qdisc',
'add',
'dev',
veth,
'root',
'netem',
'delay',
f'{args.delay}ms',
f'{int(args.delay)/2}ms',
'loss',
f'{args.loss}%',
'25%',
],
stdout=subprocess.PIPE,
)
netns.remove()
else:
for ifn in range(args.ifnum):
iface = args.ifname+str(ifn)
if not iface in ip.interfaces:
ip.create(kind='veth', ifname=iface, peer=iface+'.1').commit()
ip.create(kind='tuntap', ifname='tap'+args.ifname, mode='tap').commit()
with ip.create(kind='bridge', ifname=brige_name) as i:
for ifn in range(args.ifnum):
iface = args.ifname+str(ifn)
i.add_port(ip.interfaces[iface])
i.add_port(ip.interfaces['tap'+args.ifname])
if args.ipv4:
i.add_ip(local_addr4+'1/24')
if args.ipv6:
i.add_ip(local_addr6+'1/64')
i.up()
with ip.interfaces['tap'+args.ifname] as tap:
tap.up()
for ifn in range(args.ifnum):
iface = args.ifname+str(ifn)
nns = NetNS('node'+str(ifn))
iface1 = iface+'.1'
with ip.interfaces[iface1] as i:
i.net_ns_fd = nns.netns
with ip.interfaces[iface] as i:
i.up()
ip_ns = IPDB(nl=nns)
try:
with ip_ns.interfaces.lo as lo:
lo.up()
with ip_ns.interfaces[iface1] as i:
if args.ipv4:
i.add_ip(local_addr4+str(ifn+8)+'/24')
if args.ipv6:
i.add_ip(local_addr6+str(ifn+8)+'/64')
i.up()
finally:
ip_ns.release()
nsp = NSPopen(nns.netns, ["tc", "qdisc", "add", "dev", iface1, "root", "netem", "delay", str(args.delay)+"ms", str(int(args.delay/2))+"ms", "loss", str(args.loss)+"%", "25%"], stdout=subprocess.PIPE)
#print(nsp.communicate()[0].decode())
nsp.communicate()
nsp.wait()
nsp.release()
if args.verbose:
print(
f'netem: add {nsname}/{veth}, '
f'{args.delay}, {args.loss}'
)
# create the tap
#
# for some reason we should create the tap inteface first,
# and only then bring it up, thus two commit() calls
(
ndb.interfaces.create(
kind='tuntap', ifname=tap_name, mode='tap'
)
.commit()
.set('state', 'up')
.commit()
)
if args.verbose:
print(f'link: add main/{tap_name}')
if args.ipv4:
subprocess.call(["sysctl", "-w", "net.ipv4.conf."+brige_name+".forwarding=1"])
if args.ipv6:
subprocess.call(["sysctl", "-w", "net.ipv6.conf."+brige_name+".forwarding=1"])
# create the bridge and add all the ports
with ndb.interfaces.create(
ifname=bridge_name, kind='bridge', state='up'
) as i:
if args.ipv4:
i.add_ip(bripv4)
if args.ipv6:
i.add_ip(bripv6)
for iface in veth_names + [tap_name]:
i.add_port(iface)
if args.verbose:
print(f'link: add main/{bridge_name}, {bripv4}, {bripv6}')
with open(os.devnull, 'w') as fnull:
if args.ipv4:
subprocess.call(
[
'sysctl',
'-w',
f'net.ipv4.conf.{bridge_name}.forwarding=1',
],
stdout=fnull,
)
if args.verbose:
print(f'sysctl: set {bridge_name} ipv4 forwarding')
if args.ipv6:
subprocess.call(
[
'sysctl',
'-w',
f'net.ipv6.conf.{bridge_name}.forwarding=1',
],
stdout=fnull,
)
if args.verbose:
print(f'sysctl: set {bridge_name} ipv4 forwarding')
if args.debug:
fname = 'main-ndb.db'
print('dump: the main netns topology database')
ndb.schema.backup(fname)
except Exception as e:
print('Error',e)
finally:
if ip:
ip.release()
+1 -1
View File
@@ -72,7 +72,7 @@ if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Launch an OpenDHT node with an HTTP control interface')
parser.add_argument('-p', '--port', help='OpenDHT port to bind', type=int, default=4222)
parser.add_argument('-hp', '--http-port', help='HTTP port to bind', type=int, default=8080)
parser.add_argument('-b', '--bootstrap', help='bootstrap address', default="bootstrap.jami.net:4222")
parser.add_argument('-b', '--bootstrap', help='bootstrap address', default="bootstrap.ring.cx:4222")
args = parser.parse_args()
endpoints.serverFromString(reactor, "tcp:"+str(args.http_port)).listen(server.Site(DhtServer(args.port, args.bootstrap)))
reactor.run()
+1 -1
View File
@@ -24,7 +24,7 @@ from datetime import datetime
import opendht as dht
parser = argparse.ArgumentParser(description='Create a dht network of -n nodes')
parser.add_argument('-b', '--bootstrap', help='bootstrap address', default='bootstrap.jami.net')
parser.add_argument('-b', '--bootstrap', help='bootstrap address', default='bootstrap.ring.cx')
parser.add_argument('-n', '--num-ops', help='number of concurrent operations on the DHT', type=int, default=8)
parser.add_argument('-p', '--period', help='duration between each test (seconds)', type=int, default=60)
parser.add_argument('-t', '--timeout', help='timeout for a test to complete (seconds)', type=float, default=15)
+2 -1
View File
@@ -25,7 +25,8 @@ config.setRateLimit(-1, -1)
ping_node = dht.DhtRunner()
ping_node.run(config=config)
#ping_node.enableLogging()
#ping_node.bootstrap("bootstrap.jami.net", "4222")
#ping_node.bootstrap("bootstrap.ring.cx", "4222")
pong_node = dht.DhtRunner()
pong_node.run(config=config)
#pong_node.enableLogging()
+1 -1
View File
@@ -8,4 +8,4 @@ edition = "2018"
[dependencies]
libc="0.2.0"
os_socketaddr="0.2.3"
os_socketaddr="0.1.0"
-10
View File
@@ -47,8 +47,6 @@ impl DhtRunnerConfig {
proxy_server: ptr::null(),
push_node_id: ptr::null(),
push_token: ptr::null(),
push_topic: ptr::null(),
push_platform: ptr::null(),
peer_discovery: false,
peer_publish: false,
server_ca: ptr::null_mut(),
@@ -75,14 +73,6 @@ impl DhtRunnerConfig {
self.push_token = CString::new(push_token).unwrap().as_ptr();
}
pub fn set_push_topic(&mut self, push_topic: &str) {
self.push_topic = CString::new(push_topic).unwrap().as_ptr();
}
pub fn set_push_platform(&mut self, push_platform: &str) {
self.push_platform = CString::new(push_platform).unwrap().as_ptr();
}
pub fn set_identity(&mut self, certificate: Box<DhtCertificate>, privatekey: Box<PrivateKey>) {
self.dht_config.id.privatekey = Box::into_raw(privatekey);
self.dht_config.id.certificate = Box::into_raw(certificate);
-2
View File
@@ -118,8 +118,6 @@ pub struct DhtRunnerConfig
pub proxy_server: *const c_char,
pub push_node_id: *const c_char,
pub push_token: *const c_char,
pub push_topic: *const c_char,
pub push_platform: *const c_char,
pub peer_discovery: bool,
pub peer_publish: bool,
pub server_ca: *mut DhtCertificate,
+5 -1
View File
@@ -19,6 +19,7 @@ libopendht_la_SOURCES = \
routing_table.cpp \
network_engine.cpp \
utils.cpp \
infohash.cpp \
node.cpp \
value.cpp \
crypto.cpp \
@@ -39,12 +40,12 @@ nobase_include_HEADERS = \
../include/opendht/dht.h \
../include/opendht/callbacks.h \
../include/opendht/node_cache.h \
../include/opendht/node_export.h \
../include/opendht/routing_table.h \
../include/opendht/network_engine.h \
../include/opendht/scheduler.h \
../include/opendht/rate_limiter.h \
../include/opendht/utils.h \
../include/opendht/sockaddr.h \
../include/opendht/infohash.h \
../include/opendht/node.h \
../include/opendht/value.h \
@@ -83,3 +84,6 @@ if ENABLE_INDEXATION
libopendht_la_SOURCES += indexation/pht.cpp
nobase_include_HEADERS += ../include/opendht/indexation/pht.h
endif
clean-local:
rm -rf libargon2.la
+1 -1
View File
@@ -92,7 +92,7 @@ bindDoneCbSimple(DoneCallbackSimpleRaw raw_cb, void* user_data) {
std::string
NodeStats::toString() const
{
std::ostringstream ss;
std::stringstream ss;
ss << "Known nodes: " << good_nodes << " good, " << dubious_nodes << " dubious, " << incoming_nodes << " incoming." << std::endl;
ss << searches << " searches, " << node_cache_size << " total cached nodes" << std::endl;
if (table_depth > 1) {
+83 -70
View File
@@ -43,6 +43,18 @@ static std::uniform_int_distribution<int> rand_byte{ 0, std::numeric_limits<uint
static std::uniform_int_distribution<uint8_t> rand_byte;
#endif
// support for GnuTLS < 3.4.
#if GNUTLS_VERSION_NUMBER < 0x030400
#define GNUTLS_PKCS_PKCS12_3DES GNUTLS_PKCS_USE_PKCS12_3DES
#define GNUTLS_PKCS_PKCS12_ARCFOUR GNUTLS_PKCS_USE_PKCS12_ARCFOUR
#define GNUTLS_PKCS_PKCS12_RC2_40 GNUTLS_PKCS_USE_PKCS12_RC2_40
#define GNUTLS_PKCS_PBES2_3DES GNUTLS_PKCS_USE_PBES2_3DES
#define GNUTLS_PKCS_PBES2_AES_128 GNUTLS_PKCS_USE_PBES2_AES_128
#define GNUTLS_PKCS_PBES2_AES_192 GNUTLS_PKCS_USE_PBES2_AES_192
#define GNUTLS_PKCS_PBES2_AES_256 GNUTLS_PKCS_USE_PBES2_AES_256
#endif
#define DHT_AES_LEGACY_ENCRYPT 0
#define DHT_AES_LEGACY_DECRYPT 1
namespace dht {
@@ -98,6 +110,10 @@ Blob aesEncrypt(const uint8_t* data, size_t data_length, const Blob& key)
struct gcm_aes_ctx aes;
gcm_aes_set_key(&aes, key.size(), key.data());
gcm_aes_set_iv(&aes, GCM_IV_SIZE, ret.data());
#if DHT_AES_LEGACY_ENCRYPT
gcm_aes_update(&aes, data_length, data);
#endif
gcm_aes_encrypt(&aes, data_length, ret.data() + GCM_IV_SIZE, data);
gcm_aes_digest(&aes, GCM_DIGEST_SIZE, ret.data() + GCM_IV_SIZE + data_length);
return ret;
@@ -361,13 +377,12 @@ PrivateKey::getPublicKey() const
const std::shared_ptr<PublicKey>&
PrivateKey::getSharedPublicKey() const
{
std::lock_guard<std::mutex> lock(publicKeyMutex_);
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));
publicKey_ = std::move(pk);
}
if (publicKey_)
return 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));
publicKey_ = pk;
return publicKey_;
}
@@ -527,36 +542,36 @@ PublicKey::encrypt(const uint8_t* data, size_t data_len) const
return ret;
}
const InfoHash&
InfoHash
PublicKey::getId() const
{
if (pk and not idCached_.load()) {
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));
if (sz != id.size())
throw CryptoException("Can't get public key ID: wrong output length.");
cachedId_ = id;
idCached_.store(true);
}
return cachedId_;
if (not pk)
return {};
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));
if (sz != id.size())
throw CryptoException("Can't get public key ID: wrong output length.");
return id;
}
const PkId&
PkId
PublicKey::getLongId() const
{
if (pk and not longIdCached_.load()) {
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));
if (sz != h.size())
throw CryptoException("Can't get 256 bits public key ID: wrong output length.");
cachedLongId_ = h;
longIdCached_.store(true);
}
return cachedLongId_;
if (not pk)
return {};
#if GNUTLS_VERSION_NUMBER < 0x030401
throw CryptoException("Can't get 256 bits public key ID: GnuTLS 3.4.1 or higher required.");
#else
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));
if (sz != h.size())
throw CryptoException("Can't get 256 bits public key ID: wrong output length.");
return h;
#endif
}
gnutls_digest_algorithm_t
@@ -824,55 +839,51 @@ Certificate::~Certificate()
}
}
const PublicKey&
PublicKey
Certificate::getPublicKey() const
{
return *getSharedPublicKey();
PublicKey pk_ret;
if (auto err = gnutls_pubkey_import_x509(pk_ret.pk, cert, 0))
throw CryptoException(std::string("Can't get certificate public key: ") + gnutls_strerror(err));
return pk_ret;
}
const std::shared_ptr<PublicKey>&
Certificate::getSharedPublicKey() const
{
std::lock_guard<std::mutex> lock(publicKeyMutex_);
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));
publicKey_ = std::move(pk);
}
return publicKey_;
}
const InfoHash&
InfoHash
Certificate::getId() const
{
if (cert and not idCached_.load()) {
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));
if (sz != id.size())
throw CryptoException("Can't get certificate public key ID: wrong output length.");
cachedId_ = id;
idCached_.store(true);
}
return cachedId_;
if (not cert)
return {};
if (cachedId_)
return cachedId_;
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));
if (sz != id.size())
throw CryptoException("Can't get certificate public key ID: wrong output length.");
cachedId_ = id;
return id;
}
const PkId&
PkId
Certificate::getLongId() const
{
if (cert and not longIdCached_.load()) {
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));
if (sz != id.size())
throw CryptoException("Can't get certificate 256 bits public key ID: wrong output length.");
cachedLongId_ = id;
longIdCached_.store(true);
}
return cachedLongId_;
if (not cert)
return {};
if (cachedLongId_)
return cachedLongId_;
#if GNUTLS_VERSION_NUMBER < 0x030401
throw CryptoException("Can't get certificate 256 bits public key ID: GnuTLS 3.4.1 or higher required.");
#else
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));
if (sz != id.size())
throw CryptoException("Can't get certificate 256 bits public key ID: wrong output length.");
cachedLongId_ = id;
return id;
#endif
}
Blob
@@ -1760,8 +1771,10 @@ std::ostream& operator<< (std::ostream& o, const TrustList::VerifyResult& h)
o << "* Certificate has expired" << std::endl;
if (h.result & GNUTLS_CERT_UNEXPECTED_OWNER)
o << "* The owner is not the expected one" << std::endl;
#if GNUTLS_VERSION_NUMBER >= 0x030401
if (h.result & GNUTLS_CERT_PURPOSE_MISMATCH)
o << "* Certificate or an intermediate does not match the intended purpose" << std::endl;
#endif
if (h.result & GNUTLS_CERT_MISMATCH)
o << "* Certificate presented isn't the expected one" << std::endl;
} else {
+8 -1
View File
@@ -54,7 +54,14 @@ DhtMessage::ServiceFilter(const std::string& s)
std::ostream& operator<< (std::ostream& s, const IpServiceAnnouncement& v)
{
s << "Peer: " << v.addr;
if (v.addr) {
s << "Peer: ";
s << "port " << v.getPort();
char hbuf[NI_MAXHOST];
if (getnameinfo(v.addr.get(), v.addr.getLength(), hbuf, sizeof(hbuf), nullptr, 0, NI_NUMERICHOST) == 0) {
s << " addr " << std::string(hbuf, strlen(hbuf));
}
}
return s;
}
+243 -332
View File
File diff suppressed because it is too large. Load diff
+126 -134
View File
@@ -24,11 +24,8 @@
#include "utils.h"
#include <http_parser.h>
#include <asio/ip/address.hpp>
#include <deque>
namespace dht {
struct DhtProxyClient::InfoState {
@@ -110,17 +107,15 @@ DhtProxyClient::DhtProxyClient() {}
DhtProxyClient::DhtProxyClient(
std::shared_ptr<dht::crypto::Certificate> serverCA, dht::crypto::Identity clientIdentity,
std::shared_ptr<asio::io_context::strand> strand, const std::string& serverHost,
std::function<void()> signal, const std::string& serverHost,
const std::string& pushClientId, std::shared_ptr<dht::Logger> logger)
: DhtInterface(logger)
, proxyUrl_(serverHost)
, clientIdentity_(clientIdentity), serverCertificate_(serverCA)
, pushClientId_(pushClientId), pushSessionId_(getRandomSessionId())
, strand_(strand)
, loopSignal_(signal)
, jsonReader_(Json::CharReaderBuilder{}.newCharReader())
{
/*localAddrv4_.setFamily(AF_INET);
localAddrv6_.setFamily(AF_INET6);*/
jsonBuilder_["commentStyle"] = "None";
jsonBuilder_["indentation"] = "";
if (logger_) {
@@ -132,13 +127,13 @@ DhtProxyClient::DhtProxyClient(
clientIdentity_.second->toString(false/*chain*/).c_str());
}
// run http client
/*httpClientThread_ = std::thread([this](){
httpClientThread_ = std::thread([this](){
try {
if (logger_)
logger_->d("[proxy:client] starting io_context");
// Ensures the context() won't run out of work
auto work = asio::make_work_guard(context());
context().run();
// Ensures the httpContext_ won't run out of work
auto work = asio::make_work_guard(httpContext_);
httpContext_.run();
if (logger_)
logger_->d("[proxy:client] http client io_context stopped");
}
@@ -146,7 +141,7 @@ DhtProxyClient::DhtProxyClient(
if (logger_)
logger_->e("[proxy:client] run error: %s", ex.what());
}
});*/
});
if (!proxyUrl_.empty())
startProxy();
}
@@ -160,10 +155,12 @@ DhtProxyClient::startProxy()
if (logger_)
logger_->d("[proxy:client] start proxy with %s", proxyUrl_.c_str());
nextProxyConfirmationTimer_ = std::make_unique<asio::steady_timer>(context(), std::chrono::steady_clock::now());
nextProxyConfirmationTimer_ = std::make_unique<asio::steady_timer>(httpContext_, std::chrono::steady_clock::now());
nextProxyConfirmationTimer_->async_wait(std::bind(&DhtProxyClient::handleProxyConfirm, this, std::placeholders::_1));
listenerRestartTimer_ = std::make_unique<asio::steady_timer>(context());
listenerRestartTimer_ = std::make_unique<asio::steady_timer>(httpContext_);
loopSignal_();
}
void
@@ -190,10 +187,7 @@ void
DhtProxyClient::stop()
{
if (not isDestroying_.exchange(true)) {
{
std::lock_guard<std::mutex> l(resolverLock_);
resolver_.reset();
}
resolver_.reset();
cancelAllListeners();
if (infoState_)
infoState_->cancel = true;
@@ -202,8 +196,8 @@ DhtProxyClient::stop()
for (auto& request : requests_)
request.second->cancel();
}
if (not context().stopped())
context().stop();
if (not httpContext_.stopped())
httpContext_.stop();
if (httpClientThread_.joinable())
httpClientThread_.join();
requests_.clear();
@@ -285,6 +279,21 @@ DhtProxyClient::isRunning(sa_family_t af) const
}
}
time_point
DhtProxyClient::periodic(const uint8_t*, size_t, SockAddr, const time_point& /*now*/)
{
// Exec all currently stored callbacks
decltype(callbacks_) callbacks;
{
std::lock_guard<std::mutex> lock(lockCallbacks_);
callbacks = std::move(callbacks_);
}
for (auto& callback : callbacks)
callback();
callbacks.clear();
return time_point::max();
}
void
DhtProxyClient::setHeaderFields(http::Request& request){
request.set_header_field(restinio::http_field_t::accept, "*/*");
@@ -302,30 +311,26 @@ DhtProxyClient::get(const InfoHash& key, GetCallback cb, DoneCallback donecb, Va
return;
}
try {
auto request = buildRequest("/key/" + key.toString());
auto request = buildRequest("/" + key.toString());
auto reqid = request->id();
//request->set_connection_type(restinio::http_connection_header_t::keep_alive);
request->set_method(restinio::http_method_get());
setHeaderFields(*request);
auto opstate = std::make_shared<OperationState>();
Value::Filter filter = w.empty() ? f : f.chain(w.getFilter());
request->add_on_body_callback([
this,
key,
opstate,
filter = Value::Filter::chain(std::move(f), w.getFilter()),
rxBuf = std::make_shared<LineSplit>(),
cb
](const char* at, size_t length){
auto rxBuf = std::make_shared<LineSplit>();
request->add_on_body_callback([this, key, opstate, filter, rxBuf, cb](const char* at, size_t length){
try {
rxBuf->append(at, length);
auto& b = *rxBuf;
b.append(at, length);
// one value per body line
std::vector<Sp<Value>> values;
while (rxBuf->getLine('\n') and !opstate->stop) {
while (b.getLine('\n') and !opstate->stop) {
std::string err;
Json::Value json;
const auto& line = rxBuf->line();
const auto& line = b.line();
if (!jsonReader_->parse(line.data(), line.data() + line.size(), &json, &err)){
opstate->ok.store(false);
return;
@@ -335,11 +340,15 @@ DhtProxyClient::get(const InfoHash& key, GetCallback cb, DoneCallback donecb, Va
values.emplace_back(std::move(value));
}
if (not values.empty() and cb) {
context().post([opstate, cb, values = std::move(values)](){
if (not opstate->stop.load() and not cb(values)){
opstate->stop.store(true);
}
});
{
std::lock_guard<std::mutex> lock(lockCallbacks_);
callbacks_.emplace_back([opstate, cb, values = std::move(values)](){
if (not opstate->stop.load() and not cb(values)){
opstate->stop.store(true);
}
});
}
loopSignal_();
}
} catch(const std::exception& e) {
if (logger_)
@@ -356,10 +365,14 @@ DhtProxyClient::get(const InfoHash& key, GetCallback cb, DoneCallback donecb, Va
opFailed();
}
if (donecb) {
context().post([donecb, opstate](){
donecb(opstate->ok, {});
opstate->stop.store(true);
});
{
std::lock_guard<std::mutex> lock(lockCallbacks_);
callbacks_.emplace_back([donecb, opstate](){
donecb(opstate->ok, {});
opstate->stop.store(true);
});
}
loopSignal_();
}
if (not isDestroying_) {
std::lock_guard<std::mutex> l(requestLock_);
@@ -395,7 +408,7 @@ DhtProxyClient::put(const InfoHash& key, Sp<Value> val, DoneCallback cb, time_po
auto& search = searches_[key];
if (val->id) {
auto id = val->id;
auto refreshPutTimer = std::make_unique<asio::steady_timer>(context(), proxy::OP_TIMEOUT - proxy::OP_MARGIN);
auto refreshPutTimer = std::make_unique<asio::steady_timer>(httpContext_, proxy::OP_TIMEOUT - proxy::OP_MARGIN);
refreshPutTimer->async_wait(std::bind(&DhtProxyClient::handleRefreshPut, this, std::placeholders::_1, key, id));
search.puts.erase(id);
search.puts.emplace(std::piecewise_construct,
@@ -409,10 +422,12 @@ DhtProxyClient::put(const InfoHash& key, Sp<Value> val, DoneCallback cb, time_po
if (ok)
*ok = result;
if (cb) {
context().post([cb, result](){
std::lock_guard<std::mutex> lock(lockCallbacks_);
callbacks_.emplace_back([cb, result](){
cb(result, {});
});
}
loopSignal_();
}, created, permanent);
}
@@ -445,14 +460,12 @@ DhtProxyClient::handleRefreshPut(const asio::error_code &ec, InfoHash key, Value
std::shared_ptr<http::Request>
DhtProxyClient::buildRequest(const std::string& target)
{
std::unique_lock<std::mutex> l(resolverLock_);
auto resolver = resolver_;
l.unlock();
if (not resolver)
resolver = std::make_shared<http::Resolver>(context(), proxyUrl_, logger_);
resolver = std::make_shared<http::Resolver>(httpContext_, proxyUrl_, logger_);
auto request = target.empty()
? std::make_shared<http::Request>(context(), resolver)
: std::make_shared<http::Request>(context(), resolver, target);
? std::make_shared<http::Request>(httpContext_, resolver)
: std::make_shared<http::Request>(httpContext_, resolver, target);
if (serverCertificate_)
request->set_certificate_authority(serverCertificate_);
if (clientIdentity_.first and clientIdentity_.second)
@@ -468,7 +481,7 @@ DhtProxyClient::doPut(const InfoHash& key, Sp<Value> val, DoneCallbackSimple cb,
logger_->d("[proxy:client] [put] [search %s] executing for %s", key.to_c_str(), val->toString().c_str());
try {
auto request = buildRequest("/key/" + key.toString());
auto request = buildRequest("/" + key.toString());
auto reqid = request->id();
request->set_method(restinio::http_method_post());
setHeaderFields(*request);
@@ -503,7 +516,7 @@ DhtProxyClient::doPut(const InfoHash& key, Sp<Value> val, DoneCallbackSimple cb,
auto it = search.pendingPuts.find(val);
if (it != search.pendingPuts.end()) {
auto sok = std::make_shared<std::atomic_bool>(ok);
auto refreshPutTimer = std::make_unique<asio::steady_timer>(context(), proxy::OP_TIMEOUT - proxy::OP_MARGIN);
auto refreshPutTimer = std::make_unique<asio::steady_timer>(httpContext_, proxy::OP_TIMEOUT - proxy::OP_MARGIN);
refreshPutTimer->async_wait(std::bind(&DhtProxyClient::handleRefreshPut, this, std::placeholders::_1, key, id));
search.puts.emplace(std::piecewise_construct,
std::forward_as_tuple(id),
@@ -610,10 +623,9 @@ DhtProxyClient::getProxyInfos()
if (logger_)
logger_->d("[proxy:client] [status] sending request");
auto resolver = std::make_shared<http::Resolver>(context(), proxyUrl_, logger_);
auto resolver = std::make_shared<http::Resolver>(httpContext_, proxyUrl_, logger_);
queryProxyInfo(infoState, resolver, AF_INET);
queryProxyInfo(infoState, resolver, AF_INET6);
std::lock_guard<std::mutex> l(resolverLock_);
resolver_ = resolver;
}
@@ -623,7 +635,7 @@ DhtProxyClient::queryProxyInfo(const Sp<InfoState>& infoState, const Sp<http::Re
if (logger_)
logger_->d("[proxy:client] [status] query ipv%i info", family == AF_INET ? 4 : 6);
try {
auto request = std::make_shared<http::Request>(context(), resolver, family);
auto request = std::make_shared<http::Request>(httpContext_, resolver, family);
if (serverCertificate_)
request->set_certificate_authority(serverCertificate_);
auto reqid = request->id();
@@ -645,12 +657,6 @@ DhtProxyClient::queryProxyInfo(const Sp<InfoState>& infoState, const Sp<http::Re
if (!jsonReader_->parse(response.body.data(), response.body.data() + response.body.size(), &proxyInfos, &err)){
onProxyInfos(Json::Value{}, family);
} else if (not infoState->cancel) {
if (auto req = response.request.lock()) {
if (auto conn = req->get_connection()) {
const auto& localAddr = conn->local_address();
proxyInfos["local_ip"] = localAddr.to_string();
}
}
onProxyInfos(proxyInfos, family);
}
}
@@ -682,17 +688,10 @@ DhtProxyClient::onProxyInfos(const Json::Value& proxyInfos, const sa_family_t fa
std::unique_lock<std::mutex> l(lockCurrentProxyInfos_);
auto oldStatus = std::max(statusIpv4_, statusIpv6_);
auto& status = family == AF_INET ? statusIpv4_ : statusIpv6_;
auto ipChanged = false;
auto& pubAddress = family == AF_INET? publicAddressV4_ : publicAddressV6_;
auto& localAddress = family == AF_INET? localAddrv4_ : localAddrv6_;
if (not proxyInfos.isMember("node_id")) {
if (logger_)
logger_->e("[proxy:client] [info] request failed for %s", family == AF_INET ? "ipv4" : "ipv6");
status = NodeStatus::Disconnected;
if (pubAddress != SockAddr{}) {
pubAddress = {};
ipChanged = true;
}
} else {
if (logger_)
logger_->d("[proxy:client] [info] got proxy reply for %s",
@@ -701,46 +700,33 @@ DhtProxyClient::onProxyInfos(const Json::Value& proxyInfos, const sa_family_t fa
myid = InfoHash(proxyInfos["node_id"].asString());
stats4_ = NodeStats(proxyInfos["ipv4"]);
stats6_ = NodeStats(proxyInfos["ipv6"]);
auto publicIp = parsePublicAddress(proxyInfos["public_ip"]);
ipChanged = pubAddress != SockAddr{} && pubAddress != publicIp;
pubAddress = publicIp;
if (proxyInfos.isMember("local_ip")) {
std::string localIp = proxyInfos["local_ip"].asString();
auto localAddr = asio::ip::make_address(localIp);
if (localAddr != localAddress.address()) {
localAddress = SockAddr(localAddr, localAddress.port());
ipChanged = localAddress != SockAddr{};
}
/*if (localAddress.toString() != localIp) {
localAddress.setAddress(localIp.c_str());
ipChanged = localAddress != SockAddr{};
}*/
}
if (!ipChanged && stats4_.good_nodes + stats6_.good_nodes)
if (stats4_.good_nodes + stats6_.good_nodes)
status = NodeStatus::Connected;
else if (!ipChanged && stats4_.dubious_nodes + stats6_.dubious_nodes)
else if (stats4_.dubious_nodes + stats6_.dubious_nodes)
status = NodeStatus::Connecting;
else
status = NodeStatus::Disconnected;
auto publicIp = parsePublicAddress(proxyInfos["public_ip"]);
auto publicFamily = publicIp.getFamily();
if (publicFamily == AF_INET)
publicAddressV4_ = publicIp;
else if (publicFamily == AF_INET6)
publicAddressV6_ = publicIp;
} catch (const std::exception& e) {
if (logger_)
logger_->e("[proxy:client] [info] error processing: %s", e.what());
}
}
auto newStatus = std::max(statusIpv4_, statusIpv6_);
if (newStatus != oldStatus) {
onStateChanged();
}
if (newStatus == NodeStatus::Connected) {
if (oldStatus == NodeStatus::Disconnected || oldStatus == NodeStatus::Connecting || launchConnectedCbs_) {
launchConnectedCbs_ = false;
listenerRestartTimer_->expires_at(std::chrono::steady_clock::now());
listenerRestartTimer_->async_wait(std::bind(&DhtProxyClient::restartListeners, this, std::placeholders::_1));
if (not onConnectCallbacks_.empty()) {
context().post([cbs = std::move(onConnectCallbacks_)]() mutable {
std::lock_guard<std::mutex> lock(lockCallbacks_);
callbacks_.emplace_back([cbs = std::move(onConnectCallbacks_)]() mutable {
while (not cbs.empty()) {
cbs.front()();
cbs.pop();
@@ -752,31 +738,21 @@ DhtProxyClient::onProxyInfos(const Json::Value& proxyInfos, const sa_family_t fa
nextProxyConfirmationTimer_->async_wait(std::bind(&DhtProxyClient::handleProxyConfirm, this, std::placeholders::_1));
}
else if (newStatus == NodeStatus::Disconnected) {
auto next = std::chrono::steady_clock::now();
if (!ipChanged)
next += std::chrono::minutes(1);
nextProxyConfirmationTimer_->expires_at(next);
nextProxyConfirmationTimer_->expires_at(std::chrono::steady_clock::now() + std::chrono::minutes(1));
nextProxyConfirmationTimer_->async_wait(std::bind(&DhtProxyClient::handleProxyConfirm, this, std::placeholders::_1));
}
l.unlock();
loopSignal_();
}
void
DhtProxyClient::onStateChanged(){
for (auto& cb : onStateChangeCallbacks_)
cb(DhtNodeStatus{statusIpv4_, statusIpv6_});
}
SockAddr
DhtProxyClient::parsePublicAddress(const Json::Value& val)
{
auto public_ip = val.asString();
auto hostAndService = splitPort(public_ip);
auto sa = asio::ip::make_address(hostAndService.first);
if (sa == asio::ip::address{}) return {};
if (sa.is_v6() and sa.to_v6().is_v4_mapped())
return SockAddr(asio::ip::make_address_v4(asio::ip::v4_mapped, sa.to_v6()), 0);
return SockAddr(sa, 0);
auto sa = SockAddr::resolve(hostAndService.first);
if (sa.empty()) return {};
return sa.front().getMappedIPv4();
}
std::vector<SockAddr>
@@ -784,8 +760,8 @@ DhtProxyClient::getPublicAddress(sa_family_t family)
{
std::lock_guard<std::mutex> l(lockCurrentProxyInfos_);
std::vector<SockAddr> result;
if (publicAddressV6_ != SockAddr{} && family != AF_INET) result.emplace_back(publicAddressV6_);
if (publicAddressV4_ != SockAddr{} && family != AF_INET6) result.emplace_back(publicAddressV4_);
if (publicAddressV6_ && family != AF_INET) result.emplace_back(publicAddressV6_);
if (publicAddressV4_ && family != AF_INET6) result.emplace_back(publicAddressV4_);
return result;
}
@@ -800,7 +776,7 @@ DhtProxyClient::listen(const InfoHash& key, ValueCallback cb, Value::Filter filt
std::lock_guard<std::mutex> lock(searchLock_);
auto& search = searches_[key];
auto query = std::make_shared<Query>(Select{}, std::move(where));
return search.ops.listen(cb, query, std::move(filter), [this, key](Sp<Query>, ValueCallback cb, SyncCallback) -> size_t {
return search.ops.listen(cb, query, filter, [this, key](Sp<Query>, ValueCallback cb, SyncCallback) -> size_t {
// Find search
auto search = searches_.find(key);
if (search == searches_.end()) {
@@ -845,7 +821,7 @@ DhtProxyClient::listen(const InfoHash& key, ValueCallback cb, Value::Filter filt
* (if the proxy crash for any reason)
*/
if (!l->second.refreshSubscriberTimer)
l->second.refreshSubscriberTimer = std::make_unique<asio::steady_timer>(context());
l->second.refreshSubscriberTimer = std::make_unique<asio::steady_timer>(httpContext_);
l->second.refreshSubscriberTimer->expires_at(std::chrono::steady_clock::now() +
proxy::OP_TIMEOUT - proxy::OP_MARGIN);
l->second.refreshSubscriberTimer->async_wait(std::bind(&DhtProxyClient::handleResubscribe, this,
@@ -862,7 +838,7 @@ DhtProxyClient::listen(const InfoHash& key, ValueCallback cb, Value::Filter filt
else {
method = ListenMethod::SUBSCRIBE;
header.method(restinio::http_method_subscribe());
header.request_target("/key/" + key.toString());
header.request_target("/" + key.toString());
}
sendListen(header, l->second.cb, opstate, l->second, method);
return token;
@@ -912,7 +888,7 @@ DhtProxyClient::cancelListen(const InfoHash& key, size_t gtoken)
// define real cancel listen only once
if (not it->second.opExpirationTimer)
it->second.opExpirationTimer = std::make_unique<asio::steady_timer>(context(), ops.getExpiration());
it->second.opExpirationTimer = std::make_unique<asio::steady_timer>(httpContext_, ops.getExpiration());
else
it->second.opExpirationTimer->expires_at(ops.getExpiration());
it->second.opExpirationTimer->async_wait(std::bind(&DhtProxyClient::handleExpireListener, this, std::placeholders::_1, key));
@@ -948,7 +924,7 @@ DhtProxyClient::handleExpireListener(const asio::error_code &ec, const InfoHash&
if (not deviceKey_.empty()) {
// UNSUBSCRIBE
auto request = buildRequest("/key/" + key.toString());
auto request = buildRequest("/" + key.toString());
auto reqid = request->id();
try {
request->set_method(restinio::http_method_unsubscribe());
@@ -1044,10 +1020,14 @@ DhtProxyClient::sendListen(const restinio::http_request_header_t& header,
auto value = std::make_shared<Value>(json);
if (cb){
auto expired = json.get("expired", Json::Value(false)).asBool();
context().post([cb, value, opstate, expired]() {
if (not opstate->stop.load() and not cb({value}, expired, system_clock::time_point::min()))
opstate->stop.store(true);
});
{
std::lock_guard<std::mutex> lock(lockCallbacks_);
callbacks_.emplace_back([cb, value, opstate, expired]() {
if (not opstate->stop.load() and not cb({value}, expired, system_clock::time_point::min()))
opstate->stop.store(true);
});
}
loopSignal_();
}
}
} catch(const std::exception& e) {
@@ -1095,6 +1075,7 @@ DhtProxyClient::opFailed()
statusIpv6_ = NodeStatus::Disconnected;
}
getConnectivityStatus();
loopSignal_();
}
void
@@ -1130,7 +1111,7 @@ DhtProxyClient::restartListeners(const asio::error_code &ec)
*ok = result;
}, time_point::max(), true);
if (!put.second.refreshPutTimer) {
put.second.refreshPutTimer = std::make_unique<asio::steady_timer>(context());
put.second.refreshPutTimer = std::make_unique<asio::steady_timer>(httpContext_);
}
put.second.refreshPutTimer->expires_at(std::chrono::steady_clock::now() + proxy::OP_TIMEOUT - proxy::OP_MARGIN);
put.second.refreshPutTimer->async_wait(std::bind(&DhtProxyClient::handleRefreshPut, this,
@@ -1188,6 +1169,7 @@ DhtProxyClient::pushNotificationReceived(const std::map<std::string, std::string
statusIpv4_ = NodeStatus::Connected;
statusIpv6_ = NodeStatus::Connected;
}
auto launchLoop = false;
try {
auto sessionId = notification.find("s");
if (sessionId != notification.end() and sessionId->second != pushSessionId_) {
@@ -1206,7 +1188,7 @@ DhtProxyClient::pushNotificationReceived(const std::map<std::string, std::string
auto vid = std::stoull(vidIt->second);
auto& put = search.puts.at(vid);
if (!put.refreshPutTimer)
put.refreshPutTimer = std::make_unique<asio::steady_timer>(context(), std::chrono::steady_clock::now());
put.refreshPutTimer = std::make_unique<asio::steady_timer>(httpContext_, std::chrono::steady_clock::now());
else
put.refreshPutTimer->expires_at(std::chrono::steady_clock::now());
put.refreshPutTimer->async_wait(std::bind(&DhtProxyClient::handleRefreshPut, this, std::placeholders::_1, key, vid));
@@ -1248,19 +1230,23 @@ DhtProxyClient::pushNotificationReceived(const std::map<std::string, std::string
getline(ss, substr, ',');
ids.emplace_back(std::stoull(substr));
}
context().post([this, key, token, opstate, ids, sendTime]() {
if (opstate->stop)
return;
std::lock_guard<std::mutex> lock(searchLock_);
auto s = searches_.find(key);
if (s == searches_.end())
return;
auto l = s->second.listeners.find(token);
if (l == s->second.listeners.end())
return;
if (not opstate->stop and not l->second.cache.onValuesExpired(ids, sendTime))
opstate->stop = true;
});
{
std::lock_guard<std::mutex> lockCb(lockCallbacks_);
callbacks_.emplace_back([this, key, token, opstate, ids, sendTime]() {
if (opstate->stop)
return;
std::lock_guard<std::mutex> lock(searchLock_);
auto s = searches_.find(key);
if (s == searches_.end())
return;
auto l = s->second.listeners.find(token);
if (l == s->second.listeners.end())
return;
if (not opstate->stop and not l->second.cache.onValuesExpired(ids, sendTime))
opstate->stop = true;
});
}
launchLoop = true;
}
}
}
@@ -1268,6 +1254,8 @@ DhtProxyClient::pushNotificationReceived(const std::map<std::string, std::string
if (logger_)
logger_->e("[proxy:client] [push] receive error: %s", e.what());
}
if (launchLoop)
loopSignal_();
#else
(void) notification;
#endif
@@ -1290,7 +1278,7 @@ DhtProxyClient::resubscribe(const InfoHash& key, const size_t token, Listener& l
header.method(restinio::http_method_subscribe());
header.request_target("/" + key.toString());
if (!listener.refreshSubscriberTimer){
listener.refreshSubscriberTimer = std::make_unique<asio::steady_timer>(context());
listener.refreshSubscriberTimer = std::make_unique<asio::steady_timer>(httpContext_);
}
listener.refreshSubscriberTimer->expires_at(std::chrono::steady_clock::now() +
proxy::OP_TIMEOUT - proxy::OP_MARGIN);
@@ -1311,8 +1299,12 @@ DhtProxyClient::getPushRequest(Json::Value& body) const
body["key"] = deviceKey_;
body["client_id"] = pushClientId_;
body["session_id"] = pushSessionId_;
body["topic"] = notificationTopic_;
body["platform"] = platform_;
#ifdef __ANDROID__
body["platform"] = "android";
#endif
#ifdef __APPLE__
body["platform"] = "apple";
#endif
}
std::string
+75 -186
View File
@@ -173,9 +173,6 @@ DhtProxyServer::PermanentPut::msgpack_unpack(const msgpack::object& o)
if (auto val = findMapValue(o, "value"sv)) {
value = std::make_shared<dht::Value>(*val);
}
if (auto top = findMapValue(o, "top"sv)) {
topic = top->as<std::string>();
}
}
#ifdef OPENDHT_PUSH_NOTIFICATIONS
@@ -197,9 +194,6 @@ DhtProxyServer::Listener::msgpack_unpack(const msgpack::object& o)
if (auto t = findMapValue(o, "t"sv)) {
type = t->as<PushType>();
}
if (auto top = findMapValue(o, "top"sv)) {
topic = top->as<std::string>();
}
}
#endif
@@ -211,8 +205,7 @@ DhtProxyServer::DhtProxyServer(const std::shared_ptr<DhtRunner>& dht,
dht_(dht), persistPath_(config.persistStatePath), logger_(logger),
printStatsTimer_(std::make_unique<asio::steady_timer>(*ioContext_, 3s)),
connListener_(std::make_shared<ConnectionListener>(std::bind(&DhtProxyServer::onConnectionClosed, this, std::placeholders::_1))),
pushServer_(config.pushServer),
bundleId_(config.bundleId)
pushServer_(config.pushServer)
{
if (not dht_)
throw std::invalid_argument("A DHT instance must be provided");
@@ -271,15 +264,13 @@ DhtProxyServer::DhtProxyServer(const std::shared_ptr<DhtRunner>& dht,
// build http server
auto settings = restinio::run_on_this_thread_settings_t<RestRouterTraitsTls>();
addServerSettings(settings);
if (not config.address.empty())
settings.address(config.address);
settings.port(config.port);
settings.tls_context(std::move(tls_context));
httpsServer_ = std::make_unique<restinio::http_server_t<RestRouterTraitsTls>>(
ioContext_,
std::forward<restinio::run_on_this_thread_settings_t<RestRouterTraitsTls>>(std::move(settings))
);
// run https server
// run http server
serverThread_ = std::thread([this]{
httpsServer_->open_async([]{/*ok*/}, [](std::exception_ptr ex){
std::rethrow_exception(ex);
@@ -290,8 +281,6 @@ DhtProxyServer::DhtProxyServer(const std::shared_ptr<DhtRunner>& dht,
else {
auto settings = restinio::run_on_this_thread_settings_t<RestRouterTraits>();
addServerSettings(settings);
if (not config.address.empty())
settings.address(config.address);
settings.port(config.port);
httpServer_ = std::make_unique<restinio::http_server_t<RestRouterTraits>>(
ioContext_,
@@ -383,7 +372,7 @@ DhtProxyServer::loadState(Is& is, size_t size) {
pput.second.expireNotifyTimer = std::make_unique<asio::steady_timer>(io_context(), pput.second.expiration - proxy::OP_MARGIN);
pput.second.expireNotifyTimer->async_wait(std::bind(
&DhtProxyServer::handleNotifyPushListenExpire, this,
std::placeholders::_1, pput.second.pushToken, std::move(jsonProvider), pput.second.type, pput.second.topic));
std::placeholders::_1, pput.second.pushToken, std::move(jsonProvider), pput.second.type));
}
#endif
dht_->put(put.first, pput.second.value, DoneCallbackSimple{}, time_point::max(), true);
@@ -403,7 +392,7 @@ DhtProxyServer::loadState(Is& is, size_t size) {
for (auto& listeners : pushListener.second.listeners) {
for (auto& listener : listeners.second) {
listener.internalToken = dht_->listen(listeners.first,
[this, infoHash=listeners.first, pushToken=pushListener.first, type=listener.type, clientId=listener.clientId, sessionCtx = listener.sessionCtx, topic=listener.topic]
[this, infoHash=listeners.first, pushToken=pushListener.first, type=listener.type, clientId=listener.clientId, sessionCtx = listener.sessionCtx]
(const std::vector<std::shared_ptr<Value>>& values, bool expired) {
// Build message content
Json::Value json;
@@ -415,7 +404,7 @@ DhtProxyServer::loadState(Is& is, size_t size) {
json["s"] = sessionCtx->sessionId;
}
if (expired and values.size() < 2){
std::ostringstream ss;
std::stringstream ss;
for(size_t i = 0; i < values.size(); ++i){
if(i != 0) ss << ",";
ss << values[i]->id;
@@ -425,7 +414,7 @@ DhtProxyServer::loadState(Is& is, size_t size) {
auto maxPrio = 1000u;
for (const auto& v : values)
maxPrio = std::min(maxPrio, v->priority);
sendPushNotification(pushToken, std::move(json), type, !expired and maxPrio == 0, topic);
sendPushNotification(pushToken, std::move(json), type, !expired and maxPrio == 0);
return true;
}
);
@@ -440,7 +429,7 @@ DhtProxyServer::loadState(Is& is, size_t size) {
return json;
};
listener.expireNotifyTimer->async_wait(std::bind(&DhtProxyServer::handleNotifyPushListenExpire, this,
std::placeholders::_1, pushListener.first, std::move(jsonProvider), listener.type, listener.topic));
std::placeholders::_1, pushListener.first, std::move(jsonProvider), listener.type));
// cancel push listen
listener.expireTimer = std::make_unique<asio::steady_timer>(io_context(), listener.expiration);
listener.expireTimer->async_wait(std::bind(&DhtProxyServer::handleCancelPushListen, this,
@@ -466,17 +455,8 @@ DhtProxyServer::io_context() const
return *ioContext_;
}
void
DhtProxyServer::stop()
DhtProxyServer::~DhtProxyServer()
{
if (logger_)
logger_->d("[proxy:server] closing http server");
ioContext_->stop();
if (serverThread_.joinable())
serverThread_.join();
if (logger_)
logger_->d("[proxy:server] http server closed");
if (not persistPath_.empty()) {
if (logger_)
logger_->d("Saving proxy state to %.*s", (int)persistPath_.size(), persistPath_.c_str());
@@ -504,13 +484,13 @@ DhtProxyServer::stop()
pushListeners_.clear();
#endif
}
}
DhtProxyServer::~DhtProxyServer()
{
if (logger_)
logger_->d("[proxy:server] closing http server");
ioContext_->stop();
if (serverThread_.joinable())
serverThread_.join();
if (logger_)
logger_->d("[proxy:server] http server closed");
}
template< typename ServerSettings >
@@ -640,8 +620,6 @@ DhtProxyServer::createRestRouter()
// key.listen
router->http_get("/key/:hash/listen", std::bind(&DhtProxyServer::listen, this, _1, _2));
#ifdef OPENDHT_PUSH_NOTIFICATIONS
// node.pingPush
router->http_post("/node/pingPush", std::bind(&DhtProxyServer::pingPush, this, _1, _2));
// key.subscribe
router->add_handler(restinio::http_method_subscribe(),
"/key/:hash", std::bind(&DhtProxyServer::subscribe, this, _1, _2));
@@ -713,7 +691,7 @@ DhtProxyServer::get(restinio::request_handle_t request,
initHttpResponse(request->create_response<ResponseByParts>()));
response->flush();
dht_->get(infoHash, [this, response](const std::vector<Sp<Value>>& values) {
std::ostringstream output;
std::stringstream output;
for (const auto& value : values) {
output << Json::writeString(jsonBuilder_, value->toJson()) << "\n";
}
@@ -767,59 +745,6 @@ DhtProxyServer::listen(restinio::request_handle_t request,
#ifdef OPENDHT_PUSH_NOTIFICATIONS
PushType
DhtProxyServer::getTypeFromString(const std::string& type) {
if (type == "android")
return PushType::Android;
else if (type == "ios")
return PushType::iOS;
else if (type == "unifiedpush")
return PushType::UnifiedPush;
return PushType::None;
}
std::string
DhtProxyServer::getDefaultTopic(PushType) {
return bundleId_;
}
RequestStatus
DhtProxyServer::pingPush(restinio::request_handle_t request,
restinio::router::route_params_t /*params*/)
{
requestNum_++;
try {
std::string err;
Json::Value r;
auto* char_data = reinterpret_cast<const char*>(request->body().data());
auto reader = std::unique_ptr<Json::CharReader>(jsonReaderBuilder_.newCharReader());
if (!reader->parse(char_data, char_data + request->body().size(), &r, &err)){
auto response = initHttpResponse(request->create_response(restinio::status_bad_request()));
response.set_body(RESP_MSG_JSON_INCORRECT);
return response.done();
}
const Json::Value& root(r); // parse using const Json so [] never creates element
auto pushToken = root["key"].asString();
if (pushToken.empty()){
auto response = initHttpResponse(request->create_response(restinio::status_bad_request()));
response.set_body(RESP_MSG_NO_TOKEN);
return response.done();
}
auto type = getTypeFromString(root["platform"].asString());
auto topic = root["topic"].asString();
if (topic.empty()) {
topic = getDefaultTopic(type);
}
Json::Value json;
json["to"] = root["client_id"];
json["pong"] = true;
sendPushNotification(pushToken, std::move(json), type, true, topic);
} catch (...) {
return serverError(*request);
}
return restinio::request_handling_status_t::accepted;
}
RequestStatus
DhtProxyServer::subscribe(restinio::request_handle_t request,
restinio::router::route_params_t params)
@@ -846,11 +771,7 @@ DhtProxyServer::subscribe(restinio::request_handle_t request,
response.set_body(RESP_MSG_NO_TOKEN);
return response.done();
}
auto type = getTypeFromString(root["platform"].asString());
auto topic = root["topic"].asString();
if (topic.empty()) {
topic = getDefaultTopic(type);
}
auto type = root["platform"].asString() == "android" ? PushType::Android : PushType::iOS;
auto clientId = root["client_id"].asString();
auto sessionId = root["session_id"].asString();
@@ -881,7 +802,6 @@ DhtProxyServer::subscribe(restinio::request_handle_t request,
auto timeout = std::chrono::steady_clock::now() + proxy::OP_TIMEOUT;
listener.expiration = timeout;
listener.type = type;
listener.topic = topic;
if (listener.expireNotifyTimer)
listener.expireNotifyTimer->expires_at(timeout - proxy::OP_MARGIN);
else
@@ -895,7 +815,7 @@ DhtProxyServer::subscribe(restinio::request_handle_t request,
return json;
};
listener.expireNotifyTimer->async_wait(std::bind(&DhtProxyServer::handleNotifyPushListenExpire, this,
std::placeholders::_1, pushToken, std::move(jsonProvider), listener.type, listener.topic));
std::placeholders::_1, pushToken, std::move(jsonProvider), listener.type));
if (!listener.expireTimer)
listener.expireTimer = std::make_unique<asio::steady_timer>(io_context(), timeout);
else
@@ -930,7 +850,7 @@ DhtProxyServer::subscribe(restinio::request_handle_t request,
// =========== No existing listener for an infoHash ============
// Add listen on dht
listener.internalToken = dht_->listen(infoHash,
[this, infoHash, pushToken, type, clientId, sessionCtx = listener.sessionCtx, topic]
[this, infoHash, pushToken, type, clientId, sessionCtx = listener.sessionCtx]
(const std::vector<std::shared_ptr<Value>>& values, bool expired){
// Build message content
Json::Value json;
@@ -942,8 +862,8 @@ DhtProxyServer::subscribe(restinio::request_handle_t request,
json["s"] = sessionCtx->sessionId;
}
if (expired and values.size() < 2){
std::ostringstream ss;
for(size_t i = 0; i < values.size(); ++i) {
std::stringstream ss;
for(size_t i = 0; i < values.size(); ++i){
if(i != 0) ss << ",";
ss << values[i]->id;
}
@@ -952,7 +872,7 @@ DhtProxyServer::subscribe(restinio::request_handle_t request,
auto maxPrio = 1000u;
for (const auto& v : values)
maxPrio = std::min(maxPrio, v->priority);
sendPushNotification(pushToken, std::move(json), type, !expired and maxPrio == 0, topic);
sendPushNotification(pushToken, std::move(json), type, !expired and maxPrio == 0);
return true;
}
);
@@ -1008,7 +928,7 @@ DhtProxyServer::unsubscribe(restinio::request_handle_t request,
void
DhtProxyServer::handleNotifyPushListenExpire(const asio::error_code &ec, const std::string pushToken,
std::function<Json::Value()> jsonProvider, PushType type, const std::string& topic)
std::function<Json::Value()> jsonProvider, PushType type)
{
if (ec == asio::error::operation_aborted)
return;
@@ -1017,8 +937,8 @@ DhtProxyServer::handleNotifyPushListenExpire(const asio::error_code &ec, const s
logger_->e("[proxy:server] [subscribe] error sending put refresh: %s", ec.message().c_str());
}
if (logger_)
logger_->d("[proxy:server] [subscribe] sending refresh to %s token", pushToken.c_str());
sendPushNotification(pushToken, jsonProvider(), type, false, topic);
logger_->d("[proxy:server] [subscribe] sending put refresh to %s token", pushToken.c_str());
sendPushNotification(pushToken, jsonProvider(), type, false);
}
void
@@ -1060,70 +980,45 @@ DhtProxyServer::handleCancelPushListen(const asio::error_code &ec, const std::st
}
void
DhtProxyServer::sendPushNotification(const std::string& token, Json::Value&& json, PushType type, bool highPriority, const std::string& topic)
DhtProxyServer::sendPushNotification(const std::string& token, Json::Value&& json, PushType type, bool highPriority)
{
if (pushServer_.empty())
return;
unsigned reqid = 0;
try {
std::shared_ptr<http::Request> request;
http::Url tokenUrl(token);
if (type == PushType::UnifiedPush)
request = std::make_shared<http::Request>(io_context(), tokenUrl.protocol + "://" + tokenUrl.host, logger_);
else
request = std::make_shared<http::Request>(io_context(), pushHostPort_.first, pushHostPort_.second, pushHostPort_.first.find("https://") == 0, logger_);;
auto request = std::make_shared<http::Request>(io_context(), pushHostPort_.first, pushHostPort_.second,
httpsServer_ ? true : false, logger_);
reqid = request->id();
request->set_target(type == PushType::UnifiedPush ? (tokenUrl.target) : "/api/push");
request->set_target("/api/push");
request->set_method(restinio::http_method_post());
request->set_header_field(restinio::http_field_t::host, type == PushType::UnifiedPush ? tokenUrl.host.c_str() : pushServer_.c_str());
request->set_header_field(restinio::http_field_t::host, pushServer_.c_str());
request->set_header_field(restinio::http_field_t::user_agent, "RESTinio client");
request->set_header_field(restinio::http_field_t::accept, "*/*");
request->set_header_field(restinio::http_field_t::content_type, "application/json");
if (type == PushType::UnifiedPush) {
Json::Value notification(Json::objectValue);
notification["message"] = Json::writeString(jsonBuilder_, std::move(json));
notification["topic"] = token;
notification["priority"] = highPriority ? 5 : 1;
request->set_body(Json::writeString(jsonBuilder_, std::move(json)));
} else {
// NOTE: see https://github.com/appleboy/gorush
Json::Value notification(Json::objectValue);
Json::Value tokens(Json::arrayValue);
tokens[0] = token;
notification["tokens"] = std::move(tokens);
notification["platform"] = type == PushType::Android ? 2 : 1;
notification["data"] = std::move(json);
notification["priority"] = highPriority ? "high" : "normal";
if (type == PushType::Android)
notification["time_to_live"] = 3600 * 24; // 24 hours
else {
const auto expiration = std::chrono::system_clock::now() + std::chrono::hours(24);
uint32_t exp = std::chrono::duration_cast<std::chrono::seconds>(expiration.time_since_epoch()).count();
notification["expiration"] = exp;
if (!topic.empty())
notification["topic"] = topic;
if (highPriority) {
Json::Value alert(Json::objectValue);
alert["title"]="hello";
notification["push_type"] = "alert";
notification["alert"] = alert;
notification["mutable_content"] = true;
} else {
notification["push_type"] = "background";
notification["content_available"] = true;
}
}
Json::Value notifications(Json::arrayValue);
notifications[0] = notification;
Json::Value content;
content["notifications"] = std::move(notifications);
request->set_body(Json::writeString(jsonBuilder_, content));
// NOTE: see https://github.com/appleboy/gorush
Json::Value notification(Json::objectValue);
Json::Value tokens(Json::arrayValue);
tokens[0] = token;
notification["tokens"] = std::move(tokens);
notification["platform"] = type == PushType::Android ? 2 : 1;
notification["data"] = std::move(json);
notification["priority"] = highPriority ? "high" : "normal";
if (type == PushType::Android)
notification["time_to_live"] = 3600 * 24; // 24 hours
else {
const auto expiration = std::chrono::system_clock::now() + std::chrono::hours(24);
uint32_t exp = std::chrono::duration_cast<std::chrono::seconds>(expiration.time_since_epoch()).count();
notification["expiration"] = exp;
}
Json::Value notifications(Json::arrayValue);
notifications[0] = notification;
Json::Value content;
content["notifications"] = std::move(notifications);
request->set_body(Json::writeString(jsonBuilder_, content));
request->add_on_state_change_callback([this, reqid]
(http::Request::State state, const http::Response& response){
if (state == http::Request::State::DONE){
@@ -1141,7 +1036,7 @@ DhtProxyServer::sendPushNotification(const std::string& token, Json::Value&& jso
}
catch (const std::exception &e){
if (logger_)
logger_->e("[proxy:server] [notification] error send push: %s", e.what());
logger_->e("[proxy:server] [notification] error send push: %i", e.what());
if (reqid) {
std::lock_guard<std::mutex> l(requestLock_);
requests_.erase(reqid);
@@ -1209,14 +1104,13 @@ DhtProxyServer::put(restinio::request_handle_t request,
logger_->d("[proxy:server] [put %s] %s %s", infoHash.toString().c_str(),
value->toString().c_str(), (permanent ? "permanent" : ""));
if (permanent) {
std::string pushToken, clientId, sessionId, platform, topic;
std::string pushToken, clientId, sessionId, platform;
auto& pVal = root["permanent"];
if (pVal.isObject()){
pushToken = pVal["key"].asString();
clientId = pVal["client_id"].asString();
platform = pVal["platform"].asString();
sessionId = pVal["session_id"].asString();
topic = pVal["topic"].asString();
}
std::lock_guard<std::mutex> lock(lockSearchPuts_);
auto timeout = std::chrono::steady_clock::now() + proxy::OP_TIMEOUT;
@@ -1239,7 +1133,7 @@ DhtProxyServer::put(restinio::request_handle_t request,
}
}
auto response = initHttpResponse(request->create_response());
response.append_body(Json::writeString(jsonBuilder_, pp.second.value->toJson()) + "\n");
response.append_body(Json::writeString(jsonBuilder_, value->toJson()) + "\n");
return response.done();
}
}
@@ -1251,17 +1145,34 @@ DhtProxyServer::put(restinio::request_handle_t request,
pput.value = value;
pput.expiration = timeout;
if (not pput.expireTimer) {
auto &ctx = io_context();
// cancel permanent put
pput.expireTimer = std::make_unique<asio::steady_timer>(io_context(), timeout);
pput.expireTimer = std::make_unique<asio::steady_timer>(ctx, timeout);
#ifdef OPENDHT_PUSH_NOTIFICATIONS
if (not pushToken.empty()) {
if (not pushToken.empty()){
bool isAndroid = platform == "android";
pput.pushToken = pushToken;
pput.clientId = clientId;
pput.type = getTypeFromString(platform);
if (topic.empty())
topic = getDefaultTopic(pput.type);
pput.topic = topic;
pput.type = isAndroid ? PushType::Android : PushType::iOS;
pput.sessionCtx = std::make_shared<PushSessionContext>(sessionId);
// notify push listen expire
auto jsonProvider = [infoHash, clientId, vid, sessionCtx = pput.sessionCtx](){
Json::Value json;
json["timeout"] = infoHash.toString();
json["to"] = clientId;
json["vid"] = std::to_string(vid);
std::lock_guard<std::mutex> l(sessionCtx->lock);
json["s"] = sessionCtx->sessionId;
return json;
};
if (!pput.expireNotifyTimer)
pput.expireNotifyTimer = std::make_unique<asio::steady_timer>(ctx,
timeout - proxy::OP_MARGIN);
else
pput.expireNotifyTimer->expires_at(timeout - proxy::OP_MARGIN);
pput.expireNotifyTimer->async_wait(std::bind(
&DhtProxyServer::handleNotifyPushListenExpire, this,
std::placeholders::_1, pushToken, std::move(jsonProvider), pput.type));
}
#endif
} else {
@@ -1274,34 +1185,12 @@ DhtProxyServer::put(restinio::request_handle_t request,
}
}
pput.expireTimer->expires_at(timeout);
if (pput.expireNotifyTimer)
pput.expireNotifyTimer->expires_at(timeout - proxy::OP_MARGIN);
}
pput.expireTimer->async_wait(std::bind(&DhtProxyServer::handleCancelPermamentPut, this,
std::placeholders::_1, infoHash, vid));
#ifdef OPENDHT_PUSH_NOTIFICATIONS
// notify put permanent expiration
if (pput.sessionCtx) {
auto jsonProvider = [infoHash, clientId, vid, sessionCtx = pput.sessionCtx](){
Json::Value json;
json["timeout"] = infoHash.toString();
json["to"] = clientId;
json["vid"] = std::to_string(vid);
std::lock_guard<std::mutex> l(sessionCtx->lock);
json["s"] = sessionCtx->sessionId;
return json;
};
if (!pput.expireNotifyTimer)
pput.expireNotifyTimer = std::make_unique<asio::steady_timer>(io_context(),
timeout - proxy::OP_MARGIN);
else
pput.expireNotifyTimer->expires_at(timeout - proxy::OP_MARGIN);
pput.expireNotifyTimer->async_wait(std::bind(
&DhtProxyServer::handleNotifyPushListenExpire, this,
std::placeholders::_1, pushToken, std::move(jsonProvider), pput.type, pput.topic));
}
#endif
}
dht_->put(infoHash, value, [this, request, value](bool ok){
if (ok){
auto response = initHttpResponse(request->create_response());
+394 -285
View File
File diff suppressed because it is too large. Load diff
+21 -28
View File
@@ -87,7 +87,7 @@ Url::Url(const std::string& url): url(url)
std::string
Url::toString() const
{
std::ostringstream ss;
std::stringstream ss;
if (not protocol.empty()) {
ss << protocol << "://";
}
@@ -567,12 +567,9 @@ Connection::async_connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, Conn
}
auto& base = ssl_socket_? ssl_socket_->lowest_layer() : *socket_;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
ConnectHandlerCb wcb = [this, &base, cb=std::move(cb)](const asio::error_code& ec, const asio::ip::tcp::endpoint& endpoint) {
ConnectHandlerCb wcb = [&base, cb=std::move(cb)](const asio::error_code& ec, const asio::ip::tcp::endpoint& endpoint) {
if (!ec) {
auto socket = base.native_handle();
local_address_ = base.local_endpoint().address();
// Once connected, set a keep alive on the TCP socket with 30 seconds delay
// This will generate broken pipes as soon as possible.
// Note this needs to be done once connected to have a valid native_handle()
@@ -617,12 +614,10 @@ Connection::async_connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, Conn
if (cb)
cb(ec, endpoint);
};
#pragma GCC diagnostic pop
if (ssl_socket_)
asio::async_connect(ssl_socket_->lowest_layer(), std::move(endpoints), wrapCallback(std::move(wcb)));
asio::async_connect(ssl_socket_->lowest_layer(), std::move(endpoints), wrapCallabck(std::move(wcb)));
else
asio::async_connect(*socket_, std::move(endpoints), wrapCallback(std::move(wcb)));
asio::async_connect(*socket_, std::move(endpoints), wrapCallabck(std::move(wcb)));
}
void
@@ -667,8 +662,8 @@ Connection::async_write(BytesHandlerCb cb)
if (cb) ctx_.post([cb](){ cb(asio::error::broken_pipe, 0); });
return;
}
if (ssl_socket_) asio::async_write(*ssl_socket_, write_buf_, wrapCallback(std::move(cb)));
else if (socket_) asio::async_write(*socket_, write_buf_, wrapCallback(std::move(cb)));
if (ssl_socket_) asio::async_write(*ssl_socket_, write_buf_, wrapCallabck(std::move(cb)));
else if (socket_) asio::async_write(*socket_, write_buf_, wrapCallabck(std::move(cb)));
else if (cb) ctx_.post([cb](){ cb(asio::error::operation_aborted, 0); });
}
@@ -680,8 +675,8 @@ Connection::async_read_until(const char* delim, BytesHandlerCb cb)
if (cb) ctx_.post([cb](){ cb(asio::error::broken_pipe, 0); });
return;
}
if (ssl_socket_) asio::async_read_until(*ssl_socket_, read_buf_, delim, wrapCallback(std::move(cb)));
else if (socket_) asio::async_read_until(*socket_, read_buf_, delim, wrapCallback(std::move(cb)));
if (ssl_socket_) asio::async_read_until(*ssl_socket_, read_buf_, delim, wrapCallabck(std::move(cb)));
else if (socket_) asio::async_read_until(*socket_, read_buf_, delim, wrapCallabck(std::move(cb)));
else if (cb) ctx_.post([cb](){ cb(asio::error::operation_aborted, 0); });
}
@@ -693,8 +688,8 @@ Connection::async_read_until(char delim, BytesHandlerCb cb)
if (cb) ctx_.post([cb](){ cb(asio::error::broken_pipe, 0); });
return;
}
if (ssl_socket_) asio::async_read_until(*ssl_socket_, read_buf_, delim, wrapCallback(std::move(cb)));
else if (socket_) asio::async_read_until(*socket_, read_buf_, delim, wrapCallback(std::move(cb)));
if (ssl_socket_) asio::async_read_until(*ssl_socket_, read_buf_, delim, wrapCallabck(std::move(cb)));
else if (socket_) asio::async_read_until(*socket_, read_buf_, delim, wrapCallabck(std::move(cb)));
else if (cb) ctx_.post([cb](){ cb(asio::error::operation_aborted, 0); });
}
@@ -706,8 +701,8 @@ Connection::async_read(size_t bytes, BytesHandlerCb cb)
if (cb) ctx_.post([cb](){ cb(asio::error::broken_pipe, 0); });
return;
}
if (ssl_socket_) asio::async_read(*ssl_socket_, read_buf_, asio::transfer_exactly(bytes), wrapCallback(std::move(cb)));
else if (socket_) asio::async_read(*socket_, read_buf_, asio::transfer_exactly(bytes), wrapCallback(std::move(cb)));
if (ssl_socket_) asio::async_read(*ssl_socket_, read_buf_, asio::transfer_exactly(bytes), wrapCallabck(std::move(cb)));
else if (socket_) asio::async_read(*socket_, read_buf_, asio::transfer_exactly(bytes), wrapCallabck(std::move(cb)));
else if (cb) ctx_.post([cb](){ cb(asio::error::operation_aborted, 0); });
}
@@ -728,15 +723,16 @@ Connection::async_read_some(size_t bytes, BytesHandlerCb cb)
else socket_->async_read_some(buf, onEnd);
}
const asio::ip::address&
Connection::local_address() const
{
return local_address_;
}
void
Connection::timeout(const std::chrono::seconds& timeout, HandlerCb cb)
Connection::timeout(const std::chrono::seconds timeout, HandlerCb cb)
{
if (!is_open()){
if (logger_)
logger_->e("[connection:%i] closed, can't timeout", id_);
if (cb)
cb(asio::error::operation_aborted);
return;
}
if (!timeout_timer_)
timeout_timer_ = std::make_unique<asio::steady_timer>(ctx_);
timeout_timer_->expires_at(std::chrono::steady_clock::now() + timeout);
@@ -1044,7 +1040,7 @@ Request::set_auth(const std::string& username, const std::string& password)
void
Request::build()
{
std::ostringstream request;
std::stringstream request;
bool append_body = !body_.empty();
// first header
@@ -1195,9 +1191,6 @@ Request::connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, HandlerCb cb)
else
conn_ = std::make_shared<Connection>(ctx_, false/*ssl*/, logger_);
if (conn_ && timeoutCb_)
conn_->timeout(timeout_, std::move(timeoutCb_));
// try to connect to any until one works
std::weak_ptr<Request> wthis = shared_from_this();
conn_->async_connect(std::move(endpoints), [wthis, cb, isHttps]
+2 -2
View File
@@ -32,7 +32,7 @@ namespace indexation {
* @return string that represent the blob into a readable way
*/
static std::string blobToString(const Blob &bl) {
std::ostringstream ss;
std::stringstream ss;
auto bn = bl.size() % 8;
auto n = bl.size() / 8;
@@ -46,7 +46,7 @@ static std::string blobToString(const Blob &bl) {
}
std::string Prefix::toString() const {
std::ostringstream ss;
std::stringstream ss;
ss << "Prefix : " << std::endl << "\tContent_ : \"";
ss << blobToString(content_);
+58
View File
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* 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/>.
*/
#include "infohash.h"
#include <functional>
#include <sstream>
#include <cstdio>
using namespace std::literals;
namespace dht {
const HexMap hex_map = {};
void
NodeExport::msgpack_unpack(msgpack::object o)
{
if (o.type != msgpack::type::MAP)
throw msgpack::type_error();
if (o.via.map.size < 2)
throw msgpack::type_error();
if (o.via.map.ptr[0].key.as<std::string_view>() != "id"sv)
throw msgpack::type_error();
if (o.via.map.ptr[1].key.as<std::string_view>() != "addr"sv)
throw msgpack::type_error();
const auto& addr = o.via.map.ptr[1].val;
if (addr.type != msgpack::type::BIN)
throw msgpack::type_error();
if (addr.via.bin.size > sizeof(sockaddr_storage))
throw msgpack::type_error();
id.msgpack_unpack(o.via.map.ptr[0].val);
sslen = addr.via.bin.size;
std::copy_n(addr.via.bin.ptr, addr.via.bin.size, (char*)&ss);
}
std::ostream& operator<< (std::ostream& s, const NodeExport& h)
{
msgpack::pack(s, h);
return s;
}
}
+108 -168
View File
@@ -53,8 +53,6 @@ struct NetworkEngine::PartialMessage {
time_point start;
time_point last_part;
std::unique_ptr<ParsedMessage> msg;
std::unique_ptr<asio::steady_timer> timeout;
std::unique_ptr<asio::steady_timer> final_timeout;
};
std::vector<Blob>
@@ -85,10 +83,10 @@ RequestAnswer::RequestAnswer(ParsedMessage&& msg)
{}
NetworkEngine::NetworkEngine(InfoHash& myid, NetworkConfig c,
const Sp<strand>& strand,
std::unique_ptr<DatagramSocket>&& sock,
const Sp<Logger>& log,
std::mt19937_64& rand,
Scheduler& scheduler,
decltype(NetworkEngine::onError)&& onError,
decltype(NetworkEngine::onNewNode)&& onNewNode,
decltype(NetworkEngine::onReportedAddr)&& onReportedAddr,
@@ -98,7 +96,6 @@ NetworkEngine::NetworkEngine(InfoHash& myid, NetworkConfig c,
decltype(NetworkEngine::onListen)&& onListen,
decltype(NetworkEngine::onAnnounce)&& onAnnounce,
decltype(NetworkEngine::onRefresh)&& onRefresh) :
strand_(strand),
onError(std::move(onError)),
onNewNode(std::move(onNewNode)),
onReportedAddr(std::move(onReportedAddr)),
@@ -110,19 +107,16 @@ NetworkEngine::NetworkEngine(InfoHash& myid, NetworkConfig c,
onRefresh(std::move(onRefresh)),
myid(myid), config(c), dht_socket(std::move(sock)), logger_(log), rd(rand),
cache(rd),
rate_limiter(config.max_req_per_sec)
{
dht_socket->setOnReceive([this](const ReceivedPacket& pkt) {
processMessage(pkt.data.data(), pkt.data.size(), pkt.from);
});
}
rate_limiter(config.max_req_per_sec),
scheduler(scheduler)
{}
NetworkEngine::~NetworkEngine() {
clear();
}
void
NetworkEngine::tellListener(const Sp<Node>& node, Tid socket_id, const InfoHash& hash, want_t want,
NetworkEngine::tellListener(Sp<Node> node, Tid socket_id, const InfoHash& hash, want_t want,
const Blob& ntoken, std::vector<Sp<Node>>&& nodes,
std::vector<Sp<Node>>&& nodes6, std::vector<Sp<Value>>&& values,
const Query& query, int version)
@@ -130,9 +124,9 @@ NetworkEngine::tellListener(const Sp<Node>& node, Tid socket_id, const InfoHash&
auto nnodes = bufferNodes(node->getFamily(), hash, want, nodes, nodes6);
try {
if (version >= 1) {
sendUpdateValues(node, hash, std::move(values), time(), ntoken, socket_id);
sendUpdateValues(node, hash, values, scheduler.time(), ntoken, socket_id);
} else {
sendNodesValues(node->getAddr(), socket_id, nnodes.first, nnodes.second, std::move(values), query, ntoken);
sendNodesValues(node->getAddr(), socket_id, nnodes.first, nnodes.second, values, query, ntoken);
}
} catch (const std::overflow_error& e) {
if (logger_)
@@ -141,7 +135,7 @@ NetworkEngine::tellListener(const Sp<Node>& node, Tid socket_id, const InfoHash&
}
void
NetworkEngine::tellListenerRefreshed(const Sp<Node>& n, Tid socket_id, const InfoHash&, const Blob& token, const std::vector<Value::Id>& values, int version)
NetworkEngine::tellListenerRefreshed(Sp<Node> n, Tid socket_id, const InfoHash&, const Blob& token, const std::vector<Value::Id>& values, int version)
{
msgpack::sbuffer buffer;
msgpack::packer<msgpack::sbuffer> pk(&buffer);
@@ -192,7 +186,7 @@ NetworkEngine::tellListenerRefreshed(const Sp<Node>& n, Tid socket_id, const Inf
}
void
NetworkEngine::tellListenerExpired(const Sp<Node>& n, Tid socket_id, const InfoHash&, const Blob& token, const std::vector<Value::Id>& values, int version)
NetworkEngine::tellListenerExpired(Sp<Node> n, Tid socket_id, const InfoHash&, const Blob& token, const std::vector<Value::Id>& values, int version)
{
msgpack::sbuffer buffer;
msgpack::packer<msgpack::sbuffer> pk(&buffer);
@@ -281,7 +275,7 @@ NetworkEngine::requestStep(Sp<Request> sreq)
if (not req.pending())
return;
const auto& now = time();
auto now = scheduler.time();
auto& node = *req.node;
if (req.isExpired(now)) {
// if (logger_)
@@ -295,19 +289,18 @@ NetworkEngine::requestStep(Sp<Request> sreq)
}
auto err = send(node.getAddr(), (char*)req.msg.data(), req.msg.size(), node.getReplyTime() < now - UDP_REPLY_TIME);
if (err == asio::error::network_unreachable ||
err == asio::error::host_unreachable ||
err == asio::error::address_family_not_supported ||
err == asio::error::broken_pipe ||
err == asio::error::no_permission)
if (err == ENETUNREACH ||
err == EHOSTUNREACH ||
err == EAFNOSUPPORT ||
err == EPIPE ||
err == EPERM)
{
node.setExpired();
if (not node.id)
requests.erase(req.tid);
} else {
req.last_try = now;
if (err != asio::error::try_again) {
if (err != EAGAIN) {
++req.attempt_count;
req.attempt_duration +=
req.attempt_duration + uniform_duration_distribution<>(0ms, ((duration)Node::MAX_RESPONSE_TIME)/4)(rd);
@@ -316,15 +309,9 @@ NetworkEngine::requestStep(Sp<Request> sreq)
}
}
std::weak_ptr<Request> wreq = sreq;
if (!req.expiration_timer)
req.expiration_timer = std::make_unique<asio::steady_timer>(context());
req.expiration_timer->expires_at(req.last_try + req.attempt_duration);
req.expiration_timer->async_wait([this,wreq](const asio::error_code& ec) {
if (ec != asio::error::operation_aborted) {
syncTime();
if (auto req = wreq.lock())
requestStep(req);
}
scheduler.add(req.last_try + req.attempt_duration, [this,wreq] {
if (auto req = wreq.lock())
requestStep(req);
});
}
}
@@ -339,7 +326,7 @@ NetworkEngine::sendRequest(const Sp<Request>& request)
auto& node = *request->node;
if (not node.id)
requests.emplace(request->tid, request);
request->start = time();
request->start = scheduler.time();
node.requested(request);
requestStep(request);
}
@@ -349,7 +336,7 @@ NetworkEngine::sendRequest(const Sp<Request>& request)
bool
NetworkEngine::rateLimit(const SockAddr& addr)
{
const auto& now = time();
const auto& now = scheduler.time();
// occasional IP limiter maintenance (a few times every second at max rate)
if (limiter_maintenance++ == config.max_peer_req_per_sec) {
@@ -373,20 +360,24 @@ NetworkEngine::rateLimit(const SockAddr& addr)
bool
NetworkEngine::isMartian(const SockAddr& addr)
{
if (addr.port() == 0)
if (addr.getPort() == 0)
return true;
switch(addr.protocol().family()) {
switch(addr.getFamily()) {
case AF_INET: {
const auto& sin = addr.address().to_v4().to_bytes();
return (sin[0] == 0) ||
((sin[0] & 0xE0) == 0xE0);
const auto& sin = addr.getIPv4();
const uint8_t* address = (const uint8_t*)&sin.sin_addr;
return (address[0] == 0) ||
((address[0] & 0xE0) == 0xE0);
}
case AF_INET6: {
const auto& sin6 = addr.address().to_v6().to_bytes();
return sin6[0] == 0xFF ||
(sin6[0] == 0xFE && (sin6[1] & 0xC0) == 0x80) ||
memcmp(sin6.data(), InfoHash::zero().data(), 16) == 0 ||
memcmp(sin6.data(), v4prefix, 12) == 0;
if (addr.getLength() < sizeof(sockaddr_in6))
return true;
const auto& sin6 = addr.getIPv6();
const uint8_t* address = (const uint8_t*)&sin6.sin6_addr;
return address[0] == 0xFF ||
(address[0] == 0xFE && (address[1] & 0xC0) == 0x80) ||
memcmp(address, InfoHash::zero().data(), 16) == 0 ||
memcmp(address, v4prefix, 12) == 0;
}
default:
return true;
@@ -411,21 +402,20 @@ NetworkEngine::isNodeBlacklisted(const SockAddr& addr) const
void
NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, SockAddr f)
{
syncTime();
const auto& from = f;/*.getMappedIPv4();
auto from = f.getMappedIPv4();
if (isMartian(from)) {
if (logger_)
logger_->w("Received packet from martian node %s", from.toString().c_str());
return;
}*/
}
if (isNodeBlacklisted(from)) {
//if (logger_)
// logger_->w("Received packet from blacklisted node %s", from.toString().c_str());
if (logger_)
logger_->w("Received packet from blacklisted node %s", from.toString().c_str());
return;
}
auto msg = std::make_unique<ParsedMessage>();
std::unique_ptr<ParsedMessage> msg {new ParsedMessage};
try {
msgpack::unpacked msg_res = msgpack::unpack((const char*)buf, buflen);
msg->msgpack_unpack(msg_res.get());
@@ -443,7 +433,7 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, SockAddr f)
return;
}
const auto& now = time();
const auto& now = scheduler.time();
// partial value data
if (msg->type == MessageType::ValueData) {
@@ -455,7 +445,7 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, SockAddr f)
rateLimit(from);
return;
}
if (pmsg_it->second.from != from) {
if (!pmsg_it->second.from.equals(from)) {
if (logger_)
logger_->d("Received partial message data from unexpected IP address");
rateLimit(from);
@@ -473,13 +463,8 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, SockAddr f)
} catch (...) {
return;
}
} else {
pmsg_it->second.timeout->expires_at(now + RX_TIMEOUT);
pmsg_it->second.timeout->async_wait([this, t=msg->tid](const asio::error_code& ec) {
if (ec != asio::error::operation_aborted)
maintainRxBuffer(t);
});
}
} else
scheduler.add(now + RX_TIMEOUT, std::bind(&NetworkEngine::maintainRxBuffer, this, msg->tid));
}
return;
}
@@ -514,27 +499,18 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, SockAddr f)
pmsg.msg = std::move(msg);
pmsg.start = now;
pmsg.last_part = now;
//scheduler.add(now + RX_MAX_PACKET_TIME, std::bind(&NetworkEngine::maintainRxBuffer, this, k));
//scheduler.add(now + RX_TIMEOUT, std::bind(&NetworkEngine::maintainRxBuffer, this, k));
pmsg.timeout = std::make_unique<asio::steady_timer>(context(), now + RX_TIMEOUT);
pmsg.final_timeout = std::make_unique<asio::steady_timer>(context(), now + RX_MAX_PACKET_TIME);
pmsg.timeout->async_wait([this,k](const asio::error_code& ec) {
if (ec != asio::error::operation_aborted)
maintainRxBuffer(k);
});
pmsg.final_timeout->async_wait([this,k](const asio::error_code& ec) {
if (ec != asio::error::operation_aborted)
maintainRxBuffer(k);
});
} else if (logger_)
logger_->e("Partial message with given TID %u already exists", k);
scheduler.add(now + RX_MAX_PACKET_TIME, std::bind(&NetworkEngine::maintainRxBuffer, this, k));
scheduler.add(now + RX_TIMEOUT, std::bind(&NetworkEngine::maintainRxBuffer, this, k));
} else
if (logger_)
logger_->e("Partial message with given TID already exists");
}
}
void
NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& from)
{
const auto& now = time();
const auto& now = scheduler.time();
auto node = cache.getNode(msg->id, from, now, true, msg->is_client);
if (msg->type == MessageType::ValueUpdate) {
@@ -591,10 +567,10 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
if (not req->setError(DhtProtocolException {msg->error_code}))
onError(req, DhtProtocolException {msg->error_code});
} else {
/*if (logIncoming_)
if (logIncoming_)
if (logger_)
logger_->w(msg->id, "[node %s %s] received unknown error message %u",
msg->id.toString().c_str(), from.toString().c_str(), msg->error_code);*/
msg->id.toString().c_str(), from.toString().c_str(), msg->error_code);
}
break;
}
@@ -606,7 +582,7 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
or r.getType() == MessageType::Refresh) {
r.node->authSuccess();
}
r.reply_time = time();
r.reply_time = scheduler.time();
deserializeNodes(*msg, from);
r.setDone(std::move(*msg));
@@ -638,7 +614,7 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
// logger_->d(msg->target, node->id, "[node %s] got 'find' request for %s (%d)", node->toString().c_str(), msg->target.toString().c_str(), msg->want);
++in_stats.find;
RequestAnswer answer = onFindNode(node, msg->target, msg->want);
auto nnodes = bufferNodes(from.protocol().family(), msg->target, msg->want, answer.nodes4, answer.nodes6);
auto nnodes = bufferNodes(from.getFamily(), msg->target, msg->want, answer.nodes4, answer.nodes6);
sendNodesValues(from, msg->tid, nnodes.first, nnodes.second, {}, {}, answer.ntoken);
break;
}
@@ -647,7 +623,7 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
// logger_->d(msg->info_hash, node->id, "[node %s] got 'get' request for %s", node->toString().c_str(), msg->info_hash.toString().c_str());
++in_stats.get;
RequestAnswer answer = onGetValues(node, msg->info_hash, msg->want, msg->query);
auto nnodes = bufferNodes(from.protocol().family(), msg->info_hash, msg->want, answer.nodes4, answer.nodes6);
auto nnodes = bufferNodes(from.getFamily(), msg->info_hash, msg->want, answer.nodes4, answer.nodes6);
sendNodesValues(from, msg->tid, nnodes.first, nnodes.second, answer.values, msg->query, answer.ntoken);
break;
}
@@ -677,7 +653,7 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
logger_->d(msg->info_hash, node->id, "[node %s] got 'listen' request for %s", node->toString().c_str(), msg->info_hash.toString().c_str());
++in_stats.listen;
RequestAnswer answer = onListen(node, msg->info_hash, msg->token, msg->socket_id, std::move(msg->query), msg->version);
auto nnodes = bufferNodes(from.protocol().family(), msg->info_hash, msg->want, answer.nodes4, answer.nodes6);
auto nnodes = bufferNodes(from.getFamily(), msg->info_hash, msg->want, answer.nodes4, answer.nodes6);
sendListenConfirmation(from, msg->tid);
break;
}
@@ -707,27 +683,23 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
void
insertAddr(msgpack::packer<msgpack::sbuffer>& pk, const SockAddr& addr)
{
size_t addr_len = std::min<size_t>(addr.getLength(),
(addr.getFamily() == AF_INET) ? sizeof(in_addr) : sizeof(in6_addr));
void* addr_ptr = (addr.getFamily() == AF_INET) ? (void*)&addr.getIPv4().sin_addr
: (void*)&addr.getIPv6().sin6_addr;
pk.pack("sa");
const auto& a = addr.address();
if (a.is_v4()) {
auto bytes = a.to_v4().to_bytes();
pk.pack_bin(bytes.size());
pk.pack_bin_body((char*)bytes.data(), bytes.size());
} else {
auto bytes = a.to_v6().to_bytes();
pk.pack_bin(bytes.size());
pk.pack_bin_body((char*)bytes.data(), bytes.size());
}
pk.pack_bin(addr_len);
pk.pack_bin_body((char*)addr_ptr, addr_len);
}
asio::error_code
int
NetworkEngine::send(const SockAddr& addr, const char *buf, size_t len, bool confirmed)
{
return dht_socket ? dht_socket->sendTo((const uint8_t*)buf, len, addr) : asio::error::not_connected;
return dht_socket ? dht_socket->sendTo(addr, (const uint8_t*)buf, len, confirmed) : ENOTCONN;
}
Sp<Request>
NetworkEngine::sendPing(const Sp<Node>& node, RequestCb&& on_done, RequestExpiredCb&& on_expired) {
NetworkEngine::sendPing(Sp<Node> node, RequestCb&& on_done, RequestExpiredCb&& on_expired) {
Tid tid (node->getNewTid());
msgpack::sbuffer buffer;
msgpack::packer<msgpack::sbuffer> pk(&buffer);
@@ -785,7 +757,7 @@ NetworkEngine::sendPong(const SockAddr& addr, Tid tid) {
}
Sp<Request>
NetworkEngine::sendFindNode(const Sp<Node>& n, const InfoHash& target, want_t want,
NetworkEngine::sendFindNode(Sp<Node> n, const InfoHash& target, want_t want,
RequestCb&& on_done, RequestExpiredCb&& on_expired) {
Tid tid (n->getNewTid());
msgpack::sbuffer buffer;
@@ -830,7 +802,7 @@ NetworkEngine::sendFindNode(const Sp<Node>& n, const InfoHash& target, want_t wa
Sp<Request>
NetworkEngine::sendGetValues(const Sp<Node>& n, const InfoHash& info_hash, const Query& query, want_t want,
NetworkEngine::sendGetValues(Sp<Node> n, const InfoHash& info_hash, const Query& query, want_t want,
RequestCb&& on_done, RequestExpiredCb&& on_expired) {
Tid tid (n->getNewTid());
msgpack::sbuffer buffer;
@@ -883,15 +855,18 @@ NetworkEngine::sendGetValues(const Sp<Node>& n, const InfoHash& info_hash, const
SockAddr deserializeIPv4(const uint8_t* ni) {
SockAddr addr;
addr.address(asio::ip::address_v4({ni[0], ni[1], ni[2], ni[3]}));
addr.port(ntohs(*reinterpret_cast<const uint16_t*>(ni + 4)));
addr.setFamily(AF_INET);
auto& sin = addr.getIPv4();
std::memcpy(&sin.sin_addr, ni, 4);
std::memcpy(&sin.sin_port, ni + 4, 2);
return addr;
}
SockAddr deserializeIPv6(const uint8_t* ni) {
SockAddr addr;
addr.address(asio::ip::address_v6({ni[0], ni[1], ni[2], ni[3], ni[4], ni[5], ni[6], ni[7],
ni[8], ni[9], ni[10], ni[11], ni[12], ni[13], ni[14], ni[15]}));
addr.port(ntohs(*reinterpret_cast<const uint16_t*>(ni + 16)));
addr.setFamily(AF_INET6);
auto& sin6 = addr.getIPv6();
std::memcpy(&sin6.sin6_addr, ni, 16);
std::memcpy(&sin6.sin6_port, ni + 16, 2);
return addr;
}
@@ -901,21 +876,21 @@ NetworkEngine::deserializeNodes(ParsedMessage& msg, const SockAddr& from) {
throw DhtProtocolException {DhtProtocolException::WRONG_NODE_INFO_BUF_LEN};
}
// deserialize nodes
const auto& now = time();
const auto& now = scheduler.time();
for (unsigned i = 0, n = msg.nodes4_raw.size() / NODE4_INFO_BUF_LEN; i < n; i++) {
const uint8_t* ni = msg.nodes4_raw.data() + i * NODE4_INFO_BUF_LEN;
const auto& ni_id = *reinterpret_cast<const InfoHash*>(ni);
if (ni_id == myid)
continue;
SockAddr addr = deserializeIPv4(ni + ni_id.size());
if (addr.address().is_loopback() and from.protocol().family() == AF_INET) {
auto port = addr.port();
if (addr.isLoopback() and from.getFamily() == AF_INET) {
auto port = addr.getPort();
addr = from;
addr.port(port);
addr.setPort(port);
}
if (isMartian(addr) || isNodeBlacklisted(addr))
continue;
msg.nodes4.emplace_back(cache.getNode(ni_id, std::move(addr), now, false));
msg.nodes4.emplace_back(cache.getNode(ni_id, addr, now, false));
onNewNode(msg.nodes4.back(), 0);
}
for (unsigned i = 0, n = msg.nodes6_raw.size() / NODE6_INFO_BUF_LEN; i < n; i++) {
@@ -924,29 +899,25 @@ NetworkEngine::deserializeNodes(ParsedMessage& msg, const SockAddr& from) {
if (ni_id == myid)
continue;
SockAddr addr = deserializeIPv6(ni + ni_id.size());
if (addr.address().is_loopback() and from.protocol().family() == AF_INET6) {
auto port = addr.port();
if (addr.isLoopback() and from.getFamily() == AF_INET6) {
auto port = addr.getPort();
addr = from;
addr.port(port);
addr.setPort(port);
}
if (isMartian(addr) || isNodeBlacklisted(addr))
continue;
msg.nodes6.emplace_back(cache.getNode(ni_id, std::move(addr), now, false));
msg.nodes6.emplace_back(cache.getNode(ni_id, addr, now, false));
onNewNode(msg.nodes6.back(), 0);
}
}
std::vector<Blob>
NetworkEngine::packValueHeader(msgpack::sbuffer& buffer, std::vector<Sp<Value>>::const_iterator b, std::vector<Sp<Value>>::const_iterator e) const
NetworkEngine::packValueHeader(msgpack::sbuffer& buffer, const std::vector<Sp<Value>>& st)
{
std::vector<Blob> svals;
auto svals = serializeValues(st);
size_t total_size = 0;
svals.reserve(std::distance(b, e));
for (; b != e; ++b) {
svals.emplace_back(packMsg(*b));
total_size += svals.back().size();
}
for (const auto& v : svals)
total_size += v.size();
msgpack::packer<msgpack::sbuffer> pk(&buffer);
pk.pack(KEY_REQ_VALUES);
@@ -1063,25 +1034,21 @@ NetworkEngine::bufferNodes(sa_family_t af, const InfoHash& id, std::vector<Sp<No
bnodes.resize(NODE4_INFO_BUF_LEN * nnode);
for (size_t i=0; i<nnode; i++) {
const Node& n = *nodes[i];
const auto& sin = n.getAddr();
auto port = htons(sin.port());
auto addr = sin.address().to_v4().to_bytes();
const auto& sin = n.getAddr().getIPv4();
auto dest = bnodes.data() + NODE4_INFO_BUF_LEN * i;
memcpy(dest, n.id.data(), HASH_LEN);
memcpy(dest + HASH_LEN, addr.data(), sizeof(in_addr));
memcpy(dest + HASH_LEN + sizeof(in_addr), &port, sizeof(in_port_t));
memcpy(dest + HASH_LEN, &sin.sin_addr, sizeof(in_addr));
memcpy(dest + HASH_LEN + sizeof(in_addr), &sin.sin_port, sizeof(in_port_t));
}
} else if (af == AF_INET6) {
bnodes.resize(NODE6_INFO_BUF_LEN * nnode);
for (size_t i=0; i<nnode; i++) {
const Node& n = *nodes[i];
const auto& sin6 = n.getAddr();
auto port = htons(sin6.port());
auto addr = sin6.address().to_v6().to_bytes();
const auto& sin6 = n.getAddr().getIPv6();
auto dest = bnodes.data() + NODE6_INFO_BUF_LEN * i;
memcpy(dest, n.id.data(), HASH_LEN);
memcpy(dest + HASH_LEN, addr.data(), sizeof(in6_addr));
memcpy(dest + HASH_LEN + sizeof(in6_addr), &port, sizeof(in_port_t));
memcpy(dest + HASH_LEN, &sin6.sin6_addr, sizeof(in6_addr));
memcpy(dest + HASH_LEN + sizeof(in6_addr), &sin6.sin6_port, sizeof(in_port_t));
}
}
return bnodes;
@@ -1106,7 +1073,7 @@ NetworkEngine::bufferNodes(sa_family_t af, const InfoHash& id, want_t want,
}
Sp<Request>
NetworkEngine::sendListen(const Sp<Node>& n,
NetworkEngine::sendListen(Sp<Node> n,
const InfoHash& hash,
const Query& query,
const Blob& token,
@@ -1175,7 +1142,7 @@ NetworkEngine::sendListenConfirmation(const SockAddr& addr, Tid tid) {
}
Sp<Request>
NetworkEngine::sendAnnounceValue(const Sp<Node>& n,
NetworkEngine::sendAnnounceValue(Sp<Node> n,
const InfoHash& infohash,
const Sp<Value>& value,
time_point created,
@@ -1188,12 +1155,11 @@ NetworkEngine::sendAnnounceValue(const Sp<Node>& n,
msgpack::packer<msgpack::sbuffer> pk(&buffer);
pk.pack_map(5+(config.network?1:0));
bool add_created = created < time();
pk.pack(KEY_A); pk.pack_map(add_created ? 5 : 4);
pk.pack(KEY_A); pk.pack_map((created < scheduler.time() ? 5 : 4));
pk.pack(KEY_REQ_ID); pk.pack(myid);
pk.pack(KEY_REQ_H); pk.pack(infohash);
auto v = packValueHeader(buffer, {value});
if (add_created) {
if (created < scheduler.time()) {
pk.pack(KEY_REQ_CREATION);
pk.pack(to_time_t(created));
}
@@ -1233,39 +1199,13 @@ NetworkEngine::sendAnnounceValue(const Sp<Node>& n,
return req;
}
void
NetworkEngine::sendUpdateValues(const Sp<Node>& n,
const InfoHash& infohash,
std::vector<Sp<Value>>&& values,
time_point created,
const Blob& token,
size_t socket_id)
{
size_t total_size = 0;
auto b = values.begin(), e = values.begin();
while (e != values.end()) {
if (total_size >= MAX_MESSAGE_VALUE_SIZE) {
sendUpdateValues(n, infohash, b, e, created, token, socket_id);
b = e;
total_size = 0;
}
total_size += (*e)->size();
++e;
}
if (b != e) {
sendUpdateValues(n, infohash, b, e, created, token, socket_id);
}
}
Sp<Request>
NetworkEngine::sendUpdateValues(const Sp<Node>& n,
NetworkEngine::sendUpdateValues(Sp<Node> n,
const InfoHash& infohash,
std::vector<Sp<Value>>::iterator begin,
std::vector<Sp<Value>>::iterator end,
const std::vector<Sp<Value>>& values,
time_point created,
const Blob& token,
size_t socket_id)
const size_t& socket_id)
{
Tid tid (n->getNewTid());
Tid sid (socket_id);
@@ -1274,13 +1214,13 @@ NetworkEngine::sendUpdateValues(const Sp<Node>& n,
msgpack::packer<msgpack::sbuffer> pk(&buffer);
pk.pack_map(5+(config.network?1:0));
pk.pack(KEY_A); pk.pack_map((created < time() ? 7 : 6));
pk.pack(KEY_A); pk.pack_map((created < scheduler.time() ? 7 : 6));
pk.pack(KEY_REQ_ID); pk.pack(myid);
pk.pack(KEY_VERSION); pk.pack(1);
pk.pack(KEY_REQ_H); pk.pack(infohash);
pk.pack(KEY_REQ_SID); pk.pack(sid);
auto v = packValueHeader(buffer, begin, end);
if (created < time()) {
auto v = packValueHeader(buffer, values);
if (created < scheduler.time()) {
pk.pack(KEY_REQ_CREATION);
pk.pack(to_time_t(created));
}
@@ -1306,7 +1246,7 @@ NetworkEngine::sendUpdateValues(const Sp<Node>& n,
}
Sp<Request>
NetworkEngine::sendRefreshValue(const Sp<Node>& n,
NetworkEngine::sendRefreshValue(Sp<Node> n,
const InfoHash& infohash,
const Value::Id& vid,
const Blob& token,
@@ -1415,11 +1355,11 @@ NetworkEngine::maintainRxBuffer(Tid tid)
{
auto msg = partial_messages.find(tid);
if (msg != partial_messages.end()) {
const auto& now = time();
const auto& now = scheduler.time();
if (msg->second.start + RX_MAX_PACKET_TIME < now
|| msg->second.last_part + RX_TIMEOUT < now) {
//if (logger_)
// logger_->w("Dropping expired partial message from %s", msg->second.from.toString().c_str());
if (logger_)
logger_->w("Dropping expired partial message from %s", msg->second.from.toString().c_str());
partial_messages.erase(msg);
}
}
+310 -66
View File
@@ -34,84 +34,328 @@
namespace dht {
namespace net {
/*
UdpSocket::UdpSocket(in_port_t port, const std::shared_ptr<Logger>& l)
: logger(l)
int
bindSocket(const SockAddr& addr, SockAddr& bound)
{
asio::io_service io_service;
asio::ip::udp::endpoint endpoint(asio::ip::udp::v4(), port);
s4.open(endpoint.protocol());
s4.set_option(asio::ip::udp::socket::reuse_address(true));
s4.bind(endpoint);
s4.non_blocking(true);
try {
asio::ip::udp::endpoint endpoint(asio::ip::udp::v6(), port);
s6.open(endpoint.protocol());
s6.set_option(asio::ip::udp::socket::reuse_address(true));
s6.bind(endpoint);
s6.non_blocking(true);
} catch (...) {
bool is_ipv6 = addr.getFamily() == AF_INET6;
int sock = socket(is_ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0);
if (sock < 0)
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));
#endif
if (is_ipv6)
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) {
rc = errno;
close(sock);
throw DhtException("Can't bind socket on " + addr.toString() + " " + strerror(rc));
}
sockaddr_storage ss;
socklen_t ss_len = sizeof(ss);
getsockname(sock, (sockaddr*)&ss, &ss_len);
bound = {ss, ss_len};
return sock;
}
UdpSocket::UdpSocket(const SockAddr& bind4, const SockAddr& bind6, const std::shared_ptr<Logger>& l)
: logger(l)
bool
setNonblocking(int fd, bool nonblocking)
{
if (bind4.isSet()) {
asio::io_service io_service;
asio::ip::udp::endpoint endpoint(asio::ip::udp::v4(), bind4.getPort());
s4.open(endpoint.protocol());
s4.set_option(asio::ip::udp::socket::reuse_address(true));
s4.bind(endpoint);
s4.non_blocking(true);
#ifdef _WIN32
unsigned long mode = !!nonblocking;
int rc = ioctlsocket(fd, FIONBIO, &mode);
return rc == 0;
#else
int rc = fcntl(fd, F_GETFL, 0);
if (rc < 0)
return false;
rc = fcntl(fd, F_SETFL, nonblocking ? (rc | O_NONBLOCK) : (rc & ~O_NONBLOCK));
return rc >= 0;
#endif
}
#ifdef _WIN32
void udpPipe(int fds[2])
{
int lst = socket(AF_INET, SOCK_DGRAM, 0);
if (lst < 0)
throw DhtException(std::string("Can't open socket: ") + strerror(WSAGetLastError()));
sockaddr_in inaddr;
sockaddr addr;
memset(&inaddr, 0, sizeof(inaddr));
memset(&addr, 0, sizeof(addr));
inaddr.sin_family = AF_INET;
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));
if (rc < 0) {
close(lst);
throw DhtException("Can't bind socket on " + print_addr((sockaddr*)&inaddr, sizeof(inaddr)) + " " + strerror(rc));
}
socklen_t len = sizeof(addr);
getsockname(lst, &addr, &len);
fds[0] = lst;
fds[1] = socket(AF_INET, SOCK_DGRAM, 0);
connect(fds[1], &addr, len);
}
#endif
UdpSocket::UdpSocket(in_port_t port, const std::shared_ptr<Logger>& l) : logger(l) {
SockAddr bind4;
bind4.setFamily(AF_INET);
bind4.setPort(port);
SockAddr bind6;
bind6.setFamily(AF_INET6);
bind6.setPort(port);
std::lock_guard<std::mutex> lk(lock);
openSockets(bind4, bind6);
}
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() {
stop();
if (rcv_thread.joinable())
rcv_thread.join();
}
int
UdpSocket::sendTo(const SockAddr& dest, const uint8_t* data, size_t size, 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;
}
if (bind6.isSet()) {
if (s < 0)
return EAFNOSUPPORT;
int flags = 0;
#ifdef MSG_CONFIRM
if (replied)
flags |= MSG_CONFIRM;
#endif
#ifdef MSG_NOSIGNAL
flags |= MSG_NOSIGNAL;
#endif
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));
if (err == EPIPE || err == ENOTCONN || err == ECONNRESET) {
std::lock_guard<std::mutex> lk(lock);
auto bind4 = std::move(bound4), bind6 = std::move(bound6);
openSockets(bind4, bind6);
return sendTo(dest, data, size, false);
}
return err;
}
return 0;
}
void
UdpSocket::openSockets(const SockAddr& bind4, const SockAddr& bind6)
{
stop();
if (rcv_thread.joinable())
rcv_thread.join();
int stopfds[2];
#ifndef _WIN32
auto status = pipe(stopfds);
if (status == -1) {
throw DhtException(std::string("Can't open pipe: ") + strerror(errno));
}
#else
udpPipe(stopfds);
#endif
int stop_readfd = stopfds[0];
stopfd = stopfds[1];
s4 = -1;
s6 = -1;
bound4 = {};
if (bind4) {
try {
asio::io_service io_service;
asio::ip::udp::endpoint endpoint(asio::ip::udp::v6(), bind6.getPort());
s6.open(endpoint.protocol());
s6.set_option(asio::ip::udp::socket::reuse_address(true));
s6.bind(endpoint);
s6.non_blocking(true);
} catch (...) {
s4 = bindSocket(bind4, bound4);
} catch (const DhtException& e) {
if (logger)
logger->e("Can't bind inet socket: %s", e.what());
}
}
#if 1
bound6 = {};
if (bind6) {
if (bind6.getPort() == 0) {
// Attempt to use the same port as IPv4 with IPv6
if (auto p4 = bound4.getPort()) {
auto b6 = bind6;
b6.setPort(p4);
try {
s6 = bindSocket(b6, bound6);
} catch (const DhtException& e) {
if (logger)
logger->e("Can't bind inet6 socket: %s", e.what());
}
}
}
if (s6 == -1) {
try {
s6 = bindSocket(bind6, bound6);
} catch (const DhtException& e) {
if (logger)
logger->e("Can't bind inet6 socket: %s", e.what());
}
}
}
#endif
if (s4 == -1 && s6 == -1) {
throw DhtException("Can't bind socket");
}
running = true;
rcv_thread = std::thread([this, stop_readfd, ls4=s4, ls6=s6]() mutable {
int selectFd = std::max({ls4, ls6, stop_readfd}) + 1;
try {
while (running) {
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(stop_readfd, &readfds);
if(ls4 >= 0)
FD_SET(ls4, &readfds);
if(ls6 >= 0)
FD_SET(ls6, &readfds);
int rc = select(selectFd, &readfds, nullptr, nullptr, nullptr);
if (rc < 0) {
if (errno != EINTR) {
if (logger)
logger->e("Select error: %s", strerror(errno));
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
if (not running)
break;
if (rc > 0) {
std::array<uint8_t, 1024 * 64> buf;
sockaddr_storage from;
socklen_t from_len = sizeof(from);
if (FD_ISSET(stop_readfd, &readfds)) {
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 (ls4 >= 0 && FD_ISSET(ls4, &readfds))
rc = recvfrom(ls4, (char*)buf.data(), buf.size(), 0, (sockaddr*)&from, &from_len);
else if (ls6 >= 0 && FD_ISSET(ls6, &readfds))
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.from = {from, from_len};
pkt.received = clock::now();
onReceived(std::move(pkts));
} else if (rc == -1) {
if (logger)
logger->e("Error receiving packet: %s", strerror(errno));
int err = errno;
if (err == EPIPE || err == ENOTCONN || err == ECONNRESET) {
if (not running) break;
std::unique_lock<std::mutex> lk(lock, std::try_to_lock);
if (lk.owns_lock()) {
if (not running) break;
if (ls4 >= 0) {
close(ls4);
try {
ls4 = bindSocket(bound4, bound4);
} catch (const DhtException& e) {
if (logger)
logger->e("Can't bind inet socket: %s", e.what());
}
}
if (ls6 >= 0) {
close(ls6);
try {
ls6 = bindSocket(bound6, bound6);
} catch (const DhtException& e) {
if (logger)
logger->e("Can't bind inet6 socket: %s", e.what());
}
}
if (ls4 < 0 && ls6 < 0)
break;
s4 = ls4;
s6 = ls6;
selectFd = std::max({ls4, ls6, stop_readfd}) + 1;
} else {
break;
}
}
}
}
}
} catch (const std::exception& e) {
if (logger)
logger->e("Error in UdpSocket rx thread: %s", e.what());
}
if (ls4 >= 0)
close(ls4);
if (ls6 >= 0)
close(ls6);
if (stop_readfd != -1)
close(stop_readfd);
if (stopfd != -1)
close(stopfd);
std::unique_lock<std::mutex> lk(lock, std::try_to_lock);
if (lk.owns_lock()) {
s4 = -1;
s6 = -1;
bound4 = {};
bound6 = {};
stopfd = -1;
}
});
}
void
UdpSocket::stop()
{
if (running.exchange(false)) {
auto sfd = stopfd;
if (sfd != -1 && write(sfd, "\0", 1) == -1) {
if (logger)
logger->e("Can't write to stop fd");
}
}
}
UdpSocket::~UdpSocket()
{
stop();
}
int UdpSocket::sendTo(const SockAddr& dest, const uint8_t* data, size_t size, bool replied)
{
asio::ip::udp::socket* sock = nullptr;
if (dest.isV4())
sock = &s4;
else if (dest.isV6())
sock = &s6;
else
return -1;
try {
asio::ip::udp::endpoint endpoint(dest.toAsio());
return sock->send_to(asio::buffer(data, size), endpoint);
} catch (...) {
return -1;
}
}
void UdpSocket::stop()
{
s4.cancel();
s4.close();
s6.cancel();
s6.close();
}
*/
}
}
+2 -29
View File
@@ -160,41 +160,14 @@ Node::closeSocket(Tid id)
std::string
Node::toString() const
{
std::ostringstream ss;
std::stringstream ss;
ss << (*this);
return ss.str();
}
std::ostream& operator<< (std::ostream& s, const Node& h)
{
s << h.id << " " << h.addr;
return s;
}
void
NodeExport::msgpack_unpack(msgpack::object o)
{
if (o.type != msgpack::type::MAP)
throw msgpack::type_error();
if (o.via.map.size < 2)
throw msgpack::type_error();
if (o.via.map.ptr[0].key.as<std::string_view>() != "id"sv)
throw msgpack::type_error();
if (o.via.map.ptr[1].key.as<std::string_view>() != "addr"sv)
throw msgpack::type_error();
const auto& maddr = o.via.map.ptr[1].val;
if (maddr.type != msgpack::type::BIN)
throw msgpack::type_error();
if (maddr.via.bin.size > sizeof(sockaddr_storage))
throw msgpack::type_error();
id.msgpack_unpack(o.via.map.ptr[0].val);
//addr = {(const sockaddr*)maddr.via.bin.ptr, (socklen_t)maddr.via.bin.size};
// TODO
}
std::ostream& operator<< (std::ostream& s, const NodeExport& h)
{
msgpack::pack(s, h);
s << h.id << " " << h.addr.toString();
return s;
}
+1 -1
View File
@@ -38,7 +38,7 @@ Sp<Node>
NodeCache::getNode(const InfoHash& id, const SockAddr& addr, time_point now, bool confirm, bool client) {
if (not id)
return std::make_shared<Node>(id, addr, rd, client);
return cache(addr.protocol().family()).getNode(id, addr, now, confirm, client, rd);
return cache(addr.getFamily()).getNode(id, addr, now, confirm, client, rd);
}
std::vector<Sp<Node>>
+2 -8
View File
@@ -28,12 +28,6 @@ OpValueCache::onValuesAdded(const std::vector<Sp<Value>>& vals, const system_clo
auto viop = values.emplace(v->id, v);
if (viop.second) {
newValues.emplace_back(v);
} else if (*viop.first->second.data != *v) {
// Special case for edition
if (v->seq > viop.first->second.data->seq) {
viop.first->second.data = v;
newValues.emplace_back(v);
}
} else {
viop.first->second.refCount++;
}
@@ -175,7 +169,7 @@ SearchCache::getOp(const Sp<Query>& q) const
}
size_t
SearchCache::listen(const ValueCallback& get_cb, const Sp<Query>& q, Value::Filter&& filter, const OnListen& onListen)
SearchCache::listen(const ValueCallback& get_cb, const Sp<Query>& q, const Value::Filter& filter, const OnListen& onListen)
{
// find exact match
auto op = getOp(q);
@@ -192,7 +186,7 @@ SearchCache::listen(const ValueCallback& get_cb, const Sp<Query>& q, Value::Filt
auto token = nextToken_++;
if (nextToken_ == 0)
nextToken_++;
return op->second->addListener(token, get_cb, q, std::move(filter)) ? token : 0;
return op->second->addListener(token, get_cb, q, filter) ? token : 0;
}
bool
+4 -4
View File
@@ -28,7 +28,7 @@ struct OpCacheValueStorage
Sp<Value> data {};
unsigned refCount {1};
system_clock::time_point updated {system_clock::time_point::min()};
explicit OpCacheValueStorage(Sp<Value> val) : data(std::move(val)) {}
explicit OpCacheValueStorage(Sp<Value> val) : data(val) {}
};
class OpValueCache {
@@ -107,12 +107,12 @@ public:
void onValuesAdded(const std::vector<Sp<Value>>& vals);
void onValuesExpired(const std::vector<Sp<Value>>& vals);
bool addListener(size_t token, const ValueCallback& cb, const Sp<Query>& q, Value::Filter&& filter) {
bool addListener(size_t token, const ValueCallback& cb, const Sp<Query>& q, const Value::Filter& filter) {
auto cached = cache.get(filter);
if (not cached.empty() and not cb(cached, false)) {
return false;
}
listeners.emplace(token, LocalListener{q, std::move(filter), cb});
listeners.emplace(token, LocalListener{q, filter, cb});
return true;
}
@@ -167,7 +167,7 @@ public:
SearchCache(SearchCache&&) = default;
using OnListen = std::function<size_t(Sp<Query>, ValueCallback, SyncCallback)>;
size_t listen(const ValueCallback& get_cb, const Sp<Query>& q, Value::Filter&& filter, const OnListen& onListen);
size_t listen(const ValueCallback& get_cb, const Sp<Query>& q, const Value::Filter& filter, const OnListen& onListen);
bool cancelListen(size_t gtoken, const time_point& now);
void cancelAll(const std::function<void(size_t)>& onCancel);
+11 -25
View File
@@ -18,6 +18,7 @@
#pragma once
#include "infohash.h"
#include "sockaddr.h"
#include "net.h"
#include <map>
@@ -151,10 +152,9 @@ bool
ParsedMessage::complete()
{
for (auto& e : value_parts) {
if (e.second.first > e.second.second.size()) {
//std::cout << "uncomplete part " << e.first << ": " << e.second.second.size() << "/" << e.second.first << std::endl;
//std::cout << "part " << e.first << ": " << e.second.second.size() << "/" << e.second.first << std::endl;
if (e.second.first > e.second.second.size())
return false;
}
}
for (auto& e : value_parts) {
msgpack::unpacked msg;
@@ -312,29 +312,15 @@ ParsedMessage::msgpack_unpack(const msgpack::object& msg)
throw msgpack::type_error();
auto l = parsedReq.sa->via.bin.size;
if (l == sizeof(in_addr)) {
addr = {asio::ip::address_v4({
(uint8_t)parsedReq.sa->via.bin.ptr[0],
(uint8_t)parsedReq.sa->via.bin.ptr[1],
(uint8_t)parsedReq.sa->via.bin.ptr[2],
(uint8_t)parsedReq.sa->via.bin.ptr[3]}), 0};
addr.setFamily(AF_INET);
auto& a = addr.getIPv4();
a.sin_port = 0;
std::copy_n(parsedReq.sa->via.bin.ptr, l, (char*)&a.sin_addr);
} else if (l == sizeof(in6_addr)) {
addr = {asio::ip::address_v6({
(uint8_t)parsedReq.sa->via.bin.ptr[0],
(uint8_t)parsedReq.sa->via.bin.ptr[1],
(uint8_t)parsedReq.sa->via.bin.ptr[2],
(uint8_t)parsedReq.sa->via.bin.ptr[3],
(uint8_t)parsedReq.sa->via.bin.ptr[4],
(uint8_t)parsedReq.sa->via.bin.ptr[5],
(uint8_t)parsedReq.sa->via.bin.ptr[6],
(uint8_t)parsedReq.sa->via.bin.ptr[7],
(uint8_t)parsedReq.sa->via.bin.ptr[8],
(uint8_t)parsedReq.sa->via.bin.ptr[9],
(uint8_t)parsedReq.sa->via.bin.ptr[10],
(uint8_t)parsedReq.sa->via.bin.ptr[11],
(uint8_t)parsedReq.sa->via.bin.ptr[12],
(uint8_t)parsedReq.sa->via.bin.ptr[13],
(uint8_t)parsedReq.sa->via.bin.ptr[14],
(uint8_t)parsedReq.sa->via.bin.ptr[15]}), 0};
addr.setFamily(AF_INET6);
auto& a = addr.getIPv6();
a.sin6_port = 0;
std::copy_n(parsedReq.sa->via.bin.ptr, l, (char*)&a.sin6_addr);
}
} else
addr = {};
+5 -4
View File
@@ -19,6 +19,7 @@
*/
#include "peer_discovery.h"
#include "network_utils.h"
#include "utils.h"
#include <asio.hpp>
@@ -152,7 +153,7 @@ PeerDiscovery::DomainPeerDiscovery::loopListener()
return;
}
if (cb)
cb(std::move(o.val), receiveFrom_);
cb(std::move(o.val), SockAddr{ receiveFrom_.data(), (socklen_t)receiveFrom_.size() });
}
} else {
throw msgpack::type_error{};
@@ -193,7 +194,7 @@ PeerDiscovery::DomainPeerDiscovery::publish(const asio::ip::udp::endpoint& peer)
if (not lrunning_)
return;
sockFd_.async_send_to(asio::buffer((const void*)sbuf_.data(), sbuf_.size()), peer,
sockFd_.async_send_to(asio::buffer(sbuf_.data(), sbuf_.size()), peer,
[logger=logger_, peer] (const asio::error_code& ec, size_t)
{
if (ec and (ec != asio::error::operation_aborted) and logger)
@@ -208,8 +209,8 @@ PeerDiscovery::DomainPeerDiscovery::publish(const asio::ip::udp::endpoint& peer)
void
PeerDiscovery::DomainPeerDiscovery::startPublish(const std::string &type, const msgpack::sbuffer &pack_buf)
{
msgpack::sbuffer pack_buf_c(pack_buf.size());
pack_buf_c.write(pack_buf.data(), pack_buf.size());
msgpack::sbuffer pack_buf_c;
pack_buf_c.write(pack_buf.data(),pack_buf.size());
std::lock_guard<std::mutex> lck(mtx_);
messages_[type] = std::move(pack_buf_c);
+2 -4
View File
@@ -21,7 +21,6 @@
#include "net.h"
#include "value.h"
#include <asio/steady_timer.hpp>
namespace dht {
struct Node;
@@ -75,14 +74,14 @@ struct Request {
Blob&& msg,
std::function<void(const Request&, ParsedMessage&&)> on_done,
std::function<void(const Request&, bool)> on_expired) :
node(std::move(node)), tid(tid), type(type), on_done(std::move(on_done)), on_expired(std::move(on_expired)), msg(std::move(msg)) { }
node(node), tid(tid), type(type), on_done(on_done), on_expired(on_expired), msg(std::move(msg)) { }
Request(MessageType type, Tid tid,
Sp<Node> node,
Blob&& msg,
std::function<void(const Request&, ParsedMessage&&)> on_done,
std::function<bool(const Request&, DhtProtocolException&&)> on_error,
std::function<void(const Request&, bool)> on_expired) :
node(std::move(node)), tid(tid), type(type), on_done(std::move(on_done)), on_error(std::move(on_error)), on_expired(std::move(on_expired)), msg(std::move(msg)) { }
node(node), tid(tid), type(type), on_done(on_done), on_error(on_error), on_expired(on_expired), msg(std::move(msg)) { }
Tid getTid() const { return tid; }
MessageType getType() const { return type; }
@@ -141,7 +140,6 @@ private:
duration attempt_duration {((duration)Node::MAX_RESPONSE_TIME)/2};
time_point start {time_point::min()}; /* time when the request is created. */
time_point last_try {time_point::min()}; /* time of the last attempt to process the request. */
std::unique_ptr<asio::steady_timer> expiration_timer;
std::function<void(const Request&, ParsedMessage&&)> on_done {};
std::function<bool(const Request&, DhtProtocolException&&)> on_error {};
+39 -73
View File
@@ -23,10 +23,6 @@
#include "listener.h"
#include "value_cache.h"
#include "op_cache.h"
#include "node.h"
#include "dht.h"
#include <asio/steady_timer.hpp>
namespace dht {
@@ -56,7 +52,7 @@ struct Dht::SearchNode {
struct AnnounceStatus {
Sp<net::Request> req {};
std::unique_ptr<asio::steady_timer> refresh {};
Sp<Scheduler::Job> refresh {};
time_point refresh_time;
AnnounceStatus(){};
AnnounceStatus(Sp<net::Request> r, time_point t): req(std::move(r)), refresh_time(t)
@@ -76,9 +72,8 @@ struct Dht::SearchNode {
struct CachedListenStatus {
ValueCache cache;
std::unique_ptr<asio::steady_timer> refresh {};
std::unique_ptr<asio::steady_timer> cacheExpirationJob {};
std::function<void()> onCacheExpired {};
Sp<Scheduler::Job> refresh {};
Sp<Scheduler::Job> cacheExpirationJob {};
Sp<net::Request> req {};
Tid socketId {0};
CachedListenStatus(ValueStateCallback&& cb, SyncCallback scb, Tid sid)
@@ -90,14 +85,6 @@ struct Dht::SearchNode {
req->node->closeSocket(socketId);
}
}
void scheduleCacheExpired(const time_point& t) {
cacheExpirationJob->expires_at(t);
cacheExpirationJob->async_wait([cb = onCacheExpired](const asio::error_code &ec){
if (ec != asio::error::operation_aborted)
cb();
});
}
};
using NodeListenerStatus = std::map<Sp<Query>, CachedListenStatus>;
@@ -114,7 +101,7 @@ struct Dht::SearchNode {
Blob token {}; /* last token the node sent to us after a get request */
time_point last_get_reply {time_point::min()}; /* last time received valid token */
std::unique_ptr<asio::steady_timer> syncJob {};
Sp<Scheduler::Job> syncJob {};
bool candidate {false}; /* A search node is candidate if the search is/was synced and this
node is a new candidate for inclusion. */
@@ -251,17 +238,18 @@ struct Dht::SearchNode {
getStatus.clear();
}
void onValues(const Sp<Query>& q, net::RequestAnswer&& answer, const TypeStore& types, const time_point& now)
void onValues(const Sp<Query>& q, net::RequestAnswer&& answer, const TypeStore& types, Scheduler& scheduler)
{
auto l = listenStatus.find(q);
if (l != listenStatus.end()) {
l->second.scheduleCacheExpired(l->second.cache.onValues(answer.values,
auto next = l->second.cache.onValues(answer.values,
answer.refreshed_values,
answer.expired_values, types, now));
answer.expired_values, types, scheduler.time());
scheduler.edit(l->second.cacheExpirationJob, next);
}
}
void onListenSynced(const Sp<Query>& q, bool synced = true, std::unique_ptr<asio::steady_timer> refreshJob = {}) {
void onListenSynced(const Sp<Query>& q, bool synced = true, Sp<Scheduler::Job> refreshJob = {}) {
auto l = listenStatus.find(q);
if (l != listenStatus.end()) {
if (l->second.refresh)
@@ -271,10 +259,11 @@ struct Dht::SearchNode {
}
}
void expireValues(const Sp<Query>& q, const time_point& now) {
void expireValues(const Sp<Query>& q, Scheduler& scheduler) {
auto l = listenStatus.find(q);
if (l != listenStatus.end()) {
l->second.scheduleCacheExpired(l->second.cache.expireValues(now));
auto next = l->second.cache.expireValues(scheduler.time());
scheduler.edit(l->second.cacheExpirationJob, next);
}
}
@@ -420,17 +409,7 @@ struct Dht::Search {
uint16_t tid;
time_point refill_time {time_point::min()};
time_point step_time {time_point::min()}; /* the time of the last search step */
std::unique_ptr<asio::steady_timer> nextSearchStep {};
std::function<void()> onSearchStep {};
void scheduleStep(const time_point& now) {
if (nextSearchStep) {
nextSearchStep->expires_at(now);
nextSearchStep->async_wait([cb=onSearchStep](const asio::error_code &ec){
if (ec != asio::error::operation_aborted)
cb();
});
}
}
Sp<Scheduler::Job> nextSearchStep {};
bool expired {false}; /* no node, or all nodes expired */
bool done {false}; /* search is over, cached for later */
@@ -453,9 +432,7 @@ struct Dht::Search {
/* Cache */
SearchCache cache;
//std::unique_ptr<asio::steady_timer> opExpirationJob;
std::unique_ptr<asio::steady_timer> opExpirationJob {};
Sp<Scheduler::Job> opExpirationJob;
~Search() {
if (opExpirationJob)
@@ -560,57 +537,46 @@ struct Dht::Search {
bool isAnnounced(Value::Id id) const;
bool isListening(time_point now, duration exp) const;
void get(const Value::Filter& f, const Sp<Query>& q, const QueryCallback& qcb, const GetCallback& gcb, const DoneCallback& dcb, const time_point& now) {
void get(const Value::Filter& f, const Sp<Query>& q, const QueryCallback& qcb, const GetCallback& gcb, const DoneCallback& dcb, Scheduler& scheduler) {
if (gcb or qcb) {
if (not cache.get(f, q, gcb, dcb)) {
const auto& now = scheduler.time();
callbacks.emplace(now, Get { now, f, q, qcb, gcb, dcb });
scheduleStep(now);
scheduler.edit(nextSearchStep, now);
}
}
}
size_t listen(const ValueCallback& cb, Value::Filter&& f, const Sp<Query>& q, const time_point& now) {
size_t listen(const ValueCallback& cb, const Value::Filter& f, const Sp<Query>& q, Scheduler& scheduler) {
//DHT_LOG.e(id, "[search %s IPv%c] listen", id.toString().c_str(), (af == AF_INET) ? '4' : '6');
return cache.listen(cb, q, std::move(f), [&](const Sp<Query>& q, ValueCallback vcb, SyncCallback scb){
return cache.listen(cb, q, f, [&](const Sp<Query>& q, ValueCallback vcb, SyncCallback scb){
done = false;
auto token = ++listener_token;
listeners.emplace(token, SearchListener{q, vcb, scb});
scheduleStep(now);
scheduler.edit(nextSearchStep, scheduler.time());
return token;
});
}
time_point expireListeners(const time_point& now) {
return cache.expire(now, [&](size_t t){
const auto& ll = listeners.find(t);
if (ll != listeners.cend()) {
auto query = ll->second.query;
listeners.erase(ll);
if (listeners.empty()) {
for (auto& sn : nodes) sn->cancelListen();
} else if (query) {
for (auto& sn : nodes) sn->cancelListen(query);
}
}
});
}
void scheduleListenerExpiration() {
opExpirationJob->async_wait([this](const asio::error_code &ec){
if (ec != asio::error::operation_aborted) {
opExpirationJob->expires_at(expireListeners(clock::now()));
scheduleListenerExpiration();
}
});
}
void cancelListen(size_t token, const time_point& now) {
if (cache.cancelListen(token, now)) {
if (opExpirationJob) {
opExpirationJob->expires_at(cache.getExpiration());
scheduleListenerExpiration();
}
}
void cancelListen(size_t token, Scheduler& scheduler) {
cache.cancelListen(token, scheduler.time());
if (not opExpirationJob)
opExpirationJob = scheduler.add(time_point::max(), [this,&scheduler]{
auto nextExpire = cache.expire(scheduler.time(), [&](size_t t){
const auto& ll = listeners.find(t);
if (ll != listeners.cend()) {
auto query = ll->second.query;
listeners.erase(ll);
if (listeners.empty()) {
for (auto& sn : nodes) sn->cancelListen();
} else if (query) {
for (auto& sn : nodes) sn->cancelListen(query);
}
}
});
scheduler.edit(opExpirationJob, nextExpire);
});
scheduler.edit(opExpirationJob, cache.getExpiration());
}
std::vector<Sp<Value>> getPut() const {
+44 -21
View File
@@ -71,20 +71,33 @@ SecureDht::~SecureDht() = default;
ValueType
SecureDht::secureType(ValueType&& type)
{
type.storePolicy = [type](InfoHash id, Sp<Value>& v, const InfoHash& nid, const SockAddr& a) {
if (v->isSigned())
return v->checkSignature();
type.storePolicy = [this,type](InfoHash id, Sp<Value>& v, const InfoHash& nid, const SockAddr& a) {
if (v->isSigned()) {
if (!v->signatureChecked) {
v->signatureChecked = true;
v->signatureValid = v->owner and v->owner->checkSignature(v->getToSign(), v->signature);
}
if (!v->signatureValid) {
if (logger_)
logger_->w("Signature verification failed");
return false;
}
}
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) {
if (not o->isSigned())
if (!o->isSigned())
return type.editPolicy(id, o, n, nid, a);
if (*o->owner != *n->owner or not n->isSigned()) {
if (o->owner != n->owner) {
if (logger_)
logger_->w("Edition forbidden: not signed or wrong owner.");
logger_->w("Edition forbidden: owner changed.");
return false;
}
if (not n->checkSignature()) {
if (!n->signatureChecked) {
n->signatureChecked = true;
n->signatureValid = o->owner and o->owner->checkSignature(n->getToSign(), n->signature);
}
if (!n->signatureValid) {
if (logger_)
logger_->w("Edition forbidden: signature verification failed.");
return false;
@@ -121,7 +134,7 @@ Sp<crypto::PublicKey>
SecureDht::getPublicKey(const InfoHash& node) const
{
if (node == getId())
return certificate_->getSharedPublicKey();
return std::make_shared<crypto::PublicKey>(certificate_->getPublicKey());
auto it = nodesPubKeys_.find(node);
if (it == nodesPubKeys_.end())
return nullptr;
@@ -217,7 +230,7 @@ SecureDht::findPublicKey(const InfoHash& node, const std::function<void(const Sp
}
findCertificate(node, [=](const Sp<crypto::Certificate>& crt) {
if (crt && *crt) {
auto pk = crt->getSharedPublicKey();
auto pk = std::make_shared<crypto::PublicKey>(crt->getPublicKey());
if (*pk) {
nodesPubKeys_[pk->getId()] = pk;
if (cb) cb(pk);
@@ -240,14 +253,19 @@ SecureDht::checkValue(const Sp<Value>& v)
#endif
return {};
}
if (v->decrypted) {
return v->decryptedValue;
}
v->decrypted = true;
try {
auto isDecrypted = v->isDecrypted();
if (auto decrypted_val = v->decrypt(*key_)) {
auto cacheValue = not isDecrypted and decrypted_val->owner;
if (cacheValue)
nodesPubKeys_[decrypted_val->owner->getId()] = decrypted_val->owner;
return decrypted_val;
Value decrypted_val (decrypt(*v));
if (decrypted_val.recipient == getId()) {
if (decrypted_val.owner)
nodesPubKeys_[decrypted_val.owner->getId()] = decrypted_val.owner;
v->decryptedValue = std::make_shared<Value>(std::move(decrypted_val));
return v->decryptedValue;
}
// Ignore values belonging to other people
} catch (const std::exception& e) {
if (logger_)
logger_->w("Could not decrypt value %s : %s", v->toString().c_str(), e.what());
@@ -255,12 +273,17 @@ SecureDht::checkValue(const Sp<Value>& v)
}
// Check signed values
else if (v->isSigned()) {
auto cacheValue = not v->isSignatureChecked() and enableCache_ and v->owner;
if (v->checkSignature()) {
if (cacheValue)
if (v->signatureChecked) {
return v->signatureValid ? v : Sp<Value>{};
}
v->signatureChecked = true;
if (v->owner and v->owner->checkSignature(v->getToSign(), v->signature)) {
v->signatureValid = true;
if (enableCache_)
nodesPubKeys_[v->owner->getId()] = v->owner;
return v;
} else if (logger_)
}
else if (logger_)
logger_->w("Signature verification failed for %s", v->toString().c_str());
}
// Forward normal values
@@ -329,7 +352,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_) {
if (callback)
callback(false, {});
return;
@@ -368,7 +391,7 @@ SecureDht::putSigned(const InfoHash& hash, Sp<Value> val, DoneCallback callback,
dht_->put(hash, val, callback, time_point::max(), permanent);
},
Value::IdFilter(val->id),
Where().id(val->id)
std::move(Where().id(val->id))
);
}
+13 -34
View File
@@ -37,32 +37,18 @@ public:
storedValues_.emplace(expiration, std::pair<InfoHash, Value::Id>(id, value.id));
}
void erase(const InfoHash& id, const Value& value, time_point expiration) {
totalSize_ -= value.size();
auto range = storedValues_.equal_range(expiration);
for (auto rit = range.first; rit != range.second;) {
if (rit->second.first == id && rit->second.second == value.id) {
totalSize_ -= value.size();
storedValues_.erase(rit);
return;
break;
} else
++rit;
}
// printf("StorageBucket::erase can't 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);
for (auto rit = range.first; rit != range.second;) {
if (rit->second.first == id && rit->second.second == value.id) {
storedValues_.erase(rit);
storedValues_.emplace(expiration, std::pair<InfoHash, Value::Id>(id, value.id));
return;
} else
++rit;
}
// printf("StorageBucket::refresh can't find value %s %016" PRIx64 "\n", id.to_c_str(), value.id);
insert(id, value, expiration);
}
size_t size() const { return totalSize_; }
std::pair<InfoHash, Value::Id> getOldest() const { return storedValues_.empty() ? std::pair<InfoHash, Value::Id>{} : storedValues_.begin()->second; }
std::pair<InfoHash, Value::Id> getOldest() const { return storedValues_.begin()->second; }
private:
std::multimap<time_point, std::pair<InfoHash, Value::Id>> storedValues_;
size_t totalSize_ {0};
@@ -72,7 +58,7 @@ struct ValueStorage {
Sp<Value> data {};
time_point created {};
time_point expiration {};
std::unique_ptr<asio::steady_timer> expiration_job {};
Sp<Scheduler::Job> expiration_job {};
StorageBucket* store_bucket {nullptr};
ValueStorage() {}
@@ -83,13 +69,12 @@ struct ValueStorage {
struct Storage {
time_point maintenance_time {};
std::unique_ptr<asio::steady_timer> maintenance_job {};
std::map<Sp<Node>, std::map<size_t, Listener>> listeners;
std::map<size_t, LocalListener> local_listeners {};
size_t listener_token {1};
/* The maximum number of values we store for a given hash. */
static constexpr unsigned MAX_VALUES {64 * 1024};
static constexpr unsigned MAX_VALUES {1024};
/**
* Changes caused by an operation on the storage.
@@ -101,8 +86,6 @@ struct Storage {
ssize_t values_diff;
/** Difference in number of listeners */
ssize_t listeners_diff;
/** Number of edited values */
size_t edited_values;
};
Storage() {}
@@ -161,14 +144,11 @@ struct Storage {
* @return time of the next expiration, time_point::max() if no expiration
*/
std::pair<ValueStorage*, time_point>
refresh(const InfoHash& id, const time_point& now, const Value::Id& vid, const TypeStore& types) {
refresh(const time_point& now, const Value::Id& vid, const TypeStore& types) {
for (auto& vs : values)
if (vs.data->id == vid) {
vs.created = now;
auto oldExp = vs.expiration;
vs.expiration = std::max(oldExp, now + types.getType(vs.data->type).expiration);
if (vs.store_bucket)
vs.store_bucket->refresh(id, *vs.data, oldExp, vs.expiration);
vs.expiration = std::max(vs.expiration, now + types.getType(vs.data->type).expiration);
return {&vs, vs.expiration};
}
return {nullptr, time_point::max()};
@@ -180,7 +160,7 @@ struct Storage {
local_listeners.erase(token);
}
Sp<Value> remove(const InfoHash& id, Value::Id);
StoreDiff remove(const InfoHash& id, Value::Id);
std::pair<ssize_t, std::vector<Sp<Value>>> expire(const InfoHash& id, time_point now);
@@ -233,7 +213,7 @@ Storage::store(const InfoHash& id, const Sp<Value>& value, time_point created, t
sb->insert(id, *value, expiration);
it->data = value;
total_size += size_diff;
return std::make_pair(&(*it), StoreDiff{size_diff, 0, 0, 1});
return std::make_pair(&(*it), StoreDiff{size_diff, 0, 0});
}
} else {
//DHT_LOG.DEBUG("Storing %s -> %s", id.toString().c_str(), value->toString().c_str());
@@ -243,13 +223,13 @@ Storage::store(const InfoHash& id, const Sp<Value>& value, time_point created, t
values.back().store_bucket = sb;
if (sb)
sb->insert(id, *value, expiration);
return std::make_pair(&values.back(), StoreDiff{size_new, 1, 0, 0});
return std::make_pair(&values.back(), StoreDiff{size_new, 1, 0});
}
}
return std::make_pair(nullptr, StoreDiff{});
}
Sp<Value>
Storage::StoreDiff
Storage::remove(const InfoHash& id, Value::Id vid)
{
auto it = std::find_if (values.begin(), values.end(), [&](const ValueStorage& vr) {
@@ -263,9 +243,8 @@ Storage::remove(const InfoHash& id, Value::Id vid)
if (it->expiration_job)
it->expiration_job->cancel();
total_size -= size;
auto value = it->data;
values.erase(it);
return value;
return {-size, -1, 0};
}
Storage::StoreDiff
@@ -275,7 +254,7 @@ Storage::clear()
ssize_t tot_size = total_size;
values.clear();
total_size = 0;
return {-tot_size, -num_values, 0, 0};
return {-tot_size, -num_values, 0};
}
std::pair<ssize_t, std::vector<Sp<Value>>>
+26 -33
View File
@@ -26,7 +26,7 @@
namespace dht {
constexpr const size_t IO_THREADS_MAX {512};
constexpr const size_t IO_THREADS_MAX {64};
ThreadPool&
ThreadPool::computation()
@@ -65,39 +65,33 @@ ThreadPool::run(std::function<void()>&& cb)
// launch new thread if necessary
if (not readyThreads_ && threads_.size() < maxThreads_) {
try {
threads_.emplace_back(std::make_unique<std::thread>([this]() {
while (true) {
std::function<void()> task;
threads_.emplace_back(std::make_unique<std::thread>([this]() {
while (true) {
std::function<void()> task;
// pick task from queue
{
std::unique_lock<std::mutex> l(lock_);
readyThreads_++;
cv_.wait(l, [&](){
return not running_ or not tasks_.empty();
});
readyThreads_--;
if (not running_)
break;
task = std::move(tasks_.front());
tasks_.pop();
}
// run task
try {
task();
} catch (const std::exception& e) {
// LOG_ERR("Exception running task: %s", e.what());
std::cerr << "Exception running task: " << e.what() << std::endl;
}
// pick task from queue
{
std::unique_lock<std::mutex> l(lock_);
readyThreads_++;
cv_.wait(l, [&](){
return not running_ or not tasks_.empty();
});
readyThreads_--;
if (not running_)
break;
task = std::move(tasks_.front());
tasks_.pop();
}
}));
} catch(const std::exception& e) {
std::cerr << "Exception starting thread: " << e.what() << std::endl;
if (threads_.empty())
throw;
}
// run task
try {
task();
} catch (const std::exception& e) {
// LOG_ERR("Exception running task: %s", e.what());
std::cerr << "Exception running task: " << e.what() << std::endl;
}
}
}));
}
// push task to queue
@@ -112,7 +106,6 @@ ThreadPool::stop()
{
std::lock_guard<std::mutex> l(lock_);
running_ = false;
tasks_ = {};
cv_.notify_all();
}
-150
View File
@@ -1,150 +0,0 @@
#include "udp_socket.h"
namespace dht {
namespace net {
class UdpSocket::SocketHandler : public std::enable_shared_from_this<UdpSocket::SocketHandler> {
public:
SocketHandler(const std::shared_ptr<strand>& strand, const udp::endpoint& endpoint)
: strand_(strand), socket_(strand->context())
{
asio::error_code ec;
socket_.open(endpoint.protocol(), ec);
if (ec)
throw std::runtime_error("Failed to open socket: " + ec.message());
socket_.set_option(asio::socket_base::reuse_address(true), ec);
if (ec)
throw std::runtime_error("Failed to set socket option: " + ec.message());
socket_.bind(endpoint, ec);
if (ec)
throw std::runtime_error("Failed to bind socket: " + ec.message());
}
void set_receive_callback(const ReceiveCallback& callback) {
receive_callback_ = callback;
}
void start_receive() {
receive_next();
}
void stop() {
socket_.close();
}
void send_to_async(std::vector<uint8_t> data, const udp::endpoint& to) {
auto ctx = std::make_shared<std::vector<uint8_t>>(std::move(data));
socket_.async_send_to(asio::buffer(*ctx), to, [ctx](const asio::error_code&, std::size_t) {});
}
asio::error_code send_to(const uint8_t* buf, size_t len, const udp::endpoint& to) {
asio::error_code ec;
socket_.send_to(asio::buffer(buf, len), to, 0, ec);
return ec;
}
udp::endpoint getBound() const {
return socket_.local_endpoint();
}
private:
void receive_next() {
socket_.async_receive_from(asio::buffer(receive_buffer_),
remote_endpoint_,
asio::bind_executor(*strand_, [self = shared_from_this()](const asio::error_code& error, std::size_t bytes) {
self->handle_receive(error, bytes);
self->receive_next();
}));
}
void handle_receive(const asio::error_code& error, std::size_t bytes) {
if (!error) {
if (receive_callback_) {
receive_callback_(ReceivedPacket{std::vector<uint8_t>(receive_buffer_.begin(), receive_buffer_.begin() + bytes),
remote_endpoint_/*, std::chrono::high_resolution_clock::now()*/});
}
} else {
// Handle error
}
}
std::shared_ptr<strand> strand_;
udp::socket socket_;
udp::endpoint remote_endpoint_;
std::array<uint8_t, 65536> receive_buffer_;
ReceiveCallback receive_callback_;
};
UdpSocket::UdpSocket(std::shared_ptr<strand> strand, const udp::endpoint& ipv4_endpoint, const udp::endpoint& ipv6_endpoint)
//: strand_(strand)
{
try {
ipv4_handler_ = std::make_shared<SocketHandler>(strand, ipv4_endpoint);
} catch (const std::exception&) {
}
// Try to use the same port for IPv6 if not specified
udp::endpoint ipv6_endpoint_stack;
if (ipv6_endpoint.port() == 0) {
ipv6_endpoint_stack = ipv6_endpoint;
ipv6_endpoint_stack.port(ipv4_handler_->getBound().port());
}
const auto& ipv6_endpoint_to_use = ipv6_endpoint.port() == 0 ? ipv6_endpoint_stack : ipv6_endpoint;
try {
ipv6_handler_ = std::make_shared<SocketHandler>(strand, ipv6_endpoint_to_use);
} catch (const std::exception&) {
}
if (!ipv4_handler_ && !ipv6_handler_)
throw std::runtime_error("Failed to bind sockets");
}
void UdpSocket::setOnReceive(const ReceiveCallback& callback) {
ipv4_handler_->set_receive_callback(callback);
ipv6_handler_->set_receive_callback(callback);
start_receive();
}
void UdpSocket::start_receive() {
ipv4_handler_->start_receive();
ipv6_handler_->start_receive();
}
void UdpSocket::stop() {
ipv4_handler_->stop();
ipv6_handler_->stop();
}
void UdpSocket::sendToAsync(std::vector<uint8_t> data, const udp::endpoint& to) {
auto handler = (to.address().is_v4()) ? ipv4_handler_ : ipv6_handler_;
handler->send_to_async(std::move(data), to);
}
asio::error_code UdpSocket::sendTo(const uint8_t* buf, size_t len, const udp::endpoint& to) {
auto handler = (to.address().is_v4()) ? ipv4_handler_ : ipv6_handler_;
return handler->send_to(buf, len, to);
}
bool UdpSocket::hasIPv4() const {
return ipv4_handler_ != nullptr;
}
bool UdpSocket::hasIPv6() const {
return ipv6_handler_ != nullptr;
}
udp::endpoint UdpSocket::getBound(sa_family_t af) const {
if (af == AF_INET && ipv4_handler_) {
return ipv4_handler_->getBound();
} else if (af == AF_INET6 && ipv6_handler_) {
return ipv6_handler_->getBound();
}
throw std::runtime_error("Invalid address family");
}
} // namespace net
} // namespace dht
+5 -7
View File
@@ -21,6 +21,7 @@
#endif
#include "utils.h"
#include "sockaddr.h"
#include "default_types.h"
/* An IPv4 equivalent to IN6_IS_ADDR_UNSPECIFIED */
@@ -34,12 +35,12 @@
namespace dht {
static constexpr std::array<uint8_t, 12> MAPPED_IPV4_PREFIX {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}};
const char* version() {
return PACKAGE_VERSION;
}
const HexMap hex_map = {};
std::pair<std::string, std::string>
splitPort(const std::string& s) {
if (s.empty())
@@ -60,7 +61,6 @@ splitPort(const std::string& s) {
return {s.substr(0,found), s.substr(found+1)};
}
/*
std::vector<SockAddr>
SockAddr::resolve(const std::string& host, const std::string& service)
{
@@ -100,14 +100,13 @@ SockAddr::setAddress(const char* address)
if (inet_pton(family, address, addr) <= 0)
throw std::runtime_error(std::string("Can't parse IP address: ") + strerror(errno));
}
*/
std::string
print_addr(const sockaddr* sa, socklen_t slen)
{
char hbuf[NI_MAXHOST];
char sbuf[NI_MAXSERV];
std::ostringstream out;
std::stringstream out;
if (sa and slen and !getnameinfo(sa, slen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV)) {
if (sa->sa_family == AF_INET6)
out << "[" << hbuf << "]";
@@ -126,7 +125,6 @@ print_addr(const sockaddr_storage& ss, socklen_t sslen)
return print_addr((const sockaddr*)&ss, sslen);
}
/*
bool
SockAddr::isUnspecified() const
{
@@ -232,7 +230,7 @@ SockAddr::getMappedIPv6()
bool operator==(const SockAddr& a, const SockAddr& b) {
return a.equals(b);
}*/
}
time_point from_time_t(std::time_t t) {
auto dt = system_clock::from_time_t(t) - system_clock::now();
+1 -57
View File
@@ -98,7 +98,7 @@ ValueType::DEFAULT_STORE_POLICY(InfoHash, const std::shared_ptr<Value>& v, const
size_t
Value::size() const
{
return cypher.size() + data.size() + signature.size() + user_type.size();
return cypher.size() + data.size() + signature.size() + user_type.size();
}
void
@@ -254,62 +254,6 @@ unpackId(const Json::Value& json, const std::string& key) {
}
#endif
void
Value::sign(const crypto::PrivateKey& key)
{
if (isEncrypted())
throw DhtException("Can't sign encrypted data.");
owner = key.getSharedPublicKey();
signature = key.sign(getToSign());
}
Value
Value::encrypt(const crypto::PrivateKey& from, const crypto::PublicKey& to)
{
if (isEncrypted())
throw DhtException("Data is already encrypted.");
setRecipient(to.getId());
sign(from);
Value nv {id};
nv.setCypher(to.encrypt(getToEncrypt()));
return nv;
}
bool
Value::checkSignature()
{
if (!signatureChecked) {
signatureChecked = true;
if (isSigned()) {
signatureValid = owner and owner->checkSignature(getToSign(), signature);
} else {
signatureValid = true;
}
}
return signatureValid;
}
Sp<Value>
Value::decrypt(const crypto::PrivateKey& key)
{
if (not decrypted) {
decrypted = true;
if (isEncrypted()) {
auto decryptedBlob = key.decrypt(cypher);
auto msg = msgpack::unpack((const char*)decryptedBlob.data(), decryptedBlob.size());
auto v = std::make_unique<Value>(id);
v->msgpack_unpack_body(msg.get());
if (v->recipient != key.getPublicKey().getId())
throw crypto::DecryptError("Recipient mismatch");
// Ignore values belonging to other people
if (not v->owner or not v->owner->checkSignature(v->getToSign(), v->signature))
throw crypto::DecryptError("Signature mismatch");
decryptedValue = std::move(v);
}
}
return decryptedValue;
}
bool
FieldValue::operator==(const FieldValue& vfd) const
{
+1 -1
View File
@@ -53,7 +53,7 @@ public:
CallbackQueue clear() {
std::vector<Sp<Value>> expired_values;
expired_values.reserve(values.size());
for (auto& v : values)
for (const auto& v : values)
expired_values.emplace_back(std::move(v.second.data));
values.clear();
CallbackQueue ret;
+2 -4
View File
@@ -62,8 +62,6 @@ DhtProxyTester::tearDown() {
nodePeer.join();
nodeClient.join();
serverProxy->stop();
bool done = false;
std::condition_variable cv;
std::mutex cv_m;
@@ -311,13 +309,13 @@ DhtProxyTester::testShutdownStop()
for (size_t i = 0; i < C; i++) {
auto nodeTest = std::make_shared<dht::DhtRunner>();
nodeTest->run(0, clientConfig);
nodeTest->put(key, dht::Value(mtu), [&](bool /*ok*/) {
nodeTest->put(key, dht::Value(mtu), [&](bool ok) {
callback_count++;
});
nodeTest->get(key, [&](const std::vector<std::shared_ptr<dht::Value>>& vals){
values.insert(values.end(), vals.begin(), vals.end());
return true;
},[&](bool /*ok*/){
},[&](bool ok){
callback_count++;
});
bool done = false;
+23 -91
View File
@@ -35,11 +35,9 @@ DhtRunnerTester::setUp() {
dht::DhtRunner::Config config;
config.dht_config.node_config.max_peer_req_per_sec = -1;
config.dht_config.node_config.max_req_per_sec = -1;
config.dht_config.node_config.max_store_size = -1;
config.dht_config.node_config.max_store_keys = -1;
node1.run(0, config);
node2.run(0, config);
node1.run(42222, config);
node2.run(42232, config);
node2.bootstrap(node1.getBound());
}
@@ -56,17 +54,15 @@ DhtRunnerTester::tearDown() {
node1.shutdown(shutdown);
node2.shutdown(shutdown);
std::unique_lock<std::mutex> lk(cv_m);
CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]{ return done == 2u; }));
CPPUNIT_ASSERT(cv.wait_for(lk, 12s, [&]{ return done == 2; }));
node1.join();
node2.join();
}
void
DhtRunnerTester::testConstructors() {
CPPUNIT_ASSERT(node1.getBoundPort());
CPPUNIT_ASSERT_EQUAL(node1.getBoundPort(), node1.getBound().port());
CPPUNIT_ASSERT(node2.getBoundPort());
CPPUNIT_ASSERT_EQUAL(node2.getBoundPort(), node2.getBound().port());
CPPUNIT_ASSERT(node1.getBoundPort() == 42222);
CPPUNIT_ASSERT(node2.getBoundPort() == 42232);
dht::DhtRunner::Config config {};
dht::DhtRunner::Context context {};
@@ -94,53 +90,34 @@ void
DhtRunnerTester::testListen() {
std::mutex mutex;
std::condition_variable cv;
std::atomic_uint valueCounta(0);
std::atomic_uint valueCountb(0);
std::atomic_uint valueCountc(0);
std::atomic_uint valueCountd(0);
std::atomic_uint valueCount(0);
unsigned putCount(0);
unsigned putOkCount1(0);
unsigned putOkCount2(0);
unsigned putOkCount3(0);
unsigned putOkCount(0);
auto a = dht::InfoHash::get("234");
auto b = dht::InfoHash::get("2345");
auto c = dht::InfoHash::get("23456");
auto d = dht::InfoHash::get("234567");
constexpr unsigned N = 2048;
constexpr unsigned N = 256;
constexpr unsigned SZ = 56 * 1024;
auto ftokena = node1.listen(a, [&](const std::vector<std::shared_ptr<dht::Value>>& values, bool expired) {
if (expired)
valueCounta -= values.size();
else
valueCounta += values.size();
auto ftokena = node1.listen(a, [&](const std::shared_ptr<dht::Value>&) {
valueCount++;
return true;
});
auto ftokenb = node1.listen(b, [&](const std::shared_ptr<dht::Value>&) {
/*if (expired)
valueCountb -= values.size();
else
valueCountb += values.size();*/
valueCountb++;
valueCount++;
return false;
});
auto ftokenc = node1.listen(c, [&](const std::vector<std::shared_ptr<dht::Value>>& values, bool expired) {
if (expired)
valueCountc -= values.size();
else
valueCountc += values.size();
auto ftokenc = node1.listen(c, [&](const std::shared_ptr<dht::Value>&) {
valueCount++;
return true;
});
auto ftokend = node1.listen(d, [&](const std::vector<std::shared_ptr<dht::Value>>& values, bool expired) {
if (expired)
valueCountd -= values.size();
else
valueCountd += values.size();
auto ftokend = node1.listen(d, [&](const std::shared_ptr<dht::Value>&) {
valueCount++;
return true;
});
@@ -153,21 +130,21 @@ DhtRunnerTester::testListen() {
node2.put(a, dht::Value("v1"), [&](bool ok) {
std::lock_guard<std::mutex> lock(mutex);
putCount++;
if (ok) putOkCount1++;
if (ok) putOkCount++;
cv.notify_all();
});
node2.put(b, dht::Value("v2"), [&](bool ok) {
std::lock_guard<std::mutex> lock(mutex);
putCount++;
if (ok) putOkCount2++;
if (ok) putOkCount++;
cv.notify_all();
});
auto bigVal = std::make_shared<dht::Value>();
bigVal->data = mtu;
node2.put(c, std::move(bigVal), [&](bool ok) {
node2.put(c, bigVal, [&](bool ok) {
std::lock_guard<std::mutex> lock(mutex);
putCount++;
if (ok) putOkCount3++;
if (ok) putOkCount++;
cv.notify_all();
});
}
@@ -175,9 +152,7 @@ DhtRunnerTester::testListen() {
{
std::unique_lock<std::mutex> lk(mutex);
CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]{ return putCount == N * 3u; }));
CPPUNIT_ASSERT_EQUAL(N, putOkCount1);
CPPUNIT_ASSERT_EQUAL(N, putOkCount2);
CPPUNIT_ASSERT_EQUAL(N, putOkCount3);
CPPUNIT_ASSERT_EQUAL(N * 3u, putOkCount);
}
CPPUNIT_ASSERT(ftokena.valid());
@@ -193,10 +168,7 @@ DhtRunnerTester::testListen() {
CPPUNIT_ASSERT(tokena);
CPPUNIT_ASSERT(tokenc);
CPPUNIT_ASSERT(tokend);
CPPUNIT_ASSERT_EQUAL(N, valueCounta.load());
CPPUNIT_ASSERT_EQUAL(1u, valueCountb.load());
CPPUNIT_ASSERT_EQUAL(N, valueCountc.load());
CPPUNIT_ASSERT_EQUAL(0u, valueCountd.load());
CPPUNIT_ASSERT_EQUAL(N * 2u + 1u, valueCount.load());
node1.cancelListen(a, tokena);
node1.cancelListen(b, std::move(ftokenb));
@@ -209,7 +181,6 @@ DhtRunnerTester::testIdOps() {
std::mutex mutex;
std::condition_variable cv;
unsigned valueCount(0);
unsigned valueCountEdit(0);
dht::DhtRunner::Config config2;
config2.dht_config.node_config.max_peer_req_per_sec = -1;
@@ -228,11 +199,6 @@ DhtRunnerTester::testIdOps() {
node2.run(42232, config2, std::move(context2));
node2.bootstrap(node1.getBound());
{
std::unique_lock<std::mutex> lk(mutex);
CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&]{ return valueCount == 1; }));
}
node1.findCertificate(node2.getId(), [&](const std::shared_ptr<dht::crypto::Certificate>& crt){
CPPUNIT_ASSERT(crt);
std::lock_guard<std::mutex> lk(mutex);
@@ -281,44 +247,10 @@ DhtRunnerTester::testIdOps() {
return true;
});
auto key2 = dht::InfoHash::get("key2");
auto editValue = std::make_shared<dht::Value>("v1");
node1.putSigned(key2, editValue, [&](bool ok){
CPPUNIT_ASSERT(ok);
std::lock_guard<std::mutex> lk(mutex);
valueCountEdit++;
cv.notify_all();
});
node2.listen(key2, [&](const std::vector<std::shared_ptr<dht::Value>>& values, bool expired){
for (const auto& v : values) {
if (v->seq == 0)
CPPUNIT_ASSERT_EQUAL("v1"s, dht::unpackMsg<std::string>(v->data));
else if (v->seq == 1)
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);
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 && valueCountEdit == 2; }));
CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&]{ return valueCount == 7; }));
}
// editValue->data = dht::packMsg("v2");
editValue = std::make_shared<dht::Value>(editValue->id);
editValue->data = dht::packMsg("v2");
node1.putSigned(key2, editValue, [&](bool ok){
CPPUNIT_ASSERT(ok);
std::lock_guard<std::mutex> lk(mutex);
valueCountEdit++;
cv.notify_all();
});
std::unique_lock<std::mutex> lk(mutex);
CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&]{ return valueCountEdit == 4; }));
}
void
@@ -332,7 +264,7 @@ DhtRunnerTester::testListenLotOfBytes() {
std::string data(10000, 'a');
auto foo = dht::InfoHash::get("foo");
constexpr unsigned N = 1024;
constexpr unsigned N = 50;
for (unsigned i=0; i<N; i++) {
node2.put(foo, data, [&](bool ok) {
-1
View File
@@ -47,7 +47,6 @@ HttpTester::setUp() {
void
HttpTester::tearDown() {
serverProxy->stop();
serverProxy.reset();
nodePeer->join();
}
+13 -18
View File
@@ -1,4 +1,7 @@
if (OPENDHT_STATIC)
if (OPENDHT_SHARED)
set (OPENDHT_LIBS opendht)
else ()
set (OPENDHT_LIBS opendht-static)
if (MSVC)
set (MSC_COMPAT_SOURCES ${MSC_COMPAT_DIR}/wingetopt.c)
endif ()
@@ -6,8 +9,8 @@ endif ()
function (configure_tool name extra_files)
add_executable (${name} ${name}.cpp ${extra_files})
add_dependencies(${name} opendht)
target_link_libraries (${name} LINK_PUBLIC opendht ${READLINE_LIBRARIES})
target_link_libraries (${name} LINK_PUBLIC ${READLINE_LIBRARIES})
target_link_libraries (${name} LINK_PUBLIC ${OPENDHT_LIBS})
if (MSVC)
target_sources(${name} PRIVATE ${MSC_COMPAT_SOURCES})
target_include_directories (${name} PRIVATE ${MSC_COMPAT_DIR})
@@ -26,7 +29,6 @@ endif ()
if (OPENDHT_C)
add_executable (dhtcnode dhtcnode.c)
add_dependencies(dhtcnode opendht-c)
target_link_libraries (dhtcnode LINK_PUBLIC opendht-c ${READLINE_LIBRARIES})
target_include_directories (dhtcnode SYSTEM PRIVATE ${PROJECT_SOURCE_DIR}/c)
endif ()
@@ -38,7 +40,7 @@ endif ()
install (TARGETS dhtnode dhtscanner dhtchat RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
if (OPENDHT_SYSTEMD)
if (NOT DEFINED OPENDHT_SYSTEMD_UNIT_FILE_LOCATION OR NOT OPENDHT_SYSTEMD_UNIT_FILE_LOCATION)
if (NOT DEFINED OPENDHT_SYSTEMD_UNIT_FILE_LOCATION)
execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} systemd --variable=systemdsystemunitdir
OUTPUT_VARIABLE SYSTEMD_UNIT_INSTALL_DIR)
message("-- Using Systemd unit installation directory by pkg-config: " ${SYSTEMD_UNIT_INSTALL_DIR})
@@ -46,30 +48,23 @@ if (OPENDHT_SYSTEMD)
message("-- Using Systemd unit installation directory requested: " ${OPENDHT_SYSTEMD_UNIT_FILE_LOCATION})
set(SYSTEMD_UNIT_INSTALL_DIR ${OPENDHT_SYSTEMD_UNIT_FILE_LOCATION})
endif()
string(REGEX REPLACE "[ \t\n]+" "" SYSTEMD_UNIT_INSTALL_DIR "${SYSTEMD_UNIT_INSTALL_DIR}")
set (systemdunitdir "${SYSTEMD_UNIT_INSTALL_DIR}")
configure_file (
systemd/dhtnode.service.in
systemd/dhtnode.service
@ONLY
)
if (SYSTEMD_UNIT_INSTALL_DIR)
string(REGEX REPLACE "[ \t\n]+" "" SYSTEMD_UNIT_INSTALL_DIR "${SYSTEMD_UNIT_INSTALL_DIR}")
set (systemdunitdir "${SYSTEMD_UNIT_INSTALL_DIR}")
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/systemd/dhtnode.service DESTINATION ${systemdunitdir})
install (FILES systemd/dhtnode.conf DESTINATION ${sysconfdir})
else()
message(WARNING "Systemd unit installation directory not found. The systemd unit won't be installed.")
endif()
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/systemd/dhtnode.service DESTINATION ${systemdunitdir})
install (FILES systemd/dhtnode.conf DESTINATION ${sysconfdir})
if (OPENDHT_PYTHON)
configure_file (
systemd/dhtcluster.service.in
systemd/dhtcluster.service
@ONLY
)
if (SYSTEMD_UNIT_INSTALL_DIR)
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/systemd/dhtcluster.service DESTINATION ${systemdunitdir})
install (FILES systemd/dhtcluster.conf DESTINATION ${sysconfdir})
endif()
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/systemd/dhtcluster.service DESTINATION ${systemdunitdir})
install (FILES systemd/dhtcluster.conf DESTINATION ${sysconfdir})
endif()
endif ()
-7
View File
@@ -11,10 +11,3 @@ dhtchat_LDFLAGS = -lopendht -lreadline -L@top_builddir@/src/.libs @GnuTLS_LIBS@
dhtscanner_SOURCES = dhtscanner.cpp
dhtscanner_LDFLAGS = -lopendht -lreadline -L@top_builddir@/src/.libs @GnuTLS_LIBS@
if ENABLE_C
bin_PROGRAMS += dhtcnode
dhtcnode_CFLAGS = -std=c11 -isystem @top_srcdir@/c -isystem @top_srcdir@/include
dhtcnode_SOURCES = dhtcnode.c
dhtcnode_LDFLAGS = -lopendht-c -lreadline -L@top_builddir@/c/.libs
endif
+59 -219
View File
@@ -1,53 +1,20 @@
/*
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* 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/>.
*/
#include <opendht_c.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdatomic.h>
#include <getopt.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <arpa/inet.h>
struct op_context {
dht_runner* runner;
atomic_bool stop;
};
struct listen_context {
dht_runner* runner;
dht_op_token* token;
size_t count;
int d;
};
bool dht_value_callback(const dht_value* value, bool expired, void* user_data)
{
struct listen_context* ctx = (struct listen_context*) user_data;
if (expired)
ctx->count--;
else
ctx->count++;
dht_data_view data = dht_value_get_data(value);
printf("Listen: %s value: %.*s (total %zu).\n", expired ? "expired" : "new", (int)data.size, data.data, ctx->count);
printf("Value callback %s: %.*s.\n", expired ? "expired" : "new", (int)data.size, data.data);
return true;
}
@@ -59,30 +26,16 @@ bool dht_get_callback(const dht_value* value, void* user_data)
return true;
}
void dht_get_done_callback(bool ok, void* user_data)
void dht_done_callback(bool ok, void* user_data)
{
dht_runner* runner = (dht_runner*)user_data;
printf("Get completed: %s\n", ok ? "success !" : "failure :-(");
printf("Done callback. %s\n", ok ? "Success !" : "Failure :-(");
}
void dht_put_done_callback(bool ok, void* user_data)
void op_context_free(void* user_data)
{
dht_runner* runner = (dht_runner*)user_data;
printf("Put completed: %s\n", ok ? "success !" : "failure :-(");
}
void dht_shutdown_callback(void* user_data)
{
printf("Stopped.\n");
struct op_context* ctx = (struct op_context*)user_data;
atomic_store(&ctx->stop, true);
}
void listen_context_free(void* user_data)
{
printf("listen_context_free.\n");
struct listen_context* ctx = (struct listen_context*)user_data;
dht_op_token_delete(ctx->token);
printf("op_context_free %d.\n", ctx->d);
free(ctx);
}
@@ -107,179 +60,66 @@ char* print_addr(const struct sockaddr* addr) {
return s;
}
struct dht_params {
bool help;
bool version;
bool generate_identity;
bool service;
bool peer_discovery;
bool log;
const char* bootstrap;
unsigned network;
in_port_t port;
};
static const struct option long_options[] = {
{"help", no_argument , NULL, 'h'},
{"port", required_argument, NULL, 'p'},
{"net", required_argument, NULL, 'n'},
{"bootstrap", required_argument, NULL, 'b'},
{"identity", no_argument , NULL, 'i'},
{"verbose", no_argument , NULL, 'v'},
{"service", no_argument , NULL, 's'},
{"peer-discovery", no_argument , NULL, 'D'},
{"no-rate-limit", no_argument , NULL, 'U'},
{"persist", required_argument, NULL, 'f'},
{"logfile", required_argument, NULL, 'l'},
{"syslog", no_argument , NULL, 'L'},
{"version", no_argument , NULL, 'V'},
{NULL, 0 , NULL, 0}
};
struct dht_params
parse_args(int argc, char **argv) {
struct dht_params params;
memset(&params, 0, sizeof params);
int opt;
while ((opt = getopt_long(argc, argv, "hisvDp:n:b:f:l:", long_options, NULL)) != -1) {
switch (opt) {
case 'p': {
int port_arg = atoi(optarg);
if (port_arg >= 0 && port_arg < 0x10000)
params.port = port_arg;
}
break;
case 'D':
params.peer_discovery = true;
break;
case 'n':
params.network = strtoul(optarg, NULL, 0);
break;
case 'b':
params.bootstrap = (optarg[0] == '=') ? optarg+1 : optarg;
break;
case 'h':
params.help = true;
break;
case 'v':
params.log = true;
break;
case 'i':
params.generate_identity = true;
break;
case 's':
params.service = true;
break;
case 'V':
params.version = true;
break;
default:
break;
}
}
return params;
}
dht_infohash parse_key(const char* key_str) {
dht_infohash key;
dht_infohash_from_hex_null(&key, key_str);
if (dht_infohash_is_zero(&key)) {
dht_infohash_get_from_string(&key, key_str);
printf("Using h(%s) = %s\n", key_str, dht_infohash_print(&key));
}
return key;
}
int main(int argc, char **argv)
int main()
{
struct dht_params params = parse_args(argc, argv);
dht_identity id = dht_identity_generate("testNode", NULL);
dht_infohash cert_id = dht_certificate_get_id(id.certificate);
printf("Cert ID: %s\n", dht_infohash_print(&cert_id));
if (params.version) {
printf("OpenDHT version %s\n", dht_version());
return EXIT_SUCCESS;
}
dht_publickey* pk = dht_certificate_get_publickey(id.certificate);
dht_infohash pk_id = dht_publickey_get_id(pk);
printf("PK ID: %s\n", dht_infohash_print(&pk_id));
dht_publickey_delete(pk);
pk = dht_privatekey_get_publickey(id.privatekey);
pk_id = dht_publickey_get_id(pk);
printf("Key ID: %s\n", dht_infohash_print(&pk_id));
dht_publickey_delete(pk);
dht_identity_delete(&id);
dht_runner* runner = dht_runner_new();
dht_runner_config dht_config;
dht_runner_config_default(&dht_config);
dht_config.peer_discovery = params.peer_discovery; // Look for other peers on the network
dht_config.peer_publish = params.peer_discovery; // Publish our own peer info
dht_config.dht_config.node_config.network = params.network;
dht_config.log = params.log;
dht_runner_run_config(runner, params.port, &dht_config);
dht_runner_run(runner, 4040);
if (params.bootstrap) {
printf("Bootstrap using %s\n", params.bootstrap);
dht_runner_bootstrap(runner, params.bootstrap, NULL);
dht_infohash h;
dht_infohash_random(&h);
printf("random hash: %s\n", dht_infohash_print(&h));
// Put data
const char* data_str = "yo, this is some data";
dht_value* val = dht_value_new_from_string(data_str);
dht_runner_put(runner, &h, val, dht_done_callback, runner, false);
dht_value_unref(val);
// Get data
dht_runner_get(runner, &h, dht_get_callback, dht_done_callback, runner);
// Listen for data
struct op_context* ctx = malloc(sizeof(struct op_context));
ctx->runner = runner;
ctx->d = 42;
dht_op_token* token = dht_runner_listen(runner, &h, dht_value_callback, op_context_free, ctx);
sleep(1);
dht_runner_bootstrap(runner, "bootstrap.jami.net", NULL);
sleep(2);
struct sockaddr** addrs = dht_runner_get_public_address(runner);
for (struct sockaddr** addrIt = addrs; *addrIt; addrIt++) {
struct sockaddr* addr = *addrIt;
char* addr_str = print_addr(addr);
free(addr);
printf("Found public address: %s\n", addr_str);
free(addr_str);
}
free(addrs);
char cmd[64];
char arg[64];
char value[256];
dht_infohash key;
while (true) {
const char* line_read = readline("> ");
if (!line_read)
break;
if (!*line_read)
continue;
add_history(line_read);
dht_runner_cancel_listen(runner, &h, token);
dht_op_token_delete(token);
memset(cmd, 0, sizeof cmd);
memset(arg, 0, sizeof arg);
memset(value, 0, sizeof value);
sscanf(line_read, "%64s %64s %256s", cmd, arg, value);
if (!strcmp(cmd, "la")) {
struct sockaddr** addrs = dht_runner_get_public_address(runner);
if (addrs) {
for (struct sockaddr** addrIt = addrs; *addrIt; addrIt++) {
struct sockaddr* addr = *addrIt;
char* addr_str = print_addr(addr);
free(addr);
printf("Found public address: %s\n", addr_str);
free(addr_str);
}
free(addrs);
}
}
else if (!strcmp(cmd, "ll")) {
key = dht_runner_get_node_id(runner);
printf("DHT node %s running on port %u\n", dht_infohash_print(&key), dht_runner_get_bound_port(runner, AF_INET));
}
else if (!strcmp(cmd, "g")) {
key = parse_key(arg);
dht_runner_get(runner, &key, dht_get_callback, dht_get_done_callback, runner);
}
else if (!strcmp(cmd, "l")) {
key = parse_key(arg);
struct listen_context* ctx = malloc(sizeof(struct listen_context));
ctx->runner = runner;
ctx->count = 0;
ctx->token = dht_runner_listen(runner, &key, dht_value_callback, listen_context_free, ctx);
}
else if (!strcmp(cmd, "p")) {
key = parse_key(arg);
dht_value* val = dht_value_new_from_string(value);
dht_runner_put(runner, &key, val, dht_put_done_callback, runner, true);
dht_value_unref(val);
}
else {
printf("Unkown command: %s\n", cmd);
}
}
// Graceful shutdown
printf("Stopping…\n");
struct op_context ctx;
ctx.runner = runner;
atomic_init(&ctx.stop, false);
dht_runner_shutdown(runner, dht_shutdown_callback, &ctx);
// Wait until shutdown callback is called
while (!atomic_load(&ctx.stop)) {
usleep(10000);
}
dht_runner_delete(runner);
return EXIT_SUCCESS;
return 0;
}
+1 -4
View File
@@ -43,7 +43,7 @@ void print_version() {
}
void print_usage() {
std::cout << "Usage: dhtnode [-v [-l logfile]] [-i] [-d] [-n network_id] [-p local_port] [-b bootstrap_host[:port]] [--proxyserver local_port] [--proxyserverssl local_port] [--bundleid bundleid] [--openpush endpoint]" << std::endl << std::endl;
std::cout << "Usage: dhtnode [-v [-l logfile]] [-i] [-d] [-n network_id] [-p local_port] [-b bootstrap_host[:port]] [--proxyserver local_port] [--proxyserverssl local_port]" << std::endl << std::endl;
print_info();
}
@@ -146,7 +146,6 @@ void cmd_loop(std::shared_ptr<DhtRunner>& node, dht_params& params
node->getNodeInfo([&](const std::shared_ptr<dht::NodeInfo>& nodeInfo) {
print_node_info(*nodeInfo);
std::cout << nodeInfo->ongoing_ops << " ongoing operations" << std::endl;
std::cout << "Storage has " << nodeInfo->storage_values << " values, using " << (nodeInfo->storage_size/1024) << " KB" << std::endl;
std::cout << "IPv4 stats:" << std::endl;
std::cout << nodeInfo->ipv4.toString() << std::endl;
std::cout << "IPv6 stats:" << std::endl;
@@ -550,8 +549,6 @@ main(int argc, char **argv)
#ifdef OPENDHT_PROXY_SERVER
ProxyServerConfig serverConfig;
serverConfig.pushServer = params.pushserver;
serverConfig.bundleId = params.bundle_id;
serverConfig.address = params.proxy_address;
if (params.proxyserverssl and params.proxy_id.first and params.proxy_id.second){
serverConfig.identity = params.proxy_id;
serverConfig.port = params.proxyserverssl;
+1 -1
View File
@@ -1 +1 @@
DHT_ARGS=-b bootstrap.jami.net -p 4222 -n 16
DHT_ARGS=-b bootstrap.ring.cx -p 4222 -n 16
+4 -14
View File
@@ -24,7 +24,7 @@
#include <opendht.h>
#include <opendht/log.h>
#include <opendht/crypto.h>
//#include <opendht/network_utils.h>
#include <opendht/network_utils.h>
#ifdef OPENDHT_INDEXATION
#include <opendht/indexation/pht.h>
#endif
@@ -131,10 +131,8 @@ struct dht_params {
in_port_t proxyserver {0};
in_port_t proxyserverssl {0};
std::string proxyclient {};
std::string proxy_address {};
std::string pushserver {};
std::string devicekey {};
std::string bundle_id {};
std::string persist_path {};
dht::crypto::Identity id {};
dht::crypto::Identity proxy_id {};
@@ -187,8 +185,8 @@ getDhtConfig(dht_params& params)
context.logger = dht::log::getStdLogger();
}
if (context.logger) {
context.statusChangedCallback = [logger = context.logger](dht::DhtNodeStatus status) {
logger->WARN("Connectivity changed: IPv4: %s, IPv6: %s", dht::statusToStr(status.ipv4), dht::statusToStr(status.ipv6));
context.statusChangedCallback = [logger = *context.logger](dht::NodeStatus status4, dht::NodeStatus status6) {
logger.WARN("Connectivity changed: IPv4: %s, IPv6: %s", dht::statusToStr(status4), dht::statusToStr(status6));
};
}
return {std::move(config), std::move(context)};
@@ -225,14 +223,12 @@ static const constexpr struct option long_options[] = {
{"syslog", no_argument , nullptr, 'L'},
{"proxyserver", required_argument, nullptr, 'S'},
{"proxyserverssl", required_argument, nullptr, 'e'},
{"proxy-addr", required_argument, nullptr, 'a'},
{"proxy-certificate", required_argument, nullptr, 'w'},
{"proxy-privkey", required_argument, nullptr, 'K'},
{"proxy-privkey-password", required_argument, nullptr, 'M'},
{"proxyclient", required_argument, nullptr, 'C'},
{"pushserver", required_argument, nullptr, 'y'},
{"devicekey", required_argument, nullptr, 'z'},
{"bundleid", required_argument, nullptr, 'u'},
{"version", no_argument , nullptr, 'V'},
{nullptr, 0 , nullptr, 0}
};
@@ -243,7 +239,7 @@ parseArgs(int argc, char **argv) {
int opt;
std::string privkey;
std::string proxy_privkey;
while ((opt = getopt_long(argc, argv, "hidsvODUPp:n:b:f:l:", long_options, nullptr)) != -1) {
while ((opt = getopt_long(argc, argv, "hidsvDUPp:n:b:f:l:", long_options, nullptr)) != -1) {
switch (opt) {
case 'p': {
int port_arg = atoi(optarg);
@@ -272,9 +268,6 @@ parseArgs(int argc, char **argv) {
case 'D':
params.peer_discovery = true;
break;
case 'a':
params.proxy_address = optarg;
break;
case 'y':
params.pushserver = optarg;
break;
@@ -343,9 +336,6 @@ parseArgs(int argc, char **argv) {
case 'k':
privkey = optarg;
break;
case 'u':
params.bundle_id = optarg;
break;
case 'K':
proxy_privkey = optarg;
break;