mirror of
https://github.com/savoirfairelinux/opendht.git
synced 2026-11-08 20:37:23 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a222e30428 | ||
|
|
bd822fce19 | ||
|
|
cabe9dbeb5 | ||
|
|
99b7a07350 | ||
|
|
756e0bd3d8 | ||
|
|
9b0ce12b5f | ||
|
|
e29c2df002 | ||
|
|
b03a8b5b50 | ||
|
|
32f5175d68 | ||
|
|
7d3a54db57 | ||
|
|
3fb7c2acfe | ||
|
|
bce5d35b51 | ||
|
|
b6dca5ccd6 | ||
|
|
23b2712400 | ||
|
|
63bec73885 | ||
|
|
af5e0c8982 | ||
|
|
5a7c8753e1 | ||
|
|
19fdec96ca | ||
|
|
2208a06232 | ||
|
|
2cfcaf3909 | ||
|
|
620868b5d6 | ||
|
|
e7295bac7b | ||
|
|
38aa86de2a | ||
|
|
fdf476a3f3 | ||
|
|
22f27a6cc0 | ||
|
|
c72ea8ce77 | ||
|
|
ea168353b9 | ||
|
|
79b9c4838e | ||
|
|
d6e86ebcc9 | ||
|
|
197aaa47f0 | ||
|
|
ad234004aa | ||
|
|
a88e6a4c5f | ||
|
|
c776dad96f | ||
|
|
31fedd529b | ||
|
|
e9adff9dc5 | ||
|
|
bfd58a204b | ||
|
|
867bcf1514 | ||
|
|
fb882ee440 | ||
|
|
215d3d2847 | ||
|
|
43a345ba0c | ||
|
|
f73f5e2798 | ||
|
|
3724670fcc | ||
|
|
e7db64bd74 | ||
|
|
9c29b8a29e | ||
|
|
c22f0e4d43 | ||
|
|
4e2b11c24c | ||
|
|
8c34e927ea | ||
|
|
8a5de47aa0 | ||
|
|
7f84c1e397 | ||
|
|
65c915fd9d | ||
|
|
0fd4f73ab1 | ||
|
|
01117567b1 | ||
|
|
d68cc5cf40 | ||
|
|
6db16be2fa | ||
|
|
5166b1add9 | ||
|
|
9fadcc907c | ||
|
|
42171efc3f | ||
|
|
206f12648f | ||
|
|
ecd0573c4d | ||
|
|
2d9f6241a9 | ||
|
|
77ee50f5a6 | ||
|
|
297c37e609 | ||
|
|
9bb1ab43eb | ||
|
|
cd36b13c30 | ||
|
|
5d63084627 | ||
|
|
ab7d1d6582 | ||
|
|
77ea6d8c2f | ||
|
|
b5cd629160 | ||
|
|
39467c8b71 | ||
|
|
87740e3b38 | ||
|
|
19848a9b72 | ||
|
|
36e9998fe2 | ||
|
|
b2529c241e | ||
|
|
83c47037d2 | ||
|
|
125b6f9236 | ||
|
|
e260ba6b78 | ||
|
|
eafb934e85 | ||
|
|
c3d3da20f6 | ||
|
|
6036ccdbaa | ||
|
|
9404a4d069 | ||
|
|
274621dcec | ||
|
|
2ab756906d | ||
|
|
2b3f63443f | ||
|
|
c71f960948 | ||
|
|
9d3514e087 | ||
|
|
6703e039b1 | ||
|
|
29b81750ad | ||
|
|
d6fff53f6b | ||
|
|
7ee6281426 | ||
|
|
28a5740c37 | ||
|
|
09a3332cf0 | ||
|
|
f584c4849b | ||
|
|
e3663c995a | ||
|
|
1d62b3ca64 | ||
|
|
3af04a4259 | ||
|
|
78d98822a5 | ||
|
|
8c04ddc66b | ||
|
|
5277b8d23e | ||
|
|
86fea49178 | ||
|
|
5b1242f786 | ||
|
|
a8cfdbe762 | ||
|
|
70b1ebae0b | ||
|
|
9dc6004329 | ||
|
|
d4bd17a2dc | ||
|
|
6c73e5fa5c | ||
|
|
24edfb8eaf | ||
|
|
4ef5a2216e | ||
|
|
0d646eb244 | ||
|
|
757fea37f3 | ||
|
|
09e41cc9f2 | ||
|
|
71ad92e286 | ||
|
|
d4f3d3aa98 | ||
|
|
c0612a1b2c | ||
|
|
6743cbbf50 | ||
|
|
da1bdf9d4c | ||
|
|
96da4cd85d | ||
|
|
f233340cb2 | ||
|
|
cfb817d37f | ||
|
|
9440c03048 | ||
|
|
aa611bde50 | ||
|
|
7af856508e | ||
|
|
aaed64039a | ||
|
|
7c5e8c3234 | ||
|
|
a56bf3f115 | ||
|
|
2d2fa9cfa9 | ||
|
|
9f11930f3d | ||
|
|
013b1dfe2e | ||
|
|
733f699ff2 | ||
|
|
a366d502a2 | ||
|
|
eed8ffc792 | ||
|
|
998547e1a1 | ||
|
|
8567b7ad84 | ||
|
|
64118404fe | ||
|
|
bb74dc846c | ||
|
|
659d893dee | ||
|
|
e3f371c2d8 | ||
|
|
f53c7a6785 | ||
|
|
261c193b50 | ||
|
|
dc0b650aa3 | ||
|
|
87cf6faedd | ||
|
|
ca6aa6706d | ||
|
|
16e55416a6 | ||
|
|
3e24058175 | ||
|
|
d1c2b20b28 | ||
|
|
ccd0cbd8f2 | ||
|
|
e5213674e3 | ||
|
|
b9cc848810 | ||
|
|
65bdfaf22b | ||
|
|
de31d836b4 | ||
|
|
25b339956e | ||
|
|
23bb6e7a45 | ||
|
|
b44f7ec653 | ||
|
|
6d38c18572 | ||
|
|
8eaf78bfe2 | ||
|
|
dc836b38a6 | ||
|
|
b01f8d079b | ||
|
|
6f7ac21a85 | ||
|
|
7c5db84f33 | ||
|
|
4fecb2066b | ||
|
|
eabdddcfb3 | ||
|
|
ceab27d723 | ||
|
|
a9cc669a95 | ||
|
|
927c835b8f | ||
|
|
5415eaaf60 | ||
|
|
ec587f9b58 | ||
|
|
babfed73f3 | ||
|
|
2c0ce576e3 | ||
|
|
8f2cc937c5 | ||
|
|
c9f6567128 | ||
|
|
d60672e8ba | ||
|
|
fd4cce4883 | ||
|
|
df15d3d48e | ||
|
|
76fd216386 | ||
|
|
48735aad94 | ||
|
|
91a1471de5 | ||
|
|
047ba76ff1 | ||
|
|
e57eb63c87 | ||
|
|
8461f4018d | ||
|
|
c33c2e7e95 | ||
|
|
e5a9a2af4c | ||
|
|
7919ca8de1 | ||
|
|
00ef660952 | ||
|
|
862a03713b | ||
|
|
89d0d2f109 | ||
|
|
3283e34dcf | ||
|
|
34c6b264df | ||
|
|
2d8fe502f2 | ||
|
|
73df308250 | ||
|
|
cdca40cb59 | ||
|
|
2a3c301314 | ||
|
|
6f4923da2e | ||
|
|
ae26a2b39a | ||
|
|
48c40c8a27 | ||
|
|
a4a4eee495 | ||
|
|
d185428d1d | ||
|
|
6855aaa6f4 | ||
|
|
2aee4f418f | ||
|
|
95aef01a59 | ||
|
|
d2ccde4859 | ||
|
|
7ab64ea0e6 | ||
|
|
fe1b8b88e1 | ||
|
|
b6832869b1 | ||
|
|
6da8d39a0e | ||
|
|
f8cf1316ad | ||
|
|
736606a845 | ||
|
|
cba240e15e | ||
|
|
eb90fffb5e | ||
|
|
041e280bde | ||
|
|
33fae1cb66 | ||
|
|
4e8408e225 | ||
|
|
e85a423f6a | ||
|
|
532cb40665 | ||
|
|
dd58b0497e | ||
|
|
cc070c80ba | ||
|
|
e332dae576 | ||
|
|
727fd4e98b | ||
|
|
5a5f7012d8 | ||
|
|
5a239cddf7 | ||
|
|
c4928553ba | ||
|
|
48b0cff08e | ||
|
|
c8fcffbe6b | ||
|
|
63c4eeb9c9 | ||
|
|
d45f777099 | ||
|
|
9c4035dd8f | ||
|
|
acc2b71c86 | ||
|
|
09d849c93d | ||
|
|
1be1ad88c6 | ||
|
|
8ecaf25339 | ||
|
|
7a6dce4727 | ||
|
|
b357ffb0e6 | ||
|
|
517bd5f22b | ||
|
|
d1d0a67d35 | ||
|
|
85a9dbafc6 | ||
|
|
79390f72fa | ||
|
|
b7a37dd771 | ||
|
|
b24d1ccc8b | ||
|
|
4f53e10e7e | ||
|
|
a608ad3cf6 | ||
|
|
a135f81073 | ||
|
|
2da48557ff | ||
|
|
73686debaf | ||
|
|
97950621cd | ||
|
|
b3401828e4 | ||
|
|
0a7d4a68b3 | ||
|
|
8d7deffa6a | ||
|
|
669c08210e | ||
|
|
27a3231a55 | ||
|
|
86ce695939 | ||
|
|
2759a136df | ||
|
|
690682408b | ||
|
|
bfdb8a24e9 | ||
|
|
e9bf042ac5 | ||
|
|
5aab3078c8 | ||
|
|
1ac5840bb1 | ||
|
|
426b3d6834 | ||
|
|
97b573c182 | ||
|
|
5c41cbb964 | ||
|
|
e192e3beaf | ||
|
|
5dacb50d63 | ||
|
|
86d837b5a9 | ||
|
|
6454a6dc3f | ||
|
|
bbd1200ad7 | ||
|
|
aa59242843 | ||
|
|
c04ede4909 | ||
|
|
a1eaf2e83c | ||
|
|
a21d4d4adc | ||
|
|
f0a92d999c | ||
|
|
28a3498a69 | ||
|
|
7eb5d892f1 | ||
|
|
f6cd79952c | ||
|
|
9c1a0ae27f | ||
|
|
29c1a955d0 | ||
|
|
b366432a8a | ||
|
|
af8bfdc1c1 | ||
|
|
88040ad467 | ||
|
|
a35e67bcdf | ||
|
|
df3317a5b2 | ||
|
|
33f95c00a9 | ||
|
|
7383505dcc | ||
|
|
2c9a29af0e | ||
|
|
5847273b79 | ||
|
|
98204991ec | ||
|
|
b8e10bfe15 | ||
|
|
132fe66699 | ||
|
|
a28fc4dfb1 | ||
|
|
7721aa5643 | ||
|
|
68707aff17 | ||
|
|
3ecbc9adff | ||
|
|
b5a7a0ca31 | ||
|
|
2362db8528 | ||
|
|
62835bdc54 | ||
|
|
fe1cf209f1 | ||
|
|
2f72563b1f | ||
|
|
0d754d6ca8 | ||
|
|
f474f6499b | ||
|
|
97baacf609 | ||
|
|
0fa8cfdb71 | ||
|
|
78c7cae7e9 | ||
|
|
a4834efd37 | ||
|
|
07e4435a07 | ||
|
|
9f93771771 | ||
|
|
a533807fcf | ||
|
|
49129ec1b2 | ||
|
|
803354552f | ||
|
|
fa6c365679 | ||
|
|
6ac1ae3527 | ||
|
|
29834b714e | ||
|
|
bba27adf4d | ||
|
|
e3a280d7a9 | ||
|
|
63f1a736f4 | ||
|
|
0f8720c433 | ||
|
|
7a6263b940 | ||
|
|
b09e3df2e2 | ||
|
|
041a9e0e7f |
No files matched your search
+10
@@ -45,3 +45,13 @@ Makefile.in
|
||||
# KDevelop
|
||||
.kdev4/
|
||||
*.kdev4
|
||||
|
||||
# Python
|
||||
*.pyc
|
||||
|
||||
# git backup files
|
||||
*.orig
|
||||
|
||||
# vim swap files
|
||||
*.swp
|
||||
*.swo
|
||||
+47
-27
@@ -1,43 +1,52 @@
|
||||
cmake_minimum_required (VERSION 2.8.6)
|
||||
project (opendht)
|
||||
set (opendht_VERSION_MAJOR 0)
|
||||
set (opendht_VERSION_MINOR 6.1)
|
||||
set (opendht_VERSION_MAJOR 1)
|
||||
set (opendht_VERSION_MINOR 3.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}/lib")
|
||||
set (includedir "\${prefix}/include")
|
||||
set (libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
|
||||
set (includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
|
||||
option (OPENDHT_DEBUG "Build with debug flags" OFF)
|
||||
option (OPENDHT_STATIC "Build static library" ON)
|
||||
option (OPENDHT_SHARED "Build shared library" ON)
|
||||
option (OPENDHT_LTO "Build with LTO" OFF)
|
||||
|
||||
option (OPENDHT_LOG "Build with logs" ON)
|
||||
option (OPENDHT_PYTHON "Build Python bindings" OFF)
|
||||
option (OPENDHT_TOOLS "Build DHT tools" ON)
|
||||
option (OPENDHT_LTO "Build with LTO" OFF)
|
||||
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -std=c++11 -Wno-return-type -Wall -Wextra -Wnon-virtual-dtor")
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMSGPACK_DISABLE_LEGACY_NIL -DMSGPACK_DISABLE_LEGACY_CONVERT")
|
||||
if (OPENDHT_LTO)
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto -fuse-linker-plugin")
|
||||
if (CMAKE_COMPILER_IS_GNUCC)
|
||||
SET (CMAKE_AR "gcc-ar")
|
||||
SET (CMAKE_NM "gcc-nm")
|
||||
SET (CMAKE_RANLIB "gcc-ranlib")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
find_package (GnuTLS 3.1 REQUIRED)
|
||||
find_package (GnuTLS 3.3 REQUIRED)
|
||||
find_package (Msgpack 1.2 REQUIRED)
|
||||
if (OPENDHT_TOOLS)
|
||||
find_package (Readline 6 REQUIRED)
|
||||
endif ()
|
||||
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -std=c++11 -Wno-return-type -Wall -Wextra -Wnon-virtual-dtor -pedantic-errors -fvisibility=hidden")
|
||||
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)
|
||||
endif ()
|
||||
if (OPENDHT_LOG)
|
||||
add_definitions(-DOPENDHT_LOG=true)
|
||||
else ()
|
||||
add_definitions(-DOPENDHT_LOG=false)
|
||||
endif()
|
||||
if (OPENDHT_LTO)
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto")
|
||||
if (CMAKE_COMPILER_IS_GNUCC)
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-linker-plugin")
|
||||
set (CMAKE_AR "gcc-ar")
|
||||
set (CMAKE_NM "gcc-nm")
|
||||
set (CMAKE_RANLIB "gcc-ranlib")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
list (APPEND opendht_SOURCES
|
||||
src/utils.cpp
|
||||
src/infohash.cpp
|
||||
@@ -58,10 +67,14 @@ list (APPEND opendht_SOURCES
|
||||
src/argon2/thread.c
|
||||
src/argon2/encoding.c
|
||||
src/argon2/ref.c
|
||||
src/indexation/pht.cpp
|
||||
src/log.cpp
|
||||
)
|
||||
|
||||
list (APPEND opendht_HEADERS
|
||||
include/opendht/def.h
|
||||
include/opendht/utils.h
|
||||
include/opendht/sockaddr.h
|
||||
include/opendht/rng.h
|
||||
include/opendht/crypto.h
|
||||
include/opendht/infohash.h
|
||||
@@ -73,10 +86,14 @@ list (APPEND opendht_HEADERS
|
||||
include/opendht/routing_table.h
|
||||
include/opendht/node_cache.h
|
||||
include/opendht/network_engine.h
|
||||
include/opendht/net.h
|
||||
include/opendht/scheduler.h
|
||||
include/opendht/rate_limiter.h
|
||||
include/opendht/securedht.h
|
||||
include/opendht/log.h
|
||||
include/opendht/log_enable.h
|
||||
include/opendht.h
|
||||
include/opendht/indexation/pht.h
|
||||
)
|
||||
|
||||
configure_file (
|
||||
@@ -92,12 +109,6 @@ include_directories (
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/
|
||||
)
|
||||
|
||||
if (OPENDHT_DEBUG)
|
||||
set(CMAKE_BUILD_TYPE Debug)
|
||||
else ()
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED CMAKE_INSTALL_LIBDIR)
|
||||
set(CMAKE_INSTALL_LIBDIR lib)
|
||||
endif ()
|
||||
@@ -112,7 +123,7 @@ if (OPENDHT_STATIC)
|
||||
target_link_libraries(opendht-static -flto -fuse-linker-plugin)
|
||||
endif ()
|
||||
target_link_libraries(opendht-static gnutls nettle)
|
||||
install (TARGETS opendht-static DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
install (TARGETS opendht-static DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht)
|
||||
endif ()
|
||||
|
||||
if (OPENDHT_SHARED)
|
||||
@@ -122,20 +133,29 @@ if (OPENDHT_SHARED)
|
||||
)
|
||||
set_target_properties (opendht PROPERTIES IMPORT_SUFFIX "_import.lib")
|
||||
set_target_properties (opendht PROPERTIES SOVERSION ${opendht_VERSION_MAJOR} VERSION ${opendht_VERSION})
|
||||
target_compile_definitions(opendht PRIVATE OPENDHT_BUILD)
|
||||
if (OPENDHT_LTO)
|
||||
target_link_libraries(opendht -flto -fuse-linker-plugin)
|
||||
endif ()
|
||||
target_link_libraries(opendht gnutls nettle)
|
||||
install (TARGETS opendht DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
install (TARGETS opendht DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht)
|
||||
endif ()
|
||||
|
||||
if (OPENDHT_TOOLS)
|
||||
add_subdirectory(tools)
|
||||
endif ()
|
||||
add_subdirectory(doc)
|
||||
|
||||
if (OPENDHT_PYTHON)
|
||||
add_subdirectory(python)
|
||||
endif ()
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/opendhtConfigVersion.cmake"
|
||||
VERSION ${opendht_VERSION}
|
||||
COMPATIBILITY AnyNewerVersion
|
||||
)
|
||||
install (DIRECTORY include DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/opendht.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
install (EXPORT opendht DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/opendht FILE opendhtConfig.cmake)
|
||||
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/opendhtConfigVersion.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/opendht)
|
||||
@@ -0,0 +1,162 @@
|
||||
<?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="..\src\argon2\argon2.c" />
|
||||
<ClCompile Include="..\src\argon2\core.c" />
|
||||
<ClCompile Include="..\src\argon2\encoding.c" />
|
||||
<ClCompile Include="..\src\argon2\opt.c" />
|
||||
<ClCompile Include="..\src\argon2\ref.c" />
|
||||
<ClCompile Include="..\src\argon2\thread.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\src\argon2\argon2.h" />
|
||||
<ClInclude Include="..\src\argon2\core.h" />
|
||||
<ClInclude Include="..\src\argon2\encoding.h" />
|
||||
<ClInclude Include="..\src\argon2\opt.h" />
|
||||
<ClInclude Include="..\src\argon2\ref.h" />
|
||||
<ClInclude Include="..\src\argon2\thread.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,151 @@
|
||||
<?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="..\src\argon2\blake2\blake2b.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\src\argon2\blake2\blake2-impl.h" />
|
||||
<ClInclude Include="..\src\argon2\blake2\blake2.h" />
|
||||
<ClInclude Include="..\src\argon2\blake2\blamka-round-opt.h" />
|
||||
<ClInclude Include="..\src\argon2\blake2\blamka-round-ref.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,166 @@
|
||||
<?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>{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}</ProjectGuid>
|
||||
<RootNamespace>dhtchat</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</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'">
|
||||
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\..\msgpack-c\include;$(ProjectDir)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4267;4244;4800;4273;</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>crypt32.lib;blake.lib;argon.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)..\..\lib\x64</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4049 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\..\msgpack-c\include;$(ProjectDir)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4267;4244;4800;4273;</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>crypt32.lib;blake.lib;argon.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)..\..\lib\x64</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4049 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\..\msgpack-c\include;$(ProjectDir)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4267;4244;4800;4273;</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>crypt32.lib;blake.lib;argon.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)..\..\lib\x64</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4049 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\..\msgpack-c\include;$(ProjectDir)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4267;4244;4800;4273;</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>crypt32.lib;blake.lib;argon.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)..\..\lib\x64</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4049 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\tools\dhtchat.cpp" />
|
||||
<ClCompile Include="wingetopt.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\tools\tools_common.h" />
|
||||
<ClInclude Include="wingetopt.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,166 @@
|
||||
<?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>{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}</ProjectGuid>
|
||||
<RootNamespace>dhtnode</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</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'">
|
||||
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\..\msgpack-c\include;$(ProjectDir)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4267;4244;4800;4273;4101;</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>crypt32.lib;blake.lib;argon.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)..\..\lib\x64</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4049 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\..\msgpack-c\include;$(ProjectDir)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4267;4244;4800;4273;4101;</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>crypt32.lib;blake.lib;argon.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)..\..\lib\x64</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4049 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\..\msgpack-c\include;$(ProjectDir)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4267;4244;4800;4273;4101;</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>crypt32.lib;blake.lib;argon.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)..\..\lib\x64</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4049 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\..\msgpack-c\include;$(ProjectDir)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4267;4244;4800;4273;4101;</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>crypt32.lib;blake.lib;argon.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)..\..\lib\x64</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4049 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\tools\dhtnode.cpp" />
|
||||
<ClCompile Include="wingetopt.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\tools\tools_common.h" />
|
||||
<ClInclude Include="wingetopt.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,166 @@
|
||||
<?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>{78443BCD-4689-4007-A246-F8F34B27F561}</ProjectGuid>
|
||||
<RootNamespace>dhtscanner</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</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'">
|
||||
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\..\msgpack-c\include;$(ProjectDir)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4267;4244;4800;4273;4101;</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>crypt32.lib;blake.lib;argon.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)..\..\lib\x64</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4049 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\..\msgpack-c\include;$(ProjectDir)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4267;4244;4800;4273;4101;</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>crypt32.lib;blake.lib;argon.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)..\..\lib\x64</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4049 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\..\msgpack-c\include;$(ProjectDir)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4267;4244;4800;4273;4101;</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>crypt32.lib;blake.lib;argon.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)..\..\lib\x64</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4049 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\..\msgpack-c\include;$(ProjectDir)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4267;4244;4800;4273;4101;</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>crypt32.lib;blake.lib;argon.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)..\..\lib\x64</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/ignore:4049 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\tools\dhtscanner.cpp" />
|
||||
<ClCompile Include="wingetopt.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\tools\tools_common.h" />
|
||||
<ClInclude Include="wingetopt.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,221 @@
|
||||
--- /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
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
<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\network_engine.cpp" />
|
||||
<ClCompile Include="..\src\node.cpp" />
|
||||
@@ -40,11 +41,14 @@
|
||||
<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" />
|
||||
@@ -135,6 +139,8 @@
|
||||
<PostBuildEvent>
|
||||
<Command>mkdir $(OutDir)\include
|
||||
mkdir $(OutDir)\include\opendht
|
||||
mkdir $(OutDir)\include\opendht\indexation
|
||||
copy ..\include\opendht\indexation\*.h $(OutDir)\include\opendht\indexation
|
||||
copy ..\include\opendht\*.h $(OutDir)\include\opendht
|
||||
copy ..\include\opendht.h $(OutDir)\include\</Command>
|
||||
</PostBuildEvent>
|
||||
@@ -155,6 +161,8 @@ copy ..\include\opendht.h $(OutDir)\include\</Command>
|
||||
<PostBuildEvent>
|
||||
<Command>mkdir $(OutDir)\include
|
||||
mkdir $(OutDir)\include\opendht
|
||||
mkdir $(OutDir)\include\opendht\indexation
|
||||
copy ..\include\opendht\indexation\*.h $(OutDir)\include\opendht\indexation
|
||||
copy ..\include\opendht\*.h $(OutDir)\include\opendht
|
||||
copy ..\include\opendht.h $(OutDir)\include\</Command>
|
||||
</PostBuildEvent>
|
||||
@@ -182,6 +190,8 @@ copy ..\include\opendht.h $(OutDir)\include\</Command>
|
||||
<PostBuildEvent>
|
||||
<Command>mkdir $(OutDir)\include
|
||||
mkdir $(OutDir)\include\opendht
|
||||
mkdir $(OutDir)\include\opendht\indexation
|
||||
copy ..\include\opendht\indexation\*.h $(OutDir)\include\opendht\indexation
|
||||
copy ..\include\opendht\*.h $(OutDir)\include\opendht
|
||||
copy ..\include\opendht.h $(OutDir)\include\</Command>
|
||||
</PostBuildEvent>
|
||||
@@ -210,6 +220,8 @@ copy ..\include\opendht.h $(OutDir)\include\</Command>
|
||||
<PostBuildEvent>
|
||||
<Command>mkdir $(OutDir)\include
|
||||
mkdir $(OutDir)\include\opendht
|
||||
mkdir $(OutDir)\include\opendht\indexation
|
||||
copy ..\include\opendht\indexation\*.h $(OutDir)\include\opendht\indexation
|
||||
copy ..\include\opendht\*.h $(OutDir)\include\opendht
|
||||
copy ..\include\opendht.h $(OutDir)\include\</Command>
|
||||
</PostBuildEvent>
|
||||
|
||||
+177
-7
@@ -1,10 +1,11 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25123.0
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opendht", "opendht.vcxproj", "{711397CE-E5D5-467D-9457-8716C047E50C}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA} = {2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518} = {070FEF2B-0C3F-4F33-9D3C-53C7330BF518}
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2} = {122A2EA4-B283-4241-9655-786DE78283B2}
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02} = {6F610EE8-7F59-4191-AB88-F63843267C02}
|
||||
@@ -12,9 +13,9 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opendht", "opendht.vcxproj"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dependencies", "dependencies", "{71A1A9EC-1104-4335-A87E-AF8749FD5B34}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libgmp", "..\..\gmp\MSVC\libgmp.vcxproj", "{02B94302-23D6-43EF-8865-95CDE99D5DC2}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libgmp", "..\..\gmp\SMP\libgmp.vcxproj", "{02B94302-23D6-43EF-8865-95CDE99D5DC2}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libgnutls", "..\..\gnutls\MSVC\libgnutls.vcxproj", "{6F610EE8-7F59-4191-AB88-F63843267C02}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libgnutls", "..\..\gnutls\SMP\libgnutls.vcxproj", "{6F610EE8-7F59-4191-AB88-F63843267C02}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2} = {02B94302-23D6-43EF-8865-95CDE99D5DC2}
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518} = {070FEF2B-0C3F-4F33-9D3C-53C7330BF518}
|
||||
@@ -23,19 +24,43 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libgnutls", "..\..\gnutls\M
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B} = {CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libiconv", "..\..\libiconv\MSVC\libiconv.vcxproj", "{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libiconv", "..\..\libiconv\SMP\libiconv.vcxproj", "{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libhogweed", "..\..\nettle\MSVC\libhogweed.vcxproj", "{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libhogweed", "..\..\nettle\SMP\libhogweed.vcxproj", "{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2} = {02B94302-23D6-43EF-8865-95CDE99D5DC2}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnettle", "..\..\nettle\MSVC\libnettle.vcxproj", "{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnettle", "..\..\nettle\SMP\libnettle.vcxproj", "{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libzlib", "..\..\zlib\MSVC\libzlib.vcxproj", "{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libzlib", "..\..\zlib\SMP\libzlib.vcxproj", "{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MessagePack", "..\..\msgpack-c\msgpack_vc8.vcxproj", "{122A2EA4-B283-4241-9655-786DE78283B2}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "argon", "argon.vcxproj", "{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6} = {36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "blake", "blake.vcxproj", "{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{296E2989-CF09-43AC-BBD2-BCF1A4B56D07}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dhtnode", "dhtnode.vcxproj", "{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C} = {711397CE-E5D5-467D-9457-8716C047E50C}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dhtscanner", "dhtscanner.vcxproj", "{78443BCD-4689-4007-A246-F8F34B27F561}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C} = {711397CE-E5D5-467D-9457-8716C047E50C}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dhtchat", "dhtchat.vcxproj", "{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C} = {711397CE-E5D5-467D-9457-8716C047E50C}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
@@ -276,6 +301,146 @@ Global
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseLTO|x64.Build.0 = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseLTO|x86.ActiveCfg = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseLTO|x86.Build.0 = Release|Win32
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.Debug|x64.Build.0 = Debug|x64
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.Debug|x86.Build.0 = Debug|Win32
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.DebugDLL|x64.ActiveCfg = Debug|x64
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.DebugDLL|x64.Build.0 = Debug|x64
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.DebugDLL|x86.ActiveCfg = Debug|Win32
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.DebugDLL|x86.Build.0 = Debug|Win32
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.DebugDLLStaticDeps|x64.ActiveCfg = Debug|x64
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.DebugDLLStaticDeps|x64.Build.0 = Debug|x64
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.DebugDLLStaticDeps|x86.ActiveCfg = Debug|Win32
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.DebugDLLStaticDeps|x86.Build.0 = Debug|Win32
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.Release|x64.ActiveCfg = Release|x64
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.Release|x64.Build.0 = Release|x64
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.Release|x86.ActiveCfg = Release|Win32
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.Release|x86.Build.0 = Release|Win32
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.ReleaseDLL|x64.ActiveCfg = Release|x64
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.ReleaseDLL|x64.Build.0 = Release|x64
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.ReleaseDLL|x86.ActiveCfg = Release|Win32
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.ReleaseDLL|x86.Build.0 = Release|Win32
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.ReleaseDLLStaticDeps|x64.ActiveCfg = Release|x64
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.ReleaseDLLStaticDeps|x64.Build.0 = Release|x64
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.ReleaseDLLStaticDeps|x86.ActiveCfg = Release|Win32
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.ReleaseDLLStaticDeps|x86.Build.0 = Release|Win32
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.ReleaseLTO|x64.ActiveCfg = Release|x64
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.ReleaseLTO|x64.Build.0 = Release|x64
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.ReleaseLTO|x86.ActiveCfg = Release|Win32
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}.ReleaseLTO|x86.Build.0 = Release|Win32
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.Debug|x64.Build.0 = Debug|x64
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.Debug|x86.Build.0 = Debug|Win32
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.DebugDLL|x64.ActiveCfg = Debug|x64
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.DebugDLL|x64.Build.0 = Debug|x64
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.DebugDLL|x86.ActiveCfg = Debug|Win32
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.DebugDLL|x86.Build.0 = Debug|Win32
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.DebugDLLStaticDeps|x64.ActiveCfg = Debug|x64
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.DebugDLLStaticDeps|x64.Build.0 = Debug|x64
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.DebugDLLStaticDeps|x86.ActiveCfg = Debug|Win32
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.DebugDLLStaticDeps|x86.Build.0 = Debug|Win32
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.Release|x64.ActiveCfg = Release|x64
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.Release|x64.Build.0 = Release|x64
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.Release|x86.ActiveCfg = Release|Win32
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.Release|x86.Build.0 = Release|Win32
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.ReleaseDLL|x64.ActiveCfg = Release|x64
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.ReleaseDLL|x64.Build.0 = Release|x64
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.ReleaseDLL|x86.ActiveCfg = Release|Win32
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.ReleaseDLL|x86.Build.0 = Release|Win32
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.ReleaseDLLStaticDeps|x64.ActiveCfg = Release|x64
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.ReleaseDLLStaticDeps|x64.Build.0 = Release|x64
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.ReleaseDLLStaticDeps|x86.ActiveCfg = Release|Win32
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.ReleaseDLLStaticDeps|x86.Build.0 = Release|Win32
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.ReleaseLTO|x64.ActiveCfg = Release|x64
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.ReleaseLTO|x64.Build.0 = Release|x64
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.ReleaseLTO|x86.ActiveCfg = Release|Win32
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}.ReleaseLTO|x86.Build.0 = Release|Win32
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.Debug|x64.Build.0 = Debug|x64
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.Debug|x86.Build.0 = Debug|Win32
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.DebugDLL|x64.ActiveCfg = Debug|x64
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.DebugDLL|x64.Build.0 = Debug|x64
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.DebugDLL|x86.ActiveCfg = Debug|Win32
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.DebugDLL|x86.Build.0 = Debug|Win32
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.DebugDLLStaticDeps|x64.ActiveCfg = Debug|x64
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.DebugDLLStaticDeps|x64.Build.0 = Debug|x64
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.DebugDLLStaticDeps|x86.ActiveCfg = Debug|Win32
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.DebugDLLStaticDeps|x86.Build.0 = Debug|Win32
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.Release|x64.ActiveCfg = Release|x64
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.Release|x64.Build.0 = Release|x64
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.Release|x86.ActiveCfg = Release|Win32
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.Release|x86.Build.0 = Release|Win32
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.ReleaseDLL|x64.ActiveCfg = Release|x64
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.ReleaseDLL|x64.Build.0 = Release|x64
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.ReleaseDLL|x86.ActiveCfg = Release|Win32
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.ReleaseDLL|x86.Build.0 = Release|Win32
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.ReleaseDLLStaticDeps|x64.ActiveCfg = Release|x64
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.ReleaseDLLStaticDeps|x64.Build.0 = Release|x64
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.ReleaseDLLStaticDeps|x86.ActiveCfg = Release|Win32
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.ReleaseDLLStaticDeps|x86.Build.0 = Release|Win32
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.ReleaseLTO|x64.ActiveCfg = Release|x64
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.ReleaseLTO|x64.Build.0 = Release|x64
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.ReleaseLTO|x86.ActiveCfg = Release|Win32
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5}.ReleaseLTO|x86.Build.0 = Release|Win32
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.Debug|x64.Build.0 = Debug|x64
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.Debug|x86.Build.0 = Debug|Win32
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.DebugDLL|x64.ActiveCfg = Debug|x64
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.DebugDLL|x64.Build.0 = Debug|x64
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.DebugDLL|x86.ActiveCfg = Debug|Win32
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.DebugDLL|x86.Build.0 = Debug|Win32
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.DebugDLLStaticDeps|x64.ActiveCfg = Debug|x64
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.DebugDLLStaticDeps|x64.Build.0 = Debug|x64
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.DebugDLLStaticDeps|x86.ActiveCfg = Debug|Win32
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.DebugDLLStaticDeps|x86.Build.0 = Debug|Win32
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.Release|x64.ActiveCfg = Release|x64
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.Release|x64.Build.0 = Release|x64
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.Release|x86.ActiveCfg = Release|Win32
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.Release|x86.Build.0 = Release|Win32
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.ReleaseDLL|x64.ActiveCfg = Release|x64
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.ReleaseDLL|x64.Build.0 = Release|x64
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.ReleaseDLL|x86.ActiveCfg = Release|Win32
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.ReleaseDLL|x86.Build.0 = Release|Win32
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.ReleaseDLLStaticDeps|x64.ActiveCfg = Release|x64
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.ReleaseDLLStaticDeps|x64.Build.0 = Release|x64
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.ReleaseDLLStaticDeps|x86.ActiveCfg = Release|Win32
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.ReleaseDLLStaticDeps|x86.Build.0 = Release|Win32
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.ReleaseLTO|x64.ActiveCfg = Release|x64
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.ReleaseLTO|x64.Build.0 = Release|x64
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.ReleaseLTO|x86.ActiveCfg = Release|Win32
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561}.ReleaseLTO|x86.Build.0 = Release|Win32
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.Debug|x64.Build.0 = Debug|x64
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.Debug|x86.Build.0 = Debug|Win32
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.DebugDLL|x64.ActiveCfg = Debug|x64
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.DebugDLL|x64.Build.0 = Debug|x64
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.DebugDLL|x86.ActiveCfg = Debug|Win32
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.DebugDLL|x86.Build.0 = Debug|Win32
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.DebugDLLStaticDeps|x64.ActiveCfg = Debug|x64
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.DebugDLLStaticDeps|x64.Build.0 = Debug|x64
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.DebugDLLStaticDeps|x86.ActiveCfg = Debug|Win32
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.DebugDLLStaticDeps|x86.Build.0 = Debug|Win32
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.Release|x64.ActiveCfg = Release|x64
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.Release|x64.Build.0 = Release|x64
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.Release|x86.ActiveCfg = Release|Win32
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.Release|x86.Build.0 = Release|Win32
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.ReleaseDLL|x64.ActiveCfg = Release|x64
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.ReleaseDLL|x64.Build.0 = Release|x64
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.ReleaseDLL|x86.ActiveCfg = Release|Win32
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.ReleaseDLL|x86.Build.0 = Release|Win32
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.ReleaseDLLStaticDeps|x64.ActiveCfg = Release|x64
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.ReleaseDLLStaticDeps|x64.Build.0 = Release|x64
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.ReleaseDLLStaticDeps|x86.ActiveCfg = Release|Win32
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.ReleaseDLLStaticDeps|x86.Build.0 = Release|Win32
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.ReleaseLTO|x64.ActiveCfg = Release|x64
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.ReleaseLTO|x64.Build.0 = Release|x64
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.ReleaseLTO|x86.ActiveCfg = Release|Win32
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA}.ReleaseLTO|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -288,5 +453,10 @@ Global
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518} = {71A1A9EC-1104-4335-A87E-AF8749FD5B34}
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32} = {71A1A9EC-1104-4335-A87E-AF8749FD5B34}
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2} = {71A1A9EC-1104-4335-A87E-AF8749FD5B34}
|
||||
{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA} = {71A1A9EC-1104-4335-A87E-AF8749FD5B34}
|
||||
{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6} = {71A1A9EC-1104-4335-A87E-AF8749FD5B34}
|
||||
{BF92AECF-AA1D-4B05-9D00-0247E92A24B5} = {296E2989-CF09-43AC-BBD2-BCF1A4B56D07}
|
||||
{78443BCD-4689-4007-A246-F8F34B27F561} = {296E2989-CF09-43AC-BBD2-BCF1A4B56D07}
|
||||
{8BE7F14D-B227-4D54-9105-7E5473F2D0BA} = {296E2989-CF09-43AC-BBD2-BCF1A4B56D07}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,12 +1,11 @@
|
||||
@ECHO OFF
|
||||
SETLOCAL EnableDelayedExpansion
|
||||
|
||||
SET UPSTREAMURL=https://github.com/atraczyk
|
||||
SET UPSTREAMURL=https://github.com/ShiftMediaProject
|
||||
SET DEPENDENCIES=( ^
|
||||
gmp, ^
|
||||
gnutls, ^
|
||||
nettle, ^
|
||||
msgpack-c ^
|
||||
nettle ^
|
||||
)
|
||||
|
||||
SET PASSDEPENDENCIES=%~1
|
||||
@@ -24,6 +23,15 @@ IF %ERRORLEVEL% EQU 128 (
|
||||
SET CURRDIR=%~dp1
|
||||
|
||||
cd ..\..
|
||||
set MSGPACK_VERSION=1df97bc37b363a340c5ad06c5cbcc53310aaff80
|
||||
set MSGPACK_URL="https://github.com/msgpack/msgpack-c.git"
|
||||
ECHO Cloning msgpack-c...
|
||||
git clone %MSGPACK_URL% --quiet
|
||||
cd msgpack-c
|
||||
git checkout %MSGPACK_VERSION%
|
||||
git apply --reject --whitespace=fix ..\opendht\MSVC\msgpack-msvc.patch
|
||||
cd ..
|
||||
|
||||
FOR %%I IN %DEPENDENCIES% DO (
|
||||
ECHO !PASSDEPENDENCIES! | FINDSTR /C:"%%I" >NUL 2>&1 || (
|
||||
CALL :cloneOrUpdateRepo "%%I" )
|
||||
@@ -46,10 +54,10 @@ IF NOT EXIST "%REPONAME%" (
|
||||
|
||||
SET PASSDEPENDENCIES=%PASSDEPENDENCIES% %REPONAME%
|
||||
|
||||
IF EXIST "%REPONAME%\MSVC\project_get_dependencies.bat" (
|
||||
IF EXIST "%REPONAME%\SMP\project_get_dependencies.bat" (
|
||||
ECHO %REPONAME%: Found additional dependencies...
|
||||
ECHO.
|
||||
cd %REPONAME%\MSVC
|
||||
cd %REPONAME%\SMP
|
||||
project_get_dependencies.bat "!PASSDEPENDENCIES!" || GOTO exitOnError
|
||||
cd ..\..
|
||||
)
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __STRICT_ANSI__
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <process.h>
|
||||
#include <direct.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define R_OK 4
|
||||
#define W_OK 2
|
||||
#define F_OK 0
|
||||
|
||||
#ifndef STDIN_FILENO
|
||||
#define STDIN_FILENO 0
|
||||
#endif
|
||||
|
||||
#ifndef STDOUT_FILENO
|
||||
#define STDOUT_FILENO 1
|
||||
#endif
|
||||
|
||||
#ifndef STDERR_FILENO
|
||||
#define STDERR_FILENO 2
|
||||
#endif
|
||||
|
||||
#define srandom srand
|
||||
#define random rand
|
||||
|
||||
#define inline __inline
|
||||
typedef int mode_t;
|
||||
#include <BaseTsd.h>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,971 @@
|
||||
/* Getopt for Microsoft C
|
||||
This code is a modification of the Free Software Foundation, Inc.
|
||||
Getopt library for parsing command line argument the purpose was
|
||||
to provide a Microsoft Visual C friendly derivative. This code
|
||||
provides functionality for both Unicode and Multibyte builds.
|
||||
Date: 02/03/2011 - Ludvik Jerabek - Initial Release
|
||||
Version: 1.0
|
||||
Comment: Supports getopt, getopt_long, and getopt_long_only
|
||||
and POSIXLY_CORRECT environment flag
|
||||
License: LGPL
|
||||
Revisions:
|
||||
02/03/2011 - Ludvik Jerabek - Initial Release
|
||||
02/20/2011 - Ludvik Jerabek - Fixed compiler warnings at Level 4
|
||||
07/05/2011 - Ludvik Jerabek - Added no_argument, required_argument, optional_argument defs
|
||||
08/03/2011 - Ludvik Jerabek - Fixed non-argument runtime bug which caused runtime exception
|
||||
08/09/2011 - Ludvik Jerabek - Added code to export functions for DLL and LIB
|
||||
02/15/2012 - Ludvik Jerabek - Fixed _GETOPT_THROW definition missing in implementation file
|
||||
08/01/2012 - Ludvik Jerabek - Created separate functions for char and wchar_t characters so single dll can do both unicode and ansi
|
||||
10/15/2012 - Ludvik Jerabek - Modified to match latest GNU features
|
||||
06/19/2015 - Ludvik Jerabek - Fixed maximum option limitation caused by option_a (255) and option_w (65535) structure val variable
|
||||
**DISCLAIMER**
|
||||
THIS MATERIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
|
||||
EITHER EXPRESS OR IMPLIED, INCLUDING, BUT Not LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE, OR NON-INFRINGEMENT. SOME JURISDICTIONS DO NOT ALLOW THE
|
||||
EXCLUSION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT
|
||||
APPLY TO YOU. IN NO EVENT WILL I BE LIABLE TO ANY PARTY FOR ANY
|
||||
DIRECT, INDIRECT, SPECIAL OR OTHER CONSEQUENTIAL DAMAGES FOR ANY
|
||||
USE OF THIS MATERIAL INCLUDING, WITHOUT LIMITATION, ANY LOST
|
||||
PROFITS, BUSINESS INTERRUPTION, LOSS OF PROGRAMS OR OTHER DATA ON
|
||||
YOUR INFORMATION HANDLING SYSTEM OR OTHERWISE, EVEN If WE ARE
|
||||
EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
*/
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
# define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <malloc.h>
|
||||
#include "wingetopt.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define _GETOPT_THROW throw()
|
||||
#else
|
||||
#define _GETOPT_THROW
|
||||
#endif
|
||||
|
||||
int optind = 1;
|
||||
int opterr = 1;
|
||||
int optopt = '?';
|
||||
enum ENUM_ORDERING { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER };
|
||||
|
||||
//
|
||||
//
|
||||
// Ansi structures and functions follow
|
||||
//
|
||||
//
|
||||
|
||||
static struct _getopt_data_a
|
||||
{
|
||||
int optind;
|
||||
int opterr;
|
||||
int optopt;
|
||||
char *optarg;
|
||||
int __initialized;
|
||||
char *__nextchar;
|
||||
enum ENUM_ORDERING __ordering;
|
||||
int __posixly_correct;
|
||||
int __first_nonopt;
|
||||
int __last_nonopt;
|
||||
} getopt_data_a;
|
||||
char *optarg_a;
|
||||
|
||||
static void exchange_a(char **argv, struct _getopt_data_a *d)
|
||||
{
|
||||
int bottom = d->__first_nonopt;
|
||||
int middle = d->__last_nonopt;
|
||||
int top = d->optind;
|
||||
char *tem;
|
||||
while (top > middle && middle > bottom)
|
||||
{
|
||||
if (top - middle > middle - bottom)
|
||||
{
|
||||
int len = middle - bottom;
|
||||
register int i;
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
tem = argv[bottom + i];
|
||||
argv[bottom + i] = argv[top - (middle - bottom) + i];
|
||||
argv[top - (middle - bottom) + i] = tem;
|
||||
}
|
||||
top -= len;
|
||||
}
|
||||
else
|
||||
{
|
||||
int len = top - middle;
|
||||
register int i;
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
tem = argv[bottom + i];
|
||||
argv[bottom + i] = argv[middle + i];
|
||||
argv[middle + i] = tem;
|
||||
}
|
||||
bottom += len;
|
||||
}
|
||||
}
|
||||
d->__first_nonopt += (d->optind - d->__last_nonopt);
|
||||
d->__last_nonopt = d->optind;
|
||||
}
|
||||
static const char *_getopt_initialize_a (const char *optstring, struct _getopt_data_a *d, int posixly_correct)
|
||||
{
|
||||
d->__first_nonopt = d->__last_nonopt = d->optind;
|
||||
d->__nextchar = NULL;
|
||||
d->__posixly_correct = posixly_correct | !!getenv("POSIXLY_CORRECT");
|
||||
if (optstring[0] == '-')
|
||||
{
|
||||
d->__ordering = RETURN_IN_ORDER;
|
||||
++optstring;
|
||||
}
|
||||
else if (optstring[0] == '+')
|
||||
{
|
||||
d->__ordering = REQUIRE_ORDER;
|
||||
++optstring;
|
||||
}
|
||||
else if (d->__posixly_correct)
|
||||
d->__ordering = REQUIRE_ORDER;
|
||||
else
|
||||
d->__ordering = PERMUTE;
|
||||
return optstring;
|
||||
}
|
||||
int _getopt_internal_r_a (int argc, char *const *argv, const char *optstring, const struct option_a *longopts, int *longind, int long_only, struct _getopt_data_a *d, int posixly_correct)
|
||||
{
|
||||
int print_errors = d->opterr;
|
||||
if (argc < 1)
|
||||
return -1;
|
||||
d->optarg = NULL;
|
||||
if (d->optind == 0 || !d->__initialized)
|
||||
{
|
||||
if (d->optind == 0)
|
||||
d->optind = 1;
|
||||
optstring = _getopt_initialize_a (optstring, d, posixly_correct);
|
||||
d->__initialized = 1;
|
||||
}
|
||||
else if (optstring[0] == '-' || optstring[0] == '+')
|
||||
optstring++;
|
||||
if (optstring[0] == ':')
|
||||
print_errors = 0;
|
||||
if (d->__nextchar == NULL || *d->__nextchar == '\0')
|
||||
{
|
||||
if (d->__last_nonopt > d->optind)
|
||||
d->__last_nonopt = d->optind;
|
||||
if (d->__first_nonopt > d->optind)
|
||||
d->__first_nonopt = d->optind;
|
||||
if (d->__ordering == PERMUTE)
|
||||
{
|
||||
if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind)
|
||||
exchange_a ((char **) argv, d);
|
||||
else if (d->__last_nonopt != d->optind)
|
||||
d->__first_nonopt = d->optind;
|
||||
while (d->optind < argc && (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0'))
|
||||
d->optind++;
|
||||
d->__last_nonopt = d->optind;
|
||||
}
|
||||
if (d->optind != argc && !strcmp(argv[d->optind], "--"))
|
||||
{
|
||||
d->optind++;
|
||||
if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind)
|
||||
exchange_a((char **) argv, d);
|
||||
else if (d->__first_nonopt == d->__last_nonopt)
|
||||
d->__first_nonopt = d->optind;
|
||||
d->__last_nonopt = argc;
|
||||
d->optind = argc;
|
||||
}
|
||||
if (d->optind == argc)
|
||||
{
|
||||
if (d->__first_nonopt != d->__last_nonopt)
|
||||
d->optind = d->__first_nonopt;
|
||||
return -1;
|
||||
}
|
||||
if ((argv[d->optind][0] != '-' || argv[d->optind][1] == '\0'))
|
||||
{
|
||||
if (d->__ordering == REQUIRE_ORDER)
|
||||
return -1;
|
||||
d->optarg = argv[d->optind++];
|
||||
return 1;
|
||||
}
|
||||
d->__nextchar = (argv[d->optind] + 1 + (longopts != NULL && argv[d->optind][1] == '-'));
|
||||
}
|
||||
if (longopts != NULL && (argv[d->optind][1] == '-' || (long_only && (argv[d->optind][2] || !strchr(optstring, argv[d->optind][1])))))
|
||||
{
|
||||
char *nameend;
|
||||
unsigned int namelen;
|
||||
const struct option_a *p;
|
||||
const struct option_a *pfound = NULL;
|
||||
struct option_list
|
||||
{
|
||||
const struct option_a *p;
|
||||
struct option_list *next;
|
||||
} *ambig_list = NULL;
|
||||
int exact = 0;
|
||||
int indfound = -1;
|
||||
int option_index;
|
||||
for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++);
|
||||
namelen = (unsigned int)(nameend - d->__nextchar);
|
||||
for (p = longopts, option_index = 0; p->name; p++, option_index++)
|
||||
if (!strncmp(p->name, d->__nextchar, namelen))
|
||||
{
|
||||
if (namelen == (unsigned int)strlen(p->name))
|
||||
{
|
||||
pfound = p;
|
||||
indfound = option_index;
|
||||
exact = 1;
|
||||
break;
|
||||
}
|
||||
else if (pfound == NULL)
|
||||
{
|
||||
pfound = p;
|
||||
indfound = option_index;
|
||||
}
|
||||
else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val)
|
||||
{
|
||||
struct option_list *newp = (struct option_list*)alloca(sizeof(*newp));
|
||||
newp->p = p;
|
||||
newp->next = ambig_list;
|
||||
ambig_list = newp;
|
||||
}
|
||||
}
|
||||
if (ambig_list != NULL && !exact)
|
||||
{
|
||||
if (print_errors)
|
||||
{
|
||||
struct option_list first;
|
||||
first.p = pfound;
|
||||
first.next = ambig_list;
|
||||
ambig_list = &first;
|
||||
fprintf (stderr, "%s: option '%s' is ambiguous; possibilities:", argv[0], argv[d->optind]);
|
||||
do
|
||||
{
|
||||
fprintf (stderr, " '--%s'", ambig_list->p->name);
|
||||
ambig_list = ambig_list->next;
|
||||
}
|
||||
while (ambig_list != NULL);
|
||||
fputc ('\n', stderr);
|
||||
}
|
||||
d->__nextchar += strlen(d->__nextchar);
|
||||
d->optind++;
|
||||
d->optopt = 0;
|
||||
return '?';
|
||||
}
|
||||
if (pfound != NULL)
|
||||
{
|
||||
option_index = indfound;
|
||||
d->optind++;
|
||||
if (*nameend)
|
||||
{
|
||||
if (pfound->has_arg)
|
||||
d->optarg = nameend + 1;
|
||||
else
|
||||
{
|
||||
if (print_errors)
|
||||
{
|
||||
if (argv[d->optind - 1][1] == '-')
|
||||
{
|
||||
fprintf(stderr, "%s: option '--%s' doesn't allow an argument\n",argv[0], pfound->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s: option '%c%s' doesn't allow an argument\n",argv[0], argv[d->optind - 1][0],pfound->name);
|
||||
}
|
||||
}
|
||||
d->__nextchar += strlen(d->__nextchar);
|
||||
d->optopt = pfound->val;
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
else if (pfound->has_arg == 1)
|
||||
{
|
||||
if (d->optind < argc)
|
||||
d->optarg = argv[d->optind++];
|
||||
else
|
||||
{
|
||||
if (print_errors)
|
||||
{
|
||||
fprintf(stderr,"%s: option '--%s' requires an argument\n",argv[0], pfound->name);
|
||||
}
|
||||
d->__nextchar += strlen(d->__nextchar);
|
||||
d->optopt = pfound->val;
|
||||
return optstring[0] == ':' ? ':' : '?';
|
||||
}
|
||||
}
|
||||
d->__nextchar += strlen(d->__nextchar);
|
||||
if (longind != NULL)
|
||||
*longind = option_index;
|
||||
if (pfound->flag)
|
||||
{
|
||||
*(pfound->flag) = pfound->val;
|
||||
return 0;
|
||||
}
|
||||
return pfound->val;
|
||||
}
|
||||
if (!long_only || argv[d->optind][1] == '-' || strchr(optstring, *d->__nextchar) == NULL)
|
||||
{
|
||||
if (print_errors)
|
||||
{
|
||||
if (argv[d->optind][1] == '-')
|
||||
{
|
||||
fprintf(stderr, "%s: unrecognized option '--%s'\n",argv[0], d->__nextchar);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s: unrecognized option '%c%s'\n",argv[0], argv[d->optind][0], d->__nextchar);
|
||||
}
|
||||
}
|
||||
d->__nextchar = (char *)"";
|
||||
d->optind++;
|
||||
d->optopt = 0;
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
{
|
||||
char c = *d->__nextchar++;
|
||||
char *temp = (char*)strchr(optstring, c);
|
||||
if (*d->__nextchar == '\0')
|
||||
++d->optind;
|
||||
if (temp == NULL || c == ':' || c == ';')
|
||||
{
|
||||
if (print_errors)
|
||||
{
|
||||
fprintf(stderr, "%s: invalid option -- '%c'\n", argv[0], c);
|
||||
}
|
||||
d->optopt = c;
|
||||
return '?';
|
||||
}
|
||||
if (temp[0] == 'W' && temp[1] == ';')
|
||||
{
|
||||
char *nameend;
|
||||
const struct option_a *p;
|
||||
const struct option_a *pfound = NULL;
|
||||
int exact = 0;
|
||||
int ambig = 0;
|
||||
int indfound = 0;
|
||||
int option_index;
|
||||
if (longopts == NULL)
|
||||
goto no_longs;
|
||||
if (*d->__nextchar != '\0')
|
||||
{
|
||||
d->optarg = d->__nextchar;
|
||||
d->optind++;
|
||||
}
|
||||
else if (d->optind == argc)
|
||||
{
|
||||
if (print_errors)
|
||||
{
|
||||
fprintf(stderr,"%s: option requires an argument -- '%c'\n",argv[0], c);
|
||||
}
|
||||
d->optopt = c;
|
||||
if (optstring[0] == ':')
|
||||
c = ':';
|
||||
else
|
||||
c = '?';
|
||||
return c;
|
||||
}
|
||||
else
|
||||
d->optarg = argv[d->optind++];
|
||||
for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != '='; nameend++);
|
||||
for (p = longopts, option_index = 0; p->name; p++, option_index++)
|
||||
if (!strncmp(p->name, d->__nextchar, nameend - d->__nextchar))
|
||||
{
|
||||
if ((unsigned int) (nameend - d->__nextchar) == strlen(p->name))
|
||||
{
|
||||
pfound = p;
|
||||
indfound = option_index;
|
||||
exact = 1;
|
||||
break;
|
||||
}
|
||||
else if (pfound == NULL)
|
||||
{
|
||||
pfound = p;
|
||||
indfound = option_index;
|
||||
}
|
||||
else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val)
|
||||
ambig = 1;
|
||||
}
|
||||
if (ambig && !exact)
|
||||
{
|
||||
if (print_errors)
|
||||
{
|
||||
fprintf(stderr, "%s: option '-W %s' is ambiguous\n",argv[0], d->optarg);
|
||||
}
|
||||
d->__nextchar += strlen(d->__nextchar);
|
||||
d->optind++;
|
||||
return '?';
|
||||
}
|
||||
if (pfound != NULL)
|
||||
{
|
||||
option_index = indfound;
|
||||
if (*nameend)
|
||||
{
|
||||
if (pfound->has_arg)
|
||||
d->optarg = nameend + 1;
|
||||
else
|
||||
{
|
||||
if (print_errors)
|
||||
{
|
||||
fprintf(stderr, "%s: option '-W %s' doesn't allow an argument\n",argv[0], pfound->name);
|
||||
}
|
||||
d->__nextchar += strlen(d->__nextchar);
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
else if (pfound->has_arg == 1)
|
||||
{
|
||||
if (d->optind < argc)
|
||||
d->optarg = argv[d->optind++];
|
||||
else
|
||||
{
|
||||
if (print_errors)
|
||||
{
|
||||
fprintf(stderr, "%s: option '-W %s' requires an argument\n",argv[0], pfound->name);
|
||||
}
|
||||
d->__nextchar += strlen(d->__nextchar);
|
||||
return optstring[0] == ':' ? ':' : '?';
|
||||
}
|
||||
}
|
||||
else
|
||||
d->optarg = NULL;
|
||||
d->__nextchar += strlen(d->__nextchar);
|
||||
if (longind != NULL)
|
||||
*longind = option_index;
|
||||
if (pfound->flag)
|
||||
{
|
||||
*(pfound->flag) = pfound->val;
|
||||
return 0;
|
||||
}
|
||||
return pfound->val;
|
||||
}
|
||||
no_longs:
|
||||
d->__nextchar = NULL;
|
||||
return 'W';
|
||||
}
|
||||
if (temp[1] == ':')
|
||||
{
|
||||
if (temp[2] == ':')
|
||||
{
|
||||
if (*d->__nextchar != '\0')
|
||||
{
|
||||
d->optarg = d->__nextchar;
|
||||
d->optind++;
|
||||
}
|
||||
else
|
||||
d->optarg = NULL;
|
||||
d->__nextchar = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*d->__nextchar != '\0')
|
||||
{
|
||||
d->optarg = d->__nextchar;
|
||||
d->optind++;
|
||||
}
|
||||
else if (d->optind == argc)
|
||||
{
|
||||
if (print_errors)
|
||||
{
|
||||
fprintf(stderr,"%s: option requires an argument -- '%c'\n",argv[0], c);
|
||||
}
|
||||
d->optopt = c;
|
||||
if (optstring[0] == ':')
|
||||
c = ':';
|
||||
else
|
||||
c = '?';
|
||||
}
|
||||
else
|
||||
d->optarg = argv[d->optind++];
|
||||
d->__nextchar = NULL;
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
int _getopt_internal_a (int argc, char *const *argv, const char *optstring, const struct option_a *longopts, int *longind, int long_only, int posixly_correct)
|
||||
{
|
||||
int result;
|
||||
getopt_data_a.optind = optind;
|
||||
getopt_data_a.opterr = opterr;
|
||||
result = _getopt_internal_r_a (argc, argv, optstring, longopts,longind, long_only, &getopt_data_a,posixly_correct);
|
||||
optind = getopt_data_a.optind;
|
||||
optarg_a = getopt_data_a.optarg;
|
||||
optopt = getopt_data_a.optopt;
|
||||
return result;
|
||||
}
|
||||
int getopt_a (int argc, char *const *argv, const char *optstring) _GETOPT_THROW
|
||||
{
|
||||
return _getopt_internal_a (argc, argv, optstring, (const struct option_a *) 0, (int *) 0, 0, 0);
|
||||
}
|
||||
int getopt_long_a (int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW
|
||||
{
|
||||
return _getopt_internal_a (argc, argv, options, long_options, opt_index, 0, 0);
|
||||
}
|
||||
int getopt_long_only_a (int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW
|
||||
{
|
||||
return _getopt_internal_a (argc, argv, options, long_options, opt_index, 1, 0);
|
||||
}
|
||||
int _getopt_long_r_a (int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index, struct _getopt_data_a *d)
|
||||
{
|
||||
return _getopt_internal_r_a (argc, argv, options, long_options, opt_index,0, d, 0);
|
||||
}
|
||||
int _getopt_long_only_r_a (int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index, struct _getopt_data_a *d)
|
||||
{
|
||||
return _getopt_internal_r_a (argc, argv, options, long_options, opt_index, 1, d, 0);
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// Unicode Structures and Functions
|
||||
//
|
||||
//
|
||||
|
||||
static struct _getopt_data_w
|
||||
{
|
||||
int optind;
|
||||
int opterr;
|
||||
int optopt;
|
||||
wchar_t *optarg;
|
||||
int __initialized;
|
||||
wchar_t *__nextchar;
|
||||
enum ENUM_ORDERING __ordering;
|
||||
int __posixly_correct;
|
||||
int __first_nonopt;
|
||||
int __last_nonopt;
|
||||
} getopt_data_w;
|
||||
wchar_t *optarg_w;
|
||||
|
||||
static void exchange_w(wchar_t **argv, struct _getopt_data_w *d)
|
||||
{
|
||||
int bottom = d->__first_nonopt;
|
||||
int middle = d->__last_nonopt;
|
||||
int top = d->optind;
|
||||
wchar_t *tem;
|
||||
while (top > middle && middle > bottom)
|
||||
{
|
||||
if (top - middle > middle - bottom)
|
||||
{
|
||||
int len = middle - bottom;
|
||||
register int i;
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
tem = argv[bottom + i];
|
||||
argv[bottom + i] = argv[top - (middle - bottom) + i];
|
||||
argv[top - (middle - bottom) + i] = tem;
|
||||
}
|
||||
top -= len;
|
||||
}
|
||||
else
|
||||
{
|
||||
int len = top - middle;
|
||||
register int i;
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
tem = argv[bottom + i];
|
||||
argv[bottom + i] = argv[middle + i];
|
||||
argv[middle + i] = tem;
|
||||
}
|
||||
bottom += len;
|
||||
}
|
||||
}
|
||||
d->__first_nonopt += (d->optind - d->__last_nonopt);
|
||||
d->__last_nonopt = d->optind;
|
||||
}
|
||||
static const wchar_t *_getopt_initialize_w (const wchar_t *optstring, struct _getopt_data_w *d, int posixly_correct)
|
||||
{
|
||||
d->__first_nonopt = d->__last_nonopt = d->optind;
|
||||
d->__nextchar = NULL;
|
||||
d->__posixly_correct = posixly_correct | !!_wgetenv(L"POSIXLY_CORRECT");
|
||||
if (optstring[0] == L'-')
|
||||
{
|
||||
d->__ordering = RETURN_IN_ORDER;
|
||||
++optstring;
|
||||
}
|
||||
else if (optstring[0] == L'+')
|
||||
{
|
||||
d->__ordering = REQUIRE_ORDER;
|
||||
++optstring;
|
||||
}
|
||||
else if (d->__posixly_correct)
|
||||
d->__ordering = REQUIRE_ORDER;
|
||||
else
|
||||
d->__ordering = PERMUTE;
|
||||
return optstring;
|
||||
}
|
||||
int _getopt_internal_r_w (int argc, wchar_t *const *argv, const wchar_t *optstring, const struct option_w *longopts, int *longind, int long_only, struct _getopt_data_w *d, int posixly_correct)
|
||||
{
|
||||
int print_errors = d->opterr;
|
||||
if (argc < 1)
|
||||
return -1;
|
||||
d->optarg = NULL;
|
||||
if (d->optind == 0 || !d->__initialized)
|
||||
{
|
||||
if (d->optind == 0)
|
||||
d->optind = 1;
|
||||
optstring = _getopt_initialize_w (optstring, d, posixly_correct);
|
||||
d->__initialized = 1;
|
||||
}
|
||||
else if (optstring[0] == L'-' || optstring[0] == L'+')
|
||||
optstring++;
|
||||
if (optstring[0] == L':')
|
||||
print_errors = 0;
|
||||
if (d->__nextchar == NULL || *d->__nextchar == L'\0')
|
||||
{
|
||||
if (d->__last_nonopt > d->optind)
|
||||
d->__last_nonopt = d->optind;
|
||||
if (d->__first_nonopt > d->optind)
|
||||
d->__first_nonopt = d->optind;
|
||||
if (d->__ordering == PERMUTE)
|
||||
{
|
||||
if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind)
|
||||
exchange_w((wchar_t **) argv, d);
|
||||
else if (d->__last_nonopt != d->optind)
|
||||
d->__first_nonopt = d->optind;
|
||||
while (d->optind < argc && (argv[d->optind][0] != L'-' || argv[d->optind][1] == L'\0'))
|
||||
d->optind++;
|
||||
d->__last_nonopt = d->optind;
|
||||
}
|
||||
if (d->optind != argc && !wcscmp(argv[d->optind], L"--"))
|
||||
{
|
||||
d->optind++;
|
||||
if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind)
|
||||
exchange_w((wchar_t **) argv, d);
|
||||
else if (d->__first_nonopt == d->__last_nonopt)
|
||||
d->__first_nonopt = d->optind;
|
||||
d->__last_nonopt = argc;
|
||||
d->optind = argc;
|
||||
}
|
||||
if (d->optind == argc)
|
||||
{
|
||||
if (d->__first_nonopt != d->__last_nonopt)
|
||||
d->optind = d->__first_nonopt;
|
||||
return -1;
|
||||
}
|
||||
if ((argv[d->optind][0] != L'-' || argv[d->optind][1] == L'\0'))
|
||||
{
|
||||
if (d->__ordering == REQUIRE_ORDER)
|
||||
return -1;
|
||||
d->optarg = argv[d->optind++];
|
||||
return 1;
|
||||
}
|
||||
d->__nextchar = (argv[d->optind] + 1 + (longopts != NULL && argv[d->optind][1] == L'-'));
|
||||
}
|
||||
if (longopts != NULL && (argv[d->optind][1] == L'-' || (long_only && (argv[d->optind][2] || !wcschr(optstring, argv[d->optind][1])))))
|
||||
{
|
||||
wchar_t *nameend;
|
||||
unsigned int namelen;
|
||||
const struct option_w *p;
|
||||
const struct option_w *pfound = NULL;
|
||||
struct option_list
|
||||
{
|
||||
const struct option_w *p;
|
||||
struct option_list *next;
|
||||
} *ambig_list = NULL;
|
||||
int exact = 0;
|
||||
int indfound = -1;
|
||||
int option_index;
|
||||
for (nameend = d->__nextchar; *nameend && *nameend != L'='; nameend++);
|
||||
namelen = (unsigned int)(nameend - d->__nextchar);
|
||||
for (p = longopts, option_index = 0; p->name; p++, option_index++)
|
||||
if (!wcsncmp(p->name, d->__nextchar, namelen))
|
||||
{
|
||||
if (namelen == (unsigned int)wcslen(p->name))
|
||||
{
|
||||
pfound = p;
|
||||
indfound = option_index;
|
||||
exact = 1;
|
||||
break;
|
||||
}
|
||||
else if (pfound == NULL)
|
||||
{
|
||||
pfound = p;
|
||||
indfound = option_index;
|
||||
}
|
||||
else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val)
|
||||
{
|
||||
struct option_list *newp = (struct option_list*)alloca(sizeof(*newp));
|
||||
newp->p = p;
|
||||
newp->next = ambig_list;
|
||||
ambig_list = newp;
|
||||
}
|
||||
}
|
||||
if (ambig_list != NULL && !exact)
|
||||
{
|
||||
if (print_errors)
|
||||
{
|
||||
struct option_list first;
|
||||
first.p = pfound;
|
||||
first.next = ambig_list;
|
||||
ambig_list = &first;
|
||||
fwprintf(stderr, L"%s: option '%s' is ambiguous; possibilities:", argv[0], argv[d->optind]);
|
||||
do
|
||||
{
|
||||
fwprintf (stderr, L" '--%s'", ambig_list->p->name);
|
||||
ambig_list = ambig_list->next;
|
||||
}
|
||||
while (ambig_list != NULL);
|
||||
fputwc (L'\n', stderr);
|
||||
}
|
||||
d->__nextchar += wcslen(d->__nextchar);
|
||||
d->optind++;
|
||||
d->optopt = 0;
|
||||
return L'?';
|
||||
}
|
||||
if (pfound != NULL)
|
||||
{
|
||||
option_index = indfound;
|
||||
d->optind++;
|
||||
if (*nameend)
|
||||
{
|
||||
if (pfound->has_arg)
|
||||
d->optarg = nameend + 1;
|
||||
else
|
||||
{
|
||||
if (print_errors)
|
||||
{
|
||||
if (argv[d->optind - 1][1] == L'-')
|
||||
{
|
||||
fwprintf(stderr, L"%s: option '--%s' doesn't allow an argument\n",argv[0], pfound->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
fwprintf(stderr, L"%s: option '%c%s' doesn't allow an argument\n",argv[0], argv[d->optind - 1][0],pfound->name);
|
||||
}
|
||||
}
|
||||
d->__nextchar += wcslen(d->__nextchar);
|
||||
d->optopt = pfound->val;
|
||||
return L'?';
|
||||
}
|
||||
}
|
||||
else if (pfound->has_arg == 1)
|
||||
{
|
||||
if (d->optind < argc)
|
||||
d->optarg = argv[d->optind++];
|
||||
else
|
||||
{
|
||||
if (print_errors)
|
||||
{
|
||||
fwprintf(stderr,L"%s: option '--%s' requires an argument\n",argv[0], pfound->name);
|
||||
}
|
||||
d->__nextchar += wcslen(d->__nextchar);
|
||||
d->optopt = pfound->val;
|
||||
return optstring[0] == L':' ? L':' : L'?';
|
||||
}
|
||||
}
|
||||
d->__nextchar += wcslen(d->__nextchar);
|
||||
if (longind != NULL)
|
||||
*longind = option_index;
|
||||
if (pfound->flag)
|
||||
{
|
||||
*(pfound->flag) = pfound->val;
|
||||
return 0;
|
||||
}
|
||||
return pfound->val;
|
||||
}
|
||||
if (!long_only || argv[d->optind][1] == L'-' || wcschr(optstring, *d->__nextchar) == NULL)
|
||||
{
|
||||
if (print_errors)
|
||||
{
|
||||
if (argv[d->optind][1] == L'-')
|
||||
{
|
||||
fwprintf(stderr, L"%s: unrecognized option '--%s'\n",argv[0], d->__nextchar);
|
||||
}
|
||||
else
|
||||
{
|
||||
fwprintf(stderr, L"%s: unrecognized option '%c%s'\n",argv[0], argv[d->optind][0], d->__nextchar);
|
||||
}
|
||||
}
|
||||
d->__nextchar = (wchar_t *)L"";
|
||||
d->optind++;
|
||||
d->optopt = 0;
|
||||
return L'?';
|
||||
}
|
||||
}
|
||||
{
|
||||
wchar_t c = *d->__nextchar++;
|
||||
wchar_t *temp = (wchar_t*)wcschr(optstring, c);
|
||||
if (*d->__nextchar == L'\0')
|
||||
++d->optind;
|
||||
if (temp == NULL || c == L':' || c == L';')
|
||||
{
|
||||
if (print_errors)
|
||||
{
|
||||
fwprintf(stderr, L"%s: invalid option -- '%c'\n", argv[0], c);
|
||||
}
|
||||
d->optopt = c;
|
||||
return L'?';
|
||||
}
|
||||
if (temp[0] == L'W' && temp[1] == L';')
|
||||
{
|
||||
wchar_t *nameend;
|
||||
const struct option_w *p;
|
||||
const struct option_w *pfound = NULL;
|
||||
int exact = 0;
|
||||
int ambig = 0;
|
||||
int indfound = 0;
|
||||
int option_index;
|
||||
if (longopts == NULL)
|
||||
goto no_longs;
|
||||
if (*d->__nextchar != L'\0')
|
||||
{
|
||||
d->optarg = d->__nextchar;
|
||||
d->optind++;
|
||||
}
|
||||
else if (d->optind == argc)
|
||||
{
|
||||
if (print_errors)
|
||||
{
|
||||
fwprintf(stderr,L"%s: option requires an argument -- '%c'\n",argv[0], c);
|
||||
}
|
||||
d->optopt = c;
|
||||
if (optstring[0] == L':')
|
||||
c = L':';
|
||||
else
|
||||
c = L'?';
|
||||
return c;
|
||||
}
|
||||
else
|
||||
d->optarg = argv[d->optind++];
|
||||
for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != L'='; nameend++);
|
||||
for (p = longopts, option_index = 0; p->name; p++, option_index++)
|
||||
if (!wcsncmp(p->name, d->__nextchar, nameend - d->__nextchar))
|
||||
{
|
||||
if ((unsigned int) (nameend - d->__nextchar) == wcslen(p->name))
|
||||
{
|
||||
pfound = p;
|
||||
indfound = option_index;
|
||||
exact = 1;
|
||||
break;
|
||||
}
|
||||
else if (pfound == NULL)
|
||||
{
|
||||
pfound = p;
|
||||
indfound = option_index;
|
||||
}
|
||||
else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val)
|
||||
ambig = 1;
|
||||
}
|
||||
if (ambig && !exact)
|
||||
{
|
||||
if (print_errors)
|
||||
{
|
||||
fwprintf(stderr, L"%s: option '-W %s' is ambiguous\n",argv[0], d->optarg);
|
||||
}
|
||||
d->__nextchar += wcslen(d->__nextchar);
|
||||
d->optind++;
|
||||
return L'?';
|
||||
}
|
||||
if (pfound != NULL)
|
||||
{
|
||||
option_index = indfound;
|
||||
if (*nameend)
|
||||
{
|
||||
if (pfound->has_arg)
|
||||
d->optarg = nameend + 1;
|
||||
else
|
||||
{
|
||||
if (print_errors)
|
||||
{
|
||||
fwprintf(stderr, L"%s: option '-W %s' doesn't allow an argument\n",argv[0], pfound->name);
|
||||
}
|
||||
d->__nextchar += wcslen(d->__nextchar);
|
||||
return L'?';
|
||||
}
|
||||
}
|
||||
else if (pfound->has_arg == 1)
|
||||
{
|
||||
if (d->optind < argc)
|
||||
d->optarg = argv[d->optind++];
|
||||
else
|
||||
{
|
||||
if (print_errors)
|
||||
{
|
||||
fwprintf(stderr, L"%s: option '-W %s' requires an argument\n",argv[0], pfound->name);
|
||||
}
|
||||
d->__nextchar += wcslen(d->__nextchar);
|
||||
return optstring[0] == L':' ? L':' : L'?';
|
||||
}
|
||||
}
|
||||
else
|
||||
d->optarg = NULL;
|
||||
d->__nextchar += wcslen(d->__nextchar);
|
||||
if (longind != NULL)
|
||||
*longind = option_index;
|
||||
if (pfound->flag)
|
||||
{
|
||||
*(pfound->flag) = pfound->val;
|
||||
return 0;
|
||||
}
|
||||
return pfound->val;
|
||||
}
|
||||
no_longs:
|
||||
d->__nextchar = NULL;
|
||||
return L'W';
|
||||
}
|
||||
if (temp[1] == L':')
|
||||
{
|
||||
if (temp[2] == L':')
|
||||
{
|
||||
if (*d->__nextchar != L'\0')
|
||||
{
|
||||
d->optarg = d->__nextchar;
|
||||
d->optind++;
|
||||
}
|
||||
else
|
||||
d->optarg = NULL;
|
||||
d->__nextchar = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*d->__nextchar != L'\0')
|
||||
{
|
||||
d->optarg = d->__nextchar;
|
||||
d->optind++;
|
||||
}
|
||||
else if (d->optind == argc)
|
||||
{
|
||||
if (print_errors)
|
||||
{
|
||||
fwprintf(stderr,L"%s: option requires an argument -- '%c'\n",argv[0], c);
|
||||
}
|
||||
d->optopt = c;
|
||||
if (optstring[0] == L':')
|
||||
c = L':';
|
||||
else
|
||||
c = L'?';
|
||||
}
|
||||
else
|
||||
d->optarg = argv[d->optind++];
|
||||
d->__nextchar = NULL;
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
int _getopt_internal_w (int argc, wchar_t *const *argv, const wchar_t *optstring, const struct option_w *longopts, int *longind, int long_only, int posixly_correct)
|
||||
{
|
||||
int result;
|
||||
getopt_data_w.optind = optind;
|
||||
getopt_data_w.opterr = opterr;
|
||||
result = _getopt_internal_r_w (argc, argv, optstring, longopts,longind, long_only, &getopt_data_w,posixly_correct);
|
||||
optind = getopt_data_w.optind;
|
||||
optarg_w = getopt_data_w.optarg;
|
||||
optopt = getopt_data_w.optopt;
|
||||
return result;
|
||||
}
|
||||
int getopt_w (int argc, wchar_t *const *argv, const wchar_t *optstring) _GETOPT_THROW
|
||||
{
|
||||
return _getopt_internal_w (argc, argv, optstring, (const struct option_w *) 0, (int *) 0, 0, 0);
|
||||
}
|
||||
int getopt_long_w (int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW
|
||||
{
|
||||
return _getopt_internal_w (argc, argv, options, long_options, opt_index, 0, 0);
|
||||
}
|
||||
int getopt_long_only_w (int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW
|
||||
{
|
||||
return _getopt_internal_w (argc, argv, options, long_options, opt_index, 1, 0);
|
||||
}
|
||||
int _getopt_long_r_w (int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index, struct _getopt_data_w *d)
|
||||
{
|
||||
return _getopt_internal_r_w (argc, argv, options, long_options, opt_index,0, d, 0);
|
||||
}
|
||||
int _getopt_long_only_r_w (int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index, struct _getopt_data_w *d)
|
||||
{
|
||||
return _getopt_internal_r_w (argc, argv, options, long_options, opt_index, 1, d, 0);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
#ifndef __WINGETOPT_H_
|
||||
#define __WINGETOPT_H_
|
||||
|
||||
#ifdef _GETOPT_API
|
||||
#undef _GETOPT_API
|
||||
#endif
|
||||
|
||||
#if defined(EXPORTS_GETOPT) && defined(STATIC_GETOPT)
|
||||
#error "The preprocessor definitions of EXPORTS_GETOPT and STATIC_GETOPT can only be used individually"
|
||||
#elif defined(STATIC_GETOPT)
|
||||
#define _GETOPT_API
|
||||
#elif defined(EXPORTS_GETOPT)
|
||||
#define _GETOPT_API __declspec(dllexport)
|
||||
#else
|
||||
#define _GETOPT_API __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
// Change behavior for C\C++
|
||||
#ifdef __cplusplus
|
||||
#define _BEGIN_EXTERN_C extern "C" {
|
||||
#define _END_EXTERN_C }
|
||||
#define _GETOPT_THROW throw()
|
||||
#else
|
||||
#define _BEGIN_EXTERN_C
|
||||
#define _END_EXTERN_C
|
||||
#define _GETOPT_THROW
|
||||
#endif
|
||||
|
||||
// Standard GNU options
|
||||
#define null_argument 0 /*Argument Null*/
|
||||
#define no_argument 0 /*Argument Switch Only*/
|
||||
#define required_argument 1 /*Argument Required*/
|
||||
#define optional_argument 2 /*Argument Optional*/
|
||||
|
||||
|
||||
// Shorter Options
|
||||
#define ARG_NULL 0 /*Argument Null*/
|
||||
#define ARG_NONE 0 /*Argument Switch Only*/
|
||||
#define ARG_REQ 1 /*Argument Required*/
|
||||
#define ARG_OPT 2 /*Argument Optional*/
|
||||
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
_BEGIN_EXTERN_C
|
||||
|
||||
extern _GETOPT_API int optind;
|
||||
extern _GETOPT_API int opterr;
|
||||
extern _GETOPT_API int optopt;
|
||||
|
||||
// Ansi
|
||||
struct option_a
|
||||
{
|
||||
const char* name;
|
||||
int has_arg;
|
||||
int *flag;
|
||||
int val;
|
||||
};
|
||||
extern _GETOPT_API char *optarg_a;
|
||||
extern _GETOPT_API int getopt_a(int argc, char *const *argv, const char *optstring) _GETOPT_THROW;
|
||||
extern _GETOPT_API int getopt_long_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW;
|
||||
extern _GETOPT_API int getopt_long_only_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW;
|
||||
|
||||
// Unicode
|
||||
struct option_w
|
||||
{
|
||||
const wchar_t* name;
|
||||
int has_arg;
|
||||
int *flag;
|
||||
int val;
|
||||
};
|
||||
extern _GETOPT_API wchar_t *optarg_w;
|
||||
extern _GETOPT_API int getopt_w(int argc, wchar_t *const *argv, const wchar_t *optstring) _GETOPT_THROW;
|
||||
extern _GETOPT_API int getopt_long_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW;
|
||||
extern _GETOPT_API int getopt_long_only_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW;
|
||||
|
||||
_END_EXTERN_C
|
||||
|
||||
#undef _BEGIN_EXTERN_C
|
||||
#undef _END_EXTERN_C
|
||||
#undef _GETOPT_THROW
|
||||
#undef _GETOPT_API
|
||||
|
||||
#ifdef _UNICODE
|
||||
#define getopt getopt_w
|
||||
#define getopt_long getopt_long_w
|
||||
#define getopt_long_only getopt_long_only_w
|
||||
#define option option_w
|
||||
#define optarg optarg_w
|
||||
#else
|
||||
#define getopt getopt_a
|
||||
#define getopt_long getopt_long_a
|
||||
#define getopt_long_only getopt_long_only_a
|
||||
#define option option_a
|
||||
#define optarg optarg_a
|
||||
#endif
|
||||
#endif // __WINGETOPT_H_
|
||||
+1
-3
@@ -10,15 +10,13 @@ if USE_CYTHON
|
||||
SUBDIRS += python
|
||||
endif
|
||||
|
||||
if HAVE_DOXYGEN
|
||||
SUBDIRS += doc
|
||||
endif
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
DOC_FILES = \
|
||||
README.md \
|
||||
LICENSE
|
||||
COPYING
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(DOC_FILES)
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
OpenDHT
|
||||
===
|
||||
<img src="https://raw.githubusercontent.com/savoirfairelinux/opendht/master/resources/opendht_logo_512.png" width="100" align="right">
|
||||
<br>
|
||||
<h1 style="margin-top:10px">
|
||||
<a id="user-content-opendht-" class="anchor" href="/savoirfairelinux/opendht/blob/master/README.md#opendht-" aria-hidden="true"></a>OpenDHT
|
||||
</h1>
|
||||
|
||||
A lightweight C++11 Distributed Hash Table implementation originally based on https://github.com/jech/dht by Juliusz Chroboczek.
|
||||
|
||||
* Light and fast C++11 Kademlia DHT library.
|
||||
* Distributed shared key->value data-store
|
||||
* Clean and powerfull distributed map API with storage of arbitrary binary values of up to 56 KB.
|
||||
* Clean and powerful distributed map API with storage of arbitrary binary values of up to 56 KB.
|
||||
* Optional public key cryptography layer providing data signature and encryption (using GnuTLS).
|
||||
* IPv4 and IPv6 support.
|
||||
* Python binding.
|
||||
|
||||
Documentation
|
||||
-
|
||||
## Documentation
|
||||
See the wiki: <https://github.com/savoirfairelinux/opendht/wiki>
|
||||
|
||||
#### How-to build and install
|
||||
@@ -22,8 +25,7 @@ Build instructions : <https://github.com/savoirfairelinux/opendht/wiki/Build-the
|
||||
g++ main.cpp -std=c++11 -lopendht -lgnutls
|
||||
```
|
||||
|
||||
Examples
|
||||
-
|
||||
## Examples
|
||||
### C++ example
|
||||
The `tools` directory includes simple example programs :
|
||||
* `dhtnode`, a command line tool, mostly used for debuging, allowing to perform operations supported by the library (get, put etc.) with text values.
|
||||
@@ -85,22 +87,27 @@ for r in results:
|
||||
print(r)
|
||||
```
|
||||
|
||||
Dependencies
|
||||
-
|
||||
## Dependencies
|
||||
- msgpack-c 1.2+, used for data serialization.
|
||||
- GnuTLS 3.1+, used for cryptographic operations.
|
||||
- GnuTLS 3.3+, used for cryptographic operations.
|
||||
- Nettle 2.4+, a GnuTLS dependency for crypto.
|
||||
- Build tested with GCC 4.8+ (GNU/Linux, Android, Windows with MinGW), Clang/LLVM (Linux, OS X).
|
||||
- Build tested with Microsoft Visual Studio 2015
|
||||
|
||||
Licence
|
||||
-
|
||||
Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
## Contact
|
||||
|
||||
Licenced under the GNU General Public License version 3 (see COPYING for the full licence).
|
||||
IRC: join us on Freenode at [`#opendht`](https://webchat.freenode.net/?channels=%23opendht).
|
||||
|
||||
## Licence
|
||||
Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
|
||||
OpenDHT is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
See COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html for the full GPLv3 licence.
|
||||
|
||||
This project is independent from another project called OpenDHT (Sean Rhea. Ph.D. Thesis, 2005), now extinct.
|
||||
|
||||
Donations
|
||||
-
|
||||
## Donations
|
||||
We gratefully accept Bitcoin donations to support OpenDHT development at: `bitcoin:3EykSd1An888efq4Bq3KaV3hJ3JQ4FPnwm`.
|
||||
+27
-8
@@ -1,23 +1,42 @@
|
||||
AC_INIT(opendht, 0.6.1)
|
||||
dnl define macros
|
||||
m4_define([opendht_major_version], 1)
|
||||
m4_define([opendht_minor_version], 3)
|
||||
m4_define([opendht_patch_version], 0)
|
||||
m4_define([opendht_version],
|
||||
[opendht_major_version.opendht_minor_version.opendht_patch_version])
|
||||
|
||||
AC_INIT(opendht, [opendht_version])
|
||||
AC_CONFIG_AUX_DIR(ac)
|
||||
AM_INIT_AUTOMAKE([foreign subdir-objects])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CANONICAL_HOST
|
||||
|
||||
AC_SUBST(OPENDHT_MAJOR_VERSION, opendht_major_version)
|
||||
AC_SUBST(OPENDHT_MINOR_VERSION, opendht_minor_version)
|
||||
AC_SUBST(OPENDHT_PATCH_VERSION, opendht_patch_version)
|
||||
|
||||
AC_ARG_ENABLE([debug], AS_HELP_STRING([--enable-debug], [Build in debug mode, adds stricter warnings, disables optimization]))
|
||||
AS_IF([test "x$enable_debug" = "xyes"],
|
||||
[CXXFLAGS="${CXXFLAGS} -g -Wno-return-type -Wall -Wextra -Wnon-virtual-dtor -O0"],
|
||||
[CXXFLAGS="${CXXFLAGS} -O3"])
|
||||
[CXXFLAGS="${CXXFLAGS} -g -Wno-return-type -Wall -Wextra -Wnon-virtual-dtor -O0 -pedantic-errors"],
|
||||
[CXXFLAGS="${CXXFLAGS} -O3 -pedantic-errors"])
|
||||
|
||||
AC_PROG_CXX
|
||||
AM_PROG_AR
|
||||
|
||||
dnl Check for logs
|
||||
AC_ARG_ENABLE([logs], [AS_HELP_STRING([--disable-logs], [Disble DHT logs])])
|
||||
AS_IF([test "x$enable_logs" != "xno"], [
|
||||
AC_DEFINE([OPENDHT_LOG], [true], [Define if DHT logs are enabled])
|
||||
], [
|
||||
AC_DEFINE([OPENDHT_LOG], [false], [Define if DHT logs are enabled])
|
||||
])
|
||||
|
||||
dnl Check for Doxygen
|
||||
AC_ARG_ENABLE([doc], AS_HELP_STRING([--enable-doc], [Enable documentation generation (doxygen)]))
|
||||
AS_IF([test "x$enable_doc" = "xyes"], [
|
||||
AC_CHECK_PROGS([DOXYGEN], [doxygen])
|
||||
AS_IF([test -z "$DOXYGEN"], [AC_MSG_WARN([Doxygen not found - continuing without Doxygen support])])
|
||||
AS_IF([test -z "$DOXYGEN"], [AC_MSG_WARN([Doxygen not found - continuing without Doxygen support])])
|
||||
])
|
||||
AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"])
|
||||
|
||||
@@ -26,8 +45,8 @@ AC_ARG_ENABLE([python], [AS_HELP_STRING([--disable-python], [Disble python bindi
|
||||
AS_IF([test "x$enable_python" != "xno"], [
|
||||
AM_PATH_PYTHON([3.3],, [:])
|
||||
AS_IF([test -n "$PYTHON"],[
|
||||
AC_CHECK_PROGS([CYTHON], [cython3])
|
||||
AS_IF([test -z "$CYTHON"],[AC_MSG_WARN([Cython not found - continuing without python support])])
|
||||
echo 'import Cython' | $PYTHON
|
||||
AS_IF([test $? == 0],[CYTHON=yes],[AC_MSG_WARN([Cython not found - continuing without python support])])
|
||||
AC_CHECK_PROGS([PIP], [pip3])
|
||||
AS_IF([test -z "$PIP"],[AC_MSG_WARN([pip not found - continuing without python uninstall support])])
|
||||
])
|
||||
@@ -76,7 +95,7 @@ AX_CXX_COMPILE_STDCXX(11,[noext],[mandatory])
|
||||
|
||||
PKG_PROG_PKG_CONFIG()
|
||||
PKG_CHECK_MODULES([Nettle], [nettle >= 2.4])
|
||||
PKG_CHECK_MODULES([GnuTLS], [gnutls >= 3.1])
|
||||
PKG_CHECK_MODULES([GnuTLS], [gnutls >= 3.3])
|
||||
PKG_CHECK_MODULES([MsgPack], [msgpack >= 1.2])
|
||||
CXXFLAGS="${CXXFLAGS} -DMSGPACK_DISABLE_LEGACY_NIL -DMSGPACK_DISABLE_LEGACY_CONVERT"
|
||||
|
||||
@@ -88,7 +107,7 @@ AM_COND_IF([ENABLE_TOOLS], [
|
||||
])
|
||||
])
|
||||
|
||||
AM_COND_IF([HAVE_DOXYGEN], [AC_CONFIG_FILES([doc/Doxyfile doc/Makefile])])
|
||||
AC_CONFIG_FILES([doc/Doxyfile doc/Makefile])
|
||||
|
||||
dnl Configure setup.py if we build the python module
|
||||
AM_COND_IF([USE_CYTHON], [
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
if (OPENDHT_TOOLS)
|
||||
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dhtnode.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1)
|
||||
endif ()
|
||||
@@ -1,3 +1,6 @@
|
||||
dist_man1_MANS = dhtnode.1
|
||||
|
||||
if HAVE_DOXYGEN
|
||||
doxyfile.stamp:
|
||||
$(DOXYGEN) Doxyfile
|
||||
echo stamp > doxyfile.stamp
|
||||
@@ -7,3 +10,4 @@ CLEANFILES = doxyfile.stamp
|
||||
all-local: doxyfile.stamp
|
||||
clean-local:
|
||||
rm -rf $(top_srcdir)/doc/man $(top_srcdir)/doc/html
|
||||
endif
|
||||
Binary file not shown.
@@ -0,0 +1,142 @@
|
||||
\documentclass[11pt]{article}
|
||||
|
||||
\usepackage[utf8x]{inputenc}
|
||||
\usepackage[top=2cm,bottom=2cm]{geometry}
|
||||
|
||||
\usepackage{forloop}
|
||||
\newcounter{counter}
|
||||
|
||||
% maths
|
||||
\usepackage{amssymb}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{mathrsfs}
|
||||
\usepackage{shadethm}
|
||||
\usepackage{amsthm}
|
||||
\newshadetheorem{shadeDef}{Definition}[section]
|
||||
\newtheorem{remark}{Remark}[section]
|
||||
\renewcommand{\P}{\mathbb{P}}
|
||||
|
||||
\usepackage{float}
|
||||
\usepackage{booktabs}
|
||||
|
||||
\setlength{\parindent}{0ex}
|
||||
\setlength{\parskip}{0.5em}
|
||||
|
||||
\begin{document}
|
||||
\title{Annex 1: Probabilistic analysis of connectivity changes}
|
||||
\author{Adrien Béraud, Simon Désaulniers, Guillaume Roguez}
|
||||
\maketitle
|
||||
\pagestyle{empty}
|
||||
\begin{shadeDef}
|
||||
A node flagged as \emph{``expired''} by a node $n$ is a node which has not responded to any
|
||||
of $n$'s last three requests.
|
||||
\end{shadeDef}
|
||||
\begin{remark}
|
||||
An expired node will not be contacted before 10 minutes from its expiration time.
|
||||
\end{remark}
|
||||
|
||||
Let $N$ the DHT network, $n_0\in N$, a given node and the following probabilistic events:
|
||||
\begin{itemize}
|
||||
\item $A$: $\forall n \in N$ $n$ is unreachable by $n_0$, \emph{i.e.} $n_0$ lost connection
|
||||
with $N$;
|
||||
\item $B$: $S\subset N$, the nodes unreachable by $n_0$ with $k={|S|\over|N|}$;
|
||||
\item $C$: $m \le |N|$ nodes are flagged as ``expired''.
|
||||
\end{itemize}
|
||||
|
||||
We are interested in knowing $\P(A|C)$, \emph{i.e.} the probability of the event where $A$ occurs
|
||||
prior to $C$. From the above, we immediately get
|
||||
$$\left\{
|
||||
\begin{array}{ll}
|
||||
\P(C|A) & = 1\\
|
||||
\P(A) + \P(B) & = 1
|
||||
\end{array}
|
||||
\right.$$
|
||||
Also, the event $A|C$ can be abstracted as the urn problem of draw without replacement. Then,
|
||||
$$\P(C|B) = \prod_{i=0}^m \left[k|N| - i \over |N|\right] = \prod_{i=0}^m \left[k - { i \over |N| }\right]$$
|
||||
Furthermore, using Bayes' theroem we have
|
||||
\begin{align*}
|
||||
\P(A|C) & = { \P(C|A)\P(A) \over \P(C|A)\P(A) + \P(C|B)\P(B)}\\
|
||||
& = { \P(A) \over \P(A) + \P(C|B)\P(B) }\\
|
||||
& = { \P(A) \over \P(A) + \P(C|B)\left[1 - \P(A)\right] }\\
|
||||
\Rightarrow \forloop{counter}{0}{\value{counter} < 5}{\qquad}\quad \P(A) & =
|
||||
\P(A|C)\left[\P(A) + \P(C|B)\left(1 - \P(A)\right) \right] \\
|
||||
\Rightarrow \forloop{counter}{0}{\value{counter} < 2}{\qquad}\;\:\, \P(A)\left[{ 1 \over \P(A|C)} - 1\right] & =
|
||||
\P(C|B)\left(1 - \P(A)\right) \\
|
||||
\end{align*}
|
||||
Finally,
|
||||
\begin{equation}
|
||||
\label{eq:final}
|
||||
\left[{\P(A) \over 1 - \P(A)}\right]\left[{ 1 \over \P(A|C)} - 1\right] =
|
||||
\prod_{i=0}^m \left[k - { i \over |N| }\right]
|
||||
\end{equation}
|
||||
|
||||
From \eqref{eq:final}, we may set a plausible configuration $\{\P(A),\P(A|C),k,|N|\}$ letting us
|
||||
produce results such as in table \ref{tbl:k_1_2}, \ref{tbl:k_2_3} and \ref{tbl:k_3_4}.
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\caption{The values for $m$ assuming $\P(A|C) \ge 0.95,\, k = {1 \over 2}$}
|
||||
\label{tbl:k_1_2}
|
||||
\begin{tabular}{lcccc}
|
||||
\toprule
|
||||
$|N| \diagdown \P(A)$ & ${1 \over 10 }$ & ${1 \over 100 }$ & ${1 \over 1000 }$ & ${1 \over 10000 }$\\
|
||||
\midrule
|
||||
$2^0$ & 1 & 1 & 1 & 1\\
|
||||
$2^1$ & 1 & 1 & 1 & 1\\
|
||||
$2^2$ & 2 & 2 & 2 & 2\\
|
||||
$2^3$ & 4 & 4 & 4 & 4\\
|
||||
$2^4$ & 5 & 6 & 7 & 8\\
|
||||
$2^5$ & 5 & 7 & 9 & 10\\
|
||||
$2^6$ & 6 & 9 & 11 & 13\\
|
||||
$2^7$ & 6 & 9 & 12 & 14\\
|
||||
$2^8$ & 7 & 10 & 13 & 16\\
|
||||
$2^9$ & 7 & 10 & 13 & 16\\
|
||||
$2^{10}$ & 7 & 10 & 13 & 17\\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\caption{The values for $m$ assuming $\P(A|C) \ge 0.95,\, k = {2 \over 3}$}
|
||||
\label{tbl:k_2_3}
|
||||
\begin{tabular}{lcccc}
|
||||
\toprule
|
||||
$|N| \diagdown \P(A)$ & ${1 \over 10 }$ & ${1 \over 100 }$ & ${1 \over 1000 }$ & ${1 \over 10000 }$\\
|
||||
\midrule
|
||||
$2^0$ & 1 & 1 & 1 & 1\\
|
||||
$2^1$ & 2 & 2 & 2 & 2\\
|
||||
$2^2$ & 3 & 3 & 4 & 4\\
|
||||
$2^3$ & 5 & 5 & 6 & 8\\
|
||||
$2^4$ & 6 & 8 & 9 & 10\\
|
||||
$2^5$ & 8 & 10 & 12 & 14\\
|
||||
$2^6$ & 9 & 13 & 16 & 18\\
|
||||
$2^7$ & 11 & 15 & 18 & 22\\
|
||||
$2^8$ & 11 & 16 & 21 & 25\\
|
||||
$2^9$ & 12 & 17 & 22 & 27\\
|
||||
$2^{10}$ & 12 & 18 & 23 & 28\\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\caption{The values for $m$ assuming $\P(A|C) \ge 0.95,\, k = {3 \over 4}$}
|
||||
\label{tbl:k_3_4}
|
||||
\begin{tabular}{lcccc}
|
||||
\toprule
|
||||
$|N| \diagdown \P(A)$ & ${1 \over 10 }$ & ${1 \over 100 }$ & ${1 \over 1000 }$ & ${1 \over 10000 }$\\
|
||||
\midrule
|
||||
$2^0$ & 1 & 1 & 1 & 1\\
|
||||
$2^1$ & 2 & 2 & 2 & 2\\
|
||||
$2^2$ & 3 & 3 & 3 & 3\\
|
||||
$2^3$ & 5 & 6 & 6 & 6\\
|
||||
$2^4$ & 7 & 9 & 10 & 11\\
|
||||
$2^5$ & 10 & 12 & 14 & 16\\
|
||||
$2^6$ & 12 & 16 & 19 & 22\\
|
||||
$2^7$ & 14 & 19 & 23 & 27\\
|
||||
$2^8$ & 15 & 21 & 27 & 32\\
|
||||
$2^9$ & 16 & 23 & 30 & 36\\
|
||||
$2^{10}$ & 17 & 24 & 31 & 38\\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
\end{document}
|
||||
@@ -0,0 +1,87 @@
|
||||
.TH DHTNODE 1 2016-07-29
|
||||
|
||||
.SH NAME
|
||||
.B dhtnode
|
||||
- a simple OpenDHT command line node runner.
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B dhtnode [-h]
|
||||
|
||||
.B dhtnode [-v [-l \fIlogfile\fP]] [-i] [-d] [-n \fInetwork_id\fP] [-p \fIlocal_port\fP] [-b \fIbootstrap_host\fP[:\fIport\fP]]
|
||||
|
||||
.SH DESCRIPTION
|
||||
|
||||
This program runs a simple OpenDHT node in an interactive way. If you rather
|
||||
want to run the node in daemon mode, option \fB'-d'\fP is provided. When running
|
||||
in the interactive shell, you benefit from the readline capabilities for writing
|
||||
your commands such as command history. Here are the available commands in the
|
||||
interactive shell:
|
||||
|
||||
.EE
|
||||
h, help Print this help message.
|
||||
q, quit Quit the program.
|
||||
log Start/stop printing DHT logs.
|
||||
|
||||
Node information:
|
||||
ll Print basic information and stats about the current node.
|
||||
ls Print basic information about current searches.
|
||||
ld Print basic information about currenty stored values on this node.
|
||||
lr Print the full current routing table of this node
|
||||
|
||||
Operations on the DHT:
|
||||
b ip:port Ping potential node at given IP address/port.
|
||||
g [key] Get values at [key].
|
||||
l [key] Listen for value changes at [key].
|
||||
p [key] [str] Put string value at [key].
|
||||
s [key] [str] Put string value at [key], signed with our generated
|
||||
private key.
|
||||
e [key] [dest] [str] Put string value at [key], encrypted for [dest] with
|
||||
its public key (if found).
|
||||
|
||||
.SH OPTIONS
|
||||
|
||||
.TP
|
||||
\fB-h\fP
|
||||
Prints some help.
|
||||
|
||||
.TP
|
||||
\fB-v\fP
|
||||
Enable the verbose mode (log to stdout by default)
|
||||
|
||||
.TP
|
||||
\fB-l\fP \fIlog_file\fP
|
||||
Write log to file instead of stdout
|
||||
|
||||
.TP
|
||||
\fB-i\fP
|
||||
Generate cryptographic identity for the node.
|
||||
|
||||
.TP
|
||||
\fB-d\fP
|
||||
Run the program in daemon mode (will fork in the background).
|
||||
|
||||
.TP
|
||||
\fB-n\fP \fInetwork_id\fP
|
||||
Specify the network id. This let you connect to distinct networks and prevents
|
||||
the merge of two different networks (available since OpenDHT v0.6.1).
|
||||
|
||||
.TP
|
||||
\fB-p\fP \fIlocal_port\fP
|
||||
Use port \fIlocal_port\fP for the program to bind to.
|
||||
|
||||
.TP
|
||||
\fB-b\fP \fIbootstrap_host\fP[:\fIport\fP]
|
||||
The program needs to be given a node to connect to the network. You use this
|
||||
option to provide the ip address of that node.
|
||||
|
||||
.SH AUTHORS
|
||||
.TP
|
||||
Program written by
|
||||
.IP \(bu
|
||||
.\}
|
||||
Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
.TP
|
||||
Man page written by
|
||||
.IP \(bu
|
||||
.\}
|
||||
Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
+2
-2
@@ -2,5 +2,5 @@ FROM aberaud/opendht-deps
|
||||
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
RUN git clone https://github.com/savoirfairelinux/opendht.git \
|
||||
&& cd opendht && mkdir build && cd build \
|
||||
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr && make -j8 && make install \
|
||||
&& cd ../.. && rm -rf opendht
|
||||
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On -DOPENDHT_LTO=On && make -j8 && make install \
|
||||
&& cd ../.. && rm -rf opendht
|
||||
@@ -1,9 +1,9 @@
|
||||
FROM ubuntu:16.04
|
||||
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
RUN apt-get update && apt-get install -y build-essential cmake git wget libncurses5-dev libreadline-dev nettle-dev libgnutls-dev cython3 python3-dev python3-setuptools
|
||||
RUN wget https://github.com/msgpack/msgpack-c/releases/download/cpp-1.4.2/msgpack-1.4.2.tar.gz \
|
||||
&& tar -xzf msgpack-1.4.2.tar.gz \
|
||||
&& cd msgpack-1.4.2 && mkdir build && cd build \
|
||||
RUN apt-get update && apt-get install -y build-essential cmake git wget libncurses5-dev libreadline-dev nettle-dev libgnutls-dev cython3 python3-dev python3-setuptools && apt-get clean
|
||||
RUN wget https://github.com/msgpack/msgpack-c/releases/download/cpp-2.0.0/msgpack-2.0.0.tar.gz \
|
||||
&& tar -xzf msgpack-2.0.0.tar.gz \
|
||||
&& cd msgpack-2.0.0 && mkdir build && cd build \
|
||||
&& cmake -DMSGPACK_CXX11=ON -DMSGPACK_BUILD_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX=/usr .. \
|
||||
&& make -j8 && make install \
|
||||
&& cd ../.. && rm -rf msgpack-1.4.2
|
||||
&& cd ../.. && rm -rf msgpack-2.0.0 msgpack-2.0.0.tar.gz
|
||||
@@ -1,11 +1,13 @@
|
||||
FROM ubuntu:16.04
|
||||
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y llvm clang make cmake git wget libncurses5-dev libreadline-dev nettle-dev libgnutls-dev cython3 python3-dev python3-setuptools \
|
||||
&& apt-get install -y llvm llvm-dev clang make cmake git wget libncurses5-dev libreadline-dev nettle-dev libgnutls-dev cython3 python3-dev python3-setuptools \
|
||||
&& apt-get remove -y gcc g++ && apt-get autoremove -y && apt-get clean
|
||||
RUN wget https://github.com/msgpack/msgpack-c/releases/download/cpp-1.4.2/msgpack-1.4.2.tar.gz \
|
||||
&& tar -xzf msgpack-1.4.2.tar.gz \
|
||||
&& cd msgpack-1.4.2 && mkdir build && cd build \
|
||||
ENV CC cc
|
||||
ENV CXX c++
|
||||
RUN wget https://github.com/msgpack/msgpack-c/releases/download/cpp-2.0.0/msgpack-2.0.0.tar.gz \
|
||||
&& tar -xzf msgpack-2.0.0.tar.gz \
|
||||
&& cd msgpack-2.0.0 && mkdir build && cd build \
|
||||
&& cmake -DMSGPACK_CXX11=ON -DMSGPACK_BUILD_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX=/usr .. \
|
||||
&& make -j8 && make install \
|
||||
&& cd ../.. && rm -rf msgpack-1.4.2
|
||||
&& cd ../.. && rm -rf msgpack-2.0.0 msgpack-2.0.0.tar.gz
|
||||
@@ -2,5 +2,5 @@ FROM aberaud/opendht-deps-llvm
|
||||
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
RUN git clone https://github.com/savoirfairelinux/opendht.git \
|
||||
&& cd opendht && mkdir build && cd build \
|
||||
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr && make -j8 && make install \
|
||||
&& cd ../.. && rm -rf opendht
|
||||
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On && make -j8 && make install \
|
||||
&& cd ../.. && rm -rf opendht
|
||||
@@ -2,5 +2,5 @@ FROM aberaud/opendht-deps
|
||||
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
COPY . /root/opendht
|
||||
RUN cd /root/opendht && mkdir build && cd build \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr .. && make -j8 && make install \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On -DOPENDHT_LTO=On .. && make -j8 && make install \
|
||||
&& cd ../.. && rm -rf opendht
|
||||
@@ -2,5 +2,5 @@ FROM aberaud/opendht-deps-llvm
|
||||
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
COPY . /root/opendht
|
||||
RUN cd /root/opendht && mkdir build && cd build \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr .. && make -j8 && make install \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On .. && make -j8 && make install \
|
||||
&& cd ../.. && rm -rf opendht
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-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
|
||||
@@ -22,3 +22,4 @@
|
||||
#include "opendht/dhtrunner.h"
|
||||
#include "opendht/log.h"
|
||||
#include "opendht/default_types.h"
|
||||
#include "opendht/indexation/pht.h"
|
||||
+48
-13
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -30,25 +31,60 @@ namespace dht {
|
||||
|
||||
struct Node;
|
||||
|
||||
/**
|
||||
* Current status of a DHT node.
|
||||
*/
|
||||
enum class NodeStatus {
|
||||
Disconnected, // 0 nodes
|
||||
Connecting, // 1+ nodes
|
||||
Connected // 1+ good nodes
|
||||
};
|
||||
|
||||
struct Config {
|
||||
InfoHash node_id;
|
||||
bool is_bootstrap;
|
||||
struct OPENDHT_PUBLIC NodeStats {
|
||||
unsigned good_nodes,
|
||||
dubious_nodes,
|
||||
cached_nodes,
|
||||
incoming_nodes;
|
||||
unsigned table_depth;
|
||||
unsigned getKnownNodes() const { return good_nodes + dubious_nodes; }
|
||||
std::string toString() const;
|
||||
};
|
||||
|
||||
struct SecureDhtConfig
|
||||
/**
|
||||
* Dht configuration.
|
||||
*/
|
||||
struct OPENDHT_PUBLIC Config {
|
||||
/** DHT node ID */
|
||||
InfoHash node_id;
|
||||
|
||||
/**
|
||||
* DHT network ID. A node will only talk with other nodes having
|
||||
* the same network ID.
|
||||
* Network ID 0 (default) represents the main public network.
|
||||
*/
|
||||
NetId network;
|
||||
|
||||
/** For testing purposes only, enables bootstrap mode */
|
||||
bool is_bootstrap;
|
||||
|
||||
/** Makes the DHT responsible to maintain its stored values. Consumes more ressources. */
|
||||
bool maintain_storage;
|
||||
};
|
||||
|
||||
/**
|
||||
* SecureDht configuration.
|
||||
*/
|
||||
struct OPENDHT_PUBLIC SecureDhtConfig
|
||||
{
|
||||
Config node_config;
|
||||
crypto::Identity id;
|
||||
};
|
||||
|
||||
static constexpr size_t DEFAULT_STORAGE_LIMIT {1024 * 1024 * 64};
|
||||
|
||||
using ValuesExport = std::pair<InfoHash, Blob>;
|
||||
|
||||
using QueryCallback = std::function<bool(const std::vector<std::shared_ptr<FieldValueIndex>>& fields)>;
|
||||
using GetCallback = std::function<bool(const std::vector<std::shared_ptr<Value>>& values)>;
|
||||
using GetCallbackSimple = std::function<bool(std::shared_ptr<Value> value)>;
|
||||
using ShutdownCallback = std::function<void()>;
|
||||
@@ -57,20 +93,19 @@ using CertificateStoreQuery = std::function<std::vector<std::shared_ptr<crypto::
|
||||
|
||||
typedef bool (*GetCallbackRaw)(std::shared_ptr<Value>, void *user_data);
|
||||
|
||||
static constexpr size_t DEFAULT_STORAGE_LIMIT {1024 * 1024 * 64};
|
||||
|
||||
GetCallbackSimple bindGetCb(GetCallbackRaw raw_cb, void* user_data);
|
||||
GetCallback bindGetCb(GetCallbackSimple cb);
|
||||
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)>;
|
||||
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);
|
||||
|
||||
using DoneCallbackSimple = std::function<void(bool success)>;
|
||||
|
||||
ShutdownCallback bindShutdownCb(ShutdownCallbackRaw shutdown_cb_raw, void* user_data);
|
||||
DoneCallback bindDoneCb(DoneCallbackSimple donecb);
|
||||
DoneCallback bindDoneCb(DoneCallbackRaw raw_cb, void* user_data);
|
||||
|
||||
OPENDHT_PUBLIC ShutdownCallback bindShutdownCb(ShutdownCallbackRaw shutdown_cb_raw, void* user_data);
|
||||
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);
|
||||
|
||||
}
|
||||
+109
-15
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-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
|
||||
@@ -38,7 +38,7 @@ extern "C" {
|
||||
namespace dht {
|
||||
namespace crypto {
|
||||
|
||||
class CryptoException : public std::runtime_error {
|
||||
class OPENDHT_PUBLIC CryptoException : public std::runtime_error {
|
||||
public:
|
||||
CryptoException(const std::string& str) : std::runtime_error(str) {};
|
||||
};
|
||||
@@ -46,20 +46,21 @@ class CryptoException : public std::runtime_error {
|
||||
/**
|
||||
* Exception thrown when a decryption error happened.
|
||||
*/
|
||||
class DecryptError : public CryptoException {
|
||||
class OPENDHT_PUBLIC DecryptError : public CryptoException {
|
||||
public:
|
||||
DecryptError(const std::string& str = "") : CryptoException(str) {};
|
||||
};
|
||||
|
||||
struct PrivateKey;
|
||||
struct Certificate;
|
||||
class RevocationList;
|
||||
|
||||
using Identity = std::pair<std::shared_ptr<PrivateKey>, std::shared_ptr<Certificate>>;
|
||||
|
||||
/**
|
||||
* A public key.
|
||||
*/
|
||||
struct PublicKey
|
||||
struct OPENDHT_PUBLIC PublicKey
|
||||
{
|
||||
PublicKey() {}
|
||||
|
||||
@@ -109,7 +110,7 @@ private:
|
||||
/**
|
||||
* A private key, including the corresponding public key.
|
||||
*/
|
||||
struct PrivateKey
|
||||
struct OPENDHT_PUBLIC PrivateKey
|
||||
{
|
||||
PrivateKey();
|
||||
//PrivateKey(gnutls_privkey_t k) : key(k) {}
|
||||
@@ -148,6 +149,7 @@ struct PrivateKey
|
||||
* Recommended values: 4096, 8192
|
||||
*/
|
||||
static PrivateKey generate(unsigned key_length = 4096);
|
||||
static PrivateKey generateEC();
|
||||
|
||||
gnutls_privkey_t key {};
|
||||
gnutls_x509_privkey_t x509_key {};
|
||||
@@ -159,7 +161,77 @@ private:
|
||||
//friend dht::crypto::Identity dht::crypto::generateIdentity(const std::string&, dht::crypto::Identity, unsigned key_length);
|
||||
};
|
||||
|
||||
struct Certificate {
|
||||
|
||||
class OPENDHT_PUBLIC RevocationList
|
||||
{
|
||||
using clock = std::chrono::system_clock;
|
||||
using time_point = clock::time_point;
|
||||
using duration = clock::duration;
|
||||
public:
|
||||
RevocationList();
|
||||
RevocationList(const Blob& b);
|
||||
RevocationList(RevocationList&& o) : crl(o.crl) { o.crl = nullptr; }
|
||||
~RevocationList();
|
||||
|
||||
RevocationList& operator=(RevocationList&& o) { crl = o.crl; o.crl = nullptr; return *this; }
|
||||
|
||||
void pack(Blob& b) const;
|
||||
void unpack(const uint8_t* dat, size_t dat_size);
|
||||
Blob getPacked() const {
|
||||
Blob b;
|
||||
pack(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& p) const
|
||||
{
|
||||
Blob b = getPacked();
|
||||
p.pack_bin(b.size());
|
||||
p.pack_bin_body((const char*)b.data(), b.size());
|
||||
}
|
||||
|
||||
void msgpack_unpack(msgpack::object o);
|
||||
|
||||
void revoke(const Certificate& crt, time_point t = time_point::min());
|
||||
|
||||
bool isRevoked(const Certificate& crt) const;
|
||||
|
||||
/**
|
||||
* Sign this revocation list using provided key and certificate.
|
||||
* Validity_period sets the duration until next update (default to no next update).
|
||||
*/
|
||||
void sign(const PrivateKey&, const Certificate&, duration validity_period = {});
|
||||
void sign(const Identity& id) { sign(*id.first, *id.second); }
|
||||
|
||||
bool isSignedBy(const Certificate& issuer) const;
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
/**
|
||||
* Read the CRL number extension field.
|
||||
*/
|
||||
Blob getNumber() const;
|
||||
|
||||
/** Read CRL issuer Common Name (CN) */
|
||||
std::string getIssuerName() const;
|
||||
|
||||
/** Read CRL issuer User ID (UID) */
|
||||
std::string getIssuerUID() const;
|
||||
|
||||
time_point getUpdateTime() const;
|
||||
time_point getNextUpdateTime() const;
|
||||
|
||||
gnutls_x509_crl_t get() { return crl; }
|
||||
|
||||
private:
|
||||
gnutls_x509_crl_t crl {};
|
||||
RevocationList(const RevocationList&) = delete;
|
||||
RevocationList& operator=(const RevocationList&) = delete;
|
||||
};
|
||||
|
||||
|
||||
struct OPENDHT_PUBLIC Certificate {
|
||||
Certificate() {}
|
||||
|
||||
/**
|
||||
@@ -296,6 +368,8 @@ struct Certificate {
|
||||
/** Read certificate alternative names */
|
||||
std::vector<std::pair<NameType, std::string>> getAltNames() const;
|
||||
|
||||
std::chrono::system_clock::time_point getExpiration() const;
|
||||
|
||||
/**
|
||||
* Returns true if the certificate is marked as a Certificate Authority.
|
||||
*/
|
||||
@@ -307,6 +381,13 @@ struct Certificate {
|
||||
*/
|
||||
std::string toString(bool chain = true) const;
|
||||
|
||||
std::string print() const;
|
||||
|
||||
void revoke(const PrivateKey&, const Certificate&);
|
||||
std::vector<std::shared_ptr<RevocationList>> getRevocationLists() const;
|
||||
void addRevocationList(RevocationList&&);
|
||||
void addRevocationList(std::shared_ptr<RevocationList>);
|
||||
|
||||
static Certificate generate(const PrivateKey& key, const std::string& name = "dhtnode", Identity ca = {}, bool is_ca = false);
|
||||
|
||||
gnutls_x509_crt_t cert {};
|
||||
@@ -314,6 +395,14 @@ struct Certificate {
|
||||
private:
|
||||
Certificate(const Certificate&) = delete;
|
||||
Certificate& operator=(const Certificate&) = delete;
|
||||
|
||||
struct crlNumberCmp {
|
||||
bool operator() (const std::shared_ptr<RevocationList>& lhs, const std::shared_ptr<RevocationList>& rhs) const {
|
||||
return lhs->getNumber() < rhs->getNumber();
|
||||
}
|
||||
};
|
||||
|
||||
std::set<std::shared_ptr<RevocationList>, crlNumberCmp> revocation_lists;
|
||||
};
|
||||
|
||||
|
||||
@@ -324,14 +413,19 @@ private:
|
||||
* If not set, the generated certificate will be a self-signed CA.
|
||||
* @param key_length stength of the generated private key (bits).
|
||||
*/
|
||||
Identity generateIdentity(const std::string& name, Identity ca, unsigned key_length, bool is_ca);
|
||||
Identity generateIdentity(const std::string& name = "dhtnode", Identity ca = {}, unsigned key_length = 4096);
|
||||
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);
|
||||
|
||||
|
||||
/**
|
||||
* SHA512
|
||||
* Performs SHA512, SHA256 or SHA1, depending on hash_length.
|
||||
* Attempts to choose an hash function with
|
||||
* output size of at least hash_length bytes, Current implementation
|
||||
* will use SHA1 for hash_length up to 20 bytes,
|
||||
* will use SHA256 for hash_length up to 32 bytes,
|
||||
* will use SHA512 for hash_length of 33 bytes and more.
|
||||
*/
|
||||
Blob hash(const Blob& data);
|
||||
OPENDHT_PUBLIC Blob hash(const Blob& data, size_t hash_length = 512/8);
|
||||
|
||||
/**
|
||||
* Generates an encryption key from a text password,
|
||||
@@ -340,19 +434,19 @@ Blob hash(const Blob& data);
|
||||
* that can be transmitted in clear, and will be generated if
|
||||
* not provided (32 bytes).
|
||||
*/
|
||||
Blob stretchKey(const std::string& password, Blob& salt);
|
||||
OPENDHT_PUBLIC Blob stretchKey(const std::string& password, Blob& salt, size_t key_length = 512/8);
|
||||
|
||||
/**
|
||||
* AES-GCM encryption. Key must be 128, 192 or 256 bits long (16, 24 or 32 bytes).
|
||||
*/
|
||||
Blob aesEncrypt(const Blob& data, const Blob& key);
|
||||
Blob aesEncrypt(const Blob& data, const std::string& password);
|
||||
OPENDHT_PUBLIC Blob aesEncrypt(const Blob& data, const Blob& key);
|
||||
OPENDHT_PUBLIC Blob aesEncrypt(const Blob& data, const std::string& password);
|
||||
|
||||
/**
|
||||
* AES-GCM decryption.
|
||||
*/
|
||||
Blob aesDecrypt(const Blob& data, const Blob& key);
|
||||
Blob aesDecrypt(const Blob& data, const std::string& password);
|
||||
OPENDHT_PUBLIC Blob aesDecrypt(const Blob& data, const Blob& key);
|
||||
OPENDHT_PUBLIC Blob aesDecrypt(const Blob& data, const std::string& password);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
// Generic helper definitions for shared library support
|
||||
#if defined _WIN32 || defined __CYGWIN__
|
||||
#define OPENDTH_IMPORT __declspec(dllimport)
|
||||
#define OPENDHT_EXPORT __declspec(dllexport)
|
||||
#define OPENDHT_HIDDEN
|
||||
#else
|
||||
#define OPENDTH_IMPORT __attribute__ ((visibility ("default")))
|
||||
#define OPENDHT_EXPORT __attribute__ ((visibility ("default")))
|
||||
#define OPENDHT_HIDDEN __attribute__ ((visibility ("hidden")))
|
||||
#endif
|
||||
|
||||
// Now we use the generic helper definitions above to define OPENDHT_PUBLIC and OPENDHT_LOCAL.
|
||||
// OPENDHT_PUBLIC is used for the public API symbols. It either DLL imports or DLL exports (or does nothing for static build)
|
||||
// OPENDHT_LOCAL is used for non-api symbols.
|
||||
|
||||
#ifdef opendht_EXPORTS // defined if OpenDHT is compiled as a shared library
|
||||
#ifdef OPENDHT_BUILD // defined if we are building the OpenDHT shared library (instead of using it)
|
||||
#define OPENDHT_PUBLIC OPENDHT_EXPORT
|
||||
#else
|
||||
#define OPENDHT_PUBLIC OPENDTH_IMPORT
|
||||
#endif // OPENDHT_BUILD
|
||||
#define OPENDHT_LOCAL OPENDHT_HIDDEN
|
||||
#else // opendht_EXPORTS is not defined: this means OpenDHT is a static lib.
|
||||
#define OPENDHT_PUBLIC
|
||||
#define OPENDHT_LOCAL
|
||||
#endif // opendht_EXPORTS
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-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
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
#include "value.h"
|
||||
|
||||
MSGPACK_ADD_ENUM(dht::Value::Field)
|
||||
|
||||
namespace dht {
|
||||
enum class ImStatus : uint8_t {
|
||||
NONE = 0,
|
||||
@@ -29,11 +31,11 @@ enum class ImStatus : uint8_t {
|
||||
READ
|
||||
};
|
||||
}
|
||||
MSGPACK_ADD_ENUM(dht::ImStatus);
|
||||
MSGPACK_ADD_ENUM(dht::ImStatus)
|
||||
|
||||
namespace dht {
|
||||
|
||||
class DhtMessage : public Value::Serializable<DhtMessage>
|
||||
class OPENDHT_PUBLIC DhtMessage : public Value::Serializable<DhtMessage>
|
||||
{
|
||||
public:
|
||||
static const ValueType TYPE;
|
||||
@@ -55,11 +57,11 @@ public:
|
||||
|
||||
std::string service;
|
||||
Blob data;
|
||||
MSGPACK_DEFINE(service, data);
|
||||
MSGPACK_DEFINE(service, data)
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class SignedValue : public Value::Serializable<T>
|
||||
class OPENDHT_PUBLIC SignedValue : public Value::Serializable<T>
|
||||
{
|
||||
private:
|
||||
using BaseClass = Value::Serializable<T>;
|
||||
@@ -79,7 +81,7 @@ public:
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class EncryptedValue : public SignedValue<T>
|
||||
class OPENDHT_PUBLIC EncryptedValue : public SignedValue<T>
|
||||
{
|
||||
public:
|
||||
using BaseClass = SignedValue<T>;
|
||||
@@ -103,7 +105,7 @@ public:
|
||||
|
||||
|
||||
|
||||
class ImMessage : public SignedValue<ImMessage>
|
||||
class OPENDHT_PUBLIC ImMessage : public SignedValue<ImMessage>
|
||||
{
|
||||
private:
|
||||
using BaseClass = SignedValue<ImMessage>;
|
||||
@@ -126,10 +128,10 @@ public:
|
||||
long date {0};
|
||||
ImStatus status {ImStatus::NONE};
|
||||
|
||||
MSGPACK_DEFINE_MAP(id, msg, date, status);
|
||||
MSGPACK_DEFINE_MAP(id, msg, date, status)
|
||||
};
|
||||
|
||||
class TrustRequest : public EncryptedValue<TrustRequest>
|
||||
class OPENDHT_PUBLIC TrustRequest : public EncryptedValue<TrustRequest>
|
||||
{
|
||||
private:
|
||||
using BaseClass = EncryptedValue<TrustRequest>;
|
||||
@@ -147,10 +149,11 @@ public:
|
||||
|
||||
std::string service;
|
||||
Blob payload;
|
||||
MSGPACK_DEFINE(service, payload);
|
||||
bool confirm {false};
|
||||
MSGPACK_DEFINE_MAP(service, payload, confirm)
|
||||
};
|
||||
|
||||
class IceCandidates : public EncryptedValue<IceCandidates>
|
||||
class OPENDHT_PUBLIC IceCandidates : public EncryptedValue<IceCandidates>
|
||||
{
|
||||
private:
|
||||
using BaseClass = EncryptedValue<IceCandidates>;
|
||||
@@ -175,7 +178,7 @@ public:
|
||||
pk.pack_bin_body((const char*)ice_data.data(), ice_data.size());
|
||||
#else
|
||||
// hack for backward compatibility with old opendht compiled with msgpack 1.0
|
||||
// remove when enough people have moved to new versions
|
||||
// remove when enough people have moved to new versions
|
||||
pk.pack_array(ice_data.size());
|
||||
for (uint8_t b : ice_data)
|
||||
pk.pack(b);
|
||||
@@ -196,7 +199,7 @@ public:
|
||||
|
||||
/* "Peer" announcement
|
||||
*/
|
||||
class IpServiceAnnouncement : public Value::Serializable<IpServiceAnnouncement>
|
||||
class OPENDHT_PUBLIC IpServiceAnnouncement : public Value::Serializable<IpServiceAnnouncement>
|
||||
{
|
||||
private:
|
||||
using BaseClass = Value::Serializable<IpServiceAnnouncement>;
|
||||
@@ -271,8 +274,8 @@ private:
|
||||
};
|
||||
|
||||
|
||||
extern const std::array<std::reference_wrapper<const ValueType>, 5> DEFAULT_TYPES;
|
||||
OPENDHT_PUBLIC extern const std::array<std::reference_wrapper<const ValueType>, 5> DEFAULT_TYPES;
|
||||
|
||||
extern const std::array<std::reference_wrapper<const ValueType>, 1> DEFAULT_INSECURE_TYPES;
|
||||
OPENDHT_PUBLIC extern const std::array<std::reference_wrapper<const ValueType>, 1> DEFAULT_INSECURE_TYPES;
|
||||
|
||||
}
|
||||
+218
-137
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -14,11 +14,9 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "infohash.h"
|
||||
@@ -28,15 +26,13 @@
|
||||
#include "scheduler.h"
|
||||
#include "routing_table.h"
|
||||
#include "callbacks.h"
|
||||
#include "log_enable.h"
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <queue>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -45,7 +41,9 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
namespace net {
|
||||
struct Request;
|
||||
} /* namespace net */
|
||||
|
||||
/**
|
||||
* Main Dht class.
|
||||
@@ -54,7 +52,7 @@ struct Request;
|
||||
* Must be given open UDP sockets and ::periodic must be
|
||||
* called regularly.
|
||||
*/
|
||||
class Dht {
|
||||
class OPENDHT_PUBLIC Dht {
|
||||
public:
|
||||
|
||||
// [[deprecated]]
|
||||
@@ -104,6 +102,13 @@ public:
|
||||
*/
|
||||
void setLoggers(LogMethod error = NOLOG, LogMethod warn = NOLOG, LogMethod debug = NOLOG);
|
||||
|
||||
/**
|
||||
* Only print logs related to the given InfoHash (if given), or disable filter (if zeroes).
|
||||
*/
|
||||
void setLogFilter(const InfoHash& f) {
|
||||
DHT_LOG.setFilter(f);
|
||||
}
|
||||
|
||||
virtual void registerType(const ValueType& type) {
|
||||
types[type.id] = type;
|
||||
}
|
||||
@@ -117,14 +122,20 @@ public:
|
||||
* The node is not pinged, so this should be
|
||||
* used to bootstrap efficiently from previously known nodes.
|
||||
*/
|
||||
bool insertNode(const InfoHash& id, const sockaddr*, socklen_t);
|
||||
bool insertNode(const NodeExport& n) {
|
||||
return insertNode(n.id, reinterpret_cast<const sockaddr*>(&n.ss), n.sslen);
|
||||
void insertNode(const InfoHash& id, const SockAddr&);
|
||||
void insertNode(const InfoHash& id, const sockaddr* sa, socklen_t salen) {
|
||||
insertNode(id, SockAddr(sa, salen));
|
||||
}
|
||||
void insertNode(const NodeExport& n) {
|
||||
insertNode(n.id, SockAddr(n.ss, n.sslen));
|
||||
}
|
||||
|
||||
int pingNode(const sockaddr*, socklen_t);
|
||||
void pingNode(const sockaddr*, socklen_t, DoneCallbackSimple&& cb={});
|
||||
|
||||
time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr *from, socklen_t fromlen);
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value by searching on all available protocols (IPv4, IPv6),
|
||||
@@ -133,18 +144,32 @@ public:
|
||||
* @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.
|
||||
cb and donecb won't be called again afterward.
|
||||
* @param f a filter function used to prefilter values.
|
||||
*/
|
||||
virtual void get(const InfoHash& key, GetCallback cb, DoneCallback donecb={}, Value::Filter&& f={});
|
||||
virtual void get(const InfoHash& key, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f={}) {
|
||||
get(key, cb, bindDoneCb(donecb), std::forward<Value::Filter>(f));
|
||||
virtual void get(const InfoHash& key, GetCallback cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {});
|
||||
virtual void get(const InfoHash& key, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f={}, Where&& w = {}) {
|
||||
get(key, cb, bindDoneCb(donecb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}) {
|
||||
get(key, bindGetCb(cb), donecb, std::forward<Value::Filter>(f));
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) {
|
||||
get(key, bindGetCb(cb), donecb, std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}) {
|
||||
get(key, bindGetCb(cb), bindDoneCb(donecb), std::forward<Value::Filter>(f));
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}, Where&& w = {}) {
|
||||
get(key, bindGetCb(cb), bindDoneCb(donecb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
/**
|
||||
* Similar to Dht::get, but sends a Query to filter data remotely.
|
||||
* @param key the key for which to query data for.
|
||||
* @param cb a function called when new values are found on the network.
|
||||
* It should return false to stop the operation.
|
||||
* @param donecb a function called when the operation is complete.
|
||||
cb and donecb won't be called again afterward.
|
||||
* @param q a query used to filter values on the remotes before they send a
|
||||
* response.
|
||||
*/
|
||||
virtual void query(const InfoHash& key, QueryCallback cb, DoneCallback done_cb = {}, Query&& q = {});
|
||||
virtual void query(const InfoHash& key, QueryCallback cb, DoneCallbackSimple done_cb = {}, Query&& q = {}) {
|
||||
query(key, cb, bindDoneCb(done_cb), std::forward<Query>(q));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,14 +183,10 @@ public:
|
||||
std::shared_ptr<Value> getLocalById(const InfoHash& key, Value::Id vid) const;
|
||||
|
||||
/**
|
||||
* Announce a value on all available protocols (IPv4, IPv6), and
|
||||
* automatically re-announce when it's about to expire.
|
||||
* Announce a value on all available protocols (IPv4, IPv6).
|
||||
*
|
||||
* The operation will start as soon as the node is connected to the network.
|
||||
* The done callback will be called once, when the first announce succeeds, or fails.
|
||||
*
|
||||
* A "put" operation will never end by itself because the value will need to be
|
||||
* reannounced on a regular basis.
|
||||
* User can call #cancelPut(InfoHash, Value::Id) to cancel a put operation.
|
||||
*/
|
||||
void put(const InfoHash& key,
|
||||
std::shared_ptr<Value>,
|
||||
@@ -221,9 +242,9 @@ public:
|
||||
*
|
||||
* @return a token to cancel the listener later.
|
||||
*/
|
||||
virtual size_t listen(const InfoHash&, GetCallback, Value::Filter&&={});
|
||||
virtual size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}) {
|
||||
return listen(key, bindGetCb(cb), std::forward<Value::Filter>(f));
|
||||
virtual size_t listen(const InfoHash&, GetCallback, Value::Filter&&={}, Where&& w = {});
|
||||
virtual size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w = {}) {
|
||||
return listen(key, bindGetCb(cb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
|
||||
virtual bool cancelListen(const InfoHash&, size_t token);
|
||||
@@ -233,7 +254,12 @@ public:
|
||||
* This will cause the DHT to assume a public IP address change.
|
||||
* The DHT will recontact neighbor nodes, re-register for listen ops etc.
|
||||
*/
|
||||
void connectivityChanged();
|
||||
void connectivityChanged(sa_family_t);
|
||||
void connectivityChanged() {
|
||||
reported_addr.clear();
|
||||
connectivityChanged(AF_INET);
|
||||
connectivityChanged(AF_INET6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of good nodes for local storage saving purposes
|
||||
@@ -244,11 +270,14 @@ public:
|
||||
std::vector<ValuesExport> exportValues() const;
|
||||
void importValues(const std::vector<ValuesExport>&);
|
||||
|
||||
int getNodesStats(sa_family_t af, unsigned *good_return, unsigned *dubious_return, unsigned *cached_return,
|
||||
unsigned *incoming_return) const;
|
||||
NodeStats getNodesStats(sa_family_t af) const;
|
||||
|
||||
std::string getStorageLog() const;
|
||||
std::string getStorageLog(const InfoHash&) const;
|
||||
|
||||
std::string getRoutingTablesLog(sa_family_t) const;
|
||||
std::string getSearchesLog(sa_family_t) const;
|
||||
std::string getSearchLog(const InfoHash&, sa_family_t af = AF_UNSPEC) const;
|
||||
|
||||
void dumpTables() const;
|
||||
std::vector<unsigned> getNodeMessageStats(bool in = false) {
|
||||
@@ -270,10 +299,12 @@ public:
|
||||
return {total_store_size, total_values};
|
||||
}
|
||||
|
||||
std::vector<Address> getPublicAddress(sa_family_t family = 0);
|
||||
std::vector<SockAddr> getPublicAddress(sa_family_t family = 0);
|
||||
|
||||
protected:
|
||||
Logger DHT_LOG;
|
||||
bool logFilerEnable_ {};
|
||||
InfoHash logFiler_ {};
|
||||
|
||||
private:
|
||||
|
||||
@@ -282,8 +313,18 @@ private:
|
||||
the target 8 turn out to be dead. */
|
||||
static constexpr unsigned SEARCH_NODES {14};
|
||||
|
||||
/* Concurrent requests during a search */
|
||||
static constexpr unsigned SEARCH_REQUESTS {4};
|
||||
/* The number of bad nodes is limited in order to help determine
|
||||
* presence of connectivity changes. See
|
||||
* https://github.com/savoirfairelinux/opendht/issues/137 for details.
|
||||
*
|
||||
* According to the tables, 25 is a good average value for big networks. If
|
||||
* the network is small, normal search expiration process will handle the
|
||||
* situation.
|
||||
* */
|
||||
static constexpr unsigned SEARCH_MAX_BAD_NODES {25};
|
||||
|
||||
/* Concurrent search nodes requested count */
|
||||
static constexpr unsigned MAX_REQUESTED_SEARCH_NODES {4};
|
||||
|
||||
/* Number of listening nodes */
|
||||
static constexpr unsigned LISTEN_NODES {4};
|
||||
@@ -305,69 +346,19 @@ private:
|
||||
/* Timeout for listen */
|
||||
static constexpr std::chrono::seconds LISTEN_EXPIRE_TIME {30};
|
||||
|
||||
static constexpr std::chrono::seconds REANNOUNCE_MARGIN {5};
|
||||
static constexpr std::chrono::seconds REANNOUNCE_MARGIN {10};
|
||||
|
||||
static constexpr size_t TOKEN_SIZE {64};
|
||||
static constexpr size_t TOKEN_SIZE {32};
|
||||
|
||||
// internal structures
|
||||
struct SearchNode;
|
||||
|
||||
/**
|
||||
* A single "get" operation data
|
||||
*/
|
||||
struct Get {
|
||||
time_point start;
|
||||
Value::Filter filter;
|
||||
GetCallback get_cb;
|
||||
DoneCallback done_cb;
|
||||
};
|
||||
|
||||
/**
|
||||
* A single "put" operation data
|
||||
*/
|
||||
struct Announce {
|
||||
bool permanent;
|
||||
std::shared_ptr<Value> value;
|
||||
time_point created;
|
||||
DoneCallback callback;
|
||||
};
|
||||
|
||||
/**
|
||||
* A single "listen" operation data
|
||||
*/
|
||||
struct LocalListener {
|
||||
Value::Filter filter;
|
||||
GetCallback get_cb;
|
||||
};
|
||||
|
||||
/**
|
||||
* A search is a list of the nodes we think are responsible
|
||||
* for storing values for a given hash.
|
||||
*/
|
||||
struct Get;
|
||||
struct Announce;
|
||||
struct LocalListener;
|
||||
struct Search;
|
||||
|
||||
struct ValueStorage {
|
||||
std::shared_ptr<Value> data {};
|
||||
time_point time {};
|
||||
|
||||
ValueStorage() {}
|
||||
ValueStorage(const std::shared_ptr<Value>& v, time_point t) : data(v), time(t) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Foreign nodes asking for updates about an InfoHash.
|
||||
*/
|
||||
struct Listener {
|
||||
size_t rid {};
|
||||
time_point time {};
|
||||
|
||||
/*constexpr*/ Listener(size_t rid, time_point t) : rid(rid), time(t) {}
|
||||
|
||||
void refresh(size_t tid, time_point t) {
|
||||
rid = tid;
|
||||
time = t;
|
||||
}
|
||||
};
|
||||
|
||||
struct ValueStorage;
|
||||
class StorageBucket;
|
||||
struct Listener;
|
||||
struct Storage;
|
||||
|
||||
// prevent copy
|
||||
@@ -387,18 +378,21 @@ private:
|
||||
// Only nodes running only as bootstrap nodes should
|
||||
// be put in bootstrap mode.
|
||||
const bool is_bootstrap {false};
|
||||
const bool maintain_storage {false};
|
||||
|
||||
// the stuff
|
||||
RoutingTable buckets {};
|
||||
RoutingTable buckets4 {};
|
||||
RoutingTable buckets6 {};
|
||||
|
||||
std::vector<Storage> store;
|
||||
std::map<InfoHash, Storage> store;
|
||||
std::map<SockAddr, StorageBucket, SockAddr::ipCmp> store_quota;
|
||||
size_t total_values {0};
|
||||
size_t total_store_size {0};
|
||||
size_t max_store_size {DEFAULT_STORAGE_LIMIT};
|
||||
|
||||
std::map<InfoHash, std::shared_ptr<Search>> searches4 {};
|
||||
std::map<InfoHash, std::shared_ptr<Search>> searches6 {};
|
||||
using SearchMap = std::map<InfoHash, std::shared_ptr<Search>>;
|
||||
SearchMap searches4 {};
|
||||
SearchMap searches6 {};
|
||||
uint16_t search_id {0};
|
||||
|
||||
// map a global listen token to IPv4, IPv6 specific listen tokens.
|
||||
@@ -407,13 +401,16 @@ private:
|
||||
size_t listener_token {1};
|
||||
|
||||
// timing
|
||||
Scheduler scheduler {};
|
||||
Scheduler scheduler;
|
||||
std::shared_ptr<Scheduler::Job> nextNodesConfirmation {};
|
||||
std::shared_ptr<Scheduler::Job> nextStorageMaintenance {};
|
||||
time_point mybucket_grow_time {time_point::min()}, mybucket6_grow_time {time_point::min()};
|
||||
|
||||
NetworkEngine network_engine;
|
||||
net::NetworkEngine network_engine;
|
||||
unsigned pending_pings4 {0};
|
||||
unsigned pending_pings6 {0};
|
||||
|
||||
using ReportedAddr = std::pair<unsigned, Address>;
|
||||
using ReportedAddr = std::pair<unsigned, SockAddr>;
|
||||
std::vector<ReportedAddr> reported_addr;
|
||||
|
||||
void rotateSecrets();
|
||||
@@ -421,32 +418,36 @@ private:
|
||||
Blob makeToken(const sockaddr *sa, bool old) const;
|
||||
bool tokenMatch(const Blob& token, const sockaddr *sa) const;
|
||||
|
||||
void reportedAddr(const sockaddr *sa, socklen_t sa_len);
|
||||
void reportedAddr(const SockAddr&);
|
||||
|
||||
// Storage
|
||||
decltype(Dht::store)::iterator findStorage(const InfoHash& id);
|
||||
decltype(Dht::store)::const_iterator findStorage(const InfoHash& id) const;
|
||||
void storageAddListener(const InfoHash& id, const std::shared_ptr<Node>& node, size_t tid, Query&& = {});
|
||||
bool storageStore(const InfoHash& id, const std::shared_ptr<Value>& value, time_point created, const SockAddr* sa = nullptr);
|
||||
void expireStore();
|
||||
void expireStorage(InfoHash h);
|
||||
void expireStore(decltype(store)::iterator);
|
||||
|
||||
void storageAddListener(const InfoHash& id, const std::shared_ptr<Node>& node, size_t tid);
|
||||
bool storageStore(const InfoHash& id, const std::shared_ptr<Value>& value, time_point created);
|
||||
void expireStorage();
|
||||
void storageChanged(Storage& st, ValueStorage&);
|
||||
void storageChanged(const InfoHash& id, Storage& st, ValueStorage&);
|
||||
std::string printStorageLog(const decltype(store)::value_type&) const;
|
||||
void printStorageQuota(std::ostream& out, const decltype(store_quota)::value_type& ip) const;
|
||||
|
||||
/**
|
||||
* For a given storage, if values don't belong there anymore because this
|
||||
* node is too far from the target, values are sent to the appropriate
|
||||
* nodes.
|
||||
*/
|
||||
void dataPersistence();
|
||||
size_t maintainStorage(InfoHash id, bool force=false, DoneCallback donecb=nullptr);
|
||||
void dataPersistence(InfoHash id);
|
||||
size_t maintainStorage(decltype(store)::value_type&, bool force=false, DoneCallback donecb=nullptr);
|
||||
|
||||
// Buckets
|
||||
RoutingTable& buckets(sa_family_t af) { return af == AF_INET ? buckets4 : buckets6; }
|
||||
const RoutingTable& buckets(sa_family_t af) const { return af == AF_INET ? buckets4 : buckets6; }
|
||||
Bucket* findBucket(const InfoHash& id, sa_family_t af) {
|
||||
RoutingTable::iterator b;
|
||||
switch (af) {
|
||||
case AF_INET:
|
||||
b = buckets.findBucket(id);
|
||||
return b == buckets.end() ? nullptr : &(*b);
|
||||
b = buckets4.findBucket(id);
|
||||
return b == buckets4.end() ? nullptr : &(*b);
|
||||
case AF_INET6:
|
||||
b = buckets6.findBucket(id);
|
||||
return b == buckets6.end() ? nullptr : &(*b);
|
||||
@@ -471,52 +472,132 @@ private:
|
||||
|
||||
// Searches
|
||||
|
||||
/**
|
||||
* Low-level method that will perform a search on the DHT for the
|
||||
* specified infohash (id), using the specified IP version (IPv4 or IPv6).
|
||||
* The values can be filtered by an arbitrary provided filter.
|
||||
*/
|
||||
std::shared_ptr<Search> search(const InfoHash& id, sa_family_t af, GetCallback = nullptr, DoneCallback = nullptr, Value::Filter = Value::AllFilter());
|
||||
void announce(const InfoHash& id, sa_family_t af, std::shared_ptr<Value> value, DoneCallback callback, time_point created=time_point::max(), bool permanent = false);
|
||||
size_t listenTo(const InfoHash& id, sa_family_t af, GetCallback cb, Value::Filter f = Value::AllFilter());
|
||||
inline SearchMap& searches(sa_family_t af) { return af == AF_INET ? searches4 : searches6; }
|
||||
inline const SearchMap& searches(sa_family_t af) const { return af == AF_INET ? searches4 : searches6; }
|
||||
|
||||
void bootstrapSearch(Search& sr);
|
||||
Search *findSearch(unsigned short tid, sa_family_t af);
|
||||
/**
|
||||
* Low-level method that will perform a search on the DHT for the specified
|
||||
* infohash (id), using the specified IP version (IPv4 or IPv6).
|
||||
*/
|
||||
std::shared_ptr<Search> search(const InfoHash& id, sa_family_t af, GetCallback = {}, QueryCallback = {}, DoneCallback = {}, Value::Filter = {}, Query q = {});
|
||||
|
||||
void announce(const InfoHash& id, sa_family_t af, std::shared_ptr<Value> value, DoneCallback callback, time_point created=time_point::max(), bool permanent = false);
|
||||
size_t listenTo(const InfoHash& id, sa_family_t af, GetCallback cb, Value::Filter f = Value::AllFilter(), const std::shared_ptr<Query>& q = {});
|
||||
|
||||
/**
|
||||
* Refill the search with good nodes if possible.
|
||||
*
|
||||
* @param sr The search to refill.
|
||||
*
|
||||
* @return the number inserted nodes.
|
||||
*/
|
||||
unsigned refill(Search& sr);
|
||||
void expireSearches();
|
||||
|
||||
void confirmNodes();
|
||||
void expire();
|
||||
|
||||
/**
|
||||
* Generic function to execute when a 'get' request has completed.
|
||||
*
|
||||
* @param status The request passed by the network engine.
|
||||
* @param answer The answer from the network engine.
|
||||
* @param ws A weak pointer to the search concerned by the request.
|
||||
* @param query The query sent to the node.
|
||||
*/
|
||||
void searchNodeGetDone(const net::Request& status,
|
||||
net::NetworkEngine::RequestAnswer&& answer,
|
||||
std::weak_ptr<Search> ws,
|
||||
std::shared_ptr<Query> query);
|
||||
|
||||
/**
|
||||
* Generic function to execute when a 'get' request expires.
|
||||
*
|
||||
* @param status The request passed by the network engine.
|
||||
* @param over Whether we're done to try sending the request to the node
|
||||
* or not. This lets us mark a node as candidate.
|
||||
* @param ws A weak pointer to the search concerned by the request.
|
||||
* @param query The query sent to the node.
|
||||
*/
|
||||
void searchNodeGetExpired(const net::Request& status, bool over, std::weak_ptr<Search> ws, std::shared_ptr<Query> query);
|
||||
|
||||
/**
|
||||
* This method recovers sends individual request for values per id.
|
||||
*
|
||||
* @param ws A weak pointer to the Search.
|
||||
* @param query The initial query passed through the API.
|
||||
* @param n The node to which send the requests.
|
||||
*/
|
||||
void paginate(std::weak_ptr<Search> ws, std::shared_ptr<Query> query, SearchNode* n);
|
||||
|
||||
/**
|
||||
* If update is true, this method will also send message to synced but non-updated search nodes.
|
||||
*/
|
||||
SearchNode* searchSendGetValues(std::shared_ptr<Search> sr, SearchNode *n = nullptr, bool update = true);
|
||||
|
||||
/**
|
||||
* Forwards an 'announce' request for a list of nodes to the network engine.
|
||||
*
|
||||
* @param sr The search for which we want to announce a value.
|
||||
* @param announce The 'announce' structure.
|
||||
*/
|
||||
void searchSendAnnounceValue(const std::shared_ptr<Search>& sr);
|
||||
|
||||
/**
|
||||
* Main process of a Search's operations. This function will demand the
|
||||
* network engine to send requests packets for all pending operations
|
||||
* ('get', 'put' and 'listen').
|
||||
*
|
||||
* @param sr The search to execute its operations.
|
||||
*/
|
||||
void searchStep(std::shared_ptr<Search> sr);
|
||||
void dumpSearch(const Search& sr, std::ostream& out) const;
|
||||
|
||||
bool neighbourhoodMaintenance(RoutingTable&);
|
||||
|
||||
void processMessage(const uint8_t *buf, size_t buflen, const sockaddr *from, socklen_t fromlen);
|
||||
void processMessage(const uint8_t *buf, size_t buflen, const SockAddr&);
|
||||
|
||||
void onError(std::shared_ptr<Request> node, DhtProtocolException e);
|
||||
void onError(std::shared_ptr<net::Request> node, net::DhtProtocolException e);
|
||||
/* when our address is reported by a distant peer. */
|
||||
void onReportedAddr(const InfoHash& id, sockaddr* sa , socklen_t salen);
|
||||
void onReportedAddr(const InfoHash& id, const SockAddr&);
|
||||
/* when we receive a ping request */
|
||||
NetworkEngine::RequestAnswer onPing(std::shared_ptr<Node> node);
|
||||
net::NetworkEngine::RequestAnswer onPing(std::shared_ptr<Node> node);
|
||||
/* when we receive a "find node" request */
|
||||
NetworkEngine::RequestAnswer onFindNode(std::shared_ptr<Node> node, InfoHash& hash, want_t want);
|
||||
void onFindNodeDone(const Request& status, NetworkEngine::RequestAnswer& a, std::shared_ptr<Search> sr);
|
||||
net::NetworkEngine::RequestAnswer onFindNode(std::shared_ptr<Node> node, const InfoHash& hash, want_t want);
|
||||
void onFindNodeDone(const std::shared_ptr<Node>& status,
|
||||
net::NetworkEngine::RequestAnswer& a,
|
||||
std::shared_ptr<Search> sr);
|
||||
/* when we receive a "get values" request */
|
||||
NetworkEngine::RequestAnswer onGetValues(std::shared_ptr<Node> node, InfoHash& hash, want_t want);
|
||||
void onGetValuesDone(const Request& status, NetworkEngine::RequestAnswer& a, std::shared_ptr<Search> sr);
|
||||
net::NetworkEngine::RequestAnswer onGetValues(std::shared_ptr<Node> node,
|
||||
const InfoHash& hash,
|
||||
want_t want,
|
||||
const Query& q);
|
||||
void onGetValuesDone(const std::shared_ptr<Node>& status,
|
||||
net::NetworkEngine::RequestAnswer& a,
|
||||
std::shared_ptr<Search>& sr,
|
||||
const std::shared_ptr<Query>& orig_query);
|
||||
/* when we receive a listen request */
|
||||
NetworkEngine::RequestAnswer onListen(std::shared_ptr<Node> node, InfoHash& hash, Blob& token, size_t rid);
|
||||
void onListenDone(const Request& status, NetworkEngine::RequestAnswer& a, std::shared_ptr<Search>& sr);
|
||||
net::NetworkEngine::RequestAnswer onListen(std::shared_ptr<Node> node,
|
||||
const InfoHash& hash,
|
||||
const Blob& token,
|
||||
size_t socket_id,
|
||||
const Query& query);
|
||||
void onListenDone(const std::shared_ptr<Node>& status,
|
||||
net::NetworkEngine::RequestAnswer& a,
|
||||
std::shared_ptr<Search>& sr);
|
||||
/* when we receive an announce request */
|
||||
NetworkEngine::RequestAnswer onAnnounce(std::shared_ptr<Node> node,
|
||||
InfoHash& hash, Blob& token, std::vector<std::shared_ptr<Value>> v, time_point created);
|
||||
void onAnnounceDone(const Request& status, NetworkEngine::RequestAnswer& a, std::shared_ptr<Search>& sr);
|
||||
net::NetworkEngine::RequestAnswer onAnnounce(std::shared_ptr<Node> node,
|
||||
const InfoHash& hash,
|
||||
const Blob& token,
|
||||
const std::vector<std::shared_ptr<Value>>& v,
|
||||
const time_point& created);
|
||||
net::NetworkEngine::RequestAnswer onRefresh(std::shared_ptr<Node> node,
|
||||
const InfoHash& hash,
|
||||
const Blob& token,
|
||||
const Value::Id& vid);
|
||||
void onAnnounceDone(const std::shared_ptr<Node>& status,
|
||||
net::NetworkEngine::RequestAnswer& a,
|
||||
std::shared_ptr<Search>& sr);
|
||||
};
|
||||
|
||||
}
|
||||
+97
-39
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -24,6 +24,9 @@
|
||||
#include "infohash.h"
|
||||
#include "value.h"
|
||||
#include "callbacks.h"
|
||||
#include "sockaddr.h"
|
||||
#include "log_enable.h"
|
||||
#include "def.h"
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
@@ -46,7 +49,7 @@ struct SecureDhtConfig;
|
||||
* either wait for (expectedly frequent) calls to ::loop() or start an internal
|
||||
* thread that will update the DHT when appropriate.
|
||||
*/
|
||||
class DhtRunner {
|
||||
class OPENDHT_PUBLIC DhtRunner {
|
||||
|
||||
public:
|
||||
typedef std::function<void(NodeStatus, NodeStatus)> StatusCallback;
|
||||
@@ -54,20 +57,20 @@ public:
|
||||
DhtRunner();
|
||||
virtual ~DhtRunner();
|
||||
|
||||
void get(InfoHash id, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter f = Value::AllFilter()) {
|
||||
get(id, bindGetCb(cb), donecb, f);
|
||||
void get(InfoHash id, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter f = Value::AllFilter(), Where w = {}) {
|
||||
get(id, bindGetCb(cb), donecb, f, w);
|
||||
}
|
||||
|
||||
void get(InfoHash id, GetCallbackSimple cb, DoneCallbackSimple donecb={}, Value::Filter f = Value::AllFilter()) {
|
||||
get(id, bindGetCb(cb), donecb, f);
|
||||
void get(InfoHash id, GetCallbackSimple cb, DoneCallbackSimple donecb={}, Value::Filter f = Value::AllFilter(), Where w = {}) {
|
||||
get(id, bindGetCb(cb), donecb, f, w);
|
||||
}
|
||||
|
||||
void get(InfoHash hash, GetCallback vcb, DoneCallback dcb, Value::Filter f={});
|
||||
void get(InfoHash hash, GetCallback vcb, DoneCallback dcb, Value::Filter f={}, Where w = {});
|
||||
|
||||
void get(InfoHash id, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter f = Value::AllFilter()) {
|
||||
get(id, cb, bindDoneCb(donecb), f);
|
||||
void get(InfoHash id, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter f = Value::AllFilter(), Where w = {}) {
|
||||
get(id, cb, bindDoneCb(donecb), f, w);
|
||||
}
|
||||
void get(const std::string& key, GetCallback vcb, DoneCallbackSimple dcb={}, Value::Filter f = Value::AllFilter());
|
||||
void get(const std::string& key, GetCallback vcb, DoneCallbackSimple dcb={}, Value::Filter f = Value::AllFilter(), Where w = {});
|
||||
|
||||
template <class T>
|
||||
void get(InfoHash hash, std::function<bool(std::vector<T>&&)> cb, DoneCallbackSimple dcb={})
|
||||
@@ -96,7 +99,7 @@ public:
|
||||
getFilterSet<T>());
|
||||
}
|
||||
|
||||
std::future<std::vector<std::shared_ptr<dht::Value>>> get(InfoHash key, Value::Filter f = Value::AllFilter()) {
|
||||
std::future<std::vector<std::shared_ptr<dht::Value>>> get(InfoHash key, Value::Filter f = Value::AllFilter(), Where w = {}) {
|
||||
auto p = std::make_shared<std::promise<std::vector<std::shared_ptr< dht::Value >>>>();
|
||||
auto values = std::make_shared<std::vector<std::shared_ptr< dht::Value >>>();
|
||||
get(key, [=](const std::vector<std::shared_ptr<dht::Value>>& vlist) {
|
||||
@@ -105,7 +108,7 @@ public:
|
||||
}, [=](bool) {
|
||||
p->set_value(std::move(*values));
|
||||
},
|
||||
f);
|
||||
f, w);
|
||||
return p->get_future();
|
||||
}
|
||||
|
||||
@@ -122,10 +125,15 @@ public:
|
||||
return p->get_future();
|
||||
}
|
||||
|
||||
std::future<size_t> listen(InfoHash key, GetCallback vcb, Value::Filter f = Value::AllFilter());
|
||||
std::future<size_t> listen(const std::string& key, GetCallback vcb, Value::Filter f = Value::AllFilter());
|
||||
std::future<size_t> listen(InfoHash key, GetCallbackSimple cb, Value::Filter f = Value::AllFilter()) {
|
||||
return listen(key, bindGetCb(cb), f);
|
||||
void query(const InfoHash& hash, QueryCallback cb, DoneCallback done_cb = {}, Query q = {});
|
||||
void query(const InfoHash& hash, QueryCallback cb, DoneCallbackSimple done_cb = {}, Query q = {}) {
|
||||
query(hash, cb, bindDoneCb(done_cb), q);
|
||||
}
|
||||
|
||||
std::future<size_t> listen(InfoHash key, GetCallback vcb, Value::Filter f = Value::AllFilter(), Where w = {});
|
||||
std::future<size_t> listen(const std::string& key, GetCallback vcb, Value::Filter f = Value::AllFilter(), Where w = {});
|
||||
std::future<size_t> listen(InfoHash key, GetCallbackSimple cb, Value::Filter f = Value::AllFilter(), Where w = {}) {
|
||||
return listen(key, bindGetCb(cb), f, w);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
@@ -137,7 +145,7 @@ public:
|
||||
getFilterSet<T>());
|
||||
}
|
||||
template <typename T>
|
||||
std::future<size_t> listen(InfoHash hash, std::function<bool(T&&)> cb, Value::Filter f = Value::AllFilter())
|
||||
std::future<size_t> listen(InfoHash hash, std::function<bool(T&&)> cb, Value::Filter f = Value::AllFilter(), Where w = {})
|
||||
{
|
||||
return listen(hash, [=](const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
for (const auto& v : vals) {
|
||||
@@ -150,22 +158,22 @@ public:
|
||||
}
|
||||
return true;
|
||||
},
|
||||
getFilterSet<T>(f));
|
||||
getFilterSet<T>(f), w);
|
||||
}
|
||||
|
||||
void cancelListen(InfoHash h, size_t token);
|
||||
void cancelListen(InfoHash h, std::shared_future<size_t> token);
|
||||
|
||||
void put(InfoHash hash, std::shared_ptr<Value> value, DoneCallback cb={}, bool permanent = false);
|
||||
void put(InfoHash hash, std::shared_ptr<Value> value, DoneCallbackSimple cb, bool permanent = false) {
|
||||
put(hash, value, bindDoneCb(cb), permanent);
|
||||
void put(InfoHash hash, std::shared_ptr<Value> value, DoneCallback cb={}, time_point created=time_point::max(), bool permanent = false);
|
||||
void put(InfoHash hash, std::shared_ptr<Value> value, DoneCallbackSimple cb, time_point created=time_point::max(), bool permanent = false) {
|
||||
put(hash, value, bindDoneCb(cb), created, permanent);
|
||||
}
|
||||
|
||||
void put(InfoHash hash, Value&& value, DoneCallback cb={}, bool permanent = false);
|
||||
void put(InfoHash hash, Value&& value, DoneCallbackSimple cb, bool permanent = false) {
|
||||
put(hash, std::forward<Value>(value), bindDoneCb(cb), permanent);
|
||||
void put(InfoHash hash, Value&& value, DoneCallback cb={}, time_point created=time_point::max(), bool permanent = false);
|
||||
void put(InfoHash hash, Value&& value, DoneCallbackSimple cb, time_point created=time_point::max(), bool permanent = false) {
|
||||
put(hash, std::forward<Value>(value), bindDoneCb(cb), created, permanent);
|
||||
}
|
||||
void put(const std::string& key, Value&& value, DoneCallbackSimple cb={}, bool permanent = false);
|
||||
void put(const std::string& key, Value&& value, DoneCallbackSimple cb={}, time_point created=time_point::max(), bool permanent = false);
|
||||
|
||||
void cancelPut(const InfoHash& h, const Value::Id& id);
|
||||
|
||||
@@ -191,10 +199,31 @@ public:
|
||||
}
|
||||
void putEncrypted(const std::string& key, InfoHash to, Value&& value, DoneCallback cb={});
|
||||
|
||||
void bootstrap(const char* host, const char* service);
|
||||
void bootstrap(const std::vector<std::pair<sockaddr_storage, socklen_t>>& nodes);
|
||||
/**
|
||||
* 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={});
|
||||
|
||||
/**
|
||||
* 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<NodeExport>& nodes);
|
||||
|
||||
/**
|
||||
* Add host:service to bootstrap nodes, and ping this node.
|
||||
* DNS resolution is performed asynchronously.
|
||||
* When disconnected, all bootstrap nodes added with this method will be tried regularly until connection
|
||||
* to the DHT network is established.
|
||||
*/
|
||||
void bootstrap(const std::string& host, const std::string& service);
|
||||
|
||||
/**
|
||||
* Clear the list of bootstrap added using bootstrap(const std::string&, const std::string&).
|
||||
*/
|
||||
void clearBootstrap();
|
||||
|
||||
/**
|
||||
* Inform the DHT of lower-layer connectivity changes.
|
||||
* This will cause the DHT to assume an IP address change.
|
||||
@@ -212,7 +241,7 @@ public:
|
||||
* Returns the currently bound address.
|
||||
* @param f: address family of the bound address to retreive.
|
||||
*/
|
||||
const Address& getBound(sa_family_t f = AF_INET) const {
|
||||
const SockAddr& getBound(sa_family_t f = AF_INET) const {
|
||||
return (f == AF_INET) ? bound4 : bound6;
|
||||
}
|
||||
|
||||
@@ -234,6 +263,11 @@ public:
|
||||
|
||||
void setLoggers(LogMethod err = NOLOG, LogMethod warn = NOLOG, LogMethod debug = NOLOG);
|
||||
|
||||
/**
|
||||
* Only print logs related to the given InfoHash (if given), or disable filter (if zeroes).
|
||||
*/
|
||||
void setLogFilter(const InfoHash& f = {});
|
||||
|
||||
void registerType(const ValueType& type);
|
||||
|
||||
void importValues(const std::vector<ValuesExport>& values);
|
||||
@@ -242,14 +276,17 @@ public:
|
||||
return running;
|
||||
}
|
||||
|
||||
int getNodesStats(sa_family_t af, unsigned *good_return, unsigned *dubious_return, unsigned *cached_return, unsigned *incoming_return) const;
|
||||
NodeStats getNodesStats(sa_family_t af) const;
|
||||
unsigned getNodesStats(sa_family_t af, unsigned *good_return, unsigned *dubious_return, unsigned *cached_return, unsigned *incoming_return) const;
|
||||
|
||||
std::vector<unsigned> getNodeMessageStats(bool in = false) const;
|
||||
std::string getStorageLog() const;
|
||||
std::string getStorageLog(const InfoHash&) const;
|
||||
std::string getRoutingTablesLog(sa_family_t af) const;
|
||||
std::string getSearchesLog(sa_family_t af = 0) const;
|
||||
std::vector<Address> getPublicAddress(sa_family_t af = 0);
|
||||
std::vector<std::string> getPublicAddressStr(sa_family_t af = 0);
|
||||
std::string getSearchesLog(sa_family_t af = AF_UNSPEC) const;
|
||||
std::string getSearchLog(const InfoHash&, sa_family_t af = AF_UNSPEC) const;
|
||||
std::vector<SockAddr> getPublicAddress(sa_family_t af = AF_UNSPEC);
|
||||
std::vector<std::string> getPublicAddressStr(sa_family_t af = AF_UNSPEC);
|
||||
|
||||
// securedht methods
|
||||
|
||||
@@ -268,12 +305,14 @@ 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(in_port_t port, const crypto::Identity identity, bool threaded = false, bool is_bootstrap = false) {
|
||||
void run(in_port_t port, const crypto::Identity identity, bool threaded = false, NetId network = 0) {
|
||||
run(port, {
|
||||
/*.dht_config = */{
|
||||
/*.node_config = */{
|
||||
/*.node_id = */{},
|
||||
/*.is_bootstrap = */is_bootstrap
|
||||
/*.network = */network,
|
||||
/*.is_bootstrap = */false,
|
||||
/*.maintain_storage*/false
|
||||
},
|
||||
/*.id = */identity
|
||||
},
|
||||
@@ -324,11 +363,20 @@ public:
|
||||
void join();
|
||||
|
||||
private:
|
||||
static constexpr std::chrono::seconds BOOTSTRAP_PERIOD {10};
|
||||
|
||||
/**
|
||||
* Will try to resolve the list of hostnames `bootstrap_nodes` on seperate
|
||||
* thread and then queue ping requests. This list should contain reliable
|
||||
* nodes so that the DHT node can recover quickly from losing connection
|
||||
* with the network.
|
||||
*/
|
||||
void tryBootstrapContinuously();
|
||||
|
||||
void doRun(const sockaddr_in* sin4, const sockaddr_in6* sin6, SecureDhtConfig config);
|
||||
time_point loop_();
|
||||
|
||||
static std::vector<std::pair<sockaddr_storage, socklen_t>> getAddrInfo(const char* host, const char* service);
|
||||
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);
|
||||
@@ -341,7 +389,17 @@ private:
|
||||
|
||||
std::thread rcv_thread {};
|
||||
std::mutex sock_mtx {};
|
||||
std::vector<std::pair<Blob, std::pair<sockaddr_storage, socklen_t>>> rcv {};
|
||||
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 {};
|
||||
std::thread bootstrap_thread {};
|
||||
/** protects bootstrap_nodes, bootstrap_thread */
|
||||
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 {};
|
||||
@@ -353,8 +411,8 @@ private:
|
||||
status6 {NodeStatus::Disconnected};
|
||||
StatusCallback statusCb {nullptr};
|
||||
|
||||
Address bound4 {};
|
||||
Address bound6 {};
|
||||
SockAddr bound4 {};
|
||||
SockAddr bound6 {};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,534 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Nicolas Reynaud <nicolas.reynaud@savoirfairelinux.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
#include <bitset>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "../value.h"
|
||||
#include "../dhtrunner.h"
|
||||
#include "../infohash.h"
|
||||
|
||||
namespace dht {
|
||||
namespace indexation {
|
||||
|
||||
/*!
|
||||
* @class Prefix
|
||||
* @brief A blob structure which prefixes a Key in the PHT.
|
||||
* @details
|
||||
* Since the PHT structure is a "trie", every node in this structure have a
|
||||
* label which is defined by the path from the root of the trie to the node. If
|
||||
* the node in question is a leaf, *the label is a prefix of all the keys
|
||||
* contained in the leaf*.
|
||||
*/
|
||||
struct OPENDHT_PUBLIC Prefix {
|
||||
Prefix() {}
|
||||
Prefix(InfoHash h) : size_(h.size() * 8), content_(h.begin(), h.end()) { }
|
||||
Prefix(const Blob& d, const Blob& f={}) : size_(d.size()*8), flags_(f), content_(d) { }
|
||||
|
||||
Prefix(const Prefix& p, size_t first) :
|
||||
size_(std::min(first, p.content_.size()*8)),
|
||||
content_(Blob(p.content_.begin(), p.content_.begin()+size_/8))
|
||||
{
|
||||
|
||||
auto rem = size_ % 8;
|
||||
if ( not p.flags_.empty() ) {
|
||||
flags_ = Blob(p.flags_.begin(), p.flags_.begin()+size_/8);
|
||||
if (rem)
|
||||
flags_.push_back(p.flags_[size_/8] & (0xFF << (8 - rem)));
|
||||
}
|
||||
|
||||
if (rem)
|
||||
content_.push_back(p.content_[size_/8] & (0xFF << (8 - rem)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a sub prefix of the Prefix
|
||||
*
|
||||
* @param len lenght of the prefix to get, could be negative
|
||||
* if len is negativ then you will get the prefix
|
||||
* of size of the previous prefix minus len
|
||||
*
|
||||
* @return Sub-prefix of size len or if len is negative sub-prefix of size
|
||||
* of prefix minus len
|
||||
*
|
||||
* @throw out_of_range if len is larger than size of the content
|
||||
*/
|
||||
Prefix getPrefix(ssize_t len) const {
|
||||
if ((size_t)std::abs(len) >= content_.size() * 8)
|
||||
throw std::out_of_range("len larger than prefix size.");
|
||||
if (len < 0)
|
||||
len += size_;
|
||||
|
||||
return Prefix(*this, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags are considered as active if flag is empty or if the flag
|
||||
* at pos 'pos' is active
|
||||
ee *
|
||||
* @see isActiveBit in private function
|
||||
*/
|
||||
bool isFlagActive(size_t pos) const {
|
||||
return flags_.empty() or isActiveBit(flags_, pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see isActiveBit in private function
|
||||
*/
|
||||
bool isContentBitActive(size_t pos) const {
|
||||
return isActiveBit(content_, pos);
|
||||
}
|
||||
|
||||
Prefix getFullSize() { return Prefix(*this, content_.size()*8); }
|
||||
|
||||
/**
|
||||
* This methods gets the prefix of its sibling in the PHT structure.
|
||||
*
|
||||
* @return The prefix of this sibling.
|
||||
*/
|
||||
Prefix getSibling() const {
|
||||
Prefix copy = *this;
|
||||
if ( size_ )
|
||||
copy.swapContentBit(size_ - 1);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
InfoHash hash() const {
|
||||
Blob copy(content_);
|
||||
copy.push_back(size_);
|
||||
return InfoHash::get(copy);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method count total of bit in common between 2 prefix
|
||||
*
|
||||
* @param p1 first prefix to compared
|
||||
* @param p2 second prefix to compared
|
||||
* @return Lenght of the larger common prefix between both
|
||||
*/
|
||||
static inline unsigned commonBits(const Prefix& p1, const Prefix& p2) {
|
||||
unsigned i, j;
|
||||
uint8_t x;
|
||||
auto longest_prefix_size = std::min(p1.size_, p2.size_);
|
||||
|
||||
for (i = 0; i < longest_prefix_size; i++) {
|
||||
if (p1.content_.data()[i] != p2.content_.data()[i]
|
||||
or not p1.isFlagActive(i)
|
||||
or not p2.isFlagActive(i) ) {
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == longest_prefix_size)
|
||||
return 8*longest_prefix_size;
|
||||
|
||||
x = p1.content_.data()[i] ^ p2.content_.data()[i];
|
||||
|
||||
j = 0;
|
||||
while ((x & 0x80) == 0) {
|
||||
x <<= 1;
|
||||
j++;
|
||||
}
|
||||
|
||||
return 8 * i + j;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see doc of swap private function
|
||||
*/
|
||||
void swapContentBit(size_t bit) {
|
||||
swapBit(content_, bit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see doc of swap private function
|
||||
*/
|
||||
void swapFlagBit(size_t bit) {
|
||||
swapBit(flags_, bit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see doc of addPadding private function
|
||||
*/
|
||||
void addPaddingContent(size_t size) {
|
||||
content_ = addPadding(content_, size);
|
||||
}
|
||||
|
||||
void updateFlags() {
|
||||
/* Fill first known bit */
|
||||
auto csize = size_ - flags_.size() * 8;
|
||||
while(csize >= 8) {
|
||||
flags_.push_back(0xFF);
|
||||
csize -= 8;
|
||||
}
|
||||
|
||||
/* if needed fill remaining bit */
|
||||
if ( csize )
|
||||
flags_.push_back(0xFF << (8 - csize));
|
||||
|
||||
/* Complet vector space missing */
|
||||
for ( auto i = flags_.size(); i < content_.size(); i++ )
|
||||
flags_.push_back(0xFF);
|
||||
}
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
size_t size_ {0};
|
||||
|
||||
/* Will contain flags according to content_.
|
||||
If flags_[i] == 0, then content_[i] is unknown
|
||||
else if flags_[i] == 1, then content_[i] is known */
|
||||
Blob flags_ {};
|
||||
Blob content_ {};
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Add a padding to the input blob
|
||||
*
|
||||
* @param toP : Prefix where to add a padding
|
||||
* @param size : Final size of the prefix with padding
|
||||
*
|
||||
* @return Copy of the input Blob but with a padding
|
||||
*/
|
||||
Blob addPadding(Blob toP, size_t size) {
|
||||
Blob copy = toP;
|
||||
for ( auto i = copy.size(); i < size; i++ )
|
||||
copy.push_back(0);
|
||||
|
||||
swapBit(copy, size_ + 1);
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the bit a pos 'pos' is active, i.e. equal to 1
|
||||
*
|
||||
* @param b : Blob to check
|
||||
* @param pos : Position to check
|
||||
*
|
||||
* @return true if the bit is equal to 1, false otherwise
|
||||
*
|
||||
* @throw out_of_range if bit is superior to blob size * 8
|
||||
*/
|
||||
bool isActiveBit(const Blob &b, size_t pos) const {
|
||||
if ( pos >= content_.size() * 8 )
|
||||
throw std::out_of_range("Can't detect active bit at pos, pos larger than prefix size or empty prefix");
|
||||
|
||||
return ((b[pos / 8] >> (7 - (pos % 8)) ) & 1) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap bit at position bit [from 0 to 1 and vice-versa]
|
||||
*
|
||||
* @param b : Blob to swap
|
||||
* @param bit : Bit to swap on b
|
||||
*
|
||||
* @return the input prefix with the bit at pos 'bit' swapped
|
||||
*
|
||||
* @throw out_of_range if bit is superior to blob size * 8
|
||||
*/
|
||||
void swapBit(Blob &b, size_t bit) {
|
||||
if ( bit >= b.size() * 8 )
|
||||
throw std::out_of_range("bit larger than prefix size.");
|
||||
|
||||
size_t offset_bit = (8 - bit) % 8;
|
||||
b[bit / 8] ^= (1 << offset_bit);
|
||||
}
|
||||
};
|
||||
|
||||
using Value = std::pair<InfoHash, dht::Value::Id>;
|
||||
struct OPENDHT_PUBLIC IndexEntry : public dht::Value::Serializable<IndexEntry> {
|
||||
static const ValueType TYPE;
|
||||
|
||||
virtual void unpackValue(const dht::Value& v) {
|
||||
Serializable<IndexEntry>::unpackValue(v);
|
||||
name = v.user_type;
|
||||
}
|
||||
|
||||
virtual dht::Value packValue() const {
|
||||
auto pack = Serializable<IndexEntry>::packValue();
|
||||
pack.user_type = name;
|
||||
return pack;
|
||||
}
|
||||
|
||||
Blob prefix;
|
||||
Value value;
|
||||
std::string name;
|
||||
MSGPACK_DEFINE_MAP(prefix, value)
|
||||
};
|
||||
|
||||
class OPENDHT_PUBLIC Pht {
|
||||
static constexpr const char* INVALID_KEY = "Key does not match the PHT key spec.";
|
||||
|
||||
/* Prefixes the user_type for all dht values put on the DHT */
|
||||
static constexpr const char* INDEX_PREFIX = "index.pht.";
|
||||
|
||||
public:
|
||||
|
||||
/* This is the maximum number of entries per node. This parameter is
|
||||
* critical and influences the traffic a lot during a lookup operation.
|
||||
*/
|
||||
static constexpr const size_t MAX_NODE_ENTRY_COUNT {16};
|
||||
|
||||
/* A key for a an index entry */
|
||||
using Key = std::map<std::string, Blob>;
|
||||
|
||||
/* Specifications of the keys. It defines the number, the length and the
|
||||
* serialization order of fields. */
|
||||
using KeySpec = std::map<std::string, size_t>;
|
||||
using LookupCallback = std::function<void(std::vector<std::shared_ptr<Value>>& values, Prefix p)>;
|
||||
|
||||
typedef void (*LookupCallbackRaw)(std::vector<std::shared_ptr<Value>>* values, Prefix* p, void *user_data);
|
||||
static LookupCallback
|
||||
bindLookupCb(LookupCallbackRaw raw_cb, void* user_data) {
|
||||
if (not raw_cb) return {};
|
||||
return [=](std::vector<std::shared_ptr<Value>>& values, Prefix p) {
|
||||
raw_cb((std::vector<std::shared_ptr<Value>>*) &values, (Prefix*) &p, user_data);
|
||||
};
|
||||
}
|
||||
using LookupCallbackSimple = std::function<void(std::vector<std::shared_ptr<Value>>& values)>;
|
||||
typedef void (*LookupCallbackSimpleRaw)(std::vector<std::shared_ptr<Value>>* values, void *user_data);
|
||||
static LookupCallbackSimple
|
||||
bindLookupCbSimple(LookupCallbackSimpleRaw raw_cb, void* user_data) {
|
||||
if (not raw_cb) return {};
|
||||
return [=](std::vector<std::shared_ptr<Value>>& values) {
|
||||
raw_cb((std::vector<std::shared_ptr<Value>>*) &values, user_data);
|
||||
};
|
||||
}
|
||||
|
||||
Pht(std::string name, KeySpec k_spec, std::shared_ptr<DhtRunner> dht)
|
||||
: name_(INDEX_PREFIX + name), canary_(name_ + ".canary"), keySpec_(k_spec), dht_(dht) {}
|
||||
|
||||
virtual ~Pht () { }
|
||||
|
||||
/**
|
||||
* Lookup a key for a value.
|
||||
*/
|
||||
void lookup(Key k, LookupCallback cb = {}, DoneCallbackSimple done_cb = {}, bool exact_match = true);
|
||||
void lookup(Key k, LookupCallbackSimple cb = {}, DoneCallbackSimple done_cb = {}, bool exact_match = true)
|
||||
{
|
||||
lookup(k, [=](std::vector<std::shared_ptr<Value>>& values, Prefix) { cb(values); }, done_cb, exact_match);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function which call the private one.
|
||||
*
|
||||
* @param k : Key to insert [i.e map of string, blob]
|
||||
* @param v : Value to insert
|
||||
* @param done_cb : Callbakc which going to be call when all the insert is done
|
||||
*/
|
||||
void insert(Key k, Value v, DoneCallbackSimple done_cb = {}) {
|
||||
Prefix p = linearize(k);
|
||||
|
||||
auto lo = std::make_shared<int>(0);
|
||||
auto hi = std::make_shared<int>(p.size_);
|
||||
|
||||
IndexEntry entry;
|
||||
entry.value = v;
|
||||
entry.prefix = p.content_;
|
||||
entry.name = name_;
|
||||
|
||||
Pht::insert(p, entry, lo, hi, clock::now(), true, done_cb);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Insert function which really insert onto the pht
|
||||
*
|
||||
* @param kp : Prefix to insert (linearize the the key)
|
||||
* @param entry : Entry created from the value
|
||||
* @param lo : Lowest point to start in the prefix
|
||||
* @param hi : Highest point to end in the prefix
|
||||
* @param time_p : Timepoint to use for the insertion into the dht (must be < now)
|
||||
* @param check_split : If this flag is true then the algoritm will not use the merge algorithm
|
||||
* @param done_cb : Callback to call when the insert is done
|
||||
*/
|
||||
|
||||
void insert(Prefix kp, IndexEntry entry, std::shared_ptr<int> lo, std::shared_ptr<int> hi, time_point time_p,
|
||||
bool check_split, DoneCallbackSimple done_cb = {});
|
||||
|
||||
class Cache {
|
||||
public:
|
||||
/**
|
||||
* Insert all needed node into the tree according to a prefix
|
||||
* @param p : Prefix that we need to insert
|
||||
*/
|
||||
void insert(const Prefix& p);
|
||||
|
||||
/**
|
||||
* Lookup into the tree to return the maximum prefix length in the cache tree
|
||||
*
|
||||
* @param p : Prefix that we are looking for
|
||||
*
|
||||
* @return : The size of the longest prefix known in the cache between 0 and p.size_
|
||||
*/
|
||||
|
||||
int lookup(const Prefix& p);
|
||||
|
||||
private:
|
||||
static constexpr const size_t MAX_ELEMENT {1024};
|
||||
static constexpr const std::chrono::minutes NODE_EXPIRE_TIME {5};
|
||||
|
||||
struct Node {
|
||||
time_point last_reply; /* Made the assocation between leaves and leaves multimap */
|
||||
std::shared_ptr<Node> parent; /* Share_ptr to the parent, it allow the self destruction of tree */
|
||||
std::weak_ptr<Node> left_child; /* Left child, for bit equal to 1 */
|
||||
std::weak_ptr<Node> right_child; /* Right child, for bit equal to 0 */
|
||||
};
|
||||
|
||||
std::weak_ptr<Node> root_; /* Root of the tree */
|
||||
|
||||
/**
|
||||
* This mutlimap contains all prefix insert in the tree in time order
|
||||
* We could then delete the last one if there is too much node
|
||||
* The tree will self destroy is branch ( thanks to share_ptr )
|
||||
*/
|
||||
std::multimap<time_point, std::shared_ptr<Node>> leaves_;
|
||||
};
|
||||
|
||||
/* Callback used for insert value by using the pht */
|
||||
using RealInsertCallback = std::function<void(std::shared_ptr<Prefix> p, IndexEntry entry)>;
|
||||
using LookupCallbackWrapper = std::function<void(std::vector<std::shared_ptr<IndexEntry>>& values, Prefix p)>;
|
||||
|
||||
/**
|
||||
* Performs a step in the lookup operation. Each steps are performed
|
||||
* asynchronously.
|
||||
*
|
||||
* @param k : Prefix on which the lookup is performed
|
||||
* @param lo : lowest bound on the prefix (where to start)
|
||||
* @param hi : highest bound on the prefix (where to stop)
|
||||
* @param vals : Shared ptr to a vector of IndexEntry (going to contains all values found)
|
||||
* @param cb : Callback to use at the end of the lookupStep (call on the value of vals)
|
||||
* @param done_cb : Callback at the end of the lookupStep
|
||||
* @param max_common_prefix_len: used in the inexacte lookup match case, indicate the longest common prefix found
|
||||
* @param start : If start is set then lo and hi will be ignore for the first step, if the step fail lo and hi will be used
|
||||
* @param all_values : If all value is true then all value met during the lookupstep will be in the vector vals
|
||||
*/
|
||||
void lookupStep(Prefix k, std::shared_ptr<int> lo, std::shared_ptr<int> hi,
|
||||
std::shared_ptr<std::vector<std::shared_ptr<IndexEntry>>> vals,
|
||||
LookupCallbackWrapper cb, DoneCallbackSimple done_cb,
|
||||
std::shared_ptr<unsigned> max_common_prefix_len,
|
||||
int start = -1, bool all_values = false);
|
||||
|
||||
/**
|
||||
* Apply the zcurve algorithm on the list of input prefix
|
||||
*
|
||||
* @param all_prefix : Vector of prefix to interleave
|
||||
*
|
||||
* @return The output prefix where all flags and content are interleaves
|
||||
*/
|
||||
Prefix zcurve(const std::vector<Prefix>& all_prefix) const;
|
||||
|
||||
/**
|
||||
* Linearizes the key into a unidimensional key. A pht only takes
|
||||
* unidimensional key.
|
||||
*
|
||||
* @param Key The initial key.
|
||||
*
|
||||
* @return the prefix of the linearized key.
|
||||
*/
|
||||
virtual Prefix linearize(Key k) const;
|
||||
|
||||
/**
|
||||
* Looking where to put the data cause if there is free space on the node
|
||||
* above then this node will became the real leave.
|
||||
*
|
||||
* @param p Share_ptr on the Prefix to check
|
||||
* @param entry The entry to put at the prefix p
|
||||
* @param end_cb Callback to use at the end of counting
|
||||
*/
|
||||
void getRealPrefix(std::shared_ptr<Prefix> p, IndexEntry entry, RealInsertCallback end_cb );
|
||||
|
||||
/**
|
||||
* Looking where to put the data cause if there is free space on the node
|
||||
* above then this node will became the real leave.
|
||||
*
|
||||
* @param p Share_ptr on the Prefix to check
|
||||
* @param entry The entry to put at the prefix p
|
||||
* @param end_cb Callback to use at the end of counting
|
||||
*/
|
||||
void checkPhtUpdate(Prefix p, IndexEntry entry, time_point time_p);
|
||||
|
||||
/**
|
||||
* Search for the split location by comparing 'compared' to all values in vals.
|
||||
*
|
||||
* @param compared : Value which going to be compared
|
||||
* @param vals : The vector of values to compare with comapred
|
||||
* @return position compared diverge from all others
|
||||
*/
|
||||
size_t foundSplitLocation(Prefix compared, std::shared_ptr<std::vector<std::shared_ptr<IndexEntry>>> vals) {
|
||||
for ( size_t i = 0; i < compared.content_.size() * 8 - 1; i++ )
|
||||
for ( auto const& v : *vals)
|
||||
if ( Prefix(v->prefix).isContentBitActive(i) != compared.isContentBitActive(i) )
|
||||
return i + 1;
|
||||
|
||||
return compared.content_.size() * 8 - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put canary from the split point until the last known canary and add the prefix at the good place
|
||||
*
|
||||
* @param insert : Prefix to insertm but also prefix which going to check where we need to split
|
||||
* @param vals : Vector of vals for the comparaison
|
||||
* @param entry : Entry to put on the pht
|
||||
* @param end_cb : Callback to apply to the insert prefi (here does the insert)
|
||||
*/
|
||||
void split(Prefix insert, std::shared_ptr<std::vector<std::shared_ptr<IndexEntry>>> vals, IndexEntry entry, RealInsertCallback end_cb);
|
||||
|
||||
/**
|
||||
* Tells if the key is valid according to the key spec.
|
||||
*/
|
||||
bool validKey(const Key& k) const {
|
||||
return k.size() == keySpec_.size() and
|
||||
std::equal(k.begin(), k.end(), keySpec_.begin(),
|
||||
[&](const Key::value_type& key, const KeySpec::value_type& key_spec) {
|
||||
return key.first == key_spec.first and key.second.size() <= key_spec.second;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the canary token on the node responsible for the specified
|
||||
* Prefix.
|
||||
*/
|
||||
void updateCanary(Prefix p);
|
||||
|
||||
const std::string name_;
|
||||
const std::string canary_;
|
||||
const KeySpec keySpec_;
|
||||
Cache cache_;
|
||||
std::shared_ptr<DhtRunner> dht_;
|
||||
};
|
||||
|
||||
} /* indexation */
|
||||
} /* dht */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-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
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "def.h"
|
||||
|
||||
#include <msgpack.hpp>
|
||||
|
||||
#ifndef _WIN32
|
||||
@@ -53,7 +55,7 @@ namespace dht {
|
||||
* An InfoHash is a byte array of HASH_LEN bytes.
|
||||
* InfoHashes identify nodes and values in the Dht.
|
||||
*/
|
||||
class InfoHash final : public std::array<uint8_t, HASH_LEN> {
|
||||
class OPENDHT_PUBLIC InfoHash final : public std::array<uint8_t, HASH_LEN> {
|
||||
public:
|
||||
constexpr InfoHash() : std::array<uint8_t, HASH_LEN>() {}
|
||||
constexpr InfoHash(const std::array<uint8_t, HASH_LEN>& h) : std::array<uint8_t, HASH_LEN>(h) {}
|
||||
@@ -96,11 +98,7 @@ public:
|
||||
* Forget about the ``XOR-metric''. An id is just a path from the
|
||||
* root of the tree, so bits are numbered from the start.
|
||||
*/
|
||||
#ifdef WIN32_NATIVE
|
||||
static inline int cmp(const InfoHash& __restrict id1, const InfoHash& __restrict id2) {
|
||||
#else
|
||||
static inline int cmp(const InfoHash& __restrict__ id1, const InfoHash& __restrict__ id2) {
|
||||
#endif
|
||||
static inline int cmp(const InfoHash& id1, const InfoHash& id2) {
|
||||
return std::memcmp(id1.data(), id2.data(), HASH_LEN);
|
||||
}
|
||||
|
||||
@@ -196,7 +194,8 @@ public:
|
||||
|
||||
static InfoHash getRandom();
|
||||
|
||||
friend std::ostream& operator<< (std::ostream& s, const InfoHash& h);
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const InfoHash& h);
|
||||
OPENDHT_PUBLIC friend std::istream& operator>> (std::istream& s, InfoHash& h);
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
|
||||
+10
-40
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <chrono>
|
||||
|
||||
namespace dht {
|
||||
namespace log {
|
||||
@@ -61,48 +62,17 @@ constexpr const Color::Modifier yellow(Color::FG_YELLOW);
|
||||
/**
|
||||
* Print va_list to std::ostream (used for logging).
|
||||
*/
|
||||
void
|
||||
printLog(std::ostream& s, char const* m, va_list args) {
|
||||
static constexpr int BUF_SZ = 8192;
|
||||
char buffer[BUF_SZ];
|
||||
int ret = vsnprintf(buffer, sizeof(buffer), m, args);
|
||||
if (ret < 0)
|
||||
return;
|
||||
s.write(buffer, std::min(ret, BUF_SZ));
|
||||
if (ret >= BUF_SZ)
|
||||
s << "[[TRUNCATED]]";
|
||||
s.put('\n');
|
||||
}
|
||||
OPENDHT_PUBLIC void
|
||||
printLog(std::ostream &s, char const *m, va_list args);
|
||||
|
||||
void
|
||||
enableLogging(dht::DhtRunner& dht)
|
||||
{
|
||||
dht.setLoggers(
|
||||
[](char const* m, va_list args){ std::cerr << red; printLog(std::cerr, m, args); std::cerr << def; },
|
||||
[](char const* m, va_list args){ std::cout << yellow; printLog(std::cout, m, args); std::cout << def; },
|
||||
[](char const* m, va_list args){ printLog(std::cout, m, args); }
|
||||
);
|
||||
}
|
||||
OPENDHT_PUBLIC void
|
||||
enableLogging(dht::DhtRunner &dht);
|
||||
|
||||
void
|
||||
enableFileLogging(dht::DhtRunner& dht, const std::string& path)
|
||||
{
|
||||
auto logfile = std::make_shared<std::fstream>();
|
||||
logfile->open(path, std::ios::out);
|
||||
OPENDHT_PUBLIC void
|
||||
enableFileLogging(dht::DhtRunner &dht, const std::string &path);
|
||||
|
||||
dht.setLoggers(
|
||||
[=](char const* m, va_list args){ printLog(*logfile, m, args); },
|
||||
[=](char const* m, va_list args){ printLog(*logfile, m, args); },
|
||||
[=](char const* m, va_list args){ printLog(*logfile, m, args); }
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
disableLogging(dht::DhtRunner& dht)
|
||||
{
|
||||
dht.setLoggers(dht::NOLOG, dht::NOLOG, dht::NOLOG);
|
||||
}
|
||||
OPENDHT_PUBLIC void
|
||||
disableLogging(dht::DhtRunner &dht);
|
||||
|
||||
} /* log */
|
||||
} /* dht */
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "infohash.h"
|
||||
|
||||
#ifndef OPENDHT_LOG
|
||||
#define OPENDHT_LOG true
|
||||
#endif
|
||||
|
||||
namespace dht {
|
||||
|
||||
// Logging related utility functions
|
||||
|
||||
/**
|
||||
* Dummy function used to disable logging
|
||||
*/
|
||||
inline void NOLOG(char const*, va_list) {}
|
||||
|
||||
/**
|
||||
* Wrapper for logging methods
|
||||
*/
|
||||
struct LogMethod {
|
||||
LogMethod() = default;
|
||||
|
||||
template<typename T>
|
||||
LogMethod(T&& t) : func(std::forward<T>(t)) {}
|
||||
|
||||
void operator()(char const* format, ...) const {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
func(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
void log(char const* format, va_list args) const {
|
||||
func(format, args);
|
||||
}
|
||||
explicit operator bool() const {
|
||||
return (bool)func;
|
||||
}
|
||||
|
||||
void logPrintable(const uint8_t *buf, size_t buflen) const {
|
||||
std::string buf_clean(buflen, '\0');
|
||||
for (size_t i=0; i<buflen; i++)
|
||||
buf_clean[i] = isprint(buf[i]) ? buf[i] : '.';
|
||||
(*this)("%s", buf_clean.c_str());
|
||||
}
|
||||
private:
|
||||
std::function<void(char const*, va_list)> func;
|
||||
};
|
||||
|
||||
struct Logger {
|
||||
LogMethod DEBUG = NOLOG;
|
||||
LogMethod WARN = NOLOG;
|
||||
LogMethod ERR = NOLOG;
|
||||
void setFilter(const InfoHash& f) {
|
||||
filter_ = f;
|
||||
filterEnable_ = filter_ != InfoHash{};
|
||||
}
|
||||
inline void log0(const LogMethod& logger, char const* format, va_list args) const {
|
||||
#if OPENDHT_LOG
|
||||
if (logger and not filterEnable_)
|
||||
logger.log(format, args);
|
||||
#endif
|
||||
}
|
||||
inline void log1(const LogMethod& logger, const InfoHash& f, char const* format, va_list args) const {
|
||||
#if OPENDHT_LOG
|
||||
if (logger and (not filterEnable_ or f == filter_))
|
||||
logger.log(format, args);
|
||||
#endif
|
||||
}
|
||||
inline void log2(const LogMethod& logger, const InfoHash& f1, const InfoHash& f2, char const* format, va_list args) const {
|
||||
#if OPENDHT_LOG
|
||||
if (logger and (not filterEnable_ or f1 == filter_ or f2 == filter_))
|
||||
logger.log(format, args);
|
||||
#endif
|
||||
}
|
||||
inline void d(char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log0(DEBUG, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void d(const InfoHash& f, char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log1(DEBUG, f, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void d(const InfoHash& f1, const InfoHash& f2, char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log2(DEBUG, f1, f2, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void w(char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log0(WARN, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void w(const InfoHash& f, char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log1(WARN, f, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void w(const InfoHash& f1, const InfoHash& f2, char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log2(WARN, f1, f2, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void e(char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log0(ERR, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void e(const InfoHash& f, char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log1(ERR, f, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void e(const InfoHash& f1, const InfoHash& f2, char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log2(ERR, f1, f2, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
private:
|
||||
bool filterEnable_ {false};
|
||||
InfoHash filter_ {};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace dht {
|
||||
namespace net {
|
||||
|
||||
struct TransPrefix : public std::array<uint8_t, 2> {
|
||||
TransPrefix(const std::string& str) : std::array<uint8_t, 2>({{(uint8_t)str[0], (uint8_t)str[1]}}) {}
|
||||
static const TransPrefix PING;
|
||||
static const TransPrefix FIND_NODE;
|
||||
static const TransPrefix GET_VALUES;
|
||||
static const TransPrefix ANNOUNCE_VALUES;
|
||||
static const TransPrefix REFRESH;
|
||||
static const TransPrefix LISTEN;
|
||||
};
|
||||
|
||||
/* Transaction-ids are 4-bytes long, with the first two bytes identifying
|
||||
* the kind of request, and the remaining two a sequence number in
|
||||
* host order.
|
||||
*/
|
||||
struct TransId final : public std::array<uint8_t, 4> {
|
||||
static const constexpr uint16_t INVALID {0};
|
||||
|
||||
TransId() { std::fill_n(begin(), 4, 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]);
|
||||
}
|
||||
|
||||
uint32_t toInt() const {
|
||||
return *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;
|
||||
}
|
||||
|
||||
unsigned length {4};
|
||||
};
|
||||
|
||||
} /* namespace net */
|
||||
} /* dht */
|
||||
+310
-162
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -14,11 +14,9 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "node_cache.h"
|
||||
@@ -28,6 +26,8 @@
|
||||
#include "scheduler.h"
|
||||
#include "utils.h"
|
||||
#include "rng.h"
|
||||
#include "rate_limiter.h"
|
||||
#include "net.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
@@ -38,6 +38,10 @@
|
||||
#include <queue>
|
||||
|
||||
namespace dht {
|
||||
namespace net {
|
||||
|
||||
struct Request;
|
||||
struct Socket;
|
||||
|
||||
#ifndef MSG_CONFIRM
|
||||
#define MSG_CONFIRM 0
|
||||
@@ -48,6 +52,7 @@ public:
|
||||
// sent to another peer (http-like).
|
||||
static const constexpr uint16_t NON_AUTHORITATIVE_INFORMATION {203}; /* incomplete request packet. */
|
||||
static const constexpr uint16_t UNAUTHORIZED {401}; /* wrong tokens. */
|
||||
static const constexpr uint16_t NOT_FOUND {404}; /* storage not found */
|
||||
// for internal use (custom).
|
||||
static const constexpr uint16_t INVALID_TID_SIZE {421}; /* id was truncated. */
|
||||
static const constexpr uint16_t UNKNOWN_TID {422}; /* unknown tid */
|
||||
@@ -58,6 +63,7 @@ public:
|
||||
static const std::string LISTEN_WRONG_TOKEN; /* wrong token in "listen" request */
|
||||
static const std::string PUT_NO_INFOHASH; /* no infohash in "put" request */
|
||||
static const std::string PUT_WRONG_TOKEN; /* got "put" request with wrong token */
|
||||
static const std::string STORAGE_NOT_FOUND; /* got access request for an unknown storage */
|
||||
static const std::string PUT_INVALID_ID; /* invalid id in "put" request */
|
||||
|
||||
DhtProtocolException(uint16_t code, const std::string& msg="", InfoHash failing_node_id={})
|
||||
@@ -91,57 +97,11 @@ struct ParsedMessage;
|
||||
* @param onGetValues callback for "get values" request.
|
||||
* @param onListen callback for "listen" request.
|
||||
* @param onAnnounce callback for "announce" request.
|
||||
* @param onRefresh callback for "refresh" request.
|
||||
*/
|
||||
class NetworkEngine final {
|
||||
struct TransPrefix : public std::array<uint8_t, 2> {
|
||||
TransPrefix(const std::string& str) : std::array<uint8_t, 2>({{(uint8_t)str[0], (uint8_t)str[1]}}) {}
|
||||
static const TransPrefix PING;
|
||||
static const TransPrefix FIND_NODE;
|
||||
static const TransPrefix GET_VALUES;
|
||||
static const TransPrefix ANNOUNCE_VALUES;
|
||||
static const TransPrefix LISTEN;
|
||||
};
|
||||
class NetworkEngine final
|
||||
{
|
||||
public:
|
||||
|
||||
/* Transaction-ids are 4-bytes long, with the first two bytes identifying
|
||||
* the kind of request, and the remaining two a sequence number in
|
||||
* host order.
|
||||
*/
|
||||
struct TransId final : public std::array<uint8_t, 4> {
|
||||
static const constexpr uint16_t INVALID {0};
|
||||
|
||||
TransId() {}
|
||||
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]);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
unsigned length {4};
|
||||
};
|
||||
|
||||
/*!
|
||||
* @class RequestAnswer
|
||||
* @brief Answer for a request.
|
||||
@@ -153,6 +113,7 @@ public:
|
||||
Blob ntoken {};
|
||||
Value::Id vid {};
|
||||
std::vector<std::shared_ptr<Value>> values {};
|
||||
std::vector<std::shared_ptr<FieldValueIndex>> fields {};
|
||||
std::vector<std::shared_ptr<Node>> nodes4 {};
|
||||
std::vector<std::shared_ptr<Node>> nodes6 {};
|
||||
RequestAnswer() {}
|
||||
@@ -160,16 +121,7 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Cancel a request. Setting req->cancelled = true is not enough in the case
|
||||
* a request is "persistent".
|
||||
*/
|
||||
void cancelRequest(std::shared_ptr<Request>& req);
|
||||
|
||||
void connectivityChanged();
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief when we receive an error message.
|
||||
*
|
||||
@@ -195,7 +147,7 @@ private:
|
||||
* @param saddr (type: sockaddr*) sockaddr* pointer containing address ip information.
|
||||
* @param saddr_len (type: socklen_t) lenght of the sockaddr struct.
|
||||
*/
|
||||
std::function<void(const InfoHash&, sockaddr*, socklen_t)> onReportedAddr;
|
||||
std::function<void(const InfoHash&, const SockAddr&)> onReportedAddr;
|
||||
/**
|
||||
* @brief on ping request callback.
|
||||
*
|
||||
@@ -211,7 +163,7 @@ private:
|
||||
* or ipv6.
|
||||
*/
|
||||
std::function<RequestAnswer(std::shared_ptr<Node>,
|
||||
InfoHash&,
|
||||
const InfoHash&,
|
||||
want_t)> onFindNode {};
|
||||
/**
|
||||
* @brief on "get values" request callback.
|
||||
@@ -222,8 +174,9 @@ private:
|
||||
* or ipv6.
|
||||
*/
|
||||
std::function<RequestAnswer(std::shared_ptr<Node>,
|
||||
InfoHash&,
|
||||
want_t)> onGetValues {};
|
||||
const InfoHash&,
|
||||
want_t,
|
||||
const Query&)> onGetValues {};
|
||||
/**
|
||||
* @brief on listen request callback.
|
||||
*
|
||||
@@ -233,9 +186,10 @@ private:
|
||||
* @param rid (type: uint16_t) request id.
|
||||
*/
|
||||
std::function<RequestAnswer(std::shared_ptr<Node>,
|
||||
InfoHash&,
|
||||
Blob&,
|
||||
uint16_t)> onListen {};
|
||||
const InfoHash&,
|
||||
const Blob&,
|
||||
uint32_t,
|
||||
const Query&)> onListen {};
|
||||
/**
|
||||
* @brief on announce request callback.
|
||||
*
|
||||
@@ -246,17 +200,30 @@ private:
|
||||
* @param created (type: time_point) time when the value was created.
|
||||
*/
|
||||
std::function<RequestAnswer(std::shared_ptr<Node>,
|
||||
InfoHash&,
|
||||
Blob&,
|
||||
std::vector<std::shared_ptr<Value>>,
|
||||
time_point)> onAnnounce {};
|
||||
const InfoHash&,
|
||||
const Blob&,
|
||||
const std::vector<std::shared_ptr<Value>>&,
|
||||
const time_point&)> onAnnounce {};
|
||||
/**
|
||||
* @brief on refresh request callback.
|
||||
*
|
||||
* @param node (type: std::shared_ptr<Node>) the requesting node.
|
||||
* @param vhash (type: InfoHash) hash of the value of interest.
|
||||
* @param token (type: Blob) security token.
|
||||
* @param vid (type: Value::id) the value id.
|
||||
*/
|
||||
std::function<RequestAnswer(std::shared_ptr<Node>,
|
||||
const InfoHash&,
|
||||
const Blob&,
|
||||
const Value::Id&)> onRefresh {};
|
||||
|
||||
public:
|
||||
using SocketCb = std::function<void(const std::shared_ptr<Node>&, RequestAnswer&&)>;
|
||||
using RequestCb = std::function<void(const Request&, RequestAnswer&&)>;
|
||||
using RequestExpiredCb = std::function<void(const Request&, bool)>;
|
||||
|
||||
NetworkEngine(Logger& log, Scheduler& scheduler) : myid(zeroes), DHT_LOG(log), scheduler(scheduler) {}
|
||||
NetworkEngine(InfoHash& myid, int s, int s6, Logger& log, Scheduler& scheduler,
|
||||
NetworkEngine(Logger& log, Scheduler& scheduler);
|
||||
NetworkEngine(InfoHash& myid, NetId net, int s, int s6, Logger& log, Scheduler& scheduler,
|
||||
decltype(NetworkEngine::onError) onError,
|
||||
decltype(NetworkEngine::onNewNode) onNewNode,
|
||||
decltype(NetworkEngine::onReportedAddr) onReportedAddr,
|
||||
@@ -264,74 +231,196 @@ public:
|
||||
decltype(NetworkEngine::onFindNode) onFindNode,
|
||||
decltype(NetworkEngine::onGetValues) onGetValues,
|
||||
decltype(NetworkEngine::onListen) onListen,
|
||||
decltype(NetworkEngine::onAnnounce) onAnnounce) :
|
||||
onError(onError), onNewNode(onNewNode), onReportedAddr(onReportedAddr), onPing(onPing), onFindNode(onFindNode),
|
||||
onGetValues(onGetValues), onListen(onListen), onAnnounce(onAnnounce), myid(myid),
|
||||
dht_socket(s), dht_socket6(s6), DHT_LOG(log), scheduler(scheduler)
|
||||
{
|
||||
transaction_id = std::uniform_int_distribution<decltype(transaction_id)>{1}(rd_device);
|
||||
}
|
||||
virtual ~NetworkEngine() {
|
||||
clear();
|
||||
};
|
||||
decltype(NetworkEngine::onAnnounce) onAnnounce,
|
||||
decltype(NetworkEngine::onRefresh) onRefresh);
|
||||
|
||||
virtual ~NetworkEngine();
|
||||
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Sends values (with closest nodes) to a listenner.
|
||||
*
|
||||
* @param sa The address of the listenner.
|
||||
* @param sslen The length of the sockaddr structure.
|
||||
* @param rid The request id of the initial listen request.
|
||||
* @param hash The hash key of the value.
|
||||
* @param want Wether to send ipv4 and/or ipv6 nodes.
|
||||
* @param ntoken Listen security token.
|
||||
* @param nodes The ipv4 closest nodes.
|
||||
* @param nodes6 The ipv6 closest nodes.
|
||||
* @param values The values to send.
|
||||
* @param sa The address of the listenner.
|
||||
* @param sslen The length of the sockaddr structure.
|
||||
* @param socket_id The tid to use to write to the request socket.
|
||||
* @param hash The hash key of the value.
|
||||
* @param want Wether to send ipv4 and/or ipv6 nodes.
|
||||
* @param ntoken Listen security token.
|
||||
* @param nodes The ipv4 closest nodes.
|
||||
* @param nodes6 The ipv6 closest nodes.
|
||||
* @param values The values to send.
|
||||
*/
|
||||
void tellListener(std::shared_ptr<Node> n, uint16_t rid, InfoHash hash, want_t want, Blob ntoken,
|
||||
std::vector<std::shared_ptr<Node>> nodes, std::vector<std::shared_ptr<Node>> nodes6,
|
||||
std::vector<std::shared_ptr<Value>> values);
|
||||
void tellListener(std::shared_ptr<Node> n, uint32_t socket_id, const InfoHash& hash, want_t want, const Blob& ntoken,
|
||||
std::vector<std::shared_ptr<Node>>&& nodes, std::vector<std::shared_ptr<Node>>&& nodes6,
|
||||
std::vector<std::shared_ptr<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(std::shared_ptr<Request>& req);
|
||||
|
||||
void connectivityChanged(sa_family_t);
|
||||
|
||||
/**************
|
||||
* Requests *
|
||||
**************/
|
||||
|
||||
/**
|
||||
* Send a "ping" request to a given node.
|
||||
*
|
||||
* @param n The node.
|
||||
* @param on_done Request callback when the request is completed.
|
||||
* @param on_expired Request callback when the request expires.
|
||||
*
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
std::shared_ptr<Request>
|
||||
sendPing(std::shared_ptr<Node> n, RequestCb on_done, RequestExpiredCb on_expired);
|
||||
sendPing(std::shared_ptr<Node> n, RequestCb&& on_done, RequestExpiredCb&& on_expired);
|
||||
/**
|
||||
* Send a "ping" request to a given node.
|
||||
*
|
||||
* @param sa The node's ip sockaddr info.
|
||||
* @param salen The associated sockaddr struct length.
|
||||
* @param on_done Request callback when the request is completed.
|
||||
* @param on_expired Request callback when the request expires.
|
||||
*
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
std::shared_ptr<Request>
|
||||
sendPing(const sockaddr* sa, socklen_t salen, RequestCb on_done, RequestExpiredCb on_expired) {
|
||||
return sendPing(std::make_shared<Node>(zeroes, sa, salen), on_done, on_expired);
|
||||
sendPing(const sockaddr* sa, socklen_t salen, RequestCb&& on_done, RequestExpiredCb&& on_expired) {
|
||||
return sendPing(std::make_shared<Node>(zeroes, sa, salen),
|
||||
std::forward<RequestCb>(on_done),
|
||||
std::forward<RequestExpiredCb>(on_expired));
|
||||
}
|
||||
std::shared_ptr<Request>
|
||||
sendFindNode(std::shared_ptr<Node> n,
|
||||
const InfoHash& target,
|
||||
want_t want,
|
||||
RequestCb on_done,
|
||||
RequestExpiredCb on_expired);
|
||||
std::shared_ptr<Request>
|
||||
sendGetValues(std::shared_ptr<Node> n,
|
||||
const InfoHash& target,
|
||||
want_t want,
|
||||
RequestCb on_done,
|
||||
RequestExpiredCb on_expired);
|
||||
std::shared_ptr<Request>
|
||||
sendListen(std::shared_ptr<Node> n,
|
||||
const InfoHash& infohash,
|
||||
const Blob& token,
|
||||
RequestCb on_done,
|
||||
RequestExpiredCb on_expired);
|
||||
std::shared_ptr<Request>
|
||||
sendAnnounceValue(std::shared_ptr<Node> n,
|
||||
const InfoHash& infohash,
|
||||
const Value& v,
|
||||
time_point created,
|
||||
const Blob& token,
|
||||
RequestCb on_done,
|
||||
RequestExpiredCb on_expired);
|
||||
/**
|
||||
* Send a "find node" request to a given node.
|
||||
*
|
||||
* @param n The node.
|
||||
* @param target The target hash.
|
||||
* @param want Indicating wether IPv4 or IPv6 are wanted in response.
|
||||
* Use NetworkEngine::want()
|
||||
* @param on_done Request callback when the request is completed.
|
||||
* @param on_expired Request callback when the request expires.
|
||||
*
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
std::shared_ptr<Request> sendFindNode(std::shared_ptr<Node> n,
|
||||
const InfoHash& hash,
|
||||
want_t want,
|
||||
RequestCb&& on_done,
|
||||
RequestExpiredCb&& on_expired);
|
||||
/**
|
||||
* Send a "get" request to a given node.
|
||||
*
|
||||
* @param n The node.
|
||||
* @param hash The target hash.
|
||||
* @param query The query describing filters.
|
||||
* @param token A security token.
|
||||
* @param want Indicating wether IPv4 or IPv6 are wanted in response.
|
||||
* Use NetworkEngine::want()
|
||||
* @param on_done Request callback when the request is completed.
|
||||
* @param on_expired Request callback when the request expires.
|
||||
*
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
std::shared_ptr<Request> sendGetValues(std::shared_ptr<Node> n,
|
||||
const InfoHash& hash,
|
||||
const Query& query,
|
||||
want_t want,
|
||||
RequestCb&& on_done,
|
||||
RequestExpiredCb&& on_expired);
|
||||
/**
|
||||
* Send a "listen" request to a given node.
|
||||
*
|
||||
* @param n The node.
|
||||
* @param hash The storage's hash.
|
||||
* @param query The query describing filters.
|
||||
* @param token A security token.
|
||||
* @param previous The previous request "listen" sent to this node.
|
||||
* @param socket **UNUSED** The socket for further response.
|
||||
*
|
||||
* For backward compatibility purpose, sendListen has to
|
||||
* handle creation of the socket. Therefor, you cannot
|
||||
* use openSocket yourself. TODO: Once we don't support
|
||||
* the old "listen" negociation, sendListen shall not
|
||||
* create the socket itself.
|
||||
*
|
||||
* @param on_done Request callback when the request is completed.
|
||||
* @param on_expired Request callback when the request expires.
|
||||
* @param socket_cb Callback to execute each time new updates arrive on
|
||||
* the socket.
|
||||
*
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
std::shared_ptr<Request> sendListen(std::shared_ptr<Node> n,
|
||||
const InfoHash& hash,
|
||||
const Query& query,
|
||||
const Blob& token,
|
||||
std::shared_ptr<Request> previous,
|
||||
RequestCb&& on_done,
|
||||
RequestExpiredCb&& on_expired,
|
||||
SocketCb&& socket_cb);
|
||||
/**
|
||||
* Send a "announce" request to a given node.
|
||||
*
|
||||
* @param n The node.
|
||||
* @param hash The target hash.
|
||||
* @param created The time when the value was created (avoiding extended
|
||||
* value lifetime)
|
||||
* @param token A security token.
|
||||
* @param on_done Request callback when the request is completed.
|
||||
* @param on_expired Request callback when the request expires.
|
||||
*
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
std::shared_ptr<Request> sendAnnounceValue(std::shared_ptr<Node> n,
|
||||
const InfoHash& hash,
|
||||
const std::shared_ptr<Value>& v,
|
||||
time_point created,
|
||||
const Blob& token,
|
||||
RequestCb&& on_done,
|
||||
RequestExpiredCb&& on_expired);
|
||||
/**
|
||||
* Send a "refresh" request to a given node. Asks a node to keep the
|
||||
* associated value Value.type.expiration more minutes in its storage.
|
||||
*
|
||||
* @param n The node.
|
||||
* @param hash The target hash.
|
||||
* @param vid The value id.
|
||||
* @param token A security token.
|
||||
* @param on_done Request callback when the request is completed.
|
||||
* @param on_expired Request callback when the request expires.
|
||||
*
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
std::shared_ptr<Request> sendRefreshValue(std::shared_ptr<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.
|
||||
*/
|
||||
std::shared_ptr<Socket> openSocket(const std::shared_ptr<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(std::shared_ptr<Socket> socket);
|
||||
|
||||
/**
|
||||
* Parses a message and calls appropriate callbacks.
|
||||
@@ -342,10 +431,12 @@ public:
|
||||
* @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* from, socklen_t fromlen);
|
||||
void processMessage(const uint8_t *buf, size_t buflen, const SockAddr& addr);
|
||||
|
||||
std::shared_ptr<Node> insertNode(const InfoHash& myid, const sockaddr* from, socklen_t fromlen) {
|
||||
return cache.getNode(myid, from, fromlen, scheduler.time(), 0);
|
||||
std::shared_ptr<Node> insertNode(const InfoHash& myid, const SockAddr& addr) {
|
||||
auto n = cache.getNode(myid, addr, scheduler.time(), 0);
|
||||
onNewNode(n, 0);
|
||||
return n;
|
||||
}
|
||||
|
||||
std::vector<unsigned> getNodeMessageStats(bool in) {
|
||||
@@ -357,7 +448,14 @@ public:
|
||||
|
||||
void blacklistNode(const std::shared_ptr<Node>& n);
|
||||
|
||||
std::vector<std::shared_ptr<Node>> getCachedNodes(const InfoHash& id, sa_family_t sa_f, size_t count) {
|
||||
return cache.getCachedNodes(id, sa_f, count);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
struct PartialMessage;
|
||||
|
||||
/***************
|
||||
* Constants *
|
||||
***************/
|
||||
@@ -366,28 +464,29 @@ private:
|
||||
static const constexpr size_t NODE4_INFO_BUF_LEN {26};
|
||||
/* the length of a node info buffer in ipv6 format */
|
||||
static const constexpr size_t NODE6_INFO_BUF_LEN {38};
|
||||
/* TODO */
|
||||
/* after a UDP reply, the period during which we tell the link layer about it */
|
||||
static constexpr std::chrono::seconds UDP_REPLY_TIME {15};
|
||||
|
||||
/* Max. time to receive a full fragmented packet */
|
||||
static constexpr std::chrono::seconds RX_MAX_PACKET_TIME {10};
|
||||
/* Max. time between packet fragments */
|
||||
static constexpr std::chrono::seconds RX_TIMEOUT {3};
|
||||
/* The maximum number of nodes that we snub. There is probably little
|
||||
reason to increase this value. */
|
||||
static constexpr unsigned BLACKLISTED_MAX {10};
|
||||
/* TODO */
|
||||
|
||||
static constexpr size_t MTU {1280};
|
||||
static constexpr size_t MAX_PACKET_VALUE_SIZE {8 * 1024};
|
||||
|
||||
static const std::string my_v;
|
||||
static std::mt19937 rd_device;
|
||||
|
||||
/* DHT info */
|
||||
const InfoHash& myid;
|
||||
const int dht_socket {-1};
|
||||
const int dht_socket6 {-1};
|
||||
const Logger& DHT_LOG;
|
||||
void process(std::unique_ptr<ParsedMessage>&&, const SockAddr& from);
|
||||
|
||||
NodeCache cache {};
|
||||
sockaddr_storage blacklist[BLACKLISTED_MAX] {};
|
||||
unsigned next_blacklisted = 0;
|
||||
bool rateLimit(const SockAddr& addr);
|
||||
|
||||
bool rateLimit();
|
||||
|
||||
static bool isMartian(const sockaddr* sa, socklen_t len);
|
||||
bool isNodeBlacklisted(const sockaddr*, socklen_t) const;
|
||||
static bool isMartian(const SockAddr& addr);
|
||||
bool isNodeBlacklisted(const SockAddr& addr) const;
|
||||
|
||||
void requestStep(std::shared_ptr<Request> req);
|
||||
|
||||
@@ -408,29 +507,34 @@ private:
|
||||
}
|
||||
|
||||
struct MessageStats {
|
||||
unsigned ping {0};
|
||||
unsigned find {0};
|
||||
unsigned get {0};
|
||||
unsigned put {0};
|
||||
unsigned listen {0};
|
||||
unsigned ping {0};
|
||||
unsigned find {0};
|
||||
unsigned get {0};
|
||||
unsigned put {0};
|
||||
unsigned listen {0};
|
||||
unsigned refresh {0};
|
||||
};
|
||||
|
||||
|
||||
// basic wrapper for socket sendto function
|
||||
int send(const char *buf, size_t len, int flags, const sockaddr *sa, socklen_t salen);
|
||||
int send(const char *buf, size_t len, int flags, const SockAddr& addr);
|
||||
|
||||
void sendValueParts(TransId tid, const std::vector<Blob>& svals, const SockAddr& addr);
|
||||
std::vector<Blob> packValueHeader(msgpack::sbuffer&, const std::vector<std::shared_ptr<Value>>&);
|
||||
void maintainRxBuffer(const TransId& tid);
|
||||
|
||||
/*************
|
||||
* Answers *
|
||||
*************/
|
||||
/* answer to a ping request */
|
||||
void sendPong(const sockaddr* sa, socklen_t salen, TransId tid);
|
||||
void sendPong(const SockAddr& addr, TransId tid);
|
||||
/* answer to findnodes/getvalues request */
|
||||
void sendNodesValues(const sockaddr* sa,
|
||||
socklen_t salen,
|
||||
void sendNodesValues(const SockAddr& addr,
|
||||
TransId tid,
|
||||
const Blob& nodes,
|
||||
const Blob& nodes6,
|
||||
const std::vector<std::shared_ptr<Value>>& st,
|
||||
const Query& query,
|
||||
const Blob& token);
|
||||
Blob bufferNodes(sa_family_t af, const InfoHash& id, std::vector<std::shared_ptr<Node>>& nodes);
|
||||
|
||||
@@ -440,28 +544,72 @@ private:
|
||||
std::vector<std::shared_ptr<Node>>& nodes,
|
||||
std::vector<std::shared_ptr<Node>>& nodes6);
|
||||
/* answer to a listen request */
|
||||
void sendListenConfirmation(const sockaddr* sa, socklen_t salen, TransId tid);
|
||||
void sendListenConfirmation(const SockAddr& addr, TransId tid);
|
||||
/* answer to put request */
|
||||
void sendValueAnnounced(const sockaddr* sa, socklen_t salen, TransId, Value::Id);
|
||||
void sendValueAnnounced(const SockAddr& addr, TransId, Value::Id);
|
||||
/* answer in case of error */
|
||||
void sendError(const sockaddr* sa,
|
||||
socklen_t salen,
|
||||
void sendError(const SockAddr& addr,
|
||||
TransId tid,
|
||||
uint16_t code,
|
||||
const std::string& message,
|
||||
bool include_id=false);
|
||||
|
||||
void deserializeNodesValues(ParsedMessage& msg);
|
||||
void deserializeNodes(ParsedMessage& msg);
|
||||
|
||||
std::queue<time_point> rate_limit_time {};
|
||||
static std::mt19937 rd_device;
|
||||
/* DHT info */
|
||||
const InfoHash& myid;
|
||||
const NetId network {0};
|
||||
const int dht_socket {-1};
|
||||
const int dht_socket6 {-1};
|
||||
const Logger& DHT_LOG;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 {};
|
||||
|
||||
// requests handling
|
||||
uint16_t transaction_id {1};
|
||||
std::map<uint16_t, std::shared_ptr<Request>> requests {};
|
||||
std::map<TransId, std::shared_ptr<Request>> requests {};
|
||||
std::map<TransId, PartialMessage> partial_messages;
|
||||
std::map<TransId, std::shared_ptr<Socket>> opened_sockets {};
|
||||
|
||||
MessageStats in_stats {}, out_stats {};
|
||||
std::set<SockAddr> blacklist {};
|
||||
|
||||
Scheduler& scheduler;
|
||||
};
|
||||
|
||||
}
|
||||
} /* namespace net */
|
||||
} /* namespace dht */
|
||||
+35
-24
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -22,62 +22,71 @@
|
||||
|
||||
#include "infohash.h" // includes socket structures
|
||||
#include "utils.h"
|
||||
#include "sockaddr.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
namespace dht {
|
||||
|
||||
namespace net {
|
||||
struct Request;
|
||||
} /* namespace net */
|
||||
|
||||
struct Node {
|
||||
friend class NetworkEngine;
|
||||
|
||||
InfoHash id;
|
||||
|
||||
socklen_t sslen {0};
|
||||
sockaddr_storage ss;
|
||||
SockAddr addr;
|
||||
|
||||
time_point time {time_point::min()}; /* last time eared about */
|
||||
time_point reply_time {time_point::min()}; /* time of last correct reply received */
|
||||
|
||||
Node(const InfoHash& id, const sockaddr* sa, socklen_t salen)
|
||||
: id(id), sslen(salen), ss() {
|
||||
std::copy_n((const uint8_t*)sa, salen, (uint8_t*)&ss);
|
||||
if ((unsigned)salen < sizeof(ss))
|
||||
std::fill_n((uint8_t*)&ss+salen, sizeof(ss)-salen, 0);
|
||||
}
|
||||
: id(id), addr(sa, salen) {}
|
||||
Node(const InfoHash& id, const SockAddr& addr) : id(id), addr(addr) {}
|
||||
|
||||
InfoHash getId() const {
|
||||
return id;
|
||||
}
|
||||
std::pair<const sockaddr*, socklen_t> getAddr() const {
|
||||
return {(const sockaddr*)&ss, sslen};
|
||||
return {(const sockaddr*)&addr.first, addr.second};
|
||||
}
|
||||
std::string getAddrStr() const {
|
||||
return print_addr(ss, sslen);
|
||||
return addr.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Upon this limit, the node expires.
|
||||
*/
|
||||
void authError() {
|
||||
if (++auth_errors > MAX_AUTH_ERRORS)
|
||||
setExpired();
|
||||
}
|
||||
void authSuccess() { auth_errors = 0; }
|
||||
|
||||
bool isExpired() const { return expired_; }
|
||||
bool isGood(time_point now) const;
|
||||
bool isPendingMessage() const;
|
||||
size_t getPendingMessageCount() const;
|
||||
|
||||
NodeExport exportNode() const { return NodeExport {id, ss, sslen}; }
|
||||
sa_family_t getFamily() const { return ss.ss_family; }
|
||||
NodeExport exportNode() const { return NodeExport {id, addr.first, addr.second}; }
|
||||
sa_family_t getFamily() const { return addr.getFamily(); }
|
||||
|
||||
void update(const sockaddr* sa, socklen_t salen);
|
||||
void update(const SockAddr&);
|
||||
|
||||
void requested(std::shared_ptr<Request>& req);
|
||||
void received(time_point now, std::shared_ptr<Request> req);
|
||||
void requested(std::shared_ptr<net::Request>& req);
|
||||
void received(time_point now, std::shared_ptr<net::Request> req);
|
||||
|
||||
void setExpired();
|
||||
|
||||
/**
|
||||
* Resets the state of the node so it's not expired anymore.
|
||||
*/
|
||||
void reset() { expired_ = false; }
|
||||
void reset() { expired_ = false; reply_time = time_point::min(); }
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
friend std::ostream& operator<< (std::ostream& s, const Node& h);
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const Node& h);
|
||||
|
||||
static constexpr const std::chrono::minutes NODE_GOOD_TIME {120};
|
||||
|
||||
@@ -88,16 +97,18 @@ struct Node {
|
||||
static constexpr const std::chrono::seconds MAX_RESPONSE_TIME {1};
|
||||
|
||||
private:
|
||||
/* Number of times we accept authentication errors from this node. */
|
||||
static const constexpr unsigned MAX_AUTH_ERRORS {3};
|
||||
|
||||
std::list<std::weak_ptr<Request>> requests_ {};
|
||||
std::list<std::weak_ptr<net::Request>> requests_ {};
|
||||
unsigned auth_errors {0};
|
||||
bool expired_ {false};
|
||||
|
||||
void clearPendingQueue() {
|
||||
requests_.remove_if([](std::weak_ptr<Request>& w) {
|
||||
requests_.remove_if([](std::weak_ptr<net::Request>& w) {
|
||||
return w.expired();
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -28,7 +28,8 @@ 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* sa, socklen_t sa_len, time_point now, int confirmed);
|
||||
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);
|
||||
|
||||
/**
|
||||
* Reset the connectivity state of every node,
|
||||
@@ -41,7 +42,7 @@ 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* sa, socklen_t sa_len, time_point now, int confirmed);
|
||||
std::shared_ptr<Node> getNode(const InfoHash& id, const SockAddr&, time_point now, bool confirmed);
|
||||
void clearBadNodes();
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "utils.h"
|
||||
#include <queue>
|
||||
|
||||
namespace dht {
|
||||
|
||||
template<size_t Quota, unsigned long Perdiod=1>
|
||||
class RateLimiter {
|
||||
public:
|
||||
/** Clear outdated records and return current quota usage */
|
||||
size_t maintain(const time_point& now) {
|
||||
using namespace std::chrono;
|
||||
while (not records.empty() and duration_cast<seconds>(now - records.front()) >= seconds(Perdiod))
|
||||
records.pop();
|
||||
return records.size();
|
||||
}
|
||||
/** Return false if quota is reached, insert record and return true otherwise. */
|
||||
bool limit(const time_point& now) {
|
||||
if (maintain(now) >= Quota)
|
||||
return false;
|
||||
records.emplace(now);
|
||||
return true;
|
||||
}
|
||||
bool empty() const {
|
||||
return records.empty();
|
||||
}
|
||||
private:
|
||||
std::queue<time_point> records {};
|
||||
};
|
||||
|
||||
}
|
||||
+42
-15
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -18,11 +18,37 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "net.h"
|
||||
|
||||
namespace dht {
|
||||
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.
|
||||
@@ -32,7 +58,8 @@ struct ParsedMessage;
|
||||
* request is done.
|
||||
*/
|
||||
struct Request {
|
||||
friend class dht::NetworkEngine;
|
||||
friend class dht::net::NetworkEngine;
|
||||
|
||||
std::shared_ptr<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. */
|
||||
|
||||
@@ -52,13 +79,13 @@ struct Request {
|
||||
State getState() const { return state_; }
|
||||
|
||||
Request() {}
|
||||
Request(uint16_t tid,
|
||||
Request(TransId tid,
|
||||
std::shared_ptr<Node> node,
|
||||
Blob&& msg,
|
||||
std::function<void(const Request& req_status, ParsedMessage&&)> on_done,
|
||||
std::function<void(const Request& req_status, bool)> on_expired,
|
||||
bool persistent = false) :
|
||||
node(node), on_done(on_done), on_expired(on_expired), tid(tid), msg(std::move(msg)), persistent(persistent) { }
|
||||
std::function<void(const Request&, ParsedMessage&&)> on_done,
|
||||
std::function<void(const Request&, bool)> on_expired,
|
||||
std::shared_ptr<Socket> socket = {}) :
|
||||
node(node), on_done(on_done), on_expired(on_expired), tid(tid), msg(std::move(msg)), socket(socket) { }
|
||||
|
||||
void setExpired() {
|
||||
if (pending()) {
|
||||
@@ -68,11 +95,10 @@ struct Request {
|
||||
}
|
||||
}
|
||||
void setDone(ParsedMessage&& msg) {
|
||||
if (pending() or persistent) {
|
||||
if (pending()) {
|
||||
state_ = Request::State::COMPLETED;
|
||||
on_done(*this, std::forward<ParsedMessage>(msg));
|
||||
if (not persistent)
|
||||
clear();
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,12 +128,13 @@ private:
|
||||
time_point start {time_point::min()}; /* time when the request is created. */
|
||||
time_point last_try {time_point::min()}; /* time of the last attempt to process the request. */
|
||||
|
||||
std::function<void(const Request& req_status, ParsedMessage&&)> on_done {};
|
||||
std::function<void(const Request& req_status, bool)> on_expired {};
|
||||
std::function<void(const Request&, ParsedMessage&&)> on_done {};
|
||||
std::function<void(const Request&, bool)> on_expired {};
|
||||
|
||||
const uint16_t tid {0}; /* the request id. */
|
||||
Blob msg {}; /* the serialized message. */
|
||||
const bool persistent {false}; /* the request is not erased upon completion. */
|
||||
const TransId tid; /* the request id. */
|
||||
Blob msg {}; /* the serialized message. */
|
||||
std::shared_ptr<Socket> socket; /* the socket used for further reponses. */
|
||||
};
|
||||
|
||||
} /* namespace net */
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-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
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
* 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
|
||||
@@ -23,6 +24,8 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
static constexpr unsigned TARGET_NODES {8};
|
||||
|
||||
struct Bucket {
|
||||
Bucket() : cached() {}
|
||||
Bucket(sa_family_t af, const InfoHash& f = {}, time_point t = time_point::min())
|
||||
|
||||
+18
-11
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -22,6 +22,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils.h"
|
||||
#include "log_enable.h"
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
@@ -36,8 +37,10 @@ namespace dht {
|
||||
*/
|
||||
class Scheduler {
|
||||
public:
|
||||
Scheduler(const Logger& l) : DHT_LOG(l) {}
|
||||
|
||||
struct Job {
|
||||
Job(std::function<void()>&& f) : do_(std::forward<std::function<void()>>(f)) {}
|
||||
Job(std::function<void()>&& f) : do_(std::move(f)) {}
|
||||
std::function<void()> do_;
|
||||
};
|
||||
|
||||
@@ -50,7 +53,7 @@ public:
|
||||
* @return pointer to the newly scheduled job.
|
||||
*/
|
||||
std::shared_ptr<Scheduler::Job> add(time_point t, std::function<void()>&& job_func) {
|
||||
auto job = std::make_shared<Job>(std::forward<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);
|
||||
return job;
|
||||
@@ -66,10 +69,14 @@ public:
|
||||
*/
|
||||
void edit(std::shared_ptr<Scheduler::Job>& job, time_point t) {
|
||||
if (not job) {
|
||||
std::cerr << "editing an empty job" << std::endl;
|
||||
DHT_LOG.ERR("editing an empty job");
|
||||
return;
|
||||
}
|
||||
job = add(t, std::move(job->do_));
|
||||
// std::function move doesn't garantee to leave the object empty.
|
||||
// Force clearing old value.
|
||||
auto task = std::move(job->do_);
|
||||
job->do_ = {};
|
||||
job = add(t, std::move(task));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,17 +96,16 @@ public:
|
||||
if (timer->first > now)
|
||||
break;
|
||||
|
||||
const auto& job = *timer->second;
|
||||
if (job.do_)
|
||||
job.do_();
|
||||
auto job = std::move(timer->second);
|
||||
timers.erase(timer);
|
||||
|
||||
if (job->do_)
|
||||
job->do_();
|
||||
}
|
||||
return getNextJobTime();
|
||||
}
|
||||
|
||||
inline time_point getNextJobTime() const {
|
||||
//if (not timers.empty())
|
||||
// std::cout << "Next job in " << print_dt(timers.begin()->first - clock::now()) << std::endl;
|
||||
return timers.empty() ? time_point::max() : timers.begin()->first;
|
||||
}
|
||||
|
||||
@@ -113,6 +119,7 @@ public:
|
||||
private:
|
||||
time_point now {clock::now()};
|
||||
std::multimap<time_point, std::shared_ptr<Job>> timers {}; /* the jobs ordered by time */
|
||||
const Logger& DHT_LOG;
|
||||
};
|
||||
|
||||
}
|
||||
+13
-12
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -40,7 +41,7 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
class SecureDht : public Dht {
|
||||
class OPENDHT_PUBLIC SecureDht : public Dht {
|
||||
public:
|
||||
|
||||
typedef std::function<void(bool)> SignatureCheckCallback;
|
||||
@@ -70,7 +71,7 @@ public:
|
||||
return secureType(std::move(tmp_type));
|
||||
}
|
||||
|
||||
virtual void registerType(const ValueType& type) {
|
||||
virtual void registerType(const ValueType& type) override {
|
||||
Dht::registerType(secureType(type));
|
||||
}
|
||||
virtual void registerType(ValueType&& type) {
|
||||
@@ -85,18 +86,18 @@ public:
|
||||
* If the signature can't be checked, or if the data can't be decrypted, it is not returned.
|
||||
* Public, non-signed & non-encrypted data is retransmitted as-is.
|
||||
*/
|
||||
virtual void get(const InfoHash& id, GetCallback cb, DoneCallback donecb={}, Value::Filter&& = {}) override;
|
||||
virtual void get(const InfoHash& id, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f = {}) override {
|
||||
get(id, cb, bindDoneCb(donecb), std::forward<Value::Filter>(f));
|
||||
virtual void get(const InfoHash& id, GetCallback cb, DoneCallback donecb={}, Value::Filter&& = {}, Where&& w = {}) override;
|
||||
virtual void get(const InfoHash& id, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f = {}, Where&& w = {}) override {
|
||||
get(id, cb, bindDoneCb(donecb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}) override {
|
||||
get(key, bindGetCb(cb), donecb, std::forward<Value::Filter>(f));
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) override {
|
||||
get(key, bindGetCb(cb), donecb, std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}) override {
|
||||
get(key, bindGetCb(cb), bindDoneCb(donecb), std::forward<Value::Filter>(f));
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}, Where&& w = {}) override {
|
||||
get(key, bindGetCb(cb), bindDoneCb(donecb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
|
||||
virtual size_t listen(const InfoHash& id, GetCallback cb, Value::Filter&& = {}) override;
|
||||
virtual size_t listen(const InfoHash& id, GetCallback cb, Value::Filter&& = {}, Where&& w = {}) override;
|
||||
|
||||
/**
|
||||
* Will take ownership of the value, sign it using our private key and put it in the DHT.
|
||||
@@ -165,7 +166,7 @@ private:
|
||||
|
||||
const ValueType CERTIFICATE_TYPE = {
|
||||
8, "Certificate", std::chrono::hours(24 * 7),
|
||||
// A certificate can only be stored at it's public key ID.
|
||||
// A certificate can only be stored at its public key ID.
|
||||
[](InfoHash id, std::shared_ptr<Value>& v, InfoHash, const sockaddr*, socklen_t) {
|
||||
try {
|
||||
crypto::Certificate crt(v->data);
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "def.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/socket.h>
|
||||
#else
|
||||
#include <iso646.h>
|
||||
#include <ws2def.h>
|
||||
#include <ws2tcpip.h>
|
||||
typedef uint16_t sa_family_t;
|
||||
typedef uint16_t in_port_t;
|
||||
#endif
|
||||
|
||||
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> {
|
||||
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(const sockaddr* sa, socklen_t len) : pair<sockaddr_storage, socklen_t>::pair({},len) {
|
||||
if (len > sizeof(sockaddr_storage))
|
||||
throw std::runtime_error("Socket address length is too large");
|
||||
std::copy_n((uint8_t*)sa, len, (uint8_t*)&first);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
bool equals(const SockAddr& o) const {
|
||||
return second == o.second
|
||||
&& std::memcmp((uint8_t*)&first, (uint8_t*)&o.first, second) == 0;
|
||||
}
|
||||
SockAddr& operator=(const SockAddr& o) {
|
||||
std::copy_n((const uint8_t*)&o.first, o.second, (uint8_t*)&first);
|
||||
second = o.second;
|
||||
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; }
|
||||
|
||||
/**
|
||||
* 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;
|
||||
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;
|
||||
}
|
||||
|
||||
return std::memcmp((uint8_t*)&a.first+start, (uint8_t*)&b.first+start, len) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
OPENDHT_PUBLIC bool operator==(const SockAddr& a, const SockAddr& b);
|
||||
|
||||
OPENDHT_PUBLIC std::string printAddr(const SockAddr& addr);
|
||||
|
||||
}
|
||||
+12
-64
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-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
|
||||
@@ -13,24 +13,15 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define WANT4 1
|
||||
#define WANT6 2
|
||||
#include "def.h"
|
||||
|
||||
#include <msgpack.hpp>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/socket.h>
|
||||
#else
|
||||
#include <ws2def.h>
|
||||
#include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
#include <chrono>
|
||||
#include <random>
|
||||
#include <functional>
|
||||
@@ -38,17 +29,14 @@
|
||||
|
||||
#include <cstdarg>
|
||||
|
||||
#define WANT4 1
|
||||
#define WANT6 2
|
||||
|
||||
namespace dht {
|
||||
|
||||
static constexpr unsigned TARGET_NODES {8};
|
||||
|
||||
using Address = std::pair<sockaddr_storage, socklen_t>;
|
||||
using NetId = uint32_t;
|
||||
using want_t = int_fast8_t;
|
||||
|
||||
std::string print_addr(const sockaddr* sa, socklen_t slen);
|
||||
std::string print_addr(const sockaddr_storage& ss, socklen_t sslen);
|
||||
std::string printAddr(const Address& addr);
|
||||
|
||||
template <typename Key, typename Item, typename Condition>
|
||||
void erase_if(std::map<Key, Item>& map, const Condition& condition)
|
||||
{
|
||||
@@ -59,10 +47,10 @@ void erase_if(std::map<Key, Item>& map, const Condition& condition)
|
||||
}
|
||||
}
|
||||
|
||||
class DhtException : public std::runtime_error {
|
||||
class OPENDHT_PUBLIC DhtException : public std::runtime_error {
|
||||
public:
|
||||
DhtException(const std::string &str = "") :
|
||||
std::runtime_error("DhtException occured: " + str) {}
|
||||
std::runtime_error("DhtException occurred: " + str) {}
|
||||
};
|
||||
|
||||
|
||||
@@ -84,9 +72,6 @@ print_dt(DT d) {
|
||||
return std::chrono::duration_cast<std::chrono::duration<double>>(d).count();
|
||||
}
|
||||
|
||||
static /*constexpr*/ const time_point TIME_INVALID = {time_point::min()};
|
||||
static /*constexpr*/ const time_point TIME_MAX {time_point::max()};
|
||||
|
||||
template <typename Duration = duration>
|
||||
class uniform_duration_distribution : public std::uniform_int_distribution<typename Duration::rep> {
|
||||
using Base = std::uniform_int_distribution<typename Duration::rep>;
|
||||
@@ -103,45 +88,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// Logging related utility functions
|
||||
|
||||
/**
|
||||
* Dummy function used to disable logging
|
||||
*/
|
||||
inline void NOLOG(char const*, va_list) {}
|
||||
|
||||
/**
|
||||
* Wrapper for logging methods
|
||||
*/
|
||||
struct LogMethod {
|
||||
LogMethod() = default;
|
||||
|
||||
template<typename T>
|
||||
LogMethod(T&& t) : func(std::forward<T>(t)) {}
|
||||
|
||||
void operator()(char const* format, ...) const {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
func(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void logPrintable(const uint8_t *buf, size_t buflen) const {
|
||||
std::string buf_clean(buflen, '\0');
|
||||
for (size_t i=0; i<buflen; i++)
|
||||
buf_clean[i] = isprint(buf[i]) ? buf[i] : '.';
|
||||
(*this)("%s", buf_clean.c_str());
|
||||
}
|
||||
private:
|
||||
std::function<void(char const*, va_list)> func;
|
||||
};
|
||||
|
||||
struct Logger {
|
||||
LogMethod DEBUG = NOLOG;
|
||||
LogMethod WARN = NOLOG;
|
||||
LogMethod ERR = NOLOG;
|
||||
};
|
||||
|
||||
// Serialization related definitions and utility functions
|
||||
|
||||
using Blob = std::vector<uint8_t>;
|
||||
@@ -149,7 +95,7 @@ using Blob = std::vector<uint8_t>;
|
||||
/**
|
||||
* Provides backward compatibility with msgpack 1.0
|
||||
*/
|
||||
Blob unpackBlob(msgpack::object& o);
|
||||
OPENDHT_PUBLIC Blob unpackBlob(msgpack::object& o);
|
||||
|
||||
template <typename Type>
|
||||
Blob
|
||||
@@ -169,4 +115,6 @@ unpackMsg(Blob b) {
|
||||
|
||||
msgpack::unpacked unpackMsg(Blob b);
|
||||
|
||||
msgpack::object* findMapValue(msgpack::object& map, const std::string& key);
|
||||
|
||||
} // namespace dht
|
||||
+448
-15
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -35,10 +35,12 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
#include <set>
|
||||
|
||||
namespace dht {
|
||||
|
||||
struct Value;
|
||||
struct Query;
|
||||
|
||||
/**
|
||||
* A storage policy is applied once to every incoming value storage requests.
|
||||
@@ -68,9 +70,9 @@ using StorePolicy = std::function<bool(InfoHash key, std::shared_ptr<Value>& val
|
||||
*/
|
||||
using EditPolicy = std::function<bool(InfoHash key, const std::shared_ptr<Value>& old_val, std::shared_ptr<Value>& new_val, InfoHash from, const sockaddr* from_addr, socklen_t from_len)>;
|
||||
|
||||
static constexpr const size_t MAX_VALUE_SIZE {1024 * 14};
|
||||
static constexpr const size_t MAX_VALUE_SIZE {1024 * 64};
|
||||
|
||||
struct ValueType {
|
||||
struct OPENDHT_PUBLIC ValueType {
|
||||
typedef uint16_t Id;
|
||||
|
||||
static bool DEFAULT_STORE_POLICY(InfoHash, std::shared_ptr<Value>& v, InfoHash, const sockaddr*, socklen_t);
|
||||
@@ -112,14 +114,29 @@ struct ValueType {
|
||||
* Values are stored at a given InfoHash in the Dht, but also have a
|
||||
* unique ID to distinguish between values stored at the same location.
|
||||
*/
|
||||
struct Value
|
||||
struct OPENDHT_PUBLIC Value
|
||||
{
|
||||
enum class Field : int {
|
||||
None = 0,
|
||||
Id, /* Value::id */
|
||||
ValueType, /* Value::type */
|
||||
OwnerPk, /* Value::owner */
|
||||
SeqNum, /* Value::seq */
|
||||
UserType, /* Value::user_type */
|
||||
|
||||
COUNT /* the total number of fields */
|
||||
};
|
||||
|
||||
typedef uint64_t Id;
|
||||
static const Id INVALID_ID {0};
|
||||
static const constexpr Id INVALID_ID {0};
|
||||
|
||||
class Filter : public std::function<bool(const Value&)> {
|
||||
using std::function<bool(const Value&)>::function;
|
||||
public:
|
||||
Filter() {}
|
||||
|
||||
template<typename Functor>
|
||||
Filter(Functor f) : std::function<bool(const Value&)>::function(f) {}
|
||||
|
||||
Filter chain(Filter&& f2) {
|
||||
auto f1 = *this;
|
||||
return chain(std::move(f1), std::move(f2));
|
||||
@@ -135,14 +152,17 @@ struct Value
|
||||
return f1(v) and f2(v);
|
||||
};
|
||||
}
|
||||
static Filter chain(std::initializer_list<Filter> l) {
|
||||
const std::vector<Filter> list(l.begin(), l.end());
|
||||
return [list](const Value& v){
|
||||
for (const auto& f : list)
|
||||
static Filter chainAll(std::vector<Filter>&& set) {
|
||||
if (set.empty()) return {};
|
||||
return std::bind([](const Value& v, std::vector<Filter>& s) {
|
||||
for (const auto& f : s)
|
||||
if (f and not f(v))
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
}, std::placeholders::_1, std::move(set));
|
||||
}
|
||||
static Filter chain(std::initializer_list<Filter> l) {
|
||||
return chainAll(std::vector<Filter>(l.begin(), l.end()));
|
||||
}
|
||||
static Filter chainOr(Filter&& f1, Filter&& f2) {
|
||||
if (not f1 or not f2) return AllFilter();
|
||||
@@ -152,8 +172,10 @@ struct Value
|
||||
}
|
||||
};
|
||||
|
||||
/* Sneaky functions disguised in classes */
|
||||
|
||||
static const Filter AllFilter() {
|
||||
return [](const Value&){return true;};
|
||||
return {};
|
||||
}
|
||||
|
||||
static Filter TypeFilter(const ValueType& t) {
|
||||
@@ -162,6 +184,11 @@ struct Value
|
||||
return v.type == tid;
|
||||
};
|
||||
}
|
||||
static Filter TypeFilter(const ValueType::Id& tid) {
|
||||
return [tid](const Value& v) {
|
||||
return v.type == tid;
|
||||
};
|
||||
}
|
||||
|
||||
static Filter IdFilter(const Id id) {
|
||||
return [id](const Value& v) {
|
||||
@@ -169,12 +196,34 @@ struct Value
|
||||
};
|
||||
}
|
||||
|
||||
static Filter recipientFilter(const InfoHash& r) {
|
||||
static Filter RecipientFilter(const InfoHash& r) {
|
||||
return [r](const Value& v) {
|
||||
return v.recipient == r;
|
||||
};
|
||||
}
|
||||
|
||||
static Filter OwnerFilter(const crypto::PublicKey& pk) {
|
||||
return OwnerFilter(pk.getId());
|
||||
}
|
||||
|
||||
static Filter OwnerFilter(const InfoHash& pkh) {
|
||||
return [pkh](const Value& v) {
|
||||
return v.owner and v.owner->getId() == pkh;
|
||||
};
|
||||
}
|
||||
|
||||
static Filter SeqNumFilter(uint16_t seq_no) {
|
||||
return [seq_no](const Value& v) {
|
||||
return v.seq == seq_no;
|
||||
};
|
||||
}
|
||||
|
||||
static Filter UserTypeFilter(const std::string& ut) {
|
||||
return [ut](const Value& v) {
|
||||
return v.user_type == ut;
|
||||
};
|
||||
}
|
||||
|
||||
class SerializableBase
|
||||
{
|
||||
public:
|
||||
@@ -360,7 +409,7 @@ struct Value
|
||||
}
|
||||
|
||||
/** print value for debugging */
|
||||
friend std::ostream& operator<< (std::ostream& s, const Value& v);
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const Value& v);
|
||||
|
||||
std::string toString() const {
|
||||
std::stringstream ss;
|
||||
@@ -415,6 +464,34 @@ struct Value
|
||||
pk.pack(std::string("dat")); msgpack_pack_to_encrypt(pk);
|
||||
}
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack_fields(const std::set<Value::Field>& fields, Packer& pk) const
|
||||
{
|
||||
for (const auto& field : fields)
|
||||
switch (field) {
|
||||
case Value::Field::Id:
|
||||
pk.pack(static_cast<uint64_t>(id));
|
||||
break;
|
||||
case Value::Field::ValueType:
|
||||
pk.pack(static_cast<uint64_t>(type));
|
||||
break;
|
||||
case Value::Field::OwnerPk:
|
||||
if (owner)
|
||||
owner->msgpack_pack(pk);
|
||||
else
|
||||
InfoHash().msgpack_pack(pk);
|
||||
break;
|
||||
case Value::Field::SeqNum:
|
||||
pk.pack(static_cast<uint64_t>(seq));
|
||||
break;
|
||||
case Value::Field::UserType:
|
||||
pk.pack(user_type);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void msgpack_unpack(msgpack::object o);
|
||||
void msgpack_unpack_body(const msgpack::object& o);
|
||||
Blob getPacked() const {
|
||||
@@ -424,6 +501,8 @@ struct Value
|
||||
return {buffer.data(), buffer.data()+buffer.size()};
|
||||
}
|
||||
|
||||
void msgpack_unpack_fields(const std::set<Value::Field>& fields, const msgpack::object& o, unsigned offset);
|
||||
|
||||
Id id {INVALID_ID};
|
||||
|
||||
/**
|
||||
@@ -467,6 +546,360 @@ struct Value
|
||||
|
||||
using ValuesExport = std::pair<InfoHash, Blob>;
|
||||
|
||||
/**
|
||||
* @class FieldValue
|
||||
* @brief Describes a value filter.
|
||||
* @details
|
||||
* This structure holds the value for a specified field. It's type can either be
|
||||
* uint64_t, InfoHash or Blob.
|
||||
*/
|
||||
struct OPENDHT_PUBLIC FieldValue
|
||||
{
|
||||
FieldValue() {}
|
||||
FieldValue(Value::Field f, uint64_t int_value) : field(f), intValue(int_value) {}
|
||||
FieldValue(Value::Field f, InfoHash hash_value) : field(f), hashValue(hash_value) {}
|
||||
FieldValue(Value::Field f, Blob blob_value) : field(f), blobValue(blob_value) {}
|
||||
|
||||
bool operator==(const FieldValue& fd) const;
|
||||
|
||||
// accessors
|
||||
Value::Field getField() const { return field; }
|
||||
uint64_t getInt() const { return intValue; }
|
||||
InfoHash getHash() const { return hashValue; }
|
||||
Blob getBlob() const { return blobValue; }
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& p) const {
|
||||
p.pack_map(2);
|
||||
p.pack(std::string("f")); p.pack(static_cast<uint8_t>(field));
|
||||
|
||||
p.pack(std::string("v"));
|
||||
switch (field) {
|
||||
case Value::Field::Id:
|
||||
case Value::Field::ValueType:
|
||||
p.pack(intValue);
|
||||
break;
|
||||
case Value::Field::OwnerPk:
|
||||
p.pack(hashValue);
|
||||
break;
|
||||
case Value::Field::UserType:
|
||||
p.pack_bin(blobValue.size());
|
||||
p.pack_bin_body((const char*)blobValue.data(), blobValue.size());
|
||||
break;
|
||||
default:
|
||||
throw msgpack::type_error();
|
||||
}
|
||||
}
|
||||
|
||||
void msgpack_unpack(msgpack::object msg) {
|
||||
hashValue = {};
|
||||
blobValue.clear();
|
||||
|
||||
if (auto f = findMapValue(msg, "f"))
|
||||
field = (Value::Field)f->as<unsigned>();
|
||||
else
|
||||
throw msgpack::type_error();
|
||||
|
||||
auto v = findMapValue(msg, "v");
|
||||
if (not v)
|
||||
throw msgpack::type_error();
|
||||
else
|
||||
switch (field) {
|
||||
case Value::Field::Id:
|
||||
case Value::Field::ValueType:
|
||||
intValue = v->as<decltype(intValue)>();
|
||||
break;
|
||||
case Value::Field::OwnerPk:
|
||||
hashValue = v->as<decltype(hashValue)>();
|
||||
break;
|
||||
case Value::Field::UserType:
|
||||
blobValue = unpackBlob(*v);
|
||||
break;
|
||||
default:
|
||||
throw msgpack::type_error();
|
||||
}
|
||||
}
|
||||
|
||||
Value::Filter getLocalFilter() const;
|
||||
|
||||
private:
|
||||
Value::Field field {Value::Field::None};
|
||||
// three possible value types
|
||||
uint64_t intValue {};
|
||||
InfoHash hashValue {};
|
||||
Blob blobValue {};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @struct FieldSelectorDescription
|
||||
* @brief Describes a selection.
|
||||
* @details
|
||||
* This is meant to narrow data to a set of specified fields. This structure is
|
||||
* used to construct a Select structure.
|
||||
*/
|
||||
struct OPENDHT_PUBLIC FieldSelectorDescription
|
||||
{
|
||||
FieldSelectorDescription() {}
|
||||
FieldSelectorDescription(Value::Field f) : field(f) {}
|
||||
|
||||
Value::Field getField() const { return field; }
|
||||
|
||||
bool operator==(const FieldSelectorDescription& fd) const { return field == fd.field; }
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& p) const { p.pack(static_cast<uint8_t>(field)); }
|
||||
void msgpack_unpack(msgpack::object msg) { field = static_cast<Value::Field>(msg.as<int>()); }
|
||||
private:
|
||||
Value::Field field {Value::Field::None};
|
||||
};
|
||||
|
||||
/**
|
||||
* @class Select
|
||||
* @brief Serializable Value field selection.
|
||||
* @details
|
||||
* This is a container for a list of FieldSelectorDescription instances. It
|
||||
* describes a complete SELECT query for dht::Value.
|
||||
*/
|
||||
struct OPENDHT_PUBLIC Select
|
||||
{
|
||||
Select() { }
|
||||
Select(const std::string& q_str);
|
||||
|
||||
bool isSatisfiedBy(const Select& os) const;
|
||||
|
||||
/**
|
||||
* Selects a field of type Value::Field.
|
||||
*
|
||||
* @param field the field to require.
|
||||
*
|
||||
* @return the resulting Select instance.
|
||||
*/
|
||||
Select& field(Value::Field field) {
|
||||
fieldSelection_.emplace_back(field);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the set of selected fields based on previous require* calls.
|
||||
*
|
||||
* @return the set of fields.
|
||||
*/
|
||||
std::set<Value::Field> getSelection() const {
|
||||
std::set<Value::Field> fields {};
|
||||
for (const auto& f : fieldSelection_) {
|
||||
fields.insert(f.getField());
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& pk) const { pk.pack(fieldSelection_); }
|
||||
void msgpack_unpack(const msgpack::object& o) {
|
||||
fieldSelection_.clear();
|
||||
fieldSelection_ = o.as<decltype(fieldSelection_)>();
|
||||
}
|
||||
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<<(std::ostream& s, const dht::Select& q);
|
||||
private:
|
||||
std::vector<FieldSelectorDescription> fieldSelection_ {};
|
||||
};
|
||||
|
||||
/**
|
||||
* @class Where
|
||||
* @brief Serializable dht::Value filter.
|
||||
* @details
|
||||
* This is container for a list of FieldValue instances. It describes a
|
||||
* complete WHERE query for dht::Value.
|
||||
*/
|
||||
struct OPENDHT_PUBLIC Where
|
||||
{
|
||||
Where() { }
|
||||
Where(const std::string& q_str);
|
||||
|
||||
bool isSatisfiedBy(const Where& where) const;
|
||||
|
||||
/**
|
||||
* Adds restriction on Value::Id based on the id argument.
|
||||
*
|
||||
* @param id the id.
|
||||
*
|
||||
* @return the resulting Where instance.
|
||||
*/
|
||||
Where& id(Value::Id id) {
|
||||
filters_.emplace_back(Value::Field::Id, id);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds restriction on Value::ValueType based on the type argument.
|
||||
*
|
||||
* @param type the value type.
|
||||
*
|
||||
* @return the resulting Where instance.
|
||||
*/
|
||||
Where& valueType(ValueType::Id type) {
|
||||
filters_.emplace_back(Value::Field::ValueType, type);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds restriction on Value::OwnerPk based on the owner_pk_hash argument.
|
||||
*
|
||||
* @param owner_pk_hash the owner public key fingerprint.
|
||||
*
|
||||
* @return the resulting Where instance.
|
||||
*/
|
||||
Where& owner(InfoHash owner_pk_hash) {
|
||||
filters_.emplace_back(Value::Field::OwnerPk, owner_pk_hash);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds restriction on Value::OwnerPk based on the owner_pk_hash argument.
|
||||
*
|
||||
* @param owner_pk_hash the owner public key fingerprint.
|
||||
*
|
||||
* @return the resulting Where instance.
|
||||
*/
|
||||
Where& seq(uint16_t seq_no) {
|
||||
filters_.emplace_back(Value::Field::SeqNum, seq_no);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds restriction on Value::UserType based on the user_type argument.
|
||||
*
|
||||
* @param user_type the user type.
|
||||
*
|
||||
* @return the resulting Where instance.
|
||||
*/
|
||||
Where& userType(std::string user_type) {
|
||||
filters_.emplace_back(Value::Field::UserType, Blob {user_type.begin(), user_type.end()});
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the Value::Filter based on the list of field value set.
|
||||
*
|
||||
* @return the resulting Value::Filter.
|
||||
*/
|
||||
Value::Filter getFilter() const {
|
||||
if (filters_.empty()) return {};
|
||||
std::vector<Value::Filter> fset;
|
||||
fset.reserve(filters_.size());
|
||||
for (const auto& f : filters_) {
|
||||
if (auto lf = f.getLocalFilter())
|
||||
fset.emplace_back(std::move(lf));
|
||||
}
|
||||
return Value::Filter::chainAll(std::move(fset));
|
||||
}
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& pk) const { pk.pack(filters_); }
|
||||
void msgpack_unpack(const msgpack::object& o) {
|
||||
filters_.clear();
|
||||
filters_ = o.as<decltype(filters_)>();
|
||||
}
|
||||
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<<(std::ostream& s, const dht::Where& q);
|
||||
|
||||
private:
|
||||
std::vector<FieldValue> filters_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class Query
|
||||
* @brief Describes a query destined to another peer.
|
||||
* @details
|
||||
* This class describes the list of filters on field values and the field
|
||||
* itselves to include in the peer response to a GET operation. See
|
||||
* FieldValue.
|
||||
*/
|
||||
struct OPENDHT_PUBLIC Query
|
||||
{
|
||||
static const std::string QUERY_PARSE_ERROR;
|
||||
|
||||
Query(Select s = {}, Where w = {}, bool none = false) : select(s), where(w), none(none) { };
|
||||
|
||||
/**
|
||||
* Initializes a query based on a SQL-ish formatted string. The abstract
|
||||
* form of such a string is the following:
|
||||
*
|
||||
* [SELECT $field$ [WHERE $field$=$value$]]
|
||||
*
|
||||
* where
|
||||
*
|
||||
* - $field$ = *|id|value_type|owner_pk|user_type
|
||||
* - $value$ = $string$|$integer$
|
||||
* - $string$: a simple string WITHOUT SPACES.
|
||||
* - $integer$: a simple integer.
|
||||
*/
|
||||
Query(std::string q_str) {
|
||||
auto pos_W = q_str.find("WHERE");
|
||||
auto pos_w = q_str.find("where");
|
||||
auto pos = std::min(pos_W != std::string::npos ? pos_W : q_str.size(),
|
||||
pos_w != std::string::npos ? pos_w : q_str.size());
|
||||
select = q_str.substr(0, pos);
|
||||
where = q_str.substr(pos, q_str.size()-pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the query is satisfied by another query.
|
||||
*/
|
||||
bool isSatisfiedBy(const Query& q) const;
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& pk) const {
|
||||
pk.pack_map(2);
|
||||
pk.pack(std::string("s")); pk.pack(select); /* packing field selectors */
|
||||
pk.pack(std::string("w")); pk.pack(where); /* packing filters */
|
||||
}
|
||||
|
||||
void msgpack_unpack(const msgpack::object& o);
|
||||
|
||||
std::string toString() {
|
||||
std::stringstream ss;
|
||||
ss << *this;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<<(std::ostream& s, const dht::Query& q) {
|
||||
return s << "Query[" << q.select << " " << q.where << "]";
|
||||
}
|
||||
|
||||
Select select {};
|
||||
Where where {};
|
||||
bool none {false}; /* When true, any query satisfies this. */
|
||||
};
|
||||
|
||||
/*!
|
||||
* @class FieldValueIndex
|
||||
* @brief An index for field values.
|
||||
* @details
|
||||
* This structures is meant to manipulate a subset of fields normally contained
|
||||
* in Value.
|
||||
*/
|
||||
struct OPENDHT_PUBLIC FieldValueIndex {
|
||||
FieldValueIndex() {}
|
||||
FieldValueIndex(const Value& v, Select s = {});
|
||||
/**
|
||||
* Tells if all the fields of this are contained in the other
|
||||
* FieldValueIndex with the same value.
|
||||
*
|
||||
* @param other The other FieldValueIndex instance.
|
||||
*/
|
||||
bool containedIn(const FieldValueIndex& other) const;
|
||||
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<<(std::ostream& os, const FieldValueIndex& fvi);
|
||||
|
||||
void msgpack_unpack_fields(const std::set<Value::Field>& fields,
|
||||
const msgpack::object& o,
|
||||
unsigned offset);
|
||||
|
||||
std::map<Value::Field, FieldValue> index {};
|
||||
};
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_base_of<Value::SerializableBase, T>::value, T>::type* = nullptr>
|
||||
Value::Filter
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
if USE_CYTHON
|
||||
|
||||
noinst_HEADERS = \
|
||||
opendht.pyx \
|
||||
opendht_cpp.pxd
|
||||
|
||||
PYTHON_INSTALL_RECORD = $(builddir)/install_record.txt
|
||||
|
||||
pybuild.stamp:
|
||||
|
||||
+125
-38
@@ -31,6 +31,7 @@ from libcpp.map cimport map as map
|
||||
from libcpp cimport bool
|
||||
from libcpp.utility cimport pair
|
||||
from libcpp.string cimport string
|
||||
from libcpp.memory cimport shared_ptr
|
||||
|
||||
from cython.parallel import parallel, prange
|
||||
from cython.operator cimport dereference as deref, preincrement as inc, predecrement as dec
|
||||
@@ -40,19 +41,29 @@ cimport opendht_cpp as cpp
|
||||
|
||||
import threading
|
||||
|
||||
cdef inline void lookup_callback(cpp.vector[cpp.shared_ptr[cpp.IndexValue]]* values, cpp.Prefix* p, void *user_data) with gil:
|
||||
cbs = <object>user_data
|
||||
if 'lookup' in cbs and cbs['lookup']:
|
||||
vals = []
|
||||
for val in deref(values):
|
||||
v = IndexValue()
|
||||
v._value = val
|
||||
vals.append(v)
|
||||
cbs['lookup'](vals, p.toString())
|
||||
|
||||
cdef inline void shutdown_callback(void* user_data) with gil:
|
||||
cbs = <object>user_data
|
||||
if 'shutdown' in cbs and cbs['shutdown']:
|
||||
cbs['shutdown']()
|
||||
ref.Py_DECREF(cbs)
|
||||
|
||||
cdef inline bool get_callback(cpp.shared_ptr[cpp.Value] value, void *user_data) with gil:
|
||||
cdef inline bool get_callback(shared_ptr[cpp.Value] value, void *user_data) with gil:
|
||||
cb = (<object>user_data)['get']
|
||||
pv = Value()
|
||||
pv._value = value
|
||||
return cb(pv)
|
||||
|
||||
cdef inline void done_callback(bool done, cpp.vector[cpp.shared_ptr[cpp.Node]]* nodes, void *user_data) with gil:
|
||||
cdef inline void done_callback(bool done, cpp.vector[shared_ptr[cpp.Node]]* nodes, void *user_data) with gil:
|
||||
node_ids = []
|
||||
for n in deref(nodes):
|
||||
h = NodeEntry()
|
||||
@@ -64,6 +75,12 @@ cdef inline void done_callback(bool done, cpp.vector[cpp.shared_ptr[cpp.Node]]*
|
||||
cbs['done'](done, node_ids)
|
||||
ref.Py_DECREF(cbs)
|
||||
|
||||
cdef inline void done_callback_simple(bool done, void *user_data) with gil:
|
||||
cbs = <object>user_data
|
||||
if 'done' in cbs and cbs['done']:
|
||||
cbs['done'](done)
|
||||
ref.Py_DECREF(cbs)
|
||||
|
||||
cdef class _WithID(object):
|
||||
def __repr__(self):
|
||||
return "<%s '%s'>" % (self.__class__.__name__, str(self))
|
||||
@@ -72,7 +89,7 @@ cdef class _WithID(object):
|
||||
|
||||
cdef class InfoHash(_WithID):
|
||||
cdef cpp.InfoHash _infohash
|
||||
def __init__(self, bytes str=b''):
|
||||
def __cinit__(self, bytes str=b''):
|
||||
self._infohash = cpp.InfoHash(str)
|
||||
def __bool__(InfoHash self):
|
||||
return not (self._infohash == cpp.InfoHash())
|
||||
@@ -109,7 +126,7 @@ cdef class InfoHash(_WithID):
|
||||
return h
|
||||
|
||||
cdef class Node(_WithID):
|
||||
cdef cpp.shared_ptr[cpp.Node] _node
|
||||
cdef shared_ptr[cpp.Node] _node
|
||||
def getId(self):
|
||||
h = InfoHash()
|
||||
h._infohash = self._node.get().getId()
|
||||
@@ -120,7 +137,7 @@ cdef class Node(_WithID):
|
||||
return self._node.get().isExpired()
|
||||
|
||||
cdef class NodeEntry(_WithID):
|
||||
cdef cpp.pair[cpp.InfoHash, cpp.shared_ptr[cpp.Node]] _v
|
||||
cdef cpp.pair[cpp.InfoHash, shared_ptr[cpp.Node]] _v
|
||||
def getId(self):
|
||||
h = InfoHash()
|
||||
h._infohash = self._v.first
|
||||
@@ -131,7 +148,7 @@ cdef class NodeEntry(_WithID):
|
||||
return n
|
||||
|
||||
cdef class Value(object):
|
||||
cdef cpp.shared_ptr[cpp.Value] _value
|
||||
cdef shared_ptr[cpp.Value] _value
|
||||
def __init__(self, bytes val=b''):
|
||||
self._value.reset(new cpp.Value(val, len(val)))
|
||||
def __str__(self):
|
||||
@@ -163,8 +180,8 @@ cdef class Value(object):
|
||||
return self._value.get().size()
|
||||
|
||||
cdef class NodeSetIter(object):
|
||||
cdef map[cpp.InfoHash, cpp.shared_ptr[cpp.Node]]* _nodes
|
||||
cdef map[cpp.InfoHash, cpp.shared_ptr[cpp.Node]].iterator _curIter
|
||||
cdef map[cpp.InfoHash, shared_ptr[cpp.Node]]* _nodes
|
||||
cdef map[cpp.InfoHash, shared_ptr[cpp.Node]].iterator _curIter
|
||||
def __init__(self, NodeSet s):
|
||||
self._nodes = &s._nodes
|
||||
self._curIter = self._nodes.begin()
|
||||
@@ -177,7 +194,7 @@ cdef class NodeSetIter(object):
|
||||
return h
|
||||
|
||||
cdef class NodeSet(object):
|
||||
cdef map[cpp.InfoHash, cpp.shared_ptr[cpp.Node]] _nodes
|
||||
cdef map[cpp.InfoHash, shared_ptr[cpp.Node]] _nodes
|
||||
def size(self):
|
||||
return self._nodes.size()
|
||||
def insert(self, NodeEntry l):
|
||||
@@ -199,7 +216,7 @@ cdef class NodeSet(object):
|
||||
return h
|
||||
def __str__(self):
|
||||
s = ''
|
||||
cdef map[cpp.InfoHash, cpp.shared_ptr[cpp.Node]].iterator it = self._nodes.begin()
|
||||
cdef map[cpp.InfoHash, shared_ptr[cpp.Node]].iterator it = self._nodes.begin()
|
||||
while it != self._nodes.end():
|
||||
s += deref(it).first.toString().decode() + ' ' + deref(it).second.get().getAddrStr().decode() + '\n'
|
||||
inc(it)
|
||||
@@ -215,7 +232,7 @@ cdef class PublicKey(_WithID):
|
||||
return h
|
||||
|
||||
cdef class Certificate(_WithID):
|
||||
cdef cpp.shared_ptr[cpp.Certificate] _cert
|
||||
cdef shared_ptr[cpp.Certificate] _cert
|
||||
def getId(self):
|
||||
h = InfoHash()
|
||||
h._infohash = self._cert.get().getId()
|
||||
@@ -254,49 +271,52 @@ cdef class DhtConfig(object):
|
||||
self._config.dht_config.node_config.node_id = id._infohash
|
||||
|
||||
cdef class DhtRunner(_WithID):
|
||||
cdef cpp.DhtRunner* thisptr
|
||||
cdef cpp.shared_ptr[cpp.DhtRunner] thisptr
|
||||
def __cinit__(self):
|
||||
self.thisptr = new cpp.DhtRunner()
|
||||
self.thisptr.reset(new cpp.DhtRunner())
|
||||
def getId(self):
|
||||
h = InfoHash()
|
||||
h._infohash = self.thisptr.getId()
|
||||
h._infohash = self.thisptr.get().getId()
|
||||
return h
|
||||
def getNodeId(self):
|
||||
return self.thisptr.getNodeId().toString()
|
||||
def bootstrap(self, str host, str port):
|
||||
self.thisptr.bootstrap(host.encode(), port.encode())
|
||||
return self.thisptr.get().getNodeId().toString()
|
||||
def bootstrap(self, str host, str port=None):
|
||||
if port:
|
||||
self.thisptr.get().bootstrap(host.encode(), port.encode())
|
||||
else:
|
||||
self.thisptr.get().bootstrap(host.encode(), b'4222')
|
||||
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.run(bind4, bind6, str(port).encode(), config._config)
|
||||
self.thisptr.get().run(bind4, bind6, str(port).encode(), config._config)
|
||||
else:
|
||||
self.thisptr.run(port, config._config)
|
||||
self.thisptr.get().run(port, config._config)
|
||||
def join(self):
|
||||
self.thisptr.join()
|
||||
self.thisptr.get().join()
|
||||
def shutdown(self, shutdown_cb=None):
|
||||
cb_obj = {'shutdown':shutdown_cb}
|
||||
ref.Py_INCREF(cb_obj)
|
||||
self.thisptr.shutdown(cpp.bindShutdownCb(shutdown_callback, <void*>cb_obj))
|
||||
self.thisptr.get().shutdown(cpp.bindShutdownCb(shutdown_callback, <void*>cb_obj))
|
||||
def enableLogging(self):
|
||||
cpp.enableLogging(self.thisptr[0])
|
||||
cpp.enableLogging(self.thisptr.get()[0])
|
||||
def disableLogging(self):
|
||||
cpp.disableLogging(self.thisptr[0])
|
||||
cpp.disableLogging(self.thisptr.get()[0])
|
||||
def enableFileLogging(self, str path):
|
||||
cpp.enableFileLogging(self.thisptr[0], path)
|
||||
cpp.enableFileLogging(self.thisptr.get()[0], path.encode())
|
||||
def isRunning(self):
|
||||
return self.thisptr.isRunning()
|
||||
return self.thisptr.get().isRunning()
|
||||
def getStorageLog(self):
|
||||
return self.thisptr.getStorageLog().decode()
|
||||
return self.thisptr.get().getStorageLog().decode()
|
||||
def getRoutingTablesLog(self, cpp.sa_family_t af):
|
||||
return self.thisptr.getRoutingTablesLog(af).decode()
|
||||
return self.thisptr.get().getRoutingTablesLog(af).decode()
|
||||
def getSearchesLog(self, cpp.sa_family_t af):
|
||||
return self.thisptr.getSearchesLog(af).decode()
|
||||
return self.thisptr.get().getSearchesLog(af).decode()
|
||||
def getNodeMessageStats(self):
|
||||
stats = []
|
||||
cdef cpp.vector[unsigned] res = self.thisptr.getNodeMessageStats(False)
|
||||
cdef cpp.vector[unsigned] res = self.thisptr.get().getNodeMessageStats(False)
|
||||
for n in res:
|
||||
stats.append(n)
|
||||
return stats
|
||||
@@ -304,14 +324,16 @@ cdef class DhtRunner(_WithID):
|
||||
def get(self, InfoHash key, get_cb=None, done_cb=None):
|
||||
"""Retreive values associated with a key on the DHT.
|
||||
|
||||
key -- the key for which to search
|
||||
get_cb -- is set, makes the operation non-blocking. Called when a value is found on the DHT.
|
||||
done_cb -- optional callback used when get_cb is set. Called when the operation is completed.
|
||||
key -- the key for which to search
|
||||
get_cb -- is set, makes the operation non-blocking. Called when a value
|
||||
is found on the DHT.
|
||||
done_cb -- optional callback used when get_cb is set. Called when the
|
||||
operation is completed.
|
||||
"""
|
||||
if get_cb:
|
||||
cb_obj = {'get':get_cb, 'done':done_cb}
|
||||
ref.Py_INCREF(cb_obj)
|
||||
self.thisptr.get(key._infohash, cpp.bindGetCb(get_callback, <void*>cb_obj), cpp.bindDoneCb(done_callback, <void*>cb_obj))
|
||||
self.thisptr.get().get(key._infohash, cpp.bindGetCb(get_callback, <void*>cb_obj), cpp.bindDoneCb(done_callback, <void*>cb_obj))
|
||||
else:
|
||||
lock = threading.Condition()
|
||||
pending = 0
|
||||
@@ -334,14 +356,14 @@ cdef class DhtRunner(_WithID):
|
||||
def put(self, InfoHash key, Value val, done_cb=None):
|
||||
"""Publish a new value on the DHT at key.
|
||||
|
||||
key -- the DHT key where to put the value
|
||||
val -- the value to put on the DHT
|
||||
key -- the DHT key where to put the value
|
||||
val -- the value to put on the DHT
|
||||
done_cb -- optional callback called when the operation is completed.
|
||||
"""
|
||||
if done_cb:
|
||||
cb_obj = {'done':done_cb}
|
||||
ref.Py_INCREF(cb_obj)
|
||||
self.thisptr.put(key._infohash, val._value, cpp.bindDoneCb(done_callback, <void*>cb_obj))
|
||||
self.thisptr.get().put(key._infohash, val._value, cpp.bindDoneCb(done_callback, <void*>cb_obj))
|
||||
else:
|
||||
lock = threading.Condition()
|
||||
pending = 0
|
||||
@@ -365,9 +387,74 @@ cdef class DhtRunner(_WithID):
|
||||
t._cb['cb'] = cb_obj
|
||||
# avoid the callback being destructed if the token is destroyed
|
||||
ref.Py_INCREF(cb_obj)
|
||||
t._t = self.thisptr.listen(t._h, cpp.bindGetCb(get_callback, <void*>cb_obj)).share()
|
||||
t._t = self.thisptr.get().listen(t._h, cpp.bindGetCb(get_callback, <void*>cb_obj)).share()
|
||||
return t
|
||||
def cancelListen(self, ListenToken token):
|
||||
self.thisptr.cancelListen(token._h, token._t)
|
||||
self.thisptr.get().cancelListen(token._h, token._t)
|
||||
ref.Py_DECREF(<object>token._cb['cb'])
|
||||
# fixme: not thread safe
|
||||
|
||||
cdef class IndexValue(object):
|
||||
cdef cpp.shared_ptr[cpp.IndexValue] _value
|
||||
def __init__(self, InfoHash h=None, cpp.uint64_t vid=0):
|
||||
cdef cpp.InfoHash hh = h._infohash
|
||||
self._value.reset(new cpp.IndexValue(hh, vid))
|
||||
def __str__(self):
|
||||
return "(" + self.getKey().toString().decode() +", "+ str(self.getValueId()) +")"
|
||||
def getKey(self):
|
||||
h = InfoHash()
|
||||
h._infohash = self._value.get().first
|
||||
return h
|
||||
def getValueId(self):
|
||||
return self._value.get().second
|
||||
|
||||
cdef class Pht(object):
|
||||
cdef cpp.Pht* thisptr
|
||||
def __cinit__(self, bytes name, key_spec, DhtRunner dht):
|
||||
cdef cpp.IndexKeySpec cpp_key_spec
|
||||
for kk, size in key_spec.items():
|
||||
cpp_key_spec[bytes(kk, 'utf-8')] = size
|
||||
self.thisptr = new cpp.Pht(name, cpp_key_spec, dht.thisptr)
|
||||
property MAX_NODE_ENTRY_COUNT:
|
||||
def __get__(self):
|
||||
return cpp.PHT_MAX_NODE_ENTRY_COUNT
|
||||
def lookup(self, key, lookup_cb=None, done_cb=None):
|
||||
"""Query the Index with a specified key.
|
||||
|
||||
key -- the key for to the entry in the index.
|
||||
lookup_cb -- function called when the operation is completed. This
|
||||
function takes a list of IndexValue objects and a string
|
||||
representation of the prefix where the value was indexed in
|
||||
the PHT.
|
||||
"""
|
||||
cb_obj = {'lookup':lookup_cb, 'done':done_cb} # TODO: donecallback is to be removed
|
||||
ref.Py_INCREF(cb_obj)
|
||||
cdef cpp.IndexKey cppk
|
||||
for kk, v in key.items():
|
||||
cppk[bytes(kk, 'utf-8')] = bytes(v)
|
||||
self.thisptr.lookup(
|
||||
cppk,
|
||||
cpp.Pht.bindLookupCb(lookup_callback, <void*>cb_obj),
|
||||
cpp.bindDoneCbSimple(done_callback_simple, <void*>cb_obj)
|
||||
)
|
||||
def insert(self, key, IndexValue value, done_cb=None):
|
||||
"""Add an index entry to the Index.
|
||||
|
||||
key -- the key for to the entry in the index.
|
||||
value -- an IndexValue object describing the indexed value.
|
||||
done_cb -- Called when the operation is completed.
|
||||
"""
|
||||
cb_obj = {'done':done_cb}
|
||||
ref.Py_INCREF(cb_obj)
|
||||
cdef cpp.IndexKey cppk
|
||||
for kk, v in key.items():
|
||||
cppk[bytes(kk, 'utf-8')] = bytes(v)
|
||||
cdef cpp.IndexValue val
|
||||
val.first = (<InfoHash>value.getKey())._infohash
|
||||
val.second = value.getValueId()
|
||||
self.thisptr.insert(
|
||||
cppk,
|
||||
val,
|
||||
cpp.bindDoneCbSimple(done_callback_simple, <void*>cb_obj)
|
||||
)
|
||||
|
||||
+25
-2
@@ -20,6 +20,7 @@ from libcpp cimport bool
|
||||
from libcpp.string cimport string
|
||||
from libcpp.vector cimport vector
|
||||
from libcpp.utility cimport pair
|
||||
from libcpp.map cimport map
|
||||
|
||||
ctypedef uint16_t in_port_t
|
||||
ctypedef unsigned short int sa_family_t;
|
||||
@@ -27,6 +28,7 @@ ctypedef unsigned short int sa_family_t;
|
||||
cdef extern from "<memory>" namespace "std" nogil:
|
||||
cdef cppclass shared_ptr[T]:
|
||||
shared_ptr() except +
|
||||
shared_ptr(T*) except +
|
||||
T* get()
|
||||
T operator*()
|
||||
void reset(T*)
|
||||
@@ -103,19 +105,21 @@ cdef extern from "opendht/callbacks.h" namespace "dht":
|
||||
ctypedef void (*ShutdownCallbackRaw)(void *user_data)
|
||||
ctypedef bool (*GetCallbackRaw)(shared_ptr[Value] values, void *user_data)
|
||||
ctypedef void (*DoneCallbackRaw)(bool done, vector[shared_ptr[Node]]* nodes, void *user_data)
|
||||
ctypedef void (*DoneCallbackSimpleRaw)(bool done, void *user_data)
|
||||
|
||||
cppclass ShutdownCallback:
|
||||
ShutdownCallback() except +
|
||||
cppclass GetCallback:
|
||||
GetCallback() except +
|
||||
#GetCallback(GetCallbackRaw cb, void *user_data) except +
|
||||
cppclass DoneCallback:
|
||||
DoneCallback() except +
|
||||
#DoneCallback(DoneCallbackRaw, void *user_data) except +
|
||||
cppclass DoneCallbackSimple:
|
||||
DoneCallbackSimple() except +
|
||||
|
||||
cdef ShutdownCallback bindShutdownCb(ShutdownCallbackRaw cb, void *user_data)
|
||||
cdef GetCallback bindGetCb(GetCallbackRaw cb, void *user_data)
|
||||
cdef DoneCallback bindDoneCb(DoneCallbackRaw cb, void *user_data)
|
||||
cdef DoneCallbackSimple bindDoneCbSimple(DoneCallbackSimpleRaw cb, void *user_data)
|
||||
|
||||
cppclass Config:
|
||||
InfoHash node_id
|
||||
@@ -156,3 +160,22 @@ cdef extern from "opendht/log.h" namespace "dht::log":
|
||||
void disableLogging(DhtRunner& dht)
|
||||
void enableFileLogging(DhtRunner& dht, const string& path)
|
||||
|
||||
cdef extern from "opendht/indexation/pht.h" namespace "dht::indexation":
|
||||
size_t PHT_MAX_NODE_ENTRY_COUNT "dht::indexation::Pht::MAX_NODE_ENTRY_COUNT"
|
||||
cdef cppclass Prefix:
|
||||
Prefix() except +
|
||||
Prefix(vector[uint8_t]) except +
|
||||
string toString() const
|
||||
ctypedef pair[InfoHash, uint64_t] IndexValue "dht::indexation::Value"
|
||||
ctypedef map[string, vector[uint8_t]] IndexKey "dht::indexation::Pht::Key"
|
||||
ctypedef map[string, uint32_t] IndexKeySpec "dht::indexation::Pht::KeySpec"
|
||||
ctypedef void (*LookupCallbackRaw)(vector[shared_ptr[IndexValue]]* values, Prefix* p, void* user_data);
|
||||
cdef cppclass Pht:
|
||||
cppclass LookupCallback:
|
||||
LookupCallback() except +
|
||||
Pht(string, IndexKeySpec, shared_ptr[DhtRunner]) except +
|
||||
void lookup(IndexKey k, LookupCallback cb, DoneCallbackSimple doneCb);
|
||||
void insert(IndexKey k, IndexValue v, DoneCallbackSimple cb)
|
||||
@staticmethod
|
||||
LookupCallback bindLookupCb(LookupCallbackRaw cb, void *user_data)
|
||||
|
||||
@@ -27,7 +27,7 @@ import random
|
||||
|
||||
from dht.network import DhtNetwork
|
||||
from dht.network import DhtNetworkSubProcess
|
||||
from dht.tests import PerformanceTest, PersistenceTest
|
||||
from dht.tests import PerformanceTest, PersistenceTest, PhtTest
|
||||
from dht import virtual_network_builder
|
||||
from dht import network as dhtnetwork
|
||||
|
||||
@@ -66,7 +66,11 @@ class WorkBench():
|
||||
|
||||
def create_virtual_net(self):
|
||||
if self.virtual_locs > 1:
|
||||
cmd = ["python3", os.path.abspath(virtual_network_builder.__file__), "-i", self.ifname, "-n", str(self.clusters), '-l', str(self.loss), '-d', str(self.delay)]
|
||||
cmd = ["python3", os.path.abspath(virtual_network_builder.__file__),
|
||||
"-i", self.ifname,
|
||||
"-n", str(self.clusters),
|
||||
'-l', str(self.loss),
|
||||
'-d', str(self.delay)]
|
||||
if not self.disable_ipv4:
|
||||
cmd.append('-4')
|
||||
if not self.disable_ipv6:
|
||||
@@ -90,10 +94,17 @@ class WorkBench():
|
||||
cmd.extend(['-b', self.local_bootstrap.ip4])
|
||||
if not self.disable_ipv6 and self.local_bootstrap.ip6:
|
||||
cmd.extend(['-b6', self.local_bootstrap.ip6])
|
||||
self.procs[i] = DhtNetworkSubProcess('node'+str(i), cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
while DhtNetworkSubProcess.NOTIFY_TOKEN not in self.procs[i].getline():
|
||||
# waiting for process to spawn
|
||||
time.sleep(0.5)
|
||||
lock = threading.Condition()
|
||||
def dcb(success):
|
||||
nonlocal lock
|
||||
if not success:
|
||||
DhtNetwork.Log.err("Failed to initialize network...")
|
||||
with lock:
|
||||
lock.notify()
|
||||
with lock:
|
||||
self.procs[i] = DhtNetworkSubProcess('node'+str(i), cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
self.procs[i].sendPing(done_cb=dcb)
|
||||
lock.wait()
|
||||
else:
|
||||
raise Exception('First create bootstrap.')
|
||||
|
||||
@@ -157,11 +168,16 @@ if __name__ == '__main__':
|
||||
featureArgs = parser.add_mutually_exclusive_group(required=True)
|
||||
featureArgs.add_argument('--performance', action='store_true', default=False,
|
||||
help='Launches performance benchmark test. Available args for "-t" are: gets.')
|
||||
featureArgs.add_argument('--pht', action='store_true', default=False,
|
||||
help='Launches PHT benchmark test. '\
|
||||
'Available args for "-t" are: insert. '\
|
||||
'Timer available by adding "timer" to "-o" args'\
|
||||
'Use "-m" option for fixing number of keys to create during the test.')
|
||||
featureArgs.add_argument('--data-persistence', action='store_true', default=0,
|
||||
help='Launches data persistence benchmark test. '\
|
||||
'Available args for "-t" are: delete, replace, mult_time. '\
|
||||
'Available args for "-o" are : dump_str_log, keep_alive, trigger, traffic_plot, op_plot. '\
|
||||
'Use "-m" to specify the number of producers on the DHT.'\
|
||||
'Use "-m" to specify the number of producers on the DHT. '\
|
||||
'Use "-e" to specify the number of values to put on the DHT.')
|
||||
|
||||
args = parser.parse_args()
|
||||
@@ -206,6 +222,10 @@ if __name__ == '__main__':
|
||||
PerformanceTest(args.test, wb, test_opt).run()
|
||||
elif args.data_persistence:
|
||||
PersistenceTest(args.test, wb, test_opt).run()
|
||||
elif args.pht:
|
||||
if args.m:
|
||||
test_opt.update({ 'num_keys' : args.m })
|
||||
PhtTest(args.test, wb, test_opt).run()
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
Executable
+265
@@ -0,0 +1,265 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2015-2016 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os, sys, time, cmd
|
||||
import subprocess
|
||||
import signal
|
||||
import argparse
|
||||
import threading
|
||||
|
||||
import opendht as dht
|
||||
|
||||
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)
|
||||
n = dht.DhtRunner()
|
||||
n.run(ipv4=ip4 if ip4 else "", ipv6=ip6 if ip6 else "", port=p, is_bootstrap=is_bootstrap)
|
||||
if logfile:
|
||||
n.enableFileLogging(logfile)
|
||||
if bootstrap:
|
||||
n.bootstrap(bootstrap[0], bootstrap[1])
|
||||
time.sleep(.01)
|
||||
return ((ip4, ip6, p), n, id)
|
||||
|
||||
@staticmethod
|
||||
def find_ip(iface):
|
||||
if not iface or iface == 'any':
|
||||
return ('0.0.0.0','')
|
||||
|
||||
if_ip4 = netifaces.ifaddresses(iface)[netifaces.AF_INET][0]['addr']
|
||||
if_ip6 = netifaces.ifaddresses(iface)[netifaces.AF_INET6][0]['addr']
|
||||
return (if_ip4, if_ip6)
|
||||
|
||||
def __init__(self, iface=None, ip4=None, ip6=None, port=4000, bootstrap=None, first_bootstrap=False, logfile=None):
|
||||
NodeCluster.iface = iface
|
||||
self.port = port
|
||||
ips = NodeCluster.find_ip(iface)
|
||||
self.logfile = logfile
|
||||
self.ip4 = ip4 if ip4 else ips[0]
|
||||
self.ip6 = ip6 if ip6 else ips[1]
|
||||
self.bootstrap = bootstrap
|
||||
if bootstrap:
|
||||
self.bootstrap = (bootstrap.hostname, str(bootstrap.port) if bootstrap.port else "4222")
|
||||
else:
|
||||
print("Using fallback bootstrap", self.ip4, self.port)
|
||||
self.bootstrap = ((self.ip4, str(self.port)))
|
||||
if first_bootstrap:
|
||||
print("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]
|
||||
|
||||
def get(self, i=None):
|
||||
if not 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]
|
||||
|
||||
def getNodeInfoById(self, id=None):
|
||||
if id:
|
||||
for n in self.nodes:
|
||||
if n[1].getNodeId() == id:
|
||||
return n
|
||||
return None
|
||||
|
||||
def launch_node(self):
|
||||
node_logfile = (self.logfile + str(self.node_uid) + '.log') if self.logfile else None
|
||||
n = NodeCluster.run_node(self.ip4, self.ip6, self.port, self.bootstrap, logfile=node_logfile)
|
||||
self.nodes.append(n)
|
||||
self.port += 1
|
||||
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()
|
||||
|
||||
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)
|
||||
for i in range(l, n):
|
||||
self.launch_node()
|
||||
else:
|
||||
print("Ending", l-n, "nodes", self.ip4, self.ip6)
|
||||
#random.shuffle(self.nodes)
|
||||
for i in range(n, l):
|
||||
self.end_node()
|
||||
|
||||
def close(self):
|
||||
self.resize(0)
|
||||
|
||||
def getMessageStats(self):
|
||||
stats = np.array([0,0,0,0,0])
|
||||
for n in self.nodes:
|
||||
stats += np.array(n[1].getNodeMessageStats())
|
||||
stats_list = [len(self.nodes)]
|
||||
stats_list.extend(stats.tolist())
|
||||
return stats_list
|
||||
|
||||
class ClusterShell(cmd.Cmd):
|
||||
intro = 'Welcome to the OpenDHT node cluster control. Type help or ? to list commands.\n'
|
||||
prompt = '>> '
|
||||
net = None
|
||||
node = None
|
||||
log = False
|
||||
def __init__(self, network):
|
||||
super(ClusterShell, self).__init__()
|
||||
self.net = network
|
||||
def setNode(self, node=None, num=0):
|
||||
if node == self.node:
|
||||
return
|
||||
if self.node:
|
||||
self.node.disableLogging()
|
||||
self.node = node
|
||||
if self.node:
|
||||
self.prompt = '('+str(num)+') >> '
|
||||
if self.log:
|
||||
self.node.enableLogging()
|
||||
else:
|
||||
self.prompt = '>> '
|
||||
def do_exit(self, arg):
|
||||
self.close()
|
||||
return True
|
||||
def do_node(self, arg):
|
||||
if not arg:
|
||||
setNode()
|
||||
else:
|
||||
nodenum = int(arg)
|
||||
if nodenum > len(self.net.nodes) or nodenum < 1:
|
||||
print("Invalid node number:", nodenum, " (accepted: 1-", len(self.net.nodes), ")")
|
||||
else:
|
||||
self.setNode(self.net.nodes[nodenum-1][1], nodenum)
|
||||
def do_ll(self, arg):
|
||||
if self.node:
|
||||
print('Node', self.node.getNodeId().decode())
|
||||
else:
|
||||
print(len(self.net.nodes), 'nodes running.')
|
||||
def do_ls(self, arg):
|
||||
if self.node:
|
||||
print(self.node.getSearchesLog(0))
|
||||
else:
|
||||
print('No node selected.')
|
||||
def do_log(self, arg):
|
||||
if self.node:
|
||||
self.log = not self.log
|
||||
if self.log:
|
||||
self.node.enableLogging()
|
||||
else:
|
||||
self.node.disableLogging()
|
||||
def do_EOF(self, line):
|
||||
self.close()
|
||||
return True
|
||||
def close(self):
|
||||
if self.net:
|
||||
self.net.close()
|
||||
self.net = None
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
from urllib.parse import urlparse
|
||||
net = None
|
||||
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')
|
||||
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)
|
||||
else:
|
||||
ClusterShell(net).cmdloop()
|
||||
except Exception as e:
|
||||
traceback.print_tb(e.__traceback__)
|
||||
print(type(e).__name__+':', e, file=sys.stderr)
|
||||
finally:
|
||||
if net:
|
||||
net.resize(0)
|
||||
+234
-174
@@ -25,11 +25,13 @@ import time
|
||||
import threading
|
||||
import queue
|
||||
import re
|
||||
import traceback
|
||||
|
||||
import ipaddress
|
||||
import netifaces
|
||||
import numpy as np
|
||||
from pyroute2.netns.process.proxy import NSPopen
|
||||
import msgpack
|
||||
|
||||
from opendht import *
|
||||
|
||||
@@ -37,8 +39,6 @@ from opendht import *
|
||||
# useful functions
|
||||
b_space_join = lambda *l: b' '.join(map(bytes, l))
|
||||
|
||||
# TODO: find where token "notifyend" gets printed... Or switch to MSGPACK for
|
||||
# serialisation of packets between both processes.
|
||||
class DhtNetworkSubProcess(NSPopen):
|
||||
"""
|
||||
Handles communication with DhtNetwork sub process.
|
||||
@@ -47,20 +47,21 @@ class DhtNetworkSubProcess(NSPopen):
|
||||
process' stdout until it finds 'DhtNetworkSubProcess.NOTIFY_TOKEN' token,
|
||||
therefor, waits for the sub process to spawn.
|
||||
"""
|
||||
# Packet types
|
||||
REQUEST = 'DhtNetworkSubProcess.request'
|
||||
ANSWER = 'DhtNetworkSubProcess.answer'
|
||||
OUT = 'DhtNetworkSubProcess.out'
|
||||
|
||||
# requests
|
||||
NODE_PUT_REQ = b"np"
|
||||
NEW_NODE_REQ = b"nn"
|
||||
REMOVE_NODE_REQ = b"rn"
|
||||
SHUTDOWN_NODE_REQ = b"sdn"
|
||||
SHUTDOWN_REPLACE_NODE_REQ = b'sdrn'
|
||||
SHUTDOWN_CLUSTER_REQ = b"sdc"
|
||||
DUMP_STORAGE_REQ = b"strl"
|
||||
MESSAGE_STATS = b"gms"
|
||||
|
||||
|
||||
# tokens
|
||||
NOTIFY_TOKEN = 'notify'
|
||||
NOTIFY_END_TOKEN = 'notifyend'
|
||||
PING_REQ = "p"
|
||||
NODE_PUT_REQ = "np" # "np <hash> <value>"
|
||||
NEW_NODE_REQ = "nn" # "nn"
|
||||
REMOVE_NODE_REQ = "rn" # "rn <id0>[ <id1>[ id2[...]]]"
|
||||
SHUTDOWN_NODE_REQ = "sdn" # "sdn <id0>[ <id1>[ id2[...]]]"
|
||||
SHUTDOWN_REPLACE_NODE_REQ = "sdrn" # "sdn <id0>[ <id1>[ id2[...]]]"
|
||||
SHUTDOWN_CLUSTER_REQ = "sdc" # "sdc"
|
||||
DUMP_STORAGE_REQ = "strl" # "strl"
|
||||
MESSAGE_STATS = "gms" # "gms"
|
||||
|
||||
def __init__(self, ns, cmd, quit=False, **kwargs):
|
||||
super(DhtNetworkSubProcess, self).__init__(ns, cmd, **kwargs)
|
||||
@@ -70,11 +71,11 @@ class DhtNetworkSubProcess(NSPopen):
|
||||
self._quit = quit
|
||||
self._lock = threading.Condition()
|
||||
self._in_queue = queue.Queue()
|
||||
self._out_queue = queue.Queue()
|
||||
self._callbacks = {}
|
||||
self._tid = 0
|
||||
|
||||
# starting thread
|
||||
self._thread = threading.Thread(target=self._communicate)
|
||||
self._thread.daemon = True
|
||||
self._thread.start()
|
||||
|
||||
def __repr__(self):
|
||||
@@ -92,42 +93,38 @@ class DhtNetworkSubProcess(NSPopen):
|
||||
"""
|
||||
Communication thread. This reads and writes to the sub process.
|
||||
"""
|
||||
ENCODING = 'utf-8'
|
||||
sleep_time = 0.1
|
||||
stdin_line, stdout_line = '', ''
|
||||
|
||||
# first read of process living. Expecting NOTIFY_TOKEN
|
||||
while DhtNetworkSubProcess.NOTIFY_TOKEN not in stdout_line:
|
||||
stdout_line = self.stdout.readline().decode()
|
||||
time.sleep(sleep_time)
|
||||
|
||||
with self._lock:
|
||||
self._out_queue.put(stdout_line)
|
||||
|
||||
while not self._quit:
|
||||
with self._lock:
|
||||
try:
|
||||
stdin_line = self._in_queue.get_nowait()
|
||||
packet = self._in_queue.get_nowait()
|
||||
|
||||
# sending data to sub process
|
||||
self.stdin.write(stdin_line if isinstance(stdin_line, bytes) else
|
||||
bytes(str(stdin_line), encoding=ENCODING))
|
||||
self.stdin.write(packet)
|
||||
self.stdin.flush()
|
||||
except queue.Empty:
|
||||
#waiting for next stdin req to send
|
||||
self._lock.wait(timeout=sleep_time)
|
||||
pass
|
||||
|
||||
# reading response from sub process
|
||||
for stdout_line in iter(self.stdout.readline, b''):
|
||||
stdout_line = stdout_line.decode().replace('\n', '')
|
||||
if stdout_line:
|
||||
with self._lock:
|
||||
self._out_queue.put(stdout_line)
|
||||
# reading from sub process
|
||||
out_string = ''
|
||||
for p in msgpack.Unpacker(self.stdout):
|
||||
if isinstance(p, dict):
|
||||
self._process_packet(p)
|
||||
else:
|
||||
# Some non-msgpack data could slip into the stream. We
|
||||
# have to treat those as characters.
|
||||
out_string += chr(p)
|
||||
if out_string:
|
||||
print(out_string)
|
||||
|
||||
#waiting for next stdin req to send
|
||||
self._lock.wait(timeout=sleep_time)
|
||||
|
||||
with self._lock:
|
||||
self._lock.notify()
|
||||
|
||||
def stop_communicating(self):
|
||||
def _stop_communicating(self):
|
||||
"""
|
||||
Stops the I/O thread from communicating with the subprocess.
|
||||
"""
|
||||
@@ -142,12 +139,12 @@ class DhtNetworkSubProcess(NSPopen):
|
||||
Notifies thread and sub process to terminate. This is blocking call
|
||||
until the sub process finishes.
|
||||
"""
|
||||
self.stop_communicating()
|
||||
self._stop_communicating()
|
||||
self.send_signal(signal.SIGINT);
|
||||
self.wait()
|
||||
self.release()
|
||||
|
||||
def send(self, msg):
|
||||
def _send(self, msg):
|
||||
"""
|
||||
Send data to sub process.
|
||||
"""
|
||||
@@ -155,83 +152,76 @@ class DhtNetworkSubProcess(NSPopen):
|
||||
self._in_queue.put(msg)
|
||||
self._lock.notify()
|
||||
|
||||
def getline(self):
|
||||
def _process_packet(self, p):
|
||||
"""
|
||||
Read line from sub process.
|
||||
|
||||
@return: A line on sub process' stdout.
|
||||
@rtype : str
|
||||
Process msgpack packet received from
|
||||
"""
|
||||
line = ''
|
||||
with self._lock:
|
||||
try:
|
||||
line = self._out_queue.get_nowait()
|
||||
except queue.Empty:
|
||||
pass
|
||||
return line
|
||||
if not b'tid' in p:
|
||||
DhtNetwork.Log.err('Bad packet...')
|
||||
try:
|
||||
self._callbacks[p[b'tid']](p)
|
||||
except KeyError:
|
||||
DhtNetwork.Log.err('Unknown tid...')
|
||||
|
||||
def getlinesUntilNotify(self, answer_cb=None):
|
||||
"""
|
||||
Reads the stdout queue until a proper notification is given by the sub
|
||||
process.
|
||||
|
||||
@param answer_cb: Callback to call when an answer is given after notify.
|
||||
The function takes a list of strings as argument.
|
||||
@type answer_cb: function
|
||||
"""
|
||||
notified = False
|
||||
answer = []
|
||||
while True:
|
||||
out = self.getline()
|
||||
if out.split(' ')[0] == DhtNetworkSubProcess.NOTIFY_TOKEN:
|
||||
notified = True
|
||||
elif notified and out.split(' ')[0] == DhtNetworkSubProcess.NOTIFY_END_TOKEN:
|
||||
if answer_cb:
|
||||
answer_cb(answer)
|
||||
break
|
||||
elif notified:
|
||||
answer.append(out)
|
||||
elif out:
|
||||
yield out
|
||||
else:
|
||||
time.sleep(0.1)
|
||||
|
||||
def _sendRequest(self, request, answer_cb=None):
|
||||
def _sendRequest(self, request, tid, done_cb):
|
||||
"""
|
||||
Sends a request to the sub network and wait for output.
|
||||
|
||||
@param request: The serialized request.
|
||||
@type request: bytes
|
||||
@type request: Msgpack object
|
||||
"""
|
||||
self.send(request + b'\n')
|
||||
for line in self.getlinesUntilNotify(answer_cb=answer_cb):
|
||||
DhtNetwork.log(line)
|
||||
self._callbacks[tid] = done_cb
|
||||
self._send(request)
|
||||
|
||||
def sendGetMessageStats(self):
|
||||
def sendPing(self, done_cb=None):
|
||||
"""Sends a ping request to the DhtNetworkSubProcess.
|
||||
|
||||
@param done_cb: The callback to be executed when we get a response. This
|
||||
function takes a boolean "success" as parameter.
|
||||
@type done_cb: Function
|
||||
"""
|
||||
self._tid += 1
|
||||
def dcb(packet):
|
||||
try:
|
||||
done_cb(packet[b'success'])
|
||||
except KeyError:
|
||||
done_cb(False)
|
||||
self._sendRequest(msgpack.packb({
|
||||
DhtNetworkSubProcess.REQUEST : True,
|
||||
'tid' : self._tid,
|
||||
'req' : DhtNetworkSubProcess.PING_REQ
|
||||
}), self._tid, dcb)
|
||||
|
||||
def sendGetMessageStats(self, done_cb=None):
|
||||
"""
|
||||
Sends DhtNetwork sub process statistics request about nodes messages
|
||||
sent.
|
||||
|
||||
@param done_cb: A function taking as parameter the returned list of
|
||||
stats.
|
||||
@type done_cb: function
|
||||
|
||||
@return: A list [num_nodes, ping, find, get, put, listen].
|
||||
@rtype : list
|
||||
"""
|
||||
stats = []
|
||||
def cb(answer):
|
||||
"""
|
||||
Callback fed to getlinesUntilNotify made to recover answer from the
|
||||
DhtNetwork sub process.
|
||||
self._tid += 1
|
||||
def dcb(packet):
|
||||
nonlocal done_cb
|
||||
if not done_cb:
|
||||
return
|
||||
try:
|
||||
stats = packet[b'stats']
|
||||
done_cb([] if not isinstance(stats, list) else done_cb(stats))
|
||||
except KeyError:
|
||||
done_cb([])
|
||||
self._sendRequest(msgpack.packb({
|
||||
DhtNetworkSubProcess.REQUEST : True,
|
||||
'tid' : self._tid,
|
||||
'req' : DhtNetworkSubProcess.MESSAGE_STATS
|
||||
}), self._tid, dcb)
|
||||
|
||||
@param answer: the list of lines answered by the sub process.
|
||||
@type answer: function
|
||||
"""
|
||||
nonlocal stats
|
||||
if answer:
|
||||
stats = [int(v) for v in re.findall("[0-9]+", answer.pop())]
|
||||
|
||||
self._sendRequest(DhtNetworkSubProcess.MESSAGE_STATS, answer_cb=cb)
|
||||
return stats
|
||||
|
||||
def sendNodePutRequest(self, _hash, value):
|
||||
def sendClusterPutRequest(self, _hash, value, done_cb=None):
|
||||
"""
|
||||
Sends a put operation request.
|
||||
|
||||
@@ -239,40 +229,89 @@ class DhtNetworkSubProcess(NSPopen):
|
||||
@type _hash: bytes.
|
||||
@param value: the value.
|
||||
@type value: bytes.
|
||||
@param done_cb: A function taking as parameter a boolean "success".
|
||||
@type done_cb: function
|
||||
"""
|
||||
self._sendRequest(b_space_join(DhtNetworkSubProcess.NODE_PUT_REQ, _hash,
|
||||
value))
|
||||
self._tid += 1
|
||||
def dcb(packet):
|
||||
nonlocal done_cb
|
||||
if not done_cb:
|
||||
return
|
||||
try:
|
||||
done_cb(packet[b'success'])
|
||||
except KeyError:
|
||||
done_cb(False)
|
||||
self._sendRequest(msgpack.packb({
|
||||
DhtNetworkSubProcess.REQUEST : True,
|
||||
'tid' : self._tid,
|
||||
'req' : DhtNetworkSubProcess.NODE_PUT_REQ,
|
||||
'hash' : _hash,
|
||||
'value' : value
|
||||
}), self._tid, dcb)
|
||||
|
||||
def sendNodesRequest(self, request, ids=b''):
|
||||
def sendClusterRequest(self, request, ids=[], done_cb=None):
|
||||
"""
|
||||
Send request to a list of nodes or the whole cluster.
|
||||
|
||||
@param request: The request. Possible values are:
|
||||
DhtNetworkSubProcess.NODE_PUT_REQ
|
||||
DhtNetworkSubProcess.REMOVE_NODE_REQ
|
||||
DhtNetworkSubProcess.SHUTDOWN_NODE_REQ
|
||||
DhtNetworkSubProcess.SHUTDOWN_REPLACE_NODE_REQ
|
||||
DhtNetworkSubProcess.SHUTDOWN_CLUSTER_REQ
|
||||
DhtNetworkSubProcess.DUMP_STORAGE_REQ
|
||||
DhtNetworkSubProcess.MESSAGE_STATS
|
||||
@type request: bytes
|
||||
@param ids: The list of ids concerned by the request.
|
||||
@type ids: list
|
||||
"""
|
||||
self._sendRequest(b_space_join(request, b_space_join(*ids)))
|
||||
self._tid += 1
|
||||
def dcb(packet):
|
||||
nonlocal done_cb
|
||||
if not done_cb:
|
||||
return
|
||||
try:
|
||||
done_cb(packet[b'success'])
|
||||
except KeyError:
|
||||
done_cb(False)
|
||||
self._sendRequest(msgpack.packb({
|
||||
DhtNetworkSubProcess.REQUEST : True,
|
||||
'tid' : self._tid,
|
||||
'req' : request,
|
||||
'ids' : ids
|
||||
}), self._tid, dcb)
|
||||
|
||||
|
||||
class DhtNetwork(object):
|
||||
nodes = []
|
||||
|
||||
@staticmethod
|
||||
def log(*to_print):
|
||||
class Log(object):
|
||||
BOLD = "\033[1m"
|
||||
NORMAL = "\033[0m"
|
||||
print('%s[DhtNetwork-%s]%s' % (BOLD, DhtNetwork.iface, NORMAL), ':' , *to_print, file=sys.stderr)
|
||||
WHITE = "\033[97m"
|
||||
RED = "\033[31m"
|
||||
YELLOW = "\033[33m"
|
||||
|
||||
@staticmethod
|
||||
def _log_with_color(*to_print, color=None):
|
||||
color = color if color else DhtNetwork.Log.WHITE
|
||||
print('%s%s[DhtNetwork-%s]%s%s' %
|
||||
(DhtNetwork.Log.BOLD, color, DhtNetwork.iface, DhtNetwork.Log.NORMAL, color),
|
||||
*to_print, DhtNetwork.Log.NORMAL, file=sys.stderr)
|
||||
|
||||
@staticmethod
|
||||
def log(*to_print):
|
||||
DhtNetwork.Log._log_with_color(*to_print, color=DhtNetwork.Log.WHITE)
|
||||
|
||||
@staticmethod
|
||||
def warn(*to_print):
|
||||
DhtNetwork.Log._log_with_color(*to_print, color=DhtNetwork.Log.YELLOW)
|
||||
|
||||
@staticmethod
|
||||
def err(*to_print):
|
||||
DhtNetwork.Log._log_with_color(*to_print, color=DhtNetwork.Log.RED)
|
||||
|
||||
@staticmethod
|
||||
def run_node(ip4, ip6, p, bootstrap=[], is_bootstrap=False):
|
||||
DhtNetwork.log("run_node", ip4, ip6, p, bootstrap)
|
||||
DhtNetwork.Log.log("run_node", ip4, ip6, p, bootstrap)
|
||||
n = DhtRunner()
|
||||
n.run(ipv4=ip4 if ip4 else "", ipv6=ip6 if ip6 else "", port=p, is_bootstrap=is_bootstrap)
|
||||
for b in bootstrap:
|
||||
@@ -297,7 +336,7 @@ class DhtNetwork(object):
|
||||
self.ip6 = ip6 if ip6 else ips[1]
|
||||
self.bootstrap = bootstrap
|
||||
if first_bootstrap:
|
||||
DhtNetwork.log("Starting bootstrap node")
|
||||
DhtNetwork.Log.log("Starting bootstrap node")
|
||||
self.nodes.append(DhtNetwork.run_node(self.ip4, self.ip6, self.port, self.bootstrap, is_bootstrap=True))
|
||||
self.bootstrap = [(self.ip4, str(self.port))]
|
||||
self.port += 1
|
||||
@@ -309,6 +348,9 @@ class DhtNetwork(object):
|
||||
return self.nodes[0][1]
|
||||
|
||||
def get(self, i=None):
|
||||
if not self.nodes:
|
||||
return None
|
||||
|
||||
if i is None:
|
||||
l = list(self.nodes)
|
||||
random.shuffle(l)
|
||||
@@ -321,14 +363,13 @@ class DhtNetwork(object):
|
||||
for n in self.nodes:
|
||||
if n[1].getNodeId() == id:
|
||||
return n
|
||||
else:
|
||||
return None
|
||||
return None
|
||||
|
||||
def launch_node(self):
|
||||
n = DhtNetwork.run_node(self.ip4, self.ip6, self.port, self.bootstrap)
|
||||
self.nodes.append(n)
|
||||
if not self.bootstrap:
|
||||
DhtNetwork.log("Using fallback bootstrap", self.ip4, self.port)
|
||||
DhtNetwork.Log.log("Using fallback bootstrap", self.ip4, self.port)
|
||||
self.bootstrap = [(self.ip4, str(self.port))]
|
||||
self.port += 1
|
||||
return n
|
||||
@@ -346,25 +387,25 @@ class DhtNetwork(object):
|
||||
lock = threading.Condition()
|
||||
def shutdown_cb():
|
||||
nonlocal lock
|
||||
DhtNetwork.log('Done.')
|
||||
DhtNetwork.Log.log('Done.')
|
||||
with lock:
|
||||
lock.notify()
|
||||
|
||||
if not self.nodes:
|
||||
return
|
||||
elif id is not None:
|
||||
elif id:
|
||||
n = self.getNodeInfoById(id)
|
||||
if n:
|
||||
if shutdown:
|
||||
with lock:
|
||||
DhtNetwork.log('Waiting for node to shutdown... ')
|
||||
DhtNetwork.Log.log('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)
|
||||
DhtNetwork.log(id, 'deleted !')
|
||||
DhtNetwork.Log.log(id, 'deleted !')
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@@ -385,11 +426,11 @@ class DhtNetwork(object):
|
||||
if n == l:
|
||||
return
|
||||
if n > l:
|
||||
DhtNetwork.log("Launching", n-l, "nodes", self.ip4, self.ip6)
|
||||
DhtNetwork.Log.log("Launching", n-l, "nodes", self.ip4, self.ip6)
|
||||
for i in range(l, n):
|
||||
self.launch_node()
|
||||
else:
|
||||
DhtNetwork.log("Ending", l-n, "nodes", self.ip4, self.ip6)
|
||||
DhtNetwork.Log.log("Ending", l-n, "nodes", self.ip4, self.ip6)
|
||||
#random.shuffle(self.nodes)
|
||||
for i in range(n, l):
|
||||
self.end_node()
|
||||
@@ -409,30 +450,39 @@ if __name__ == '__main__':
|
||||
lock = threading.Condition()
|
||||
quit = False
|
||||
|
||||
def notify_benchmark(answer=[]):
|
||||
sys.stdout.write('%s\n' % DhtNetworkSubProcess.NOTIFY_TOKEN)
|
||||
for line in answer:
|
||||
sys.stdout.write(str(line)+'\n')
|
||||
sys.stdout.write('%s\n' % DhtNetworkSubProcess.NOTIFY_END_TOKEN)
|
||||
sys.stdout.flush()
|
||||
def send_msgpack_packet(packet):
|
||||
sys.stdout.buffer.write(packet)
|
||||
sys.stdout.buffer.flush()
|
||||
|
||||
def listen_to_mother_nature(stdin, q):
|
||||
def notify_benchmark(packet, success):
|
||||
"""Notifies the benchmark when an operation has been completed.
|
||||
|
||||
@param success: If the operation has been successful
|
||||
@type success: boolean
|
||||
@param packet: The packet we are providing an answer for.
|
||||
@type packet: dict
|
||||
"""
|
||||
send_msgpack_packet(msgpack.packb({
|
||||
DhtNetworkSubProcess.ANSWER : True,
|
||||
'tid' : packet[b'tid'],
|
||||
'success' : success
|
||||
}))
|
||||
|
||||
def send_stats(packet, stats):
|
||||
send_msgpack_packet(msgpack.packb({
|
||||
DhtNetworkSubProcess.ANSWER : True,
|
||||
'tid' : packet[b'tid'],
|
||||
'stats' : stats
|
||||
}))
|
||||
|
||||
def listen_to_mother_nature(q):
|
||||
global quit
|
||||
|
||||
def parse_req(req):
|
||||
split_req = req.split(' ')
|
||||
|
||||
op = split_req[0]
|
||||
hashes = [this_hash.replace('\n', '').encode() for this_hash in split_req[1:]]
|
||||
|
||||
return (op, hashes)
|
||||
|
||||
while not quit:
|
||||
req = stdin.readline()
|
||||
parsed_req = parse_req(req)
|
||||
q.put(parsed_req)
|
||||
with lock:
|
||||
lock.notify()
|
||||
for p in msgpack.Unpacker(sys.stdin.buffer.raw):
|
||||
if isinstance(p, dict) and DhtNetworkSubProcess.REQUEST.encode() in p:
|
||||
with lock:
|
||||
q.put(p)
|
||||
lock.notify()
|
||||
|
||||
def handler(signum, frame):
|
||||
global quit
|
||||
@@ -466,48 +516,52 @@ if __name__ == '__main__':
|
||||
net.resize(args.node_num)
|
||||
|
||||
q = queue.Queue()
|
||||
t = threading.Thread(target=listen_to_mother_nature, args=(sys.stdin, q))
|
||||
t = threading.Thread(target=listen_to_mother_nature, args=tuple([q]))
|
||||
t.daemon = True
|
||||
t.start()
|
||||
|
||||
notify_benchmark()
|
||||
|
||||
msg_stats = []
|
||||
|
||||
with lock:
|
||||
while not quit:
|
||||
lock.wait()
|
||||
try:
|
||||
req,req_args = q.get_nowait()
|
||||
packet = q.get_nowait()
|
||||
except queue.Empty:
|
||||
pass
|
||||
lock.wait()
|
||||
else:
|
||||
NODE_PUT_REQ = DhtNetworkSubProcess.NODE_PUT_REQ.decode()
|
||||
NEW_NODE_REQ = DhtNetworkSubProcess.NEW_NODE_REQ.decode()
|
||||
REMOVE_NODE_REQ = DhtNetworkSubProcess.REMOVE_NODE_REQ.decode()
|
||||
SHUTDOWN_NODE_REQ = DhtNetworkSubProcess.SHUTDOWN_NODE_REQ.decode()
|
||||
SHUTDOWN_REPLACE_NODE_REQ = DhtNetworkSubProcess.SHUTDOWN_REPLACE_NODE_REQ.decode()
|
||||
SHUTDOWN_CLUSTER_REQ = DhtNetworkSubProcess.SHUTDOWN_CLUSTER_REQ.decode()
|
||||
DUMP_STORAGE_REQ = DhtNetworkSubProcess.DUMP_STORAGE_REQ.decode()
|
||||
MESSAGE_STATS = DhtNetworkSubProcess.MESSAGE_STATS.decode()
|
||||
NODE_PUT_REQ = DhtNetworkSubProcess.NODE_PUT_REQ
|
||||
NEW_NODE_REQ = DhtNetworkSubProcess.NEW_NODE_REQ
|
||||
REMOVE_NODE_REQ = DhtNetworkSubProcess.REMOVE_NODE_REQ
|
||||
SHUTDOWN_NODE_REQ = DhtNetworkSubProcess.SHUTDOWN_NODE_REQ
|
||||
SHUTDOWN_REPLACE_NODE_REQ = DhtNetworkSubProcess.SHUTDOWN_REPLACE_NODE_REQ
|
||||
SHUTDOWN_CLUSTER_REQ = DhtNetworkSubProcess.SHUTDOWN_CLUSTER_REQ
|
||||
DUMP_STORAGE_REQ = DhtNetworkSubProcess.DUMP_STORAGE_REQ
|
||||
MESSAGE_STATS = DhtNetworkSubProcess.MESSAGE_STATS
|
||||
|
||||
if req in [SHUTDOWN_NODE_REQ,
|
||||
SHUTDOWN_REPLACE_NODE_REQ,
|
||||
REMOVE_NODE_REQ]:
|
||||
def delete_request(req, n):
|
||||
req = packet[b'req'].decode()
|
||||
success = True
|
||||
if req in [SHUTDOWN_NODE_REQ, SHUTDOWN_REPLACE_NODE_REQ, REMOVE_NODE_REQ]:
|
||||
def delete_request(req, nid):
|
||||
global msg_stats
|
||||
if not nid:
|
||||
return
|
||||
if req == SHUTDOWN_NODE_REQ:
|
||||
net.end_node(id=n, shutdown=True, last_msg_stats=msg_stats)
|
||||
net.end_node(id=nid, shutdown=True, last_msg_stats=msg_stats)
|
||||
elif req == SHUTDOWN_REPLACE_NODE_REQ:
|
||||
net.replace_node(id=n, shutdown=True, last_msg_stats=msg_stats)
|
||||
net.replace_node(id=nid, shutdown=True, last_msg_stats=msg_stats)
|
||||
elif req == REMOVE_NODE_REQ:
|
||||
net.end_node(id=n, last_msg_stats=msg_stats)
|
||||
net.end_node(id=nid, last_msg_stats=msg_stats)
|
||||
|
||||
if len(req) > 0:
|
||||
for n in req_args:
|
||||
delete_request(req, n)
|
||||
nodes = packet[b'ids']
|
||||
if nodes:
|
||||
for nid in nodes:
|
||||
delete_request(req, nid)
|
||||
else:
|
||||
delete_request(req, net.get().getNodeId())
|
||||
n = net.get()
|
||||
if n:
|
||||
delete_request(req, n.getNodeId())
|
||||
else:
|
||||
success = False
|
||||
elif req == SHUTDOWN_CLUSTER_REQ:
|
||||
for n in net.nodes:
|
||||
net.end_node(id=n[2], shutdown=True, last_msg_stats=msg_stats)
|
||||
@@ -515,20 +569,26 @@ if __name__ == '__main__':
|
||||
elif req == NEW_NODE_REQ:
|
||||
net.launch_node()
|
||||
elif req == NODE_PUT_REQ:
|
||||
_hash, v = req_args[:2]
|
||||
net.get().put(InfoHash(_hash), Value(v))
|
||||
|
||||
_hash = packet[b'hash']
|
||||
v = packet[b'value']
|
||||
n = net.get()
|
||||
if n:
|
||||
n.put(InfoHash(_hash), Value(v))
|
||||
else:
|
||||
success = False
|
||||
elif req == DUMP_STORAGE_REQ:
|
||||
for n in [m[1] for m in net.nodes if m[1].getNodeId() in req_args]:
|
||||
hashes = packet[b'ids']
|
||||
for n in [m[1] for m in net.nodes if m[1].getNodeId() in hashes]:
|
||||
net.log(n.getStorageLog())
|
||||
elif MESSAGE_STATS in req:
|
||||
elif req == MESSAGE_STATS:
|
||||
stats = sum([np.array(x) for x in [net.getMessageStats()]+msg_stats])
|
||||
notify_benchmark(answer=[stats])
|
||||
send_stats(packet, [int(_) for _ in stats])
|
||||
msg_stats.clear()
|
||||
continue
|
||||
notify_benchmark()
|
||||
notify_benchmark(packet, success)
|
||||
except Exception as e:
|
||||
DhtNetwork.log(e)
|
||||
traceback.print_tb(e.__traceback__)
|
||||
print(type(e).__name__+':', e, file=sys.stderr)
|
||||
finally:
|
||||
if net:
|
||||
net.resize(0)
|
||||
+362
-120
@@ -3,6 +3,7 @@
|
||||
# Author(s): Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
# Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
|
||||
import sys
|
||||
import os
|
||||
import threading
|
||||
import random
|
||||
@@ -10,10 +11,16 @@ import string
|
||||
import time
|
||||
import subprocess
|
||||
import re
|
||||
import collections
|
||||
|
||||
from matplotlib.ticker import FuncFormatter
|
||||
import math
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.ticker import FuncFormatter
|
||||
import networkx as nx
|
||||
from networkx.drawing.nx_agraph import graphviz_layout
|
||||
|
||||
|
||||
from opendht import *
|
||||
from dht.network import DhtNetwork, DhtNetworkSubProcess
|
||||
@@ -27,8 +34,40 @@ bit_format = None
|
||||
Kbit_format = FuncFormatter(lambda x, pos: '%1.1f' % (x*1024**-1) + 'Kb')
|
||||
Mbit_format = FuncFormatter(lambda x, pos: '%1.1f' % (x*1024**-2) + 'Mb')
|
||||
|
||||
def random_str_val(size=1024):
|
||||
"""Creates a random string value of specified size.
|
||||
|
||||
@param size: Size, in bytes, of the value.
|
||||
@type size: int
|
||||
|
||||
@return: Random string value
|
||||
@rtype : str
|
||||
"""
|
||||
return ''.join(random.choice(string.hexdigits) for _ in range(size))
|
||||
|
||||
|
||||
def random_hash():
|
||||
return InfoHash(''.join(random.SystemRandom().choice(string.hexdigits) for _ in range(40)).encode())
|
||||
"""Creates random InfoHash.
|
||||
"""
|
||||
return InfoHash(random_str_val(size=40).encode())
|
||||
|
||||
def timer(f, *args):
|
||||
"""
|
||||
Start a timer which count time taken for execute function f
|
||||
|
||||
@param f : Function to time
|
||||
@type f : function
|
||||
|
||||
@param args : Arguments of the function f
|
||||
@type args : list
|
||||
|
||||
@rtype : timer
|
||||
@return : Time taken by the function f
|
||||
"""
|
||||
start = time.time()
|
||||
f(*args)
|
||||
|
||||
return time.time() - start
|
||||
|
||||
def reset_before_test(featureTestMethod):
|
||||
"""
|
||||
@@ -70,6 +109,21 @@ def display_plot(yvals, xvals=None, yformatter=None, display_time=3, **kwargs):
|
||||
plt.plot(yvals, **kwargs)
|
||||
plt.pause(display_time)
|
||||
|
||||
def display_traffic_plot(ifname):
|
||||
"""Displays the traffic plot for a given interface name.
|
||||
|
||||
@param ifname: Interface name.
|
||||
@type ifname: string
|
||||
"""
|
||||
ydata = []
|
||||
xdata = []
|
||||
# warning: infinite loop
|
||||
interval = 2
|
||||
for rate in iftop_traffic_data(ifname, interval=interval):
|
||||
ydata.append(rate)
|
||||
xdata.append((xdata[-1] if len(xdata) > 0 else 0) + interval)
|
||||
display_plot(ydata, xvals=xdata, yformatter=Kbit_format, color='blue')
|
||||
|
||||
def iftop_traffic_data(ifname, interval=2, rate_type='send_receive'):
|
||||
"""
|
||||
Generator (yields data) function collecting traffic data from iftop
|
||||
@@ -125,13 +179,13 @@ def iftop_traffic_data(ifname, interval=2, rate_type='send_receive'):
|
||||
###########
|
||||
|
||||
class FeatureTest(object):
|
||||
done = 0
|
||||
lock = None
|
||||
|
||||
"""
|
||||
This is a base test.
|
||||
"""
|
||||
|
||||
done = 0
|
||||
lock = None
|
||||
|
||||
def __init__(self, test, workbench):
|
||||
"""
|
||||
@param test: The test string indicating the test to run. This string is
|
||||
@@ -143,6 +197,7 @@ class FeatureTest(object):
|
||||
"""
|
||||
self._test = test
|
||||
self._workbench = workbench
|
||||
self._bootstrap = self._workbench.get_bootstrap()
|
||||
|
||||
def _reset(self):
|
||||
"""
|
||||
@@ -156,7 +211,155 @@ class FeatureTest(object):
|
||||
def run(self):
|
||||
raise NotImplementedError('This method must be implemented.')
|
||||
|
||||
##################################
|
||||
# PHT #
|
||||
##################################
|
||||
|
||||
class PhtTest(FeatureTest):
|
||||
"""TODO
|
||||
"""
|
||||
|
||||
indexEntries = None
|
||||
prefix = None
|
||||
key = None
|
||||
|
||||
def __init__(self, test, workbench, opts):
|
||||
"""
|
||||
@param test: is one of the following:
|
||||
- 'insert': indexes a considerable amount of data in
|
||||
the PHT structure.
|
||||
TODO
|
||||
@type test: string
|
||||
|
||||
@param opts: Dictionnary containing options for the test. Allowed
|
||||
options are:
|
||||
- 'num_keys': this specifies the number of keys to insert
|
||||
in the PHT during the test.
|
||||
@type opts: dict
|
||||
"""
|
||||
super(PhtTest, self).__init__(test, workbench)
|
||||
self._num_keys = opts['num_keys'] if 'num_keys' in opts else 32
|
||||
self._timer = True if 'timer' in opts else False
|
||||
|
||||
def _reset(self):
|
||||
super(PhtTest, self)._reset()
|
||||
PhtTest.indexEntries = []
|
||||
|
||||
@staticmethod
|
||||
def lookupCb(vals, prefix):
|
||||
PhtTest.indexEntries = list(vals)
|
||||
PhtTest.prefix = prefix.decode()
|
||||
DhtNetwork.log('Index name: <todo>')
|
||||
DhtNetwork.log('Leaf prefix:', prefix)
|
||||
for v in vals:
|
||||
DhtNetwork.log('[ENTRY]:', v)
|
||||
|
||||
@staticmethod
|
||||
def lookupDoneCb(ok):
|
||||
DhtNetwork.log('[LOOKUP]:', PhtTest.key, "--", "success!" if ok else "Fail...")
|
||||
with FeatureTest.lock:
|
||||
FeatureTest.lock.notify()
|
||||
|
||||
@staticmethod
|
||||
def insertDoneCb(ok):
|
||||
DhtNetwork.log('[INSERT]:', PhtTest.key, "--", "success!" if ok else "Fail...")
|
||||
with FeatureTest.lock:
|
||||
FeatureTest.lock.notify()
|
||||
|
||||
@staticmethod
|
||||
def drawTrie(trie_dict):
|
||||
"""
|
||||
Draws the trie structure of the PHT from dictionnary.
|
||||
|
||||
@param trie_dict: Dictionnary of index entries (prefix -> entry).
|
||||
@type trie_dict: dict
|
||||
"""
|
||||
prefixes = list(trie_dict.keys())
|
||||
if len(prefixes) == 0:
|
||||
return
|
||||
|
||||
edges = list([])
|
||||
for prefix in prefixes:
|
||||
for i in range(-1, len(prefix)-1):
|
||||
u = prefix[:i+1]
|
||||
x = ("." if i == -1 else u, u+"0")
|
||||
y = ("." if i == -1 else u, u+"1")
|
||||
if x not in edges:
|
||||
edges.append(x)
|
||||
if y not in edges:
|
||||
edges.append(y)
|
||||
|
||||
# TODO: use a binary tree position layout...
|
||||
# UPDATE : In a better way [change lib]
|
||||
G = nx.Graph(sorted(edges, key=lambda x: len(x[0])))
|
||||
plt.title("PHT: Tree")
|
||||
pos=graphviz_layout(G,prog='dot')
|
||||
nx.draw(G, pos, with_labels=True, node_color='white')
|
||||
plt.show()
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
if self._test == 'insert':
|
||||
self._insertTest()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
finally:
|
||||
self._bootstrap.resize(1)
|
||||
|
||||
###########
|
||||
# Tests #
|
||||
###########
|
||||
|
||||
@reset_before_test
|
||||
def _insertTest(self):
|
||||
"""TODO: Docstring for _massIndexTest.
|
||||
"""
|
||||
bootstrap = self._bootstrap
|
||||
bootstrap.resize(2)
|
||||
dht = bootstrap.get(1)
|
||||
|
||||
NUM_DIG = max(math.log(self._num_keys, 2)/4, 5) # at least 5 digit keys.
|
||||
keyspec = collections.OrderedDict([('foo', NUM_DIG)])
|
||||
pht = Pht(b'foo_index', keyspec, dht)
|
||||
|
||||
DhtNetwork.log('PHT has',
|
||||
pht.MAX_NODE_ENTRY_COUNT,
|
||||
'node'+ ('s' if pht.MAX_NODE_ENTRY_COUNT > 1 else ''),
|
||||
'per leaf bucket.')
|
||||
keys = [{
|
||||
[_ for _ in keyspec.keys()][0] :
|
||||
''.join(random.SystemRandom().choice(string.hexdigits)
|
||||
for _ in range(NUM_DIG)).encode()
|
||||
} for n in range(self._num_keys)]
|
||||
all_entries = {}
|
||||
|
||||
# Index all entries.
|
||||
for key in keys:
|
||||
PhtTest.key = key
|
||||
with FeatureTest.lock:
|
||||
time_taken = timer(pht.insert, key, IndexValue(random_hash()), PhtTest.insertDoneCb)
|
||||
if self._timer:
|
||||
DhtNetwork.log('This insert step took : ', time_taken, 'second')
|
||||
FeatureTest.lock.wait()
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
# Recover entries now that the trie is complete.
|
||||
for key in keys:
|
||||
PhtTest.key = key
|
||||
with FeatureTest.lock:
|
||||
time_taken = timer(pht.lookup, key, PhtTest.lookupCb, PhtTest.lookupDoneCb)
|
||||
if self._timer:
|
||||
DhtNetwork.log('This lookup step took : ', time_taken, 'second')
|
||||
FeatureTest.lock.wait()
|
||||
|
||||
all_entries[PhtTest.prefix] = [e.__str__()
|
||||
for e in PhtTest.indexEntries]
|
||||
|
||||
for p in all_entries.keys():
|
||||
DhtNetwork.log('All entries under prefix', p, ':')
|
||||
DhtNetwork.log(all_entries[p])
|
||||
PhtTest.drawTrie(all_entries)
|
||||
|
||||
##################################
|
||||
# DHT #
|
||||
@@ -164,7 +367,7 @@ class FeatureTest(object):
|
||||
|
||||
class DhtFeatureTest(FeatureTest):
|
||||
"""
|
||||
This is base test. A method run() implementation is required.
|
||||
This is a base dht test.
|
||||
"""
|
||||
#static variables used by class callbacks
|
||||
successfullTransfer = lambda lv,fv: len(lv) == len(fv)
|
||||
@@ -173,7 +376,6 @@ class DhtFeatureTest(FeatureTest):
|
||||
|
||||
def __init__(self, test, workbench):
|
||||
super(DhtFeatureTest, self).__init__(test, workbench)
|
||||
self.bootstrap = self._workbench.get_bootstrap()
|
||||
|
||||
def _reset(self):
|
||||
super(DhtFeatureTest, self)._reset()
|
||||
@@ -183,15 +385,15 @@ class DhtFeatureTest(FeatureTest):
|
||||
@staticmethod
|
||||
def getcb(value):
|
||||
vstr = value.__str__()[:100]
|
||||
DhtNetwork.log('[GET]: %s' % vstr + ("..." if len(vstr) > 100 else ""))
|
||||
DhtNetwork.Log.log('[GET]: %s' % vstr + ("..." if len(vstr) > 100 else ""))
|
||||
DhtFeatureTest.foreignValues.append(value)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def putDoneCb(ok, nodes):
|
||||
if not ok:
|
||||
DhtNetwork.log("[PUT]: failed!")
|
||||
with FeatureTest.lock:
|
||||
if not ok:
|
||||
DhtNetwork.Log.log("[PUT]: failed!")
|
||||
FeatureTest.done -= 1
|
||||
FeatureTest.lock.notify()
|
||||
|
||||
@@ -199,7 +401,7 @@ class DhtFeatureTest(FeatureTest):
|
||||
def getDoneCb(ok, nodes):
|
||||
with FeatureTest.lock:
|
||||
if not ok:
|
||||
DhtNetwork.log("[GET]: failed!")
|
||||
DhtNetwork.Log.log("[GET]: failed!")
|
||||
else:
|
||||
for node in nodes:
|
||||
if not node.getNode().isExpired():
|
||||
@@ -210,10 +412,10 @@ class DhtFeatureTest(FeatureTest):
|
||||
def _dhtPut(self, producer, _hash, *values):
|
||||
with FeatureTest.lock:
|
||||
for val in values:
|
||||
vstr = val.__str__()[:100]
|
||||
DhtNetwork.log('[PUT]:', _hash.toString(), '->', vstr + ("..." if len(vstr) > 100 else ""))
|
||||
FeatureTest.done += 1
|
||||
producer.put(_hash, val, DhtFeatureTest.putDoneCb)
|
||||
vstr = val.__str__()[:100]
|
||||
DhtNetwork.Log.log('[PUT]:', _hash.toString(), '->', vstr + ("..." if len(vstr) > 100 else ""))
|
||||
FeatureTest.done += 1
|
||||
producer.put(_hash, val, DhtFeatureTest.putDoneCb)
|
||||
while FeatureTest.done > 0:
|
||||
FeatureTest.lock.wait()
|
||||
|
||||
@@ -222,7 +424,7 @@ class DhtFeatureTest(FeatureTest):
|
||||
DhtFeatureTest.foreignNodes = []
|
||||
with FeatureTest.lock:
|
||||
FeatureTest.done += 1
|
||||
DhtNetwork.log('[GET]:', _hash.toString())
|
||||
DhtNetwork.Log.log('[GET]:', _hash.toString())
|
||||
consumer.get(_hash, DhtFeatureTest.getcb, DhtFeatureTest.getDoneCb)
|
||||
while FeatureTest.done > 0:
|
||||
FeatureTest.lock.wait()
|
||||
@@ -234,7 +436,6 @@ class DhtFeatureTest(FeatureTest):
|
||||
for n in DhtFeatureTest.foreignNodes:
|
||||
nodes.add(n)
|
||||
|
||||
|
||||
class PersistenceTest(DhtFeatureTest):
|
||||
"""
|
||||
This tests persistence of data on the network.
|
||||
@@ -292,8 +493,8 @@ class PersistenceTest(DhtFeatureTest):
|
||||
config.setNodeId(InfoHash(_hash_str.encode()))
|
||||
n = DhtRunner()
|
||||
n.run(config=config)
|
||||
n.bootstrap(self.bootstrap.ip4,
|
||||
str(self.bootstrap.port))
|
||||
n.bootstrap(self._bootstrap.ip4,
|
||||
str(self._bootstrap.port))
|
||||
DhtNetwork.log('Node','['+_hash_str+']',
|
||||
'started around', _hash.toString().decode()
|
||||
if n.isRunning() else
|
||||
@@ -302,24 +503,24 @@ class PersistenceTest(DhtFeatureTest):
|
||||
trigger_nodes.append(n)
|
||||
|
||||
def _result(self, local_values, new_nodes):
|
||||
bootstrap = self.bootstrap
|
||||
bootstrap = self._bootstrap
|
||||
if not DhtFeatureTest.successfullTransfer(local_values, DhtFeatureTest.foreignValues):
|
||||
DhtNetwork.log('[GET]: Only %s on %s values persisted.' %
|
||||
DhtNetwork.Log.log('[GET]: Only %s on %s values persisted.' %
|
||||
(len(DhtFeatureTest.foreignValues), len(local_values)))
|
||||
else:
|
||||
DhtNetwork.log('[GET]: All values successfully persisted.')
|
||||
DhtNetwork.Log.log('[GET]: All values successfully persisted.')
|
||||
if DhtFeatureTest.foreignValues:
|
||||
if new_nodes:
|
||||
DhtNetwork.log('Values are newly found on:')
|
||||
DhtNetwork.Log.log('Values are newly found on:')
|
||||
for node in new_nodes:
|
||||
DhtNetwork.log(node)
|
||||
DhtNetwork.Log.log(node)
|
||||
if self._dump_storage:
|
||||
DhtNetwork.log('Dumping all storage log from '\
|
||||
DhtNetwork.Log.log('Dumping all storage log from '\
|
||||
'hosting nodes.')
|
||||
for proc in self._workbench.procs:
|
||||
proc.sendNodesRequest(DhtNetworkSubProcess.DUMP_STORAGE_REQ, DhtFeatureTest.foreignNodes)
|
||||
proc.sendClusterRequest(DhtNetworkSubProcess.DUMP_STORAGE_REQ, DhtFeatureTest.foreignNodes)
|
||||
else:
|
||||
DhtNetwork.log("Values didn't reach new hosting nodes after shutdown.")
|
||||
DhtNetwork.Log.log("Values didn't reach new hosting nodes after shutdown.")
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
@@ -334,13 +535,14 @@ class PersistenceTest(DhtFeatureTest):
|
||||
else:
|
||||
raise NameError("This test is not defined '" + self._test + "'")
|
||||
except Exception as e:
|
||||
print(e)
|
||||
traceback.print_tb(e.__traceback__)
|
||||
print(type(e).__name__+':', e, file=sys.stderr)
|
||||
finally:
|
||||
if self._traffic_plot or self._op_plot:
|
||||
plot_fname = "traffic-plot"
|
||||
print('plot saved to', plot_fname)
|
||||
plt.savefig(plot_fname)
|
||||
self.bootstrap.resize(1)
|
||||
self._bootstrap.resize(1)
|
||||
|
||||
###########
|
||||
# Tests #
|
||||
@@ -353,71 +555,89 @@ class PersistenceTest(DhtFeatureTest):
|
||||
"""
|
||||
trigger_nodes = []
|
||||
wb = self._workbench
|
||||
bootstrap = self.bootstrap
|
||||
bootstrap = self._bootstrap
|
||||
# Value representing an ICE packet. Each ICE packet is around 1KB.
|
||||
VALUE_SIZE = 1024
|
||||
NUM_VALUES = self._num_values/wb.node_num if self._num_values else 5
|
||||
nr_values = NUM_VALUES * wb.node_num
|
||||
nr_nodes = wb.node_num
|
||||
nr_nodes_cv = threading.Condition()
|
||||
num_values_per_hash = self._num_values/wb.node_num if self._num_values else 5
|
||||
|
||||
values = [b''.join(random.choice(string.hexdigits).encode() for _ in range(VALUE_SIZE)) for __ in range(NUM_VALUES)]
|
||||
# nodes and values counters
|
||||
total_nr_values = 0
|
||||
nr_nodes = wb.node_num
|
||||
op_cv = threading.Condition()
|
||||
|
||||
# values string in string format. Used for sending cluster request.
|
||||
hashes = [random_hash() for _ in range(wb.node_num)]
|
||||
|
||||
# initial set of values
|
||||
i = 0
|
||||
for h in hashes:
|
||||
self._dhtPut(bootstrap.front(), h, *[Value(v) for v in values])
|
||||
print("at: ", i)
|
||||
i += 1
|
||||
|
||||
def normalBehavior(do, t, log=None):
|
||||
nonlocal nr_values
|
||||
def normalBehavior(do, t):
|
||||
nonlocal total_nr_values, op_cv
|
||||
while True:
|
||||
do()
|
||||
time.sleep(random.choice(range(t)))
|
||||
with op_cv:
|
||||
do()
|
||||
time.sleep(random.uniform(0.0, float(t)))
|
||||
|
||||
def putRequest():
|
||||
nonlocal hashes, values, nr_values
|
||||
nr_values += 1
|
||||
DhtNetwork.log("Random value put on the DHT.", "(now "+ str(nr_values)+" values on the dht)")
|
||||
random.choice(wb.procs).sendNodePutRequest(random.choice(hashes).toString(), random.choice(values))
|
||||
puts = threading.Thread(target=normalBehavior, args=(putRequest, 30))
|
||||
nonlocal hashes, VALUE_SIZE, total_nr_values
|
||||
lock = threading.Condition()
|
||||
def dcb(success):
|
||||
nonlocal total_nr_values, lock
|
||||
if success:
|
||||
total_nr_values += 1
|
||||
DhtNetwork.Log.log("INFO: "+ str(total_nr_values)+" values put on the dht since begining")
|
||||
with lock:
|
||||
lock.notify()
|
||||
with lock:
|
||||
DhtNetwork.Log.warn("Random value put on the DHT...")
|
||||
random.choice(wb.procs).sendClusterPutRequest(random.choice(hashes).toString(),
|
||||
random_str_val(size=VALUE_SIZE).encode(),
|
||||
done_cb=dcb)
|
||||
lock.wait()
|
||||
|
||||
puts = threading.Thread(target=normalBehavior, args=(putRequest, 30.0/wb.node_num))
|
||||
puts.daemon = True
|
||||
puts.start()
|
||||
|
||||
def newNodeRequest():
|
||||
nonlocal nr_nodes
|
||||
with nr_nodes_cv:
|
||||
lock = threading.Condition()
|
||||
def dcb(success):
|
||||
nonlocal nr_nodes, lock
|
||||
nr_nodes += 1
|
||||
DhtNetwork.log("Node joining the DHT.", "(now "+str(nr_nodes)+" nodes on the dht)")
|
||||
nr_nodes_cv.notify()
|
||||
random.choice(wb.procs).sendNodesRequest(DhtNetworkSubProcess.NEW_NODE_REQ)
|
||||
connections = threading.Thread(target=normalBehavior, args=(newNodeRequest, 1*60))
|
||||
DhtNetwork.Log.log("INFO: now "+str(nr_nodes)+" nodes on the dht")
|
||||
with lock:
|
||||
lock.notify()
|
||||
with lock:
|
||||
DhtNetwork.Log.warn("Node joining...")
|
||||
random.choice(wb.procs).sendClusterRequest(DhtNetworkSubProcess.NEW_NODE_REQ, done_cb=dcb)
|
||||
lock.wait()
|
||||
|
||||
connections = threading.Thread(target=normalBehavior, args=(newNodeRequest, 1*50.0/wb.node_num))
|
||||
connections.daemon = True
|
||||
connections.start()
|
||||
|
||||
def shutdownNodeRequest():
|
||||
nonlocal nr_nodes
|
||||
with nr_nodes_cv:
|
||||
nr_nodes -= 1
|
||||
DhtNetwork.log("Node quitting the DHT.", "(now "+str(nr_nodes)+" nodes on the dht)")
|
||||
nr_nodes_cv.notify()
|
||||
random.choice(wb.procs).sendNodesRequest(DhtNetworkSubProcess.SHUTDOWN_NODE_REQ)
|
||||
shutdowns = threading.Thread(target=normalBehavior, args=(shutdownNodeRequest, 1*60))
|
||||
lock = threading.Condition()
|
||||
def dcb(success):
|
||||
nonlocal nr_nodes, lock
|
||||
if success:
|
||||
nr_nodes -= 1
|
||||
DhtNetwork.Log.log("INFO: now "+str(nr_nodes)+" nodes on the dht")
|
||||
else:
|
||||
DhtNetwork.Log.err("Oops.. No node to shutodwn.")
|
||||
|
||||
with lock:
|
||||
lock.notify()
|
||||
with lock:
|
||||
DhtNetwork.Log.warn("Node shutting down...")
|
||||
random.choice(wb.procs).sendClusterRequest(DhtNetworkSubProcess.SHUTDOWN_NODE_REQ, done_cb=dcb)
|
||||
lock.wait()
|
||||
|
||||
shutdowns = threading.Thread(target=normalBehavior, args=(shutdownNodeRequest, 1*60.0/wb.node_num))
|
||||
shutdowns.daemon = True
|
||||
shutdowns.start()
|
||||
|
||||
for h in hashes:
|
||||
self._trigger_dp(trigger_nodes, h)
|
||||
|
||||
if self._traffic_plot:
|
||||
ydata = []
|
||||
xdata = []
|
||||
# warning: infinite loop
|
||||
interval = 2
|
||||
for rate in iftop_traffic_data("br"+wb.ifname, interval=interval):
|
||||
ydata.append(rate)
|
||||
xdata.append((xdata[-1] if len(xdata) > 0 else 0) + interval)
|
||||
display_plot(ydata, xvals=xdata, yformatter=Kbit_format, color='blue')
|
||||
display_traffic_plot('br'+wb.ifname)
|
||||
else:
|
||||
# blocks in matplotlib thread
|
||||
while True:
|
||||
@@ -430,7 +650,7 @@ class PersistenceTest(DhtFeatureTest):
|
||||
It uses Dht shutdown call from the API to gracefuly finish the nodes one
|
||||
after the other.
|
||||
"""
|
||||
bootstrap = self.bootstrap
|
||||
bootstrap = self._bootstrap
|
||||
|
||||
ops_count = []
|
||||
|
||||
@@ -447,36 +667,54 @@ class PersistenceTest(DhtFeatureTest):
|
||||
self._dhtGet(consumer, myhash)
|
||||
if not DhtFeatureTest.successfullTransfer(local_values, DhtFeatureTest.foreignValues):
|
||||
if DhtFeatureTest.foreignValues:
|
||||
DhtNetwork.log('[GET]: Only ', len(DhtFeatureTest.foreignValues) ,' on ',
|
||||
DhtNetwork.Log.log('[GET]: Only ', len(DhtFeatureTest.foreignValues) ,' on ',
|
||||
len(local_values), ' values successfully put.')
|
||||
else:
|
||||
DhtNetwork.log('[GET]: 0 values successfully put')
|
||||
DhtNetwork.Log.log('[GET]: 0 values successfully put')
|
||||
|
||||
|
||||
if DhtFeatureTest.foreignValues and DhtFeatureTest.foreignNodes:
|
||||
DhtNetwork.log('Values are found on :')
|
||||
DhtNetwork.Log.log('Values are found on :')
|
||||
for node in DhtFeatureTest.foreignNodes:
|
||||
DhtNetwork.log(node)
|
||||
DhtNetwork.Log.log(node)
|
||||
|
||||
for _ in range(max(1, int(self._workbench.node_num/32))):
|
||||
DhtNetwork.log('Removing all nodes hosting target values...')
|
||||
DhtNetwork.Log.log('Removing all nodes hosting target values...')
|
||||
cluster_ops_count = 0
|
||||
for proc in self._workbench.procs:
|
||||
DhtNetwork.log('[REMOVE]: sending shutdown request to', proc)
|
||||
proc.sendNodesRequest(
|
||||
DhtNetwork.Log.log('[REMOVE]: sending shutdown request to', proc)
|
||||
lock = threading.Condition()
|
||||
def dcb(success):
|
||||
nonlocal lock
|
||||
if not success:
|
||||
DhtNetwork.Log.err("Failed to shutdown.")
|
||||
with lock:
|
||||
lock.notify()
|
||||
|
||||
with lock:
|
||||
proc.sendClusterRequest(
|
||||
DhtNetworkSubProcess.SHUTDOWN_NODE_REQ,
|
||||
DhtFeatureTest.foreignNodes
|
||||
)
|
||||
DhtNetwork.log('sending message stats request')
|
||||
stats = proc.sendGetMessageStats()
|
||||
cluster_ops_count += sum(stats[1:])
|
||||
DhtNetwork.log("5 seconds wait...")
|
||||
DhtFeatureTest.foreignNodes,
|
||||
done_cb=dcb
|
||||
)
|
||||
lock.wait()
|
||||
DhtNetwork.Log.log('sending message stats request')
|
||||
def msg_dcb(stats):
|
||||
nonlocal cluster_ops_count, lock
|
||||
if stats:
|
||||
cluster_ops_count += sum(stats[1:])
|
||||
with lock:
|
||||
lock.notify()
|
||||
with lock:
|
||||
proc.sendGetMessageStats(done_cb=msg_dcb)
|
||||
lock.wait()
|
||||
DhtNetwork.Log.log("5 seconds wait...")
|
||||
time.sleep(5)
|
||||
ops_count.append(cluster_ops_count/self._workbench.node_num)
|
||||
|
||||
# checking if values were transfered to new nodes
|
||||
foreignNodes_before_delete = DhtFeatureTest.foreignNodes
|
||||
DhtNetwork.log('[GET]: trying to fetch persistent values')
|
||||
DhtNetwork.Log.log('[GET]: trying to fetch persistent values')
|
||||
self._dhtGet(consumer, myhash)
|
||||
new_nodes = set(DhtFeatureTest.foreignNodes) - set(foreignNodes_before_delete)
|
||||
|
||||
@@ -485,7 +723,11 @@ class PersistenceTest(DhtFeatureTest):
|
||||
if self._op_plot:
|
||||
display_plot(ops_count, color='blue')
|
||||
else:
|
||||
DhtNetwork.log("[GET]: either couldn't fetch values or nodes hosting values...")
|
||||
DhtNetwork.Log.log("[GET]: either couldn't fetch values or nodes hosting values...")
|
||||
|
||||
if traffic_plot_thread:
|
||||
print("Traffic plot running for ever. Ctrl-c for stopping it.")
|
||||
traffic_plot_thread.join()
|
||||
|
||||
@reset_before_test
|
||||
def _replaceClusterTest(self):
|
||||
@@ -494,7 +736,7 @@ class PersistenceTest(DhtFeatureTest):
|
||||
"""
|
||||
clusters = 8
|
||||
|
||||
bootstrap = self.bootstrap
|
||||
bootstrap = self._bootstrap
|
||||
|
||||
bootstrap.resize(3)
|
||||
consumer = bootstrap.get(1)
|
||||
@@ -507,16 +749,16 @@ class PersistenceTest(DhtFeatureTest):
|
||||
self._dhtGet(consumer, myhash)
|
||||
initial_nodes = DhtFeatureTest.foreignNodes
|
||||
|
||||
DhtNetwork.log('Replacing', clusters, 'random clusters successively...')
|
||||
DhtNetwork.Log.log('Replacing', clusters, 'random clusters successively...')
|
||||
for n in range(clusters):
|
||||
i = random.randint(0, len(self._workbench.procs)-1)
|
||||
proc = self._workbench.procs[i]
|
||||
DhtNetwork.log('Replacing', proc)
|
||||
proc.sendNodesRequest(DhtNetworkSubProcess.SHUTDOWN_CLUSTER_REQ)
|
||||
DhtNetwork.Log.log('Replacing', proc)
|
||||
proc.sendClusterRequest(DhtNetworkSubProcess.SHUTDOWN_CLUSTER_REQ)
|
||||
self._workbench.stop_cluster(i)
|
||||
self._workbench.start_cluster(i)
|
||||
|
||||
DhtNetwork.log('[GET]: trying to fetch persistent values')
|
||||
DhtNetwork.Log.log('[GET]: trying to fetch persistent values')
|
||||
self._dhtGet(consumer, myhash)
|
||||
new_nodes = set(DhtFeatureTest.foreignNodes) - set(initial_nodes)
|
||||
|
||||
@@ -531,17 +773,17 @@ class PersistenceTest(DhtFeatureTest):
|
||||
minutes for the nodes to trigger storage maintenance.
|
||||
"""
|
||||
trigger_nodes = []
|
||||
bootstrap = self.bootstrap
|
||||
bootstrap = self._bootstrap
|
||||
|
||||
N_PRODUCERS = self._num_producers if self._num_values else 16
|
||||
DP_TIMEOUT = 1
|
||||
|
||||
hashes = []
|
||||
|
||||
# Generating considerable amount of values of size 1KB.
|
||||
# Generating considerable amount of values of size 1KB.
|
||||
VALUE_SIZE = 1024
|
||||
NUM_VALUES = self._num_values if self._num_values else 50
|
||||
values = [Value(''.join(random.choice(string.hexdigits) for _ in range(VALUE_SIZE)).encode()) for _ in range(NUM_VALUES)]
|
||||
values = [Value(random_str_val(size=VALUE_SIZE).encode()) for _ in range(NUM_VALUES)]
|
||||
|
||||
bootstrap.resize(N_PRODUCERS+2)
|
||||
consumer = bootstrap.get(N_PRODUCERS+1)
|
||||
@@ -555,21 +797,21 @@ class PersistenceTest(DhtFeatureTest):
|
||||
nodes = set([])
|
||||
self._gottaGetThemAllPokeNodes(consumer, hashes, nodes=nodes)
|
||||
|
||||
DhtNetwork.log("Values are found on:")
|
||||
DhtNetwork.Log.log("Values are found on:")
|
||||
for n in nodes:
|
||||
DhtNetwork.log(n)
|
||||
DhtNetwork.Log.log(n)
|
||||
|
||||
DhtNetwork.log("Creating 8 nodes around all of these hashes...")
|
||||
DhtNetwork.Log.log("Creating 8 nodes around all of these hashes...")
|
||||
for _hash in hashes:
|
||||
self._trigger_dp(trigger_nodes, _hash, count=8)
|
||||
|
||||
DhtNetwork.log('Waiting', DP_TIMEOUT+1, 'minutes for normal storage maintenance.')
|
||||
DhtNetwork.Log.log('Waiting', DP_TIMEOUT+1, 'minutes for normal storage maintenance.')
|
||||
time.sleep((DP_TIMEOUT+1)*60)
|
||||
|
||||
DhtNetwork.log('Deleting old nodes from previous search.')
|
||||
DhtNetwork.Log.log('Deleting old nodes from previous search.')
|
||||
for proc in self._workbench.procs:
|
||||
DhtNetwork.log('[REMOVE]: sending delete request to', proc)
|
||||
proc.sendNodesRequest(
|
||||
DhtNetwork.Log.log('[REMOVE]: sending delete request to', proc)
|
||||
proc.sendClusterRequest(
|
||||
DhtNetworkSubProcess.REMOVE_NODE_REQ,
|
||||
nodes)
|
||||
|
||||
@@ -610,9 +852,10 @@ class PerformanceTest(DhtFeatureTest):
|
||||
else:
|
||||
raise NameError("This test is not defined '" + self._test + "'")
|
||||
except Exception as e:
|
||||
print(e)
|
||||
traceback.print_tb(e.__traceback__)
|
||||
print(type(e).__name__+':', e, file=sys.stderr)
|
||||
finally:
|
||||
self.bootstrap.resize(1)
|
||||
self._bootstrap.resize(1)
|
||||
|
||||
|
||||
###########
|
||||
@@ -624,7 +867,7 @@ class PerformanceTest(DhtFeatureTest):
|
||||
"""
|
||||
Tests for performance of the DHT doing multiple get() operation.
|
||||
"""
|
||||
bootstrap = self.bootstrap
|
||||
bootstrap = self._bootstrap
|
||||
|
||||
plt.ion()
|
||||
|
||||
@@ -649,7 +892,7 @@ class PerformanceTest(DhtFeatureTest):
|
||||
|
||||
def getcb(v):
|
||||
nonlocal bootstrap
|
||||
DhtNetwork.log("found", v)
|
||||
DhtNetwork.Log.log("found", v)
|
||||
return True
|
||||
|
||||
def donecb(ok, nodes, start):
|
||||
@@ -657,7 +900,7 @@ class PerformanceTest(DhtFeatureTest):
|
||||
t = time.time()-start
|
||||
with lock:
|
||||
if not ok:
|
||||
DhtNetwork.log("failed !")
|
||||
DhtNetwork.Log.log("failed !")
|
||||
times.append(t)
|
||||
done -= 1
|
||||
lock.notify()
|
||||
@@ -689,7 +932,7 @@ class PerformanceTest(DhtFeatureTest):
|
||||
for n in range(10):
|
||||
self._workbench.replace_cluster()
|
||||
plt.pause(2)
|
||||
DhtNetwork.log("Getting 50 random hashes succesively.")
|
||||
DhtNetwork.Log.log("Getting 50 random hashes succesively.")
|
||||
for i in range(50):
|
||||
with lock:
|
||||
for _ in range(1):
|
||||
@@ -713,8 +956,7 @@ class PerformanceTest(DhtFeatureTest):
|
||||
deleting around the target hash.
|
||||
"""
|
||||
|
||||
|
||||
bootstrap = self.bootstrap
|
||||
bootstrap = self._bootstrap
|
||||
|
||||
bootstrap.resize(3)
|
||||
consumer = bootstrap.get(1)
|
||||
@@ -725,28 +967,28 @@ class PerformanceTest(DhtFeatureTest):
|
||||
|
||||
for _ in range(max(1, int(self._workbench.node_num/32))):
|
||||
self._dhtGet(consumer, myhash)
|
||||
DhtNetwork.log("Waiting 15 seconds...")
|
||||
DhtNetwork.Log.log("Waiting 15 seconds...")
|
||||
time.sleep(15)
|
||||
|
||||
self._dhtPut(producer, myhash, *local_values)
|
||||
|
||||
#checking if values were transfered
|
||||
self._dhtGet(consumer, myhash)
|
||||
DhtNetwork.log('Values are found on :')
|
||||
DhtNetwork.Log.log('Values are found on :')
|
||||
for node in DhtFeatureTest.foreignNodes:
|
||||
DhtNetwork.log(node)
|
||||
DhtNetwork.Log.log(node)
|
||||
|
||||
if not DhtFeatureTest.successfullTransfer(local_values, DhtFeatureTest.foreignValues):
|
||||
if DhtFeatureTest.foreignValues:
|
||||
DhtNetwork.log('[GET]: Only ', len(DhtFeatureTest.foreignValues) ,' on ',
|
||||
DhtNetwork.Log.log('[GET]: Only ', len(DhtFeatureTest.foreignValues) ,' on ',
|
||||
len(local_values), ' values successfully put.')
|
||||
else:
|
||||
DhtNetwork.log('[GET]: 0 values successfully put')
|
||||
DhtNetwork.Log.log('[GET]: 0 values successfully put')
|
||||
|
||||
DhtNetwork.log('Removing all nodes hosting target values...')
|
||||
DhtNetwork.Log.log('Removing all nodes hosting target values...')
|
||||
for proc in self._workbench.procs:
|
||||
DhtNetwork.log('[REMOVE]: sending shutdown request to', proc)
|
||||
proc.sendNodesRequest(
|
||||
DhtNetwork.Log.log('[REMOVE]: sending shutdown request to', proc)
|
||||
proc.sendClusterRequest(
|
||||
DhtNetworkSubProcess.SHUTDOWN_NODE_REQ,
|
||||
DhtFeatureTest.foreignNodes
|
||||
)
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016 Savoir-faire Linux Inc.
|
||||
# Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from twisted.web import server, resource
|
||||
from twisted.internet import reactor, endpoints
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import opendht as dht
|
||||
import base64, json
|
||||
|
||||
class DhtServer(resource.Resource):
|
||||
isLeaf = True
|
||||
node = dht.DhtRunner()
|
||||
|
||||
def __init__(self, port, bootstrap):
|
||||
self.node.run(port=port)
|
||||
b_url = urlparse('//'+bootstrap)
|
||||
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)
|
||||
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
|
||||
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))
|
||||
return json.dumps({'success':True}).encode()
|
||||
else:
|
||||
req.setResponseCode(400)
|
||||
return json.dumps({'success':False, 'error':'no data parameter'}).encode()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description='Launch an OpenDHT node with an HTTP control interface')
|
||||
parser.add_argument('-p', '--port', help='OpenDHT port to bind', type=int, default=4222)
|
||||
parser.add_argument('-hp', '--http-port', help='HTTP port to bind', type=int, default=8080)
|
||||
parser.add_argument('-b', '--bootstrap', help='bootstrap address', default="bootstrap.ring.cx:4222")
|
||||
args = parser.parse_args()
|
||||
endpoints.serverFromString(reactor, "tcp:"+str(args.http_port)).listen(server.Site(DhtServer(args.port, args.bootstrap)))
|
||||
reactor.run()
|
||||
Executable
+83
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2016 Savoir-faire Linux Inc.
|
||||
# Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import time
|
||||
import argparse
|
||||
import time
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
|
||||
import opendht as dht
|
||||
|
||||
parser = argparse.ArgumentParser(description='Create a dht network of -n nodes')
|
||||
parser.add_argument('-b', '--bootstrap', help='bootstrap address', default='bootstrap.ring.cx')
|
||||
parser.add_argument('-n', '--num-ops', help='number of concurrent operations on the DHT', type=int, default=8)
|
||||
parser.add_argument('-p', '--period', help='duration between each test (seconds)', type=int, default=60)
|
||||
parser.add_argument('-t', '--timeout', help='timeout for a test to complete (seconds)', type=float, default=15)
|
||||
args = parser.parse_args()
|
||||
|
||||
node1 = dht.DhtRunner()
|
||||
node1.run()
|
||||
|
||||
node2 = dht.DhtRunner()
|
||||
node2.run()
|
||||
|
||||
node1.bootstrap(args.bootstrap)
|
||||
node2.bootstrap(args.bootstrap)
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
pending_tests = {}
|
||||
keys = [dht.InfoHash.getRandom() for _ in range(args.num_ops)]
|
||||
|
||||
def listen_cb(key, val):
|
||||
global pending_tests
|
||||
kstr = str(key)
|
||||
if kstr in pending_tests:
|
||||
if pending_tests[kstr]['v'].id == val.id:
|
||||
pending_tests.pop(kstr, None)
|
||||
else:
|
||||
print("Expected vid", val.id, "got", pending_tests[kstr]['v'].id)
|
||||
return True
|
||||
|
||||
def listen(key):
|
||||
node1.listen(key, lambda v: loop.call_soon_threadsafe(listen_cb, key, v))
|
||||
|
||||
for key in keys:
|
||||
listen(key)
|
||||
|
||||
next_test = time.time()
|
||||
while True:
|
||||
start = time.time()
|
||||
#print(datetime.fromtimestamp(start).strftime('%Y-%m-%d %H:%M:%S'), 'Test started')
|
||||
for key in keys:
|
||||
val = dht.Value(str(dht.InfoHash.getRandom()).encode())
|
||||
pending_tests[str(key)] = {'v':val, 'c':0}
|
||||
node2.put(key, val, lambda ok, nodes: ok)
|
||||
while len(pending_tests):
|
||||
loop.stop()
|
||||
loop.run_forever()
|
||||
time.sleep(1)
|
||||
if time.time()-start > args.timeout:
|
||||
print('Test timeout !')
|
||||
exit(1)
|
||||
|
||||
end = time.time()
|
||||
print(datetime.fromtimestamp(end).strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'Test completed successfully in', end-start)
|
||||
next_test += args.period
|
||||
if next_test > end:
|
||||
time.sleep(next_test-end)
|
||||
Executable
+63
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2016 Savoir-faire Linux Inc.
|
||||
# Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import opendht as dht
|
||||
import time
|
||||
import asyncio
|
||||
|
||||
ping_node = dht.DhtRunner()
|
||||
ping_node.run(port = 5632)
|
||||
#ping_node.enableLogging()
|
||||
#ping_node.bootstrap("bootstrap.ring.cx", "4222")
|
||||
|
||||
pong_node = dht.DhtRunner()
|
||||
pong_node.run(port = 5633)
|
||||
#pong_node.enableLogging()
|
||||
pong_node.bootstrap("192.168.49.55", "5632");
|
||||
#pong_node.bootstrap("192.168.49.55", "5632");
|
||||
|
||||
loc_ping = dht.InfoHash.get("toto99")
|
||||
loc_pong = dht.InfoHash.get(str(loc_ping))
|
||||
|
||||
i = 0
|
||||
MAX = 8192
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
def done(h, ok):
|
||||
print(h, "over", ok)
|
||||
|
||||
def ping(node, h):
|
||||
global i
|
||||
time.sleep(0.0075)
|
||||
i += 1
|
||||
if i < MAX:
|
||||
node.put(h, dht.Value(b"hey"), lambda ok, nodes: done(node.getNodeId().decode(), ok))
|
||||
else:
|
||||
loop.stop()
|
||||
|
||||
def pong(node, h):
|
||||
print(node.getNodeId().decode(), "got ping", h, i)
|
||||
loop.call_soon_threadsafe(ping, node, h);
|
||||
return True
|
||||
|
||||
ping_node.listen(loc_ping, lambda v: pong(pong_node, loc_pong))
|
||||
pong_node.listen(loc_pong, lambda v: pong(ping_node, loc_ping))
|
||||
|
||||
ping(pong_node, loc_ping)
|
||||
|
||||
loop.run_forever()
|
||||
@@ -18,7 +18,7 @@
|
||||
import time, sys, os
|
||||
from pprint import pprint
|
||||
from math import cos, sin, pi
|
||||
from urllib import request
|
||||
import urllib3
|
||||
import gzip
|
||||
import asyncio
|
||||
|
||||
@@ -35,6 +35,8 @@ from mpl_toolkits.basemap import Basemap
|
||||
|
||||
import GeoIP
|
||||
|
||||
http = urllib3.PoolManager()
|
||||
|
||||
done = 0
|
||||
all_nodes = NodeSet()
|
||||
|
||||
@@ -72,7 +74,7 @@ def check_dl(fname, url):
|
||||
if os.path.isfile(fname):
|
||||
return
|
||||
print('downloading', url)
|
||||
ghandle = gzip.GzipFile(fileobj=request.urlopen(url))
|
||||
ghandle = gzip.GzipFile(fileobj=http.request('GET', url, headers={'User-Agent': 'Mozilla/5.0'}, preload_content=False))
|
||||
with open(fname, 'wb') as out:
|
||||
for line in ghandle:
|
||||
out.write(line)
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg3492"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
xml:space="preserve"
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 256 255.99999"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90"><title
|
||||
id="title3357">OpenDHT</title><metadata
|
||||
id="metadata3498"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title>OpenDHT</dc:title><dc:rights><cc:Agent><dc:title></dc:title></cc:Agent></dc:rights><dc:creator><cc:Agent><dc:title>Savoir-faire Linux Inc.</dc:title></cc:Agent></dc:creator><dc:relation>https://opendht.net</dc:relation></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs3496"><clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath3534"><path
|
||||
d="m 0,0 1152,0 0,648 L 0,648 0,0 Z"
|
||||
id="path3536"
|
||||
inkscape:connector-curvature="0" /></clipPath></defs><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1855"
|
||||
inkscape:window-height="1056"
|
||||
id="namedview3494"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
fit-margin-top="5"
|
||||
fit-margin-right="5"
|
||||
fit-margin-bottom="5"
|
||||
fit-margin-left="5"
|
||||
inkscape:zoom="1.9555556"
|
||||
inkscape:cx="63.161953"
|
||||
inkscape:cy="141.16919"
|
||||
inkscape:window-x="1985"
|
||||
inkscape:window-y="24"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g3532" /><g
|
||||
id="g3500"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="SFL-logo-DHT"
|
||||
transform="matrix(1.25,0,0,-1.25,-589.00821,604.10631)"><g
|
||||
id="g3530"><g
|
||||
id="g3532"
|
||||
clip-path="url(#clipPath3534)"><g
|
||||
id="g3359"
|
||||
transform="matrix(0.9931555,0,0,0.99058777,1.5489503,0.9813209)"><g
|
||||
transform="translate(485.3798,383.7826)"
|
||||
id="g3538"><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3540"
|
||||
style="fill:#47b3d1;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 0,50.03 40.554,90.586 90.586,90.586 50.028,0 90.587,-40.556 90.587,-90.586 0,-50.03 -40.559,-90.588 -90.587,-90.588 C 40.554,-90.588 0,-50.03 0,0" /></g><g
|
||||
transform="translate(576.0338,313.2273)"
|
||||
id="g3542"><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3544"
|
||||
style="fill:#0091ba;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 0,54.307 -24.009,102.977 -61.954,136.075 -17.613,-16.526 -28.632,-39.996 -28.632,-66.056 0,-49.629 39.916,-89.912 89.393,-90.555 0.396,-0.009 0.792,-0.033 1.193,-0.033 L 0,-0.082 0,0 Z" /></g><g
|
||||
transform="translate(575.9661,292.6626)"
|
||||
id="g3546"><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3548"
|
||||
style="fill:#007aa3;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c -1.439,49.482 -41.105,89.355 -90.504,91.12 0,-0.178 -0.015,-0.355 -0.015,-0.536 C -90.519,40.578 -49.998,0.039 0,0" /></g><g
|
||||
transform="translate(532.4411,337.497)"
|
||||
id="g3550"><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3552"
|
||||
style="fill:#006382;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c -10.555,0 -20.236,-3.754 -27.777,-10 16.565,-21.182 42.472,-34.815 71.442,-34.834 0.01,0.415 -0.072,0.821 -0.072,1.241 C 43.593,-19.516 24.077,0 0,0" /></g><g
|
||||
transform="translate(576.0338,313.2273)"
|
||||
id="g3554"><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3556"
|
||||
style="fill:#0091ba;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="M 0,0 C 0,54.307 24.014,102.977 61.954,136.075 79.567,119.549 90.586,96.079 90.586,70.019 90.586,20.39 50.675,-19.893 1.198,-20.536 0.797,-20.545 0.406,-20.569 0.005,-20.569 l 0,20.487 C 0.005,-0.053 0,-0.029 0,0" /></g><g
|
||||
transform="translate(576.1063,292.6626)"
|
||||
id="g3558"><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3560"
|
||||
style="fill:#007aa3;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 1.44,49.482 41.105,89.355 90.504,91.12 0,-0.178 0.01,-0.355 0.01,-0.536 C 90.514,40.578 49.998,0.039 0,0" /></g><g
|
||||
transform="translate(619.6266,337.497)"
|
||||
id="g3562"><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3564"
|
||||
style="fill:#006382;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 10.56,0 20.241,-3.754 27.782,-10 -16.57,-21.182 -42.405,-34.815 -71.375,-34.834 -0.009,0.415 0,0.821 0,1.241 C -43.593,-19.516 -24.072,0 0,0" /></g></g></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
+14
-2
@@ -3,6 +3,7 @@ SUBDIRS = argon2
|
||||
lib_LTLIBRARIES = libopendht.la
|
||||
|
||||
AM_CPPFLAGS = -I../include/opendht
|
||||
libopendht_la_CXXFLAGS = @CXXFLAGS@
|
||||
libopendht_la_LDFLAGS = @LDFLAGS@ @GnuTLS_LIBS@ @Nettle_LIBS@
|
||||
libopendht_la_LIBADD = ./argon2/libargon2.la
|
||||
|
||||
@@ -19,7 +20,9 @@ libopendht_la_SOURCES = \
|
||||
crypto.cpp \
|
||||
securedht.cpp \
|
||||
dhtrunner.cpp \
|
||||
default_types.cpp
|
||||
default_types.cpp \
|
||||
indexation/pht.cpp \
|
||||
log.cpp
|
||||
|
||||
if WIN32
|
||||
libopendht_la_SOURCES += rng.cpp
|
||||
@@ -27,13 +30,17 @@ endif
|
||||
|
||||
nobase_include_HEADERS = \
|
||||
../include/opendht.h \
|
||||
../include/opendht/def.h \
|
||||
../include/opendht/dht.h \
|
||||
../include/opendht/callbacks.h \
|
||||
../include/opendht/node_cache.h \
|
||||
../include/opendht/routing_table.h \
|
||||
../include/opendht/network_engine.h \
|
||||
../include/opendht/net.h \
|
||||
../include/opendht/scheduler.h \
|
||||
../include/opendht/rate_limiter.h \
|
||||
../include/opendht/utils.h \
|
||||
../include/opendht/sockaddr.h \
|
||||
../include/opendht/infohash.h \
|
||||
../include/opendht/node.h \
|
||||
../include/opendht/value.h \
|
||||
@@ -42,4 +49,9 @@ nobase_include_HEADERS = \
|
||||
../include/opendht/dhtrunner.h \
|
||||
../include/opendht/default_types.h \
|
||||
../include/opendht/log.h \
|
||||
../include/opendht/rng.h
|
||||
../include/opendht/log_enable.h \
|
||||
../include/opendht/rng.h \
|
||||
../include/opendht/indexation/pht.h
|
||||
|
||||
nobase_noinst_HEADERS = \
|
||||
../include/opendht/request.h
|
||||
+11
-1
@@ -10,4 +10,14 @@ libargon2_la_SOURCES = \
|
||||
encoding.c \
|
||||
ref.c
|
||||
|
||||
EXTRA_libargon2_la_SOURCES = opt.c
|
||||
noinst_HEADERS = \
|
||||
argon2.h \
|
||||
core.h \
|
||||
blake2/blake2.h \
|
||||
blake2/blake2-impl.h \
|
||||
blake2/blamka-round-ref.h \
|
||||
blake2/blamka-round-opt.h \
|
||||
thread.h \
|
||||
encoding.h \
|
||||
opt.h \
|
||||
ref.h
|
||||
+73
-22
@@ -1,14 +1,18 @@
|
||||
/*
|
||||
* Argon2 source code package
|
||||
* Argon2 reference source code package - reference C implementations
|
||||
*
|
||||
* Written by Daniel Dinu and Dmitry Khovratovich, 2015
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*
|
||||
* This work is licensed under a Creative Commons CC0 1.0 License/Waiver.
|
||||
* You may use this work under the terms of a Creative Commons CC0 1.0
|
||||
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
|
||||
* these licenses can be found at:
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication along
|
||||
* with
|
||||
* this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* You should have received a copy of both of these licenses along with this
|
||||
* software. If not, they may be obtained at the above URLs.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
@@ -19,6 +23,18 @@
|
||||
#include "encoding.h"
|
||||
#include "core.h"
|
||||
|
||||
const char *argon2_type2string(argon2_type type, int uppercase) {
|
||||
switch (type) {
|
||||
case Argon2_d:
|
||||
return uppercase ? "Argon2d" : "argon2d";
|
||||
case Argon2_i:
|
||||
return uppercase ? "Argon2i" : "argon2i";
|
||||
case Argon2_id:
|
||||
return uppercase ? "Argon2id" : "argon2id";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int argon2_ctx(argon2_context *context, argon2_type type) {
|
||||
/* 1. Validate all inputs */
|
||||
@@ -30,7 +46,7 @@ int argon2_ctx(argon2_context *context, argon2_type type) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (Argon2_d != type && Argon2_i != type) {
|
||||
if (Argon2_d != type && Argon2_i != type && Argon2_id != type) {
|
||||
return ARGON2_INCORRECT_TYPE;
|
||||
}
|
||||
|
||||
@@ -81,25 +97,19 @@ int argon2_hash(const uint32_t t_cost, const uint32_t m_cost,
|
||||
const uint32_t parallelism, const void *pwd,
|
||||
const size_t pwdlen, const void *salt, const size_t saltlen,
|
||||
void *hash, const size_t hashlen, char *encoded,
|
||||
const size_t encodedlen, argon2_type type,
|
||||
const size_t encodedlen, argon2_type type,
|
||||
const uint32_t version){
|
||||
|
||||
argon2_context context;
|
||||
int result;
|
||||
uint8_t *out;
|
||||
|
||||
/* Detect and reject overflowing sizes */
|
||||
/* TODO: This should probably be fixed in the function signature */
|
||||
if (pwdlen > ARGON2_MAX_PWD_LENGTH) {
|
||||
return ARGON2_PWD_TOO_LONG;
|
||||
}
|
||||
|
||||
if (hashlen > ARGON2_MAX_OUTLEN) {
|
||||
return ARGON2_OUTPUT_TOO_LONG;
|
||||
}
|
||||
|
||||
if (saltlen > ARGON2_MAX_SALT_LENGTH) {
|
||||
return ARGON2_SALT_TOO_LONG;
|
||||
if (hashlen < ARGON2_MIN_OUTLEN) {
|
||||
return ARGON2_OUTPUT_TOO_SHORT;
|
||||
}
|
||||
|
||||
out = malloc(hashlen);
|
||||
@@ -194,6 +204,26 @@ int argon2d_hash_raw(const uint32_t t_cost, const uint32_t m_cost,
|
||||
hash, hashlen, NULL, 0, Argon2_d, ARGON2_VERSION_NUMBER);
|
||||
}
|
||||
|
||||
int argon2id_hash_encoded(const uint32_t t_cost, const uint32_t m_cost,
|
||||
const uint32_t parallelism, const void *pwd,
|
||||
const size_t pwdlen, const void *salt,
|
||||
const size_t saltlen, const size_t hashlen,
|
||||
char *encoded, const size_t encodedlen) {
|
||||
|
||||
return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
|
||||
NULL, hashlen, encoded, encodedlen, Argon2_id,
|
||||
ARGON2_VERSION_NUMBER);
|
||||
}
|
||||
|
||||
int argon2id_hash_raw(const uint32_t t_cost, const uint32_t m_cost,
|
||||
const uint32_t parallelism, const void *pwd,
|
||||
const size_t pwdlen, const void *salt,
|
||||
const size_t saltlen, void *hash, const size_t hashlen) {
|
||||
return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
|
||||
hash, hashlen, NULL, 0, Argon2_id,
|
||||
ARGON2_VERSION_NUMBER);
|
||||
}
|
||||
|
||||
static int argon2_compare(const uint8_t *b1, const uint8_t *b2, size_t len) {
|
||||
size_t i;
|
||||
uint8_t d = 0U;
|
||||
@@ -212,13 +242,19 @@ int argon2_verify(const char *encoded, const void *pwd, const size_t pwdlen,
|
||||
int ret;
|
||||
int decode_result;
|
||||
uint32_t encoded_len;
|
||||
size_t encoded_len_tmp;
|
||||
|
||||
if(encoded == NULL) {
|
||||
return ARGON2_DECODING_FAIL;
|
||||
}
|
||||
|
||||
encoded_len_tmp = strlen(encoded);
|
||||
/* max values, to be updated in decode_string */
|
||||
encoded_len = strlen(encoded);
|
||||
if (UINT32_MAX < encoded_len_tmp) {
|
||||
return ARGON2_DECODING_FAIL;
|
||||
}
|
||||
|
||||
encoded_len = (uint32_t)encoded_len_tmp;
|
||||
ctx.adlen = encoded_len;
|
||||
ctx.saltlen = encoded_len;
|
||||
ctx.outlen = encoded_len;
|
||||
@@ -226,6 +262,8 @@ int argon2_verify(const char *encoded, const void *pwd, const size_t pwdlen,
|
||||
ctx.free_cbk = NULL;
|
||||
ctx.secret = NULL;
|
||||
ctx.secretlen = 0;
|
||||
ctx.pwdlen = 0;
|
||||
ctx.pwd = NULL;
|
||||
ctx.ad = malloc(ctx.adlen);
|
||||
ctx.salt = malloc(ctx.saltlen);
|
||||
ctx.out = malloc(ctx.outlen);
|
||||
@@ -277,6 +315,11 @@ int argon2d_verify(const char *encoded, const void *pwd, const size_t pwdlen) {
|
||||
return argon2_verify(encoded, pwd, pwdlen, Argon2_d);
|
||||
}
|
||||
|
||||
int argon2id_verify(const char *encoded, const void *pwd, const size_t pwdlen) {
|
||||
|
||||
return argon2_verify(encoded, pwd, pwdlen, Argon2_id);
|
||||
}
|
||||
|
||||
int argon2d_ctx(argon2_context *context) {
|
||||
return argon2_ctx(context, Argon2_d);
|
||||
}
|
||||
@@ -285,6 +328,10 @@ int argon2i_ctx(argon2_context *context) {
|
||||
return argon2_ctx(context, Argon2_i);
|
||||
}
|
||||
|
||||
int argon2id_ctx(argon2_context *context) {
|
||||
return argon2_ctx(context, Argon2_id);
|
||||
}
|
||||
|
||||
int argon2_verify_ctx(argon2_context *context, const char *hash,
|
||||
argon2_type type) {
|
||||
int result;
|
||||
@@ -309,6 +356,10 @@ int argon2i_verify_ctx(argon2_context *context, const char *hash) {
|
||||
return argon2_verify_ctx(context, hash, Argon2_i);
|
||||
}
|
||||
|
||||
int argon2id_verify_ctx(argon2_context *context, const char *hash) {
|
||||
return argon2_verify_ctx(context, hash, Argon2_id);
|
||||
}
|
||||
|
||||
const char *argon2_error_message(int error_code) {
|
||||
switch (error_code) {
|
||||
case ARGON2_OK:
|
||||
@@ -389,8 +440,8 @@ const char *argon2_error_message(int error_code) {
|
||||
}
|
||||
|
||||
size_t argon2_encodedlen(uint32_t t_cost, uint32_t m_cost, uint32_t parallelism,
|
||||
uint32_t saltlen, uint32_t hashlen) {
|
||||
return strlen("$argon2x$v=$m=,t=,p=$$") + numlen(t_cost) + numlen(m_cost)
|
||||
+ numlen(parallelism) + b64len(saltlen) + b64len(hashlen)
|
||||
+ numlen(ARGON2_VERSION_NUMBER);
|
||||
uint32_t saltlen, uint32_t hashlen, argon2_type type) {
|
||||
return strlen("$$v=$m=,t=,p=$$") + strlen(argon2_type2string(type, 0)) +
|
||||
numlen(t_cost) + numlen(m_cost) + numlen(parallelism) +
|
||||
b64len(saltlen) + b64len(hashlen) + numlen(ARGON2_VERSION_NUMBER) + 1;
|
||||
}
|
||||
+66
-8
@@ -1,13 +1,18 @@
|
||||
/*
|
||||
* Argon2 source code package
|
||||
* Argon2 reference source code package - reference C implementations
|
||||
*
|
||||
* Written by Daniel Dinu and Dmitry Khovratovich, 2015
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*
|
||||
* This work is licensed under a Creative Commons CC0 1.0 License/Waiver.
|
||||
* You may use this work under the terms of a Creative Commons CC0 1.0
|
||||
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
|
||||
* these licenses can be found at:
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication
|
||||
* along with this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* You should have received a copy of both of these licenses along with this
|
||||
* software. If not, they may be obtained at the above URLs.
|
||||
*/
|
||||
|
||||
#ifndef ARGON2_H
|
||||
@@ -24,6 +29,8 @@ extern "C" {
|
||||
/* Symbols visibility control */
|
||||
#ifdef A2_VISCTL
|
||||
#define ARGON2_PUBLIC __attribute__((visibility("default")))
|
||||
#elif _MSC_VER
|
||||
#define ARGON2_PUBLIC __declspec(dllexport)
|
||||
#else
|
||||
#define ARGON2_PUBLIC
|
||||
#endif
|
||||
@@ -204,7 +211,11 @@ typedef struct Argon2_Context {
|
||||
} argon2_context;
|
||||
|
||||
/* Argon2 primitive type */
|
||||
typedef enum Argon2_type { Argon2_d = 0, Argon2_i = 1 } argon2_type;
|
||||
typedef enum Argon2_type {
|
||||
Argon2_d = 0,
|
||||
Argon2_i = 1,
|
||||
Argon2_id = 2
|
||||
} argon2_type;
|
||||
|
||||
/* Version of the algorithm */
|
||||
typedef enum Argon2_version {
|
||||
@@ -213,6 +224,14 @@ typedef enum Argon2_version {
|
||||
ARGON2_VERSION_NUMBER = ARGON2_VERSION_13
|
||||
} argon2_version;
|
||||
|
||||
/*
|
||||
* Function that gives the string representation of an argon2_type.
|
||||
* @param type The argon2_type that we want the string for
|
||||
* @param uppercase Whether the string should have the first letter uppercase
|
||||
* @return NULL if invalid type, otherwise the string representation.
|
||||
*/
|
||||
ARGON2_PUBLIC const char *argon2_type2string(argon2_type type, int uppercase);
|
||||
|
||||
/*
|
||||
* Function that performs memory-hard hashing with certain degree of parallelism
|
||||
* @param context Pointer to the Argon2 internal structure
|
||||
@@ -278,6 +297,21 @@ ARGON2_PUBLIC int argon2d_hash_raw(const uint32_t t_cost, const uint32_t m_cost,
|
||||
const size_t saltlen, void *hash,
|
||||
const size_t hashlen);
|
||||
|
||||
ARGON2_PUBLIC int argon2id_hash_encoded(const uint32_t t_cost,
|
||||
const uint32_t m_cost,
|
||||
const uint32_t parallelism,
|
||||
const void *pwd, const size_t pwdlen,
|
||||
const void *salt, const size_t saltlen,
|
||||
const size_t hashlen, char *encoded,
|
||||
const size_t encodedlen);
|
||||
|
||||
ARGON2_PUBLIC int argon2id_hash_raw(const uint32_t t_cost,
|
||||
const uint32_t m_cost,
|
||||
const uint32_t parallelism, const void *pwd,
|
||||
const size_t pwdlen, const void *salt,
|
||||
const size_t saltlen, void *hash,
|
||||
const size_t hashlen);
|
||||
|
||||
/* generic function underlying the above ones */
|
||||
ARGON2_PUBLIC int argon2_hash(const uint32_t t_cost, const uint32_t m_cost,
|
||||
const uint32_t parallelism, const void *pwd,
|
||||
@@ -300,6 +334,9 @@ ARGON2_PUBLIC int argon2i_verify(const char *encoded, const void *pwd,
|
||||
ARGON2_PUBLIC int argon2d_verify(const char *encoded, const void *pwd,
|
||||
const size_t pwdlen);
|
||||
|
||||
ARGON2_PUBLIC int argon2id_verify(const char *encoded, const void *pwd,
|
||||
const size_t pwdlen);
|
||||
|
||||
/* generic function underlying the above ones */
|
||||
ARGON2_PUBLIC int argon2_verify(const char *encoded, const void *pwd,
|
||||
const size_t pwdlen, argon2_type type);
|
||||
@@ -324,6 +361,17 @@ ARGON2_PUBLIC int argon2d_ctx(argon2_context *context);
|
||||
*/
|
||||
ARGON2_PUBLIC int argon2i_ctx(argon2_context *context);
|
||||
|
||||
/**
|
||||
* Argon2id: Version of Argon2 where the first half-pass over memory is
|
||||
* password-independent, the rest are password-dependent (on the password and
|
||||
* salt). OK against side channels (they reduce to 1/2-pass Argon2i), and
|
||||
* better with w.r.t. tradeoff attacks (similar to Argon2d).
|
||||
*****
|
||||
* @param context Pointer to current Argon2 context
|
||||
* @return Zero if successful, a non zero error code otherwise
|
||||
*/
|
||||
ARGON2_PUBLIC int argon2id_ctx(argon2_context *context);
|
||||
|
||||
/**
|
||||
* Verify if a given password is correct for Argon2d hashing
|
||||
* @param context Pointer to current Argon2 context
|
||||
@@ -342,6 +390,16 @@ ARGON2_PUBLIC int argon2d_verify_ctx(argon2_context *context, const char *hash);
|
||||
*/
|
||||
ARGON2_PUBLIC int argon2i_verify_ctx(argon2_context *context, const char *hash);
|
||||
|
||||
/**
|
||||
* Verify if a given password is correct for Argon2id hashing
|
||||
* @param context Pointer to current Argon2 context
|
||||
* @param hash The password hash to verify. The length of the hash is
|
||||
* specified by the context outlen member
|
||||
* @return Zero if successful, a non zero error code otherwise
|
||||
*/
|
||||
ARGON2_PUBLIC int argon2id_verify_ctx(argon2_context *context,
|
||||
const char *hash);
|
||||
|
||||
/* generic function underlying the above ones */
|
||||
ARGON2_PUBLIC int argon2_verify_ctx(argon2_context *context, const char *hash,
|
||||
argon2_type type);
|
||||
@@ -363,7 +421,7 @@ ARGON2_PUBLIC const char *argon2_error_message(int error_code);
|
||||
*/
|
||||
ARGON2_PUBLIC size_t argon2_encodedlen(uint32_t t_cost, uint32_t m_cost,
|
||||
uint32_t parallelism, uint32_t saltlen,
|
||||
uint32_t hashlen);
|
||||
uint32_t hashlen, argon2_type type);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Argon2 reference source code package - reference C implementations
|
||||
*
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*
|
||||
* You may use this work under the terms of a Creative Commons CC0 1.0
|
||||
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
|
||||
* these licenses can be found at:
|
||||
*
|
||||
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* You should have received a copy of both of these licenses along with this
|
||||
* software. If not, they may be obtained at the above URLs.
|
||||
*/
|
||||
|
||||
#ifndef PORTABLE_BLAKE2_IMPL_H
|
||||
#define PORTABLE_BLAKE2_IMPL_H
|
||||
|
||||
@@ -134,10 +151,6 @@ static BLAKE2_INLINE uint64_t rotr64(const uint64_t w, const unsigned c) {
|
||||
return (w >> c) | (w << (64 - c));
|
||||
}
|
||||
|
||||
/* prevents compiler optimizing out memset() */
|
||||
static BLAKE2_INLINE void burn(void *v, size_t n) {
|
||||
static void *(*const volatile memset_v)(void *, int, size_t) = &memset;
|
||||
memset_v(v, 0, n);
|
||||
}
|
||||
void secure_wipe_memory(void *v, size_t n);
|
||||
|
||||
#endif
|
||||
@@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Argon2 reference source code package - reference C implementations
|
||||
*
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*
|
||||
* You may use this work under the terms of a Creative Commons CC0 1.0
|
||||
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
|
||||
* these licenses can be found at:
|
||||
*
|
||||
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* You should have received a copy of both of these licenses along with this
|
||||
* software. If not, they may be obtained at the above URLs.
|
||||
*/
|
||||
|
||||
#ifndef PORTABLE_BLAKE2_H
|
||||
#define PORTABLE_BLAKE2_H
|
||||
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Argon2 reference source code package - reference C implementations
|
||||
*
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*
|
||||
* You may use this work under the terms of a Creative Commons CC0 1.0
|
||||
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
|
||||
* these licenses can be found at:
|
||||
*
|
||||
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* You should have received a copy of both of these licenses along with this
|
||||
* software. If not, they may be obtained at the above URLs.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
@@ -44,7 +61,7 @@ static BLAKE2_INLINE void blake2b_increment_counter(blake2b_state *S,
|
||||
}
|
||||
|
||||
static BLAKE2_INLINE void blake2b_invalidate_state(blake2b_state *S) {
|
||||
burn(S, sizeof(*S)); /* wipe */
|
||||
secure_wipe_memory(S, sizeof(*S)); /* wipe */
|
||||
blake2b_set_lastblock(S); /* invalidate for further use */
|
||||
}
|
||||
|
||||
@@ -140,7 +157,7 @@ int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key,
|
||||
memset(block, 0, BLAKE2B_BLOCKBYTES);
|
||||
memcpy(block, key, keylen);
|
||||
blake2b_update(S, block, BLAKE2B_BLOCKBYTES);
|
||||
burn(block, BLAKE2B_BLOCKBYTES); /* Burn the key from stack */
|
||||
secure_wipe_memory(block, BLAKE2B_BLOCKBYTES); /* Burn the key from stack */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -267,9 +284,9 @@ int blake2b_final(blake2b_state *S, void *out, size_t outlen) {
|
||||
}
|
||||
|
||||
memcpy(out, buffer, S->outlen);
|
||||
burn(buffer, sizeof(buffer));
|
||||
burn(S->buf, sizeof(S->buf));
|
||||
burn(S->h, sizeof(S->h));
|
||||
secure_wipe_memory(buffer, sizeof(buffer));
|
||||
secure_wipe_memory(S->buf, sizeof(S->buf));
|
||||
secure_wipe_memory(S->h, sizeof(S->h));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -307,7 +324,7 @@ int blake2b(void *out, size_t outlen, const void *in, size_t inlen,
|
||||
ret = blake2b_final(&S, out, outlen);
|
||||
|
||||
fail:
|
||||
burn(&S, sizeof(S));
|
||||
secure_wipe_memory(&S, sizeof(S));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -365,7 +382,7 @@ int blake2b_long(void *pout, size_t outlen, const void *in, size_t inlen) {
|
||||
memcpy(out, out_buffer, toproduce);
|
||||
}
|
||||
fail:
|
||||
burn(&blake_state, sizeof(blake_state));
|
||||
secure_wipe_memory(&blake_state, sizeof(blake_state));
|
||||
return ret;
|
||||
#undef TRY
|
||||
}
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Argon2 reference source code package - reference C implementations
|
||||
*
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*
|
||||
* You may use this work under the terms of a Creative Commons CC0 1.0
|
||||
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
|
||||
* these licenses can be found at:
|
||||
*
|
||||
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* You should have received a copy of both of these licenses along with this
|
||||
* software. If not, they may be obtained at the above URLs.
|
||||
*/
|
||||
|
||||
#ifndef BLAKE_ROUND_MKA_OPT_H
|
||||
#define BLAKE_ROUND_MKA_OPT_H
|
||||
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Argon2 reference source code package - reference C implementations
|
||||
*
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*
|
||||
* You may use this work under the terms of a Creative Commons CC0 1.0
|
||||
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
|
||||
* these licenses can be found at:
|
||||
*
|
||||
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* You should have received a copy of both of these licenses along with this
|
||||
* software. If not, they may be obtained at the above URLs.
|
||||
*/
|
||||
|
||||
#ifndef BLAKE_ROUND_MKA_H
|
||||
#define BLAKE_ROUND_MKA_H
|
||||
|
||||
|
||||
+31
-28
@@ -1,14 +1,18 @@
|
||||
/*
|
||||
* Argon2 source code package
|
||||
* Argon2 reference source code package - reference C implementations
|
||||
*
|
||||
* Written by Daniel Dinu and Dmitry Khovratovich, 2015
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*
|
||||
* This work is licensed under a Creative Commons CC0 1.0 License/Waiver.
|
||||
* You may use this work under the terms of a Creative Commons CC0 1.0
|
||||
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
|
||||
* these licenses can be found at:
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication along
|
||||
* with
|
||||
* this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* You should have received a copy of both of these licenses along with this
|
||||
* software. If not, they may be obtained at the above URLs.
|
||||
*/
|
||||
|
||||
/*For memory wiping*/
|
||||
@@ -252,26 +256,28 @@ int fill_memory_blocks(argon2_instance_t *instance) {
|
||||
uint32_t r, s;
|
||||
argon2_thread_handle_t *thread = NULL;
|
||||
argon2_thread_data *thr_data = NULL;
|
||||
int rc = ARGON2_OK;
|
||||
|
||||
if (instance == NULL || instance->lanes == 0) {
|
||||
return ARGON2_THREAD_FAIL;
|
||||
rc = ARGON2_THREAD_FAIL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* 1. Allocating space for threads */
|
||||
thread = calloc(instance->lanes, sizeof(argon2_thread_handle_t));
|
||||
if (thread == NULL) {
|
||||
return ARGON2_MEMORY_ALLOCATION_ERROR;
|
||||
rc = ARGON2_MEMORY_ALLOCATION_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
thr_data = calloc(instance->lanes, sizeof(argon2_thread_data));
|
||||
if (thr_data == NULL) {
|
||||
free(thread);
|
||||
return ARGON2_MEMORY_ALLOCATION_ERROR;
|
||||
rc = ARGON2_MEMORY_ALLOCATION_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (r = 0; r < instance->passes; ++r) {
|
||||
for (s = 0; s < ARGON2_SYNC_POINTS; ++s) {
|
||||
int rc;
|
||||
uint32_t l;
|
||||
|
||||
/* 2. Calling threads */
|
||||
@@ -280,11 +286,9 @@ int fill_memory_blocks(argon2_instance_t *instance) {
|
||||
|
||||
/* 2.1 Join a thread if limit is exceeded */
|
||||
if (l >= instance->threads) {
|
||||
rc = argon2_thread_join(thread[l - instance->threads]);
|
||||
if (rc) {
|
||||
free(thr_data);
|
||||
free(thread);
|
||||
return ARGON2_THREAD_FAIL;
|
||||
if (argon2_thread_join(thread[l - instance->threads])) {
|
||||
rc = ARGON2_THREAD_FAIL;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,12 +301,10 @@ int fill_memory_blocks(argon2_instance_t *instance) {
|
||||
instance; /* preparing the thread input */
|
||||
memcpy(&(thr_data[l].pos), &position,
|
||||
sizeof(argon2_position_t));
|
||||
rc = argon2_thread_create(&thread[l], &fill_segment_thr,
|
||||
(void *)&thr_data[l]);
|
||||
if (rc) {
|
||||
free(thr_data);
|
||||
free(thread);
|
||||
return ARGON2_THREAD_FAIL;
|
||||
if (argon2_thread_create(&thread[l], &fill_segment_thr,
|
||||
(void *)&thr_data[l])) {
|
||||
rc = ARGON2_THREAD_FAIL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* fill_segment(instance, position); */
|
||||
@@ -312,9 +314,9 @@ int fill_memory_blocks(argon2_instance_t *instance) {
|
||||
/* 3. Joining remaining threads */
|
||||
for (l = instance->lanes - instance->threads; l < instance->lanes;
|
||||
++l) {
|
||||
rc = argon2_thread_join(thread[l]);
|
||||
if (rc) {
|
||||
return ARGON2_THREAD_FAIL;
|
||||
if (argon2_thread_join(thread[l])) {
|
||||
rc = ARGON2_THREAD_FAIL;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -324,13 +326,14 @@ int fill_memory_blocks(argon2_instance_t *instance) {
|
||||
#endif
|
||||
}
|
||||
|
||||
fail:
|
||||
if (thread != NULL) {
|
||||
free(thread);
|
||||
}
|
||||
if (thr_data != NULL) {
|
||||
free(thr_data);
|
||||
}
|
||||
return ARGON2_OK;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int validate_inputs(const argon2_context *context) {
|
||||
@@ -575,7 +578,7 @@ int initialize(argon2_instance_t *instance, argon2_context *context) {
|
||||
if (ARGON2_OK != result) {
|
||||
return result;
|
||||
}
|
||||
memcpy(&(instance->memory), p, sizeof(instance->memory));
|
||||
instance->memory = (block *)p;
|
||||
} else {
|
||||
result = allocate_memory(&(instance->memory), instance->memory_blocks);
|
||||
if (ARGON2_OK != result) {
|
||||
|
||||
+11
-7
@@ -1,14 +1,18 @@
|
||||
/*
|
||||
* Argon2 source code package
|
||||
* Argon2 reference source code package - reference C implementations
|
||||
*
|
||||
* Written by Daniel Dinu and Dmitry Khovratovich, 2015
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*
|
||||
* This work is licensed under a Creative Commons CC0 1.0 License/Waiver.
|
||||
* You may use this work under the terms of a Creative Commons CC0 1.0
|
||||
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
|
||||
* these licenses can be found at:
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication along
|
||||
* with
|
||||
* this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* You should have received a copy of both of these licenses along with this
|
||||
* software. If not, they may be obtained at the above URLs.
|
||||
*/
|
||||
|
||||
#ifndef ARGON2_CORE_H
|
||||
|
||||
+48
-12
@@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Argon2 reference source code package - reference C implementations
|
||||
*
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*
|
||||
* You may use this work under the terms of a Creative Commons CC0 1.0
|
||||
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
|
||||
* these licenses can be found at:
|
||||
*
|
||||
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* You should have received a copy of both of these licenses along with this
|
||||
* software. If not, they may be obtained at the above URLs.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -291,21 +308,26 @@ int decode_string(argon2_context *ctx, const char *str, argon2_type type) {
|
||||
size_t maxsaltlen = ctx->saltlen;
|
||||
size_t maxoutlen = ctx->outlen;
|
||||
int validation_result;
|
||||
const char* type_string;
|
||||
|
||||
ctx->adlen = 0;
|
||||
ctx->saltlen = 0;
|
||||
ctx->outlen = 0;
|
||||
ctx->pwdlen = 0;
|
||||
|
||||
if (type == Argon2_i)
|
||||
CC("$argon2i");
|
||||
else if (type == Argon2_d)
|
||||
CC("$argon2d");
|
||||
else
|
||||
/* We should start with the argon2_type we are using */
|
||||
CC("$");
|
||||
type_string = argon2_type2string(type, 0);
|
||||
if (type_string) {
|
||||
CC(type_string);
|
||||
} else {
|
||||
return ARGON2_INCORRECT_TYPE;
|
||||
ctx->version = ARGON2_VERSION_10;
|
||||
}
|
||||
|
||||
/* Reading the version number if the default is suppressed */
|
||||
ctx->version = ARGON2_VERSION_10;
|
||||
CC_opt("$v=", DECIMAL(ctx->version));
|
||||
|
||||
CC("$m=");
|
||||
DECIMAL(ctx->m_cost);
|
||||
CC(",t=");
|
||||
@@ -370,16 +392,19 @@ int encode_string(char *dst, size_t dst_len, argon2_context *ctx,
|
||||
dst_len -= sb_len; \
|
||||
} while ((void)0, 0)
|
||||
|
||||
if (type == Argon2_i)
|
||||
SS("$argon2i$v=");
|
||||
else if (type == Argon2_d)
|
||||
SS("$argon2d$v=");
|
||||
else
|
||||
const char* type_string = argon2_type2string(type, 0);
|
||||
SS("$");
|
||||
if (type_string) {
|
||||
SS(type_string);
|
||||
} else {
|
||||
return ARGON2_ENCODING_FAIL;
|
||||
}
|
||||
|
||||
if (validate_inputs(ctx) != ARGON2_OK) {
|
||||
return validate_inputs(ctx);
|
||||
}
|
||||
|
||||
SS("$v=");
|
||||
SX(ctx->version);
|
||||
SS("$m=");
|
||||
SX(ctx->m_cost);
|
||||
@@ -412,7 +437,18 @@ int encode_string(char *dst, size_t dst_len, argon2_context *ctx,
|
||||
}
|
||||
|
||||
size_t b64len(uint32_t len) {
|
||||
return (((size_t)len + 2) / 3) * 4;
|
||||
size_t olen = ((size_t)len / 3) << 2;
|
||||
|
||||
switch (len % 3) {
|
||||
case 2:
|
||||
olen++;
|
||||
/* fall through */
|
||||
case 1:
|
||||
olen += 2;
|
||||
break;
|
||||
}
|
||||
|
||||
return olen;
|
||||
}
|
||||
|
||||
size_t numlen(uint32_t num) {
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Argon2 reference source code package - reference C implementations
|
||||
*
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*
|
||||
* You may use this work under the terms of a Creative Commons CC0 1.0
|
||||
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
|
||||
* these licenses can be found at:
|
||||
*
|
||||
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* You should have received a copy of both of these licenses along with this
|
||||
* software. If not, they may be obtained at the above URLs.
|
||||
*/
|
||||
|
||||
#ifndef ENCODING_H
|
||||
#define ENCODING_H
|
||||
#include "argon2.h"
|
||||
|
||||
+15
-8
@@ -1,14 +1,18 @@
|
||||
/*
|
||||
* Argon2 source code package
|
||||
* Argon2 reference source code package - reference C implementations
|
||||
*
|
||||
* Written by Daniel Dinu and Dmitry Khovratovich, 2015
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*
|
||||
* This work is licensed under a Creative Commons CC0 1.0 License/Waiver.
|
||||
* You may use this work under the terms of a Creative Commons CC0 1.0
|
||||
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
|
||||
* these licenses can be found at:
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication along
|
||||
* with
|
||||
* this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* You should have received a copy of both of these licenses along with this
|
||||
* software. If not, they may be obtained at the above URLs.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
@@ -135,7 +139,10 @@ void fill_segment(const argon2_instance_t *instance,
|
||||
return;
|
||||
}
|
||||
|
||||
data_independent_addressing = (instance->type == Argon2_i);
|
||||
data_independent_addressing =
|
||||
(instance->type == Argon2_i) ||
|
||||
(instance->type == Argon2_id && (position.pass == 0) &&
|
||||
(position.slice < ARGON2_SYNC_POINTS / 2));
|
||||
|
||||
pseudo_rands =
|
||||
(uint64_t *)malloc(sizeof(uint64_t) * instance->segment_length);
|
||||
|
||||
+11
-7
@@ -1,14 +1,18 @@
|
||||
/*
|
||||
* Argon2 source code package
|
||||
* Argon2 reference source code package - reference C implementations
|
||||
*
|
||||
* Written by Daniel Dinu and Dmitry Khovratovich, 2015
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*
|
||||
* This work is licensed under a Creative Commons CC0 1.0 License/Waiver.
|
||||
* You may use this work under the terms of a Creative Commons CC0 1.0
|
||||
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
|
||||
* these licenses can be found at:
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication along
|
||||
* with
|
||||
* this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* You should have received a copy of both of these licenses along with this
|
||||
* software. If not, they may be obtained at the above URLs.
|
||||
*/
|
||||
|
||||
#ifndef ARGON2_OPT_H
|
||||
|
||||
+15
-8
@@ -1,14 +1,18 @@
|
||||
/*
|
||||
* Argon2 source code package
|
||||
* Argon2 reference source code package - reference C implementations
|
||||
*
|
||||
* Written by Daniel Dinu and Dmitry Khovratovich, 2015
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*
|
||||
* This work is licensed under a Creative Commons CC0 1.0 License/Waiver.
|
||||
* You may use this work under the terms of a Creative Commons CC0 1.0
|
||||
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
|
||||
* these licenses can be found at:
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication along
|
||||
* with
|
||||
* this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* You should have received a copy of both of these licenses along with this
|
||||
* software. If not, they may be obtained at the above URLs.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
@@ -145,7 +149,10 @@ void fill_segment(const argon2_instance_t *instance,
|
||||
return;
|
||||
}
|
||||
|
||||
data_independent_addressing = (instance->type == Argon2_i);
|
||||
data_independent_addressing =
|
||||
(instance->type == Argon2_i) ||
|
||||
(instance->type == Argon2_id && (position.pass == 0) &&
|
||||
(position.slice < ARGON2_SYNC_POINTS / 2));
|
||||
|
||||
pseudo_rands =
|
||||
(uint64_t *)malloc(sizeof(uint64_t) * (instance->segment_length));
|
||||
|
||||
+11
-7
@@ -1,14 +1,18 @@
|
||||
/*
|
||||
* Argon2 source code package
|
||||
* Argon2 reference source code package - reference C implementations
|
||||
*
|
||||
* Written by Daniel Dinu and Dmitry Khovratovich, 2015
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*
|
||||
* This work is licensed under a Creative Commons CC0 1.0 License/Waiver.
|
||||
* You may use this work under the terms of a Creative Commons CC0 1.0
|
||||
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
|
||||
* these licenses can be found at:
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication along
|
||||
* with
|
||||
* this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* You should have received a copy of both of these licenses along with this
|
||||
* software. If not, they may be obtained at the above URLs.
|
||||
*/
|
||||
|
||||
#ifndef ARGON2_REF_H
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Argon2 reference source code package - reference C implementations
|
||||
*
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*
|
||||
* You may use this work under the terms of a Creative Commons CC0 1.0
|
||||
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
|
||||
* these licenses can be found at:
|
||||
*
|
||||
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* You should have received a copy of both of these licenses along with this
|
||||
* software. If not, they may be obtained at the above URLs.
|
||||
*/
|
||||
|
||||
#include "thread.h"
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Argon2 reference source code package - reference C implementations
|
||||
*
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*
|
||||
* You may use this work under the terms of a Creative Commons CC0 1.0
|
||||
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
|
||||
* these licenses can be found at:
|
||||
*
|
||||
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* You should have received a copy of both of these licenses along with this
|
||||
* software. If not, they may be obtained at the above URLs.
|
||||
*/
|
||||
|
||||
#ifndef ARGON2_THREAD_H
|
||||
#define ARGON2_THREAD_H
|
||||
/*
|
||||
|
||||
+22
-1
@@ -47,4 +47,25 @@ bindDoneCb(DoneCallbackRaw raw_cb, void* user_data)
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
DoneCallbackSimple
|
||||
bindDoneCbSimple(DoneCallbackSimpleRaw raw_cb, void* user_data) {
|
||||
if (not raw_cb) return {};
|
||||
return [=](bool success) {
|
||||
raw_cb(success, user_data);
|
||||
};
|
||||
}
|
||||
|
||||
std::string
|
||||
NodeStats::toString() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Known nodes: " << good_nodes << " good, " << dubious_nodes << " dubious, " << incoming_nodes << " incoming." << std::endl;
|
||||
if (table_depth > 1) {
|
||||
ss << "Routing table depth: " << table_depth << std::endl;
|
||||
unsigned long tot_nodes = 8 * std::exp2(table_depth);
|
||||
ss << "Network size estimation: " << tot_nodes << " nodes" << std::endl;
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
}
|
||||
+315
-47
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-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
|
||||
@@ -83,6 +83,18 @@ namespace crypto {
|
||||
static constexpr std::array<size_t, 3> AES_LENGTHS {{128/8, 192/8, 256/8}};
|
||||
static constexpr size_t PASSWORD_SALT_LENGTH {16};
|
||||
|
||||
constexpr gnutls_digest_algorithm_t gnutlsHashAlgo(size_t min_res) {
|
||||
return (min_res > 256/8) ? GNUTLS_DIG_SHA512 : (
|
||||
(min_res > 128/8) ? GNUTLS_DIG_SHA256 : (
|
||||
GNUTLS_DIG_SHA1));
|
||||
}
|
||||
|
||||
constexpr size_t gnutlsHashSize(int algo) {
|
||||
return (algo == GNUTLS_DIG_SHA512) ? 512/8 : (
|
||||
(algo == GNUTLS_DIG_SHA256) ? 256/8 : (
|
||||
(algo == GNUTLS_DIG_SHA1) ? 160/8 : 0 ));
|
||||
}
|
||||
|
||||
size_t aesKeySize(size_t max)
|
||||
{
|
||||
size_t aes_key_len = 0;
|
||||
@@ -128,8 +140,7 @@ Blob aesEncrypt(const Blob& data, const Blob& key)
|
||||
Blob aesEncrypt(const Blob& data, const std::string& password)
|
||||
{
|
||||
Blob salt;
|
||||
Blob key = stretchKey(password, salt);
|
||||
key.resize(256 / 8);
|
||||
Blob key = stretchKey(password, salt, 256 / 8);
|
||||
Blob encrypted = aesEncrypt(data, key);
|
||||
encrypted.insert(encrypted.begin(), salt.begin(), salt.end());
|
||||
return encrypted;
|
||||
@@ -175,13 +186,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);
|
||||
key.resize(256 / 8);
|
||||
Blob 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)
|
||||
Blob stretchKey(const std::string& password, Blob& salt, size_t key_length)
|
||||
{
|
||||
if (salt.empty()) {
|
||||
salt.resize(PASSWORD_SALT_LENGTH);
|
||||
@@ -193,35 +203,27 @@ Blob stretchKey(const std::string& password, Blob& salt)
|
||||
auto ret = argon2i_hash_raw(16, 64*1024, 1, password.data(), password.size(), salt.data(), salt.size(), res.data(), res.size());
|
||||
if (ret != ARGON2_OK)
|
||||
throw CryptoException("Can't compute argon2i !");
|
||||
return hash(res);
|
||||
return hash(res, key_length);
|
||||
}
|
||||
|
||||
Blob hash(const Blob& data)
|
||||
Blob hash(const Blob& data, size_t hash_len)
|
||||
{
|
||||
auto algo = gnutlsHashAlgo(hash_len);
|
||||
size_t res_size = gnutlsHashSize(algo);
|
||||
Blob res;
|
||||
size_t res_size = 64;
|
||||
res.resize(res_size);
|
||||
const gnutls_datum_t gdat {(uint8_t*)data.data(), (unsigned)data.size()};
|
||||
if (gnutls_fingerprint(GNUTLS_DIG_SHA512, &gdat, res.data(), &res_size))
|
||||
if (gnutls_fingerprint(algo, &gdat, res.data(), &res_size))
|
||||
throw CryptoException("Can't compute hash !");
|
||||
res.resize(res_size);
|
||||
res.resize(std::min(hash_len, res_size));
|
||||
return res;
|
||||
}
|
||||
|
||||
PrivateKey::PrivateKey()
|
||||
{
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030300
|
||||
if (gnutls_global_init() != GNUTLS_E_SUCCESS)
|
||||
throw CryptoException("Can't initialize GnuTLS.");
|
||||
#endif
|
||||
}
|
||||
{}
|
||||
|
||||
PrivateKey::PrivateKey(gnutls_x509_privkey_t k) : x509_key(k)
|
||||
{
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030300
|
||||
if (gnutls_global_init() != GNUTLS_E_SUCCESS)
|
||||
throw CryptoException("Can't initialize GnuTLS.");
|
||||
#endif
|
||||
gnutls_privkey_init(&key);
|
||||
if (gnutls_privkey_import_x509(key, k, GNUTLS_PRIVKEY_IMPORT_COPY) != GNUTLS_E_SUCCESS) {
|
||||
key = nullptr;
|
||||
@@ -231,10 +233,6 @@ PrivateKey::PrivateKey(gnutls_x509_privkey_t k) : x509_key(k)
|
||||
|
||||
PrivateKey::PrivateKey(const Blob& import, const std::string& password)
|
||||
{
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030300
|
||||
if (gnutls_global_init() != GNUTLS_E_SUCCESS)
|
||||
throw CryptoException("Can't initialize GnuTLS.");
|
||||
#endif
|
||||
int err = gnutls_x509_privkey_init(&x509_key);
|
||||
if (err != GNUTLS_E_SUCCESS)
|
||||
throw CryptoException("Can't initialize private key !");
|
||||
@@ -266,14 +264,9 @@ PrivateKey::PrivateKey(const Blob& import, const std::string& password)
|
||||
|
||||
PrivateKey::PrivateKey(PrivateKey&& o) noexcept : key(o.key), x509_key(o.x509_key)
|
||||
{
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030300
|
||||
// gnutls_global_init already succeeded at least once here so no real need to check.
|
||||
int ret = gnutls_global_init();
|
||||
assert(ret == GNUTLS_E_SUCCESS);
|
||||
#endif
|
||||
o.key = nullptr;
|
||||
o.x509_key = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
PrivateKey::~PrivateKey()
|
||||
{
|
||||
@@ -285,9 +278,6 @@ PrivateKey::~PrivateKey()
|
||||
gnutls_x509_privkey_deinit(x509_key);
|
||||
x509_key = nullptr;
|
||||
}
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030300
|
||||
gnutls_global_deinit();
|
||||
#endif
|
||||
}
|
||||
|
||||
PrivateKey&
|
||||
@@ -311,6 +301,8 @@ PrivateKey::sign(const Blob& data) const
|
||||
{
|
||||
if (!key)
|
||||
throw CryptoException("Can't sign data: no private key set !");
|
||||
if (std::numeric_limits<unsigned>::max() < data.size())
|
||||
throw CryptoException("Can't sign data: too large !");
|
||||
gnutls_datum_t sig;
|
||||
const gnutls_datum_t dat {(unsigned char*)data.data(), (unsigned)data.size()};
|
||||
if (gnutls_privkey_sign_data(key, GNUTLS_DIG_SHA512, 0, &dat, &sig) != GNUTLS_E_SUCCESS)
|
||||
@@ -733,13 +725,54 @@ Certificate::toString(bool chain) const
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string
|
||||
Certificate::print() const
|
||||
{
|
||||
gnutls_datum_t out;
|
||||
gnutls_x509_crt_print(cert, GNUTLS_CRT_PRINT_FULL, &out);
|
||||
std::string ret(out.data, out.data+out.size);
|
||||
gnutls_free(out.data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
Certificate::revoke(const PrivateKey& key, const Certificate& to_revoke)
|
||||
{
|
||||
if (revocation_lists.empty())
|
||||
revocation_lists.emplace(std::make_shared<RevocationList>());
|
||||
auto& list = *(*revocation_lists.begin());
|
||||
list.revoke(to_revoke);
|
||||
list.sign(key, *this);
|
||||
}
|
||||
|
||||
void
|
||||
Certificate::addRevocationList(RevocationList&& list)
|
||||
{
|
||||
addRevocationList(std::make_shared<RevocationList>(std::forward<RevocationList>(list)));
|
||||
}
|
||||
|
||||
void
|
||||
Certificate::addRevocationList(std::shared_ptr<RevocationList> list)
|
||||
{
|
||||
if (revocation_lists.find(list) != revocation_lists.end())
|
||||
return; // Already in the list
|
||||
if (not list->isSignedBy(*this))
|
||||
throw CryptoException("CRL is not signed by this certificate");
|
||||
revocation_lists.emplace(std::move(list));
|
||||
}
|
||||
|
||||
std::chrono::system_clock::time_point
|
||||
Certificate::getExpiration() const
|
||||
{
|
||||
auto t = gnutls_x509_crt_get_expiration_time(cert);
|
||||
if (t == (time_t)-1)
|
||||
return std::chrono::system_clock::time_point::min();
|
||||
return std::chrono::system_clock::from_time_t(t);
|
||||
}
|
||||
|
||||
PrivateKey
|
||||
PrivateKey::generate(unsigned key_length)
|
||||
{
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030300
|
||||
if (gnutls_global_init() != GNUTLS_E_SUCCESS)
|
||||
throw CryptoException("Can't initialize GnuTLS.");
|
||||
#endif
|
||||
gnutls_x509_privkey_t key;
|
||||
if (gnutls_x509_privkey_init(&key) != GNUTLS_E_SUCCESS)
|
||||
throw CryptoException("Can't initialize private key.");
|
||||
@@ -751,20 +784,25 @@ PrivateKey::generate(unsigned key_length)
|
||||
return PrivateKey{key};
|
||||
}
|
||||
|
||||
PrivateKey
|
||||
PrivateKey::generateEC()
|
||||
{
|
||||
gnutls_x509_privkey_t key;
|
||||
if (gnutls_x509_privkey_init(&key) != GNUTLS_E_SUCCESS)
|
||||
throw CryptoException("Can't initialize private key.");
|
||||
int err = gnutls_x509_privkey_generate(key, GNUTLS_PK_EC, gnutls_sec_param_to_pk_bits(GNUTLS_PK_EC, GNUTLS_SEC_PARAM_ULTRA), 0);
|
||||
if (err != GNUTLS_E_SUCCESS) {
|
||||
gnutls_x509_privkey_deinit(key);
|
||||
throw CryptoException(std::string("Can't generate EC key pair: ") + gnutls_strerror(err));
|
||||
}
|
||||
return PrivateKey{key};
|
||||
}
|
||||
|
||||
Identity
|
||||
generateIdentity(const std::string& name, crypto::Identity ca, unsigned key_length, bool is_ca)
|
||||
{
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030300
|
||||
if (gnutls_global_init() != GNUTLS_E_SUCCESS)
|
||||
return {};
|
||||
#endif
|
||||
auto key = std::make_shared<PrivateKey>(PrivateKey::generate(key_length));
|
||||
|
||||
auto cert = std::make_shared<Certificate>(Certificate::generate(*key, name, ca, is_ca));
|
||||
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030300
|
||||
gnutls_global_deinit();
|
||||
#endif
|
||||
return {std::move(key), std::move(cert)};
|
||||
}
|
||||
|
||||
@@ -816,6 +854,10 @@ Certificate::generate(const PrivateKey& key, const std::string& name, Identity c
|
||||
gnutls_x509_crt_set_key_usage(cert, key_usage);
|
||||
|
||||
if (ca.first && ca.second) {
|
||||
if (not ca.second->isCA()) {
|
||||
// Signing certificate must be CA.
|
||||
return {};
|
||||
}
|
||||
//if (gnutls_x509_crt_sign2(cert, ca.second->cert, ca.first->x509_key, get_dig(cert), 0) != GNUTLS_E_SUCCESS) {
|
||||
if (gnutls_x509_crt_privkey_sign(cert, ca.second->cert, ca.first->key, get_dig(cert), 0) != GNUTLS_E_SUCCESS) {
|
||||
std::cerr << "Error when signing certificate" << std::endl;
|
||||
@@ -833,5 +875,231 @@ Certificate::generate(const PrivateKey& key, const std::string& name, Identity c
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<RevocationList>>
|
||||
Certificate::getRevocationLists() const
|
||||
{
|
||||
std::vector<std::shared_ptr<RevocationList>> ret;
|
||||
ret.reserve(revocation_lists.size());
|
||||
for (const auto& crl : revocation_lists)
|
||||
ret.emplace_back(crl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RevocationList::RevocationList()
|
||||
{
|
||||
gnutls_x509_crl_init(&crl);
|
||||
}
|
||||
|
||||
RevocationList::RevocationList(const Blob& b)
|
||||
{
|
||||
gnutls_x509_crl_init(&crl);
|
||||
try {
|
||||
unpack(b.data(), b.size());
|
||||
} catch (const std::exception& e) {
|
||||
gnutls_x509_crl_deinit(crl);
|
||||
crl = nullptr;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
RevocationList::~RevocationList()
|
||||
{
|
||||
if (crl) {
|
||||
gnutls_x509_crl_deinit(crl);
|
||||
crl = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RevocationList::pack(Blob& b) const
|
||||
{
|
||||
gnutls_datum_t gdat {nullptr, 0};
|
||||
if (auto err = gnutls_x509_crl_export2(crl, GNUTLS_X509_FMT_DER, &gdat)) {
|
||||
throw CryptoException(std::string("Can't export CRL: ") + gnutls_strerror(err));
|
||||
}
|
||||
b.insert(b.end(), gdat.data, gdat.data + gdat.size);
|
||||
}
|
||||
|
||||
void
|
||||
RevocationList::unpack(const uint8_t* dat, size_t dat_size)
|
||||
{
|
||||
if (std::numeric_limits<unsigned>::max() < dat_size)
|
||||
throw CryptoException("Can't load CRL: too large!");
|
||||
const gnutls_datum_t gdat {(uint8_t*)dat, (unsigned)dat_size};
|
||||
if (auto err_pem = gnutls_x509_crl_import(crl, &gdat, GNUTLS_X509_FMT_PEM))
|
||||
if (auto err_der = gnutls_x509_crl_import(crl, &gdat, GNUTLS_X509_FMT_DER)) {
|
||||
throw CryptoException(std::string("Can't load CRL: PEM: ") + gnutls_strerror(err_pem)
|
||||
+ " DER: " + gnutls_strerror(err_der));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RevocationList::msgpack_unpack(msgpack::object o)
|
||||
{
|
||||
try {
|
||||
if (o.type == msgpack::type::BIN)
|
||||
unpack((const uint8_t*)o.via.bin.ptr, o.via.bin.size);
|
||||
else {
|
||||
Blob dat = unpackBlob(o);
|
||||
unpack(dat.data(), dat.size());
|
||||
}
|
||||
} catch (...) {
|
||||
throw msgpack::type_error();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
RevocationList::isRevoked(const Certificate& crt) const
|
||||
{
|
||||
auto ret = gnutls_x509_crt_check_revocation(crt.cert, &crl, 1);
|
||||
if (ret < 0)
|
||||
throw CryptoException(std::string("Can't check certificate revocation status: ") + gnutls_strerror(ret));
|
||||
return ret != 0;
|
||||
}
|
||||
|
||||
void
|
||||
RevocationList::revoke(const Certificate& crt, std::chrono::system_clock::time_point t)
|
||||
{
|
||||
if (t == time_point::min())
|
||||
t = clock::now();
|
||||
if (auto err = gnutls_x509_crl_set_crt(crl, crt.cert, std::chrono::system_clock::to_time_t(t)))
|
||||
throw CryptoException(std::string("Can't revoke certificate: ") + gnutls_strerror(err));
|
||||
}
|
||||
|
||||
static std::string
|
||||
getCRLIssuerDN(gnutls_x509_crl_t cert, const char* oid)
|
||||
{
|
||||
std::string dn;
|
||||
dn.resize(512);
|
||||
size_t dn_sz = dn.size();
|
||||
int ret = gnutls_x509_crl_get_issuer_dn_by_oid(cert, oid, 0, 0, &(*dn.begin()), &dn_sz);
|
||||
if (ret != GNUTLS_E_SUCCESS)
|
||||
return {};
|
||||
dn.resize(dn_sz);
|
||||
return dn;
|
||||
}
|
||||
|
||||
std::string
|
||||
RevocationList::getIssuerName() const
|
||||
{
|
||||
return getCRLIssuerDN(crl, GNUTLS_OID_X520_COMMON_NAME);
|
||||
}
|
||||
|
||||
/** Read CRL issuer User ID (UID) */
|
||||
std::string
|
||||
RevocationList::getIssuerUID() const
|
||||
{
|
||||
return getCRLIssuerDN(crl, GNUTLS_OID_LDAP_UID);
|
||||
}
|
||||
|
||||
RevocationList::time_point
|
||||
RevocationList::getNextUpdateTime() const
|
||||
{
|
||||
auto t = gnutls_x509_crl_get_next_update(crl);
|
||||
if (t == (time_t)-1)
|
||||
return std::chrono::system_clock::time_point::min();
|
||||
return std::chrono::system_clock::from_time_t(t);
|
||||
}
|
||||
|
||||
RevocationList::time_point
|
||||
RevocationList::getUpdateTime() const
|
||||
{
|
||||
auto t = gnutls_x509_crl_get_this_update(crl);
|
||||
if (t == (time_t)-1)
|
||||
return std::chrono::system_clock::time_point::min();
|
||||
return std::chrono::system_clock::from_time_t(t);
|
||||
}
|
||||
|
||||
enum class Endian : uint32_t
|
||||
{
|
||||
LITTLE = 0,
|
||||
BIG = 1
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
T endian(T w, Endian endian = Endian::BIG)
|
||||
{
|
||||
// this gets optimized out into if (endian == host_endian) return w;
|
||||
union { uint64_t quad; uint32_t islittle; } t;
|
||||
t.quad = 1;
|
||||
if (t.islittle ^ (uint32_t)endian) return w;
|
||||
T r = 0;
|
||||
|
||||
// decent compilers will unroll this (gcc)
|
||||
// or even convert straight into single bswap (clang)
|
||||
for (size_t i = 0; i < sizeof(r); i++) {
|
||||
r <<= 8;
|
||||
r |= w & 0xff;
|
||||
w >>= 8;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
RevocationList::sign(const PrivateKey& key, const Certificate& ca, duration validity)
|
||||
{
|
||||
if (auto err = gnutls_x509_crl_set_version(crl, 2))
|
||||
throw CryptoException(std::string("Can't set CRL version: ") + gnutls_strerror(err));
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto next_update = (validity == duration{}) ? ca.getExpiration() : now + validity;
|
||||
if (auto err = gnutls_x509_crl_set_this_update(crl, std::chrono::system_clock::to_time_t(now)))
|
||||
throw CryptoException(std::string("Can't set CRL update time: ") + gnutls_strerror(err));
|
||||
if (auto err = gnutls_x509_crl_set_next_update(crl, std::chrono::system_clock::to_time_t(next_update)))
|
||||
throw CryptoException(std::string("Can't set CRL next update time: ") + gnutls_strerror(err));
|
||||
uint64_t number {0};
|
||||
size_t number_sz {sizeof(number)};
|
||||
unsigned critical {0};
|
||||
gnutls_x509_crl_get_number(crl, &number, &number_sz, &critical);
|
||||
if (number == 0) {
|
||||
// initialize to a random number
|
||||
number_sz = sizeof(number);
|
||||
random_device rdev;
|
||||
std::generate_n((uint8_t*)&number, sizeof(number), std::bind(rand_byte, std::ref(rdev)));
|
||||
} else
|
||||
number = endian(endian(number) + 1);
|
||||
if (auto err = gnutls_x509_crl_set_number(crl, &number, sizeof(number)))
|
||||
throw CryptoException(std::string("Can't set CRL update time: ") + gnutls_strerror(err));
|
||||
if (auto err = gnutls_x509_crl_sign2(crl, ca.cert, key.x509_key, GNUTLS_DIG_SHA512, 0))
|
||||
throw CryptoException(std::string("Can't sign certificate revocation list: ") + gnutls_strerror(err));
|
||||
// to be able to actually use the CRL we need to serialize/deserialize it
|
||||
auto packed = getPacked();
|
||||
unpack(packed.data(), packed.size());
|
||||
}
|
||||
|
||||
bool
|
||||
RevocationList::isSignedBy(const Certificate& issuer) const
|
||||
{
|
||||
unsigned result {0};
|
||||
auto err = gnutls_x509_crl_verify(crl, &issuer.cert, 1, 0, &result);
|
||||
if (err < 0) {
|
||||
//std::cout << "Can't verify CRL: " << err << " " << result << " " << gnutls_strerror(err) << std::endl;
|
||||
return false;
|
||||
}
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
|
||||
Blob
|
||||
RevocationList::getNumber() const
|
||||
{
|
||||
Blob number(20);
|
||||
size_t number_sz {number.size()};
|
||||
unsigned critical {0};
|
||||
gnutls_x509_crl_get_number(crl, number.data(), &number_sz, &critical);
|
||||
if (number_sz != number.size())
|
||||
number.resize(number_sz);
|
||||
return number;
|
||||
}
|
||||
|
||||
std::string
|
||||
RevocationList::toString() const
|
||||
{
|
||||
gnutls_datum_t out;
|
||||
gnutls_x509_crl_print(crl, GNUTLS_CRT_PRINT_FULL, &out);
|
||||
std::string ret(out.data, out.data+out.size);
|
||||
gnutls_free(out.data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-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
|
||||
|
||||
+1704
-877
File diff suppressed because it is too large.
Load diff
+243
-92
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -37,6 +37,8 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
constexpr std::chrono::seconds DhtRunner::BOOTSTRAP_PERIOD;
|
||||
|
||||
DhtRunner::DhtRunner() : dht_()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
@@ -104,7 +106,8 @@ DhtRunner::run(const sockaddr_in* local4, const sockaddr_in6* local6, DhtRunner:
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
if (not pending_ops_prio.empty())
|
||||
return true;
|
||||
if (not pending_ops.empty() and getStatus() >= NodeStatus::Connecting)
|
||||
auto s = getStatus();
|
||||
if (not pending_ops.empty() and (s == NodeStatus::Connected or (s == NodeStatus::Disconnected and not bootstraping)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -127,10 +130,14 @@ DhtRunner::join()
|
||||
{
|
||||
running = false;
|
||||
cv.notify_all();
|
||||
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();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops = decltype(pending_ops)();
|
||||
@@ -208,6 +215,12 @@ DhtRunner::setLoggers(LogMethod error, LogMethod warn, LogMethod debug) {
|
||||
dht_->setLoggers(std::forward<LogMethod>(error), std::forward<LogMethod>(warn), std::forward<LogMethod>(debug));
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::setLogFilter(const InfoHash& f) {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
dht_->setLogFilter(f);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::registerType(const ValueType& type) {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
@@ -220,11 +233,27 @@ DhtRunner::importValues(const std::vector<ValuesExport>& values) {
|
||||
dht_->importValues(values);
|
||||
}
|
||||
|
||||
int
|
||||
unsigned
|
||||
DhtRunner::getNodesStats(sa_family_t af, unsigned *good_return, unsigned *dubious_return, unsigned *cached_return, unsigned *incoming_return) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
return dht_->getNodesStats(af, good_return, dubious_return, cached_return, incoming_return);
|
||||
const auto stats = dht_->getNodesStats(af);
|
||||
if (good_return)
|
||||
*good_return = stats.good_nodes;
|
||||
if (dubious_return)
|
||||
*dubious_return = stats.dubious_nodes;
|
||||
if (cached_return)
|
||||
*cached_return = stats.cached_nodes;
|
||||
if (incoming_return)
|
||||
*incoming_return = stats.incoming_nodes;
|
||||
return stats.good_nodes + stats.dubious_nodes;
|
||||
}
|
||||
|
||||
NodeStats
|
||||
DhtRunner::getNodesStats(sa_family_t af) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
return dht_->getNodesStats(af);
|
||||
}
|
||||
|
||||
std::vector<unsigned>
|
||||
@@ -241,6 +270,12 @@ DhtRunner::getStorageLog() const
|
||||
return dht_->getStorageLog();
|
||||
}
|
||||
std::string
|
||||
DhtRunner::getStorageLog(const InfoHash& f) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
return dht_->getStorageLog(f);
|
||||
}
|
||||
std::string
|
||||
DhtRunner::getRoutingTablesLog(sa_family_t af) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
@@ -252,7 +287,13 @@ DhtRunner::getSearchesLog(sa_family_t af) const
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
return dht_->getSearchesLog(af);
|
||||
}
|
||||
std::vector<Address>
|
||||
std::string
|
||||
DhtRunner::getSearchLog(const InfoHash& f, sa_family_t af) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
return dht_->getSearchLog(f, af);
|
||||
}
|
||||
std::vector<SockAddr>
|
||||
DhtRunner::getPublicAddress(sa_family_t af)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
@@ -287,22 +328,14 @@ DhtRunner::loop_()
|
||||
decltype(pending_ops) ops {};
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
ops = std::move(pending_ops_prio);
|
||||
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();
|
||||
}
|
||||
if (getStatus() >= NodeStatus::Connecting) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
ops = std::move(pending_ops);
|
||||
}
|
||||
while (not ops.empty()) {
|
||||
ops.front()(*dht_);
|
||||
ops.pop();
|
||||
}
|
||||
}
|
||||
|
||||
time_point wakeup {};
|
||||
decltype(rcv) received {};
|
||||
@@ -315,7 +348,7 @@ DhtRunner::loop_()
|
||||
for (const auto& pck : received) {
|
||||
auto& buf = pck.first;
|
||||
auto& from = pck.second;
|
||||
wakeup = dht_->periodic(buf.data(), buf.size()-1, (sockaddr*)&from.first, from.second);
|
||||
wakeup = dht_->periodic(buf.data(), buf.size()-1, from);
|
||||
}
|
||||
received.clear();
|
||||
} else {
|
||||
@@ -327,6 +360,15 @@ DhtRunner::loop_()
|
||||
if (nstatus4 != status4 || nstatus6 != status6) {
|
||||
status4 = nstatus4;
|
||||
status6 = nstatus6;
|
||||
if (status4 == NodeStatus::Disconnected and status6 == NodeStatus::Disconnected) {
|
||||
// We have lost connection with the DHT. Try to recover using bootstrap nodes.
|
||||
std::unique_lock<std::mutex> lck(bootstrap_mtx);
|
||||
bootstrap_nodes = bootstrap_nodes_all;
|
||||
tryBootstrapContinuously();
|
||||
} else {
|
||||
std::unique_lock<std::mutex> lck(bootstrap_mtx);
|
||||
bootstrap_nodes.clear();
|
||||
}
|
||||
if (statusCb)
|
||||
statusCb(status4, status6);
|
||||
}
|
||||
@@ -400,19 +442,19 @@ DhtRunner::doRun(const sockaddr_in* sin4, const sockaddr_in6* sin6, SecureDht::C
|
||||
|
||||
if(rc > 0) {
|
||||
std::array<uint8_t, 1024 * 64> buf;
|
||||
sockaddr_storage from;
|
||||
socklen_t fromlen {sizeof(from)};
|
||||
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, &fromlen);
|
||||
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, &fromlen);
|
||||
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}, std::make_pair(from, fromlen));
|
||||
rcv.emplace_back(Blob {buf.begin(), buf.begin()+rc+1}, from);
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
@@ -429,104 +471,129 @@ DhtRunner::doRun(const sockaddr_in* sin4, const sockaddr_in6* sin6, SecureDht::C
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::get(InfoHash hash, GetCallback vcb, DoneCallback dcb, Value::Filter f)
|
||||
DhtRunner::get(InfoHash hash, GetCallback vcb, DoneCallback dcb, Value::Filter f, Where w)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) mutable {
|
||||
dht.get(hash, vcb, dcb, std::move(f));
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) mutable {
|
||||
dht.get(hash, vcb, dcb, std::move(f), std::move(w));
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::get(const std::string& key, GetCallback vcb, DoneCallbackSimple dcb, Value::Filter f)
|
||||
DhtRunner::get(const std::string& key, GetCallback vcb, DoneCallbackSimple dcb, Value::Filter f, Where w)
|
||||
{
|
||||
get(InfoHash::get(key), vcb, dcb, f);
|
||||
get(InfoHash::get(key), vcb, dcb, f, w);
|
||||
}
|
||||
void DhtRunner::query(const InfoHash& hash, QueryCallback cb, DoneCallback done_cb, Query q) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) mutable {
|
||||
dht.query(hash, cb, done_cb, std::move(q));
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
std::future<size_t>
|
||||
DhtRunner::listen(InfoHash hash, GetCallback vcb, Value::Filter f)
|
||||
DhtRunner::listen(InfoHash hash, GetCallback vcb, Value::Filter f, Where w)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
auto ret_token = std::make_shared<std::promise<size_t>>();
|
||||
pending_ops.emplace([=](SecureDht& dht) mutable {
|
||||
ret_token->set_value(dht.listen(hash, vcb, std::move(f)));
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) mutable {
|
||||
ret_token->set_value(dht.listen(hash, vcb, std::move(f), std::move(w)));
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
return ret_token->get_future();
|
||||
}
|
||||
|
||||
std::future<size_t>
|
||||
DhtRunner::listen(const std::string& key, GetCallback vcb, Value::Filter f)
|
||||
DhtRunner::listen(const std::string& key, GetCallback vcb, Value::Filter f, Where w)
|
||||
{
|
||||
return listen(InfoHash::get(key), vcb, f);
|
||||
return listen(InfoHash::get(key), vcb, f, w);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::cancelListen(InfoHash h, size_t token)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.cancelListen(h, token);
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.cancelListen(h, token);
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::cancelListen(InfoHash h, std::shared_future<size_t> token)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
auto tk = token.get();
|
||||
dht.cancelListen(h, tk);
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
auto tk = token.get();
|
||||
dht.cancelListen(h, tk);
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::put(InfoHash hash, Value&& value, DoneCallback cb, bool permanent)
|
||||
DhtRunner::put(InfoHash hash, Value&& value, DoneCallback cb, time_point created, bool permanent)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
auto sv = std::make_shared<Value>(std::move(value));
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.put(hash, sv, cb, {}, permanent);
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
auto sv = std::make_shared<Value>(std::move(value));
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.put(hash, sv, cb, created, permanent);
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::put(InfoHash hash, std::shared_ptr<Value> value, DoneCallback cb, bool permanent)
|
||||
DhtRunner::put(InfoHash hash, std::shared_ptr<Value> value, DoneCallback cb, time_point created, bool permanent)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.put(hash, value, cb, {}, permanent);
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.put(hash, value, cb, created, permanent);
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::put(const std::string& key, Value&& value, DoneCallbackSimple cb, bool permanent)
|
||||
DhtRunner::put(const std::string& key, Value&& value, DoneCallbackSimple cb, time_point created, bool permanent)
|
||||
{
|
||||
put(InfoHash::get(key), std::forward<Value>(value), cb, permanent);
|
||||
put(InfoHash::get(key), std::forward<Value>(value), cb, created, permanent);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::cancelPut(const InfoHash& h , const Value::Id& id)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.cancelPut(h, id);
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.cancelPut(h, id);
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::putSigned(InfoHash hash, std::shared_ptr<Value> value, DoneCallback cb)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.putSigned(hash, value, cb);
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.putSigned(hash, value, cb);
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
@@ -545,10 +612,12 @@ DhtRunner::putSigned(const std::string& key, Value&& value, DoneCallbackSimple c
|
||||
void
|
||||
DhtRunner::putEncrypted(InfoHash hash, InfoHash to, std::shared_ptr<Value> value, DoneCallback cb)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.putEncrypted(hash, to, value, cb);
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.putEncrypted(hash, to, value, cb);
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
@@ -564,18 +633,77 @@ DhtRunner::putEncrypted(const std::string& key, InfoHash to, Value&& value, Done
|
||||
putEncrypted(InfoHash::get(key), to, std::forward<Value>(value), cb);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::tryBootstrapContinuously()
|
||||
{
|
||||
if (bootstrap_thread.joinable()) {
|
||||
if (bootstraping)
|
||||
return; // already running
|
||||
else
|
||||
bootstrap_thread.join();
|
||||
}
|
||||
bootstraping = true;
|
||||
bootstrap_thread = std::thread([this]() {
|
||||
auto next = clock::now();
|
||||
do {
|
||||
decltype(bootstrap_nodes) nodes;
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(bootstrap_mtx);
|
||||
nodes = bootstrap_nodes;
|
||||
}
|
||||
|
||||
next += BOOTSTRAP_PERIOD;
|
||||
{
|
||||
std::mutex mtx;
|
||||
std::unique_lock<std::mutex> blck(mtx);
|
||||
unsigned ping_count(0);
|
||||
// Reverse: try last inserted bootstrap nodes first
|
||||
for (auto it = nodes.rbegin(); it != nodes.rend(); it++) {
|
||||
++ping_count;
|
||||
try {
|
||||
bootstrap(getAddrInfo(it->first, it->second), [&](bool) {
|
||||
if (not running)
|
||||
return;
|
||||
{
|
||||
std::unique_lock<std::mutex> blck(mtx);
|
||||
--ping_count;
|
||||
}
|
||||
bootstrap_cv.notify_all();
|
||||
});
|
||||
} catch (std::invalid_argument& e) {
|
||||
--ping_count;
|
||||
std::cerr << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
// wait at least until the next BOOTSTRAP_PERIOD
|
||||
bootstrap_cv.wait_until(blck, next, [&]() { return not running; });
|
||||
// wait for bootstrap requests to end.
|
||||
if (running)
|
||||
bootstrap_cv.wait(blck, [&]() { return not running or ping_count == 0; });
|
||||
}
|
||||
// update state
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
bootstraping = running and
|
||||
status4 == NodeStatus::Disconnected and
|
||||
status6 == NodeStatus::Disconnected;
|
||||
}
|
||||
} while (bootstraping);
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<std::pair<sockaddr_storage, socklen_t>>
|
||||
DhtRunner::getAddrInfo(const char* host, const char* service)
|
||||
DhtRunner::getAddrInfo(const std::string& host, const std::string& service)
|
||||
{
|
||||
std::vector<std::pair<sockaddr_storage, socklen_t>> ips {};
|
||||
if (not host or not service or strlen(host) == 0)
|
||||
if (host.empty())
|
||||
return ips;
|
||||
|
||||
addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
addrinfo* info = nullptr;
|
||||
int rc = getaddrinfo(host, service, &hints, &info);
|
||||
int rc = getaddrinfo(host.c_str(), service.c_str(), &hints, &info);
|
||||
if(rc != 0)
|
||||
throw std::invalid_argument(std::string("Error: `") + host + ":" + service + "`: " + gai_strerror(rc));
|
||||
|
||||
@@ -590,18 +718,35 @@ DhtRunner::getAddrInfo(const char* host, const char* service)
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::bootstrap(const char* host, const char* service)
|
||||
DhtRunner::bootstrap(const std::string& host, const std::string& service)
|
||||
{
|
||||
bootstrap(getAddrInfo(host, service));
|
||||
std::lock_guard<std::mutex> lck(bootstrap_mtx);
|
||||
bootstrap_nodes_all.emplace_back(host, service);
|
||||
bootstrap_nodes.emplace_back(host, service);
|
||||
tryBootstrapContinuously();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::bootstrap(const std::vector<std::pair<sockaddr_storage, socklen_t>>& nodes)
|
||||
DhtRunner::clearBootstrap()
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(bootstrap_mtx);
|
||||
bootstrap_nodes_all.clear();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::bootstrap(const std::vector<std::pair<sockaddr_storage, socklen_t>>& nodes, DoneCallbackSimple&& cb)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht& dht) {
|
||||
pending_ops_prio.emplace([=](SecureDht& dht) mutable {
|
||||
auto rem = cb ? std::make_shared<std::pair<size_t, bool>>(nodes.size(), false) : nullptr;
|
||||
for (auto& node : nodes)
|
||||
dht.pingNode((sockaddr*)&node.first, node.second);
|
||||
dht.pingNode((sockaddr*)&node.first, node.second, cb ? [rem,cb](bool ok) {
|
||||
auto& r = *rem;
|
||||
r.first--;
|
||||
r.second |= ok;
|
||||
if (not r.first)
|
||||
cb(r.second);
|
||||
} : DoneCallbackSimple{});
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
@@ -609,30 +754,36 @@ DhtRunner::bootstrap(const std::vector<std::pair<sockaddr_storage, socklen_t>>&
|
||||
void
|
||||
DhtRunner::bootstrap(const std::vector<NodeExport>& nodes)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht& dht) {
|
||||
for (auto& node : nodes)
|
||||
dht.insertNode(node);
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht& dht) {
|
||||
for (auto& node : nodes)
|
||||
dht.insertNode(node);
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::connectivityChanged()
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.connectivityChanged();
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.connectivityChanged();
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::findCertificate(InfoHash hash, std::function<void(const std::shared_ptr<crypto::Certificate>)> cb) {
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.findCertificate(hash, cb);
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.findCertificate(hash, cb);
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,538 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Nicolas Reynaud <nicolas.reynaud@savoirfairelinux.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "indexation/pht.h"
|
||||
#include "rng.h"
|
||||
|
||||
namespace dht {
|
||||
namespace indexation {
|
||||
|
||||
/**
|
||||
* Output the blob into string and readable way
|
||||
*
|
||||
* @param bl : Blob to print
|
||||
*
|
||||
* @return string that represent the blob into a readable way
|
||||
*/
|
||||
static std::string blobToString(const Blob &bl) {
|
||||
std::stringstream ss;
|
||||
auto bn = bl.size() % 8;
|
||||
auto n = bl.size() / 8;
|
||||
|
||||
for (size_t i = 0; i < bl.size(); i++)
|
||||
ss << std::bitset<8>(bl[i]) << " ";
|
||||
if (bn)
|
||||
for (unsigned b=0; b < bn; b++)
|
||||
ss << (char)((bl[n] & (1 << (7 - b))) ? '1':'0');
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string Prefix::toString() const {
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "Prefix : " << std::endl << "\tContent_ : \"";
|
||||
ss << blobToString(content_);
|
||||
ss << "\"" << std::endl;
|
||||
|
||||
ss << "\tFlags_ : \"";
|
||||
ss << blobToString(flags_);
|
||||
ss << "\"" << std::endl;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void Pht::Cache::insert(const Prefix& p) {
|
||||
size_t i = 0;
|
||||
auto now = clock::now();
|
||||
|
||||
std::shared_ptr<Node> curr_node;
|
||||
|
||||
while ((leaves_.size() > 0
|
||||
and leaves_.begin()->first + NODE_EXPIRE_TIME < now)
|
||||
or leaves_.size() > MAX_ELEMENT) {
|
||||
|
||||
leaves_.erase(leaves_.begin());
|
||||
}
|
||||
|
||||
if (not (curr_node = root_.lock()) ) {
|
||||
/* Root does not exist, need to create one*/
|
||||
curr_node = std::make_shared<Node>();
|
||||
root_ = curr_node;
|
||||
}
|
||||
|
||||
curr_node->last_reply = now;
|
||||
|
||||
/* Iterate through all bit of the Blob */
|
||||
for ( i = 0; i < p.size_; i++ ) {
|
||||
|
||||
/* According to the bit define which node is the next one */
|
||||
auto& next = ( p.isContentBitActive(i) ) ? curr_node->right_child : curr_node->left_child;
|
||||
|
||||
/**
|
||||
* If lock, node exists
|
||||
* else create it
|
||||
*/
|
||||
if (auto n = next.lock()) {
|
||||
curr_node = std::move(n);
|
||||
} else {
|
||||
/* Create the next node if doesn't exist*/
|
||||
auto tmp_curr_node = std::make_shared<Node>();
|
||||
tmp_curr_node->parent = curr_node;
|
||||
next = tmp_curr_node;
|
||||
curr_node = std::move(tmp_curr_node);
|
||||
}
|
||||
|
||||
curr_node->last_reply = now;
|
||||
}
|
||||
|
||||
/* Insert the leaf (curr_node) into the multimap */
|
||||
leaves_.emplace(std::move(now), std::move(curr_node) );
|
||||
}
|
||||
|
||||
int Pht::Cache::lookup(const Prefix& p) {
|
||||
int pos = -1;
|
||||
auto now = clock::now(), last_node_time = now;
|
||||
|
||||
/* Before lookup remove the useless one [i.e. too old] */
|
||||
while ( leaves_.size() > 0
|
||||
and leaves_.begin()->first + NODE_EXPIRE_TIME < now ) {
|
||||
|
||||
leaves_.erase(leaves_.begin());
|
||||
}
|
||||
|
||||
auto next = root_;
|
||||
std::shared_ptr<Node> curr_node;
|
||||
|
||||
while ( auto n = next.lock() ) {
|
||||
++pos;
|
||||
/* Safe since pos is equal to 0 until here */
|
||||
if ( (unsigned) pos >= p.content_.size() * 8) break;
|
||||
|
||||
curr_node = n;
|
||||
last_node_time = curr_node->last_reply;
|
||||
curr_node->last_reply = now;
|
||||
|
||||
/* Get the Prefix bit by bit, starting from left */
|
||||
next = ( p.isContentBitActive(pos) ) ? curr_node->right_child : curr_node->left_child;
|
||||
}
|
||||
|
||||
if ( pos >= 0 ) {
|
||||
auto to_erase = leaves_.find(last_node_time);
|
||||
if ( to_erase != leaves_.end() )
|
||||
leaves_.erase( to_erase );
|
||||
|
||||
leaves_.emplace( std::move(now), std::move(curr_node) );
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
const ValueType IndexEntry::TYPE = ValueType::USER_DATA;
|
||||
constexpr std::chrono::minutes Pht::Cache::NODE_EXPIRE_TIME;
|
||||
|
||||
void Pht::lookupStep(Prefix p, std::shared_ptr<int> lo, std::shared_ptr<int> hi,
|
||||
std::shared_ptr<std::vector<std::shared_ptr<IndexEntry>>> vals,
|
||||
LookupCallbackWrapper cb, DoneCallbackSimple done_cb,
|
||||
std::shared_ptr<unsigned> max_common_prefix_len,
|
||||
int start, bool all_values)
|
||||
{
|
||||
struct node_lookup_result {
|
||||
bool done {false};
|
||||
bool is_pht {false};
|
||||
};
|
||||
|
||||
/* start could be under 0 but after the compare it to 0 it always will be unsigned, so we can cast it*/
|
||||
auto mid = (start >= 0) ? (unsigned) start : (*lo + *hi)/2;
|
||||
|
||||
auto first_res = std::make_shared<node_lookup_result>();
|
||||
auto second_res = std::make_shared<node_lookup_result>();
|
||||
|
||||
auto on_done = [=](bool ok) {
|
||||
bool is_leaf = first_res->is_pht and not second_res->is_pht;
|
||||
if (not ok) {
|
||||
if (done_cb)
|
||||
done_cb(false);
|
||||
}
|
||||
else if (is_leaf or *lo > *hi) {
|
||||
// leaf node
|
||||
Prefix to_insert = p.getPrefix(mid);
|
||||
cache_.insert(to_insert);
|
||||
|
||||
if (cb) {
|
||||
if (vals->size() == 0 and max_common_prefix_len and mid > 0) {
|
||||
auto p_ = (p.getPrefix(mid)).getSibling().getFullSize();
|
||||
*lo = mid;
|
||||
*hi = p_.size_;
|
||||
lookupStep(p_, lo, hi, vals, cb, done_cb, max_common_prefix_len, -1, all_values);
|
||||
}
|
||||
|
||||
cb(*vals, to_insert);
|
||||
}
|
||||
|
||||
if (done_cb)
|
||||
done_cb(true);
|
||||
} else if (first_res->is_pht) {
|
||||
// internal node
|
||||
*lo = mid+1;
|
||||
lookupStep(p, lo, hi, vals, cb, done_cb, max_common_prefix_len, -1, all_values);
|
||||
} else {
|
||||
// first get failed before second.
|
||||
if (done_cb)
|
||||
done_cb(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (*lo <= *hi) {
|
||||
auto pht_filter = [&](const dht::Value& v) {
|
||||
return v.user_type.compare(0, name_.size(), name_) == 0;
|
||||
};
|
||||
|
||||
auto on_get = [=](const std::shared_ptr<dht::Value>& value, std::shared_ptr<node_lookup_result> res) {
|
||||
if (value->user_type == canary_) {
|
||||
res->is_pht = true;
|
||||
}
|
||||
else {
|
||||
IndexEntry entry;
|
||||
entry.unpackValue(*value);
|
||||
|
||||
auto it = std::find_if(vals->cbegin(), vals->cend(), [&](const std::shared_ptr<IndexEntry>& ie) {
|
||||
return ie->value == entry.value;
|
||||
});
|
||||
|
||||
/* If we already got the value then get the next one */
|
||||
if (it != vals->cend())
|
||||
return true;
|
||||
|
||||
if (max_common_prefix_len) { /* inexact match case */
|
||||
auto common_bits = Prefix::commonBits(p, entry.prefix);
|
||||
|
||||
if (vals->empty()) {
|
||||
vals->emplace_back(std::make_shared<IndexEntry>(entry));
|
||||
*max_common_prefix_len = common_bits;
|
||||
}
|
||||
else {
|
||||
if (common_bits == *max_common_prefix_len) /* this is the max so far */
|
||||
vals->emplace_back(std::make_shared<IndexEntry>(entry));
|
||||
else if (common_bits > *max_common_prefix_len) { /* new max found! */
|
||||
vals->clear();
|
||||
vals->emplace_back(std::make_shared<IndexEntry>(entry));
|
||||
*max_common_prefix_len = common_bits;
|
||||
}
|
||||
}
|
||||
} else if (all_values or entry.prefix == p.content_) /* exact match case */
|
||||
vals->emplace_back(std::make_shared<IndexEntry>(entry));
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
dht_->get(p.getPrefix(mid).hash(),
|
||||
std::bind(on_get, std::placeholders::_1, first_res),
|
||||
[=](bool ok) {
|
||||
if (not ok) {
|
||||
// DHT failed
|
||||
first_res->done = true;
|
||||
if (done_cb and second_res->done)
|
||||
on_done(false);
|
||||
}
|
||||
else {
|
||||
if (not first_res->is_pht) {
|
||||
// Not a PHT node.
|
||||
*hi = mid-1;
|
||||
lookupStep(p, lo, hi, vals, cb, done_cb, max_common_prefix_len, -1, all_values);
|
||||
} else {
|
||||
first_res->done = true;
|
||||
if (second_res->done or mid >= p.size_ - 1)
|
||||
on_done(true);
|
||||
}
|
||||
}
|
||||
}, pht_filter);
|
||||
|
||||
if (mid < p.size_ - 1)
|
||||
dht_->get(p.getPrefix(mid+1).hash(),
|
||||
std::bind(on_get, std::placeholders::_1, second_res),
|
||||
[=](bool ok) {
|
||||
if (not ok) {
|
||||
// DHT failed
|
||||
second_res->done = true;
|
||||
if (done_cb and first_res->done)
|
||||
on_done(false);
|
||||
}
|
||||
else {
|
||||
second_res->done = true;
|
||||
if (first_res->done)
|
||||
on_done(true);
|
||||
}
|
||||
}, pht_filter);
|
||||
} else {
|
||||
on_done(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Pht::lookup(Key k, Pht::LookupCallback cb, DoneCallbackSimple done_cb, bool exact_match) {
|
||||
auto prefix = linearize(k);
|
||||
auto values = std::make_shared<std::vector<std::shared_ptr<IndexEntry>>>();
|
||||
|
||||
auto lo = std::make_shared<int>(0);
|
||||
auto hi = std::make_shared<int>(prefix.size_);
|
||||
std::shared_ptr<unsigned> max_common_prefix_len = not exact_match ? std::make_shared<unsigned>(0) : nullptr;
|
||||
|
||||
lookupStep(prefix, lo, hi, values,
|
||||
[=](std::vector<std::shared_ptr<IndexEntry>>& entries, Prefix p) {
|
||||
std::vector<std::shared_ptr<Value>> vals(entries.size());
|
||||
|
||||
std::transform(entries.begin(), entries.end(), vals.begin(),
|
||||
[](const std::shared_ptr<IndexEntry>& ie) {
|
||||
return std::make_shared<Value>(ie->value);
|
||||
});
|
||||
|
||||
cb(vals, p);
|
||||
}, done_cb, max_common_prefix_len, cache_.lookup(prefix));
|
||||
}
|
||||
|
||||
void Pht::updateCanary(Prefix p) {
|
||||
// TODO: change this... copy value
|
||||
dht::Value canary_value;
|
||||
canary_value.user_type = canary_;
|
||||
|
||||
dht_->put(p.hash(), std::move(canary_value),
|
||||
[=](bool){
|
||||
static std::bernoulli_distribution d(0.5);
|
||||
crypto::random_device rd;
|
||||
if (p.size_ and d(rd))
|
||||
updateCanary(p.getPrefix(-1));
|
||||
}
|
||||
);
|
||||
|
||||
if (p.size_) {
|
||||
dht::Value canary_second_value;
|
||||
canary_second_value.user_type = canary_;
|
||||
dht_->put(p.getSibling().hash(), std::move(canary_second_value));
|
||||
}
|
||||
}
|
||||
|
||||
void Pht::insert(Prefix kp, IndexEntry entry, std::shared_ptr<int> lo, std::shared_ptr<int> hi, time_point time_p,
|
||||
bool check_split, DoneCallbackSimple done_cb) {
|
||||
|
||||
if (time_p + ValueType::USER_DATA.expiration < clock::now()) return;
|
||||
|
||||
auto vals = std::make_shared<std::vector<std::shared_ptr<IndexEntry>>>();
|
||||
auto final_prefix = std::make_shared<Prefix>();
|
||||
|
||||
lookupStep(kp, lo, hi, vals,
|
||||
[=](std::vector<std::shared_ptr<IndexEntry>>&, Prefix p) {
|
||||
*final_prefix = Prefix(p);
|
||||
},
|
||||
[=](bool ok){
|
||||
if (not ok) {
|
||||
if (done_cb)
|
||||
done_cb(false);
|
||||
} else {
|
||||
|
||||
RealInsertCallback real_insert = [=]( std::shared_ptr<Prefix> p, IndexEntry entry) {
|
||||
updateCanary(*p);
|
||||
checkPhtUpdate(*p, entry, time_p);
|
||||
cache_.insert(*p);
|
||||
dht_->put(p->hash(), std::move(entry), done_cb , time_p);
|
||||
};
|
||||
|
||||
if ( not check_split or final_prefix->size_ == kp.size_ ) {
|
||||
real_insert(final_prefix, std::move(entry));
|
||||
} else {
|
||||
if ( vals->size() < MAX_NODE_ENTRY_COUNT ) {
|
||||
getRealPrefix(final_prefix, std::move(entry), real_insert);
|
||||
}
|
||||
else {
|
||||
split(*final_prefix, vals, entry, real_insert);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, nullptr, cache_.lookup(kp), true
|
||||
);
|
||||
}
|
||||
|
||||
Prefix Pht::zcurve(const std::vector<Prefix>& all_prefix) const {
|
||||
Prefix p;
|
||||
|
||||
if ( all_prefix.size() == 1 )
|
||||
return all_prefix[0];
|
||||
|
||||
/* All prefix got the same size (thanks to padding) */
|
||||
size_t prefix_size = all_prefix[0].content_.size();
|
||||
|
||||
/* Loop on all uint8_t of the input prefix */
|
||||
for ( size_t j = 0, bit = 0; j < prefix_size; j++) {
|
||||
|
||||
uint8_t mask = 0x80;
|
||||
/* For each of the 8 bits of the input uint8_t */
|
||||
for ( int i = 0; i < 8; ) {
|
||||
|
||||
uint8_t flags = 0;
|
||||
uint8_t content = 0;
|
||||
|
||||
/* For each bit of the output uint8_t */
|
||||
for ( int k = 0 ; k < 8; k++ ) {
|
||||
|
||||
auto diff = k - i;
|
||||
|
||||
/*get the content 'c', and the flag 'f' of the input prefix */
|
||||
auto c = all_prefix[bit].content_[j] & mask;
|
||||
auto f = all_prefix[bit].flags_[j] & mask;
|
||||
|
||||
/* Move this bit at the right position according to the diff
|
||||
and merge it into content and flags in the same way */
|
||||
content |= ( diff >= 0 ) ? c >> diff : c << std::abs(diff);
|
||||
flags |= ( diff >= 0 ) ? f >> diff : f << std::abs(diff);
|
||||
|
||||
/* If we are on the last prefix of the vector get back to the first and
|
||||
,move the mask in order to get the n + 1nth bit */
|
||||
if ( ++bit == all_prefix.size() ) { bit = 0; ++i; mask >>= 1; }
|
||||
}
|
||||
|
||||
/* Add the next flags + content to the output prefix */
|
||||
p.content_.push_back(content);
|
||||
p.flags_.push_back(flags);
|
||||
p.size_ += 8;
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
Prefix Pht::linearize(Key k) const {
|
||||
if (not validKey(k)) { throw std::invalid_argument(INVALID_KEY); }
|
||||
|
||||
std::vector<Prefix> all_prefix;
|
||||
all_prefix.reserve(k.size());
|
||||
|
||||
/* Get the max size of the keyspec and take it for size limit (for padding) */
|
||||
auto max = std::max_element(keySpec_.begin(), keySpec_.end(),
|
||||
[](const std::pair<std::string, size_t>& a, const std::pair<std::string, size_t>& b) {
|
||||
return a.second < b.second;
|
||||
})->second + 1;
|
||||
|
||||
for ( auto const& it : k ) {
|
||||
Prefix p = Blob {it.second.begin(), it.second.end()};
|
||||
p.addPaddingContent(max);
|
||||
p.updateFlags();
|
||||
|
||||
all_prefix.emplace_back(std::move(p));
|
||||
}
|
||||
|
||||
return zcurve(all_prefix);
|
||||
}
|
||||
|
||||
void Pht::getRealPrefix(std::shared_ptr<Prefix> p, IndexEntry entry, RealInsertCallback end_cb ) {
|
||||
|
||||
if ( p->size_ == 0 ) {
|
||||
end_cb(p, std::move(entry));
|
||||
return;
|
||||
}
|
||||
|
||||
auto total = std::make_shared<unsigned int>(0); /* Will contains the total number of data on 3 nodes */
|
||||
auto ended = std::make_shared<unsigned int>(0); /* Just indicate how many have end */
|
||||
|
||||
auto parent = std::make_shared<Prefix>(p->getPrefix(-1));
|
||||
auto sibling = std::make_shared<Prefix>(p->getSibling());
|
||||
|
||||
auto pht_filter = [&](const dht::Value& v) {
|
||||
return v.user_type.compare(0, name_.size(), name_) == 0;
|
||||
};
|
||||
|
||||
/* Lambda will count total number of data node */
|
||||
auto count = [=]( const std::shared_ptr<dht::Value> value ) {
|
||||
if ( value->user_type != canary_)
|
||||
(*total)++;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
auto on_done = [=] ( bool ) {
|
||||
(*ended)++;
|
||||
/* Only the last one do the CallBack*/
|
||||
if ( *ended == 3 ) {
|
||||
if ( *total < MAX_NODE_ENTRY_COUNT )
|
||||
end_cb(parent, std::move(entry));
|
||||
else
|
||||
end_cb(p, std::move(entry));
|
||||
}
|
||||
};
|
||||
|
||||
dht_->get(parent->hash(),
|
||||
count,
|
||||
on_done,
|
||||
pht_filter
|
||||
);
|
||||
|
||||
dht_->get(p->hash(),
|
||||
count,
|
||||
on_done,
|
||||
pht_filter
|
||||
);
|
||||
|
||||
dht_->get(sibling->hash(),
|
||||
count,
|
||||
on_done,
|
||||
pht_filter
|
||||
);
|
||||
}
|
||||
|
||||
void Pht::checkPhtUpdate(Prefix p, IndexEntry entry, time_point time_p) {
|
||||
|
||||
Prefix full = entry.prefix;
|
||||
if ( p.content_.size() * 8 >= full.content_.size() * 8 ) return;
|
||||
|
||||
auto next_prefix = full.getPrefix( p.size_ + 1 );
|
||||
|
||||
dht_->listen(next_prefix.hash(),
|
||||
[=](const std::shared_ptr<dht::Value> &value) {
|
||||
if (value->user_type == canary_) {
|
||||
insert(full, entry, std::make_shared<int>(0), std::make_shared<int>(full.size_), time_p, false, nullptr);
|
||||
|
||||
/* Cancel listen since we found where we need to update*/
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
[=](const dht::Value& v) {
|
||||
/* Filter value v thats start with the same name as ours */
|
||||
return v.user_type.compare(0, name_.size(), name_) == 0;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void Pht::split(Prefix insert, std::shared_ptr<std::vector<std::shared_ptr<IndexEntry>>> vals, IndexEntry entry, RealInsertCallback end_cb ) {
|
||||
auto full = Prefix(entry.prefix);
|
||||
|
||||
auto loc = foundSplitLocation(full, vals);
|
||||
auto prefix_to_insert = std::make_shared<Prefix>(full.getPrefix(loc));
|
||||
|
||||
for(;loc != insert.size_ - 1; loc--) {
|
||||
updateCanary(full.getPrefix(loc));
|
||||
}
|
||||
|
||||
end_cb(prefix_to_insert, entry);
|
||||
}
|
||||
|
||||
} /* indexation */
|
||||
|
||||
} /* dht */
|
||||
+14
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-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
|
||||
@@ -91,4 +91,17 @@ std::ostream& operator<< (std::ostream& s, const InfoHash& h)
|
||||
return s;
|
||||
}
|
||||
|
||||
std::istream& operator>> (std::istream& s, InfoHash& h)
|
||||
{
|
||||
std::array<char, HASH_LEN*2> dat;
|
||||
s.exceptions(std::istream::eofbit | std::istream::failbit);
|
||||
s.read(&(*dat.begin()), dat.size());
|
||||
for (size_t i = 0; i < HASH_LEN; i++) {
|
||||
unsigned res = 0;
|
||||
sscanf(dat.data() + 2*i, "%02x", &res);
|
||||
h[i] = res;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2014-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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "log.h"
|
||||
|
||||
namespace dht {
|
||||
namespace log {
|
||||
|
||||
/**
|
||||
* Print va_list to std::ostream (used for logging).
|
||||
*/
|
||||
void
|
||||
printLog(std::ostream& s, char const *m, va_list args) {
|
||||
// print log to buffer
|
||||
std::array<char, 8192> buffer;
|
||||
int ret = vsnprintf(buffer.data(), buffer.size(), m, args);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
// write timestamp
|
||||
using namespace std::chrono;
|
||||
using log_precision = microseconds;
|
||||
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 << "]" << " ";
|
||||
|
||||
// write log
|
||||
s.write(buffer.data(), std::min((size_t) ret, buffer.size()));
|
||||
if ((size_t) ret >= buffer.size())
|
||||
s << "[[TRUNCATED]]";
|
||||
s << std::endl;
|
||||
}
|
||||
|
||||
void
|
||||
enableLogging(dht::DhtRunner &dht) {
|
||||
dht.setLoggers(
|
||||
[](char const *m, va_list args) {
|
||||
std::cerr << red;
|
||||
printLog(std::cerr, m, args);
|
||||
std::cerr << def;
|
||||
},
|
||||
[](char const *m, va_list args) {
|
||||
std::cout << yellow;
|
||||
printLog(std::cout, m, args);
|
||||
std::cout << def;
|
||||
},
|
||||
[](char const *m, va_list args) { printLog(std::cout, m, args); }
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
enableFileLogging(dht::DhtRunner &dht, const std::string &path) {
|
||||
auto logfile = std::make_shared<std::fstream>();
|
||||
logfile->open(path, std::ios::out);
|
||||
|
||||
dht.setLoggers(
|
||||
[=](char const *m, va_list args) { printLog(*logfile, m, args); },
|
||||
[=](char const *m, va_list args) { printLog(*logfile, m, args); },
|
||||
[=](char const *m, va_list args) { printLog(*logfile, m, args); }
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
disableLogging(dht::DhtRunner &dht) {
|
||||
dht.setLoggers(dht::NOLOG, dht::NOLOG, dht::NOLOG);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+748
-326
File diff suppressed because it is too large.
Load diff
+8
-9
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -65,15 +65,14 @@ Node::getPendingMessageCount() const
|
||||
}
|
||||
|
||||
void
|
||||
Node::update(const sockaddr* sa, socklen_t salen)
|
||||
Node::update(const SockAddr& new_addr)
|
||||
{
|
||||
std::copy_n((const uint8_t*)sa, salen, (uint8_t*)&ss);
|
||||
sslen = salen;
|
||||
addr = new_addr;
|
||||
}
|
||||
|
||||
/** To be called when a message was sent to the node */
|
||||
void
|
||||
Node::requested(std::shared_ptr<Request>& req)
|
||||
Node::requested(std::shared_ptr<net::Request>& req)
|
||||
{
|
||||
requests_.emplace_back(req);
|
||||
}
|
||||
@@ -81,12 +80,12 @@ Node::requested(std::shared_ptr<Request>& 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<Request> req)
|
||||
Node::received(time_point now, std::shared_ptr<net::Request> req)
|
||||
{
|
||||
time = now;
|
||||
expired_ = false;
|
||||
if (req) {
|
||||
reply_time = now;
|
||||
expired_ = false;
|
||||
for (auto it = requests_.begin(); it != requests_.end();) {
|
||||
auto r = it->lock();
|
||||
if (not r or r == req)
|
||||
@@ -119,7 +118,7 @@ Node::toString() const
|
||||
|
||||
std::ostream& operator<< (std::ostream& s, const Node& h)
|
||||
{
|
||||
s << h.id << " " << print_addr(h.ss, h.sslen);
|
||||
s << h.id << " " << h.addr.toString();
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
+39
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -27,10 +27,42 @@ NodeCache::getNode(const InfoHash& id, sa_family_t family) {
|
||||
}
|
||||
|
||||
std::shared_ptr<Node>
|
||||
NodeCache::getNode(const InfoHash& id, const sockaddr* sa, socklen_t sa_len, time_point now, int confirm) {
|
||||
NodeCache::getNode(const InfoHash& id, const SockAddr& addr, time_point now, bool confirm) {
|
||||
if (id == zeroes)
|
||||
return std::make_shared<Node>(id, sa, sa_len);
|
||||
return (sa->sa_family == AF_INET ? cache_4 : cache_6).getNode(id, sa, sa_len, now, confirm);
|
||||
return std::make_shared<Node>(id, addr);
|
||||
return (addr.getFamily() == AF_INET ? cache_4 : cache_6).getNode(id, addr, now, confirm);
|
||||
}
|
||||
|
||||
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<std::shared_ptr<Node>> nodes;
|
||||
nodes.reserve(std::min(c.size(), count));
|
||||
NodeMap::const_iterator it;
|
||||
|
||||
if (it_p != c.begin()) /* Create 2 separate iterator if we could */
|
||||
--it_p;
|
||||
|
||||
while (nodes.size() < count and (it_n != c.end() or it_p != c.end())) {
|
||||
/* 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 (auto n = it->second.lock())
|
||||
if ( not n->isExpired() )
|
||||
nodes.emplace_back(std::move(n));
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -57,15 +89,15 @@ NodeCache::NodeMap::getNode(const InfoHash& id)
|
||||
}
|
||||
|
||||
std::shared_ptr<Node>
|
||||
NodeCache::NodeMap::getNode(const InfoHash& id, const sockaddr* sa, socklen_t sa_len, time_point now, int confirm)
|
||||
NodeCache::NodeMap::getNode(const InfoHash& id, const SockAddr& addr, time_point now, bool confirm)
|
||||
{
|
||||
auto it = emplace(id, std::weak_ptr<Node>{});
|
||||
auto node = it.first->second.lock();
|
||||
if (not node) {
|
||||
node = std::make_shared<Node>(id, sa, sa_len);
|
||||
node = std::make_shared<Node>(id, addr);
|
||||
it.first->second = node;
|
||||
} else if (confirm || node->time < now - Node::NODE_EXPIRE_TIME) {
|
||||
node->update(sa, sa_len);
|
||||
node->update(addr);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-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
|
||||
|
||||
+16
-3
@@ -17,10 +17,21 @@ Bucket::randomNode()
|
||||
{
|
||||
if (nodes.empty())
|
||||
return nullptr;
|
||||
std::uniform_int_distribution<unsigned> rand_node(0, nodes.size()-1);
|
||||
unsigned expired_node_count = std::count_if(nodes.cbegin(), nodes.cend(), [](const decltype(nodes)::value_type& node) {
|
||||
return node->isExpired();
|
||||
});
|
||||
auto prioritize_not_expired = expired_node_count < nodes.size();
|
||||
|
||||
std::uniform_int_distribution<unsigned> rand_node(0, prioritize_not_expired
|
||||
? nodes.size() - expired_node_count - 1
|
||||
: nodes.size()-1);
|
||||
unsigned nn = rand_node(rd);
|
||||
for (auto& n : nodes)
|
||||
if (not nn--) return n;
|
||||
for (auto& n : nodes) {
|
||||
if (not (prioritize_not_expired and n->isExpired())) {
|
||||
if (not nn--)
|
||||
return n;
|
||||
}
|
||||
}
|
||||
return nodes.back();
|
||||
}
|
||||
|
||||
@@ -59,6 +70,8 @@ RoutingTable::middle(const RoutingTable::const_iterator& it) const
|
||||
unsigned
|
||||
RoutingTable::depth(const RoutingTable::const_iterator& it) const
|
||||
{
|
||||
if (it == end())
|
||||
return 0;
|
||||
int bit1 = it->first.lowbit();
|
||||
int bit2 = std::next(it) != end() ? std::next(it)->first.lowbit() : -1;
|
||||
return std::max(bit1, bit2)+1;
|
||||
|
||||
+10
-21
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -50,12 +51,6 @@ SecureDht::SecureDht(int s, int s6, SecureDht::Config conf)
|
||||
if (s < 0 && s6 < 0)
|
||||
return;
|
||||
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030300
|
||||
int rc = gnutls_global_init();
|
||||
if (rc != GNUTLS_E_SUCCESS)
|
||||
throw DhtException(std::string("Error initializing GnuTLS: ")+gnutls_strerror(rc));
|
||||
#endif
|
||||
|
||||
for (const auto& type : DEFAULT_TYPES)
|
||||
registerType(type);
|
||||
|
||||
@@ -76,18 +71,12 @@ SecureDht::SecureDht(int s, int s6, SecureDht::Config conf)
|
||||
}, [this](bool ok) {
|
||||
if (ok)
|
||||
DHT_LOG.DEBUG("SecureDht: public key announced successfully");
|
||||
else
|
||||
DHT_LOG.ERR("SecureDht: error while announcing public key!");
|
||||
}, {}, true);
|
||||
}
|
||||
}
|
||||
|
||||
SecureDht::~SecureDht()
|
||||
{
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030300
|
||||
gnutls_global_deinit();
|
||||
#endif
|
||||
}
|
||||
{}
|
||||
|
||||
ValueType
|
||||
SecureDht::secureType(ValueType&& type)
|
||||
@@ -99,7 +88,7 @@ SecureDht::secureType(ValueType&& type)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
DHT_LOG.WARN("Signature verification succeded");
|
||||
DHT_LOG.WARN("Signature verification succeeded");
|
||||
}
|
||||
return type.storePolicy(id, v, nid, a, al);
|
||||
};
|
||||
@@ -291,15 +280,15 @@ SecureDht::getCallbackFilter(GetCallback cb, Value::Filter&& filter)
|
||||
}
|
||||
|
||||
void
|
||||
SecureDht::get(const InfoHash& id, GetCallback cb, DoneCallback donecb, Value::Filter&& f)
|
||||
SecureDht::get(const InfoHash& id, GetCallback cb, DoneCallback donecb, Value::Filter&& f, Where&& w)
|
||||
{
|
||||
Dht::get(id, getCallbackFilter(cb, std::forward<Value::Filter>(f)), donecb);
|
||||
Dht::get(id, getCallbackFilter(cb, std::forward<Value::Filter>(f)), donecb, {}, std::forward<Where>(w));
|
||||
}
|
||||
|
||||
size_t
|
||||
SecureDht::listen(const InfoHash& id, GetCallback cb, Value::Filter&& f)
|
||||
SecureDht::listen(const InfoHash& id, GetCallback cb, Value::Filter&& f, Where&& w)
|
||||
{
|
||||
return Dht::listen(id, getCallbackFilter(cb, std::forward<Value::Filter>(f)));
|
||||
return Dht::listen(id, getCallbackFilter(cb, std::forward<Value::Filter>(f)), {}, std::forward<Where>(w));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -333,7 +322,7 @@ SecureDht::putSigned(const InfoHash& hash, std::shared_ptr<Value> val, DoneCallb
|
||||
},
|
||||
[hash,val,this,callback,permanent] (bool /* ok */) {
|
||||
sign(*val);
|
||||
put(hash, val, callback, {}, permanent);
|
||||
put(hash, val, callback, time_point::max(), permanent);
|
||||
},
|
||||
Value::IdFilter(val->id)
|
||||
);
|
||||
@@ -350,7 +339,7 @@ SecureDht::putEncrypted(const InfoHash& hash, const InfoHash& to, std::shared_pt
|
||||
}
|
||||
DHT_LOG.WARN("Encrypting data for PK: %s", pk->getId().toString().c_str());
|
||||
try {
|
||||
put(hash, encrypt(*val, *pk), callback, {}, permanent);
|
||||
put(hash, encrypt(*val, *pk), callback, time_point::max(), permanent);
|
||||
} catch (const std::exception& e) {
|
||||
DHT_LOG.ERR("Error putting encrypted data: %s", e.what());
|
||||
if (callback)
|
||||
|
||||
+18
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-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
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include "utils.h"
|
||||
#include "sockaddr.h"
|
||||
#include "default_types.h"
|
||||
|
||||
namespace dht {
|
||||
@@ -47,10 +48,14 @@ print_addr(const sockaddr_storage& ss, socklen_t sslen)
|
||||
}
|
||||
|
||||
std::string
|
||||
printAddr(const Address& addr) {
|
||||
printAddr(const SockAddr& addr) {
|
||||
return print_addr((const sockaddr*)&addr.first, addr.second);
|
||||
}
|
||||
|
||||
bool operator==(const SockAddr& a, const SockAddr& b) {
|
||||
return a.equals(b);
|
||||
}
|
||||
|
||||
time_point from_time_t(std::time_t t) {
|
||||
return clock::now() + (std::chrono::system_clock::from_time_t(t) - std::chrono::system_clock::now());
|
||||
}
|
||||
@@ -85,4 +90,15 @@ unpackMsg(Blob b) {
|
||||
return msgpack::unpack((const char*)b.data(), b.size());
|
||||
}
|
||||
|
||||
msgpack::object*
|
||||
findMapValue(msgpack::object& map, const std::string& key) {
|
||||
if (map.type != msgpack::type::MAP) throw msgpack::type_error();
|
||||
for (unsigned i = 0; i < map.via.map.size; i++) {
|
||||
auto& o = map.via.map.ptr[i];
|
||||
if (o.key.type == msgpack::type::STR && o.key.as<std::string>() == key)
|
||||
return &o.val;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
+331
-4
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
const std::string Query::QUERY_PARSE_ERROR {"Error parsing query."};
|
||||
|
||||
std::ostream& operator<< (std::ostream& s, const Value& v)
|
||||
{
|
||||
s << "Value[id:" << std::hex << v.id << std::dec << " ";
|
||||
@@ -50,7 +52,7 @@ std::ostream& operator<< (std::ostream& s, const Value& v)
|
||||
s << "Data (type: " << v.type << " ): ";
|
||||
s << std::hex;
|
||||
for (size_t i=0; i<v.data.size(); i++)
|
||||
s << std::setfill('0') << std::setw(2) << (unsigned)v.data[i] << " ";
|
||||
s << std::setfill('0') << std::setw(2) << (unsigned)v.data[i];
|
||||
s << std::dec;
|
||||
}
|
||||
}
|
||||
@@ -63,7 +65,7 @@ const ValueType ValueType::USER_DATA = {0, "User Data"};
|
||||
bool
|
||||
ValueType::DEFAULT_STORE_POLICY(InfoHash, std::shared_ptr<Value>& v, InfoHash, const sockaddr*, socklen_t)
|
||||
{
|
||||
return v->data.size() <= MAX_VALUE_SIZE;
|
||||
return v->size() <= MAX_VALUE_SIZE;
|
||||
}
|
||||
|
||||
msgpack::object*
|
||||
@@ -157,4 +159,329 @@ Value::msgpack_unpack_body(const msgpack::object& o)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
FieldValue::operator==(const FieldValue& vfd) const
|
||||
{
|
||||
if (field != vfd.field)
|
||||
return false;
|
||||
switch (field) {
|
||||
case Value::Field::Id:
|
||||
case Value::Field::ValueType:
|
||||
case Value::Field::SeqNum:
|
||||
return intValue == vfd.intValue;
|
||||
case Value::Field::OwnerPk:
|
||||
return hashValue == vfd.hashValue;
|
||||
case Value::Field::UserType:
|
||||
return blobValue == vfd.blobValue;
|
||||
case Value::Field::None:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Value::Filter
|
||||
FieldValue::getLocalFilter() const
|
||||
{
|
||||
switch (field) {
|
||||
case Value::Field::Id:
|
||||
return Value::IdFilter(intValue);
|
||||
case Value::Field::ValueType:
|
||||
return Value::TypeFilter(intValue);
|
||||
case Value::Field::OwnerPk:
|
||||
return Value::OwnerFilter(hashValue);
|
||||
case Value::Field::SeqNum:
|
||||
return Value::SeqNumFilter(intValue);
|
||||
case Value::Field::UserType:
|
||||
return Value::UserTypeFilter(std::string {blobValue.begin(), blobValue.end()});
|
||||
default:
|
||||
return Value::AllFilter();
|
||||
}
|
||||
}
|
||||
|
||||
FieldValueIndex::FieldValueIndex(const Value& v, Select s)
|
||||
{
|
||||
auto selection = s.getSelection();
|
||||
if (not selection.empty()) {
|
||||
std::transform(selection.begin(), selection.end(), std::inserter(index, index.end()),
|
||||
[](const std::set<Value::Field>::value_type& f) {
|
||||
return std::make_pair(f, FieldValue {});
|
||||
});
|
||||
} else {
|
||||
index.clear();
|
||||
for (size_t f = 1 ; f < static_cast<int>(Value::Field::COUNT) ; ++f)
|
||||
index[static_cast<Value::Field>(f)] = {};
|
||||
}
|
||||
for (const auto& fvp : index) {
|
||||
const auto& f = fvp.first;
|
||||
switch (f) {
|
||||
case Value::Field::Id:
|
||||
index[f] = {f, v.id};
|
||||
break;
|
||||
case Value::Field::ValueType:
|
||||
index[f] = {f, v.type};
|
||||
break;
|
||||
case Value::Field::OwnerPk:
|
||||
index[f] = {f, v.owner ? v.owner->getId() : InfoHash() };
|
||||
break;
|
||||
case Value::Field::SeqNum:
|
||||
index[f] = {f, v.seq};
|
||||
break;
|
||||
case Value::Field::UserType:
|
||||
index[f] = {f, Blob {v.user_type.begin(), v.user_type.end()}};
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FieldValueIndex::containedIn(const FieldValueIndex& other) const {
|
||||
if (index.size() > other.index.size())
|
||||
return false;
|
||||
for (const auto& field : index) {
|
||||
auto other_field = other.index.find(field.first);
|
||||
if (other_field == other.index.end())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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:
|
||||
os << "Id:" << std::hex << v->second.getInt();
|
||||
break;
|
||||
case Value::Field::ValueType:
|
||||
os << "ValueType:" << v->second.getInt();
|
||||
break;
|
||||
case Value::Field::OwnerPk:
|
||||
os << "Owner:" << v->second.getHash().toString();
|
||||
break;
|
||||
case Value::Field::SeqNum:
|
||||
os << "Seq:" << v->second.getInt();
|
||||
break;
|
||||
case Value::Field::UserType: {
|
||||
auto ut = v->second.getBlob();
|
||||
os << "UserType:" << std::string(ut.begin(), ut.end());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
os << (std::next(v) != fvi.index.end() ? "," : "");
|
||||
}
|
||||
return os << "]";
|
||||
}
|
||||
|
||||
void
|
||||
FieldValueIndex::msgpack_unpack_fields(const std::set<Value::Field>& fields, const msgpack::object& o, unsigned offset)
|
||||
{
|
||||
index.clear();
|
||||
|
||||
unsigned j = 0;
|
||||
for (const auto& field : fields) {
|
||||
auto& field_value = o.via.array.ptr[offset+(j++)];
|
||||
switch (field) {
|
||||
case Value::Field::Id:
|
||||
case Value::Field::ValueType:
|
||||
case Value::Field::SeqNum:
|
||||
index[field] = FieldValue(field, field_value.as<uint64_t>());
|
||||
break;
|
||||
case Value::Field::OwnerPk:
|
||||
index[field] = FieldValue(field, field_value.as<InfoHash>());
|
||||
break;
|
||||
case Value::Field::UserType:
|
||||
index[field] = FieldValue(field, field_value.as<Blob>());
|
||||
break;
|
||||
default:
|
||||
throw msgpack::type_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void trim_str(std::string& str) {
|
||||
auto first = std::min(str.size(), str.find_first_not_of(" "));
|
||||
auto last = std::min(str.size(), str.find_last_not_of(" "));
|
||||
str = str.substr(first, last - first + 1);
|
||||
}
|
||||
|
||||
Select::Select(const std::string& q_str) {
|
||||
std::istringstream q_iss {q_str};
|
||||
std::string token {};
|
||||
q_iss >> token;
|
||||
|
||||
if (token == "SELECT" or token == "select") {
|
||||
q_iss >> token;
|
||||
std::istringstream fields {token};
|
||||
|
||||
while (std::getline(fields, token, ',')) {
|
||||
trim_str(token);
|
||||
if (token == "id")
|
||||
field(Value::Field::Id);
|
||||
else if (token == "value_type")
|
||||
field(Value::Field::ValueType);
|
||||
else if (token == "owner_pk")
|
||||
field(Value::Field::OwnerPk);
|
||||
if (token == "seq")
|
||||
field(Value::Field::SeqNum);
|
||||
else if (token == "user_type")
|
||||
field(Value::Field::UserType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Where::Where(const std::string& q_str) {
|
||||
std::istringstream q_iss {q_str};
|
||||
std::string token {};
|
||||
q_iss >> token;
|
||||
if (token == "WHERE" or token == "where") {
|
||||
std::getline(q_iss, token);
|
||||
std::istringstream restrictions {token};
|
||||
while (std::getline(restrictions, token, ',')) {
|
||||
trim_str(token);
|
||||
std::istringstream eq_ss {token};
|
||||
std::string field_str, value_str;
|
||||
std::getline(eq_ss, field_str, '=');
|
||||
trim_str(field_str);
|
||||
std::getline(eq_ss, value_str, '=');
|
||||
trim_str(value_str);
|
||||
|
||||
if (not value_str.empty()) {
|
||||
uint64_t v = 0;
|
||||
std::string s {};
|
||||
std::istringstream convert {value_str};
|
||||
convert >> v;
|
||||
if (convert.failbit
|
||||
and 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;
|
||||
if (field_str == "id")
|
||||
id(v);
|
||||
else if (field_str == "value_type")
|
||||
valueType(v);
|
||||
else if (field_str == "owner_pk")
|
||||
owner(InfoHash(s));
|
||||
else if (field_str == "seq")
|
||||
seq(v);
|
||||
else if (field_str == "user_type")
|
||||
userType(s);
|
||||
else
|
||||
throw std::invalid_argument(Query::QUERY_PARSE_ERROR + " (WHERE) wrong token near: " + field_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Query::msgpack_unpack(const msgpack::object& o)
|
||||
{
|
||||
if (o.type != msgpack::type::MAP)
|
||||
throw msgpack::type_error();
|
||||
|
||||
auto rfilters = findMapValue(o, "w"); /* unpacking filters */
|
||||
if (rfilters)
|
||||
where.msgpack_unpack(*rfilters);
|
||||
else
|
||||
throw msgpack::type_error();
|
||||
|
||||
auto rfield_selector = findMapValue(o, "s"); /* unpacking field selectors */
|
||||
if (rfield_selector)
|
||||
select.msgpack_unpack(*rfield_selector);
|
||||
else
|
||||
throw msgpack::type_error();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool subset(std::vector<T> fds, std::vector<T> qfds)
|
||||
{
|
||||
for (auto& fd : fds) {
|
||||
auto correspondance = std::find_if(qfds.begin(), qfds.end(), [&fd](T& _vfd) { return fd == _vfd; });
|
||||
if (correspondance == qfds.end())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Select::isSatisfiedBy(const Select& os) const {
|
||||
/* empty, means all values are selected. */
|
||||
if (fieldSelection_.empty() and not os.fieldSelection_.empty())
|
||||
return false;
|
||||
else
|
||||
return subset(fieldSelection_, os.fieldSelection_);
|
||||
}
|
||||
|
||||
bool Where::isSatisfiedBy(const Where& ow) const {
|
||||
return subset(ow.filters_, filters_);
|
||||
}
|
||||
|
||||
bool Query::isSatisfiedBy(const Query& q) const {
|
||||
return none or (where.isSatisfiedBy(q.where) and select.isSatisfiedBy(q.select));
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& s, const dht::Select& select) {
|
||||
s << "SELECT " << (select.fieldSelection_.empty() ? "*" : "");
|
||||
for (auto fs = select.fieldSelection_.begin() ; fs != select.fieldSelection_.end() ; ++fs) {
|
||||
switch (fs->getField()) {
|
||||
case Value::Field::Id:
|
||||
s << "id";
|
||||
break;
|
||||
case Value::Field::ValueType:
|
||||
s << "value_type";
|
||||
break;
|
||||
case Value::Field::UserType:
|
||||
s << "user_type";
|
||||
break;
|
||||
case Value::Field::OwnerPk:
|
||||
s << "owner_public_key";
|
||||
break;
|
||||
case Value::Field::SeqNum:
|
||||
s << "seq";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
s << (std::next(fs) != select.fieldSelection_.end() ? "," : "");
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& s, const dht::Where& where) {
|
||||
if (not where.filters_.empty()) {
|
||||
s << "WHERE ";
|
||||
for (auto f = where.filters_.begin() ; f != where.filters_.end() ; ++f) {
|
||||
switch (f->getField()) {
|
||||
case Value::Field::Id:
|
||||
s << "id=" << f->getInt();
|
||||
break;
|
||||
case Value::Field::ValueType:
|
||||
s << "value_type=" << f->getInt();
|
||||
break;
|
||||
case Value::Field::OwnerPk:
|
||||
s << "owner_pk_hash=" << f->getHash().toString();
|
||||
break;
|
||||
case Value::Field::SeqNum:
|
||||
s << "seq=" << f->getInt();
|
||||
break;
|
||||
case Value::Field::UserType: {
|
||||
auto b = f->getBlob();
|
||||
s << "user_type=" << std::string {b.begin(), b.end()};
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
s << (std::next(f) != where.filters_.end() ? "," : "");
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Loaded 100 of 105 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user