mirror of
https://github.com/savoirfairelinux/opendht.git
synced 2026-11-08 20:37:23 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5593107dc4 | ||
|
|
679ac3badb | ||
|
|
419fc24f1f | ||
|
|
2acdc1f1aa | ||
|
|
631c5f91ec | ||
|
|
81213d9d50 | ||
|
|
5c87e81c42 | ||
|
|
aa0fbbce76 | ||
|
|
ac06dbe45f | ||
|
|
4a97265b22 | ||
|
|
5a75b54e04 | ||
|
|
64185d74d1 | ||
|
|
c16b73b056 |
No files matched your search
@@ -48,12 +48,6 @@ Makefile.in
|
||||
|
||||
# Python
|
||||
*.pyc
|
||||
*.stamp
|
||||
python/opendht.cpp
|
||||
python/setup.py
|
||||
|
||||
# Doxygen
|
||||
doc/Doxyfile
|
||||
|
||||
# git backup files
|
||||
*.orig
|
||||
@@ -61,6 +55,3 @@ doc/Doxyfile
|
||||
# vim swap files
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# build dir
|
||||
build
|
||||
+4
-54
@@ -1,4 +1,3 @@
|
||||
dist: xenial
|
||||
sudo: required
|
||||
|
||||
services:
|
||||
@@ -6,62 +5,13 @@ services:
|
||||
|
||||
language: cpp
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- OPENDHT_TEST_JOB="opendht.classic"
|
||||
- OPENDHT_TEST_JOB="opendht.llvm"
|
||||
- OPENDHT_TEST_JOB="opendht.proxyserver"
|
||||
- OPENDHT_TEST_JOB="opendht.proxyclient"
|
||||
- OPENDHT_TEST_JOB="opendht.push"
|
||||
|
||||
before_install:
|
||||
- |
|
||||
# non llvm builds
|
||||
if [[ "$OPENDHT_TEST_JOB" != *"opendht.llvm"* ]]; then
|
||||
docker pull aberaud/opendht-deps;
|
||||
fi
|
||||
|
||||
- |
|
||||
# classic build
|
||||
if [[ "$OPENDHT_TEST_JOB" == *"opendht.llvm"* ]]; then
|
||||
docker pull aberaud/opendht-deps-llvm
|
||||
fi
|
||||
- docker pull aberaud/opendht-deps
|
||||
- docker pull aberaud/opendht-deps-llvm
|
||||
|
||||
script:
|
||||
- |
|
||||
# classic build
|
||||
if [[ "$OPENDHT_TEST_JOB" == *"opendht.classic"* ]]; then
|
||||
docker build -t opendht -f docker/DockerfileTravis .;
|
||||
fi
|
||||
|
||||
- |
|
||||
# proxy builds
|
||||
if [[ "$OPENDHT_TEST_JOB" != *"opendht.llvm"* ]] && [[ "$OPENDHT_TEST_JOB" != *"opendht.classic"* ]]; then
|
||||
docker build -t opendht-proxy -f docker/DockerfileTravisProxy .;
|
||||
options='-DOPENDHT_SANITIZE=On ';
|
||||
if [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyserver"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.push"* ]]; then
|
||||
options+='-DOPENDHT_PROXY_SERVER=ON ';
|
||||
else
|
||||
options+='-DOPENDHT_PROXY_SERVER=OFF ';
|
||||
fi
|
||||
if [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyclient"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.push"* ]]; then
|
||||
options+='-DOPENDHT_PROXY_CLIENT=ON ';
|
||||
else
|
||||
options+='-DOPENDHT_PROXY_CLIENT=OFF ';
|
||||
fi
|
||||
if [[ "$OPENDHT_TEST_JOB" == *"opendht.push"* ]]; then
|
||||
options+='-DOPENDHT_PUSH_NOTIFICATIONS=ON ';
|
||||
else
|
||||
options+='-DOPENDHT_PUSH_NOTIFICATIONS=OFF ';
|
||||
fi
|
||||
docker run opendht-proxy /bin/sh -c "cd /root/opendht && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=ON -DOPENDHT_LTO=ON -DOPENDHT_TESTS=ON $options .. && make -j8 && ./opendht_unit_tests && make install";
|
||||
fi
|
||||
|
||||
- |
|
||||
# llvm build
|
||||
if [[ "$OPENDHT_TEST_JOB" == *"opendht.llvm"* ]]; then
|
||||
docker build -f docker/DockerfileTravisLlvm .
|
||||
fi
|
||||
- docker build -f docker/DockerfileTravis .
|
||||
- docker build -f docker/DockerfileTravisLlvm .
|
||||
|
||||
notifications:
|
||||
email:
|
||||
|
||||
+21
-123
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required (VERSION 3.1)
|
||||
project (opendht)
|
||||
set (opendht_VERSION_MAJOR 1)
|
||||
set (opendht_VERSION_MINOR 8.0)
|
||||
set (opendht_VERSION_MINOR 4.0)
|
||||
set (opendht_VERSION ${opendht_VERSION_MAJOR}.${opendht_VERSION_MINOR})
|
||||
set (PACKAGE_VERSION ${opendht_VERSION})
|
||||
set (VERSION "${opendht_VERSION}")
|
||||
@@ -16,12 +16,6 @@ option (OPENDHT_SYSTEMD "Install systemd module" OFF)
|
||||
option (OPENDHT_ARGON2 "Use included argon2 sources" OFF)
|
||||
option (OPENDHT_LTO "Build with LTO" OFF)
|
||||
option (OPENDHT_SANITIZE "Build with address sanitizer and stack protector" OFF)
|
||||
option (OPENDHT_PROXY_SERVER "Enable DHT proxy server, use Restbed and jsoncpp" OFF)
|
||||
option (OPENDHT_PUSH_NOTIFICATIONS "Enable push notifications support" OFF)
|
||||
option (OPENDHT_PROXY_SERVER_IDENTITY "Allow clients to use the node identity" OFF)
|
||||
option (OPENDHT_PROXY_CLIENT "Enable DHT proxy client, use Restbed and jsoncpp" OFF)
|
||||
option (OPENDHT_INDEX "Build DHT indexation feature" OFF)
|
||||
option (OPENDHT_TESTS "Add unit tests executable" OFF)
|
||||
|
||||
find_package(Doxygen)
|
||||
option (OPENDHT_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" ${DOXYGEN_FOUND})
|
||||
@@ -30,6 +24,8 @@ option (OPENDHT_DOCUMENTATION "Create and install the HTML based API documentati
|
||||
list (APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
||||
find_package (Threads)
|
||||
find_package (PkgConfig)
|
||||
pkg_search_module(libuv libuv)
|
||||
pkg_search_module(libsodium libsodium)
|
||||
find_package (GnuTLS 3.3 REQUIRED)
|
||||
pkg_search_module (Nettle nettle)
|
||||
find_package (Msgpack 1.2 REQUIRED)
|
||||
@@ -37,7 +33,6 @@ if (OPENDHT_TOOLS)
|
||||
find_package (Readline 6 REQUIRED)
|
||||
endif ()
|
||||
if (NOT OPENDHT_ARGON2)
|
||||
find_package(PkgConfig)
|
||||
pkg_search_module(argon2 libargon2)
|
||||
if (argon2_FOUND)
|
||||
message("Argon2 found " ${argon2_req})
|
||||
@@ -48,24 +43,8 @@ if (NOT OPENDHT_ARGON2)
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
pkg_search_module(Jsoncpp jsoncpp)
|
||||
if (Jsoncpp_FOUND)
|
||||
add_definitions(-DOPENDHT_JSONCPP)
|
||||
list (APPEND opendht_SOURCES
|
||||
src/base64.h
|
||||
src/base64.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if (OPENDHT_PROXY_SERVER OR OPENDHT_PROXY_CLIENT)
|
||||
find_package(Restbed REQUIRED)
|
||||
if (NOT Jsoncpp_FOUND)
|
||||
message(SEND_ERROR "Jsoncpp is required for DHT proxy support")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Build flags
|
||||
set (CMAKE_CXX_STANDARD 11)
|
||||
set (CMAKE_CXX_STANDARD 14)
|
||||
set (CMAKE_CXX_STANDARD_REQUIRED on)
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-return-type -Wall -Wextra -Wnon-virtual-dtor -pedantic-errors -fvisibility=hidden")
|
||||
if (OPENDHT_SANITIZE)
|
||||
@@ -99,14 +78,13 @@ endif ()
|
||||
if (Nettle_INCLUDE_DIRS)
|
||||
include_directories (SYSTEM "${Nettle_INCLUDE_DIRS}")
|
||||
endif ()
|
||||
if (Restbed_INCLUDE_DIR)
|
||||
include_directories (SYSTEM "${Restbed_INCLUDE_DIR}")
|
||||
if (libuv_INCLUDE_DIRS)
|
||||
include_directories (SYSTEM "${libuv_INCLUDE_DIRS}")
|
||||
endif ()
|
||||
if (Jsoncpp_INCLUDE_DIRS)
|
||||
include_directories (SYSTEM "${Jsoncpp_INCLUDE_DIRS}")
|
||||
if (libsodium_INCLUDE_DIRS)
|
||||
include_directories (SYSTEM "${libsodium_INCLUDE_DIRS}")
|
||||
endif ()
|
||||
link_directories (${Nettle_LIBRARY_DIRS})
|
||||
link_directories (${Jsoncpp_LIBRARY_DIRS})
|
||||
link_directories (${libuv_LIBRARY_DIRS} ${libsodium_LIBRARY_DIRS} ${Nettle_LIBRARY_DIRS})
|
||||
include_directories (
|
||||
./
|
||||
include/
|
||||
@@ -133,13 +111,9 @@ list (APPEND opendht_SOURCES
|
||||
src/node.cpp
|
||||
src/value.cpp
|
||||
src/dht.cpp
|
||||
src/op_cache.cpp
|
||||
src/storage.h
|
||||
src/listener.h
|
||||
src/search.h
|
||||
src/value_cache.h
|
||||
src/op_cache.h
|
||||
src/net.h
|
||||
src/parsed_message.h
|
||||
src/request.h
|
||||
src/callbacks.cpp
|
||||
@@ -148,6 +122,7 @@ list (APPEND opendht_SOURCES
|
||||
src/network_engine.cpp
|
||||
src/securedht.cpp
|
||||
src/dhtrunner.cpp
|
||||
src/indexation/pht.cpp
|
||||
src/log.cpp
|
||||
)
|
||||
|
||||
@@ -162,66 +137,20 @@ list (APPEND opendht_HEADERS
|
||||
include/opendht/node.h
|
||||
include/opendht/value.h
|
||||
include/opendht/dht.h
|
||||
include/opendht/dht_interface.h
|
||||
include/opendht/callbacks.h
|
||||
include/opendht/routing_table.h
|
||||
include/opendht/node_cache.h
|
||||
include/opendht/network_engine.h
|
||||
include/opendht/net.h
|
||||
include/opendht/scheduler.h
|
||||
include/opendht/rate_limiter.h
|
||||
include/opendht/securedht.h
|
||||
include/opendht/log.h
|
||||
include/opendht/log_enable.h
|
||||
include/opendht.h
|
||||
include/opendht/indexation/pht.h
|
||||
)
|
||||
|
||||
if (OPENDHT_INDEX)
|
||||
list (APPEND opendht_SOURCES src/indexation/pht.cpp)
|
||||
list (APPEND opendht_HEADERS include/opendht/indexation/pht.h)
|
||||
add_definitions(-DOPENDHT_INDEXATION)
|
||||
endif()
|
||||
|
||||
if (OPENDHT_PROXY_SERVER)
|
||||
add_definitions(-DOPENDHT_PROXY_SERVER=true)
|
||||
if (OPENDHT_PROXY_SERVER_IDENTITY)
|
||||
add_definitions(-DOPENDHT_PROXY_SERVER_IDENTITY=true)
|
||||
else ()
|
||||
add_definitions(-DOPENDHT_PROXY_SERVER_IDENTITY=false)
|
||||
endif()
|
||||
list (APPEND opendht_HEADERS
|
||||
include/opendht/dht_proxy_server.h
|
||||
)
|
||||
list (APPEND opendht_SOURCES
|
||||
src/dht_proxy_server.cpp
|
||||
)
|
||||
else ()
|
||||
add_definitions(-DENABLE_PROXY_SERVER=false)
|
||||
endif ()
|
||||
|
||||
if (OPENDHT_PROXY_CLIENT)
|
||||
add_definitions(-DOPENDHT_PROXY_CLIENT=true)
|
||||
list (APPEND opendht_HEADERS
|
||||
include/opendht/dht_proxy_client.h
|
||||
)
|
||||
list (APPEND opendht_SOURCES
|
||||
src/dht_proxy_client.cpp
|
||||
)
|
||||
else ()
|
||||
add_definitions(-DOPENDHT_PROXY_CLIENT=false)
|
||||
endif ()
|
||||
|
||||
if (OPENDHT_PROXY_SERVER OR OPENDHT_PROXY_CLIENT)
|
||||
if (OPENDHT_PUSH_NOTIFICATIONS)
|
||||
message("Using push notification")
|
||||
add_definitions(-DOPENDHT_PUSH_NOTIFICATIONS=true)
|
||||
else ()
|
||||
add_definitions(-DOPENDHT_PUSH_NOTIFICATIONS=false)
|
||||
endif ()
|
||||
list (APPEND opendht_HEADERS
|
||||
include/opendht/proxy.h
|
||||
)
|
||||
endif ()
|
||||
|
||||
if(OPENDHT_ARGON2)
|
||||
# make sure argon2 submodule is up to date and initialized
|
||||
message("Initializing Argon2 submodule")
|
||||
@@ -246,14 +175,16 @@ if (OPENDHT_STATIC)
|
||||
${opendht_HEADERS}
|
||||
)
|
||||
set_target_properties (opendht-static PROPERTIES OUTPUT_NAME "opendht")
|
||||
if (OPENDHT_LTO)
|
||||
target_link_libraries(opendht-static -flto -fuse-linker-plugin)
|
||||
endif ()
|
||||
if (OPENDHT_ARGON2)
|
||||
target_include_directories(opendht-static SYSTEM PRIVATE argon2)
|
||||
else ()
|
||||
target_link_libraries(opendht-static ${argon2_LIBRARIES})
|
||||
target_include_directories(opendht-static SYSTEM PRIVATE ${argon2_INCLUDE_DIRS})
|
||||
endif ()
|
||||
target_link_libraries(opendht-static
|
||||
PRIVATE ${Restbed_LIBRARY} ${argon2_LIBRARIES}
|
||||
PUBLIC ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} ${Jsoncpp_LIBRARIES})
|
||||
target_link_libraries(opendht-static ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} ${libuv_LIBRARIES} ${libsodium_LIBRARIES})
|
||||
install (TARGETS opendht-static DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht)
|
||||
endif ()
|
||||
|
||||
@@ -265,13 +196,16 @@ if (OPENDHT_SHARED)
|
||||
set_target_properties (opendht PROPERTIES IMPORT_SUFFIX "_import.lib")
|
||||
set_target_properties (opendht PROPERTIES SOVERSION ${opendht_VERSION_MAJOR} VERSION ${opendht_VERSION})
|
||||
target_compile_definitions(opendht PRIVATE OPENDHT_BUILD)
|
||||
if (OPENDHT_LTO)
|
||||
target_link_libraries(opendht PRIVATE -flto -fuse-linker-plugin)
|
||||
endif ()
|
||||
if (OPENDHT_ARGON2)
|
||||
target_include_directories(opendht SYSTEM PRIVATE argon2)
|
||||
else ()
|
||||
target_link_libraries(opendht PRIVATE ${argon2_LIBRARIES})
|
||||
target_include_directories(opendht SYSTEM PRIVATE ${argon2_INCLUDE_DIRS})
|
||||
endif ()
|
||||
target_link_libraries(opendht PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} ${Restbed_LIBRARY} ${Jsoncpp_LIBRARIES})
|
||||
target_link_libraries(opendht PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} ${libuv_LIBRARIES} ${libsodium_LIBRARIES})
|
||||
install (TARGETS opendht DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht)
|
||||
endif ()
|
||||
|
||||
@@ -302,39 +236,3 @@ install (DIRECTORY include DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/opendht.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
install (EXPORT opendht DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/opendht FILE opendhtConfig.cmake)
|
||||
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/opendhtConfigVersion.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/opendht)
|
||||
|
||||
# Unit tests
|
||||
IF(OPENDHT_TESTS)
|
||||
FIND_PACKAGE(Cppunit REQUIRED)
|
||||
# unit testing
|
||||
list (APPEND test_FILES
|
||||
tests/infohashtester.h
|
||||
tests/infohashtester.cpp
|
||||
tests/cryptotester.h
|
||||
tests/cryptotester.cpp
|
||||
tests/dhtrunnertester.h
|
||||
tests/dhtrunnertester.cpp
|
||||
)
|
||||
if (OPENDHT_PROXY_SERVER AND OPENDHT_PROXY_CLIENT)
|
||||
list (APPEND test_FILES
|
||||
tests/dhtproxytester.h
|
||||
tests/dhtproxytester.cpp
|
||||
)
|
||||
endif()
|
||||
add_executable(opendht_unit_tests
|
||||
tests/tests_runner.cpp
|
||||
${test_FILES}
|
||||
)
|
||||
if (OPENDHT_SHARED)
|
||||
TARGET_LINK_LIBRARIES(opendht_unit_tests opendht)
|
||||
else ()
|
||||
TARGET_LINK_LIBRARIES(opendht_unit_tests opendht-static)
|
||||
endif ()
|
||||
TARGET_LINK_LIBRARIES(opendht_unit_tests
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${CPPUNIT_LIBRARIES}
|
||||
${GNUTLS_LIBRARIES}
|
||||
)
|
||||
enable_testing()
|
||||
add_test(TEST opendht_unit_tests)
|
||||
ENDIF()
|
||||
+7
-18
@@ -19,14 +19,11 @@
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="wingetopt.c" />
|
||||
<ClCompile Include="..\src\base64.cpp" />
|
||||
<ClCompile Include="..\src\callbacks.cpp" />
|
||||
<ClCompile Include="..\src\crypto.cpp" />
|
||||
<ClCompile Include="..\src\default_types.cpp" />
|
||||
<ClCompile Include="..\src\dht.cpp" />
|
||||
<ClCompile Include="..\src\dhtrunner.cpp" />
|
||||
<ClCompile Include="..\src\dht_proxy_server.cpp" />
|
||||
<ClCompile Include="..\src\indexation\pht.cpp" />
|
||||
<ClCompile Include="..\src\infohash.cpp" />
|
||||
<ClCompile Include="..\src\log.cpp" />
|
||||
@@ -39,16 +36,12 @@
|
||||
<ClCompile Include="..\src\value.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="unistd.h" />
|
||||
<ClInclude Include="wingetopt.h" />
|
||||
<ClInclude Include="..\include\opendht.h" />
|
||||
<ClInclude Include="..\include\opendht\callbacks.h" />
|
||||
<ClInclude Include="..\include\opendht\crypto.h" />
|
||||
<ClInclude Include="..\include\opendht\def.h" />
|
||||
<ClInclude Include="..\include\opendht\default_types.h" />
|
||||
<ClInclude Include="..\include\opendht\dht.h" />
|
||||
<ClInclude Include="..\include\opendht\dhtrunner.h" />
|
||||
<ClInclude Include="..\include\opendht\dht_proxy_server.h" />
|
||||
<ClInclude Include="..\include\opendht\indexation\pht.h" />
|
||||
<ClInclude Include="..\include\opendht\infohash.h" />
|
||||
<ClInclude Include="..\include\opendht\log.h" />
|
||||
@@ -57,18 +50,14 @@
|
||||
<ClInclude Include="..\include\opendht\node.h" />
|
||||
<ClInclude Include="..\include\opendht\node_cache.h" />
|
||||
<ClInclude Include="..\include\opendht\rate_limiter.h" />
|
||||
<ClInclude Include="..\include\opendht\request.h" />
|
||||
<ClInclude Include="..\include\opendht\rng.h" />
|
||||
<ClInclude Include="..\include\opendht\routing_table.h" />
|
||||
<ClInclude Include="..\include\opendht\scheduler.h" />
|
||||
<ClInclude Include="..\include\opendht\securedht.h" />
|
||||
<ClInclude Include="..\include\opendht\sockaddr.h" />
|
||||
<ClInclude Include="..\include\opendht\utils.h" />
|
||||
<ClInclude Include="..\include\opendht\value.h" />
|
||||
<ClInclude Include="..\src\base64.h" />
|
||||
<ClInclude Include="..\src\listener.h" />
|
||||
<ClInclude Include="..\src\net.h" />
|
||||
<ClInclude Include="..\src\parsed_message.h" />
|
||||
<ClInclude Include="..\src\request.h" />
|
||||
<ClInclude Include="..\src\search.h" />
|
||||
<ClInclude Include="..\src\storage.h" />
|
||||
</ItemGroup>
|
||||
@@ -146,7 +135,7 @@
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\include;..\include\opendht;..\..\msgpack-c\include;</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4800;4101;4267;4244;4503;</DisableSpecificWarnings>
|
||||
<AdditionalOptions>-D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions)</AdditionalOptions>
|
||||
<SuppressStartupBanner>false</SuppressStartupBanner>
|
||||
@@ -169,7 +158,7 @@ copy ..\include\opendht.h $(OutDir)\include\</Command>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\include;..\include\opendht;..\..\msgpack-c\include;</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4800;4101;4267;4244;4503;</DisableSpecificWarnings>
|
||||
<AdditionalOptions>-D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
@@ -193,7 +182,7 @@ copy ..\include\opendht.h $(OutDir)\include\</Command>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\include;..\include\opendht;..\..\msgpack-c\include;</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4800;4101;4267;4244;4503;</DisableSpecificWarnings>
|
||||
<AdditionalOptions>-D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions)</AdditionalOptions>
|
||||
<ProgramDataBaseFileName>$(OutDir)\lib\x86\$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
@@ -222,9 +211,9 @@ copy ..\include\opendht.h $(OutDir)\include\</Command>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)contrib\build\include;$(ProjectDir)..\include;$(ProjectDir)..\include\opendht;$(ProjectDir)contrib\build\msgpack-c\include;$(ProjectDir)contrib\build\argon2\include;$(ProjectDir)..\..\include;$(ProjectDir)..\..\argon2\include</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4800;4101;4267;4244;4503;4273;</DisableSpecificWarnings>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)contrib\build\include;$(ProjectDir)..\include;$(ProjectDir)..\include\opendht;$(ProjectDir)contrib\build\msgpack-c\include;$(ProjectDir)contrib\build\argon2\include</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4800;4101;4267;4244;4503;</DisableSpecificWarnings>
|
||||
<AdditionalOptions>-D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions)</AdditionalOptions>
|
||||
<ProgramDataBaseFileName>$(OutDir)\lib\x64\$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
<ClCompile Include="..\src\node_cache.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\indexation\pht.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\routing_table.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -43,102 +46,80 @@
|
||||
<ClCompile Include="..\src\value.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\base64.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\dht_proxy_server.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\indexation\pht.cpp">
|
||||
<Filter>Source Files\indexation</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="wingetopt.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\include\opendht\def.h" />
|
||||
<ClInclude Include="..\include\opendht\sockaddr.h" />
|
||||
<ClInclude Include="..\src\net.h" />
|
||||
<ClInclude Include="..\src\parsed_message.h" />
|
||||
<ClInclude Include="..\src\base64.h">
|
||||
<Filter>Source Files</Filter>
|
||||
<ClInclude Include="..\include\opendht\callbacks.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\crypto.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\default_types.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\dht.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\dhtrunner.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\infohash.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\listener.h">
|
||||
<Filter>Source Files</Filter>
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\request.h">
|
||||
<Filter>Source Files</Filter>
|
||||
<ClInclude Include="..\include\opendht\log.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\search.h">
|
||||
<Filter>Source Files</Filter>
|
||||
<ClInclude Include="..\include\opendht\log_enable.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\storage.h">
|
||||
<Filter>Source Files</Filter>
|
||||
<ClInclude Include="..\include\opendht\network_engine.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\node.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\node_cache.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\callbacks.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\crypto.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\default_types.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\dht.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\dht_proxy_server.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\dhtrunner.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\infohash.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\log.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\log_enable.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\network_engine.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\node.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\node_cache.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
<ClInclude Include="..\include\opendht\indexation\pht.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\rate_limiter.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\request.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\rng.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\routing_table.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\scheduler.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\search.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\securedht.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\storage.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\utils.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\value.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\indexation\pht.h">
|
||||
<Filter>Header Files\opendht\indexation</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="unistd.h" />
|
||||
<ClInclude Include="wingetopt.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
@@ -147,14 +128,5 @@
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{d1ab5bfe-3ab1-45ee-9324-b4b071887668}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\indexation">
|
||||
<UniqueIdentifier>{939134d3-d5a2-4458-bca8-a10ea9042ea2}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\opendht">
|
||||
<UniqueIdentifier>{0b46e2d0-4874-404c-bae3-fae8d49b85b8}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\opendht\indexation">
|
||||
<UniqueIdentifier>{03bd5139-2ae5-41b0-a77b-944814e1b2b6}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -12,13 +12,7 @@ if USE_CYTHON
|
||||
SUBDIRS += python
|
||||
endif
|
||||
|
||||
if ENABLE_TESTS
|
||||
SUBDIRS += tests
|
||||
endif
|
||||
|
||||
if HAVE_DOXYGEN
|
||||
SUBDIRS += doc
|
||||
endif
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
|
||||
@@ -1,29 +1,24 @@
|
||||
<img src="https://raw.githubusercontent.com/savoirfairelinux/opendht/master/resources/opendht_logo_512.png" width="100" align="right">
|
||||
<br>
|
||||
<h1 style="margin-top:10px">
|
||||
<a id="user-content-opendht-" class="anchor" href="/savoirfairelinux/opendht/blob/master/README.md#opendht-" aria-hidden="true"></a>OpenDHT
|
||||
<a id="user-content-opendht-" class="anchor" href="/savoirfairelinux/opendht/blob/master/README.md#opendht-" aria-hidden="true"></a>OpenDHT
|
||||
</h1>
|
||||
|
||||
A lightweight C++11 Distributed Hash Table implementation.
|
||||
A lightweight C++11 Distributed Hash Table implementation originally based on https://github.com/jech/dht by Juliusz Chroboczek.
|
||||
|
||||
OpenDHT provides an easy to use distributed in-memory data store.
|
||||
Every node in the network can read and write values to the store.
|
||||
Values are distributed over the network, with redundancy.
|
||||
|
||||
* Lightweight and scalable, designed for large networks and small devices
|
||||
* High resilience to network disruption
|
||||
* Public key cryptography layer providing optional data signature and encryption (using GnuTLS)
|
||||
* IPv4 and IPv6 support
|
||||
* Clean and powerful C++11 map API
|
||||
* Python 3 bindings
|
||||
* REST API
|
||||
* Light and fast C++11 Kademlia DHT library.
|
||||
* Distributed shared key->value data-store
|
||||
* Clean and powerful distributed map API with storage of arbitrary binary values of up to 56 KB.
|
||||
* Optional public key cryptography layer providing data signature and encryption (using GnuTLS).
|
||||
* IPv4 and IPv6 support.
|
||||
* Python binding.
|
||||
|
||||
## Documentation
|
||||
See the wiki: <https://github.com/savoirfairelinux/opendht/wiki>
|
||||
|
||||
#### How-to build and install
|
||||
|
||||
Build instructions: <https://github.com/savoirfairelinux/opendht/wiki/Build-the-library>
|
||||
Build instructions : <https://github.com/savoirfairelinux/opendht/wiki/Build-the-library>
|
||||
|
||||
#### How-to build a simple client app
|
||||
```bash
|
||||
@@ -96,27 +91,23 @@ 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.
|
||||
- (optional) restbed used for the REST API. commit fb84213e170bc171fecd825a8e47ed9f881a12cd (https://github.com/AmarOk1412/restbed/tree/async_read_until)
|
||||
- (optional) jsoncpp 1.7.4-3+, used for the REST API.
|
||||
- Build tested with GCC 5.2+ (GNU/Linux, Windows with MinGW), Clang/LLVM (GNU/Linux, Android, macOS, iOS).
|
||||
- Build tested with GCC 4.8+ (GNU/Linux, Android, Windows with MinGW), Clang/LLVM (Linux, macOS).
|
||||
- Build tested with Microsoft Visual Studio 2015
|
||||
|
||||
## Contact
|
||||
|
||||
IRC: join us on Freenode at [`#opendht`](https://webchat.freenode.net/?channels=%23opendht).
|
||||
|
||||
## License
|
||||
Copyright (C) 2014-2018 Savoir-faire Linux Inc.
|
||||
## Licence
|
||||
Copyright (C) 2014-2017 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.
|
||||
|
||||
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.
|
||||
|
||||
See COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html for the full GPLv3 license.
|
||||
See COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html for the full GPLv3 licence.
|
||||
|
||||
## Acknowledgements
|
||||
This project was originally based on https://github.com/jech/dht by Juliusz Chroboczek.
|
||||
It is independent from another project called OpenDHT (Sean Rhea. Ph.D. Thesis, 2005), now extinct.
|
||||
This project is independent from another project called OpenDHT (Sean Rhea. Ph.D. Thesis, 2005), now extinct.
|
||||
|
||||
## Donations
|
||||
We gratefully accept Bitcoin donations to support OpenDHT development at: `bitcoin:3EykSd1An888efq4Bq3KaV3hJ3JQ4FPnwm`.
|
||||
+1
-1
Submodule argon2 updated: 670229c849...6f5427fb67.
@@ -1,28 +0,0 @@
|
||||
INCLUDE(FindPkgConfig)
|
||||
PKG_CHECK_MODULES(PC_CPPUNIT "cppunit")
|
||||
|
||||
FIND_PATH(CPPUNIT_INCLUDE_DIRS
|
||||
NAMES cppunit/TestCase.h
|
||||
HINTS ${PC_CPPUNIT_INCLUDE_DIR}
|
||||
${CMAKE_INSTALL_PREFIX}/include
|
||||
PATHS
|
||||
/usr/local/include
|
||||
/usr/include
|
||||
)
|
||||
|
||||
FIND_LIBRARY(CPPUNIT_LIBRARIES
|
||||
NAMES cppunit
|
||||
HINTS ${PC_CPPUNIT_LIBDIR}
|
||||
${CMAKE_INSTALL_PREFIX}/lib
|
||||
${CMAKE_INSTALL_PREFIX}/lib64
|
||||
PATHS
|
||||
${CPPUNIT_INCLUDE_DIRS}/../lib
|
||||
/usr/local/lib
|
||||
/usr/lib
|
||||
)
|
||||
|
||||
LIST(APPEND CPPUNIT_LIBRARIES ${CMAKE_DL_LIBS})
|
||||
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(CPPUNIT DEFAULT_MSG CPPUNIT_LIBRARIES CPPUNIT_INCLUDE_DIRS)
|
||||
MARK_AS_ADVANCED(CPPUNIT_LIBRARIES CPPUNIT_INCLUDE_DIRS)
|
||||
@@ -1,16 +0,0 @@
|
||||
if(NOT Restbed_FOUND)
|
||||
find_path (Restbed_INCLUDE_DIR restbed
|
||||
HINTS
|
||||
"/usr/include"
|
||||
"/usr/local/include"
|
||||
"/opt/local/include")
|
||||
find_library(Restbed_LIBRARY restbed
|
||||
HINTS ${Restbed_ROOT_DIR} PATH_SUFFIXES lib)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Restbed DEFAULT_MSG Restbed_LIBRARY Restbed_INCLUDE_DIR)
|
||||
if (Restbed_INCLUDE_DIR)
|
||||
set(Restbed_FOUND TRUE)
|
||||
set(Restbed_LIBRARIES ${Restbed_LIBRARY})
|
||||
set(Restbed_INCLUDE_DIRS ${Restbed_INCLUDE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
+3
-71
@@ -1,6 +1,6 @@
|
||||
dnl define macros
|
||||
m4_define([opendht_major_version], 1)
|
||||
m4_define([opendht_minor_version], 8)
|
||||
m4_define([opendht_minor_version], 4)
|
||||
m4_define([opendht_patch_version], 0)
|
||||
m4_define([opendht_version],
|
||||
[opendht_major_version.opendht_minor_version.opendht_patch_version])
|
||||
@@ -25,20 +25,13 @@ AC_PROG_CXX
|
||||
AM_PROG_AR
|
||||
|
||||
dnl Check for logs
|
||||
AC_ARG_ENABLE([logs], [AS_HELP_STRING([--disable-logs], [Disable DHT logs])])
|
||||
AC_ARG_ENABLE([logs], [AS_HELP_STRING([--disable-logs], [Disble DHT logs])])
|
||||
AS_IF([test "x$enable_logs" != "xno"], [
|
||||
AC_DEFINE([OPENDHT_LOG], [true], [Define if DHT logs are enabled])
|
||||
], [
|
||||
AC_DEFINE([OPENDHT_LOG], [false], [Define if DHT logs are enabled])
|
||||
])
|
||||
|
||||
dnl Check for indexation
|
||||
AC_ARG_ENABLE([indexation], [AS_HELP_STRING([--disable-indexation], [Disable DHT indexation])])
|
||||
AM_CONDITIONAL(ENABLE_INDEXATION, test x$enable_indexation != "xno")
|
||||
AS_IF([test "x$enable_indexation" != "xno"], [
|
||||
AC_DEFINE([OPENDHT_INDEXATION], [1], [Define if DHT indexation is enabled])
|
||||
])
|
||||
|
||||
dnl Check for Doxygen
|
||||
AC_ARG_ENABLE([doc], AS_HELP_STRING([--enable-doc], [Enable documentation generation (doxygen)]))
|
||||
AS_IF([test "x$enable_doc" = "xyes"], [
|
||||
@@ -101,51 +94,9 @@ LT_LANG(C++)
|
||||
AX_CXX_COMPILE_STDCXX(11,[noext],[mandatory])
|
||||
|
||||
PKG_PROG_PKG_CONFIG()
|
||||
|
||||
AC_ARG_ENABLE([proxy_server], AS_HELP_STRING([--enable-proxy-server], [Enable proxy server ability]), proxy_server=yes, proxy_server=no)
|
||||
AM_CONDITIONAL(ENABLE_PROXY_SERVER, test x$proxy_server == xyes)
|
||||
|
||||
AC_ARG_ENABLE([push_notifications], AS_HELP_STRING([--enable-push-notifications], [Enable push notifications support]), push_notifications=yes, push_notifications=no)
|
||||
AM_CONDITIONAL(ENABLE_PUSH_NOTIFICATIONS, test x$push_notifications == xyes)
|
||||
|
||||
AC_ARG_ENABLE([proxy_server_identity], AS_HELP_STRING([--enable-proxy-server-identity],
|
||||
[Enable proxy server ability]), proxy_server_identity=yes, proxy_server_identity=no)
|
||||
AM_CONDITIONAL(ENABLE_PROXY_SERVER_IDENTITY, test x$proxy_server_identity == xyes -a x$proxy_server == xyes)
|
||||
AC_ARG_ENABLE([proxy_server_identity], AS_HELP_STRING([--enable-proxy-server-identity],
|
||||
[Enable proxy server ability]), proxy_server_identity=yes, proxy_server_identity=no)
|
||||
|
||||
AC_ARG_ENABLE([proxy_client], AS_HELP_STRING([--enable-proxy-client], [Enable proxy client ability]), proxy_client=yes, proxy_client=no)
|
||||
AM_CONDITIONAL(ENABLE_PROXY_CLIENT, test x$proxy_client == xyes)
|
||||
|
||||
AC_ARG_ENABLE([tests], AS_HELP_STRING([--enable-tests], [Enable tests]), build_tests=yes, build_tests=no)
|
||||
AM_CONDITIONAL(ENABLE_TESTS, test x$build_tests == xyes)
|
||||
AM_COND_IF([ENABLE_TESTS], [
|
||||
PKG_CHECK_MODULES([CppUnit], [cppunit >= 1.12])
|
||||
])
|
||||
|
||||
AM_CONDITIONAL(PROXY_CLIENT_OR_SERVER, test x$proxy_client == xyes | test x$proxy_server == xyes)
|
||||
|
||||
PKG_CHECK_MODULES([Nettle], [nettle >= 2.4])
|
||||
PKG_CHECK_MODULES([GnuTLS], [gnutls >= 3.3])
|
||||
PKG_CHECK_MODULES([MsgPack], [msgpack >= 1.2])
|
||||
|
||||
AC_ARG_WITH([jsoncpp], AS_HELP_STRING([--without-jsoncpp], [Build without JsonCpp support]))
|
||||
AS_IF([test "x$with_jsoncpp" != "xno"],
|
||||
[PKG_CHECK_MODULES([JsonCpp], [jsoncpp >= 1.7.2], [have_jsoncpp=yes], [have_jsoncpp=no])],
|
||||
[have_jsoncpp=no])
|
||||
AS_IF([test "x$have_jsoncpp" = "xyes"], [
|
||||
AC_MSG_NOTICE([Using JsonCpp])
|
||||
CPPFLAGS+=" -DOPENDHT_JSONCPP=1"
|
||||
], [
|
||||
AC_MSG_NOTICE([Not using JsonCpp])
|
||||
AM_COND_IF(PROXY_CLIENT_OR_SERVER, AC_MSG_ERROR(["JsonCpp is required for proxy/push notification support"]))
|
||||
])
|
||||
|
||||
AM_COND_IF([PROXY_CLIENT_OR_SERVER], [
|
||||
AC_CHECK_LIB(restbed, exit,, AC_MSG_ERROR([Missing restbed files]))
|
||||
LDFLAGS="${LDFLAGS} -lrestbed"
|
||||
])
|
||||
|
||||
CXXFLAGS="${CXXFLAGS} -DMSGPACK_DISABLE_LEGACY_NIL -DMSGPACK_DISABLE_LEGACY_CONVERT"
|
||||
|
||||
dnl Check for Argon2
|
||||
@@ -177,25 +128,7 @@ AM_COND_IF([ENABLE_TOOLS], [
|
||||
])
|
||||
])
|
||||
|
||||
AM_COND_IF(ENABLE_PROXY_SERVER,
|
||||
[CPPFLAGS+=" -DOPENDHT_PROXY_SERVER=true"],
|
||||
[CPPFLAGS+=" -DOPENDHT_PROXY_SERVER=false"])
|
||||
|
||||
AM_COND_IF(ENABLE_PROXY_CLIENT,
|
||||
[CPPFLAGS+=" -DOPENDHT_PROXY_CLIENT=true"],
|
||||
[CPPFLAGS+=" -DOPENDHT_PROXY_CLIENT=false"])
|
||||
|
||||
AM_COND_IF(ENABLE_PUSH_NOTIFICATIONS,
|
||||
[CPPFLAGS+=" -DOPENDHT_PUSH_NOTIFICATIONS=true"],
|
||||
[CPPFLAGS+=" -DOPENDHT_PUSH_NOTIFICATIONS=false"])
|
||||
|
||||
AM_COND_IF(ENABLE_PROXY_SERVER_IDENTITY,
|
||||
[CPPFLAGS+=" -DOPENDHT_PROXY_SERVER_IDENTITY=true"],
|
||||
[CPPFLAGS+=" -DOPENDHT_PROXY_SERVER_IDENTITY=false"])
|
||||
|
||||
AM_COND_IF([HAVE_DOXYGEN], [
|
||||
AC_CONFIG_FILES([doc/Doxyfile doc/Makefile])
|
||||
])
|
||||
AC_CONFIG_FILES([doc/Doxyfile doc/Makefile])
|
||||
|
||||
dnl Configure setup.py if we build the python module
|
||||
AM_COND_IF([USE_CYTHON], [
|
||||
@@ -209,6 +142,5 @@ AM_COND_IF([USE_CYTHON], [
|
||||
AC_CONFIG_FILES([Makefile
|
||||
src/Makefile
|
||||
tools/Makefile
|
||||
tests/Makefile
|
||||
opendht.pc])
|
||||
AC_OUTPUT
|
||||
+2
-17
@@ -1,18 +1,3 @@
|
||||
FROM ubuntu:16.04
|
||||
FROM ubuntu:17.04
|
||||
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
RUN apt-get update && apt-get install -y build-essential cmake git wget libncurses5-dev libreadline-dev nettle-dev libgnutls28-dev libuv1-dev cython3 python3-dev libcppunit-dev libjsoncpp-dev libasio-dev libssl-dev python3-setuptools && apt-get clean
|
||||
|
||||
# build restbed from sources
|
||||
RUN git clone --recursive https://github.com/corvusoft/restbed.git \
|
||||
&& cd restbed && mkdir build && cd build \
|
||||
&& cmake -DBUILD_TESTS=NO -DBUILD_EXAMPLES=NO -DBUILD_SSL=NO -DBUILD_SHARED=YES -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_INSTALL_LIBDIR=lib .. \
|
||||
&& make -j8 install \
|
||||
&& cd .. && rm -rf restbed
|
||||
|
||||
#build msgpack from source
|
||||
RUN wget https://github.com/msgpack/msgpack-c/releases/download/cpp-2.1.5/msgpack-2.1.5.tar.gz \
|
||||
&& tar -xzf msgpack-2.1.5.tar.gz \
|
||||
&& cd msgpack-2.1.5 && mkdir build && cd build \
|
||||
&& cmake -DMSGPACK_CXX11=ON -DMSGPACK_BUILD_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX=/usr .. \
|
||||
&& make -j8 && make install \
|
||||
&& cd ../.. && rm -rf msgpack-2.1.5 msgpack-2.1.5.tar.gz
|
||||
RUN apt-get update && apt-get install -y build-essential cmake git wget libncurses5-dev libreadline-dev nettle-dev libgnutls28-dev libuv1-dev libmsgpack-dev libargon2-0-dev cython3 python3-dev python3-setuptools && apt-get clean
|
||||
@@ -1,23 +1,7 @@
|
||||
FROM ubuntu:16.04
|
||||
FROM ubuntu:17.04
|
||||
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y llvm llvm-dev clang make cmake git wget libncurses5-dev libreadline-dev nettle-dev libgnutls28-dev libuv1-dev libmsgpack-dev libjsoncpp-dev libasio-dev cython3 python3-dev python3-setuptools libcppunit-dev \
|
||||
&& apt-get install -y llvm llvm-dev clang make cmake git wget libncurses5-dev libreadline-dev nettle-dev libgnutls28-dev libuv1-dev libmsgpack-dev libargon2-0-dev cython3 python3-dev python3-setuptools \
|
||||
&& apt-get remove -y gcc g++ && apt-get autoremove -y && apt-get clean
|
||||
|
||||
ENV CC cc
|
||||
ENV CXX c++
|
||||
|
||||
# build restbed from sources
|
||||
RUN git clone --recursive https://github.com/corvusoft/restbed.git \
|
||||
&& cd restbed && mkdir build && cd build \
|
||||
&& cmake -DBUILD_TESTS=NO -DBUILD_EXAMPLES=NO -DBUILD_SSL=NO -DBUILD_SHARED=YES -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_INSTALL_LIBDIR=lib .. \
|
||||
&& make -j8 install \
|
||||
&& cd .. && rm -rf restbed
|
||||
|
||||
#build msgpack from source
|
||||
RUN wget https://github.com/msgpack/msgpack-c/releases/download/cpp-2.1.5/msgpack-2.1.5.tar.gz \
|
||||
&& tar -xzf msgpack-2.1.5.tar.gz \
|
||||
&& cd msgpack-2.1.5 && mkdir build && cd build \
|
||||
&& cmake -DMSGPACK_CXX11=ON -DMSGPACK_BUILD_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX=/usr .. \
|
||||
&& make -j8 && make install \
|
||||
&& cd ../.. && rm -rf msgpack-2.1.5 msgpack-2.1.5.tar.gz
|
||||
ENV CXX c++
|
||||
@@ -0,0 +1,6 @@
|
||||
FROM aberaud/opendht-deps-llvm
|
||||
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
RUN git clone https://github.com/savoirfairelinux/opendht.git \
|
||||
&& cd opendht && mkdir build && cd build \
|
||||
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On && make -j8 && make install \
|
||||
&& cd ../.. && rm -rf opendht
|
||||
@@ -1,7 +1,6 @@
|
||||
FROM aberaud/opendht-deps
|
||||
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
|
||||
COPY . /root/opendht
|
||||
RUN cd /root/opendht && mkdir build && cd build \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On -DOPENDHT_LTO=On -DOPENDHT_TESTS=ON .. \
|
||||
&& make -j8 && ./opendht_unit_tests && make install
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On -DOPENDHT_LTO=On .. && make -j8 && make install \
|
||||
&& cd ../.. && rm -rf opendht
|
||||
@@ -1,7 +1,6 @@
|
||||
FROM aberaud/opendht-deps-llvm
|
||||
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
|
||||
COPY . /root/opendht
|
||||
RUN cd /root/opendht && mkdir build && cd build \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On -DOPENDHT_TESTS=ON .. \
|
||||
&& make -j8 && ./opendht_unit_tests && make install
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On .. && make -j8 && make install \
|
||||
&& cd ../.. && rm -rf opendht
|
||||
@@ -1,4 +0,0 @@
|
||||
FROM aberaud/opendht-deps
|
||||
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
|
||||
COPY . /root/opendht
|
||||
@@ -19,11 +19,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "opendht/dhtrunner.h"
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
#include "opendht/dht_proxy_server.h"
|
||||
#endif
|
||||
#include "opendht/log.h"
|
||||
#include "opendht/default_types.h"
|
||||
#ifdef OPENDHT_INDEXATION
|
||||
#include "opendht/indexation/pht.h"
|
||||
#endif
|
||||
+13
-40
@@ -1,8 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@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
|
||||
@@ -27,10 +26,6 @@
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
#include <json/json.h>
|
||||
#endif
|
||||
|
||||
namespace dht {
|
||||
|
||||
struct Node;
|
||||
@@ -45,43 +40,19 @@ enum class NodeStatus {
|
||||
};
|
||||
|
||||
struct OPENDHT_PUBLIC NodeStats {
|
||||
unsigned good_nodes {0},
|
||||
dubious_nodes {0},
|
||||
cached_nodes {0},
|
||||
incoming_nodes {0};
|
||||
unsigned table_depth {0};
|
||||
unsigned good_nodes,
|
||||
dubious_nodes,
|
||||
cached_nodes,
|
||||
incoming_nodes;
|
||||
unsigned table_depth;
|
||||
unsigned getKnownNodes() const { return good_nodes + dubious_nodes; }
|
||||
unsigned long getNetworkSizeEstimation() const { return 8 * std::exp2(table_depth); }
|
||||
std::string toString() const;
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
/**
|
||||
* Build a json object from a NodeStats
|
||||
*/
|
||||
Json::Value toJson() const;
|
||||
NodeStats() {};
|
||||
explicit NodeStats(const Json::Value& v);
|
||||
#endif
|
||||
|
||||
MSGPACK_DEFINE_MAP(good_nodes, dubious_nodes, cached_nodes, incoming_nodes, table_depth)
|
||||
};
|
||||
|
||||
struct OPENDHT_PUBLIC NodeInfo {
|
||||
InfoHash id;
|
||||
InfoHash node_id;
|
||||
NodeStats ipv4;
|
||||
NodeStats ipv6;
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
/**
|
||||
* Build a json object from a NodeStats
|
||||
*/
|
||||
Json::Value toJson() const;
|
||||
NodeInfo() {};
|
||||
explicit NodeInfo(const Json::Value& v);
|
||||
#endif
|
||||
|
||||
MSGPACK_DEFINE_MAP(id, node_id, ipv4, ipv6)
|
||||
struct OPENDHT_PUBLIC NetworkConfig {
|
||||
std::string bind_addr {"::0"};
|
||||
in_port_t bind_port {0};
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -103,6 +74,8 @@ struct OPENDHT_PUBLIC Config {
|
||||
|
||||
/** Makes the DHT responsible to maintain its stored values. Consumes more ressources. */
|
||||
bool maintain_storage;
|
||||
|
||||
NetworkConfig network_config;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -120,9 +93,9 @@ using ValuesExport = std::pair<InfoHash, Blob>;
|
||||
|
||||
using QueryCallback = std::function<bool(const std::vector<std::shared_ptr<FieldValueIndex>>& fields)>;
|
||||
using GetCallback = std::function<bool(const std::vector<std::shared_ptr<Value>>& values)>;
|
||||
using ValueCallback = std::function<bool(const std::vector<std::shared_ptr<Value>>& values, bool expired)>;
|
||||
using GetCallbackSimple = std::function<bool(std::shared_ptr<Value> value)>;
|
||||
using ShutdownCallback = std::function<void()>;
|
||||
using StatusCallback = std::function<void(NodeStatus, NodeStatus)>;
|
||||
|
||||
using CertificateStoreQuery = std::function<std::vector<std::shared_ptr<crypto::Certificate>>(const InfoHash& pk_id)>;
|
||||
|
||||
@@ -131,7 +104,7 @@ typedef bool (*GetCallbackRaw)(std::shared_ptr<Value>, void *user_data);
|
||||
OPENDHT_PUBLIC GetCallbackSimple bindGetCb(GetCallbackRaw raw_cb, void* user_data);
|
||||
OPENDHT_PUBLIC GetCallback bindGetCb(GetCallbackSimple cb);
|
||||
|
||||
using DoneCallback = std::function<void(bool success, const std::vector<std::shared_ptr<Node>>& nodes)>;
|
||||
using DoneCallback = std::function<void(bool success, 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);
|
||||
|
||||
+178
-38
@@ -28,6 +28,8 @@ extern "C" {
|
||||
#include <gnutls/x509.h>
|
||||
}
|
||||
|
||||
#include <sodium.h>
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
@@ -35,6 +37,8 @@ extern "C" {
|
||||
#include <iso646.h>
|
||||
#endif
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
namespace dht {
|
||||
|
||||
/**
|
||||
@@ -86,16 +90,8 @@ struct OPENDHT_PUBLIC PublicKey
|
||||
|
||||
PublicKey& operator=(PublicKey&& o) noexcept;
|
||||
|
||||
/**
|
||||
* Get public key fingerprint
|
||||
*/
|
||||
InfoHash getId() const;
|
||||
|
||||
/**
|
||||
* Get public key long fingerprint
|
||||
*/
|
||||
PkId getLongId() const;
|
||||
|
||||
bool checkSignature(const Blob& data, const Blob& signature) const;
|
||||
Blob encrypt(const Blob&) const;
|
||||
|
||||
@@ -141,7 +137,6 @@ struct OPENDHT_PUBLIC PrivateKey
|
||||
PrivateKey(const Blob& import, const std::string& password = {});
|
||||
~PrivateKey();
|
||||
explicit operator bool() const { return key; }
|
||||
|
||||
PublicKey getPublicKey() const;
|
||||
Blob serialize(const std::string& password = {}) const;
|
||||
|
||||
@@ -173,6 +168,8 @@ private:
|
||||
PrivateKey(const PrivateKey&) = delete;
|
||||
PrivateKey& operator=(const PrivateKey&) = delete;
|
||||
Blob decryptBloc(const uint8_t* src, size_t src_size) const;
|
||||
|
||||
//friend dht::crypto::Identity dht::crypto::generateIdentity(const std::string&, dht::crypto::Identity, unsigned key_length);
|
||||
};
|
||||
|
||||
|
||||
@@ -375,7 +372,6 @@ struct OPENDHT_PUBLIC Certificate {
|
||||
|
||||
/** Same as getPublicKey().getId() */
|
||||
InfoHash getId() const;
|
||||
/** Same as getPublicKey().getLongId() */
|
||||
PkId getLongId() const;
|
||||
|
||||
/** Read certificate Common Name (CN) */
|
||||
@@ -395,7 +391,6 @@ struct OPENDHT_PUBLIC Certificate {
|
||||
/** Read certificate alternative names */
|
||||
std::vector<std::pair<NameType, std::string>> getAltNames() const;
|
||||
|
||||
std::chrono::system_clock::time_point getActivation() const;
|
||||
std::chrono::system_clock::time_point getExpiration() const;
|
||||
|
||||
/**
|
||||
@@ -412,20 +407,8 @@ struct OPENDHT_PUBLIC Certificate {
|
||||
|
||||
std::string print() const;
|
||||
|
||||
/**
|
||||
* As a CA, revoke a certificate, adding it to
|
||||
* the attached Certificate Revocation List (CRL)
|
||||
*/
|
||||
void revoke(const PrivateKey&, const Certificate&);
|
||||
|
||||
/**
|
||||
* Get the list of certificates revoked as as CA.
|
||||
*/
|
||||
std::vector<std::shared_ptr<RevocationList>> getRevocationLists() const;
|
||||
|
||||
/**
|
||||
* Attach existing revocation list.
|
||||
*/
|
||||
void addRevocationList(RevocationList&&);
|
||||
void addRevocationList(std::shared_ptr<RevocationList>);
|
||||
|
||||
@@ -521,20 +504,24 @@ 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(); }
|
||||
explicit secure_vector(unsigned size): data_(size) {
|
||||
mlock(data_.data(), data_.size()*sizeof(T));
|
||||
}
|
||||
explicit secure_vector(unsigned size, T _item): data_(size, _item) {
|
||||
mlock(data_.data(), data_.size()*sizeof(T));
|
||||
}
|
||||
explicit secure_vector(const std::vector<T>& c): data_(c) {
|
||||
mlock(data_.data(), data_.size()*sizeof(T));
|
||||
}
|
||||
secure_vector(std::vector<T>&& c): data_(std::move(c)) {
|
||||
mlock(data_.data(), data_.size()*sizeof(T));
|
||||
}
|
||||
~secure_vector() { clean(); munlock(data_.data(), data_.size()*sizeof(T)); }
|
||||
|
||||
static secure_vector<T> getRandom(size_t size) {
|
||||
secure_vector<T> ret(size);
|
||||
secure_vector<T> ret(size/sizeof(T));
|
||||
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;
|
||||
}
|
||||
@@ -542,24 +529,30 @@ public:
|
||||
if (&c == this)
|
||||
return *this;
|
||||
clean();
|
||||
munlock(data_.data(), data_.size()*sizeof(T));
|
||||
data_ = c.data_;
|
||||
mlock(data_.data(), data_.size()*sizeof(T));
|
||||
return *this;
|
||||
}
|
||||
secure_vector<T>& operator=(secure_vector<T>&& c) {
|
||||
if (&c == this)
|
||||
return *this;
|
||||
clean();
|
||||
munlock(data_.data(), data_.size()*sizeof(T));
|
||||
data_ = std::move(c.data_);
|
||||
return *this;
|
||||
}
|
||||
secure_vector<T>& operator=(std::vector<T>&& c) {
|
||||
clean();
|
||||
munlock(data_.data(), data_.size()*sizeof(T));
|
||||
data_ = std::move(c);
|
||||
mlock(data_.data(), data_.size()*sizeof(T));
|
||||
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(); }
|
||||
uint8_t* data() { return data_.data(); }
|
||||
|
||||
void clean() {
|
||||
clean(data_.begin(), data_.end());
|
||||
@@ -576,14 +569,17 @@ public:
|
||||
if (s < data_.size()) {
|
||||
//shrink
|
||||
clean(data_.begin()+s, data_.end());
|
||||
munlock(data_.data()+s, (data_.size()-s)*sizeof(T));
|
||||
data_.resize(s);
|
||||
} else {
|
||||
//grow
|
||||
auto data = std::move(data_); // move protected data
|
||||
clear();
|
||||
data_.resize(s);
|
||||
mlock(data_.data(), data_.size()*sizeof(T));
|
||||
std::copy(data.begin(), data.end(), data_.begin());
|
||||
clean(data.begin(), data.end());
|
||||
munlock(data.data(), data.size()*sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -600,8 +596,154 @@ private:
|
||||
std::vector<T> data_;
|
||||
};
|
||||
|
||||
|
||||
template <class T, size_t N>
|
||||
class OPENDHT_PUBLIC secure_array
|
||||
{
|
||||
public:
|
||||
secure_array() {
|
||||
mlock(data_.data(), data_.size()*sizeof(T));
|
||||
}
|
||||
explicit secure_array(T _item): data_( _item) {
|
||||
mlock(data_.data(), data_.size()*sizeof(T));
|
||||
}
|
||||
explicit secure_array(const std::array<T,N>& c): data_(c) {
|
||||
mlock(data_.data(), data_.size()*sizeof(T));
|
||||
}
|
||||
secure_array(secure_array<T,N> const&) = default;
|
||||
secure_array(secure_array<T,N> &&) = delete;
|
||||
~secure_array() { clean(); munlock(data_.data(), data_.size()*sizeof(T)); }
|
||||
|
||||
static secure_array<T,N> getRandom() {
|
||||
secure_array<T,N> ret;
|
||||
crypto::random_device rdev;
|
||||
std::uniform_int_distribution<uint8_t> rand_byte;
|
||||
std::generate_n((uint8_t*)ret.data_.data(), ret.size()*sizeof(T), std::bind(rand_byte, std::ref(rdev)));
|
||||
return ret;
|
||||
}
|
||||
secure_array<T,N>& operator=(const secure_array<T,N>& c) {
|
||||
if (&c == this)
|
||||
return *this;
|
||||
clean();
|
||||
data_ = c.data_;
|
||||
return *this;
|
||||
}
|
||||
secure_array<T,N>& operator=(secure_array<T,N>&& c) = delete;
|
||||
std::array<T,N>& writable() { clean(); return data_; }
|
||||
const std::array<T,N>& makeInsecure() const { return data_; }
|
||||
const uint8_t* data() const { return data_.data(); }
|
||||
uint8_t* data() { return data_.data(); }
|
||||
|
||||
void clean() {
|
||||
clean(data_.begin(), data_.end());
|
||||
}
|
||||
size_t size() const { return data_.size(); }
|
||||
|
||||
private:
|
||||
/**
|
||||
* Securely wipe memory
|
||||
*/
|
||||
static void clean(const typename std::array<T,N>::iterator& i, const typename std::array<T,N>::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::array<T, N> data_;
|
||||
};
|
||||
|
||||
|
||||
using SecureBlob = secure_vector<uint8_t>;
|
||||
|
||||
/*class EcPublicKey : public std::array<uint8_t, crypto_box_PUBLICKEYBYTES> {
|
||||
public:
|
||||
Blob encrypt(const Blob& message) const {
|
||||
Blob ret(crypto_box_SEALBYTES+message.size());
|
||||
crypto_box_seal(ret.data(), message.data(), message.size(), data());
|
||||
return ret;
|
||||
}
|
||||
|
||||
const InfoHash& getId() const {
|
||||
return *reinterpret_cast<const InfoHash*>(this);
|
||||
}
|
||||
|
||||
};*/
|
||||
|
||||
class EcSecretKey {
|
||||
public:
|
||||
using KeyData = secure_array<uint8_t, crypto_box_SECRETKEYBYTES>;
|
||||
using EcPublicKey = InfoHash;
|
||||
|
||||
static EcSecretKey generate() {
|
||||
EcSecretKey ret;
|
||||
crypto_box_keypair(ret.pk.data(), ret.key.data());
|
||||
return ret;
|
||||
}
|
||||
|
||||
const EcPublicKey& getPublicKey() const {
|
||||
return pk;
|
||||
}
|
||||
|
||||
Blob encrypt(const uint8_t* data, size_t size, const EcPublicKey& pub) const {
|
||||
Blob ret(crypto_box_NONCEBYTES+crypto_box_MACBYTES+size);
|
||||
randombytes_buf(ret.data(), crypto_box_NONCEBYTES);
|
||||
if (crypto_box_easy(ret.data()+crypto_box_NONCEBYTES, data, size, ret.data(),
|
||||
pub.data(), key.data()) != 0) {
|
||||
throw CryptoException("Can't encrypt data");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Blob decrypt(const uint8_t* cypher, size_t cypher_size) const {
|
||||
if (cypher_size <= crypto_box_SEALBYTES)
|
||||
throw DecryptError("Unexpected cipher length");
|
||||
Blob ret(cypher_size - crypto_box_SEALBYTES);
|
||||
if (crypto_box_seal_open(ret.data(), cypher, cypher_size, pk.data(), key.data()) != 0) {
|
||||
throw DecryptError("Can't decrypt data");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Blob decrypt(const uint8_t* cypher, size_t cypher_size, const EcPublicKey& pub) const {
|
||||
if (cypher_size <= crypto_box_NONCEBYTES+crypto_box_MACBYTES)
|
||||
throw DecryptError("Unexpected cipher length");
|
||||
Blob ret(cypher_size - crypto_box_NONCEBYTES - crypto_box_MACBYTES);
|
||||
if (crypto_box_open_easy(ret.data(),
|
||||
cypher+crypto_box_NONCEBYTES,
|
||||
cypher_size-crypto_box_NONCEBYTES,
|
||||
cypher,
|
||||
pub.data(), key.data()) != 0) {
|
||||
throw DecryptError("Can't decrypt data");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
SecureBlob computeSecret(const EcPublicKey& pub, bool sending) const {
|
||||
// shared key = h(q ‖ client_publickey ‖ server_publickey)
|
||||
crypto_generichash_state h;
|
||||
crypto_generichash_init(&h, nullptr, 0U, crypto_generichash_BYTES);
|
||||
{
|
||||
std::array<uint8_t, crypto_scalarmult_BYTES> q;
|
||||
if (auto err = crypto_scalarmult(q.data(), key.data(), pub.data()))
|
||||
throw CryptoException("Can't compute secret: " + std::to_string(err));
|
||||
crypto_generichash_update(&h, q.data(), q.size());
|
||||
}
|
||||
{
|
||||
const auto& sender_pk = sending ? pk : pub;
|
||||
const auto& receiver_pk = sending ? pub : pk;
|
||||
crypto_generichash_update(&h, sender_pk.data(), sender_pk.size());
|
||||
crypto_generichash_update(&h, receiver_pk.data(), receiver_pk.size());
|
||||
}
|
||||
SecureBlob ret(crypto_generichash_BYTES);
|
||||
crypto_generichash_final(&h, ret.data(), ret.size());
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
KeyData key;
|
||||
EcPublicKey pk;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate an RSA key pair (4096 bits) and a certificate.
|
||||
* @param name the name used in the generated certificate
|
||||
@@ -626,8 +768,6 @@ OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name = "dhtnode",
|
||||
*/
|
||||
OPENDHT_PUBLIC Blob hash(const Blob& data, size_t hash_length = 512/8);
|
||||
|
||||
OPENDHT_PUBLIC void hash(const uint8_t* data, size_t data_length, uint8_t* hash, size_t hash_length);
|
||||
|
||||
/**
|
||||
* Generates an encryption key from a text password,
|
||||
* making the key longer to bruteforce.
|
||||
@@ -635,18 +775,18 @@ 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(const std::string& password, Blob& salt, size_t key_length = 512/8);
|
||||
OPENDHT_PUBLIC SecureBlob 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).
|
||||
*/
|
||||
OPENDHT_PUBLIC Blob aesEncrypt(const Blob& data, const Blob& key);
|
||||
OPENDHT_PUBLIC Blob aesEncrypt(const Blob& data, const SecureBlob& key);
|
||||
OPENDHT_PUBLIC Blob aesEncrypt(const Blob& data, const std::string& password);
|
||||
|
||||
/**
|
||||
* AES-GCM decryption.
|
||||
*/
|
||||
OPENDHT_PUBLIC Blob aesDecrypt(const Blob& data, const Blob& key);
|
||||
OPENDHT_PUBLIC Blob aesDecrypt(const Blob& data, const SecureBlob& key);
|
||||
OPENDHT_PUBLIC Blob aesDecrypt(const Blob& data, const std::string& password);
|
||||
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
static Value::Filter getFilter() {
|
||||
return Value::Filter::chain(
|
||||
BaseClass::getFilter(),
|
||||
[](const Value& v) { return static_cast<bool>(v.recipient); }
|
||||
[](const Value& v){ return v.recipient != InfoHash(); }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@ public:
|
||||
ImMessage() {}
|
||||
ImMessage(dht::Value::Id id, std::string&& m, long d = 0)
|
||||
: id(id), msg(std::move(m)), date(d) {}
|
||||
ImMessage(dht::Value::Id id, std::string&& dt, std::string&& m, long d = 0)
|
||||
: id(id), msg(std::move(m)), datatype(std::move(dt)), date(d) {}
|
||||
|
||||
virtual void unpackValue(const Value& v) override {
|
||||
to = v.recipient;
|
||||
@@ -125,11 +123,10 @@ public:
|
||||
dht::InfoHash to;
|
||||
dht::Value::Id id {0};
|
||||
std::string msg;
|
||||
std::string datatype;
|
||||
long date {0};
|
||||
ImStatus status {ImStatus::NONE};
|
||||
|
||||
MSGPACK_DEFINE_MAP(id, msg, date, status, datatype)
|
||||
MSGPACK_DEFINE_MAP(id, msg, date, status)
|
||||
};
|
||||
|
||||
class OPENDHT_PUBLIC TrustRequest : public EncryptedValue<TrustRequest>
|
||||
|
||||
+61
-43
@@ -1,8 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 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>
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@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
|
||||
@@ -27,7 +26,7 @@
|
||||
#include "scheduler.h"
|
||||
#include "routing_table.h"
|
||||
#include "callbacks.h"
|
||||
#include "dht_interface.h"
|
||||
#include "log_enable.h"
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
@@ -55,26 +54,29 @@ struct LocalListener;
|
||||
/**
|
||||
* Main Dht class.
|
||||
* Provides a Distributed Hash Table node.
|
||||
*
|
||||
* Must be given open UDP sockets and ::periodic must be
|
||||
* called regularly.
|
||||
*/
|
||||
class OPENDHT_PUBLIC Dht final : public DhtInterface {
|
||||
class OPENDHT_PUBLIC Dht {
|
||||
public:
|
||||
|
||||
// [[deprecated]]
|
||||
using NodeExport = dht::NodeExport;
|
||||
|
||||
// [[deprecated]]
|
||||
using Status = NodeStatus;
|
||||
|
||||
Dht();
|
||||
|
||||
/**
|
||||
* Initialise the Dht with two open sockets (for IPv4 and IP6)
|
||||
* and an ID for the node.
|
||||
*/
|
||||
Dht(const int& s, const int& s6, Config config);
|
||||
Dht(uv_loop_t* loop, Config config);
|
||||
virtual ~Dht();
|
||||
|
||||
/**
|
||||
* Get the ID of the node.
|
||||
*/
|
||||
inline const InfoHash& getNodeId() const { return myid; }
|
||||
inline const InfoHash& getNodeId() const { return network_engine.getNodeId(); }
|
||||
|
||||
/**
|
||||
* Get the current status of the node for the given family.
|
||||
@@ -84,6 +86,9 @@ public:
|
||||
NodeStatus getStatus() const {
|
||||
return std::max(getStatus(AF_INET), getStatus(AF_INET6));
|
||||
}
|
||||
void setOnStatusChanged(StatusCallback&& cb) {
|
||||
statusCb = std::move(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs final operations before quitting.
|
||||
@@ -98,11 +103,24 @@ public:
|
||||
*/
|
||||
bool isRunning(sa_family_t af = 0) const;
|
||||
|
||||
/**
|
||||
* Enable or disable logging of DHT internal messages
|
||||
*/
|
||||
void setLoggers(LogMethod error = NOLOG, LogMethod warn = NOLOG, LogMethod debug = NOLOG);
|
||||
|
||||
/**
|
||||
* Only print logs related to the given InfoHash (if given), or disable filter (if zeroes).
|
||||
*/
|
||||
void setLogFilter(const InfoHash& f) {
|
||||
DHT_LOG.setFilter(f);
|
||||
}
|
||||
|
||||
virtual void registerType(const ValueType& type) {
|
||||
types.registerType(type);
|
||||
types[type.id] = type;
|
||||
}
|
||||
const ValueType& getType(ValueType::Id type_id) const {
|
||||
return types.getType(type_id);
|
||||
const auto& t_it = types.find(type_id);
|
||||
return (t_it == types.end()) ? ValueType::USER_DATA : t_it->second;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,13 +136,13 @@ public:
|
||||
insertNode(n.id, SockAddr(n.ss, n.sslen));
|
||||
}
|
||||
|
||||
void pingNode(const sockaddr*, socklen_t, DoneCallbackSimple&& cb={});
|
||||
|
||||
time_point periodic(const uint8_t *buf, size_t buflen, const SockAddr&);
|
||||
time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen) {
|
||||
return periodic(buf, buflen, SockAddr(from, fromlen));
|
||||
void bootstrap(const std::string& host, const std::string& service);
|
||||
void clearBootstrap() {
|
||||
bootstrap_nodes.clear();
|
||||
}
|
||||
|
||||
void pingNode(const SockAddr&, DoneCallback&& cb={});
|
||||
|
||||
/**
|
||||
* Get a value by searching on all available protocols (IPv4, IPv6),
|
||||
* and call the provided get callback when values are found at key.
|
||||
@@ -230,16 +248,8 @@ public:
|
||||
*
|
||||
* @return a token to cancel the listener later.
|
||||
*/
|
||||
virtual size_t listen(const InfoHash&, ValueCallback, Value::Filter={}, Where={});
|
||||
|
||||
virtual size_t listen(const InfoHash& key, GetCallback cb, Value::Filter f={}, Where w={}) {
|
||||
return listen(key, [cb](const std::vector<Sp<Value>>& vals, bool expired){
|
||||
if (not expired)
|
||||
return cb(vals);
|
||||
return true;
|
||||
}, std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w={}) {
|
||||
virtual size_t listen(const InfoHash&, GetCallback, Value::Filter&&={}, Where&& w = {});
|
||||
virtual size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w = {}) {
|
||||
return listen(key, bindGetCb(cb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
|
||||
@@ -297,8 +307,14 @@ public:
|
||||
|
||||
std::vector<SockAddr> getPublicAddress(sa_family_t family = 0);
|
||||
|
||||
void pushNotificationReceived(const std::map<std::string, std::string>&) {}
|
||||
void resubscribe(unsigned) {}
|
||||
SockAddr getBoundAddr() {
|
||||
return network_engine.getBoundAddr();
|
||||
}
|
||||
|
||||
protected:
|
||||
Logger DHT_LOG;
|
||||
bool logFilerEnable_ {};
|
||||
InfoHash logFiler_ {};
|
||||
|
||||
private:
|
||||
|
||||
@@ -327,7 +343,7 @@ private:
|
||||
static constexpr unsigned MAX_HASHES {16384};
|
||||
|
||||
/* The maximum number of searches we keep data about. */
|
||||
static constexpr unsigned MAX_SEARCHES {16384};
|
||||
static constexpr unsigned MAX_SEARCHES {2048};
|
||||
|
||||
static constexpr std::chrono::minutes MAX_STORAGE_MAINTENANCE_EXPIRE_TIME {10};
|
||||
|
||||
@@ -357,7 +373,7 @@ private:
|
||||
uint64_t oldsecret {};
|
||||
|
||||
// registred types
|
||||
TypeStore types;
|
||||
std::map<ValueType::Id, ValueType> types;
|
||||
|
||||
// are we a bootstrap node ?
|
||||
// note: Any running node can be used as a bootstrap node.
|
||||
@@ -366,6 +382,8 @@ private:
|
||||
const bool is_bootstrap {false};
|
||||
const bool maintain_storage {false};
|
||||
|
||||
std::vector<std::pair<std::string,std::string>> bootstrap_nodes {};
|
||||
|
||||
// the stuff
|
||||
RoutingTable buckets4 {};
|
||||
RoutingTable buckets6 {};
|
||||
@@ -388,8 +406,7 @@ private:
|
||||
|
||||
// timing
|
||||
Scheduler scheduler;
|
||||
Sp<Scheduler::Job> nextNodesConfirmation {};
|
||||
Sp<Scheduler::Job> nextStorageMaintenance {};
|
||||
Sp<Job> nextNodesConfirmation {};
|
||||
|
||||
net::NetworkEngine network_engine;
|
||||
unsigned pending_pings4 {0};
|
||||
@@ -397,9 +414,13 @@ private:
|
||||
|
||||
using ReportedAddr = std::pair<unsigned, SockAddr>;
|
||||
std::vector<ReportedAddr> reported_addr;
|
||||
|
||||
std::mt19937_64 rd {crypto::getSeededRandomEngine<std::mt19937_64>()};
|
||||
|
||||
NodeStatus status4 {NodeStatus::Disconnected},
|
||||
status6 {NodeStatus::Disconnected};
|
||||
StatusCallback statusCb {nullptr};
|
||||
|
||||
void updateStatus();
|
||||
void rotateSecrets();
|
||||
|
||||
Blob makeToken(const SockAddr&, bool old) const;
|
||||
@@ -409,15 +430,14 @@ private:
|
||||
|
||||
// Storage
|
||||
void storageAddListener(const InfoHash& id, const Sp<Node>& node, size_t tid, Query&& = {});
|
||||
bool storageStore(const InfoHash& id, const Sp<Value>& value, time_point created, const SockAddr& sa = {}, bool permanent = false);
|
||||
bool storageErase(const InfoHash& id, Value::Id vid);
|
||||
bool storageRefresh(const InfoHash& id, Value::Id vid);
|
||||
bool storageStore(const InfoHash& id, const Sp<Value>& value, time_point created, const SockAddr& sa = {});
|
||||
void expireStore();
|
||||
void expireStorage(InfoHash h);
|
||||
void expireStore(decltype(store)::iterator);
|
||||
|
||||
void storageChanged(const InfoHash& id, Storage& st, ValueStorage&, bool newValue);
|
||||
void storageChanged(const InfoHash& id, Storage& st, ValueStorage&);
|
||||
std::string printStorageLog(const decltype(store)::value_type&) const;
|
||||
void printStorageQuota(std::ostream& out, const decltype(store_quota)::value_type& ip) const;
|
||||
|
||||
/**
|
||||
* For a given storage, if values don't belong there anymore because this
|
||||
@@ -466,10 +486,10 @@ private:
|
||||
* Low-level method that will perform a search on the DHT for the specified
|
||||
* 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 = {});
|
||||
Sp<Search> search(const InfoHash& id, sa_family_t af, GetCallback = {}, QueryCallback = {}, DoneCallback = {}, Value::Filter = {}, Query q = {});
|
||||
|
||||
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 = Value::AllFilter(), const Sp<Query>& q = {});
|
||||
size_t listenTo(const InfoHash& id, sa_family_t af, GetCallback cb, Value::Filter f = Value::AllFilter(), const Sp<Query>& q = {});
|
||||
|
||||
/**
|
||||
* Refill the search with good nodes if possible.
|
||||
@@ -537,9 +557,7 @@ private:
|
||||
*
|
||||
* @param sr The search to execute its operations.
|
||||
*/
|
||||
void searchStep(Sp<Search>);
|
||||
void searchSynchedNodeListen(const Sp<Search>&, SearchNode&);
|
||||
|
||||
void searchStep(Sp<Search> sr);
|
||||
void dumpSearch(const Search& sr, std::ostream& out) const;
|
||||
|
||||
bool neighbourhoodMaintenance(RoutingTable&);
|
||||
|
||||
@@ -1,246 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@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 "log_enable.h"
|
||||
|
||||
namespace dht {
|
||||
|
||||
class OPENDHT_PUBLIC DhtInterface {
|
||||
public:
|
||||
DhtInterface() = default;
|
||||
virtual ~DhtInterface() = default;
|
||||
|
||||
// [[deprecated]]
|
||||
using Status = NodeStatus;
|
||||
// [[deprecated]]
|
||||
using NodeExport = dht::NodeExport;
|
||||
|
||||
/**
|
||||
* Get the current status of the node for the given family.
|
||||
*/
|
||||
virtual NodeStatus getStatus(sa_family_t af) const = 0;
|
||||
virtual NodeStatus getStatus() const = 0;
|
||||
|
||||
/**
|
||||
* Get the ID of the DHT node.
|
||||
*/
|
||||
virtual const InfoHash& getNodeId() const = 0;
|
||||
|
||||
/**
|
||||
* Performs final operations before quitting.
|
||||
*/
|
||||
virtual void shutdown(ShutdownCallback cb) = 0;
|
||||
|
||||
/**
|
||||
* Returns true if the node is running (have access to an open socket).
|
||||
*
|
||||
* af: address family. If non-zero, will return true if the node
|
||||
* is running for the provided family.
|
||||
*/
|
||||
virtual bool isRunning(sa_family_t af = 0) const = 0;
|
||||
|
||||
virtual void registerType(const ValueType& type) = 0;
|
||||
|
||||
virtual const ValueType& getType(ValueType::Id type_id) const = 0;
|
||||
|
||||
/**
|
||||
* Insert a node in the main routing table.
|
||||
* The node is not pinged, so this should be
|
||||
* used to bootstrap efficiently from previously known nodes.
|
||||
*/
|
||||
virtual void insertNode(const InfoHash& id, const SockAddr&) = 0;
|
||||
virtual void insertNode(const InfoHash& id, const sockaddr* sa, socklen_t salen) = 0;
|
||||
virtual void insertNode(const NodeExport& n) = 0;
|
||||
|
||||
virtual void pingNode(const sockaddr*, socklen_t, DoneCallbackSimple&& cb={}) = 0;
|
||||
|
||||
virtual time_point periodic(const uint8_t *buf, size_t buflen, const SockAddr&) = 0;
|
||||
virtual time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen) = 0;
|
||||
|
||||
/**
|
||||
* Get a value by searching on all available protocols (IPv4, IPv6),
|
||||
* and call the provided get callback when values are found at key.
|
||||
* The operation will start as soon as the node is connected to the network.
|
||||
* @param cb a function called when new values are found on the network.
|
||||
* It should return false to stop the operation.
|
||||
* @param donecb a function called when the operation is complete.
|
||||
cb and donecb won't be called again afterward.
|
||||
* @param f a filter function used to prefilter values.
|
||||
*/
|
||||
virtual void get(const InfoHash& key, GetCallback cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) = 0;
|
||||
virtual void get(const InfoHash& key, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f={}, Where&& w = {}) = 0;
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) = 0;
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}, Where&& w = {}) = 0;
|
||||
|
||||
/**
|
||||
* Similar to Dht::get, but sends a Query to filter data remotely.
|
||||
* @param key the key for which to query data for.
|
||||
* @param cb a function called when new values are found on the network.
|
||||
* It should return false to stop the operation.
|
||||
* @param done_cb a function called when the operation is complete.
|
||||
cb and done_cb won't be called again afterward.
|
||||
* @param q a query used to filter values on the remotes before they send a
|
||||
* response.
|
||||
*/
|
||||
virtual void query(const InfoHash& key, QueryCallback cb, DoneCallback done_cb = {}, Query&& q = {}) = 0;
|
||||
virtual void query(const InfoHash& key, QueryCallback cb, DoneCallbackSimple done_cb = {}, Query&& q = {}) = 0;
|
||||
|
||||
/**
|
||||
* Get locally stored data for the given hash.
|
||||
*/
|
||||
virtual std::vector<Sp<Value>> getLocal(const InfoHash& key, Value::Filter f = Value::AllFilter()) const = 0;
|
||||
|
||||
/**
|
||||
* Get locally stored data for the given key and value id.
|
||||
*/
|
||||
virtual Sp<Value> getLocalById(const InfoHash& key, Value::Id vid) const = 0;
|
||||
|
||||
/**
|
||||
* Announce a value on all available protocols (IPv4, IPv6).
|
||||
*
|
||||
* The operation will start as soon as the node is connected to the network.
|
||||
* The done callback will be called once, when the first announce succeeds, or fails.
|
||||
*/
|
||||
virtual void put(const InfoHash& key,
|
||||
Sp<Value>,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) = 0;
|
||||
virtual void put(const InfoHash& key,
|
||||
const Sp<Value>& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) = 0;
|
||||
virtual void put(const InfoHash& key,
|
||||
Value&& v,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) = 0;
|
||||
virtual void put(const InfoHash& key,
|
||||
Value&& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) = 0;
|
||||
|
||||
/**
|
||||
* Get data currently being put at the given hash.
|
||||
*/
|
||||
virtual std::vector<Sp<Value>> getPut(const InfoHash&) = 0;
|
||||
|
||||
/**
|
||||
* Get data currently being put at the given hash with the given id.
|
||||
*/
|
||||
virtual Sp<Value> getPut(const InfoHash&, const Value::Id&) = 0;
|
||||
|
||||
/**
|
||||
* Stop any put/announce operation at the given location,
|
||||
* for the value with the given id.
|
||||
*/
|
||||
virtual bool cancelPut(const InfoHash&, const Value::Id&) = 0;
|
||||
|
||||
/**
|
||||
* Listen on the network for any changes involving a specified hash.
|
||||
* The node will register to receive updates from relevent nodes when
|
||||
* new values are added or removed.
|
||||
*
|
||||
* @return a token to cancel the listener later.
|
||||
*/
|
||||
virtual size_t listen(const InfoHash&, GetCallback, Value::Filter={}, Where w = {}) = 0;
|
||||
virtual size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w = {}) = 0;
|
||||
virtual size_t listen(const InfoHash&, ValueCallback, Value::Filter={}, Where w = {}) = 0;
|
||||
|
||||
virtual bool cancelListen(const InfoHash&, size_t token) = 0;
|
||||
|
||||
/**
|
||||
* Inform the DHT of lower-layer connectivity changes.
|
||||
* This will cause the DHT to assume a public IP address change.
|
||||
* The DHT will recontact neighbor nodes, re-register for listen ops etc.
|
||||
*/
|
||||
virtual void connectivityChanged(sa_family_t) = 0;
|
||||
virtual void connectivityChanged() = 0;
|
||||
|
||||
/**
|
||||
* Get the list of good nodes for local storage saving purposes
|
||||
* The list is ordered to minimize the back-to-work delay.
|
||||
*/
|
||||
virtual std::vector<NodeExport> exportNodes() = 0;
|
||||
|
||||
virtual std::vector<ValuesExport> exportValues() const = 0;
|
||||
virtual void importValues(const std::vector<ValuesExport>&) = 0;
|
||||
|
||||
virtual NodeStats getNodesStats(sa_family_t af) const = 0;
|
||||
|
||||
virtual std::string getStorageLog() const = 0;
|
||||
virtual std::string getStorageLog(const InfoHash&) const = 0;
|
||||
|
||||
virtual std::string getRoutingTablesLog(sa_family_t) const = 0;
|
||||
virtual std::string getSearchesLog(sa_family_t) const = 0;
|
||||
virtual std::string getSearchLog(const InfoHash&, sa_family_t af = AF_UNSPEC) const = 0;
|
||||
|
||||
virtual void dumpTables() const = 0;
|
||||
virtual std::vector<unsigned> getNodeMessageStats(bool in = false) = 0;
|
||||
|
||||
/**
|
||||
* Set the in-memory storage limit in bytes
|
||||
*/
|
||||
virtual void setStorageLimit(size_t limit = DEFAULT_STORAGE_LIMIT) = 0;
|
||||
|
||||
/**
|
||||
* Returns the total memory usage of stored values and the number
|
||||
* of stored values.
|
||||
*/
|
||||
virtual std::pair<size_t, size_t> getStoreSize() const = 0;
|
||||
|
||||
virtual std::vector<SockAddr> getPublicAddress(sa_family_t family = 0) = 0;
|
||||
|
||||
/**
|
||||
* Enable or disable logging of DHT internal messages
|
||||
*/
|
||||
virtual void setLoggers(LogMethod error = NOLOG, LogMethod warn = NOLOG, LogMethod debug = NOLOG)
|
||||
{
|
||||
DHT_LOG.DBG = debug;
|
||||
DHT_LOG.WARN = warn;
|
||||
DHT_LOG.ERR = error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only print logs related to the given InfoHash (if given), or disable filter (if zeroes).
|
||||
*/
|
||||
virtual void setLogFilter(const InfoHash& f)
|
||||
{
|
||||
DHT_LOG.setFilter(f);
|
||||
}
|
||||
|
||||
virtual void setPushNotificationToken(const std::string&) {};
|
||||
|
||||
/**
|
||||
* Call linked callback with a push notification
|
||||
* @param notification to process
|
||||
*/
|
||||
virtual void pushNotificationReceived(const std::map<std::string, std::string>& data) = 0;
|
||||
|
||||
protected:
|
||||
bool logFilerEnable_ {};
|
||||
InfoHash logFiler_ {};
|
||||
Logger DHT_LOG;
|
||||
};
|
||||
|
||||
} // namespace dht
|
||||
@@ -1,383 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016-2018 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
#include "callbacks.h"
|
||||
#include "def.h"
|
||||
#include "dht_interface.h"
|
||||
#include "scheduler.h"
|
||||
#include "proxy.h"
|
||||
|
||||
namespace restbed {
|
||||
class Request;
|
||||
}
|
||||
|
||||
namespace Json {
|
||||
class Value;
|
||||
}
|
||||
|
||||
namespace dht {
|
||||
|
||||
class SearchCache;
|
||||
|
||||
class OPENDHT_PUBLIC DhtProxyClient final : public DhtInterface {
|
||||
public:
|
||||
|
||||
DhtProxyClient();
|
||||
|
||||
explicit DhtProxyClient(std::function<void()> loopSignal, const std::string& serverHost, const std::string& pushClientId = "");
|
||||
|
||||
virtual void setPushNotificationToken(const std::string& token) {
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
deviceKey_ = token;
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual ~DhtProxyClient();
|
||||
|
||||
/**
|
||||
* Get the ID of the node.
|
||||
*/
|
||||
inline const InfoHash& getNodeId() const { return myid; }
|
||||
|
||||
/**
|
||||
* Get the current status of the node for the given family.
|
||||
*/
|
||||
NodeStatus getStatus(sa_family_t af) const;
|
||||
NodeStatus getStatus() const {
|
||||
return std::max(getStatus(AF_INET), getStatus(AF_INET6));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs final operations before quitting.
|
||||
*/
|
||||
void shutdown(ShutdownCallback cb);
|
||||
|
||||
/**
|
||||
* Returns true if the node is running (have access to an open socket).
|
||||
*
|
||||
* af: address family. If non-zero, will return true if the node
|
||||
* is running for the provided family.
|
||||
*/
|
||||
bool isRunning(sa_family_t af = 0) const;
|
||||
|
||||
/**
|
||||
* Get a value by asking the proxy and call the provided get callback when
|
||||
* values are found at key.
|
||||
* The operation will start as soon as the node is connected to the network.
|
||||
* @param cb a function called when new values are found on the network.
|
||||
* It should return false to stop the operation.
|
||||
* @param donecb a function called when the operation is complete.
|
||||
cb and donecb won't be called again afterward.
|
||||
* @param f a filter function used to prefilter values.
|
||||
*/
|
||||
virtual void get(const InfoHash& key, GetCallback cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {});
|
||||
virtual void get(const InfoHash& key, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f={}, Where&& w = {}) {
|
||||
get(key, cb, bindDoneCb(donecb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) {
|
||||
get(key, bindGetCb(cb), donecb, std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}, Where&& w = {}) {
|
||||
get(key, bindGetCb(cb), bindDoneCb(donecb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
|
||||
/**
|
||||
* Announce a value on all available protocols (IPv4, IPv6).
|
||||
*
|
||||
* The operation will start as soon as the node is connected to the network.
|
||||
* The done callback will be called once, when the first announce succeeds, or fails.
|
||||
* NOTE: For now, created parameter is ignored.
|
||||
*/
|
||||
void put(const InfoHash& key,
|
||||
Sp<Value>,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false);
|
||||
void put(const InfoHash& key,
|
||||
const Sp<Value>& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false)
|
||||
{
|
||||
put(key, v, bindDoneCb(cb), created, permanent);
|
||||
}
|
||||
|
||||
void put(const InfoHash& key,
|
||||
Value&& v,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false)
|
||||
{
|
||||
put(key, std::make_shared<Value>(std::move(v)), cb, created, permanent);
|
||||
}
|
||||
void put(const InfoHash& key,
|
||||
Value&& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false)
|
||||
{
|
||||
put(key, std::forward<Value>(v), bindDoneCb(cb), created, permanent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param af the socket family
|
||||
* @return node stats from the proxy
|
||||
*/
|
||||
NodeStats getNodesStats(sa_family_t af) const;
|
||||
|
||||
/**
|
||||
* @param family the socket family
|
||||
* @return public address
|
||||
*/
|
||||
std::vector<SockAddr> getPublicAddress(sa_family_t family = 0);
|
||||
|
||||
/**
|
||||
* Listen on the network for any changes involving a specified hash.
|
||||
* The node will register to receive updates from relevent nodes when
|
||||
* new values are added or removed.
|
||||
*
|
||||
* @return a token to cancel the listener later.
|
||||
*/
|
||||
virtual size_t listen(const InfoHash&, ValueCallback, Value::Filter={}, Where={});
|
||||
|
||||
virtual size_t listen(const InfoHash& key, GetCallback cb, Value::Filter f={}, Where w={}) {
|
||||
return listen(key, [cb](const std::vector<Sp<Value>>& vals, bool expired){
|
||||
if (not expired)
|
||||
return cb(vals);
|
||||
return true;
|
||||
}, std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w={}) {
|
||||
return listen(key, bindGetCb(cb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual bool cancelListen(const InfoHash& key, size_t token);
|
||||
|
||||
/**
|
||||
* Call linked callback with a push notification
|
||||
* @param notification to process
|
||||
*/
|
||||
void pushNotificationReceived(const std::map<std::string, std::string>& notification);
|
||||
|
||||
time_point periodic(const uint8_t*, size_t, const SockAddr&);
|
||||
time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen) {
|
||||
return periodic(buf, buflen, SockAddr(from, fromlen));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Similar to Dht::get, but sends a Query to filter data remotely.
|
||||
* @param key the key for which to query data for.
|
||||
* @param cb a function called when new values are found on the network.
|
||||
* It should return false to stop the operation.
|
||||
* @param done_cb a function called when the operation is complete.
|
||||
cb and done_cb won't be called again afterward.
|
||||
* @param q a query used to filter values on the remotes before they send a
|
||||
* response.
|
||||
*/
|
||||
virtual void query(const InfoHash& /*key*/, QueryCallback /*cb*/, DoneCallback /*done_cb*/ = {}, Query&& /*q*/ = {}) { }
|
||||
virtual void query(const InfoHash& key, QueryCallback cb, DoneCallbackSimple done_cb = {}, Query&& q = {}) {
|
||||
query(key, cb, bindDoneCb(done_cb), std::forward<Query>(q));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data currently being put at the given hash.
|
||||
*/
|
||||
std::vector<Sp<Value>> getPut(const InfoHash&);
|
||||
|
||||
/**
|
||||
* Get data currently being put at the given hash with the given id.
|
||||
*/
|
||||
Sp<Value> getPut(const InfoHash&, const Value::Id&);
|
||||
|
||||
/**
|
||||
* Stop any put/announce operation at the given location,
|
||||
* for the value with the given id.
|
||||
*/
|
||||
bool cancelPut(const InfoHash&, const Value::Id&);
|
||||
|
||||
void pingNode(const sockaddr*, socklen_t, DoneCallbackSimple&& /*cb*/={}) { }
|
||||
|
||||
virtual void registerType(const ValueType& type) {
|
||||
types.registerType(type);
|
||||
}
|
||||
const ValueType& getType(ValueType::Id type_id) const {
|
||||
return types.getType(type_id);
|
||||
}
|
||||
|
||||
std::vector<Sp<Value>> getLocal(const InfoHash& k, Value::Filter filter) const;
|
||||
Sp<Value> getLocalById(const InfoHash& k, Value::Id id) const;
|
||||
|
||||
/**
|
||||
* NOTE: The following methods will not be implemented because the
|
||||
* DhtProxyClient doesn't have any storage nor synchronization process
|
||||
*/
|
||||
void insertNode(const InfoHash&, const SockAddr&) { }
|
||||
void insertNode(const InfoHash&, const sockaddr*, socklen_t) { }
|
||||
void insertNode(const NodeExport&) { }
|
||||
std::pair<size_t, size_t> getStoreSize() const { return {}; }
|
||||
std::vector<NodeExport> exportNodes() { return {}; }
|
||||
std::vector<ValuesExport> exportValues() const { return {}; }
|
||||
void importValues(const std::vector<ValuesExport>&) {}
|
||||
std::string getStorageLog() const { return {}; }
|
||||
std::string getStorageLog(const InfoHash&) const { return {}; }
|
||||
std::string getRoutingTablesLog(sa_family_t) const { return {}; }
|
||||
std::string getSearchesLog(sa_family_t) const { return {}; }
|
||||
std::string getSearchLog(const InfoHash&, sa_family_t) const { return {}; }
|
||||
void dumpTables() const {}
|
||||
std::vector<unsigned> getNodeMessageStats(bool) { return {}; }
|
||||
void setStorageLimit(size_t) {}
|
||||
void connectivityChanged(sa_family_t) {
|
||||
restartListeners();
|
||||
}
|
||||
void connectivityChanged() {
|
||||
getProxyInfos();
|
||||
restartListeners();
|
||||
loopSignal_();
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Start the connection with a server.
|
||||
*/
|
||||
void startProxy();
|
||||
|
||||
/**
|
||||
* Get informations from the proxy node
|
||||
* @return the JSON returned by the proxy
|
||||
*/
|
||||
struct InfoState;
|
||||
void getProxyInfos();
|
||||
void onProxyInfos(const Json::Value& val, sa_family_t family);
|
||||
SockAddr parsePublicAddress(const Json::Value& val);
|
||||
|
||||
void opFailed();
|
||||
|
||||
size_t doListen(const InfoHash& key, ValueCallback, Value::Filter);
|
||||
bool doCancelListen(const InfoHash& key, size_t token);
|
||||
|
||||
struct ListenState;
|
||||
void sendListen(const std::shared_ptr<restbed::Request>& request, const ValueCallback&, const Value::Filter& filter, const Sp<ListenState>& state);
|
||||
void sendSubscribe(const std::shared_ptr<restbed::Request>& request, const Sp<proxy::ListenToken>&, const Sp<ListenState>& state);
|
||||
|
||||
void doPut(const InfoHash&, Sp<Value>, DoneCallback, time_point created, bool permanent);
|
||||
|
||||
/**
|
||||
* Initialize statusIpvX_
|
||||
*/
|
||||
void getConnectivityStatus();
|
||||
/**
|
||||
* cancel all Listeners
|
||||
*/
|
||||
void cancelAllListeners();
|
||||
/**
|
||||
* cancel all Operations
|
||||
*/
|
||||
void cancelAllOperations();
|
||||
|
||||
std::string serverHost_;
|
||||
std::string pushClientId_;
|
||||
|
||||
mutable std::mutex lockCurrentProxyInfos_;
|
||||
NodeStatus statusIpv4_ {NodeStatus::Disconnected};
|
||||
NodeStatus statusIpv6_ {NodeStatus::Disconnected};
|
||||
NodeStats stats4_ {};
|
||||
NodeStats stats6_ {};
|
||||
SockAddr publicAddressV4_;
|
||||
SockAddr publicAddressV6_;
|
||||
|
||||
InfoHash myid {};
|
||||
|
||||
// registred types
|
||||
TypeStore types;
|
||||
|
||||
/**
|
||||
* Store listen requests.
|
||||
*/
|
||||
struct Listener;
|
||||
struct ProxySearch;
|
||||
|
||||
size_t listenerToken_ {0};
|
||||
std::map<InfoHash, ProxySearch> searches_;
|
||||
mutable std::mutex searchLock_;
|
||||
|
||||
/**
|
||||
* Store current put and get requests.
|
||||
*/
|
||||
struct Operation
|
||||
{
|
||||
std::shared_ptr<restbed::Request> req;
|
||||
std::thread thread;
|
||||
std::shared_ptr<std::atomic_bool> finished;
|
||||
};
|
||||
std::vector<Operation> operations_;
|
||||
std::mutex lockOperations_;
|
||||
/**
|
||||
* Callbacks should be executed in the main thread.
|
||||
*/
|
||||
std::vector<std::function<void()>> callbacks_;
|
||||
std::mutex lockCallbacks;
|
||||
|
||||
Sp<InfoState> infoState_;
|
||||
std::thread statusThread_;
|
||||
mutable std::mutex statusLock_;
|
||||
|
||||
Scheduler scheduler;
|
||||
/**
|
||||
* Retrieve if we can connect to the proxy (update statusIpvX_)
|
||||
*/
|
||||
void confirmProxy();
|
||||
Sp<Scheduler::Job> nextProxyConfirmation {};
|
||||
/**
|
||||
* Relaunch LISTEN requests if the client disconnect/reconnect.
|
||||
*/
|
||||
void restartListeners();
|
||||
|
||||
/**
|
||||
* Refresh a listen via a token
|
||||
* @param token
|
||||
*/
|
||||
void resubscribe(const InfoHash& key, Listener& listener);
|
||||
|
||||
/**
|
||||
* If we want to use push notifications by default.
|
||||
* NOTE: empty by default to avoid to use services like FCM or APN.
|
||||
*/
|
||||
std::string deviceKey_ {};
|
||||
|
||||
const std::function<void()> loopSignal_;
|
||||
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
void fillBodyToGetToken(std::shared_ptr<restbed::Request> request, unsigned token = 0);
|
||||
void getPushRequest(Json::Value&) const;
|
||||
#endif // OPENDHT_PUSH_NOTIFICATIONS
|
||||
|
||||
std::atomic_bool isDestroying_ {false};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OPENDHT_PROXY_CLIENT
|
||||
@@ -1,319 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2018 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "callbacks.h"
|
||||
#include "def.h"
|
||||
#include "infohash.h"
|
||||
#include "proxy.h"
|
||||
#include "scheduler.h"
|
||||
#include "sockaddr.h"
|
||||
#include "value.h"
|
||||
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <restbed>
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
#include <json/json.h>
|
||||
#endif
|
||||
|
||||
namespace Json {
|
||||
class Value;
|
||||
}
|
||||
|
||||
namespace dht {
|
||||
|
||||
class DhtRunner;
|
||||
|
||||
/**
|
||||
* Describes the REST API
|
||||
*/
|
||||
class OPENDHT_PUBLIC DhtProxyServer
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Start the Http server for OpenDHT
|
||||
* @param dht the DhtRunner linked to this proxy server
|
||||
* @param port to listen
|
||||
* @param pushServer where to push notifications
|
||||
* @note if the server fails to start (if port is already used or reserved),
|
||||
* it will fails silently
|
||||
*/
|
||||
DhtProxyServer(std::shared_ptr<DhtRunner> dht, in_port_t port = 8000, const std::string& pushServer = "");
|
||||
virtual ~DhtProxyServer();
|
||||
|
||||
DhtProxyServer(const DhtProxyServer& other) = delete;
|
||||
DhtProxyServer(DhtProxyServer&& other) = delete;
|
||||
DhtProxyServer& operator=(const DhtProxyServer& other) = delete;
|
||||
DhtProxyServer& operator=(DhtProxyServer&& other) = delete;
|
||||
|
||||
struct ServerStats {
|
||||
/** Current number of listen operations */
|
||||
size_t listenCount;
|
||||
/** Current number of permanent put operations */
|
||||
size_t putCount;
|
||||
/** Current number of push tokens with at least one listen operation */
|
||||
size_t pushListenersCount;
|
||||
/** Average requests per second */
|
||||
double requestRate;
|
||||
/** Node Info **/
|
||||
NodeInfo nodeInfo;
|
||||
|
||||
std::string toString() const {
|
||||
std::ostringstream ss;
|
||||
ss << "Listens: " << listenCount << " Puts: " << putCount << " PushListeners: " << pushListenersCount << std::endl;
|
||||
ss << "Requests: " << requestRate << " per second." << std::endl;
|
||||
auto& ni = nodeInfo;
|
||||
auto& ipv4 = ni.ipv4;
|
||||
if (ipv4.table_depth > 1) {
|
||||
ss << "IPv4 Network estimation: " << ipv4.getNetworkSizeEstimation() << std::endl;;
|
||||
}
|
||||
auto& ipv6 = ni.ipv6;
|
||||
if (ipv6.table_depth > 1) {
|
||||
ss << "IPv6 Network estimation: " << ipv6.getNetworkSizeEstimation() << std::endl;;
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
/**
|
||||
* Build a json object from a NodeStats
|
||||
*/
|
||||
Json::Value toJson() const {
|
||||
Json::Value result;
|
||||
result["listenCount"] = static_cast<Json::UInt64>(listenCount);
|
||||
result["putCount"] = static_cast<Json::UInt64>(putCount);
|
||||
result["pushListenersCount"] = static_cast<Json::UInt64>(pushListenersCount);
|
||||
result["requestRate"] = requestRate;
|
||||
result["nodeInfo"] = nodeInfo.toJson();
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
ServerStats stats() const { return stats_; }
|
||||
|
||||
void updateStats() const;
|
||||
|
||||
std::shared_ptr<DhtRunner> getNode() const { return dht_; }
|
||||
|
||||
/**
|
||||
* Stop the DhtProxyServer
|
||||
*/
|
||||
void stop();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Return the PublicKey id, the node id and node stats
|
||||
* Method: GET "/"
|
||||
* Result: HTTP 200, body: Node infos in JSON format
|
||||
* On error: HTTP 503, body: {"err":"xxxx"}
|
||||
* @param session
|
||||
*/
|
||||
void getNodeInfo(const std::shared_ptr<restbed::Session>& session) const;
|
||||
|
||||
/**
|
||||
* Return ServerStats in JSON format
|
||||
* Method: STATS "/"
|
||||
* Result: HTTP 200, body: Node infos in JSON format
|
||||
* @param session
|
||||
*/
|
||||
void getStats(const std::shared_ptr<restbed::Session>& session) const;
|
||||
|
||||
/**
|
||||
* Return Values of an infoHash
|
||||
* Method: GET "/{InfoHash: .*}"
|
||||
* Return: Multiple JSON object in parts. Example:
|
||||
* Value in JSON format\n
|
||||
* Value in JSON format
|
||||
*
|
||||
* On error: HTTP 503, body: {"err":"xxxx"}
|
||||
* @param session
|
||||
*/
|
||||
void get(const std::shared_ptr<restbed::Session>& session) const;
|
||||
|
||||
/**
|
||||
* Listen incoming Values of an infoHash.
|
||||
* Method: LISTEN "/{InfoHash: .*}"
|
||||
* Return: Multiple JSON object in parts. Example:
|
||||
* Value in JSON format\n
|
||||
* Value in JSON format
|
||||
*
|
||||
* On error: HTTP 503, body: {"err":"xxxx"}
|
||||
* @param session
|
||||
*/
|
||||
void listen(const std::shared_ptr<restbed::Session>& session);
|
||||
|
||||
/**
|
||||
* Put a value on the DHT
|
||||
* Method: POST "/{InfoHash: .*}"
|
||||
* body = Value to put in JSON
|
||||
* Return: HTTP 200 if success and the value put in JSON
|
||||
* On error: HTTP 503, body: {"err":"xxxx"} if no dht
|
||||
* HTTP 400, body: {"err":"xxxx"} if bad json or HTTP 502 if put fails
|
||||
* @param session
|
||||
*/
|
||||
void put(const std::shared_ptr<restbed::Session>& session);
|
||||
|
||||
void cancelPut(const InfoHash& key, Value::Id vid);
|
||||
|
||||
#if OPENDHT_PROXY_SERVER_IDENTITY
|
||||
/**
|
||||
* Put a value to sign by the proxy on the DHT
|
||||
* Method: SIGN "/{InfoHash: .*}"
|
||||
* body = Value to put in JSON
|
||||
* Return: HTTP 200 if success and the value put in JSON
|
||||
* On error: HTTP 503, body: {"err":"xxxx"} if no dht
|
||||
* HTTP 400, body: {"err":"xxxx"} if bad json
|
||||
* @param session
|
||||
*/
|
||||
void putSigned(const std::shared_ptr<restbed::Session>& session) const;
|
||||
|
||||
/**
|
||||
* Put a value to encrypt by the proxy on the DHT
|
||||
* Method: ENCRYPT "/{hash: .*}"
|
||||
* body = Value to put in JSON + "to":"infoHash"
|
||||
* Return: HTTP 200 if success and the value put in JSON
|
||||
* On error: HTTP 503, body: {"err":"xxxx"} if no dht
|
||||
* HTTP 400, body: {"err":"xxxx"} if bad json
|
||||
* @param session
|
||||
*/
|
||||
void putEncrypted(const std::shared_ptr<restbed::Session>& session) const;
|
||||
#endif // OPENDHT_PROXY_SERVER_IDENTITY
|
||||
|
||||
/**
|
||||
* Return Values of an infoHash filtered by a value id
|
||||
* Method: GET "/{InfoHash: .*}/{ValueId: .*}"
|
||||
* Return: Multiple JSON object in parts. Example:
|
||||
* Value in JSON format\n
|
||||
* Value in JSON format
|
||||
*
|
||||
* On error: HTTP 503, body: {"err":"xxxx"}
|
||||
* @param session
|
||||
*/
|
||||
void getFiltered(const std::shared_ptr<restbed::Session>& session) const;
|
||||
|
||||
/**
|
||||
* Respond allowed Methods
|
||||
* Method: OPTIONS "/{hash: .*}"
|
||||
* Return: HTTP 200 + Allow: allowed methods
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS
|
||||
* @param session
|
||||
*/
|
||||
void handleOptionsMethod(const std::shared_ptr<restbed::Session>& session) const;
|
||||
|
||||
/**
|
||||
* Remove finished listeners
|
||||
* @param testSession if we remove the listener only if the session is closed
|
||||
*/
|
||||
void removeClosedListeners(bool testSession = true);
|
||||
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
/**
|
||||
* Subscribe to push notifications for an iOS or Android device.
|
||||
* Method: SUBSCRIBE "/{InfoHash: .*}"
|
||||
* Body: {"key": "device_key", (optional)"isAndroid":false (default true)}"
|
||||
* Return: {"token": x}" where x if a token to save
|
||||
* @note: the listen will timeout after six hours (and send a push notification).
|
||||
* so you need to refresh the operation each six hours.
|
||||
* @param session
|
||||
*/
|
||||
void subscribe(const std::shared_ptr<restbed::Session>& session);
|
||||
/**
|
||||
* Unsubscribe to push notifications for an iOS or Android device.
|
||||
* Method: UNSUBSCRIBE "/{InfoHash: .*}"
|
||||
* Body: {"key": "device_key", "token": x} where x if the token to cancel
|
||||
* Return: nothing
|
||||
* @param session
|
||||
*/
|
||||
void unsubscribe(const std::shared_ptr<restbed::Session>& session);
|
||||
/**
|
||||
* Send a push notification via a gorush push gateway
|
||||
* @param key of the device
|
||||
* @param json, the content to send
|
||||
*/
|
||||
void sendPushNotification(const std::string& key, const Json::Value& json, bool isAndroid) const;
|
||||
|
||||
/**
|
||||
* Remove a push listener between a client and a hash
|
||||
* @param pushToken
|
||||
* @param key
|
||||
* @param clientId
|
||||
*/
|
||||
void cancelPushListen(const std::string& pushToken, const InfoHash& key, const std::string& clientId);
|
||||
|
||||
|
||||
#endif //OPENDHT_PUSH_NOTIFICATIONS
|
||||
|
||||
using clock = std::chrono::steady_clock;
|
||||
using time_point = clock::time_point;
|
||||
|
||||
std::thread server_thread {};
|
||||
std::unique_ptr<restbed::Service> service_;
|
||||
std::shared_ptr<DhtRunner> dht_;
|
||||
|
||||
std::mutex schedulerLock_;
|
||||
std::condition_variable schedulerCv_;
|
||||
Scheduler scheduler_;
|
||||
std::thread schedulerThread_;
|
||||
|
||||
Sp<Scheduler::Job> printStatsJob_;
|
||||
mutable std::mutex statsMutex_;
|
||||
mutable NodeInfo nodeInfo_ {};
|
||||
|
||||
// Handle client quit for listen.
|
||||
// NOTE: can be simplified when we will supports restbed 5.0
|
||||
std::thread listenThread_;
|
||||
struct SessionToHashToken {
|
||||
std::shared_ptr<restbed::Session> session;
|
||||
InfoHash hash;
|
||||
std::future<size_t> token;
|
||||
};
|
||||
std::vector<SessionToHashToken> currentListeners_;
|
||||
std::mutex lockListener_;
|
||||
std::atomic_bool stopListeners {false};
|
||||
|
||||
struct PermanentPut;
|
||||
struct SearchPuts;
|
||||
std::map<InfoHash, SearchPuts> puts_;
|
||||
|
||||
mutable std::atomic<size_t> requestNum_ {0};
|
||||
mutable std::atomic<time_point> lastStatsReset_ {time_point::min()};
|
||||
|
||||
const std::string pushServer_;
|
||||
|
||||
mutable ServerStats stats_;
|
||||
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
struct Listener;
|
||||
struct PushListener;
|
||||
std::mutex lockPushListeners_;
|
||||
std::map<std::string, PushListener> pushListeners_;
|
||||
proxy::ListenToken tokenPushNotif_ {0};
|
||||
#endif //OPENDHT_PUSH_NOTIFICATIONS
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
+58
-161
@@ -1,8 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 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>
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@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
|
||||
@@ -20,12 +19,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
//#include "securedht.h"
|
||||
#include "infohash.h"
|
||||
#include "value.h"
|
||||
#include "callbacks.h"
|
||||
#include "sockaddr.h"
|
||||
#include "log_enable.h"
|
||||
#include "def.h"
|
||||
#include "uv_utils.h"
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
@@ -51,15 +52,6 @@ struct SecureDhtConfig;
|
||||
class OPENDHT_PUBLIC DhtRunner {
|
||||
|
||||
public:
|
||||
typedef std::function<void(NodeStatus, NodeStatus)> StatusCallback;
|
||||
|
||||
struct Config {
|
||||
SecureDhtConfig dht_config;
|
||||
bool threaded;
|
||||
std::string proxy_server;
|
||||
std::string push_node_id;
|
||||
};
|
||||
|
||||
DhtRunner();
|
||||
virtual ~DhtRunner();
|
||||
|
||||
@@ -136,15 +128,7 @@ public:
|
||||
query(hash, cb, bindDoneCb(done_cb), q);
|
||||
}
|
||||
|
||||
std::future<size_t> listen(InfoHash key, ValueCallback vcb, Value::Filter f = Value::AllFilter(), Where w = {});
|
||||
|
||||
std::future<size_t> listen(InfoHash key, GetCallback cb, Value::Filter f={}, Where w={}) {
|
||||
return listen(key, [cb](const std::vector<Sp<Value>>& vals, bool expired){
|
||||
if (not expired)
|
||||
return cb(vals);
|
||||
return true;
|
||||
}, std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
std::future<size_t> listen(InfoHash key, GetCallback vcb, Value::Filter f = Value::AllFilter(), Where w = {});
|
||||
std::future<size_t> listen(const std::string& key, GetCallback vcb, Value::Filter f = Value::AllFilter(), Where w = {});
|
||||
std::future<size_t> listen(InfoHash key, GetCallbackSimple cb, Value::Filter f = Value::AllFilter(), Where w = {}) {
|
||||
return listen(key, bindGetCb(cb), f, w);
|
||||
@@ -158,15 +142,6 @@ public:
|
||||
},
|
||||
getFilterSet<T>());
|
||||
}
|
||||
template <class T>
|
||||
std::future<size_t> listen(InfoHash hash, std::function<bool(std::vector<T>&&, bool)> cb)
|
||||
{
|
||||
return listen(hash, [=](const std::vector<std::shared_ptr<Value>>& vals, bool expired) {
|
||||
return cb(unpackVector<T>(vals), expired);
|
||||
},
|
||||
getFilterSet<T>());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::future<size_t> listen(InfoHash hash, std::function<bool(T&&)> cb, Value::Filter f = Value::AllFilter(), Where w = {})
|
||||
{
|
||||
@@ -183,22 +158,6 @@ public:
|
||||
},
|
||||
getFilterSet<T>(f), w);
|
||||
}
|
||||
template <typename T>
|
||||
std::future<size_t> listen(InfoHash hash, std::function<bool(T&&, bool)> cb, Value::Filter f = Value::AllFilter(), Where w = {})
|
||||
{
|
||||
return listen(hash, [=](const std::vector<std::shared_ptr<Value>>& vals, bool expired) {
|
||||
for (const auto& v : vals) {
|
||||
try {
|
||||
if (not cb(Value::unpack<T>(*v), expired))
|
||||
return false;
|
||||
} catch (const std::exception&) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
getFilterSet<T>(f), w);
|
||||
}
|
||||
|
||||
void cancelListen(InfoHash h, size_t token);
|
||||
void cancelListen(InfoHash h, std::shared_future<size_t> token);
|
||||
@@ -242,8 +201,11 @@ public:
|
||||
* Insert known nodes to the routing table, without necessarly ping them.
|
||||
* Usefull to restart a node and get things running fast without putting load on the network.
|
||||
*/
|
||||
void bootstrap(const std::vector<SockAddr>& nodes, DoneCallbackSimple&& cb={});
|
||||
void bootstrap(const SockAddr& addr, DoneCallbackSimple&& cb={});
|
||||
void ping(const std::vector<std::pair<sockaddr_storage, socklen_t>>& nodes, DoneCallback&& cb={});
|
||||
void ping(const SockAddr& addr, DoneCallback&& cb={});
|
||||
void ping(const SockAddr& addr, DoneCallbackSimple&& cb={}) {
|
||||
ping(addr, bindDoneCb(cb));
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert known nodes to the routing table, without necessarly ping them.
|
||||
@@ -273,30 +235,22 @@ public:
|
||||
|
||||
void dumpTables() const;
|
||||
|
||||
/**
|
||||
* Get the public key fingerprint if an identity is used with this node, 0 otherwise.
|
||||
*/
|
||||
InfoHash getId() const;
|
||||
|
||||
/**
|
||||
* Get the ID of the DHT node.
|
||||
*/
|
||||
InfoHash getNodeId() const;
|
||||
|
||||
/**
|
||||
* Returns the currently bound address.
|
||||
* @param f: address family of the bound address to retreive.
|
||||
*/
|
||||
const SockAddr& getBound(sa_family_t f = AF_INET) const {
|
||||
return (f == AF_INET) ? bound4 : bound6;
|
||||
}
|
||||
SockAddr getBound() const;
|
||||
|
||||
/**
|
||||
* Returns the currently bound port, in host byte order.
|
||||
* @param f: address family of the bound port to retreive.
|
||||
*/
|
||||
in_port_t getBoundPort(sa_family_t f = AF_INET) const {
|
||||
return getBound(f).getPort();
|
||||
in_port_t getBoundPort() const {
|
||||
return getBound().getPort();
|
||||
}
|
||||
|
||||
std::pair<size_t, size_t> getStoreSize() const;
|
||||
@@ -324,7 +278,6 @@ public:
|
||||
|
||||
NodeStats getNodesStats(sa_family_t af) const;
|
||||
unsigned getNodesStats(sa_family_t af, unsigned *good_return, unsigned *dubious_return, unsigned *cached_return, unsigned *incoming_return) const;
|
||||
NodeInfo getNodeInfo() const;
|
||||
|
||||
std::vector<unsigned> getNodeMessageStats(bool in = false) const;
|
||||
std::string getStorageLog() const;
|
||||
@@ -341,29 +294,36 @@ public:
|
||||
void registerCertificate(std::shared_ptr<crypto::Certificate> cert);
|
||||
void setLocalCertificateStore(CertificateStoreQuery&& query_method);
|
||||
|
||||
struct Config {
|
||||
SecureDhtConfig dht_config;
|
||||
bool threaded;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param port: Local port to bind. Both IPv4 and IPv6 will be tried (ANY).
|
||||
* @param identity: RSA key pair to use for cryptographic operations.
|
||||
* @param threaded: If false, ::loop() must be called periodically. Otherwise a thread is launched.
|
||||
* @param cb: Optional callback to receive general state information.
|
||||
*/
|
||||
void run(in_port_t port = 4222, const crypto::Identity identity = {}, bool threaded = false, NetId network = 0) {
|
||||
run(port, {
|
||||
void run(in_port_t port, const crypto::Identity identity, bool threaded = false, NetId network = 0) {
|
||||
run({
|
||||
/*.dht_config = */{
|
||||
/*.node_config = */{
|
||||
/*.node_id = */{},
|
||||
/*.network = */network,
|
||||
/*.is_bootstrap = */false,
|
||||
/*.maintain_storage*/false
|
||||
/*.maintain_storage = */false,
|
||||
/* .network_config = */{
|
||||
/*.addr = */ "::0",
|
||||
/*.port = */ port
|
||||
}
|
||||
},
|
||||
/*.id = */identity
|
||||
},
|
||||
/*.threaded = */threaded,
|
||||
/*.proxy_server = */"",
|
||||
/*.push_node_id = */""
|
||||
/*.threaded = */threaded
|
||||
});
|
||||
}
|
||||
void run(in_port_t port, Config config);
|
||||
void run(Config config);
|
||||
|
||||
/**
|
||||
* @param local4: Local IPv4 address and port to bind. Can be null.
|
||||
@@ -373,31 +333,22 @@ public:
|
||||
* @param threaded: If false, loop() must be called periodically. Otherwise a thread is launched.
|
||||
* @param cb: Optional callback to receive general state information.
|
||||
*/
|
||||
void run(const SockAddr& local4, const SockAddr& local6, Config config);
|
||||
//void run(const sockaddr_in* local4, const sockaddr_in6* local6, Config config);
|
||||
|
||||
/**
|
||||
* Same as @run(sockaddr_in, sockaddr_in6, Identity, bool, StatusCallback), but with string IP addresses and service (port).
|
||||
*/
|
||||
void run(const char* ip4, const char* ip6, const char* service, Config config);
|
||||
//void run(const char* ip4, const char* ip6, const char* service, Config config);
|
||||
|
||||
void setOnStatusChanged(StatusCallback&& cb) {
|
||||
statusCb = std::move(cb);
|
||||
}
|
||||
void setOnStatusChanged(StatusCallback&& 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);
|
||||
time_point wakeup = time_point::min();
|
||||
try {
|
||||
wakeup = loop_();
|
||||
} catch (const dht::SocketException& e) {
|
||||
startNetwork(bound4, bound6);
|
||||
}
|
||||
return wakeup;
|
||||
void loop() {
|
||||
uv_loop_.runOnce();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,30 +363,6 @@ public:
|
||||
*/
|
||||
void join();
|
||||
|
||||
void setProxyServer(const std::string& proxy, const std::string& pushNodeId = "");
|
||||
|
||||
/**
|
||||
* Start or stop the proxy
|
||||
* @param proxify if we want to use the proxy
|
||||
* @param deviceKey non empty to enable push notifications
|
||||
*/
|
||||
void enableProxy(bool proxify);
|
||||
|
||||
/* Push notification methods */
|
||||
|
||||
/**
|
||||
* Updates the push notification device token
|
||||
*/
|
||||
void setPushNotificationToken(const std::string& token);
|
||||
|
||||
/**
|
||||
* Insert a push notification to process for OpenDHT
|
||||
*/
|
||||
void pushNotificationReceived(const std::map<std::string, std::string>& data);
|
||||
|
||||
/* Proxy server mothods */
|
||||
void forwardAllMessages(bool forward);
|
||||
|
||||
private:
|
||||
static constexpr std::chrono::seconds BOOTSTRAP_PERIOD {10};
|
||||
|
||||
@@ -447,84 +374,54 @@ private:
|
||||
*/
|
||||
void tryBootstrapContinuously();
|
||||
|
||||
void stopNetwork();
|
||||
void startNetwork(const SockAddr sin4, const SockAddr sin6);
|
||||
time_point loop_();
|
||||
void doRun(SecureDhtConfig config);
|
||||
void loop_();
|
||||
|
||||
NodeStatus getStatus() const {
|
||||
return std::max(status4, status6);
|
||||
static std::vector<std::pair<sockaddr_storage, socklen_t>> getAddrInfo(const std::string& host, const std::string& service);
|
||||
|
||||
void loopCallback() {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
if (running)
|
||||
loop_();
|
||||
else {
|
||||
uv_close((uv_handle_t*)&uv_async_, nullptr);
|
||||
uv_loop_.stop();
|
||||
}
|
||||
}
|
||||
|
||||
static void loop_callback(uv_async_t* handle) {
|
||||
if (handle->data)
|
||||
static_cast<DhtRunner*>(handle->data)->loopCallback();
|
||||
}
|
||||
|
||||
/** Local DHT instance */
|
||||
std::unique_ptr<SecureDht> dht_;
|
||||
|
||||
/** Proxy client instance */
|
||||
std::unique_ptr<SecureDht> dht_via_proxy_;
|
||||
|
||||
/** true if we are currently using a proxy */
|
||||
std::atomic_bool use_proxy {false};
|
||||
|
||||
/** Current configuration */
|
||||
Config config_;
|
||||
|
||||
/**
|
||||
* reset dht clients
|
||||
*/
|
||||
void resetDht();
|
||||
/**
|
||||
* @return the current active DHT
|
||||
*/
|
||||
SecureDht* activeDht() const;
|
||||
|
||||
/**
|
||||
* Store current listeners and translates global tokens for each client.
|
||||
*/
|
||||
struct Listener;
|
||||
std::map<size_t, Listener> listeners_;
|
||||
size_t listener_token_ {1};
|
||||
|
||||
EventLoop uv_loop_;
|
||||
uv_async_t uv_async_;
|
||||
mutable std::mutex dht_mtx {};
|
||||
std::thread dht_thread {};
|
||||
std::condition_variable cv {};
|
||||
|
||||
std::thread rcv_thread {};
|
||||
std::mutex sock_mtx {};
|
||||
|
||||
struct ReceivedPacket {
|
||||
Blob data;
|
||||
SockAddr from;
|
||||
time_point received;
|
||||
};
|
||||
std::queue<ReceivedPacket> rcv {};
|
||||
|
||||
/** true if currently actively boostraping */
|
||||
std::atomic_bool bootstraping {false};
|
||||
/* bootstrap nodes given as (host, service) pairs */
|
||||
std::vector<std::pair<std::string,std::string>> bootstrap_nodes_all {};
|
||||
/*std::vector<std::pair<std::string,std::string>> bootstrap_nodes_all {};
|
||||
std::vector<std::pair<std::string,std::string>> bootstrap_nodes {};
|
||||
std::thread bootstrap_thread {};
|
||||
std::thread bootstrap_thread {};*/
|
||||
/** protects bootstrap_nodes, bootstrap_thread */
|
||||
std::mutex bootstrap_mtx {};
|
||||
std::condition_variable bootstrap_cv {};
|
||||
/*std::mutex bootstrap_mtx {};
|
||||
std::condition_variable bootstrap_cv {};*/
|
||||
|
||||
std::queue<std::function<void(SecureDht&)>> pending_ops_prio {};
|
||||
std::queue<std::function<void(SecureDht&)>> pending_ops {};
|
||||
std::mutex storage_mtx {};
|
||||
|
||||
std::atomic_bool running {false};
|
||||
std::atomic_bool running_network {false};
|
||||
std::atomic<bool> running {false};
|
||||
|
||||
NodeStatus status4 {NodeStatus::Disconnected},
|
||||
/*NodeStatus status4 {NodeStatus::Disconnected},
|
||||
status6 {NodeStatus::Disconnected};
|
||||
StatusCallback statusCb {nullptr};
|
||||
|
||||
int stop_writefd {-1};
|
||||
int s4 {-1}, s6 {-1};
|
||||
SockAddr bound4 {};
|
||||
SockAddr bound6 {};
|
||||
|
||||
/** Push notification token */
|
||||
std::string pushToken_;
|
||||
SockAddr bound6 {};*/
|
||||
};
|
||||
|
||||
}
|
||||
+151
-197
@@ -19,10 +19,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "def.h"
|
||||
#include "rng.h"
|
||||
|
||||
#include <msgpack.hpp>
|
||||
|
||||
#include <sodium.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
@@ -45,99 +46,139 @@ typedef uint16_t in_port_t;
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
// bytes
|
||||
#define HASH_LEN crypto_box_PUBLICKEYBYTES
|
||||
|
||||
namespace dht {
|
||||
|
||||
using byte = uint8_t;
|
||||
|
||||
namespace crypto {
|
||||
OPENDHT_PUBLIC void hash(const uint8_t* data, size_t data_length, uint8_t* hash, size_t hash_length);
|
||||
template <size_t N>
|
||||
class OPENDHT_PUBLIC Hash
|
||||
{
|
||||
public:
|
||||
Hash() {
|
||||
data_.fill(0);
|
||||
}
|
||||
Hash(const msgpack::object& o) {
|
||||
msgpack_unpack(o);
|
||||
}
|
||||
byte* data() {
|
||||
return data_.data();
|
||||
}
|
||||
const byte* data() const {
|
||||
return data_.data();
|
||||
}
|
||||
constexpr size_t size() const { return data_.size(); }
|
||||
explicit operator bool() const { return std::any_of(data_.begin(), data_.end(), [](byte _b) { return _b != 0; }); }
|
||||
bool operator==(Hash const& _c) const { return data_ == _c.data_; }
|
||||
bool operator!=(Hash const& _c) const { return data_ != _c.data_; }
|
||||
bool operator<(Hash const& _c) const { for (unsigned i = 0; i < N; ++i) if (data_[i] < _c.data_[i]) return true; else if (data_[i] > _c.data_[i]) return false; return false; }
|
||||
bool operator>=(Hash const& _c) const { return !operator<(_c); }
|
||||
bool operator<=(Hash const& _c) const { return operator==(_c) || operator<(_c); }
|
||||
bool operator>(Hash const& _c) const { return !operator<=(_c); }
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& pk) const
|
||||
{
|
||||
pk.pack_bin(N);
|
||||
pk.pack_bin_body((char*)data_.data(), N);
|
||||
}
|
||||
void msgpack_unpack(msgpack::object o) {
|
||||
if (o.type != msgpack::type::BIN or o.via.bin.size != N)
|
||||
throw msgpack::type_error();
|
||||
std::copy_n(o.via.bin.ptr, N, data_.data());
|
||||
}
|
||||
|
||||
template <size_t M>
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const Hash<M>& h);
|
||||
|
||||
template <size_t M>
|
||||
OPENDHT_PUBLIC friend std::istream& operator>> (std::istream& s, Hash<M>& h);
|
||||
|
||||
std::string toString() const {
|
||||
std::stringstream ss;
|
||||
ss << *this;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<byte, N> data_;
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
std::ostream& operator<< (std::ostream& s, const Hash<N>& h)
|
||||
{
|
||||
s << std::hex;
|
||||
for (unsigned i=0; i<h.size(); i++)
|
||||
s << std::setfill('0') << std::setw(2) << (unsigned)h.data_[i];
|
||||
s << std::dec;
|
||||
return s;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
std::istream& operator>> (std::istream& s, Hash<N>& h)
|
||||
{
|
||||
std::array<char, h.size()*2> dat;
|
||||
s.exceptions(std::istream::eofbit | std::istream::failbit);
|
||||
s.read(&(*dat.begin()), dat.size());
|
||||
for (size_t i = 0; i < h.size(); i++) {
|
||||
unsigned res = 0;
|
||||
sscanf(dat.data() + 2*i, "%02x", &res);
|
||||
h[i] = res;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
using h256 = Hash<32>;
|
||||
using PkId = h256;
|
||||
|
||||
/**
|
||||
* Represents an InfoHash.
|
||||
* An InfoHash is a byte array of HASH_LEN bytes.
|
||||
* InfoHashes identify nodes and values in the Dht.
|
||||
*/
|
||||
template <size_t N>
|
||||
class OPENDHT_PUBLIC Hash {
|
||||
class OPENDHT_PUBLIC InfoHash final : public std::array<uint8_t, HASH_LEN> {
|
||||
public:
|
||||
using T = std::array<uint8_t, N>;
|
||||
typedef typename T::iterator iterator;
|
||||
typedef typename T::const_iterator const_iterator;
|
||||
|
||||
Hash () {
|
||||
data_.fill(0);
|
||||
}
|
||||
Hash (const uint8_t* h, size_t data_len) {
|
||||
if (data_len < N)
|
||||
data_.fill(0);
|
||||
constexpr InfoHash() : std::array<uint8_t, HASH_LEN>() {}
|
||||
constexpr InfoHash(const std::array<uint8_t, HASH_LEN>& h) : std::array<uint8_t, HASH_LEN>(h) {}
|
||||
InfoHash(const uint8_t* h, size_t h_len) : std::array<uint8_t, HASH_LEN>() {
|
||||
if (h_len < HASH_LEN)
|
||||
fill(0);
|
||||
else
|
||||
std::copy_n(h, N, data_.begin());
|
||||
std::copy_n(h, HASH_LEN, begin());
|
||||
}
|
||||
|
||||
std::vector<uint8_t> encrypt(const uint8_t* msg, size_t msg_size) const {
|
||||
std::vector<uint8_t> ret(crypto_box_SEALBYTES+msg_size);
|
||||
crypto_box_seal(ret.data(), msg, msg_size, data());
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor from an hexadecimal string (without "0x").
|
||||
* hex must be at least 2.HASH_LEN characters long.
|
||||
* If too long, only the first 2.HASH_LEN characters are read.
|
||||
*/
|
||||
explicit Hash(const std::string& hex);
|
||||
explicit InfoHash(const std::string& hex);
|
||||
|
||||
Hash(const msgpack::object& o) {
|
||||
InfoHash(const msgpack::object& o) {
|
||||
msgpack_unpack(o);
|
||||
}
|
||||
|
||||
size_t size() const { return data_.size(); }
|
||||
const uint8_t* data() const { return data_.data(); }
|
||||
uint8_t* data() { return data_.data(); }
|
||||
iterator begin() { return data_.begin(); }
|
||||
const_iterator cbegin() const { return data_.cbegin(); }
|
||||
iterator end() { return data_.end(); }
|
||||
const_iterator cend() const { return data_.cend(); }
|
||||
|
||||
bool operator==(const Hash& h) const {
|
||||
auto a = reinterpret_cast<const uint32_t*>(data_.data());
|
||||
auto b = reinterpret_cast<const uint32_t*>(h.data_.data());
|
||||
constexpr unsigned n = N / sizeof(uint32_t);
|
||||
for (unsigned i=0; i < n; i++)
|
||||
if (a[i] != b[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator!=(const Hash& h) const { return !(*this == h); }
|
||||
|
||||
bool operator<(const Hash& o) const {
|
||||
for(unsigned i = 0; i < N; i++) {
|
||||
if(data_[i] != o.data_[i])
|
||||
return data_[i] < o.data_[i];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
auto a = reinterpret_cast<const uint32_t*>(data_.data());
|
||||
auto b = reinterpret_cast<const uint32_t*>(data_.data() + N);
|
||||
for (; a != b; a++) {
|
||||
if (*a)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t& operator[](size_t index) { return data_[index]; }
|
||||
const uint8_t& operator[](size_t index) const { return data_[index]; }
|
||||
|
||||
/**
|
||||
* Find the lowest 1 bit in an id.
|
||||
* Result will allways be lower than 8*N
|
||||
* Result will allways be lower than 8*HASH_LEN
|
||||
*/
|
||||
inline int lowbit() const {
|
||||
inline unsigned lowbit() const {
|
||||
int i, j;
|
||||
for(i = N-1; i >= 0; i--)
|
||||
if(data_[i] != 0)
|
||||
for(i = HASH_LEN-1; i >= 0; i--)
|
||||
if((*this)[i] != 0)
|
||||
break;
|
||||
if(i < 0)
|
||||
return -1;
|
||||
for(j = 7; j >= 0; j--)
|
||||
if((data_[i] & (0x80 >> j)) != 0)
|
||||
if(((*this)[i] & (0x80 >> j)) != 0)
|
||||
break;
|
||||
return 8 * i + j;
|
||||
}
|
||||
@@ -146,25 +187,25 @@ public:
|
||||
* Forget about the ``XOR-metric''. An id is just a path from the
|
||||
* root of the tree, so bits are numbered from the start.
|
||||
*/
|
||||
static inline int cmp(const Hash& id1, const Hash& id2) {
|
||||
return std::memcmp(id1.data_.data(), id2.data_.data(), N);
|
||||
static inline int cmp(const InfoHash& id1, const InfoHash& id2) {
|
||||
return std::memcmp(id1.data(), id2.data(), HASH_LEN);
|
||||
}
|
||||
|
||||
/** Find how many bits two ids have in common. */
|
||||
static inline unsigned
|
||||
commonBits(const Hash& id1, const Hash& id2)
|
||||
commonBits(const InfoHash& id1, const InfoHash& id2)
|
||||
{
|
||||
unsigned i, j;
|
||||
uint8_t x;
|
||||
for(i = 0; i < N; i++) {
|
||||
if(id1.data_[i] != id2.data_[i])
|
||||
for(i = 0; i < HASH_LEN; i++) {
|
||||
if(id1[i] != id2[i])
|
||||
break;
|
||||
}
|
||||
|
||||
if(i == N)
|
||||
return 8*N;
|
||||
if(i == HASH_LEN)
|
||||
return 8*HASH_LEN;
|
||||
|
||||
x = id1.data_[i] ^ id2.data_[i];
|
||||
x = id1[i] ^ id2[i];
|
||||
|
||||
j = 0;
|
||||
while((x & 0x80) == 0) {
|
||||
@@ -177,14 +218,14 @@ public:
|
||||
|
||||
/** Determine whether id1 or id2 is closer to this */
|
||||
int
|
||||
xorCmp(const Hash& id1, const Hash& id2) const
|
||||
xorCmp(const InfoHash& id1, const InfoHash& id2) const
|
||||
{
|
||||
for(unsigned i = 0; i < N; i++) {
|
||||
for(unsigned i = 0; i < HASH_LEN; i++) {
|
||||
uint8_t xor1, xor2;
|
||||
if(id1.data_[i] == id2.data_[i])
|
||||
if(id1[i] == id2[i])
|
||||
continue;
|
||||
xor1 = id1.data_[i] ^ data_[i];
|
||||
xor2 = id2.data_[i] ^ data_[i];
|
||||
xor1 = id1[i] ^ (*this)[i];
|
||||
xor2 = id2[i] ^ (*this)[i];
|
||||
if(xor1 < xor2)
|
||||
return -1;
|
||||
else
|
||||
@@ -196,7 +237,7 @@ public:
|
||||
bool
|
||||
getBit(unsigned nbit) const
|
||||
{
|
||||
auto& num = *(data_.cbegin()+(nbit/8));
|
||||
auto& num = *(cbegin()+(nbit/8));
|
||||
unsigned bit = 7 - (nbit % 8);
|
||||
return (num >> bit) & 1;
|
||||
}
|
||||
@@ -204,7 +245,7 @@ public:
|
||||
void
|
||||
setBit(unsigned nbit, bool b)
|
||||
{
|
||||
auto& num = data_[nbit/8];
|
||||
auto& num = (*this)[nbit/8];
|
||||
unsigned bit = 7 - (nbit % 8);
|
||||
num ^= (-b ^ num) & (1 << bit);
|
||||
}
|
||||
@@ -212,36 +253,45 @@ public:
|
||||
double toFloat() const {
|
||||
using D = size_t;
|
||||
double v = 0.;
|
||||
for (size_t i = 0; i < std::min<size_t>(N, sizeof(D)-1); i++)
|
||||
v += *(data_.cbegin()+i) / (double)((D)1 << 8*(i+1));
|
||||
for (size_t i = 0; i < std::min<size_t>(HASH_LEN, sizeof(D)-1); i++)
|
||||
v += *(cbegin()+i) / (double)((D)1 << 8*(i+1));
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline Hash get(const std::string& data) {
|
||||
bool operator==(const InfoHash& h) const {
|
||||
auto a = reinterpret_cast<const uint32_t*>(data());
|
||||
auto b = reinterpret_cast<const uint32_t*>(h.data());
|
||||
for (size_t i=0; i<(size()/sizeof(uint32_t)); i++)
|
||||
if (a[i] != b[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator<(const InfoHash& o) const {
|
||||
for(unsigned i = 0; i < HASH_LEN; i++) {
|
||||
if((*this)[i] != o[i])
|
||||
return (*this)[i] < o[i];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline InfoHash get(const std::string& data) {
|
||||
return get((const uint8_t*)data.data(), data.size());
|
||||
}
|
||||
|
||||
static inline Hash get(const std::vector<uint8_t>& data) {
|
||||
static inline InfoHash get(const std::vector<uint8_t>& data) {
|
||||
return get(data.data(), data.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the hash from a given data buffer of size data_len.
|
||||
*/
|
||||
static Hash get(const uint8_t* data, size_t data_len)
|
||||
{
|
||||
Hash ret;
|
||||
crypto::hash(data, data_len, ret.data(), N);
|
||||
return ret;
|
||||
}
|
||||
static InfoHash get(const uint8_t* data, size_t data_len);
|
||||
|
||||
static Hash getRandom();
|
||||
static InfoHash getRandom();
|
||||
|
||||
template <size_t M>
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const Hash<M>& h);
|
||||
|
||||
template <size_t M>
|
||||
OPENDHT_PUBLIC friend std::istream& operator>> (std::istream& s, Hash<M>& h);
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const InfoHash& h);
|
||||
OPENDHT_PUBLIC friend std::istream& operator>> (std::istream& s, InfoHash& h);
|
||||
|
||||
const char* to_c_str() const;
|
||||
|
||||
@@ -250,115 +300,19 @@ public:
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& pk) const
|
||||
{
|
||||
pk.pack_bin(N);
|
||||
pk.pack_bin_body((char*)data_.data(), N);
|
||||
pk.pack_bin(HASH_LEN);
|
||||
pk.pack_bin_body((char*)data(), HASH_LEN);
|
||||
}
|
||||
|
||||
void msgpack_unpack(msgpack::object o) {
|
||||
if (o.type != msgpack::type::BIN or o.via.bin.size != N)
|
||||
if (o.type != msgpack::type::BIN or o.via.bin.size != HASH_LEN)
|
||||
throw msgpack::type_error();
|
||||
std::copy_n(o.via.bin.ptr, N, data_.data());
|
||||
std::copy_n(o.via.bin.ptr, HASH_LEN, data());
|
||||
}
|
||||
private:
|
||||
T data_;
|
||||
void fromString(const char*);
|
||||
|
||||
};
|
||||
|
||||
#define HASH_LEN 20u
|
||||
using InfoHash = Hash<HASH_LEN>;
|
||||
using h256 = Hash<32>;
|
||||
using PkId = h256;
|
||||
|
||||
template <size_t N>
|
||||
std::ostream& operator<< (std::ostream& s, const Hash<N>& h)
|
||||
{
|
||||
s.write(h.to_c_str(), N*2);
|
||||
return s;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
std::istream& operator>> (std::istream& s, Hash<N>& h)
|
||||
{
|
||||
std::array<char, h.size()*2> dat;
|
||||
s.exceptions(std::istream::eofbit | std::istream::failbit);
|
||||
s.read(&(*dat.begin()), dat.size());
|
||||
fromString(dat.data());
|
||||
return s;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
Hash<N>::Hash(const std::string& hex) {
|
||||
if (hex.size() < 2*N)
|
||||
data_.fill(0);
|
||||
else
|
||||
fromString(hex.c_str());
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
void
|
||||
Hash<N>::fromString(const char* in) {
|
||||
auto hex2bin = [](char c) -> uint8_t {
|
||||
if (c >= 'a' and c <= 'f') return 10 + c - 'a';
|
||||
else if (c >= 'A' and c <= 'F') return 10 + c - 'A';
|
||||
else if (c >= '0' and c <= '9') return c - '0';
|
||||
else throw std::domain_error("not an hex character");
|
||||
};
|
||||
try {
|
||||
for (size_t i=0; i<N; i++)
|
||||
data_[i] = (hex2bin(in[2*i]) << 4) | hex2bin(in[2*i+1]);
|
||||
} catch (const std::domain_error&) {
|
||||
data_.fill(0);
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
Hash<N>
|
||||
Hash<N>::getRandom()
|
||||
{
|
||||
Hash h;
|
||||
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());
|
||||
std::generate(a, b, std::bind(rand_int, std::ref(rdev)));
|
||||
return h;
|
||||
}
|
||||
|
||||
struct HexMap : public std::array<std::array<char, 2>, 256> {
|
||||
HexMap() {
|
||||
for (size_t i=0; i<size(); i++) {
|
||||
auto& e = (*this)[i];
|
||||
e[0] = hex_digits[(i >> 4) & 0x0F];
|
||||
e[1] = hex_digits[i & 0x0F];
|
||||
}
|
||||
}
|
||||
private:
|
||||
static constexpr const char* hex_digits = "0123456789abcdef";
|
||||
};
|
||||
|
||||
OPENDHT_PUBLIC extern const HexMap hex_map;
|
||||
|
||||
template <size_t N>
|
||||
const char*
|
||||
Hash<N>::to_c_str() const
|
||||
{
|
||||
thread_local std::array<char, N*2+1> buf;
|
||||
for (size_t i=0; i<N; i++) {
|
||||
auto b = buf.data()+i*2;
|
||||
const auto& m = hex_map[data_[i]];
|
||||
*((uint16_t*)b) = *((uint16_t*)&m);
|
||||
}
|
||||
return buf.data();
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
std::string
|
||||
Hash<N>::toString() const
|
||||
{
|
||||
return std::string(to_c_str(), N*2);
|
||||
}
|
||||
|
||||
const InfoHash zeroes {};
|
||||
static constexpr const InfoHash zeroes {};
|
||||
|
||||
struct OPENDHT_PUBLIC NodeExport {
|
||||
InfoHash id;
|
||||
@@ -378,7 +332,7 @@ struct OPENDHT_PUBLIC NodeExport {
|
||||
|
||||
void msgpack_unpack(msgpack::object o);
|
||||
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const NodeExport& h);
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const InfoHash& h);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -43,23 +43,8 @@ inline void NOLOG(char const*, va_list) {}
|
||||
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) {}
|
||||
LogMethod(T&& t) : func(std::forward<T>(t)) {}
|
||||
|
||||
void operator()(char const* format, ...) const {
|
||||
va_list args;
|
||||
@@ -85,12 +70,12 @@ private:
|
||||
};
|
||||
|
||||
struct Logger {
|
||||
LogMethod DBG = NOLOG;
|
||||
LogMethod DEBUG = NOLOG;
|
||||
LogMethod WARN = NOLOG;
|
||||
LogMethod ERR = NOLOG;
|
||||
void setFilter(const InfoHash& f) {
|
||||
filter_ = f;
|
||||
filterEnable_ = static_cast<bool>(filter_);
|
||||
filterEnable_ = filter_ != InfoHash{};
|
||||
}
|
||||
inline void log0(const LogMethod& logger, char const* format, va_list args) const {
|
||||
#if OPENDHT_LOG
|
||||
@@ -114,7 +99,7 @@ struct Logger {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log0(DBG, format, args);
|
||||
log0(DEBUG, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
@@ -122,7 +107,7 @@ struct Logger {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log1(DBG, f, format, args);
|
||||
log1(DEBUG, f, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
@@ -130,7 +115,7 @@ struct Logger {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log2(DBG, f1, f2, format, args);
|
||||
log2(DEBUG, f1, f2, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@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 <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace dht {
|
||||
namespace net {
|
||||
|
||||
struct TransPrefix : public std::array<uint8_t, 2> {
|
||||
TransPrefix(const std::string& str) : std::array<uint8_t, 2>({{(uint8_t)str[0], (uint8_t)str[1]}}) {}
|
||||
static const TransPrefix PING;
|
||||
static const TransPrefix FIND_NODE;
|
||||
static const TransPrefix GET_VALUES;
|
||||
static const TransPrefix ANNOUNCE_VALUES;
|
||||
static const TransPrefix REFRESH;
|
||||
static const TransPrefix LISTEN;
|
||||
};
|
||||
|
||||
/* Transaction-ids are 4-bytes long, with the first two bytes identifying
|
||||
* the kind of request, and the remaining two a sequence number in
|
||||
* host order.
|
||||
*/
|
||||
struct TransId final : public std::array<uint8_t, 4> {
|
||||
static const constexpr uint16_t INVALID {0};
|
||||
|
||||
TransId() { std::fill(begin(), end(), 0); }
|
||||
TransId(const std::array<char, 4>& o) { std::copy(o.begin(), o.end(), begin()); }
|
||||
TransId(const TransPrefix prefix, uint16_t seqno = 0) {
|
||||
std::copy_n(prefix.begin(), prefix.size(), begin());
|
||||
*reinterpret_cast<uint16_t*>(data()+prefix.size()) = seqno;
|
||||
}
|
||||
TransId(uint32_t id) {
|
||||
*reinterpret_cast<uint32_t*>(data()) = htonl(id);
|
||||
}
|
||||
|
||||
uint32_t toInt() const {
|
||||
return ntohl(*reinterpret_cast<const uint32_t*>(&(*this)[0]));
|
||||
}
|
||||
|
||||
bool matches(const TransPrefix prefix) const {
|
||||
return std::equal(begin(), begin()+2, prefix.begin());
|
||||
}
|
||||
};
|
||||
|
||||
TransId unpackTid(msgpack::object& o);
|
||||
|
||||
} /* namespace net */
|
||||
} /* dht */
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2018 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
@@ -27,6 +27,9 @@
|
||||
#include "utils.h"
|
||||
#include "rng.h"
|
||||
#include "rate_limiter.h"
|
||||
#include "net.h"
|
||||
#include "callbacks.h"
|
||||
#include "uv_utils.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
@@ -40,7 +43,6 @@ namespace net {
|
||||
|
||||
struct Request;
|
||||
struct Socket;
|
||||
struct TransId;
|
||||
|
||||
#ifndef MSG_CONFIRM
|
||||
#define MSG_CONFIRM 0
|
||||
@@ -87,8 +89,6 @@ struct RequestAnswer {
|
||||
Blob ntoken {};
|
||||
Value::Id vid {};
|
||||
std::vector<Sp<Value>> values {};
|
||||
std::vector<Value::Id> refreshed_values {};
|
||||
std::vector<Value::Id> expired_values {};
|
||||
std::vector<Sp<FieldValueIndex>> fields {};
|
||||
std::vector<Sp<Node>> nodes4 {};
|
||||
std::vector<Sp<Node>> nodes6 {};
|
||||
@@ -171,7 +171,7 @@ private:
|
||||
std::function<RequestAnswer(Sp<Node>,
|
||||
const InfoHash&,
|
||||
const Blob&,
|
||||
Tid,
|
||||
uint32_t,
|
||||
const Query&)> onListen {};
|
||||
/**
|
||||
* Called on announce request.
|
||||
@@ -204,8 +204,8 @@ public:
|
||||
using RequestCb = std::function<void(const Request&, RequestAnswer&&)>;
|
||||
using RequestExpiredCb = std::function<void(const Request&, bool)>;
|
||||
|
||||
NetworkEngine(Logger& log, Scheduler& scheduler, const int& s = -1, const int& s6 = -1);
|
||||
NetworkEngine(InfoHash& myid, NetId net, const int& s, const int& s6, Logger& log, Scheduler& scheduler,
|
||||
NetworkEngine(uv_loop_t*, Logger& log, Scheduler& scheduler);
|
||||
NetworkEngine(uv_loop_t*, const NetworkConfig& config, NetId net, Logger& log, Scheduler& scheduler,
|
||||
decltype(NetworkEngine::onError) onError,
|
||||
decltype(NetworkEngine::onNewNode) onNewNode,
|
||||
decltype(NetworkEngine::onReportedAddr) onReportedAddr,
|
||||
@@ -218,7 +218,16 @@ public:
|
||||
|
||||
virtual ~NetworkEngine();
|
||||
|
||||
const InfoHash& getNodeId() const {
|
||||
return id_key.getPublicKey();
|
||||
}
|
||||
|
||||
void clear();
|
||||
void close(OnClose cb) {
|
||||
sock->close(cb);
|
||||
tcp_sock->close();
|
||||
cache.closeAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends values (with closest nodes) to a listenner.
|
||||
@@ -233,15 +242,12 @@ public:
|
||||
* @param nodes6 The ipv6 closest nodes.
|
||||
* @param values The values to send.
|
||||
*/
|
||||
void tellListener(Sp<Node> n, Tid socket_id, const InfoHash& hash, want_t want, const Blob& ntoken,
|
||||
void tellListener(Sp<Node> n, uint32_t socket_id, const InfoHash& hash, want_t want, const Blob& ntoken,
|
||||
std::vector<Sp<Node>>&& nodes, std::vector<Sp<Node>>&& nodes6,
|
||||
std::vector<Sp<Value>>&& values, const Query& q);
|
||||
|
||||
void tellListenerRefreshed(Sp<Node> n, Tid socket_id, const InfoHash& hash, const Blob& ntoken, const std::vector<Value::Id>& values);
|
||||
void tellListenerExpired(Sp<Node> n, Tid socket_id, const InfoHash& hash, const Blob& ntoken, const std::vector<Value::Id>& values);
|
||||
|
||||
bool isRunning(sa_family_t af) const;
|
||||
inline want_t want () const { return dht_socket >= 0 && dht_socket6 >= 0 ? (WANT4 | WANT6) : -1; }
|
||||
inline want_t want () const { /*return dht_socket >= 0 && dht_socket6 >= 0 ? (WANT4 | WANT6) : -1;*/return WANT4 | WANT6; }
|
||||
|
||||
void connectivityChanged(sa_family_t);
|
||||
|
||||
@@ -259,7 +265,7 @@ public:
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
Sp<Request>
|
||||
sendPing(Sp<Node> n, RequestCb&& on_done, RequestExpiredCb&& on_expired);
|
||||
sendPing(const Sp<Node>& n, RequestCb&& on_done, RequestExpiredCb&& on_expired);
|
||||
/**
|
||||
* Send a "ping" request to a given node.
|
||||
*
|
||||
@@ -271,8 +277,8 @@ public:
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
Sp<Request>
|
||||
sendPing(const sockaddr* sa, socklen_t salen, RequestCb&& on_done, RequestExpiredCb&& on_expired) {
|
||||
return sendPing(std::make_shared<Node>(zeroes, sa, salen),
|
||||
sendPing(const SockAddr& sa, RequestCb&& on_done, RequestExpiredCb&& on_expired) {
|
||||
return sendPing(std::make_shared<Node>(zeroes, sa),
|
||||
std::forward<RequestCb>(on_done),
|
||||
std::forward<RequestExpiredCb>(on_expired));
|
||||
}
|
||||
@@ -288,7 +294,7 @@ public:
|
||||
*
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
Sp<Request> sendFindNode(Sp<Node> n,
|
||||
Sp<Request> sendFindNode(const Sp<Node>& n,
|
||||
const InfoHash& hash,
|
||||
want_t want,
|
||||
RequestCb&& on_done,
|
||||
@@ -307,7 +313,7 @@ public:
|
||||
*
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
Sp<Request> sendGetValues(Sp<Node> n,
|
||||
Sp<Request> sendGetValues(const Sp<Node>& n,
|
||||
const InfoHash& hash,
|
||||
const Query& query,
|
||||
want_t want,
|
||||
@@ -336,7 +342,7 @@ public:
|
||||
*
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
Sp<Request> sendListen(Sp<Node> n,
|
||||
Sp<Request> sendListen(const Sp<Node>& n,
|
||||
const InfoHash& hash,
|
||||
const Query& query,
|
||||
const Blob& token,
|
||||
@@ -357,7 +363,7 @@ public:
|
||||
*
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
Sp<Request> sendAnnounceValue(Sp<Node> n,
|
||||
Sp<Request> sendAnnounceValue(const Sp<Node>& n,
|
||||
const InfoHash& hash,
|
||||
const Sp<Value>& v,
|
||||
time_point created,
|
||||
@@ -377,26 +383,15 @@ public:
|
||||
*
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
Sp<Request> sendRefreshValue(Sp<Node> n,
|
||||
Sp<Request> sendRefreshValue(const Sp<Node>& n,
|
||||
const InfoHash& hash,
|
||||
const Value::Id& vid,
|
||||
const Blob& token,
|
||||
RequestCb&& on_done,
|
||||
RequestExpiredCb&& on_expired);
|
||||
|
||||
/**
|
||||
* Parses a message and calls appropriate callbacks.
|
||||
*
|
||||
* @param buf The buffer containing the binary message.
|
||||
* @param buflen The length of the buffer.
|
||||
* @param from The address info of the sender.
|
||||
* @param fromlen The length of the corresponding sockaddr structure.
|
||||
* @param now The time to adjust the clock in the network engine.
|
||||
*/
|
||||
void processMessage(const uint8_t *buf, size_t buflen, const SockAddr& addr);
|
||||
|
||||
Sp<Node> insertNode(const InfoHash& myid, const SockAddr& addr) {
|
||||
auto n = cache.getNode(myid, addr, scheduler.time(), 0);
|
||||
auto n = cache.getNode(myid, addr, {}, scheduler.time(), 0);
|
||||
onNewNode(n, 0);
|
||||
return n;
|
||||
}
|
||||
@@ -414,6 +409,10 @@ public:
|
||||
return cache.getCachedNodes(id, sa_f, count);
|
||||
}
|
||||
|
||||
SockAddr getBoundAddr() {
|
||||
return sock->getBoundAddr();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
struct PartialMessage;
|
||||
@@ -442,7 +441,20 @@ private:
|
||||
|
||||
static const std::string my_v;
|
||||
|
||||
void process(std::unique_ptr<ParsedMessage>&&, const SockAddr& from);
|
||||
/**
|
||||
* Parses a message and calls appropriate callbacks.
|
||||
*
|
||||
* @param buf The buffer containing the binary message.
|
||||
* @param buflen The length of the buffer.
|
||||
* @param from The address info of the sender.
|
||||
* @param fromlen The length of the corresponding sockaddr structure.
|
||||
* @param now The time to adjust the clock in the network engine.
|
||||
*/
|
||||
void processMessage(const uint8_t* data, size_t size, const SockAddr& addr);
|
||||
Sp<Node> processEncrypted(const msgpack::object& o, const SockAddr& from);
|
||||
Sp<Node> process(const msgpack::object& o, const SockAddr& addr);
|
||||
|
||||
Sp<Node> process(std::unique_ptr<ParsedMessage>&&, const SockAddr& from, const Sp<TcpSocket>& s = {});
|
||||
|
||||
bool rateLimit(const SockAddr& addr);
|
||||
|
||||
@@ -467,21 +479,25 @@ private:
|
||||
};
|
||||
|
||||
|
||||
// basic wrapper for socket sendto function
|
||||
int send(const char *buf, size_t len, int flags, const SockAddr& addr);
|
||||
int send(msgpack::sbuffer& msg, const Node& node, const Sp<TcpSocket>& sock = {});
|
||||
int sendUDP(msgpack::sbuffer& msg, const SockAddr& addr);
|
||||
|
||||
void sendValueParts(const TransId& tid, const std::vector<Blob>& svals, const SockAddr& addr);
|
||||
std::vector<Blob> packValueHeader(msgpack::sbuffer&, const std::vector<Sp<Value>>&);
|
||||
void maintainRxBuffer(Tid tid);
|
||||
int send(const Blob& msg, int flags, const Node& node, UdpSocket::OnSent&& cb);
|
||||
|
||||
void startTcp(const Sp<TcpSocket>& sock, const Sp<Node>& node = {});
|
||||
|
||||
void sendValueParts(TransId tid, const std::vector<Blob>& svals, const SockAddr& addr);
|
||||
std::vector<Blob> packValueHeader(msgpack::sbuffer&, const std::vector<Sp<Value>>&, bool stream);
|
||||
void maintainRxBuffer(const TransId& tid);
|
||||
|
||||
/*************
|
||||
* Answers *
|
||||
*************/
|
||||
/* answer to a ping request */
|
||||
void sendPong(const SockAddr& addr, Tid tid);
|
||||
void sendPong(const Sp<Node>& node, TransId tid);
|
||||
/* answer to findnodes/getvalues request */
|
||||
void sendNodesValues(const SockAddr& addr,
|
||||
Tid tid,
|
||||
void sendNodesValues(const Sp<Node>& node,
|
||||
TransId tid,
|
||||
const Blob& nodes,
|
||||
const Blob& nodes6,
|
||||
const std::vector<Sp<Value>>& st,
|
||||
@@ -495,27 +511,32 @@ private:
|
||||
std::vector<Sp<Node>>& nodes,
|
||||
std::vector<Sp<Node>>& nodes6);
|
||||
/* answer to a listen request */
|
||||
void sendListenConfirmation(const SockAddr& addr, Tid tid);
|
||||
void sendListenConfirmation(const Sp<Node>& node, TransId tid);
|
||||
/* answer to put request */
|
||||
void sendValueAnnounced(const SockAddr& addr, Tid, Value::Id);
|
||||
void sendValueAnnounced(const Sp<Node>& node, TransId, Value::Id);
|
||||
/* answer in case of error */
|
||||
void sendError(const SockAddr& addr,
|
||||
Tid tid,
|
||||
void sendError(const Sp<Node>& node,
|
||||
TransId tid,
|
||||
uint16_t code,
|
||||
const std::string& message,
|
||||
bool include_id=false);
|
||||
bool include_id=false,
|
||||
const Sp<TcpSocket>& sock= {});
|
||||
|
||||
void deserializeNodes(ParsedMessage& msg, const SockAddr& from);
|
||||
|
||||
/* DHT info */
|
||||
const InfoHash& myid;
|
||||
//const InfoHash& myid;
|
||||
const NetId network {0};
|
||||
const int& dht_socket;
|
||||
const int& dht_socket6;
|
||||
const Logger& DHT_LOG;
|
||||
|
||||
Sp<UdpSocket> sock;
|
||||
Sp<TcpSocket> tcp_sock;
|
||||
std::set<Sp<TcpSocket>> pending_connect;
|
||||
NodeCache cache {};
|
||||
|
||||
crypto::EcSecretKey id_key;
|
||||
//crypto::EcSecretKey id_key;
|
||||
|
||||
// global limiting should be triggered by at least 8 different IPs
|
||||
using IpLimiter = RateLimiter<MAX_REQUESTS_PER_SEC/8>;
|
||||
using IpLimiterMap = std::map<SockAddr, IpLimiter, SockAddr::ipCmp>;
|
||||
@@ -524,8 +545,8 @@ private:
|
||||
size_t limiter_maintenance {0};
|
||||
|
||||
// requests handling
|
||||
std::map<Tid, Sp<Request>> requests {};
|
||||
std::map<Tid, PartialMessage> partial_messages;
|
||||
std::map<TransId, Sp<Request>> requests {};
|
||||
std::map<TransId, PartialMessage> partial_messages;
|
||||
|
||||
MessageStats in_stats {}, out_stats {};
|
||||
std::set<SockAddr> blacklist {};
|
||||
|
||||
+37
-12
@@ -22,6 +22,9 @@
|
||||
#include "infohash.h" // includes socket structures
|
||||
#include "utils.h"
|
||||
#include "sockaddr.h"
|
||||
#include "net.h"
|
||||
#include "uv_utils.h"
|
||||
#include "crypto.h"
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
@@ -35,8 +38,8 @@ struct Socket;
|
||||
struct RequestAnswer;
|
||||
} /* namespace net */
|
||||
|
||||
using Tid = uint32_t;
|
||||
using SocketCb = std::function<void(const Sp<Node>&, net::RequestAnswer&&)>;
|
||||
using SocketId = uint32_t;
|
||||
struct Socket {
|
||||
Socket() {}
|
||||
Socket(SocketCb&& on_receive) :
|
||||
@@ -47,9 +50,18 @@ struct Socket {
|
||||
struct Node {
|
||||
const InfoHash id;
|
||||
|
||||
Node(const InfoHash& id, const SockAddr& addr, bool client=false);
|
||||
Sp<TcpSocket> sock;
|
||||
|
||||
InfoHash last_known_pk;
|
||||
//std::list<crypto::EcSecretKey>
|
||||
|
||||
Node(const InfoHash& id, const SockAddr& addr, const Sp<TcpSocket>& s = {}, bool client=false);
|
||||
Node(const InfoHash& id, const sockaddr* sa, socklen_t salen)
|
||||
: Node(id, SockAddr(sa, salen)) {}
|
||||
~Node() {
|
||||
if (sock)
|
||||
sock->close();
|
||||
}
|
||||
|
||||
InfoHash getId() const {
|
||||
return id;
|
||||
@@ -58,6 +70,7 @@ struct Node {
|
||||
std::string getAddrStr() const {
|
||||
return addr.toString();
|
||||
}
|
||||
|
||||
bool isClient() const { return is_client; }
|
||||
bool isIncoming() { return time > reply_time; }
|
||||
|
||||
@@ -65,6 +78,14 @@ struct Node {
|
||||
const time_point& getReplyTime() const { return reply_time; }
|
||||
void setTime(const time_point& t) { time = t; }
|
||||
|
||||
SockAddr getAddress() const {
|
||||
return canStream() ? sock->getPeerAddr() : addr;
|
||||
}
|
||||
|
||||
void startTCP() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes notice about an additionnal authentication error with this node. Up
|
||||
* to MAX_AUTH_ERRORS errors are accepted in order to let the node recover.
|
||||
@@ -97,11 +118,15 @@ struct Node {
|
||||
}
|
||||
sa_family_t getFamily() const { return addr.getFamily(); }
|
||||
|
||||
void update(const SockAddr&);
|
||||
void update(const SockAddr&, const Sp<TcpSocket>&);
|
||||
|
||||
bool canStream() const {
|
||||
return sock and not sock->isClosed() and sock->canWrite();
|
||||
}
|
||||
|
||||
void requested(const Sp<net::Request>& req);
|
||||
void received(time_point now, const Sp<net::Request>& req);
|
||||
Sp<net::Request> getRequest(Tid tid);
|
||||
Sp<net::Request> getRequest(const net::TransId& tid);
|
||||
void cancelRequest(const Sp<net::Request>& req);
|
||||
|
||||
void setExpired();
|
||||
@@ -115,16 +140,16 @@ struct Node {
|
||||
*
|
||||
* @return the socket.
|
||||
*/
|
||||
Tid openSocket(SocketCb&& cb);
|
||||
SocketId openSocket(SocketCb&& cb);
|
||||
|
||||
Sp<Socket> getSocket(Tid id);
|
||||
Socket* getSocket(SocketId id);
|
||||
|
||||
/**
|
||||
* Closes a socket so that no further data will be red on that socket.
|
||||
*
|
||||
* @param socket The socket to close.
|
||||
*/
|
||||
void closeSocket(Tid id);
|
||||
void closeSocket(SocketId id);
|
||||
|
||||
/**
|
||||
* Resets the state of the node so it's not expired anymore.
|
||||
@@ -136,9 +161,9 @@ struct Node {
|
||||
*
|
||||
* @return the new id.
|
||||
*/
|
||||
Tid getNewTid() {
|
||||
uint16_t getNewTid() {
|
||||
++transaction_id;
|
||||
return transaction_id ? ++transaction_id : transaction_id;
|
||||
return transaction_id == net::TransId::INVALID ? ++transaction_id : transaction_id;
|
||||
}
|
||||
|
||||
std::string toString() const;
|
||||
@@ -163,11 +188,11 @@ private:
|
||||
time_point reply_time {time_point::min()}; /* time of last correct reply received */
|
||||
unsigned auth_errors {0};
|
||||
bool expired_ {false};
|
||||
Tid transaction_id;
|
||||
SocketId transaction_id;
|
||||
using TransactionDist = std::uniform_int_distribution<decltype(transaction_id)>;
|
||||
|
||||
std::map<Tid, Sp<net::Request>> requests_ {};
|
||||
std::map<Tid, Sp<Socket>> sockets_;
|
||||
std::map<net::TransId, Sp<net::Request>> requests_ {};
|
||||
std::map<SocketId, Socket> sockets_;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -27,7 +27,7 @@ namespace dht {
|
||||
|
||||
struct NodeCache {
|
||||
Sp<Node> getNode(const InfoHash& id, sa_family_t family);
|
||||
Sp<Node> getNode(const InfoHash& id, const SockAddr&, time_point now, bool confirmed, bool client=false);
|
||||
Sp<Node> getNode(const InfoHash& id, const SockAddr&, const Sp<TcpSocket>&, time_point now, bool confirmed, bool client=false);
|
||||
std::vector<Sp<Node>> getCachedNodes(const InfoHash& id, sa_family_t sa_f, size_t count) const;
|
||||
|
||||
/**
|
||||
@@ -36,20 +36,15 @@ struct NodeCache {
|
||||
* To use in case of connectivity change etc.
|
||||
*/
|
||||
void clearBadNodes(sa_family_t family = 0);
|
||||
|
||||
~NodeCache();
|
||||
void closeAll(sa_family_t family = 0);
|
||||
|
||||
private:
|
||||
class NodeMap : private std::map<InfoHash, std::weak_ptr<Node>> {
|
||||
class NodeMap : public std::map<InfoHash, std::weak_ptr<Node>> {
|
||||
public:
|
||||
Sp<Node> getNode(const InfoHash& id);
|
||||
Sp<Node> getNode(const InfoHash& id, const SockAddr&, time_point now, bool confirmed, bool client);
|
||||
std::vector<Sp<Node>> getCachedNodes(const InfoHash& id, size_t count) const;
|
||||
Sp<Node> getNode(const InfoHash& id, const SockAddr&, const Sp<TcpSocket>&, time_point now, bool confirmed, bool client);
|
||||
void clearBadNodes();
|
||||
void setExpired();
|
||||
void cleanup();
|
||||
private:
|
||||
size_t cleanup_counter {0};
|
||||
void closeAll();
|
||||
};
|
||||
|
||||
const NodeMap& cache(sa_family_t af) const { return af == AF_INET ? cache_4 : cache_6; }
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2018 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 <chrono>
|
||||
|
||||
namespace dht {
|
||||
namespace proxy {
|
||||
|
||||
constexpr const std::chrono::seconds OP_TIMEOUT {1 * 60 * 60}; // one hour
|
||||
constexpr const std::chrono::seconds OP_MARGIN {5 * 60}; // 5 minutes
|
||||
constexpr const char* const HTTP_PROTO {"http://"};
|
||||
using ListenToken = uint64_t;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -23,12 +23,12 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
template<size_t Quota, unsigned long Period=1>
|
||||
template<size_t Quota, unsigned long Perdiod=1>
|
||||
class RateLimiter {
|
||||
public:
|
||||
/** Clear outdated records and return current quota usage */
|
||||
size_t maintain(const time_point& now) {
|
||||
auto limit = now - std::chrono::seconds(Period);
|
||||
auto limit = now - std::chrono::seconds(Perdiod);
|
||||
while (not records.empty() and records.front() < limit)
|
||||
records.pop();
|
||||
return records.size();
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
namespace dht {
|
||||
namespace crypto {
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#ifndef WIN32_NATIVE
|
||||
#ifdef _WIN32
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,6 +42,14 @@ struct Bucket {
|
||||
Sp<Node> randomNode();
|
||||
|
||||
void sendCachedPing(net::NetworkEngine& ne);
|
||||
|
||||
/** Return false if there is a single non-expired node in this bucket. */
|
||||
bool expired() const {
|
||||
for (const auto& n : nodes)
|
||||
if (not n->isExpired())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class RoutingTable : public std::list<Bucket> {
|
||||
|
||||
+90
-45
@@ -22,6 +22,9 @@
|
||||
|
||||
#include "utils.h"
|
||||
#include "log_enable.h"
|
||||
#include "uv_utils.h"
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
@@ -36,11 +39,7 @@ namespace dht {
|
||||
*/
|
||||
class Scheduler {
|
||||
public:
|
||||
struct Job {
|
||||
Job(std::function<void()>&& f) : do_(std::move(f)) {}
|
||||
std::function<void()> do_;
|
||||
void cancel() { do_ = {}; }
|
||||
};
|
||||
Scheduler(uv_loop_t* loop, const Logger& l) : loop_(loop), DHT_LOG(l) {}
|
||||
|
||||
/**
|
||||
* Adds another job to the queue.
|
||||
@@ -50,75 +49,121 @@ public:
|
||||
*
|
||||
* @return pointer to the newly scheduled job.
|
||||
*/
|
||||
Sp<Scheduler::Job> add(time_point t, std::function<void()>&& job_func) {
|
||||
auto job = std::make_shared<Job>(std::move(job_func));
|
||||
if (t != time_point::max())
|
||||
timers.emplace(std::move(t), job);
|
||||
Sp<Job> add(const time_point& t, std::function<void()>&& job_func) {
|
||||
if (not loop_)
|
||||
return {};
|
||||
auto job = Job::make(loop_, std::move(job_func));
|
||||
if (t != time_point::max()) {
|
||||
garbageCollection();
|
||||
pending_jobs.emplace(job);
|
||||
job->run(t);
|
||||
} else
|
||||
job->cancel();
|
||||
return job;
|
||||
}
|
||||
|
||||
void add(const Sp<Scheduler::Job>& job, time_point t) {
|
||||
if (t != time_point::max())
|
||||
timers.emplace(std::move(t), job);
|
||||
Sp<Job> add(const duration& t, std::function<void()>&& job_func) {
|
||||
if (not loop_)
|
||||
return {};
|
||||
auto job = Job::make(loop_, std::move(job_func));
|
||||
garbageCollection();
|
||||
pending_jobs.emplace(job);
|
||||
job->run(t);
|
||||
return job;
|
||||
}
|
||||
Sp<Job> run(std::function<void()>&& job_func) {
|
||||
auto job = Job::make(loop_, std::move(job_func));
|
||||
garbageCollection();
|
||||
pending_jobs.emplace(job);
|
||||
job->run();
|
||||
return job;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reschedules a job.
|
||||
*
|
||||
* @param time The time at which the job shall be rescheduled.
|
||||
* @param job The job to edit.
|
||||
* @param t The time at which the job shall be rescheduled.
|
||||
*
|
||||
* @return pointer to the newly scheduled job.
|
||||
*/
|
||||
void edit(Sp<Scheduler::Job>& job, time_point t) {
|
||||
void edit(Sp<Job>& job, const duration& t = {}) {
|
||||
if (not loop_)
|
||||
return;
|
||||
if (not job) {
|
||||
DHT_LOG.ERR("editing an empty job");
|
||||
return;
|
||||
}
|
||||
// std::function move doesn't garantee to leave the object empty.
|
||||
// Force clearing old value.
|
||||
auto task = std::move(job->do_);
|
||||
job->do_ = {};
|
||||
job->cancel();
|
||||
job = add(t, std::move(task));
|
||||
}
|
||||
void edit(Sp<Job>& job, const time_point& t) {
|
||||
if (not loop_)
|
||||
return;
|
||||
if (not job) {
|
||||
DHT_LOG.ERR("editing an empty job");
|
||||
return;
|
||||
}
|
||||
auto task = std::move(job->do_);
|
||||
job->do_ = {};
|
||||
job->cancel();
|
||||
job = add(t, std::move(task));
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the jobs to do up to now.
|
||||
*
|
||||
* @return The time for the next job to run.
|
||||
*/
|
||||
time_point run() {
|
||||
syncTime();
|
||||
while (not timers.empty()) {
|
||||
auto timer = timers.begin();
|
||||
/*
|
||||
* Running jobs scheduled before "now" prevents run+rescheduling
|
||||
* loops before this method ends. It is garanteed by the fact that a
|
||||
* job will at least be scheduled for "now" and not before.
|
||||
*/
|
||||
if (timer->first > now)
|
||||
break;
|
||||
|
||||
auto job = std::move(timer->second);
|
||||
timers.erase(timer);
|
||||
|
||||
if (job->do_)
|
||||
job->do_();
|
||||
}
|
||||
return getNextJobTime();
|
||||
void stop() {
|
||||
loop_ = nullptr;
|
||||
size_t del {0};
|
||||
for (auto& w : pending_jobs)
|
||||
if (auto job = w.lock()) {
|
||||
job->cancel();
|
||||
del++;
|
||||
}
|
||||
pending_jobs.clear();
|
||||
DHT_LOG.DEBUG("stopping scheduler, %lu events canceled", del);
|
||||
}
|
||||
|
||||
inline time_point getNextJobTime() const {
|
||||
return timers.empty() ? time_point::max() : timers.begin()->first;
|
||||
~Scheduler(){
|
||||
stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessors for the common time reference used for synchronizing
|
||||
* operations.
|
||||
*/
|
||||
inline const time_point& time() const { return now; }
|
||||
inline time_point syncTime() { return (now = clock::now()); }
|
||||
inline time_point time() const {
|
||||
return time_point(std::chrono::duration_cast<duration>(std::chrono::milliseconds(uv_now(loop_))));
|
||||
}
|
||||
inline time_point syncTime() {
|
||||
return time();
|
||||
}
|
||||
uv_loop_t* getLoop() const {
|
||||
return loop_;
|
||||
}
|
||||
|
||||
private:
|
||||
time_point now {clock::now()};
|
||||
std::multimap<time_point, Sp<Job>> timers {}; /* the jobs ordered by time */
|
||||
std::set<std::weak_ptr<Job>, std::owner_less<std::weak_ptr<Job>>> pending_jobs {};
|
||||
size_t lastSize_ {0};
|
||||
uv_loop_t* loop_;
|
||||
const Logger& DHT_LOG;
|
||||
|
||||
void garbageCollection() {
|
||||
if (pending_jobs.size() > lastSize_ * 2) {
|
||||
unsigned del {0};
|
||||
for (auto it = pending_jobs.begin(); it != pending_jobs.end();) {
|
||||
if (it->expired()) {
|
||||
it = pending_jobs.erase(it);
|
||||
del++;
|
||||
} else
|
||||
++it;
|
||||
}
|
||||
//DHT_LOG.DEBUG("pending_jobs cleanup: %u removed, %zu remaining", del, pending_jobs.size());
|
||||
lastSize_ = pending_jobs.size();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
+16
-209
@@ -1,8 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@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
|
||||
@@ -30,21 +29,13 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
class OPENDHT_PUBLIC SecureDht final : public DhtInterface {
|
||||
class OPENDHT_PUBLIC SecureDht : public Dht {
|
||||
public:
|
||||
|
||||
typedef std::function<void(bool)> SignatureCheckCallback;
|
||||
|
||||
using Config = SecureDhtConfig;
|
||||
|
||||
static dht::Config& getConfig(SecureDht::Config& conf)
|
||||
{
|
||||
auto& c = conf.node_config;
|
||||
if (not c.node_id and conf.id.second)
|
||||
c.node_id = InfoHash::get("node:"+conf.id.second->getId().toString());
|
||||
return c;
|
||||
}
|
||||
|
||||
SecureDht() {}
|
||||
|
||||
/**
|
||||
@@ -53,7 +44,7 @@ public:
|
||||
* id: the identity to use for the crypto layer and to compute
|
||||
* our own hash on the Dht.
|
||||
*/
|
||||
SecureDht(std::unique_ptr<DhtInterface> dht, Config config);
|
||||
SecureDht(uv_loop_t* loop, Config config);
|
||||
|
||||
virtual ~SecureDht();
|
||||
|
||||
@@ -71,17 +62,14 @@ public:
|
||||
return secureType(std::move(tmp_type));
|
||||
}
|
||||
|
||||
void registerType(const ValueType& type) override {
|
||||
if (dht_)
|
||||
dht_->registerType(secureType(type));
|
||||
virtual void registerType(const ValueType& type) override {
|
||||
Dht::registerType(secureType(type));
|
||||
}
|
||||
void registerType(ValueType&& type) {
|
||||
if (dht_)
|
||||
dht_->registerType(secureType(std::forward<ValueType>(type)));
|
||||
virtual void registerType(ValueType&& type) {
|
||||
Dht::registerType(secureType(std::forward<ValueType>(type)));
|
||||
}
|
||||
void registerInsecureType(const ValueType& type) {
|
||||
if (dht_)
|
||||
dht_->registerType(type);
|
||||
virtual void registerInsecureType(const ValueType& type) {
|
||||
Dht::registerType(type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,17 +77,19 @@ public:
|
||||
* If the signature can't be checked, or if the data can't be decrypted, it is not returned.
|
||||
* Public, non-signed & non-encrypted data is retransmitted as-is.
|
||||
*/
|
||||
void get(const InfoHash& id, GetCallback cb, DoneCallback donecb={}, Value::Filter&& = {}, Where&& w = {}) override;
|
||||
void get(const InfoHash& id, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f = {}, Where&& w = {}) override {
|
||||
virtual void get(const InfoHash& id, GetCallback cb, DoneCallback donecb={}, Value::Filter&& = {}, Where&& w = {}) override;
|
||||
virtual void get(const InfoHash& id, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f = {}, Where&& w = {}) override {
|
||||
get(id, cb, bindDoneCb(donecb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) override {
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) override {
|
||||
get(key, bindGetCb(cb), donecb, std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}, Where&& w = {}) override {
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}, Where&& w = {}) override {
|
||||
get(key, bindGetCb(cb), bindDoneCb(donecb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
|
||||
virtual size_t listen(const InfoHash& id, GetCallback cb, Value::Filter&& = {}, Where&& w = {}) override;
|
||||
|
||||
/**
|
||||
* Will take ownership of the value, sign it using our private key and put it in the DHT.
|
||||
*/
|
||||
@@ -145,194 +135,11 @@ public:
|
||||
localQueryMethod_ = std::move(query_method);
|
||||
}
|
||||
|
||||
/**
|
||||
* SecureDht to Dht proxy
|
||||
*/
|
||||
void shutdown(ShutdownCallback cb) override {
|
||||
dht_->shutdown(cb);
|
||||
}
|
||||
void dumpTables() const override {
|
||||
dht_->dumpTables();
|
||||
}
|
||||
inline const InfoHash& getNodeId() const override { return dht_->getNodeId(); }
|
||||
|
||||
std::pair<size_t, size_t> getStoreSize() const override {
|
||||
return dht_->getStoreSize();
|
||||
}
|
||||
std::string getStorageLog() const override {
|
||||
return dht_->getStorageLog();
|
||||
}
|
||||
std::string getStorageLog(const InfoHash& h) const override {
|
||||
return dht_->getStorageLog(h);
|
||||
}
|
||||
void setStorageLimit(size_t limit = DEFAULT_STORAGE_LIMIT) override {
|
||||
dht_->setStorageLimit(limit);
|
||||
}
|
||||
std::vector<NodeExport> exportNodes() override {
|
||||
return dht_->exportNodes();
|
||||
}
|
||||
std::vector<ValuesExport> exportValues() const override {
|
||||
return dht_->exportValues();
|
||||
}
|
||||
void importValues(const std::vector<ValuesExport>& v) override {
|
||||
dht_->importValues(v);
|
||||
}
|
||||
NodeStats getNodesStats(sa_family_t af) const override {
|
||||
return dht_->getNodesStats(af);
|
||||
}
|
||||
std::vector<unsigned> getNodeMessageStats(bool in = false) override {
|
||||
return dht_->getNodeMessageStats(in);
|
||||
}
|
||||
std::string getRoutingTablesLog(sa_family_t af) const override {
|
||||
return dht_->getRoutingTablesLog(af);
|
||||
}
|
||||
std::string getSearchesLog(sa_family_t af) const override {
|
||||
return dht_->getSearchesLog(af);
|
||||
}
|
||||
std::string getSearchLog(const InfoHash& h, sa_family_t af = AF_UNSPEC) const override {
|
||||
return dht_->getSearchLog(h, af);
|
||||
}
|
||||
std::vector<SockAddr> getPublicAddress(sa_family_t family = 0) override {
|
||||
return dht_->getPublicAddress(family);
|
||||
}
|
||||
time_point periodic(const uint8_t *buf, size_t buflen, const SockAddr& sa) override {
|
||||
return dht_->periodic(buf, buflen, sa);
|
||||
}
|
||||
time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen) override {
|
||||
return dht_->periodic(buf, buflen, from, fromlen);
|
||||
}
|
||||
NodeStatus getStatus(sa_family_t af) const override {
|
||||
return dht_->getStatus(af);
|
||||
}
|
||||
NodeStatus getStatus() const override {
|
||||
return dht_->getStatus();
|
||||
}
|
||||
bool isRunning(sa_family_t af = 0) const override {
|
||||
return dht_->isRunning(af);
|
||||
}
|
||||
const ValueType& getType(ValueType::Id type_id) const override {
|
||||
return dht_->getType(type_id);
|
||||
}
|
||||
void insertNode(const InfoHash& id, const SockAddr& sa) override {
|
||||
dht_->insertNode(id, sa);
|
||||
}
|
||||
void insertNode(const InfoHash& id, const sockaddr* sa, socklen_t salen) override {
|
||||
dht_->insertNode(id, sa, salen);
|
||||
}
|
||||
void insertNode(const NodeExport& n) override {
|
||||
dht_->insertNode(n);
|
||||
}
|
||||
void pingNode(const sockaddr* sa, socklen_t salen, DoneCallbackSimple&& cb={}) override {
|
||||
dht_->pingNode(sa, salen, std::move(cb));
|
||||
}
|
||||
void query(const InfoHash& key, QueryCallback cb, DoneCallback done_cb = {}, Query&& q = {}) override {
|
||||
dht_->query(key, cb, done_cb, std::move(q));
|
||||
}
|
||||
void query(const InfoHash& key, QueryCallback cb, DoneCallbackSimple done_cb = {}, Query&& q = {}) override {
|
||||
dht_->query(key, cb, done_cb, std::move(q));
|
||||
}
|
||||
std::vector<Sp<Value>> getLocal(const InfoHash& key, Value::Filter f = Value::AllFilter()) const override {
|
||||
return dht_->getLocal(key, f);
|
||||
}
|
||||
Sp<Value> getLocalById(const InfoHash& key, Value::Id vid) const override {
|
||||
return dht_->getLocalById(key, vid);
|
||||
}
|
||||
void put(const InfoHash& key,
|
||||
Sp<Value> v,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) override
|
||||
{
|
||||
dht_->put(key, v, cb, created, permanent);
|
||||
}
|
||||
void put(const InfoHash& key,
|
||||
const Sp<Value>& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) override
|
||||
{
|
||||
dht_->put(key, v, cb, created, permanent);
|
||||
}
|
||||
|
||||
void put(const InfoHash& key,
|
||||
Value&& v,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) override
|
||||
{
|
||||
dht_->put(key, std::move(v), cb, created, permanent);
|
||||
}
|
||||
void put(const InfoHash& key,
|
||||
Value&& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) override
|
||||
{
|
||||
dht_->put(key, std::move(v), cb, created, permanent);
|
||||
}
|
||||
std::vector<Sp<Value>> getPut(const InfoHash& h) override {
|
||||
return dht_->getPut(h);
|
||||
}
|
||||
Sp<Value> getPut(const InfoHash& h, const Value::Id& vid) override {
|
||||
return dht_->getPut(h, vid);
|
||||
}
|
||||
bool cancelPut(const InfoHash& h, const Value::Id& vid) override {
|
||||
return dht_->cancelPut(h, vid);
|
||||
}
|
||||
|
||||
size_t listen(const InfoHash& key, ValueCallback, Value::Filter={}, Where={}) override;
|
||||
size_t listen(const InfoHash& key, GetCallback cb, Value::Filter = {}, Where w = {}) override;
|
||||
size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w = {}) override {
|
||||
return listen(key, bindGetCb(cb), f, w);
|
||||
}
|
||||
bool cancelListen(const InfoHash& h, size_t token) override {
|
||||
return dht_->cancelListen(h, token);
|
||||
}
|
||||
void connectivityChanged(sa_family_t af) override {
|
||||
dht_->connectivityChanged(af);
|
||||
}
|
||||
void connectivityChanged() override {
|
||||
dht_->connectivityChanged();
|
||||
}
|
||||
|
||||
void forwardAllMessages(bool forward) {
|
||||
forward_all_ = forward;
|
||||
}
|
||||
|
||||
void setPushNotificationToken(const std::string& token = "") override {
|
||||
dht_->setPushNotificationToken(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call linked callback with push_notification
|
||||
* @param notification to process
|
||||
*/
|
||||
void pushNotificationReceived(const std::map<std::string, std::string>& notification) override {
|
||||
dht_->pushNotificationReceived(notification);
|
||||
}
|
||||
|
||||
void setLoggers(LogMethod error = NOLOG, LogMethod warn = NOLOG, LogMethod debug = NOLOG) override
|
||||
{
|
||||
DhtInterface::setLoggers(error, warn, debug);
|
||||
dht_->setLoggers(error, warn, debug);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only print logs related to the given InfoHash (if given), or disable filter (if zeroes).
|
||||
*/
|
||||
void setLogFilter(const InfoHash& f) override {
|
||||
DHT_LOG.setFilter(f);
|
||||
dht_->setLogFilter(f);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<DhtInterface> dht_;
|
||||
// prevent copy
|
||||
SecureDht(const SecureDht&) = delete;
|
||||
SecureDht& operator=(const SecureDht&) = delete;
|
||||
|
||||
Sp<Value> checkValue(const Sp<Value>& v);
|
||||
ValueCallback getCallbackFilter(ValueCallback, Value::Filter&&);
|
||||
GetCallback getCallbackFilter(GetCallback, Value::Filter&&);
|
||||
|
||||
Sp<crypto::PrivateKey> key_ {};
|
||||
@@ -345,7 +152,7 @@ private:
|
||||
std::map<InfoHash, Sp<crypto::Certificate>> nodesCertificates_ {};
|
||||
std::map<InfoHash, Sp<const crypto::PublicKey>> nodesPubKeys_ {};
|
||||
|
||||
std::atomic_bool forward_all_ {false};
|
||||
std::uniform_int_distribution<Value::Id> rand_id {};
|
||||
};
|
||||
|
||||
const ValueType CERTIFICATE_TYPE = {
|
||||
|
||||
@@ -28,8 +28,6 @@ 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;
|
||||
@@ -38,8 +36,6 @@ typedef uint16_t in_port_t;
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
@@ -67,7 +63,7 @@ public:
|
||||
*/
|
||||
SockAddr(const sockaddr* sa, socklen_t length) {
|
||||
if (length > sizeof(sockaddr_storage))
|
||||
throw std::invalid_argument("Socket address length is too large");
|
||||
throw std::runtime_error("Socket address length is too large");
|
||||
set(sa, length);
|
||||
}
|
||||
SockAddr(const sockaddr* sa) {
|
||||
@@ -78,7 +74,7 @@ public:
|
||||
else if(sa->sa_family == AF_INET6)
|
||||
len = sizeof(sockaddr_in6);
|
||||
else
|
||||
throw std::invalid_argument("Unknown address family");
|
||||
throw std::runtime_error("Unknown address family");
|
||||
}
|
||||
set(sa, len);
|
||||
}
|
||||
@@ -88,8 +84,6 @@ public:
|
||||
*/
|
||||
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;
|
||||
@@ -139,7 +133,7 @@ public:
|
||||
}
|
||||
if (new_length != len) {
|
||||
len = new_length;
|
||||
if (len) addr.reset((sockaddr*)::calloc(len, 1));
|
||||
if (len) addr.reset((sockaddr*)std::calloc(len, 1));
|
||||
else addr.reset();
|
||||
}
|
||||
if (len > sizeof(sa_family_t))
|
||||
@@ -258,13 +252,13 @@ public:
|
||||
};
|
||||
private:
|
||||
socklen_t len {0};
|
||||
struct free_delete { void operator()(void* p) { ::free(p); } };
|
||||
struct free_delete { void operator()(void* p) { std::free(p); } };
|
||||
std::unique_ptr<sockaddr, free_delete> addr {};
|
||||
|
||||
void set(const sockaddr* sa, socklen_t length) {
|
||||
if (len != length) {
|
||||
len = length;
|
||||
if (len) addr.reset((sockaddr*)::malloc(len));
|
||||
if (len) addr.reset((sockaddr*)std::malloc(len));
|
||||
else addr.reset();
|
||||
}
|
||||
if (len)
|
||||
|
||||
+3
-15
@@ -54,22 +54,10 @@ void erase_if(std::map<Key, Item>& map, const Condition& condition)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Split "[host]:port" or "host:port" to pair<"host", "port">.
|
||||
*/
|
||||
OPENDHT_PUBLIC std::pair<std::string, std::string>
|
||||
splitPort(const std::string& s);
|
||||
|
||||
class OPENDHT_PUBLIC DhtException : public std::runtime_error {
|
||||
public:
|
||||
DhtException(const std::string &str = "") :
|
||||
std::runtime_error("DhtException occurred: " + str) {}
|
||||
};
|
||||
|
||||
class OPENDHT_PUBLIC SocketException : public DhtException {
|
||||
public:
|
||||
SocketException(int err) :
|
||||
DhtException(strerror(err)) {}
|
||||
public:
|
||||
DhtException(const std::string &str = "") :
|
||||
std::runtime_error("DhtException occurred: " + str) {}
|
||||
};
|
||||
|
||||
// Time related definitions and utility functions
|
||||
|
||||
@@ -0,0 +1,419 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
namespace dht {
|
||||
|
||||
struct EventLoop {
|
||||
using OnSignal = std::function<void(int)>;
|
||||
|
||||
EventLoop() {
|
||||
uv_loop_init(&loop);
|
||||
uv_signal_init(&loop, &sig_handler);
|
||||
sig_handler.data = this;
|
||||
uv_signal_start(&sig_handler, &EventLoop::signal_handler, SIGINT);
|
||||
}
|
||||
~EventLoop() {
|
||||
uv_loop_close(&loop);
|
||||
}
|
||||
auto run() {
|
||||
return uv_run(&loop, UV_RUN_DEFAULT);
|
||||
}
|
||||
auto runOnce() {
|
||||
return uv_run(&loop, UV_RUN_NOWAIT);
|
||||
}
|
||||
void stop() {
|
||||
uv_signal_stop(&sig_handler);
|
||||
uv_walk(&loop, &EventLoop::close_walk_cb, this);
|
||||
}
|
||||
uv_loop_t* get() { return &loop; }
|
||||
void setOnSignal(OnSignal cb) {
|
||||
onSignal_ = cb;
|
||||
}
|
||||
private:
|
||||
uv_loop_t loop;
|
||||
uv_signal_t sig_handler;
|
||||
OnSignal onSignal_;
|
||||
void onSignal(int sig) {
|
||||
if (onSignal_)
|
||||
onSignal_(sig);
|
||||
}
|
||||
static void close_walk_cb(uv_handle_t* handle, void* /*arg*/) {
|
||||
if (not uv_is_closing(handle))
|
||||
uv_close(handle, [](uv_handle_t* handle) {
|
||||
std::cout << "handle " << handle << " of type " << handle->type << " closed" << std::endl;
|
||||
});
|
||||
}
|
||||
static void signal_handler(uv_signal_t* handle, int signum) {
|
||||
if (handle->data) {
|
||||
static_cast<EventLoop*>(handle->data)->onSignal(signum);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using GetAddrInfoCb = std::function<void(std::vector<SockAddr>&&)>;
|
||||
|
||||
void getAddrInfo(uv_loop_t* loop, const char* node, const char* service, GetAddrInfoCb&& cb);
|
||||
|
||||
struct Job : std::enable_shared_from_this<Job> {
|
||||
Job(uv_loop_t* loop, std::function<void()>&& f = {}) : do_(std::move(f)) {
|
||||
uv_timer_init(loop, &timer_);
|
||||
timer_.data = nullptr;
|
||||
}
|
||||
void run(uint64_t t = 0) {
|
||||
uv_timer_start(&timer_, &Job::timer_cb, t, 0);
|
||||
}
|
||||
inline void run(const duration& t) {
|
||||
run(getTimeout(t));
|
||||
}
|
||||
inline void run(const time_point& t) {
|
||||
run(getTimeout(t));
|
||||
}
|
||||
void cancel() {
|
||||
if (timer_.data and not uv_is_closing((uv_handle_t*)&timer_)) {
|
||||
uv_timer_stop(&timer_);
|
||||
uv_close((uv_handle_t*)&timer_, &Job::close_cb);
|
||||
}
|
||||
}
|
||||
static Sp<Job> make(uv_loop_t* loop, std::function<void()>&& f = {}) {
|
||||
auto job = std::make_shared<Job>(loop, std::move(f));
|
||||
job->timer_.data = new Sp<Job>(job);
|
||||
return job;
|
||||
}
|
||||
static uint64_t getTimeout(const duration& d) {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(d).count();
|
||||
}
|
||||
uint64_t getTimeout(const time_point& tp) const {
|
||||
uint64_t t = getTimeout(tp.time_since_epoch());
|
||||
uint64_t now = uv_now(timer_.loop);
|
||||
return (t < now) ? 0 : t - now;
|
||||
}
|
||||
std::function<void()> do_;
|
||||
private:
|
||||
uv_timer_t timer_;
|
||||
void onTimer() {
|
||||
if (do_)
|
||||
do_();
|
||||
cancel();
|
||||
}
|
||||
void onClosed() {
|
||||
if (timer_.data) {
|
||||
delete static_cast<Sp<Job>*>(timer_.data);
|
||||
timer_.data = nullptr;
|
||||
}
|
||||
}
|
||||
static void timer_cb(uv_timer_t* handle) {
|
||||
if (handle->data) {
|
||||
auto timer = *static_cast<Sp<Job>*>(handle->data);
|
||||
timer->onTimer();
|
||||
}
|
||||
}
|
||||
static void close_cb(uv_handle_t* handle) {
|
||||
if (handle->data) {
|
||||
auto timer = *static_cast<Sp<Job>*>(handle->data);
|
||||
timer->onClosed();
|
||||
} else {
|
||||
std::cerr << "Job::onClosed(): no data! " << handle << std::endl;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static inline void alloc_buffer(uv_handle_t* /*handle*/, size_t suggested_size, uv_buf_t *buf) {
|
||||
buf->base = (char*)malloc(suggested_size);
|
||||
buf->len = suggested_size;
|
||||
}
|
||||
|
||||
|
||||
using OnClose = std::function<void()>;
|
||||
|
||||
struct UdpSocket : public std::enable_shared_from_this<UdpSocket>
|
||||
{
|
||||
using OnReceive = std::function<void(const uint8_t* data, size_t size, const SockAddr& addr)>;
|
||||
using OnSent = std::function<void(int status)>;
|
||||
|
||||
UdpSocket(uv_loop_t* loop) {
|
||||
uv_udp_init(loop, &sock);
|
||||
sock.data = nullptr;
|
||||
}
|
||||
void open(const char* bind_addr, in_port_t port, OnReceive cb) {
|
||||
if (not sock.data)
|
||||
sock.data = new std::shared_ptr<UdpSocket>(shared_from_this());
|
||||
receive_cb = std::move(cb);
|
||||
struct sockaddr_in6 addr;
|
||||
uv_ip6_addr(bind_addr, port, &addr);
|
||||
uv_udp_bind(&sock, (const struct sockaddr*)&addr, 0);
|
||||
uv_udp_recv_start(&sock, &UdpSocket::on_alloc_buffer, &UdpSocket::on_receive);
|
||||
}
|
||||
|
||||
int send(uint8_t* data, size_t size, const SockAddr& addr, OnSent&& cb = {}) {
|
||||
auto req = new SendReq;
|
||||
req->msg = data;
|
||||
req->req.data = req;
|
||||
req->cb = std::move(cb);
|
||||
const uv_buf_t buf {
|
||||
/*.base =*/ (char*)data,
|
||||
/*.len =*/ size
|
||||
};
|
||||
return uv_udp_send(&req->req, &sock, &buf, 1, addr.get(), &UdpSocket::on_sent);
|
||||
}
|
||||
|
||||
void close(OnClose cb) {
|
||||
close_cb = cb;
|
||||
uv_close((uv_handle_t*) &sock, &UdpSocket::on_close);
|
||||
}
|
||||
bool isRunning() const {
|
||||
return uv_is_active((uv_handle_t*) &sock);
|
||||
}
|
||||
SockAddr getBoundAddr() {
|
||||
sockaddr_storage addr;
|
||||
int size = sizeof(addr);
|
||||
uv_udp_getsockname(&sock, (sockaddr*)&addr, &size);
|
||||
return {(const sockaddr*)&addr, (socklen_t)size};
|
||||
}
|
||||
private:
|
||||
struct SendReq {
|
||||
uv_udp_send_t req;
|
||||
uint8_t* msg;
|
||||
OnSent cb;
|
||||
};
|
||||
|
||||
uv_udp_t sock;
|
||||
OnReceive receive_cb;
|
||||
OnClose close_cb;
|
||||
|
||||
void onAllocBuffer(size_t suggested_size, uv_buf_t* buf) {
|
||||
buf->base = (char*)malloc(suggested_size);
|
||||
buf->len = suggested_size;
|
||||
}
|
||||
void onReceive(ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned /*flags*/) {
|
||||
if (nread > 0 and addr and receive_cb) {
|
||||
receive_cb((const uint8_t*)buf->base, nread, SockAddr(addr));
|
||||
}
|
||||
if (buf->base)
|
||||
free(buf->base);
|
||||
}
|
||||
void onClosed() {
|
||||
if (close_cb)
|
||||
close_cb();
|
||||
if (auto s = static_cast<std::shared_ptr<UdpSocket>*>(sock.data)) {
|
||||
sock.data = nullptr;
|
||||
delete s;
|
||||
}
|
||||
}
|
||||
void onSent(SendReq& req, int status) {
|
||||
if (req.cb) {
|
||||
req.cb(status);
|
||||
}
|
||||
free(req.msg);
|
||||
}
|
||||
static std::shared_ptr<UdpSocket> get(uv_handle_t* handle) {
|
||||
return handle->data ? *static_cast<std::shared_ptr<UdpSocket>*>(handle->data) : std::shared_ptr<UdpSocket>();
|
||||
}
|
||||
static void on_close(uv_handle_t* handle) {
|
||||
if (auto node = get(handle))
|
||||
node->onClosed();
|
||||
}
|
||||
static void on_sent(uv_udp_send_t* req, int status) {
|
||||
if (auto data = static_cast<SendReq*>(req->data)) {
|
||||
if (auto node = get((uv_handle_t*)req->handle))
|
||||
node->onSent(*data, status);
|
||||
delete data;
|
||||
}
|
||||
}
|
||||
static void on_receive(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) {
|
||||
if (auto node = get((uv_handle_t*)handle))
|
||||
node->onReceive(nread, buf, addr, flags);
|
||||
}
|
||||
static inline void on_alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t *buf) {
|
||||
if (auto node = get((uv_handle_t*)handle))
|
||||
node->onAllocBuffer(suggested_size, buf);
|
||||
}
|
||||
};
|
||||
|
||||
struct TcpSocket : public std::enable_shared_from_this<TcpSocket>
|
||||
{
|
||||
using OnConnect = std::function<void(std::shared_ptr<TcpSocket>&& new_socket)>;
|
||||
using OnConnected = std::function<void(int status)>;
|
||||
using OnRead = std::function<void(const uint8_t* data, size_t size)>;
|
||||
using OnWrite = std::function<void(int status)>;
|
||||
|
||||
TcpSocket(uv_loop_t* loop) {
|
||||
uv_tcp_init(loop, &sock);
|
||||
sock.data = nullptr;
|
||||
}
|
||||
void listen(const char* bind_addr, in_port_t port, OnConnect cb) {
|
||||
if (not sock.data)
|
||||
sock.data = new std::shared_ptr<TcpSocket>(shared_from_this());
|
||||
connect_cb = cb;
|
||||
struct sockaddr_in6 addr;
|
||||
uv_ip6_addr(bind_addr, port, &addr);
|
||||
uv_tcp_bind(&sock, (const struct sockaddr*)&addr, 0);
|
||||
if (int r = uv_listen((uv_stream_t*)&sock, 128, &TcpSocket::on_new_connection)) {
|
||||
fprintf(stderr, "Listen error %s\n", uv_strerror(r));
|
||||
}
|
||||
}
|
||||
void connect(const sockaddr* addr, OnConnected&& cb) {
|
||||
if (not sock.data)
|
||||
sock.data = new std::shared_ptr<TcpSocket>(shared_from_this());
|
||||
auto req = new ConnectReq;
|
||||
req->connect.data = req;
|
||||
req->sock = shared_from_this();
|
||||
req->callback = std::move(cb);
|
||||
uv_tcp_connect(&req->connect, &sock, addr, &TcpSocket::on_connect);
|
||||
}
|
||||
void start(OnRead&& cb) {
|
||||
std::cerr << "TCP start" << std::endl;
|
||||
read_cb = std::move(cb);
|
||||
uv_read_start((uv_stream_t*)&sock, &TcpSocket::on_alloc_buffer, &TcpSocket::on_read);
|
||||
}
|
||||
int write(uint8_t* msg, size_t len, OnWrite&& cb = {}) {
|
||||
std::cerr << "TCP write " << len << std::endl;
|
||||
auto req = new WriteReq;
|
||||
req->write.data = req;
|
||||
req->msg = msg;
|
||||
req->sock = shared_from_this();
|
||||
req->callback = std::move(cb);
|
||||
uv_buf_t buf;
|
||||
buf.base = (char*)msg;
|
||||
buf.len = len;
|
||||
return uv_write(&req->write, (uv_stream_t *)&sock, &buf, 1, &TcpSocket::on_write_end);
|
||||
}
|
||||
void close(OnClose onclose = {}) {
|
||||
std::cerr << "TCP close" << std::endl;
|
||||
if (not isClosed()) {
|
||||
close_cb = onclose;
|
||||
uv_close((uv_handle_t*) &sock, &TcpSocket::on_close);
|
||||
}
|
||||
}
|
||||
bool isClosed() {
|
||||
return not sock.data or uv_is_closing((const uv_handle_t*) &sock);
|
||||
}
|
||||
bool canWrite() const {
|
||||
return uv_is_writable((const uv_stream_t*)&sock);
|
||||
}
|
||||
SockAddr getBoundAddr() {
|
||||
sockaddr_storage addr;
|
||||
int size = sizeof(addr);
|
||||
uv_tcp_getsockname(&sock, (sockaddr*)&addr, &size);
|
||||
return {(const sockaddr*)&addr, (socklen_t)size};
|
||||
}
|
||||
SockAddr getPeerAddr() {
|
||||
sockaddr_storage addr;
|
||||
int size = sizeof(addr);
|
||||
uv_tcp_getpeername(&sock, (sockaddr*)&addr, &size);
|
||||
return {(const sockaddr*)&addr, (socklen_t)size};
|
||||
}
|
||||
private:
|
||||
uv_tcp_t sock;
|
||||
OnConnect connect_cb;
|
||||
OnRead read_cb;
|
||||
OnClose close_cb;
|
||||
struct ConnectReq {
|
||||
uv_connect_t connect;
|
||||
std::shared_ptr<TcpSocket> sock;
|
||||
OnConnected callback;
|
||||
};
|
||||
struct WriteReq {
|
||||
uv_write_t write;
|
||||
uint8_t* msg;
|
||||
std::shared_ptr<TcpSocket> sock;
|
||||
OnWrite callback;
|
||||
};
|
||||
std::shared_ptr<TcpSocket> accept() {
|
||||
auto s = std::make_shared<TcpSocket>(sock.loop);
|
||||
s->sock.data = new std::shared_ptr<TcpSocket>(s);
|
||||
if (uv_accept((uv_stream_t*)&sock, (uv_stream_t*) &s->sock) == 0) {
|
||||
return s;
|
||||
} else {
|
||||
s->close();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
void onNewConnection(int status) {
|
||||
if (status < 0) {
|
||||
fprintf(stderr, "New connection error %s\n", uv_strerror(status));
|
||||
return;
|
||||
}
|
||||
if (auto s = accept())
|
||||
connect_cb(std::move(s));
|
||||
fprintf(stderr, "on_new_connection end\n");
|
||||
}
|
||||
void onConnected(int status, OnConnected&& cb) {
|
||||
if (cb)
|
||||
cb(status);
|
||||
}
|
||||
void onAllocBuffer(size_t suggested_size, uv_buf_t* buf) {
|
||||
buf->base = (char*)malloc(suggested_size);
|
||||
buf->len = suggested_size;
|
||||
}
|
||||
void onRead(ssize_t nread, const uv_buf_t *buf) {
|
||||
if (nread < 0) {
|
||||
if (nread != UV_EOF) {
|
||||
fprintf(stderr, "Read error %s\n", uv_err_name(nread));
|
||||
} else {
|
||||
fprintf(stderr, "EOF\n");
|
||||
}
|
||||
close();
|
||||
} else if (nread > 0) {
|
||||
if (read_cb)
|
||||
read_cb((const uint8_t*)buf->base, nread);
|
||||
}
|
||||
if (buf->base)
|
||||
free(buf->base);
|
||||
}
|
||||
void onWrite(int status, OnWrite&& cb) {
|
||||
if (cb)
|
||||
cb(status);
|
||||
}
|
||||
void onClosed() {
|
||||
read_cb = {};
|
||||
connect_cb = {};
|
||||
if (close_cb) {
|
||||
close_cb();
|
||||
close_cb = {};
|
||||
}
|
||||
if (auto s = static_cast<std::shared_ptr<TcpSocket>*>(sock.data)) {
|
||||
sock.data = nullptr;
|
||||
delete s;
|
||||
}
|
||||
}
|
||||
static std::shared_ptr<TcpSocket> get(uv_handle_t* handle) {
|
||||
return handle->data ? *static_cast<std::shared_ptr<TcpSocket>*>(handle->data) : std::shared_ptr<TcpSocket>();
|
||||
}
|
||||
static void on_new_connection(uv_stream_t* handle, int status) {
|
||||
if (auto node = get((uv_handle_t*)handle))
|
||||
node->onNewConnection(status);
|
||||
}
|
||||
static void on_close(uv_handle_t* handle) {
|
||||
if (auto node = get(handle))
|
||||
node->onClosed();
|
||||
}
|
||||
static inline void on_alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t *buf) {
|
||||
if (auto node = get((uv_handle_t*)handle))
|
||||
node->onAllocBuffer(suggested_size, buf);
|
||||
}
|
||||
static void on_read(uv_stream_t* client, ssize_t nread, const uv_buf_t *buf) {
|
||||
if (auto node = get((uv_handle_t*)client))
|
||||
node->onRead(nread, buf);
|
||||
}
|
||||
static void on_write_end(uv_write_t* req, int status) {
|
||||
if (auto write = static_cast<WriteReq*>(req->data)) {
|
||||
write->sock->onWrite(status, std::move(write->callback));
|
||||
if (write->msg)
|
||||
free(write->msg);
|
||||
delete write;
|
||||
}
|
||||
}
|
||||
static void on_connect(uv_connect_t* req, int status) {
|
||||
if (auto conn = static_cast<ConnectReq*>(req->data)) {
|
||||
conn->sock->onConnected(status, std::move(conn->callback));
|
||||
delete conn;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
+3
-69
@@ -37,10 +37,6 @@
|
||||
#include <chrono>
|
||||
#include <set>
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
#include <json/json.h>
|
||||
#endif
|
||||
|
||||
namespace dht {
|
||||
|
||||
struct Value;
|
||||
@@ -109,21 +105,6 @@ struct OPENDHT_PUBLIC ValueType {
|
||||
EditPolicy editPolicy {DEFAULT_EDIT_POLICY};
|
||||
};
|
||||
|
||||
class TypeStore {
|
||||
public:
|
||||
void registerType(const ValueType& type) {
|
||||
types[type.id] = type;
|
||||
}
|
||||
const ValueType& getType(ValueType::Id type_id) const {
|
||||
const auto& t_it = types.find(type_id);
|
||||
return (t_it == types.end()) ? ValueType::USER_DATA : t_it->second;
|
||||
}
|
||||
private:
|
||||
std::map<ValueType::Id, ValueType> types {};
|
||||
};
|
||||
|
||||
struct CryptoValueCache;
|
||||
|
||||
/**
|
||||
* A "value" is data potentially stored on the Dht, with some metadata.
|
||||
*
|
||||
@@ -189,15 +170,6 @@ struct OPENDHT_PUBLIC Value
|
||||
return f1(v) or f2(v);
|
||||
};
|
||||
}
|
||||
std::vector<Sp<Value>> filter(const std::vector<Sp<Value>>& values) {
|
||||
if (not (*this))
|
||||
return values;
|
||||
std::vector<Sp<Value>> ret;
|
||||
for (const auto& v : values)
|
||||
if ((*this)(v))
|
||||
ret.emplace_back(v);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
/* Sneaky functions disguised in classes */
|
||||
@@ -374,14 +346,6 @@ struct OPENDHT_PUBLIC Value
|
||||
Value(ValueType::Id t, const uint8_t* dat_ptr, size_t dat_len, Id id = INVALID_ID)
|
||||
: id(id), type(t), data(dat_ptr, dat_ptr+dat_len) {}
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
/**
|
||||
* Build a value from a json object
|
||||
* @param json
|
||||
*/
|
||||
Value(Json::Value& json);
|
||||
#endif
|
||||
|
||||
template <typename Type>
|
||||
Value(ValueType::Id t, const Type& d, Id id = INVALID_ID)
|
||||
: id(id), type(t), data(packMsg(d)) {}
|
||||
@@ -453,18 +417,6 @@ struct OPENDHT_PUBLIC Value
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
/**
|
||||
* Build a json object from a value
|
||||
* Example:
|
||||
* {
|
||||
* "data":"base64ofdata",
|
||||
* id":"0", "seq":0,"type":3
|
||||
* }
|
||||
*/
|
||||
Json::Value toJson() const;
|
||||
#endif
|
||||
|
||||
/** Return the size in bytes used by this value in memory (minimum). */
|
||||
size_t size() const;
|
||||
|
||||
@@ -472,11 +424,11 @@ struct OPENDHT_PUBLIC Value
|
||||
void msgpack_pack_to_sign(Packer& pk) const
|
||||
{
|
||||
bool has_owner = owner && *owner;
|
||||
pk.pack_map((user_type.empty()?0:1) + (has_owner?(recipient ? 5 : 4):2));
|
||||
pk.pack_map((user_type.empty()?0:1) + (has_owner?(recipient == InfoHash() ? 4 : 5):2));
|
||||
if (has_owner) { // isSigned
|
||||
pk.pack(std::string("seq")); pk.pack(seq);
|
||||
pk.pack(std::string("owner")); owner->msgpack_pack(pk);
|
||||
if (recipient) {
|
||||
if (recipient != InfoHash()) {
|
||||
pk.pack(std::string("to")); pk.pack(recipient);
|
||||
}
|
||||
}
|
||||
@@ -577,7 +529,7 @@ struct OPENDHT_PUBLIC Value
|
||||
std::string user_type {};
|
||||
|
||||
/**
|
||||
* Sequence number
|
||||
* Sequence number to avoid replay attacks
|
||||
*/
|
||||
uint16_t seq {0};
|
||||
|
||||
@@ -590,16 +542,6 @@ struct OPENDHT_PUBLIC Value
|
||||
* Hold encrypted version of the data.
|
||||
*/
|
||||
Blob cypher {};
|
||||
|
||||
bool checkSignature();
|
||||
Sp<Value> decrypt(const crypto::PrivateKey& key);
|
||||
|
||||
private:
|
||||
/* Cache for crypto ops */
|
||||
bool signatureChecked {false};
|
||||
bool signatureValid {false};
|
||||
bool decrypted {false};
|
||||
Sp<Value> decryptedValue {};
|
||||
};
|
||||
|
||||
using ValuesExport = std::pair<InfoHash, Blob>;
|
||||
@@ -854,10 +796,6 @@ struct OPENDHT_PUBLIC Where
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return filters_.empty();
|
||||
}
|
||||
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<<(std::ostream& s, const dht::Where& q);
|
||||
|
||||
private:
|
||||
@@ -1008,10 +946,6 @@ unpackVector(const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
uint64_t unpackId(const Json::Value& json, const std::string& key);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
MSGPACK_ADD_ENUM(dht::Value::Field)
|
||||
+15
-11
@@ -92,9 +92,9 @@ cdef class _WithID(object):
|
||||
cdef class InfoHash(_WithID):
|
||||
cdef cpp.InfoHash _infohash
|
||||
def __cinit__(self, bytes str=b''):
|
||||
self._infohash = cpp.InfoHash(str) if str else cpp.InfoHash()
|
||||
self._infohash = cpp.InfoHash(str)
|
||||
def __bool__(InfoHash self):
|
||||
return <bool>self._infohash
|
||||
return not (self._infohash == cpp.InfoHash())
|
||||
def __richcmp__(InfoHash self, InfoHash other, int op):
|
||||
if op == 0:
|
||||
return self._infohash < other._infohash
|
||||
@@ -300,6 +300,8 @@ cdef class NodeSet(object):
|
||||
return s
|
||||
def __iter__(self):
|
||||
return NodeSetIter(self)
|
||||
def __len__(self):
|
||||
return self.size()
|
||||
|
||||
cdef class PrivateKey(_WithID):
|
||||
cdef shared_ptr[cpp.PrivateKey] _key
|
||||
@@ -442,6 +444,10 @@ cdef class DhtConfig(object):
|
||||
self._config.dht_config.node_config.node_id = id._infohash
|
||||
def setNetwork(self, unsigned netid):
|
||||
self._config.dht_config.node_config.network = netid
|
||||
def setPort(self, cpp.in_port_t port):
|
||||
self._config.dht_config.node_config.network_config.bind_port = port
|
||||
def setBindAddr(self, string addr):
|
||||
self._config.dht_config.node_config.network_config.bind_addr = addr
|
||||
def setMaintainStorage(self, bool maintain_storage):
|
||||
self._config.dht_config.node_config.maintain_storage = maintain_storage
|
||||
|
||||
@@ -460,7 +466,7 @@ cdef class DhtRunner(_WithID):
|
||||
if done_cb:
|
||||
cb_obj = {'done':done_cb}
|
||||
ref.Py_INCREF(cb_obj)
|
||||
self.thisptr.get().bootstrap(addr._addr, cpp.bindDoneCbSimple(done_callback_simple, <void*>cb_obj))
|
||||
self.thisptr.get().ping(addr._addr, cpp.bindDoneCbSimple(done_callback_simple, <void*>cb_obj))
|
||||
else:
|
||||
lock = threading.Condition()
|
||||
pending = 0
|
||||
@@ -484,12 +490,10 @@ cdef class DhtRunner(_WithID):
|
||||
def run(self, Identity id=None, is_bootstrap=False, cpp.in_port_t port=0, str ipv4="", str ipv6="", DhtConfig config=DhtConfig()):
|
||||
if id:
|
||||
config.setIdentity(id)
|
||||
if ipv4 or ipv6:
|
||||
bind4 = ipv4.encode() if ipv4 else b''
|
||||
bind6 = ipv6.encode() if ipv6 else b''
|
||||
self.thisptr.get().run(bind4, bind6, str(port).encode(), config._config)
|
||||
else:
|
||||
self.thisptr.get().run(port, config._config)
|
||||
config.setPort(port)
|
||||
if ipv6:
|
||||
config.setBindAddr(ipv6)
|
||||
self.thisptr.get().run(config._config)
|
||||
def join(self):
|
||||
self.thisptr.get().join()
|
||||
def shutdown(self, shutdown_cb=None):
|
||||
@@ -504,9 +508,9 @@ cdef class DhtRunner(_WithID):
|
||||
cpp.enableFileLogging(self.thisptr.get()[0], path.encode())
|
||||
def isRunning(self):
|
||||
return self.thisptr.get().isRunning()
|
||||
def getBound(self, cpp.sa_family_t af = 0):
|
||||
def getBound(self):
|
||||
s = SockAddr()
|
||||
s._addr = self.thisptr.get().getBound(af)
|
||||
s._addr = self.thisptr.get().getBound()
|
||||
return s
|
||||
def getStorageLog(self):
|
||||
return self.thisptr.get().getStorageLog().decode()
|
||||
|
||||
+11
-8
@@ -65,16 +65,15 @@ cdef extern from "opendht/infohash.h" namespace "dht":
|
||||
InfoHash get(string s)
|
||||
@staticmethod
|
||||
InfoHash getRandom()
|
||||
bool operator==(InfoHash) const
|
||||
bool operator<(InfoHash) const
|
||||
bool operator bool() const
|
||||
bool operator==(InfoHash)
|
||||
bool operator<(InfoHash)
|
||||
|
||||
cdef extern from "opendht/sockaddr.h" namespace "dht":
|
||||
cdef cppclass SockAddr:
|
||||
SockAddr() except +
|
||||
string toString() const
|
||||
in_port_t getPort() const
|
||||
void setPort(in_port_t p)
|
||||
void setPort(in_port_t p) const
|
||||
sa_family_t getFamily() const
|
||||
void setFamily(sa_family_t f)
|
||||
|
||||
@@ -199,11 +198,15 @@ cdef extern from "opendht/callbacks.h" namespace "dht":
|
||||
cdef DoneCallback bindDoneCb(DoneCallbackRaw cb, void *user_data)
|
||||
cdef DoneCallbackSimple bindDoneCbSimple(DoneCallbackSimpleRaw cb, void *user_data)
|
||||
|
||||
cppclass NetworkConfig:
|
||||
string bind_addr
|
||||
in_port_t bind_port
|
||||
cppclass Config:
|
||||
InfoHash node_id
|
||||
uint32_t network
|
||||
bool is_bootstrap
|
||||
bool maintain_storage
|
||||
NetworkConfig network_config
|
||||
cppclass SecureDhtConfig:
|
||||
Config node_config
|
||||
Identity id
|
||||
@@ -219,13 +222,12 @@ cdef extern from "opendht/dhtrunner.h" namespace "dht":
|
||||
InfoHash getId() const
|
||||
InfoHash getNodeId() const
|
||||
void bootstrap(const_char*, const_char*)
|
||||
void bootstrap(const SockAddr&, DoneCallbackSimple done_cb)
|
||||
void run(in_port_t, Config config)
|
||||
void run(const_char*, const_char*, const_char*, Config config)
|
||||
void ping(const SockAddr&, DoneCallbackSimple done_cb)
|
||||
void run(Config config)
|
||||
void join()
|
||||
void shutdown(ShutdownCallback)
|
||||
bool isRunning()
|
||||
SockAddr getBound(sa_family_t af) const
|
||||
SockAddr getBound() const
|
||||
string getStorageLog() const
|
||||
string getRoutingTablesLog(sa_family_t af) const
|
||||
string getSearchesLog(sa_family_t af) const
|
||||
@@ -260,3 +262,4 @@ cdef extern from "opendht/indexation/pht.h" namespace "dht::indexation":
|
||||
void insert(IndexKey k, IndexValue v, DoneCallbackSimple cb)
|
||||
@staticmethod
|
||||
LookupCallback bindLookupCb(LookupCallbackRaw cb, void *user_data)
|
||||
|
||||
+2
-2
@@ -50,8 +50,8 @@ setup(name="opendht",
|
||||
["@CURRENT_SOURCE_DIR@/opendht.pyx"],
|
||||
include_dirs = ['@PROJECT_SOURCE_DIR@/include'],
|
||||
language="c++",
|
||||
extra_compile_args=["-std=c++11"],
|
||||
extra_link_args=["-std=c++11"],
|
||||
extra_compile_args=["-std=c++14"],
|
||||
extra_link_args=["-std=c++14"],
|
||||
libraries=["opendht"],
|
||||
library_dirs = ['@CURRENT_BINARY_DIR@', '@PROJECT_BINARY_DIR@']
|
||||
))
|
||||
|
||||
+6
-27
@@ -1,7 +1,7 @@
|
||||
lib_LTLIBRARIES = libopendht.la
|
||||
|
||||
libopendht_la_CPPFLAGS = @CPPFLAGS@ -I$(top_srcdir)/include/opendht @Argon2_CFLAGS@ @JsonCpp_CFLAGS@ @MsgPack_CFLAGS@
|
||||
libopendht_la_LIBADD = @Argon2_LIBS@ @JsonCpp_LIBS@ @GnuTLS_LIBS@ @Nettle_LIBS@
|
||||
libopendht_la_CPPFLAGS = @CPPFLAGS@ -I$(top_srcdir)/include/opendht @Argon2_CFLAGS@
|
||||
libopendht_la_LIBADD = @Argon2_LIBS@ @GnuTLS_LIBS@ @Nettle_LIBS@
|
||||
libopendht_la_LDFLAGS = @LDFLAGS@ @Argon2_LDFLAGS@
|
||||
libopendht_la_SOURCES = \
|
||||
dht.cpp \
|
||||
@@ -9,10 +9,6 @@ libopendht_la_SOURCES = \
|
||||
listener.h \
|
||||
request.h \
|
||||
search.h \
|
||||
value_cache.h \
|
||||
op_cache.h \
|
||||
op_cache.cpp \
|
||||
net.h \
|
||||
parsed_message.h \
|
||||
node_cache.cpp \
|
||||
callbacks.cpp \
|
||||
@@ -26,6 +22,7 @@ libopendht_la_SOURCES = \
|
||||
securedht.cpp \
|
||||
dhtrunner.cpp \
|
||||
default_types.cpp \
|
||||
indexation/pht.cpp \
|
||||
log.cpp
|
||||
|
||||
if WIN32
|
||||
@@ -40,6 +37,7 @@ nobase_include_HEADERS = \
|
||||
../include/opendht/node_cache.h \
|
||||
../include/opendht/routing_table.h \
|
||||
../include/opendht/network_engine.h \
|
||||
../include/opendht/net.h \
|
||||
../include/opendht/scheduler.h \
|
||||
../include/opendht/rate_limiter.h \
|
||||
../include/opendht/utils.h \
|
||||
@@ -53,27 +51,8 @@ nobase_include_HEADERS = \
|
||||
../include/opendht/default_types.h \
|
||||
../include/opendht/log.h \
|
||||
../include/opendht/log_enable.h \
|
||||
../include/opendht/rng.h
|
||||
|
||||
if ENABLE_PROXY_SERVER
|
||||
libopendht_la_SOURCES += dht_proxy_server.cpp
|
||||
nobase_include_HEADERS += ../include/opendht/dht_proxy_server.h
|
||||
endif
|
||||
|
||||
if ENABLE_PROXY_CLIENT
|
||||
libopendht_la_SOURCES += dht_proxy_client.cpp
|
||||
nobase_include_HEADERS += ../include/opendht/dht_proxy_client.h ../include/opendht/dht_interface.h
|
||||
endif
|
||||
|
||||
libopendht_la_SOURCES += base64.h base64.cpp
|
||||
if PROXY_CLIENT_OR_SERVER
|
||||
nobase_include_HEADERS += ../include/opendht/proxy.h
|
||||
endif
|
||||
|
||||
if ENABLE_INDEXATION
|
||||
libopendht_la_SOURCES += indexation/pht.cpp
|
||||
nobase_include_HEADERS += ../include/opendht/indexation/pht.h
|
||||
endif
|
||||
../include/opendht/rng.h \
|
||||
../include/opendht/indexation/pht.h
|
||||
|
||||
clean-local:
|
||||
rm -rf libargon2.la
|
||||
|
||||
-143
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2017 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/>.
|
||||
*/
|
||||
|
||||
#include "base64.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Mainly based on the following stackoverflow question:
|
||||
* http://stackoverflow.com/questions/342409/how-do-i-base64-encode-decode-in-c
|
||||
*/
|
||||
static const char encoding_table[] = {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
|
||||
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
|
||||
'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
|
||||
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
|
||||
's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2',
|
||||
'3', '4', '5', '6', '7', '8', '9', '+', '/'
|
||||
};
|
||||
|
||||
static const size_t mod_table[] = { 0, 2, 1 };
|
||||
|
||||
char *base64_encode(const uint8_t *input, size_t input_length,
|
||||
char *output, size_t *output_length)
|
||||
{
|
||||
size_t i, j;
|
||||
size_t out_sz = *output_length;
|
||||
*output_length = 4 * ((input_length + 2) / 3);
|
||||
if (out_sz < *output_length || output == nullptr)
|
||||
return nullptr;
|
||||
|
||||
for (i = 0, j = 0; i < input_length; ) {
|
||||
uint8_t octet_a = i < input_length ? input[i++] : 0;
|
||||
uint8_t octet_b = i < input_length ? input[i++] : 0;
|
||||
uint8_t octet_c = i < input_length ? input[i++] : 0;
|
||||
|
||||
uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
|
||||
|
||||
output[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
|
||||
output[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
|
||||
output[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
|
||||
output[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
|
||||
}
|
||||
|
||||
for (i = 0; i < mod_table[input_length % 3]; i++)
|
||||
output[*output_length - 1 - i] = '=';
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
uint8_t *base64_decode(const char *input, size_t input_length,
|
||||
uint8_t *output, size_t *output_length)
|
||||
{
|
||||
size_t i, j;
|
||||
uint8_t decoding_table[256];
|
||||
|
||||
uint8_t c;
|
||||
for (c = 0; c < 64; c++)
|
||||
decoding_table[static_cast<int>(encoding_table[c])] = c;
|
||||
|
||||
if (input_length % 4 != 0 || input_length < 2)
|
||||
return nullptr;
|
||||
|
||||
size_t out_sz = *output_length;
|
||||
*output_length = input_length / 4 * 3;
|
||||
if (input[input_length - 1] == '=')
|
||||
(*output_length)--;
|
||||
if (input[input_length - 2] == '=')
|
||||
(*output_length)--;
|
||||
|
||||
if (out_sz < *output_length || output == nullptr)
|
||||
return nullptr;
|
||||
|
||||
for (i = 0, j = 0; i < input_length;) {
|
||||
uint8_t sextet_a = input[i] == '=' ? 0 & i++
|
||||
: decoding_table[static_cast<int>(input[i++])];
|
||||
uint8_t sextet_b = input[i] == '=' ? 0 & i++
|
||||
: decoding_table[static_cast<int>(input[i++])];
|
||||
uint8_t sextet_c = input[i] == '=' ? 0 & i++
|
||||
: decoding_table[static_cast<int>(input[i++])];
|
||||
uint8_t sextet_d = input[i] == '=' ? 0 & i++
|
||||
: decoding_table[static_cast<int>(input[i++])];
|
||||
|
||||
uint32_t triple = (sextet_a << 3 * 6) +
|
||||
(sextet_b << 2 * 6) +
|
||||
(sextet_c << 1 * 6) +
|
||||
(sextet_d << 0 * 6);
|
||||
|
||||
if (j < *output_length)
|
||||
output[j++] = (triple >> 2 * 8) & 0xFF;
|
||||
if (j < *output_length)
|
||||
output[j++] = (triple >> 1 * 8) & 0xFF;
|
||||
if (j < *output_length)
|
||||
output[j++] = (triple >> 0 * 8) & 0xFF;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string
|
||||
base64_encode(const std::vector<uint8_t>::const_iterator begin,
|
||||
const std::vector<uint8_t>::const_iterator end)
|
||||
{
|
||||
size_t output_length = 4 * ((std::distance(begin, end) + 2) / 3);
|
||||
std::string out;
|
||||
out.resize(output_length);
|
||||
base64_encode(&(*begin), std::distance(begin, end),
|
||||
&(*out.begin()), &output_length);
|
||||
out.resize(output_length);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
base64_encode(const std::vector<unsigned char>& str)
|
||||
{
|
||||
return base64_encode(str.cbegin(), str.cend());
|
||||
}
|
||||
|
||||
std::string
|
||||
base64_decode(const std::string& str)
|
||||
{
|
||||
size_t output_length = str.length() / 4 * 3 + 2;
|
||||
std::vector<uint8_t> output;
|
||||
output.resize(output_length);
|
||||
base64_decode(str.data(), str.size(), output.data(), &output_length);
|
||||
output.resize(output_length);
|
||||
return std::string(output.begin(), output.end());
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2017 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 <string>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* Encode a buffer in base64.
|
||||
*
|
||||
* @param str the input buffer
|
||||
* @return a base64-encoded buffer
|
||||
*/
|
||||
std::string base64_encode(const std::vector<unsigned char>& str);
|
||||
/**
|
||||
* Decode a buffer in base64.
|
||||
*
|
||||
* @param str the input buffer
|
||||
* @return a base64-decoded buffer
|
||||
*/
|
||||
std::string base64_decode(const std::string& str);
|
||||
+2
-58
@@ -62,66 +62,10 @@ NodeStats::toString() const
|
||||
ss << "Known nodes: " << good_nodes << " good, " << dubious_nodes << " dubious, " << incoming_nodes << " incoming." << std::endl;
|
||||
if (table_depth > 1) {
|
||||
ss << "Routing table depth: " << table_depth << std::endl;
|
||||
ss << "Network size estimation: " << getNetworkSizeEstimation() << " nodes" << std::endl;
|
||||
unsigned long tot_nodes = 8 * std::exp2(table_depth);
|
||||
ss << "Network size estimation: " << tot_nodes << " nodes" << std::endl;
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
/**
|
||||
* Build a json object from a NodeStats
|
||||
*/
|
||||
Json::Value
|
||||
NodeStats::toJson() const
|
||||
{
|
||||
Json::Value val;
|
||||
val["good"] = static_cast<Json::LargestUInt>(good_nodes);
|
||||
val["dubious"] = static_cast<Json::LargestUInt>(dubious_nodes);
|
||||
val["incoming"] = static_cast<Json::LargestUInt>(incoming_nodes);
|
||||
if (table_depth > 1) {
|
||||
val["table_depth"] = static_cast<Json::LargestUInt>(table_depth);
|
||||
val["network_size_estimation"] = static_cast<Json::LargestUInt>(getNetworkSizeEstimation());
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
NodeStats::NodeStats(const Json::Value& val)
|
||||
{
|
||||
if (val.isMember("good"))
|
||||
good_nodes = static_cast<unsigned>(val["good"].asLargestUInt());
|
||||
if (val.isMember("dubious"))
|
||||
dubious_nodes = static_cast<unsigned>(val["dubious"].asLargestUInt());
|
||||
if (val.isMember("incoming"))
|
||||
incoming_nodes = static_cast<unsigned>(val["incoming"].asLargestUInt());
|
||||
if (val.isMember("table_depth"))
|
||||
table_depth = static_cast<unsigned>(val["table_depth"].asLargestUInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a json object from a NodeStats
|
||||
*/
|
||||
Json::Value
|
||||
NodeInfo::toJson() const
|
||||
{
|
||||
Json::Value val;
|
||||
if (id)
|
||||
val["id"] = id.toString();
|
||||
val["node_id"] = node_id.toString();
|
||||
val["ipv4"] = ipv4.toJson();
|
||||
val["ipv6"] = ipv6.toJson();
|
||||
return val;
|
||||
}
|
||||
|
||||
NodeInfo::NodeInfo(const Json::Value& v)
|
||||
{
|
||||
if (v.isMember("id"))
|
||||
id = InfoHash(v["id"].asString());
|
||||
node_id = InfoHash(v["node_id"].asString());
|
||||
ipv4 = NodeStats(v["ipv4"]);
|
||||
ipv6 = NodeStats(v["ipv6"]);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
+14
-51
@@ -84,7 +84,7 @@ static constexpr size_t PASSWORD_SALT_LENGTH {16};
|
||||
|
||||
constexpr gnutls_digest_algorithm_t gnutlsHashAlgo(size_t min_res) {
|
||||
return (min_res > 256/8) ? GNUTLS_DIG_SHA512 : (
|
||||
(min_res > 160/8) ? GNUTLS_DIG_SHA256 : (
|
||||
(min_res > 128/8) ? GNUTLS_DIG_SHA256 : (
|
||||
GNUTLS_DIG_SHA1));
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ bool aesKeySizeGood(size_t key_size)
|
||||
#define GCM_DIGEST_SIZE GCM_BLOCK_SIZE
|
||||
#endif
|
||||
|
||||
Blob aesEncrypt(const Blob& data, const Blob& key)
|
||||
Blob aesEncrypt(const Blob& data, const SecureBlob& key)
|
||||
{
|
||||
if (not aesKeySizeGood(key.size()))
|
||||
throw DecryptError("Wrong key size");
|
||||
@@ -127,7 +127,7 @@ Blob aesEncrypt(const Blob& data, const Blob& key)
|
||||
std::generate_n(ret.begin(), GCM_IV_SIZE, std::bind(rand_byte, std::ref(rdev)));
|
||||
}
|
||||
struct gcm_aes_ctx aes;
|
||||
gcm_aes_set_key(&aes, key.size(), key.data());
|
||||
gcm_aes_set_key(&aes, key.size(), key.makeInsecure().data());
|
||||
gcm_aes_set_iv(&aes, GCM_IV_SIZE, ret.data());
|
||||
gcm_aes_update(&aes, data.size(), data.data());
|
||||
|
||||
@@ -139,13 +139,13 @@ Blob aesEncrypt(const Blob& data, const Blob& key)
|
||||
Blob aesEncrypt(const Blob& data, const std::string& password)
|
||||
{
|
||||
Blob salt;
|
||||
Blob key = stretchKey(password, salt, 256 / 8);
|
||||
SecureBlob key = stretchKey(password, salt, 256 / 8);
|
||||
Blob encrypted = aesEncrypt(data, key);
|
||||
encrypted.insert(encrypted.begin(), salt.begin(), salt.end());
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
Blob aesDecrypt(const Blob& data, const Blob& key)
|
||||
Blob aesDecrypt(const Blob& data, const SecureBlob& key)
|
||||
{
|
||||
if (not aesKeySizeGood(key.size()))
|
||||
throw DecryptError("Wrong key size");
|
||||
@@ -185,12 +185,12 @@ Blob aesDecrypt(const Blob& data, const std::string& password)
|
||||
if (data.size() <= PASSWORD_SALT_LENGTH)
|
||||
throw DecryptError("Wrong data size");
|
||||
Blob salt {data.begin(), data.begin()+PASSWORD_SALT_LENGTH};
|
||||
Blob key = stretchKey(password, salt, 256/8);
|
||||
SecureBlob key = stretchKey(password, salt, 256/8);
|
||||
Blob encrypted {data.begin()+PASSWORD_SALT_LENGTH, data.end()};
|
||||
return aesDecrypt(encrypted, key);
|
||||
}
|
||||
|
||||
Blob stretchKey(const std::string& password, Blob& salt, size_t key_length)
|
||||
SecureBlob stretchKey(const std::string& password, Blob& salt, size_t key_length)
|
||||
{
|
||||
if (salt.empty()) {
|
||||
salt.resize(PASSWORD_SALT_LENGTH);
|
||||
@@ -212,21 +212,12 @@ Blob hash(const Blob& data, size_t hash_len)
|
||||
Blob res;
|
||||
res.resize(res_size);
|
||||
const gnutls_datum_t gdat {(uint8_t*)data.data(), (unsigned)data.size()};
|
||||
if (auto err = gnutls_fingerprint(algo, &gdat, res.data(), &res_size))
|
||||
throw CryptoException(std::string("Can't compute hash: ") + gnutls_strerror(err));
|
||||
if (gnutls_fingerprint(algo, &gdat, res.data(), &res_size))
|
||||
throw CryptoException("Can't compute hash !");
|
||||
res.resize(std::min(hash_len, res_size));
|
||||
return res;
|
||||
}
|
||||
|
||||
void hash(const uint8_t* data, size_t data_length, uint8_t* hash, size_t hash_length)
|
||||
{
|
||||
auto algo = gnutlsHashAlgo(hash_length);
|
||||
size_t res_size = hash_length;
|
||||
const gnutls_datum_t gdat {(uint8_t*)data, (unsigned)data_length};
|
||||
if (auto err = gnutls_fingerprint(algo, &gdat, hash, &res_size))
|
||||
throw CryptoException(std::string("Can't compute hash: ") + gnutls_strerror(err));
|
||||
}
|
||||
|
||||
PrivateKey::PrivateKey()
|
||||
{}
|
||||
|
||||
@@ -411,8 +402,6 @@ PublicKey::operator=(PublicKey&& o) noexcept
|
||||
void
|
||||
PublicKey::pack(Blob& b) const
|
||||
{
|
||||
if (not pk)
|
||||
throw CryptoException(std::string("Could not export public key: null key"));
|
||||
std::vector<uint8_t> tmp(2048);
|
||||
size_t sz = tmp.size();
|
||||
int err = gnutls_pubkey_export(pk, GNUTLS_X509_FMT_DER, tmp.data(), &sz);
|
||||
@@ -439,8 +428,6 @@ PublicKey::unpack(const uint8_t* data, size_t data_size)
|
||||
std::string
|
||||
PublicKey::toString() const
|
||||
{
|
||||
if (not pk)
|
||||
throw CryptoException(std::string("Could not print public key: null key"));
|
||||
std::string ret;
|
||||
size_t sz = ret.size();
|
||||
int err = gnutls_pubkey_export(pk, GNUTLS_X509_FMT_PEM, (void*)ret.data(), &sz);
|
||||
@@ -519,11 +506,7 @@ PublicKey::encrypt(const Blob& data) const
|
||||
unsigned aes_key_sz = aesKeySize(max_block_sz);
|
||||
if (aes_key_sz == 0)
|
||||
throw CryptoException("Key is not long enough for AES128");
|
||||
Blob key(aes_key_sz);
|
||||
{
|
||||
crypto::random_device rdev;
|
||||
std::generate_n(key.begin(), key.size(), std::bind(rand_byte, std::ref(rdev)));
|
||||
}
|
||||
auto key = SecureBlob::getRandom(aes_key_sz);
|
||||
auto data_encrypted = aesEncrypt(data, key);
|
||||
|
||||
Blob ret;
|
||||
@@ -542,11 +525,7 @@ PublicKey::getId() const
|
||||
return {};
|
||||
InfoHash id;
|
||||
size_t sz = id.size();
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030401
|
||||
const int flags = 0;
|
||||
#else
|
||||
const int flags = (id.size() == 32) ? GNUTLS_KEYID_USE_SHA256 : 0;
|
||||
#endif
|
||||
constexpr int flags = (id.size() == 32) ? GNUTLS_KEYID_USE_SHA256 : 0;
|
||||
if (auto err = gnutls_pubkey_get_key_id(pk, flags, id.data(), &sz))
|
||||
throw CryptoException(std::string("Can't get public key ID: ") + gnutls_strerror(err));
|
||||
if (sz != id.size())
|
||||
@@ -849,15 +828,6 @@ Certificate::addRevocationList(std::shared_ptr<RevocationList> list)
|
||||
revocation_lists.emplace(std::move(list));
|
||||
}
|
||||
|
||||
std::chrono::system_clock::time_point
|
||||
Certificate::getActivation() const
|
||||
{
|
||||
auto t = gnutls_x509_crt_get_activation_time(cert);
|
||||
if (t == (time_t)-1)
|
||||
return std::chrono::system_clock::time_point::min();
|
||||
return std::chrono::system_clock::from_time_t(t);
|
||||
}
|
||||
|
||||
std::chrono::system_clock::time_point
|
||||
Certificate::getExpiration() const
|
||||
{
|
||||
@@ -930,13 +900,8 @@ Certificate::generate(const PrivateKey& key, const std::string& name, Identity c
|
||||
return {};
|
||||
Certificate ret {cert};
|
||||
|
||||
int64_t now = time(NULL);
|
||||
/* 2038 bug: don't allow time wrap */
|
||||
auto boundTime = [](int64_t t) -> time_t {
|
||||
return std::min<int64_t>(t, std::numeric_limits<time_t>::max());
|
||||
};
|
||||
gnutls_x509_crt_set_activation_time(cert, boundTime(now));
|
||||
gnutls_x509_crt_set_expiration_time(cert, boundTime(now + (10 * 365 * 24 * 60 * 60)));
|
||||
gnutls_x509_crt_set_activation_time(cert, time(NULL));
|
||||
gnutls_x509_crt_set_expiration_time(cert, time(NULL) + (20 * 365 * 24 * 60 * 60));
|
||||
if (gnutls_x509_crt_set_key(cert, key.x509_key) != GNUTLS_E_SUCCESS) {
|
||||
std::cerr << "Error when setting certificate key" << std::endl;
|
||||
return {};
|
||||
@@ -972,7 +937,7 @@ Certificate::generate(const PrivateKey& key, const std::string& name, Identity c
|
||||
|
||||
if (ca.first && ca.second) {
|
||||
if (not ca.second->isCA()) {
|
||||
// Signing certificate must be CA.
|
||||
std::cerr << "Error: signing certificate must be CA." << std::endl;
|
||||
return {};
|
||||
}
|
||||
//if (gnutls_x509_crt_sign2(cert, ca.second->cert, ca.first->x509_key, get_dig(cert), 0) != GNUTLS_E_SUCCESS) {
|
||||
@@ -1309,10 +1274,8 @@ std::ostream& operator<< (std::ostream& o, const TrustList::VerifyResult& h)
|
||||
o << "* Certificate has expired" << std::endl;
|
||||
if (h.result & GNUTLS_CERT_UNEXPECTED_OWNER)
|
||||
o << "* The owner is not the expected one" << std::endl;
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030401
|
||||
if (h.result & GNUTLS_CERT_PURPOSE_MISMATCH)
|
||||
o << "* Certificate or an intermediate does not match the intended purpose" << std::endl;
|
||||
#endif
|
||||
if (h.result & GNUTLS_CERT_MISMATCH)
|
||||
o << "* Certificate presented isn't the expected one" << std::endl;
|
||||
} else {
|
||||
|
||||
+431
-481
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,887 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2018 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
#include "dht_proxy_server.h"
|
||||
|
||||
#include "default_types.h"
|
||||
#include "dhtrunner.h"
|
||||
|
||||
#include <msgpack.hpp>
|
||||
#include <json/json.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
namespace dht {
|
||||
|
||||
struct DhtProxyServer::PermanentPut {
|
||||
time_point expiration;
|
||||
std::string pushToken;
|
||||
std::string clientId;
|
||||
Sp<Scheduler::Job> expireJob;
|
||||
Sp<Scheduler::Job> expireNotifyJob;
|
||||
};
|
||||
struct DhtProxyServer::SearchPuts {
|
||||
std::map<dht::Value::Id, PermanentPut> puts;
|
||||
};
|
||||
|
||||
constexpr const std::chrono::minutes PRINT_STATS_PERIOD {2};
|
||||
|
||||
DhtProxyServer::DhtProxyServer(std::shared_ptr<DhtRunner> dht, in_port_t port , const std::string& pushServer)
|
||||
: dht_(dht) , pushServer_(pushServer)
|
||||
{
|
||||
if (not dht_)
|
||||
throw std::invalid_argument("A DHT instance must be provided");
|
||||
// NOTE in c++14, use make_unique
|
||||
service_ = std::unique_ptr<restbed::Service>(new restbed::Service());
|
||||
|
||||
std::cout << "Running DHT proxy server on port " << port << std::endl;
|
||||
if (not pushServer.empty()) {
|
||||
#if !OPENDHT_PUSH_NOTIFICATIONS
|
||||
std::cerr << "Push server defined but built OpenDHT built without push notification support" << std::endl;
|
||||
#else
|
||||
std::cout << "Using push notification server: " << pushServer << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
server_thread = std::thread([this, port]() {
|
||||
// Create endpoints
|
||||
auto resource = std::make_shared<restbed::Resource>();
|
||||
resource->set_path("/");
|
||||
resource->set_method_handler("GET", std::bind(&DhtProxyServer::getNodeInfo, this, _1));
|
||||
resource->set_method_handler("STATS", std::bind(&DhtProxyServer::getStats, this, _1));
|
||||
service_->publish(resource);
|
||||
resource = std::make_shared<restbed::Resource>();
|
||||
resource->set_path("/{hash: .*}");
|
||||
resource->set_method_handler("GET", std::bind(&DhtProxyServer::get, this, _1));
|
||||
resource->set_method_handler("LISTEN", [this](const Sp<restbed::Session>& session) mutable { listen(session); } );
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
resource->set_method_handler("SUBSCRIBE", [this](const Sp<restbed::Session>& session) mutable { subscribe(session); } );
|
||||
resource->set_method_handler("UNSUBSCRIBE", [this](const Sp<restbed::Session>& session) mutable { unsubscribe(session); } );
|
||||
#endif //OPENDHT_PUSH_NOTIFICATIONS
|
||||
resource->set_method_handler("POST", [this](const Sp<restbed::Session>& session) mutable { put(session); });
|
||||
#if OPENDHT_PROXY_SERVER_IDENTITY
|
||||
resource->set_method_handler("SIGN", std::bind(&DhtProxyServer::putSigned, this, _1));
|
||||
resource->set_method_handler("ENCRYPT", std::bind(&DhtProxyServer::putEncrypted, this, _1));
|
||||
#endif // OPENDHT_PROXY_SERVER_IDENTITY
|
||||
resource->set_method_handler("OPTIONS", std::bind(&DhtProxyServer::handleOptionsMethod, this, _1));
|
||||
service_->publish(resource);
|
||||
resource = std::make_shared<restbed::Resource>();
|
||||
resource->set_path("/{hash: .*}/{value: .*}");
|
||||
resource->set_method_handler("GET", std::bind(&DhtProxyServer::getFiltered, this, _1));
|
||||
service_->publish(resource);
|
||||
|
||||
// Start server
|
||||
auto settings = std::make_shared<restbed::Settings>();
|
||||
settings->set_default_header("Content-Type", "application/json");
|
||||
settings->set_default_header("Connection", "keep-alive");
|
||||
settings->set_default_header("Access-Control-Allow-Origin", "*");
|
||||
std::chrono::milliseconds timeout(std::numeric_limits<int>::max());
|
||||
settings->set_connection_timeout(timeout); // there is a timeout, but really huge
|
||||
settings->set_port(port);
|
||||
auto maxThreads = std::thread::hardware_concurrency() - 1;
|
||||
settings->set_worker_limit(maxThreads > 1 ? maxThreads : 1);
|
||||
lastStatsReset_ = clock::now();
|
||||
try {
|
||||
service_->start(settings);
|
||||
} catch(std::system_error& e) {
|
||||
std::cerr << "Error running server on port " << port << ": " << e.what() << std::endl;
|
||||
}
|
||||
});
|
||||
|
||||
listenThread_ = std::thread([this]() {
|
||||
while (not service_->is_up() and not stopListeners) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
while (service_->is_up() and not stopListeners) {
|
||||
removeClosedListeners();
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
// Remove last listeners
|
||||
removeClosedListeners(false);
|
||||
});
|
||||
schedulerThread_ = std::thread([this]() {
|
||||
while (not service_->is_up() and not stopListeners) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
while (service_->is_up() and not stopListeners) {
|
||||
std::unique_lock<std::mutex> lock(schedulerLock_);
|
||||
auto next = scheduler_.run();
|
||||
if (next == time_point::max())
|
||||
schedulerCv_.wait(lock);
|
||||
else
|
||||
schedulerCv_.wait_until(lock, next);
|
||||
}
|
||||
});
|
||||
dht->forwardAllMessages(true);
|
||||
printStatsJob_ = scheduler_.add(scheduler_.time() + PRINT_STATS_PERIOD, [this] {
|
||||
if (stopListeners) return;
|
||||
if (service_->is_up())
|
||||
updateStats();
|
||||
// Refresh stats cache
|
||||
auto newInfo = dht_->getNodeInfo();
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(statsMutex_);
|
||||
nodeInfo_ = std::move(newInfo);
|
||||
}
|
||||
scheduler_.edit(printStatsJob_, scheduler_.time() + PRINT_STATS_PERIOD);
|
||||
});
|
||||
}
|
||||
|
||||
DhtProxyServer::~DhtProxyServer()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::stop()
|
||||
{
|
||||
if (printStatsJob_)
|
||||
printStatsJob_->cancel();
|
||||
service_->stop();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(lockListener_);
|
||||
auto listener = currentListeners_.begin();
|
||||
while (listener != currentListeners_.end()) {
|
||||
listener->session->close();
|
||||
++listener;
|
||||
}
|
||||
}
|
||||
stopListeners = true;
|
||||
schedulerCv_.notify_all();
|
||||
// listenThreads_ will stop because there is no more sessions
|
||||
if (listenThread_.joinable())
|
||||
listenThread_.join();
|
||||
if (schedulerThread_.joinable())
|
||||
schedulerThread_.join();
|
||||
if (server_thread.joinable())
|
||||
server_thread.join();
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::updateStats() const
|
||||
{
|
||||
auto now = clock::now();
|
||||
auto last = lastStatsReset_.exchange(now);
|
||||
auto count = requestNum_.exchange(0);
|
||||
auto dt = std::chrono::duration<double>(now - last);
|
||||
stats_.requestRate = count / dt.count();
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
stats_.pushListenersCount = pushListeners_.size();
|
||||
#endif
|
||||
stats_.putCount = puts_.size();
|
||||
stats_.listenCount = currentListeners_.size();
|
||||
stats_.nodeInfo = nodeInfo_;
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::getNodeInfo(const Sp<restbed::Session>& session) const
|
||||
{
|
||||
requestNum_++;
|
||||
const auto request = session->get_request();
|
||||
int content_length = std::stoi(request->get_header("Content-Length", "0"));
|
||||
session->fetch(content_length,
|
||||
[this](const Sp<restbed::Session>& s, const restbed::Bytes& /*b*/) mutable
|
||||
{
|
||||
try {
|
||||
if (dht_) {
|
||||
Json::Value result;
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(statsMutex_);
|
||||
if (nodeInfo_.ipv4.good_nodes == 0 && nodeInfo_.ipv6.good_nodes == 0) {
|
||||
// NOTE: we want to avoid the disconnected state as much as possible
|
||||
// So, if the node is disconnected, we should force the update of the cache
|
||||
// and reconnect as soon as possible
|
||||
// This should not happen much
|
||||
nodeInfo_ = dht_->getNodeInfo();
|
||||
}
|
||||
result = nodeInfo_.toJson();
|
||||
}
|
||||
result["public_ip"] = s->get_origin(); // [ipv6:ipv4]:port or ipv4:port
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["commentStyle"] = "None";
|
||||
wbuilder["indentation"] = "";
|
||||
auto output = Json::writeString(wbuilder, result) + "\n";
|
||||
s->close(restbed::OK, output);
|
||||
}
|
||||
else
|
||||
s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}");
|
||||
} catch (...) {
|
||||
s->close(restbed::INTERNAL_SERVER_ERROR, "{\"err\":\"Internal server error\"}");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::getStats(const Sp<restbed::Session>& session) const
|
||||
{
|
||||
requestNum_++;
|
||||
const auto request = session->get_request();
|
||||
int content_length = std::stoi(request->get_header("Content-Length", "0"));
|
||||
session->fetch(content_length,
|
||||
[this](const Sp<restbed::Session>& s, const restbed::Bytes& /*b*/) mutable
|
||||
{
|
||||
try {
|
||||
if (dht_) {
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["commentStyle"] = "None";
|
||||
wbuilder["indentation"] = "";
|
||||
auto output = Json::writeString(wbuilder, stats_.toJson()) + "\n";
|
||||
s->close(restbed::OK, output);
|
||||
#else
|
||||
s->close(restbed::NotFound, "{\"err\":\"JSON not enabled on this instance\"}");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}");
|
||||
} catch (...) {
|
||||
s->close(restbed::INTERNAL_SERVER_ERROR, "{\"err\":\"Internal server error\"}");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::get(const Sp<restbed::Session>& session) const
|
||||
{
|
||||
requestNum_++;
|
||||
const auto request = session->get_request();
|
||||
int content_length = std::stoi(request->get_header("Content-Length", "0"));
|
||||
auto hash = request->get_path_parameter("hash");
|
||||
session->fetch(content_length,
|
||||
[=](const Sp<restbed::Session>& s, const restbed::Bytes& /*b* */)
|
||||
{
|
||||
try {
|
||||
if (dht_) {
|
||||
InfoHash infoHash(hash);
|
||||
if (!infoHash) {
|
||||
infoHash = InfoHash::get(hash);
|
||||
}
|
||||
s->yield(restbed::OK, "", [=](const Sp<restbed::Session>&) {});
|
||||
dht_->get(infoHash, [s](const Sp<Value>& value) {
|
||||
if (s->is_closed()) return false;
|
||||
// Send values as soon as we get them
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["commentStyle"] = "None";
|
||||
wbuilder["indentation"] = "";
|
||||
auto output = Json::writeString(wbuilder, value->toJson()) + "\n";
|
||||
s->yield(output, [](const Sp<restbed::Session>& /*session*/){ });
|
||||
return true;
|
||||
}, [s](bool /*ok* */) {
|
||||
// Communication is finished
|
||||
if (not s->is_closed()) {
|
||||
s->close();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}");
|
||||
}
|
||||
} catch (...) {
|
||||
s->close(restbed::INTERNAL_SERVER_ERROR, "{\"err\":\"Internal server error\"}");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::listen(const Sp<restbed::Session>& session)
|
||||
{
|
||||
requestNum_++;
|
||||
const auto request = session->get_request();
|
||||
int content_length = std::stoi(request->get_header("Content-Length", "0"));
|
||||
auto hash = request->get_path_parameter("hash");
|
||||
InfoHash infoHash(hash);
|
||||
if (!infoHash)
|
||||
infoHash = InfoHash::get(hash);
|
||||
session->fetch(content_length,
|
||||
[=](const Sp<restbed::Session>& s, const restbed::Bytes& /*b* */)
|
||||
{
|
||||
try {
|
||||
if (dht_) {
|
||||
InfoHash infoHash(hash);
|
||||
if (!infoHash) {
|
||||
infoHash = InfoHash::get(hash);
|
||||
}
|
||||
s->yield(restbed::OK);
|
||||
// Handle client deconnection
|
||||
// NOTE: for now, there is no handler, so we test the session in a thread
|
||||
// will be the case in restbed 5.0
|
||||
SessionToHashToken listener;
|
||||
listener.session = session;
|
||||
listener.hash = infoHash;
|
||||
// cache the session to avoid an incrementation of the shared_ptr's counter
|
||||
// else, the session->close() will not close the socket.
|
||||
auto cacheSession = std::weak_ptr<restbed::Session>(s);
|
||||
listener.token = dht_->listen(infoHash, [cacheSession](const std::vector<Sp<Value>>& values, bool expired) {
|
||||
auto s = cacheSession.lock();
|
||||
if (!s) return false;
|
||||
// Send values as soon as we get them
|
||||
if (!s->is_closed()) {
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["commentStyle"] = "None";
|
||||
wbuilder["indentation"] = "";
|
||||
for (const auto& value : values) {
|
||||
auto val = value->toJson();
|
||||
if (expired)
|
||||
val["expired"] = true;
|
||||
auto output = Json::writeString(wbuilder, val) + "\n";
|
||||
s->yield(output, [](const Sp<restbed::Session>&){ });
|
||||
}
|
||||
}
|
||||
return !s->is_closed();
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(lockListener_);
|
||||
currentListeners_.emplace_back(std::move(listener));
|
||||
}
|
||||
} else {
|
||||
session->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}");
|
||||
}
|
||||
} catch (...) {
|
||||
s->close(restbed::INTERNAL_SERVER_ERROR, "{\"err\":\"Internal server error\"}");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
|
||||
struct DhtProxyServer::Listener {
|
||||
std::shared_ptr<proxy::ListenToken> token;
|
||||
std::string clientId;
|
||||
std::future<size_t> internalToken;
|
||||
Sp<Scheduler::Job> expireJob;
|
||||
Sp<Scheduler::Job> expireNotifyJob;
|
||||
};
|
||||
struct DhtProxyServer::PushListener {
|
||||
std::map<InfoHash, std::vector<Listener>> listeners;
|
||||
bool isAndroid;
|
||||
};
|
||||
|
||||
void
|
||||
DhtProxyServer::subscribe(const std::shared_ptr<restbed::Session>& session)
|
||||
{
|
||||
requestNum_++;
|
||||
const auto request = session->get_request();
|
||||
int content_length = std::stoi(request->get_header("Content-Length", "0"));
|
||||
auto hash = request->get_path_parameter("hash");
|
||||
InfoHash infoHash(hash);
|
||||
if (!infoHash)
|
||||
infoHash = InfoHash::get(hash);
|
||||
session->fetch(content_length,
|
||||
[=](const std::shared_ptr<restbed::Session> s, const restbed::Bytes& b) mutable
|
||||
{
|
||||
try {
|
||||
std::string err;
|
||||
Json::Value root;
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto* char_data = reinterpret_cast<const char*>(b.data());
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
if (!reader->parse(char_data, char_data + b.size(), &root, &err)) {
|
||||
s->close(restbed::BAD_REQUEST, "{\"err\":\"Incorrect JSON\"}");
|
||||
return;
|
||||
}
|
||||
auto pushToken = root["key"].asString();
|
||||
if (pushToken.empty()) {
|
||||
s->close(restbed::BAD_REQUEST, "{\"err\":\"No token\"}");
|
||||
return;
|
||||
}
|
||||
auto tokenFromReq = unpackId(root, "token");
|
||||
auto platform = root["platform"].asString();
|
||||
auto isAndroid = platform == "android";
|
||||
auto clientId = root.isMember("client_id") ? root["client_id"].asString() : std::string();
|
||||
|
||||
std::cout << "Subscribe " << infoHash << " token:" << tokenFromReq << " client:" << clientId << std::endl;
|
||||
|
||||
{
|
||||
std::lock(schedulerLock_, lockListener_);
|
||||
std::lock_guard<std::mutex> lk1(lockListener_, std::adopt_lock);
|
||||
std::lock_guard<std::mutex> lk2(schedulerLock_, std::adopt_lock);
|
||||
scheduler_.syncTime();
|
||||
auto timeout = scheduler_.time() + proxy::OP_TIMEOUT;
|
||||
// Check if listener is already present and refresh timeout if launched
|
||||
// One push listener per pushToken.infoHash.clientId
|
||||
auto pushListener = pushListeners_.emplace(pushToken, PushListener{}).first;
|
||||
auto listeners = pushListener->second.listeners.emplace(infoHash, std::vector<Listener>{}).first;
|
||||
for (auto& listener: listeners->second) {
|
||||
if (listener.clientId == clientId) {
|
||||
*listener.token = tokenFromReq;
|
||||
scheduler_.edit(listener.expireJob, timeout);
|
||||
scheduler_.edit(listener.expireNotifyJob, timeout - proxy::OP_MARGIN);
|
||||
s->close(restbed::OK, "{\"token\": " + std::to_string(tokenFromReq) + "}\n");
|
||||
schedulerCv_.notify_one();
|
||||
return;
|
||||
}
|
||||
}
|
||||
listeners->second.emplace_back(Listener{});
|
||||
auto& listener = listeners->second.back();
|
||||
listener.clientId = clientId;
|
||||
|
||||
// New listener
|
||||
pushListener->second.isAndroid = isAndroid;
|
||||
|
||||
// The listener is not found, so add it.
|
||||
auto token = std::make_shared<proxy::ListenToken>(tokenFromReq);
|
||||
listener.token = token;
|
||||
listener.internalToken = dht_->listen(infoHash,
|
||||
[this, token, infoHash, pushToken, isAndroid, clientId](std::vector<std::shared_ptr<Value>> /*value*/) {
|
||||
// Build message content.
|
||||
Json::Value json;
|
||||
json["key"] = infoHash.toString();
|
||||
json["to"] = clientId;
|
||||
json["token"] = std::to_string(*token);
|
||||
sendPushNotification(pushToken, json, isAndroid);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
listener.expireJob = scheduler_.add(timeout,
|
||||
[this, clientId, infoHash, pushToken] {
|
||||
cancelPushListen(pushToken, infoHash, clientId);
|
||||
}
|
||||
);
|
||||
listener.expireNotifyJob = scheduler_.add(timeout - proxy::OP_MARGIN,
|
||||
[this, token, infoHash, pushToken, isAndroid, clientId] {
|
||||
std::cout << "Listener: sending refresh " << infoHash << std::endl;
|
||||
Json::Value json;
|
||||
json["timeout"] = infoHash.toString();
|
||||
json["to"] = clientId;
|
||||
json["token"] = std::to_string(*token);
|
||||
sendPushNotification(pushToken, json, isAndroid);
|
||||
}
|
||||
);
|
||||
}
|
||||
schedulerCv_.notify_one();
|
||||
s->close(restbed::OK, "{\"token\": " + std::to_string(tokenFromReq) + "}\n");
|
||||
} catch (...) {
|
||||
s->close(restbed::INTERNAL_SERVER_ERROR, "{\"err\":\"Internal server error\"}");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::unsubscribe(const std::shared_ptr<restbed::Session>& session)
|
||||
{
|
||||
requestNum_++;
|
||||
const auto request = session->get_request();
|
||||
int content_length = std::stoi(request->get_header("Content-Length", "0"));
|
||||
auto hash = request->get_path_parameter("hash");
|
||||
InfoHash infoHash(hash);
|
||||
if (!infoHash)
|
||||
infoHash = InfoHash::get(hash);
|
||||
session->fetch(content_length,
|
||||
[=](const std::shared_ptr<restbed::Session> s, const restbed::Bytes& b)
|
||||
{
|
||||
try {
|
||||
std::string err;
|
||||
Json::Value root;
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto* char_data = reinterpret_cast<const char*>(b.data());
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
if (!reader->parse(char_data, char_data + b.size(), &root, &err)) {
|
||||
s->close(restbed::BAD_REQUEST, "{\"err\":\"Incorrect JSON\"}");
|
||||
return;
|
||||
}
|
||||
auto pushToken = root["key"].asString();
|
||||
if (pushToken.empty()) return;
|
||||
auto clientId = root["client_id"].asString();
|
||||
|
||||
cancelPushListen(pushToken, infoHash, clientId);
|
||||
s->close(restbed::OK);
|
||||
} catch (...) {
|
||||
s->close(restbed::INTERNAL_SERVER_ERROR, "{\"err\":\"Internal server error\"}");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::cancelPushListen(const std::string& pushToken, const dht::InfoHash& key, const std::string& clientId)
|
||||
{
|
||||
std::cout << "cancelPushListen: " << key << " clientId:" << clientId << std::endl;
|
||||
std::lock_guard<std::mutex> lock(lockListener_);
|
||||
auto pushListener = pushListeners_.find(pushToken);
|
||||
if (pushListener == pushListeners_.end())
|
||||
return;
|
||||
auto listeners = pushListener->second.listeners.find(key);
|
||||
if (listeners == pushListener->second.listeners.end())
|
||||
return;
|
||||
for (auto listener = listeners->second.begin(); listener != listeners->second.end();) {
|
||||
if (listener->clientId == clientId) {
|
||||
if (dht_)
|
||||
dht_->cancelListen(key, std::move(listener->internalToken));
|
||||
listener = listeners->second.erase(listener);
|
||||
} else {
|
||||
++listener;
|
||||
}
|
||||
}
|
||||
if (listeners->second.empty()) {
|
||||
pushListener->second.listeners.erase(listeners);
|
||||
}
|
||||
if (pushListener->second.listeners.empty()) {
|
||||
pushListeners_.erase(pushListener);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::sendPushNotification(const std::string& token, const Json::Value& json, bool isAndroid) const
|
||||
{
|
||||
restbed::Uri uri(proxy::HTTP_PROTO + pushServer_ + "/api/push");
|
||||
auto req = std::make_shared<restbed::Request>(uri);
|
||||
req->set_method("POST");
|
||||
|
||||
// NOTE: see https://github.com/appleboy/gorush
|
||||
Json::Value notification(Json::objectValue);
|
||||
Json::Value tokens(Json::arrayValue);
|
||||
tokens[0] = token;
|
||||
notification["tokens"] = tokens;
|
||||
notification["platform"] = isAndroid ? 2 : 1;
|
||||
notification["data"] = json;
|
||||
notification["priority"] = "high";
|
||||
notification["time_to_live"] = 600;
|
||||
|
||||
Json::Value notifications(Json::arrayValue);
|
||||
notifications[0] = notification;
|
||||
|
||||
Json::Value content;
|
||||
content["notifications"] = notifications;
|
||||
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["commentStyle"] = "None";
|
||||
wbuilder["indentation"] = "";
|
||||
auto valueStr = Json::writeString(wbuilder, content);
|
||||
|
||||
req->set_header("Content-Type", "application/json");
|
||||
req->set_header("Accept", "*/*");
|
||||
req->set_header("Host", pushServer_);
|
||||
req->set_header("Content-Length", std::to_string(valueStr.length()));
|
||||
req->set_body(valueStr);
|
||||
|
||||
// Send request.
|
||||
restbed::Http::async(req, {});
|
||||
}
|
||||
|
||||
#endif //OPENDHT_PUSH_NOTIFICATIONS
|
||||
|
||||
void
|
||||
DhtProxyServer::cancelPut(const InfoHash& key, Value::Id vid)
|
||||
{
|
||||
std::cout << "cancelPut " << key << " " << vid << std::endl;
|
||||
auto sPuts = puts_.find(key);
|
||||
if (sPuts == puts_.end())
|
||||
return;
|
||||
auto& sPutsMap = sPuts->second.puts;
|
||||
auto put = sPutsMap.find(vid);
|
||||
if (put == sPutsMap.end())
|
||||
return;
|
||||
if (dht_)
|
||||
dht_->cancelPut(key, vid);
|
||||
if (put->second.expireNotifyJob)
|
||||
put->second.expireNotifyJob->cancel();
|
||||
sPutsMap.erase(put);
|
||||
if (sPutsMap.empty())
|
||||
puts_.erase(sPuts);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::put(const std::shared_ptr<restbed::Session>& session)
|
||||
{
|
||||
requestNum_++;
|
||||
const auto request = session->get_request();
|
||||
int content_length = std::stoi(request->get_header("Content-Length", "0"));
|
||||
auto hash = request->get_path_parameter("hash");
|
||||
InfoHash infoHash(hash);
|
||||
if (!infoHash)
|
||||
infoHash = InfoHash::get(hash);
|
||||
|
||||
session->fetch(content_length,
|
||||
[=](const std::shared_ptr<restbed::Session> s, const restbed::Bytes& b)
|
||||
{
|
||||
try {
|
||||
if (dht_) {
|
||||
if(b.empty()) {
|
||||
std::string response("{\"err\":\"Missing parameters\"}");
|
||||
s->close(restbed::BAD_REQUEST, response);
|
||||
} else {
|
||||
std::string err;
|
||||
Json::Value root;
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto* char_data = reinterpret_cast<const char*>(b.data());
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
if (reader->parse(char_data, char_data + b.size(), &root, &err)) {
|
||||
// Build the Value from json
|
||||
auto value = std::make_shared<Value>(root);
|
||||
bool permanent = root.isMember("permanent");
|
||||
std::cout << "Got put " << infoHash << " " << *value << " " << (permanent ? "permanent" : "") << std::endl;
|
||||
|
||||
if (permanent) {
|
||||
std::string pushToken, clientId, platform;
|
||||
auto& pVal = root["permanent"];
|
||||
if (pVal.isObject()) {
|
||||
pushToken = pVal["key"].asString();
|
||||
clientId = pVal["client_id"].asString();
|
||||
platform = pVal["platform"].asString();
|
||||
}
|
||||
bool isAndroid = platform == "android";
|
||||
std::unique_lock<std::mutex> lock(schedulerLock_);
|
||||
scheduler_.syncTime();
|
||||
auto timeout = scheduler_.time() + proxy::OP_TIMEOUT;
|
||||
auto vid = value->id;
|
||||
auto sPuts = puts_.emplace(infoHash, SearchPuts{}).first;
|
||||
auto r = sPuts->second.puts.emplace(vid, PermanentPut{});
|
||||
auto& pput = r.first->second;
|
||||
if (r.second) {
|
||||
pput.expireJob = scheduler_.add(timeout, [this, infoHash, vid]{
|
||||
std::cout << "Permanent put expired: " << infoHash << " " << vid << std::endl;
|
||||
cancelPut(infoHash, vid);
|
||||
});
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
if (not pushToken.empty()) {
|
||||
pput.expireNotifyJob = scheduler_.add(timeout - proxy::OP_MARGIN,
|
||||
[this, infoHash, vid, pushToken, clientId, isAndroid]
|
||||
{
|
||||
std::cout << "Permanent put refresh: " << infoHash << " " << vid << std::endl;
|
||||
Json::Value json;
|
||||
json["timeout"] = infoHash.toString();
|
||||
json["to"] = clientId;
|
||||
json["vid"] = std::to_string(vid);
|
||||
sendPushNotification(pushToken, json, isAndroid);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
scheduler_.edit(pput.expireJob, timeout);
|
||||
if (pput.expireNotifyJob)
|
||||
scheduler_.edit(pput.expireNotifyJob, timeout - proxy::OP_MARGIN);
|
||||
}
|
||||
lock.unlock();
|
||||
schedulerCv_.notify_one();
|
||||
}
|
||||
|
||||
dht_->put(infoHash, value, [s, value](bool ok) {
|
||||
if (ok) {
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["commentStyle"] = "None";
|
||||
wbuilder["indentation"] = "";
|
||||
if (s->is_open())
|
||||
s->close(restbed::OK, Json::writeString(wbuilder, value->toJson()) + "\n");
|
||||
} else {
|
||||
if (s->is_open())
|
||||
s->close(restbed::BAD_GATEWAY, "{\"err\":\"put failed\"}");
|
||||
}
|
||||
}, time_point::max(), permanent);
|
||||
} else {
|
||||
s->close(restbed::BAD_REQUEST, "{\"err\":\"Incorrect JSON\"}");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}");
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << "Error performing put: " << e.what() << std::endl;
|
||||
s->close(restbed::INTERNAL_SERVER_ERROR, "{\"err\":\"Internal server error\"}");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#if OPENDHT_PROXY_SERVER_IDENTITY
|
||||
void
|
||||
DhtProxyServer::putSigned(const std::shared_ptr<restbed::Session>& session) const
|
||||
{
|
||||
requestNum_++;
|
||||
const auto request = session->get_request();
|
||||
int content_length = std::stoi(request->get_header("Content-Length", "0"));
|
||||
auto hash = request->get_path_parameter("hash");
|
||||
InfoHash infoHash(hash);
|
||||
if (!infoHash)
|
||||
infoHash = InfoHash::get(hash);
|
||||
|
||||
session->fetch(content_length,
|
||||
[=](const std::shared_ptr<restbed::Session> s, const restbed::Bytes& b)
|
||||
{
|
||||
try {
|
||||
if (dht_) {
|
||||
if(b.empty()) {
|
||||
std::string response("{\"err\":\"Missing parameters\"}");
|
||||
s->close(restbed::BAD_REQUEST, response);
|
||||
} else {
|
||||
std::string err;
|
||||
Json::Value root;
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto* char_data = reinterpret_cast<const char*>(b.data());
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
if (reader->parse(char_data, char_data + b.size(), &root, &err)) {
|
||||
auto value = std::make_shared<Value>(root);
|
||||
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["commentStyle"] = "None";
|
||||
wbuilder["indentation"] = "";
|
||||
auto output = Json::writeString(wbuilder, value->toJson()) + "\n";
|
||||
dht_->putSigned(infoHash, value);
|
||||
s->close(restbed::OK, output);
|
||||
} else {
|
||||
s->close(restbed::BAD_REQUEST, "{\"err\":\"Incorrect JSON\"}");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}");
|
||||
}
|
||||
} catch (...) {
|
||||
s->close(restbed::INTERNAL_SERVER_ERROR, "{\"err\":\"Internal server error\"}");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::putEncrypted(const std::shared_ptr<restbed::Session>& session) const
|
||||
{
|
||||
requestNum_++;
|
||||
const auto request = session->get_request();
|
||||
int content_length = std::stoi(request->get_header("Content-Length", "0"));
|
||||
auto hash = request->get_path_parameter("hash");
|
||||
InfoHash key(hash);
|
||||
if (!key)
|
||||
key = InfoHash::get(hash);
|
||||
|
||||
session->fetch(content_length,
|
||||
[=](const std::shared_ptr<restbed::Session> s, const restbed::Bytes& b)
|
||||
{
|
||||
try {
|
||||
if (dht_) {
|
||||
if(b.empty()) {
|
||||
std::string response("{\"err\":\"Missing parameters\"}");
|
||||
s->close(restbed::BAD_REQUEST, response);
|
||||
} else {
|
||||
std::string err;
|
||||
Json::Value root;
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto* char_data = reinterpret_cast<const char*>(b.data());
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
bool parsingSuccessful = reader->parse(char_data, char_data + b.size(), &root, &err);
|
||||
InfoHash to(root["to"].asString());
|
||||
if (parsingSuccessful && to) {
|
||||
auto value = std::make_shared<Value>(root);
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["commentStyle"] = "None";
|
||||
wbuilder["indentation"] = "";
|
||||
auto output = Json::writeString(wbuilder, value->toJson()) + "\n";
|
||||
dht_->putEncrypted(key, to, value);
|
||||
s->close(restbed::OK, output);
|
||||
} else {
|
||||
if(!parsingSuccessful)
|
||||
s->close(restbed::BAD_REQUEST, "{\"err\":\"Incorrect JSON\"}");
|
||||
else
|
||||
s->close(restbed::BAD_REQUEST, "{\"err\":\"No destination found\"}");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}");
|
||||
}
|
||||
} catch (...) {
|
||||
s->close(restbed::INTERNAL_SERVER_ERROR, "{\"err\":\"Internal server error\"}");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
#endif // OPENDHT_PROXY_SERVER_IDENTITY
|
||||
|
||||
void
|
||||
DhtProxyServer::handleOptionsMethod(const std::shared_ptr<restbed::Session>& session) const
|
||||
{
|
||||
requestNum_++;
|
||||
#if OPENDHT_PROXY_SERVER_IDENTITY
|
||||
const auto allowed = "OPTIONS, GET, POST, LISTEN, SIGN, ENCRYPT";
|
||||
#else
|
||||
const auto allowed = "OPTIONS, GET, POST, LISTEN";
|
||||
#endif //OPENDHT_PROXY_SERVER_IDENTITY
|
||||
session->close(restbed::OK, {{"Access-Control-Allow-Methods", allowed},
|
||||
{"Access-Control-Allow-Headers", "content-type"},
|
||||
{"Access-Control-Max-Age", "86400"}});
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::getFiltered(const std::shared_ptr<restbed::Session>& session) const
|
||||
{
|
||||
requestNum_++;
|
||||
const auto request = session->get_request();
|
||||
int content_length = std::stoi(request->get_header("Content-Length", "0"));
|
||||
auto hash = request->get_path_parameter("hash");
|
||||
auto value = request->get_path_parameter("value");
|
||||
session->fetch(content_length,
|
||||
[=](const std::shared_ptr<restbed::Session> s, const restbed::Bytes& /*b* */)
|
||||
{
|
||||
try {
|
||||
if (dht_) {
|
||||
InfoHash infoHash(hash);
|
||||
if (!infoHash) {
|
||||
infoHash = InfoHash::get(hash);
|
||||
}
|
||||
s->yield(restbed::OK, "", [=]( const std::shared_ptr< restbed::Session > s) {
|
||||
dht_->get(infoHash, [s](std::shared_ptr<Value> v) {
|
||||
// Send values as soon as we get them
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["commentStyle"] = "None";
|
||||
wbuilder["indentation"] = "";
|
||||
auto output = Json::writeString(wbuilder, v->toJson()) + "\n";
|
||||
s->yield(output, [](const std::shared_ptr<restbed::Session> /*session*/){ });
|
||||
return true;
|
||||
}, [s](bool /*ok* */) {
|
||||
// Communication is finished
|
||||
s->close();
|
||||
}, {}, value);
|
||||
});
|
||||
} else {
|
||||
s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}");
|
||||
}
|
||||
} catch (...) {
|
||||
s->close(restbed::INTERNAL_SERVER_ERROR, "{\"err\":\"Internal server error\"}");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::removeClosedListeners(bool testSession)
|
||||
{
|
||||
// clean useless listeners
|
||||
std::lock_guard<std::mutex> lock(lockListener_);
|
||||
auto listener = currentListeners_.begin();
|
||||
while (listener != currentListeners_.end()) {
|
||||
auto cancel = dht_ and (not testSession or listener->session->is_closed());
|
||||
if (cancel) {
|
||||
dht_->cancelListen(listener->hash, std::move(listener->token));
|
||||
// Remove listener if unused
|
||||
listener = currentListeners_.erase(listener);
|
||||
} else {
|
||||
++listener;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
+147
-578
File diff suppressed because it is too large.
Load diff
+96
-1
@@ -17,6 +17,11 @@
|
||||
*/
|
||||
|
||||
#include "infohash.h"
|
||||
#include "rng.h"
|
||||
|
||||
extern "C" {
|
||||
#include <gnutls/gnutls.h>
|
||||
}
|
||||
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
@@ -24,7 +29,97 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
const HexMap hex_map = {};
|
||||
InfoHash::InfoHash(const std::string& hex) {
|
||||
if (hex.size() < 2*HASH_LEN) {
|
||||
fill(0);
|
||||
return;
|
||||
}
|
||||
const auto p = (const char*)hex.data();
|
||||
for (size_t i = 0; i < HASH_LEN; i++) {
|
||||
unsigned res = 0;
|
||||
sscanf(p + 2*i, "%02x", &res);
|
||||
(*this)[i] = res;
|
||||
}
|
||||
}
|
||||
|
||||
InfoHash
|
||||
InfoHash::get(const uint8_t* data, size_t data_len)
|
||||
{
|
||||
InfoHash h;
|
||||
size_t s = h.size();
|
||||
const gnutls_datum_t gnudata = {(uint8_t*)data, (unsigned)data_len};
|
||||
const gnutls_digest_algorithm_t algo = (HASH_LEN == 64) ? GNUTLS_DIG_SHA512 : (
|
||||
(HASH_LEN == 32) ? GNUTLS_DIG_SHA256 : (
|
||||
(HASH_LEN == 20) ? GNUTLS_DIG_SHA1 :
|
||||
GNUTLS_DIG_NULL ));
|
||||
static_assert(algo != GNUTLS_DIG_NULL, "Can't find hash function to use.");
|
||||
int rc = gnutls_fingerprint(algo, &gnudata, h.data(), &s);
|
||||
if (rc == 0 && s == HASH_LEN)
|
||||
return h;
|
||||
throw std::runtime_error(std::string("Error hashing: ") + gnutls_strerror(rc));
|
||||
}
|
||||
|
||||
InfoHash
|
||||
InfoHash::getRandom()
|
||||
{
|
||||
InfoHash h;
|
||||
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());
|
||||
std::generate(a, b, std::bind(rand_int, std::ref(rdev)));
|
||||
return h;
|
||||
}
|
||||
|
||||
struct HexMap : public std::array<std::array<char, 2>, 256> {
|
||||
HexMap() {
|
||||
for (size_t i=0; i<size(); i++) {
|
||||
auto& e = (*this)[i];
|
||||
e[0] = hex_digits[(i >> 4) & 0x0F];
|
||||
e[1] = hex_digits[i & 0x0F];
|
||||
}
|
||||
}
|
||||
private:
|
||||
static constexpr const char* hex_digits = "0123456789abcdef";
|
||||
};
|
||||
|
||||
const char*
|
||||
InfoHash::to_c_str() const
|
||||
{
|
||||
static const HexMap map;
|
||||
thread_local std::array<char, HASH_LEN*2+1> buf;
|
||||
for (size_t i=0; i<HASH_LEN; i++) {
|
||||
auto b = buf.data()+i*2;
|
||||
const auto& m = map[(*this)[i]];
|
||||
*((uint16_t*)b) = *((uint16_t*)&m);
|
||||
}
|
||||
return buf.data();
|
||||
}
|
||||
|
||||
std::string
|
||||
InfoHash::toString() const
|
||||
{
|
||||
return std::string(to_c_str(), HASH_LEN*2);
|
||||
}
|
||||
|
||||
std::ostream& operator<< (std::ostream& s, const InfoHash& h)
|
||||
{
|
||||
s.write(h.to_c_str(), HASH_LEN*2);
|
||||
return s;
|
||||
}
|
||||
|
||||
std::istream& operator>> (std::istream& s, InfoHash& h)
|
||||
{
|
||||
std::array<char, HASH_LEN*2> dat;
|
||||
s.exceptions(std::istream::eofbit | std::istream::failbit);
|
||||
s.read(&(*dat.begin()), dat.size());
|
||||
for (size_t i = 0; i < HASH_LEN; i++) {
|
||||
unsigned res = 0;
|
||||
sscanf(dat.data() + 2*i, "%02x", &res);
|
||||
h[i] = res;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
NodeExport::msgpack_unpack(msgpack::object o)
|
||||
|
||||
+5
-11
@@ -20,7 +20,6 @@
|
||||
|
||||
#include "value.h"
|
||||
#include "utils.h"
|
||||
#include "callbacks.h"
|
||||
|
||||
namespace dht {
|
||||
|
||||
@@ -28,12 +27,14 @@ namespace dht {
|
||||
* Foreign nodes asking for updates about an InfoHash.
|
||||
*/
|
||||
struct Listener {
|
||||
size_t sid;
|
||||
time_point time;
|
||||
Query query;
|
||||
|
||||
Listener(time_point t, Query&& q) : time(t), query(std::move(q)) {}
|
||||
Listener(size_t sid, time_point t, Query&& q) : sid(sid), time(t), query(std::move(q)) {}
|
||||
|
||||
void refresh(time_point t, Query&& q) {
|
||||
void refresh(size_t s, time_point t, Query&& q) {
|
||||
sid = s;
|
||||
time = t;
|
||||
query = std::move(q);
|
||||
}
|
||||
@@ -45,14 +46,7 @@ struct Listener {
|
||||
struct LocalListener {
|
||||
Sp<Query> query;
|
||||
Value::Filter filter;
|
||||
ValueCallback get_cb;
|
||||
};
|
||||
|
||||
|
||||
struct SearchListener {
|
||||
Sp<Query> query;
|
||||
Value::Filter filter;
|
||||
ValueCallback get_cb;
|
||||
GetCallback get_cb;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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
|
||||
* 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
|
||||
|
||||
namespace dht {
|
||||
namespace net {
|
||||
|
||||
enum class MessageType {
|
||||
Error = 0,
|
||||
Reply,
|
||||
Ping,
|
||||
FindNode,
|
||||
GetValues,
|
||||
AnnounceValue,
|
||||
Refresh,
|
||||
Listen,
|
||||
ValueData,
|
||||
ValueUpdate
|
||||
};
|
||||
|
||||
} /* namespace net */
|
||||
} /* dht */
|
||||
+368
-325
File diff suppressed because it is too large.
Load diff
+15
-12
@@ -30,11 +30,12 @@ constexpr std::chrono::minutes Node::NODE_EXPIRE_TIME;
|
||||
constexpr std::chrono::minutes Node::NODE_GOOD_TIME;
|
||||
constexpr std::chrono::seconds Node::MAX_RESPONSE_TIME;
|
||||
|
||||
Node::Node(const InfoHash& id, const SockAddr& addr, bool client)
|
||||
: id(id), addr(addr), is_client(client), sockets_()
|
||||
|
||||
Node::Node(const InfoHash& id, const SockAddr& addr, const Sp<TcpSocket>& s, bool client)
|
||||
: id(id), addr(addr), sock(s), is_client(client), sockets_()
|
||||
{
|
||||
thread_local crypto::random_device rd {};
|
||||
transaction_id = std::uniform_int_distribution<Tid>{1}(rd);
|
||||
crypto::random_device rd;
|
||||
transaction_id = std::uniform_int_distribution<SocketId>{1}(rd);
|
||||
}
|
||||
|
||||
/* This is our definition of a known-good node. */
|
||||
@@ -68,9 +69,11 @@ Node::getPendingMessageCount() const
|
||||
}
|
||||
|
||||
void
|
||||
Node::update(const SockAddr& new_addr)
|
||||
Node::update(const SockAddr& new_addr, const Sp<TcpSocket>& s)
|
||||
{
|
||||
addr = new_addr;
|
||||
if (not sock or (s and sock->isClosed() and not s->isClosed()))
|
||||
sock = s;
|
||||
}
|
||||
|
||||
/** To be called when a message was sent to the node */
|
||||
@@ -100,7 +103,7 @@ Node::received(time_point now, const Sp<net::Request>& req)
|
||||
}
|
||||
|
||||
Sp<net::Request>
|
||||
Node::getRequest(Tid tid)
|
||||
Node::getRequest(const net::TransId& tid)
|
||||
{
|
||||
auto it = requests_.find(tid);
|
||||
return it != requests_.end() ? it->second : nullptr;
|
||||
@@ -127,28 +130,28 @@ Node::setExpired()
|
||||
sockets_.clear();
|
||||
}
|
||||
|
||||
Tid
|
||||
SocketId
|
||||
Node::openSocket(SocketCb&& cb)
|
||||
{
|
||||
if (++transaction_id == 0)
|
||||
transaction_id = 1;
|
||||
|
||||
auto sock = std::make_shared<Socket>(std::move(cb));
|
||||
auto sock = Socket(std::move(cb));
|
||||
auto s = sockets_.emplace(transaction_id, std::move(sock));
|
||||
if (not s.second)
|
||||
s.first->second = std::move(sock);
|
||||
return transaction_id;
|
||||
}
|
||||
|
||||
Sp<Socket>
|
||||
Node::getSocket(Tid id)
|
||||
Socket*
|
||||
Node::getSocket(SocketId id)
|
||||
{
|
||||
auto it = sockets_.find(id);
|
||||
return it == sockets_.end() ? nullptr : it->second;
|
||||
return it == sockets_.end() ? nullptr : &it->second;
|
||||
}
|
||||
|
||||
void
|
||||
Node::closeSocket(Tid id)
|
||||
Node::closeSocket(SocketId id)
|
||||
{
|
||||
if (id) {
|
||||
sockets_.erase(id);
|
||||
|
||||
+39
-52
@@ -20,57 +20,44 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
constexpr size_t CLEANUP_MAX_NODES {1024};
|
||||
constexpr size_t CLEANUP_FREQ {1024};
|
||||
|
||||
NodeCache::~NodeCache()
|
||||
{
|
||||
cache_4.setExpired();
|
||||
cache_6.setExpired();
|
||||
}
|
||||
|
||||
Sp<Node>
|
||||
NodeCache::getNode(const InfoHash& id, sa_family_t family) {
|
||||
return cache(family).getNode(id);
|
||||
}
|
||||
|
||||
Sp<Node>
|
||||
NodeCache::getNode(const InfoHash& id, const SockAddr& addr, time_point now, bool confirm, bool client) {
|
||||
if (not id)
|
||||
NodeCache::getNode(const InfoHash& id, const SockAddr& addr, const Sp<TcpSocket>& sock, time_point now, bool confirm, bool client) {
|
||||
if (id == zeroes)
|
||||
return std::make_shared<Node>(id, addr);
|
||||
return cache(addr.getFamily()).getNode(id, addr, now, confirm, client);
|
||||
return cache(addr.getFamily()).getNode(id, addr, sock, now, confirm, client);
|
||||
}
|
||||
|
||||
std::vector<Sp<Node>>
|
||||
NodeCache::getCachedNodes(const InfoHash& id, sa_family_t sa_f, size_t count) const
|
||||
{
|
||||
return cache(sa_f).getCachedNodes(id, count);
|
||||
}
|
||||
const auto& c = cache(sa_f);
|
||||
|
||||
std::vector<Sp<Node>>
|
||||
NodeCache::NodeMap::getCachedNodes(const InfoHash& id, size_t count) const
|
||||
{
|
||||
std::vector<Sp<Node>> nodes;
|
||||
nodes.reserve(std::min(size(), count));
|
||||
const_iterator it;
|
||||
auto dec_it = [this](const_iterator& it) {
|
||||
nodes.reserve(std::min(c.size(), count));
|
||||
NodeMap::const_iterator it;
|
||||
auto dec_it = [&c](NodeMap::const_iterator& it) {
|
||||
auto ret = it;
|
||||
it = (it == cbegin()) ? cend() : std::prev(it);
|
||||
it = (it == c.cbegin()) ? c.cend() : std::prev(it);
|
||||
return ret;
|
||||
};
|
||||
|
||||
auto it_p = lower_bound(id),
|
||||
auto it_p = c.lower_bound(id),
|
||||
it_n = it_p;
|
||||
if (not empty())
|
||||
if (not c.empty())
|
||||
dec_it(it_p); /* Create 2 separate iterator if we could */
|
||||
|
||||
while (nodes.size() < count and (it_n != cend() or it_p != cend())) {
|
||||
while (nodes.size() < count and (it_n != c.cend() or it_p != c.cend())) {
|
||||
/* If one of the iterator is at the end, then take the other one
|
||||
If they are both in middle of somewhere comapre both and take
|
||||
the closest to the id. */
|
||||
if (it_p == cend()) it = it_n++;
|
||||
else if (it_n == cend()) it = dec_it(it_p);
|
||||
else it = id.xorCmp(it_p->first, it_n->first) < 0 ? dec_it(it_p) : it_n++;
|
||||
if (it_p == c.cend()) it = it_n++;
|
||||
else if (it_n == c.cend()) it = dec_it(it_p);
|
||||
else it = id.xorCmp(it_p->first, it_n->first) < 0 ? dec_it(it_p) : it_n++;
|
||||
|
||||
if (auto n = it->second.lock())
|
||||
if ( not n->isExpired() and not n->isClient() )
|
||||
@@ -91,6 +78,17 @@ NodeCache::clearBadNodes(sa_family_t family)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NodeCache::closeAll(sa_family_t family)
|
||||
{
|
||||
if (family == 0) {
|
||||
closeAll(AF_INET);
|
||||
closeAll(AF_INET6);
|
||||
} else {
|
||||
(family == AF_INET ? cache_4 : cache_6).closeAll();
|
||||
}
|
||||
}
|
||||
|
||||
Sp<Node>
|
||||
NodeCache::NodeMap::getNode(const InfoHash& id)
|
||||
{
|
||||
@@ -104,19 +102,15 @@ NodeCache::NodeMap::getNode(const InfoHash& id)
|
||||
}
|
||||
|
||||
Sp<Node>
|
||||
NodeCache::NodeMap::getNode(const InfoHash& id, const SockAddr& addr, time_point now, bool confirm, bool client)
|
||||
NodeCache::NodeMap::getNode(const InfoHash& id, const SockAddr& addr, const Sp<TcpSocket>& sock, time_point now, bool confirm, bool client)
|
||||
{
|
||||
auto it = emplace(id, std::weak_ptr<Node>{});
|
||||
auto node = it.first->second.lock();
|
||||
if (not node) {
|
||||
node = std::make_shared<Node>(id, addr, client);
|
||||
node = std::make_shared<Node>(id, addr, sock, client);
|
||||
it.first->second = node;
|
||||
if (cleanup_counter++ == CLEANUP_FREQ) {
|
||||
cleanup();
|
||||
cleanup_counter = 0;
|
||||
}
|
||||
} else if (confirm or node->isOld(now)) {
|
||||
node->update(addr);
|
||||
node->update(addr, sock);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
@@ -131,30 +125,23 @@ NodeCache::NodeMap::clearBadNodes() {
|
||||
erase(it++);
|
||||
}
|
||||
}
|
||||
cleanup_counter = 0;
|
||||
}
|
||||
|
||||
void
|
||||
NodeCache::NodeMap::setExpired() {
|
||||
for (auto& wn : *this)
|
||||
if (auto n = wn.second.lock())
|
||||
n->setExpired();
|
||||
clear();
|
||||
cleanup_counter = 0;
|
||||
}
|
||||
|
||||
void
|
||||
NodeCache::NodeMap::cleanup()
|
||||
NodeCache::NodeMap::closeAll()
|
||||
{
|
||||
auto it = lower_bound(InfoHash::getRandom());
|
||||
for (size_t n = 0, maxNodes = std::min(size(), CLEANUP_MAX_NODES); n != maxNodes; n++) {
|
||||
if (it == end())
|
||||
it = begin();
|
||||
if (it->second.expired())
|
||||
erase(it++);
|
||||
else
|
||||
for (auto it = cbegin(); it != cend();) {
|
||||
if (auto n = it->second.lock()) {
|
||||
if (n->sock) {
|
||||
n->sock->close();
|
||||
n->sock.reset();
|
||||
}
|
||||
++it;
|
||||
} else {
|
||||
erase(it++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "op_cache.h"
|
||||
|
||||
namespace dht {
|
||||
|
||||
constexpr const std::chrono::seconds OpCache::EXPIRATION;
|
||||
|
||||
bool
|
||||
OpValueCache::onValuesAdded(const std::vector<Sp<Value>>& vals) {
|
||||
std::vector<Sp<Value>> newValues;
|
||||
for (const auto& v : vals) {
|
||||
auto viop = values.emplace(v->id, OpCacheValueStorage{v});
|
||||
if (viop.second) {
|
||||
newValues.emplace_back(v);
|
||||
} else {
|
||||
viop.first->second.refCount++;
|
||||
}
|
||||
}
|
||||
return newValues.empty() ? true : callback(newValues, false);
|
||||
}
|
||||
bool
|
||||
OpValueCache::onValuesExpired(const std::vector<Sp<Value>>& vals) {
|
||||
std::vector<Sp<Value>> expiredValues;
|
||||
for (const auto& v : vals) {
|
||||
auto vit = values.find(v->id);
|
||||
if (vit != values.end()) {
|
||||
vit->second.refCount--;
|
||||
if (not vit->second.refCount) {
|
||||
expiredValues.emplace_back(std::move(vit->second.data));
|
||||
values.erase(vit);
|
||||
}
|
||||
}
|
||||
}
|
||||
return expiredValues.empty() ? true : callback(expiredValues, true);
|
||||
}
|
||||
std::vector<Sp<Value>>
|
||||
OpValueCache::get(Value::Filter& filter) const {
|
||||
std::vector<Sp<Value>> ret;
|
||||
if (not filter)
|
||||
ret.reserve(values.size());
|
||||
for (const auto& v : values)
|
||||
if (not filter or filter(*v.second.data))
|
||||
ret.emplace_back(v.second.data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Sp<Value>
|
||||
OpValueCache::get(Value::Id id) const {
|
||||
auto v = values.find(id);
|
||||
if (v == values.end())
|
||||
return {};
|
||||
return v->second.data;
|
||||
}
|
||||
|
||||
std::vector<Sp<Value>>
|
||||
OpValueCache::getValues() const {
|
||||
std::vector<Sp<Value>> ret;
|
||||
ret.reserve(values.size());
|
||||
for (const auto& v : values)
|
||||
ret.emplace_back(v.second.data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
OpCache::onValuesAdded(const std::vector<Sp<Value>>& vals) {
|
||||
if (not listeners.empty()) {
|
||||
std::vector<LocalListener> list;
|
||||
list.reserve(listeners.size());
|
||||
for (const auto& l : listeners)
|
||||
list.emplace_back(l.second);
|
||||
for (auto& l : list)
|
||||
l.get_cb(l.filter.filter(vals), false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
OpCache::onValuesExpired(const std::vector<Sp<Value>>& vals) {
|
||||
if (not listeners.empty()) {
|
||||
std::vector<LocalListener> list;
|
||||
list.reserve(listeners.size());
|
||||
for (const auto& l : listeners)
|
||||
list.emplace_back(l.second);
|
||||
for (auto& l : list)
|
||||
l.get_cb(l.filter.filter(vals), true);
|
||||
}
|
||||
}
|
||||
|
||||
time_point
|
||||
OpCache::getExpiration() const {
|
||||
if (not listeners.empty())
|
||||
return time_point::max();
|
||||
return lastRemoved + EXPIRATION;
|
||||
}
|
||||
|
||||
size_t
|
||||
SearchCache::listen(ValueCallback get_cb, Sp<Query> q, Value::Filter filter, std::function<size_t(Sp<Query>, ValueCallback)> onListen)
|
||||
{
|
||||
// find exact match
|
||||
auto op = ops.find(q);
|
||||
if (op == ops.end()) {
|
||||
// find satisfying query
|
||||
for (auto it = ops.begin(); it != ops.end(); it++) {
|
||||
if (q->isSatisfiedBy(*it->first)) {
|
||||
op = it;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (op == ops.end()) {
|
||||
// New query
|
||||
op = ops.emplace(q, std::unique_ptr<OpCache>(new OpCache)).first;
|
||||
auto& cache = *op->second;
|
||||
cache.searchToken = onListen(q, [&](const std::vector<Sp<Value>>& values, bool expired){
|
||||
return cache.onValue(values, expired);
|
||||
});
|
||||
}
|
||||
auto token = nextToken_++;
|
||||
if (nextToken_ == 0)
|
||||
nextToken_++;
|
||||
op->second->addListener(token, get_cb, q, filter);
|
||||
return token;
|
||||
}
|
||||
|
||||
bool
|
||||
SearchCache::cancelListen(size_t gtoken, const time_point& now) {
|
||||
for (auto& op : ops) {
|
||||
if (op.second->removeListener(gtoken, now)) {
|
||||
nextExpiration_ = std::min(nextExpiration_, op.second->getExpiration());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
SearchCache::cancelAll(std::function<void(size_t)> onCancel) {
|
||||
for (auto& op : ops) {
|
||||
auto cache = std::move(op.second);
|
||||
cache->removeAll();
|
||||
onCancel(cache->searchToken);
|
||||
}
|
||||
ops.clear();
|
||||
}
|
||||
|
||||
time_point
|
||||
SearchCache::expire(const time_point& now, std::function<void(size_t)> onCancel) {
|
||||
nextExpiration_ = time_point::max();
|
||||
auto ret = nextExpiration_;
|
||||
for (auto it = ops.begin(); it != ops.end();) {
|
||||
auto expiration = it->second->getExpiration();
|
||||
if (expiration < now) {
|
||||
auto cache = std::move(it->second);
|
||||
it = ops.erase(it);
|
||||
onCancel(cache->searchToken);
|
||||
} else {
|
||||
nextExpiration_ = std::min(nextExpiration_, expiration);
|
||||
ret = nextExpiration_;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<Sp<Value>>
|
||||
SearchCache::get(Value::Filter& filter) const {
|
||||
if (ops.size() == 1)
|
||||
return ops.begin()->second->get(filter);
|
||||
std::map<Value::Id, Sp<Value>> c;
|
||||
for (const auto& op : ops) {
|
||||
for (const auto& v : op.second->get(filter))
|
||||
c.emplace(v->id, v);
|
||||
}
|
||||
std::vector<Sp<Value>> ret;
|
||||
ret.reserve(c.size());
|
||||
for (auto& v : c)
|
||||
ret.emplace_back(std::move(v.second));
|
||||
return ret;
|
||||
}
|
||||
|
||||
Sp<Value>
|
||||
SearchCache::get(Value::Id id) const {
|
||||
for (const auto& op : ops)
|
||||
if (auto v = op.second->get(id))
|
||||
return v;
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
-155
@@ -1,155 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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
|
||||
* 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 "value.h"
|
||||
#include "value_cache.h"
|
||||
#include "listener.h"
|
||||
|
||||
namespace dht {
|
||||
|
||||
struct OpCacheValueStorage
|
||||
{
|
||||
Sp<Value> data {};
|
||||
unsigned refCount {1};
|
||||
OpCacheValueStorage(Sp<Value> val = {}) : data(val) {}
|
||||
};
|
||||
|
||||
class OpValueCache {
|
||||
public:
|
||||
OpValueCache(ValueCallback&& cb) : callback(std::forward<ValueCallback>(cb)) {}
|
||||
OpValueCache(OpValueCache&& o) : values(std::move(o.values)), callback(std::move(o.callback)) {
|
||||
o.callback = {};
|
||||
}
|
||||
|
||||
static ValueCallback cacheCallback(ValueCallback&& cb) {
|
||||
auto cache = std::make_shared<OpValueCache>(std::forward<ValueCallback>(cb));
|
||||
return [cache](const std::vector<Sp<Value>>& vals, bool expired){
|
||||
return cache->onValue(vals, expired);
|
||||
};
|
||||
}
|
||||
|
||||
bool onValue(const std::vector<Sp<Value>>& vals, bool expired) {
|
||||
if (expired)
|
||||
return onValuesExpired(vals);
|
||||
else
|
||||
return onValuesAdded(vals);
|
||||
}
|
||||
|
||||
bool onValuesAdded(const std::vector<Sp<Value>>& vals);
|
||||
bool onValuesExpired(const std::vector<Sp<Value>>& vals);
|
||||
|
||||
std::vector<Sp<Value>> get(Value::Filter& filter) const;
|
||||
Sp<Value> get(Value::Id id) const;
|
||||
std::vector<Sp<Value>> getValues() const;
|
||||
|
||||
private:
|
||||
OpValueCache(const OpValueCache&) = delete;
|
||||
OpValueCache& operator=(const OpValueCache&) = delete;
|
||||
|
||||
std::map<Value::Id, OpCacheValueStorage> values {};
|
||||
ValueCallback callback;
|
||||
};
|
||||
|
||||
class OpCache {
|
||||
public:
|
||||
OpCache() : cache([this](const std::vector<Sp<Value>>& vals, bool expired){
|
||||
if (expired)
|
||||
onValuesExpired(vals);
|
||||
else
|
||||
onValuesAdded(vals);
|
||||
return true;
|
||||
}) {}
|
||||
|
||||
bool onValue(const std::vector<Sp<Value>>& vals, bool expired) {
|
||||
cache.onValue(vals, expired);
|
||||
return not listeners.empty();
|
||||
}
|
||||
|
||||
void onValuesAdded(const std::vector<Sp<Value>>& vals);
|
||||
void onValuesExpired(const std::vector<Sp<Value>>& vals);
|
||||
|
||||
void addListener(size_t token, ValueCallback cb, Sp<Query> q, Value::Filter filter) {
|
||||
listeners.emplace(token, LocalListener{q, filter, cb});
|
||||
cb(cache.get(filter), false);
|
||||
}
|
||||
|
||||
bool removeListener(size_t token, const time_point& now) {
|
||||
lastRemoved = now;
|
||||
return listeners.erase(token) > 0;
|
||||
}
|
||||
|
||||
void removeAll() {
|
||||
listeners.clear();
|
||||
}
|
||||
|
||||
bool isDone() {
|
||||
return listeners.empty();
|
||||
}
|
||||
|
||||
std::vector<Sp<Value>> get(Value::Filter& filter) const {
|
||||
return cache.get(filter);
|
||||
}
|
||||
|
||||
Sp<Value> get(Value::Id id) const {
|
||||
return cache.get(id);
|
||||
}
|
||||
|
||||
bool isExpired(const time_point& now) const {
|
||||
return listeners.empty() and (lastRemoved + EXPIRATION < now);
|
||||
}
|
||||
time_point getExpiration() const;
|
||||
|
||||
size_t searchToken;
|
||||
private:
|
||||
constexpr static const std::chrono::seconds EXPIRATION {60};
|
||||
OpCache(const OpCache&) = delete;
|
||||
OpCache& operator=(const OpCache&) = delete;
|
||||
|
||||
OpValueCache cache;
|
||||
std::map<size_t, LocalListener> listeners;
|
||||
time_point lastRemoved {clock::now()};
|
||||
};
|
||||
|
||||
class SearchCache {
|
||||
public:
|
||||
SearchCache() {}
|
||||
SearchCache(SearchCache&&) = default;
|
||||
size_t listen(ValueCallback get_cb, Sp<Query> q, Value::Filter filter, std::function<size_t(Sp<Query>, ValueCallback)> onListen);
|
||||
|
||||
bool cancelListen(size_t gtoken, const time_point& now);
|
||||
void cancelAll(std::function<void(size_t)> onCancel);
|
||||
|
||||
time_point expire(const time_point& now, std::function<void(size_t)> onCancel);
|
||||
time_point getExpiration() const {
|
||||
return nextExpiration_;
|
||||
}
|
||||
|
||||
std::vector<Sp<Value>> get(Value::Filter& filter) const;
|
||||
Sp<Value> get(Value::Id id) const;
|
||||
|
||||
private:
|
||||
SearchCache(const SearchCache&) = delete;
|
||||
SearchCache& operator=(const SearchCache&) = delete;
|
||||
|
||||
std::map<Sp<Query>, std::unique_ptr<OpCache>> ops {};
|
||||
size_t nextToken_ {1};
|
||||
time_point nextExpiration_ {time_point::max()};
|
||||
};
|
||||
|
||||
}
|
||||
+17
-21
@@ -26,14 +26,17 @@
|
||||
namespace dht {
|
||||
namespace net {
|
||||
|
||||
Tid unpackTid(const msgpack::object& o) {
|
||||
switch (o.type) {
|
||||
case msgpack::type::POSITIVE_INTEGER:
|
||||
return o.as<Tid>();
|
||||
default:
|
||||
return ntohl(*reinterpret_cast<const uint32_t*>(o.as<std::array<char, 4>>().data()));
|
||||
}
|
||||
}
|
||||
enum class MessageType {
|
||||
Error = 0,
|
||||
Reply,
|
||||
Ping,
|
||||
FindNode,
|
||||
GetValues,
|
||||
AnnounceValue,
|
||||
Refresh,
|
||||
Listen,
|
||||
ValueData
|
||||
};
|
||||
|
||||
struct ParsedMessage {
|
||||
MessageType type;
|
||||
@@ -48,9 +51,9 @@ struct ParsedMessage {
|
||||
/* target id around which to find nodes */
|
||||
InfoHash target;
|
||||
/* transaction id */
|
||||
Tid tid;
|
||||
TransId tid;
|
||||
/* tid for packets going through request socket */
|
||||
Tid socket_id;
|
||||
TransId socket_id;
|
||||
/* security token */
|
||||
Blob token;
|
||||
/* the value id (announce confirmation) */
|
||||
@@ -62,8 +65,6 @@ struct ParsedMessage {
|
||||
std::vector<Sp<Node>> nodes4, nodes6;
|
||||
/* values to store or retreive request */
|
||||
std::vector<Sp<Value>> values;
|
||||
std::vector<Value::Id> refreshed_values {};
|
||||
std::vector<Value::Id> expired_values {};
|
||||
/* index for fields values */
|
||||
std::vector<Sp<FieldValueIndex>> fields;
|
||||
/** When part of the message header: {index -> (total size, {})}
|
||||
@@ -127,7 +128,6 @@ ParsedMessage::msgpack_unpack(msgpack::object msg)
|
||||
{
|
||||
auto y = findMapValue(msg, "y");
|
||||
auto r = findMapValue(msg, "r");
|
||||
auto u = findMapValue(msg, "u");
|
||||
auto e = findMapValue(msg, "e");
|
||||
auto v = findMapValue(msg, "p");
|
||||
|
||||
@@ -156,8 +156,6 @@ ParsedMessage::msgpack_unpack(msgpack::object msg)
|
||||
type = MessageType::Reply;
|
||||
else if (v)
|
||||
type = MessageType::ValueData;
|
||||
else if (u)
|
||||
type = MessageType::ValueUpdate;
|
||||
else if (y and y->as<std::string>() != "q")
|
||||
throw msgpack::type_error();
|
||||
else if (q == "ping")
|
||||
@@ -190,9 +188,9 @@ ParsedMessage::msgpack_unpack(msgpack::object msg)
|
||||
}
|
||||
|
||||
auto a = findMapValue(msg, "a");
|
||||
if (!a && !r && !e && !u)
|
||||
if (!a && !r && !e)
|
||||
throw msgpack::type_error();
|
||||
auto& req = a ? *a : (r ? *r : (u ? *u : *e));
|
||||
auto& req = a ? *a : (r ? *r : *e);
|
||||
|
||||
if (e) {
|
||||
if (e->type != msgpack::type::ARRAY)
|
||||
@@ -234,11 +232,13 @@ ParsedMessage::msgpack_unpack(msgpack::object msg)
|
||||
if (l == sizeof(in_addr)) {
|
||||
addr.setFamily(AF_INET);
|
||||
auto& a = addr.getIPv4();
|
||||
std::fill_n((uint8_t*)&a, sizeof(a), 0);
|
||||
a.sin_port = 0;
|
||||
std::copy_n(sa->via.bin.ptr, l, (char*)&a.sin_addr);
|
||||
} else if (l == sizeof(in6_addr)) {
|
||||
addr.setFamily(AF_INET6);
|
||||
auto& a = addr.getIPv6();
|
||||
std::fill_n((uint8_t*)&a, sizeof(a), 0);
|
||||
a.sin6_port = 0;
|
||||
std::copy_n(sa->via.bin.ptr, l, (char*)&a.sin6_addr);
|
||||
}
|
||||
@@ -284,10 +284,6 @@ ParsedMessage::msgpack_unpack(msgpack::object msg)
|
||||
} else {
|
||||
throw msgpack::type_error();
|
||||
}
|
||||
} else if (auto raw_fields = findMapValue(req, "exp")) {
|
||||
expired_values = raw_fields->as<decltype(expired_values)>();
|
||||
} else if (auto raw_fields = findMapValue(req, "re")) {
|
||||
refreshed_values = raw_fields->as<decltype(refreshed_values)>();
|
||||
}
|
||||
|
||||
if (auto w = findMapValue(req, "w")) {
|
||||
|
||||
+8
-19
@@ -57,30 +57,20 @@ struct Request {
|
||||
bool pending() const { return state_ == State::PENDING; }
|
||||
bool over() const { return not pending(); }
|
||||
State getState() const { return state_; }
|
||||
char getStateChar() const {
|
||||
switch (state_) {
|
||||
case State::PENDING: return 'f';
|
||||
case State::CANCELLED: return 'c';
|
||||
case State::EXPIRED: return 'e';
|
||||
case State::COMPLETED: return 'a';
|
||||
default: return '?';
|
||||
}
|
||||
}
|
||||
|
||||
Request(State state = State::PENDING) : state_(state) {}
|
||||
Request(MessageType type, Tid tid,
|
||||
Request(const TransId& tid,
|
||||
Sp<Node> node,
|
||||
Blob&& msg,
|
||||
std::function<void(const Request&, ParsedMessage&&)> on_done,
|
||||
std::function<void(const Request&, bool)> on_expired,
|
||||
Tid socket = 0) :
|
||||
node(node), tid(tid), type(type), on_done(on_done), on_expired(on_expired), msg(std::move(msg)), socket(socket) { }
|
||||
uint32_t socket = 0) :
|
||||
node(node), on_done(on_done), on_expired(on_expired), tid(tid), msg(std::move(msg)), socket(socket) { }
|
||||
|
||||
Tid getTid() const { return tid; }
|
||||
MessageType getType() const { return type; }
|
||||
TransId getTid() const { return tid; }
|
||||
|
||||
Tid getSocket() { return socket; }
|
||||
Tid closeSocket() { auto ret = socket; socket = 0; return ret; }
|
||||
uint32_t getSocket() { return socket; }
|
||||
uint32_t closeSocket() { auto ret = socket; socket = 0; return ret; }
|
||||
|
||||
void setExpired() {
|
||||
if (pending()) {
|
||||
@@ -117,8 +107,6 @@ private:
|
||||
msg.clear();
|
||||
}
|
||||
|
||||
const Tid tid {0}; /* the request id. */
|
||||
const MessageType type {};
|
||||
State state_ {State::PENDING};
|
||||
|
||||
unsigned attempt_count {0}; /* number of attempt to process the request. */
|
||||
@@ -128,8 +116,9 @@ private:
|
||||
std::function<void(const Request&, ParsedMessage&&)> on_done {};
|
||||
std::function<void(const Request&, bool)> on_expired {};
|
||||
|
||||
const TransId tid; /* the request id. */
|
||||
Blob msg {}; /* the serialized message. */
|
||||
Tid socket; /* the socket used for further reponses. */
|
||||
uint32_t socket; /* the socket used for further reponses. */
|
||||
};
|
||||
|
||||
} /* namespace net */
|
||||
|
||||
@@ -76,7 +76,7 @@ RoutingTable::randomId(const RoutingTable::const_iterator& it) const
|
||||
|
||||
int b = bit/8;
|
||||
InfoHash id_return;
|
||||
std::copy_n(it->first.cbegin(), b, id_return.begin());
|
||||
std::copy_n(it->first.begin(), b, id_return.begin());
|
||||
id_return[b] = it->first[b] & (0xFF00 >> (bit % 8));
|
||||
id_return[b] |= rand_byte(rd) >> (bit % 8);
|
||||
for (unsigned i = b + 1; i < HASH_LEN; i++)
|
||||
|
||||
+168
-132
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2018 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2017 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
|
||||
@@ -21,8 +21,6 @@
|
||||
#include "value.h"
|
||||
#include "request.h"
|
||||
#include "listener.h"
|
||||
#include "value_cache.h"
|
||||
#include "op_cache.h"
|
||||
|
||||
namespace dht {
|
||||
|
||||
@@ -61,17 +59,6 @@ struct Dht::SearchNode {
|
||||
*/
|
||||
using SyncStatus = std::map<Sp<Query>, Sp<net::Request>>;
|
||||
|
||||
struct CachedListenStatus {
|
||||
ValueCache cache;
|
||||
Sp<Scheduler::Job> cacheExpirationJob {};
|
||||
Sp<net::Request> req {};
|
||||
CachedListenStatus(ValueStateCallback&& cb) : cache(std::forward<ValueStateCallback>(cb)) {}
|
||||
CachedListenStatus(CachedListenStatus&&) = default;
|
||||
CachedListenStatus(const CachedListenStatus&) = delete;
|
||||
CachedListenStatus& operator=(const CachedListenStatus&) = delete;
|
||||
};
|
||||
using NodeListenerStatus = std::map<Sp<Query>, CachedListenStatus>;
|
||||
|
||||
Sp<Node> node {}; /* the node info */
|
||||
|
||||
/* queries sent for finding out values hosted by the node */
|
||||
@@ -80,14 +67,13 @@ struct Dht::SearchNode {
|
||||
std::map<Sp<Query>, std::vector<Sp<Query>>> pagination_queries {};
|
||||
|
||||
SyncStatus getStatus {}; /* get/sync status */
|
||||
NodeListenerStatus listenStatus {}; /* listen status */
|
||||
SyncStatus listenStatus {}; /* listen status */
|
||||
AnnounceStatus acked {}; /* announcement status for a given value id */
|
||||
|
||||
Blob token {}; /* last token the node sent to us after a get request */
|
||||
time_point last_get_reply {time_point::min()}; /* last time received valid token */
|
||||
bool candidate {false}; /* A search node is candidate if the search is/was synced and this
|
||||
node is a new candidate for inclusion. */
|
||||
Sp<Scheduler::Job> syncJob {};
|
||||
|
||||
SearchNode() : node() {}
|
||||
SearchNode(const SearchNode&) = delete;
|
||||
@@ -107,17 +93,11 @@ struct Dht::SearchNode {
|
||||
/**
|
||||
* Can we use this node to listen/announce now ?
|
||||
*/
|
||||
bool isSynced(const time_point& now) const {
|
||||
bool isSynced(time_point now) const {
|
||||
return not node->isExpired() and
|
||||
not token.empty() and last_get_reply >= now - Node::NODE_EXPIRE_TIME;
|
||||
}
|
||||
|
||||
time_point getSyncTime(const time_point& now) const {
|
||||
if (node->isExpired() or token.empty())
|
||||
return now;
|
||||
return last_get_reply + Node::NODE_EXPIRE_TIME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Could a particular "get" request be sent to this node now ?
|
||||
*
|
||||
@@ -136,7 +116,7 @@ struct Dht::SearchNode {
|
||||
*
|
||||
* @return true if we can send get, else false.
|
||||
*/
|
||||
bool canGet(time_point now, time_point update, const Sp<Query>& q) const {
|
||||
bool canGet(time_point now, time_point update, Sp<Query> q = {}) const {
|
||||
if (node->isExpired())
|
||||
return false;
|
||||
|
||||
@@ -147,9 +127,9 @@ struct Dht::SearchNode {
|
||||
if (s.second and s.second->pending())
|
||||
pending = true;
|
||||
if (s.first and q and q->isSatisfiedBy(*s.first) and s.second) {
|
||||
if (s.second->pending())
|
||||
if (s.second->pending() and not pending_sq_status)
|
||||
pending_sq_status = true;
|
||||
else if (s.second->completed() and not (update > s.second->reply_time))
|
||||
if (s.second->completed() and not (update > s.second->reply_time) and not completed_sq_status)
|
||||
completed_sq_status = true;
|
||||
if (completed_sq_status and pending_sq_status)
|
||||
break;
|
||||
@@ -157,7 +137,7 @@ struct Dht::SearchNode {
|
||||
}
|
||||
|
||||
return (not pending and now > last_get_reply + Node::NODE_EXPIRE_TIME) or
|
||||
not (completed_sq_status or pending_sq_status or hasStartedPagination(q));
|
||||
not (hasStartedPagination(q) or completed_sq_status or pending_sq_status);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,25 +194,6 @@ struct Dht::SearchNode {
|
||||
getStatus.clear();
|
||||
}
|
||||
|
||||
void onValues(const Sp<Query>& q, net::RequestAnswer&& answer, const TypeStore& types, Scheduler& scheduler)
|
||||
{
|
||||
auto l = listenStatus.find(q);
|
||||
if (l != listenStatus.end()) {
|
||||
auto next = l->second.cache.onValues(answer.values,
|
||||
answer.refreshed_values,
|
||||
answer.expired_values, types, scheduler.time());
|
||||
scheduler.edit(l->second.cacheExpirationJob, next);
|
||||
}
|
||||
}
|
||||
|
||||
void expireValues(const Sp<Query>& q, Scheduler& scheduler) {
|
||||
auto l = listenStatus.find(q);
|
||||
if (l != listenStatus.end()) {
|
||||
auto next = l->second.cache.expireValues(scheduler.time());
|
||||
scheduler.edit(l->second.cacheExpirationJob, next);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if a request in the status map is expired.
|
||||
*
|
||||
@@ -260,12 +221,6 @@ struct Dht::SearchNode {
|
||||
return r.second and r.second->pending();
|
||||
}) != status.end();
|
||||
}
|
||||
static bool pending(const NodeListenerStatus& status) {
|
||||
return std::find_if(status.begin(), status.end(),
|
||||
[](const NodeListenerStatus::value_type& r){
|
||||
return r.second.req and r.second.req->pending();
|
||||
}) != status.end();
|
||||
}
|
||||
|
||||
bool pendingGet() const { return pending(getStatus); }
|
||||
|
||||
@@ -301,48 +256,49 @@ struct Dht::SearchNode {
|
||||
else
|
||||
return isListening(now, ls);
|
||||
}
|
||||
bool isListening(time_point now, NodeListenerStatus::const_iterator listen_status) const {
|
||||
if (listen_status == listenStatus.end() or not listen_status->second.req)
|
||||
bool isListening(time_point now, SyncStatus::const_iterator listen_status) const {
|
||||
if (listen_status == listenStatus.end())
|
||||
return false;
|
||||
return listen_status->second.req->reply_time + LISTEN_EXPIRE_TIME > now;
|
||||
return listen_status->second->reply_time + LISTEN_EXPIRE_TIME > now;
|
||||
}
|
||||
void cancelListen() {
|
||||
for (const auto& status : listenStatus)
|
||||
node->cancelRequest(status.second.req);
|
||||
node->cancelRequest(status.second);
|
||||
listenStatus.clear();
|
||||
}
|
||||
void cancelListen(const Sp<Query>& query) {
|
||||
auto it = listenStatus.find(query);
|
||||
if (it != listenStatus.end()) {
|
||||
node->cancelRequest(it->second.req);
|
||||
node->cancelRequest(it->second);
|
||||
listenStatus.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assuming the node is synced, should a "put" request be sent to this node now ?
|
||||
* Assumng the node is synced, should a "put" request be sent to this node now ?
|
||||
*/
|
||||
time_point getAnnounceTime(Value::Id vid) const {
|
||||
const auto& ack = acked.find(vid);
|
||||
if (ack == acked.cend() or not ack->second.first) {
|
||||
const auto& gs = probe_query ? getStatus.find(probe_query) : getStatus.cend();
|
||||
if ((ack == acked.cend() or not ack->second.first) and (gs == getStatus.cend()
|
||||
or not gs->second or not gs->second->pending()))
|
||||
return time_point::min();
|
||||
}
|
||||
if (ack->second.first->completed()) {
|
||||
return ack->second.second - REANNOUNCE_MARGIN;
|
||||
}
|
||||
return ack->second.first->pending() ? time_point::max() : time_point::min();
|
||||
return ((gs != getStatus.cend() and gs->second and gs->second->pending())
|
||||
or ack == acked.cend() or not ack->second.first or ack->second.first->pending())
|
||||
? time_point::max()
|
||||
: ack->second.second - REANNOUNCE_MARGIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assuming the node is synced, should the "listen" request with Query q be
|
||||
* Assumng the node is synced, should the "listen" request with Query q be
|
||||
* sent to this node now ?
|
||||
*/
|
||||
time_point getListenTime(const Sp<Query>& q) const {
|
||||
auto listen_status = listenStatus.find(q);
|
||||
if (listen_status == listenStatus.end() or not listen_status->second.req)
|
||||
if (listen_status == listenStatus.end() or not listen_status->second)
|
||||
return time_point::min();
|
||||
return listen_status->second.req->pending() ? time_point::max() :
|
||||
listen_status->second.req->reply_time + LISTEN_EXPIRE_TIME - REANNOUNCE_MARGIN;
|
||||
return listen_status->second->pending() ? time_point::max() :
|
||||
listen_status->second->reply_time + LISTEN_EXPIRE_TIME - REANNOUNCE_MARGIN;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,7 +320,7 @@ struct Dht::Search {
|
||||
uint16_t tid;
|
||||
time_point refill_time {time_point::min()};
|
||||
time_point step_time {time_point::min()}; /* the time of the last search step */
|
||||
Sp<Scheduler::Job> nextSearchStep {};
|
||||
Sp<Job> nextSearchStep {};
|
||||
|
||||
bool expired {false}; /* no node, or all nodes expired */
|
||||
bool done {false}; /* search is over, cached for later */
|
||||
@@ -377,16 +333,10 @@ struct Dht::Search {
|
||||
std::multimap<time_point, Get> callbacks {};
|
||||
|
||||
/* listeners */
|
||||
std::map<size_t, SearchListener> listeners {};
|
||||
std::map<size_t, LocalListener> listeners {};
|
||||
size_t listener_token = 1;
|
||||
|
||||
/* Cache */
|
||||
SearchCache cache;
|
||||
Sp<Scheduler::Job> opExpirationJob;
|
||||
|
||||
~Search() {
|
||||
if (opExpirationJob)
|
||||
opExpirationJob->cancel();
|
||||
for (auto& get : callbacks) {
|
||||
get.second.done_cb(false, {});
|
||||
get.second.done_cb = {};
|
||||
@@ -429,8 +379,7 @@ struct Dht::Search {
|
||||
*
|
||||
* @param query The query identifying a 'get' request.
|
||||
*/
|
||||
time_point getLastGetTime(const Query&) const;
|
||||
time_point getLastGetTime() const;
|
||||
time_point getLastGetTime(Sp<Query> query = {}) const;
|
||||
|
||||
/**
|
||||
* Is this get operation done ?
|
||||
@@ -472,52 +421,24 @@ struct Dht::Search {
|
||||
done = true;
|
||||
}
|
||||
|
||||
time_point getUpdateTime(time_point now) const;
|
||||
|
||||
bool isAnnounced(Value::Id id) const;
|
||||
bool isListening(time_point now) const;
|
||||
|
||||
void get(Value::Filter f, const Sp<Query>& q, const QueryCallback& qcb, const GetCallback& gcb, const DoneCallback& dcb, Scheduler& scheduler) {
|
||||
if (gcb or qcb) {
|
||||
const auto& now = scheduler.time();
|
||||
callbacks.emplace(now, Get { now, f, q, qcb, gcb, dcb });
|
||||
auto values = cache.get(f);
|
||||
if (not values.empty())
|
||||
gcb(values);
|
||||
scheduler.edit(nextSearchStep, now);
|
||||
void cancelListen(size_t token) {
|
||||
Sp<Query> query;
|
||||
const auto& ll = listeners.find(token);
|
||||
if (ll != listeners.cend()) {
|
||||
query = ll->second.query;
|
||||
listeners.erase(ll);
|
||||
}
|
||||
for (auto& sn : nodes) {
|
||||
if (listeners.empty())
|
||||
sn.cancelListen();
|
||||
else if (query)
|
||||
sn.cancelListen(query);
|
||||
}
|
||||
}
|
||||
|
||||
size_t listen(ValueCallback cb, Value::Filter f, const Sp<Query>& q, Scheduler& scheduler) {
|
||||
//DHT_LOG.e(id, "[search %s IPv%c] listen", id.toString().c_str(), (af == AF_INET) ? '4' : '6');
|
||||
return cache.listen(cb, q, f, [&](const Sp<Query>& q, ValueCallback vcb){
|
||||
done = false;
|
||||
auto token = ++listener_token;
|
||||
listeners.emplace(token, SearchListener{q, f, vcb});
|
||||
scheduler.edit(nextSearchStep, scheduler.time());
|
||||
return token;
|
||||
});
|
||||
}
|
||||
|
||||
void cancelListen(size_t token, Scheduler& scheduler) {
|
||||
cache.cancelListen(token, scheduler.time());
|
||||
if (not opExpirationJob)
|
||||
opExpirationJob = scheduler.add(time_point::max(), [this,&scheduler]{
|
||||
auto nextExpire = cache.expire(scheduler.time(), [&](size_t t){
|
||||
Sp<Query> query;
|
||||
const auto& ll = listeners.find(t);
|
||||
if (ll != listeners.cend()) {
|
||||
query = ll->second.query;
|
||||
listeners.erase(ll);
|
||||
}
|
||||
for (auto& sn : nodes) {
|
||||
if (listeners.empty())
|
||||
sn.cancelListen();
|
||||
else if (query)
|
||||
sn.cancelListen(query);
|
||||
}
|
||||
});
|
||||
scheduler.edit(opExpirationJob, nextExpire);
|
||||
});
|
||||
scheduler.edit(opExpirationJob, cache.getExpiration());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -539,6 +460,26 @@ struct Dht::Search {
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time of the next "announce" event for this search,
|
||||
* or time_point::max() if no such event is planned.
|
||||
* Only makes sense when the search is synced.
|
||||
*/
|
||||
time_point getAnnounceTime(time_point now) const;
|
||||
|
||||
/**
|
||||
* Returns the time of the next "listen" event for this search,
|
||||
* or time_point::max() if no such event is planned.
|
||||
* Only makes sense when the search is synced.
|
||||
*/
|
||||
time_point getListenTime(time_point now) const;
|
||||
|
||||
/**
|
||||
* Returns the time of the next event for this search,
|
||||
* or time_point::max() if no such event is planned.
|
||||
*/
|
||||
time_point getNextStepTime(time_point now) const;
|
||||
|
||||
/**
|
||||
* Removes a node which have been expired for at least
|
||||
* NODE::NODE_EXPIRE_TIME minutes. The search for an expired node starts
|
||||
@@ -757,20 +698,11 @@ Dht::Search::isSynced(time_point now) const
|
||||
}
|
||||
|
||||
time_point
|
||||
Dht::Search::getLastGetTime(const Query& q) const
|
||||
Dht::Search::getLastGetTime(Sp<Query> q) const
|
||||
{
|
||||
time_point last = time_point::min();
|
||||
for (const auto& g : callbacks)
|
||||
last = std::max(last, (q.isSatisfiedBy(*g.second.query) ? g.second.start : time_point::min()));
|
||||
return last;
|
||||
}
|
||||
|
||||
time_point
|
||||
Dht::Search::getLastGetTime() const
|
||||
{
|
||||
time_point last = time_point::min();
|
||||
for (const auto& g : callbacks)
|
||||
last = std::max(last, g.second.start);
|
||||
last = std::max(last, (not q or q->isSatisfiedBy(*g.second.query) ? g.second.start : time_point::min()));
|
||||
return last;
|
||||
}
|
||||
|
||||
@@ -789,6 +721,36 @@ Dht::Search::isDone(const Get& get) const
|
||||
return true;
|
||||
}
|
||||
|
||||
time_point
|
||||
Dht::Search::getUpdateTime(time_point now) const
|
||||
{
|
||||
time_point ut = time_point::max();
|
||||
const auto last_get = getLastGetTime();
|
||||
unsigned i = 0, t = 0, d = 0;
|
||||
const auto solicited_nodes = currentlySolicitedNodeCount();
|
||||
for (const auto& sn : nodes) {
|
||||
if (sn.node->isExpired() or (sn.candidate and t >= TARGET_NODES))
|
||||
continue;
|
||||
auto pending = sn.pendingGet();
|
||||
if (sn.last_get_reply < std::max(now - Node::NODE_EXPIRE_TIME, last_get) or pending) {
|
||||
// not isSynced
|
||||
if (not pending and solicited_nodes < MAX_REQUESTED_SEARCH_NODES)
|
||||
ut = std::min(ut, now);
|
||||
if (not sn.candidate)
|
||||
d++;
|
||||
} else
|
||||
ut = std::min(ut, sn.last_get_reply + Node::NODE_EXPIRE_TIME);
|
||||
|
||||
t++;
|
||||
if (not sn.candidate and ++i == TARGET_NODES)
|
||||
break;
|
||||
}
|
||||
if (not callbacks.empty() and d == 0)
|
||||
// If all synced/updated but some callbacks remain, step now to clear them
|
||||
return now;
|
||||
return ut;
|
||||
}
|
||||
|
||||
bool
|
||||
Dht::Search::isAnnounced(Value::Id id) const
|
||||
{
|
||||
@@ -815,7 +777,7 @@ Dht::Search::isListening(time_point now) const
|
||||
for (const auto& n : nodes) {
|
||||
if (n.isBad())
|
||||
continue;
|
||||
SearchNode::NodeListenerStatus::const_iterator ls {};
|
||||
SearchNode::SyncStatus::const_iterator ls {};
|
||||
for (ls = n.listenStatus.begin(); ls != n.listenStatus.end() ; ++ls) {
|
||||
if (n.isListening(now, ls))
|
||||
break;
|
||||
@@ -828,4 +790,78 @@ Dht::Search::isListening(time_point now) const
|
||||
return i;
|
||||
}
|
||||
|
||||
time_point
|
||||
Dht::Search::getAnnounceTime(time_point now) const
|
||||
{
|
||||
if (nodes.empty())
|
||||
return time_point::max();
|
||||
time_point ret {time_point::max()};
|
||||
for (const auto& a : announce) {
|
||||
if (!a.value) continue;
|
||||
unsigned i = 0, t = 0;
|
||||
for (const auto& n : nodes) {
|
||||
if (not n.isSynced(now) or (n.candidate and t >= TARGET_NODES))
|
||||
continue;
|
||||
ret = std::min(ret, n.getAnnounceTime(a.value->id));
|
||||
t++;
|
||||
if (not n.candidate and ++i == TARGET_NODES)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
time_point
|
||||
Dht::Search::getListenTime(time_point now) const
|
||||
{
|
||||
if (listeners.empty())
|
||||
return time_point::max();
|
||||
|
||||
time_point listen_time {time_point::max()};
|
||||
unsigned i = 0, t = 0;
|
||||
for (const auto& sn : nodes) {
|
||||
if (not sn.isSynced(now) or (sn.candidate and t >= LISTEN_NODES))
|
||||
continue;
|
||||
for (auto& l : listeners)
|
||||
listen_time = std::min(listen_time, sn.getListenTime(l.second.query));
|
||||
t++;
|
||||
if (not sn.candidate and ++i == LISTEN_NODES)
|
||||
break;
|
||||
}
|
||||
return listen_time;
|
||||
}
|
||||
|
||||
time_point
|
||||
Dht::Search::getNextStepTime(time_point now) const
|
||||
{
|
||||
auto next_step = time_point::max();
|
||||
if (expired or done)
|
||||
return next_step;
|
||||
|
||||
auto ut = getUpdateTime(now);
|
||||
if (ut != time_point::max()) {
|
||||
//std::cout << id.toString() << " IPv" << (af==AF_INET?"4":"6") << " update time in " << print_dt(ut - now) << " s" << std::endl;
|
||||
next_step = std::min(next_step, ut);
|
||||
}
|
||||
|
||||
if (isSynced(now))
|
||||
{
|
||||
auto at = getAnnounceTime(now);
|
||||
if (at != time_point::max()) {
|
||||
//std::cout << id.toString() << " IPv" << (af==AF_INET?"4":"6") << " announce time in " << print_dt(at - now) << " s" << std::endl;
|
||||
next_step = std::min(next_step, at);
|
||||
}
|
||||
|
||||
auto lt = getListenTime(now);
|
||||
if (lt != time_point::max()) {
|
||||
//std::cout << id.toString() << " IPv" << (af==AF_INET?"4":"6") << " listen time in " << print_dt(lt - now) << " s" << std::endl;
|
||||
next_step = std::min(next_step, lt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return next_step;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+81
-100
@@ -1,8 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@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
|
||||
@@ -33,10 +32,20 @@ extern "C" {
|
||||
|
||||
namespace dht {
|
||||
|
||||
SecureDht::SecureDht(std::unique_ptr<DhtInterface> dht, SecureDht::Config conf)
|
||||
: dht_(std::move(dht)), key_(conf.id.first), certificate_(conf.id.second)
|
||||
Config& getConfig(SecureDht::Config& conf)
|
||||
{
|
||||
if (!dht_) return;
|
||||
auto& c = conf.node_config;
|
||||
if (c.node_id == InfoHash() and conf.id.second)
|
||||
c.node_id = InfoHash::get("node:"+conf.id.second->getId().toString());
|
||||
return c;
|
||||
}
|
||||
|
||||
SecureDht::SecureDht(uv_loop_t* loop, SecureDht::Config conf)
|
||||
: Dht(loop, getConfig(conf)), key_(conf.id.first), certificate_(conf.id.second)
|
||||
{
|
||||
if (not loop)
|
||||
return;
|
||||
|
||||
for (const auto& type : DEFAULT_TYPES)
|
||||
registerType(type);
|
||||
|
||||
@@ -50,13 +59,13 @@ SecureDht::SecureDht(std::unique_ptr<DhtInterface> dht, SecureDht::Config conf)
|
||||
if (key_ and certId != key_->getPublicKey().getId())
|
||||
throw DhtException("SecureDht: provided certificate doesn't match private key.");
|
||||
|
||||
dht_->put(certId, Value {
|
||||
Dht::put(certId, Value {
|
||||
CERTIFICATE_TYPE,
|
||||
*certificate_,
|
||||
1
|
||||
}, [this](bool ok) {
|
||||
if (ok)
|
||||
DHT_LOG.DBG("SecureDht: public key announced successfully");
|
||||
DHT_LOG.DEBUG("SecureDht: public key announced successfully");
|
||||
}, {}, true);
|
||||
}
|
||||
}
|
||||
@@ -68,19 +77,27 @@ ValueType
|
||||
SecureDht::secureType(ValueType&& type)
|
||||
{
|
||||
type.storePolicy = [this,type](InfoHash id, Sp<Value>& v, const InfoHash& nid, const SockAddr& a) {
|
||||
if (v->isSigned())
|
||||
return v->checkSignature();
|
||||
if (v->isSigned()) {
|
||||
if (!v->owner or !v->owner->checkSignature(v->getToSign(), v->signature)) {
|
||||
DHT_LOG.WARN("Signature verification failed");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
DHT_LOG.WARN("Signature verification succeeded");
|
||||
}
|
||||
return type.storePolicy(id, v, nid, a);
|
||||
};
|
||||
type.editPolicy = [this,type](InfoHash id, const Sp<Value>& o, Sp<Value>& n, const InfoHash& nid, const SockAddr& a) {
|
||||
if (not o->isSigned())
|
||||
if (!o->isSigned())
|
||||
return type.editPolicy(id, o, n, nid, a);
|
||||
if (o->owner != n->owner or not n->isSigned()) {
|
||||
if (o->owner != n->owner) {
|
||||
DHT_LOG.WARN("Edition forbidden: owner changed.");
|
||||
return false;
|
||||
}
|
||||
if (not n->checkSignature())
|
||||
if (!o->owner or !o->owner->checkSignature(n->getToSign(), n->signature)) {
|
||||
DHT_LOG.WARN("Edition forbidden: signature verification failed.");
|
||||
return false;
|
||||
}
|
||||
if (o->seq == n->seq) {
|
||||
// If the data is exactly the same,
|
||||
// it can be reannounced, possibly by someone else.
|
||||
@@ -131,7 +148,7 @@ SecureDht::registerCertificate(const InfoHash& node, const Blob& data)
|
||||
}
|
||||
InfoHash h = crt->getPublicKey().getId();
|
||||
if (node == h) {
|
||||
DHT_LOG.DBG("Registering certificate for %s", h.toString().c_str());
|
||||
DHT_LOG.DEBUG("Registering certificate for %s", h.toString().c_str());
|
||||
auto it = nodesCertificates_.find(h);
|
||||
if (it == nodesCertificates_.end())
|
||||
std::tie(it, std::ignore) = nodesCertificates_.emplace(h, std::move(crt));
|
||||
@@ -139,7 +156,7 @@ SecureDht::registerCertificate(const InfoHash& node, const Blob& data)
|
||||
it->second = std::move(crt);
|
||||
return it->second;
|
||||
} else {
|
||||
DHT_LOG.WARN("Certificate %s for node %s does not match node id !", h.toString().c_str(), node.toString().c_str());
|
||||
DHT_LOG.DEBUG("Certificate %s for node %s does not match node id !", h.toString().c_str(), node.toString().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -156,7 +173,7 @@ SecureDht::findCertificate(const InfoHash& node, std::function<void(const Sp<cry
|
||||
{
|
||||
Sp<crypto::Certificate> b = getCertificate(node);
|
||||
if (b && *b) {
|
||||
DHT_LOG.DBG("Using certificate from cache for %s", node.toString().c_str());
|
||||
DHT_LOG.DEBUG("Using certificate from cache for %s", node.toString().c_str());
|
||||
if (cb)
|
||||
cb(b);
|
||||
return;
|
||||
@@ -164,7 +181,7 @@ SecureDht::findCertificate(const InfoHash& node, std::function<void(const Sp<cry
|
||||
if (localQueryMethod_) {
|
||||
auto res = localQueryMethod_(node);
|
||||
if (not res.empty()) {
|
||||
DHT_LOG.DBG("Registering certificate from local store for %s", node.toString().c_str());
|
||||
DHT_LOG.DEBUG("Registering certificate from local store for %s", node.toString().c_str());
|
||||
nodesCertificates_.emplace(node, res.front());
|
||||
if (cb)
|
||||
cb(res.front());
|
||||
@@ -173,13 +190,13 @@ SecureDht::findCertificate(const InfoHash& node, std::function<void(const Sp<cry
|
||||
}
|
||||
|
||||
auto found = std::make_shared<bool>(false);
|
||||
dht_->get(node, [cb,node,found,this](const std::vector<Sp<Value>>& vals) {
|
||||
Dht::get(node, [cb,node,found,this](const std::vector<Sp<Value>>& vals) {
|
||||
if (*found)
|
||||
return false;
|
||||
for (const auto& v : vals) {
|
||||
if (auto cert = registerCertificate(node, v->data)) {
|
||||
*found = true;
|
||||
DHT_LOG.DBG("Found certificate for %s", node.toString().c_str());
|
||||
DHT_LOG.DEBUG("Found certificate for %s", node.toString().c_str());
|
||||
if (cb)
|
||||
cb(cert);
|
||||
return false;
|
||||
@@ -197,7 +214,7 @@ SecureDht::findPublicKey(const InfoHash& node, std::function<void(const Sp<const
|
||||
{
|
||||
auto pk = getPublicKey(node);
|
||||
if (pk && *pk) {
|
||||
DHT_LOG.DBG("Found public key from cache for %s", node.toString().c_str());
|
||||
DHT_LOG.DEBUG("Found public key from cache for %s", node.toString().c_str());
|
||||
if (cb)
|
||||
cb(pk);
|
||||
return;
|
||||
@@ -205,82 +222,53 @@ SecureDht::findPublicKey(const InfoHash& node, std::function<void(const Sp<const
|
||||
findCertificate(node, [=](const Sp<crypto::Certificate> crt) {
|
||||
if (crt && *crt) {
|
||||
auto pk = std::make_shared<crypto::PublicKey>(crt->getPublicKey());
|
||||
if (*pk) {
|
||||
nodesPubKeys_[pk->getId()] = pk;
|
||||
if (cb) cb(pk);
|
||||
return;
|
||||
}
|
||||
nodesPubKeys_[pk->getId()] = pk;
|
||||
if (cb) cb(pk);
|
||||
} else {
|
||||
if (cb) cb(nullptr);
|
||||
}
|
||||
if (cb) cb(nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
Sp<Value>
|
||||
SecureDht::checkValue(const Sp<Value>& v)
|
||||
{
|
||||
// Decrypt encrypted values
|
||||
if (v->isEncrypted()) {
|
||||
if (not key_) {
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
if (forward_all_) // We are currently a proxy, send messages to clients.
|
||||
return v;
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
try {
|
||||
if (auto decrypted_val = v->decrypt(*key_)) {
|
||||
if (decrypted_val->owner)
|
||||
nodesPubKeys_[decrypted_val->owner->getId()] = decrypted_val->owner;
|
||||
return decrypted_val;
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
DHT_LOG.WARN("Could not decrypt value %s : %s", v->toString().c_str(), e.what());
|
||||
}
|
||||
}
|
||||
// Check signed values
|
||||
else if (v->isSigned()) {
|
||||
if (v->checkSignature()) {
|
||||
if (v->owner)
|
||||
nodesPubKeys_[v->owner->getId()] = v->owner;
|
||||
return v;
|
||||
} else
|
||||
DHT_LOG.WARN("Signature verification failed for %s", v->toString().c_str());
|
||||
}
|
||||
// Forward normal values
|
||||
else {
|
||||
return v;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ValueCallback
|
||||
SecureDht::getCallbackFilter(ValueCallback cb, Value::Filter&& filter)
|
||||
{
|
||||
return [=](const std::vector<Sp<Value>>& values, bool expired) {
|
||||
std::vector<Sp<Value>> tmpvals {};
|
||||
for (const auto& v : values) {
|
||||
if (auto nv = checkValue(v))
|
||||
if (not filter or filter(*nv))
|
||||
tmpvals.emplace_back(std::move(nv));
|
||||
}
|
||||
if (cb and not tmpvals.empty())
|
||||
return cb(tmpvals, expired);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
GetCallback
|
||||
SecureDht::getCallbackFilter(GetCallback cb, Value::Filter&& filter)
|
||||
{
|
||||
return [=](const std::vector<Sp<Value>>& values) {
|
||||
std::vector<Sp<Value>> tmpvals {};
|
||||
for (const auto& v : values) {
|
||||
if (auto nv = checkValue(v))
|
||||
if (not filter or filter(*nv))
|
||||
tmpvals.emplace_back(std::move(nv));
|
||||
// Decrypt encrypted values
|
||||
if (v->isEncrypted()) {
|
||||
if (not key_)
|
||||
continue;
|
||||
try {
|
||||
Value decrypted_val (decrypt(*v));
|
||||
if (decrypted_val.recipient == getId()) {
|
||||
nodesPubKeys_[decrypted_val.owner->getId()] = decrypted_val.owner;
|
||||
if (not filter or filter(decrypted_val))
|
||||
tmpvals.push_back(std::make_shared<Value>(std::move(decrypted_val)));
|
||||
}
|
||||
// Ignore values belonging to other people
|
||||
} catch (const std::exception& e) {
|
||||
DHT_LOG.WARN("Could not decrypt value %s : %s", v->toString().c_str(), e.what());
|
||||
}
|
||||
}
|
||||
// Check signed values
|
||||
else if (v->isSigned()) {
|
||||
if (v->owner and v->owner->checkSignature(v->getToSign(), v->signature)) {
|
||||
nodesPubKeys_[v->owner->getId()] = v->owner;
|
||||
if (not filter or filter(*v))
|
||||
tmpvals.push_back(v);
|
||||
}
|
||||
else
|
||||
DHT_LOG.WARN("Signature verification failed for %s", v->toString().c_str());
|
||||
}
|
||||
// Forward normal values
|
||||
else {
|
||||
if (not filter or filter(*v))
|
||||
tmpvals.push_back(v);
|
||||
}
|
||||
}
|
||||
if (cb and not tmpvals.empty())
|
||||
if (cb && not tmpvals.empty())
|
||||
return cb(tmpvals);
|
||||
return true;
|
||||
};
|
||||
@@ -289,20 +277,13 @@ SecureDht::getCallbackFilter(GetCallback cb, Value::Filter&& filter)
|
||||
void
|
||||
SecureDht::get(const InfoHash& id, GetCallback cb, DoneCallback donecb, Value::Filter&& f, Where&& w)
|
||||
{
|
||||
dht_->get(id, getCallbackFilter(cb, std::forward<Value::Filter>(f)), donecb, {}, std::forward<Where>(w));
|
||||
Dht::get(id, getCallbackFilter(cb, std::forward<Value::Filter>(f)), donecb, {}, std::forward<Where>(w));
|
||||
}
|
||||
|
||||
size_t
|
||||
SecureDht::listen(const InfoHash& id, ValueCallback cb, Value::Filter f, Where w)
|
||||
SecureDht::listen(const InfoHash& id, GetCallback cb, Value::Filter&& f, Where&& w)
|
||||
{
|
||||
return dht_->listen(id, getCallbackFilter(cb, std::forward<Value::Filter>(f)), {}, std::forward<Where>(w));
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
SecureDht::listen(const InfoHash& id, GetCallback cb, Value::Filter f, Where w)
|
||||
{
|
||||
return dht_->listen(id, getCallbackFilter(cb, std::forward<Value::Filter>(f)), {}, std::forward<Where>(w));
|
||||
return Dht::listen(id, getCallbackFilter(cb, std::forward<Value::Filter>(f)), {}, std::forward<Where>(w));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -310,20 +291,20 @@ SecureDht::putSigned(const InfoHash& hash, Sp<Value> val, DoneCallback callback,
|
||||
{
|
||||
if (val->id == Value::INVALID_ID) {
|
||||
crypto::random_device rdev;
|
||||
std::uniform_int_distribution<Value::Id> rand_id;
|
||||
val->id = rand_id(rdev);
|
||||
}
|
||||
|
||||
// Check if we are already announcing a value
|
||||
auto p = dht_->getPut(hash, val->id);
|
||||
auto p = getPut(hash, val->id);
|
||||
if (p && val->seq <= p->seq) {
|
||||
DHT_LOG.DEBUG("Found previous value being announced.");
|
||||
val->seq = p->seq + 1;
|
||||
}
|
||||
|
||||
// Check if data already exists on the dht
|
||||
get(hash,
|
||||
[val,this] (const std::vector<Sp<Value>>& vals) {
|
||||
DHT_LOG.DBG("Found online previous value being announced.");
|
||||
DHT_LOG.DEBUG("Found online previous value being announced.");
|
||||
for (const auto& v : vals) {
|
||||
if (!v->isSigned())
|
||||
DHT_LOG.ERR("Existing non-signed value seems to exists at this location.");
|
||||
@@ -336,7 +317,7 @@ SecureDht::putSigned(const InfoHash& hash, Sp<Value> val, DoneCallback callback,
|
||||
},
|
||||
[hash,val,this,callback,permanent] (bool /* ok */) {
|
||||
sign(*val);
|
||||
dht_->put(hash, val, callback, time_point::max(), permanent);
|
||||
put(hash, val, callback, time_point::max(), permanent);
|
||||
},
|
||||
Value::IdFilter(val->id)
|
||||
);
|
||||
@@ -345,7 +326,7 @@ SecureDht::putSigned(const InfoHash& hash, Sp<Value> val, DoneCallback callback,
|
||||
void
|
||||
SecureDht::putEncrypted(const InfoHash& hash, const InfoHash& to, Sp<Value> val, DoneCallback callback, bool permanent)
|
||||
{
|
||||
findPublicKey(to, [=](const Sp<const crypto::PublicKey>& pk) {
|
||||
findPublicKey(to, [=](const Sp<const crypto::PublicKey> pk) {
|
||||
if(!pk || !*pk) {
|
||||
if (callback)
|
||||
callback(false, {});
|
||||
@@ -353,7 +334,7 @@ SecureDht::putEncrypted(const InfoHash& hash, const InfoHash& to, Sp<Value> val,
|
||||
}
|
||||
DHT_LOG.WARN("Encrypting data for PK: %s", pk->getId().toString().c_str());
|
||||
try {
|
||||
dht_->put(hash, encrypt(*val, *pk), callback, time_point::max(), permanent);
|
||||
put(hash, encrypt(*val, *pk), callback, time_point::max(), permanent);
|
||||
} catch (const std::exception& e) {
|
||||
DHT_LOG.ERR("Error putting encrypted data: %s", e.what());
|
||||
if (callback)
|
||||
|
||||
+8
-11
@@ -154,21 +154,20 @@ struct Storage {
|
||||
*
|
||||
* @param now The reference to now
|
||||
* @param vid The value id
|
||||
* @return time of the next expiration, time_point::max() if no expiration
|
||||
* @return true if a value storage was updated, false otherwise
|
||||
*/
|
||||
time_point refresh(const time_point& now, const Value::Id& vid, const TypeStore& types) {
|
||||
bool refresh(const time_point& now, const Value::Id& vid) {
|
||||
for (auto& vs : values)
|
||||
if (vs.data->id == vid) {
|
||||
vs.created = now;
|
||||
vs.expiration = std::max(vs.expiration, now + types.getType(vs.data->type).expiration);
|
||||
return vs.expiration;
|
||||
return true;
|
||||
}
|
||||
return time_point::max();
|
||||
return false;
|
||||
}
|
||||
|
||||
StoreDiff remove(const InfoHash& id, Value::Id);
|
||||
|
||||
std::pair<ssize_t, std::vector<Sp<Value>>> expire(const InfoHash& id, time_point now);
|
||||
StoreDiff expire(const InfoHash& id, time_point now);
|
||||
|
||||
private:
|
||||
Storage(const Storage&) = delete;
|
||||
@@ -246,7 +245,7 @@ Storage::clear()
|
||||
return {-tot_size, -num_values, 0};
|
||||
}
|
||||
|
||||
std::pair<ssize_t, std::vector<Sp<Value>>>
|
||||
Storage::StoreDiff
|
||||
Storage::expire(const InfoHash& id, time_point now)
|
||||
{
|
||||
// expire listeners
|
||||
@@ -272,18 +271,16 @@ Storage::expire(const InfoHash& id, time_point now)
|
||||
auto r = std::partition(values.begin(), values.end(), [&](const ValueStorage& v) {
|
||||
return v.expiration > now;
|
||||
});
|
||||
std::vector<Sp<Value>> ret;
|
||||
ret.reserve(std::distance(r, values.end()));
|
||||
ssize_t del_num = -std::distance(r, values.end());
|
||||
ssize_t size_diff {};
|
||||
std::for_each(r, values.end(), [&](const ValueStorage& v) {
|
||||
size_diff -= v.data->size();
|
||||
if (v.store_bucket)
|
||||
v.store_bucket->erase(id, *v.data, v.expiration);
|
||||
ret.emplace_back(std::move(v.data));
|
||||
});
|
||||
total_size += size_diff;
|
||||
values.erase(r, values.end());
|
||||
return {size_diff, std::move(ret)};
|
||||
return {size_diff, del_num, del_listen};
|
||||
}
|
||||
|
||||
}
|
||||
+49
-54
@@ -19,60 +19,17 @@
|
||||
#include "utils.h"
|
||||
#include "sockaddr.h"
|
||||
#include "default_types.h"
|
||||
#include "net.h"
|
||||
#include "uv_utils.h"
|
||||
|
||||
/* An IPv4 equivalent to IN6_IS_ADDR_UNSPECIFIED */
|
||||
#ifndef IN_IS_ADDR_UNSPECIFIED
|
||||
#define IN_IS_ADDR_UNSPECIFIED(a) (((long int) (a)->s_addr) == 0x00000000)
|
||||
#endif /* IN_IS_ADDR_UNSPECIFIED */
|
||||
|
||||
namespace dht {
|
||||
|
||||
static constexpr std::array<uint8_t, 12> MAPPED_IPV4_PREFIX {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}};
|
||||
|
||||
std::pair<std::string, std::string>
|
||||
splitPort(const std::string& s) {
|
||||
if (s.empty())
|
||||
return {};
|
||||
if (s[0] == '[') {
|
||||
std::size_t closure = s.find_first_of(']');
|
||||
std::size_t found = s.find_last_of(':');
|
||||
if (closure == std::string::npos)
|
||||
return {s, ""};
|
||||
if (found == std::string::npos or found < closure)
|
||||
return {s.substr(1,closure-1), ""};
|
||||
return {s.substr(1,closure-1), s.substr(found+1)};
|
||||
}
|
||||
std::size_t found = s.find_last_of(':');
|
||||
std::size_t first = s.find_first_of(':');
|
||||
if (found == std::string::npos or found != first)
|
||||
return {s, ""};
|
||||
return {s.substr(0,found), s.substr(found+1)};
|
||||
}
|
||||
|
||||
std::vector<SockAddr>
|
||||
SockAddr::resolve(const std::string& host, const std::string& service)
|
||||
{
|
||||
std::vector<SockAddr> ips {};
|
||||
if (host.empty())
|
||||
return ips;
|
||||
|
||||
addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
addrinfo* info = nullptr;
|
||||
int rc = getaddrinfo(host.c_str(), service.empty() ? nullptr : service.c_str(), &hints, &info);
|
||||
if(rc != 0)
|
||||
throw std::invalid_argument(std::string("Error: `") + host + ":" + service + "`: " + gai_strerror(rc));
|
||||
|
||||
addrinfo* infop = info;
|
||||
while (infop) {
|
||||
ips.emplace_back(infop->ai_addr, infop->ai_addrlen);
|
||||
infop = infop->ai_next;
|
||||
}
|
||||
freeaddrinfo(info);
|
||||
return ips;
|
||||
}
|
||||
|
||||
namespace dht {
|
||||
|
||||
std::string
|
||||
print_addr(const sockaddr* sa, socklen_t slen)
|
||||
@@ -116,8 +73,7 @@ SockAddr::isLoopback() const
|
||||
{
|
||||
switch (getFamily()) {
|
||||
case AF_INET: {
|
||||
auto addr_host = ntohl(getIPv4().sin_addr.s_addr);
|
||||
uint8_t b1 = (uint8_t)(addr_host >> 24);
|
||||
uint8_t b1 = (uint8_t)(getIPv4().sin_addr.s_addr >> 24);
|
||||
return b1 == 127;
|
||||
}
|
||||
case AF_INET6:
|
||||
@@ -134,11 +90,10 @@ SockAddr::isPrivate() const
|
||||
return true;
|
||||
}
|
||||
switch (getFamily()) {
|
||||
case AF_INET: {
|
||||
auto addr_host = ntohl(getIPv4().sin_addr.s_addr);
|
||||
case AF_INET:
|
||||
uint8_t b1, b2;
|
||||
b1 = (uint8_t)(addr_host >> 24);
|
||||
b2 = (uint8_t)((addr_host >> 16) & 0x0ff);
|
||||
b1 = (uint8_t)(getIPv4().sin_addr.s_addr >> 24);
|
||||
b2 = (uint8_t)((getIPv4().sin_addr.s_addr >> 16) & 0x0ff);
|
||||
// 10.x.y.z
|
||||
if (b1 == 10)
|
||||
return true;
|
||||
@@ -149,7 +104,6 @@ SockAddr::isPrivate() const
|
||||
if ((b1 == 192) && (b2 == 168))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
case AF_INET6: {
|
||||
const uint8_t* addr6 = reinterpret_cast<const uint8_t*>(&getIPv6().sin6_addr);
|
||||
if (addr6[0] == 0xfc)
|
||||
@@ -173,7 +127,7 @@ SockAddr::isMappedIPv4() const
|
||||
SockAddr
|
||||
SockAddr::getMappedIPv4() const
|
||||
{
|
||||
if (not isMappedIPv4())
|
||||
if (getFamily() != AF_INET6)
|
||||
return *this;
|
||||
SockAddr ret;
|
||||
ret.setFamily(AF_INET);
|
||||
@@ -185,6 +139,33 @@ SockAddr::getMappedIPv4() const
|
||||
return ret;
|
||||
}
|
||||
|
||||
void get_addr_info_cb(uv_getaddrinfo_t* req, int status, struct addrinfo* res) {
|
||||
if (auto cb = static_cast<GetAddrInfoCb*>(req->data)) {
|
||||
size_t count {0};
|
||||
for (auto addr = res; addr; addr = addr->ai_next)
|
||||
count++;
|
||||
std::vector<SockAddr> ret;
|
||||
ret.reserve(count);
|
||||
for (auto addr = res; addr; addr = addr->ai_next)
|
||||
ret.emplace_back(addr->ai_addr, addr->ai_addrlen);
|
||||
(*cb)(std::move(ret));
|
||||
delete cb;
|
||||
}
|
||||
delete req;
|
||||
uv_freeaddrinfo(res);
|
||||
}
|
||||
|
||||
void getAddrInfo(uv_loop_t* loop, const char* node, const char* service, GetAddrInfoCb&& cb)
|
||||
{
|
||||
auto req = new uv_getaddrinfo_t;
|
||||
req->data = new GetAddrInfoCb(std::move(cb));
|
||||
addrinfo hints {};
|
||||
hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
uv_getaddrinfo(loop, req, get_addr_info_cb, node, service, &hints);
|
||||
}
|
||||
|
||||
bool operator==(const SockAddr& a, const SockAddr& b) {
|
||||
return a.equals(b);
|
||||
}
|
||||
@@ -234,4 +215,18 @@ findMapValue(msgpack::object& map, const std::string& key) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
namespace net {
|
||||
|
||||
TransId
|
||||
unpackTid(msgpack::object& o) {
|
||||
switch (o.type) {
|
||||
case msgpack::type::POSITIVE_INTEGER:
|
||||
return o.as<uint32_t>();
|
||||
default:
|
||||
return o.as<std::array<char, 4>>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+1
-117
@@ -22,11 +22,6 @@
|
||||
#include "default_types.h"
|
||||
#include "securedht.h" // print certificate ID
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
#include "base64.h"
|
||||
#endif
|
||||
|
||||
|
||||
namespace dht {
|
||||
|
||||
const std::string Query::QUERY_PARSE_ERROR {"Error parsing query."};
|
||||
@@ -46,7 +41,7 @@ std::ostream& operator<< (std::ostream& s, const Value& v)
|
||||
s << "encrypted ";
|
||||
else if (v.isSigned()) {
|
||||
s << "signed (v" << v.seq << ") ";
|
||||
if (v.recipient)
|
||||
if (v.recipient != InfoHash())
|
||||
s << "decrypted ";
|
||||
}
|
||||
if (not v.isEncrypted()) {
|
||||
@@ -172,117 +167,6 @@ Value::msgpack_unpack_body(const msgpack::object& o)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
Value::Value(Json::Value& json)
|
||||
{
|
||||
id = Value::Id(unpackId(json, "id"));
|
||||
if (json.isMember("cypher")) {
|
||||
auto cypherStr = json["cypher"].asString();
|
||||
cypherStr = base64_decode(cypherStr);
|
||||
cypher = std::vector<unsigned char>(cypherStr.begin(), cypherStr.end());
|
||||
}
|
||||
if (json.isMember("sig")) {
|
||||
auto sigStr = json["sig"].asString();
|
||||
sigStr = base64_decode(sigStr);
|
||||
signature = std::vector<unsigned char>(sigStr.begin(), sigStr.end());
|
||||
}
|
||||
if (json.isMember("seq"))
|
||||
seq = json["seq"].asInt();
|
||||
if (json.isMember("owner")) {
|
||||
auto ownerStr = json["owner"].asString();
|
||||
auto ownerBlob = std::vector<unsigned char>(ownerStr.begin(), ownerStr.end());
|
||||
owner = std::make_shared<const crypto::PublicKey>(ownerBlob);
|
||||
}
|
||||
if (json.isMember("to")) {
|
||||
auto toStr = json["to"].asString();
|
||||
recipient = InfoHash(toStr);
|
||||
}
|
||||
if (json.isMember("type"))
|
||||
type = json["type"].asInt();
|
||||
if (json.isMember("data")){
|
||||
auto dataStr = json["data"].asString();
|
||||
dataStr = base64_decode(dataStr);
|
||||
data = std::vector<unsigned char>(dataStr.begin(), dataStr.end());
|
||||
}
|
||||
if (json.isMember("utype"))
|
||||
user_type = json["utype"].asString();
|
||||
}
|
||||
|
||||
Json::Value
|
||||
Value::toJson() const
|
||||
{
|
||||
Json::Value val;
|
||||
val["id"] = std::to_string(id);
|
||||
if (isEncrypted()) {
|
||||
val["cypher"] = base64_encode(cypher);
|
||||
} else {
|
||||
if (isSigned())
|
||||
val["sig"] = base64_encode(signature);
|
||||
bool has_owner = owner && *owner;
|
||||
if (has_owner) { // isSigned
|
||||
val["seq"] = seq;
|
||||
val["owner"] = owner->toString();
|
||||
if (recipient)
|
||||
val["to"] = recipient.toString();
|
||||
}
|
||||
val["type"] = type;
|
||||
val["data"] = base64_encode(data);
|
||||
if (not user_type.empty())
|
||||
val["utype"] = user_type;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
bool
|
||||
Value::checkSignature()
|
||||
{
|
||||
if (!signatureChecked) {
|
||||
signatureChecked = true;
|
||||
if (isSigned()) {
|
||||
signatureValid = owner and owner->checkSignature(getToSign(), signature);
|
||||
}
|
||||
}
|
||||
return signatureValid;
|
||||
}
|
||||
|
||||
Sp<Value>
|
||||
Value::decrypt(const crypto::PrivateKey& key)
|
||||
{
|
||||
if (not decrypted) {
|
||||
decrypted = true;
|
||||
if (isEncrypted()) {
|
||||
auto decryptedBlob = key.decrypt(cypher);
|
||||
std::unique_ptr<Value> v {new Value(id)};
|
||||
auto msg = msgpack::unpack((const char*)decryptedBlob.data(), decryptedBlob.size());
|
||||
v->msgpack_unpack_body(msg.get());
|
||||
if (v->recipient != key.getPublicKey().getId())
|
||||
throw crypto::DecryptError("Recipient mismatch");
|
||||
// Ignore values belonging to other people
|
||||
if (not v->owner or not v->owner->checkSignature(v->getToSign(), v->signature))
|
||||
throw crypto::DecryptError("Signature mismatch");
|
||||
decryptedValue = std::move(v);
|
||||
}
|
||||
}
|
||||
return decryptedValue;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
unpackId(const Json::Value& json, const std::string& key) {
|
||||
uint64_t ret = 0;
|
||||
try {
|
||||
if (json.isMember(key)) {
|
||||
const auto& t = json[key];
|
||||
if (t.isString()) {
|
||||
ret = std::stoull(t.asString());
|
||||
} else {
|
||||
ret = t.asLargestUInt();
|
||||
}
|
||||
}
|
||||
} catch (...) {}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
FieldValue::operator==(const FieldValue& vfd) const
|
||||
{
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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
|
||||
* 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 "value.h"
|
||||
|
||||
namespace dht {
|
||||
|
||||
using ValueStateCallback = std::function<void(const std::vector<Sp<Value>>&, bool)>;
|
||||
using CallbackQueue = std::list<std::function<void()>>;
|
||||
|
||||
class ValueCache {
|
||||
public:
|
||||
ValueCache(ValueStateCallback&& cb) : callback(std::forward<ValueStateCallback>(cb)) {}
|
||||
ValueCache(ValueCache&& o) : values(std::move(o.values)), callback(std::move(o.callback)) {
|
||||
o.callback = {};
|
||||
}
|
||||
|
||||
~ValueCache() {
|
||||
auto q = clear();
|
||||
for (auto& cb: q)
|
||||
cb();
|
||||
}
|
||||
|
||||
CallbackQueue clear() {
|
||||
std::vector<Sp<Value>> expired_values;
|
||||
expired_values.reserve(values.size());
|
||||
for (const auto& v : values)
|
||||
expired_values.emplace_back(std::move(v.second.data));
|
||||
values.clear();
|
||||
CallbackQueue ret;
|
||||
if (not expired_values.empty() and callback) {
|
||||
auto cb = callback;
|
||||
ret.emplace_back([expired_values, cb]{
|
||||
cb(expired_values, true);
|
||||
});
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
time_point expireValues(const time_point& now) {
|
||||
time_point ret = time_point::max();
|
||||
auto cbs = expireValues(now, ret);
|
||||
while (not cbs.empty()) {
|
||||
cbs.front()();
|
||||
cbs.pop_front();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
CallbackQueue expireValues(const time_point& now, time_point& next) {
|
||||
std::vector<Sp<Value>> expired_values;
|
||||
for (auto it = values.begin(); it != values.end();) {
|
||||
if (it->second.expiration <= now) {
|
||||
expired_values.emplace_back(std::move(it->second.data));
|
||||
it = values.erase(it);
|
||||
} else {
|
||||
next = std::min(next, it->second.expiration);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
while (values.size() > MAX_VALUES) {
|
||||
// too many values, remove oldest values
|
||||
time_point oldest_creation = time_point::max();
|
||||
auto oldest_value = values.end();
|
||||
for (auto it = values.begin(); it != values.end(); ++it)
|
||||
if (it->second.created < oldest_creation) {
|
||||
oldest_value = it;
|
||||
oldest_creation = it->second.created;
|
||||
}
|
||||
if (oldest_value != values.end()) {
|
||||
expired_values.emplace_back(std::move(oldest_value->second.data));
|
||||
values.erase(oldest_value);
|
||||
}
|
||||
}
|
||||
CallbackQueue ret;
|
||||
if (not expired_values.empty() and callback) {
|
||||
auto cb = callback;
|
||||
ret.emplace_back([cb, expired_values]{
|
||||
if (cb) cb(expired_values, true);
|
||||
});
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
time_point onValues
|
||||
(const std::vector<Sp<Value>>& values,
|
||||
const std::vector<Value::Id>& refreshed_values,
|
||||
const std::vector<Value::Id>& expired_values,
|
||||
const TypeStore& types, const time_point& now)
|
||||
{
|
||||
CallbackQueue cbs;
|
||||
time_point ret = time_point::max();
|
||||
if (not values.empty())
|
||||
cbs.splice(cbs.end(), addValues(values, types, now));
|
||||
for (const auto& vid : refreshed_values)
|
||||
refreshValue(vid, types, now);
|
||||
for (const auto& vid : expired_values)
|
||||
cbs.splice(cbs.end(), expireValue(vid));
|
||||
cbs.splice(cbs.end(), expireValues(now, ret));
|
||||
while (not cbs.empty()) {
|
||||
cbs.front()();
|
||||
cbs.pop_front();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
// prevent copy
|
||||
ValueCache(const ValueCache&) = delete;
|
||||
ValueCache& operator=(const ValueCache&) = delete;
|
||||
ValueCache& operator=(ValueCache&&) = delete;
|
||||
|
||||
/* The maximum number of values we store in the cache. */
|
||||
static constexpr unsigned MAX_VALUES {4096};
|
||||
|
||||
struct CacheValueStorage {
|
||||
Sp<Value> data {};
|
||||
time_point created {};
|
||||
time_point expiration {};
|
||||
|
||||
CacheValueStorage() {}
|
||||
CacheValueStorage(const Sp<Value>& v, time_point t, time_point e)
|
||||
: data(v), created(t), expiration(e) {}
|
||||
};
|
||||
|
||||
std::map<Value::Id, CacheValueStorage> values;
|
||||
ValueStateCallback callback;
|
||||
|
||||
CallbackQueue addValues(const std::vector<Sp<Value>>& new_values, const TypeStore& types, const time_point& now) {
|
||||
std::vector<Sp<Value>> nvals;
|
||||
for (const auto& value : new_values) {
|
||||
auto v = values.find(value->id);
|
||||
if (v == values.end()) {
|
||||
// new value
|
||||
nvals.emplace_back(value);
|
||||
values.emplace(value->id, CacheValueStorage(value, now, now + types.getType(value->type).expiration));
|
||||
} else {
|
||||
// refreshed value
|
||||
v->second.created = now;
|
||||
v->second.expiration = now + types.getType(v->second.data->type).expiration;
|
||||
}
|
||||
}
|
||||
auto cb = callback;
|
||||
CallbackQueue ret;
|
||||
if (not nvals.empty())
|
||||
ret.emplace_back([cb, nvals]{
|
||||
if (cb) cb(nvals, false);
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
CallbackQueue expireValue(Value::Id vid) {
|
||||
auto v = values.find(vid);
|
||||
if (v == values.end())
|
||||
return {};
|
||||
const std::vector<Sp<Value>> val {std::move(v->second.data)};
|
||||
values.erase(v);
|
||||
auto cb = callback;
|
||||
CallbackQueue ret;
|
||||
ret.emplace_back([cb, val]{
|
||||
if (cb) cb(val, true);
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
void refreshValue(Value::Id vid, const TypeStore& types, const time_point& now) {
|
||||
auto v = values.find(vid);
|
||||
if (v == values.end())
|
||||
return;
|
||||
v->second.created = now;
|
||||
v->second.expiration = now + types.getType(v->second.data->type).expiration;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
if ENABLE_TESTS
|
||||
bin_PROGRAMS = opendht_unit_tests
|
||||
|
||||
AM_CPPFLAGS = -I../include
|
||||
|
||||
nobase_include_HEADERS = infohashtester.h cryptotester.h dhtrunnertester.h dhtproxytester.h
|
||||
opendht_unit_tests_SOURCES = tests_runner.cpp cryptotester.cpp infohashtester.cpp dhtrunnertester.cpp dhtproxytester.cpp
|
||||
opendht_unit_tests_LDFLAGS = -lopendht -lcppunit -L@top_builddir@/src/.libs @GnuTLS_LIBS@
|
||||
endif
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "cryptotester.h"
|
||||
|
||||
#include "opendht/crypto.h"
|
||||
|
||||
namespace test {
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(CryptoTester);
|
||||
|
||||
void
|
||||
CryptoTester::setUp() {
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
CryptoTester::testSignatureEncryption() {
|
||||
auto key = dht::crypto::PrivateKey::generate();
|
||||
auto public_key = key.getPublicKey();
|
||||
|
||||
std::vector<uint8_t> data {5, 10};
|
||||
std::vector<uint8_t> signature = key.sign(data);
|
||||
|
||||
// check signature
|
||||
CPPUNIT_ASSERT(public_key.checkSignature(data, signature));
|
||||
|
||||
// encrypt data
|
||||
std::vector<uint8_t> encrypted = public_key.encrypt(data);
|
||||
std::vector<uint8_t> decrypted = key.decrypt(encrypted);
|
||||
CPPUNIT_ASSERT(data == decrypted);
|
||||
}
|
||||
|
||||
void
|
||||
CryptoTester::testCertificateRevocation()
|
||||
{
|
||||
auto ca1 = dht::crypto::generateIdentity("ca1");
|
||||
auto account1 = dht::crypto::generateIdentity("acc1", ca1, 4096, true);
|
||||
auto device11 = dht::crypto::generateIdentity("dev11", account1);
|
||||
auto device12 = dht::crypto::generateIdentity("dev12", account1);
|
||||
|
||||
|
||||
dht::crypto::TrustList list;
|
||||
list.add(*ca1.second);
|
||||
auto v = list.verify(*account1.second);
|
||||
CPPUNIT_ASSERT_MESSAGE(v.toString(), v);
|
||||
|
||||
list.add(*account1.second);
|
||||
v = list.verify(*device11.second);
|
||||
CPPUNIT_ASSERT_MESSAGE(v.toString(), v);
|
||||
v = list.verify(*device12.second);
|
||||
CPPUNIT_ASSERT_MESSAGE(v.toString(), v);
|
||||
|
||||
auto ca2 = dht::crypto::generateIdentity("ca2");
|
||||
auto account2 = dht::crypto::generateIdentity("acc2", ca2, 4096, true);
|
||||
auto device2 = dht::crypto::generateIdentity("dev2", account2);
|
||||
|
||||
v = list.verify(*device2.second);
|
||||
CPPUNIT_ASSERT_MESSAGE(v.toString(), !v);
|
||||
|
||||
account1.second->revoke(*account1.first, *device11.second);
|
||||
dht::crypto::TrustList list2;
|
||||
list2.add(*account1.second);
|
||||
|
||||
v = list2.verify(*device11.second);
|
||||
CPPUNIT_ASSERT_MESSAGE(v.toString(), !v);
|
||||
v = list2.verify(*device12.second);
|
||||
CPPUNIT_ASSERT_MESSAGE(v.toString(), v);
|
||||
}
|
||||
|
||||
void
|
||||
CryptoTester::tearDown() {
|
||||
|
||||
}
|
||||
} // namespace test
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
// cppunit
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
namespace test {
|
||||
|
||||
class CryptoTester : public CppUnit::TestFixture {
|
||||
CPPUNIT_TEST_SUITE(CryptoTester);
|
||||
CPPUNIT_TEST(testSignatureEncryption);
|
||||
CPPUNIT_TEST(testCertificateRevocation);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Method automatically called before each test by CppUnit
|
||||
*/
|
||||
void setUp();
|
||||
/**
|
||||
* Method automatically called after each test CppUnit
|
||||
*/
|
||||
void tearDown();
|
||||
/**
|
||||
* Test data signature, encryption and decryption
|
||||
*/
|
||||
void testSignatureEncryption();
|
||||
/**
|
||||
* Test certificate generation, validation and revocation
|
||||
*/
|
||||
void testCertificateRevocation();
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dhtproxytester.h"
|
||||
|
||||
// std
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
|
||||
|
||||
namespace test {
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(DhtProxyTester);
|
||||
|
||||
void
|
||||
DhtProxyTester::setUp() {
|
||||
nodePeer.run(42222, {}, true);
|
||||
nodeProxy = std::make_shared<dht::DhtRunner>();
|
||||
nodeClient = std::make_shared<dht::DhtRunner>();
|
||||
|
||||
nodeProxy->run(42232, {}, true);
|
||||
nodeProxy->bootstrap(nodePeer.getBound());
|
||||
server = std::unique_ptr<dht::DhtProxyServer>(new dht::DhtProxyServer(nodeProxy, 8080));
|
||||
|
||||
nodeClient->run(42242, {}, true);
|
||||
nodeClient->bootstrap(nodePeer.getBound());
|
||||
nodeClient->setProxyServer("127.0.0.1:8080");
|
||||
nodeClient->enableProxy(true);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyTester::tearDown() {
|
||||
nodePeer.join();
|
||||
nodeClient->join();
|
||||
server->stop();
|
||||
server = nullptr;
|
||||
nodeProxy->join();
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyTester::testGetPut() {
|
||||
bool done = false;
|
||||
std::condition_variable cv;
|
||||
std::mutex cv_m;
|
||||
|
||||
auto key = dht::InfoHash::get("GLaDOs");
|
||||
dht::Value val {"Hey! It's been a long time. How have you been?"};
|
||||
auto val_data = val.data;
|
||||
|
||||
nodePeer.put(key, std::move(val), [&](bool) {
|
||||
done = true;
|
||||
cv.notify_all();
|
||||
});
|
||||
std::unique_lock<std::mutex> lk(cv_m);
|
||||
cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; });
|
||||
|
||||
auto vals = nodeClient->get(key).get();
|
||||
CPPUNIT_ASSERT(not vals.empty());
|
||||
CPPUNIT_ASSERT(vals.front()->data == val_data);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DhtProxyTester::testListen() {
|
||||
bool done = false;
|
||||
std::condition_variable cv;
|
||||
std::mutex cv_m;
|
||||
std::unique_lock<std::mutex> lk(cv_m);
|
||||
auto key = dht::InfoHash::get("GLaDOs");
|
||||
|
||||
// If a peer send a value, the listen operation from the client
|
||||
// should retrieve this value
|
||||
dht::Value firstVal {"Hey! It's been a long time. How have you been?"};
|
||||
auto firstVal_data = firstVal.data;
|
||||
nodePeer.put(key, std::move(firstVal), [&](bool) {
|
||||
done = true;
|
||||
cv.notify_all();
|
||||
});
|
||||
cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; });
|
||||
done = false;
|
||||
|
||||
auto values = std::vector<dht::Blob>();
|
||||
nodeClient->listen(key, [&](const std::vector<std::shared_ptr<dht::Value>>& v, bool) {
|
||||
for (const auto& value : v)
|
||||
values.emplace_back(value->data);
|
||||
done = true;
|
||||
cv.notify_all();
|
||||
return true;
|
||||
});
|
||||
|
||||
cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; });
|
||||
done = false;
|
||||
// Here values should contains 2 values
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<int>(values.size()), 1);
|
||||
CPPUNIT_ASSERT(values.front() == firstVal_data);
|
||||
|
||||
// And the listen should retrieve futures values
|
||||
// All values
|
||||
dht::Value secondVal {"You're a monster"};
|
||||
auto secondVal_data = secondVal.data;
|
||||
nodePeer.put(key, std::move(secondVal));
|
||||
cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; });
|
||||
// Here values should contains 3 values
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<int>(values.size()), 2);
|
||||
CPPUNIT_ASSERT(values.back() == secondVal_data);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Sébastien Blin <sebastien.blin@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
|
||||
|
||||
// cppunit
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
#include <opendht/dhtrunner.h>
|
||||
#include <opendht/dht_proxy_server.h>
|
||||
|
||||
namespace test {
|
||||
|
||||
class DhtProxyTester : public CppUnit::TestFixture {
|
||||
CPPUNIT_TEST_SUITE(DhtProxyTester);
|
||||
CPPUNIT_TEST(testGetPut);
|
||||
CPPUNIT_TEST(testListen);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Method automatically called before each test by CppUnit
|
||||
* Init nodes
|
||||
*/
|
||||
void setUp();
|
||||
/**
|
||||
* Method automatically called after each test CppUnit
|
||||
*/
|
||||
void tearDown();
|
||||
/**
|
||||
* Test get and put methods
|
||||
*/
|
||||
void testGetPut();
|
||||
/**
|
||||
* Test listen
|
||||
*/
|
||||
void testListen();
|
||||
|
||||
private:
|
||||
dht::DhtRunner nodePeer {};
|
||||
|
||||
std::shared_ptr<dht::DhtRunner> nodeClient;
|
||||
std::shared_ptr<dht::DhtRunner> nodeProxy;
|
||||
std::unique_ptr<dht::DhtProxyServer> server;
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dhtrunnertester.h"
|
||||
|
||||
// std
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace test {
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(DhtRunnerTester);
|
||||
|
||||
void
|
||||
DhtRunnerTester::setUp() {
|
||||
node1.run(42222, {}, true);
|
||||
node2.run(42232, {}, true);
|
||||
node2.bootstrap(node1.getBound());
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunnerTester::tearDown() {
|
||||
node1.join();
|
||||
node2.join();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunnerTester::testConstructors() {
|
||||
CPPUNIT_ASSERT(node1.getBoundPort() == 42222);
|
||||
CPPUNIT_ASSERT(node2.getBoundPort() == 42232);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunnerTester::testGetPut() {
|
||||
auto key = dht::InfoHash::get("123");
|
||||
dht::Value val {"hey"};
|
||||
auto val_data = val.data;
|
||||
node1.put(key, std::move(val));
|
||||
auto vals = node2.get(key).get();
|
||||
CPPUNIT_ASSERT(not vals.empty());
|
||||
CPPUNIT_ASSERT(vals.front()->data == val_data);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunnerTester::testListen() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
// cppunit
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
#include <opendht/dhtrunner.h>
|
||||
|
||||
namespace test {
|
||||
|
||||
class DhtRunnerTester : public CppUnit::TestFixture {
|
||||
CPPUNIT_TEST_SUITE(DhtRunnerTester);
|
||||
CPPUNIT_TEST(testConstructors);
|
||||
CPPUNIT_TEST(testGetPut);
|
||||
CPPUNIT_TEST(testListen);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
dht::DhtRunner node1 {};
|
||||
dht::DhtRunner node2 {};
|
||||
public:
|
||||
/**
|
||||
* Method automatically called before each test by CppUnit
|
||||
*/
|
||||
void setUp();
|
||||
/**
|
||||
* Method automatically called after each test CppUnit
|
||||
*/
|
||||
void tearDown();
|
||||
/**
|
||||
* Test the differents behaviors of constructors
|
||||
*/
|
||||
void testConstructors();
|
||||
/**
|
||||
* Test get and put methods
|
||||
*/
|
||||
void testGetPut();
|
||||
/**
|
||||
* Test listen method
|
||||
*/
|
||||
void testListen();
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "infohashtester.h"
|
||||
|
||||
// std
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
// opendht
|
||||
#include "opendht/infohash.h"
|
||||
|
||||
namespace test {
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(InfoHashTester);
|
||||
|
||||
void
|
||||
InfoHashTester::setUp() {
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
InfoHashTester::testConstructors() {
|
||||
// Default constructor creates a null infohash
|
||||
auto nullHash = dht::InfoHash();
|
||||
CPPUNIT_ASSERT(nullHash.size() == 20);
|
||||
CPPUNIT_ASSERT(!nullHash);
|
||||
// Build from a uint8_t. if length to short, should get a null infohash
|
||||
uint8_t to_short[] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8};
|
||||
auto infohash = dht::InfoHash(to_short, 8);
|
||||
CPPUNIT_ASSERT(infohash.size() == 20);
|
||||
CPPUNIT_ASSERT_EQUAL(infohash.toString(),
|
||||
std::string("0000000000000000000000000000000000000000"));
|
||||
// Build from a uint8_t. if length is enough, data should contains the uint8_t
|
||||
uint8_t enough[] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa,
|
||||
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa};
|
||||
infohash = dht::InfoHash(enough, 20);
|
||||
CPPUNIT_ASSERT(infohash.size() == 20);
|
||||
const auto* data = infohash.data();
|
||||
for (auto i = 0; i < 20; ++i) {
|
||||
CPPUNIT_ASSERT_EQUAL(enough[i], data[i]);
|
||||
}
|
||||
// if too long, should be cutted to 20
|
||||
uint8_t tooLong[] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa,
|
||||
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb0};
|
||||
infohash = dht::InfoHash(tooLong, 21);
|
||||
CPPUNIT_ASSERT(infohash.size() == 20);
|
||||
const auto* data2 = infohash.data();
|
||||
for (auto i = 0; i < 20; ++i) {
|
||||
CPPUNIT_ASSERT_EQUAL(enough[i], data2[i]);
|
||||
}
|
||||
// Build from string
|
||||
auto infohashFromStr = dht::InfoHash("0102030405060708090A0102030405060708090A");
|
||||
CPPUNIT_ASSERT(infohashFromStr.size() == 20);
|
||||
const auto* dataStr = infohashFromStr.data();
|
||||
for (auto i = 0; i < 20; ++i) {
|
||||
CPPUNIT_ASSERT_EQUAL((int)dataStr[i], (int)data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InfoHashTester::testComperators() {
|
||||
auto nullHash = dht::InfoHash();
|
||||
auto minHash = dht::InfoHash("0000000000000000000000000000000000111110");
|
||||
auto maxHash = dht::InfoHash("0111110000000000000000000000000000000000");
|
||||
// operator ==
|
||||
CPPUNIT_ASSERT_EQUAL(minHash, minHash);
|
||||
CPPUNIT_ASSERT_EQUAL(minHash, dht::InfoHash("0000000000000000000000000000000000111110"));
|
||||
CPPUNIT_ASSERT(!(minHash == maxHash));
|
||||
// operator !=
|
||||
CPPUNIT_ASSERT(!(minHash != minHash));
|
||||
CPPUNIT_ASSERT(!(minHash != dht::InfoHash("0000000000000000000000000000000000111110")));
|
||||
CPPUNIT_ASSERT(minHash != maxHash);
|
||||
// operator<
|
||||
CPPUNIT_ASSERT(nullHash < minHash);
|
||||
CPPUNIT_ASSERT(nullHash < maxHash);
|
||||
CPPUNIT_ASSERT(minHash < maxHash);
|
||||
CPPUNIT_ASSERT(!(minHash < nullHash));
|
||||
CPPUNIT_ASSERT(!(maxHash < nullHash));
|
||||
CPPUNIT_ASSERT(!(maxHash < minHash));
|
||||
CPPUNIT_ASSERT(!(minHash < minHash));
|
||||
// bool()
|
||||
CPPUNIT_ASSERT(maxHash);
|
||||
CPPUNIT_ASSERT(!nullHash);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
InfoHashTester::testLowBit() {
|
||||
auto nullHash = dht::InfoHash();
|
||||
auto minHash = dht::InfoHash("0000000000000000000000000000000000000010");
|
||||
auto maxHash = dht::InfoHash("0100000000000000000000000000000000000000");
|
||||
CPPUNIT_ASSERT_EQUAL(nullHash.lowbit(), -1);
|
||||
CPPUNIT_ASSERT_EQUAL(minHash.lowbit(), 155);
|
||||
CPPUNIT_ASSERT_EQUAL(maxHash.lowbit(), 7);
|
||||
}
|
||||
|
||||
void
|
||||
InfoHashTester::testCommonBits() {
|
||||
auto nullHash = dht::InfoHash();
|
||||
auto minHash = dht::InfoHash("0000000000000000000000000000000000000010");
|
||||
auto maxHash = dht::InfoHash("0100000000000000000000000000000000000000");
|
||||
CPPUNIT_ASSERT_EQUAL(dht::InfoHash::commonBits(nullHash, nullHash), (unsigned)160);
|
||||
CPPUNIT_ASSERT_EQUAL(dht::InfoHash::commonBits(nullHash, minHash), (unsigned)155);
|
||||
CPPUNIT_ASSERT_EQUAL(dht::InfoHash::commonBits(nullHash, maxHash), (unsigned)7);
|
||||
CPPUNIT_ASSERT_EQUAL(dht::InfoHash::commonBits(minHash, maxHash), (unsigned)7);
|
||||
}
|
||||
|
||||
void
|
||||
InfoHashTester::testXorCmp() {
|
||||
auto nullHash = dht::InfoHash();
|
||||
auto minHash = dht::InfoHash("0000000000000000000000000000000000000010");
|
||||
auto maxHash = dht::InfoHash("0100000000000000000000000000000000000000");
|
||||
CPPUNIT_ASSERT_EQUAL(minHash.xorCmp(nullHash, maxHash), -1);
|
||||
CPPUNIT_ASSERT_EQUAL(minHash.xorCmp(maxHash, nullHash), 1);
|
||||
CPPUNIT_ASSERT_EQUAL(minHash.xorCmp(minHash, maxHash), -1);
|
||||
CPPUNIT_ASSERT_EQUAL(minHash.xorCmp(maxHash, minHash), 1);
|
||||
CPPUNIT_ASSERT_EQUAL(nullHash.xorCmp(minHash, maxHash), -1);
|
||||
CPPUNIT_ASSERT_EQUAL(nullHash.xorCmp(maxHash, minHash), 1);
|
||||
// Because hashes are circular in distance.
|
||||
CPPUNIT_ASSERT_EQUAL(maxHash.xorCmp(nullHash, minHash), -1);
|
||||
CPPUNIT_ASSERT_EQUAL(maxHash.xorCmp(minHash, nullHash), 1);
|
||||
}
|
||||
|
||||
void
|
||||
InfoHashTester::tearDown() {
|
||||
|
||||
}
|
||||
} // namespace test
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Sébastien Blin <sebastien.blin@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
|
||||
|
||||
// cppunit
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
namespace test {
|
||||
|
||||
class InfoHashTester : public CppUnit::TestFixture {
|
||||
CPPUNIT_TEST_SUITE(InfoHashTester);
|
||||
CPPUNIT_TEST(testConstructors);
|
||||
CPPUNIT_TEST(testComperators);
|
||||
CPPUNIT_TEST(testLowBit);
|
||||
CPPUNIT_TEST(testCommonBits);
|
||||
CPPUNIT_TEST(testXorCmp);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Method automatically called before each test by CppUnit
|
||||
*/
|
||||
void setUp();
|
||||
/**
|
||||
* Method automatically called after each test CppUnit
|
||||
*/
|
||||
void tearDown();
|
||||
/**
|
||||
* Test the differents behaviors of constructors
|
||||
*/
|
||||
void testConstructors();
|
||||
/**
|
||||
* Test compare operators
|
||||
*/
|
||||
void testComperators();
|
||||
/**
|
||||
* Test lowbit method
|
||||
*/
|
||||
void testLowBit();
|
||||
/**
|
||||
* Test commonBits method
|
||||
*/
|
||||
void testCommonBits();
|
||||
/**
|
||||
* Test xorCmp operators
|
||||
*/
|
||||
void testXorCmp();
|
||||
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2018 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
#include <cppunit/extensions/TestFactoryRegistry.h>
|
||||
#include <cppunit/ui/text/TestRunner.h>
|
||||
#include <cppunit/CompilerOutputter.h>
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
CppUnit::TestFactoryRegistry ®istry = CppUnit::TestFactoryRegistry::getRegistry();
|
||||
CppUnit::Test *suite = registry.makeTest();
|
||||
if (suite->countTestCases() == 0) {
|
||||
std::cout << "No test cases specified for suite" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
CppUnit::TextUi::TestRunner runner;
|
||||
runner.addTest(suite);
|
||||
auto result = runner.run() ? 0 : 1;
|
||||
return result;
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
bin_PROGRAMS = dhtnode dhtchat dhtscanner
|
||||
noinst_HEADERS = tools_common.h
|
||||
|
||||
AM_CPPFLAGS = -I../include @JsonCpp_CFLAGS@ @MsgPack_CFLAGS@
|
||||
AM_CPPFLAGS = -I../include
|
||||
|
||||
dhtnode_SOURCES = dhtnode.cpp
|
||||
dhtnode_LDFLAGS = -lopendht -lreadline -L@top_builddir@/src/.libs @Argon2_LDFLAGS@ @GnuTLS_LIBS@
|
||||
|
||||
+4
-9
@@ -45,7 +45,7 @@ void print_node_info(const DhtRunner& dht, const dht_params&) {
|
||||
void print_usage() {
|
||||
std::cout << "Usage: dhtchat [-n network_id] [-p local_port] [-b bootstrap_host[:port]]" << std::endl << std::endl;
|
||||
std::cout << "dhtchat, a simple OpenDHT command line chat client." << std::endl;
|
||||
std::cout << "Report bugs to: https://opendht.net" << std::endl;
|
||||
std::cout << "Report bugs to: http://opendht.net" << std::endl;
|
||||
}
|
||||
|
||||
int
|
||||
@@ -67,13 +67,6 @@ main(int argc, char **argv)
|
||||
if (not params.bootstrap.first.empty())
|
||||
dht.bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str());
|
||||
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
if (!params.proxyclient.empty()) {
|
||||
dht.setProxyServer(params.proxyclient);
|
||||
dht.enableProxy(true);
|
||||
}
|
||||
#endif //OPENDHT_PROXY_CLIENT
|
||||
|
||||
print_node_info(dht, params);
|
||||
std::cout << " type 'c {hash}' to join a channel" << std::endl << std::endl;
|
||||
|
||||
@@ -95,6 +88,8 @@ main(int argc, char **argv)
|
||||
if (line.empty())
|
||||
continue;
|
||||
|
||||
static constexpr dht::InfoHash INVALID_ID {};
|
||||
|
||||
std::istringstream iss(line);
|
||||
std::string op, idstr;
|
||||
iss >> op;
|
||||
@@ -104,7 +99,7 @@ main(int argc, char **argv)
|
||||
else if (op == "c") {
|
||||
iss >> idstr;
|
||||
room = InfoHash(idstr);
|
||||
if (not room) {
|
||||
if (room == INVALID_ID) {
|
||||
room = InfoHash::get(idstr);
|
||||
std::cout << "Joining h(" << idstr << ") = " << room << std::endl;
|
||||
}
|
||||
|
||||
+42
-192
@@ -1,9 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 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>
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@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
|
||||
@@ -30,9 +29,9 @@ extern "C" {
|
||||
using namespace dht;
|
||||
|
||||
void print_usage() {
|
||||
std::cout << "Usage: dhtnode [-v [-l logfile]] [-i] [-d] [-n network_id] [-p local_port] [-b bootstrap_host[:port]] [--proxyserver local_port]" << std::endl << std::endl;
|
||||
std::cout << "Usage: dhtnode [-v [-l logfile]] [-i] [-d] [-n network_id] [-p local_port] [-b bootstrap_host[:port]]" << std::endl << std::endl;
|
||||
std::cout << "dhtnode, a simple OpenDHT command line node runner." << std::endl;
|
||||
std::cout << "Report bugs to: https://opendht.net" << std::endl;
|
||||
std::cout << "Report bugs to: http://opendht.net" << std::endl;
|
||||
}
|
||||
|
||||
void print_id_req() {
|
||||
@@ -40,7 +39,7 @@ void print_id_req() {
|
||||
}
|
||||
|
||||
void print_node_info(const std::shared_ptr<DhtRunner>& dht, const dht_params& params) {
|
||||
std::cout << "OpenDht node " << dht->getNodeId() << " running on port " << dht->getBoundPort() << std::endl;
|
||||
std::cout << "OpenDht node " << dht->getNodeId() << " running on port " << dht->getBound().toString() << std::endl;
|
||||
if (params.generate_identity)
|
||||
std::cout << "Public key ID " << dht->getId() << std::endl;
|
||||
}
|
||||
@@ -56,56 +55,23 @@ void print_help() {
|
||||
<< " ll Print basic information and stats about the current node." << std::endl
|
||||
<< " ls [key] Print basic information about current search(es)." << std::endl
|
||||
<< " ld [key] Print basic information about currenty stored values on this node (or key)." << std::endl
|
||||
<< " lr Print the full current routing table of this node." << std::endl;
|
||||
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
std::cout << std::endl << "Operations with the proxy:" << std::endl
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
<< " pst [port] <pushServer> Start the proxy interface on port." << std::endl
|
||||
#else
|
||||
<< " pst [port] Start the proxy interface on port." << std::endl
|
||||
#endif // OPENDHT_PUSH_NOTIFICATIONS
|
||||
<< " psp [port] Stop the proxy interface on port." << std::endl;
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
std::cout << std::endl << "Operations with the proxy:" << std::endl
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
<< " stt [server_address] <device_key> Start the proxy client." << std::endl
|
||||
<< " rs [token] Resubscribe to opendht." << std::endl
|
||||
<< " rp [token] Inject a push notification in Opendht." << std::endl
|
||||
#else
|
||||
<< " stt [server_address] Start the proxy client." << std::endl
|
||||
#endif // OPENDHT_PUSH_NOTIFICATIONS
|
||||
<< " stp Stop the proxy client." << std::endl;
|
||||
#endif //OPENDHT_PROXY_CLIENT
|
||||
<< " lr Print the full current routing table of this node" << std::endl;
|
||||
|
||||
std::cout << std::endl << "Operations on the DHT:" << std::endl
|
||||
<< " b <ip:port> Ping potential node at given IP address/port." << std::endl
|
||||
<< " b <ip:port> Ping potential node at given IP address/port." << std::endl
|
||||
<< " g <key> Get values at <key>." << std::endl
|
||||
<< " l <key> Listen for value changes at <key>." << std::endl
|
||||
<< " cl <key> <token> Cancel listen for <token> and <key>." << std::endl
|
||||
<< " p <key> <str> Put string value at <key>." << std::endl
|
||||
<< " pp <key> <str> Put string value at <key> (persistent version)." << std::endl
|
||||
<< " cpp <key> <id> Cancel persistent put operation for <key> and value <id>." << std::endl
|
||||
<< " s <key> <str> Put string value at <key>, signed with our generated private key." << std::endl
|
||||
<< " e <key> <dest> <str> Put string value at <key>, encrypted for <dest> with its public key (if found)." << std::endl
|
||||
<< " cc Trigger connectivity changed signal." << std::endl;
|
||||
|
||||
#ifdef OPENDHT_INDEXATION
|
||||
<< " e <key> <dest> <str> Put string value at <key>, encrypted for <dest> with its public key (if found)." << std::endl;
|
||||
std::cout << std::endl << "Indexation operations on the DHT:" << std::endl
|
||||
<< " il <name> <key> [exact match] Lookup the index named <name> with the key <key>." << std::endl
|
||||
<< " Set [exact match] to 'false' for inexact match lookup." << std::endl
|
||||
<< " ii <name> <key> <value> Inserts the value <value> under the key <key> in the index named <name>." << std::endl
|
||||
<< std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
, std::map<in_port_t, std::unique_ptr<DhtProxyServer>>& proxies
|
||||
#endif
|
||||
)
|
||||
void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params)
|
||||
{
|
||||
print_node_info(dht, params);
|
||||
std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl << std::endl;
|
||||
@@ -115,9 +81,7 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params
|
||||
using_history();
|
||||
#endif
|
||||
|
||||
#ifdef OPENDHT_INDEXATION
|
||||
std::map<std::string, indexation::Pht> indexes;
|
||||
#endif
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -127,7 +91,7 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params
|
||||
break;
|
||||
|
||||
std::istringstream iss(line);
|
||||
std::string op, idstr, value, index, keystr, pushServer, deviceKey;
|
||||
std::string op, idstr, value, index, keystr;
|
||||
iss >> op;
|
||||
|
||||
if (op == "x" || op == "exit" || op == "quit") {
|
||||
@@ -141,12 +105,6 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params
|
||||
std::cout << dht->getNodesStats(AF_INET).toString() << std::endl;
|
||||
std::cout << "IPv6 stats:" << std::endl;
|
||||
std::cout << dht->getNodesStats(AF_INET6).toString() << std::endl;
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
for (const auto& proxy : proxies) {
|
||||
std::cout << "Stats for proxy on port " << proxy.first << std::endl;
|
||||
std::cout << " " << proxy.second->stats().toString() << std::endl;
|
||||
}
|
||||
#endif
|
||||
continue;
|
||||
} else if (op == "lr") {
|
||||
std::cout << "IPv4 routing table:" << std::endl;
|
||||
@@ -157,18 +115,20 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params
|
||||
} else if (op == "ld") {
|
||||
iss >> idstr;
|
||||
InfoHash filter(idstr);
|
||||
if (filter)
|
||||
std::cout << dht->getStorageLog(filter) << std::endl;
|
||||
else
|
||||
if (filter == InfoHash{})
|
||||
std::cout << dht->getStorageLog() << std::endl;
|
||||
else
|
||||
std::cout << dht->getStorageLog(filter) << std::endl;
|
||||
continue;
|
||||
} else if (op == "ls") {
|
||||
iss >> idstr;
|
||||
InfoHash filter(idstr);
|
||||
if (filter)
|
||||
std::cout << dht->getSearchLog(filter) << std::endl;
|
||||
else
|
||||
if (filter == InfoHash{}) {
|
||||
std::cout << "Searches:" << std::endl;
|
||||
std::cout << dht->getSearchesLog() << std::endl;
|
||||
} else {
|
||||
std::cout << dht->getSearchLog(filter) << std::endl;
|
||||
}
|
||||
continue;
|
||||
} else if (op == "la") {
|
||||
std::cout << "Reported public addresses:" << std::endl;
|
||||
@@ -180,8 +140,11 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params
|
||||
iss >> idstr;
|
||||
try {
|
||||
auto addr = splitPort(idstr);
|
||||
if (not addr.first.empty() and addr.second.empty())
|
||||
addr.second = std::to_string(DHT_DEFAULT_PORT);
|
||||
if (not addr.first.empty() and addr.second.empty()){
|
||||
std::stringstream ss;
|
||||
ss << DHT_DEFAULT_PORT;
|
||||
addr.second = ss.str();
|
||||
}
|
||||
dht->bootstrap(addr.first.c_str(), addr.second.c_str());
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
@@ -197,67 +160,19 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params
|
||||
log::disableLogging(*dht);
|
||||
dht->setLogFilter(filter);
|
||||
continue;
|
||||
} else if (op == "cc") {
|
||||
dht->connectivityChanged();
|
||||
continue;
|
||||
}
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
else if (op == "pst") {
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
iss >> idstr >> pushServer;
|
||||
#else
|
||||
iss >> idstr;
|
||||
#endif // OPENDHT_PUSH_NOTIFICATIONS
|
||||
try {
|
||||
unsigned int port = std::stoi(idstr);
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
proxies.emplace(port, std::unique_ptr<DhtProxyServer>(new DhtProxyServer(dht, port, pushServer)));
|
||||
#else
|
||||
proxies.emplace(port, std::unique_ptr<DhtProxyServer>(new DhtProxyServer(dht, port)));
|
||||
#endif // OPENDHT_PUSH_NOTIFICATIONS
|
||||
} catch (...) { }
|
||||
continue;
|
||||
} else if (op == "psp") {
|
||||
iss >> idstr;
|
||||
try {
|
||||
auto it = proxies.find(std::stoi(idstr));
|
||||
if (it != proxies.end())
|
||||
proxies.erase(it);
|
||||
} catch (...) { }
|
||||
continue;
|
||||
}
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
else if (op == "stt") {
|
||||
dht->enableProxy(true);
|
||||
continue;
|
||||
} else if (op == "stp") {
|
||||
dht->enableProxy(false);
|
||||
continue;
|
||||
}
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
else if (op == "rp") {
|
||||
iss >> value;
|
||||
dht->pushNotificationReceived({{"to", "dhtnode"}, {"token", value}});
|
||||
continue;
|
||||
}
|
||||
#endif // OPENDHT_PUSH_NOTIFICATIONS
|
||||
#endif //OPENDHT_PROXY_CLIENT
|
||||
|
||||
if (op.empty())
|
||||
continue;
|
||||
|
||||
static const std::set<std::string> VALID_OPS {"g", "l", "cl", "il", "ii", "p", "pp", "cpp", "s", "e", "a", "q"};
|
||||
static const std::set<std::string> VALID_OPS {"g", "l", "il", "ii", "p", "s", "e", "a"};
|
||||
if (VALID_OPS.find(op) == VALID_OPS.cend()) {
|
||||
std::cout << "Unknown command: " << op << std::endl;
|
||||
std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl;
|
||||
continue;
|
||||
}
|
||||
dht::InfoHash id;
|
||||
|
||||
if (false) {}
|
||||
#ifdef OPENDHT_INDEXATION
|
||||
else if (op == "il" or op == "ii") {
|
||||
if (op == "il" or op == "ii") {
|
||||
// Pht syntax
|
||||
iss >> index >> keystr;
|
||||
auto new_index = std::find_if(indexes.begin(), indexes.end(),
|
||||
@@ -265,7 +180,7 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params
|
||||
return i.first == index;
|
||||
}) == indexes.end();
|
||||
if (not index.size()) {
|
||||
std::cerr << "You must enter the index name." << std::endl;
|
||||
std::cout << "You must enter the index name." << std::endl;
|
||||
continue;
|
||||
} else if (new_index) {
|
||||
using namespace dht::indexation;
|
||||
@@ -279,21 +194,15 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params
|
||||
} catch (std::invalid_argument& e) { std::cout << e.what() << std::endl; }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
// Dht syntax
|
||||
iss >> idstr;
|
||||
id = dht::InfoHash(idstr);
|
||||
if (not id) {
|
||||
if (idstr.empty()) {
|
||||
std::cerr << "Syntax error: invalid InfoHash." << std::endl;
|
||||
continue;
|
||||
}
|
||||
id = InfoHash::get(idstr);
|
||||
std::cout << "Using h(" << idstr << ") = " << id << std::endl;
|
||||
}
|
||||
InfoHash h {idstr};
|
||||
if (not isInfoHash(h))
|
||||
continue;
|
||||
}
|
||||
|
||||
dht::InfoHash id {idstr};
|
||||
// Dht
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
if (op == "g") {
|
||||
@@ -327,26 +236,11 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params
|
||||
else if (op == "l") {
|
||||
std::string rem;
|
||||
std::getline(iss, rem);
|
||||
auto token = dht->listen(id, [](const std::vector<std::shared_ptr<Value>>& values, bool expired) {
|
||||
std::cout << "Listen: found " << values.size() << " values" << (expired ? " expired" : "") << std::endl;
|
||||
for (const auto& value : values)
|
||||
std::cout << "\t" << *value << std::endl;
|
||||
dht->listen(id, [](std::shared_ptr<Value> value) {
|
||||
std::cout << "Listen: found value:" << std::endl;
|
||||
std::cout << "\t" << *value << std::endl;
|
||||
return true;
|
||||
}, {}, dht::Where {std::move(rem)});
|
||||
auto t = token.get();
|
||||
std::cout << "Listening, token: " << t << std::endl;
|
||||
}
|
||||
if (op == "cl") {
|
||||
std::string rem;
|
||||
iss >> rem;
|
||||
size_t token;
|
||||
try {
|
||||
token = std::stoul(rem);
|
||||
} catch(...) {
|
||||
std::cerr << "Syntax: cl [key] [token]" << std::endl;
|
||||
continue;
|
||||
}
|
||||
dht->cancelListen(id, token);
|
||||
}
|
||||
else if (op == "p") {
|
||||
std::string v;
|
||||
@@ -359,25 +253,6 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params
|
||||
std::cout << "Put: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
|
||||
});
|
||||
}
|
||||
else if (op == "pp") {
|
||||
std::string v;
|
||||
iss >> v;
|
||||
auto value = std::make_shared<dht::Value>(
|
||||
dht::ValueType::USER_DATA.id,
|
||||
std::vector<uint8_t> {v.begin(), v.end()}
|
||||
);
|
||||
dht->put(id, value, [start,value](bool ok) {
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto flags(std::cout.flags());
|
||||
std::cout << "Put: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s). Value ID: " << std::hex << value->id << std::endl;
|
||||
std::cout.flags(flags);
|
||||
}, time_point::max(), true);
|
||||
}
|
||||
else if (op == "cpp") {
|
||||
std::string rem;
|
||||
iss >> rem;
|
||||
dht->cancelPut(id, std::stoul(rem, nullptr, 16));
|
||||
}
|
||||
else if (op == "s") {
|
||||
if (not params.generate_identity) {
|
||||
print_id_req();
|
||||
@@ -417,7 +292,6 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params
|
||||
std::cout << "Announce: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
|
||||
});
|
||||
}
|
||||
#ifdef OPENDHT_INDEXATION
|
||||
else if (op == "il") {
|
||||
std::string exact_match;
|
||||
iss >> exact_match;
|
||||
@@ -463,7 +337,6 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params
|
||||
catch (std::invalid_argument& e) { std::cout << e.what() << std::endl; }
|
||||
catch (std::out_of_range& e) { }
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::cout << std::endl << "Stopping node..." << std::endl;
|
||||
@@ -484,7 +357,6 @@ main(int argc, char **argv)
|
||||
print_usage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (params.daemonize) {
|
||||
daemonize();
|
||||
} else if (params.service) {
|
||||
@@ -494,20 +366,13 @@ main(int argc, char **argv)
|
||||
dht::crypto::Identity crt {};
|
||||
if (params.generate_identity) {
|
||||
auto ca_tmp = dht::crypto::generateEcIdentity("DHT Node CA");
|
||||
crt = dht::crypto::generateIdentity("DHT Node", ca_tmp);
|
||||
crt = dht::crypto::generateEcIdentity("DHT Node", ca_tmp);
|
||||
}
|
||||
|
||||
dht::DhtRunner::Config config {};
|
||||
config.dht_config.node_config.network = params.network;
|
||||
config.dht_config.node_config.maintain_storage = false;
|
||||
config.dht_config.id = crt;
|
||||
config.threaded = true;
|
||||
config.proxy_server = params.proxyclient;
|
||||
config.push_node_id = "dhtnode";
|
||||
if (not params.proxyclient.empty())
|
||||
dht->setPushNotificationToken(params.devicekey);
|
||||
|
||||
dht->run(params.port, config);
|
||||
dht->run(params.port, crt, true, params.network);
|
||||
dht->setOnStatusChanged([](dht::NodeStatus status4, dht::NodeStatus status6){
|
||||
std::cout << "Node status changed : IPv4:" << (unsigned)status4 << " IPv6:" << (unsigned)status6 << std::endl;
|
||||
});
|
||||
|
||||
if (params.log) {
|
||||
if (params.syslog or (params.daemonize and params.logfile.empty()))
|
||||
@@ -523,26 +388,11 @@ main(int argc, char **argv)
|
||||
dht->bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str());
|
||||
}
|
||||
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
std::map<in_port_t, std::unique_ptr<DhtProxyServer>> proxies;
|
||||
#endif
|
||||
if (params.proxyserver != 0) {
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
proxies.emplace(params.proxyserver, std::unique_ptr<DhtProxyServer>(new DhtProxyServer(dht, params.proxyserver, params.pushserver)));
|
||||
#else
|
||||
std::cerr << "DHT proxy server requested but OpenDHT built without proxy server support." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (params.daemonize or params.service)
|
||||
if (params.daemonize or params.service) {
|
||||
while (runner.wait());
|
||||
else
|
||||
cmd_loop(dht, params
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
, proxies
|
||||
#endif
|
||||
);
|
||||
} else {
|
||||
cmd_loop(dht, params);
|
||||
}
|
||||
|
||||
} catch(const std::exception&e) {
|
||||
std::cerr << std::endl << e.what() << std::endl;
|
||||
|
||||
@@ -32,7 +32,7 @@ using namespace dht;
|
||||
void print_usage() {
|
||||
std::cout << "Usage: dhtscanner [-n network_id] [-p local_port] [-b bootstrap_host[:port]]" << std::endl << std::endl;
|
||||
std::cout << "dhtscanner, a simple OpenDHT command line utility generating scan result the network." << std::endl;
|
||||
std::cout << "Report bugs to: https://opendht.net" << std::endl;
|
||||
std::cout << "Report bugs to: http://opendht.net" << std::endl;
|
||||
}
|
||||
|
||||
struct snode_compare {
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>OpenDHT tester</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" />
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript">"use strict";
|
||||
let onGet;
|
||||
let onPut;
|
||||
let setServer;
|
||||
const valueGetElement = function(o) {
|
||||
const d = window.atob(o.data);
|
||||
return d;
|
||||
};
|
||||
$(function() {
|
||||
let request = undefined;
|
||||
let server;
|
||||
const getTools = $("#getTools");
|
||||
const getBtn = $("#getBtn");
|
||||
const getDropdown = $("#getDropdown");
|
||||
const listenBtn = $("#listenBtn").click(function(){onGet('LISTEN');});
|
||||
const setGetRequest = function() {
|
||||
getBtn.button('loading');
|
||||
getStopBtn.appendTo(getTools);
|
||||
getDropdown.hide();
|
||||
}
|
||||
const clearGetRequest = function() {
|
||||
if (request === undefined)
|
||||
return;
|
||||
request.abort();
|
||||
request = undefined;
|
||||
getStopBtn.detach();
|
||||
getDropdown.show();
|
||||
getBtn.button('reset');
|
||||
}
|
||||
const getStopBtn = $("#getStopBtn").detach().click(clearGetRequest);
|
||||
const putBtn = $("#putBtn");
|
||||
const result = $("#dhtResult");
|
||||
const group = $('<ul class="list-group"/>').appendTo(result);
|
||||
onGet = function (method) {
|
||||
if (request !== undefined)
|
||||
return false;
|
||||
const input = $("#getKey").val();
|
||||
group.empty();
|
||||
let lastAppended = 0;
|
||||
let start = new Date().getTime();
|
||||
request = new XMLHttpRequest();
|
||||
request.onreadystatechange = function(event) {
|
||||
if (this.readyState >= XMLHttpRequest.LOADING) {
|
||||
if (this.readyState == XMLHttpRequest.DONE) {
|
||||
clearGetRequest();
|
||||
}
|
||||
if (this.status === 200) {
|
||||
const elements = this.responseText.split("\n");
|
||||
const elementsLength = elements.length;
|
||||
const now = new Date().getTime();
|
||||
for (let i = lastAppended; i < elementsLength; i++) {
|
||||
const element = elements[i];
|
||||
if (!element || element.length == 0)
|
||||
return;
|
||||
const o = JSON.parse(element);
|
||||
if (o.expired) {
|
||||
$('#value'+o.id).slideUp(100, (e) => $(e.target).remove());
|
||||
} else {
|
||||
const d = window.atob(o.data);
|
||||
const delay = Math.max(0, start-now);
|
||||
$('<li class="list-group-item" id="value'+o.id+'"/>').append(valueGetElement(o)).appendTo(group).hide().delay(delay).slideDown(100);
|
||||
lastAppended = i+1;
|
||||
start = Math.max(start, now)+25;
|
||||
}
|
||||
}
|
||||
} else if (this.status !== 0) {
|
||||
group.empty().append($('<li class="list-group-item list-group-item-danger"/>').text("Error loading content: " + this.statusText));
|
||||
}
|
||||
}
|
||||
};
|
||||
request.onerror = function(event) {
|
||||
clearGetRequest();
|
||||
group.empty().append($('<li class="list-group-item list-group-item-danger"/>').text("Error loading content."));
|
||||
};
|
||||
request.open(method, server + input, true);
|
||||
request.send(null);
|
||||
setGetRequest();
|
||||
return false;
|
||||
};
|
||||
|
||||
onPut = function( ) {
|
||||
const key = $("#getKey").val();
|
||||
const value = $("#putValue").val();
|
||||
$.ajax({
|
||||
url: server + key,
|
||||
type: 'POST',
|
||||
data: JSON.stringify({
|
||||
data:window.btoa(value)
|
||||
}),
|
||||
contentType: 'application/json; charset=utf-8',
|
||||
dataType: 'json',
|
||||
success: function( result ) {
|
||||
putBtn.button('reset');
|
||||
//$('<li class="list-group-item list-group-item-success"/>').append(valueGetElement(result)).appendTo(group.empty());
|
||||
},
|
||||
error: function(result) {
|
||||
putBtn.button('reset');
|
||||
group.empty().append($('<li class="list-group-item list-group-item-danger"/>').text(result.statusText));
|
||||
}
|
||||
});
|
||||
putBtn.button('loading');
|
||||
return false;
|
||||
};
|
||||
|
||||
const serverValue = $("#serverValue");
|
||||
const serverStatus = $("#serverStatus");
|
||||
const serverBtn = $("#serverBtn");
|
||||
setServer = function(event) {
|
||||
server = 'http://'+serverValue.val() + '/';
|
||||
serverStatus.empty();
|
||||
serverBtn.button('loading');
|
||||
$.getJSON(server, function(data){
|
||||
$('<span><b>Node</b> '+data.node_id+'</span>').appendTo(serverStatus).hide().fadeIn();
|
||||
}).fail(function(error) {
|
||||
serverStatus.html("<div class='alert alert-danger' style='margin-bottom: 0px;'><span class='glyphicon glyphicon-remove' aria-hidden='true'></span> Can't access node</div>");
|
||||
}).always(function(error) {
|
||||
serverBtn.button('reset');
|
||||
});
|
||||
return false;
|
||||
};
|
||||
setServer();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container" style="max-width: 730px;">
|
||||
<header class="page-header">
|
||||
<div class="row">
|
||||
<div class="col-sm-5">
|
||||
<h1>OpenDHT tester</h1>
|
||||
</div>
|
||||
<div class="col-sm-7">
|
||||
<div class="well well-sm" style="margin-top:10px; margin-bottom:0px;">
|
||||
<form id="serverForm" class="form-inline" onsubmit="return setServer();" style="margin-bottom:4px;">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="serverValue" placeholder="Proxy server" value="127.0.0.1:8080"/>
|
||||
<span class="input-group-btn">
|
||||
<button id="serverBtn" type="submit" class="btn btn-default" data-loading-text="<i class='fa fa-circle-o-notch fa-spin'></i>"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span></button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
<div id="serverStatus"><i class='fa fa-circle-o-notch fa-spin'></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="panel panel-default" id="dhtResult">
|
||||
<div class="panel-heading">
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<form class="form-inline" onsubmit="return onGet('GET');">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="getKey" placeholder="Key" aria-label="Key" />
|
||||
<span class="input-group-btn" id="getTools">
|
||||
<button id="getBtn" class="btn btn-default" data-loading-text="<i class='fa fa-circle-o-notch fa-spin'></i>" type="submit">Get</button>
|
||||
<button id="getDropdown"type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <span class="caret"></span> <span class="sr-only">Toggle Dropdown</span> </button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a id="listenBtn" href="#">Listen</a></li>
|
||||
</ul>
|
||||
<button id="getStopBtn" class="btn btn-default" type="submit"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<form class="form-inline" onsubmit="return onPut();">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control input-group-input" id="putValue" placeholder="Value" />
|
||||
<span class="input-group-btn">
|
||||
<button id="putBtn" type="submit" class="btn btn-default" data-loading-text="<i class='fa fa-circle-o-notch fa-spin'></i> Loading">Put</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
+28
-31
@@ -2,7 +2,6 @@
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Author: Sébastien Blin <sebastien.blin@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
|
||||
@@ -45,6 +44,29 @@
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
|
||||
/**
|
||||
* Split "[host]:port" or "host:port" to pair<"host", "port">.
|
||||
*/
|
||||
std::pair<std::string, std::string>
|
||||
splitPort(const std::string& s) {
|
||||
if (s.empty())
|
||||
return {};
|
||||
if (s[0] == '[') {
|
||||
std::size_t closure = s.find_first_of(']');
|
||||
std::size_t found = s.find_last_of(':');
|
||||
if (closure == std::string::npos)
|
||||
return {s, ""};
|
||||
if (found == std::string::npos or found < closure)
|
||||
return {s.substr(1,closure-1), ""};
|
||||
return {s.substr(1,closure-1), s.substr(found+1)};
|
||||
}
|
||||
std::size_t found = s.find_last_of(':');
|
||||
std::size_t first = s.find_first_of(':');
|
||||
if (found == std::string::npos or found != first)
|
||||
return {s, ""};
|
||||
return {s.substr(0,found), s.substr(found+1)};
|
||||
}
|
||||
|
||||
/*
|
||||
* The mapString shall have the following format:
|
||||
*
|
||||
@@ -67,7 +89,6 @@ std::map<std::string, std::string> parseStringMap(std::string mapString) {
|
||||
return map;
|
||||
}
|
||||
|
||||
#ifdef OPENDHT_INDEXATION
|
||||
dht::indexation::Pht::Key createPhtKey(std::map<std::string, std::string> pht_key_str_map) {
|
||||
dht::indexation::Pht::Key pht_key;
|
||||
for (auto f : pht_key_str_map) {
|
||||
@@ -76,10 +97,11 @@ dht::indexation::Pht::Key createPhtKey(std::map<std::string, std::string> pht_ke
|
||||
}
|
||||
return pht_key;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool isInfoHash(const dht::InfoHash& h) {
|
||||
if (not h) {
|
||||
bool isInfoHash(dht::InfoHash& h) {
|
||||
static constexpr dht::InfoHash INVALID_ID {};
|
||||
|
||||
if (h == INVALID_ID) {
|
||||
std::cout << "Syntax error: invalid InfoHash." << std::endl;
|
||||
return false;
|
||||
}
|
||||
@@ -99,10 +121,6 @@ struct dht_params {
|
||||
bool daemonize {false};
|
||||
bool service {false};
|
||||
std::pair<std::string, std::string> bootstrap {};
|
||||
in_port_t proxyserver {0};
|
||||
std::string proxyclient {};
|
||||
std::string pushserver {};
|
||||
std::string devicekey {};
|
||||
};
|
||||
|
||||
static const constexpr struct option long_options[] = {
|
||||
@@ -116,10 +134,6 @@ static const constexpr struct option long_options[] = {
|
||||
{"service", no_argument , nullptr, 's'},
|
||||
{"logfile", required_argument, nullptr, 'l'},
|
||||
{"syslog", no_argument , nullptr, 'L'},
|
||||
{"proxyserver",required_argument, nullptr, 'S'},
|
||||
{"proxyclient",required_argument, nullptr, 'C'},
|
||||
{"pushserver", required_argument, nullptr, 'P'},
|
||||
{"devicekey", required_argument, nullptr, 'D'},
|
||||
{nullptr, 0 , nullptr, 0}
|
||||
};
|
||||
|
||||
@@ -137,28 +151,11 @@ parseArgs(int argc, char **argv) {
|
||||
std::cout << "Invalid port: " << port_arg << std::endl;
|
||||
}
|
||||
break;
|
||||
case 'S': {
|
||||
int port_arg = atoi(optarg);
|
||||
if (port_arg >= 0 && port_arg < 0x10000)
|
||||
params.proxyserver = port_arg;
|
||||
else
|
||||
std::cout << "Invalid port: " << port_arg << std::endl;
|
||||
}
|
||||
break;
|
||||
case 'P':
|
||||
params.pushserver = optarg;
|
||||
break;
|
||||
case 'C':
|
||||
params.proxyclient = optarg;
|
||||
break;
|
||||
case 'D':
|
||||
params.devicekey = optarg;
|
||||
break;
|
||||
case 'n':
|
||||
params.network = strtoul(optarg, nullptr, 0);
|
||||
break;
|
||||
case 'b':
|
||||
params.bootstrap = dht::splitPort((optarg[0] == '=') ? optarg+1 : optarg);
|
||||
params.bootstrap = splitPort((optarg[0] == '=') ? optarg+1 : optarg);
|
||||
if (not params.bootstrap.first.empty() and params.bootstrap.second.empty()) {
|
||||
params.bootstrap.second = std::to_string(DHT_DEFAULT_PORT);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user