diff --git a/.gitignore b/.gitignore
index 9d50e4d5..c79e0d15 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,6 +48,11 @@ Makefile.in
# Python
*.pyc
+python/opendht.cpp
+python/setup.py
+
+# Doxygen
+doc/Doxyfile
# git backup files
*.orig
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6f607dbb..4332712d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,7 @@
cmake_minimum_required (VERSION 3.1)
project (opendht)
set (opendht_VERSION_MAJOR 1)
-set (opendht_VERSION_MINOR 3.5)
+set (opendht_VERSION_MINOR 6.0)
set (opendht_VERSION ${opendht_VERSION_MAJOR}.${opendht_VERSION_MINOR})
set (PACKAGE_VERSION ${opendht_VERSION})
set (VERSION "${opendht_VERSION}")
@@ -15,9 +15,18 @@ option (OPENDHT_TOOLS "Build DHT tools" ON)
option (OPENDHT_SYSTEMD "Install systemd module" OFF)
option (OPENDHT_ARGON2 "Use included argon2 sources" OFF)
option (OPENDHT_LTO "Build with LTO" OFF)
+option (OPENDHT_SANITIZE "Build with address sanitizer and stack protector" OFF)
+option (OPENDHT_PROXY_SERVER "Enable DHT proxy server, use Restbed and jsoncpp" OFF)
+option (OPENDHT_PUSH_NOTIFICATIONS "Enable push notifications support" OFF)
+option (OPENDHT_PROXY_SERVER_IDENTITY "Allow clients to use the node identity" OFF)
+option (OPENDHT_PROXY_CLIENT "Enable DHT proxy client, use Restbed and jsoncpp" OFF)
+
+find_package(Doxygen)
+option (OPENDHT_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" ${DOXYGEN_FOUND})
# Dependencies
list (APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
+find_package (Threads)
find_package (PkgConfig)
find_package (GnuTLS 3.3 REQUIRED)
pkg_search_module (Nettle nettle)
@@ -36,11 +45,18 @@ if (NOT OPENDHT_ARGON2)
set(OPENDHT_ARGON2 ON)
endif()
endif ()
+if (OPENDHT_PROXY_SERVER OR OPENDHT_PROXY_CLIENT)
+ find_package(Restbed REQUIRED)
+ pkg_search_module(Jsoncpp jsoncpp)
+endif()
# Build flags
set (CMAKE_CXX_STANDARD 11)
set (CMAKE_CXX_STANDARD_REQUIRED on)
-set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -Wno-return-type -Wall -Wextra -Wnon-virtual-dtor -pedantic-errors -fvisibility=hidden")
+set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-return-type -Wall -Wextra -Wnon-virtual-dtor -pedantic-errors -fvisibility=hidden")
+if (OPENDHT_SANITIZE)
+ set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fstack-protector-strong")
+endif ()
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMSGPACK_DISABLE_LEGACY_NIL -DMSGPACK_DISABLE_LEGACY_CONVERT")
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
@@ -69,7 +85,14 @@ endif ()
if (Nettle_INCLUDE_DIRS)
include_directories (SYSTEM "${Nettle_INCLUDE_DIRS}")
endif ()
+if (RESTBED_INCLUDE)
+ include_directories (SYSTEM "${RESTBED_INCLUDE}")
+endif ()
+if (Jsoncpp_INCLUDE_DIRS)
+ include_directories (SYSTEM "${Jsoncpp_INCLUDE_DIRS}")
+endif ()
link_directories (${Nettle_LIBRARY_DIRS})
+link_directories (${Jsoncpp_LIBRARY_DIRS})
include_directories (
./
include/
@@ -85,6 +108,7 @@ set (libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
set (includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
set (bindir "${CMAKE_INSTALL_FULL_BINDIR}")
set (sysconfdir "${CMAKE_INSTALL_FULL_SYSCONFDIR}")
+set (top_srcdir "${CMAKE_CURRENT_SOURCE_DIR}")
# Sources
list (APPEND opendht_SOURCES
@@ -98,6 +122,8 @@ list (APPEND opendht_SOURCES
src/storage.h
src/listener.h
src/search.h
+ src/net.h
+ src/parsed_message.h
src/request.h
src/callbacks.cpp
src/routing_table.cpp
@@ -120,11 +146,11 @@ list (APPEND opendht_HEADERS
include/opendht/node.h
include/opendht/value.h
include/opendht/dht.h
+ include/opendht/dht_interface.h
include/opendht/callbacks.h
include/opendht/routing_table.h
include/opendht/node_cache.h
include/opendht/network_engine.h
- include/opendht/net.h
include/opendht/scheduler.h
include/opendht/rate_limiter.h
include/opendht/securedht.h
@@ -134,6 +160,48 @@ list (APPEND opendht_HEADERS
include/opendht/indexation/pht.h
)
+if (OPENDHT_PROXY_SERVER)
+ add_definitions(-DOPENDHT_PROXY_SERVER=true)
+ if (OPENDHT_PROXY_SERVER_IDENTITY)
+ add_definitions(-DOPENDHT_PROXY_SERVER_IDENTITY=true)
+ else ()
+ add_definitions(-DOPENDHT_PROXY_SERVER_IDENTITY=false)
+ endif()
+ list (APPEND opendht_HEADERS
+ include/opendht/dht_proxy_server.h
+ )
+ list (APPEND opendht_SOURCES
+ src/dht_proxy_server.cpp
+ )
+ if (OPENDHT_PUSH_NOTIFICATIONS)
+ add_definitions(-DOPENDHT_PUSH_NOTIFICATIONS=true)
+ else ()
+ add_definitions(-DOPENDHT_PUSH_NOTIFICATIONS=false)
+ endif ()
+else ()
+ add_definitions(-DENABLE_PROXY_SERVER=false)
+endif ()
+
+if (OPENDHT_PROXY_CLIENT)
+ add_definitions(-DOPENDHT_PROXY_CLIENT=true)
+ list (APPEND opendht_HEADERS
+ include/opendht/dht_proxy_client.h
+ )
+ list (APPEND opendht_SOURCES
+ src/dht_proxy_client.cpp
+ )
+else ()
+ add_definitions(-DOPENDHT_PROXY_CLIENT=false)
+endif ()
+
+if (OPENDHT_PROXY_SERVER OR OPENDHT_PROXY_CLIENT)
+ list (APPEND opendht_SOURCES
+ src/base64.h
+ src/base64.cpp
+ )
+endif ()
+
+
if(OPENDHT_ARGON2)
# make sure argon2 submodule is up to date and initialized
message("Initializing Argon2 submodule")
@@ -167,7 +235,7 @@ if (OPENDHT_STATIC)
target_link_libraries(opendht-static ${argon2_LIBRARIES})
target_include_directories(opendht-static SYSTEM PRIVATE ${argon2_INCLUDE_DIRS})
endif ()
- target_link_libraries(opendht-static ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES})
+ target_link_libraries(opendht-static ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} ${Restbed_LIBRARIES} ${Jsoncpp_LIBRARIES})
install (TARGETS opendht-static DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht)
endif ()
@@ -180,15 +248,15 @@ if (OPENDHT_SHARED)
set_target_properties (opendht PROPERTIES SOVERSION ${opendht_VERSION_MAJOR} VERSION ${opendht_VERSION})
target_compile_definitions(opendht PRIVATE OPENDHT_BUILD)
if (OPENDHT_LTO)
- target_link_libraries(opendht -flto -fuse-linker-plugin)
+ target_link_libraries(opendht PRIVATE -flto -fuse-linker-plugin)
endif ()
if (OPENDHT_ARGON2)
target_include_directories(opendht SYSTEM PRIVATE argon2)
else ()
- target_link_libraries(opendht ${argon2_LIBRARIES})
+ target_link_libraries(opendht PRIVATE ${argon2_LIBRARIES})
target_include_directories(opendht SYSTEM PRIVATE ${argon2_INCLUDE_DIRS})
endif ()
- target_link_libraries(opendht ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES})
+ target_link_libraries(opendht PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} ${Restbed_LIBRARIES} ${Jsoncpp_LIBRARIES})
install (TARGETS opendht DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht)
endif ()
diff --git a/MSVC/opendht.vcxproj b/MSVC/opendht.vcxproj
index ce94bdd5..38158f9c 100644
--- a/MSVC/opendht.vcxproj
+++ b/MSVC/opendht.vcxproj
@@ -19,11 +19,14 @@
+
+
+
@@ -36,12 +39,16 @@
+
+
+
+
@@ -50,14 +57,18 @@
-
+
+
+
+
+
@@ -211,9 +222,9 @@ copy ..\include\opendht.h $(OutDir)\include\
true
true
true
- $(ProjectDir)contrib\build\include;$(ProjectDir)..\include;$(ProjectDir)..\include\opendht;$(ProjectDir)contrib\build\msgpack-c\include;$(ProjectDir)contrib\build\argon2\include
+ $(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
_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)
- 4804;4800;4101;4267;4244;4503;
+ 4804;4800;4101;4267;4244;4503;4273;
-D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions)
$(OutDir)\lib\x64\$(TargetName).pdb
diff --git a/MSVC/opendht.vcxproj.filters b/MSVC/opendht.vcxproj.filters
index 3cec5eab..c0c9bb86 100644
--- a/MSVC/opendht.vcxproj.filters
+++ b/MSVC/opendht.vcxproj.filters
@@ -31,9 +31,6 @@
Source Files
-
- Source Files
-
Source Files
@@ -46,80 +43,102 @@
Source Files
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files\indexation
+
+
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
+
+
+
+
+
+ Source Files
- Header Files
+ Source Files
-
- Header Files
+
+ Source Files
-
- Header Files
+
+ Source Files
-
- Header Files
-
-
- Header Files
-
-
- Header Files
+
+ Source Files
Header Files
-
- Header Files
+
+ Header Files\opendht
+
+
+ Header Files\opendht
+
+
+ Header Files\opendht
+
+
+ Header Files\opendht
+
+
+ Header Files\opendht
+
+
+ Header Files\opendht
+
+
+ Header Files\opendht
+
+
+ Header Files\opendht
+
+
+ Header Files\opendht
+
+
+ Header Files\opendht
+
+
+ Header Files\opendht
+
+
+ Header Files\opendht
- Header Files
-
-
- Header Files
+ Header Files\opendht
- Header Files
+ Header Files\opendht
- Header Files
+ Header Files\opendht
- Header Files
-
-
- Header Files
+ Header Files\opendht
- Header Files
-
-
- Header Files
+ Header Files\opendht
- Header Files
+ Header Files\opendht
- Header Files
+ Header Files\opendht
+
+ Header Files\opendht\indexation
+
+
+
@@ -128,5 +147,14 @@
{d1ab5bfe-3ab1-45ee-9324-b4b071887668}
+
+ {939134d3-d5a2-4458-bca8-a10ea9042ea2}
+
+
+ {0b46e2d0-4874-404c-bae3-fae8d49b85b8}
+
+
+ {03bd5139-2ae5-41b0-a77b-944814e1b2b6}
+
\ No newline at end of file
diff --git a/README.md b/README.md
index c556cdba..3402b065 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
- OpenDHT
+ OpenDHT
A lightweight C++11 Distributed Hash Table implementation originally based on https://github.com/jech/dht by Juliusz Chroboczek.
@@ -18,7 +18,7 @@ See the wiki:
#### How-to build and install
-Build instructions :
+Build instructions:
#### How-to build a simple client app
```bash
@@ -91,7 +91,9 @@ for r in results:
- msgpack-c 1.2+, used for data serialization.
- GnuTLS 3.3+, used for cryptographic operations.
- Nettle 2.4+, a GnuTLS dependency for crypto.
-- Build tested with GCC 5.4+ (GNU/Linux, Android, Windows with MinGW), Clang/LLVM (Linux, macOS).
+- (optional) restbed used for the REST API. commit fb84213e170bc171fecd825a8e47ed9f881a12cd (https://github.com/AmarOk1412/restbed/tree/async_read_until)
+- (optional) jsoncpp 1.7.4-3+, used for the REST API.
+- Build tested with GCC 5.2+ (GNU/Linux, Android, Windows with MinGW), Clang/LLVM (Linux, macOS).
- Build tested with Microsoft Visual Studio 2015
## Contact
diff --git a/cmake/FindRestbed.cmake b/cmake/FindRestbed.cmake
new file mode 100644
index 00000000..05f1e814
--- /dev/null
+++ b/cmake/FindRestbed.cmake
@@ -0,0 +1,17 @@
+find_path (RESTBED_INCLUDE restbed
+ HINTS
+ "/usr/include"
+ "/usr/local/include"
+ "/opt/local/include"
+)
+
+if (RESTBED_INCLUDE)
+ message(STATUS "${green}Found Restbed: ${RESTBED_INCLUDE}")
+else()
+ message(FATAL_ERROR "${red}Failed to locate Restbed.}")
+endif()
+
+if (RESTBED_INCLUDE)
+ set(RESTBED_FOUND TRUE)
+ set(Restbed_LIBRARIES restbed)
+endif()
diff --git a/configure.ac b/configure.ac
index dbc5359c..7d051d72 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,7 +1,7 @@
dnl define macros
m4_define([opendht_major_version], 1)
-m4_define([opendht_minor_version], 3)
-m4_define([opendht_patch_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])
@@ -128,6 +128,54 @@ AM_COND_IF([ENABLE_TOOLS], [
])
])
+AC_ARG_ENABLE([proxy_server], AS_HELP_STRING([--enable-proxy-server], [Enable proxy server ability]), proxy_server=yes, proxy_server=no)
+AM_CONDITIONAL(ENABLE_PROXY_SERVER, test x$proxy_server == xyes)
+AC_ARG_ENABLE([push_notifications], AS_HELP_STRING([--enable-push-notifications], [Enable push notifications support]), push_notifications=yes, push_notifications=no)
+AM_CONDITIONAL(ENABLE_PUSH_NOTIFICATIONS, test x$push_notifications == xyes)
+AC_ARG_ENABLE([proxy_server_identity], AS_HELP_STRING([--enable-proxy-server-identity],
+ [Enable proxy server ability]), proxy_server_identity=yes, proxy_server_identity=no)
+AM_CONDITIONAL(ENABLE_PROXY_SERVER_IDENTITY, test x$proxy_server_identity == xyes)
+AC_ARG_ENABLE([proxy_client], AS_HELP_STRING([--enable-proxy-client], [Enable proxy client ability]), proxy_client=yes, proxy_client=no)
+AM_CONDITIONAL(ENABLE_PROXY_CLIENT, test x$proxy_client == xyes)
+
+AM_COND_IF([ENABLE_PROXY_SERVER], [
+ AC_CHECK_LIB(restbed, exit,, AC_MSG_ERROR([Missing restbed files]))
+ PKG_CHECK_MODULES([Jsoncpp], [jsoncpp >= 1.7.2])
+ CPPFLAGS+=" -DOPENDHT_PROXY_SERVER=true -ljsoncpp -lrestbed"
+], [
+ CPPFLAGS+=" -DOPENDHT_PROXY_SERVER=false"
+])
+
+AM_COND_IF([ENABLE_PUSH_NOTIFICATIONS], [
+ CPPFLAGS+=" -DOPENDHT_PUSH_NOTIFICATIONS=true"
+], [
+ CPPFLAGS+=" -DOPENDHT_PUSH_NOTIFICATIONS=false"
+])
+
+AC_ARG_ENABLE([proxy_server_identity], AS_HELP_STRING([--enable-proxy-server-identity],
+ [Enable proxy server ability]), proxy_server_identity=yes, proxy_server_identity=no)
+AM_CONDITIONAL(ENABLE_PROXY_SERVER_IDENTITY, test x$proxy_server_identity == xyes -a x$proxy_server == xyes)
+AM_COND_IF([ENABLE_PROXY_SERVER_IDENTITY], [
+ CPPFLAGS+=" -DOPENDHT_PROXY_SERVER_IDENTITY=true"
+], [
+ CPPFLAGS+=" -DOPENDHT_PROXY_SERVER_IDENTITY=false"
+])
+
+AM_COND_IF([ENABLE_PROXY_CLIENT], [
+ CPPFLAGS+=" -DOPENDHT_PROXY_CLIENT=true"
+], [
+ CPPFLAGS+=" -DOPENDHT_PROXY_CLIENT=false"
+])
+
+AM_CONDITIONAL(PROXY_CLIENT_OR_SERVER, test x$proxy_client == xyes | test x$proxy_server == xyes)
+AM_COND_IF([PROXY_CLIENT_OR_SERVER], [
+ AC_CHECK_LIB(restbed, exit,, AC_MSG_ERROR([Missing restbed files]))
+ PKG_CHECK_MODULES([Jsoncpp], [jsoncpp >= 1.7.4])
+ CPPFLAGS="${CPPFLAGS} ${Jsoncpp_CFLAGS}"
+ LDFLAGS="${LDFLAGS} ${Jsoncpp_LIBS} -lrestbed"
+], [])
+
+
AC_CONFIG_FILES([doc/Doxyfile doc/Makefile])
dnl Configure setup.py if we build the python module
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
index 3a204fe5..681cb281 100644
--- a/doc/CMakeLists.txt
+++ b/doc/CMakeLists.txt
@@ -1,3 +1,16 @@
if (OPENDHT_TOOLS)
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dhtnode.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1)
endif ()
+
+if (OPENDHT_DOCUMENTATION)
+ if (NOT DOXYGEN_FOUND)
+ message(FATAL_ERROR "Doxygen is needed to build the documentation.")
+ endif()
+ configure_file (Doxyfile.in Doxyfile @ONLY)
+ add_custom_target(doc ALL
+ COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ COMMENT "Generating API documentation with Doxygen"
+ VERBATIM)
+ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION share/doc/opendht)
+endif()
diff --git a/include/opendht.h b/include/opendht.h
index 98d3ece9..e003a216 100644
--- a/include/opendht.h
+++ b/include/opendht.h
@@ -19,6 +19,9 @@
#pragma once
#include "opendht/dhtrunner.h"
+#if OPENDHT_PROXY_SERVER
+#include "opendht/dht_proxy_server.h"
+#endif
#include "opendht/log.h"
#include "opendht/default_types.h"
#include "opendht/indexation/pht.h"
diff --git a/include/opendht/callbacks.h b/include/opendht/callbacks.h
index 21f2dd0c..fd067c6b 100644
--- a/include/opendht/callbacks.h
+++ b/include/opendht/callbacks.h
@@ -1,7 +1,8 @@
/*
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
- * Author : Adrien Béraud
+ * Authors: Adrien Béraud
* Simon Désaulniers
+ * Sébastien Blin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,6 +27,10 @@
#include
#include
+#if OPENDHT_PROXY_SERVER
+#include
+#endif //OPENDHT_PROXY_SERVER
+
namespace dht {
struct Node;
@@ -40,13 +45,21 @@ enum class NodeStatus {
};
struct OPENDHT_PUBLIC NodeStats {
- unsigned good_nodes,
- dubious_nodes,
- cached_nodes,
- incoming_nodes;
- unsigned table_depth;
+ unsigned good_nodes {0},
+ dubious_nodes {0},
+ cached_nodes {0},
+ incoming_nodes {0};
+ unsigned table_depth {0};
unsigned getKnownNodes() const { return good_nodes + dubious_nodes; }
std::string toString() const;
+#if OPENDHT_PROXY_SERVER || OPENDHT_PROXY_CLIENT
+ /**
+ * Build a json object from a NodeStats
+ */
+ Json::Value toJson() const;
+ NodeStats() {};
+ explicit NodeStats(const Json::Value& v);
+#endif //OPENDHT_PROXY_SERVER
};
/**
diff --git a/include/opendht/crypto.h b/include/opendht/crypto.h
index 81985c66..a840576b 100644
--- a/include/opendht/crypto.h
+++ b/include/opendht/crypto.h
@@ -36,6 +36,10 @@ extern "C" {
#endif
namespace dht {
+
+/**
+ * Contains all crypto primitives
+ */
namespace crypto {
class OPENDHT_PUBLIC CryptoException : public std::runtime_error {
@@ -583,6 +587,9 @@ using SecureBlob = secure_vector;
OPENDHT_PUBLIC Identity generateIdentity(const std::string& name, Identity ca, unsigned key_length, bool is_ca);
OPENDHT_PUBLIC Identity generateIdentity(const std::string& name = "dhtnode", Identity ca = {}, unsigned key_length = 4096);
+OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name, Identity ca, bool is_ca);
+OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name = "dhtnode", Identity ca = {});
+
/**
* Performs SHA512, SHA256 or SHA1, depending on hash_length.
@@ -594,6 +601,8 @@ OPENDHT_PUBLIC Identity generateIdentity(const std::string& name = "dhtnode", Id
*/
OPENDHT_PUBLIC Blob hash(const Blob& data, size_t hash_length = 512/8);
+OPENDHT_PUBLIC void hash(const uint8_t* data, size_t data_length, uint8_t* hash, size_t hash_length);
+
/**
* Generates an encryption key from a text password,
* making the key longer to bruteforce.
diff --git a/include/opendht/default_types.h b/include/opendht/default_types.h
index cbfc7d25..58595163 100644
--- a/include/opendht/default_types.h
+++ b/include/opendht/default_types.h
@@ -19,6 +19,7 @@
#pragma once
#include "value.h"
+#include "sockaddr.h"
namespace dht {
enum class ImStatus : uint8_t {
@@ -45,7 +46,7 @@ public:
static Value::Filter getFilter() { return {}; }
- static bool storePolicy(InfoHash key, std::shared_ptr& value, InfoHash from, const sockaddr* from_addr, socklen_t from_len);
+ static bool storePolicy(InfoHash key, std::shared_ptr& value, const InfoHash& from, const SockAddr&);
static Value::Filter ServiceFilter(std::string s);
@@ -92,7 +93,7 @@ public:
static Value::Filter getFilter() {
return Value::Filter::chain(
BaseClass::getFilter(),
- [](const Value& v){ return v.recipient != InfoHash(); }
+ [](const Value& v) { return static_cast(v.recipient); }
);
}
@@ -204,16 +205,12 @@ private:
public:
static const ValueType TYPE;
- IpServiceAnnouncement(in_port_t p = 0) {
- ss.ss_family = 0;
- setPort(p);
+ IpServiceAnnouncement(sa_family_t family = AF_UNSPEC, in_port_t p = 0) {
+ addr.setFamily(family);
+ addr.setPort(p);
}
- IpServiceAnnouncement(const sockaddr* sa, socklen_t sa_len) {
- ss.ss_family = 0;
- if (sa)
- std::copy_n((const uint8_t*)sa, sa_len, (uint8_t*)&ss);
- }
+ IpServiceAnnouncement(const SockAddr& sa) : addr(sa) {}
IpServiceAnnouncement(const Blob& b) {
msgpack_unpack(unpackMsg(b).get());
@@ -222,53 +219,40 @@ public:
template
void msgpack_pack(Packer& pk) const
{
- pk.pack_array(2);
- pk.pack(reinterpret_cast(&ss)->sin_port);
- if (ss.ss_family == AF_INET) {
- pk.pack_bin(sizeof(in_addr));
- pk.pack_bin_body((const char*)&reinterpret_cast(&ss)->sin_addr, sizeof(in_addr));
- } else if (ss.ss_family == AF_INET6) {
- pk.pack_bin(sizeof(in6_addr));
- pk.pack_bin_body((const char*)&reinterpret_cast(&ss)->sin6_addr, sizeof(in6_addr));
- }
+ pk.pack_bin(addr.getLength());
+ pk.pack_bin_body((const char*)addr.get(), addr.getLength());
}
virtual void msgpack_unpack(msgpack::object o)
{
- if (o.type != msgpack::type::ARRAY) throw msgpack::type_error();
- if (o.via.array.size < 2) throw msgpack::type_error();
- reinterpret_cast(&ss)->sin_port = o.via.array.ptr[0].as();
- auto ip_dat = o.via.array.ptr[1].as();
- if (ip_dat.size() == sizeof(in_addr))
- std::copy(ip_dat.begin(), ip_dat.end(), (char*)&reinterpret_cast(&ss)->sin_addr);
- else if (ip_dat.size() == sizeof(in6_addr))
- std::copy(ip_dat.begin(), ip_dat.end(), (char*)&reinterpret_cast(&ss)->sin6_addr);
+ if (o.type == msgpack::type::BIN)
+ addr = {(sockaddr*)o.via.bin.ptr, (socklen_t)o.via.bin.size};
else
throw msgpack::type_error();
}
in_port_t getPort() const {
- return ntohs(reinterpret_cast(&ss)->sin_port);
+ return addr.getPort();
}
void setPort(in_port_t p) {
- reinterpret_cast(&ss)->sin_port = htons(p);
+ addr.setPort(p);
}
- sockaddr_storage getPeerAddr() const {
- return ss;
+ const SockAddr& getPeerAddr() const {
+ return addr;
}
virtual const ValueType& getType() const {
return TYPE;
}
- static bool storePolicy(InfoHash, std::shared_ptr&, InfoHash, const sockaddr*, socklen_t);
+ static bool storePolicy(InfoHash, std::shared_ptr&, const InfoHash&, const SockAddr&);
/** print value for debugging */
friend std::ostream& operator<< (std::ostream&, const IpServiceAnnouncement&);
private:
- sockaddr_storage ss;
+ SockAddr addr;
};
diff --git a/include/opendht/dht.h b/include/opendht/dht.h
index 7167fa1f..274a34cf 100644
--- a/include/opendht/dht.h
+++ b/include/opendht/dht.h
@@ -1,7 +1,8 @@
/*
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
- * Author(s) : Adrien Béraud
- * Simon Désaulniers
+ * Authors: Adrien Béraud
+ * Simon Désaulniers
+ * Sébastien Blin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,7 +27,7 @@
#include "scheduler.h"
#include "routing_table.h"
#include "callbacks.h"
-#include "log_enable.h"
+#include "dht_interface.h"
#include
#include
@@ -58,22 +59,16 @@ struct LocalListener;
* Must be given open UDP sockets and ::periodic must be
* called regularly.
*/
-class OPENDHT_PUBLIC Dht {
+class OPENDHT_PUBLIC Dht final : public DhtInterface {
public:
- // [[deprecated]]
- using NodeExport = dht::NodeExport;
-
- // [[deprecated]]
- using Status = NodeStatus;
-
Dht();
/**
* Initialise the Dht with two open sockets (for IPv4 and IP6)
* and an ID for the node.
*/
- Dht(int s, int s6, Config config);
+ Dht(const int& s, const int& s6, Config config);
virtual ~Dht();
/**
@@ -103,18 +98,6 @@ public:
*/
bool isRunning(sa_family_t af = 0) const;
- /**
- * Enable or disable logging of DHT internal messages
- */
- void setLoggers(LogMethod error = NOLOG, LogMethod warn = NOLOG, LogMethod debug = NOLOG);
-
- /**
- * Only print logs related to the given InfoHash (if given), or disable filter (if zeroes).
- */
- void setLogFilter(const InfoHash& f) {
- DHT_LOG.setFilter(f);
- }
-
virtual void registerType(const ValueType& type) {
types[type.id] = type;
}
@@ -168,8 +151,8 @@ public:
* @param key the key for which to query data for.
* @param cb a function called when new values are found on the network.
* It should return false to stop the operation.
- * @param donecb a function called when the operation is complete.
- cb and donecb won't be called again afterward.
+ * @param done_cb a function called when the operation is complete.
+ cb and done_cb won't be called again afterward.
* @param q a query used to filter values on the remotes before they send a
* response.
*/
@@ -248,7 +231,7 @@ public:
*
* @return a token to cancel the listener later.
*/
- virtual size_t listen(const InfoHash&, GetCallback, Value::Filter&&={}, Where&& w = {});
+ virtual size_t listen(const InfoHash&, GetCallback, Value::Filter={}, Where w = {});
virtual size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w = {}) {
return listen(key, bindGetCb(cb), std::forward(f), std::forward(w));
}
@@ -307,10 +290,8 @@ public:
std::vector getPublicAddress(sa_family_t family = 0);
-protected:
- Logger DHT_LOG;
- bool logFilerEnable_ {};
- InfoHash logFiler_ {};
+ void pushNotificationReceived(const std::map&) {}
+ void resubscribe(unsigned) {}
private:
@@ -339,7 +320,7 @@ private:
static constexpr unsigned MAX_HASHES {16384};
/* The maximum number of searches we keep data about. */
- static constexpr unsigned MAX_SEARCHES {2048};
+ static constexpr unsigned MAX_SEARCHES {16384};
static constexpr std::chrono::minutes MAX_STORAGE_MAINTENANCE_EXPIRE_TIME {10};
@@ -402,7 +383,6 @@ private:
Scheduler scheduler;
Sp nextNodesConfirmation {};
Sp nextStorageMaintenance {};
- time_point mybucket_grow_time {time_point::min()}, mybucket6_grow_time {time_point::min()};
net::NetworkEngine network_engine;
unsigned pending_pings4 {0};
@@ -422,14 +402,13 @@ private:
// Storage
void storageAddListener(const InfoHash& id, const Sp& node, size_t tid, Query&& = {});
- bool storageStore(const InfoHash& id, const Sp& value, time_point created, const SockAddr* sa = nullptr);
+ bool storageStore(const InfoHash& id, const Sp& value, time_point created, const SockAddr& sa = {});
void expireStore();
void expireStorage(InfoHash h);
void expireStore(decltype(store)::iterator);
void storageChanged(const InfoHash& id, Storage& st, ValueStorage&);
std::string printStorageLog(const decltype(store)::value_type&) const;
- void printStorageQuota(std::ostream& out, const decltype(store_quota)::value_type& ip) const;
/**
* For a given storage, if values don't belong there anymore because this
@@ -505,7 +484,7 @@ private:
* @param query The query sent to the node.
*/
void searchNodeGetDone(const net::Request& status,
- net::NetworkEngine::RequestAnswer&& answer,
+ net::RequestAnswer&& answer,
std::weak_ptr ws,
Sp query);
@@ -554,48 +533,46 @@ private:
bool neighbourhoodMaintenance(RoutingTable&);
- void processMessage(const uint8_t *buf, size_t buflen, const SockAddr&);
-
void onError(Sp node, net::DhtProtocolException e);
/* when our address is reported by a distant peer. */
void onReportedAddr(const InfoHash& id, const SockAddr&);
/* when we receive a ping request */
- net::NetworkEngine::RequestAnswer onPing(Sp node);
+ net::RequestAnswer onPing(Sp node);
/* when we receive a "find node" request */
- net::NetworkEngine::RequestAnswer onFindNode(Sp node, const InfoHash& hash, want_t want);
+ net::RequestAnswer onFindNode(Sp node, const InfoHash& hash, want_t want);
void onFindNodeDone(const Sp& status,
- net::NetworkEngine::RequestAnswer& a,
+ net::RequestAnswer& a,
Sp sr);
/* when we receive a "get values" request */
- net::NetworkEngine::RequestAnswer onGetValues(Sp node,
+ net::RequestAnswer onGetValues(Sp node,
const InfoHash& hash,
want_t want,
const Query& q);
void onGetValuesDone(const Sp& status,
- net::NetworkEngine::RequestAnswer& a,
+ net::RequestAnswer& a,
Sp& sr,
const Sp& orig_query);
/* when we receive a listen request */
- net::NetworkEngine::RequestAnswer onListen(Sp node,
+ net::RequestAnswer onListen(Sp node,
const InfoHash& hash,
const Blob& token,
size_t socket_id,
const Query& query);
void onListenDone(const Sp& status,
- net::NetworkEngine::RequestAnswer& a,
+ net::RequestAnswer& a,
Sp& sr);
/* when we receive an announce request */
- net::NetworkEngine::RequestAnswer onAnnounce(Sp node,
+ net::RequestAnswer onAnnounce(Sp node,
const InfoHash& hash,
const Blob& token,
const std::vector>& v,
const time_point& created);
- net::NetworkEngine::RequestAnswer onRefresh(Sp node,
+ net::RequestAnswer onRefresh(Sp node,
const InfoHash& hash,
const Blob& token,
const Value::Id& vid);
void onAnnounceDone(const Sp& status,
- net::NetworkEngine::RequestAnswer& a,
+ net::RequestAnswer& a,
Sp& sr);
};
diff --git a/include/opendht/dht_interface.h b/include/opendht/dht_interface.h
new file mode 100644
index 00000000..7f364196
--- /dev/null
+++ b/include/opendht/dht_interface.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2014-2017 Savoir-faire Linux Inc.
+ * Author: Sébastien Blin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include "infohash.h"
+#include "log_enable.h"
+
+namespace dht {
+
+class OPENDHT_PUBLIC DhtInterface {
+public:
+ DhtInterface() = default;
+ virtual ~DhtInterface() = default;
+
+ // [[deprecated]]
+ using Status = NodeStatus;
+ // [[deprecated]]
+ using NodeExport = dht::NodeExport;
+
+ /**
+ * Get the current status of the node for the given family.
+ */
+ virtual NodeStatus getStatus(sa_family_t af) const = 0;
+ virtual NodeStatus getStatus() const = 0;
+
+ /**
+ * Get the ID of the node.
+ */
+ virtual const InfoHash& getNodeId() const = 0;
+
+ /**
+ * Performs final operations before quitting.
+ */
+ virtual void shutdown(ShutdownCallback cb) = 0;
+
+ /**
+ * Returns true if the node is running (have access to an open socket).
+ *
+ * af: address family. If non-zero, will return true if the node
+ * is running for the provided family.
+ */
+ virtual bool isRunning(sa_family_t af = 0) const = 0;
+
+ virtual void registerType(const ValueType& type) = 0;
+
+ virtual const ValueType& getType(ValueType::Id type_id) const = 0;
+
+ /**
+ * Insert a node in the main routing table.
+ * The node is not pinged, so this should be
+ * used to bootstrap efficiently from previously known nodes.
+ */
+ virtual void insertNode(const InfoHash& id, const SockAddr&) = 0;
+ virtual void insertNode(const InfoHash& id, const sockaddr* sa, socklen_t salen) = 0;
+ virtual void insertNode(const NodeExport& n) = 0;
+
+ virtual void pingNode(const sockaddr*, socklen_t, DoneCallbackSimple&& cb={}) = 0;
+
+ virtual time_point periodic(const uint8_t *buf, size_t buflen, const SockAddr&) = 0;
+ virtual time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen) = 0;
+
+ /**
+ * Get a value by searching on all available protocols (IPv4, IPv6),
+ * and call the provided get callback when values are found at key.
+ * The operation will start as soon as the node is connected to the network.
+ * @param cb a function called when new values are found on the network.
+ * It should return false to stop the operation.
+ * @param donecb a function called when the operation is complete.
+ cb and donecb won't be called again afterward.
+ * @param f a filter function used to prefilter values.
+ */
+ virtual void get(const InfoHash& key, GetCallback cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) = 0;
+ virtual void get(const InfoHash& key, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f={}, Where&& w = {}) = 0;
+ virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) = 0;
+ virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}, Where&& w = {}) = 0;
+
+ /**
+ * Similar to Dht::get, but sends a Query to filter data remotely.
+ * @param key the key for which to query data for.
+ * @param cb a function called when new values are found on the network.
+ * It should return false to stop the operation.
+ * @param done_cb a function called when the operation is complete.
+ cb and done_cb won't be called again afterward.
+ * @param q a query used to filter values on the remotes before they send a
+ * response.
+ */
+ virtual void query(const InfoHash& key, QueryCallback cb, DoneCallback done_cb = {}, Query&& q = {}) = 0;
+ virtual void query(const InfoHash& key, QueryCallback cb, DoneCallbackSimple done_cb = {}, Query&& q = {}) = 0;
+
+ /**
+ * Get locally stored data for the given hash.
+ */
+ virtual std::vector> getLocal(const InfoHash& key, Value::Filter f = Value::AllFilter()) const = 0;
+
+ /**
+ * Get locally stored data for the given key and value id.
+ */
+ virtual Sp getLocalById(const InfoHash& key, Value::Id vid) const = 0;
+
+ /**
+ * Announce a value on all available protocols (IPv4, IPv6).
+ *
+ * The operation will start as soon as the node is connected to the network.
+ * The done callback will be called once, when the first announce succeeds, or fails.
+ */
+ virtual void put(const InfoHash& key,
+ Sp,
+ DoneCallback cb=nullptr,
+ time_point created=time_point::max(),
+ bool permanent = false) = 0;
+ virtual void put(const InfoHash& key,
+ const Sp& v,
+ DoneCallbackSimple cb,
+ time_point created=time_point::max(),
+ bool permanent = false) = 0;
+ virtual void put(const InfoHash& key,
+ Value&& v,
+ DoneCallback cb=nullptr,
+ time_point created=time_point::max(),
+ bool permanent = false) = 0;
+ virtual void put(const InfoHash& key,
+ Value&& v,
+ DoneCallbackSimple cb,
+ time_point created=time_point::max(),
+ bool permanent = false) = 0;
+
+ /**
+ * Get data currently being put at the given hash.
+ */
+ virtual std::vector> getPut(const InfoHash&) = 0;
+
+ /**
+ * Get data currently being put at the given hash with the given id.
+ */
+ virtual Sp getPut(const InfoHash&, const Value::Id&) = 0;
+
+ /**
+ * Stop any put/announce operation at the given location,
+ * for the value with the given id.
+ */
+ virtual bool cancelPut(const InfoHash&, const Value::Id&) = 0;
+
+ /**
+ * Listen on the network for any changes involving a specified hash.
+ * The node will register to receive updates from relevent nodes when
+ * new values are added or removed.
+ *
+ * @return a token to cancel the listener later.
+ */
+ virtual size_t listen(const InfoHash&, GetCallback, Value::Filter={}, Where w = {}) = 0;
+ virtual size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w = {}) = 0;
+
+ virtual bool cancelListen(const InfoHash&, size_t token) = 0;
+
+ /**
+ * Inform the DHT of lower-layer connectivity changes.
+ * This will cause the DHT to assume a public IP address change.
+ * The DHT will recontact neighbor nodes, re-register for listen ops etc.
+ */
+ virtual void connectivityChanged(sa_family_t) = 0;
+ virtual void connectivityChanged() = 0;
+
+ /**
+ * Get the list of good nodes for local storage saving purposes
+ * The list is ordered to minimize the back-to-work delay.
+ */
+ virtual std::vector exportNodes() = 0;
+
+ virtual std::vector exportValues() const = 0;
+ virtual void importValues(const std::vector&) = 0;
+
+ virtual NodeStats getNodesStats(sa_family_t af) const = 0;
+
+ virtual std::string getStorageLog() const = 0;
+ virtual std::string getStorageLog(const InfoHash&) const = 0;
+
+ virtual std::string getRoutingTablesLog(sa_family_t) const = 0;
+ virtual std::string getSearchesLog(sa_family_t) const = 0;
+ virtual std::string getSearchLog(const InfoHash&, sa_family_t af = AF_UNSPEC) const = 0;
+
+ virtual void dumpTables() const = 0;
+ virtual std::vector getNodeMessageStats(bool in = false) = 0;
+
+ /**
+ * Set the in-memory storage limit in bytes
+ */
+ virtual void setStorageLimit(size_t limit = DEFAULT_STORAGE_LIMIT) = 0;
+
+ /**
+ * Returns the total memory usage of stored values and the number
+ * of stored values.
+ */
+ virtual std::pair getStoreSize() const = 0;
+
+ virtual std::vector getPublicAddress(sa_family_t family = 0) = 0;
+
+ /**
+ * Enable or disable logging of DHT internal messages
+ */
+ virtual void setLoggers(LogMethod error = NOLOG, LogMethod warn = NOLOG, LogMethod debug = NOLOG)
+ {
+ DHT_LOG.DEBUG = debug;
+ DHT_LOG.WARN = warn;
+ DHT_LOG.ERR = error;
+ }
+
+ /**
+ * Only print logs related to the given InfoHash (if given), or disable filter (if zeroes).
+ */
+ virtual void setLogFilter(const InfoHash& f)
+ {
+ DHT_LOG.setFilter(f);
+ }
+
+ virtual void setPushNotificationToken(const std::string&) {};
+
+ /**
+ * Call linked callback with a push notification
+ * @param notification to process
+ */
+ virtual void pushNotificationReceived(const std::map& 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
diff --git a/include/opendht/dht_proxy_client.h b/include/opendht/dht_proxy_client.h
new file mode 100644
index 00000000..8fd8f5ba
--- /dev/null
+++ b/include/opendht/dht_proxy_client.h
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2016-2018 Savoir-faire Linux Inc.
+ * Author: Sébastien Blin
+ * Adrien Béraud
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#if OPENDHT_PROXY_CLIENT
+
+#pragma once
+
+#include
+#include
+#include
+
+#include "callbacks.h"
+#include "def.h"
+#include "dht_interface.h"
+#include "scheduler.h"
+
+namespace restbed {
+ class Request;
+}
+
+namespace Json {
+ class Value;
+}
+
+namespace dht {
+
+class OPENDHT_PUBLIC DhtProxyClient final : public DhtInterface {
+public:
+
+ DhtProxyClient() : scheduler(DHT_LOG) {}
+
+ explicit DhtProxyClient(std::function loopSignal, const std::string& serverHost, const std::string& pushClientId = "");
+
+#if OPENDHT_PUSH_NOTIFICATIONS
+ virtual void setPushNotificationToken(const std::string& token) {
+ deviceKey_ = token;
+ }
+#endif
+
+ virtual ~DhtProxyClient();
+
+ /**
+ * Get the ID of the node.
+ */
+ inline const InfoHash& getNodeId() const { return myid; }
+
+ /**
+ * Get the current status of the node for the given family.
+ */
+ NodeStatus getStatus(sa_family_t af) const;
+ NodeStatus getStatus() const {
+ return std::max(getStatus(AF_INET), getStatus(AF_INET6));
+ }
+
+ /**
+ * Performs final operations before quitting.
+ */
+ void shutdown(ShutdownCallback cb);
+
+ /**
+ * Returns true if the node is running (have access to an open socket).
+ *
+ * af: address family. If non-zero, will return true if the node
+ * is running for the provided family.
+ */
+ bool isRunning(sa_family_t af = 0) const;
+
+ /**
+ * Get a value by asking the proxy and call the provided get callback when
+ * values are found at key.
+ * The operation will start as soon as the node is connected to the network.
+ * @param cb a function called when new values are found on the network.
+ * It should return false to stop the operation.
+ * @param donecb a function called when the operation is complete.
+ cb and donecb won't be called again afterward.
+ * @param f a filter function used to prefilter values.
+ */
+ virtual void get(const InfoHash& key, GetCallback cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {});
+ virtual void get(const InfoHash& key, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f={}, Where&& w = {}) {
+ get(key, cb, bindDoneCb(donecb), std::forward(f), std::forward(w));
+ }
+ virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) {
+ get(key, bindGetCb(cb), donecb, std::forward(f), std::forward(w));
+ }
+ virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}, Where&& w = {}) {
+ get(key, bindGetCb(cb), bindDoneCb(donecb), std::forward(f), std::forward(w));
+ }
+
+ void get(const InfoHash& key, const GetCallback& cb, DoneCallback donecb, const Value::Filter& filterChain);
+
+ /**
+ * Announce a value on all available protocols (IPv4, IPv6).
+ *
+ * The operation will start as soon as the node is connected to the network.
+ * The done callback will be called once, when the first announce succeeds, or fails.
+ * NOTE: For now, created parameter is ignored.
+ */
+ void put(const InfoHash& key,
+ Sp,
+ DoneCallback cb=nullptr,
+ time_point created=time_point::max(),
+ bool permanent = false);
+ void put(const InfoHash& key,
+ const Sp& v,
+ DoneCallbackSimple cb,
+ time_point created=time_point::max(),
+ bool permanent = false)
+ {
+ put(key, v, bindDoneCb(cb), created, permanent);
+ }
+
+ void put(const InfoHash& key,
+ Value&& v,
+ DoneCallback cb=nullptr,
+ time_point created=time_point::max(),
+ bool permanent = false)
+ {
+ put(key, std::make_shared(std::move(v)), cb, created, permanent);
+ }
+ void put(const InfoHash& key,
+ Value&& v,
+ DoneCallbackSimple cb,
+ time_point created=time_point::max(),
+ bool permanent = false)
+ {
+ put(key, std::forward(v), bindDoneCb(cb), created, permanent);
+ }
+
+ /**
+ * @param af the socket family
+ * @return node stats from the proxy
+ */
+ NodeStats getNodesStats(sa_family_t af) const;
+
+ /**
+ * @param family the socket family
+ * @return public address
+ */
+ std::vector getPublicAddress(sa_family_t family = 0);
+
+ /**
+ * Listen on the network for any changes involving a specified hash.
+ * The node will register to receive updates from relevent nodes when
+ * new values are added or removed.
+ *
+ * @return a token to cancel the listener later.
+ */
+ virtual size_t listen(const InfoHash&, GetCallback, Value::Filter={}, Where={});
+ virtual size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w = {}) {
+ return listen(key, bindGetCb(cb), std::forward(f), std::forward(w));
+ }
+ virtual bool cancelListen(const InfoHash&, size_t token);
+
+#if OPENDHT_PUSH_NOTIFICATIONS
+ /**
+ * Call linked callback with a push notification
+ * @param notification to process
+ */
+ void pushNotificationReceived(const std::map& notification);
+ /**
+ * Refresh a listen via a token
+ * @param token
+ */
+ void resubscribe(const unsigned token);
+#endif // OPENDHT_PUSH_NOTIFICATIONS
+
+ time_point periodic(const uint8_t*, size_t, const SockAddr&);
+ time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen) {
+ return periodic(buf, buflen, SockAddr(from, fromlen));
+ }
+
+
+ /**
+ * Similar to Dht::get, but sends a Query to filter data remotely.
+ * @param key the key for which to query data for.
+ * @param cb a function called when new values are found on the network.
+ * It should return false to stop the operation.
+ * @param done_cb a function called when the operation is complete.
+ cb and done_cb won't be called again afterward.
+ * @param q a query used to filter values on the remotes before they send a
+ * response.
+ */
+ virtual void query(const InfoHash& /*key*/, QueryCallback /*cb*/, DoneCallback /*done_cb*/ = {}, Query&& /*q*/ = {}) { }
+ virtual void query(const InfoHash& key, QueryCallback cb, DoneCallbackSimple done_cb = {}, Query&& q = {}) {
+ query(key, cb, bindDoneCb(done_cb), std::forward(q));
+ }
+
+ /**
+ * Get data currently being put at the given hash.
+ */
+ std::vector> getPut(const InfoHash&) { return {}; }
+
+ /**
+ * Get data currently being put at the given hash with the given id.
+ */
+ Sp getPut(const InfoHash&, const Value::Id&) { return {}; }
+
+ /**
+ * Stop any put/announce operation at the given location,
+ * for the value with the given id.
+ */
+ bool cancelPut(const InfoHash&, const Value::Id&) { return false; }
+
+ void pingNode(const sockaddr*, socklen_t, DoneCallbackSimple&& /*cb*/={}) { }
+
+ /**
+ * NOTE: The following methods will not be implemented because the
+ * DhtProxyClient doesn't have any storage nor synchronization process
+ */
+ void insertNode(const InfoHash&, const SockAddr&) { }
+ void insertNode(const InfoHash&, const sockaddr*, socklen_t) { }
+ void insertNode(const NodeExport&) { }
+ std::pair getStoreSize() const { return {}; }
+ virtual void registerType(const ValueType&) { }
+ const ValueType& getType(ValueType::Id) const { return NO_VALUE; }
+ std::vector> getLocal(const InfoHash&, Value::Filter) const { return {}; }
+ Sp getLocalById(const InfoHash&, Value::Id) const { return {}; }
+ std::vector exportNodes() { return {}; }
+ std::vector exportValues() const { return {}; }
+ void importValues(const std::vector&) {}
+ std::string getStorageLog() const { return {}; }
+ std::string getStorageLog(const InfoHash&) const { return {}; }
+ std::string getRoutingTablesLog(sa_family_t) const { return {}; }
+ std::string getSearchesLog(sa_family_t) const { return {}; }
+ std::string getSearchLog(const InfoHash&, sa_family_t) const { return {}; }
+ void dumpTables() const {}
+ std::vector getNodeMessageStats(bool) { return {}; }
+ void setStorageLimit(size_t) {}
+ void connectivityChanged(sa_family_t) {}
+ void connectivityChanged() { }
+
+private:
+ const ValueType NO_VALUE;
+
+ /**
+ * Start the connection with a server.
+ */
+ void startProxy();
+
+ /**
+ * Get informations from the proxy node
+ * @return the JSON returned by the proxy
+ */
+ void getProxyInfos();
+ void onProxyInfos(const Json::Value& val);
+ SockAddr parsePublicAddress(const Json::Value& val);
+
+ void opFailed();
+
+ /**
+ * Initialize statusIpvX_
+ */
+ void getConnectivityStatus();
+ /**
+ * cancel all Listeners
+ */
+ void cancelAllListeners();
+ /**
+ * cancel all Operations
+ */
+ void cancelAllOperations();
+ std::string serverHost_;
+ std::string pushClientId_;
+
+ std::atomic_flag ongoingStatusUpdate_ = ATOMIC_FLAG_INIT;
+ NodeStatus statusIpv4_ {NodeStatus::Disconnected};
+ NodeStatus statusIpv6_ {NodeStatus::Disconnected};
+ NodeStats stats4_ {};
+ NodeStats stats6_ {};
+ SockAddr publicAddress_;
+
+ InfoHash myid {};
+
+ /**
+ * Store listen requests.
+ */
+ struct Listener
+ {
+ size_t token;
+ std::shared_ptr req;
+ std::string key;
+ GetCallback cb;
+ Value::Filter filterChain;
+ std::thread thread;
+ std::shared_ptr pushNotifToken; // NOTE: unused if not using push notifications
+ };
+ std::vector listeners_;
+ size_t listener_token_ {0};
+ std::mutex lockListener_;
+
+ /**
+ * Store current put and get requests.
+ */
+ struct Operation
+ {
+ std::shared_ptr req;
+ std::thread thread;
+ std::shared_ptr finished;
+ };
+ std::vector operations_;
+ std::mutex lockOperations_;
+ /**
+ * Callbacks should be executed in the main thread.
+ */
+ std::vector> callbacks_;
+ std::mutex lockCallbacks;
+
+ std::thread statusThread_;
+ mutable std::mutex lockCurrentProxyInfos_;
+
+ Scheduler scheduler;
+ /**
+ * Retrieve if we can connect to the proxy (update statusIpvX_)
+ */
+ void confirmProxy();
+ Sp nextProxyConfirmation {};
+ /**
+ * Relaunch LISTEN requests if the client disconnect/reconnect.
+ */
+ void restartListeners();
+
+ /**
+ * If we want to use push notifications by default.
+ * NOTE: empty by default to avoid to use services like FCM or APN.
+ */
+ std::string deviceKey_ {};
+ unsigned callbackId_ {0};
+ std::mutex lockCallback_;
+
+ const std::function loopSignal_;
+
+#if OPENDHT_PUSH_NOTIFICATIONS
+ void fillBodyToGetToken(std::shared_ptr request);
+#endif // OPENDHT_PUSH_NOTIFICATIONS
+
+};
+
+}
+
+#endif // OPENDHT_PROXY_CLIENT
diff --git a/include/opendht/dht_proxy_server.h b/include/opendht/dht_proxy_server.h
new file mode 100644
index 00000000..51dc56aa
--- /dev/null
+++ b/include/opendht/dht_proxy_server.h
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2017-2018 Savoir-faire Linux Inc.
+ * Author: Sébastien Blin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#if OPENDHT_PROXY_SERVER
+
+#pragma once
+
+#include "def.h"
+#include "sockaddr.h"
+#include "infohash.h"
+
+#include
+#include
+#include
+#include
+
+namespace Json {
+ class Value;
+}
+
+namespace dht {
+
+class DhtRunner;
+
+/**
+ * Describes the REST API
+ */
+class OPENDHT_PUBLIC DhtProxyServer
+{
+public:
+ /**
+ * Start the Http server for OpenDHT
+ * @param dht the DhtRunner linked to this proxy server
+ * @param port to listen
+ * @param pushServer where to push notifications
+ * @note if the server fails to start (if port is already used or reserved),
+ * it will fails silently
+ */
+ DhtProxyServer(std::shared_ptr dht, in_port_t port = 8000, const std::string& pushServer = "");
+ virtual ~DhtProxyServer();
+
+ DhtProxyServer(const DhtProxyServer& other) = delete;
+ DhtProxyServer(DhtProxyServer&& other) = delete;
+ DhtProxyServer& operator=(const DhtProxyServer& other) = delete;
+ DhtProxyServer& operator=(DhtProxyServer&& other) = delete;
+
+ /**
+ * Stop the DhtProxyServer
+ */
+ void stop();
+
+private:
+ /**
+ * Return the PublicKey id, the node id and node stats
+ * Method: GET "/"
+ * Result: HTTP 200, body: Node infos in JSON format
+ * On error: HTTP 503, body: {"err":"xxxx"}
+ * @param session
+ */
+ void getNodeInfo(const std::shared_ptr& session) const;
+
+ /**
+ * Return Values of an infoHash
+ * Method: GET "/{InfoHash: .*}"
+ * Return: Multiple JSON object in parts. Example:
+ * Value in JSON format\n
+ * Value in JSON format
+ *
+ * On error: HTTP 503, body: {"err":"xxxx"}
+ * @param session
+ */
+ void get(const std::shared_ptr& session) const;
+
+ /**
+ * Listen incoming Values of an infoHash.
+ * Method: LISTEN "/{InfoHash: .*}"
+ * Return: Multiple JSON object in parts. Example:
+ * Value in JSON format\n
+ * Value in JSON format
+ *
+ * On error: HTTP 503, body: {"err":"xxxx"}
+ * @param session
+ */
+ void listen(const std::shared_ptr& session) const;
+
+ /**
+ * Put a value on the DHT
+ * Method: POST "/{InfoHash: .*}"
+ * body = Value to put in JSON
+ * Return: HTTP 200 if success and the value put in JSON
+ * On error: HTTP 503, body: {"err":"xxxx"} if no dht
+ * HTTP 400, body: {"err":"xxxx"} if bad json or HTTP 502 if put fails
+ * @param session
+ */
+ void put(const std::shared_ptr& session) const;
+
+#if OPENDHT_PROXY_SERVER_IDENTITY
+ /**
+ * Put a value to sign by the proxy on the DHT
+ * Method: SIGN "/{InfoHash: .*}"
+ * body = Value to put in JSON
+ * Return: HTTP 200 if success and the value put in JSON
+ * On error: HTTP 503, body: {"err":"xxxx"} if no dht
+ * HTTP 400, body: {"err":"xxxx"} if bad json
+ * @param session
+ */
+ void putSigned(const std::shared_ptr& session) const;
+
+ /**
+ * Put a value to encrypt by the proxy on the DHT
+ * Method: ENCRYPT "/{hash: .*}"
+ * body = Value to put in JSON + "to":"infoHash"
+ * Return: HTTP 200 if success and the value put in JSON
+ * On error: HTTP 503, body: {"err":"xxxx"} if no dht
+ * HTTP 400, body: {"err":"xxxx"} if bad json
+ * @param session
+ */
+ void putEncrypted(const std::shared_ptr& session) const;
+#endif // OPENDHT_PROXY_SERVER_IDENTITY
+
+ /**
+ * Return Values of an infoHash filtered by a value id
+ * Method: GET "/{InfoHash: .*}/{ValueId: .*}"
+ * Return: Multiple JSON object in parts. Example:
+ * Value in JSON format\n
+ * Value in JSON format
+ *
+ * On error: HTTP 503, body: {"err":"xxxx"}
+ * @param session
+ */
+ void getFiltered(const std::shared_ptr& session) const;
+
+ /**
+ * Respond allowed Methods
+ * Method: OPTIONS "/{hash: .*}"
+ * Return: HTTP 200 + Allow: allowed methods
+ * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS
+ * @param session
+ */
+ void handleOptionsMethod(const std::shared_ptr& 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& 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& 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 service_;
+ std::shared_ptr dht_;
+
+ // Handle client quit for listen.
+ // NOTE: can be simplified when we will supports restbed 5.0
+ std::thread listenThread_;
+ struct SessionToHashToken {
+ std::shared_ptr session;
+ InfoHash hash;
+ std::future token;
+ };
+ mutable std::vector 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 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 pushListeners_;
+ mutable unsigned tokenPushNotif_ {0};
+ const std::string pushServer_;
+#endif //OPENDHT_PUSH_NOTIFICATIONS
+
+ /**
+ * Remove finished listeners
+ * @param testSession if we remove the listener only if the session is closed
+ */
+ void removeClosedListeners(bool testSession = true);
+ /**
+ * Launch or remove push listeners if needed
+ */
+ void handlePushListeners();
+};
+
+}
+
+#endif //OPENDHT_PROXY_SERVER
diff --git a/include/opendht/dhtrunner.h b/include/opendht/dhtrunner.h
index 0007c31a..6814f517 100644
--- a/include/opendht/dhtrunner.h
+++ b/include/opendht/dhtrunner.h
@@ -1,7 +1,8 @@
/*
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
- * Author(s) : Adrien Béraud
- * Simon Désaulniers
+ * Authors: Adrien Béraud
+ * Simon Désaulniers
+ * Sébastien Blin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,7 +20,6 @@
#pragma once
-//#include "securedht.h"
#include "infohash.h"
#include "value.h"
#include "callbacks.h"
@@ -53,6 +53,13 @@ class OPENDHT_PUBLIC DhtRunner {
public:
typedef std::function StatusCallback;
+ struct Config {
+ SecureDhtConfig dht_config;
+ bool threaded;
+ std::string proxy_server;
+ std::string push_node_id;
+ };
+
DhtRunner();
virtual ~DhtRunner();
@@ -202,7 +209,7 @@ public:
* Insert known nodes to the routing table, without necessarly ping them.
* Usefull to restart a node and get things running fast without putting load on the network.
*/
- void bootstrap(const std::vector>& nodes, DoneCallbackSimple&& cb={});
+ void bootstrap(const std::vector& nodes, DoneCallbackSimple&& cb={});
void bootstrap(const SockAddr& addr, DoneCallbackSimple&& cb={});
/**
@@ -250,7 +257,7 @@ public:
* @param f: address family of the bound port to retreive.
*/
in_port_t getBoundPort(sa_family_t f = AF_INET) const {
- return ntohs(((sockaddr_in*)&getBound(f).first)->sin_port);
+ return getBound(f).getPort();
}
std::pair getStoreSize() const;
@@ -294,11 +301,6 @@ public:
void registerCertificate(std::shared_ptr cert);
void setLocalCertificateStore(CertificateStoreQuery&& query_method);
- struct Config {
- SecureDhtConfig dht_config;
- bool threaded;
- };
-
/**
* @param port: Local port to bind. Both IPv4 and IPv6 will be tried (ANY).
* @param identity: RSA key pair to use for cryptographic operations.
@@ -316,7 +318,9 @@ public:
},
/*.id = */identity
},
- /*.threaded = */threaded
+ /*.threaded = */threaded,
+ /*.proxy_server = */"",
+ /*.push_node_id = */""
});
}
void run(in_port_t port, Config config);
@@ -329,7 +333,7 @@ public:
* @param threaded: If false, loop() must be called periodically. Otherwise a thread is launched.
* @param cb: Optional callback to receive general state information.
*/
- void run(const sockaddr_in* local4, const sockaddr_in6* local6, Config config);
+ void run(const SockAddr& local4, const SockAddr& local6, Config config);
/**
* Same as @run(sockaddr_in, sockaddr_in6, Identity, bool, StatusCallback), but with string IP addresses and service (port).
@@ -347,7 +351,13 @@ public:
*/
time_point loop() {
std::lock_guard lck(dht_mtx);
- return loop_();
+ time_point wakeup = time_point::min();
+ try {
+ wakeup = loop_();
+ } catch (const dht::SocketException& e) {
+ startNetwork(bound4, bound6);
+ }
+ return wakeup;
}
/**
@@ -362,6 +372,43 @@ public:
*/
void join();
+ void setProxyServer(const std::string& proxy, const std::string& pushNodeId = "") {
+#if OPENDHT_PROXY_CLIENT
+ if (config_.proxy_server == proxy and config_.push_node_id == pushNodeId)
+ return;
+ config_.proxy_server = proxy;
+ config_.push_node_id = pushNodeId;
+ enableProxy(use_proxy and not config_.proxy_server.empty());
+#endif
+ }
+
+ /**
+ * Start or stop the proxy
+ * @param proxify if we want to use the proxy
+ * @param deviceKey non empty to enable push notifications
+ */
+ void enableProxy(bool proxify);
+
+ /* Push notification methods */
+
+ /**
+ * Updates the push notification device token
+ */
+ void setPushNotificationToken(const std::string& token);
+
+ /**
+ * Insert a push notification to process for OpenDHT
+ */
+ void pushNotificationReceived(const std::map& data) const;
+ /**
+ * Refresh a listen via a token
+ * @param token
+ */
+ void resubscribe(unsigned token);
+
+ /* Proxy server mothods */
+ void forwardAllMessages(bool forward);
+
private:
static constexpr std::chrono::seconds BOOTSTRAP_PERIOD {10};
@@ -373,16 +420,48 @@ private:
*/
void tryBootstrapContinuously();
- void doRun(const sockaddr_in* sin4, const sockaddr_in6* sin6, SecureDhtConfig config);
+ void startNetwork(const SockAddr sin4, const SockAddr sin6);
time_point loop_();
- static std::vector> getAddrInfo(const std::string& host, const std::string& service);
-
NodeStatus getStatus() const {
return std::max(status4, status6);
}
+ /** Local DHT instance */
std::unique_ptr dht_;
+
+ /** Proxy client instance */
+ std::unique_ptr dht_via_proxy_;
+
+ /** true if we are currently using a proxy */
+ std::atomic_bool use_proxy {false};
+
+ /** Current configuration */
+ Config config_;
+
+ /**
+ * reset dht clients
+ */
+ void resetDht();
+ /**
+ * @return the current active DHT
+ */
+ SecureDht* activeDht() const;
+
+ /**
+ * Store current listeners and translates global tokens for each client.
+ */
+ struct Listener {
+ size_t tokenClassicDht;
+ size_t tokenProxyDht;
+ GetCallback gcb;
+ InfoHash hash;
+ Value::Filter f;
+ Where w;
+ };
+ std::map listeners_ {};
+ size_t listener_token_ {1};
+
mutable std::mutex dht_mtx {};
std::thread dht_thread {};
std::condition_variable cv {};
@@ -405,14 +484,19 @@ private:
std::queue> pending_ops {};
std::mutex storage_mtx {};
- std::atomic 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_;
};
}
diff --git a/include/opendht/infohash.h b/include/opendht/infohash.h
index 74186ed2..ade90dc3 100644
--- a/include/opendht/infohash.h
+++ b/include/opendht/infohash.h
@@ -19,6 +19,7 @@
#pragma once
#include "def.h"
+#include "rng.h"
#include
@@ -44,133 +45,99 @@ typedef uint16_t in_port_t;
#include
#include
-
-// bytes
-#define HASH_LEN 20u
-
namespace dht {
using byte = uint8_t;
-template
-class OPENDHT_PUBLIC Hash
-{
-public:
- Hash() {
- data_.fill(0);
- }
- Hash(const msgpack::object& o) {
- msgpack_unpack(o);
- }
- byte* data() {
- return data_.data();
- }
- const byte* data() const {
- return data_.data();
- }
- constexpr size_t size() const { return data_.size(); }
- explicit operator bool() const { return std::any_of(data_.begin(), data_.end(), [](byte _b) { return _b != 0; }); }
- bool operator==(Hash const& _c) const { return data_ == _c.data_; }
- bool operator!=(Hash const& _c) const { return data_ != _c.data_; }
- bool operator<(Hash const& _c) const { for (unsigned i = 0; i < N; ++i) if (data_[i] < _c.data_[i]) return true; else if (data_[i] > _c.data_[i]) return false; return false; }
- bool operator>=(Hash const& _c) const { return !operator<(_c); }
- bool operator<=(Hash const& _c) const { return operator==(_c) || operator<(_c); }
- bool operator>(Hash const& _c) const { return !operator<=(_c); }
- template
- void msgpack_pack(Packer& pk) const
- {
- pk.pack_bin(N);
- pk.pack_bin_body((char*)data_.data(), N);
- }
- void msgpack_unpack(msgpack::object o) {
- if (o.type != msgpack::type::BIN or o.via.bin.size != N)
- throw msgpack::type_error();
- std::copy_n(o.via.bin.ptr, N, data_.data());
- }
-
- template
- OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const Hash& h);
-
- template
- OPENDHT_PUBLIC friend std::istream& operator>> (std::istream& s, Hash& h);
-
- std::string toString() const {
- std::stringstream ss;
- ss << *this;
- return ss.str();
- }
-
-private:
- std::array data_;
-};
-
-template
-std::ostream& operator<< (std::ostream& s, const Hash& h)
-{
- s << std::hex;
- for (unsigned i=0; i
-std::istream& operator>> (std::istream& s, Hash& h)
-{
- std::array dat;
- s.exceptions(std::istream::eofbit | std::istream::failbit);
- s.read(&(*dat.begin()), dat.size());
- for (size_t i = 0; i < h.size(); i++) {
- unsigned res = 0;
- sscanf(dat.data() + 2*i, "%02x", &res);
- h[i] = res;
- }
- return s;
-}
-
-using h256 = Hash<32>;
-using PkId = h256;
-
/**
* Represents an InfoHash.
* An InfoHash is a byte array of HASH_LEN bytes.
* InfoHashes identify nodes and values in the Dht.
*/
-class OPENDHT_PUBLIC InfoHash final : public std::array {
+template
+class OPENDHT_PUBLIC Hash {
public:
- constexpr InfoHash() : std::array() {}
- constexpr InfoHash(const std::array& h) : std::array(h) {}
- InfoHash(const uint8_t* h, size_t h_len) : std::array() {
- if (h_len < HASH_LEN)
- fill(0);
- else
- std::copy_n(h, HASH_LEN, begin());
- }
+ using T = std::array;
+ typedef typename T::iterator iterator;
+ typedef typename T::const_iterator const_iterator;
+ Hash () {
+ data_.fill(0);
+ }
+ Hash (const uint8_t* h, size_t data_len) {
+ if (data_len < N)
+ data_.fill(0);
+ else
+ std::copy_n(h, N, data_.begin());
+ }
/**
* Constructor from an hexadecimal string (without "0x").
* hex must be at least 2.HASH_LEN characters long.
* If too long, only the first 2.HASH_LEN characters are read.
*/
- explicit InfoHash(const std::string& hex);
+ explicit Hash(const std::string& hex);
- InfoHash(const msgpack::object& o) {
+ Hash(const msgpack::object& o) {
msgpack_unpack(o);
}
+ size_t size() const { return data_.size(); }
+ const uint8_t* data() const { return data_.data(); }
+ uint8_t* data() { return data_.data(); }
+ iterator begin() { return data_.begin(); }
+ const_iterator cbegin() const { return data_.cbegin(); }
+ iterator end() { return data_.end(); }
+ const_iterator cend() const { return data_.cend(); }
+
+ bool operator==(const Hash& h) const {
+ auto a = reinterpret_cast(data_.data());
+ auto b = reinterpret_cast(h.data_.data());
+ constexpr unsigned n = N / sizeof(uint32_t);
+ for (unsigned i=0; i < n; i++)
+ if (a[i] != b[i])
+ return false;
+ return true;
+ }
+ bool operator!=(const Hash& h) const { return !(*this == h); }
+
+ bool operator<(const Hash& o) const {
+ for(unsigned i = 0; i < N; i++) {
+ if(data_[i] != o.data_[i])
+ return data_[i] < o.data_[i];
+ }
+ return false;
+ }
+
+ explicit operator bool() const {
+ auto a = reinterpret_cast(data_.data());
+ auto b = reinterpret_cast(data_.data() + N);
+ for (; a != b; a++) {
+ if (*a)
+ return true;
+ }
+ return false;
+ }
+
+ uint8_t& operator[](size_t index) { return data_[index]; }
+ const uint8_t& operator[](size_t index) const { return data_[index]; }
+
/**
* Find the lowest 1 bit in an id.
- * Result will allways be lower than 8*HASH_LEN
+ * Result will allways be lower than 8*N
*/
inline unsigned lowbit() const {
int i, j;
- for(i = HASH_LEN-1; i >= 0; i--)
- if((*this)[i] != 0)
+ for(i = N-1; i >= 0; i--)
+ if(data_[i] != 0)
break;
if(i < 0)
return -1;
for(j = 7; j >= 0; j--)
- if(((*this)[i] & (0x80 >> j)) != 0)
+ if((data_[i] & (0x80 >> j)) != 0)
break;
return 8 * i + j;
}
@@ -179,25 +146,25 @@ public:
* Forget about the ``XOR-metric''. An id is just a path from the
* root of the tree, so bits are numbered from the start.
*/
- static inline int cmp(const InfoHash& id1, const InfoHash& id2) {
- return std::memcmp(id1.data(), id2.data(), HASH_LEN);
+ static inline int cmp(const Hash& id1, const Hash& id2) {
+ return std::memcmp(id1.data_.data(), id2.data_.data(), N);
}
/** Find how many bits two ids have in common. */
static inline unsigned
- commonBits(const InfoHash& id1, const InfoHash& id2)
+ commonBits(const Hash& id1, const Hash& id2)
{
unsigned i, j;
uint8_t x;
- for(i = 0; i < HASH_LEN; i++) {
- if(id1[i] != id2[i])
+ for(i = 0; i < N; i++) {
+ if(id1.data_[i] != id2.data_[i])
break;
}
- if(i == HASH_LEN)
- return 8*HASH_LEN;
+ if(i == N)
+ return 8*N;
- x = id1[i] ^ id2[i];
+ x = id1.data_[i] ^ id2.data_[i];
j = 0;
while((x & 0x80) == 0) {
@@ -210,14 +177,14 @@ public:
/** Determine whether id1 or id2 is closer to this */
int
- xorCmp(const InfoHash& id1, const InfoHash& id2) const
+ xorCmp(const Hash& id1, const Hash& id2) const
{
- for(unsigned i = 0; i < HASH_LEN; i++) {
+ for(unsigned i = 0; i < N; i++) {
uint8_t xor1, xor2;
- if(id1[i] == id2[i])
+ if(id1.data_[i] == id2.data_[i])
continue;
- xor1 = id1[i] ^ (*this)[i];
- xor2 = id2[i] ^ (*this)[i];
+ xor1 = id1.data_[i] ^ data_[i];
+ xor2 = id2.data_[i] ^ data_[i];
if(xor1 < xor2)
return -1;
else
@@ -229,7 +196,7 @@ public:
bool
getBit(unsigned nbit) const
{
- auto& num = *(cbegin()+(nbit/8));
+ auto& num = *(data_.cbegin()+(nbit/8));
unsigned bit = 7 - (nbit % 8);
return (num >> bit) & 1;
}
@@ -237,46 +204,44 @@ public:
void
setBit(unsigned nbit, bool b)
{
- auto& num = (*this)[nbit/8];
+ auto& num = data_[nbit/8];
unsigned bit = 7 - (nbit % 8);
num ^= (-b ^ num) & (1 << bit);
}
- double
- toFloat() const
- {
+ double toFloat() const {
+ using D = size_t;
double v = 0.;
- for (unsigned i = 0; i < std::min(HASH_LEN, sizeof(unsigned)-1); i++)
- v += *(cbegin()+i)/(double)(1<<(8*(i+1)));
+ for (size_t i = 0; i < std::min(N, sizeof(D)-1); i++)
+ v += *(data_.cbegin()+i) / (double)((D)1 << 8*(i+1));
return v;
}
- bool
- operator<(const InfoHash& o) const {
- for(unsigned i = 0; i < HASH_LEN; i++) {
- if((*this)[i] != o[i])
- return (*this)[i] < o[i];
- }
- return false;
- }
-
- static inline InfoHash get(const std::string& data) {
+ static inline Hash get(const std::string& data) {
return get((const uint8_t*)data.data(), data.size());
}
- static inline InfoHash get(const std::vector& data) {
+ static inline Hash get(const std::vector& data) {
return get(data.data(), data.size());
}
/**
* Computes the hash from a given data buffer of size data_len.
*/
- static InfoHash get(const uint8_t* data, size_t data_len);
+ static Hash get(const uint8_t* data, size_t data_len)
+ {
+ Hash ret;
+ crypto::hash(data, data_len, ret.data(), N);
+ return ret;
+ }
- static InfoHash getRandom();
+ static Hash getRandom();
- OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const InfoHash& h);
- OPENDHT_PUBLIC friend std::istream& operator>> (std::istream& s, InfoHash& h);
+ template
+ OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const Hash& h);
+
+ template
+ OPENDHT_PUBLIC friend std::istream& operator>> (std::istream& s, Hash& h);
const char* to_c_str() const;
@@ -285,24 +250,135 @@ public:
template
void msgpack_pack(Packer& pk) const
{
- pk.pack_bin(HASH_LEN);
- pk.pack_bin_body((char*)data(), HASH_LEN);
+ pk.pack_bin(N);
+ pk.pack_bin_body((char*)data_.data(), N);
}
void msgpack_unpack(msgpack::object o) {
- if (o.type != msgpack::type::BIN or o.via.bin.size != HASH_LEN)
+ if (o.type != msgpack::type::BIN or o.via.bin.size != N)
throw msgpack::type_error();
- std::copy_n(o.via.bin.ptr, HASH_LEN, data());
+ std::copy_n(o.via.bin.ptr, N, data_.data());
}
-
+private:
+ T data_;
+ void fromString(const char*);
};
-static constexpr const InfoHash zeroes {};
+#define HASH_LEN 20u
+using InfoHash = Hash;
+using h256 = Hash<32>;
+using PkId = h256;
-struct NodeExport {
+template
+std::ostream& operator<< (std::ostream& s, const Hash& h)
+{
+ s.write(h.to_c_str(), N*2);
+ return s;
+}
+
+template
+std::istream& operator>> (std::istream& s, Hash& h)
+{
+ std::array dat;
+ s.exceptions(std::istream::eofbit | std::istream::failbit);
+ s.read(&(*dat.begin()), dat.size());
+ fromString(dat.data());
+ return s;
+}
+
+template
+Hash::Hash(const std::string& hex) {
+ if (hex.size() < 2*N)
+ data_.fill(0);
+ else
+ fromString(hex.c_str());
+}
+
+template
+void
+Hash::fromString(const char* in) {
+ auto hex2bin = [](char c) -> uint8_t {
+ if (c >= 'a' and c <= 'f') return 10 + c - 'a';
+ else if (c >= 'A' and c <= 'F') return 10 + c - 'A';
+ else if (c >= '0' and c <= '9') return c - '0';
+ else throw std::domain_error("not an hex character");
+ };
+ try {
+ for (size_t i=0; i
+Hash
+Hash::getRandom()
+{
+ Hash h;
+ crypto::random_device rdev;
+ std::uniform_int_distribution rand_int;
+ auto a = reinterpret_cast(h.data());
+ auto b = reinterpret_cast(h.data() + h.size());
+ std::generate(a, b, std::bind(rand_int, std::ref(rdev)));
+ return h;
+}
+
+struct HexMap : public std::array, 256> {
+ HexMap() {
+ for (size_t i=0; i> 4) & 0x0F];
+ e[1] = hex_digits[i & 0x0F];
+ }
+ }
+private:
+ static constexpr const char* hex_digits = "0123456789abcdef";
+};
+
+OPENDHT_PUBLIC extern const HexMap hex_map;
+
+template
+const char*
+Hash::to_c_str() const
+{
+ thread_local std::array buf;
+ for (size_t i=0; i
+std::string
+Hash::toString() const
+{
+ return std::string(to_c_str(), N*2);
+}
+
+const InfoHash zeroes {};
+
+struct OPENDHT_PUBLIC NodeExport {
InfoHash id;
sockaddr_storage ss;
socklen_t sslen;
+
+ template
+ void msgpack_pack(Packer& pk) const
+ {
+ pk.pack_map(2);
+ pk.pack(std::string("id"));
+ pk.pack(id);
+ pk.pack(std::string("addr"));
+ pk.pack_bin(sslen);
+ pk.pack_bin_body((char*)&ss, sslen);
+ }
+
+ void msgpack_unpack(msgpack::object o);
+
+ OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const NodeExport& h);
};
}
diff --git a/include/opendht/log.h b/include/opendht/log.h
index 220eaa70..9fda9eb9 100644
--- a/include/opendht/log.h
+++ b/include/opendht/log.h
@@ -24,6 +24,10 @@
#include
namespace dht {
+
+/**
+ * Logging-related functions
+ */
namespace log {
/**
diff --git a/include/opendht/log_enable.h b/include/opendht/log_enable.h
index b55f5556..7310de54 100644
--- a/include/opendht/log_enable.h
+++ b/include/opendht/log_enable.h
@@ -75,7 +75,7 @@ struct Logger {
LogMethod ERR = NOLOG;
void setFilter(const InfoHash& f) {
filter_ = f;
- filterEnable_ = filter_ != InfoHash{};
+ filterEnable_ = static_cast(filter_);
}
inline void log0(const LogMethod& logger, char const* format, va_list args) const {
#if OPENDHT_LOG
diff --git a/include/opendht/net.h b/include/opendht/net.h
deleted file mode 100644
index 180c4cf9..00000000
--- a/include/opendht/net.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2014-2017 Savoir-faire Linux Inc.
- * Author(s) : Adrien Béraud
- * Simon Désaulniers
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#pragma once
-
-#include
-#include
-
-namespace dht {
-namespace net {
-
-struct TransPrefix : public std::array {
- TransPrefix(const std::string& str) : std::array({{(uint8_t)str[0], (uint8_t)str[1]}}) {}
- static const TransPrefix PING;
- static const TransPrefix FIND_NODE;
- static const TransPrefix GET_VALUES;
- static const TransPrefix ANNOUNCE_VALUES;
- static const TransPrefix REFRESH;
- static const TransPrefix LISTEN;
-};
-
-/* Transaction-ids are 4-bytes long, with the first two bytes identifying
- * the kind of request, and the remaining two a sequence number in
- * host order.
- */
-struct TransId final : public std::array {
- static const constexpr uint16_t INVALID {0};
-
- TransId() { std::fill_n(begin(), 4, 0); }
- TransId(const std::array& o) { std::copy(o.begin(), o.end(), begin()); }
- TransId(const TransPrefix prefix, uint16_t seqno = 0) {
- std::copy_n(prefix.begin(), prefix.size(), begin());
- *reinterpret_cast(data()+prefix.size()) = seqno;
- }
-
- TransId(const char* q, size_t l) : array() {
- if (l > 4) {
- length = 0;
- } else {
- std::copy_n(q, l, begin());
- length = l;
- }
- }
-
- uint16_t getTid() const {
- return *reinterpret_cast(&(*this)[2]);
- }
-
- uint32_t toInt() const {
- return *reinterpret_cast(&(*this)[0]);
- }
-
- bool matches(const TransPrefix prefix, uint16_t* tid = nullptr) const {
- if (std::equal(begin(), begin()+2, prefix.begin())) {
- if (tid)
- *tid = getTid();
- return true;
- } else
- return false;
- }
-
- unsigned length {4};
-};
-
-} /* namespace net */
-} /* dht */
diff --git a/include/opendht/network_engine.h b/include/opendht/network_engine.h
index 8cf6827a..2a75e9a3 100644
--- a/include/opendht/network_engine.h
+++ b/include/opendht/network_engine.h
@@ -27,14 +27,12 @@
#include "utils.h"
#include "rng.h"
#include "rate_limiter.h"
-#include "net.h"
#include
#include
#include
#include
#include
-#include
#include
namespace dht {
@@ -42,6 +40,7 @@ namespace net {
struct Request;
struct Socket;
+struct TransId;
#ifndef MSG_CONFIRM
#define MSG_CONFIRM 0
@@ -81,6 +80,20 @@ private:
struct ParsedMessage;
+/**
+ * Answer for a request.
+ */
+struct RequestAnswer {
+ Blob ntoken {};
+ Value::Id vid {};
+ std::vector> values {};
+ std::vector> fields {};
+ std::vector> nodes4 {};
+ std::vector> nodes6 {};
+ RequestAnswer() {}
+ RequestAnswer(ParsedMessage&& msg);
+};
+
/*!
* @class NetworkEngine
* @brief An abstraction of communication protocol on the network.
@@ -101,26 +114,6 @@ struct ParsedMessage;
*/
class NetworkEngine final
{
-public:
- /*!
- * @class RequestAnswer
- * @brief Answer for a request.
- * @details
- * Answer for a request to be (de)serialized. Used for reponding to a node
- * and looking up the response from a node.
- */
- struct RequestAnswer {
- Blob ntoken {};
- Value::Id vid {};
- std::vector> values {};
- std::vector> fields {};
- std::vector> nodes4 {};
- std::vector> nodes6 {};
- RequestAnswer() {}
- RequestAnswer(ParsedMessage&& msg);
- };
-
-
private:
/**
* Called when we receive an error message.
@@ -137,7 +130,7 @@ private:
/**
* Called when an addres is reported from a requested node.
*
- * @param h: id
+ * @param h: id
* @param saddr_len (type: socklen_t) lenght of the sockaddr struct.
*/
std::function onReportedAddr;
@@ -176,7 +169,7 @@ private:
std::function,
const InfoHash&,
const Blob&,
- uint32_t,
+ Tid,
const Query&)> onListen {};
/**
* Called on announce request.
@@ -206,12 +199,11 @@ private:
const Value::Id&)> onRefresh {};
public:
- using SocketCb = std::function