Compare commits

...
Author SHA1 Message Date
Adrien Béraud c8399996d9 readme: update 2018-02-28 21:36:17 -05:00
Adrien Béraud c9cbced0c8 build: make indexation optional 2018-02-27 21:48:49 -05:00
Adrien Béraud 2cf2112aec dhtnode: simplify macros 2018-02-27 21:33:48 -05:00
Adrien Béraud 012f6ed2f6 Merge pull request #247 from AmarOk1412/fixCancelListen
proxyserver: fix cancelListen and reduce push notifications number
2018-02-26 17:19:32 -05:00
Sébastien Blin fcac10c4c8 proxyserver: fix cancelListen and reduce push notifications number 2018-02-26 17:01:23 -05:00
Adrien Béraud f2b692c716 Merge pull request #245 from AmarOk1412/fixCancelListen
dhtproxy: fix cancelListen with pushNotifications
2018-02-26 15:37:00 -05:00
Sébastien Blin 22ef53acef dhtproxy: fix cancelListen with pushNotifications
Fill the body of the UNSUBSCRIBE method and fix dhtnode application
2018-02-26 15:25:48 -05:00
Adrien Béraud 2af70e6d50 Merge pull request #243 from AmarOk1412/fixPushBuild
pushnotifications: fix build without push notifications, improve travis script
2018-02-26 14:21:34 -05:00
Sébastien Blin 63c9c607bd pushnotifications: fix build without push notifications, improve travis script 2018-02-26 13:27:06 -05:00
Adrien Béraud cab0813b17 dht: reduce log verbosity 2018-02-24 15:15:49 -05:00
Adrien Béraud 06cc490ee4 listener: remove unused field 2018-02-24 15:05:14 -05:00
Adrien Béraud 6821988ddf node: use thread local random device 2018-02-24 14:30:40 -05:00
Adrien Béraud 8cce9fcb85 search: remove getNextStepTime 2018-02-23 13:50:25 -05:00
Adrien Béraud 56caae019c proxy server: add push notification ttl 2018-02-19 02:10:56 +01:00
Adrien Béraud f5afcbf62b proxy server: use high priority notifications
Normal priority notifications may be delayed.
2018-02-16 01:54:44 +01:00
Adrien Béraud 0957d30e86 y2038 bug: don't allow time wrap for certificate validity 2018-02-10 23:21:31 +01:00
Adrien Béraud 3a632b1544 proxy server: add logs 2018-02-10 14:31:23 +01:00
Adrien Béraud 2c0f4a897c dhtnode: fix cancelListen 2018-02-10 14:31:00 +01:00
Adrien Béraud be6a5671ed y2038 bug: change certificate duration to 10 years
A certificate duration of 20 years was making certificate expire
after 2038 which is not supported on many 32 bits platforms.
Reduce to 10 years, which gives another decade to deprecate 32-bits platforms.
2018-02-09 01:40:03 +01:00
Adrien Béraud 61ac297b1b crypto: add method to read Certificate activation date 2018-02-09 00:42:25 +01:00
Adrien Béraud da867dd07c Merge pull request #239 from kiddouk/fix/dhtnode-identity
Generates RSA pairs for dhtnode instead of EC cert
2018-02-08 13:05:46 +01:00
Sébastien Requiem 962249ec75 Generates RSA pairs for dhtnode instead of EC cert
* dhtnode was generating twice an EC cert which rendered encryption
impossible since opendht lib is expecting an RSA public key to encrypt
the data for the destination.

* fix savoirfairelinux/opendht#235
2018-02-08 10:56:17 +01:00
Adrien Béraud c03ba57895 dhtnode: use explicit unique_ptr constructor 2018-02-07 17:40:59 +01:00
Adrien Béraud e2224fac5a Merge pull request #209 from dctremblay/readme-update-gcc-version
README : update GCC required version from 4.8 to 5.2
2018-02-01 00:52:39 +01:00
Adrien Béraud 48aaaf1e5d Merge branch 'master' into readme-update-gcc-version 2018-02-01 00:51:41 +01:00
Adrien Béraud 80ee256712 update to version 1.6.0rc1
* added REST proxy client implementing the DHT API
* added optional support for push notifications through the proxy server,
  using the gorush REST API: https://github.com/appleboy/gorush#web-api
* DhtRunner: now able to use DhtProxyClient as a DHT node
* DhtRunner: can switch between local or proxy node during runtime
* SockAddr: added methods to handle IPv6-mapped IPv4 addresses
* iOS: handle SIGPIPE on UDP sockets by recreating sockets
* fixed occasional crash during packet processing
2018-01-31 18:57:54 +01:00
Adrien Béraud e274ffd83b headers: unify API across build options 2018-01-31 18:07:43 +01:00
Adrien Béraud bd9496e2ba crypto: fix build on GnuTLS 3.3 2018-01-26 15:10:07 -05:00
Adrien Béraud 4d4ba2be96 Merge pull request #234 from atraczyk/master
fix bad paths
2018-01-24 17:31:36 -05:00
atraczyk 680e30be9b fix bad paths 2018-01-24 17:30:21 -05:00
Adrien Béraud 233301c3f3 dhtrunner: use proxy by default if configured 2018-01-23 12:19:38 -05:00
Adrien Béraud 0db21772c1 Merge pull request #232 from atraczyk/sigpipe
dhtrunner: pass SockAddrs by value to startNetwork
2018-01-23 11:34:14 -05:00
Adrien Béraud e6a2d96622 net: restart socket in case of sigpipe 2018-01-23 11:34:14 -05:00
atraczyk e5d83d47be dhtrunner: don't pass SockAddrs by reference to startNetwork 2018-01-23 11:34:14 -05:00
Adrien Béraud 3d77c76a4d dhtrunner: restart listeners only if one exists 2018-01-23 11:31:12 -05:00
Adrien Béraud 043d5a7c7c dhtrunner: check dht instance in setLocalCertificateStore 2018-01-19 14:05:21 -05:00
Adrien Béraud 636eb1972d proxy client: improve robustness, switch behavior 2018-01-18 16:36:13 -05:00
Adrien Béraud 27a250d2c0 proxyclient: fall to disconnected in case of connectivity failure 2018-01-17 12:09:39 -05:00
Adrien Béraud 02f4c81f2a dhtrunner: improve proxy listen handling 2018-01-17 11:40:18 -05:00
Adrien Béraud 0420ebcce3 dhtinterface: pass listen filters by value 2018-01-17 11:40:18 -05:00
Adrien Béraud 15e3696b88 proxy client: fix build when push notifications are disabled 2018-01-13 16:16:19 -05:00
Adrien Béraud ca693a6ac1 pushNotificationReceived: take a map<string, string>
JSON is used for the DHT proxy protocol and to communicate with gorush.
Push notifications services might use any format, not necessarily JSON.
Push values are received as a map<string, string>, so receive them in
that format.
2018-01-12 13:20:07 -05:00
Adrien Béraud 1d6a307b6b proxy server: send data push message instead of notification 2018-01-11 17:01:10 -05:00
Adrien Béraud 227cca883c dhtrunner: start proxy ASAP, use local dht for node stats/import/export 2018-01-11 14:24:59 -05:00
Adrien Béraud ef82a7ecba proxy client: make proxy status check asynchronous 2018-01-11 14:24:59 -05:00
Adrien Béraud 6e9f2cda79 SockAddr: add isMappedIPv4, getMappedIPv4 2018-01-11 13:29:18 -05:00
Adrien Béraud 10b58c5ce7 SockAddr: add constructor from simple sockaddr* 2018-01-11 13:29:18 -05:00
Adrien Béraud ab731cd7d0 Merge pull request #228 from AmarOk1412/push_notifications_client
Add support for push notifications
2018-01-11 13:14:43 -05:00
Adrien Béraud a0d8f4734a dhtproxy: allow token refresh, set clientID (#6)
* add method to set the push token independently from other configuration,
  since the push token can be updated during runtime.
* allow to provide a "clientID" to be sent in push notifications,
  used to demultiplex push notifications to multiple OpenDHT nodes running
  on the same host. For instance Ring uses the accountID as the clientID
  to know which account received the push notification.
* use {"platform":"android"/"ios"} to communicate the platform instead of {"isAndroid":true/false}.
2018-01-10 16:32:57 -05:00
Adrien Béraud 237859d8c3 Merge branch 'master' into push_notifications_client 2017-12-20 17:51:01 +01:00
Adrien Béraud 24dec0682f Merge pull request #231 from AmarOk1412/fixLog
dht_interface: fix logger issues
2017-12-19 02:11:25 +01:00
AmarOk 4b46f6a821 dht_interface: fix logger issues
Make DHT_LOG static to use it in the whole library
2017-12-15 12:45:10 -06:00
Sébastien Blin 37a9a3d9da dhtproxy: inject push notifications in opendht
+ add the ability to inject push notifications in opendht to call
correct callbacks
+ if a timeout occurs and no cancelListen was called, relaunch the
listen operation
+ Pass devicekey to enable push notifications for DhtProxyClient
+ Update sockaddr to fix the build on all platforms

Note: update dhtnode
2017-12-12 15:03:02 -06:00
Sébastien Blin 75cca66d7e dhtproxy: support push notifications for mobile clients
+ Add SUBSCRIBE and UNSUBSCRIBE for DhtProxyServer
+ Add the ability to stop Listen via a timeout (and send a push
Notification for the client)
+ Add the ability to use gorush to send push notifications

Note: Update dhtnode
2017-12-12 10:24:54 -06:00
Adrien Béraud 762a1c7ad2 Merge pull request #220 from AmarOk1412/dhtproxyclient_merge
proxy: implement DhtProxyClient
2017-12-12 13:38:23 +01:00
AmarOk d19ba2cb41 proxyserver: add lock_guard to avoid to manually unlock 2017-12-06 14:43:28 -06:00
AmarOk 557323deff refactor: use new jsoncpp API
Use the latest jsoncpp API to remove depreciation warning.
2017-12-06 08:49:24 -06:00
AmarOk 29ffbf6aa7 dhtrunner: remove DhtRunner::getAddrInfo
Was replaced by SockAddr::resolve
2017-12-05 10:02:38 -06:00
Adrien Béraud aaa577d9e3 proxy_client: use atomic bool for shared state flags (#3) 2017-12-05 09:50:38 -06:00
Adrien Béraud d2f91af946 node: use shared_ptr to manage Sockets 2017-12-02 16:03:27 +01:00
Adrien Béraud f9d48cd15a Merge pull request #227 from atraczyk/master
Build: add new files to VS project and adjust filters
2017-11-30 00:17:29 +01:00
atraczyk 507326cebc Build: add new files to VS project and adjust filters 2017-11-29 13:46:07 -05:00
Adrien Béraud b24832e0e1 Merge pull request #226 from atraczyk/master
InfoHash: fix stream operator<< declaration
2017-11-29 17:14:03 +01:00
atraczyk 6cdcd183b4 InfoHash: fix stream operator<< declaration 2017-11-29 11:08:39 -05:00
David Côté-Tremblay f69d6426bd README : GCC required version from 4.8 to 5.4 2017-08-10 11:52:46 -04:00
42 changed files with 1652 additions and 841 deletions

No files matched your search

+58 -4
View File
@@ -5,13 +5,67 @@ services:
language: cpp
env:
matrix:
- OPENDHT_TEST_JOB="opendht.classic"
- OPENDHT_TEST_JOB="opendht.llvm"
- OPENDHT_TEST_JOB="opendht.proxyserver"
- OPENDHT_TEST_JOB="opendht.proxyclient"
- OPENDHT_TEST_JOB="opendht.proxyserverpush"
- OPENDHT_TEST_JOB="opendht.proxyclientpush"
- OPENDHT_TEST_JOB="opendht.all"
before_install:
- docker pull aberaud/opendht-deps
- docker pull aberaud/opendht-deps-llvm
- |
# non llvm builds
if [[ "$OPENDHT_TEST_JOB" != *"opendht.llvm"* ]]; then
docker pull aberaud/opendht-deps;
if [[ "$OPENDHT_TEST_JOB" != *"opendht.classic"* ]]; then
docker build -t opendht-deps-proxy -f docker/DockerfileDepsProxy .;
fi
fi
- |
# classic build
if [[ "$OPENDHT_TEST_JOB" == *"opendht.llvm"* ]]; then
docker pull aberaud/opendht-deps-llvm
fi
script:
- docker build -f docker/DockerfileTravis .
- docker build -f docker/DockerfileTravisLlvm .
- |
# classic build
if [[ "$OPENDHT_TEST_JOB" == *"opendht.classic"* ]]; then
docker build -t opendht -f docker/DockerfileTravis .;
fi
- |
# proxy builds
if [[ "$OPENDHT_TEST_JOB" != *"opendht.llvm"* ]] && [[ "$OPENDHT_TEST_JOB" != *"opendht.classic"* ]]; then
docker build -t opendht-proxy -f docker/DockerfileTravisProxy .;
options='';
if [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyserver"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyserverpush"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.all"* ]]; then
options+='-DOPENDHT_PROXY_SERVER=ON ';
else
options+='-DOPENDHT_PROXY_SERVER=OFF ';
fi
if [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyclient"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyclientpush"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.all"* ]]; then
options+='-DOPENDHT_PROXY_CLIENT=ON ';
else
options+='-DOPENDHT_PROXY_CLIENT=OFF ';
fi
if [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyserverpush"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyclientpush"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.all"* ]]; then
options+='-DOPENDHT_PUSH_NOTIFICATIONS=ON ';
else
options+='-DOPENDHT_PUSH_NOTIFICATIONS=OFF ';
fi
docker run opendht-proxy /bin/sh -c "cd /root/opendht && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=ON -DOPENDHT_LTO=ON $options .. && make -j8 && make install";
fi
- |
# llvm build
if [[ "$OPENDHT_TEST_JOB" == *"opendht.llvm"* ]]; then
docker build -f docker/DockerfileTravisLlvm .
fi
notifications:
email:
+14 -3
View File
@@ -1,7 +1,7 @@
cmake_minimum_required (VERSION 3.1)
project (opendht)
set (opendht_VERSION_MAJOR 1)
set (opendht_VERSION_MINOR 5.0)
set (opendht_VERSION_MINOR 6.0)
set (opendht_VERSION ${opendht_VERSION_MAJOR}.${opendht_VERSION_MINOR})
set (PACKAGE_VERSION ${opendht_VERSION})
set (VERSION "${opendht_VERSION}")
@@ -17,8 +17,10 @@ option (OPENDHT_ARGON2 "Use included argon2 sources" OFF)
option (OPENDHT_LTO "Build with LTO" OFF)
option (OPENDHT_SANITIZE "Build with address sanitizer and stack protector" OFF)
option (OPENDHT_PROXY_SERVER "Enable DHT proxy server, use Restbed and jsoncpp" OFF)
option (OPENDHT_PUSH_NOTIFICATIONS "Enable push notifications support" OFF)
option (OPENDHT_PROXY_SERVER_IDENTITY "Allow clients to use the node identity" OFF)
option (OPENDHT_PROXY_CLIENT "Enable DHT proxy client, use Restbed and jsoncpp" OFF)
option (OPENDHT_INDEX "Build DHT indexation feature" OFF)
find_package(Doxygen)
option (OPENDHT_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" ${DOXYGEN_FOUND})
@@ -130,7 +132,6 @@ list (APPEND opendht_SOURCES
src/network_engine.cpp
src/securedht.cpp
src/dhtrunner.cpp
src/indexation/pht.cpp
src/log.cpp
)
@@ -156,9 +157,14 @@ list (APPEND opendht_HEADERS
include/opendht/log.h
include/opendht/log_enable.h
include/opendht.h
include/opendht/indexation/pht.h
)
if (OPENDHT_INDEX)
list (APPEND opendht_SOURCES src/indexation/pht.cpp)
list (APPEND opendht_HEADERS include/opendht/indexation/pht.h)
add_definitions(-DOPENDHT_INDEXATION)
endif()
if (OPENDHT_PROXY_SERVER)
add_definitions(-DOPENDHT_PROXY_SERVER=true)
if (OPENDHT_PROXY_SERVER_IDENTITY)
@@ -172,6 +178,11 @@ 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 ()
+11 -4
View File
@@ -19,11 +19,14 @@
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="wingetopt.c" />
<ClCompile Include="..\src\base64.cpp" />
<ClCompile Include="..\src\callbacks.cpp" />
<ClCompile Include="..\src\crypto.cpp" />
<ClCompile Include="..\src\default_types.cpp" />
<ClCompile Include="..\src\dht.cpp" />
<ClCompile Include="..\src\dhtrunner.cpp" />
<ClCompile Include="..\src\dht_proxy_server.cpp" />
<ClCompile Include="..\src\indexation\pht.cpp" />
<ClCompile Include="..\src\infohash.cpp" />
<ClCompile Include="..\src\log.cpp" />
@@ -36,6 +39,8 @@
<ClCompile Include="..\src\value.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="unistd.h" />
<ClInclude Include="wingetopt.h" />
<ClInclude Include="..\include\opendht.h" />
<ClInclude Include="..\include\opendht\callbacks.h" />
<ClInclude Include="..\include\opendht\crypto.h" />
@@ -43,6 +48,7 @@
<ClInclude Include="..\include\opendht\default_types.h" />
<ClInclude Include="..\include\opendht\dht.h" />
<ClInclude Include="..\include\opendht\dhtrunner.h" />
<ClInclude Include="..\include\opendht\dht_proxy_server.h" />
<ClInclude Include="..\include\opendht\indexation\pht.h" />
<ClInclude Include="..\include\opendht\infohash.h" />
<ClInclude Include="..\include\opendht\log.h" />
@@ -51,7 +57,6 @@
<ClInclude Include="..\include\opendht\node.h" />
<ClInclude Include="..\include\opendht\node_cache.h" />
<ClInclude Include="..\include\opendht\rate_limiter.h" />
<ClInclude Include="..\include\opendht\request.h" />
<ClInclude Include="..\include\opendht\rng.h" />
<ClInclude Include="..\include\opendht\routing_table.h" />
<ClInclude Include="..\include\opendht\scheduler.h" />
@@ -59,9 +64,11 @@
<ClInclude Include="..\include\opendht\sockaddr.h" />
<ClInclude Include="..\include\opendht\utils.h" />
<ClInclude Include="..\include\opendht\value.h" />
<ClInclude Include="..\src\net.h" />
<ClInclude Include="..\src\base64.h" />
<ClInclude Include="..\src\listener.h" />
<ClInclude Include="..\src\net.h" />
<ClInclude Include="..\src\parsed_message.h" />
<ClInclude Include="..\src\request.h" />
<ClInclude Include="..\src\search.h" />
<ClInclude Include="..\src\storage.h" />
</ItemGroup>
@@ -215,9 +222,9 @@ copy ..\include\opendht.h $(OutDir)\include\</Command>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>$(ProjectDir)contrib\build\include;$(ProjectDir)..\include;$(ProjectDir)..\include\opendht;$(ProjectDir)contrib\build\msgpack-c\include;$(ProjectDir)contrib\build\argon2\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir)contrib\build\include;$(ProjectDir)..\include;$(ProjectDir)..\include\opendht;$(ProjectDir)contrib\build\msgpack-c\include;$(ProjectDir)contrib\build\argon2\include;$(ProjectDir)..\..\include;$(ProjectDir)..\..\argon2\include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<DisableSpecificWarnings>4804;4800;4101;4267;4244;4503;</DisableSpecificWarnings>
<DisableSpecificWarnings>4804;4800;4101;4267;4244;4503;4273;</DisableSpecificWarnings>
<AdditionalOptions>-D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions)</AdditionalOptions>
<ProgramDataBaseFileName>$(OutDir)\lib\x64\$(TargetName).pdb</ProgramDataBaseFileName>
</ClCompile>
+79 -51
View File
@@ -31,9 +31,6 @@
<ClCompile Include="..\src\node_cache.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\indexation\pht.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\routing_table.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -46,80 +43,102 @@
<ClCompile Include="..\src\value.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\base64.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\dht_proxy_server.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\indexation\pht.cpp">
<Filter>Source Files\indexation</Filter>
</ClCompile>
<ClCompile Include="wingetopt.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\opendht\callbacks.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\crypto.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\default_types.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\dht.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\dhtrunner.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\infohash.h">
<Filter>Header Files</Filter>
<ClInclude Include="..\include\opendht\def.h" />
<ClInclude Include="..\include\opendht\sockaddr.h" />
<ClInclude Include="..\src\net.h" />
<ClInclude Include="..\src\parsed_message.h" />
<ClInclude Include="..\src\base64.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\src\listener.h">
<Filter>Header Files</Filter>
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\log.h">
<Filter>Header Files</Filter>
<ClInclude Include="..\src\request.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\log_enable.h">
<Filter>Header Files</Filter>
<ClInclude Include="..\src\search.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\network_engine.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\node.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\node_cache.h">
<Filter>Header Files</Filter>
<ClInclude Include="..\src\storage.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\indexation\pht.h">
<Filter>Header Files</Filter>
<ClInclude Include="..\include\opendht\callbacks.h">
<Filter>Header Files\opendht</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\crypto.h">
<Filter>Header Files\opendht</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\default_types.h">
<Filter>Header Files\opendht</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\dht.h">
<Filter>Header Files\opendht</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\dht_proxy_server.h">
<Filter>Header Files\opendht</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\dhtrunner.h">
<Filter>Header Files\opendht</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\infohash.h">
<Filter>Header Files\opendht</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\log.h">
<Filter>Header Files\opendht</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\log_enable.h">
<Filter>Header Files\opendht</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\network_engine.h">
<Filter>Header Files\opendht</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\node.h">
<Filter>Header Files\opendht</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\node_cache.h">
<Filter>Header Files\opendht</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\rate_limiter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\request.h">
<Filter>Header Files</Filter>
<Filter>Header Files\opendht</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\rng.h">
<Filter>Header Files</Filter>
<Filter>Header Files\opendht</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\routing_table.h">
<Filter>Header Files</Filter>
<Filter>Header Files\opendht</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\scheduler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\search.h">
<Filter>Header Files</Filter>
<Filter>Header Files\opendht</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\securedht.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\storage.h">
<Filter>Header Files</Filter>
<Filter>Header Files\opendht</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\utils.h">
<Filter>Header Files</Filter>
<Filter>Header Files\opendht</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\value.h">
<Filter>Header Files</Filter>
<Filter>Header Files\opendht</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\indexation\pht.h">
<Filter>Header Files\opendht\indexation</Filter>
</ClInclude>
<ClInclude Include="unistd.h" />
<ClInclude Include="wingetopt.h" />
</ItemGroup>
<ItemGroup>
<Filter Include="Source Files">
@@ -128,5 +147,14 @@
<Filter Include="Header Files">
<UniqueIdentifier>{d1ab5bfe-3ab1-45ee-9324-b4b071887668}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\indexation">
<UniqueIdentifier>{939134d3-d5a2-4458-bca8-a10ea9042ea2}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\opendht">
<UniqueIdentifier>{0b46e2d0-4874-404c-bae3-fae8d49b85b8}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\opendht\indexation">
<UniqueIdentifier>{03bd5139-2ae5-41b0-a77b-944814e1b2b6}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
+11 -9
View File
@@ -4,14 +4,14 @@
<a id="user-content-opendht-" class="anchor" href="/savoirfairelinux/opendht/blob/master/README.md#opendht-" aria-hidden="true"></a>OpenDHT
</h1>
A lightweight C++11 Distributed Hash Table implementation originally based on https://github.com/jech/dht by Juliusz Chroboczek.
A lightweight C++11 Distributed Hash Table implementation.
* Light and fast C++11 Kademlia DHT library.
* Light and fast C++11 Kademlia DHT library
* Distributed shared key->value data-store
* Clean and powerful distributed map API with storage of arbitrary binary values of up to 56 KB.
* Optional public key cryptography layer providing data signature and encryption (using GnuTLS).
* IPv4 and IPv6 support.
* Python binding.
* Clean and powerful distributed map API with storage of arbitrary binary values of up to 56 KB
* Public key cryptography layer providing optional data signature and encryption (using GnuTLS)
* IPv4 and IPv6 support
* Python binding
## Documentation
See the wiki: <https://github.com/savoirfairelinux/opendht/wiki>
@@ -93,7 +93,7 @@ for r in results:
- Nettle 2.4+, a GnuTLS dependency for crypto.
- (optional) restbed used for the REST API. commit fb84213e170bc171fecd825a8e47ed9f881a12cd (https://github.com/AmarOk1412/restbed/tree/async_read_until)
- (optional) jsoncpp 1.7.4-3+, used for the REST API.
- Build tested with GCC 4.8+ (GNU/Linux, Android, Windows with MinGW), Clang/LLVM (Linux, macOS).
- Build tested with GCC 5.2+ (GNU/Linux, Windows with MinGW), Clang/LLVM (GNU/Linux, Android, macOS, iOS).
- Build tested with Microsoft Visual Studio 2015
## Contact
@@ -101,7 +101,7 @@ for r in results:
IRC: join us on Freenode at [`#opendht`](https://webchat.freenode.net/?channels=%23opendht).
## Licence
Copyright (C) 2014-2017 Savoir-faire Linux Inc.
Copyright (C) 2014-2018 Savoir-faire Linux Inc.
OpenDHT is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
@@ -109,7 +109,9 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY
See COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html for the full GPLv3 licence.
This project is independent from another project called OpenDHT (Sean Rhea. Ph.D. Thesis, 2005), now extinct.
## Acknowledgements
This project was originally based on https://github.com/jech/dht by Juliusz Chroboczek.
It is independent from another project called OpenDHT (Sean Rhea. Ph.D. Thesis, 2005), now extinct.
## Donations
We gratefully accept Bitcoin donations to support OpenDHT development at: `bitcoin:3EykSd1An888efq4Bq3KaV3hJ3JQ4FPnwm`.
+18 -2
View File
@@ -1,6 +1,6 @@
dnl define macros
m4_define([opendht_major_version], 1)
m4_define([opendht_minor_version], 5)
m4_define([opendht_minor_version], 6)
m4_define([opendht_patch_version], 0)
m4_define([opendht_version],
[opendht_major_version.opendht_minor_version.opendht_patch_version])
@@ -32,6 +32,13 @@ AS_IF([test "x$enable_logs" != "xno"], [
AC_DEFINE([OPENDHT_LOG], [false], [Define if DHT logs are enabled])
])
dnl Check for indexation
AC_ARG_ENABLE([indexation], [AS_HELP_STRING([--enable-indexation], [Enable DHT indexation])])
AM_CONDITIONAL(ENABLE_INDEXATION, test x$enable_indexation == xyes)
AS_IF([test "x$enable_indexation" = "xyes"], [
AC_DEFINE([OPENDHT_INDEXATION], [1], [Define if DHT indexation is enabled])
])
dnl Check for Doxygen
AC_ARG_ENABLE([doc], AS_HELP_STRING([--enable-doc], [Enable documentation generation (doxygen)]))
AS_IF([test "x$enable_doc" = "xyes"], [
@@ -130,6 +137,8 @@ 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)
@@ -138,11 +147,18 @@ AM_CONDITIONAL(ENABLE_PROXY_CLIENT, test x$proxy_client == xyes)
AM_COND_IF([ENABLE_PROXY_SERVER], [
AC_CHECK_LIB(restbed, exit,, AC_MSG_ERROR([Missing restbed files]))
PKG_CHECK_MODULES([Jsoncpp], [jsoncpp >= 1.7.4])
PKG_CHECK_MODULES([Jsoncpp], [jsoncpp >= 1.7.2])
CPPFLAGS+=" -DOPENDHT_PROXY_SERVER=true -ljsoncpp -lrestbed"
], [
CPPFLAGS+=" -DOPENDHT_PROXY_SERVER=false"
])
AM_COND_IF([ENABLE_PUSH_NOTIFICATIONS], [
CPPFLAGS+=" -DOPENDHT_PUSH_NOTIFICATIONS=true"
], [
CPPFLAGS+=" -DOPENDHT_PUSH_NOTIFICATIONS=false"
])
AC_ARG_ENABLE([proxy_server_identity], AS_HELP_STRING([--enable-proxy-server-identity],
[Enable proxy server ability]), proxy_server_identity=yes, proxy_server_identity=no)
AM_CONDITIONAL(ENABLE_PROXY_SERVER_IDENTITY, test x$proxy_server_identity == xyes -a x$proxy_server == xyes)
+16
View File
@@ -0,0 +1,16 @@
FROM aberaud/opendht-deps
# install jsoncpp
RUN apt-get install libjsoncpp-dev -y
# install restbed dependencies
RUN sed -i -e 's/archive.ubuntu.com\|security.ubuntu.com/old-releases.ubuntu.com/g' /etc/apt/sources.list # LTS... avoid 404 for libasio-dev
RUN apt-get update -y
RUN apt-get install libasio-dev -y
# build restbed from sources
RUN git clone --recursive https://github.com/corvusoft/restbed.git
WORKDIR restbed/build
RUN cmake -DBUILD_TESTS=NO -DBUILD_EXAMPLES=NO -DBUILD_SSL=NO -DBUILD_SHARED=YES -DCMAKE_INSTALL_PREFIX=/usr ..
RUN make -j 8 install
RUN mv /usr/library/librestbed* /usr/lib/
-6
View File
@@ -1,6 +0,0 @@
FROM aberaud/opendht-deps-llvm
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
RUN git clone https://github.com/savoirfairelinux/opendht.git \
&& cd opendht && mkdir build && cd build \
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On && make -j8 && make install \
&& cd ../.. && rm -rf opendht
+1 -2
View File
@@ -2,5 +2,4 @@ FROM aberaud/opendht-deps
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
COPY . /root/opendht
RUN cd /root/opendht && mkdir build && cd build \
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On -DOPENDHT_LTO=On .. && make -j8 && make install \
&& cd ../.. && rm -rf opendht
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On -DOPENDHT_LTO=On .. && make -j8 && make install
+1 -2
View File
@@ -2,5 +2,4 @@ FROM aberaud/opendht-deps-llvm
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
COPY . /root/opendht
RUN cd /root/opendht && mkdir build && cd build \
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On .. && make -j8 && make install \
&& cd ../.. && rm -rf opendht
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On .. && make -j8 && make install
+3
View File
@@ -0,0 +1,3 @@
FROM opendht-deps-proxy
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
COPY . /root/opendht
+2
View File
@@ -24,4 +24,6 @@
#endif
#include "opendht/log.h"
#include "opendht/default_types.h"
#ifdef OPENDHT_INDEXATION
#include "opendht/indexation/pht.h"
#endif
+3 -2
View File
@@ -1,7 +1,8 @@
/*
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -51,7 +52,7 @@ struct OPENDHT_PUBLIC NodeStats {
unsigned table_depth {0};
unsigned getKnownNodes() const { return good_nodes + dubious_nodes; }
std::string toString() const;
#if OPENDHT_PROXY_SERVER
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
/**
* Build a json object from a NodeStats
*/
+1
View File
@@ -387,6 +387,7 @@ struct OPENDHT_PUBLIC Certificate {
/** Read certificate alternative names */
std::vector<std::pair<NameType, std::string>> getAltNames() const;
std::chrono::system_clock::time_point getActivation() const;
std::chrono::system_clock::time_point getExpiration() const;
/**
+8 -9
View File
@@ -1,7 +1,8 @@
/*
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -67,14 +68,9 @@ public:
* Initialise the Dht with two open sockets (for IPv4 and IP6)
* and an ID for the node.
*/
Dht(int s, int s6, Config config);
Dht(const int& s, const int& s6, Config config);
virtual ~Dht();
#if OPENDHT_PROXY_CLIENT
void startProxy(const std::string&) {};
#endif
/**
* Get the ID of the node.
*/
@@ -235,7 +231,7 @@ public:
*
* @return a token to cancel the listener later.
*/
virtual size_t listen(const InfoHash&, GetCallback, Value::Filter&&={}, Where&& w = {});
virtual size_t listen(const InfoHash&, GetCallback, Value::Filter={}, Where w = {});
virtual size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w = {}) {
return listen(key, bindGetCb(cb), std::forward<Value::Filter>(f), std::forward<Where>(w));
}
@@ -294,6 +290,9 @@ public:
std::vector<SockAddr> getPublicAddress(sa_family_t family = 0);
void pushNotificationReceived(const std::map<std::string, std::string>&) {}
void resubscribe(unsigned) {}
private:
/* When performing a search, we search for up to SEARCH_NODES closest nodes
+18 -7
View File
@@ -28,10 +28,6 @@ public:
DhtInterface() = default;
virtual ~DhtInterface() = default;
#if OPENDHT_PROXY_CLIENT
virtual void startProxy(const std::string& host) = 0;
#endif
// [[deprecated]]
using Status = NodeStatus;
// [[deprecated]]
@@ -167,7 +163,7 @@ public:
*
* @return a token to cancel the listener later.
*/
virtual size_t listen(const InfoHash&, GetCallback, Value::Filter&&={}, Where&& w = {}) = 0;
virtual size_t listen(const InfoHash&, GetCallback, Value::Filter={}, Where w = {}) = 0;
virtual size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w = {}) = 0;
virtual bool cancelListen(const InfoHash&, size_t token) = 0;
@@ -214,7 +210,6 @@ public:
virtual std::vector<SockAddr> getPublicAddress(sa_family_t family = 0) = 0;
Logger DHT_LOG;
/**
* Enable or disable logging of DHT internal messages
*/
@@ -228,12 +223,28 @@ public:
/**
* Only print logs related to the given InfoHash (if given), or disable filter (if zeroes).
*/
virtual void setLogFilter(const InfoHash& f) {
virtual void setLogFilter(const InfoHash& f)
{
DHT_LOG.setFilter(f);
}
virtual void setPushNotificationToken(const std::string&) {};
/**
* Call linked callback with a push notification
* @param notification to process
*/
virtual void pushNotificationReceived(const std::map<std::string, std::string>& data) = 0;
/**
* Refresh a listen via a token
* @param token
*/
virtual void resubscribe(unsigned token) = 0;
protected:
bool logFilerEnable_ {};
InfoHash logFiler_ {};
Logger DHT_LOG;
};
} // namespace dht
+65 -30
View File
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2016 Savoir-faire Linux Inc.
* Copyright (C) 2016-2018 Savoir-faire Linux Inc.
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
* Adrien Béraud <adrien.beraud@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,11 +30,14 @@
#include "dht_interface.h"
#include "scheduler.h"
namespace restbed
{
namespace restbed {
class Request;
}
namespace Json {
class Value;
}
namespace dht {
class OPENDHT_PUBLIC DhtProxyClient final : public DhtInterface {
@@ -41,16 +45,13 @@ public:
DhtProxyClient() : scheduler(DHT_LOG) {}
/**
* Initialise the DhtProxyClient with two open sockets (for IPv4 and IP6)
* and an ID for the node.
*/
explicit DhtProxyClient(const std::string& serverHost);
/**
* Start the connection with a server.
* @param serverHost the server address
*/
void startProxy(const std::string& serverHost);
explicit DhtProxyClient(std::function<void()> loopSignal, const std::string& serverHost, const std::string& pushClientId = "");
virtual void setPushNotificationToken(const std::string& token) {
#if OPENDHT_PUSH_NOTIFICATIONS
deviceKey_ = token;
#endif
}
virtual ~DhtProxyClient();
@@ -101,6 +102,8 @@ public:
get(key, bindGetCb(cb), bindDoneCb(donecb), std::forward<Value::Filter>(f), std::forward<Where>(w));
}
void get(const InfoHash& key, const GetCallback& cb, DoneCallback donecb, const Value::Filter& filterChain);
/**
* Announce a value on all available protocols (IPv4, IPv6).
*
@@ -158,22 +161,28 @@ public:
*
* @return a token to cancel the listener later.
*/
virtual size_t listen(const InfoHash&, GetCallback, Value::Filter&&={}, Where&&={});
virtual size_t listen(const InfoHash&, GetCallback, Value::Filter={}, Where={});
virtual size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w = {}) {
return listen(key, bindGetCb(cb), std::forward<Value::Filter>(f), std::forward<Where>(w));
}
virtual bool cancelListen(const InfoHash&, size_t token);
/**
* Call linked callback with a push notification
* @param notification to process
*/
void pushNotificationReceived(const std::map<std::string, std::string>& notification);
/**
* Refresh a listen via a token
* @param token
*/
void resubscribe(const unsigned token);
time_point periodic(const uint8_t*, size_t, const SockAddr&);
time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen) {
return periodic(buf, buflen, SockAddr(from, fromlen));
}
/**
* TODO
* NOTE: For now, there is no endpoint in the DhtProxyServer to do the following methods.
* It will come in another version. (with push_notifications support)
*/
virtual bool cancelListen(const InfoHash&, size_t token);
/**
* Similar to Dht::get, but sends a Query to filter data remotely.
@@ -236,11 +245,22 @@ public:
private:
const ValueType NO_VALUE;
/**
* Start the connection with a server.
*/
void startProxy();
/**
* Get informations from the proxy node
* @return the JSON returned by the proxy
*/
Json::Value getProxyInfos() const;
void getProxyInfos();
void onProxyInfos(const Json::Value& val);
SockAddr parsePublicAddress(const Json::Value& val);
void opFailed();
/**
* Initialize statusIpvX_
*/
@@ -254,8 +274,14 @@ private:
*/
void cancelAllOperations();
std::string serverHost_;
std::string pushClientId_;
std::atomic_flag ongoingStatusUpdate_ = ATOMIC_FLAG_INIT;
NodeStatus statusIpv4_ {NodeStatus::Disconnected};
NodeStatus statusIpv6_ {NodeStatus::Disconnected};
NodeStats stats4_ {};
NodeStats stats6_ {};
SockAddr publicAddress_;
InfoHash myid {};
@@ -270,6 +296,8 @@ private:
GetCallback cb;
Value::Filter filterChain;
std::thread thread;
unsigned callbackId;
std::shared_ptr<unsigned> pushNotifToken; // NOTE: unused if not using push notifications
};
std::vector<Listener> listeners_;
size_t listener_token_ {0};
@@ -282,7 +310,7 @@ private:
{
std::shared_ptr<restbed::Request> req;
std::thread thread;
std::shared_ptr<bool> finished;
std::shared_ptr<std::atomic_bool> finished;
};
std::vector<Operation> operations_;
std::mutex lockOperations_;
@@ -291,7 +319,9 @@ private:
*/
std::vector<std::function<void()>> callbacks_;
std::mutex lockCallbacks;
std::unique_ptr<std::mutex> lockCurrentProxyInfos_;
std::thread statusThread_;
mutable std::mutex lockCurrentProxyInfos_;
Scheduler scheduler;
/**
@@ -299,20 +329,25 @@ private:
*/
void confirmProxy();
Sp<Scheduler::Job> nextProxyConfirmation {};
/**
* Verify if we are still connected.
*/
void confirmConnectivity();
Sp<Scheduler::Job> nextConnectivityConfirmation {};
/**
* Relaunch LISTEN requests if the client disconnect/reconnect.
*/
void restartListeners();
/**
* Store the current proxy status
* If we want to use push notifications by default.
* NOTE: empty by default to avoid to use services like FCM or APN.
*/
std::unique_ptr<Json::Value> currentProxyInfos_;
std::string deviceKey_ {};
unsigned callbackId_ {0};
std::mutex lockCallback_;
const std::function<void()> loopSignal_;
#if OPENDHT_PUSH_NOTIFICATIONS
void fillBodyToGetToken(std::shared_ptr<restbed::Request> request, unsigned callbackId);
#endif // OPENDHT_PUSH_NOTIFICATIONS
};
}
+72 -5
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2017 Savoir-faire Linux Inc.
* Copyright (C) 2017-2018 Savoir-faire Linux Inc.
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -29,6 +29,10 @@
#include <mutex>
#include <restbed>
namespace Json {
class Value;
}
namespace dht {
class DhtRunner;
@@ -43,10 +47,11 @@ public:
* Start the Http server for OpenDHT
* @param dht the DhtRunner linked to this proxy server
* @param port to listen
* @param pushServer where to push notifications
* @note if the server fails to start (if port is already used or reserved),
* it will fails silently
*/
DhtProxyServer(std::shared_ptr<DhtRunner> dht, in_port_t port = 8000);
DhtProxyServer(std::shared_ptr<DhtRunner> dht, in_port_t port = 8000, const std::string& pushServer = "");
virtual ~DhtProxyServer();
DhtProxyServer(const DhtProxyServer& other) = delete;
@@ -70,7 +75,7 @@ private:
void getNodeInfo(const std::shared_ptr<restbed::Session>& session) const;
/**
* Return Values of a InfoHash
* Return Values of an infoHash
* Method: GET "/{InfoHash: .*}"
* Return: Multiple JSON object in parts. Example:
* Value in JSON format\n
@@ -82,7 +87,7 @@ private:
void get(const std::shared_ptr<restbed::Session>& session) const;
/**
* Listen incoming Values of a InfoHash.
* Listen incoming Values of an infoHash.
* Method: LISTEN "/{InfoHash: .*}"
* Return: Multiple JSON object in parts. Example:
* Value in JSON format\n
@@ -129,7 +134,7 @@ private:
#endif // OPENDHT_PROXY_SERVER_IDENTITY
/**
* Return Values of a InfoHash filtered by a value id
* Return Values of an infoHash filtered by a value id
* Method: GET "/{InfoHash: .*}/{ValueId: .*}"
* Return: Multiple JSON object in parts. Example:
* Value in JSON format\n
@@ -145,9 +150,43 @@ private:
* Method: OPTIONS "/{hash: .*}"
* Return: HTTP 200 + Allow: allowed methods
* See https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS
* @param session
*/
void handleOptionsMethod(const std::shared_ptr<restbed::Session>& session) const;
#if OPENDHT_PUSH_NOTIFICATIONS
/**
* Subscribe to push notifications for an iOS or Android device.
* Method: SUBSCRIBE "/{InfoHash: .*}"
* Body: {"key": "device_key", (optional)"callback_id":y,
* (optional)"isAndroid":false (default true)}"
* Return: {"token": x}" where x if a token to save
* @note: the listen will timeout after six hours (and send a push notification).
* so you need to refresh the operation each six hours.
* @note: callback_id is used to add the possibility to have multiple listen
* on same hash for same device and must be > 0
* @param session
*/
void subscribe(const std::shared_ptr<restbed::Session>& session) const;
/**
* Unsubscribe to push notifications for an iOS or Android device.
* Method: UNSUBSCRIBE "/{InfoHash: .*}"
* Body: {"key": "device_key", "token": x, (optional)"callback_id":y"
* where x if the token to cancel
* Return: nothing
* @note: callback id is used to add the possibility to have multiple listen
* on same hash for same device
* @param session
*/
void unsubscribe(const std::shared_ptr<restbed::Session>& session) const;
/**
* Send a push notification via a gorush push gateway
* @param key of the device
* @param json, the content to send
*/
void sendPushNotification(const std::string& key, const Json::Value& json, bool isAndroid) const;
#endif //OPENDHT_PUSH_NOTIFICATIONS
std::thread server_thread {};
std::unique_ptr<restbed::Service> service_;
std::shared_ptr<DhtRunner> dht_;
@@ -163,6 +202,34 @@ private:
mutable std::vector<SessionToHashToken> currentListeners_;
mutable std::mutex lockListener_;
std::atomic_bool stopListeners {false};
#if OPENDHT_PUSH_NOTIFICATIONS
struct PushListener {
std::string pushToken;
InfoHash hash;
unsigned token;
std::future<size_t> internalToken;
std::chrono::steady_clock::time_point deadline;
bool started {false};
unsigned callbackId {0};
std::string clientId {};
bool isAndroid {true};
};
mutable std::mutex lockPushListeners_;
mutable std::vector<PushListener> pushListeners_;
mutable unsigned tokenPushNotif_ {0};
#endif //OPENDHT_PUSH_NOTIFICATIONS
const std::string pushServer_;
/**
* Remove finished listeners
* @param testSession if we remove the listener only if the session is closed
*/
void removeClosedListeners(bool testSession = true);
/**
* Launch or remove push listeners if needed
*/
void handlePushListeners();
};
}
+74 -47
View File
@@ -1,7 +1,8 @@
/*
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,7 +20,6 @@
#pragma once
//#include "securedht.h"
#include "infohash.h"
#include "value.h"
#include "callbacks.h"
@@ -36,10 +36,6 @@
#include <queue>
#include <chrono>
#if OPENDHT_PROXY_CLIENT
#include "dht_proxy_client.h"
#endif // OPENDHT_PROXY_CLIENT
namespace dht {
struct Node;
@@ -57,6 +53,13 @@ class OPENDHT_PUBLIC DhtRunner {
public:
typedef std::function<void(NodeStatus, NodeStatus)> StatusCallback;
struct Config {
SecureDhtConfig dht_config;
bool threaded;
std::string proxy_server;
std::string push_node_id;
};
DhtRunner();
virtual ~DhtRunner();
@@ -298,25 +301,13 @@ public:
void registerCertificate(std::shared_ptr<crypto::Certificate> cert);
void setLocalCertificateStore(CertificateStoreQuery&& query_method);
struct Config {
SecureDhtConfig dht_config;
bool threaded;
#if OPENDHT_PROXY_CLIENT
std::string proxy_server;
#endif //OPENDHT_PROXY_CLIENT
};
/**
* @param port: Local port to bind. Both IPv4 and IPv6 will be tried (ANY).
* @param identity: RSA key pair to use for cryptographic operations.
* @param threaded: If false, ::loop() must be called periodically. Otherwise a thread is launched.
* @param cb: Optional callback to receive general state information.
*/
void run(in_port_t port, const crypto::Identity identity, bool threaded = false, NetId network = 0
#if OPENDHT_PROXY_CLIENT
, const std::string& proxy_server = "127.0.0.1:8000"
#endif //OPENDHT_PROXY_CLIENT
) {
void run(in_port_t port, const crypto::Identity identity, bool threaded = false, NetId network = 0) {
run(port, {
/*.dht_config = */{
/*.node_config = */{
@@ -328,9 +319,8 @@ public:
/*.id = */identity
},
/*.threaded = */threaded,
#if OPENDHT_PROXY_CLIENT
/*.proxy_server = */proxy_server
#endif //OPENDHT_PROXY_CLIENT
/*.proxy_server = */"",
/*.push_node_id = */""
});
}
void run(in_port_t port, Config config);
@@ -361,7 +351,13 @@ public:
*/
time_point loop() {
std::lock_guard<std::mutex> lck(dht_mtx);
return loop_();
time_point wakeup = time_point::min();
try {
wakeup = loop_();
} catch (const dht::SocketException& e) {
startNetwork(bound4, bound6);
}
return wakeup;
}
/**
@@ -376,17 +372,43 @@ public:
*/
void join();
void setProxyServer(const std::string& proxy, const std::string& pushNodeId = "") {
#if OPENDHT_PROXY_CLIENT
void setProxyServer(const std::string& url = "127.0.0.1:8000") {
config_.proxy_server = url;
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 enableProxy(bool proxify);
#endif // OPENDHT_PROXY_CLIENT
#if OPENDHT_PROXY_SERVER
void forwardAllMessages(bool forward);
#endif // OPENDHT_PROXY_SERVER
static std::vector<SockAddr> getAddrInfo(const std::string& host, const std::string& service);
/**
* Start or stop the proxy
* @param proxify if we want to use the proxy
* @param deviceKey non empty to enable push notifications
*/
void enableProxy(bool proxify);
/* Push notification methods */
/**
* Updates the push notification device token
*/
void setPushNotificationToken(const std::string& token);
/**
* Insert a push notification to process for OpenDHT
*/
void pushNotificationReceived(const std::map<std::string, std::string>& data) const;
/**
* Refresh a listen via a token
* @param token
*/
void resubscribe(unsigned token);
/* Proxy server mothods */
void forwardAllMessages(bool forward);
private:
static constexpr std::chrono::seconds BOOTSTRAP_PERIOD {10};
@@ -398,14 +420,25 @@ private:
*/
void tryBootstrapContinuously();
void doRun(const SockAddr& sin4, const SockAddr& sin6, SecureDhtConfig config);
void startNetwork(const SockAddr sin4, const SockAddr sin6);
time_point loop_();
NodeStatus getStatus() const {
return std::max(status4, status6);
}
/** Local DHT instance */
std::unique_ptr<SecureDht> dht_;
/** Proxy client instance */
std::unique_ptr<SecureDht> dht_via_proxy_;
/** true if we are currently using a proxy */
std::atomic_bool use_proxy {false};
/** Current configuration */
Config config_;
/**
* reset dht clients
*/
@@ -414,22 +447,11 @@ private:
* @return the current active DHT
*/
SecureDht* activeDht() const;
#if OPENDHT_PROXY_CLIENT
/**
* true if we are currently using a proxy
*/
std::atomic_bool use_proxy {false};
/**
* The current proxy client
*/
std::unique_ptr<SecureDht> dht_via_proxy_;
Config config_;
#endif // OPENDHT_PROXY_CLIENT
/**
* Store current listeners and translates global tokens for each client.
*/
struct Listener {
size_t globalToken;
size_t tokenClassicDht;
size_t tokenProxyDht;
GetCallback gcb;
@@ -437,7 +459,7 @@ private:
Value::Filter f;
Where w;
};
std::vector<std::unique_ptr<Listener>> listeners_ {};
std::map<size_t, Listener> listeners_ {};
size_t listener_token_ {1};
mutable std::mutex dht_mtx {};
@@ -462,14 +484,19 @@ private:
std::queue<std::function<void(SecureDht&)>> pending_ops {};
std::mutex storage_mtx {};
std::atomic<bool> running {false};
std::atomic_bool running {false};
std::atomic_bool running_network {false};
NodeStatus status4 {NodeStatus::Disconnected},
status6 {NodeStatus::Disconnected};
StatusCallback statusCb {nullptr};
int s4 {-1}, s6 {-1};
SockAddr bound4 {};
SockAddr bound6 {};
/** Push notification token */
std::string pushToken_;
};
}
+1 -1
View File
@@ -378,7 +378,7 @@ struct OPENDHT_PUBLIC NodeExport {
void msgpack_unpack(msgpack::object o);
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const InfoHash& h);
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const NodeExport& h);
};
}
+4 -4
View File
@@ -202,8 +202,8 @@ public:
using RequestCb = std::function<void(const Request&, RequestAnswer&&)>;
using RequestExpiredCb = std::function<void(const Request&, bool)>;
NetworkEngine(Logger& log, Scheduler& scheduler);
NetworkEngine(InfoHash& myid, NetId net, int s, int s6, Logger& log, Scheduler& scheduler,
NetworkEngine(Logger& log, Scheduler& scheduler, const int& s = -1, const int& s6 = -1);
NetworkEngine(InfoHash& myid, NetId net, const int& s, const int& s6, Logger& log, Scheduler& scheduler,
decltype(NetworkEngine::onError) onError,
decltype(NetworkEngine::onNewNode) onNewNode,
decltype(NetworkEngine::onReportedAddr) onReportedAddr,
@@ -505,8 +505,8 @@ private:
/* DHT info */
const InfoHash& myid;
const NetId network {0};
const int dht_socket {-1};
const int dht_socket6 {-1};
const int& dht_socket;
const int& dht_socket6;
const Logger& DHT_LOG;
NodeCache cache {};
+2 -2
View File
@@ -117,7 +117,7 @@ struct Node {
*/
Tid openSocket(SocketCb&& cb);
Socket* getSocket(Tid id);
Sp<Socket> getSocket(Tid id);
/**
* Closes a socket so that no further data will be red on that socket.
@@ -167,7 +167,7 @@ private:
using TransactionDist = std::uniform_int_distribution<decltype(transaction_id)>;
std::map<Tid, Sp<net::Request>> requests_ {};
std::map<Tid, Socket> sockets_;
std::map<Tid, Sp<Socket>> sockets_;
};
}
+38 -12
View File
@@ -1,7 +1,8 @@
/*
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -99,7 +100,7 @@ public:
get(key, bindGetCb(cb), bindDoneCb(donecb), std::forward<Value::Filter>(f), std::forward<Where>(w));
}
size_t listen(const InfoHash& id, GetCallback cb, Value::Filter&& = {}, Where&& w = {});
size_t listen(const InfoHash& id, GetCallback cb, Value::Filter = {}, Where w = {});
/**
* Will take ownership of the value, sign it using our private key and put it in the DHT.
@@ -292,17 +293,44 @@ public:
dht_->connectivityChanged();
}
#if OPENDHT_PROXY_CLIENT
void startProxy(const std::string& host) {
dht_->startProxy(host);
}
#endif
#if OPENDHT_PROXY_SERVER
void forwardAllMessages(bool forward) {
forward_all_ = forward;
}
#endif //OPENDHT_PROXY_SERVER
void setPushNotificationToken(const std::string& token = "") {
dht_->setPushNotificationToken(token);
}
/**
* Call linked callback with push_notification
* @param notification to process
*/
void pushNotificationReceived(const std::map<std::string, std::string>& notification) {
dht_->pushNotificationReceived(notification);
}
/**
* Refresh a listen via a token
* @param token
*/
void resubscribe(const unsigned token) {
dht_->resubscribe(token);
}
void setLoggers(LogMethod error = NOLOG, LogMethod warn = NOLOG, LogMethod debug = NOLOG)
{
DHT_LOG.DEBUG = debug;
DHT_LOG.WARN = warn;
DHT_LOG.ERR = error;
dht_->setLoggers(std::forward<LogMethod>(error), std::forward<LogMethod>(warn), std::forward<LogMethod>(debug));
}
/**
* Only print logs related to the given InfoHash (if given), or disable filter (if zeroes).
*/
void setLogFilter(const InfoHash& f) {
DHT_LOG.setFilter(f);
dht_->setLogFilter(f);
}
private:
std::unique_ptr<DhtInterface> dht_;
@@ -324,9 +352,7 @@ private:
std::uniform_int_distribution<Value::Id> rand_id {};
#if OPENDHT_PROXY_SERVER
std::atomic_bool forward_all_ {false};
#endif //OPENDHT_PROXY_SERVER
};
const ValueType CERTIFICATE_TYPE = {
+22 -3
View File
@@ -28,6 +28,8 @@ typedef uint16_t in_port_t;
#endif
#else
#include <iso646.h>
#include <stdint.h>
#include <winsock2.h>
#include <ws2def.h>
#include <ws2tcpip.h>
typedef uint16_t sa_family_t;
@@ -37,6 +39,7 @@ typedef uint16_t in_port_t;
#include <string>
#include <memory>
#include <vector>
#include <stdlib.h>
#include <cstring>
#include <cstddef>
@@ -67,6 +70,19 @@ public:
throw std::runtime_error("Socket address length is too large");
set(sa, length);
}
SockAddr(const sockaddr* sa) {
socklen_t len = 0;
if (sa) {
if (sa->sa_family == AF_INET)
len = sizeof(sockaddr_in);
else if(sa->sa_family == AF_INET6)
len = sizeof(sockaddr_in6);
else
throw std::runtime_error("Unknown address family");
}
set(sa, len);
}
/**
* Build from an existing sockaddr_storage structure.
*/
@@ -123,7 +139,7 @@ public:
}
if (new_length != len) {
len = new_length;
if (len) addr.reset((sockaddr*)std::calloc(len, 1));
if (len) addr.reset((sockaddr*)::calloc(len, 1));
else addr.reset();
}
if (len > sizeof(sa_family_t))
@@ -209,6 +225,9 @@ public:
bool isUnspecified() const;
bool isMappedIPv4() const;
SockAddr getMappedIPv4() const;
/**
* A comparator to classify IP addresses, only considering the
* first 64 bits in IPv6.
@@ -239,13 +258,13 @@ public:
};
private:
socklen_t len {0};
struct free_delete { void operator()(void* p) { std::free(p); } };
struct free_delete { void operator()(void* p) { ::free(p); } };
std::unique_ptr<sockaddr, free_delete> addr {};
void set(const sockaddr* sa, socklen_t length) {
if (len != length) {
len = length;
if (len) addr.reset((sockaddr*)std::malloc(len));
if (len) addr.reset((sockaddr*)::malloc(len));
else addr.reset();
}
if (len)
+9 -3
View File
@@ -55,9 +55,15 @@ void erase_if(std::map<Key, Item>& map, const Condition& condition)
}
class OPENDHT_PUBLIC DhtException : public std::runtime_error {
public:
DhtException(const std::string &str = "") :
std::runtime_error("DhtException occurred: " + str) {}
public:
DhtException(const std::string &str = "") :
std::runtime_error("DhtException occurred: " + str) {}
};
class OPENDHT_PUBLIC SocketException : public DhtException {
public:
SocketException(int err) :
DhtException(strerror(err)) {}
};
// Time related definitions and utility functions
+3 -3
View File
@@ -37,7 +37,7 @@
#include <chrono>
#include <set>
#if OPENDHT_PROXY_SERVER
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
#include <json/json.h>
#endif //OPENDHT_PROXY_SERVER
@@ -350,7 +350,7 @@ 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
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
/**
* Build a value from a json object
* @param json
@@ -429,7 +429,7 @@ struct OPENDHT_PUBLIC Value
return ss.str();
}
#if OPENDHT_PROXY_SERVER
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
/**
* Build a json object from a value
* Example:
+11 -3
View File
@@ -23,7 +23,6 @@ libopendht_la_SOURCES = \
securedht.cpp \
dhtrunner.cpp \
default_types.cpp \
indexation/pht.cpp \
log.cpp
if WIN32
@@ -51,14 +50,23 @@ nobase_include_HEADERS = \
../include/opendht/default_types.h \
../include/opendht/log.h \
../include/opendht/log_enable.h \
../include/opendht/rng.h \
../include/opendht/indexation/pht.h
../include/opendht/rng.h
if ENABLE_PROXY_SERVER
libopendht_la_SOURCES += base64.h base64.cpp dht_proxy_server.cpp
nobase_include_HEADERS += ../include/opendht/dht_proxy_server.h
endif
if ENABLE_PROXY_CLIENT
libopendht_la_SOURCES += dht_proxy_client.cpp
nobase_include_HEADERS += ../include/opendht/dht_proxy_client.h ../include/opendht/dht_interface.h
endif
if ENABLE_INDEXATION
libopendht_la_SOURCES += indexation/pht.cpp
nobase_include_HEADERS += ../include/opendht/indexation/pht.h
endif
clean-local:
rm -rf libargon2.la
+1 -1
View File
@@ -68,7 +68,7 @@ NodeStats::toString() const
return ss.str();
}
#if OPENDHT_PROXY_SERVER
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
/**
* Build a json object from a NodeStats
*/
+22 -2
View File
@@ -538,7 +538,11 @@ PublicKey::getId() const
return {};
InfoHash id;
size_t sz = id.size();
#if GNUTLS_VERSION_NUMBER < 0x030401
const int flags = 0;
#else
const int flags = (id.size() == 32) ? GNUTLS_KEYID_USE_SHA256 : 0;
#endif
if (auto err = gnutls_pubkey_get_key_id(pk, flags, id.data(), &sz))
throw CryptoException(std::string("Can't get public key ID: ") + gnutls_strerror(err));
if (sz != id.size())
@@ -841,6 +845,15 @@ Certificate::addRevocationList(std::shared_ptr<RevocationList> list)
revocation_lists.emplace(std::move(list));
}
std::chrono::system_clock::time_point
Certificate::getActivation() const
{
auto t = gnutls_x509_crt_get_activation_time(cert);
if (t == (time_t)-1)
return std::chrono::system_clock::time_point::min();
return std::chrono::system_clock::from_time_t(t);
}
std::chrono::system_clock::time_point
Certificate::getExpiration() const
{
@@ -913,8 +926,13 @@ Certificate::generate(const PrivateKey& key, const std::string& name, Identity c
return {};
Certificate ret {cert};
gnutls_x509_crt_set_activation_time(cert, time(NULL));
gnutls_x509_crt_set_expiration_time(cert, time(NULL) + (20 * 365 * 24 * 60 * 60));
int64_t now = time(NULL);
/* 2038 bug: don't allow time wrap */
auto boundTime = [](int64_t t) -> time_t {
return std::min<int64_t>(t, std::numeric_limits<time_t>::max());
};
gnutls_x509_crt_set_activation_time(cert, boundTime(now));
gnutls_x509_crt_set_expiration_time(cert, boundTime(now + (10 * 365 * 24 * 60 * 60)));
if (gnutls_x509_crt_set_key(cert, key.x509_key) != GNUTLS_E_SUCCESS) {
std::cerr << "Error when setting certificate key" << std::endl;
return {};
@@ -1287,8 +1305,10 @@ std::ostream& operator<< (std::ostream& o, const TrustList::VerifyResult& h)
o << "* Certificate has expired" << std::endl;
if (h.result & GNUTLS_CERT_UNEXPECTED_OWNER)
o << "* The owner is not the expected one" << std::endl;
#if GNUTLS_VERSION_NUMBER >= 0x030401
if (h.result & GNUTLS_CERT_PURPOSE_MISMATCH)
o << "* Certificate or an intermediate does not match the intended purpose" << std::endl;
#endif
if (h.result & GNUTLS_CERT_MISMATCH)
o << "* Certificate presented isn't the expected one" << std::endl;
} else {
+48 -44
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
* Copyright (C) 2014-2018 Savoir-faire Linux Inc.
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
*
@@ -130,7 +130,7 @@ Dht::trySearchInsert(const Sp<Node>& node)
auto& s = *it->second;
if (s.insertNode(node, now)) {
inserted = true;
scheduler.edit(s.nextSearchStep, s.getNextStepTime(now));
scheduler.edit(s.nextSearchStep, now);
} else if (not s.expired and not s.done)
break;
++it;
@@ -142,7 +142,7 @@ Dht::trySearchInsert(const Sp<Node>& node)
auto& s = *it->second;
if (s.insertNode(node, now)) {
inserted = true;
scheduler.edit(s.nextSearchStep, s.getNextStepTime(now));
scheduler.edit(s.nextSearchStep, now);
} else if (not s.expired and not s.done)
break;
}
@@ -217,6 +217,7 @@ Dht::searchNodeGetDone(const net::Request& req,
{
const auto& now = scheduler.time();
if (auto sr = ws.lock()) {
sr->insertNode(req.node, now, answer.ntoken);
if (auto srn = sr->getNode(req.node)) {
/* all other get requests which are satisfied by this answer
should not be sent anymore */
@@ -228,8 +229,12 @@ Dht::searchNodeGetDone(const net::Request& req,
srn->getStatus[q] = std::move(dummy_req);
}
}
auto syncTime = srn->getSyncTime(scheduler.time());
if (srn->syncJob)
scheduler.edit(srn->syncJob, syncTime);
else
srn->syncJob = scheduler.add(syncTime, std::bind(&Dht::searchStep, this, sr));
}
sr->insertNode(req.node, now, answer.ntoken);
onGetValuesDone(req.node, answer, sr, query);
}
}
@@ -336,8 +341,8 @@ Dht::searchSendGetValues(Sp<Search> sr, SearchNode* pn, bool update)
if (not n)
return nullptr;
DHT_LOG.w(sr->id, n->node->id, "[search %s] [node %s] sending 'find_node'",
sr->id.toString().c_str(), n->node->toString().c_str());
/*DHT_LOG.d(sr->id, n->node->id, "[search %s] [node %s] sending 'find_node'",
sr->id.toString().c_str(), n->node->toString().c_str());*/
n->getStatus[query] = network_engine.sendFindNode(n->node,
sr->id,
-1,
@@ -350,8 +355,8 @@ Dht::searchSendGetValues(Sp<Search> sr, SearchNode* pn, bool update)
if (query and not query->select.getSelection().empty()) {
/* The request contains a select. No need to paginate... */
DHT_LOG.w(sr->id, n->node->id, "[search %s] [node %s] sending 'get'",
sr->id.toString().c_str(), n->node->toString().c_str());
/*DHT_LOG.d(sr->id, n->node->id, "[search %s] [node %s] sending 'get'",
sr->id.toString().c_str(), n->node->toString().c_str());*/
n->getStatus[query] = network_engine.sendGetValues(n->node,
sr->id,
*query,
@@ -411,7 +416,7 @@ void Dht::searchSendAnnounceValue(const Sp<Search>& sr) {
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, sr->getNextStepTime(now));
scheduler.edit(sr->nextSearchStep, now);
return;
}
for (auto& a : sr->announce) {
@@ -549,6 +554,8 @@ Dht::searchStep(Sp<Search> sr)
{ /* on done */
if (auto sr = ws.lock()) {
scheduler.edit(sr->nextSearchStep, scheduler.time());
if (auto sn = sr->getNode(req.node))
scheduler.add(sn->getListenTime(query), std::bind(&Dht::searchStep, this, sr));
onListenDone(req.node, answer, sr);
}
},
@@ -606,8 +613,8 @@ Dht::searchStep(Sp<Search> sr)
/* dumpSearch(*sr, std::cout); */
/* periodic searchStep scheduling. */
if (not sr->done)
scheduler.edit(sr->nextSearchStep, sr->getNextStepTime(now));
//if (not sr->done)
// scheduler.edit(sr->nextSearchStep, now);
}
unsigned Dht::refill(Dht::Search& sr) {
@@ -695,7 +702,7 @@ Dht::search(const InfoHash& id, sa_family_t af, GetCallback gcb, QueryCallback q
refill(*sr);
if (sr->nextSearchStep)
scheduler.edit(sr->nextSearchStep, sr->getNextStepTime(scheduler.time()));
scheduler.edit(sr->nextSearchStep, scheduler.time());
else
sr->nextSearchStep = scheduler.add(scheduler.time(), std::bind(&Dht::searchStep, this, sr));
@@ -782,12 +789,12 @@ Dht::listenTo(const InfoHash& id, sa_family_t af, GetCallback cb, Value::Filter
sr->done = false;
auto token = ++sr->listener_token;
sr->listeners.emplace(token, LocalListener{q, f, cb});
scheduler.edit(sr->nextSearchStep, sr->getNextStepTime(now));
scheduler.edit(sr->nextSearchStep, now);
return token;
}
size_t
Dht::listen(const InfoHash& id, GetCallback cb, Value::Filter&& f, Where&& where)
Dht::listen(const InfoHash& id, GetCallback cb, Value::Filter f, Where where)
{
scheduler.syncTime();
@@ -1130,7 +1137,6 @@ Dht::cancelPut(const InfoHash& id, const Value::Id& vid)
void
Dht::storageChanged(const InfoHash& id, Storage& st, ValueStorage& v)
{
DHT_LOG.d(id, "[store %s] changed", id.toString().c_str());
if (not st.local_listeners.empty()) {
DHT_LOG.d(id, "[store %s] %lu local listeners", id.toString().c_str(), st.local_listeners.size());
std::vector<std::pair<GetCallback, std::vector<Sp<Value>>>> cbs;
@@ -1150,20 +1156,22 @@ Dht::storageChanged(const InfoHash& id, Storage& st, ValueStorage& v)
cb.first(cb.second);
}
DHT_LOG.d(id, "[store %s] %lu remote listeners", id.toString().c_str(), st.listeners.size());
for (const auto& node_listeners : st.listeners) {
for (const auto& l : node_listeners.second) {
auto f = l.second.query.where.getFilter();
if (f and not f(*v.data))
continue;
DHT_LOG.w(id, node_listeners.first->id, "[store %s] [node %s] sending update",
id.toString().c_str(),
node_listeners.first->toString().c_str());
std::vector<Sp<Value>> vals {};
vals.push_back(v.data);
Blob ntoken = makeToken(node_listeners.first->getAddr(), false);
network_engine.tellListener(node_listeners.first, l.second.sid, id, 0, ntoken, {}, {},
std::move(vals), l.second.query);
if (not st.listeners.empty()) {
DHT_LOG.d(id, "[store %s] %lu remote listeners", id.toString().c_str(), st.listeners.size());
for (const auto& node_listeners : st.listeners) {
for (const auto& l : node_listeners.second) {
auto f = l.second.query.where.getFilter();
if (f and not f(*v.data))
continue;
DHT_LOG.w(id, node_listeners.first->id, "[store %s] [node %s] sending update",
id.toString().c_str(),
node_listeners.first->toString().c_str());
std::vector<Sp<Value>> vals {};
vals.push_back(v.data);
Blob ntoken = makeToken(node_listeners.first->getAddr(), false);
network_engine.tellListener(node_listeners.first, l.first, id, 0, ntoken, {}, {},
std::move(vals), l.second.query);
}
}
}
}
@@ -1224,10 +1232,10 @@ Dht::storageAddListener(const InfoHash& id, const Sp<Node>& node, size_t socket_
buckets4.findClosestNodes(id, now, TARGET_NODES), buckets6.findClosestNodes(id, now, TARGET_NODES),
std::move(vals), query);
}
node_listeners->second.emplace(socket_id, Listener {socket_id, now, std::forward<Query>(query)});
node_listeners->second.emplace(socket_id, Listener {now, std::forward<Query>(query)});
}
else
l->second.refresh(socket_id, now, std::forward<Query>(query));
l->second.refresh(now, std::forward<Query>(query));
}
void
@@ -1434,10 +1442,8 @@ Dht::dumpSearch(const Search& sr, std::ostream& out) const
out << " [expired]";
bool synced = sr.isSynced(now);
out << (synced ? " [synced]" : " [not synced]");
if (synced && sr.isListening(now)) {
auto lt = sr.getListenTime(now);
out << " [listening, next in " << duration_cast<seconds>(lt-now).count() << " s]";
}
if (synced && sr.isListening(now))
out << " [listening]";
out << std::endl;
/*printing the queries*/
@@ -1649,7 +1655,7 @@ Dht::~Dht()
Dht::Dht() : store(), scheduler(DHT_LOG), network_engine(DHT_LOG, scheduler) {}
Dht::Dht(int s, int s6, Config config)
Dht::Dht(const int& s, const int& s6, Config config)
: myid(config.node_id ? config.node_id : InfoHash::getRandom()),
is_bootstrap(config.is_bootstrap),
maintain_storage(config.maintain_storage), store(), store_quota(),
@@ -2059,6 +2065,7 @@ Dht::onError(Sp<net::Request> req, net::DhtProtocolException e) {
n.token.clear();
n.last_get_reply = time_point::min();
searchSendGetValues(sr);
scheduler.edit(sr->nextSearchStep, scheduler.time());
break;
}
}
@@ -2113,8 +2120,6 @@ Dht::onGetValues(Sp<Node> node, const InfoHash& hash, want_t, const Query& query
if (st != store.end() && not st->second.empty()) {
answer.values = st->second.get(query.where.getFilter());
DHT_LOG.d(hash, "[node %s] sending %u values", node->toString().c_str(), answer.values.size());
} else {
DHT_LOG.d(hash, "[node %s] sending nodes", node->toString().c_str());
}
return answer;
}
@@ -2129,14 +2134,13 @@ void Dht::onGetValuesDone(const Sp<Node>& node,
return;
}
DHT_LOG.d(sr->id, "[search %s] [node %s] got reply to 'get' with %u nodes",
sr->id.toString().c_str(), node->toString().c_str(), a.nodes4.size()+a.nodes6.size());
/*DHT_LOG.d(sr->id, "[search %s] [node %s] got reply to 'get' with %u nodes",
sr->id.toString().c_str(), node->toString().c_str(), a.nodes4.size()+a.nodes6.size());*/
if (not a.ntoken.empty()) {
if (not a.values.empty() or not a.fields.empty()) {
DHT_LOG.d(sr->id, "[search %s IPv%c] found %u values",
sr->id.toString().c_str(), sr->af == AF_INET ? '4' : '6',
a.values.size());
DHT_LOG.d(sr->id, node->id, "[search %s] [node %s] found %u values",
sr->id.toString().c_str(), node->toString().c_str(), a.values.size());
for (auto& getp : sr->callbacks) { /* call all callbacks for this search */
auto& get = getp.second;
if (not (get.get_cb or get.query_cb) or
@@ -2221,7 +2225,7 @@ Dht::onListenDone(const Sp<Node>& node,
if (not sr->done) {
const auto& now = scheduler.time();
searchSendGetValues(sr);
scheduler.edit(sr->nextSearchStep, sr->getNextStepTime(now));
scheduler.edit(sr->nextSearchStep, now);
}
}
+372 -177
View File
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2016 Savoir-faire Linux Inc.
* Copyright (C) 2016-2018 Savoir-faire Linux Inc.
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
* Adrien Béraud <adrien.beraud@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,55 +28,30 @@
#include "dhtrunner.h"
#include <iostream>
constexpr const char* const HTTP_PROTO {"http://"};
namespace dht {
DhtProxyClient::DhtProxyClient(const std::string& serverHost)
: serverHost_(serverHost), lockCurrentProxyInfos_(new std::mutex()),
scheduler(DHT_LOG), currentProxyInfos_(new Json::Value())
DhtProxyClient::DhtProxyClient(std::function<void()> signal, const std::string& serverHost, const std::string& pushClientId)
: serverHost_(serverHost), pushClientId_(pushClientId), scheduler(DHT_LOG), loopSignal_(signal)
{
if (!serverHost_.empty())
startProxy(serverHost_);
startProxy();
}
void
DhtProxyClient::confirmProxy()
{
if (serverHost_.empty()) return;
// Retrieve the connectivity each hours if connected, else every 5 seconds.
auto disconnected_old_status = statusIpv4_ == NodeStatus::Disconnected && statusIpv6_ == NodeStatus::Disconnected;
getConnectivityStatus();
auto disconnected_new_status = statusIpv4_ == NodeStatus::Disconnected && statusIpv6_ == NodeStatus::Disconnected;
auto time = disconnected_new_status ? std::chrono::seconds(5) : std::chrono::hours(1);
if (disconnected_old_status && !disconnected_new_status) {
restartListeners();
}
auto confirm_proxy_time = scheduler.time() + time;
scheduler.edit(nextProxyConfirmation, confirm_proxy_time);
}
void
DhtProxyClient::startProxy(const std::string& serverHost)
DhtProxyClient::startProxy()
{
serverHost_ = serverHost;
if (serverHost_.empty()) return;
auto confirm_proxy_time = scheduler.time() + std::chrono::seconds(5);
nextProxyConfirmation = scheduler.add(confirm_proxy_time, std::bind(&DhtProxyClient::confirmProxy, this));
auto confirm_connectivity = scheduler.time() + std::chrono::seconds(5);
nextConnectivityConfirmation = scheduler.add(confirm_connectivity, std::bind(&DhtProxyClient::confirmConnectivity, this));
getConnectivityStatus();
}
void
DhtProxyClient::confirmConnectivity()
{
// The scheduler must get if the proxy is disconnected
auto confirm_connectivity = scheduler.time() + std::chrono::seconds(3);
scheduler.edit(nextConnectivityConfirmation, confirm_connectivity);
DHT_LOG.WARN("Staring proxy client to %s", serverHost_.c_str());
nextProxyConfirmation = scheduler.add(scheduler.time(), std::bind(&DhtProxyClient::confirmProxy, this));
}
DhtProxyClient::~DhtProxyClient()
@@ -127,6 +103,7 @@ DhtProxyClient::shutdown(ShutdownCallback cb)
NodeStatus
DhtProxyClient::getStatus(sa_family_t af) const
{
std::lock_guard<std::mutex> l(lockCurrentProxyInfos_);
switch (af)
{
case AF_INET:
@@ -141,12 +118,13 @@ DhtProxyClient::getStatus(sa_family_t af) const
bool
DhtProxyClient::isRunning(sa_family_t af) const
{
std::lock_guard<std::mutex> l(lockCurrentProxyInfos_);
switch (af)
{
case AF_INET:
return statusIpv4_ == NodeStatus::Connected;
return statusIpv4_ != NodeStatus::Disconnected;
case AF_INET6:
return statusIpv6_ == NodeStatus::Connected;
return statusIpv6_ != NodeStatus::Disconnected;
default:
return false;
}
@@ -184,22 +162,19 @@ DhtProxyClient::periodic(const uint8_t*, size_t, const SockAddr&)
}
void
DhtProxyClient::get(const InfoHash& key, GetCallback cb, DoneCallback donecb,
Value::Filter&& filter, Where&& where)
DhtProxyClient::get(const InfoHash& key, const GetCallback& cb, DoneCallback donecb, const Value::Filter& filterChain)
{
restbed::Uri uri(HTTP_PROTO + serverHost_ + "/" + key.toString());
auto req = std::make_shared<restbed::Request>(uri);
Query query {{}, where};
auto filterChain = filter.chain(query.where.getFilter());
auto finished = std::make_shared<bool>(false);
auto finished = std::make_shared<std::atomic_bool>(false);
Operation o;
o.req = req;
o.finished = finished;
o.thread = std::thread([=](){
// Try to contact the proxy and set the status to connected when done.
// will change the connectivity status
auto ok = std::make_shared<bool>(true);
auto ok = std::make_shared<std::atomic_bool>(true);
restbed::Http::async(req,
[=](const std::shared_ptr<restbed::Request>& req,
const std::shared_ptr<restbed::Response>& reply) {
@@ -215,16 +190,20 @@ DhtProxyClient::get(const InfoHash& key, GetCallback cb, DoneCallback donecb,
reply->get_body(body);
reply->set_body(""); // Reset the body for the next fetch
std::string err;
Json::Value json;
Json::Reader reader;
if (reader.parse(body, json)) {
Json::CharReaderBuilder rbuilder;
auto* char_data = reinterpret_cast<const char*>(&body[0]);
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
if (reader->parse(char_data, char_data + body.size(), &json, &err)) {
auto value = std::make_shared<Value>(json);
if ((not filterChain or filterChain(*value)) && cb) {
std::lock_guard<std::mutex> lock(lockCallbacks);
callbacks_.emplace_back([cb, value, finished]() {
if (not *finished and not cb({value}))
if (not cb({value}))
*finished = true;
});
loopSignal_();
}
} else {
*ok = false;
@@ -240,10 +219,11 @@ DhtProxyClient::get(const InfoHash& key, GetCallback cb, DoneCallback donecb,
callbacks_.emplace_back([=](){
donecb(*ok, {});
});
loopSignal_();
}
if (!ok) {
// Connection failed, update connectivity
getConnectivityStatus();
opFailed();
}
*finished = true;
});
@@ -253,26 +233,37 @@ DhtProxyClient::get(const InfoHash& key, GetCallback cb, DoneCallback donecb,
}
}
void
DhtProxyClient::get(const InfoHash& key, GetCallback cb, DoneCallback donecb,
Value::Filter&& filter, Where&& where)
{
Query query {{}, where};
auto filterChain = filter.chain(query.where.getFilter());
get(key, cb, donecb, filterChain);
}
void
DhtProxyClient::put(const InfoHash& key, Sp<Value> val, DoneCallback cb, time_point, bool permanent)
{
restbed::Uri uri(HTTP_PROTO + serverHost_ + "/" + key.toString());
auto req = std::make_shared<restbed::Request>(uri);
req->set_method("POST");
Json::FastWriter writer;
auto json = val->toJson();
if (permanent)
json["permanent"] = true;
auto body = writer.write(json);
Json::StreamWriterBuilder wbuilder;
wbuilder["commentStyle"] = "None";
wbuilder["indentation"] = "";
auto body = Json::writeString(wbuilder, json) + "\n";
req->set_body(body);
req->set_header("Content-Length", std::to_string(body.size()));
auto finished = std::make_shared<bool>(false);
auto finished = std::make_shared<std::atomic_bool>(false);
Operation o;
o.req = req;
o.finished = finished;
o.thread = std::thread([=](){
auto ok = std::make_shared<bool>(true);
auto ok = std::make_shared<std::atomic_bool>(true);
restbed::Http::async(req,
[this, ok](const std::shared_ptr<restbed::Request>& /*req*/,
const std::shared_ptr<restbed::Response>& reply) {
@@ -284,10 +275,13 @@ DhtProxyClient::put(const InfoHash& key, Sp<Value> val, DoneCallback cb, time_po
reply->get_body(body);
reply->set_body(""); // Reset the body for the next fetch
Json::Value json;
Json::Reader reader;
try {
if (!reader.parse(body, json))
std::string err;
Json::Value json;
Json::CharReaderBuilder rbuilder;
auto* char_data = reinterpret_cast<const char*>(&body[0]);
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
if (not reader->parse(char_data, char_data + body.size(), &json, &err))
*ok = false;
} catch (...) {
*ok = false;
@@ -301,10 +295,11 @@ DhtProxyClient::put(const InfoHash& key, Sp<Value> val, DoneCallback cb, time_po
callbacks_.emplace_back([=](){
cb(*ok, {});
});
loopSignal_();
}
if (!ok) {
// Connection failed, update connectivity
getConnectivityStatus();
opFailed();
}
*finished = true;
});
@@ -317,100 +312,136 @@ DhtProxyClient::put(const InfoHash& key, Sp<Value> val, DoneCallback cb, time_po
NodeStats
DhtProxyClient::getNodesStats(sa_family_t af) const
{
lockCurrentProxyInfos_->lock();
auto proxyInfos = *currentProxyInfos_;
lockCurrentProxyInfos_->unlock();
NodeStats stats {};
auto identifier = af == AF_INET6 ? "ipv6" : "ipv4";
try {
stats = NodeStats(proxyInfos[identifier]);
} catch (...) { }
return stats;
return af == AF_INET ? stats4_ : stats6_;
}
Json::Value
DhtProxyClient::getProxyInfos() const
void
DhtProxyClient::getProxyInfos()
{
DHT_LOG.DEBUG("Requesting proxy server node information");
if (ongoingStatusUpdate_.test_and_set())
return;
{
std::lock_guard<std::mutex> l(lockCurrentProxyInfos_);
if (statusIpv4_ == NodeStatus::Disconnected)
statusIpv4_ = NodeStatus::Connecting;
if (statusIpv6_ == NodeStatus::Disconnected)
statusIpv6_ = NodeStatus::Connecting;
}
restbed::Uri uri(HTTP_PROTO + serverHost_ + "/");
auto req = std::make_shared<restbed::Request>(uri);
// Try to contact the proxy and set the status to connected when done.
// will change the connectivity status
restbed::Http::async(req,
[this](const std::shared_ptr<restbed::Request>&,
const std::shared_ptr<restbed::Response>& reply) {
auto code = reply->get_status_code();
statusThread_ = std::thread([this, req]{
restbed::Http::async(req,
[this](const std::shared_ptr<restbed::Request>&,
const std::shared_ptr<restbed::Response>& reply) {
auto code = reply->get_status_code();
Json::Value proxyInfos;
if (code == 200) {
restbed::Http::fetch("\n", reply);
std::string body;
reply->get_body(body);
if (code == 200) {
restbed::Http::fetch("\n", reply);
std::string body;
reply->get_body(body);
Json::Reader reader;
lockCurrentProxyInfos_->lock();
try {
reader.parse(body, *currentProxyInfos_);
} catch (...) {
*currentProxyInfos_ = Json::Value();
std::string err;
Json::CharReaderBuilder rbuilder;
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
try {
reader->parse(body.data(), body.data() + body.size(), &proxyInfos, &err);
} catch (...) {
}
}
lockCurrentProxyInfos_->unlock();
} else {
lockCurrentProxyInfos_->lock();
*currentProxyInfos_ = Json::Value();
lockCurrentProxyInfos_->unlock();
onProxyInfos(proxyInfos);
ongoingStatusUpdate_.clear();
});
});
statusThread_.detach();
}
void
DhtProxyClient::onProxyInfos(const Json::Value& proxyInfos)
{
std::lock_guard<std::mutex> l(lockCurrentProxyInfos_);
auto oldStatus = std::max(statusIpv4_, statusIpv6_);
try {
myid = InfoHash(proxyInfos["node_id"].asString());
stats4_ = NodeStats(proxyInfos["ipv4"]);
if (stats4_.good_nodes)
statusIpv4_ = NodeStatus::Connected;
else if (stats4_.dubious_nodes)
statusIpv4_ = NodeStatus::Connecting;
else
statusIpv4_ = NodeStatus::Disconnected;
stats6_ = NodeStats(proxyInfos["ipv6"]);
if (stats6_.good_nodes)
statusIpv6_ = NodeStatus::Connected;
else if (stats6_.dubious_nodes)
statusIpv6_ = NodeStatus::Connecting;
else
statusIpv6_ = NodeStatus::Disconnected;
publicAddress_ = parsePublicAddress(proxyInfos["public_ip"]);
} catch (...) {}
auto newStatus = std::max(statusIpv4_, statusIpv6_);
if (newStatus == NodeStatus::Connecting || newStatus == NodeStatus::Connected) {
if (oldStatus == NodeStatus::Disconnected) {
restartListeners();
}
}).wait();
lockCurrentProxyInfos_->lock();
auto result = *currentProxyInfos_;
lockCurrentProxyInfos_->unlock();
return result;
scheduler.edit(nextProxyConfirmation, scheduler.time() + std::chrono::minutes(15));
}
else if (newStatus == NodeStatus::Disconnected) {
scheduler.edit(nextProxyConfirmation, scheduler.time() + std::chrono::minutes(1));
}
loopSignal_();
}
SockAddr
DhtProxyClient::parsePublicAddress(const Json::Value& val)
{
auto public_ip = val.asString();
auto endIp = public_ip.find_last_of(':');
std::string service = public_ip.substr(endIp + 1);
std::string address = public_ip.substr(0, endIp - 1);
auto sa = SockAddr::resolve(address, service);
if (sa.empty()) return {};
return sa.front().getMappedIPv4();
}
std::vector<SockAddr>
DhtProxyClient::getPublicAddress(sa_family_t family)
{
lockCurrentProxyInfos_->lock();
auto proxyInfos = *currentProxyInfos_;
lockCurrentProxyInfos_->unlock();
// json["public_ip"] contains [ipv6:ipv4]:port or ipv4:port
auto public_ip = proxyInfos["public_ip"].asString();
if (!proxyInfos.isMember("public_ip") || (public_ip.length() < 2)) return {};
std::string ipv4Address = "", ipv6Address = "", port = "";
if (public_ip[0] == '[') {
// ipv6 complient
auto endIp = public_ip.find(']');
if (public_ip.length() > endIp + 2) {
port = public_ip.substr(endIp + 2);
auto ips = public_ip.substr(1, endIp - 1);
auto ipv4And6Separator = ips.find_last_of(':');
ipv4Address = ips.substr(ipv4And6Separator + 1);
ipv6Address = ips.substr(0, ipv4And6Separator - 1);
}
} else {
auto endIp = public_ip.find_last_of(':');
port = public_ip.substr(endIp + 1);
ipv4Address = public_ip.substr(0, endIp - 1);
}
switch (family)
{
case AF_INET:
return SockAddr::resolve(ipv4Address, port);
case AF_INET6:
return SockAddr::resolve(ipv6Address, port);
default:
return {};
}
std::lock_guard<std::mutex> l(lockCurrentProxyInfos_);
if (not publicAddress_) return {};
return publicAddress_.getFamily() == family ? std::vector<SockAddr>{publicAddress_} : std::vector<SockAddr>{};
}
size_t
DhtProxyClient::listen(const InfoHash& key, GetCallback cb, Value::Filter&& filter, Where&& where)
DhtProxyClient::listen(const InfoHash& key, GetCallback cb, Value::Filter filter, Where where)
{
restbed::Uri uri(HTTP_PROTO + serverHost_ + "/" + key.toString());
auto req = std::make_shared<restbed::Request>(uri);
req->set_method("LISTEN");
req->set_method(deviceKey_.empty() ? "LISTEN" : "SUBSCRIBE");
Query query {{}, where};
auto filterChain = filter.chain(query.where.getFilter());
auto pushNotifToken = std::make_shared<unsigned>(0);
unsigned callbackId = 0;
{
std::lock_guard<std::mutex> lock(lockCallback_);
callbackId_ += 1;
callbackId = callbackId_;
}
Listener l;
++listener_token_;
@@ -418,48 +449,75 @@ DhtProxyClient::listen(const InfoHash& key, GetCallback cb, Value::Filter&& filt
l.token = listener_token_;
l.req = req;
l.cb = cb;
l.callbackId = callbackId;
l.pushNotifToken = pushNotifToken;
l.filterChain = std::move(filterChain);
l.thread = std::thread([=]()
{
auto settings = std::make_shared<restbed::Settings>();
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 (deviceKey_.empty()) {
std::chrono::milliseconds timeout(std::numeric_limits<int>::max());
settings->set_connection_timeout(timeout); // Avoid the client to close the socket after 5 seconds.
}
#if OPENDHT_PUSH_NOTIFICATIONS
else
fillBodyToGetToken(req, callbackId);
#endif
struct State {
bool ok {true};
bool cancel {false};
std::atomic_bool ok {true};
std::atomic_bool cancel {false};
};
auto state = std::make_shared<State>();
restbed::Http::async(req,
[this, filterChain, cb, state](const std::shared_ptr<restbed::Request>& req,
const std::shared_ptr<restbed::Response>& reply) {
[this, filterChain, cb, pushNotifToken, state](const std::shared_ptr<restbed::Request>& req,
const std::shared_ptr<restbed::Response>& reply) {
auto code = reply->get_status_code();
if (code == 200) {
try {
while (restbed::Http::is_open(req) and not state->cancel) {
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)
break;
return;
std::string body;
reply->get_body(body);
reply->set_body(""); // Reset the body for the next fetch
Json::Value json;
Json::Reader reader;
if (reader.parse(body, json)) {
auto value = std::make_shared<Value>(json);
if ((not filterChain or filterChain(*value)) && cb) {
std::lock_guard<std::mutex> lock(lockCallbacks);
callbacks_.emplace_back([cb, value, state]() {
if (not state->cancel and not cb({value})) {
state->cancel = true;
}
});
}
auto* char_data = reinterpret_cast<const char*>(&body[0]);
if (reader->parse(char_data, char_data + body.size(), &json, &err)) {
if (!json.isMember("token")) return;
*pushNotifToken = json["token"].asLargestUInt();
} else {
state->ok = false;
}
} else {
while (restbed::Http::is_open(req) and not state->cancel) {
restbed::Http::fetch("\n", reply);
if (state->cancel)
break;
std::string body;
reply->get_body(body);
reply->set_body(""); // Reset the body for the next fetch
auto* char_data = reinterpret_cast<const char*>(&body[0]);
if (reader->parse(char_data, char_data + body.size(), &json, &err)) {
auto value = std::make_shared<Value>(json);
if ((not filterChain or filterChain(*value)) && cb) {
std::lock_guard<std::mutex> lock(lockCallbacks);
callbacks_.emplace_back([cb, value, state]() {
if (not state->cancel and not cb({value})) {
state->cancel = true;
}
});
loopSignal_();
}
} else {
state->ok = false;
}
}
}
} catch (std::runtime_error&) {
state->ok = false;
@@ -469,7 +527,7 @@ DhtProxyClient::listen(const InfoHash& key, GetCallback cb, Value::Filter&& filt
}
}, settings).get();
if (not state->ok) {
getConnectivityStatus();
opFailed();
}
}
);
@@ -487,13 +545,46 @@ DhtProxyClient::cancelListen(const InfoHash&, size_t token)
for (auto it = listeners_.begin(); it != listeners_.end(); ++it) {
auto& listener = *it;
if (listener.token == token) {
if (listener.thread.joinable()) {
// Close connection to stop listener?
if (listener.req)
restbed::Http::close(listener.req);
listener.thread.join();
if (!deviceKey_.empty()) {
// First, be sure to have a token
if (listener.thread.joinable()) {
listener.thread.join();
}
// UNSUBSCRIBE
restbed::Uri uri(HTTP_PROTO + serverHost_ + "/" + listener.key);
auto req = std::make_shared<restbed::Request>(uri);
req->set_method("UNSUBSCRIBE");
// fill request body
Json::Value body;
body["key"] = deviceKey_;
body["client_id"] = pushClientId_;
body["token"] = std::to_string(token);
body["callback_id"] = listener.callbackId;
Json::StreamWriterBuilder wbuilder;
wbuilder["commentStyle"] = "None";
wbuilder["indentation"] = "";
auto content = Json::writeString(wbuilder, body) + "\n";
std::replace(content.begin(), content.end(), '\n', ' ');
req->set_body(content);
req->set_header("Content-Length", std::to_string(content.size()));
restbed::Http::async(req,
[](const std::shared_ptr<restbed::Request>&,
const std::shared_ptr<restbed::Response>&){}
);
// And remove
listeners_.erase(it);
return true;
} else {
// Just stop the request
if (listener.thread.joinable()) {
// Close connection to stop listener?
if (listener.req)
restbed::Http::close(listener.req);
listener.thread.join();
listeners_.erase(it);
return true;
}
}
}
}
@@ -501,30 +592,22 @@ DhtProxyClient::cancelListen(const InfoHash&, size_t token)
}
void
DhtProxyClient::getConnectivityStatus()
DhtProxyClient::opFailed()
{
auto proxyInfos = getProxyInfos();
// NOTE: json["ipvX"] contains NodeStats::toJson()
try {
auto goodIpv4 = static_cast<long>(proxyInfos["ipv4"]["good"].asLargestUInt());
auto dubiousIpv4 = static_cast<long>(proxyInfos["ipv4"]["dubious"].asLargestUInt());
statusIpv4_ = (goodIpv4 + dubiousIpv4 > 0) ? NodeStatus::Connected : NodeStatus::Disconnected;
auto goodIpv6 = static_cast<long>(proxyInfos["ipv6"]["good"].asLargestUInt());
auto dubiousIpv6 = static_cast<long>(proxyInfos["ipv6"]["dubious"].asLargestUInt());
statusIpv6_ = (goodIpv6 + dubiousIpv6 > 0) ? NodeStatus::Connected : NodeStatus::Disconnected;
myid = InfoHash(proxyInfos["node_id"].asString());
if (statusIpv4_ == NodeStatus::Disconnected && statusIpv6_ == NodeStatus::Disconnected) {
const auto& now = scheduler.time();
scheduler.edit(nextProxyConfirmation, now);
}
} catch (...) {
DHT_LOG.ERR("Proxy request failed");
{
std::lock_guard<std::mutex> l(lockCurrentProxyInfos_);
statusIpv4_ = NodeStatus::Disconnected;
statusIpv6_ = NodeStatus::Disconnected;
const auto& now = scheduler.time();
scheduler.edit(nextProxyConfirmation, now);
}
getConnectivityStatus();
loopSignal_();
}
void
DhtProxyClient::getConnectivityStatus()
{
getProxyInfos();
}
void
@@ -547,9 +630,10 @@ DhtProxyClient::restartListeners()
std::chrono::milliseconds timeout(std::numeric_limits<int>::max());
settings->set_connection_timeout(timeout); // Avoid the client to close the socket after 5 seconds.
auto ok = std::make_shared<std::atomic_bool>(true);
restbed::Http::async(req,
[this, filterChain, cb](const std::shared_ptr<restbed::Request>& req,
const std::shared_ptr<restbed::Response>& reply) {
[this, filterChain, cb, ok](const std::shared_ptr<restbed::Request>& req,
const std::shared_ptr<restbed::Response>& reply) {
auto code = reply->get_status_code();
if (code == 200) {
@@ -561,8 +645,11 @@ DhtProxyClient::restartListeners()
reply->set_body(""); // Reset the body for the next fetch
Json::Value json;
Json::Reader reader;
if (reader.parse(body, json)) {
std::string err;
Json::CharReaderBuilder rbuilder;
auto* char_data = reinterpret_cast<const char*>(&body[0]);
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
if (reader->parse(char_data, char_data + body.size(), &json, &err)) {
auto value = std::make_shared<Value>(json);
if ((not filterChain or filterChain(*value)) && cb) {
auto okCb = std::make_shared<std::promise<bool>>();
@@ -572,6 +659,7 @@ DhtProxyClient::restartListeners()
callbacks_.emplace_back([cb, value, okCb](){
okCb->set_value(cb({value}));
});
loopSignal_();
}
futureCb.wait();
if (!futureCb.get()) {
@@ -583,18 +671,125 @@ DhtProxyClient::restartListeners()
} catch (std::runtime_error&) {
// NOTE: Http::close() can occurs here. Ignore this.
}
} else {
this->statusIpv4_ = NodeStatus::Disconnected;
this->statusIpv6_ = NodeStatus::Disconnected;
*ok = false;
}
}, settings).get();
getConnectivityStatus();
if (!ok) opFailed();
}
);
}
}
void
DhtProxyClient::pushNotificationReceived(const std::map<std::string, std::string>& notification)
{
#if OPENDHT_PUSH_NOTIFICATIONS
try {
auto token = std::stoul(notification.at("token"));
for (const auto& listener: listeners_) {
if (*(listener.pushNotifToken) != token)
continue;
if (notification.find("timeout") == notification.cend()) {
// Wake up daemon and get values
get(InfoHash(listener.key), listener.cb, {}, listener.filterChain);
} else {
// A timeout has occured, we need to relaunch the listener
resubscribe(token);
}
}
} catch (...) {
}
#endif
}
void
DhtProxyClient::resubscribe(const unsigned token)
{
#if OPENDHT_PUSH_NOTIFICATIONS
if (deviceKey_.empty()) return;
for (auto& listener: listeners_) {
if (*(listener.pushNotifToken) == token) {
// Subscribe
restbed::Uri uri(HTTP_PROTO + serverHost_ + "/" + listener.key);
auto req = std::make_shared<restbed::Request>(uri);
req->set_method("SUBSCRIBE");
auto pushNotifToken = std::make_shared<unsigned>(0);
if (listener.thread.joinable())
listener.thread.join();
listener.req = req;
listener.pushNotifToken = pushNotifToken;
auto callbackId = listener.callbackId;
listener.thread = std::thread([=]()
{
fillBodyToGetToken(req, callbackId);
auto settings = std::make_shared<restbed::Settings>();
auto ok = std::make_shared<std::atomic_bool>(true);
restbed::Http::async(req,
[this, pushNotifToken, ok](const std::shared_ptr<restbed::Request>&,
const std::shared_ptr<restbed::Response>& reply) {
auto code = reply->get_status_code();
if (code == 200) {
try {
restbed::Http::fetch("\n", reply);
std::string body;
reply->get_body(body);
std::string err;
Json::Value json;
Json::CharReaderBuilder rbuilder;
auto* char_data = reinterpret_cast<const char*>(&body[0]);
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
if (reader->parse(char_data, char_data + body.size(), &json, &err)) {
if (!json.isMember("token")) return;
*pushNotifToken = json["token"].asLargestUInt();
}
} catch (std::runtime_error&) {
// NOTE: Http::close() can occurs here. Ignore this.
}
} else {
*ok = false;
}
}, settings).get();
if (!ok) opFailed();
});
}
}
#endif
}
#if OPENDHT_PUSH_NOTIFICATIONS
void
DhtProxyClient::fillBodyToGetToken(std::shared_ptr<restbed::Request> req, unsigned callbackId)
{
// Fill body with
// {
// "key":"device_key",
// "callback_id": xxx
// }
Json::Value body;
body["key"] = deviceKey_;
body["client_id"] = pushClientId_;
body["callback_id"] = callbackId;
#ifdef __ANDROID__
body["platform"] = "android";
#endif
#ifdef __APPLE__
body["platform"] = "apple";
#endif
Json::StreamWriterBuilder wbuilder;
wbuilder["commentStyle"] = "None";
wbuilder["indentation"] = "";
auto content = Json::writeString(wbuilder, body) + "\n";
std::replace(content.begin(), content.end(), '\n', ' ');
req->set_body(content);
req->set_header("Content-Length", std::to_string(content.size()));
}
#endif // OPENDHT_PUSH_NOTIFICATIONS
} // namespace dht
+315 -61
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2017 Savoir-faire Linux Inc.
* Copyright (C) 2017-2018 Savoir-faire Linux Inc.
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -28,16 +28,30 @@
#include <json/json.h>
#include <limits>
#if OPENDHT_PUSH_NOTIFICATIONS
constexpr int const TIMEOUT {6 * 60 * 60}; // in seconds (so six hours)
constexpr const char* const HTTP_PROTO {"http://"}; // TODO, https for prod
#endif //OPENDHT_PUSH_NOTIFICATIONS
using namespace std::placeholders;
namespace dht {
DhtProxyServer::DhtProxyServer(std::shared_ptr<DhtRunner> dht, in_port_t port)
: dht_(dht)
DhtProxyServer::DhtProxyServer(std::shared_ptr<DhtRunner> dht, in_port_t port , const std::string& pushServer)
: dht_(dht) , pushServer_(pushServer)
{
// NOTE in c++14, use make_unique
service_ = std::unique_ptr<restbed::Service>(new restbed::Service());
std::cout << "Running DHT proxy server on port " << port << std::endl;
if (not pushServer.empty()) {
#if !OPENDHT_PUSH_NOTIFICATIONS
std::cerr << "Push server defined but built OpenDHT built without push notification support" << std::endl;
#else
std::cout << "Using push notification server: " << pushServer << std::endl;
#endif
}
server_thread = std::thread([this, port]() {
// Create endpoints
auto resource = std::make_shared<restbed::Resource>();
@@ -48,6 +62,10 @@ DhtProxyServer::DhtProxyServer(std::shared_ptr<DhtRunner> dht, in_port_t port)
resource->set_path("/{hash: .*}");
resource->set_method_handler("GET", std::bind(&DhtProxyServer::get, this, _1));
resource->set_method_handler("LISTEN", std::bind(&DhtProxyServer::listen, this, _1));
#if OPENDHT_PUSH_NOTIFICATIONS
resource->set_method_handler("SUBSCRIBE", std::bind(&DhtProxyServer::subscribe, this, _1));
resource->set_method_handler("UNSUBSCRIBE", std::bind(&DhtProxyServer::unsubscribe, this, _1));
#endif //OPENDHT_PUSH_NOTIFICATIONS
resource->set_method_handler("POST", std::bind(&DhtProxyServer::put, this, _1));
#if OPENDHT_PROXY_SERVER_IDENTITY
resource->set_method_handler("SIGN", std::bind(&DhtProxyServer::putSigned, this, _1));
@@ -82,33 +100,15 @@ DhtProxyServer::DhtProxyServer(std::shared_ptr<DhtRunner> dht, in_port_t port)
std::this_thread::sleep_for(std::chrono::seconds(1));
}
while (service_->is_up() && !stopListeners) {
lockListener_.lock();
auto listener = currentListeners_.begin();
while (listener != currentListeners_.end()) {
if (dht_ && listener->session->is_closed()) {
dht_->cancelListen(listener->hash, std::move(listener->token));
// Remove listener if unused
listener = currentListeners_.erase(listener);
} else {
++listener;
}
}
lockListener_.unlock();
removeClosedListeners();
// add listener from push notifs
#if OPENDHT_PUSH_NOTIFICATIONS
handlePushListeners();
#endif //OPENDHT_PUSH_NOTIFICATIONS
std::this_thread::sleep_for(std::chrono::seconds(1));
}
// Remove last listeners
lockListener_.lock();
auto listener = currentListeners_.begin();
while (listener != currentListeners_.end()) {
if (dht_) {
dht_->cancelListen(listener->hash, std::move(listener->token.get()));
// Remove listener if unused
listener = currentListeners_.erase(listener);
} else {
++listener;
}
}
lockListener_.unlock();
removeClosedListeners(false);
});
dht->forwardAllMessages(true);
@@ -123,13 +123,14 @@ void
DhtProxyServer::stop()
{
service_->stop();
lockListener_.lock();
auto listener = currentListeners_.begin();
while (listener != currentListeners_.end()) {
listener->session->close();
++ listener;
{
std::lock_guard<std::mutex> lock(lockListener_);
auto listener = currentListeners_.begin();
while (listener != currentListeners_.end()) {
listener->session->close();
++ listener;
}
}
lockListener_.unlock();
stopListeners = true;
// listenThreads_ will stop because there is no more sessions
if (listenThread_.joinable())
@@ -155,8 +156,11 @@ DhtProxyServer::getNodeInfo(const std::shared_ptr<restbed::Session>& session) co
result["ipv4"] = dht_->getNodesStats(AF_INET).toJson();
result["ipv6"] = dht_->getNodesStats(AF_INET6).toJson();
result["public_ip"] = s->get_origin(); // [ipv6:ipv4]:port or ipv4:port
Json::FastWriter writer;
s->close(restbed::OK, writer.write(result));
Json::StreamWriterBuilder wbuilder;
wbuilder["commentStyle"] = "None";
wbuilder["indentation"] = "";
auto output = Json::writeString(wbuilder, result) + "\n";
s->close(restbed::OK, output);
}
else
s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}");
@@ -185,8 +189,11 @@ DhtProxyServer::get(const std::shared_ptr<restbed::Session>& session) const
auto s = cacheSession.lock();
if (!s) return false;
// Send values as soon as we get them
Json::FastWriter writer;
s->yield(writer.write(value->toJson()), [](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> /*session*/){ });
return true;
}, [s](bool /*ok* */) {
// Communication is finished
@@ -234,14 +241,18 @@ DhtProxyServer::listen(const std::shared_ptr<restbed::Session>& session) const
if (!s) return false;
// Send values as soon as we get them
if (!s->is_closed()) {
Json::FastWriter writer;
s->yield(writer.write(value->toJson()), [](const std::shared_ptr<restbed::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>){ });
}
return !s->is_closed();
});
lockListener_.lock();
currentListeners_.emplace_back(std::move(listener));
lockListener_.unlock();
{
std::lock_guard<std::mutex> lock(lockListener_);
currentListeners_.emplace_back(std::move(listener));
}
} else {
session->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}");
}
@@ -249,6 +260,210 @@ DhtProxyServer::listen(const std::shared_ptr<restbed::Session>& session) const
);
}
#if OPENDHT_PUSH_NOTIFICATIONS
void
DhtProxyServer::subscribe(const std::shared_ptr<restbed::Session>& session) const
{
const auto request = session->get_request();
int content_length = std::stoi(request->get_header("Content-Length", "0"));
auto hash = request->get_path_parameter("hash");
InfoHash infoHash(hash);
if (!infoHash)
infoHash = InfoHash::get(hash);
session->fetch(content_length,
[=](const std::shared_ptr<restbed::Session> s, const restbed::Bytes& b)
{
try {
restbed::Bytes buf(b);
std::string strJson(buf.begin(), buf.end());
std::string err;
Json::Value root;
Json::CharReaderBuilder rbuilder;
auto* char_data = reinterpret_cast<const char*>(&strJson[0]);
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
if (!reader->parse(char_data, char_data + strJson.size(), &root, &err)) {
s->close(restbed::BAD_REQUEST, "{\"err\":\"Incorrect JSON\"}");
return;
}
auto pushToken = root["key"].asString();
if (pushToken.empty()) return;
auto callbackId = root.isMember("callback_id") ? root["callback_id"].asLargestUInt() : 0;
auto platform = root["platform"].asString();
auto isAndroid = platform == "android";
auto clientId = root.isMember("client_id") ? root["client_id"].asString() : std::string();
auto token = 0;
{
std::lock_guard<std::mutex> lock(lockListener_);
// Check if listener is already present and refresh timeout if launched
for(auto& listener: pushListeners_) {
if (listener.pushToken == pushToken && listener.hash == infoHash
&& listener.callbackId == callbackId) {
if (listener.started)
listener.deadline = std::chrono::steady_clock::now()
+ std::chrono::seconds(TIMEOUT);
s->close(restbed::OK, "{\"token\": " + std::to_string(listener.token) + "}\n");
return;
}
}
// The listener is not found, so add it.
++tokenPushNotif_;
token = tokenPushNotif_;
PushListener listener;
listener.pushToken = pushToken;
listener.hash = std::move(infoHash);
listener.token = token;
listener.started = false;
listener.callbackId = callbackId;
listener.clientId = clientId;
listener.isAndroid = isAndroid;
pushListeners_.emplace_back(std::move(listener));
}
s->close(restbed::OK, "{\"token\": " + std::to_string(token) + "}\n");
} catch (...) {
// do nothing
}
}
);
}
void
DhtProxyServer::unsubscribe(const std::shared_ptr<restbed::Session>& session) const
{
const auto request = session->get_request();
int content_length = std::stoi(request->get_header("Content-Length", "0"));
auto hash = request->get_path_parameter("hash");
InfoHash infoHash(hash);
if (!infoHash)
infoHash = InfoHash::get(hash);
session->fetch(content_length,
[=](const std::shared_ptr<restbed::Session> s, const restbed::Bytes& b)
{
try {
restbed::Bytes buf(b);
std::string strJson(buf.begin(), buf.end());
std::string err;
Json::Value root;
Json::CharReaderBuilder rbuilder;
auto* char_data = reinterpret_cast<const char*>(&strJson[0]);
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
if (!reader->parse(char_data, char_data + strJson.size(), &root, &err)) {
s->close(restbed::BAD_REQUEST, "{\"err\":\"Incorrect JSON\"}");
return;
}
auto pushToken = root["key"].asString();
if (pushToken.empty()) return;
auto token = std::stoull(root["token"].asString());
if (token == 0) return;
auto callbackId = root.isMember("callback_id") ? root["callback_id"].asLargestUInt() : 0;
std::lock_guard<std::mutex> lock(lockListener_);
// Check if listener is already present and refresh timeout if launched
auto listener = pushListeners_.begin();
while (listener != pushListeners_.end()) {
if (listener->pushToken == pushToken && listener->token == token
&& listener->hash == infoHash && listener->callbackId == callbackId) {
if (dht_ && listener->started)
dht_->cancelListen(listener->hash, std::move(listener->internalToken.get()));
listener = pushListeners_.erase(listener);
} else {
++listener;
}
}
} catch (...) {
// do nothing
}
}
);
}
void
DhtProxyServer::sendPushNotification(const std::string& token, const Json::Value& json, bool isAndroid) const
{
restbed::Uri uri(HTTP_PROTO + pushServer_ + "/api/push");
auto req = std::make_shared<restbed::Request>(uri);
req->set_method("POST");
// NOTE: see https://github.com/appleboy/gorush
Json::Value notification(Json::objectValue);
Json::Value tokens(Json::arrayValue);
tokens[0] = token;
notification["tokens"] = tokens;
notification["platform"] = isAndroid ? 2 : 1;
notification["data"] = json;
notification["priority"] = "high";
notification["time_to_live"] = 600;
Json::Value notifications(Json::arrayValue);
notifications[0] = notification;
Json::Value content;
content["notifications"] = notifications;
Json::StreamWriterBuilder wbuilder;
wbuilder["commentStyle"] = "None";
wbuilder["indentation"] = "";
auto valueStr = Json::writeString(wbuilder, content);
req->set_header("Content-Type", "application/json");
req->set_header("Accept", "*/*");
req->set_header("Host", pushServer_);
req->set_header("Content-Length", std::to_string(valueStr.length()));
req->set_body(valueStr);
// Send request.
restbed::Http::async(req, {});
}
void
DhtProxyServer::handlePushListeners()
{
std::lock_guard<std::mutex> lock(lockListener_);
auto pushListener = pushListeners_.begin();
while (pushListener != pushListeners_.end()) {
if (dht_ && !pushListener->started) {
// Try to start unstarted listeners
auto key = pushListener->pushToken;
auto token = pushListener->token;
auto callbackId = pushListener->callbackId;
auto isAndroid = pushListener->isAndroid;
auto clientId = pushListener->clientId;
pushListener->internalToken = dht_->listen(pushListener->hash,
[this, key, callbackId, token, isAndroid, clientId](std::vector<std::shared_ptr<Value>> /*value*/) {
// Build message content.
Json::Value json;
if (callbackId > 0) {
json["callback_id"] = callbackId;
}
json["to"] = clientId;
json["token"] = token;
sendPushNotification(key, json, isAndroid);
return true;
}
);
pushListener->deadline = std::chrono::steady_clock::now() + std::chrono::seconds(TIMEOUT);
pushListener->started = true;
pushListener++;
} else if (dht_ && pushListener->started && pushListener->deadline < std::chrono::steady_clock::now()) {
// Cancel listen if deadline has been reached
dht_->cancelListen(pushListener->hash, std::move(pushListener->internalToken.get()));
// Send a push notification to inform the client that this listen has timeout
Json::Value json;
json["timeout"] = pushListener->hash.toString();
if (pushListener->callbackId > 0) {
json["callback_id"] = pushListener->callbackId;
}
json["to"] = pushListener->clientId;
json["token"] = pushListener->token;
sendPushNotification(pushListener->pushToken, json, pushListener->isAndroid);
pushListener = pushListeners_.erase(pushListener);
} else {
pushListener++;
}
}
}
#endif //OPENDHT_PUSH_NOTIFICATIONS
void
DhtProxyServer::put(const std::shared_ptr<restbed::Session>& session) const
{
@@ -268,20 +483,25 @@ DhtProxyServer::put(const std::shared_ptr<restbed::Session>& session) const
s->close(restbed::BAD_REQUEST, response);
} else {
restbed::Bytes buf(b);
Json::Value root;
Json::Reader reader;
std::string strJson(buf.begin(), buf.end());
bool parsingSuccessful = reader.parse(strJson.c_str(), root);
if (parsingSuccessful) {
std::string err;
Json::Value root;
Json::CharReaderBuilder rbuilder;
auto* char_data = reinterpret_cast<const char*>(&strJson[0]);
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
if (reader->parse(char_data, char_data + strJson.size(), &root, &err)) {
// Build the Value from json
auto value = std::make_shared<Value>(root);
auto permanent = root.isMember("permanent") ? root["permanent"].asBool() : false;
dht_->put(infoHash, value, [s, value](bool ok) {
if (ok) {
Json::FastWriter writer;
Json::StreamWriterBuilder wbuilder;
wbuilder["commentStyle"] = "None";
wbuilder["indentation"] = "";
if (s->is_open())
s->close(restbed::OK, writer.write(value->toJson()));
s->close(restbed::OK, Json::writeString(wbuilder, value->toJson()) + "\n");
} else {
if (s->is_open())
s->close(restbed::BAD_GATEWAY, "{\"err\":\"put failed\"}");
@@ -318,16 +538,22 @@ DhtProxyServer::putSigned(const std::shared_ptr<restbed::Session>& session) cons
s->close(restbed::BAD_REQUEST, response);
} else {
restbed::Bytes buf(b);
Json::Value root;
Json::Reader reader;
std::string strJson(buf.begin(), buf.end());
bool parsingSuccessful = reader.parse(strJson.c_str(), root);
if (parsingSuccessful) {
std::string err;
Json::Value root;
Json::CharReaderBuilder rbuilder;
auto* char_data = reinterpret_cast<const char*>(&strJson[0]);
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
if (reader->parse(char_data, char_data + strJson.size(), &root, &err)) {
auto value = std::make_shared<Value>(root);
Json::FastWriter writer;
Json::StreamWriterBuilder wbuilder;
wbuilder["commentStyle"] = "None";
wbuilder["indentation"] = "";
auto output = Json::writeString(wbuilder, value->toJson()) + "\n";
dht_->putSigned(infoHash, value);
s->close(restbed::OK, writer.write(value->toJson()));
s->close(restbed::OK, output);
} else {
s->close(restbed::BAD_REQUEST, "{\"err\":\"Incorrect JSON\"}");
}
@@ -358,16 +584,23 @@ DhtProxyServer::putEncrypted(const std::shared_ptr<restbed::Session>& session) c
s->close(restbed::BAD_REQUEST, response);
} else {
restbed::Bytes buf(b);
Json::Value root;
Json::Reader reader;
std::string strJson(buf.begin(), buf.end());
bool parsingSuccessful = reader.parse(strJson.c_str(), root);
std::string err;
Json::Value root;
Json::CharReaderBuilder rbuilder;
auto* char_data = reinterpret_cast<const char*>(&strJson[0]);
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
bool parsingSuccessful = reader->parse(char_data, char_data + strJson.size(), &root, &err);
InfoHash to(root["to"].asString());
if (parsingSuccessful && to) {
auto value = std::make_shared<Value>(root);
Json::FastWriter writer;
Json::StreamWriterBuilder wbuilder;
wbuilder["commentStyle"] = "None";
wbuilder["indentation"] = "";
auto output = Json::writeString(wbuilder, value->toJson()) + "\n";
dht_->putEncrypted(key, to, value);
s->close(restbed::OK, writer.write(value->toJson()));
s->close(restbed::OK, output);
} else {
if(!parsingSuccessful)
s->close(restbed::BAD_REQUEST, "{\"err\":\"Incorrect JSON\"}");
@@ -414,8 +647,11 @@ DhtProxyServer::getFiltered(const std::shared_ptr<restbed::Session>& session) co
s->yield(restbed::OK, "", [=]( const std::shared_ptr< restbed::Session > s) {
dht_->get(infoHash, [s](std::shared_ptr<Value> v) {
// Send values as soon as we get them
Json::FastWriter writer;
s->yield(writer.write(v->toJson()), [](const std::shared_ptr<restbed::Session> /*session*/){ });
Json::StreamWriterBuilder wbuilder;
wbuilder["commentStyle"] = "None";
wbuilder["indentation"] = "";
auto output = Json::writeString(wbuilder, v->toJson()) + "\n";
s->yield(output, [](const std::shared_ptr<restbed::Session> /*session*/){ });
return true;
}, [s](bool /*ok* */) {
// Communication is finished
@@ -429,5 +665,23 @@ DhtProxyServer::getFiltered(const std::shared_ptr<restbed::Session>& session) co
);
}
void
DhtProxyServer::removeClosedListeners(bool testSession)
{
// clean useless listeners
std::lock_guard<std::mutex> lock(lockListener_);
auto listener = currentListeners_.begin();
while (listener != currentListeners_.end()) {
auto cancel = testSession ? dht_ && listener->session->is_closed() : static_cast<bool>(dht_);
if (cancel) {
dht_->cancelListen(listener->hash, std::move(listener->token.get()));
// Remove listener if unused
listener = currentListeners_.erase(listener);
} else {
++listener;
}
}
}
}
#endif //OPENDHT_PROXY_SERVER
+187 -160
View File
@@ -1,7 +1,8 @@
/*
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,6 +21,10 @@
#include "dhtrunner.h"
#include "securedht.h"
#if OPENDHT_PROXY_CLIENT
#include "dht_proxy_client.h"
#endif
#ifndef _WIN32
#include <unistd.h>
#else
@@ -84,20 +89,28 @@ DhtRunner::run(const SockAddr& local4, const SockAddr& local6, DhtRunner::Config
{
if (running)
return;
if (rcv_thread.joinable())
rcv_thread.join();
running = true;
startNetwork(local4, local6);
auto dht = std::unique_ptr<DhtInterface>(new Dht(s4, s6, SecureDht::getConfig(config.dht_config)));
dht_ = std::unique_ptr<SecureDht>(new SecureDht(std::move(dht), config.dht_config));
#if OPENDHT_PROXY_CLIENT
config_.dht_config = config.dht_config;
config_.threaded = config.threaded;
#endif //OPENDHT_PROXY_CLIENT
doRun(local4, local6, config.dht_config);
config_ = config;
#endif
enableProxy(not config.proxy_server.empty());
running = true;
if (not config.threaded)
return;
dht_thread = std::thread([this]() {
dht_thread = std::thread([this, local4, local6]() {
while (running) {
std::unique_lock<std::mutex> lk(dht_mtx);
auto wakeup = loop_();
time_point wakeup;
try {
wakeup = loop_();
} catch (const dht::SocketException& e) {
startNetwork(local4, local6);
}
cv.wait_until(lk, wakeup, [this]() {
if (not running)
return true;
@@ -123,7 +136,8 @@ DhtRunner::run(const SockAddr& local4, const SockAddr& local6, DhtRunner::Config
void
DhtRunner::shutdown(ShutdownCallback cb) {
#if OPENDHT_PROXY_CLIENT
dht_via_proxy_->shutdown(cb);
if (dht_via_proxy_)
dht_via_proxy_->shutdown(cb);
#endif
std::lock_guard<std::mutex> lck(storage_mtx);
pending_ops_prio.emplace([=](SecureDht& dht) mutable {
@@ -135,15 +149,16 @@ DhtRunner::shutdown(ShutdownCallback cb) {
void
DhtRunner::join()
{
running_network = false;
running = false;
cv.notify_all();
bootstrap_cv.notify_all();
if (dht_thread.joinable())
dht_thread.join();
if (rcv_thread.joinable())
rcv_thread.join();
if (bootstrap_thread.joinable())
bootstrap_thread.join();
if (rcv_thread.joinable())
rcv_thread.join();
{
std::lock_guard<std::mutex> lck(storage_mtx);
@@ -155,8 +170,6 @@ DhtRunner::join()
resetDht();
status4 = NodeStatus::Disconnected;
status6 = NodeStatus::Disconnected;
bound4 = {};
bound6 = {};
}
}
@@ -187,17 +200,17 @@ DhtRunner::getNodeId() const
std::pair<size_t, size_t>
DhtRunner::getStoreSize() const {
std::lock_guard<std::mutex> lck(dht_mtx);
if (!activeDht())
if (!dht_)
return {};
return activeDht()->getStoreSize();
return dht_->getStoreSize();
}
void
DhtRunner::setStorageLimit(size_t limit) {
std::lock_guard<std::mutex> lck(dht_mtx);
if (!activeDht())
if (!dht_)
throw std::runtime_error("dht is not running");
return activeDht()->setStorageLimit(limit);
return dht_->setStorageLimit(limit);
}
std::vector<NodeExport>
@@ -205,27 +218,38 @@ DhtRunner::exportNodes() const {
std::lock_guard<std::mutex> lck(dht_mtx);
if (!dht_)
return {};
return activeDht()->exportNodes();
return dht_->exportNodes();
}
std::vector<ValuesExport>
DhtRunner::exportValues() const {
std::lock_guard<std::mutex> lck(dht_mtx);
if (!activeDht())
if (!dht_)
return {};
return activeDht()->exportValues();
return dht_->exportValues();
}
void
DhtRunner::setLoggers(LogMethod error, LogMethod warn, LogMethod debug) {
std::lock_guard<std::mutex> lck(dht_mtx);
activeDht()->setLoggers(std::forward<LogMethod>(error), std::forward<LogMethod>(warn), std::forward<LogMethod>(debug));
if (dht_)
dht_->setLoggers(std::forward<LogMethod>(error), std::forward<LogMethod>(warn), std::forward<LogMethod>(debug));
#if OPENDHT_PROXY_CLIENT
if (dht_via_proxy_)
dht_via_proxy_->setLoggers(std::forward<LogMethod>(error), std::forward<LogMethod>(warn), std::forward<LogMethod>(debug));
#endif
}
void
DhtRunner::setLogFilter(const InfoHash& f) {
std::lock_guard<std::mutex> lck(dht_mtx);
activeDht()->setLogFilter(f);
if (dht_)
dht_->setLogFilter(f);
#if OPENDHT_PROXY_CLIENT
if (dht_via_proxy_)
dht_via_proxy_->setLogFilter(f);
#endif
}
void
@@ -237,7 +261,7 @@ DhtRunner::registerType(const ValueType& type) {
void
DhtRunner::importValues(const std::vector<ValuesExport>& values) {
std::lock_guard<std::mutex> lck(dht_mtx);
activeDht()->importValues(values);
dht_->importValues(values);
}
unsigned
@@ -324,15 +348,18 @@ void
DhtRunner::setLocalCertificateStore(CertificateStoreQuery&& query_method) {
std::lock_guard<std::mutex> lck(dht_mtx);
#if OPENDHT_PROXY_CLIENT
dht_via_proxy_->setLocalCertificateStore(std::forward<CertificateStoreQuery>(query_method));
if (dht_via_proxy_)
dht_via_proxy_->setLocalCertificateStore(std::forward<CertificateStoreQuery>(query_method));
#endif
dht_->setLocalCertificateStore(std::forward<CertificateStoreQuery>(query_method));
if (dht_)
dht_->setLocalCertificateStore(std::forward<CertificateStoreQuery>(query_method));
}
time_point
DhtRunner::loop_()
{
if (!activeDht())
auto dht = activeDht();
if (not dht)
return {};
decltype(pending_ops) ops {};
@@ -343,7 +370,7 @@ DhtRunner::loop_()
std::move(pending_ops) : std::move(pending_ops_prio);
}
while (not ops.empty()) {
ops.front()(*activeDht());
ops.front()(*dht);
ops.pop();
}
@@ -358,15 +385,15 @@ DhtRunner::loop_()
for (const auto& pck : received) {
auto& buf = pck.first;
auto& from = pck.second;
wakeup = activeDht()->periodic(buf.data(), buf.size()-1, from);
wakeup = dht->periodic(buf.data(), buf.size()-1, from);
}
received.clear();
} else {
wakeup = activeDht()->periodic(nullptr, 0, nullptr, 0);
wakeup = dht->periodic(nullptr, 0, nullptr, 0);
}
NodeStatus nstatus4 = activeDht()->getStatus(AF_INET);
NodeStatus nstatus6 = activeDht()->getStatus(AF_INET6);
NodeStatus nstatus4 = dht->getStatus(AF_INET);
NodeStatus nstatus6 = dht->getStatus(AF_INET6);
if (nstatus4 != status4 || nstatus6 != status6) {
status4 = nstatus4;
status6 = nstatus6;
@@ -410,12 +437,13 @@ int bindSocket(const SockAddr& addr, SockAddr& bound)
}
void
DhtRunner::doRun(const SockAddr& sin4, const SockAddr& sin6, SecureDht::Config config)
DhtRunner::startNetwork(const SockAddr sin4, const SockAddr sin6)
{
resetDht();
int s4 = -1,
s6 = -1;
running_network = false;
if (rcv_thread.joinable())
rcv_thread.join();
s4 = -1;
s6 = -1;
bound4 = {};
if (sin4)
@@ -427,23 +455,10 @@ DhtRunner::doRun(const SockAddr& sin4, const SockAddr& sin6, SecureDht::Config c
s6 = bindSocket(sin6, bound6);
#endif
auto dht = std::unique_ptr<DhtInterface>(
new Dht(s4, s6, SecureDht::getConfig(config))
);
dht_ = std::unique_ptr<SecureDht>(new SecureDht(std::move(dht), config));
#if OPENDHT_PROXY_CLIENT
if (!dht_via_proxy_) {
auto dht_via_proxy = std::unique_ptr<DhtInterface>(
new DhtProxyClient(config_.proxy_server)
);
dht_via_proxy_ = std::unique_ptr<SecureDht>(new SecureDht(std::move(dht_via_proxy), config_.dht_config));
}
#endif
rcv_thread = std::thread([this,s4,s6]() {
running_network = true;
rcv_thread = std::thread([this]() {
try {
while (true) {
while (running_network) {
struct timeval tv {/*.tv_sec = */0, /*.tv_usec = */250000};
fd_set readfds;
@@ -461,7 +476,7 @@ DhtRunner::doRun(const SockAddr& sin4, const SockAddr& sin6, SecureDht::Config c
}
}
if(!running)
if (not running_network)
break;
if(rc > 0) {
@@ -491,6 +506,10 @@ DhtRunner::doRun(const SockAddr& sin4, const SockAddr& sin6, SecureDht::Config c
close(s4);
if (s6 >= 0)
close(s6);
s4 = -1;
s6 = -1;
bound4 = {};
bound6 = {};
});
}
@@ -528,29 +547,29 @@ DhtRunner::listen(InfoHash hash, GetCallback vcb, Value::Filter f, Where w)
auto ret_token = std::make_shared<std::promise<size_t>>();
{
std::lock_guard<std::mutex> lck(storage_mtx);
#if OPENDHT_PROXY_CLIENT
pending_ops.emplace([=](SecureDht&) mutable {
auto tokenProxy = 0, tokenClassic = 0;
if (!use_proxy)
tokenClassic = dht_->listen(hash, vcb, std::move(f), std::move(w));
else
tokenProxy = dht_via_proxy_->listen(hash, vcb, std::move(f), std::move(w));
#else
pending_ops.emplace([=](SecureDht& dht) mutable {
auto tokenClassic = dht.listen(hash, vcb, std::move(f), std::move(w));
auto tokenProxy = 0;
auto tokenbGlobal = listener_token_++;
Listener listener {};
listener.hash = hash;
listener.f = std::move(f);
listener.w = std::move(w);
listener.gcb = [hash,vcb,tokenbGlobal,this](const std::vector<Sp<Value>>& vals){
if (not vcb(vals)) {
#if OPENDHT_PROXY_CLIENT
cancelListen(hash, tokenbGlobal);
#endif
auto listener = std::unique_ptr<Listener>(new Listener());
listener->globalToken = listener_token_;
listener->tokenClassicDht = tokenClassic;
listener->tokenProxyDht = tokenProxy;
listener->gcb = vcb;
listener->hash = hash;
listener->f = f;
listener->w = w;
this->listeners_.emplace_back(std::move(listener));
ret_token->set_value(listener_token_);
listener_token_++;
return false;
}
return true;
};
#if OPENDHT_PROXY_CLIENT
if (use_proxy)
listener.tokenProxyDht = dht.listen(hash, listener.gcb, listener.f, listener.w);
else
#endif
listener.tokenClassicDht = dht.listen(hash, listener.gcb, listener.f, listener.w);
listeners_.emplace(tokenbGlobal, std::move(listener));
ret_token->set_value(tokenbGlobal);
});
}
cv.notify_all();
@@ -566,33 +585,22 @@ DhtRunner::listen(const std::string& key, GetCallback vcb, Value::Filter f, Wher
void
DhtRunner::cancelListen(InfoHash h, size_t token)
{
{
std::lock_guard<std::mutex> lck(storage_mtx);
#if OPENDHT_PROXY_CLIENT
auto it = listeners_.begin();
for (; it != listeners_.end(); ++it) {
auto& listener = *it;
if (listener->globalToken == token) {
break;
}
}
pending_ops.emplace([=](SecureDht&) {
auto it = listeners_.find(token);
if (it == listeners_.end()) return;
#endif // OPENDHT_PROXY_CLIENT
std::lock_guard<std::mutex> lck(storage_mtx);
#if OPENDHT_PROXY_CLIENT
pending_ops.emplace([=](SecureDht&) {
auto& listener = *it;
if (listener->tokenClassicDht != 0) {
dht_->cancelListen(h, listener->tokenClassicDht);
}
if (listener->tokenProxyDht != 0) {
dht_via_proxy_->cancelListen(h, listener->tokenProxyDht);
}
if (it->second.tokenClassicDht)
dht_->cancelListen(h, it->second.tokenClassicDht);
if (it->second.tokenProxyDht and dht_via_proxy_)
dht_via_proxy_->cancelListen(h, it->second.tokenProxyDht);
listeners_.erase(it);
});
#else
pending_ops.emplace([=](SecureDht& dht) {
dht.cancelListen(h, token);
pending_ops.emplace([=](SecureDht& dht) {
dht.cancelListen(h, token);
});
#endif // OPENDHT_PROXY_CLIENT
});
}
cv.notify_all();
}
@@ -759,30 +767,6 @@ DhtRunner::tryBootstrapContinuously()
});
}
std::vector<SockAddr>
DhtRunner::getAddrInfo(const std::string& host, const std::string& service)
{
std::vector<SockAddr> ips {};
if (host.empty())
return ips;
addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_DGRAM;
addrinfo* info = nullptr;
int rc = getaddrinfo(host.c_str(), service.c_str(), &hints, &info);
if(rc != 0)
throw std::invalid_argument(std::string("Error: `") + host + ":" + service + "`: " + gai_strerror(rc));
addrinfo* infop = info;
while (infop) {
ips.emplace_back(infop->ai_addr, infop->ai_addrlen);
infop = infop->ai_next;
}
freeaddrinfo(info);
return ips;
}
void
DhtRunner::bootstrap(const std::string& host, const std::string& service)
{
@@ -864,12 +848,13 @@ DhtRunner::findCertificate(InfoHash hash, std::function<void(const std::shared_p
}
void
DhtRunner::resetDht() {
DhtRunner::resetDht()
{
listeners_.clear();
#if OPENDHT_PROXY_CLIENT
use_proxy? dht_via_proxy_.reset() : dht_.reset();
#else
dht_.reset();
dht_via_proxy_.reset();
#endif // OPENDHT_PROXY_CLIENT
dht_.reset();
}
SecureDht*
@@ -882,56 +867,98 @@ DhtRunner::activeDht() const
#endif // OPENDHT_PROXY_CLIENT
}
#if OPENDHT_PROXY_CLIENT
void
DhtRunner::enableProxy(bool proxify) {
if (!dht_via_proxy_) {
auto dht_via_proxy = std::unique_ptr<DhtInterface>(
new DhtProxyClient(config_.proxy_server)
);
dht_via_proxy_ = std::unique_ptr<SecureDht>(new SecureDht(std::move(dht_via_proxy), config_.dht_config));
DhtRunner::enableProxy(bool proxify)
{
#if OPENDHT_PROXY_CLIENT
if (dht_via_proxy_) {
dht_via_proxy_->shutdown({});
}
if (proxify) {
// Init the proxy client
dht_via_proxy_->startProxy(config_.proxy_server);
auto dht_via_proxy = std::unique_ptr<DhtInterface>(
new DhtProxyClient([this]{
if (config_.threaded) {
{
std::lock_guard<std::mutex> lck(storage_mtx);
pending_ops_prio.emplace([=](SecureDht&) mutable {});
}
cv.notify_all();
}
}, config_.proxy_server, config_.push_node_id)
);
dht_via_proxy_ = std::unique_ptr<SecureDht>(new SecureDht(std::move(dht_via_proxy), config_.dht_config));
#if OPENDHT_PUSH_NOTIFICATIONS
if (not pushToken_.empty())
dht_via_proxy_->setPushNotificationToken(pushToken_);
#endif
// add current listeners
for (auto& listener: listeners_) {
auto tokenProxy = dht_via_proxy_->listen(listener->hash, listener->gcb, std::move(listener->f), std::move(listener->w));
listener->tokenProxyDht = tokenProxy;
}
for (auto& l: listeners_)
l.second.tokenProxyDht = dht_via_proxy_->listen(l.second.hash, l.second.gcb, l.second.f, l.second.w);
// and use it
use_proxy = proxify;
} else {
use_proxy = proxify;
loop_(); // Restart the classic DHT.
// We doesn't need to maintain the connection with the proxy.
// Delete it
dht_via_proxy_->shutdown({});
// update all proxyToken for all proxyListener
auto it = listeners_.begin();
for (; it != listeners_.end(); ++it) {
auto& listener = *it;
if (listener->tokenClassicDht == 0) {
pending_ops.emplace([it](SecureDht& dht) mutable {
auto& listener = *it;
auto token = dht.listen(listener->hash, listener->gcb, std::move(listener->f), std::move(listener->w));
listener->tokenClassicDht = token;
});
}
listener->tokenProxyDht = 0;
std::lock_guard<std::mutex> lck(storage_mtx);
if (not listeners_.empty()) {
pending_ops.emplace([this](SecureDht& /*dht*/) mutable {
if (not dht_)
return;
for (auto& l : listeners_) {
if (not l.second.tokenClassicDht) {
l.second.tokenClassicDht = dht_->listen(l.second.hash, l.second.gcb, l.second.f, l.second.w);
}
}
});
}
}
#else
if (proxify)
std::cerr << "DHT proxy requested but OpenDHT built without proxy support." << std::endl;
#endif
}
#endif // OPENDHT_PROXY_CLIENT
#if OPENDHT_PROXY_SERVER
void
DhtRunner::forwardAllMessages(bool forward)
{
#if OPENDHT_PROXY_SERVER
#if OPENDHT_PROXY_CLIENT
dht_via_proxy_->forwardAllMessages(forward);
if (dht_via_proxy_)
dht_via_proxy_->forwardAllMessages(forward);
#endif // OPENDHT_PROXY_CLIENT
dht_->forwardAllMessages(forward);
}
if (dht_)
dht_->forwardAllMessages(forward);
#endif // OPENDHT_PROXY_SERVER
}
/**
* Updates the push notification device token
*/
void
DhtRunner::setPushNotificationToken(const std::string& token) {
#if OPENDHT_PROXY_CLIENT && OPENDHT_PUSH_NOTIFICATIONS
pushToken_ = token;
if (dht_via_proxy_)
dht_via_proxy_->setPushNotificationToken(token);
#endif
}
void
DhtRunner::pushNotificationReceived(const std::map<std::string, std::string>& data) const
{
#if OPENDHT_PROXY_CLIENT && OPENDHT_PUSH_NOTIFICATIONS
if (dht_via_proxy_)
dht_via_proxy_->pushNotificationReceived(data);
#endif
}
void
DhtRunner::resubscribe(unsigned token)
{
#if OPENDHT_PROXY_CLIENT && OPENDHT_PUSH_NOTIFICATIONS
if (dht_via_proxy_)
dht_via_proxy_->resubscribe(token);
#endif
}
}
+2 -4
View File
@@ -27,14 +27,12 @@ namespace dht {
* Foreign nodes asking for updates about an InfoHash.
*/
struct Listener {
size_t sid;
time_point time;
Query query;
Listener(size_t sid, time_point t, Query&& q) : sid(sid), time(t), query(std::move(q)) {}
Listener(time_point t, Query&& q) : time(t), query(std::move(q)) {}
void refresh(size_t s, time_point t, Query&& q) {
sid = s;
void refresh(time_point t, Query&& q) {
time = t;
query = std::move(q);
}
+9 -4
View File
@@ -126,8 +126,10 @@ RequestAnswer::RequestAnswer(ParsedMessage&& msg)
: ntoken(std::move(msg.token)), values(std::move(msg.values)), fields(std::move(msg.fields)),
nodes4(std::move(msg.nodes4)), nodes6(std::move(msg.nodes6)) {}
NetworkEngine::NetworkEngine(Logger& log, Scheduler& scheduler) : myid(zeroes), DHT_LOG(log), scheduler(scheduler) {}
NetworkEngine::NetworkEngine(InfoHash& myid, NetId net, int s, int s6, Logger& log, Scheduler& scheduler,
NetworkEngine::NetworkEngine(Logger& log, Scheduler& scheduler, const int& s, const int& s6)
: myid(zeroes), DHT_LOG(log), scheduler(scheduler), dht_socket(s), dht_socket6(s6)
{}
NetworkEngine::NetworkEngine(InfoHash& myid, NetId net, const int& s, const int& s6, Logger& log, Scheduler& scheduler,
decltype(NetworkEngine::onError) onError,
decltype(NetworkEngine::onNewNode) onNewNode,
decltype(NetworkEngine::onReportedAddr) onReportedAddr,
@@ -496,7 +498,7 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
sendPong(from, msg->tid);
break;
case MessageType::FindNode: {
DHT_LOG.d(msg->target, node->id, "[node %s] got 'find' request for %s (%d)", node->toString().c_str(), msg->target.toString().c_str(), msg->want);
//DHT_LOG.d(msg->target, node->id, "[node %s] got 'find' request for %s (%d)", node->toString().c_str(), msg->target.toString().c_str(), msg->want);
++in_stats.find;
RequestAnswer answer = onFindNode(node, msg->target, msg->want);
auto nnodes = bufferNodes(from.getFamily(), msg->target, msg->want, answer.nodes4, answer.nodes6);
@@ -504,7 +506,7 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
break;
}
case MessageType::GetValues: {
DHT_LOG.d(msg->info_hash, node->id, "[node %s] got 'get' request for %s", node->toString().c_str(), msg->info_hash.toString().c_str());
//DHT_LOG.d(msg->info_hash, node->id, "[node %s] got 'get' request for %s", node->toString().c_str(), msg->info_hash.toString().c_str());
++in_stats.get;
RequestAnswer answer = onGetValues(node, msg->info_hash, msg->want, msg->query);
auto nnodes = bufferNodes(from.getFamily(), msg->info_hash, msg->want, answer.nodes4, answer.nodes6);
@@ -589,6 +591,9 @@ NetworkEngine::send(const char *buf, size_t len, int flags, const SockAddr& addr
if (sendto(s, buf, len, flags, addr.get(), addr.getLength()) == -1) {
int err = errno;
DHT_LOG.e("Can't send message to %s: %s", addr.toString().c_str(), strerror(err));
if (err == EPIPE) {
throw SocketException(EPIPE);
}
return err;
}
return 0;
+4 -5
View File
@@ -30,11 +30,10 @@ constexpr std::chrono::minutes Node::NODE_EXPIRE_TIME;
constexpr std::chrono::minutes Node::NODE_GOOD_TIME;
constexpr std::chrono::seconds Node::MAX_RESPONSE_TIME;
Node::Node(const InfoHash& id, const SockAddr& addr, bool client)
: id(id), addr(addr), is_client(client), sockets_()
{
crypto::random_device rd;
thread_local crypto::random_device rd {};
transaction_id = std::uniform_int_distribution<Tid>{1}(rd);
}
@@ -134,18 +133,18 @@ Node::openSocket(SocketCb&& cb)
if (++transaction_id == 0)
transaction_id = 1;
auto sock = Socket(std::move(cb));
auto sock = std::make_shared<Socket>(std::move(cb));
auto s = sockets_.emplace(transaction_id, std::move(sock));
if (not s.second)
s.first->second = std::move(sock);
return transaction_id;
}
Socket*
Sp<Socket>
Node::getSocket(Tid id)
{
auto it = sockets_.find(id);
return it == sockets_.end() ? nullptr : &it->second;
return it == sockets_.end() ? nullptr : it->second;
}
void
+9 -128
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
* Copyright (C) 2014-2018 Savoir-faire Linux Inc.
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -74,6 +74,7 @@ struct Dht::SearchNode {
time_point last_get_reply {time_point::min()}; /* last time received valid token */
bool candidate {false}; /* A search node is candidate if the search is/was synced and this
node is a new candidate for inclusion. */
Sp<Scheduler::Job> syncJob {};
SearchNode() : node() {}
SearchNode(const SearchNode&) = delete;
@@ -93,11 +94,17 @@ struct Dht::SearchNode {
/**
* Can we use this node to listen/announce now ?
*/
bool isSynced(time_point now) const {
bool isSynced(const time_point& now) const {
return not node->isExpired() and
not token.empty() and last_get_reply >= now - Node::NODE_EXPIRE_TIME;
}
time_point getSyncTime(const time_point& now) const {
if (node->isExpired() or token.empty())
return now;
return last_get_reply + Node::NODE_EXPIRE_TIME;
}
/**
* Could a particular "get" request be sent to this node now ?
*
@@ -421,8 +428,6 @@ struct Dht::Search {
done = true;
}
time_point getUpdateTime(time_point now) const;
bool isAnnounced(Value::Id id) const;
bool isListening(time_point now) const;
@@ -460,26 +465,6 @@ struct Dht::Search {
return count;
}
/**
* Returns the time of the next "announce" event for this search,
* or time_point::max() if no such event is planned.
* Only makes sense when the search is synced.
*/
time_point getAnnounceTime(time_point now) const;
/**
* Returns the time of the next "listen" event for this search,
* or time_point::max() if no such event is planned.
* Only makes sense when the search is synced.
*/
time_point getListenTime(time_point now) const;
/**
* Returns the time of the next event for this search,
* or time_point::max() if no such event is planned.
*/
time_point getNextStepTime(time_point now) const;
/**
* Removes a node which have been expired for at least
* NODE::NODE_EXPIRE_TIME minutes. The search for an expired node starts
@@ -721,36 +706,6 @@ Dht::Search::isDone(const Get& get) const
return true;
}
time_point
Dht::Search::getUpdateTime(time_point now) const
{
time_point ut = time_point::max();
const auto last_get = getLastGetTime();
unsigned i = 0, t = 0, d = 0;
const auto solicited_nodes = currentlySolicitedNodeCount();
for (const auto& sn : nodes) {
if (sn.node->isExpired() or (sn.candidate and t >= TARGET_NODES))
continue;
auto pending = sn.pendingGet();
if (sn.last_get_reply < std::max(now - Node::NODE_EXPIRE_TIME, last_get) or pending) {
// not isSynced
if (not pending and solicited_nodes < MAX_REQUESTED_SEARCH_NODES)
ut = std::min(ut, now);
if (not sn.candidate)
d++;
} else
ut = std::min(ut, sn.last_get_reply + Node::NODE_EXPIRE_TIME);
t++;
if (not sn.candidate and ++i == TARGET_NODES)
break;
}
if (not callbacks.empty() and d == 0)
// If all synced/updated but some callbacks remain, step now to clear them
return now;
return ut;
}
bool
Dht::Search::isAnnounced(Value::Id id) const
{
@@ -790,78 +745,4 @@ Dht::Search::isListening(time_point now) const
return i;
}
time_point
Dht::Search::getAnnounceTime(time_point now) const
{
if (nodes.empty())
return time_point::max();
time_point ret {time_point::max()};
for (const auto& a : announce) {
if (!a.value) continue;
unsigned i = 0, t = 0;
for (const auto& n : nodes) {
if (not n.isSynced(now) or (n.candidate and t >= TARGET_NODES))
continue;
ret = std::min(ret, n.getAnnounceTime(a.value->id));
t++;
if (not n.candidate and ++i == TARGET_NODES)
break;
}
}
return ret;
}
time_point
Dht::Search::getListenTime(time_point now) const
{
if (listeners.empty())
return time_point::max();
time_point listen_time {time_point::max()};
unsigned i = 0, t = 0;
for (const auto& sn : nodes) {
if (not sn.isSynced(now) or (sn.candidate and t >= LISTEN_NODES))
continue;
for (auto& l : listeners)
listen_time = std::min(listen_time, sn.getListenTime(l.second.query));
t++;
if (not sn.candidate and ++i == LISTEN_NODES)
break;
}
return listen_time;
}
time_point
Dht::Search::getNextStepTime(time_point now) const
{
auto next_step = time_point::max();
if (expired or done)
return next_step;
auto ut = getUpdateTime(now);
if (ut != time_point::max()) {
//std::cout << id.toString() << " IPv" << (af==AF_INET?"4":"6") << " update time in " << print_dt(ut - now) << " s" << std::endl;
next_step = std::min(next_step, ut);
}
if (isSynced(now))
{
auto at = getAnnounceTime(now);
if (at != time_point::max()) {
//std::cout << id.toString() << " IPv" << (af==AF_INET?"4":"6") << " announce time in " << print_dt(at - now) << " s" << std::endl;
next_step = std::min(next_step, at);
}
auto lt = getListenTime(now);
if (lt != time_point::max()) {
//std::cout << id.toString() << " IPv" << (af==AF_INET?"4":"6") << " listen time in " << print_dt(lt - now) << " s" << std::endl;
next_step = std::min(next_step, lt);
}
}
return next_step;
}
}
+3 -2
View File
@@ -1,7 +1,8 @@
/*
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -276,7 +277,7 @@ SecureDht::get(const InfoHash& id, GetCallback cb, DoneCallback donecb, Value::F
}
size_t
SecureDht::listen(const InfoHash& id, GetCallback cb, Value::Filter&& f, Where&& w)
SecureDht::listen(const InfoHash& id, GetCallback cb, Value::Filter f, Where w)
{
return dht_->listen(id, getCallbackFilter(cb, std::forward<Value::Filter>(f)), {}, std::forward<Where>(w));
}
+26
View File
@@ -27,6 +27,8 @@
namespace dht {
static constexpr std::array<uint8_t, 12> MAPPED_IPV4_PREFIX {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}};
std::vector<SockAddr>
SockAddr::resolve(const std::string& host, const std::string& service)
{
@@ -139,6 +141,30 @@ SockAddr::isPrivate() const
}
}
bool
SockAddr::isMappedIPv4() const
{
if (getFamily() != AF_INET6)
return false;
const uint8_t* addr6 = reinterpret_cast<const uint8_t*>(&getIPv6().sin6_addr);
return std::equal(MAPPED_IPV4_PREFIX.begin(), MAPPED_IPV4_PREFIX.end(), addr6);
}
SockAddr
SockAddr::getMappedIPv4() const
{
if (not isMappedIPv4())
return *this;
SockAddr ret;
ret.setFamily(AF_INET);
ret.setPort(getPort());
auto addr6 = reinterpret_cast<const uint8_t*>(&getIPv6().sin6_addr);
auto addr4 = reinterpret_cast<uint8_t*>(&ret.getIPv4().sin_addr);
addr6 += MAPPED_IPV4_PREFIX.size();
std::copy_n(addr6, sizeof(in_addr), addr4);
return ret;
}
bool operator==(const SockAddr& a, const SockAddr& b) {
return a.equals(b);
}
+2 -2
View File
@@ -22,7 +22,7 @@
#include "default_types.h"
#include "securedht.h" // print certificate ID
#if OPENDHT_PROXY_SERVER
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
#include "base64.h"
#endif //OPENDHT_PROXY_SERVER
@@ -172,7 +172,7 @@ Value::msgpack_unpack_body(const msgpack::object& o)
}
}
#if OPENDHT_PROXY_SERVER
#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
Value::Value(Json::Value& json)
{
try {
+94 -37
View File
@@ -1,8 +1,9 @@
/*
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
*
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -59,14 +60,24 @@ void print_help() {
#if OPENDHT_PROXY_SERVER
std::cout << std::endl << "Operations with the proxy:" << std::endl
<< " pst [port] Start the proxy interface on port." << std::endl
<< " psp [port] Stop the proxy interface on port." << std::endl;
#if OPENDHT_PUSH_NOTIFICATIONS
<< " pst [port] <pushServer> Start the proxy interface on port." << std::endl
#else
<< " pst [port] Start the proxy interface on port." << std::endl
#endif // OPENDHT_PUSH_NOTIFICATIONS
<< " psp [port] Stop the proxy interface on port." << std::endl;
#endif //OPENDHT_PROXY_SERVER
#if OPENDHT_PROXY_CLIENT
std::cout << std::endl << "Operations with the proxy:" << std::endl
<< " stt [server_address] Start the proxy client." << std::endl
<< " stp Stop the proxy client." << std::endl;
#if OPENDHT_PUSH_NOTIFICATIONS
<< " stt [server_address] <device_key> Start the proxy client." << std::endl
<< " rs [token] Resubscribe to opendht." << std::endl
<< " rp [push_notification] Inject a push notification in Opendht." << std::endl
#else
<< " stt [server_address] Start the proxy client." << std::endl
#endif // OPENDHT_PUSH_NOTIFICATIONS
<< " stp Stop the proxy client." << std::endl;
#endif //OPENDHT_PROXY_CLIENT
std::cout << std::endl << "Operations on the DHT:" << std::endl
@@ -79,14 +90,20 @@ void print_help() {
<< " s <key> <str> Put string value at <key>, signed with our generated private key." << std::endl
<< " e <key> <dest> <str> Put string value at <key>, encrypted for <dest> with its public key (if found)." << std::endl;
#ifdef OPENDHT_INDEXATION
std::cout << std::endl << "Indexation operations on the DHT:" << std::endl
<< " il <name> <key> [exact match] Lookup the index named <name> with the key <key>." << std::endl
<< " Set [exact match] to 'false' for inexact match lookup." << std::endl
<< " ii <name> <key> <value> Inserts the value <value> under the key <key> in the index named <name>." << std::endl
<< std::endl;
#endif
}
void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params)
void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params
#if OPENDHT_PROXY_SERVER
, std::map<in_port_t, std::unique_ptr<DhtProxyServer>>& proxies
#endif
)
{
print_node_info(dht, params);
std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl << std::endl;
@@ -96,19 +113,9 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params)
using_history();
#endif
#ifdef OPENDHT_INDEXATION
std::map<std::string, indexation::Pht> indexes;
#if OPENDHT_PROXY_SERVER
std::map<in_port_t, std::unique_ptr<DhtProxyServer>> proxies;
if (params.proxyserver != 0) {
proxies.emplace(params.proxyserver, new DhtProxyServer(dht, params.proxyserver));
}
#endif //OPENDHT_PROXY_SERVER
#if OPENDHT_PROXY_CLIENT
if (!params.proxyclient.empty()) {
dht->setProxyServer(params.proxyclient);
dht->enableProxy(true);
}
#endif //OPENDHT_PROXY_CLIENT
#endif
while (true)
{
@@ -118,7 +125,7 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params)
break;
std::istringstream iss(line);
std::string op, idstr, value, index, keystr;
std::string op, idstr, value, index, keystr, pushServer, deviceKey;
iss >> op;
if (op == "x" || op == "exit" || op == "quit") {
@@ -188,10 +195,18 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params)
}
#if OPENDHT_PROXY_SERVER
else if (op == "pst") {
iss >> idstr;
#if OPENDHT_PUSH_NOTIFICATIONS
iss >> idstr >> pushServer;
#else
iss >> idstr;
#endif // OPENDHT_PUSH_NOTIFICATIONS
try {
unsigned int port = std::stoi(idstr);
proxies.emplace(port, new DhtProxyServer(dht, port));
#if OPENDHT_PUSH_NOTIFICATIONS
proxies.emplace(port, std::unique_ptr<DhtProxyServer>(new DhtProxyServer(dht, port, pushServer)));
#else
proxies.emplace(port, std::unique_ptr<DhtProxyServer>(new DhtProxyServer(dht, port)));
#endif // OPENDHT_PUSH_NOTIFICATIONS
} catch (...) { }
continue;
} else if (op == "psp") {
@@ -206,14 +221,26 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params)
#endif //OPENDHT_PROXY_SERVER
#if OPENDHT_PROXY_CLIENT
else if (op == "stt") {
iss >> idstr;
dht->setProxyServer(idstr);
dht->enableProxy(true);
continue;
} else if (op == "stp") {
dht->enableProxy(false);
continue;
}
#if OPENDHT_PUSH_NOTIFICATIONS
else if (op == "rp") {
iss >> value;
dht->pushNotificationReceived({{"token", value}});
continue;
} else if (op == "re") {
iss >> value;
try {
unsigned token = std::stoul(value);
dht->resubscribe(token);
} catch (...) { }
continue;
}
#endif // OPENDHT_PUSH_NOTIFICATIONS
#endif //OPENDHT_PROXY_CLIENT
if (op.empty())
@@ -227,7 +254,13 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params)
}
dht::InfoHash id;
if (op == "il" or op == "ii") {
if (op == "cl") {
std::string hash, rem;
iss >> hash >> rem;
dht->cancelListen(dht::InfoHash(hash), std::stoul(rem));
}
#ifdef OPENDHT_INDEXATION
else if (op == "il" or op == "ii") {
// Pht syntax
iss >> index >> keystr;
auto new_index = std::find_if(indexes.begin(), indexes.end(),
@@ -249,6 +282,7 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params)
} catch (std::invalid_argument& e) { std::cout << e.what() << std::endl; }
}
}
#endif
else {
// Dht syntax
iss >> idstr;
@@ -301,12 +335,8 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params)
std::cout << "\t" << *value << std::endl;
return true;
}, {}, dht::Where {std::move(rem)});
std::cout << "Listening, token: " << token.get() << std::endl;
}
else if (op == "cl") {
std::string rem;
iss >> rem;
dht->cancelListen(id, std::stoul(rem));
auto t = token.get();
std::cout << "Listening, token: " << t << std::endl;
}
else if (op == "p") {
std::string v;
@@ -377,6 +407,7 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params)
std::cout << "Announce: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
});
}
#ifdef OPENDHT_INDEXATION
else if (op == "il") {
std::string exact_match;
iss >> exact_match;
@@ -422,6 +453,7 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params)
catch (std::invalid_argument& e) { std::cout << e.what() << std::endl; }
catch (std::out_of_range& e) { }
}
#endif
}
std::cout << std::endl << "Stopping node..." << std::endl;
@@ -442,6 +474,7 @@ main(int argc, char **argv)
print_usage();
return 0;
}
if (params.daemonize) {
daemonize();
} else if (params.service) {
@@ -451,10 +484,19 @@ main(int argc, char **argv)
dht::crypto::Identity crt {};
if (params.generate_identity) {
auto ca_tmp = dht::crypto::generateEcIdentity("DHT Node CA");
crt = dht::crypto::generateEcIdentity("DHT Node", ca_tmp);
crt = dht::crypto::generateIdentity("DHT Node", ca_tmp);
}
dht->run(params.port, crt, true, params.network);
dht::DhtRunner::Config config;
config.dht_config.node_config.network = params.network;
config.dht_config.id = crt;
config.threaded = true;
config.proxy_server = params.proxyclient;
config.push_node_id = "dhtnode";
if (not params.proxyclient.empty())
dht->setPushNotificationToken(params.devicekey);
dht->run(params.port, config);
if (params.log) {
if (params.syslog or (params.daemonize and params.logfile.empty()))
@@ -470,12 +512,27 @@ main(int argc, char **argv)
dht->bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str());
}
if (params.daemonize or params.service) {
while (runner.wait());
} else {
cmd_loop(dht, params);
#if OPENDHT_PROXY_SERVER
std::map<in_port_t, std::unique_ptr<DhtProxyServer>> proxies;
#endif
if (params.proxyserver != 0) {
#if OPENDHT_PROXY_SERVER
proxies.emplace(params.proxyserver, std::unique_ptr<DhtProxyServer>(new DhtProxyServer(dht, params.proxyserver, params.pushserver)));
#else
std::cerr << "DHT proxy server requested but OpenDHT built without proxy server support." << std::endl;
exit(EXIT_FAILURE);
#endif
}
if (params.daemonize or params.service)
while (runner.wait());
else
cmd_loop(dht, params
#if OPENDHT_PROXY_SERVER
, proxies
#endif
);
} catch(const std::exception&e) {
std::cerr << std::endl << e.what() << std::endl;
}
+13
View File
@@ -2,6 +2,7 @@
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
*
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -89,6 +90,7 @@ std::map<std::string, std::string> parseStringMap(std::string mapString) {
return map;
}
#ifdef OPENDHT_INDEXATION
dht::indexation::Pht::Key createPhtKey(std::map<std::string, std::string> pht_key_str_map) {
dht::indexation::Pht::Key pht_key;
for (auto f : pht_key_str_map) {
@@ -97,6 +99,7 @@ dht::indexation::Pht::Key createPhtKey(std::map<std::string, std::string> pht_ke
}
return pht_key;
}
#endif
bool isInfoHash(const dht::InfoHash& h) {
if (not h) {
@@ -121,6 +124,8 @@ struct dht_params {
std::pair<std::string, std::string> bootstrap {};
in_port_t proxyserver {0};
std::string proxyclient {};
std::string pushserver {};
std::string devicekey {};
};
static const constexpr struct option long_options[] = {
@@ -136,6 +141,8 @@ static const constexpr struct option long_options[] = {
{"syslog", no_argument , nullptr, 'L'},
{"proxyserver",required_argument, nullptr, 'S'},
{"proxyclient",required_argument, nullptr, 'C'},
{"pushserver", required_argument, nullptr, 'P'},
{"devicekey", required_argument, nullptr, 'D'},
{nullptr, 0 , nullptr, 0}
};
@@ -161,9 +168,15 @@ parseArgs(int argc, char **argv) {
std::cout << "Invalid port: " << port_arg << std::endl;
}
break;
case 'P':
params.pushserver = optarg;
break;
case 'C':
params.proxyclient = optarg;
break;
case 'D':
params.devicekey = optarg;
break;
case 'n':
params.network = strtoul(optarg, nullptr, 0);
break;