mirror of
https://github.com/savoirfairelinux/opendht.git
synced 2026-11-08 20:37:23 +00:00
Compare commits
149
Commits
v2
...
optional_index
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8399996d9 | ||
|
|
c9cbced0c8 | ||
|
|
2cf2112aec | ||
|
|
012f6ed2f6 | ||
|
|
fcac10c4c8 | ||
|
|
f2b692c716 | ||
|
|
22ef53acef | ||
|
|
2af70e6d50 | ||
|
|
63c9c607bd | ||
|
|
cab0813b17 | ||
|
|
06cc490ee4 | ||
|
|
6821988ddf | ||
|
|
8cce9fcb85 | ||
|
|
56caae019c | ||
|
|
f5afcbf62b | ||
|
|
0957d30e86 | ||
|
|
3a632b1544 | ||
|
|
2c0f4a897c | ||
|
|
be6a5671ed | ||
|
|
61ac297b1b | ||
|
|
da867dd07c | ||
|
|
962249ec75 | ||
|
|
c03ba57895 | ||
|
|
e2224fac5a | ||
|
|
48aaaf1e5d | ||
|
|
80ee256712 | ||
|
|
e274ffd83b | ||
|
|
bd9496e2ba | ||
|
|
4d4ba2be96 | ||
|
|
680e30be9b | ||
|
|
233301c3f3 | ||
|
|
0db21772c1 | ||
|
|
e6a2d96622 | ||
|
|
e5d83d47be | ||
|
|
3d77c76a4d | ||
|
|
043d5a7c7c | ||
|
|
636eb1972d | ||
|
|
27a250d2c0 | ||
|
|
02f4c81f2a | ||
|
|
0420ebcce3 | ||
|
|
15e3696b88 | ||
|
|
ca693a6ac1 | ||
|
|
1d6a307b6b | ||
|
|
227cca883c | ||
|
|
ef82a7ecba | ||
|
|
6e9f2cda79 | ||
|
|
10b58c5ce7 | ||
|
|
ab731cd7d0 | ||
|
|
a0d8f4734a | ||
|
|
237859d8c3 | ||
|
|
24dec0682f | ||
|
|
4b46f6a821 | ||
|
|
37a9a3d9da | ||
|
|
75cca66d7e | ||
|
|
762a1c7ad2 | ||
|
|
d19ba2cb41 | ||
|
|
557323deff | ||
|
|
29ffbf6aa7 | ||
|
|
aaa577d9e3 | ||
|
|
333c44dbd3 | ||
|
|
8243bc4c3f | ||
|
|
b1a9742889 | ||
|
|
d2f91af946 | ||
|
|
5adc5b4c94 | ||
|
|
f9d48cd15a | ||
|
|
e7d8bc4275 | ||
|
|
507326cebc | ||
|
|
b24832e0e1 | ||
|
|
6cdcd183b4 | ||
|
|
eb750f5b7f | ||
|
|
5967c89c23 | ||
|
|
4a83b36dcf | ||
|
|
eb1f9d64e1 | ||
|
|
cb72c104fd | ||
|
|
5fde50be4a | ||
|
|
1c16e6f38d | ||
|
|
6790d1ec5c | ||
|
|
4dc1847978 | ||
|
|
3a61692618 | ||
|
|
6f9afac0a5 | ||
|
|
b17c7c2965 | ||
|
|
6d238836ad | ||
|
|
5115fab9e6 | ||
|
|
93498ecf0b | ||
|
|
72d790953a | ||
|
|
72b556a09b | ||
|
|
747f86f8f4 | ||
|
|
161769f4ec | ||
|
|
9948079c97 | ||
|
|
607e5eeccf | ||
|
|
111ac57b6f | ||
|
|
153641dde5 | ||
|
|
113bebabd4 | ||
|
|
b549055034 | ||
|
|
7d7410b472 | ||
|
|
d7aaa4ae56 | ||
|
|
5a049f89f3 | ||
|
|
65e88f2fa6 | ||
|
|
4430b1e616 | ||
|
|
e2c534df5a | ||
|
|
2ca2835c9b | ||
|
|
bbe922dd7d | ||
|
|
38ad5b9ef2 | ||
|
|
67ba435ba6 | ||
|
|
6413571301 | ||
|
|
0dccae68e6 | ||
|
|
92320cef59 | ||
|
|
bf4e838e20 | ||
|
|
88b4233bda | ||
|
|
e9eb97ea9a | ||
|
|
0e865976f9 | ||
|
|
87dc080bbf | ||
|
|
9abc88f378 | ||
|
|
834d62e6e9 | ||
|
|
61a148e83f | ||
|
|
8e43775394 | ||
|
|
8344138bc8 | ||
|
|
dc66b3847a | ||
|
|
d608da4307 | ||
|
|
7ddd6629e3 | ||
|
|
20abc3e495 | ||
|
|
c4ba8670e4 | ||
|
|
a3348c85d6 | ||
|
|
ca465d7159 | ||
|
|
1413d32ee6 | ||
|
|
58fdfa9452 | ||
|
|
6f641073ae | ||
|
|
32153b11ab | ||
|
|
e33ac02351 | ||
|
|
dc99919338 | ||
|
|
fcaaa01b2d | ||
|
|
8cd3ff9fc4 | ||
|
|
0a130c7045 | ||
|
|
eb9f314356 | ||
|
|
a401aea132 | ||
|
|
41ebbe21d4 | ||
|
|
6378840d33 | ||
|
|
64f0aa1532 | ||
|
|
63366722a9 | ||
|
|
cd17858ae1 | ||
|
|
f4ebbc6651 | ||
|
|
464339822b | ||
|
|
fe09146609 | ||
|
|
a789ffe7ae | ||
|
|
476fbb1160 | ||
|
|
4501217c21 | ||
|
|
207675a181 | ||
|
|
cf6447dc80 | ||
|
|
f69d6426bd |
No files matched your search
@@ -48,6 +48,11 @@ Makefile.in
|
||||
|
||||
# Python
|
||||
*.pyc
|
||||
python/opendht.cpp
|
||||
python/setup.py
|
||||
|
||||
# Doxygen
|
||||
doc/Doxyfile
|
||||
|
||||
# git backup files
|
||||
*.orig
|
||||
|
||||
+58
-4
@@ -5,13 +5,67 @@ services:
|
||||
|
||||
language: cpp
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- OPENDHT_TEST_JOB="opendht.classic"
|
||||
- OPENDHT_TEST_JOB="opendht.llvm"
|
||||
- OPENDHT_TEST_JOB="opendht.proxyserver"
|
||||
- OPENDHT_TEST_JOB="opendht.proxyclient"
|
||||
- OPENDHT_TEST_JOB="opendht.proxyserverpush"
|
||||
- OPENDHT_TEST_JOB="opendht.proxyclientpush"
|
||||
- OPENDHT_TEST_JOB="opendht.all"
|
||||
|
||||
before_install:
|
||||
- docker pull aberaud/opendht-deps
|
||||
- docker pull aberaud/opendht-deps-llvm
|
||||
- |
|
||||
# non llvm builds
|
||||
if [[ "$OPENDHT_TEST_JOB" != *"opendht.llvm"* ]]; then
|
||||
docker pull aberaud/opendht-deps;
|
||||
if [[ "$OPENDHT_TEST_JOB" != *"opendht.classic"* ]]; then
|
||||
docker build -t opendht-deps-proxy -f docker/DockerfileDepsProxy .;
|
||||
fi
|
||||
fi
|
||||
|
||||
- |
|
||||
# classic build
|
||||
if [[ "$OPENDHT_TEST_JOB" == *"opendht.llvm"* ]]; then
|
||||
docker pull aberaud/opendht-deps-llvm
|
||||
fi
|
||||
|
||||
script:
|
||||
- docker build -f docker/DockerfileTravis .
|
||||
- docker build -f docker/DockerfileTravisLlvm .
|
||||
- |
|
||||
# classic build
|
||||
if [[ "$OPENDHT_TEST_JOB" == *"opendht.classic"* ]]; then
|
||||
docker build -t opendht -f docker/DockerfileTravis .;
|
||||
fi
|
||||
|
||||
- |
|
||||
# proxy builds
|
||||
if [[ "$OPENDHT_TEST_JOB" != *"opendht.llvm"* ]] && [[ "$OPENDHT_TEST_JOB" != *"opendht.classic"* ]]; then
|
||||
docker build -t opendht-proxy -f docker/DockerfileTravisProxy .;
|
||||
options='';
|
||||
if [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyserver"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyserverpush"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.all"* ]]; then
|
||||
options+='-DOPENDHT_PROXY_SERVER=ON ';
|
||||
else
|
||||
options+='-DOPENDHT_PROXY_SERVER=OFF ';
|
||||
fi
|
||||
if [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyclient"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyclientpush"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.all"* ]]; then
|
||||
options+='-DOPENDHT_PROXY_CLIENT=ON ';
|
||||
else
|
||||
options+='-DOPENDHT_PROXY_CLIENT=OFF ';
|
||||
fi
|
||||
if [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyserverpush"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyclientpush"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.all"* ]]; then
|
||||
options+='-DOPENDHT_PUSH_NOTIFICATIONS=ON ';
|
||||
else
|
||||
options+='-DOPENDHT_PUSH_NOTIFICATIONS=OFF ';
|
||||
fi
|
||||
docker run opendht-proxy /bin/sh -c "cd /root/opendht && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=ON -DOPENDHT_LTO=ON $options .. && make -j8 && make install";
|
||||
fi
|
||||
|
||||
- |
|
||||
# llvm build
|
||||
if [[ "$OPENDHT_TEST_JOB" == *"opendht.llvm"* ]]; then
|
||||
docker build -f docker/DockerfileTravisLlvm .
|
||||
fi
|
||||
|
||||
notifications:
|
||||
email:
|
||||
|
||||
+69
-6
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required (VERSION 3.1)
|
||||
project (opendht)
|
||||
set (opendht_VERSION_MAJOR 1)
|
||||
set (opendht_VERSION_MINOR 4.0)
|
||||
set (opendht_VERSION_MINOR 6.0)
|
||||
set (opendht_VERSION ${opendht_VERSION_MAJOR}.${opendht_VERSION_MINOR})
|
||||
set (PACKAGE_VERSION ${opendht_VERSION})
|
||||
set (VERSION "${opendht_VERSION}")
|
||||
@@ -16,6 +16,11 @@ option (OPENDHT_SYSTEMD "Install systemd module" OFF)
|
||||
option (OPENDHT_ARGON2 "Use included argon2 sources" OFF)
|
||||
option (OPENDHT_LTO "Build with LTO" OFF)
|
||||
option (OPENDHT_SANITIZE "Build with address sanitizer and stack protector" OFF)
|
||||
option (OPENDHT_PROXY_SERVER "Enable DHT proxy server, use Restbed and jsoncpp" OFF)
|
||||
option (OPENDHT_PUSH_NOTIFICATIONS "Enable push notifications support" OFF)
|
||||
option (OPENDHT_PROXY_SERVER_IDENTITY "Allow clients to use the node identity" OFF)
|
||||
option (OPENDHT_PROXY_CLIENT "Enable DHT proxy client, use Restbed and jsoncpp" OFF)
|
||||
option (OPENDHT_INDEX "Build DHT indexation feature" OFF)
|
||||
|
||||
find_package(Doxygen)
|
||||
option (OPENDHT_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" ${DOXYGEN_FOUND})
|
||||
@@ -41,6 +46,10 @@ if (NOT OPENDHT_ARGON2)
|
||||
set(OPENDHT_ARGON2 ON)
|
||||
endif()
|
||||
endif ()
|
||||
if (OPENDHT_PROXY_SERVER OR OPENDHT_PROXY_CLIENT)
|
||||
find_package(Restbed REQUIRED)
|
||||
pkg_search_module(Jsoncpp jsoncpp)
|
||||
endif()
|
||||
|
||||
# Build flags
|
||||
set (CMAKE_CXX_STANDARD 11)
|
||||
@@ -77,7 +86,14 @@ endif ()
|
||||
if (Nettle_INCLUDE_DIRS)
|
||||
include_directories (SYSTEM "${Nettle_INCLUDE_DIRS}")
|
||||
endif ()
|
||||
if (RESTBED_INCLUDE)
|
||||
include_directories (SYSTEM "${RESTBED_INCLUDE}")
|
||||
endif ()
|
||||
if (Jsoncpp_INCLUDE_DIRS)
|
||||
include_directories (SYSTEM "${Jsoncpp_INCLUDE_DIRS}")
|
||||
endif ()
|
||||
link_directories (${Nettle_LIBRARY_DIRS})
|
||||
link_directories (${Jsoncpp_LIBRARY_DIRS})
|
||||
include_directories (
|
||||
./
|
||||
include/
|
||||
@@ -107,6 +123,7 @@ list (APPEND opendht_SOURCES
|
||||
src/storage.h
|
||||
src/listener.h
|
||||
src/search.h
|
||||
src/net.h
|
||||
src/parsed_message.h
|
||||
src/request.h
|
||||
src/callbacks.cpp
|
||||
@@ -115,7 +132,6 @@ list (APPEND opendht_SOURCES
|
||||
src/network_engine.cpp
|
||||
src/securedht.cpp
|
||||
src/dhtrunner.cpp
|
||||
src/indexation/pht.cpp
|
||||
src/log.cpp
|
||||
)
|
||||
|
||||
@@ -130,20 +146,67 @@ list (APPEND opendht_HEADERS
|
||||
include/opendht/node.h
|
||||
include/opendht/value.h
|
||||
include/opendht/dht.h
|
||||
include/opendht/dht_interface.h
|
||||
include/opendht/callbacks.h
|
||||
include/opendht/routing_table.h
|
||||
include/opendht/node_cache.h
|
||||
include/opendht/network_engine.h
|
||||
include/opendht/net.h
|
||||
include/opendht/scheduler.h
|
||||
include/opendht/rate_limiter.h
|
||||
include/opendht/securedht.h
|
||||
include/opendht/log.h
|
||||
include/opendht/log_enable.h
|
||||
include/opendht.h
|
||||
include/opendht/indexation/pht.h
|
||||
)
|
||||
|
||||
if (OPENDHT_INDEX)
|
||||
list (APPEND opendht_SOURCES src/indexation/pht.cpp)
|
||||
list (APPEND opendht_HEADERS include/opendht/indexation/pht.h)
|
||||
add_definitions(-DOPENDHT_INDEXATION)
|
||||
endif()
|
||||
|
||||
if (OPENDHT_PROXY_SERVER)
|
||||
add_definitions(-DOPENDHT_PROXY_SERVER=true)
|
||||
if (OPENDHT_PROXY_SERVER_IDENTITY)
|
||||
add_definitions(-DOPENDHT_PROXY_SERVER_IDENTITY=true)
|
||||
else ()
|
||||
add_definitions(-DOPENDHT_PROXY_SERVER_IDENTITY=false)
|
||||
endif()
|
||||
list (APPEND opendht_HEADERS
|
||||
include/opendht/dht_proxy_server.h
|
||||
)
|
||||
list (APPEND opendht_SOURCES
|
||||
src/dht_proxy_server.cpp
|
||||
)
|
||||
if (OPENDHT_PUSH_NOTIFICATIONS)
|
||||
add_definitions(-DOPENDHT_PUSH_NOTIFICATIONS=true)
|
||||
else ()
|
||||
add_definitions(-DOPENDHT_PUSH_NOTIFICATIONS=false)
|
||||
endif ()
|
||||
else ()
|
||||
add_definitions(-DENABLE_PROXY_SERVER=false)
|
||||
endif ()
|
||||
|
||||
if (OPENDHT_PROXY_CLIENT)
|
||||
add_definitions(-DOPENDHT_PROXY_CLIENT=true)
|
||||
list (APPEND opendht_HEADERS
|
||||
include/opendht/dht_proxy_client.h
|
||||
)
|
||||
list (APPEND opendht_SOURCES
|
||||
src/dht_proxy_client.cpp
|
||||
)
|
||||
else ()
|
||||
add_definitions(-DOPENDHT_PROXY_CLIENT=false)
|
||||
endif ()
|
||||
|
||||
if (OPENDHT_PROXY_SERVER OR OPENDHT_PROXY_CLIENT)
|
||||
list (APPEND opendht_SOURCES
|
||||
src/base64.h
|
||||
src/base64.cpp
|
||||
)
|
||||
endif ()
|
||||
|
||||
|
||||
if(OPENDHT_ARGON2)
|
||||
# make sure argon2 submodule is up to date and initialized
|
||||
message("Initializing Argon2 submodule")
|
||||
@@ -177,7 +240,7 @@ if (OPENDHT_STATIC)
|
||||
target_link_libraries(opendht-static ${argon2_LIBRARIES})
|
||||
target_include_directories(opendht-static SYSTEM PRIVATE ${argon2_INCLUDE_DIRS})
|
||||
endif ()
|
||||
target_link_libraries(opendht-static ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES})
|
||||
target_link_libraries(opendht-static ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} ${Restbed_LIBRARIES} ${Jsoncpp_LIBRARIES})
|
||||
install (TARGETS opendht-static DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht)
|
||||
endif ()
|
||||
|
||||
@@ -198,7 +261,7 @@ if (OPENDHT_SHARED)
|
||||
target_link_libraries(opendht PRIVATE ${argon2_LIBRARIES})
|
||||
target_include_directories(opendht SYSTEM PRIVATE ${argon2_INCLUDE_DIRS})
|
||||
endif ()
|
||||
target_link_libraries(opendht PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES})
|
||||
target_link_libraries(opendht PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} ${Restbed_LIBRARIES} ${Jsoncpp_LIBRARIES})
|
||||
install (TARGETS opendht DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht)
|
||||
endif ()
|
||||
|
||||
|
||||
+14
-3
@@ -19,11 +19,14 @@
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="wingetopt.c" />
|
||||
<ClCompile Include="..\src\base64.cpp" />
|
||||
<ClCompile Include="..\src\callbacks.cpp" />
|
||||
<ClCompile Include="..\src\crypto.cpp" />
|
||||
<ClCompile Include="..\src\default_types.cpp" />
|
||||
<ClCompile Include="..\src\dht.cpp" />
|
||||
<ClCompile Include="..\src\dhtrunner.cpp" />
|
||||
<ClCompile Include="..\src\dht_proxy_server.cpp" />
|
||||
<ClCompile Include="..\src\indexation\pht.cpp" />
|
||||
<ClCompile Include="..\src\infohash.cpp" />
|
||||
<ClCompile Include="..\src\log.cpp" />
|
||||
@@ -36,12 +39,16 @@
|
||||
<ClCompile Include="..\src\value.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="unistd.h" />
|
||||
<ClInclude Include="wingetopt.h" />
|
||||
<ClInclude Include="..\include\opendht.h" />
|
||||
<ClInclude Include="..\include\opendht\callbacks.h" />
|
||||
<ClInclude Include="..\include\opendht\crypto.h" />
|
||||
<ClInclude Include="..\include\opendht\def.h" />
|
||||
<ClInclude Include="..\include\opendht\default_types.h" />
|
||||
<ClInclude Include="..\include\opendht\dht.h" />
|
||||
<ClInclude Include="..\include\opendht\dhtrunner.h" />
|
||||
<ClInclude Include="..\include\opendht\dht_proxy_server.h" />
|
||||
<ClInclude Include="..\include\opendht\indexation\pht.h" />
|
||||
<ClInclude Include="..\include\opendht\infohash.h" />
|
||||
<ClInclude Include="..\include\opendht\log.h" />
|
||||
@@ -50,14 +57,18 @@
|
||||
<ClInclude Include="..\include\opendht\node.h" />
|
||||
<ClInclude Include="..\include\opendht\node_cache.h" />
|
||||
<ClInclude Include="..\include\opendht\rate_limiter.h" />
|
||||
<ClInclude Include="..\include\opendht\request.h" />
|
||||
<ClInclude Include="..\include\opendht\rng.h" />
|
||||
<ClInclude Include="..\include\opendht\routing_table.h" />
|
||||
<ClInclude Include="..\include\opendht\scheduler.h" />
|
||||
<ClInclude Include="..\include\opendht\securedht.h" />
|
||||
<ClInclude Include="..\include\opendht\sockaddr.h" />
|
||||
<ClInclude Include="..\include\opendht\utils.h" />
|
||||
<ClInclude Include="..\include\opendht\value.h" />
|
||||
<ClInclude Include="..\src\base64.h" />
|
||||
<ClInclude Include="..\src\listener.h" />
|
||||
<ClInclude Include="..\src\net.h" />
|
||||
<ClInclude Include="..\src\parsed_message.h" />
|
||||
<ClInclude Include="..\src\request.h" />
|
||||
<ClInclude Include="..\src\search.h" />
|
||||
<ClInclude Include="..\src\storage.h" />
|
||||
</ItemGroup>
|
||||
@@ -211,9 +222,9 @@ copy ..\include\opendht.h $(OutDir)\include\</Command>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)contrib\build\include;$(ProjectDir)..\include;$(ProjectDir)..\include\opendht;$(ProjectDir)contrib\build\msgpack-c\include;$(ProjectDir)contrib\build\argon2\include</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)contrib\build\include;$(ProjectDir)..\include;$(ProjectDir)..\include\opendht;$(ProjectDir)contrib\build\msgpack-c\include;$(ProjectDir)contrib\build\argon2\include;$(ProjectDir)..\..\include;$(ProjectDir)..\..\argon2\include</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4800;4101;4267;4244;4503;</DisableSpecificWarnings>
|
||||
<DisableSpecificWarnings>4804;4800;4101;4267;4244;4503;4273;</DisableSpecificWarnings>
|
||||
<AdditionalOptions>-D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions)</AdditionalOptions>
|
||||
<ProgramDataBaseFileName>$(OutDir)\lib\x64\$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
|
||||
@@ -31,9 +31,6 @@
|
||||
<ClCompile Include="..\src\node_cache.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\indexation\pht.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\routing_table.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -46,80 +43,102 @@
|
||||
<ClCompile Include="..\src\value.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\base64.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\dht_proxy_server.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\indexation\pht.cpp">
|
||||
<Filter>Source Files\indexation</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="wingetopt.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\include\opendht\callbacks.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\crypto.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\default_types.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\dht.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\dhtrunner.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\infohash.h">
|
||||
<Filter>Header Files</Filter>
|
||||
<ClInclude Include="..\include\opendht\def.h" />
|
||||
<ClInclude Include="..\include\opendht\sockaddr.h" />
|
||||
<ClInclude Include="..\src\net.h" />
|
||||
<ClInclude Include="..\src\parsed_message.h" />
|
||||
<ClInclude Include="..\src\base64.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\listener.h">
|
||||
<Filter>Header Files</Filter>
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\log.h">
|
||||
<Filter>Header Files</Filter>
|
||||
<ClInclude Include="..\src\request.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\log_enable.h">
|
||||
<Filter>Header Files</Filter>
|
||||
<ClInclude Include="..\src\search.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\network_engine.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\node.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\node_cache.h">
|
||||
<Filter>Header Files</Filter>
|
||||
<ClInclude Include="..\src\storage.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\indexation\pht.h">
|
||||
<Filter>Header Files</Filter>
|
||||
<ClInclude Include="..\include\opendht\callbacks.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\crypto.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\default_types.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\dht.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\dht_proxy_server.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\dhtrunner.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\infohash.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\log.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\log_enable.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\network_engine.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\node.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\node_cache.h">
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\rate_limiter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\request.h">
|
||||
<Filter>Header Files</Filter>
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\rng.h">
|
||||
<Filter>Header Files</Filter>
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\routing_table.h">
|
||||
<Filter>Header Files</Filter>
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\scheduler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\search.h">
|
||||
<Filter>Header Files</Filter>
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\securedht.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\storage.h">
|
||||
<Filter>Header Files</Filter>
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\utils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\value.h">
|
||||
<Filter>Header Files</Filter>
|
||||
<Filter>Header Files\opendht</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\opendht\indexation\pht.h">
|
||||
<Filter>Header Files\opendht\indexation</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="unistd.h" />
|
||||
<ClInclude Include="wingetopt.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
@@ -128,5 +147,14 @@
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{d1ab5bfe-3ab1-45ee-9324-b4b071887668}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\indexation">
|
||||
<UniqueIdentifier>{939134d3-d5a2-4458-bca8-a10ea9042ea2}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\opendht">
|
||||
<UniqueIdentifier>{0b46e2d0-4874-404c-bae3-fae8d49b85b8}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\opendht\indexation">
|
||||
<UniqueIdentifier>{03bd5139-2ae5-41b0-a77b-944814e1b2b6}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,24 +1,24 @@
|
||||
<img src="https://raw.githubusercontent.com/savoirfairelinux/opendht/master/resources/opendht_logo_512.png" width="100" align="right">
|
||||
<br>
|
||||
<h1 style="margin-top:10px">
|
||||
<a id="user-content-opendht-" class="anchor" href="/savoirfairelinux/opendht/blob/master/README.md#opendht-" aria-hidden="true"></a>OpenDHT
|
||||
<a id="user-content-opendht-" class="anchor" href="/savoirfairelinux/opendht/blob/master/README.md#opendht-" aria-hidden="true"></a>OpenDHT
|
||||
</h1>
|
||||
|
||||
A lightweight C++11 Distributed Hash Table implementation originally based on https://github.com/jech/dht by Juliusz Chroboczek.
|
||||
A lightweight C++11 Distributed Hash Table implementation.
|
||||
|
||||
* Light and fast C++11 Kademlia DHT library.
|
||||
* Light and fast C++11 Kademlia DHT library
|
||||
* Distributed shared key->value data-store
|
||||
* Clean and powerful distributed map API with storage of arbitrary binary values of up to 56 KB.
|
||||
* Optional public key cryptography layer providing data signature and encryption (using GnuTLS).
|
||||
* IPv4 and IPv6 support.
|
||||
* Python binding.
|
||||
* Clean and powerful distributed map API with storage of arbitrary binary values of up to 56 KB
|
||||
* Public key cryptography layer providing optional data signature and encryption (using GnuTLS)
|
||||
* IPv4 and IPv6 support
|
||||
* Python binding
|
||||
|
||||
## Documentation
|
||||
See the wiki: <https://github.com/savoirfairelinux/opendht/wiki>
|
||||
|
||||
#### How-to build and install
|
||||
|
||||
Build instructions : <https://github.com/savoirfairelinux/opendht/wiki/Build-the-library>
|
||||
Build instructions: <https://github.com/savoirfairelinux/opendht/wiki/Build-the-library>
|
||||
|
||||
#### How-to build a simple client app
|
||||
```bash
|
||||
@@ -91,7 +91,9 @@ for r in results:
|
||||
- msgpack-c 1.2+, used for data serialization.
|
||||
- GnuTLS 3.3+, used for cryptographic operations.
|
||||
- Nettle 2.4+, a GnuTLS dependency for crypto.
|
||||
- Build tested with GCC 4.8+ (GNU/Linux, Android, Windows with MinGW), Clang/LLVM (Linux, macOS).
|
||||
- (optional) restbed used for the REST API. commit fb84213e170bc171fecd825a8e47ed9f881a12cd (https://github.com/AmarOk1412/restbed/tree/async_read_until)
|
||||
- (optional) jsoncpp 1.7.4-3+, used for the REST API.
|
||||
- Build tested with GCC 5.2+ (GNU/Linux, Windows with MinGW), Clang/LLVM (GNU/Linux, Android, macOS, iOS).
|
||||
- Build tested with Microsoft Visual Studio 2015
|
||||
|
||||
## Contact
|
||||
@@ -99,7 +101,7 @@ for r in results:
|
||||
IRC: join us on Freenode at [`#opendht`](https://webchat.freenode.net/?channels=%23opendht).
|
||||
|
||||
## Licence
|
||||
Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
Copyright (C) 2014-2018 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.
|
||||
|
||||
@@ -107,7 +109,9 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
|
||||
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.
|
||||
## Acknowledgements
|
||||
This project was originally based on https://github.com/jech/dht by Juliusz Chroboczek.
|
||||
It is independent from another project called OpenDHT (Sean Rhea. Ph.D. Thesis, 2005), now extinct.
|
||||
|
||||
## Donations
|
||||
We gratefully accept Bitcoin donations to support OpenDHT development at: `bitcoin:3EykSd1An888efq4Bq3KaV3hJ3JQ4FPnwm`.
|
||||
@@ -0,0 +1,17 @@
|
||||
find_path (RESTBED_INCLUDE restbed
|
||||
HINTS
|
||||
"/usr/include"
|
||||
"/usr/local/include"
|
||||
"/opt/local/include"
|
||||
)
|
||||
|
||||
if (RESTBED_INCLUDE)
|
||||
message(STATUS "${green}Found Restbed: ${RESTBED_INCLUDE}")
|
||||
else()
|
||||
message(FATAL_ERROR "${red}Failed to locate Restbed.}")
|
||||
endif()
|
||||
|
||||
if (RESTBED_INCLUDE)
|
||||
set(RESTBED_FOUND TRUE)
|
||||
set(Restbed_LIBRARIES restbed)
|
||||
endif()
|
||||
+56
-1
@@ -1,6 +1,6 @@
|
||||
dnl define macros
|
||||
m4_define([opendht_major_version], 1)
|
||||
m4_define([opendht_minor_version], 4)
|
||||
m4_define([opendht_minor_version], 6)
|
||||
m4_define([opendht_patch_version], 0)
|
||||
m4_define([opendht_version],
|
||||
[opendht_major_version.opendht_minor_version.opendht_patch_version])
|
||||
@@ -32,6 +32,13 @@ AS_IF([test "x$enable_logs" != "xno"], [
|
||||
AC_DEFINE([OPENDHT_LOG], [false], [Define if DHT logs are enabled])
|
||||
])
|
||||
|
||||
dnl Check for indexation
|
||||
AC_ARG_ENABLE([indexation], [AS_HELP_STRING([--enable-indexation], [Enable DHT indexation])])
|
||||
AM_CONDITIONAL(ENABLE_INDEXATION, test x$enable_indexation == xyes)
|
||||
AS_IF([test "x$enable_indexation" = "xyes"], [
|
||||
AC_DEFINE([OPENDHT_INDEXATION], [1], [Define if DHT indexation is enabled])
|
||||
])
|
||||
|
||||
dnl Check for Doxygen
|
||||
AC_ARG_ENABLE([doc], AS_HELP_STRING([--enable-doc], [Enable documentation generation (doxygen)]))
|
||||
AS_IF([test "x$enable_doc" = "xyes"], [
|
||||
@@ -128,6 +135,54 @@ AM_COND_IF([ENABLE_TOOLS], [
|
||||
])
|
||||
])
|
||||
|
||||
AC_ARG_ENABLE([proxy_server], AS_HELP_STRING([--enable-proxy-server], [Enable proxy server ability]), proxy_server=yes, proxy_server=no)
|
||||
AM_CONDITIONAL(ENABLE_PROXY_SERVER, test x$proxy_server == xyes)
|
||||
AC_ARG_ENABLE([push_notifications], AS_HELP_STRING([--enable-push-notifications], [Enable push notifications support]), push_notifications=yes, push_notifications=no)
|
||||
AM_CONDITIONAL(ENABLE_PUSH_NOTIFICATIONS, test x$push_notifications == xyes)
|
||||
AC_ARG_ENABLE([proxy_server_identity], AS_HELP_STRING([--enable-proxy-server-identity],
|
||||
[Enable proxy server ability]), proxy_server_identity=yes, proxy_server_identity=no)
|
||||
AM_CONDITIONAL(ENABLE_PROXY_SERVER_IDENTITY, test x$proxy_server_identity == xyes)
|
||||
AC_ARG_ENABLE([proxy_client], AS_HELP_STRING([--enable-proxy-client], [Enable proxy client ability]), proxy_client=yes, proxy_client=no)
|
||||
AM_CONDITIONAL(ENABLE_PROXY_CLIENT, test x$proxy_client == xyes)
|
||||
|
||||
AM_COND_IF([ENABLE_PROXY_SERVER], [
|
||||
AC_CHECK_LIB(restbed, exit,, AC_MSG_ERROR([Missing restbed files]))
|
||||
PKG_CHECK_MODULES([Jsoncpp], [jsoncpp >= 1.7.2])
|
||||
CPPFLAGS+=" -DOPENDHT_PROXY_SERVER=true -ljsoncpp -lrestbed"
|
||||
], [
|
||||
CPPFLAGS+=" -DOPENDHT_PROXY_SERVER=false"
|
||||
])
|
||||
|
||||
AM_COND_IF([ENABLE_PUSH_NOTIFICATIONS], [
|
||||
CPPFLAGS+=" -DOPENDHT_PUSH_NOTIFICATIONS=true"
|
||||
], [
|
||||
CPPFLAGS+=" -DOPENDHT_PUSH_NOTIFICATIONS=false"
|
||||
])
|
||||
|
||||
AC_ARG_ENABLE([proxy_server_identity], AS_HELP_STRING([--enable-proxy-server-identity],
|
||||
[Enable proxy server ability]), proxy_server_identity=yes, proxy_server_identity=no)
|
||||
AM_CONDITIONAL(ENABLE_PROXY_SERVER_IDENTITY, test x$proxy_server_identity == xyes -a x$proxy_server == xyes)
|
||||
AM_COND_IF([ENABLE_PROXY_SERVER_IDENTITY], [
|
||||
CPPFLAGS+=" -DOPENDHT_PROXY_SERVER_IDENTITY=true"
|
||||
], [
|
||||
CPPFLAGS+=" -DOPENDHT_PROXY_SERVER_IDENTITY=false"
|
||||
])
|
||||
|
||||
AM_COND_IF([ENABLE_PROXY_CLIENT], [
|
||||
CPPFLAGS+=" -DOPENDHT_PROXY_CLIENT=true"
|
||||
], [
|
||||
CPPFLAGS+=" -DOPENDHT_PROXY_CLIENT=false"
|
||||
])
|
||||
|
||||
AM_CONDITIONAL(PROXY_CLIENT_OR_SERVER, test x$proxy_client == xyes | test x$proxy_server == xyes)
|
||||
AM_COND_IF([PROXY_CLIENT_OR_SERVER], [
|
||||
AC_CHECK_LIB(restbed, exit,, AC_MSG_ERROR([Missing restbed files]))
|
||||
PKG_CHECK_MODULES([Jsoncpp], [jsoncpp >= 1.7.4])
|
||||
CPPFLAGS="${CPPFLAGS} ${Jsoncpp_CFLAGS}"
|
||||
LDFLAGS="${LDFLAGS} ${Jsoncpp_LIBS} -lrestbed"
|
||||
], [])
|
||||
|
||||
|
||||
AC_CONFIG_FILES([doc/Doxyfile doc/Makefile])
|
||||
|
||||
dnl Configure setup.py if we build the python module
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
FROM aberaud/opendht-deps
|
||||
|
||||
# install jsoncpp
|
||||
RUN apt-get install libjsoncpp-dev -y
|
||||
|
||||
# install restbed dependencies
|
||||
RUN sed -i -e 's/archive.ubuntu.com\|security.ubuntu.com/old-releases.ubuntu.com/g' /etc/apt/sources.list # LTS... avoid 404 for libasio-dev
|
||||
RUN apt-get update -y
|
||||
RUN apt-get install libasio-dev -y
|
||||
|
||||
# build restbed from sources
|
||||
RUN git clone --recursive https://github.com/corvusoft/restbed.git
|
||||
WORKDIR restbed/build
|
||||
RUN cmake -DBUILD_TESTS=NO -DBUILD_EXAMPLES=NO -DBUILD_SSL=NO -DBUILD_SHARED=YES -DCMAKE_INSTALL_PREFIX=/usr ..
|
||||
RUN make -j 8 install
|
||||
RUN mv /usr/library/librestbed* /usr/lib/
|
||||
@@ -1,6 +0,0 @@
|
||||
FROM aberaud/opendht-deps-llvm
|
||||
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
RUN git clone https://github.com/savoirfairelinux/opendht.git \
|
||||
&& cd opendht && mkdir build && cd build \
|
||||
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On && make -j8 && make install \
|
||||
&& cd ../.. && rm -rf opendht
|
||||
@@ -2,5 +2,4 @@ FROM aberaud/opendht-deps
|
||||
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
COPY . /root/opendht
|
||||
RUN cd /root/opendht && mkdir build && cd build \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On -DOPENDHT_LTO=On .. && make -j8 && make install \
|
||||
&& cd ../.. && rm -rf opendht
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On -DOPENDHT_LTO=On .. && make -j8 && make install
|
||||
@@ -2,5 +2,4 @@ FROM aberaud/opendht-deps-llvm
|
||||
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
COPY . /root/opendht
|
||||
RUN cd /root/opendht && mkdir build && cd build \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On .. && make -j8 && make install \
|
||||
&& cd ../.. && rm -rf opendht
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On .. && make -j8 && make install
|
||||
@@ -0,0 +1,3 @@
|
||||
FROM opendht-deps-proxy
|
||||
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
COPY . /root/opendht
|
||||
@@ -19,6 +19,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "opendht/dhtrunner.h"
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
#include "opendht/dht_proxy_server.h"
|
||||
#endif
|
||||
#include "opendht/log.h"
|
||||
#include "opendht/default_types.h"
|
||||
#ifdef OPENDHT_INDEXATION
|
||||
#include "opendht/indexation/pht.h"
|
||||
#endif
|
||||
@@ -1,7 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -26,6 +27,10 @@
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
#include <json/json.h>
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
|
||||
namespace dht {
|
||||
|
||||
struct Node;
|
||||
@@ -40,13 +45,21 @@ enum class NodeStatus {
|
||||
};
|
||||
|
||||
struct OPENDHT_PUBLIC NodeStats {
|
||||
unsigned good_nodes,
|
||||
dubious_nodes,
|
||||
cached_nodes,
|
||||
incoming_nodes;
|
||||
unsigned table_depth;
|
||||
unsigned good_nodes {0},
|
||||
dubious_nodes {0},
|
||||
cached_nodes {0},
|
||||
incoming_nodes {0};
|
||||
unsigned table_depth {0};
|
||||
unsigned getKnownNodes() const { return good_nodes + dubious_nodes; }
|
||||
std::string toString() const;
|
||||
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
|
||||
/**
|
||||
* Build a json object from a NodeStats
|
||||
*/
|
||||
Json::Value toJson() const;
|
||||
NodeStats() {};
|
||||
explicit NodeStats(const Json::Value& v);
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -387,6 +387,7 @@ struct OPENDHT_PUBLIC Certificate {
|
||||
/** Read certificate alternative names */
|
||||
std::vector<std::pair<NameType, std::string>> getAltNames() const;
|
||||
|
||||
std::chrono::system_clock::time_point getActivation() const;
|
||||
std::chrono::system_clock::time_point getExpiration() const;
|
||||
|
||||
/**
|
||||
@@ -601,6 +602,8 @@ OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name = "dhtnode",
|
||||
*/
|
||||
OPENDHT_PUBLIC Blob hash(const Blob& data, size_t hash_length = 512/8);
|
||||
|
||||
OPENDHT_PUBLIC void hash(const uint8_t* data, size_t data_length, uint8_t* hash, size_t hash_length);
|
||||
|
||||
/**
|
||||
* Generates an encryption key from a text password,
|
||||
* making the key longer to bruteforce.
|
||||
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
static Value::Filter getFilter() {
|
||||
return Value::Filter::chain(
|
||||
BaseClass::getFilter(),
|
||||
[](const Value& v){ return v.recipient != InfoHash(); }
|
||||
[](const Value& v) { return static_cast<bool>(v.recipient); }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+10
-30
@@ -1,7 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -26,7 +27,7 @@
|
||||
#include "scheduler.h"
|
||||
#include "routing_table.h"
|
||||
#include "callbacks.h"
|
||||
#include "log_enable.h"
|
||||
#include "dht_interface.h"
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
@@ -58,22 +59,16 @@ struct LocalListener;
|
||||
* Must be given open UDP sockets and ::periodic must be
|
||||
* called regularly.
|
||||
*/
|
||||
class OPENDHT_PUBLIC Dht {
|
||||
class OPENDHT_PUBLIC Dht final : public DhtInterface {
|
||||
public:
|
||||
|
||||
// [[deprecated]]
|
||||
using NodeExport = dht::NodeExport;
|
||||
|
||||
// [[deprecated]]
|
||||
using Status = NodeStatus;
|
||||
|
||||
Dht();
|
||||
|
||||
/**
|
||||
* Initialise the Dht with two open sockets (for IPv4 and IP6)
|
||||
* and an ID for the node.
|
||||
*/
|
||||
Dht(int s, int s6, Config config);
|
||||
Dht(const int& s, const int& s6, Config config);
|
||||
virtual ~Dht();
|
||||
|
||||
/**
|
||||
@@ -103,18 +98,6 @@ public:
|
||||
*/
|
||||
bool isRunning(sa_family_t af = 0) const;
|
||||
|
||||
/**
|
||||
* Enable or disable logging of DHT internal messages
|
||||
*/
|
||||
void setLoggers(LogMethod error = NOLOG, LogMethod warn = NOLOG, LogMethod debug = NOLOG);
|
||||
|
||||
/**
|
||||
* Only print logs related to the given InfoHash (if given), or disable filter (if zeroes).
|
||||
*/
|
||||
void setLogFilter(const InfoHash& f) {
|
||||
DHT_LOG.setFilter(f);
|
||||
}
|
||||
|
||||
virtual void registerType(const ValueType& type) {
|
||||
types[type.id] = type;
|
||||
}
|
||||
@@ -248,7 +231,7 @@ public:
|
||||
*
|
||||
* @return a token to cancel the listener later.
|
||||
*/
|
||||
virtual size_t listen(const InfoHash&, GetCallback, Value::Filter&&={}, Where&& w = {});
|
||||
virtual size_t listen(const InfoHash&, GetCallback, Value::Filter={}, Where w = {});
|
||||
virtual size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w = {}) {
|
||||
return listen(key, bindGetCb(cb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
@@ -307,10 +290,8 @@ public:
|
||||
|
||||
std::vector<SockAddr> getPublicAddress(sa_family_t family = 0);
|
||||
|
||||
protected:
|
||||
Logger DHT_LOG;
|
||||
bool logFilerEnable_ {};
|
||||
InfoHash logFiler_ {};
|
||||
void pushNotificationReceived(const std::map<std::string, std::string>&) {}
|
||||
void resubscribe(unsigned) {}
|
||||
|
||||
private:
|
||||
|
||||
@@ -339,7 +320,7 @@ private:
|
||||
static constexpr unsigned MAX_HASHES {16384};
|
||||
|
||||
/* The maximum number of searches we keep data about. */
|
||||
static constexpr unsigned MAX_SEARCHES {2048};
|
||||
static constexpr unsigned MAX_SEARCHES {16384};
|
||||
|
||||
static constexpr std::chrono::minutes MAX_STORAGE_MAINTENANCE_EXPIRE_TIME {10};
|
||||
|
||||
@@ -428,7 +409,6 @@ private:
|
||||
|
||||
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
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "infohash.h"
|
||||
#include "log_enable.h"
|
||||
|
||||
namespace dht {
|
||||
|
||||
class OPENDHT_PUBLIC DhtInterface {
|
||||
public:
|
||||
DhtInterface() = default;
|
||||
virtual ~DhtInterface() = default;
|
||||
|
||||
// [[deprecated]]
|
||||
using Status = NodeStatus;
|
||||
// [[deprecated]]
|
||||
using NodeExport = dht::NodeExport;
|
||||
|
||||
/**
|
||||
* Get the current status of the node for the given family.
|
||||
*/
|
||||
virtual NodeStatus getStatus(sa_family_t af) const = 0;
|
||||
virtual NodeStatus getStatus() const = 0;
|
||||
|
||||
/**
|
||||
* Get the ID of the node.
|
||||
*/
|
||||
virtual const InfoHash& getNodeId() const = 0;
|
||||
|
||||
/**
|
||||
* Performs final operations before quitting.
|
||||
*/
|
||||
virtual void shutdown(ShutdownCallback cb) = 0;
|
||||
|
||||
/**
|
||||
* Returns true if the node is running (have access to an open socket).
|
||||
*
|
||||
* af: address family. If non-zero, will return true if the node
|
||||
* is running for the provided family.
|
||||
*/
|
||||
virtual bool isRunning(sa_family_t af = 0) const = 0;
|
||||
|
||||
virtual void registerType(const ValueType& type) = 0;
|
||||
|
||||
virtual const ValueType& getType(ValueType::Id type_id) const = 0;
|
||||
|
||||
/**
|
||||
* Insert a node in the main routing table.
|
||||
* The node is not pinged, so this should be
|
||||
* used to bootstrap efficiently from previously known nodes.
|
||||
*/
|
||||
virtual void insertNode(const InfoHash& id, const SockAddr&) = 0;
|
||||
virtual void insertNode(const InfoHash& id, const sockaddr* sa, socklen_t salen) = 0;
|
||||
virtual void insertNode(const NodeExport& n) = 0;
|
||||
|
||||
virtual void pingNode(const sockaddr*, socklen_t, DoneCallbackSimple&& cb={}) = 0;
|
||||
|
||||
virtual time_point periodic(const uint8_t *buf, size_t buflen, const SockAddr&) = 0;
|
||||
virtual time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen) = 0;
|
||||
|
||||
/**
|
||||
* Get a value by searching on all available protocols (IPv4, IPv6),
|
||||
* and call the provided get callback when values are found at key.
|
||||
* The operation will start as soon as the node is connected to the network.
|
||||
* @param cb a function called when new values are found on the network.
|
||||
* It should return false to stop the operation.
|
||||
* @param donecb a function called when the operation is complete.
|
||||
cb and donecb won't be called again afterward.
|
||||
* @param f a filter function used to prefilter values.
|
||||
*/
|
||||
virtual void get(const InfoHash& key, GetCallback cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) = 0;
|
||||
virtual void get(const InfoHash& key, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f={}, Where&& w = {}) = 0;
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) = 0;
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}, Where&& w = {}) = 0;
|
||||
|
||||
/**
|
||||
* Similar to Dht::get, but sends a Query to filter data remotely.
|
||||
* @param key the key for which to query data for.
|
||||
* @param cb a function called when new values are found on the network.
|
||||
* It should return false to stop the operation.
|
||||
* @param done_cb a function called when the operation is complete.
|
||||
cb and done_cb won't be called again afterward.
|
||||
* @param q a query used to filter values on the remotes before they send a
|
||||
* response.
|
||||
*/
|
||||
virtual void query(const InfoHash& key, QueryCallback cb, DoneCallback done_cb = {}, Query&& q = {}) = 0;
|
||||
virtual void query(const InfoHash& key, QueryCallback cb, DoneCallbackSimple done_cb = {}, Query&& q = {}) = 0;
|
||||
|
||||
/**
|
||||
* Get locally stored data for the given hash.
|
||||
*/
|
||||
virtual std::vector<Sp<Value>> getLocal(const InfoHash& key, Value::Filter f = Value::AllFilter()) const = 0;
|
||||
|
||||
/**
|
||||
* Get locally stored data for the given key and value id.
|
||||
*/
|
||||
virtual Sp<Value> getLocalById(const InfoHash& key, Value::Id vid) const = 0;
|
||||
|
||||
/**
|
||||
* Announce a value on all available protocols (IPv4, IPv6).
|
||||
*
|
||||
* The operation will start as soon as the node is connected to the network.
|
||||
* The done callback will be called once, when the first announce succeeds, or fails.
|
||||
*/
|
||||
virtual void put(const InfoHash& key,
|
||||
Sp<Value>,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) = 0;
|
||||
virtual void put(const InfoHash& key,
|
||||
const Sp<Value>& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) = 0;
|
||||
virtual void put(const InfoHash& key,
|
||||
Value&& v,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) = 0;
|
||||
virtual void put(const InfoHash& key,
|
||||
Value&& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) = 0;
|
||||
|
||||
/**
|
||||
* Get data currently being put at the given hash.
|
||||
*/
|
||||
virtual std::vector<Sp<Value>> getPut(const InfoHash&) = 0;
|
||||
|
||||
/**
|
||||
* Get data currently being put at the given hash with the given id.
|
||||
*/
|
||||
virtual Sp<Value> getPut(const InfoHash&, const Value::Id&) = 0;
|
||||
|
||||
/**
|
||||
* Stop any put/announce operation at the given location,
|
||||
* for the value with the given id.
|
||||
*/
|
||||
virtual bool cancelPut(const InfoHash&, const Value::Id&) = 0;
|
||||
|
||||
/**
|
||||
* Listen on the network for any changes involving a specified hash.
|
||||
* The node will register to receive updates from relevent nodes when
|
||||
* new values are added or removed.
|
||||
*
|
||||
* @return a token to cancel the listener later.
|
||||
*/
|
||||
virtual size_t listen(const InfoHash&, GetCallback, Value::Filter={}, Where w = {}) = 0;
|
||||
virtual size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w = {}) = 0;
|
||||
|
||||
virtual bool cancelListen(const InfoHash&, size_t token) = 0;
|
||||
|
||||
/**
|
||||
* Inform the DHT of lower-layer connectivity changes.
|
||||
* This will cause the DHT to assume a public IP address change.
|
||||
* The DHT will recontact neighbor nodes, re-register for listen ops etc.
|
||||
*/
|
||||
virtual void connectivityChanged(sa_family_t) = 0;
|
||||
virtual void connectivityChanged() = 0;
|
||||
|
||||
/**
|
||||
* Get the list of good nodes for local storage saving purposes
|
||||
* The list is ordered to minimize the back-to-work delay.
|
||||
*/
|
||||
virtual std::vector<NodeExport> exportNodes() = 0;
|
||||
|
||||
virtual std::vector<ValuesExport> exportValues() const = 0;
|
||||
virtual void importValues(const std::vector<ValuesExport>&) = 0;
|
||||
|
||||
virtual NodeStats getNodesStats(sa_family_t af) const = 0;
|
||||
|
||||
virtual std::string getStorageLog() const = 0;
|
||||
virtual std::string getStorageLog(const InfoHash&) const = 0;
|
||||
|
||||
virtual std::string getRoutingTablesLog(sa_family_t) const = 0;
|
||||
virtual std::string getSearchesLog(sa_family_t) const = 0;
|
||||
virtual std::string getSearchLog(const InfoHash&, sa_family_t af = AF_UNSPEC) const = 0;
|
||||
|
||||
virtual void dumpTables() const = 0;
|
||||
virtual std::vector<unsigned> getNodeMessageStats(bool in = false) = 0;
|
||||
|
||||
/**
|
||||
* Set the in-memory storage limit in bytes
|
||||
*/
|
||||
virtual void setStorageLimit(size_t limit = DEFAULT_STORAGE_LIMIT) = 0;
|
||||
|
||||
/**
|
||||
* Returns the total memory usage of stored values and the number
|
||||
* of stored values.
|
||||
*/
|
||||
virtual std::pair<size_t, size_t> getStoreSize() const = 0;
|
||||
|
||||
virtual std::vector<SockAddr> getPublicAddress(sa_family_t family = 0) = 0;
|
||||
|
||||
/**
|
||||
* Enable or disable logging of DHT internal messages
|
||||
*/
|
||||
virtual void setLoggers(LogMethod error = NOLOG, LogMethod warn = NOLOG, LogMethod debug = NOLOG)
|
||||
{
|
||||
DHT_LOG.DEBUG = debug;
|
||||
DHT_LOG.WARN = warn;
|
||||
DHT_LOG.ERR = error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only print logs related to the given InfoHash (if given), or disable filter (if zeroes).
|
||||
*/
|
||||
virtual void setLogFilter(const InfoHash& f)
|
||||
{
|
||||
DHT_LOG.setFilter(f);
|
||||
}
|
||||
|
||||
virtual void setPushNotificationToken(const std::string&) {};
|
||||
|
||||
/**
|
||||
* Call linked callback with a push notification
|
||||
* @param notification to process
|
||||
*/
|
||||
virtual void pushNotificationReceived(const std::map<std::string, std::string>& data) = 0;
|
||||
/**
|
||||
* Refresh a listen via a token
|
||||
* @param token
|
||||
*/
|
||||
virtual void resubscribe(unsigned token) = 0;
|
||||
|
||||
protected:
|
||||
bool logFilerEnable_ {};
|
||||
InfoHash logFiler_ {};
|
||||
Logger DHT_LOG;
|
||||
};
|
||||
|
||||
} // namespace dht
|
||||
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* Copyright (C) 2016-2018 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
* Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
#include "callbacks.h"
|
||||
#include "def.h"
|
||||
#include "dht_interface.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
namespace restbed {
|
||||
class Request;
|
||||
}
|
||||
|
||||
namespace Json {
|
||||
class Value;
|
||||
}
|
||||
|
||||
namespace dht {
|
||||
|
||||
class OPENDHT_PUBLIC DhtProxyClient final : public DhtInterface {
|
||||
public:
|
||||
|
||||
DhtProxyClient() : scheduler(DHT_LOG) {}
|
||||
|
||||
explicit DhtProxyClient(std::function<void()> loopSignal, const std::string& serverHost, const std::string& pushClientId = "");
|
||||
|
||||
virtual void setPushNotificationToken(const std::string& token) {
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
deviceKey_ = token;
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual ~DhtProxyClient();
|
||||
|
||||
/**
|
||||
* Get the ID of the node.
|
||||
*/
|
||||
inline const InfoHash& getNodeId() const { return myid; }
|
||||
|
||||
/**
|
||||
* Get the current status of the node for the given family.
|
||||
*/
|
||||
NodeStatus getStatus(sa_family_t af) const;
|
||||
NodeStatus getStatus() const {
|
||||
return std::max(getStatus(AF_INET), getStatus(AF_INET6));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs final operations before quitting.
|
||||
*/
|
||||
void shutdown(ShutdownCallback cb);
|
||||
|
||||
/**
|
||||
* Returns true if the node is running (have access to an open socket).
|
||||
*
|
||||
* af: address family. If non-zero, will return true if the node
|
||||
* is running for the provided family.
|
||||
*/
|
||||
bool isRunning(sa_family_t af = 0) const;
|
||||
|
||||
/**
|
||||
* Get a value by asking the proxy and call the provided get callback when
|
||||
* values are found at key.
|
||||
* The operation will start as soon as the node is connected to the network.
|
||||
* @param cb a function called when new values are found on the network.
|
||||
* It should return false to stop the operation.
|
||||
* @param donecb a function called when the operation is complete.
|
||||
cb and donecb won't be called again afterward.
|
||||
* @param f a filter function used to prefilter values.
|
||||
*/
|
||||
virtual void get(const InfoHash& key, GetCallback cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {});
|
||||
virtual void get(const InfoHash& key, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f={}, Where&& w = {}) {
|
||||
get(key, cb, bindDoneCb(donecb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) {
|
||||
get(key, bindGetCb(cb), donecb, std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}, Where&& w = {}) {
|
||||
get(key, bindGetCb(cb), bindDoneCb(donecb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
|
||||
void get(const InfoHash& key, const GetCallback& cb, DoneCallback donecb, const Value::Filter& filterChain);
|
||||
|
||||
/**
|
||||
* Announce a value on all available protocols (IPv4, IPv6).
|
||||
*
|
||||
* The operation will start as soon as the node is connected to the network.
|
||||
* The done callback will be called once, when the first announce succeeds, or fails.
|
||||
* NOTE: For now, created parameter is ignored.
|
||||
*/
|
||||
void put(const InfoHash& key,
|
||||
Sp<Value>,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false);
|
||||
void put(const InfoHash& key,
|
||||
const Sp<Value>& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false)
|
||||
{
|
||||
put(key, v, bindDoneCb(cb), created, permanent);
|
||||
}
|
||||
|
||||
void put(const InfoHash& key,
|
||||
Value&& v,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false)
|
||||
{
|
||||
put(key, std::make_shared<Value>(std::move(v)), cb, created, permanent);
|
||||
}
|
||||
void put(const InfoHash& key,
|
||||
Value&& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false)
|
||||
{
|
||||
put(key, std::forward<Value>(v), bindDoneCb(cb), created, permanent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param af the socket family
|
||||
* @return node stats from the proxy
|
||||
*/
|
||||
NodeStats getNodesStats(sa_family_t af) const;
|
||||
|
||||
/**
|
||||
* @param family the socket family
|
||||
* @return public address
|
||||
*/
|
||||
std::vector<SockAddr> getPublicAddress(sa_family_t family = 0);
|
||||
|
||||
/**
|
||||
* Listen on the network for any changes involving a specified hash.
|
||||
* The node will register to receive updates from relevent nodes when
|
||||
* new values are added or removed.
|
||||
*
|
||||
* @return a token to cancel the listener later.
|
||||
*/
|
||||
virtual size_t listen(const InfoHash&, GetCallback, Value::Filter={}, Where={});
|
||||
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);
|
||||
|
||||
/**
|
||||
* Call linked callback with a push notification
|
||||
* @param notification to process
|
||||
*/
|
||||
void pushNotificationReceived(const std::map<std::string, std::string>& notification);
|
||||
/**
|
||||
* Refresh a listen via a token
|
||||
* @param token
|
||||
*/
|
||||
void resubscribe(const unsigned token);
|
||||
|
||||
time_point periodic(const uint8_t*, size_t, const SockAddr&);
|
||||
time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen) {
|
||||
return periodic(buf, buflen, SockAddr(from, fromlen));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Similar to Dht::get, but sends a Query to filter data remotely.
|
||||
* @param key the key for which to query data for.
|
||||
* @param cb a function called when new values are found on the network.
|
||||
* It should return false to stop the operation.
|
||||
* @param done_cb a function called when the operation is complete.
|
||||
cb and done_cb won't be called again afterward.
|
||||
* @param q a query used to filter values on the remotes before they send a
|
||||
* response.
|
||||
*/
|
||||
virtual void query(const InfoHash& /*key*/, QueryCallback /*cb*/, DoneCallback /*done_cb*/ = {}, Query&& /*q*/ = {}) { }
|
||||
virtual void query(const InfoHash& key, QueryCallback cb, DoneCallbackSimple done_cb = {}, Query&& q = {}) {
|
||||
query(key, cb, bindDoneCb(done_cb), std::forward<Query>(q));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data currently being put at the given hash.
|
||||
*/
|
||||
std::vector<Sp<Value>> getPut(const InfoHash&) { return {}; }
|
||||
|
||||
/**
|
||||
* Get data currently being put at the given hash with the given id.
|
||||
*/
|
||||
Sp<Value> getPut(const InfoHash&, const Value::Id&) { return {}; }
|
||||
|
||||
/**
|
||||
* Stop any put/announce operation at the given location,
|
||||
* for the value with the given id.
|
||||
*/
|
||||
bool cancelPut(const InfoHash&, const Value::Id&) { return false; }
|
||||
|
||||
void pingNode(const sockaddr*, socklen_t, DoneCallbackSimple&& /*cb*/={}) { }
|
||||
|
||||
/**
|
||||
* NOTE: The following methods will not be implemented because the
|
||||
* DhtProxyClient doesn't have any storage nor synchronization process
|
||||
*/
|
||||
void insertNode(const InfoHash&, const SockAddr&) { }
|
||||
void insertNode(const InfoHash&, const sockaddr*, socklen_t) { }
|
||||
void insertNode(const NodeExport&) { }
|
||||
std::pair<size_t, size_t> getStoreSize() const { return {}; }
|
||||
virtual void registerType(const ValueType&) { }
|
||||
const ValueType& getType(ValueType::Id) const { return NO_VALUE; }
|
||||
std::vector<Sp<Value>> getLocal(const InfoHash&, Value::Filter) const { return {}; }
|
||||
Sp<Value> getLocalById(const InfoHash&, Value::Id) const { return {}; }
|
||||
std::vector<NodeExport> exportNodes() { return {}; }
|
||||
std::vector<ValuesExport> exportValues() const { return {}; }
|
||||
void importValues(const std::vector<ValuesExport>&) {}
|
||||
std::string getStorageLog() const { return {}; }
|
||||
std::string getStorageLog(const InfoHash&) const { return {}; }
|
||||
std::string getRoutingTablesLog(sa_family_t) const { return {}; }
|
||||
std::string getSearchesLog(sa_family_t) const { return {}; }
|
||||
std::string getSearchLog(const InfoHash&, sa_family_t) const { return {}; }
|
||||
void dumpTables() const {}
|
||||
std::vector<unsigned> getNodeMessageStats(bool) { return {}; }
|
||||
void setStorageLimit(size_t) {}
|
||||
void connectivityChanged(sa_family_t) {}
|
||||
void connectivityChanged() { }
|
||||
|
||||
private:
|
||||
const ValueType NO_VALUE;
|
||||
|
||||
/**
|
||||
* Start the connection with a server.
|
||||
*/
|
||||
void startProxy();
|
||||
|
||||
/**
|
||||
* Get informations from the proxy node
|
||||
* @return the JSON returned by the proxy
|
||||
*/
|
||||
void getProxyInfos();
|
||||
void onProxyInfos(const Json::Value& val);
|
||||
SockAddr parsePublicAddress(const Json::Value& val);
|
||||
|
||||
void opFailed();
|
||||
|
||||
/**
|
||||
* Initialize statusIpvX_
|
||||
*/
|
||||
void getConnectivityStatus();
|
||||
/**
|
||||
* cancel all Listeners
|
||||
*/
|
||||
void cancelAllListeners();
|
||||
/**
|
||||
* cancel all Operations
|
||||
*/
|
||||
void cancelAllOperations();
|
||||
std::string serverHost_;
|
||||
std::string pushClientId_;
|
||||
|
||||
std::atomic_flag ongoingStatusUpdate_ = ATOMIC_FLAG_INIT;
|
||||
NodeStatus statusIpv4_ {NodeStatus::Disconnected};
|
||||
NodeStatus statusIpv6_ {NodeStatus::Disconnected};
|
||||
NodeStats stats4_ {};
|
||||
NodeStats stats6_ {};
|
||||
SockAddr publicAddress_;
|
||||
|
||||
InfoHash myid {};
|
||||
|
||||
/**
|
||||
* Store listen requests.
|
||||
*/
|
||||
struct Listener
|
||||
{
|
||||
size_t token;
|
||||
std::shared_ptr<restbed::Request> req;
|
||||
std::string key;
|
||||
GetCallback cb;
|
||||
Value::Filter filterChain;
|
||||
std::thread thread;
|
||||
unsigned callbackId;
|
||||
std::shared_ptr<unsigned> pushNotifToken; // NOTE: unused if not using push notifications
|
||||
};
|
||||
std::vector<Listener> listeners_;
|
||||
size_t listener_token_ {0};
|
||||
std::mutex lockListener_;
|
||||
|
||||
/**
|
||||
* Store current put and get requests.
|
||||
*/
|
||||
struct Operation
|
||||
{
|
||||
std::shared_ptr<restbed::Request> req;
|
||||
std::thread thread;
|
||||
std::shared_ptr<std::atomic_bool> finished;
|
||||
};
|
||||
std::vector<Operation> operations_;
|
||||
std::mutex lockOperations_;
|
||||
/**
|
||||
* Callbacks should be executed in the main thread.
|
||||
*/
|
||||
std::vector<std::function<void()>> callbacks_;
|
||||
std::mutex lockCallbacks;
|
||||
|
||||
std::thread statusThread_;
|
||||
mutable std::mutex lockCurrentProxyInfos_;
|
||||
|
||||
Scheduler scheduler;
|
||||
/**
|
||||
* Retrieve if we can connect to the proxy (update statusIpvX_)
|
||||
*/
|
||||
void confirmProxy();
|
||||
Sp<Scheduler::Job> nextProxyConfirmation {};
|
||||
/**
|
||||
* Relaunch LISTEN requests if the client disconnect/reconnect.
|
||||
*/
|
||||
void restartListeners();
|
||||
|
||||
/**
|
||||
* If we want to use push notifications by default.
|
||||
* NOTE: empty by default to avoid to use services like FCM or APN.
|
||||
*/
|
||||
std::string deviceKey_ {};
|
||||
unsigned callbackId_ {0};
|
||||
std::mutex lockCallback_;
|
||||
|
||||
const std::function<void()> loopSignal_;
|
||||
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
void fillBodyToGetToken(std::shared_ptr<restbed::Request> request, unsigned callbackId);
|
||||
#endif // OPENDHT_PUSH_NOTIFICATIONS
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OPENDHT_PROXY_CLIENT
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2018 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "def.h"
|
||||
#include "sockaddr.h"
|
||||
#include "infohash.h"
|
||||
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <restbed>
|
||||
|
||||
namespace Json {
|
||||
class Value;
|
||||
}
|
||||
|
||||
namespace dht {
|
||||
|
||||
class DhtRunner;
|
||||
|
||||
/**
|
||||
* Describes the REST API
|
||||
*/
|
||||
class OPENDHT_PUBLIC DhtProxyServer
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Start the Http server for OpenDHT
|
||||
* @param dht the DhtRunner linked to this proxy server
|
||||
* @param port to listen
|
||||
* @param pushServer where to push notifications
|
||||
* @note if the server fails to start (if port is already used or reserved),
|
||||
* it will fails silently
|
||||
*/
|
||||
DhtProxyServer(std::shared_ptr<DhtRunner> dht, in_port_t port = 8000, const std::string& pushServer = "");
|
||||
virtual ~DhtProxyServer();
|
||||
|
||||
DhtProxyServer(const DhtProxyServer& other) = delete;
|
||||
DhtProxyServer(DhtProxyServer&& other) = delete;
|
||||
DhtProxyServer& operator=(const DhtProxyServer& other) = delete;
|
||||
DhtProxyServer& operator=(DhtProxyServer&& other) = delete;
|
||||
|
||||
/**
|
||||
* Stop the DhtProxyServer
|
||||
*/
|
||||
void stop();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Return the PublicKey id, the node id and node stats
|
||||
* Method: GET "/"
|
||||
* Result: HTTP 200, body: Node infos in JSON format
|
||||
* On error: HTTP 503, body: {"err":"xxxx"}
|
||||
* @param session
|
||||
*/
|
||||
void getNodeInfo(const std::shared_ptr<restbed::Session>& session) const;
|
||||
|
||||
/**
|
||||
* Return Values of an infoHash
|
||||
* Method: GET "/{InfoHash: .*}"
|
||||
* Return: Multiple JSON object in parts. Example:
|
||||
* Value in JSON format\n
|
||||
* Value in JSON format
|
||||
*
|
||||
* On error: HTTP 503, body: {"err":"xxxx"}
|
||||
* @param session
|
||||
*/
|
||||
void get(const std::shared_ptr<restbed::Session>& session) const;
|
||||
|
||||
/**
|
||||
* Listen incoming Values of an infoHash.
|
||||
* Method: LISTEN "/{InfoHash: .*}"
|
||||
* Return: Multiple JSON object in parts. Example:
|
||||
* Value in JSON format\n
|
||||
* Value in JSON format
|
||||
*
|
||||
* On error: HTTP 503, body: {"err":"xxxx"}
|
||||
* @param session
|
||||
*/
|
||||
void listen(const std::shared_ptr<restbed::Session>& session) const;
|
||||
|
||||
/**
|
||||
* Put a value on the DHT
|
||||
* Method: POST "/{InfoHash: .*}"
|
||||
* body = Value to put in JSON
|
||||
* Return: HTTP 200 if success and the value put in JSON
|
||||
* On error: HTTP 503, body: {"err":"xxxx"} if no dht
|
||||
* HTTP 400, body: {"err":"xxxx"} if bad json or HTTP 502 if put fails
|
||||
* @param session
|
||||
*/
|
||||
void put(const std::shared_ptr<restbed::Session>& session) const;
|
||||
|
||||
#if OPENDHT_PROXY_SERVER_IDENTITY
|
||||
/**
|
||||
* Put a value to sign by the proxy on the DHT
|
||||
* Method: SIGN "/{InfoHash: .*}"
|
||||
* body = Value to put in JSON
|
||||
* Return: HTTP 200 if success and the value put in JSON
|
||||
* On error: HTTP 503, body: {"err":"xxxx"} if no dht
|
||||
* HTTP 400, body: {"err":"xxxx"} if bad json
|
||||
* @param session
|
||||
*/
|
||||
void putSigned(const std::shared_ptr<restbed::Session>& session) const;
|
||||
|
||||
/**
|
||||
* Put a value to encrypt by the proxy on the DHT
|
||||
* Method: ENCRYPT "/{hash: .*}"
|
||||
* body = Value to put in JSON + "to":"infoHash"
|
||||
* Return: HTTP 200 if success and the value put in JSON
|
||||
* On error: HTTP 503, body: {"err":"xxxx"} if no dht
|
||||
* HTTP 400, body: {"err":"xxxx"} if bad json
|
||||
* @param session
|
||||
*/
|
||||
void putEncrypted(const std::shared_ptr<restbed::Session>& session) const;
|
||||
#endif // OPENDHT_PROXY_SERVER_IDENTITY
|
||||
|
||||
/**
|
||||
* Return Values of an infoHash filtered by a value id
|
||||
* Method: GET "/{InfoHash: .*}/{ValueId: .*}"
|
||||
* Return: Multiple JSON object in parts. Example:
|
||||
* Value in JSON format\n
|
||||
* Value in JSON format
|
||||
*
|
||||
* On error: HTTP 503, body: {"err":"xxxx"}
|
||||
* @param session
|
||||
*/
|
||||
void getFiltered(const std::shared_ptr<restbed::Session>& session) const;
|
||||
|
||||
/**
|
||||
* Respond allowed Methods
|
||||
* Method: OPTIONS "/{hash: .*}"
|
||||
* Return: HTTP 200 + Allow: allowed methods
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS
|
||||
* @param session
|
||||
*/
|
||||
void handleOptionsMethod(const std::shared_ptr<restbed::Session>& session) const;
|
||||
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
/**
|
||||
* Subscribe to push notifications for an iOS or Android device.
|
||||
* Method: SUBSCRIBE "/{InfoHash: .*}"
|
||||
* Body: {"key": "device_key", (optional)"callback_id":y,
|
||||
* (optional)"isAndroid":false (default true)}"
|
||||
* Return: {"token": x}" where x if a token to save
|
||||
* @note: the listen will timeout after six hours (and send a push notification).
|
||||
* so you need to refresh the operation each six hours.
|
||||
* @note: callback_id is used to add the possibility to have multiple listen
|
||||
* on same hash for same device and must be > 0
|
||||
* @param session
|
||||
*/
|
||||
void subscribe(const std::shared_ptr<restbed::Session>& session) const;
|
||||
/**
|
||||
* Unsubscribe to push notifications for an iOS or Android device.
|
||||
* Method: UNSUBSCRIBE "/{InfoHash: .*}"
|
||||
* Body: {"key": "device_key", "token": x, (optional)"callback_id":y"
|
||||
* where x if the token to cancel
|
||||
* Return: nothing
|
||||
* @note: callback id is used to add the possibility to have multiple listen
|
||||
* on same hash for same device
|
||||
* @param session
|
||||
*/
|
||||
void unsubscribe(const std::shared_ptr<restbed::Session>& session) const;
|
||||
/**
|
||||
* Send a push notification via a gorush push gateway
|
||||
* @param key of the device
|
||||
* @param json, the content to send
|
||||
*/
|
||||
void sendPushNotification(const std::string& key, const Json::Value& json, bool isAndroid) const;
|
||||
#endif //OPENDHT_PUSH_NOTIFICATIONS
|
||||
|
||||
std::thread server_thread {};
|
||||
std::unique_ptr<restbed::Service> service_;
|
||||
std::shared_ptr<DhtRunner> dht_;
|
||||
|
||||
// Handle client quit for listen.
|
||||
// NOTE: can be simplified when we will supports restbed 5.0
|
||||
std::thread listenThread_;
|
||||
struct SessionToHashToken {
|
||||
std::shared_ptr<restbed::Session> session;
|
||||
InfoHash hash;
|
||||
std::future<size_t> token;
|
||||
};
|
||||
mutable std::vector<SessionToHashToken> currentListeners_;
|
||||
mutable std::mutex lockListener_;
|
||||
std::atomic_bool stopListeners {false};
|
||||
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
struct PushListener {
|
||||
std::string pushToken;
|
||||
InfoHash hash;
|
||||
unsigned token;
|
||||
std::future<size_t> internalToken;
|
||||
std::chrono::steady_clock::time_point deadline;
|
||||
bool started {false};
|
||||
unsigned callbackId {0};
|
||||
std::string clientId {};
|
||||
bool isAndroid {true};
|
||||
};
|
||||
mutable std::mutex lockPushListeners_;
|
||||
mutable std::vector<PushListener> pushListeners_;
|
||||
mutable unsigned tokenPushNotif_ {0};
|
||||
#endif //OPENDHT_PUSH_NOTIFICATIONS
|
||||
const std::string pushServer_;
|
||||
|
||||
/**
|
||||
* Remove finished listeners
|
||||
* @param testSession if we remove the listener only if the session is closed
|
||||
*/
|
||||
void removeClosedListeners(bool testSession = true);
|
||||
/**
|
||||
* Launch or remove push listeners if needed
|
||||
*/
|
||||
void handlePushListeners();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
+100
-16
@@ -1,7 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,7 +20,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
//#include "securedht.h"
|
||||
#include "infohash.h"
|
||||
#include "value.h"
|
||||
#include "callbacks.h"
|
||||
@@ -53,6 +53,13 @@ class OPENDHT_PUBLIC DhtRunner {
|
||||
public:
|
||||
typedef std::function<void(NodeStatus, NodeStatus)> StatusCallback;
|
||||
|
||||
struct Config {
|
||||
SecureDhtConfig dht_config;
|
||||
bool threaded;
|
||||
std::string proxy_server;
|
||||
std::string push_node_id;
|
||||
};
|
||||
|
||||
DhtRunner();
|
||||
virtual ~DhtRunner();
|
||||
|
||||
@@ -202,7 +209,7 @@ public:
|
||||
* Insert known nodes to the routing table, without necessarly ping them.
|
||||
* Usefull to restart a node and get things running fast without putting load on the network.
|
||||
*/
|
||||
void bootstrap(const std::vector<std::pair<sockaddr_storage, socklen_t>>& nodes, DoneCallbackSimple&& cb={});
|
||||
void bootstrap(const std::vector<SockAddr>& nodes, DoneCallbackSimple&& cb={});
|
||||
void bootstrap(const SockAddr& addr, DoneCallbackSimple&& cb={});
|
||||
|
||||
/**
|
||||
@@ -294,11 +301,6 @@ public:
|
||||
void registerCertificate(std::shared_ptr<crypto::Certificate> cert);
|
||||
void setLocalCertificateStore(CertificateStoreQuery&& query_method);
|
||||
|
||||
struct Config {
|
||||
SecureDhtConfig dht_config;
|
||||
bool threaded;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param port: Local port to bind. Both IPv4 and IPv6 will be tried (ANY).
|
||||
* @param identity: RSA key pair to use for cryptographic operations.
|
||||
@@ -316,7 +318,9 @@ public:
|
||||
},
|
||||
/*.id = */identity
|
||||
},
|
||||
/*.threaded = */threaded
|
||||
/*.threaded = */threaded,
|
||||
/*.proxy_server = */"",
|
||||
/*.push_node_id = */""
|
||||
});
|
||||
}
|
||||
void run(in_port_t port, Config config);
|
||||
@@ -329,7 +333,7 @@ public:
|
||||
* @param threaded: If false, loop() must be called periodically. Otherwise a thread is launched.
|
||||
* @param cb: Optional callback to receive general state information.
|
||||
*/
|
||||
void run(const sockaddr_in* local4, const sockaddr_in6* local6, Config config);
|
||||
void run(const SockAddr& local4, const SockAddr& local6, Config config);
|
||||
|
||||
/**
|
||||
* Same as @run(sockaddr_in, sockaddr_in6, Identity, bool, StatusCallback), but with string IP addresses and service (port).
|
||||
@@ -347,7 +351,13 @@ public:
|
||||
*/
|
||||
time_point loop() {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
return loop_();
|
||||
time_point wakeup = time_point::min();
|
||||
try {
|
||||
wakeup = loop_();
|
||||
} catch (const dht::SocketException& e) {
|
||||
startNetwork(bound4, bound6);
|
||||
}
|
||||
return wakeup;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -362,6 +372,43 @@ public:
|
||||
*/
|
||||
void join();
|
||||
|
||||
void setProxyServer(const std::string& proxy, const std::string& pushNodeId = "") {
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
if (config_.proxy_server == proxy and config_.push_node_id == pushNodeId)
|
||||
return;
|
||||
config_.proxy_server = proxy;
|
||||
config_.push_node_id = pushNodeId;
|
||||
enableProxy(use_proxy and not config_.proxy_server.empty());
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Start or stop the proxy
|
||||
* @param proxify if we want to use the proxy
|
||||
* @param deviceKey non empty to enable push notifications
|
||||
*/
|
||||
void enableProxy(bool proxify);
|
||||
|
||||
/* Push notification methods */
|
||||
|
||||
/**
|
||||
* Updates the push notification device token
|
||||
*/
|
||||
void setPushNotificationToken(const std::string& token);
|
||||
|
||||
/**
|
||||
* Insert a push notification to process for OpenDHT
|
||||
*/
|
||||
void pushNotificationReceived(const std::map<std::string, std::string>& data) const;
|
||||
/**
|
||||
* Refresh a listen via a token
|
||||
* @param token
|
||||
*/
|
||||
void resubscribe(unsigned token);
|
||||
|
||||
/* Proxy server mothods */
|
||||
void forwardAllMessages(bool forward);
|
||||
|
||||
private:
|
||||
static constexpr std::chrono::seconds BOOTSTRAP_PERIOD {10};
|
||||
|
||||
@@ -373,16 +420,48 @@ private:
|
||||
*/
|
||||
void tryBootstrapContinuously();
|
||||
|
||||
void doRun(const sockaddr_in* sin4, const sockaddr_in6* sin6, SecureDhtConfig config);
|
||||
void startNetwork(const SockAddr sin4, const SockAddr sin6);
|
||||
time_point loop_();
|
||||
|
||||
static std::vector<std::pair<sockaddr_storage, socklen_t>> getAddrInfo(const std::string& host, const std::string& service);
|
||||
|
||||
NodeStatus getStatus() const {
|
||||
return std::max(status4, status6);
|
||||
}
|
||||
|
||||
/** Local DHT instance */
|
||||
std::unique_ptr<SecureDht> dht_;
|
||||
|
||||
/** Proxy client instance */
|
||||
std::unique_ptr<SecureDht> dht_via_proxy_;
|
||||
|
||||
/** true if we are currently using a proxy */
|
||||
std::atomic_bool use_proxy {false};
|
||||
|
||||
/** Current configuration */
|
||||
Config config_;
|
||||
|
||||
/**
|
||||
* reset dht clients
|
||||
*/
|
||||
void resetDht();
|
||||
/**
|
||||
* @return the current active DHT
|
||||
*/
|
||||
SecureDht* activeDht() const;
|
||||
|
||||
/**
|
||||
* Store current listeners and translates global tokens for each client.
|
||||
*/
|
||||
struct Listener {
|
||||
size_t tokenClassicDht;
|
||||
size_t tokenProxyDht;
|
||||
GetCallback gcb;
|
||||
InfoHash hash;
|
||||
Value::Filter f;
|
||||
Where w;
|
||||
};
|
||||
std::map<size_t, Listener> listeners_ {};
|
||||
size_t listener_token_ {1};
|
||||
|
||||
mutable std::mutex dht_mtx {};
|
||||
std::thread dht_thread {};
|
||||
std::condition_variable cv {};
|
||||
@@ -405,14 +484,19 @@ private:
|
||||
std::queue<std::function<void(SecureDht&)>> pending_ops {};
|
||||
std::mutex storage_mtx {};
|
||||
|
||||
std::atomic<bool> running {false};
|
||||
std::atomic_bool running {false};
|
||||
std::atomic_bool running_network {false};
|
||||
|
||||
NodeStatus status4 {NodeStatus::Disconnected},
|
||||
status6 {NodeStatus::Disconnected};
|
||||
StatusCallback statusCb {nullptr};
|
||||
|
||||
int s4 {-1}, s6 {-1};
|
||||
SockAddr bound4 {};
|
||||
SockAddr bound6 {};
|
||||
|
||||
/** Push notification token */
|
||||
std::string pushToken_;
|
||||
};
|
||||
|
||||
}
|
||||
+197
-143
@@ -19,6 +19,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "def.h"
|
||||
#include "rng.h"
|
||||
|
||||
#include <msgpack.hpp>
|
||||
|
||||
@@ -44,133 +45,99 @@ typedef uint16_t in_port_t;
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
// bytes
|
||||
#define HASH_LEN 20u
|
||||
|
||||
namespace dht {
|
||||
|
||||
using byte = uint8_t;
|
||||
|
||||
template <size_t N>
|
||||
class OPENDHT_PUBLIC Hash
|
||||
{
|
||||
public:
|
||||
Hash() {
|
||||
data_.fill(0);
|
||||
}
|
||||
Hash(const msgpack::object& o) {
|
||||
msgpack_unpack(o);
|
||||
}
|
||||
byte* data() {
|
||||
return data_.data();
|
||||
}
|
||||
const byte* data() const {
|
||||
return data_.data();
|
||||
}
|
||||
constexpr size_t size() const { return data_.size(); }
|
||||
explicit operator bool() const { return std::any_of(data_.begin(), data_.end(), [](byte _b) { return _b != 0; }); }
|
||||
bool operator==(Hash const& _c) const { return data_ == _c.data_; }
|
||||
bool operator!=(Hash const& _c) const { return data_ != _c.data_; }
|
||||
bool operator<(Hash const& _c) const { for (unsigned i = 0; i < N; ++i) if (data_[i] < _c.data_[i]) return true; else if (data_[i] > _c.data_[i]) return false; return false; }
|
||||
bool operator>=(Hash const& _c) const { return !operator<(_c); }
|
||||
bool operator<=(Hash const& _c) const { return operator==(_c) || operator<(_c); }
|
||||
bool operator>(Hash const& _c) const { return !operator<=(_c); }
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& pk) const
|
||||
{
|
||||
pk.pack_bin(N);
|
||||
pk.pack_bin_body((char*)data_.data(), N);
|
||||
}
|
||||
void msgpack_unpack(msgpack::object o) {
|
||||
if (o.type != msgpack::type::BIN or o.via.bin.size != N)
|
||||
throw msgpack::type_error();
|
||||
std::copy_n(o.via.bin.ptr, N, data_.data());
|
||||
}
|
||||
|
||||
template <size_t M>
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const Hash<M>& h);
|
||||
|
||||
template <size_t M>
|
||||
OPENDHT_PUBLIC friend std::istream& operator>> (std::istream& s, Hash<M>& h);
|
||||
|
||||
std::string toString() const {
|
||||
std::stringstream ss;
|
||||
ss << *this;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<byte, N> data_;
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
std::ostream& operator<< (std::ostream& s, const Hash<N>& h)
|
||||
{
|
||||
s << std::hex;
|
||||
for (unsigned i=0; i<h.size(); i++)
|
||||
s << std::setfill('0') << std::setw(2) << (unsigned)h.data_[i];
|
||||
s << std::dec;
|
||||
return s;
|
||||
namespace crypto {
|
||||
OPENDHT_PUBLIC void hash(const uint8_t* data, size_t data_length, uint8_t* hash, size_t hash_length);
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
std::istream& operator>> (std::istream& s, Hash<N>& h)
|
||||
{
|
||||
std::array<char, h.size()*2> dat;
|
||||
s.exceptions(std::istream::eofbit | std::istream::failbit);
|
||||
s.read(&(*dat.begin()), dat.size());
|
||||
for (size_t i = 0; i < h.size(); i++) {
|
||||
unsigned res = 0;
|
||||
sscanf(dat.data() + 2*i, "%02x", &res);
|
||||
h[i] = res;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
using h256 = Hash<32>;
|
||||
using PkId = h256;
|
||||
|
||||
/**
|
||||
* Represents an InfoHash.
|
||||
* An InfoHash is a byte array of HASH_LEN bytes.
|
||||
* InfoHashes identify nodes and values in the Dht.
|
||||
*/
|
||||
class OPENDHT_PUBLIC InfoHash final : public std::array<uint8_t, HASH_LEN> {
|
||||
template <size_t N>
|
||||
class OPENDHT_PUBLIC Hash {
|
||||
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) {}
|
||||
InfoHash(const uint8_t* h, size_t h_len) : std::array<uint8_t, HASH_LEN>() {
|
||||
if (h_len < HASH_LEN)
|
||||
fill(0);
|
||||
else
|
||||
std::copy_n(h, HASH_LEN, begin());
|
||||
}
|
||||
using T = std::array<uint8_t, N>;
|
||||
typedef typename T::iterator iterator;
|
||||
typedef typename T::const_iterator const_iterator;
|
||||
|
||||
Hash () {
|
||||
data_.fill(0);
|
||||
}
|
||||
Hash (const uint8_t* h, size_t data_len) {
|
||||
if (data_len < N)
|
||||
data_.fill(0);
|
||||
else
|
||||
std::copy_n(h, N, data_.begin());
|
||||
}
|
||||
/**
|
||||
* Constructor from an hexadecimal string (without "0x").
|
||||
* hex must be at least 2.HASH_LEN characters long.
|
||||
* If too long, only the first 2.HASH_LEN characters are read.
|
||||
*/
|
||||
explicit InfoHash(const std::string& hex);
|
||||
explicit Hash(const std::string& hex);
|
||||
|
||||
InfoHash(const msgpack::object& o) {
|
||||
Hash(const msgpack::object& o) {
|
||||
msgpack_unpack(o);
|
||||
}
|
||||
|
||||
size_t size() const { return data_.size(); }
|
||||
const uint8_t* data() const { return data_.data(); }
|
||||
uint8_t* data() { return data_.data(); }
|
||||
iterator begin() { return data_.begin(); }
|
||||
const_iterator cbegin() const { return data_.cbegin(); }
|
||||
iterator end() { return data_.end(); }
|
||||
const_iterator cend() const { return data_.cend(); }
|
||||
|
||||
bool operator==(const Hash& h) const {
|
||||
auto a = reinterpret_cast<const uint32_t*>(data_.data());
|
||||
auto b = reinterpret_cast<const uint32_t*>(h.data_.data());
|
||||
constexpr unsigned n = N / sizeof(uint32_t);
|
||||
for (unsigned i=0; i < n; i++)
|
||||
if (a[i] != b[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator!=(const Hash& h) const { return !(*this == h); }
|
||||
|
||||
bool operator<(const Hash& o) const {
|
||||
for(unsigned i = 0; i < N; i++) {
|
||||
if(data_[i] != o.data_[i])
|
||||
return data_[i] < o.data_[i];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
auto a = reinterpret_cast<const uint32_t*>(data_.data());
|
||||
auto b = reinterpret_cast<const uint32_t*>(data_.data() + N);
|
||||
for (; a != b; a++) {
|
||||
if (*a)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t& operator[](size_t index) { return data_[index]; }
|
||||
const uint8_t& operator[](size_t index) const { return data_[index]; }
|
||||
|
||||
/**
|
||||
* Find the lowest 1 bit in an id.
|
||||
* Result will allways be lower than 8*HASH_LEN
|
||||
* Result will allways be lower than 8*N
|
||||
*/
|
||||
inline unsigned lowbit() const {
|
||||
int i, j;
|
||||
for(i = HASH_LEN-1; i >= 0; i--)
|
||||
if((*this)[i] != 0)
|
||||
for(i = N-1; i >= 0; i--)
|
||||
if(data_[i] != 0)
|
||||
break;
|
||||
if(i < 0)
|
||||
return -1;
|
||||
for(j = 7; j >= 0; j--)
|
||||
if(((*this)[i] & (0x80 >> j)) != 0)
|
||||
if((data_[i] & (0x80 >> j)) != 0)
|
||||
break;
|
||||
return 8 * i + j;
|
||||
}
|
||||
@@ -179,25 +146,25 @@ public:
|
||||
* Forget about the ``XOR-metric''. An id is just a path from the
|
||||
* root of the tree, so bits are numbered from the start.
|
||||
*/
|
||||
static inline int cmp(const InfoHash& id1, const InfoHash& id2) {
|
||||
return std::memcmp(id1.data(), id2.data(), HASH_LEN);
|
||||
static inline int cmp(const Hash& id1, const Hash& id2) {
|
||||
return std::memcmp(id1.data_.data(), id2.data_.data(), N);
|
||||
}
|
||||
|
||||
/** Find how many bits two ids have in common. */
|
||||
static inline unsigned
|
||||
commonBits(const InfoHash& id1, const InfoHash& id2)
|
||||
commonBits(const Hash& id1, const Hash& id2)
|
||||
{
|
||||
unsigned i, j;
|
||||
uint8_t x;
|
||||
for(i = 0; i < HASH_LEN; i++) {
|
||||
if(id1[i] != id2[i])
|
||||
for(i = 0; i < N; i++) {
|
||||
if(id1.data_[i] != id2.data_[i])
|
||||
break;
|
||||
}
|
||||
|
||||
if(i == HASH_LEN)
|
||||
return 8*HASH_LEN;
|
||||
if(i == N)
|
||||
return 8*N;
|
||||
|
||||
x = id1[i] ^ id2[i];
|
||||
x = id1.data_[i] ^ id2.data_[i];
|
||||
|
||||
j = 0;
|
||||
while((x & 0x80) == 0) {
|
||||
@@ -210,14 +177,14 @@ public:
|
||||
|
||||
/** Determine whether id1 or id2 is closer to this */
|
||||
int
|
||||
xorCmp(const InfoHash& id1, const InfoHash& id2) const
|
||||
xorCmp(const Hash& id1, const Hash& id2) const
|
||||
{
|
||||
for(unsigned i = 0; i < HASH_LEN; i++) {
|
||||
for(unsigned i = 0; i < N; i++) {
|
||||
uint8_t xor1, xor2;
|
||||
if(id1[i] == id2[i])
|
||||
if(id1.data_[i] == id2.data_[i])
|
||||
continue;
|
||||
xor1 = id1[i] ^ (*this)[i];
|
||||
xor2 = id2[i] ^ (*this)[i];
|
||||
xor1 = id1.data_[i] ^ data_[i];
|
||||
xor2 = id2.data_[i] ^ data_[i];
|
||||
if(xor1 < xor2)
|
||||
return -1;
|
||||
else
|
||||
@@ -229,7 +196,7 @@ public:
|
||||
bool
|
||||
getBit(unsigned nbit) const
|
||||
{
|
||||
auto& num = *(cbegin()+(nbit/8));
|
||||
auto& num = *(data_.cbegin()+(nbit/8));
|
||||
unsigned bit = 7 - (nbit % 8);
|
||||
return (num >> bit) & 1;
|
||||
}
|
||||
@@ -237,7 +204,7 @@ public:
|
||||
void
|
||||
setBit(unsigned nbit, bool b)
|
||||
{
|
||||
auto& num = (*this)[nbit/8];
|
||||
auto& num = data_[nbit/8];
|
||||
unsigned bit = 7 - (nbit % 8);
|
||||
num ^= (-b ^ num) & (1 << bit);
|
||||
}
|
||||
@@ -245,45 +212,36 @@ public:
|
||||
double toFloat() const {
|
||||
using D = size_t;
|
||||
double v = 0.;
|
||||
for (size_t i = 0; i < std::min<size_t>(HASH_LEN, sizeof(D)-1); i++)
|
||||
v += *(cbegin()+i) / (double)((D)1 << 8*(i+1));
|
||||
for (size_t i = 0; i < std::min<size_t>(N, sizeof(D)-1); i++)
|
||||
v += *(data_.cbegin()+i) / (double)((D)1 << 8*(i+1));
|
||||
return v;
|
||||
}
|
||||
|
||||
bool operator==(const InfoHash& h) const {
|
||||
auto a = reinterpret_cast<const uint32_t*>(data());
|
||||
auto b = reinterpret_cast<const uint32_t*>(h.data());
|
||||
for (size_t i=0; i<(size()/sizeof(uint32_t)); i++)
|
||||
if (a[i] != b[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator<(const InfoHash& o) const {
|
||||
for(unsigned i = 0; i < HASH_LEN; i++) {
|
||||
if((*this)[i] != o[i])
|
||||
return (*this)[i] < o[i];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline InfoHash get(const std::string& data) {
|
||||
static inline Hash get(const std::string& data) {
|
||||
return get((const uint8_t*)data.data(), data.size());
|
||||
}
|
||||
|
||||
static inline InfoHash get(const std::vector<uint8_t>& data) {
|
||||
static inline Hash get(const std::vector<uint8_t>& data) {
|
||||
return get(data.data(), data.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the hash from a given data buffer of size data_len.
|
||||
*/
|
||||
static InfoHash get(const uint8_t* data, size_t data_len);
|
||||
static Hash get(const uint8_t* data, size_t data_len)
|
||||
{
|
||||
Hash ret;
|
||||
crypto::hash(data, data_len, ret.data(), N);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static InfoHash getRandom();
|
||||
static Hash getRandom();
|
||||
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const InfoHash& h);
|
||||
OPENDHT_PUBLIC friend std::istream& operator>> (std::istream& s, InfoHash& h);
|
||||
template <size_t M>
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const Hash<M>& h);
|
||||
|
||||
template <size_t M>
|
||||
OPENDHT_PUBLIC friend std::istream& operator>> (std::istream& s, Hash<M>& h);
|
||||
|
||||
const char* to_c_str() const;
|
||||
|
||||
@@ -292,19 +250,115 @@ public:
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& pk) const
|
||||
{
|
||||
pk.pack_bin(HASH_LEN);
|
||||
pk.pack_bin_body((char*)data(), HASH_LEN);
|
||||
pk.pack_bin(N);
|
||||
pk.pack_bin_body((char*)data_.data(), N);
|
||||
}
|
||||
|
||||
void msgpack_unpack(msgpack::object o) {
|
||||
if (o.type != msgpack::type::BIN or o.via.bin.size != HASH_LEN)
|
||||
if (o.type != msgpack::type::BIN or o.via.bin.size != N)
|
||||
throw msgpack::type_error();
|
||||
std::copy_n(o.via.bin.ptr, HASH_LEN, data());
|
||||
std::copy_n(o.via.bin.ptr, N, data_.data());
|
||||
}
|
||||
|
||||
private:
|
||||
T data_;
|
||||
void fromString(const char*);
|
||||
};
|
||||
|
||||
static constexpr const InfoHash zeroes {};
|
||||
#define HASH_LEN 20u
|
||||
using InfoHash = Hash<HASH_LEN>;
|
||||
using h256 = Hash<32>;
|
||||
using PkId = h256;
|
||||
|
||||
template <size_t N>
|
||||
std::ostream& operator<< (std::ostream& s, const Hash<N>& h)
|
||||
{
|
||||
s.write(h.to_c_str(), N*2);
|
||||
return s;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
std::istream& operator>> (std::istream& s, Hash<N>& h)
|
||||
{
|
||||
std::array<char, h.size()*2> dat;
|
||||
s.exceptions(std::istream::eofbit | std::istream::failbit);
|
||||
s.read(&(*dat.begin()), dat.size());
|
||||
fromString(dat.data());
|
||||
return s;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
Hash<N>::Hash(const std::string& hex) {
|
||||
if (hex.size() < 2*N)
|
||||
data_.fill(0);
|
||||
else
|
||||
fromString(hex.c_str());
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
void
|
||||
Hash<N>::fromString(const char* in) {
|
||||
auto hex2bin = [](char c) -> uint8_t {
|
||||
if (c >= 'a' and c <= 'f') return 10 + c - 'a';
|
||||
else if (c >= 'A' and c <= 'F') return 10 + c - 'A';
|
||||
else if (c >= '0' and c <= '9') return c - '0';
|
||||
else throw std::domain_error("not an hex character");
|
||||
};
|
||||
try {
|
||||
for (size_t i=0; i<N; i++)
|
||||
data_[i] = (hex2bin(in[2*i]) << 4) | hex2bin(in[2*i+1]);
|
||||
} catch (const std::domain_error&) {
|
||||
data_.fill(0);
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
Hash<N>
|
||||
Hash<N>::getRandom()
|
||||
{
|
||||
Hash h;
|
||||
crypto::random_device rdev;
|
||||
std::uniform_int_distribution<uint32_t> rand_int;
|
||||
auto a = reinterpret_cast<uint32_t*>(h.data());
|
||||
auto b = reinterpret_cast<uint32_t*>(h.data() + h.size());
|
||||
std::generate(a, b, std::bind(rand_int, std::ref(rdev)));
|
||||
return h;
|
||||
}
|
||||
|
||||
struct HexMap : public std::array<std::array<char, 2>, 256> {
|
||||
HexMap() {
|
||||
for (size_t i=0; i<size(); i++) {
|
||||
auto& e = (*this)[i];
|
||||
e[0] = hex_digits[(i >> 4) & 0x0F];
|
||||
e[1] = hex_digits[i & 0x0F];
|
||||
}
|
||||
}
|
||||
private:
|
||||
static constexpr const char* hex_digits = "0123456789abcdef";
|
||||
};
|
||||
|
||||
OPENDHT_PUBLIC extern const HexMap hex_map;
|
||||
|
||||
template <size_t N>
|
||||
const char*
|
||||
Hash<N>::to_c_str() const
|
||||
{
|
||||
thread_local std::array<char, N*2+1> buf;
|
||||
for (size_t i=0; i<N; i++) {
|
||||
auto b = buf.data()+i*2;
|
||||
const auto& m = hex_map[data_[i]];
|
||||
*((uint16_t*)b) = *((uint16_t*)&m);
|
||||
}
|
||||
return buf.data();
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
std::string
|
||||
Hash<N>::toString() const
|
||||
{
|
||||
return std::string(to_c_str(), N*2);
|
||||
}
|
||||
|
||||
const InfoHash zeroes {};
|
||||
|
||||
struct OPENDHT_PUBLIC NodeExport {
|
||||
InfoHash id;
|
||||
@@ -324,7 +378,7 @@ struct OPENDHT_PUBLIC NodeExport {
|
||||
|
||||
void msgpack_unpack(msgpack::object o);
|
||||
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const InfoHash& h);
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const NodeExport& h);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -75,7 +75,7 @@ struct Logger {
|
||||
LogMethod ERR = NOLOG;
|
||||
void setFilter(const InfoHash& f) {
|
||||
filter_ = f;
|
||||
filterEnable_ = filter_ != InfoHash{};
|
||||
filterEnable_ = static_cast<bool>(filter_);
|
||||
}
|
||||
inline void log0(const LogMethod& logger, char const* format, va_list args) const {
|
||||
#if OPENDHT_LOG
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace dht {
|
||||
namespace net {
|
||||
|
||||
struct TransPrefix : public std::array<uint8_t, 2> {
|
||||
TransPrefix(const std::string& str) : std::array<uint8_t, 2>({{(uint8_t)str[0], (uint8_t)str[1]}}) {}
|
||||
static const TransPrefix PING;
|
||||
static const TransPrefix FIND_NODE;
|
||||
static const TransPrefix GET_VALUES;
|
||||
static const TransPrefix ANNOUNCE_VALUES;
|
||||
static const TransPrefix REFRESH;
|
||||
static const TransPrefix LISTEN;
|
||||
};
|
||||
|
||||
/* Transaction-ids are 4-bytes long, with the first two bytes identifying
|
||||
* the kind of request, and the remaining two a sequence number in
|
||||
* host order.
|
||||
*/
|
||||
struct TransId final : public std::array<uint8_t, 4> {
|
||||
static const constexpr uint16_t INVALID {0};
|
||||
|
||||
TransId() { std::fill(begin(), end(), 0); }
|
||||
TransId(const std::array<char, 4>& o) { std::copy(o.begin(), o.end(), begin()); }
|
||||
TransId(const TransPrefix prefix, uint16_t seqno = 0) {
|
||||
std::copy_n(prefix.begin(), prefix.size(), begin());
|
||||
*reinterpret_cast<uint16_t*>(data()+prefix.size()) = seqno;
|
||||
}
|
||||
TransId(uint32_t id) {
|
||||
*reinterpret_cast<uint32_t*>(data()) = htonl(id);
|
||||
}
|
||||
|
||||
uint32_t toInt() const {
|
||||
return ntohl(*reinterpret_cast<const uint32_t*>(&(*this)[0]));
|
||||
}
|
||||
|
||||
bool matches(const TransPrefix prefix) const {
|
||||
return std::equal(begin(), begin()+2, prefix.begin());
|
||||
}
|
||||
};
|
||||
|
||||
TransId unpackTid(msgpack::object& o);
|
||||
|
||||
} /* namespace net */
|
||||
} /* dht */
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "utils.h"
|
||||
#include "rng.h"
|
||||
#include "rate_limiter.h"
|
||||
#include "net.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
@@ -41,6 +40,7 @@ namespace net {
|
||||
|
||||
struct Request;
|
||||
struct Socket;
|
||||
struct TransId;
|
||||
|
||||
#ifndef MSG_CONFIRM
|
||||
#define MSG_CONFIRM 0
|
||||
@@ -169,7 +169,7 @@ private:
|
||||
std::function<RequestAnswer(Sp<Node>,
|
||||
const InfoHash&,
|
||||
const Blob&,
|
||||
uint32_t,
|
||||
Tid,
|
||||
const Query&)> onListen {};
|
||||
/**
|
||||
* Called on announce request.
|
||||
@@ -202,8 +202,8 @@ public:
|
||||
using RequestCb = std::function<void(const Request&, RequestAnswer&&)>;
|
||||
using RequestExpiredCb = std::function<void(const Request&, bool)>;
|
||||
|
||||
NetworkEngine(Logger& log, Scheduler& scheduler);
|
||||
NetworkEngine(InfoHash& myid, NetId net, int s, int s6, Logger& log, Scheduler& scheduler,
|
||||
NetworkEngine(Logger& log, Scheduler& scheduler, const int& s = -1, const int& s6 = -1);
|
||||
NetworkEngine(InfoHash& myid, NetId net, const int& s, const int& s6, Logger& log, Scheduler& scheduler,
|
||||
decltype(NetworkEngine::onError) onError,
|
||||
decltype(NetworkEngine::onNewNode) onNewNode,
|
||||
decltype(NetworkEngine::onReportedAddr) onReportedAddr,
|
||||
@@ -231,7 +231,7 @@ public:
|
||||
* @param nodes6 The ipv6 closest nodes.
|
||||
* @param values The values to send.
|
||||
*/
|
||||
void tellListener(Sp<Node> n, uint32_t socket_id, const InfoHash& hash, want_t want, const Blob& ntoken,
|
||||
void tellListener(Sp<Node> n, Tid socket_id, const InfoHash& hash, want_t want, const Blob& ntoken,
|
||||
std::vector<Sp<Node>>&& nodes, std::vector<Sp<Node>>&& nodes6,
|
||||
std::vector<Sp<Value>>&& values, const Query& q);
|
||||
|
||||
@@ -465,18 +465,18 @@ private:
|
||||
// basic wrapper for socket sendto function
|
||||
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);
|
||||
void sendValueParts(const TransId& tid, const std::vector<Blob>& svals, const SockAddr& addr);
|
||||
std::vector<Blob> packValueHeader(msgpack::sbuffer&, const std::vector<Sp<Value>>&);
|
||||
void maintainRxBuffer(const TransId& tid);
|
||||
void maintainRxBuffer(Tid tid);
|
||||
|
||||
/*************
|
||||
* Answers *
|
||||
*************/
|
||||
/* answer to a ping request */
|
||||
void sendPong(const SockAddr& addr, TransId tid);
|
||||
void sendPong(const SockAddr& addr, Tid tid);
|
||||
/* answer to findnodes/getvalues request */
|
||||
void sendNodesValues(const SockAddr& addr,
|
||||
TransId tid,
|
||||
Tid tid,
|
||||
const Blob& nodes,
|
||||
const Blob& nodes6,
|
||||
const std::vector<Sp<Value>>& st,
|
||||
@@ -490,12 +490,12 @@ private:
|
||||
std::vector<Sp<Node>>& nodes,
|
||||
std::vector<Sp<Node>>& nodes6);
|
||||
/* answer to a listen request */
|
||||
void sendListenConfirmation(const SockAddr& addr, TransId tid);
|
||||
void sendListenConfirmation(const SockAddr& addr, Tid tid);
|
||||
/* answer to put request */
|
||||
void sendValueAnnounced(const SockAddr& addr, TransId, Value::Id);
|
||||
void sendValueAnnounced(const SockAddr& addr, Tid, Value::Id);
|
||||
/* answer in case of error */
|
||||
void sendError(const SockAddr& addr,
|
||||
TransId tid,
|
||||
Tid tid,
|
||||
uint16_t code,
|
||||
const std::string& message,
|
||||
bool include_id=false);
|
||||
@@ -505,8 +505,8 @@ private:
|
||||
/* DHT info */
|
||||
const InfoHash& myid;
|
||||
const NetId network {0};
|
||||
const int dht_socket {-1};
|
||||
const int dht_socket6 {-1};
|
||||
const int& dht_socket;
|
||||
const int& dht_socket6;
|
||||
const Logger& DHT_LOG;
|
||||
|
||||
NodeCache cache {};
|
||||
@@ -519,8 +519,8 @@ private:
|
||||
size_t limiter_maintenance {0};
|
||||
|
||||
// requests handling
|
||||
std::map<TransId, Sp<Request>> requests {};
|
||||
std::map<TransId, PartialMessage> partial_messages;
|
||||
std::map<Tid, Sp<Request>> requests {};
|
||||
std::map<Tid, PartialMessage> partial_messages;
|
||||
|
||||
MessageStats in_stats {}, out_stats {};
|
||||
std::set<SockAddr> blacklist {};
|
||||
|
||||
+10
-11
@@ -22,7 +22,6 @@
|
||||
#include "infohash.h" // includes socket structures
|
||||
#include "utils.h"
|
||||
#include "sockaddr.h"
|
||||
#include "net.h"
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
@@ -36,8 +35,8 @@ struct Socket;
|
||||
struct RequestAnswer;
|
||||
} /* namespace net */
|
||||
|
||||
using Tid = uint32_t;
|
||||
using SocketCb = std::function<void(const Sp<Node>&, net::RequestAnswer&&)>;
|
||||
using SocketId = uint32_t;
|
||||
struct Socket {
|
||||
Socket() {}
|
||||
Socket(SocketCb&& on_receive) :
|
||||
@@ -102,7 +101,7 @@ struct Node {
|
||||
|
||||
void requested(const Sp<net::Request>& req);
|
||||
void received(time_point now, const Sp<net::Request>& req);
|
||||
Sp<net::Request> getRequest(const net::TransId& tid);
|
||||
Sp<net::Request> getRequest(Tid tid);
|
||||
void cancelRequest(const Sp<net::Request>& req);
|
||||
|
||||
void setExpired();
|
||||
@@ -116,16 +115,16 @@ struct Node {
|
||||
*
|
||||
* @return the socket.
|
||||
*/
|
||||
SocketId openSocket(SocketCb&& cb);
|
||||
Tid openSocket(SocketCb&& cb);
|
||||
|
||||
Socket* getSocket(SocketId id);
|
||||
Sp<Socket> getSocket(Tid id);
|
||||
|
||||
/**
|
||||
* Closes a socket so that no further data will be red on that socket.
|
||||
*
|
||||
* @param socket The socket to close.
|
||||
*/
|
||||
void closeSocket(SocketId id);
|
||||
void closeSocket(Tid id);
|
||||
|
||||
/**
|
||||
* Resets the state of the node so it's not expired anymore.
|
||||
@@ -137,9 +136,9 @@ struct Node {
|
||||
*
|
||||
* @return the new id.
|
||||
*/
|
||||
uint16_t getNewTid() {
|
||||
Tid getNewTid() {
|
||||
++transaction_id;
|
||||
return transaction_id == net::TransId::INVALID ? ++transaction_id : transaction_id;
|
||||
return transaction_id ? ++transaction_id : transaction_id;
|
||||
}
|
||||
|
||||
std::string toString() const;
|
||||
@@ -164,11 +163,11 @@ private:
|
||||
time_point reply_time {time_point::min()}; /* time of last correct reply received */
|
||||
unsigned auth_errors {0};
|
||||
bool expired_ {false};
|
||||
SocketId transaction_id;
|
||||
Tid transaction_id;
|
||||
using TransactionDist = std::uniform_int_distribution<decltype(transaction_id)>;
|
||||
|
||||
std::map<net::TransId, Sp<net::Request>> requests_ {};
|
||||
std::map<SocketId, Socket> sockets_;
|
||||
std::map<Tid, Sp<net::Request>> requests_ {};
|
||||
std::map<Tid, Sp<Socket>> sockets_;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -37,12 +37,15 @@ struct NodeCache {
|
||||
*/
|
||||
void clearBadNodes(sa_family_t family = 0);
|
||||
|
||||
~NodeCache();
|
||||
|
||||
private:
|
||||
class NodeMap : public std::map<InfoHash, std::weak_ptr<Node>> {
|
||||
public:
|
||||
Sp<Node> getNode(const InfoHash& id);
|
||||
Sp<Node> getNode(const InfoHash& id, const SockAddr&, time_point now, bool confirmed, bool client);
|
||||
void clearBadNodes();
|
||||
void setExpired();
|
||||
};
|
||||
|
||||
const NodeMap& cache(sa_family_t af) const { return af == AF_INET ? cache_4 : cache_6; }
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
template<size_t Quota, unsigned long Perdiod=1>
|
||||
template<size_t Quota, unsigned long Period=1>
|
||||
class RateLimiter {
|
||||
public:
|
||||
/** Clear outdated records and return current quota usage */
|
||||
size_t maintain(const time_point& now) {
|
||||
auto limit = now - std::chrono::seconds(Perdiod);
|
||||
auto limit = now - std::chrono::seconds(Period);
|
||||
while (not records.empty() and records.front() < limit)
|
||||
records.pop();
|
||||
return records.size();
|
||||
|
||||
+214
-14
@@ -1,7 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -29,13 +30,21 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
class OPENDHT_PUBLIC SecureDht : public Dht {
|
||||
class OPENDHT_PUBLIC SecureDht final : public DhtInterface {
|
||||
public:
|
||||
|
||||
typedef std::function<void(bool)> SignatureCheckCallback;
|
||||
|
||||
using Config = SecureDhtConfig;
|
||||
|
||||
static dht::Config& getConfig(SecureDht::Config& conf)
|
||||
{
|
||||
auto& c = conf.node_config;
|
||||
if (not c.node_id and conf.id.second)
|
||||
c.node_id = InfoHash::get("node:"+conf.id.second->getId().toString());
|
||||
return c;
|
||||
}
|
||||
|
||||
SecureDht() {}
|
||||
|
||||
/**
|
||||
@@ -44,7 +53,7 @@ public:
|
||||
* id: the identity to use for the crypto layer and to compute
|
||||
* our own hash on the Dht.
|
||||
*/
|
||||
SecureDht(int s, int s6, Config config);
|
||||
SecureDht(std::unique_ptr<DhtInterface> dht, Config config);
|
||||
|
||||
virtual ~SecureDht();
|
||||
|
||||
@@ -62,14 +71,17 @@ public:
|
||||
return secureType(std::move(tmp_type));
|
||||
}
|
||||
|
||||
virtual void registerType(const ValueType& type) override {
|
||||
Dht::registerType(secureType(type));
|
||||
void registerType(const ValueType& type) {
|
||||
if (dht_)
|
||||
dht_->registerType(secureType(type));
|
||||
}
|
||||
virtual void registerType(ValueType&& type) {
|
||||
Dht::registerType(secureType(std::forward<ValueType>(type)));
|
||||
void registerType(ValueType&& type) {
|
||||
if (dht_)
|
||||
dht_->registerType(secureType(std::forward<ValueType>(type)));
|
||||
}
|
||||
virtual void registerInsecureType(const ValueType& type) {
|
||||
Dht::registerType(type);
|
||||
void registerInsecureType(const ValueType& type) {
|
||||
if (dht_)
|
||||
dht_->registerType(type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,18 +89,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&& = {}, Where&& w = {}) override;
|
||||
virtual void get(const InfoHash& id, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f = {}, Where&& w = {}) override {
|
||||
void get(const InfoHash& id, GetCallback cb, DoneCallback donecb={}, Value::Filter&& = {}, Where&& w = {});
|
||||
void get(const InfoHash& id, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f = {}, Where&& w = {}) {
|
||||
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={}, Where&& w = {}) override {
|
||||
void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) {
|
||||
get(key, bindGetCb(cb), donecb, std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}, Where&& w = {}) override {
|
||||
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));
|
||||
}
|
||||
|
||||
virtual size_t listen(const InfoHash& id, GetCallback cb, Value::Filter&& = {}, Where&& w = {}) override;
|
||||
size_t listen(const InfoHash& id, GetCallback cb, Value::Filter = {}, Where w = {});
|
||||
|
||||
/**
|
||||
* Will take ownership of the value, sign it using our private key and put it in the DHT.
|
||||
@@ -135,7 +147,193 @@ public:
|
||||
localQueryMethod_ = std::move(query_method);
|
||||
}
|
||||
|
||||
/**
|
||||
* SecureDht to Dht proxy
|
||||
*/
|
||||
void shutdown(ShutdownCallback cb) {
|
||||
dht_->shutdown(cb);
|
||||
}
|
||||
void dumpTables() const {
|
||||
dht_->dumpTables();
|
||||
}
|
||||
inline const InfoHash& getNodeId() const { return dht_->getNodeId(); }
|
||||
std::pair<size_t, size_t> getStoreSize() const {
|
||||
return dht_->getStoreSize();
|
||||
}
|
||||
std::string getStorageLog() const {
|
||||
return dht_->getStorageLog();
|
||||
}
|
||||
std::string getStorageLog(const InfoHash& h) const {
|
||||
return dht_->getStorageLog(h);
|
||||
}
|
||||
void setStorageLimit(size_t limit = DEFAULT_STORAGE_LIMIT) {
|
||||
dht_->setStorageLimit(limit);
|
||||
}
|
||||
std::vector<NodeExport> exportNodes() {
|
||||
return dht_->exportNodes();
|
||||
}
|
||||
std::vector<ValuesExport> exportValues() const {
|
||||
return dht_->exportValues();
|
||||
}
|
||||
void importValues(const std::vector<ValuesExport>& v) {
|
||||
dht_->importValues(v);
|
||||
}
|
||||
NodeStats getNodesStats(sa_family_t af) const {
|
||||
return dht_->getNodesStats(af);
|
||||
}
|
||||
std::vector<unsigned> getNodeMessageStats(bool in = false) {
|
||||
return dht_->getNodeMessageStats(in);
|
||||
}
|
||||
std::string getRoutingTablesLog(sa_family_t af) const {
|
||||
return dht_->getRoutingTablesLog(af);
|
||||
}
|
||||
std::string getSearchesLog(sa_family_t af) const {
|
||||
return dht_->getSearchesLog(af);
|
||||
}
|
||||
std::string getSearchLog(const InfoHash& h, sa_family_t af = AF_UNSPEC) const {
|
||||
return dht_->getSearchLog(h, af);
|
||||
}
|
||||
std::vector<SockAddr> getPublicAddress(sa_family_t family = 0) {
|
||||
return dht_->getPublicAddress(family);
|
||||
}
|
||||
time_point periodic(const uint8_t *buf, size_t buflen, const SockAddr& sa) {
|
||||
return dht_->periodic(buf, buflen, sa);
|
||||
}
|
||||
time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen) {
|
||||
return dht_->periodic(buf, buflen, from, fromlen);
|
||||
}
|
||||
NodeStatus getStatus(sa_family_t af) const {
|
||||
return dht_->getStatus(af);
|
||||
}
|
||||
NodeStatus getStatus() const {
|
||||
return dht_->getStatus();
|
||||
}
|
||||
bool isRunning(sa_family_t af = 0) const {
|
||||
return dht_->isRunning(af);
|
||||
}
|
||||
const ValueType& getType(ValueType::Id type_id) const {
|
||||
return dht_->getType(type_id);
|
||||
}
|
||||
void insertNode(const InfoHash& id, const SockAddr& sa) {
|
||||
dht_->insertNode(id, sa);
|
||||
}
|
||||
void insertNode(const InfoHash& id, const sockaddr* sa, socklen_t salen) {
|
||||
dht_->insertNode(id, sa, salen);
|
||||
}
|
||||
void insertNode(const NodeExport& n) {
|
||||
dht_->insertNode(n);
|
||||
}
|
||||
void pingNode(const sockaddr* sa, socklen_t salen, DoneCallbackSimple&& cb={}) {
|
||||
dht_->pingNode(sa, salen, std::move(cb));
|
||||
}
|
||||
void query(const InfoHash& key, QueryCallback cb, DoneCallback done_cb = {}, Query&& q = {}) {
|
||||
dht_->query(key, cb, done_cb, std::move(q));
|
||||
}
|
||||
void query(const InfoHash& key, QueryCallback cb, DoneCallbackSimple done_cb = {}, Query&& q = {}) {
|
||||
dht_->query(key, cb, done_cb, std::move(q));
|
||||
}
|
||||
std::vector<Sp<Value>> getLocal(const InfoHash& key, Value::Filter f = Value::AllFilter()) const {
|
||||
return dht_->getLocal(key, f);
|
||||
}
|
||||
Sp<Value> getLocalById(const InfoHash& key, Value::Id vid) const {
|
||||
return dht_->getLocalById(key, vid);
|
||||
}
|
||||
void put(const InfoHash& key,
|
||||
Sp<Value> v,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false)
|
||||
{
|
||||
dht_->put(key, v, cb, created, permanent);
|
||||
}
|
||||
void put(const InfoHash& key,
|
||||
const Sp<Value>& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false)
|
||||
{
|
||||
dht_->put(key, v, cb, created, permanent);
|
||||
}
|
||||
|
||||
void put(const InfoHash& key,
|
||||
Value&& v,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false)
|
||||
{
|
||||
dht_->put(key, std::move(v), cb, created, permanent);
|
||||
}
|
||||
void put(const InfoHash& key,
|
||||
Value&& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false)
|
||||
{
|
||||
dht_->put(key, std::move(v), cb, created, permanent);
|
||||
}
|
||||
std::vector<Sp<Value>> getPut(const InfoHash& h) {
|
||||
return dht_->getPut(h);
|
||||
}
|
||||
Sp<Value> getPut(const InfoHash& h, const Value::Id& vid) {
|
||||
return dht_->getPut(h, vid);
|
||||
}
|
||||
bool cancelPut(const InfoHash& h, const Value::Id& vid) {
|
||||
return dht_->cancelPut(h, vid);
|
||||
}
|
||||
size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w = {}) {
|
||||
return dht_->listen(key, cb, f, w);
|
||||
}
|
||||
bool cancelListen(const InfoHash& h, size_t token) {
|
||||
return dht_->cancelListen(h, token);
|
||||
}
|
||||
void connectivityChanged(sa_family_t af) {
|
||||
dht_->connectivityChanged(af);
|
||||
}
|
||||
void connectivityChanged() {
|
||||
dht_->connectivityChanged();
|
||||
}
|
||||
|
||||
void forwardAllMessages(bool forward) {
|
||||
forward_all_ = forward;
|
||||
}
|
||||
|
||||
void setPushNotificationToken(const std::string& token = "") {
|
||||
dht_->setPushNotificationToken(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call linked callback with push_notification
|
||||
* @param notification to process
|
||||
*/
|
||||
void pushNotificationReceived(const std::map<std::string, std::string>& notification) {
|
||||
dht_->pushNotificationReceived(notification);
|
||||
}
|
||||
/**
|
||||
* Refresh a listen via a token
|
||||
* @param token
|
||||
*/
|
||||
void resubscribe(const unsigned token) {
|
||||
dht_->resubscribe(token);
|
||||
}
|
||||
|
||||
void setLoggers(LogMethod error = NOLOG, LogMethod warn = NOLOG, LogMethod debug = NOLOG)
|
||||
{
|
||||
DHT_LOG.DEBUG = debug;
|
||||
DHT_LOG.WARN = warn;
|
||||
DHT_LOG.ERR = error;
|
||||
dht_->setLoggers(std::forward<LogMethod>(error), std::forward<LogMethod>(warn), std::forward<LogMethod>(debug));
|
||||
}
|
||||
|
||||
/**
|
||||
* Only print logs related to the given InfoHash (if given), or disable filter (if zeroes).
|
||||
*/
|
||||
void setLogFilter(const InfoHash& f) {
|
||||
DHT_LOG.setFilter(f);
|
||||
dht_->setLogFilter(f);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<DhtInterface> dht_;
|
||||
// prevent copy
|
||||
SecureDht(const SecureDht&) = delete;
|
||||
SecureDht& operator=(const SecureDht&) = delete;
|
||||
@@ -153,6 +351,8 @@ private:
|
||||
std::map<InfoHash, Sp<const crypto::PublicKey>> nodesPubKeys_ {};
|
||||
|
||||
std::uniform_int_distribution<Value::Id> rand_id {};
|
||||
|
||||
std::atomic_bool forward_all_ {false};
|
||||
};
|
||||
|
||||
const ValueType CERTIFICATE_TYPE = {
|
||||
|
||||
@@ -28,6 +28,8 @@ typedef uint16_t in_port_t;
|
||||
#endif
|
||||
#else
|
||||
#include <iso646.h>
|
||||
#include <stdint.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2def.h>
|
||||
#include <ws2tcpip.h>
|
||||
typedef uint16_t sa_family_t;
|
||||
@@ -36,6 +38,8 @@ typedef uint16_t in_port_t;
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
@@ -66,11 +70,26 @@ public:
|
||||
throw std::runtime_error("Socket address length is too large");
|
||||
set(sa, length);
|
||||
}
|
||||
SockAddr(const sockaddr* sa) {
|
||||
socklen_t len = 0;
|
||||
if (sa) {
|
||||
if (sa->sa_family == AF_INET)
|
||||
len = sizeof(sockaddr_in);
|
||||
else if(sa->sa_family == AF_INET6)
|
||||
len = sizeof(sockaddr_in6);
|
||||
else
|
||||
throw std::runtime_error("Unknown address family");
|
||||
}
|
||||
set(sa, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build from an existing sockaddr_storage structure.
|
||||
*/
|
||||
SockAddr(const sockaddr_storage& ss, socklen_t len) : SockAddr((const sockaddr*)&ss, len) {}
|
||||
|
||||
static std::vector<SockAddr> resolve(const std::string& host, const std::string& service = {});
|
||||
|
||||
bool operator<(const SockAddr& o) const {
|
||||
if (len != o.len)
|
||||
return len < o.len;
|
||||
@@ -120,7 +139,7 @@ public:
|
||||
}
|
||||
if (new_length != len) {
|
||||
len = new_length;
|
||||
if (len) addr.reset((sockaddr*)std::calloc(len, 1));
|
||||
if (len) addr.reset((sockaddr*)::calloc(len, 1));
|
||||
else addr.reset();
|
||||
}
|
||||
if (len > sizeof(sa_family_t))
|
||||
@@ -206,6 +225,9 @@ public:
|
||||
|
||||
bool isUnspecified() const;
|
||||
|
||||
bool isMappedIPv4() const;
|
||||
SockAddr getMappedIPv4() const;
|
||||
|
||||
/**
|
||||
* A comparator to classify IP addresses, only considering the
|
||||
* first 64 bits in IPv6.
|
||||
@@ -236,13 +258,13 @@ public:
|
||||
};
|
||||
private:
|
||||
socklen_t len {0};
|
||||
struct free_delete { void operator()(void* p) { std::free(p); } };
|
||||
struct free_delete { void operator()(void* p) { ::free(p); } };
|
||||
std::unique_ptr<sockaddr, free_delete> addr {};
|
||||
|
||||
void set(const sockaddr* sa, socklen_t length) {
|
||||
if (len != length) {
|
||||
len = length;
|
||||
if (len) addr.reset((sockaddr*)std::malloc(len));
|
||||
if (len) addr.reset((sockaddr*)::malloc(len));
|
||||
else addr.reset();
|
||||
}
|
||||
if (len)
|
||||
|
||||
@@ -55,9 +55,15 @@ void erase_if(std::map<Key, Item>& map, const Condition& condition)
|
||||
}
|
||||
|
||||
class OPENDHT_PUBLIC DhtException : public std::runtime_error {
|
||||
public:
|
||||
DhtException(const std::string &str = "") :
|
||||
std::runtime_error("DhtException occurred: " + str) {}
|
||||
public:
|
||||
DhtException(const std::string &str = "") :
|
||||
std::runtime_error("DhtException occurred: " + str) {}
|
||||
};
|
||||
|
||||
class OPENDHT_PUBLIC SocketException : public DhtException {
|
||||
public:
|
||||
SocketException(int err) :
|
||||
DhtException(strerror(err)) {}
|
||||
};
|
||||
|
||||
// Time related definitions and utility functions
|
||||
|
||||
+26
-2
@@ -37,6 +37,10 @@
|
||||
#include <chrono>
|
||||
#include <set>
|
||||
|
||||
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
|
||||
#include <json/json.h>
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
|
||||
namespace dht {
|
||||
|
||||
struct Value;
|
||||
@@ -346,6 +350,14 @@ struct OPENDHT_PUBLIC Value
|
||||
Value(ValueType::Id t, const uint8_t* dat_ptr, size_t dat_len, Id id = INVALID_ID)
|
||||
: id(id), type(t), data(dat_ptr, dat_ptr+dat_len) {}
|
||||
|
||||
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
|
||||
/**
|
||||
* Build a value from a json object
|
||||
* @param json
|
||||
*/
|
||||
Value(Json::Value& json);
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
|
||||
template <typename Type>
|
||||
Value(ValueType::Id t, const Type& d, Id id = INVALID_ID)
|
||||
: id(id), type(t), data(packMsg(d)) {}
|
||||
@@ -417,6 +429,18 @@ struct OPENDHT_PUBLIC Value
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
|
||||
/**
|
||||
* Build a json object from a value
|
||||
* Example:
|
||||
* {
|
||||
* "data":"base64ofdata",
|
||||
* id":"0", "seq":0,"type":3
|
||||
* }
|
||||
*/
|
||||
Json::Value toJson() const;
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
|
||||
/** Return the size in bytes used by this value in memory (minimum). */
|
||||
size_t size() const;
|
||||
|
||||
@@ -424,11 +448,11 @@ struct OPENDHT_PUBLIC Value
|
||||
void msgpack_pack_to_sign(Packer& pk) const
|
||||
{
|
||||
bool has_owner = owner && *owner;
|
||||
pk.pack_map((user_type.empty()?0:1) + (has_owner?(recipient == InfoHash() ? 4 : 5):2));
|
||||
pk.pack_map((user_type.empty()?0:1) + (has_owner?(recipient ? 5 : 4):2));
|
||||
if (has_owner) { // isSigned
|
||||
pk.pack(std::string("seq")); pk.pack(seq);
|
||||
pk.pack(std::string("owner")); owner->msgpack_pack(pk);
|
||||
if (recipient != InfoHash()) {
|
||||
if (recipient) {
|
||||
pk.pack(std::string("to")); pk.pack(recipient);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -92,9 +92,9 @@ cdef class _WithID(object):
|
||||
cdef class InfoHash(_WithID):
|
||||
cdef cpp.InfoHash _infohash
|
||||
def __cinit__(self, bytes str=b''):
|
||||
self._infohash = cpp.InfoHash(str)
|
||||
self._infohash = cpp.InfoHash(str) if str else cpp.InfoHash()
|
||||
def __bool__(InfoHash self):
|
||||
return not (self._infohash == cpp.InfoHash())
|
||||
return <bool>self._infohash
|
||||
def __richcmp__(InfoHash self, InfoHash other, int op):
|
||||
if op == 0:
|
||||
return self._infohash < other._infohash
|
||||
|
||||
@@ -65,8 +65,9 @@ cdef extern from "opendht/infohash.h" namespace "dht":
|
||||
InfoHash get(string s)
|
||||
@staticmethod
|
||||
InfoHash getRandom()
|
||||
bool operator==(InfoHash)
|
||||
bool operator<(InfoHash)
|
||||
bool operator==(InfoHash) const
|
||||
bool operator<(InfoHash) const
|
||||
bool operator bool() const
|
||||
|
||||
cdef extern from "opendht/sockaddr.h" namespace "dht":
|
||||
cdef cppclass SockAddr:
|
||||
@@ -259,4 +260,3 @@ cdef extern from "opendht/indexation/pht.h" namespace "dht::indexation":
|
||||
void insert(IndexKey k, IndexValue v, DoneCallbackSimple cb)
|
||||
@staticmethod
|
||||
LookupCallback bindLookupCb(LookupCallbackRaw cb, void *user_data)
|
||||
|
||||
+17
-4
@@ -9,6 +9,7 @@ libopendht_la_SOURCES = \
|
||||
listener.h \
|
||||
request.h \
|
||||
search.h \
|
||||
net.h \
|
||||
parsed_message.h \
|
||||
node_cache.cpp \
|
||||
callbacks.cpp \
|
||||
@@ -22,7 +23,6 @@ libopendht_la_SOURCES = \
|
||||
securedht.cpp \
|
||||
dhtrunner.cpp \
|
||||
default_types.cpp \
|
||||
indexation/pht.cpp \
|
||||
log.cpp
|
||||
|
||||
if WIN32
|
||||
@@ -37,7 +37,6 @@ nobase_include_HEADERS = \
|
||||
../include/opendht/node_cache.h \
|
||||
../include/opendht/routing_table.h \
|
||||
../include/opendht/network_engine.h \
|
||||
../include/opendht/net.h \
|
||||
../include/opendht/scheduler.h \
|
||||
../include/opendht/rate_limiter.h \
|
||||
../include/opendht/utils.h \
|
||||
@@ -51,8 +50,22 @@ nobase_include_HEADERS = \
|
||||
../include/opendht/default_types.h \
|
||||
../include/opendht/log.h \
|
||||
../include/opendht/log_enable.h \
|
||||
../include/opendht/rng.h \
|
||||
../include/opendht/indexation/pht.h
|
||||
../include/opendht/rng.h
|
||||
|
||||
if ENABLE_PROXY_SERVER
|
||||
libopendht_la_SOURCES += base64.h base64.cpp dht_proxy_server.cpp
|
||||
nobase_include_HEADERS += ../include/opendht/dht_proxy_server.h
|
||||
endif
|
||||
|
||||
if ENABLE_PROXY_CLIENT
|
||||
libopendht_la_SOURCES += dht_proxy_client.cpp
|
||||
nobase_include_HEADERS += ../include/opendht/dht_proxy_client.h ../include/opendht/dht_interface.h
|
||||
endif
|
||||
|
||||
if ENABLE_INDEXATION
|
||||
libopendht_la_SOURCES += indexation/pht.cpp
|
||||
nobase_include_HEADERS += ../include/opendht/indexation/pht.h
|
||||
endif
|
||||
|
||||
clean-local:
|
||||
rm -rf libargon2.la
|
||||
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2017 Savoir-faire Linux Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "base64.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Mainly based on the following stackoverflow question:
|
||||
* http://stackoverflow.com/questions/342409/how-do-i-base64-encode-decode-in-c
|
||||
*/
|
||||
static const char encoding_table[] = {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
|
||||
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
|
||||
'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
|
||||
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
|
||||
's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2',
|
||||
'3', '4', '5', '6', '7', '8', '9', '+', '/'
|
||||
};
|
||||
|
||||
static const size_t mod_table[] = { 0, 2, 1 };
|
||||
|
||||
char *base64_encode(const uint8_t *input, size_t input_length,
|
||||
char *output, size_t *output_length)
|
||||
{
|
||||
size_t i, j;
|
||||
size_t out_sz = *output_length;
|
||||
*output_length = 4 * ((input_length + 2) / 3);
|
||||
if (out_sz < *output_length || output == nullptr)
|
||||
return nullptr;
|
||||
|
||||
for (i = 0, j = 0; i < input_length; ) {
|
||||
uint8_t octet_a = i < input_length ? input[i++] : 0;
|
||||
uint8_t octet_b = i < input_length ? input[i++] : 0;
|
||||
uint8_t octet_c = i < input_length ? input[i++] : 0;
|
||||
|
||||
uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
|
||||
|
||||
output[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
|
||||
output[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
|
||||
output[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
|
||||
output[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
|
||||
}
|
||||
|
||||
for (i = 0; i < mod_table[input_length % 3]; i++)
|
||||
output[*output_length - 1 - i] = '=';
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
uint8_t *base64_decode(const char *input, size_t input_length,
|
||||
uint8_t *output, size_t *output_length)
|
||||
{
|
||||
size_t i, j;
|
||||
uint8_t decoding_table[256];
|
||||
|
||||
uint8_t c;
|
||||
for (c = 0; c < 64; c++)
|
||||
decoding_table[static_cast<int>(encoding_table[c])] = c;
|
||||
|
||||
if (input_length % 4 != 0 || input_length < 2)
|
||||
return nullptr;
|
||||
|
||||
size_t out_sz = *output_length;
|
||||
*output_length = input_length / 4 * 3;
|
||||
if (input[input_length - 1] == '=')
|
||||
(*output_length)--;
|
||||
if (input[input_length - 2] == '=')
|
||||
(*output_length)--;
|
||||
|
||||
if (out_sz < *output_length || output == nullptr)
|
||||
return nullptr;
|
||||
|
||||
for (i = 0, j = 0; i < input_length;) {
|
||||
uint8_t sextet_a = input[i] == '=' ? 0 & i++
|
||||
: decoding_table[static_cast<int>(input[i++])];
|
||||
uint8_t sextet_b = input[i] == '=' ? 0 & i++
|
||||
: decoding_table[static_cast<int>(input[i++])];
|
||||
uint8_t sextet_c = input[i] == '=' ? 0 & i++
|
||||
: decoding_table[static_cast<int>(input[i++])];
|
||||
uint8_t sextet_d = input[i] == '=' ? 0 & i++
|
||||
: decoding_table[static_cast<int>(input[i++])];
|
||||
|
||||
uint32_t triple = (sextet_a << 3 * 6) +
|
||||
(sextet_b << 2 * 6) +
|
||||
(sextet_c << 1 * 6) +
|
||||
(sextet_d << 0 * 6);
|
||||
|
||||
if (j < *output_length)
|
||||
output[j++] = (triple >> 2 * 8) & 0xFF;
|
||||
if (j < *output_length)
|
||||
output[j++] = (triple >> 1 * 8) & 0xFF;
|
||||
if (j < *output_length)
|
||||
output[j++] = (triple >> 0 * 8) & 0xFF;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string
|
||||
base64_encode(const std::vector<uint8_t>::const_iterator begin,
|
||||
const std::vector<uint8_t>::const_iterator end)
|
||||
{
|
||||
size_t output_length = 4 * ((std::distance(begin, end) + 2) / 3);
|
||||
std::string out;
|
||||
out.resize(output_length);
|
||||
base64_encode(&(*begin), std::distance(begin, end),
|
||||
&(*out.begin()), &output_length);
|
||||
out.resize(output_length);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
base64_encode(const std::vector<unsigned char>& str)
|
||||
{
|
||||
return base64_encode(str.cbegin(), str.cend());
|
||||
}
|
||||
|
||||
std::string
|
||||
base64_decode(const std::string& str)
|
||||
{
|
||||
size_t output_length = str.length() / 4 * 3 + 2;
|
||||
std::vector<uint8_t> output;
|
||||
output.resize(output_length);
|
||||
base64_decode(str.data(), str.size(), output.data(), &output_length);
|
||||
output.resize(output_length);
|
||||
return std::string(output.begin(), output.end());
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2017 Savoir-faire Linux Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* Encode a buffer in base64.
|
||||
*
|
||||
* @param str the input buffer
|
||||
* @return a base64-encoded buffer
|
||||
*/
|
||||
std::string base64_encode(const std::vector<unsigned char>& str);
|
||||
/**
|
||||
* Decode a buffer in base64.
|
||||
*
|
||||
* @param str the input buffer
|
||||
* @return a base64-decoded buffer
|
||||
*/
|
||||
std::string base64_decode(const std::string& str);
|
||||
@@ -68,4 +68,36 @@ NodeStats::toString() const
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
|
||||
/**
|
||||
* Build a json object from a NodeStats
|
||||
*/
|
||||
Json::Value
|
||||
NodeStats::toJson() const
|
||||
{
|
||||
Json::Value val;
|
||||
val["good"] = static_cast<Json::LargestUInt>(good_nodes);
|
||||
val["dubious"] = static_cast<Json::LargestUInt>(dubious_nodes);
|
||||
val["incoming"] = static_cast<Json::LargestUInt>(incoming_nodes);
|
||||
if (table_depth > 1) {
|
||||
val["table_depth"] = static_cast<Json::LargestUInt>(table_depth);
|
||||
unsigned long tot_nodes = 8 * std::exp2(table_depth);
|
||||
val["network_size_estimation"] = static_cast<Json::LargestUInt>(tot_nodes);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
NodeStats::NodeStats(const Json::Value& val)
|
||||
{
|
||||
if (val.isMember("good"))
|
||||
good_nodes = static_cast<unsigned>(val["good"].asLargestUInt());
|
||||
if (val.isMember("dubious"))
|
||||
dubious_nodes = static_cast<unsigned>(val["dubious"].asLargestUInt());
|
||||
if (val.isMember("incoming"))
|
||||
incoming_nodes = static_cast<unsigned>(val["incoming"].asLargestUInt());
|
||||
if (val.isMember("table_depth"))
|
||||
table_depth = static_cast<unsigned>(val["table_depth"].asLargestUInt());
|
||||
}
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
|
||||
}
|
||||
+35
-6
@@ -84,7 +84,7 @@ static constexpr size_t PASSWORD_SALT_LENGTH {16};
|
||||
|
||||
constexpr gnutls_digest_algorithm_t gnutlsHashAlgo(size_t min_res) {
|
||||
return (min_res > 256/8) ? GNUTLS_DIG_SHA512 : (
|
||||
(min_res > 128/8) ? GNUTLS_DIG_SHA256 : (
|
||||
(min_res > 160/8) ? GNUTLS_DIG_SHA256 : (
|
||||
GNUTLS_DIG_SHA1));
|
||||
}
|
||||
|
||||
@@ -212,12 +212,21 @@ Blob hash(const Blob& data, size_t hash_len)
|
||||
Blob res;
|
||||
res.resize(res_size);
|
||||
const gnutls_datum_t gdat {(uint8_t*)data.data(), (unsigned)data.size()};
|
||||
if (gnutls_fingerprint(algo, &gdat, res.data(), &res_size))
|
||||
throw CryptoException("Can't compute hash !");
|
||||
if (auto err = gnutls_fingerprint(algo, &gdat, res.data(), &res_size))
|
||||
throw CryptoException(std::string("Can't compute hash: ") + gnutls_strerror(err));
|
||||
res.resize(std::min(hash_len, res_size));
|
||||
return res;
|
||||
}
|
||||
|
||||
void hash(const uint8_t* data, size_t data_length, uint8_t* hash, size_t hash_length)
|
||||
{
|
||||
auto algo = gnutlsHashAlgo(hash_length);
|
||||
size_t res_size = hash_length;
|
||||
const gnutls_datum_t gdat {(uint8_t*)data, (unsigned)data_length};
|
||||
if (auto err = gnutls_fingerprint(algo, &gdat, hash, &res_size))
|
||||
throw CryptoException(std::string("Can't compute hash: ") + gnutls_strerror(err));
|
||||
}
|
||||
|
||||
PrivateKey::PrivateKey()
|
||||
{}
|
||||
|
||||
@@ -529,7 +538,11 @@ PublicKey::getId() const
|
||||
return {};
|
||||
InfoHash id;
|
||||
size_t sz = id.size();
|
||||
constexpr int flags = (id.size() == 32) ? GNUTLS_KEYID_USE_SHA256 : 0;
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030401
|
||||
const int flags = 0;
|
||||
#else
|
||||
const int flags = (id.size() == 32) ? GNUTLS_KEYID_USE_SHA256 : 0;
|
||||
#endif
|
||||
if (auto err = gnutls_pubkey_get_key_id(pk, flags, id.data(), &sz))
|
||||
throw CryptoException(std::string("Can't get public key ID: ") + gnutls_strerror(err));
|
||||
if (sz != id.size())
|
||||
@@ -832,6 +845,15 @@ Certificate::addRevocationList(std::shared_ptr<RevocationList> list)
|
||||
revocation_lists.emplace(std::move(list));
|
||||
}
|
||||
|
||||
std::chrono::system_clock::time_point
|
||||
Certificate::getActivation() const
|
||||
{
|
||||
auto t = gnutls_x509_crt_get_activation_time(cert);
|
||||
if (t == (time_t)-1)
|
||||
return std::chrono::system_clock::time_point::min();
|
||||
return std::chrono::system_clock::from_time_t(t);
|
||||
}
|
||||
|
||||
std::chrono::system_clock::time_point
|
||||
Certificate::getExpiration() const
|
||||
{
|
||||
@@ -904,8 +926,13 @@ Certificate::generate(const PrivateKey& key, const std::string& name, Identity c
|
||||
return {};
|
||||
Certificate ret {cert};
|
||||
|
||||
gnutls_x509_crt_set_activation_time(cert, time(NULL));
|
||||
gnutls_x509_crt_set_expiration_time(cert, time(NULL) + (20 * 365 * 24 * 60 * 60));
|
||||
int64_t now = time(NULL);
|
||||
/* 2038 bug: don't allow time wrap */
|
||||
auto boundTime = [](int64_t t) -> time_t {
|
||||
return std::min<int64_t>(t, std::numeric_limits<time_t>::max());
|
||||
};
|
||||
gnutls_x509_crt_set_activation_time(cert, boundTime(now));
|
||||
gnutls_x509_crt_set_expiration_time(cert, boundTime(now + (10 * 365 * 24 * 60 * 60)));
|
||||
if (gnutls_x509_crt_set_key(cert, key.x509_key) != GNUTLS_E_SUCCESS) {
|
||||
std::cerr << "Error when setting certificate key" << std::endl;
|
||||
return {};
|
||||
@@ -1278,8 +1305,10 @@ std::ostream& operator<< (std::ostream& o, const TrustList::VerifyResult& h)
|
||||
o << "* Certificate has expired" << std::endl;
|
||||
if (h.result & GNUTLS_CERT_UNEXPECTED_OWNER)
|
||||
o << "* The owner is not the expected one" << std::endl;
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030401
|
||||
if (h.result & GNUTLS_CERT_PURPOSE_MISMATCH)
|
||||
o << "* Certificate or an intermediate does not match the intended purpose" << std::endl;
|
||||
#endif
|
||||
if (h.result & GNUTLS_CERT_MISMATCH)
|
||||
o << "* Certificate presented isn't the expected one" << std::endl;
|
||||
} else {
|
||||
|
||||
+142
-144
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2018 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
@@ -39,14 +39,6 @@ constexpr std::chrono::minutes Dht::SEARCH_EXPIRE_TIME;
|
||||
constexpr std::chrono::seconds Dht::LISTEN_EXPIRE_TIME;
|
||||
constexpr std::chrono::seconds Dht::REANNOUNCE_MARGIN;
|
||||
|
||||
void
|
||||
Dht::setLoggers(LogMethod error, LogMethod warn, LogMethod debug)
|
||||
{
|
||||
DHT_LOG.DEBUG = debug;
|
||||
DHT_LOG.WARN = warn;
|
||||
DHT_LOG.ERR = error;
|
||||
}
|
||||
|
||||
NodeStatus
|
||||
Dht::getStatus(sa_family_t af) const
|
||||
{
|
||||
@@ -115,6 +107,7 @@ Dht::getPublicAddress(sa_family_t family)
|
||||
return a.first > b.first;
|
||||
});
|
||||
std::vector<SockAddr> ret;
|
||||
ret.reserve(!family ? reported_addr.size() : reported_addr.size()/2);
|
||||
for (const auto& addr : reported_addr)
|
||||
if (!family || family == addr.second.getFamily())
|
||||
ret.emplace_back(addr.second);
|
||||
@@ -137,7 +130,7 @@ Dht::trySearchInsert(const Sp<Node>& node)
|
||||
auto& s = *it->second;
|
||||
if (s.insertNode(node, now)) {
|
||||
inserted = true;
|
||||
scheduler.edit(s.nextSearchStep, s.getNextStepTime(now));
|
||||
scheduler.edit(s.nextSearchStep, now);
|
||||
} else if (not s.expired and not s.done)
|
||||
break;
|
||||
++it;
|
||||
@@ -149,7 +142,7 @@ Dht::trySearchInsert(const Sp<Node>& node)
|
||||
auto& s = *it->second;
|
||||
if (s.insertNode(node, now)) {
|
||||
inserted = true;
|
||||
scheduler.edit(s.nextSearchStep, s.getNextStepTime(now));
|
||||
scheduler.edit(s.nextSearchStep, now);
|
||||
} else if (not s.expired and not s.done)
|
||||
break;
|
||||
}
|
||||
@@ -224,6 +217,7 @@ Dht::searchNodeGetDone(const net::Request& req,
|
||||
{
|
||||
const auto& now = scheduler.time();
|
||||
if (auto sr = ws.lock()) {
|
||||
sr->insertNode(req.node, now, answer.ntoken);
|
||||
if (auto srn = sr->getNode(req.node)) {
|
||||
/* all other get requests which are satisfied by this answer
|
||||
should not be sent anymore */
|
||||
@@ -235,8 +229,12 @@ Dht::searchNodeGetDone(const net::Request& req,
|
||||
srn->getStatus[q] = std::move(dummy_req);
|
||||
}
|
||||
}
|
||||
auto syncTime = srn->getSyncTime(scheduler.time());
|
||||
if (srn->syncJob)
|
||||
scheduler.edit(srn->syncJob, syncTime);
|
||||
else
|
||||
srn->syncJob = scheduler.add(syncTime, std::bind(&Dht::searchStep, this, sr));
|
||||
}
|
||||
sr->insertNode(req.node, now, answer.ntoken);
|
||||
onGetValuesDone(req.node, answer, sr, query);
|
||||
}
|
||||
}
|
||||
@@ -343,8 +341,8 @@ Dht::searchSendGetValues(Sp<Search> sr, SearchNode* pn, bool update)
|
||||
if (not n)
|
||||
return nullptr;
|
||||
|
||||
DHT_LOG.w(sr->id, n->node->id, "[search %s] [node %s] sending 'find_node'",
|
||||
sr->id.toString().c_str(), n->node->toString().c_str());
|
||||
/*DHT_LOG.d(sr->id, n->node->id, "[search %s] [node %s] sending 'find_node'",
|
||||
sr->id.toString().c_str(), n->node->toString().c_str());*/
|
||||
n->getStatus[query] = network_engine.sendFindNode(n->node,
|
||||
sr->id,
|
||||
-1,
|
||||
@@ -357,8 +355,8 @@ Dht::searchSendGetValues(Sp<Search> sr, SearchNode* pn, bool update)
|
||||
|
||||
if (query and not query->select.getSelection().empty()) {
|
||||
/* The request contains a select. No need to paginate... */
|
||||
DHT_LOG.w(sr->id, n->node->id, "[search %s] [node %s] sending 'get'",
|
||||
sr->id.toString().c_str(), n->node->toString().c_str());
|
||||
/*DHT_LOG.d(sr->id, n->node->id, "[search %s] [node %s] sending 'get'",
|
||||
sr->id.toString().c_str(), n->node->toString().c_str());*/
|
||||
n->getStatus[query] = network_engine.sendGetValues(n->node,
|
||||
sr->id,
|
||||
*query,
|
||||
@@ -386,11 +384,12 @@ void Dht::searchSendAnnounceValue(const Sp<Search>& sr) {
|
||||
std::weak_ptr<Search> ws = sr;
|
||||
const auto& now = scheduler.time();
|
||||
for (auto& n : sr->nodes) {
|
||||
auto something_to_announce = std::find_if(sr->announce.cbegin(), sr->announce.cend(),
|
||||
[this,&now,&sr,&n](const Announce& a) {
|
||||
return n.isSynced(now) and n.getAnnounceTime(a.value->id) <= now;
|
||||
}) != sr->announce.cend();
|
||||
if (not something_to_announce)
|
||||
if (not n.isSynced(now))
|
||||
continue;
|
||||
if (std::find_if(sr->announce.cbegin(), sr->announce.cend(),
|
||||
[this,&now,&n](const Announce& a) {
|
||||
return n.getAnnounceTime(a.value->id) <= now;
|
||||
}) == sr->announce.cend())
|
||||
continue;
|
||||
|
||||
auto onDone = [this,ws](const net::Request& req, net::RequestAnswer&& answer)
|
||||
@@ -409,62 +408,63 @@ void Dht::searchSendAnnounceValue(const Sp<Search>& sr) {
|
||||
auto onSelectDone =
|
||||
[this,ws,onDone,onExpired](const net::Request& req, net::RequestAnswer&& answer) mutable
|
||||
{ /* on probing done */
|
||||
auto sr = ws.lock();
|
||||
if (not sr) return;
|
||||
auto sn = sr->getNode(req.node);
|
||||
if (not sn) return;
|
||||
|
||||
const auto& now = scheduler.time();
|
||||
if (auto sr = ws.lock()) {
|
||||
if (auto sn = sr->getNode(req.node)) {
|
||||
for (auto ait = sr->announce.begin(); ait != sr->announce.end();) {
|
||||
auto& a = *ait;
|
||||
if (sn->isSynced(now) and sn->getAnnounceTime(a.value->id) <= now) {
|
||||
bool hasValue {false};
|
||||
uint16_t seq_no = 0;
|
||||
try {
|
||||
const auto& f = std::find_if(answer.fields.cbegin(), answer.fields.cend(),
|
||||
[&a](const Sp<FieldValueIndex>& i){
|
||||
return i->index.at(Value::Field::Id).getInt() == a.value->id;
|
||||
});
|
||||
if (f != answer.fields.cend() and *f) {
|
||||
hasValue = true;
|
||||
seq_no = static_cast<uint16_t>((*f)->index.at(Value::Field::SeqNum).getInt());
|
||||
}
|
||||
} catch (std::out_of_range&) { }
|
||||
|
||||
auto next_refresh_time = now + getType(a.value->type).expiration;
|
||||
/* only put the value if the node doesn't already have it */
|
||||
if (not hasValue or seq_no < a.value->seq) {
|
||||
DHT_LOG.w(sr->id, sn->node->id, "[search %s] [node %s] sending 'put' (vid: %d)",
|
||||
sr->id.toString().c_str(), sn->node->toString().c_str(), a.value->id);
|
||||
sn->acked[a.value->id] = std::make_pair(network_engine.sendAnnounceValue(sn->node,
|
||||
sr->id,
|
||||
a.value,
|
||||
a.permanent ? time_point::max() : a.created,
|
||||
sn->token,
|
||||
onDone,
|
||||
onExpired), next_refresh_time);
|
||||
} else if (hasValue and a.permanent) {
|
||||
DHT_LOG.w(sr->id, sn->node->id, "[search %s] [node %s] sending 'refresh' (vid: %d)",
|
||||
sr->id.toString().c_str(), sn->node->toString().c_str(), a.value->id);
|
||||
sn->acked[a.value->id] = std::make_pair(network_engine.sendRefreshValue(sn->node,
|
||||
sr->id,
|
||||
a.value->id,
|
||||
sn->token,
|
||||
onDone,
|
||||
onExpired), next_refresh_time);
|
||||
} else {
|
||||
DHT_LOG.w(sr->id, sn->node->id, "[search %s] [node %s] already has value (vid: %d). Aborting.",
|
||||
sr->id.toString().c_str(), sn->node->toString().c_str(), a.value->id);
|
||||
auto ack_req = std::make_shared<net::Request>(net::Request::State::COMPLETED);
|
||||
ack_req->reply_time = now;
|
||||
sn->acked[a.value->id] = std::make_pair(std::move(ack_req), next_refresh_time);
|
||||
|
||||
/* step to clear announces */
|
||||
scheduler.edit(sr->nextSearchStep, now);
|
||||
}
|
||||
} else {
|
||||
/* Search is now unsynced. Let's call searchStep to sync again. */
|
||||
scheduler.edit(sr->nextSearchStep, sr->getNextStepTime(now));
|
||||
}
|
||||
++ait;
|
||||
if (not sn->isSynced(now)) {
|
||||
/* Search is now unsynced. Let's call searchStep to sync again. */
|
||||
scheduler.edit(sr->nextSearchStep, now);
|
||||
return;
|
||||
}
|
||||
for (auto& a : sr->announce) {
|
||||
if (sn->getAnnounceTime(a.value->id) > now)
|
||||
continue;
|
||||
bool hasValue {false};
|
||||
uint16_t seq_no = 0;
|
||||
try {
|
||||
const auto& f = std::find_if(answer.fields.cbegin(), answer.fields.cend(),
|
||||
[&a](const Sp<FieldValueIndex>& i){
|
||||
return i->index.at(Value::Field::Id).getInt() == a.value->id;
|
||||
});
|
||||
if (f != answer.fields.cend() and *f) {
|
||||
hasValue = true;
|
||||
seq_no = static_cast<uint16_t>((*f)->index.at(Value::Field::SeqNum).getInt());
|
||||
}
|
||||
} catch (std::out_of_range&) { }
|
||||
|
||||
auto next_refresh_time = now + getType(a.value->type).expiration;
|
||||
/* only put the value if the node doesn't already have it */
|
||||
if (not hasValue or seq_no < a.value->seq) {
|
||||
DHT_LOG.w(sr->id, sn->node->id, "[search %s] [node %s] sending 'put' (vid: %d)",
|
||||
sr->id.toString().c_str(), sn->node->toString().c_str(), a.value->id);
|
||||
sn->acked[a.value->id] = std::make_pair(network_engine.sendAnnounceValue(sn->node,
|
||||
sr->id,
|
||||
a.value,
|
||||
a.permanent ? time_point::max() : a.created,
|
||||
sn->token,
|
||||
onDone,
|
||||
onExpired), next_refresh_time);
|
||||
} else if (hasValue and a.permanent) {
|
||||
DHT_LOG.w(sr->id, sn->node->id, "[search %s] [node %s] sending 'refresh' (vid: %d)",
|
||||
sr->id.toString().c_str(), sn->node->toString().c_str(), a.value->id);
|
||||
sn->acked[a.value->id] = std::make_pair(network_engine.sendRefreshValue(sn->node,
|
||||
sr->id,
|
||||
a.value->id,
|
||||
sn->token,
|
||||
onDone,
|
||||
onExpired), next_refresh_time);
|
||||
} else {
|
||||
DHT_LOG.w(sr->id, sn->node->id, "[search %s] [node %s] already has value (vid: %d). Aborting.",
|
||||
sr->id.toString().c_str(), sn->node->toString().c_str(), a.value->id);
|
||||
auto ack_req = std::make_shared<net::Request>(net::Request::State::COMPLETED);
|
||||
ack_req->reply_time = now;
|
||||
sn->acked[a.value->id] = std::make_pair(std::move(ack_req), next_refresh_time);
|
||||
|
||||
/* step to clear announces */
|
||||
scheduler.edit(sr->nextSearchStep, now);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -553,8 +553,10 @@ Dht::searchStep(Sp<Search> sr)
|
||||
[this,ws,query](const net::Request& req, net::RequestAnswer&& answer) mutable
|
||||
{ /* on done */
|
||||
if (auto sr = ws.lock()) {
|
||||
onListenDone(req.node, answer, sr);
|
||||
scheduler.edit(sr->nextSearchStep, scheduler.time());
|
||||
if (auto sn = sr->getNode(req.node))
|
||||
scheduler.add(sn->getListenTime(query), std::bind(&Dht::searchStep, this, sr));
|
||||
onListenDone(req.node, answer, sr);
|
||||
}
|
||||
},
|
||||
[this,ws,query](const net::Request& req, bool over) mutable
|
||||
@@ -569,8 +571,8 @@ Dht::searchStep(Sp<Search> sr)
|
||||
[this,ws,query](const Sp<Node>& node, net::RequestAnswer&& answer) mutable
|
||||
{ /* on new values */
|
||||
if (auto sr = ws.lock()) {
|
||||
onGetValuesDone(node, answer, sr, query);
|
||||
scheduler.edit(sr->nextSearchStep, scheduler.time());
|
||||
onGetValuesDone(node, answer, sr, query);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -611,8 +613,8 @@ Dht::searchStep(Sp<Search> sr)
|
||||
/* dumpSearch(*sr, std::cout); */
|
||||
|
||||
/* periodic searchStep scheduling. */
|
||||
if (not sr->done)
|
||||
scheduler.edit(sr->nextSearchStep, sr->getNextStepTime(now));
|
||||
//if (not sr->done)
|
||||
// scheduler.edit(sr->nextSearchStep, now);
|
||||
}
|
||||
|
||||
unsigned Dht::refill(Dht::Search& sr) {
|
||||
@@ -670,8 +672,10 @@ Dht::search(const InfoHash& id, sa_family_t af, GetCallback gcb, QueryCallback q
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (not sr)
|
||||
throw DhtException("Can't create search");
|
||||
if (not sr) {
|
||||
DHT_LOG.e(id, "[search %s IPv%c] maximum number of searches reached !", id.toString().c_str(), (af == AF_INET) ? '4' : '6');
|
||||
return {};
|
||||
}
|
||||
}
|
||||
sr->af = af;
|
||||
sr->tid = search_id++;
|
||||
@@ -698,7 +702,7 @@ Dht::search(const InfoHash& id, sa_family_t af, GetCallback gcb, QueryCallback q
|
||||
|
||||
refill(*sr);
|
||||
if (sr->nextSearchStep)
|
||||
scheduler.edit(sr->nextSearchStep, sr->getNextStepTime(scheduler.time()));
|
||||
scheduler.edit(sr->nextSearchStep, scheduler.time());
|
||||
else
|
||||
sr->nextSearchStep = scheduler.add(scheduler.time(), std::bind(&Dht::searchStep, this, sr));
|
||||
|
||||
@@ -741,8 +745,9 @@ Dht::announce(const InfoHash& id,
|
||||
n.probe_query.reset();
|
||||
n.acked[value->id].first.reset();
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
a_sr->permanent = permanent;
|
||||
a_sr->created = created;
|
||||
if (a_sr->value != value) {
|
||||
a_sr->value = value;
|
||||
for (auto& n : sr->nodes) {
|
||||
@@ -754,9 +759,8 @@ Dht::announce(const InfoHash& id,
|
||||
if (a_sr->callback)
|
||||
a_sr->callback(true, {});
|
||||
a_sr->callback = {};
|
||||
if (callback) {
|
||||
if (callback)
|
||||
callback(true, {});
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
if (a_sr->callback)
|
||||
@@ -785,12 +789,12 @@ Dht::listenTo(const InfoHash& id, sa_family_t af, GetCallback cb, Value::Filter
|
||||
sr->done = false;
|
||||
auto token = ++sr->listener_token;
|
||||
sr->listeners.emplace(token, LocalListener{q, f, cb});
|
||||
scheduler.edit(sr->nextSearchStep, sr->getNextStepTime(now));
|
||||
scheduler.edit(sr->nextSearchStep, now);
|
||||
return token;
|
||||
}
|
||||
|
||||
size_t
|
||||
Dht::listen(const InfoHash& id, GetCallback cb, Value::Filter&& f, Where&& where)
|
||||
Dht::listen(const InfoHash& id, GetCallback cb, Value::Filter f, Where where)
|
||||
{
|
||||
scheduler.syncTime();
|
||||
|
||||
@@ -1133,7 +1137,6 @@ Dht::cancelPut(const InfoHash& id, const Value::Id& vid)
|
||||
void
|
||||
Dht::storageChanged(const InfoHash& id, Storage& st, ValueStorage& v)
|
||||
{
|
||||
DHT_LOG.d(id, "[store %s] changed", id.toString().c_str());
|
||||
if (not st.local_listeners.empty()) {
|
||||
DHT_LOG.d(id, "[store %s] %lu local listeners", id.toString().c_str(), st.local_listeners.size());
|
||||
std::vector<std::pair<GetCallback, std::vector<Sp<Value>>>> cbs;
|
||||
@@ -1153,20 +1156,22 @@ Dht::storageChanged(const InfoHash& id, Storage& st, ValueStorage& v)
|
||||
cb.first(cb.second);
|
||||
}
|
||||
|
||||
DHT_LOG.d(id, "[store %s] %lu remote listeners", id.toString().c_str(), st.listeners.size());
|
||||
for (const auto& node_listeners : st.listeners) {
|
||||
for (const auto& l : node_listeners.second) {
|
||||
auto f = l.second.query.where.getFilter();
|
||||
if (f and not f(*v.data))
|
||||
continue;
|
||||
DHT_LOG.w(id, node_listeners.first->id, "[store %s] [node %s] sending update",
|
||||
id.toString().c_str(),
|
||||
node_listeners.first->toString().c_str());
|
||||
std::vector<Sp<Value>> vals {};
|
||||
vals.push_back(v.data);
|
||||
Blob ntoken = makeToken(node_listeners.first->getAddr(), false);
|
||||
network_engine.tellListener(node_listeners.first, l.second.sid, id, 0, ntoken, {}, {},
|
||||
std::move(vals), l.second.query);
|
||||
if (not st.listeners.empty()) {
|
||||
DHT_LOG.d(id, "[store %s] %lu remote listeners", id.toString().c_str(), st.listeners.size());
|
||||
for (const auto& node_listeners : st.listeners) {
|
||||
for (const auto& l : node_listeners.second) {
|
||||
auto f = l.second.query.where.getFilter();
|
||||
if (f and not f(*v.data))
|
||||
continue;
|
||||
DHT_LOG.w(id, node_listeners.first->id, "[store %s] [node %s] sending update",
|
||||
id.toString().c_str(),
|
||||
node_listeners.first->toString().c_str());
|
||||
std::vector<Sp<Value>> vals {};
|
||||
vals.push_back(v.data);
|
||||
Blob ntoken = makeToken(node_listeners.first->getAddr(), false);
|
||||
network_engine.tellListener(node_listeners.first, l.first, id, 0, ntoken, {}, {},
|
||||
std::move(vals), l.second.query);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1227,10 +1232,10 @@ Dht::storageAddListener(const InfoHash& id, const Sp<Node>& node, size_t socket_
|
||||
buckets4.findClosestNodes(id, now, TARGET_NODES), buckets6.findClosestNodes(id, now, TARGET_NODES),
|
||||
std::move(vals), query);
|
||||
}
|
||||
node_listeners->second.emplace(socket_id, Listener {socket_id, now, std::forward<Query>(query)});
|
||||
node_listeners->second.emplace(socket_id, Listener {now, std::forward<Query>(query)});
|
||||
}
|
||||
else
|
||||
l->second.refresh(socket_id, now, std::forward<Query>(query));
|
||||
l->second.refresh(now, std::forward<Query>(query));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1256,8 +1261,7 @@ void
|
||||
Dht::expireStore()
|
||||
{
|
||||
// removing expired values
|
||||
auto i = store.begin();
|
||||
while (i != store.end()) {
|
||||
for (auto i = store.begin(); i != store.end();) {
|
||||
expireStore(i);
|
||||
|
||||
if (i->second.empty() && i->second.listeners.empty() && i->second.local_listeners.empty()) {
|
||||
@@ -1291,10 +1295,17 @@ Dht::expireStore()
|
||||
DHT_LOG.w("Discarded %ld bytes, still %ld used", largest->first.toString().c_str(), total_store_size);
|
||||
if (ret.values_diff)
|
||||
break;
|
||||
} else
|
||||
std::cout << "exp_value not found " << exp_value.first << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove unused quota entires
|
||||
for (auto i = store_quota.begin(); i != store_quota.end();) {
|
||||
if (i->second.size() == 0)
|
||||
i = store_quota.erase(i);
|
||||
else
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1431,10 +1442,8 @@ Dht::dumpSearch(const Search& sr, std::ostream& out) const
|
||||
out << " [expired]";
|
||||
bool synced = sr.isSynced(now);
|
||||
out << (synced ? " [synced]" : " [not synced]");
|
||||
if (synced && sr.isListening(now)) {
|
||||
auto lt = sr.getListenTime(now);
|
||||
out << " [listening, next in " << duration_cast<seconds>(lt-now).count() << " s]";
|
||||
}
|
||||
if (synced && sr.isListening(now))
|
||||
out << " [listening]";
|
||||
out << std::endl;
|
||||
|
||||
/*printing the queries*/
|
||||
@@ -1534,12 +1543,6 @@ Dht::dumpTables() const
|
||||
DHT_LOG.d("%s", out.str().c_str());
|
||||
}
|
||||
|
||||
void
|
||||
Dht::printStorageQuota(std::ostream& out, const decltype(store_quota)::value_type& ip) const
|
||||
{
|
||||
out << "IP " << ip.first.toString() << " uses " << ip.second.size() << " bytes" << std::endl;
|
||||
}
|
||||
|
||||
std::string
|
||||
Dht::getStorageLog() const
|
||||
{
|
||||
@@ -1552,9 +1555,12 @@ Dht::getStorageLog() const
|
||||
else
|
||||
out << (total_store_size/1024) << " KB)";
|
||||
out << std::endl << std::endl;
|
||||
for (const auto& ip : store_quota) {
|
||||
printStorageQuota(out, ip);
|
||||
}
|
||||
std::multimap<size_t, const SockAddr*> q_map;
|
||||
for (const auto& ip : store_quota)
|
||||
if (ip.second.size())
|
||||
q_map.emplace(ip.second.size(), &ip.first);
|
||||
for (auto ip = q_map.rbegin(); ip != q_map.rend(); ++ip)
|
||||
out << "IP " << ip->second->toString() << " uses " << ip->first << " bytes" << std::endl;
|
||||
out << std::endl;
|
||||
return out.str();
|
||||
}
|
||||
@@ -1583,15 +1589,9 @@ Dht::printStorageLog(const decltype(store)::value_type& s) const
|
||||
<< st.totalSize() << " bytes)" << std::endl;
|
||||
if (not st.local_listeners.empty())
|
||||
out << " " << st.local_listeners.size() << " local listeners" << std::endl;
|
||||
const auto& now = scheduler.time();
|
||||
for (const auto& node_listeners : st.listeners) {
|
||||
const auto& node = node_listeners.first;
|
||||
for (const auto& l : node_listeners.second) {
|
||||
out << " " << "Listener " << node->toString();
|
||||
auto since = duration_cast<seconds>(now - l.second.time);
|
||||
auto expires = duration_cast<seconds>(l.second.time + Node::NODE_EXPIRE_TIME - now);
|
||||
out << " (since " << since.count() << "s, exp in " << expires.count() << "s)" << std::endl;
|
||||
}
|
||||
out << " " << "Listener " << node->toString() << " : " << node_listeners.second.size() << " entries" << std::endl;
|
||||
}
|
||||
return out.str();
|
||||
}
|
||||
@@ -1655,8 +1655,8 @@ Dht::~Dht()
|
||||
|
||||
Dht::Dht() : store(), scheduler(DHT_LOG), network_engine(DHT_LOG, scheduler) {}
|
||||
|
||||
Dht::Dht(int s, int s6, Config config)
|
||||
: myid(config.node_id != zeroes ? config.node_id : InfoHash::getRandom()),
|
||||
Dht::Dht(const int& s, const int& s6, Config config)
|
||||
: myid(config.node_id ? config.node_id : InfoHash::getRandom()),
|
||||
is_bootstrap(config.is_bootstrap),
|
||||
maintain_storage(config.maintain_storage), store(), store_quota(),
|
||||
scheduler(DHT_LOG),
|
||||
@@ -1968,7 +1968,7 @@ Dht::importValues(const std::vector<ValuesExport>& import)
|
||||
throw msgpack::type_error();
|
||||
for (unsigned i = 0; i < valarr.via.array.size; i++) {
|
||||
auto& valel = valarr.via.array.ptr[i];
|
||||
if (valel.via.array.size < 2)
|
||||
if (valel.type != msgpack::type::ARRAY or valel.via.array.size < 2)
|
||||
throw msgpack::type_error();
|
||||
time_point val_time;
|
||||
Value tmp_val;
|
||||
@@ -2065,6 +2065,7 @@ Dht::onError(Sp<net::Request> req, net::DhtProtocolException e) {
|
||||
n.token.clear();
|
||||
n.last_get_reply = time_point::min();
|
||||
searchSendGetValues(sr);
|
||||
scheduler.edit(sr->nextSearchStep, scheduler.time());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2103,7 +2104,7 @@ Dht::onFindNode(Sp<Node> node, const InfoHash& target, want_t want)
|
||||
net::RequestAnswer
|
||||
Dht::onGetValues(Sp<Node> node, const InfoHash& hash, want_t, const Query& query)
|
||||
{
|
||||
if (hash == zeroes) {
|
||||
if (not hash) {
|
||||
DHT_LOG.w("[node %s] Eek! Got get_values with no info_hash", node->toString().c_str());
|
||||
throw net::DhtProtocolException {
|
||||
net::DhtProtocolException::NON_AUTHORITATIVE_INFORMATION,
|
||||
@@ -2119,8 +2120,6 @@ Dht::onGetValues(Sp<Node> node, const InfoHash& hash, want_t, const Query& query
|
||||
if (st != store.end() && not st->second.empty()) {
|
||||
answer.values = st->second.get(query.where.getFilter());
|
||||
DHT_LOG.d(hash, "[node %s] sending %u values", node->toString().c_str(), answer.values.size());
|
||||
} else {
|
||||
DHT_LOG.d(hash, "[node %s] sending nodes", node->toString().c_str());
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
@@ -2135,14 +2134,13 @@ void Dht::onGetValuesDone(const Sp<Node>& node,
|
||||
return;
|
||||
}
|
||||
|
||||
DHT_LOG.d(sr->id, "[search %s] [node %s] got reply to 'get' with %u nodes",
|
||||
sr->id.toString().c_str(), node->toString().c_str(), a.nodes4.size()+a.nodes6.size());
|
||||
/*DHT_LOG.d(sr->id, "[search %s] [node %s] got reply to 'get' with %u nodes",
|
||||
sr->id.toString().c_str(), node->toString().c_str(), a.nodes4.size()+a.nodes6.size());*/
|
||||
|
||||
if (not a.ntoken.empty()) {
|
||||
if (not a.values.empty() or not a.fields.empty()) {
|
||||
DHT_LOG.d(sr->id, "[search %s IPv%c] found %u values",
|
||||
sr->id.toString().c_str(), sr->af == AF_INET ? '4' : '6',
|
||||
a.values.size());
|
||||
DHT_LOG.d(sr->id, node->id, "[search %s] [node %s] found %u values",
|
||||
sr->id.toString().c_str(), node->toString().c_str(), a.values.size());
|
||||
for (auto& getp : sr->callbacks) { /* call all callbacks for this search */
|
||||
auto& get = getp.second;
|
||||
if (not (get.get_cb or get.query_cb) or
|
||||
@@ -2200,14 +2198,14 @@ void Dht::onGetValuesDone(const Sp<Node>& node,
|
||||
net::RequestAnswer
|
||||
Dht::onListen(Sp<Node> node, const InfoHash& hash, const Blob& token, size_t socket_id, const Query& query)
|
||||
{
|
||||
if (hash == zeroes) {
|
||||
if (not hash) {
|
||||
DHT_LOG.w(node->id, "[node %s] listen with no info_hash", node->toString().c_str());
|
||||
throw net::DhtProtocolException {
|
||||
net::DhtProtocolException::NON_AUTHORITATIVE_INFORMATION,
|
||||
net::DhtProtocolException::LISTEN_NO_INFOHASH
|
||||
};
|
||||
}
|
||||
if (!tokenMatch(token, node->getAddr())) {
|
||||
if (not tokenMatch(token, node->getAddr())) {
|
||||
DHT_LOG.w(hash, node->id, "[node %s] incorrect token %s for 'listen'", node->toString().c_str(), hash.toString().c_str());
|
||||
throw net::DhtProtocolException {net::DhtProtocolException::UNAUTHORIZED, net::DhtProtocolException::LISTEN_WRONG_TOKEN};
|
||||
}
|
||||
@@ -2227,7 +2225,7 @@ Dht::onListenDone(const Sp<Node>& node,
|
||||
if (not sr->done) {
|
||||
const auto& now = scheduler.time();
|
||||
searchSendGetValues(sr);
|
||||
scheduler.edit(sr->nextSearchStep, sr->getNextStepTime(now));
|
||||
scheduler.edit(sr->nextSearchStep, now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2239,7 +2237,7 @@ Dht::onAnnounce(Sp<Node> n,
|
||||
const time_point& creation_date)
|
||||
{
|
||||
auto& node = *n;
|
||||
if (hash == zeroes) {
|
||||
if (not hash) {
|
||||
DHT_LOG.w(node.id, "put with no info_hash");
|
||||
throw net::DhtProtocolException {
|
||||
net::DhtProtocolException::NON_AUTHORITATIVE_INFORMATION,
|
||||
|
||||
@@ -0,0 +1,796 @@
|
||||
/*
|
||||
* Copyright (C) 2016-2018 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
* Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
|
||||
#include "dht_proxy_client.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <json/json.h>
|
||||
#include <restbed>
|
||||
#include <vector>
|
||||
|
||||
#include "dhtrunner.h"
|
||||
|
||||
constexpr const char* const HTTP_PROTO {"http://"};
|
||||
|
||||
namespace dht {
|
||||
|
||||
DhtProxyClient::DhtProxyClient(std::function<void()> signal, const std::string& serverHost, const std::string& pushClientId)
|
||||
: serverHost_(serverHost), pushClientId_(pushClientId), scheduler(DHT_LOG), loopSignal_(signal)
|
||||
{
|
||||
if (!serverHost_.empty())
|
||||
startProxy();
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyClient::confirmProxy()
|
||||
{
|
||||
if (serverHost_.empty()) return;
|
||||
getConnectivityStatus();
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyClient::startProxy()
|
||||
{
|
||||
if (serverHost_.empty()) return;
|
||||
DHT_LOG.WARN("Staring proxy client to %s", serverHost_.c_str());
|
||||
nextProxyConfirmation = scheduler.add(scheduler.time(), std::bind(&DhtProxyClient::confirmProxy, this));
|
||||
}
|
||||
|
||||
DhtProxyClient::~DhtProxyClient()
|
||||
{
|
||||
cancelAllOperations();
|
||||
cancelAllListeners();
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyClient::cancelAllOperations()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(lockOperations_);
|
||||
auto operation = operations_.begin();
|
||||
while (operation != operations_.end()) {
|
||||
if (operation->thread.joinable()) {
|
||||
// Close connection to stop operation?
|
||||
restbed::Http::close(operation->req);
|
||||
operation->thread.join();
|
||||
operation = operations_.erase(operation);
|
||||
} else {
|
||||
++operation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyClient::cancelAllListeners()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(lockListener_);
|
||||
for (auto& listener: listeners_) {
|
||||
if (listener.thread.joinable()) {
|
||||
// Close connection to stop listener?
|
||||
if (listener.req)
|
||||
restbed::Http::close(listener.req);
|
||||
listener.thread.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyClient::shutdown(ShutdownCallback cb)
|
||||
{
|
||||
cancelAllOperations();
|
||||
cancelAllListeners();
|
||||
if (cb)
|
||||
cb();
|
||||
}
|
||||
|
||||
NodeStatus
|
||||
DhtProxyClient::getStatus(sa_family_t af) const
|
||||
{
|
||||
std::lock_guard<std::mutex> l(lockCurrentProxyInfos_);
|
||||
switch (af)
|
||||
{
|
||||
case AF_INET:
|
||||
return statusIpv4_;
|
||||
case AF_INET6:
|
||||
return statusIpv6_;
|
||||
default:
|
||||
return NodeStatus::Disconnected;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
DhtProxyClient::isRunning(sa_family_t af) const
|
||||
{
|
||||
std::lock_guard<std::mutex> l(lockCurrentProxyInfos_);
|
||||
switch (af)
|
||||
{
|
||||
case AF_INET:
|
||||
return statusIpv4_ != NodeStatus::Disconnected;
|
||||
case AF_INET6:
|
||||
return statusIpv6_ != NodeStatus::Disconnected;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
time_point
|
||||
DhtProxyClient::periodic(const uint8_t*, size_t, const SockAddr&)
|
||||
{
|
||||
// Exec all currently stored callbacks
|
||||
scheduler.syncTime();
|
||||
if (!callbacks_.empty()) {
|
||||
std::lock_guard<std::mutex> lock(lockCallbacks);
|
||||
for (auto& callback : callbacks_)
|
||||
callback();
|
||||
callbacks_.clear();
|
||||
}
|
||||
// Remove finished operations
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(lockOperations_);
|
||||
auto operation = operations_.begin();
|
||||
while (operation != operations_.end()) {
|
||||
if (*(operation->finished)) {
|
||||
if (operation->thread.joinable()) {
|
||||
// Close connection to stop operation?
|
||||
restbed::Http::close(operation->req);
|
||||
operation->thread.join();
|
||||
}
|
||||
operation = operations_.erase(operation);
|
||||
} else {
|
||||
++operation;
|
||||
}
|
||||
}
|
||||
}
|
||||
return scheduler.run();
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyClient::get(const InfoHash& key, const GetCallback& cb, DoneCallback donecb, const Value::Filter& filterChain)
|
||||
{
|
||||
restbed::Uri uri(HTTP_PROTO + serverHost_ + "/" + key.toString());
|
||||
auto req = std::make_shared<restbed::Request>(uri);
|
||||
|
||||
auto finished = std::make_shared<std::atomic_bool>(false);
|
||||
Operation o;
|
||||
o.req = req;
|
||||
o.finished = finished;
|
||||
o.thread = std::thread([=](){
|
||||
// Try to contact the proxy and set the status to connected when done.
|
||||
// will change the connectivity status
|
||||
auto ok = std::make_shared<std::atomic_bool>(true);
|
||||
restbed::Http::async(req,
|
||||
[=](const std::shared_ptr<restbed::Request>& req,
|
||||
const std::shared_ptr<restbed::Response>& reply) {
|
||||
auto code = reply->get_status_code();
|
||||
|
||||
if (code == 200) {
|
||||
try {
|
||||
while (restbed::Http::is_open(req) and not *finished) {
|
||||
restbed::Http::fetch("\n", reply);
|
||||
if (*finished)
|
||||
break;
|
||||
std::string body;
|
||||
reply->get_body(body);
|
||||
reply->set_body(""); // Reset the body for the next fetch
|
||||
|
||||
std::string err;
|
||||
Json::Value json;
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto* char_data = reinterpret_cast<const char*>(&body[0]);
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
if (reader->parse(char_data, char_data + body.size(), &json, &err)) {
|
||||
auto value = std::make_shared<Value>(json);
|
||||
if ((not filterChain or filterChain(*value)) && cb) {
|
||||
std::lock_guard<std::mutex> lock(lockCallbacks);
|
||||
callbacks_.emplace_back([cb, value, finished]() {
|
||||
if (not cb({value}))
|
||||
*finished = true;
|
||||
});
|
||||
loopSignal_();
|
||||
}
|
||||
} else {
|
||||
*ok = false;
|
||||
}
|
||||
}
|
||||
} catch (std::runtime_error& e) { }
|
||||
} else {
|
||||
*ok = false;
|
||||
}
|
||||
}).wait();
|
||||
if (donecb) {
|
||||
std::lock_guard<std::mutex> lock(lockCallbacks);
|
||||
callbacks_.emplace_back([=](){
|
||||
donecb(*ok, {});
|
||||
});
|
||||
loopSignal_();
|
||||
}
|
||||
if (!ok) {
|
||||
// Connection failed, update connectivity
|
||||
opFailed();
|
||||
}
|
||||
*finished = true;
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(lockOperations_);
|
||||
operations_.emplace_back(std::move(o));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyClient::get(const InfoHash& key, GetCallback cb, DoneCallback donecb,
|
||||
Value::Filter&& filter, Where&& where)
|
||||
{
|
||||
Query query {{}, where};
|
||||
auto filterChain = filter.chain(query.where.getFilter());
|
||||
get(key, cb, donecb, filterChain);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyClient::put(const InfoHash& key, Sp<Value> val, DoneCallback cb, time_point, bool permanent)
|
||||
{
|
||||
restbed::Uri uri(HTTP_PROTO + serverHost_ + "/" + key.toString());
|
||||
auto req = std::make_shared<restbed::Request>(uri);
|
||||
req->set_method("POST");
|
||||
auto json = val->toJson();
|
||||
if (permanent)
|
||||
json["permanent"] = true;
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["commentStyle"] = "None";
|
||||
wbuilder["indentation"] = "";
|
||||
auto body = Json::writeString(wbuilder, json) + "\n";
|
||||
req->set_body(body);
|
||||
req->set_header("Content-Length", std::to_string(body.size()));
|
||||
|
||||
auto finished = std::make_shared<std::atomic_bool>(false);
|
||||
Operation o;
|
||||
o.req = req;
|
||||
o.finished = finished;
|
||||
o.thread = std::thread([=](){
|
||||
auto ok = std::make_shared<std::atomic_bool>(true);
|
||||
restbed::Http::async(req,
|
||||
[this, ok](const std::shared_ptr<restbed::Request>& /*req*/,
|
||||
const std::shared_ptr<restbed::Response>& reply) {
|
||||
auto code = reply->get_status_code();
|
||||
|
||||
if (code == 200) {
|
||||
restbed::Http::fetch("\n", reply);
|
||||
std::string body;
|
||||
reply->get_body(body);
|
||||
reply->set_body(""); // Reset the body for the next fetch
|
||||
|
||||
try {
|
||||
std::string err;
|
||||
Json::Value json;
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto* char_data = reinterpret_cast<const char*>(&body[0]);
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
if (not reader->parse(char_data, char_data + body.size(), &json, &err))
|
||||
*ok = false;
|
||||
} catch (...) {
|
||||
*ok = false;
|
||||
}
|
||||
} else {
|
||||
*ok = false;
|
||||
}
|
||||
}).wait();
|
||||
if (cb) {
|
||||
std::lock_guard<std::mutex> lock(lockCallbacks);
|
||||
callbacks_.emplace_back([=](){
|
||||
cb(*ok, {});
|
||||
});
|
||||
loopSignal_();
|
||||
}
|
||||
if (!ok) {
|
||||
// Connection failed, update connectivity
|
||||
opFailed();
|
||||
}
|
||||
*finished = true;
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(lockOperations_);
|
||||
operations_.emplace_back(std::move(o));
|
||||
}
|
||||
}
|
||||
|
||||
NodeStats
|
||||
DhtProxyClient::getNodesStats(sa_family_t af) const
|
||||
{
|
||||
return af == AF_INET ? stats4_ : stats6_;
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyClient::getProxyInfos()
|
||||
{
|
||||
DHT_LOG.DEBUG("Requesting proxy server node information");
|
||||
|
||||
if (ongoingStatusUpdate_.test_and_set())
|
||||
return;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> l(lockCurrentProxyInfos_);
|
||||
if (statusIpv4_ == NodeStatus::Disconnected)
|
||||
statusIpv4_ = NodeStatus::Connecting;
|
||||
if (statusIpv6_ == NodeStatus::Disconnected)
|
||||
statusIpv6_ = NodeStatus::Connecting;
|
||||
}
|
||||
|
||||
restbed::Uri uri(HTTP_PROTO + serverHost_ + "/");
|
||||
auto req = std::make_shared<restbed::Request>(uri);
|
||||
|
||||
// Try to contact the proxy and set the status to connected when done.
|
||||
// will change the connectivity status
|
||||
statusThread_ = std::thread([this, req]{
|
||||
restbed::Http::async(req,
|
||||
[this](const std::shared_ptr<restbed::Request>&,
|
||||
const std::shared_ptr<restbed::Response>& reply) {
|
||||
auto code = reply->get_status_code();
|
||||
Json::Value proxyInfos;
|
||||
if (code == 200) {
|
||||
restbed::Http::fetch("\n", reply);
|
||||
std::string body;
|
||||
reply->get_body(body);
|
||||
|
||||
std::string err;
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
try {
|
||||
reader->parse(body.data(), body.data() + body.size(), &proxyInfos, &err);
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
onProxyInfos(proxyInfos);
|
||||
ongoingStatusUpdate_.clear();
|
||||
});
|
||||
});
|
||||
statusThread_.detach();
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyClient::onProxyInfos(const Json::Value& proxyInfos)
|
||||
{
|
||||
std::lock_guard<std::mutex> l(lockCurrentProxyInfos_);
|
||||
|
||||
auto oldStatus = std::max(statusIpv4_, statusIpv6_);
|
||||
|
||||
try {
|
||||
myid = InfoHash(proxyInfos["node_id"].asString());
|
||||
|
||||
stats4_ = NodeStats(proxyInfos["ipv4"]);
|
||||
if (stats4_.good_nodes)
|
||||
statusIpv4_ = NodeStatus::Connected;
|
||||
else if (stats4_.dubious_nodes)
|
||||
statusIpv4_ = NodeStatus::Connecting;
|
||||
else
|
||||
statusIpv4_ = NodeStatus::Disconnected;
|
||||
|
||||
stats6_ = NodeStats(proxyInfos["ipv6"]);
|
||||
if (stats6_.good_nodes)
|
||||
statusIpv6_ = NodeStatus::Connected;
|
||||
else if (stats6_.dubious_nodes)
|
||||
statusIpv6_ = NodeStatus::Connecting;
|
||||
else
|
||||
statusIpv6_ = NodeStatus::Disconnected;
|
||||
|
||||
publicAddress_ = parsePublicAddress(proxyInfos["public_ip"]);
|
||||
} catch (...) {}
|
||||
|
||||
auto newStatus = std::max(statusIpv4_, statusIpv6_);
|
||||
|
||||
if (newStatus == NodeStatus::Connecting || newStatus == NodeStatus::Connected) {
|
||||
if (oldStatus == NodeStatus::Disconnected) {
|
||||
restartListeners();
|
||||
}
|
||||
scheduler.edit(nextProxyConfirmation, scheduler.time() + std::chrono::minutes(15));
|
||||
}
|
||||
else if (newStatus == NodeStatus::Disconnected) {
|
||||
scheduler.edit(nextProxyConfirmation, scheduler.time() + std::chrono::minutes(1));
|
||||
}
|
||||
loopSignal_();
|
||||
}
|
||||
|
||||
SockAddr
|
||||
DhtProxyClient::parsePublicAddress(const Json::Value& val)
|
||||
{
|
||||
auto public_ip = val.asString();
|
||||
auto endIp = public_ip.find_last_of(':');
|
||||
std::string service = public_ip.substr(endIp + 1);
|
||||
std::string address = public_ip.substr(0, endIp - 1);
|
||||
auto sa = SockAddr::resolve(address, service);
|
||||
if (sa.empty()) return {};
|
||||
return sa.front().getMappedIPv4();
|
||||
}
|
||||
|
||||
std::vector<SockAddr>
|
||||
DhtProxyClient::getPublicAddress(sa_family_t family)
|
||||
{
|
||||
std::lock_guard<std::mutex> l(lockCurrentProxyInfos_);
|
||||
if (not publicAddress_) return {};
|
||||
return publicAddress_.getFamily() == family ? std::vector<SockAddr>{publicAddress_} : std::vector<SockAddr>{};
|
||||
}
|
||||
|
||||
size_t
|
||||
DhtProxyClient::listen(const InfoHash& key, GetCallback cb, Value::Filter filter, Where where)
|
||||
{
|
||||
restbed::Uri uri(HTTP_PROTO + serverHost_ + "/" + key.toString());
|
||||
auto req = std::make_shared<restbed::Request>(uri);
|
||||
req->set_method(deviceKey_.empty() ? "LISTEN" : "SUBSCRIBE");
|
||||
|
||||
Query query {{}, where};
|
||||
auto filterChain = filter.chain(query.where.getFilter());
|
||||
auto pushNotifToken = std::make_shared<unsigned>(0);
|
||||
|
||||
unsigned callbackId = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(lockCallback_);
|
||||
callbackId_ += 1;
|
||||
callbackId = callbackId_;
|
||||
}
|
||||
|
||||
Listener l;
|
||||
++listener_token_;
|
||||
l.key = key.toString();
|
||||
l.token = listener_token_;
|
||||
l.req = req;
|
||||
l.cb = cb;
|
||||
l.callbackId = callbackId;
|
||||
l.pushNotifToken = pushNotifToken;
|
||||
l.filterChain = std::move(filterChain);
|
||||
l.thread = std::thread([=]()
|
||||
{
|
||||
auto settings = std::make_shared<restbed::Settings>();
|
||||
if (deviceKey_.empty()) {
|
||||
std::chrono::milliseconds timeout(std::numeric_limits<int>::max());
|
||||
settings->set_connection_timeout(timeout); // Avoid the client to close the socket after 5 seconds.
|
||||
}
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
else
|
||||
fillBodyToGetToken(req, callbackId);
|
||||
#endif
|
||||
|
||||
struct State {
|
||||
std::atomic_bool ok {true};
|
||||
std::atomic_bool cancel {false};
|
||||
};
|
||||
auto state = std::make_shared<State>();
|
||||
restbed::Http::async(req,
|
||||
[this, filterChain, cb, pushNotifToken, state](const std::shared_ptr<restbed::Request>& req,
|
||||
const std::shared_ptr<restbed::Response>& reply) {
|
||||
auto code = reply->get_status_code();
|
||||
if (code == 200) {
|
||||
try {
|
||||
std::string err;
|
||||
Json::Value json;
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
if (!deviceKey_.empty()) {
|
||||
restbed::Http::fetch("\n", reply);
|
||||
if (state->cancel)
|
||||
return;
|
||||
std::string body;
|
||||
reply->get_body(body);
|
||||
|
||||
auto* char_data = reinterpret_cast<const char*>(&body[0]);
|
||||
if (reader->parse(char_data, char_data + body.size(), &json, &err)) {
|
||||
if (!json.isMember("token")) return;
|
||||
*pushNotifToken = json["token"].asLargestUInt();
|
||||
} else {
|
||||
state->ok = false;
|
||||
}
|
||||
} else {
|
||||
while (restbed::Http::is_open(req) and not state->cancel) {
|
||||
restbed::Http::fetch("\n", reply);
|
||||
if (state->cancel)
|
||||
break;
|
||||
std::string body;
|
||||
reply->get_body(body);
|
||||
reply->set_body(""); // Reset the body for the next fetch
|
||||
|
||||
auto* char_data = reinterpret_cast<const char*>(&body[0]);
|
||||
if (reader->parse(char_data, char_data + body.size(), &json, &err)) {
|
||||
auto value = std::make_shared<Value>(json);
|
||||
if ((not filterChain or filterChain(*value)) && cb) {
|
||||
std::lock_guard<std::mutex> lock(lockCallbacks);
|
||||
callbacks_.emplace_back([cb, value, state]() {
|
||||
if (not state->cancel and not cb({value})) {
|
||||
state->cancel = true;
|
||||
}
|
||||
});
|
||||
loopSignal_();
|
||||
}
|
||||
} else {
|
||||
state->ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (std::runtime_error&) {
|
||||
state->ok = false;
|
||||
}
|
||||
} else {
|
||||
state->ok = false;
|
||||
}
|
||||
}, settings).get();
|
||||
if (not state->ok) {
|
||||
opFailed();
|
||||
}
|
||||
}
|
||||
);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(lockListener_);
|
||||
listeners_.emplace_back(std::move(l));
|
||||
}
|
||||
return listener_token_;
|
||||
}
|
||||
|
||||
bool
|
||||
DhtProxyClient::cancelListen(const InfoHash&, size_t token)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(lockListener_);
|
||||
for (auto it = listeners_.begin(); it != listeners_.end(); ++it) {
|
||||
auto& listener = *it;
|
||||
if (listener.token == token) {
|
||||
if (!deviceKey_.empty()) {
|
||||
// First, be sure to have a token
|
||||
if (listener.thread.joinable()) {
|
||||
listener.thread.join();
|
||||
}
|
||||
// UNSUBSCRIBE
|
||||
restbed::Uri uri(HTTP_PROTO + serverHost_ + "/" + listener.key);
|
||||
auto req = std::make_shared<restbed::Request>(uri);
|
||||
req->set_method("UNSUBSCRIBE");
|
||||
// fill request body
|
||||
Json::Value body;
|
||||
body["key"] = deviceKey_;
|
||||
body["client_id"] = pushClientId_;
|
||||
body["token"] = std::to_string(token);
|
||||
body["callback_id"] = listener.callbackId;
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["commentStyle"] = "None";
|
||||
wbuilder["indentation"] = "";
|
||||
auto content = Json::writeString(wbuilder, body) + "\n";
|
||||
std::replace(content.begin(), content.end(), '\n', ' ');
|
||||
req->set_body(content);
|
||||
req->set_header("Content-Length", std::to_string(content.size()));
|
||||
|
||||
restbed::Http::async(req,
|
||||
[](const std::shared_ptr<restbed::Request>&,
|
||||
const std::shared_ptr<restbed::Response>&){}
|
||||
);
|
||||
// And remove
|
||||
listeners_.erase(it);
|
||||
return true;
|
||||
} else {
|
||||
// Just stop the request
|
||||
if (listener.thread.joinable()) {
|
||||
// Close connection to stop listener?
|
||||
if (listener.req)
|
||||
restbed::Http::close(listener.req);
|
||||
listener.thread.join();
|
||||
listeners_.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyClient::opFailed()
|
||||
{
|
||||
DHT_LOG.ERR("Proxy request failed");
|
||||
{
|
||||
std::lock_guard<std::mutex> l(lockCurrentProxyInfos_);
|
||||
statusIpv4_ = NodeStatus::Disconnected;
|
||||
statusIpv6_ = NodeStatus::Disconnected;
|
||||
}
|
||||
getConnectivityStatus();
|
||||
loopSignal_();
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyClient::getConnectivityStatus()
|
||||
{
|
||||
getProxyInfos();
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyClient::restartListeners()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(lockListener_);
|
||||
for (auto& listener: listeners_) {
|
||||
if (listener.thread.joinable())
|
||||
listener.thread.join();
|
||||
// Redo listen
|
||||
auto filterChain = listener.filterChain;
|
||||
auto cb = listener.cb;
|
||||
restbed::Uri uri(HTTP_PROTO + serverHost_ + "/" + listener.key);
|
||||
auto req = std::make_shared<restbed::Request>(uri);
|
||||
req->set_method("LISTEN");
|
||||
listener.req = req;
|
||||
listener.thread = std::thread([this, filterChain, cb, req]()
|
||||
{
|
||||
auto settings = std::make_shared<restbed::Settings>();
|
||||
std::chrono::milliseconds timeout(std::numeric_limits<int>::max());
|
||||
settings->set_connection_timeout(timeout); // Avoid the client to close the socket after 5 seconds.
|
||||
|
||||
auto ok = std::make_shared<std::atomic_bool>(true);
|
||||
restbed::Http::async(req,
|
||||
[this, filterChain, cb, ok](const std::shared_ptr<restbed::Request>& req,
|
||||
const std::shared_ptr<restbed::Response>& reply) {
|
||||
auto code = reply->get_status_code();
|
||||
|
||||
if (code == 200) {
|
||||
try {
|
||||
while (restbed::Http::is_open(req)) {
|
||||
restbed::Http::fetch("\n", reply);
|
||||
std::string body;
|
||||
reply->get_body(body);
|
||||
reply->set_body(""); // Reset the body for the next fetch
|
||||
|
||||
Json::Value json;
|
||||
std::string err;
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto* char_data = reinterpret_cast<const char*>(&body[0]);
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
if (reader->parse(char_data, char_data + body.size(), &json, &err)) {
|
||||
auto value = std::make_shared<Value>(json);
|
||||
if ((not filterChain or filterChain(*value)) && cb) {
|
||||
auto okCb = std::make_shared<std::promise<bool>>();
|
||||
auto futureCb = okCb->get_future();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(lockCallbacks);
|
||||
callbacks_.emplace_back([cb, value, okCb](){
|
||||
okCb->set_value(cb({value}));
|
||||
});
|
||||
loopSignal_();
|
||||
}
|
||||
futureCb.wait();
|
||||
if (!futureCb.get()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (std::runtime_error&) {
|
||||
// NOTE: Http::close() can occurs here. Ignore this.
|
||||
}
|
||||
} else {
|
||||
*ok = false;
|
||||
}
|
||||
}, settings).get();
|
||||
if (!ok) opFailed();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyClient::pushNotificationReceived(const std::map<std::string, std::string>& notification)
|
||||
{
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
try {
|
||||
auto token = std::stoul(notification.at("token"));
|
||||
for (const auto& listener: listeners_) {
|
||||
if (*(listener.pushNotifToken) != token)
|
||||
continue;
|
||||
if (notification.find("timeout") == notification.cend()) {
|
||||
// Wake up daemon and get values
|
||||
get(InfoHash(listener.key), listener.cb, {}, listener.filterChain);
|
||||
} else {
|
||||
// A timeout has occured, we need to relaunch the listener
|
||||
resubscribe(token);
|
||||
}
|
||||
|
||||
}
|
||||
} catch (...) {
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyClient::resubscribe(const unsigned token)
|
||||
{
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
if (deviceKey_.empty()) return;
|
||||
for (auto& listener: listeners_) {
|
||||
if (*(listener.pushNotifToken) == token) {
|
||||
// Subscribe
|
||||
restbed::Uri uri(HTTP_PROTO + serverHost_ + "/" + listener.key);
|
||||
auto req = std::make_shared<restbed::Request>(uri);
|
||||
req->set_method("SUBSCRIBE");
|
||||
|
||||
auto pushNotifToken = std::make_shared<unsigned>(0);
|
||||
|
||||
if (listener.thread.joinable())
|
||||
listener.thread.join();
|
||||
listener.req = req;
|
||||
listener.pushNotifToken = pushNotifToken;
|
||||
auto callbackId = listener.callbackId;
|
||||
listener.thread = std::thread([=]()
|
||||
{
|
||||
fillBodyToGetToken(req, callbackId);
|
||||
auto settings = std::make_shared<restbed::Settings>();
|
||||
auto ok = std::make_shared<std::atomic_bool>(true);
|
||||
restbed::Http::async(req,
|
||||
[this, pushNotifToken, ok](const std::shared_ptr<restbed::Request>&,
|
||||
const std::shared_ptr<restbed::Response>& reply) {
|
||||
auto code = reply->get_status_code();
|
||||
if (code == 200) {
|
||||
try {
|
||||
restbed::Http::fetch("\n", reply);
|
||||
std::string body;
|
||||
reply->get_body(body);
|
||||
|
||||
std::string err;
|
||||
Json::Value json;
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto* char_data = reinterpret_cast<const char*>(&body[0]);
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
if (reader->parse(char_data, char_data + body.size(), &json, &err)) {
|
||||
if (!json.isMember("token")) return;
|
||||
*pushNotifToken = json["token"].asLargestUInt();
|
||||
}
|
||||
} catch (std::runtime_error&) {
|
||||
// NOTE: Http::close() can occurs here. Ignore this.
|
||||
}
|
||||
} else {
|
||||
*ok = false;
|
||||
}
|
||||
}, settings).get();
|
||||
if (!ok) opFailed();
|
||||
});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
void
|
||||
DhtProxyClient::fillBodyToGetToken(std::shared_ptr<restbed::Request> req, unsigned callbackId)
|
||||
{
|
||||
// Fill body with
|
||||
// {
|
||||
// "key":"device_key",
|
||||
// "callback_id": xxx
|
||||
// }
|
||||
Json::Value body;
|
||||
body["key"] = deviceKey_;
|
||||
body["client_id"] = pushClientId_;
|
||||
body["callback_id"] = callbackId;
|
||||
#ifdef __ANDROID__
|
||||
body["platform"] = "android";
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
body["platform"] = "apple";
|
||||
#endif
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["commentStyle"] = "None";
|
||||
wbuilder["indentation"] = "";
|
||||
auto content = Json::writeString(wbuilder, body) + "\n";
|
||||
std::replace(content.begin(), content.end(), '\n', ' ');
|
||||
req->set_body(content);
|
||||
req->set_header("Content-Length", std::to_string(content.size()));
|
||||
}
|
||||
#endif // OPENDHT_PUSH_NOTIFICATIONS
|
||||
|
||||
} // namespace dht
|
||||
|
||||
#endif // OPENDHT_PROXY_CLIENT
|
||||
@@ -0,0 +1,687 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2018 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
#include "dht_proxy_server.h"
|
||||
|
||||
#include "default_types.h"
|
||||
#include "dhtrunner.h"
|
||||
#include "msgpack.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <json/json.h>
|
||||
#include <limits>
|
||||
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
constexpr int const TIMEOUT {6 * 60 * 60}; // in seconds (so six hours)
|
||||
constexpr const char* const HTTP_PROTO {"http://"}; // TODO, https for prod
|
||||
#endif //OPENDHT_PUSH_NOTIFICATIONS
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
namespace dht {
|
||||
|
||||
DhtProxyServer::DhtProxyServer(std::shared_ptr<DhtRunner> dht, in_port_t port , const std::string& pushServer)
|
||||
: dht_(dht) , pushServer_(pushServer)
|
||||
{
|
||||
// NOTE in c++14, use make_unique
|
||||
service_ = std::unique_ptr<restbed::Service>(new restbed::Service());
|
||||
|
||||
std::cout << "Running DHT proxy server on port " << port << std::endl;
|
||||
if (not pushServer.empty()) {
|
||||
#if !OPENDHT_PUSH_NOTIFICATIONS
|
||||
std::cerr << "Push server defined but built OpenDHT built without push notification support" << std::endl;
|
||||
#else
|
||||
std::cout << "Using push notification server: " << pushServer << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
server_thread = std::thread([this, port]() {
|
||||
// Create endpoints
|
||||
auto resource = std::make_shared<restbed::Resource>();
|
||||
resource->set_path("/");
|
||||
resource->set_method_handler("GET", std::bind(&DhtProxyServer::getNodeInfo, this, _1));
|
||||
service_->publish(resource);
|
||||
resource = std::make_shared<restbed::Resource>();
|
||||
resource->set_path("/{hash: .*}");
|
||||
resource->set_method_handler("GET", std::bind(&DhtProxyServer::get, this, _1));
|
||||
resource->set_method_handler("LISTEN", std::bind(&DhtProxyServer::listen, this, _1));
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
resource->set_method_handler("SUBSCRIBE", std::bind(&DhtProxyServer::subscribe, this, _1));
|
||||
resource->set_method_handler("UNSUBSCRIBE", std::bind(&DhtProxyServer::unsubscribe, this, _1));
|
||||
#endif //OPENDHT_PUSH_NOTIFICATIONS
|
||||
resource->set_method_handler("POST", std::bind(&DhtProxyServer::put, this, _1));
|
||||
#if OPENDHT_PROXY_SERVER_IDENTITY
|
||||
resource->set_method_handler("SIGN", std::bind(&DhtProxyServer::putSigned, this, _1));
|
||||
resource->set_method_handler("ENCRYPT", std::bind(&DhtProxyServer::putEncrypted, this, _1));
|
||||
#endif // OPENDHT_PROXY_SERVER_IDENTITY
|
||||
resource->set_method_handler("OPTIONS", std::bind(&DhtProxyServer::handleOptionsMethod, this, _1));
|
||||
service_->publish(resource);
|
||||
resource = std::make_shared<restbed::Resource>();
|
||||
resource->set_path("/{hash: .*}/{value: .*}");
|
||||
resource->set_method_handler("GET", std::bind(&DhtProxyServer::getFiltered, this, _1));
|
||||
service_->publish(resource);
|
||||
|
||||
// Start server
|
||||
auto settings = std::make_shared<restbed::Settings>();
|
||||
settings->set_default_header("Content-Type", "application/json");
|
||||
settings->set_default_header("Connection", "keep-alive");
|
||||
settings->set_default_header("Access-Control-Allow-Origin", "*");
|
||||
std::chrono::milliseconds timeout(std::numeric_limits<int>::max());
|
||||
settings->set_connection_timeout(timeout); // there is a timeout, but really huge
|
||||
settings->set_port(port);
|
||||
auto maxThreads = std::thread::hardware_concurrency() - 1;
|
||||
settings->set_worker_limit(maxThreads > 1 ? maxThreads : 1);
|
||||
try {
|
||||
service_->start(settings);
|
||||
} catch(std::system_error& e) {
|
||||
std::cerr << "Error running server on port " << port << ": " << e.what() << std::endl;
|
||||
}
|
||||
});
|
||||
|
||||
listenThread_ = std::thread([this]() {
|
||||
while (!service_->is_up() && !stopListeners) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
while (service_->is_up() && !stopListeners) {
|
||||
removeClosedListeners();
|
||||
// add listener from push notifs
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
handlePushListeners();
|
||||
#endif //OPENDHT_PUSH_NOTIFICATIONS
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
// Remove last listeners
|
||||
removeClosedListeners(false);
|
||||
});
|
||||
|
||||
dht->forwardAllMessages(true);
|
||||
}
|
||||
|
||||
DhtProxyServer::~DhtProxyServer()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::stop()
|
||||
{
|
||||
service_->stop();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(lockListener_);
|
||||
auto listener = currentListeners_.begin();
|
||||
while (listener != currentListeners_.end()) {
|
||||
listener->session->close();
|
||||
++ listener;
|
||||
}
|
||||
}
|
||||
stopListeners = true;
|
||||
// listenThreads_ will stop because there is no more sessions
|
||||
if (listenThread_.joinable())
|
||||
listenThread_.join();
|
||||
if (server_thread.joinable())
|
||||
server_thread.join();
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::getNodeInfo(const std::shared_ptr<restbed::Session>& session) const
|
||||
{
|
||||
const auto request = session->get_request();
|
||||
int content_length = std::stoi(request->get_header("Content-Length", "0"));
|
||||
session->fetch(content_length,
|
||||
[=](const std::shared_ptr<restbed::Session> s, const restbed::Bytes& /*b*/)
|
||||
{
|
||||
if (dht_) {
|
||||
Json::Value result;
|
||||
auto id = dht_->getId();
|
||||
if (id)
|
||||
result["id"] = id.toString();
|
||||
result["node_id"] = dht_->getNodeId().toString();
|
||||
result["ipv4"] = dht_->getNodesStats(AF_INET).toJson();
|
||||
result["ipv6"] = dht_->getNodesStats(AF_INET6).toJson();
|
||||
result["public_ip"] = s->get_origin(); // [ipv6:ipv4]:port or ipv4:port
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["commentStyle"] = "None";
|
||||
wbuilder["indentation"] = "";
|
||||
auto output = Json::writeString(wbuilder, result) + "\n";
|
||||
s->close(restbed::OK, output);
|
||||
}
|
||||
else
|
||||
s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::get(const std::shared_ptr<restbed::Session>& session) const
|
||||
{
|
||||
const auto request = session->get_request();
|
||||
int content_length = std::stoi(request->get_header("Content-Length", "0"));
|
||||
auto hash = request->get_path_parameter("hash");
|
||||
session->fetch(content_length,
|
||||
[=](const std::shared_ptr<restbed::Session> s, const restbed::Bytes& /*b* */)
|
||||
{
|
||||
if (dht_) {
|
||||
InfoHash infoHash(hash);
|
||||
if (!infoHash) {
|
||||
infoHash = InfoHash::get(hash);
|
||||
}
|
||||
s->yield(restbed::OK, "", [=]( const std::shared_ptr< restbed::Session > s) {
|
||||
auto cacheSession = std::weak_ptr<restbed::Session>(s);
|
||||
|
||||
dht_->get(infoHash, [cacheSession](std::shared_ptr<Value> value) {
|
||||
auto s = cacheSession.lock();
|
||||
if (!s) return false;
|
||||
// Send values as soon as we get them
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["commentStyle"] = "None";
|
||||
wbuilder["indentation"] = "";
|
||||
auto output = Json::writeString(wbuilder, value->toJson()) + "\n";
|
||||
s->yield(output, [](const std::shared_ptr<restbed::Session> /*session*/){ });
|
||||
return true;
|
||||
}, [s](bool /*ok* */) {
|
||||
// Communication is finished
|
||||
s->close();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::listen(const std::shared_ptr<restbed::Session>& session) const
|
||||
{
|
||||
|
||||
const auto request = session->get_request();
|
||||
int content_length = std::stoi(request->get_header("Content-Length", "0"));
|
||||
auto hash = request->get_path_parameter("hash");
|
||||
InfoHash infoHash(hash);
|
||||
if (!infoHash)
|
||||
infoHash = InfoHash::get(hash);
|
||||
session->fetch(content_length,
|
||||
[=](const std::shared_ptr<restbed::Session> s, const restbed::Bytes& /*b* */)
|
||||
{
|
||||
if (dht_) {
|
||||
InfoHash infoHash(hash);
|
||||
if (!infoHash) {
|
||||
infoHash = InfoHash::get(hash);
|
||||
}
|
||||
s->yield(restbed::OK);
|
||||
// Handle client deconnection
|
||||
// NOTE: for now, there is no handler, so we test the session in a thread
|
||||
// will be the case in restbed 5.0
|
||||
SessionToHashToken listener;
|
||||
listener.session = session;
|
||||
listener.hash = infoHash;
|
||||
// cache the session to avoid an incrementation of the shared_ptr's counter
|
||||
// else, the session->close() will not close the socket.
|
||||
auto cacheSession = std::weak_ptr<restbed::Session>(s);
|
||||
listener.token = dht_->listen(infoHash, [cacheSession](std::shared_ptr<Value> value) {
|
||||
auto s = cacheSession.lock();
|
||||
if (!s) return false;
|
||||
// Send values as soon as we get them
|
||||
if (!s->is_closed()) {
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["commentStyle"] = "None";
|
||||
wbuilder["indentation"] = "";
|
||||
auto output = Json::writeString(wbuilder, value->toJson()) + "\n";
|
||||
s->yield(output, [](const std::shared_ptr<restbed::Session>){ });
|
||||
}
|
||||
return !s->is_closed();
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(lockListener_);
|
||||
currentListeners_.emplace_back(std::move(listener));
|
||||
}
|
||||
} else {
|
||||
session->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
void
|
||||
DhtProxyServer::subscribe(const std::shared_ptr<restbed::Session>& session) const
|
||||
{
|
||||
const auto request = session->get_request();
|
||||
int content_length = std::stoi(request->get_header("Content-Length", "0"));
|
||||
auto hash = request->get_path_parameter("hash");
|
||||
InfoHash infoHash(hash);
|
||||
if (!infoHash)
|
||||
infoHash = InfoHash::get(hash);
|
||||
session->fetch(content_length,
|
||||
[=](const std::shared_ptr<restbed::Session> s, const restbed::Bytes& b)
|
||||
{
|
||||
try {
|
||||
restbed::Bytes buf(b);
|
||||
std::string strJson(buf.begin(), buf.end());
|
||||
|
||||
std::string err;
|
||||
Json::Value root;
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto* char_data = reinterpret_cast<const char*>(&strJson[0]);
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
if (!reader->parse(char_data, char_data + strJson.size(), &root, &err)) {
|
||||
s->close(restbed::BAD_REQUEST, "{\"err\":\"Incorrect JSON\"}");
|
||||
return;
|
||||
}
|
||||
auto pushToken = root["key"].asString();
|
||||
if (pushToken.empty()) return;
|
||||
auto callbackId = root.isMember("callback_id") ? root["callback_id"].asLargestUInt() : 0;
|
||||
auto platform = root["platform"].asString();
|
||||
auto isAndroid = platform == "android";
|
||||
auto clientId = root.isMember("client_id") ? root["client_id"].asString() : std::string();
|
||||
|
||||
auto token = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(lockListener_);
|
||||
// Check if listener is already present and refresh timeout if launched
|
||||
for(auto& listener: pushListeners_) {
|
||||
if (listener.pushToken == pushToken && listener.hash == infoHash
|
||||
&& listener.callbackId == callbackId) {
|
||||
if (listener.started)
|
||||
listener.deadline = std::chrono::steady_clock::now()
|
||||
+ std::chrono::seconds(TIMEOUT);
|
||||
s->close(restbed::OK, "{\"token\": " + std::to_string(listener.token) + "}\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
// The listener is not found, so add it.
|
||||
++tokenPushNotif_;
|
||||
token = tokenPushNotif_;
|
||||
PushListener listener;
|
||||
listener.pushToken = pushToken;
|
||||
listener.hash = std::move(infoHash);
|
||||
listener.token = token;
|
||||
listener.started = false;
|
||||
listener.callbackId = callbackId;
|
||||
listener.clientId = clientId;
|
||||
listener.isAndroid = isAndroid;
|
||||
pushListeners_.emplace_back(std::move(listener));
|
||||
}
|
||||
s->close(restbed::OK, "{\"token\": " + std::to_string(token) + "}\n");
|
||||
} catch (...) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::unsubscribe(const std::shared_ptr<restbed::Session>& session) const
|
||||
{
|
||||
const auto request = session->get_request();
|
||||
int content_length = std::stoi(request->get_header("Content-Length", "0"));
|
||||
auto hash = request->get_path_parameter("hash");
|
||||
InfoHash infoHash(hash);
|
||||
if (!infoHash)
|
||||
infoHash = InfoHash::get(hash);
|
||||
session->fetch(content_length,
|
||||
[=](const std::shared_ptr<restbed::Session> s, const restbed::Bytes& b)
|
||||
{
|
||||
try {
|
||||
restbed::Bytes buf(b);
|
||||
std::string strJson(buf.begin(), buf.end());
|
||||
std::string err;
|
||||
Json::Value root;
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto* char_data = reinterpret_cast<const char*>(&strJson[0]);
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
if (!reader->parse(char_data, char_data + strJson.size(), &root, &err)) {
|
||||
s->close(restbed::BAD_REQUEST, "{\"err\":\"Incorrect JSON\"}");
|
||||
return;
|
||||
}
|
||||
auto pushToken = root["key"].asString();
|
||||
if (pushToken.empty()) return;
|
||||
auto token = std::stoull(root["token"].asString());
|
||||
if (token == 0) return;
|
||||
auto callbackId = root.isMember("callback_id") ? root["callback_id"].asLargestUInt() : 0;
|
||||
|
||||
std::lock_guard<std::mutex> lock(lockListener_);
|
||||
// Check if listener is already present and refresh timeout if launched
|
||||
auto listener = pushListeners_.begin();
|
||||
while (listener != pushListeners_.end()) {
|
||||
if (listener->pushToken == pushToken && listener->token == token
|
||||
&& listener->hash == infoHash && listener->callbackId == callbackId) {
|
||||
if (dht_ && listener->started)
|
||||
dht_->cancelListen(listener->hash, std::move(listener->internalToken.get()));
|
||||
listener = pushListeners_.erase(listener);
|
||||
} else {
|
||||
++listener;
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::sendPushNotification(const std::string& token, const Json::Value& json, bool isAndroid) const
|
||||
{
|
||||
restbed::Uri uri(HTTP_PROTO + pushServer_ + "/api/push");
|
||||
auto req = std::make_shared<restbed::Request>(uri);
|
||||
req->set_method("POST");
|
||||
|
||||
// NOTE: see https://github.com/appleboy/gorush
|
||||
Json::Value notification(Json::objectValue);
|
||||
Json::Value tokens(Json::arrayValue);
|
||||
tokens[0] = token;
|
||||
notification["tokens"] = tokens;
|
||||
notification["platform"] = isAndroid ? 2 : 1;
|
||||
notification["data"] = json;
|
||||
notification["priority"] = "high";
|
||||
notification["time_to_live"] = 600;
|
||||
|
||||
Json::Value notifications(Json::arrayValue);
|
||||
notifications[0] = notification;
|
||||
|
||||
Json::Value content;
|
||||
content["notifications"] = notifications;
|
||||
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["commentStyle"] = "None";
|
||||
wbuilder["indentation"] = "";
|
||||
auto valueStr = Json::writeString(wbuilder, content);
|
||||
|
||||
req->set_header("Content-Type", "application/json");
|
||||
req->set_header("Accept", "*/*");
|
||||
req->set_header("Host", pushServer_);
|
||||
req->set_header("Content-Length", std::to_string(valueStr.length()));
|
||||
req->set_body(valueStr);
|
||||
// Send request.
|
||||
restbed::Http::async(req, {});
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::handlePushListeners()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(lockListener_);
|
||||
auto pushListener = pushListeners_.begin();
|
||||
while (pushListener != pushListeners_.end()) {
|
||||
if (dht_ && !pushListener->started) {
|
||||
// Try to start unstarted listeners
|
||||
auto key = pushListener->pushToken;
|
||||
auto token = pushListener->token;
|
||||
auto callbackId = pushListener->callbackId;
|
||||
auto isAndroid = pushListener->isAndroid;
|
||||
auto clientId = pushListener->clientId;
|
||||
pushListener->internalToken = dht_->listen(pushListener->hash,
|
||||
[this, key, callbackId, token, isAndroid, clientId](std::vector<std::shared_ptr<Value>> /*value*/) {
|
||||
// Build message content.
|
||||
Json::Value json;
|
||||
if (callbackId > 0) {
|
||||
json["callback_id"] = callbackId;
|
||||
}
|
||||
json["to"] = clientId;
|
||||
json["token"] = token;
|
||||
sendPushNotification(key, json, isAndroid);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
pushListener->deadline = std::chrono::steady_clock::now() + std::chrono::seconds(TIMEOUT);
|
||||
pushListener->started = true;
|
||||
pushListener++;
|
||||
} else if (dht_ && pushListener->started && pushListener->deadline < std::chrono::steady_clock::now()) {
|
||||
// Cancel listen if deadline has been reached
|
||||
dht_->cancelListen(pushListener->hash, std::move(pushListener->internalToken.get()));
|
||||
// Send a push notification to inform the client that this listen has timeout
|
||||
Json::Value json;
|
||||
json["timeout"] = pushListener->hash.toString();
|
||||
if (pushListener->callbackId > 0) {
|
||||
json["callback_id"] = pushListener->callbackId;
|
||||
}
|
||||
json["to"] = pushListener->clientId;
|
||||
json["token"] = pushListener->token;
|
||||
sendPushNotification(pushListener->pushToken, json, pushListener->isAndroid);
|
||||
pushListener = pushListeners_.erase(pushListener);
|
||||
} else {
|
||||
pushListener++;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif //OPENDHT_PUSH_NOTIFICATIONS
|
||||
|
||||
void
|
||||
DhtProxyServer::put(const std::shared_ptr<restbed::Session>& session) const
|
||||
{
|
||||
const auto request = session->get_request();
|
||||
int content_length = std::stoi(request->get_header("Content-Length", "0"));
|
||||
auto hash = request->get_path_parameter("hash");
|
||||
InfoHash infoHash(hash);
|
||||
if (!infoHash)
|
||||
infoHash = InfoHash::get(hash);
|
||||
|
||||
session->fetch(content_length,
|
||||
[=](const std::shared_ptr<restbed::Session> s, const restbed::Bytes& b)
|
||||
{
|
||||
if (dht_) {
|
||||
if(b.empty()) {
|
||||
std::string response("{\"err\":\"Missing parameters\"}");
|
||||
s->close(restbed::BAD_REQUEST, response);
|
||||
} else {
|
||||
restbed::Bytes buf(b);
|
||||
std::string strJson(buf.begin(), buf.end());
|
||||
|
||||
std::string err;
|
||||
Json::Value root;
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto* char_data = reinterpret_cast<const char*>(&strJson[0]);
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
if (reader->parse(char_data, char_data + strJson.size(), &root, &err)) {
|
||||
// Build the Value from json
|
||||
auto value = std::make_shared<Value>(root);
|
||||
auto permanent = root.isMember("permanent") ? root["permanent"].asBool() : false;
|
||||
|
||||
dht_->put(infoHash, value, [s, value](bool ok) {
|
||||
if (ok) {
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["commentStyle"] = "None";
|
||||
wbuilder["indentation"] = "";
|
||||
if (s->is_open())
|
||||
s->close(restbed::OK, Json::writeString(wbuilder, value->toJson()) + "\n");
|
||||
} else {
|
||||
if (s->is_open())
|
||||
s->close(restbed::BAD_GATEWAY, "{\"err\":\"put failed\"}");
|
||||
}
|
||||
}, time_point::max(), permanent);
|
||||
} else {
|
||||
s->close(restbed::BAD_REQUEST, "{\"err\":\"Incorrect JSON\"}");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#if OPENDHT_PROXY_SERVER_IDENTITY
|
||||
void
|
||||
DhtProxyServer::putSigned(const std::shared_ptr<restbed::Session>& session) const
|
||||
{
|
||||
const auto request = session->get_request();
|
||||
int content_length = std::stoi(request->get_header("Content-Length", "0"));
|
||||
auto hash = request->get_path_parameter("hash");
|
||||
InfoHash infoHash(hash);
|
||||
if (!infoHash)
|
||||
infoHash = InfoHash::get(hash);
|
||||
|
||||
session->fetch(content_length,
|
||||
[=](const std::shared_ptr<restbed::Session> s, const restbed::Bytes& b)
|
||||
{
|
||||
if (dht_) {
|
||||
if(b.empty()) {
|
||||
std::string response("{\"err\":\"Missing parameters\"}");
|
||||
s->close(restbed::BAD_REQUEST, response);
|
||||
} else {
|
||||
restbed::Bytes buf(b);
|
||||
std::string strJson(buf.begin(), buf.end());
|
||||
|
||||
std::string err;
|
||||
Json::Value root;
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto* char_data = reinterpret_cast<const char*>(&strJson[0]);
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
if (reader->parse(char_data, char_data + strJson.size(), &root, &err)) {
|
||||
auto value = std::make_shared<Value>(root);
|
||||
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["commentStyle"] = "None";
|
||||
wbuilder["indentation"] = "";
|
||||
auto output = Json::writeString(wbuilder, value->toJson()) + "\n";
|
||||
dht_->putSigned(infoHash, value);
|
||||
s->close(restbed::OK, output);
|
||||
} else {
|
||||
s->close(restbed::BAD_REQUEST, "{\"err\":\"Incorrect JSON\"}");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::putEncrypted(const std::shared_ptr<restbed::Session>& session) const
|
||||
{
|
||||
const auto request = session->get_request();
|
||||
int content_length = std::stoi(request->get_header("Content-Length", "0"));
|
||||
auto hash = request->get_path_parameter("hash");
|
||||
InfoHash key(hash);
|
||||
if (!key)
|
||||
key = InfoHash::get(hash);
|
||||
|
||||
session->fetch(content_length,
|
||||
[=](const std::shared_ptr<restbed::Session> s, const restbed::Bytes& b)
|
||||
{
|
||||
if (dht_) {
|
||||
if(b.empty()) {
|
||||
std::string response("{\"err\":\"Missing parameters\"}");
|
||||
s->close(restbed::BAD_REQUEST, response);
|
||||
} else {
|
||||
restbed::Bytes buf(b);
|
||||
std::string strJson(buf.begin(), buf.end());
|
||||
|
||||
std::string err;
|
||||
Json::Value root;
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto* char_data = reinterpret_cast<const char*>(&strJson[0]);
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
bool parsingSuccessful = reader->parse(char_data, char_data + strJson.size(), &root, &err);
|
||||
InfoHash to(root["to"].asString());
|
||||
if (parsingSuccessful && to) {
|
||||
auto value = std::make_shared<Value>(root);
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["commentStyle"] = "None";
|
||||
wbuilder["indentation"] = "";
|
||||
auto output = Json::writeString(wbuilder, value->toJson()) + "\n";
|
||||
dht_->putEncrypted(key, to, value);
|
||||
s->close(restbed::OK, output);
|
||||
} else {
|
||||
if(!parsingSuccessful)
|
||||
s->close(restbed::BAD_REQUEST, "{\"err\":\"Incorrect JSON\"}");
|
||||
else
|
||||
s->close(restbed::BAD_REQUEST, "{\"err\":\"No destination found\"}");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
#endif // OPENDHT_PROXY_SERVER_IDENTITY
|
||||
|
||||
void
|
||||
DhtProxyServer::handleOptionsMethod(const std::shared_ptr<restbed::Session>& session) const
|
||||
{
|
||||
#if OPENDHT_PROXY_SERVER_IDENTITY
|
||||
const auto allowed = "OPTIONS, GET, POST, LISTEN, SIGN, ENCRYPT";
|
||||
#else
|
||||
const auto allowed = "OPTIONS, GET, POST, LISTEN";
|
||||
#endif //OPENDHT_PROXY_SERVER_IDENTITY
|
||||
session->close(restbed::OK, {{"Access-Control-Allow-Methods", allowed},
|
||||
{"Access-Control-Allow-Headers", "content-type"},
|
||||
{"Access-Control-Max-Age", "86400"}});
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::getFiltered(const std::shared_ptr<restbed::Session>& session) const
|
||||
{
|
||||
const auto request = session->get_request();
|
||||
int content_length = std::stoi(request->get_header("Content-Length", "0"));
|
||||
auto hash = request->get_path_parameter("hash");
|
||||
auto value = request->get_path_parameter("value");
|
||||
session->fetch(content_length,
|
||||
[=](const std::shared_ptr<restbed::Session> s, const restbed::Bytes& /*b* */)
|
||||
{
|
||||
if (dht_) {
|
||||
InfoHash infoHash(hash);
|
||||
if (!infoHash) {
|
||||
infoHash = InfoHash::get(hash);
|
||||
}
|
||||
s->yield(restbed::OK, "", [=]( const std::shared_ptr< restbed::Session > s) {
|
||||
dht_->get(infoHash, [s](std::shared_ptr<Value> v) {
|
||||
// Send values as soon as we get them
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["commentStyle"] = "None";
|
||||
wbuilder["indentation"] = "";
|
||||
auto output = Json::writeString(wbuilder, v->toJson()) + "\n";
|
||||
s->yield(output, [](const std::shared_ptr<restbed::Session> /*session*/){ });
|
||||
return true;
|
||||
}, [s](bool /*ok* */) {
|
||||
// Communication is finished
|
||||
s->close();
|
||||
}, {}, value);
|
||||
});
|
||||
} else {
|
||||
s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyServer::removeClosedListeners(bool testSession)
|
||||
{
|
||||
// clean useless listeners
|
||||
std::lock_guard<std::mutex> lock(lockListener_);
|
||||
auto listener = currentListeners_.begin();
|
||||
while (listener != currentListeners_.end()) {
|
||||
auto cancel = testSession ? dht_ && listener->session->is_closed() : static_cast<bool>(dht_);
|
||||
if (cancel) {
|
||||
dht_->cancelListen(listener->hash, std::move(listener->token.get()));
|
||||
// Remove listener if unused
|
||||
listener = currentListeners_.erase(listener);
|
||||
} else {
|
||||
++listener;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
+289
-128
@@ -1,7 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,6 +21,10 @@
|
||||
#include "dhtrunner.h"
|
||||
#include "securedht.h"
|
||||
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
#include "dht_proxy_client.h"
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#else
|
||||
@@ -39,6 +44,9 @@ namespace dht {
|
||||
constexpr std::chrono::seconds DhtRunner::BOOTSTRAP_PERIOD;
|
||||
|
||||
DhtRunner::DhtRunner() : dht_()
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
, dht_via_proxy_()
|
||||
#endif //OPENDHT_PROXY_CLIENT
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSADATA wsd;
|
||||
@@ -58,41 +66,51 @@ DhtRunner::~DhtRunner()
|
||||
void
|
||||
DhtRunner::run(in_port_t port, DhtRunner::Config config)
|
||||
{
|
||||
sockaddr_in sin4;
|
||||
std::fill_n((uint8_t*)&sin4, sizeof(sin4), 0);
|
||||
sin4.sin_family = AF_INET;
|
||||
sin4.sin_port = htons(port);
|
||||
sockaddr_in6 sin6;
|
||||
std::fill_n((uint8_t*)&sin6, sizeof(sin6), 0);
|
||||
sin6.sin6_family = AF_INET6;
|
||||
sin6.sin6_port = htons(port);
|
||||
run(&sin4, &sin6, config);
|
||||
SockAddr sin4;
|
||||
sin4.setFamily(AF_INET);
|
||||
sin4.setPort(port);
|
||||
SockAddr sin6;
|
||||
sin6.setFamily(AF_INET6);
|
||||
sin6.setPort(port);
|
||||
run(sin4, sin6, config);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::run(const char* ip4, const char* ip6, const char* service, DhtRunner::Config config)
|
||||
{
|
||||
auto res4 = getAddrInfo(ip4, service);
|
||||
auto res6 = getAddrInfo(ip6, service);
|
||||
run(res4.empty() ? nullptr : (sockaddr_in*) &res4.front().first,
|
||||
res6.empty() ? nullptr : (sockaddr_in6*)&res6.front().first, config);
|
||||
auto res4 = SockAddr::resolve(ip4, service);
|
||||
auto res6 = SockAddr::resolve(ip6, service);
|
||||
run(res4.empty() ? SockAddr() : res4.front(),
|
||||
res6.empty() ? SockAddr() : res6.front(), config);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::run(const sockaddr_in* local4, const sockaddr_in6* local6, DhtRunner::Config config)
|
||||
DhtRunner::run(const SockAddr& local4, const SockAddr& local6, DhtRunner::Config config)
|
||||
{
|
||||
if (running)
|
||||
return;
|
||||
if (rcv_thread.joinable())
|
||||
rcv_thread.join();
|
||||
startNetwork(local4, local6);
|
||||
|
||||
auto dht = std::unique_ptr<DhtInterface>(new Dht(s4, s6, SecureDht::getConfig(config.dht_config)));
|
||||
dht_ = std::unique_ptr<SecureDht>(new SecureDht(std::move(dht), config.dht_config));
|
||||
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
config_ = config;
|
||||
#endif
|
||||
enableProxy(not config.proxy_server.empty());
|
||||
|
||||
running = true;
|
||||
doRun(local4, local6, config.dht_config);
|
||||
if (not config.threaded)
|
||||
return;
|
||||
dht_thread = std::thread([this]() {
|
||||
dht_thread = std::thread([this, local4, local6]() {
|
||||
while (running) {
|
||||
std::unique_lock<std::mutex> lk(dht_mtx);
|
||||
auto wakeup = loop_();
|
||||
time_point wakeup;
|
||||
try {
|
||||
wakeup = loop_();
|
||||
} catch (const dht::SocketException& e) {
|
||||
startNetwork(local4, local6);
|
||||
}
|
||||
cv.wait_until(lk, wakeup, [this]() {
|
||||
if (not running)
|
||||
return true;
|
||||
@@ -117,6 +135,10 @@ DhtRunner::run(const sockaddr_in* local4, const sockaddr_in6* local6, DhtRunner:
|
||||
|
||||
void
|
||||
DhtRunner::shutdown(ShutdownCallback cb) {
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->shutdown(cb);
|
||||
#endif
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht& dht) mutable {
|
||||
dht.shutdown(cb);
|
||||
@@ -127,15 +149,16 @@ DhtRunner::shutdown(ShutdownCallback cb) {
|
||||
void
|
||||
DhtRunner::join()
|
||||
{
|
||||
running_network = false;
|
||||
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();
|
||||
if (rcv_thread.joinable())
|
||||
rcv_thread.join();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
@@ -144,11 +167,9 @@ DhtRunner::join()
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
dht_.reset();
|
||||
resetDht();
|
||||
status4 = NodeStatus::Disconnected;
|
||||
status6 = NodeStatus::Disconnected;
|
||||
bound4 = {};
|
||||
bound6 = {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,23 +177,23 @@ void
|
||||
DhtRunner::dumpTables() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
dht_->dumpTables();
|
||||
activeDht()->dumpTables();
|
||||
}
|
||||
|
||||
InfoHash
|
||||
DhtRunner::getId() const
|
||||
{
|
||||
if (!dht_)
|
||||
if (!activeDht())
|
||||
return {};
|
||||
return dht_->getId();
|
||||
return activeDht()->getId();
|
||||
}
|
||||
|
||||
InfoHash
|
||||
DhtRunner::getNodeId() const
|
||||
{
|
||||
if (!dht_)
|
||||
if (!activeDht())
|
||||
return {};
|
||||
return dht_->getNodeId();
|
||||
return activeDht()->getNodeId();
|
||||
}
|
||||
|
||||
|
||||
@@ -211,19 +232,30 @@ DhtRunner::exportValues() const {
|
||||
void
|
||||
DhtRunner::setLoggers(LogMethod error, LogMethod warn, LogMethod debug) {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
dht_->setLoggers(std::forward<LogMethod>(error), std::forward<LogMethod>(warn), std::forward<LogMethod>(debug));
|
||||
if (dht_)
|
||||
dht_->setLoggers(std::forward<LogMethod>(error), std::forward<LogMethod>(warn), std::forward<LogMethod>(debug));
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->setLoggers(std::forward<LogMethod>(error), std::forward<LogMethod>(warn), std::forward<LogMethod>(debug));
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::setLogFilter(const InfoHash& f) {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
dht_->setLogFilter(f);
|
||||
activeDht()->setLogFilter(f);
|
||||
if (dht_)
|
||||
dht_->setLogFilter(f);
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->setLogFilter(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::registerType(const ValueType& type) {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
dht_->registerType(type);
|
||||
activeDht()->registerType(type);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -236,7 +268,7 @@ 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);
|
||||
const auto stats = dht_->getNodesStats(af);
|
||||
const auto stats = activeDht()->getNodesStats(af);
|
||||
if (good_return)
|
||||
*good_return = stats.good_nodes;
|
||||
if (dubious_return)
|
||||
@@ -252,51 +284,51 @@ NodeStats
|
||||
DhtRunner::getNodesStats(sa_family_t af) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
return dht_->getNodesStats(af);
|
||||
return activeDht()->getNodesStats(af);
|
||||
}
|
||||
|
||||
std::vector<unsigned>
|
||||
DhtRunner::getNodeMessageStats(bool in) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
return dht_->getNodeMessageStats(in);
|
||||
return activeDht()->getNodeMessageStats(in);
|
||||
}
|
||||
|
||||
std::string
|
||||
DhtRunner::getStorageLog() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
return dht_->getStorageLog();
|
||||
return activeDht()->getStorageLog();
|
||||
}
|
||||
std::string
|
||||
DhtRunner::getStorageLog(const InfoHash& f) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
return dht_->getStorageLog(f);
|
||||
return activeDht()->getStorageLog(f);
|
||||
}
|
||||
std::string
|
||||
DhtRunner::getRoutingTablesLog(sa_family_t af) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
return dht_->getRoutingTablesLog(af);
|
||||
return activeDht()->getRoutingTablesLog(af);
|
||||
}
|
||||
std::string
|
||||
DhtRunner::getSearchesLog(sa_family_t af) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
return dht_->getSearchesLog(af);
|
||||
return activeDht()->getSearchesLog(af);
|
||||
}
|
||||
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);
|
||||
return activeDht()->getSearchLog(f, af);
|
||||
}
|
||||
std::vector<SockAddr>
|
||||
DhtRunner::getPublicAddress(sa_family_t af)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
return dht_->getPublicAddress(af);
|
||||
return activeDht()->getPublicAddress(af);
|
||||
}
|
||||
std::vector<std::string>
|
||||
DhtRunner::getPublicAddressStr(sa_family_t af)
|
||||
@@ -310,18 +342,24 @@ DhtRunner::getPublicAddressStr(sa_family_t af)
|
||||
void
|
||||
DhtRunner::registerCertificate(std::shared_ptr<crypto::Certificate> cert) {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
dht_->registerCertificate(cert);
|
||||
activeDht()->registerCertificate(cert);
|
||||
}
|
||||
void
|
||||
DhtRunner::setLocalCertificateStore(CertificateStoreQuery&& query_method) {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
dht_->setLocalCertificateStore(std::forward<CertificateStoreQuery>(query_method));
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->setLocalCertificateStore(std::forward<CertificateStoreQuery>(query_method));
|
||||
#endif
|
||||
if (dht_)
|
||||
dht_->setLocalCertificateStore(std::forward<CertificateStoreQuery>(query_method));
|
||||
}
|
||||
|
||||
time_point
|
||||
DhtRunner::loop_()
|
||||
{
|
||||
if (!dht_)
|
||||
auto dht = activeDht();
|
||||
if (not dht)
|
||||
return {};
|
||||
|
||||
decltype(pending_ops) ops {};
|
||||
@@ -332,7 +370,7 @@ DhtRunner::loop_()
|
||||
std::move(pending_ops) : std::move(pending_ops_prio);
|
||||
}
|
||||
while (not ops.empty()) {
|
||||
ops.front()(*dht_);
|
||||
ops.front()(*dht);
|
||||
ops.pop();
|
||||
}
|
||||
|
||||
@@ -347,15 +385,15 @@ DhtRunner::loop_()
|
||||
for (const auto& pck : received) {
|
||||
auto& buf = pck.first;
|
||||
auto& from = pck.second;
|
||||
wakeup = dht_->periodic(buf.data(), buf.size()-1, from);
|
||||
wakeup = dht->periodic(buf.data(), buf.size()-1, from);
|
||||
}
|
||||
received.clear();
|
||||
} else {
|
||||
wakeup = dht_->periodic(nullptr, 0, nullptr, 0);
|
||||
wakeup = dht->periodic(nullptr, 0, nullptr, 0);
|
||||
}
|
||||
|
||||
NodeStatus nstatus4 = dht_->getStatus(AF_INET);
|
||||
NodeStatus nstatus6 = dht_->getStatus(AF_INET6);
|
||||
NodeStatus nstatus4 = dht->getStatus(AF_INET);
|
||||
NodeStatus nstatus6 = dht->getStatus(AF_INET6);
|
||||
if (nstatus4 != status4 || nstatus6 != status6) {
|
||||
status4 = nstatus4;
|
||||
status6 = nstatus6;
|
||||
@@ -375,54 +413,52 @@ DhtRunner::loop_()
|
||||
return wakeup;
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::doRun(const sockaddr_in* sin4, const sockaddr_in6* sin6, SecureDht::Config config)
|
||||
{
|
||||
dht_.reset();
|
||||
|
||||
int s4 = -1,
|
||||
s6 = -1;
|
||||
int bindSocket(const SockAddr& addr, SockAddr& bound)
|
||||
{
|
||||
bool is_ipv6 = addr.getFamily() == AF_INET6;
|
||||
int sock = socket(is_ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0);
|
||||
if (sock < 0)
|
||||
throw DhtException(std::string("Can't open socket: ") + strerror(sock));
|
||||
int set = 1;
|
||||
#ifdef SO_NOSIGPIPE
|
||||
setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, (const char*)&set, sizeof(set));
|
||||
#endif
|
||||
if (is_ipv6)
|
||||
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&set, sizeof(set));
|
||||
int rc = bind(sock, addr.get(), addr.getLength());
|
||||
if(rc < 0)
|
||||
throw DhtException("Can't bind socket on " + addr.toString() + " " + strerror(rc));
|
||||
sockaddr_storage ss;
|
||||
socklen_t ss_len = sizeof(ss);
|
||||
getsockname(sock, (sockaddr*)&ss, &ss_len);
|
||||
bound = {ss, ss_len};
|
||||
return sock;
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::startNetwork(const SockAddr sin4, const SockAddr sin6)
|
||||
{
|
||||
running_network = false;
|
||||
if (rcv_thread.joinable())
|
||||
rcv_thread.join();
|
||||
s4 = -1;
|
||||
s6 = -1;
|
||||
|
||||
bound4 = {};
|
||||
if (sin4) {
|
||||
s4 = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if(s4 >= 0) {
|
||||
int rc = bind(s4, (sockaddr*)sin4, sizeof(sockaddr_in));
|
||||
if(rc < 0)
|
||||
throw DhtException("Can't bind IPv4 socket on " + dht::print_addr((sockaddr*)sin4, sizeof(sockaddr_in)));
|
||||
sockaddr_storage ss;
|
||||
socklen_t ss_len = sizeof(ss);
|
||||
getsockname(s4, (sockaddr*)&ss, &ss_len);
|
||||
bound4 = {ss, ss_len};
|
||||
}
|
||||
}
|
||||
if (sin4)
|
||||
s4 = bindSocket(sin4, bound4);
|
||||
|
||||
#if 1
|
||||
bound6 = {};
|
||||
if (sin6) {
|
||||
s6 = socket(PF_INET6, SOCK_DGRAM, 0);
|
||||
if(s6 >= 0) {
|
||||
int val = 1;
|
||||
int rc = setsockopt(s6, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&val, sizeof(val));
|
||||
if(rc < 0)
|
||||
throw DhtException("Can't set IPV6_V6ONLY");
|
||||
|
||||
rc = bind(s6, (sockaddr*)sin6, sizeof(sockaddr_in6));
|
||||
if(rc < 0)
|
||||
throw DhtException("Can't bind IPv6 socket on " + dht::print_addr((sockaddr*)sin6, sizeof(sockaddr_in6)));
|
||||
sockaddr_storage ss;
|
||||
socklen_t ss_len = sizeof(ss);
|
||||
getsockname(s6, (sockaddr*)&ss, &ss_len);
|
||||
bound6 = {ss, ss_len};
|
||||
}
|
||||
}
|
||||
if (sin6)
|
||||
s6 = bindSocket(sin6, bound6);
|
||||
#endif
|
||||
|
||||
dht_ = std::unique_ptr<SecureDht>(new SecureDht {s4, s6, config});
|
||||
|
||||
rcv_thread = std::thread([this,s4,s6]() {
|
||||
running_network = true;
|
||||
rcv_thread = std::thread([this]() {
|
||||
try {
|
||||
while (true) {
|
||||
while (running_network) {
|
||||
struct timeval tv {/*.tv_sec = */0, /*.tv_usec = */250000};
|
||||
fd_set readfds;
|
||||
|
||||
@@ -440,7 +476,7 @@ DhtRunner::doRun(const sockaddr_in* sin4, const sockaddr_in6* sin6, SecureDht::C
|
||||
}
|
||||
}
|
||||
|
||||
if(!running)
|
||||
if (not running_network)
|
||||
break;
|
||||
|
||||
if(rc > 0) {
|
||||
@@ -470,6 +506,10 @@ DhtRunner::doRun(const sockaddr_in* sin4, const sockaddr_in6* sin6, SecureDht::C
|
||||
close(s4);
|
||||
if (s6 >= 0)
|
||||
close(s6);
|
||||
s4 = -1;
|
||||
s6 = -1;
|
||||
bound4 = {};
|
||||
bound6 = {};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -490,7 +530,8 @@ DhtRunner::get(const std::string& key, GetCallback vcb, DoneCallbackSimple dcb,
|
||||
{
|
||||
get(InfoHash::get(key), vcb, dcb, f, w);
|
||||
}
|
||||
void DhtRunner::query(const InfoHash& hash, QueryCallback cb, DoneCallback done_cb, Query q) {
|
||||
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 {
|
||||
@@ -507,7 +548,28 @@ DhtRunner::listen(InfoHash hash, GetCallback vcb, Value::Filter f, Where w)
|
||||
{
|
||||
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)));
|
||||
auto tokenbGlobal = listener_token_++;
|
||||
Listener listener {};
|
||||
listener.hash = hash;
|
||||
listener.f = std::move(f);
|
||||
listener.w = std::move(w);
|
||||
listener.gcb = [hash,vcb,tokenbGlobal,this](const std::vector<Sp<Value>>& vals){
|
||||
if (not vcb(vals)) {
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
cancelListen(hash, tokenbGlobal);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
if (use_proxy)
|
||||
listener.tokenProxyDht = dht.listen(hash, listener.gcb, listener.f, listener.w);
|
||||
else
|
||||
#endif
|
||||
listener.tokenClassicDht = dht.listen(hash, listener.gcb, listener.f, listener.w);
|
||||
listeners_.emplace(tokenbGlobal, std::move(listener));
|
||||
ret_token->set_value(tokenbGlobal);
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
@@ -523,12 +585,22 @@ DhtRunner::listen(const std::string& key, GetCallback vcb, Value::Filter f, Wher
|
||||
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);
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
pending_ops.emplace([=](SecureDht&) {
|
||||
auto it = listeners_.find(token);
|
||||
if (it == listeners_.end()) return;
|
||||
if (it->second.tokenClassicDht)
|
||||
dht_->cancelListen(h, it->second.tokenClassicDht);
|
||||
if (it->second.tokenProxyDht and dht_via_proxy_)
|
||||
dht_via_proxy_->cancelListen(h, it->second.tokenProxyDht);
|
||||
listeners_.erase(it);
|
||||
});
|
||||
#else
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.cancelListen(h, token);
|
||||
});
|
||||
#endif // OPENDHT_PROXY_CLIENT
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
@@ -664,7 +736,7 @@ DhtRunner::tryBootstrapContinuously()
|
||||
for (auto it = nodes.rbegin(); it != nodes.rend(); it++) {
|
||||
++ping_count;
|
||||
try {
|
||||
bootstrap(getAddrInfo(it->first, it->second), [&](bool) {
|
||||
bootstrap(SockAddr::resolve(it->first, it->second), [&](bool) {
|
||||
if (not running)
|
||||
return;
|
||||
{
|
||||
@@ -695,31 +767,6 @@ DhtRunner::tryBootstrapContinuously()
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<std::pair<sockaddr_storage, socklen_t>>
|
||||
DhtRunner::getAddrInfo(const std::string& host, const std::string& service)
|
||||
{
|
||||
std::vector<std::pair<sockaddr_storage, socklen_t>> ips {};
|
||||
if (host.empty())
|
||||
return ips;
|
||||
|
||||
addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
addrinfo* info = nullptr;
|
||||
int rc = getaddrinfo(host.c_str(), service.c_str(), &hints, &info);
|
||||
if(rc != 0)
|
||||
throw std::invalid_argument(std::string("Error: `") + host + ":" + service + "`: " + gai_strerror(rc));
|
||||
|
||||
addrinfo* infop = info;
|
||||
while (infop) {
|
||||
ips.emplace_back(sockaddr_storage(), infop->ai_addrlen);
|
||||
std::copy_n((uint8_t*)infop->ai_addr, infop->ai_addrlen, (uint8_t*)&ips.back().first);
|
||||
infop = infop->ai_next;
|
||||
}
|
||||
freeaddrinfo(info);
|
||||
return ips;
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::bootstrap(const std::string& host, const std::string& service)
|
||||
{
|
||||
@@ -737,13 +784,13 @@ DhtRunner::clearBootstrap()
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::bootstrap(const std::vector<std::pair<sockaddr_storage, socklen_t>>& nodes, DoneCallbackSimple&& cb)
|
||||
DhtRunner::bootstrap(const std::vector<SockAddr>& nodes, DoneCallbackSimple&& cb)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht& dht) mutable {
|
||||
auto rem = cb ? std::make_shared<std::pair<size_t, bool>>(nodes.size(), false) : nullptr;
|
||||
for (auto& node : nodes)
|
||||
dht.pingNode((sockaddr*)&node.first, node.second, cb ? [rem,cb](bool ok) {
|
||||
for (const auto& node : nodes)
|
||||
dht.pingNode(node.get(), node.getLength(), cb ? [rem,cb](bool ok) {
|
||||
auto& r = *rem;
|
||||
r.first--;
|
||||
r.second |= ok;
|
||||
@@ -800,4 +847,118 @@ DhtRunner::findCertificate(InfoHash hash, std::function<void(const std::shared_p
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::resetDht()
|
||||
{
|
||||
listeners_.clear();
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
dht_via_proxy_.reset();
|
||||
#endif // OPENDHT_PROXY_CLIENT
|
||||
dht_.reset();
|
||||
}
|
||||
|
||||
SecureDht*
|
||||
DhtRunner::activeDht() const
|
||||
{
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
return use_proxy? dht_via_proxy_.get() : dht_.get();
|
||||
#else
|
||||
return dht_.get();
|
||||
#endif // OPENDHT_PROXY_CLIENT
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::enableProxy(bool proxify)
|
||||
{
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
if (dht_via_proxy_) {
|
||||
dht_via_proxy_->shutdown({});
|
||||
}
|
||||
if (proxify) {
|
||||
// Init the proxy client
|
||||
auto dht_via_proxy = std::unique_ptr<DhtInterface>(
|
||||
new DhtProxyClient([this]{
|
||||
if (config_.threaded) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht&) mutable {});
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
}, config_.proxy_server, config_.push_node_id)
|
||||
);
|
||||
dht_via_proxy_ = std::unique_ptr<SecureDht>(new SecureDht(std::move(dht_via_proxy), config_.dht_config));
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
if (not pushToken_.empty())
|
||||
dht_via_proxy_->setPushNotificationToken(pushToken_);
|
||||
#endif
|
||||
// add current listeners
|
||||
for (auto& l: listeners_)
|
||||
l.second.tokenProxyDht = dht_via_proxy_->listen(l.second.hash, l.second.gcb, l.second.f, l.second.w);
|
||||
// and use it
|
||||
use_proxy = proxify;
|
||||
} else {
|
||||
use_proxy = proxify;
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
if (not listeners_.empty()) {
|
||||
pending_ops.emplace([this](SecureDht& /*dht*/) mutable {
|
||||
if (not dht_)
|
||||
return;
|
||||
for (auto& l : listeners_) {
|
||||
if (not l.second.tokenClassicDht) {
|
||||
l.second.tokenClassicDht = dht_->listen(l.second.hash, l.second.gcb, l.second.f, l.second.w);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (proxify)
|
||||
std::cerr << "DHT proxy requested but OpenDHT built without proxy support." << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::forwardAllMessages(bool forward)
|
||||
{
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->forwardAllMessages(forward);
|
||||
#endif // OPENDHT_PROXY_CLIENT
|
||||
if (dht_)
|
||||
dht_->forwardAllMessages(forward);
|
||||
#endif // OPENDHT_PROXY_SERVER
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the push notification device token
|
||||
*/
|
||||
void
|
||||
DhtRunner::setPushNotificationToken(const std::string& token) {
|
||||
#if OPENDHT_PROXY_CLIENT && OPENDHT_PUSH_NOTIFICATIONS
|
||||
pushToken_ = token;
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->setPushNotificationToken(token);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::pushNotificationReceived(const std::map<std::string, std::string>& data) const
|
||||
{
|
||||
#if OPENDHT_PROXY_CLIENT && OPENDHT_PUSH_NOTIFICATIONS
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->pushNotificationReceived(data);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::resubscribe(unsigned token)
|
||||
{
|
||||
#if OPENDHT_PROXY_CLIENT && OPENDHT_PUSH_NOTIFICATIONS
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->resubscribe(token);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
+1
-96
@@ -17,11 +17,6 @@
|
||||
*/
|
||||
|
||||
#include "infohash.h"
|
||||
#include "rng.h"
|
||||
|
||||
extern "C" {
|
||||
#include <gnutls/gnutls.h>
|
||||
}
|
||||
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
@@ -29,97 +24,7 @@ extern "C" {
|
||||
|
||||
namespace dht {
|
||||
|
||||
InfoHash::InfoHash(const std::string& hex) {
|
||||
if (hex.size() < 2*HASH_LEN) {
|
||||
fill(0);
|
||||
return;
|
||||
}
|
||||
const auto p = (const char*)hex.data();
|
||||
for (size_t i = 0; i < HASH_LEN; i++) {
|
||||
unsigned res = 0;
|
||||
sscanf(p + 2*i, "%02x", &res);
|
||||
(*this)[i] = res;
|
||||
}
|
||||
}
|
||||
|
||||
InfoHash
|
||||
InfoHash::get(const uint8_t* data, size_t data_len)
|
||||
{
|
||||
InfoHash h;
|
||||
size_t s = h.size();
|
||||
const gnutls_datum_t gnudata = {(uint8_t*)data, (unsigned)data_len};
|
||||
const gnutls_digest_algorithm_t algo = (HASH_LEN == 64) ? GNUTLS_DIG_SHA512 : (
|
||||
(HASH_LEN == 32) ? GNUTLS_DIG_SHA256 : (
|
||||
(HASH_LEN == 20) ? GNUTLS_DIG_SHA1 :
|
||||
GNUTLS_DIG_NULL ));
|
||||
static_assert(algo != GNUTLS_DIG_NULL, "Can't find hash function to use.");
|
||||
int rc = gnutls_fingerprint(algo, &gnudata, h.data(), &s);
|
||||
if (rc == 0 && s == HASH_LEN)
|
||||
return h;
|
||||
throw std::runtime_error(std::string("Error hashing: ") + gnutls_strerror(rc));
|
||||
}
|
||||
|
||||
InfoHash
|
||||
InfoHash::getRandom()
|
||||
{
|
||||
InfoHash h;
|
||||
crypto::random_device rdev;
|
||||
std::uniform_int_distribution<uint32_t> rand_int;
|
||||
auto a = reinterpret_cast<uint32_t*>(h.data());
|
||||
auto b = reinterpret_cast<uint32_t*>(h.data() + h.size());
|
||||
std::generate(a, b, std::bind(rand_int, std::ref(rdev)));
|
||||
return h;
|
||||
}
|
||||
|
||||
struct HexMap : public std::array<std::array<char, 2>, 256> {
|
||||
HexMap() {
|
||||
for (size_t i=0; i<size(); i++) {
|
||||
auto& e = (*this)[i];
|
||||
e[0] = hex_digits[(i >> 4) & 0x0F];
|
||||
e[1] = hex_digits[i & 0x0F];
|
||||
}
|
||||
}
|
||||
private:
|
||||
static constexpr const char* hex_digits = "0123456789abcdef";
|
||||
};
|
||||
|
||||
const char*
|
||||
InfoHash::to_c_str() const
|
||||
{
|
||||
static const HexMap map;
|
||||
thread_local std::array<char, HASH_LEN*2+1> buf;
|
||||
for (size_t i=0; i<HASH_LEN; i++) {
|
||||
auto b = buf.data()+i*2;
|
||||
const auto& m = map[(*this)[i]];
|
||||
*((uint16_t*)b) = *((uint16_t*)&m);
|
||||
}
|
||||
return buf.data();
|
||||
}
|
||||
|
||||
std::string
|
||||
InfoHash::toString() const
|
||||
{
|
||||
return std::string(to_c_str(), HASH_LEN*2);
|
||||
}
|
||||
|
||||
std::ostream& operator<< (std::ostream& s, const InfoHash& h)
|
||||
{
|
||||
s.write(h.to_c_str(), HASH_LEN*2);
|
||||
return s;
|
||||
}
|
||||
|
||||
std::istream& operator>> (std::istream& s, InfoHash& h)
|
||||
{
|
||||
std::array<char, HASH_LEN*2> dat;
|
||||
s.exceptions(std::istream::eofbit | std::istream::failbit);
|
||||
s.read(&(*dat.begin()), dat.size());
|
||||
for (size_t i = 0; i < HASH_LEN; i++) {
|
||||
unsigned res = 0;
|
||||
sscanf(dat.data() + 2*i, "%02x", &res);
|
||||
h[i] = res;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
const HexMap hex_map = {};
|
||||
|
||||
void
|
||||
NodeExport::msgpack_unpack(msgpack::object o)
|
||||
|
||||
+2
-4
@@ -27,14 +27,12 @@ namespace dht {
|
||||
* Foreign nodes asking for updates about an InfoHash.
|
||||
*/
|
||||
struct Listener {
|
||||
size_t sid;
|
||||
time_point time;
|
||||
Query query;
|
||||
|
||||
Listener(size_t sid, time_point t, Query&& q) : sid(sid), time(t), query(std::move(q)) {}
|
||||
Listener(time_point t, Query&& q) : time(t), query(std::move(q)) {}
|
||||
|
||||
void refresh(size_t s, time_point t, Query&& q) {
|
||||
sid = s;
|
||||
void refresh(time_point t, Query&& q) {
|
||||
time = t;
|
||||
query = std::move(q);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace dht {
|
||||
namespace net {
|
||||
|
||||
enum class MessageType {
|
||||
Error = 0,
|
||||
Reply,
|
||||
Ping,
|
||||
FindNode,
|
||||
GetValues,
|
||||
AnnounceValue,
|
||||
Refresh,
|
||||
Listen,
|
||||
ValueData,
|
||||
ValueUpdate
|
||||
};
|
||||
|
||||
} /* namespace net */
|
||||
} /* dht */
|
||||
+91
-58
@@ -53,15 +53,7 @@ constexpr std::chrono::seconds NetworkEngine::RX_MAX_PACKET_TIME;
|
||||
constexpr std::chrono::seconds NetworkEngine::RX_TIMEOUT;
|
||||
|
||||
const std::string NetworkEngine::my_v {"RNG1"};
|
||||
const constexpr uint16_t TransId::INVALID;
|
||||
|
||||
const TransPrefix TransPrefix::PING = {"pn"};
|
||||
const TransPrefix TransPrefix::FIND_NODE = {"fn"};
|
||||
const TransPrefix TransPrefix::GET_VALUES = {"gt"};
|
||||
const TransPrefix TransPrefix::ANNOUNCE_VALUES = {"pt"};
|
||||
const TransPrefix TransPrefix::REFRESH = {"rf"};
|
||||
const TransPrefix TransPrefix::LISTEN = {"lt"};
|
||||
constexpr long unsigned NetworkEngine::MAX_REQUESTS_PER_SEC;
|
||||
constexpr size_t NetworkEngine::MAX_REQUESTS_PER_SEC;
|
||||
|
||||
static const uint8_t v4prefix[16] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0
|
||||
@@ -95,6 +87,24 @@ set_nonblocking(int fd, int nonblocking)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* 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> {
|
||||
TransId() { std::fill(begin(), end(), 0); }
|
||||
TransId(const std::array<char, 4>& o) { std::copy(o.begin(), o.end(), begin()); }
|
||||
TransId(uint32_t id) {
|
||||
*reinterpret_cast<uint32_t*>(data()) = htonl(id);
|
||||
}
|
||||
|
||||
uint32_t toInt() const {
|
||||
return ntohl(*reinterpret_cast<const uint32_t*>(&(*this)[0]));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct NetworkEngine::PartialMessage {
|
||||
SockAddr from;
|
||||
time_point start;
|
||||
@@ -116,8 +126,10 @@ RequestAnswer::RequestAnswer(ParsedMessage&& msg)
|
||||
: ntoken(std::move(msg.token)), values(std::move(msg.values)), fields(std::move(msg.fields)),
|
||||
nodes4(std::move(msg.nodes4)), nodes6(std::move(msg.nodes6)) {}
|
||||
|
||||
NetworkEngine::NetworkEngine(Logger& log, Scheduler& scheduler) : myid(zeroes), DHT_LOG(log), scheduler(scheduler) {}
|
||||
NetworkEngine::NetworkEngine(InfoHash& myid, NetId net, int s, int s6, Logger& log, Scheduler& scheduler,
|
||||
NetworkEngine::NetworkEngine(Logger& log, Scheduler& scheduler, const int& s, const int& s6)
|
||||
: myid(zeroes), DHT_LOG(log), scheduler(scheduler), dht_socket(s), dht_socket6(s6)
|
||||
{}
|
||||
NetworkEngine::NetworkEngine(InfoHash& myid, NetId net, const int& s, const int& s6, Logger& log, Scheduler& scheduler,
|
||||
decltype(NetworkEngine::onError) onError,
|
||||
decltype(NetworkEngine::onNewNode) onNewNode,
|
||||
decltype(NetworkEngine::onReportedAddr) onReportedAddr,
|
||||
@@ -146,14 +158,14 @@ NetworkEngine::~NetworkEngine() {
|
||||
}
|
||||
|
||||
void
|
||||
NetworkEngine::tellListener(Sp<Node> node, uint32_t socket_id, const InfoHash& hash, want_t want,
|
||||
NetworkEngine::tellListener(Sp<Node> node, Tid socket_id, const InfoHash& hash, want_t want,
|
||||
const Blob& ntoken, std::vector<Sp<Node>>&& nodes,
|
||||
std::vector<Sp<Node>>&& nodes6, std::vector<Sp<Value>>&& values,
|
||||
const Query& query)
|
||||
{
|
||||
auto nnodes = bufferNodes(node->getFamily(), hash, want, nodes, nodes6);
|
||||
try {
|
||||
sendNodesValues(node->getAddr(), TransId(socket_id), nnodes.first, nnodes.second, values, query, ntoken);
|
||||
sendNodesValues(node->getAddr(), socket_id, nnodes.first, nnodes.second, values, query, ntoken);
|
||||
} catch (const std::overflow_error& e) {
|
||||
DHT_LOG.e("Can't send value: buffer not large enough !");
|
||||
}
|
||||
@@ -196,7 +208,7 @@ NetworkEngine::requestStep(Sp<Request> sreq)
|
||||
if (req.isExpired(now)) {
|
||||
DHT_LOG.e(node.id, "[node %s] expired !", node.toString().c_str());
|
||||
node.setExpired();
|
||||
if (node.id == zeroes)
|
||||
if (not node.id)
|
||||
requests.erase(req.tid);
|
||||
return;
|
||||
} else if (req.attempt_count == 1) {
|
||||
@@ -208,10 +220,11 @@ NetworkEngine::requestStep(Sp<Request> sreq)
|
||||
node.getAddr());
|
||||
if (err == ENETUNREACH ||
|
||||
err == EHOSTUNREACH ||
|
||||
err == EAFNOSUPPORT)
|
||||
err == EAFNOSUPPORT ||
|
||||
err == EPIPE)
|
||||
{
|
||||
node.setExpired();
|
||||
if (node.id == zeroes)
|
||||
if (not node.id)
|
||||
requests.erase(req.tid);
|
||||
} else {
|
||||
if (err != EAGAIN) {
|
||||
@@ -234,7 +247,7 @@ void
|
||||
NetworkEngine::sendRequest(const Sp<Request>& request)
|
||||
{
|
||||
auto& node = request->node;
|
||||
if (node->id == zeroes)
|
||||
if (not node->id)
|
||||
requests.emplace(request->tid, request);
|
||||
request->start = scheduler.time();
|
||||
node->requested(request);
|
||||
@@ -363,7 +376,7 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, const SockAddr&
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg->id == myid || msg->id == zeroes) {
|
||||
if (msg->id == myid or not msg->id) {
|
||||
DHT_LOG.d("Received message from self");
|
||||
return;
|
||||
}
|
||||
@@ -400,14 +413,23 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
|
||||
const auto& now = scheduler.time();
|
||||
auto node = cache.getNode(msg->id, from, now, true, msg->is_client);
|
||||
|
||||
if (msg->type == MessageType::Error or msg->type == MessageType::Reply) {
|
||||
auto rsocket = node->getSocket(msg->tid.toInt());
|
||||
if (msg->type == MessageType::ValueUpdate) {
|
||||
auto rsocket = node->getSocket(msg->tid);
|
||||
if (not rsocket)
|
||||
throw DhtProtocolException {DhtProtocolException::UNKNOWN_TID, "Can't find socket", msg->id};
|
||||
node->received(now, {});
|
||||
onNewNode(node, 2);
|
||||
deserializeNodes(*msg, from);
|
||||
rsocket->on_receive(node, std::move(*msg));
|
||||
}
|
||||
else if (msg->type == MessageType::Error or msg->type == MessageType::Reply) {
|
||||
auto rsocket = node->getSocket(msg->tid);
|
||||
auto req = node->getRequest(msg->tid);
|
||||
|
||||
/* either response for a request or data for an opened socket */
|
||||
if (not req and not rsocket) {
|
||||
auto req_it = requests.find(msg->tid);
|
||||
if (req_it != requests.end() and req_it->second->node->id == zeroes) {
|
||||
if (req_it != requests.end() and not req_it->second->node->id) {
|
||||
req = req_it->second;
|
||||
req->node = node;
|
||||
requests.erase(req_it);
|
||||
@@ -432,10 +454,10 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
|
||||
|
||||
switch (msg->type) {
|
||||
case MessageType::Error: {
|
||||
if (msg->id != zeroes and (
|
||||
(msg->error_code == DhtProtocolException::NOT_FOUND and msg->tid.matches(TransPrefix::REFRESH)) or
|
||||
(msg->error_code == DhtProtocolException::UNAUTHORIZED and (msg->tid.matches(TransPrefix::ANNOUNCE_VALUES)
|
||||
or msg->tid.matches(TransPrefix::LISTEN)))))
|
||||
if (msg->id and req and (
|
||||
(msg->error_code == DhtProtocolException::NOT_FOUND and req->getType() == MessageType::Refresh) or
|
||||
(msg->error_code == DhtProtocolException::UNAUTHORIZED and (req->getType() == MessageType::AnnounceValue
|
||||
or req->getType() == MessageType::Listen))))
|
||||
{
|
||||
req->last_try = time_point::min();
|
||||
req->reply_time = time_point::min();
|
||||
@@ -476,7 +498,7 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
|
||||
sendPong(from, msg->tid);
|
||||
break;
|
||||
case MessageType::FindNode: {
|
||||
DHT_LOG.d(msg->target, node->id, "[node %s] got 'find' request for %s (%d)", node->toString().c_str(), msg->target.toString().c_str(), msg->want);
|
||||
//DHT_LOG.d(msg->target, node->id, "[node %s] got 'find' request for %s (%d)", node->toString().c_str(), msg->target.toString().c_str(), msg->want);
|
||||
++in_stats.find;
|
||||
RequestAnswer answer = onFindNode(node, msg->target, msg->want);
|
||||
auto nnodes = bufferNodes(from.getFamily(), msg->target, msg->want, answer.nodes4, answer.nodes6);
|
||||
@@ -484,7 +506,7 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
|
||||
break;
|
||||
}
|
||||
case MessageType::GetValues: {
|
||||
DHT_LOG.d(msg->info_hash, node->id, "[node %s] got 'get' request for %s", node->toString().c_str(), msg->info_hash.toString().c_str());
|
||||
//DHT_LOG.d(msg->info_hash, node->id, "[node %s] got 'get' request for %s", node->toString().c_str(), msg->info_hash.toString().c_str());
|
||||
++in_stats.get;
|
||||
RequestAnswer answer = onGetValues(node, msg->info_hash, msg->want, msg->query);
|
||||
auto nnodes = bufferNodes(from.getFamily(), msg->info_hash, msg->want, answer.nodes4, answer.nodes6);
|
||||
@@ -513,7 +535,7 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
|
||||
case MessageType::Listen: {
|
||||
DHT_LOG.d(msg->info_hash, node->id, "[node %s] got 'listen' request for %s", node->toString().c_str(), msg->info_hash.toString().c_str());
|
||||
++in_stats.listen;
|
||||
RequestAnswer answer = onListen(node, msg->info_hash, msg->token, msg->socket_id.toInt(), std::move(msg->query));
|
||||
RequestAnswer answer = onListen(node, msg->info_hash, msg->token, msg->socket_id, std::move(msg->query));
|
||||
sendListenConfirmation(from, msg->tid);
|
||||
break;
|
||||
}
|
||||
@@ -563,9 +585,15 @@ NetworkEngine::send(const char *buf, size_t len, int flags, const SockAddr& addr
|
||||
|
||||
if (s < 0)
|
||||
return EAFNOSUPPORT;
|
||||
#ifdef MSG_NOSIGNAL
|
||||
flags &= MSG_NOSIGNAL;
|
||||
#endif
|
||||
if (sendto(s, buf, len, flags, addr.get(), addr.getLength()) == -1) {
|
||||
int err = errno;
|
||||
DHT_LOG.e("Can't send message to %s: %s", addr.toString().c_str(), strerror(err));
|
||||
if (err == EPIPE) {
|
||||
throw SocketException(EPIPE);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
@@ -573,7 +601,7 @@ NetworkEngine::send(const char *buf, size_t len, int flags, const SockAddr& addr
|
||||
|
||||
Sp<Request>
|
||||
NetworkEngine::sendPing(Sp<Node> node, RequestCb&& on_done, RequestExpiredCb&& on_expired) {
|
||||
auto tid = TransId {TransPrefix::PING, node->getNewTid()};
|
||||
TransId tid (node->getNewTid());
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(network?1:0));
|
||||
@@ -590,7 +618,7 @@ NetworkEngine::sendPing(Sp<Node> node, RequestCb&& on_done, RequestExpiredCb&& o
|
||||
pk.pack(std::string("n")); pk.pack(network);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(tid, node,
|
||||
auto req = std::make_shared<Request>(MessageType::Ping, tid.toInt(), node,
|
||||
Blob(buffer.data(), buffer.data() + buffer.size()),
|
||||
[=](const Request& req_status, ParsedMessage&&) {
|
||||
DHT_LOG.d(req_status.node->id, "[node %s] got pong !", req_status.node->toString().c_str());
|
||||
@@ -610,7 +638,7 @@ NetworkEngine::sendPing(Sp<Node> node, RequestCb&& on_done, RequestExpiredCb&& o
|
||||
}
|
||||
|
||||
void
|
||||
NetworkEngine::sendPong(const SockAddr& addr, TransId tid) {
|
||||
NetworkEngine::sendPong(const SockAddr& addr, Tid tid) {
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(4+(network?1:0));
|
||||
@@ -619,8 +647,9 @@ NetworkEngine::sendPong(const SockAddr& addr, TransId tid) {
|
||||
pk.pack(std::string("id")); pk.pack(myid);
|
||||
insertAddr(pk, addr);
|
||||
|
||||
pk.pack(std::string("t")); pk.pack_bin(tid.size());
|
||||
pk.pack_bin_body((const char*)tid.data(), tid.size());
|
||||
TransId t (tid);
|
||||
pk.pack(std::string("t")); pk.pack_bin(t.size());
|
||||
pk.pack_bin_body((const char*)t.data(), t.size());
|
||||
pk.pack(std::string("y")); pk.pack(std::string("r"));
|
||||
pk.pack(std::string("v")); pk.pack(my_v);
|
||||
if (network) {
|
||||
@@ -633,7 +662,7 @@ NetworkEngine::sendPong(const SockAddr& addr, TransId tid) {
|
||||
Sp<Request>
|
||||
NetworkEngine::sendFindNode(Sp<Node> n, const InfoHash& target, want_t want,
|
||||
RequestCb&& on_done, RequestExpiredCb&& on_expired) {
|
||||
auto tid = TransId {TransPrefix::FIND_NODE, n->getNewTid()};
|
||||
TransId tid (n->getNewTid());
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(network?1:0));
|
||||
@@ -657,7 +686,7 @@ NetworkEngine::sendFindNode(Sp<Node> n, const InfoHash& target, want_t want,
|
||||
pk.pack(std::string("n")); pk.pack(network);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(tid, n,
|
||||
auto req = std::make_shared<Request>(MessageType::FindNode, tid.toInt(), n,
|
||||
Blob(buffer.data(), buffer.data() + buffer.size()),
|
||||
[=](const Request& req_status, ParsedMessage&& msg) { /* on done */
|
||||
if (on_done) {
|
||||
@@ -679,7 +708,7 @@ NetworkEngine::sendFindNode(Sp<Node> n, const InfoHash& target, want_t want,
|
||||
Sp<Request>
|
||||
NetworkEngine::sendGetValues(Sp<Node> n, const InfoHash& info_hash, const Query& query, want_t want,
|
||||
RequestCb&& on_done, RequestExpiredCb&& on_expired) {
|
||||
auto tid = TransId {TransPrefix::GET_VALUES, n->getNewTid()};
|
||||
TransId tid (n->getNewTid());
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(network?1:0));
|
||||
@@ -706,7 +735,7 @@ NetworkEngine::sendGetValues(Sp<Node> n, const InfoHash& info_hash, const Query&
|
||||
pk.pack(std::string("n")); pk.pack(network);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(tid, n,
|
||||
auto req = std::make_shared<Request>(MessageType::GetValues, tid.toInt(), n,
|
||||
Blob(buffer.data(), buffer.data() + buffer.size()),
|
||||
[=](const Request& req_status, ParsedMessage&& msg) { /* on done */
|
||||
if (on_done) {
|
||||
@@ -807,7 +836,7 @@ NetworkEngine::packValueHeader(msgpack::sbuffer& buffer, const std::vector<Sp<Va
|
||||
}
|
||||
|
||||
void
|
||||
NetworkEngine::sendValueParts(TransId tid, const std::vector<Blob>& svals, const SockAddr& addr)
|
||||
NetworkEngine::sendValueParts(const TransId& tid, const std::vector<Blob>& svals, const SockAddr& addr)
|
||||
{
|
||||
msgpack::sbuffer buffer;
|
||||
unsigned i=0;
|
||||
@@ -837,7 +866,7 @@ NetworkEngine::sendValueParts(TransId tid, const std::vector<Blob>& svals, const
|
||||
}
|
||||
|
||||
void
|
||||
NetworkEngine::sendNodesValues(const SockAddr& addr, TransId tid, const Blob& nodes, const Blob& nodes6,
|
||||
NetworkEngine::sendNodesValues(const SockAddr& addr, Tid tid, const Blob& nodes, const Blob& nodes6,
|
||||
const std::vector<Sp<Value>>& st, const Query& query, const Blob& token)
|
||||
{
|
||||
msgpack::sbuffer buffer;
|
||||
@@ -878,8 +907,9 @@ NetworkEngine::sendNodesValues(const SockAddr& addr, TransId tid, const Blob& no
|
||||
}
|
||||
}
|
||||
|
||||
pk.pack(std::string("t")); pk.pack_bin(tid.size());
|
||||
pk.pack_bin_body((const char*)tid.data(), tid.size());
|
||||
TransId t (tid);
|
||||
pk.pack(std::string("t")); pk.pack_bin(t.size());
|
||||
pk.pack_bin_body((const char*)t.data(), t.size());
|
||||
pk.pack(std::string("y")); pk.pack(std::string("r"));
|
||||
pk.pack(std::string("v")); pk.pack(my_v);
|
||||
if (network) {
|
||||
@@ -954,8 +984,8 @@ NetworkEngine::sendListen(Sp<Node> n,
|
||||
RequestExpiredCb&& on_expired,
|
||||
SocketCb&& socket_cb)
|
||||
{
|
||||
uint32_t socket;
|
||||
auto tid = TransId { TransPrefix::LISTEN, n->getNewTid() };
|
||||
Tid socket;
|
||||
TransId tid (n->getNewTid());
|
||||
if (previous and previous->node == n) {
|
||||
socket = previous->getSocket();
|
||||
} else {
|
||||
@@ -994,7 +1024,7 @@ NetworkEngine::sendListen(Sp<Node> n,
|
||||
pk.pack(std::string("n")); pk.pack(network);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(tid, n,
|
||||
auto req = std::make_shared<Request>(MessageType::Listen, tid.toInt(), n,
|
||||
Blob(buffer.data(), buffer.data() + buffer.size()),
|
||||
[=](const Request& req_status, ParsedMessage&& msg) { /* on done */
|
||||
if (on_done)
|
||||
@@ -1012,7 +1042,7 @@ NetworkEngine::sendListen(Sp<Node> n,
|
||||
}
|
||||
|
||||
void
|
||||
NetworkEngine::sendListenConfirmation(const SockAddr& addr, TransId tid) {
|
||||
NetworkEngine::sendListenConfirmation(const SockAddr& addr, Tid tid) {
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(4+(network?1:0));
|
||||
@@ -1021,8 +1051,9 @@ NetworkEngine::sendListenConfirmation(const SockAddr& addr, TransId tid) {
|
||||
pk.pack(std::string("id")); pk.pack(myid);
|
||||
insertAddr(pk, addr);
|
||||
|
||||
pk.pack(std::string("t")); pk.pack_bin(tid.size());
|
||||
pk.pack_bin_body((const char*)tid.data(), tid.size());
|
||||
TransId t (tid);
|
||||
pk.pack(std::string("t")); pk.pack_bin(t.size());
|
||||
pk.pack_bin_body((const char*)t.data(), t.size());
|
||||
pk.pack(std::string("y")); pk.pack(std::string("r"));
|
||||
pk.pack(std::string("v")); pk.pack(my_v);
|
||||
if (network) {
|
||||
@@ -1041,7 +1072,7 @@ NetworkEngine::sendAnnounceValue(Sp<Node> n,
|
||||
RequestCb&& on_done,
|
||||
RequestExpiredCb&& on_expired)
|
||||
{
|
||||
auto tid = TransId {TransPrefix::ANNOUNCE_VALUES, n->getNewTid()};
|
||||
TransId tid (n->getNewTid());
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(network?1:0));
|
||||
@@ -1065,7 +1096,7 @@ NetworkEngine::sendAnnounceValue(Sp<Node> n,
|
||||
pk.pack(std::string("n")); pk.pack(network);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(tid, n,
|
||||
auto req = std::make_shared<Request>(MessageType::AnnounceValue, tid.toInt(), n,
|
||||
Blob(buffer.data(), buffer.data() + buffer.size()),
|
||||
[=](const Request& req_status, ParsedMessage&& msg) { /* on done */
|
||||
if (msg.value_id == Value::INVALID_ID) {
|
||||
@@ -1099,7 +1130,7 @@ NetworkEngine::sendRefreshValue(Sp<Node> n,
|
||||
RequestCb&& on_done,
|
||||
RequestExpiredCb&& on_expired)
|
||||
{
|
||||
auto tid = TransId {TransPrefix::REFRESH, n->getNewTid()};
|
||||
TransId tid (n->getNewTid());
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(network?1:0));
|
||||
@@ -1119,7 +1150,7 @@ NetworkEngine::sendRefreshValue(Sp<Node> n,
|
||||
pk.pack(std::string("n")); pk.pack(network);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(tid, n,
|
||||
auto req = std::make_shared<Request>(MessageType::Refresh, tid.toInt(), n,
|
||||
Blob(buffer.data(), buffer.data() + buffer.size()),
|
||||
[=](const Request& req_status, ParsedMessage&& msg) { /* on done */
|
||||
if (msg.value_id == Value::INVALID_ID) {
|
||||
@@ -1144,7 +1175,7 @@ NetworkEngine::sendRefreshValue(Sp<Node> n,
|
||||
}
|
||||
|
||||
void
|
||||
NetworkEngine::sendValueAnnounced(const SockAddr& addr, TransId tid, Value::Id vid) {
|
||||
NetworkEngine::sendValueAnnounced(const SockAddr& addr, Tid tid, Value::Id vid) {
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(4+(network?1:0));
|
||||
@@ -1154,8 +1185,9 @@ NetworkEngine::sendValueAnnounced(const SockAddr& addr, TransId tid, Value::Id v
|
||||
pk.pack(std::string("vid")); pk.pack(vid);
|
||||
insertAddr(pk, addr);
|
||||
|
||||
pk.pack(std::string("t")); pk.pack_bin(tid.size());
|
||||
pk.pack_bin_body((const char*)tid.data(), tid.size());
|
||||
TransId t(tid);
|
||||
pk.pack(std::string("t")); pk.pack_bin(t.size());
|
||||
pk.pack_bin_body((const char*)t.data(), t.size());
|
||||
pk.pack(std::string("y")); pk.pack(std::string("r"));
|
||||
pk.pack(std::string("v")); pk.pack(my_v);
|
||||
if (network) {
|
||||
@@ -1167,7 +1199,7 @@ NetworkEngine::sendValueAnnounced(const SockAddr& addr, TransId tid, Value::Id v
|
||||
|
||||
void
|
||||
NetworkEngine::sendError(const SockAddr& addr,
|
||||
TransId tid,
|
||||
Tid tid,
|
||||
uint16_t code,
|
||||
const std::string& message,
|
||||
bool include_id)
|
||||
@@ -1185,8 +1217,9 @@ NetworkEngine::sendError(const SockAddr& addr,
|
||||
pk.pack(std::string("id")); pk.pack(myid);
|
||||
}
|
||||
|
||||
pk.pack(std::string("t")); pk.pack_bin(tid.size());
|
||||
pk.pack_bin_body((const char*)tid.data(), tid.size());
|
||||
TransId t(tid);
|
||||
pk.pack(std::string("t")); pk.pack_bin(t.size());
|
||||
pk.pack_bin_body((const char*)t.data(), t.size());
|
||||
pk.pack(std::string("y")); pk.pack(std::string("e"));
|
||||
pk.pack(std::string("v")); pk.pack(my_v);
|
||||
if (network) {
|
||||
@@ -1197,7 +1230,7 @@ NetworkEngine::sendError(const SockAddr& addr,
|
||||
}
|
||||
|
||||
void
|
||||
NetworkEngine::maintainRxBuffer(const TransId& tid)
|
||||
NetworkEngine::maintainRxBuffer(Tid tid)
|
||||
{
|
||||
auto msg = partial_messages.find(tid);
|
||||
if (msg != partial_messages.end()) {
|
||||
|
||||
+9
-10
@@ -30,12 +30,11 @@ constexpr std::chrono::minutes Node::NODE_EXPIRE_TIME;
|
||||
constexpr std::chrono::minutes Node::NODE_GOOD_TIME;
|
||||
constexpr std::chrono::seconds Node::MAX_RESPONSE_TIME;
|
||||
|
||||
|
||||
Node::Node(const InfoHash& id, const SockAddr& addr, bool client)
|
||||
: id(id), addr(addr), is_client(client), sockets_()
|
||||
{
|
||||
crypto::random_device rd;
|
||||
transaction_id = std::uniform_int_distribution<SocketId>{1}(rd);
|
||||
thread_local crypto::random_device rd {};
|
||||
transaction_id = std::uniform_int_distribution<Tid>{1}(rd);
|
||||
}
|
||||
|
||||
/* This is our definition of a known-good node. */
|
||||
@@ -101,7 +100,7 @@ Node::received(time_point now, const Sp<net::Request>& req)
|
||||
}
|
||||
|
||||
Sp<net::Request>
|
||||
Node::getRequest(const net::TransId& tid)
|
||||
Node::getRequest(Tid tid)
|
||||
{
|
||||
auto it = requests_.find(tid);
|
||||
return it != requests_.end() ? it->second : nullptr;
|
||||
@@ -128,28 +127,28 @@ Node::setExpired()
|
||||
sockets_.clear();
|
||||
}
|
||||
|
||||
SocketId
|
||||
Tid
|
||||
Node::openSocket(SocketCb&& cb)
|
||||
{
|
||||
if (++transaction_id == 0)
|
||||
transaction_id = 1;
|
||||
|
||||
auto sock = Socket(std::move(cb));
|
||||
auto sock = std::make_shared<Socket>(std::move(cb));
|
||||
auto s = sockets_.emplace(transaction_id, std::move(sock));
|
||||
if (not s.second)
|
||||
s.first->second = std::move(sock);
|
||||
return transaction_id;
|
||||
}
|
||||
|
||||
Socket*
|
||||
Node::getSocket(SocketId id)
|
||||
Sp<Socket>
|
||||
Node::getSocket(Tid id)
|
||||
{
|
||||
auto it = sockets_.find(id);
|
||||
return it == sockets_.end() ? nullptr : &it->second;
|
||||
return it == sockets_.end() ? nullptr : it->second;
|
||||
}
|
||||
|
||||
void
|
||||
Node::closeSocket(SocketId id)
|
||||
Node::closeSocket(Tid id)
|
||||
{
|
||||
if (id) {
|
||||
sockets_.erase(id);
|
||||
|
||||
+15
-1
@@ -20,6 +20,12 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
NodeCache::~NodeCache()
|
||||
{
|
||||
cache_4.setExpired();
|
||||
cache_6.setExpired();
|
||||
}
|
||||
|
||||
Sp<Node>
|
||||
NodeCache::getNode(const InfoHash& id, sa_family_t family) {
|
||||
return cache(family).getNode(id);
|
||||
@@ -27,7 +33,7 @@ NodeCache::getNode(const InfoHash& id, sa_family_t family) {
|
||||
|
||||
Sp<Node>
|
||||
NodeCache::getNode(const InfoHash& id, const SockAddr& addr, time_point now, bool confirm, bool client) {
|
||||
if (id == zeroes)
|
||||
if (not id)
|
||||
return std::make_shared<Node>(id, addr);
|
||||
return cache(addr.getFamily()).getNode(id, addr, now, confirm, client);
|
||||
}
|
||||
@@ -116,4 +122,12 @@ NodeCache::NodeMap::clearBadNodes() {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NodeCache::NodeMap::setExpired() {
|
||||
for (auto& wn : *this)
|
||||
if (auto n = wn.second.lock())
|
||||
n->setExpired();
|
||||
clear();
|
||||
}
|
||||
|
||||
}
|
||||
+15
-17
@@ -26,17 +26,14 @@
|
||||
namespace dht {
|
||||
namespace net {
|
||||
|
||||
enum class MessageType {
|
||||
Error = 0,
|
||||
Reply,
|
||||
Ping,
|
||||
FindNode,
|
||||
GetValues,
|
||||
AnnounceValue,
|
||||
Refresh,
|
||||
Listen,
|
||||
ValueData
|
||||
};
|
||||
Tid unpackTid(const msgpack::object& o) {
|
||||
switch (o.type) {
|
||||
case msgpack::type::POSITIVE_INTEGER:
|
||||
return o.as<Tid>();
|
||||
default:
|
||||
return ntohl(*reinterpret_cast<const uint32_t*>(o.as<std::array<char, 4>>().data()));
|
||||
}
|
||||
}
|
||||
|
||||
struct ParsedMessage {
|
||||
MessageType type;
|
||||
@@ -51,9 +48,9 @@ struct ParsedMessage {
|
||||
/* target id around which to find nodes */
|
||||
InfoHash target;
|
||||
/* transaction id */
|
||||
TransId tid;
|
||||
Tid tid;
|
||||
/* tid for packets going through request socket */
|
||||
TransId socket_id;
|
||||
Tid socket_id;
|
||||
/* security token */
|
||||
Blob token;
|
||||
/* the value id (announce confirmation) */
|
||||
@@ -128,6 +125,7 @@ ParsedMessage::msgpack_unpack(msgpack::object msg)
|
||||
{
|
||||
auto y = findMapValue(msg, "y");
|
||||
auto r = findMapValue(msg, "r");
|
||||
auto u = findMapValue(msg, "u");
|
||||
auto e = findMapValue(msg, "e");
|
||||
auto v = findMapValue(msg, "p");
|
||||
|
||||
@@ -156,6 +154,8 @@ ParsedMessage::msgpack_unpack(msgpack::object msg)
|
||||
type = MessageType::Reply;
|
||||
else if (v)
|
||||
type = MessageType::ValueData;
|
||||
else if (u)
|
||||
type = MessageType::ValueUpdate;
|
||||
else if (y and y->as<std::string>() != "q")
|
||||
throw msgpack::type_error();
|
||||
else if (q == "ping")
|
||||
@@ -188,9 +188,9 @@ ParsedMessage::msgpack_unpack(msgpack::object msg)
|
||||
}
|
||||
|
||||
auto a = findMapValue(msg, "a");
|
||||
if (!a && !r && !e)
|
||||
if (!a && !r && !e && !u)
|
||||
throw msgpack::type_error();
|
||||
auto& req = a ? *a : (r ? *r : *e);
|
||||
auto& req = a ? *a : (r ? *r : (u ? *u : *e));
|
||||
|
||||
if (e) {
|
||||
if (e->type != msgpack::type::ARRAY)
|
||||
@@ -232,13 +232,11 @@ ParsedMessage::msgpack_unpack(msgpack::object msg)
|
||||
if (l == sizeof(in_addr)) {
|
||||
addr.setFamily(AF_INET);
|
||||
auto& a = addr.getIPv4();
|
||||
std::fill_n((uint8_t*)&a, sizeof(a), 0);
|
||||
a.sin_port = 0;
|
||||
std::copy_n(sa->via.bin.ptr, l, (char*)&a.sin_addr);
|
||||
} else if (l == sizeof(in6_addr)) {
|
||||
addr.setFamily(AF_INET6);
|
||||
auto& a = addr.getIPv6();
|
||||
std::fill_n((uint8_t*)&a, sizeof(a), 0);
|
||||
a.sin6_port = 0;
|
||||
std::copy_n(sa->via.bin.ptr, l, (char*)&a.sin6_addr);
|
||||
}
|
||||
|
||||
+10
-8
@@ -59,18 +59,19 @@ struct Request {
|
||||
State getState() const { return state_; }
|
||||
|
||||
Request(State state = State::PENDING) : state_(state) {}
|
||||
Request(const TransId& tid,
|
||||
Request(MessageType type, Tid tid,
|
||||
Sp<Node> node,
|
||||
Blob&& msg,
|
||||
std::function<void(const Request&, ParsedMessage&&)> on_done,
|
||||
std::function<void(const Request&, bool)> on_expired,
|
||||
uint32_t socket = 0) :
|
||||
node(node), on_done(on_done), on_expired(on_expired), tid(tid), msg(std::move(msg)), socket(socket) { }
|
||||
Tid socket = 0) :
|
||||
node(node), tid(tid), type(type), on_done(on_done), on_expired(on_expired), msg(std::move(msg)), socket(socket) { }
|
||||
|
||||
TransId getTid() const { return tid; }
|
||||
Tid getTid() const { return tid; }
|
||||
MessageType getType() const { return type; }
|
||||
|
||||
uint32_t getSocket() { return socket; }
|
||||
uint32_t closeSocket() { auto ret = socket; socket = 0; return ret; }
|
||||
Tid getSocket() { return socket; }
|
||||
Tid closeSocket() { auto ret = socket; socket = 0; return ret; }
|
||||
|
||||
void setExpired() {
|
||||
if (pending()) {
|
||||
@@ -107,6 +108,8 @@ private:
|
||||
msg.clear();
|
||||
}
|
||||
|
||||
const Tid tid {0}; /* the request id. */
|
||||
const MessageType type {};
|
||||
State state_ {State::PENDING};
|
||||
|
||||
unsigned attempt_count {0}; /* number of attempt to process the request. */
|
||||
@@ -116,9 +119,8 @@ private:
|
||||
std::function<void(const Request&, ParsedMessage&&)> on_done {};
|
||||
std::function<void(const Request&, bool)> on_expired {};
|
||||
|
||||
const TransId tid; /* the request id. */
|
||||
Blob msg {}; /* the serialized message. */
|
||||
uint32_t socket; /* the socket used for further reponses. */
|
||||
Tid socket; /* the socket used for further reponses. */
|
||||
};
|
||||
|
||||
} /* namespace net */
|
||||
|
||||
@@ -76,7 +76,7 @@ RoutingTable::randomId(const RoutingTable::const_iterator& it) const
|
||||
|
||||
int b = bit/8;
|
||||
InfoHash id_return;
|
||||
std::copy_n(it->first.begin(), b, id_return.begin());
|
||||
std::copy_n(it->first.cbegin(), b, id_return.begin());
|
||||
id_return[b] = it->first[b] & (0xFF00 >> (bit % 8));
|
||||
id_return[b] |= rand_byte(rd) >> (bit % 8);
|
||||
for (unsigned i = b + 1; i < HASH_LEN; i++)
|
||||
|
||||
+9
-128
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2018 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -74,6 +74,7 @@ struct Dht::SearchNode {
|
||||
time_point last_get_reply {time_point::min()}; /* last time received valid token */
|
||||
bool candidate {false}; /* A search node is candidate if the search is/was synced and this
|
||||
node is a new candidate for inclusion. */
|
||||
Sp<Scheduler::Job> syncJob {};
|
||||
|
||||
SearchNode() : node() {}
|
||||
SearchNode(const SearchNode&) = delete;
|
||||
@@ -93,11 +94,17 @@ struct Dht::SearchNode {
|
||||
/**
|
||||
* Can we use this node to listen/announce now ?
|
||||
*/
|
||||
bool isSynced(time_point now) const {
|
||||
bool isSynced(const time_point& now) const {
|
||||
return not node->isExpired() and
|
||||
not token.empty() and last_get_reply >= now - Node::NODE_EXPIRE_TIME;
|
||||
}
|
||||
|
||||
time_point getSyncTime(const time_point& now) const {
|
||||
if (node->isExpired() or token.empty())
|
||||
return now;
|
||||
return last_get_reply + Node::NODE_EXPIRE_TIME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Could a particular "get" request be sent to this node now ?
|
||||
*
|
||||
@@ -421,8 +428,6 @@ struct Dht::Search {
|
||||
done = true;
|
||||
}
|
||||
|
||||
time_point getUpdateTime(time_point now) const;
|
||||
|
||||
bool isAnnounced(Value::Id id) const;
|
||||
bool isListening(time_point now) const;
|
||||
|
||||
@@ -460,26 +465,6 @@ struct Dht::Search {
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time of the next "announce" event for this search,
|
||||
* or time_point::max() if no such event is planned.
|
||||
* Only makes sense when the search is synced.
|
||||
*/
|
||||
time_point getAnnounceTime(time_point now) const;
|
||||
|
||||
/**
|
||||
* Returns the time of the next "listen" event for this search,
|
||||
* or time_point::max() if no such event is planned.
|
||||
* Only makes sense when the search is synced.
|
||||
*/
|
||||
time_point getListenTime(time_point now) const;
|
||||
|
||||
/**
|
||||
* Returns the time of the next event for this search,
|
||||
* or time_point::max() if no such event is planned.
|
||||
*/
|
||||
time_point getNextStepTime(time_point now) const;
|
||||
|
||||
/**
|
||||
* Removes a node which have been expired for at least
|
||||
* NODE::NODE_EXPIRE_TIME minutes. The search for an expired node starts
|
||||
@@ -721,36 +706,6 @@ Dht::Search::isDone(const Get& get) const
|
||||
return true;
|
||||
}
|
||||
|
||||
time_point
|
||||
Dht::Search::getUpdateTime(time_point now) const
|
||||
{
|
||||
time_point ut = time_point::max();
|
||||
const auto last_get = getLastGetTime();
|
||||
unsigned i = 0, t = 0, d = 0;
|
||||
const auto solicited_nodes = currentlySolicitedNodeCount();
|
||||
for (const auto& sn : nodes) {
|
||||
if (sn.node->isExpired() or (sn.candidate and t >= TARGET_NODES))
|
||||
continue;
|
||||
auto pending = sn.pendingGet();
|
||||
if (sn.last_get_reply < std::max(now - Node::NODE_EXPIRE_TIME, last_get) or pending) {
|
||||
// not isSynced
|
||||
if (not pending and solicited_nodes < MAX_REQUESTED_SEARCH_NODES)
|
||||
ut = std::min(ut, now);
|
||||
if (not sn.candidate)
|
||||
d++;
|
||||
} else
|
||||
ut = std::min(ut, sn.last_get_reply + Node::NODE_EXPIRE_TIME);
|
||||
|
||||
t++;
|
||||
if (not sn.candidate and ++i == TARGET_NODES)
|
||||
break;
|
||||
}
|
||||
if (not callbacks.empty() and d == 0)
|
||||
// If all synced/updated but some callbacks remain, step now to clear them
|
||||
return now;
|
||||
return ut;
|
||||
}
|
||||
|
||||
bool
|
||||
Dht::Search::isAnnounced(Value::Id id) const
|
||||
{
|
||||
@@ -790,78 +745,4 @@ Dht::Search::isListening(time_point now) const
|
||||
return i;
|
||||
}
|
||||
|
||||
time_point
|
||||
Dht::Search::getAnnounceTime(time_point now) const
|
||||
{
|
||||
if (nodes.empty())
|
||||
return time_point::max();
|
||||
time_point ret {time_point::max()};
|
||||
for (const auto& a : announce) {
|
||||
if (!a.value) continue;
|
||||
unsigned i = 0, t = 0;
|
||||
for (const auto& n : nodes) {
|
||||
if (not n.isSynced(now) or (n.candidate and t >= TARGET_NODES))
|
||||
continue;
|
||||
ret = std::min(ret, n.getAnnounceTime(a.value->id));
|
||||
t++;
|
||||
if (not n.candidate and ++i == TARGET_NODES)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
time_point
|
||||
Dht::Search::getListenTime(time_point now) const
|
||||
{
|
||||
if (listeners.empty())
|
||||
return time_point::max();
|
||||
|
||||
time_point listen_time {time_point::max()};
|
||||
unsigned i = 0, t = 0;
|
||||
for (const auto& sn : nodes) {
|
||||
if (not sn.isSynced(now) or (sn.candidate and t >= LISTEN_NODES))
|
||||
continue;
|
||||
for (auto& l : listeners)
|
||||
listen_time = std::min(listen_time, sn.getListenTime(l.second.query));
|
||||
t++;
|
||||
if (not sn.candidate and ++i == LISTEN_NODES)
|
||||
break;
|
||||
}
|
||||
return listen_time;
|
||||
}
|
||||
|
||||
time_point
|
||||
Dht::Search::getNextStepTime(time_point now) const
|
||||
{
|
||||
auto next_step = time_point::max();
|
||||
if (expired or done)
|
||||
return next_step;
|
||||
|
||||
auto ut = getUpdateTime(now);
|
||||
if (ut != time_point::max()) {
|
||||
//std::cout << id.toString() << " IPv" << (af==AF_INET?"4":"6") << " update time in " << print_dt(ut - now) << " s" << std::endl;
|
||||
next_step = std::min(next_step, ut);
|
||||
}
|
||||
|
||||
if (isSynced(now))
|
||||
{
|
||||
auto at = getAnnounceTime(now);
|
||||
if (at != time_point::max()) {
|
||||
//std::cout << id.toString() << " IPv" << (af==AF_INET?"4":"6") << " announce time in " << print_dt(at - now) << " s" << std::endl;
|
||||
next_step = std::min(next_step, at);
|
||||
}
|
||||
|
||||
auto lt = getListenTime(now);
|
||||
if (lt != time_point::max()) {
|
||||
//std::cout << id.toString() << " IPv" << (af==AF_INET?"4":"6") << " listen time in " << print_dt(lt - now) << " s" << std::endl;
|
||||
next_step = std::min(next_step, lt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return next_step;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+19
-23
@@ -1,7 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -32,20 +33,10 @@ extern "C" {
|
||||
|
||||
namespace dht {
|
||||
|
||||
Config& getConfig(SecureDht::Config& conf)
|
||||
SecureDht::SecureDht(std::unique_ptr<DhtInterface> dht, SecureDht::Config conf)
|
||||
: dht_(std::move(dht)), key_(conf.id.first), certificate_(conf.id.second)
|
||||
{
|
||||
auto& c = conf.node_config;
|
||||
if (c.node_id == InfoHash() and conf.id.second)
|
||||
c.node_id = InfoHash::get("node:"+conf.id.second->getId().toString());
|
||||
return c;
|
||||
}
|
||||
|
||||
SecureDht::SecureDht(int s, int s6, SecureDht::Config conf)
|
||||
: Dht(s, s6, getConfig(conf)), key_(conf.id.first), certificate_(conf.id.second)
|
||||
{
|
||||
if (s < 0 && s6 < 0)
|
||||
return;
|
||||
|
||||
if (!dht_) return;
|
||||
for (const auto& type : DEFAULT_TYPES)
|
||||
registerType(type);
|
||||
|
||||
@@ -59,7 +50,7 @@ SecureDht::SecureDht(int s, int s6, SecureDht::Config conf)
|
||||
if (key_ and certId != key_->getPublicKey().getId())
|
||||
throw DhtException("SecureDht: provided certificate doesn't match private key.");
|
||||
|
||||
Dht::put(certId, Value {
|
||||
dht_->put(certId, Value {
|
||||
CERTIFICATE_TYPE,
|
||||
*certificate_,
|
||||
1
|
||||
@@ -190,7 +181,7 @@ SecureDht::findCertificate(const InfoHash& node, std::function<void(const Sp<cry
|
||||
}
|
||||
|
||||
auto found = std::make_shared<bool>(false);
|
||||
Dht::get(node, [cb,node,found,this](const std::vector<Sp<Value>>& vals) {
|
||||
dht_->get(node, [cb,node,found,this](const std::vector<Sp<Value>>& vals) {
|
||||
if (*found)
|
||||
return false;
|
||||
for (const auto& v : vals) {
|
||||
@@ -238,8 +229,13 @@ SecureDht::getCallbackFilter(GetCallback cb, Value::Filter&& filter)
|
||||
for (const auto& v : values) {
|
||||
// Decrypt encrypted values
|
||||
if (v->isEncrypted()) {
|
||||
if (not key_)
|
||||
if (not key_) {
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
if (forward_all_) // We are currently a proxy, send messages to clients.
|
||||
tmpvals.push_back(v);
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
Value decrypted_val (decrypt(*v));
|
||||
if (decrypted_val.recipient == getId()) {
|
||||
@@ -277,13 +273,13 @@ SecureDht::getCallbackFilter(GetCallback cb, Value::Filter&& filter)
|
||||
void
|
||||
SecureDht::get(const InfoHash& id, GetCallback cb, DoneCallback donecb, Value::Filter&& f, Where&& w)
|
||||
{
|
||||
Dht::get(id, getCallbackFilter(cb, std::forward<Value::Filter>(f)), donecb, {}, std::forward<Where>(w));
|
||||
dht_->get(id, getCallbackFilter(cb, std::forward<Value::Filter>(f)), donecb, {}, std::forward<Where>(w));
|
||||
}
|
||||
|
||||
size_t
|
||||
SecureDht::listen(const InfoHash& id, GetCallback cb, Value::Filter&& f, Where&& w)
|
||||
SecureDht::listen(const InfoHash& id, GetCallback cb, Value::Filter f, Where w)
|
||||
{
|
||||
return Dht::listen(id, getCallbackFilter(cb, std::forward<Value::Filter>(f)), {}, std::forward<Where>(w));
|
||||
return dht_->listen(id, getCallbackFilter(cb, std::forward<Value::Filter>(f)), {}, std::forward<Where>(w));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -295,7 +291,7 @@ SecureDht::putSigned(const InfoHash& hash, Sp<Value> val, DoneCallback callback,
|
||||
}
|
||||
|
||||
// Check if we are already announcing a value
|
||||
auto p = getPut(hash, val->id);
|
||||
auto p = dht_->getPut(hash, val->id);
|
||||
if (p && val->seq <= p->seq) {
|
||||
DHT_LOG.DEBUG("Found previous value being announced.");
|
||||
val->seq = p->seq + 1;
|
||||
@@ -317,7 +313,7 @@ SecureDht::putSigned(const InfoHash& hash, Sp<Value> val, DoneCallback callback,
|
||||
},
|
||||
[hash,val,this,callback,permanent] (bool /* ok */) {
|
||||
sign(*val);
|
||||
put(hash, val, callback, time_point::max(), permanent);
|
||||
dht_->put(hash, val, callback, time_point::max(), permanent);
|
||||
},
|
||||
Value::IdFilter(val->id)
|
||||
);
|
||||
@@ -334,7 +330,7 @@ SecureDht::putEncrypted(const InfoHash& hash, const InfoHash& to, Sp<Value> val,
|
||||
}
|
||||
DHT_LOG.WARN("Encrypting data for PK: %s", pk->getId().toString().c_str());
|
||||
try {
|
||||
put(hash, encrypt(*val, *pk), callback, time_point::max(), permanent);
|
||||
dht_->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)
|
||||
|
||||
+58
-19
@@ -19,7 +19,6 @@
|
||||
#include "utils.h"
|
||||
#include "sockaddr.h"
|
||||
#include "default_types.h"
|
||||
#include "net.h"
|
||||
|
||||
/* An IPv4 equivalent to IN6_IS_ADDR_UNSPECIFIED */
|
||||
#ifndef IN_IS_ADDR_UNSPECIFIED
|
||||
@@ -28,6 +27,33 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
static constexpr std::array<uint8_t, 12> MAPPED_IPV4_PREFIX {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}};
|
||||
|
||||
std::vector<SockAddr>
|
||||
SockAddr::resolve(const std::string& host, const std::string& service)
|
||||
{
|
||||
std::vector<SockAddr> ips {};
|
||||
if (host.empty())
|
||||
return ips;
|
||||
|
||||
addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
addrinfo* info = nullptr;
|
||||
int rc = getaddrinfo(host.c_str(), service.empty() ? nullptr : service.c_str(), &hints, &info);
|
||||
if(rc != 0)
|
||||
throw std::invalid_argument(std::string("Error: `") + host + ":" + service + "`: " + gai_strerror(rc));
|
||||
|
||||
addrinfo* infop = info;
|
||||
while (infop) {
|
||||
ips.emplace_back(infop->ai_addr, infop->ai_addrlen);
|
||||
infop = infop->ai_next;
|
||||
}
|
||||
freeaddrinfo(info);
|
||||
return ips;
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
print_addr(const sockaddr* sa, socklen_t slen)
|
||||
{
|
||||
@@ -70,7 +96,8 @@ SockAddr::isLoopback() const
|
||||
{
|
||||
switch (getFamily()) {
|
||||
case AF_INET: {
|
||||
uint8_t b1 = (uint8_t)(getIPv4().sin_addr.s_addr >> 24);
|
||||
auto addr_host = ntohl(getIPv4().sin_addr.s_addr);
|
||||
uint8_t b1 = (uint8_t)(addr_host >> 24);
|
||||
return b1 == 127;
|
||||
}
|
||||
case AF_INET6:
|
||||
@@ -87,10 +114,11 @@ SockAddr::isPrivate() const
|
||||
return true;
|
||||
}
|
||||
switch (getFamily()) {
|
||||
case AF_INET:
|
||||
case AF_INET: {
|
||||
auto addr_host = ntohl(getIPv4().sin_addr.s_addr);
|
||||
uint8_t b1, b2;
|
||||
b1 = (uint8_t)(getIPv4().sin_addr.s_addr >> 24);
|
||||
b2 = (uint8_t)((getIPv4().sin_addr.s_addr >> 16) & 0x0ff);
|
||||
b1 = (uint8_t)(addr_host >> 24);
|
||||
b2 = (uint8_t)((addr_host >> 16) & 0x0ff);
|
||||
// 10.x.y.z
|
||||
if (b1 == 10)
|
||||
return true;
|
||||
@@ -101,6 +129,7 @@ SockAddr::isPrivate() const
|
||||
if ((b1 == 192) && (b2 == 168))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
case AF_INET6: {
|
||||
const uint8_t* addr6 = reinterpret_cast<const uint8_t*>(&getIPv6().sin6_addr);
|
||||
if (addr6[0] == 0xfc)
|
||||
@@ -112,6 +141,30 @@ SockAddr::isPrivate() const
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SockAddr::isMappedIPv4() const
|
||||
{
|
||||
if (getFamily() != AF_INET6)
|
||||
return false;
|
||||
const uint8_t* addr6 = reinterpret_cast<const uint8_t*>(&getIPv6().sin6_addr);
|
||||
return std::equal(MAPPED_IPV4_PREFIX.begin(), MAPPED_IPV4_PREFIX.end(), addr6);
|
||||
}
|
||||
|
||||
SockAddr
|
||||
SockAddr::getMappedIPv4() const
|
||||
{
|
||||
if (not isMappedIPv4())
|
||||
return *this;
|
||||
SockAddr ret;
|
||||
ret.setFamily(AF_INET);
|
||||
ret.setPort(getPort());
|
||||
auto addr6 = reinterpret_cast<const uint8_t*>(&getIPv6().sin6_addr);
|
||||
auto addr4 = reinterpret_cast<uint8_t*>(&ret.getIPv4().sin_addr);
|
||||
addr6 += MAPPED_IPV4_PREFIX.size();
|
||||
std::copy_n(addr6, sizeof(in_addr), addr4);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool operator==(const SockAddr& a, const SockAddr& b) {
|
||||
return a.equals(b);
|
||||
}
|
||||
@@ -161,18 +214,4 @@ findMapValue(msgpack::object& map, const std::string& key) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
namespace net {
|
||||
|
||||
TransId
|
||||
unpackTid(msgpack::object& o) {
|
||||
switch (o.type) {
|
||||
case msgpack::type::POSITIVE_INTEGER:
|
||||
return o.as<uint32_t>();
|
||||
default:
|
||||
return o.as<std::array<char, 4>>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+71
-1
@@ -22,6 +22,11 @@
|
||||
#include "default_types.h"
|
||||
#include "securedht.h" // print certificate ID
|
||||
|
||||
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
|
||||
#include "base64.h"
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
|
||||
|
||||
namespace dht {
|
||||
|
||||
const std::string Query::QUERY_PARSE_ERROR {"Error parsing query."};
|
||||
@@ -41,7 +46,7 @@ std::ostream& operator<< (std::ostream& s, const Value& v)
|
||||
s << "encrypted ";
|
||||
else if (v.isSigned()) {
|
||||
s << "signed (v" << v.seq << ") ";
|
||||
if (v.recipient != InfoHash())
|
||||
if (v.recipient)
|
||||
s << "decrypted ";
|
||||
}
|
||||
if (not v.isEncrypted()) {
|
||||
@@ -167,6 +172,71 @@ Value::msgpack_unpack_body(const msgpack::object& o)
|
||||
}
|
||||
}
|
||||
|
||||
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
|
||||
Value::Value(Json::Value& json)
|
||||
{
|
||||
try {
|
||||
if (json.isMember("id"))
|
||||
id = ValueType::Id(json["id"].asInt());
|
||||
} catch (...) { }
|
||||
if (json.isMember("cypher")) {
|
||||
auto cypherStr = json["cypher"].asString();
|
||||
cypherStr = base64_decode(cypherStr);
|
||||
cypher = std::vector<unsigned char>(cypherStr.begin(), cypherStr.end());
|
||||
}
|
||||
if (json.isMember("sig")) {
|
||||
auto sigStr = json["sig"].asString();
|
||||
sigStr = base64_decode(sigStr);
|
||||
signature = std::vector<unsigned char>(sigStr.begin(), sigStr.end());
|
||||
}
|
||||
if (json.isMember("seq"))
|
||||
seq = json["seq"].asInt();
|
||||
if (json.isMember("owner")) {
|
||||
auto ownerStr = json["owner"].asString();
|
||||
auto ownerBlob = std::vector<unsigned char>(ownerStr.begin(), ownerStr.end());
|
||||
owner = std::make_shared<const crypto::PublicKey>(ownerBlob);
|
||||
}
|
||||
if (json.isMember("to")) {
|
||||
auto toStr = json["to"].asString();
|
||||
recipient = InfoHash(toStr);
|
||||
}
|
||||
if (json.isMember("type"))
|
||||
type = json["type"].asInt();
|
||||
if (json.isMember("data")){
|
||||
auto dataStr = json["data"].asString();
|
||||
dataStr = base64_decode(dataStr);
|
||||
data = std::vector<unsigned char>(dataStr.begin(), dataStr.end());
|
||||
}
|
||||
if (json.isMember("utype"))
|
||||
user_type = json["utype"].asString();
|
||||
}
|
||||
|
||||
Json::Value
|
||||
Value::toJson() const
|
||||
{
|
||||
Json::Value val;
|
||||
val["id"] = std::to_string(id);
|
||||
if (isEncrypted()) {
|
||||
val["cypher"] = base64_encode(cypher);
|
||||
} else {
|
||||
if (isSigned())
|
||||
val["sig"] = base64_encode(signature);
|
||||
bool has_owner = owner && *owner;
|
||||
if (has_owner) { // isSigned
|
||||
val["seq"] = seq;
|
||||
val["owner"] = owner->toString();
|
||||
if (recipient != InfoHash())
|
||||
val["to"] = recipient.toString();
|
||||
}
|
||||
val["type"] = type;
|
||||
val["data"] = base64_encode(data);
|
||||
if (not user_type.empty())
|
||||
val["utype"] = user_type;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
|
||||
bool
|
||||
FieldValue::operator==(const FieldValue& vfd) const
|
||||
{
|
||||
|
||||
+9
-4
@@ -45,7 +45,7 @@ void print_node_info(const DhtRunner& dht, const dht_params&) {
|
||||
void print_usage() {
|
||||
std::cout << "Usage: dhtchat [-n network_id] [-p local_port] [-b bootstrap_host[:port]]" << std::endl << std::endl;
|
||||
std::cout << "dhtchat, a simple OpenDHT command line chat client." << std::endl;
|
||||
std::cout << "Report bugs to: http://opendht.net" << std::endl;
|
||||
std::cout << "Report bugs to: https://opendht.net" << std::endl;
|
||||
}
|
||||
|
||||
int
|
||||
@@ -67,6 +67,13 @@ main(int argc, char **argv)
|
||||
if (not params.bootstrap.first.empty())
|
||||
dht.bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str());
|
||||
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
if (!params.proxyclient.empty()) {
|
||||
dht.setProxyServer(params.proxyclient);
|
||||
dht.enableProxy(true);
|
||||
}
|
||||
#endif //OPENDHT_PROXY_CLIENT
|
||||
|
||||
print_node_info(dht, params);
|
||||
std::cout << " type 'c {hash}' to join a channel" << std::endl << std::endl;
|
||||
|
||||
@@ -88,8 +95,6 @@ main(int argc, char **argv)
|
||||
if (line.empty())
|
||||
continue;
|
||||
|
||||
static constexpr dht::InfoHash INVALID_ID {};
|
||||
|
||||
std::istringstream iss(line);
|
||||
std::string op, idstr;
|
||||
iss >> op;
|
||||
@@ -99,7 +104,7 @@ main(int argc, char **argv)
|
||||
else if (op == "c") {
|
||||
iss >> idstr;
|
||||
room = InfoHash(idstr);
|
||||
if (room == INVALID_ID) {
|
||||
if (not room) {
|
||||
room = InfoHash::get(idstr);
|
||||
std::cout << "Joining h(" << idstr << ") = " << room << std::endl;
|
||||
}
|
||||
|
||||
+171
-29
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -29,9 +30,9 @@ extern "C" {
|
||||
using namespace dht;
|
||||
|
||||
void print_usage() {
|
||||
std::cout << "Usage: dhtnode [-v [-l logfile]] [-i] [-d] [-n network_id] [-p local_port] [-b bootstrap_host[:port]]" << std::endl << std::endl;
|
||||
std::cout << "Usage: dhtnode [-v [-l logfile]] [-i] [-d] [-n network_id] [-p local_port] [-b bootstrap_host[:port]] [--proxyserver local_port]" << std::endl << std::endl;
|
||||
std::cout << "dhtnode, a simple OpenDHT command line node runner." << std::endl;
|
||||
std::cout << "Report bugs to: http://opendht.net" << std::endl;
|
||||
std::cout << "Report bugs to: https://opendht.net" << std::endl;
|
||||
}
|
||||
|
||||
void print_id_req() {
|
||||
@@ -55,23 +56,54 @@ void print_help() {
|
||||
<< " ll Print basic information and stats about the current node." << std::endl
|
||||
<< " ls [key] Print basic information about current search(es)." << std::endl
|
||||
<< " ld [key] Print basic information about currenty stored values on this node (or key)." << std::endl
|
||||
<< " lr Print the full current routing table of this node" << std::endl;
|
||||
<< " lr Print the full current routing table of this node." << std::endl;
|
||||
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
std::cout << std::endl << "Operations with the proxy:" << std::endl
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
<< " pst [port] <pushServer> Start the proxy interface on port." << std::endl
|
||||
#else
|
||||
<< " pst [port] Start the proxy interface on port." << std::endl
|
||||
#endif // OPENDHT_PUSH_NOTIFICATIONS
|
||||
<< " psp [port] Stop the proxy interface on port." << std::endl;
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
std::cout << std::endl << "Operations with the proxy:" << std::endl
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
<< " stt [server_address] <device_key> Start the proxy client." << std::endl
|
||||
<< " rs [token] Resubscribe to opendht." << std::endl
|
||||
<< " rp [push_notification] Inject a push notification in Opendht." << std::endl
|
||||
#else
|
||||
<< " stt [server_address] Start the proxy client." << std::endl
|
||||
#endif // OPENDHT_PUSH_NOTIFICATIONS
|
||||
<< " stp Stop the proxy client." << std::endl;
|
||||
#endif //OPENDHT_PROXY_CLIENT
|
||||
|
||||
std::cout << std::endl << "Operations on the DHT:" << std::endl
|
||||
<< " b <ip:port> Ping potential node at given IP address/port." << std::endl
|
||||
<< " b <ip:port> Ping potential node at given IP address/port." << std::endl
|
||||
<< " g <key> Get values at <key>." << std::endl
|
||||
<< " l <key> Listen for value changes at <key>." << std::endl
|
||||
<< " cl <key> <token> Cancel listen for <token> and <key>." << std::endl
|
||||
<< " p <key> <str> Put string value at <key>." << std::endl
|
||||
<< " pp <key> <str> Put string value at <key> (persistent version)." << std::endl
|
||||
<< " s <key> <str> Put string value at <key>, signed with our generated private key." << std::endl
|
||||
<< " e <key> <dest> <str> Put string value at <key>, encrypted for <dest> with its public key (if found)." << std::endl;
|
||||
|
||||
#ifdef OPENDHT_INDEXATION
|
||||
std::cout << std::endl << "Indexation operations on the DHT:" << std::endl
|
||||
<< " il <name> <key> [exact match] Lookup the index named <name> with the key <key>." << std::endl
|
||||
<< " Set [exact match] to 'false' for inexact match lookup." << std::endl
|
||||
<< " ii <name> <key> <value> Inserts the value <value> under the key <key> in the index named <name>." << std::endl
|
||||
<< std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params)
|
||||
void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
, std::map<in_port_t, std::unique_ptr<DhtProxyServer>>& proxies
|
||||
#endif
|
||||
)
|
||||
{
|
||||
print_node_info(dht, params);
|
||||
std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl << std::endl;
|
||||
@@ -81,7 +113,9 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params)
|
||||
using_history();
|
||||
#endif
|
||||
|
||||
#ifdef OPENDHT_INDEXATION
|
||||
std::map<std::string, indexation::Pht> indexes;
|
||||
#endif
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -91,7 +125,7 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params)
|
||||
break;
|
||||
|
||||
std::istringstream iss(line);
|
||||
std::string op, idstr, value, index, keystr;
|
||||
std::string op, idstr, value, index, keystr, pushServer, deviceKey;
|
||||
iss >> op;
|
||||
|
||||
if (op == "x" || op == "exit" || op == "quit") {
|
||||
@@ -115,20 +149,18 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params)
|
||||
} else if (op == "ld") {
|
||||
iss >> idstr;
|
||||
InfoHash filter(idstr);
|
||||
if (filter == InfoHash{})
|
||||
std::cout << dht->getStorageLog() << std::endl;
|
||||
else
|
||||
if (filter)
|
||||
std::cout << dht->getStorageLog(filter) << std::endl;
|
||||
else
|
||||
std::cout << dht->getStorageLog() << std::endl;
|
||||
continue;
|
||||
} else if (op == "ls") {
|
||||
iss >> idstr;
|
||||
InfoHash filter(idstr);
|
||||
if (filter == InfoHash{}) {
|
||||
std::cout << "Searches:" << std::endl;
|
||||
std::cout << dht->getSearchesLog() << std::endl;
|
||||
} else {
|
||||
if (filter)
|
||||
std::cout << dht->getSearchLog(filter) << std::endl;
|
||||
}
|
||||
else
|
||||
std::cout << dht->getSearchesLog() << std::endl;
|
||||
continue;
|
||||
} else if (op == "la") {
|
||||
std::cout << "Reported public addresses:" << std::endl;
|
||||
@@ -161,18 +193,74 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params)
|
||||
dht->setLogFilter(filter);
|
||||
continue;
|
||||
}
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
else if (op == "pst") {
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
iss >> idstr >> pushServer;
|
||||
#else
|
||||
iss >> idstr;
|
||||
#endif // OPENDHT_PUSH_NOTIFICATIONS
|
||||
try {
|
||||
unsigned int port = std::stoi(idstr);
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
proxies.emplace(port, std::unique_ptr<DhtProxyServer>(new DhtProxyServer(dht, port, pushServer)));
|
||||
#else
|
||||
proxies.emplace(port, std::unique_ptr<DhtProxyServer>(new DhtProxyServer(dht, port)));
|
||||
#endif // OPENDHT_PUSH_NOTIFICATIONS
|
||||
} catch (...) { }
|
||||
continue;
|
||||
} else if (op == "psp") {
|
||||
iss >> idstr;
|
||||
try {
|
||||
auto it = proxies.find(std::stoi(idstr));
|
||||
if (it != proxies.end())
|
||||
proxies.erase(it);
|
||||
} catch (...) { }
|
||||
continue;
|
||||
}
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
else if (op == "stt") {
|
||||
dht->enableProxy(true);
|
||||
continue;
|
||||
} else if (op == "stp") {
|
||||
dht->enableProxy(false);
|
||||
continue;
|
||||
}
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
else if (op == "rp") {
|
||||
iss >> value;
|
||||
dht->pushNotificationReceived({{"token", value}});
|
||||
continue;
|
||||
} else if (op == "re") {
|
||||
iss >> value;
|
||||
try {
|
||||
unsigned token = std::stoul(value);
|
||||
dht->resubscribe(token);
|
||||
} catch (...) { }
|
||||
continue;
|
||||
}
|
||||
#endif // OPENDHT_PUSH_NOTIFICATIONS
|
||||
#endif //OPENDHT_PROXY_CLIENT
|
||||
|
||||
if (op.empty())
|
||||
continue;
|
||||
|
||||
static const std::set<std::string> VALID_OPS {"g", "l", "il", "ii", "p", "s", "e", "a"};
|
||||
static const std::set<std::string> VALID_OPS {"g", "l", "cl", "il", "ii", "p", "pp", "cpp", "s", "e", "a", "q"};
|
||||
if (VALID_OPS.find(op) == VALID_OPS.cend()) {
|
||||
std::cout << "Unknown command: " << op << std::endl;
|
||||
std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl;
|
||||
continue;
|
||||
}
|
||||
dht::InfoHash id;
|
||||
|
||||
if (op == "il" or op == "ii") {
|
||||
if (op == "cl") {
|
||||
std::string hash, rem;
|
||||
iss >> hash >> rem;
|
||||
dht->cancelListen(dht::InfoHash(hash), std::stoul(rem));
|
||||
}
|
||||
#ifdef OPENDHT_INDEXATION
|
||||
else if (op == "il" or op == "ii") {
|
||||
// Pht syntax
|
||||
iss >> index >> keystr;
|
||||
auto new_index = std::find_if(indexes.begin(), indexes.end(),
|
||||
@@ -194,15 +282,21 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params)
|
||||
} catch (std::invalid_argument& e) { std::cout << e.what() << std::endl; }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
// Dht syntax
|
||||
iss >> idstr;
|
||||
InfoHash h {idstr};
|
||||
if (not isInfoHash(h))
|
||||
continue;
|
||||
id = dht::InfoHash(idstr);
|
||||
if (not id) {
|
||||
if (idstr.empty()) {
|
||||
std::cerr << "Syntax error: invalid InfoHash." << std::endl;
|
||||
continue;
|
||||
}
|
||||
id = InfoHash::get(idstr);
|
||||
std::cout << "Using h(" << idstr << ") = " << id << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
dht::InfoHash id {idstr};
|
||||
// Dht
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
if (op == "g") {
|
||||
@@ -236,11 +330,13 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params)
|
||||
else if (op == "l") {
|
||||
std::string rem;
|
||||
std::getline(iss, rem);
|
||||
dht->listen(id, [](std::shared_ptr<Value> value) {
|
||||
auto token = dht->listen(id, [](std::shared_ptr<Value> value) {
|
||||
std::cout << "Listen: found value:" << std::endl;
|
||||
std::cout << "\t" << *value << std::endl;
|
||||
return true;
|
||||
}, {}, dht::Where {std::move(rem)});
|
||||
auto t = token.get();
|
||||
std::cout << "Listening, token: " << t << std::endl;
|
||||
}
|
||||
else if (op == "p") {
|
||||
std::string v;
|
||||
@@ -253,6 +349,25 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params)
|
||||
std::cout << "Put: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
|
||||
});
|
||||
}
|
||||
else if (op == "pp") {
|
||||
std::string v;
|
||||
iss >> v;
|
||||
auto value = std::make_shared<dht::Value>(
|
||||
dht::ValueType::USER_DATA.id,
|
||||
std::vector<uint8_t> {v.begin(), v.end()}
|
||||
);
|
||||
dht->put(id, value, [start,value](bool ok) {
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto flags(std::cout.flags());
|
||||
std::cout << "Put: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s). Value ID: " << std::hex << value->id << std::endl;
|
||||
std::cout.flags(flags);
|
||||
}, time_point::max(), true);
|
||||
}
|
||||
else if (op == "cpp") {
|
||||
std::string rem;
|
||||
iss >> rem;
|
||||
dht->cancelPut(id, std::stoul(rem, nullptr, 16));
|
||||
}
|
||||
else if (op == "s") {
|
||||
if (not params.generate_identity) {
|
||||
print_id_req();
|
||||
@@ -292,6 +407,7 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params)
|
||||
std::cout << "Announce: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
|
||||
});
|
||||
}
|
||||
#ifdef OPENDHT_INDEXATION
|
||||
else if (op == "il") {
|
||||
std::string exact_match;
|
||||
iss >> exact_match;
|
||||
@@ -337,6 +453,7 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params)
|
||||
catch (std::invalid_argument& e) { std::cout << e.what() << std::endl; }
|
||||
catch (std::out_of_range& e) { }
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::cout << std::endl << "Stopping node..." << std::endl;
|
||||
@@ -357,6 +474,7 @@ main(int argc, char **argv)
|
||||
print_usage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (params.daemonize) {
|
||||
daemonize();
|
||||
} else if (params.service) {
|
||||
@@ -366,10 +484,19 @@ main(int argc, char **argv)
|
||||
dht::crypto::Identity crt {};
|
||||
if (params.generate_identity) {
|
||||
auto ca_tmp = dht::crypto::generateEcIdentity("DHT Node CA");
|
||||
crt = dht::crypto::generateEcIdentity("DHT Node", ca_tmp);
|
||||
crt = dht::crypto::generateIdentity("DHT Node", ca_tmp);
|
||||
}
|
||||
|
||||
dht->run(params.port, crt, true, params.network);
|
||||
dht::DhtRunner::Config config;
|
||||
config.dht_config.node_config.network = params.network;
|
||||
config.dht_config.id = crt;
|
||||
config.threaded = true;
|
||||
config.proxy_server = params.proxyclient;
|
||||
config.push_node_id = "dhtnode";
|
||||
if (not params.proxyclient.empty())
|
||||
dht->setPushNotificationToken(params.devicekey);
|
||||
|
||||
dht->run(params.port, config);
|
||||
|
||||
if (params.log) {
|
||||
if (params.syslog or (params.daemonize and params.logfile.empty()))
|
||||
@@ -385,12 +512,27 @@ main(int argc, char **argv)
|
||||
dht->bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str());
|
||||
}
|
||||
|
||||
if (params.daemonize or params.service) {
|
||||
while (runner.wait());
|
||||
} else {
|
||||
cmd_loop(dht, params);
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
std::map<in_port_t, std::unique_ptr<DhtProxyServer>> proxies;
|
||||
#endif
|
||||
if (params.proxyserver != 0) {
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
proxies.emplace(params.proxyserver, std::unique_ptr<DhtProxyServer>(new DhtProxyServer(dht, params.proxyserver, params.pushserver)));
|
||||
#else
|
||||
std::cerr << "DHT proxy server requested but OpenDHT built without proxy server support." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (params.daemonize or params.service)
|
||||
while (runner.wait());
|
||||
else
|
||||
cmd_loop(dht, params
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
, proxies
|
||||
#endif
|
||||
);
|
||||
|
||||
} catch(const std::exception&e) {
|
||||
std::cerr << std::endl << e.what() << std::endl;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ using namespace dht;
|
||||
void print_usage() {
|
||||
std::cout << "Usage: dhtscanner [-n network_id] [-p local_port] [-b bootstrap_host[:port]]" << std::endl << std::endl;
|
||||
std::cout << "dhtscanner, a simple OpenDHT command line utility generating scan result the network." << std::endl;
|
||||
std::cout << "Report bugs to: http://opendht.net" << std::endl;
|
||||
std::cout << "Report bugs to: https://opendht.net" << std::endl;
|
||||
}
|
||||
|
||||
struct snode_compare {
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>OpenDHT tester</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" />
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript">"use strict";
|
||||
let onGet;
|
||||
let onPut;
|
||||
let setServer;
|
||||
const valueGetElement = function(o) {
|
||||
const d = window.atob(o.data);
|
||||
return d;
|
||||
};
|
||||
$(function() {
|
||||
let request = undefined;
|
||||
let server;
|
||||
const getTools = $("#getTools");
|
||||
const getBtn = $("#getBtn");
|
||||
const getDropdown = $("#getDropdown");
|
||||
const listenBtn = $("#listenBtn").click(function(){onGet('LISTEN');});
|
||||
const setGetRequest = function() {
|
||||
getBtn.button('loading');
|
||||
getStopBtn.appendTo(getTools);
|
||||
getDropdown.hide();
|
||||
}
|
||||
const clearGetRequest = function() {
|
||||
if (request === undefined)
|
||||
return;
|
||||
request.abort();
|
||||
request = undefined;
|
||||
getStopBtn.detach();
|
||||
getDropdown.show();
|
||||
getBtn.button('reset');
|
||||
}
|
||||
const getStopBtn = $("#getStopBtn").detach().click(clearGetRequest);
|
||||
const putBtn = $("#putBtn");
|
||||
const result = $("#dhtResult");
|
||||
const group = $('<ul class="list-group"/>').appendTo(result);
|
||||
onGet = function (method) {
|
||||
if (request !== undefined)
|
||||
return false;
|
||||
const input = $("#getKey").val();
|
||||
group.empty();
|
||||
let lastAppended = 0;
|
||||
let start = new Date().getTime();
|
||||
request = new XMLHttpRequest();
|
||||
request.onreadystatechange = function(event) {
|
||||
if (this.readyState >= XMLHttpRequest.LOADING) {
|
||||
if (this.readyState == XMLHttpRequest.DONE) {
|
||||
clearGetRequest();
|
||||
}
|
||||
if (this.status === 200) {
|
||||
const elements = this.responseText.split("\n");
|
||||
const elementsLength = elements.length;
|
||||
const now = new Date().getTime();
|
||||
for (let i = lastAppended; i < elementsLength; i++) {
|
||||
const element = elements[i];
|
||||
if (!element || element.length == 0)
|
||||
return;
|
||||
const o = JSON.parse(element);
|
||||
const d = window.atob(o.data);
|
||||
const delay = Math.max(0, start-now);
|
||||
$('<li class="list-group-item"/>').append(valueGetElement(o)).appendTo(group).hide().delay(delay).slideDown(100);
|
||||
lastAppended = i+1;
|
||||
start = Math.max(start, now)+25;
|
||||
}
|
||||
} else if (this.status !== 0) {
|
||||
group.empty().append($('<li class="list-group-item list-group-item-danger"/>').text("Error loading content: " + this.statusText));
|
||||
}
|
||||
}
|
||||
};
|
||||
request.onerror = function(event) {
|
||||
clearGetRequest();
|
||||
group.empty().append($('<li class="list-group-item list-group-item-danger"/>').text("Error loading content."));
|
||||
};
|
||||
request.open(method, server + input, true);
|
||||
request.send(null);
|
||||
setGetRequest();
|
||||
return false;
|
||||
};
|
||||
|
||||
onPut = function( ) {
|
||||
const key = $("#getKey").val();
|
||||
const value = $("#putValue").val();
|
||||
$.ajax({
|
||||
url: server + key,
|
||||
type: 'POST',
|
||||
data: JSON.stringify({
|
||||
data:window.btoa(value)
|
||||
}),
|
||||
contentType: 'application/json; charset=utf-8',
|
||||
dataType: 'json',
|
||||
success: function( result ) {
|
||||
putBtn.button('reset');
|
||||
//$('<li class="list-group-item list-group-item-success"/>').append(valueGetElement(result)).appendTo(group.empty());
|
||||
},
|
||||
error: function(result) {
|
||||
putBtn.button('reset');
|
||||
group.empty().append($('<li class="list-group-item list-group-item-danger"/>').text(result.statusText));
|
||||
}
|
||||
});
|
||||
putBtn.button('loading');
|
||||
return false;
|
||||
};
|
||||
|
||||
const serverValue = $("#serverValue");
|
||||
const serverStatus = $("#serverStatus");
|
||||
const serverBtn = $("#serverBtn");
|
||||
setServer = function(event) {
|
||||
server = 'http://'+serverValue.val() + '/';
|
||||
serverStatus.empty();
|
||||
serverBtn.button('loading');
|
||||
$.getJSON(server, function(data){
|
||||
$('<span><b>Node</b> '+data.node_id+'</span>').appendTo(serverStatus).hide().fadeIn();
|
||||
}).fail(function(error) {
|
||||
serverStatus.html("<div class='alert alert-danger' style='margin-bottom: 0px;'><span class='glyphicon glyphicon-remove' aria-hidden='true'></span> Can't access node</div>");
|
||||
}).always(function(error) {
|
||||
serverBtn.button('reset');
|
||||
});
|
||||
return false;
|
||||
};
|
||||
setServer();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container" style="max-width: 730px;">
|
||||
<header class="page-header">
|
||||
<div class="row">
|
||||
<div class="col-sm-5">
|
||||
<h1>OpenDHT tester</h1>
|
||||
</div>
|
||||
<div class="col-sm-7">
|
||||
<div class="well well-sm" style="margin-top:10px; margin-bottom:0px;">
|
||||
<form id="serverForm" class="form-inline" onsubmit="return setServer();" style="margin-bottom:4px;">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="serverValue" placeholder="Proxy server" value="127.0.0.1:8080"/>
|
||||
<span class="input-group-btn">
|
||||
<button id="serverBtn" type="submit" class="btn btn-default" data-loading-text="<i class='fa fa-circle-o-notch fa-spin'></i>"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span></button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
<div id="serverStatus"><i class='fa fa-circle-o-notch fa-spin'></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="panel panel-default" id="dhtResult">
|
||||
<div class="panel-heading">
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<form class="form-inline" onsubmit="return onGet('GET');">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="getKey" placeholder="Key" aria-label="Key" />
|
||||
<span class="input-group-btn" id="getTools">
|
||||
<button id="getBtn" class="btn btn-default" data-loading-text="<i class='fa fa-circle-o-notch fa-spin'></i>" type="submit">Get</button>
|
||||
<button id="getDropdown"type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <span class="caret"></span> <span class="sr-only">Toggle Dropdown</span> </button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a id="listenBtn" href="#">Listen</a></li>
|
||||
</ul>
|
||||
<button id="getStopBtn" class="btn btn-default" type="submit"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<form class="form-inline" onsubmit="return onPut();">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control input-group-input" id="putValue" placeholder="Value" />
|
||||
<span class="input-group-btn">
|
||||
<button id="putBtn" type="submit" class="btn btn-default" data-loading-text="<i class='fa fa-circle-o-notch fa-spin'></i> Loading">Put</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
+30
-4
@@ -2,6 +2,7 @@
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -89,6 +90,7 @@ std::map<std::string, std::string> parseStringMap(std::string mapString) {
|
||||
return map;
|
||||
}
|
||||
|
||||
#ifdef OPENDHT_INDEXATION
|
||||
dht::indexation::Pht::Key createPhtKey(std::map<std::string, std::string> pht_key_str_map) {
|
||||
dht::indexation::Pht::Key pht_key;
|
||||
for (auto f : pht_key_str_map) {
|
||||
@@ -97,11 +99,10 @@ dht::indexation::Pht::Key createPhtKey(std::map<std::string, std::string> pht_ke
|
||||
}
|
||||
return pht_key;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool isInfoHash(dht::InfoHash& h) {
|
||||
static constexpr dht::InfoHash INVALID_ID {};
|
||||
|
||||
if (h == INVALID_ID) {
|
||||
bool isInfoHash(const dht::InfoHash& h) {
|
||||
if (not h) {
|
||||
std::cout << "Syntax error: invalid InfoHash." << std::endl;
|
||||
return false;
|
||||
}
|
||||
@@ -121,6 +122,10 @@ struct dht_params {
|
||||
bool daemonize {false};
|
||||
bool service {false};
|
||||
std::pair<std::string, std::string> bootstrap {};
|
||||
in_port_t proxyserver {0};
|
||||
std::string proxyclient {};
|
||||
std::string pushserver {};
|
||||
std::string devicekey {};
|
||||
};
|
||||
|
||||
static const constexpr struct option long_options[] = {
|
||||
@@ -134,6 +139,10 @@ static const constexpr struct option long_options[] = {
|
||||
{"service", no_argument , nullptr, 's'},
|
||||
{"logfile", required_argument, nullptr, 'l'},
|
||||
{"syslog", no_argument , nullptr, 'L'},
|
||||
{"proxyserver",required_argument, nullptr, 'S'},
|
||||
{"proxyclient",required_argument, nullptr, 'C'},
|
||||
{"pushserver", required_argument, nullptr, 'P'},
|
||||
{"devicekey", required_argument, nullptr, 'D'},
|
||||
{nullptr, 0 , nullptr, 0}
|
||||
};
|
||||
|
||||
@@ -151,6 +160,23 @@ parseArgs(int argc, char **argv) {
|
||||
std::cout << "Invalid port: " << port_arg << std::endl;
|
||||
}
|
||||
break;
|
||||
case 'S': {
|
||||
int port_arg = atoi(optarg);
|
||||
if (port_arg >= 0 && port_arg < 0x10000)
|
||||
params.proxyserver = port_arg;
|
||||
else
|
||||
std::cout << "Invalid port: " << port_arg << std::endl;
|
||||
}
|
||||
break;
|
||||
case 'P':
|
||||
params.pushserver = optarg;
|
||||
break;
|
||||
case 'C':
|
||||
params.proxyclient = optarg;
|
||||
break;
|
||||
case 'D':
|
||||
params.devicekey = optarg;
|
||||
break;
|
||||
case 'n':
|
||||
params.network = strtoul(optarg, nullptr, 0);
|
||||
break;
|
||||
|
||||
Reference in new issue
Block a user