mirror of
https://github.com/savoirfairelinux/opendht.git
synced 2026-11-09 12:57:03 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5593107dc4 | ||
|
|
679ac3badb | ||
|
|
419fc24f1f | ||
|
|
2acdc1f1aa | ||
|
|
631c5f91ec | ||
|
|
81213d9d50 | ||
|
|
5c87e81c42 | ||
|
|
aa0fbbce76 | ||
|
|
ac06dbe45f | ||
|
|
4a97265b22 | ||
|
|
5a75b54e04 | ||
|
|
64185d74d1 | ||
|
|
c16b73b056 | ||
|
|
775eb2e599 | ||
|
|
376486e64f | ||
|
|
63013bf374 | ||
|
|
488aa771a9 | ||
|
|
6bc2f19a36 | ||
|
|
2dd94fbfe8 | ||
|
|
be2b9fd7dc | ||
|
|
9d4e6e0984 | ||
|
|
4856a6b424 | ||
|
|
7df375fc75 | ||
|
|
63e0c1266a | ||
|
|
1a54aff7bc | ||
|
|
f2b9f8eeda | ||
|
|
e9f007025f | ||
|
|
6ce1531e1f | ||
|
|
84a45c4f91 | ||
|
|
e20ae56cab | ||
|
|
4ce0168916 | ||
|
|
024dd42e5a | ||
|
|
98ec789526 | ||
|
|
3bb5e7ee12 | ||
|
|
8c529b7d7f | ||
|
|
be7494961c | ||
|
|
bdb0bef0ee | ||
|
|
73f26e120d | ||
|
|
9f4b16e4f5 | ||
|
|
323ad774e5 | ||
|
|
d0bcd39341 | ||
|
|
42125367d7 | ||
|
|
d87a4ef28c | ||
|
|
a3f4388556 | ||
|
|
97ee4b6fea | ||
|
|
0abd288958 | ||
|
|
131073e211 | ||
|
|
078c4f064a | ||
|
|
f17a4e66f5 | ||
|
|
dd13ebb6d1 | ||
|
|
1c45f34262 | ||
|
|
a82f575232 | ||
|
|
1d84b34cf6 | ||
|
|
bedb51bf52 | ||
|
|
1b9e9452c4 | ||
|
|
3bdaf2e20b | ||
|
|
a1d67b881b | ||
|
|
c40750f6e4 | ||
|
|
63b40d3e3a | ||
|
|
21f5918af8 | ||
|
|
6989236985 | ||
|
|
1efa120010 | ||
|
|
a99ca1cf05 | ||
|
|
8fc8209731 | ||
|
|
6f3eafc5ee | ||
|
|
83d1cb97c8 | ||
|
|
477799a2b9 | ||
|
|
7e8c9ce944 | ||
|
|
6decb6609a | ||
|
|
f1e7cd897d | ||
|
|
9bfaa224bc | ||
|
|
5657661249 | ||
|
|
e69d61d8c7 | ||
|
|
729c055f5b | ||
|
|
61f91d626b | ||
|
|
732d10713d | ||
|
|
15e5fa433b | ||
|
|
43ff0e9e52 | ||
|
|
cf32e78f1b | ||
|
|
f170726b4b | ||
|
|
ebb20bdf0c | ||
|
|
2ca7302352 | ||
|
|
8a123f9f68 | ||
|
|
7e92eb6192 | ||
|
|
56edf1720c | ||
|
|
7c32dc49b1 | ||
|
|
5cb5ab019f | ||
|
|
42fa09a6fa | ||
|
|
673884a409 | ||
|
|
ec6296e44b | ||
|
|
b96d0de8a2 | ||
|
|
98c4e39ed1 | ||
|
|
9c483880cb | ||
|
|
ae7e298946 | ||
|
|
7197c1fbb6 | ||
|
|
c10e04d526 | ||
|
|
ee883492ee | ||
|
|
2ed99db9b1 | ||
|
|
c11dcf5d76 | ||
|
|
4132e990ec | ||
|
|
76eba8c235 | ||
|
|
e1ac26773b | ||
|
|
339ea70230 | ||
|
|
943a39178c | ||
|
|
ae0cb58a5c | ||
|
|
dd116d8ad8 | ||
|
|
74c3c5bc87 | ||
|
|
b6bfa14872 | ||
|
|
50a769c8fb | ||
|
|
39a254ce9f | ||
|
|
3aa720fe1f | ||
|
|
7908a1d2ea | ||
|
|
37d8481e28 |
No files matched your search
+79
-33
@@ -1,42 +1,55 @@
|
||||
cmake_minimum_required (VERSION 2.8.6)
|
||||
cmake_minimum_required (VERSION 3.1)
|
||||
project (opendht)
|
||||
set (opendht_VERSION_MAJOR 1)
|
||||
set (opendht_VERSION_MINOR 3.2)
|
||||
set (opendht_VERSION_MINOR 4.0)
|
||||
set (opendht_VERSION ${opendht_VERSION_MAJOR}.${opendht_VERSION_MINOR})
|
||||
set (PACKAGE_VERSION ${opendht_VERSION})
|
||||
set (VERSION "${opendht_VERSION}")
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
||||
|
||||
include(GNUInstallDirs)
|
||||
set (prefix ${CMAKE_INSTALL_PREFIX})
|
||||
set (exec_prefix "\${prefix}")
|
||||
set (libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
|
||||
set (includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
|
||||
# Options
|
||||
option (OPENDHT_STATIC "Build static library" ON)
|
||||
option (OPENDHT_SHARED "Build shared library" ON)
|
||||
option (OPENDHT_LOG "Build with logs" ON)
|
||||
option (OPENDHT_PYTHON "Build Python bindings" OFF)
|
||||
option (OPENDHT_TOOLS "Build DHT tools" ON)
|
||||
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)
|
||||
|
||||
find_package(Doxygen)
|
||||
option (OPENDHT_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" ${DOXYGEN_FOUND})
|
||||
|
||||
# Dependencies
|
||||
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)
|
||||
if (OPENDHT_TOOLS)
|
||||
find_package (Readline 6 REQUIRED)
|
||||
endif ()
|
||||
if (NOT OPENDHT_ARGON2)
|
||||
find_package(PkgConfig)
|
||||
pkg_search_module(argon2 libargon2)
|
||||
if (NOT argon2_FOUND)
|
||||
if (argon2_FOUND)
|
||||
message("Argon2 found " ${argon2_req})
|
||||
set(argon2_lib ", libargon2")
|
||||
else ()
|
||||
message("Argon2 not found, using included version.")
|
||||
set(OPENDHT_ARGON2 ON)
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -std=c++11 -Wno-return-type -Wall -Wextra -Wnon-virtual-dtor -pedantic-errors -fvisibility=hidden")
|
||||
# Build flags
|
||||
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)
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fstack-protector-strong")
|
||||
endif ()
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMSGPACK_DISABLE_LEGACY_NIL -DMSGPACK_DISABLE_LEGACY_CONVERT")
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
@@ -56,6 +69,40 @@ if (OPENDHT_LTO)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (MSGPACK_INCLUDE_DIRS)
|
||||
include_directories (SYSTEM "${MSGPACK_INCLUDE_DIRS}")
|
||||
endif ()
|
||||
if (GNUTLS_INCLUDE_DIRS)
|
||||
include_directories (SYSTEM "${GNUTLS_INCLUDE_DIRS}")
|
||||
endif ()
|
||||
if (Nettle_INCLUDE_DIRS)
|
||||
include_directories (SYSTEM "${Nettle_INCLUDE_DIRS}")
|
||||
endif ()
|
||||
if (libuv_INCLUDE_DIRS)
|
||||
include_directories (SYSTEM "${libuv_INCLUDE_DIRS}")
|
||||
endif ()
|
||||
if (libsodium_INCLUDE_DIRS)
|
||||
include_directories (SYSTEM "${libsodium_INCLUDE_DIRS}")
|
||||
endif ()
|
||||
link_directories (${libuv_LIBRARY_DIRS} ${libsodium_LIBRARY_DIRS} ${Nettle_LIBRARY_DIRS})
|
||||
include_directories (
|
||||
./
|
||||
include/
|
||||
include/opendht/
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/
|
||||
)
|
||||
|
||||
# Install dirs
|
||||
include (GNUInstallDirs)
|
||||
set (prefix ${CMAKE_INSTALL_PREFIX})
|
||||
set (exec_prefix "\${prefix}")
|
||||
set (libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
|
||||
set (includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
set (bindir "${CMAKE_INSTALL_FULL_BINDIR}")
|
||||
set (sysconfdir "${CMAKE_INSTALL_FULL_SYSCONFDIR}")
|
||||
set (top_srcdir "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
# Sources
|
||||
list (APPEND opendht_SOURCES
|
||||
src/utils.cpp
|
||||
src/infohash.cpp
|
||||
@@ -64,6 +111,11 @@ list (APPEND opendht_SOURCES
|
||||
src/node.cpp
|
||||
src/value.cpp
|
||||
src/dht.cpp
|
||||
src/storage.h
|
||||
src/listener.h
|
||||
src/search.h
|
||||
src/parsed_message.h
|
||||
src/request.h
|
||||
src/callbacks.cpp
|
||||
src/routing_table.cpp
|
||||
src/node_cache.cpp
|
||||
@@ -99,18 +151,6 @@ list (APPEND opendht_HEADERS
|
||||
include/opendht/indexation/pht.h
|
||||
)
|
||||
|
||||
configure_file (
|
||||
opendht.pc.in
|
||||
opendht.pc
|
||||
@ONLY
|
||||
)
|
||||
|
||||
include_directories(
|
||||
./
|
||||
include/
|
||||
include/opendht/
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/
|
||||
)
|
||||
if(OPENDHT_ARGON2)
|
||||
# make sure argon2 submodule is up to date and initialized
|
||||
message("Initializing Argon2 submodule")
|
||||
@@ -128,10 +168,7 @@ if(OPENDHT_ARGON2)
|
||||
include_directories(argon2/include/)
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED CMAKE_INSTALL_LIBDIR)
|
||||
set(CMAKE_INSTALL_LIBDIR lib)
|
||||
endif ()
|
||||
|
||||
# Targets
|
||||
if (OPENDHT_STATIC)
|
||||
add_library (opendht-static STATIC
|
||||
${opendht_SOURCES}
|
||||
@@ -147,7 +184,7 @@ if (OPENDHT_STATIC)
|
||||
target_link_libraries(opendht-static ${argon2_LIBRARIES})
|
||||
target_include_directories(opendht-static SYSTEM PRIVATE ${argon2_INCLUDE_DIRS})
|
||||
endif ()
|
||||
target_link_libraries(opendht-static gnutls nettle)
|
||||
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 ()
|
||||
|
||||
@@ -160,15 +197,15 @@ if (OPENDHT_SHARED)
|
||||
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 -flto -fuse-linker-plugin)
|
||||
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 ${argon2_LIBRARIES})
|
||||
target_link_libraries(opendht PRIVATE ${argon2_LIBRARIES})
|
||||
target_include_directories(opendht SYSTEM PRIVATE ${argon2_INCLUDE_DIRS})
|
||||
endif ()
|
||||
target_link_libraries(opendht gnutls nettle)
|
||||
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 ()
|
||||
|
||||
@@ -181,11 +218,20 @@ if (OPENDHT_PYTHON)
|
||||
add_subdirectory(python)
|
||||
endif ()
|
||||
|
||||
# CMake module
|
||||
include(CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/opendhtConfigVersion.cmake"
|
||||
VERSION ${opendht_VERSION}
|
||||
COMPATIBILITY AnyNewerVersion
|
||||
)
|
||||
# PkgConfig module
|
||||
configure_file (
|
||||
opendht.pc.in
|
||||
opendht.pc
|
||||
@ONLY
|
||||
)
|
||||
|
||||
# Install targets
|
||||
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)
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}</ProjectGuid>
|
||||
<RootNamespace>argon</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<TargetExt>.lib</TargetExt>
|
||||
<OutDir>$(ProjectDir)..\..\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<TargetExt>.lib</TargetExt>
|
||||
<OutDir>$(ProjectDir)..\..\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<TargetExt>.lib</TargetExt>
|
||||
<OutDir>$(ProjectDir)..\..\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<TargetExt>.lib</TargetExt>
|
||||
<OutDir>$(ProjectDir)..\..\</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)\lib\x86\$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)\lib\x64\$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)\lib\x86\$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)\lib\x64\$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\argon2\argon2.c" />
|
||||
<ClCompile Include="..\argon2\core.c" />
|
||||
<ClCompile Include="..\argon2\encoding.c" />
|
||||
<ClCompile Include="..\argon2\opt.c" />
|
||||
<ClCompile Include="..\argon2\ref.c" />
|
||||
<ClCompile Include="..\argon2\thread.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\argon2\argon2.h" />
|
||||
<ClInclude Include="..\argon2\core.h" />
|
||||
<ClInclude Include="..\argon2\encoding.h" />
|
||||
<ClInclude Include="..\argon2\opt.h" />
|
||||
<ClInclude Include="..\argon2\ref.h" />
|
||||
<ClInclude Include="..\argon2\thread.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -1,151 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}</ProjectGuid>
|
||||
<RootNamespace>blake</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<TargetExt>.lib</TargetExt>
|
||||
<OutDir>$(ProjectDir)..\..\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<TargetExt>.lib</TargetExt>
|
||||
<OutDir>$(ProjectDir)..\..\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<TargetExt>.lib</TargetExt>
|
||||
<OutDir>$(ProjectDir)..\..\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<TargetExt>.lib</TargetExt>
|
||||
<OutDir>$(ProjectDir)..\..\</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)\lib\x86\$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)\lib\x64\$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)\lib\x86\$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)\lib\x64\$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\argon2\blake2\blake2b.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\argon2\blake2\blake2-impl.h" />
|
||||
<ClInclude Include="..\argon2\blake2\blake2.h" />
|
||||
<ClInclude Include="..\argon2\blake2\blamka-round-opt.h" />
|
||||
<ClInclude Include="..\argon2\blake2\blamka-round-ref.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,35 @@
|
||||
@echo on
|
||||
SETLOCAL EnableDelayedExpansion
|
||||
|
||||
set SRC=%~dp0
|
||||
|
||||
set PATH=%PATH%;%ProgramFiles(x86)%\MSBuild\14.0\Bin\
|
||||
|
||||
set MSBUILD_ARGS=/nologo /p:Configuration=Release /p:Platform=x64 /verbosity:normal /maxcpucount:%NUMBER_OF_PROCESSORS%
|
||||
|
||||
set TOBUILD=( ^
|
||||
contrib\build\argon2\vs2015\Argon2Ref\Argon2Ref.vcxproj, ^
|
||||
contrib\build\gmp\SMP\libgmp.vcxproj, ^
|
||||
contrib\build\nettle\SMP\libnettle.vcxproj, ^
|
||||
contrib\build\nettle\SMP\libhogweed.vcxproj, ^
|
||||
contrib\build\libiconv\SMP\libiconv.vcxproj, ^
|
||||
contrib\build\nettle\SMP\libiconv.vcxproj, ^
|
||||
contrib\build\zlib\SMP\libzlib.vcxproj, ^
|
||||
contrib\build\gnutls\SMP\libgnutls.vcxproj, ^
|
||||
contrib\build\msgpack-c\msgpack_vc8.vcxproj, ^
|
||||
opendht.vcxproj, ^
|
||||
dhtchat.vcxproj, ^
|
||||
dhtscanner.vcxproj, ^
|
||||
dhtnode.vcxproj ^
|
||||
)
|
||||
|
||||
for %%I in %TOBUILD% do (
|
||||
call :build "%SRC%%%I"
|
||||
)
|
||||
|
||||
exit /B %ERRORLEVEL%
|
||||
|
||||
:build
|
||||
echo "Building project: " %*
|
||||
msbuild %* %MSBUILD_ARGS%
|
||||
exit /B 0
|
||||
@@ -0,0 +1,39 @@
|
||||
--- a/vs2015/Argon2Ref/Argon2Ref.vcxproj
|
||||
+++ b/vs2015/Argon2Ref/Argon2Ref.vcxproj
|
||||
@@ -59,7 +59,7 @@
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
- <ConfigurationType>Application</ConfigurationType>
|
||||
+ <ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
@@ -117,8 +117,8 @@
|
||||
<IncludePath>$(SolutionDir)include;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
- <OutDir>$(SolutionDir)vs2015\build\</OutDir>
|
||||
- <IntDir>$(SolutionDir)vs2015\build\$(ProjectName)\</IntDir>
|
||||
+ <OutDir>$(ProjectDir)..\..\..\lib\x64</OutDir>
|
||||
+ <IntDir>$(ProjectDir)vs2015\build\$(ProjectName)\</IntDir>
|
||||
<IncludePath>$(SolutionDir)include;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|x64'">
|
||||
@@ -179,6 +179,7 @@
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
+ <AdditionalIncludeDirectories>$(ProjectDir)..\..\include</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
@@ -223,4 +224,4 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
-</Project>
|
||||
+</Project>
|
||||
\ No newline at end of file
|
||||
--
|
||||
2.10.2.windows.1
|
||||
@@ -17,4 +17,6 @@ rename %BUILD%\phc-winner-argon2-%ARGON2_VERSION% argon2
|
||||
|
||||
cd %BUILD%\argon2
|
||||
|
||||
git apply --reject --whitespace=fix %SRC%\argon2\argon2-uwp.patch
|
||||
|
||||
cd %SRC%
|
||||
@@ -79,6 +79,7 @@
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
<OutDir>$(ProjectDir)..\..\bin\x64</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
@@ -148,8 +149,9 @@
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>crypt32.lib;Argon2Ref.lib;libgnutls.lib;opendht.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(ProjectDir)contrib\build\lib\x64;$(ProjectDir)..\..\lib\x64</AdditionalLibraryDirectories>
|
||||
<AdditionalLibraryDirectories>$(ProjectDir)contrib\build\lib\x64;$(ProjectDir)..\..\lib\x64;$(ProjectDir)vs2015\build</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4049 %(AdditionalOptions)</AdditionalOptions>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
<OutDir>$(ProjectDir)..\..\bin\x64</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
@@ -148,8 +149,9 @@
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>crypt32.lib;Argon2Ref.lib;libgnutls.lib;opendht.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(ProjectDir)contrib\build\lib\x64;$(ProjectDir)..\..\lib\x64</AdditionalLibraryDirectories>
|
||||
<AdditionalLibraryDirectories>$(ProjectDir)contrib\build\lib\x64;$(ProjectDir)..\..\lib\x64;$(ProjectDir)vs2015\build</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4049 %(AdditionalOptions)</AdditionalOptions>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
<OutDir>$(ProjectDir)..\..\bin\x64</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
@@ -148,8 +149,9 @@
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>crypt32.lib;Argon2Ref.lib;libgnutls.lib;opendht.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(ProjectDir)contrib\build\lib\x64;$(ProjectDir)..\..\lib\x64</AdditionalLibraryDirectories>
|
||||
<AdditionalLibraryDirectories>$(ProjectDir)contrib\build\lib\x64;$(ProjectDir)..\..\lib\x64;$(ProjectDir)vs2015\build</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4049 %(AdditionalOptions)</AdditionalOptions>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
@echo on
|
||||
call contrib\src\fetch_and_patch.bat
|
||||
@@ -1,221 +0,0 @@
|
||||
--- /dev/null
|
||||
+++ b/msgpack_vc8.vcxproj
|
||||
@@ -0,0 +1,214 @@
|
||||
+<?xml version="1.0" encoding="utf-8"?>
|
||||
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
+ <ItemGroup Label="ProjectConfigurations">
|
||||
+ <ProjectConfiguration Include="Debug|Win32">
|
||||
+ <Configuration>Debug</Configuration>
|
||||
+ <Platform>Win32</Platform>
|
||||
+ </ProjectConfiguration>
|
||||
+ <ProjectConfiguration Include="Debug|x64">
|
||||
+ <Configuration>Debug</Configuration>
|
||||
+ <Platform>x64</Platform>
|
||||
+ </ProjectConfiguration>
|
||||
+ <ProjectConfiguration Include="Release|Win32">
|
||||
+ <Configuration>Release</Configuration>
|
||||
+ <Platform>Win32</Platform>
|
||||
+ </ProjectConfiguration>
|
||||
+ <ProjectConfiguration Include="Release|x64">
|
||||
+ <Configuration>Release</Configuration>
|
||||
+ <Platform>x64</Platform>
|
||||
+ </ProjectConfiguration>
|
||||
+ </ItemGroup>
|
||||
+ <PropertyGroup Label="Globals">
|
||||
+ <ProjectName>MessagePack</ProjectName>
|
||||
+ <ProjectGuid>{122A2EA4-B283-4241-9655-786DE78283B2}</ProjectGuid>
|
||||
+ <RootNamespace>MessagePack</RootNamespace>
|
||||
+ <Keyword>Win32Proj</Keyword>
|
||||
+ <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
+ </PropertyGroup>
|
||||
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
+ <ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
+ <PlatformToolset>v140</PlatformToolset>
|
||||
+ <CharacterSet>Unicode</CharacterSet>
|
||||
+ <WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
+ </PropertyGroup>
|
||||
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
+ <ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
+ <PlatformToolset>v140</PlatformToolset>
|
||||
+ <CharacterSet>Unicode</CharacterSet>
|
||||
+ <WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
+ </PropertyGroup>
|
||||
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
+ <ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
+ <PlatformToolset>v140</PlatformToolset>
|
||||
+ <CharacterSet>Unicode</CharacterSet>
|
||||
+ </PropertyGroup>
|
||||
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
+ <ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
+ <PlatformToolset>v140</PlatformToolset>
|
||||
+ <CharacterSet>Unicode</CharacterSet>
|
||||
+ </PropertyGroup>
|
||||
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
+ <ImportGroup Label="ExtensionSettings">
|
||||
+ </ImportGroup>
|
||||
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
+ </ImportGroup>
|
||||
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
+ </ImportGroup>
|
||||
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
+ </ImportGroup>
|
||||
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
+ </ImportGroup>
|
||||
+ <PropertyGroup Label="UserMacros" />
|
||||
+ <PropertyGroup>
|
||||
+ <_ProjectFileVersion>14.0.25123.0</_ProjectFileVersion>
|
||||
+ </PropertyGroup>
|
||||
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
+ <OutDir>$(ProjectDir)..\</OutDir>
|
||||
+ <IntDir>$(Configuration)\</IntDir>
|
||||
+ <TargetName>msgpackd</TargetName>
|
||||
+ </PropertyGroup>
|
||||
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
+ <TargetName>msgpackd</TargetName>
|
||||
+ <OutDir>$(ProjectDir)..\</OutDir>
|
||||
+ <IntDir>$(Configuration)\</IntDir>
|
||||
+ </PropertyGroup>
|
||||
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
+ <OutDir>$(ProjectDir)..\</OutDir>
|
||||
+ <IntDir>$(Configuration)\</IntDir>
|
||||
+ <TargetName>msgpack</TargetName>
|
||||
+ </PropertyGroup>
|
||||
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
+ <TargetName>msgpack</TargetName>
|
||||
+ <OutDir>$(ProjectDir)..\</OutDir>
|
||||
+ <IntDir>$(Configuration)\</IntDir>
|
||||
+ </PropertyGroup>
|
||||
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
+ <ClCompile>
|
||||
+ <Optimization>Disabled</Optimization>
|
||||
+ <AdditionalIncludeDirectories>.;include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
+ <PreprocessorDefinitions>WIN32_NATIVE;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
+ <MinimalRebuild>true</MinimalRebuild>
|
||||
+ <BasicRuntimeChecks>StackFrameRuntimeCheck</BasicRuntimeChecks>
|
||||
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
+ <PrecompiledHeader />
|
||||
+ <WarningLevel>Level3</WarningLevel>
|
||||
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
|
||||
+ </ClCompile>
|
||||
+ <Lib>
|
||||
+ <OutputFile>$(OutDir)\lib\x86\$(TargetName)$(TargetExt)</OutputFile>
|
||||
+ </Lib>
|
||||
+ <PostBuildEvent>
|
||||
+ <Command>mkdir $(OutDir)\include
|
||||
+
|
||||
+mkdir $(OutDir)\include\msgpack
|
||||
+
|
||||
+copy include\*.h $(OutDir)\include\
|
||||
+
|
||||
+copy include\*.hpp $(OutDir)\include\
|
||||
+xcopy /S /Y include\msgpack\*.h $(OutDir)\include\msgpack
|
||||
+xcopy /S /Y include\msgpack\*.hpp $(OutDir)\include\msgpack
|
||||
+</Command>
|
||||
+ </PostBuildEvent>
|
||||
+ </ItemDefinitionGroup>
|
||||
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
+ <ClCompile>
|
||||
+ <Optimization>Disabled</Optimization>
|
||||
+ <AdditionalIncludeDirectories>.;include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
+ <PreprocessorDefinitions>WIN32_NATIVE;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
+ <BasicRuntimeChecks>StackFrameRuntimeCheck</BasicRuntimeChecks>
|
||||
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
+ <PrecompiledHeader>
|
||||
+ </PrecompiledHeader>
|
||||
+ <WarningLevel>Level3</WarningLevel>
|
||||
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
+ </ClCompile>
|
||||
+ <Lib>
|
||||
+ <OutputFile>$(OutDir)\lib\x64\$(TargetName)$(TargetExt)</OutputFile>
|
||||
+ </Lib>
|
||||
+ <PostBuildEvent>
|
||||
+ <Command>mkdir $(OutDir)\include
|
||||
+
|
||||
+mkdir $(OutDir)\include\msgpack
|
||||
+
|
||||
+copy include\*.h $(OutDir)\include\
|
||||
+
|
||||
+copy include\*.hpp $(OutDir)\include\
|
||||
+xcopy /S /Y include\msgpack\*.h $(OutDir)\include\msgpack
|
||||
+xcopy /S /Y include\msgpack\*.hpp $(OutDir)\include\msgpack
|
||||
+</Command>
|
||||
+ </PostBuildEvent>
|
||||
+ </ItemDefinitionGroup>
|
||||
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
+ <ClCompile>
|
||||
+ <AdditionalIncludeDirectories>.;include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
+ <PreprocessorDefinitions>WIN32_NATIVE;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
+ <PrecompiledHeader />
|
||||
+ <WarningLevel>Level3</WarningLevel>
|
||||
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
+ <ProgramDataBaseFileName>$(OutDir)\lib\x86\$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
+ </ClCompile>
|
||||
+ <Lib>
|
||||
+ <OutputFile>$(OutDir)\lib\x86\$(TargetName)$(TargetExt)</OutputFile>
|
||||
+ <LinkTimeCodeGeneration />
|
||||
+ </Lib>
|
||||
+ <PostBuildEvent>
|
||||
+ <Command>mkdir $(OutDir)\include
|
||||
+
|
||||
+mkdir $(OutDir)\include\msgpack
|
||||
+
|
||||
+copy include\*.h $(OutDir)\include\
|
||||
+
|
||||
+copy include\*.hpp $(OutDir)\include\
|
||||
+xcopy /S /Y include\msgpack\*.h $(OutDir)\include\msgpack
|
||||
+xcopy /S /Y include\msgpack\*.hpp $(OutDir)\include\msgpack
|
||||
+</Command>
|
||||
+ </PostBuildEvent>
|
||||
+ </ItemDefinitionGroup>
|
||||
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
+ <ClCompile>
|
||||
+ <AdditionalIncludeDirectories>.;include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
+ <PreprocessorDefinitions>MSGPACK_USE_CPP03;WIN32_NATIVE;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
+ <PrecompiledHeader>
|
||||
+ </PrecompiledHeader>
|
||||
+ <WarningLevel>Level3</WarningLevel>
|
||||
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
+ <ProgramDataBaseFileName>$(OutDir)\lib\x64\$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
+ </ClCompile>
|
||||
+ <Lib>
|
||||
+ <OutputFile>$(OutDir)\lib\x64\$(TargetName)$(TargetExt)</OutputFile>
|
||||
+ <LinkTimeCodeGeneration />
|
||||
+ </Lib>
|
||||
+ <PostBuildEvent>
|
||||
+ <Command>mkdir $(OutDir)\include
|
||||
+
|
||||
+mkdir $(OutDir)\include\msgpack
|
||||
+
|
||||
+copy include\*.h $(OutDir)\include\
|
||||
+
|
||||
+copy include\*.hpp $(OutDir)\include\
|
||||
+xcopy /S /Y include\msgpack\*.h $(OutDir)\include\msgpack
|
||||
+xcopy /S /Y include\msgpack\*.hpp $(OutDir)\include\msgpack
|
||||
+</Command>
|
||||
+ </PostBuildEvent>
|
||||
+ </ItemDefinitionGroup>
|
||||
+ <ItemGroup>
|
||||
+ <ClCompile Include="src\objectc.c" />
|
||||
+ <ClCompile Include="src\unpack.c" />
|
||||
+ <ClCompile Include="src\version.c" />
|
||||
+ <ClCompile Include="src\vrefbuffer.c" />
|
||||
+ <ClCompile Include="src\zone.c" />
|
||||
+ </ItemGroup>
|
||||
+ <ItemGroup>
|
||||
+ <ClInclude Include="include\msgpack.hpp" />
|
||||
+ </ItemGroup>
|
||||
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
+ <ImportGroup Label="ExtensionTargets">
|
||||
+ </ImportGroup>
|
||||
+</Project>
|
||||
\ No newline at end of file
|
||||
--
|
||||
2.8.1.windows.1
|
||||
|
||||
@@ -57,6 +57,9 @@
|
||||
<ClInclude Include="..\include\opendht\securedht.h" />
|
||||
<ClInclude Include="..\include\opendht\utils.h" />
|
||||
<ClInclude Include="..\include\opendht\value.h" />
|
||||
<ClInclude Include="..\src\listener.h" />
|
||||
<ClInclude Include="..\src\search.h" />
|
||||
<ClInclude Include="..\src\storage.h" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{711397CE-E5D5-467D-9457-8716C047E50C}</ProjectGuid>
|
||||
@@ -229,6 +232,8 @@ copy ..\include\opendht.h $(OutDir)\include\</Command>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)\lib\x64\$(TargetName)$(TargetExt)</OutputFile>
|
||||
<LinkTimeCodeGeneration>false</LinkTimeCodeGeneration>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
||||
+125
-36
@@ -1,43 +1,132 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<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\indexation\pht.cpp" />
|
||||
<ClCompile Include="..\src\infohash.cpp" />
|
||||
<ClCompile Include="..\src\log.cpp" />
|
||||
<ClCompile Include="..\src\network_engine.cpp" />
|
||||
<ClCompile Include="..\src\node.cpp" />
|
||||
<ClCompile Include="..\src\node_cache.cpp" />
|
||||
<ClCompile Include="..\src\routing_table.cpp" />
|
||||
<ClCompile Include="..\src\securedht.cpp" />
|
||||
<ClCompile Include="..\src\utils.cpp" />
|
||||
<ClCompile Include="..\src\value.cpp" />
|
||||
<ClCompile Include="..\src\callbacks.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\crypto.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\default_types.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\dht.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\dhtrunner.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\infohash.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\log.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\network_engine.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\node.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<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>
|
||||
<ClCompile Include="..\src\securedht.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\utils.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\value.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\include\opendht.h" />
|
||||
<ClInclude Include="..\include\opendht\callbacks.h" />
|
||||
<ClInclude Include="..\include\opendht\crypto.h" />
|
||||
<ClInclude Include="..\include\opendht\default_types.h" />
|
||||
<ClInclude Include="..\include\opendht\dht.h" />
|
||||
<ClInclude Include="..\include\opendht\dhtrunner.h" />
|
||||
<ClInclude Include="..\include\opendht\indexation\pht.h" />
|
||||
<ClInclude Include="..\include\opendht\infohash.h" />
|
||||
<ClInclude Include="..\include\opendht\log.h" />
|
||||
<ClInclude Include="..\include\opendht\log_enable.h" />
|
||||
<ClInclude Include="..\include\opendht\network_engine.h" />
|
||||
<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\utils.h" />
|
||||
<ClInclude Include="..\include\opendht\value.h" />
|
||||
<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>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\log.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\log_enable.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<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\indexation\pht.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\rate_limiter.h">
|
||||
<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</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\routing_table.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\scheduler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\search.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\securedht.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\storage.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\utils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\value.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{be73d179-6cf2-4c05-8d91-53c8499a7134}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{d1ab5bfe-3ab1-45ee-9324-b4b071887668}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
+6
-3
@@ -1,7 +1,7 @@
|
||||
dnl define macros
|
||||
m4_define([opendht_major_version], 1)
|
||||
m4_define([opendht_minor_version], 3)
|
||||
m4_define([opendht_patch_version], 2)
|
||||
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])
|
||||
|
||||
@@ -104,7 +104,10 @@ AC_ARG_WITH([argon2], AS_HELP_STRING([--without-argon2], [Use included Argon2]))
|
||||
AS_IF([test "x$with_argon2" != "xno"],
|
||||
[PKG_CHECK_MODULES([Argon2], [libargon2], [have_argon2=yes], [have_argon2=no])],
|
||||
[have_argon2=no])
|
||||
AS_IF([test "x$have_argon2" = "xyes"], [], [
|
||||
AS_IF([test "x$have_argon2" = "xyes"], [
|
||||
AC_MSG_NOTICE([Using system Argon2])
|
||||
AC_SUBST(argon2_lib, [", libargon2"])
|
||||
], [
|
||||
AS_IF([test "x$with_argon2" = "xyes"], [
|
||||
AC_MSG_ERROR([Argon2 requested but not found])
|
||||
],[
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
if (OPENDHT_TOOLS)
|
||||
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dhtnode.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1)
|
||||
endif ()
|
||||
|
||||
if (OPENDHT_DOCUMENTATION)
|
||||
if (NOT DOXYGEN_FOUND)
|
||||
message(FATAL_ERROR "Doxygen is needed to build the documentation.")
|
||||
endif()
|
||||
configure_file (Doxyfile.in Doxyfile @ONLY)
|
||||
add_custom_target(doc ALL
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Generating API documentation with Doxygen"
|
||||
VERBATIM)
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION share/doc/opendht)
|
||||
endif()
|
||||
@@ -49,6 +49,12 @@ struct OPENDHT_PUBLIC NodeStats {
|
||||
std::string toString() const;
|
||||
};
|
||||
|
||||
|
||||
struct OPENDHT_PUBLIC NetworkConfig {
|
||||
std::string bind_addr {"::0"};
|
||||
in_port_t bind_port {0};
|
||||
};
|
||||
|
||||
/**
|
||||
* Dht configuration.
|
||||
*/
|
||||
@@ -68,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;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -87,6 +95,7 @@ using QueryCallback = std::function<bool(const std::vector<std::shared_ptr<Field
|
||||
using GetCallback = std::function<bool(const std::vector<std::shared_ptr<Value>>& values)>;
|
||||
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)>;
|
||||
|
||||
@@ -95,10 +104,11 @@ 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);
|
||||
typedef bool (*FilterRaw)(const Value&, void *user_data);
|
||||
|
||||
using DoneCallbackSimple = std::function<void(bool success)>;
|
||||
|
||||
@@ -106,5 +116,6 @@ OPENDHT_PUBLIC ShutdownCallback bindShutdownCb(ShutdownCallbackRaw shutdown_cb_r
|
||||
OPENDHT_PUBLIC DoneCallback bindDoneCb(DoneCallbackSimple donecb);
|
||||
OPENDHT_PUBLIC DoneCallback bindDoneCb(DoneCallbackRaw raw_cb, void* user_data);
|
||||
OPENDHT_PUBLIC DoneCallbackSimple bindDoneCbSimple(DoneCallbackSimpleRaw raw_cb, void* user_data);
|
||||
OPENDHT_PUBLIC Value::Filter bindFilterRaw(FilterRaw raw_filter, void* user_data);
|
||||
|
||||
}
|
||||
+193
-16
@@ -28,6 +28,8 @@ extern "C" {
|
||||
#include <gnutls/x509.h>
|
||||
}
|
||||
|
||||
#include <sodium.h>
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
@@ -35,7 +37,13 @@ extern "C" {
|
||||
#include <iso646.h>
|
||||
#endif
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
namespace dht {
|
||||
|
||||
/**
|
||||
* Contains all crypto primitives
|
||||
*/
|
||||
namespace crypto {
|
||||
|
||||
class OPENDHT_PUBLIC CryptoException : public std::runtime_error {
|
||||
@@ -227,6 +235,8 @@ public:
|
||||
|
||||
gnutls_x509_crl_t get() { return crl; }
|
||||
gnutls_x509_crl_t getCopy() const {
|
||||
if (not crl)
|
||||
return nullptr;
|
||||
auto copy = RevocationList(getPacked());
|
||||
gnutls_x509_crl_t ret = copy.crl;
|
||||
copy.crl = nullptr;
|
||||
@@ -405,6 +415,8 @@ struct OPENDHT_PUBLIC Certificate {
|
||||
static Certificate generate(const PrivateKey& key, const std::string& name = "dhtnode", Identity ca = {}, bool is_ca = false);
|
||||
|
||||
gnutls_x509_crt_t getCopy() const {
|
||||
if (not cert)
|
||||
return nullptr;
|
||||
auto copy = Certificate(getPacked());
|
||||
gnutls_x509_crt_t ret = copy.cert;
|
||||
copy.cert = nullptr;
|
||||
@@ -414,12 +426,11 @@ struct OPENDHT_PUBLIC Certificate {
|
||||
std::vector<gnutls_x509_crt_t>
|
||||
getChain(bool copy = false) const
|
||||
{
|
||||
if (not cert)
|
||||
return {};
|
||||
std::vector<gnutls_x509_crt_t> crts;
|
||||
auto c = this;
|
||||
do {
|
||||
for (auto c = this; c; c = c->issuer.get())
|
||||
crts.emplace_back(copy ? c->getCopy() : c->cert);
|
||||
c = c->issuer.get();
|
||||
} while (c);
|
||||
return crts;
|
||||
}
|
||||
|
||||
@@ -429,16 +440,16 @@ struct OPENDHT_PUBLIC Certificate {
|
||||
>
|
||||
getChainWithRevocations(bool copy = false) const
|
||||
{
|
||||
if (not cert)
|
||||
return {};
|
||||
std::vector<gnutls_x509_crt_t> crts;
|
||||
std::vector<gnutls_x509_crl_t> crls;
|
||||
auto c = this;
|
||||
do {
|
||||
for (auto c = this; c; c = c->issuer.get()) {
|
||||
crts.emplace_back(copy ? c->getCopy() : c->cert);
|
||||
crls.reserve(crls.size() + c->revocation_lists.size());
|
||||
for (const auto& crl : c->revocation_lists)
|
||||
crls.emplace_back(copy ? crl->getCopy() : crl->get());
|
||||
c = c->issuer.get();
|
||||
} while (c);
|
||||
}
|
||||
return {crts, crls};
|
||||
}
|
||||
|
||||
@@ -493,11 +504,19 @@ 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/sizeof(T));
|
||||
@@ -510,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());
|
||||
@@ -544,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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -568,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
|
||||
@@ -580,6 +754,9 @@ using SecureBlob = secure_vector<uint8_t>;
|
||||
OPENDHT_PUBLIC Identity generateIdentity(const std::string& name, Identity ca, unsigned key_length, bool is_ca);
|
||||
OPENDHT_PUBLIC Identity generateIdentity(const std::string& name = "dhtnode", Identity ca = {}, unsigned key_length = 4096);
|
||||
|
||||
OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name, Identity ca, bool is_ca);
|
||||
OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name = "dhtnode", Identity ca = {});
|
||||
|
||||
|
||||
/**
|
||||
* Performs SHA512, SHA256 or SHA1, depending on hash_length.
|
||||
@@ -598,18 +775,18 @@ OPENDHT_PUBLIC Blob hash(const Blob& data, size_t hash_length = 512/8);
|
||||
* 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);
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "value.h"
|
||||
#include "sockaddr.h"
|
||||
|
||||
namespace dht {
|
||||
enum class ImStatus : uint8_t {
|
||||
@@ -45,7 +46,7 @@ public:
|
||||
|
||||
static Value::Filter getFilter() { return {}; }
|
||||
|
||||
static bool storePolicy(InfoHash key, std::shared_ptr<Value>& value, InfoHash from, const sockaddr* from_addr, socklen_t from_len);
|
||||
static bool storePolicy(InfoHash key, std::shared_ptr<Value>& value, const InfoHash& from, const SockAddr&);
|
||||
|
||||
static Value::Filter ServiceFilter(std::string s);
|
||||
|
||||
@@ -204,15 +205,12 @@ private:
|
||||
public:
|
||||
static const ValueType TYPE;
|
||||
|
||||
IpServiceAnnouncement(in_port_t p = 0) {
|
||||
ss.ss_family = 0;
|
||||
setPort(p);
|
||||
IpServiceAnnouncement(sa_family_t family = AF_UNSPEC, in_port_t p = 0) {
|
||||
addr.setFamily(family);
|
||||
addr.setPort(p);
|
||||
}
|
||||
|
||||
IpServiceAnnouncement(const sockaddr* sa, socklen_t sa_len) {
|
||||
if (sa)
|
||||
std::copy_n((const uint8_t*)sa, sa_len, (uint8_t*)&ss);
|
||||
}
|
||||
IpServiceAnnouncement(const SockAddr& sa) : addr(sa) {}
|
||||
|
||||
IpServiceAnnouncement(const Blob& b) {
|
||||
msgpack_unpack(unpackMsg(b).get());
|
||||
@@ -221,53 +219,40 @@ public:
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& pk) const
|
||||
{
|
||||
pk.pack_array(2);
|
||||
pk.pack(reinterpret_cast<const sockaddr_in*>(&ss)->sin_port);
|
||||
if (ss.ss_family == AF_INET) {
|
||||
pk.pack_bin(sizeof(in_addr));
|
||||
pk.pack_bin_body((const char*)&reinterpret_cast<const sockaddr_in*>(&ss)->sin_addr, sizeof(in_addr));
|
||||
} else if (ss.ss_family == AF_INET6) {
|
||||
pk.pack_bin(sizeof(in6_addr));
|
||||
pk.pack_bin_body((const char*)&reinterpret_cast<const sockaddr_in6*>(&ss)->sin6_addr, sizeof(in6_addr));
|
||||
}
|
||||
pk.pack_bin(addr.getLength());
|
||||
pk.pack_bin_body((const char*)addr.get(), addr.getLength());
|
||||
}
|
||||
|
||||
virtual void msgpack_unpack(msgpack::object o)
|
||||
{
|
||||
if (o.type != msgpack::type::ARRAY) throw msgpack::type_error();
|
||||
if (o.via.array.size < 2) throw msgpack::type_error();
|
||||
reinterpret_cast<sockaddr_in*>(&ss)->sin_port = o.via.array.ptr[0].as<in_port_t>();
|
||||
auto ip_dat = o.via.array.ptr[1].as<Blob>();
|
||||
if (ip_dat.size() == sizeof(in_addr))
|
||||
std::copy(ip_dat.begin(), ip_dat.end(), (char*)&reinterpret_cast<sockaddr_in*>(&ss)->sin_addr);
|
||||
else if (ip_dat.size() == sizeof(in6_addr))
|
||||
std::copy(ip_dat.begin(), ip_dat.end(), (char*)&reinterpret_cast<sockaddr_in6*>(&ss)->sin6_addr);
|
||||
if (o.type == msgpack::type::BIN)
|
||||
addr = {(sockaddr*)o.via.bin.ptr, (socklen_t)o.via.bin.size};
|
||||
else
|
||||
throw msgpack::type_error();
|
||||
}
|
||||
|
||||
in_port_t getPort() const {
|
||||
return ntohs(reinterpret_cast<const sockaddr_in*>(&ss)->sin_port);
|
||||
return addr.getPort();
|
||||
}
|
||||
void setPort(in_port_t p) {
|
||||
reinterpret_cast<sockaddr_in*>(&ss)->sin_port = htons(p);
|
||||
addr.setPort(p);
|
||||
}
|
||||
|
||||
sockaddr_storage getPeerAddr() const {
|
||||
return ss;
|
||||
const SockAddr& getPeerAddr() const {
|
||||
return addr;
|
||||
}
|
||||
|
||||
virtual const ValueType& getType() const {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
static bool storePolicy(InfoHash, std::shared_ptr<Value>&, InfoHash, const sockaddr*, socklen_t);
|
||||
static bool storePolicy(InfoHash, std::shared_ptr<Value>&, const InfoHash&, const SockAddr&);
|
||||
|
||||
/** print value for debugging */
|
||||
friend std::ostream& operator<< (std::ostream&, const IpServiceAnnouncement&);
|
||||
|
||||
private:
|
||||
sockaddr_storage ss;
|
||||
SockAddr addr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
+48
-43
@@ -45,12 +45,15 @@ namespace net {
|
||||
struct Request;
|
||||
} /* namespace net */
|
||||
|
||||
struct Storage;
|
||||
struct ValueStorage;
|
||||
class StorageBucket;
|
||||
struct Listener;
|
||||
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 {
|
||||
public:
|
||||
@@ -67,13 +70,13 @@ public:
|
||||
* Initialise the Dht with two open sockets (for IPv4 and IP6)
|
||||
* and an ID for the node.
|
||||
*/
|
||||
Dht(int s, 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.
|
||||
@@ -83,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.
|
||||
@@ -130,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.
|
||||
@@ -162,8 +168,8 @@ public:
|
||||
* @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 donecb a function called when the operation is complete.
|
||||
cb and donecb won't be called again afterward.
|
||||
* @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.
|
||||
*/
|
||||
@@ -301,6 +307,10 @@ public:
|
||||
|
||||
std::vector<SockAddr> getPublicAddress(sa_family_t family = 0);
|
||||
|
||||
SockAddr getBoundAddr() {
|
||||
return network_engine.getBoundAddr();
|
||||
}
|
||||
|
||||
protected:
|
||||
Logger DHT_LOG;
|
||||
bool logFilerEnable_ {};
|
||||
@@ -329,9 +339,6 @@ private:
|
||||
/* Number of listening nodes */
|
||||
static constexpr unsigned LISTEN_NODES {4};
|
||||
|
||||
/* The maximum number of values we store for a given hash. */
|
||||
static constexpr unsigned MAX_VALUES {1024};
|
||||
|
||||
/* The maximum number of hashes we're willing to track. */
|
||||
static constexpr unsigned MAX_HASHES {16384};
|
||||
|
||||
@@ -354,12 +361,7 @@ private:
|
||||
struct SearchNode;
|
||||
struct Get;
|
||||
struct Announce;
|
||||
struct LocalListener;
|
||||
struct Search;
|
||||
struct ValueStorage;
|
||||
class StorageBucket;
|
||||
struct Listener;
|
||||
struct Storage;
|
||||
|
||||
// prevent copy
|
||||
Dht(const Dht&) = delete;
|
||||
@@ -367,8 +369,8 @@ private:
|
||||
|
||||
InfoHash myid {};
|
||||
|
||||
std::array<uint8_t, 8> secret {{}};
|
||||
std::array<uint8_t, 8> oldsecret {{}};
|
||||
uint64_t secret {};
|
||||
uint64_t oldsecret {};
|
||||
|
||||
// registred types
|
||||
std::map<ValueType::Id, ValueType> types;
|
||||
@@ -380,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 {};
|
||||
@@ -402,9 +406,7 @@ private:
|
||||
|
||||
// timing
|
||||
Scheduler scheduler;
|
||||
Sp<Scheduler::Job> nextNodesConfirmation {};
|
||||
Sp<Scheduler::Job> nextStorageMaintenance {};
|
||||
time_point mybucket_grow_time {time_point::min()}, mybucket6_grow_time {time_point::min()};
|
||||
Sp<Job> nextNodesConfirmation {};
|
||||
|
||||
net::NetworkEngine network_engine;
|
||||
unsigned pending_pings4 {0};
|
||||
@@ -412,17 +414,23 @@ 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 *sa, bool old) const;
|
||||
bool tokenMatch(const Blob& token, const sockaddr *sa) const;
|
||||
Blob makeToken(const SockAddr&, bool old) const;
|
||||
bool tokenMatch(const Blob& token, const SockAddr&) const;
|
||||
|
||||
void reportedAddr(const SockAddr&);
|
||||
|
||||
// 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 = nullptr);
|
||||
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);
|
||||
@@ -460,13 +468,12 @@ private:
|
||||
}
|
||||
|
||||
void expireBuckets(RoutingTable&);
|
||||
int sendCachedPing(Bucket& b);
|
||||
void sendCachedPing(Bucket& b);
|
||||
bool bucketMaintenance(RoutingTable&);
|
||||
void dumpBucket(const Bucket& b, std::ostream& out) const;
|
||||
|
||||
// Nodes
|
||||
void onNewNode(const Sp<Node>& node, int confirm);
|
||||
Sp<Node> findNode(const InfoHash& id, sa_family_t af);
|
||||
const Sp<Node> findNode(const InfoHash& id, sa_family_t af) const;
|
||||
bool trySearchInsert(const Sp<Node>& node);
|
||||
|
||||
@@ -506,7 +513,7 @@ private:
|
||||
* @param query The query sent to the node.
|
||||
*/
|
||||
void searchNodeGetDone(const net::Request& status,
|
||||
net::NetworkEngine::RequestAnswer&& answer,
|
||||
net::RequestAnswer&& answer,
|
||||
std::weak_ptr<Search> ws,
|
||||
Sp<Query> query);
|
||||
|
||||
@@ -555,48 +562,46 @@ private:
|
||||
|
||||
bool neighbourhoodMaintenance(RoutingTable&);
|
||||
|
||||
void processMessage(const uint8_t *buf, size_t buflen, const SockAddr&);
|
||||
|
||||
void onError(Sp<net::Request> node, net::DhtProtocolException e);
|
||||
/* when our address is reported by a distant peer. */
|
||||
void onReportedAddr(const InfoHash& id, const SockAddr&);
|
||||
/* when we receive a ping request */
|
||||
net::NetworkEngine::RequestAnswer onPing(Sp<Node> node);
|
||||
net::RequestAnswer onPing(Sp<Node> node);
|
||||
/* when we receive a "find node" request */
|
||||
net::NetworkEngine::RequestAnswer onFindNode(Sp<Node> node, const InfoHash& hash, want_t want);
|
||||
net::RequestAnswer onFindNode(Sp<Node> node, const InfoHash& hash, want_t want);
|
||||
void onFindNodeDone(const Sp<Node>& status,
|
||||
net::NetworkEngine::RequestAnswer& a,
|
||||
net::RequestAnswer& a,
|
||||
Sp<Search> sr);
|
||||
/* when we receive a "get values" request */
|
||||
net::NetworkEngine::RequestAnswer onGetValues(Sp<Node> node,
|
||||
net::RequestAnswer onGetValues(Sp<Node> node,
|
||||
const InfoHash& hash,
|
||||
want_t want,
|
||||
const Query& q);
|
||||
void onGetValuesDone(const Sp<Node>& status,
|
||||
net::NetworkEngine::RequestAnswer& a,
|
||||
net::RequestAnswer& a,
|
||||
Sp<Search>& sr,
|
||||
const Sp<Query>& orig_query);
|
||||
/* when we receive a listen request */
|
||||
net::NetworkEngine::RequestAnswer onListen(Sp<Node> node,
|
||||
net::RequestAnswer onListen(Sp<Node> node,
|
||||
const InfoHash& hash,
|
||||
const Blob& token,
|
||||
size_t socket_id,
|
||||
const Query& query);
|
||||
void onListenDone(const Sp<Node>& status,
|
||||
net::NetworkEngine::RequestAnswer& a,
|
||||
net::RequestAnswer& a,
|
||||
Sp<Search>& sr);
|
||||
/* when we receive an announce request */
|
||||
net::NetworkEngine::RequestAnswer onAnnounce(Sp<Node> node,
|
||||
net::RequestAnswer onAnnounce(Sp<Node> node,
|
||||
const InfoHash& hash,
|
||||
const Blob& token,
|
||||
const std::vector<Sp<Value>>& v,
|
||||
const time_point& created);
|
||||
net::NetworkEngine::RequestAnswer onRefresh(Sp<Node> node,
|
||||
net::RequestAnswer onRefresh(Sp<Node> node,
|
||||
const InfoHash& hash,
|
||||
const Blob& token,
|
||||
const Value::Id& vid);
|
||||
void onAnnounceDone(const Sp<Node>& status,
|
||||
net::NetworkEngine::RequestAnswer& a,
|
||||
net::RequestAnswer& a,
|
||||
Sp<Search>& sr);
|
||||
};
|
||||
|
||||
|
||||
+44
-35
@@ -26,6 +26,7 @@
|
||||
#include "sockaddr.h"
|
||||
#include "log_enable.h"
|
||||
#include "def.h"
|
||||
#include "uv_utils.h"
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
@@ -51,8 +52,6 @@ struct SecureDhtConfig;
|
||||
class OPENDHT_PUBLIC DhtRunner {
|
||||
|
||||
public:
|
||||
typedef std::function<void(NodeStatus, NodeStatus)> StatusCallback;
|
||||
|
||||
DhtRunner();
|
||||
virtual ~DhtRunner();
|
||||
|
||||
@@ -202,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<std::pair<sockaddr_storage, socklen_t>>& 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.
|
||||
@@ -241,16 +243,14 @@ public:
|
||||
* 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 ntohs(((sockaddr_in*)&getBound(f).first)->sin_port);
|
||||
in_port_t getBoundPort() const {
|
||||
return getBound().getPort();
|
||||
}
|
||||
|
||||
std::pair<size_t, size_t> getStoreSize() const;
|
||||
@@ -306,20 +306,24 @@ public:
|
||||
* @param cb: Optional callback to receive general state information.
|
||||
*/
|
||||
void run(in_port_t port, const crypto::Identity identity, bool threaded = false, NetId network = 0) {
|
||||
run(port, {
|
||||
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
|
||||
});
|
||||
}
|
||||
void run(in_port_t port, Config config);
|
||||
void run(Config config);
|
||||
|
||||
/**
|
||||
* @param local4: Local IPv4 address and port to bind. Can be null.
|
||||
@@ -329,25 +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_in* local4, const sockaddr_in6* 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);
|
||||
return loop_();
|
||||
void loop() {
|
||||
uv_loop_.runOnce();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -373,33 +374,41 @@ private:
|
||||
*/
|
||||
void tryBootstrapContinuously();
|
||||
|
||||
void doRun(const sockaddr_in* sin4, const sockaddr_in6* sin6, SecureDhtConfig config);
|
||||
time_point loop_();
|
||||
void doRun(SecureDhtConfig config);
|
||||
void loop_();
|
||||
|
||||
static std::vector<std::pair<sockaddr_storage, socklen_t>> getAddrInfo(const std::string& host, const std::string& service);
|
||||
|
||||
NodeStatus getStatus() const {
|
||||
return std::max(status4, status6);
|
||||
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();
|
||||
}
|
||||
|
||||
std::unique_ptr<SecureDht> dht_;
|
||||
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 {};
|
||||
std::vector<std::pair<Blob, SockAddr>> 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 {};
|
||||
@@ -407,12 +416,12 @@ private:
|
||||
|
||||
std::atomic<bool> running {false};
|
||||
|
||||
NodeStatus status4 {NodeStatus::Disconnected},
|
||||
/*NodeStatus status4 {NodeStatus::Disconnected},
|
||||
status6 {NodeStatus::Disconnected};
|
||||
StatusCallback statusCb {nullptr};
|
||||
|
||||
SockAddr bound4 {};
|
||||
SockAddr bound6 {};
|
||||
SockAddr bound6 {};*/
|
||||
};
|
||||
|
||||
}
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
#include <msgpack.hpp>
|
||||
|
||||
#include <sodium.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
@@ -46,7 +48,7 @@ typedef uint16_t in_port_t;
|
||||
|
||||
|
||||
// bytes
|
||||
#define HASH_LEN 20u
|
||||
#define HASH_LEN crypto_box_PUBLICKEYBYTES
|
||||
|
||||
namespace dht {
|
||||
|
||||
@@ -147,6 +149,12 @@ public:
|
||||
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.
|
||||
@@ -242,17 +250,24 @@ public:
|
||||
num ^= (-b ^ num) & (1 << bit);
|
||||
}
|
||||
|
||||
double
|
||||
toFloat() const
|
||||
{
|
||||
double toFloat() const {
|
||||
using D = size_t;
|
||||
double v = 0.;
|
||||
for (unsigned i = 0; i < std::min<size_t>(HASH_LEN, sizeof(unsigned)-1); i++)
|
||||
v += *(cbegin()+i)/(double)(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;
|
||||
}
|
||||
|
||||
bool
|
||||
operator<(const InfoHash& o) const {
|
||||
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];
|
||||
@@ -299,10 +314,25 @@ public:
|
||||
|
||||
static constexpr const InfoHash zeroes {};
|
||||
|
||||
struct NodeExport {
|
||||
struct OPENDHT_PUBLIC NodeExport {
|
||||
InfoHash id;
|
||||
sockaddr_storage ss;
|
||||
socklen_t sslen;
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& pk) const
|
||||
{
|
||||
pk.pack_map(2);
|
||||
pk.pack(std::string("id"));
|
||||
pk.pack(id);
|
||||
pk.pack(std::string("addr"));
|
||||
pk.pack_bin(sslen);
|
||||
pk.pack_bin_body((char*)&ss, sslen);
|
||||
}
|
||||
|
||||
void msgpack_unpack(msgpack::object o);
|
||||
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const InfoHash& h);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -22,10 +22,12 @@
|
||||
#include "dhtrunner.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <chrono>
|
||||
|
||||
namespace dht {
|
||||
|
||||
/**
|
||||
* Logging-related functions
|
||||
*/
|
||||
namespace log {
|
||||
|
||||
/**
|
||||
@@ -73,5 +75,8 @@ enableFileLogging(dht::DhtRunner &dht, const std::string &path);
|
||||
OPENDHT_PUBLIC void
|
||||
disableLogging(dht::DhtRunner &dht);
|
||||
|
||||
OPENDHT_PUBLIC void
|
||||
enableSyslog(dht::DhtRunner &dht, const char* name);
|
||||
|
||||
} /* log */
|
||||
} /* dht */
|
||||
+8
-23
@@ -42,41 +42,26 @@ struct TransPrefix : public std::array<uint8_t, 2> {
|
||||
struct TransId final : public std::array<uint8_t, 4> {
|
||||
static const constexpr uint16_t INVALID {0};
|
||||
|
||||
TransId() { std::fill_n(begin(), 4, 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(const char* q, size_t l) : array<uint8_t, 4>() {
|
||||
if (l > 4) {
|
||||
length = 0;
|
||||
} else {
|
||||
std::copy_n(q, l, begin());
|
||||
length = l;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t getTid() const {
|
||||
return *reinterpret_cast<const uint16_t*>(&(*this)[2]);
|
||||
TransId(uint32_t id) {
|
||||
*reinterpret_cast<uint32_t*>(data()) = htonl(id);
|
||||
}
|
||||
|
||||
uint32_t toInt() const {
|
||||
return *reinterpret_cast<const uint32_t*>(&(*this)[0]);
|
||||
return ntohl(*reinterpret_cast<const uint32_t*>(&(*this)[0]));
|
||||
}
|
||||
|
||||
bool matches(const TransPrefix prefix, uint16_t* tid = nullptr) const {
|
||||
if (std::equal(begin(), begin()+2, prefix.begin())) {
|
||||
if (tid)
|
||||
*tid = getTid();
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
bool matches(const TransPrefix prefix) const {
|
||||
return std::equal(begin(), begin()+2, prefix.begin());
|
||||
}
|
||||
|
||||
unsigned length {4};
|
||||
};
|
||||
|
||||
TransId unpackTid(msgpack::object& o);
|
||||
|
||||
} /* namespace net */
|
||||
} /* dht */
|
||||
@@ -28,13 +28,14 @@
|
||||
#include "rng.h"
|
||||
#include "rate_limiter.h"
|
||||
#include "net.h"
|
||||
#include "callbacks.h"
|
||||
#include "uv_utils.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <queue>
|
||||
|
||||
namespace dht {
|
||||
@@ -81,6 +82,20 @@ private:
|
||||
|
||||
struct ParsedMessage;
|
||||
|
||||
/**
|
||||
* Answer for a request.
|
||||
*/
|
||||
struct RequestAnswer {
|
||||
Blob ntoken {};
|
||||
Value::Id vid {};
|
||||
std::vector<Sp<Value>> values {};
|
||||
std::vector<Sp<FieldValueIndex>> fields {};
|
||||
std::vector<Sp<Node>> nodes4 {};
|
||||
std::vector<Sp<Node>> nodes6 {};
|
||||
RequestAnswer() {}
|
||||
RequestAnswer(ParsedMessage&& msg);
|
||||
};
|
||||
|
||||
/*!
|
||||
* @class NetworkEngine
|
||||
* @brief An abstraction of communication protocol on the network.
|
||||
@@ -101,26 +116,6 @@ struct ParsedMessage;
|
||||
*/
|
||||
class NetworkEngine final
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* @class RequestAnswer
|
||||
* @brief Answer for a request.
|
||||
* @details
|
||||
* Answer for a request to be (de)serialized. Used for reponding to a node
|
||||
* and looking up the response from a node.
|
||||
*/
|
||||
struct RequestAnswer {
|
||||
Blob ntoken {};
|
||||
Value::Id vid {};
|
||||
std::vector<Sp<Value>> values {};
|
||||
std::vector<Sp<FieldValueIndex>> fields {};
|
||||
std::vector<Sp<Node>> nodes4 {};
|
||||
std::vector<Sp<Node>> nodes6 {};
|
||||
RequestAnswer() {}
|
||||
RequestAnswer(ParsedMessage&& msg);
|
||||
};
|
||||
|
||||
|
||||
private:
|
||||
/**
|
||||
* Called when we receive an error message.
|
||||
@@ -137,7 +132,7 @@ private:
|
||||
/**
|
||||
* Called when an addres is reported from a requested node.
|
||||
*
|
||||
* @param h: id
|
||||
* @param h: id
|
||||
* @param saddr_len (type: socklen_t) lenght of the sockaddr struct.
|
||||
*/
|
||||
std::function<void(const InfoHash&, const SockAddr&)> onReportedAddr;
|
||||
@@ -206,12 +201,11 @@ private:
|
||||
const Value::Id&)> onRefresh {};
|
||||
|
||||
public:
|
||||
using SocketCb = std::function<void(const Sp<Node>&, RequestAnswer&&)>;
|
||||
using RequestCb = std::function<void(const Request&, RequestAnswer&&)>;
|
||||
using RequestExpiredCb = std::function<void(const Request&, bool)>;
|
||||
|
||||
NetworkEngine(Logger& log, Scheduler& scheduler);
|
||||
NetworkEngine(InfoHash& myid, NetId net, int s, 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,
|
||||
@@ -224,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.
|
||||
@@ -244,13 +247,7 @@ public:
|
||||
std::vector<Sp<Value>>&& values, const Query& q);
|
||||
|
||||
bool isRunning(sa_family_t af) const;
|
||||
inline want_t want () const { return dht_socket >= 0 && dht_socket6 >= 0 ? (WANT4 | WANT6) : -1; }
|
||||
|
||||
/**
|
||||
* Cancel a request. Setting req->cancelled = true is not enough in the case
|
||||
* a request is "persistent".
|
||||
*/
|
||||
void cancelRequest(Sp<Request>& req);
|
||||
inline want_t want () const { /*return dht_socket >= 0 && dht_socket6 >= 0 ? (WANT4 | WANT6) : -1;*/return WANT4 | WANT6; }
|
||||
|
||||
void connectivityChanged(sa_family_t);
|
||||
|
||||
@@ -268,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.
|
||||
*
|
||||
@@ -280,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));
|
||||
}
|
||||
@@ -297,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,
|
||||
@@ -316,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,
|
||||
@@ -345,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,
|
||||
@@ -366,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,
|
||||
@@ -386,43 +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);
|
||||
/**
|
||||
* Opens a socket on which a node will be able allowed to write for further
|
||||
* additionnal updates following the response to a previous request.
|
||||
*
|
||||
* @param node The node which will be allowed to write on this socket.
|
||||
* @param cb The callback to execute once updates arrive on the socket.
|
||||
*
|
||||
* @return the socket.
|
||||
*/
|
||||
Sp<Socket> openSocket(const Sp<Node>& node, TransPrefix tp, SocketCb&& cb);
|
||||
|
||||
/**
|
||||
* Closes a socket so that no further data will be red on that socket.
|
||||
*
|
||||
* @param socket The socket to close.
|
||||
*/
|
||||
void closeSocket(Sp<Socket> socket);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
@@ -440,6 +409,10 @@ public:
|
||||
return cache.getCachedNodes(id, sa_f, count);
|
||||
}
|
||||
|
||||
SockAddr getBoundAddr() {
|
||||
return sock->getBoundAddr();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
struct PartialMessage;
|
||||
@@ -447,7 +420,7 @@ private:
|
||||
/***************
|
||||
* Constants *
|
||||
***************/
|
||||
static constexpr long unsigned MAX_REQUESTS_PER_SEC {1600};
|
||||
static constexpr size_t MAX_REQUESTS_PER_SEC {1600};
|
||||
/* the length of a node info buffer in ipv4 format */
|
||||
static const constexpr size_t NODE4_INFO_BUF_LEN {HASH_LEN + sizeof(in_addr) + sizeof(in_port_t)};
|
||||
/* the length of a node info buffer in ipv6 format */
|
||||
@@ -464,12 +437,24 @@ private:
|
||||
static constexpr unsigned BLACKLISTED_MAX {10};
|
||||
|
||||
static constexpr size_t MTU {1280};
|
||||
static constexpr size_t MAX_PACKET_VALUE_SIZE {8 * 1024};
|
||||
static constexpr size_t MAX_PACKET_VALUE_SIZE {600};
|
||||
|
||||
static const std::string my_v;
|
||||
static std::mt19937 rd_device;
|
||||
|
||||
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);
|
||||
|
||||
@@ -482,17 +467,7 @@ private:
|
||||
* Sends a request to a node. Request::MAX_ATTEMPT_COUNT attempts will
|
||||
* be made before the request expires.
|
||||
*/
|
||||
void sendRequest(Sp<Request>& request);
|
||||
|
||||
/**
|
||||
* Generates a new request id, skipping the invalid id.
|
||||
*
|
||||
* @return the new id.
|
||||
*/
|
||||
uint16_t getNewTid() {
|
||||
++transaction_id;
|
||||
return transaction_id == TransId::INVALID ? ++transaction_id : transaction_id;
|
||||
}
|
||||
void sendRequest(const Sp<Request>& request);
|
||||
|
||||
struct MessageStats {
|
||||
unsigned ping {0};
|
||||
@@ -504,20 +479,24 @@ private:
|
||||
};
|
||||
|
||||
|
||||
// basic wrapper for socket sendto function
|
||||
void 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);
|
||||
|
||||
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>>&);
|
||||
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, TransId tid);
|
||||
void sendPong(const Sp<Node>& node, TransId tid);
|
||||
/* answer to findnodes/getvalues request */
|
||||
void sendNodesValues(const SockAddr& addr,
|
||||
void sendNodesValues(const Sp<Node>& node,
|
||||
TransId tid,
|
||||
const Blob& nodes,
|
||||
const Blob& nodes6,
|
||||
@@ -532,66 +511,42 @@ private:
|
||||
std::vector<Sp<Node>>& nodes,
|
||||
std::vector<Sp<Node>>& nodes6);
|
||||
/* answer to a listen request */
|
||||
void sendListenConfirmation(const SockAddr& addr, TransId tid);
|
||||
void sendListenConfirmation(const Sp<Node>& node, TransId tid);
|
||||
/* answer to put request */
|
||||
void sendValueAnnounced(const SockAddr& addr, TransId, Value::Id);
|
||||
void sendValueAnnounced(const Sp<Node>& node, TransId, Value::Id);
|
||||
/* answer in case of error */
|
||||
void sendError(const SockAddr& addr,
|
||||
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 {-1};
|
||||
const int dht_socket6 {-1};
|
||||
const Logger& DHT_LOG;
|
||||
|
||||
Sp<UdpSocket> sock;
|
||||
Sp<TcpSocket> tcp_sock;
|
||||
std::set<Sp<TcpSocket>> pending_connect;
|
||||
NodeCache cache {};
|
||||
|
||||
/**
|
||||
* A comparator to classify IP addresses, only considering the
|
||||
* first 64 bits in IPv6.
|
||||
*/
|
||||
struct cmpSockAddr {
|
||||
bool operator()(const SockAddr& a, const SockAddr& b) const {
|
||||
if (a.second != b.second)
|
||||
return a.second < b.second;
|
||||
socklen_t start, len;
|
||||
switch(a.getFamily()) {
|
||||
case AF_INET:
|
||||
start = offsetof(sockaddr_in, sin_addr);
|
||||
len = sizeof(in_addr);
|
||||
break;
|
||||
case AF_INET6:
|
||||
start = offsetof(sockaddr_in6, sin6_addr);
|
||||
// don't consider more than 64 bits (IPv6)
|
||||
len = 8;
|
||||
break;
|
||||
default:
|
||||
start = 0;
|
||||
len = a.second;
|
||||
break;
|
||||
}
|
||||
crypto::EcSecretKey id_key;
|
||||
//crypto::EcSecretKey id_key;
|
||||
|
||||
return std::memcmp((uint8_t*)&a.first+start, (uint8_t*)&b.first+start, len) < 0;
|
||||
}
|
||||
};
|
||||
// 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>;
|
||||
IpLimiterMap address_rate_limiter {};
|
||||
RateLimiter<MAX_REQUESTS_PER_SEC> rate_limiter {};
|
||||
size_t limiter_maintenance {0};
|
||||
|
||||
// requests handling
|
||||
uint16_t transaction_id {1};
|
||||
std::map<TransId, Sp<Request>> requests {};
|
||||
std::map<TransId, PartialMessage> partial_messages;
|
||||
std::map<TransId, Sp<Socket>> opened_sockets {};
|
||||
|
||||
MessageStats in_stats {}, out_stats {};
|
||||
std::set<SockAddr> blacklist {};
|
||||
|
||||
+104
-19
@@ -22,36 +22,70 @@
|
||||
#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>
|
||||
|
||||
namespace dht {
|
||||
|
||||
struct Node;
|
||||
namespace net {
|
||||
struct Request;
|
||||
struct Socket;
|
||||
struct RequestAnswer;
|
||||
} /* namespace net */
|
||||
|
||||
using SocketCb = std::function<void(const Sp<Node>&, net::RequestAnswer&&)>;
|
||||
using SocketId = uint32_t;
|
||||
struct Socket {
|
||||
Socket() {}
|
||||
Socket(SocketCb&& on_receive) :
|
||||
on_receive(std::move(on_receive)) {}
|
||||
SocketCb on_receive {};
|
||||
};
|
||||
|
||||
struct Node {
|
||||
InfoHash id;
|
||||
SockAddr addr;
|
||||
const InfoHash id;
|
||||
|
||||
time_point time {time_point::min()}; /* last time eared about */
|
||||
time_point reply_time {time_point::min()}; /* time of last correct reply received */
|
||||
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)
|
||||
: id(id), addr(sa, salen) {}
|
||||
Node(const InfoHash& id, const SockAddr& addr) : id(id), addr(addr) {}
|
||||
: Node(id, SockAddr(sa, salen)) {}
|
||||
~Node() {
|
||||
if (sock)
|
||||
sock->close();
|
||||
}
|
||||
|
||||
InfoHash getId() const {
|
||||
return id;
|
||||
}
|
||||
std::pair<const sockaddr*, socklen_t> getAddr() const {
|
||||
return {(const sockaddr*)&addr.first, addr.second};
|
||||
}
|
||||
const SockAddr& getAddr() const { return addr; }
|
||||
std::string getAddrStr() const {
|
||||
return addr.toString();
|
||||
}
|
||||
|
||||
bool isClient() const { return is_client; }
|
||||
bool isIncoming() { return time > reply_time; }
|
||||
|
||||
const time_point& getTime() const { return time; }
|
||||
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.
|
||||
@@ -68,21 +102,70 @@ struct Node {
|
||||
bool isPendingMessage() const;
|
||||
size_t getPendingMessageCount() const;
|
||||
|
||||
NodeExport exportNode() const { return NodeExport {id, addr.first, addr.second}; }
|
||||
bool isOld(const time_point& now) const {
|
||||
return time + NODE_EXPIRE_TIME < now;
|
||||
}
|
||||
bool isRemovable(const time_point& now) const {
|
||||
return isExpired() and isOld(now);
|
||||
}
|
||||
|
||||
NodeExport exportNode() const {
|
||||
NodeExport ne;
|
||||
ne.id = id;
|
||||
ne.sslen = addr.getLength();
|
||||
std::memcpy(&ne.ss, addr.get(), ne.sslen);
|
||||
return ne;
|
||||
}
|
||||
sa_family_t getFamily() const { return addr.getFamily(); }
|
||||
|
||||
void update(const SockAddr&);
|
||||
void update(const SockAddr&, const Sp<TcpSocket>&);
|
||||
|
||||
void requested(std::shared_ptr<net::Request>& req);
|
||||
void received(time_point now, std::shared_ptr<net::Request> req);
|
||||
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(const net::TransId& tid);
|
||||
void cancelRequest(const Sp<net::Request>& req);
|
||||
|
||||
void setExpired();
|
||||
|
||||
/**
|
||||
* Opens a socket on which a node will be able allowed to write for further
|
||||
* additionnal updates following the response to a previous request.
|
||||
*
|
||||
* @param node The node which will be allowed to write on this socket.
|
||||
* @param cb The callback to execute once updates arrive on the socket.
|
||||
*
|
||||
* @return the socket.
|
||||
*/
|
||||
SocketId openSocket(SocketCb&& cb);
|
||||
|
||||
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(SocketId id);
|
||||
|
||||
/**
|
||||
* Resets the state of the node so it's not expired anymore.
|
||||
*/
|
||||
void reset() { expired_ = false; reply_time = time_point::min(); }
|
||||
|
||||
/**
|
||||
* Generates a new request id, skipping the invalid id.
|
||||
*
|
||||
* @return the new id.
|
||||
*/
|
||||
uint16_t getNewTid() {
|
||||
++transaction_id;
|
||||
return transaction_id == net::TransId::INVALID ? ++transaction_id : transaction_id;
|
||||
}
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const Node& h);
|
||||
@@ -99,15 +182,17 @@ private:
|
||||
/* Number of times we accept authentication errors from this node. */
|
||||
static const constexpr unsigned MAX_AUTH_ERRORS {3};
|
||||
|
||||
std::list<std::weak_ptr<net::Request>> requests_ {};
|
||||
SockAddr addr;
|
||||
bool is_client {false};
|
||||
time_point time {time_point::min()}; /* last time eared about */
|
||||
time_point reply_time {time_point::min()}; /* time of last correct reply received */
|
||||
unsigned auth_errors {0};
|
||||
bool expired_ {false};
|
||||
SocketId transaction_id;
|
||||
using TransactionDist = std::uniform_int_distribution<decltype(transaction_id)>;
|
||||
|
||||
void clearPendingQueue() {
|
||||
requests_.remove_if([](std::weak_ptr<net::Request>& w) {
|
||||
return w.expired();
|
||||
});
|
||||
}
|
||||
std::map<net::TransId, Sp<net::Request>> requests_ {};
|
||||
std::map<SocketId, Socket> sockets_;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -26,9 +26,9 @@
|
||||
namespace dht {
|
||||
|
||||
struct NodeCache {
|
||||
std::shared_ptr<Node> getNode(const InfoHash& id, sa_family_t family);
|
||||
std::shared_ptr<Node> getNode(const InfoHash& id, const SockAddr&, time_point now, bool confirmed);
|
||||
std::vector<std::shared_ptr<Node>> getCachedNodes(const InfoHash& id, sa_family_t sa_f, size_t count);
|
||||
Sp<Node> getNode(const InfoHash& id, sa_family_t family);
|
||||
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;
|
||||
|
||||
/**
|
||||
* Reset the connectivity state of every node,
|
||||
@@ -36,15 +36,19 @@ struct NodeCache {
|
||||
* To use in case of connectivity change etc.
|
||||
*/
|
||||
void clearBadNodes(sa_family_t family = 0);
|
||||
void closeAll(sa_family_t family = 0);
|
||||
|
||||
private:
|
||||
class NodeMap : public std::map<InfoHash, std::weak_ptr<Node>> {
|
||||
public:
|
||||
std::shared_ptr<Node> getNode(const InfoHash& id);
|
||||
std::shared_ptr<Node> getNode(const InfoHash& id, const SockAddr&, time_point now, bool confirmed);
|
||||
Sp<Node> getNode(const InfoHash& id);
|
||||
Sp<Node> getNode(const InfoHash& id, const SockAddr&, const Sp<TcpSocket>&, time_point now, bool confirmed, bool client);
|
||||
void clearBadNodes();
|
||||
void closeAll();
|
||||
};
|
||||
|
||||
const NodeMap& cache(sa_family_t af) const { return af == AF_INET ? cache_4 : cache_6; }
|
||||
NodeMap& cache(sa_family_t af) { return af == AF_INET ? cache_4 : cache_6; }
|
||||
NodeMap cache_4;
|
||||
NodeMap cache_6;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2017 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -26,10 +26,10 @@ namespace dht {
|
||||
template<size_t Quota, unsigned long Perdiod=1>
|
||||
class RateLimiter {
|
||||
public:
|
||||
/** Clear outdated records and return current quota usage */
|
||||
/** Clear outdated records and return current quota usage */
|
||||
size_t maintain(const time_point& now) {
|
||||
using namespace std::chrono;
|
||||
while (not records.empty() and duration_cast<seconds>(now - records.front()) >= seconds(Perdiod))
|
||||
auto limit = now - std::chrono::seconds(Perdiod);
|
||||
while (not records.empty() and records.front() < limit)
|
||||
records.pop();
|
||||
return records.size();
|
||||
}
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
namespace dht {
|
||||
|
||||
static constexpr unsigned TARGET_NODES {8};
|
||||
namespace net {
|
||||
class NetworkEngine;
|
||||
}
|
||||
|
||||
struct Bucket {
|
||||
Bucket() : cached() {}
|
||||
@@ -37,12 +40,25 @@ struct Bucket {
|
||||
|
||||
/** Return a random node in a 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> {
|
||||
public:
|
||||
using std::list<Bucket>::list;
|
||||
|
||||
time_point grow_time {time_point::min()};
|
||||
bool is_client {false};
|
||||
|
||||
InfoHash middle(const RoutingTable::const_iterator&) const;
|
||||
|
||||
std::vector<Sp<Node>> findClosestNodes(const InfoHash id, time_point now, size_t count = TARGET_NODES) const;
|
||||
@@ -65,6 +81,14 @@ public:
|
||||
return empty() || (size() == 1 && front().nodes.empty());
|
||||
}
|
||||
|
||||
void connectivityChanged(const time_point& now) {
|
||||
grow_time = now;
|
||||
for (auto& b : *this)
|
||||
b.time = time_point::min();
|
||||
}
|
||||
|
||||
bool onNewNode(const Sp<Node>& node, int comfirm, const time_point& now, const InfoHash& myid, net::NetworkEngine& ne);
|
||||
|
||||
/**
|
||||
* Return a random id in the bucket's range.
|
||||
*/
|
||||
|
||||
+86
-41
@@ -22,6 +22,9 @@
|
||||
|
||||
#include "utils.h"
|
||||
#include "log_enable.h"
|
||||
#include "uv_utils.h"
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
@@ -36,12 +39,7 @@ namespace dht {
|
||||
*/
|
||||
class Scheduler {
|
||||
public:
|
||||
Scheduler(const Logger& l) : DHT_LOG(l) {}
|
||||
|
||||
struct Job {
|
||||
Job(std::function<void()>&& f) : do_(std::move(f)) {}
|
||||
std::function<void()> do_;
|
||||
};
|
||||
Scheduler(uv_loop_t* loop, const Logger& l) : loop_(loop), DHT_LOG(l) {}
|
||||
|
||||
/**
|
||||
* Adds another job to the queue.
|
||||
@@ -51,10 +49,32 @@ 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -66,7 +86,9 @@ public:
|
||||
*
|
||||
* @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;
|
||||
@@ -75,50 +97,73 @@ public:
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
* id: the identity to use for the crypto layer and to compute
|
||||
* our own hash on the Dht.
|
||||
*/
|
||||
SecureDht(int s, int s6, Config config);
|
||||
SecureDht(uv_loop_t* loop, Config config);
|
||||
|
||||
virtual ~SecureDht();
|
||||
|
||||
@@ -158,7 +158,7 @@ private:
|
||||
const ValueType CERTIFICATE_TYPE = {
|
||||
8, "Certificate", std::chrono::hours(24 * 7),
|
||||
// A certificate can only be stored at its public key ID.
|
||||
[](InfoHash id, Sp<Value>& v, InfoHash, const sockaddr*, socklen_t) {
|
||||
[](InfoHash id, Sp<Value>& v, const InfoHash&, const SockAddr&) {
|
||||
try {
|
||||
crypto::Certificate crt(v->data);
|
||||
// TODO check certificate signature
|
||||
@@ -166,7 +166,7 @@ const ValueType CERTIFICATE_TYPE = {
|
||||
} catch (const std::exception& e) {}
|
||||
return false;
|
||||
},
|
||||
[](InfoHash, const Sp<Value>& o, Sp<Value>& n, InfoHash, const sockaddr*, socklen_t) {
|
||||
[](InfoHash, const Sp<Value>& o, Sp<Value>& n, const InfoHash&, const SockAddr&) {
|
||||
try {
|
||||
return crypto::Certificate(o->data).getPublicKey().getId() == crypto::Certificate(n->data).getPublicKey().getId();
|
||||
} catch (const std::exception& e) {}
|
||||
|
||||
+142
-38
@@ -34,56 +34,116 @@ typedef uint16_t sa_family_t;
|
||||
typedef uint16_t in_port_t;
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
|
||||
namespace dht {
|
||||
|
||||
OPENDHT_PUBLIC std::string print_addr(const sockaddr* sa, socklen_t slen);
|
||||
OPENDHT_PUBLIC std::string print_addr(const sockaddr_storage& ss, socklen_t sslen);
|
||||
|
||||
struct OPENDHT_PUBLIC SockAddr : public std::pair<sockaddr_storage, socklen_t> {
|
||||
/**
|
||||
* A Socket Address (sockaddr*), with abstraction for IPv4, IPv6 address families.
|
||||
*/
|
||||
class OPENDHT_PUBLIC SockAddr {
|
||||
public:
|
||||
SockAddr() : pair<sockaddr_storage, socklen_t>::pair({},0) {}
|
||||
SockAddr(const SockAddr& o) : pair<sockaddr_storage, socklen_t>::pair({},o.second) {
|
||||
std::copy_n((uint8_t*)&o.first, o.second, (uint8_t*)&first);
|
||||
SockAddr() {}
|
||||
SockAddr(const SockAddr& o) {
|
||||
set(o.get(), o.getLength());
|
||||
}
|
||||
SockAddr(const sockaddr* sa, socklen_t len) : pair<sockaddr_storage, socklen_t>::pair({},len) {
|
||||
if (len > sizeof(sockaddr_storage))
|
||||
SockAddr(SockAddr&& o) : len(o.len), addr(std::move(o.addr)) {
|
||||
o.len = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build from existing address.
|
||||
*/
|
||||
SockAddr(const sockaddr* sa, socklen_t length) {
|
||||
if (length > sizeof(sockaddr_storage))
|
||||
throw std::runtime_error("Socket address length is too large");
|
||||
std::copy_n((uint8_t*)sa, len, (uint8_t*)&first);
|
||||
set(sa, length);
|
||||
}
|
||||
SockAddr(const sockaddr* sa) {
|
||||
socklen_t len = 0;
|
||||
if (sa) {
|
||||
if (sa->sa_family == AF_INET)
|
||||
len = sizeof(sockaddr_in);
|
||||
else if(sa->sa_family == AF_INET6)
|
||||
len = sizeof(sockaddr_in6);
|
||||
else
|
||||
throw std::runtime_error("Unknown address family");
|
||||
}
|
||||
set(sa, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build from an existing sockaddr_storage structure.
|
||||
*/
|
||||
SockAddr(const sockaddr_storage& ss, socklen_t len) : SockAddr((const sockaddr*)&ss, len) {}
|
||||
|
||||
bool operator<(const SockAddr& o) const {
|
||||
if (second != o.second)
|
||||
return second < o.second;
|
||||
return std::memcmp((uint8_t*)&first, (uint8_t*)&o.first, second) < 0;
|
||||
if (len != o.len)
|
||||
return len < o.len;
|
||||
return std::memcmp((const uint8_t*)get(), (const uint8_t*)o.get(), len) < 0;
|
||||
}
|
||||
|
||||
bool equals(const SockAddr& o) const {
|
||||
return second == o.second
|
||||
&& std::memcmp((uint8_t*)&first, (uint8_t*)&o.first, second) == 0;
|
||||
return len == o.len
|
||||
&& std::memcmp((const uint8_t*)get(), (const uint8_t*)o.get(), len) == 0;
|
||||
}
|
||||
SockAddr& operator=(const SockAddr& o) {
|
||||
std::copy_n((const uint8_t*)&o.first, o.second, (uint8_t*)&first);
|
||||
second = o.second;
|
||||
set(o.get(), o.getLength());
|
||||
return *this;
|
||||
}
|
||||
SockAddr& operator=(SockAddr&& o) {
|
||||
len = o.len;
|
||||
o.len = 0;
|
||||
addr = std::move(o.addr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
return print_addr(first, second);
|
||||
}
|
||||
sa_family_t getFamily() const { return second > sizeof(sa_family_t) ? first.ss_family : AF_UNSPEC; }
|
||||
void setFamily(sa_family_t af) {
|
||||
first.ss_family = af;
|
||||
switch(af) {
|
||||
case AF_INET:
|
||||
second = sizeof(sockaddr_in);
|
||||
break;
|
||||
case AF_INET6:
|
||||
second = sizeof(sockaddr_in6);
|
||||
break;
|
||||
}
|
||||
return print_addr(get(), getLength());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address family or AF_UNSPEC if the address is not set.
|
||||
*/
|
||||
sa_family_t getFamily() const { return len > sizeof(sa_family_t) ? addr->sa_family : AF_UNSPEC; }
|
||||
|
||||
/**
|
||||
* Resize the managed structure to the appropriate size (if needed),
|
||||
* in which case the sockaddr structure is cleared to zero,
|
||||
* and set the address family field (sa_family).
|
||||
*/
|
||||
void setFamily(sa_family_t af) {
|
||||
socklen_t new_length;
|
||||
switch(af) {
|
||||
case AF_INET:
|
||||
new_length = sizeof(sockaddr_in);
|
||||
break;
|
||||
case AF_INET6:
|
||||
new_length = sizeof(sockaddr_in6);
|
||||
break;
|
||||
default:
|
||||
new_length = 0;
|
||||
}
|
||||
if (new_length != len) {
|
||||
len = new_length;
|
||||
if (len) addr.reset((sockaddr*)std::calloc(len, 1));
|
||||
else addr.reset();
|
||||
}
|
||||
if (len > sizeof(sa_family_t))
|
||||
addr->sa_family = af;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retreive the port (in host byte order) or 0 if the address is not
|
||||
* of a supported family.
|
||||
*/
|
||||
in_port_t getPort() const {
|
||||
switch(getFamily()) {
|
||||
case AF_INET:
|
||||
@@ -94,6 +154,10 @@ public:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Set the port. The address must be of a supported family.
|
||||
* @param p The port in host byte order.
|
||||
*/
|
||||
void setPort(in_port_t p) {
|
||||
switch(getFamily()) {
|
||||
case AF_INET:
|
||||
@@ -105,17 +169,42 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the accessible byte length at the pointer returned by #get().
|
||||
* If zero, #get() returns null.
|
||||
*/
|
||||
socklen_t getLength() const { return len; }
|
||||
|
||||
/**
|
||||
* An address is defined to be true if its length is not zero.
|
||||
*/
|
||||
explicit operator bool() const noexcept {
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address to the managed sockaddr structure.
|
||||
* The accessible length is returned by #getLength().
|
||||
*/
|
||||
const sockaddr* get() const { return addr.get(); }
|
||||
|
||||
/**
|
||||
* Returns the address to the managed sockaddr structure.
|
||||
* The accessible length is returned by #getLength().
|
||||
*/
|
||||
sockaddr* get() { return addr.get(); }
|
||||
|
||||
const sockaddr_in& getIPv4() const {
|
||||
return *reinterpret_cast<const sockaddr_in*>(&first);
|
||||
return *reinterpret_cast<const sockaddr_in*>(get());
|
||||
}
|
||||
const sockaddr_in6& getIPv6() const {
|
||||
return *reinterpret_cast<const sockaddr_in6*>(&first);
|
||||
return *reinterpret_cast<const sockaddr_in6*>(get());
|
||||
}
|
||||
sockaddr_in& getIPv4() {
|
||||
return *reinterpret_cast<sockaddr_in*>(&first);
|
||||
return *reinterpret_cast<sockaddr_in*>(get());
|
||||
}
|
||||
sockaddr_in6& getIPv6() {
|
||||
return *reinterpret_cast<sockaddr_in6*>(&first);
|
||||
return *reinterpret_cast<sockaddr_in6*>(get());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,14 +219,17 @@ public:
|
||||
|
||||
bool isUnspecified() const;
|
||||
|
||||
bool isMappedIPv4() const;
|
||||
SockAddr getMappedIPv4() const;
|
||||
|
||||
/**
|
||||
* A comparator to classify IP addresses, only considering the
|
||||
* first 64 bits in IPv6.
|
||||
*/
|
||||
struct ipCmp {
|
||||
bool operator()(const SockAddr& a, const SockAddr& b) const {
|
||||
if (a.second != b.second)
|
||||
return a.second < b.second;
|
||||
if (a.len != b.len)
|
||||
return a.len < b.len;
|
||||
socklen_t start, len;
|
||||
switch(a.getFamily()) {
|
||||
case AF_INET:
|
||||
@@ -151,18 +243,30 @@ public:
|
||||
break;
|
||||
default:
|
||||
start = 0;
|
||||
len = a.second;
|
||||
len = a.len;
|
||||
break;
|
||||
}
|
||||
|
||||
return std::memcmp((uint8_t*)&a.first+start, (uint8_t*)&b.first+start, len) < 0;
|
||||
return std::memcmp((uint8_t*)a.get()+start,
|
||||
(uint8_t*)b.get()+start, len) < 0;
|
||||
}
|
||||
};
|
||||
private:
|
||||
socklen_t len {0};
|
||||
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*)std::malloc(len));
|
||||
else addr.reset();
|
||||
}
|
||||
if (len)
|
||||
std::memcpy((uint8_t*)get(), (const uint8_t*)sa, len);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
OPENDHT_PUBLIC bool operator==(const SockAddr& a, const SockAddr& b);
|
||||
|
||||
OPENDHT_PUBLIC std::string printAddr(const SockAddr& addr);
|
||||
|
||||
}
|
||||
@@ -32,6 +32,9 @@
|
||||
#define WANT4 1
|
||||
#define WANT6 2
|
||||
|
||||
/**
|
||||
* OpenDHT C++ namespace
|
||||
*/
|
||||
namespace dht {
|
||||
|
||||
using NetId = uint32_t;
|
||||
@@ -93,6 +96,9 @@ public:
|
||||
|
||||
// Serialization related definitions and utility functions
|
||||
|
||||
/**
|
||||
* Arbitrary binary data.
|
||||
*/
|
||||
using Blob = std::vector<uint8_t>;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
+35
-11
@@ -22,6 +22,7 @@
|
||||
#include "infohash.h"
|
||||
#include "crypto.h"
|
||||
#include "utils.h"
|
||||
#include "sockaddr.h"
|
||||
|
||||
#include <msgpack.hpp>
|
||||
|
||||
@@ -51,7 +52,7 @@ struct Query;
|
||||
* @param form_addr: network address of the incoming request.
|
||||
* @param from_len: network address lendth of the incoming request.
|
||||
*/
|
||||
using StorePolicy = std::function<bool(InfoHash key, std::shared_ptr<Value>& value, InfoHash from, const sockaddr* from_addr, socklen_t from_len)>;
|
||||
using StorePolicy = std::function<bool(InfoHash key, std::shared_ptr<Value>& value, const InfoHash& from, const SockAddr& addr)>;
|
||||
|
||||
/**
|
||||
* An edition policy is applied once to every incoming value storage requests,
|
||||
@@ -67,15 +68,15 @@ using StorePolicy = std::function<bool(InfoHash key, std::shared_ptr<Value>& val
|
||||
* @param form_addr: network address of the incoming request.
|
||||
* @param from_len: network address lendth of the incoming request.
|
||||
*/
|
||||
using EditPolicy = std::function<bool(InfoHash key, const std::shared_ptr<Value>& old_val, std::shared_ptr<Value>& new_val, InfoHash from, const sockaddr* from_addr, socklen_t from_len)>;
|
||||
using EditPolicy = std::function<bool(InfoHash key, const std::shared_ptr<Value>& old_val, std::shared_ptr<Value>& new_val, const InfoHash& from, const SockAddr& addr)>;
|
||||
|
||||
static constexpr const size_t MAX_VALUE_SIZE {1024 * 64};
|
||||
|
||||
struct OPENDHT_PUBLIC ValueType {
|
||||
typedef uint16_t Id;
|
||||
|
||||
static bool DEFAULT_STORE_POLICY(InfoHash, std::shared_ptr<Value>& v, InfoHash, const sockaddr*, socklen_t);
|
||||
static bool DEFAULT_EDIT_POLICY(InfoHash, const std::shared_ptr<Value>&, std::shared_ptr<Value>&, InfoHash, const sockaddr*, socklen_t) {
|
||||
static bool DEFAULT_STORE_POLICY(InfoHash, std::shared_ptr<Value>& v, const InfoHash&, const SockAddr&);
|
||||
static bool DEFAULT_EDIT_POLICY(InfoHash, const std::shared_ptr<Value>&, std::shared_ptr<Value>&, const InfoHash&, const SockAddr&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -651,7 +652,8 @@ struct OPENDHT_PUBLIC Select
|
||||
* @return the resulting Select instance.
|
||||
*/
|
||||
Select& field(Value::Field field) {
|
||||
fieldSelection_.emplace_back(field);
|
||||
if (std::find(fieldSelection_.begin(), fieldSelection_.end(), field) == fieldSelection_.end())
|
||||
fieldSelection_.emplace_back(field);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -670,6 +672,12 @@ struct OPENDHT_PUBLIC Select
|
||||
fieldSelection_ = o.as<decltype(fieldSelection_)>();
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
std::stringstream ss;
|
||||
ss << *this;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<<(std::ostream& s, const dht::Select& q);
|
||||
private:
|
||||
std::vector<Value::Field> fieldSelection_ {};
|
||||
@@ -697,7 +705,9 @@ struct OPENDHT_PUBLIC Where
|
||||
* @return the resulting Where instance.
|
||||
*/
|
||||
Where& id(Value::Id id) {
|
||||
filters_.emplace_back(Value::Field::Id, id);
|
||||
FieldValue fv {Value::Field::Id, id};
|
||||
if (std::find(filters_.begin(), filters_.end(), fv) == filters_.end())
|
||||
filters_.emplace_back(std::move(fv));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -709,7 +719,9 @@ struct OPENDHT_PUBLIC Where
|
||||
* @return the resulting Where instance.
|
||||
*/
|
||||
Where& valueType(ValueType::Id type) {
|
||||
filters_.emplace_back(Value::Field::ValueType, type);
|
||||
FieldValue fv {Value::Field::ValueType, type};
|
||||
if (std::find(filters_.begin(), filters_.end(), fv) == filters_.end())
|
||||
filters_.emplace_back(std::move(fv));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -721,7 +733,9 @@ struct OPENDHT_PUBLIC Where
|
||||
* @return the resulting Where instance.
|
||||
*/
|
||||
Where& owner(InfoHash owner_pk_hash) {
|
||||
filters_.emplace_back(Value::Field::OwnerPk, owner_pk_hash);
|
||||
FieldValue fv {Value::Field::OwnerPk, owner_pk_hash};
|
||||
if (std::find(filters_.begin(), filters_.end(), fv) == filters_.end())
|
||||
filters_.emplace_back(std::move(fv));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -733,7 +747,9 @@ struct OPENDHT_PUBLIC Where
|
||||
* @return the resulting Where instance.
|
||||
*/
|
||||
Where& seq(uint16_t seq_no) {
|
||||
filters_.emplace_back(Value::Field::SeqNum, seq_no);
|
||||
FieldValue fv {Value::Field::SeqNum, seq_no};
|
||||
if (std::find(filters_.begin(), filters_.end(), fv) == filters_.end())
|
||||
filters_.emplace_back(std::move(fv));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -745,7 +761,9 @@ struct OPENDHT_PUBLIC Where
|
||||
* @return the resulting Where instance.
|
||||
*/
|
||||
Where& userType(std::string user_type) {
|
||||
filters_.emplace_back(Value::Field::UserType, Blob {user_type.begin(), user_type.end()});
|
||||
FieldValue fv {Value::Field::UserType, Blob {user_type.begin(), user_type.end()}};
|
||||
if (std::find(filters_.begin(), filters_.end(), fv) == filters_.end())
|
||||
filters_.emplace_back(std::move(fv));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -772,6 +790,12 @@ struct OPENDHT_PUBLIC Where
|
||||
filters_ = o.as<decltype(filters_)>();
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
std::stringstream ss;
|
||||
ss << *this;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<<(std::ostream& s, const dht::Where& q);
|
||||
|
||||
private:
|
||||
@@ -828,7 +852,7 @@ struct OPENDHT_PUBLIC Query
|
||||
|
||||
void msgpack_unpack(const msgpack::object& o);
|
||||
|
||||
std::string toString() {
|
||||
std::string toString() const {
|
||||
std::stringstream ss;
|
||||
ss << *this;
|
||||
return ss.str();
|
||||
|
||||
+1
-1
@@ -7,5 +7,5 @@ Description: C++ Distributed Hash Table library
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lopendht
|
||||
Libs.private: -lpthread
|
||||
Requires.private: gnutls >= 3.1
|
||||
Requires.private: gnutls >= 3.1@argon2_lib@
|
||||
Cflags: -I${includedir}
|
||||
@@ -9,3 +9,6 @@ add_custom_target(python ALL
|
||||
DEPENDS opendht opendht_cpp.pxd opendht.pyx)
|
||||
|
||||
install(CODE "execute_process(COMMAND python3 setup.py install --root=\$ENV{DESTDIR}/ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})")
|
||||
if (OPENDHT_TOOLS)
|
||||
install(PROGRAMS tools/dhtcluster.py DESTINATION ${CMAKE_INSTALL_BINDIR} RENAME dhtcluster)
|
||||
endif()
|
||||
+85
-17
@@ -58,10 +58,12 @@ cdef inline void shutdown_callback(void* user_data) with gil:
|
||||
ref.Py_DECREF(cbs)
|
||||
|
||||
cdef inline bool get_callback(shared_ptr[cpp.Value] value, void *user_data) with gil:
|
||||
cb = (<object>user_data)['get']
|
||||
cbs = <object>user_data
|
||||
cb = cbs['get']
|
||||
f = cbs['filter'] if 'filter' in cbs else None
|
||||
pv = Value()
|
||||
pv._value = value
|
||||
return cb(pv)
|
||||
return cb(pv) if not f or f(pv) else True
|
||||
|
||||
cdef inline void done_callback(bool done, cpp.vector[shared_ptr[cpp.Node]]* nodes, void *user_data) with gil:
|
||||
node_ids = []
|
||||
@@ -164,6 +166,59 @@ cdef class NodeEntry(_WithID):
|
||||
n._node = self._v.second
|
||||
return n
|
||||
|
||||
cdef class Query(object):
|
||||
cdef cpp.Query _query
|
||||
def __cinit__(self, str q_str=''):
|
||||
self._query = cpp.Query(q_str.encode())
|
||||
def __str__(self):
|
||||
return self._query.toString().decode()
|
||||
def buildFrom(self, Select s, Where w):
|
||||
self._query = cpp.Query(s._select, w._where)
|
||||
def isSatisfiedBy(self, Query q):
|
||||
return self._query.isSatisfiedBy(q._query)
|
||||
|
||||
cdef class Select(object):
|
||||
cdef cpp.Select _select
|
||||
def __cinit__(self, str q_str=None):
|
||||
if q_str:
|
||||
self._select = cpp.Select(q_str.encode())
|
||||
else:
|
||||
self._select = cpp.Select()
|
||||
def __str__(self):
|
||||
return self._select.toString().decode()
|
||||
def isSatisfiedBy(self, Select os):
|
||||
return self._select.isSatisfiedBy(os._select)
|
||||
def field(self, int field):
|
||||
self._select.field(<cpp.Field> field)
|
||||
return self
|
||||
|
||||
cdef class Where(object):
|
||||
cdef cpp.Where _where
|
||||
def __cinit__(self, str q_str=None):
|
||||
if q_str:
|
||||
self._where = cpp.Where(q_str.encode())
|
||||
else:
|
||||
self._where = cpp.Where()
|
||||
def __str__(self):
|
||||
return self._where.toString().decode()
|
||||
def isSatisfiedBy(self, Where where):
|
||||
return self._where.isSatisfiedBy(where._where)
|
||||
def id(self, cpp.uint64_t id):
|
||||
self._where.id(id)
|
||||
return self
|
||||
def valueType(self, cpp.uint16_t type):
|
||||
self._where.valueType(type)
|
||||
return self
|
||||
def owner(self, InfoHash owner_pk_hash):
|
||||
self._where.owner(owner_pk_hash._infohash)
|
||||
return self
|
||||
def seq(self, cpp.uint16_t seq_no):
|
||||
self._where.seq(seq_no)
|
||||
return self
|
||||
def userType(self, str user_type):
|
||||
self._where.userType(user_type.encode())
|
||||
return self
|
||||
|
||||
cdef class Value(object):
|
||||
cdef shared_ptr[cpp.Value] _value
|
||||
def __init__(self, bytes val=b''):
|
||||
@@ -187,6 +242,11 @@ cdef class Value(object):
|
||||
return string(<char*>self._value.get().data.data(), self._value.get().data.size())
|
||||
def __set__(self, bytes value):
|
||||
self._value.get().data = value
|
||||
property user_type:
|
||||
def __get__(self):
|
||||
return self._value.get().user_type.decode()
|
||||
def __set__(self, str t):
|
||||
self._value.get().user_type = t.encode()
|
||||
property id:
|
||||
def __get__(self):
|
||||
return self._value.get().id
|
||||
@@ -215,7 +275,7 @@ cdef class NodeSet(object):
|
||||
def size(self):
|
||||
return self._nodes.size()
|
||||
def insert(self, NodeEntry l):
|
||||
self._nodes.insert(l._v)
|
||||
return self._nodes.insert(l._v).second
|
||||
def extend(self, li):
|
||||
for n in li:
|
||||
self.insert(n)
|
||||
@@ -240,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
|
||||
@@ -382,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
|
||||
|
||||
@@ -400,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
|
||||
@@ -424,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):
|
||||
@@ -444,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()
|
||||
@@ -461,7 +525,7 @@ cdef class DhtRunner(_WithID):
|
||||
stats.append(n)
|
||||
return stats
|
||||
|
||||
def get(self, InfoHash key, get_cb=None, done_cb=None):
|
||||
def get(self, InfoHash key, get_cb=None, done_cb=None, filter=None, Where where=None):
|
||||
"""Retreive values associated with a key on the DHT.
|
||||
|
||||
key -- the key for which to search
|
||||
@@ -471,9 +535,14 @@ cdef class DhtRunner(_WithID):
|
||||
operation is completed.
|
||||
"""
|
||||
if get_cb:
|
||||
cb_obj = {'get':get_cb, 'done':done_cb}
|
||||
cb_obj = {'get':get_cb, 'done':done_cb, 'filter':filter}
|
||||
ref.Py_INCREF(cb_obj)
|
||||
self.thisptr.get().get(key._infohash, cpp.bindGetCb(get_callback, <void*>cb_obj), cpp.bindDoneCb(done_callback, <void*>cb_obj))
|
||||
if where is None:
|
||||
where = Where()
|
||||
self.thisptr.get().get(key._infohash, cpp.bindGetCb(get_callback, <void*>cb_obj),
|
||||
cpp.bindDoneCb(done_callback, <void*>cb_obj),
|
||||
cpp.nullptr, #filter implemented in the get_callback
|
||||
where._where)
|
||||
else:
|
||||
lock = threading.Condition()
|
||||
pending = 0
|
||||
@@ -489,7 +558,7 @@ cdef class DhtRunner(_WithID):
|
||||
lock.notify()
|
||||
with lock:
|
||||
pending += 1
|
||||
self.get(key, get_cb=tmp_get, done_cb=tmp_done)
|
||||
self.get(key, get_cb=tmp_get, done_cb=tmp_done, filter=filter, where=where)
|
||||
while pending > 0:
|
||||
lock.wait()
|
||||
return res
|
||||
@@ -597,4 +666,3 @@ cdef class Pht(object):
|
||||
val,
|
||||
cpp.bindDoneCbSimple(done_callback_simple, <void*>cb_obj)
|
||||
)
|
||||
|
||||
+48
-7
@@ -16,7 +16,7 @@
|
||||
# along with this program; If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
from libc.stdint cimport *
|
||||
from libcpp cimport bool
|
||||
from libcpp cimport bool, nullptr_t, nullptr
|
||||
from libcpp.string cimport string
|
||||
from libcpp.vector cimport vector
|
||||
from libcpp.utility cimport pair
|
||||
@@ -73,7 +73,7 @@ cdef extern from "opendht/sockaddr.h" namespace "dht":
|
||||
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)
|
||||
|
||||
@@ -120,6 +120,19 @@ cdef extern from "opendht/crypto.h" namespace "dht::crypto":
|
||||
|
||||
ctypedef TrustList.VerifyResult TrustListVerifyResult
|
||||
|
||||
cdef extern from "opendht/value.h" namespace "dht::Value":
|
||||
cdef cppclass Field:
|
||||
pass
|
||||
|
||||
cdef extern from "opendht/value.h" namespace "dht::Value::Field":
|
||||
cdef Field None
|
||||
cdef Field Id
|
||||
cdef Field ValueType
|
||||
cdef Field OwnerPk
|
||||
cdef Field SeqNum
|
||||
cdef Field UserType
|
||||
cdef Field COUNT
|
||||
|
||||
cdef extern from "opendht/value.h" namespace "dht":
|
||||
cdef cppclass Value:
|
||||
Value() except +
|
||||
@@ -133,6 +146,31 @@ cdef extern from "opendht/value.h" namespace "dht":
|
||||
vector[uint8_t] data
|
||||
string user_type
|
||||
|
||||
cdef cppclass Query:
|
||||
Query() except +
|
||||
Query(Select s, Where w) except +
|
||||
Query(string q_str) except +
|
||||
bool isSatisfiedBy(const Query& q) const
|
||||
string toString() const
|
||||
|
||||
cdef cppclass Select:
|
||||
Select() except +
|
||||
Select(const string& q_str) except +
|
||||
bool isSatisfiedBy(const Select& os) const
|
||||
Select& field(Field field)
|
||||
string toString() const
|
||||
|
||||
cdef cppclass Where:
|
||||
Where() except +
|
||||
Where(const string& q_str)
|
||||
bool isSatisfiedBy(const Where& where) const
|
||||
Where& id(uint64_t id)
|
||||
Where& valueType(uint16_t type)
|
||||
Where& owner(InfoHash owner_pk_hash)
|
||||
Where& seq(uint16_t seq_no)
|
||||
Where& userType(string user_type)
|
||||
string toString() const
|
||||
|
||||
cdef extern from "opendht/node.h" namespace "dht":
|
||||
cdef cppclass Node:
|
||||
Node() except +
|
||||
@@ -160,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
|
||||
@@ -180,17 +222,16 @@ 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
|
||||
void get(InfoHash key, GetCallback get_cb, DoneCallback done_cb)
|
||||
void get(InfoHash key, GetCallback get_cb, DoneCallback done_cb, nullptr_t f, Where w)
|
||||
void put(InfoHash key, shared_ptr[Value] val, DoneCallback done_cb)
|
||||
ListenToken listen(InfoHash key, GetCallback get_cb)
|
||||
void cancelListen(InfoHash key, SharedListenToken token)
|
||||
|
||||
+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@']
|
||||
))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2015-2016 Savoir-faire Linux Inc.
|
||||
# Copyright (C) 2015-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
|
||||
@@ -17,20 +17,22 @@
|
||||
# along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os, sys, time, cmd
|
||||
import subprocess
|
||||
import signal
|
||||
import argparse
|
||||
import threading
|
||||
import logging
|
||||
import logging.handlers
|
||||
|
||||
import opendht as dht
|
||||
|
||||
logger = logging.getLogger('dhtcluster')
|
||||
|
||||
class NodeCluster(object):
|
||||
nodes = []
|
||||
node_uid = 0
|
||||
|
||||
@staticmethod
|
||||
def run_node(ip4, ip6, p, bootstrap=None, is_bootstrap=False, logfile=None):
|
||||
print("run_node", ip4, ip6, p, bootstrap, logfile)
|
||||
logger.info("Running node on port %s with bootstrap %s", p, bootstrap)
|
||||
n = dht.DhtRunner()
|
||||
n.run(ipv4=ip4 if ip4 else "", ipv6=ip6 if ip6 else "", port=p, is_bootstrap=is_bootstrap)
|
||||
if logfile:
|
||||
@@ -43,7 +45,7 @@ class NodeCluster(object):
|
||||
@staticmethod
|
||||
def find_ip(iface):
|
||||
if not iface or iface == 'any':
|
||||
return ('0.0.0.0','')
|
||||
return ('0.0.0.0','::0')
|
||||
|
||||
if_ip4 = netifaces.ifaddresses(iface)[netifaces.AF_INET][0]['addr']
|
||||
if_ip6 = netifaces.ifaddresses(iface)[netifaces.AF_INET6][0]['addr']
|
||||
@@ -60,30 +62,22 @@ class NodeCluster(object):
|
||||
if bootstrap:
|
||||
self.bootstrap = (bootstrap.hostname, str(bootstrap.port) if bootstrap.port else "4222")
|
||||
else:
|
||||
print("Using fallback bootstrap", self.ip4, self.port)
|
||||
logger.info("Using fallback bootstrap %s %s", self.ip4, self.port)
|
||||
self.bootstrap = ((self.ip4, str(self.port)))
|
||||
if first_bootstrap:
|
||||
print("Starting bootstrap node")
|
||||
logger.info("Starting bootstrap node")
|
||||
self.nodes.append(NodeCluster.run_node(self.ip4, self.ip6, self.port, self.bootstrap, is_bootstrap=True))
|
||||
self.bootstrap = ((self.ip4, str(self.port)))
|
||||
self.port += 1
|
||||
#print(self.ip4, self.ip6, self.port)
|
||||
|
||||
def front(self):
|
||||
if len(self.nodes) == 0:
|
||||
return None
|
||||
return self.nodes[0][1]
|
||||
return self.nodes[0][1] if self.nodes else None
|
||||
|
||||
def get(self, i=None):
|
||||
if not self.nodes:
|
||||
def get(self, i):
|
||||
if not self.nodes or i < 0 or i >= len(self.nodes):
|
||||
return None
|
||||
|
||||
if i is None:
|
||||
l = list(self.nodes)
|
||||
random.shuffle(l)
|
||||
return l[0][1]
|
||||
else:
|
||||
return self.nodes[i][1]
|
||||
return self.nodes[i][1]
|
||||
|
||||
def getNodeInfoById(self, id=None):
|
||||
if id:
|
||||
@@ -100,64 +94,25 @@ class NodeCluster(object):
|
||||
self.node_uid += 1
|
||||
return n
|
||||
|
||||
def end_node(self, id=None, shutdown=False, last_msg_stats=None):
|
||||
"""
|
||||
Ends a running node.
|
||||
|
||||
@param id: The 40 hex chars id of the node.
|
||||
@type id: bytes
|
||||
|
||||
@return: If a node was deleted or not.
|
||||
@rtype : boolean
|
||||
"""
|
||||
lock = threading.Condition()
|
||||
def shutdown_cb():
|
||||
nonlocal lock
|
||||
NodeCluster.Log.log('Done.')
|
||||
with lock:
|
||||
lock.notify()
|
||||
|
||||
def end_node(self):
|
||||
if not self.nodes:
|
||||
return
|
||||
elif id:
|
||||
n = self.getNodeInfoById(id)
|
||||
if n:
|
||||
if shutdown:
|
||||
with lock:
|
||||
print('Waiting for node to shutdown... ')
|
||||
n[1].shutdown(shutdown_cb)
|
||||
lock.wait()
|
||||
if last_msg_stats:
|
||||
last_msg_stats.append(self.getMessageStats())
|
||||
n[1].join()
|
||||
self.nodes.remove(n)
|
||||
print(id, 'deleted !')
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
n = self.nodes.pop()
|
||||
n[1].join()
|
||||
return True
|
||||
|
||||
def replace_node(self, id=None, shutdown=False, last_msg_stats=None):
|
||||
random.shuffle(self.nodes)
|
||||
deleted = self.end_node(id=id, shutdown=shutdown, last_msg_stats=last_msg_stats)
|
||||
if deleted:
|
||||
self.launch_node()
|
||||
|
||||
def resize(self, n):
|
||||
n = min(n, 500)
|
||||
l = len(self.nodes)
|
||||
if n == l:
|
||||
return
|
||||
if n > l:
|
||||
print("Launching", n-l, "nodes", self.ip4, self.ip6)
|
||||
logger.info("Launching %d nodes bound on IPv4 %s IPv6 %s", n-l, self.ip4, self.ip6)
|
||||
for i in range(l, n):
|
||||
self.launch_node()
|
||||
else:
|
||||
print("Ending", l-n, "nodes", self.ip4, self.ip6)
|
||||
#random.shuffle(self.nodes)
|
||||
logger.info("Ending %d nodes", l-n)
|
||||
for i in range(n, l):
|
||||
self.end_node()
|
||||
|
||||
@@ -198,13 +153,22 @@ class ClusterShell(cmd.Cmd):
|
||||
return True
|
||||
def do_node(self, arg):
|
||||
if not arg:
|
||||
setNode()
|
||||
self.setNode()
|
||||
else:
|
||||
nodenum = int(arg)
|
||||
if nodenum > len(self.net.nodes) or nodenum < 1:
|
||||
node = self.net.get(nodenum-1)
|
||||
if not node:
|
||||
print("Invalid node number:", nodenum, " (accepted: 1-", len(self.net.nodes), ")")
|
||||
else:
|
||||
self.setNode(self.net.nodes[nodenum-1][1], nodenum)
|
||||
self.setNode(node, nodenum)
|
||||
def do_resize(self, arg):
|
||||
if not arg:
|
||||
return
|
||||
try:
|
||||
nodenum = int(arg)
|
||||
self.net.resize(nodenum)
|
||||
except Exception as e:
|
||||
print("Can't resize:", e)
|
||||
def do_ll(self, arg):
|
||||
if self.node:
|
||||
print('Node', self.node.getNodeId().decode())
|
||||
@@ -234,32 +198,73 @@ if __name__ == '__main__':
|
||||
import argparse
|
||||
from urllib.parse import urlparse
|
||||
net = None
|
||||
run = True
|
||||
def clean_quit():
|
||||
global net, run
|
||||
if run:
|
||||
run = False
|
||||
if net:
|
||||
net.resize(0)
|
||||
net = None
|
||||
def quit_signal(signum, frame):
|
||||
clean_quit()
|
||||
|
||||
try:
|
||||
parser = argparse.ArgumentParser(description='Create a dht network of -n nodes')
|
||||
parser.add_argument('-n', '--node-num', help='number of dht nodes to run', type=int, default=32)
|
||||
parser.add_argument('-I', '--iface', help='local interface to bind', default='any')
|
||||
parser.add_argument('-p', '--port', help='start of port range (port, port+node_num)', type=int, default=4000)
|
||||
parser.add_argument('-b', '--bootstrap', help='bootstrap address')
|
||||
parser.add_argument('-d', '--daemonize', help='daemonize process', action='store_true')
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument('-d', '--daemonize', help='daemonize process', action='store_true')
|
||||
group.add_argument('-s', '--service', help='service mode (not forking)', action='store_true')
|
||||
parser.add_argument('-l', '--log', help='log file prefix')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.bootstrap:
|
||||
args.bootstrap = urlparse('dht://'+args.bootstrap)
|
||||
|
||||
net = NodeCluster(iface=args.iface, port=args.port, bootstrap=args.bootstrap, logfile=args.log)
|
||||
net.resize(args.node_num)
|
||||
if args.daemonize:
|
||||
import daemon, lockfile
|
||||
#pidfile=lockfile.FileLock('/var/run/dhtcluster.'+str(args.port))
|
||||
with daemon.DaemonContext():
|
||||
while True:
|
||||
time.sleep(2)
|
||||
# setup logging
|
||||
if args.daemonize or args.service:
|
||||
syslog = logging.handlers.SysLogHandler(address = '/dev/log')
|
||||
syslog.setLevel(logging.DEBUG)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
logger.addHandler(syslog)
|
||||
else:
|
||||
logging.basicConfig(stream=sys.stdout,level=logging.DEBUG)
|
||||
|
||||
# start cluster
|
||||
net = NodeCluster(iface=args.iface, port=args.port, bootstrap=args.bootstrap, logfile=args.log)
|
||||
|
||||
# main loop
|
||||
if args.daemonize:
|
||||
import daemon
|
||||
context = daemon.DaemonContext()
|
||||
context.signal_map = {
|
||||
signal.SIGHUP: 'terminate',
|
||||
signal.SIGTERM: quit_signal,
|
||||
signal.SIGINT: quit_signal,
|
||||
signal.SIGQUIT: quit_signal
|
||||
}
|
||||
with context:
|
||||
net.resize(args.node_num)
|
||||
while net:
|
||||
time.sleep(1)
|
||||
elif args.service:
|
||||
signal.signal(signal.SIGTERM, quit_signal)
|
||||
signal.signal(signal.SIGINT, quit_signal)
|
||||
signal.signal(signal.SIGQUIT, quit_signal)
|
||||
net.resize(args.node_num)
|
||||
while net:
|
||||
time.sleep(1)
|
||||
else:
|
||||
net.resize(args.node_num)
|
||||
ClusterShell(net).cmdloop()
|
||||
except Exception as e:
|
||||
traceback.print_tb(e.__traceback__)
|
||||
print(type(e).__name__+':', e, file=sys.stderr)
|
||||
except Exception:
|
||||
logger.error("Exception ", exc_info=1)
|
||||
finally:
|
||||
if net:
|
||||
net.resize(0)
|
||||
clean_quit()
|
||||
logger.warning("Ending node cluster")
|
||||
for handler in logger.handlers:
|
||||
handler.close()
|
||||
logger.removeHandler(handler)
|
||||
@@ -32,23 +32,35 @@ class DhtServer(resource.Resource):
|
||||
self.node.bootstrap(b_url.hostname, str(b_url.port) if b_url.port else '4222')
|
||||
|
||||
def render_GET(self, req):
|
||||
uri = req.uri[1:]
|
||||
h = dht.InfoHash(uri) if len(uri) == 40 else dht.InfoHash.get(uri.decode())
|
||||
print('GET', '"'+uri.decode()+'"', h)
|
||||
res = self.node.get(h)
|
||||
uri = req.uri[1:].decode().rsplit('?', 1)[0]
|
||||
h = dht.InfoHash(uri.encode()) if len(uri) == 40 else dht.InfoHash.get(uri)
|
||||
w = dht.Where('WHERE '+''.join(k.decode()+'='+req.args[k][0].decode()+','
|
||||
for k in req.args.keys()
|
||||
if k in [b'id', b'user_type', b'value_type', b'owner', b'seq'])[:-1])
|
||||
print('GET', '"'+uri+'"', h, w)
|
||||
res = self.node.get(h, where=w)
|
||||
req.setHeader(b"content-type", b"application/json")
|
||||
return json.dumps({'{:x}'.format(v.id):{'base64':base64.b64encode(v.data).decode()} for v in res}).encode()
|
||||
|
||||
def render_POST(self, req):
|
||||
uri = req.uri[1:]
|
||||
data = req.args[b'data'][0] if b'data' in req.args else None
|
||||
user_type = req.args[b'user_type'][0].decode() if b'user_type' in req.args else ""
|
||||
try:
|
||||
vid = int(req.args[b'id'][0].decode()) if b'id' in req.args else 0
|
||||
except ValueError:
|
||||
vid = 0
|
||||
if not data and b'base64' in req.args:
|
||||
data = base64.b64decode(req.args[b'base64'][0])
|
||||
h = dht.InfoHash(uri) if len(uri) == 40 else dht.InfoHash.get(uri.decode())
|
||||
print('POST', h, data)
|
||||
req.setHeader(b"content-type", b"application/json")
|
||||
if data:
|
||||
self.node.put(h, dht.Value(data))
|
||||
v = dht.Value(data)
|
||||
if vid != 0:
|
||||
v.id = vid
|
||||
v.user_type = user_type
|
||||
self.node.put(h, v)
|
||||
return json.dumps({'success':True}).encode()
|
||||
else:
|
||||
req.setResponseCode(400)
|
||||
|
||||
+127
-91
@@ -20,7 +20,7 @@ from pprint import pprint
|
||||
from math import cos, sin, pi
|
||||
import urllib3
|
||||
import gzip
|
||||
import asyncio
|
||||
import queue
|
||||
|
||||
sys.path.append('..')
|
||||
from opendht import *
|
||||
@@ -37,14 +37,25 @@ import GeoIP
|
||||
|
||||
http = urllib3.PoolManager()
|
||||
|
||||
run = True
|
||||
done = 0
|
||||
all_nodes = NodeSet()
|
||||
nodes_ip4s = {}
|
||||
nodes_ip6s = {}
|
||||
lats = []
|
||||
lons = []
|
||||
cities=[]
|
||||
xys = []
|
||||
colors = []
|
||||
all_lines = []
|
||||
points = []
|
||||
not_found = []
|
||||
|
||||
plt.ion()
|
||||
plt.figaspect(2.)
|
||||
|
||||
fig, axes = plt.subplots(2, 1)
|
||||
fig.set_size_inches(12,16,forward=True)
|
||||
fig.set_size_inches(8,16,forward=True)
|
||||
fig.tight_layout()
|
||||
fig.canvas.set_window_title('OpenDHT scanner')
|
||||
|
||||
@@ -70,6 +81,15 @@ exitbtn = Button(exitax, 'Exit')
|
||||
reloadax = plt.axes([0.92, 0.90, 0.07, 0.04])
|
||||
button = Button(reloadax, 'Reload')
|
||||
|
||||
collection = None
|
||||
infos = [ringx.text(1.2, -0.8, ""),
|
||||
ringx.text(1.2, -0.9, "")]
|
||||
|
||||
def exitcb(arg):
|
||||
global run
|
||||
run = False
|
||||
exitbtn.on_clicked(exitcb)
|
||||
|
||||
def check_dl(fname, url):
|
||||
if os.path.isfile(fname):
|
||||
return
|
||||
@@ -92,35 +112,35 @@ r = DhtRunner()
|
||||
r.run(port=4112)
|
||||
r.bootstrap("bootstrap.ring.cx", "4222")
|
||||
|
||||
all_lines = []
|
||||
plt.pause(1)
|
||||
|
||||
plt.pause(2)
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
q = queue.Queue()
|
||||
|
||||
def step(cur_h, cur_depth):
|
||||
global done, all_nodes, all_lines
|
||||
global done
|
||||
done += 1
|
||||
a = 2.*pi*cur_h.toFloat()
|
||||
b = a + 2.*pi/(2**(cur_depth))
|
||||
arc = []
|
||||
lines = []
|
||||
q.put((stepUi, (cur_h, cur_depth, arc, lines)))
|
||||
print("step", cur_h, cur_depth)
|
||||
arc = ringx.add_patch(mpatches.Wedge([0.,0,], 1., a*180/pi, b*180/pi, fill=True, color="blue", alpha=0.5))
|
||||
lines = ringx.plot([0, cos(a)], [0, sin(a)], 'k-', lw=1.2)
|
||||
all_lines.extend(lines)
|
||||
r.get(cur_h, gcb, lambda d, nodes: loop.call_soon_threadsafe(nextstep, cur_h, cur_depth, d, nodes, arc, lines))
|
||||
r.get(cur_h, gcb, lambda d, nodes: stepdone(cur_h, cur_depth, d, nodes, arc, lines))
|
||||
|
||||
def nextstep(cur_h, cur_depth, ok, nodes, arc=None, lines=[]):
|
||||
global done, all_nodes
|
||||
if arc:
|
||||
arc.remove()
|
||||
del arc
|
||||
def stepdone(cur_h, cur_depth, d, nodes, arc, lines):
|
||||
#print("stepdone", cur_h, cur_depth)
|
||||
q.put((nextstepUi, (nodes, arc, lines)))
|
||||
nextstep(cur_h, cur_depth, d, nodes)
|
||||
|
||||
def nextstep(cur_h, cur_depth, ok, nodes):
|
||||
global done
|
||||
if nodes:
|
||||
for l in lines:
|
||||
l.set_color('#444444')
|
||||
snodes = NodeSet()
|
||||
snodes.extend(nodes)
|
||||
all_nodes.extend(nodes)
|
||||
depth = min(8, InfoHash.commonBits(snodes.first(), snodes.last())+6)
|
||||
commonBits = 0
|
||||
if len(nodes) > 1:
|
||||
snodes = NodeSet()
|
||||
snodes.extend(nodes)
|
||||
commonBits = InfoHash.commonBits(snodes.first(), snodes.last())
|
||||
depth = min(8, commonBits+6)
|
||||
if cur_depth < depth:
|
||||
for b in range(cur_depth, depth):
|
||||
new_h = InfoHash(cur_h.toString());
|
||||
@@ -130,14 +150,62 @@ def nextstep(cur_h, cur_depth, ok, nodes, arc=None, lines=[]):
|
||||
print("step done with no nodes", ok, cur_h.toString().decode())
|
||||
done -= 1
|
||||
|
||||
run = True
|
||||
def exitcb(arg):
|
||||
global run
|
||||
run = False
|
||||
exitbtn.on_clicked(exitcb)
|
||||
def stepUi(cur_h, cur_depth, arc, lines):
|
||||
global all_lines
|
||||
a = 2.*pi*cur_h.toFloat()
|
||||
b = a + 2.*pi/(2**(cur_depth))
|
||||
arc.append(ringx.add_patch(mpatches.Wedge([0.,0,], 1., a*180/pi, b*180/pi, fill=True, color="blue", alpha=0.5)))
|
||||
lines.extend(ringx.plot([0, cos(a)], [0, sin(a)], 'k-', lw=1.2))
|
||||
all_lines.extend(lines)
|
||||
|
||||
def nextstepUi(nodes, arc=None, lines=[]):
|
||||
for a in arc:
|
||||
if a:
|
||||
a.remove()
|
||||
del a
|
||||
for l in lines:
|
||||
l.set_color('#aaaaaa')
|
||||
if nodes:
|
||||
appendNodes(nodes)
|
||||
|
||||
def appendNodes(nodes):
|
||||
global all_nodes
|
||||
for n in nodes:
|
||||
if all_nodes.insert(n):
|
||||
appendNewNode(n)
|
||||
|
||||
def appendNewNode(n):
|
||||
global nodes_ip4s, nodes_ip6s, colors, xys
|
||||
addr = b':'.join(n.getNode().getAddr().split(b':')[0:-1]).decode()
|
||||
colors.append('red' if n.getNode().isExpired() else 'blue')
|
||||
node_val = n.getId().toFloat()
|
||||
xys.append((cos(node_val*2*pi), sin(node_val*2*pi)))
|
||||
georecord = None
|
||||
if addr[0] == '[':
|
||||
addr = addr[1:-1]
|
||||
if addr in nodes_ip6s:
|
||||
nodes_ip6s[addr][1] += 1
|
||||
else:
|
||||
georecord = gi6.record_by_name_v6(addr)
|
||||
nodes_ip6s[addr] = [n, 1, georecord]
|
||||
else:
|
||||
if addr in nodes_ip4s:
|
||||
nodes_ip4s[addr][1] += 1
|
||||
else:
|
||||
georecord = gi.record_by_name(addr)
|
||||
nodes_ip4s[addr] = [n, 1, georecord]
|
||||
if georecord:
|
||||
appendMapPoint(georecord)
|
||||
|
||||
def appendMapPoint(res):
|
||||
global lons, lats, cities
|
||||
lons.append(res['longitude'])
|
||||
lats.append(res['latitude'])
|
||||
cities.append(res['city'] if res['city'] else (str(int(res['latitude']))+'-'+str(int(res['longitude']))))
|
||||
|
||||
|
||||
def restart(arg):
|
||||
global collection, all_lines, all_nodes, points, done
|
||||
global collection, all_lines, all_nodes, points, done, nodes_ip4s, nodes_ip6s, lats, lons, cities, xys, colors
|
||||
if done:
|
||||
return
|
||||
for l in all_lines:
|
||||
@@ -145,6 +213,13 @@ def restart(arg):
|
||||
del l
|
||||
all_lines = []
|
||||
all_nodes = NodeSet()
|
||||
nodes_ip4s = {}
|
||||
nodes_ip6s = {}
|
||||
lats = []
|
||||
lons = []
|
||||
cities=[]
|
||||
xys = []
|
||||
colors = []
|
||||
if collection:
|
||||
collection.remove()
|
||||
del collection
|
||||
@@ -160,67 +235,20 @@ def restart(arg):
|
||||
step(start_h, 0)
|
||||
plt.draw()
|
||||
|
||||
collection = None
|
||||
points = []
|
||||
not_found = []
|
||||
infos = [ringx.text(1.2, -0.8, ""),
|
||||
ringx.text(1.2, -0.9, "")]
|
||||
|
||||
def num_nodes(node_set):
|
||||
return sorted([x for x in node_set.items()], key=lambda ip: ip[1][1])
|
||||
|
||||
def generate_set():
|
||||
node_ipv4 = {}
|
||||
node_ipv6 = {}
|
||||
for n in all_nodes:
|
||||
addr = b''.join(n.getNode().getAddr().split(b':')[0:-1]).decode()
|
||||
if addr[0] == '[':
|
||||
addr = addr[1:-1]
|
||||
if addr in node_ipv6:
|
||||
node_ipv6[addr][1] += 1
|
||||
else:
|
||||
node_ipv6[addr] = [n, 1, gi6.record_by_name_v6(addr)]
|
||||
else:
|
||||
if addr in node_ipv4:
|
||||
node_ipv4[addr][1] += 1
|
||||
else:
|
||||
node_ipv4[addr] = [n, 1, gi.record_by_name(addr)]
|
||||
return num_nodes(node_ipv4), num_nodes(node_ipv6)
|
||||
|
||||
def update_plot():
|
||||
global done, m, collection, not_found, points
|
||||
#print("update_plot", done)
|
||||
global m, collection, points
|
||||
for p in points:
|
||||
p.remove()
|
||||
del p
|
||||
points = []
|
||||
lats = []
|
||||
lons = []
|
||||
cities=[]
|
||||
colors=[]
|
||||
not_found.clear()
|
||||
ip4s, ip6s = generate_set()
|
||||
ares = []
|
||||
for addr, n in ip4s:
|
||||
ares.append((addr, n[0].getNode(), n[2]))
|
||||
for addr, n in ip6s:
|
||||
ares.append((addr, n[0].getNode(), n[2]))
|
||||
for r in ares:
|
||||
res = r[2]
|
||||
n = r[1]
|
||||
if res:
|
||||
lats.append(res['latitude'])
|
||||
lons.append(res['longitude'])
|
||||
cities.append(res['city'] if res['city'] else (str(int(res['latitude']))+'-'+str(int(res['longitude']))))
|
||||
colors.append('red' if n.isExpired() else 'blue')
|
||||
else:
|
||||
not_found.append(r[0])
|
||||
|
||||
x,y = m(lons,lats)
|
||||
points.extend(m.plot(x,y,'bo'))
|
||||
for name, xpt, ypt in zip(cities, x, y):
|
||||
points.append(mpx.text(xpt+50000, ypt+50000, name))
|
||||
node_val = [n.getId().toFloat() for n in all_nodes]
|
||||
xys = [(cos(d*2*pi), sin(d*2*pi)) for d in node_val]
|
||||
if collection:
|
||||
collection.remove()
|
||||
del collection
|
||||
@@ -229,9 +257,21 @@ def update_plot():
|
||||
int(fig.dpi), 6, sizes=(10,), facecolors=colors,
|
||||
offsets = xys, transOffset = ringx.transData))
|
||||
|
||||
node_ip4s, node_ip6s = generate_set()
|
||||
infos[0].set_text("{} different IPv4s".format(len(node_ip4s)))
|
||||
infos[1].set_text("{} different IPv6s".format(len(node_ip6s)))
|
||||
infos[0].set_text("{} different IPv4s".format(len(nodes_ip4s)))
|
||||
infos[1].set_text("{} different IPv6s".format(len(nodes_ip6s)))
|
||||
|
||||
def d(arg):
|
||||
pass
|
||||
|
||||
def handle_tasks():
|
||||
while True:
|
||||
try:
|
||||
f = q.get_nowait()
|
||||
f[0](*f[1])
|
||||
q.task_done()
|
||||
except Exception as e:
|
||||
break;
|
||||
update_plot()
|
||||
|
||||
if run:
|
||||
# start first step
|
||||
@@ -239,15 +279,9 @@ if run:
|
||||
start_h.setBit(159, 1)
|
||||
step(start_h, 0)
|
||||
|
||||
def d(arg):
|
||||
pass
|
||||
|
||||
while run:
|
||||
while run and done > 0:
|
||||
loop.stop()
|
||||
loop.run_forever()
|
||||
update_plot()
|
||||
plt.draw()
|
||||
handle_tasks()
|
||||
plt.pause(.5)
|
||||
|
||||
if not run:
|
||||
@@ -255,13 +289,14 @@ while run:
|
||||
|
||||
button.on_clicked(restart)
|
||||
|
||||
node_ip4s, node_ip6s = generate_set()
|
||||
node_ip4s = num_nodes(nodes_ip4s)
|
||||
node_ip6s = num_nodes(nodes_ip6s)
|
||||
|
||||
print(all_nodes.size(), " nodes found")
|
||||
print(all_nodes)
|
||||
print(len(not_found), " nodes not geolocalized")
|
||||
for n in not_found:
|
||||
print(n)
|
||||
#print(len(not_found), " nodes not geolocalized")
|
||||
#for n in not_found:
|
||||
# print(n)
|
||||
print('')
|
||||
print(len(node_ip4s), " different IPv4s :")
|
||||
for ip in node_ip4s:
|
||||
@@ -271,6 +306,7 @@ while run:
|
||||
for ip in node_ip6s:
|
||||
print(ip[0], ":", str(ip[1][1]), "nodes", ("(" + ip[1][2]['city'] + ")") if ip[1][2] and ip[1][2]['city'] else "")
|
||||
|
||||
handle_tasks()
|
||||
while run and done == 0:
|
||||
plt.pause(.5)
|
||||
button.on_clicked(d)
|
||||
|
||||
+5
-4
@@ -5,6 +5,11 @@ libopendht_la_LIBADD = @Argon2_LIBS@ @GnuTLS_LIBS@ @Nettle_LIBS@
|
||||
libopendht_la_LDFLAGS = @LDFLAGS@ @Argon2_LDFLAGS@
|
||||
libopendht_la_SOURCES = \
|
||||
dht.cpp \
|
||||
storage.h \
|
||||
listener.h \
|
||||
request.h \
|
||||
search.h \
|
||||
parsed_message.h \
|
||||
node_cache.cpp \
|
||||
callbacks.cpp \
|
||||
routing_table.cpp \
|
||||
@@ -49,9 +54,6 @@ nobase_include_HEADERS = \
|
||||
../include/opendht/rng.h \
|
||||
../include/opendht/indexation/pht.h
|
||||
|
||||
nobase_noinst_HEADERS = \
|
||||
../include/opendht/request.h
|
||||
|
||||
clean-local:
|
||||
rm -rf libargon2.la
|
||||
|
||||
@@ -81,4 +83,3 @@ noinst_HEADERS = \
|
||||
@top_builddir@/argon2/src/encoding.h \
|
||||
@top_builddir@/argon2/src/thread.h
|
||||
endif
|
||||
|
||||
+26
-14
@@ -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);
|
||||
@@ -506,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;
|
||||
@@ -529,7 +525,8 @@ PublicKey::getId() const
|
||||
return {};
|
||||
InfoHash id;
|
||||
size_t sz = id.size();
|
||||
if (auto err = gnutls_pubkey_get_key_id(pk, 0, id.data(), &sz))
|
||||
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())
|
||||
throw CryptoException("Can't get public key ID: wrong output length.");
|
||||
@@ -882,6 +879,19 @@ generateIdentity(const std::string& name, Identity ca, unsigned key_length) {
|
||||
return generateIdentity(name, ca, key_length, !ca.first || !ca.second);
|
||||
}
|
||||
|
||||
Identity
|
||||
generateEcIdentity(const std::string& name, crypto::Identity ca, bool is_ca)
|
||||
{
|
||||
auto key = std::make_shared<PrivateKey>(PrivateKey::generateEC());
|
||||
auto cert = std::make_shared<Certificate>(Certificate::generate(*key, name, ca, is_ca));
|
||||
return {std::move(key), std::move(cert)};
|
||||
}
|
||||
|
||||
Identity
|
||||
generateEcIdentity(const std::string& name, Identity ca) {
|
||||
return generateEcIdentity(name, ca, !ca.first || !ca.second);
|
||||
}
|
||||
|
||||
Certificate
|
||||
Certificate::generate(const PrivateKey& key, const std::string& name, Identity ca, bool is_ca)
|
||||
{
|
||||
@@ -916,16 +926,18 @@ Certificate::generate(const PrivateKey& key, const std::string& name, Identity c
|
||||
gnutls_x509_crt_set_serial(cert, &cert_serial, sizeof(cert_serial));
|
||||
}
|
||||
|
||||
unsigned key_usage = GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_DATA_ENCIPHERMENT;
|
||||
unsigned key_usage = 0;
|
||||
if (is_ca) {
|
||||
gnutls_x509_crt_set_ca_status(cert, 1);
|
||||
key_usage |= GNUTLS_KEY_KEY_CERT_SIGN | GNUTLS_KEY_CRL_SIGN;
|
||||
} else {
|
||||
key_usage |= GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_DATA_ENCIPHERMENT;
|
||||
}
|
||||
gnutls_x509_crt_set_key_usage(cert, key_usage);
|
||||
|
||||
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) {
|
||||
|
||||
+10
-11
@@ -27,14 +27,14 @@ std::ostream& operator<< (std::ostream& s, const DhtMessage& v)
|
||||
}
|
||||
|
||||
bool
|
||||
DhtMessage::storePolicy(InfoHash h, std::shared_ptr<Value>& v, InfoHash f, const sockaddr* fa, socklen_t fas)
|
||||
DhtMessage::storePolicy(InfoHash h, std::shared_ptr<Value>& v, const InfoHash& f, const SockAddr& sa)
|
||||
{
|
||||
try {
|
||||
auto msg = unpackMsg<DhtMessage>(v->data);
|
||||
if (msg.service.empty())
|
||||
return false;
|
||||
} catch (const std::exception& e) {}
|
||||
return ValueType::DEFAULT_STORE_POLICY(h, v, f, fa, fas);
|
||||
return ValueType::DEFAULT_STORE_POLICY(h, v, f, sa);
|
||||
}
|
||||
|
||||
Value::Filter
|
||||
@@ -54,12 +54,11 @@ DhtMessage::ServiceFilter(std::string s)
|
||||
|
||||
std::ostream& operator<< (std::ostream& s, const IpServiceAnnouncement& v)
|
||||
{
|
||||
s << "Peer: ";
|
||||
s << "port " << v.getPort();
|
||||
|
||||
if (v.ss.ss_family == AF_INET || v.ss.ss_family == AF_INET6) {
|
||||
if (v.addr) {
|
||||
s << "Peer: ";
|
||||
s << "port " << v.getPort();
|
||||
char hbuf[NI_MAXHOST];
|
||||
if (getnameinfo((sockaddr*)&v.ss, sizeof(v.ss), hbuf, sizeof(hbuf), nullptr, 0, NI_NUMERICHOST) == 0) {
|
||||
if (getnameinfo(v.addr.get(), v.addr.getLength(), hbuf, sizeof(hbuf), nullptr, 0, NI_NUMERICHOST) == 0) {
|
||||
s << " addr " << std::string(hbuf, strlen(hbuf));
|
||||
}
|
||||
}
|
||||
@@ -67,17 +66,17 @@ std::ostream& operator<< (std::ostream& s, const IpServiceAnnouncement& v)
|
||||
}
|
||||
|
||||
bool
|
||||
IpServiceAnnouncement::storePolicy(InfoHash h, std::shared_ptr<Value>& v, InfoHash f, const sockaddr* from, socklen_t fromlen)
|
||||
IpServiceAnnouncement::storePolicy(InfoHash h, std::shared_ptr<Value>& v, const InfoHash& f, const SockAddr& sa)
|
||||
{
|
||||
try {
|
||||
auto msg = unpackMsg<IpServiceAnnouncement>(v->data);
|
||||
if (msg.getPort() == 0)
|
||||
return false;
|
||||
IpServiceAnnouncement sa_addr {from, fromlen};
|
||||
IpServiceAnnouncement sa_addr {sa};
|
||||
sa_addr.setPort(msg.getPort());
|
||||
// argument v is modified (not the value).
|
||||
v = std::make_shared<Value>(IpServiceAnnouncement::TYPE, sa_addr, v->id);
|
||||
return ValueType::DEFAULT_STORE_POLICY(h, v, f, from, fromlen);
|
||||
return ValueType::DEFAULT_STORE_POLICY(h, v, f, sa);
|
||||
} catch (const std::exception& e) {}
|
||||
return false;
|
||||
}
|
||||
@@ -86,7 +85,7 @@ const ValueType DhtMessage::TYPE(1, "DHT message", std::chrono::minutes(5), DhtM
|
||||
const ValueType IpServiceAnnouncement::TYPE(2, "Internet Service Announcement", std::chrono::minutes(15), IpServiceAnnouncement::storePolicy);
|
||||
const ValueType ImMessage::TYPE = {3, "IM message", std::chrono::minutes(5)};
|
||||
const ValueType TrustRequest::TYPE = {4, "Certificate trust request", std::chrono::hours(24*7)};
|
||||
const ValueType IceCandidates::TYPE = {5, "ICE candidates", std::chrono::minutes(5)};
|
||||
const ValueType IceCandidates::TYPE = {5, "ICE candidates", std::chrono::minutes(1)};
|
||||
|
||||
const std::array<std::reference_wrapper<const ValueType>, 5>
|
||||
DEFAULT_TYPES
|
||||
|
||||
+314
-1522
File diff suppressed because it is too large.
Load diff
+90
-236
@@ -20,98 +20,32 @@
|
||||
#include "dhtrunner.h"
|
||||
#include "securedht.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/socket.h>
|
||||
#else
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#define close(x) closesocket(x)
|
||||
#endif
|
||||
|
||||
namespace dht {
|
||||
|
||||
constexpr std::chrono::seconds DhtRunner::BOOTSTRAP_PERIOD;
|
||||
|
||||
DhtRunner::DhtRunner() : dht_()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSADATA wsd;
|
||||
if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
|
||||
throw DhtException("Can't initialize Winsock2");
|
||||
#endif
|
||||
uv_async_.data = this;
|
||||
uv_async_init(uv_loop_.get(), &uv_async_, &DhtRunner::loop_callback);
|
||||
}
|
||||
|
||||
DhtRunner::~DhtRunner()
|
||||
{
|
||||
join();
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::run(in_port_t port, DhtRunner::Config config)
|
||||
{
|
||||
sockaddr_in sin4;
|
||||
std::fill_n((uint8_t*)&sin4, sizeof(sin4), 0);
|
||||
sin4.sin_family = AF_INET;
|
||||
sin4.sin_port = htons(port);
|
||||
sockaddr_in6 sin6;
|
||||
std::fill_n((uint8_t*)&sin6, sizeof(sin6), 0);
|
||||
sin6.sin6_family = AF_INET6;
|
||||
sin6.sin6_port = htons(port);
|
||||
run(&sin4, &sin6, config);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::run(const char* ip4, const char* ip6, const char* service, DhtRunner::Config config)
|
||||
{
|
||||
auto res4 = getAddrInfo(ip4, service);
|
||||
auto res6 = getAddrInfo(ip6, service);
|
||||
run(res4.empty() ? nullptr : (sockaddr_in*) &res4.front().first,
|
||||
res6.empty() ? nullptr : (sockaddr_in6*)&res6.front().first, config);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::run(const sockaddr_in* local4, const sockaddr_in6* local6, DhtRunner::Config config)
|
||||
DhtRunner::run(DhtRunner::Config config)
|
||||
{
|
||||
if (running)
|
||||
return;
|
||||
if (rcv_thread.joinable())
|
||||
rcv_thread.join();
|
||||
running = true;
|
||||
doRun(local4, local6, config.dht_config);
|
||||
doRun(config.dht_config);
|
||||
if (not config.threaded)
|
||||
return;
|
||||
dht_thread = std::thread([this]() {
|
||||
while (running) {
|
||||
std::unique_lock<std::mutex> lk(dht_mtx);
|
||||
auto wakeup = loop_();
|
||||
cv.wait_until(lk, wakeup, [this]() {
|
||||
if (not running)
|
||||
return true;
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(sock_mtx);
|
||||
if (not rcv.empty())
|
||||
return true;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
if (not pending_ops_prio.empty())
|
||||
return true;
|
||||
auto s = getStatus();
|
||||
if (not pending_ops.empty() and (s == NodeStatus::Connected or (s == NodeStatus::Disconnected and not bootstraping)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
uv_loop_.run();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -121,21 +55,19 @@ DhtRunner::shutdown(ShutdownCallback cb) {
|
||||
pending_ops_prio.emplace([=](SecureDht& dht) mutable {
|
||||
dht.shutdown(cb);
|
||||
});
|
||||
cv.notify_all();
|
||||
uv_async_send(&uv_async_);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::join()
|
||||
{
|
||||
running = false;
|
||||
cv.notify_all();
|
||||
bootstrap_cv.notify_all();
|
||||
uv_async_send(&uv_async_);
|
||||
//bootstrap_cv.notify_all();
|
||||
if (dht_thread.joinable())
|
||||
dht_thread.join();
|
||||
if (rcv_thread.joinable())
|
||||
rcv_thread.join();
|
||||
if (bootstrap_thread.joinable())
|
||||
bootstrap_thread.join();
|
||||
/*if (bootstrap_thread.joinable())
|
||||
bootstrap_thread.join();*/
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
@@ -145,10 +77,10 @@ DhtRunner::join()
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
dht_.reset();
|
||||
status4 = NodeStatus::Disconnected;
|
||||
/*status4 = NodeStatus::Disconnected;
|
||||
status6 = NodeStatus::Disconnected;
|
||||
bound4 = {};
|
||||
bound6 = {};
|
||||
bound6 = {};*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,6 +107,14 @@ DhtRunner::getNodeId() const
|
||||
return dht_->getNodeId();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::setOnStatusChanged(StatusCallback&& cb)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
if (not dht_)
|
||||
throw std::runtime_error("dht is not running");
|
||||
dht_->setOnStatusChanged(std::move(cb));
|
||||
}
|
||||
|
||||
std::pair<size_t, size_t>
|
||||
DhtRunner::getStoreSize() const {
|
||||
@@ -303,10 +243,15 @@ DhtRunner::getPublicAddressStr(sa_family_t af)
|
||||
{
|
||||
auto addrs = getPublicAddress(af);
|
||||
std::vector<std::string> ret(addrs.size());
|
||||
std::transform(addrs.begin(), addrs.end(), ret.begin(), dht::printAddr);
|
||||
std::transform(addrs.begin(), addrs.end(), ret.begin(), [](const SockAddr& a) { return a.toString(); });
|
||||
return ret;
|
||||
}
|
||||
|
||||
SockAddr
|
||||
DhtRunner::getBound() const {
|
||||
return dht_->getBoundAddr();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::registerCertificate(std::shared_ptr<crypto::Certificate> cert) {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
@@ -318,42 +263,12 @@ DhtRunner::setLocalCertificateStore(CertificateStoreQuery&& query_method) {
|
||||
dht_->setLocalCertificateStore(std::forward<CertificateStoreQuery>(query_method));
|
||||
}
|
||||
|
||||
time_point
|
||||
void
|
||||
DhtRunner::loop_()
|
||||
{
|
||||
if (!dht_)
|
||||
return {};
|
||||
|
||||
decltype(pending_ops) ops {};
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
auto s = getStatus();
|
||||
ops = (pending_ops_prio.empty() && (s == NodeStatus::Connected or (s == NodeStatus::Disconnected and not bootstraping))) ?
|
||||
std::move(pending_ops) : std::move(pending_ops_prio);
|
||||
}
|
||||
while (not ops.empty()) {
|
||||
ops.front()(*dht_);
|
||||
ops.pop();
|
||||
}
|
||||
|
||||
time_point wakeup {};
|
||||
decltype(rcv) received {};
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(sock_mtx);
|
||||
// move to stack
|
||||
received = std::move(rcv);
|
||||
}
|
||||
if (not received.empty()) {
|
||||
for (const auto& pck : received) {
|
||||
auto& buf = pck.first;
|
||||
auto& from = pck.second;
|
||||
wakeup = dht_->periodic(buf.data(), buf.size()-1, from);
|
||||
}
|
||||
received.clear();
|
||||
} else {
|
||||
wakeup = dht_->periodic(nullptr, 0, nullptr, 0);
|
||||
}
|
||||
|
||||
return;
|
||||
/*
|
||||
NodeStatus nstatus4 = dht_->getStatus(AF_INET);
|
||||
NodeStatus nstatus6 = dht_->getStatus(AF_INET6);
|
||||
if (nstatus4 != status4 || nstatus6 != status6) {
|
||||
@@ -371,102 +286,24 @@ DhtRunner::loop_()
|
||||
if (statusCb)
|
||||
statusCb(status4, status6);
|
||||
}
|
||||
|
||||
return wakeup;
|
||||
*/
|
||||
decltype(pending_ops) ops {};
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
auto s = dht_->getStatus();
|
||||
ops = (pending_ops_prio.empty() && (s == NodeStatus::Connected or (s == NodeStatus::Disconnected and not bootstraping))) ?
|
||||
std::move(pending_ops) : std::move(pending_ops_prio);
|
||||
}
|
||||
while (not ops.empty()) {
|
||||
ops.front()(*dht_);
|
||||
ops.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::doRun(const sockaddr_in* sin4, const sockaddr_in6* sin6, SecureDht::Config config)
|
||||
DhtRunner::doRun(SecureDht::Config config)
|
||||
{
|
||||
dht_.reset();
|
||||
|
||||
int s4 = -1,
|
||||
s6 = -1;
|
||||
|
||||
bound4 = {};
|
||||
if (sin4) {
|
||||
s4 = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if(s4 >= 0) {
|
||||
int rc = bind(s4, (sockaddr*)sin4, sizeof(sockaddr_in));
|
||||
if(rc < 0)
|
||||
throw DhtException("Can't bind IPv4 socket on " + dht::print_addr((sockaddr*)sin4, sizeof(sockaddr_in)));
|
||||
bound4.second = sizeof(bound4.first);
|
||||
getsockname(s4, (sockaddr*)&bound4.first, &bound4.second);
|
||||
}
|
||||
}
|
||||
|
||||
#if 1
|
||||
bound6 = {};
|
||||
if (sin6) {
|
||||
s6 = socket(PF_INET6, SOCK_DGRAM, 0);
|
||||
if(s6 >= 0) {
|
||||
int val = 1;
|
||||
int rc = setsockopt(s6, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&val, sizeof(val));
|
||||
if(rc < 0)
|
||||
throw DhtException("Can't set IPV6_V6ONLY");
|
||||
|
||||
rc = bind(s6, (sockaddr*)sin6, sizeof(sockaddr_in6));
|
||||
if(rc < 0)
|
||||
throw DhtException("Can't bind IPv6 socket on " + dht::print_addr((sockaddr*)sin6, sizeof(sockaddr_in6)));
|
||||
bound6.second = sizeof(bound6.first);
|
||||
getsockname(s6, (sockaddr*)&bound6.first, &bound6.second);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
dht_ = std::unique_ptr<SecureDht>(new SecureDht {s4, s6, config});
|
||||
|
||||
rcv_thread = std::thread([this,s4,s6]() {
|
||||
try {
|
||||
while (true) {
|
||||
struct timeval tv {/*.tv_sec = */0, /*.tv_usec = */250000};
|
||||
fd_set readfds;
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
if(s4 >= 0)
|
||||
FD_SET(s4, &readfds);
|
||||
if(s6 >= 0)
|
||||
FD_SET(s6, &readfds);
|
||||
|
||||
int rc = select(s4 > s6 ? s4 + 1 : s6 + 1, &readfds, nullptr, nullptr, &tv);
|
||||
if(rc < 0) {
|
||||
if(errno != EINTR) {
|
||||
perror("select");
|
||||
std::this_thread::sleep_for( std::chrono::seconds(1) );
|
||||
}
|
||||
}
|
||||
|
||||
if(!running)
|
||||
break;
|
||||
|
||||
if(rc > 0) {
|
||||
std::array<uint8_t, 1024 * 64> buf;
|
||||
SockAddr from;
|
||||
from.second = sizeof(from.first);
|
||||
|
||||
if(s4 >= 0 && FD_ISSET(s4, &readfds))
|
||||
rc = recvfrom(s4, (char*)buf.data(), buf.size(), 0, (struct sockaddr*)&from.first, &from.second);
|
||||
else if(s6 >= 0 && FD_ISSET(s6, &readfds))
|
||||
rc = recvfrom(s6, (char*)buf.data(), buf.size(), 0, (struct sockaddr*)&from.first, &from.second);
|
||||
else
|
||||
break;
|
||||
if (rc > 0) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(sock_mtx);
|
||||
rcv.emplace_back(Blob {buf.begin(), buf.begin()+rc+1}, from);
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Error in DHT networking thread: " << e.what() << std::endl;
|
||||
}
|
||||
if (s4 >= 0)
|
||||
close(s4);
|
||||
if (s6 >= 0)
|
||||
close(s6);
|
||||
});
|
||||
dht_ = std::unique_ptr<SecureDht>(new SecureDht {uv_loop_.get(), config});
|
||||
}
|
||||
|
||||
void
|
||||
@@ -475,10 +312,11 @@ DhtRunner::get(InfoHash hash, GetCallback vcb, DoneCallback dcb, Value::Filter f
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) mutable {
|
||||
std::cout << "DhtRunner dht.get " << hash << std::endl;
|
||||
dht.get(hash, vcb, dcb, std::move(f), std::move(w));
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
uv_async_send(&uv_async_);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -493,7 +331,7 @@ void DhtRunner::query(const InfoHash& hash, QueryCallback cb, DoneCallback done_
|
||||
dht.query(hash, cb, done_cb, std::move(q));
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
uv_async_send(&uv_async_);
|
||||
}
|
||||
|
||||
std::future<size_t>
|
||||
@@ -506,7 +344,7 @@ DhtRunner::listen(InfoHash hash, GetCallback vcb, Value::Filter f, Where w)
|
||||
ret_token->set_value(dht.listen(hash, vcb, std::move(f), std::move(w)));
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
uv_async_send(&uv_async_);
|
||||
return ret_token->get_future();
|
||||
}
|
||||
|
||||
@@ -525,7 +363,7 @@ DhtRunner::cancelListen(InfoHash h, size_t token)
|
||||
dht.cancelListen(h, token);
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
uv_async_send(&uv_async_);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -538,7 +376,7 @@ DhtRunner::cancelListen(InfoHash h, std::shared_future<size_t> token)
|
||||
dht.cancelListen(h, tk);
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
uv_async_send(&uv_async_);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -551,7 +389,7 @@ DhtRunner::put(InfoHash hash, Value&& value, DoneCallback cb, time_point created
|
||||
dht.put(hash, sv, cb, created, permanent);
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
uv_async_send(&uv_async_);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -563,7 +401,7 @@ DhtRunner::put(InfoHash hash, std::shared_ptr<Value> value, DoneCallback cb, tim
|
||||
dht.put(hash, value, cb, created, permanent);
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
uv_async_send(&uv_async_);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -581,7 +419,7 @@ DhtRunner::cancelPut(const InfoHash& h , const Value::Id& id)
|
||||
dht.cancelPut(h, id);
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
uv_async_send(&uv_async_);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -593,7 +431,7 @@ DhtRunner::putSigned(InfoHash hash, std::shared_ptr<Value> value, DoneCallback c
|
||||
dht.putSigned(hash, value, cb);
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
uv_async_send(&uv_async_);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -617,7 +455,7 @@ DhtRunner::putEncrypted(InfoHash hash, InfoHash to, std::shared_ptr<Value> value
|
||||
dht.putEncrypted(hash, to, value, cb);
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
uv_async_send(&uv_async_);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -632,6 +470,7 @@ DhtRunner::putEncrypted(const std::string& key, InfoHash to, Value&& value, Done
|
||||
putEncrypted(InfoHash::get(key), to, std::forward<Value>(value), cb);
|
||||
}
|
||||
|
||||
/*
|
||||
void
|
||||
DhtRunner::tryBootstrapContinuously()
|
||||
{
|
||||
@@ -689,7 +528,7 @@ DhtRunner::tryBootstrapContinuously()
|
||||
}
|
||||
} while (bootstraping);
|
||||
});
|
||||
}
|
||||
}*/
|
||||
|
||||
std::vector<std::pair<sockaddr_storage, socklen_t>>
|
||||
DhtRunner::getAddrInfo(const std::string& host, const std::string& service)
|
||||
@@ -719,45 +558,60 @@ DhtRunner::getAddrInfo(const std::string& host, const std::string& service)
|
||||
void
|
||||
DhtRunner::bootstrap(const std::string& host, const std::string& service)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(bootstrap_mtx);
|
||||
/*std::lock_guard<std::mutex> lck(bootstrap_mtx);
|
||||
bootstrap_nodes_all.emplace_back(host, service);
|
||||
bootstrap_nodes.emplace_back(host, service);
|
||||
tryBootstrapContinuously();
|
||||
tryBootstrapContinuously();*/
|
||||
pending_ops_prio.emplace([=](SecureDht& dht) mutable {
|
||||
dht.bootstrap(host, service);
|
||||
});
|
||||
uv_async_send(&uv_async_);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::clearBootstrap()
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(bootstrap_mtx);
|
||||
bootstrap_nodes_all.clear();
|
||||
//std::lock_guard<std::mutex> lck(bootstrap_mtx);
|
||||
//bootstrap_nodes_all.clear();
|
||||
pending_ops_prio.emplace([=](SecureDht& dht) mutable {
|
||||
dht.clearBootstrap();
|
||||
});
|
||||
uv_async_send(&uv_async_);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::bootstrap(const std::vector<std::pair<sockaddr_storage, socklen_t>>& nodes, DoneCallbackSimple&& cb)
|
||||
DhtRunner::ping(const std::vector<std::pair<sockaddr_storage, socklen_t>>& nodes, DoneCallback&& cb)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht& dht) mutable {
|
||||
auto rem = cb ? std::make_shared<std::pair<size_t, bool>>(nodes.size(), false) : nullptr;
|
||||
struct GroupPing {
|
||||
size_t remaining {0};
|
||||
bool ok {false};
|
||||
std::vector<Sp<Node>> nodes;
|
||||
GroupPing(size_t s) : remaining(s) {}
|
||||
};
|
||||
Sp<GroupPing> rem = cb ? std::make_shared<GroupPing>(nodes.size()) : nullptr;
|
||||
for (auto& node : nodes)
|
||||
dht.pingNode((sockaddr*)&node.first, node.second, cb ? [rem,cb](bool ok) {
|
||||
dht.pingNode(SockAddr((sockaddr*)&node.first, node.second), cb ? [rem,cb](bool ok, std::vector<Sp<Node>>&& nodes) {
|
||||
auto& r = *rem;
|
||||
r.first--;
|
||||
r.second |= ok;
|
||||
if (not r.first)
|
||||
cb(r.second);
|
||||
} : DoneCallbackSimple{});
|
||||
r.remaining--;
|
||||
r.ok |= ok;
|
||||
r.nodes.insert(r.nodes.end(), nodes.begin(), nodes.end());
|
||||
if (r.remaining == 0)
|
||||
cb(r.ok, std::move(r.nodes));
|
||||
} : DoneCallback{});
|
||||
});
|
||||
cv.notify_all();
|
||||
uv_async_send(&uv_async_);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::bootstrap(const SockAddr& addr, DoneCallbackSimple&& cb)
|
||||
DhtRunner::ping(const SockAddr& addr, DoneCallback&& cb)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([addr,cb](SecureDht& dht) mutable {
|
||||
dht.pingNode((const sockaddr*)&addr.first, addr.second, std::move(cb));
|
||||
dht.pingNode(addr, std::move(cb));
|
||||
});
|
||||
cv.notify_all();
|
||||
uv_async_send(&uv_async_);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -770,7 +624,7 @@ DhtRunner::bootstrap(const std::vector<NodeExport>& nodes)
|
||||
dht.insertNode(node);
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
uv_async_send(&uv_async_);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -782,7 +636,7 @@ DhtRunner::connectivityChanged()
|
||||
dht.connectivityChanged();
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
uv_async_send(&uv_async_);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -793,7 +647,7 @@ DhtRunner::findCertificate(InfoHash hash, std::function<void(const std::shared_p
|
||||
dht.findCertificate(hash, cb);
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
uv_async_send(&uv_async_);
|
||||
}
|
||||
|
||||
}
|
||||
+29
-2
@@ -65,8 +65,8 @@ InfoHash::getRandom()
|
||||
InfoHash h;
|
||||
crypto::random_device rdev;
|
||||
std::uniform_int_distribution<uint32_t> rand_int;
|
||||
auto a = reinterpret_cast<uint32_t*>(&(*h.begin()));
|
||||
auto b = reinterpret_cast<uint32_t*>(&(*h.end()));
|
||||
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;
|
||||
}
|
||||
@@ -121,4 +121,31 @@ std::istream& operator>> (std::istream& s, InfoHash& h)
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
NodeExport::msgpack_unpack(msgpack::object o)
|
||||
{
|
||||
if (o.type != msgpack::type::MAP)
|
||||
throw msgpack::type_error();
|
||||
if (o.via.map.size < 2)
|
||||
throw msgpack::type_error();
|
||||
if (o.via.map.ptr[0].key.as<std::string>() != "id")
|
||||
throw msgpack::type_error();
|
||||
if (o.via.map.ptr[1].key.as<std::string>() != "addr")
|
||||
throw msgpack::type_error();
|
||||
const auto& addr = o.via.map.ptr[1].val;
|
||||
if (addr.type != msgpack::type::BIN)
|
||||
throw msgpack::type_error();
|
||||
if (addr.via.bin.size > sizeof(sockaddr_storage))
|
||||
throw msgpack::type_error();
|
||||
id.msgpack_unpack(o.via.map.ptr[0].val);
|
||||
sslen = addr.via.bin.size;
|
||||
std::copy_n(addr.via.bin.ptr, addr.via.bin.size, (char*)&ss);
|
||||
}
|
||||
|
||||
std::ostream& operator<< (std::ostream& s, const NodeExport& h)
|
||||
{
|
||||
msgpack::pack(s, h);
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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
|
||||
* 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 "utils.h"
|
||||
|
||||
namespace dht {
|
||||
|
||||
/**
|
||||
* Foreign nodes asking for updates about an InfoHash.
|
||||
*/
|
||||
struct Listener {
|
||||
size_t sid;
|
||||
time_point time;
|
||||
Query query;
|
||||
|
||||
Listener(size_t sid, time_point t, Query&& q) : sid(sid), time(t), query(std::move(q)) {}
|
||||
|
||||
void refresh(size_t s, time_point t, Query&& q) {
|
||||
sid = s;
|
||||
time = t;
|
||||
query = std::move(q);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A single "listen" operation data
|
||||
*/
|
||||
struct LocalListener {
|
||||
Sp<Query> query;
|
||||
Value::Filter filter;
|
||||
GetCallback get_cb;
|
||||
};
|
||||
|
||||
}
|
||||
+36
-3
@@ -19,6 +19,13 @@
|
||||
|
||||
#include "log.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
|
||||
#include <fstream>
|
||||
#include <chrono>
|
||||
|
||||
namespace dht {
|
||||
namespace log {
|
||||
|
||||
@@ -37,9 +44,9 @@ printLog(std::ostream& s, char const *m, va_list args) {
|
||||
using namespace std::chrono;
|
||||
using log_precision = microseconds;
|
||||
constexpr auto den = log_precision::period::den;
|
||||
auto micro = duration_cast<log_precision>(steady_clock::now().time_since_epoch()).count();
|
||||
s << "[" << std::setfill('0') << std::setw(6) << micro / den << "."
|
||||
<< std::setfill('0') << std::setw(6) << micro % den << "]" << " ";
|
||||
auto num = duration_cast<log_precision>(steady_clock::now().time_since_epoch()).count();
|
||||
s << "[" << std::setfill('0') << std::setw(6) << num / den << "."
|
||||
<< std::setfill('0') << std::setw(6) << num % den << "]" << " ";
|
||||
|
||||
// write log
|
||||
s.write(buffer.data(), std::min((size_t) ret, buffer.size()));
|
||||
@@ -77,6 +84,32 @@ enableFileLogging(dht::DhtRunner &dht, const std::string &path) {
|
||||
);
|
||||
}
|
||||
|
||||
OPENDHT_PUBLIC void
|
||||
enableSyslog(dht::DhtRunner &dht, const char* name) {
|
||||
#ifndef _WIN32
|
||||
struct Syslog {
|
||||
Syslog(const char* n) {
|
||||
openlog(n, LOG_NDELAY, LOG_USER);
|
||||
}
|
||||
~Syslog() {
|
||||
closelog();
|
||||
}
|
||||
};
|
||||
// syslog is global. Existing instance must be reused.
|
||||
static std::weak_ptr<Syslog> opened_logfile;
|
||||
auto logfile = opened_logfile.lock();
|
||||
if (not logfile) {
|
||||
logfile = std::make_shared<Syslog>(name);
|
||||
opened_logfile = logfile;
|
||||
}
|
||||
dht.setLoggers(
|
||||
[logfile](char const *m, va_list args) { vsyslog(LOG_ERR, m, args); },
|
||||
[logfile](char const *m, va_list args) { vsyslog(LOG_WARNING, m, args); },
|
||||
[logfile](char const *m, va_list args) { vsyslog(LOG_INFO, m, args); }
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
disableLogging(dht::DhtRunner &dht) {
|
||||
dht.setLoggers(dht::NOLOG, dht::NOLOG, dht::NOLOG);
|
||||
|
||||
+419
-548
File diff suppressed because it is too large.
Load diff
+76
-24
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "node.h"
|
||||
#include "request.h"
|
||||
#include "rng.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
@@ -29,6 +30,14 @@ 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, const Sp<TcpSocket>& s, bool client)
|
||||
: id(id), addr(addr), sock(s), is_client(client), sockets_()
|
||||
{
|
||||
crypto::random_device rd;
|
||||
transaction_id = std::uniform_int_distribution<SocketId>{1}(rd);
|
||||
}
|
||||
|
||||
/* This is our definition of a known-good node. */
|
||||
bool
|
||||
Node::isGood(time_point now) const
|
||||
@@ -41,11 +50,9 @@ Node::isGood(time_point now) const
|
||||
bool
|
||||
Node::isPendingMessage() const
|
||||
{
|
||||
for (auto w : requests_) {
|
||||
if (auto r = w.lock()) {
|
||||
if (r->pending())
|
||||
return true;
|
||||
}
|
||||
for (auto r : requests_) {
|
||||
if (r.second->pending())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -54,44 +61,61 @@ size_t
|
||||
Node::getPendingMessageCount() const
|
||||
{
|
||||
size_t count {0};
|
||||
for (auto w : requests_) {
|
||||
if (auto r = w.lock()) {
|
||||
if (r->pending())
|
||||
count++;
|
||||
}
|
||||
for (auto r : requests_) {
|
||||
if (r.second->pending())
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
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 */
|
||||
void
|
||||
Node::requested(std::shared_ptr<net::Request>& req)
|
||||
Node::requested(const Sp<net::Request>& req)
|
||||
{
|
||||
requests_.emplace_back(req);
|
||||
auto e = requests_.emplace(req->getTid(), req);
|
||||
if (not e.second and req != e.first->second) {
|
||||
// Should not happen !
|
||||
// Try to handle this scenario as well as we can
|
||||
e.first->second->setExpired();
|
||||
e.first->second = req;
|
||||
}
|
||||
}
|
||||
|
||||
/** To be called when a message was received from the node.
|
||||
Req should be true if the message was an aswer to a request we made*/
|
||||
void
|
||||
Node::received(time_point now, std::shared_ptr<net::Request> req)
|
||||
Node::received(time_point now, const Sp<net::Request>& req)
|
||||
{
|
||||
time = now;
|
||||
expired_ = false;
|
||||
if (req) {
|
||||
reply_time = now;
|
||||
for (auto it = requests_.begin(); it != requests_.end();) {
|
||||
auto r = it->lock();
|
||||
if (not r or r == req)
|
||||
it = requests_.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
requests_.erase(req->getTid());
|
||||
}
|
||||
}
|
||||
|
||||
Sp<net::Request>
|
||||
Node::getRequest(const net::TransId& tid)
|
||||
{
|
||||
auto it = requests_.find(tid);
|
||||
return it != requests_.end() ? it->second : nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
Node::cancelRequest(const Sp<net::Request>& req)
|
||||
{
|
||||
if (req) {
|
||||
req->cancel();
|
||||
closeSocket(req->closeSocket());
|
||||
requests_.erase(req->getTid());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,13 +123,41 @@ void
|
||||
Node::setExpired()
|
||||
{
|
||||
expired_ = true;
|
||||
for (auto w : requests_) {
|
||||
if (auto r = w.lock())
|
||||
r->setExpired();
|
||||
for (auto r : requests_) {
|
||||
r.second->setExpired();
|
||||
}
|
||||
requests_.clear();
|
||||
sockets_.clear();
|
||||
}
|
||||
|
||||
SocketId
|
||||
Node::openSocket(SocketCb&& cb)
|
||||
{
|
||||
if (++transaction_id == 0)
|
||||
transaction_id = 1;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Socket*
|
||||
Node::getSocket(SocketId id)
|
||||
{
|
||||
auto it = sockets_.find(id);
|
||||
return it == sockets_.end() ? nullptr : &it->second;
|
||||
}
|
||||
|
||||
void
|
||||
Node::closeSocket(SocketId id)
|
||||
{
|
||||
if (id) {
|
||||
sockets_.erase(id);
|
||||
//DHT_LOG.w("Closing socket (tid: %d), %lu remaining", socket->id, sockets_.size());
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
Node::toString() const
|
||||
|
||||
+59
-28
@@ -20,44 +20,47 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
std::shared_ptr<Node>
|
||||
Sp<Node>
|
||||
NodeCache::getNode(const InfoHash& id, sa_family_t family) {
|
||||
return (family == AF_INET ? cache_4 : cache_6).getNode(id);
|
||||
return cache(family).getNode(id);
|
||||
}
|
||||
|
||||
std::shared_ptr<Node>
|
||||
NodeCache::getNode(const InfoHash& id, const SockAddr& addr, time_point now, bool confirm) {
|
||||
Sp<Node>
|
||||
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 (addr.getFamily() == AF_INET ? cache_4 : cache_6).getNode(id, addr, now, confirm);
|
||||
return cache(addr.getFamily()).getNode(id, addr, sock, now, confirm, client);
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Node>>
|
||||
NodeCache::getCachedNodes(const InfoHash& id, sa_family_t sa_f, size_t count) {
|
||||
const auto& c = (sa_f == AF_INET ? cache_4 : cache_6);
|
||||
auto it_p = c.lower_bound(id),
|
||||
it_n = it_p;
|
||||
std::vector<Sp<Node>>
|
||||
NodeCache::getCachedNodes(const InfoHash& id, sa_family_t sa_f, size_t count) const
|
||||
{
|
||||
const auto& c = cache(sa_f);
|
||||
|
||||
std::vector<std::shared_ptr<Node>> nodes;
|
||||
std::vector<Sp<Node>> nodes;
|
||||
nodes.reserve(std::min(c.size(), count));
|
||||
NodeMap::const_iterator it;
|
||||
auto dec_it = [&c](NodeMap::const_iterator& it) {
|
||||
auto ret = it;
|
||||
it = (it == c.cbegin()) ? c.cend() : std::prev(it);
|
||||
return ret;
|
||||
};
|
||||
|
||||
if (it_p != c.begin()) /* Create 2 separate iterator if we could */
|
||||
--it_p;
|
||||
auto it_p = c.lower_bound(id),
|
||||
it_n = it_p;
|
||||
if (not c.empty())
|
||||
dec_it(it_p); /* Create 2 separate iterator if we could */
|
||||
|
||||
while (nodes.size() < count and (it_n != c.end() or it_p != c.end())) {
|
||||
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 == c.end()) it = it_n++;
|
||||
else if (it_n == c.end()) it = it_p--;
|
||||
else it = id.xorCmp(it_p->first, it_n->first) < 0 ? it_p-- : it_n++;
|
||||
|
||||
if (it == c.begin())
|
||||
it_p = c.end();
|
||||
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() )
|
||||
if ( not n->isExpired() and not n->isClient() )
|
||||
nodes.emplace_back(std::move(n));
|
||||
}
|
||||
|
||||
@@ -71,11 +74,22 @@ NodeCache::clearBadNodes(sa_family_t family)
|
||||
clearBadNodes(AF_INET);
|
||||
clearBadNodes(AF_INET6);
|
||||
} else {
|
||||
(family == AF_INET ? cache_4 : cache_6).clearBadNodes();
|
||||
cache(family).clearBadNodes();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Node>
|
||||
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)
|
||||
{
|
||||
auto wn = find(id);
|
||||
@@ -87,16 +101,16 @@ NodeCache::NodeMap::getNode(const InfoHash& id)
|
||||
return {};
|
||||
}
|
||||
|
||||
std::shared_ptr<Node>
|
||||
NodeCache::NodeMap::getNode(const InfoHash& id, const SockAddr& addr, time_point now, bool confirm)
|
||||
Sp<Node>
|
||||
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);
|
||||
node = std::make_shared<Node>(id, addr, sock, client);
|
||||
it.first->second = node;
|
||||
} else if (confirm || node->time < now - Node::NODE_EXPIRE_TIME) {
|
||||
node->update(addr);
|
||||
} else if (confirm or node->isOld(now)) {
|
||||
node->update(addr, sock);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
@@ -113,4 +127,21 @@ NodeCache::NodeMap::clearBadNodes() {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NodeCache::NodeMap::closeAll()
|
||||
{
|
||||
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++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#include "infohash.h"
|
||||
#include "sockaddr.h"
|
||||
#include "net.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace dht {
|
||||
namespace net {
|
||||
|
||||
enum class MessageType {
|
||||
Error = 0,
|
||||
Reply,
|
||||
Ping,
|
||||
FindNode,
|
||||
GetValues,
|
||||
AnnounceValue,
|
||||
Refresh,
|
||||
Listen,
|
||||
ValueData
|
||||
};
|
||||
|
||||
struct ParsedMessage {
|
||||
MessageType type;
|
||||
/* Node ID of the sender */
|
||||
InfoHash id;
|
||||
/* Network id */
|
||||
NetId network {0};
|
||||
/** Is a client node */
|
||||
bool is_client {false};
|
||||
/* hash for which values are requested */
|
||||
InfoHash info_hash;
|
||||
/* target id around which to find nodes */
|
||||
InfoHash target;
|
||||
/* transaction id */
|
||||
TransId tid;
|
||||
/* tid for packets going through request socket */
|
||||
TransId socket_id;
|
||||
/* security token */
|
||||
Blob token;
|
||||
/* the value id (announce confirmation) */
|
||||
Value::Id value_id;
|
||||
/* time when value was first created */
|
||||
time_point created { time_point::max() };
|
||||
/* IPv4 nodes in response to a 'find' request */
|
||||
Blob nodes4_raw, nodes6_raw;
|
||||
std::vector<Sp<Node>> nodes4, nodes6;
|
||||
/* values to store or retreive request */
|
||||
std::vector<Sp<Value>> values;
|
||||
/* index for fields values */
|
||||
std::vector<Sp<FieldValueIndex>> fields;
|
||||
/** When part of the message header: {index -> (total size, {})}
|
||||
* When part of partial value data: {index -> (offset, part_data)} */
|
||||
std::map<unsigned, std::pair<unsigned, Blob>> value_parts;
|
||||
/* query describing a filter to apply on values. */
|
||||
Query query;
|
||||
/* states if ipv4 or ipv6 request */
|
||||
want_t want;
|
||||
/* error code in case of error */
|
||||
uint16_t error_code;
|
||||
/* reported address by the distant node */
|
||||
std::string ua;
|
||||
SockAddr addr;
|
||||
void msgpack_unpack(msgpack::object o);
|
||||
|
||||
bool append(const ParsedMessage& block);
|
||||
bool complete();
|
||||
};
|
||||
|
||||
bool
|
||||
ParsedMessage::append(const ParsedMessage& block)
|
||||
{
|
||||
bool ret(false);
|
||||
for (const auto& ve : block.value_parts) {
|
||||
auto part_val = value_parts.find(ve.first);
|
||||
if (part_val == value_parts.end()
|
||||
|| part_val->second.second.size() >= part_val->second.first)
|
||||
continue;
|
||||
// TODO: handle out-of-order packets
|
||||
if (ve.second.first != part_val->second.second.size()) {
|
||||
//std::cout << "skipping out-of-order packet" << std::endl;
|
||||
continue;
|
||||
}
|
||||
ret = true;
|
||||
part_val->second.second.insert(part_val->second.second.end(),
|
||||
ve.second.second.begin(),
|
||||
ve.second.second.end());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
ParsedMessage::complete()
|
||||
{
|
||||
for (auto& e : value_parts) {
|
||||
//std::cout << "part " << e.first << ": " << e.second.second.size() << "/" << e.second.first << std::endl;
|
||||
if (e.second.first > e.second.second.size())
|
||||
return false;
|
||||
}
|
||||
for (auto& e : value_parts) {
|
||||
msgpack::unpacked msg;
|
||||
msgpack::unpack(msg, (const char*)e.second.second.data(), e.second.second.size());
|
||||
values.emplace_back(std::make_shared<Value>(msg.get()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ParsedMessage::msgpack_unpack(msgpack::object msg)
|
||||
{
|
||||
auto y = findMapValue(msg, "y");
|
||||
auto r = findMapValue(msg, "r");
|
||||
auto e = findMapValue(msg, "e");
|
||||
auto v = findMapValue(msg, "p");
|
||||
|
||||
if (auto t = findMapValue(msg, "t"))
|
||||
tid = unpackTid(*t);
|
||||
|
||||
if (auto rv = findMapValue(msg, "v"))
|
||||
ua = rv->as<std::string>();
|
||||
|
||||
if (auto netid = findMapValue(msg, "n"))
|
||||
network = netid->as<NetId>();
|
||||
|
||||
if (auto is_client_v = findMapValue(msg, "s"))
|
||||
is_client = is_client_v->as<bool>();
|
||||
|
||||
std::string q;
|
||||
if (auto rq = findMapValue(msg, "q")) {
|
||||
if (rq->type != msgpack::type::STR)
|
||||
throw msgpack::type_error();
|
||||
q = rq->as<std::string>();
|
||||
}
|
||||
|
||||
if (e)
|
||||
type = MessageType::Error;
|
||||
else if (r)
|
||||
type = MessageType::Reply;
|
||||
else if (v)
|
||||
type = MessageType::ValueData;
|
||||
else if (y and y->as<std::string>() != "q")
|
||||
throw msgpack::type_error();
|
||||
else if (q == "ping")
|
||||
type = MessageType::Ping;
|
||||
else if (q == "find")
|
||||
type = MessageType::FindNode;
|
||||
else if (q == "get")
|
||||
type = MessageType::GetValues;
|
||||
else if (q == "listen")
|
||||
type = MessageType::Listen;
|
||||
else if (q == "put")
|
||||
type = MessageType::AnnounceValue;
|
||||
else if (q == "refresh")
|
||||
type = MessageType::Refresh;
|
||||
else
|
||||
throw msgpack::type_error();
|
||||
|
||||
if (type == MessageType::ValueData) {
|
||||
if (v->type != msgpack::type::MAP)
|
||||
throw msgpack::type_error();
|
||||
for (size_t i = 0; i < v->via.map.size; ++i) {
|
||||
auto& vdat = v->via.map.ptr[i];
|
||||
auto o = findMapValue(vdat.val, "o");
|
||||
auto d = findMapValue(vdat.val, "d");
|
||||
if (not o or not d)
|
||||
continue;
|
||||
value_parts.emplace(vdat.key.as<unsigned>(), std::pair<size_t, Blob>(o->as<size_t>(), unpackBlob(*d)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto a = findMapValue(msg, "a");
|
||||
if (!a && !r && !e)
|
||||
throw msgpack::type_error();
|
||||
auto& req = a ? *a : (r ? *r : *e);
|
||||
|
||||
if (e) {
|
||||
if (e->type != msgpack::type::ARRAY)
|
||||
throw msgpack::type_error();
|
||||
error_code = e->via.array.ptr[0].as<uint16_t>();
|
||||
}
|
||||
|
||||
if (auto t = findMapValue(req, "sid"))
|
||||
socket_id = unpackTid(*t);
|
||||
|
||||
if (auto rid = findMapValue(req, "id"))
|
||||
id = {*rid};
|
||||
|
||||
if (auto rh = findMapValue(req, "h"))
|
||||
info_hash = {*rh};
|
||||
|
||||
if (auto rtarget = findMapValue(req, "target"))
|
||||
target = {*rtarget};
|
||||
|
||||
if (auto rquery = findMapValue(req, "q"))
|
||||
query.msgpack_unpack(*rquery);
|
||||
|
||||
if (auto otoken = findMapValue(req, "token"))
|
||||
token = unpackBlob(*otoken);
|
||||
|
||||
if (auto vid = findMapValue(req, "vid"))
|
||||
value_id = vid->as<Value::Id>();
|
||||
|
||||
if (auto rnodes4 = findMapValue(req, "n4"))
|
||||
nodes4_raw = unpackBlob(*rnodes4);
|
||||
|
||||
if (auto rnodes6 = findMapValue(req, "n6"))
|
||||
nodes6_raw = unpackBlob(*rnodes6);
|
||||
|
||||
if (auto sa = findMapValue(req, "sa")) {
|
||||
if (sa->type != msgpack::type::BIN)
|
||||
throw msgpack::type_error();
|
||||
auto l = sa->via.bin.size;
|
||||
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);
|
||||
}
|
||||
} else
|
||||
addr = {};
|
||||
|
||||
if (auto rcreated = findMapValue(req, "c"))
|
||||
created = from_time_t(rcreated->as<std::time_t>());
|
||||
|
||||
if (auto rvalues = findMapValue(req, "values")) {
|
||||
if (rvalues->type != msgpack::type::ARRAY)
|
||||
throw msgpack::type_error();
|
||||
for (size_t i = 0; i < rvalues->via.array.size; i++) {
|
||||
auto& packed_v = rvalues->via.array.ptr[i];
|
||||
if (packed_v.type == msgpack::type::POSITIVE_INTEGER) {
|
||||
// Skip oversize values with a small margin for header overhead
|
||||
if (packed_v.via.u64 > MAX_VALUE_SIZE + 32)
|
||||
continue;
|
||||
value_parts.emplace(i, std::make_pair(packed_v.via.u64, Blob{}));
|
||||
} else {
|
||||
try {
|
||||
values.emplace_back(std::make_shared<Value>(rvalues->via.array.ptr[i]));
|
||||
} catch (const std::exception& e) {
|
||||
//DHT_LOG_WARN("Error reading value: %s", e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (auto raw_fields = findMapValue(req, "fields")) {
|
||||
if (auto rfields = findMapValue(*raw_fields, "f")) {
|
||||
auto vfields = rfields->as<std::set<Value::Field>>();
|
||||
if (auto rvalues = findMapValue(*raw_fields, "v")) {
|
||||
if (rvalues->type != msgpack::type::ARRAY)
|
||||
throw msgpack::type_error();
|
||||
size_t val_num = rvalues->via.array.size / vfields.size();
|
||||
for (size_t i = 0; i < val_num; ++i) {
|
||||
try {
|
||||
auto v = std::make_shared<FieldValueIndex>();
|
||||
v->msgpack_unpack_fields(vfields, *rvalues, i*vfields.size());
|
||||
fields.emplace_back(std::move(v));
|
||||
} catch (const std::exception& e) { }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw msgpack::type_error();
|
||||
}
|
||||
}
|
||||
|
||||
if (auto w = findMapValue(req, "w")) {
|
||||
if (w->type != msgpack::type::ARRAY)
|
||||
throw msgpack::type_error();
|
||||
want = 0;
|
||||
for (unsigned i=0; i<w->via.array.size; i++) {
|
||||
auto& val = w->via.array.ptr[i];
|
||||
try {
|
||||
auto w = val.as<sa_family_t>();
|
||||
if (w == AF_INET)
|
||||
want |= WANT4;
|
||||
else if(w == AF_INET6)
|
||||
want |= WANT6;
|
||||
} catch (const std::exception& e) {};
|
||||
}
|
||||
} else {
|
||||
want = -1;
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace net */
|
||||
} /* namespace dht */
|
||||
@@ -20,34 +20,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "net.h"
|
||||
#include "value.h"
|
||||
|
||||
namespace dht {
|
||||
struct Node;
|
||||
namespace net {
|
||||
|
||||
class NetworkEngine;
|
||||
struct ParsedMessage;
|
||||
|
||||
/*!
|
||||
* @class Socket
|
||||
* @brief Open route to a node for continous incoming packets.
|
||||
* @details
|
||||
* A socket lets a remote node send us continuous packets treated using a
|
||||
* given callback. This is intended to provide an easy management of
|
||||
* specific updates nodes can send. For e.g, this is used in the case of the
|
||||
* "listen" operation for treating updates a node has for a given storage.
|
||||
*/
|
||||
struct Socket {
|
||||
Socket() {}
|
||||
Socket(std::shared_ptr<Node> node,
|
||||
TransId id,
|
||||
std::function<void(const std::shared_ptr<Node>&, ParsedMessage&&)> on_receive) :
|
||||
node(node), id(id), on_receive(on_receive) {}
|
||||
|
||||
std::shared_ptr<Node> node;
|
||||
TransId id;
|
||||
std::function<void(const std::shared_ptr<Node>&, ParsedMessage&&)> on_receive {};
|
||||
};
|
||||
|
||||
/*!
|
||||
* @class Request
|
||||
* @brief An atomic request destined to a node.
|
||||
@@ -59,7 +40,7 @@ struct Socket {
|
||||
struct Request {
|
||||
friend class dht::net::NetworkEngine;
|
||||
|
||||
std::shared_ptr<Node> node {}; /* the node to whom the request is destined. */
|
||||
Sp<Node> node {}; /* the node to whom the request is destined. */
|
||||
time_point reply_time {time_point::min()}; /* time when we received the response to the request. */
|
||||
|
||||
enum class State
|
||||
@@ -78,14 +59,19 @@ struct Request {
|
||||
State getState() const { return state_; }
|
||||
|
||||
Request(State state = State::PENDING) : state_(state) {}
|
||||
Request(TransId tid,
|
||||
std::shared_ptr<Node> node,
|
||||
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,
|
||||
std::shared_ptr<Socket> socket = {}) :
|
||||
uint32_t socket = 0) :
|
||||
node(node), on_done(on_done), on_expired(on_expired), tid(tid), msg(std::move(msg)), socket(socket) { }
|
||||
|
||||
TransId getTid() const { return tid; }
|
||||
|
||||
uint32_t getSocket() { return socket; }
|
||||
uint32_t closeSocket() { auto ret = socket; socket = 0; return ret; }
|
||||
|
||||
void setExpired() {
|
||||
if (pending()) {
|
||||
state_ = Request::State::EXPIRED;
|
||||
@@ -132,7 +118,7 @@ private:
|
||||
|
||||
const TransId tid; /* the request id. */
|
||||
Blob msg {}; /* the serialized message. */
|
||||
std::shared_ptr<Socket> socket; /* the socket used for further reponses. */
|
||||
uint32_t socket; /* the socket used for further reponses. */
|
||||
};
|
||||
|
||||
} /* namespace net */
|
||||
+95
-7
@@ -1,4 +1,24 @@
|
||||
/*
|
||||
* 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
|
||||
* 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 "routing_table.h"
|
||||
|
||||
#include "network_engine.h"
|
||||
#include "rng.h"
|
||||
|
||||
#include <memory>
|
||||
@@ -35,6 +55,15 @@ Bucket::randomNode()
|
||||
return nodes.back();
|
||||
}
|
||||
|
||||
void Bucket::sendCachedPing(net::NetworkEngine& ne)
|
||||
{
|
||||
if (not cached)
|
||||
return;
|
||||
//DHT_LOG.d(b.cached->id, "[node %s] sending ping to cached node", cached->toString().c_str());
|
||||
ne.sendPing(cached, nullptr, nullptr);
|
||||
cached = {};
|
||||
}
|
||||
|
||||
InfoHash
|
||||
RoutingTable::randomId(const RoutingTable::const_iterator& it) const
|
||||
{
|
||||
@@ -80,7 +109,8 @@ RoutingTable::depth(const RoutingTable::const_iterator& it) const
|
||||
std::vector<Sp<Node>>
|
||||
RoutingTable::findClosestNodes(const InfoHash id, time_point now, size_t count) const
|
||||
{
|
||||
std::vector<Sp<Node>> nodes {};
|
||||
std::vector<Sp<Node>> nodes;
|
||||
nodes.reserve(count);
|
||||
auto bucket = findBucket(id);
|
||||
|
||||
if (bucket == end()) { return nodes; }
|
||||
@@ -100,7 +130,7 @@ RoutingTable::findClosestNodes(const InfoHash id, time_point now, size_t count)
|
||||
};
|
||||
|
||||
auto itn = bucket;
|
||||
auto itp = std::prev(bucket);
|
||||
auto itp = (bucket == begin()) ? end() : std::prev(bucket);
|
||||
while (nodes.size() < count && (itn != end() || itp != end())) {
|
||||
if (itn != end()) {
|
||||
sortedBucketInsert(*itn);
|
||||
@@ -108,11 +138,7 @@ RoutingTable::findClosestNodes(const InfoHash id, time_point now, size_t count)
|
||||
}
|
||||
if (itp != end()) {
|
||||
sortedBucketInsert(*itp);
|
||||
if (itp == begin()) {
|
||||
itp = end();
|
||||
continue;
|
||||
}
|
||||
itp = std::prev(itp);
|
||||
itp = (itp == begin()) ? end() : std::prev(itp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,4 +201,66 @@ RoutingTable::split(const RoutingTable::iterator& b)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
RoutingTable::onNewNode(const Sp<Node>& node, int confirm, const time_point& now, const InfoHash& myid, net::NetworkEngine& ne) {
|
||||
auto b = findBucket(node->id);
|
||||
if (b == end()) return false;
|
||||
|
||||
if (confirm == 2)
|
||||
b->time = now;
|
||||
|
||||
for (auto& n : b->nodes) {
|
||||
if (n == node)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mybucket = contains(b, myid);
|
||||
if (mybucket) {
|
||||
grow_time = now;
|
||||
//scheduler.edit(nextNodesConfirmation, now);
|
||||
}
|
||||
|
||||
if (b->nodes.size() >= TARGET_NODES) {
|
||||
/* Try to get rid of an expired node. */
|
||||
for (auto& n : b->nodes)
|
||||
if (n->isExpired()) {
|
||||
n = node;
|
||||
return true;
|
||||
}
|
||||
/* Bucket full. Ping a dubious node */
|
||||
bool dubious = false;
|
||||
for (auto& n : b->nodes) {
|
||||
/* Pick the first dubious node that we haven't pinged in the
|
||||
last 9 seconds. This gives nodes the time to reply, but
|
||||
tends to concentrate on the same nodes, so that we get rid
|
||||
of bad nodes fast. */
|
||||
if (not n->isGood(now)) {
|
||||
dubious = true;
|
||||
if (not n->isPendingMessage()) {
|
||||
//DHT_LOG.d(n->id, "[node %s] sending ping to dubious node", n->toString().c_str());
|
||||
ne.sendPing(n, nullptr, nullptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((mybucket || (is_client and depth(b) < 6)) && (!dubious || size() == 1)) {
|
||||
//DHT_LOG.d("Splitting from depth %u", depth(b));
|
||||
b->sendCachedPing(ne);
|
||||
split(b);
|
||||
return onNewNode(node, confirm, now, myid, ne);
|
||||
}
|
||||
|
||||
/* No space for this node. Cache it away for later. */
|
||||
if (confirm or not b->cached)
|
||||
b->cached = node;
|
||||
} else {
|
||||
/* Create a new node. */
|
||||
b->nodes.emplace_front(node);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
+867
@@ -0,0 +1,867 @@
|
||||
/*
|
||||
* 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
|
||||
* 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 "request.h"
|
||||
#include "listener.h"
|
||||
|
||||
namespace dht {
|
||||
|
||||
/**
|
||||
* A single "get" operation data
|
||||
*/
|
||||
struct Dht::Get {
|
||||
time_point start;
|
||||
Value::Filter filter;
|
||||
Sp<Query> query;
|
||||
QueryCallback query_cb;
|
||||
GetCallback get_cb;
|
||||
DoneCallback done_cb;
|
||||
};
|
||||
|
||||
/**
|
||||
* A single "put" operation data
|
||||
*/
|
||||
struct Dht::Announce {
|
||||
bool permanent;
|
||||
Sp<Value> value;
|
||||
time_point created;
|
||||
DoneCallback callback;
|
||||
};
|
||||
|
||||
struct Dht::SearchNode {
|
||||
/**
|
||||
* Foreach value id, we keep track of a pair (net::Request, time_point) where the
|
||||
* request is the request returned by the network engine and the time_point
|
||||
* is the next time at which the value must be refreshed.
|
||||
*/
|
||||
using AnnounceStatus = std::map<Value::Id, std::pair<Sp<net::Request>, time_point>>;
|
||||
/**
|
||||
* Foreach Query, we keep track of the request returned by the network
|
||||
* engine when we sent the "get".
|
||||
*/
|
||||
using SyncStatus = std::map<Sp<Query>, Sp<net::Request>>;
|
||||
|
||||
Sp<Node> node {}; /* the node info */
|
||||
|
||||
/* queries sent for finding out values hosted by the node */
|
||||
Sp<Query> probe_query {};
|
||||
/* queries substituting formal 'get' requests */
|
||||
std::map<Sp<Query>, std::vector<Sp<Query>>> pagination_queries {};
|
||||
|
||||
SyncStatus getStatus {}; /* get/sync 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. */
|
||||
|
||||
SearchNode() : node() {}
|
||||
SearchNode(const SearchNode&) = delete;
|
||||
SearchNode(SearchNode&&) = default;
|
||||
SearchNode& operator=(const SearchNode&) = delete;
|
||||
SearchNode& operator=(SearchNode&&) = default;
|
||||
|
||||
SearchNode(const Sp<Node>& node) : node(node) {}
|
||||
~SearchNode() {
|
||||
if (node) {
|
||||
cancelGet();
|
||||
cancelListen();
|
||||
cancelAnnounce();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Can we use this node to listen/announce now ?
|
||||
*/
|
||||
bool isSynced(time_point now) const {
|
||||
return not node->isExpired() and
|
||||
not token.empty() and last_get_reply >= now - Node::NODE_EXPIRE_TIME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Could a particular "get" request be sent to this node now ?
|
||||
*
|
||||
* A 'get' request can be sent when all of the following requirements are
|
||||
* met:
|
||||
*
|
||||
* - The node is not expired;
|
||||
* - The pagination process for this particular 'get' must not have begun;
|
||||
* - There hasn't been any response for a request, satisfying the initial
|
||||
* request, anytime following the initial request.
|
||||
* - No other request satisfying the request must be pending;
|
||||
*
|
||||
* @param now The time reference to now.
|
||||
* @param update The time of the last 'get' op satisfying this request.
|
||||
* @param q The query defining the "get" operation we're referring to.
|
||||
*
|
||||
* @return true if we can send get, else false.
|
||||
*/
|
||||
bool canGet(time_point now, time_point update, Sp<Query> q = {}) const {
|
||||
if (node->isExpired())
|
||||
return false;
|
||||
|
||||
bool pending {false},
|
||||
completed_sq_status {false},
|
||||
pending_sq_status {false};
|
||||
for (const auto& s : getStatus) {
|
||||
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() and not pending_sq_status)
|
||||
pending_sq_status = true;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return (not pending and now > last_get_reply + Node::NODE_EXPIRE_TIME) or
|
||||
not (hasStartedPagination(q) or completed_sq_status or pending_sq_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if we have started sending a 'get' request in paginated form.
|
||||
*
|
||||
* @param q The query as an id for a given 'get' request.
|
||||
*
|
||||
* @return true if pagination process has started, else false.
|
||||
*/
|
||||
bool hasStartedPagination(const Sp<Query>& q) const {
|
||||
const auto& pqs = pagination_queries.find(q);
|
||||
if (pqs == pagination_queries.cend() or pqs->second.empty())
|
||||
return false;
|
||||
return std::find_if(pqs->second.cbegin(), pqs->second.cend(),
|
||||
[this](const Sp<Query>& query) {
|
||||
const auto& req = getStatus.find(query);
|
||||
return req != getStatus.cend() and req->second;
|
||||
}) != pqs->second.cend();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Tell if the node has finished responding to a given 'get' request.
|
||||
*
|
||||
* A 'get' request can be divided in multiple requests called "pagination
|
||||
* requests". If this is the case, we have to check if they're all finished.
|
||||
* Otherwise, we only check for the single request.
|
||||
*
|
||||
* @param get The 'get' request data structure;
|
||||
*
|
||||
* @return true if it has finished, else false.
|
||||
*/
|
||||
bool isDone(const Get& get) const {
|
||||
if (hasStartedPagination(get.query)) {
|
||||
const auto& pqs = pagination_queries.find(get.query);
|
||||
auto paginationPending = std::find_if(pqs->second.cbegin(), pqs->second.cend(),
|
||||
[this](const Sp<Query>& query) {
|
||||
const auto& req = getStatus.find(query);
|
||||
return req != getStatus.cend() and req->second and req->second->pending();
|
||||
}) != pqs->second.cend();
|
||||
return not paginationPending;
|
||||
} else { /* no pagination yet */
|
||||
const auto& gs = get.query ? getStatus.find(get.query) : getStatus.cend();
|
||||
return gs != getStatus.end() and gs->second and not gs->second->pending();
|
||||
}
|
||||
}
|
||||
|
||||
void cancelGet() {
|
||||
for (const auto& status : getStatus) {
|
||||
if (status.second->pending()) {
|
||||
node->cancelRequest(status.second);
|
||||
}
|
||||
}
|
||||
getStatus.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if a request in the status map is expired.
|
||||
*
|
||||
* @param status A SyncStatus reference.
|
||||
*
|
||||
* @return true if there exists an expired request, else false.
|
||||
*/
|
||||
/*static bool expired(const SyncStatus& status) const {
|
||||
return std::find_if(status.begin(), status.end(),
|
||||
[](const SyncStatus::value_type& r){
|
||||
return r.second and r.second->expired();
|
||||
}) != status.end();
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Tells if a request in the status map is pending.
|
||||
*
|
||||
* @param status A SyncStatus reference.
|
||||
*
|
||||
* @return true if there exists an expired request, else false.
|
||||
*/
|
||||
static bool pending(const SyncStatus& status) {
|
||||
return std::find_if(status.begin(), status.end(),
|
||||
[](const SyncStatus::value_type& r){
|
||||
return r.second and r.second->pending();
|
||||
}) != status.end();
|
||||
}
|
||||
|
||||
bool pendingGet() const { return pending(getStatus); }
|
||||
|
||||
bool isAnnounced(Value::Id vid) const {
|
||||
auto ack = acked.find(vid);
|
||||
if (ack == acked.end() or not ack->second.first)
|
||||
return false;
|
||||
return ack->second.first->completed();
|
||||
}
|
||||
void cancelAnnounce() {
|
||||
for (const auto& status : acked) {
|
||||
const auto& req = status.second.first;
|
||||
if (req and req->pending()) {
|
||||
node->cancelRequest(req);
|
||||
}
|
||||
}
|
||||
acked.clear();
|
||||
}
|
||||
|
||||
bool isListening(time_point now) const {
|
||||
auto ls = listenStatus.begin();
|
||||
for ( ; ls != listenStatus.end() ; ++ls) {
|
||||
if (isListening(now, ls)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ls != listenStatus.end();
|
||||
}
|
||||
bool isListening(time_point now, const Sp<Query>& q) const {
|
||||
const auto& ls = listenStatus.find(q);
|
||||
if (ls == listenStatus.end())
|
||||
return false;
|
||||
else
|
||||
return isListening(now, ls);
|
||||
}
|
||||
bool isListening(time_point now, SyncStatus::const_iterator listen_status) const {
|
||||
if (listen_status == listenStatus.end())
|
||||
return false;
|
||||
return listen_status->second->reply_time + LISTEN_EXPIRE_TIME > now;
|
||||
}
|
||||
void cancelListen() {
|
||||
for (const auto& status : listenStatus)
|
||||
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);
|
||||
listenStatus.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
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();
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
return time_point::min();
|
||||
return listen_status->second->pending() ? time_point::max() :
|
||||
listen_status->second->reply_time + LISTEN_EXPIRE_TIME - REANNOUNCE_MARGIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this node expired or candidate
|
||||
*/
|
||||
bool isBad() const {
|
||||
return not node or node->isExpired() or candidate;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A search is a list of the nodes we think are responsible
|
||||
* for storing values for a given hash.
|
||||
*/
|
||||
struct Dht::Search {
|
||||
InfoHash id {};
|
||||
sa_family_t af;
|
||||
|
||||
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<Job> nextSearchStep {};
|
||||
|
||||
bool expired {false}; /* no node, or all nodes expired */
|
||||
bool done {false}; /* search is over, cached for later */
|
||||
std::vector<SearchNode> nodes {};
|
||||
|
||||
/* pending puts */
|
||||
std::vector<Announce> announce {};
|
||||
|
||||
/* pending gets */
|
||||
std::multimap<time_point, Get> callbacks {};
|
||||
|
||||
/* listeners */
|
||||
std::map<size_t, LocalListener> listeners {};
|
||||
size_t listener_token = 1;
|
||||
|
||||
~Search() {
|
||||
for (auto& get : callbacks) {
|
||||
get.second.done_cb(false, {});
|
||||
get.second.done_cb = {};
|
||||
}
|
||||
for (auto& put : announce) {
|
||||
put.callback(false, {});
|
||||
put.callback = {};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true if the node was not present and added to the search
|
||||
*/
|
||||
bool insertNode(const Sp<Node>& n, time_point now, const Blob& token={});
|
||||
|
||||
SearchNode* getNode(const Sp<Node>& n) {
|
||||
auto srn = std::find_if(nodes.begin(), nodes.end(), [&](SearchNode& sn) {
|
||||
return n == sn.node;
|
||||
});
|
||||
return (srn == nodes.end()) ? nullptr : &(*srn);
|
||||
}
|
||||
|
||||
/* number of concurrent sync requests */
|
||||
unsigned currentlySolicitedNodeCount() const {
|
||||
unsigned count = 0;
|
||||
for (const auto& n : nodes)
|
||||
if (not n.isBad() and n.pendingGet())
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can we use this search to announce ?
|
||||
*/
|
||||
bool isSynced(time_point now) const;
|
||||
|
||||
/**
|
||||
* Get the time of the last "get" operation performed on this search,
|
||||
* or time_point::min() if no such operation have been performed.
|
||||
*
|
||||
* @param query The query identifying a 'get' request.
|
||||
*/
|
||||
time_point getLastGetTime(Sp<Query> query = {}) const;
|
||||
|
||||
/**
|
||||
* Is this get operation done ?
|
||||
*/
|
||||
bool isDone(const Get& get) const;
|
||||
|
||||
/**
|
||||
* Sets a consistent state of the search after a given 'get' operation as
|
||||
* been completed.
|
||||
*
|
||||
* This will also make sure to call the associated 'done callback'.
|
||||
*
|
||||
* @param get The 'get' operation which is now over.
|
||||
*/
|
||||
void setDone(const Get& get) {
|
||||
for (auto& n : nodes) {
|
||||
auto pqs = n.pagination_queries.find(get.query);
|
||||
if (pqs != n.pagination_queries.cend()) {
|
||||
for (auto& pq : pqs->second)
|
||||
n.getStatus.erase(pq);
|
||||
}
|
||||
n.getStatus.erase(get.query);
|
||||
}
|
||||
if (get.done_cb)
|
||||
get.done_cb(true, getNodes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the search in a consistent state after the search is done. This is
|
||||
* the opportunity we have to clear some entries in the SearchNodes status
|
||||
* maps.
|
||||
*/
|
||||
void setDone() {
|
||||
for (auto& n : nodes) {
|
||||
n.getStatus.clear();
|
||||
n.listenStatus.clear();
|
||||
n.acked.clear();
|
||||
}
|
||||
done = true;
|
||||
}
|
||||
|
||||
time_point getUpdateTime(time_point now) const;
|
||||
|
||||
bool isAnnounced(Value::Id id) const;
|
||||
bool isListening(time_point now) const;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The number of non-good search nodes.
|
||||
*/
|
||||
unsigned getNumberOfBadNodes() const {
|
||||
return std::count_if(nodes.begin(), nodes.end(), [](const SearchNode& sn) {
|
||||
return sn.isBad();
|
||||
});
|
||||
}
|
||||
unsigned getNumberOfConsecutiveBadNodes() const {
|
||||
unsigned count = 0;
|
||||
std::find_if(nodes.begin(), nodes.end(), [&count](const SearchNode& sn) {
|
||||
if (not sn.isBad())
|
||||
return true;
|
||||
++count;
|
||||
return false;
|
||||
});
|
||||
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
|
||||
* from the end.
|
||||
*
|
||||
* @param now The reference to now.
|
||||
*
|
||||
* @return true if a node has been removed, else false.
|
||||
*/
|
||||
bool removeExpiredNode(const time_point& now) {
|
||||
for (auto e = nodes.cend(); e != nodes.cbegin();) {
|
||||
const Node& n = *(--e)->node;
|
||||
if (n.isRemovable(now)) {
|
||||
//std::cout << "Removing expired node " << n.id << " from IPv" << (af==AF_INET?'4':'6') << " search " << id << std::endl;
|
||||
nodes.erase(e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when we have discovered that the search is expired.
|
||||
* We have to
|
||||
*
|
||||
* - remove all nodes from the search;
|
||||
* - clear (non-permanent) callbacks;
|
||||
*/
|
||||
void expire() {
|
||||
// no nodes or all expired nodes. This is most likely a connectivity change event.
|
||||
expired = true;
|
||||
|
||||
nodes.clear();
|
||||
if (announce.empty() && listeners.empty())
|
||||
// Listening or announcing requires keeping the cluster up to date.
|
||||
setDone();
|
||||
{
|
||||
auto get_cbs = std::move(callbacks);
|
||||
for (const auto& g : get_cbs) {
|
||||
if (g.second.done_cb)
|
||||
g.second.done_cb(false, {});
|
||||
}
|
||||
}
|
||||
{
|
||||
std::vector<DoneCallback> a_cbs;
|
||||
a_cbs.reserve(announce.size());
|
||||
for (auto ait = announce.begin() ; ait != announce.end(); ) {
|
||||
if (ait->callback)
|
||||
a_cbs.emplace_back(std::move(ait->callback));
|
||||
if (not ait->permanent)
|
||||
ait = announce.erase(ait);
|
||||
else
|
||||
ait++;
|
||||
}
|
||||
for (const auto& a : a_cbs)
|
||||
a(false, {});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the value was just successfully announced, call the callback and erase it if not permanent.
|
||||
*
|
||||
* @param vid The id of the announced value.
|
||||
* @param types The sequence of existing types.
|
||||
* @param now The time reference to now.
|
||||
*/
|
||||
void checkAnnounced(Value::Id vid = Value::INVALID_ID) {
|
||||
auto announced = std::partition(announce.begin(), announce.end(),
|
||||
[this,&vid](Announce& a) {
|
||||
if (vid != Value::INVALID_ID and (!a.value || a.value->id != vid))
|
||||
return true;
|
||||
if (isAnnounced(a.value->id)) {
|
||||
if (a.callback) {
|
||||
a.callback(true, getNodes());
|
||||
a.callback = nullptr;
|
||||
}
|
||||
if (not a.permanent)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
// remove acked for cleared annouces
|
||||
for (auto it = announced; it != announce.end(); ++it) {
|
||||
for (auto& n : nodes)
|
||||
n.acked.erase(it->value->id);
|
||||
}
|
||||
announce.erase(announced, announce.end());
|
||||
}
|
||||
|
||||
std::vector<Sp<Node>> getNodes() const;
|
||||
|
||||
void clear() {
|
||||
announce.clear();
|
||||
callbacks.clear();
|
||||
listeners.clear();
|
||||
nodes.clear();
|
||||
nextSearchStep.reset();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* A search contains a list of nodes, sorted by decreasing distance to the
|
||||
target. We just got a new candidate, insert it at the right spot or
|
||||
discard it. */
|
||||
bool
|
||||
Dht::Search::insertNode(const Sp<Node>& snode, time_point now, const Blob& token)
|
||||
{
|
||||
auto& node = *snode;
|
||||
const auto& nid = node.id;
|
||||
|
||||
if (node.getFamily() != af)
|
||||
return false;
|
||||
|
||||
bool found = false;
|
||||
auto n = nodes.end();
|
||||
while (n != nodes.begin()) {
|
||||
--n;
|
||||
if (n->node == snode) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Node not found. We could insert it after this one. */
|
||||
if (id.xorCmp(nid, n->node->id) > 0) {
|
||||
++n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool new_search_node = false;
|
||||
if (not found) {
|
||||
// find if and where to trim excessive nodes
|
||||
auto t = nodes.cend();
|
||||
size_t bad = 0; // number of bad nodes (if search is not expired)
|
||||
bool full {false}; // is the search full (has the maximum nodes)
|
||||
if (expired) {
|
||||
// if the search is expired, trim to SEARCH_NODES nodes
|
||||
if (nodes.size() >= SEARCH_NODES) {
|
||||
full = true;
|
||||
t = nodes.begin() + SEARCH_NODES;
|
||||
}
|
||||
} else {
|
||||
// otherwise, trim to SEARCH_NODES nodes, not counting bad nodes
|
||||
bad = getNumberOfBadNodes();
|
||||
full = nodes.size() - bad >= SEARCH_NODES;
|
||||
while (std::distance(nodes.cbegin(), t) - bad > SEARCH_NODES) {
|
||||
--t;
|
||||
if (t->isBad())
|
||||
bad--;
|
||||
}
|
||||
}
|
||||
|
||||
if (full) {
|
||||
if (t != nodes.cend())
|
||||
nodes.resize(std::distance(nodes.cbegin(), t));
|
||||
if (n >= t)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reset search timer if the search is empty
|
||||
if (nodes.empty()) {
|
||||
step_time = time_point::min();
|
||||
}
|
||||
n = nodes.insert(n, SearchNode(snode));
|
||||
node.setTime(now);
|
||||
new_search_node = true;
|
||||
if (node.isExpired()) {
|
||||
if (not expired)
|
||||
bad++;
|
||||
} else if (expired) {
|
||||
bad = nodes.size() - 1;
|
||||
expired = false;
|
||||
}
|
||||
|
||||
while (nodes.size() - bad > SEARCH_NODES) {
|
||||
if (not expired and nodes.back().isBad())
|
||||
bad--;
|
||||
nodes.pop_back();
|
||||
}
|
||||
}
|
||||
if (not token.empty()) {
|
||||
n->candidate = false;
|
||||
n->last_get_reply = now;
|
||||
if (token.size() <= 64)
|
||||
n->token = token;
|
||||
expired = false;
|
||||
}
|
||||
if (new_search_node)
|
||||
removeExpiredNode(now);
|
||||
return new_search_node;
|
||||
}
|
||||
|
||||
std::vector<Sp<Node>>
|
||||
Dht::Search::getNodes() const
|
||||
{
|
||||
std::vector<Sp<Node>> ret {};
|
||||
ret.reserve(nodes.size());
|
||||
for (const auto& sn : nodes)
|
||||
ret.emplace_back(sn.node);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
Dht::Search::isSynced(time_point now) const
|
||||
{
|
||||
unsigned i = 0;
|
||||
for (const auto& n : nodes) {
|
||||
if (n.isBad())
|
||||
continue;
|
||||
if (not n.isSynced(now))
|
||||
return false;
|
||||
if (++i == TARGET_NODES)
|
||||
break;
|
||||
}
|
||||
return i > 0;
|
||||
}
|
||||
|
||||
time_point
|
||||
Dht::Search::getLastGetTime(Sp<Query> q) const
|
||||
{
|
||||
time_point last = time_point::min();
|
||||
for (const auto& g : callbacks)
|
||||
last = std::max(last, (not q or q->isSatisfiedBy(*g.second.query) ? g.second.start : time_point::min()));
|
||||
return last;
|
||||
}
|
||||
|
||||
bool
|
||||
Dht::Search::isDone(const Get& get) const
|
||||
{
|
||||
unsigned i = 0;
|
||||
for (const auto& sn : nodes) {
|
||||
if (sn.isBad())
|
||||
continue;
|
||||
if (not sn.isDone(get))
|
||||
return false;
|
||||
if (++i == TARGET_NODES)
|
||||
break;
|
||||
}
|
||||
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
|
||||
{
|
||||
if (nodes.empty())
|
||||
return false;
|
||||
unsigned i = 0;
|
||||
for (const auto& n : nodes) {
|
||||
if (n.isBad())
|
||||
continue;
|
||||
if (not n.isAnnounced(id))
|
||||
return false;
|
||||
if (++i == TARGET_NODES)
|
||||
return true;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
bool
|
||||
Dht::Search::isListening(time_point now) const
|
||||
{
|
||||
if (nodes.empty() or listeners.empty())
|
||||
return false;
|
||||
unsigned i = 0;
|
||||
for (const auto& n : nodes) {
|
||||
if (n.isBad())
|
||||
continue;
|
||||
SearchNode::SyncStatus::const_iterator ls {};
|
||||
for (ls = n.listenStatus.begin(); ls != n.listenStatus.end() ; ++ls) {
|
||||
if (n.isListening(now, ls))
|
||||
break;
|
||||
}
|
||||
if (ls == n.listenStatus.end())
|
||||
return false;
|
||||
if (++i == LISTEN_NODES)
|
||||
break;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+7
-7
@@ -40,10 +40,10 @@ Config& getConfig(SecureDht::Config& conf)
|
||||
return c;
|
||||
}
|
||||
|
||||
SecureDht::SecureDht(int s, int s6, SecureDht::Config conf)
|
||||
: Dht(s, s6, getConfig(conf)), key_(conf.id.first), certificate_(conf.id.second)
|
||||
SecureDht::SecureDht(uv_loop_t* loop, SecureDht::Config conf)
|
||||
: Dht(loop, getConfig(conf)), key_(conf.id.first), certificate_(conf.id.second)
|
||||
{
|
||||
if (s < 0 && s6 < 0)
|
||||
if (not loop)
|
||||
return;
|
||||
|
||||
for (const auto& type : DEFAULT_TYPES)
|
||||
@@ -76,7 +76,7 @@ SecureDht::~SecureDht()
|
||||
ValueType
|
||||
SecureDht::secureType(ValueType&& type)
|
||||
{
|
||||
type.storePolicy = [this,type](InfoHash id, Sp<Value>& v, InfoHash nid, const sockaddr* a, socklen_t al) {
|
||||
type.storePolicy = [this,type](InfoHash id, Sp<Value>& v, const InfoHash& nid, const SockAddr& a) {
|
||||
if (v->isSigned()) {
|
||||
if (!v->owner or !v->owner->checkSignature(v->getToSign(), v->signature)) {
|
||||
DHT_LOG.WARN("Signature verification failed");
|
||||
@@ -85,11 +85,11 @@ SecureDht::secureType(ValueType&& type)
|
||||
else
|
||||
DHT_LOG.WARN("Signature verification succeeded");
|
||||
}
|
||||
return type.storePolicy(id, v, nid, a, al);
|
||||
return type.storePolicy(id, v, nid, a);
|
||||
};
|
||||
type.editPolicy = [this,type](InfoHash id, const Sp<Value>& o, Sp<Value>& n, InfoHash nid, const sockaddr* a, socklen_t al) {
|
||||
type.editPolicy = [this,type](InfoHash id, const Sp<Value>& o, Sp<Value>& n, const InfoHash& nid, const SockAddr& a) {
|
||||
if (!o->isSigned())
|
||||
return type.editPolicy(id, o, n, nid, a, al);
|
||||
return type.editPolicy(id, o, n, nid, a);
|
||||
if (o->owner != n->owner) {
|
||||
DHT_LOG.WARN("Edition forbidden: owner changed.");
|
||||
return false;
|
||||
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* 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
|
||||
* 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 "value.h"
|
||||
#include "listener.h"
|
||||
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
namespace dht {
|
||||
|
||||
/**
|
||||
* Tracks storage usage per IP or IP range
|
||||
*/
|
||||
class StorageBucket {
|
||||
public:
|
||||
void insert(const InfoHash& id, const Value& value, time_point expiration) {
|
||||
totalSize_ += value.size();
|
||||
storedValues_.emplace(expiration, std::pair<InfoHash, Value::Id>(id, value.id));
|
||||
}
|
||||
void erase(const InfoHash& id, const Value& value, time_point expiration) {
|
||||
auto size = value.size();
|
||||
totalSize_ -= size;
|
||||
auto range = storedValues_.equal_range(expiration);
|
||||
for (auto rit = range.first; rit != range.second;) {
|
||||
if (rit->second.first == id && rit->second.second == value.id) {
|
||||
storedValues_.erase(rit);
|
||||
break;
|
||||
} else
|
||||
++rit;
|
||||
}
|
||||
}
|
||||
size_t size() const { return totalSize_; }
|
||||
std::pair<InfoHash, Value::Id> getOldest() const { return storedValues_.begin()->second; }
|
||||
private:
|
||||
std::multimap<time_point, std::pair<InfoHash, Value::Id>> storedValues_;
|
||||
size_t totalSize_ {0};
|
||||
};
|
||||
|
||||
struct ValueStorage {
|
||||
Sp<Value> data {};
|
||||
time_point created {};
|
||||
time_point expiration {};
|
||||
StorageBucket* store_bucket {nullptr};
|
||||
|
||||
ValueStorage() {}
|
||||
ValueStorage(const Sp<Value>& v, time_point t, time_point e)
|
||||
: data(v), created(t), expiration(e) {}
|
||||
};
|
||||
|
||||
|
||||
struct Storage {
|
||||
time_point maintenance_time {};
|
||||
std::map<Sp<Node>, std::map<size_t, Listener>> listeners;
|
||||
std::map<size_t, LocalListener> local_listeners {};
|
||||
size_t listener_token {1};
|
||||
|
||||
/* The maximum number of values we store for a given hash. */
|
||||
static constexpr unsigned MAX_VALUES {1024};
|
||||
|
||||
/**
|
||||
* Changes caused by an operation on the storage.
|
||||
*/
|
||||
struct StoreDiff {
|
||||
/** Difference in stored size caused by the op */
|
||||
ssize_t size_diff;
|
||||
/** Difference in number of values */
|
||||
ssize_t values_diff;
|
||||
/** Difference in number of listeners */
|
||||
ssize_t listeners_diff;
|
||||
};
|
||||
|
||||
Storage() {}
|
||||
Storage(time_point t) : maintenance_time(t) {}
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 || defined(_WIN32)
|
||||
// GCC-bug: remove me when support of GCC < 4.9.2 is abandoned
|
||||
Storage(Storage&& o) noexcept
|
||||
: maintenance_time(std::move(o.maintenance_time))
|
||||
, listeners(std::move(o.listeners))
|
||||
, local_listeners(std::move(o.local_listeners))
|
||||
, listener_token(std::move(o.listener_token))
|
||||
, values(std::move(o.values))
|
||||
, total_size(std::move(o.total_size)) {}
|
||||
#else
|
||||
Storage(Storage&& o) noexcept = default;
|
||||
#endif
|
||||
|
||||
Storage& operator=(Storage&& o) = default;
|
||||
|
||||
bool empty() const {
|
||||
return values.empty();
|
||||
}
|
||||
|
||||
StoreDiff clear();
|
||||
|
||||
size_t valueCount() const {
|
||||
return values.size();
|
||||
}
|
||||
|
||||
size_t totalSize() const {
|
||||
return total_size;
|
||||
}
|
||||
|
||||
const std::vector<ValueStorage>& getValues() const { return values; }
|
||||
|
||||
Sp<Value> getById(Value::Id vid) const {
|
||||
for (auto& v : values)
|
||||
if (v.data->id == vid) return v.data;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<Sp<Value>> get(Value::Filter f = {}) const {
|
||||
std::vector<Sp<Value>> newvals {};
|
||||
if (not f) newvals.reserve(values.size());
|
||||
for (auto& v : values) {
|
||||
if (not f || f(*v.data))
|
||||
newvals.push_back(v.data);
|
||||
}
|
||||
return newvals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a new value in this storage, or replace a previous value
|
||||
*
|
||||
* @return <storage, change_size, change_value_num>
|
||||
* storage: set if a change happened
|
||||
* change_size: size difference
|
||||
* change_value_num: change of value number (0 or 1)
|
||||
*/
|
||||
std::pair<ValueStorage*, StoreDiff>
|
||||
store(const InfoHash& id, const Sp<Value>&, time_point created, time_point expiration, StorageBucket*);
|
||||
|
||||
/**
|
||||
* Refreshes the time point of the value's lifetime begining.
|
||||
*
|
||||
* @param now The reference to now
|
||||
* @param vid The value id
|
||||
* @return true if a value storage was updated, false otherwise
|
||||
*/
|
||||
bool refresh(const time_point& now, const Value::Id& vid) {
|
||||
for (auto& vs : values)
|
||||
if (vs.data->id == vid) {
|
||||
vs.created = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
StoreDiff remove(const InfoHash& id, Value::Id);
|
||||
|
||||
StoreDiff expire(const InfoHash& id, time_point now);
|
||||
|
||||
private:
|
||||
Storage(const Storage&) = delete;
|
||||
Storage& operator=(const Storage&) = delete;
|
||||
|
||||
std::vector<ValueStorage> values {};
|
||||
size_t total_size {};
|
||||
};
|
||||
|
||||
|
||||
std::pair<ValueStorage*, Storage::StoreDiff>
|
||||
Storage::store(const InfoHash& id, const Sp<Value>& value, time_point created, time_point expiration, StorageBucket* sb)
|
||||
{
|
||||
auto it = std::find_if (values.begin(), values.end(), [&](const ValueStorage& vr) {
|
||||
return vr.data == value || vr.data->id == value->id;
|
||||
});
|
||||
ssize_t size_new = value->size();
|
||||
if (it != values.end()) {
|
||||
/* Already there, only need to refresh */
|
||||
it->created = created;
|
||||
size_t size_old = it->data->size();
|
||||
ssize_t size_diff = size_new - (ssize_t)size_old;
|
||||
if (it->data != value) {
|
||||
//DHT_LOG.DEBUG("Updating %s -> %s", id.toString().c_str(), value->toString().c_str());
|
||||
// clear quota for previous value
|
||||
if (it->store_bucket)
|
||||
it->store_bucket->erase(id, *value, it->expiration);
|
||||
it->expiration = expiration;
|
||||
// update quota for new value
|
||||
it->store_bucket = sb;
|
||||
if (sb)
|
||||
sb->insert(id, *value, expiration);
|
||||
it->data = value;
|
||||
total_size += size_diff;
|
||||
return std::make_pair(&(*it), StoreDiff{size_diff, 0, 0});
|
||||
}
|
||||
return std::make_pair(nullptr, StoreDiff{});
|
||||
} else {
|
||||
//DHT_LOG.DEBUG("Storing %s -> %s", id.toString().c_str(), value->toString().c_str());
|
||||
if (values.size() < MAX_VALUES) {
|
||||
total_size += size_new;
|
||||
values.emplace_back(value, created, expiration);
|
||||
values.back().store_bucket = sb;
|
||||
if (sb)
|
||||
sb->insert(id, *value, expiration);
|
||||
return std::make_pair(&values.back(), StoreDiff{size_new, 1, 0});
|
||||
}
|
||||
return std::make_pair(nullptr, StoreDiff{});
|
||||
}
|
||||
}
|
||||
|
||||
Storage::StoreDiff
|
||||
Storage::remove(const InfoHash& id, Value::Id vid)
|
||||
{
|
||||
auto it = std::find_if (values.begin(), values.end(), [&](const ValueStorage& vr) {
|
||||
return vr.data->id == vid;
|
||||
});
|
||||
if (it == values.end())
|
||||
return {};
|
||||
ssize_t size = it->data->size();
|
||||
if (it->store_bucket)
|
||||
it->store_bucket->erase(id, *it->data, it->expiration);
|
||||
total_size -= size;
|
||||
values.erase(it);
|
||||
return {-size, -1, 0};
|
||||
}
|
||||
|
||||
Storage::StoreDiff
|
||||
Storage::clear()
|
||||
{
|
||||
ssize_t num_values = values.size();
|
||||
ssize_t tot_size = total_size;
|
||||
values.clear();
|
||||
total_size = 0;
|
||||
return {-tot_size, -num_values, 0};
|
||||
}
|
||||
|
||||
Storage::StoreDiff
|
||||
Storage::expire(const InfoHash& id, time_point now)
|
||||
{
|
||||
// expire listeners
|
||||
ssize_t del_listen {0};
|
||||
for (auto nl_it = listeners.begin(); nl_it != listeners.end();) {
|
||||
auto& node_listeners = nl_it->second;
|
||||
for (auto l = node_listeners.cbegin(); l != node_listeners.cend();) {
|
||||
bool expired = l->second.time + Node::NODE_EXPIRE_TIME < now;
|
||||
if (expired)
|
||||
l = node_listeners.erase(l);
|
||||
else
|
||||
++l;
|
||||
}
|
||||
if (node_listeners.empty()) {
|
||||
nl_it = listeners.erase(nl_it);
|
||||
del_listen--;
|
||||
}
|
||||
else
|
||||
++nl_it;
|
||||
}
|
||||
|
||||
// expire values
|
||||
auto r = std::partition(values.begin(), values.end(), [&](const ValueStorage& v) {
|
||||
return v.expiration > now;
|
||||
});
|
||||
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);
|
||||
});
|
||||
total_size += size_diff;
|
||||
values.erase(r, values.end());
|
||||
return {size_diff, del_num, del_listen};
|
||||
}
|
||||
|
||||
}
|
||||
+70
-6
@@ -19,12 +19,16 @@
|
||||
#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 */
|
||||
|
||||
static constexpr std::array<uint8_t, 12> MAPPED_IPV4_PREFIX {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}};
|
||||
|
||||
namespace dht {
|
||||
|
||||
std::string
|
||||
@@ -38,7 +42,7 @@ print_addr(const sockaddr* sa, socklen_t slen)
|
||||
out << "[" << hbuf << "]";
|
||||
else
|
||||
out << hbuf;
|
||||
if (strcmp(sbuf, "0"))
|
||||
if (std::strcmp(sbuf, "0"))
|
||||
out << ":" << sbuf;
|
||||
} else
|
||||
out << "[invalid address]";
|
||||
@@ -51,11 +55,6 @@ print_addr(const sockaddr_storage& ss, socklen_t sslen)
|
||||
return print_addr((const sockaddr*)&ss, sslen);
|
||||
}
|
||||
|
||||
std::string
|
||||
printAddr(const SockAddr& addr) {
|
||||
return print_addr((const sockaddr*)&addr.first, addr.second);
|
||||
}
|
||||
|
||||
bool
|
||||
SockAddr::isUnspecified() const
|
||||
{
|
||||
@@ -116,6 +115,57 @@ SockAddr::isPrivate() const
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SockAddr::isMappedIPv4() const
|
||||
{
|
||||
if (getFamily() != AF_INET6)
|
||||
return false;
|
||||
const uint8_t* addr6 = reinterpret_cast<const uint8_t*>(&getIPv6().sin6_addr);
|
||||
return std::equal(MAPPED_IPV4_PREFIX.begin(), MAPPED_IPV4_PREFIX.end(), addr6);
|
||||
}
|
||||
|
||||
SockAddr
|
||||
SockAddr::getMappedIPv4() const
|
||||
{
|
||||
if (getFamily() != AF_INET6)
|
||||
return *this;
|
||||
SockAddr ret;
|
||||
ret.setFamily(AF_INET);
|
||||
ret.setPort(getPort());
|
||||
auto addr6 = reinterpret_cast<const uint8_t*>(&getIPv6().sin6_addr);
|
||||
auto addr4 = reinterpret_cast<uint8_t*>(&ret.getIPv4().sin_addr);
|
||||
addr6 += MAPPED_IPV4_PREFIX.size();
|
||||
std::copy_n(addr6, sizeof(in_addr), addr4);
|
||||
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);
|
||||
}
|
||||
@@ -165,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>>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+17
-6
@@ -26,8 +26,16 @@ namespace dht {
|
||||
|
||||
const std::string Query::QUERY_PARSE_ERROR {"Error parsing query."};
|
||||
|
||||
Value::Filter bindFilterRaw(FilterRaw raw_filter, void* user_data) {
|
||||
if (not raw_filter) return {};
|
||||
return [=](const Value& value) {
|
||||
return raw_filter(value, user_data);
|
||||
};
|
||||
}
|
||||
|
||||
std::ostream& operator<< (std::ostream& s, const Value& v)
|
||||
{
|
||||
auto flags(s.flags());
|
||||
s << "Value[id:" << std::hex << v.id << std::dec << " ";
|
||||
if (v.isEncrypted())
|
||||
s << "encrypted ";
|
||||
@@ -56,13 +64,14 @@ std::ostream& operator<< (std::ostream& s, const Value& v)
|
||||
}
|
||||
}
|
||||
s << "]";
|
||||
s.flags(flags);
|
||||
return s;
|
||||
}
|
||||
|
||||
const ValueType ValueType::USER_DATA = {0, "User Data"};
|
||||
|
||||
bool
|
||||
ValueType::DEFAULT_STORE_POLICY(InfoHash, std::shared_ptr<Value>& v, InfoHash, const sockaddr*, socklen_t)
|
||||
ValueType::DEFAULT_STORE_POLICY(InfoHash, std::shared_ptr<Value>& v, const InfoHash&, const SockAddr&)
|
||||
{
|
||||
return v->size() <= MAX_VALUE_SIZE;
|
||||
}
|
||||
@@ -250,9 +259,12 @@ std::ostream& operator<<(std::ostream& os, const FieldValueIndex& fvi) {
|
||||
os << "Index[";
|
||||
for (auto v = fvi.index.begin(); v != fvi.index.end(); ++v) {
|
||||
switch (v->first) {
|
||||
case Value::Field::Id:
|
||||
case Value::Field::Id: {
|
||||
auto flags(os.flags());
|
||||
os << "Id:" << std::hex << v->second.getInt();
|
||||
os.flags(flags);
|
||||
break;
|
||||
}
|
||||
case Value::Field::ValueType:
|
||||
os << "ValueType:" << v->second.getInt();
|
||||
break;
|
||||
@@ -353,10 +365,10 @@ Where::Where(const std::string& q_str) {
|
||||
std::string s {};
|
||||
std::istringstream convert {value_str};
|
||||
convert >> v;
|
||||
if (convert.failbit
|
||||
if (not convert
|
||||
and value_str.size() > 1
|
||||
and value_str[0] == '\"'
|
||||
and value_str[value_str.size()-1] == '\"')
|
||||
and value_str[0] == '"'
|
||||
and value_str[value_str.size()-1] == '"')
|
||||
s = value_str.substr(1, value_str.size()-2);
|
||||
else
|
||||
s = value_str;
|
||||
@@ -483,4 +495,3 @@ std::ostream& operator<<(std::ostream& s, const dht::Where& where) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
+24
-4
@@ -17,8 +17,28 @@ else ()
|
||||
target_link_libraries (dhtchat LINK_PUBLIC opendht-static)
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED CMAKE_INSTALL_BINDIR)
|
||||
set(CMAKE_INSTALL_BINDIR bin)
|
||||
endif ()
|
||||
|
||||
install (TARGETS dhtnode dhtscanner dhtchat RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
if (OPENDHT_SYSTEMD)
|
||||
execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} systemd --variable=systemdsystemunitdir
|
||||
OUTPUT_VARIABLE SYSTEMD_UNIT_INSTALL_DIR)
|
||||
string(REGEX REPLACE "[ \t\n]+" "" SYSTEMD_UNIT_INSTALL_DIR "${SYSTEMD_UNIT_INSTALL_DIR}")
|
||||
set (systemdunitdir "${SYSTEMD_UNIT_INSTALL_DIR}")
|
||||
|
||||
configure_file (
|
||||
systemd/dhtnode.service.in
|
||||
systemd/dhtnode.service
|
||||
@ONLY
|
||||
)
|
||||
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/systemd/dhtnode.service DESTINATION ${systemdunitdir})
|
||||
install (FILES systemd/dhtnode.conf DESTINATION ${sysconfdir})
|
||||
if (OPENDHT_PYTHON)
|
||||
configure_file (
|
||||
systemd/dhtcluster.service.in
|
||||
systemd/dhtcluster.service
|
||||
@ONLY
|
||||
)
|
||||
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/systemd/dhtcluster.service DESTINATION ${systemdunitdir})
|
||||
install (FILES systemd/dhtcluster.conf DESTINATION ${sysconfdir})
|
||||
endif()
|
||||
endif ()
|
||||
+13
-7
@@ -39,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;
|
||||
}
|
||||
@@ -359,18 +359,25 @@ main(int argc, char **argv)
|
||||
}
|
||||
if (params.daemonize) {
|
||||
daemonize();
|
||||
} else if (params.service) {
|
||||
setupSignals();
|
||||
}
|
||||
|
||||
dht::crypto::Identity crt {};
|
||||
if (params.generate_identity) {
|
||||
auto ca_tmp = dht::crypto::generateIdentity("DHT Node CA");
|
||||
crt = dht::crypto::generateIdentity("DHT Node", ca_tmp);
|
||||
auto ca_tmp = dht::crypto::generateEcIdentity("DHT Node CA");
|
||||
crt = dht::crypto::generateEcIdentity("DHT Node", ca_tmp);
|
||||
}
|
||||
|
||||
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 (not params.logfile.empty())
|
||||
if (params.syslog or (params.daemonize and params.logfile.empty()))
|
||||
log::enableSyslog(*dht, "dhtnode");
|
||||
else if (not params.logfile.empty())
|
||||
log::enableFileLogging(*dht, params.logfile);
|
||||
else
|
||||
log::enableLogging(*dht);
|
||||
@@ -381,9 +388,8 @@ main(int argc, char **argv)
|
||||
dht->bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str());
|
||||
}
|
||||
|
||||
if (params.daemonize) {
|
||||
while (true)
|
||||
std::this_thread::sleep_for(std::chrono::seconds(30));
|
||||
if (params.daemonize or params.service) {
|
||||
while (runner.wait());
|
||||
} else {
|
||||
cmd_loop(dht, params);
|
||||
}
|
||||
|
||||
+11
-2
@@ -56,8 +56,8 @@ step(DhtRunner& dht, std::atomic_uint& done, std::shared_ptr<NodeSet> all_nodes,
|
||||
all_nodes->insert(nodes.begin(), nodes.end());
|
||||
NodeSet sbuck {nodes.begin(), nodes.end()};
|
||||
if (not sbuck.empty()) {
|
||||
unsigned bdepth = sbuck.size()==1 ? 0u : InfoHash::commonBits((*sbuck.begin())->id, (*std::prev(sbuck.end()))->id);
|
||||
unsigned target_depth = std::min(159u, bdepth+3u);
|
||||
unsigned bdepth = sbuck.size()==1 ? 0u : InfoHash::commonBits((*sbuck.begin())->id, (*sbuck.rbegin())->id);
|
||||
unsigned target_depth = std::min(8u, bdepth+6u);
|
||||
std::cout << cur_h << " : " << nodes.size() << " nodes; target is " << target_depth << " bits deep (cur " << cur_depth << ")" << std::endl;
|
||||
for (unsigned b = cur_depth ; b < target_depth; b++) {
|
||||
auto new_h = cur_h;
|
||||
@@ -87,6 +87,15 @@ main(int argc, char **argv)
|
||||
try {
|
||||
dht.run(params.port, {}, true, params.network);
|
||||
|
||||
if (params.log) {
|
||||
if (params.syslog)
|
||||
log::enableSyslog(dht, "dhtnode");
|
||||
else if (not params.logfile.empty())
|
||||
log::enableFileLogging(dht, params.logfile);
|
||||
else
|
||||
log::enableLogging(dht);
|
||||
}
|
||||
|
||||
if (not params.bootstrap.first.empty())
|
||||
dht.bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str());
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
DHT_ARGS=-b bootstrap.ring.cx -p 4222 -n 16
|
||||
@@ -0,0 +1,20 @@
|
||||
[Unit]
|
||||
Description=OpenDHT node cluster
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
EnvironmentFile=@sysconfdir@/dhtcluster.conf
|
||||
ExecStart=@bindir@/dhtcluster -s $DHT_ARGS
|
||||
KillMode=process
|
||||
Restart=on-failure
|
||||
Type=simple
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectControlGroups=yes
|
||||
PrivateDevices=yes
|
||||
PrivateUsers=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1 @@
|
||||
DHT_ARGS=-b bootstrap.ring.cx -p 4222 -v
|
||||
@@ -0,0 +1,14 @@
|
||||
[Unit]
|
||||
Description=OpenDHT standalone node
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
EnvironmentFile=@sysconfdir@/dhtnode.conf
|
||||
ExecStart=@bindir@/dhtnode -d $DHT_ARGS
|
||||
KillMode=process
|
||||
Restart=on-failure
|
||||
Type=forking
|
||||
DynamicUser=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
+51
-11
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
// Common utility methods used by C++ OpenDHT tools.
|
||||
#pragma once
|
||||
|
||||
#include <opendht.h>
|
||||
#ifndef WIN32_NATIVE
|
||||
@@ -37,6 +38,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
@@ -111,10 +114,12 @@ struct dht_params {
|
||||
bool help {false}; // print help and exit
|
||||
bool log {false};
|
||||
std::string logfile {};
|
||||
bool syslog {false};
|
||||
in_port_t port {0};
|
||||
dht::NetId network {0};
|
||||
bool generate_identity {false};
|
||||
bool daemonize {false};
|
||||
bool service {false};
|
||||
std::pair<std::string, std::string> bootstrap {};
|
||||
};
|
||||
|
||||
@@ -126,7 +131,9 @@ static const constexpr struct option long_options[] = {
|
||||
{"identity", no_argument , nullptr, 'i'},
|
||||
{"verbose", no_argument , nullptr, 'v'},
|
||||
{"daemonize", no_argument , nullptr, 'd'},
|
||||
{"service", no_argument , nullptr, 's'},
|
||||
{"logfile", required_argument, nullptr, 'l'},
|
||||
{"syslog", no_argument , nullptr, 'L'},
|
||||
{nullptr, 0 , nullptr, 0}
|
||||
};
|
||||
|
||||
@@ -134,7 +141,7 @@ dht_params
|
||||
parseArgs(int argc, char **argv) {
|
||||
dht_params params;
|
||||
int opt;
|
||||
while ((opt = getopt_long(argc, argv, "hidvp:n:b:l:", long_options, nullptr)) != -1) {
|
||||
while ((opt = getopt_long(argc, argv, "hidsvp:n:b:l:", long_options, nullptr)) != -1) {
|
||||
switch (opt) {
|
||||
case 'p': {
|
||||
int port_arg = atoi(optarg);
|
||||
@@ -150,9 +157,7 @@ parseArgs(int argc, char **argv) {
|
||||
case 'b':
|
||||
params.bootstrap = splitPort((optarg[0] == '=') ? optarg+1 : optarg);
|
||||
if (not params.bootstrap.first.empty() and params.bootstrap.second.empty()) {
|
||||
std::stringstream ss;
|
||||
ss << DHT_DEFAULT_PORT;
|
||||
params.bootstrap.second = ss.str();
|
||||
params.bootstrap.second = std::to_string(DHT_DEFAULT_PORT);
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
@@ -161,6 +166,10 @@ parseArgs(int argc, char **argv) {
|
||||
case 'l':
|
||||
params.logfile = optarg;
|
||||
break;
|
||||
case 'L':
|
||||
params.log = true;
|
||||
params.syslog = true;
|
||||
break;
|
||||
case 'v':
|
||||
params.log = true;
|
||||
break;
|
||||
@@ -170,6 +179,9 @@ parseArgs(int argc, char **argv) {
|
||||
case 'd':
|
||||
params.daemonize = true;
|
||||
break;
|
||||
case 's':
|
||||
params.service = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -195,17 +207,50 @@ readLine(const char* prefix = PROMPT)
|
||||
return line_read ? std::string(line_read) : std::string("\0", 1);
|
||||
}
|
||||
|
||||
struct ServiceRunner {
|
||||
bool wait() {
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
cv.wait(lock, [&]{return terminate;});
|
||||
return !terminate;
|
||||
}
|
||||
void kill() {
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
terminate = true;
|
||||
cv.notify_all();
|
||||
}
|
||||
private:
|
||||
std::condition_variable cv;
|
||||
std::mutex m;
|
||||
bool terminate = false;
|
||||
};
|
||||
|
||||
ServiceRunner runner;
|
||||
|
||||
void signal_handler(int sig)
|
||||
{
|
||||
switch(sig) {
|
||||
case SIGHUP:
|
||||
break;
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
exit(EXIT_SUCCESS);
|
||||
runner.kill();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void setupSignals()
|
||||
{
|
||||
#ifndef WIN32_NATIVE
|
||||
signal(SIGCHLD,SIG_IGN); /* ignore child */
|
||||
signal(SIGTSTP,SIG_IGN); /* ignore tty signals */
|
||||
signal(SIGTTOU,SIG_IGN);
|
||||
signal(SIGTTIN,SIG_IGN);
|
||||
signal(SIGHUP,signal_handler); /* catch hangup signal */
|
||||
signal(SIGINT,signal_handler); /* catch interrupt signal */
|
||||
signal(SIGTERM,signal_handler); /* catch kill signal */
|
||||
#endif
|
||||
}
|
||||
|
||||
void daemonize()
|
||||
{
|
||||
#ifndef WIN32_NATIVE
|
||||
@@ -228,11 +273,6 @@ void daemonize()
|
||||
close(STDOUT_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
|
||||
signal(SIGCHLD,SIG_IGN); /* ignore child */
|
||||
signal(SIGTSTP,SIG_IGN); /* ignore tty signals */
|
||||
signal(SIGTTOU,SIG_IGN);
|
||||
signal(SIGTTIN,SIG_IGN);
|
||||
signal(SIGHUP,signal_handler); /* catch hangup signal */
|
||||
signal(SIGTERM,signal_handler); /* catch kill signal */
|
||||
setupSignals();
|
||||
#endif
|
||||
}
|
||||
Reference in new issue
Block a user