Compare commits

..
134 Commits
Author SHA1 Message Date
Adrien Béraud b3511d223b sockaddr: use more precise exception 2018-12-17 16:41:00 -05:00
Adrien Béraud d1aca77809 securedht: move crypto ops to Value 2018-12-17 16:41:00 -05:00
Adrien Béraud 306d573223 proxy client: fix race condition 2018-12-17 16:41:00 -05:00
Sébastien Blin 20f4355b73 Merge pull request #356 from savoirfairelinux/build
securedht: add override
2018-12-11 14:52:07 -05:00
Adrien Béraud a2d7cd56a8 Merge pull request #358 from atraczyk/master
replace _write with send when building for windows
2018-12-11 14:48:26 -05:00
Andreas Traczyk 5cd53f3624 replace _write with send when building for windows 2018-12-11 14:37:14 -05:00
Adrien Béraud 32eea55f10 autotools: don't build doc if doxygen not found 2018-12-11 14:04:35 -05:00
Adrien Béraud f2f913f93f securedht: add override for relevant methods 2018-12-11 14:04:35 -05:00
Adrien Béraud cb9fe9d1fe Merge pull request #354 from atraczyk/master
msvc: replace write with _write when building on windows
2018-12-06 12:23:36 -05:00
Andreas Traczyk dcab32ef4f replace write with _write when building with msvc 2018-12-06 12:16:59 -05:00
Adrien Béraud 4948bd7ad3 node cache: remove expired weak pointers from cache
Remove expired Node weak pointers regularly,
in a way that ensures that:
* every expired pointer gets eventually cleaned up
* expired pointer ratio remains reasonably low
* no cleanup pass blocks the node for too long,
* impact on performance is negligible

Impact on performance:
Every N insertions, at most N nodes are tested by iterating the map
-> performance impact in O(1) (constant).
2018-11-29 19:27:32 -05:00
Adrien Béraud 4416b91461 update to version 1.8.0 2018-11-28 18:44:03 -05:00
Adrien Béraud 378ae396f0 Merge pull request #351 from hanyf/master
A obvious bug on network engine
2018-11-28 12:48:02 -05:00
Yanfei Han dc4a790a38 A obviouse bug on network engine 2018-11-29 00:47:00 +08:00
Sébastien Blin 07dd8f5648 Merge pull request #350 from savoirfairelinux/feature/cache
Feature/cache
2018-11-28 09:22:06 -05:00
Adrien Béraud f7433a2289 travis: update to xenial 2018-11-26 16:48:00 -05:00
Adrien Béraud ce4a65ff00 dhtrunner: add APIs for listen<T> with expiration information 2018-11-26 16:34:19 -05:00
Adrien Béraud 092f5cfef0 network_engine: fix warning 2018-11-26 16:34:19 -05:00
Adrien Béraud 12a45e7558 cmake: remove unnecessary flags for lto 2018-11-26 16:30:18 -05:00
Adrien Béraud 18d359a7d2 dht: refresh value if already exists 2018-11-26 01:57:57 -05:00
Adrien Béraud c6bb67d1ac dht: add storageRefresh 2018-11-26 01:57:57 -05:00
Adrien Béraud f4cd7636ce securedht: use signature cache in storePolicy, editPolicy 2018-11-26 01:57:57 -05:00
Adrien Béraud 2dd4e640c4 dhtrunner: add APIs for listen<T> with expiration information 2018-11-25 23:01:03 -05:00
Adrien Béraud 9a301e3590 Revert "cmake: use CMAKE_INTERPROCEDURAL_OPTIMIZATION for LTO"
This reverts commit b7b5152348.
2018-11-24 15:32:29 -05:00
Sébastien Blin aa16b9a235 Merge pull request #348 from savoirfairelinux/feature/probepermanent
dht: don't send probe query for non permanent puts
2018-11-23 10:10:35 -05:00
Adrien Béraud b2542a57df securedht: cache value signature check/decryption results 2018-11-22 15:43:55 -05:00
Adrien Béraud 00c1be9cdc logger: use DBG for DEBUG to avoid name conflicts 2018-11-22 15:43:55 -05:00
Adrien Béraud e414ca89c7 dht: don't send probe query for non permanent puts 2018-11-22 15:43:55 -05:00
Adrien Béraud 4d26537b93 Merge pull request #346 from AmarOk1412/fix/finished
dht_proxy_client: fix finished behavior
2018-11-11 22:19:50 -05:00
Sébastien Blin 00a9333de7 dht_proxy_client: fix finished behavior
callback_.emplace_back() returns immediately, so, in some case,
finished can be set to true even before we execute one callback.
Finished should not be used into callback. This patch adds a
finished variable only used by the callbacks to avoid this
case.

This improve the reactivity of the proxy client.
2018-11-11 15:27:57 -05:00
Adrien Béraud 971f96bf95 dhtrunner: improve getId()/getNodeId() documentation. 2018-10-29 16:23:21 -04:00
Adrien Béraud f9a12a9dca crypto: use proper size for getRandom()
Provided size should be the number of elements, not the number of bytes.
Also, if secure_vector where used with sizeof(T) > 1 (currently not the case),
there would be memory write to invalid address.
2018-10-29 16:21:51 -04:00
Adrien Béraud c05a93ae54 proxy server: cleanup 2018-10-29 12:45:52 -04:00
Adrien Béraud ff83a6daab proxy: support value expiration 2018-10-29 12:45:52 -04:00
Adrien Béraud b7b5152348 cmake: use CMAKE_INTERPROCEDURAL_OPTIMIZATION for LTO 2018-10-27 16:38:37 -04:00
Adrien Béraud a3768d1e50 cmake: use LINK_FLAGS for LTO 2018-10-27 14:57:51 -04:00
Adrien Béraud 94926cea22 proxy server: avoid early session close 2018-10-27 13:21:26 -04:00
Adrien Béraud 82ca2d1993 cmake: use single target_link_libraries for static library 2018-10-27 12:30:40 -04:00
Adrien Béraud 3e148599c5 proxy server: stop get if session is closed 2018-10-27 12:20:00 -04:00
Adrien Béraud 02d5592fdb cmake: improve restbed, jsoncpp search & usage 2018-10-27 11:51:42 -04:00
Adrien Béraud 328b306fd0 crypto: add documentation 2018-10-27 11:50:26 -04:00
Adrien Béraud 522a89b6f8 dht: use search cache for get ops 2018-10-27 11:49:55 -04:00
Adrien Béraud 897b451f44 crypto: fix build on Win32 2018-10-26 13:16:02 -04:00
Adrien Béraud ba3a42697c Merge pull request #331 from Dale-M/master
Some additions to automake infrastructure to get clean default build.
2018-09-20 14:24:34 -04:00
Adrien Béraud 5112a322de dht: reschedule permanent put refresh after request 2018-09-19 01:54:54 -04:00
Adrien Béraud 85961c6a62 storage: properly refresh expiration 2018-09-19 01:54:54 -04:00
Adrien Béraud 1eb1f3d5ac storageStore: add permanent mode 2018-09-19 01:54:54 -04:00
Adrien Béraud 726c138d90 dht: move storageStore to put 2018-09-19 01:54:54 -04:00
Adrien Béraud 1b6b1ca072 comments: fix typos 2018-09-19 01:54:54 -04:00
Dale Mellor 77da72c603 Some additions to automake infrastructure to get clean default build.
Be sure to pull in paths to headers for json and MsgPack dependencies.

Be sure to always build the base64 encoder and decoder, as they are
needed.
2018-09-17 09:40:07 +01:00
Adrien Béraud 7fb2da7c54 dhtrunner: avoid polling, use pipe to stop select 2018-09-15 17:22:12 -04:00
Sébastien Blin 6721a272ea Merge pull request #327 from savoirfairelinux/value_cache
value_cache: expire value if expiration is now
2018-08-12 10:42:22 -04:00
Adrien Béraud 246af2f065 value_cache: expire value if expiration is now 2018-08-11 21:27:18 -04:00
Adrien Béraud 57aba453b0 Merge pull request #325 from AmarOk1412/siteProxy
proxyserver: add stats endpoint for monitoring
2018-08-10 15:05:07 -04:00
Sébastien Blin f54fcd768f proxyserver: add stats endpoint for monitoring 2018-08-10 15:03:09 -04:00
Sébastien Blin 5b81476baf Merge pull request #326 from savoirfairelinux/tests
tests: add unit tests for certificate revocation
2018-08-09 17:11:20 -04:00
Adrien Béraud 5b510cadfb tests: add unit tests for certificate revocation 2018-08-09 14:51:02 -04:00
Adrien Béraud c794781644 update to version 1.7.4 2018-07-31 13:45:56 -04:00
Adrien Béraud 2ef4999b0f Merge pull request #322 from AmarOk1412/incorrectOk
dhtproxyclient: fix ok when a permanent PUT operations fails
2018-07-31 10:16:05 -04:00
Sébastien Blin 6069fba18f dhtproxyclient: fix ok when a permanent PUT operations fails 2018-07-31 09:42:23 -04:00
Adrien Béraud 37ddd8c7f9 update to version 1.7.3 2018-07-25 16:40:47 -04:00
Adrien Béraud 23057a3082 unit tests: fix race condition 2018-07-25 11:54:28 -04:00
Adrien Béraud dbdd3f8f63 Merge pull request #319 from AmarOk1412/fixLD
dhtrunner: don't use listeners structure without proxy client enabled
2018-07-25 11:32:55 -04:00
Sébastien Blin 751e79a250 dhtrunner: don't use listeners structure without proxy client enabled
The token used to cancel a listener was wrong. Indeed, tokenBGlobal
was used to get the linked listener, not to identify the listener
inside dhtrunner.
2018-07-25 11:28:46 -04:00
Adrien Béraud 7a961ffc51 Merge pull request #318 from atraczyk/master
build: replace WIN32_NATIVE macro with _MSC_VER
2018-07-23 14:01:17 -04:00
Andreas Traczyk bfada958b9 build: replace WIN32_NATIVE macro with _MSC_VER 2018-07-23 13:45:52 -04:00
Sébastien Blin 78d91a8e0d Merge pull request #316 from savoirfairelinux/proxy_server
dhtrunner: decrease max packet handling delay to 500ms
2018-07-13 14:37:09 -04:00
Adrien Béraud cd5dc38a57 dhtrunner: decrease max packet handling delay to 500ms
Allows for faster recovery in case of CPU contention
2018-07-13 14:29:59 -04:00
Sébastien Blin 1ff2eeb220 Merge pull request #313 from savoirfairelinux/proxy_server
Proxy server
2018-06-28 09:16:13 -04:00
Adrien Béraud ffbb9e23ef dht: show usage at the end, print max store size 2018-06-27 11:54:15 -04:00
Adrien Béraud af0a30c9c5 dhtrunner: drop oldest packet if queue is full 2018-06-26 20:41:04 -04:00
Sébastien Blin 97d5e3875d Merge pull request #312 from savoirfairelinux/dht_performance
Dht performance
2018-06-21 11:49:09 -04:00
Adrien Béraud 2509e8bc5c Revert "search: use pointer to SearchNode"
This reverts commit 3a41f2ce56.
2018-06-20 14:50:40 -04:00
Adrien Béraud e349912c07 search: improve canGet performance 2018-06-20 14:50:40 -04:00
Adrien Béraud a1fc266eac dhtrunner: add max rx queue size 2018-06-19 14:10:57 -04:00
Adrien Béraud 3e3679bebf dhtrunner: drop packet if processing took too long 2018-06-18 15:52:20 -04:00
Adrien Béraud e9cf16f361 cmake: allow to enable push notifications without proxy server 2018-06-15 21:09:37 -04:00
Adrien Béraud 3dca4db408 dhtrunner: don't setProxyServer in headers
Don't define setProxyServer in headers since it depends on a configuration option
2018-06-15 12:18:56 -04:00
Sébastien Blin fb6d290692 Merge pull request #309 from savoirfairelinux/searchnode
SearchNode: refresh token
2018-06-13 10:35:51 -04:00
Adrien Béraud 96c60d39ed proxy server: reschedule after using function 2018-06-12 18:19:12 -04:00
Adrien Béraud 3a41f2ce56 search: use pointer to SearchNode 2018-06-12 16:54:12 -04:00
Adrien Béraud 5bd88533fe SearchNode: refresh token in Query, Listen answers 2018-06-12 16:50:15 -04:00
Adrien Béraud c48e24403b Merge pull request #296 from AmarOk1412/resub
proxy: refresh puts and subscriptions when connectivity changes
2018-06-06 12:18:58 -04:00
Sébastien Blin 3cf2c092f2 proxy: remove useless convertion from InfoHash to InfoHash 2018-06-06 12:16:10 -04:00
Sébastien Blin aef8aa545b proxy: reschedule printStatsJob 2018-06-06 12:08:31 -04:00
Sébastien Blin 0c61a605ad proxy: refresh puts and subscriptions when connectivity changes 2018-06-06 11:29:40 -04:00
Sébastien Blin e6b247bead Merge pull request #307 from savoirfairelinux/crypto_tests
test: add crypto unit test
2018-06-06 10:05:10 -04:00
Sébastien Blin 6f1f18f128 Merge pull request #302 from savoirfairelinux/getnodeinfo
dhtrunner: add getNodeInfo()
2018-06-06 10:04:57 -04:00
Adrien Béraud f24458860d test: add crypto unit test 2018-06-06 01:12:18 -04:00
Adrien Béraud 07bed77714 proxy server: schedule task from outside thread 2018-06-05 19:43:56 -04:00
Adrien Béraud aa202b2cd8 dhtrunner: add getNodeInfo()
Allow to retrieve all dht stats in a single API call using
the new method getNodeInfo() and the JSON-serializable
new structure NodeInfo.
2018-06-05 19:43:56 -04:00
Adrien Béraud 4c89ce9d3f Merge pull request #303 from AmarOk1412/getNodeStats
dht_proxy_server: avoid mutex when getting /
2018-05-30 17:01:56 -04:00
Sébastien Blin 709a2c7b7c dht_proxy_server: avoid mutex when getting /
The dht_proxy_server should be always up, so getNodesStats will
be pretty always up-to-date. We can add it into a job and avoid
to wait for this method to finish (and this method use an
internal lock) to have a quick answer about the status of the proxy.

Note: the update is forced during the initialization of the proxy
2018-05-30 14:25:46 -04:00
Adrien Béraud b7e483078a proxy: fix typo 2018-05-25 10:26:58 -04:00
Adrien Béraud b2171d707a network engine: properly apply flag MSG_NOSIGNAL 2018-05-25 10:26:58 -04:00
Adrien Béraud 60db9fd9cd logs: reduce node expiration to debug level 2018-05-25 10:26:58 -04:00
Adrien Béraud e8989f1f79 network engine: handle EPERM 2018-05-24 16:07:16 -04:00
Adrien Béraud b7218bd1c9 Merge pull request #299 from savoirfairelinux/tests
travis: run CI with ASAN
2018-05-24 15:46:56 -04:00
Adrien Béraud 43aab40968 travis: run CI with ASAN 2018-05-24 15:31:52 -04:00
Sébastien Blin ed57b3bfa9 Merge pull request #300 from savoirfairelinux/leak
network engine: fix node leak during destruction
2018-05-24 15:14:22 -04:00
Adrien Béraud 9249f34c47 network engine: fix node leak during destruction 2018-05-24 15:01:23 -04:00
Sébastien Blin 57401e8d53 Merge pull request #297 from savoirfairelinux/logs
log: fix LogMethod copy
2018-05-24 14:55:06 -04:00
Adrien Béraud f5f71a0365 log: fix LogMethod copy 2018-05-24 11:57:12 -04:00
Adrien Béraud 1fe9f2fdb1 dhtnode: default-initialize dht config 2018-05-23 11:21:25 -04:00
Sébastien Blin 59d129b8ff Merge pull request #293 from savoirfairelinux/proxy_fixes
Proxy fixes
2018-05-17 14:47:16 -04:00
Adrien Béraud 5b5349d7ad proxy client: lock proxy info thread, catch detach exceptions 2018-05-17 11:34:25 -04:00
Adrien Béraud b2ba1ca002 proxy server: close socket if no token 2018-05-17 11:33:32 -04:00
Adrien Béraud 8606e87684 proxy client: allow concurrent proxy info requests (#292) 2018-05-11 15:47:24 -04:00
Adrien Béraud e4f640f11b update to version 1.7.2 2018-05-08 15:58:24 -04:00
Adrien Béraud e31d301d90 dhtrunner: run connectivityChanged() even when disconnected 2018-05-08 15:58:24 -04:00
Adrien Béraud 2a15435be3 Merge pull request #291 from AmarOk1412/fixPushIdle
proxy: set status to CONNECTED when receiving a push notifications
2018-05-08 13:46:59 -04:00
Adrien Béraud 44a411ba96 Merge pull request #279 from savoirfairelinux/testsproxy
tests: begin tests for dht_proxy_server
2018-05-08 13:46:42 -04:00
Sébastien Blin bf2f2369b1 proxy: set status to CONNECTED when receiving a push notifications
If a device receives a push notification, we can assume that the
proxy is up and running (because it sends a push notification).

Indeed, if the proxy is declared as disconnected, no operatons will
be executed when a push notification is received. This state is logic
when not using push notifications (because the local node is down),
but here, the status depends more on the proxy than the local node.
2018-05-08 13:35:13 -04:00
Adrien Béraud b8b1f61268 search: announce immediately if synced and not pending/completed 2018-05-07 17:43:31 -04:00
Adrien Béraud 63db2eafd9 dht: print detailed request state in search log 2018-05-07 17:43:31 -04:00
Adrien Béraud 68f52a815d proxy client: refactor (#289)
Factor various ways of sending listen requests.
2018-05-07 14:54:32 -04:00
Adrien Béraud 9f35acf559 dhtnode: continue cmd loop on connectivity change 2018-05-05 18:23:52 -04:00
Adrien Béraud d8e82ca7c4 dhtrunner: check dht before getting public addresses 2018-05-04 21:41:39 -04:00
Adrien Béraud 981bcfc0ba dht: handle error when sending listen 2018-05-02 14:26:24 -04:00
Adrien Béraud 4a49a53222 Revert "network engine: expire node on next run on network failure"
This reverts commit 8489433060.
2018-05-02 11:55:01 -04:00
Sébastien Blin 9f987e24fe Merge pull request #277 from savoirfairelinux/proxy_refresh
proxy client: independently resubscribe push listeners
2018-04-30 10:56:18 -04:00
Sébastien Blin 95f1d59a7f proxy: add comment for refresh job 2018-04-30 10:42:39 -04:00
Adrien Béraud a4fe27223f immessage: fix warning 2018-04-30 10:40:47 -04:00
Adrien Béraud 5408dc6f09 Merge pull request #287 from AmarOk1412/immsgwithdt
immessage: add support for custom datatypes
2018-04-27 13:34:23 -04:00
Adrien Béraud 35d4c1d332 Merge pull request #288 from AmarOk1412/restartListenerWithoutPush
proxy: restart listeners if the proxy server close the connection
2018-04-27 13:33:59 -04:00
Sébastien Blin c583b7284c tests: add testListen for DhtProxyTester 2018-04-26 17:05:23 -04:00
Sébastien Blin 2ce0c5c4c9 proxy: restart listeners if the proxy server close the connection
Detect if the proxy server close the connection linked to a listener and
relaunch listeners
2018-04-26 16:45:14 -04:00
Sébastien Blin 484194c114 immessage: add support for custom datatypes 2018-04-23 17:39:37 -04:00
Adrien Béraud b26b78c049 dhtrunner: fix warnings 2018-04-21 17:57:59 -04:00
Adrien Béraud 522a15c410 proxy client: refresh persistent put with margin 2018-04-21 17:57:59 -04:00
Adrien Béraud 6a981772fd proxy server: cleanup 2018-04-21 17:57:59 -04:00
Adrien Béraud cc37fb50e2 proxy client: take persistent put by reference 2018-04-21 17:44:45 -04:00
Sébastien Blin 7f943e07a5 tests: begin tests for dht_proxy_server
Like dhtrunnertester add testGetPut for the dht_proxy_server.
2018-04-19 13:08:26 -04:00
Adrien Béraud f98658f10f proxy client: independently resubscribe push listeners 2018-04-19 11:32:22 -04:00
43 changed files with 1618 additions and 772 deletions

No files matched your search

+2 -1
View File
@@ -1,3 +1,4 @@
dist: xenial
sudo: required
services:
@@ -37,7 +38,7 @@ script:
# proxy builds
if [[ "$OPENDHT_TEST_JOB" != *"opendht.llvm"* ]] && [[ "$OPENDHT_TEST_JOB" != *"opendht.classic"* ]]; then
docker build -t opendht-proxy -f docker/DockerfileTravisProxy .;
options='';
options='-DOPENDHT_SANITIZE=On ';
if [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyserver"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.push"* ]]; then
options+='-DOPENDHT_PROXY_SERVER=ON ';
else
+39 -25
View File
@@ -1,7 +1,7 @@
cmake_minimum_required (VERSION 3.1)
project (opendht)
set (opendht_VERSION_MAJOR 1)
set (opendht_VERSION_MINOR 7.1)
set (opendht_VERSION_MINOR 8.0)
set (opendht_VERSION ${opendht_VERSION_MAJOR}.${opendht_VERSION_MINOR})
set (PACKAGE_VERSION ${opendht_VERSION})
set (VERSION "${opendht_VERSION}")
@@ -47,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
@@ -87,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}")
@@ -182,11 +194,6 @@ if (OPENDHT_PROXY_SERVER)
list (APPEND opendht_SOURCES
src/dht_proxy_server.cpp
)
if (OPENDHT_PUSH_NOTIFICATIONS)
add_definitions(-DOPENDHT_PUSH_NOTIFICATIONS=true)
else ()
add_definitions(-DOPENDHT_PUSH_NOTIFICATIONS=false)
endif ()
else ()
add_definitions(-DENABLE_PROXY_SERVER=false)
endif ()
@@ -204,16 +211,17 @@ else ()
endif ()
if (OPENDHT_PROXY_SERVER OR OPENDHT_PROXY_CLIENT)
if (OPENDHT_PUSH_NOTIFICATIONS)
message("Using push notification")
add_definitions(-DOPENDHT_PUSH_NOTIFICATIONS=true)
else ()
add_definitions(-DOPENDHT_PUSH_NOTIFICATIONS=false)
endif ()
list (APPEND opendht_HEADERS
include/opendht/proxy.h
)
list (APPEND opendht_SOURCES
src/base64.h
src/base64.cpp
)
endif ()
if(OPENDHT_ARGON2)
# make sure argon2 submodule is up to date and initialized
message("Initializing Argon2 submodule")
@@ -238,16 +246,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 ()
@@ -259,16 +265,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 ()
@@ -304,13 +307,24 @@ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/opendhtConfigVersion.cmake DESTINATIO
IF(OPENDHT_TESTS)
FIND_PACKAGE(Cppunit REQUIRED)
# unit testing
add_executable(opendht_unit_tests
tests/tests_runner.cpp
list (APPEND test_FILES
tests/infohashtester.h
tests/infohashtester.cpp
tests/cryptotester.h
tests/cryptotester.cpp
tests/dhtrunnertester.h
tests/dhtrunnertester.cpp
)
if (OPENDHT_PROXY_SERVER AND OPENDHT_PROXY_CLIENT)
list (APPEND test_FILES
tests/dhtproxytester.h
tests/dhtproxytester.cpp
)
endif()
add_executable(opendht_unit_tests
tests/tests_runner.cpp
${test_FILES}
)
if (OPENDHT_SHARED)
TARGET_LINK_LIBRARIES(opendht_unit_tests opendht)
else ()
+4 -4
View File
@@ -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>
+2
View File
@@ -16,7 +16,9 @@ if ENABLE_TESTS
SUBDIRS += tests
endif
if HAVE_DOXYGEN
SUBDIRS += doc
endif
ACLOCAL_AMFLAGS = -I m4
+15 -16
View File
@@ -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()
+62 -55
View File
@@ -1,7 +1,7 @@
dnl define macros
m4_define([opendht_major_version], 1)
m4_define([opendht_minor_version], 7)
m4_define([opendht_patch_version], 1)
m4_define([opendht_minor_version], 8)
m4_define([opendht_patch_version], 0)
m4_define([opendht_version],
[opendht_major_version.opendht_minor_version.opendht_patch_version])
@@ -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,61 +177,26 @@ 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=true"],
[CPPFLAGS+=" -DOPENDHT_PROXY_SERVER=false"])
AM_COND_IF([ENABLE_PROXY_SERVER], [
AC_CHECK_LIB(restbed, exit,, AC_MSG_ERROR([Missing restbed files]))
PKG_CHECK_MODULES([Jsoncpp], [jsoncpp >= 1.7.2])
CPPFLAGS+=" -DOPENDHT_PROXY_SERVER=true -ljsoncpp -lrestbed"
], [
CPPFLAGS+=" -DOPENDHT_PROXY_SERVER=false"
AM_COND_IF(ENABLE_PROXY_CLIENT,
[CPPFLAGS+=" -DOPENDHT_PROXY_CLIENT=true"],
[CPPFLAGS+=" -DOPENDHT_PROXY_CLIENT=false"])
AM_COND_IF(ENABLE_PUSH_NOTIFICATIONS,
[CPPFLAGS+=" -DOPENDHT_PUSH_NOTIFICATIONS=true"],
[CPPFLAGS+=" -DOPENDHT_PUSH_NOTIFICATIONS=false"])
AM_COND_IF(ENABLE_PROXY_SERVER_IDENTITY,
[CPPFLAGS+=" -DOPENDHT_PROXY_SERVER_IDENTITY=true"],
[CPPFLAGS+=" -DOPENDHT_PROXY_SERVER_IDENTITY=false"])
AM_COND_IF([HAVE_DOXYGEN], [
AC_CONFIG_FILES([doc/Doxyfile doc/Makefile])
])
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_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])
])
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, ".")
@@ -202,6 +209,6 @@ AM_COND_IF([USE_CYTHON], [
AC_CONFIG_FILES([Makefile
src/Makefile
tools/Makefile
tests/Makefile
tests/Makefile
opendht.pc])
AC_OUTPUT
+26 -4
View File
@@ -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)
};
/**
+27 -3
View File
@@ -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;
@@ -133,6 +141,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;
@@ -164,8 +173,6 @@ private:
PrivateKey(const PrivateKey&) = delete;
PrivateKey& operator=(const PrivateKey&) = delete;
Blob decryptBloc(const uint8_t* src, size_t src_size) const;
//friend dht::crypto::Identity dht::crypto::generateIdentity(const std::string&, dht::crypto::Identity, unsigned key_length);
};
@@ -368,6 +375,7 @@ struct OPENDHT_PUBLIC Certificate {
/** Same as getPublicKey().getId() */
InfoHash getId() const;
/** Same as getPublicKey().getLongId() */
PkId getLongId() const;
/** Read certificate Common Name (CN) */
@@ -404,8 +412,20 @@ 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>);
@@ -508,9 +528,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;
}
+4 -1
View File
@@ -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>
+4 -2
View File
@@ -409,7 +409,9 @@ 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);
@@ -464,7 +466,7 @@ 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, ValueCallback cb, Value::Filter f = Value::AllFilter(), const Sp<Query>& q = {});
+2 -2
View File
@@ -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;
@@ -216,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;
}
+9 -3
View File
@@ -269,6 +269,7 @@ private:
* Get informations from the proxy node
* @return the JSON returned by the proxy
*/
struct InfoState;
void getProxyInfos();
void onProxyInfos(const Json::Value& val, sa_family_t family);
SockAddr parsePublicAddress(const Json::Value& val);
@@ -278,6 +279,10 @@ private:
size_t doListen(const InfoHash& key, ValueCallback, Value::Filter);
bool doCancelListen(const InfoHash& key, size_t token);
struct ListenState;
void sendListen(const std::shared_ptr<restbed::Request>& request, const ValueCallback&, const Value::Filter& filter, const Sp<ListenState>& state);
void sendSubscribe(const std::shared_ptr<restbed::Request>& request, const Sp<proxy::ListenToken>&, const Sp<ListenState>& state);
void doPut(const InfoHash&, Sp<Value>, DoneCallback, time_point created, bool permanent);
/**
@@ -296,7 +301,7 @@ private:
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_ {};
@@ -336,8 +341,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;
/**
@@ -369,7 +375,7 @@ private:
void getPushRequest(Json::Value&) const;
#endif // OPENDHT_PUSH_NOTIFICATIONS
bool isDestroying_ {false};
std::atomic_bool isDestroying_ {false};
};
}
+50 -4
View File
@@ -21,18 +21,23 @@
#pragma once
#include "callbacks.h"
#include "def.h"
#include "sockaddr.h"
#include "infohash.h"
#include "scheduler.h"
#include "value.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;
}
@@ -72,15 +77,44 @@ public:
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 getStats() const;
ServerStats stats() const { return stats_; }
void updateStats() const;
std::shared_ptr<DhtRunner> getNode() const { return dht_; }
@@ -99,6 +133,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: .*}"
@@ -237,6 +279,8 @@ private:
std::thread schedulerThread_;
Sp<Scheduler::Job> printStatsJob_;
mutable std::mutex statsMutex_;
mutable NodeInfo nodeInfo_ {};
// Handle client quit for listen.
// NOTE: can be simplified when we will supports restbed 5.0
@@ -259,6 +303,8 @@ private:
const std::string pushServer_;
mutable ServerStats stats_;
#if OPENDHT_PUSH_NOTIFICATIONS
struct Listener;
struct PushListener;
+42 -10
View File
@@ -158,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 = {})
{
@@ -174,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);
@@ -248,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;
/**
@@ -293,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;
@@ -380,15 +412,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
@@ -423,6 +447,7 @@ private:
*/
void tryBootstrapContinuously();
void stopNetwork();
void startNetwork(const SockAddr sin4, const SockAddr sin6);
time_point loop_();
@@ -464,7 +489,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};
@@ -487,6 +518,7 @@ private:
status6 {NodeStatus::Disconnected};
StatusCallback statusCb {nullptr};
int stop_writefd {-1};
int s4 {-1}, s6 {-1};
SockAddr bound4 {};
SockAddr bound6 {};
+20 -5
View File
@@ -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
}
+5 -1
View File
@@ -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; }
+2 -2
View File
@@ -22,8 +22,8 @@
namespace dht {
namespace proxy {
constexpr const std::chrono::minutes OP_TIMEOUT {1 * 10}; // one hour
constexpr const std::chrono::minutes OP_MARGIN {5}; // 5 minutes
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 -1
View File
@@ -25,7 +25,7 @@
namespace dht {
namespace crypto {
#ifndef WIN32_NATIVE
#ifndef _MSC_VER
#ifdef _WIN32
/**
+55 -56
View File
@@ -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,14 +89,14 @@ 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));
}
@@ -148,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() 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);
}
@@ -248,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);
}
@@ -257,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);
}
@@ -265,32 +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, ValueCallback, Value::Filter={}, Where={});
size_t listen(const InfoHash& key, GetCallback cb, Value::Filter = {}, Where w = {});
size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where 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();
}
@@ -298,7 +299,7 @@ public:
forward_all_ = forward;
}
void setPushNotificationToken(const std::string& token = "") {
void setPushNotificationToken(const std::string& token = "") override {
dht_->setPushNotificationToken(token);
}
@@ -306,22 +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);
}
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);
}
+2 -2
View File
@@ -67,7 +67,7 @@ public:
*/
SockAddr(const sockaddr* sa, socklen_t length) {
if (length > sizeof(sockaddr_storage))
throw std::runtime_error("Socket address length is too large");
throw std::invalid_argument("Socket address length is too large");
set(sa, length);
}
SockAddr(const sockaddr* sa) {
@@ -78,7 +78,7 @@ public:
else if(sa->sa_family == AF_INET6)
len = sizeof(sockaddr_in6);
else
throw std::runtime_error("Unknown address family");
throw std::invalid_argument("Unknown address family");
}
set(sa, len);
}
+20 -8
View File
@@ -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 {
@@ -122,6 +122,8 @@ private:
std::map<ValueType::Id, ValueType> types {};
};
struct CryptoValueCache;
/**
* A "value" is data potentially stored on the Dht, with some metadata.
*
@@ -372,13 +374,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)
@@ -451,7 +453,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:
@@ -461,7 +463,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;
@@ -575,7 +577,7 @@ struct OPENDHT_PUBLIC Value
std::string user_type {};
/**
* Sequence number to avoid replay attacks
* Sequence number
*/
uint16_t seq {0};
@@ -588,6 +590,16 @@ struct OPENDHT_PUBLIC Value
* Hold encrypted version of the data.
*/
Blob cypher {};
bool checkSignature();
Sp<Value> decrypt(const crypto::PrivateKey& key);
private:
/* Cache for crypto ops */
bool signatureChecked {false};
bool signatureValid {false};
bool decrypted {false};
Sp<Value> decryptedValue {};
};
using ValuesExport = std::pair<InfoHash, Blob>;
@@ -996,7 +1008,7 @@ unpackVector(const std::vector<std::shared_ptr<Value>>& vals) {
return ret;
}
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
#ifdef OPENDHT_JSONCPP
uint64_t unpackId(const Json::Value& json, const std::string& key);
#endif
+3 -3
View File
@@ -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 \
@@ -65,8 +65,8 @@ libopendht_la_SOURCES += dht_proxy_client.cpp
nobase_include_HEADERS += ../include/opendht/dht_proxy_client.h ../include/opendht/dht_interface.h
endif
if PROXY_CLIENT_OR_SERVER
libopendht_la_SOURCES += base64.h base64.cpp
if PROXY_CLIENT_OR_SERVER
nobase_include_HEADERS += ../include/opendht/proxy.h
endif
+30 -6
View File
@@ -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
}
+196 -147
View File
@@ -261,11 +261,11 @@ void Dht::paginate(std::weak_ptr<Search> ws, Sp<Query> query, SearchNode* n) {
auto select_q = std::make_shared<Query>(Select {}.field(Value::Field::Id), query ? query->where : Where {});
auto onSelectDone = [this,ws,query](const net::Request& status,
net::RequestAnswer&& answer) mutable {
// retreive search
// Retrieve search
auto sr = ws.lock();
if (not sr) return;
const auto& id = sr->id;
// retreive search node
// Retrieve search node
auto sn = sr->getNode(status.node);
if (not sn) return;
// backward compatibility
@@ -319,11 +319,12 @@ Dht::searchSendGetValues(Sp<Search> sr, SearchNode* pn, bool update)
std::weak_ptr<Search> ws = sr;
auto cb = sr->callbacks.begin();
static const auto ANY_QUERY = std::make_shared<Query>(Select {}, Where {}, true);
do { /* for all requests to send */
SearchNode* n = nullptr;
auto query = not sr->callbacks.empty() ? cb->second.query : std::make_shared<Query>(Select {}, Where {}, true);
const time_point up = not sr->callbacks.empty() and update
? sr->getLastGetTime(query)
auto& query = sr->callbacks.empty() ? ANY_QUERY : cb->second.query;
const time_point up = (not sr->callbacks.empty() and update)
? sr->getLastGetTime(*query)
: time_point::min();
if (pn and pn->canGet(now, up, query)) {
@@ -380,103 +381,135 @@ void Dht::searchSendAnnounceValue(const Sp<Search>& sr) {
if (sr->announce.empty())
return;
unsigned i = 0;
auto probe_query = std::make_shared<Query>(Select {}.field(Value::Field::Id).field(Value::Field::SeqNum));
std::weak_ptr<Search> ws = sr;
auto onDone = [this,ws](const net::Request& req, net::RequestAnswer&& answer)
{ /* when put done */
if (auto sr = ws.lock()) {
onAnnounceDone(req.node, answer, sr);
searchStep(sr);
}
};
auto onExpired = [this,ws](const net::Request&, bool over)
{ /* when put expired */
if (over)
if (auto sr = ws.lock())
scheduler.edit(sr->nextSearchStep, scheduler.time());
};
auto onSelectDone =
[this,ws,onDone,onExpired](const net::Request& req, net::RequestAnswer&& answer) mutable
{ /* on probing done */
auto sr = ws.lock();
if (not sr) return;
const auto& now = scheduler.time();
sr->insertNode(req.node, scheduler.time(), answer.ntoken);
auto sn = sr->getNode(req.node);
if (not sn) return;
if (not sn->isSynced(now)) {
/* Search is now unsynced. Let's call searchStep to sync again. */
scheduler.edit(sr->nextSearchStep, now);
return;
}
for (auto& a : sr->announce) {
if (sn->getAnnounceTime(a.value->id) > now)
continue;
bool hasValue {false};
uint16_t seq_no = 0;
try {
const auto& f = std::find_if(answer.fields.cbegin(), answer.fields.cend(),
[&a](const Sp<FieldValueIndex>& i){
return i->index.at(Value::Field::Id).getInt() == a.value->id;
});
if (f != answer.fields.cend() and *f) {
hasValue = true;
seq_no = static_cast<uint16_t>((*f)->index.at(Value::Field::SeqNum).getInt());
}
} catch (std::out_of_range&) { }
auto next_refresh_time = now + getType(a.value->type).expiration;
/* only put the value if the node doesn't already have it */
if (not hasValue or seq_no < a.value->seq) {
DHT_LOG.w(sr->id, sn->node->id, "[search %s] [node %s] sending 'put' (vid: %d)",
sr->id.toString().c_str(), sn->node->toString().c_str(), a.value->id);
sn->acked[a.value->id] = std::make_pair(network_engine.sendAnnounceValue(sn->node,
sr->id,
a.value,
a.permanent ? time_point::max() : a.created,
sn->token,
onDone,
onExpired), next_refresh_time);
} else if (hasValue and a.permanent) {
DHT_LOG.w(sr->id, sn->node->id, "[search %s] [node %s] sending 'refresh' (vid: %d)",
sr->id.toString().c_str(), sn->node->toString().c_str(), a.value->id);
sn->acked[a.value->id] = std::make_pair(network_engine.sendRefreshValue(sn->node,
sr->id,
a.value->id,
sn->token,
onDone,
onExpired), next_refresh_time);
} else {
DHT_LOG.w(sr->id, sn->node->id, "[search %s] [node %s] already has value (vid: %d). Aborting.",
sr->id.toString().c_str(), sn->node->toString().c_str(), a.value->id);
auto ack_req = std::make_shared<net::Request>(net::Request::State::COMPLETED);
ack_req->reply_time = now;
sn->acked[a.value->id] = std::make_pair(std::move(ack_req), next_refresh_time);
/* step to clear announces */
scheduler.edit(sr->nextSearchStep, now);
}
if (a.permanent) {
scheduler.add(next_refresh_time - REANNOUNCE_MARGIN, [this,ws] {
if (auto sr = ws.lock()) {
searchStep(sr);
}
});
}
}
};
Sp<Query> probe_query {};
const auto& now = scheduler.time();
for (auto& n : sr->nodes) {
if (not n.isSynced(now))
continue;
if (std::find_if(sr->announce.cbegin(), sr->announce.cend(),
[&now,&n](const Announce& a) {
return n.getAnnounceTime(a.value->id) <= now;
}) == sr->announce.cend())
const auto& gs = n.probe_query ? n.getStatus.find(n.probe_query) : n.getStatus.cend();
if (gs != n.getStatus.cend() and gs->second and gs->second->pending()) {
continue;
}
auto onDone = [this,ws](const net::Request& req, net::RequestAnswer&& answer)
{ /* when put done */
if (auto sr = ws.lock()) {
onAnnounceDone(req.node, answer, sr);
searchStep(sr);
bool sendQuery = false;
for (auto& a : sr->announce) {
if (n.getAnnounceTime(a.value->id) <= now) {
if (a.permanent) {
sendQuery = true;
} else {
DHT_LOG.w(sr->id, n.node->id, "[search %s] [node %s] sending 'put' (vid: %d)",
sr->id.toString().c_str(), n.node->toString().c_str(), a.value->id);
n.acked[a.value->id] = {
network_engine.sendAnnounceValue(n.node, sr->id, a.value, a.created, n.token, onDone, onExpired),
now + getType(a.value->type).expiration
};
}
}
};
auto onExpired = [this,ws](const net::Request&, bool over)
{ /* when put expired */
if (over)
if (auto sr = ws.lock())
scheduler.edit(sr->nextSearchStep, scheduler.time());
};
auto onSelectDone =
[this,ws,onDone,onExpired](const net::Request& req, net::RequestAnswer&& answer) mutable
{ /* on probing done */
auto sr = ws.lock();
if (not sr) return;
auto sn = sr->getNode(req.node);
if (not sn) return;
}
const auto& now = scheduler.time();
if (not sn->isSynced(now)) {
/* Search is now unsynced. Let's call searchStep to sync again. */
scheduler.edit(sr->nextSearchStep, now);
return;
}
for (auto& a : sr->announce) {
if (sn->getAnnounceTime(a.value->id) > now)
continue;
bool hasValue {false};
uint16_t seq_no = 0;
try {
const auto& f = std::find_if(answer.fields.cbegin(), answer.fields.cend(),
[&a](const Sp<FieldValueIndex>& i){
return i->index.at(Value::Field::Id).getInt() == a.value->id;
});
if (f != answer.fields.cend() and *f) {
hasValue = true;
seq_no = static_cast<uint16_t>((*f)->index.at(Value::Field::SeqNum).getInt());
}
} catch (std::out_of_range&) { }
auto next_refresh_time = now + getType(a.value->type).expiration;
/* only put the value if the node doesn't already have it */
if (not hasValue or seq_no < a.value->seq) {
DHT_LOG.w(sr->id, sn->node->id, "[search %s] [node %s] sending 'put' (vid: %d)",
sr->id.toString().c_str(), sn->node->toString().c_str(), a.value->id);
sn->acked[a.value->id] = std::make_pair(network_engine.sendAnnounceValue(sn->node,
sr->id,
a.value,
a.permanent ? time_point::max() : a.created,
sn->token,
onDone,
onExpired), next_refresh_time);
} else if (hasValue and a.permanent) {
DHT_LOG.w(sr->id, sn->node->id, "[search %s] [node %s] sending 'refresh' (vid: %d)",
sr->id.toString().c_str(), sn->node->toString().c_str(), a.value->id);
sn->acked[a.value->id] = std::make_pair(network_engine.sendRefreshValue(sn->node,
sr->id,
a.value->id,
sn->token,
onDone,
onExpired), next_refresh_time);
} else {
DHT_LOG.w(sr->id, sn->node->id, "[search %s] [node %s] already has value (vid: %d). Aborting.",
sr->id.toString().c_str(), sn->node->toString().c_str(), a.value->id);
auto ack_req = std::make_shared<net::Request>(net::Request::State::COMPLETED);
ack_req->reply_time = now;
sn->acked[a.value->id] = std::make_pair(std::move(ack_req), next_refresh_time);
/* step to clear announces */
scheduler.edit(sr->nextSearchStep, now);
}
}
};
DHT_LOG.w(sr->id, n.node->id, "[search %s] [node %s] sending %s",
sr->id.toString().c_str(), n.node->toString().c_str(), probe_query->toString().c_str());
n.probe_query = probe_query;
n.getStatus[probe_query] = network_engine.sendGetValues(n.node,
sr->id,
*probe_query,
-1,
onSelectDone,
std::bind(&Dht::searchNodeGetExpired, this, _1, _2, ws, probe_query));
if (sendQuery) {
if (not probe_query)
probe_query = std::make_shared<Query>(Select {}.field(Value::Field::Id).field(Value::Field::SeqNum));
DHT_LOG.w(sr->id, n.node->id, "[search %s] [node %s] sending %s",
sr->id.toString().c_str(), n.node->toString().c_str(), probe_query->toString().c_str());
n.probe_query = probe_query;
n.getStatus[probe_query] = network_engine.sendGetValues(n.node,
sr->id,
*probe_query,
-1,
onSelectDone,
std::bind(&Dht::searchNodeGetExpired, this, _1, _2, ws, probe_query));
}
if (not n.candidate and ++i == TARGET_NODES)
break;
}
@@ -516,7 +549,7 @@ Dht::searchSynchedNodeListen(const Sp<Search>& sr, SearchNode& n)
});
}
auto prev_req = r != n.listenStatus.end() ? r->second.req : nullptr;
r->second.req = network_engine.sendListen(n.node, sr->id, *query, n.token, prev_req,
auto new_req = network_engine.sendListen(n.node, sr->id, *query, n.token, prev_req,
[this,ws,query](const net::Request& req, net::RequestAnswer&& answer) mutable
{ /* on done */
if (auto sr = ws.lock()) {
@@ -539,12 +572,18 @@ Dht::searchSynchedNodeListen(const Sp<Search>& sr, SearchNode& n)
{ /* on new values */
if (auto sr = ws.lock()) {
scheduler.edit(sr->nextSearchStep, scheduler.time());
sr->insertNode(node, scheduler.time(), answer.ntoken);
if (auto sn = sr->getNode(node)) {
sn->onValues(query, std::move(answer), types, scheduler);
}
}
}
);
// Here the request may have failed and the CachedListenStatus removed
r = n.listenStatus.find(query);
if (r != n.listenStatus.end()) {
r->second.req = new_req;
}
}
}
@@ -596,7 +635,7 @@ Dht::searchStep(Sp<Search> sr)
// true if this node is part of the target nodes cluter.
/*bool in = sr->id.xorCmp(myid, sr->nodes.back().node->id) < 0;
DHT_LOG_DEBUG("[search %s IPv%c] synced%s",
DHT_LOG_DBG("[search %s IPv%c] synced%s",
sr->id.toString().c_str(), sr->af == AF_INET ? '4' : '6', in ? ", in" : "");*/
if (not sr->listeners.empty()) {
@@ -626,7 +665,7 @@ Dht::searchStep(Sp<Search> sr)
i++;
}
while (sent and sr->currentlySolicitedNodeCount() < MAX_REQUESTED_SEARCH_NODES);
/*DHT_LOG_DEBUG("[search %s IPv%c] step: sent %u requests (total %u).",
/*DHT_LOG_DBG("[search %s IPv%c] step: sent %u requests (total %u).",
sr->id.toString().c_str(), sr->af == AF_INET ? '4' : '6', i, sr->currentlySolicitedNodeCount());*/
}
@@ -671,7 +710,7 @@ unsigned Dht::refill(Dht::Search& sr) {
/* Start a search. */
Sp<Dht::Search>
Dht::search(const InfoHash& id, sa_family_t af, GetCallback gcb, QueryCallback qcb, DoneCallback dcb, Value::Filter f, Query q)
Dht::search(const InfoHash& id, sa_family_t af, GetCallback gcb, QueryCallback qcb, DoneCallback dcb, Value::Filter f, const Sp<Query>& q)
{
if (!isRunning(af)) {
DHT_LOG.e(id, "[search %s IPv%c] unsupported protocol", id.toString().c_str(), (af == AF_INET) ? '4' : '6');
@@ -713,26 +752,14 @@ Dht::search(const InfoHash& id, sa_family_t af, GetCallback gcb, QueryCallback q
sr->expired = false;
sr->nodes.clear();
sr->nodes.reserve(SEARCH_NODES+1);
sr->nextSearchStep = scheduler.add(time_point::max(), std::bind(&Dht::searchStep, this, sr));
DHT_LOG.w(id, "[search %s IPv%c] new search", id.toString().c_str(), (af == AF_INET) ? '4' : '6');
if (search_id == 0)
search_id++;
}
if (gcb or qcb) {
auto now = scheduler.time();
sr->callbacks.insert(std::make_pair<time_point, Get>(
std::move(now),
Get { scheduler.time(), f, std::make_shared<Query>(q),
qcb ? qcb : QueryCallback {}, gcb ? gcb : GetCallback {}, dcb
}
));
}
sr->get(f, q, qcb, gcb, dcb, scheduler);
refill(*sr);
if (sr->nextSearchStep)
scheduler.edit(sr->nextSearchStep, scheduler.time());
else
sr->nextSearchStep = scheduler.add(scheduler.time(), std::bind(&Dht::searchStep, this, sr));
return sr;
}
@@ -745,15 +772,6 @@ Dht::announce(const InfoHash& id,
time_point created,
bool permanent)
{
const auto& now = scheduler.time();
if (!value) {
if (callback)
callback(false, {});
return;
}
created = std::min(now, created);
storageStore(id, value, created);
auto& srs = searches(af);
auto srp = srs.find(id);
auto sr = srp == srs.end() ? search(id, af) : srp->second;
@@ -905,13 +923,20 @@ struct GetStatus : public OpStatus {
void
Dht::put(const InfoHash& id, Sp<Value> val, DoneCallback callback, time_point created, bool permanent)
{
scheduler.syncTime();
if (not val) {
if (callback)
callback(false, {});
return;
}
if (val->id == Value::INVALID_ID) {
crypto::random_device rdev;
std::uniform_int_distribution<Value::Id> rand_id {};
val->id = rand_id(rdev);
}
scheduler.syncTime();
const auto& now = scheduler.time();
created = std::min(now, created);
storageStore(id, val, created, {}, permanent);
DHT_LOG.d(id, "put: adding %s -> %s", id.toString().c_str(), val->toString().c_str());
@@ -974,10 +999,10 @@ Dht::get(const InfoHash& id, GetCallback getcb, DoneCallback donecb, Value::Filt
{
scheduler.syncTime();
Query q {{}, where};
auto q = std::make_shared<Query>(Select {}, std::move(where));
auto op = std::make_shared<GetStatus<Value>>();
auto f = filter.chain(q->where.getFilter());
auto f = filter.chain(q.where.getFilter());
auto add_values = [op,f](const std::vector<Sp<Value>>& values) {
std::vector<Sp<Value>> newvals {};
for (const auto& v : values) {
@@ -1043,16 +1068,17 @@ void Dht::query(const InfoHash& id, QueryCallback cb, DoneCallback done_cb, Quer
/* Try to answer this search locally. */
qcb(local_fields);
auto sq = std::make_shared<Query>(std::move(q));
Dht::search(id, AF_INET, {}, qcb, [=](bool ok, const std::vector<Sp<Node>>& nodes) {
//DHT_LOG_WARN("DHT done IPv4");
op->status4 = {true, ok};
doneCallbackWrapper(done_cb, nodes, *op);
}, f, q);
}, f, sq);
Dht::search(id, AF_INET6, {}, qcb, [=](bool ok, const std::vector<Sp<Node>>& nodes) {
//DHT_LOG_WARN("DHT done IPv6");
op->status6 = {true, ok};
doneCallbackWrapper(done_cb, nodes, *op);
}, f, q);
}, f, sq);
}
std::vector<Sp<Value>>
@@ -1115,6 +1141,8 @@ bool
Dht::cancelPut(const InfoHash& id, const Value::Id& vid)
{
bool canceled {false};
if (storageErase(id, vid))
canceled = true;
auto sr_cancel_put = [&](std::map<InfoHash, Sp<Search>> srs) {
auto srp = srs.find(id);
if (srp == srs.end())
@@ -1183,11 +1211,11 @@ Dht::storageChanged(const InfoHash& id, Storage& st, ValueStorage& v, bool newVa
}
bool
Dht::storageStore(const InfoHash& id, const Sp<Value>& value, time_point created, const SockAddr& sa)
Dht::storageStore(const InfoHash& id, const Sp<Value>& value, time_point created, const SockAddr& sa, bool permanent)
{
const auto& now = scheduler.time();
created = std::min(created, now);
auto expiration = created + getType(value->type).expiration;
auto expiration = permanent ? time_point::max() : created + getType(value->type).expiration;
if (expiration < now)
return false;
@@ -1209,7 +1237,9 @@ Dht::storageStore(const InfoHash& id, const Sp<Value>& value, time_point created
if (auto vs = store.first) {
total_store_size += store.second.size_diff;
total_values += store.second.values_diff;
scheduler.add(expiration, std::bind(&Dht::expireStorage, this, id));
if (not permanent) {
scheduler.add(expiration, std::bind(&Dht::expireStorage, this, id));
}
if (total_store_size > max_store_size) {
expireStore();
}
@@ -1219,6 +1249,18 @@ Dht::storageStore(const InfoHash& id, const Sp<Value>& value, time_point created
return std::get<0>(store);
}
bool
Dht::storageErase(const InfoHash& id, Value::Id vid)
{
auto st = store.find(id);
if (st == store.end())
return false;
auto ret = st->second.remove(id, vid);
total_store_size += ret.size_diff;
total_values += ret.values_diff;
return ret.values_diff;
}
void
Dht::storageAddListener(const InfoHash& id, const Sp<Node>& node, size_t socket_id, Query&& query)
{
@@ -1535,10 +1577,7 @@ Dht::dumpSearch(const Search& sr, std::ostream& out) const
if (ack == n.acked.end() or not ack->second.first) {
out << ' ';
} else {
if (ack->second.first->completed())
out << 'a';
else if (ack->second.first->pending())
out << 'f';
out << ack->second.first->getStateChar();
}
}
out << "] ";
@@ -1580,11 +1619,6 @@ Dht::getStorageLog() const
std::stringstream out;
for (const auto& s : store)
out << printStorageLog(s);
out << "Total " << store.size() << " storages, " << total_values << " values (";
if (total_store_size < 1024)
out << total_store_size << " bytes)";
else
out << (total_store_size/1024) << " KB)";
out << std::endl << std::endl;
std::multimap<size_t, const SockAddr*> q_map;
for (const auto& ip : store_quota)
@@ -1593,6 +1627,12 @@ Dht::getStorageLog() const
for (auto ip = q_map.rbegin(); ip != q_map.rend(); ++ip)
out << "IP " << ip->second->toString() << " uses " << ip->first << " bytes" << std::endl;
out << std::endl;
out << "Total " << store.size() << " storages, " << total_values << " values (";
if (total_store_size < 1024)
out << total_store_size << " bytes)";
else
out << (total_store_size/1024) << " / " << (max_store_size/1024) << " KB)";
out << std::endl;
return out.str();
}
@@ -1736,7 +1776,7 @@ Dht::Dht(const int& s, const int& s6, Config config)
bool
Dht::neighbourhoodMaintenance(RoutingTable& list)
{
//DHT_LOG_DEBUG("neighbourhoodMaintenance");
//DHT_LOG_DBG("neighbourhoodMaintenance");
auto b = list.findBucket(myid);
if (b == list.end())
return false;
@@ -2304,7 +2344,8 @@ Dht::onAnnounce(Sp<Node> n,
Sp<Value> vc = v;
if (lv) {
if (*lv == *vc) {
DHT_LOG.d(hash, node.id, "[store %s] nothing to do for %s", hash.toString().c_str(), lv->toString().c_str());
storageRefresh(hash, v->id);
DHT_LOG.d(hash, node.id, "[store %s] [node %s] refreshed value %s", hash.toString().c_str(), node.toString().c_str(), std::to_string(v->id).c_str());
} else {
const auto& type = getType(lv->type);
if (type.editPolicy(hash, lv, vc, node.id, node.getAddr())) {
@@ -2336,14 +2377,11 @@ Dht::onRefresh(Sp<Node> node, const InfoHash& hash, const Blob& token, const Val
{
using namespace net;
const auto& now = scheduler.time();
if (not tokenMatch(token, node->getAddr())) {
DHT_LOG.w(hash, node->id, "[node %s] incorrect token %s for 'put'", node->toString().c_str(), hash.toString().c_str());
throw DhtProtocolException {DhtProtocolException::UNAUTHORIZED, DhtProtocolException::PUT_WRONG_TOKEN};
}
auto s = store.find(hash);
if (s != store.end() and s->second.refresh(now, vid)) {
if (storageRefresh(hash, vid)) {
DHT_LOG.d(hash, node->id, "[store %s] [node %s] refreshed value %s", hash.toString().c_str(), node->toString().c_str(), std::to_string(vid).c_str());
} else {
DHT_LOG.d(hash, node->id, "[store %s] [node %s] got refresh for unknown value",
@@ -2353,15 +2391,26 @@ Dht::onRefresh(Sp<Node> node, const InfoHash& hash, const Blob& token, const Val
return {};
}
bool
Dht::storageRefresh(const InfoHash& id, Value::Id vid)
{
const auto& now = scheduler.time();
auto s = store.find(id);
if (s != store.end()) {
auto expiration = s->second.refresh(now, vid, types);
if (expiration != time_point::max())
scheduler.add(expiration, std::bind(&Dht::expireStorage, this, id));
return true;
}
return false;
}
void
Dht::onAnnounceDone(const Sp<Node>& node, net::RequestAnswer& answer, Sp<Search>& sr)
{
DHT_LOG.d(sr->id, node->id, "[search %s] [node %s] got reply to put!",
sr->id.toString().c_str(), node->toString().c_str());
searchSendGetValues(sr);
/* if (auto sn = sr->getNode(req->node)) { */
/* sn->setRefreshTime(answer.vid, now + answer) */
/* } */
sr->checkAnnounced(answer.vid);
}
+240 -218
View File
@@ -33,12 +33,18 @@
namespace dht {
struct DhtProxyClient::InfoState {
std::atomic_uint ipv4 {0}, ipv6 {0};
std::atomic_bool cancel {false};
};
struct DhtProxyClient::ListenState {
std::atomic_bool ok {true};
std::atomic_bool cancel {false};
};
struct DhtProxyClient::Listener
{
struct State {
std::atomic_bool ok {true};
std::atomic_bool cancel {false};
};
ValueCache cache;
Sp<Scheduler::Job> cacheExpirationJob {};
ValueCallback cb;
@@ -46,7 +52,7 @@ struct DhtProxyClient::Listener
Sp<restbed::Request> req;
std::thread thread;
unsigned callbackId;
Sp<State> state;
Sp<ListenState> state;
Sp<proxy::ListenToken> pushNotifToken;
Sp<Scheduler::Job> refreshJob;
Listener(ValueCache&& c, Sp<Scheduler::Job>&& j, const Sp<restbed::Request>& r, Value::Filter&& f)
@@ -60,6 +66,7 @@ struct DhtProxyClient::Listener
struct PermanentPut {
Sp<Value> value;
Sp<Scheduler::Job> refreshJob;
Sp<std::atomic_bool> ok;
};
struct DhtProxyClient::ProxySearch {
@@ -99,6 +106,8 @@ DhtProxyClient::~DhtProxyClient()
isDestroying_ = true;
cancelAllOperations();
cancelAllListeners();
if (infoState_)
infoState_->cancel = true;
if (statusThread_.joinable())
statusThread_.join();
}
@@ -245,7 +254,8 @@ DhtProxyClient::get(const InfoHash& key, GetCallback cb, DoneCallback donecb, Va
o.thread = std::thread([=](){
// Try to contact the proxy and set the status to connected when done.
// will change the connectivity status
auto ok = std::make_shared<std::atomic_bool>(true);
struct GetState{ std::atomic_bool ok {true}; std::atomic_bool stop {false}; };
auto state = std::make_shared<GetState>();
restbed::Http::async(req,
[=](const std::shared_ptr<restbed::Request>& req,
const std::shared_ptr<restbed::Response>& reply) {
@@ -253,9 +263,9 @@ DhtProxyClient::get(const InfoHash& key, GetCallback cb, DoneCallback donecb, Va
if (code == 200) {
try {
while (restbed::Http::is_open(req) and not *finished) {
while (restbed::Http::is_open(req) and not *finished and not state->stop) {
restbed::Http::fetch("\n", reply);
if (*finished)
if (*finished or state->stop)
break;
std::string body;
reply->get_body(body);
@@ -270,29 +280,30 @@ DhtProxyClient::get(const InfoHash& key, GetCallback cb, DoneCallback donecb, Va
auto value = std::make_shared<Value>(json);
if ((not filter or filter(*value)) and cb) {
std::lock_guard<std::mutex> lock(lockCallbacks);
callbacks_.emplace_back([cb, value, finished]() {
if (not *finished and not cb({value}))
*finished = true;
callbacks_.emplace_back([cb, value, state]() {
if (not state->stop and not cb({value}))
state->stop = true;
});
loopSignal_();
}
} else {
*ok = false;
state->ok = false;
}
}
} catch (std::runtime_error& e) { }
} else {
*ok = false;
state->ok = false;
}
}).wait();
if (donecb) {
std::lock_guard<std::mutex> lock(lockCallbacks);
callbacks_.emplace_back([=](){
donecb(*ok, {});
donecb(state->ok, {});
state->stop = true;
});
loopSignal_();
}
if (!ok) {
if (!state->ok) {
// Connection failed, update connectivity
opFailed();
}
@@ -322,28 +333,29 @@ DhtProxyClient::put(const InfoHash& key, Sp<Value> val, DoneCallback cb, time_po
std::lock_guard<std::mutex> lock(searchLock_);
auto id = val->id;
auto search = searches_.emplace(key, ProxySearch{}).first;
auto nextRefresh = scheduler.time() + proxy::OP_TIMEOUT;
auto nextRefresh = scheduler.time() + proxy::OP_TIMEOUT - proxy::OP_MARGIN;
auto ok = std::make_shared<std::atomic_bool>(false);
search->second.puts.erase(id);
search->second.puts.emplace(id, PermanentPut {val, scheduler.add(nextRefresh, [this, key, id]{
DHT_LOG.w(key, "[search %s]: PermanentPut refresh callback vid %llu", key.to_c_str(), id);
search->second.puts.emplace(id, PermanentPut {val, scheduler.add(nextRefresh, [this, key, id, ok]{
std::lock_guard<std::mutex> lock(searchLock_);
auto s = searches_.find(key);
if (s != searches_.end()) {
auto p = s->second.puts.find(id);
if (p != s->second.puts.end()) {
doPut(key, p->second.value, [this](bool ok, const std::vector<Sp<Node>>&) {
DHT_LOG.w("PermanentPut refresh %s", (ok ? "ok" : "failed"));
doPut(key, p->second.value,
[ok](bool result, const std::vector<std::shared_ptr<dht::Node> >&){
*ok = result;
}, time_point::max(), true);
scheduler.edit(p->second.refreshJob, scheduler.time() + proxy::OP_TIMEOUT);
scheduler.edit(p->second.refreshJob, scheduler.time() + proxy::OP_TIMEOUT - proxy::OP_MARGIN);
}
}
})});
}), ok});
}
doPut(key, val, std::move(cb), created, permanent);
}
void
DhtProxyClient::doPut(const InfoHash& key, Sp<Value> val, DoneCallback cb, time_point created, bool permanent)
DhtProxyClient::doPut(const InfoHash& key, Sp<Value> val, DoneCallback cb, time_point /*created*/, bool permanent)
{
DHT_LOG.d(key, "[search %s] performing put of %s", key.to_c_str(), val->toString().c_str());
restbed::Uri uri(proxy::HTTP_PROTO + serverHost_ + "/" + key.toString());
@@ -475,9 +487,12 @@ void
DhtProxyClient::getProxyInfos()
{
DHT_LOG.d("Requesting proxy server node information");
std::lock_guard<std::mutex> l(statusLock_);
if (ongoingStatusUpdate_.test_and_set())
return;
auto infoState = std::make_shared<InfoState>();
if (infoState_)
infoState_->cancel = true;
infoState_ = infoState;
{
std::lock_guard<std::mutex> l(lockCurrentProxyInfos_);
@@ -492,11 +507,15 @@ DhtProxyClient::getProxyInfos()
// Try to contact the proxy and set the status to connected when done.
// will change the connectivity status
if (statusThread_.joinable())
statusThread_.join();
statusThread_ = std::thread([this, serverHost]{
struct SuccessCount {std::atomic_uint ipv4 {0}, ipv6 {0};};
auto successCount = std::make_shared<SuccessCount>();
if (statusThread_.joinable()) {
try {
statusThread_.detach();
statusThread_ = {};
} catch (const std::exception& e) {
DHT_LOG.e("Error detaching thread: %s", e.what());
}
}
statusThread_ = std::thread([this, serverHost, infoState]{
try {
auto hostAndService = splitPort(serverHost);
auto resolved_proxies = SockAddr::resolve(hostAndService.first, hostAndService.second);
@@ -511,8 +530,10 @@ DhtProxyClient::getProxyInfos()
}
restbed::Uri uri(proxy::HTTP_PROTO + server + "/");
auto req = std::make_shared<restbed::Request>(uri);
if (infoState->cancel)
return;
reqs.emplace_back(restbed::Http::async(req,
[this, resolved_proxy, successCount](
[this, resolved_proxy, infoState](
const std::shared_ptr<restbed::Request>&,
const std::shared_ptr<restbed::Response>& reply)
{
@@ -520,6 +541,8 @@ DhtProxyClient::getProxyInfos()
Json::Value proxyInfos;
if (code == 200) {
restbed::Http::fetch("\n", reply);
auto& state = *infoState;
if (state.cancel) return;
std::string body;
reply->get_body(body);
@@ -528,13 +551,14 @@ DhtProxyClient::getProxyInfos()
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
try {
reader->parse(body.data(), body.data() + body.size(), &proxyInfos, &err);
} catch (const std::exception& e) {
DHT_LOG.w("Error parsing proxy infos: %s", e.what());
} catch (...) {
return;
}
auto family = resolved_proxy.getFamily();
if (family == AF_INET) successCount->ipv4++;
else if (family == AF_INET6) successCount->ipv6++;
onProxyInfos(proxyInfos, family);
if (family == AF_INET) state.ipv4++;
else if (family == AF_INET6) state.ipv6++;
if (not state.cancel)
onProxyInfos(proxyInfos, family);
}
}));
}
@@ -544,22 +568,26 @@ DhtProxyClient::getProxyInfos()
} catch (const std::exception& e) {
DHT_LOG.e("Error sending proxy info request: %s", e.what());
}
if (successCount->ipv4 == 0) onProxyInfos(Json::Value{}, AF_INET);
if (successCount->ipv6 == 0) onProxyInfos(Json::Value{}, AF_INET6);
ongoingStatusUpdate_.clear();
const auto& state = *infoState;
if (state.cancel) return;
if (state.ipv4 == 0) onProxyInfos(Json::Value{}, AF_INET);
if (state.ipv6 == 0) onProxyInfos(Json::Value{}, AF_INET6);
});
}
void
DhtProxyClient::onProxyInfos(const Json::Value& proxyInfos, sa_family_t family)
{
DHT_LOG.d("Got proxy infos %s", family == AF_INET ? "IPv4" : "IPv6");
if (isDestroying_)
return;
std::lock_guard<std::mutex> l(lockCurrentProxyInfos_);
auto oldStatus = std::max(statusIpv4_, statusIpv6_);
auto& status = family == AF_INET ? statusIpv4_ : statusIpv6_;
if (not proxyInfos.isMember("node_id")) {
DHT_LOG.e("Proxy info request failed for %s", family == AF_INET ? "IPv4" : "IPv6");
status = NodeStatus::Disconnected;
} else {
DHT_LOG.d("Got proxy reply for %s", family == AF_INET ? "IPv4" : "IPv6");
try {
myid = InfoHash(proxyInfos["node_id"].asString());
stats4_ = NodeStats(proxyInfos["ipv4"]);
@@ -623,7 +651,7 @@ DhtProxyClient::listen(const InfoHash& key, ValueCallback cb, Value::Filter filt
it = searches_.emplace(key, ProxySearch{}).first;
}
auto query = std::make_shared<Query>(Select{}, where);
auto token = it->second.ops.listen(cb, query, filter, [&](Sp<Query> q, ValueCallback vcb){
auto token = it->second.ops.listen(cb, query, filter, [&](Sp<Query> /*q*/, ValueCallback vcb){
return doListen(key, vcb, filter);
});
return token;
@@ -656,14 +684,105 @@ DhtProxyClient::cancelListen(const InfoHash& key, size_t gtoken) {
return canceled;
}
void
DhtProxyClient::sendListen(const std::shared_ptr<restbed::Request>& req, const ValueCallback& cb, const Value::Filter& filter, const Sp<ListenState>& state) {
auto settings = std::make_shared<restbed::Settings>();
std::chrono::milliseconds timeout(std::numeric_limits<int>::max());
settings->set_connection_timeout(timeout); // Avoid the client to close the socket after 5 seconds.
req->set_method("LISTEN");
restbed::Http::async(req,
[this, filter, cb, state](const std::shared_ptr<restbed::Request>& req,
const std::shared_ptr<restbed::Response>& reply) {
auto code = reply->get_status_code();
if (code == 200) {
try {
while (restbed::Http::is_open(req) and not state->cancel) {
restbed::Http::fetch("\n", reply);
if (state->cancel)
break;
std::string body;
reply->get_body(body);
reply->set_body(""); // Reset the body for the next fetch
Json::Value json;
std::string err;
Json::CharReaderBuilder rbuilder;
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
if (reader->parse(body.data(), body.data() + body.size(), &json, &err)) {
auto expired = json.get("expired", Json::Value(false)).asBool();
auto value = std::make_shared<Value>(json);
if ((not filter or filter(*value)) and cb) {
std::lock_guard<std::mutex> lock(lockCallbacks);
callbacks_.emplace_back([cb, value, state, expired]() {
if (not state->cancel and not cb({value}, expired))
state->cancel = true;
});
loopSignal_();
}
}
}
} catch (const std::exception& e) {
if (not state->cancel) {
DHT_LOG.w("Listen closed by the proxy server: %s", e.what());
state->ok = false;
}
}
} else {
state->ok = false;
}
}, settings).get();
auto& s = *state;
if (not s.ok and not s.cancel)
opFailed();
}
void
DhtProxyClient::sendSubscribe(const std::shared_ptr<restbed::Request>& req, const Sp<proxy::ListenToken>& token, const Sp<ListenState>& state) {
#if OPENDHT_PUSH_NOTIFICATIONS
req->set_method("SUBSCRIBE");
fillBodyToGetToken(req);
restbed::Http::async(req, [this,state, token](const std::shared_ptr<restbed::Request>&,
const std::shared_ptr<restbed::Response>& reply) {
auto code = reply->get_status_code();
if (code == 200) {
try {
restbed::Http::fetch("\n", reply);
std::string body;
reply->get_body(body);
std::string err;
Json::Value json;
Json::CharReaderBuilder rbuilder;
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
if (reader->parse(body.data(), body.data() + body.size(), &json, &err)) {
if (not json.isMember("token")) {
state->ok = false;
return;
}
*token = unpackId(json, "token");
}
} catch (const std::exception& e) {
if (not state->cancel) {
DHT_LOG.e("sendSubscribe: error: %s", e.what());
state->ok = false;
}
}
} else {
state->ok = false;
}
}).get();
auto& s = *state;
if (not s.ok and not s.cancel)
opFailed();
#endif
}
size_t
DhtProxyClient::doListen(const InfoHash& key, ValueCallback cb, Value::Filter filter/*, Where where*/)
{
scheduler.syncTime();
restbed::Uri uri(proxy::HTTP_PROTO + serverHost_ + "/" + key.toString());
auto req = std::make_shared<restbed::Request>(uri);
req->set_method(deviceKey_.empty() ? "LISTEN" : "SUBSCRIBE");
std::lock_guard<std::mutex> lock(searchLock_);
auto search = searches_.find(key);
if (search == searches_.end()) {
@@ -672,6 +791,7 @@ DhtProxyClient::doListen(const InfoHash& key, ValueCallback cb, Value::Filter fi
}
DHT_LOG.d(key, "[search %s] sending %s", key.to_c_str(), deviceKey_.empty() ? "listen" : "subscribe");
auto req = std::make_shared<restbed::Request>(uri);
auto token = ++listenerToken_;
auto l = search->second.listeners.find(token);
if (l == search->second.listeners.end()) {
@@ -691,9 +811,12 @@ DhtProxyClient::doListen(const InfoHash& key, ValueCallback cb, Value::Filter fi
scheduler.edit(l->second.cacheExpirationJob, next);
}), req, std::move(f)
}).first;
} else {
if (l->second.state)
l->second.state->cancel = true;
}
auto state = std::make_shared<Listener::State>();
auto state = std::make_shared<ListenState>();
l->second.state = state;
l->second.cb = [this,key,token,state](const std::vector<Sp<Value>>& values, bool expired) {
if (state->cancel)
@@ -719,82 +842,34 @@ DhtProxyClient::doListen(const InfoHash& key, ValueCallback cb, Value::Filter fi
loopSignal_();
return true;
};
auto pushNotifToken = std::make_shared<proxy::ListenToken>(0);
auto vcb = l->second.cb;
l->second.pushNotifToken = pushNotifToken;
l->second.thread = std::thread([this,req,filter,vcb,pushNotifToken,state]()
{
auto settings = std::make_shared<restbed::Settings>();
if (deviceKey_.empty()) {
std::chrono::milliseconds timeout(std::numeric_limits<int>::max());
settings->set_connection_timeout(timeout); // Avoid the client to close the socket after 5 seconds.
}
#if OPENDHT_PUSH_NOTIFICATIONS
else
fillBodyToGetToken(req);
#endif
l->second.req = req;
restbed::Http::async(req,
[this, filter, vcb, pushNotifToken, state](const std::shared_ptr<restbed::Request>& req,
const std::shared_ptr<restbed::Response>& reply) {
auto code = reply->get_status_code();
if (code == 200) {
try {
std::string err;
Json::Value json;
Json::CharReaderBuilder rbuilder;
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
if (!deviceKey_.empty()) {
restbed::Http::fetch("\n", reply);
if (state->cancel)
return;
std::string body;
reply->get_body(body);
auto* char_data = reinterpret_cast<const char*>(&body[0]);
if (reader->parse(char_data, char_data + body.size(), &json, &err)) {
*pushNotifToken = unpackId(json, "token");
} else {
state->ok = false;
}
} else {
while (restbed::Http::is_open(req) and not state->cancel) {
restbed::Http::fetch("\n", reply);
if (state->cancel)
break;
std::string body;
reply->get_body(body);
reply->set_body(""); // Reset the body for the next fetch
auto* char_data = reinterpret_cast<const char*>(&body[0]);
if (reader->parse(char_data, char_data + body.size(), &json, &err)) {
auto value = std::make_shared<Value>(json);
if ((not filter or filter(*value)) and vcb) {
std::lock_guard<std::mutex> lock(lockCallbacks);
callbacks_.emplace_back([vcb, value, state]() {
if (not state->cancel and not vcb({value}, false))
state->cancel = true;
});
loopSignal_();
}
} else {
state->ok = false;
}
}
}
} catch (std::runtime_error&) {
// NOTE: Http::close() can occurs here. Ignore this.
}
} else {
state->ok = false;
if (not deviceKey_.empty()) {
// Relaunch push listeners even if a timeout is not received (if the proxy crash for any reason)
l->second.refreshJob = scheduler.add(scheduler.time() + proxy::OP_TIMEOUT - proxy::OP_MARGIN, [this, key, token, state] {
if (state->cancel)
return;
std::lock_guard<std::mutex> lock(searchLock_);
auto s = searches_.find(key);
if (s != searches_.end()) {
auto l = s->second.listeners.find(token);
if (l != s->second.listeners.end()) {
resubscribe(key, l->second);
}
}, settings).get();
auto& s = *state;
if (not s.ok and not s.cancel) {
opFailed();
}
}
);
});
l->second.thread = std::thread([this, req,pushNotifToken, state](){
sendSubscribe(req,pushNotifToken,state);
});
} else {
l->second.thread = std::thread([this, req, vcb, filter, state]{
sendListen(req,vcb,filter,state);
});
}
return token;
}
@@ -878,81 +953,54 @@ DhtProxyClient::getConnectivityStatus()
void
DhtProxyClient::restartListeners()
{
if (isDestroying_) return;
std::lock_guard<std::mutex> lock(searchLock_);
DHT_LOG.d("Refresh permanent puts");
for (auto& search : searches_) {
for (auto& put : search.second.puts) {
if (!*put.second.ok) {
auto ok = put.second.ok;
doPut(search.first, put.second.value,
[ok](bool result, const std::vector<std::shared_ptr<dht::Node> >&){
*ok = result;
}, time_point::max(), true);
scheduler.edit(put.second.refreshJob, scheduler.time() + proxy::OP_TIMEOUT - proxy::OP_MARGIN);
}
}
}
if (not deviceKey_.empty()) {
DHT_LOG.d("resubscribe due to a connectivity change");
// Connectivity changed, refresh all subscribe
for (auto& search : searches_)
for (auto& listener : search.second.listeners)
if (!listener.second.state->ok)
resubscribe(search.first, listener.second);
return;
}
DHT_LOG.d("Restarting listeners");
std::lock_guard<std::mutex> lock(searchLock_);
for (auto& search: searches_) {
for (auto& l: search.second.listeners) {
auto& listener = l.second;
auto state = listener.state;
if (listener.thread.joinable()) {
state->cancel = true;
if (listener.req)
restbed::Http::close(listener.req);
listener.thread.join();
}
// Redo listen
state->cancel = false;
state->ok = true;
auto filter = listener.filter;
auto cb = listener.cb;
restbed::Uri uri(proxy::HTTP_PROTO + serverHost_ + "/" + search.first.toString());
auto req = std::make_shared<restbed::Request>(uri);
req->set_method("LISTEN");
listener.req = req;
listener.thread = std::thread([this, filter, cb, req, state]()
{
auto settings = std::make_shared<restbed::Settings>();
std::chrono::milliseconds timeout(std::numeric_limits<int>::max());
settings->set_connection_timeout(timeout); // Avoid the client to close the socket after 5 seconds.
auto ok = std::make_shared<std::atomic_bool>(true);
restbed::Http::async(req,
[this, filter, cb, state](const std::shared_ptr<restbed::Request>& req,
const std::shared_ptr<restbed::Response>& reply) {
auto code = reply->get_status_code();
if (code == 200) {
try {
while (restbed::Http::is_open(req) and not state->cancel) {
restbed::Http::fetch("\n", reply);
if (state->cancel)
break;
std::string body;
reply->get_body(body);
reply->set_body(""); // Reset the body for the next fetch
Json::Value json;
std::string err;
Json::CharReaderBuilder rbuilder;
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
if (reader->parse(body.data(), body.data() + body.size(), &json, &err)) {
auto value = std::make_shared<Value>(json);
if ((not filter or filter(*value)) and cb) {
std::lock_guard<std::mutex> lock(lockCallbacks);
callbacks_.emplace_back([cb, value, state]() {
if (not state->cancel and not cb({value}, false))
state->cancel = true;
});
loopSignal_();
}
}
}
} catch (std::runtime_error&) {
// NOTE: Http::close() can occurs here. Ignore this.
}
} else {
state->ok = false;
}
}, settings).get();
auto& s = *state;
if (not s.ok and not s.cancel)
opFailed();
for (auto& l: search.second.listeners) {
auto& listener = l.second;
auto state = listener.state;
if (listener.thread.joinable()) {
state->cancel = true;
if (listener.req)
restbed::Http::close(listener.req);
listener.thread.join();
}
);
}
// Redo listen
state->cancel = false;
state->ok = true;
auto filter = listener.filter;
auto cb = listener.cb;
restbed::Uri uri(proxy::HTTP_PROTO + serverHost_ + "/" + search.first.toString());
auto req = std::make_shared<restbed::Request>(uri);
req->set_method("LISTEN");
listener.req = req;
listener.thread = std::thread([this, req, cb, filter, state]() {
sendListen(req, cb, filter, state);
});
}
}
}
@@ -961,9 +1009,11 @@ DhtProxyClient::pushNotificationReceived(const std::map<std::string, std::string
{
#if OPENDHT_PUSH_NOTIFICATIONS
scheduler.syncTime();
DHT_LOG.d("Got push notification");
for (const auto& n : notification) {
DHT_LOG.d(" %s -> %s", n.first.c_str(), n.second.c_str());
{
// If a push notification is received, the proxy is up and running
std::lock_guard<std::mutex> l(lockCurrentProxyInfos_);
statusIpv4_ = NodeStatus::Connected;
statusIpv6_ = NodeStatus::Connected;
}
try {
std::lock_guard<std::mutex> lock(searchLock_);
@@ -976,7 +1026,6 @@ DhtProxyClient::pushNotificationReceived(const std::map<std::string, std::string
// Refresh put
auto vid = std::stoull(vidIt->second);
auto& put = search.puts.at(vid);
DHT_LOG.d("refresh PermanentPut: %s %llx %p %s", key.to_c_str(), vid, put.refreshJob.get(), put.refreshJob ? (put.refreshJob->do_ ? "set":"not set") : "(null job)");
scheduler.edit(put.refreshJob, scheduler.time());
loopSignal_();
} else {
@@ -1011,6 +1060,7 @@ DhtProxyClient::resubscribe(const InfoHash& key, Listener& listener)
{
#if OPENDHT_PUSH_NOTIFICATIONS
if (deviceKey_.empty()) return;
scheduler.syncTime();
DHT_LOG.d(key, "[search %s] resubscribe push listener", key.to_c_str());
// Subscribe
restbed::Uri uri(proxy::HTTP_PROTO + serverHost_ + "/" + key.toString());
@@ -1025,37 +1075,9 @@ DhtProxyClient::resubscribe(const InfoHash& key, Listener& listener)
state->ok = true;
listener.req = req;
listener.pushNotifToken = pushNotifToken;
scheduler.edit(listener.refreshJob, scheduler.time() + proxy::OP_TIMEOUT - proxy::OP_MARGIN);
listener.thread = std::thread([this, req, pushNotifToken, state]() {
fillBodyToGetToken(req);
auto settings = std::make_shared<restbed::Settings>();
restbed::Http::async(req,
[pushNotifToken, state](const std::shared_ptr<restbed::Request>&,
const std::shared_ptr<restbed::Response>& reply) {
auto code = reply->get_status_code();
if (code == 200) {
try {
restbed::Http::fetch("\n", reply);
std::string body;
reply->get_body(body);
std::string err;
Json::Value json;
Json::CharReaderBuilder rbuilder;
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
if (reader->parse(body.data(), body.data() + body.size(), &json, &err)) {
if (!json.isMember("token")) return;
*pushNotifToken = unpackId(json, "token");
}
} catch (std::runtime_error&) {
// NOTE: Http::close() can occurs here. Ignore this.
}
} else {
state->ok = false;
}
}, settings).get();
auto& s = *state;
if (not s.ok and not s.cancel)
opFailed();
sendSubscribe(req, pushNotifToken, state);
});
#endif
}
+96 -54
View File
@@ -29,6 +29,7 @@
#include <chrono>
#include <functional>
#include <limits>
#include <iostream>
using namespace std::placeholders;
@@ -69,6 +70,7 @@ DhtProxyServer::DhtProxyServer(std::shared_ptr<DhtRunner> dht, in_port_t port ,
auto resource = std::make_shared<restbed::Resource>();
resource->set_path("/");
resource->set_method_handler("GET", std::bind(&DhtProxyServer::getNodeInfo, this, _1));
resource->set_method_handler("STATS", std::bind(&DhtProxyServer::getStats, this, _1));
service_->publish(resource);
resource = std::make_shared<restbed::Resource>();
resource->set_path("/{hash: .*}");
@@ -123,12 +125,6 @@ DhtProxyServer::DhtProxyServer(std::shared_ptr<DhtRunner> dht, in_port_t port ,
while (not service_->is_up() and not stopListeners) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
printStatsJob_ = scheduler_.add(scheduler_.time() + PRINT_STATS_PERIOD, [this]{
if (service_->is_up() and not stopListeners) {
std::cout << getStats().toString() << std::endl;
scheduler_.edit(printStatsJob_, scheduler_.time() + PRINT_STATS_PERIOD);
}
});
while (service_->is_up() and not stopListeners) {
std::unique_lock<std::mutex> lock(schedulerLock_);
auto next = scheduler_.run();
@@ -138,8 +134,19 @@ DhtProxyServer::DhtProxyServer(std::shared_ptr<DhtRunner> dht, in_port_t port ,
schedulerCv_.wait_until(lock, next);
}
});
dht->forwardAllMessages(true);
printStatsJob_ = scheduler_.add(scheduler_.time() + PRINT_STATS_PERIOD, [this] {
if (stopListeners) return;
if (service_->is_up())
updateStats();
// Refresh stats cache
auto newInfo = dht_->getNodeInfo();
{
std::lock_guard<std::mutex> lck(statsMutex_);
nodeInfo_ = std::move(newInfo);
}
scheduler_.edit(printStatsJob_, scheduler_.time() + PRINT_STATS_PERIOD);
});
}
DhtProxyServer::~DhtProxyServer()
@@ -158,7 +165,7 @@ DhtProxyServer::stop()
auto listener = currentListeners_.begin();
while (listener != currentListeners_.end()) {
listener->session->close();
++ listener;
++listener;
}
}
stopListeners = true;
@@ -172,41 +179,45 @@ DhtProxyServer::stop()
server_thread.join();
}
DhtProxyServer::ServerStats
DhtProxyServer::getStats() const
void
DhtProxyServer::updateStats() const
{
ServerStats ret {};
auto now = clock::now();
auto last = lastStatsReset_.exchange(now);
auto count = requestNum_.exchange(0);
auto dt = std::chrono::duration<double>(now - last);
ret.requestRate = count / dt.count();
stats_.requestRate = count / dt.count();
#if OPENDHT_PUSH_NOTIFICATIONS
ret.pushListenersCount = pushListeners_.size();
stats_.pushListenersCount = pushListeners_.size();
#endif
ret.putCount = puts_.size();
ret.listenCount = currentListeners_.size();
return ret;
stats_.putCount = puts_.size();
stats_.listenCount = currentListeners_.size();
stats_.nodeInfo = nodeInfo_;
}
void
DhtProxyServer::getNodeInfo(const std::shared_ptr<restbed::Session>& session) const
DhtProxyServer::getNodeInfo(const Sp<restbed::Session>& session) const
{
requestNum_++;
const auto request = session->get_request();
int content_length = std::stoi(request->get_header("Content-Length", "0"));
session->fetch(content_length,
[=](const std::shared_ptr<restbed::Session> s, const restbed::Bytes& /*b*/)
[this](const Sp<restbed::Session>& s, const restbed::Bytes& /*b*/) mutable
{
try {
if (dht_) {
Json::Value result;
auto id = dht_->getId();
if (id)
result["id"] = id.toString();
result["node_id"] = dht_->getNodeId().toString();
result["ipv4"] = dht_->getNodesStats(AF_INET).toJson();
result["ipv6"] = dht_->getNodesStats(AF_INET6).toJson();
{
std::lock_guard<std::mutex> lck(statsMutex_);
if (nodeInfo_.ipv4.good_nodes == 0 && nodeInfo_.ipv6.good_nodes == 0) {
// NOTE: we want to avoid the disconnected state as much as possible
// So, if the node is disconnected, we should force the update of the cache
// and reconnect as soon as possible
// This should not happen much
nodeInfo_ = dht_->getNodeInfo();
}
result = nodeInfo_.toJson();
}
result["public_ip"] = s->get_origin(); // [ipv6:ipv4]:port or ipv4:port
Json::StreamWriterBuilder wbuilder;
wbuilder["commentStyle"] = "None";
@@ -224,14 +235,44 @@ DhtProxyServer::getNodeInfo(const std::shared_ptr<restbed::Session>& session) co
}
void
DhtProxyServer::get(const std::shared_ptr<restbed::Session>& session) const
DhtProxyServer::getStats(const Sp<restbed::Session>& session) const
{
requestNum_++;
const auto request = session->get_request();
int content_length = std::stoi(request->get_header("Content-Length", "0"));
session->fetch(content_length,
[this](const Sp<restbed::Session>& s, const restbed::Bytes& /*b*/) mutable
{
try {
if (dht_) {
#ifdef OPENDHT_JSONCPP
Json::StreamWriterBuilder wbuilder;
wbuilder["commentStyle"] = "None";
wbuilder["indentation"] = "";
auto output = Json::writeString(wbuilder, stats_.toJson()) + "\n";
s->close(restbed::OK, output);
#else
s->close(restbed::NotFound, "{\"err\":\"JSON not enabled on this instance\"}");
#endif
}
else
s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}");
} catch (...) {
s->close(restbed::INTERNAL_SERVER_ERROR, "{\"err\":\"Internal server error\"}");
}
}
);
}
void
DhtProxyServer::get(const Sp<restbed::Session>& session) const
{
requestNum_++;
const auto request = session->get_request();
int content_length = std::stoi(request->get_header("Content-Length", "0"));
auto hash = request->get_path_parameter("hash");
session->fetch(content_length,
[=](const std::shared_ptr<restbed::Session> s, const restbed::Bytes& /*b* */)
[=](const Sp<restbed::Session>& s, const restbed::Bytes& /*b* */)
{
try {
if (dht_) {
@@ -239,23 +280,21 @@ DhtProxyServer::get(const std::shared_ptr<restbed::Session>& session) const
if (!infoHash) {
infoHash = InfoHash::get(hash);
}
s->yield(restbed::OK, "", [=]( const std::shared_ptr< restbed::Session > s) {
auto cacheSession = std::weak_ptr<restbed::Session>(s);
dht_->get(infoHash, [cacheSession](std::shared_ptr<Value> value) {
auto s = cacheSession.lock();
if (!s) return false;
// Send values as soon as we get them
Json::StreamWriterBuilder wbuilder;
wbuilder["commentStyle"] = "None";
wbuilder["indentation"] = "";
auto output = Json::writeString(wbuilder, value->toJson()) + "\n";
s->yield(output, [](const std::shared_ptr<restbed::Session> /*session*/){ });
return true;
}, [s](bool /*ok* */) {
// Communication is finished
s->yield(restbed::OK, "", [=](const Sp<restbed::Session>&) {});
dht_->get(infoHash, [s](const Sp<Value>& value) {
if (s->is_closed()) return false;
// Send values as soon as we get them
Json::StreamWriterBuilder wbuilder;
wbuilder["commentStyle"] = "None";
wbuilder["indentation"] = "";
auto output = Json::writeString(wbuilder, value->toJson()) + "\n";
s->yield(output, [](const Sp<restbed::Session>& /*session*/){ });
return true;
}, [s](bool /*ok* */) {
// Communication is finished
if (not s->is_closed()) {
s->close();
});
}
});
} else {
s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}");
@@ -268,7 +307,7 @@ DhtProxyServer::get(const std::shared_ptr<restbed::Session>& session) const
}
void
DhtProxyServer::listen(const std::shared_ptr<restbed::Session>& session)
DhtProxyServer::listen(const Sp<restbed::Session>& session)
{
requestNum_++;
const auto request = session->get_request();
@@ -278,7 +317,7 @@ DhtProxyServer::listen(const std::shared_ptr<restbed::Session>& session)
if (!infoHash)
infoHash = InfoHash::get(hash);
session->fetch(content_length,
[=](const std::shared_ptr<restbed::Session> s, const restbed::Bytes& /*b* */)
[=](const Sp<restbed::Session>& s, const restbed::Bytes& /*b* */)
{
try {
if (dht_) {
@@ -296,7 +335,7 @@ DhtProxyServer::listen(const std::shared_ptr<restbed::Session>& session)
// cache the session to avoid an incrementation of the shared_ptr's counter
// else, the session->close() will not close the socket.
auto cacheSession = std::weak_ptr<restbed::Session>(s);
listener.token = dht_->listen(infoHash, [cacheSession](std::shared_ptr<Value> value) {
listener.token = dht_->listen(infoHash, [cacheSession](const std::vector<Sp<Value>>& values, bool expired) {
auto s = cacheSession.lock();
if (!s) return false;
// Send values as soon as we get them
@@ -304,8 +343,13 @@ DhtProxyServer::listen(const std::shared_ptr<restbed::Session>& session)
Json::StreamWriterBuilder wbuilder;
wbuilder["commentStyle"] = "None";
wbuilder["indentation"] = "";
auto output = Json::writeString(wbuilder, value->toJson()) + "\n";
s->yield(output, [](const std::shared_ptr<restbed::Session>){ });
for (const auto& value : values) {
auto val = value->toJson();
if (expired)
val["expired"] = true;
auto output = Json::writeString(wbuilder, val) + "\n";
s->yield(output, [](const Sp<restbed::Session>&){ });
}
}
return !s->is_closed();
});
@@ -361,7 +405,10 @@ DhtProxyServer::subscribe(const std::shared_ptr<restbed::Session>& session)
return;
}
auto pushToken = root["key"].asString();
if (pushToken.empty()) return;
if (pushToken.empty()) {
s->close(restbed::BAD_REQUEST, "{\"err\":\"No token\"}");
return;
}
auto tokenFromReq = unpackId(root, "token");
auto platform = root["platform"].asString();
auto isAndroid = platform == "android";
@@ -438,7 +485,6 @@ DhtProxyServer::subscribe(const std::shared_ptr<restbed::Session>& session)
void
DhtProxyServer::unsubscribe(const std::shared_ptr<restbed::Session>& session)
{
requestNum_++;
const auto request = session->get_request();
int content_length = std::stoi(request->get_header("Content-Length", "0"));
@@ -599,7 +645,6 @@ DhtProxyServer::put(const std::shared_ptr<restbed::Session>& session)
clientId = pVal["client_id"].asString();
platform = pVal["platform"].asString();
}
std::cout << "permanent: pushToken " << pushToken << " clientId " << clientId << " platform " << platform << std::endl;
bool isAndroid = platform == "android";
std::unique_lock<std::mutex> lock(schedulerLock_);
scheduler_.syncTime();
@@ -609,18 +654,16 @@ DhtProxyServer::put(const std::shared_ptr<restbed::Session>& session)
auto r = sPuts->second.puts.emplace(vid, PermanentPut{});
auto& pput = r.first->second;
if (r.second) {
std::cout << "New ppush entry" << std::endl;
pput.expireJob = scheduler_.add(timeout, [this, infoHash, vid]{
std::cout << "Permanent put expired: " << infoHash << " " << vid << std::endl;
cancelPut(infoHash, vid);
});
#if OPENDHT_PUSH_NOTIFICATIONS
if (not pushToken.empty()) {
std::cout << "Adding refresh job" << std::endl;
pput.expireNotifyJob = scheduler_.add(timeout - proxy::OP_MARGIN,
[this, infoHash, vid, pushToken, clientId, isAndroid]
{
std::cout << "Permanent put refresh: " << infoHash << " " << std::to_string(vid) << " " << clientId << std::endl;
std::cout << "Permanent put refresh: " << infoHash << " " << vid << std::endl;
Json::Value json;
json["timeout"] = infoHash.toString();
json["to"] = clientId;
@@ -630,7 +673,6 @@ DhtProxyServer::put(const std::shared_ptr<restbed::Session>& session)
}
#endif
} else {
std::cout << "Refreshing put jobs" << std::endl;
scheduler_.edit(pput.expireJob, timeout);
if (pput.expireNotifyJob)
scheduler_.edit(pput.expireNotifyJob, timeout - proxy::OP_MARGIN);
+141 -40
View File
@@ -37,11 +37,13 @@
#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;
@@ -163,7 +165,7 @@ DhtRunner::shutdown(ShutdownCallback cb) {
void
DhtRunner::join()
{
running_network = false;
stopNetwork();
running = false;
cv.notify_all();
bootstrap_cv.notify_all();
@@ -247,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));
dht_->setLoggers(error, warn, debug);
#if OPENDHT_PROXY_CLIENT
if (dht_via_proxy_)
dht_via_proxy_->setLoggers(std::forward<LogMethod>(error), std::forward<LogMethod>(warn), std::forward<LogMethod>(debug));
dht_via_proxy_->setLoggers(error, warn, debug);
#endif
}
@@ -301,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
{
@@ -342,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)
@@ -396,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);
}
@@ -441,8 +459,10 @@ 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) {
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);
@@ -450,12 +470,64 @@ 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("Can't open pipe");
}
#else
udpPipe(stopfds);
#endif
int stop_readfd = stopfds[0];
stop_writefd = stopfds[1];
s4 = -1;
s6 = -1;
@@ -470,19 +542,19 @@ DhtRunner::startNetwork(const SockAddr sin4, const SockAddr sin6)
#endif
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");
@@ -503,11 +575,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();
}
@@ -524,6 +600,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;
});
}
@@ -562,6 +643,7 @@ DhtRunner::listen(InfoHash hash, ValueCallback vcb, Value::Filter f, Where w)
{
std::lock_guard<std::mutex> lck(storage_mtx);
pending_ops.emplace([=](SecureDht& dht) mutable {
#if OPENDHT_PROXY_CLIENT
auto tokenbGlobal = listener_token_++;
Listener listener {};
listener.hash = hash;
@@ -569,21 +651,20 @@ DhtRunner::listen(InfoHash hash, ValueCallback vcb, Value::Filter f, Where w)
listener.w = std::move(w);
listener.gcb = [hash,vcb,tokenbGlobal,this](const std::vector<Sp<Value>>& vals, bool expired){
if (not vcb(vals, expired)) {
#if OPENDHT_PROXY_CLIENT
cancelListen(hash, tokenbGlobal);
#endif
return false;
}
return true;
};
#if OPENDHT_PROXY_CLIENT
if (use_proxy)
listener.tokenProxyDht = dht.listen(hash, listener.gcb, listener.f, listener.w);
else
#endif
listener.tokenClassicDht = dht.listen(hash, listener.gcb, listener.f, listener.w);
listeners_.emplace(tokenbGlobal, std::move(listener));
ret_token->set_value(tokenbGlobal);
#else
ret_token->set_value(dht.listen(hash, vcb, f, w));
#endif
});
}
cv.notify_all();
@@ -599,20 +680,24 @@ DhtRunner::listen(const std::string& key, GetCallback vcb, Value::Filter f, Wher
void
DhtRunner::cancelListen(InfoHash h, size_t token)
{
std::lock_guard<std::mutex> lck(storage_mtx);
pending_ops.emplace([=](SecureDht& dht) {
{
std::lock_guard<std::mutex> lck(storage_mtx);
#if OPENDHT_PROXY_CLIENT
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);
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
dht.cancelListen(h, token);
pending_ops.emplace([=](SecureDht& dht) {
dht.cancelListen(h, token);
});
#endif // OPENDHT_PROXY_CLIENT
});
}
cv.notify_all();
}
@@ -621,20 +706,21 @@ DhtRunner::cancelListen(InfoHash h, std::shared_future<size_t> ftoken)
{
{
std::lock_guard<std::mutex> lck(storage_mtx);
pending_ops.emplace([=](SecureDht& dht) {
auto token = ftoken.get();
#if OPENDHT_PROXY_CLIENT
auto it = listeners_.find(token);
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
dht.cancelListen(h, token);
#endif // OPENDHT_PROXY_CLIENT
});
#else
pending_ops.emplace([=](SecureDht& dht) {
dht.cancelListen(h, ftoken.get());
});
#endif // OPENDHT_PROXY_CLIENT
}
cv.notify_all();
}
@@ -851,7 +937,7 @@ DhtRunner::connectivityChanged()
{
{
std::lock_guard<std::mutex> lck(storage_mtx);
pending_ops.emplace([=](SecureDht& dht) {
pending_ops_prio.emplace([=](SecureDht& dht) {
dht.connectivityChanged();
});
}
@@ -872,8 +958,8 @@ DhtRunner::findCertificate(InfoHash hash, std::function<void(const std::shared_p
void
DhtRunner::resetDht()
{
listeners_.clear();
#if OPENDHT_PROXY_CLIENT
listeners_.clear();
dht_via_proxy_.reset();
#endif // OPENDHT_PROXY_CLIENT
dht_.reset();
@@ -889,6 +975,21 @@ DhtRunner::activeDht() const
#endif // OPENDHT_PROXY_CLIENT
}
void
DhtRunner::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());
#else
if (not proxy.empty())
std::cerr << "DHT proxy requested but OpenDHT built without proxy support." << std::endl;
#endif
}
void
DhtRunner::enableProxy(bool proxify)
{
@@ -971,7 +1072,7 @@ DhtRunner::pushNotificationReceived(const std::map<std::string, std::string>& da
#if OPENDHT_PROXY_CLIENT && OPENDHT_PUSH_NOTIFICATIONS
{
std::lock_guard<std::mutex> lck(storage_mtx);
pending_ops_prio.emplace([=](SecureDht& dht) {
pending_ops_prio.emplace([=](SecureDht&) {
if (dht_via_proxy_)
dht_via_proxy_->pushNotificationReceived(data);
});
+20 -17
View File
@@ -185,7 +185,7 @@ NetworkEngine::tellListener(Sp<Node> node, Tid socket_id, const InfoHash& hash,
}
void
NetworkEngine::tellListenerRefreshed(Sp<Node> n, Tid socket_id, const InfoHash& hash, const Blob& token, const std::vector<Value::Id>& values)
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);
@@ -215,7 +215,7 @@ NetworkEngine::tellListenerRefreshed(Sp<Node> n, Tid socket_id, const InfoHash&
}
void
NetworkEngine::tellListenerExpired(Sp<Node> n, Tid socket_id, const InfoHash& hash, const Blob& token, const std::vector<Value::Id>& values)
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);
@@ -262,7 +262,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)
@@ -280,7 +286,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);
@@ -292,24 +298,21 @@ NetworkEngine::requestStep(Sp<Request> sreq)
auto err = send((char*)req.msg.data(), req.msg.size(),
(node.getReplyTime() >= now - UDP_REPLY_TIME) ? 0 : MSG_CONFIRM,
node.getAddr());
std::weak_ptr<Request> wreq = sreq;
if (err == ENETUNREACH ||
err == EHOSTUNREACH ||
err == EAFNOSUPPORT ||
err == EPIPE)
err == EPIPE ||
err == EPERM)
{
scheduler.add(now, [this,wreq]() mutable {
if (auto req = wreq.lock()) {
req->node->setExpired();
if (not req->node->id)
requests.erase(req->tid);
}
});
node.setExpired();
if (not node.id)
requests.erase(req.tid);
} else {
if (err != EAGAIN) {
++req.attempt_count;
}
req.last_try = now;
std::weak_ptr<Request> wreq = sreq;
scheduler.add(req.last_try + Node::MAX_RESPONSE_TIME, [this,wreq] {
if (auto req = wreq.lock())
requestStep(req);
@@ -416,7 +419,7 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, const SockAddr&
msg->msgpack_unpack(msg_res.get());
} catch (const std::exception& e) {
DHT_LOG.w("Can't parse message of size %lu: %s", buflen, e.what());
DHT_LOG.DEBUG.logPrintable(buf, buflen);
//DHT_LOG.DBG.logPrintable(buf, buflen);
return;
}
@@ -548,7 +551,7 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
}
case MessageType::Reply:
if (req) { /* request reply */
if (msg->type == MessageType::AnnounceValue or msg->type == MessageType::Listen)
if (req->getType() == MessageType::AnnounceValue or req->getType() == MessageType::Listen)
req->node->authSuccess();
req->reply_time = scheduler.time();
@@ -658,7 +661,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;
@@ -974,7 +977,7 @@ NetworkEngine::sendNodesValues(const SockAddr& addr, Tid tid, const Blob& nodes,
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());
}
}
+38 -11
View File
@@ -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;
}
}
}
+9
View File
@@ -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,
+37 -17
View File
@@ -136,7 +136,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;
@@ -147,9 +147,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;
@@ -157,7 +157,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));
}
/**
@@ -302,7 +302,7 @@ struct Dht::SearchNode {
return isListening(now, ls);
}
bool isListening(time_point now, NodeListenerStatus::const_iterator listen_status) const {
if (listen_status == listenStatus.end())
if (listen_status == listenStatus.end() or not listen_status->second.req)
return false;
return listen_status->second.req->reply_time + LISTEN_EXPIRE_TIME > now;
}
@@ -320,22 +320,21 @@ struct Dht::SearchNode {
}
/**
* 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 {
@@ -430,7 +429,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 ?
@@ -475,6 +475,17 @@ struct Dht::Search {
bool isAnnounced(Value::Id id) const;
bool isListening(time_point now) const;
void get(Value::Filter f, const Sp<Query>& q, const QueryCallback& qcb, const GetCallback& gcb, const DoneCallback& dcb, Scheduler& scheduler) {
if (gcb or qcb) {
const auto& now = scheduler.time();
callbacks.emplace(now, Get { now, f, q, qcb, gcb, dcb });
auto values = cache.get(f);
if (not values.empty())
gcb(values);
scheduler.edit(nextSearchStep, now);
}
}
size_t listen(ValueCallback cb, Value::Filter f, const Sp<Query>& q, Scheduler& scheduler) {
//DHT_LOG.e(id, "[search %s IPv%c] listen", id.toString().c_str(), (af == AF_INET) ? '4' : '6');
return cache.listen(cb, q, f, [&](const Sp<Query>& q, ValueCallback vcb){
@@ -746,11 +757,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;
}
+22 -33
View File
@@ -56,7 +56,7 @@ 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);
}
}
@@ -68,27 +68,19 @@ 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)) {
DHT_LOG.WARN("Signature verification failed");
return false;
}
else
DHT_LOG.WARN("Signature verification succeeded");
}
if (v->isSigned())
return v->checkSignature();
return type.storePolicy(id, v, nid, a);
};
type.editPolicy = [this,type](InfoHash id, const Sp<Value>& o, Sp<Value>& n, const InfoHash& nid, const SockAddr& a) {
if (!o->isSigned())
if (not o->isSigned())
return type.editPolicy(id, o, n, nid, a);
if (o->owner != n->owner) {
if (o->owner != n->owner or not n->isSigned()) {
DHT_LOG.WARN("Edition forbidden: owner changed.");
return false;
}
if (!o->owner or !o->owner->checkSignature(n->getToSign(), n->signature)) {
DHT_LOG.WARN("Edition forbidden: signature verification failed.");
if (not n->checkSignature())
return false;
}
if (o->seq == n->seq) {
// If the data is exactly the same,
// it can be reannounced, possibly by someone else.
@@ -139,7 +131,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 +139,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;
}
}
@@ -164,7 +156,7 @@ SecureDht::findCertificate(const InfoHash& node, std::function<void(const Sp<cry
{
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 +164,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 +179,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;
@@ -205,7 +197,7 @@ SecureDht::findPublicKey(const InfoHash& node, std::function<void(const Sp<const
{
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;
@@ -236,24 +228,22 @@ SecureDht::checkValue(const Sp<Value>& v)
return {};
}
try {
Value decrypted_val (decrypt(*v));
if (decrypted_val.recipient == getId()) {
if (decrypted_val.owner)
nodesPubKeys_[decrypted_val.owner->getId()] = decrypted_val.owner;
return std::make_shared<Value>(std::move(decrypted_val));
if (auto decrypted_val = v->decrypt(*key_)) {
if (decrypted_val->owner)
nodesPubKeys_[decrypted_val->owner->getId()] = decrypted_val->owner;
return 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 (v->checkSignature()) {
if (v->owner)
nodesPubKeys_[v->owner->getId()] = v->owner;
return v;
}
else
} else
DHT_LOG.WARN("Signature verification failed for %s", v->toString().c_str());
}
// Forward normal values
@@ -327,14 +317,13 @@ SecureDht::putSigned(const InfoHash& hash, Sp<Value> val, DoneCallback callback,
// 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.");
@@ -356,7 +345,7 @@ SecureDht::putSigned(const InfoHash& hash, Sp<Value> val, DoneCallback callback,
void
SecureDht::putEncrypted(const InfoHash& hash, const InfoHash& to, Sp<Value> val, DoneCallback callback, bool permanent)
{
findPublicKey(to, [=](const Sp<const crypto::PublicKey> pk) {
findPublicKey(to, [=](const Sp<const crypto::PublicKey>& pk) {
if(!pk || !*pk) {
if (callback)
callback(false, {});
+5 -4
View File
@@ -154,15 +154,16 @@ 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();
}
StoreDiff remove(const InfoHash& id, Value::Id);
+37 -4
View File
@@ -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 {
@@ -172,7 +172,7 @@ Value::msgpack_unpack_body(const msgpack::object& o)
}
}
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
#ifdef OPENDHT_JSONCPP
Value::Value(Json::Value& json)
{
id = Value::Id(unpackId(json, "id"));
@@ -233,6 +233,39 @@ Value::toJson() const
return val;
}
bool
Value::checkSignature()
{
if (!signatureChecked) {
signatureChecked = true;
if (isSigned()) {
signatureValid = owner and owner->checkSignature(getToSign(), signature);
}
}
return signatureValid;
}
Sp<Value>
Value::decrypt(const crypto::PrivateKey& key)
{
if (not decrypted) {
decrypted = true;
if (isEncrypted()) {
auto decryptedBlob = key.decrypt(cypher);
std::unique_ptr<Value> v {new Value(id)};
auto msg = msgpack::unpack((const char*)decryptedBlob.data(), decryptedBlob.size());
v->msgpack_unpack_body(msg.get());
if (v->recipient != key.getPublicKey().getId())
throw crypto::DecryptError("Recipient mismatch");
// Ignore values belonging to other people
if (not v->owner or not v->owner->checkSignature(v->getToSign(), v->signature))
throw crypto::DecryptError("Signature mismatch");
decryptedValue = std::move(v);
}
}
return decryptedValue;
}
uint64_t
unpackId(const Json::Value& json, const std::string& key) {
uint64_t ret = 0;
@@ -248,7 +281,7 @@ unpackId(const Json::Value& json, const std::string& key) {
} catch (...) {}
return ret;
}
#endif //OPENDHT_PROXY_SERVER
#endif
bool
FieldValue::operator==(const FieldValue& vfd) const
+3 -3
View File
@@ -66,7 +66,7 @@ public:
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) {
if (it->second.expiration <= now) {
expired_values.emplace_back(std::move(it->second.data));
it = values.erase(it);
} else {
@@ -74,7 +74,7 @@ public:
++it;
}
}
while (values.size() >= MAX_VALUES) {
while (values.size() > MAX_VALUES) {
// too many values, remove oldest values
time_point oldest_creation = time_point::max();
auto oldest_value = values.end();
@@ -127,7 +127,7 @@ private:
ValueCache& operator=(ValueCache&&) = delete;
/* The maximum number of values we store in the cache. */
static constexpr unsigned MAX_VALUES {1024};
static constexpr unsigned MAX_VALUES {4096};
struct CacheValueStorage {
Sp<Value> data {};
+2 -2
View File
@@ -3,7 +3,7 @@ bin_PROGRAMS = opendht_unit_tests
AM_CPPFLAGS = -I../include
nobase_include_HEADERS = infohashtester.h dhtrunnertester.h
opendht_unit_tests_SOURCES = tests_runner.cpp infohashtester.cpp dhtrunnertester.cpp
nobase_include_HEADERS = infohashtester.h cryptotester.h dhtrunnertester.h dhtproxytester.h
opendht_unit_tests_SOURCES = tests_runner.cpp cryptotester.cpp infohashtester.cpp dhtrunnertester.cpp dhtproxytester.cpp
opendht_unit_tests_LDFLAGS = -lopendht -lcppunit -L@top_builddir@/src/.libs @GnuTLS_LIBS@
endif
+90
View File
@@ -0,0 +1,90 @@
/*
* Copyright (C) 2018 Savoir-faire Linux Inc.
*
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "cryptotester.h"
#include "opendht/crypto.h"
namespace test {
CPPUNIT_TEST_SUITE_REGISTRATION(CryptoTester);
void
CryptoTester::setUp() {
}
void
CryptoTester::testSignatureEncryption() {
auto key = dht::crypto::PrivateKey::generate();
auto public_key = key.getPublicKey();
std::vector<uint8_t> data {5, 10};
std::vector<uint8_t> signature = key.sign(data);
// check signature
CPPUNIT_ASSERT(public_key.checkSignature(data, signature));
// encrypt data
std::vector<uint8_t> encrypted = public_key.encrypt(data);
std::vector<uint8_t> decrypted = key.decrypt(encrypted);
CPPUNIT_ASSERT(data == decrypted);
}
void
CryptoTester::testCertificateRevocation()
{
auto ca1 = dht::crypto::generateIdentity("ca1");
auto account1 = dht::crypto::generateIdentity("acc1", ca1, 4096, true);
auto device11 = dht::crypto::generateIdentity("dev11", account1);
auto device12 = dht::crypto::generateIdentity("dev12", account1);
dht::crypto::TrustList list;
list.add(*ca1.second);
auto v = list.verify(*account1.second);
CPPUNIT_ASSERT_MESSAGE(v.toString(), v);
list.add(*account1.second);
v = list.verify(*device11.second);
CPPUNIT_ASSERT_MESSAGE(v.toString(), v);
v = list.verify(*device12.second);
CPPUNIT_ASSERT_MESSAGE(v.toString(), v);
auto ca2 = dht::crypto::generateIdentity("ca2");
auto account2 = dht::crypto::generateIdentity("acc2", ca2, 4096, true);
auto device2 = dht::crypto::generateIdentity("dev2", account2);
v = list.verify(*device2.second);
CPPUNIT_ASSERT_MESSAGE(v.toString(), !v);
account1.second->revoke(*account1.first, *device11.second);
dht::crypto::TrustList list2;
list2.add(*account1.second);
v = list2.verify(*device11.second);
CPPUNIT_ASSERT_MESSAGE(v.toString(), !v);
v = list2.verify(*device12.second);
CPPUNIT_ASSERT_MESSAGE(v.toString(), v);
}
void
CryptoTester::tearDown() {
}
} // namespace test
+53
View File
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2018 Savoir-faire Linux Inc.
*
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
// cppunit
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
namespace test {
class CryptoTester : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(CryptoTester);
CPPUNIT_TEST(testSignatureEncryption);
CPPUNIT_TEST(testCertificateRevocation);
CPPUNIT_TEST_SUITE_END();
public:
/**
* Method automatically called before each test by CppUnit
*/
void setUp();
/**
* Method automatically called after each test CppUnit
*/
void tearDown();
/**
* Test data signature, encryption and decryption
*/
void testSignatureEncryption();
/**
* Test certificate generation, validation and revocation
*/
void testCertificateRevocation();
};
} // namespace test
+126
View File
@@ -0,0 +1,126 @@
/*
* Copyright (C) 2018 Savoir-faire Linux Inc.
*
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "dhtproxytester.h"
// std
#include <iostream>
#include <string>
#include <chrono>
#include <condition_variable>
namespace test {
CPPUNIT_TEST_SUITE_REGISTRATION(DhtProxyTester);
void
DhtProxyTester::setUp() {
nodePeer.run(42222, {}, true);
nodeProxy = std::make_shared<dht::DhtRunner>();
nodeClient = std::make_shared<dht::DhtRunner>();
nodeProxy->run(42232, {}, true);
nodeProxy->bootstrap(nodePeer.getBound());
server = std::unique_ptr<dht::DhtProxyServer>(new dht::DhtProxyServer(nodeProxy, 8080));
nodeClient->run(42242, {}, true);
nodeClient->bootstrap(nodePeer.getBound());
nodeClient->setProxyServer("127.0.0.1:8080");
nodeClient->enableProxy(true);
}
void
DhtProxyTester::tearDown() {
nodePeer.join();
nodeClient->join();
server->stop();
server = nullptr;
nodeProxy->join();
}
void
DhtProxyTester::testGetPut() {
bool done = false;
std::condition_variable cv;
std::mutex cv_m;
auto key = dht::InfoHash::get("GLaDOs");
dht::Value val {"Hey! It's been a long time. How have you been?"};
auto val_data = val.data;
nodePeer.put(key, std::move(val), [&](bool) {
done = true;
cv.notify_all();
});
std::unique_lock<std::mutex> lk(cv_m);
cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; });
auto vals = nodeClient->get(key).get();
CPPUNIT_ASSERT(not vals.empty());
CPPUNIT_ASSERT(vals.front()->data == val_data);
}
void
DhtProxyTester::testListen() {
bool done = false;
std::condition_variable cv;
std::mutex cv_m;
std::unique_lock<std::mutex> lk(cv_m);
auto key = dht::InfoHash::get("GLaDOs");
// If a peer send a value, the listen operation from the client
// should retrieve this value
dht::Value firstVal {"Hey! It's been a long time. How have you been?"};
auto firstVal_data = firstVal.data;
nodePeer.put(key, std::move(firstVal), [&](bool) {
done = true;
cv.notify_all();
});
cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; });
done = false;
auto values = std::vector<dht::Blob>();
nodeClient->listen(key, [&](const std::vector<std::shared_ptr<dht::Value>>& v, bool) {
for (const auto& value : v)
values.emplace_back(value->data);
done = true;
cv.notify_all();
return true;
});
cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; });
done = false;
// Here values should contains 2 values
CPPUNIT_ASSERT_EQUAL(static_cast<int>(values.size()), 1);
CPPUNIT_ASSERT(values.front() == firstVal_data);
// And the listen should retrieve futures values
// All values
dht::Value secondVal {"You're a monster"};
auto secondVal_data = secondVal.data;
nodePeer.put(key, std::move(secondVal));
cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; });
// Here values should contains 3 values
CPPUNIT_ASSERT_EQUAL(static_cast<int>(values.size()), 2);
CPPUNIT_ASSERT(values.back() == secondVal_data);
}
} // namespace test
+64
View File
@@ -0,0 +1,64 @@
/*
* Copyright (C) 2018 Savoir-faire Linux Inc.
*
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
// cppunit
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <opendht/dhtrunner.h>
#include <opendht/dht_proxy_server.h>
namespace test {
class DhtProxyTester : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(DhtProxyTester);
CPPUNIT_TEST(testGetPut);
CPPUNIT_TEST(testListen);
CPPUNIT_TEST_SUITE_END();
public:
/**
* Method automatically called before each test by CppUnit
* Init nodes
*/
void setUp();
/**
* Method automatically called after each test CppUnit
*/
void tearDown();
/**
* Test get and put methods
*/
void testGetPut();
/**
* Test listen
*/
void testListen();
private:
dht::DhtRunner nodePeer {};
std::shared_ptr<dht::DhtRunner> nodeClient;
std::shared_ptr<dht::DhtRunner> nodeProxy;
std::unique_ptr<dht::DhtProxyServer> server;
};
} // namespace test
+1 -1
View File
@@ -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@
+3 -2
View File
@@ -144,7 +144,7 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params
#if OPENDHT_PROXY_SERVER
for (const auto& proxy : proxies) {
std::cout << "Stats for proxy on port " << proxy.first << std::endl;
std::cout << " " << proxy.second->getStats().toString() << std::endl;
std::cout << " " << proxy.second->stats().toString() << std::endl;
}
#endif
continue;
@@ -199,6 +199,7 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params
continue;
} else if (op == "cc") {
dht->connectivityChanged();
continue;
}
#if OPENDHT_PROXY_SERVER
else if (op == "pst") {
@@ -496,7 +497,7 @@ main(int argc, char **argv)
crt = dht::crypto::generateIdentity("DHT Node", ca_tmp);
}
dht::DhtRunner::Config config;
dht::DhtRunner::Config config {};
config.dht_config.node_config.network = params.network;
config.dht_config.node_config.maintain_storage = false;
config.dht_config.id = crt;
+9 -5
View File
@@ -63,11 +63,15 @@ $(function() {
if (!element || element.length == 0)
return;
const o = JSON.parse(element);
const d = window.atob(o.data);
const delay = Math.max(0, start-now);
$('<li class="list-group-item"/>').append(valueGetElement(o)).appendTo(group).hide().delay(delay).slideDown(100);
lastAppended = i+1;
start = Math.max(start, now)+25;
if (o.expired) {
$('#value'+o.id).slideUp(100, (e) => $(e.target).remove());
} else {
const d = window.atob(o.data);
const delay = Math.max(0, start-now);
$('<li class="list-group-item" id="value'+o.id+'"/>').append(valueGetElement(o)).appendTo(group).hide().delay(delay).slideDown(100);
lastAppended = i+1;
start = Math.max(start, now)+25;
}
}
} else if (this.status !== 0) {
group.empty().append($('<li class="list-group-item list-group-item-danger"/>').text("Error loading content: " + this.statusText));