mirror of
https://github.com/savoirfairelinux/opendht.git
synced 2026-11-08 20:37:23 +00:00
Compare commits
299
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d80aab9399 | ||
|
|
9b881d4e95 | ||
|
|
b4c3c2cba0 | ||
|
|
4519a23996 | ||
|
|
1ac4e08f45 | ||
|
|
5e92777702 | ||
|
|
4401e47002 | ||
|
|
215b44ca3b | ||
|
|
379fdae212 | ||
|
|
1e10e28fd7 | ||
|
|
dccdb84836 | ||
|
|
71599da8ea | ||
|
|
f418b3651f | ||
|
|
313f54c840 | ||
|
|
cdb721ade4 | ||
|
|
57ea09808e | ||
|
|
fec5a6ede7 | ||
|
|
5f8e9d9480 | ||
|
|
311a8981fc | ||
|
|
04850e1701 | ||
|
|
a30f1b8032 | ||
|
|
41f8b06743 | ||
|
|
1c74c2207f | ||
|
|
9319bec7b8 | ||
|
|
e5f2a6eef5 | ||
|
|
55f6c847b8 | ||
|
|
00cc4ebe28 | ||
|
|
aa93db4fa6 | ||
|
|
683570a3da | ||
|
|
43559577f6 | ||
|
|
ba2e179abe | ||
|
|
a5d29c904c | ||
|
|
989f32eb56 | ||
|
|
b66fa73c56 | ||
|
|
1ca181db1d | ||
|
|
1f3bf697c3 | ||
|
|
a1f8cc906c | ||
|
|
35c6a57526 | ||
|
|
39e69f813c | ||
|
|
8045c2cbf2 | ||
|
|
79a2f4280f | ||
|
|
769ce45740 | ||
|
|
75a14d3608 | ||
|
|
d523e9f259 | ||
|
|
3a3933e6d5 | ||
|
|
70f09ded3f | ||
|
|
824a1bf5fa | ||
|
|
e5b56cc0a5 | ||
|
|
36cf70f2a7 | ||
|
|
459c1344aa | ||
|
|
c0e376e15a | ||
|
|
71b9672b96 | ||
|
|
086f37a71a | ||
|
|
87e76a5e4e | ||
|
|
7f0a5dfda3 | ||
|
|
6d7f8004bf | ||
|
|
8168c39b63 | ||
|
|
d0fdbe5340 | ||
|
|
c7604fbb8d | ||
|
|
56773baa98 | ||
|
|
e6cf67c575 | ||
|
|
00ebc5e2bf | ||
|
|
ccef94c11b | ||
|
|
0066be49b9 | ||
|
|
592ed4ee90 | ||
|
|
353a6f9db6 | ||
|
|
7ebad7d3c5 | ||
|
|
14f6e6e5f3 | ||
|
|
9ce5b30931 | ||
|
|
92979fa96a | ||
|
|
0c1e7d2763 | ||
|
|
14a744f6cd | ||
|
|
3b26684a87 | ||
|
|
0a764ee440 | ||
|
|
7dda5a3d5a | ||
|
|
306d573223 | ||
|
|
20f4355b73 | ||
|
|
a2d7cd56a8 | ||
|
|
5cd53f3624 | ||
|
|
32eea55f10 | ||
|
|
f2f913f93f | ||
|
|
cb9fe9d1fe | ||
|
|
dcab32ef4f | ||
|
|
4948bd7ad3 | ||
|
|
4416b91461 | ||
|
|
378ae396f0 | ||
|
|
dc4a790a38 | ||
|
|
07dd8f5648 | ||
|
|
f7433a2289 | ||
|
|
ce4a65ff00 | ||
|
|
092f5cfef0 | ||
|
|
12a45e7558 | ||
|
|
18d359a7d2 | ||
|
|
c6bb67d1ac | ||
|
|
f4cd7636ce | ||
|
|
2dd4e640c4 | ||
|
|
9a301e3590 | ||
|
|
aa16b9a235 | ||
|
|
b2542a57df | ||
|
|
00c1be9cdc | ||
|
|
e414ca89c7 | ||
|
|
4d26537b93 | ||
|
|
00a9333de7 | ||
|
|
971f96bf95 | ||
|
|
f9a12a9dca | ||
|
|
c05a93ae54 | ||
|
|
ff83a6daab | ||
|
|
b7b5152348 | ||
|
|
a3768d1e50 | ||
|
|
94926cea22 | ||
|
|
82ca2d1993 | ||
|
|
3e148599c5 | ||
|
|
02d5592fdb | ||
|
|
328b306fd0 | ||
|
|
522a89b6f8 | ||
|
|
897b451f44 | ||
|
|
ba3a42697c | ||
|
|
5112a322de | ||
|
|
85961c6a62 | ||
|
|
1eb1f3d5ac | ||
|
|
726c138d90 | ||
|
|
1b6b1ca072 | ||
|
|
77da72c603 | ||
|
|
7fb2da7c54 | ||
|
|
6721a272ea | ||
|
|
246af2f065 | ||
|
|
57aba453b0 | ||
|
|
f54fcd768f | ||
|
|
5b81476baf | ||
|
|
5b510cadfb | ||
|
|
c794781644 | ||
|
|
2ef4999b0f | ||
|
|
6069fba18f | ||
|
|
37ddd8c7f9 | ||
|
|
23057a3082 | ||
|
|
dbdd3f8f63 | ||
|
|
751e79a250 | ||
|
|
7a961ffc51 | ||
|
|
bfada958b9 | ||
|
|
78d91a8e0d | ||
|
|
cd5dc38a57 | ||
|
|
1ff2eeb220 | ||
|
|
ffbb9e23ef | ||
|
|
af0a30c9c5 | ||
|
|
97d5e3875d | ||
|
|
2509e8bc5c | ||
|
|
e349912c07 | ||
|
|
a1fc266eac | ||
|
|
3e3679bebf | ||
|
|
e9cf16f361 | ||
|
|
3dca4db408 | ||
|
|
fb6d290692 | ||
|
|
96c60d39ed | ||
|
|
3a41f2ce56 | ||
|
|
5bd88533fe | ||
|
|
c48e24403b | ||
|
|
3cf2c092f2 | ||
|
|
aef8aa545b | ||
|
|
0c61a605ad | ||
|
|
e6b247bead | ||
|
|
6f1f18f128 | ||
|
|
f24458860d | ||
|
|
07bed77714 | ||
|
|
aa202b2cd8 | ||
|
|
4c89ce9d3f | ||
|
|
709a2c7b7c | ||
|
|
b7e483078a | ||
|
|
b2171d707a | ||
|
|
60db9fd9cd | ||
|
|
e8989f1f79 | ||
|
|
b7218bd1c9 | ||
|
|
43aab40968 | ||
|
|
ed57b3bfa9 | ||
|
|
9249f34c47 | ||
|
|
57401e8d53 | ||
|
|
f5f71a0365 | ||
|
|
1fe9f2fdb1 | ||
|
|
59d129b8ff | ||
|
|
5b5349d7ad | ||
|
|
b2ba1ca002 | ||
|
|
8606e87684 | ||
|
|
e4f640f11b | ||
|
|
e31d301d90 | ||
|
|
2a15435be3 | ||
|
|
44a411ba96 | ||
|
|
bf2f2369b1 | ||
|
|
b8b1f61268 | ||
|
|
63db2eafd9 | ||
|
|
68f52a815d | ||
|
|
9f35acf559 | ||
|
|
d8e82ca7c4 | ||
|
|
981bcfc0ba | ||
|
|
4a49a53222 | ||
|
|
9f987e24fe | ||
|
|
95f1d59a7f | ||
|
|
a4fe27223f | ||
|
|
5408dc6f09 | ||
|
|
35d4c1d332 | ||
|
|
c583b7284c | ||
|
|
2ce0c5c4c9 | ||
|
|
484194c114 | ||
|
|
b26b78c049 | ||
|
|
522a15c410 | ||
|
|
6a981772fd | ||
|
|
cc37fb50e2 | ||
|
|
1c9acfe001 | ||
|
|
c6cef856f0 | ||
|
|
9f503dbb65 | ||
|
|
7e4478123f | ||
|
|
e54d26aaf7 | ||
|
|
ee749d1ea7 | ||
|
|
a0d77e7b19 | ||
|
|
9421984a89 | ||
|
|
37ef1c2cff | ||
|
|
0a04a7f28d | ||
|
|
b47a85fe09 | ||
|
|
4b7b3cd518 | ||
|
|
1a2d783c2a | ||
|
|
7f943e07a5 | ||
|
|
f98658f10f | ||
|
|
8b6dcb1cd5 | ||
|
|
e140116fed | ||
|
|
c16a65a055 | ||
|
|
da1b7d4615 | ||
|
|
7b3ce69b62 | ||
|
|
8a52a7f72a | ||
|
|
abb9f9291b | ||
|
|
e533f5686e | ||
|
|
4234908b69 | ||
|
|
c10c4251b0 | ||
|
|
8b90c47379 | ||
|
|
fae6b13897 | ||
|
|
a48a965c1a | ||
|
|
ab0f4f6f17 | ||
|
|
e3114cd5b9 | ||
|
|
8489433060 | ||
|
|
6594d90042 | ||
|
|
7c5145bca5 | ||
|
|
e088cd10be | ||
|
|
6a2f61a0db | ||
|
|
7b88a2122a | ||
|
|
f9cf95d537 | ||
|
|
4884306135 | ||
|
|
fd11a4b3e4 | ||
|
|
b3c637536f | ||
|
|
1436af0321 | ||
|
|
1debe012c1 | ||
|
|
368a842d2e | ||
|
|
d250e9b630 | ||
|
|
3ff6863287 | ||
|
|
96621ea332 | ||
|
|
567fa1c703 | ||
|
|
20ab71aba6 | ||
|
|
1acace1b91 | ||
|
|
2f423fec48 | ||
|
|
7be550bf94 | ||
|
|
a8c0340bf1 | ||
|
|
5b71af3cec | ||
|
|
fcf7111872 | ||
|
|
38ca7b851b | ||
|
|
62bbce76f2 | ||
|
|
d9f6731de2 | ||
|
|
393a403ff2 | ||
|
|
62e4655d4f | ||
|
|
60d43319e2 | ||
|
|
7d3c613347 | ||
|
|
d2df93c75e | ||
|
|
0edee443f5 | ||
|
|
8c85f84b8a | ||
|
|
cea0d4f62e | ||
|
|
203150851f | ||
|
|
9a5ef25179 | ||
|
|
a5137e8ac4 | ||
|
|
c129a24492 | ||
|
|
8237f32bd5 | ||
|
|
546e63edaf | ||
|
|
647de16f1c | ||
|
|
2f4acd26d6 | ||
|
|
48bb804792 | ||
|
|
4261bd2c85 | ||
|
|
12db1f88dc | ||
|
|
1617c79e5d | ||
|
|
80b944d943 | ||
|
|
74bfced9b9 | ||
|
|
1c10872ea2 | ||
|
|
2b2e8c2a72 | ||
|
|
2315dc089a | ||
|
|
d7c0e9af38 | ||
|
|
460cf858d3 | ||
|
|
c81da2dd7d | ||
|
|
b02f8e98c4 | ||
|
|
ca9a367803 | ||
|
|
09f080220f | ||
|
|
547fa2eff5 | ||
|
|
2a8f21d4aa | ||
|
|
6f4283b7d5 | ||
|
|
babceb902a | ||
|
|
26bd4e7141 | ||
|
|
1531e13ca4 |
No files matched your search
@@ -0,0 +1 @@
|
||||
Checks: '-*,clang-diagnostic-*,performance-*,bugprone-*,llvm-*,clang-analyzer-*,misc-*,-llvm-namespace-comment,-misc-unused-parameters,-misc-non-private-member-variables-in-classes,-llvm-header-guard,-llvm-include-order,-bugprone-suspicious-string-compare'
|
||||
@@ -48,6 +48,7 @@ Makefile.in
|
||||
|
||||
# Python
|
||||
*.pyc
|
||||
*.stamp
|
||||
python/opendht.cpp
|
||||
python/setup.py
|
||||
|
||||
@@ -60,3 +61,6 @@ doc/Doxyfile
|
||||
# vim swap files
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# build dir
|
||||
build
|
||||
+7
-11
@@ -1,3 +1,4 @@
|
||||
dist: xenial
|
||||
sudo: required
|
||||
|
||||
services:
|
||||
@@ -11,18 +12,13 @@ env:
|
||||
- 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"
|
||||
- OPENDHT_TEST_JOB="opendht.push"
|
||||
|
||||
before_install:
|
||||
- |
|
||||
# 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
|
||||
|
||||
- |
|
||||
@@ -42,23 +38,23 @@ script:
|
||||
# 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_SANITIZE=On ';
|
||||
if [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyserver"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.push"* ]]; then
|
||||
options+='-DOPENDHT_PROXY_SERVER=ON ';
|
||||
else
|
||||
options+='-DOPENDHT_PROXY_SERVER=OFF ';
|
||||
fi
|
||||
if [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyclient"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyclientpush"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.all"* ]]; then
|
||||
if [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyclient"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.push"* ]]; then
|
||||
options+='-DOPENDHT_PROXY_CLIENT=ON ';
|
||||
else
|
||||
options+='-DOPENDHT_PROXY_CLIENT=OFF ';
|
||||
fi
|
||||
if [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyserverpush"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyclientpush"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.all"* ]]; then
|
||||
if [[ "$OPENDHT_TEST_JOB" == *"opendht.push"* ]]; then
|
||||
options+='-DOPENDHT_PUSH_NOTIFICATIONS=ON ';
|
||||
else
|
||||
options+='-DOPENDHT_PUSH_NOTIFICATIONS=OFF ';
|
||||
fi
|
||||
docker run opendht-proxy /bin/sh -c "cd /root/opendht && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=ON -DOPENDHT_LTO=ON $options .. && make -j8 && make install";
|
||||
docker run opendht-proxy /bin/sh -c "cd /root/opendht && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=ON -DOPENDHT_LTO=ON -DOPENDHT_TESTS=ON $options .. && make -j8 && ./opendht_unit_tests && make install";
|
||||
fi
|
||||
|
||||
- |
|
||||
|
||||
+74
-31
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required (VERSION 3.1)
|
||||
project (opendht)
|
||||
set (opendht_VERSION_MAJOR 1)
|
||||
set (opendht_VERSION_MINOR 6.0)
|
||||
set (opendht_VERSION_MINOR 8.2)
|
||||
set (opendht_VERSION ${opendht_VERSION_MAJOR}.${opendht_VERSION_MINOR})
|
||||
set (PACKAGE_VERSION ${opendht_VERSION})
|
||||
set (VERSION "${opendht_VERSION}")
|
||||
@@ -21,6 +21,7 @@ option (OPENDHT_PUSH_NOTIFICATIONS "Enable push notifications support" OFF)
|
||||
option (OPENDHT_PROXY_SERVER_IDENTITY "Allow clients to use the node identity" OFF)
|
||||
option (OPENDHT_PROXY_CLIENT "Enable DHT proxy client, use Restbed and jsoncpp" OFF)
|
||||
option (OPENDHT_INDEX "Build DHT indexation feature" OFF)
|
||||
option (OPENDHT_TESTS "Add unit tests executable" OFF)
|
||||
|
||||
find_package(Doxygen)
|
||||
option (OPENDHT_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" ${DOXYGEN_FOUND})
|
||||
@@ -46,9 +47,21 @@ if (NOT OPENDHT_ARGON2)
|
||||
set(OPENDHT_ARGON2 ON)
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
pkg_search_module(Jsoncpp jsoncpp)
|
||||
if (Jsoncpp_FOUND)
|
||||
add_definitions(-DOPENDHT_JSONCPP)
|
||||
list (APPEND opendht_SOURCES
|
||||
src/base64.h
|
||||
src/base64.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if (OPENDHT_PROXY_SERVER OR OPENDHT_PROXY_CLIENT)
|
||||
find_package(Restbed REQUIRED)
|
||||
pkg_search_module(Jsoncpp jsoncpp)
|
||||
if (NOT Jsoncpp_FOUND)
|
||||
message(SEND_ERROR "Jsoncpp is required for DHT proxy support")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Build flags
|
||||
@@ -86,8 +99,8 @@ endif ()
|
||||
if (Nettle_INCLUDE_DIRS)
|
||||
include_directories (SYSTEM "${Nettle_INCLUDE_DIRS}")
|
||||
endif ()
|
||||
if (RESTBED_INCLUDE)
|
||||
include_directories (SYSTEM "${RESTBED_INCLUDE}")
|
||||
if (Restbed_INCLUDE_DIR)
|
||||
include_directories (SYSTEM "${Restbed_INCLUDE_DIR}")
|
||||
endif ()
|
||||
if (Jsoncpp_INCLUDE_DIRS)
|
||||
include_directories (SYSTEM "${Jsoncpp_INCLUDE_DIRS}")
|
||||
@@ -120,9 +133,12 @@ list (APPEND opendht_SOURCES
|
||||
src/node.cpp
|
||||
src/value.cpp
|
||||
src/dht.cpp
|
||||
src/op_cache.cpp
|
||||
src/storage.h
|
||||
src/listener.h
|
||||
src/search.h
|
||||
src/value_cache.h
|
||||
src/op_cache.h
|
||||
src/net.h
|
||||
src/parsed_message.h
|
||||
src/request.h
|
||||
@@ -166,47 +182,40 @@ if (OPENDHT_INDEX)
|
||||
endif()
|
||||
|
||||
if (OPENDHT_PROXY_SERVER)
|
||||
add_definitions(-DOPENDHT_PROXY_SERVER=true)
|
||||
add_definitions(-DOPENDHT_PROXY_SERVER)
|
||||
if (OPENDHT_PROXY_SERVER_IDENTITY)
|
||||
add_definitions(-DOPENDHT_PROXY_SERVER_IDENTITY=true)
|
||||
else ()
|
||||
add_definitions(-DOPENDHT_PROXY_SERVER_IDENTITY=false)
|
||||
add_definitions(-DOPENDHT_PROXY_SERVER_IDENTITY)
|
||||
endif()
|
||||
list (APPEND opendht_HEADERS
|
||||
include/opendht/dht_proxy_server.h
|
||||
)
|
||||
list (APPEND opendht_SOURCES
|
||||
src/thread_pool.h
|
||||
src/thread_pool.cpp
|
||||
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)
|
||||
add_definitions(-DOPENDHT_PROXY_CLIENT)
|
||||
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
|
||||
if (OPENDHT_PUSH_NOTIFICATIONS)
|
||||
message("Using push notification")
|
||||
add_definitions(-DOPENDHT_PUSH_NOTIFICATIONS)
|
||||
endif ()
|
||||
list (APPEND opendht_HEADERS
|
||||
include/opendht/proxy.h
|
||||
)
|
||||
endif ()
|
||||
|
||||
|
||||
if(OPENDHT_ARGON2)
|
||||
# make sure argon2 submodule is up to date and initialized
|
||||
message("Initializing Argon2 submodule")
|
||||
@@ -231,16 +240,14 @@ if (OPENDHT_STATIC)
|
||||
${opendht_HEADERS}
|
||||
)
|
||||
set_target_properties (opendht-static PROPERTIES OUTPUT_NAME "opendht")
|
||||
if (OPENDHT_LTO)
|
||||
target_link_libraries(opendht-static -flto -fuse-linker-plugin)
|
||||
endif ()
|
||||
if (OPENDHT_ARGON2)
|
||||
target_include_directories(opendht-static SYSTEM PRIVATE argon2)
|
||||
else ()
|
||||
target_link_libraries(opendht-static ${argon2_LIBRARIES})
|
||||
target_include_directories(opendht-static SYSTEM PRIVATE ${argon2_INCLUDE_DIRS})
|
||||
endif ()
|
||||
target_link_libraries(opendht-static ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} ${Restbed_LIBRARIES} ${Jsoncpp_LIBRARIES})
|
||||
target_link_libraries(opendht-static
|
||||
PRIVATE ${Restbed_LIBRARY} ${argon2_LIBRARIES}
|
||||
PUBLIC ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} ${Jsoncpp_LIBRARIES})
|
||||
install (TARGETS opendht-static DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht)
|
||||
endif ()
|
||||
|
||||
@@ -252,16 +259,13 @@ if (OPENDHT_SHARED)
|
||||
set_target_properties (opendht PROPERTIES IMPORT_SUFFIX "_import.lib")
|
||||
set_target_properties (opendht PROPERTIES SOVERSION ${opendht_VERSION_MAJOR} VERSION ${opendht_VERSION})
|
||||
target_compile_definitions(opendht PRIVATE OPENDHT_BUILD)
|
||||
if (OPENDHT_LTO)
|
||||
target_link_libraries(opendht PRIVATE -flto -fuse-linker-plugin)
|
||||
endif ()
|
||||
if (OPENDHT_ARGON2)
|
||||
target_include_directories(opendht SYSTEM PRIVATE argon2)
|
||||
else ()
|
||||
target_link_libraries(opendht PRIVATE ${argon2_LIBRARIES})
|
||||
target_include_directories(opendht SYSTEM PRIVATE ${argon2_INCLUDE_DIRS})
|
||||
endif ()
|
||||
target_link_libraries(opendht PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} ${Restbed_LIBRARIES} ${Jsoncpp_LIBRARIES})
|
||||
target_link_libraries(opendht PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} ${Restbed_LIBRARY} ${Jsoncpp_LIBRARIES})
|
||||
install (TARGETS opendht DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht)
|
||||
endif ()
|
||||
|
||||
@@ -292,3 +296,42 @@ install (DIRECTORY include DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/opendht.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
install (EXPORT opendht DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/opendht FILE opendhtConfig.cmake)
|
||||
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/opendhtConfigVersion.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/opendht)
|
||||
|
||||
# Unit tests
|
||||
if (OPENDHT_TESTS)
|
||||
FIND_PACKAGE(Cppunit REQUIRED)
|
||||
# unit testing
|
||||
list (APPEND test_FILES
|
||||
tests/infohashtester.h
|
||||
tests/infohashtester.cpp
|
||||
tests/valuetester.h
|
||||
tests/valuetester.cpp
|
||||
tests/cryptotester.h
|
||||
tests/cryptotester.cpp
|
||||
tests/dhtrunnertester.h
|
||||
tests/dhtrunnertester.cpp
|
||||
)
|
||||
if (OPENDHT_PROXY_SERVER AND OPENDHT_PROXY_CLIENT)
|
||||
list (APPEND test_FILES
|
||||
tests/dhtproxytester.h
|
||||
tests/dhtproxytester.cpp
|
||||
)
|
||||
endif()
|
||||
add_executable(opendht_unit_tests
|
||||
tests/tests_runner.cpp
|
||||
${test_FILES}
|
||||
)
|
||||
target_include_directories(opendht_unit_tests SYSTEM PRIVATE ${CPPUNIT_INCLUDE_DIRS})
|
||||
if (OPENDHT_SHARED)
|
||||
target_link_libraries(opendht_unit_tests opendht)
|
||||
else ()
|
||||
target_link_libraries(opendht_unit_tests opendht-static)
|
||||
endif ()
|
||||
target_link_libraries(opendht_unit_tests
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${CPPUNIT_LIBRARIES}
|
||||
${GNUTLS_LIBRARIES}
|
||||
)
|
||||
enable_testing()
|
||||
add_test(TEST opendht_unit_tests)
|
||||
endif()
|
||||
@@ -146,7 +146,7 @@
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\include;..\include\opendht;..\..\msgpack-c\include;</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4800;4101;4267;4244;4503;</DisableSpecificWarnings>
|
||||
<AdditionalOptions>-D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions)</AdditionalOptions>
|
||||
<SuppressStartupBanner>false</SuppressStartupBanner>
|
||||
@@ -169,7 +169,7 @@ copy ..\include\opendht.h $(OutDir)\include\</Command>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\include;..\include\opendht;..\..\msgpack-c\include;</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4800;4101;4267;4244;4503;</DisableSpecificWarnings>
|
||||
<AdditionalOptions>-D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
@@ -193,7 +193,7 @@ copy ..\include\opendht.h $(OutDir)\include\</Command>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\include;..\include\opendht;..\..\msgpack-c\include;</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4800;4101;4267;4244;4503;</DisableSpecificWarnings>
|
||||
<AdditionalOptions>-D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions)</AdditionalOptions>
|
||||
<ProgramDataBaseFileName>$(OutDir)\lib\x86\$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
@@ -223,7 +223,7 @@ copy ..\include\opendht.h $(OutDir)\include\</Command>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)contrib\build\include;$(ProjectDir)..\include;$(ProjectDir)..\include\opendht;$(ProjectDir)contrib\build\msgpack-c\include;$(ProjectDir)contrib\build\argon2\include;$(ProjectDir)..\..\include;$(ProjectDir)..\..\argon2\include</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4800;4101;4267;4244;4503;4273;</DisableSpecificWarnings>
|
||||
<AdditionalOptions>-D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions)</AdditionalOptions>
|
||||
<ProgramDataBaseFileName>$(OutDir)\lib\x64\$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2016-2019 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
|
||||
*
|
||||
|
||||
@@ -12,7 +12,13 @@ if USE_CYTHON
|
||||
SUBDIRS += python
|
||||
endif
|
||||
|
||||
if ENABLE_TESTS
|
||||
SUBDIRS += tests
|
||||
endif
|
||||
|
||||
if HAVE_DOXYGEN
|
||||
SUBDIRS += doc
|
||||
endif
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
|
||||
@@ -6,12 +6,17 @@
|
||||
|
||||
A lightweight C++11 Distributed Hash Table implementation.
|
||||
|
||||
* 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
|
||||
OpenDHT provides an easy to use distributed in-memory data store.
|
||||
Every node in the network can read and write values to the store.
|
||||
Values are distributed over the network, with redundancy.
|
||||
|
||||
* Lightweight and scalable, designed for large networks and small devices
|
||||
* High resilience to network disruption
|
||||
* Public key cryptography layer providing optional data signature and encryption (using GnuTLS)
|
||||
* IPv4 and IPv6 support
|
||||
* Python binding
|
||||
* Clean and powerful C++11 map API
|
||||
* Python 3 bindings
|
||||
* REST API
|
||||
|
||||
## Documentation
|
||||
See the wiki: <https://github.com/savoirfairelinux/opendht/wiki>
|
||||
@@ -100,14 +105,14 @@ for r in results:
|
||||
|
||||
IRC: join us on Freenode at [`#opendht`](https://webchat.freenode.net/?channels=%23opendht).
|
||||
|
||||
## Licence
|
||||
Copyright (C) 2014-2018 Savoir-faire Linux Inc.
|
||||
## License
|
||||
Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
|
||||
OpenDHT is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
See COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html for the full GPLv3 licence.
|
||||
See COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html for the full GPLv3 license.
|
||||
|
||||
## Acknowledgements
|
||||
This project was originally based on https://github.com/jech/dht by Juliusz Chroboczek.
|
||||
|
||||
+1
-1
Submodule argon2 updated: 6f5427fb67...670229c849.
@@ -0,0 +1,28 @@
|
||||
INCLUDE(FindPkgConfig)
|
||||
PKG_CHECK_MODULES(PC_CPPUNIT "cppunit")
|
||||
|
||||
FIND_PATH(CPPUNIT_INCLUDE_DIRS
|
||||
NAMES cppunit/TestCase.h
|
||||
HINTS ${PC_CPPUNIT_INCLUDE_DIR}
|
||||
${CMAKE_INSTALL_PREFIX}/include
|
||||
PATHS
|
||||
/usr/local/include
|
||||
/usr/include
|
||||
)
|
||||
|
||||
FIND_LIBRARY(CPPUNIT_LIBRARIES
|
||||
NAMES cppunit
|
||||
HINTS ${PC_CPPUNIT_LIBDIR}
|
||||
${CMAKE_INSTALL_PREFIX}/lib
|
||||
${CMAKE_INSTALL_PREFIX}/lib64
|
||||
PATHS
|
||||
${CPPUNIT_INCLUDE_DIRS}/../lib
|
||||
/usr/local/lib
|
||||
/usr/lib
|
||||
)
|
||||
|
||||
LIST(APPEND CPPUNIT_LIBRARIES ${CMAKE_DL_LIBS})
|
||||
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(CPPUNIT DEFAULT_MSG CPPUNIT_LIBRARIES CPPUNIT_INCLUDE_DIRS)
|
||||
MARK_AS_ADVANCED(CPPUNIT_LIBRARIES CPPUNIT_INCLUDE_DIRS)
|
||||
+15
-16
@@ -1,17 +1,16 @@
|
||||
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)
|
||||
if(NOT Restbed_FOUND)
|
||||
find_path (Restbed_INCLUDE_DIR restbed
|
||||
HINTS
|
||||
"/usr/include"
|
||||
"/usr/local/include"
|
||||
"/opt/local/include")
|
||||
find_library(Restbed_LIBRARY restbed
|
||||
HINTS ${Restbed_ROOT_DIR} PATH_SUFFIXES lib)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Restbed DEFAULT_MSG Restbed_LIBRARY Restbed_INCLUDE_DIR)
|
||||
if (Restbed_INCLUDE_DIR)
|
||||
set(Restbed_FOUND TRUE)
|
||||
set(Restbed_LIBRARIES ${Restbed_LIBRARY})
|
||||
set(Restbed_INCLUDE_DIRS ${Restbed_INCLUDE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
+55
-53
@@ -1,7 +1,7 @@
|
||||
dnl define macros
|
||||
m4_define([opendht_major_version], 1)
|
||||
m4_define([opendht_minor_version], 6)
|
||||
m4_define([opendht_patch_version], 0)
|
||||
m4_define([opendht_minor_version], 8)
|
||||
m4_define([opendht_patch_version], 2)
|
||||
m4_define([opendht_version],
|
||||
[opendht_major_version.opendht_minor_version.opendht_patch_version])
|
||||
|
||||
@@ -25,7 +25,7 @@ AC_PROG_CXX
|
||||
AM_PROG_AR
|
||||
|
||||
dnl Check for logs
|
||||
AC_ARG_ENABLE([logs], [AS_HELP_STRING([--disable-logs], [Disble DHT logs])])
|
||||
AC_ARG_ENABLE([logs], [AS_HELP_STRING([--disable-logs], [Disable DHT logs])])
|
||||
AS_IF([test "x$enable_logs" != "xno"], [
|
||||
AC_DEFINE([OPENDHT_LOG], [true], [Define if DHT logs are enabled])
|
||||
], [
|
||||
@@ -33,9 +33,9 @@ AS_IF([test "x$enable_logs" != "xno"], [
|
||||
])
|
||||
|
||||
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_ARG_ENABLE([indexation], [AS_HELP_STRING([--disable-indexation], [Disable DHT indexation])])
|
||||
AM_CONDITIONAL(ENABLE_INDEXATION, test x$enable_indexation != "xno")
|
||||
AS_IF([test "x$enable_indexation" != "xno"], [
|
||||
AC_DEFINE([OPENDHT_INDEXATION], [1], [Define if DHT indexation is enabled])
|
||||
])
|
||||
|
||||
@@ -101,9 +101,51 @@ LT_LANG(C++)
|
||||
AX_CXX_COMPILE_STDCXX(11,[noext],[mandatory])
|
||||
|
||||
PKG_PROG_PKG_CONFIG()
|
||||
|
||||
AC_ARG_ENABLE([proxy_server], AS_HELP_STRING([--enable-proxy-server], [Enable proxy server ability]), proxy_server=yes, proxy_server=no)
|
||||
AM_CONDITIONAL(ENABLE_PROXY_SERVER, test x$proxy_server == xyes)
|
||||
|
||||
AC_ARG_ENABLE([push_notifications], AS_HELP_STRING([--enable-push-notifications], [Enable push notifications support]), push_notifications=yes, push_notifications=no)
|
||||
AM_CONDITIONAL(ENABLE_PUSH_NOTIFICATIONS, test x$push_notifications == xyes)
|
||||
|
||||
AC_ARG_ENABLE([proxy_server_identity], AS_HELP_STRING([--enable-proxy-server-identity],
|
||||
[Enable proxy server ability]), proxy_server_identity=yes, proxy_server_identity=no)
|
||||
AM_CONDITIONAL(ENABLE_PROXY_SERVER_IDENTITY, test x$proxy_server_identity == xyes -a x$proxy_server == xyes)
|
||||
AC_ARG_ENABLE([proxy_server_identity], AS_HELP_STRING([--enable-proxy-server-identity],
|
||||
[Enable proxy server ability]), proxy_server_identity=yes, proxy_server_identity=no)
|
||||
|
||||
AC_ARG_ENABLE([proxy_client], AS_HELP_STRING([--enable-proxy-client], [Enable proxy client ability]), proxy_client=yes, proxy_client=no)
|
||||
AM_CONDITIONAL(ENABLE_PROXY_CLIENT, test x$proxy_client == xyes)
|
||||
|
||||
AC_ARG_ENABLE([tests], AS_HELP_STRING([--enable-tests], [Enable tests]), build_tests=yes, build_tests=no)
|
||||
AM_CONDITIONAL(ENABLE_TESTS, test x$build_tests == xyes)
|
||||
AM_COND_IF([ENABLE_TESTS], [
|
||||
PKG_CHECK_MODULES([CppUnit], [cppunit >= 1.12])
|
||||
])
|
||||
|
||||
AM_CONDITIONAL(PROXY_CLIENT_OR_SERVER, test x$proxy_client == xyes | test x$proxy_server == xyes)
|
||||
|
||||
PKG_CHECK_MODULES([Nettle], [nettle >= 2.4])
|
||||
PKG_CHECK_MODULES([GnuTLS], [gnutls >= 3.3])
|
||||
PKG_CHECK_MODULES([MsgPack], [msgpack >= 1.2])
|
||||
|
||||
AC_ARG_WITH([jsoncpp], AS_HELP_STRING([--without-jsoncpp], [Build without JsonCpp support]))
|
||||
AS_IF([test "x$with_jsoncpp" != "xno"],
|
||||
[PKG_CHECK_MODULES([JsonCpp], [jsoncpp >= 1.7.2], [have_jsoncpp=yes], [have_jsoncpp=no])],
|
||||
[have_jsoncpp=no])
|
||||
AS_IF([test "x$have_jsoncpp" = "xyes"], [
|
||||
AC_MSG_NOTICE([Using JsonCpp])
|
||||
CPPFLAGS+=" -DOPENDHT_JSONCPP=1"
|
||||
], [
|
||||
AC_MSG_NOTICE([Not using JsonCpp])
|
||||
AM_COND_IF(PROXY_CLIENT_OR_SERVER, AC_MSG_ERROR(["JsonCpp is required for proxy/push notification support"]))
|
||||
])
|
||||
|
||||
AM_COND_IF([PROXY_CLIENT_OR_SERVER], [
|
||||
AC_CHECK_LIB(restbed, exit,, AC_MSG_ERROR([Missing restbed files]))
|
||||
LDFLAGS="${LDFLAGS} -lrestbed"
|
||||
])
|
||||
|
||||
CXXFLAGS="${CXXFLAGS} -DMSGPACK_DISABLE_LEGACY_NIL -DMSGPACK_DISABLE_LEGACY_CONVERT"
|
||||
|
||||
dnl Check for Argon2
|
||||
@@ -135,56 +177,15 @@ 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, [CPPFLAGS+=" -DOPENDHT_PROXY_SERVER"], [])
|
||||
AM_COND_IF(ENABLE_PROXY_CLIENT, [CPPFLAGS+=" -DOPENDHT_PROXY_CLIENT"], [])
|
||||
AM_COND_IF(ENABLE_PUSH_NOTIFICATIONS, [CPPFLAGS+=" -DOPENDHT_PUSH_NOTIFICATIONS"], [])
|
||||
AM_COND_IF(ENABLE_PROXY_SERVER_IDENTITY, [CPPFLAGS+=" -DOPENDHT_PROXY_SERVER_IDENTITY"], [])
|
||||
|
||||
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([HAVE_DOXYGEN], [
|
||||
AC_CONFIG_FILES([doc/Doxyfile doc/Makefile])
|
||||
])
|
||||
|
||||
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
|
||||
AM_COND_IF([USE_CYTHON], [
|
||||
AC_SUBST(CURRENT_SOURCE_DIR, ".")
|
||||
@@ -197,5 +198,6 @@ AM_COND_IF([USE_CYTHON], [
|
||||
AC_CONFIG_FILES([Makefile
|
||||
src/Makefile
|
||||
tools/Makefile
|
||||
tests/Makefile
|
||||
opendht.pc])
|
||||
AC_OUTPUT
|
||||
+17
-2
@@ -1,3 +1,18 @@
|
||||
FROM ubuntu:17.04
|
||||
FROM ubuntu:16.04
|
||||
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
RUN apt-get update && apt-get install -y build-essential cmake git wget libncurses5-dev libreadline-dev nettle-dev libgnutls28-dev libuv1-dev libmsgpack-dev libargon2-0-dev cython3 python3-dev python3-setuptools && apt-get clean
|
||||
RUN apt-get update && apt-get install -y build-essential cmake git wget libncurses5-dev libreadline-dev nettle-dev libgnutls28-dev libuv1-dev cython3 python3-dev libcppunit-dev libjsoncpp-dev libasio-dev libssl-dev python3-setuptools && apt-get clean
|
||||
|
||||
# build restbed from sources
|
||||
RUN git clone --recursive https://github.com/corvusoft/restbed.git \
|
||||
&& cd restbed && mkdir build && cd build \
|
||||
&& cmake -DBUILD_TESTS=NO -DBUILD_EXAMPLES=NO -DBUILD_SSL=NO -DBUILD_SHARED=YES -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_INSTALL_LIBDIR=lib .. \
|
||||
&& make -j8 install \
|
||||
&& cd .. && rm -rf restbed
|
||||
|
||||
#build msgpack from source
|
||||
RUN wget https://github.com/msgpack/msgpack-c/releases/download/cpp-2.1.5/msgpack-2.1.5.tar.gz \
|
||||
&& tar -xzf msgpack-2.1.5.tar.gz \
|
||||
&& cd msgpack-2.1.5 && mkdir build && cd build \
|
||||
&& cmake -DMSGPACK_CXX11=ON -DMSGPACK_BUILD_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX=/usr .. \
|
||||
&& make -j8 && make install \
|
||||
&& cd ../.. && rm -rf msgpack-2.1.5 msgpack-2.1.5.tar.gz
|
||||
@@ -1,7 +1,23 @@
|
||||
FROM ubuntu:17.04
|
||||
FROM ubuntu:16.04
|
||||
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y llvm llvm-dev clang make cmake git wget libncurses5-dev libreadline-dev nettle-dev libgnutls28-dev libuv1-dev libmsgpack-dev libargon2-0-dev cython3 python3-dev python3-setuptools \
|
||||
&& apt-get install -y llvm llvm-dev clang make cmake git wget libncurses5-dev libreadline-dev nettle-dev libgnutls28-dev libuv1-dev libmsgpack-dev libjsoncpp-dev libasio-dev cython3 python3-dev python3-setuptools libcppunit-dev \
|
||||
&& apt-get remove -y gcc g++ && apt-get autoremove -y && apt-get clean
|
||||
|
||||
ENV CC cc
|
||||
ENV CXX c++
|
||||
ENV CXX c++
|
||||
|
||||
# build restbed from sources
|
||||
RUN git clone --recursive https://github.com/corvusoft/restbed.git \
|
||||
&& cd restbed && mkdir build && cd build \
|
||||
&& cmake -DBUILD_TESTS=NO -DBUILD_EXAMPLES=NO -DBUILD_SSL=NO -DBUILD_SHARED=YES -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_INSTALL_LIBDIR=lib .. \
|
||||
&& make -j8 install \
|
||||
&& cd .. && rm -rf restbed
|
||||
|
||||
#build msgpack from source
|
||||
RUN wget https://github.com/msgpack/msgpack-c/releases/download/cpp-2.1.5/msgpack-2.1.5.tar.gz \
|
||||
&& tar -xzf msgpack-2.1.5.tar.gz \
|
||||
&& cd msgpack-2.1.5 && mkdir build && cd build \
|
||||
&& cmake -DMSGPACK_CXX11=ON -DMSGPACK_BUILD_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX=/usr .. \
|
||||
&& make -j8 && make install \
|
||||
&& cd ../.. && rm -rf msgpack-2.1.5 msgpack-2.1.5.tar.gz
|
||||
@@ -1,16 +0,0 @@
|
||||
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,5 +1,7 @@
|
||||
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
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On -DOPENDHT_LTO=On -DOPENDHT_TESTS=ON .. \
|
||||
&& make -j8 && ./opendht_unit_tests && make install
|
||||
@@ -1,5 +1,7 @@
|
||||
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
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On -DOPENDHT_TESTS=ON .. \
|
||||
&& make -j8 && ./opendht_unit_tests && make install
|
||||
@@ -1,3 +1,4 @@
|
||||
FROM opendht-deps-proxy
|
||||
FROM aberaud/opendht-deps
|
||||
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
|
||||
COPY . /root/opendht
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -19,7 +19,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "opendht/dhtrunner.h"
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
#ifdef OPENDHT_PROXY_SERVER
|
||||
#include "opendht/dht_proxy_server.h"
|
||||
#endif
|
||||
#include "opendht/log.h"
|
||||
|
||||
+37
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
@@ -27,9 +27,9 @@
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
#include <json/json.h>
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
#endif
|
||||
|
||||
namespace dht {
|
||||
|
||||
@@ -51,15 +51,37 @@ struct OPENDHT_PUBLIC NodeStats {
|
||||
incoming_nodes {0};
|
||||
unsigned table_depth {0};
|
||||
unsigned getKnownNodes() const { return good_nodes + dubious_nodes; }
|
||||
unsigned long getNetworkSizeEstimation() const { return 8 * std::exp2(table_depth); }
|
||||
std::string toString() const;
|
||||
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
/**
|
||||
* Build a json object from a NodeStats
|
||||
*/
|
||||
Json::Value toJson() const;
|
||||
NodeStats() {};
|
||||
explicit NodeStats(const Json::Value& v);
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
#endif
|
||||
|
||||
MSGPACK_DEFINE_MAP(good_nodes, dubious_nodes, cached_nodes, incoming_nodes, table_depth)
|
||||
};
|
||||
|
||||
struct OPENDHT_PUBLIC NodeInfo {
|
||||
InfoHash id;
|
||||
InfoHash node_id;
|
||||
NodeStats ipv4;
|
||||
NodeStats ipv6;
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
/**
|
||||
* Build a json object from a NodeStats
|
||||
*/
|
||||
Json::Value toJson() const;
|
||||
NodeInfo() {};
|
||||
explicit NodeInfo(const Json::Value& v);
|
||||
#endif
|
||||
|
||||
MSGPACK_DEFINE_MAP(id, node_id, ipv4, ipv6)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -81,6 +103,9 @@ struct OPENDHT_PUBLIC Config {
|
||||
|
||||
/** Makes the DHT responsible to maintain its stored values. Consumes more ressources. */
|
||||
bool maintain_storage;
|
||||
|
||||
/** If set, the dht will load its state from this file on start and save its state in this file on shutdown */
|
||||
std::string persist_path;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -98,6 +123,7 @@ using ValuesExport = std::pair<InfoHash, Blob>;
|
||||
|
||||
using QueryCallback = std::function<bool(const std::vector<std::shared_ptr<FieldValueIndex>>& fields)>;
|
||||
using GetCallback = std::function<bool(const std::vector<std::shared_ptr<Value>>& values)>;
|
||||
using ValueCallback = std::function<bool(const std::vector<std::shared_ptr<Value>>& values, bool expired)>;
|
||||
using GetCallbackSimple = std::function<bool(std::shared_ptr<Value> value)>;
|
||||
using ShutdownCallback = std::function<void()>;
|
||||
|
||||
@@ -105,8 +131,8 @@ using CertificateStoreQuery = std::function<std::vector<std::shared_ptr<crypto::
|
||||
|
||||
typedef bool (*GetCallbackRaw)(std::shared_ptr<Value>, void *user_data);
|
||||
|
||||
OPENDHT_PUBLIC GetCallbackSimple bindGetCb(GetCallbackRaw raw_cb, void* user_data);
|
||||
OPENDHT_PUBLIC GetCallback bindGetCb(GetCallbackSimple cb);
|
||||
OPENDHT_PUBLIC GetCallbackSimple bindGetCb(const GetCallbackRaw& raw_cb, void* user_data);
|
||||
OPENDHT_PUBLIC GetCallback bindGetCb(const GetCallbackSimple& cb);
|
||||
|
||||
using DoneCallback = std::function<void(bool success, const std::vector<std::shared_ptr<Node>>& nodes)>;
|
||||
typedef void (*DoneCallbackRaw)(bool, std::vector<std::shared_ptr<Node>>*, void *user_data);
|
||||
@@ -116,10 +142,10 @@ typedef bool (*FilterRaw)(const Value&, void *user_data);
|
||||
|
||||
using DoneCallbackSimple = std::function<void(bool success)>;
|
||||
|
||||
OPENDHT_PUBLIC ShutdownCallback bindShutdownCb(ShutdownCallbackRaw shutdown_cb_raw, void* user_data);
|
||||
OPENDHT_PUBLIC ShutdownCallback bindShutdownCb(const ShutdownCallbackRaw& shutdown_cb_raw, void* user_data);
|
||||
OPENDHT_PUBLIC DoneCallback bindDoneCb(DoneCallbackSimple donecb);
|
||||
OPENDHT_PUBLIC DoneCallback bindDoneCb(DoneCallbackRaw raw_cb, void* user_data);
|
||||
OPENDHT_PUBLIC DoneCallbackSimple bindDoneCbSimple(DoneCallbackSimpleRaw raw_cb, void* user_data);
|
||||
OPENDHT_PUBLIC Value::Filter bindFilterRaw(FilterRaw raw_filter, void* user_data);
|
||||
OPENDHT_PUBLIC DoneCallback bindDoneCb(const DoneCallbackRaw& raw_cb, void* user_data);
|
||||
OPENDHT_PUBLIC DoneCallbackSimple bindDoneCbSimple(const DoneCallbackSimpleRaw& raw_cb, void* user_data);
|
||||
OPENDHT_PUBLIC Value::Filter bindFilterRaw(const FilterRaw& raw_filter, void* user_data);
|
||||
|
||||
}
|
||||
+81
-18
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -66,7 +66,7 @@ using Identity = std::pair<std::shared_ptr<PrivateKey>, std::shared_ptr<Certific
|
||||
*/
|
||||
struct OPENDHT_PUBLIC PublicKey
|
||||
{
|
||||
PublicKey() {}
|
||||
PublicKey();
|
||||
|
||||
/**
|
||||
* Takes ownership of an existing gnutls_pubkey.
|
||||
@@ -86,8 +86,16 @@ struct OPENDHT_PUBLIC PublicKey
|
||||
|
||||
PublicKey& operator=(PublicKey&& o) noexcept;
|
||||
|
||||
/**
|
||||
* Get public key fingerprint
|
||||
*/
|
||||
InfoHash getId() const;
|
||||
|
||||
/**
|
||||
* Get public key long fingerprint
|
||||
*/
|
||||
PkId getLongId() const;
|
||||
|
||||
bool checkSignature(const Blob& data, const Blob& signature) const;
|
||||
Blob encrypt(const Blob&) const;
|
||||
|
||||
@@ -107,7 +115,9 @@ struct OPENDHT_PUBLIC PublicKey
|
||||
|
||||
void msgpack_unpack(msgpack::object o);
|
||||
|
||||
gnutls_pubkey_t pk {};
|
||||
gnutls_digest_algorithm_t getPreferredDigest() const;
|
||||
|
||||
gnutls_pubkey_t pk {nullptr};
|
||||
private:
|
||||
PublicKey(const PublicKey&) = delete;
|
||||
PublicKey& operator=(const PublicKey&) = delete;
|
||||
@@ -133,6 +143,7 @@ struct OPENDHT_PUBLIC PrivateKey
|
||||
PrivateKey(const Blob& import, const std::string& password = {});
|
||||
~PrivateKey();
|
||||
explicit operator bool() const { return key; }
|
||||
|
||||
PublicKey getPublicKey() const;
|
||||
Blob serialize(const std::string& password = {}) const;
|
||||
|
||||
@@ -177,7 +188,7 @@ class OPENDHT_PUBLIC RevocationList
|
||||
public:
|
||||
RevocationList();
|
||||
RevocationList(const Blob& b);
|
||||
RevocationList(RevocationList&& o) : crl(o.crl) { o.crl = nullptr; }
|
||||
RevocationList(RevocationList&& o) noexcept : crl(o.crl) { o.crl = nullptr; }
|
||||
~RevocationList();
|
||||
|
||||
RevocationList& operator=(RevocationList&& o) { crl = o.crl; o.crl = nullptr; return *this; }
|
||||
@@ -245,14 +256,48 @@ private:
|
||||
RevocationList& operator=(const RevocationList&) = delete;
|
||||
};
|
||||
|
||||
enum class NameType { UNKNOWN = 0, RFC822, DNS, URI, IP };
|
||||
|
||||
class OPENDHT_PUBLIC CertificateRequest {
|
||||
public:
|
||||
CertificateRequest();
|
||||
CertificateRequest(const uint8_t* data, size_t size);
|
||||
CertificateRequest(const Blob& data) : CertificateRequest(data.data(), data.size()) {}
|
||||
|
||||
CertificateRequest(CertificateRequest&& o) noexcept : request(std::move(o.request)) {
|
||||
o.request = nullptr;
|
||||
}
|
||||
CertificateRequest& operator=(CertificateRequest&& o) noexcept;
|
||||
|
||||
~CertificateRequest();
|
||||
|
||||
void setName(const std::string& name);
|
||||
void setUID(const std::string& name);
|
||||
void setAltName(NameType type, const std::string& name);
|
||||
|
||||
std::string getName() const;
|
||||
std::string getUID() const;
|
||||
|
||||
void sign(const PrivateKey& key, const std::string& password = {});
|
||||
|
||||
bool verify() const;
|
||||
|
||||
Blob pack() const;
|
||||
|
||||
gnutls_x509_crq_t get() const { return request; }
|
||||
private:
|
||||
CertificateRequest(const CertificateRequest& o) = delete;
|
||||
CertificateRequest& operator=(const CertificateRequest& o) = delete;
|
||||
gnutls_x509_crq_t request {nullptr};
|
||||
};
|
||||
|
||||
struct OPENDHT_PUBLIC Certificate {
|
||||
Certificate() {}
|
||||
Certificate() noexcept {}
|
||||
|
||||
/**
|
||||
* Take ownership of existing gnutls structure
|
||||
*/
|
||||
Certificate(gnutls_x509_crt_t crt) : cert(crt) {}
|
||||
Certificate(gnutls_x509_crt_t crt) noexcept : cert(crt) {}
|
||||
|
||||
Certificate(Certificate&& o) noexcept : cert(o.cert), issuer(std::move(o.issuer)) { o.cert = nullptr; };
|
||||
|
||||
@@ -368,6 +413,7 @@ struct OPENDHT_PUBLIC Certificate {
|
||||
|
||||
/** Same as getPublicKey().getId() */
|
||||
InfoHash getId() const;
|
||||
/** Same as getPublicKey().getLongId() */
|
||||
PkId getLongId() const;
|
||||
|
||||
/** Read certificate Common Name (CN) */
|
||||
@@ -382,8 +428,6 @@ struct OPENDHT_PUBLIC Certificate {
|
||||
/** Read certificate issuer User ID (UID) */
|
||||
std::string getIssuerUID() const;
|
||||
|
||||
enum class NameType { UNKNOWN = 0, RFC822, DNS, URI, IP };
|
||||
|
||||
/** Read certificate alternative names */
|
||||
std::vector<std::pair<NameType, std::string>> getAltNames() const;
|
||||
|
||||
@@ -404,12 +448,25 @@ struct OPENDHT_PUBLIC Certificate {
|
||||
|
||||
std::string print() const;
|
||||
|
||||
/**
|
||||
* As a CA, revoke a certificate, adding it to
|
||||
* the attached Certificate Revocation List (CRL)
|
||||
*/
|
||||
void revoke(const PrivateKey&, const Certificate&);
|
||||
|
||||
/**
|
||||
* Get the list of certificates revoked as as CA.
|
||||
*/
|
||||
std::vector<std::shared_ptr<RevocationList>> getRevocationLists() const;
|
||||
|
||||
/**
|
||||
* Attach existing revocation list.
|
||||
*/
|
||||
void addRevocationList(RevocationList&&);
|
||||
void addRevocationList(std::shared_ptr<RevocationList>);
|
||||
|
||||
static Certificate generate(const PrivateKey& key, const std::string& name = "dhtnode", Identity ca = {}, bool is_ca = false);
|
||||
static Certificate generate(const PrivateKey& key, const std::string& name = "dhtnode", const Identity& ca = {}, bool is_ca = false);
|
||||
static Certificate generate(const CertificateRequest& request, const Identity& ca);
|
||||
|
||||
gnutls_x509_crt_t getCopy() const {
|
||||
if (not cert)
|
||||
@@ -450,7 +507,9 @@ struct OPENDHT_PUBLIC Certificate {
|
||||
return {crts, crls};
|
||||
}
|
||||
|
||||
gnutls_x509_crt_t cert {};
|
||||
gnutls_digest_algorithm_t getPreferredDigest() const;
|
||||
|
||||
gnutls_x509_crt_t cert {nullptr};
|
||||
std::shared_ptr<Certificate> issuer {};
|
||||
private:
|
||||
Certificate(const Certificate&) = delete;
|
||||
@@ -478,10 +537,10 @@ struct OPENDHT_PUBLIC TrustList
|
||||
};
|
||||
|
||||
TrustList();
|
||||
TrustList(TrustList&& o) : trust(std::move(o.trust)) {
|
||||
TrustList(TrustList&& o) noexcept : trust(std::move(o.trust)) {
|
||||
o.trust = nullptr;
|
||||
}
|
||||
TrustList& operator=(TrustList&& o);
|
||||
TrustList& operator=(TrustList&& o) noexcept;
|
||||
~TrustList();
|
||||
void add(const Certificate& crt);
|
||||
void add(const RevocationList& crl);
|
||||
@@ -491,7 +550,7 @@ struct OPENDHT_PUBLIC TrustList
|
||||
private:
|
||||
TrustList(const TrustList& o) = delete;
|
||||
TrustList& operator=(const TrustList& o) = delete;
|
||||
gnutls_x509_trust_list_t trust;
|
||||
gnutls_x509_trust_list_t trust {nullptr};
|
||||
};
|
||||
|
||||
template <class T>
|
||||
@@ -508,9 +567,13 @@ public:
|
||||
~secure_vector() { clean(); }
|
||||
|
||||
static secure_vector<T> getRandom(size_t size) {
|
||||
secure_vector<T> ret(size/sizeof(T));
|
||||
secure_vector<T> ret(size);
|
||||
crypto::random_device rdev;
|
||||
#ifdef _WIN32
|
||||
std::uniform_int_distribution<int> rand_byte{ 0, std::numeric_limits<uint8_t>::max() };
|
||||
#else
|
||||
std::uniform_int_distribution<uint8_t> rand_byte;
|
||||
#endif
|
||||
std::generate_n((uint8_t*)ret.data_.data(), ret.size()*sizeof(T), std::bind(rand_byte, std::ref(rdev)));
|
||||
return ret;
|
||||
}
|
||||
@@ -585,11 +648,11 @@ using SecureBlob = secure_vector<uint8_t>;
|
||||
* If not set, the generated certificate will be a self-signed CA.
|
||||
* @param key_length stength of the generated private key (bits).
|
||||
*/
|
||||
OPENDHT_PUBLIC Identity generateIdentity(const std::string& name, Identity ca, unsigned key_length, bool is_ca);
|
||||
OPENDHT_PUBLIC Identity generateIdentity(const std::string& name = "dhtnode", Identity ca = {}, unsigned key_length = 4096);
|
||||
OPENDHT_PUBLIC Identity generateIdentity(const std::string& name, const Identity& ca, unsigned key_length, bool is_ca);
|
||||
OPENDHT_PUBLIC Identity generateIdentity(const std::string& name = "dhtnode", const Identity& ca = {}, unsigned key_length = 4096);
|
||||
|
||||
OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name, Identity ca, bool is_ca);
|
||||
OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name = "dhtnode", Identity ca = {});
|
||||
OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name, const Identity& ca, bool is_ca);
|
||||
OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name = "dhtnode", const Identity& ca = {});
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -38,7 +38,7 @@ class OPENDHT_PUBLIC DhtMessage : public Value::Serializable<DhtMessage>
|
||||
public:
|
||||
static const ValueType TYPE;
|
||||
|
||||
DhtMessage(std::string s = {}, Blob msg = {}) : service(s), data(msg) {}
|
||||
DhtMessage(const std::string& s = {}, const Blob& msg = {}) : service(s), data(msg) {}
|
||||
|
||||
std::string getService() const {
|
||||
return service;
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
|
||||
static bool storePolicy(InfoHash key, std::shared_ptr<Value>& value, const InfoHash& from, const SockAddr&);
|
||||
|
||||
static Value::Filter ServiceFilter(std::string s);
|
||||
static Value::Filter ServiceFilter(const std::string& s);
|
||||
|
||||
/** print value for debugging */
|
||||
friend std::ostream& operator<< (std::ostream&, const DhtMessage&);
|
||||
@@ -114,6 +114,8 @@ public:
|
||||
ImMessage() {}
|
||||
ImMessage(dht::Value::Id id, std::string&& m, long d = 0)
|
||||
: id(id), msg(std::move(m)), date(d) {}
|
||||
ImMessage(dht::Value::Id id, std::string&& dt, std::string&& m, long d = 0)
|
||||
: id(id), msg(std::move(m)), datatype(std::move(dt)), date(d) {}
|
||||
|
||||
virtual void unpackValue(const Value& v) override {
|
||||
to = v.recipient;
|
||||
@@ -123,10 +125,11 @@ public:
|
||||
dht::InfoHash to;
|
||||
dht::Value::Id id {0};
|
||||
std::string msg;
|
||||
std::string datatype;
|
||||
long date {0};
|
||||
ImStatus status {ImStatus::NONE};
|
||||
|
||||
MSGPACK_DEFINE_MAP(id, msg, date, status)
|
||||
MSGPACK_DEFINE_MAP(id, msg, date, status, datatype)
|
||||
};
|
||||
|
||||
class OPENDHT_PUBLIC TrustRequest : public EncryptedValue<TrustRequest>
|
||||
|
||||
+41
-25
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
* Initialise the Dht with two open sockets (for IPv4 and IP6)
|
||||
* and an ID for the node.
|
||||
*/
|
||||
Dht(const int& s, const int& s6, Config config);
|
||||
Dht(const int& s, const int& s6, const Config& config);
|
||||
virtual ~Dht();
|
||||
|
||||
/**
|
||||
@@ -99,11 +99,10 @@ public:
|
||||
bool isRunning(sa_family_t af = 0) const;
|
||||
|
||||
virtual void registerType(const ValueType& type) {
|
||||
types[type.id] = type;
|
||||
types.registerType(type);
|
||||
}
|
||||
const ValueType& getType(ValueType::Id type_id) const {
|
||||
const auto& t_it = types.find(type_id);
|
||||
return (t_it == types.end()) ? ValueType::USER_DATA : t_it->second;
|
||||
return types.getType(type_id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,7 +163,7 @@ public:
|
||||
/**
|
||||
* Get locally stored data for the given hash.
|
||||
*/
|
||||
std::vector<Sp<Value>> getLocal(const InfoHash& key, Value::Filter f = Value::AllFilter()) const;
|
||||
std::vector<Sp<Value>> getLocal(const InfoHash& key, Value::Filter f = {}) const;
|
||||
|
||||
/**
|
||||
* Get locally stored data for the given key and value id.
|
||||
@@ -231,8 +230,16 @@ 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& key, GetCallbackSimple cb, Value::Filter f={}, Where w = {}) {
|
||||
virtual size_t listen(const InfoHash&, ValueCallback, Value::Filter={}, Where={});
|
||||
|
||||
virtual size_t listen(const InfoHash& key, GetCallback cb, Value::Filter f={}, Where w={}) {
|
||||
return listen(key, [cb](const std::vector<Sp<Value>>& vals, bool expired){
|
||||
if (not expired)
|
||||
return cb(vals);
|
||||
return true;
|
||||
}, std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w={}) {
|
||||
return listen(key, bindGetCb(cb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
|
||||
@@ -254,11 +261,14 @@ public:
|
||||
* Get the list of good nodes for local storage saving purposes
|
||||
* The list is ordered to minimize the back-to-work delay.
|
||||
*/
|
||||
std::vector<NodeExport> exportNodes();
|
||||
std::vector<NodeExport> exportNodes() const;
|
||||
|
||||
std::vector<ValuesExport> exportValues() const;
|
||||
void importValues(const std::vector<ValuesExport>&);
|
||||
|
||||
void saveState(const std::string& path) const;
|
||||
void loadState(const std::string& path);
|
||||
|
||||
NodeStats getNodesStats(sa_family_t af) const;
|
||||
|
||||
std::string getStorageLog() const;
|
||||
@@ -317,10 +327,10 @@ private:
|
||||
static constexpr unsigned LISTEN_NODES {4};
|
||||
|
||||
/* The maximum number of hashes we're willing to track. */
|
||||
static constexpr unsigned MAX_HASHES {16384};
|
||||
static constexpr unsigned MAX_HASHES {64 * 1024};
|
||||
|
||||
/* The maximum number of searches we keep data about. */
|
||||
static constexpr unsigned MAX_SEARCHES {16384};
|
||||
static constexpr unsigned MAX_SEARCHES {64 * 1024};
|
||||
|
||||
static constexpr std::chrono::minutes MAX_STORAGE_MAINTENANCE_EXPIRE_TIME {10};
|
||||
|
||||
@@ -350,14 +360,7 @@ private:
|
||||
uint64_t oldsecret {};
|
||||
|
||||
// registred types
|
||||
std::map<ValueType::Id, ValueType> types;
|
||||
|
||||
// are we a bootstrap node ?
|
||||
// note: Any running node can be used as a bootstrap node.
|
||||
// Only nodes running only as bootstrap nodes should
|
||||
// be put in bootstrap mode.
|
||||
const bool is_bootstrap {false};
|
||||
const bool maintain_storage {false};
|
||||
TypeStore types;
|
||||
|
||||
// the stuff
|
||||
RoutingTable buckets4 {};
|
||||
@@ -393,6 +396,15 @@ private:
|
||||
|
||||
std::mt19937_64 rd {crypto::getSeededRandomEngine<std::mt19937_64>()};
|
||||
|
||||
std::string persistPath;
|
||||
|
||||
// are we a bootstrap node ?
|
||||
// note: Any running node can be used as a bootstrap node.
|
||||
// Only nodes running only as bootstrap nodes should
|
||||
// be put in bootstrap mode.
|
||||
const bool is_bootstrap {false};
|
||||
const bool maintain_storage {false};
|
||||
|
||||
void rotateSecrets();
|
||||
|
||||
Blob makeToken(const SockAddr&, bool old) const;
|
||||
@@ -402,12 +414,14 @@ private:
|
||||
|
||||
// Storage
|
||||
void storageAddListener(const InfoHash& id, const Sp<Node>& node, size_t tid, Query&& = {});
|
||||
bool storageStore(const InfoHash& id, const Sp<Value>& value, time_point created, const SockAddr& sa = {});
|
||||
bool storageStore(const InfoHash& id, const Sp<Value>& value, time_point created, const SockAddr& sa = {}, bool permanent = false);
|
||||
bool storageErase(const InfoHash& id, Value::Id vid);
|
||||
bool storageRefresh(const InfoHash& id, Value::Id vid);
|
||||
void expireStore();
|
||||
void expireStorage(InfoHash h);
|
||||
void expireStore(decltype(store)::iterator);
|
||||
|
||||
void storageChanged(const InfoHash& id, Storage& st, ValueStorage&);
|
||||
void storageChanged(const InfoHash& id, Storage& st, ValueStorage&, bool newValue);
|
||||
std::string printStorageLog(const decltype(store)::value_type&) const;
|
||||
|
||||
/**
|
||||
@@ -416,7 +430,7 @@ private:
|
||||
* nodes.
|
||||
*/
|
||||
void dataPersistence(InfoHash id);
|
||||
size_t maintainStorage(decltype(store)::value_type&, bool force=false, DoneCallback donecb=nullptr);
|
||||
size_t maintainStorage(decltype(store)::value_type&, bool force=false, const DoneCallback& donecb={});
|
||||
|
||||
// Buckets
|
||||
RoutingTable& buckets(sa_family_t af) { return af == AF_INET ? buckets4 : buckets6; }
|
||||
@@ -457,10 +471,10 @@ private:
|
||||
* Low-level method that will perform a search on the DHT for the specified
|
||||
* infohash (id), using the specified IP version (IPv4 or IPv6).
|
||||
*/
|
||||
Sp<Search> search(const InfoHash& id, sa_family_t af, GetCallback = {}, QueryCallback = {}, DoneCallback = {}, Value::Filter = {}, Query q = {});
|
||||
Sp<Search> search(const InfoHash& id, sa_family_t af, GetCallback = {}, QueryCallback = {}, DoneCallback = {}, Value::Filter = {}, const Sp<Query>& q = {});
|
||||
|
||||
void announce(const InfoHash& id, sa_family_t af, Sp<Value> value, DoneCallback callback, time_point created=time_point::max(), bool permanent = false);
|
||||
size_t listenTo(const InfoHash& id, sa_family_t af, GetCallback cb, Value::Filter f = Value::AllFilter(), const Sp<Query>& q = {});
|
||||
size_t listenTo(const InfoHash& id, sa_family_t af, ValueCallback cb, Value::Filter f = {}, const Sp<Query>& q = {});
|
||||
|
||||
/**
|
||||
* Refill the search with good nodes if possible.
|
||||
@@ -528,7 +542,9 @@ private:
|
||||
*
|
||||
* @param sr The search to execute its operations.
|
||||
*/
|
||||
void searchStep(Sp<Search> sr);
|
||||
void searchStep(Sp<Search>);
|
||||
void searchSynchedNodeListen(const Sp<Search>&, SearchNode&);
|
||||
|
||||
void dumpSearch(const Search& sr, std::ostream& out) const;
|
||||
|
||||
bool neighbourhoodMaintenance(RoutingTable&);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
virtual NodeStatus getStatus() const = 0;
|
||||
|
||||
/**
|
||||
* Get the ID of the node.
|
||||
* Get the ID of the DHT node.
|
||||
*/
|
||||
virtual const InfoHash& getNodeId() const = 0;
|
||||
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
/**
|
||||
* Get locally stored data for the given hash.
|
||||
*/
|
||||
virtual std::vector<Sp<Value>> getLocal(const InfoHash& key, Value::Filter f = Value::AllFilter()) const = 0;
|
||||
virtual std::vector<Sp<Value>> getLocal(const InfoHash& key, Value::Filter f = {}) const = 0;
|
||||
|
||||
/**
|
||||
* Get locally stored data for the given key and value id.
|
||||
@@ -165,6 +165,7 @@ public:
|
||||
*/
|
||||
virtual size_t listen(const InfoHash&, GetCallback, Value::Filter={}, Where w = {}) = 0;
|
||||
virtual size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w = {}) = 0;
|
||||
virtual size_t listen(const InfoHash&, ValueCallback, Value::Filter={}, Where w = {}) = 0;
|
||||
|
||||
virtual bool cancelListen(const InfoHash&, size_t token) = 0;
|
||||
|
||||
@@ -180,7 +181,7 @@ public:
|
||||
* 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<NodeExport> exportNodes() const = 0;
|
||||
|
||||
virtual std::vector<ValuesExport> exportValues() const = 0;
|
||||
virtual void importValues(const std::vector<ValuesExport>&) = 0;
|
||||
@@ -215,7 +216,7 @@ public:
|
||||
*/
|
||||
virtual void setLoggers(LogMethod error = NOLOG, LogMethod warn = NOLOG, LogMethod debug = NOLOG)
|
||||
{
|
||||
DHT_LOG.DEBUG = debug;
|
||||
DHT_LOG.DBG = debug;
|
||||
DHT_LOG.WARN = warn;
|
||||
DHT_LOG.ERR = error;
|
||||
}
|
||||
@@ -235,11 +236,6 @@ public:
|
||||
* @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_ {};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016-2018 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2016-2019 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
* Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
@@ -17,8 +17,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
@@ -29,6 +27,7 @@
|
||||
#include "def.h"
|
||||
#include "dht_interface.h"
|
||||
#include "scheduler.h"
|
||||
#include "proxy.h"
|
||||
|
||||
namespace restbed {
|
||||
class Request;
|
||||
@@ -43,12 +42,12 @@ namespace dht {
|
||||
class OPENDHT_PUBLIC DhtProxyClient final : public DhtInterface {
|
||||
public:
|
||||
|
||||
DhtProxyClient() : scheduler(DHT_LOG) {}
|
||||
DhtProxyClient();
|
||||
|
||||
explicit DhtProxyClient(std::function<void()> loopSignal, const std::string& serverHost, const std::string& pushClientId = "");
|
||||
|
||||
virtual void setPushNotificationToken(const std::string& token) {
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
deviceKey_ = token;
|
||||
#endif
|
||||
}
|
||||
@@ -102,8 +101,6 @@ public:
|
||||
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).
|
||||
*
|
||||
@@ -161,22 +158,25 @@ public:
|
||||
*
|
||||
* @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 = {}) {
|
||||
virtual size_t listen(const InfoHash&, ValueCallback, Value::Filter={}, Where={});
|
||||
|
||||
virtual size_t listen(const InfoHash& key, GetCallback cb, Value::Filter f={}, Where w={}) {
|
||||
return listen(key, [cb](const std::vector<Sp<Value>>& vals, bool expired){
|
||||
if (not expired)
|
||||
return cb(vals);
|
||||
return true;
|
||||
}, std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w={}) {
|
||||
return listen(key, bindGetCb(cb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual bool cancelListen(const InfoHash&, size_t token);
|
||||
virtual bool cancelListen(const InfoHash& key, size_t token);
|
||||
|
||||
/**
|
||||
* Call linked callback with a push notification
|
||||
* @param notification to process
|
||||
*/
|
||||
void pushNotificationReceived(const std::map<std::string, std::string>& notification);
|
||||
/**
|
||||
* 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) {
|
||||
@@ -202,21 +202,31 @@ public:
|
||||
/**
|
||||
* Get data currently being put at the given hash.
|
||||
*/
|
||||
std::vector<Sp<Value>> getPut(const InfoHash&) { return {}; }
|
||||
std::vector<Sp<Value>> getPut(const InfoHash&);
|
||||
|
||||
/**
|
||||
* Get data currently being put at the given hash with the given id.
|
||||
*/
|
||||
Sp<Value> getPut(const InfoHash&, const Value::Id&) { return {}; }
|
||||
Sp<Value> getPut(const InfoHash&, const Value::Id&);
|
||||
|
||||
/**
|
||||
* Stop any put/announce operation at the given location,
|
||||
* for the value with the given id.
|
||||
*/
|
||||
bool cancelPut(const InfoHash&, const Value::Id&) { return false; }
|
||||
bool cancelPut(const InfoHash&, const Value::Id&);
|
||||
|
||||
void pingNode(const sockaddr*, socklen_t, DoneCallbackSimple&& /*cb*/={}) { }
|
||||
|
||||
virtual void registerType(const ValueType& type) {
|
||||
types.registerType(type);
|
||||
}
|
||||
const ValueType& getType(ValueType::Id type_id) const {
|
||||
return types.getType(type_id);
|
||||
}
|
||||
|
||||
std::vector<Sp<Value>> getLocal(const InfoHash& k, Value::Filter filter) const;
|
||||
Sp<Value> getLocalById(const InfoHash& k, Value::Id id) const;
|
||||
|
||||
/**
|
||||
* NOTE: The following methods will not be implemented because the
|
||||
* DhtProxyClient doesn't have any storage nor synchronization process
|
||||
@@ -225,11 +235,7 @@ public:
|
||||
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<NodeExport> exportNodes() const { return {}; }
|
||||
std::vector<ValuesExport> exportValues() const { return {}; }
|
||||
void importValues(const std::vector<ValuesExport>&) {}
|
||||
std::string getStorageLog() const { return {}; }
|
||||
@@ -240,12 +246,16 @@ public:
|
||||
void dumpTables() const {}
|
||||
std::vector<unsigned> getNodeMessageStats(bool) { return {}; }
|
||||
void setStorageLimit(size_t) {}
|
||||
void connectivityChanged(sa_family_t) {}
|
||||
void connectivityChanged() { }
|
||||
void connectivityChanged(sa_family_t) {
|
||||
restartListeners();
|
||||
}
|
||||
void connectivityChanged() {
|
||||
getProxyInfos();
|
||||
restartListeners();
|
||||
loopSignal_();
|
||||
}
|
||||
|
||||
private:
|
||||
const ValueType NO_VALUE;
|
||||
|
||||
/**
|
||||
* Start the connection with a server.
|
||||
*/
|
||||
@@ -255,12 +265,28 @@ private:
|
||||
* Get informations from the proxy node
|
||||
* @return the JSON returned by the proxy
|
||||
*/
|
||||
struct InfoState;
|
||||
void getProxyInfos();
|
||||
void onProxyInfos(const Json::Value& val);
|
||||
void onProxyInfos(const Json::Value& val, sa_family_t family);
|
||||
SockAddr parsePublicAddress(const Json::Value& val);
|
||||
|
||||
void opFailed();
|
||||
|
||||
bool doCancelListen(const InfoHash& key, size_t token);
|
||||
|
||||
struct ListenState;
|
||||
enum class ListenMethod {
|
||||
LISTEN,
|
||||
SUBSCRIBE,
|
||||
RESUBSCRIBE,
|
||||
};
|
||||
void sendListen(const std::shared_ptr<restbed::Request> &request,
|
||||
const ValueCallback &, const Value::Filter &filter,
|
||||
const Sp<ListenState> &state,
|
||||
ListenMethod method = ListenMethod::LISTEN);
|
||||
|
||||
void doPut(const InfoHash&, Sp<Value>, DoneCallback, time_point created, bool permanent);
|
||||
|
||||
/**
|
||||
* Initialize statusIpvX_
|
||||
*/
|
||||
@@ -273,35 +299,32 @@ private:
|
||||
* cancel all Operations
|
||||
*/
|
||||
void cancelAllOperations();
|
||||
|
||||
std::string serverHost_;
|
||||
std::string pushClientId_;
|
||||
|
||||
std::atomic_flag ongoingStatusUpdate_ = ATOMIC_FLAG_INIT;
|
||||
mutable std::mutex lockCurrentProxyInfos_;
|
||||
NodeStatus statusIpv4_ {NodeStatus::Disconnected};
|
||||
NodeStatus statusIpv6_ {NodeStatus::Disconnected};
|
||||
NodeStats stats4_ {};
|
||||
NodeStats stats6_ {};
|
||||
SockAddr publicAddress_;
|
||||
SockAddr publicAddressV4_;
|
||||
SockAddr publicAddressV6_;
|
||||
|
||||
InfoHash myid {};
|
||||
|
||||
// registred types
|
||||
TypeStore types;
|
||||
|
||||
/**
|
||||
* 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_;
|
||||
struct Listener;
|
||||
struct ProxySearch;
|
||||
|
||||
size_t listenerToken_ {0};
|
||||
std::map<InfoHash, ProxySearch> searches_;
|
||||
mutable std::mutex searchLock_;
|
||||
|
||||
/**
|
||||
* Store current put and get requests.
|
||||
@@ -320,8 +343,9 @@ private:
|
||||
std::vector<std::function<void()>> callbacks_;
|
||||
std::mutex lockCallbacks;
|
||||
|
||||
Sp<InfoState> infoState_;
|
||||
std::thread statusThread_;
|
||||
mutable std::mutex lockCurrentProxyInfos_;
|
||||
mutable std::mutex statusLock_;
|
||||
|
||||
Scheduler scheduler;
|
||||
/**
|
||||
@@ -329,27 +353,33 @@ private:
|
||||
*/
|
||||
void confirmProxy();
|
||||
Sp<Scheduler::Job> nextProxyConfirmation {};
|
||||
Sp<Scheduler::Job> listenerRestart {};
|
||||
|
||||
/**
|
||||
* Relaunch LISTEN requests if the client disconnect/reconnect.
|
||||
*/
|
||||
void restartListeners();
|
||||
|
||||
/**
|
||||
* Refresh a listen via a token
|
||||
* @param token
|
||||
*/
|
||||
void resubscribe(const InfoHash& key, Listener& listener);
|
||||
|
||||
/**
|
||||
* If we want to use push notifications by default.
|
||||
* NOTE: empty by default to avoid to use services like FCM or APN.
|
||||
*/
|
||||
std::string deviceKey_ {};
|
||||
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);
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
void fillBody(std::shared_ptr<restbed::Request> request, bool resubscribe);
|
||||
void getPushRequest(Json::Value&) const;
|
||||
#endif // OPENDHT_PUSH_NOTIFICATIONS
|
||||
|
||||
std::atomic_bool isDestroying_ {false};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OPENDHT_PROXY_CLIENT
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2018 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2017-2019 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
|
||||
@@ -16,19 +17,25 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "callbacks.h"
|
||||
#include "def.h"
|
||||
#include "sockaddr.h"
|
||||
#include "infohash.h"
|
||||
#include "proxy.h"
|
||||
#include "scheduler.h"
|
||||
#include "sockaddr.h"
|
||||
#include "value.h"
|
||||
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <restbed>
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
#include <json/json.h>
|
||||
#endif
|
||||
|
||||
namespace Json {
|
||||
class Value;
|
||||
}
|
||||
@@ -36,6 +43,7 @@ namespace Json {
|
||||
namespace dht {
|
||||
|
||||
class DhtRunner;
|
||||
class ThreadPool;
|
||||
|
||||
/**
|
||||
* Describes the REST API
|
||||
@@ -59,6 +67,56 @@ public:
|
||||
DhtProxyServer& operator=(const DhtProxyServer& other) = delete;
|
||||
DhtProxyServer& operator=(DhtProxyServer&& other) = delete;
|
||||
|
||||
struct ServerStats {
|
||||
/** Current number of listen operations */
|
||||
size_t listenCount;
|
||||
/** Current number of permanent put operations */
|
||||
size_t putCount;
|
||||
/** Current number of push tokens with at least one listen operation */
|
||||
size_t pushListenersCount;
|
||||
/** Average requests per second */
|
||||
double requestRate;
|
||||
/** Node Info **/
|
||||
NodeInfo nodeInfo;
|
||||
|
||||
std::string toString() const {
|
||||
std::ostringstream ss;
|
||||
ss << "Listens: " << listenCount << " Puts: " << putCount << " PushListeners: " << pushListenersCount << std::endl;
|
||||
ss << "Requests: " << requestRate << " per second." << std::endl;
|
||||
auto& ni = nodeInfo;
|
||||
auto& ipv4 = ni.ipv4;
|
||||
if (ipv4.table_depth > 1) {
|
||||
ss << "IPv4 Network estimation: " << ipv4.getNetworkSizeEstimation() << std::endl;;
|
||||
}
|
||||
auto& ipv6 = ni.ipv6;
|
||||
if (ipv6.table_depth > 1) {
|
||||
ss << "IPv6 Network estimation: " << ipv6.getNetworkSizeEstimation() << std::endl;;
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
/**
|
||||
* Build a json object from a NodeStats
|
||||
*/
|
||||
Json::Value toJson() const {
|
||||
Json::Value result;
|
||||
result["listenCount"] = static_cast<Json::UInt64>(listenCount);
|
||||
result["putCount"] = static_cast<Json::UInt64>(putCount);
|
||||
result["pushListenersCount"] = static_cast<Json::UInt64>(pushListenersCount);
|
||||
result["requestRate"] = requestRate;
|
||||
result["nodeInfo"] = nodeInfo.toJson();
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
ServerStats stats() const { return stats_; }
|
||||
|
||||
void updateStats() const;
|
||||
|
||||
std::shared_ptr<DhtRunner> getNode() const { return dht_; }
|
||||
|
||||
/**
|
||||
* Stop the DhtProxyServer
|
||||
*/
|
||||
@@ -74,6 +132,14 @@ private:
|
||||
*/
|
||||
void getNodeInfo(const std::shared_ptr<restbed::Session>& session) const;
|
||||
|
||||
/**
|
||||
* Return ServerStats in JSON format
|
||||
* Method: STATS "/"
|
||||
* Result: HTTP 200, body: Node infos in JSON format
|
||||
* @param session
|
||||
*/
|
||||
void getStats(const std::shared_ptr<restbed::Session>& session) const;
|
||||
|
||||
/**
|
||||
* Return Values of an infoHash
|
||||
* Method: GET "/{InfoHash: .*}"
|
||||
@@ -96,7 +162,7 @@ private:
|
||||
* On error: HTTP 503, body: {"err":"xxxx"}
|
||||
* @param session
|
||||
*/
|
||||
void listen(const std::shared_ptr<restbed::Session>& session) const;
|
||||
void listen(const std::shared_ptr<restbed::Session>& session);
|
||||
|
||||
/**
|
||||
* Put a value on the DHT
|
||||
@@ -107,9 +173,11 @@ private:
|
||||
* 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;
|
||||
void put(const std::shared_ptr<restbed::Session>& session);
|
||||
|
||||
#if OPENDHT_PROXY_SERVER_IDENTITY
|
||||
void cancelPut(const InfoHash& key, Value::Id vid);
|
||||
|
||||
#ifdef OPENDHT_PROXY_SERVER_IDENTITY
|
||||
/**
|
||||
* Put a value to sign by the proxy on the DHT
|
||||
* Method: SIGN "/{InfoHash: .*}"
|
||||
@@ -154,42 +222,66 @@ private:
|
||||
*/
|
||||
void handleOptionsMethod(const std::shared_ptr<restbed::Session>& session) const;
|
||||
|
||||
#if OPENDHT_PUSH_NOTIFICATIONS
|
||||
/**
|
||||
* Remove finished listeners
|
||||
* @param testSession if we remove the listener only if the session is closed
|
||||
*/
|
||||
void removeClosedListeners(bool testSession = true);
|
||||
|
||||
#ifdef 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)}"
|
||||
* Body: {"key": "device_key", (optional)"isAndroid":false (default true)}"
|
||||
* Return: {"token": x}" where x if a token to save
|
||||
* @note: the listen will timeout after six hours (and send a push notification).
|
||||
* so you need to refresh the operation each six hours.
|
||||
* @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;
|
||||
void subscribe(const std::shared_ptr<restbed::Session>& session);
|
||||
/**
|
||||
* Unsubscribe to push notifications for an iOS or Android device.
|
||||
* Method: UNSUBSCRIBE "/{InfoHash: .*}"
|
||||
* Body: {"key": "device_key", "token": x, (optional)"callback_id":y"
|
||||
* where x if the token to cancel
|
||||
* Body: {"key": "device_key", "token": x} 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;
|
||||
void unsubscribe(const std::shared_ptr<restbed::Session>& session);
|
||||
/**
|
||||
* Send a push notification via a gorush push gateway
|
||||
* @param key of the device
|
||||
* @param json, the content to send
|
||||
*/
|
||||
void sendPushNotification(const std::string& key, const Json::Value& json, bool isAndroid) const;
|
||||
void sendPushNotification(const std::string& key, Json::Value&& json, bool isAndroid) const;
|
||||
|
||||
/**
|
||||
* Remove a push listener between a client and a hash
|
||||
* @param pushToken
|
||||
* @param key
|
||||
* @param clientId
|
||||
*/
|
||||
void cancelPushListen(const std::string& pushToken, const InfoHash& key, const std::string& clientId);
|
||||
|
||||
|
||||
#endif //OPENDHT_PUSH_NOTIFICATIONS
|
||||
|
||||
using clock = std::chrono::steady_clock;
|
||||
using time_point = clock::time_point;
|
||||
|
||||
std::thread server_thread {};
|
||||
std::unique_ptr<restbed::Service> service_;
|
||||
std::shared_ptr<DhtRunner> dht_;
|
||||
Json::StreamWriterBuilder jsonBuilder_;
|
||||
|
||||
std::mutex schedulerLock_;
|
||||
std::condition_variable schedulerCv_;
|
||||
Scheduler scheduler_;
|
||||
std::thread schedulerThread_;
|
||||
std::unique_ptr<ThreadPool> threadPool_;
|
||||
|
||||
Sp<Scheduler::Job> printStatsJob_;
|
||||
mutable std::mutex statsMutex_;
|
||||
mutable NodeInfo nodeInfo_ {};
|
||||
|
||||
// Handle client quit for listen.
|
||||
// NOTE: can be simplified when we will supports restbed 5.0
|
||||
@@ -199,39 +291,28 @@ private:
|
||||
InfoHash hash;
|
||||
std::future<size_t> token;
|
||||
};
|
||||
mutable std::vector<SessionToHashToken> currentListeners_;
|
||||
mutable std::mutex lockListener_;
|
||||
std::vector<SessionToHashToken> currentListeners_;
|
||||
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
|
||||
struct PermanentPut;
|
||||
struct SearchPuts;
|
||||
std::map<InfoHash, SearchPuts> puts_;
|
||||
|
||||
mutable std::atomic<size_t> requestNum_ {0};
|
||||
mutable std::atomic<time_point> lastStatsReset_ {time_point::min()};
|
||||
|
||||
const std::string pushServer_;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
mutable ServerStats stats_;
|
||||
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
struct Listener;
|
||||
struct PushListener;
|
||||
std::mutex lockPushListeners_;
|
||||
std::map<std::string, PushListener> pushListeners_;
|
||||
proxy::ListenToken tokenPushNotif_ {0};
|
||||
#endif //OPENDHT_PUSH_NOTIFICATIONS
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
+65
-36
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
@@ -63,20 +63,20 @@ public:
|
||||
DhtRunner();
|
||||
virtual ~DhtRunner();
|
||||
|
||||
void get(InfoHash id, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter f = Value::AllFilter(), Where w = {}) {
|
||||
void get(InfoHash id, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter f = {}, Where w = {}) {
|
||||
get(id, bindGetCb(cb), donecb, f, w);
|
||||
}
|
||||
|
||||
void get(InfoHash id, GetCallbackSimple cb, DoneCallbackSimple donecb={}, Value::Filter f = Value::AllFilter(), Where w = {}) {
|
||||
void get(InfoHash id, GetCallbackSimple cb, DoneCallbackSimple donecb={}, Value::Filter f = {}, Where w = {}) {
|
||||
get(id, bindGetCb(cb), donecb, f, w);
|
||||
}
|
||||
|
||||
void get(InfoHash hash, GetCallback vcb, DoneCallback dcb, Value::Filter f={}, Where w = {});
|
||||
|
||||
void get(InfoHash id, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter f = Value::AllFilter(), Where w = {}) {
|
||||
void get(InfoHash id, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter f = {}, Where w = {}) {
|
||||
get(id, cb, bindDoneCb(donecb), f, w);
|
||||
}
|
||||
void get(const std::string& key, GetCallback vcb, DoneCallbackSimple dcb={}, Value::Filter f = Value::AllFilter(), Where w = {});
|
||||
void get(const std::string& key, GetCallback vcb, DoneCallbackSimple dcb={}, Value::Filter f = {}, Where w = {});
|
||||
|
||||
template <class T>
|
||||
void get(InfoHash hash, std::function<bool(std::vector<T>&&)> cb, DoneCallbackSimple dcb={})
|
||||
@@ -136,7 +136,15 @@ public:
|
||||
query(hash, cb, bindDoneCb(done_cb), q);
|
||||
}
|
||||
|
||||
std::future<size_t> listen(InfoHash key, GetCallback vcb, Value::Filter f = Value::AllFilter(), Where w = {});
|
||||
std::future<size_t> listen(InfoHash key, ValueCallback vcb, Value::Filter f = Value::AllFilter(), Where w = {});
|
||||
|
||||
std::future<size_t> listen(InfoHash key, GetCallback cb, Value::Filter f={}, Where w={}) {
|
||||
return listen(key, [cb](const std::vector<Sp<Value>>& vals, bool expired){
|
||||
if (not expired)
|
||||
return cb(vals);
|
||||
return true;
|
||||
}, std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
std::future<size_t> listen(const std::string& key, GetCallback vcb, Value::Filter f = Value::AllFilter(), Where w = {});
|
||||
std::future<size_t> listen(InfoHash key, GetCallbackSimple cb, Value::Filter f = Value::AllFilter(), Where w = {}) {
|
||||
return listen(key, bindGetCb(cb), f, w);
|
||||
@@ -150,6 +158,15 @@ public:
|
||||
},
|
||||
getFilterSet<T>());
|
||||
}
|
||||
template <class T>
|
||||
std::future<size_t> listen(InfoHash hash, std::function<bool(std::vector<T>&&, bool)> cb)
|
||||
{
|
||||
return listen(hash, [=](const std::vector<std::shared_ptr<Value>>& vals, bool expired) {
|
||||
return cb(unpackVector<T>(vals), expired);
|
||||
},
|
||||
getFilterSet<T>());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::future<size_t> listen(InfoHash hash, std::function<bool(T&&)> cb, Value::Filter f = Value::AllFilter(), Where w = {})
|
||||
{
|
||||
@@ -166,6 +183,22 @@ public:
|
||||
},
|
||||
getFilterSet<T>(f), w);
|
||||
}
|
||||
template <typename T>
|
||||
std::future<size_t> listen(InfoHash hash, std::function<bool(T&&, bool)> cb, Value::Filter f = Value::AllFilter(), Where w = {})
|
||||
{
|
||||
return listen(hash, [=](const std::vector<std::shared_ptr<Value>>& vals, bool expired) {
|
||||
for (const auto& v : vals) {
|
||||
try {
|
||||
if (not cb(Value::unpack<T>(*v), expired))
|
||||
return false;
|
||||
} catch (const std::exception&) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
getFilterSet<T>(f), w);
|
||||
}
|
||||
|
||||
void cancelListen(InfoHash h, size_t token);
|
||||
void cancelListen(InfoHash h, std::shared_future<size_t> token);
|
||||
@@ -240,8 +273,14 @@ public:
|
||||
|
||||
void dumpTables() const;
|
||||
|
||||
/**
|
||||
* Get the public key fingerprint if an identity is used with this node, 0 otherwise.
|
||||
*/
|
||||
InfoHash getId() const;
|
||||
|
||||
/**
|
||||
* Get the ID of the DHT node.
|
||||
*/
|
||||
InfoHash getNodeId() const;
|
||||
|
||||
/**
|
||||
@@ -285,6 +324,7 @@ public:
|
||||
|
||||
NodeStats getNodesStats(sa_family_t af) const;
|
||||
unsigned getNodesStats(sa_family_t af, unsigned *good_return, unsigned *dubious_return, unsigned *cached_return, unsigned *incoming_return) const;
|
||||
NodeInfo getNodeInfo() const;
|
||||
|
||||
std::vector<unsigned> getNodeMessageStats(bool in = false) const;
|
||||
std::string getStorageLog() const;
|
||||
@@ -307,14 +347,15 @@ public:
|
||||
* @param threaded: If false, ::loop() must be called periodically. Otherwise a thread is launched.
|
||||
* @param cb: Optional callback to receive general state information.
|
||||
*/
|
||||
void run(in_port_t port, const crypto::Identity identity, bool threaded = false, NetId network = 0) {
|
||||
void run(in_port_t port = 4222, const crypto::Identity identity = {}, bool threaded = false, NetId network = 0) {
|
||||
run(port, {
|
||||
/*.dht_config = */{
|
||||
/*.node_config = */{
|
||||
/*.node_id = */{},
|
||||
/*.network = */network,
|
||||
/*.is_bootstrap = */false,
|
||||
/*.maintain_storage*/false
|
||||
/*.maintain_storage = */false,
|
||||
/*.persist_path = */{}
|
||||
},
|
||||
/*.id = */identity
|
||||
},
|
||||
@@ -323,7 +364,7 @@ public:
|
||||
/*.push_node_id = */""
|
||||
});
|
||||
}
|
||||
void run(in_port_t port, Config config);
|
||||
void run(in_port_t port, const Config& config);
|
||||
|
||||
/**
|
||||
* @param local4: Local IPv4 address and port to bind. Can be null.
|
||||
@@ -333,12 +374,12 @@ public:
|
||||
* @param threaded: If false, loop() must be called periodically. Otherwise a thread is launched.
|
||||
* @param cb: Optional callback to receive general state information.
|
||||
*/
|
||||
void run(const SockAddr& local4, const SockAddr& local6, Config config);
|
||||
void run(const SockAddr& local4, const SockAddr& local6, const Config& config);
|
||||
|
||||
/**
|
||||
* Same as @run(sockaddr_in, sockaddr_in6, Identity, bool, StatusCallback), but with string IP addresses and service (port).
|
||||
*/
|
||||
void run(const char* ip4, const char* ip6, const char* service, Config config);
|
||||
void run(const char* ip4, const char* ip6, const char* service, const Config& config);
|
||||
|
||||
void setOnStatusChanged(StatusCallback&& cb) {
|
||||
statusCb = std::move(cb);
|
||||
@@ -372,15 +413,7 @@ 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
|
||||
}
|
||||
void setProxyServer(const std::string& proxy, const std::string& pushNodeId = "");
|
||||
|
||||
/**
|
||||
* Start or stop the proxy
|
||||
@@ -399,12 +432,7 @@ public:
|
||||
/**
|
||||
* 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);
|
||||
void pushNotificationReceived(const std::map<std::string, std::string>& data);
|
||||
|
||||
/* Proxy server mothods */
|
||||
void forwardAllMessages(bool forward);
|
||||
@@ -420,6 +448,7 @@ private:
|
||||
*/
|
||||
void tryBootstrapContinuously();
|
||||
|
||||
void stopNetwork();
|
||||
void startNetwork(const SockAddr sin4, const SockAddr sin6);
|
||||
time_point loop_();
|
||||
|
||||
@@ -451,15 +480,8 @@ private:
|
||||
/**
|
||||
* 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_ {};
|
||||
struct Listener;
|
||||
std::map<size_t, Listener> listeners_;
|
||||
size_t listener_token_ {1};
|
||||
|
||||
mutable std::mutex dht_mtx {};
|
||||
@@ -468,7 +490,13 @@ private:
|
||||
|
||||
std::thread rcv_thread {};
|
||||
std::mutex sock_mtx {};
|
||||
std::vector<std::pair<Blob, SockAddr>> rcv {};
|
||||
|
||||
struct ReceivedPacket {
|
||||
Blob data;
|
||||
SockAddr from;
|
||||
time_point received;
|
||||
};
|
||||
std::queue<ReceivedPacket> rcv {};
|
||||
|
||||
/** true if currently actively boostraping */
|
||||
std::atomic_bool bootstraping {false};
|
||||
@@ -491,6 +519,7 @@ private:
|
||||
status6 {NodeStatus::Disconnected};
|
||||
StatusCallback statusCb {nullptr};
|
||||
|
||||
int stop_writefd {-1};
|
||||
int s4 {-1}, s6 {-1};
|
||||
SockAddr bound4 {};
|
||||
SockAddr bound6 {};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Nicolas Reynaud <nicolas.reynaud@savoirfairelinux.com>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -129,7 +129,7 @@ public:
|
||||
* Find the lowest 1 bit in an id.
|
||||
* Result will allways be lower than 8*N
|
||||
*/
|
||||
inline unsigned lowbit() const {
|
||||
inline int lowbit() const {
|
||||
int i, j;
|
||||
for(i = N-1; i >= 0; i--)
|
||||
if(data_[i] != 0)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2016-2019 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -43,8 +43,23 @@ inline void NOLOG(char const*, va_list) {}
|
||||
struct LogMethod {
|
||||
LogMethod() = default;
|
||||
|
||||
LogMethod(LogMethod&& l) : func(std::move(l.func)) {}
|
||||
LogMethod(const LogMethod& l) : func(l.func) {}
|
||||
|
||||
LogMethod& operator=(dht::LogMethod&& l) {
|
||||
func = std::forward<LogMethod>(l.func);
|
||||
return *this;
|
||||
}
|
||||
LogMethod& operator=(const dht::LogMethod& l) {
|
||||
func = l.func;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
LogMethod(T&& t) : func(std::forward<T>(t)) {}
|
||||
explicit LogMethod(T&& t) : func(std::forward<T>(t)) {}
|
||||
|
||||
template<typename T>
|
||||
LogMethod(const T& t) : func(t) {}
|
||||
|
||||
void operator()(char const* format, ...) const {
|
||||
va_list args;
|
||||
@@ -70,7 +85,7 @@ private:
|
||||
};
|
||||
|
||||
struct Logger {
|
||||
LogMethod DEBUG = NOLOG;
|
||||
LogMethod DBG = NOLOG;
|
||||
LogMethod WARN = NOLOG;
|
||||
LogMethod ERR = NOLOG;
|
||||
void setFilter(const InfoHash& f) {
|
||||
@@ -99,7 +114,7 @@ struct Logger {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log0(DEBUG, format, args);
|
||||
log0(DBG, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
@@ -107,7 +122,7 @@ struct Logger {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log1(DEBUG, f, format, args);
|
||||
log1(DBG, f, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
@@ -115,7 +130,7 @@ struct Logger {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log2(DEBUG, f1, f2, format, args);
|
||||
log2(DBG, f1, f2, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
@@ -87,6 +87,8 @@ struct RequestAnswer {
|
||||
Blob ntoken {};
|
||||
Value::Id vid {};
|
||||
std::vector<Sp<Value>> values {};
|
||||
std::vector<Value::Id> refreshed_values {};
|
||||
std::vector<Value::Id> expired_values {};
|
||||
std::vector<Sp<FieldValueIndex>> fields {};
|
||||
std::vector<Sp<Node>> nodes4 {};
|
||||
std::vector<Sp<Node>> nodes6 {};
|
||||
@@ -204,15 +206,15 @@ public:
|
||||
|
||||
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,
|
||||
decltype(NetworkEngine::onPing) onPing,
|
||||
decltype(NetworkEngine::onFindNode) onFindNode,
|
||||
decltype(NetworkEngine::onGetValues) onGetValues,
|
||||
decltype(NetworkEngine::onListen) onListen,
|
||||
decltype(NetworkEngine::onAnnounce) onAnnounce,
|
||||
decltype(NetworkEngine::onRefresh) onRefresh);
|
||||
decltype(NetworkEngine::onError)&& onError,
|
||||
decltype(NetworkEngine::onNewNode)&& onNewNode,
|
||||
decltype(NetworkEngine::onReportedAddr)&& onReportedAddr,
|
||||
decltype(NetworkEngine::onPing)&& onPing,
|
||||
decltype(NetworkEngine::onFindNode)&& onFindNode,
|
||||
decltype(NetworkEngine::onGetValues)&& onGetValues,
|
||||
decltype(NetworkEngine::onListen)&& onListen,
|
||||
decltype(NetworkEngine::onAnnounce)&& onAnnounce,
|
||||
decltype(NetworkEngine::onRefresh)&& onRefresh);
|
||||
|
||||
virtual ~NetworkEngine();
|
||||
|
||||
@@ -235,6 +237,9 @@ public:
|
||||
std::vector<Sp<Node>>&& nodes, std::vector<Sp<Node>>&& nodes6,
|
||||
std::vector<Sp<Value>>&& values, const Query& q);
|
||||
|
||||
void tellListenerRefreshed(Sp<Node> n, Tid socket_id, const InfoHash& hash, const Blob& ntoken, const std::vector<Value::Id>& values);
|
||||
void tellListenerExpired(Sp<Node> n, Tid socket_id, const InfoHash& hash, const Blob& ntoken, const std::vector<Value::Id>& values);
|
||||
|
||||
bool isRunning(sa_family_t af) const;
|
||||
inline want_t want () const { return dht_socket >= 0 && dht_socket6 >= 0 ? (WANT4 | WANT6) : -1; }
|
||||
|
||||
@@ -285,9 +290,9 @@ public:
|
||||
*/
|
||||
Sp<Request> sendFindNode(Sp<Node> n,
|
||||
const InfoHash& hash,
|
||||
want_t want,
|
||||
RequestCb&& on_done,
|
||||
RequestExpiredCb&& on_expired);
|
||||
want_t want = -1,
|
||||
RequestCb&& on_done = {},
|
||||
RequestExpiredCb&& on_expired = {});
|
||||
/**
|
||||
* Send a "get" request to a given node.
|
||||
*
|
||||
@@ -526,6 +531,8 @@ private:
|
||||
std::set<SockAddr> blacklist {};
|
||||
|
||||
Scheduler& scheduler;
|
||||
|
||||
bool logIncoming_ {false};
|
||||
};
|
||||
|
||||
} /* namespace net */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 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
|
||||
@@ -40,12 +40,16 @@ struct NodeCache {
|
||||
~NodeCache();
|
||||
|
||||
private:
|
||||
class NodeMap : public std::map<InfoHash, std::weak_ptr<Node>> {
|
||||
class NodeMap : private std::map<InfoHash, std::weak_ptr<Node>> {
|
||||
public:
|
||||
Sp<Node> getNode(const InfoHash& id);
|
||||
Sp<Node> getNode(const InfoHash& id, const SockAddr&, time_point now, bool confirmed, bool client);
|
||||
std::vector<Sp<Node>> getCachedNodes(const InfoHash& id, size_t count) const;
|
||||
void clearBadNodes();
|
||||
void setExpired();
|
||||
void cleanup();
|
||||
private:
|
||||
size_t cleanup_counter {0};
|
||||
};
|
||||
|
||||
const NodeMap& cache(sa_family_t af) const { return af == AF_INET ? cache_4 : cache_6; }
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2019 Savoir-faire Linux Inc.
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace dht {
|
||||
namespace proxy {
|
||||
|
||||
constexpr const std::chrono::seconds OP_TIMEOUT {1 * 60 * 60}; // one hour
|
||||
constexpr const std::chrono::seconds OP_MARGIN {5 * 60}; // 5 minutes
|
||||
constexpr const char* const HTTP_PROTO {"http://"};
|
||||
using ListenToken = uint64_t;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2017-2019 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -25,7 +25,7 @@
|
||||
namespace dht {
|
||||
namespace crypto {
|
||||
|
||||
#ifndef WIN32_NATIVE
|
||||
#ifndef _MSC_VER
|
||||
#ifdef _WIN32
|
||||
|
||||
/**
|
||||
@@ -43,8 +43,8 @@ public:
|
||||
* result_type is unsigned int so this is currently safe.
|
||||
*/
|
||||
static_assert(
|
||||
sizeof(result_type) == 2 ||
|
||||
sizeof(result_type) == 4 ||
|
||||
sizeof(result_type) == 2 ||
|
||||
sizeof(result_type) == 4 ||
|
||||
sizeof(result_type) == 8,
|
||||
"result_type must be 16, 32 or 64 bits");
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
@@ -42,6 +42,11 @@ struct Bucket {
|
||||
Sp<Node> randomNode();
|
||||
|
||||
void sendCachedPing(net::NetworkEngine& ne);
|
||||
void connectivityChanged() {
|
||||
time = time_point::min();
|
||||
for (auto& node : nodes)
|
||||
node->setTime(time_point::min());
|
||||
}
|
||||
};
|
||||
|
||||
class RoutingTable : public std::list<Bucket> {
|
||||
@@ -76,7 +81,7 @@ public:
|
||||
void connectivityChanged(const time_point& now) {
|
||||
grow_time = now;
|
||||
for (auto& b : *this)
|
||||
b.time = time_point::min();
|
||||
b.connectivityChanged();
|
||||
}
|
||||
|
||||
bool onNewNode(const Sp<Node>& node, int comfirm, const time_point& now, const InfoHash& myid, net::NetworkEngine& ne);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
@@ -36,11 +36,10 @@ namespace dht {
|
||||
*/
|
||||
class Scheduler {
|
||||
public:
|
||||
Scheduler(const Logger& l) : DHT_LOG(l) {}
|
||||
|
||||
struct Job {
|
||||
Job(std::function<void()>&& f) : do_(std::move(f)) {}
|
||||
std::function<void()> do_;
|
||||
void cancel() { do_ = {}; }
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -58,17 +57,19 @@ public:
|
||||
return job;
|
||||
}
|
||||
|
||||
void add(const Sp<Scheduler::Job>& job, time_point t) {
|
||||
if (t != time_point::max())
|
||||
timers.emplace(std::move(t), job);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reschedules a job.
|
||||
*
|
||||
* @param time The time at which the job shall be rescheduled.
|
||||
* @param job The job to edit.
|
||||
*
|
||||
* @return pointer to the newly scheduled job.
|
||||
* @param t The time at which the job shall be rescheduled.
|
||||
*/
|
||||
void edit(Sp<Scheduler::Job>& job, time_point t) {
|
||||
if (not job) {
|
||||
DHT_LOG.ERR("editing an empty job");
|
||||
return;
|
||||
}
|
||||
// std::function move doesn't garantee to leave the object empty.
|
||||
@@ -118,7 +119,6 @@ public:
|
||||
private:
|
||||
time_point now {clock::now()};
|
||||
std::multimap<time_point, Sp<Job>> timers {}; /* the jobs ordered by time */
|
||||
const Logger& DHT_LOG;
|
||||
};
|
||||
|
||||
}
|
||||
+65
-72
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
@@ -37,9 +37,9 @@ public:
|
||||
|
||||
using Config = SecureDhtConfig;
|
||||
|
||||
static dht::Config& getConfig(SecureDht::Config& conf)
|
||||
static dht::Config getConfig(const SecureDht::Config& conf)
|
||||
{
|
||||
auto& c = conf.node_config;
|
||||
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;
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
return secureType(std::move(tmp_type));
|
||||
}
|
||||
|
||||
void registerType(const ValueType& type) {
|
||||
void registerType(const ValueType& type) override {
|
||||
if (dht_)
|
||||
dht_->registerType(secureType(type));
|
||||
}
|
||||
@@ -89,19 +89,17 @@ public:
|
||||
* If the signature can't be checked, or if the data can't be decrypted, it is not returned.
|
||||
* Public, non-signed & non-encrypted data is retransmitted as-is.
|
||||
*/
|
||||
void get(const InfoHash& id, GetCallback cb, DoneCallback donecb={}, Value::Filter&& = {}, Where&& w = {});
|
||||
void get(const InfoHash& id, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f = {}, Where&& w = {}) {
|
||||
void get(const InfoHash& id, GetCallback cb, DoneCallback donecb={}, Value::Filter&& = {}, Where&& w = {}) override;
|
||||
void get(const InfoHash& id, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f = {}, Where&& w = {}) override {
|
||||
get(id, cb, bindDoneCb(donecb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) {
|
||||
void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) override {
|
||||
get(key, bindGetCb(cb), donecb, std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}, Where&& w = {}) {
|
||||
void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}, Where&& w = {}) override {
|
||||
get(key, bindGetCb(cb), bindDoneCb(donecb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
@@ -129,8 +127,8 @@ public:
|
||||
|
||||
Value decrypt(const Value& v);
|
||||
|
||||
void findCertificate(const InfoHash& node, std::function<void(const Sp<crypto::Certificate>)> cb);
|
||||
void findPublicKey(const InfoHash& node, std::function<void(const Sp<const crypto::PublicKey>)> cb);
|
||||
void findCertificate(const InfoHash& node, const std::function<void(const Sp<crypto::Certificate>)>& cb);
|
||||
void findPublicKey(const InfoHash& node, const std::function<void(const Sp<const crypto::PublicKey>)>& cb);
|
||||
|
||||
const Sp<crypto::Certificate> registerCertificate(const InfoHash& node, const Blob& cert);
|
||||
void registerCertificate(Sp<crypto::Certificate>& cert);
|
||||
@@ -150,99 +148,100 @@ public:
|
||||
/**
|
||||
* SecureDht to Dht proxy
|
||||
*/
|
||||
void shutdown(ShutdownCallback cb) {
|
||||
void shutdown(ShutdownCallback cb) override {
|
||||
dht_->shutdown(cb);
|
||||
}
|
||||
void dumpTables() const {
|
||||
void dumpTables() const override {
|
||||
dht_->dumpTables();
|
||||
}
|
||||
inline const InfoHash& getNodeId() const { return dht_->getNodeId(); }
|
||||
std::pair<size_t, size_t> getStoreSize() const {
|
||||
inline const InfoHash& getNodeId() const override { return dht_->getNodeId(); }
|
||||
|
||||
std::pair<size_t, size_t> getStoreSize() const override {
|
||||
return dht_->getStoreSize();
|
||||
}
|
||||
std::string getStorageLog() const {
|
||||
std::string getStorageLog() const override {
|
||||
return dht_->getStorageLog();
|
||||
}
|
||||
std::string getStorageLog(const InfoHash& h) const {
|
||||
std::string getStorageLog(const InfoHash& h) const override {
|
||||
return dht_->getStorageLog(h);
|
||||
}
|
||||
void setStorageLimit(size_t limit = DEFAULT_STORAGE_LIMIT) {
|
||||
void setStorageLimit(size_t limit = DEFAULT_STORAGE_LIMIT) override {
|
||||
dht_->setStorageLimit(limit);
|
||||
}
|
||||
std::vector<NodeExport> exportNodes() {
|
||||
std::vector<NodeExport> exportNodes() const override {
|
||||
return dht_->exportNodes();
|
||||
}
|
||||
std::vector<ValuesExport> exportValues() const {
|
||||
std::vector<ValuesExport> exportValues() const override {
|
||||
return dht_->exportValues();
|
||||
}
|
||||
void importValues(const std::vector<ValuesExport>& v) {
|
||||
void importValues(const std::vector<ValuesExport>& v) override {
|
||||
dht_->importValues(v);
|
||||
}
|
||||
NodeStats getNodesStats(sa_family_t af) const {
|
||||
NodeStats getNodesStats(sa_family_t af) const override {
|
||||
return dht_->getNodesStats(af);
|
||||
}
|
||||
std::vector<unsigned> getNodeMessageStats(bool in = false) {
|
||||
std::vector<unsigned> getNodeMessageStats(bool in = false) override {
|
||||
return dht_->getNodeMessageStats(in);
|
||||
}
|
||||
std::string getRoutingTablesLog(sa_family_t af) const {
|
||||
std::string getRoutingTablesLog(sa_family_t af) const override {
|
||||
return dht_->getRoutingTablesLog(af);
|
||||
}
|
||||
std::string getSearchesLog(sa_family_t af) const {
|
||||
std::string getSearchesLog(sa_family_t af) const override {
|
||||
return dht_->getSearchesLog(af);
|
||||
}
|
||||
std::string getSearchLog(const InfoHash& h, sa_family_t af = AF_UNSPEC) const {
|
||||
std::string getSearchLog(const InfoHash& h, sa_family_t af = AF_UNSPEC) const override {
|
||||
return dht_->getSearchLog(h, af);
|
||||
}
|
||||
std::vector<SockAddr> getPublicAddress(sa_family_t family = 0) {
|
||||
std::vector<SockAddr> getPublicAddress(sa_family_t family = 0) override {
|
||||
return dht_->getPublicAddress(family);
|
||||
}
|
||||
time_point periodic(const uint8_t *buf, size_t buflen, const SockAddr& sa) {
|
||||
time_point periodic(const uint8_t *buf, size_t buflen, const SockAddr& sa) override {
|
||||
return dht_->periodic(buf, buflen, sa);
|
||||
}
|
||||
time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen) {
|
||||
time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen) override {
|
||||
return dht_->periodic(buf, buflen, from, fromlen);
|
||||
}
|
||||
NodeStatus getStatus(sa_family_t af) const {
|
||||
NodeStatus getStatus(sa_family_t af) const override {
|
||||
return dht_->getStatus(af);
|
||||
}
|
||||
NodeStatus getStatus() const {
|
||||
NodeStatus getStatus() const override {
|
||||
return dht_->getStatus();
|
||||
}
|
||||
bool isRunning(sa_family_t af = 0) const {
|
||||
bool isRunning(sa_family_t af = 0) const override {
|
||||
return dht_->isRunning(af);
|
||||
}
|
||||
const ValueType& getType(ValueType::Id type_id) const {
|
||||
const ValueType& getType(ValueType::Id type_id) const override {
|
||||
return dht_->getType(type_id);
|
||||
}
|
||||
void insertNode(const InfoHash& id, const SockAddr& sa) {
|
||||
void insertNode(const InfoHash& id, const SockAddr& sa) override {
|
||||
dht_->insertNode(id, sa);
|
||||
}
|
||||
void insertNode(const InfoHash& id, const sockaddr* sa, socklen_t salen) {
|
||||
void insertNode(const InfoHash& id, const sockaddr* sa, socklen_t salen) override {
|
||||
dht_->insertNode(id, sa, salen);
|
||||
}
|
||||
void insertNode(const NodeExport& n) {
|
||||
void insertNode(const NodeExport& n) override {
|
||||
dht_->insertNode(n);
|
||||
}
|
||||
void pingNode(const sockaddr* sa, socklen_t salen, DoneCallbackSimple&& cb={}) {
|
||||
void pingNode(const sockaddr* sa, socklen_t salen, DoneCallbackSimple&& cb={}) override {
|
||||
dht_->pingNode(sa, salen, std::move(cb));
|
||||
}
|
||||
void query(const InfoHash& key, QueryCallback cb, DoneCallback done_cb = {}, Query&& q = {}) {
|
||||
void query(const InfoHash& key, QueryCallback cb, DoneCallback done_cb = {}, Query&& q = {}) override {
|
||||
dht_->query(key, cb, done_cb, std::move(q));
|
||||
}
|
||||
void query(const InfoHash& key, QueryCallback cb, DoneCallbackSimple done_cb = {}, Query&& q = {}) {
|
||||
void query(const InfoHash& key, QueryCallback cb, DoneCallbackSimple done_cb = {}, Query&& q = {}) override {
|
||||
dht_->query(key, cb, done_cb, std::move(q));
|
||||
}
|
||||
std::vector<Sp<Value>> getLocal(const InfoHash& key, Value::Filter f = Value::AllFilter()) const {
|
||||
std::vector<Sp<Value>> getLocal(const InfoHash& key, Value::Filter f = Value::AllFilter()) const override {
|
||||
return dht_->getLocal(key, f);
|
||||
}
|
||||
Sp<Value> getLocalById(const InfoHash& key, Value::Id vid) const {
|
||||
Sp<Value> getLocalById(const InfoHash& key, Value::Id vid) const override {
|
||||
return dht_->getLocalById(key, vid);
|
||||
}
|
||||
void put(const InfoHash& key,
|
||||
Sp<Value> v,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false)
|
||||
bool permanent = false) override
|
||||
{
|
||||
dht_->put(key, v, cb, created, permanent);
|
||||
}
|
||||
@@ -250,7 +249,7 @@ public:
|
||||
const Sp<Value>& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false)
|
||||
bool permanent = false) override
|
||||
{
|
||||
dht_->put(key, v, cb, created, permanent);
|
||||
}
|
||||
@@ -259,7 +258,7 @@ public:
|
||||
Value&& v,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false)
|
||||
bool permanent = false) override
|
||||
{
|
||||
dht_->put(key, std::move(v), cb, created, permanent);
|
||||
}
|
||||
@@ -267,29 +266,32 @@ public:
|
||||
Value&& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false)
|
||||
bool permanent = false) override
|
||||
{
|
||||
dht_->put(key, std::move(v), cb, created, permanent);
|
||||
}
|
||||
std::vector<Sp<Value>> getPut(const InfoHash& h) {
|
||||
std::vector<Sp<Value>> getPut(const InfoHash& h) override {
|
||||
return dht_->getPut(h);
|
||||
}
|
||||
Sp<Value> getPut(const InfoHash& h, const Value::Id& vid) {
|
||||
Sp<Value> getPut(const InfoHash& h, const Value::Id& vid) override {
|
||||
return dht_->getPut(h, vid);
|
||||
}
|
||||
bool cancelPut(const InfoHash& h, const Value::Id& vid) {
|
||||
bool cancelPut(const InfoHash& h, const Value::Id& vid) override {
|
||||
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);
|
||||
|
||||
size_t listen(const InfoHash& key, ValueCallback, Value::Filter={}, Where={}) override;
|
||||
size_t listen(const InfoHash& key, GetCallback cb, Value::Filter = {}, Where w = {}) override;
|
||||
size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w = {}) override {
|
||||
return listen(key, bindGetCb(cb), f, w);
|
||||
}
|
||||
bool cancelListen(const InfoHash& h, size_t token) {
|
||||
bool cancelListen(const InfoHash& h, size_t token) override {
|
||||
return dht_->cancelListen(h, token);
|
||||
}
|
||||
void connectivityChanged(sa_family_t af) {
|
||||
void connectivityChanged(sa_family_t af) override {
|
||||
dht_->connectivityChanged(af);
|
||||
}
|
||||
void connectivityChanged() {
|
||||
void connectivityChanged() override {
|
||||
dht_->connectivityChanged();
|
||||
}
|
||||
|
||||
@@ -297,7 +299,7 @@ public:
|
||||
forward_all_ = forward;
|
||||
}
|
||||
|
||||
void setPushNotificationToken(const std::string& token = "") {
|
||||
void setPushNotificationToken(const std::string& token = "") override {
|
||||
dht_->setPushNotificationToken(token);
|
||||
}
|
||||
|
||||
@@ -305,29 +307,20 @@ public:
|
||||
* Call linked callback with push_notification
|
||||
* @param notification to process
|
||||
*/
|
||||
void pushNotificationReceived(const std::map<std::string, std::string>& notification) {
|
||||
void pushNotificationReceived(const std::map<std::string, std::string>& notification) override {
|
||||
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)
|
||||
void setLoggers(LogMethod error = NOLOG, LogMethod warn = NOLOG, LogMethod debug = NOLOG) override
|
||||
{
|
||||
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));
|
||||
DhtInterface::setLoggers(error, warn, debug);
|
||||
dht_->setLoggers(error, warn, debug);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only print logs related to the given InfoHash (if given), or disable filter (if zeroes).
|
||||
*/
|
||||
void setLogFilter(const InfoHash& f) {
|
||||
void setLogFilter(const InfoHash& f) override {
|
||||
DHT_LOG.setFilter(f);
|
||||
dht_->setLogFilter(f);
|
||||
}
|
||||
@@ -338,7 +331,9 @@ private:
|
||||
SecureDht(const SecureDht&) = delete;
|
||||
SecureDht& operator=(const SecureDht&) = delete;
|
||||
|
||||
GetCallback getCallbackFilter(GetCallback, Value::Filter&&);
|
||||
Sp<Value> checkValue(const Sp<Value>& v);
|
||||
ValueCallback getCallbackFilter(const ValueCallback&, Value::Filter&&);
|
||||
GetCallback getCallbackFilter(const GetCallback&, Value::Filter&&);
|
||||
|
||||
Sp<crypto::PrivateKey> key_ {};
|
||||
Sp<crypto::Certificate> certificate_ {};
|
||||
@@ -350,8 +345,6 @@ private:
|
||||
std::map<InfoHash, Sp<crypto::Certificate>> nodesCertificates_ {};
|
||||
std::map<InfoHash, Sp<const crypto::PublicKey>> nodesPubKeys_ {};
|
||||
|
||||
std::uniform_int_distribution<Value::Id> rand_id {};
|
||||
|
||||
std::atomic_bool forward_all_ {false};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2016-2019 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
SockAddr(const SockAddr& o) {
|
||||
set(o.get(), o.getLength());
|
||||
}
|
||||
SockAddr(SockAddr&& o) : len(o.len), addr(std::move(o.addr)) {
|
||||
SockAddr(SockAddr&& o) noexcept : len(o.len), addr(std::move(o.addr)) {
|
||||
o.len = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -54,6 +54,12 @@ void erase_if(std::map<Key, Item>& map, const Condition& condition)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Split "[host]:port" or "host:port" to pair<"host", "port">.
|
||||
*/
|
||||
OPENDHT_PUBLIC std::pair<std::string, std::string>
|
||||
splitPort(const std::string& s);
|
||||
|
||||
class OPENDHT_PUBLIC DhtException : public std::runtime_error {
|
||||
public:
|
||||
DhtException(const std::string &str = "") :
|
||||
|
||||
+63
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
@@ -37,9 +37,9 @@
|
||||
#include <chrono>
|
||||
#include <set>
|
||||
|
||||
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
#include <json/json.h>
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
#endif
|
||||
|
||||
namespace dht {
|
||||
|
||||
@@ -79,7 +79,7 @@ static constexpr const size_t MAX_VALUE_SIZE {1024 * 64};
|
||||
struct OPENDHT_PUBLIC ValueType {
|
||||
typedef uint16_t Id;
|
||||
|
||||
static bool DEFAULT_STORE_POLICY(InfoHash, std::shared_ptr<Value>& v, const InfoHash&, const SockAddr&);
|
||||
static bool DEFAULT_STORE_POLICY(InfoHash, const std::shared_ptr<Value>& v, const InfoHash&, const SockAddr&);
|
||||
static bool DEFAULT_EDIT_POLICY(InfoHash, const std::shared_ptr<Value>&, std::shared_ptr<Value>&, const InfoHash&, const SockAddr&) {
|
||||
return false;
|
||||
}
|
||||
@@ -109,6 +109,21 @@ struct OPENDHT_PUBLIC ValueType {
|
||||
EditPolicy editPolicy {DEFAULT_EDIT_POLICY};
|
||||
};
|
||||
|
||||
class TypeStore {
|
||||
public:
|
||||
void registerType(const ValueType& type) {
|
||||
types[type.id] = type;
|
||||
}
|
||||
const ValueType& getType(ValueType::Id type_id) const {
|
||||
const auto& t_it = types.find(type_id);
|
||||
return (t_it == types.end()) ? ValueType::USER_DATA : t_it->second;
|
||||
}
|
||||
private:
|
||||
std::map<ValueType::Id, ValueType> types {};
|
||||
};
|
||||
|
||||
struct CryptoValueCache;
|
||||
|
||||
/**
|
||||
* A "value" is data potentially stored on the Dht, with some metadata.
|
||||
*
|
||||
@@ -150,6 +165,13 @@ struct OPENDHT_PUBLIC Value
|
||||
return chainOr(std::move(f1), std::move(f2));
|
||||
}
|
||||
static Filter chain(Filter&& f1, Filter&& f2) {
|
||||
if (not f1) return std::move(f2);
|
||||
if (not f2) return std::move(f1);
|
||||
return [f1,f2](const Value& v) {
|
||||
return f1(v) and f2(v);
|
||||
};
|
||||
}
|
||||
static Filter chain(const Filter& f1, const Filter& f2) {
|
||||
if (not f1) return f2;
|
||||
if (not f2) return f1;
|
||||
return [f1,f2](const Value& v) {
|
||||
@@ -169,11 +191,24 @@ struct OPENDHT_PUBLIC Value
|
||||
return chainAll(std::vector<Filter>(l.begin(), l.end()));
|
||||
}
|
||||
static Filter chainOr(Filter&& f1, Filter&& f2) {
|
||||
if (not f1 or not f2) return AllFilter();
|
||||
if (not f1 or not f2) return {};
|
||||
return [f1,f2](const Value& v) {
|
||||
return f1(v) or f2(v);
|
||||
};
|
||||
}
|
||||
static Filter notFilter(Filter&& f) {
|
||||
if (not f) return [](const Value&) { return false; };
|
||||
return [f](const Value& v) { return not f(v); };
|
||||
}
|
||||
std::vector<Sp<Value>> filter(const std::vector<Sp<Value>>& values) {
|
||||
if (not (*this))
|
||||
return values;
|
||||
std::vector<Sp<Value>> ret;
|
||||
for (const auto& v : values)
|
||||
if ((*this)(v))
|
||||
ret.emplace_back(v);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
/* Sneaky functions disguised in classes */
|
||||
@@ -350,13 +385,13 @@ 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
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
/**
|
||||
* Build a value from a json object
|
||||
* @param json
|
||||
*/
|
||||
Value(Json::Value& json);
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
#endif
|
||||
|
||||
template <typename Type>
|
||||
Value(ValueType::Id t, const Type& d, Id id = INVALID_ID)
|
||||
@@ -429,7 +464,7 @@ struct OPENDHT_PUBLIC Value
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
/**
|
||||
* Build a json object from a value
|
||||
* Example:
|
||||
@@ -439,7 +474,7 @@ struct OPENDHT_PUBLIC Value
|
||||
* }
|
||||
*/
|
||||
Json::Value toJson() const;
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
#endif
|
||||
|
||||
/** Return the size in bytes used by this value in memory (minimum). */
|
||||
size_t size() const;
|
||||
@@ -566,6 +601,14 @@ struct OPENDHT_PUBLIC Value
|
||||
* Hold encrypted version of the data.
|
||||
*/
|
||||
Blob cypher {};
|
||||
|
||||
private:
|
||||
friend class SecureDht;
|
||||
/* Cache for crypto ops */
|
||||
bool signatureChecked {false};
|
||||
bool signatureValid {false};
|
||||
bool decrypted {false};
|
||||
Sp<Value> decryptedValue {};
|
||||
};
|
||||
|
||||
using ValuesExport = std::pair<InfoHash, Blob>;
|
||||
@@ -820,6 +863,10 @@ struct OPENDHT_PUBLIC Where
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return filters_.empty();
|
||||
}
|
||||
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<<(std::ostream& s, const dht::Where& q);
|
||||
|
||||
private:
|
||||
@@ -838,7 +885,7 @@ struct OPENDHT_PUBLIC Query
|
||||
{
|
||||
static const std::string QUERY_PARSE_ERROR;
|
||||
|
||||
Query(Select s = {}, Where w = {}, bool none = false) : select(s), where(w), none(none) { };
|
||||
Query(Select s = {}, Where w = {}, bool none = false) : select(std::move(s)), where(std::move(w)), none(none) { };
|
||||
|
||||
/**
|
||||
* Initializes a query based on a SQL-ish formatted string. The abstract
|
||||
@@ -900,7 +947,7 @@ struct OPENDHT_PUBLIC Query
|
||||
*/
|
||||
struct OPENDHT_PUBLIC FieldValueIndex {
|
||||
FieldValueIndex() {}
|
||||
FieldValueIndex(const Value& v, Select s = {});
|
||||
FieldValueIndex(const Value& v, const Select& s = {});
|
||||
/**
|
||||
* Tells if all the fields of this are contained in the other
|
||||
* FieldValueIndex with the same value.
|
||||
@@ -954,7 +1001,7 @@ template <typename T,
|
||||
Value::Filter
|
||||
getFilterSet()
|
||||
{
|
||||
return Value::AllFilter();
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
@@ -970,6 +1017,10 @@ unpackVector(const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
uint64_t unpackId(const Json::Value& json, const std::string& key);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
MSGPACK_ADD_ENUM(dht::Value::Field)
|
||||
+7
-1
@@ -5,7 +5,7 @@
|
||||
# distutils: libraries = opendht gnutls
|
||||
# cython: language_level=3
|
||||
#
|
||||
# Copyright (c) 2015-2016 Savoir-faire Linux Inc.
|
||||
# Copyright (c) 2015-2019 Savoir-faire Linux Inc.
|
||||
# Author(s): Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
|
||||
# Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
# Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
@@ -139,6 +139,12 @@ cdef class SockAddr(object):
|
||||
return self._addr.setPort(port)
|
||||
def setFamily(SockAddr self, cpp.sa_family_t af):
|
||||
return self._addr.setFamily(af)
|
||||
def isLoopback(SockAddr self):
|
||||
return self._addr.isLoopback()
|
||||
def isPrivate(SockAddr self):
|
||||
return self._addr.isPrivate()
|
||||
def isUnspecified(SockAddr self):
|
||||
return self._addr.isUnspecified()
|
||||
def __str__(self):
|
||||
return self.toString().decode()
|
||||
def __repr__(self):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2015-2016 Savoir-faire Linux Inc.
|
||||
# Copyright (c) 2015-2019 Savoir-faire Linux Inc.
|
||||
# Author(s): Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
# Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
#
|
||||
@@ -77,6 +77,9 @@ cdef extern from "opendht/sockaddr.h" namespace "dht":
|
||||
void setPort(in_port_t p)
|
||||
sa_family_t getFamily() const
|
||||
void setFamily(sa_family_t f)
|
||||
bool isLoopback() const
|
||||
bool isPrivate() const
|
||||
bool isUnspecified() const
|
||||
|
||||
ctypedef vector[uint8_t] Blob
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2015-2017 Savoir-faire Linux Inc.
|
||||
# Copyright (C) 2015-2019 Savoir-faire Linux Inc.
|
||||
# Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
|
||||
# Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
#
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2015-2016 Savoir-faire Linux Inc.
|
||||
# Copyright (C) 2015-2019 Savoir-faire Linux Inc.
|
||||
# Author(s): Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
# Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
#
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015-2016 Savoir-faire Linux Inc.
|
||||
# Copyright (c) 2015-2019 Savoir-faire Linux Inc.
|
||||
# Author(s): Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
# Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
#
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2015 Savoir-Faire Linux Inc.
|
||||
# Copyright (C) 2015-2019 Savoir-Faire Linux Inc.
|
||||
# Author(s): Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
# Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015-2016 Savoir-faire Linux Inc.
|
||||
# Copyright (c) 2015-2019 Savoir-faire Linux Inc.
|
||||
# Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2015-2017 Savoir-faire Linux Inc.
|
||||
# Copyright (C) 2015-2019 Savoir-faire Linux Inc.
|
||||
# Author(s): Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016 Savoir-faire Linux Inc.
|
||||
# Copyright (c) 2016-2019 Savoir-faire Linux Inc.
|
||||
# Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2016 Savoir-faire Linux Inc.
|
||||
# Copyright (c) 2015-2019 Savoir-faire Linux Inc.
|
||||
# Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2017 Savoir-faire Linux Inc.
|
||||
# Copyright (c) 2015-2019 Savoir-faire Linux Inc.
|
||||
# Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
@@ -27,7 +27,7 @@ ping_node.run()
|
||||
pong_node = dht.DhtRunner()
|
||||
pong_node.run()
|
||||
#pong_node.enableLogging()
|
||||
pong_node.ping(ping_node.getBound());
|
||||
pong_node.ping(ping_node.getBound())
|
||||
|
||||
loc_ping = dht.InfoHash.get("toto99")
|
||||
loc_pong = dht.InfoHash.get(str(loc_ping))
|
||||
@@ -42,7 +42,7 @@ def done(h, ok):
|
||||
|
||||
def ping(node, h):
|
||||
global i
|
||||
time.sleep(0.0075)
|
||||
time.sleep(0.0075)
|
||||
i += 1
|
||||
if i < MAX:
|
||||
node.put(h, dht.Value(b"hey"), lambda ok, nodes: done(node.getNodeId().decode(), ok))
|
||||
@@ -51,7 +51,7 @@ def ping(node, h):
|
||||
|
||||
def pong(node, h):
|
||||
print(node.getNodeId().decode(), "got ping", h, i)
|
||||
loop.call_soon_threadsafe(ping, node, h);
|
||||
loop.call_soon_threadsafe(ping, node, h)
|
||||
return True
|
||||
|
||||
ping_node.listen(loc_ping, lambda v: pong(pong_node, loc_pong))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2016 Savoir-faire Linux Inc.
|
||||
# Copyright (c) 2015-2019 Savoir-faire Linux Inc.
|
||||
# Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
|
||||
+11
-3
@@ -1,7 +1,7 @@
|
||||
lib_LTLIBRARIES = libopendht.la
|
||||
|
||||
libopendht_la_CPPFLAGS = @CPPFLAGS@ -I$(top_srcdir)/include/opendht @Argon2_CFLAGS@
|
||||
libopendht_la_LIBADD = @Argon2_LIBS@ @GnuTLS_LIBS@ @Nettle_LIBS@
|
||||
libopendht_la_CPPFLAGS = @CPPFLAGS@ -I$(top_srcdir)/include/opendht @Argon2_CFLAGS@ @JsonCpp_CFLAGS@ @MsgPack_CFLAGS@
|
||||
libopendht_la_LIBADD = @Argon2_LIBS@ @JsonCpp_LIBS@ @GnuTLS_LIBS@ @Nettle_LIBS@
|
||||
libopendht_la_LDFLAGS = @LDFLAGS@ @Argon2_LDFLAGS@
|
||||
libopendht_la_SOURCES = \
|
||||
dht.cpp \
|
||||
@@ -9,6 +9,9 @@ libopendht_la_SOURCES = \
|
||||
listener.h \
|
||||
request.h \
|
||||
search.h \
|
||||
value_cache.h \
|
||||
op_cache.h \
|
||||
op_cache.cpp \
|
||||
net.h \
|
||||
parsed_message.h \
|
||||
node_cache.cpp \
|
||||
@@ -53,7 +56,7 @@ nobase_include_HEADERS = \
|
||||
../include/opendht/rng.h
|
||||
|
||||
if ENABLE_PROXY_SERVER
|
||||
libopendht_la_SOURCES += base64.h base64.cpp dht_proxy_server.cpp
|
||||
libopendht_la_SOURCES += dht_proxy_server.cpp thread_pool.h thread_pool.cpp
|
||||
nobase_include_HEADERS += ../include/opendht/dht_proxy_server.h
|
||||
endif
|
||||
|
||||
@@ -62,6 +65,11 @@ libopendht_la_SOURCES += dht_proxy_client.cpp
|
||||
nobase_include_HEADERS += ../include/opendht/dht_proxy_client.h ../include/opendht/dht_interface.h
|
||||
endif
|
||||
|
||||
libopendht_la_SOURCES += base64.h base64.cpp
|
||||
if PROXY_CLIENT_OR_SERVER
|
||||
nobase_include_HEADERS += ../include/opendht/proxy.h
|
||||
endif
|
||||
|
||||
if ENABLE_INDEXATION
|
||||
libopendht_la_SOURCES += indexation/pht.cpp
|
||||
nobase_include_HEADERS += ../include/opendht/indexation/pht.h
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2004-2019 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
|
||||
@@ -17,8 +17,8 @@
|
||||
|
||||
#include "base64.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
/* Mainly based on the following stackoverflow question:
|
||||
* http://stackoverflow.com/questions/342409/how-do-i-base64-encode-decode-in-c
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2004-2019 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
|
||||
|
||||
+35
-11
@@ -4,7 +4,7 @@ namespace dht {
|
||||
|
||||
|
||||
GetCallbackSimple
|
||||
bindGetCb(GetCallbackRaw raw_cb, void* user_data)
|
||||
bindGetCb(const GetCallbackRaw& raw_cb, void* user_data)
|
||||
{
|
||||
if (not raw_cb) return {};
|
||||
return [=](const std::shared_ptr<Value>& value) {
|
||||
@@ -13,7 +13,7 @@ bindGetCb(GetCallbackRaw raw_cb, void* user_data)
|
||||
}
|
||||
|
||||
GetCallback
|
||||
bindGetCb(GetCallbackSimple cb)
|
||||
bindGetCb(const GetCallbackSimple& cb)
|
||||
{
|
||||
if (not cb) return {};
|
||||
return [=](const std::vector<std::shared_ptr<Value>>& values) {
|
||||
@@ -25,7 +25,7 @@ bindGetCb(GetCallbackSimple cb)
|
||||
}
|
||||
|
||||
ShutdownCallback
|
||||
bindShutdownCb(ShutdownCallbackRaw shutdown_cb_raw, void* user_data)
|
||||
bindShutdownCb(const ShutdownCallbackRaw& shutdown_cb_raw, void* user_data)
|
||||
{
|
||||
return [=]() { shutdown_cb_raw(user_data); };
|
||||
}
|
||||
@@ -39,7 +39,7 @@ bindDoneCb(DoneCallbackSimple donecb)
|
||||
}
|
||||
|
||||
DoneCallback
|
||||
bindDoneCb(DoneCallbackRaw raw_cb, void* user_data)
|
||||
bindDoneCb(const DoneCallbackRaw& raw_cb, void* user_data)
|
||||
{
|
||||
if (not raw_cb) return {};
|
||||
return [=](bool success, const std::vector<std::shared_ptr<Node>>& nodes) {
|
||||
@@ -48,7 +48,7 @@ bindDoneCb(DoneCallbackRaw raw_cb, void* user_data)
|
||||
}
|
||||
|
||||
DoneCallbackSimple
|
||||
bindDoneCbSimple(DoneCallbackSimpleRaw raw_cb, void* user_data) {
|
||||
bindDoneCbSimple(const DoneCallbackSimpleRaw& raw_cb, void* user_data) {
|
||||
if (not raw_cb) return {};
|
||||
return [=](bool success) {
|
||||
raw_cb(success, user_data);
|
||||
@@ -62,13 +62,12 @@ NodeStats::toString() const
|
||||
ss << "Known nodes: " << good_nodes << " good, " << dubious_nodes << " dubious, " << incoming_nodes << " incoming." << std::endl;
|
||||
if (table_depth > 1) {
|
||||
ss << "Routing table depth: " << table_depth << std::endl;
|
||||
unsigned long tot_nodes = 8 * std::exp2(table_depth);
|
||||
ss << "Network size estimation: " << tot_nodes << " nodes" << std::endl;
|
||||
ss << "Network size estimation: " << getNetworkSizeEstimation() << " nodes" << std::endl;
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
/**
|
||||
* Build a json object from a NodeStats
|
||||
*/
|
||||
@@ -81,8 +80,7 @@ NodeStats::toJson() const
|
||||
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);
|
||||
val["network_size_estimation"] = static_cast<Json::LargestUInt>(getNetworkSizeEstimation());
|
||||
}
|
||||
return val;
|
||||
}
|
||||
@@ -98,6 +96,32 @@ NodeStats::NodeStats(const Json::Value& val)
|
||||
if (val.isMember("table_depth"))
|
||||
table_depth = static_cast<unsigned>(val["table_depth"].asLargestUInt());
|
||||
}
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
|
||||
/**
|
||||
* Build a json object from a NodeStats
|
||||
*/
|
||||
Json::Value
|
||||
NodeInfo::toJson() const
|
||||
{
|
||||
Json::Value val;
|
||||
if (id)
|
||||
val["id"] = id.toString();
|
||||
val["node_id"] = node_id.toString();
|
||||
val["ipv4"] = ipv4.toJson();
|
||||
val["ipv6"] = ipv6.toJson();
|
||||
return val;
|
||||
}
|
||||
|
||||
NodeInfo::NodeInfo(const Json::Value& v)
|
||||
{
|
||||
if (v.isMember("id"))
|
||||
id = InfoHash(v["id"].asString());
|
||||
node_id = InfoHash(v["node_id"].asString());
|
||||
ipv4 = NodeStats(v["ipv4"]);
|
||||
ipv6 = NodeStats(v["ipv6"]);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
+269
-106
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -40,31 +40,6 @@ static std::uniform_int_distribution<int> rand_byte{ 0, std::numeric_limits<uint
|
||||
static std::uniform_int_distribution<uint8_t> rand_byte;
|
||||
#endif
|
||||
|
||||
static gnutls_digest_algorithm_t get_dig_for_pub(gnutls_pubkey_t pubkey)
|
||||
{
|
||||
gnutls_digest_algorithm_t dig;
|
||||
int result = gnutls_pubkey_get_preferred_hash_algorithm(pubkey, &dig, nullptr);
|
||||
if (result < 0)
|
||||
return GNUTLS_DIG_UNKNOWN;
|
||||
return dig;
|
||||
}
|
||||
|
||||
static gnutls_digest_algorithm_t get_dig(gnutls_x509_crt_t crt)
|
||||
{
|
||||
gnutls_pubkey_t pubkey;
|
||||
gnutls_pubkey_init(&pubkey);
|
||||
|
||||
int result = gnutls_pubkey_import_x509(pubkey, crt, 0);
|
||||
if (result < 0) {
|
||||
gnutls_pubkey_deinit(pubkey);
|
||||
return GNUTLS_DIG_UNKNOWN;
|
||||
}
|
||||
|
||||
gnutls_digest_algorithm_t dig = get_dig_for_pub(pubkey);
|
||||
gnutls_pubkey_deinit(pubkey);
|
||||
return dig;
|
||||
}
|
||||
|
||||
// support for GnuTLS < 3.4.
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030400
|
||||
#define GNUTLS_PKCS_PKCS12_3DES GNUTLS_PKCS_USE_PKCS12_3DES
|
||||
@@ -377,15 +352,19 @@ PrivateKey::serialize(const std::string& password) const
|
||||
PublicKey
|
||||
PrivateKey::getPublicKey() const
|
||||
{
|
||||
gnutls_pubkey_t pk;
|
||||
gnutls_pubkey_init(&pk);
|
||||
PublicKey pk_ret {pk};
|
||||
if (gnutls_pubkey_import_privkey(pk, key, GNUTLS_KEY_KEY_CERT_SIGN | GNUTLS_KEY_CRL_SIGN, 0) != GNUTLS_E_SUCCESS)
|
||||
return {};
|
||||
return pk_ret;
|
||||
PublicKey pk;
|
||||
if (auto err = gnutls_pubkey_import_privkey(pk.pk, key, GNUTLS_KEY_KEY_CERT_SIGN | GNUTLS_KEY_CRL_SIGN, 0))
|
||||
throw CryptoException(std::string("Can't retreive public key: ") + gnutls_strerror(err));
|
||||
return pk;
|
||||
}
|
||||
|
||||
PublicKey::PublicKey(const Blob& dat) : pk(nullptr)
|
||||
PublicKey::PublicKey()
|
||||
{
|
||||
if (auto err = gnutls_pubkey_init(&pk))
|
||||
throw CryptoException(std::string("Can't initialize public key: ") + gnutls_strerror(err));
|
||||
}
|
||||
|
||||
PublicKey::PublicKey(const Blob& dat) : PublicKey()
|
||||
{
|
||||
unpack(dat.data(), dat.size());
|
||||
}
|
||||
@@ -411,10 +390,11 @@ PublicKey::operator=(PublicKey&& o) noexcept
|
||||
void
|
||||
PublicKey::pack(Blob& b) const
|
||||
{
|
||||
if (not pk)
|
||||
throw CryptoException(std::string("Could not export public key: null key"));
|
||||
std::vector<uint8_t> tmp(2048);
|
||||
size_t sz = tmp.size();
|
||||
int err = gnutls_pubkey_export(pk, GNUTLS_X509_FMT_DER, tmp.data(), &sz);
|
||||
if (err != GNUTLS_E_SUCCESS)
|
||||
if (int err = gnutls_pubkey_export(pk, GNUTLS_X509_FMT_DER, tmp.data(), &sz))
|
||||
throw CryptoException(std::string("Could not export public key: ") + gnutls_strerror(err));
|
||||
tmp.resize(sz);
|
||||
b.insert(b.end(), tmp.begin(), tmp.end());
|
||||
@@ -423,9 +403,6 @@ PublicKey::pack(Blob& b) const
|
||||
void
|
||||
PublicKey::unpack(const uint8_t* data, size_t data_size)
|
||||
{
|
||||
if (pk)
|
||||
gnutls_pubkey_deinit(pk);
|
||||
gnutls_pubkey_init(&pk);
|
||||
const gnutls_datum_t dat {(uint8_t*)data, (unsigned)data_size};
|
||||
int err = gnutls_pubkey_import(pk, &dat, GNUTLS_X509_FMT_PEM);
|
||||
if (err != GNUTLS_E_SUCCESS)
|
||||
@@ -437,15 +414,16 @@ PublicKey::unpack(const uint8_t* data, size_t data_size)
|
||||
std::string
|
||||
PublicKey::toString() const
|
||||
{
|
||||
if (not pk)
|
||||
throw CryptoException(std::string("Could not print public key: null key"));
|
||||
std::string ret;
|
||||
size_t sz = ret.size();
|
||||
int err = gnutls_pubkey_export(pk, GNUTLS_X509_FMT_PEM, (void*)ret.data(), &sz);
|
||||
if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) {
|
||||
ret.resize(sz);
|
||||
int err = gnutls_pubkey_export(pk, GNUTLS_X509_FMT_PEM, (void*)ret.data(), &sz);
|
||||
if (err != GNUTLS_E_SUCCESS)
|
||||
throw CryptoException(std::string("Could not print public key: ") + gnutls_strerror(err));
|
||||
} else if (err != GNUTLS_E_SUCCESS)
|
||||
err = gnutls_pubkey_export(pk, GNUTLS_X509_FMT_PEM, (void*)ret.data(), &sz);
|
||||
}
|
||||
if (err != GNUTLS_E_SUCCESS)
|
||||
throw CryptoException(std::string("Could not print public key: ") + gnutls_strerror(err));
|
||||
return ret;
|
||||
}
|
||||
@@ -568,7 +546,180 @@ PublicKey::getLongId() const
|
||||
#endif
|
||||
}
|
||||
|
||||
Certificate::Certificate(const Blob& certData) : cert(nullptr)
|
||||
gnutls_digest_algorithm_t
|
||||
PublicKey::getPreferredDigest() const
|
||||
{
|
||||
gnutls_digest_algorithm_t dig;
|
||||
int result = gnutls_pubkey_get_preferred_hash_algorithm(pk, &dig, nullptr);
|
||||
if (result < 0)
|
||||
return GNUTLS_DIG_UNKNOWN;
|
||||
return dig;
|
||||
}
|
||||
|
||||
// Certificate Request
|
||||
|
||||
static NameType
|
||||
typeFromGnuTLS(gnutls_x509_subject_alt_name_t type)
|
||||
{
|
||||
switch(type) {
|
||||
case GNUTLS_SAN_DNSNAME:
|
||||
return NameType::DNS;
|
||||
case GNUTLS_SAN_RFC822NAME:
|
||||
return NameType::RFC822;
|
||||
case GNUTLS_SAN_URI:
|
||||
return NameType::URI;
|
||||
case GNUTLS_SAN_IPADDRESS:
|
||||
return NameType::IP;
|
||||
default:
|
||||
return NameType::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static gnutls_x509_subject_alt_name_t
|
||||
GnuTLSFromType(NameType type)
|
||||
{
|
||||
switch(type) {
|
||||
case NameType::DNS:
|
||||
return GNUTLS_SAN_DNSNAME;
|
||||
case NameType::RFC822:
|
||||
return GNUTLS_SAN_RFC822NAME;
|
||||
case NameType::URI:
|
||||
return GNUTLS_SAN_URI;
|
||||
case NameType::IP:
|
||||
return GNUTLS_SAN_IPADDRESS;
|
||||
default:
|
||||
return (gnutls_x509_subject_alt_name_t)0;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string
|
||||
getDN(gnutls_x509_crq_t request, const char* oid)
|
||||
{
|
||||
std::string dn;
|
||||
dn.resize(512);
|
||||
size_t dn_sz = dn.size();
|
||||
int ret = gnutls_x509_crq_get_dn_by_oid(request, oid, 0, 0, &(*dn.begin()), &dn_sz);
|
||||
if (ret != GNUTLS_E_SUCCESS)
|
||||
return {};
|
||||
dn.resize(dn_sz);
|
||||
return dn;
|
||||
}
|
||||
|
||||
CertificateRequest::CertificateRequest()
|
||||
{
|
||||
if (auto err = gnutls_x509_crq_init(&request))
|
||||
throw CryptoException(std::string("Can't initialize certificate request: ") + gnutls_strerror(err));
|
||||
}
|
||||
|
||||
CertificateRequest::CertificateRequest(const uint8_t* data, size_t size) : CertificateRequest()
|
||||
{
|
||||
const gnutls_datum_t dat {(uint8_t*)data, (unsigned)size};
|
||||
if (auto err = gnutls_x509_crq_import(request, &dat, GNUTLS_X509_FMT_PEM))
|
||||
throw CryptoException(std::string("Can't import certificate request: ") + gnutls_strerror(err));
|
||||
}
|
||||
|
||||
CertificateRequest::~CertificateRequest()
|
||||
{
|
||||
if (request) {
|
||||
gnutls_x509_crq_deinit(request);
|
||||
request = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
CertificateRequest&
|
||||
CertificateRequest::operator=(CertificateRequest&& o) noexcept
|
||||
{
|
||||
if (request)
|
||||
gnutls_x509_crq_deinit(request);
|
||||
request = o.request;
|
||||
o.request = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
CertificateRequest::setAltName(NameType type, const std::string& name)
|
||||
{
|
||||
gnutls_x509_crq_set_subject_alt_name(request, GnuTLSFromType(type), name.data(), name.size(), 0);
|
||||
}
|
||||
|
||||
void
|
||||
CertificateRequest::setName(const std::string& name)
|
||||
{
|
||||
gnutls_x509_crq_set_dn_by_oid(request, GNUTLS_OID_X520_COMMON_NAME, 0, name.data(), name.length());
|
||||
}
|
||||
|
||||
std::string
|
||||
CertificateRequest::getName() const
|
||||
{
|
||||
return getDN(request, GNUTLS_OID_X520_COMMON_NAME);
|
||||
}
|
||||
|
||||
std::string
|
||||
CertificateRequest::getUID() const
|
||||
{
|
||||
return getDN(request, GNUTLS_OID_LDAP_UID);
|
||||
}
|
||||
|
||||
void
|
||||
CertificateRequest::setUID(const std::string& uid)
|
||||
{
|
||||
gnutls_x509_crq_set_dn_by_oid(request, GNUTLS_OID_LDAP_UID, 0, uid.data(), uid.length());
|
||||
}
|
||||
|
||||
void
|
||||
CertificateRequest::sign(const PrivateKey& key, const std::string& password)
|
||||
{
|
||||
gnutls_x509_crq_set_version(request, 1);
|
||||
if (not password.empty())
|
||||
gnutls_x509_crq_set_challenge_password(request, password.c_str());
|
||||
|
||||
if (auto err = gnutls_x509_crq_set_key(request, key.x509_key))
|
||||
throw CryptoException(std::string("Can't set certificate request key: ") + gnutls_strerror(err));
|
||||
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030601
|
||||
if (auto err = gnutls_x509_crq_privkey_sign(request, key.key, key.getPublicKey().getPreferredDigest(), 0))
|
||||
throw CryptoException(std::string("Can't sign certificate request: ") + gnutls_strerror(err));
|
||||
#else
|
||||
if (auto err = gnutls_x509_crq_privkey_sign(request, key.key, GNUTLS_DIG_UNKNOWN, 0))
|
||||
throw CryptoException(std::string("Can't sign certificate request: ") + gnutls_strerror(err));
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
CertificateRequest::verify() const
|
||||
{
|
||||
return gnutls_x509_crq_verify(request, 0) >= 0;
|
||||
}
|
||||
|
||||
Blob
|
||||
CertificateRequest::pack() const
|
||||
{
|
||||
gnutls_datum_t dat {nullptr, 0};
|
||||
if (auto err = gnutls_x509_crq_export2(request, GNUTLS_X509_FMT_PEM, &dat))
|
||||
throw CryptoException(std::string("Can't export certificate request: ") + gnutls_strerror(err));
|
||||
Blob ret(dat.data, dat.data + dat.size);
|
||||
gnutls_free(dat.data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Certificate
|
||||
|
||||
static std::string
|
||||
getDN(gnutls_x509_crt_t cert, const char* oid, bool issuer = false)
|
||||
{
|
||||
std::string dn;
|
||||
dn.resize(512);
|
||||
size_t dn_sz = dn.size();
|
||||
int ret = issuer
|
||||
? gnutls_x509_crt_get_issuer_dn_by_oid(cert, oid, 0, 0, &(*dn.begin()), &dn_sz)
|
||||
: gnutls_x509_crt_get_dn_by_oid( cert, oid, 0, 0, &(*dn.begin()), &dn_sz);
|
||||
if (ret != GNUTLS_E_SUCCESS)
|
||||
return {};
|
||||
dn.resize(dn_sz);
|
||||
return dn;
|
||||
}
|
||||
|
||||
Certificate::Certificate(const Blob& certData)
|
||||
{
|
||||
unpack(certData.data(), certData.size());
|
||||
}
|
||||
@@ -652,11 +803,9 @@ Certificate::~Certificate()
|
||||
PublicKey
|
||||
Certificate::getPublicKey() const
|
||||
{
|
||||
gnutls_pubkey_t pk;
|
||||
gnutls_pubkey_init(&pk);
|
||||
PublicKey pk_ret(pk);
|
||||
if (gnutls_pubkey_import_x509(pk, cert, 0) != GNUTLS_E_SUCCESS)
|
||||
return {};
|
||||
PublicKey pk_ret;
|
||||
if (auto err = gnutls_pubkey_import_x509(pk_ret.pk, cert, 0))
|
||||
throw CryptoException(std::string("Can't get certificate public key: ") + gnutls_strerror(err));
|
||||
return pk_ret;
|
||||
}
|
||||
|
||||
@@ -692,21 +841,6 @@ Certificate::getLongId() const
|
||||
#endif
|
||||
}
|
||||
|
||||
static std::string
|
||||
getDN(gnutls_x509_crt_t cert, const char* oid, bool issuer = false)
|
||||
{
|
||||
std::string dn;
|
||||
dn.resize(512);
|
||||
size_t dn_sz = dn.size();
|
||||
int ret = issuer
|
||||
? gnutls_x509_crt_get_issuer_dn_by_oid(cert, oid, 0, 0, &(*dn.begin()), &dn_sz)
|
||||
: gnutls_x509_crt_get_dn_by_oid( cert, oid, 0, 0, &(*dn.begin()), &dn_sz);
|
||||
if (ret != GNUTLS_E_SUCCESS)
|
||||
return {};
|
||||
dn.resize(dn_sz);
|
||||
return dn;
|
||||
}
|
||||
|
||||
std::string
|
||||
Certificate::getName() const
|
||||
{
|
||||
@@ -731,24 +865,7 @@ Certificate::getIssuerUID() const
|
||||
return getDN(cert, GNUTLS_OID_LDAP_UID, true);
|
||||
}
|
||||
|
||||
static Certificate::NameType
|
||||
typeFromGnuTLS(gnutls_x509_subject_alt_name_t type)
|
||||
{
|
||||
switch(type) {
|
||||
case GNUTLS_SAN_DNSNAME:
|
||||
return Certificate::NameType::DNS;
|
||||
case GNUTLS_SAN_RFC822NAME:
|
||||
return Certificate::NameType::RFC822;
|
||||
case GNUTLS_SAN_URI:
|
||||
return Certificate::NameType::URI;
|
||||
case GNUTLS_SAN_IPADDRESS:
|
||||
return Certificate::NameType::IP;
|
||||
default:
|
||||
return Certificate::NameType::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<Certificate::NameType, std::string>>
|
||||
std::vector<std::pair<NameType, std::string>>
|
||||
Certificate::getAltNames() const
|
||||
{
|
||||
std::vector<std::pair<NameType, std::string>> names;
|
||||
@@ -863,6 +980,14 @@ Certificate::getExpiration() const
|
||||
return std::chrono::system_clock::from_time_t(t);
|
||||
}
|
||||
|
||||
gnutls_digest_algorithm_t
|
||||
Certificate::getPreferredDigest() const
|
||||
{
|
||||
return getPublicKey().getPreferredDigest();
|
||||
}
|
||||
|
||||
// PrivateKey
|
||||
|
||||
PrivateKey
|
||||
PrivateKey::generate(unsigned key_length)
|
||||
{
|
||||
@@ -892,7 +1017,7 @@ PrivateKey::generateEC()
|
||||
}
|
||||
|
||||
Identity
|
||||
generateIdentity(const std::string& name, crypto::Identity ca, unsigned key_length, bool is_ca)
|
||||
generateIdentity(const std::string& name, const Identity& ca, unsigned key_length, bool is_ca)
|
||||
{
|
||||
auto key = std::make_shared<PrivateKey>(PrivateKey::generate(key_length));
|
||||
auto cert = std::make_shared<Certificate>(Certificate::generate(*key, name, ca, is_ca));
|
||||
@@ -901,12 +1026,12 @@ generateIdentity(const std::string& name, crypto::Identity ca, unsigned key_leng
|
||||
|
||||
|
||||
Identity
|
||||
generateIdentity(const std::string& name, Identity ca, unsigned key_length) {
|
||||
generateIdentity(const std::string& name, const Identity& ca, unsigned key_length) {
|
||||
return generateIdentity(name, ca, key_length, !ca.first || !ca.second);
|
||||
}
|
||||
|
||||
Identity
|
||||
generateEcIdentity(const std::string& name, crypto::Identity ca, bool is_ca)
|
||||
generateEcIdentity(const std::string& name, const Identity& ca, bool is_ca)
|
||||
{
|
||||
auto key = std::make_shared<PrivateKey>(PrivateKey::generateEC());
|
||||
auto cert = std::make_shared<Certificate>(Certificate::generate(*key, name, ca, is_ca));
|
||||
@@ -914,25 +1039,40 @@ generateEcIdentity(const std::string& name, crypto::Identity ca, bool is_ca)
|
||||
}
|
||||
|
||||
Identity
|
||||
generateEcIdentity(const std::string& name, Identity ca) {
|
||||
generateEcIdentity(const std::string& name, const Identity& ca) {
|
||||
return generateEcIdentity(name, ca, !ca.first || !ca.second);
|
||||
}
|
||||
|
||||
void
|
||||
setValidityPeriod(gnutls_x509_crt_t cert, int64_t validity)
|
||||
{
|
||||
int64_t now = time(nullptr);
|
||||
/* 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 + validity));
|
||||
}
|
||||
|
||||
void
|
||||
setRandomSerial(gnutls_x509_crt_t cert)
|
||||
{
|
||||
random_device rdev;
|
||||
std::uniform_int_distribution<uint64_t> dist{};
|
||||
uint64_t cert_serial = dist(rdev);
|
||||
gnutls_x509_crt_set_serial(cert, &cert_serial, sizeof(cert_serial));
|
||||
}
|
||||
|
||||
Certificate
|
||||
Certificate::generate(const PrivateKey& key, const std::string& name, Identity ca, bool is_ca)
|
||||
Certificate::generate(const PrivateKey& key, const std::string& name, const Identity& ca, bool is_ca)
|
||||
{
|
||||
gnutls_x509_crt_t cert;
|
||||
if (not key.x509_key or gnutls_x509_crt_init(&cert) != GNUTLS_E_SUCCESS)
|
||||
return {};
|
||||
Certificate ret {cert};
|
||||
|
||||
int64_t now = time(NULL);
|
||||
/* 2038 bug: don't allow time wrap */
|
||||
auto boundTime = [](int64_t t) -> time_t {
|
||||
return std::min<int64_t>(t, std::numeric_limits<time_t>::max());
|
||||
};
|
||||
gnutls_x509_crt_set_activation_time(cert, boundTime(now));
|
||||
gnutls_x509_crt_set_expiration_time(cert, boundTime(now + (10 * 365 * 24 * 60 * 60)));
|
||||
setValidityPeriod(cert, 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 {};
|
||||
@@ -943,19 +1083,15 @@ Certificate::generate(const PrivateKey& key, const std::string& name, Identity c
|
||||
}
|
||||
|
||||
// TODO: compute the subject key using the recommended RFC method
|
||||
auto pk_id = key.getPublicKey().getId();
|
||||
auto pk = key.getPublicKey();
|
||||
auto pk_id = pk.getId();
|
||||
const std::string uid_str = pk_id.toString();
|
||||
|
||||
gnutls_x509_crt_set_subject_key_id(cert, &pk_id, sizeof(pk_id));
|
||||
gnutls_x509_crt_set_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, name.data(), name.length());
|
||||
gnutls_x509_crt_set_dn_by_oid(cert, GNUTLS_OID_LDAP_UID, 0, uid_str.data(), uid_str.length());
|
||||
|
||||
{
|
||||
random_device rdev;
|
||||
std::uniform_int_distribution<uint64_t> dist{};
|
||||
uint64_t cert_serial = dist(rdev);
|
||||
gnutls_x509_crt_set_serial(cert, &cert_serial, sizeof(cert_serial));
|
||||
}
|
||||
setRandomSerial(cert);
|
||||
|
||||
unsigned key_usage = 0;
|
||||
if (is_ca) {
|
||||
@@ -971,15 +1107,13 @@ Certificate::generate(const PrivateKey& key, const std::string& name, Identity c
|
||||
// Signing certificate must be CA.
|
||||
return {};
|
||||
}
|
||||
//if (gnutls_x509_crt_sign2(cert, ca.second->cert, ca.first->x509_key, get_dig(cert), 0) != GNUTLS_E_SUCCESS) {
|
||||
if (gnutls_x509_crt_privkey_sign(cert, ca.second->cert, ca.first->key, get_dig(cert), 0) != GNUTLS_E_SUCCESS) {
|
||||
if (gnutls_x509_crt_privkey_sign(cert, ca.second->cert, ca.first->key, pk.getPreferredDigest(), 0) != GNUTLS_E_SUCCESS) {
|
||||
std::cerr << "Error when signing certificate" << std::endl;
|
||||
return {};
|
||||
}
|
||||
ret.issuer = ca.second;
|
||||
} else {
|
||||
//if (gnutls_x509_crt_sign2(cert, cert, key, get_dig(cert), 0) != GNUTLS_E_SUCCESS) {
|
||||
if (gnutls_x509_crt_privkey_sign(cert, cert, key.key, get_dig(cert), 0) != GNUTLS_E_SUCCESS) {
|
||||
if (gnutls_x509_crt_privkey_sign(cert, cert, key.key, pk.getPreferredDigest(), 0) != GNUTLS_E_SUCCESS) {
|
||||
std::cerr << "Error when signing certificate" << std::endl;
|
||||
return {};
|
||||
}
|
||||
@@ -988,6 +1122,31 @@ Certificate::generate(const PrivateKey& key, const std::string& name, Identity c
|
||||
return ret.getPacked();
|
||||
}
|
||||
|
||||
Certificate
|
||||
Certificate::generate(const CertificateRequest& request, const Identity& ca)
|
||||
{
|
||||
gnutls_x509_crt_t cert;
|
||||
if (auto err = gnutls_x509_crt_init(&cert))
|
||||
throw CryptoException(std::string("Can't initialize certificate: ") + gnutls_strerror(err));
|
||||
Certificate ret {cert};
|
||||
if (auto err = gnutls_x509_crt_set_crq(cert, request.get()))
|
||||
throw CryptoException(std::string("Can't initialize certificate: ") + gnutls_strerror(err));
|
||||
|
||||
if (auto err = gnutls_x509_crt_set_version(cert, 3)) {
|
||||
throw CryptoException(std::string("Can't set certificate version: ") + gnutls_strerror(err));
|
||||
}
|
||||
|
||||
setValidityPeriod(cert, 10 * 365 * 24 * 60 * 60);
|
||||
setRandomSerial(cert);
|
||||
|
||||
if (auto err = gnutls_x509_crt_privkey_sign(cert, ca.second->cert, ca.first->key, ca.second->getPreferredDigest(), 0)) {
|
||||
throw CryptoException(std::string("Can't sign certificate: ") + gnutls_strerror(err));
|
||||
}
|
||||
ret.issuer = ca.second;
|
||||
|
||||
return ret.getPacked();
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<RevocationList>>
|
||||
Certificate::getRevocationLists() const
|
||||
{
|
||||
@@ -998,6 +1157,8 @@ Certificate::getRevocationLists() const
|
||||
return ret;
|
||||
}
|
||||
|
||||
// RevocationList
|
||||
|
||||
RevocationList::RevocationList()
|
||||
{
|
||||
gnutls_x509_crl_init(&crl);
|
||||
@@ -1215,6 +1376,8 @@ RevocationList::toString() const
|
||||
return ret;
|
||||
}
|
||||
|
||||
// TrustList
|
||||
|
||||
TrustList::TrustList() {
|
||||
gnutls_x509_trust_list_init(&trust, 0);
|
||||
}
|
||||
@@ -1224,11 +1387,11 @@ TrustList::~TrustList() {
|
||||
}
|
||||
|
||||
TrustList&
|
||||
TrustList::operator=(TrustList&& o)
|
||||
TrustList::operator=(TrustList&& o) noexcept
|
||||
{
|
||||
if (trust)
|
||||
gnutls_x509_trust_list_deinit(trust, true);
|
||||
trust = std::move(o.trust);
|
||||
trust = o.trust;
|
||||
o.trust = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -38,7 +38,7 @@ DhtMessage::storePolicy(InfoHash h, std::shared_ptr<Value>& v, const InfoHash& f
|
||||
}
|
||||
|
||||
Value::Filter
|
||||
DhtMessage::ServiceFilter(std::string s)
|
||||
DhtMessage::ServiceFilter(const std::string& s)
|
||||
{
|
||||
return Value::Filter::chain(
|
||||
Value::TypeFilter(TYPE),
|
||||
|
||||
+509
-373
File diff suppressed because it is too large.
Load diff
+764
-451
File diff suppressed because it is too large.
Load diff
+561
-338
File diff suppressed because it is too large.
Load diff
+223
-88
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "dhtrunner.h"
|
||||
#include "securedht.h"
|
||||
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
#include "dht_proxy_client.h"
|
||||
#endif
|
||||
|
||||
@@ -37,14 +37,25 @@
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#define close(x) closesocket(x)
|
||||
#define write(s, b, f) send(s, b, (int)strlen(b), 0)
|
||||
#endif
|
||||
|
||||
namespace dht {
|
||||
|
||||
constexpr std::chrono::seconds DhtRunner::BOOTSTRAP_PERIOD;
|
||||
static constexpr size_t RX_QUEUE_MAX_SIZE = 1024 * 16;
|
||||
|
||||
struct DhtRunner::Listener {
|
||||
size_t tokenClassicDht;
|
||||
size_t tokenProxyDht;
|
||||
ValueCallback gcb;
|
||||
InfoHash hash;
|
||||
Value::Filter f;
|
||||
Where w;
|
||||
};
|
||||
|
||||
DhtRunner::DhtRunner() : dht_()
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
, dht_via_proxy_()
|
||||
#endif //OPENDHT_PROXY_CLIENT
|
||||
{
|
||||
@@ -64,7 +75,7 @@ DhtRunner::~DhtRunner()
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::run(in_port_t port, DhtRunner::Config config)
|
||||
DhtRunner::run(in_port_t port, const DhtRunner::Config& config)
|
||||
{
|
||||
SockAddr sin4;
|
||||
sin4.setFamily(AF_INET);
|
||||
@@ -76,7 +87,7 @@ DhtRunner::run(in_port_t port, DhtRunner::Config config)
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::run(const char* ip4, const char* ip6, const char* service, DhtRunner::Config config)
|
||||
DhtRunner::run(const char* ip4, const char* ip6, const char* service, const DhtRunner::Config& config)
|
||||
{
|
||||
auto res4 = SockAddr::resolve(ip4, service);
|
||||
auto res6 = SockAddr::resolve(ip6, service);
|
||||
@@ -85,7 +96,7 @@ DhtRunner::run(const char* ip4, const char* ip6, const char* service, DhtRunner:
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::run(const SockAddr& local4, const SockAddr& local6, DhtRunner::Config config)
|
||||
DhtRunner::run(const SockAddr& local4, const SockAddr& local6, const DhtRunner::Config& config)
|
||||
{
|
||||
if (running)
|
||||
return;
|
||||
@@ -94,7 +105,7 @@ DhtRunner::run(const SockAddr& local4, const SockAddr& local6, DhtRunner::Config
|
||||
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
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
config_ = config;
|
||||
#endif
|
||||
enableProxy(not config.proxy_server.empty());
|
||||
@@ -111,7 +122,8 @@ DhtRunner::run(const SockAddr& local4, const SockAddr& local6, DhtRunner::Config
|
||||
} catch (const dht::SocketException& e) {
|
||||
startNetwork(local4, local6);
|
||||
}
|
||||
cv.wait_until(lk, wakeup, [this]() {
|
||||
|
||||
auto hasJobToDo = [this]() {
|
||||
if (not running)
|
||||
return true;
|
||||
{
|
||||
@@ -128,14 +140,18 @@ DhtRunner::run(const SockAddr& local4, const SockAddr& local6, DhtRunner::Config
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
};
|
||||
if (wakeup == time_point::max())
|
||||
cv.wait(lk, hasJobToDo);
|
||||
else
|
||||
cv.wait_until(lk, wakeup, hasJobToDo);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::shutdown(ShutdownCallback cb) {
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->shutdown(cb);
|
||||
#endif
|
||||
@@ -149,7 +165,7 @@ DhtRunner::shutdown(ShutdownCallback cb) {
|
||||
void
|
||||
DhtRunner::join()
|
||||
{
|
||||
running_network = false;
|
||||
stopNetwork();
|
||||
running = false;
|
||||
cv.notify_all();
|
||||
bootstrap_cv.notify_all();
|
||||
@@ -233,10 +249,10 @@ void
|
||||
DhtRunner::setLoggers(LogMethod error, LogMethod warn, LogMethod debug) {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
if (dht_)
|
||||
dht_->setLoggers(std::forward<LogMethod>(error), std::forward<LogMethod>(warn), std::forward<LogMethod>(debug));
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
dht_->setLoggers(error, warn, debug);
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->setLoggers(std::forward<LogMethod>(error), std::forward<LogMethod>(warn), std::forward<LogMethod>(debug));
|
||||
dht_via_proxy_->setLoggers(error, warn, debug);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -246,7 +262,7 @@ DhtRunner::setLogFilter(const InfoHash& f) {
|
||||
activeDht()->setLogFilter(f);
|
||||
if (dht_)
|
||||
dht_->setLogFilter(f);
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->setLogFilter(f);
|
||||
#endif
|
||||
@@ -287,6 +303,17 @@ DhtRunner::getNodesStats(sa_family_t af) const
|
||||
return activeDht()->getNodesStats(af);
|
||||
}
|
||||
|
||||
NodeInfo
|
||||
DhtRunner::getNodeInfo() const {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
NodeInfo info;
|
||||
info.id = getId();
|
||||
info.node_id = getNodeId();
|
||||
info.ipv4 = dht_->getNodesStats(AF_INET);
|
||||
info.ipv6 = dht_->getNodesStats(AF_INET6);
|
||||
return info;
|
||||
}
|
||||
|
||||
std::vector<unsigned>
|
||||
DhtRunner::getNodeMessageStats(bool in) const
|
||||
{
|
||||
@@ -328,7 +355,9 @@ std::vector<SockAddr>
|
||||
DhtRunner::getPublicAddress(sa_family_t af)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
return activeDht()->getPublicAddress(af);
|
||||
if (auto dht = activeDht())
|
||||
return dht->getPublicAddress(af);
|
||||
return {};
|
||||
}
|
||||
std::vector<std::string>
|
||||
DhtRunner::getPublicAddressStr(sa_family_t af)
|
||||
@@ -347,7 +376,7 @@ DhtRunner::registerCertificate(std::shared_ptr<crypto::Certificate> cert) {
|
||||
void
|
||||
DhtRunner::setLocalCertificateStore(CertificateStoreQuery&& query_method) {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->setLocalCertificateStore(std::forward<CertificateStoreQuery>(query_method));
|
||||
#endif
|
||||
@@ -382,12 +411,15 @@ DhtRunner::loop_()
|
||||
received = std::move(rcv);
|
||||
}
|
||||
if (not received.empty()) {
|
||||
for (const auto& pck : received) {
|
||||
auto& buf = pck.first;
|
||||
auto& from = pck.second;
|
||||
wakeup = dht->periodic(buf.data(), buf.size()-1, from);
|
||||
while (not received.empty()) {
|
||||
auto& pck = received.front();
|
||||
auto delay = clock::now() - pck.received;
|
||||
if (delay > std::chrono::milliseconds(500))
|
||||
std::cerr << "Dropping packet with high delay: " << print_dt(delay) << std::endl;
|
||||
else
|
||||
wakeup = dht->periodic(pck.data.data(), pck.data.size()-1, pck.from);
|
||||
received.pop();
|
||||
}
|
||||
received.clear();
|
||||
} else {
|
||||
wakeup = dht->periodic(nullptr, 0, nullptr, 0);
|
||||
}
|
||||
@@ -427,8 +459,11 @@ int bindSocket(const SockAddr& addr, SockAddr& bound)
|
||||
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)
|
||||
if(rc < 0) {
|
||||
rc = errno;
|
||||
close(sock);
|
||||
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);
|
||||
@@ -436,39 +471,105 @@ int bindSocket(const SockAddr& addr, SockAddr& bound)
|
||||
return sock;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
inline void udpPipe(int fds[2])
|
||||
{
|
||||
int lst = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (lst < 0)
|
||||
throw DhtException(std::string("Can't open socket: ") + strerror(lst));
|
||||
sockaddr_in inaddr;
|
||||
sockaddr addr;
|
||||
memset(&inaddr, 0, sizeof(inaddr));
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
inaddr.sin_family = AF_INET;
|
||||
inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
inaddr.sin_port = 0;
|
||||
int yes=1;
|
||||
setsockopt(lst, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes));
|
||||
int rc = bind(lst, (sockaddr*)&inaddr, sizeof(inaddr));
|
||||
if (rc < 0) {
|
||||
close(lst);
|
||||
throw DhtException("Can't bind socket on " + print_addr((sockaddr*)&addr, sizeof(inaddr)) + " " + strerror(rc));
|
||||
}
|
||||
socklen_t len = sizeof(inaddr);
|
||||
getsockname(lst, &addr, &len);
|
||||
fds[0] = lst;
|
||||
fds[1] = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
connect(fds[1], &addr, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
DhtRunner::stopNetwork()
|
||||
{
|
||||
running_network = false;
|
||||
if (stop_writefd != -1) {
|
||||
if (write(stop_writefd, "\0", 1) == -1) {
|
||||
perror("write");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::startNetwork(const SockAddr sin4, const SockAddr sin6)
|
||||
{
|
||||
running_network = false;
|
||||
stopNetwork();
|
||||
if (rcv_thread.joinable())
|
||||
rcv_thread.join();
|
||||
|
||||
int stopfds[2];
|
||||
#ifndef _WIN32
|
||||
auto status = pipe(stopfds);
|
||||
if (status == -1) {
|
||||
throw DhtException(std::string("Can't open pipe: ") + strerror(errno));
|
||||
}
|
||||
#else
|
||||
udpPipe(stopfds);
|
||||
#endif
|
||||
int stop_readfd = stopfds[0];
|
||||
stop_writefd = stopfds[1];
|
||||
|
||||
s4 = -1;
|
||||
s6 = -1;
|
||||
|
||||
bound4 = {};
|
||||
if (sin4)
|
||||
s4 = bindSocket(sin4, bound4);
|
||||
if (sin4) {
|
||||
try {
|
||||
s4 = bindSocket(sin4, bound4);
|
||||
} catch (const DhtException& e) {
|
||||
std::cerr << "Can't bind inet socket: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
#if 1
|
||||
bound6 = {};
|
||||
if (sin6)
|
||||
s6 = bindSocket(sin6, bound6);
|
||||
if (sin6) {
|
||||
try {
|
||||
s6 = bindSocket(sin6, bound6);
|
||||
} catch (const DhtException& e) {
|
||||
std::cerr << "Can't bind inet6 socket: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (s4 == -1 && s6 == -1) {
|
||||
throw DhtException("Can't bind socket");
|
||||
}
|
||||
|
||||
running_network = true;
|
||||
rcv_thread = std::thread([this]() {
|
||||
rcv_thread = std::thread([this, stop_readfd]() {
|
||||
try {
|
||||
while (running_network) {
|
||||
struct timeval tv {/*.tv_sec = */0, /*.tv_usec = */250000};
|
||||
fd_set readfds;
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(stop_readfd, &readfds);
|
||||
if(s4 >= 0)
|
||||
FD_SET(s4, &readfds);
|
||||
if(s6 >= 0)
|
||||
FD_SET(s6, &readfds);
|
||||
|
||||
int rc = select(s4 > s6 ? s4 + 1 : s6 + 1, &readfds, nullptr, nullptr, &tv);
|
||||
int rc = select(s4 > s6 ? s4 + 1 : s6 + 1, &readfds, nullptr, nullptr, nullptr);
|
||||
if(rc < 0) {
|
||||
if(errno != EINTR) {
|
||||
perror("select");
|
||||
@@ -489,11 +590,15 @@ DhtRunner::startNetwork(const SockAddr sin4, const SockAddr sin6)
|
||||
else if(s6 >= 0 && FD_ISSET(s6, &readfds))
|
||||
rc = recvfrom(s6, (char*)buf.data(), buf.size(), 0, (sockaddr*)&from, &from_len);
|
||||
else
|
||||
break;
|
||||
continue;
|
||||
if (rc > 0) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(sock_mtx);
|
||||
rcv.emplace_back(Blob {buf.begin(), buf.begin()+rc+1}, SockAddr(from, from_len));
|
||||
if (rcv.size() >= RX_QUEUE_MAX_SIZE) {
|
||||
std::cerr << "Dropping packet: queue is full!" << std::endl;
|
||||
rcv.pop();
|
||||
}
|
||||
rcv.emplace(ReceivedPacket {Blob {buf.begin(), buf.begin()+rc+1}, SockAddr(from, from_len), clock::now()});
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
@@ -510,6 +615,11 @@ DhtRunner::startNetwork(const SockAddr sin4, const SockAddr sin6)
|
||||
s6 = -1;
|
||||
bound4 = {};
|
||||
bound6 = {};
|
||||
if (stop_readfd != -1)
|
||||
close(stop_readfd);
|
||||
if (stop_writefd != -1)
|
||||
close(stop_writefd);
|
||||
stop_writefd = -1;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -519,7 +629,7 @@ DhtRunner::get(InfoHash hash, GetCallback vcb, DoneCallback dcb, Value::Filter f
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) mutable {
|
||||
dht.get(hash, vcb, dcb, std::move(f), std::move(w));
|
||||
dht.get(hash, std::move(vcb), std::move(dcb), std::move(f), std::move(w));
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
@@ -528,48 +638,48 @@ DhtRunner::get(InfoHash hash, GetCallback vcb, DoneCallback dcb, Value::Filter f
|
||||
void
|
||||
DhtRunner::get(const std::string& key, GetCallback vcb, DoneCallbackSimple dcb, Value::Filter f, Where w)
|
||||
{
|
||||
get(InfoHash::get(key), vcb, dcb, f, w);
|
||||
get(InfoHash::get(key), std::move(vcb), std::move(dcb), std::move(f), std::move(w));
|
||||
}
|
||||
void
|
||||
DhtRunner::query(const InfoHash& hash, QueryCallback cb, DoneCallback done_cb, Query q) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) mutable {
|
||||
dht.query(hash, cb, done_cb, std::move(q));
|
||||
dht.query(hash, std::move(cb), std::move(done_cb), std::move(q));
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
std::future<size_t>
|
||||
DhtRunner::listen(InfoHash hash, GetCallback vcb, Value::Filter f, Where w)
|
||||
DhtRunner::listen(InfoHash hash, ValueCallback vcb, Value::Filter f, Where w)
|
||||
{
|
||||
auto ret_token = std::make_shared<std::promise<size_t>>();
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) mutable {
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
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
|
||||
listener.gcb = [hash,vcb,tokenbGlobal,this](const std::vector<Sp<Value>>& vals, bool expired){
|
||||
if (not vcb(vals, expired)) {
|
||||
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);
|
||||
#else
|
||||
ret_token->set_value(dht.listen(hash, std::move(vcb), std::move(f), std::move(w)));
|
||||
#endif
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
@@ -579,40 +689,53 @@ DhtRunner::listen(InfoHash hash, GetCallback vcb, Value::Filter f, Where w)
|
||||
std::future<size_t>
|
||||
DhtRunner::listen(const std::string& key, GetCallback vcb, Value::Filter f, Where w)
|
||||
{
|
||||
return listen(InfoHash::get(key), vcb, f, w);
|
||||
return listen(InfoHash::get(key), std::move(vcb), std::move(f), std::move(w));
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::cancelListen(InfoHash h, size_t 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);
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
#ifdef 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);
|
||||
});
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.cancelListen(h, token);
|
||||
});
|
||||
#endif // OPENDHT_PROXY_CLIENT
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::cancelListen(InfoHash h, std::shared_future<size_t> token)
|
||||
DhtRunner::cancelListen(InfoHash h, std::shared_future<size_t> ftoken)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
auto tk = token.get();
|
||||
dht.cancelListen(h, tk);
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
pending_ops.emplace([=](SecureDht&) {
|
||||
auto it = listeners_.find(ftoken.get());
|
||||
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, ftoken.get());
|
||||
});
|
||||
#endif // OPENDHT_PROXY_CLIENT
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
@@ -645,7 +768,7 @@ DhtRunner::put(InfoHash hash, std::shared_ptr<Value> value, DoneCallback cb, tim
|
||||
void
|
||||
DhtRunner::put(const std::string& key, Value&& value, DoneCallbackSimple cb, time_point created, bool permanent)
|
||||
{
|
||||
put(InfoHash::get(key), std::forward<Value>(value), cb, created, permanent);
|
||||
put(InfoHash::get(key), std::forward<Value>(value), std::move(cb), created, permanent);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -675,13 +798,13 @@ DhtRunner::putSigned(InfoHash hash, std::shared_ptr<Value> value, DoneCallback c
|
||||
void
|
||||
DhtRunner::putSigned(InfoHash hash, Value&& value, DoneCallback cb)
|
||||
{
|
||||
putSigned(hash, std::make_shared<Value>(std::move(value)), cb);
|
||||
putSigned(hash, std::make_shared<Value>(std::move(value)), std::move(cb));
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::putSigned(const std::string& key, Value&& value, DoneCallbackSimple cb)
|
||||
{
|
||||
putSigned(InfoHash::get(key), std::forward<Value>(value), cb);
|
||||
putSigned(InfoHash::get(key), std::forward<Value>(value), std::move(cb));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -699,13 +822,13 @@ DhtRunner::putEncrypted(InfoHash hash, InfoHash to, std::shared_ptr<Value> value
|
||||
void
|
||||
DhtRunner::putEncrypted(InfoHash hash, InfoHash to, Value&& value, DoneCallback cb)
|
||||
{
|
||||
putEncrypted(hash, to, std::make_shared<Value>(std::move(value)), cb);
|
||||
putEncrypted(hash, to, std::make_shared<Value>(std::move(value)), std::move(cb));
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::putEncrypted(const std::string& key, InfoHash to, Value&& value, DoneCallback cb)
|
||||
{
|
||||
putEncrypted(InfoHash::get(key), to, std::forward<Value>(value), cb);
|
||||
putEncrypted(InfoHash::get(key), to, std::forward<Value>(value), std::move(cb));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -829,7 +952,7 @@ DhtRunner::connectivityChanged()
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
pending_ops_prio.emplace([=](SecureDht& dht) {
|
||||
dht.connectivityChanged();
|
||||
});
|
||||
}
|
||||
@@ -850,8 +973,8 @@ DhtRunner::findCertificate(InfoHash hash, std::function<void(const std::shared_p
|
||||
void
|
||||
DhtRunner::resetDht()
|
||||
{
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
listeners_.clear();
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
dht_via_proxy_.reset();
|
||||
#endif // OPENDHT_PROXY_CLIENT
|
||||
dht_.reset();
|
||||
@@ -860,17 +983,32 @@ DhtRunner::resetDht()
|
||||
SecureDht*
|
||||
DhtRunner::activeDht() const
|
||||
{
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
return use_proxy? dht_via_proxy_.get() : dht_.get();
|
||||
#else
|
||||
return dht_.get();
|
||||
#endif // OPENDHT_PROXY_CLIENT
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::setProxyServer(const std::string& proxy, const std::string& pushNodeId)
|
||||
{
|
||||
#ifdef 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());
|
||||
#else
|
||||
if (not proxy.empty())
|
||||
std::cerr << "DHT proxy requested but OpenDHT built without proxy support." << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::enableProxy(bool proxify)
|
||||
{
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
if (dht_via_proxy_) {
|
||||
dht_via_proxy_->shutdown({});
|
||||
}
|
||||
@@ -888,7 +1026,7 @@ DhtRunner::enableProxy(bool proxify)
|
||||
}, 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
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
if (not pushToken_.empty())
|
||||
dht_via_proxy_->setPushNotificationToken(pushToken_);
|
||||
#endif
|
||||
@@ -921,8 +1059,8 @@ DhtRunner::enableProxy(bool proxify)
|
||||
void
|
||||
DhtRunner::forwardAllMessages(bool forward)
|
||||
{
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
#if OPENDHT_PROXY_CLIENT
|
||||
#ifdef OPENDHT_PROXY_SERVER
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->forwardAllMessages(forward);
|
||||
#endif // OPENDHT_PROXY_CLIENT
|
||||
@@ -936,7 +1074,7 @@ DhtRunner::forwardAllMessages(bool forward)
|
||||
*/
|
||||
void
|
||||
DhtRunner::setPushNotificationToken(const std::string& token) {
|
||||
#if OPENDHT_PROXY_CLIENT && OPENDHT_PUSH_NOTIFICATIONS
|
||||
#if defined(OPENDHT_PROXY_CLIENT) && defined(OPENDHT_PUSH_NOTIFICATIONS)
|
||||
pushToken_ = token;
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->setPushNotificationToken(token);
|
||||
@@ -944,20 +1082,17 @@ DhtRunner::setPushNotificationToken(const std::string& token) {
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::pushNotificationReceived(const std::map<std::string, std::string>& data) const
|
||||
DhtRunner::pushNotificationReceived(const std::map<std::string, std::string>& data)
|
||||
{
|
||||
#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);
|
||||
#if defined(OPENDHT_PROXY_CLIENT) && defined(OPENDHT_PUSH_NOTIFICATIONS)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht&) {
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->pushNotificationReceived(data);
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Nicolas Reynaud <nicolas.reynaud@savoirfairelinux.com>
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "value.h"
|
||||
#include "utils.h"
|
||||
#include "callbacks.h"
|
||||
|
||||
namespace dht {
|
||||
|
||||
@@ -44,7 +45,7 @@ struct Listener {
|
||||
struct LocalListener {
|
||||
Sp<Query> query;
|
||||
Value::Filter filter;
|
||||
GetCallback get_cb;
|
||||
ValueCallback get_cb;
|
||||
};
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2019 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
|
||||
|
||||
+242
-153
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
@@ -122,26 +122,47 @@ serializeValues(const std::vector<Sp<Value>>& st)
|
||||
return svals;
|
||||
}
|
||||
|
||||
void
|
||||
packToken(msgpack::packer<msgpack::sbuffer>& pk, const Blob& token)
|
||||
{
|
||||
pk.pack_bin(token.size());
|
||||
pk.pack_bin_body((char*)token.data(), token.size());
|
||||
}
|
||||
|
||||
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)) {}
|
||||
: ntoken(std::move(msg.token)),
|
||||
values(std::move(msg.values)),
|
||||
refreshed_values(std::move(msg.refreshed_values)),
|
||||
expired_values(std::move(msg.expired_values)),
|
||||
fields(std::move(msg.fields)),
|
||||
nodes4(std::move(msg.nodes4)),
|
||||
nodes6(std::move(msg.nodes6))
|
||||
{}
|
||||
|
||||
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)
|
||||
: myid(zeroes), dht_socket(s), dht_socket6(s6), DHT_LOG(log), scheduler(scheduler)
|
||||
{}
|
||||
|
||||
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,
|
||||
decltype(NetworkEngine::onPing) onPing,
|
||||
decltype(NetworkEngine::onFindNode) onFindNode,
|
||||
decltype(NetworkEngine::onGetValues) onGetValues,
|
||||
decltype(NetworkEngine::onListen) onListen,
|
||||
decltype(NetworkEngine::onAnnounce) onAnnounce,
|
||||
decltype(NetworkEngine::onRefresh) onRefresh) :
|
||||
onError(onError), onNewNode(onNewNode), onReportedAddr(onReportedAddr), onPing(onPing), onFindNode(onFindNode),
|
||||
onGetValues(onGetValues), onListen(onListen), onAnnounce(onAnnounce), onRefresh(onRefresh), myid(myid),
|
||||
network(net), dht_socket(s), dht_socket6(s6), DHT_LOG(log), scheduler(scheduler)
|
||||
decltype(NetworkEngine::onError)&& onError,
|
||||
decltype(NetworkEngine::onNewNode)&& onNewNode,
|
||||
decltype(NetworkEngine::onReportedAddr)&& onReportedAddr,
|
||||
decltype(NetworkEngine::onPing)&& onPing,
|
||||
decltype(NetworkEngine::onFindNode)&& onFindNode,
|
||||
decltype(NetworkEngine::onGetValues)&& onGetValues,
|
||||
decltype(NetworkEngine::onListen)&& onListen,
|
||||
decltype(NetworkEngine::onAnnounce)&& onAnnounce,
|
||||
decltype(NetworkEngine::onRefresh)&& onRefresh) :
|
||||
onError(std::move(onError)),
|
||||
onNewNode(std::move(onNewNode)),
|
||||
onReportedAddr(std::move(onReportedAddr)),
|
||||
onPing(std::move(onPing)),
|
||||
onFindNode(std::move(onFindNode)),
|
||||
onGetValues(std::move(onGetValues)),
|
||||
onListen(std::move(onListen)),
|
||||
onAnnounce(std::move(onAnnounce)),
|
||||
onRefresh(std::move(onRefresh)),
|
||||
myid(myid), network(net), dht_socket(s), dht_socket6(s6), DHT_LOG(log), scheduler(scheduler)
|
||||
{
|
||||
if (dht_socket >= 0) {
|
||||
if (!set_nonblocking(dht_socket, 1))
|
||||
@@ -171,6 +192,67 @@ NetworkEngine::tellListener(Sp<Node> node, Tid socket_id, const InfoHash& hash,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NetworkEngine::tellListenerRefreshed(Sp<Node> n, Tid socket_id, const InfoHash&, const Blob& token, const std::vector<Value::Id>& values)
|
||||
{
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(4+(network?1:0));
|
||||
|
||||
pk.pack(KEY_U);
|
||||
pk.pack_map(1 + (not values.empty()?1:0) + (not token.empty()?1:0));
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
if (not token.empty()) {
|
||||
pk.pack(KEY_REQ_TOKEN); packToken(pk, token);
|
||||
}
|
||||
if (not values.empty()) {
|
||||
pk.pack(KEY_REQ_REFRESHED);
|
||||
pk.pack(values);
|
||||
DHT_LOG.d(n->id, "[node %s] sending %zu refreshed values", n->toString().c_str(), values.size());
|
||||
}
|
||||
|
||||
pk.pack(KEY_TID); pk.pack(socket_id);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_R);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
}
|
||||
|
||||
// send response
|
||||
send(buffer.data(), buffer.size(), 0, n->getAddr());
|
||||
}
|
||||
|
||||
void
|
||||
NetworkEngine::tellListenerExpired(Sp<Node> n, Tid socket_id, const InfoHash&, const Blob& token, const std::vector<Value::Id>& values)
|
||||
{
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(4+(network?1:0));
|
||||
|
||||
pk.pack(KEY_U);
|
||||
pk.pack_map(1 + (not values.empty()?1:0) + (not token.empty()?1:0));
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
if (not token.empty()) {
|
||||
pk.pack(KEY_REQ_TOKEN); packToken(pk, token);
|
||||
}
|
||||
if (not values.empty()) {
|
||||
pk.pack(KEY_REQ_EXPIRED);
|
||||
pk.pack(values);
|
||||
DHT_LOG.d(n->id, "[node %s] sending %zu expired values", n->toString().c_str(), values.size());
|
||||
}
|
||||
|
||||
pk.pack(KEY_TID); pk.pack(socket_id);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_R);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
}
|
||||
|
||||
// send response
|
||||
send(buffer.data(), buffer.size(), 0, n->getAddr());
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
NetworkEngine::isRunning(sa_family_t af) const
|
||||
{
|
||||
@@ -188,7 +270,13 @@ NetworkEngine::isRunning(sa_family_t af) const
|
||||
|
||||
void
|
||||
NetworkEngine::clear()
|
||||
{}
|
||||
{
|
||||
for (auto& request : requests) {
|
||||
request.second->cancel();
|
||||
request.second->node->setExpired();
|
||||
}
|
||||
requests.clear();
|
||||
}
|
||||
|
||||
void
|
||||
NetworkEngine::connectivityChanged(sa_family_t af)
|
||||
@@ -206,7 +294,7 @@ NetworkEngine::requestStep(Sp<Request> sreq)
|
||||
auto now = scheduler.time();
|
||||
auto& node = *req.node;
|
||||
if (req.isExpired(now)) {
|
||||
DHT_LOG.e(node.id, "[node %s] expired !", node.toString().c_str());
|
||||
DHT_LOG.d(node.id, "[node %s] expired !", node.toString().c_str());
|
||||
node.setExpired();
|
||||
if (not node.id)
|
||||
requests.erase(req.tid);
|
||||
@@ -221,7 +309,8 @@ NetworkEngine::requestStep(Sp<Request> sreq)
|
||||
if (err == ENETUNREACH ||
|
||||
err == EHOSTUNREACH ||
|
||||
err == EAFNOSUPPORT ||
|
||||
err == EPIPE)
|
||||
err == EPIPE ||
|
||||
err == EPERM)
|
||||
{
|
||||
node.setExpired();
|
||||
if (not node.id)
|
||||
@@ -272,9 +361,8 @@ NetworkEngine::rateLimit(const SockAddr& addr)
|
||||
limiter_maintenance = 0;
|
||||
}
|
||||
|
||||
auto it = address_rate_limiter.emplace(addr, IpLimiter{});
|
||||
// invoke per IP, then global rate limiter
|
||||
return it.first->second.limit(now) and rate_limiter.limit(now);
|
||||
return address_rate_limiter[addr].limit(now) and rate_limiter.limit(now);
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -337,8 +425,8 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, const SockAddr&
|
||||
msgpack::unpacked msg_res = msgpack::unpack((const char*)buf, buflen);
|
||||
msg->msgpack_unpack(msg_res.get());
|
||||
} catch (const std::exception& e) {
|
||||
DHT_LOG.w("Can't process message of size %lu: %s", buflen, e.what());
|
||||
DHT_LOG.DEBUG.logPrintable(buf, buflen);
|
||||
DHT_LOG.w("Can't parse message of size %lu: %s", buflen, e.what());
|
||||
//DHT_LOG.DBG.logPrintable(buf, buflen);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -353,7 +441,8 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, const SockAddr&
|
||||
if (msg->type == MessageType::ValueData) {
|
||||
auto pmsg_it = partial_messages.find(msg->tid);
|
||||
if (pmsg_it == partial_messages.end()) {
|
||||
DHT_LOG.d("Can't find partial message");
|
||||
if (logIncoming_)
|
||||
DHT_LOG.d("Can't find partial message");
|
||||
rateLimit(from);
|
||||
return;
|
||||
}
|
||||
@@ -393,15 +482,15 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, const SockAddr&
|
||||
process(std::move(msg), from);
|
||||
} else {
|
||||
// starting partial message session
|
||||
PartialMessage pmsg;
|
||||
pmsg.from = from;
|
||||
pmsg.msg = std::move(msg);
|
||||
pmsg.start = now;
|
||||
pmsg.last_part = now;
|
||||
auto wmsg = partial_messages.emplace(pmsg.msg->tid, std::move(pmsg));
|
||||
if (wmsg.second) {
|
||||
scheduler.add(now + RX_MAX_PACKET_TIME, std::bind(&NetworkEngine::maintainRxBuffer, this, wmsg.first->first));
|
||||
scheduler.add(now + RX_TIMEOUT, std::bind(&NetworkEngine::maintainRxBuffer, this, wmsg.first->first));
|
||||
auto k = msg->tid;
|
||||
auto& pmsg = partial_messages[k];
|
||||
if (not pmsg.msg) {
|
||||
pmsg.from = from;
|
||||
pmsg.msg = std::move(msg);
|
||||
pmsg.start = now;
|
||||
pmsg.last_part = now;
|
||||
scheduler.add(now + RX_MAX_PACKET_TIME, std::bind(&NetworkEngine::maintainRxBuffer, this, k));
|
||||
scheduler.add(now + RX_TIMEOUT, std::bind(&NetworkEngine::maintainRxBuffer, this, k));
|
||||
} else
|
||||
DHT_LOG.e("Partial message with given TID already exists");
|
||||
}
|
||||
@@ -463,19 +552,21 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
|
||||
req->reply_time = time_point::min();
|
||||
onError(req, DhtProtocolException {msg->error_code});
|
||||
} else {
|
||||
DHT_LOG.w(msg->id, "[node %s %s] received unknown error message %u",
|
||||
if (logIncoming_)
|
||||
DHT_LOG.w(msg->id, "[node %s %s] received unknown error message %u",
|
||||
msg->id.toString().c_str(), from.toString().c_str(), msg->error_code);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MessageType::Reply:
|
||||
if (req) { /* request reply */
|
||||
if (msg->type == MessageType::AnnounceValue or msg->type == MessageType::Listen)
|
||||
req->node->authSuccess();
|
||||
req->reply_time = scheduler.time();
|
||||
auto& r = *req;
|
||||
if (r.getType() == MessageType::AnnounceValue or r.getType() == MessageType::Listen)
|
||||
r.node->authSuccess();
|
||||
r.reply_time = scheduler.time();
|
||||
|
||||
deserializeNodes(*msg, from);
|
||||
req->setDone(std::move(*msg));
|
||||
r.setDone(std::move(*msg));
|
||||
break;
|
||||
} else { /* request socket data */
|
||||
deserializeNodes(*msg, from);
|
||||
@@ -493,7 +584,8 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
|
||||
switch (msg->type) {
|
||||
case MessageType::Ping:
|
||||
++in_stats.ping;
|
||||
DHT_LOG.d(node->id, "[node %s] sending pong", node->toString().c_str());
|
||||
if (logIncoming_)
|
||||
DHT_LOG.d(node->id, "[node %s] sending pong", node->toString().c_str());
|
||||
onPing(node);
|
||||
sendPong(from, msg->tid);
|
||||
break;
|
||||
@@ -514,7 +606,8 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
|
||||
break;
|
||||
}
|
||||
case MessageType::AnnounceValue: {
|
||||
DHT_LOG.d(msg->info_hash, node->id, "[node %s] got 'put' request for %s", node->toString().c_str(), msg->info_hash.toString().c_str());
|
||||
if (logIncoming_)
|
||||
DHT_LOG.d(msg->info_hash, node->id, "[node %s] got 'put' request for %s", node->toString().c_str(), msg->info_hash.toString().c_str());
|
||||
++in_stats.put;
|
||||
onAnnounce(node, msg->info_hash, msg->token, msg->values, msg->created);
|
||||
|
||||
@@ -527,15 +620,18 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
|
||||
break;
|
||||
}
|
||||
case MessageType::Refresh:
|
||||
DHT_LOG.d(msg->info_hash, node->id, "[node %s] got 'refresh' request for %s", node->toString().c_str(), msg->info_hash.toString().c_str());
|
||||
if (logIncoming_)
|
||||
DHT_LOG.d(msg->info_hash, node->id, "[node %s] got 'refresh' request for %s", node->toString().c_str(), msg->info_hash.toString().c_str());
|
||||
onRefresh(node, msg->info_hash, msg->token, msg->value_id);
|
||||
/* Same note as above in MessageType::AnnounceValue applies. */
|
||||
sendValueAnnounced(from, msg->tid, msg->value_id);
|
||||
break;
|
||||
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());
|
||||
if (logIncoming_)
|
||||
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, std::move(msg->query));
|
||||
auto nnodes = bufferNodes(from.getFamily(), msg->info_hash, msg->want, answer.nodes4, answer.nodes6);
|
||||
sendListenConfirmation(from, msg->tid);
|
||||
break;
|
||||
}
|
||||
@@ -550,13 +646,6 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
packToken(msgpack::packer<msgpack::sbuffer>& pk, const Blob& token)
|
||||
{
|
||||
pk.pack_bin(token.size());
|
||||
pk.pack_bin_body((char*)token.data(), token.size());
|
||||
}
|
||||
|
||||
void
|
||||
insertAddr(msgpack::packer<msgpack::sbuffer>& pk, const SockAddr& addr)
|
||||
{
|
||||
@@ -586,7 +675,7 @@ NetworkEngine::send(const char *buf, size_t len, int flags, const SockAddr& addr
|
||||
if (s < 0)
|
||||
return EAFNOSUPPORT;
|
||||
#ifdef MSG_NOSIGNAL
|
||||
flags &= MSG_NOSIGNAL;
|
||||
flags |= MSG_NOSIGNAL;
|
||||
#endif
|
||||
if (sendto(s, buf, len, flags, addr.get(), addr.getLength()) == -1) {
|
||||
int err = errno;
|
||||
@@ -606,16 +695,16 @@ NetworkEngine::sendPing(Sp<Node> node, RequestCb&& on_done, RequestExpiredCb&& o
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(network?1:0));
|
||||
|
||||
pk.pack(std::string("a")); pk.pack_map(1);
|
||||
pk.pack(std::string("id")); pk.pack(myid);
|
||||
pk.pack(KEY_A); pk.pack_map(1);
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
|
||||
pk.pack(std::string("q")); pk.pack(std::string("ping"));
|
||||
pk.pack(std::string("t")); pk.pack_bin(tid.size());
|
||||
pk.pack(KEY_Q); pk.pack(QUERY_PING);
|
||||
pk.pack(KEY_TID); pk.pack_bin(tid.size());
|
||||
pk.pack_bin_body((const char*)tid.data(), tid.size());
|
||||
pk.pack(std::string("y")); pk.pack(std::string("q"));
|
||||
pk.pack(std::string("v")); pk.pack(my_v);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_Q);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(std::string("n")); pk.pack(network);
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(MessageType::Ping, tid.toInt(), node,
|
||||
@@ -643,17 +732,17 @@ NetworkEngine::sendPong(const SockAddr& addr, Tid tid) {
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(4+(network?1:0));
|
||||
|
||||
pk.pack(std::string("r")); pk.pack_map(2);
|
||||
pk.pack(std::string("id")); pk.pack(myid);
|
||||
pk.pack(KEY_R); pk.pack_map(2);
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
insertAddr(pk, addr);
|
||||
|
||||
TransId t (tid);
|
||||
pk.pack(std::string("t")); pk.pack_bin(t.size());
|
||||
pk.pack(KEY_TID); 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);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_R);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(std::string("n")); pk.pack(network);
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
}
|
||||
|
||||
send(buffer.data(), buffer.size(), 0, addr);
|
||||
@@ -667,23 +756,23 @@ NetworkEngine::sendFindNode(Sp<Node> n, const InfoHash& target, want_t want,
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(network?1:0));
|
||||
|
||||
pk.pack(std::string("a")); pk.pack_map(2 + (want>0?1:0));
|
||||
pk.pack(std::string("id")); pk.pack(myid);
|
||||
pk.pack(std::string("target")); pk.pack(target);
|
||||
pk.pack(KEY_A); pk.pack_map(2 + (want>0?1:0));
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
pk.pack(KEY_REQ_TARGET); pk.pack(target);
|
||||
if (want > 0) {
|
||||
pk.pack(std::string("w"));
|
||||
pk.pack(KEY_REQ_WANT);
|
||||
pk.pack_array(((want & WANT4)?1:0) + ((want & WANT6)?1:0));
|
||||
if (want & WANT4) pk.pack(AF_INET);
|
||||
if (want & WANT6) pk.pack(AF_INET6);
|
||||
}
|
||||
|
||||
pk.pack(std::string("q")); pk.pack(std::string("find"));
|
||||
pk.pack(std::string("t")); pk.pack_bin(tid.size());
|
||||
pk.pack(KEY_Q); pk.pack(QUERY_FIND);
|
||||
pk.pack(KEY_TID); pk.pack_bin(tid.size());
|
||||
pk.pack_bin_body((const char*)tid.data(), tid.size());
|
||||
pk.pack(std::string("y")); pk.pack(std::string("q"));
|
||||
pk.pack(std::string("v")); pk.pack(my_v);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_Q);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(std::string("n")); pk.pack(network);
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(MessageType::FindNode, tid.toInt(), n,
|
||||
@@ -713,26 +802,26 @@ NetworkEngine::sendGetValues(Sp<Node> n, const InfoHash& info_hash, const Query&
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(network?1:0));
|
||||
|
||||
pk.pack(std::string("a")); pk.pack_map(2 +
|
||||
pk.pack(KEY_A); pk.pack_map(2 +
|
||||
(query.where.getFilter() or not query.select.getSelection().empty() ? 1:0) +
|
||||
(want>0?1:0));
|
||||
pk.pack(std::string("id")); pk.pack(myid);
|
||||
pk.pack(std::string("h")); pk.pack(info_hash);
|
||||
pk.pack(std::string("q")); pk.pack(query);
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
pk.pack(KEY_REQ_H); pk.pack(info_hash);
|
||||
pk.pack(KEY_Q); pk.pack(query);
|
||||
if (want > 0) {
|
||||
pk.pack(std::string("w"));
|
||||
pk.pack(KEY_REQ_WANT);
|
||||
pk.pack_array(((want & WANT4)?1:0) + ((want & WANT6)?1:0));
|
||||
if (want & WANT4) pk.pack(AF_INET);
|
||||
if (want & WANT6) pk.pack(AF_INET6);
|
||||
}
|
||||
|
||||
pk.pack(std::string("q")); pk.pack(std::string("get"));
|
||||
pk.pack(std::string("t")); pk.pack_bin(tid.size());
|
||||
pk.pack(KEY_Q); pk.pack(QUERY_GET);
|
||||
pk.pack(KEY_TID); pk.pack_bin(tid.size());
|
||||
pk.pack_bin_body((const char*)tid.data(), tid.size());
|
||||
pk.pack(std::string("y")); pk.pack(std::string("q"));
|
||||
pk.pack(std::string("v")); pk.pack(my_v);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_Q);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(std::string("n")); pk.pack(network);
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(MessageType::GetValues, tid.toInt(), n,
|
||||
@@ -820,13 +909,13 @@ NetworkEngine::packValueHeader(msgpack::sbuffer& buffer, const std::vector<Sp<Va
|
||||
total_size += v.size();
|
||||
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack(std::string("values"));
|
||||
pk.pack(KEY_REQ_VALUES);
|
||||
pk.pack_array(svals.size());
|
||||
// try to put everything in a single UDP packet
|
||||
if (svals.size() < 50 && total_size < MAX_PACKET_VALUE_SIZE) {
|
||||
for (const auto& b : svals)
|
||||
buffer.write((const char*)b.data(), b.size());
|
||||
DHT_LOG.d("sending %lu bytes of values", total_size);
|
||||
// DHT_LOG.d("sending %lu bytes of values", total_size);
|
||||
svals.clear();
|
||||
} else {
|
||||
for (const auto& b : svals)
|
||||
@@ -848,12 +937,12 @@ NetworkEngine::sendValueParts(const TransId& tid, const std::vector<Blob>& svals
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(3+(network?1:0));
|
||||
if (network) {
|
||||
pk.pack(std::string("n")); pk.pack(network);
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
}
|
||||
pk.pack(std::string("y")); pk.pack(std::string("v"));
|
||||
pk.pack(std::string("t")); pk.pack_bin(tid.size());
|
||||
pk.pack(KEY_Y); pk.pack(KEY_V);
|
||||
pk.pack(KEY_TID); pk.pack_bin(tid.size());
|
||||
pk.pack_bin_body((const char*)tid.data(), tid.size());
|
||||
pk.pack(std::string("p")); pk.pack_map(1);
|
||||
pk.pack(KEY_V); pk.pack_map(1);
|
||||
pk.pack(i); pk.pack_map(2);
|
||||
pk.pack(std::string("o")); pk.pack(start);
|
||||
pk.pack(std::string("d")); pk.pack_bin(end-start);
|
||||
@@ -873,22 +962,22 @@ NetworkEngine::sendNodesValues(const SockAddr& addr, Tid tid, const Blob& nodes,
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(4+(network?1:0));
|
||||
|
||||
pk.pack(std::string("r"));
|
||||
pk.pack(KEY_R);
|
||||
pk.pack_map(2 + (not st.empty()?1:0) + (nodes.size()>0?1:0) + (nodes6.size()>0?1:0) + (not token.empty()?1:0));
|
||||
pk.pack(std::string("id")); pk.pack(myid);
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
insertAddr(pk, addr);
|
||||
if (nodes.size() > 0) {
|
||||
pk.pack(std::string("n4"));
|
||||
pk.pack(KEY_REQ_NODES4);
|
||||
pk.pack_bin(nodes.size());
|
||||
pk.pack_bin_body((const char*)nodes.data(), nodes.size());
|
||||
}
|
||||
if (nodes6.size() > 0) {
|
||||
pk.pack(std::string("n6"));
|
||||
pk.pack(KEY_REQ_NODES6);
|
||||
pk.pack_bin(nodes6.size());
|
||||
pk.pack_bin_body((const char*)nodes6.data(), nodes6.size());
|
||||
}
|
||||
if (not token.empty()) {
|
||||
pk.pack(std::string("token")); packToken(pk, token);
|
||||
pk.pack(KEY_REQ_TOKEN); packToken(pk, token);
|
||||
}
|
||||
std::vector<Blob> svals {};
|
||||
if (not st.empty()) { /* pack complete values */
|
||||
@@ -896,24 +985,24 @@ NetworkEngine::sendNodesValues(const SockAddr& addr, Tid tid, const Blob& nodes,
|
||||
if (fields.empty()) {
|
||||
svals = packValueHeader(buffer, st);
|
||||
} else { /* pack fields */
|
||||
pk.pack(std::string("fields"));
|
||||
pk.pack(KEY_REQ_FIELDS);
|
||||
pk.pack_map(2);
|
||||
pk.pack(std::string("f")); pk.pack(fields);
|
||||
pk.pack(std::string("v")); pk.pack_array(st.size()*fields.size());
|
||||
for (const auto& v : st)
|
||||
v->msgpack_pack_fields(fields, pk);
|
||||
//DHT_LOG_DEBUG("sending closest nodes (%d+%d nodes.), %u value headers containing %u fields",
|
||||
//DHT_LOG_DBG("sending closest nodes (%d+%d nodes.), %u value headers containing %u fields",
|
||||
// nodes.size(), nodes6.size(), st.size(), fields.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);
|
||||
pk.pack(KEY_TID); pk.pack_bin(t.size());
|
||||
pk.pack_bin_body((const char*)t.data(), t.size());
|
||||
pk.pack(KEY_Y); pk.pack(KEY_R);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(std::string("n")); pk.pack(network);
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
}
|
||||
|
||||
// send response
|
||||
@@ -1005,23 +1094,23 @@ NetworkEngine::sendListen(Sp<Node> n,
|
||||
pk.pack_map(5+(network?1:0));
|
||||
|
||||
auto has_query = query.where.getFilter() or not query.select.getSelection().empty();
|
||||
pk.pack(std::string("a")); pk.pack_map(4 + has_query);
|
||||
pk.pack(std::string("id")); pk.pack(myid);
|
||||
pk.pack(std::string("h")); pk.pack(hash);
|
||||
pk.pack(std::string("token")); packToken(pk, token);
|
||||
pk.pack(std::string("sid")); pk.pack_bin(sid.size());
|
||||
pk.pack_bin_body((const char*)sid.data(), sid.size());
|
||||
pk.pack(KEY_A); pk.pack_map(4 + has_query);
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
pk.pack(KEY_REQ_H); pk.pack(hash);
|
||||
pk.pack(KEY_REQ_TOKEN); packToken(pk, token);
|
||||
pk.pack(KEY_REQ_SID); pk.pack_bin(sid.size());
|
||||
pk.pack_bin_body((const char*)sid.data(), sid.size());
|
||||
if (has_query) {
|
||||
pk.pack(std::string("q")); pk.pack(query);
|
||||
pk.pack(KEY_REQ_QUERY); pk.pack(query);
|
||||
}
|
||||
|
||||
pk.pack(std::string("q")); pk.pack(std::string("listen"));
|
||||
pk.pack(std::string("t")); pk.pack_bin(tid.size());
|
||||
pk.pack(KEY_Q); pk.pack(QUERY_LISTEN);
|
||||
pk.pack(KEY_TID); pk.pack_bin(tid.size());
|
||||
pk.pack_bin_body((const char*)tid.data(), tid.size());
|
||||
pk.pack(std::string("y")); pk.pack(std::string("q"));
|
||||
pk.pack(std::string("v")); pk.pack(my_v);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_Q);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(std::string("n")); pk.pack(network);
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(MessageType::Listen, tid.toInt(), n,
|
||||
@@ -1047,17 +1136,17 @@ NetworkEngine::sendListenConfirmation(const SockAddr& addr, Tid tid) {
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(4+(network?1:0));
|
||||
|
||||
pk.pack(std::string("r")); pk.pack_map(2);
|
||||
pk.pack(std::string("id")); pk.pack(myid);
|
||||
pk.pack(KEY_R); pk.pack_map(2);
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
insertAddr(pk, addr);
|
||||
|
||||
TransId t (tid);
|
||||
pk.pack(std::string("t")); pk.pack_bin(t.size());
|
||||
pk.pack(KEY_TID); 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);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_R);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(std::string("n")); pk.pack(network);
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
}
|
||||
|
||||
send(buffer.data(), buffer.size(), 0, addr);
|
||||
@@ -1077,23 +1166,23 @@ NetworkEngine::sendAnnounceValue(Sp<Node> n,
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(network?1:0));
|
||||
|
||||
pk.pack(std::string("a")); pk.pack_map((created < scheduler.time() ? 5 : 4));
|
||||
pk.pack(std::string("id")); pk.pack(myid);
|
||||
pk.pack(std::string("h")); pk.pack(infohash);
|
||||
pk.pack(KEY_A); pk.pack_map((created < scheduler.time() ? 5 : 4));
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
pk.pack(KEY_REQ_H); pk.pack(infohash);
|
||||
auto v = packValueHeader(buffer, {value});
|
||||
if (created < scheduler.time()) {
|
||||
pk.pack(std::string("c"));
|
||||
pk.pack(KEY_REQ_CREATION);
|
||||
pk.pack(to_time_t(created));
|
||||
}
|
||||
pk.pack(std::string("token")); pk.pack(token);
|
||||
pk.pack(KEY_REQ_TOKEN); pk.pack(token);
|
||||
|
||||
pk.pack(std::string("q")); pk.pack(std::string("put"));
|
||||
pk.pack(std::string("t")); pk.pack_bin(tid.size());
|
||||
pk.pack_bin_body((const char*)tid.data(), tid.size());
|
||||
pk.pack(std::string("y")); pk.pack(std::string("q"));
|
||||
pk.pack(std::string("v")); pk.pack(my_v);
|
||||
pk.pack(KEY_Q); pk.pack(QUERY_PUT);
|
||||
pk.pack(KEY_TID); pk.pack_bin(tid.size());
|
||||
pk.pack_bin_body((const char*)tid.data(), tid.size());
|
||||
pk.pack(KEY_Y); pk.pack(KEY_Q);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(std::string("n")); pk.pack(network);
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(MessageType::AnnounceValue, tid.toInt(), n,
|
||||
@@ -1135,19 +1224,19 @@ NetworkEngine::sendRefreshValue(Sp<Node> n,
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(network?1:0));
|
||||
|
||||
pk.pack(std::string("a")); pk.pack_map(4);
|
||||
pk.pack(std::string("id")); pk.pack(myid);
|
||||
pk.pack(std::string("h")); pk.pack(infohash);
|
||||
pk.pack(std::string("vid")); pk.pack(vid);
|
||||
pk.pack(std::string("token")); pk.pack(token);
|
||||
pk.pack(KEY_A); pk.pack_map(4);
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
pk.pack(KEY_REQ_H); pk.pack(infohash);
|
||||
pk.pack(KEY_REQ_VALUE_ID); pk.pack(vid);
|
||||
pk.pack(KEY_REQ_TOKEN); pk.pack(token);
|
||||
|
||||
pk.pack(std::string("q")); pk.pack(std::string("refresh"));
|
||||
pk.pack(std::string("t")); pk.pack_bin(tid.size());
|
||||
pk.pack(KEY_Q); pk.pack(QUERY_REFRESH);
|
||||
pk.pack(KEY_TID); pk.pack_bin(tid.size());
|
||||
pk.pack_bin_body((const char*)tid.data(), tid.size());
|
||||
pk.pack(std::string("y")); pk.pack(std::string("q"));
|
||||
pk.pack(std::string("v")); pk.pack(my_v);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_Q);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(std::string("n")); pk.pack(network);
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(MessageType::Refresh, tid.toInt(), n,
|
||||
@@ -1180,18 +1269,18 @@ NetworkEngine::sendValueAnnounced(const SockAddr& addr, Tid tid, Value::Id vid)
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(4+(network?1:0));
|
||||
|
||||
pk.pack(std::string("r")); pk.pack_map(3);
|
||||
pk.pack(std::string("id")); pk.pack(myid);
|
||||
pk.pack(std::string("vid")); pk.pack(vid);
|
||||
pk.pack(KEY_R); pk.pack_map(3);
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
pk.pack(KEY_REQ_VALUE_ID); pk.pack(vid);
|
||||
insertAddr(pk, addr);
|
||||
|
||||
TransId t(tid);
|
||||
pk.pack(std::string("t")); pk.pack_bin(t.size());
|
||||
pk.pack(KEY_TID); 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);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_R);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(std::string("n")); pk.pack(network);
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
}
|
||||
|
||||
send(buffer.data(), buffer.size(), 0, addr);
|
||||
@@ -1208,22 +1297,22 @@ NetworkEngine::sendError(const SockAddr& addr,
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(4 + (include_id?1:0));
|
||||
|
||||
pk.pack(std::string("e")); pk.pack_array(2);
|
||||
pk.pack(KEY_E); pk.pack_array(2);
|
||||
pk.pack(code);
|
||||
pk.pack(message);
|
||||
|
||||
if (include_id) {
|
||||
pk.pack(std::string("r")); pk.pack_map(1);
|
||||
pk.pack(std::string("id")); pk.pack(myid);
|
||||
pk.pack(KEY_R); pk.pack_map(1);
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
}
|
||||
|
||||
TransId t(tid);
|
||||
pk.pack(std::string("t")); pk.pack_bin(t.size());
|
||||
pk.pack(KEY_TID); 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);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_E);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(std::string("n")); pk.pack(network);
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
}
|
||||
|
||||
send(buffer.data(), buffer.size(), 0, addr);
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
@@ -49,7 +49,7 @@ Node::isGood(time_point now) const
|
||||
bool
|
||||
Node::isPendingMessage() const
|
||||
{
|
||||
for (auto r : requests_) {
|
||||
for (const auto& r : requests_) {
|
||||
if (r.second->pending())
|
||||
return true;
|
||||
}
|
||||
@@ -60,7 +60,7 @@ size_t
|
||||
Node::getPendingMessageCount() const
|
||||
{
|
||||
size_t count {0};
|
||||
for (auto r : requests_) {
|
||||
for (const auto& r : requests_) {
|
||||
if (r.second->pending())
|
||||
count++;
|
||||
}
|
||||
|
||||
+39
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,6 +20,9 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
constexpr size_t CLEANUP_MAX_NODES {1024};
|
||||
constexpr size_t CLEANUP_FREQ {1024};
|
||||
|
||||
NodeCache::~NodeCache()
|
||||
{
|
||||
cache_4.setExpired();
|
||||
@@ -41,29 +44,33 @@ NodeCache::getNode(const InfoHash& id, const SockAddr& addr, time_point now, boo
|
||||
std::vector<Sp<Node>>
|
||||
NodeCache::getCachedNodes(const InfoHash& id, sa_family_t sa_f, size_t count) const
|
||||
{
|
||||
const auto& c = cache(sa_f);
|
||||
return cache(sa_f).getCachedNodes(id, count);
|
||||
}
|
||||
|
||||
std::vector<Sp<Node>>
|
||||
NodeCache::NodeMap::getCachedNodes(const InfoHash& id, size_t count) const
|
||||
{
|
||||
std::vector<Sp<Node>> nodes;
|
||||
nodes.reserve(std::min(c.size(), count));
|
||||
NodeMap::const_iterator it;
|
||||
auto dec_it = [&c](NodeMap::const_iterator& it) {
|
||||
nodes.reserve(std::min(size(), count));
|
||||
const_iterator it;
|
||||
auto dec_it = [this](const_iterator& it) {
|
||||
auto ret = it;
|
||||
it = (it == c.cbegin()) ? c.cend() : std::prev(it);
|
||||
it = (it == cbegin()) ? cend() : std::prev(it);
|
||||
return ret;
|
||||
};
|
||||
|
||||
auto it_p = c.lower_bound(id),
|
||||
auto it_p = lower_bound(id),
|
||||
it_n = it_p;
|
||||
if (not c.empty())
|
||||
if (not empty())
|
||||
dec_it(it_p); /* Create 2 separate iterator if we could */
|
||||
|
||||
while (nodes.size() < count and (it_n != c.cend() or it_p != c.cend())) {
|
||||
while (nodes.size() < count and (it_n != cend() or it_p != cend())) {
|
||||
/* If one of the iterator is at the end, then take the other one
|
||||
If they are both in middle of somewhere comapre both and take
|
||||
the closest to the id. */
|
||||
if (it_p == c.cend()) it = it_n++;
|
||||
else if (it_n == c.cend()) it = dec_it(it_p);
|
||||
else it = id.xorCmp(it_p->first, it_n->first) < 0 ? dec_it(it_p) : it_n++;
|
||||
if (it_p == cend()) it = it_n++;
|
||||
else if (it_n == cend()) it = dec_it(it_p);
|
||||
else it = id.xorCmp(it_p->first, it_n->first) < 0 ? dec_it(it_p) : it_n++;
|
||||
|
||||
if (auto n = it->second.lock())
|
||||
if ( not n->isExpired() and not n->isClient() )
|
||||
@@ -104,6 +111,10 @@ NodeCache::NodeMap::getNode(const InfoHash& id, const SockAddr& addr, time_point
|
||||
if (not node) {
|
||||
node = std::make_shared<Node>(id, addr, client);
|
||||
it.first->second = node;
|
||||
if (cleanup_counter++ == CLEANUP_FREQ) {
|
||||
cleanup();
|
||||
cleanup_counter = 0;
|
||||
}
|
||||
} else if (confirm or node->isOld(now)) {
|
||||
node->update(addr);
|
||||
}
|
||||
@@ -120,6 +131,7 @@ NodeCache::NodeMap::clearBadNodes() {
|
||||
erase(it++);
|
||||
}
|
||||
}
|
||||
cleanup_counter = 0;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -128,6 +140,21 @@ NodeCache::NodeMap::setExpired() {
|
||||
if (auto n = wn.second.lock())
|
||||
n->setExpired();
|
||||
clear();
|
||||
cleanup_counter = 0;
|
||||
}
|
||||
|
||||
void
|
||||
NodeCache::NodeMap::cleanup()
|
||||
{
|
||||
auto it = lower_bound(InfoHash::getRandom());
|
||||
for (size_t n = 0, maxNodes = std::min(size(), CLEANUP_MAX_NODES); n != maxNodes; n++) {
|
||||
if (it == end())
|
||||
it = begin();
|
||||
if (it->second.expired())
|
||||
erase(it++);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "op_cache.h"
|
||||
|
||||
namespace dht {
|
||||
|
||||
constexpr const std::chrono::seconds OpCache::EXPIRATION;
|
||||
|
||||
bool
|
||||
OpValueCache::onValuesAdded(const std::vector<Sp<Value>>& vals) {
|
||||
std::vector<Sp<Value>> newValues;
|
||||
for (const auto& v : vals) {
|
||||
auto viop = values.emplace(v->id, v);
|
||||
if (viop.second) {
|
||||
newValues.emplace_back(v);
|
||||
} else {
|
||||
viop.first->second.refCount++;
|
||||
}
|
||||
}
|
||||
return newValues.empty() ? true : callback(newValues, false);
|
||||
}
|
||||
|
||||
bool
|
||||
OpValueCache::onValuesExpired(const std::vector<Sp<Value>>& vals) {
|
||||
std::vector<Sp<Value>> expiredValues;
|
||||
for (const auto& v : vals) {
|
||||
auto vit = values.find(v->id);
|
||||
if (vit != values.end()) {
|
||||
vit->second.refCount--;
|
||||
if (not vit->second.refCount) {
|
||||
expiredValues.emplace_back(std::move(vit->second.data));
|
||||
values.erase(vit);
|
||||
}
|
||||
}
|
||||
}
|
||||
return expiredValues.empty() ? true : callback(expiredValues, true);
|
||||
}
|
||||
|
||||
bool
|
||||
OpValueCache::onValuesExpired(const std::vector<Value::Id>& vids)
|
||||
{
|
||||
std::vector<Sp<Value>> expiredValues;
|
||||
for (const auto& vid : vids) {
|
||||
auto vit = values.find(vid);
|
||||
if (vit != values.end()) {
|
||||
vit->second.refCount--;
|
||||
if (not vit->second.refCount) {
|
||||
expiredValues.emplace_back(std::move(vit->second.data));
|
||||
values.erase(vit);
|
||||
}
|
||||
}
|
||||
}
|
||||
return expiredValues.empty() ? true : callback(expiredValues, true);
|
||||
}
|
||||
|
||||
std::vector<Sp<Value>>
|
||||
OpValueCache::get(const Value::Filter& filter) const {
|
||||
std::vector<Sp<Value>> ret;
|
||||
if (not filter)
|
||||
ret.reserve(values.size());
|
||||
for (const auto& v : values)
|
||||
if (not filter or filter(*v.second.data))
|
||||
ret.emplace_back(v.second.data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Sp<Value>
|
||||
OpValueCache::get(Value::Id id) const {
|
||||
auto v = values.find(id);
|
||||
if (v == values.end())
|
||||
return {};
|
||||
return v->second.data;
|
||||
}
|
||||
|
||||
std::vector<Sp<Value>>
|
||||
OpValueCache::getValues() const {
|
||||
std::vector<Sp<Value>> ret;
|
||||
ret.reserve(values.size());
|
||||
for (const auto& v : values)
|
||||
ret.emplace_back(v.second.data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
OpCache::onValuesAdded(const std::vector<Sp<Value>>& vals) {
|
||||
if (not listeners.empty()) {
|
||||
std::vector<LocalListener> list;
|
||||
list.reserve(listeners.size());
|
||||
for (const auto& l : listeners)
|
||||
list.emplace_back(l.second);
|
||||
for (auto& l : list)
|
||||
l.get_cb(l.filter.filter(vals), false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
OpCache::onValuesExpired(const std::vector<Sp<Value>>& vals) {
|
||||
if (not listeners.empty()) {
|
||||
std::vector<LocalListener> list;
|
||||
list.reserve(listeners.size());
|
||||
for (const auto& l : listeners)
|
||||
list.emplace_back(l.second);
|
||||
for (auto& l : list)
|
||||
l.get_cb(l.filter.filter(vals), true);
|
||||
}
|
||||
}
|
||||
|
||||
time_point
|
||||
OpCache::getExpiration() const {
|
||||
if (not listeners.empty())
|
||||
return time_point::max();
|
||||
return lastRemoved + EXPIRATION;
|
||||
}
|
||||
|
||||
SearchCache::OpMap::iterator
|
||||
SearchCache::getOp(const Sp<Query>& q)
|
||||
{
|
||||
// find exact match
|
||||
auto op = ops.find(q);
|
||||
if (op != ops.end())
|
||||
return op;
|
||||
// find satisfying query
|
||||
for (auto it = ops.begin(); it != ops.end(); it++) {
|
||||
if (q->isSatisfiedBy(*it->first)) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return ops.end();
|
||||
}
|
||||
|
||||
SearchCache::OpMap::const_iterator
|
||||
SearchCache::getOp(const Sp<Query>& q) const
|
||||
{
|
||||
// find exact match
|
||||
auto op = ops.find(q);
|
||||
if (op != ops.cend())
|
||||
return op;
|
||||
// find satisfying query
|
||||
for (auto it = ops.begin(); it != ops.end(); it++) {
|
||||
if (q->isSatisfiedBy(*it->first)) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return ops.cend();
|
||||
}
|
||||
|
||||
size_t
|
||||
SearchCache::listen(const ValueCallback& get_cb, const Sp<Query>& q, const Value::Filter& filter, const OnListen& onListen)
|
||||
{
|
||||
// find exact match
|
||||
auto op = getOp(q);
|
||||
if (op == ops.end()) {
|
||||
// New query
|
||||
op = ops.emplace(q, std::unique_ptr<OpCache>(new OpCache)).first;
|
||||
auto& cache = *op->second;
|
||||
cache.searchToken = onListen(q, [&](const std::vector<Sp<Value>>& values, bool expired){
|
||||
return cache.onValue(values, expired);
|
||||
}, [&](ListenSyncStatus status) {
|
||||
cache.onNodeChanged(status);
|
||||
});
|
||||
}
|
||||
auto token = nextToken_++;
|
||||
if (nextToken_ == 0)
|
||||
nextToken_++;
|
||||
return op->second->addListener(token, get_cb, q, filter) ? token : 0;
|
||||
}
|
||||
|
||||
bool
|
||||
SearchCache::cancelListen(size_t gtoken, const time_point& now) {
|
||||
for (auto& op : ops) {
|
||||
if (op.second->removeListener(gtoken, now)) {
|
||||
nextExpiration_ = std::min(nextExpiration_, op.second->getExpiration());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
SearchCache::cancelAll(const std::function<void(size_t)>& onCancel) {
|
||||
for (auto& op : ops) {
|
||||
auto cache = std::move(op.second);
|
||||
cache->removeAll();
|
||||
onCancel(cache->searchToken);
|
||||
}
|
||||
ops.clear();
|
||||
}
|
||||
|
||||
time_point
|
||||
SearchCache::expire(const time_point& now, const std::function<void(size_t)>& onCancel) {
|
||||
nextExpiration_ = time_point::max();
|
||||
auto ret = nextExpiration_;
|
||||
for (auto it = ops.begin(); it != ops.end();) {
|
||||
auto expiration = it->second->getExpiration();
|
||||
if (expiration < now) {
|
||||
auto cache = std::move(it->second);
|
||||
it = ops.erase(it);
|
||||
onCancel(cache->searchToken);
|
||||
} else {
|
||||
nextExpiration_ = std::min(nextExpiration_, expiration);
|
||||
ret = nextExpiration_;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
SearchCache::get(const Value::Filter& f, const Sp<Query>& q, const GetCallback& gcb, const DoneCallback& dcb) const
|
||||
{
|
||||
auto op = getOp(q);
|
||||
if (op != ops.end()) {
|
||||
auto vals = op->second->get(f);
|
||||
if ((not vals.empty() and not gcb(vals)) or op->second->isSynced()) {
|
||||
dcb(true, {});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<Sp<Value>>
|
||||
SearchCache::get(const Value::Filter& filter) const {
|
||||
if (ops.size() == 1)
|
||||
return ops.begin()->second->get(filter);
|
||||
std::map<Value::Id, Sp<Value>> c;
|
||||
for (const auto& op : ops) {
|
||||
for (const auto& v : op.second->get(filter))
|
||||
c.emplace(v->id, v);
|
||||
}
|
||||
std::vector<Sp<Value>> ret;
|
||||
ret.reserve(c.size());
|
||||
for (auto& v : c)
|
||||
ret.emplace_back(std::move(v.second));
|
||||
return ret;
|
||||
}
|
||||
|
||||
Sp<Value>
|
||||
SearchCache::get(Value::Id id) const {
|
||||
for (const auto& op : ops)
|
||||
if (auto v = op.second->get(id))
|
||||
return v;
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "value.h"
|
||||
#include "value_cache.h"
|
||||
#include "listener.h"
|
||||
|
||||
namespace dht {
|
||||
|
||||
struct OpCacheValueStorage
|
||||
{
|
||||
Sp<Value> data {};
|
||||
unsigned refCount {1};
|
||||
OpCacheValueStorage(Sp<Value> val) : data(val) {}
|
||||
};
|
||||
|
||||
class OpValueCache {
|
||||
public:
|
||||
OpValueCache(ValueCallback&& cb) noexcept : callback(std::forward<ValueCallback>(cb)) {}
|
||||
OpValueCache(OpValueCache&& o) noexcept : values(std::move(o.values)), callback(std::move(o.callback)) {
|
||||
o.callback = {};
|
||||
}
|
||||
|
||||
static ValueCallback cacheCallback(ValueCallback&& cb, std::function<void()>&& onCancel) {
|
||||
auto cache = std::make_shared<OpValueCache>(std::forward<ValueCallback>(cb));
|
||||
return [cache, onCancel](const std::vector<Sp<Value>>& vals, bool expired){
|
||||
auto ret = cache->onValue(vals, expired);
|
||||
if (not ret)
|
||||
onCancel();
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
|
||||
bool onValue(const std::vector<Sp<Value>>& vals, bool expired) {
|
||||
if (expired)
|
||||
return onValuesExpired(vals);
|
||||
else
|
||||
return onValuesAdded(vals);
|
||||
}
|
||||
|
||||
bool onValuesAdded(const std::vector<Sp<Value>>& vals);
|
||||
bool onValuesExpired(const std::vector<Sp<Value>>& vals);
|
||||
bool onValuesExpired(const std::vector<Value::Id>& vals);
|
||||
|
||||
void onNodeChanged(ListenSyncStatus status) {
|
||||
switch (status) {
|
||||
case ListenSyncStatus::ADDED: nodes++; break;
|
||||
case ListenSyncStatus::REMOVED: nodes--; break;
|
||||
case ListenSyncStatus::SYNCED : syncedNodes++; break;
|
||||
case ListenSyncStatus::UNSYNCED: syncedNodes--; break;
|
||||
}
|
||||
}
|
||||
|
||||
bool isSynced() const { return nodes > 0 and syncedNodes == nodes; }
|
||||
|
||||
std::vector<Sp<Value>> get(const Value::Filter& filter) const;
|
||||
Sp<Value> get(Value::Id id) const;
|
||||
std::vector<Sp<Value>> getValues() const;
|
||||
|
||||
private:
|
||||
OpValueCache(const OpValueCache&) = delete;
|
||||
OpValueCache& operator=(const OpValueCache&) = delete;
|
||||
|
||||
size_t nodes {0};
|
||||
size_t syncedNodes {0};
|
||||
std::map<Value::Id, OpCacheValueStorage> values {};
|
||||
ValueCallback callback;
|
||||
};
|
||||
|
||||
class OpCache {
|
||||
public:
|
||||
OpCache() : cache([this](const std::vector<Sp<Value>>& vals, bool expired){
|
||||
if (expired)
|
||||
onValuesExpired(vals);
|
||||
else
|
||||
onValuesAdded(vals);
|
||||
return true;
|
||||
}) {}
|
||||
|
||||
bool onValue(const std::vector<Sp<Value>>& vals, bool expired) {
|
||||
cache.onValue(vals, expired);
|
||||
return not listeners.empty();
|
||||
}
|
||||
void onNodeChanged(ListenSyncStatus status) {
|
||||
cache.onNodeChanged(status);
|
||||
}
|
||||
|
||||
void onValuesAdded(const std::vector<Sp<Value>>& vals);
|
||||
void onValuesExpired(const std::vector<Sp<Value>>& vals);
|
||||
|
||||
bool addListener(size_t token, const ValueCallback& cb, const Sp<Query>& q, const Value::Filter& filter) {
|
||||
listeners.emplace(token, LocalListener{q, filter, cb});
|
||||
auto cached = cache.get(filter);
|
||||
if (not cached.empty()) {
|
||||
auto ret = cb(cached, false);
|
||||
if (not ret)
|
||||
listeners.erase(token);
|
||||
return ret;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool removeListener(size_t token, const time_point& now) {
|
||||
lastRemoved = now;
|
||||
return listeners.erase(token) > 0;
|
||||
}
|
||||
|
||||
void removeAll() {
|
||||
listeners.clear();
|
||||
}
|
||||
|
||||
bool isDone() {
|
||||
return listeners.empty();
|
||||
}
|
||||
|
||||
std::vector<Sp<Value>> get(const Value::Filter& filter) const {
|
||||
return cache.get(filter);
|
||||
}
|
||||
|
||||
Sp<Value> get(Value::Id id) const {
|
||||
return cache.get(id);
|
||||
}
|
||||
|
||||
bool isSynced() const {
|
||||
return cache.isSynced();
|
||||
}
|
||||
|
||||
bool isExpired(const time_point& now) const {
|
||||
return listeners.empty() and (lastRemoved + EXPIRATION < now);
|
||||
}
|
||||
time_point getExpiration() const;
|
||||
|
||||
size_t searchToken;
|
||||
private:
|
||||
constexpr static const std::chrono::seconds EXPIRATION {60};
|
||||
OpCache(const OpCache&) = delete;
|
||||
OpCache& operator=(const OpCache&) = delete;
|
||||
|
||||
OpValueCache cache;
|
||||
std::map<size_t, LocalListener> listeners;
|
||||
time_point lastRemoved {clock::now()};
|
||||
};
|
||||
|
||||
class SearchCache {
|
||||
public:
|
||||
SearchCache() {}
|
||||
SearchCache(SearchCache&&) = default;
|
||||
|
||||
using OnListen = std::function<size_t(Sp<Query>, ValueCallback, SyncCallback)>;
|
||||
size_t listen(const ValueCallback& get_cb, const Sp<Query>& q, const Value::Filter& filter, const OnListen& onListen);
|
||||
|
||||
bool cancelListen(size_t gtoken, const time_point& now);
|
||||
void cancelAll(const std::function<void(size_t)>& onCancel);
|
||||
|
||||
time_point expire(const time_point& now, const std::function<void(size_t)>& onCancel);
|
||||
time_point getExpiration() const {
|
||||
return nextExpiration_;
|
||||
}
|
||||
|
||||
bool get(const Value::Filter& f, const Sp<Query>& q, const GetCallback& gcb, const DoneCallback& dcb) const;
|
||||
std::vector<Sp<Value>> get(const Value::Filter& filter) const;
|
||||
Sp<Value> get(Value::Id id) const;
|
||||
|
||||
private:
|
||||
SearchCache(const SearchCache&) = delete;
|
||||
SearchCache& operator=(const SearchCache&) = delete;
|
||||
|
||||
using OpMap = std::map<Sp<Query>, std::unique_ptr<OpCache>>;
|
||||
OpMap ops {};
|
||||
OpMap::iterator getOp(const Sp<Query>& q);
|
||||
OpMap::const_iterator getOp(const Sp<Query>& q) const;
|
||||
|
||||
size_t nextToken_ {1};
|
||||
time_point nextExpiration_ {time_point::max()};
|
||||
};
|
||||
|
||||
}
|
||||
+158
-89
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2019 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
|
||||
@@ -26,6 +26,42 @@
|
||||
namespace dht {
|
||||
namespace net {
|
||||
|
||||
static const std::string KEY_Y {"y"};
|
||||
static const std::string KEY_R {"r"};
|
||||
static const std::string KEY_U {"u"};
|
||||
static const std::string KEY_E {"e"};
|
||||
static const std::string KEY_V {"p"};
|
||||
static const std::string KEY_TID {"t"};
|
||||
static const std::string KEY_UA {"v"};
|
||||
static const std::string KEY_NETID {"n"};
|
||||
static const std::string KEY_ISCLIENT {"s"};
|
||||
static const std::string KEY_Q {"q"};
|
||||
static const std::string KEY_A {"a"};
|
||||
|
||||
static const std::string KEY_REQ_SID {"sid"};
|
||||
static const std::string KEY_REQ_ID {"id"};
|
||||
static const std::string KEY_REQ_H {"h"};
|
||||
static const std::string KEY_REQ_TARGET {"target"};
|
||||
static const std::string KEY_REQ_QUERY {"q"};
|
||||
static const std::string KEY_REQ_TOKEN {"token"};
|
||||
static const std::string KEY_REQ_VALUE_ID {"vid"};
|
||||
static const std::string KEY_REQ_NODES4 {"n4"};
|
||||
static const std::string KEY_REQ_NODES6 {"n6"};
|
||||
static const std::string KEY_REQ_CREATION {"c"};
|
||||
static const std::string KEY_REQ_ADDRESS {"sa"};
|
||||
static const std::string KEY_REQ_VALUES {"values"};
|
||||
static const std::string KEY_REQ_EXPIRED {"exp"};
|
||||
static const std::string KEY_REQ_REFRESHED {"re"};
|
||||
static const std::string KEY_REQ_FIELDS {"fileds"};
|
||||
static const std::string KEY_REQ_WANT {"w"};
|
||||
|
||||
static const std::string QUERY_PING {"ping"};
|
||||
static const std::string QUERY_FIND {"find"};
|
||||
static const std::string QUERY_GET {"get"};
|
||||
static const std::string QUERY_PUT {"put"};
|
||||
static const std::string QUERY_LISTEN {"listen"};
|
||||
static const std::string QUERY_REFRESH {"refresh"};
|
||||
|
||||
Tid unpackTid(const msgpack::object& o) {
|
||||
switch (o.type) {
|
||||
case msgpack::type::POSITIVE_INTEGER:
|
||||
@@ -62,6 +98,8 @@ struct ParsedMessage {
|
||||
std::vector<Sp<Node>> nodes4, nodes6;
|
||||
/* values to store or retreive request */
|
||||
std::vector<Sp<Value>> values;
|
||||
std::vector<Value::Id> refreshed_values {};
|
||||
std::vector<Value::Id> expired_values {};
|
||||
/* index for fields values */
|
||||
std::vector<Sp<FieldValueIndex>> fields;
|
||||
/** When part of the message header: {index -> (total size, {})}
|
||||
@@ -76,7 +114,7 @@ struct ParsedMessage {
|
||||
/* reported address by the distant node */
|
||||
std::string ua;
|
||||
SockAddr addr;
|
||||
void msgpack_unpack(msgpack::object o);
|
||||
void msgpack_unpack(const msgpack::object& o);
|
||||
|
||||
bool append(const ParsedMessage& block);
|
||||
bool complete();
|
||||
@@ -121,63 +159,79 @@ ParsedMessage::complete()
|
||||
}
|
||||
|
||||
void
|
||||
ParsedMessage::msgpack_unpack(msgpack::object msg)
|
||||
ParsedMessage::msgpack_unpack(const 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");
|
||||
if (msg.type != msgpack::type::MAP) throw msgpack::type_error();
|
||||
|
||||
if (auto t = findMapValue(msg, "t"))
|
||||
tid = unpackTid(*t);
|
||||
struct ParsedMsg {
|
||||
msgpack::object* y;
|
||||
msgpack::object* r;
|
||||
msgpack::object* u;
|
||||
msgpack::object* e;
|
||||
msgpack::object* v;
|
||||
msgpack::object* a;
|
||||
std::string q;
|
||||
} parsed {};
|
||||
|
||||
if (auto rv = findMapValue(msg, "v"))
|
||||
ua = rv->as<std::string>();
|
||||
|
||||
if (auto netid = findMapValue(msg, "n"))
|
||||
network = netid->as<NetId>();
|
||||
|
||||
if (auto is_client_v = findMapValue(msg, "s"))
|
||||
is_client = is_client_v->as<bool>();
|
||||
|
||||
std::string q;
|
||||
if (auto rq = findMapValue(msg, "q")) {
|
||||
if (rq->type != msgpack::type::STR)
|
||||
throw msgpack::type_error();
|
||||
q = rq->as<std::string>();
|
||||
for (unsigned i = 0; i < msg.via.map.size; i++) {
|
||||
auto& o = msg.via.map.ptr[i];
|
||||
if (o.key.type != msgpack::type::STR)
|
||||
continue;
|
||||
auto key = o.key.as<std::string>();
|
||||
if (key == KEY_Y)
|
||||
parsed.y = &o.val;
|
||||
else if (key == KEY_R)
|
||||
parsed.r = &o.val;
|
||||
else if (key == KEY_U)
|
||||
parsed.u = &o.val;
|
||||
else if (key == KEY_E)
|
||||
parsed.e = &o.val;
|
||||
else if (key == KEY_V)
|
||||
parsed.v = &o.val;
|
||||
else if (key == KEY_TID)
|
||||
tid = unpackTid(o.val);
|
||||
else if (key == KEY_UA)
|
||||
ua = o.val.as<std::string>();
|
||||
else if (key == KEY_NETID)
|
||||
network = o.val.as<NetId>();
|
||||
else if (key == KEY_ISCLIENT)
|
||||
is_client = o.val.as<bool>();
|
||||
else if (key == KEY_Q)
|
||||
parsed.q = o.val.as<std::string>();
|
||||
else if (key == KEY_A)
|
||||
parsed.a = &o.val;
|
||||
}
|
||||
|
||||
if (e)
|
||||
if (parsed.e)
|
||||
type = MessageType::Error;
|
||||
else if (r)
|
||||
else if (parsed.r)
|
||||
type = MessageType::Reply;
|
||||
else if (v)
|
||||
else if (parsed.v)
|
||||
type = MessageType::ValueData;
|
||||
else if (u)
|
||||
else if (parsed.u)
|
||||
type = MessageType::ValueUpdate;
|
||||
else if (y and y->as<std::string>() != "q")
|
||||
else if (parsed.y and parsed.y->as<std::string>() != "q")
|
||||
throw msgpack::type_error();
|
||||
else if (q == "ping")
|
||||
else if (parsed.q == QUERY_PING)
|
||||
type = MessageType::Ping;
|
||||
else if (q == "find")
|
||||
else if (parsed.q == QUERY_FIND)
|
||||
type = MessageType::FindNode;
|
||||
else if (q == "get")
|
||||
else if (parsed.q == QUERY_GET)
|
||||
type = MessageType::GetValues;
|
||||
else if (q == "listen")
|
||||
else if (parsed.q == QUERY_LISTEN)
|
||||
type = MessageType::Listen;
|
||||
else if (q == "put")
|
||||
else if (parsed.q == QUERY_PUT)
|
||||
type = MessageType::AnnounceValue;
|
||||
else if (q == "refresh")
|
||||
else if (parsed.q == QUERY_REFRESH)
|
||||
type = MessageType::Refresh;
|
||||
else
|
||||
throw msgpack::type_error();
|
||||
|
||||
if (type == MessageType::ValueData) {
|
||||
if (v->type != msgpack::type::MAP)
|
||||
if (parsed.v->type != msgpack::type::MAP)
|
||||
throw msgpack::type_error();
|
||||
for (size_t i = 0; i < v->via.map.size; ++i) {
|
||||
auto& vdat = v->via.map.ptr[i];
|
||||
for (size_t i = 0; i < parsed.v->via.map.size; ++i) {
|
||||
auto& vdat = parsed.v->via.map.ptr[i];
|
||||
auto o = findMapValue(vdat.val, "o");
|
||||
auto d = findMapValue(vdat.val, "d");
|
||||
if (not o or not d)
|
||||
@@ -187,70 +241,85 @@ ParsedMessage::msgpack_unpack(msgpack::object msg)
|
||||
return;
|
||||
}
|
||||
|
||||
auto a = findMapValue(msg, "a");
|
||||
if (!a && !r && !e && !u)
|
||||
if (!parsed.a && !parsed.r && !parsed.e && !parsed.u)
|
||||
throw msgpack::type_error();
|
||||
auto& req = a ? *a : (r ? *r : (u ? *u : *e));
|
||||
auto& req = parsed.a ? *parsed.a : (parsed.r ? *parsed.r : (parsed.u ? *parsed.u : *parsed.e));
|
||||
|
||||
if (e) {
|
||||
if (e->type != msgpack::type::ARRAY)
|
||||
if (parsed.e) {
|
||||
if (parsed.e->type != msgpack::type::ARRAY)
|
||||
throw msgpack::type_error();
|
||||
error_code = e->via.array.ptr[0].as<uint16_t>();
|
||||
error_code = parsed.e->via.array.ptr[0].as<uint16_t>();
|
||||
}
|
||||
|
||||
if (auto t = findMapValue(req, "sid"))
|
||||
socket_id = unpackTid(*t);
|
||||
struct ParsedReq {
|
||||
msgpack::object* values;
|
||||
msgpack::object* fields;
|
||||
msgpack::object* sa;
|
||||
msgpack::object* want;
|
||||
} parsedReq {};
|
||||
|
||||
if (auto rid = findMapValue(req, "id"))
|
||||
id = {*rid};
|
||||
for (unsigned i = 0; i < req.via.map.size; i++) {
|
||||
auto& o = req.via.map.ptr[i];
|
||||
if (o.key.type != msgpack::type::STR)
|
||||
continue;
|
||||
auto key = o.key.as<std::string>();
|
||||
if (key == KEY_REQ_SID)
|
||||
socket_id = unpackTid(o.val);
|
||||
else if (key == KEY_REQ_ID)
|
||||
id = {o.val};
|
||||
else if (key == KEY_REQ_H)
|
||||
info_hash = {o.val};
|
||||
else if (key == KEY_REQ_TARGET)
|
||||
target = {o.val};
|
||||
else if (key == KEY_REQ_QUERY)
|
||||
query.msgpack_unpack(o.val);
|
||||
else if (key == KEY_REQ_TOKEN)
|
||||
token = unpackBlob(o.val);
|
||||
else if (key == KEY_REQ_VALUE_ID)
|
||||
value_id = o.val.as<Value::Id>();
|
||||
else if (key == KEY_REQ_NODES4)
|
||||
nodes4_raw = unpackBlob(o.val);
|
||||
else if (key == KEY_REQ_NODES6)
|
||||
nodes6_raw = unpackBlob(o.val);
|
||||
else if (key == KEY_REQ_ADDRESS)
|
||||
parsedReq.sa = &o.val;
|
||||
else if (key == KEY_REQ_CREATION)
|
||||
created = from_time_t(o.val.as<std::time_t>());
|
||||
else if (key == KEY_REQ_VALUES)
|
||||
parsedReq.values = &o.val;
|
||||
else if (key == KEY_REQ_EXPIRED)
|
||||
expired_values = o.val.as<decltype(expired_values)>();
|
||||
else if (key == KEY_REQ_REFRESHED)
|
||||
refreshed_values = o.val.as<decltype(refreshed_values)>();
|
||||
else if (key == KEY_REQ_FIELDS)
|
||||
parsedReq.fields = &o.val;
|
||||
else if (key == KEY_REQ_WANT)
|
||||
parsedReq.want = &o.val;
|
||||
}
|
||||
|
||||
if (auto rh = findMapValue(req, "h"))
|
||||
info_hash = {*rh};
|
||||
|
||||
if (auto rtarget = findMapValue(req, "target"))
|
||||
target = {*rtarget};
|
||||
|
||||
if (auto rquery = findMapValue(req, "q"))
|
||||
query.msgpack_unpack(*rquery);
|
||||
|
||||
if (auto otoken = findMapValue(req, "token"))
|
||||
token = unpackBlob(*otoken);
|
||||
|
||||
if (auto vid = findMapValue(req, "vid"))
|
||||
value_id = vid->as<Value::Id>();
|
||||
|
||||
if (auto rnodes4 = findMapValue(req, "n4"))
|
||||
nodes4_raw = unpackBlob(*rnodes4);
|
||||
|
||||
if (auto rnodes6 = findMapValue(req, "n6"))
|
||||
nodes6_raw = unpackBlob(*rnodes6);
|
||||
|
||||
if (auto sa = findMapValue(req, "sa")) {
|
||||
if (sa->type != msgpack::type::BIN)
|
||||
if (parsedReq.sa) {
|
||||
if (parsedReq.sa->type != msgpack::type::BIN)
|
||||
throw msgpack::type_error();
|
||||
auto l = sa->via.bin.size;
|
||||
auto l = parsedReq.sa->via.bin.size;
|
||||
if (l == sizeof(in_addr)) {
|
||||
addr.setFamily(AF_INET);
|
||||
auto& a = addr.getIPv4();
|
||||
a.sin_port = 0;
|
||||
std::copy_n(sa->via.bin.ptr, l, (char*)&a.sin_addr);
|
||||
std::copy_n(parsedReq.sa->via.bin.ptr, l, (char*)&a.sin_addr);
|
||||
} else if (l == sizeof(in6_addr)) {
|
||||
addr.setFamily(AF_INET6);
|
||||
auto& a = addr.getIPv6();
|
||||
a.sin6_port = 0;
|
||||
std::copy_n(sa->via.bin.ptr, l, (char*)&a.sin6_addr);
|
||||
std::copy_n(parsedReq.sa->via.bin.ptr, l, (char*)&a.sin6_addr);
|
||||
}
|
||||
} else
|
||||
addr = {};
|
||||
|
||||
if (auto rcreated = findMapValue(req, "c"))
|
||||
created = from_time_t(rcreated->as<std::time_t>());
|
||||
|
||||
if (auto rvalues = findMapValue(req, "values")) {
|
||||
if (rvalues->type != msgpack::type::ARRAY)
|
||||
if (parsedReq.values) {
|
||||
if (parsedReq.values->type != msgpack::type::ARRAY)
|
||||
throw msgpack::type_error();
|
||||
for (size_t i = 0; i < rvalues->via.array.size; i++) {
|
||||
auto& packed_v = rvalues->via.array.ptr[i];
|
||||
for (size_t i = 0; i < parsedReq.values->via.array.size; i++) {
|
||||
auto& packed_v = parsedReq.values->via.array.ptr[i];
|
||||
if (packed_v.type == msgpack::type::POSITIVE_INTEGER) {
|
||||
// Skip oversize values with a small margin for header overhead
|
||||
if (packed_v.via.u64 > MAX_VALUE_SIZE + 32)
|
||||
@@ -258,16 +327,16 @@ ParsedMessage::msgpack_unpack(msgpack::object msg)
|
||||
value_parts.emplace(i, std::make_pair(packed_v.via.u64, Blob{}));
|
||||
} else {
|
||||
try {
|
||||
values.emplace_back(std::make_shared<Value>(rvalues->via.array.ptr[i]));
|
||||
values.emplace_back(std::make_shared<Value>(parsedReq.values->via.array.ptr[i]));
|
||||
} catch (const std::exception& e) {
|
||||
//DHT_LOG_WARN("Error reading value: %s", e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (auto raw_fields = findMapValue(req, "fields")) {
|
||||
if (auto rfields = findMapValue(*raw_fields, "f")) {
|
||||
} else if (parsedReq.fields) {
|
||||
if (auto rfields = findMapValue(*parsedReq.fields, "f")) {
|
||||
auto vfields = rfields->as<std::set<Value::Field>>();
|
||||
if (auto rvalues = findMapValue(*raw_fields, "v")) {
|
||||
if (auto rvalues = findMapValue(*parsedReq.fields, "v")) {
|
||||
if (rvalues->type != msgpack::type::ARRAY)
|
||||
throw msgpack::type_error();
|
||||
size_t val_num = rvalues->via.array.size / vfields.size();
|
||||
@@ -284,12 +353,12 @@ ParsedMessage::msgpack_unpack(msgpack::object msg)
|
||||
}
|
||||
}
|
||||
|
||||
if (auto w = findMapValue(req, "w")) {
|
||||
if (w->type != msgpack::type::ARRAY)
|
||||
if (parsedReq.want) {
|
||||
if (parsedReq.want->type != msgpack::type::ARRAY)
|
||||
throw msgpack::type_error();
|
||||
want = 0;
|
||||
for (unsigned i=0; i<w->via.array.size; i++) {
|
||||
auto& val = w->via.array.ptr[i];
|
||||
for (unsigned i=0; i<parsedReq.want->via.array.size; i++) {
|
||||
auto& val = parsedReq.want->via.array.ptr[i];
|
||||
try {
|
||||
auto w = val.as<sa_family_t>();
|
||||
if (w == AF_INET)
|
||||
|
||||
+10
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2016-2019 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
@@ -57,6 +57,15 @@ struct Request {
|
||||
bool pending() const { return state_ == State::PENDING; }
|
||||
bool over() const { return not pending(); }
|
||||
State getState() const { return state_; }
|
||||
char getStateChar() const {
|
||||
switch (state_) {
|
||||
case State::PENDING: return 'f';
|
||||
case State::CANCELLED: return 'c';
|
||||
case State::EXPIRED: return 'e';
|
||||
case State::COMPLETED: return 'a';
|
||||
default: return '?';
|
||||
}
|
||||
}
|
||||
|
||||
Request(State state = State::PENDING) : state_(state) {}
|
||||
Request(MessageType type, Tid tid,
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 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
|
||||
@@ -92,7 +92,7 @@ RoutingTable::middle(const RoutingTable::const_iterator& it) const
|
||||
throw std::out_of_range("End of table");
|
||||
|
||||
InfoHash id = it->first;
|
||||
id.setBit(bit, 1);
|
||||
id.setBit(bit, true);
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
+135
-40
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2018 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "value.h"
|
||||
#include "request.h"
|
||||
#include "listener.h"
|
||||
#include "value_cache.h"
|
||||
#include "op_cache.h"
|
||||
|
||||
namespace dht {
|
||||
|
||||
@@ -59,6 +61,18 @@ struct Dht::SearchNode {
|
||||
*/
|
||||
using SyncStatus = std::map<Sp<Query>, Sp<net::Request>>;
|
||||
|
||||
struct CachedListenStatus {
|
||||
ValueCache cache;
|
||||
Sp<Scheduler::Job> cacheExpirationJob {};
|
||||
Sp<net::Request> req {};
|
||||
CachedListenStatus(ValueStateCallback&& cb, SyncCallback&& scb)
|
||||
: cache(std::forward<ValueStateCallback>(cb), std::forward<SyncCallback>(scb)) {}
|
||||
CachedListenStatus(CachedListenStatus&&) = default;
|
||||
CachedListenStatus(const CachedListenStatus&) = delete;
|
||||
CachedListenStatus& operator=(const CachedListenStatus&) = delete;
|
||||
};
|
||||
using NodeListenerStatus = std::map<Sp<Query>, CachedListenStatus>;
|
||||
|
||||
Sp<Node> node {}; /* the node info */
|
||||
|
||||
/* queries sent for finding out values hosted by the node */
|
||||
@@ -67,7 +81,7 @@ struct Dht::SearchNode {
|
||||
std::map<Sp<Query>, std::vector<Sp<Query>>> pagination_queries {};
|
||||
|
||||
SyncStatus getStatus {}; /* get/sync status */
|
||||
SyncStatus listenStatus {}; /* listen status */
|
||||
NodeListenerStatus listenStatus {}; /* listen status */
|
||||
AnnounceStatus acked {}; /* announcement status for a given value id */
|
||||
|
||||
Blob token {}; /* last token the node sent to us after a get request */
|
||||
@@ -123,7 +137,7 @@ struct Dht::SearchNode {
|
||||
*
|
||||
* @return true if we can send get, else false.
|
||||
*/
|
||||
bool canGet(time_point now, time_point update, Sp<Query> q = {}) const {
|
||||
bool canGet(time_point now, time_point update, const Sp<Query>& q) const {
|
||||
if (node->isExpired())
|
||||
return false;
|
||||
|
||||
@@ -134,9 +148,9 @@ struct Dht::SearchNode {
|
||||
if (s.second and s.second->pending())
|
||||
pending = true;
|
||||
if (s.first and q and q->isSatisfiedBy(*s.first) and s.second) {
|
||||
if (s.second->pending() and not pending_sq_status)
|
||||
if (s.second->pending())
|
||||
pending_sq_status = true;
|
||||
if (s.second->completed() and not (update > s.second->reply_time) and not completed_sq_status)
|
||||
else if (s.second->completed() and not (update > s.second->reply_time))
|
||||
completed_sq_status = true;
|
||||
if (completed_sq_status and pending_sq_status)
|
||||
break;
|
||||
@@ -144,7 +158,7 @@ struct Dht::SearchNode {
|
||||
}
|
||||
|
||||
return (not pending and now > last_get_reply + Node::NODE_EXPIRE_TIME) or
|
||||
not (hasStartedPagination(q) or completed_sq_status or pending_sq_status);
|
||||
not (completed_sq_status or pending_sq_status or hasStartedPagination(q));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -201,6 +215,32 @@ struct Dht::SearchNode {
|
||||
getStatus.clear();
|
||||
}
|
||||
|
||||
void onValues(const Sp<Query>& q, net::RequestAnswer&& answer, const TypeStore& types, Scheduler& scheduler)
|
||||
{
|
||||
auto l = listenStatus.find(q);
|
||||
if (l != listenStatus.end()) {
|
||||
auto next = l->second.cache.onValues(answer.values,
|
||||
answer.refreshed_values,
|
||||
answer.expired_values, types, scheduler.time());
|
||||
scheduler.edit(l->second.cacheExpirationJob, next);
|
||||
}
|
||||
}
|
||||
|
||||
void onListenSynced(const Sp<Query>& q, bool synced = true) {
|
||||
auto l = listenStatus.find(q);
|
||||
if (l != listenStatus.end()) {
|
||||
l->second.cache.onSynced(synced);
|
||||
}
|
||||
}
|
||||
|
||||
void expireValues(const Sp<Query>& q, Scheduler& scheduler) {
|
||||
auto l = listenStatus.find(q);
|
||||
if (l != listenStatus.end()) {
|
||||
auto next = l->second.cache.expireValues(scheduler.time());
|
||||
scheduler.edit(l->second.cacheExpirationJob, next);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if a request in the status map is expired.
|
||||
*
|
||||
@@ -228,6 +268,12 @@ struct Dht::SearchNode {
|
||||
return r.second and r.second->pending();
|
||||
}) != status.end();
|
||||
}
|
||||
static bool pending(const NodeListenerStatus& status) {
|
||||
return std::find_if(status.begin(), status.end(),
|
||||
[](const NodeListenerStatus::value_type& r){
|
||||
return r.second.req and r.second.req->pending();
|
||||
}) != status.end();
|
||||
}
|
||||
|
||||
bool pendingGet() const { return pending(getStatus); }
|
||||
|
||||
@@ -263,49 +309,48 @@ struct Dht::SearchNode {
|
||||
else
|
||||
return isListening(now, ls);
|
||||
}
|
||||
bool isListening(time_point now, SyncStatus::const_iterator listen_status) const {
|
||||
if (listen_status == listenStatus.end())
|
||||
bool isListening(time_point now, NodeListenerStatus::const_iterator listen_status) const {
|
||||
if (listen_status == listenStatus.end() or not listen_status->second.req)
|
||||
return false;
|
||||
return listen_status->second->reply_time + LISTEN_EXPIRE_TIME > now;
|
||||
return listen_status->second.req->reply_time + LISTEN_EXPIRE_TIME > now;
|
||||
}
|
||||
void cancelListen() {
|
||||
for (const auto& status : listenStatus)
|
||||
node->cancelRequest(status.second);
|
||||
node->cancelRequest(status.second.req);
|
||||
listenStatus.clear();
|
||||
}
|
||||
void cancelListen(const Sp<Query>& query) {
|
||||
auto it = listenStatus.find(query);
|
||||
if (it != listenStatus.end()) {
|
||||
node->cancelRequest(it->second);
|
||||
node->cancelRequest(it->second.req);
|
||||
listenStatus.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumng the node is synced, should a "put" request be sent to this node now ?
|
||||
* Assuming the node is synced, should a "put" request be sent to this node now ?
|
||||
*/
|
||||
time_point getAnnounceTime(Value::Id vid) const {
|
||||
const auto& ack = acked.find(vid);
|
||||
const auto& gs = probe_query ? getStatus.find(probe_query) : getStatus.cend();
|
||||
if ((ack == acked.cend() or not ack->second.first) and (gs == getStatus.cend()
|
||||
or not gs->second or not gs->second->pending()))
|
||||
if (ack == acked.cend() or not ack->second.first) {
|
||||
return time_point::min();
|
||||
return ((gs != getStatus.cend() and gs->second and gs->second->pending())
|
||||
or ack == acked.cend() or not ack->second.first or ack->second.first->pending())
|
||||
? time_point::max()
|
||||
: ack->second.second - REANNOUNCE_MARGIN;
|
||||
}
|
||||
if (ack->second.first->completed()) {
|
||||
return ack->second.second - REANNOUNCE_MARGIN;
|
||||
}
|
||||
return ack->second.first->pending() ? time_point::max() : time_point::min();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumng the node is synced, should the "listen" request with Query q be
|
||||
* Assuming the node is synced, should the "listen" request with Query q be
|
||||
* sent to this node now ?
|
||||
*/
|
||||
time_point getListenTime(const Sp<Query>& q) const {
|
||||
auto listen_status = listenStatus.find(q);
|
||||
if (listen_status == listenStatus.end() or not listen_status->second)
|
||||
if (listen_status == listenStatus.end() or not listen_status->second.req)
|
||||
return time_point::min();
|
||||
return listen_status->second->pending() ? time_point::max() :
|
||||
listen_status->second->reply_time + LISTEN_EXPIRE_TIME - REANNOUNCE_MARGIN;
|
||||
return listen_status->second.req->pending() ? time_point::max() :
|
||||
listen_status->second.req->reply_time + LISTEN_EXPIRE_TIME - REANNOUNCE_MARGIN;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -340,10 +385,21 @@ struct Dht::Search {
|
||||
std::multimap<time_point, Get> callbacks {};
|
||||
|
||||
/* listeners */
|
||||
std::map<size_t, LocalListener> listeners {};
|
||||
struct SearchListener {
|
||||
Sp<Query> query;
|
||||
ValueCallback get_cb;
|
||||
SyncCallback sync_cb;
|
||||
};
|
||||
std::map<size_t, SearchListener> listeners {};
|
||||
size_t listener_token = 1;
|
||||
|
||||
/* Cache */
|
||||
SearchCache cache;
|
||||
Sp<Scheduler::Job> opExpirationJob;
|
||||
|
||||
~Search() {
|
||||
if (opExpirationJob)
|
||||
opExpirationJob->cancel();
|
||||
for (auto& get : callbacks) {
|
||||
get.second.done_cb(false, {});
|
||||
get.second.done_cb = {};
|
||||
@@ -386,7 +442,8 @@ struct Dht::Search {
|
||||
*
|
||||
* @param query The query identifying a 'get' request.
|
||||
*/
|
||||
time_point getLastGetTime(Sp<Query> query = {}) const;
|
||||
time_point getLastGetTime(const Query&) const;
|
||||
time_point getLastGetTime() const;
|
||||
|
||||
/**
|
||||
* Is this get operation done ?
|
||||
@@ -431,21 +488,50 @@ struct Dht::Search {
|
||||
bool isAnnounced(Value::Id id) const;
|
||||
bool isListening(time_point now) const;
|
||||
|
||||
void cancelListen(size_t token) {
|
||||
Sp<Query> query;
|
||||
const auto& ll = listeners.find(token);
|
||||
if (ll != listeners.cend()) {
|
||||
query = ll->second.query;
|
||||
listeners.erase(ll);
|
||||
}
|
||||
for (auto& sn : nodes) {
|
||||
if (listeners.empty())
|
||||
sn.cancelListen();
|
||||
else if (query)
|
||||
sn.cancelListen(query);
|
||||
void get(const Value::Filter& f, const Sp<Query>& q, const QueryCallback& qcb, const GetCallback& gcb, const DoneCallback& dcb, Scheduler& scheduler) {
|
||||
if (gcb or qcb) {
|
||||
if (not cache.get(f, q, gcb, dcb)) {
|
||||
const auto& now = scheduler.time();
|
||||
callbacks.emplace(now, Get { now, f, q, qcb, gcb, dcb });
|
||||
scheduler.edit(nextSearchStep, now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t listen(const ValueCallback& cb, const Value::Filter& f, const Sp<Query>& q, Scheduler& scheduler) {
|
||||
//DHT_LOG.e(id, "[search %s IPv%c] listen", id.toString().c_str(), (af == AF_INET) ? '4' : '6');
|
||||
return cache.listen(cb, q, f, [&](const Sp<Query>& q, ValueCallback vcb, SyncCallback scb){
|
||||
done = false;
|
||||
auto token = ++listener_token;
|
||||
listeners.emplace(token, SearchListener{q, vcb, scb});
|
||||
scheduler.edit(nextSearchStep, scheduler.time());
|
||||
return token;
|
||||
});
|
||||
}
|
||||
|
||||
void cancelListen(size_t token, Scheduler& scheduler) {
|
||||
cache.cancelListen(token, scheduler.time());
|
||||
if (not opExpirationJob)
|
||||
opExpirationJob = scheduler.add(time_point::max(), [this,&scheduler]{
|
||||
auto nextExpire = cache.expire(scheduler.time(), [&](size_t t){
|
||||
Sp<Query> query;
|
||||
const auto& ll = listeners.find(t);
|
||||
if (ll != listeners.cend()) {
|
||||
query = ll->second.query;
|
||||
listeners.erase(ll);
|
||||
}
|
||||
for (auto& sn : nodes) {
|
||||
if (listeners.empty())
|
||||
sn.cancelListen();
|
||||
else if (query)
|
||||
sn.cancelListen(query);
|
||||
}
|
||||
});
|
||||
scheduler.edit(opExpirationJob, nextExpire);
|
||||
});
|
||||
scheduler.edit(opExpirationJob, cache.getExpiration());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The number of non-good search nodes.
|
||||
*/
|
||||
@@ -683,11 +769,20 @@ Dht::Search::isSynced(time_point now) const
|
||||
}
|
||||
|
||||
time_point
|
||||
Dht::Search::getLastGetTime(Sp<Query> q) const
|
||||
Dht::Search::getLastGetTime(const Query& q) const
|
||||
{
|
||||
time_point last = time_point::min();
|
||||
for (const auto& g : callbacks)
|
||||
last = std::max(last, (not q or q->isSatisfiedBy(*g.second.query) ? g.second.start : time_point::min()));
|
||||
last = std::max(last, (q.isSatisfiedBy(*g.second.query) ? g.second.start : time_point::min()));
|
||||
return last;
|
||||
}
|
||||
|
||||
time_point
|
||||
Dht::Search::getLastGetTime() const
|
||||
{
|
||||
time_point last = time_point::min();
|
||||
for (const auto& g : callbacks)
|
||||
last = std::max(last, g.second.start);
|
||||
return last;
|
||||
}
|
||||
|
||||
@@ -732,7 +827,7 @@ Dht::Search::isListening(time_point now) const
|
||||
for (const auto& n : nodes) {
|
||||
if (n.isBad())
|
||||
continue;
|
||||
SearchNode::SyncStatus::const_iterator ls {};
|
||||
SearchNode::NodeListenerStatus::const_iterator ls {};
|
||||
for (ls = n.listenStatus.begin(); ls != n.listenStatus.end() ; ++ls) {
|
||||
if (n.isListening(now, ls))
|
||||
break;
|
||||
|
||||
+115
-62
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
@@ -56,25 +56,26 @@ SecureDht::SecureDht(std::unique_ptr<DhtInterface> dht, SecureDht::Config conf)
|
||||
1
|
||||
}, [this](bool ok) {
|
||||
if (ok)
|
||||
DHT_LOG.DEBUG("SecureDht: public key announced successfully");
|
||||
DHT_LOG.DBG("SecureDht: public key announced successfully");
|
||||
}, {}, true);
|
||||
}
|
||||
}
|
||||
|
||||
SecureDht::~SecureDht()
|
||||
{}
|
||||
SecureDht::~SecureDht() = default;
|
||||
|
||||
ValueType
|
||||
SecureDht::secureType(ValueType&& type)
|
||||
{
|
||||
type.storePolicy = [this,type](InfoHash id, Sp<Value>& v, const InfoHash& nid, const SockAddr& a) {
|
||||
if (v->isSigned()) {
|
||||
if (!v->owner or !v->owner->checkSignature(v->getToSign(), v->signature)) {
|
||||
if (!v->signatureChecked) {
|
||||
v->signatureChecked = true;
|
||||
v->signatureValid = v->owner and v->owner->checkSignature(v->getToSign(), v->signature);
|
||||
}
|
||||
if (!v->signatureValid) {
|
||||
DHT_LOG.WARN("Signature verification failed");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
DHT_LOG.WARN("Signature verification succeeded");
|
||||
}
|
||||
return type.storePolicy(id, v, nid, a);
|
||||
};
|
||||
@@ -85,7 +86,11 @@ SecureDht::secureType(ValueType&& type)
|
||||
DHT_LOG.WARN("Edition forbidden: owner changed.");
|
||||
return false;
|
||||
}
|
||||
if (!o->owner or !o->owner->checkSignature(n->getToSign(), n->signature)) {
|
||||
if (!n->signatureChecked) {
|
||||
n->signatureChecked = true;
|
||||
n->signatureValid = o->owner and o->owner->checkSignature(n->getToSign(), n->signature);
|
||||
}
|
||||
if (!n->signatureValid) {
|
||||
DHT_LOG.WARN("Edition forbidden: signature verification failed.");
|
||||
return false;
|
||||
}
|
||||
@@ -139,7 +144,7 @@ SecureDht::registerCertificate(const InfoHash& node, const Blob& data)
|
||||
}
|
||||
InfoHash h = crt->getPublicKey().getId();
|
||||
if (node == h) {
|
||||
DHT_LOG.DEBUG("Registering certificate for %s", h.toString().c_str());
|
||||
DHT_LOG.DBG("Registering certificate for %s", h.toString().c_str());
|
||||
auto it = nodesCertificates_.find(h);
|
||||
if (it == nodesCertificates_.end())
|
||||
std::tie(it, std::ignore) = nodesCertificates_.emplace(h, std::move(crt));
|
||||
@@ -147,7 +152,7 @@ SecureDht::registerCertificate(const InfoHash& node, const Blob& data)
|
||||
it->second = std::move(crt);
|
||||
return it->second;
|
||||
} else {
|
||||
DHT_LOG.DEBUG("Certificate %s for node %s does not match node id !", h.toString().c_str(), node.toString().c_str());
|
||||
DHT_LOG.WARN("Certificate %s for node %s does not match node id !", h.toString().c_str(), node.toString().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -160,11 +165,11 @@ SecureDht::registerCertificate(Sp<crypto::Certificate>& cert)
|
||||
}
|
||||
|
||||
void
|
||||
SecureDht::findCertificate(const InfoHash& node, std::function<void(const Sp<crypto::Certificate>)> cb)
|
||||
SecureDht::findCertificate(const InfoHash& node, const std::function<void(const Sp<crypto::Certificate>)>& cb)
|
||||
{
|
||||
Sp<crypto::Certificate> b = getCertificate(node);
|
||||
if (b && *b) {
|
||||
DHT_LOG.DEBUG("Using certificate from cache for %s", node.toString().c_str());
|
||||
DHT_LOG.DBG("Using certificate from cache for %s", node.toString().c_str());
|
||||
if (cb)
|
||||
cb(b);
|
||||
return;
|
||||
@@ -172,7 +177,7 @@ SecureDht::findCertificate(const InfoHash& node, std::function<void(const Sp<cry
|
||||
if (localQueryMethod_) {
|
||||
auto res = localQueryMethod_(node);
|
||||
if (not res.empty()) {
|
||||
DHT_LOG.DEBUG("Registering certificate from local store for %s", node.toString().c_str());
|
||||
DHT_LOG.DBG("Registering certificate from local store for %s", node.toString().c_str());
|
||||
nodesCertificates_.emplace(node, res.front());
|
||||
if (cb)
|
||||
cb(res.front());
|
||||
@@ -187,7 +192,7 @@ SecureDht::findCertificate(const InfoHash& node, std::function<void(const Sp<cry
|
||||
for (const auto& v : vals) {
|
||||
if (auto cert = registerCertificate(node, v->data)) {
|
||||
*found = true;
|
||||
DHT_LOG.DEBUG("Found certificate for %s", node.toString().c_str());
|
||||
DHT_LOG.DBG("Found certificate for %s", node.toString().c_str());
|
||||
if (cb)
|
||||
cb(cert);
|
||||
return false;
|
||||
@@ -201,11 +206,11 @@ SecureDht::findCertificate(const InfoHash& node, std::function<void(const Sp<cry
|
||||
}
|
||||
|
||||
void
|
||||
SecureDht::findPublicKey(const InfoHash& node, std::function<void(const Sp<const crypto::PublicKey>)> cb)
|
||||
SecureDht::findPublicKey(const InfoHash& node, const std::function<void(const Sp<const crypto::PublicKey>)>& cb)
|
||||
{
|
||||
auto pk = getPublicKey(node);
|
||||
if (pk && *pk) {
|
||||
DHT_LOG.DEBUG("Found public key from cache for %s", node.toString().c_str());
|
||||
DHT_LOG.DBG("Found public key from cache for %s", node.toString().c_str());
|
||||
if (cb)
|
||||
cb(pk);
|
||||
return;
|
||||
@@ -213,58 +218,98 @@ SecureDht::findPublicKey(const InfoHash& node, std::function<void(const Sp<const
|
||||
findCertificate(node, [=](const Sp<crypto::Certificate> crt) {
|
||||
if (crt && *crt) {
|
||||
auto pk = std::make_shared<crypto::PublicKey>(crt->getPublicKey());
|
||||
nodesPubKeys_[pk->getId()] = pk;
|
||||
if (cb) cb(pk);
|
||||
} else {
|
||||
if (cb) cb(nullptr);
|
||||
if (*pk) {
|
||||
nodesPubKeys_[pk->getId()] = pk;
|
||||
if (cb) cb(pk);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (cb) cb(nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
Sp<Value>
|
||||
SecureDht::checkValue(const Sp<Value>& v)
|
||||
{
|
||||
// Decrypt encrypted values
|
||||
if (v->isEncrypted()) {
|
||||
if (not key_) {
|
||||
#ifdef OPENDHT_PROXY_SERVER
|
||||
if (forward_all_) // We are currently a proxy, send messages to clients.
|
||||
return v;
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
if (v->decrypted) {
|
||||
return v->decryptedValue;
|
||||
}
|
||||
v->decrypted = true;
|
||||
try {
|
||||
Value decrypted_val (decrypt(*v));
|
||||
if (decrypted_val.recipient == getId()) {
|
||||
if (decrypted_val.owner)
|
||||
nodesPubKeys_[decrypted_val.owner->getId()] = decrypted_val.owner;
|
||||
v->decryptedValue = std::make_shared<Value>(std::move(decrypted_val));
|
||||
return v->decryptedValue;
|
||||
}
|
||||
// Ignore values belonging to other people
|
||||
} catch (const std::exception& e) {
|
||||
DHT_LOG.WARN("Could not decrypt value %s : %s", v->toString().c_str(), e.what());
|
||||
}
|
||||
}
|
||||
// Check signed values
|
||||
else if (v->isSigned()) {
|
||||
if (v->signatureChecked) {
|
||||
return v->signatureValid ? v : Sp<Value>{};
|
||||
}
|
||||
v->signatureChecked = true;
|
||||
if (v->owner and v->owner->checkSignature(v->getToSign(), v->signature)) {
|
||||
v->signatureValid = true;
|
||||
nodesPubKeys_[v->owner->getId()] = v->owner;
|
||||
return v;
|
||||
}
|
||||
else
|
||||
DHT_LOG.WARN("Signature verification failed for %s", v->toString().c_str());
|
||||
}
|
||||
// Forward normal values
|
||||
else {
|
||||
return v;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ValueCallback
|
||||
SecureDht::getCallbackFilter(const ValueCallback& cb, Value::Filter&& filter)
|
||||
{
|
||||
return [=](const std::vector<Sp<Value>>& values, bool expired) {
|
||||
std::vector<Sp<Value>> tmpvals {};
|
||||
if (not filter)
|
||||
tmpvals.reserve(values.size());
|
||||
for (const auto& v : values) {
|
||||
if (auto nv = checkValue(v))
|
||||
if (not filter or filter(*nv))
|
||||
tmpvals.emplace_back(std::move(nv));
|
||||
}
|
||||
if (cb and not tmpvals.empty())
|
||||
return cb(tmpvals, expired);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
GetCallback
|
||||
SecureDht::getCallbackFilter(GetCallback cb, Value::Filter&& filter)
|
||||
SecureDht::getCallbackFilter(const GetCallback& cb, Value::Filter&& filter)
|
||||
{
|
||||
return [=](const std::vector<Sp<Value>>& values) {
|
||||
std::vector<Sp<Value>> tmpvals {};
|
||||
if (not filter)
|
||||
tmpvals.reserve(values.size());
|
||||
for (const auto& v : values) {
|
||||
// Decrypt encrypted values
|
||||
if (v->isEncrypted()) {
|
||||
if (not key_) {
|
||||
#if OPENDHT_PROXY_SERVER
|
||||
if (forward_all_) // We are currently a proxy, send messages to clients.
|
||||
tmpvals.push_back(v);
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
Value decrypted_val (decrypt(*v));
|
||||
if (decrypted_val.recipient == getId()) {
|
||||
nodesPubKeys_[decrypted_val.owner->getId()] = decrypted_val.owner;
|
||||
if (not filter or filter(decrypted_val))
|
||||
tmpvals.push_back(std::make_shared<Value>(std::move(decrypted_val)));
|
||||
}
|
||||
// Ignore values belonging to other people
|
||||
} catch (const std::exception& e) {
|
||||
DHT_LOG.WARN("Could not decrypt value %s : %s", v->toString().c_str(), e.what());
|
||||
}
|
||||
}
|
||||
// Check signed values
|
||||
else if (v->isSigned()) {
|
||||
if (v->owner and v->owner->checkSignature(v->getToSign(), v->signature)) {
|
||||
nodesPubKeys_[v->owner->getId()] = v->owner;
|
||||
if (not filter or filter(*v))
|
||||
tmpvals.push_back(v);
|
||||
}
|
||||
else
|
||||
DHT_LOG.WARN("Signature verification failed for %s", v->toString().c_str());
|
||||
}
|
||||
// Forward normal values
|
||||
else {
|
||||
if (not filter or filter(*v))
|
||||
tmpvals.push_back(v);
|
||||
}
|
||||
if (auto nv = checkValue(v))
|
||||
if (not filter or filter(*nv))
|
||||
tmpvals.emplace_back(std::move(nv));
|
||||
}
|
||||
if (cb && not tmpvals.empty())
|
||||
if (cb and not tmpvals.empty())
|
||||
return cb(tmpvals);
|
||||
return true;
|
||||
};
|
||||
@@ -276,6 +321,13 @@ SecureDht::get(const InfoHash& id, GetCallback cb, DoneCallback donecb, Value::F
|
||||
dht_->get(id, getCallbackFilter(cb, std::forward<Value::Filter>(f)), donecb, {}, std::forward<Where>(w));
|
||||
}
|
||||
|
||||
size_t
|
||||
SecureDht::listen(const InfoHash& id, ValueCallback cb, Value::Filter f, Where w)
|
||||
{
|
||||
return dht_->listen(id, getCallbackFilter(cb, std::forward<Value::Filter>(f)), {}, std::forward<Where>(w));
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
SecureDht::listen(const InfoHash& id, GetCallback cb, Value::Filter f, Where w)
|
||||
{
|
||||
@@ -287,20 +339,20 @@ SecureDht::putSigned(const InfoHash& hash, Sp<Value> val, DoneCallback callback,
|
||||
{
|
||||
if (val->id == Value::INVALID_ID) {
|
||||
crypto::random_device rdev;
|
||||
std::uniform_int_distribution<Value::Id> rand_id;
|
||||
val->id = rand_id(rdev);
|
||||
}
|
||||
|
||||
// Check if we are already announcing a value
|
||||
auto p = dht_->getPut(hash, val->id);
|
||||
if (p && val->seq <= p->seq) {
|
||||
DHT_LOG.DEBUG("Found previous value being announced.");
|
||||
val->seq = p->seq + 1;
|
||||
}
|
||||
|
||||
// Check if data already exists on the dht
|
||||
get(hash,
|
||||
[val,this] (const std::vector<Sp<Value>>& vals) {
|
||||
DHT_LOG.DEBUG("Found online previous value being announced.");
|
||||
DHT_LOG.DBG("Found online previous value being announced.");
|
||||
for (const auto& v : vals) {
|
||||
if (!v->isSigned())
|
||||
DHT_LOG.ERR("Existing non-signed value seems to exists at this location.");
|
||||
@@ -315,14 +367,15 @@ SecureDht::putSigned(const InfoHash& hash, Sp<Value> val, DoneCallback callback,
|
||||
sign(*val);
|
||||
dht_->put(hash, val, callback, time_point::max(), permanent);
|
||||
},
|
||||
Value::IdFilter(val->id)
|
||||
Value::IdFilter(val->id),
|
||||
std::move(Where().id(val->id))
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
SecureDht::putEncrypted(const InfoHash& hash, const InfoHash& to, Sp<Value> val, DoneCallback callback, bool permanent)
|
||||
{
|
||||
findPublicKey(to, [=](const Sp<const crypto::PublicKey> pk) {
|
||||
findPublicKey(to, [=](const Sp<const crypto::PublicKey>& pk) {
|
||||
if(!pk || !*pk) {
|
||||
if (callback)
|
||||
callback(false, {});
|
||||
|
||||
+34
-22
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 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
|
||||
@@ -90,20 +90,7 @@ struct Storage {
|
||||
|
||||
Storage() {}
|
||||
Storage(time_point t) : maintenance_time(t) {}
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 || defined(_WIN32)
|
||||
// GCC-bug: remove me when support of GCC < 4.9.2 is abandoned
|
||||
Storage(Storage&& o) noexcept
|
||||
: maintenance_time(std::move(o.maintenance_time))
|
||||
, listeners(std::move(o.listeners))
|
||||
, local_listeners(std::move(o.local_listeners))
|
||||
, listener_token(std::move(o.listener_token))
|
||||
, values(std::move(o.values))
|
||||
, total_size(std::move(o.total_size)) {}
|
||||
#else
|
||||
Storage(Storage&& o) noexcept = default;
|
||||
#endif
|
||||
|
||||
Storage& operator=(Storage&& o) = default;
|
||||
|
||||
bool empty() const {
|
||||
@@ -154,20 +141,27 @@ struct Storage {
|
||||
*
|
||||
* @param now The reference to now
|
||||
* @param vid The value id
|
||||
* @return true if a value storage was updated, false otherwise
|
||||
* @return time of the next expiration, time_point::max() if no expiration
|
||||
*/
|
||||
bool refresh(const time_point& now, const Value::Id& vid) {
|
||||
time_point refresh(const time_point& now, const Value::Id& vid, const TypeStore& types) {
|
||||
for (auto& vs : values)
|
||||
if (vs.data->id == vid) {
|
||||
vs.created = now;
|
||||
return true;
|
||||
vs.expiration = std::max(vs.expiration, now + types.getType(vs.data->type).expiration);
|
||||
return vs.expiration;
|
||||
}
|
||||
return false;
|
||||
return time_point::max();
|
||||
}
|
||||
|
||||
size_t listen(ValueCallback& cb, Value::Filter& f, const Sp<Query>& q);
|
||||
|
||||
void cancelListen(size_t token) {
|
||||
local_listeners.erase(token);
|
||||
}
|
||||
|
||||
StoreDiff remove(const InfoHash& id, Value::Id);
|
||||
|
||||
StoreDiff expire(const InfoHash& id, time_point now);
|
||||
std::pair<ssize_t, std::vector<Sp<Value>>> expire(const InfoHash& id, time_point now);
|
||||
|
||||
private:
|
||||
Storage(const Storage&) = delete;
|
||||
@@ -178,6 +172,22 @@ private:
|
||||
};
|
||||
|
||||
|
||||
size_t
|
||||
Storage::listen(ValueCallback& gcb, Value::Filter& filter, const Sp<Query>& query)
|
||||
{
|
||||
if (not empty()) {
|
||||
std::vector<Sp<Value>> newvals = get(filter);
|
||||
if (not newvals.empty()) {
|
||||
if (!gcb(newvals, false))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
auto tokenlocal = ++listener_token;
|
||||
local_listeners.emplace(tokenlocal, LocalListener{query, filter, gcb});
|
||||
return tokenlocal;
|
||||
}
|
||||
|
||||
|
||||
std::pair<ValueStorage*, Storage::StoreDiff>
|
||||
Storage::store(const InfoHash& id, const Sp<Value>& value, time_point created, time_point expiration, StorageBucket* sb)
|
||||
{
|
||||
@@ -245,7 +255,7 @@ Storage::clear()
|
||||
return {-tot_size, -num_values, 0};
|
||||
}
|
||||
|
||||
Storage::StoreDiff
|
||||
std::pair<ssize_t, std::vector<Sp<Value>>>
|
||||
Storage::expire(const InfoHash& id, time_point now)
|
||||
{
|
||||
// expire listeners
|
||||
@@ -271,16 +281,18 @@ Storage::expire(const InfoHash& id, time_point now)
|
||||
auto r = std::partition(values.begin(), values.end(), [&](const ValueStorage& v) {
|
||||
return v.expiration > now;
|
||||
});
|
||||
ssize_t del_num = -std::distance(r, values.end());
|
||||
std::vector<Sp<Value>> ret;
|
||||
ret.reserve(std::distance(r, values.end()));
|
||||
ssize_t size_diff {};
|
||||
std::for_each(r, values.end(), [&](const ValueStorage& v) {
|
||||
size_diff -= v.data->size();
|
||||
if (v.store_bucket)
|
||||
v.store_bucket->erase(id, *v.data, v.expiration);
|
||||
ret.emplace_back(std::move(v.data));
|
||||
});
|
||||
total_size += size_diff;
|
||||
values.erase(r, values.end());
|
||||
return {size_diff, del_num, del_listen};
|
||||
return {size_diff, std::move(ret)};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (C) 2016-2019 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "thread_pool.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
#include <ciso646> // fix windows compiler bug
|
||||
|
||||
namespace dht {
|
||||
|
||||
constexpr const size_t IO_THREADS_MAX {64};
|
||||
|
||||
struct ThreadPool::ThreadState
|
||||
{
|
||||
std::thread thread {};
|
||||
std::atomic_bool run {true};
|
||||
};
|
||||
|
||||
ThreadPool&
|
||||
ThreadPool::computation()
|
||||
{
|
||||
static ThreadPool pool;
|
||||
return pool;
|
||||
}
|
||||
|
||||
ThreadPool&
|
||||
ThreadPool::io()
|
||||
{
|
||||
static ThreadPool pool(IO_THREADS_MAX);
|
||||
return pool;
|
||||
}
|
||||
|
||||
|
||||
ThreadPool::ThreadPool(size_t maxThreads) : maxThreads_(maxThreads)
|
||||
{
|
||||
threads_.reserve(maxThreads_);
|
||||
}
|
||||
|
||||
ThreadPool::ThreadPool()
|
||||
: ThreadPool(std::max<size_t>(std::thread::hardware_concurrency(), 4))
|
||||
{}
|
||||
|
||||
ThreadPool::~ThreadPool()
|
||||
{
|
||||
join();
|
||||
}
|
||||
|
||||
void
|
||||
ThreadPool::run(std::function<void()>&& cb)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(lock_);
|
||||
if (not running_) return;
|
||||
|
||||
// launch new thread if necessary
|
||||
if (not readyThreads_ && threads_.size() < maxThreads_) {
|
||||
threads_.emplace_back(new ThreadState());
|
||||
auto& t = *threads_.back();
|
||||
t.thread = std::thread([&]() {
|
||||
while (t.run) {
|
||||
std::function<void()> task;
|
||||
|
||||
// pick task from queue
|
||||
{
|
||||
std::unique_lock<std::mutex> l(lock_);
|
||||
readyThreads_++;
|
||||
cv_.wait(l, [&](){
|
||||
return not t.run or not tasks_.empty();
|
||||
});
|
||||
readyThreads_--;
|
||||
if (not t.run)
|
||||
break;
|
||||
task = std::move(tasks_.front());
|
||||
tasks_.pop();
|
||||
}
|
||||
|
||||
// run task
|
||||
try {
|
||||
if (task)
|
||||
task();
|
||||
} catch (const std::exception& e) {
|
||||
// LOG_ERR("Exception running task: %s", e.what());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// push task to queue
|
||||
tasks_.emplace(std::move(cb));
|
||||
|
||||
// notify thread
|
||||
l.unlock();
|
||||
cv_.notify_one();
|
||||
}
|
||||
|
||||
void
|
||||
ThreadPool::stop()
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> l(lock_);
|
||||
running_ = false;
|
||||
}
|
||||
for (auto& t : threads_)
|
||||
t->run = false;
|
||||
cv_.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
ThreadPool::join()
|
||||
{
|
||||
stop();
|
||||
for (auto& t : threads_)
|
||||
t->thread.join();
|
||||
threads_.clear();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2016-2019 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <future>
|
||||
#include <functional>
|
||||
|
||||
namespace dht {
|
||||
|
||||
class ThreadPool {
|
||||
public:
|
||||
static ThreadPool& computation();
|
||||
static ThreadPool& io();
|
||||
|
||||
ThreadPool();
|
||||
ThreadPool(size_t maxThreads);
|
||||
~ThreadPool();
|
||||
|
||||
void run(std::function<void()>&& cb);
|
||||
|
||||
template<class T>
|
||||
std::future<T> get(std::function<T()>&& cb) {
|
||||
auto ret = std::make_shared<std::promise<T>>();
|
||||
run(std::bind([=](std::function<T()>& mcb) mutable {
|
||||
ret->set_value(mcb());
|
||||
}, std::move(cb)));
|
||||
return ret->get_future();
|
||||
}
|
||||
template<class T>
|
||||
std::shared_ptr<std::future<T>> getShared(std::function<T()>&& cb) {
|
||||
return std::make_shared<std::future<T>>(get(std::move(cb)));
|
||||
}
|
||||
|
||||
void stop();
|
||||
void join();
|
||||
|
||||
private:
|
||||
struct ThreadState;
|
||||
std::queue<std::function<void()>> tasks_ {};
|
||||
std::vector<std::unique_ptr<ThreadState>> threads_;
|
||||
unsigned readyThreads_ {0};
|
||||
std::mutex lock_ {};
|
||||
std::condition_variable cv_ {};
|
||||
|
||||
const unsigned maxThreads_;
|
||||
bool running_ {true};
|
||||
};
|
||||
|
||||
}
|
||||
+21
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -29,6 +29,26 @@ namespace dht {
|
||||
|
||||
static constexpr std::array<uint8_t, 12> MAPPED_IPV4_PREFIX {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}};
|
||||
|
||||
std::pair<std::string, std::string>
|
||||
splitPort(const std::string& s) {
|
||||
if (s.empty())
|
||||
return {};
|
||||
if (s[0] == '[') {
|
||||
std::size_t closure = s.find_first_of(']');
|
||||
std::size_t found = s.find_last_of(':');
|
||||
if (closure == std::string::npos)
|
||||
return {s, ""};
|
||||
if (found == std::string::npos or found < closure)
|
||||
return {s.substr(1,closure-1), ""};
|
||||
return {s.substr(1,closure-1), s.substr(found+1)};
|
||||
}
|
||||
std::size_t found = s.find_last_of(':');
|
||||
std::size_t first = s.find_first_of(':');
|
||||
if (found == std::string::npos or found != first)
|
||||
return {s, ""};
|
||||
return {s.substr(0,found), s.substr(found+1)};
|
||||
}
|
||||
|
||||
std::vector<SockAddr>
|
||||
SockAddr::resolve(const std::string& host, const std::string& service)
|
||||
{
|
||||
|
||||
+67
-54
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2019 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
@@ -22,9 +22,9 @@
|
||||
#include "default_types.h"
|
||||
#include "securedht.h" // print certificate ID
|
||||
|
||||
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
#include "base64.h"
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
#endif
|
||||
|
||||
|
||||
namespace dht {
|
||||
@@ -54,17 +54,19 @@ std::ostream& operator<< (std::ostream& s, const Value& v)
|
||||
s << IpServiceAnnouncement(v.data);
|
||||
} else if (v.type == CERTIFICATE_TYPE.id) {
|
||||
s << "Certificate";
|
||||
#ifdef OPENDHT_LOG_CRT_ID
|
||||
try {
|
||||
InfoHash h = crypto::Certificate(v.data).getPublicKey().getId();
|
||||
auto h = crypto::Certificate(v.data).getPublicKey().getLongId();
|
||||
s << " with ID " << h;
|
||||
} catch (const std::exception& e) {
|
||||
s << " (invalid)";
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
s << "Data (type: " << v.type << " ): ";
|
||||
s << std::hex;
|
||||
for (size_t i=0; i<v.data.size(); i++)
|
||||
s << std::setfill('0') << std::setw(2) << (unsigned)v.data[i];
|
||||
for (auto i : v.data)
|
||||
s << std::setfill('0') << std::setw(2) << (unsigned)i;
|
||||
s << std::dec;
|
||||
}
|
||||
}
|
||||
@@ -76,7 +78,7 @@ std::ostream& operator<< (std::ostream& s, const Value& v)
|
||||
const ValueType ValueType::USER_DATA = {0, "User Data"};
|
||||
|
||||
bool
|
||||
ValueType::DEFAULT_STORE_POLICY(InfoHash, std::shared_ptr<Value>& v, const InfoHash&, const SockAddr&)
|
||||
ValueType::DEFAULT_STORE_POLICY(InfoHash, const std::shared_ptr<Value>& v, const InfoHash&, const SockAddr&)
|
||||
{
|
||||
return v->size() <= MAX_VALUE_SIZE;
|
||||
}
|
||||
@@ -172,43 +174,40 @@ Value::msgpack_unpack_body(const msgpack::object& o)
|
||||
}
|
||||
}
|
||||
|
||||
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
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();
|
||||
id = Value::Id(unpackId(json, "id"));
|
||||
if (json.isMember("cypher")) {
|
||||
auto cypherStr = json["cypher"].asString();
|
||||
cypherStr = base64_decode(cypherStr);
|
||||
cypher = std::vector<unsigned char>(cypherStr.begin(), cypherStr.end());
|
||||
}
|
||||
if (json.isMember("sig")) {
|
||||
auto sigStr = json["sig"].asString();
|
||||
sigStr = base64_decode(sigStr);
|
||||
signature = std::vector<unsigned char>(sigStr.begin(), sigStr.end());
|
||||
}
|
||||
if (json.isMember("seq"))
|
||||
seq = json["seq"].asInt();
|
||||
if (json.isMember("owner")) {
|
||||
auto ownerStr = json["owner"].asString();
|
||||
auto ownerBlob = std::vector<unsigned char>(ownerStr.begin(), ownerStr.end());
|
||||
owner = std::make_shared<const crypto::PublicKey>(ownerBlob);
|
||||
}
|
||||
if (json.isMember("to")) {
|
||||
auto toStr = json["to"].asString();
|
||||
recipient = InfoHash(toStr);
|
||||
}
|
||||
if (json.isMember("type"))
|
||||
type = json["type"].asInt();
|
||||
if (json.isMember("data")){
|
||||
auto dataStr = json["data"].asString();
|
||||
dataStr = base64_decode(dataStr);
|
||||
data = std::vector<unsigned char>(dataStr.begin(), dataStr.end());
|
||||
}
|
||||
if (json.isMember("utype"))
|
||||
user_type = json["utype"].asString();
|
||||
}
|
||||
|
||||
Json::Value
|
||||
@@ -225,7 +224,7 @@ Value::toJson() const
|
||||
if (has_owner) { // isSigned
|
||||
val["seq"] = seq;
|
||||
val["owner"] = owner->toString();
|
||||
if (recipient != InfoHash())
|
||||
if (recipient)
|
||||
val["to"] = recipient.toString();
|
||||
}
|
||||
val["type"] = type;
|
||||
@@ -235,7 +234,23 @@ Value::toJson() const
|
||||
}
|
||||
return val;
|
||||
}
|
||||
#endif //OPENDHT_PROXY_SERVER
|
||||
|
||||
uint64_t
|
||||
unpackId(const Json::Value& json, const std::string& key) {
|
||||
uint64_t ret = 0;
|
||||
try {
|
||||
if (json.isMember(key)) {
|
||||
const auto& t = json[key];
|
||||
if (t.isString()) {
|
||||
ret = std::stoull(t.asString());
|
||||
} else {
|
||||
ret = t.asLargestUInt();
|
||||
}
|
||||
}
|
||||
} catch (...) {}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
FieldValue::operator==(const FieldValue& vfd) const
|
||||
@@ -273,11 +288,11 @@ FieldValue::getLocalFilter() const
|
||||
case Value::Field::UserType:
|
||||
return Value::UserTypeFilter(std::string {blobValue.begin(), blobValue.end()});
|
||||
default:
|
||||
return Value::AllFilter();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
FieldValueIndex::FieldValueIndex(const Value& v, Select s)
|
||||
FieldValueIndex::FieldValueIndex(const Value& v, const Select& s)
|
||||
{
|
||||
auto selection = s.getSelection();
|
||||
if (not selection.empty()) {
|
||||
@@ -384,8 +399,8 @@ FieldValueIndex::msgpack_unpack_fields(const std::set<Value::Field>& fields, con
|
||||
}
|
||||
|
||||
void trim_str(std::string& str) {
|
||||
auto first = std::min(str.size(), str.find_first_not_of(" "));
|
||||
auto last = std::min(str.size(), str.find_last_not_of(" "));
|
||||
auto first = std::min(str.size(), str.find_first_not_of(' '));
|
||||
auto last = std::min(str.size(), str.find_last_not_of(' '));
|
||||
str = str.substr(first, last - first + 1);
|
||||
}
|
||||
|
||||
@@ -482,8 +497,7 @@ template <typename T>
|
||||
bool subset(std::vector<T> fds, std::vector<T> qfds)
|
||||
{
|
||||
for (auto& fd : fds) {
|
||||
auto correspondance = std::find_if(qfds.begin(), qfds.end(), [&fd](T& _vfd) { return fd == _vfd; });
|
||||
if (correspondance == qfds.end())
|
||||
if (std::find_if(qfds.begin(), qfds.end(), [&fd](T& _vfd) { return fd == _vfd; }) == qfds.end())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -491,10 +505,9 @@ bool subset(std::vector<T> fds, std::vector<T> qfds)
|
||||
|
||||
bool Select::isSatisfiedBy(const Select& os) const {
|
||||
/* empty, means all values are selected. */
|
||||
if (fieldSelection_.empty() and not os.fieldSelection_.empty())
|
||||
return false;
|
||||
else
|
||||
return subset(fieldSelection_, os.fieldSelection_);
|
||||
return fieldSelection_.empty() ?
|
||||
os.fieldSelection_.empty() :
|
||||
subset(fieldSelection_, os.fieldSelection_);
|
||||
}
|
||||
|
||||
bool Where::isSatisfiedBy(const Where& ow) const {
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "value.h"
|
||||
|
||||
namespace dht {
|
||||
|
||||
using ValueStateCallback = std::function<void(const std::vector<Sp<Value>>&, bool)>;
|
||||
enum class ListenSyncStatus { ADDED, SYNCED, UNSYNCED, REMOVED };
|
||||
using SyncCallback = std::function<void(ListenSyncStatus)>;
|
||||
using CallbackQueue = std::list<std::function<void()>>;
|
||||
|
||||
class ValueCache {
|
||||
public:
|
||||
ValueCache(ValueStateCallback&& cb, SyncCallback&& scb = {})
|
||||
: callback(std::forward<ValueStateCallback>(cb)), syncCallback(std::move(scb))
|
||||
{
|
||||
if (syncCallback)
|
||||
syncCallback(ListenSyncStatus::ADDED);
|
||||
}
|
||||
ValueCache(ValueCache&& o) noexcept : values(std::move(o.values)), callback(std::move(o.callback)), syncCallback(std::move(o.syncCallback)) {
|
||||
o.callback = {};
|
||||
o.syncCallback = {};
|
||||
}
|
||||
|
||||
~ValueCache() {
|
||||
auto q = clear();
|
||||
for (auto& cb: q)
|
||||
cb();
|
||||
if (syncCallback) {
|
||||
if (status == ListenSyncStatus::SYNCED)
|
||||
syncCallback(ListenSyncStatus::UNSYNCED);
|
||||
syncCallback(ListenSyncStatus::REMOVED);
|
||||
}
|
||||
}
|
||||
|
||||
CallbackQueue clear() {
|
||||
std::vector<Sp<Value>> expired_values;
|
||||
expired_values.reserve(values.size());
|
||||
for (const auto& v : values)
|
||||
expired_values.emplace_back(std::move(v.second.data));
|
||||
values.clear();
|
||||
CallbackQueue ret;
|
||||
if (not expired_values.empty() and callback) {
|
||||
auto cb = callback;
|
||||
ret.emplace_back([expired_values, cb]{
|
||||
cb(expired_values, true);
|
||||
});
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
time_point expireValues(const time_point& now) {
|
||||
time_point ret = time_point::max();
|
||||
auto cbs = expireValues(now, ret);
|
||||
while (not cbs.empty()) {
|
||||
cbs.front()();
|
||||
cbs.pop_front();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
CallbackQueue expireValues(const time_point& now, time_point& next) {
|
||||
std::vector<Sp<Value>> expired_values;
|
||||
for (auto it = values.begin(); it != values.end();) {
|
||||
if (it->second.expiration <= now) {
|
||||
expired_values.emplace_back(std::move(it->second.data));
|
||||
it = values.erase(it);
|
||||
} else {
|
||||
next = std::min(next, it->second.expiration);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
while (values.size() > MAX_VALUES) {
|
||||
// too many values, remove oldest values
|
||||
time_point oldest_creation = time_point::max();
|
||||
auto oldest_value = values.end();
|
||||
for (auto it = values.begin(); it != values.end(); ++it)
|
||||
if (it->second.created < oldest_creation) {
|
||||
oldest_value = it;
|
||||
oldest_creation = it->second.created;
|
||||
}
|
||||
if (oldest_value != values.end()) {
|
||||
expired_values.emplace_back(std::move(oldest_value->second.data));
|
||||
values.erase(oldest_value);
|
||||
}
|
||||
}
|
||||
CallbackQueue ret;
|
||||
if (not expired_values.empty() and callback) {
|
||||
auto cb = callback;
|
||||
ret.emplace_back([cb, expired_values]{
|
||||
if (cb) cb(expired_values, true);
|
||||
});
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
time_point onValues
|
||||
(const std::vector<Sp<Value>>& values,
|
||||
const std::vector<Value::Id>& refreshed_values,
|
||||
const std::vector<Value::Id>& expired_values,
|
||||
const TypeStore& types, const time_point& now)
|
||||
{
|
||||
CallbackQueue cbs;
|
||||
time_point ret = time_point::max();
|
||||
if (not values.empty())
|
||||
cbs.splice(cbs.end(), addValues(values, types, now));
|
||||
for (const auto& vid : refreshed_values)
|
||||
refreshValue(vid, types, now);
|
||||
for (const auto& vid : expired_values)
|
||||
cbs.splice(cbs.end(), expireValue(vid));
|
||||
cbs.splice(cbs.end(), expireValues(now, ret));
|
||||
while (not cbs.empty()) {
|
||||
cbs.front()();
|
||||
cbs.pop_front();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void onSynced(bool synced) {
|
||||
auto newStatus = synced ? ListenSyncStatus::SYNCED : ListenSyncStatus::UNSYNCED;
|
||||
if (status != newStatus) {
|
||||
status = newStatus;
|
||||
if (syncCallback)
|
||||
syncCallback(newStatus);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// prevent copy
|
||||
ValueCache(const ValueCache&) = delete;
|
||||
ValueCache& operator=(const ValueCache&) = delete;
|
||||
ValueCache& operator=(ValueCache&&) = delete;
|
||||
|
||||
/* The maximum number of values we store in the cache. */
|
||||
static constexpr unsigned MAX_VALUES {4096};
|
||||
|
||||
struct CacheValueStorage {
|
||||
Sp<Value> data {};
|
||||
time_point created {};
|
||||
time_point expiration {};
|
||||
|
||||
CacheValueStorage() {}
|
||||
CacheValueStorage(const Sp<Value>& v, time_point t, time_point e)
|
||||
: data(v), created(t), expiration(e) {}
|
||||
};
|
||||
|
||||
std::map<Value::Id, CacheValueStorage> values;
|
||||
ValueStateCallback callback;
|
||||
SyncCallback syncCallback;
|
||||
ListenSyncStatus status {ListenSyncStatus::UNSYNCED};
|
||||
|
||||
CallbackQueue addValues(const std::vector<Sp<Value>>& new_values, const TypeStore& types, const time_point& now) {
|
||||
std::vector<Sp<Value>> nvals;
|
||||
for (const auto& value : new_values) {
|
||||
auto v = values.find(value->id);
|
||||
if (v == values.end()) {
|
||||
// new value
|
||||
nvals.emplace_back(value);
|
||||
values.emplace(value->id, CacheValueStorage(value, now, now + types.getType(value->type).expiration));
|
||||
} else {
|
||||
// refreshed value
|
||||
v->second.created = now;
|
||||
v->second.expiration = now + types.getType(v->second.data->type).expiration;
|
||||
}
|
||||
}
|
||||
auto cb = callback;
|
||||
CallbackQueue ret;
|
||||
if (not nvals.empty())
|
||||
ret.emplace_back([cb, nvals]{
|
||||
if (cb) cb(nvals, false);
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
CallbackQueue expireValue(Value::Id vid) {
|
||||
auto v = values.find(vid);
|
||||
if (v == values.end())
|
||||
return {};
|
||||
const std::vector<Sp<Value>> val {std::move(v->second.data)};
|
||||
values.erase(v);
|
||||
auto cb = callback;
|
||||
CallbackQueue ret;
|
||||
ret.emplace_back([cb, val]{
|
||||
if (cb) cb(val, true);
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
void refreshValue(Value::Id vid, const TypeStore& types, const time_point& now) {
|
||||
auto v = values.find(vid);
|
||||
if (v == values.end())
|
||||
return;
|
||||
v->second.created = now;
|
||||
v->second.expiration = now + types.getType(v->second.data->type).expiration;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
if ENABLE_TESTS
|
||||
bin_PROGRAMS = opendht_unit_tests
|
||||
|
||||
AM_CPPFLAGS = -I../include
|
||||
|
||||
nobase_include_HEADERS = infohashtester.h valuetester.h cryptotester.h dhtrunnertester.h dhtproxytester.h
|
||||
opendht_unit_tests_SOURCES = tests_runner.cpp cryptotester.cpp infohashtester.cpp valuetester.cpp dhtrunnertester.cpp dhtproxytester.cpp
|
||||
opendht_unit_tests_LDFLAGS = -lopendht -lcppunit -L@top_builddir@/src/.libs @GnuTLS_LIBS@
|
||||
endif
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "cryptotester.h"
|
||||
|
||||
#include "opendht/crypto.h"
|
||||
|
||||
namespace test {
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(CryptoTester);
|
||||
|
||||
void
|
||||
CryptoTester::setUp() {
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
CryptoTester::testSignatureEncryption() {
|
||||
auto key = dht::crypto::PrivateKey::generate();
|
||||
auto public_key = key.getPublicKey();
|
||||
|
||||
std::vector<uint8_t> data {5, 10};
|
||||
std::vector<uint8_t> signature = key.sign(data);
|
||||
|
||||
// check signature
|
||||
CPPUNIT_ASSERT(public_key.checkSignature(data, signature));
|
||||
|
||||
// encrypt data
|
||||
std::vector<uint8_t> encrypted = public_key.encrypt(data);
|
||||
std::vector<uint8_t> decrypted = key.decrypt(encrypted);
|
||||
CPPUNIT_ASSERT(data == decrypted);
|
||||
}
|
||||
|
||||
void
|
||||
CryptoTester::testCertificateRevocation()
|
||||
{
|
||||
auto ca1 = dht::crypto::generateIdentity("ca1");
|
||||
auto account1 = dht::crypto::generateIdentity("acc1", ca1, 4096, true);
|
||||
auto device11 = dht::crypto::generateIdentity("dev11", account1);
|
||||
auto device12 = dht::crypto::generateIdentity("dev12", account1);
|
||||
|
||||
dht::crypto::TrustList list;
|
||||
list.add(*ca1.second);
|
||||
auto v = list.verify(*account1.second);
|
||||
CPPUNIT_ASSERT_MESSAGE(v.toString(), v);
|
||||
|
||||
list.add(*account1.second);
|
||||
v = list.verify(*device11.second);
|
||||
CPPUNIT_ASSERT_MESSAGE(v.toString(), v);
|
||||
v = list.verify(*device12.second);
|
||||
CPPUNIT_ASSERT_MESSAGE(v.toString(), v);
|
||||
|
||||
auto ca2 = dht::crypto::generateIdentity("ca2");
|
||||
auto account2 = dht::crypto::generateIdentity("acc2", ca2, 4096, true);
|
||||
auto device2 = dht::crypto::generateIdentity("dev2", account2);
|
||||
|
||||
v = list.verify(*device2.second);
|
||||
CPPUNIT_ASSERT_MESSAGE(v.toString(), !v);
|
||||
|
||||
account1.second->revoke(*account1.first, *device11.second);
|
||||
dht::crypto::TrustList list2;
|
||||
list2.add(*account1.second);
|
||||
|
||||
v = list2.verify(*device11.second);
|
||||
CPPUNIT_ASSERT_MESSAGE(v.toString(), !v);
|
||||
v = list2.verify(*device12.second);
|
||||
CPPUNIT_ASSERT_MESSAGE(v.toString(), v);
|
||||
}
|
||||
|
||||
void
|
||||
CryptoTester::testCertificateRequest()
|
||||
{
|
||||
// Generate CA
|
||||
auto ca = dht::crypto::generateIdentity("Test CA");
|
||||
|
||||
// Generate signed request
|
||||
auto deviceKey = dht::crypto::PrivateKey::generate();
|
||||
auto request = dht::crypto::CertificateRequest();
|
||||
request.setName("Test Device");
|
||||
request.sign(deviceKey);
|
||||
|
||||
// Export/import request
|
||||
auto importedRequest = dht::crypto::CertificateRequest(request.pack());
|
||||
CPPUNIT_ASSERT(importedRequest.verify());
|
||||
|
||||
// Generate/sign certificate from request
|
||||
auto signedCert = dht::crypto::Certificate::generate(request, ca);
|
||||
CPPUNIT_ASSERT_EQUAL(ca.second->getName(), signedCert.getIssuerName());
|
||||
CPPUNIT_ASSERT_EQUAL(request.getName(), signedCert.getName());
|
||||
|
||||
// Check generated certificate
|
||||
dht::crypto::TrustList list;
|
||||
list.add(*ca.second);
|
||||
auto v = list.verify(signedCert);
|
||||
CPPUNIT_ASSERT_MESSAGE(v.toString(), v);
|
||||
}
|
||||
|
||||
void
|
||||
CryptoTester::tearDown() {
|
||||
|
||||
}
|
||||
} // namespace test
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// cppunit
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
namespace test {
|
||||
|
||||
class CryptoTester : public CppUnit::TestFixture {
|
||||
CPPUNIT_TEST_SUITE(CryptoTester);
|
||||
CPPUNIT_TEST(testSignatureEncryption);
|
||||
CPPUNIT_TEST(testCertificateRevocation);
|
||||
CPPUNIT_TEST(testCertificateRequest);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Method automatically called before each test by CppUnit
|
||||
*/
|
||||
void setUp();
|
||||
/**
|
||||
* Method automatically called after each test CppUnit
|
||||
*/
|
||||
void tearDown();
|
||||
/**
|
||||
* Test data signature, encryption and decryption
|
||||
*/
|
||||
void testSignatureEncryption();
|
||||
/**
|
||||
* Test certificate generation, validation and revocation
|
||||
*/
|
||||
void testCertificateRevocation();
|
||||
/**
|
||||
* Test certificate requests
|
||||
*/
|
||||
void testCertificateRequest();
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dhtproxytester.h"
|
||||
|
||||
// std
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
|
||||
|
||||
namespace test {
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(DhtProxyTester);
|
||||
|
||||
void
|
||||
DhtProxyTester::setUp() {
|
||||
nodePeer.run(42222, {}, true);
|
||||
nodeProxy = std::make_shared<dht::DhtRunner>();
|
||||
nodeClient = std::make_shared<dht::DhtRunner>();
|
||||
|
||||
nodeProxy->run(42232, {}, true);
|
||||
nodeProxy->bootstrap(nodePeer.getBound());
|
||||
server = std::unique_ptr<dht::DhtProxyServer>(new dht::DhtProxyServer(nodeProxy, 8080));
|
||||
|
||||
nodeClient->run(42242, {}, true);
|
||||
nodeClient->bootstrap(nodePeer.getBound());
|
||||
nodeClient->setProxyServer("127.0.0.1:8080");
|
||||
nodeClient->enableProxy(true);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyTester::tearDown() {
|
||||
nodePeer.join();
|
||||
nodeClient->join();
|
||||
server->stop();
|
||||
server = nullptr;
|
||||
nodeProxy->join();
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyTester::testGetPut() {
|
||||
bool done = false;
|
||||
std::condition_variable cv;
|
||||
std::mutex cv_m;
|
||||
|
||||
auto key = dht::InfoHash::get("GLaDOs");
|
||||
dht::Value val {"Hey! It's been a long time. How have you been?"};
|
||||
auto val_data = val.data;
|
||||
|
||||
nodePeer.put(key, std::move(val), [&](bool) {
|
||||
done = true;
|
||||
cv.notify_all();
|
||||
});
|
||||
std::unique_lock<std::mutex> lk(cv_m);
|
||||
cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; });
|
||||
|
||||
auto vals = nodeClient->get(key).get();
|
||||
CPPUNIT_ASSERT(not vals.empty());
|
||||
CPPUNIT_ASSERT(vals.front()->data == val_data);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyTester::testListen() {
|
||||
bool done = false;
|
||||
std::condition_variable cv;
|
||||
std::mutex cv_m;
|
||||
std::unique_lock<std::mutex> lk(cv_m);
|
||||
auto key = dht::InfoHash::get("GLaDOs");
|
||||
|
||||
// If a peer send a value, the listen operation from the client
|
||||
// should retrieve this value
|
||||
dht::Value firstVal {"Hey! It's been a long time. How have you been?"};
|
||||
auto firstVal_data = firstVal.data;
|
||||
nodePeer.put(key, std::move(firstVal), [&](bool) {
|
||||
done = true;
|
||||
cv.notify_all();
|
||||
});
|
||||
cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; });
|
||||
done = false;
|
||||
|
||||
auto values = std::vector<dht::Blob>();
|
||||
nodeClient->listen(key, [&](const std::vector<std::shared_ptr<dht::Value>>& v, bool) {
|
||||
for (const auto& value : v)
|
||||
values.emplace_back(value->data);
|
||||
done = true;
|
||||
cv.notify_all();
|
||||
return true;
|
||||
});
|
||||
|
||||
cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; });
|
||||
done = false;
|
||||
// Here values should contains 1 values
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<int>(values.size()), 1);
|
||||
CPPUNIT_ASSERT(values.front() == firstVal_data);
|
||||
|
||||
// And the listen should retrieve futures values
|
||||
// All values
|
||||
dht::Value secondVal {"You're a monster"};
|
||||
auto secondVal_data = secondVal.data;
|
||||
nodePeer.put(key, std::move(secondVal));
|
||||
cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; });
|
||||
// Here values should contains 2 values
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<int>(values.size()), 2);
|
||||
CPPUNIT_ASSERT(values.back() == secondVal_data);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyTester::testResubscribeGetValues() {
|
||||
nodeClient->setPushNotificationToken("atlas");
|
||||
|
||||
bool done = false;
|
||||
std::condition_variable cv;
|
||||
std::mutex cv_m;
|
||||
std::unique_lock<std::mutex> lk(cv_m);
|
||||
auto key = dht::InfoHash::get("GLaDOs");
|
||||
|
||||
// If a peer send a value, the listen operation from the client
|
||||
// should retrieve this value
|
||||
dht::Value firstVal {"Hey! It's been a long time. How have you been?"};
|
||||
auto firstVal_data = firstVal.data;
|
||||
nodePeer.put(key, std::move(firstVal), [&](bool) {
|
||||
done = true;
|
||||
cv.notify_all();
|
||||
});
|
||||
cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; });
|
||||
done = false;
|
||||
|
||||
// Send a first subscribe, the value is sent via a push notification
|
||||
// So ignore values here.
|
||||
nodeClient->listen(key, [&](const std::vector<std::shared_ptr<dht::Value>>&, bool) {
|
||||
return true;
|
||||
});
|
||||
cv.wait_for(lk, std::chrono::seconds(1));
|
||||
|
||||
// Reboot node (to avoid cache)
|
||||
nodeClient->join();
|
||||
nodeClient->run(42242, {}, true);
|
||||
nodeClient->bootstrap(nodePeer.getBound());
|
||||
nodeClient->setProxyServer("127.0.0.1:8080");
|
||||
nodeClient->enableProxy(true);
|
||||
nodeClient->setPushNotificationToken("atlas");
|
||||
|
||||
// For the second subscribe, the proxy will return the value in the body
|
||||
auto values = std::vector<dht::Blob>();
|
||||
nodeClient->listen(key, [&](const std::vector<std::shared_ptr<dht::Value>>& v, bool) {
|
||||
for (const auto& value : v)
|
||||
values.emplace_back(value->data);
|
||||
done = true;
|
||||
cv.notify_all();
|
||||
return true;
|
||||
});
|
||||
|
||||
cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; });
|
||||
done = false;
|
||||
// Here values should still contains 1 values
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<int>(values.size()), 1);
|
||||
CPPUNIT_ASSERT(values.front() == firstVal_data);
|
||||
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// cppunit
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
#include <opendht/dhtrunner.h>
|
||||
#include <opendht/dht_proxy_server.h>
|
||||
|
||||
namespace test {
|
||||
|
||||
class DhtProxyTester : public CppUnit::TestFixture {
|
||||
CPPUNIT_TEST_SUITE(DhtProxyTester);
|
||||
CPPUNIT_TEST(testGetPut);
|
||||
CPPUNIT_TEST(testListen);
|
||||
CPPUNIT_TEST(testResubscribeGetValues);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Method automatically called before each test by CppUnit
|
||||
* Init nodes
|
||||
*/
|
||||
void setUp();
|
||||
/**
|
||||
* Method automatically called after each test CppUnit
|
||||
*/
|
||||
void tearDown();
|
||||
/**
|
||||
* Test get and put methods
|
||||
*/
|
||||
void testGetPut();
|
||||
/**
|
||||
* Test listen
|
||||
*/
|
||||
void testListen();
|
||||
|
||||
/**
|
||||
* When a proxy redo a subscribe on the proxy
|
||||
* it should retrieve existant values
|
||||
*/
|
||||
void testResubscribeGetValues();
|
||||
|
||||
private:
|
||||
dht::DhtRunner nodePeer {};
|
||||
|
||||
std::shared_ptr<dht::DhtRunner> nodeClient;
|
||||
std::shared_ptr<dht::DhtRunner> nodeProxy;
|
||||
std::unique_ptr<dht::DhtProxyServer> server;
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dhtrunnertester.h"
|
||||
|
||||
// std
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace test {
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(DhtRunnerTester);
|
||||
|
||||
void
|
||||
DhtRunnerTester::setUp() {
|
||||
node1.run(42222, {}, true);
|
||||
node2.run(42232, {}, true);
|
||||
node2.bootstrap(node1.getBound());
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunnerTester::tearDown() {
|
||||
node1.join();
|
||||
node2.join();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunnerTester::testConstructors() {
|
||||
CPPUNIT_ASSERT(node1.getBoundPort() == 42222);
|
||||
CPPUNIT_ASSERT(node2.getBoundPort() == 42232);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunnerTester::testGetPut() {
|
||||
auto key = dht::InfoHash::get("123");
|
||||
dht::Value val {"hey"};
|
||||
auto val_data = val.data;
|
||||
std::promise<bool> p;
|
||||
node2.put(key, std::move(val), [&](bool ok){
|
||||
p.set_value(ok);
|
||||
});
|
||||
CPPUNIT_ASSERT(p.get_future().get());
|
||||
auto vals = node1.get(key).get();
|
||||
CPPUNIT_ASSERT(not vals.empty());
|
||||
CPPUNIT_ASSERT(vals.front()->data == val_data);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunnerTester::testListen() {
|
||||
std::atomic_uint valueCount(0);
|
||||
std::atomic_uint putCount(0);
|
||||
|
||||
auto a = dht::InfoHash::get("234");
|
||||
auto b = dht::InfoHash::get("2345");
|
||||
auto c = dht::InfoHash::get("23456");
|
||||
constexpr unsigned N = 32;
|
||||
|
||||
auto ftokena = node1.listen(a, [&](const std::shared_ptr<dht::Value>&){
|
||||
valueCount++;
|
||||
return true;
|
||||
});
|
||||
|
||||
auto ftokenb = node1.listen(b, [&](const std::shared_ptr<dht::Value>&){
|
||||
valueCount++;
|
||||
return false;
|
||||
});
|
||||
|
||||
auto ftokenc = node1.listen(c, [&](const std::shared_ptr<dht::Value>&){
|
||||
valueCount++;
|
||||
return true;
|
||||
});
|
||||
|
||||
for (unsigned i=0; i<N; i++) {
|
||||
node2.put(a, dht::Value("v1"), [&](bool ok) { if (ok) putCount++; });
|
||||
node2.put(b, dht::Value("v2"), [&](bool ok) { if (ok) putCount++; });
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
auto tokena = ftokena.get();
|
||||
auto tokenb = ftokenb.get();
|
||||
auto tokenc = ftokenc.get();
|
||||
|
||||
CPPUNIT_ASSERT(tokena);
|
||||
CPPUNIT_ASSERT(tokenb);
|
||||
CPPUNIT_ASSERT(tokenc);
|
||||
CPPUNIT_ASSERT_EQUAL(N * 2u, putCount.load());
|
||||
CPPUNIT_ASSERT_EQUAL(N + 1u, valueCount.load());
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// cppunit
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
#include <opendht/dhtrunner.h>
|
||||
|
||||
namespace test {
|
||||
|
||||
class DhtRunnerTester : public CppUnit::TestFixture {
|
||||
CPPUNIT_TEST_SUITE(DhtRunnerTester);
|
||||
CPPUNIT_TEST(testConstructors);
|
||||
CPPUNIT_TEST(testGetPut);
|
||||
CPPUNIT_TEST(testListen);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
dht::DhtRunner node1 {};
|
||||
dht::DhtRunner node2 {};
|
||||
public:
|
||||
/**
|
||||
* Method automatically called before each test by CppUnit
|
||||
*/
|
||||
void setUp();
|
||||
/**
|
||||
* Method automatically called after each test CppUnit
|
||||
*/
|
||||
void tearDown();
|
||||
/**
|
||||
* Test the differents behaviors of constructors
|
||||
*/
|
||||
void testConstructors();
|
||||
/**
|
||||
* Test get and put methods
|
||||
*/
|
||||
void testGetPut();
|
||||
/**
|
||||
* Test listen method
|
||||
*/
|
||||
void testListen();
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "infohashtester.h"
|
||||
|
||||
// std
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
// opendht
|
||||
#include "opendht/infohash.h"
|
||||
|
||||
namespace test {
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(InfoHashTester);
|
||||
|
||||
void
|
||||
InfoHashTester::setUp() {
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
InfoHashTester::testConstructors() {
|
||||
// Default constructor creates a null infohash
|
||||
auto nullHash = dht::InfoHash();
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)20u, nullHash.size());
|
||||
CPPUNIT_ASSERT(!nullHash);
|
||||
// Build from a uint8_t. if length to short, should get a null infohash
|
||||
uint8_t to_short[] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8};
|
||||
auto infohash = dht::InfoHash(to_short, 8);
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)20u, infohash.size());
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("0000000000000000000000000000000000000000"), infohash.toString());
|
||||
// Build from a uint8_t. if length is enough, data should contains the uint8_t
|
||||
uint8_t enough[] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa,
|
||||
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa};
|
||||
infohash = dht::InfoHash(enough, 20);
|
||||
CPPUNIT_ASSERT(infohash.size() == 20);
|
||||
const auto* data = infohash.data();
|
||||
for (auto i = 0; i < 20; ++i) {
|
||||
CPPUNIT_ASSERT_EQUAL(enough[i], data[i]);
|
||||
}
|
||||
// if too long, should be cutted to 20
|
||||
uint8_t tooLong[] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa,
|
||||
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb0};
|
||||
infohash = dht::InfoHash(tooLong, 21);
|
||||
CPPUNIT_ASSERT(infohash.size() == 20);
|
||||
const auto* data2 = infohash.data();
|
||||
for (auto i = 0; i < 20; ++i) {
|
||||
CPPUNIT_ASSERT_EQUAL(enough[i], data2[i]);
|
||||
}
|
||||
// Build from string
|
||||
auto infohashFromStr = dht::InfoHash("0102030405060708090A0102030405060708090A");
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)20u, infohashFromStr.size());
|
||||
const auto* dataStr = infohashFromStr.data();
|
||||
for (auto i = 0; i < 20; ++i) {
|
||||
CPPUNIT_ASSERT_EQUAL((int)dataStr[i], (int)data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InfoHashTester::testComperators() {
|
||||
auto nullHash = dht::InfoHash();
|
||||
auto minHash = dht::InfoHash("0000000000000000000000000000000000111110");
|
||||
auto maxHash = dht::InfoHash("0111110000000000000000000000000000000000");
|
||||
// operator ==
|
||||
CPPUNIT_ASSERT_EQUAL(minHash, minHash);
|
||||
CPPUNIT_ASSERT_EQUAL(minHash, dht::InfoHash("0000000000000000000000000000000000111110"));
|
||||
CPPUNIT_ASSERT(!(minHash == maxHash));
|
||||
// operator !=
|
||||
CPPUNIT_ASSERT(!(minHash != minHash));
|
||||
CPPUNIT_ASSERT(!(minHash != dht::InfoHash("0000000000000000000000000000000000111110")));
|
||||
CPPUNIT_ASSERT(minHash != maxHash);
|
||||
// operator<
|
||||
CPPUNIT_ASSERT(nullHash < minHash);
|
||||
CPPUNIT_ASSERT(nullHash < maxHash);
|
||||
CPPUNIT_ASSERT(minHash < maxHash);
|
||||
CPPUNIT_ASSERT(!(minHash < nullHash));
|
||||
CPPUNIT_ASSERT(!(maxHash < nullHash));
|
||||
CPPUNIT_ASSERT(!(maxHash < minHash));
|
||||
CPPUNIT_ASSERT(!(minHash < minHash));
|
||||
// bool()
|
||||
CPPUNIT_ASSERT(maxHash);
|
||||
CPPUNIT_ASSERT(!nullHash);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
InfoHashTester::testLowBit() {
|
||||
auto nullHash = dht::InfoHash();
|
||||
auto minHash = dht::InfoHash("0000000000000000000000000000000000000010");
|
||||
auto maxHash = dht::InfoHash("0100000000000000000000000000000000000000");
|
||||
CPPUNIT_ASSERT_EQUAL(nullHash.lowbit(), -1);
|
||||
CPPUNIT_ASSERT_EQUAL(minHash.lowbit(), 155);
|
||||
CPPUNIT_ASSERT_EQUAL(maxHash.lowbit(), 7);
|
||||
}
|
||||
|
||||
void
|
||||
InfoHashTester::testCommonBits() {
|
||||
auto nullHash = dht::InfoHash();
|
||||
auto minHash = dht::InfoHash("0000000000000000000000000000000000000010");
|
||||
auto maxHash = dht::InfoHash("0100000000000000000000000000000000000000");
|
||||
CPPUNIT_ASSERT_EQUAL(dht::InfoHash::commonBits(nullHash, nullHash), (unsigned)160);
|
||||
CPPUNIT_ASSERT_EQUAL(dht::InfoHash::commonBits(nullHash, minHash), (unsigned)155);
|
||||
CPPUNIT_ASSERT_EQUAL(dht::InfoHash::commonBits(nullHash, maxHash), (unsigned)7);
|
||||
CPPUNIT_ASSERT_EQUAL(dht::InfoHash::commonBits(minHash, maxHash), (unsigned)7);
|
||||
}
|
||||
|
||||
void
|
||||
InfoHashTester::testXorCmp() {
|
||||
auto nullHash = dht::InfoHash();
|
||||
auto minHash = dht::InfoHash("0000000000000000000000000000000000000010");
|
||||
auto maxHash = dht::InfoHash("0100000000000000000000000000000000000000");
|
||||
CPPUNIT_ASSERT_EQUAL(minHash.xorCmp(nullHash, maxHash), -1);
|
||||
CPPUNIT_ASSERT_EQUAL(minHash.xorCmp(maxHash, nullHash), 1);
|
||||
CPPUNIT_ASSERT_EQUAL(minHash.xorCmp(minHash, maxHash), -1);
|
||||
CPPUNIT_ASSERT_EQUAL(minHash.xorCmp(maxHash, minHash), 1);
|
||||
CPPUNIT_ASSERT_EQUAL(nullHash.xorCmp(minHash, maxHash), -1);
|
||||
CPPUNIT_ASSERT_EQUAL(nullHash.xorCmp(maxHash, minHash), 1);
|
||||
// Because hashes are circular in distance.
|
||||
CPPUNIT_ASSERT_EQUAL(maxHash.xorCmp(nullHash, minHash), -1);
|
||||
CPPUNIT_ASSERT_EQUAL(maxHash.xorCmp(minHash, nullHash), 1);
|
||||
}
|
||||
|
||||
void
|
||||
InfoHashTester::tearDown() {
|
||||
|
||||
}
|
||||
} // namespace test
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// cppunit
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
namespace test {
|
||||
|
||||
class InfoHashTester : public CppUnit::TestFixture {
|
||||
CPPUNIT_TEST_SUITE(InfoHashTester);
|
||||
CPPUNIT_TEST(testConstructors);
|
||||
CPPUNIT_TEST(testComperators);
|
||||
CPPUNIT_TEST(testLowBit);
|
||||
CPPUNIT_TEST(testCommonBits);
|
||||
CPPUNIT_TEST(testXorCmp);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Method automatically called before each test by CppUnit
|
||||
*/
|
||||
void setUp();
|
||||
/**
|
||||
* Method automatically called after each test CppUnit
|
||||
*/
|
||||
void tearDown();
|
||||
/**
|
||||
* Test the differents behaviors of constructors
|
||||
*/
|
||||
void testConstructors();
|
||||
/**
|
||||
* Test compare operators
|
||||
*/
|
||||
void testComperators();
|
||||
/**
|
||||
* Test lowbit method
|
||||
*/
|
||||
void testLowBit();
|
||||
/**
|
||||
* Test commonBits method
|
||||
*/
|
||||
void testCommonBits();
|
||||
/**
|
||||
* Test xorCmp operators
|
||||
*/
|
||||
void testXorCmp();
|
||||
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2019 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
#include <cppunit/extensions/TestFactoryRegistry.h>
|
||||
#include <cppunit/ui/text/TestRunner.h>
|
||||
#include <cppunit/CompilerOutputter.h>
|
||||
#include <iostream>
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
CppUnit::TestFactoryRegistry ®istry = CppUnit::TestFactoryRegistry::getRegistry();
|
||||
CppUnit::Test *suite = registry.makeTest();
|
||||
if (suite->countTestCases() == 0) {
|
||||
std::cout << "No test cases specified for suite" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
CppUnit::TextUi::TestRunner runner;
|
||||
runner.addTest(suite);
|
||||
auto result = runner.run() ? 0 : 1;
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "valuetester.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
// opendht
|
||||
#include "opendht/value.h"
|
||||
|
||||
namespace test {
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(ValueTester);
|
||||
|
||||
void
|
||||
ValueTester::setUp() {
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
ValueTester::testConstructors() {
|
||||
std::string the_data {"42 cats"};
|
||||
dht::Value the_dht_value {(const uint8_t*)the_data.data(), the_data.size()};
|
||||
std::string from_value {the_dht_value.data.begin(), the_dht_value.data.end()};
|
||||
CPPUNIT_ASSERT_EQUAL(the_data, from_value);
|
||||
}
|
||||
|
||||
void
|
||||
ValueTester::testFilter()
|
||||
{
|
||||
dht::Value::Filter defaultFiler {};
|
||||
|
||||
auto isPairSize = dht::Value::Filter([](const dht::Value& v) {
|
||||
return v.data.size() % 2 == 0;
|
||||
});
|
||||
|
||||
auto isUserTypeTest = dht::Value::Filter([](const dht::Value& v) {
|
||||
return v.user_type == "test";
|
||||
});
|
||||
|
||||
std::string data1 {"42 cats"};
|
||||
dht::Value value1 {(const uint8_t*)data1.data(), data1.size()};
|
||||
value1.user_type = "test";
|
||||
|
||||
std::string data2 {"420 cats"};
|
||||
dht::Value value2 {(const uint8_t*)data2.data(), data2.size()};
|
||||
dht::Value value3 {(const uint8_t*)data2.data(), data2.size()};
|
||||
value3.user_type = "test";
|
||||
|
||||
CPPUNIT_ASSERT(!isPairSize(value1));
|
||||
CPPUNIT_ASSERT(isUserTypeTest(value1));
|
||||
|
||||
auto isBoth = dht::Value::Filter::chain(isPairSize, isUserTypeTest);
|
||||
auto isUserTypeTest2 = dht::Value::Filter::chain(defaultFiler, isUserTypeTest);
|
||||
|
||||
CPPUNIT_ASSERT(isUserTypeTest2(value1));
|
||||
CPPUNIT_ASSERT(!isUserTypeTest2(value2));
|
||||
CPPUNIT_ASSERT(!isBoth(value1));
|
||||
CPPUNIT_ASSERT(!isBoth(value2));
|
||||
CPPUNIT_ASSERT(isBoth(value3));
|
||||
}
|
||||
|
||||
void
|
||||
ValueTester::tearDown() {
|
||||
|
||||
}
|
||||
} // namespace test
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// cppunit
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
namespace test {
|
||||
|
||||
class ValueTester : public CppUnit::TestFixture {
|
||||
CPPUNIT_TEST_SUITE(ValueTester);
|
||||
CPPUNIT_TEST(testConstructors);
|
||||
CPPUNIT_TEST(testFilter);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Method automatically called before each test by CppUnit
|
||||
*/
|
||||
void setUp();
|
||||
/**
|
||||
* Method automatically called after each test CppUnit
|
||||
*/
|
||||
void tearDown();
|
||||
/**
|
||||
* Test the differents behaviors of constructors
|
||||
*/
|
||||
void testConstructors();
|
||||
/**
|
||||
* Test compare operators
|
||||
*/
|
||||
void testFilter();
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
bin_PROGRAMS = dhtnode dhtchat dhtscanner
|
||||
noinst_HEADERS = tools_common.h
|
||||
|
||||
AM_CPPFLAGS = -I../include
|
||||
AM_CPPFLAGS = -I../include @JsonCpp_CFLAGS@ @MsgPack_CFLAGS@
|
||||
|
||||
dhtnode_SOURCES = dhtnode.cpp
|
||||
dhtnode_LDFLAGS = -lopendht -lreadline -L@top_builddir@/src/.libs @Argon2_LDFLAGS@ @GnuTLS_LIBS@
|
||||
|
||||
Loaded 100 of 105 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user