Compare commits

..
Author SHA1 Message Date
Adrien Béraud 1751cb02cb dhtnode: print metrics 2020-03-30 14:18:41 -04:00
Adrien Béraud c8cc0e9750 add req count debug 2020-03-30 14:18:41 -04:00
Adrien Béraud 6f05112970 dht: add node metrics
Provides info about internal dht structures
2020-03-30 14:18:41 -04:00
Adrien Béraud a1d62fd062 stats: add search, cached node count 2020-03-30 14:18:23 -04:00
46 changed files with 944 additions and 1366 deletions

No files matched your search

+5 -4
View File
@@ -16,12 +16,13 @@ env:
before_install:
- |
if [[ "$OPENDHT_TEST_JOB" == *"opendht.classic"* ]]; then
docker pull aberaud/opendht-deps-bionic;
fi
if [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyclient"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.proxyserver"* ]] || [[ "$OPENDHT_TEST_JOB" == *"opendht.push"* ]]; then
# non llvm builds
if [[ "$OPENDHT_TEST_JOB" != *"opendht.llvm"* ]]; then
docker pull aberaud/opendht-deps;
fi
- |
# classic build
if [[ "$OPENDHT_TEST_JOB" == *"opendht.llvm"* ]]; then
docker pull aberaud/opendht-deps-llvm
fi
+9 -9
View File
@@ -4,7 +4,7 @@ include(CMakeDependentOption)
project (opendht)
set (opendht_VERSION_MAJOR 2)
set (opendht_VERSION_MINOR 1.3)
set (opendht_VERSION_MINOR 0.0)
set (opendht_VERSION ${opendht_VERSION_MAJOR}.${opendht_VERSION_MINOR})
set (PACKAGE_VERSION ${opendht_VERSION})
set (VERSION "${opendht_VERSION}")
@@ -37,7 +37,7 @@ option (OPENDHT_DOCUMENTATION "Create and install the HTML based API documentati
# Dependencies
list (APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
find_package (Threads)
find_package (PkgConfig REQUIRED)
find_package (PkgConfig)
find_package (GnuTLS 3.3 REQUIRED)
pkg_search_module (Nettle nettle)
find_package (Msgpack 1.2 REQUIRED)
@@ -45,6 +45,7 @@ if (OPENDHT_TOOLS)
find_package (Readline 6 REQUIRED)
endif ()
if (NOT OPENDHT_ARGON2)
find_package(PkgConfig)
pkg_search_module(argon2 libargon2)
if (argon2_FOUND)
message("-- Found Argon2: " ${argon2_LIBRARY_DIRS} " (found version \"" ${argon2_VERSION} "\")")
@@ -299,9 +300,9 @@ if (OPENDHT_STATIC)
endif ()
target_link_libraries(opendht-static
PRIVATE ${argon2_LIBRARIES}
PUBLIC ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_STATIC_LIBRARIES}
${Jsoncpp_STATIC_LIBRARIES} ${FMT_LIBRARY} ${HTTP_PARSER_LIBRARY}
${OPENSSL_STATIC_LIBRARIES})
PUBLIC ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES}
${Jsoncpp_LIBRARIES} ${FMT_LIBRARY} ${HTTP_PARSER_LIBRARY}
${OPENSSL_LIBRARIES})
install (TARGETS opendht-static DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht)
endif ()
@@ -379,7 +380,7 @@ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/opendhtConfigVersion.cmake DESTINATIO
# Unit tests
if (OPENDHT_TESTS)
pkg_search_module(Cppunit REQUIRED cppunit)
FIND_PACKAGE(Cppunit REQUIRED)
# unit testing
list (APPEND test_FILES
tests/infohashtester.h
@@ -411,8 +412,7 @@ if (OPENDHT_TESTS)
tests/tests_runner.cpp
${test_FILES}
)
target_include_directories(opendht_unit_tests SYSTEM PRIVATE ${Cppunit_INCLUDE_DIR})
target_link_directories(opendht_unit_tests PRIVATE ${Cppunit_LIBRARY_DIRS})
target_include_directories(opendht_unit_tests SYSTEM PRIVATE ${CPPUNIT_INCLUDE_DIRS})
if (OPENDHT_SHARED)
target_link_libraries(opendht_unit_tests opendht)
else ()
@@ -420,7 +420,7 @@ if (OPENDHT_TESTS)
endif ()
target_link_libraries(opendht_unit_tests
${CMAKE_THREAD_LIBS_INIT}
${Cppunit_LIBRARIES}
${CPPUNIT_LIBRARIES}
${GNUTLS_LIBRARIES}
${Jsoncpp_LIBRARIES}
)
+3 -3
View File
@@ -27,7 +27,7 @@ Build instructions: <https://github.com/savoirfairelinux/opendht/wiki/Build-the-
#### How-to build a simple client app
```bash
g++ main.cpp -std=c++14 -lopendht
g++ main.cpp -std=c++14 -lopendht -lgnutls
```
## Examples
@@ -51,7 +51,7 @@ int main()
// Join the network through any running node,
// here using a known bootstrap node.
node.bootstrap("bootstrap.jami.net", "4222");
node.bootstrap("bootstrap.ring.cx", "4222");
// put some data on the dht
std::vector<uint8_t> some_data(5, 10);
@@ -82,7 +82,7 @@ node.run()
# Join the network through any running node,
# here using a known bootstrap node.
node.bootstrap("bootstrap.jami.net", "4222")
node.bootstrap("bootstrap.ring.cx", "4222")
# blocking call (provide callback arguments to make the call non-blocking)
node.put(dht.InfoHash.get("unique_key"), dht.Value(b'some binary data'))
+28
View File
@@ -0,0 +1,28 @@
INCLUDE(FindPkgConfig)
PKG_CHECK_MODULES(PC_CPPUNIT "cppunit")
FIND_PATH(CPPUNIT_INCLUDE_DIRS
NAMES cppunit/TestCase.h
HINTS ${PC_CPPUNIT_INCLUDE_DIR}
${CMAKE_INSTALL_PREFIX}/include
PATHS
/usr/local/include
/usr/include
)
FIND_LIBRARY(CPPUNIT_LIBRARIES
NAMES cppunit
HINTS ${PC_CPPUNIT_LIBDIR}
${CMAKE_INSTALL_PREFIX}/lib
${CMAKE_INSTALL_PREFIX}/lib64
PATHS
${CPPUNIT_INCLUDE_DIRS}/../lib
/usr/local/lib
/usr/lib
)
LIST(APPEND CPPUNIT_LIBRARIES ${CMAKE_DL_LIBS})
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(CPPUNIT DEFAULT_MSG CPPUNIT_LIBRARIES CPPUNIT_INCLUDE_DIRS)
MARK_AS_ADVANCED(CPPUNIT_LIBRARIES CPPUNIT_INCLUDE_DIRS)
+2 -5
View File
@@ -1,7 +1,7 @@
dnl define macros
m4_define([opendht_major_version], 2)
m4_define([opendht_minor_version], 1)
m4_define([opendht_patch_version], 3)
m4_define([opendht_minor_version], 0)
m4_define([opendht_patch_version], 0)
m4_define([opendht_version],
[opendht_major_version.opendht_minor_version.opendht_patch_version])
@@ -140,7 +140,6 @@ AS_IF([test "x$with_jsoncpp" != "xno"],
AS_IF([test "x$have_jsoncpp" = "xyes"], [
AC_MSG_NOTICE([Using JsonCpp])
CPPFLAGS+=" -DOPENDHT_JSONCPP"
AC_SUBST(jsoncpp_lib, [", jsoncpp"])
], [
AC_MSG_NOTICE([Not using JsonCpp])
AM_COND_IF(PROXY_CLIENT_OR_SERVER, AC_MSG_ERROR(["JsonCpp is required for proxy/push notification support"]))
@@ -152,7 +151,6 @@ AS_IF([test "x$with_openssl" != "xno"],
[have_openssl=no])
AS_IF([test "x$have_openssl" = "xyes"], [
AC_MSG_NOTICE([Using OpenSSL])
AC_SUBST(openssl_lib, [", openssl"])
], [
AC_MSG_NOTICE([Not using OpenSSL])
])
@@ -177,7 +175,6 @@ AM_COND_IF([PROXY_CLIENT_OR_SERVER], [
# http_parser has no pkgconfig, instead we check with:
AC_CHECK_LIB(http_parser, exit,, AC_MSG_ERROR([Missing HttpParser library files]))
AC_CHECK_HEADERS([http_parser.h], [http_parser_headers=yes; break;])
AC_SUBST(http_parser_lib, ["-lhttp_parser"])
AS_IF([test "x$http_parser_headers" != "xyes"], AC_MSG_ERROR([Missing HttpParser headers files]))
])
+42 -13
View File
@@ -1,20 +1,33 @@
FROM ubuntu:20.04
FROM ubuntu:16.04
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
RUN apt-get update && apt-get install -y \
dialog apt-utils \
&& apt-get clean \
&& echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
RUN apt-get update && apt-get install -y \
build-essential pkg-config cmake git wget \
autotools-dev autoconf \
cython3 python3-dev python3-setuptools \
libncurses5-dev libreadline-dev nettle-dev libcppunit-dev \
libgnutls28-dev libuv1-dev libjsoncpp-dev libargon2-dev \
libssl-dev libfmt-dev libhttp-parser-dev libasio-dev libmsgpack-dev \
build-essential cmake git wget libncurses5-dev libreadline-dev nettle-dev \
libgnutls28-dev libuv1-dev cython3 python3-dev python3-setuptools libcppunit-dev libjsoncpp-dev \
autotools-dev autoconf libssl-dev \
&& apt-get clean
RUN echo "*** Installing libfmt ***" \
&& wget https://github.com/fmtlib/fmt/archive/5.3.0.tar.gz \
&& tar -xzf 5.3.0.tar.gz && cd fmt-5.3.0/ \
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_SHARED_LIBS=On -DFMT_TEST=Off -DFMT_DOC=Off . \
&& make -j8 && make install \
&& cd ../ && rm -rf fmt*
# libhttp-parser-dev is 2.1-2 which is too old
RUN echo "*** Building nodejs/http_parser dependency ***" \
&& wget https://github.com/nodejs/http-parser/archive/v2.9.2.tar.gz \
&& tar xvf v2.9.2.tar.gz && cd http-parser-2.9.2/ \
&& PREFIX=/usr make -j8 install \
&& cd ../ && rm -rf http-parser*
# libasio-dev (1.10.6-3) is too old
RUN echo "** Building a recent version of asio ***" \
&& wget https://github.com/aberaud/asio/archive/a7d66ef4017d8f1b7f2cef1bb4ba8e23b0961571.tar.gz \
&& tar -xvf a7d66ef4017d8f1b7f2cef1bb4ba8e23b0961571.tar.gz && cd asio-a7d66ef4017d8f1b7f2cef1bb4ba8e23b0961571/asio \
&& ./autogen.sh && ./configure --prefix=/usr --without-boost --disable-examples --disable-tests \
&& make install \
&& cd ../../ && rm -rf asio*
RUN echo "*** Downloading RESTinio ***" \
&& mkdir restinio && cd restinio \
&& wget https://github.com/aberaud/restinio/archive/a7a10e419d9089c5b8ee63f5e3098c892f22fae4.tar.gz \
@@ -25,3 +38,19 @@ RUN echo "*** Downloading RESTinio ***" \
-DRESTINIO_FIND_DEPS=ON -DRESTINIO_ALLOW_SOBJECTIZER=Off -DRESTINIO_USE_BOOST_ASIO=none . \
&& make -j8 && make install \
&& cd ../../ && rm -rf restinio*
# build restbed from sources
RUN git clone --recursive https://github.com/corvusoft/restbed.git \
&& cd restbed && mkdir build && cd build \
&& cmake -DBUILD_TESTS=NO -DBUILD_EXAMPLES=NO -DBUILD_SSL=NO -DBUILD_SHARED=YES -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_INSTALL_LIBDIR=lib .. \
&& make -j8 install \
&& cd .. && rm -rf restbed
#build msgpack from source
RUN wget https://github.com/msgpack/msgpack-c/releases/download/cpp-2.1.5/msgpack-2.1.5.tar.gz \
&& tar -xzf msgpack-2.1.5.tar.gz \
&& cd msgpack-2.1.5 && mkdir build && cd build \
&& cmake -DMSGPACK_CXX11=ON -DMSGPACK_BUILD_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX=/usr .. \
&& make -j8 && make install \
&& cd ../.. && rm -rf msgpack-2.1.5 msgpack-2.1.5.tar.gz
+1 -3
View File
@@ -3,13 +3,11 @@ MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
RUN echo "APT::Acquire::Retries \"3\";" > /etc/apt/apt.conf.d/80-retries
RUN apt-get update && apt-get install -y \
apt-transport-https build-essential pkg-config git wget libncurses5-dev libreadline-dev nettle-dev \
apt-transport-https build-essential cmake git wget libncurses5-dev libreadline-dev nettle-dev \
libgnutls28-dev libuv1-dev cython3 python3-dev python3-setuptools libcppunit-dev libjsoncpp-dev \
autotools-dev autoconf libfmt-dev libhttp-parser-dev libmsgpack-dev libssl-dev \
&& apt-get clean
RUN apt-get update && apt-get install -y python3-pip && pip3 install --upgrade cmake
# libasio-dev (1.10) is too old
RUN echo "** Building a recent version of asio ***" \
&& wget https://github.com/aberaud/asio/archive/a7d66ef4017d8f1b7f2cef1bb4ba8e23b0961571.tar.gz \
+43 -9
View File
@@ -1,21 +1,40 @@
FROM ubuntu:20.04
FROM ubuntu:16.04
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
RUN apt-get update && apt-get install -y \
dialog apt-utils \
&& apt-get clean \
&& echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
RUN apt-get update \
&& apt-get install -y llvm llvm-dev clang make cmake pkg-config git wget libncurses5-dev libreadline-dev \
&& apt-get install -y llvm llvm-dev clang make cmake git wget libncurses5-dev libreadline-dev \
nettle-dev libgnutls28-dev libuv1-dev libmsgpack-dev libjsoncpp-dev cython3 python3-dev \
python3-setuptools libcppunit-dev python3-pip \
autotools-dev autoconf libssl-dev libargon2-dev \
libfmt-dev libhttp-parser-dev libasio-dev \
autotools-dev autoconf libssl-dev \
&& apt-get remove -y gcc g++ && apt-get autoremove -y && apt-get clean
ENV CC cc
ENV CXX c++
RUN echo "*** Installing libfmt ***" \
&& wget https://github.com/fmtlib/fmt/archive/5.3.0.tar.gz \
&& tar -xzf 5.3.0.tar.gz && cd fmt-5.3.0/ \
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_SHARED_LIBS=On -DFMT_TEST=Off -DFMT_DOC=Off . \
&& make -j8 && make install \
&& cd ../ && rm -rf fmt*
# libhttp-parser-dev is 2.1-2 which is too old
RUN echo "*** Building nodejs/http_parser dependency ***" \
&& wget https://github.com/nodejs/http-parser/archive/v2.9.2.tar.gz \
&& tar xvf v2.9.2.tar.gz && cd http-parser-2.9.2/ \
&& PREFIX=/usr make -j8 install \
&& cd ../ && rm -rf http-parser*
# libasio-dev (1.10.6-3) is too old
RUN echo "** Building a recent version of asio ***" \
&& wget https://github.com/aberaud/asio/archive/a7d66ef4017d8f1b7f2cef1bb4ba8e23b0961571.tar.gz \
&& tar -xvf a7d66ef4017d8f1b7f2cef1bb4ba8e23b0961571.tar.gz && cd asio-a7d66ef4017d8f1b7f2cef1bb4ba8e23b0961571/asio \
&& ./autogen.sh && ./configure --prefix=/usr --without-boost --disable-examples --disable-tests \
&& make install \
&& cd ../../ && rm -rf asio*
#patch for https://github.com/Stiffstream/restinio-conan-example/issues/2
RUN apt-get update && apt-get install -y python3-pip && pip3 install --upgrade cmake
RUN echo "*** Downloading RESTinio ***" \
&& mkdir restinio && cd restinio \
&& wget https://github.com/aberaud/restinio/archive/a7a10e419d9089c5b8ee63f5e3098c892f22fae4.tar.gz \
@@ -26,3 +45,18 @@ RUN echo "*** Downloading RESTinio ***" \
-DRESTINIO_FIND_DEPS=ON -DRESTINIO_ALLOW_SOBJECTIZER=Off -DRESTINIO_USE_BOOST_ASIO=none . \
&& make -j8 && make install \
&& cd ../../ && rm -rf restinio*
# build restbed from sources
RUN git clone --recursive https://github.com/corvusoft/restbed.git \
&& cd restbed && mkdir build && cd build \
&& cmake -DBUILD_TESTS=NO -DBUILD_EXAMPLES=NO -DBUILD_SSL=NO -DBUILD_SHARED=YES -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_INSTALL_LIBDIR=lib .. \
&& make -j8 install \
&& cd .. && rm -rf restbed
#build msgpack from source
RUN wget https://github.com/msgpack/msgpack-c/releases/download/cpp-2.1.5/msgpack-2.1.5.tar.gz \
&& tar -xzf msgpack-2.1.5.tar.gz \
&& cd msgpack-2.1.5 && mkdir build && cd build \
&& cmake -DMSGPACK_CXX11=ON -DMSGPACK_BUILD_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX=/usr .. \
&& make -j8 && make install \
&& cd ../.. && rm -rf msgpack-2.1.5 msgpack-2.1.5.tar.gz
-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 -DOPENDHT_LTO=On && make -j8 && make install \
&& cd ../.. && rm -rf opendht
+1 -3
View File
@@ -1,8 +1,6 @@
FROM aberaud/opendht-deps-bionic
FROM aberaud/opendht-deps
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
RUN apt-get update && apt-get install -y python3-pip && pip3 install --upgrade cmake
COPY . /root/opendht
RUN cd /root/opendht && mkdir build && cd build \
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On -DOPENDHT_C=On -DOPENDHT_LTO=On -DOPENDHT_TESTS=ON .. \
+22 -11
View File
@@ -58,8 +58,8 @@ struct OPENDHT_PUBLIC NodeStats {
cached_nodes {0},
incoming_nodes {0};
unsigned table_depth {0};
unsigned cachedNodes {0};
unsigned searches {0};
unsigned node_cache_size {0};
unsigned getKnownNodes() const { return good_nodes + dubious_nodes; }
unsigned long getNetworkSizeEstimation() const { return 8 * std::exp2(table_depth); }
std::string toString() const;
@@ -73,7 +73,25 @@ struct OPENDHT_PUBLIC NodeStats {
explicit NodeStats(const Json::Value& v);
#endif
MSGPACK_DEFINE_MAP(good_nodes, dubious_nodes, cached_nodes, incoming_nodes, table_depth, searches, node_cache_size)
MSGPACK_DEFINE_MAP(good_nodes, dubious_nodes, cached_nodes, incoming_nodes, table_depth, searches)
};
struct OPENDHT_PUBLIC NodeMetrics {
size_t scheduler_size {0};
size_t node_cache_size {0};
size_t request_count {0};
size_t rate_limiter_size {0};
size_t reported_addr_num {0};
size_t store_size {0};
size_t store_values {0};
size_t store_quota_size {0};
#ifdef OPENDHT_JSONCPP
/**
* Build a json object from a NodeStats
*/
Json::Value toJson() const;
#endif
};
struct OPENDHT_PUBLIC NodeInfo {
@@ -82,8 +100,7 @@ struct OPENDHT_PUBLIC NodeInfo {
NodeStats ipv4 {};
NodeStats ipv6 {};
size_t ongoing_ops {0};
in_port_t bound4 {0};
in_port_t bound6 {0};
NodeMetrics metrics {};
#ifdef OPENDHT_JSONCPP
/**
@@ -120,18 +137,12 @@ struct OPENDHT_PUBLIC Config {
/** If set, the dht will load its state from this file on start and save its state in this file on shutdown */
std::string persist_path {};
/** If non-0, overrides the default global rate-limit. -1 means no limit. */
/** If non-0, overrides the default global rate-limit.-1 means no limit. */
ssize_t max_req_per_sec {0};
/** If non-0, overrides the default per-IP address rate-limit. -1 means no limit. */
ssize_t max_peer_req_per_sec {0};
/* If non-0, overrides the default maximum number of searches. -1 means no limit. */
ssize_t max_searches {0};
/* If non-0, overrides the default maximum store size. -1 means no limit. */
ssize_t max_store_size {0};
/**
* Use appropriate bahavior for a public IP, stable node:
* - No connectivity change triggered when a search fails
+21 -48
View File
@@ -80,14 +80,10 @@ public:
*/
inline const InfoHash& getNodeId() const override { return myid; }
NodeStatus updateStatus(sa_family_t af) override;
/**
* Get the current status of the node for the given family.
*/
NodeStatus getStatus(sa_family_t af) const override {
return dht(af).status;
}
NodeStatus getStatus(sa_family_t af) const override;
NodeStatus getStatus() const override {
return std::max(getStatus(AF_INET), getStatus(AF_INET6));
@@ -115,15 +111,6 @@ public:
return types.getType(type_id);
}
void addBootstrap(const std::string& host, const std::string& service) override {
bootstrap_nodes.emplace_back(host, service);
onDisconnected();
}
void clearBootstrap() override {
bootstrap_nodes.clear();
}
/**
* Insert a node in the main routing table.
* The node is not pinged, so this should be
@@ -286,6 +273,7 @@ public:
void loadState(const std::string& path);
NodeStats getNodesStats(sa_family_t af) const override;
NodeMetrics getNodeMetrics() const override;
std::string getStorageLog() const override;
std::string getStorageLog(const InfoHash&) const override;
@@ -343,10 +331,10 @@ private:
static constexpr unsigned LISTEN_NODES {4};
/* The maximum number of hashes we're willing to track. */
static constexpr unsigned MAX_HASHES {1024 * 1024};
static constexpr unsigned MAX_HASHES {64 * 1024};
/* The maximum number of searches we keep data about. */
static constexpr unsigned MAX_SEARCHES {1024 * 1024};
static constexpr unsigned MAX_SEARCHES {64 * 1024};
static constexpr std::chrono::minutes MAX_STORAGE_MAINTENANCE_EXPIRE_TIME {10};
@@ -371,8 +359,6 @@ private:
Dht(const Dht&) = delete;
Dht& operator=(const Dht&) = delete;
std::mt19937_64 rd {crypto::getSeededRandomEngine<std::mt19937_64>()};
InfoHash myid {};
uint64_t secret {};
@@ -381,39 +367,27 @@ private:
// registred types
TypeStore types;
using SearchMap = std::map<InfoHash, Sp<Search>>;
struct Kad {
RoutingTable buckets {};
SearchMap searches {};
unsigned pending_pings {0};
NodeStatus status;
NodeStatus getStatus(time_point now) const;
NodeStats getNodesStats(time_point now, const InfoHash& myid) const;
};
Kad dht4 {};
Kad dht6 {};
std::vector<std::pair<std::string,std::string>> bootstrap_nodes {};
std::chrono::steady_clock::duration bootstrap_period {std::chrono::seconds(10)};
Sp<Scheduler::Job> bootstrapJob {};
// the stuff
RoutingTable buckets4 {};
RoutingTable buckets6 {};
std::map<InfoHash, Storage> store;
std::map<SockAddr, StorageBucket, SockAddr::ipCmp> store_quota;
size_t total_values {0};
size_t total_store_size {0};
size_t max_store_keys {MAX_HASHES};
size_t max_store_size {DEFAULT_STORAGE_LIMIT};
size_t max_searches {MAX_SEARCHES};
size_t search_id {0};
using SearchMap = std::map<InfoHash, Sp<Search>>;
SearchMap searches4 {};
SearchMap searches6 {};
uint16_t search_id {0};
// map a global listen token to IPv4, IPv6 specific listen tokens.
// 0 is the invalid token.
std::map<size_t, std::tuple<size_t, size_t, size_t>> listeners {};
size_t listener_token {1};
std::mt19937_64 rd {crypto::getSeededRandomEngine<std::mt19937_64>()};
// timing
Scheduler scheduler;
@@ -421,6 +395,9 @@ private:
Sp<Scheduler::Job> nextStorageMaintenance {};
net::NetworkEngine network_engine;
unsigned pending_pings4 {0};
unsigned pending_pings6 {0};
using ReportedAddr = std::pair<unsigned, SockAddr>;
std::vector<ReportedAddr> reported_addr;
@@ -446,7 +423,7 @@ private:
void reportedAddr(const SockAddr&);
// Storage
void storageAddListener(const InfoHash& id, const Sp<Node>& node, size_t tid, Query&& = {}, int version = 0);
void storageAddListener(const InfoHash& id, const Sp<Node>& node, size_t tid, Query&& = {});
bool storageStore(const InfoHash& id, const Sp<Value>& value, time_point created, const SockAddr& sa = {}, bool permanent = false);
bool storageErase(const InfoHash& id, Value::Id vid);
bool storageRefresh(const InfoHash& id, Value::Id vid);
@@ -466,10 +443,8 @@ private:
size_t maintainStorage(decltype(store)::value_type&, bool force=false, const DoneCallback& donecb={});
// Buckets
Kad& dht(sa_family_t af) { return af == AF_INET ? dht4 : dht6; }
const Kad& dht(sa_family_t af) const { return af == AF_INET ? dht4 : dht6; }
RoutingTable& buckets(sa_family_t af) { return dht(af).buckets; }
const RoutingTable& buckets(sa_family_t af) const { return dht(af).buckets; }
RoutingTable& buckets(sa_family_t af) { return af == AF_INET ? buckets4 : buckets6; }
const RoutingTable& buckets(sa_family_t af) const { return af == AF_INET ? buckets4 : buckets6; }
Bucket* findBucket(const InfoHash& id, sa_family_t af) {
auto& b = buckets(af);
auto it = b.findBucket(id);
@@ -491,8 +466,8 @@ private:
// Searches
inline SearchMap& searches(sa_family_t af) { return dht(af).searches; }
inline const SearchMap& searches(sa_family_t af) const { return dht(af).searches; }
inline SearchMap& searches(sa_family_t af) { return af == AF_INET ? searches4 : searches6; }
inline const SearchMap& searches(sa_family_t af) const { return af == AF_INET ? searches4 : searches6; }
/**
* Low-level method that will perform a search on the DHT for the specified
@@ -515,7 +490,6 @@ private:
void confirmNodes();
void expire();
void onDisconnected();
/**
* Generic function to execute when a 'get' request has completed.
@@ -601,8 +575,7 @@ private:
const InfoHash& hash,
const Blob& token,
size_t socket_id,
const Query& query,
int version = 0);
const Query& query);
void onListenDone(const Sp<Node>& status,
net::RequestAnswer& a,
Sp<Search>& sr);
+1 -4
View File
@@ -42,7 +42,6 @@ public:
/**
* Get the current status of the node for the given family.
*/
virtual NodeStatus updateStatus(sa_family_t af) { return getStatus(af); };
virtual NodeStatus getStatus(sa_family_t af) const = 0;
virtual NodeStatus getStatus() const = 0;
@@ -70,9 +69,6 @@ public:
virtual const ValueType& getType(ValueType::Id type_id) const = 0;
virtual void addBootstrap(const std::string& /*host*/, const std::string& /*service*/) {};
virtual void clearBootstrap() {};
/**
* Insert a node in the main routing table.
* The node is not pinged, so this should be
@@ -198,6 +194,7 @@ public:
virtual void importValues(const std::vector<ValuesExport>&) = 0;
virtual NodeStats getNodesStats(sa_family_t af) const = 0;
virtual NodeMetrics getNodeMetrics() const { return {}; }
virtual std::string getStorageLog() const = 0;
virtual std::string getStorageLog(const InfoHash&) const = 0;
+8 -77
View File
@@ -36,15 +36,6 @@
#include <memory>
#include <mutex>
namespace dht {
enum class PushType {
None = 0,
Android,
iOS
};
}
MSGPACK_ADD_ENUM(dht::PushType)
namespace http {
class Request;
struct ListenerSession;
@@ -61,13 +52,6 @@ class DhtRunner;
using RestRouter = restinio::router::express_router_t<>;
using RequestStatus = restinio::request_handling_status_t;
struct ProxyServerConfig {
in_port_t port {8000};
std::string pushServer {};
std::string persistStatePath {};
dht::crypto::Identity identity {};
};
/**
* Describes the REST API
*/
@@ -82,9 +66,10 @@ public:
* @note if the server fails to start (if port is already used or reserved),
* it will fails silently
*/
DhtProxyServer(const std::shared_ptr<DhtRunner>& dht,
const ProxyServerConfig& config = {},
const std::shared_ptr<dht::Logger>& logger = {});
DhtProxyServer(
dht::crypto::Identity identity,
std::shared_ptr<DhtRunner> dht, in_port_t port = 8000, const std::string& pushServer = "",
std::shared_ptr<dht::Logger> logger = {});
virtual ~DhtProxyServer();
@@ -98,10 +83,8 @@ public:
struct ServerStats {
/** Current number of listen operations */
size_t listenCount {0};
/** Current number of permanent put operations (hash used) */
/** Current number of permanent put operations */
size_t putCount {0};
/** Current number of permanent put values */
size_t totalPermanentPuts {0};
/** Current number of push tokens with at least one listen operation */
size_t pushListenersCount {0};
/** Average requests per second */
@@ -131,7 +114,6 @@ public:
Json::Value result;
result["listenCount"] = static_cast<Json::UInt64>(listenCount);
result["putCount"] = static_cast<Json::UInt64>(putCount);
result["totalPermanentPuts"] = static_cast<Json::UInt64>(totalPermanentPuts);
result["pushListenersCount"] = static_cast<Json::UInt64>(pushListenersCount);
result["requestRate"] = requestRate;
if (nodeInfo)
@@ -300,17 +282,17 @@ private:
* @param key of the device
* @param json, the content to send
*/
void sendPushNotification(const std::string& key, Json::Value&& json, PushType type, bool highPriority);
void sendPushNotification(const std::string& key, Json::Value&& json, bool isAndroid, bool highPriority);
/**
* Send push notification with an expire timeout.
* @param ec
* @param pushToken
* @param json
* @param type
* @param isAndroid
*/
void handleNotifyPushListenExpire(const asio::error_code &ec, const std::string pushToken,
std::function<Json::Value()> json, PushType type);
std::function<Json::Value()> json, const bool isAndroid);
/**
* Remove a push listener between a client and a hash
@@ -327,12 +309,6 @@ private:
void handlePrintStats(const asio::error_code &ec);
void updateStats();
template <typename Os>
void saveState(Os& stream);
template <typename Is>
void loadState(Is& is, size_t size);
using clock = std::chrono::steady_clock;
using time_point = clock::time_point;
@@ -340,9 +316,6 @@ private:
std::shared_ptr<DhtRunner> dht_;
Json::StreamWriterBuilder jsonBuilder_;
Json::CharReaderBuilder jsonReaderBuilder_;
std::mt19937_64 rd {crypto::getSeededRandomEngine<std::mt19937_64>()};
std::string persistPath_;
// http server
std::thread serverThread_;
@@ -371,7 +344,6 @@ private:
struct PushSessionContext {
std::mutex lock;
std::string sessionId;
PushSessionContext(const std::string& id) : sessionId(id) {}
};
struct PermanentPut {
time_point expiration;
@@ -381,32 +353,9 @@ private:
std::unique_ptr<asio::steady_timer> expireTimer;
std::unique_ptr<asio::steady_timer> expireNotifyTimer;
Sp<Value> value;
PushType type;
template <typename Packer>
void msgpack_pack(Packer& p) const
{
p.pack_map(2 + (sessionCtx ? 1 : 0) + (clientId.empty() ? 0 : 1) + (type == PushType::None ? 0 : 2));
p.pack("value"); p.pack(value);
p.pack("exp"); p.pack(to_time_t(expiration));
if (not clientId.empty()) {
p.pack("cid"); p.pack(clientId);
}
if (sessionCtx) {
std::lock_guard<std::mutex> l(sessionCtx->lock);
p.pack("sid"); p.pack(sessionCtx->sessionId);
}
if (type != PushType::None) {
p.pack("t"); p.pack(type);
p.pack("token"); p.pack(pushToken);
}
}
void msgpack_unpack(const msgpack::object& o);
};
struct SearchPuts {
std::map<dht::Value::Id, PermanentPut> puts;
MSGPACK_DEFINE_ARRAY(puts)
};
std::mutex lockSearchPuts_;
std::map<InfoHash, SearchPuts> puts_;
@@ -418,32 +367,14 @@ private:
#ifdef OPENDHT_PUSH_NOTIFICATIONS
struct Listener {
time_point expiration;
std::string clientId;
std::shared_ptr<PushSessionContext> sessionCtx;
std::future<size_t> internalToken;
std::unique_ptr<asio::steady_timer> expireTimer;
std::unique_ptr<asio::steady_timer> expireNotifyTimer;
PushType type;
template <typename Packer>
void msgpack_pack(Packer& p) const
{
p.pack_map(sessionCtx ? 4 : 3);
p.pack("cid"); p.pack(clientId);
p.pack("exp"); p.pack(to_time_t(expiration));
if (sessionCtx) {
std::lock_guard<std::mutex> l(sessionCtx->lock);
p.pack("sid"); p.pack(sessionCtx->sessionId);
}
p.pack("t"); p.pack(type);
}
void msgpack_unpack(const msgpack::object& o);
};
struct PushListener {
std::map<InfoHash, std::vector<Listener>> listeners;
MSGPACK_DEFINE_ARRAY(listeners)
};
std::mutex lockPushListeners_;
std::map<std::string, PushListener> pushListeners_;
+18 -1
View File
@@ -389,7 +389,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(SockAddr& local4, SockAddr& local6, const Config& config, Context&& context = {});
void run(const SockAddr& local4, const SockAddr& local6, const Config& config, Context&& context = {});
/**
* Same as @run(sockaddr_in, sockaddr_in6, Identity, bool, StatusCallback), but with string IP addresses and service (port).
@@ -459,6 +459,14 @@ private:
Stopping
};
/**
* Will try to resolve the list of hostnames `bootstrap_nodes` on seperate
* thread and then queue ping requests. This list should contain reliable
* nodes so that the DHT node can recover quickly from losing connection
* with the network.
*/
void tryBootstrapContinuously();
time_point loop_();
NodeStatus getStatus() const {
@@ -505,6 +513,15 @@ private:
net::PacketList rcv {};
decltype(rcv) rcv_free {};
/** true if currently actively boostraping */
std::atomic_bool bootstraping {false};
/* bootstrap nodes given as (host, service) pairs */
std::vector<std::pair<std::string,std::string>> bootstrap_nodes_all {};
std::vector<std::pair<std::string,std::string>> bootstrap_nodes {};
std::thread bootstrap_thread {};
std::mutex bootstrap_mtx {};
std::condition_variable bootstrap_cv {};
std::queue<std::function<void(SecureDht&)>> pending_ops_prio {};
std::queue<std::function<void(SecureDht&)>> pending_ops {};
std::mutex storage_mtx {};
-16
View File
@@ -237,9 +237,6 @@ public:
static Hash getRandom();
template <typename Rd>
static Hash getRandom(Rd&);
template <size_t M>
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const Hash<M>& h);
@@ -327,19 +324,6 @@ Hash<N>::getRandom()
return h;
}
template <size_t N>
template <typename Rd>
Hash<N>
Hash<N>::getRandom(Rd& rdev)
{
Hash h;
std::uniform_int_distribution<uint32_t> rand_int;
auto a = reinterpret_cast<uint32_t*>(h.data());
auto b = reinterpret_cast<uint32_t*>(h.data() + h.size());
std::generate(a, b, std::bind(rand_int, std::ref(rdev)));
return h;
}
struct HexMap : public std::array<std::array<char, 2>, 256> {
HexMap() {
for (size_t i=0; i<size(); i++) {
+20 -43
View File
@@ -180,8 +180,7 @@ private:
const InfoHash&,
const Blob&,
Tid,
const Query&,
int)> onListen {};
const Query&)> onListen {};
/**
* Called on announce request.
*
@@ -211,7 +210,6 @@ private:
public:
using RequestCb = std::function<void(const Request&, RequestAnswer&&)>;
using RequestErrorCb = std::function<bool(const Request&, DhtProtocolException&&)>;
using RequestExpiredCb = std::function<void(const Request&, bool)>;
NetworkEngine(const Sp<Logger>& log, std::mt19937_64& rd, Scheduler& scheduler, std::unique_ptr<DatagramSocket>&& sock);
@@ -239,10 +237,9 @@ public:
void clear();
/**
* Sends values (with closest nodes) to a listener.
* Sends values (with closest nodes) to a listenner.
*
* @deprecated
* @param sa The address of the listener.
* @param sa The address of the listenner.
* @param sslen The length of the sockaddr structure.
* @param socket_id The tid to use to write to the request socket.
* @param hash The hash key of the value.
@@ -251,14 +248,13 @@ public:
* @param nodes The ipv4 closest nodes.
* @param nodes6 The ipv6 closest nodes.
* @param values The values to send.
* @param version If version = 1, a request will be used to answer to the listener
*/
void tellListener(Sp<Node> n, Tid socket_id, const InfoHash& hash, want_t want, const Blob& ntoken,
std::vector<Sp<Node>>&& nodes, std::vector<Sp<Node>>&& nodes6,
std::vector<Sp<Value>>&& values, const Query& q, int version);
std::vector<Sp<Value>>&& values, const Query& q);
void tellListenerRefreshed(Sp<Node> n, Tid socket_id, const InfoHash& hash, const Blob& ntoken, const std::vector<Value::Id>& values, int version);
void tellListenerExpired(Sp<Node> n, Tid socket_id, const InfoHash& hash, const Blob& ntoken, const std::vector<Value::Id>& values, int version);
void tellListenerRefreshed(Sp<Node> n, Tid socket_id, const InfoHash& hash, const Blob& ntoken, const std::vector<Value::Id>& values);
void tellListenerExpired(Sp<Node> n, Tid socket_id, const InfoHash& hash, const Blob& ntoken, const std::vector<Value::Id>& values);
bool isRunning(sa_family_t af) const;
inline want_t want () const { return dht_socket->hasIPv4() and dht_socket->hasIPv6() ? (WANT4 | WANT6) : -1; }
@@ -280,7 +276,7 @@ public:
*/
Sp<Request>
sendPing(Sp<Node> n, RequestCb&& on_done, RequestExpiredCb&& on_expired);
/**
* Send a "ping" request to a given node.
*
@@ -297,7 +293,7 @@ public:
std::forward<RequestCb>(on_done),
std::forward<RequestExpiredCb>(on_expired));
}
/**
* Send a "find node" request to a given node.
*
@@ -362,9 +358,10 @@ public:
const InfoHash& hash,
const Query& query,
const Blob& token,
Tid socketId,
Sp<Request> previous,
RequestCb&& on_done,
RequestExpiredCb&& on_expired);
RequestExpiredCb&& on_expired,
SocketCb&& socket_cb);
/**
* Send a "announce" request to a given node.
*
@@ -403,26 +400,7 @@ public:
const Value::Id& vid,
const Blob& token,
RequestCb&& on_done,
RequestErrorCb&& on_error,
RequestExpiredCb&& on_expired);
/**
* Send a "update" request to a given node. Used for Listen operations
*
* @param n The node.
* @param hash The target hash.
* @param values The values.
* @param created Time id.
* @param token A security token.
* @param sid The socket id.
*
* @return the request with information concerning its success.
*/
Sp<Request> sendUpdateValues(Sp<Node> n,
const InfoHash& infohash,
const std::vector<Sp<Value>>& values,
time_point created,
const Blob& token,
const size_t& sid);
/**
* Parses a message and calls appropriate callbacks.
@@ -435,8 +413,8 @@ public:
*/
void processMessage(const uint8_t *buf, size_t buflen, SockAddr addr);
Sp<Node> insertNode(const InfoHash& id, const SockAddr& addr) {
auto n = cache.getNode(id, addr, scheduler.time(), 0);
Sp<Node> insertNode(const InfoHash& myid, const SockAddr& addr) {
auto n = cache.getNode(myid, addr, scheduler.time(), 0);
onNewNode(n, 0);
return n;
}
@@ -512,20 +490,19 @@ private:
void sendRequest(const Sp<Request>& request);
struct MessageStats {
unsigned ping {0};
unsigned find {0};
unsigned get {0};
unsigned put {0};
unsigned listen {0};
unsigned refresh {0};
unsigned updateValue {0};
unsigned ping {0};
unsigned find {0};
unsigned get {0};
unsigned put {0};
unsigned listen {0};
unsigned refresh {0};
};
// basic wrapper for socket sendto function
int send(const SockAddr& addr, const char *buf, size_t len, bool confirmed = false);
void sendValueParts(Tid tid, const std::vector<Blob>& svals, const SockAddr& addr);
void sendValueParts(const TransId& tid, const std::vector<Blob>& svals, const SockAddr& addr);
std::vector<Blob> packValueHeader(msgpack::sbuffer&, const std::vector<Sp<Value>>&);
void maintainRxBuffer(Tid tid);
-8
View File
@@ -61,9 +61,6 @@ using PacketList = std::list<ReceivedPacket>;
class OPENDHT_PUBLIC DatagramSocket {
public:
/** A function that takes a list of new received packets and
* optionally returns consumed packets for recycling.
**/
using OnReceive = std::function<PacketList(PacketList&& packets)>;
virtual ~DatagramSocket() {};
@@ -88,11 +85,6 @@ public:
virtual const SockAddr& getBoundRef(sa_family_t family = AF_UNSPEC) const = 0;
/** Virtual resolver mothod allows to implement custom resolver */
virtual std::vector<SockAddr> resolve(const std::string& host, const std::string& service = {}) {
return SockAddr::resolve(host, service);
}
virtual void stop() = 0;
protected:
+5 -14
View File
@@ -21,8 +21,6 @@
#include <random>
#include <algorithm>
#include <functional>
#include <thread>
#include <stdexcept>
namespace dht {
namespace crypto {
@@ -113,18 +111,11 @@ using random_device = std::random_device;
template<class T = std::mt19937, std::size_t N = T::state_size>
auto getSeededRandomEngine () -> typename std::enable_if<!!N, T>::type {
typename T::result_type random_data[N];
for (unsigned i=0; i<256; i++) {
try {
random_device source;
std::generate(std::begin(random_data), std::end(random_data), std::ref(source));
std::seed_seq seeds(std::begin(random_data), std::end(random_data));
T seededEngine (seeds);
return seededEngine;
} catch (...) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
throw std::runtime_error("Can't seed random engine");
random_device source;
std::generate(std::begin(random_data), std::end(random_data), std::ref(source));
std::seed_seq seeds(std::begin(random_data), std::end(random_data));
T seededEngine (seeds);
return seededEngine;
}
}} // dht::crypto
+2
View File
@@ -115,6 +115,8 @@ public:
inline time_point syncTime() { return (now = clock::now()); }
inline void syncTime(const time_point& n) { now = n; }
inline size_t size() const { return timers.size(); }
private:
time_point now {clock::now()};
std::multimap<time_point, Sp<Job>> timers {}; /* the jobs ordered by time */
+3 -9
View File
@@ -180,6 +180,9 @@ public:
NodeStats getNodesStats(sa_family_t af) const override {
return dht_->getNodesStats(af);
}
NodeMetrics getNodeMetrics() const override {
return dht_->getNodeMetrics();
}
std::vector<unsigned> getNodeMessageStats(bool in = false) override {
return dht_->getNodeMessageStats(in);
}
@@ -201,9 +204,6 @@ public:
time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen, const time_point& now) override {
return dht_->periodic(buf, buflen, from, fromlen, now);
}
NodeStatus updateStatus(sa_family_t af) override {
return dht_->updateStatus(af);
}
NodeStatus getStatus(sa_family_t af) const override {
return dht_->getStatus(af);
}
@@ -219,12 +219,6 @@ public:
const ValueType& getType(ValueType::Id type_id) const override {
return dht_->getType(type_id);
}
void addBootstrap(const std::string& host, const std::string& service) override {
dht_->addBootstrap(host, service);
}
void clearBootstrap() override {
dht_->clearBootstrap();
}
void insertNode(const InfoHash& id, const SockAddr& sa) override {
dht_->insertNode(id, sa);
}
+4 -9
View File
@@ -105,7 +105,8 @@ static std::string
print_duration(DT d) {
if (d < std::chrono::seconds(0)) {
return "-" + print_duration(-d);
} else if (d < std::chrono::milliseconds(1)) {
}
if (d < std::chrono::milliseconds(1)) {
return to_str(std::chrono::duration_cast<std::chrono::duration<double, std::micro>>(d).count()) + " us";
} else if (d < std::chrono::seconds(1)) {
return to_str(std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(d).count()) + " ms";
@@ -173,13 +174,7 @@ unpackMsg(Blob b) {
msgpack::unpacked unpackMsg(Blob b);
msgpack::object* findMapValue(const msgpack::object& map, const char* key, size_t length);
inline msgpack::object* findMapValue(const msgpack::object& map, const char* key) {
return findMapValue(map, key, strlen(key));
}
inline msgpack::object* findMapValue(const msgpack::object& map, const std::string& key) {
return findMapValue(map, key.c_str(), key.size());
}
msgpack::object* findMapValue(const msgpack::object& map, const char* key);
msgpack::object* findMapValue(const msgpack::object& map, const std::string& key);
} // namespace dht
+3 -3
View File
@@ -3,9 +3,9 @@ exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: OpenDHT
Description: C++14 Distributed Hash Table library
Description: C++ Distributed Hash Table library
Version: @VERSION@
Libs: -L${libdir} -lopendht
Libs.private: @http_parser_lib@ -pthread
Requires.private: gnutls >= 3.3, nettle >= 2.4@argon2_lib@@jsoncpp_lib@@openssl_lib@
Libs.private: -lpthread
Requires.private: gnutls >= 3.1@argon2_lib@
Cflags: -I${includedir}
+16 -1
View File
@@ -94,7 +94,6 @@ NodeStats::toString() const
{
std::stringstream ss;
ss << "Known nodes: " << good_nodes << " good, " << dubious_nodes << " dubious, " << incoming_nodes << " incoming." << std::endl;
ss << searches << " searches, " << node_cache_size << " total cached nodes" << std::endl;
if (table_depth > 1) {
ss << "Routing table depth: " << table_depth << std::endl;
ss << "Network size estimation: " << getNetworkSizeEstimation() << " nodes" << std::endl;
@@ -145,6 +144,7 @@ NodeInfo::toJson() const
val["ipv4"] = ipv4.toJson();
val["ipv6"] = ipv6.toJson();
val["ops"] = Json::Value::LargestUInt(ongoing_ops);
val["metrics"] = metrics.toJson();
return val;
}
@@ -158,6 +158,21 @@ NodeInfo::NodeInfo(const Json::Value& v)
ongoing_ops = v["ops"].asLargestUInt();
}
Json::Value
NodeMetrics::toJson() const
{
Json::Value val;
val["scheduler_size"] = Json::Value::LargestUInt(scheduler_size);
val["node_cache_size"] = Json::Value::LargestUInt(node_cache_size);
val["request_count"] = Json::Value::LargestUInt(request_count);
val["rate_limiter_size"] = Json::Value::LargestUInt(rate_limiter_size);
val["reported_addr_num"] = Json::Value::LargestUInt(reported_addr_num);
val["store_size"] = Json::Value::LargestUInt(store_size);
val["store_values"] = Json::Value::LargestUInt(store_values);
val["store_quota_size"] = Json::Value::LargestUInt(store_quota_size);
return val;
}
#endif
+186 -216
View File
@@ -17,6 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "dht.h"
#include "rng.h"
#include "search.h"
@@ -42,41 +43,13 @@ constexpr duration Dht::REANNOUNCE_MARGIN;
static constexpr size_t MAX_REQUESTS_PER_SEC {8 * 1024};
NodeStatus
Dht::updateStatus(sa_family_t af)
Dht::getStatus(sa_family_t af) const
{
auto& d = dht(af);
auto old = d.status;
d.status = d.getStatus(scheduler.time());
if (d.status != old) {
auto& other = dht(af == AF_INET ? AF_INET6 : AF_INET);
if (other.status == NodeStatus::Disconnected && d.status == NodeStatus::Disconnected)
onDisconnected();
else if (other.status == NodeStatus::Connected || d.status == NodeStatus::Connected) {
// On connected
if (bootstrapJob) {
bootstrapJob->cancel();
bootstrapJob.reset();
}
bootstrap_period = std::chrono::seconds(10);
}
}
return d.status;
}
NodeStatus
Dht::Kad::getStatus(time_point now) const
{
unsigned dubious = 0;
for (const auto& b : buckets) {
for (auto& n : b.nodes) {
if (n->isGood(now)) {
return NodeStatus::Connected;
} else if (not n->isExpired())
dubious++;
}
}
auto& ping = pending_pings;
if (dubious or ping)
const auto& stats = getNodesStats(af);
if (stats.good_nodes)
return NodeStatus::Connected;
auto& ping = af == AF_INET ? pending_pings4 : pending_pings6;
if (ping or stats.getKnownNodes())
return NodeStatus::Connecting;
return NodeStatus::Disconnected;
}
@@ -245,8 +218,8 @@ Dht::expireSearches()
return b;
} else { return false; }
};
erase_if(dht4.searches, expired);
erase_if(dht6.searches, expired);
erase_if(searches4, expired);
erase_if(searches6, expired);
}
void
@@ -490,29 +463,7 @@ void Dht::searchSendAnnounceValue(const Sp<Search>& sr) {
logger_->w(sr->id, sn->node->id, "[search %s] [node %s] sending 'refresh' (vid: %d)",
sr->id.toString().c_str(), sn->node->toString().c_str(), a.value->id);
sn->acked[a.value->id] = {
network_engine.sendRefreshValue(sn->node, sr->id, a.value->id, sn->token, onDone,
[this, ws, node=sn->node, v=a.value,
onDone,
onExpired,
created = a.permanent ? time_point::max() : a.created,
next_refresh_time
](const net::Request& req, net::DhtProtocolException&& e){
if (e.getCode() == net::DhtProtocolException::NOT_FOUND) {
if (logger_)
logger_->e(node->id, "[node %s] returned error 404: storage not found", node->toString().c_str());
if (auto sr = ws.lock()) {
if (auto sn = sr->getNode(node)) {
sn->acked[v->id] = {
network_engine.sendAnnounceValue(sn->node, sr->id, v, created, sn->token, onDone, onExpired),
next_refresh_time
};
scheduler.edit(sr->nextSearchStep, scheduler.time());
return true;
}
}
}
return false;
}, onExpired),
network_engine.sendRefreshValue(sn->node, sr->id, a.value->id, sn->token, onDone, onExpired),
next_refresh_time
};
} else {
@@ -591,30 +542,33 @@ Dht::searchSynchedNodeListen(const Sp<Search>& sr, SearchNode& n)
std::weak_ptr<Search> ws = sr;
for (const auto& l : sr->listeners) {
const auto& query = l.second.query;
auto r = n.listenStatus.find(query);
if (n.getListenTime(r, listenExp) > scheduler.time())
auto list_token = l.first;
if (n.getListenTime(query, listenExp) > scheduler.time())
continue;
// if (logger_)
// logger_->d(sr->id, n.node->id, "[search %s] [node %s] sending 'listen'",
// sr->id.toString().c_str(), n.node->toString().c_str());
auto r = n.listenStatus.find(query);
if (r == n.listenStatus.end()) {
r = n.listenStatus.emplace(std::piecewise_construct,
std::forward_as_tuple(query),
std::forward_as_tuple(
l.second.get_cb,
l.second.sync_cb,
n.node->openSocket([this,ws,query](const Sp<Node>& node, net::RequestAnswer&& answer) mutable {
/* on new values */
if (auto sr = ws.lock()) {
scheduler.edit(sr->nextSearchStep, scheduler.time());
sr->insertNode(node, scheduler.time(), answer.ntoken);
if (auto sn = sr->getNode(node)) {
sn->onValues(query, std::move(answer), types, scheduler);
}
[ws,list_token](const std::vector<Sp<Value>>& values, bool expired){
if (auto sr = ws.lock()) {
auto l = sr->listeners.find(list_token);
if (l != sr->listeners.end()) {
l->second.get_cb(values, expired);
}
}))).first;
}
}, [ws,list_token] (ListenSyncStatus status) {
if (auto sr = ws.lock()) {
auto l = sr->listeners.find(list_token);
if (l != sr->listeners.end()) {
l->second.sync_cb(status);
}
}
})).first;
r->second.cacheExpirationJob = scheduler.add(time_point::max(), [this,ws,query,node=n.node]{
if (auto sr = ws.lock()) {
if (auto sn = sr->getNode(node)) {
@@ -623,7 +577,8 @@ Dht::searchSynchedNodeListen(const Sp<Search>& sr, SearchNode& n)
}
});
}
auto new_req = network_engine.sendListen(n.node, sr->id, *query, n.token, r->second.socketId,
auto prev_req = r != n.listenStatus.end() ? r->second.req : nullptr;
auto new_req = network_engine.sendListen(n.node, sr->id, *query, n.token, prev_req,
[this,ws,query](const net::Request& req, net::RequestAnswer&& answer) mutable
{ /* on done */
if (auto sr = ws.lock()) {
@@ -643,6 +598,16 @@ Dht::searchSynchedNodeListen(const Sp<Search>& sr, SearchNode& n)
if (auto sn = sr->getNode(req.node))
sn->listenStatus.erase(query);
}
},
[this,ws,query](const Sp<Node>& node, net::RequestAnswer&& answer) mutable
{ /* on new values */
if (auto sr = ws.lock()) {
scheduler.edit(sr->nextSearchStep, scheduler.time());
sr->insertNode(node, scheduler.time(), answer.ntoken);
if (auto sn = sr->getNode(node)) {
sn->onValues(query, std::move(answer), types, scheduler);
}
}
}
);
// Here the request may have failed and the CachedListenStatus removed
@@ -725,8 +690,8 @@ Dht::searchStep(Sp<Search> sr)
while (sr->currentlySolicitedNodeCount() < MAX_REQUESTED_SEARCH_NODES and searchSendGetValues(sr));
if (sr->getNumberOfConsecutiveBadNodes() >= std::min<size_t>(sr->nodes.size(), SEARCH_MAX_BAD_NODES))
if (sr->getNumberOfConsecutiveBadNodes() >= std::min(sr->nodes.size(),
static_cast<size_t>(SEARCH_MAX_BAD_NODES)))
{
if (logger_)
logger_->w(sr->id, "[search %s IPv%c] expired", sr->id.toString().c_str(), sr->af == AF_INET ? '4' : '6');
@@ -785,7 +750,7 @@ Dht::search(const InfoHash& id, sa_family_t af, GetCallback gcb, QueryCallback q
sr->done = false;
sr->expired = false;
} else {
if (srs.size() < max_searches) {
if (searches4.size() + searches6.size() < MAX_SEARCHES) {
sr = std::make_shared<Search>();
srs.emplace(id, sr);
} else {
@@ -833,12 +798,47 @@ Dht::announce(const InfoHash& id,
{
auto& srs = searches(af);
auto srp = srs.find(id);
if (auto sr = srp == srs.end() ? search(id, af) : srp->second) {
sr->put(value, callback, created, permanent);
scheduler.edit(sr->nextSearchStep, scheduler.time());
} else if (callback) {
callback(false, {});
auto sr = srp == srs.end() ? search(id, af) : srp->second;
if (!sr) {
if (callback)
callback(false, {});
return;
}
sr->done = false;
sr->expired = false;
auto a_sr = std::find_if(sr->announce.begin(), sr->announce.end(), [&](const Announce& a){
return a.value->id == value->id;
});
if (a_sr == sr->announce.end()) {
sr->announce.emplace_back(Announce {permanent, value, created, callback});
for (auto& n : sr->nodes) {
n->probe_query.reset();
n->acked[value->id].first.reset();
}
} else {
a_sr->permanent = permanent;
a_sr->created = created;
if (a_sr->value != value) {
a_sr->value = value;
for (auto& n : sr->nodes) {
n->acked[value->id].first.reset();
n->probe_query.reset();
}
}
if (sr->isAnnounced(value->id)) {
if (a_sr->callback)
a_sr->callback(true, {});
a_sr->callback = {};
if (callback)
callback(true, {});
return;
} else {
if (a_sr->callback)
a_sr->callback(false, {});
a_sr->callback = callback;
}
}
scheduler.edit(sr->nextSearchStep, scheduler.time());
}
size_t
@@ -872,7 +872,7 @@ Dht::listen(const InfoHash& id, ValueCallback cb, Value::Filter f, Where where)
auto query = std::make_shared<Query>(Select{}, std::move(where));
auto filter = f.chain(query->where.getFilter());
auto st = store.find(id);
if (st == store.end() && store.size() < max_store_keys)
if (st == store.end() && store.size() < MAX_HASHES)
st = store.emplace(id, scheduler.time() + MAX_STORAGE_MAINTENANCE_EXPIRE_TIME).first;
size_t tokenlocal = 0;
@@ -918,8 +918,8 @@ Dht::cancelListen(const InfoHash& id, size_t token)
srp->second->cancelListen(token, scheduler);
}
};
searches_cancel_listen(dht4.searches, std::get<1>(it->second));
searches_cancel_listen(dht6.searches, std::get<2>(it->second));
searches_cancel_listen(searches4, std::get<1>(it->second));
searches_cancel_listen(searches6, std::get<2>(it->second));
listeners.erase(it);
return true;
}
@@ -1130,8 +1130,8 @@ Dht::getPut(const InfoHash& id) const
auto vals = srp->second->getPut();
ret.insert(ret.end(), vals.begin(), vals.end());
};
find_values(dht4.searches);
find_values(dht6.searches);
find_values(searches4);
find_values(searches6);
return ret;
}
@@ -1142,9 +1142,9 @@ Dht::getPut(const InfoHash& id, const Value::Id& vid) const
auto srp = srs.find(id);
return (srp != srs.end()) ? srp->second->getPut(vid) : Sp<Value> {};
};
if (auto v4 = find_value(dht4.searches))
if (auto v4 = find_value(searches4))
return v4;
if (auto v6 = find_value(dht6.searches))
if (auto v6 = find_value(searches6))
return v6;
return {};
}
@@ -1157,8 +1157,8 @@ Dht::cancelPut(const InfoHash& id, const Value::Id& vid)
auto srp = srs.find(id);
return (srp != srs.end()) ? srp->second->cancelPut(vid) : false;
};
canceled |= sr_cancel_put(dht4.searches);
canceled |= sr_cancel_put(dht6.searches);
canceled |= sr_cancel_put(searches4);
canceled |= sr_cancel_put(searches6);
if (canceled)
storageErase(id, vid);
return canceled;
@@ -1209,7 +1209,7 @@ Dht::storageChanged(const InfoHash& id, Storage& st, ValueStorage& v, bool newVa
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, l.second.version);
std::move(vals), l.second.query);
}
}
}
@@ -1226,7 +1226,7 @@ Dht::storageStore(const InfoHash& id, const Sp<Value>& value, time_point created
auto st = store.find(id);
if (st == store.end()) {
if (store.size() >= max_store_keys)
if (store.size() >= MAX_HASHES)
return false;
auto st_i = store.emplace(id, now);
st = st_i.first;
@@ -1267,12 +1267,12 @@ Dht::storageErase(const InfoHash& id, Value::Id vid)
}
void
Dht::storageAddListener(const InfoHash& id, const Sp<Node>& node, size_t socket_id, Query&& query, int version)
Dht::storageAddListener(const InfoHash& id, const Sp<Node>& node, size_t socket_id, Query&& query)
{
const auto& now = scheduler.time();
auto st = store.find(id);
if (st == store.end()) {
if (store.size() >= max_store_keys)
if (store.size() >= MAX_HASHES)
return;
st = store.emplace(id, now).first;
}
@@ -1282,10 +1282,10 @@ Dht::storageAddListener(const InfoHash& id, const Sp<Node>& node, size_t socket_
auto vals = st->second.get(query.where.getFilter());
if (not vals.empty()) {
network_engine.tellListener(node, socket_id, id, WANT4 | WANT6, makeToken(node->getAddr(), false),
dht4.buckets.findClosestNodes(id, now, TARGET_NODES), dht6.buckets.findClosestNodes(id, now, TARGET_NODES),
std::move(vals), query, version);
buckets4.findClosestNodes(id, now, TARGET_NODES), buckets6.findClosestNodes(id, now, TARGET_NODES),
std::move(vals), query);
}
node_listeners.emplace(socket_id, Listener {now, std::forward<Query>(query), version});
node_listeners.emplace(socket_id, Listener {now, std::forward<Query>(query)});
}
else
l->second.refresh(now, std::forward<Query>(query));
@@ -1320,7 +1320,7 @@ Dht::expireStore(decltype(store)::iterator i)
id.toString().c_str(),
node_listeners.first->toString().c_str());
Blob ntoken = makeToken(node_listeners.first->getAddr(), false);
network_engine.tellListenerExpired(node_listeners.first, l.first, id, ntoken, ids, l.second.version);
network_engine.tellListenerExpired(node_listeners.first, l.first, id, ntoken, ids);
}
}
}
@@ -1460,17 +1460,11 @@ Dht::tokenMatch(const Blob& token, const SockAddr& addr) const
NodeStats
Dht::getNodesStats(sa_family_t af) const
{
NodeStats stats = dht(af).getNodesStats(scheduler.time(), myid);
stats.node_cache_size = network_engine.getNodeCacheSize(af);
return stats;
}
NodeStats
Dht::Kad::getNodesStats(time_point now, const InfoHash& myid) const
{
NodeStats stats {};
for (const auto& b : buckets) {
const auto& now = scheduler.time();
const auto& bcks = buckets(af);
for (const auto& b : bcks) {
for (auto& n : b.nodes) {
if (n->isGood(now)) {
stats.good_nodes++;
@@ -1482,11 +1476,26 @@ Dht::Kad::getNodesStats(time_point now, const InfoHash& myid) const
if (b.cached)
stats.cached_nodes++;
}
stats.table_depth = buckets.depth(buckets.findBucket(myid));
stats.searches = searches.size();
stats.table_depth = bcks.depth(bcks.findBucket(myid));
stats.searches = searches(af).size();
stats.cached_nodes = network_engine.getNodeCacheSize(af);
return stats;
}
NodeMetrics
Dht::getNodeMetrics() const
{
NodeMetrics metrics;
metrics.scheduler_size = scheduler.size();
metrics.node_cache_size = network_engine.getNodeCacheSize();
metrics.rate_limiter_size = network_engine.getRateLimiterSize();
metrics.store_size = store.size();
metrics.store_quota_size = store_quota.size();
metrics.reported_addr_num = reported_addr.size();
metrics.request_count = net::REQ_COUNT.load();
return metrics;
}
void
Dht::dumpBucket(const Bucket& b, std::ostream& out) const
{
@@ -1604,18 +1613,18 @@ Dht::dumpTables() const
out << "My id " << myid << std::endl;
out << "Buckets IPv4 :" << std::endl;
for (const auto& b : dht4.buckets)
for (const auto& b : buckets4)
dumpBucket(b, out);
out << "Buckets IPv6 :" << std::endl;
for (const auto& b : dht6.buckets)
for (const auto& b : buckets6)
dumpBucket(b, out);
auto dump_searches = [&](std::map<InfoHash, Sp<Search>> srs) {
for (auto& srp : srs)
dumpSearch(*srp.second, out);
};
dump_searches(dht4.searches);
dump_searches(dht6.searches);
dump_searches(searches4);
dump_searches(searches6);
out << std::endl;
out << getStorageLog() << std::endl;
@@ -1691,24 +1700,24 @@ std::string
Dht::getSearchesLog(sa_family_t af) const
{
std::stringstream out;
auto num_searches = dht4.searches.size() + dht6.searches.size();
auto num_searches = searches4.size() + searches6.size();
if (num_searches > 8) {
if (not af or af == AF_INET)
for (const auto& sr : dht4.searches)
for (const auto& sr : searches4)
out << "[search " << sr.first << " IPv4]" << std::endl;
if (not af or af == AF_INET6)
for (const auto& sr : dht6.searches)
for (const auto& sr : searches6)
out << "[search " << sr.first << " IPv6]" << std::endl;
} else {
out << "s:synched, u:updated, a:announced, c:candidate, f:cur req, x:expired, *:known" << std::endl;
if (not af or af == AF_INET)
for (const auto& sr : dht4.searches)
for (const auto& sr : searches4)
dumpSearch(*sr.second, out);
if (not af or af == AF_INET6)
for (const auto& sr : dht6.searches)
for (const auto& sr : searches6)
dumpSearch(*sr.second, out);
}
out << "Total: " << num_searches << " searches (" << dht4.searches.size() << " IPv4, " << dht6.searches.size() << " IPv6)." << std::endl;
out << "Total: " << num_searches << " searches (" << searches4.size() << " IPv4, " << searches6.size() << " IPv6)." << std::endl;
return out.str();
}
@@ -1729,9 +1738,9 @@ Dht::getSearchLog(const InfoHash& id, sa_family_t af) const
Dht::~Dht()
{
for (auto& s : dht4.searches)
for (auto& s : searches4)
s.second->clear();
for (auto& s : dht6.searches)
for (auto& s : searches6)
s.second->clear();
}
@@ -1750,12 +1759,7 @@ fromDhtConfig(const Config& config)
Dht::Dht() : store(), network_engine(logger_, rd, scheduler, {}) {}
Dht::Dht(std::unique_ptr<net::DatagramSocket>&& sock, const Config& config, const Sp<Logger>& l)
: DhtInterface(l),
myid(config.node_id ? config.node_id : InfoHash::getRandom(rd)),
store(),
store_quota(),
max_store_keys(config.max_store_size ? (int)config.max_store_size : MAX_HASHES),
max_searches(config.max_searches ? (int)config.max_searches : MAX_SEARCHES),
: DhtInterface(l), myid(config.node_id ? config.node_id : InfoHash::getRandom()), store(), store_quota(),
network_engine(myid, fromDhtConfig(config), std::move(sock), logger_, rd, scheduler,
std::bind(&Dht::onError, this, _1, _2),
std::bind(&Dht::onNewNode, this, _1, _2),
@@ -1763,7 +1767,7 @@ Dht::Dht(std::unique_ptr<net::DatagramSocket>&& sock, const Config& config, cons
std::bind(&Dht::onPing, this, _1),
std::bind(&Dht::onFindNode, this, _1, _2, _3),
std::bind(&Dht::onGetValues, this, _1, _2, _3, _4),
std::bind(&Dht::onListen, this, _1, _2, _3, _4, _5, _6),
std::bind(&Dht::onListen, this, _1, _2, _3, _4, _5),
std::bind(&Dht::onAnnounce, this, _1, _2, _3, _4, _5),
std::bind(&Dht::onRefresh, this, _1, _2, _3, _4)),
persistPath(config.persist_path),
@@ -1776,12 +1780,12 @@ Dht::Dht(std::unique_ptr<net::DatagramSocket>&& sock, const Config& config, cons
if (not s or (not s->hasIPv4() and not s->hasIPv6()))
throw DhtException("Opened socket required");
if (s->hasIPv4()) {
dht4.buckets = {Bucket {AF_INET}};
dht4.buckets.is_client = config.is_bootstrap;
buckets4 = {Bucket {AF_INET}};
buckets4.is_client = config.is_bootstrap;
}
if (s->hasIPv6()) {
dht6.buckets = {Bucket {AF_INET6}};
dht6.buckets.is_client = config.is_bootstrap;
buckets6 = {Bucket {AF_INET6}};
buckets6.is_client = config.is_bootstrap;
}
search_id = std::uniform_int_distribution<decltype(search_id)>{}(rd);
@@ -1793,13 +1797,13 @@ Dht::Dht(std::unique_ptr<net::DatagramSocket>&& sock, const Config& config, cons
secret = std::uniform_int_distribution<uint64_t>{}(rd);
rotateSecrets();
if (not persistPath.empty())
loadState(persistPath);
expire();
if (logger_)
logger_->d("DHT node initialised with ID %s", myid.toString().c_str());
if (not persistPath.empty())
loadState(persistPath);
}
bool
@@ -1976,39 +1980,13 @@ Dht::expire()
uniform_duration_distribution<> time_dis(std::chrono::minutes(2), std::chrono::minutes(6));
auto expire_stuff_time = scheduler.time() + duration(time_dis(rd));
expireBuckets(dht4.buckets);
expireBuckets(dht6.buckets);
expireBuckets(buckets4);
expireBuckets(buckets6);
expireStore();
expireSearches();
scheduler.add(expire_stuff_time, std::bind(&Dht::expire, this));
}
void
Dht::onDisconnected()
{
if (dht4.status != NodeStatus::Disconnected || dht6.status != NodeStatus::Disconnected)
return;
if (logger_)
logger_->d(myid, "Bootstraping");
for (const auto& boootstrap : bootstrap_nodes) {
try {
auto ips = network_engine.getSocket()->resolve(boootstrap.first, boootstrap.second);
for (auto& ip : ips) {
if (ip.getPort() == 0)
ip.setPort(net::DHT_DEFAULT_PORT);
pingNode(ip);
}
} catch (const std::exception& e) {
if (logger_)
logger_->e(myid, "Can't resolve %s:%s: %s", boootstrap.first.c_str(), boootstrap.second.c_str(), e.what());
}
}
if (bootstrapJob)
bootstrapJob->cancel();
bootstrapJob = scheduler.add(scheduler.time() + bootstrap_period, std::bind(&Dht::onDisconnected, this));
bootstrap_period *= 2;
}
void
Dht::confirmNodes()
{
@@ -2016,25 +1994,25 @@ Dht::confirmNodes()
bool soon = false;
const auto& now = scheduler.time();
if (dht4.searches.empty() and dht4.status == NodeStatus::Connected) {
if (searches4.empty() and getStatus(AF_INET) == NodeStatus::Connected) {
if (logger_)
logger_->d(myid, "[confirm nodes] initial IPv4 'get' for my id (%s)", myid.toString().c_str());
search(myid, AF_INET);
}
if (dht6.searches.empty() and dht6.status == NodeStatus::Connected) {
if (searches6.empty() and getStatus(AF_INET6) == NodeStatus::Connected) {
if (logger_)
logger_->d(myid, "[confirm nodes] initial IPv6 'get' for my id (%s)", myid.toString().c_str());
search(myid, AF_INET6);
}
soon |= bucketMaintenance(dht4.buckets);
soon |= bucketMaintenance(dht6.buckets);
soon |= bucketMaintenance(buckets4);
soon |= bucketMaintenance(buckets6);
if (!soon) {
if (dht4.buckets.grow_time >= now - seconds(150))
soon |= neighbourhoodMaintenance(dht4.buckets);
if (dht6.buckets.grow_time >= now - seconds(150))
soon |= neighbourhoodMaintenance(dht6.buckets);
if (buckets4.grow_time >= now - seconds(150))
soon |= neighbourhoodMaintenance(buckets4);
if (buckets6.grow_time >= now - seconds(150))
soon |= neighbourhoodMaintenance(buckets6);
}
/* In order to maintain all buckets' age within 600 seconds, worst
@@ -2078,13 +2056,13 @@ Dht::importValues(const std::vector<ValuesExport>& import)
{
const auto& now = scheduler.time();
for (const auto& value : import) {
if (value.second.empty())
for (const auto& node : import) {
if (node.second.empty())
continue;
try {
msgpack::unpacked msg;
msgpack::unpack(msg, (const char*)value.second.data(), value.second.size());
msgpack::unpack(msg, (const char*)node.second.data(), node.second.size());
auto valarr = msg.get();
if (valarr.type != msgpack::type::ARRAY)
throw msgpack::type_error();
@@ -2099,15 +2077,15 @@ Dht::importValues(const std::vector<ValuesExport>& import)
tmp_val.msgpack_unpack(valel.via.array.ptr[1]);
} catch (const std::exception&) {
if (logger_)
logger_->e(value.first, "Error reading value at %s", value.first.toString().c_str());
logger_->e(node.first, "Error reading value at %s", node.first.toString().c_str());
continue;
}
val_time = std::min(val_time, now);
storageStore(value.first, std::make_shared<Value>(std::move(tmp_val)), val_time);
storageStore(node.first, std::make_shared<Value>(std::move(tmp_val)), val_time);
}
} catch (const std::exception&) {
if (logger_)
logger_->e(value.first, "Error reading values at %s", value.first.toString().c_str());
logger_->e(node.first, "Error reading values at %s", node.first.toString().c_str());
continue;
}
}
@@ -2119,25 +2097,25 @@ Dht::exportNodes() const
{
const auto& now = scheduler.time();
std::vector<NodeExport> nodes;
const auto b4 = dht4.buckets.findBucket(myid);
if (b4 != dht4.buckets.end()) {
const auto b4 = buckets4.findBucket(myid);
if (b4 != buckets4.end()) {
for (auto& n : b4->nodes)
if (n->isGood(now))
nodes.push_back(n->exportNode());
}
const auto b6 = dht6.buckets.findBucket(myid);
if (b6 != dht6.buckets.end()) {
const auto b6 = buckets6.findBucket(myid);
if (b6 != buckets6.end()) {
for (auto& n : b6->nodes)
if (n->isGood(now))
nodes.push_back(n->exportNode());
}
for (auto b = dht4.buckets.begin(); b != dht4.buckets.end(); ++b) {
for (auto b = buckets4.begin(); b != buckets4.end(); ++b) {
if (b == b4) continue;
for (auto& n : b->nodes)
if (n->isGood(now))
nodes.push_back(n->exportNode());
}
for (auto b = dht6.buckets.begin(); b != dht6.buckets.end(); ++b) {
for (auto b = buckets6.begin(); b != buckets6.end(); ++b) {
if (b == b6) continue;
for (auto& n : b->nodes)
if (n->isGood(now))
@@ -2161,7 +2139,7 @@ Dht::pingNode(SockAddr sa, DoneCallbackSimple&& cb)
scheduler.syncTime();
if (logger_)
logger_->d("Sending ping to %s", sa.toString().c_str());
auto& count = dht(sa.getFamily()).pending_pings;
auto& count = sa.getFamily() == AF_INET ? pending_pings4 : pending_pings6;
count++;
network_engine.sendPing(std::move(sa), [&count,cb](const net::Request&, net::RequestAnswer&&) {
count--;
@@ -2183,6 +2161,7 @@ Dht::onError(Sp<net::Request> req, net::DhtProtocolException e) {
if (logger_)
logger_->e(node->id, "[node %s] token flush", node->toString().c_str());
node->authError();
node->cancelRequest(req);
for (auto& srp : searches(node->getFamily())) {
auto& sr = srp.second;
for (auto& n : sr->nodes) {
@@ -2221,9 +2200,9 @@ Dht::onFindNode(Sp<Node> node, const InfoHash& target, want_t want)
net::RequestAnswer answer;
answer.ntoken = makeToken(node->getAddr(), false);
if (want & WANT4)
answer.nodes4 = dht4.buckets.findClosestNodes(target, now, TARGET_NODES);
answer.nodes4 = buckets4.findClosestNodes(target, now, TARGET_NODES);
if (want & WANT6)
answer.nodes6 = dht6.buckets.findClosestNodes(target, now, TARGET_NODES);
answer.nodes6 = buckets6.findClosestNodes(target, now, TARGET_NODES);
return answer;
}
@@ -2242,8 +2221,8 @@ Dht::onGetValues(Sp<Node> node, const InfoHash& hash, want_t, const Query& query
net::RequestAnswer answer {};
auto st = store.find(hash);
answer.ntoken = makeToken(node->getAddr(), false);
answer.nodes4 = dht4.buckets.findClosestNodes(hash, now, TARGET_NODES);
answer.nodes6 = dht6.buckets.findClosestNodes(hash, now, TARGET_NODES);
answer.nodes4 = buckets4.findClosestNodes(hash, now, TARGET_NODES);
answer.nodes6 = buckets6.findClosestNodes(hash, now, TARGET_NODES);
if (st != store.end() && not st->second.empty()) {
answer.values = st->second.get(query.where.getFilter());
if (logger_)
@@ -2332,7 +2311,7 @@ void Dht::onGetValuesDone(const Sp<Node>& node,
}
net::RequestAnswer
Dht::onListen(Sp<Node> node, const InfoHash& hash, const Blob& token, size_t socket_id, const Query& query, int version)
Dht::onListen(Sp<Node> node, const InfoHash& hash, const Blob& token, size_t socket_id, const Query& query)
{
if (not hash) {
if (logger_)
@@ -2348,7 +2327,7 @@ Dht::onListen(Sp<Node> node, const InfoHash& hash, const Blob& token, size_t soc
throw net::DhtProtocolException {net::DhtProtocolException::UNAUTHORIZED, net::DhtProtocolException::LISTEN_WRONG_TOKEN};
}
Query q = query;
storageAddListener(hash, node, socket_id, std::move(q), version);
storageAddListener(hash, node, socket_id, std::move(q));
return {};
}
@@ -2487,7 +2466,7 @@ Dht::storageRefresh(const InfoHash& id, Value::Id vid)
id.toString().c_str(),
node_listeners.first->toString().c_str());
Blob ntoken = makeToken(node_listeners.first->getAddr(), false);
network_engine.tellListenerRefreshed(node_listeners.first, l.first, id, ntoken, ids, l.second.version);
network_engine.tellListenerRefreshed(node_listeners.first, l.first, id, ntoken, ids);
}
}
}
@@ -2511,24 +2490,12 @@ Dht::onAnnounceDone(const Sp<Node>& node, net::RequestAnswer& answer, Sp<Search>
}
struct DhtState {
unsigned v {1};
InfoHash id;
std::vector<NodeExport> nodes;
std::vector<ValuesExport> values;
MSGPACK_DEFINE_MAP(v, id, nodes, values)
};
void
Dht::saveState(const std::string& path) const
{
DhtState state;
state.id = myid;
state.nodes = exportNodes();
state.values = exportValues();
std::ofstream file(path);
msgpack::pack(file, state);
msgpack::pack(file, exportNodes());
msgpack::pack(file, exportValues());
}
void
@@ -2554,16 +2521,19 @@ Dht::loadState(const std::string& path)
// Import nodes
msgpack::object_handle oh;
if (pac.next(oh)) {
auto state = oh.get().as<DhtState>();
if (logger_)
logger_->d("Importing %zu nodes", state.nodes.size());
if (state.id)
myid = state.id;
std::vector<Sp<Node>> tmpNodes;
tmpNodes.reserve(state.nodes.size());
for (const auto& node : state.nodes)
tmpNodes.emplace_back(network_engine.insertNode(node.id, SockAddr(node.ss, node.sslen)));
importValues(state.values);
{
auto imported_nodes = oh.get().as<std::vector<NodeExport>>();
if (logger_)
logger_->d("Importing %zu nodes", imported_nodes.size());
for (const auto& node : imported_nodes)
insertNode(node);
}
if (pac.next(oh)) {
auto imported_values = oh.get().as<std::vector<ValuesExport>>();
if (logger_)
logger_->d("Importing %zu values", imported_values.size());
importValues(imported_values);
}
}
} catch (const std::exception& e) {
if (logger_)
+1 -1
View File
@@ -98,7 +98,7 @@ getRandomSessionId(size_t length = 8) {
static constexpr const char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_`{|}~";
std::string str(length, 0);
crypto::random_device rdev;
std::uniform_int_distribution<> dist(0, (sizeof(chars)/sizeof(char)) - 2);
std::uniform_int_distribution<> dist(0, (sizeof(chars)/sizeof(char)) - 1);
std::generate_n( str.begin(), length, [&]{ return chars[dist(rdev)]; } );
return str;
}
+124 -338
View File
@@ -34,7 +34,6 @@
#include <functional>
#include <limits>
#include <iostream>
#include <fstream>
using namespace std::placeholders;
using namespace std::chrono_literals;
@@ -174,73 +173,26 @@ struct DhtProxyServer::RestRouterTraits : public restinio::default_traits_t
using connection_state_listener_t = ConnectionListener;
};
void
DhtProxyServer::PermanentPut::msgpack_unpack(const msgpack::object& o)
{
if (auto cid = findMapValue(o, "cid")) {
clientId = cid->as<std::string>();
}
if (auto exp = findMapValue(o, "exp")) {
expiration = from_time_t(exp->as<time_t>());
}
if (auto token = findMapValue(o, "token")) {
pushToken = token->as<std::string>();
}
if (auto sid = findMapValue(o, "sid")) {
if (not sessionCtx)
sessionCtx = std::make_shared<PushSessionContext>(sid->as<std::string>());
else
sessionCtx->sessionId = sid->as<std::string>();
}
if (auto t = findMapValue(o, "t")) {
type = t->as<PushType>();
}
if (auto val = findMapValue(o, "value")) {
value = std::make_shared<dht::Value>(*val);
}
}
#ifdef OPENDHT_PUSH_NOTIFICATIONS
void
DhtProxyServer::Listener::msgpack_unpack(const msgpack::object& o)
{
if (auto cid = findMapValue(o, "cid")) {
clientId = cid->as<std::string>();
}
if (auto exp = findMapValue(o, "exp")) {
expiration = from_time_t(exp->as<time_t>());
}
if (auto sid = findMapValue(o, "sid")) {
if (not sessionCtx)
sessionCtx = std::make_shared<PushSessionContext>(sid->as<std::string>());
else
sessionCtx->sessionId = sid->as<std::string>();
}
if (auto t = findMapValue(o, "t")) {
type = t->as<PushType>();
}
}
#endif
DhtProxyServer::DhtProxyServer(const std::shared_ptr<DhtRunner>& dht,
const ProxyServerConfig& config,
const std::shared_ptr<dht::Logger>& logger
DhtProxyServer::DhtProxyServer(
crypto::Identity identity,
std::shared_ptr<DhtRunner> dht, in_port_t port, const std::string& pushServer,
std::shared_ptr<Logger> logger
)
: ioContext_(std::make_shared<asio::io_context>()),
dht_(dht), persistPath_(config.persistStatePath), logger_(logger),
dht_(dht), logger_(logger),
printStatsTimer_(std::make_unique<asio::steady_timer>(*ioContext_, 3s)),
connListener_(std::make_shared<ConnectionListener>(std::bind(&DhtProxyServer::onConnectionClosed, this, std::placeholders::_1))),
pushServer_(config.pushServer)
pushServer_(pushServer)
{
if (not dht_)
throw std::invalid_argument("A DHT instance must be provided");
if (logger_)
logger_->d("[proxy:server] [init] running on %i", config.port);
if (not pushServer_.empty()){
logger_->d("[proxy:server] [init] running on %i", port);
if (not pushServer.empty()){
#ifdef OPENDHT_PUSH_NOTIFICATIONS
if (logger_)
logger_->d("[proxy:server] [init] using push server %s", pushServer_.c_str());
logger_->d("[proxy:server] [init] using push server %s", pushServer.c_str());
#else
if (logger_)
logger_->e("[proxy:server] [init] opendht built without push notification support");
@@ -250,9 +202,9 @@ DhtProxyServer::DhtProxyServer(const std::shared_ptr<DhtRunner>& dht,
jsonBuilder_["commentStyle"] = "None";
jsonBuilder_["indentation"] = "";
if (!pushServer_.empty()){
if (!pushServer.empty()){
// no host delim, assume port only
if (pushServer_.find(":") == std::string::npos)
if (pushServer.find(":") == std::string::npos)
pushServer_ = "localhost:" + pushServer_;
// define http request destination for push notifications
pushHostPort_ = splitPort(pushServer_);
@@ -260,7 +212,7 @@ DhtProxyServer::DhtProxyServer(const std::shared_ptr<DhtRunner>& dht,
logger_->d("Using push server for notifications: %s:%s", pushHostPort_.first.c_str(),
pushHostPort_.second.c_str());
}
if (config.identity.first and config.identity.second) {
if (identity.first and identity.second) {
asio::error_code ec;
// define tls context
asio::ssl::context tls_context { asio::ssl::context::sslv23 };
@@ -274,13 +226,13 @@ DhtProxyServer::DhtProxyServer(const std::shared_ptr<DhtRunner>& dht,
SSL_CTX_set_options(tls_context.native_handle(), SSL_OP_NO_RENEGOTIATION); // CVE-2009-3555
#endif
// node private key
auto key = config.identity.first->serialize();
auto key = identity.first->serialize();
tls_context.use_private_key(asio::const_buffer{key.data(), key.size()},
asio::ssl::context::file_format::pem, ec);
if (ec)
throw std::runtime_error("Error setting node's private key: " + ec.message());
// certificate chain
auto certchain = config.identity.second->toString(true/*chain*/);
auto certchain = identity.second->toString(true/*chain*/);
tls_context.use_certificate_chain(asio::const_buffer{certchain.data(), certchain.size()}, ec);
if (ec)
throw std::runtime_error("Error setting certificate chain: " + ec.message());
@@ -289,7 +241,7 @@ DhtProxyServer::DhtProxyServer(const std::shared_ptr<DhtRunner>& dht,
// build http server
auto settings = restinio::run_on_this_thread_settings_t<RestRouterTraitsTls>();
addServerSettings(settings);
settings.port(config.port);
settings.port(port);
settings.tls_context(std::move(tls_context));
httpsServer_ = std::make_unique<restinio::http_server_t<RestRouterTraitsTls>>(
ioContext_,
@@ -306,7 +258,7 @@ DhtProxyServer::DhtProxyServer(const std::shared_ptr<DhtRunner>& dht,
else {
auto settings = restinio::run_on_this_thread_settings_t<RestRouterTraits>();
addServerSettings(settings);
settings.port(config.port);
settings.port(port);
httpServer_ = std::make_unique<restinio::http_server_t<RestRouterTraits>>(
ioContext_,
std::forward<restinio::run_on_this_thread_settings_t<RestRouterTraits>>(std::move(settings))
@@ -322,158 +274,8 @@ DhtProxyServer::DhtProxyServer(const std::shared_ptr<DhtRunner>& dht,
dht->forwardAllMessages(true);
updateStats();
printStatsTimer_->async_wait(std::bind(&DhtProxyServer::handlePrintStats, this, std::placeholders::_1));
if (not persistPath_.empty()) {
try {
std::ifstream stateFile(persistPath_, std::ios::binary | std::ios::ate);
if (stateFile) {
std::streamsize size = stateFile.tellg();
stateFile.seekg(0, std::ios::beg);
if (logger_)
logger_->d("Loading proxy state from %.*s (%td bytes)", (int)persistPath_.size(), persistPath_.c_str(), size);
loadState(stateFile, size);
}
} catch (const std::exception& e) {
if (logger_)
logger_->e("Error loading state from file: %s", e.what());
}
}
}
template <typename Os>
void
DhtProxyServer::saveState(Os& stream) {
msgpack::packer<Os> pk(&stream);
pk.pack_map(2);
{
std::lock_guard<std::mutex> lock(lockSearchPuts_);
pk.pack("puts");
pk.pack(puts_);
}
#ifdef OPENDHT_PUSH_NOTIFICATIONS
{
std::lock_guard<std::mutex> lock(lockListener_);
pk.pack("pushListeners");
pk.pack(pushListeners_);
}
#endif
}
template <typename Is>
void
DhtProxyServer::loadState(Is& is, size_t size) {
msgpack::unpacker pac;
pac.reserve_buffer(size);
if (is.read(pac.buffer(), size)) {
pac.buffer_consumed(size);
msgpack::object_handle oh;
while (pac.next(oh)) {
if (oh.get().type != msgpack::type::MAP)
continue;
if (auto puts = findMapValue(oh.get(), "puts")) {
std::lock_guard<std::mutex> lock(lockSearchPuts_);
puts_ = puts->as<decltype(puts_)>();
if (logger_)
logger_->d("Loading %zu persistent puts", puts_.size());
for (auto& put : puts_) {
for (auto& pput : put.second.puts) {
pput.second.expireTimer = std::make_unique<asio::steady_timer>(io_context(), pput.second.expiration);
pput.second.expireTimer->async_wait(std::bind(&DhtProxyServer::handleCancelPermamentPut, this,
std::placeholders::_1, put.first, pput.first));
#ifdef OPENDHT_PUSH_NOTIFICATIONS
if (not pput.second.pushToken.empty()) {
auto jsonProvider = [infoHash=put.first.toString(), clientId=pput.second.clientId, vid = pput.first, sessionCtx = pput.second.sessionCtx](){
Json::Value json;
json["timeout"] = infoHash;
json["to"] = clientId;
json["vid"] = std::to_string(vid);
if (sessionCtx) {
std::lock_guard<std::mutex> l(sessionCtx->lock);
json["s"] = sessionCtx->sessionId;
}
return json;
};
pput.second.expireNotifyTimer = std::make_unique<asio::steady_timer>(io_context(), pput.second.expiration - proxy::OP_MARGIN);
pput.second.expireNotifyTimer->async_wait(std::bind(
&DhtProxyServer::handleNotifyPushListenExpire, this,
std::placeholders::_1, pput.second.pushToken, std::move(jsonProvider), pput.second.type));
}
#endif
dht_->put(put.first, pput.second.value, DoneCallbackSimple{}, time_point::max(), true);
}
}
} else {
if (logger_)
logger_->d("No persistent puts in state");
}
#ifdef OPENDHT_PUSH_NOTIFICATIONS
if (auto listeners = findMapValue(oh.get(), "pushListeners")) {
std::lock_guard<std::mutex> lock(lockListener_);
pushListeners_ = listeners->as<decltype(pushListeners_)>();
if (logger_)
logger_->d("Loading %zu push listeners", pushListeners_.size());
for (auto& pushListener : pushListeners_) {
for (auto& listeners : pushListener.second.listeners) {
for (auto& listener : listeners.second) {
listener.internalToken = dht_->listen(listeners.first,
[this, infoHash=listeners.first, pushToken=pushListener.first, type=listener.type, clientId=listener.clientId, sessionCtx = listener.sessionCtx]
(const std::vector<std::shared_ptr<Value>>& values, bool expired) {
// Build message content
Json::Value json;
json["key"] = infoHash.toString();
json["to"] = clientId;
json["t"] = Json::Value::Int64(std::chrono::duration_cast<std::chrono::milliseconds>(system_clock::now().time_since_epoch()).count());
{
std::lock_guard<std::mutex> l(sessionCtx->lock);
json["s"] = sessionCtx->sessionId;
}
if (expired and values.size() < 2){
std::stringstream ss;
for(size_t i = 0; i < values.size(); ++i){
if(i != 0) ss << ",";
ss << values[i]->id;
}
json["exp"] = ss.str();
}
auto maxPrio = 1000u;
for (const auto& v : values)
maxPrio = std::min(maxPrio, v->priority);
sendPushNotification(pushToken, std::move(json), type, !expired and maxPrio == 0);
return true;
}
);
// expire notify
listener.expireNotifyTimer = std::make_unique<asio::steady_timer>(io_context(), listener.expiration - proxy::OP_MARGIN);
auto jsonProvider = [infoHash = listeners.first.toString(), clientId = listener.clientId, sessionCtx = listener.sessionCtx](){
Json::Value json;
json["timeout"] = infoHash;
json["to"] = clientId;
std::lock_guard<std::mutex> l(sessionCtx->lock);
json["s"] = sessionCtx->sessionId;
return json;
};
listener.expireNotifyTimer->async_wait(std::bind(&DhtProxyServer::handleNotifyPushListenExpire, this,
std::placeholders::_1, pushListener.first, std::move(jsonProvider), listener.type));
// cancel push listen
listener.expireTimer = std::make_unique<asio::steady_timer>(io_context(), listener.expiration);
listener.expireTimer->async_wait(std::bind(&DhtProxyServer::handleCancelPushListen, this,
std::placeholders::_1, pushListener.first, listeners.first, listener.clientId));
}
}
}
} else {
if (logger_)
logger_->d("No push listeners in state");
}
#endif
}
if (logger_)
logger_->d("loading ended");
}
}
asio::io_context&
DhtProxyServer::io_context() const
{
@@ -482,12 +284,6 @@ DhtProxyServer::io_context() const
DhtProxyServer::~DhtProxyServer()
{
if (not persistPath_.empty()) {
if (logger_)
logger_->d("Saving proxy state to %.*s", (int)persistPath_.size(), persistPath_.c_str());
std::ofstream stateFile(persistPath_, std::ios::binary);
saveState(stateFile);
}
if (dht_) {
std::lock_guard<std::mutex> lock(lockListener_);
for (auto& l : listeners_) {
@@ -563,10 +359,6 @@ DhtProxyServer::updateStats(std::shared_ptr<NodeInfo> info) const
#ifdef OPENDHT_PUSH_NOTIFICATIONS
stats.pushListenersCount = pushListeners_.size();
#endif
stats.totalPermanentPuts = 0;
std::for_each(puts_.begin(), puts_.end(), [&stats](const auto& put) {
stats.totalPermanentPuts += put.second.puts.size();
});
stats.putCount = puts_.size();
stats.listenCount = listeners_.size();
stats.nodeInfo = std::move(info);
@@ -793,11 +585,12 @@ DhtProxyServer::subscribe(restinio::request_handle_t request,
restinio::router::route_params_t params)
{
requestNum_++;
try {
InfoHash infoHash(params["hash"].to_string());
if (!infoHash)
infoHash = InfoHash::get(params["hash"].to_string());
InfoHash infoHash(params["hash"].to_string());
if (!infoHash)
infoHash = InfoHash::get(params["hash"].to_string());
try {
std::string err;
Json::Value r;
auto* char_data = reinterpret_cast<const char*>(request->body().data());
@@ -814,115 +607,117 @@ DhtProxyServer::subscribe(restinio::request_handle_t request,
response.set_body(RESP_MSG_NO_TOKEN);
return response.done();
}
auto type = root["platform"].asString() == "android" ? PushType::Android : PushType::iOS;
auto isAndroid = root["platform"].asString() == "android";
auto clientId = root["client_id"].asString();
auto sessionId = root["session_id"].asString();
if (logger_)
logger_->d("[proxy:server] [subscribe %s] [client %s] [session %s]", infoHash.toString().c_str(), clientId.c_str(), sessionId.c_str());
// ================ Search for existing listener ===================
// start the timer
auto timeout = std::chrono::steady_clock::now() + proxy::OP_TIMEOUT;
std::lock_guard<std::mutex> lock(lockPushListeners_);
// Insert new or return existing push listeners of a token
std::lock_guard<std::mutex> lock(lockPushListeners_);
auto& pushListener = pushListeners_[pushToken];
auto& pushListeners = pushListener.listeners[infoHash];
auto listIt = std::find_if(pushListeners.begin(), pushListeners.end(), [&](const Listener& l) {
return l.clientId == clientId;
});
bool newListener = listIt == pushListeners.end();
if (newListener) {
pushListeners.emplace_back(Listener{});
listIt = std::prev(pushListeners.end());
listIt->clientId = clientId;
listIt->sessionCtx = std::make_shared<PushSessionContext>(sessionId);
} else {
std::lock_guard<std::mutex> l(listIt->sessionCtx->lock);
listIt->sessionCtx->sessionId = sessionId;
for (auto &listener: pushListeners) {
// Found -> Resubscribe
if (listener.clientId == clientId) {
if (logger_)
logger_->d("[proxy:server] [subscribe] found [client %s]", listener.clientId.c_str());
// Reset timers
listener.expireTimer->expires_at(timeout);
listener.expireNotifyTimer->expires_at(timeout - proxy::OP_MARGIN);
{
std::lock_guard<std::mutex> l(listener.sessionCtx->lock);
listener.sessionCtx->sessionId = sessionId;
}
// Send response header
auto response = std::make_shared<ResponseByPartsBuilder>(
initHttpResponse(request->create_response<ResponseByParts>()));
response->flush();
if (!root["refresh"].asBool()) {
// No Refresh
dht_->get(infoHash, [this, response](const Sp<Value>& value){
auto output = Json::writeString(jsonBuilder_, value->toJson()) + "\n";
response->append_chunk(output);
response->flush();
return true;
},
[response] (bool){
response->done();
});
} else {
// Refresh
response->append_chunk("{}\n");
response->done();
}
return restinio::request_handling_status_t::accepted;
}
}
auto& listener = *listIt;
// =========== No existing listener for an infoHash ============
// Add new listener to list of listeners
pushListeners.emplace_back(Listener{});
auto &listener = pushListeners.back();
listener.clientId = clientId;
listener.sessionCtx = std::make_shared<PushSessionContext>();
listener.sessionCtx->sessionId = sessionId;
// Expiration
auto timeout = std::chrono::steady_clock::now() + proxy::OP_TIMEOUT;
listener.expiration = timeout;
listener.type = type;
if (listener.expireNotifyTimer)
listener.expireNotifyTimer->expires_at(timeout - proxy::OP_MARGIN);
else
// Add listen on dht
listener.internalToken = dht_->listen(infoHash,
[this, infoHash, pushToken, isAndroid, clientId, sessionCtx = listener.sessionCtx]
(const std::vector<std::shared_ptr<Value>>& values, bool expired){
// Build message content
Json::Value json;
json["key"] = infoHash.toString();
json["to"] = clientId;
json["t"] = Json::Value::Int64(std::chrono::duration_cast<std::chrono::milliseconds>(system_clock::now().time_since_epoch()).count());
{
std::lock_guard<std::mutex> l(sessionCtx->lock);
json["s"] = sessionCtx->sessionId;
}
if (expired and values.size() < 2){
std::stringstream ss;
for(size_t i = 0; i < values.size(); ++i){
if(i != 0) ss << ",";
ss << values[i]->id;
}
json["exp"] = ss.str();
}
auto maxPrio = 1000u;
for (const auto& v : values)
maxPrio = std::min(maxPrio, v->priority);
sendPushNotification(pushToken, std::move(json), isAndroid, !expired and maxPrio == 0);
return true;
}
);
// expire notify
if (!listener.expireNotifyTimer)
listener.expireNotifyTimer = std::make_unique<asio::steady_timer>(io_context(), timeout - proxy::OP_MARGIN);
auto jsonProvider = [h=infoHash.toString(), clientId, sessionCtx = listener.sessionCtx](){
else
listener.expireNotifyTimer->expires_at(timeout - proxy::OP_MARGIN);
auto jsonProvider = [infoHash, clientId, sessionCtx = listener.sessionCtx](){
Json::Value json;
json["timeout"] = h;
json["timeout"] = infoHash.toString();
json["to"] = clientId;
std::lock_guard<std::mutex> l(sessionCtx->lock);
json["s"] = sessionCtx->sessionId;
return json;
};
listener.expireNotifyTimer->async_wait(std::bind(&DhtProxyServer::handleNotifyPushListenExpire, this,
std::placeholders::_1, pushToken, std::move(jsonProvider), listener.type));
std::placeholders::_1, pushToken, std::move(jsonProvider), isAndroid));
// cancel push listen
if (!listener.expireTimer)
listener.expireTimer = std::make_unique<asio::steady_timer>(io_context(), timeout);
else
listener.expireTimer->expires_at(timeout);
listener.expireTimer->async_wait(std::bind(&DhtProxyServer::handleCancelPushListen, this,
std::placeholders::_1, pushToken, infoHash, clientId));
// Send response
if (not newListener) {
if (logger_)
logger_->d("[proxy:server] [subscribe] found [client %s]", listener.clientId.c_str());
// Send response header
auto response = std::make_shared<ResponseByPartsBuilder>(initHttpResponse(request->create_response<ResponseByParts>()));
response->flush();
if (!root["refresh"].asBool()) {
// No Refresh
dht_->get(infoHash, [this, response](const Sp<Value>& value){
auto output = Json::writeString(jsonBuilder_, value->toJson()) + "\n";
response->append_chunk(output);
response->flush();
return true;
},
[response] (bool){
response->done();
});
} else {
// Refresh
response->append_chunk("{}\n");
return response->done();
}
} else {
// =========== No existing listener for an infoHash ============
// Add listen on dht
listener.internalToken = dht_->listen(infoHash,
[this, infoHash, pushToken, type, clientId, sessionCtx = listener.sessionCtx]
(const std::vector<std::shared_ptr<Value>>& values, bool expired){
// Build message content
Json::Value json;
json["key"] = infoHash.toString();
json["to"] = clientId;
json["t"] = Json::Value::Int64(std::chrono::duration_cast<std::chrono::milliseconds>(system_clock::now().time_since_epoch()).count());
{
std::lock_guard<std::mutex> l(sessionCtx->lock);
json["s"] = sessionCtx->sessionId;
}
if (expired and values.size() < 2){
std::stringstream ss;
for(size_t i = 0; i < values.size(); ++i){
if(i != 0) ss << ",";
ss << values[i]->id;
}
json["exp"] = ss.str();
}
auto maxPrio = 1000u;
for (const auto& v : values)
maxPrio = std::min(maxPrio, v->priority);
sendPushNotification(pushToken, std::move(json), type, !expired and maxPrio == 0);
return true;
}
);
auto response = initHttpResponse(request->create_response());
response.set_body("{}\n");
return response.done();
}
std::placeholders::_1, pushToken, infoHash, clientId));
auto response = initHttpResponse(request->create_response());
response.set_body("{}\n");
return response.done();
}
catch (...) {
return serverError(*request);
@@ -971,7 +766,7 @@ DhtProxyServer::unsubscribe(restinio::request_handle_t request,
void
DhtProxyServer::handleNotifyPushListenExpire(const asio::error_code &ec, const std::string pushToken,
std::function<Json::Value()> jsonProvider, PushType type)
std::function<Json::Value()> jsonProvider, const bool isAndroid)
{
if (ec == asio::error::operation_aborted)
return;
@@ -981,7 +776,7 @@ DhtProxyServer::handleNotifyPushListenExpire(const asio::error_code &ec, const s
}
if (logger_)
logger_->d("[proxy:server] [subscribe] sending put refresh to %s token", pushToken.c_str());
sendPushNotification(pushToken, jsonProvider(), type, false);
sendPushNotification(pushToken, jsonProvider(), isAndroid, false);
}
void
@@ -1023,7 +818,7 @@ DhtProxyServer::handleCancelPushListen(const asio::error_code &ec, const std::st
}
void
DhtProxyServer::sendPushNotification(const std::string& token, Json::Value&& json, PushType type, bool highPriority)
DhtProxyServer::sendPushNotification(const std::string& token, Json::Value&& json, bool isAndroid, bool highPriority)
{
if (pushServer_.empty())
return;
@@ -1045,7 +840,7 @@ DhtProxyServer::sendPushNotification(const std::string& token, Json::Value&& jso
Json::Value tokens(Json::arrayValue);
tokens[0] = token;
notification["tokens"] = std::move(tokens);
notification["platform"] = type == PushType::Android ? 2 : 1;
notification["platform"] = isAndroid ? 2 : 1;
notification["data"] = std::move(json);
notification["priority"] = highPriority ? "high" : "normal";
notification["time_to_live"] = 600;
@@ -1094,7 +889,6 @@ DhtProxyServer::handleCancelPermamentPut(const asio::error_code &ec, const InfoH
}
if (logger_)
logger_->d("[proxy:server] [put %s] cancel permament put %i", key.toString().c_str(), vid);
std::lock_guard<std::mutex> lock(lockSearchPuts_);
auto sPuts = puts_.find(key);
if (sPuts == puts_.end())
return;
@@ -1104,8 +898,6 @@ DhtProxyServer::handleCancelPermamentPut(const asio::error_code &ec, const InfoH
return;
if (dht_)
dht_->cancelPut(key, vid);
if (put->second.expireTimer)
put->second.expireTimer->cancel();
if (put->second.expireNotifyTimer)
put->second.expireNotifyTimer->cancel();
sPutsMap.erase(put);
@@ -1149,13 +941,13 @@ DhtProxyServer::put(restinio::request_handle_t request,
platform = pVal["platform"].asString();
sessionId = pVal["session_id"].asString();
}
std::lock_guard<std::mutex> lock(lockSearchPuts_);
std::unique_lock<std::mutex> lock(lockSearchPuts_);
auto timeout = std::chrono::steady_clock::now() + proxy::OP_TIMEOUT;
auto& sPuts = puts_[infoHash];
if (value->id == Value::INVALID_ID) {
for (auto& pp : sPuts.puts) {
if (pp.second.pushToken == pushToken
and pp.second.clientId == clientId
if (pp.second.pushToken == pushToken
and pp.second.clientId == clientId
and pp.second.value->contentEquals(*value))
{
pp.second.expireTimer->expires_at(timeout);
@@ -1163,36 +955,32 @@ DhtProxyServer::put(restinio::request_handle_t request,
std::placeholders::_1, infoHash, pp.second.value->id));
if (not sessionId.empty()) {
if (not pp.second.sessionCtx)
pp.second.sessionCtx = std::make_shared<PushSessionContext>(sessionId);
else {
std::lock_guard<std::mutex> l(pp.second.sessionCtx->lock);
pp.second.sessionCtx->sessionId = sessionId;
}
pp.second.sessionCtx = std::make_shared<PushSessionContext>();
std::lock_guard<std::mutex> l(pp.second.sessionCtx->lock);
pp.second.sessionCtx->sessionId = sessionId;
}
auto response = initHttpResponse(request->create_response());
response.append_body(Json::writeString(jsonBuilder_, value->toJson()) + "\n");
return response.done();
}
}
value->id = std::uniform_int_distribution<Value::Id>{1}(rd);
}
auto vid = value->id;
auto& pput = sPuts.puts[vid];
pput.value = value;
pput.expiration = timeout;
if (not pput.expireTimer) {
auto &ctx = io_context();
// cancel permanent put
pput.expireTimer = std::make_unique<asio::steady_timer>(ctx, timeout);
#ifdef OPENDHT_PUSH_NOTIFICATIONS
if (not pushToken.empty()){
bool isAndroid = platform == "android";
pput.pushToken = pushToken;
pput.clientId = clientId;
pput.type = isAndroid ? PushType::Android : PushType::iOS;
pput.sessionCtx = std::make_shared<PushSessionContext>(sessionId);
pput.sessionCtx = std::make_shared<PushSessionContext>();
pput.sessionCtx->sessionId = sessionId;
// notify push listen expire
bool isAndroid = platform == "android";
auto jsonProvider = [infoHash, clientId, vid, sessionCtx = pput.sessionCtx](){
Json::Value json;
json["timeout"] = infoHash.toString();
@@ -1209,17 +997,15 @@ DhtProxyServer::put(restinio::request_handle_t request,
pput.expireNotifyTimer->expires_at(timeout - proxy::OP_MARGIN);
pput.expireNotifyTimer->async_wait(std::bind(
&DhtProxyServer::handleNotifyPushListenExpire, this,
std::placeholders::_1, pushToken, std::move(jsonProvider), pput.type));
std::placeholders::_1, pushToken, std::move(jsonProvider), isAndroid));
}
#endif
} else {
if (not sessionId.empty()) {
if (not pput.sessionCtx)
pput.sessionCtx = std::make_shared<PushSessionContext>(sessionId);
else {
std::lock_guard<std::mutex> l(pput.sessionCtx->lock);
pput.sessionCtx->sessionId = sessionId;
}
pput.sessionCtx = std::make_shared<PushSessionContext>();
std::lock_guard<std::mutex> l(pput.sessionCtx->lock);
pput.sessionCtx->sessionId = sessionId;
}
pput.expireTimer->expires_at(timeout);
if (pput.expireNotifyTimer)
+93 -62
View File
@@ -32,8 +32,6 @@
#include "dht_proxy_client.h"
#endif
#include <fstream>
namespace dht {
constexpr std::chrono::seconds DhtRunner::BOOTSTRAP_PERIOD;
@@ -92,46 +90,16 @@ DhtRunner::run(const char* ip4, const char* ip6, const char* service, const Conf
{
auto res4 = SockAddr::resolve(ip4, service);
auto res6 = SockAddr::resolve(ip6, service);
if (res4.empty())
res4.emplace_back();
if (res6.empty())
res6.emplace_back();
run(res4.front(), res6.front(), config, std::move(context));
run(res4.empty() ? SockAddr() : res4.front(),
res6.empty() ? SockAddr() : res6.front(), config, std::move(context));
}
void
DhtRunner::run(SockAddr& local4, SockAddr& local6, const Config& config, Context&& context)
DhtRunner::run(const SockAddr& local4, const SockAddr& local6, const Config& config, Context&& context)
{
if (running == State::Idle) {
auto state_path = config.dht_config.node_config.persist_path;
if (not state_path.empty()) {
state_path += "_port.txt";
std::ifstream inConfig(state_path);
if (inConfig.is_open()) {
in_port_t port;
if (inConfig >> port) {
if (context.logger)
context.logger->d("[runner %p] Using IPv4 port %hu from saved configuration", this, port);
if (local4.getPort() == 0)
local4.setPort(port);
}
if (inConfig >> port) {
if (context.logger)
context.logger->d("[runner %p] Using IPv6 port %hu from saved configuration", this, port);
if (local6.getPort() == 0)
local6.setPort(port);
}
}
}
if (not context.sock)
context.sock.reset(new net::UdpSocket(local4, local6, context.logger));
if (not state_path.empty()) {
std::ofstream outConfig(state_path);
outConfig << context.sock->getBoundRef(AF_INET).getPort() << std::endl;
outConfig << context.sock->getBoundRef(AF_INET6).getPort() << std::endl;
}
run(config, std::move(context));
}
}
@@ -206,7 +174,7 @@ DhtRunner::run(const Config& config, Context&& context)
if (not pending_ops_prio.empty())
return true;
auto s = getStatus();
if (not pending_ops.empty() and (s == NodeStatus::Connected or s == NodeStatus::Disconnected))
if (not pending_ops.empty() and (s == NodeStatus::Connected or (s == NodeStatus::Disconnected and not bootstraping)))
return true;
}
return false;
@@ -332,6 +300,7 @@ DhtRunner::join()
if (running.exchange(State::Idle) == State::Idle)
return;
cv.notify_all();
bootstrap_cv.notify_all();
#ifdef OPENDHT_PEER_DISCOVERY
if (peerDiscovery_)
peerDiscovery_->stop();
@@ -346,6 +315,9 @@ DhtRunner::join()
if (dht_thread.joinable())
dht_thread.join();
if (bootstrap_thread.joinable())
bootstrap_thread.join();
{
std::lock_guard<std::mutex> lck(storage_mtx);
pending_ops = decltype(pending_ops)();
@@ -507,10 +479,8 @@ DhtRunner::getNodeInfo() const {
info.node_id = dht->getNodeId();
info.ipv4 = dht->getNodesStats(AF_INET);
info.ipv6 = dht->getNodesStats(AF_INET6);
if (auto sock = dht->getSocket()) {
info.bound4 = sock->getBoundRef(AF_INET).getPort();
info.bound6 = sock->getBoundRef(AF_INET6).getPort();
}
info.metrics = dht->getNodeMetrics();
info.ongoing_ops = ongoing_ops;
}
info.ongoing_ops = ongoing_ops;
return info;
@@ -528,10 +498,7 @@ DhtRunner::getNodeInfo(std::function<void(std::shared_ptr<NodeInfo>)> cb)
info.node_id = dht.getNodeId();
info.ipv4 = dht.getNodesStats(AF_INET);
info.ipv6 = dht.getNodesStats(AF_INET6);
if (auto sock = dht.getSocket()) {
info.bound4 = sock->getBoundRef(AF_INET).getPort();
info.bound6 = sock->getBoundRef(AF_INET6).getPort();
}
info.metrics = dht.getNodeMetrics();
info.ongoing_ops = ongoing_ops;
cb(std::move(sinfo));
opEnded();
@@ -620,7 +587,7 @@ DhtRunner::loop_()
{
std::lock_guard<std::mutex> lck(storage_mtx);
auto s = getStatus();
ops = (pending_ops_prio.empty() && (s == NodeStatus::Connected or s == NodeStatus::Disconnected)) ?
ops = (pending_ops_prio.empty() && (s == NodeStatus::Connected or (s == NodeStatus::Disconnected and not bootstraping))) ?
std::move(pending_ops) : std::move(pending_ops_prio);
}
while (not ops.empty()) {
@@ -675,11 +642,20 @@ DhtRunner::loop_()
if (dropped)
std::cerr << "Dropped " << dropped << " packets with high delay" << std::endl;
NodeStatus nstatus4 = dht->updateStatus(AF_INET);
NodeStatus nstatus6 = dht->updateStatus(AF_INET6);
NodeStatus nstatus4 = dht->getStatus(AF_INET);
NodeStatus nstatus6 = dht->getStatus(AF_INET6);
if (nstatus4 != status4 || nstatus6 != status6) {
status4 = nstatus4;
status6 = nstatus6;
if (status4 == NodeStatus::Disconnected and status6 == NodeStatus::Disconnected) {
// We have lost connection with the DHT. Try to recover using bootstrap nodes.
std::unique_lock<std::mutex> lck(bootstrap_mtx);
bootstrap_nodes = bootstrap_nodes_all;
tryBootstrapContinuously();
} else {
std::unique_lock<std::mutex> lck(bootstrap_mtx);
bootstrap_nodes.clear();
}
if (statusCb)
statusCb(status4, status6);
}
@@ -926,34 +902,89 @@ DhtRunner::putEncrypted(const std::string& key, InfoHash to, Value&& value, Done
putEncrypted(InfoHash::get(key), to, std::forward<Value>(value), std::move(cb), permanent);
}
void
DhtRunner::tryBootstrapContinuously()
{
if (bootstrap_thread.joinable()) {
if (bootstraping)
return; // already running
else
bootstrap_thread.join();
}
bootstraping = true;
bootstrap_thread = std::thread([this]() {
auto next = clock::now();
do {
decltype(bootstrap_nodes) nodes;
{
std::lock_guard<std::mutex> lck(bootstrap_mtx);
nodes = bootstrap_nodes;
}
next += BOOTSTRAP_PERIOD;
{
std::mutex mtx;
std::unique_lock<std::mutex> blck(mtx);
unsigned ping_count(0);
// Reverse: try last inserted bootstrap nodes first
for (auto it = nodes.rbegin(); it != nodes.rend(); it++) {
++ping_count;
try {
bootstrap(SockAddr::resolve(it->first, it->second), [&](bool) {
if (running != State::Running)
return;
{
std::unique_lock<std::mutex> blck(mtx);
--ping_count;
}
bootstrap_cv.notify_all();
});
} catch (std::invalid_argument& e) {
--ping_count;
std::cerr << e.what() << std::endl;
}
}
// wait at least until the next BOOTSTRAP_PERIOD
bootstrap_cv.wait_until(blck, next, [&]() { return running != State::Running; });
// wait for bootstrap requests to end.
if (running != State::Running)
bootstrap_cv.wait(blck, [&]() { return running != State::Running or ping_count == 0; });
}
// update state
{
std::lock_guard<std::mutex> lck(dht_mtx);
bootstraping = running == State::Running and
status4 == NodeStatus::Disconnected and
status6 == NodeStatus::Disconnected;
}
} while (bootstraping);
});
}
void
DhtRunner::bootstrap(const std::string& host, const std::string& service)
{
std::lock_guard<std::mutex> lck(storage_mtx);
pending_ops_prio.emplace([host, service] (SecureDht& dht) mutable {
dht.addBootstrap(host, service);
});
cv.notify_all();
std::lock_guard<std::mutex> lck(bootstrap_mtx);
bootstrap_nodes_all.emplace_back(host, service);
bootstrap_nodes.emplace_back(host, service);
tryBootstrapContinuously();
}
void
DhtRunner::bootstrap(const std::string& hostService)
{
std::lock_guard<std::mutex> lck(storage_mtx);
pending_ops_prio.emplace([host_service = splitPort(hostService)] (SecureDht& dht) mutable {
dht.addBootstrap(host_service.first, host_service.second);
});
cv.notify_all();
std::lock_guard<std::mutex> lck(bootstrap_mtx);
auto host_service = splitPort(hostService);
bootstrap_nodes_all.emplace_back(host_service.first, host_service.second);
bootstrap_nodes.emplace_back(std::move(host_service.first), std::move(host_service.second));
tryBootstrapContinuously();
}
void
DhtRunner::clearBootstrap()
{
std::lock_guard<std::mutex> lck(storage_mtx);
pending_ops_prio.emplace([] (SecureDht& dht) mutable {
dht.clearBootstrap();
});
cv.notify_all();
std::lock_guard<std::mutex> lck(bootstrap_mtx);
bootstrap_nodes_all.clear();
}
void
+1 -2
View File
@@ -30,9 +30,8 @@ namespace dht {
struct Listener {
time_point time;
Query query;
int version;
Listener(time_point t, Query&& q, int version = 0) : time(t), query(std::move(q)), version(version) {}
Listener(time_point t, Query&& q) : time(t), query(std::move(q)) {}
void refresh(time_point t, Query&& q) {
time = t;
+1 -2
View File
@@ -31,8 +31,7 @@ enum class MessageType {
Refresh,
Listen,
ValueData,
ValueUpdate,
UpdateValue
ValueUpdate
};
} /* namespace net */
+125 -181
View File
@@ -48,6 +48,23 @@ static constexpr uint8_t v4prefix[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xF
constexpr unsigned SEND_NODES {8};
/* Transaction-ids are 4-bytes long, with the first two bytes identifying
* the kind of request, and the remaining two a sequence number in
* host order.
*/
struct TransId final : public std::array<uint8_t, 4> {
TransId() { std::fill(begin(), end(), 0); }
TransId(const std::array<char, 4>& o) { std::copy(o.begin(), o.end(), begin()); }
TransId(uint32_t id) {
*reinterpret_cast<uint32_t*>(data()) = htonl(id);
}
uint32_t toInt() const {
return ntohl(*reinterpret_cast<const uint32_t*>(&(*this)[0]));
}
};
struct NetworkEngine::PartialMessage {
SockAddr from;
time_point start;
@@ -123,15 +140,11 @@ void
NetworkEngine::tellListener(Sp<Node> node, Tid socket_id, const InfoHash& hash, want_t want,
const Blob& ntoken, std::vector<Sp<Node>>&& nodes,
std::vector<Sp<Node>>&& nodes6, std::vector<Sp<Value>>&& values,
const Query& query, int version)
const Query& query)
{
auto nnodes = bufferNodes(node->getFamily(), hash, want, nodes, nodes6);
try {
if (version >= 1) {
sendUpdateValues(node, hash, values, scheduler.time(), ntoken, socket_id);
} else {
sendNodesValues(node->getAddr(), socket_id, nnodes.first, nnodes.second, values, query, ntoken);
}
sendNodesValues(node->getAddr(), socket_id, nnodes.first, nnodes.second, values, query, ntoken);
} catch (const std::overflow_error& e) {
if (logger_)
logger_->e("Can't send value: buffer not large enough !");
@@ -139,103 +152,63 @@ NetworkEngine::tellListener(Sp<Node> node, Tid socket_id, const InfoHash& hash,
}
void
NetworkEngine::tellListenerRefreshed(Sp<Node> n, Tid socket_id, const InfoHash&, const Blob& token, const std::vector<Value::Id>& values, int version)
NetworkEngine::tellListenerRefreshed(Sp<Node> n, Tid socket_id, const InfoHash&, const Blob& token, const std::vector<Value::Id>& values)
{
msgpack::sbuffer buffer;
msgpack::packer<msgpack::sbuffer> pk(&buffer);
pk.pack_map(4+(config.network?1:0));
pk.pack_map(4 + (version >= 1 ? 1 : 0) + (config.network?1:0));
pk.pack(KEY_U);
pk.pack_map(1 + (not values.empty()?1:0) + (not token.empty()?1:0));
pk.pack(KEY_REQ_ID); pk.pack(myid);
if (not token.empty()) {
pk.pack(KEY_REQ_TOKEN); packToken(pk, token);
}
if (not values.empty()) {
pk.pack(KEY_REQ_REFRESHED);
pk.pack(values);
if (logger_)
logger_->d(n->id, "[node %s] sending %zu refreshed values", n->toString().c_str(), values.size());
}
pk.pack(version >= 1 ? KEY_A : KEY_U);
pk.pack_map(1 + (version >= 1 ? 1 : 0) + (not values.empty()?1:0) + (not token.empty()?1:0));
pk.pack(KEY_REQ_ID); pk.pack(myid);
if (version >= 1) {
pk.pack(KEY_REQ_SID); pk.pack(socket_id);
}
if (not token.empty()) {
pk.pack(KEY_REQ_TOKEN); packToken(pk, token);
}
if (not values.empty()) {
pk.pack(KEY_REQ_REFRESHED);
pk.pack(values);
if (logger_)
logger_->d(n->id, "[node %s] sending %zu refreshed values", n->toString().c_str(), values.size());
}
pk.pack(KEY_Y); pk.pack(version >= 1 ? KEY_Q : KEY_R);
pk.pack(KEY_TID); pk.pack(socket_id);
pk.pack(KEY_Y); pk.pack(KEY_R);
pk.pack(KEY_UA); pk.pack(my_v);
if (config.network) {
pk.pack(KEY_NETID); pk.pack(config.network);
}
if (version >= 1) {
Tid tid (n->getNewTid());
pk.pack(KEY_Q); pk.pack(QUERY_UPDATE);
pk.pack(KEY_TID); pk.pack(tid);
auto req = std::make_shared<Request>(MessageType::UpdateValue, tid, n,
Blob(buffer.data(), buffer.data() + buffer.size()),
[=](const Request&, ParsedMessage&&) { /* on done */ },
[=](const Request&, bool) { /* on expired */ }
);
sendRequest(req);
++out_stats.updateValue;
return;
}
pk.pack(KEY_TID); pk.pack(socket_id);
// send response
send(n->getAddr(), buffer.data(), buffer.size());
}
void
NetworkEngine::tellListenerExpired(Sp<Node> n, Tid socket_id, const InfoHash&, const Blob& token, const std::vector<Value::Id>& values, int version)
NetworkEngine::tellListenerExpired(Sp<Node> n, Tid socket_id, const InfoHash&, const Blob& token, const std::vector<Value::Id>& values)
{
msgpack::sbuffer buffer;
msgpack::packer<msgpack::sbuffer> pk(&buffer);
pk.pack_map(4+(config.network?1:0));
pk.pack_map(4 + (version >= 1 ? 1 : 0) + (config.network?1:0));
pk.pack(KEY_U);
pk.pack_map(1 + (not values.empty()?1:0) + (not token.empty()?1:0));
pk.pack(KEY_REQ_ID); pk.pack(myid);
if (not token.empty()) {
pk.pack(KEY_REQ_TOKEN); packToken(pk, token);
}
if (not values.empty()) {
pk.pack(KEY_REQ_EXPIRED);
pk.pack(values);
if (logger_)
logger_->d(n->id, "[node %s] sending %zu expired values", n->toString().c_str(), values.size());
}
pk.pack(version >= 1 ? KEY_A : KEY_U);
pk.pack_map(1 + (version >= 1 ? 1 : 0) + (not values.empty()?1:0) + (not token.empty()?1:0));
pk.pack(KEY_REQ_ID); pk.pack(myid);
if (version >= 1) {
pk.pack(KEY_REQ_SID); pk.pack(socket_id);
}
if (not token.empty()) {
pk.pack(KEY_REQ_TOKEN); packToken(pk, token);
}
if (not values.empty()) {
pk.pack(KEY_REQ_EXPIRED);
pk.pack(values);
if (logger_)
logger_->d(n->id, "[node %s] sending %zu expired values", n->toString().c_str(), values.size());
}
pk.pack(KEY_Y); pk.pack(version >= 1 ? KEY_Q : KEY_R);
pk.pack(KEY_TID); pk.pack(socket_id);
pk.pack(KEY_Y); pk.pack(KEY_R);
pk.pack(KEY_UA); pk.pack(my_v);
if (config.network) {
pk.pack(KEY_NETID); pk.pack(config.network);
}
if (version >= 1) {
Tid tid (n->getNewTid());
pk.pack(KEY_Q); pk.pack(QUERY_UPDATE);
pk.pack(KEY_TID); pk.pack(tid);
auto req = std::make_shared<Request>(MessageType::UpdateValue, tid, n,
Blob(buffer.data(), buffer.data() + buffer.size()),
[=](const Request&, ParsedMessage&&) { /* on done */ },
[=](const Request&, bool) { /* on expired */ }
);
sendRequest(req);
++out_stats.updateValue;
return;
}
pk.pack(KEY_TID); pk.pack(socket_id);
// send response
send(n->getAddr(), buffer.data(), buffer.size());
}
@@ -308,9 +281,6 @@ NetworkEngine::requestStep(Sp<Request> sreq)
++req.attempt_count;
req.attempt_duration +=
req.attempt_duration + uniform_duration_distribution<>(0ms, ((duration)Node::MAX_RESPONSE_TIME)/4)(rd);
if (not req.parts.empty()){
sendValueParts(req.tid, req.parts, node.getAddr());
}
}
std::weak_ptr<Request> wreq = sreq;
scheduler.add(req.last_try + req.attempt_duration, [this,wreq] {
@@ -560,8 +530,7 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
{
req->last_try = time_point::min();
req->reply_time = time_point::min();
if (not req->setError(DhtProtocolException {msg->error_code}))
onError(req, DhtProtocolException {msg->error_code});
onError(req, DhtProtocolException {msg->error_code});
} else {
if (logIncoming_)
if (logger_)
@@ -573,11 +542,8 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
case MessageType::Reply:
if (req) { /* request reply */
auto& r = *req;
if (r.getType() == MessageType::AnnounceValue
or r.getType() == MessageType::Listen
or r.getType() == MessageType::Refresh) {
if (r.getType() == MessageType::AnnounceValue or r.getType() == MessageType::Listen)
r.node->authSuccess();
}
r.reply_time = scheduler.time();
deserializeNodes(*msg, from);
@@ -648,29 +614,18 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
if (logIncoming_ and logger_)
logger_->d(msg->info_hash, node->id, "[node %s] got 'listen' request for %s", node->toString().c_str(), msg->info_hash.toString().c_str());
++in_stats.listen;
RequestAnswer answer = onListen(node, msg->info_hash, msg->token, msg->socket_id, std::move(msg->query), msg->version);
RequestAnswer answer = onListen(node, msg->info_hash, msg->token, msg->socket_id, std::move(msg->query));
auto nnodes = bufferNodes(from.getFamily(), msg->info_hash, msg->want, answer.nodes4, answer.nodes6);
sendListenConfirmation(from, msg->tid);
break;
}
case MessageType::UpdateValue: {
if (logIncoming_ and logger_)
logger_->d(msg->info_hash, node->id, "[node %s] got 'update' request for %s", node->toString().c_str(), msg->info_hash.toString().c_str());
++in_stats.updateValue;
if (auto rsocket = node->getSocket(msg->socket_id))
rsocket->on_receive(node, std::move(*msg));
else if (logger_)
logger_->e(msg->info_hash, node->id, "[node %s] 'update' request without socket for %s", node->toString().c_str(), msg->info_hash.toString().c_str());
sendListenConfirmation(from, msg->tid);
break;
}
default:
break;
}
} catch (const std::overflow_error& e) {
if (logger_)
logger_->e("Can't send value: buffer not large enough !");
} catch (const DhtProtocolException& e) {
} catch (DhtProtocolException& e) {
sendError(from, msg->tid, e.getCode(), e.getMsg().c_str(), true);
}
}
@@ -696,7 +651,7 @@ NetworkEngine::send(const SockAddr& addr, const char *buf, size_t len, bool conf
Sp<Request>
NetworkEngine::sendPing(Sp<Node> node, RequestCb&& on_done, RequestExpiredCb&& on_expired) {
Tid tid (node->getNewTid());
TransId tid (node->getNewTid());
msgpack::sbuffer buffer;
msgpack::packer<msgpack::sbuffer> pk(&buffer);
pk.pack_map(5+(config.network?1:0));
@@ -705,14 +660,15 @@ NetworkEngine::sendPing(Sp<Node> node, RequestCb&& on_done, RequestExpiredCb&& o
pk.pack(KEY_REQ_ID); pk.pack(myid);
pk.pack(KEY_Q); pk.pack(QUERY_PING);
pk.pack(KEY_TID); pk.pack(tid);
pk.pack(KEY_TID); pk.pack_bin(tid.size());
pk.pack_bin_body((const char*)tid.data(), tid.size());
pk.pack(KEY_Y); pk.pack(KEY_Q);
pk.pack(KEY_UA); pk.pack(my_v);
if (config.network) {
pk.pack(KEY_NETID); pk.pack(config.network);
}
auto req = std::make_shared<Request>(MessageType::Ping, tid, node,
auto req = std::make_shared<Request>(MessageType::Ping, tid.toInt(), node,
Blob(buffer.data(), buffer.data() + buffer.size()),
[=](const Request& req_status, ParsedMessage&&) {
if (logger_)
@@ -742,7 +698,9 @@ NetworkEngine::sendPong(const SockAddr& addr, Tid tid) {
pk.pack(KEY_REQ_ID); pk.pack(myid);
insertAddr(pk, addr);
pk.pack(KEY_TID); pk.pack(tid);
TransId t (tid);
pk.pack(KEY_TID); pk.pack_bin(t.size());
pk.pack_bin_body((const char*)t.data(), t.size());
pk.pack(KEY_Y); pk.pack(KEY_R);
pk.pack(KEY_UA); pk.pack(my_v);
if (config.network) {
@@ -755,7 +713,7 @@ NetworkEngine::sendPong(const SockAddr& addr, Tid tid) {
Sp<Request>
NetworkEngine::sendFindNode(Sp<Node> n, const InfoHash& target, want_t want,
RequestCb&& on_done, RequestExpiredCb&& on_expired) {
Tid tid (n->getNewTid());
TransId tid (n->getNewTid());
msgpack::sbuffer buffer;
msgpack::packer<msgpack::sbuffer> pk(&buffer);
pk.pack_map(5+(config.network?1:0));
@@ -771,14 +729,15 @@ NetworkEngine::sendFindNode(Sp<Node> n, const InfoHash& target, want_t want,
}
pk.pack(KEY_Q); pk.pack(QUERY_FIND);
pk.pack(KEY_TID); pk.pack(tid);
pk.pack(KEY_TID); pk.pack_bin(tid.size());
pk.pack_bin_body((const char*)tid.data(), tid.size());
pk.pack(KEY_Y); pk.pack(KEY_Q);
pk.pack(KEY_UA); pk.pack(my_v);
if (config.network) {
pk.pack(KEY_NETID); pk.pack(config.network);
}
auto req = std::make_shared<Request>(MessageType::FindNode, tid, n,
auto req = std::make_shared<Request>(MessageType::FindNode, tid.toInt(), n,
Blob(buffer.data(), buffer.data() + buffer.size()),
[=](const Request& req_status, ParsedMessage&& msg) { /* on done */
if (on_done) {
@@ -800,7 +759,7 @@ NetworkEngine::sendFindNode(Sp<Node> n, const InfoHash& target, want_t want,
Sp<Request>
NetworkEngine::sendGetValues(Sp<Node> n, const InfoHash& info_hash, const Query& query, want_t want,
RequestCb&& on_done, RequestExpiredCb&& on_expired) {
Tid tid (n->getNewTid());
TransId tid (n->getNewTid());
msgpack::sbuffer buffer;
msgpack::packer<msgpack::sbuffer> pk(&buffer);
pk.pack_map(5+(config.network?1:0));
@@ -824,14 +783,15 @@ NetworkEngine::sendGetValues(Sp<Node> n, const InfoHash& info_hash, const Query&
}
pk.pack(KEY_Q); pk.pack(QUERY_GET);
pk.pack(KEY_TID); pk.pack(tid);
pk.pack(KEY_TID); pk.pack_bin(tid.size());
pk.pack_bin_body((const char*)tid.data(), tid.size());
pk.pack(KEY_Y); pk.pack(KEY_Q);
pk.pack(KEY_UA); pk.pack(my_v);
if (config.network) {
pk.pack(KEY_NETID); pk.pack(config.network);
}
auto req = std::make_shared<Request>(MessageType::GetValues, tid, n,
auto req = std::make_shared<Request>(MessageType::GetValues, tid.toInt(), n,
Blob(buffer.data(), buffer.data() + buffer.size()),
[=](const Request& req_status, ParsedMessage&& msg) { /* on done */
if (on_done) {
@@ -933,7 +893,7 @@ NetworkEngine::packValueHeader(msgpack::sbuffer& buffer, const std::vector<Sp<Va
}
void
NetworkEngine::sendValueParts(Tid tid, const std::vector<Blob>& svals, const SockAddr& addr)
NetworkEngine::sendValueParts(const TransId& tid, const std::vector<Blob>& svals, const SockAddr& addr)
{
msgpack::sbuffer buffer;
unsigned i=0;
@@ -948,7 +908,8 @@ NetworkEngine::sendValueParts(Tid tid, const std::vector<Blob>& svals, const Soc
pk.pack(KEY_NETID); pk.pack(config.network);
}
pk.pack(KEY_Y); pk.pack(KEY_V);
pk.pack(KEY_TID); pk.pack(tid);
pk.pack(KEY_TID); pk.pack_bin(tid.size());
pk.pack_bin_body((const char*)tid.data(), tid.size());
pk.pack(KEY_V); pk.pack_map(1);
pk.pack(i); pk.pack_map(2);
pk.pack(std::string("o")); pk.pack(start);
@@ -1003,7 +964,9 @@ NetworkEngine::sendNodesValues(const SockAddr& addr, Tid tid, const Blob& nodes,
}
}
pk.pack(KEY_TID); pk.pack(tid);
TransId t (tid);
pk.pack(KEY_TID); pk.pack_bin(t.size());
pk.pack_bin_body((const char*)t.data(), t.size());
pk.pack(KEY_Y); pk.pack(KEY_R);
pk.pack(KEY_UA); pk.pack(my_v);
if (config.network) {
@@ -1073,35 +1036,54 @@ NetworkEngine::sendListen(Sp<Node> n,
const InfoHash& hash,
const Query& query,
const Blob& token,
Tid socketId,
Sp<Request> previous,
RequestCb&& on_done,
RequestExpiredCb&& on_expired)
RequestExpiredCb&& on_expired,
SocketCb&& socket_cb)
{
Tid tid (n->getNewTid());
Tid socket;
TransId tid (n->getNewTid());
if (previous and previous->node == n) {
socket = previous->getSocket();
} else {
if (previous)
if (logger_)
logger_->e(hash, "[node %s] trying refresh listen contract with wrong node", previous->node->toString().c_str());
socket = n->openSocket(std::move(socket_cb));
}
if (not socket) {
if (logger_)
logger_->e(hash, "[node %s] unable to get a valid socket for listen. Aborting listen", n->toString().c_str());
return {};
}
TransId sid(socket);
msgpack::sbuffer buffer;
msgpack::packer<msgpack::sbuffer> pk(&buffer);
pk.pack_map(5+(config.network?1:0));
auto has_query = not query.where.empty() or not query.select.empty();
pk.pack(KEY_A); pk.pack_map(5 + has_query);
pk.pack(KEY_A); pk.pack_map(4 + has_query);
pk.pack(KEY_REQ_ID); pk.pack(myid);
pk.pack(KEY_VERSION); pk.pack(1);
pk.pack(KEY_REQ_H); pk.pack(hash);
pk.pack(KEY_REQ_TOKEN); packToken(pk, token);
pk.pack(KEY_REQ_SID); pk.pack(socketId);
pk.pack(KEY_REQ_SID); pk.pack_bin(sid.size());
pk.pack_bin_body((const char*)sid.data(), sid.size());
if (has_query) {
pk.pack(KEY_REQ_QUERY); pk.pack(query);
}
pk.pack(KEY_Q); pk.pack(QUERY_LISTEN);
pk.pack(KEY_TID); pk.pack(tid);
pk.pack(KEY_TID); pk.pack_bin(tid.size());
pk.pack_bin_body((const char*)tid.data(), tid.size());
pk.pack(KEY_Y); pk.pack(KEY_Q);
pk.pack(KEY_UA); pk.pack(my_v);
if (config.network) {
pk.pack(KEY_NETID); pk.pack(config.network);
}
auto req = std::make_shared<Request>(MessageType::Listen, tid, n,
auto req = std::make_shared<Request>(MessageType::Listen, tid.toInt(), n,
Blob(buffer.data(), buffer.data() + buffer.size()),
[=](const Request& req_status, ParsedMessage&& msg) { /* on done */
if (on_done)
@@ -1110,7 +1092,8 @@ NetworkEngine::sendListen(Sp<Node> n,
[=](const Request& req_status, bool done) { /* on expired */
if (on_expired)
on_expired(req_status, done);
}
},
socket
);
sendRequest(req);
++out_stats.listen;
@@ -1127,7 +1110,9 @@ NetworkEngine::sendListenConfirmation(const SockAddr& addr, Tid tid) {
pk.pack(KEY_REQ_ID); pk.pack(myid);
insertAddr(pk, addr);
pk.pack(KEY_TID); pk.pack(tid);
TransId t (tid);
pk.pack(KEY_TID); pk.pack_bin(t.size());
pk.pack_bin_body((const char*)t.data(), t.size());
pk.pack(KEY_Y); pk.pack(KEY_R);
pk.pack(KEY_UA); pk.pack(my_v);
if (config.network) {
@@ -1146,7 +1131,7 @@ NetworkEngine::sendAnnounceValue(Sp<Node> n,
RequestCb&& on_done,
RequestExpiredCb&& on_expired)
{
Tid tid (n->getNewTid());
TransId tid (n->getNewTid());
msgpack::sbuffer buffer;
msgpack::packer<msgpack::sbuffer> pk(&buffer);
pk.pack_map(5+(config.network?1:0));
@@ -1162,14 +1147,15 @@ NetworkEngine::sendAnnounceValue(Sp<Node> n,
pk.pack(KEY_REQ_TOKEN); pk.pack(token);
pk.pack(KEY_Q); pk.pack(QUERY_PUT);
pk.pack(KEY_TID); pk.pack(tid);
pk.pack(KEY_TID); pk.pack_bin(tid.size());
pk.pack_bin_body((const char*)tid.data(), tid.size());
pk.pack(KEY_Y); pk.pack(KEY_Q);
pk.pack(KEY_UA); pk.pack(my_v);
if (config.network) {
pk.pack(KEY_NETID); pk.pack(config.network);
}
auto req = std::make_shared<Request>(MessageType::AnnounceValue, tid, n,
auto req = std::make_shared<Request>(MessageType::AnnounceValue, tid.toInt(), n,
Blob(buffer.data(), buffer.data() + buffer.size()),
[=](const Request& req_status, ParsedMessage&& msg) { /* on done */
if (msg.value_id == Value::INVALID_ID) {
@@ -1189,68 +1175,22 @@ NetworkEngine::sendAnnounceValue(Sp<Node> n,
}
}
);
req->parts = std::move(v);
sendRequest(req);
if (not v.empty())
sendValueParts(tid, v, n->getAddr());
++out_stats.put;
return req;
}
Sp<Request>
NetworkEngine::sendUpdateValues(Sp<Node> n,
const InfoHash& infohash,
const std::vector<Sp<Value>>& values,
time_point created,
const Blob& token,
const size_t& socket_id)
{
Tid tid (n->getNewTid());
Tid sid (socket_id);
msgpack::sbuffer buffer;
msgpack::packer<msgpack::sbuffer> pk(&buffer);
pk.pack_map(5+(config.network?1:0));
pk.pack(KEY_A); pk.pack_map((created < scheduler.time() ? 7 : 6));
pk.pack(KEY_REQ_ID); pk.pack(myid);
pk.pack(KEY_VERSION); pk.pack(1);
pk.pack(KEY_REQ_H); pk.pack(infohash);
pk.pack(KEY_REQ_SID); pk.pack(sid);
auto v = packValueHeader(buffer, values);
if (created < scheduler.time()) {
pk.pack(KEY_REQ_CREATION);
pk.pack(to_time_t(created));
}
pk.pack(KEY_REQ_TOKEN); pk.pack(token);
pk.pack(KEY_Q); pk.pack(QUERY_UPDATE);
pk.pack(KEY_TID); pk.pack(tid);
pk.pack(KEY_Y); pk.pack(KEY_Q);
pk.pack(KEY_UA); pk.pack(my_v);
if (config.network) {
pk.pack(KEY_NETID); pk.pack(config.network);
}
auto req = std::make_shared<Request>(MessageType::UpdateValue, tid, n,
Blob(buffer.data(), buffer.data() + buffer.size()),
[=](const Request&, ParsedMessage&&) { /* on done */ },
[=](const Request&, bool) { /* on expired */ }
);
req->parts = std::move(v);
sendRequest(req);
++out_stats.updateValue;
return req;
}
Sp<Request>
NetworkEngine::sendRefreshValue(Sp<Node> n,
const InfoHash& infohash,
const Value::Id& vid,
const Blob& token,
RequestCb&& on_done,
RequestErrorCb&& on_error,
RequestExpiredCb&& on_expired)
{
Tid tid (n->getNewTid());
TransId tid (n->getNewTid());
msgpack::sbuffer buffer;
msgpack::packer<msgpack::sbuffer> pk(&buffer);
pk.pack_map(5+(config.network?1:0));
@@ -1262,14 +1202,15 @@ NetworkEngine::sendRefreshValue(Sp<Node> n,
pk.pack(KEY_REQ_TOKEN); pk.pack(token);
pk.pack(KEY_Q); pk.pack(QUERY_REFRESH);
pk.pack(KEY_TID); pk.pack(tid);
pk.pack(KEY_TID); pk.pack_bin(tid.size());
pk.pack_bin_body((const char*)tid.data(), tid.size());
pk.pack(KEY_Y); pk.pack(KEY_Q);
pk.pack(KEY_UA); pk.pack(my_v);
if (config.network) {
pk.pack(KEY_NETID); pk.pack(config.network);
}
auto req = std::make_shared<Request>(MessageType::Refresh, tid, n,
auto req = std::make_shared<Request>(MessageType::Refresh, tid.toInt(), n,
Blob(buffer.data(), buffer.data() + buffer.size()),
[=](const Request& req_status, ParsedMessage&& msg) { /* on done */
if (msg.value_id == Value::INVALID_ID) {
@@ -1283,7 +1224,6 @@ NetworkEngine::sendRefreshValue(Sp<Node> n,
}
}
},
on_error,
[=](const Request& req_status, bool done) { /* on expired */
if (on_expired) {
on_expired(req_status, done);
@@ -1306,7 +1246,9 @@ NetworkEngine::sendValueAnnounced(const SockAddr& addr, Tid tid, Value::Id vid)
pk.pack(KEY_REQ_VALUE_ID); pk.pack(vid);
insertAddr(pk, addr);
pk.pack(KEY_TID); pk.pack(tid);
TransId t(tid);
pk.pack(KEY_TID); pk.pack_bin(t.size());
pk.pack_bin_body((const char*)t.data(), t.size());
pk.pack(KEY_Y); pk.pack(KEY_R);
pk.pack(KEY_UA); pk.pack(my_v);
if (config.network) {
@@ -1325,7 +1267,7 @@ NetworkEngine::sendError(const SockAddr& addr,
{
msgpack::sbuffer buffer;
msgpack::packer<msgpack::sbuffer> pk(&buffer);
pk.pack_map(4 + (include_id?1:0) + (config.network?1:0));
pk.pack_map(4 + (include_id?1:0));
pk.pack(KEY_E); pk.pack_array(2);
pk.pack(code);
@@ -1336,7 +1278,9 @@ NetworkEngine::sendError(const SockAddr& addr,
pk.pack(KEY_REQ_ID); pk.pack(myid);
}
pk.pack(KEY_TID); pk.pack(tid);
TransId t(tid);
pk.pack(KEY_TID); pk.pack_bin(t.size());
pk.pack_bin_body((const char*)t.data(), t.size());
pk.pack(KEY_Y); pk.pack(KEY_E);
pk.pack(KEY_UA); pk.pack(my_v);
if (config.network) {
+1
View File
@@ -116,6 +116,7 @@ Node::cancelRequest(const Sp<net::Request>& req)
{
if (req) {
req->cancel();
closeSocket(req->closeSocket());
requests_.erase(req->getTid());
}
}
-7
View File
@@ -54,12 +54,10 @@ static const std::string KEY_REQ_EXPIRED {"exp"};
static const std::string KEY_REQ_REFRESHED {"re"};
static const std::string KEY_REQ_FIELDS {"fileds"};
static const std::string KEY_REQ_WANT {"w"};
static const std::string KEY_VERSION {"ve"};
static const std::string QUERY_PING {"ping"};
static const std::string QUERY_FIND {"find"};
static const std::string QUERY_GET {"get"};
static const std::string QUERY_UPDATE {"update"};
static const std::string QUERY_PUT {"put"};
static const std::string QUERY_LISTEN {"listen"};
static const std::string QUERY_REFRESH {"refresh"};
@@ -115,7 +113,6 @@ struct ParsedMessage {
uint16_t error_code;
/* reported address by the distant node */
std::string ua;
int version {0};
SockAddr addr;
void msgpack_unpack(const msgpack::object& o);
@@ -227,8 +224,6 @@ ParsedMessage::msgpack_unpack(const msgpack::object& msg)
type = MessageType::AnnounceValue;
else if (parsed.q == QUERY_REFRESH)
type = MessageType::Refresh;
else if (parsed.q == QUERY_UPDATE)
type = MessageType::UpdateValue;
else
throw msgpack::type_error();
@@ -300,8 +295,6 @@ ParsedMessage::msgpack_unpack(const msgpack::object& msg)
parsedReq.fields = &o.val;
else if (key == KEY_REQ_WANT)
parsedReq.want = &o.val;
else if (key == KEY_VERSION)
version = o.val.as<int>();
}
if (parsedReq.sa) {
+18 -24
View File
@@ -27,9 +27,10 @@ struct Node;
namespace net {
class NetworkEngine;
class DhtProtocolException;
struct ParsedMessage;
extern std::atomic<size_t> REQ_COUNT;
/*!
* @class Request
* @brief An atomic request destined to a node.
@@ -68,24 +69,29 @@ struct Request {
}
}
Request(State state = State::PENDING) : state_(state) {}
Request(State state = State::PENDING) : state_(state) {
REQ_COUNT++;
}
Request(MessageType type, Tid tid,
Sp<Node> node,
Blob&& msg,
std::function<void(const Request&, ParsedMessage&&)> on_done,
std::function<void(const Request&, bool)> on_expired) :
node(node), tid(tid), type(type), on_done(on_done), on_expired(on_expired), msg(std::move(msg)) { }
Request(MessageType type, Tid tid,
Sp<Node> node,
Blob&& msg,
std::function<void(const Request&, ParsedMessage&&)> on_done,
std::function<bool(const Request&, DhtProtocolException&&)> on_error,
std::function<void(const Request&, bool)> on_expired) :
node(node), tid(tid), type(type), on_done(on_done), on_error(on_error), on_expired(on_expired), msg(std::move(msg)) { }
std::function<void(const Request&, bool)> on_expired,
Tid socket = 0) :
node(node), tid(tid), type(type), on_done(on_done), on_expired(on_expired), msg(std::move(msg)), socket(socket) {
REQ_COUNT++;
}
~Request() {
REQ_COUNT--;
}
Tid getTid() const { return tid; }
MessageType getType() const { return type; }
Tid getSocket() { return socket; }
Tid closeSocket() { auto ret = socket; socket = 0; return ret; }
void setExpired() {
if (pending()) {
state_ = Request::State::EXPIRED;
@@ -100,15 +106,6 @@ struct Request {
clear();
}
}
bool setError(DhtProtocolException&& e) {
if (pending()) {
state_ = Request::State::EXPIRED;
bool handled = on_error and on_error(*this, std::forward<DhtProtocolException>(e));
clear();
return handled;
}
return true;
}
void cancel() {
if (pending()) {
@@ -126,10 +123,8 @@ private:
void clear() {
on_done = {};
on_error = {};
on_expired = {};
msg = {};
parts = {};
}
const Tid tid {0}; /* the request id. */
@@ -142,11 +137,10 @@ private:
time_point last_try {time_point::min()}; /* time of the last attempt to process the request. */
std::function<void(const Request&, ParsedMessage&&)> on_done {};
std::function<bool(const Request&, DhtProtocolException&&)> on_error {};
std::function<void(const Request&, bool)> on_expired {};
Blob msg {}; /* the serialized message. */
std::vector<Blob> parts;
Tid socket; /* the socket used for further reponses. */
};
} /* namespace net */
+6 -51
View File
@@ -65,16 +65,10 @@ struct Dht::SearchNode {
ValueCache cache;
Sp<Scheduler::Job> cacheExpirationJob {};
Sp<net::Request> req {};
Tid socketId {0};
CachedListenStatus(ValueStateCallback&& cb, SyncCallback scb, Tid sid)
: cache(std::forward<ValueStateCallback>(cb), std::forward<SyncCallback>(scb)), socketId(sid) {}
CachedListenStatus(ValueStateCallback&& cb, SyncCallback&& scb)
: cache(std::forward<ValueStateCallback>(cb), std::forward<SyncCallback>(scb)) {}
CachedListenStatus(CachedListenStatus&&) = delete;
CachedListenStatus(const CachedListenStatus&) = delete;
~CachedListenStatus() {
if (socketId and req and req->node) {
req->node->closeSocket(socketId);
}
}
};
using NodeListenerStatus = std::map<Sp<Query>, CachedListenStatus>;
@@ -350,15 +344,13 @@ struct Dht::SearchNode {
* Assuming the node is synced, should the "listen" request with Query q be
* sent to this node now ?
*/
time_point getListenTime(const decltype(listenStatus)::const_iterator listen_status, duration listen_expire) const {
if (listen_status == listenStatus.cend() or not listen_status->second.req)
time_point getListenTime(const Sp<Query>& q, duration listen_expire) const {
auto listen_status = listenStatus.find(q);
if (listen_status == listenStatus.end() or not listen_status->second.req)
return time_point::min();
return listen_status->second.req->pending() ? time_point::max() :
listen_status->second.req->reply_time + listen_expire - REANNOUNCE_MARGIN;
}
time_point getListenTime(const Sp<Query>& q, duration listen_expire) const {
return getListenTime(listenStatus.find(q), listen_expire);
}
/**
* Is this node expired or candidate
@@ -576,43 +568,6 @@ struct Dht::Search {
return canceled;
}
void put(const Sp<Value>& value, DoneCallback callback, time_point created, bool permanent) {
done = false;
expired = false;
auto a_sr = std::find_if(announce.begin(), announce.end(), [&](const Announce& a){
return a.value->id == value->id;
});
if (a_sr == announce.end()) {
announce.emplace_back(Announce {permanent, value, created, callback});
for (auto& n : nodes) {
n->probe_query.reset();
n->acked[value->id].first.reset();
}
} else {
a_sr->permanent = permanent;
a_sr->created = created;
if (a_sr->value != value) {
a_sr->value = value;
for (auto& n : nodes) {
n->acked[value->id].first.reset();
n->probe_query.reset();
}
}
if (isAnnounced(value->id)) {
if (a_sr->callback)
a_sr->callback(true, {});
a_sr->callback = {};
if (callback)
callback(true, {});
return;
} else {
if (a_sr->callback)
a_sr->callback(false, {});
a_sr->callback = callback;
}
}
}
/**
* @return The number of non-good search nodes.
*/
@@ -624,7 +579,7 @@ struct Dht::Search {
unsigned getNumberOfConsecutiveBadNodes() const {
unsigned count = 0;
std::find_if(nodes.begin(), nodes.end(), [&count](const std::unique_ptr<SearchNode>& sn) {
if (not sn->node->isExpired())
if (not sn->isBad())
return true;
++count;
return false;
+21 -3
View File
@@ -34,6 +34,9 @@
#endif
namespace dht {
namespace net {
std::atomic<size_t> REQ_COUNT {0};
}
static constexpr std::array<uint8_t, 12> MAPPED_IPV4_PREFIX {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}};
@@ -76,8 +79,11 @@ SockAddr::resolve(const std::string& host, const std::string& service)
if(rc != 0)
throw std::invalid_argument(std::string("Error: `") + host + ":" + service + "`: " + gai_strerror(rc));
for (addrinfo* infop = info; infop; infop = infop->ai_next)
addrinfo* infop = info;
while (infop) {
ips.emplace_back(infop->ai_addr, infop->ai_addrlen);
infop = infop->ai_next;
}
freeaddrinfo(info);
return ips;
}
@@ -277,16 +283,28 @@ unpackMsg(Blob b) {
}
msgpack::object*
findMapValue(const msgpack::object& map, const char* key, size_t key_length) {
findMapValue(const msgpack::object& map, const char* key) {
if (map.type != msgpack::type::MAP) throw msgpack::type_error();
for (unsigned i = 0; i < map.via.map.size; i++) {
auto& o = map.via.map.ptr[i];
if (o.key.type == msgpack::type::STR
&& key_length == o.key.via.str.size
&& std::strncmp(o.key.via.str.ptr, key, o.key.via.str.size) == 0)
return &o.val;
}
return nullptr;
}
msgpack::object*
findMapValue(const msgpack::object& map, const std::string& key) {
if (map.type != msgpack::type::MAP) throw msgpack::type_error();
for (unsigned i = 0; i < map.via.map.size; i++) {
auto& o = map.via.map.ptr[i];
if (o.key.type == msgpack::type::STR
&& key.size() == o.key.via.str.size
&& std::strncmp(o.key.via.str.ptr, key.data(), o.key.via.str.size) == 0)
return &o.val;
}
return nullptr;
}
}
+1 -3
View File
@@ -75,13 +75,11 @@ std::ostream& operator<< (std::ostream& s, const Value& v)
s << '"';
s.write((const char*)v.data.data(), v.data.size());
s << '"';
} else if (v.data.size() < 1024) {
} else {
s << std::hex;
for (auto i : v.data)
s << std::setfill('0') << std::setw(2) << (unsigned)i;
s << std::dec;
} else {
s << v.data.size() << " bytes";
}
}
}
+10 -11
View File
@@ -34,25 +34,24 @@ CPPUNIT_TEST_SUITE_REGISTRATION(DhtProxyTester);
void
DhtProxyTester::setUp() {
clientConfig.dht_config.node_config.max_peer_req_per_sec = -1;
clientConfig.dht_config.node_config.max_req_per_sec = -1;
nodePeer.run(0, clientConfig);
nodePeer.run(0);
nodeProxy = std::make_shared<dht::DhtRunner>();
nodeProxy->run(0, clientConfig);
nodeProxy->run(0);
nodeProxy->bootstrap(nodePeer.getBound());
auto serverCAIdentity = dht::crypto::generateEcIdentity("DHT Node CA");
auto serverIdentity = dht::crypto::generateIdentity("DHT Node", serverCAIdentity);
dht::ProxyServerConfig serverConfig;
serverConfig.identity = dht::crypto::generateIdentity("DHT Node", serverCAIdentity);
serverConfig.port = 8080;
serverConfig.pushServer = "127.0.0.1:8090";
serverProxy = std::make_unique<dht::DhtProxyServer>(nodeProxy, serverConfig);
serverProxy = std::make_unique<dht::DhtProxyServer>(
//dht::crypto::Identity{}, // http
serverIdentity, // https
nodeProxy, 8080, /*pushServer*/"127.0.0.1:8090");
clientConfig.server_ca = serverCAIdentity.second;
clientConfig.client_identity = dht::crypto::generateIdentity("DhtProxyTester");
clientConfig.dht_config.node_config.maintain_storage = false;
clientConfig.threaded = true;
clientConfig.push_node_id = "dhtnode";
clientConfig.proxy_server = "https://127.0.0.1:8080";
}
@@ -281,7 +280,7 @@ DhtProxyTester::testFuzzy()
},[&](bool ok){
CPPUNIT_ASSERT(ok);
});
std::this_thread::sleep_for(5ms);
std::this_thread::sleep_for(10ms);
}
// Assert
+6 -81
View File
@@ -29,12 +29,8 @@ CPPUNIT_TEST_SUITE_REGISTRATION(DhtRunnerTester);
void
DhtRunnerTester::setUp() {
dht::DhtRunner::Config config;
config.dht_config.node_config.max_peer_req_per_sec = -1;
config.dht_config.node_config.max_req_per_sec = -1;
node1.run(42222, config);
node2.run(42232, config);
node1.run(42222);
node2.run(42232);
node2.bootstrap(node1.getBound());
}
@@ -88,9 +84,7 @@ DhtRunnerTester::testListen() {
auto a = dht::InfoHash::get("234");
auto b = dht::InfoHash::get("2345");
auto c = dht::InfoHash::get("23456");
auto d = dht::InfoHash::get("234567");
constexpr unsigned N = 256;
constexpr unsigned SZ = 56 * 1024;
constexpr unsigned N = 32;
auto ftokena = node1.listen(a, [&](const std::shared_ptr<dht::Value>&) {
valueCount++;
@@ -107,16 +101,6 @@ DhtRunnerTester::testListen() {
return true;
});
auto ftokend = node1.listen(d, [&](const std::shared_ptr<dht::Value>&) {
valueCount++;
return true;
});
std::vector<uint8_t> mtu;
mtu.reserve(SZ);
for (size_t i = 0; i < SZ; i++)
mtu.emplace_back((i % 2) ? 'T' : 'M');
for (unsigned i=0; i<N; i++) {
node2.put(a, dht::Value("v1"), [&](bool ok) {
std::lock_guard<std::mutex> lock(mutex);
@@ -130,88 +114,29 @@ DhtRunnerTester::testListen() {
if (ok) putOkCount++;
cv.notify_all();
});
auto bigVal = std::make_shared<dht::Value>();
bigVal->data = mtu;
node2.put(c, bigVal, [&](bool ok) {
std::lock_guard<std::mutex> lock(mutex);
putCount++;
if (ok) putOkCount++;
cv.notify_all();
});
}
{
std::unique_lock<std::mutex> lk(mutex);
CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]{ return putCount == N * 3u; }));
CPPUNIT_ASSERT_EQUAL(N * 3u, putOkCount);
CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]{ return putCount == N * 2u; }));
CPPUNIT_ASSERT_EQUAL(N * 2u, putOkCount);
}
CPPUNIT_ASSERT(ftokena.valid());
CPPUNIT_ASSERT(ftokenb.valid());
CPPUNIT_ASSERT(ftokenc.valid());
CPPUNIT_ASSERT(ftokend.valid());
auto tokena = ftokena.get();
auto tokenc = ftokenc.get();
auto tokend = ftokend.get();
// tokenb might be 0 since the callback returns false.
CPPUNIT_ASSERT(tokena);
CPPUNIT_ASSERT(tokenc);
CPPUNIT_ASSERT(tokend);
CPPUNIT_ASSERT_EQUAL(N * 2u + 1u, valueCount.load());
CPPUNIT_ASSERT_EQUAL(N + 1u, valueCount.load());
node1.cancelListen(a, tokena);
node1.cancelListen(b, std::move(ftokenb));
node1.cancelListen(c, tokenc);
node1.cancelListen(d, tokend);
}
void
DhtRunnerTester::testListenLotOfBytes() {
std::mutex mutex;
std::condition_variable cv;
std::atomic_uint valueCount(0);
unsigned putCount(0);
unsigned putOkCount(0);
std::string data(10000, 'a');
auto foo = dht::InfoHash::get("foo");
constexpr unsigned N = 50;
for (unsigned i=0; i<N; i++) {
node2.put(foo, data, [&](bool ok) {
std::lock_guard<std::mutex> lock(mutex);
putCount++;
if (ok) putOkCount++;
cv.notify_all();
});
}
{
std::unique_lock<std::mutex> lk(mutex);
CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]{ return putCount == N; }));
}
dht::DhtRunner node3 {};
dht::DhtRunner::Config config;
config.dht_config.node_config.max_peer_req_per_sec = -1;
config.dht_config.node_config.max_req_per_sec = -1;
node3.run(42242, config);
node3.bootstrap(node1.getBound());
auto ftokenfoo = node3.listen(foo, [&](const std::shared_ptr<dht::Value>&) {
valueCount++;
cv.notify_all();
return true;
});
{
std::unique_lock<std::mutex> lk(mutex);
CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]{ return valueCount == N; }));
}
node3.cancelListen(foo, ftokenfoo.get());
}
} // namespace test
-5
View File
@@ -32,7 +32,6 @@ class DhtRunnerTester : public CppUnit::TestFixture {
CPPUNIT_TEST(testConstructors);
CPPUNIT_TEST(testGetPut);
CPPUNIT_TEST(testListen);
CPPUNIT_TEST(testListenLotOfBytes);
CPPUNIT_TEST_SUITE_END();
dht::DhtRunner node1 {};
@@ -58,10 +57,6 @@ class DhtRunnerTester : public CppUnit::TestFixture {
* Test listen method
*/
void testListen();
/**
* Test listen method with lot of datas
*/
void testListenLotOfBytes();
};
} // namespace test
+3 -4
View File
@@ -39,10 +39,9 @@ HttpTester::setUp() {
nodeProxy->run(0, /*identity*/{}, /*threaded*/true);
nodeProxy->bootstrap(nodePeer->getBound());
dht::ProxyServerConfig config;
config.port = 8080;
config.pushServer = "127.0.0.1:8090";
serverProxy = std::make_unique<dht::DhtProxyServer>(nodeProxy, config);
serverProxy = std::unique_ptr<dht::DhtProxyServer>(
new dht::DhtProxyServer(
/*http*/dht::crypto::Identity{}, nodeProxy, 8080, /*pushServer*/"127.0.0.1:8090"));
}
void
+3 -3
View File
@@ -64,10 +64,10 @@ main(int argc, char **argv)
auto dhtConf = getDhtConfig(params);
dht.run(params.port, dhtConf.first, std::move(dhtConf.second));
if (not params.bootstrap.empty())
dht.bootstrap(params.bootstrap);
if (not params.bootstrap.first.empty())
dht.bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str());
print_node_info(dht.getNodeInfo());
print_node_info(dht, params);
std::cout << " type 'c {hash}' to join a channel" << std::endl << std::endl;
bool connected {false};
+59 -51
View File
@@ -114,7 +114,7 @@ void cmd_loop(std::shared_ptr<DhtRunner>& node, dht_params& params
#endif
)
{
print_node_info(node->getNodeInfo());
print_node_info(*node, params);
std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl << std::endl;
#ifndef WIN32_NATIVE
@@ -143,23 +143,30 @@ void cmd_loop(std::shared_ptr<DhtRunner>& node, dht_params& params
print_help();
continue;
} else if (op == "ll") {
node->getNodeInfo([&](const std::shared_ptr<dht::NodeInfo>& nodeInfo) {
print_node_info(*nodeInfo);
std::cout << nodeInfo->ongoing_ops << " ongoing operations" << std::endl;
std::cout << "IPv4 stats:" << std::endl;
std::cout << nodeInfo->ipv4.toString() << std::endl;
std::cout << "IPv6 stats:" << std::endl;
std::cout << nodeInfo->ipv6.toString() << std::endl;
auto nodeInfo = node->getNodeInfo();
print_node_info(*node, params);
std::cout << "Scheduler size:" << nodeInfo.metrics.scheduler_size << std::endl;
std::cout << "RateLimiter size:" << nodeInfo.metrics.rate_limiter_size << std::endl;
std::cout << "ReportedAddr count:" << nodeInfo.metrics.reported_addr_num << std::endl;
std::cout << "Node cache size:" << nodeInfo.metrics.node_cache_size << std::endl;
std::cout << "Store size:" << nodeInfo.metrics.store_size << std::endl;
std::cout << "Store quota size:" << nodeInfo.metrics.store_quota_size << std::endl;
std::cout << "Global request count:" << nodeInfo.metrics.request_count << std::endl;
std::cout << nodeInfo.ongoing_ops << " ongoing operations" << std::endl;
std::cout << "IPv4 stats:" << std::endl;
std::cout << nodeInfo.ipv4.toString() << std::endl;
std::cout << "IPv6 stats:" << std::endl;
std::cout << nodeInfo.ipv6.toString() << std::endl;
#ifdef OPENDHT_PROXY_SERVER
for (const auto& proxy : proxies) {
std::cout << "Stats for proxy server on port " << proxy.first << std::endl;
if (auto stats = proxy.second->stats())
std::cout << " " << stats->toString() << std::endl;
else
std::cout << " (stats not available yet)" << std::endl;
}
for (const auto& proxy : proxies) {
std::cout << "Stats for proxy server on port " << proxy.first << std::endl;
if (auto stats = proxy.second->stats())
std::cout << " " << stats->toString() << std::endl;
else
std::cout << " (stats not available yet)" << std::endl;
}
#endif
});
continue;
} else if (op == "lr") {
std::cout << "IPv4 routing table:" << std::endl;
@@ -191,10 +198,14 @@ void cmd_loop(std::shared_ptr<DhtRunner>& node, dht_params& params
continue;
} else if (op == "b") {
iss >> idstr;
if (not idstr.empty())
node->bootstrap(idstr);
else
std::cerr << "No service provided. Syntax: b hostname:port" << std::endl;
try {
auto addr = splitPort(idstr);
if (not addr.first.empty() and addr.second.empty())
addr.second = std::to_string(dht::net::DHT_DEFAULT_PORT);
node->bootstrap(addr.first.c_str(), addr.second.c_str());
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
continue;
} else if (op == "log") {
iss >> idstr;
@@ -218,11 +229,14 @@ void cmd_loop(std::shared_ptr<DhtRunner>& node, dht_params& params
iss >> idstr;
#endif // OPENDHT_PUSH_NOTIFICATIONS
try {
in_port_t port = std::stoi(idstr);
ProxyServerConfig serverConfig;
serverConfig.port = port;
serverConfig.pushServer = pushServer;
proxies.emplace(port, std::make_unique<DhtProxyServer>(node, serverConfig));
unsigned int port = std::stoi(idstr);
proxies.emplace(port, std::unique_ptr<DhtProxyServer>(
new DhtProxyServer(
dht::crypto::Identity{}, node, port
#ifdef OPENDHT_PUSH_NOTIFICATIONS
,pushServer
#endif
)));
} catch (...) { }
continue;
} else if (op == "psx") {
@@ -233,12 +247,13 @@ void cmd_loop(std::shared_ptr<DhtRunner>& node, dht_params& params
#endif // OPENDHT_PUSH_NOTIFICATIONS
try {
if (params.proxy_id.first and params.proxy_id.second){
in_port_t port = std::stoi(idstr);
ProxyServerConfig serverConfig;
serverConfig.identity = params.proxy_id;
serverConfig.port = port;
serverConfig.pushServer = pushServer;
proxies.emplace(port, std::make_unique<DhtProxyServer>(node, serverConfig));
unsigned int port = std::stoi(idstr);
proxies.emplace(port, std::unique_ptr<DhtProxyServer>(
new DhtProxyServer(params.proxy_id, node, port
#ifdef OPENDHT_PUSH_NOTIFICATIONS
,pushServer
#endif
)));
}
else {
std::cerr << "Missing Identity private key or certificate" << std::endl;
@@ -530,32 +545,25 @@ main(int argc, char **argv)
auto dhtConf = getDhtConfig(params);
node->run(params.port, dhtConf.first, std::move(dhtConf.second));
if (not params.bootstrap.empty()) {
std::cout << "Bootstrap: " << params.bootstrap << std::endl;
node->bootstrap(params.bootstrap);
if (not params.bootstrap.first.empty()) {
std::cout << "Bootstrap: " << params.bootstrap.first << ":" << params.bootstrap.second << std::endl;
node->bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str());
}
#ifdef OPENDHT_PROXY_SERVER
std::map<in_port_t, std::unique_ptr<DhtProxyServer>> proxies;
#endif
if (params.proxyserver or params.proxyserverssl) {
if (params.proxyserverssl and params.proxy_id.first and params.proxy_id.second){
#ifdef OPENDHT_PROXY_SERVER
ProxyServerConfig serverConfig;
serverConfig.pushServer = params.pushserver;
if (params.proxyserverssl and params.proxy_id.first and params.proxy_id.second){
serverConfig.identity = params.proxy_id;
serverConfig.port = params.proxyserverssl;
if (not params.persist_path.empty())
serverConfig.persistStatePath = params.persist_path + '_' + std::to_string(serverConfig.port);
proxies.emplace(params.proxyserverssl, std::make_unique<DhtProxyServer>(node, serverConfig, dhtConf.second.logger));
}
if (params.proxyserver) {
serverConfig.identity = {};
serverConfig.port = params.proxyserver;
if (not params.persist_path.empty())
serverConfig.persistStatePath = params.persist_path + '_' + std::to_string(serverConfig.port);
proxies.emplace(params.proxyserver, std::make_unique<DhtProxyServer>(node, serverConfig, dhtConf.second.logger));
}
proxies.emplace(params.proxyserverssl, std::unique_ptr<DhtProxyServer>(
new DhtProxyServer(params.proxy_id,
node, params.proxyserverssl, params.pushserver, dhtConf.second.logger)));
}
if (params.proxyserver) {
proxies.emplace(params.proxyserver, std::unique_ptr<DhtProxyServer>(
new DhtProxyServer(
dht::crypto::Identity{},
node, params.proxyserver, params.pushserver, dhtConf.second.logger)));
#else
std::cerr << "DHT proxy server requested but OpenDHT built without proxy server support." << std::endl;
exit(EXIT_FAILURE);
+11 -8
View File
@@ -7,7 +7,7 @@ stats_total = {"users":0, "pushListenersCount":0, "listenCount":0, "totalListene
for i in range(80,101):
print("Collecting stats for proxy " + str(i))
response = requests.request('STATS', 'http://127.0.0.1:' + str(i))
response = requests.request('STATS', 'http://dhtproxy.jami.net:' + str(i))
if response.status_code == 200:
result = response.json()
@@ -38,17 +38,20 @@ for i in range(80,101):
except:
pass
try:
stats['totalPermanentPuts'] = int(result["totalPermanentPuts"])
stats_total['totalPermanentPuts'] += int(result["totalPermanentPuts"])
total = 0
for h,v in result["puts"].items():
total += int(v)
stats['totalPermanentPuts'] = total
stats_total['totalPermanentPuts'] += total
except:
pass
stats['timestamp'] = str(ts)
#with open("stats_proxy_" + str(i), "a") as stat_file:
# stat_file.write(str(stats))
# stat_file.write('\n')
with open("stats_proxy_" + str(i), "a") as stat_file:
stat_file.write(str(stats))
stat_file.write('\n')
with open("stats_proxy_total", "w") as stat_file:
with open("stats_proxy_total", "a") as stat_file:
stat_file.write(str(stats_total))
stat_file.write('\n')
+3 -3
View File
@@ -91,10 +91,10 @@ main(int argc, char **argv)
auto dhtConf = getDhtConfig(params);
dht.run(params.port, dhtConf.first, std::move(dhtConf.second));
if (not params.bootstrap.empty())
dht.bootstrap(params.bootstrap);
if (not params.bootstrap.first.empty())
dht.bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str());
print_node_info(dht.getNodeInfo());
print_node_info(dht, params);
std::cout << "Scanning network..." << std::endl;
auto all_nodes = std::make_shared<NodeSet>();
+14 -11
View File
@@ -124,7 +124,7 @@ struct dht_params {
bool log {false};
bool syslog {false};
std::string logfile {};
std::string bootstrap {};
std::pair<std::string, std::string> bootstrap {};
dht::NetId network {0};
in_port_t port {0};
in_port_t proxyserver {0};
@@ -170,8 +170,6 @@ getDhtConfig(dht_params& params)
if (params.no_rate_limit) {
config.dht_config.node_config.max_req_per_sec = -1;
config.dht_config.node_config.max_peer_req_per_sec = -1;
config.dht_config.node_config.max_searches = -1;
config.dht_config.node_config.max_store_size = -1;
}
dht::DhtRunner::Context context {};
@@ -191,14 +189,16 @@ getDhtConfig(dht_params& params)
return {std::move(config), std::move(context)};
}
void print_node_info(const dht::NodeInfo& info) {
std::cout << "OpenDHT node " << info.node_id << " running on ";
if (info.bound4 == info.bound6)
std::cout << "port " << info.bound4 << std::endl;
void print_node_info(const dht::DhtRunner& node, const dht_params& params) {
std::cout << "OpenDHT node " << node.getNodeId() << " running on ";
auto port4 = node.getBoundPort(AF_INET);
auto port6 = node.getBoundPort(AF_INET6);
if (port4 == port6)
std::cout << "port " << port4 << std::endl;
else
std::cout << "IPv4 port " << info.bound4 << ", IPv6 port " << info.bound6 << std::endl;
if (info.id)
std::cout << "Public key ID " << info.id << std::endl;
std::cout << "IPv4 port " << port4 << ", IPv6 port " << port6 << std::endl;
if (params.id.first)
std::cout << "Public key ID " << node.getId() << std::endl;
}
static const constexpr struct option long_options[] = {
@@ -289,7 +289,10 @@ parseArgs(int argc, char **argv) {
params.public_stable = true;
break;
case 'b':
params.bootstrap = (optarg[0] == '=') ? optarg+1 : optarg;
params.bootstrap = dht::splitPort((optarg[0] == '=') ? optarg+1 : optarg);
if (not params.bootstrap.first.empty() and params.bootstrap.second.empty()) {
params.bootstrap.second = std::to_string(dht::net::DHT_DEFAULT_PORT);
}
break;
case 'V':
params.version = true;