mirror of
https://github.com/savoirfairelinux/opendht.git
synced 2026-11-08 12:28:42 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aee08022fc | ||
|
|
0f8bb3301a |
No files matched your search
+23
-59
@@ -7,42 +7,16 @@ jobs:
|
||||
name: Ubuntu/GCC Autotools build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Dependencies
|
||||
run: |
|
||||
sudo apt update && \
|
||||
sudo apt install libncurses5-dev libreadline-dev nettle-dev libasio-dev \
|
||||
libgnutls28-dev libuv1-dev python3-pip python3-dev python3-setuptools libcppunit-dev libjsoncpp-dev \
|
||||
autotools-dev autoconf libfmt-dev libmsgpack-dev libargon2-0-dev
|
||||
- name: Cython
|
||||
run: sudo pip3 install Cython
|
||||
- name: llhttp
|
||||
run: |
|
||||
mkdir llhttp
|
||||
wget https://github.com/nodejs/llhttp/archive/refs/tags/release/v9.2.0.tar.gz -O llhttp.tar.gz
|
||||
tar -xzf llhttp.tar.gz -C llhttp --strip-components=1
|
||||
cd llhttp && cmake -DCMAKE_INSTALL_PREFIX=/usr . && make -j2 && sudo make install
|
||||
cd ../ && rm -rf llhttp*
|
||||
- name: expected-lite
|
||||
run: |
|
||||
sudo mkdir /usr/local/include/nonstd && \
|
||||
sudo wget https://raw.githubusercontent.com/martinmoene/expected-lite/master/include/nonstd/expected.hpp \
|
||||
-O /usr/local/include/nonstd/expected.hpp
|
||||
- name: restinio
|
||||
run: |
|
||||
mkdir restinio && cd restinio
|
||||
wget https://github.com/Stiffstream/restinio/releases/download/v.0.7.2/restinio-0.7.2.tar.bz2
|
||||
ls -l && tar -xjf restinio-0.7.2.tar.bz2
|
||||
cd restinio-0.7.2/dev
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DRESTINIO_TEST=Off -DRESTINIO_SAMPLE=Off -DRESTINIO_BENCHMARK=Off \
|
||||
-DRESTINIO_WITH_SOBJECTIZER=Off -DRESTINIO_DEP_STANDALONE_ASIO=system -DRESTINIO_DEP_LLHTTP=system \
|
||||
-DRESTINIO_DEP_FMT=system -DRESTINIO_DEP_EXPECTED_LITE=system .
|
||||
sudo make install
|
||||
cd ../../ && rm -rf restinio*
|
||||
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 --enable-proxy-client
|
||||
./configure
|
||||
- name: Build
|
||||
run: make
|
||||
|
||||
@@ -50,15 +24,12 @@ jobs:
|
||||
name: Ubuntu/GCC Meson build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Dependencies
|
||||
run: |
|
||||
sudo apt update && \
|
||||
sudo apt install ninja-build libncurses5-dev libreadline-dev nettle-dev libasio-dev \
|
||||
libgnutls28-dev libuv1-dev python3-dev python3-setuptools python3-pip \
|
||||
libcppunit-dev libjsoncpp-dev libfmt-dev libmsgpack-dev libargon2-0-dev
|
||||
- name: Python dependencies
|
||||
run: sudo pip3 install meson Cython
|
||||
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
|
||||
@@ -68,17 +39,16 @@ jobs:
|
||||
name: Ubuntu/GCC minimal build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Dependencies
|
||||
run: |
|
||||
sudo apt update && \
|
||||
sudo apt install libncurses5-dev libreadline-dev nettle-dev libfmt-dev \
|
||||
sudo apt install libncurses5-dev libreadline-dev nettle-dev \
|
||||
libgnutls28-dev libcppunit-dev libmsgpack-dev libargon2-0-dev
|
||||
- name: Configure
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Debug \
|
||||
-DOPENDHT_C=Off -DBUILD_TESTING=On -DOPENDHT_PEER_DISCOVERY=Off -DOPENDHT_PYTHON=Off \
|
||||
-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
|
||||
@@ -87,30 +57,24 @@ jobs:
|
||||
|
||||
build-macos:
|
||||
name: macOS/Clang build
|
||||
runs-on: macOS-15
|
||||
runs-on: macos-11
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Dependencies
|
||||
run: |
|
||||
brew install msgpack-cxx asio gnutls nettle readline fmt jsoncpp argon2 openssl cppunit
|
||||
|
||||
- name: expected-lite
|
||||
run: |
|
||||
sudo mkdir -p /usr/local/include/nonstd && \
|
||||
sudo wget https://raw.githubusercontent.com/martinmoene/expected-lite/master/include/nonstd/expected.hpp \
|
||||
-O /usr/local/include/nonstd/expected.hpp
|
||||
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/Stiffstream/restinio/releases/download/v.0.7.2/restinio-0.7.2.tar.bz2
|
||||
ls -l && tar -xjf restinio-0.7.2.tar.bz2
|
||||
cd restinio-0.7.2/dev
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DRESTINIO_TEST=Off -DRESTINIO_SAMPLE=Off -DRESTINIO_BENCHMARK=Off \
|
||||
-DRESTINIO_WITH_SOBJECTIZER=Off -DRESTINIO_DEP_STANDALONE_ASIO=system -DRESTINIO_DEP_LLHTTP=system \
|
||||
-DRESTINIO_DEP_FMT=system -DRESTINIO_DEP_EXPECTED_LITE=system .
|
||||
make -j2 && sudo make install
|
||||
cd ../../ && rm -rf restinio*
|
||||
wget https://github.com/aberaud/restinio/archive/bbaa034dbcc7555ce67df0f8a1475591a7441733.tar.gz
|
||||
ls -l && tar -xzf bbaa034dbcc7555ce67df0f8a1475591a7441733.tar.gz
|
||||
cd restinio-bbaa034dbcc7555ce67df0f8a1475591a7441733/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
|
||||
run: |
|
||||
@@ -120,7 +84,7 @@ jobs:
|
||||
export CPPFLAGS="-I/usr/local/opt/openssl@3/include"
|
||||
export PKG_CONFIG_PATH="/usr/local/opt/openssl@3/lib/pkgconfig"
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Debug \
|
||||
-DOPENDHT_C=On -DBUILD_TESTING=On -DOPENDHT_PEER_DISCOVERY=On -DOPENDHT_PYTHON=Off \
|
||||
-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
|
||||
|
||||
- name: Build
|
||||
|
||||
@@ -6,33 +6,26 @@ jobs:
|
||||
name: Clang Static Analysis
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: deps
|
||||
run: |
|
||||
sudo apt update && \
|
||||
sudo apt install libncurses5-dev libreadline-dev nettle-dev \
|
||||
libgnutls28-dev libuv1-dev python3-dev python3-setuptools libcppunit-dev libjsoncpp-dev \
|
||||
autotools-dev autoconf libfmt-dev libmsgpack-dev libargon2-0-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 libasio-dev \
|
||||
llvm llvm-dev clang clang-tools && \
|
||||
sudo apt remove gcc g++
|
||||
|
||||
- name: expected-lite
|
||||
run: |
|
||||
sudo mkdir /usr/include/nonstd && \
|
||||
sudo wget https://raw.githubusercontent.com/martinmoene/expected-lite/master/include/nonstd/expected.hpp \
|
||||
-O /usr/include/nonstd/expected.hpp
|
||||
|
||||
- name: restinio
|
||||
run: |
|
||||
mkdir restinio && cd restinio
|
||||
wget https://github.com/Stiffstream/restinio/releases/download/v.0.7.2/restinio-0.7.2.tar.bz2
|
||||
ls -l && tar -xjf restinio-0.7.2.tar.bz2
|
||||
cd restinio-0.7.2/dev
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr -DRESTINIO_TEST=Off -DRESTINIO_SAMPLE=Off -DRESTINIO_BENCHMARK=Off \
|
||||
-DRESTINIO_WITH_SOBJECTIZER=Off -DRESTINIO_DEP_STANDALONE_ASIO=system -DRESTINIO_DEP_LLHTTP=system \
|
||||
-DRESTINIO_DEP_FMT=system -DRESTINIO_DEP_EXPECTED_LITE=system .
|
||||
make -j2 && sudo make install
|
||||
cd ../../ && rm -rf 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 \
|
||||
&& 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 && sudo make install \
|
||||
&& cd ../../.. && rm -rf restinio
|
||||
|
||||
- name: cmake
|
||||
run: |
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
||||
@@ -25,10 +25,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -36,12 +36,12 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_DEPS }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
file: docker/DockerfileDeps
|
||||
@@ -58,10 +58,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -69,13 +69,13 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
file: docker/Dockerfile
|
||||
@@ -92,10 +92,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -103,12 +103,12 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
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@v5
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
file: docker/DockerfileDepsLlvm
|
||||
@@ -125,10 +125,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -136,13 +136,13 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LLVM }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
file: docker/DockerfileLlvm
|
||||
@@ -159,10 +159,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -170,13 +170,13 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME_DHTNODE }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
file: docker/DockerfileDhtnode
|
||||
@@ -193,10 +193,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -204,13 +204,13 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
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@v5
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
file: docker/DockerfileDepsAlpine
|
||||
@@ -227,10 +227,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -238,13 +238,13 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME_ALPINE }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
file: docker/DockerfileAlpine
|
||||
@@ -262,7 +262,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: cmake
|
||||
run: |
|
||||
@@ -280,14 +280,14 @@ jobs:
|
||||
- name: build
|
||||
run: cd build && make dist
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- 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@v2
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
files: build/python/dist/*.whl
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"cmake.configureSettings": {
|
||||
"OPENDHT_SANITIZE": true,
|
||||
"OPENDHT_PROXY_CLIENT": true,
|
||||
"OPENDHT_PROXY_SERVER": true,
|
||||
"OPENDHT_C": true,
|
||||
"OPENDHT_DOCUMENTATION": false
|
||||
}
|
||||
}
|
||||
Vendored
+1
-1
@@ -22,10 +22,10 @@
|
||||
"${workspaceRoot}",
|
||||
"-DCMAKE_EXPORT_COMPILE_COMMANDS=On",
|
||||
"-DCMAKE_BUILD_TYPE=Debug",
|
||||
"-DBUILD_TESTING=On",
|
||||
"-DOPENDHT_SANITIZE=On",
|
||||
"-DOPENDHT_PROXY_CLIENT=On",
|
||||
"-DOPENDHT_PROXY_SERVER=On",
|
||||
"-DOPENDHT_TESTS=On",
|
||||
"-DOPENDHT_C=On"
|
||||
],
|
||||
"dependsOn": "builddir"
|
||||
|
||||
+27
-64
@@ -1,6 +1,9 @@
|
||||
cmake_minimum_required (VERSION 3.16..3.28)
|
||||
if(POLICY CMP0135)
|
||||
cmake_policy(SET CMP0135 NEW)
|
||||
cmake_minimum_required (VERSION 3.10..3.20)
|
||||
if(POLICY CMP0073)
|
||||
cmake_policy(SET CMP0073 NEW)
|
||||
endif()
|
||||
if(POLICY CMP0074)
|
||||
cmake_policy(SET CMP0074 NEW)
|
||||
endif()
|
||||
project (opendht)
|
||||
|
||||
@@ -10,10 +13,9 @@ include(CheckIncludeFileCXX)
|
||||
include(FindPkgConfig)
|
||||
include(cmake/CheckAtomic.cmake)
|
||||
include(CTest)
|
||||
include(FetchContent)
|
||||
|
||||
set (opendht_VERSION_MAJOR 3)
|
||||
set (opendht_VERSION_MINOR 2.0)
|
||||
set (opendht_VERSION_MAJOR 2)
|
||||
set (opendht_VERSION_MINOR 5.5)
|
||||
set (opendht_VERSION ${opendht_VERSION_MAJOR}.${opendht_VERSION_MINOR})
|
||||
set (PACKAGE_VERSION ${opendht_VERSION})
|
||||
set (VERSION "${opendht_VERSION}")
|
||||
@@ -21,6 +23,7 @@ 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_LOG "Build with logs" ON)
|
||||
option (OPENDHT_PYTHON "Build Python bindings" OFF)
|
||||
option (OPENDHT_TOOLS "Build DHT tools" ON)
|
||||
option (OPENDHT_SYSTEMD "Install systemd module" OFF)
|
||||
@@ -45,9 +48,7 @@ set (CMAKE_CXX_STANDARD 17)
|
||||
set (CMAKE_CXX_STANDARD_REQUIRED on)
|
||||
|
||||
# Dependencies
|
||||
if (NOT HAVE_CXX_ATOMICS_WITHOUT_LIB
|
||||
# For ARM EABI (armel), little-endian MIPS (mipsel), etc.
|
||||
OR NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB)
|
||||
if (NOT HAVE_CXX_ATOMICS_WITHOUT_LIB)
|
||||
link_libraries (atomic)
|
||||
endif ()
|
||||
|
||||
@@ -83,46 +84,15 @@ if (NOT MSVC)
|
||||
)
|
||||
endif()
|
||||
|
||||
if (OPENDHT_HTTP OR OPENDHT_PEER_DISCOVERY)
|
||||
find_path(ASIO_INCLUDE_DIR asio.hpp REQUIRED)
|
||||
message(STATUS "Found ASIO ${ASIO_INCLUDE_DIR}")
|
||||
else()
|
||||
message(STATUS "ASIO not required")
|
||||
endif ()
|
||||
|
||||
find_package(fmt)
|
||||
find_path(ASIO_INCLUDE_DIR asio.hpp REQUIRED)
|
||||
|
||||
if (OPENDHT_HTTP)
|
||||
find_package(Restinio REQUIRED)
|
||||
|
||||
# llhttp
|
||||
find_path(LLHTTP_INCLUDE_DIR llhttp.h)
|
||||
find_library(LLHTTP_LIBRARY NAMES llhttp libllhttp)
|
||||
if (LLHTTP_INCLUDE_DIR AND LLHTTP_LIBRARY)
|
||||
message(STATUS "Found llhttp ${LLHTTP_INCLUDE_DIR} ${LLHTTP_LIBRARY}")
|
||||
add_library(llhttp_static STATIC IMPORTED)
|
||||
set_target_properties(llhttp_static PROPERTIES
|
||||
IMPORTED_LOCATION ${LLHTTP_LIBRARY}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${LLHTTP_INCLUDE_DIR}
|
||||
)
|
||||
set(llhttp_target llhttp_static)
|
||||
else()
|
||||
FetchContent_Declare(llhttp-local URL "https://github.com/nodejs/llhttp/archive/refs/tags/release/v9.2.0.tar.gz")
|
||||
if (BUILD_SHARED_LIBS)
|
||||
set(BUILD_SHARED_LIBS ON CACHE INTERNAL "")
|
||||
else()
|
||||
set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "")
|
||||
set(BUILD_STATIC_LIBS ON CACHE INTERNAL "")
|
||||
endif()
|
||||
FetchContent_MakeAvailable(llhttp-local)
|
||||
if (BUILD_SHARED_LIBS)
|
||||
set(llhttp_target llhttp_shared)
|
||||
else()
|
||||
set(llhttp_target llhttp_static)
|
||||
endif()
|
||||
endif()
|
||||
set(http_lib "-lllhttp")
|
||||
|
||||
find_library(FMT_LIBRARY fmt)
|
||||
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()
|
||||
@@ -163,16 +133,12 @@ endif ()
|
||||
|
||||
if (OPENDHT_HTTP OR OPENDHT_PEER_DISCOVERY)
|
||||
add_definitions(-DASIO_STANDALONE)
|
||||
if (UNIX AND NOT APPLE)
|
||||
pkg_search_module(liburing IMPORTED_TARGET liburing)
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
if (NOT MSVC)
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-return-type -Wno-deprecated -Wall -Wextra -Wnon-virtual-dtor -pedantic-errors -fvisibility=hidden")
|
||||
if (OPENDHT_SANITIZE)
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fstack-protector-strong")
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fstack-protector-strong")
|
||||
endif ()
|
||||
else ()
|
||||
add_definitions(-D_WINSOCK_DEPRECATED_NO_WARNINGS
|
||||
@@ -187,6 +153,11 @@ endif ()
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMSGPACK_NO_BOOST -DMSGPACK_DISABLE_LEGACY_NIL -DMSGPACK_DISABLE_LEGACY_CONVERT")
|
||||
|
||||
add_definitions(-DPACKAGE_VERSION="${opendht_VERSION}")
|
||||
if (OPENDHT_LOG)
|
||||
add_definitions(-DOPENDHT_LOG=true)
|
||||
else ()
|
||||
add_definitions(-DOPENDHT_LOG=false)
|
||||
endif()
|
||||
|
||||
if (ASIO_INCLUDE_DIR)
|
||||
include_directories (SYSTEM "${ASIO_INCLUDE_DIR}")
|
||||
@@ -237,13 +208,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.h
|
||||
include/opendht/def.h
|
||||
include/opendht/utils.h
|
||||
include/opendht/sockaddr.h
|
||||
include/opendht/rng.h
|
||||
include/opendht/crypto.h
|
||||
include/opendht/infohash.h
|
||||
@@ -260,9 +230,9 @@ list (APPEND opendht_HEADERS
|
||||
include/opendht/rate_limiter.h
|
||||
include/opendht/securedht.h
|
||||
include/opendht/log.h
|
||||
include/opendht/logger.h
|
||||
include/opendht/log_enable.h
|
||||
include/opendht/thread_pool.h
|
||||
include/opendht/network_utils.h
|
||||
include/opendht/udp_socket.h
|
||||
include/opendht.h
|
||||
)
|
||||
|
||||
@@ -381,7 +351,7 @@ if (NOT HAVE_MSGPACKCXX)
|
||||
target_link_libraries(opendht PUBLIC ${MSGPACK_TARGET})
|
||||
endif()
|
||||
if (APPLE)
|
||||
target_link_libraries(opendht PRIVATE "-framework CoreFoundation" "-framework Security")
|
||||
target_link_libraries(opendht PRIVATE SYSTEM "-framework CoreFoundation" "-framework Security")
|
||||
endif()
|
||||
if (MSVC)
|
||||
if (OPENDHT_STATIC)
|
||||
@@ -393,25 +363,18 @@ else()
|
||||
PRIVATE
|
||||
PkgConfig::argon2
|
||||
PkgConfig::Nettle
|
||||
${HTTP_PARSER_LIBRARY}
|
||||
PUBLIC
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
PkgConfig::GnuTLS
|
||||
fmt::fmt
|
||||
${FMT_LIBRARY}
|
||||
)
|
||||
if (OPENDHT_HTTP)
|
||||
target_link_libraries(opendht PUBLIC ${llhttp_target})
|
||||
endif()
|
||||
if (Jsoncpp_FOUND)
|
||||
target_link_libraries(opendht PUBLIC PkgConfig::Jsoncpp)
|
||||
endif()
|
||||
if (OPENDHT_PROXY_OPENSSL)
|
||||
target_link_libraries(opendht PUBLIC PkgConfig::OPENSSL)
|
||||
endif()
|
||||
if (liburing_FOUND)
|
||||
set(iouring_lib ", liburing")
|
||||
target_link_libraries(opendht PUBLIC PkgConfig::liburing)
|
||||
target_compile_definitions(opendht PUBLIC ASIO_HAS_IO_URING ASIO_DISABLE_EPOLL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (BUILD_SHARED_LIBS)
|
||||
|
||||
@@ -21,11 +21,6 @@ Values are distributed over the network, with redundancy.
|
||||
## Documentation
|
||||
See the wiki: <https://github.com/savoirfairelinux/opendht/wiki>
|
||||
|
||||
#### How to run a node
|
||||
|
||||
You can help contributing to the public network by running a stable node with a public IP address.
|
||||
https://github.com/savoirfairelinux/opendht/wiki/Running-a-node-with-dhtnode
|
||||
|
||||
#### How-to build and install
|
||||
|
||||
Build instructions: <https://github.com/savoirfairelinux/opendht/wiki/Build-the-library>
|
||||
@@ -96,19 +91,17 @@ for r in results:
|
||||
- msgpack-c 1.2+, used for data serialization.
|
||||
- GnuTLS 3.3+, used for cryptographic operations.
|
||||
- Nettle 2.4+, a GnuTLS dependency for crypto.
|
||||
- {fmt} 9.0+, for log formatting.
|
||||
- (optional) restinio used for the REST API.
|
||||
- (optional) llhttp used for the REST API.
|
||||
- (optional) jsoncpp 1.7.4-3+, used for the REST API.
|
||||
- Build tested with GCC 7+ (GNU/Linux, Windows with MinGW), Clang/LLVM (GNU/Linux, Android, macOS, iOS).
|
||||
- Build tested with Microsoft Visual Studio 2019, 2022
|
||||
- Build tested with Microsoft Visual Studio 2017, 2019, 2022
|
||||
|
||||
## Contact
|
||||
|
||||
IRC: join us on Libera.chat at [`#opendht`](https://web.libera.chat/#opendht).
|
||||
|
||||
## License
|
||||
Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
|
||||
OpenDHT is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
|
||||
|
||||
|
||||
+44
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* 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
|
||||
@@ -31,6 +31,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <cstdlib>
|
||||
|
||||
const char* dht_version()
|
||||
{
|
||||
@@ -344,7 +345,6 @@ int dht_runner_run_config(dht_runner* r, in_port_t port, const dht_runner_config
|
||||
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.proxy_user_agent = conf->proxy_user_agent ? std::string(conf->proxy_user_agent) : std::string{};
|
||||
config.peer_discovery = conf->peer_discovery;
|
||||
config.peer_publish = conf->peer_publish;
|
||||
|
||||
@@ -359,14 +359,53 @@ int dht_runner_run_config(dht_runner* r, in_port_t port, const dht_runner_config
|
||||
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(dht::SockAddr(addr, addr_len), [done_cb, cb_user_data](bool ok){
|
||||
runner->bootstrap(sockaddr_to_endpoint(addr, addr_len), [done_cb, cb_user_data](bool ok){
|
||||
done_cb(ok, cb_user_data);
|
||||
});
|
||||
} else {
|
||||
runner->bootstrap(dht::SockAddr(addr, addr_len));
|
||||
runner->bootstrap(sockaddr_to_endpoint(addr, addr_len));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,11 +545,7 @@ struct sockaddr** dht_runner_get_public_address(const dht_runner* r) {
|
||||
auto addrs = const_cast<dht::DhtRunner*>(runner)->getPublicAddress();
|
||||
if (addrs.empty())
|
||||
return nullptr;
|
||||
auto ret = (struct sockaddr**)malloc(sizeof(struct sockaddr*) * (addrs.size() + 1));
|
||||
for (size_t i=0; i<addrs.size(); i++)
|
||||
ret[i] = addrs[i].release();
|
||||
ret[addrs.size()] = nullptr;
|
||||
return ret;
|
||||
return endpoints_to_sockaddrs(addrs);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
+1
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* 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
|
||||
@@ -148,7 +148,6 @@ struct OPENDHT_PUBLIC dht_runner_config {
|
||||
const char* push_token;
|
||||
const char* push_topic;
|
||||
const char* push_platform;
|
||||
const char* proxy_user_agent;
|
||||
bool peer_discovery;
|
||||
bool peer_publish;
|
||||
dht_certificate* server_ca;
|
||||
|
||||
+25
-20
@@ -1,7 +1,7 @@
|
||||
dnl define macros
|
||||
m4_define([opendht_major_version], 3)
|
||||
m4_define([opendht_minor_version], 2)
|
||||
m4_define([opendht_patch_version], 0)
|
||||
m4_define([opendht_major_version], 2)
|
||||
m4_define([opendht_minor_version], 5)
|
||||
m4_define([opendht_patch_version], 5)
|
||||
m4_define([opendht_version],
|
||||
[opendht_major_version.opendht_minor_version.opendht_patch_version])
|
||||
|
||||
@@ -21,10 +21,10 @@ 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"])
|
||||
|
||||
CPPFLAGS="${CPPFLAGS} -DOPENDHT_BUILD"
|
||||
CPPFLAGS+=" -DOPENDHT_BUILD"
|
||||
AM_CONDITIONAL([OPENDHT_SHARED], [test "x$enable_shared" != xno])
|
||||
AM_COND_IF(OPENDHT_SHARED, [
|
||||
CPPFLAGS="${CPPFLAGS} -Dopendht_EXPORTS"
|
||||
CPPFLAGS+=" -Dopendht_EXPORTS"
|
||||
])
|
||||
|
||||
AM_PROG_AR
|
||||
@@ -34,6 +34,12 @@ AC_LANG(C++)
|
||||
AC_PROG_CXX
|
||||
AX_CXX_COMPILE_STDCXX(17,[noext],[mandatory])
|
||||
|
||||
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")
|
||||
@@ -147,7 +153,7 @@ AS_IF([test "x$with_jsoncpp" != "xno"],
|
||||
[have_jsoncpp=no])
|
||||
AS_IF([test "x$have_jsoncpp" = "xyes"], [
|
||||
AC_MSG_NOTICE([Using JsonCpp])
|
||||
CPPFLAGS="${CPPFLAGS} -DOPENDHT_JSONCPP"
|
||||
CPPFLAGS+=" -DOPENDHT_JSONCPP"
|
||||
AC_SUBST(jsoncpp_lib, [", jsoncpp"])
|
||||
], [
|
||||
AC_MSG_NOTICE([Not using JsonCpp])
|
||||
@@ -165,21 +171,20 @@ AS_IF([test "x$have_openssl" = "xyes"], [
|
||||
AC_MSG_NOTICE([Not using OpenSSL])
|
||||
])
|
||||
|
||||
PKG_CHECK_MODULES([Fmt], [fmt >= 5.3.0], [have_fmt=yes], [have_fmt=no])
|
||||
AS_IF([test "x$have_fmt" = "xyes"], [
|
||||
AC_MSG_NOTICE([Using libfmt])
|
||||
], [
|
||||
AC_MSG_NOTICE([Missing libfmt files])
|
||||
])
|
||||
|
||||
AM_COND_IF([PROXY_CLIENT_OR_SERVER], [
|
||||
AC_CHECK_HEADERS([asio.hpp],, AC_MSG_ERROR([Missing Asio headers files]))
|
||||
CXXFLAGS="${CXXFLAGS} -DASIO_STANDALONE"
|
||||
# llhttp has no pkgconfig, instead we check with:
|
||||
AC_CHECK_LIB(llhttp, exit,, AC_MSG_ERROR([Missing llhttp library files]))
|
||||
AC_CHECK_HEADERS([llhttp.h], [llhttp_headers=yes; break;])
|
||||
AC_SUBST(http_lib, ["-lllhttp"])
|
||||
AS_IF([test "x$llhttp_headers" != "xyes"], AC_MSG_ERROR([Missing llhttp headers files]))
|
||||
PKG_CHECK_MODULES([Fmt], [fmt >= 5.3.0], [have_fmt=yes], [have_fmt=no])
|
||||
AS_IF([test "x$have_fmt" = "xyes"], [
|
||||
AC_MSG_NOTICE([Using libfmt])
|
||||
], [
|
||||
AC_MSG_NOTICE([Missing libfmt files])
|
||||
])
|
||||
# http_parser has no pkgconfig, instead we check with:
|
||||
AC_CHECK_LIB(http_parser, exit,, AC_MSG_ERROR([Missing HttpParser library files]))
|
||||
AC_CHECK_HEADERS([http_parser.h], [http_parser_headers=yes; break;])
|
||||
AC_SUBST(http_parser_lib, ["-lhttp_parser"])
|
||||
AS_IF([test "x$http_parser_headers" != "xyes"], AC_MSG_ERROR([Missing HttpParser headers files]))
|
||||
])
|
||||
|
||||
CXXFLAGS="${CXXFLAGS} -DMSGPACK_NO_BOOST -DMSGPACK_DISABLE_LEGACY_NIL -DMSGPACK_DISABLE_LEGACY_CONVERT"
|
||||
@@ -194,8 +199,8 @@ AM_COND_IF([ENABLE_TOOLS], [
|
||||
|
||||
AM_COND_IF(ENABLE_PROXY_SERVER, AC_DEFINE([OPENDHT_PROXY_SERVER], [], [Building with proxy server]))
|
||||
AM_COND_IF(ENABLE_PROXY_CLIENT, AC_DEFINE([OPENDHT_PROXY_CLIENT], [], [Building with proxy client]))
|
||||
AM_COND_IF(ENABLE_PUSH_NOTIFICATIONS, [CPPFLAGS="${CPPFLAGS} -DOPENDHT_PUSH_NOTIFICATIONS"], [])
|
||||
AM_COND_IF(ENABLE_PROXY_SERVER_IDENTITY, [CPPFLAGS="${CPPFLAGS} -DOPENDHT_PROXY_SERVER_IDENTITY"], [])
|
||||
AM_COND_IF(ENABLE_PUSH_NOTIFICATIONS, [CPPFLAGS+=" -DOPENDHT_PUSH_NOTIFICATIONS"], [])
|
||||
AM_COND_IF(ENABLE_PROXY_SERVER_IDENTITY, [CPPFLAGS+=" -DOPENDHT_PROXY_SERVER_IDENTITY"], [])
|
||||
|
||||
dnl Configure setup.py if we build the python module
|
||||
AC_SUBST(CURRENT_SOURCE_DIR, ".")
|
||||
|
||||
+1
-2
@@ -19,7 +19,7 @@
|
||||
.SH DESCRIPTION
|
||||
Runs an OpenDHT node, with a CLI (default) or as a daemon (with \fB'-d'\fP or \fB'-s'\fP).
|
||||
Commands available in the interactive shell are:
|
||||
.EX
|
||||
.EE
|
||||
h, help Print this help message.
|
||||
q, quit Quit the program.
|
||||
log Start/stop printing DHT logs.
|
||||
@@ -39,7 +39,6 @@ Commands available in the interactive shell are:
|
||||
private key.
|
||||
e [key] [dest] [str] Put string value at [key], encrypted for [dest] with
|
||||
its public key (if found).
|
||||
.EE
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-h\fP
|
||||
|
||||
@@ -19,7 +19,7 @@ RUN cd opendht && mkdir build && cd build \
|
||||
-DOPENDHT_SYSTEMD=On \
|
||||
&& make -j8 && make install
|
||||
|
||||
FROM alpine:3.19 AS install
|
||||
FROM alpine:3.17 AS install
|
||||
COPY --from=build /install /
|
||||
RUN apk add --no-cache \
|
||||
libstdc++ \
|
||||
@@ -28,8 +28,8 @@ RUN apk add --no-cache \
|
||||
openssl \
|
||||
argon2-dev \
|
||||
jsoncpp \
|
||||
llhttp \
|
||||
fmt \
|
||||
http-parser \
|
||||
readline \
|
||||
ncurses
|
||||
CMD ["dhtnode", "-b", "bootstrap.jami.net", "-p", "4222", "--proxyserver", "8080"]
|
||||
|
||||
+10
-16
@@ -10,25 +10,19 @@ 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 \
|
||||
python3-pip python3-dev python3-setuptools python3-build python3-virtualenv \
|
||||
cython3 python3-dev python3-setuptools python3-build python3-virtualenv \
|
||||
libncurses5-dev libreadline-dev nettle-dev libcppunit-dev \
|
||||
libgnutls28-dev libuv1-dev libjsoncpp-dev libargon2-dev \
|
||||
libssl-dev libfmt-dev libasio-dev libmsgpack-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 pip3 install meson Cython
|
||||
|
||||
RUN mkdir /usr/include/nonstd \
|
||||
&& wget https://raw.githubusercontent.com/martinmoene/expected-lite/master/include/nonstd/expected.hpp \
|
||||
-O /usr/include/nonstd/expected.hpp
|
||||
|
||||
RUN echo "*** Downloading RESTinio ***" \
|
||||
&& mkdir restinio && cd restinio \
|
||||
&& wget https://github.com/Stiffstream/restinio/releases/download/v.0.7.2/restinio-0.7.2.tar.bz2 \
|
||||
&& ls -l && tar -xjf restinio-0.7.2.tar.bz2 \
|
||||
&& cd restinio-0.7.2/dev \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DRESTINIO_TEST=Off -DRESTINIO_SAMPLE=Off -DRESTINIO_BENCHMARK=Off \
|
||||
-DRESTINIO_WITH_SOBJECTIZER=Off -DRESTINIO_DEP_STANDALONE_ASIO=system -DRESTINIO_DEP_LLHTTP=system \
|
||||
-DRESTINIO_DEP_FMT=system -DRESTINIO_DEP_EXPECTED_LITE=system . \
|
||||
&& make -j2 && make install \
|
||||
&& cd ../../ && rm -rf restinio*
|
||||
&& wget https://github.com/aberaud/restinio/archive/bbaa034dbcc7555ce67df0f8a1475591a7441733.tar.gz \
|
||||
&& ls -l && 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
|
||||
+10
-14
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.19
|
||||
FROM alpine:3.17
|
||||
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
|
||||
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
|
||||
|
||||
@@ -8,20 +8,16 @@ RUN apk add --no-cache \
|
||||
ncurses-dev readline-dev nettle-dev \
|
||||
cppunit-dev gnutls-dev jsoncpp-dev \
|
||||
argon2-dev openssl-dev fmt-dev \
|
||||
llhttp-dev asio-dev msgpack-cxx-dev \
|
||||
http-parser-dev asio-dev msgpack-cxx-dev \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
RUN mkdir /usr/include/nonstd \
|
||||
&& wget https://raw.githubusercontent.com/martinmoene/expected-lite/master/include/nonstd/expected.hpp \
|
||||
-O /usr/include/nonstd/expected.hpp
|
||||
|
||||
RUN echo "*** Downloading RESTinio ***" \
|
||||
&& mkdir restinio && cd restinio \
|
||||
&& wget https://github.com/Stiffstream/restinio/releases/download/v.0.7.2/restinio-0.7.2.tar.bz2 \
|
||||
&& ls -l && tar -xjf restinio-0.7.2.tar.bz2 \
|
||||
&& cd restinio-0.7.2/dev \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DRESTINIO_TEST=Off -DRESTINIO_SAMPLE=Off -DRESTINIO_BENCHMARK=Off \
|
||||
-DRESTINIO_WITH_SOBJECTIZER=Off -DRESTINIO_DEP_STANDALONE_ASIO=system -DRESTINIO_DEP_LLHTTP=system \
|
||||
-DRESTINIO_DEP_FMT=system -DRESTINIO_DEP_EXPECTED_LITE=system . \
|
||||
&& make -j2 && make install \
|
||||
&& cd ../../ && rm -rf restinio*
|
||||
&& 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
|
||||
+10
-14
@@ -5,12 +5,12 @@ LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendh
|
||||
RUN echo "APT::Acquire::Retries \"3\";" > /etc/apt/apt.conf.d/80-retries
|
||||
RUN apt-get update && apt-get install -y \
|
||||
apt-transport-https build-essential pkg-config git wget libncurses5-dev libreadline-dev nettle-dev \
|
||||
libgnutls28-dev libuv1-dev python3-dev python3-setuptools libcppunit-dev libjsoncpp-dev \
|
||||
libgnutls28-dev libuv1-dev cython3 python3-dev python3-setuptools libcppunit-dev libjsoncpp-dev \
|
||||
libargon2-0-dev \
|
||||
autotools-dev autoconf libfmt-dev libmsgpack-dev libssl-dev python3-pip \
|
||||
autotools-dev autoconf libfmt-dev libhttp-parser-dev libmsgpack-dev libssl-dev python3-pip \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/* /var/cache/apt/*
|
||||
|
||||
RUN pip3 install --upgrade cmake meson Cython
|
||||
RUN pip3 install --upgrade cmake
|
||||
|
||||
# libasio-dev (1.10) is too old
|
||||
RUN echo "** Building a recent version of asio ***" \
|
||||
@@ -20,17 +20,13 @@ RUN echo "** Building a recent version of asio ***" \
|
||||
&& make install \
|
||||
&& cd ../../ && rm -rf asio*
|
||||
|
||||
RUN mkdir /usr/include/nonstd \
|
||||
&& wget https://raw.githubusercontent.com/martinmoene/expected-lite/master/include/nonstd/expected.hpp \
|
||||
-O /usr/include/nonstd/expected.hpp
|
||||
|
||||
RUN echo "*** Downloading RESTinio ***" \
|
||||
&& mkdir restinio && cd restinio \
|
||||
&& wget https://github.com/Stiffstream/restinio/releases/download/v.0.7.2/restinio-0.7.2.tar.bz2 \
|
||||
&& ls -l && tar -xjf restinio-0.7.2.tar.bz2 \
|
||||
&& cd restinio-0.7.2/dev \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DRESTINIO_TEST=Off -DRESTINIO_SAMPLE=Off -DRESTINIO_BENCHMARK=Off \
|
||||
-DRESTINIO_WITH_SOBJECTIZER=Off -DRESTINIO_DEP_STANDALONE_ASIO=system -DRESTINIO_DEP_LLHTTP=system \
|
||||
-DRESTINIO_DEP_FMT=system -DRESTINIO_DEP_EXPECTED_LITE=system . \
|
||||
&& make -j2 && make install \
|
||||
&& wget https://github.com/aberaud/restinio/archive/8d5d3e8237e0947adb9ba1ffc8281f4ad7cb2a59.tar.gz \
|
||||
&& ls -l && tar -xzf 8d5d3e8237e0947adb9ba1ffc8281f4ad7cb2a59.tar.gz \
|
||||
&& cd restinio-8d5d3e8237e0947adb9ba1ffc8281f4ad7cb2a59/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*
|
||||
+10
-16
@@ -10,25 +10,19 @@ 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 \
|
||||
python3-dev python3-setuptools python3-pip \
|
||||
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 libasio-dev libmsgpack-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 pip3 install meson Cython
|
||||
|
||||
RUN mkdir /usr/include/nonstd \
|
||||
&& wget https://raw.githubusercontent.com/martinmoene/expected-lite/master/include/nonstd/expected.hpp \
|
||||
-O /usr/include/nonstd/expected.hpp
|
||||
|
||||
RUN echo "*** Downloading RESTinio ***" \
|
||||
&& mkdir restinio && cd restinio \
|
||||
&& wget https://github.com/Stiffstream/restinio/releases/download/v.0.7.2/restinio-0.7.2.tar.bz2 \
|
||||
&& ls -l && tar -xjf restinio-0.7.2.tar.bz2 \
|
||||
&& cd restinio-0.7.2/dev \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DRESTINIO_TEST=Off -DRESTINIO_SAMPLE=Off -DRESTINIO_BENCHMARK=Off \
|
||||
-DRESTINIO_WITH_SOBJECTIZER=Off -DRESTINIO_DEP_STANDALONE_ASIO=system -DRESTINIO_DEP_LLHTTP=system \
|
||||
-DRESTINIO_DEP_FMT=system -DRESTINIO_DEP_EXPECTED_LITE=system . \
|
||||
&& make -j2 && make install \
|
||||
&& cd ../../ && rm -rf restinio*
|
||||
&& 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
|
||||
@@ -10,29 +10,23 @@ RUN apt-get update && apt-get install -y \
|
||||
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 \
|
||||
nettle-dev libgnutls28-dev libuv1-dev libmsgpack-dev libjsoncpp-dev python3-dev \
|
||||
nettle-dev libgnutls28-dev libuv1-dev libmsgpack-dev libjsoncpp-dev cython3 python3-dev \
|
||||
python3-setuptools libcppunit-dev python3-pip python3-build python3-virtualenv \
|
||||
autotools-dev autoconf libssl-dev libargon2-dev \
|
||||
libfmt-dev libasio-dev \
|
||||
libfmt-dev libhttp-parser-dev libasio-dev \
|
||||
&& apt-get remove -y gcc g++ && apt-get autoremove -y \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/* /var/cache/apt/*
|
||||
|
||||
RUN pip3 install meson Cython
|
||||
|
||||
ENV CC cc
|
||||
ENV CXX c++
|
||||
|
||||
RUN mkdir /usr/include/nonstd \
|
||||
&& wget https://raw.githubusercontent.com/martinmoene/expected-lite/master/include/nonstd/expected.hpp \
|
||||
-O /usr/include/nonstd/expected.hpp
|
||||
|
||||
RUN echo "*** Downloading RESTinio ***" \
|
||||
&& mkdir restinio && cd restinio \
|
||||
&& wget https://github.com/Stiffstream/restinio/releases/download/v.0.7.2/restinio-0.7.2.tar.bz2 \
|
||||
&& ls -l && tar -xjf restinio-0.7.2.tar.bz2 \
|
||||
&& cd restinio-0.7.2/dev \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DRESTINIO_TEST=Off -DRESTINIO_SAMPLE=Off -DRESTINIO_BENCHMARK=Off \
|
||||
-DRESTINIO_WITH_SOBJECTIZER=Off -DRESTINIO_DEP_STANDALONE_ASIO=system -DRESTINIO_DEP_LLHTTP=system \
|
||||
-DRESTINIO_DEP_FMT=system -DRESTINIO_DEP_EXPECTED_LITE=system . \
|
||||
&& make -j2 && make install \
|
||||
&& wget https://github.com/aberaud/restinio/archive/bbaa034dbcc7555ce67df0f8a1475591a7441733.tar.gz \
|
||||
&& ls -l && 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*
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
@@ -44,6 +44,18 @@ 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) {
|
||||
@@ -79,6 +91,7 @@ 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};
|
||||
@@ -143,9 +156,6 @@ struct OPENDHT_PUBLIC Config {
|
||||
* - Larger listen refresh time
|
||||
*/
|
||||
bool public_stable {false};
|
||||
|
||||
/* Client mode, node will not be used by other nodes to store data. */
|
||||
bool client_mode {false};
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -173,19 +183,19 @@ using ValueCallback = std::function<bool(const std::vector<std::shared_ptr<Value
|
||||
using GetCallbackSimple = std::function<bool(std::shared_ptr<Value> value)>;
|
||||
using ShutdownCallback = std::function<void()>;
|
||||
using IdentityAnnouncedCb = std::function<void(bool)>;
|
||||
using PublicAddressChangedCb = std::function<void(std::vector<SockAddr>)>;
|
||||
|
||||
using CertificateStoreQuery = std::function<std::vector<std::shared_ptr<crypto::Certificate>>(const InfoHash& pk_id)>;
|
||||
using DoneCallback = std::function<void(bool success, const std::vector<std::shared_ptr<Node>>& nodes)>;
|
||||
using DoneCallbackSimple = std::function<void(bool success)>;
|
||||
|
||||
typedef bool (*GetCallbackRaw)(std::shared_ptr<Value>, void *user_data);
|
||||
typedef bool (*ValueCallbackRaw)(std::shared_ptr<Value>, bool expired, void *user_data);
|
||||
|
||||
using DoneCallback = std::function<void(bool success, const std::vector<std::shared_ptr<Node>>& nodes)>;
|
||||
typedef void (*DoneCallbackRaw)(bool, std::vector<std::shared_ptr<Node>>*, void *user_data);
|
||||
typedef void (*ShutdownCallbackRaw)(void *user_data);
|
||||
typedef void (*DoneCallbackSimpleRaw)(bool, void *user_data);
|
||||
typedef bool (*FilterRaw)(const Value&, void *user_data);
|
||||
|
||||
using DoneCallbackSimple = std::function<void(bool success)>;
|
||||
|
||||
OPENDHT_PUBLIC GetCallbackSimple bindGetCb(GetCallbackRaw raw_cb, void* user_data);
|
||||
OPENDHT_PUBLIC GetCallback bindGetCb(GetCallbackSimple cb);
|
||||
|
||||
+93
-49
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Vsevolod Ivanov <vsevolod.ivanov@savoirfairelinux.com>
|
||||
*
|
||||
@@ -670,6 +670,94 @@ private:
|
||||
gnutls_x509_trust_list_t trust {nullptr};
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class OPENDHT_PUBLIC secure_vector
|
||||
{
|
||||
public:
|
||||
secure_vector() {}
|
||||
secure_vector(secure_vector<T> const&) = default;
|
||||
secure_vector(secure_vector<T> &&) = default;
|
||||
explicit secure_vector(unsigned size): data_(size) {}
|
||||
explicit secure_vector(unsigned size, T _item): data_(size, _item) {}
|
||||
explicit secure_vector(const std::vector<T>& c): data_(c) {}
|
||||
secure_vector(std::vector<T>&& c): data_(std::move(c)) {}
|
||||
~secure_vector() { clean(); }
|
||||
|
||||
static secure_vector<T> getRandom(size_t size) {
|
||||
secure_vector<T> ret(size);
|
||||
crypto::random_device rdev;
|
||||
#ifdef _WIN32
|
||||
std::uniform_int_distribution<int> rand_byte{ 0, std::numeric_limits<uint8_t>::max() };
|
||||
#else
|
||||
std::uniform_int_distribution<uint8_t> rand_byte;
|
||||
#endif
|
||||
std::generate_n((uint8_t*)ret.data_.data(), ret.size()*sizeof(T), std::bind(rand_byte, std::ref(rdev)));
|
||||
return ret;
|
||||
}
|
||||
secure_vector<T>& operator=(const secure_vector<T>& c) {
|
||||
if (&c == this)
|
||||
return *this;
|
||||
clean();
|
||||
data_ = c.data_;
|
||||
return *this;
|
||||
}
|
||||
secure_vector<T>& operator=(secure_vector<T>&& c) {
|
||||
if (&c == this)
|
||||
return *this;
|
||||
clean();
|
||||
data_ = std::move(c.data_);
|
||||
return *this;
|
||||
}
|
||||
secure_vector<T>& operator=(std::vector<T>&& c) {
|
||||
clean();
|
||||
data_ = std::move(c);
|
||||
return *this;
|
||||
}
|
||||
std::vector<T>& writable() { clean(); return data_; }
|
||||
const std::vector<T>& makeInsecure() const { return data_; }
|
||||
const uint8_t* data() const { return data_.data(); }
|
||||
|
||||
void clean() {
|
||||
clean(data_.begin(), data_.end());
|
||||
}
|
||||
|
||||
void clear() { clean(); data_.clear(); }
|
||||
|
||||
size_t size() const { return data_.size(); }
|
||||
bool empty() const { return data_.empty(); }
|
||||
|
||||
void swap(secure_vector<T>& other) { data_.swap(other.data_); }
|
||||
void resize(size_t s) {
|
||||
if (s == data_.size()) return;
|
||||
if (s < data_.size()) {
|
||||
//shrink
|
||||
clean(data_.begin()+s, data_.end());
|
||||
data_.resize(s);
|
||||
} else {
|
||||
//grow
|
||||
auto data = std::move(data_); // move protected data
|
||||
clear();
|
||||
data_.resize(s);
|
||||
std::copy(data.begin(), data.end(), data_.begin());
|
||||
clean(data.begin(), data.end());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Securely wipe memory
|
||||
*/
|
||||
static void clean(const typename std::vector<T>::iterator& i, const typename std::vector<T>::iterator& j) {
|
||||
volatile uint8_t* b = reinterpret_cast<uint8_t*>(&*i);
|
||||
volatile uint8_t* e = reinterpret_cast<uint8_t*>(&*j);
|
||||
std::fill(b, e, 0);
|
||||
}
|
||||
|
||||
std::vector<T> data_;
|
||||
};
|
||||
|
||||
using SecureBlob = secure_vector<uint8_t>;
|
||||
|
||||
/**
|
||||
* Generate an RSA key pair (4096 bits) and a certificate.
|
||||
* @param name the name used in the generated certificate
|
||||
@@ -684,7 +772,6 @@ OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name, const Identi
|
||||
OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name = "dhtnode", const Identity& ca = {});
|
||||
|
||||
OPENDHT_PUBLIC void saveIdentity(const Identity& id, const std::string& path, const std::string& privkey_password = {});
|
||||
OPENDHT_PUBLIC Identity loadIdentity(const std::string &path,const std::string &privkey_password = {});
|
||||
|
||||
/**
|
||||
* Performs SHA512, SHA256 or SHA1, depending on hash_length.
|
||||
@@ -705,7 +792,7 @@ OPENDHT_PUBLIC void hash(const uint8_t* data, size_t data_length, uint8_t* hash,
|
||||
* that can be transmitted in clear, and will be generated if
|
||||
* not provided (32 bytes).
|
||||
*/
|
||||
OPENDHT_PUBLIC Blob stretchKey(std::string_view password, Blob& salt, size_t key_length = 512/8);
|
||||
OPENDHT_PUBLIC Blob stretchKey(const std::string& password, Blob& salt, size_t key_length = 512/8);
|
||||
|
||||
/**
|
||||
* AES-GCM encryption. Key must be 128, 192 or 256 bits long (16, 24 or 32 bytes).
|
||||
@@ -714,58 +801,15 @@ OPENDHT_PUBLIC Blob aesEncrypt(const uint8_t* data, size_t data_length, const Bl
|
||||
OPENDHT_PUBLIC inline Blob aesEncrypt(const Blob& data, const Blob& key) {
|
||||
return aesEncrypt(data.data(), data.size(), key);
|
||||
}
|
||||
/**
|
||||
* AES-GCM encryption with argon2 key derivation.
|
||||
* This function uses `stretchKey` to generate an AES key from the password and a random salt.
|
||||
* The result is a bundle including the salt that can be decrypted with `aesDecrypt(data, password)`.
|
||||
* If needed, the salt or encrypted data can be individually extracted from the bundle with `aesGetSalt` and `aesGetEncrypted`.
|
||||
* @param data: data to encrypt
|
||||
* @param password: password to encrypt the data with
|
||||
* @param salt: optional salt to use for key derivation. If not provided, a random salt will be generated.
|
||||
*/
|
||||
OPENDHT_PUBLIC Blob aesEncrypt(const Blob& data, std::string_view password, const Blob& salt = {});
|
||||
OPENDHT_PUBLIC Blob aesEncrypt(const Blob& data, const std::string& password);
|
||||
|
||||
/**
|
||||
* AES-GCM decryption.
|
||||
*/
|
||||
OPENDHT_PUBLIC Blob aesDecrypt(const uint8_t* data, size_t data_length, const Blob& key);
|
||||
OPENDHT_PUBLIC inline Blob aesDecrypt(const Blob& data, const Blob& key) { return aesDecrypt(data.data(), data.size(), key); }
|
||||
OPENDHT_PUBLIC inline Blob aesDecrypt(std::string_view data, const Blob& key) { return aesDecrypt((uint8_t*)data.data(), data.size(), key); }
|
||||
|
||||
OPENDHT_PUBLIC Blob aesDecrypt(const uint8_t* data, size_t data_length, std::string_view password);
|
||||
OPENDHT_PUBLIC inline Blob aesDecrypt(const Blob& data, std::string_view password) { return aesDecrypt(data.data(), data.size(), password); }
|
||||
OPENDHT_PUBLIC inline Blob aesDecrypt(std::string_view data, std::string_view password) { return aesDecrypt((uint8_t*)data.data(), data.size(), password); }
|
||||
|
||||
/**
|
||||
* Get raw AES key from password and salt stored with the encrypted data.
|
||||
*/
|
||||
OPENDHT_PUBLIC Blob aesGetKey(const uint8_t* data, size_t data_length, std::string_view password);
|
||||
OPENDHT_PUBLIC Blob inline aesGetKey(const Blob& data, std::string_view password) {
|
||||
return aesGetKey(data.data(), data.size(), password);
|
||||
}
|
||||
/** Get the salt part of data password-encrypted with `aesEncrypt(data, password)` */
|
||||
OPENDHT_PUBLIC Blob aesGetSalt(const uint8_t* data, size_t data_length);
|
||||
OPENDHT_PUBLIC Blob inline aesGetSalt(const Blob& data) {
|
||||
return aesGetSalt(data.data(), data.size());
|
||||
}
|
||||
/** Get the encrypted data part of data password-encrypted with `aesEncrypt(data, password)` */
|
||||
OPENDHT_PUBLIC std::string_view aesGetEncrypted(const uint8_t* data, size_t data_length);
|
||||
OPENDHT_PUBLIC std::string_view inline aesGetEncrypted(const Blob& data) {
|
||||
return aesGetEncrypted(data.data(), data.size());
|
||||
}
|
||||
|
||||
/** Build an encrypted bundle that can be decrypted with aesDecrypt(data, password).
|
||||
* @param encryptedData: result of `aesEncrypt(data, key)` or `aesGetEncrypted`
|
||||
* @param salt: should match the encryption key and password so that `stretchKey(password, salk) == key`.
|
||||
* Can be obtained from an existing bundle with `aesGetSalt`.
|
||||
**/
|
||||
OPENDHT_PUBLIC Blob aesBuildEncrypted(const uint8_t* encryptedData, size_t data_length, const Blob& salt);
|
||||
OPENDHT_PUBLIC Blob inline aesBuildEncrypted(const Blob& encryptedData, const Blob& salt) {
|
||||
return aesBuildEncrypted(encryptedData.data(), encryptedData.size(), salt);
|
||||
}
|
||||
OPENDHT_PUBLIC Blob inline aesBuildEncrypted(std::string_view encryptedData, const Blob& salt) {
|
||||
return aesBuildEncrypted((const uint8_t*)encryptedData.data(), encryptedData.size(), salt);
|
||||
}
|
||||
OPENDHT_PUBLIC Blob aesDecrypt(const uint8_t* data, size_t data_length, const std::string& password);
|
||||
OPENDHT_PUBLIC inline Blob aesDecrypt(const Blob& data, const std::string& password) { return aesDecrypt(data.data(), data.size(), password); }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,114 +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 <vector>
|
||||
#include <random>
|
||||
|
||||
namespace dht {
|
||||
namespace crypto {
|
||||
|
||||
template <class T>
|
||||
class secure_vector
|
||||
{
|
||||
public:
|
||||
secure_vector() {}
|
||||
secure_vector(secure_vector<T> const&) = default;
|
||||
secure_vector(secure_vector<T> &&) = default;
|
||||
explicit secure_vector(unsigned size): data_(size) {}
|
||||
explicit secure_vector(unsigned size, T _item): data_(size, _item) {}
|
||||
explicit secure_vector(const std::vector<T>& c): data_(c) {}
|
||||
secure_vector(std::vector<T>&& c): data_(std::move(c)) {}
|
||||
~secure_vector() { clean(); }
|
||||
|
||||
static secure_vector<T> getRandom(size_t size) {
|
||||
secure_vector<T> ret(size);
|
||||
std::random_device rdev;
|
||||
#ifdef _WIN32
|
||||
std::uniform_int_distribution<int> rand_byte{ 0, std::numeric_limits<uint8_t>::max() };
|
||||
#else
|
||||
std::uniform_int_distribution<uint8_t> rand_byte;
|
||||
#endif
|
||||
std::generate_n((uint8_t*)ret.data_.data(), ret.size()*sizeof(T), std::bind(rand_byte, std::ref(rdev)));
|
||||
return ret;
|
||||
}
|
||||
secure_vector<T>& operator=(const secure_vector<T>& c) {
|
||||
if (&c == this)
|
||||
return *this;
|
||||
clean();
|
||||
data_ = c.data_;
|
||||
return *this;
|
||||
}
|
||||
secure_vector<T>& operator=(secure_vector<T>&& c) {
|
||||
if (&c == this)
|
||||
return *this;
|
||||
clean();
|
||||
data_ = std::move(c.data_);
|
||||
return *this;
|
||||
}
|
||||
secure_vector<T>& operator=(std::vector<T>&& c) {
|
||||
clean();
|
||||
data_ = std::move(c);
|
||||
return *this;
|
||||
}
|
||||
std::vector<T>& writable() { clean(); return data_; }
|
||||
const std::vector<T>& makeInsecure() const { return data_; }
|
||||
const uint8_t* data() const { return data_.data(); }
|
||||
|
||||
void clean() {
|
||||
clean(data_.begin(), data_.end());
|
||||
}
|
||||
|
||||
void clear() { clean(); data_.clear(); }
|
||||
|
||||
size_t size() const { return data_.size(); }
|
||||
bool empty() const { return data_.empty(); }
|
||||
|
||||
void swap(secure_vector<T>& other) { data_.swap(other.data_); }
|
||||
void resize(size_t s) {
|
||||
if (s == data_.size()) return;
|
||||
if (s < data_.size()) {
|
||||
//shrink
|
||||
clean(data_.begin()+s, data_.end());
|
||||
data_.resize(s);
|
||||
} else {
|
||||
//grow
|
||||
auto data = std::move(data_); // move protected data
|
||||
clear();
|
||||
data_.resize(s);
|
||||
std::copy(data.begin(), data.end(), data_.begin());
|
||||
clean(data.begin(), data.end());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Securely wipe memory
|
||||
*/
|
||||
static void clean(const typename std::vector<T>::iterator& i, const typename std::vector<T>::iterator& j) {
|
||||
volatile uint8_t* b = reinterpret_cast<uint8_t*>(&*i);
|
||||
volatile uint8_t* e = reinterpret_cast<uint8_t*>(&*j);
|
||||
std::fill(b, e, 0);
|
||||
}
|
||||
|
||||
std::vector<T> data_;
|
||||
};
|
||||
|
||||
using SecureBlob = secure_vector<uint8_t>;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* 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
|
||||
@@ -19,7 +19,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "value.h"
|
||||
#include "sockaddr.h"
|
||||
|
||||
#include <asio/ip/udp.hpp>
|
||||
|
||||
namespace dht {
|
||||
enum class ImStatus : uint8_t {
|
||||
@@ -215,9 +216,10 @@ private:
|
||||
public:
|
||||
static const ValueType TYPE;
|
||||
|
||||
IpServiceAnnouncement(sa_family_t family = AF_UNSPEC, in_port_t p = 0) {
|
||||
addr.setFamily(family);
|
||||
addr.setPort(p);
|
||||
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(const SockAddr& sa) : addr(sa) {}
|
||||
@@ -229,23 +231,25 @@ public:
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& pk) const
|
||||
{
|
||||
pk.pack_bin(addr.getLength());
|
||||
pk.pack_bin_body((const char*)addr.get(), addr.getLength());
|
||||
pk.pack_bin(addr.size());
|
||||
pk.pack_bin_body((const char*)addr.data(), addr.size());
|
||||
}
|
||||
|
||||
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 = {(sockaddr*)o.via.bin.ptr, (socklen_t)o.via.bin.size};
|
||||
addr = *reinterpret_cast<const asio::ip::udp::endpoint*>((sockaddr*)o.via.bin.ptr);
|
||||
else
|
||||
throw msgpack::type_error();
|
||||
}
|
||||
|
||||
in_port_t getPort() const {
|
||||
return addr.getPort();
|
||||
return addr.port();
|
||||
}
|
||||
void setPort(in_port_t p) {
|
||||
addr.setPort(p);
|
||||
addr.port(p);
|
||||
}
|
||||
|
||||
const SockAddr& getPeerAddr() const {
|
||||
|
||||
+36
-21
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
@@ -29,6 +29,8 @@
|
||||
#include "callbacks.h"
|
||||
#include "dht_interface.h"
|
||||
|
||||
#include <asio/strand.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
@@ -44,6 +46,7 @@ namespace dht {
|
||||
|
||||
namespace net {
|
||||
struct Request;
|
||||
class UdpSocket;
|
||||
} /* namespace net */
|
||||
|
||||
struct Storage;
|
||||
@@ -65,7 +68,7 @@ public:
|
||||
* Initialise the Dht with two open sockets (for IPv4 and IP6)
|
||||
* and an ID for the node.
|
||||
*/
|
||||
Dht(std::unique_ptr<net::DatagramSocket>&& sock, const Config& config, const Sp<Logger>& l = {}, std::unique_ptr<std::mt19937_64>&& rd = {});
|
||||
Dht(std::shared_ptr<net::strand> strand, std::unique_ptr<net::DatagramSocket>&& sock, const Config& config, const Sp<Logger>& l = {});
|
||||
|
||||
virtual ~Dht();
|
||||
|
||||
@@ -73,9 +76,6 @@ public:
|
||||
* Get the ID of the node.
|
||||
*/
|
||||
inline const InfoHash& getNodeId() const override { return myid; }
|
||||
void setOnPublicAddressChanged(PublicAddressChangedCb cb) override {
|
||||
publicAddressChangedCb_ = std::move(cb);
|
||||
}
|
||||
|
||||
NodeStatus updateStatus(sa_family_t af) override;
|
||||
|
||||
@@ -89,8 +89,14 @@ 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));
|
||||
}
|
||||
|
||||
net::DatagramSocket* getSocket() const override { return network_engine.getSocket(); };
|
||||
const net::DatagramSocket* getSocket() const override { return network_engine.getSocket(); };
|
||||
|
||||
/**
|
||||
* Performs final operations before quitting.
|
||||
@@ -133,10 +139,10 @@ public:
|
||||
|
||||
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),
|
||||
@@ -265,6 +271,7 @@ public:
|
||||
*/
|
||||
void connectivityChanged(sa_family_t) override;
|
||||
void connectivityChanged() override {
|
||||
reported_addr.clear();
|
||||
connectivityChanged(AF_INET);
|
||||
connectivityChanged(AF_INET6);
|
||||
}
|
||||
@@ -372,25 +379,23 @@ private:
|
||||
Dht(const Dht&) = delete;
|
||||
Dht& operator=(const Dht&) = delete;
|
||||
|
||||
std::mt19937_64 rd;
|
||||
std::mt19937_64 rd {crypto::getSeededRandomEngine<std::mt19937_64>()};
|
||||
|
||||
InfoHash myid {};
|
||||
|
||||
uint64_t secret {};
|
||||
uint64_t oldsecret {};
|
||||
asio::steady_timer rotateRecretsJob;
|
||||
|
||||
// registred types
|
||||
TypeStore types;
|
||||
|
||||
using SearchMap = std::map<InfoHash, Sp<Search>>;
|
||||
using ReportedAddr = std::pair<unsigned, SockAddr>;
|
||||
|
||||
struct Kad {
|
||||
RoutingTable buckets {};
|
||||
SearchMap searches {};
|
||||
unsigned pending_pings {0};
|
||||
NodeStatus status;
|
||||
std::vector<ReportedAddr> reported_addr;
|
||||
|
||||
NodeStatus getStatus(time_point now) const;
|
||||
NodeStats getNodesStats(time_point now, const InfoHash& myid) const;
|
||||
@@ -398,14 +403,13 @@ private:
|
||||
|
||||
Kad dht4 {};
|
||||
Kad dht6 {};
|
||||
PublicAddressChangedCb publicAddressChangedCb_ {};
|
||||
|
||||
std::vector<std::pair<std::string,std::string>> bootstrap_nodes {};
|
||||
std::chrono::steady_clock::duration bootstrap_period {BOOTSTRAP_PERIOD};
|
||||
Sp<Scheduler::Job> bootstrapJob {};
|
||||
asio::steady_timer bootstrapJob;
|
||||
|
||||
std::map<InfoHash, Storage> store;
|
||||
std::map<SockAddr, StorageBucket, SockAddr::ipCmp> store_quota;
|
||||
std::map<SockAddr, StorageBucket, ipCmp> store_quota;
|
||||
size_t total_values {0};
|
||||
size_t total_store_size {0};
|
||||
size_t max_store_keys {MAX_HASHES};
|
||||
@@ -421,13 +425,18 @@ private:
|
||||
|
||||
|
||||
// timing
|
||||
Scheduler scheduler;
|
||||
Sp<Scheduler::Job> nextNodesConfirmation {};
|
||||
Sp<Scheduler::Job> nextStorageMaintenance {};
|
||||
asio::steady_timer nextNodesConfirmation;
|
||||
asio::steady_timer nextStorageMaintenance;
|
||||
asio::steady_timer expirationJob;
|
||||
asio::steady_timer statusCheckJob;
|
||||
|
||||
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.
|
||||
@@ -450,7 +459,7 @@ 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 = {}, bool permanent = false);
|
||||
bool storageStore(const InfoHash& id, const Sp<Value>& value, time_point created, const SockAddr* sa = nullptr, bool permanent = false);
|
||||
bool storageRefresh(const InfoHash& id, Value::Id vid);
|
||||
void expireStore();
|
||||
void expireStorage(InfoHash h);
|
||||
@@ -494,7 +503,8 @@ 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; }
|
||||
@@ -504,6 +514,7 @@ 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 = {});
|
||||
@@ -521,9 +532,13 @@ 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.
|
||||
*
|
||||
@@ -568,7 +583,7 @@ private:
|
||||
* @param sr The search for which we want to announce a value.
|
||||
* @param announce The 'announce' structure.
|
||||
*/
|
||||
void searchSendAnnounceValue(const Sp<Search>& sr, unsigned syncLevel = TARGET_NODES);
|
||||
void searchSendAnnounceValue(const Sp<Search>& sr);
|
||||
|
||||
/**
|
||||
* Main process of a Search's operations. This function will demand the
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -19,22 +19,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "infohash.h"
|
||||
#include "logger.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 {
|
||||
public:
|
||||
DhtInterface() = default;
|
||||
DhtInterface(const Logger& l) : logger_(std::make_shared<Logger>(l)) {}
|
||||
DhtInterface(const std::shared_ptr<Logger>& l) : logger_(l) {}
|
||||
DhtInterface(const Logger& l) : logger_(std::make_shared<Logger>(l)) {};
|
||||
DhtInterface(const std::shared_ptr<Logger>& l) : logger_(l) {};
|
||||
virtual ~DhtInterface() = default;
|
||||
|
||||
// [[deprecated]]
|
||||
@@ -45,16 +46,14 @@ public:
|
||||
/**
|
||||
* Get the current status of the node for the given family.
|
||||
*/
|
||||
virtual NodeStatus updateStatus(sa_family_t af) { return getStatus(af); }
|
||||
virtual NodeStatus updateStatus(sa_family_t af) { return getStatus(af); };
|
||||
virtual NodeStatus getStatus(sa_family_t af) const = 0;
|
||||
virtual NodeStatus getStatus() const = 0;
|
||||
|
||||
void addOnConnectedCallback(std::function<void()> cb) {
|
||||
onConnectCallbacks_.emplace(std::move(cb));
|
||||
}
|
||||
virtual void setOnPublicAddressChanged(PublicAddressChangedCb) {}
|
||||
virtual void addOnConnectedCallback(std::function<void()> cb) = 0;
|
||||
virtual void addOnStateChangeCallback(StatusCallback cb) = 0;
|
||||
|
||||
virtual net::DatagramSocket* getSocket() const { return {}; }
|
||||
virtual const net::DatagramSocket* getSocket() const { return {}; };
|
||||
|
||||
/**
|
||||
* Get the ID of the DHT node.
|
||||
@@ -93,9 +92,6 @@ 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.
|
||||
@@ -233,6 +229,18 @@ public:
|
||||
|
||||
virtual std::vector<SockAddr> getPublicAddress(sa_family_t family = 0) = 0;
|
||||
|
||||
/**
|
||||
* Enable or disable logging of DHT internal messages
|
||||
*/
|
||||
virtual void setLoggers(LogMethod error = {}, LogMethod warn = {}, LogMethod debug = {}) {
|
||||
if (logger_) {
|
||||
logger_->DBG = std::move(debug);
|
||||
logger_->WARN = std::move(warn);
|
||||
logger_->ERR = std::move(error);
|
||||
} else
|
||||
logger_= std::make_shared<Logger>(std::move(error), std::move(warn), std::move(debug));
|
||||
}
|
||||
|
||||
virtual void setLogger(const Logger& l) {
|
||||
if (logger_)
|
||||
*logger_ = l;
|
||||
@@ -266,7 +274,6 @@ public:
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Logger> logger_ {};
|
||||
std::queue<std::function<void()>> onConnectCallbacks_ {};
|
||||
};
|
||||
|
||||
} // namespace dht
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
* Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Vsevolod Ivanov <vsevolod.ivanov@savoirfairelinux.com>
|
||||
@@ -53,21 +53,19 @@ public:
|
||||
DhtProxyClient();
|
||||
|
||||
explicit DhtProxyClient(
|
||||
std::shared_ptr<crypto::Certificate> serverCA,
|
||||
crypto::Identity clientIdentity,
|
||||
std::function<void()> loopSignal,
|
||||
const std::string& serverHost,
|
||||
const std::string& userAgent = "",
|
||||
const std::string& pushClientId = "",
|
||||
const std::string& pushToken = "",
|
||||
const std::string& pushTopic = "",
|
||||
const std::string& pushPlatform = "",
|
||||
std::shared_ptr<Logger> logger = {}
|
||||
);
|
||||
std::shared_ptr<crypto::Certificate> serverCA, crypto::Identity clientIdentity,
|
||||
std::shared_ptr<asio::io_context::strand> strand, const std::string& serverHost,
|
||||
const std::string& pushClientId = "", std::shared_ptr<Logger> logger = {});
|
||||
|
||||
void setHeaderFields(http::Request& request);
|
||||
|
||||
virtual void setPushNotificationToken(const std::string& token) override;
|
||||
virtual void setPushNotificationToken(const std::string& token) override {
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
deviceKey_ = token;
|
||||
#else
|
||||
(void) token;
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual void setPushNotificationTopic(const std::string& topic) override {
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
@@ -91,9 +89,6 @@ public:
|
||||
* Get the ID of the node.
|
||||
*/
|
||||
inline const InfoHash& getNodeId() const override { return myid; }
|
||||
void setOnPublicAddressChanged(PublicAddressChangedCb cb) override {
|
||||
publicAddressChangedCb_ = std::move(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current status of the node for the given family.
|
||||
@@ -102,6 +97,13 @@ 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.
|
||||
@@ -218,11 +220,6 @@ 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.
|
||||
@@ -290,14 +287,8 @@ public:
|
||||
}
|
||||
void connectivityChanged() override {
|
||||
getProxyInfos();
|
||||
loopSignal_();
|
||||
}
|
||||
|
||||
void listenKeepIdle(uint32_t seconds) {
|
||||
listenKeepIdle_ = seconds;
|
||||
}
|
||||
inline uint32_t listenKeepIdle() { return listenKeepIdle_; }
|
||||
|
||||
private:
|
||||
/**
|
||||
* Start the connection with a server.
|
||||
@@ -353,7 +344,7 @@ private:
|
||||
std::string proxyUrl_;
|
||||
dht::crypto::Identity clientIdentity_;
|
||||
std::shared_ptr<dht::crypto::Certificate> serverCertificate_;
|
||||
std::string userAgent_ {"OpenDHT"};
|
||||
//std::pair<std::string, std::string> serverHostService_;
|
||||
std::string pushClientId_;
|
||||
std::string pushSessionId_;
|
||||
|
||||
@@ -367,7 +358,8 @@ private:
|
||||
SockAddr publicAddressV4_;
|
||||
SockAddr publicAddressV6_;
|
||||
std::atomic_bool launchConnectedCbs_ {false};
|
||||
PublicAddressChangedCb publicAddressChangedCb_ {};
|
||||
std::vector<StatusCallback> onStateChangeCallbacks_ {};
|
||||
std::queue<std::function<void()>> onConnectCallbacks_ {};
|
||||
|
||||
InfoHash myid {};
|
||||
|
||||
@@ -378,7 +370,10 @@ private:
|
||||
* ASIO I/O Context for sockets in httpClient_
|
||||
* Note: Each context is used in one thread only
|
||||
*/
|
||||
asio::io_context httpContext_;
|
||||
//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_;
|
||||
std::shared_ptr<http::Resolver> resolver_;
|
||||
|
||||
@@ -398,17 +393,6 @@ private:
|
||||
size_t listenerToken_ {0};
|
||||
std::map<InfoHash, ProxySearch> searches_;
|
||||
|
||||
/**
|
||||
* Keepalive idle time for listen
|
||||
*/
|
||||
uint32_t listenKeepIdle_ {120};
|
||||
|
||||
/**
|
||||
* Callbacks should be executed in the main thread.
|
||||
*/
|
||||
std::mutex lockCallbacks_;
|
||||
std::vector<std::function<void()>> callbacks_;
|
||||
|
||||
Sp<InfoState> infoState_;
|
||||
|
||||
/**
|
||||
@@ -454,8 +438,6 @@ private:
|
||||
#endif
|
||||
#endif
|
||||
|
||||
const std::function<void()> loopSignal_;
|
||||
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
std::string fillBody(bool resubscribe);
|
||||
void getPushRequest(Json::Value&) const;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
* Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Vsevolod Ivanov <vsevolod.ivanov@savoirfairelinux.com>
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "infohash.h"
|
||||
#include "proxy.h"
|
||||
#include "scheduler.h"
|
||||
#include "sockaddr.h"
|
||||
#include "value.h"
|
||||
#include "http.h"
|
||||
|
||||
@@ -46,17 +45,17 @@ enum class PushType {
|
||||
}
|
||||
MSGPACK_ADD_ENUM(dht::PushType)
|
||||
|
||||
namespace http {
|
||||
class Request;
|
||||
struct ListenerSession;
|
||||
}
|
||||
|
||||
namespace Json {
|
||||
class Value;
|
||||
}
|
||||
|
||||
namespace dht {
|
||||
|
||||
namespace http {
|
||||
class Request;
|
||||
struct ListenerSession;
|
||||
}
|
||||
|
||||
class DhtRunner;
|
||||
|
||||
using RestRouter = restinio::router::express_router_t<>;
|
||||
@@ -87,8 +86,9 @@ public:
|
||||
*/
|
||||
DhtProxyServer(const std::shared_ptr<DhtRunner>& dht,
|
||||
const ProxyServerConfig& config = {},
|
||||
const std::shared_ptr<log::Logger>& logger = {});
|
||||
const std::shared_ptr<dht::Logger>& logger = {});
|
||||
|
||||
void stop();
|
||||
virtual ~DhtProxyServer();
|
||||
|
||||
DhtProxyServer(const DhtProxyServer& other) = delete;
|
||||
@@ -98,32 +98,6 @@ public:
|
||||
|
||||
asio::io_context& io_context() const;
|
||||
|
||||
using clock = std::chrono::steady_clock;
|
||||
using time_point = clock::time_point;
|
||||
|
||||
struct PushStats {
|
||||
uint64_t highPriorityCount {0};
|
||||
uint64_t normalPriorityCount {0};
|
||||
|
||||
void increment(bool highPriority) {
|
||||
if (highPriority)
|
||||
highPriorityCount++;
|
||||
else
|
||||
normalPriorityCount++;
|
||||
}
|
||||
|
||||
Json::Value toJson() const {
|
||||
Json::Value val;
|
||||
val["highPriorityCount"] = static_cast<Json::UInt64>(highPriorityCount);
|
||||
val["normalPriorityCount"] = static_cast<Json::UInt64>(normalPriorityCount);
|
||||
return val;
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
return fmt::format("{} high priority, {} normal priority", highPriorityCount, normalPriorityCount);
|
||||
}
|
||||
};
|
||||
|
||||
struct ServerStats {
|
||||
/** Current number of listen operations */
|
||||
size_t listenCount {0};
|
||||
@@ -133,17 +107,6 @@ public:
|
||||
size_t totalPermanentPuts {0};
|
||||
/** Current number of push tokens with at least one listen operation */
|
||||
size_t pushListenersCount {0};
|
||||
|
||||
/** Time at which the server was started */
|
||||
time_point serverStartTime;
|
||||
/** Last time at which the stats were updated */
|
||||
time_point lastUpdated;
|
||||
/** Total number of push notification requests that the server attempted to
|
||||
* send since being started, broken down by type and priority level */
|
||||
PushStats androidPush;
|
||||
PushStats iosPush;
|
||||
PushStats unifiedPush;
|
||||
|
||||
/** Average requests per second */
|
||||
double requestRate {0};
|
||||
/** Node Info **/
|
||||
@@ -152,10 +115,6 @@ public:
|
||||
std::string toString() const {
|
||||
std::ostringstream ss;
|
||||
ss << "Listens: " << listenCount << " Puts: " << putCount << " PushListeners: " << pushListenersCount << std::endl;
|
||||
ss << "Push requests in the last " << print_duration(lastUpdated - serverStartTime) << ": "
|
||||
<< "[Android: " << androidPush.toString() << "], "
|
||||
<< "[iOS: " << iosPush.toString() << "], "
|
||||
<< "[Unified: " << unifiedPush.toString() << "]" << std::endl;
|
||||
ss << "Requests: " << requestRate << " per second." << std::endl;
|
||||
if (nodeInfo) {
|
||||
auto& ipv4 = nodeInfo->ipv4;
|
||||
@@ -177,11 +136,6 @@ public:
|
||||
result["putCount"] = static_cast<Json::UInt64>(putCount);
|
||||
result["totalPermanentPuts"] = static_cast<Json::UInt64>(totalPermanentPuts);
|
||||
result["pushListenersCount"] = static_cast<Json::UInt64>(pushListenersCount);
|
||||
result["serverStartTime"] = static_cast<Json::LargestInt>(to_time_t(serverStartTime));
|
||||
result["lastUpdated"] = static_cast<Json::LargestInt>(to_time_t(lastUpdated));
|
||||
result["androidPush"] = androidPush.toJson();
|
||||
result["iosPush"] = iosPush.toJson();
|
||||
result["unifiedPush"] = unifiedPush.toJson();
|
||||
result["requestRate"] = requestRate;
|
||||
if (nodeInfo)
|
||||
result["nodeInfo"] = nodeInfo->toJson();
|
||||
@@ -321,12 +275,6 @@ private:
|
||||
RequestStatus options(restinio::request_handle_t request,
|
||||
restinio::router::route_params_t params);
|
||||
|
||||
struct PushSessionContext {
|
||||
std::mutex lock;
|
||||
std::string sessionId;
|
||||
PushSessionContext(const std::string& id) : sessionId(id) {}
|
||||
};
|
||||
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
PushType getTypeFromString(const std::string& type);
|
||||
std::string getDefaultTopic(PushType type);
|
||||
@@ -383,24 +331,6 @@ private:
|
||||
void handleCancelPushListen(const asio::error_code &ec, const std::string pushToken,
|
||||
const InfoHash key, const std::string clientId);
|
||||
|
||||
/**
|
||||
* Handles a push listen request.
|
||||
*
|
||||
* @param infoHash The information hash associated with the push listen request.
|
||||
* @param pushToken The push token associated with the push listen request.
|
||||
* @param type The type of the push listen request.
|
||||
* @param clientId The client ID associated with the push listen request.
|
||||
* @param sessionCtx The shared pointer to the push session context associated with the push listen request.
|
||||
* @param topic The topic associated with the push listen request.
|
||||
* @param values The vector of shared pointers to values associated with the push listen request.
|
||||
* @param expired A boolean indicating whether the push listen request has expired.
|
||||
* @return true.
|
||||
*/
|
||||
bool handlePushListen(const InfoHash& infoHash, const std::string& pushToken,
|
||||
PushType type, const std::string& clientId,
|
||||
const std::shared_ptr<DhtProxyServer::PushSessionContext>& sessionCtx, const std::string& topic,
|
||||
const std::vector<std::shared_ptr<Value>>& values, bool expired);
|
||||
|
||||
#endif //OPENDHT_PUSH_NOTIFICATIONS
|
||||
|
||||
void handlePrintStats(const asio::error_code &ec);
|
||||
@@ -412,6 +342,9 @@ private:
|
||||
template <typename Is>
|
||||
void loadState(Is& is, size_t size);
|
||||
|
||||
using clock = std::chrono::steady_clock;
|
||||
using time_point = clock::time_point;
|
||||
|
||||
std::shared_ptr<asio::io_context> ioContext_;
|
||||
std::shared_ptr<DhtRunner> dht_;
|
||||
Json::StreamWriterBuilder jsonBuilder_;
|
||||
@@ -431,16 +364,11 @@ private:
|
||||
mutable std::mutex requestLock_;
|
||||
std::map<unsigned int /*id*/, std::shared_ptr<http::Request>> requests_;
|
||||
|
||||
std::shared_ptr<log::Logger> logger_;
|
||||
std::shared_ptr<dht::Logger> logger_;
|
||||
|
||||
std::shared_ptr<ServerStats> stats_;
|
||||
std::shared_ptr<NodeInfo> nodeInfo_ {};
|
||||
std::unique_ptr<asio::steady_timer> printStatsTimer_;
|
||||
const time_point serverStartTime_;
|
||||
mutable std::mutex pushStatsMutex_;
|
||||
PushStats androidPush_;
|
||||
PushStats iosPush_;
|
||||
PushStats unifiedPush_;
|
||||
|
||||
// Thread-safe access to listeners map.
|
||||
std::mutex lockListener_;
|
||||
@@ -448,6 +376,12 @@ private:
|
||||
std::map<restinio::connection_id_t, http::ListenerSession> listeners_;
|
||||
// Connection Listener observing conn state changes.
|
||||
std::shared_ptr<ConnectionListener> connListener_;
|
||||
|
||||
struct PushSessionContext {
|
||||
std::mutex lock;
|
||||
std::string sessionId;
|
||||
PushSessionContext(const std::string& id) : sessionId(id) {}
|
||||
};
|
||||
struct PermanentPut {
|
||||
time_point expiration;
|
||||
std::string pushToken;
|
||||
|
||||
+16
-41
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
@@ -24,10 +24,10 @@
|
||||
#include "infohash.h"
|
||||
#include "value.h"
|
||||
#include "callbacks.h"
|
||||
#include "sockaddr.h"
|
||||
#include "logger.h"
|
||||
#include "network_utils.h"
|
||||
#include "log_enable.h"
|
||||
#include "node_export.h"
|
||||
#include "network_utils.h"
|
||||
#include "udp_socket.h"
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
@@ -52,10 +52,7 @@ 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};
|
||||
@@ -64,7 +61,6 @@ public:
|
||||
std::string push_token {};
|
||||
std::string push_topic {};
|
||||
std::string push_platform {};
|
||||
std::string proxy_user_agent {};
|
||||
bool peer_discovery {false};
|
||||
bool peer_publish {false};
|
||||
std::shared_ptr<dht::crypto::Certificate> server_ca;
|
||||
@@ -75,12 +71,11 @@ 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 {};
|
||||
IdentityAnnouncedCb identityAnnouncedCb {};
|
||||
PublicAddressChangedCb publicAddressChangedCb {};
|
||||
std::unique_ptr<std::mt19937_64> rng {};
|
||||
Context() {}
|
||||
};
|
||||
|
||||
@@ -352,6 +347,7 @@ public:
|
||||
void setLogger(const Logger& logger) {
|
||||
setLogger(std::make_shared<Logger>(logger));
|
||||
}
|
||||
void setLoggers(LogMethod err = {}, LogMethod warn = {}, LogMethod debug = {});
|
||||
|
||||
/**
|
||||
* Only print logs related to the given InfoHash (if given), or disable filter (if zeroes).
|
||||
@@ -409,21 +405,6 @@ public:
|
||||
|
||||
void run(const Config& config, Context&& context);
|
||||
|
||||
void setOnStatusChanged(StatusCallback&& cb) {
|
||||
if (cb)
|
||||
statusCbs.emplace_back(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.
|
||||
*/
|
||||
@@ -478,17 +459,20 @@ 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 */
|
||||
std::unique_ptr<SecureDht> dht_;
|
||||
|
||||
@@ -507,11 +491,7 @@ private:
|
||||
mutable std::mutex dht_mtx {};
|
||||
std::thread dht_thread {};
|
||||
std::condition_variable cv {};
|
||||
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_prio {};*/
|
||||
std::queue<std::function<void(SecureDht&)>> pending_ops {};
|
||||
std::mutex storage_mtx {};
|
||||
|
||||
@@ -519,11 +499,6 @@ private:
|
||||
std::atomic_size_t ongoing_ops {0};
|
||||
std::vector<ShutdownCallback> shutdownCallbacks_;
|
||||
|
||||
NodeStatus status4 {NodeStatus::Disconnected},
|
||||
status6 {NodeStatus::Disconnected};
|
||||
|
||||
std::vector<StatusCallback> statusCbs {};
|
||||
|
||||
/** PeerDiscovery Parameters */
|
||||
std::shared_ptr<PeerDiscovery> peerDiscovery_;
|
||||
|
||||
|
||||
+38
-32
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Vsevolod Ivanov <vsevolod.ivanov@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -29,20 +29,23 @@
|
||||
# define snprintf snprintf
|
||||
#endif
|
||||
|
||||
#include <asio/ip/tcp.hpp>
|
||||
#include <asio/streambuf.hpp>
|
||||
#include <asio/ssl/context.hpp>
|
||||
#include <restinio/http_headers.hpp>
|
||||
#include <restinio/message_builders.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <future>
|
||||
|
||||
namespace Json {
|
||||
class Value;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
struct http_parser;
|
||||
struct http_parser_settings;
|
||||
}
|
||||
|
||||
namespace restinio {
|
||||
namespace impl {
|
||||
class tls_socket_t;
|
||||
@@ -50,9 +53,7 @@ class tls_socket_t;
|
||||
}
|
||||
|
||||
namespace dht {
|
||||
namespace log {
|
||||
struct Logger;
|
||||
}
|
||||
|
||||
namespace crypto {
|
||||
struct Certificate;
|
||||
@@ -87,9 +88,9 @@ public:
|
||||
class OPENDHT_PUBLIC Connection : public std::enable_shared_from_this<Connection>
|
||||
{
|
||||
public:
|
||||
Connection(asio::io_context& ctx, const bool ssl = true, std::shared_ptr<log::Logger> l = {});
|
||||
Connection(asio::io_context& ctx, const bool ssl = true, std::shared_ptr<dht::Logger> l = {});
|
||||
Connection(asio::io_context& ctx, std::shared_ptr<dht::crypto::Certificate> server_ca,
|
||||
const dht::crypto::Identity& identity, std::shared_ptr<log::Logger> l = {});
|
||||
const dht::crypto::Identity& identity, std::shared_ptr<dht::Logger> l = {});
|
||||
~Connection();
|
||||
|
||||
inline unsigned int id() const { return id_; };
|
||||
@@ -113,8 +114,6 @@ public:
|
||||
void async_read(size_t bytes, BytesHandlerCb cb);
|
||||
void async_read_some(size_t bytes, BytesHandlerCb cb);
|
||||
|
||||
void set_keepalive(uint32_t seconds);
|
||||
|
||||
const asio::ip::address& local_address() const;
|
||||
|
||||
void timeout(const std::chrono::seconds& timeout, HandlerCb cb = {});
|
||||
@@ -149,10 +148,21 @@ private:
|
||||
asio::ip::address local_address_;
|
||||
|
||||
std::unique_ptr<asio::steady_timer> timeout_timer_;
|
||||
std::shared_ptr<log::Logger> logger_;
|
||||
std::shared_ptr<dht::Logger> logger_;
|
||||
bool checkOcsp_ {false};
|
||||
};
|
||||
|
||||
/**
|
||||
* Session value associated with a connection_id_t key.
|
||||
*/
|
||||
struct ListenerSession
|
||||
{
|
||||
ListenerSession() = default;
|
||||
dht::InfoHash hash;
|
||||
std::future<size_t> token;
|
||||
std::shared_ptr<restinio::response_builder_t<restinio::chunked_output_t>> response;
|
||||
};
|
||||
|
||||
/* @class Resolver
|
||||
* @brief The purpose is to only resolve once to avoid mutliple dns requests per operation.
|
||||
*/
|
||||
@@ -162,15 +172,15 @@ public:
|
||||
using ResolverCb = std::function<void(const asio::error_code& ec,
|
||||
const std::vector<asio::ip::tcp::endpoint>& endpoints)>;
|
||||
|
||||
Resolver(asio::io_context& ctx, const std::string& url, std::shared_ptr<log::Logger> logger = {});
|
||||
Resolver(asio::io_context& ctx, const std::string& url, std::shared_ptr<dht::Logger> logger = {});
|
||||
Resolver(asio::io_context& ctx, const std::string& host, const std::string& service,
|
||||
const bool ssl = false, std::shared_ptr<log::Logger> logger = {});
|
||||
const bool ssl = false, std::shared_ptr<dht::Logger> logger = {});
|
||||
|
||||
// use already resolved endpoints with classes using this resolver
|
||||
Resolver(asio::io_context& ctx, std::vector<asio::ip::tcp::endpoint> endpoints,
|
||||
const bool ssl = false, std::shared_ptr<log::Logger> logger = {});
|
||||
const bool ssl = false, std::shared_ptr<dht::Logger> logger = {});
|
||||
Resolver(asio::io_context& ctx, const std::string& url, std::vector<asio::ip::tcp::endpoint> endpoints,
|
||||
std::shared_ptr<log::Logger> logger = {});
|
||||
std::shared_ptr<dht::Logger> logger = {});
|
||||
|
||||
~Resolver();
|
||||
|
||||
@@ -180,12 +190,10 @@ public:
|
||||
|
||||
void add_callback(ResolverCb cb, sa_family_t family = AF_UNSPEC);
|
||||
|
||||
std::shared_ptr<log::Logger> getLogger() const {
|
||||
std::shared_ptr<Logger> getLogger() const {
|
||||
return logger_;
|
||||
}
|
||||
|
||||
void cancel();
|
||||
|
||||
private:
|
||||
void resolve(const std::string& host, const std::string& service);
|
||||
|
||||
@@ -200,7 +208,7 @@ private:
|
||||
bool completed_ {false};
|
||||
std::queue<ResolverCb> cbs_;
|
||||
|
||||
std::shared_ptr<log::Logger> logger_;
|
||||
std::shared_ptr<dht::Logger> logger_;
|
||||
};
|
||||
|
||||
class Request;
|
||||
@@ -232,14 +240,14 @@ public:
|
||||
|
||||
// resolves implicitly
|
||||
Request(asio::io_context& ctx, const std::string& url, const Json::Value& json, OnJsonCb jsoncb,
|
||||
std::shared_ptr<log::Logger> logger = {});
|
||||
std::shared_ptr<dht::Logger> logger = {});
|
||||
Request(asio::io_context& ctx, const std::string& url, OnJsonCb jsoncb,
|
||||
std::shared_ptr<log::Logger> logger = {});
|
||||
std::shared_ptr<dht::Logger> logger = {});
|
||||
|
||||
Request(asio::io_context& ctx, const std::string& url, std::shared_ptr<log::Logger> logger = {});
|
||||
Request(asio::io_context& ctx, const std::string& url, std::shared_ptr<dht::Logger> logger = {});
|
||||
Request(asio::io_context& ctx, const std::string& host, const std::string& service,
|
||||
const bool ssl = false, std::shared_ptr<log::Logger> logger = {});
|
||||
Request(asio::io_context& ctx, const std::string& url, OnDoneCb onDone, std::shared_ptr<log::Logger> logger = {});
|
||||
const bool ssl = false, std::shared_ptr<dht::Logger> logger = {});
|
||||
Request(asio::io_context& ctx, const std::string& url, OnDoneCb onDone, std::shared_ptr<dht::Logger> logger = {});
|
||||
|
||||
// user defined resolver
|
||||
Request(asio::io_context& ctx, std::shared_ptr<Resolver> resolver, sa_family_t family = AF_UNSPEC);
|
||||
@@ -247,7 +255,7 @@ public:
|
||||
|
||||
// user defined resolved endpoints
|
||||
Request(asio::io_context& ctx, std::vector<asio::ip::tcp::endpoint>&& endpoints,
|
||||
const bool ssl = false, std::shared_ptr<log::Logger> logger = {});
|
||||
const bool ssl = false, std::shared_ptr<dht::Logger> logger = {});
|
||||
|
||||
~Request();
|
||||
|
||||
@@ -274,7 +282,7 @@ public:
|
||||
|
||||
void set_certificate_authority(std::shared_ptr<dht::crypto::Certificate> certificate);
|
||||
void set_identity(const dht::crypto::Identity& identity);
|
||||
void set_logger(std::shared_ptr<log::Logger> logger);
|
||||
void set_logger(std::shared_ptr<dht::Logger> logger);
|
||||
|
||||
/**
|
||||
* Define the HTTP header/body as per https://tools.ietf.org/html/rfc7230.
|
||||
@@ -320,11 +328,9 @@ private:
|
||||
|
||||
void build();
|
||||
|
||||
static std::string url_encode(std::string_view value);
|
||||
|
||||
void init_default_headers();
|
||||
/**
|
||||
* Initialized and wraps the llhttp callbacks with our user defined callbacks.
|
||||
* Initialized and wraps the http_parser callbacks with our user defined callbacks.
|
||||
*/
|
||||
void init_parser();
|
||||
|
||||
@@ -341,7 +347,7 @@ private:
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
std::shared_ptr<log::Logger> logger_;
|
||||
std::shared_ptr<dht::Logger> logger_;
|
||||
|
||||
restinio::http_request_header_t header_;
|
||||
std::map<restinio::http_field_t, std::string> headers_;
|
||||
@@ -366,8 +372,8 @@ private:
|
||||
Response response_ {};
|
||||
std::string request_;
|
||||
std::atomic<bool> finishing_ {false};
|
||||
std::unique_ptr<llhttp_t> parser_;
|
||||
std::unique_ptr<llhttp_settings_t> parser_s_;
|
||||
std::unique_ptr<http_parser> parser_;
|
||||
std::unique_ptr<http_parser_settings> parser_s_;
|
||||
|
||||
// Next request in case of redirect following
|
||||
std::shared_ptr<Request> next_;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Nicolas Reynaud <nicolas.reynaud@savoirfairelinux.com>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* 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
|
||||
@@ -22,20 +22,7 @@
|
||||
#include "rng.h"
|
||||
|
||||
#include <msgpack.hpp>
|
||||
#include <fmt/core.h>
|
||||
|
||||
#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 <asio/ip/udp.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
@@ -51,6 +38,7 @@ typedef uint16_t in_port_t;
|
||||
|
||||
namespace dht {
|
||||
|
||||
|
||||
using byte = uint8_t;
|
||||
|
||||
namespace crypto {
|
||||
@@ -323,7 +311,7 @@ Hash<N>
|
||||
Hash<N>::getRandom()
|
||||
{
|
||||
Hash h;
|
||||
std::random_device rdev;
|
||||
crypto::random_device rdev;
|
||||
std::uniform_int_distribution<uint32_t> rand_int;
|
||||
auto a = reinterpret_cast<uint32_t*>(h.data());
|
||||
auto b = reinterpret_cast<uint32_t*>(h.data() + h.size());
|
||||
@@ -395,10 +383,3 @@ Hash<N>::toString() const
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
struct fmt::formatter<dht::Hash<N>>: formatter<string_view> {
|
||||
constexpr auto format(const dht::Hash<N>& c, format_context& ctx) const {
|
||||
return formatter<string_view>::format(c.to_view(), ctx);
|
||||
}
|
||||
};
|
||||
+38
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
@@ -20,7 +20,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "def.h"
|
||||
#include "logger.h"
|
||||
#include "log_enable.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
@@ -34,6 +34,42 @@ class DhtRunner;
|
||||
*/
|
||||
namespace log {
|
||||
|
||||
/**
|
||||
* Terminal colors for logging
|
||||
*/
|
||||
namespace Color {
|
||||
enum Code {
|
||||
FG_RED = 31,
|
||||
FG_GREEN = 32,
|
||||
FG_YELLOW = 33,
|
||||
FG_BLUE = 34,
|
||||
FG_DEFAULT = 39,
|
||||
BG_RED = 41,
|
||||
BG_GREEN = 42,
|
||||
BG_BLUE = 44,
|
||||
BG_DEFAULT = 49
|
||||
};
|
||||
class Modifier {
|
||||
const Code code;
|
||||
public:
|
||||
constexpr Modifier(Code pCode) : code(pCode) {}
|
||||
friend std::ostream&
|
||||
operator<<(std::ostream& os, const Modifier& mod) {
|
||||
return os << "\033[" << mod.code << 'm';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
constexpr const Color::Modifier def(Color::FG_DEFAULT);
|
||||
constexpr const Color::Modifier red(Color::FG_RED);
|
||||
constexpr const Color::Modifier yellow(Color::FG_YELLOW);
|
||||
|
||||
/**
|
||||
* Print va_list to std::ostream (used for logging).
|
||||
*/
|
||||
OPENDHT_PUBLIC void
|
||||
printLog(std::ostream &s, char const *m, va_list args);
|
||||
|
||||
OPENDHT_PUBLIC
|
||||
std::shared_ptr<Logger> getStdLogger();
|
||||
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "infohash.h"
|
||||
|
||||
#include <cstdarg>
|
||||
|
||||
#ifndef OPENDHT_LOG
|
||||
#define OPENDHT_LOG true
|
||||
#endif
|
||||
|
||||
namespace dht {
|
||||
|
||||
// Logging related utility functions
|
||||
|
||||
/**
|
||||
* Wrapper for logging methods
|
||||
*/
|
||||
struct LogMethod {
|
||||
LogMethod() = default;
|
||||
|
||||
LogMethod(LogMethod&& l) : func(std::move(l.func)) {}
|
||||
LogMethod(const LogMethod& l) : func(l.func) {}
|
||||
|
||||
LogMethod& operator=(dht::LogMethod&& l) {
|
||||
func = std::forward<LogMethod>(l.func);
|
||||
return *this;
|
||||
}
|
||||
LogMethod& operator=(const dht::LogMethod& l) {
|
||||
func = l.func;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
explicit LogMethod(T&& t) : func(std::forward<T>(t)) {}
|
||||
|
||||
template<typename T>
|
||||
LogMethod(const T& t) : func(t) {}
|
||||
|
||||
void operator()(char const* format, ...) const {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
func(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
inline void log(char const* format, va_list args) const {
|
||||
func(format, args);
|
||||
}
|
||||
explicit operator bool() const {
|
||||
return (bool)func;
|
||||
}
|
||||
|
||||
void logPrintable(const uint8_t *buf, size_t buflen) const {
|
||||
std::string buf_clean(buflen, '\0');
|
||||
for (size_t i=0; i<buflen; i++)
|
||||
buf_clean[i] = isprint(buf[i]) ? buf[i] : '.';
|
||||
(*this)("%s", buf_clean.c_str());
|
||||
}
|
||||
private:
|
||||
std::function<void(char const*, va_list)> func;
|
||||
};
|
||||
|
||||
struct Logger {
|
||||
LogMethod ERR = {};
|
||||
LogMethod WARN = {};
|
||||
LogMethod DBG = {};
|
||||
|
||||
Logger() = default;
|
||||
Logger(LogMethod&& err, LogMethod&& warn, LogMethod&& dbg)
|
||||
: ERR(std::move(err)), WARN(std::move(warn)), DBG(std::move(dbg)) {}
|
||||
void setFilter(const InfoHash& f) {
|
||||
filter_ = f;
|
||||
filterEnable_ = static_cast<bool>(filter_);
|
||||
}
|
||||
inline void log0(const LogMethod& logger, char const* format, va_list args) const {
|
||||
#if OPENDHT_LOG
|
||||
if (logger and not filterEnable_)
|
||||
logger.log(format, args);
|
||||
#endif
|
||||
}
|
||||
inline void log1(const LogMethod& logger, const InfoHash& f, char const* format, va_list args) const {
|
||||
#if OPENDHT_LOG
|
||||
if (logger and (not filterEnable_ or f == filter_))
|
||||
logger.log(format, args);
|
||||
#endif
|
||||
}
|
||||
inline void log2(const LogMethod& logger, const InfoHash& f1, const InfoHash& f2, char const* format, va_list args) const {
|
||||
#if OPENDHT_LOG
|
||||
if (logger and (not filterEnable_ or f1 == filter_ or f2 == filter_))
|
||||
logger.log(format, args);
|
||||
#endif
|
||||
}
|
||||
inline void d(char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log0(DBG, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void d(const InfoHash& f, char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log1(DBG, f, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void d(const InfoHash& f1, const InfoHash& f2, char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log2(DBG, f1, f2, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void w(char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log0(WARN, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void w(const InfoHash& f, char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log1(WARN, f, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void w(const InfoHash& f1, const InfoHash& f2, char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log2(WARN, f1, f2, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void e(char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log0(ERR, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void e(const InfoHash& f, char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log1(ERR, f, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void e(const InfoHash& f1, const InfoHash& f2, char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log2(ERR, f1, f2, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
private:
|
||||
bool filterEnable_ {false};
|
||||
InfoHash filter_ {};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 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 "infohash.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/printf.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
#include <cstdarg>
|
||||
|
||||
namespace dht {
|
||||
namespace log {
|
||||
|
||||
enum class LogLevel {
|
||||
debug, warning, error
|
||||
};
|
||||
|
||||
using LogMethod = std::function<void(LogLevel, std::string&&)>;
|
||||
|
||||
struct OPENDHT_PUBLIC Logger {
|
||||
Logger() = delete;
|
||||
Logger(LogMethod&& l)
|
||||
: logger(std::move(l)) {
|
||||
if (!logger)
|
||||
throw std::invalid_argument{"logger and loggerf must be set"};
|
||||
}
|
||||
void setFilter(const InfoHash& f) {
|
||||
filter_ = f;
|
||||
filterEnable_ = static_cast<bool>(filter_);
|
||||
}
|
||||
inline void log0(LogLevel level, fmt::string_view format, fmt::printf_args args) const {
|
||||
if (not filterEnable_)
|
||||
logger(level, fmt::vsprintf(format, args));
|
||||
}
|
||||
inline void log1(LogLevel level, const InfoHash& f, fmt::string_view format, fmt::printf_args args) const {
|
||||
if (not filterEnable_ or f == filter_)
|
||||
logger(level, fmt::vsprintf(format, args));
|
||||
}
|
||||
inline void log2(LogLevel level, const InfoHash& f1, const InfoHash& f2, fmt::string_view format, fmt::printf_args args) const {
|
||||
if (not filterEnable_ or f1 == filter_ or f2 == filter_)
|
||||
logger(level, fmt::vsprintf(format, args));
|
||||
}
|
||||
template<typename S, typename... Args>
|
||||
inline void debug(S&& format, Args&&... args) const {
|
||||
logger(LogLevel::debug, fmt::format(format, args...));
|
||||
}
|
||||
template<typename S, typename... Args>
|
||||
inline void warn(S&& format, Args&&... args) const {
|
||||
logger(LogLevel::warning, fmt::format(format, args...));
|
||||
}
|
||||
template<typename S, typename... Args>
|
||||
inline void error(S&& format, Args&&... args) const {
|
||||
logger(LogLevel::error, fmt::format(format, args...));
|
||||
}
|
||||
template <typename... T>
|
||||
inline void d(fmt::format_string<T...> format, T&&... args) const {
|
||||
log0(LogLevel::debug, format, fmt::make_printf_args(args...));
|
||||
}
|
||||
template <typename... T>
|
||||
inline void d(const InfoHash& f, fmt::format_string<T...> format, T&&... args) const {
|
||||
log1(LogLevel::debug, f, format, fmt::make_printf_args(args...));
|
||||
}
|
||||
template <typename... T>
|
||||
inline void d(const InfoHash& f1, const InfoHash& f2, fmt::format_string<T...> format, T&&... args) const {
|
||||
log2(LogLevel::debug, f1, f2, format, fmt::make_printf_args(args...));
|
||||
}
|
||||
template <typename... T>
|
||||
inline void w(fmt::format_string<T...> format, T&&... args) const {
|
||||
log0(LogLevel::warning, format, fmt::make_printf_args(args...));
|
||||
}
|
||||
template <typename... T>
|
||||
inline void w(const InfoHash& f, fmt::format_string<T...> format, T&&... args) const {
|
||||
log1(LogLevel::warning, f, format, fmt::make_printf_args(args...));
|
||||
}
|
||||
template <typename... T>
|
||||
inline void w(const InfoHash& f1, const InfoHash& f2, fmt::format_string<T...> format, T&&... args) const {
|
||||
log2(LogLevel::warning, f1, f2, format, fmt::make_printf_args(args...));
|
||||
}
|
||||
template <typename... T>
|
||||
inline void e(fmt::format_string<T...> format, T&&... args) const {
|
||||
log0(LogLevel::error, format, fmt::make_printf_args(args...));
|
||||
}
|
||||
template <typename... T>
|
||||
inline void e(const InfoHash& f, fmt::format_string<T...> format, T&&... args) const {
|
||||
log1(LogLevel::error, f, format, fmt::make_printf_args(args...));
|
||||
}
|
||||
template <typename... T>
|
||||
inline void e(const InfoHash& f1, const InfoHash& f2, fmt::format_string<T...> format, T&&... args) const {
|
||||
log2(LogLevel::error, f1, f2, format, fmt::make_printf_args(args...));
|
||||
}
|
||||
private:
|
||||
LogMethod logger = {};
|
||||
bool filterEnable_ {false};
|
||||
InfoHash filter_ {};
|
||||
};
|
||||
|
||||
}
|
||||
using Logger = log::Logger;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
@@ -19,16 +19,19 @@
|
||||
|
||||
#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 "logger.h"
|
||||
#include "network_utils.h"
|
||||
#include "log_enable.h"
|
||||
#include "udp_socket.h"
|
||||
|
||||
#include <asio/strand.hpp>
|
||||
#include <asio/ip/udp.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
@@ -44,15 +47,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};
|
||||
ssize_t max_peer_req_per_sec {0};
|
||||
bool is_client {false};
|
||||
};
|
||||
|
||||
class DhtProtocolException : public DhtException {
|
||||
@@ -218,10 +216,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,
|
||||
@@ -234,7 +232,9 @@ public:
|
||||
|
||||
~NetworkEngine();
|
||||
|
||||
net::DatagramSocket* getSocket() const { return dht_socket.get(); };
|
||||
asio::io_context& context() const { return strand_->context(); }
|
||||
|
||||
const net::DatagramSocket* getSocket() const { return dht_socket.get(); };
|
||||
|
||||
void clear();
|
||||
|
||||
@@ -442,8 +442,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, scheduler.time(), 0);
|
||||
Sp<Node> insertNode(const InfoHash& id, const SockAddr& addr) {
|
||||
auto n = cache.getNode(id, addr, time(), 0);
|
||||
onNewNode(n, 0);
|
||||
return n;
|
||||
}
|
||||
@@ -475,6 +475,12 @@ 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:
|
||||
|
||||
@@ -500,7 +506,9 @@ private:
|
||||
|
||||
static constexpr size_t MTU {1280};
|
||||
static constexpr size_t MAX_PACKET_VALUE_SIZE {600};
|
||||
static constexpr size_t MAX_MESSAGE_VALUE_SIZE {56 * 1024};
|
||||
static constexpr size_t MAX_MESSAGE_VALUE_SIZE {16 * MTU};
|
||||
|
||||
static const std::string my_v;
|
||||
|
||||
void process(std::unique_ptr<ParsedMessage>&&, const SockAddr& from);
|
||||
|
||||
@@ -529,7 +537,7 @@ private:
|
||||
|
||||
|
||||
// basic wrapper for socket sendto function
|
||||
int send(const SockAddr& addr, const char *buf, size_t len, bool confirmed = false);
|
||||
asio::error_code 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;
|
||||
@@ -572,9 +580,10 @@ private:
|
||||
void deserializeNodes(ParsedMessage& msg, const SockAddr& from);
|
||||
|
||||
/* DHT info */
|
||||
Sp<strand> strand_;
|
||||
const InfoHash& myid;
|
||||
const NetworkConfig config {};
|
||||
const std::unique_ptr<DatagramSocket> dht_socket;
|
||||
std::unique_ptr<DatagramSocket> dht_socket;
|
||||
Sp<Logger> logger_;
|
||||
std::mt19937_64& rd;
|
||||
|
||||
@@ -582,7 +591,7 @@ private:
|
||||
|
||||
// global limiting should be triggered by at least 8 different IPs
|
||||
using IpLimiter = RateLimiter;
|
||||
using IpLimiterMap = std::map<SockAddr, IpLimiter, SockAddr::ipCmp>;
|
||||
using IpLimiterMap = std::map<SockAddr, IpLimiter, ipCmp>;
|
||||
IpLimiterMap address_rate_limiter;
|
||||
RateLimiter rate_limiter;
|
||||
ssize_t limiter_maintenance {0};
|
||||
@@ -593,10 +602,9 @@ private:
|
||||
|
||||
MessageStats in_stats {}, out_stats {};
|
||||
std::set<SockAddr> blacklist {};
|
||||
|
||||
Scheduler& scheduler;
|
||||
|
||||
bool logIncoming_ {false};
|
||||
|
||||
time_point now {clock::now()};
|
||||
};
|
||||
|
||||
} /* namespace net */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -19,143 +19,21 @@
|
||||
|
||||
#include "def.h"
|
||||
|
||||
#include "sockaddr.h"
|
||||
#include "utils.h"
|
||||
#include "logger.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <ws2tcpip.h>
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#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 * 64;
|
||||
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);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
#include "infohash.h" // includes socket structures
|
||||
#include "utils.h"
|
||||
#include "sockaddr.h"
|
||||
#include "node_export.h"
|
||||
|
||||
#include <list>
|
||||
@@ -50,16 +49,13 @@ 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 {
|
||||
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; }
|
||||
|
||||
@@ -96,7 +92,7 @@ struct Node {
|
||||
ne.addr = addr;
|
||||
return ne;
|
||||
}
|
||||
sa_family_t getFamily() const { return addr.getFamily(); }
|
||||
sa_family_t getFamily() const { return addr.protocol().family(); }
|
||||
|
||||
void update(const SockAddr&);
|
||||
|
||||
@@ -139,7 +135,7 @@ struct Node {
|
||||
*/
|
||||
Tid getNewTid() {
|
||||
++transaction_id;
|
||||
return transaction_id ? transaction_id : ++transaction_id;
|
||||
return transaction_id ? ++transaction_id : transaction_id;
|
||||
}
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
|
||||
#include "def.h"
|
||||
#include "infohash.h"
|
||||
#include "sockaddr.h"
|
||||
#include <asio/ip/udp.hpp>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
@@ -27,7 +29,7 @@ using namespace std::literals;
|
||||
|
||||
struct OPENDHT_PUBLIC NodeExport {
|
||||
InfoHash id;
|
||||
SockAddr addr;
|
||||
asio::ip::udp::endpoint addr;
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& pk) const
|
||||
@@ -36,8 +38,8 @@ struct OPENDHT_PUBLIC NodeExport {
|
||||
pk.pack("id"sv);
|
||||
pk.pack(id);
|
||||
pk.pack("addr"sv);
|
||||
pk.pack_bin(addr.getLength());
|
||||
pk.pack_bin_body((const char*)addr.get(), (size_t)addr.getLength());
|
||||
pk.pack_bin(addr.size());
|
||||
pk.pack_bin_body((const char*)addr.data(), addr.size());
|
||||
}
|
||||
|
||||
void msgpack_unpack(msgpack::object o);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
|
||||
* Vsevolod Ivanov <vsevolod.ivanov@savoirfairelinux.com>
|
||||
*
|
||||
@@ -20,12 +20,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "def.h"
|
||||
#include "sockaddr.h"
|
||||
#include "infohash.h"
|
||||
#include "logger.h"
|
||||
#include "utils.h"
|
||||
#include "log_enable.h"
|
||||
|
||||
#include <asio/steady_timer.hpp>
|
||||
#include <thread>
|
||||
|
||||
namespace asio {
|
||||
@@ -38,34 +36,31 @@ class OPENDHT_PUBLIC PeerDiscovery
|
||||
{
|
||||
public:
|
||||
static constexpr in_port_t DEFAULT_PORT = 8888;
|
||||
using ServiceDiscoveredCallback = std::function<void(msgpack::object &&, SockAddr &&)>;
|
||||
using ServiceDiscoveredCallback = std::function<void(msgpack::object&&, asio::ip::udp::endpoint)>;
|
||||
|
||||
PeerDiscovery(in_port_t port = DEFAULT_PORT,
|
||||
std::shared_ptr<asio::io_context> ioContext = {},
|
||||
std::shared_ptr<Logger> logger = {});
|
||||
PeerDiscovery(in_port_t port = DEFAULT_PORT, std::shared_ptr<asio::io_context> ioContext = {}, std::shared_ptr<Logger> logger = {});
|
||||
~PeerDiscovery();
|
||||
|
||||
/**
|
||||
* startDiscovery - Keep Listening data from the sender until node is joinned
|
||||
* or stop is called
|
||||
*/
|
||||
* startDiscovery - Keep Listening data from the sender until node is joinned or stop is called
|
||||
*/
|
||||
void startDiscovery(const std::string &type, ServiceDiscoveredCallback callback);
|
||||
|
||||
template <typename T>
|
||||
void startDiscovery(const std::string &type, std::function<void(T &&, SockAddr &&)> cb) {
|
||||
startDiscovery(type, [cb](msgpack::object &&ob, SockAddr &&addr) {
|
||||
template<typename T>
|
||||
void startDiscovery(const std::string &type, std::function<void(T&&, SockAddr&&)> cb) {
|
||||
startDiscovery(type, [cb](msgpack::object&& ob, SockAddr&& addr) {
|
||||
cb(ob.as<T>(), std::move(addr));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* startPublish - Keeping sending data until node is joinned or stop is called
|
||||
*/
|
||||
*/
|
||||
void startPublish(const std::string &type, const msgpack::sbuffer &pack_buf);
|
||||
void startPublish(sa_family_t domain, const std::string &type, const msgpack::sbuffer &pack_buf);
|
||||
|
||||
template <typename T>
|
||||
void startPublish(const std::string &type, const T &object) {
|
||||
template<typename T>
|
||||
void startPublish(const std::string &type, const T& object) {
|
||||
msgpack::sbuffer buf;
|
||||
msgpack::pack(buf, object);
|
||||
startPublish(type, buf);
|
||||
@@ -73,24 +68,22 @@ public:
|
||||
|
||||
/**
|
||||
* Thread Stopper
|
||||
*/
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* Remove possible callBack to discovery
|
||||
*/
|
||||
*/
|
||||
bool stopDiscovery(const std::string &type);
|
||||
|
||||
/**
|
||||
* Remove different serivce message to send
|
||||
*/
|
||||
*/
|
||||
bool stopPublish(const std::string &type);
|
||||
bool stopPublish(sa_family_t domain, const std::string &type);
|
||||
|
||||
void connectivityChanged();
|
||||
|
||||
void stopConnectivityChanged();
|
||||
|
||||
private:
|
||||
class DomainPeerDiscovery;
|
||||
std::unique_ptr<DomainPeerDiscovery> peerDiscovery4_;
|
||||
@@ -99,4 +92,4 @@ private:
|
||||
std::thread ioRunnner_;
|
||||
};
|
||||
|
||||
} // namespace dht
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* 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
|
||||
|
||||
+96
-41
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* 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
|
||||
@@ -27,49 +27,104 @@
|
||||
namespace dht {
|
||||
namespace crypto {
|
||||
|
||||
/**
|
||||
* Generate a seeded random engine.
|
||||
*/
|
||||
template<class T = std::mt19937, std::size_t N = T::state_size+1>
|
||||
auto getSeededRandomEngine() -> typename std::enable_if<!!N, T>::type {
|
||||
std::array<typename T::result_type, N> random_data;
|
||||
constexpr auto gen = [](std::random_device& source) -> typename T::result_type {
|
||||
for (unsigned j=0; j<64; j++) {
|
||||
try {
|
||||
return source();
|
||||
} catch (...) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(500));
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("Can't generate random number");
|
||||
};
|
||||
for (unsigned i=0; i<8; i++) {
|
||||
try {
|
||||
std::random_device source;
|
||||
for (auto& r : random_data)
|
||||
r = gen(source);
|
||||
std::seed_seq seed(
|
||||
(std::seed_seq::result_type*)random_data.data(),
|
||||
(std::seed_seq::result_type*)(random_data.data() + random_data.size()));
|
||||
return T(seed);
|
||||
} catch (...) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(500));
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("Can't seed random seed");
|
||||
}
|
||||
#ifndef _MSC_VER
|
||||
#ifdef _WIN32
|
||||
|
||||
/**
|
||||
* Generate a random engine from another source.
|
||||
* Hardware random number generator using Intel RDRAND/RDSEED,
|
||||
* API-compatible with std::random_device.
|
||||
*/
|
||||
template<class T = std::mt19937, std::size_t N = T::state_size+1>
|
||||
auto getDerivedRandomEngine(T& source) -> typename std::enable_if<!!N, T>::type {
|
||||
std::array<typename T::result_type, N> random_data;
|
||||
std::generate(random_data.begin(), random_data.end(), std::ref(source));
|
||||
std::seed_seq seed(
|
||||
(std::seed_seq::result_type*)random_data.data(),
|
||||
(std::seed_seq::result_type*)(random_data.data() + random_data.size()));
|
||||
return T(seed);
|
||||
class random_device {
|
||||
public:
|
||||
using result_type = std::random_device::result_type;
|
||||
using pseudo_engine = std::mt19937_64;
|
||||
|
||||
/**
|
||||
* Current implementation assumption : result_type must be of a size
|
||||
* supported by Intel RDRAND/RDSEED.
|
||||
* result_type is unsigned int so this is currently safe.
|
||||
*/
|
||||
static_assert(
|
||||
sizeof(result_type) == 2 ||
|
||||
sizeof(result_type) == 4 ||
|
||||
sizeof(result_type) == 8,
|
||||
"result_type must be 16, 32 or 64 bits");
|
||||
|
||||
random_device();
|
||||
|
||||
result_type operator()();
|
||||
|
||||
static constexpr result_type min() {
|
||||
return std::numeric_limits<result_type>::lowest();
|
||||
}
|
||||
|
||||
static constexpr result_type max() {
|
||||
return std::numeric_limits<result_type>::max();
|
||||
}
|
||||
|
||||
double entropy() const {
|
||||
if (hasRdrand() or hasRdseed())
|
||||
return 1.;
|
||||
return 0.;
|
||||
}
|
||||
|
||||
static bool hasRdrand() {
|
||||
static const bool hasrdrand = _hasRdrand();
|
||||
return hasrdrand;
|
||||
}
|
||||
|
||||
static bool hasRdseed() {
|
||||
static const bool hasrdseed = _hasRdseed();
|
||||
return hasrdseed;
|
||||
}
|
||||
|
||||
private:
|
||||
random_device& operator=(random_device&) = delete;
|
||||
|
||||
pseudo_engine gen;
|
||||
std::uniform_int_distribution<result_type> dis {};
|
||||
|
||||
static bool hasIntelCpu();
|
||||
static bool _hasRdrand();
|
||||
static bool _hasRdseed();
|
||||
|
||||
struct CPUIDinfo {
|
||||
unsigned int EAX;
|
||||
unsigned int EBX;
|
||||
unsigned int ECX;
|
||||
unsigned int EDX;
|
||||
CPUIDinfo(const unsigned int func, const unsigned int subfunc);
|
||||
};
|
||||
bool rdrandStep(result_type* r);
|
||||
bool rdrand(result_type* r);
|
||||
bool rdseedStep(result_type* r);
|
||||
bool rdseed(result_type* r);
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
using random_device = std::random_device;
|
||||
|
||||
#endif
|
||||
#else
|
||||
using random_device = std::random_device;
|
||||
#endif
|
||||
|
||||
template<class T = std::mt19937, std::size_t N = T::state_size>
|
||||
auto getSeededRandomEngine () -> typename std::enable_if<!!N, T>::type {
|
||||
typename T::result_type random_data[N];
|
||||
for (unsigned i=0; i<256; i++) {
|
||||
try {
|
||||
random_device source;
|
||||
std::generate(std::begin(random_data), std::end(random_data), std::ref(source));
|
||||
std::seed_seq seeds(std::begin(random_data), std::end(random_data));
|
||||
T seededEngine (seeds);
|
||||
return seededEngine;
|
||||
} catch (...) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("Can't seed random engine");
|
||||
}
|
||||
|
||||
}} // dht::crypto
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
@@ -149,9 +149,6 @@ public:
|
||||
void setLocalCertificateStore(CertificateStoreQuery&& query_method) {
|
||||
localQueryMethod_ = std::move(query_method);
|
||||
}
|
||||
void setOnPublicAddressChanged(PublicAddressChangedCb cb) override {
|
||||
dht_->setOnPublicAddressChanged(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* SecureDht to Dht proxy
|
||||
@@ -207,12 +204,6 @@ 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);
|
||||
}
|
||||
@@ -222,7 +213,13 @@ public:
|
||||
NodeStatus getStatus() const override {
|
||||
return dht_->getStatus();
|
||||
}
|
||||
net::DatagramSocket* getSocket() const override {
|
||||
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 {
|
||||
return dht_->getSocket();
|
||||
};
|
||||
bool isRunning(sa_family_t af = 0) const override {
|
||||
|
||||
@@ -1,325 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 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"
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/ostream.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 void print_addr(std::ostream& os, const sockaddr* sa, socklen_t slen);
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the ownership of the managed object, if any.
|
||||
* The caller is responsible for deleting the object with free().
|
||||
*/
|
||||
inline sockaddr* release() {
|
||||
len = 0;
|
||||
return addr.release();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
};
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const SockAddr& h) {
|
||||
print_addr(s, h.get(), h.getLength());
|
||||
return s;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
#if FMT_VERSION >= 90000
|
||||
template <> struct fmt::formatter<dht::SockAddr> : ostream_formatter {};
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
static ThreadPool& io();
|
||||
|
||||
ThreadPool();
|
||||
ThreadPool(unsigned minThreads, unsigned maxThreads = 0);
|
||||
ThreadPool(size_t maxThreads);
|
||||
~ThreadPool();
|
||||
|
||||
void run(std::function<void()>&& cb);
|
||||
@@ -61,9 +61,8 @@ public:
|
||||
return get(std::move(cb));
|
||||
}
|
||||
|
||||
void stop(bool wait = true);
|
||||
void stop();
|
||||
void join();
|
||||
void detach();
|
||||
|
||||
private:
|
||||
std::mutex lock_ {};
|
||||
@@ -73,12 +72,7 @@ private:
|
||||
unsigned readyThreads_ {0};
|
||||
bool running_ {true};
|
||||
|
||||
unsigned minThreads_;
|
||||
const unsigned maxThreads_;
|
||||
std::chrono::steady_clock::duration threadExpirationDelay {std::chrono::minutes(5)};
|
||||
double threadDelayRatio_ {2};
|
||||
|
||||
void threadEnded(std::thread&);
|
||||
};
|
||||
|
||||
class OPENDHT_PUBLIC Executor : public std::enable_shared_from_this<Executor> {
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
#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
|
||||
+40
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* 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
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "def.h"
|
||||
|
||||
#include <msgpack.hpp>
|
||||
#include <asio/ip/address.hpp>
|
||||
#include <asio/ip/udp.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <random>
|
||||
@@ -62,6 +64,11 @@ 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 = "") :
|
||||
@@ -182,4 +189,36 @@ 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
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "infohash.h"
|
||||
#include "crypto.h"
|
||||
#include "utils.h"
|
||||
#include "sockaddr.h"
|
||||
|
||||
#include <msgpack.hpp>
|
||||
|
||||
|
||||
+17
-170
@@ -1,5 +1,5 @@
|
||||
project('opendht', 'c', 'cpp', 'cython',
|
||||
version: '3.2.0',
|
||||
project('opendht', 'c', 'cpp',
|
||||
version: '2.5.5',
|
||||
default_options: [
|
||||
'cpp_std=c++17',
|
||||
'warning_level=3'
|
||||
@@ -11,18 +11,17 @@ 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'))
|
||||
fmt = dependency('fmt')
|
||||
llhttp = dependency('llhttp', 'libllhttp', required: get_option('proxy_client'))
|
||||
io_uring = dependency('liburing', required: false)
|
||||
|
||||
deps = [fmt, gnutls, nettle, msgpack, argon2, openssl, jsoncpp, llhttp, io_uring]
|
||||
conf_data = configuration_data()
|
||||
|
||||
add_project_arguments('-DMSGPACK_NO_BOOST', '-DASIO_STANDALONE', language : 'cpp')
|
||||
if io_uring.found()
|
||||
add_project_arguments('-DASIO_HAS_IO_URING', '-DASIO_DISABLE_EPOLL', language : 'cpp')
|
||||
conf_data.set('iouring_lib', ', liburing')
|
||||
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')
|
||||
@@ -42,28 +41,17 @@ opendht_src = [
|
||||
'src/dhtrunner.cpp',
|
||||
'src/log.cpp',
|
||||
'src/op_cache.cpp',
|
||||
'src/network_utils.cpp',
|
||||
'src/thread_pool.cpp',
|
||||
'src/network_utils.cpp'
|
||||
]
|
||||
|
||||
if get_option('indexation').enabled()
|
||||
opendht_src += 'src/indexation/pht.cpp'
|
||||
add_project_arguments('-DOPENDHT_INDEXATION', language : 'cpp')
|
||||
endif
|
||||
if jsoncpp.found()
|
||||
opendht_src += ['src/base64.cpp']
|
||||
add_project_arguments('-DOPENDHT_JSONCPP', language : 'cpp')
|
||||
conf_data.set('jsoncpp_lib', ', jsoncpp')
|
||||
endif
|
||||
if llhttp.found()
|
||||
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
|
||||
conf_data.set('http_lib', '-lllhttp')
|
||||
endif
|
||||
if openssl.found()
|
||||
conf_data.set('openssl_lib', ', openssl')
|
||||
endif
|
||||
if get_option('proxy_client').enabled()
|
||||
opendht_src += ['src/dht_proxy_client.cpp']
|
||||
@@ -73,169 +61,28 @@ 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('push_notifications').enabled()
|
||||
add_project_arguments('-DOPENDHT_PUSH_NOTIFICATIONS', language : 'cpp')
|
||||
endif
|
||||
if get_option('peer_discovery').enabled()
|
||||
opendht_src += 'src/peer_discovery.cpp'
|
||||
add_project_arguments('-DOPENDHT_PEER_DISCOVERY', language : 'cpp')
|
||||
endif
|
||||
opendht = library('opendht',
|
||||
opendht = shared_library('opendht',
|
||||
opendht_src,
|
||||
include_directories : opendht_inc,
|
||||
dependencies : deps,
|
||||
cpp_args : ['-DOPENHT_BUILD', '-Dopendht_EXPORTS'],
|
||||
install : true)
|
||||
|
||||
conf_data.set('VERSION', meson.project_version())
|
||||
conf_data.set('prefix', get_option('prefix'))
|
||||
conf_data.set('exec_prefix', get_option('prefix'))
|
||||
conf_data.set('libdir', join_paths(get_option('prefix'), get_option('libdir')))
|
||||
conf_data.set('includedir', join_paths(get_option('prefix'), get_option('includedir')))
|
||||
conf_data.set('argon2_lib', ', libargon2')
|
||||
|
||||
configure_file(input : 'opendht.pc.in',
|
||||
output : 'opendht.pc',
|
||||
configuration : conf_data,
|
||||
install: true,
|
||||
install_dir: join_paths(get_option('prefix'), get_option('libdir'), 'pkgconfig'))
|
||||
|
||||
readline = meson.get_compiler('c').find_library('readline', required: get_option('tools'))
|
||||
|
||||
if get_option('c').enabled()
|
||||
opendht_c = library('opendht-c',
|
||||
['c/opendht.cpp', 'c/opendht_c.h'],
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [msgpack, jsoncpp, fmt],
|
||||
cpp_args : ['-DOPENDHT_C_BUILD', '-Dopendht_c_EXPORTS'],
|
||||
install : true)
|
||||
|
||||
configure_file(input : 'opendht-c.pc.in',
|
||||
output : 'opendht-c.pc',
|
||||
configuration : conf_data,
|
||||
install: true,
|
||||
install_dir: join_paths(get_option('prefix'), get_option('libdir'), 'pkgconfig'))
|
||||
|
||||
if get_option('tools').enabled()
|
||||
dhtcnode = executable('dhtcnode', 'tools/dhtcnode.c',
|
||||
include_directories : ['c', opendht_interface_inc],
|
||||
link_with : opendht_c,
|
||||
dependencies : [readline],
|
||||
install : true)
|
||||
endif
|
||||
endif
|
||||
|
||||
if get_option('python').require(get_option('indexation').enabled(), error_message: 'python requires indexation').allowed()
|
||||
py = import('python').find_installation()
|
||||
dep_py = py.dependency()
|
||||
py.extension_module(
|
||||
'opendht',
|
||||
'python/opendht.pyx',
|
||||
include_directories : opendht_interface_inc,
|
||||
override_options : ['cython_language=cpp'],
|
||||
link_with : [opendht],
|
||||
dependencies : [msgpack, jsoncpp, fmt, dep_py],
|
||||
install : true
|
||||
)
|
||||
endif
|
||||
|
||||
if get_option('tools').enabled()
|
||||
dhtnode = executable('dhtnode', 'tools/dhtnode.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [readline, jsoncpp, msgpack, fmt, openssl],
|
||||
dependencies : [readline, msgpack],
|
||||
install : true)
|
||||
dhtchat = executable('dhtchat', 'tools/dhtchat.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [readline, jsoncpp, msgpack, fmt, openssl],
|
||||
install : true)
|
||||
dhtscanner = executable('dhtscanner', 'tools/dhtscanner.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [readline, jsoncpp, msgpack, fmt, openssl],
|
||||
install : true)
|
||||
if llhttp.found()
|
||||
if http_parser.found()
|
||||
durl = executable('durl', 'tools/durl.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [msgpack, openssl])
|
||||
endif
|
||||
endif
|
||||
|
||||
# Unit tests
|
||||
if get_option('tests').enabled()
|
||||
cppunit = dependency('cppunit')
|
||||
|
||||
test_infohash = executable('test_infohash',
|
||||
'tests/infohashtester.cpp', 'tests/tests_runner.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [cppunit, jsoncpp, fmt, openssl, msgpack])
|
||||
test('Infohash', test_infohash)
|
||||
|
||||
test_value = executable('test_value',
|
||||
'tests/valuetester.cpp', 'tests/tests_runner.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [cppunit, jsoncpp, fmt, openssl, msgpack])
|
||||
test('Value', test_value)
|
||||
|
||||
test_crypto = executable('test_crypto',
|
||||
'tests/cryptotester.cpp', 'tests/tests_runner.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [cppunit, jsoncpp, fmt, openssl, msgpack])
|
||||
test('Crypto', test_crypto)
|
||||
|
||||
test_dhtrunner = executable('test_dhtrunner',
|
||||
'tests/dhtrunnertester.cpp', 'tests/tests_runner.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [cppunit, jsoncpp, fmt, openssl, msgpack])
|
||||
test('DhtRunner', test_dhtrunner)
|
||||
|
||||
test_threadpool = executable('test_threadpool',
|
||||
'tests/threadpooltester.cpp', 'tests/tests_runner.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [cppunit, jsoncpp, fmt, openssl, msgpack])
|
||||
test('ThreadPool', test_threadpool)
|
||||
|
||||
if get_option('proxy_client').enabled() or get_option('proxy_server').enabled()
|
||||
test_http = executable('test_http',
|
||||
'tests/httptester.cpp', 'tests/tests_runner.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [cppunit, jsoncpp, fmt, openssl, msgpack])
|
||||
test('Http', test_http)
|
||||
endif
|
||||
|
||||
if get_option('peer_discovery').enabled()
|
||||
test_peerdiscovery = executable('test_peerdiscovery',
|
||||
'tests/peerdiscoverytester.cpp', 'tests/tests_runner.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [cppunit, jsoncpp, fmt, openssl, msgpack])
|
||||
test('PeerDiscovery', test_peerdiscovery)
|
||||
endif
|
||||
|
||||
if get_option('proxy_server').enabled()
|
||||
test_proxy = executable('test_dhtproxy',
|
||||
'tests/dhtproxytester.cpp', 'tests/tests_runner.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [cppunit, jsoncpp, fmt, openssl, msgpack])
|
||||
test('DhtProxy', test_proxy)
|
||||
|
||||
if get_option('long_tests').enabled()
|
||||
test_proxystress = executable('test_dhtproxystress',
|
||||
'tests/dhtproxy_stress_tester.cpp', 'tests/tests_runner.cpp',
|
||||
include_directories : opendht_interface_inc,
|
||||
link_with : opendht,
|
||||
dependencies : [cppunit, jsoncpp, fmt, openssl, msgpack])
|
||||
test('DhtProxy Stress', test_proxystress)
|
||||
endif
|
||||
dependencies : [msgpack, openssl])
|
||||
endif
|
||||
endif
|
||||
@@ -1,10 +0,0 @@
|
||||
option('proxy_client', type : 'feature', value : 'disabled')
|
||||
option('proxy_server', type : 'feature', value : 'disabled')
|
||||
option('push_notifications', type : 'feature', value : 'disabled')
|
||||
option('peer_discovery', type : 'feature', value : 'enabled')
|
||||
option('tools', type : 'feature', value : 'enabled')
|
||||
option('c', type : 'feature', value : 'enabled')
|
||||
option('indexation', type : 'feature', value : 'enabled')
|
||||
option('python', type : 'feature', value : 'enabled')
|
||||
option('tests', type : 'feature', value : 'enabled')
|
||||
option('long_tests', type : 'feature', value : 'disabled')
|
||||
@@ -0,0 +1,4 @@
|
||||
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
-2
@@ -6,7 +6,7 @@ Name: OpenDHT
|
||||
Description: C++17 Distributed Hash Table library
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lopendht
|
||||
Libs.private: @http_lib@ -pthread
|
||||
Libs.private: @http_parser_lib@ -pthread
|
||||
Requires: gnutls >= 3.3@jsoncpp_lib@@openssl_lib@
|
||||
Requires.private: nettle >= 2.4@argon2_lib@@iouring_lib@
|
||||
Requires.private: nettle >= 2.4@argon2_lib@
|
||||
Cflags: -I${includedir}
|
||||
+10
-124
@@ -5,7 +5,7 @@
|
||||
# distutils: libraries = opendht gnutls
|
||||
# cython: language_level=3
|
||||
#
|
||||
# Copyright (c) 2015-2023 Savoir-faire Linux Inc.
|
||||
# Copyright (c) 2015-2022 Savoir-faire Linux Inc.
|
||||
# Author(s): Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
|
||||
# Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
# Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
@@ -42,7 +42,7 @@ cimport opendht_cpp as cpp
|
||||
|
||||
import threading
|
||||
|
||||
cdef inline void lookup_callback(cpp.vector[cpp.shared_ptr[cpp.IndexValue]]* values, cpp.Prefix* p, void *user_data) noexcept with gil:
|
||||
cdef inline void lookup_callback(cpp.vector[cpp.shared_ptr[cpp.IndexValue]]* values, cpp.Prefix* p, void *user_data) with gil:
|
||||
cbs = <object>user_data
|
||||
if 'lookup' in cbs and cbs['lookup']:
|
||||
vals = []
|
||||
@@ -52,13 +52,13 @@ cdef inline void lookup_callback(cpp.vector[cpp.shared_ptr[cpp.IndexValue]]* val
|
||||
vals.append(v)
|
||||
cbs['lookup'](vals, p.toString())
|
||||
|
||||
cdef inline void shutdown_callback(void* user_data) noexcept with gil:
|
||||
cdef inline void shutdown_callback(void* user_data) with gil:
|
||||
cbs = <object>user_data
|
||||
if 'shutdown' in cbs and cbs['shutdown']:
|
||||
cbs['shutdown']()
|
||||
ref.Py_DECREF(cbs)
|
||||
|
||||
cdef inline bool get_callback(shared_ptr[cpp.Value] value, void *user_data) noexcept with gil:
|
||||
cdef inline bool get_callback(shared_ptr[cpp.Value] value, void *user_data) with gil:
|
||||
cbs = <object>user_data
|
||||
cb = cbs['get']
|
||||
f = cbs['filter'] if 'filter' in cbs else None
|
||||
@@ -66,7 +66,7 @@ cdef inline bool get_callback(shared_ptr[cpp.Value] value, void *user_data) noex
|
||||
pv._value = value
|
||||
return cb(pv) if not f or f(pv) else True
|
||||
|
||||
cdef inline bool value_callback(shared_ptr[cpp.Value] value, bool expired, void *user_data) noexcept with gil:
|
||||
cdef inline bool value_callback(shared_ptr[cpp.Value] value, bool expired, void *user_data) with gil:
|
||||
cbs = <object>user_data
|
||||
cb = cbs['valcb']
|
||||
f = cbs['filter'] if 'filter' in cbs else None
|
||||
@@ -74,7 +74,7 @@ cdef inline bool value_callback(shared_ptr[cpp.Value] value, bool expired, void
|
||||
pv._value = value
|
||||
return cb(pv, expired) if not f or f(pv) else True
|
||||
|
||||
cdef inline void done_callback(bool done, cpp.vector[shared_ptr[cpp.Node]]* nodes, void *user_data) noexcept with gil:
|
||||
cdef inline void done_callback(bool done, cpp.vector[shared_ptr[cpp.Node]]* nodes, void *user_data) with gil:
|
||||
node_ids = []
|
||||
for n in deref(nodes):
|
||||
h = NodeEntry()
|
||||
@@ -86,7 +86,7 @@ cdef inline void done_callback(bool done, cpp.vector[shared_ptr[cpp.Node]]* node
|
||||
cbs['done'](done, node_ids)
|
||||
ref.Py_DECREF(cbs)
|
||||
|
||||
cdef inline void done_callback_simple(bool done, void *user_data) noexcept with gil:
|
||||
cdef inline void done_callback_simple(bool done, void *user_data) with gil:
|
||||
cbs = <object>user_data
|
||||
if 'done' in cbs and cbs['done']:
|
||||
cbs['done'](done)
|
||||
@@ -158,15 +158,6 @@ cdef class SockAddr(object):
|
||||
return self.toString().decode()
|
||||
def __repr__(self):
|
||||
return "<%s '%s'>" % (self.__class__.__name__, str(self))
|
||||
@staticmethod
|
||||
def resolve(str host, str service=None):
|
||||
vals = []
|
||||
result = cpp.SockAddr.resolve(host.encode(), service.encode() if service else b'')
|
||||
for val in result:
|
||||
a = SockAddr()
|
||||
a._addr = val
|
||||
vals.append(a)
|
||||
return vals
|
||||
|
||||
cdef class Node(_WithID):
|
||||
cdef shared_ptr[cpp.Node] _node
|
||||
@@ -462,31 +453,11 @@ cdef class Identity(object):
|
||||
k._key = self._id.first
|
||||
return k
|
||||
|
||||
def aesEncrypt(bytes data, str password) -> bytes :
|
||||
cdef size_t d_len = len(data)
|
||||
cdef cpp.uint8_t* d_ptr = <cpp.uint8_t*>data
|
||||
cdef cpp.Blob indat
|
||||
indat.assign(d_ptr, <cpp.uint8_t*>(d_ptr + d_len))
|
||||
cdef cpp.Blob encrypted = cpp.aesEncrypt(indat, password.encode())
|
||||
cdef char* encrypted_c_str = <char *>encrypted.data()
|
||||
cdef Py_ssize_t length = encrypted.size()
|
||||
return encrypted_c_str[:length]
|
||||
|
||||
def aesDecrypt(bytes data, str password) -> bytes :
|
||||
cdef size_t d_len = len(data)
|
||||
cdef cpp.uint8_t* d_ptr = <cpp.uint8_t*>data
|
||||
cdef cpp.Blob indat
|
||||
indat.assign(d_ptr, <cpp.uint8_t*>(d_ptr + d_len))
|
||||
cdef cpp.Blob decrypted = cpp.aesDecrypt(indat, password.encode())
|
||||
cdef char* decrypted_c_str = <char *>decrypted.data()
|
||||
cdef Py_ssize_t length = decrypted.size()
|
||||
return decrypted_c_str[:length]
|
||||
|
||||
cdef class DhtConfig(object):
|
||||
cdef cpp.DhtRunnerConfig _config
|
||||
def __init__(self):
|
||||
self._config = cpp.DhtRunnerConfig()
|
||||
self._config.threaded = True
|
||||
self._config.threaded = True;
|
||||
def setIdentity(self, Identity id):
|
||||
self._config.dht_config.id = id._id
|
||||
def setBootstrapMode(self, bool bootstrap):
|
||||
@@ -500,20 +471,6 @@ cdef class DhtConfig(object):
|
||||
def setRateLimit(self, ssize_t max_req_per_sec, ssize_t max_peer_req_per_sec):
|
||||
self._config.dht_config.node_config.max_req_per_sec = max_req_per_sec
|
||||
self._config.dht_config.node_config.max_peer_req_per_sec = max_peer_req_per_sec
|
||||
def setProxyInfo(self, str proxy_server, str push_node_id="", str push_token="", str push_topic="", str push_platform="", str user_agent="", Certificate server_ca = Certificate()):
|
||||
self._config.proxy_server = proxy_server.encode()
|
||||
self._config.push_node_id = push_node_id.encode()
|
||||
self._config.push_token = push_token.encode()
|
||||
self._config.push_topic = push_topic.encode()
|
||||
self._config.push_platform = push_platform.encode()
|
||||
self._config.proxy_user_agent = user_agent.encode()
|
||||
self._config.server_ca = server_ca._cert
|
||||
def setPeerDiscovery(self, bool peer_discovery, bool peer_publish):
|
||||
self._config.peer_discovery = peer_discovery
|
||||
self._config.peer_publish = peer_publish
|
||||
def setBound(self, SockAddr bind4, SockAddr bind6):
|
||||
self._config.bind4 = bind4._addr
|
||||
self._config.bind6 = bind6._addr
|
||||
|
||||
cdef class DhtRunner(_WithID):
|
||||
cdef cpp.shared_ptr[cpp.DhtRunner] thisptr
|
||||
@@ -630,8 +587,7 @@ cdef class DhtRunner(_WithID):
|
||||
while pending > 0:
|
||||
lock.wait()
|
||||
return res
|
||||
|
||||
def put(self, InfoHash key, Value val, done_cb=None, permanent=False):
|
||||
def put(self, InfoHash key, Value val, done_cb=None):
|
||||
"""Publish a new value on the DHT at key.
|
||||
|
||||
key -- the DHT key where to put the value
|
||||
@@ -641,7 +597,7 @@ cdef class DhtRunner(_WithID):
|
||||
if done_cb:
|
||||
cb_obj = {'done':done_cb}
|
||||
ref.Py_INCREF(cb_obj)
|
||||
self.thisptr.get().put(key._infohash, val._value, cpp.bindDoneCb(done_callback, <void*>cb_obj), cpp.time_point.max(), permanent)
|
||||
self.thisptr.get().put(key._infohash, val._value, cpp.bindDoneCb(done_callback, <void*>cb_obj))
|
||||
else:
|
||||
lock = threading.Condition()
|
||||
pending = 0
|
||||
@@ -658,76 +614,6 @@ cdef class DhtRunner(_WithID):
|
||||
while pending > 0:
|
||||
lock.wait()
|
||||
return ok
|
||||
|
||||
def putSigned(self, InfoHash key, Value val, done_cb=None, permanent=False):
|
||||
if done_cb:
|
||||
cb_obj = {'done':done_cb}
|
||||
ref.Py_INCREF(cb_obj)
|
||||
self.thisptr.get().putSigned(key._infohash, val._value, cpp.bindDoneCb(done_callback, <void*>cb_obj), permanent)
|
||||
else:
|
||||
lock = threading.Condition()
|
||||
pending = 0
|
||||
ok = False
|
||||
def tmp_done(ok_ret, nodes):
|
||||
nonlocal pending, ok, lock
|
||||
with lock:
|
||||
ok = ok_ret
|
||||
pending -= 1
|
||||
lock.notify()
|
||||
with lock:
|
||||
pending += 1
|
||||
self.putSigned(key, val, done_cb=tmp_done)
|
||||
while pending > 0:
|
||||
lock.wait()
|
||||
return ok
|
||||
|
||||
def putEncrypted(self, InfoHash key, InfoHash to, Value val, done_cb=None, bool permanent=False):
|
||||
if done_cb:
|
||||
cb_obj = {'done':done_cb}
|
||||
ref.Py_INCREF(cb_obj)
|
||||
self.thisptr.get().putEncrypted(key._infohash, to._infohash, val._value, cpp.bindDoneCb(done_callback, <void*>cb_obj), permanent)
|
||||
else:
|
||||
lock = threading.Condition()
|
||||
pending = 0
|
||||
ok = False
|
||||
def tmp_done(ok_ret, nodes):
|
||||
nonlocal pending, ok, lock
|
||||
with lock:
|
||||
ok = ok_ret
|
||||
pending -= 1
|
||||
lock.notify()
|
||||
with lock:
|
||||
pending += 1
|
||||
self.putEncrypted(key, to, val, done_cb=tmp_done, permanent=permanent)
|
||||
while pending > 0:
|
||||
lock.wait()
|
||||
return ok
|
||||
|
||||
def putEncrypted(self, InfoHash key, PublicKey to, Value val, done_cb=None, bool permanent=False):
|
||||
if done_cb:
|
||||
cb_obj = {'done':done_cb}
|
||||
ref.Py_INCREF(cb_obj)
|
||||
self.thisptr.get().putEncrypted(key._infohash, to._key, val._value, cpp.bindDoneCb(done_callback, <void*>cb_obj), permanent)
|
||||
else:
|
||||
lock = threading.Condition()
|
||||
pending = 0
|
||||
ok = False
|
||||
def tmp_done(ok_ret, nodes):
|
||||
nonlocal pending, ok, lock
|
||||
with lock:
|
||||
ok = ok_ret
|
||||
pending -= 1
|
||||
lock.notify()
|
||||
with lock:
|
||||
pending += 1
|
||||
self.putEncrypted(key, to, val, done_cb=tmp_done, permanent=permanent)
|
||||
while pending > 0:
|
||||
lock.wait()
|
||||
return ok
|
||||
|
||||
def cancelPut(self, InfoHash key, Value val):
|
||||
self.thisptr.get().cancelPut(key._infohash, val._value)
|
||||
|
||||
def listen(self, InfoHash key, value_cb):
|
||||
t = ListenToken()
|
||||
t._h = key._infohash
|
||||
|
||||
+13
-32
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2015-2023 Savoir-faire Linux Inc.
|
||||
# Copyright (c) 2015-2022 Savoir-faire Linux Inc.
|
||||
# Author(s): Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
# Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
#
|
||||
@@ -21,13 +21,12 @@ from libcpp.string cimport string
|
||||
from libcpp.vector cimport vector
|
||||
from libcpp.utility cimport pair
|
||||
from libcpp.map cimport map
|
||||
from libcpp.memory cimport shared_ptr, make_shared
|
||||
from libc.string cimport const_char, const_uchar
|
||||
|
||||
ctypedef uint16_t in_port_t
|
||||
ctypedef unsigned short int sa_family_t;
|
||||
|
||||
cdef extern from "<chrono>" namespace "std::chrono" nogil:
|
||||
cdef extern from "<iostream>" namespace "std::chrono" nogil:
|
||||
cdef cppclass duration[ulong]:
|
||||
duration() except +
|
||||
|
||||
@@ -35,6 +34,16 @@ cdef extern from "<chrono>" namespace "std::chrono" nogil:
|
||||
duration seconds(uint64_t) except +
|
||||
duration seconds()
|
||||
|
||||
cdef extern from "<memory>" namespace "std" nogil:
|
||||
cdef cppclass shared_ptr[T]:
|
||||
shared_ptr() except +
|
||||
shared_ptr(T*) except +
|
||||
T* get()
|
||||
T operator*()
|
||||
bool operator bool() const
|
||||
void reset(T*)
|
||||
shared_ptr[T] make_shared[T](...) except +
|
||||
|
||||
cdef extern from "<functional>" namespace "std" nogil:
|
||||
cdef cppclass hash[T]:
|
||||
hash() except +
|
||||
@@ -50,13 +59,6 @@ cdef extern from "<future>" namespace "std" nogil:
|
||||
bool valid() const
|
||||
shared_future[T] share()
|
||||
|
||||
cdef extern from "opendht/utils.h" namespace "dht":
|
||||
cdef cppclass time_point:
|
||||
@staticmethod
|
||||
time_point min() const
|
||||
@staticmethod
|
||||
time_point max() const
|
||||
|
||||
cdef extern from "opendht/infohash.h" namespace "dht":
|
||||
cdef cppclass InfoHash:
|
||||
InfoHash() except +
|
||||
@@ -86,16 +88,12 @@ cdef extern from "opendht/sockaddr.h" namespace "dht":
|
||||
bool isLoopback() const
|
||||
bool isPrivate() const
|
||||
bool isUnspecified() const
|
||||
@staticmethod
|
||||
vector[SockAddr] resolve(string host, string service) except +
|
||||
|
||||
ctypedef vector[uint8_t] Blob
|
||||
|
||||
cdef extern from "opendht/crypto.h" namespace "dht::crypto":
|
||||
ctypedef pair[shared_ptr[PrivateKey], shared_ptr[Certificate]] Identity
|
||||
cdef Identity generateIdentity(string name, Identity ca, unsigned bits)
|
||||
cdef Blob aesEncrypt(Blob data, string password) except +
|
||||
cdef Blob aesDecrypt(Blob encrypted, string password) except +
|
||||
|
||||
cdef cppclass PrivateKey:
|
||||
PrivateKey()
|
||||
@@ -241,19 +239,6 @@ cdef extern from "opendht/dhtrunner.h" namespace "dht":
|
||||
cppclass Config:
|
||||
SecureDhtConfig dht_config
|
||||
bool threaded
|
||||
string proxy_server
|
||||
string push_node_id
|
||||
string push_token
|
||||
string push_topic
|
||||
string push_platform
|
||||
string proxy_user_agent
|
||||
bool peer_discovery
|
||||
bool peer_publish
|
||||
shared_ptr[Certificate] server_ca
|
||||
Identity client_identity
|
||||
SockAddr bind4
|
||||
SockAddr bind6
|
||||
|
||||
InfoHash getId() const
|
||||
InfoHash getNodeId() const
|
||||
void bootstrap(const_char*, const_char*)
|
||||
@@ -269,11 +254,7 @@ cdef extern from "opendht/dhtrunner.h" namespace "dht":
|
||||
string getRoutingTablesLog(sa_family_t af) const
|
||||
string getSearchesLog(sa_family_t af) const
|
||||
void get(InfoHash key, GetCallback get_cb, DoneCallback done_cb, nullptr_t f, Where w)
|
||||
void put(InfoHash key, shared_ptr[Value] val, DoneCallback done_cb, time_point created, bool permanent)
|
||||
void putSigned(InfoHash key, shared_ptr[Value] val, DoneCallback done_cb, bool permanent)
|
||||
void putEncrypted(InfoHash key, InfoHash to, shared_ptr[Value] val, DoneCallback done_cb, bool permanent)
|
||||
void putEncrypted(InfoHash key, shared_ptr[PublicKey] to, shared_ptr[Value] val, DoneCallback done_cb, bool permanent)
|
||||
void cancelPut(InfoHash key, shared_ptr[Value] val)
|
||||
void put(InfoHash key, shared_ptr[Value] val, DoneCallback done_cb)
|
||||
ListenToken listen(InfoHash key, ValueCallback get_cb)
|
||||
void cancelListen(InfoHash key, SharedListenToken token)
|
||||
vector[unsigned] getNodeMessageStats(bool i)
|
||||
|
||||
+6
-3
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
# Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
# Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
|
||||
# Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
#
|
||||
@@ -32,14 +32,17 @@ setup(name="opendht",
|
||||
author="Adrien Béraud, Guillaume Roguez, Simon Désaulniers",
|
||||
license="GPLv3",
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Development Status :: 3 - Alpha',
|
||||
'Intended Audience :: Developers',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||
'Topic :: System :: Distributed Computing',
|
||||
'Topic :: System :: Networking'
|
||||
'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Programming Language :: Python :: 3.2',
|
||||
'Programming Language :: Python :: 3.3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
],
|
||||
cmdclass = { 'build_ext' : build_ext },
|
||||
ext_modules = cythonize(Extension(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
# Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
# Author(s): Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
# Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
#
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015-2023 Savoir-faire Linux Inc.
|
||||
# Copyright (c) 2015-2022 Savoir-faire Linux Inc.
|
||||
# Author(s): Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
# Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
#
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2015-2023 Savoir-Faire Linux Inc.
|
||||
# Copyright (C) 2015-2022 Savoir-Faire Linux Inc.
|
||||
# Author(s): Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
# Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015-2023 Savoir-faire Linux Inc.
|
||||
# Copyright (c) 2015-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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
# Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
# Author(s): Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016-2023 Savoir-faire Linux Inc.
|
||||
# Copyright (c) 2016-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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2023 Savoir-faire Linux Inc.
|
||||
# Copyright (c) 2015-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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2023 Savoir-faire Linux Inc.
|
||||
# Copyright (c) 2015-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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2023 Savoir-faire Linux Inc.
|
||||
# Copyright (c) 2015-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
|
||||
|
||||
+5
-5
@@ -2,10 +2,10 @@
|
||||
name = "opendht"
|
||||
version = "0.1.0"
|
||||
authors = ["Sébastien Blin <sebastien.blin@savoirfairelinux.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc="0.2"
|
||||
os_socketaddr="0.2.3"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
libc="0.2.0"
|
||||
os_socketaddr="0.2.3"
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
+9
-15
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -49,7 +49,6 @@ impl DhtRunnerConfig {
|
||||
push_token: ptr::null(),
|
||||
push_topic: ptr::null(),
|
||||
push_platform: ptr::null(),
|
||||
proxy_user_agent: ptr::null(),
|
||||
peer_discovery: false,
|
||||
peer_publish: false,
|
||||
server_ca: ptr::null_mut(),
|
||||
@@ -57,7 +56,6 @@ impl DhtRunnerConfig {
|
||||
privatekey: ptr::null_mut(),
|
||||
certificate: ptr::null_mut(),
|
||||
},
|
||||
log: true,
|
||||
});
|
||||
unsafe {
|
||||
dht_runner_config_default(&mut *config);
|
||||
@@ -66,27 +64,23 @@ impl DhtRunnerConfig {
|
||||
}
|
||||
|
||||
pub fn set_proxy_server(&mut self, proxy_server: &str) {
|
||||
self.proxy_server = CString::new(proxy_server).unwrap().into_raw();
|
||||
self.proxy_server = CString::new(proxy_server).unwrap().as_ptr();
|
||||
}
|
||||
|
||||
pub fn set_push_node_id(&mut self, push_node_id: &str) {
|
||||
self.push_node_id = CString::new(push_node_id).unwrap().into_raw();
|
||||
self.push_node_id = CString::new(push_node_id).unwrap().as_ptr();
|
||||
}
|
||||
|
||||
pub fn set_push_token(&mut self, push_token: &str) {
|
||||
self.push_token = CString::new(push_token).unwrap().into_raw();
|
||||
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().into_raw();
|
||||
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().into_raw();
|
||||
}
|
||||
|
||||
pub fn set_proxy_user_agent(&mut self, proxy_user_agent: &str) {
|
||||
self.proxy_user_agent = CString::new(proxy_user_agent).unwrap().into_raw();
|
||||
self.push_platform = CString::new(push_platform).unwrap().as_ptr();
|
||||
}
|
||||
|
||||
pub fn set_identity(&mut self, certificate: Box<DhtCertificate>, privatekey: Box<PrivateKey>) {
|
||||
@@ -99,7 +93,7 @@ impl DhtRunnerConfig {
|
||||
impl DhtNodeConfig
|
||||
{
|
||||
pub fn set_persist_path(&mut self, persist_path: &str) {
|
||||
self.persist_path = CString::new(persist_path).unwrap().into_raw();
|
||||
self.persist_path = CString::new(persist_path).unwrap().as_ptr();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,8 +197,8 @@ impl DhtRunner {
|
||||
pub fn bootstrap(&mut self, host: &str, service: u16) {
|
||||
unsafe {
|
||||
dht_runner_bootstrap(&mut *self,
|
||||
CString::new(host).unwrap().into_raw(),
|
||||
CString::new(service.to_string()).unwrap().into_raw())
|
||||
CString::new(host).unwrap().as_ptr(),
|
||||
CString::new(service.to_string()).unwrap().as_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -120,14 +120,13 @@ pub struct DhtRunnerConfig
|
||||
pub push_token: *const c_char,
|
||||
pub push_topic: *const c_char,
|
||||
pub push_platform: *const c_char,
|
||||
pub proxy_user_agent: *const c_char,
|
||||
pub peer_discovery: bool,
|
||||
pub peer_publish: bool,
|
||||
pub server_ca: *mut DhtCertificate,
|
||||
pub client_identity: DhtIdentity,
|
||||
pub log: bool,
|
||||
}
|
||||
|
||||
|
||||
#[link(name = "opendht-c")]
|
||||
extern {
|
||||
// dht::Value
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
+5
-2
@@ -29,6 +29,10 @@ libopendht_la_SOURCES = \
|
||||
network_utils.cpp \
|
||||
thread_pool.cpp
|
||||
|
||||
if WIN32
|
||||
libopendht_la_SOURCES += rng.cpp
|
||||
endif
|
||||
|
||||
nobase_include_HEADERS = \
|
||||
../include/opendht.h \
|
||||
../include/opendht/def.h \
|
||||
@@ -41,7 +45,6 @@ nobase_include_HEADERS = \
|
||||
../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 \
|
||||
@@ -50,7 +53,7 @@ nobase_include_HEADERS = \
|
||||
../include/opendht/dhtrunner.h \
|
||||
../include/opendht/default_types.h \
|
||||
../include/opendht/log.h \
|
||||
../include/opendht/logger.h \
|
||||
../include/opendht/log_enable.h \
|
||||
../include/opendht/network_utils.h \
|
||||
../include/opendht/rng.h \
|
||||
../include/opendht/thread_pool.h
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 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
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 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
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
//Code from getifaddrs.3 man
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <net/if.h>
|
||||
#include <asio.hpp>
|
||||
#include <stdexcept>
|
||||
namespace dht{
|
||||
namespace workaround{
|
||||
|
||||
asio::ip::address_v4
|
||||
get_interface(){
|
||||
struct ifaddrs *ifaddr;
|
||||
if(getifaddrs(&ifaddr) == -1){
|
||||
throw std::runtime_error("Can't getifaddrs");
|
||||
}
|
||||
for (struct ifaddrs *if1 = ifaddr; if1 != NULL; if1 = if1->ifa_next){
|
||||
if (if1->ifa_addr == NULL)
|
||||
continue;
|
||||
if (if1->ifa_addr->sa_family != AF_INET)
|
||||
continue;
|
||||
if((if1->ifa_flags & IFF_MULTICAST) == IFF_MULTICAST){
|
||||
auto sa_data = reinterpret_cast<uint8_t*>(if1->ifa_addr->sa_data);
|
||||
uint32_t value = sa_data[2] << 24
|
||||
| sa_data[3] << 16
|
||||
| sa_data[4] << 8
|
||||
| sa_data[5];
|
||||
freeifaddrs(ifaddr);
|
||||
return asio::ip::address_v4(value);
|
||||
}
|
||||
}
|
||||
freeifaddrs(ifaddr);
|
||||
throw std::runtime_error("Can't find a interface which supports multicast");
|
||||
}
|
||||
|
||||
} /* namespace workaround */
|
||||
} /* namespace dht */
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
|
||||
*
|
||||
|
||||
+93
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,7 +17,8 @@
|
||||
*/
|
||||
|
||||
#include "os_cert.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include "log_enable.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
@@ -31,6 +32,96 @@
|
||||
#include <Security/Security.h>
|
||||
#endif /*TARGET_OS_OSX*/
|
||||
|
||||
#if EMBEDDED_ASN1_TIME_PARSE
|
||||
#undef X509_NAME
|
||||
#undef OCSP_REQUEST
|
||||
#undef OCSP_RESPONSE
|
||||
#define GENTIME_LENGTH 15
|
||||
#define UTCTIME_LENGTH 13
|
||||
#define ATOI2(ar) ((ar) += 2, ((ar)[-2] - '0') * 10 + ((ar)[-1] - '0'))
|
||||
extern "C" {
|
||||
// Taken from libressl/src/crypto/asn1/a_time_tm.c
|
||||
int
|
||||
ASN1_time_parse(const char* bytes, size_t len, struct tm* tm, int mode)
|
||||
{
|
||||
size_t i;
|
||||
int type = 0;
|
||||
struct tm ltm;
|
||||
struct tm* lt;
|
||||
const char* p;
|
||||
|
||||
if (bytes == NULL)
|
||||
return (-1);
|
||||
|
||||
/* Constrain to valid lengths. */
|
||||
if (len != UTCTIME_LENGTH && len != GENTIME_LENGTH)
|
||||
return (-1);
|
||||
|
||||
lt = tm;
|
||||
if (lt == NULL) {
|
||||
memset(<m, 0, sizeof(ltm));
|
||||
lt = <m;
|
||||
}
|
||||
|
||||
/* Timezone is required and must be GMT (Zulu). */
|
||||
if (bytes[len - 1] != 'Z')
|
||||
return (-1);
|
||||
|
||||
/* Make sure everything else is digits. */
|
||||
for (i = 0; i < len - 1; i++) {
|
||||
if (isdigit((unsigned char) bytes[i]))
|
||||
continue;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate and convert the time
|
||||
*/
|
||||
p = bytes;
|
||||
switch (len) {
|
||||
case GENTIME_LENGTH:
|
||||
if (mode == V_ASN1_UTCTIME)
|
||||
return (-1);
|
||||
lt->tm_year = (ATOI2(p) * 100) - 1900; /* cc */
|
||||
type = V_ASN1_GENERALIZEDTIME;
|
||||
/* FALLTHROUGH */
|
||||
case UTCTIME_LENGTH:
|
||||
if (type == 0) {
|
||||
if (mode == V_ASN1_GENERALIZEDTIME)
|
||||
return (-1);
|
||||
type = V_ASN1_UTCTIME;
|
||||
}
|
||||
lt->tm_year += ATOI2(p); /* yy */
|
||||
if (type == V_ASN1_UTCTIME) {
|
||||
if (lt->tm_year < 50)
|
||||
lt->tm_year += 100;
|
||||
}
|
||||
lt->tm_mon = ATOI2(p) - 1; /* mm */
|
||||
if (lt->tm_mon < 0 || lt->tm_mon > 11)
|
||||
return (-1);
|
||||
lt->tm_mday = ATOI2(p); /* dd */
|
||||
if (lt->tm_mday < 1 || lt->tm_mday > 31)
|
||||
return (-1);
|
||||
lt->tm_hour = ATOI2(p); /* HH */
|
||||
if (lt->tm_hour < 0 || lt->tm_hour > 23)
|
||||
return (-1);
|
||||
lt->tm_min = ATOI2(p); /* MM */
|
||||
if (lt->tm_min < 0 || lt->tm_min > 59)
|
||||
return (-1);
|
||||
lt->tm_sec = ATOI2(p); /* SS */
|
||||
/* Leap second 60 is not accepted. Reconsider later? */
|
||||
if (lt->tm_sec < 0 || lt->tm_sec > 59)
|
||||
return (-1);
|
||||
break;
|
||||
default:
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
}
|
||||
#endif /* EMBEDDED_ASN1_TIME_PARSE */
|
||||
|
||||
namespace dht {
|
||||
namespace http {
|
||||
|
||||
|
||||
+32
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -18,12 +18,42 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "logger.h"
|
||||
#include "log_enable.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <openssl/x509.h>
|
||||
|
||||
#if (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER > 0x20501000L))
|
||||
#define EMBEDDED_ASN1_TIME_PARSE 0
|
||||
#else
|
||||
#define EMBEDDED_ASN1_TIME_PARSE 1
|
||||
#endif
|
||||
|
||||
#if EMBEDDED_ASN1_TIME_PARSE
|
||||
#define V_ASN1_UTCTIME 23
|
||||
#define V_ASN1_GENERALIZEDTIME 24
|
||||
|
||||
extern "C" {
|
||||
/*
|
||||
* Parse an RFC 5280 format ASN.1 time string.
|
||||
*
|
||||
* mode must be:
|
||||
* 0 if we expect to parse a time as specified in RFC 5280 for an X509 object.
|
||||
* V_ASN1_UTCTIME if we wish to parse an RFC5280 format UTC time.
|
||||
* V_ASN1_GENERALIZEDTIME if we wish to parse an RFC5280 format Generalized time.
|
||||
*
|
||||
* Returns:
|
||||
* -1 if the string was invalid.
|
||||
* V_ASN1_UTCTIME if the string validated as a UTC time string.
|
||||
* V_ASN1_GENERALIZEDTIME if the string validated as a Generalized time string.
|
||||
*
|
||||
* Fills in *tm with the corresponding time if tm is non NULL.
|
||||
*/
|
||||
int ASN1_time_parse(const char* bytes, size_t len, struct tm* tm, int mode);
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace dht {
|
||||
namespace http {
|
||||
|
||||
|
||||
+65
-156
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Vsevolod Ivanov <vsevolod.ivanov@savoirfairelinux.com>
|
||||
*
|
||||
@@ -43,6 +43,8 @@ static std::uniform_int_distribution<int> rand_byte{ 0, std::numeric_limits<uint
|
||||
static std::uniform_int_distribution<uint8_t> rand_byte;
|
||||
#endif
|
||||
|
||||
#define DHT_AES_LEGACY_DECRYPT 1
|
||||
|
||||
namespace dht {
|
||||
namespace crypto {
|
||||
|
||||
@@ -90,38 +92,24 @@ Blob aesEncrypt(const uint8_t* data, size_t data_length, const Blob& key)
|
||||
|
||||
Blob ret(data_length + GCM_IV_SIZE + GCM_DIGEST_SIZE);
|
||||
{
|
||||
std::random_device rdev;
|
||||
crypto::random_device rdev;
|
||||
std::generate_n(ret.begin(), GCM_IV_SIZE, std::bind(rand_byte, std::ref(rdev)));
|
||||
}
|
||||
|
||||
if (key.size() == AES_LENGTHS[0]) {
|
||||
struct gcm_aes128_ctx aes;
|
||||
gcm_aes128_set_key(&aes, key.data());
|
||||
gcm_aes128_set_iv(&aes, GCM_IV_SIZE, ret.data());
|
||||
gcm_aes128_encrypt(&aes, data_length, ret.data() + GCM_IV_SIZE, data);
|
||||
gcm_aes128_digest(&aes, GCM_DIGEST_SIZE, ret.data() + GCM_IV_SIZE + data_length);
|
||||
} else if (key.size() == AES_LENGTHS[1]) {
|
||||
struct gcm_aes192_ctx aes;
|
||||
gcm_aes192_set_key(&aes, key.data());
|
||||
gcm_aes192_set_iv(&aes, GCM_IV_SIZE, ret.data());
|
||||
gcm_aes192_encrypt(&aes, data_length, ret.data() + GCM_IV_SIZE, data);
|
||||
gcm_aes192_digest(&aes, GCM_DIGEST_SIZE, ret.data() + GCM_IV_SIZE + data_length);
|
||||
} else if (key.size() == AES_LENGTHS[2]) {
|
||||
struct gcm_aes256_ctx aes;
|
||||
gcm_aes256_set_key(&aes, key.data());
|
||||
gcm_aes256_set_iv(&aes, GCM_IV_SIZE, ret.data());
|
||||
gcm_aes256_encrypt(&aes, data_length, ret.data() + GCM_IV_SIZE, data);
|
||||
gcm_aes256_digest(&aes, GCM_DIGEST_SIZE, ret.data() + GCM_IV_SIZE + data_length);
|
||||
}
|
||||
|
||||
struct gcm_aes_ctx aes;
|
||||
gcm_aes_set_key(&aes, key.size(), key.data());
|
||||
gcm_aes_set_iv(&aes, GCM_IV_SIZE, ret.data());
|
||||
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;
|
||||
}
|
||||
|
||||
Blob aesEncrypt(const Blob& data, std::string_view password, const Blob& salt)
|
||||
Blob aesEncrypt(const Blob& data, const std::string& password)
|
||||
{
|
||||
Blob salt_actual = salt;
|
||||
Blob key = stretchKey(password, salt_actual, 256 / 8);
|
||||
return aesBuildEncrypted(aesEncrypt(data, key), salt_actual);
|
||||
Blob salt;
|
||||
Blob key = stretchKey(password, salt, 256 / 8);
|
||||
Blob encrypted = aesEncrypt(data, key);
|
||||
encrypted.insert(encrypted.begin(), salt.begin(), salt.end());
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
Blob aesDecrypt(const uint8_t* data, size_t data_length, const Blob& key)
|
||||
@@ -134,78 +122,50 @@ Blob aesDecrypt(const uint8_t* data, size_t data_length, const Blob& key)
|
||||
|
||||
std::array<uint8_t, GCM_DIGEST_SIZE> digest;
|
||||
|
||||
struct gcm_aes_ctx aes;
|
||||
gcm_aes_set_key(&aes, key.size(), key.data());
|
||||
gcm_aes_set_iv(&aes, GCM_IV_SIZE, data);
|
||||
|
||||
size_t data_sz = data_length - GCM_IV_SIZE - GCM_DIGEST_SIZE;
|
||||
Blob ret(data_sz);
|
||||
|
||||
if (key.size() == AES_LENGTHS[0]) {
|
||||
struct gcm_aes128_ctx aes;
|
||||
gcm_aes128_set_key(&aes, key.data());
|
||||
gcm_aes128_set_iv(&aes, GCM_IV_SIZE, data);
|
||||
gcm_aes128_decrypt(&aes, data_sz, ret.data(), data + GCM_IV_SIZE);
|
||||
gcm_aes128_digest(&aes, GCM_DIGEST_SIZE, digest.data());
|
||||
} else if (key.size() == AES_LENGTHS[1]) {
|
||||
struct gcm_aes192_ctx aes;
|
||||
gcm_aes192_set_key(&aes, key.data());
|
||||
gcm_aes192_set_iv(&aes, GCM_IV_SIZE, data);
|
||||
gcm_aes192_decrypt(&aes, data_sz, ret.data(), data + GCM_IV_SIZE);
|
||||
gcm_aes192_digest(&aes, GCM_DIGEST_SIZE, digest.data());
|
||||
} else if (key.size() == AES_LENGTHS[2]) {
|
||||
struct gcm_aes256_ctx aes;
|
||||
gcm_aes256_set_key(&aes, key.data());
|
||||
gcm_aes256_set_iv(&aes, GCM_IV_SIZE, data);
|
||||
gcm_aes256_decrypt(&aes, data_sz, ret.data(), data + GCM_IV_SIZE);
|
||||
gcm_aes256_digest(&aes, GCM_DIGEST_SIZE, digest.data());
|
||||
}
|
||||
gcm_aes_decrypt(&aes, data_sz, ret.data(), data + GCM_IV_SIZE);
|
||||
gcm_aes_digest(&aes, GCM_DIGEST_SIZE, digest.data());
|
||||
|
||||
if (not std::equal(digest.begin(), digest.end(), data + data_length - GCM_DIGEST_SIZE)) {
|
||||
#if DHT_AES_LEGACY_DECRYPT
|
||||
//gcm_aes_decrypt(&aes, data_sz, ret.data(), data + GCM_IV_SIZE);
|
||||
Blob ret_tmp(data_sz);
|
||||
struct gcm_aes_ctx aes_d;
|
||||
gcm_aes_set_key(&aes_d, key.size(), key.data());
|
||||
gcm_aes_set_iv(&aes_d, GCM_IV_SIZE, data);
|
||||
gcm_aes_update(&aes_d, ret.size(), ret.data());
|
||||
gcm_aes_encrypt(&aes_d, ret.size(), ret_tmp.data(), ret.data());
|
||||
gcm_aes_digest(&aes_d, GCM_DIGEST_SIZE, digest.data());
|
||||
|
||||
if (not std::equal(digest.begin(), digest.end(), data + data_length - GCM_DIGEST_SIZE))
|
||||
throw DecryptError("Can't decrypt data");
|
||||
#else
|
||||
throw DecryptError("Can't decrypt data");
|
||||
#endif
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Blob aesDecrypt(const uint8_t* data, size_t data_length, std::string_view password)
|
||||
Blob aesDecrypt(const uint8_t* data, size_t data_len, const std::string& password)
|
||||
{
|
||||
return aesDecrypt(
|
||||
aesGetEncrypted(data, data_length),
|
||||
aesGetKey(data, data_length, password)
|
||||
);
|
||||
}
|
||||
|
||||
Blob aesGetSalt(const uint8_t* data, size_t data_length)
|
||||
{
|
||||
if (data_length <= PASSWORD_SALT_LENGTH)
|
||||
if (data_len <= PASSWORD_SALT_LENGTH)
|
||||
throw DecryptError("Wrong data size");
|
||||
return Blob {data, data+PASSWORD_SALT_LENGTH};
|
||||
Blob salt {data, data+PASSWORD_SALT_LENGTH};
|
||||
Blob key = stretchKey(password, salt, 256/8);
|
||||
return aesDecrypt(data+PASSWORD_SALT_LENGTH, data_len - PASSWORD_SALT_LENGTH, key);
|
||||
}
|
||||
|
||||
std::string_view aesGetEncrypted(const uint8_t* data, size_t data_length)
|
||||
{
|
||||
if (data_length <= PASSWORD_SALT_LENGTH)
|
||||
throw DecryptError("Wrong data size");
|
||||
return std::string_view((const char*)(data+PASSWORD_SALT_LENGTH), data_length - PASSWORD_SALT_LENGTH);
|
||||
}
|
||||
|
||||
Blob aesBuildEncrypted(const uint8_t* data, size_t data_length, const Blob& salt)
|
||||
{
|
||||
Blob ret;
|
||||
ret.reserve(data_length + salt.size());
|
||||
ret.insert(ret.end(), salt.begin(), salt.end());
|
||||
ret.insert(ret.end(), data, data + data_length);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Blob aesGetKey(const uint8_t* data, size_t data_length, std::string_view password)
|
||||
{
|
||||
Blob salt = aesGetSalt(data, data_length);
|
||||
return stretchKey(password, salt, 256/8);
|
||||
}
|
||||
|
||||
Blob stretchKey(std::string_view password, Blob& salt, size_t key_length)
|
||||
Blob stretchKey(const std::string& password, Blob& salt, size_t key_length)
|
||||
{
|
||||
if (salt.empty()) {
|
||||
salt.resize(PASSWORD_SALT_LENGTH);
|
||||
std::random_device rdev;
|
||||
crypto::random_device rdev;
|
||||
std::generate_n(salt.begin(), salt.size(), std::bind(rand_byte, std::ref(rdev)));
|
||||
}
|
||||
Blob res;
|
||||
@@ -257,7 +217,7 @@ PrivateKey::PrivateKey(const uint8_t* src, size_t src_size, const char* password
|
||||
throw CryptoException("Can't initialize private key !");
|
||||
|
||||
const gnutls_datum_t dt {(uint8_t*)src, static_cast<unsigned>(src_size)};
|
||||
int flags = (password_ptr == NULL || strlen(password_ptr)==0) ? GNUTLS_PKCS_PLAIN
|
||||
int flags = password_ptr ? GNUTLS_PKCS_PLAIN
|
||||
: ( GNUTLS_PKCS_PBES2_AES_128 | GNUTLS_PKCS_PBES2_AES_192 | GNUTLS_PKCS_PBES2_AES_256
|
||||
| GNUTLS_PKCS_PKCS12_3DES | GNUTLS_PKCS_PKCS12_ARCFOUR | GNUTLS_PKCS_PKCS12_RC2_40);
|
||||
|
||||
@@ -321,7 +281,7 @@ PrivateKey::sign(const uint8_t* data, size_t data_length) const
|
||||
throw CryptoException("Can't sign data: no private key set !");
|
||||
if (std::numeric_limits<unsigned>::max() < data_length)
|
||||
throw CryptoException("Can't sign data: too large !");
|
||||
gnutls_datum_t sig {nullptr, 0};
|
||||
gnutls_datum_t sig;
|
||||
const gnutls_datum_t dat {(unsigned char*)data, (unsigned)data_length};
|
||||
if (gnutls_privkey_sign_data(key, GNUTLS_DIG_SHA512, 0, &dat, &sig) != GNUTLS_E_SUCCESS)
|
||||
throw CryptoException("Can't sign data !");
|
||||
@@ -334,7 +294,7 @@ Blob
|
||||
PrivateKey::decryptBloc(const uint8_t* src, size_t src_size) const
|
||||
{
|
||||
const gnutls_datum_t dat {(uint8_t*)src, (unsigned)src_size};
|
||||
gnutls_datum_t out {nullptr, 0};
|
||||
gnutls_datum_t out;
|
||||
int err = gnutls_privkey_decrypt_data(key, 0, &dat, &out);
|
||||
if (err != GNUTLS_E_SUCCESS)
|
||||
throw DecryptError(std::string("Can't decrypt data: ") + gnutls_strerror(err));
|
||||
@@ -512,7 +472,7 @@ void
|
||||
PublicKey::encryptBloc(const uint8_t* src, size_t src_size, uint8_t* dst, size_t dst_size) const
|
||||
{
|
||||
const gnutls_datum_t key_dat {(uint8_t*)src, (unsigned)src_size};
|
||||
gnutls_datum_t encrypted {nullptr, 0};
|
||||
gnutls_datum_t encrypted;
|
||||
auto err = gnutls_pubkey_encrypt_data(pk, 0, &key_dat, &encrypted);
|
||||
if (err != GNUTLS_E_SUCCESS)
|
||||
throw CryptoException(std::string("Can't encrypt data: ") + gnutls_strerror(err));
|
||||
@@ -553,7 +513,7 @@ PublicKey::encrypt(const uint8_t* data, size_t data_len) const
|
||||
throw CryptoException("Key is not long enough for AES128");
|
||||
Blob key(aes_key_sz);
|
||||
{
|
||||
std::random_device rdev;
|
||||
crypto::random_device rdev;
|
||||
std::generate_n(key.begin(), key.size(), std::bind(rand_byte, std::ref(rdev)));
|
||||
}
|
||||
auto data_encrypted = aesEncrypt(data, data_len, key);
|
||||
@@ -1015,7 +975,7 @@ Certificate::toString(bool chain) const
|
||||
std::string
|
||||
Certificate::print() const
|
||||
{
|
||||
gnutls_datum_t out {nullptr, 0};
|
||||
gnutls_datum_t out;
|
||||
gnutls_x509_crt_print(cert, GNUTLS_CRT_PRINT_FULL, &out);
|
||||
std::string ret(out.data, out.data+out.size);
|
||||
gnutls_free(out.data);
|
||||
@@ -1091,7 +1051,7 @@ Certificate::generateOcspRequest(gnutls_x509_crt_t& issuer)
|
||||
err = gnutls_ocsp_req_set_nonce(req.get(), 0, &nonce);
|
||||
if (err < 0)
|
||||
throw CryptoException(gnutls_strerror(err));
|
||||
gnutls_datum_t rdata {nullptr, 0};
|
||||
gnutls_datum_t rdata;
|
||||
err = gnutls_ocsp_req_export(req.get(), &rdata);
|
||||
if (err != 0)
|
||||
throw CryptoException(gnutls_strerror(err));
|
||||
@@ -1164,47 +1124,14 @@ saveIdentity(const Identity& id, const std::string& path, const std::string& pri
|
||||
auto ca_key_data = id.first->serialize(privkey_password);
|
||||
std::ofstream key_file(path + ".pem");
|
||||
key_file.write((char*)ca_key_data.data(), ca_key_data.size());
|
||||
// Throw error if the file is not written
|
||||
if (!key_file)
|
||||
throw CryptoException("Could not write private key file");
|
||||
}
|
||||
{
|
||||
auto ca_key_data = id.second->getPacked();
|
||||
std::ofstream crt_file(path + ".crt");
|
||||
crt_file.write((char*)ca_key_data.data(), ca_key_data.size());
|
||||
// Throw error if the file is not written
|
||||
if (!crt_file)
|
||||
throw CryptoException("Could not write certificate file");
|
||||
}
|
||||
}
|
||||
|
||||
Identity
|
||||
loadIdentity(const std::string &path,const std::string &privkey_password)
|
||||
{
|
||||
std::ifstream pkStream(path + ".pem", std::ios::in | std::ios::binary);
|
||||
std::vector<uint8_t> pkContent((std::istreambuf_iterator<char>(pkStream)),
|
||||
std::istreambuf_iterator<char>());
|
||||
auto key = std::make_shared<PrivateKey>(pkContent, privkey_password);
|
||||
pkStream.close();
|
||||
// Create a certificate
|
||||
gnutls_x509_crt_t gnuCert;
|
||||
if (gnutls_x509_crt_init(&gnuCert))
|
||||
throw std::runtime_error("Failed to initialize gnutls certificate struct");
|
||||
gnutls_datum_t crtContent {nullptr, 0};
|
||||
|
||||
// Read the certificate file
|
||||
int err = gnutls_load_file((path + ".crt").c_str(), &crtContent);
|
||||
if (err)
|
||||
throw CryptoException(gnutls_strerror(err));
|
||||
|
||||
err = gnutls_x509_crt_import(gnuCert, &crtContent, GNUTLS_X509_FMT_PEM);
|
||||
if (err)
|
||||
throw CryptoException(gnutls_strerror(err));
|
||||
|
||||
auto cert = std::make_shared<Certificate>(gnuCert);
|
||||
return {std::move(key), std::move(cert)};
|
||||
}
|
||||
|
||||
void
|
||||
setValidityPeriod(gnutls_x509_crt_t cert, int64_t validity)
|
||||
{
|
||||
@@ -1220,7 +1147,7 @@ setValidityPeriod(gnutls_x509_crt_t cert, int64_t validity)
|
||||
void
|
||||
setRandomSerial(gnutls_x509_crt_t cert)
|
||||
{
|
||||
std::random_device rdev;
|
||||
random_device rdev;
|
||||
std::uniform_int_distribution<int64_t> dist{1};
|
||||
int64_t cert_serial = dist(rdev);
|
||||
gnutls_x509_crt_set_serial(cert, &cert_serial, sizeof(cert_serial));
|
||||
@@ -1247,49 +1174,31 @@ Certificate::generate(const PrivateKey& key, const std::string& name, const Iden
|
||||
auto pk_id = pk.getId();
|
||||
const std::string uid_str = pk_id.toString();
|
||||
|
||||
int err = gnutls_x509_crt_set_subject_key_id(cert, &pk_id, sizeof(pk_id));
|
||||
if(err) {
|
||||
throw CryptoException(std::string("Error when setting subject key id ") + gnutls_strerror(err));
|
||||
}
|
||||
|
||||
err = gnutls_x509_crt_set_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, name.data(), name.length());
|
||||
if(err) {
|
||||
throw CryptoException(std::string("Error when setting subject key id ") + gnutls_strerror(err));
|
||||
}
|
||||
err = gnutls_x509_crt_set_dn_by_oid(cert, GNUTLS_OID_LDAP_UID, 0, uid_str.data(), uid_str.length());
|
||||
if(err) {
|
||||
throw CryptoException(std::string("Error when setting dn by oid ") + gnutls_strerror(err));
|
||||
}
|
||||
gnutls_x509_crt_set_subject_key_id(cert, &pk_id, sizeof(pk_id));
|
||||
gnutls_x509_crt_set_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, name.data(), name.length());
|
||||
gnutls_x509_crt_set_dn_by_oid(cert, GNUTLS_OID_LDAP_UID, 0, uid_str.data(), uid_str.length());
|
||||
|
||||
setRandomSerial(cert);
|
||||
|
||||
unsigned key_usage = 0;
|
||||
if (is_ca) {
|
||||
err = gnutls_x509_crt_set_ca_status(cert, 1);
|
||||
if(err) {
|
||||
throw CryptoException(std::string("Error when setting ca status ") + gnutls_strerror(err));
|
||||
}
|
||||
gnutls_x509_crt_set_ca_status(cert, 1);
|
||||
key_usage |= GNUTLS_KEY_KEY_CERT_SIGN | GNUTLS_KEY_CRL_SIGN;
|
||||
} else {
|
||||
key_usage |= GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_DATA_ENCIPHERMENT;
|
||||
}
|
||||
err = gnutls_x509_crt_set_key_usage(cert, key_usage);
|
||||
if(err) {
|
||||
throw CryptoException(std::string("Error when setting ca status ") + gnutls_strerror(err));
|
||||
}
|
||||
gnutls_x509_crt_set_key_usage(cert, key_usage);
|
||||
|
||||
if (ca.first && ca.second) {
|
||||
if (not ca.second->isCA()) {
|
||||
throw CryptoException("Signing certificate must be CA");
|
||||
}
|
||||
err = gnutls_x509_crt_privkey_sign(cert, ca.second->cert, ca.first->key, pk.getPreferredDigest(), 0);
|
||||
if (err) {
|
||||
if (int err = gnutls_x509_crt_privkey_sign(cert, ca.second->cert, ca.first->key, pk.getPreferredDigest(), 0)) {
|
||||
throw CryptoException(std::string("Error when signing certificate ") + gnutls_strerror(err));
|
||||
}
|
||||
ret.issuer = ca.second;
|
||||
} else {
|
||||
err = gnutls_x509_crt_privkey_sign(cert, cert, key.key, pk.getPreferredDigest(), 0);
|
||||
if (err) {
|
||||
if (int err = gnutls_x509_crt_privkey_sign(cert, cert, key.key, pk.getPreferredDigest(), 0)) {
|
||||
throw CryptoException(std::string("Error when signing certificate ") + gnutls_strerror(err));
|
||||
}
|
||||
}
|
||||
@@ -1385,7 +1294,7 @@ std::string
|
||||
OcspRequest::toString(const bool compact) const
|
||||
{
|
||||
int ret;
|
||||
gnutls_datum_t dat {nullptr, 0};
|
||||
gnutls_datum_t dat;
|
||||
ret = gnutls_ocsp_req_print(request, compact ? GNUTLS_OCSP_PRINT_COMPACT : GNUTLS_OCSP_PRINT_FULL, &dat);
|
||||
|
||||
std::string str;
|
||||
@@ -1400,7 +1309,7 @@ OcspRequest::toString(const bool compact) const
|
||||
Blob
|
||||
OcspRequest::pack() const
|
||||
{
|
||||
gnutls_datum_t dat {nullptr, 0};
|
||||
gnutls_datum_t dat;
|
||||
int err = gnutls_ocsp_req_export(request, &dat);
|
||||
if (err < 0)
|
||||
throw CryptoException(gnutls_strerror(err));
|
||||
@@ -1412,7 +1321,7 @@ OcspRequest::pack() const
|
||||
Blob
|
||||
OcspRequest::getNonce() const
|
||||
{
|
||||
gnutls_datum_t dat {nullptr, 0};
|
||||
gnutls_datum_t dat;
|
||||
unsigned critical;
|
||||
int err = gnutls_ocsp_req_get_nonce(request, &critical, &dat);
|
||||
if (err < 0)
|
||||
@@ -1445,7 +1354,7 @@ OcspResponse::~OcspResponse()
|
||||
Blob
|
||||
OcspResponse::pack() const
|
||||
{
|
||||
gnutls_datum_t dat {nullptr, 0};
|
||||
gnutls_datum_t dat;
|
||||
int err = gnutls_ocsp_resp_export(response, &dat);
|
||||
if (err < 0)
|
||||
throw CryptoException(gnutls_strerror(err));
|
||||
@@ -1459,7 +1368,7 @@ OcspResponse::toString(const bool compact) const
|
||||
{
|
||||
int ret;
|
||||
std::string str;
|
||||
gnutls_datum_t dat {nullptr, 0};
|
||||
gnutls_datum_t dat;
|
||||
ret = gnutls_ocsp_resp_print(response, compact ? GNUTLS_OCSP_PRINT_COMPACT : GNUTLS_OCSP_PRINT_FULL, &dat);
|
||||
if (ret == 0)
|
||||
str = std::string((const char*)dat.data, (size_t)dat.size);
|
||||
@@ -1494,7 +1403,7 @@ OcspResponse::verifyDirect(const Certificate& crt, const Blob& nonce)
|
||||
|
||||
if (not nonce.empty()) {
|
||||
// Ensure no replay attack has been done
|
||||
gnutls_datum_t rnonce {nullptr, 0};
|
||||
gnutls_datum_t rnonce;
|
||||
ret = gnutls_ocsp_resp_get_nonce(response, NULL, &rnonce);
|
||||
if (ret < 0)
|
||||
throw CryptoException(gnutls_strerror(ret));
|
||||
@@ -1711,7 +1620,7 @@ RevocationList::sign(const PrivateKey& key, const Certificate& ca, duration vali
|
||||
if (number == 0) {
|
||||
// initialize to a random number
|
||||
number_sz = sizeof(number);
|
||||
std::random_device rdev;
|
||||
random_device rdev;
|
||||
std::generate_n((uint8_t*)&number, sizeof(number), std::bind(rand_byte, std::ref(rdev)));
|
||||
} else
|
||||
number = endian(endian(number) + 1);
|
||||
@@ -1752,7 +1661,7 @@ RevocationList::getNumber() const
|
||||
std::string
|
||||
RevocationList::toString() const
|
||||
{
|
||||
gnutls_datum_t out {nullptr, 0};
|
||||
gnutls_datum_t out;
|
||||
gnutls_x509_crl_print(crl, GNUTLS_CRT_PRINT_FULL, &out);
|
||||
std::string ret(out.data, out.data+out.size);
|
||||
gnutls_free(out.data);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* 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
|
||||
@@ -54,14 +54,7 @@ DhtMessage::ServiceFilter(const std::string& s)
|
||||
|
||||
std::ostream& operator<< (std::ostream& s, const IpServiceAnnouncement& v)
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
s << "Peer: " << v.addr;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
+312
-257
File diff suppressed because it is too large.
Load diff
+105
-187
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
* Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Vsevolod Ivanov <vsevolod.ivanov@savoirfairelinux.com>
|
||||
@@ -23,9 +23,12 @@
|
||||
#include "op_cache.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <llhttp.h>
|
||||
#include <http_parser.h>
|
||||
|
||||
#include <asio/ip/address.hpp>
|
||||
#include <deque>
|
||||
|
||||
|
||||
namespace dht {
|
||||
|
||||
struct DhtProxyClient::InfoState {
|
||||
@@ -97,7 +100,7 @@ std::string
|
||||
getRandomSessionId(size_t length = 8) {
|
||||
static constexpr const char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_`{|}~";
|
||||
std::string str(length, 0);
|
||||
std::random_device rdev;
|
||||
crypto::random_device rdev;
|
||||
std::uniform_int_distribution<> dist(0, (sizeof(chars)/sizeof(char)) - 2);
|
||||
std::generate_n( str.begin(), length, [&]{ return chars[dist(rdev)]; } );
|
||||
return str;
|
||||
@@ -106,32 +109,18 @@ getRandomSessionId(size_t length = 8) {
|
||||
DhtProxyClient::DhtProxyClient() {}
|
||||
|
||||
DhtProxyClient::DhtProxyClient(
|
||||
std::shared_ptr<dht::crypto::Certificate> serverCA,
|
||||
dht::crypto::Identity clientIdentity,
|
||||
std::function<void()> signal,
|
||||
const std::string& serverHost,
|
||||
const std::string& userAgent,
|
||||
const std::string& pushClientId,
|
||||
const std::string& pushToken,
|
||||
const std::string& pushTopic,
|
||||
const std::string& pushPlatform,
|
||||
std::shared_ptr<dht::Logger> logger
|
||||
)
|
||||
std::shared_ptr<dht::crypto::Certificate> serverCA, dht::crypto::Identity clientIdentity,
|
||||
std::shared_ptr<asio::io_context::strand> strand, const std::string& serverHost,
|
||||
const std::string& pushClientId, std::shared_ptr<dht::Logger> logger)
|
||||
: DhtInterface(logger)
|
||||
, proxyUrl_(serverHost)
|
||||
, clientIdentity_(clientIdentity), serverCertificate_(serverCA)
|
||||
, userAgent_(userAgent)
|
||||
, pushClientId_(pushClientId)
|
||||
, pushSessionId_(getRandomSessionId())
|
||||
, deviceKey_(pushToken)
|
||||
, notificationTopic_(pushTopic)
|
||||
, platform_(pushPlatform)
|
||||
, loopSignal_(signal)
|
||||
, pushClientId_(pushClientId), pushSessionId_(getRandomSessionId())
|
||||
, strand_(strand)
|
||||
, jsonReader_(Json::CharReaderBuilder{}.newCharReader())
|
||||
{
|
||||
localAddrv4_.setFamily(AF_INET);
|
||||
localAddrv6_.setFamily(AF_INET6);
|
||||
|
||||
/*localAddrv4_.setFamily(AF_INET);
|
||||
localAddrv6_.setFamily(AF_INET6);*/
|
||||
jsonBuilder_["commentStyle"] = "None";
|
||||
jsonBuilder_["indentation"] = "";
|
||||
if (logger_) {
|
||||
@@ -143,13 +132,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 httpContext_ won't run out of work
|
||||
auto work = asio::make_work_guard(httpContext_);
|
||||
httpContext_.run();
|
||||
// Ensures the context() won't run out of work
|
||||
auto work = asio::make_work_guard(context());
|
||||
context().run();
|
||||
if (logger_)
|
||||
logger_->d("[proxy:client] http client io_context stopped");
|
||||
}
|
||||
@@ -157,7 +146,7 @@ DhtProxyClient::DhtProxyClient(
|
||||
if (logger_)
|
||||
logger_->e("[proxy:client] run error: %s", ex.what());
|
||||
}
|
||||
});
|
||||
});*/
|
||||
if (!proxyUrl_.empty())
|
||||
startProxy();
|
||||
}
|
||||
@@ -171,12 +160,10 @@ DhtProxyClient::startProxy()
|
||||
if (logger_)
|
||||
logger_->d("[proxy:client] start proxy with %s", proxyUrl_.c_str());
|
||||
|
||||
nextProxyConfirmationTimer_ = std::make_unique<asio::steady_timer>(httpContext_, std::chrono::steady_clock::now());
|
||||
nextProxyConfirmationTimer_ = std::make_unique<asio::steady_timer>(context(), std::chrono::steady_clock::now());
|
||||
nextProxyConfirmationTimer_->async_wait(std::bind(&DhtProxyClient::handleProxyConfirm, this, std::placeholders::_1));
|
||||
|
||||
listenerRestartTimer_ = std::make_unique<asio::steady_timer>(httpContext_);
|
||||
|
||||
loopSignal_();
|
||||
listenerRestartTimer_ = std::make_unique<asio::steady_timer>(context());
|
||||
}
|
||||
|
||||
void
|
||||
@@ -203,12 +190,10 @@ void
|
||||
DhtProxyClient::stop()
|
||||
{
|
||||
if (not isDestroying_.exchange(true)) {
|
||||
std::shared_ptr<http::Resolver> resolver;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(resolverLock_);
|
||||
resolver = std::move(resolver_);
|
||||
resolver_.reset();
|
||||
}
|
||||
resolver->cancel();
|
||||
cancelAllListeners();
|
||||
if (infoState_)
|
||||
infoState_->cancel = true;
|
||||
@@ -217,8 +202,8 @@ DhtProxyClient::stop()
|
||||
for (auto& request : requests_)
|
||||
request.second->cancel();
|
||||
}
|
||||
if (not httpContext_.stopped())
|
||||
httpContext_.stop();
|
||||
if (not context().stopped())
|
||||
context().stop();
|
||||
if (httpClientThread_.joinable())
|
||||
httpClientThread_.join();
|
||||
requests_.clear();
|
||||
@@ -300,21 +285,6 @@ 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, "*/*");
|
||||
@@ -365,15 +335,11 @@ DhtProxyClient::get(const InfoHash& key, GetCallback cb, DoneCallback donecb, Va
|
||||
values.emplace_back(std::move(value));
|
||||
}
|
||||
if (not values.empty() and cb) {
|
||||
{
|
||||
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_();
|
||||
context().post([opstate, cb, values = std::move(values)](){
|
||||
if (not opstate->stop.load() and not cb(values)){
|
||||
opstate->stop.store(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch(const std::exception& e) {
|
||||
if (logger_)
|
||||
@@ -390,14 +356,10 @@ DhtProxyClient::get(const InfoHash& key, GetCallback cb, DoneCallback donecb, Va
|
||||
opFailed();
|
||||
}
|
||||
if (donecb) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(lockCallbacks_);
|
||||
callbacks_.emplace_back([donecb, opstate](){
|
||||
donecb(opstate->ok, {});
|
||||
opstate->stop.store(true);
|
||||
});
|
||||
}
|
||||
loopSignal_();
|
||||
context().post([donecb, opstate](){
|
||||
donecb(opstate->ok, {});
|
||||
opstate->stop.store(true);
|
||||
});
|
||||
}
|
||||
if (not isDestroying_) {
|
||||
std::lock_guard<std::mutex> l(requestLock_);
|
||||
@@ -433,7 +395,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>(httpContext_, proxy::OP_TIMEOUT - proxy::OP_MARGIN);
|
||||
auto refreshPutTimer = std::make_unique<asio::steady_timer>(context(), 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,
|
||||
@@ -447,12 +409,10 @@ DhtProxyClient::put(const InfoHash& key, Sp<Value> val, DoneCallback cb, time_po
|
||||
if (ok)
|
||||
*ok = result;
|
||||
if (cb) {
|
||||
std::lock_guard<std::mutex> lock(lockCallbacks_);
|
||||
callbacks_.emplace_back([cb, result](){
|
||||
context().post([cb, result](){
|
||||
cb(result, {});
|
||||
});
|
||||
}
|
||||
loopSignal_();
|
||||
}, created, permanent);
|
||||
}
|
||||
|
||||
@@ -489,15 +449,15 @@ DhtProxyClient::buildRequest(const std::string& target)
|
||||
auto resolver = resolver_;
|
||||
l.unlock();
|
||||
if (not resolver)
|
||||
resolver = std::make_shared<http::Resolver>(httpContext_, proxyUrl_, logger_);
|
||||
resolver = std::make_shared<http::Resolver>(context(), proxyUrl_, logger_);
|
||||
auto request = target.empty()
|
||||
? std::make_shared<http::Request>(httpContext_, resolver)
|
||||
: std::make_shared<http::Request>(httpContext_, resolver, target);
|
||||
? std::make_shared<http::Request>(context(), resolver)
|
||||
: std::make_shared<http::Request>(context(), resolver, target);
|
||||
if (serverCertificate_)
|
||||
request->set_certificate_authority(serverCertificate_);
|
||||
if (clientIdentity_.first and clientIdentity_.second)
|
||||
request->set_identity(clientIdentity_);
|
||||
request->set_header_field(restinio::http_field_t::user_agent, userAgent_);
|
||||
request->set_header_field(restinio::http_field_t::user_agent, "RESTinio client");
|
||||
return request;
|
||||
}
|
||||
|
||||
@@ -505,7 +465,7 @@ void
|
||||
DhtProxyClient::doPut(const InfoHash& key, Sp<Value> val, DoneCallbackSimple cb, time_point /*created*/, bool permanent)
|
||||
{
|
||||
if (logger_)
|
||||
logger_->debug("[proxy:client] [put] [search {}] executing for {}", key, val->toString());
|
||||
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());
|
||||
@@ -543,7 +503,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>(httpContext_, proxy::OP_TIMEOUT - proxy::OP_MARGIN);
|
||||
auto refreshPutTimer = std::make_unique<asio::steady_timer>(context(), 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),
|
||||
@@ -553,12 +513,12 @@ DhtProxyClient::doPut(const InfoHash& key, Sp<Value> val, DoneCallbackSimple cb,
|
||||
}
|
||||
} else {
|
||||
if (logger_)
|
||||
logger_->error("[proxy:client] [put] failed to parse value from server: {}", err);
|
||||
logger_->e("[proxy:client] [status] failed to parse value from server", response.status_code);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (logger_)
|
||||
logger_->error("[proxy:client] [put] failed with code={:d}", response.status_code);
|
||||
logger_->e("[proxy:client] [status] failed with code=%i", response.status_code);
|
||||
if (not response.aborted and response.status_code == 0)
|
||||
opFailed();
|
||||
}
|
||||
@@ -577,8 +537,7 @@ DhtProxyClient::doPut(const InfoHash& key, Sp<Value> val, DoneCallbackSimple cb,
|
||||
}
|
||||
catch (const std::exception &e){
|
||||
if (logger_)
|
||||
logger_->error("[proxy:client] [put {}] error: {}", key, e.what());
|
||||
opFailed();
|
||||
logger_->e("[proxy:client] [put %s] error: %s", key.to_c_str(), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -651,7 +610,7 @@ DhtProxyClient::getProxyInfos()
|
||||
if (logger_)
|
||||
logger_->d("[proxy:client] [status] sending request");
|
||||
|
||||
auto resolver = std::make_shared<http::Resolver>(httpContext_, proxyUrl_, logger_);
|
||||
auto resolver = std::make_shared<http::Resolver>(context(), proxyUrl_, logger_);
|
||||
queryProxyInfo(infoState, resolver, AF_INET);
|
||||
queryProxyInfo(infoState, resolver, AF_INET6);
|
||||
std::lock_guard<std::mutex> l(resolverLock_);
|
||||
@@ -664,7 +623,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>(httpContext_, resolver, family);
|
||||
auto request = std::make_shared<http::Request>(context(), resolver, family);
|
||||
if (serverCertificate_)
|
||||
request->set_certificate_authority(serverCertificate_);
|
||||
auto reqid = request->id();
|
||||
@@ -730,41 +689,33 @@ DhtProxyClient::onProxyInfos(const Json::Value& proxyInfos, const sa_family_t fa
|
||||
if (logger_)
|
||||
logger_->e("[proxy:client] [info] request failed for %s", family == AF_INET ? "ipv4" : "ipv6");
|
||||
status = NodeStatus::Disconnected;
|
||||
if (pubAddress) {
|
||||
if (pubAddress != SockAddr{}) {
|
||||
pubAddress = {};
|
||||
ipChanged = true;
|
||||
}
|
||||
} else {
|
||||
if (logger_)
|
||||
logger_->debug("[proxy:client] [info] got proxy reply for {}",
|
||||
logger_->d("[proxy:client] [info] got proxy reply for %s",
|
||||
family == AF_INET ? "ipv4" : "ipv6");
|
||||
try {
|
||||
myid = InfoHash(proxyInfos["node_id"].asString());
|
||||
stats4_ = NodeStats(proxyInfos["ipv4"]);
|
||||
stats6_ = NodeStats(proxyInfos["ipv6"]);
|
||||
auto publicIp = parsePublicAddress(proxyInfos["public_ip"]);
|
||||
ipChanged = pubAddress && pubAddress.toString() != publicIp.toString();
|
||||
bool pub = ipChanged || !pubAddress;
|
||||
ipChanged = pubAddress != SockAddr{} && pubAddress != publicIp;
|
||||
pubAddress = publicIp;
|
||||
|
||||
if (proxyInfos.isMember("local_ip")) {
|
||||
std::string localIp = proxyInfos["local_ip"].asString();
|
||||
if (localAddress.toString() != localIp) {
|
||||
localAddress.setAddress(localIp.c_str());
|
||||
ipChanged = (bool)localAddress;
|
||||
auto localAddr = asio::ip::make_address(localIp);
|
||||
if (localAddr != localAddress.address()) {
|
||||
localAddress = SockAddr(localAddr, localAddress.port());
|
||||
ipChanged = localAddress != SockAddr{};
|
||||
}
|
||||
}
|
||||
|
||||
if (pub && publicAddressChangedCb_) {
|
||||
std::vector<SockAddr> addresses;
|
||||
if (publicAddressV4_)
|
||||
addresses.emplace_back(publicAddressV4_);
|
||||
if (publicAddressV6_)
|
||||
addresses.emplace_back(publicAddressV6_);
|
||||
std::lock_guard<std::mutex> lock(lockCallbacks_);
|
||||
callbacks_.emplace_back([cb=publicAddressChangedCb_, addresses = std::move(addresses)](){
|
||||
cb(std::move(addresses));
|
||||
});
|
||||
/*if (localAddress.toString() != localIp) {
|
||||
localAddress.setAddress(localIp.c_str());
|
||||
ipChanged = localAddress != SockAddr{};
|
||||
}*/
|
||||
}
|
||||
|
||||
if (!ipChanged && stats4_.good_nodes + stats6_.good_nodes)
|
||||
@@ -780,14 +731,16 @@ DhtProxyClient::onProxyInfos(const Json::Value& proxyInfos, const sa_family_t fa
|
||||
}
|
||||
}
|
||||
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()) {
|
||||
std::lock_guard<std::mutex> lock(lockCallbacks_);
|
||||
callbacks_.emplace_back([cbs = std::move(onConnectCallbacks_)]() mutable {
|
||||
context().post([cbs = std::move(onConnectCallbacks_)]() mutable {
|
||||
while (not cbs.empty()) {
|
||||
cbs.front()();
|
||||
cbs.pop();
|
||||
@@ -805,18 +758,25 @@ DhtProxyClient::onProxyInfos(const Json::Value& proxyInfos, const sa_family_t fa
|
||||
nextProxyConfirmationTimer_->expires_at(next);
|
||||
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 = SockAddr::resolve(hostAndService.first);
|
||||
if (sa.empty()) return {};
|
||||
return sa.front().getMappedIPv4();
|
||||
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);
|
||||
}
|
||||
|
||||
std::vector<SockAddr>
|
||||
@@ -824,8 +784,8 @@ DhtProxyClient::getPublicAddress(sa_family_t family)
|
||||
{
|
||||
std::lock_guard<std::mutex> l(lockCurrentProxyInfos_);
|
||||
std::vector<SockAddr> result;
|
||||
if (publicAddressV6_ && family != AF_INET) result.emplace_back(publicAddressV6_);
|
||||
if (publicAddressV4_ && family != AF_INET6) result.emplace_back(publicAddressV4_);
|
||||
if (publicAddressV6_ != SockAddr{} && family != AF_INET) result.emplace_back(publicAddressV6_);
|
||||
if (publicAddressV4_ != SockAddr{} && family != AF_INET6) result.emplace_back(publicAddressV4_);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -885,7 +845,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>(httpContext_);
|
||||
l->second.refreshSubscriberTimer = std::make_unique<asio::steady_timer>(context());
|
||||
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,
|
||||
@@ -952,7 +912,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>(httpContext_, ops.getExpiration());
|
||||
it->second.opExpirationTimer = std::make_unique<asio::steady_timer>(context(), ops.getExpiration());
|
||||
else
|
||||
it->second.opExpirationTimer->expires_at(ops.getExpiration());
|
||||
it->second.opExpirationTimer->async_wait(std::bind(&DhtProxyClient::handleExpireListener, this, std::placeholders::_1, key));
|
||||
@@ -1023,10 +983,7 @@ DhtProxyClient::handleExpireListener(const asio::error_code &ec, const InfoHash&
|
||||
}
|
||||
} else {
|
||||
// stop the request
|
||||
if (listener.request) {
|
||||
listener.request->cancel();
|
||||
listener.request.reset();
|
||||
}
|
||||
listener.request.reset();
|
||||
}
|
||||
search->second.listeners.erase(it);
|
||||
if (logger_)
|
||||
@@ -1087,14 +1044,10 @@ 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();
|
||||
{
|
||||
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_();
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch(const std::exception& e) {
|
||||
@@ -1121,14 +1074,6 @@ DhtProxyClient::sendListen(const restinio::http_request_header_t& header,
|
||||
std::lock_guard<std::mutex> l(requestLock_);
|
||||
requests_[reqid] = request;
|
||||
}
|
||||
request->add_on_status_callback([r=std::weak_ptr(request), seconds = this->listenKeepIdle()] (unsigned status_code) {
|
||||
if(status_code == 200) {
|
||||
// increase TCP_KEEPIDLE to save power
|
||||
if (auto request = r.lock()) {
|
||||
request->get_connection()->set_keepalive(seconds);
|
||||
}
|
||||
}
|
||||
});
|
||||
request->send();
|
||||
}
|
||||
catch (const std::exception &e){
|
||||
@@ -1150,7 +1095,6 @@ DhtProxyClient::opFailed()
|
||||
statusIpv6_ = NodeStatus::Disconnected;
|
||||
}
|
||||
getConnectivityStatus();
|
||||
loopSignal_();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1180,22 +1124,18 @@ DhtProxyClient::restartListeners(const asio::error_code &ec)
|
||||
|
||||
std::lock_guard<std::mutex> lock(searchLock_);
|
||||
for (auto& search : searches_) {
|
||||
const auto& key = search.first;
|
||||
auto key = search.first;
|
||||
for (auto& put : search.second.puts) {
|
||||
doPut(key, put.second.value, [ok = put.second.ok](bool result){
|
||||
*ok = result;
|
||||
}, time_point::max(), true);
|
||||
if (!put.second.refreshPutTimer) {
|
||||
put.second.refreshPutTimer = std::make_unique<asio::steady_timer>(httpContext_);
|
||||
put.second.refreshPutTimer = std::make_unique<asio::steady_timer>(context());
|
||||
}
|
||||
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,
|
||||
std::placeholders::_1, key, put.first));
|
||||
}
|
||||
// Restart failed pending puts
|
||||
for (auto& pput : search.second.pendingPuts) {
|
||||
doPut(key, pput, {}, time_point::max(), true);
|
||||
}
|
||||
}
|
||||
if (not deviceKey_.empty()) {
|
||||
if (logger_)
|
||||
@@ -1236,7 +1176,7 @@ DhtProxyClient::restartListeners(const asio::error_code &ec)
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyClient::pushNotificationReceived([[maybe_unused]] const std::map<std::string, std::string>& notification)
|
||||
DhtProxyClient::pushNotificationReceived(const std::map<std::string, std::string>& notification)
|
||||
{
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
{
|
||||
@@ -1248,7 +1188,6 @@ DhtProxyClient::pushNotificationReceived([[maybe_unused]] const std::map<std::st
|
||||
statusIpv4_ = NodeStatus::Connected;
|
||||
statusIpv6_ = NodeStatus::Connected;
|
||||
}
|
||||
auto launchLoop = false;
|
||||
try {
|
||||
auto sessionId = notification.find("s");
|
||||
if (sessionId != notification.end() and sessionId->second != pushSessionId_) {
|
||||
@@ -1267,7 +1206,7 @@ DhtProxyClient::pushNotificationReceived([[maybe_unused]] const std::map<std::st
|
||||
auto vid = std::stoull(vidIt->second);
|
||||
auto& put = search.puts.at(vid);
|
||||
if (!put.refreshPutTimer)
|
||||
put.refreshPutTimer = std::make_unique<asio::steady_timer>(httpContext_, std::chrono::steady_clock::now());
|
||||
put.refreshPutTimer = std::make_unique<asio::steady_timer>(context(), 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));
|
||||
@@ -1309,23 +1248,19 @@ DhtProxyClient::pushNotificationReceived([[maybe_unused]] const std::map<std::st
|
||||
getline(ss, substr, ',');
|
||||
ids.emplace_back(std::stoull(substr));
|
||||
}
|
||||
{
|
||||
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;
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1333,13 +1268,13 @@ DhtProxyClient::pushNotificationReceived([[maybe_unused]] const std::map<std::st
|
||||
if (logger_)
|
||||
logger_->e("[proxy:client] [push] receive error: %s", e.what());
|
||||
}
|
||||
if (launchLoop)
|
||||
loopSignal_();
|
||||
#else
|
||||
(void) notification;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyClient::resubscribe([[maybe_unused]] const InfoHash& key, [[maybe_unused]] const size_t token, [[maybe_unused]] Listener& listener)
|
||||
DhtProxyClient::resubscribe(const InfoHash& key, const size_t token, Listener& listener)
|
||||
{
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
if (deviceKey_.empty())
|
||||
@@ -1355,7 +1290,7 @@ DhtProxyClient::resubscribe([[maybe_unused]] const InfoHash& key, [[maybe_unused
|
||||
header.method(restinio::http_method_subscribe());
|
||||
header.request_target("/" + key.toString());
|
||||
if (!listener.refreshSubscriberTimer){
|
||||
listener.refreshSubscriberTimer = std::make_unique<asio::steady_timer>(httpContext_);
|
||||
listener.refreshSubscriberTimer = std::make_unique<asio::steady_timer>(context());
|
||||
}
|
||||
listener.refreshSubscriberTimer->expires_at(std::chrono::steady_clock::now() +
|
||||
proxy::OP_TIMEOUT - proxy::OP_MARGIN);
|
||||
@@ -1363,6 +1298,9 @@ DhtProxyClient::resubscribe([[maybe_unused]] const InfoHash& key, [[maybe_unused
|
||||
std::placeholders::_1, key, token, opstate));
|
||||
auto vcb = listener.cb;
|
||||
sendListen(header, vcb, opstate, listener, ListenMethod::RESUBSCRIBE);
|
||||
#else
|
||||
(void) key;
|
||||
(void) listener;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1396,24 +1334,4 @@ DhtProxyClient::fillBody(bool resubscribe)
|
||||
}
|
||||
#endif // OPENDHT_PUSH_NOTIFICATIONS
|
||||
|
||||
void
|
||||
DhtProxyClient::setPushNotificationToken([[maybe_unused]] const std::string& token) {
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
std::unique_lock<std::mutex> l(lockCurrentProxyInfos_);
|
||||
if (deviceKey_ != token) {
|
||||
deviceKey_ = token;
|
||||
if (statusIpv4_ == NodeStatus::Connected || statusIpv6_ == NodeStatus::Connected) {
|
||||
if (logger_)
|
||||
logger_->d("[proxy:client] [push] token changed, resubscribing");
|
||||
for (auto& search : searches_) {
|
||||
for (auto& listener : search.second.listeners) {
|
||||
resubscribe(search.first, listener.first, listener.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
} // namespace dht
|
||||
+73
-114
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
* Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Vsevolod Ivanov <vsevolod.ivanov@savoirfairelinux.com>
|
||||
@@ -58,19 +58,6 @@ constexpr const std::chrono::minutes PRINT_STATS_PERIOD {2};
|
||||
using ResponseByParts = restinio::chunked_output_t;
|
||||
using ResponseByPartsBuilder = restinio::response_builder_t<ResponseByParts>;
|
||||
|
||||
namespace http {
|
||||
/**
|
||||
* Session value associated with a connection_id_t key.
|
||||
*/
|
||||
struct ListenerSession
|
||||
{
|
||||
ListenerSession() = default;
|
||||
dht::InfoHash hash;
|
||||
std::future<size_t> token;
|
||||
std::shared_ptr<restinio::response_builder_t<restinio::chunked_output_t>> response;
|
||||
};
|
||||
}
|
||||
|
||||
class opendht_logger_t
|
||||
{
|
||||
public:
|
||||
@@ -129,7 +116,7 @@ private:
|
||||
void
|
||||
DhtProxyServer::ConnectionListener::state_changed(const restinio::connection_state::notice_t& notice) noexcept
|
||||
{
|
||||
if (std::holds_alternative<restinio::connection_state::closed_t>(notice.cause())) {
|
||||
if (restinio::holds_alternative<restinio::connection_state::closed_t>(notice.cause())) {
|
||||
onClosed_(notice.connection_id());
|
||||
}
|
||||
}
|
||||
@@ -223,7 +210,6 @@ DhtProxyServer::DhtProxyServer(const std::shared_ptr<DhtRunner>& dht,
|
||||
: ioContext_(std::make_shared<asio::io_context>()),
|
||||
dht_(dht), persistPath_(config.persistStatePath), logger_(logger),
|
||||
printStatsTimer_(std::make_unique<asio::steady_timer>(*ioContext_, 3s)),
|
||||
serverStartTime_(clock::now()),
|
||||
connListener_(std::make_shared<ConnectionListener>(std::bind(&DhtProxyServer::onConnectionClosed, this, std::placeholders::_1))),
|
||||
pushServer_(config.pushServer),
|
||||
bundleId_(config.bundleId)
|
||||
@@ -293,7 +279,7 @@ DhtProxyServer::DhtProxyServer(const std::shared_ptr<DhtRunner>& dht,
|
||||
ioContext_,
|
||||
std::forward<restinio::run_on_this_thread_settings_t<RestRouterTraitsTls>>(std::move(settings))
|
||||
);
|
||||
// run http server
|
||||
// run https server
|
||||
serverThread_ = std::thread([this]{
|
||||
httpsServer_->open_async([]{/*ok*/}, [](std::exception_ptr ex){
|
||||
std::rethrow_exception(ex);
|
||||
@@ -416,12 +402,31 @@ DhtProxyServer::loadState(Is& is, size_t size) {
|
||||
for (auto& pushListener : pushListeners_) {
|
||||
for (auto& listeners : pushListener.second.listeners) {
|
||||
for (auto& listener : listeners.second) {
|
||||
// start listening
|
||||
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]
|
||||
(const std::vector<Sp<Value>>& values, bool expired) {
|
||||
return this->handlePushListen(infoHash, pushToken, type, clientId, sessionCtx, topic, values, expired);
|
||||
[this, infoHash=listeners.first, pushToken=pushListener.first, type=listener.type, clientId=listener.clientId, sessionCtx = listener.sessionCtx, topic=listener.topic]
|
||||
(const std::vector<std::shared_ptr<Value>>& values, bool expired) {
|
||||
// Build message content
|
||||
Json::Value json;
|
||||
json["key"] = infoHash.toString();
|
||||
json["to"] = clientId;
|
||||
json["t"] = Json::Value::Int64(std::chrono::duration_cast<std::chrono::milliseconds>(system_clock::now().time_since_epoch()).count());
|
||||
{
|
||||
std::lock_guard<std::mutex> l(sessionCtx->lock);
|
||||
json["s"] = sessionCtx->sessionId;
|
||||
}
|
||||
if (expired and values.size() < 2){
|
||||
std::ostringstream ss;
|
||||
for(size_t i = 0; i < values.size(); ++i){
|
||||
if(i != 0) ss << ",";
|
||||
ss << values[i]->id;
|
||||
}
|
||||
json["exp"] = ss.str();
|
||||
}
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
// expire notify
|
||||
@@ -461,8 +466,17 @@ DhtProxyServer::io_context() const
|
||||
return *ioContext_;
|
||||
}
|
||||
|
||||
DhtProxyServer::~DhtProxyServer()
|
||||
void
|
||||
DhtProxyServer::stop()
|
||||
{
|
||||
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());
|
||||
@@ -490,13 +504,13 @@ DhtProxyServer::~DhtProxyServer()
|
||||
pushListeners_.clear();
|
||||
#endif
|
||||
}
|
||||
if (logger_)
|
||||
logger_->d("[proxy:server] closing http server");
|
||||
}
|
||||
|
||||
DhtProxyServer::~DhtProxyServer()
|
||||
{
|
||||
ioContext_->stop();
|
||||
if (serverThread_.joinable())
|
||||
serverThread_.join();
|
||||
if (logger_)
|
||||
logger_->d("[proxy:server] http server closed");
|
||||
}
|
||||
|
||||
template< typename ServerSettings >
|
||||
@@ -543,15 +557,7 @@ DhtProxyServer::updateStats(std::shared_ptr<NodeInfo> info) const
|
||||
stats.requestRate = count / dt.count();
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
stats.pushListenersCount = pushListeners_.size();
|
||||
{
|
||||
std::lock_guard lk(pushStatsMutex_);
|
||||
stats.androidPush = androidPush_;
|
||||
stats.iosPush = iosPush_;
|
||||
stats.unifiedPush = unifiedPush_;
|
||||
}
|
||||
#endif
|
||||
stats.serverStartTime = serverStartTime_;
|
||||
stats.lastUpdated = now;
|
||||
stats.totalPermanentPuts = 0;
|
||||
std::for_each(puts_.begin(), puts_.end(), [&stats](const auto& put) {
|
||||
stats.totalPermanentPuts += put.second.puts.size();
|
||||
@@ -900,13 +906,13 @@ DhtProxyServer::subscribe(restinio::request_handle_t request,
|
||||
// Send response
|
||||
if (not newListener) {
|
||||
if (logger_)
|
||||
logger_->d("[proxy:server] [subscribe %s] found [client %s]", infoHash.toString().c_str(), listener.clientId.c_str());
|
||||
logger_->d("[proxy:server] [subscribe] found [client %s]", listener.clientId.c_str());
|
||||
// Send response header
|
||||
auto response = std::make_shared<ResponseByPartsBuilder>(initHttpResponse(request->create_response<ResponseByParts>()));
|
||||
response->flush();
|
||||
if (!root["refresh"].asBool()) {
|
||||
// No Refresh
|
||||
dht_->get(infoHash, [this, response](const Sp<Value>& value) {
|
||||
dht_->get(infoHash, [this, response](const Sp<Value>& value){
|
||||
auto output = Json::writeString(jsonBuilder_, value->toJson()) + "\n";
|
||||
response->append_chunk(output);
|
||||
response->flush();
|
||||
@@ -923,15 +929,33 @@ DhtProxyServer::subscribe(restinio::request_handle_t request,
|
||||
} else {
|
||||
// =========== No existing listener for an infoHash ============
|
||||
// Add listen on dht
|
||||
if (logger_)
|
||||
logger_->d("[proxy:server] [subscribe %s] new", infoHash.toString().c_str());
|
||||
listener.internalToken = dht_->listen(infoHash,
|
||||
[this, infoHash, pushToken, type, clientId, sessionCtx = listener.sessionCtx, topic]
|
||||
(const std::vector<Sp<Value>>& values, bool expired) {
|
||||
return this->handlePushListen(infoHash, pushToken, type, clientId, sessionCtx, topic, values, expired);
|
||||
(const std::vector<std::shared_ptr<Value>>& values, bool expired){
|
||||
// Build message content
|
||||
Json::Value json;
|
||||
json["key"] = infoHash.toString();
|
||||
json["to"] = clientId;
|
||||
json["t"] = Json::Value::Int64(std::chrono::duration_cast<std::chrono::milliseconds>(system_clock::now().time_since_epoch()).count());
|
||||
{
|
||||
std::lock_guard<std::mutex> l(sessionCtx->lock);
|
||||
json["s"] = sessionCtx->sessionId;
|
||||
}
|
||||
if (expired and values.size() < 2){
|
||||
std::ostringstream ss;
|
||||
for(size_t i = 0; i < values.size(); ++i) {
|
||||
if(i != 0) ss << ",";
|
||||
ss << values[i]->id;
|
||||
}
|
||||
json["exp"] = ss.str();
|
||||
}
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
// Send response header
|
||||
auto response = initHttpResponse(request->create_response());
|
||||
response.set_body("{}\n");
|
||||
return response.done();
|
||||
@@ -1035,54 +1059,10 @@ DhtProxyServer::handleCancelPushListen(const asio::error_code &ec, const std::st
|
||||
pushListeners_.erase(pushListener);
|
||||
}
|
||||
|
||||
bool
|
||||
DhtProxyServer::handlePushListen(const InfoHash& infoHash, const std::string& pushToken, PushType type, const std::string& clientId,
|
||||
const std::shared_ptr<DhtProxyServer::PushSessionContext>& sessionCtx, const std::string& topic,
|
||||
const std::vector<std::shared_ptr<Value>>& values, bool expired)
|
||||
{
|
||||
Json::Value json;
|
||||
json["key"] = infoHash.toString();
|
||||
json["to"] = clientId;
|
||||
using namespace std::chrono;
|
||||
json["t"] = Json::Value::Int64(duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count());
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> l(sessionCtx->lock);
|
||||
json["s"] = sessionCtx->sessionId;
|
||||
}
|
||||
|
||||
// Build a comma-separated list of ids from the values. This is intended to be used when
|
||||
// streaming from the iOS notification extension in order to filter out unwanted values.
|
||||
std::string ids;
|
||||
for(const auto& value : values) {
|
||||
if (!ids.empty()) ids += ",";
|
||||
ids += std::to_string(value->id);
|
||||
}
|
||||
json["ids"] = ids;
|
||||
|
||||
// If message is expired copy the value ID to the `exp` field.
|
||||
// This is presumably used by the Android notification system.
|
||||
if (expired && values.size() < 2) {
|
||||
json["exp"] = json["ids"];
|
||||
}
|
||||
|
||||
auto minPriority = 1000u;
|
||||
for (const auto& v : values)
|
||||
minPriority = std::min(minPriority, v->priority);
|
||||
|
||||
if (logger_)
|
||||
logger_->d("[proxy:server] [listen %s] [client %s] [session %s] [expired %i] [priority %i] [values %zu]",
|
||||
infoHash.toString().c_str(), clientId.c_str(), sessionCtx->sessionId.c_str(), expired, minPriority, values.size());
|
||||
|
||||
sendPushNotification(pushToken, std::move(json), type, !expired and minPriority == 0, topic);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::sendPushNotification(const std::string& token, Json::Value&& json, PushType type, bool highPriority, const std::string& topic)
|
||||
{
|
||||
if (pushServer_.empty() && type != PushType::UnifiedPush)
|
||||
if (pushServer_.empty())
|
||||
return;
|
||||
|
||||
unsigned reqid = 0;
|
||||
@@ -1090,7 +1070,7 @@ DhtProxyServer::sendPushNotification(const std::string& token, Json::Value&& jso
|
||||
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 + (tokenUrl.service.empty() ? "" : (":" + tokenUrl.service)), logger_);
|
||||
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_);;
|
||||
reqid = request->id();
|
||||
@@ -1109,33 +1089,27 @@ DhtProxyServer::sendPushNotification(const std::string& token, Json::Value&& jso
|
||||
request->set_body(Json::writeString(jsonBuilder_, std::move(json)));
|
||||
} else {
|
||||
// NOTE: see https://github.com/appleboy/gorush
|
||||
auto isResubscribe = json.isMember("timeout");
|
||||
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);
|
||||
auto priority = highPriority ? "high" : "normal";
|
||||
if (type == PushType::Android) {
|
||||
Json::Value androidConfig(Json::objectValue);
|
||||
androidConfig["priority"] = priority;
|
||||
androidConfig["ttl"] = "86400s"; // time to live = 24 hours
|
||||
notification["android"] = std::move(androidConfig);
|
||||
} else {
|
||||
notification["priority"] = priority;
|
||||
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 || isResubscribe) {
|
||||
if (highPriority) {
|
||||
Json::Value alert(Json::objectValue);
|
||||
alert["title"]="hello";
|
||||
notification["push_type"] = "alert";
|
||||
notification["alert"] = alert;
|
||||
notification["mutable_content"] = true;
|
||||
notification["priority"] = "high";
|
||||
} else {
|
||||
notification["push_type"] = "background";
|
||||
notification["content_available"] = true;
|
||||
@@ -1164,21 +1138,6 @@ DhtProxyServer::sendPushNotification(const std::string& token, Json::Value&& jso
|
||||
requests_[reqid] = request;
|
||||
}
|
||||
request->send();
|
||||
// For monitoring purposes
|
||||
std::lock_guard lk(pushStatsMutex_);
|
||||
switch (type) {
|
||||
case PushType::Android:
|
||||
androidPush_.increment(highPriority);
|
||||
break;
|
||||
case PushType::iOS:
|
||||
iosPush_.increment(highPriority);
|
||||
break;
|
||||
case PushType::UnifiedPush:
|
||||
unifiedPush_.increment(highPriority);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e){
|
||||
if (logger_)
|
||||
|
||||
+178
-210
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include "dhtrunner.h"
|
||||
#include "securedht.h"
|
||||
#include "network_utils.h"
|
||||
#ifdef OPENDHT_PEER_DISCOVERY
|
||||
#include "peer_discovery.h"
|
||||
#endif
|
||||
@@ -65,24 +64,22 @@ DhtRunner::~DhtRunner()
|
||||
void
|
||||
DhtRunner::run(in_port_t port, Config& config, Context&& context)
|
||||
{
|
||||
config.bind4.setFamily(AF_INET);
|
||||
config.bind4.setPort(port);
|
||||
config.bind6.setFamily(AF_INET6);
|
||||
config.bind6.setPort(port);
|
||||
config.bind4 = {asio::ip::address_v4::any(), port};
|
||||
config.bind6 = {asio::ip::address_v6::any(), port};
|
||||
run(config, std::move(context));
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::run(const char* ip4, const char* ip6, const char* service, Config& config, Context&& context)
|
||||
{
|
||||
auto res4 = SockAddr::resolve(ip4, service);
|
||||
auto res6 = SockAddr::resolve(ip6, service);
|
||||
if (res4.empty())
|
||||
res4.emplace_back();
|
||||
if (res6.empty())
|
||||
res6.emplace_back();
|
||||
config.bind4 = std::move(res4.front());
|
||||
config.bind6 = std::move(res6.front());
|
||||
auto s = asio::ip::udp::resolver::query(service);
|
||||
asio::ip::udp::resolver resolver(*context.ioContext);
|
||||
auto res4 = resolver.resolve(ip4, service);
|
||||
auto res6 = resolver.resolve(ip6, service);
|
||||
if (not res4.empty())
|
||||
config.bind4 = std::move(*res4.begin());
|
||||
if (not res6.empty())
|
||||
config.bind6 = std::move(*res6.begin());
|
||||
run(config, std::move(context));
|
||||
}
|
||||
|
||||
@@ -93,38 +90,32 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
auto expected = State::Idle;
|
||||
if (not running.compare_exchange_strong(expected, State::Running)) {
|
||||
if (context.logger)
|
||||
context.logger->w("[runner %p] Node is already running. Call join() first before calling run() again.", fmt::ptr(this));
|
||||
context.logger->w("[runner %p] Node is already running. Call join() first before calling run() again.", this);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auto local4 = config.bind4;
|
||||
auto local6 = config.bind6;
|
||||
if (not local4 and not local6) {
|
||||
if (context.logger)
|
||||
context.logger->w("[runner %p] No address to bind specified in the configuration, using default addresses", fmt::ptr(this));
|
||||
local4.setFamily(AF_INET);
|
||||
local6.setFamily(AF_INET6);
|
||||
}
|
||||
auto state_path = config.dht_config.node_config.persist_path;
|
||||
if (not state_path.empty())
|
||||
state_path += "_port.txt";
|
||||
if (not state_path.empty() && (local4.getPort() == 0 || local6.getPort() == 0)) {
|
||||
if (not state_path.empty() && (local4.port() == 0 || local6.port() == 0)) {
|
||||
std::ifstream inConfig(state_path);
|
||||
if (inConfig.is_open()) {
|
||||
in_port_t port;
|
||||
if (inConfig >> port) {
|
||||
if (local4.getPort() == 0) {
|
||||
if (local4.port() == 0) {
|
||||
if (context.logger)
|
||||
context.logger->d("[runner %p] Using IPv4 port %hu from saved configuration", fmt::ptr(this), port);
|
||||
local4.setPort(port);
|
||||
context.logger->d("[runner %p] Using IPv4 port %hu from saved configuration", this, port);
|
||||
local4.port(port);
|
||||
}
|
||||
}
|
||||
if (inConfig >> port) {
|
||||
if (local6.getPort() == 0) {
|
||||
if (local6.port() == 0) {
|
||||
if (context.logger)
|
||||
context.logger->d("[runner %p] Using IPv6 port %hu from saved configuration", fmt::ptr(this), port);
|
||||
local6.setPort(port);
|
||||
context.logger->d("[runner %p] Using IPv6 port %hu from saved configuration", this, port);
|
||||
local6.port(port);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,7 +123,7 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
|
||||
if (context.logger) {
|
||||
logger_ = context.logger;
|
||||
logger_->d("[runner %p] state changed to Running", fmt::ptr(this));
|
||||
logger_->d("[runner %p] state changed to Running", this);
|
||||
}
|
||||
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
@@ -140,34 +131,24 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
identityAnnouncedCb_ = context.identityAnnouncedCb;
|
||||
#endif
|
||||
|
||||
if (not context.ioContext)
|
||||
context.ioContext.reset(new asio::io_context);
|
||||
|
||||
ioContext_ = context.ioContext;
|
||||
strand_ = std::make_shared<asio::io_context::strand>(asio::io_context::strand(*ioContext_));
|
||||
|
||||
if (config.proxy_server.empty()) {
|
||||
if (not context.sock) {
|
||||
context.sock.reset(new net::UdpSocket(local4, local6, context.logger));
|
||||
//if (context.logger)
|
||||
// context.logger->d("[runner %p] Creating new socket with local addresses %s and %s", this, local4.toString().c_str(), local6.toString().c_str());
|
||||
context.sock.reset(new net::UdpSocket(strand_, local4, local6/*, context.logger*/));
|
||||
}
|
||||
context.sock->setOnReceive([&] (net::PacketList&& pkts) {
|
||||
net::PacketList ret;
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(sock_mtx);
|
||||
rcv.splice(rcv.end(), std::move(pkts));
|
||||
size_t dropped = 0;
|
||||
while (rcv.size() > net::RX_QUEUE_MAX_SIZE) {
|
||||
rcv.pop_front();
|
||||
dropped++;
|
||||
}
|
||||
if (dropped and logger_) {
|
||||
logger_->w("[runner %p] dropped %zu packets: queue is full!", fmt::ptr(this), dropped);
|
||||
}
|
||||
ret = std::move(rcv_free);
|
||||
}
|
||||
cv.notify_all();
|
||||
return ret;
|
||||
});
|
||||
if (not state_path.empty()) {
|
||||
std::ofstream outConfig(state_path);
|
||||
outConfig << context.sock->getBoundRef(AF_INET).getPort() << std::endl;
|
||||
outConfig << context.sock->getBoundRef(AF_INET6).getPort() << std::endl;
|
||||
outConfig << context.sock->getPort(AF_INET) << std::endl;
|
||||
outConfig << context.sock->getPort(AF_INET6) << std::endl;
|
||||
}
|
||||
auto dht = std::make_unique<Dht>(std::move(context.sock), SecureDht::getConfig(config.dht_config), context.logger, std::move(context.rng));
|
||||
auto dht = std::make_unique<Dht>(strand_, std::move(context.sock), SecureDht::getConfig(config.dht_config), context.logger);
|
||||
dht_ = std::make_unique<SecureDht>(std::move(dht), config.dht_config, std::move(context.identityAnnouncedCb), context.logger);
|
||||
} else {
|
||||
enableProxy(true);
|
||||
@@ -180,45 +161,40 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
throw;
|
||||
}
|
||||
|
||||
statusCbs.clear();
|
||||
if (context.statusChangedCallback)
|
||||
statusCbs.emplace_back(std::move(context.statusChangedCallback));
|
||||
dht_->addOnStateChangeCallback([this](const DhtNodeStatus& status){
|
||||
if (status.get() != NodeStatus::Connecting) {
|
||||
auto pending = std::move(pending_ops);
|
||||
while (not pending.empty()) {
|
||||
pending.front()(*dht_);
|
||||
pending.pop();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (context.statusChangedCallback) {
|
||||
if (logger_)
|
||||
logger_->d("[dhtrunner] starting io_context");
|
||||
dht_->addOnStateChangeCallback(std::move(context.statusChangedCallback));
|
||||
}
|
||||
if (context.certificateStore) {
|
||||
dht_->setLocalCertificateStore(std::move(context.certificateStore));
|
||||
}
|
||||
if (context.publicAddressChangedCb) {
|
||||
dht_->setOnPublicAddressChanged(std::move(context.publicAddressChangedCb));
|
||||
}
|
||||
|
||||
if (not config.threaded)
|
||||
return;
|
||||
dht_thread = std::thread([this]() {
|
||||
while (running != State::Idle) {
|
||||
std::unique_lock<std::mutex> lk(dht_mtx);
|
||||
time_point wakeup = loop_();
|
||||
|
||||
auto hasJobToDo = [this]() {
|
||||
if (running == State::Idle)
|
||||
return true;
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(sock_mtx);
|
||||
if (not rcv.empty())
|
||||
return true;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
if (not pending_ops_prio.empty())
|
||||
return true;
|
||||
auto s = getStatus();
|
||||
if (not pending_ops.empty() and (s == NodeStatus::Connected or s == NodeStatus::Disconnected or running == State::Stopping))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (wakeup == time_point::max())
|
||||
cv.wait(lk, hasJobToDo);
|
||||
else
|
||||
cv.wait_until(lk, wakeup, hasJobToDo);
|
||||
try {
|
||||
if (logger_)
|
||||
logger_->d("[dhtrunner] starting io_context");
|
||||
// Ensures the context won't run out of work
|
||||
auto work = asio::make_work_guard(*ioContext_);
|
||||
ioContext_->run();
|
||||
if (logger_)
|
||||
logger_->d("[dhtrunner] io_context stopped");
|
||||
}
|
||||
catch(const std::exception& ex){
|
||||
if (logger_)
|
||||
logger_->e("[dhtrunner] run error: %s", ex.what());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -237,7 +213,7 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
auto netId = config.dht_config.node_config.network;
|
||||
if (config.peer_discovery) {
|
||||
peerDiscovery_->startDiscovery<NodeInsertionPack>(PEER_DISCOVERY_DHT_SERVICE, [this, netId](NodeInsertionPack&& v, SockAddr&& addr){
|
||||
addr.setPort(v.port);
|
||||
addr.port(v.port);
|
||||
if (v.nodeId != dht_->getNodeId() && netId == v.net){
|
||||
bootstrap(v.nodeId, addr);
|
||||
}
|
||||
@@ -250,62 +226,68 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
adc.nodeId = dht_->getNodeId();
|
||||
if (auto socket = dht_->getSocket()) {
|
||||
// IPv4
|
||||
if (const auto& bound4 = socket->getBoundRef(AF_INET)) {
|
||||
adc.port = bound4.getPort();
|
||||
const auto& bound4 = socket->getBound(AF_INET);
|
||||
if (bound4 != asio::ip::udp::endpoint{}) {
|
||||
adc.port = bound4.port();
|
||||
msgpack::pack(sbuf_node, adc);
|
||||
peerDiscovery_->startPublish(AF_INET, PEER_DISCOVERY_DHT_SERVICE, sbuf_node);
|
||||
}
|
||||
// IPv6
|
||||
if (const auto& bound6 = socket->getBoundRef(AF_INET6)) {
|
||||
adc.port = bound6.getPort();
|
||||
const auto& bound6 = socket->getBound(AF_INET6);
|
||||
if (bound6 != asio::ip::udp::endpoint{}) {
|
||||
adc.port = bound6.port();
|
||||
sbuf_node.clear();
|
||||
msgpack::pack(sbuf_node, adc);
|
||||
peerDiscovery_->startPublish(AF_INET6, PEER_DISCOVERY_DHT_SERVICE, sbuf_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (peerDiscovery_) {
|
||||
statusCbs.emplace_back([this](NodeStatus status4, NodeStatus status6) {
|
||||
if (status4 == NodeStatus::Disconnected && status6 == NodeStatus::Disconnected) {
|
||||
peerDiscovery_->connectivityChanged();
|
||||
}
|
||||
else if (status4 != NodeStatus::Connected || status6 != NodeStatus::Connected) {
|
||||
peerDiscovery_->stopConnectivityChanged();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::shutdown(ShutdownCallback cb, bool stop) {
|
||||
std::unique_lock<std::mutex> lck(storage_mtx);
|
||||
//std::unique_lock<std::mutex> lck(storage_mtx);
|
||||
auto expected = State::Running;
|
||||
if (not running.compare_exchange_strong(expected, State::Stopping)) {
|
||||
if (expected == State::Stopping and ongoing_ops) {
|
||||
if (cb)
|
||||
shutdownCallbacks_.emplace_back(std::move(cb));
|
||||
//asio::post(*ioContext_, [cb] { cb(); });
|
||||
// ioContext_->post([cb] { cb(); });
|
||||
}
|
||||
else if (cb) {
|
||||
lck.unlock();
|
||||
cb();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (logger_)
|
||||
logger_->debug("[runner {:p}] state changed to Stopping, {:d} ongoing ops", fmt::ptr(this), ongoing_ops.load());
|
||||
logger_->d("[runner %p] state changed to Stopping, %zu ongoing ops", this, ongoing_ops.load());
|
||||
ongoing_ops++;
|
||||
shutdownCallbacks_.emplace_back(std::move(cb));
|
||||
pending_ops.emplace([=](SecureDht&) mutable {
|
||||
post([=]() mutable {
|
||||
auto onShutdown = [this]{ opEnded(); };
|
||||
if (dht_)
|
||||
dht_->shutdown(onShutdown, stop);
|
||||
else
|
||||
opEnded();
|
||||
});
|
||||
cv.notify_all();
|
||||
//cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::postOp(std::function<void(SecureDht&)>&& op, bool prio) {
|
||||
ongoing_ops++;
|
||||
/*if (prio)
|
||||
ioContext_->post(asio::bind_executor(*strand_, std::move(op)));
|
||||
else*/
|
||||
ioContext_->post(asio::bind_executor(*strand_, [this, prio, op=std::move(op)]{
|
||||
if (dht_ && (prio || dht_->getStatus() != NodeStatus::Connecting))
|
||||
op(*dht_);
|
||||
else
|
||||
pending_ops.emplace(std::move(op));
|
||||
}));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -334,7 +316,6 @@ bool
|
||||
DhtRunner::checkShutdown() {
|
||||
decltype(shutdownCallbacks_) cbs;
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
if (running != State::Stopping or ongoing_ops)
|
||||
return false;
|
||||
cbs = std::move(shutdownCallbacks_);
|
||||
@@ -356,31 +337,29 @@ DhtRunner::join()
|
||||
if (peerDiscovery_)
|
||||
peerDiscovery_->stop();
|
||||
#endif
|
||||
if (dht_)
|
||||
/*if (dht_)
|
||||
if (auto sock = dht_->getSocket())
|
||||
sock->stop();
|
||||
sock->stop();*/
|
||||
if (logger_)
|
||||
logger_->debug("[runner {:p}] state changed to Idle", fmt::ptr(this));
|
||||
logger_->d("[runner %p] state changed to Idle", this);
|
||||
}
|
||||
|
||||
if (dht_thread.joinable())
|
||||
if (dht_thread.joinable()) {
|
||||
ioContext_->stop();
|
||||
dht_thread.join();
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
if (ongoing_ops and logger_) {
|
||||
logger_->warn("[runner {:p}] stopping with {:d} remaining ops", fmt::ptr(this), ongoing_ops.load());
|
||||
logger_->w("[runner %p] stopping with %zu remaining ops", this, ongoing_ops.load());
|
||||
}
|
||||
pending_ops = decltype(pending_ops)();
|
||||
pending_ops_prio = decltype(pending_ops_prio)();
|
||||
ongoing_ops = 0;
|
||||
shutdownCallbacks_.clear();
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
resetDht();
|
||||
status4 = NodeStatus::Disconnected;
|
||||
status6 = NodeStatus::Disconnected;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,7 +377,7 @@ DhtRunner::getBoundPort(sa_family_t af) const {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
if (dht_)
|
||||
if (auto sock = dht_->getSocket())
|
||||
return sock->getPort(af);
|
||||
return sock->getBound(af).port();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -468,6 +447,12 @@ DhtRunner::setLogger(const Sp<Logger>& logger) {
|
||||
dht_->setLogger(logger);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::setLoggers(LogMethod error, LogMethod warn, LogMethod debug) {
|
||||
Logger logger {std::move(error), std::move(warn), std::move(debug)};
|
||||
setLogger(logger);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::setLogFilter(const InfoHash& f) {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
@@ -520,8 +505,8 @@ DhtRunner::getNodeInfo() const {
|
||||
info.ipv4 = dht_->getNodesStats(AF_INET);
|
||||
info.ipv6 = dht_->getNodesStats(AF_INET6);
|
||||
if (auto sock = dht_->getSocket()) {
|
||||
info.bound4 = sock->getBoundRef(AF_INET).getPort();
|
||||
info.bound6 = sock->getBoundRef(AF_INET6).getPort();
|
||||
info.bound4 = sock->getBound(AF_INET).port();
|
||||
info.bound6 = sock->getBound(AF_INET6).port();
|
||||
}
|
||||
}
|
||||
info.ongoing_ops = ongoing_ops;
|
||||
@@ -533,17 +518,17 @@ DhtRunner::getNodeInfo(std::function<void(std::shared_ptr<NodeInfo>)> cb)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
ongoing_ops++;
|
||||
pending_ops_prio.emplace([cb = std::move(cb), this](SecureDht& dht){
|
||||
post([cb = std::move(cb), this](){
|
||||
auto sinfo = std::make_shared<NodeInfo>();
|
||||
auto& info = *sinfo;
|
||||
info.id = dht.getId();
|
||||
info.node_id = dht.getNodeId();
|
||||
info.ipv4 = dht.getNodesStats(AF_INET);
|
||||
info.ipv6 = dht.getNodesStats(AF_INET6);
|
||||
std::tie(info.storage_size, info.storage_values) = dht.getStoreSize();
|
||||
if (auto sock = dht.getSocket()) {
|
||||
info.bound4 = sock->getBoundRef(AF_INET).getPort();
|
||||
info.bound6 = sock->getBoundRef(AF_INET6).getPort();
|
||||
info.id = dht_->getId();
|
||||
info.node_id = dht_->getNodeId();
|
||||
info.ipv4 = dht_->getNodesStats(AF_INET);
|
||||
info.ipv6 = dht_->getNodesStats(AF_INET6);
|
||||
std::tie(info.storage_size, info.storage_values) = dht_->getStoreSize();
|
||||
if (auto sock = dht_->getSocket()) {
|
||||
info.bound4 = sock->getBound(AF_INET).port();
|
||||
info.bound6 = sock->getBound(AF_INET6).port();
|
||||
}
|
||||
info.ongoing_ops = ongoing_ops;
|
||||
cb(std::move(sinfo));
|
||||
@@ -602,7 +587,12 @@ DhtRunner::getPublicAddressStr(sa_family_t af) const
|
||||
{
|
||||
auto addrs = getPublicAddress(af);
|
||||
std::vector<std::string> ret(addrs.size());
|
||||
std::transform(addrs.begin(), addrs.end(), ret.begin(), [](const SockAddr& a) { return a.toString(); });
|
||||
std::transform(addrs.begin(), addrs.end(), ret.begin(), [](const SockAddr& a) {
|
||||
return asio::ip::detail::endpoint(a.address(), a.port()).to_string();
|
||||
/*std::ostringstream ss;
|
||||
ss << a;
|
||||
return ss.str();*/
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -611,8 +601,8 @@ DhtRunner::getPublicAddress(std::function<void(std::vector<SockAddr>&&)> cb, sa_
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
ongoing_ops++;
|
||||
pending_ops_prio.emplace([cb = std::move(cb), this, af](SecureDht& dht){
|
||||
cb(dht.getPublicAddress(af));
|
||||
post([cb = std::move(cb), this, af](){
|
||||
cb(dht_->getPublicAddress(af));
|
||||
opEnded();
|
||||
});
|
||||
cv.notify_all();
|
||||
@@ -630,7 +620,7 @@ DhtRunner::setLocalCertificateStore(CertificateStoreQuery&& query_method) {
|
||||
if (dht_)
|
||||
dht_->setLocalCertificateStore(std::forward<CertificateStoreQuery>(query_method));
|
||||
}
|
||||
|
||||
/*
|
||||
time_point
|
||||
DhtRunner::loop_()
|
||||
{
|
||||
@@ -641,7 +631,7 @@ DhtRunner::loop_()
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
auto s = getStatus();
|
||||
ops = (pending_ops_prio.empty() && (s == NodeStatus::Connected or s == NodeStatus::Disconnected or running == State::Stopping)) ?
|
||||
ops = (pending_ops_prio.empty() && (s == NodeStatus::Connected or s == NodeStatus::Disconnected)) ?
|
||||
std::move(pending_ops) : std::move(pending_ops_prio);
|
||||
}
|
||||
while (not ops.empty()) {
|
||||
@@ -694,35 +684,36 @@ DhtRunner::loop_()
|
||||
}
|
||||
|
||||
if (dropped && logger_)
|
||||
logger_->e("[runner %p] Dropped %zu packets with high delay.", fmt::ptr(this), dropped);
|
||||
logger_->e("[runner %p] Dropped %zu packets with high delay.", this, dropped);
|
||||
|
||||
NodeStatus nstatus4 = dht_->updateStatus(AF_INET);
|
||||
NodeStatus nstatus6 = dht_->updateStatus(AF_INET6);
|
||||
if (nstatus4 != status4 || nstatus6 != status6) {
|
||||
status4 = nstatus4;
|
||||
status6 = nstatus6;
|
||||
for (auto& cb : statusCbs){
|
||||
cb(status4, status6);
|
||||
}
|
||||
if (statusCb)
|
||||
statusCb(status4, status6);
|
||||
}
|
||||
|
||||
return wakeup;
|
||||
}
|
||||
}*/
|
||||
|
||||
void
|
||||
DhtRunner::get(InfoHash hash, GetCallback vcb, DoneCallback dcb, Value::Filter f, Where w)
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(storage_mtx);
|
||||
if (running != State::Running) {
|
||||
lck.unlock();
|
||||
if (dcb) dcb(false, {});
|
||||
return;
|
||||
}
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([=](SecureDht& dht) mutable {
|
||||
dht.get(hash, std::move(vcb), bindOpDoneCallback(std::move(dcb)), std::move(f), std::move(w));
|
||||
postOp([
|
||||
hash,
|
||||
vcb = std::move(vcb),
|
||||
dcb=bindOpDoneCallback(std::move(dcb)),
|
||||
f = std::move(f),
|
||||
w = std::move(w)
|
||||
](SecureDht& dht) mutable {
|
||||
dht.get(hash, std::move(vcb), std::move(dcb), std::move(f), std::move(w));
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -738,11 +729,9 @@ DhtRunner::query(const InfoHash& hash, QueryCallback cb, DoneCallback done_cb, Q
|
||||
if (done_cb) done_cb(false, {});
|
||||
return;
|
||||
}
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([=](SecureDht& dht) mutable {
|
||||
postOp([=](SecureDht& dht) mutable {
|
||||
dht.query(hash, std::move(cb), bindOpDoneCallback(std::move(done_cb)), std::move(q));
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
std::future<size_t>
|
||||
@@ -755,8 +744,8 @@ DhtRunner::listen(InfoHash hash, ValueCallback vcb, Value::Filter f, Where w)
|
||||
ret_token->set_value(0);
|
||||
return ret_token->get_future();
|
||||
}
|
||||
pending_ops.emplace([=](SecureDht& dht) mutable {
|
||||
ret_token->set_value(dht.listen(hash, std::move(vcb), std::move(f), std::move(w)));
|
||||
post([=]() mutable {
|
||||
ret_token->set_value(dht_->listen(hash, std::move(vcb), std::move(f), std::move(w)));
|
||||
});
|
||||
cv.notify_all();
|
||||
return ret_token->get_future();
|
||||
@@ -774,26 +763,21 @@ DhtRunner::cancelListen(InfoHash h, size_t token)
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
if (running != State::Running)
|
||||
return;
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
postOp([=](SecureDht& dht) {
|
||||
dht.cancelListen(h, token);
|
||||
opEnded();
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::cancelListen(InfoHash h, std::shared_future<size_t> ftoken)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
if (running != State::Running)
|
||||
return;
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([this, h, ftoken = std::move(ftoken)](SecureDht& dht) {
|
||||
postOp([this, h, ftoken = std::move(ftoken)](SecureDht& dht) {
|
||||
dht.cancelListen(h, ftoken.get());
|
||||
opEnded();
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -805,14 +789,12 @@ DhtRunner::put(InfoHash hash, Value&& value, DoneCallback cb, time_point created
|
||||
if (cb) cb(false, {});
|
||||
return;
|
||||
}
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([=,
|
||||
postOp([=,
|
||||
cb = std::move(cb),
|
||||
sv = std::make_shared<Value>(std::move(value))
|
||||
] (SecureDht& dht) mutable {
|
||||
dht.put(hash, sv, bindOpDoneCallback(std::move(cb)), created, permanent);
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -824,11 +806,9 @@ DhtRunner::put(InfoHash hash, std::shared_ptr<Value> value, DoneCallback cb, tim
|
||||
if (cb) cb(false, {});
|
||||
return;
|
||||
}
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([=, value = std::move(value), cb = std::move(cb)](SecureDht& dht) mutable {
|
||||
postOp([=, value = std::move(value), cb = std::move(cb)](SecureDht& dht) mutable {
|
||||
dht.put(hash, value, bindOpDoneCallback(std::move(cb)), created, permanent);
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -841,20 +821,18 @@ void
|
||||
DhtRunner::cancelPut(const InfoHash& h, Value::Id id)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.cancelPut(h, id);
|
||||
post([=]() {
|
||||
dht_->cancelPut(h, id);
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::cancelPut(const InfoHash& h, const std::shared_ptr<Value>& value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.cancelPut(h, value->id);
|
||||
post([=]() {
|
||||
dht_->cancelPut(h, value->id);
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -866,14 +844,12 @@ DhtRunner::putSigned(InfoHash hash, std::shared_ptr<Value> value, DoneCallback c
|
||||
if (cb) cb(false, {});
|
||||
return;
|
||||
}
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([=,
|
||||
postOp([=,
|
||||
cb = std::move(cb),
|
||||
value = std::move(value)
|
||||
](SecureDht& dht) mutable {
|
||||
dht.putSigned(hash, value, bindOpDoneCallback(std::move(cb)), permanent);
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -897,14 +873,12 @@ DhtRunner::putEncrypted(InfoHash hash, InfoHash to, std::shared_ptr<Value> value
|
||||
if (cb) cb(false, {});
|
||||
return;
|
||||
}
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([=,
|
||||
postOp([=,
|
||||
cb = std::move(cb),
|
||||
value = std::move(value)
|
||||
] (SecureDht& dht) mutable {
|
||||
dht.putEncrypted(hash, to, value, bindOpDoneCallback(std::move(cb)), permanent);
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -929,7 +903,7 @@ DhtRunner::putEncrypted(InfoHash hash, const std::shared_ptr<crypto::PublicKey>&
|
||||
return;
|
||||
}
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([=,
|
||||
postOp([=,
|
||||
cb = std::move(cb),
|
||||
value = std::move(value)
|
||||
] (SecureDht& dht) mutable {
|
||||
@@ -948,8 +922,8 @@ void
|
||||
DhtRunner::bootstrap(const std::string& host, const std::string& service)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([host, service] (SecureDht& dht) mutable {
|
||||
dht.addBootstrap(host, service);
|
||||
post([this, host, service] () mutable {
|
||||
dht_->addBootstrap(host, service);
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
@@ -958,8 +932,8 @@ void
|
||||
DhtRunner::bootstrap(const std::string& hostService)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([host_service = splitPort(hostService)] (SecureDht& dht) mutable {
|
||||
dht.addBootstrap(host_service.first, host_service.second);
|
||||
post([this, host_service = splitPort(hostService)] () mutable {
|
||||
dht_->addBootstrap(host_service.first, host_service.second);
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
@@ -968,8 +942,8 @@ void
|
||||
DhtRunner::clearBootstrap()
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([] (SecureDht& dht) mutable {
|
||||
dht.clearBootstrap();
|
||||
post([this] () mutable {
|
||||
dht_->clearBootstrap();
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
@@ -983,15 +957,15 @@ DhtRunner::bootstrap(std::vector<SockAddr> nodes, DoneCallbackSimple cb)
|
||||
}
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
ongoing_ops++;
|
||||
pending_ops_prio.emplace([
|
||||
post([this,
|
||||
cb = bindOpDoneCallback(std::move(cb)),
|
||||
nodes = std::move(nodes)
|
||||
] (SecureDht& dht) mutable {
|
||||
] () mutable {
|
||||
auto rem = cb ? std::make_shared<std::pair<size_t, bool>>(nodes.size(), false) : nullptr;
|
||||
for (auto& node : nodes) {
|
||||
if (node.getPort() == 0)
|
||||
node.setPort(net::DHT_DEFAULT_PORT);
|
||||
dht.pingNode(std::move(node), [rem,cb](bool ok) {
|
||||
if (node.port() == 0)
|
||||
node.port(net::DHT_DEFAULT_PORT);
|
||||
dht_->pingNode(std::move(node), [rem,cb](bool ok) {
|
||||
auto& r = *rem;
|
||||
r.first--;
|
||||
r.second |= ok;
|
||||
@@ -1014,8 +988,10 @@ DhtRunner::bootstrap(SockAddr addr, DoneCallbackSimple cb)
|
||||
return;
|
||||
}
|
||||
ongoing_ops++;
|
||||
pending_ops_prio.emplace([addr = std::move(addr), cb = bindOpDoneCallback(std::move(cb))](SecureDht& dht) mutable {
|
||||
dht.pingNode(std::move(addr), std::move(cb));
|
||||
if (addr.address().is_unspecified())
|
||||
addr.address(addr.address().is_v4() ? asio::ip::address(asio::ip::address_v4::loopback()) : asio::ip::address(asio::ip::address_v6::loopback()));
|
||||
post([this, addr = std::move(addr), cb = bindOpDoneCallback(std::move(cb))]() mutable {
|
||||
dht_->pingNode(std::move(addr), std::move(cb));
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
@@ -1026,8 +1002,8 @@ DhtRunner::bootstrap(const InfoHash& id, const SockAddr& address)
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
if (running != State::Running)
|
||||
return;
|
||||
pending_ops_prio.emplace([id, address](SecureDht& dht) mutable {
|
||||
dht.insertNode(id, address);
|
||||
post([this, id, address]() mutable {
|
||||
dht_->insertNode(id, address);
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
@@ -1038,9 +1014,9 @@ DhtRunner::bootstrap(std::vector<NodeExport> nodes)
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
if (running != State::Running)
|
||||
return;
|
||||
pending_ops_prio.emplace([nodes = std::move(nodes)](SecureDht& dht) {
|
||||
post([this, nodes = std::move(nodes)]() {
|
||||
for (auto& node : nodes)
|
||||
dht.insertNode(node);
|
||||
dht_->insertNode(node);
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
@@ -1049,8 +1025,8 @@ void
|
||||
DhtRunner::connectivityChanged()
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht& dht) {
|
||||
dht.connectivityChanged();
|
||||
post([=]() {
|
||||
dht_->connectivityChanged();
|
||||
#ifdef OPENDHT_PEER_DISCOVERY
|
||||
if (peerDiscovery_)
|
||||
peerDiscovery_->connectivityChanged();
|
||||
@@ -1067,14 +1043,12 @@ DhtRunner::findCertificate(InfoHash hash, std::function<void(const Sp<crypto::Ce
|
||||
cb({});
|
||||
return;
|
||||
}
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([this, hash, cb = std::move(cb)] (SecureDht& dht) {
|
||||
postOp([this, hash, cb = std::move(cb)] (SecureDht& dht) {
|
||||
dht.findCertificate(hash, [this, cb = std::move(cb)](const Sp<crypto::Certificate>& crt){
|
||||
cb(crt);
|
||||
opEnded();
|
||||
});
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1113,20 +1087,14 @@ DhtRunner::enableProxy(bool proxify)
|
||||
auto dht_via_proxy = std::make_unique<DhtProxyClient>(
|
||||
config_.server_ca,
|
||||
config_.client_identity,
|
||||
[this]{
|
||||
if (config_.threaded) {
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht&) mutable {});
|
||||
cv.notify_all();
|
||||
}
|
||||
},
|
||||
config_.proxy_server,
|
||||
config_.proxy_user_agent,
|
||||
config_.push_node_id,
|
||||
config_.push_token,
|
||||
config_.push_topic,
|
||||
config_.push_platform,
|
||||
logger_);
|
||||
strand_,
|
||||
config_.proxy_server, config_.push_node_id, logger_);
|
||||
if (not config_.push_token.empty())
|
||||
dht_via_proxy->setPushNotificationToken(config_.push_token);
|
||||
if (not config_.push_topic.empty())
|
||||
dht_via_proxy->setPushNotificationTopic(config_.push_topic);
|
||||
if (not config_.push_platform.empty())
|
||||
dht_via_proxy->setPushNotificationPlatform(config_.push_platform);
|
||||
dht_ = std::make_unique<SecureDht>(std::move(dht_via_proxy), config_.dht_config, identityAnnouncedCb_, logger_);
|
||||
// and use it
|
||||
use_proxy = proxify;
|
||||
@@ -1191,7 +1159,7 @@ DhtRunner::pushNotificationReceived(const std::map<std::string, std::string>& da
|
||||
{
|
||||
#if defined(OPENDHT_PROXY_CLIENT) && defined(OPENDHT_PUSH_NOTIFICATIONS)
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht&) {
|
||||
post([=]() {
|
||||
if (dht_)
|
||||
dht_->pushNotificationReceived(data);
|
||||
});
|
||||
|
||||
+179
-194
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Vsevolod Ivanov <vsevolod.ivanov@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,14 +17,14 @@
|
||||
*/
|
||||
|
||||
#include "http.h"
|
||||
#include "logger.h"
|
||||
#include "log_enable.h"
|
||||
#include "crypto.h"
|
||||
#include "base64.h"
|
||||
#include "compat/os_cert.h"
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <restinio/impl/tls_socket.hpp>
|
||||
#include <llhttp.h>
|
||||
#include <http_parser.h>
|
||||
#include <json/json.h>
|
||||
|
||||
#include <openssl/ocsp.h>
|
||||
@@ -33,10 +33,6 @@
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/x509_vfy.h>
|
||||
|
||||
#include <cctype>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#define MAXAGE_SEC (14*24*60*60)
|
||||
#define JITTER_SEC (60)
|
||||
#define OCSP_MAX_RESPONSE_SIZE (20480)
|
||||
@@ -115,11 +111,11 @@ newTlsClientContext(const std::shared_ptr<dht::Logger>& logger)
|
||||
|
||||
if (char* path = getenv("CA_ROOT_FILE")) {
|
||||
if (logger)
|
||||
logger->debug("Using CA file: {}", path);
|
||||
logger->d("Using CA file: %s", path);
|
||||
ctx->load_verify_file(path);
|
||||
} else if (char* path = getenv("CA_ROOT_PATH")) {
|
||||
if (logger)
|
||||
logger->debug("Using CA path: {}", path);
|
||||
logger->d("Using CA path: %s", path);
|
||||
ctx->add_verify_path(path);
|
||||
} else {
|
||||
#ifdef __ANDROID__
|
||||
@@ -144,12 +140,12 @@ Connection::Connection(asio::io_context& ctx, const bool ssl, std::shared_ptr<dh
|
||||
ssl_ctx_ = newTlsClientContext(l);
|
||||
ssl_socket_ = std::make_unique<ssl_socket_t>(ctx_, ssl_ctx_);
|
||||
if (logger_)
|
||||
logger_->debug("[connection:{:d}] start https session with system CA", id_);
|
||||
logger_->d("[connection:%i] start https session with system CA", id_);
|
||||
}
|
||||
else {
|
||||
socket_ = std::make_unique<socket_t>(ctx);
|
||||
if (logger_)
|
||||
logger_->debug("[connection:{:d}] start http session", id_);
|
||||
logger_->d("[connection:%i] start http session", id_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,11 +162,11 @@ Connection::Connection(asio::io_context& ctx, std::shared_ptr<dht::crypto::Certi
|
||||
if (ec)
|
||||
throw std::runtime_error("Error adding certificate authority: " + ec.message());
|
||||
else if (logger_)
|
||||
logger_->debug("[connection:{:d}] start https with custom CA {:s}", id_, server_ca->getUID());
|
||||
logger_->d("[connection:%i] start https with custom CA %s", id_, server_ca->getUID().c_str());
|
||||
} else {
|
||||
ssl_ctx_ = newTlsClientContext(l);
|
||||
if (logger_)
|
||||
logger_->debug("[connection:{:d}] start https session with system CA", id_);
|
||||
logger_->d("[connection:%i] start https session with system CA", id_);
|
||||
}
|
||||
if (identity.first){
|
||||
auto key = identity.first->serialize();
|
||||
@@ -185,7 +181,7 @@ Connection::Connection(asio::io_context& ctx, std::shared_ptr<dht::crypto::Certi
|
||||
if (ec)
|
||||
throw std::runtime_error("Error adding client certificate: " + ec.message());
|
||||
else if (logger_)
|
||||
logger_->debug("[connection:{:d}] client certificate {:s}", id_, identity.second->getUID());
|
||||
logger_->d("[connection:%i] client certificate %s", id_, identity.second->getUID().c_str());
|
||||
}
|
||||
ssl_socket_ = std::make_unique<ssl_socket_t>(ctx_, ssl_ctx_);
|
||||
}
|
||||
@@ -208,7 +204,7 @@ Connection::close()
|
||||
socket_->close(ec);
|
||||
}
|
||||
if (ec and logger_)
|
||||
logger_->error("[connection:{:d}] error closing: {:s}", id_, ec.message());
|
||||
logger_->e("[connection:%i] error closing: %s", id_, ec.message().c_str());
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -225,25 +221,20 @@ Connection::is_ssl() const
|
||||
return ssl_ctx_ ? true : false;
|
||||
}
|
||||
|
||||
std::string asn1ToString(ASN1_GENERALIZEDTIME* time) {
|
||||
if (!time) {
|
||||
return "(null)";
|
||||
}
|
||||
BIO* memBio = BIO_new(BIO_s_mem());
|
||||
if (!memBio) {
|
||||
throw std::runtime_error("Failed to create BIO");
|
||||
}
|
||||
static time_t
|
||||
parse_ocsp_time(ASN1_GENERALIZEDTIME* gt)
|
||||
{
|
||||
struct tm tm;
|
||||
time_t rv = -1;
|
||||
|
||||
if (ASN1_GENERALIZEDTIME_print(memBio, time) <= 0) {
|
||||
BIO_free(memBio);
|
||||
throw std::runtime_error("Failed to print ASN1_GENERALIZEDTIME");
|
||||
}
|
||||
|
||||
char* bioData;
|
||||
long bioLength = BIO_get_mem_data(memBio, &bioData);
|
||||
std::string result(bioData, bioLength);
|
||||
BIO_free(memBio);
|
||||
return result;
|
||||
if (gt == nullptr)
|
||||
return -1;
|
||||
// RFC 6960 specifies that all times in OCSP must be GENERALIZEDTIME
|
||||
if (ASN1_time_parse((const char*)gt->data, gt->length, &tm, V_ASN1_GENERALIZEDTIME) == -1)
|
||||
return -1;
|
||||
if ((rv = timegm(&tm)) == -1)
|
||||
return -1;
|
||||
return rv;
|
||||
}
|
||||
|
||||
static inline X509*
|
||||
@@ -281,26 +272,26 @@ ocspRequestFromCert(STACK_OF(X509)* fullchain, const std::shared_ptr<Logger>& lo
|
||||
|
||||
if (sk_X509_num(fullchain) <= 1) {
|
||||
if (logger)
|
||||
logger->error("Cert does not contain a cert chain");
|
||||
logger->e("Cert does not contain a cert chain");
|
||||
return {};
|
||||
}
|
||||
X509* cert = cert_from_chain(fullchain);
|
||||
if (cert == nullptr) {
|
||||
if (logger)
|
||||
logger->error("No certificate found");
|
||||
logger->e("No certificate found");
|
||||
return {};
|
||||
}
|
||||
X509* issuer = issuer_from_chain(fullchain);
|
||||
if (issuer == nullptr) {
|
||||
if (logger)
|
||||
logger->error("Unable to find issuer for cert");
|
||||
logger->e("Unable to find issuer for cert");
|
||||
return {};
|
||||
}
|
||||
|
||||
auto urls = X509_get1_ocsp(cert);
|
||||
if (urls == nullptr || sk_OPENSSL_STRING_num(urls) <= 0) {
|
||||
if (logger)
|
||||
logger->error("Certificate contains no OCSP url");
|
||||
logger->e("Certificate contains no OCSP url");
|
||||
return {};
|
||||
}
|
||||
auto url = sk_OPENSSL_STRING_value(urls, 0);
|
||||
@@ -315,12 +306,12 @@ ocspRequestFromCert(STACK_OF(X509)* fullchain, const std::shared_ptr<Logger>& lo
|
||||
OCSP_CERTID* id = OCSP_cert_to_id(EVP_sha1(), cert, issuer);
|
||||
if (id == nullptr) {
|
||||
if (logger)
|
||||
logger->error("Unable to get certificate id from cert");
|
||||
logger->e("Unable to get certificate id from cert");
|
||||
return {};
|
||||
}
|
||||
if (OCSP_request_add0_id(request->req.get(), id) == nullptr) {
|
||||
if (logger)
|
||||
logger->error("Unable to add certificate id to request");
|
||||
logger->e("Unable to add certificate id to request");
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -331,12 +322,12 @@ ocspRequestFromCert(STACK_OF(X509)* fullchain, const std::shared_ptr<Logger>& lo
|
||||
uint8_t* data {nullptr};
|
||||
if ((size = i2d_OCSP_REQUEST(request->req.get(), &data)) <= 0) {
|
||||
if (logger)
|
||||
logger->error("Unable to encode ocsp request");
|
||||
logger->e("Unable to encode ocsp request");
|
||||
return {};
|
||||
}
|
||||
if (data == nullptr) {
|
||||
if (logger)
|
||||
logger->error("Unable to allocte memory");
|
||||
logger->e("Unable to allocte memory");
|
||||
return {};
|
||||
}
|
||||
request->data = std::string((char*)data, (char*)data+size);
|
||||
@@ -347,32 +338,36 @@ ocspRequestFromCert(STACK_OF(X509)* fullchain, const std::shared_ptr<Logger>& lo
|
||||
bool
|
||||
ocspValidateResponse(const OscpRequestInfo& info, STACK_OF(X509)* fullchain, const std::string& response, X509_STORE *store, const std::shared_ptr<Logger>& logger)
|
||||
{
|
||||
ASN1_GENERALIZEDTIME *revtime = nullptr, *thisupd = nullptr, *nextupd = nullptr;
|
||||
const uint8_t* p = (const uint8_t*)response.data();
|
||||
int status, cert_status=0, crl_reason=0;
|
||||
time_t now, rev_t = -1, this_t, next_t;
|
||||
|
||||
X509* cert = cert_from_chain(fullchain);
|
||||
if (cert == nullptr) {
|
||||
if (logger)
|
||||
logger->error("ocsp: no certificate found");
|
||||
logger->e("No certificate found");
|
||||
return false;
|
||||
}
|
||||
X509* issuer = issuer_from_chain(fullchain);
|
||||
if (issuer == nullptr) {
|
||||
if (logger)
|
||||
logger->error("ocsp: unable to find issuer for cert");
|
||||
logger->e("Unable to find issuer for cert");
|
||||
return false;
|
||||
}
|
||||
|
||||
OCSP_CERTID *cidr;
|
||||
if ((cidr = OCSP_cert_to_id(nullptr, cert, issuer)) == nullptr) {
|
||||
if (logger)
|
||||
logger->error("ocsp: unable to get issuer cert/CID");
|
||||
logger->e("Unable to get issuer cert/CID");
|
||||
return false;
|
||||
}
|
||||
std::unique_ptr<OCSP_CERTID, decltype(&OCSP_CERTID_free)> cid(cidr, &OCSP_CERTID_free);
|
||||
|
||||
const uint8_t* resp_data = (const uint8_t*)response.data();
|
||||
OCSP_RESPONSE *r;
|
||||
if ((r = d2i_OCSP_RESPONSE(nullptr, &resp_data, response.size())) == nullptr) {
|
||||
if ((r = d2i_OCSP_RESPONSE(nullptr, &p, response.size())) == nullptr) {
|
||||
if (logger)
|
||||
logger->error("OCSP response unserializable");
|
||||
logger->e("OCSP response unserializable");
|
||||
return false;
|
||||
}
|
||||
std::unique_ptr<OCSP_RESPONSE, decltype(&OCSP_RESPONSE_free)> resp(r, &OCSP_RESPONSE_free);
|
||||
@@ -380,59 +375,99 @@ ocspValidateResponse(const OscpRequestInfo& info, STACK_OF(X509)* fullchain, con
|
||||
OCSP_BASICRESP *brespr;
|
||||
if ((brespr = OCSP_response_get1_basic(resp.get())) == nullptr) {
|
||||
if (logger)
|
||||
logger->error("Failed to load OCSP response");
|
||||
logger->e("Failed to load OCSP response");
|
||||
return false;
|
||||
}
|
||||
std::unique_ptr<OCSP_BASICRESP, decltype(&OCSP_BASICRESP_free)> bresp(brespr, &OCSP_BASICRESP_free);
|
||||
|
||||
if (OCSP_basic_verify(bresp.get(), fullchain, store, OCSP_TRUSTOTHER) != 1) {
|
||||
if (logger)
|
||||
logger->warn("OCSP verify failed");
|
||||
logger->w("OCSP verify failed");
|
||||
return false;
|
||||
}
|
||||
printf("OCSP response signature validated\n");
|
||||
|
||||
int status = OCSP_response_status(resp.get());
|
||||
status = OCSP_response_status(resp.get());
|
||||
if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
|
||||
if (logger)
|
||||
logger->warn("OCSP Failure: code {:d} ({:s})", status, OCSP_response_status_str(status));
|
||||
logger->w("OCSP Failure: code %d (%s)", status, OCSP_response_status_str(status));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the nonce if we sent one
|
||||
if (OCSP_check_nonce(info.req.get(), bresp.get()) <= 0) {
|
||||
if (logger)
|
||||
logger->warn("No OCSP nonce, or mismatch");
|
||||
logger->w("No OCSP nonce, or mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
ASN1_GENERALIZEDTIME *revtime = nullptr, *thisupd = nullptr, *nextupd = nullptr;
|
||||
int cert_status=0, crl_reason=0;
|
||||
if (OCSP_resp_find_status(bresp.get(), cid.get(), &cert_status, &crl_reason,
|
||||
&revtime, &thisupd, &nextupd) != 1) {
|
||||
if (logger)
|
||||
logger->warn("OCSP verify failed: no result for cert");
|
||||
logger->w("OCSP verify failed: no result for cert");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (revtime && (rev_t = parse_ocsp_time(revtime)) == -1) {
|
||||
if (logger)
|
||||
logger->w("Unable to parse revocation time in OCSP reply");
|
||||
return false;
|
||||
}
|
||||
// Belt and suspenders, Treat it as revoked if there is either
|
||||
// a revocation time, or status revoked.
|
||||
if (revtime || cert_status == V_OCSP_CERTSTATUS_REVOKED) {
|
||||
if (logger) {
|
||||
logger->warn("OCSP verify failed: certificate revoked since {}", asn1ToString(revtime));
|
||||
if (rev_t != -1 || cert_status == V_OCSP_CERTSTATUS_REVOKED) {
|
||||
if (logger)
|
||||
logger->w("Invalid OCSP reply: certificate is revoked");
|
||||
if (rev_t != -1) {
|
||||
if (logger)
|
||||
logger->w("Certificate revoked at: %s", ctime(&rev_t));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (OCSP_check_validity(thisupd, nextupd, 1, -1) == 0) {
|
||||
if ((this_t = parse_ocsp_time(thisupd)) == -1) {
|
||||
if (logger)
|
||||
logger->warn("OCSP reply is expired or not yet valid");
|
||||
logger->w("unable to parse this update time in OCSP reply");
|
||||
return false;
|
||||
}
|
||||
if ((next_t = parse_ocsp_time(nextupd)) == -1) {
|
||||
if (logger)
|
||||
logger->w("unable to parse next update time in OCSP reply");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't allow this update to precede next update
|
||||
if (this_t >= next_t) {
|
||||
if (logger)
|
||||
logger->w("Invalid OCSP reply: this update >= next update");
|
||||
return false;
|
||||
}
|
||||
|
||||
now = time(nullptr);
|
||||
// Check that this update is not more than JITTER seconds in the future.
|
||||
if (this_t > now + JITTER_SEC) {
|
||||
if (logger)
|
||||
logger->e("Invalid OCSP reply: this update is in the future (%s)", ctime(&this_t));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that this update is not more than MAXSEC in the past.
|
||||
if (this_t < now - MAXAGE_SEC) {
|
||||
if (logger)
|
||||
logger->e("Invalid OCSP reply: this update is too old (%s)", ctime(&this_t));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that next update is still valid
|
||||
if (next_t < now - JITTER_SEC) {
|
||||
if (logger)
|
||||
logger->w("Invalid OCSP reply: reply has expired (%s)", ctime(&next_t));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (logger) {
|
||||
logger->debug("OCSP response validated");
|
||||
logger->debug(" This Update: {}", asn1ToString(thisupd));
|
||||
logger->debug(" Next Update: {}", asn1ToString(nextupd));
|
||||
logger->d("OCSP response validated");
|
||||
logger->d(" This Update: %s", ctime(&this_t));
|
||||
logger->d(" Next Update: %s", ctime(&next_t));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -453,7 +488,7 @@ Connection::set_ssl_verification(const std::string& hostname, const asio::ssl::v
|
||||
if (logger) {
|
||||
char subject_name[1024];
|
||||
X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 1024);
|
||||
logger->debug("[connection:{:d}] verify {:s} compliance to RFC 2818:\n{:s}", id, hostname, subject_name);
|
||||
logger->d("[connection:%i] verify %s compliance to RFC 2818:\n%s", id, hostname.c_str(), subject_name);
|
||||
}
|
||||
|
||||
// starts from CA and goes down the presented chain
|
||||
@@ -461,14 +496,14 @@ Connection::set_ssl_verification(const std::string& hostname, const asio::ssl::v
|
||||
bool verified = verifier(preverified, ctx);
|
||||
auto verify_ec = X509_STORE_CTX_get_error(ctx.native_handle());
|
||||
if (verify_ec != 0 /*X509_V_OK*/ and logger)
|
||||
logger->error("[http::connection:{:d}] ssl verification error={:d} {}", id, verify_ec, verified);
|
||||
logger->e("[http::connection:%i] ssl verification error=%i %d", id, verify_ec, verified);
|
||||
if (verified and checkOcsp) {
|
||||
std::unique_ptr<stack_st_X509, void(*)(stack_st_X509*)> chain(
|
||||
X509_STORE_CTX_get1_chain(ctx.native_handle()),
|
||||
[](stack_st_X509* c){ sk_X509_pop_free(c, X509_free); });
|
||||
if (auto ocspInfo = ocspRequestFromCert(chain.get(), logger)) {
|
||||
if (logger)
|
||||
logger->warn("[http::connection:{:d}] TLS OCSP server: {:s}, request size: {:d}", id, ocspInfo->url, ocspInfo->data.size());
|
||||
logger->w("[http::connection:%i] TLS OCSP server: %s, request size: %zu", id, ocspInfo->url.c_str(), ocspInfo->data.size());
|
||||
bool ocspVerified = false;
|
||||
asio::io_context io_ctx;
|
||||
auto ocspReq = std::make_shared<Request>(io_ctx, ocspInfo->url, [&](const Response& ocspResp){
|
||||
@@ -476,7 +511,7 @@ Connection::set_ssl_verification(const std::string& hostname, const asio::ssl::v
|
||||
ocspVerified = ocspValidateResponse(*ocspInfo, chain.get(), ocspResp.body, X509_STORE_CTX_get0_store(ctx.native_handle()), logger);
|
||||
} else {
|
||||
if (logger)
|
||||
logger->warn("[http::connection:{:d}] TLS OCSP check error", id);
|
||||
logger->w("[http::connection:%i] TLS OCSP check error", id);
|
||||
}
|
||||
}, logger);
|
||||
ocspReq->set_method(restinio::http_method_post());
|
||||
@@ -536,11 +571,48 @@ Connection::async_connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, Conn
|
||||
#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) {
|
||||
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()
|
||||
this->set_keepalive(30);
|
||||
uint32_t start = 30;
|
||||
uint32_t interval = 30;
|
||||
uint32_t cnt = 1;
|
||||
#ifdef _WIN32
|
||||
std::string val = "1";
|
||||
setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, val.c_str(), sizeof(val));
|
||||
|
||||
// TCP_KEEPIDLE and TCP_KEEPINTVL are available since Win 10 version 1709
|
||||
// TCP_KEEPCNT since Win 10 version 1703
|
||||
#ifdef TCP_KEEPIDLE
|
||||
std::string start_str = std::to_string(start);
|
||||
setsockopt(socket, IPPROTO_TCP, TCP_KEEPIDLE,
|
||||
start_str.c_str(), sizeof(start_str));
|
||||
#endif
|
||||
#ifdef TCP_KEEPINTVL
|
||||
std::string interval_str = std::to_string(interval);
|
||||
setsockopt(socket, IPPROTO_TCP, TCP_KEEPINTVL,
|
||||
interval_str.c_str(), sizeof(interval_str));
|
||||
#endif
|
||||
#ifdef TCP_KEEPCNT
|
||||
std::string cnt_str = std::to_string(cnt);
|
||||
setsockopt(socket, IPPROTO_TCP, TCP_KEEPCNT,
|
||||
cnt_str.c_str(), sizeof(cnt_str));
|
||||
#endif
|
||||
#else
|
||||
uint32_t val = 1;
|
||||
setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(uint32_t));
|
||||
#ifdef __APPLE__
|
||||
// Apple devices only have one parameter
|
||||
setsockopt(socket, IPPROTO_TCP, TCP_KEEPALIVE, &start, sizeof(uint32_t));
|
||||
#else
|
||||
// Linux based systems
|
||||
setsockopt(socket, SOL_TCP, TCP_KEEPIDLE, &start, sizeof(uint32_t));
|
||||
setsockopt(socket, SOL_TCP, TCP_KEEPINTVL, &interval, sizeof(uint32_t));
|
||||
setsockopt(socket, SOL_TCP, TCP_KEEPCNT, &cnt, sizeof(uint32_t));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
if (cb)
|
||||
cb(ec, endpoint);
|
||||
@@ -570,11 +642,11 @@ Connection::async_handshake(HandlerCb cb)
|
||||
if (this_.logger_) {
|
||||
if (verify_ec == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT /*18*/
|
||||
|| verify_ec == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN /*19*/)
|
||||
this_.logger_->debug("[connection:{:d}] self-signed certificate in handshake: {:d}", this_.id_, verify_ec);
|
||||
this_.logger_->d("[connection:%i] self-signed certificate in handshake: %i", this_.id_, verify_ec);
|
||||
else if (verify_ec != X509_V_OK)
|
||||
this_.logger_->error("[connection:{:d}] verify handshake error: {:d}", this_.id_, verify_ec);
|
||||
this_.logger_->e("[connection:%i] verify handshake error: %i", this_.id_, verify_ec);
|
||||
else
|
||||
this_.logger_->warn("[connection:{:d}] verify handshake success", this_.id_);
|
||||
this_.logger_->w("[connection:%i] verify handshake success", this_.id_);
|
||||
}
|
||||
}
|
||||
if (cb)
|
||||
@@ -656,64 +728,6 @@ Connection::async_read_some(size_t bytes, BytesHandlerCb cb)
|
||||
else socket_->async_read_some(buf, onEnd);
|
||||
}
|
||||
|
||||
void
|
||||
Connection::set_keepalive(uint32_t seconds)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (!ssl_socket_ && !socket_) return;
|
||||
|
||||
auto& base = ssl_socket_? ssl_socket_->lowest_layer() : *socket_;
|
||||
auto socket = base.native_handle();
|
||||
|
||||
uint32_t interval = 1;
|
||||
uint32_t cnt = 10;
|
||||
#ifdef _WIN32
|
||||
// TCP_KEEPIDLE and TCP_KEEPINTVL are available since Win 10 version 1709
|
||||
// TCP_KEEPCNT since Win 10 version 1703
|
||||
#if defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) && defined(TCP_KEEPCNT)
|
||||
std::string val = "1";
|
||||
setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, val.c_str(), sizeof(val));
|
||||
std::string seconds_str = std::to_string(seconds);
|
||||
setsockopt(socket, IPPROTO_TCP, TCP_KEEPIDLE,
|
||||
seconds_str.c_str(), sizeof(seconds_str));
|
||||
std::string interval_str = std::to_string(interval);
|
||||
setsockopt(socket, IPPROTO_TCP, TCP_KEEPINTVL,
|
||||
interval_str.c_str(), sizeof(interval_str));
|
||||
std::string cnt_str = std::to_string(cnt);
|
||||
setsockopt(socket, IPPROTO_TCP, TCP_KEEPCNT,
|
||||
cnt_str.c_str(), sizeof(cnt_str));
|
||||
#else
|
||||
struct {
|
||||
uint32_t onoff;
|
||||
uint32_t keepalivetime;
|
||||
uint32_t keepaliveinterval;
|
||||
} keepalive;
|
||||
keepalive.onoff = 1;
|
||||
keepalive.keepalivetime = seconds * 1000;
|
||||
keepalive.keepaliveinterval = interval * 1000;
|
||||
int32_t out = 0;
|
||||
WSAIoctl(socket, SIO_KEEPALIVE_VALS, keepalive, sizeof(tcp_keepalive),
|
||||
nullptr, 0, &out, nullptr, nullptr);
|
||||
#endif
|
||||
#else
|
||||
uint32_t val = 1;
|
||||
setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(uint32_t));
|
||||
#ifdef __APPLE__
|
||||
// Old Apple devices only have one parameter
|
||||
setsockopt(socket, IPPROTO_TCP, TCP_KEEPALIVE, &seconds, sizeof(uint32_t));
|
||||
#if defined(TCP_KEEPINTVL) && defined(TCP_KEEPCNT)
|
||||
setsockopt(socket, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(uint32_t));
|
||||
setsockopt(socket, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(uint32_t));
|
||||
#endif
|
||||
#else
|
||||
// Linux based systems
|
||||
setsockopt(socket, SOL_TCP, TCP_KEEPIDLE, &seconds, sizeof(uint32_t));
|
||||
setsockopt(socket, SOL_TCP, TCP_KEEPINTVL, &interval, sizeof(uint32_t));
|
||||
setsockopt(socket, SOL_TCP, TCP_KEEPCNT, &cnt, sizeof(uint32_t));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
const asio::ip::address&
|
||||
Connection::local_address() const
|
||||
{
|
||||
@@ -731,7 +745,7 @@ Connection::timeout(const std::chrono::seconds& timeout, HandlerCb cb)
|
||||
return;
|
||||
else if (ec){
|
||||
if (logger)
|
||||
logger->error("[connection:{:d}] timeout error: {:s}", id, ec.message());
|
||||
logger->e("[connection:%i] timeout error: %s", id, ec.message().c_str());
|
||||
}
|
||||
if (cb)
|
||||
cb(ec);
|
||||
@@ -781,12 +795,6 @@ Resolver::~Resolver()
|
||||
*destroyed_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
Resolver::cancel()
|
||||
{
|
||||
resolver_.cancel();
|
||||
}
|
||||
|
||||
inline
|
||||
std::vector<asio::ip::tcp::endpoint>
|
||||
filter(const std::vector<asio::ip::tcp::endpoint>& epts, sa_family_t family)
|
||||
@@ -828,8 +836,9 @@ Resolver::resolve(const std::string& host, const std::string& service)
|
||||
if (ec == asio::error::operation_aborted or *destroyed)
|
||||
return;
|
||||
if (logger_) {
|
||||
logger_->error("[http:client] [resolver] result for {:s}:{:s}: {:s}",
|
||||
host, service, ec.message());
|
||||
if (ec)
|
||||
logger_->e("[http:client] [resolver] error for %s:%s: %s",
|
||||
host.c_str(), service.c_str(), ec.message().c_str());
|
||||
}
|
||||
decltype(cbs_) cbs;
|
||||
{
|
||||
@@ -871,7 +880,7 @@ Request::Request(asio::io_context& ctx, const std::string& url, const Json::Valu
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
if (!reader->parse(response.body.data(), response.body.data() + response.body.size(), &json, &err) and logger_)
|
||||
logger_->error("[http:request:{:d}] can't parse response to json: {:s}", id_, err);
|
||||
logger_->e("[http:request:%i] can't parse response to json", id_, err.c_str());
|
||||
}
|
||||
if (jsoncb)
|
||||
jsoncb(std::move(json), response);
|
||||
@@ -893,7 +902,7 @@ Request::Request(asio::io_context& ctx, const std::string& url, OnJsonCb jsoncb,
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
if (!reader->parse(response.body.data(), response.body.data() + response.body.size(), &json, &err) and logger_)
|
||||
logger_->error("[http:request:{:d}] can't parse response to json: {:s}", id_, err);
|
||||
logger_->e("[http:request:%i] can't parse response to json", id_, err.c_str());
|
||||
}
|
||||
if (jsoncb)
|
||||
jsoncb(std::move(json), response);
|
||||
@@ -961,8 +970,6 @@ Request::init_default_headers()
|
||||
void
|
||||
Request::cancel()
|
||||
{
|
||||
if (auto r = resolver_)
|
||||
r->cancel();
|
||||
if (auto c = conn_)
|
||||
c->close();
|
||||
}
|
||||
@@ -1059,7 +1066,7 @@ Request::build()
|
||||
break;
|
||||
case restinio::http_connection_header_t::upgrade:
|
||||
if (logger_)
|
||||
logger_->error("Unsupported connection type 'upgrade', fallback to 'close'");
|
||||
logger_->e("Unsupported connection type 'upgrade', fallback to 'close'");
|
||||
// fallthrough
|
||||
case restinio::http_connection_header_t::close:
|
||||
conn_str = "close"; // default
|
||||
@@ -1077,30 +1084,6 @@ Request::build()
|
||||
request_ = request.str();
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/17708801
|
||||
std::string
|
||||
Request::url_encode(std::string_view value)
|
||||
{
|
||||
std::ostringstream escaped;
|
||||
escaped.fill('0');
|
||||
escaped << std::hex;
|
||||
|
||||
for (const char& c : value) {
|
||||
// Keep alphanumeric and other accepted characters intact
|
||||
if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
|
||||
escaped << c;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Any other characters are percent-encoded
|
||||
escaped << std::uppercase;
|
||||
escaped << '%' << std::setw(2) << static_cast<int>(static_cast<unsigned char>(c));
|
||||
escaped << std::nouppercase;
|
||||
}
|
||||
|
||||
return escaped.str();
|
||||
}
|
||||
|
||||
void
|
||||
Request::add_on_status_callback(OnStatusCb cb) {
|
||||
cbs_.on_status = std::move(cb);
|
||||
@@ -1137,11 +1120,13 @@ Request::init_parser()
|
||||
response_.request = shared_from_this();
|
||||
|
||||
if (!parser_)
|
||||
parser_ = std::make_unique<llhttp_t>();
|
||||
parser_ = std::make_unique<http_parser>();
|
||||
http_parser_init(parser_.get(), HTTP_RESPONSE);
|
||||
parser_->data = static_cast<void*>(this);
|
||||
|
||||
if (!parser_s_)
|
||||
parser_s_ = std::make_unique<llhttp_settings_t>();
|
||||
llhttp_settings_init(parser_s_.get());
|
||||
parser_s_ = std::make_unique<http_parser_settings>();
|
||||
http_parser_settings_init(parser_s_.get());
|
||||
|
||||
cbs_.on_status = [this, statusCb = std::move(cbs_.on_status)](unsigned int status_code){
|
||||
response_.status_code = status_code;
|
||||
@@ -1156,33 +1141,31 @@ Request::init_parser()
|
||||
response_.headers[*header_field] = std::string(at, length);
|
||||
};
|
||||
|
||||
// llhttp raw c callback (note: no context can be passed into them)
|
||||
parser_s_->on_status = [](llhttp_t* parser, const char* /*at*/, size_t /*length*/) -> int {
|
||||
// http_parser raw c callback (note: no context can be passed into them)
|
||||
parser_s_->on_status = [](http_parser* parser, const char* /*at*/, size_t /*length*/) -> int {
|
||||
static_cast<Request*>(parser->data)->cbs_.on_status(parser->status_code);
|
||||
return 0;
|
||||
};
|
||||
parser_s_->on_header_field = [](llhttp_t* parser, const char* at, size_t length) -> int {
|
||||
parser_s_->on_header_field = [](http_parser* parser, const char* at, size_t length) -> int {
|
||||
static_cast<Request*>(parser->data)->cbs_.on_header_field(at, length);
|
||||
return 0;
|
||||
};
|
||||
parser_s_->on_header_value = [](llhttp_t* parser, const char* at, size_t length) -> int {
|
||||
parser_s_->on_header_value = [](http_parser* parser, const char* at, size_t length) -> int {
|
||||
static_cast<Request*>(parser->data)->cbs_.on_header_value(at, length);
|
||||
return 0;
|
||||
};
|
||||
parser_s_->on_body = [](llhttp_t* parser, const char* at, size_t length) -> int {
|
||||
parser_s_->on_body = [](http_parser* parser, const char* at, size_t length) -> int {
|
||||
static_cast<Request*>(parser->data)->onBody(at, length);
|
||||
return 0;
|
||||
};
|
||||
parser_s_->on_headers_complete = [](llhttp_t* parser) -> int {
|
||||
parser_s_->on_headers_complete = [](http_parser* parser) -> int {
|
||||
static_cast<Request*>(parser->data)->onHeadersComplete();
|
||||
return 0;
|
||||
};
|
||||
parser_s_->on_message_complete = [](llhttp_t* parser) -> int {
|
||||
parser_s_->on_message_complete = [](http_parser* parser) -> int {
|
||||
static_cast<Request*>(parser->data)->onComplete();
|
||||
return 0;
|
||||
};
|
||||
llhttp_init(parser_.get(), HTTP_RESPONSE, parser_s_.get());
|
||||
parser_->data = static_cast<void*>(this);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1190,7 +1173,7 @@ Request::connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, HandlerCb cb)
|
||||
{
|
||||
if (endpoints.empty()){
|
||||
if (logger_)
|
||||
logger_->error("[http:request:{:d}] connect: no endpoints provided", id_);
|
||||
logger_->e("[http:request:%i] connect: no endpoints provided", id_);
|
||||
if (cb)
|
||||
cb(asio::error::connection_aborted);
|
||||
return;
|
||||
@@ -1199,7 +1182,7 @@ Request::connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, HandlerCb cb)
|
||||
std::string eps = "";
|
||||
for (const auto& endpoint : endpoints)
|
||||
eps.append(endpoint.address().to_string() + ":" + std::to_string(endpoint.port()) + " ");
|
||||
logger_->debug("[http:request:{:d}] connect begin: {:s}", id_, eps);
|
||||
logger_->d("[http:request:%i] connect begin: %s", id_, eps.c_str());
|
||||
}
|
||||
bool isHttps = get_url().protocol == "https";
|
||||
if (isHttps) {
|
||||
@@ -1230,7 +1213,7 @@ Request::connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, HandlerCb cb)
|
||||
}
|
||||
else if (ec) {
|
||||
if (this_.logger_)
|
||||
this_.logger_->error("[http:request:{:d}] connect failed with all endpoints: {:s}", this_.id_, ec.message());
|
||||
this_.logger_->e("[http:request:%i] connect failed with all endpoints: %s", this_.id_, ec.message().c_str());
|
||||
} else {
|
||||
const auto& url = this_.get_url();
|
||||
auto port = endpoint.port();
|
||||
@@ -1246,9 +1229,9 @@ Request::connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, HandlerCb cb)
|
||||
if (ec == asio::error::operation_aborted)
|
||||
return;
|
||||
if (ec and logger)
|
||||
logger->error("[http:request:{:d}] handshake error: {:s}", id, ec.message());
|
||||
logger->e("[http:request:%i] handshake error: %s", id, ec.message().c_str());
|
||||
//else if (logger)
|
||||
// logger->d("[http:request:{:d}] handshake success", id);
|
||||
// logger->d("[http:request:%i] handshake success", id);
|
||||
if (cb)
|
||||
cb(ec);
|
||||
});
|
||||
@@ -1276,7 +1259,7 @@ Request::send()
|
||||
std::lock_guard<std::mutex> lock(this_.mutex_);
|
||||
if (ec){
|
||||
if (this_.logger_)
|
||||
this_.logger_->error("[http:request:{:d}] resolve error: {:s}", this_.id_, ec.message());
|
||||
this_.logger_->e("[http:request:%i] resolve error: %s", this_.id_, ec.message().c_str());
|
||||
this_.terminate(asio::error::connection_aborted);
|
||||
}
|
||||
else if (!this_.conn_ or !this_.conn_->is_open()) {
|
||||
@@ -1306,7 +1289,7 @@ Request::post()
|
||||
init_parser();
|
||||
|
||||
if (logger_)
|
||||
logger_->debug("[http:request:{}] sending {} bytes", id_, request_.size());
|
||||
logger_->d("[http:request:%i] sending %zu bytes", id_, request_.size());
|
||||
|
||||
// write the request to buffer
|
||||
std::ostream request_stream(&conn_->input());
|
||||
@@ -1334,12 +1317,12 @@ Request::terminate(const asio::error_code& ec)
|
||||
|
||||
if (logger_) {
|
||||
if (ec and ec != asio::error::eof and !response_.aborted)
|
||||
logger_->error("[http:request:{:d}] end with error: {:s}", id_, ec.message());
|
||||
logger_->e("[http:request:%i] end with error: %s", id_, ec.message().c_str());
|
||||
else
|
||||
logger_->debug("[http:request:{:d}] done with status code {:d}", id_, response_.status_code);
|
||||
logger_->d("[http:request:%i] done with status code %u", id_, response_.status_code);
|
||||
}
|
||||
|
||||
if (!parser_ or !llhttp_should_keep_alive(parser_.get()))
|
||||
if (!parser_ or !http_should_keep_alive(parser_.get()))
|
||||
if (auto c = conn_)
|
||||
c->close();
|
||||
notify_state_change(State::DONE);
|
||||
@@ -1378,15 +1361,17 @@ Request::handle_response(const asio::error_code& ec, size_t /* n_bytes */)
|
||||
return;
|
||||
}
|
||||
auto request = (ec == asio::error::eof) ? std::string{} : conn_->read_bytes();
|
||||
enum llhttp_errno ret = llhttp_execute(parser_.get(), request.c_str(), request.size());
|
||||
if (ret != HPE_OK && ret != HPE_PAUSED) {
|
||||
size_t ret = http_parser_execute(parser_.get(), parser_s_.get(), request.c_str(), request.size());
|
||||
if (ret != request.size()) {
|
||||
if (logger_)
|
||||
logger_->e("Error parsing HTTP: %zu %s %d", (int)ret, llhttp_errno_name(ret), llhttp_get_error_reason(parser_.get()));
|
||||
logger_->e("Error parsing HTTP: %zu %s %s", ret,
|
||||
http_errno_name(HTTP_PARSER_ERRNO(parser_)),
|
||||
http_errno_description(HTTP_PARSER_ERRNO(parser_)));
|
||||
terminate(asio::error::basic_errors::broken_pipe);
|
||||
return;
|
||||
}
|
||||
|
||||
if (state_ != State::DONE and parser_ and not llhttp_message_needs_eof(parser_.get())) {
|
||||
if (state_ != State::DONE and parser_ and not http_body_is_final(parser_.get())) {
|
||||
auto toRead = parser_->content_length ? std::min<uint64_t>(parser_->content_length, 64 * 1024) : 64 * 1024;
|
||||
std::weak_ptr<Request> wthis = shared_from_this();
|
||||
conn_->async_read_some(toRead, [wthis](const asio::error_code& ec, size_t bytes){
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Nicolas Reynaud <nicolas.reynaud@savoirfairelinux.com>
|
||||
@@ -316,7 +316,7 @@ void Pht::updateCanary(Prefix p) {
|
||||
dht_->put(p.hash(), std::move(canary_value),
|
||||
[=](bool){
|
||||
static std::bernoulli_distribution d(0.5);
|
||||
std::random_device rd;
|
||||
crypto::random_device rd;
|
||||
if (p.size_ and d(rd))
|
||||
updateCanary(p.getPrefix(-1));
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
+35
-76
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
@@ -20,10 +20,6 @@
|
||||
#include "log.h"
|
||||
#include "dhtrunner.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/ostream.h>
|
||||
#include <fmt/printf.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
@@ -34,70 +30,46 @@
|
||||
namespace dht {
|
||||
namespace log {
|
||||
|
||||
/**
|
||||
* Terminal colors for logging
|
||||
*/
|
||||
namespace Color {
|
||||
enum Code {
|
||||
FG_RED = 31,
|
||||
FG_GREEN = 32,
|
||||
FG_YELLOW = 33,
|
||||
FG_BLUE = 34,
|
||||
FG_DEFAULT = 39,
|
||||
BG_RED = 41,
|
||||
BG_GREEN = 42,
|
||||
BG_BLUE = 44,
|
||||
BG_DEFAULT = 49
|
||||
};
|
||||
class Modifier {
|
||||
const Code code;
|
||||
public:
|
||||
constexpr Modifier(Code pCode) : code(pCode) {}
|
||||
friend std::ostream&
|
||||
operator<<(std::ostream& os, const Modifier& mod) {
|
||||
return os << "\033[" << mod.code << 'm';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
constexpr const Color::Modifier def(Color::FG_DEFAULT);
|
||||
constexpr const Color::Modifier red(Color::FG_RED);
|
||||
constexpr const Color::Modifier yellow(Color::FG_YELLOW);
|
||||
|
||||
/**
|
||||
* Print va_list to std::ostream (used for logging).
|
||||
*/
|
||||
void
|
||||
printfLog(std::ostream& s, const std::string& message) {
|
||||
printLog(std::ostream& s, char const *m, va_list args) {
|
||||
// print log to buffer
|
||||
std::array<char, 8192> buffer;
|
||||
int ret = vsnprintf(buffer.data(), buffer.size(), m, args);
|
||||
if (ret <= 0)
|
||||
return;
|
||||
|
||||
// write timestamp
|
||||
using namespace std::chrono;
|
||||
using log_precision = microseconds;
|
||||
auto num = duration_cast<log_precision>(steady_clock::now().time_since_epoch()).count();
|
||||
constexpr auto den = log_precision::period::den;
|
||||
fmt::print(s, "[{:06d}.{:06d}] ", num / den, num % den);
|
||||
s << message << std::endl;
|
||||
}
|
||||
void
|
||||
printLog(std::ostream& s, fmt::string_view format, fmt::format_args args) {
|
||||
using namespace std::chrono;
|
||||
using log_precision = microseconds;
|
||||
auto num = duration_cast<log_precision>(steady_clock::now().time_since_epoch()).count();
|
||||
constexpr auto den = log_precision::period::den;
|
||||
fmt::print(s, "[{:06d}.{:06d}] ", num / den, num % den);
|
||||
fmt::vprint(s, format, args);
|
||||
s << "[" << std::setfill('0') << std::setw(6) << num / den << "."
|
||||
<< std::setfill('0') << std::setw(6) << num % den << "]" << " ";
|
||||
|
||||
// write log
|
||||
s.write(buffer.data(), std::min((size_t) ret, buffer.size()));
|
||||
if ((size_t) ret >= buffer.size())
|
||||
s << "[[TRUNCATED]]";
|
||||
s << std::endl;
|
||||
}
|
||||
|
||||
std::shared_ptr<Logger>
|
||||
getStdLogger() {
|
||||
return std::make_shared<Logger>(
|
||||
[](LogLevel level, std::string&& message) {
|
||||
if (level == LogLevel::error)
|
||||
std::cerr << red;
|
||||
else if (level == LogLevel::warning)
|
||||
std::cerr << yellow;
|
||||
printfLog(std::cerr, message);
|
||||
[](char const *m, va_list args) {
|
||||
std::cerr << red;
|
||||
printLog(std::cerr, m, args);
|
||||
std::cerr << def;
|
||||
}
|
||||
},
|
||||
[](char const *m, va_list args) {
|
||||
std::cout << yellow;
|
||||
printLog(std::cout, m, args);
|
||||
std::cout << def;
|
||||
},
|
||||
[](char const *m, va_list args) { printLog(std::cout, m, args); }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -105,28 +77,14 @@ std::shared_ptr<Logger>
|
||||
getFileLogger(const std::string &path) {
|
||||
auto logfile = std::make_shared<std::ofstream>();
|
||||
logfile->open(path, std::ios::out);
|
||||
|
||||
return std::make_shared<Logger>(
|
||||
[logfile](LogLevel /*level*/, std::string&& message) {
|
||||
printfLog(*logfile, message);
|
||||
}
|
||||
[=](char const *m, va_list args) { printLog(*logfile, m, args); },
|
||||
[=](char const *m, va_list args) { printLog(*logfile, m, args); },
|
||||
[=](char const *m, va_list args) { printLog(*logfile, m, args); }
|
||||
);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
constexpr
|
||||
int syslogLevel(LogLevel level) {
|
||||
switch (level) {
|
||||
case LogLevel::error:
|
||||
return LOG_ERR;
|
||||
case LogLevel::warning:
|
||||
return LOG_WARNING;
|
||||
case LogLevel::debug:
|
||||
return LOG_INFO;
|
||||
}
|
||||
return LOG_ERR;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::shared_ptr<Logger>
|
||||
getSyslogLogger(const char* name) {
|
||||
#ifndef _WIN32
|
||||
@@ -146,11 +104,12 @@ getSyslogLogger(const char* name) {
|
||||
opened_logfile = logfile;
|
||||
}
|
||||
return std::make_shared<Logger>(
|
||||
[logfile](LogLevel level, std::string&& message) {
|
||||
syslog(syslogLevel(level), "%s", message.c_str());
|
||||
});
|
||||
[logfile](char const *m, va_list args) { vsyslog(LOG_ERR, m, args); },
|
||||
[logfile](char const *m, va_list args) { vsyslog(LOG_WARNING, m, args); },
|
||||
[logfile](char const *m, va_list args) { vsyslog(LOG_INFO, m, args); }
|
||||
);
|
||||
#else
|
||||
return getStdLogger();
|
||||
return std::make_shared<Logger>();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
+148
-165
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
@@ -20,12 +20,10 @@
|
||||
#include "network_engine.h"
|
||||
#include "request.h"
|
||||
#include "default_types.h"
|
||||
#include "logger.h"
|
||||
#include "log_enable.h"
|
||||
#include "parsed_message.h"
|
||||
|
||||
#include <msgpack.hpp>
|
||||
#include <chrono>
|
||||
#include <string_view>
|
||||
|
||||
namespace dht {
|
||||
namespace net {
|
||||
@@ -43,8 +41,7 @@ constexpr std::chrono::seconds NetworkEngine::UDP_REPLY_TIME;
|
||||
constexpr std::chrono::seconds NetworkEngine::RX_MAX_PACKET_TIME;
|
||||
constexpr std::chrono::seconds NetworkEngine::RX_TIMEOUT;
|
||||
|
||||
/* OpenDHT User Agent (UA) */
|
||||
constexpr std::string_view OPENDHT_UA {"o2"};
|
||||
const std::string NetworkEngine::my_v {"RNG1"};
|
||||
|
||||
static constexpr uint8_t v4prefix[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0};
|
||||
|
||||
@@ -56,6 +53,8 @@ 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>
|
||||
@@ -86,10 +85,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,
|
||||
@@ -99,6 +98,7 @@ 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,9 +110,12 @@ 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),
|
||||
scheduler(scheduler)
|
||||
{}
|
||||
rate_limiter(config.max_req_per_sec)
|
||||
{
|
||||
dht_socket->setOnReceive([this](const ReceivedPacket& pkt) {
|
||||
processMessage(pkt.data.data(), pkt.data.size(), pkt.from);
|
||||
});
|
||||
}
|
||||
|
||||
NetworkEngine::~NetworkEngine() {
|
||||
clear();
|
||||
@@ -127,7 +130,7 @@ 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), scheduler.time(), ntoken, socket_id);
|
||||
sendUpdateValues(node, hash, std::move(values), time(), ntoken, socket_id);
|
||||
} else {
|
||||
sendNodesValues(node->getAddr(), socket_id, nnodes.first, nnodes.second, std::move(values), query, ntoken);
|
||||
}
|
||||
@@ -143,7 +146,7 @@ NetworkEngine::tellListenerRefreshed(const Sp<Node>& n, Tid socket_id, const Inf
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
|
||||
pk.pack_map(4 + (version >= 1 ? 1 : 0) + (config.network?1:0)+(config.is_client?1:0));
|
||||
pk.pack_map(4 + (version >= 1 ? 1 : 0) + (config.network?1:0));
|
||||
|
||||
pk.pack(version >= 1 ? KEY_A : KEY_U);
|
||||
pk.pack_map(1 + (version >= 1 ? 1 : 0) + (not values.empty()?1:0) + (not token.empty()?1:0));
|
||||
@@ -162,13 +165,10 @@ NetworkEngine::tellListenerRefreshed(const Sp<Node>& n, Tid socket_id, const Inf
|
||||
}
|
||||
|
||||
pk.pack(KEY_Y); pk.pack(version >= 1 ? KEY_Q : KEY_R);
|
||||
pk.pack(KEY_UA); pk.pack(OPENDHT_UA);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
if (config.is_client) {
|
||||
pk.pack(KEY_ISCLIENT); pk.pack(config.is_client);
|
||||
}
|
||||
|
||||
if (version >= 1) {
|
||||
Tid tid (n->getNewTid());
|
||||
@@ -197,7 +197,7 @@ NetworkEngine::tellListenerExpired(const Sp<Node>& n, Tid socket_id, const InfoH
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
|
||||
pk.pack_map(4 + (version >= 1 ? 1 : 0) + (config.network?1:0)+(config.is_client?1:0));
|
||||
pk.pack_map(4 + (version >= 1 ? 1 : 0) + (config.network?1:0));
|
||||
|
||||
pk.pack(version >= 1 ? KEY_A : KEY_U);
|
||||
pk.pack_map(1 + (version >= 1 ? 1 : 0) + (not values.empty()?1:0) + (not token.empty()?1:0));
|
||||
@@ -216,13 +216,10 @@ NetworkEngine::tellListenerExpired(const Sp<Node>& n, Tid socket_id, const InfoH
|
||||
}
|
||||
|
||||
pk.pack(KEY_Y); pk.pack(version >= 1 ? KEY_Q : KEY_R);
|
||||
pk.pack(KEY_UA); pk.pack(OPENDHT_UA);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
if (config.is_client) {
|
||||
pk.pack(KEY_ISCLIENT); pk.pack(config.is_client);
|
||||
}
|
||||
|
||||
if (version >= 1) {
|
||||
Tid tid (n->getNewTid());
|
||||
@@ -284,7 +281,7 @@ NetworkEngine::requestStep(Sp<Request> sreq)
|
||||
if (not req.pending())
|
||||
return;
|
||||
|
||||
auto now = scheduler.time();
|
||||
const auto& now = time();
|
||||
auto& node = *req.node;
|
||||
if (req.isExpired(now)) {
|
||||
// if (logger_)
|
||||
@@ -298,18 +295,19 @@ 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 == ENETUNREACH ||
|
||||
err == EHOSTUNREACH ||
|
||||
err == EAFNOSUPPORT ||
|
||||
err == EPIPE ||
|
||||
err == EPERM)
|
||||
|
||||
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)
|
||||
{
|
||||
node.setExpired();
|
||||
if (not node.id)
|
||||
requests.erase(req.tid);
|
||||
} else {
|
||||
req.last_try = now;
|
||||
if (err != EAGAIN) {
|
||||
if (err != asio::error::try_again) {
|
||||
++req.attempt_count;
|
||||
req.attempt_duration +=
|
||||
req.attempt_duration + uniform_duration_distribution<>(0ms, ((duration)Node::MAX_RESPONSE_TIME)/4)(rd);
|
||||
@@ -318,9 +316,15 @@ NetworkEngine::requestStep(Sp<Request> sreq)
|
||||
}
|
||||
}
|
||||
std::weak_ptr<Request> wreq = sreq;
|
||||
scheduler.add(req.last_try + req.attempt_duration, [this,wreq] {
|
||||
if (auto req = wreq.lock())
|
||||
requestStep(req);
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -335,7 +339,7 @@ NetworkEngine::sendRequest(const Sp<Request>& request)
|
||||
auto& node = *request->node;
|
||||
if (not node.id)
|
||||
requests.emplace(request->tid, request);
|
||||
request->start = scheduler.time();
|
||||
request->start = time();
|
||||
node.requested(request);
|
||||
requestStep(request);
|
||||
}
|
||||
@@ -345,7 +349,7 @@ NetworkEngine::sendRequest(const Sp<Request>& request)
|
||||
bool
|
||||
NetworkEngine::rateLimit(const SockAddr& addr)
|
||||
{
|
||||
const auto& now = scheduler.time();
|
||||
const auto& now = time();
|
||||
|
||||
// occasional IP limiter maintenance (a few times every second at max rate)
|
||||
if (limiter_maintenance++ == config.max_peer_req_per_sec) {
|
||||
@@ -369,23 +373,20 @@ NetworkEngine::rateLimit(const SockAddr& addr)
|
||||
bool
|
||||
NetworkEngine::isMartian(const SockAddr& addr)
|
||||
{
|
||||
if (addr.getPort() == 0)
|
||||
if (addr.port() == 0)
|
||||
return true;
|
||||
switch(addr.getFamily()) {
|
||||
switch(addr.protocol().family()) {
|
||||
case AF_INET: {
|
||||
const auto& sin = addr.getIPv4();
|
||||
const uint8_t* address = (const uint8_t*)&sin.sin_addr;
|
||||
return (address[0] == 0) ||
|
||||
((address[0] & 0xE0) == 0xE0);
|
||||
const auto& sin = addr.address().to_v4().to_bytes();
|
||||
return (sin[0] == 0) ||
|
||||
((sin[0] & 0xE0) == 0xE0);
|
||||
}
|
||||
case AF_INET6: {
|
||||
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 ||
|
||||
memcmp(address, InfoHash::zero().data(), 16) == 0 ||
|
||||
memcmp(address, v4prefix, 12) == 0;
|
||||
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;
|
||||
}
|
||||
default:
|
||||
return true;
|
||||
@@ -410,16 +411,17 @@ NetworkEngine::isNodeBlacklisted(const SockAddr& addr) const
|
||||
void
|
||||
NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, SockAddr f)
|
||||
{
|
||||
auto from = f.getMappedIPv4();
|
||||
syncTime();
|
||||
const 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;
|
||||
}
|
||||
|
||||
@@ -441,7 +443,7 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, SockAddr f)
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& now = scheduler.time();
|
||||
const auto& now = time();
|
||||
|
||||
// partial value data
|
||||
if (msg->type == MessageType::ValueData) {
|
||||
@@ -453,7 +455,7 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, SockAddr f)
|
||||
rateLimit(from);
|
||||
return;
|
||||
}
|
||||
if (!pmsg_it->second.from.equals(from)) {
|
||||
if (pmsg_it->second.from != from) {
|
||||
if (logger_)
|
||||
logger_->d("Received partial message data from unexpected IP address");
|
||||
rateLimit(from);
|
||||
@@ -471,8 +473,13 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, SockAddr f)
|
||||
} catch (...) {
|
||||
return;
|
||||
}
|
||||
} else
|
||||
scheduler.add(now + RX_TIMEOUT, std::bind(&NetworkEngine::maintainRxBuffer, this, msg->tid));
|
||||
} 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -507,8 +514,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));
|
||||
//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);
|
||||
}
|
||||
@@ -517,7 +534,7 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, SockAddr f)
|
||||
void
|
||||
NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& from)
|
||||
{
|
||||
const auto& now = scheduler.time();
|
||||
const auto& now = time();
|
||||
auto node = cache.getNode(msg->id, from, now, true, msg->is_client);
|
||||
|
||||
if (msg->type == MessageType::ValueUpdate) {
|
||||
@@ -574,10 +591,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;
|
||||
}
|
||||
@@ -589,7 +606,7 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
|
||||
or r.getType() == MessageType::Refresh) {
|
||||
r.node->authSuccess();
|
||||
}
|
||||
r.reply_time = scheduler.time();
|
||||
r.reply_time = time();
|
||||
|
||||
deserializeNodes(*msg, from);
|
||||
r.setDone(std::move(*msg));
|
||||
@@ -621,7 +638,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.getFamily(), msg->target, msg->want, answer.nodes4, answer.nodes6);
|
||||
auto nnodes = bufferNodes(from.protocol().family(), msg->target, msg->want, answer.nodes4, answer.nodes6);
|
||||
sendNodesValues(from, msg->tid, nnodes.first, nnodes.second, {}, {}, answer.ntoken);
|
||||
break;
|
||||
}
|
||||
@@ -630,7 +647,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.getFamily(), msg->info_hash, msg->want, answer.nodes4, answer.nodes6);
|
||||
auto nnodes = bufferNodes(from.protocol().family(), msg->info_hash, msg->want, answer.nodes4, answer.nodes6);
|
||||
sendNodesValues(from, msg->tid, nnodes.first, nnodes.second, answer.values, msg->query, answer.ntoken);
|
||||
break;
|
||||
}
|
||||
@@ -660,7 +677,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.getFamily(), msg->info_hash, msg->want, answer.nodes4, answer.nodes6);
|
||||
auto nnodes = bufferNodes(from.protocol().family(), msg->info_hash, msg->want, answer.nodes4, answer.nodes6);
|
||||
sendListenConfirmation(from, msg->tid);
|
||||
break;
|
||||
}
|
||||
@@ -690,19 +707,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");
|
||||
pk.pack_bin(addr_len);
|
||||
pk.pack_bin_body((char*)addr_ptr, addr_len);
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
asio::error_code
|
||||
NetworkEngine::send(const SockAddr& addr, const char *buf, size_t len, bool confirmed)
|
||||
{
|
||||
return dht_socket ? dht_socket->sendTo(addr, (const uint8_t*)buf, len, confirmed) : ENOTCONN;
|
||||
return dht_socket ? dht_socket->sendTo((const uint8_t*)buf, len, addr) : asio::error::not_connected;
|
||||
}
|
||||
|
||||
Sp<Request>
|
||||
@@ -710,7 +731,7 @@ NetworkEngine::sendPing(const Sp<Node>& node, RequestCb&& on_done, RequestExpire
|
||||
Tid tid (node->getNewTid());
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(config.network?1:0)+(config.is_client?1:0));
|
||||
pk.pack_map(5+(config.network?1:0));
|
||||
|
||||
pk.pack(KEY_A); pk.pack_map(1);
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
@@ -718,13 +739,10 @@ NetworkEngine::sendPing(const Sp<Node>& node, RequestCb&& on_done, RequestExpire
|
||||
pk.pack(KEY_Q); pk.pack(QUERY_PING);
|
||||
pk.pack(KEY_TID); pk.pack(tid);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_Q);
|
||||
pk.pack(KEY_UA); pk.pack(OPENDHT_UA);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
if (config.is_client) {
|
||||
pk.pack(KEY_ISCLIENT); pk.pack(config.is_client);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(MessageType::Ping, tid, node,
|
||||
Blob(buffer.data(), buffer.data() + buffer.size()),
|
||||
@@ -750,7 +768,7 @@ void
|
||||
NetworkEngine::sendPong(const SockAddr& addr, Tid tid) {
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(4+(config.network?1:0)+(config.is_client?1:0));
|
||||
pk.pack_map(4+(config.network?1:0));
|
||||
|
||||
pk.pack(KEY_R); pk.pack_map(2);
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
@@ -758,13 +776,10 @@ NetworkEngine::sendPong(const SockAddr& addr, Tid tid) {
|
||||
|
||||
pk.pack(KEY_TID); pk.pack(tid);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_R);
|
||||
pk.pack(KEY_UA); pk.pack(OPENDHT_UA);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
if (config.is_client) {
|
||||
pk.pack(KEY_ISCLIENT); pk.pack(config.is_client);
|
||||
}
|
||||
|
||||
send(addr, buffer.data(), buffer.size());
|
||||
}
|
||||
@@ -775,7 +790,7 @@ NetworkEngine::sendFindNode(const Sp<Node>& n, const InfoHash& target, want_t wa
|
||||
Tid tid (n->getNewTid());
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(config.network?1:0)+(config.is_client?1:0));
|
||||
pk.pack_map(5+(config.network?1:0));
|
||||
|
||||
pk.pack(KEY_A); pk.pack_map(2 + (want>0?1:0));
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
@@ -790,13 +805,10 @@ NetworkEngine::sendFindNode(const Sp<Node>& n, const InfoHash& target, want_t wa
|
||||
pk.pack(KEY_Q); pk.pack(QUERY_FIND);
|
||||
pk.pack(KEY_TID); pk.pack(tid);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_Q);
|
||||
pk.pack(KEY_UA); pk.pack(OPENDHT_UA);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
if (config.is_client) {
|
||||
pk.pack(KEY_ISCLIENT); pk.pack(config.is_client);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(MessageType::FindNode, tid, n,
|
||||
Blob(buffer.data(), buffer.data() + buffer.size()),
|
||||
@@ -823,7 +835,7 @@ NetworkEngine::sendGetValues(const Sp<Node>& n, const InfoHash& info_hash, const
|
||||
Tid tid (n->getNewTid());
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(config.network?1:0)+(config.is_client?1:0));
|
||||
pk.pack_map(5+(config.network?1:0));
|
||||
|
||||
unsigned sendQuery = (not query.where.empty() or not query.select.empty()) ? 1 : 0;
|
||||
unsigned sendWant = (want > 0) ? 1 : 0;
|
||||
@@ -846,14 +858,11 @@ NetworkEngine::sendGetValues(const Sp<Node>& n, const InfoHash& info_hash, const
|
||||
pk.pack(KEY_Q); pk.pack(QUERY_GET);
|
||||
pk.pack(KEY_TID); pk.pack(tid);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_Q);
|
||||
pk.pack(KEY_UA); pk.pack(OPENDHT_UA);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
if (config.is_client) {
|
||||
pk.pack(KEY_ISCLIENT); pk.pack(config.is_client);
|
||||
}
|
||||
|
||||
|
||||
auto req = std::make_shared<Request>(MessageType::GetValues, tid, n,
|
||||
Blob(buffer.data(), buffer.data() + buffer.size()),
|
||||
[=](const Request& req_status, ParsedMessage&& msg) { /* on done */
|
||||
@@ -874,18 +883,15 @@ NetworkEngine::sendGetValues(const Sp<Node>& n, const InfoHash& info_hash, const
|
||||
|
||||
SockAddr deserializeIPv4(const uint8_t* ni) {
|
||||
SockAddr addr;
|
||||
addr.setFamily(AF_INET);
|
||||
auto& sin = addr.getIPv4();
|
||||
std::memcpy(&sin.sin_addr, ni, 4);
|
||||
std::memcpy(&sin.sin_port, ni + 4, 2);
|
||||
addr.address(asio::ip::address_v4({ni[0], ni[1], ni[2], ni[3]}));
|
||||
addr.port(ntohs(*reinterpret_cast<const uint16_t*>(ni + 4)));
|
||||
return addr;
|
||||
}
|
||||
SockAddr deserializeIPv6(const uint8_t* ni) {
|
||||
SockAddr addr;
|
||||
addr.setFamily(AF_INET6);
|
||||
auto& sin6 = addr.getIPv6();
|
||||
std::memcpy(&sin6.sin6_addr, ni, 16);
|
||||
std::memcpy(&sin6.sin6_port, ni + 16, 2);
|
||||
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)));
|
||||
return addr;
|
||||
}
|
||||
|
||||
@@ -895,21 +901,21 @@ NetworkEngine::deserializeNodes(ParsedMessage& msg, const SockAddr& from) {
|
||||
throw DhtProtocolException {DhtProtocolException::WRONG_NODE_INFO_BUF_LEN};
|
||||
}
|
||||
// deserialize nodes
|
||||
const auto& now = scheduler.time();
|
||||
const auto& now = 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.isLoopback() and from.getFamily() == AF_INET) {
|
||||
auto port = addr.getPort();
|
||||
if (addr.address().is_loopback() and from.protocol().family() == AF_INET) {
|
||||
auto port = addr.port();
|
||||
addr = from;
|
||||
addr.setPort(port);
|
||||
addr.port(port);
|
||||
}
|
||||
if (isMartian(addr) || isNodeBlacklisted(addr))
|
||||
continue;
|
||||
msg.nodes4.emplace_back(cache.getNode(ni_id, addr, now, false));
|
||||
msg.nodes4.emplace_back(cache.getNode(ni_id, std::move(addr), now, false));
|
||||
onNewNode(msg.nodes4.back(), 0);
|
||||
}
|
||||
for (unsigned i = 0, n = msg.nodes6_raw.size() / NODE6_INFO_BUF_LEN; i < n; i++) {
|
||||
@@ -918,14 +924,14 @@ NetworkEngine::deserializeNodes(ParsedMessage& msg, const SockAddr& from) {
|
||||
if (ni_id == myid)
|
||||
continue;
|
||||
SockAddr addr = deserializeIPv6(ni + ni_id.size());
|
||||
if (addr.isLoopback() and from.getFamily() == AF_INET6) {
|
||||
auto port = addr.getPort();
|
||||
if (addr.address().is_loopback() and from.protocol().family() == AF_INET6) {
|
||||
auto port = addr.port();
|
||||
addr = from;
|
||||
addr.setPort(port);
|
||||
addr.port(port);
|
||||
}
|
||||
if (isMartian(addr) || isNodeBlacklisted(addr))
|
||||
continue;
|
||||
msg.nodes6.emplace_back(cache.getNode(ni_id, addr, now, false));
|
||||
msg.nodes6.emplace_back(cache.getNode(ni_id, std::move(addr), now, false));
|
||||
onNewNode(msg.nodes6.back(), 0);
|
||||
}
|
||||
}
|
||||
@@ -970,13 +976,10 @@ NetworkEngine::sendValueParts(Tid tid, const std::vector<Blob>& svals, const Soc
|
||||
end = std::min(start + MTU, v.size());
|
||||
buffer.clear();
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(3+(config.network?1:0)+(config.is_client?1:0));
|
||||
pk.pack_map(3+(config.network?1:0));
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
if (config.is_client) {
|
||||
pk.pack(KEY_ISCLIENT); pk.pack(config.is_client);
|
||||
}
|
||||
pk.pack(KEY_Y); pk.pack(KEY_V);
|
||||
pk.pack(KEY_TID); pk.pack(tid);
|
||||
pk.pack(KEY_V); pk.pack_map(1);
|
||||
@@ -997,7 +1000,7 @@ NetworkEngine::sendNodesValues(const SockAddr& addr, Tid tid, const Blob& nodes,
|
||||
{
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(4+(config.network?1:0)+(config.is_client?1:0));
|
||||
pk.pack_map(4+(config.network?1:0));
|
||||
|
||||
pk.pack(KEY_R);
|
||||
pk.pack_map(2 + (not st.empty()?1:0) + (nodes.size()>0?1:0) + (nodes6.size()>0?1:0) + (not token.empty()?1:0));
|
||||
@@ -1035,13 +1038,10 @@ NetworkEngine::sendNodesValues(const SockAddr& addr, Tid tid, const Blob& nodes,
|
||||
|
||||
pk.pack(KEY_TID); pk.pack(tid);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_R);
|
||||
pk.pack(KEY_UA); pk.pack(OPENDHT_UA);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
if (config.is_client) {
|
||||
pk.pack(KEY_ISCLIENT); pk.pack(config.is_client);
|
||||
}
|
||||
|
||||
// send response
|
||||
send(addr, buffer.data(), buffer.size());
|
||||
@@ -1063,21 +1063,25 @@ 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().getIPv4();
|
||||
const auto& sin = n.getAddr();
|
||||
auto port = htons(sin.port());
|
||||
auto addr = sin.address().to_v4().to_bytes();
|
||||
auto dest = bnodes.data() + NODE4_INFO_BUF_LEN * i;
|
||||
memcpy(dest, n.id.data(), HASH_LEN);
|
||||
memcpy(dest + HASH_LEN, &sin.sin_addr, sizeof(in_addr));
|
||||
memcpy(dest + HASH_LEN + sizeof(in_addr), &sin.sin_port, sizeof(in_port_t));
|
||||
memcpy(dest + HASH_LEN, addr.data(), sizeof(in_addr));
|
||||
memcpy(dest + HASH_LEN + sizeof(in_addr), &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().getIPv6();
|
||||
const auto& sin6 = n.getAddr();
|
||||
auto port = htons(sin6.port());
|
||||
auto addr = sin6.address().to_v6().to_bytes();
|
||||
auto dest = bnodes.data() + NODE6_INFO_BUF_LEN * i;
|
||||
memcpy(dest, n.id.data(), HASH_LEN);
|
||||
memcpy(dest + HASH_LEN, &sin6.sin6_addr, sizeof(in6_addr));
|
||||
memcpy(dest + HASH_LEN + sizeof(in6_addr), &sin6.sin6_port, sizeof(in_port_t));
|
||||
memcpy(dest + HASH_LEN, addr.data(), sizeof(in6_addr));
|
||||
memcpy(dest + HASH_LEN + sizeof(in6_addr), &port, sizeof(in_port_t));
|
||||
}
|
||||
}
|
||||
return bnodes;
|
||||
@@ -1113,7 +1117,7 @@ NetworkEngine::sendListen(const Sp<Node>& n,
|
||||
Tid tid (n->getNewTid());
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(config.network?1:0)+(config.is_client?1:0));
|
||||
pk.pack_map(5+(config.network?1:0));
|
||||
|
||||
auto has_query = not query.where.empty() or not query.select.empty();
|
||||
pk.pack(KEY_A); pk.pack_map(5 + has_query);
|
||||
@@ -1129,13 +1133,10 @@ NetworkEngine::sendListen(const Sp<Node>& n,
|
||||
pk.pack(KEY_Q); pk.pack(QUERY_LISTEN);
|
||||
pk.pack(KEY_TID); pk.pack(tid);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_Q);
|
||||
pk.pack(KEY_UA); pk.pack(OPENDHT_UA);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
if (config.is_client) {
|
||||
pk.pack(KEY_ISCLIENT); pk.pack(config.is_client);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(MessageType::Listen, tid, n,
|
||||
Blob(buffer.data(), buffer.data() + buffer.size()),
|
||||
@@ -1157,7 +1158,7 @@ void
|
||||
NetworkEngine::sendListenConfirmation(const SockAddr& addr, Tid tid) {
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(4+(config.network?1:0)+(config.is_client?1:0));
|
||||
pk.pack_map(4+(config.network?1:0));
|
||||
|
||||
pk.pack(KEY_R); pk.pack_map(2);
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
@@ -1165,13 +1166,10 @@ NetworkEngine::sendListenConfirmation(const SockAddr& addr, Tid tid) {
|
||||
|
||||
pk.pack(KEY_TID); pk.pack(tid);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_R);
|
||||
pk.pack(KEY_UA); pk.pack(OPENDHT_UA);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
if (config.is_client) {
|
||||
pk.pack(KEY_ISCLIENT); pk.pack(config.is_client);
|
||||
}
|
||||
|
||||
send(addr, buffer.data(), buffer.size());
|
||||
}
|
||||
@@ -1188,9 +1186,9 @@ NetworkEngine::sendAnnounceValue(const Sp<Node>& n,
|
||||
Tid tid (n->getNewTid());
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(config.network?1:0)+(config.is_client?1:0));
|
||||
pk.pack_map(5+(config.network?1:0));
|
||||
|
||||
bool add_created = created < scheduler.time();
|
||||
bool add_created = created < time();
|
||||
pk.pack(KEY_A); pk.pack_map(add_created ? 5 : 4);
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
pk.pack(KEY_REQ_H); pk.pack(infohash);
|
||||
@@ -1204,13 +1202,10 @@ NetworkEngine::sendAnnounceValue(const Sp<Node>& n,
|
||||
pk.pack(KEY_Q); pk.pack(QUERY_PUT);
|
||||
pk.pack(KEY_TID); pk.pack(tid);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_Q);
|
||||
pk.pack(KEY_UA); pk.pack(OPENDHT_UA);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
if (config.is_client) {
|
||||
pk.pack(KEY_ISCLIENT); pk.pack(config.is_client);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(MessageType::AnnounceValue, tid, n,
|
||||
Blob(buffer.data(), buffer.data() + buffer.size()),
|
||||
@@ -1277,15 +1272,15 @@ NetworkEngine::sendUpdateValues(const Sp<Node>& n,
|
||||
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(config.network?1:0)+(config.is_client?1:0));
|
||||
pk.pack_map(5+(config.network?1:0));
|
||||
|
||||
pk.pack(KEY_A); pk.pack_map((created < scheduler.time() ? 7 : 6));
|
||||
pk.pack(KEY_A); pk.pack_map((created < 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 < scheduler.time()) {
|
||||
if (created < time()) {
|
||||
pk.pack(KEY_REQ_CREATION);
|
||||
pk.pack(to_time_t(created));
|
||||
}
|
||||
@@ -1294,13 +1289,10 @@ NetworkEngine::sendUpdateValues(const Sp<Node>& n,
|
||||
pk.pack(KEY_Q); pk.pack(QUERY_UPDATE);
|
||||
pk.pack(KEY_TID); pk.pack(tid);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_Q);
|
||||
pk.pack(KEY_UA); pk.pack(OPENDHT_UA);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
if (config.is_client) {
|
||||
pk.pack(KEY_ISCLIENT); pk.pack(config.is_client);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(MessageType::UpdateValue, tid, n,
|
||||
Blob(buffer.data(), buffer.data() + buffer.size()),
|
||||
@@ -1325,7 +1317,7 @@ NetworkEngine::sendRefreshValue(const Sp<Node>& n,
|
||||
Tid tid (n->getNewTid());
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(config.network?1:0)+(config.is_client?1:0));
|
||||
pk.pack_map(5+(config.network?1:0));
|
||||
|
||||
pk.pack(KEY_A); pk.pack_map(4);
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
@@ -1336,13 +1328,10 @@ NetworkEngine::sendRefreshValue(const Sp<Node>& n,
|
||||
pk.pack(KEY_Q); pk.pack(QUERY_REFRESH);
|
||||
pk.pack(KEY_TID); pk.pack(tid);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_Q);
|
||||
pk.pack(KEY_UA); pk.pack(OPENDHT_UA);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
if (config.is_client) {
|
||||
pk.pack(KEY_ISCLIENT); pk.pack(config.is_client);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(MessageType::Refresh, tid, n,
|
||||
Blob(buffer.data(), buffer.data() + buffer.size()),
|
||||
@@ -1374,7 +1363,7 @@ void
|
||||
NetworkEngine::sendValueAnnounced(const SockAddr& addr, Tid tid, Value::Id vid) {
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(4+(config.network?1:0)+(config.is_client?1:0));
|
||||
pk.pack_map(4+(config.network?1:0));
|
||||
|
||||
pk.pack(KEY_R); pk.pack_map(3);
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
@@ -1383,13 +1372,10 @@ NetworkEngine::sendValueAnnounced(const SockAddr& addr, Tid tid, Value::Id vid)
|
||||
|
||||
pk.pack(KEY_TID); pk.pack(tid);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_R);
|
||||
pk.pack(KEY_UA); pk.pack(OPENDHT_UA);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
if (config.is_client) {
|
||||
pk.pack(KEY_ISCLIENT); pk.pack(config.is_client);
|
||||
}
|
||||
|
||||
send(addr, buffer.data(), buffer.size());
|
||||
}
|
||||
@@ -1403,7 +1389,7 @@ NetworkEngine::sendError(const SockAddr& addr,
|
||||
{
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(4 + (include_id?1:0) + (config.network?1:0)+(config.is_client?1:0));
|
||||
pk.pack_map(4 + (include_id?1:0) + (config.network?1:0));
|
||||
|
||||
pk.pack(KEY_E); pk.pack_array(2);
|
||||
pk.pack(code);
|
||||
@@ -1416,13 +1402,10 @@ NetworkEngine::sendError(const SockAddr& addr,
|
||||
|
||||
pk.pack(KEY_TID); pk.pack(tid);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_E);
|
||||
pk.pack(KEY_UA); pk.pack(OPENDHT_UA);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
if (config.is_client) {
|
||||
pk.pack(KEY_ISCLIENT); pk.pack(config.is_client);
|
||||
}
|
||||
|
||||
send(addr, buffer.data(), buffer.size());
|
||||
}
|
||||
@@ -1432,11 +1415,11 @@ NetworkEngine::maintainRxBuffer(Tid tid)
|
||||
{
|
||||
auto msg = partial_messages.find(tid);
|
||||
if (msg != partial_messages.end()) {
|
||||
const auto& now = scheduler.time();
|
||||
const auto& now = 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);
|
||||
}
|
||||
}
|
||||
|
||||
+61
-311
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -25,343 +25,93 @@
|
||||
#include <cstring>
|
||||
#define close(x) closesocket(x)
|
||||
#define write(s, b, f) send(s, b, (int)strlen(b), 0)
|
||||
#define _poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
|
||||
#else
|
||||
#include <sys/select.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#define _poll(fds, nfds, timeout) poll(fds, nfds, timeout)
|
||||
#endif
|
||||
|
||||
// number of file descriptors to poll in openSockets()
|
||||
#define NUM_FDS 3
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace dht {
|
||||
namespace net {
|
||||
/*
|
||||
|
||||
int
|
||||
bindSocket(const SockAddr& addr, SockAddr& bound)
|
||||
UdpSocket::UdpSocket(in_port_t port, const std::shared_ptr<Logger>& l)
|
||||
: logger(l)
|
||||
{
|
||||
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));
|
||||
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 (...) {
|
||||
}
|
||||
sockaddr_storage ss;
|
||||
socklen_t ss_len = sizeof(ss);
|
||||
getsockname(sock, (sockaddr*)&ss, &ss_len);
|
||||
bound = {ss, ss_len};
|
||||
return sock;
|
||||
}
|
||||
|
||||
bool
|
||||
setNonblocking(int fd, bool nonblocking)
|
||||
UdpSocket::UdpSocket(const SockAddr& bind4, const SockAddr& bind6, const std::shared_ptr<Logger>& l)
|
||||
: logger(l)
|
||||
{
|
||||
#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 (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);
|
||||
}
|
||||
|
||||
if (s < 0)
|
||||
return EAFNOSUPPORT;
|
||||
|
||||
int flags = 0;
|
||||
#ifdef MSG_CONFIRM
|
||||
if (replied)
|
||||
flags |= MSG_CONFIRM;
|
||||
#else
|
||||
(void) replied;
|
||||
#endif
|
||||
#ifdef MSG_NOSIGNAL
|
||||
flags |= MSG_NOSIGNAL;
|
||||
#endif
|
||||
|
||||
if (sendto(s, (const char*)data, size, flags, dest.get(), dest.getLength()) == -1) {
|
||||
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);
|
||||
if (bind6.isSet()) {
|
||||
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 (...) {
|
||||
}
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
UdpSocket::openSockets(const SockAddr& bind4, const SockAddr& bind6)
|
||||
UdpSocket::~UdpSocket()
|
||||
{
|
||||
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 {
|
||||
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 {
|
||||
struct pollfd fds[NUM_FDS];
|
||||
for (int i = 0; i < NUM_FDS; i++)
|
||||
fds[i].events = POLLIN;
|
||||
constexpr size_t stop_readfd_index = 0, ls4_index = 1, ls6_index = 2;
|
||||
try {
|
||||
while (running) {
|
||||
// ls4 and ls6 can be negative, but this doesn't require any special handling
|
||||
// because poll() will simply ignore them (and set revents to 0) in that case
|
||||
fds[stop_readfd_index].fd = stop_readfd;
|
||||
fds[ls4_index].fd = ls4;
|
||||
fds[ls6_index].fd = ls6;
|
||||
|
||||
int rc = _poll(fds, NUM_FDS, -1);
|
||||
if (rc < 0) {
|
||||
if (errno != EINTR) {
|
||||
if (logger)
|
||||
logger->e("Poll 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 (fds[stop_readfd_index].revents & POLLIN) {
|
||||
if (recv(stop_readfd, (char*)buf.data(), buf.size(), 0) < 0) {
|
||||
if (logger)
|
||||
logger->e("Got stop packet error: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (fds[ls4_index].revents & POLLIN)
|
||||
rc = recvfrom(ls4, (char*)buf.data(), buf.size(), 0, (sockaddr*)&from, &from_len);
|
||||
else if (fds[ls6_index].revents & POLLIN)
|
||||
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;
|
||||
} 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()
|
||||
int UdpSocket::sendTo(const SockAddr& dest, const uint8_t* data, size_t size, bool replied)
|
||||
{
|
||||
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");
|
||||
}
|
||||
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();
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
@@ -167,7 +167,7 @@ Node::toString() const
|
||||
|
||||
std::ostream& operator<< (std::ostream& s, const Node& h)
|
||||
{
|
||||
s << h.id << " " << h.addr.toString();
|
||||
s << h.id << " " << h.addr;
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -188,7 +188,8 @@ NodeExport::msgpack_unpack(msgpack::object o)
|
||||
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};
|
||||
//addr = {(const sockaddr*)maddr.via.bin.ptr, (socklen_t)maddr.via.bin.size};
|
||||
// TODO
|
||||
}
|
||||
|
||||
std::ostream& operator<< (std::ostream& s, const NodeExport& h)
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2023 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,8 +20,8 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
constexpr size_t CLEANUP_MAX_NODES {4 * 1024};
|
||||
constexpr size_t CLEANUP_FREQ {4 * 1024};
|
||||
constexpr size_t CLEANUP_MAX_NODES {1024};
|
||||
constexpr size_t CLEANUP_FREQ {1024};
|
||||
|
||||
NodeCache::~NodeCache()
|
||||
{
|
||||
@@ -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.getFamily()).getNode(id, addr, now, confirm, client, rd);
|
||||
return cache(addr.protocol().family()).getNode(id, addr, now, confirm, client, rd);
|
||||
}
|
||||
|
||||
std::vector<Sp<Node>>
|
||||
|
||||
Loaded 100 of 143 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user