mirror of
https://github.com/savoirfairelinux/opendht.git
synced 2026-11-08 20:37:23 +00:00
Compare commits
5
Commits
proxy_cleanup
..
c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb38ba8a6f | ||
|
|
a1d0273514 | ||
|
|
a1da0af78c | ||
|
|
6d95f7a181 | ||
|
|
76097b0da2 |
No files matched your search
@@ -29,24 +29,6 @@ jobs:
|
||||
- name: make check
|
||||
run: make check
|
||||
|
||||
build-ubuntu-minimal:
|
||||
name: Ubuntu/GCC minimal build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: deps
|
||||
run: |
|
||||
sudo apt install libncurses5-dev libreadline-dev nettle-dev \
|
||||
libgnutls28-dev libcppunit-dev libmsgpack-dev libargon2-0-dev
|
||||
- name: cmake
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Debug \
|
||||
-DOPENDHT_C=Off -DOPENDHT_TESTS=On -DOPENDHT_PEER_DISCOVERY=Off -DOPENDHT_PYTHON=Off \
|
||||
-DOPENDHT_TOOLS=On -DOPENDHT_PROXY_SERVER=Off -DOPENDHT_PROXY_CLIENT=Off
|
||||
- name: make
|
||||
run: cd build && make
|
||||
|
||||
build-macos:
|
||||
name: macOS/Clang build
|
||||
runs-on: macos-11
|
||||
@@ -55,7 +37,7 @@ jobs:
|
||||
- name: deps
|
||||
run: |
|
||||
brew install msgpack-cxx asio gnutls nettle readline fmt jsoncpp argon2 openssl http-parser cppunit
|
||||
|
||||
|
||||
- name: restinio
|
||||
run: |
|
||||
mkdir restinio && cd restinio
|
||||
|
||||
Vendored
-1
@@ -20,7 +20,6 @@
|
||||
"command": "cmake",
|
||||
"args": [
|
||||
"${workspaceRoot}",
|
||||
"-DCMAKE_EXPORT_COMPILE_COMMANDS=On",
|
||||
"-DCMAKE_BUILD_TYPE=Debug",
|
||||
"-DOPENDHT_PROXY_CLIENT=On",
|
||||
"-DOPENDHT_PROXY_SERVER=On",
|
||||
|
||||
+10
-17
@@ -19,7 +19,7 @@ include(FindPkgConfig)
|
||||
include(cmake/CheckAtomic.cmake)
|
||||
|
||||
set (opendht_VERSION_MAJOR 2)
|
||||
set (opendht_VERSION_MINOR 4.5)
|
||||
set (opendht_VERSION_MINOR 4.0)
|
||||
set (opendht_VERSION ${opendht_VERSION_MAJOR}.${opendht_VERSION_MINOR})
|
||||
set (PACKAGE_VERSION ${opendht_VERSION})
|
||||
set (VERSION "${opendht_VERSION}")
|
||||
@@ -403,23 +403,15 @@ if (OPENDHT_SHARED)
|
||||
endif ()
|
||||
|
||||
if (OPENDHT_C)
|
||||
add_library (opendht-c SHARED
|
||||
c/opendht.cpp
|
||||
c/opendht_c.h
|
||||
)
|
||||
target_compile_definitions(opendht-c PRIVATE OPENDHT_C_BUILD)
|
||||
if (OPENDHT_SHARED)
|
||||
add_library (opendht-c SHARED
|
||||
c/opendht.cpp
|
||||
c/opendht_c.h
|
||||
)
|
||||
target_compile_definitions(opendht-c PRIVATE OPENDHT_C_BUILD)
|
||||
target_link_libraries(opendht-c PRIVATE opendht)
|
||||
install (TARGETS opendht-c DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht-c)
|
||||
endif ()
|
||||
|
||||
if (OPENDHT_STATIC)
|
||||
add_library (opendht-c-static STATIC
|
||||
c/opendht.cpp
|
||||
c/opendht_c.h
|
||||
)
|
||||
target_link_libraries(opendht-c-static PRIVATE opendht-static)
|
||||
install (TARGETS opendht-c-static DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht-c-static)
|
||||
target_link_libraries(opendht-c opendht)
|
||||
else ()
|
||||
target_link_libraries(opendht-c opendht-static)
|
||||
endif ()
|
||||
|
||||
# PkgConfig module
|
||||
@@ -428,6 +420,7 @@ if (OPENDHT_C)
|
||||
opendht-c.pc
|
||||
@ONLY
|
||||
)
|
||||
install (TARGETS opendht-c DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht-c)
|
||||
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/opendht-c.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
install (FILES c/opendht_c.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include/opendht)
|
||||
endif ()
|
||||
|
||||
+1
-10
@@ -32,11 +32,6 @@ extern "C" {
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
const char* dht_version()
|
||||
{
|
||||
return dht::version();
|
||||
}
|
||||
|
||||
// dht::InfoHash
|
||||
|
||||
inline dht_infohash dht_infohash_to_c(const dht::InfoHash& h) {
|
||||
@@ -76,11 +71,7 @@ bool dht_infohash_is_zero(const dht_infohash* h) {
|
||||
}
|
||||
|
||||
void dht_infohash_from_hex(dht_infohash* h, const char* dat) {
|
||||
*h = dht_infohash_to_c(dht::InfoHash(std::string_view(dat, HASH_LEN*2)));
|
||||
}
|
||||
|
||||
void dht_infohash_from_hex_null(dht_infohash* h, const char* dat) {
|
||||
*h = dht_infohash_to_c(dht::InfoHash(std::string_view(dat)));
|
||||
*h = dht_infohash_to_c(dht::InfoHash(std::string(dat, HASH_LEN*2)));
|
||||
}
|
||||
|
||||
const char* dht_pkid_print(const dht_pkid* h) {
|
||||
|
||||
@@ -36,8 +36,6 @@ struct OPENDHT_C_PUBLIC dht_data_view {
|
||||
};
|
||||
typedef struct dht_data_view dht_data_view;
|
||||
|
||||
OPENDHT_C_PUBLIC const char* dht_version(void);
|
||||
|
||||
// dht::Blob
|
||||
struct OPENDHT_C_PUBLIC dht_blob;
|
||||
typedef struct dht_blob dht_blob;
|
||||
@@ -50,7 +48,6 @@ typedef struct dht_infohash dht_infohash;
|
||||
OPENDHT_C_PUBLIC void dht_infohash_zero(dht_infohash* h);
|
||||
OPENDHT_C_PUBLIC void dht_infohash_random(dht_infohash* h);
|
||||
OPENDHT_C_PUBLIC void dht_infohash_from_hex(dht_infohash* h, const char* dat);
|
||||
OPENDHT_C_PUBLIC void dht_infohash_from_hex_null(dht_infohash* h, const char* dat);
|
||||
OPENDHT_C_PUBLIC void dht_infohash_get(dht_infohash* h, const uint8_t* dat, size_t dat_size);
|
||||
OPENDHT_C_PUBLIC void dht_infohash_get_from_string(dht_infohash* h, const char* str);
|
||||
OPENDHT_C_PUBLIC const char* dht_infohash_print(const dht_infohash* h);
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
dnl define macros
|
||||
m4_define([opendht_major_version], 2)
|
||||
m4_define([opendht_minor_version], 4)
|
||||
m4_define([opendht_patch_version], 5)
|
||||
m4_define([opendht_patch_version], 0)
|
||||
m4_define([opendht_version],
|
||||
[opendht_major_version.opendht_minor_version.opendht_patch_version])
|
||||
|
||||
@@ -129,8 +129,8 @@ AC_ARG_ENABLE([proxy_client], AS_HELP_STRING([--enable-proxy-client], [Enable pr
|
||||
AM_CONDITIONAL(ENABLE_PROXY_CLIENT, test x$proxy_client == xyes)
|
||||
|
||||
AC_ARG_ENABLE([tests], AS_HELP_STRING([--enable-tests], [Enable tests]), build_tests=yes, build_tests=no)
|
||||
AM_CONDITIONAL(ENABLE_TESTS, test "x$build_tests" == xyes)
|
||||
AS_IF([test "x$build_tests" == xyes], [
|
||||
AM_CONDITIONAL(ENABLE_TESTS, test x$build_tests == xyes)
|
||||
AM_COND_IF([ENABLE_TESTS], [
|
||||
PKG_CHECK_MODULES([CppUnit], [cppunit >= 1.12])
|
||||
])
|
||||
|
||||
|
||||
@@ -82,8 +82,6 @@ struct OPENDHT_PUBLIC NodeInfo {
|
||||
NodeStats ipv4 {};
|
||||
NodeStats ipv6 {};
|
||||
size_t ongoing_ops {0};
|
||||
size_t storage_values {0};
|
||||
size_t storage_size {0};
|
||||
in_port_t bound4 {0};
|
||||
in_port_t bound6 {0};
|
||||
|
||||
@@ -134,9 +132,6 @@ struct OPENDHT_PUBLIC Config {
|
||||
/* If non-0, overrides the default maximum store size. -1 means no limit. */
|
||||
ssize_t max_store_size {0};
|
||||
|
||||
/* If non-0, overrides the default maximum store key count. -1 means no limit. */
|
||||
ssize_t max_store_keys {0};
|
||||
|
||||
/**
|
||||
* Use appropriate bahavior for a public IP, stable node:
|
||||
* - No connectivity change triggered when a search fails
|
||||
|
||||
@@ -302,9 +302,6 @@ public:
|
||||
void setStorageLimit(size_t limit = DEFAULT_STORAGE_LIMIT) override {
|
||||
max_store_size = limit;
|
||||
}
|
||||
size_t getStorageLimit() const override {
|
||||
return max_store_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total memory usage of stored values and the number
|
||||
@@ -343,7 +340,7 @@ private:
|
||||
static constexpr unsigned LISTEN_NODES {4};
|
||||
|
||||
/* The maximum number of hashes we're willing to track. */
|
||||
static constexpr unsigned MAX_HASHES {1024 * 1024 * 1024};
|
||||
static constexpr unsigned MAX_HASHES {1024 * 1024};
|
||||
|
||||
/* The maximum number of searches we keep data about. */
|
||||
static constexpr unsigned MAX_SEARCHES {1024 * 1024};
|
||||
@@ -450,13 +447,13 @@ private:
|
||||
// Storage
|
||||
void storageAddListener(const InfoHash& id, const Sp<Node>& node, size_t tid, Query&& = {}, int version = 0);
|
||||
bool storageStore(const InfoHash& id, const Sp<Value>& value, time_point created, const SockAddr& sa = {}, bool permanent = false);
|
||||
bool storageErase(const InfoHash& id, Value::Id vid);
|
||||
bool storageRefresh(const InfoHash& id, Value::Id vid);
|
||||
void expireStore();
|
||||
void expireStorage(InfoHash h);
|
||||
void expireStore(decltype(store)::iterator);
|
||||
|
||||
void storageRemoved(const InfoHash& id, Storage& st, const std::vector<Sp<Value>>& values, size_t totalSize);
|
||||
void storageChanged(const InfoHash& id, Storage& st, const Sp<Value>&, bool newValue);
|
||||
void storageChanged(const InfoHash& id, Storage& st, ValueStorage&, bool newValue);
|
||||
std::string printStorageLog(const decltype(store)::value_type&) const;
|
||||
|
||||
/**
|
||||
|
||||
@@ -221,7 +221,6 @@ public:
|
||||
* Set the in-memory storage limit in bytes
|
||||
*/
|
||||
virtual void setStorageLimit(size_t limit = DEFAULT_STORAGE_LIMIT) = 0;
|
||||
virtual size_t getStorageLimit() const = 0;
|
||||
|
||||
/**
|
||||
* Returns the total memory usage of stored values and the number
|
||||
|
||||
@@ -263,7 +263,6 @@ public:
|
||||
void dumpTables() const override {}
|
||||
std::vector<unsigned> getNodeMessageStats(bool) override { return {}; }
|
||||
void setStorageLimit(size_t) override {}
|
||||
virtual size_t getStorageLimit() const override { return 0; }
|
||||
void connectivityChanged(sa_family_t) override {
|
||||
getProxyInfos();
|
||||
}
|
||||
|
||||
@@ -335,7 +335,6 @@ public:
|
||||
|
||||
std::pair<size_t, size_t> getStoreSize() const;
|
||||
|
||||
void getStorageLimit() const;
|
||||
void setStorageLimit(size_t limit = DEFAULT_STORAGE_LIMIT);
|
||||
|
||||
std::vector<NodeExport> exportNodes() const;
|
||||
@@ -437,6 +436,7 @@ public:
|
||||
/**
|
||||
* Start or stop the proxy
|
||||
* @param proxify if we want to use the proxy
|
||||
* @param deviceKey non empty to enable push notifications
|
||||
*/
|
||||
void enableProxy(bool proxify);
|
||||
|
||||
@@ -473,9 +473,12 @@ private:
|
||||
DoneCallback bindOpDoneCallback(DoneCallback&& cb);
|
||||
DoneCallbackSimple bindOpDoneCallback(DoneCallbackSimple&& cb);
|
||||
|
||||
/** DHT instance */
|
||||
/** Local DHT instance */
|
||||
std::unique_ptr<SecureDht> dht_;
|
||||
|
||||
/** Proxy client instance */
|
||||
std::unique_ptr<SecureDht> dht_via_proxy_;
|
||||
|
||||
/** true if we are currently using a proxy */
|
||||
std::atomic_bool use_proxy {false};
|
||||
|
||||
@@ -487,6 +490,17 @@ private:
|
||||
* reset dht clients
|
||||
*/
|
||||
void resetDht();
|
||||
/**
|
||||
* @return the current active DHT
|
||||
*/
|
||||
SecureDht* activeDht() const;
|
||||
|
||||
/**
|
||||
* Store current listeners and translates global tokens for each client.
|
||||
*/
|
||||
struct Listener;
|
||||
std::map<size_t, Listener> listeners_;
|
||||
size_t listener_token_ {1};
|
||||
|
||||
mutable std::mutex dht_mtx {};
|
||||
std::thread dht_thread {};
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace dht {
|
||||
namespace net {
|
||||
|
||||
static const constexpr in_port_t DHT_DEFAULT_PORT = 4222;
|
||||
static const constexpr size_t RX_QUEUE_MAX_SIZE = 1024 * 64;
|
||||
static const constexpr size_t RX_QUEUE_MAX_SIZE = 1024 * 16;
|
||||
static const constexpr std::chrono::milliseconds RX_QUEUE_MAX_DELAY(650);
|
||||
|
||||
int bindSocket(const SockAddr& addr, SockAddr& bound);
|
||||
|
||||
@@ -173,10 +173,6 @@ public:
|
||||
void setStorageLimit(size_t limit = DEFAULT_STORAGE_LIMIT) override {
|
||||
dht_->setStorageLimit(limit);
|
||||
}
|
||||
size_t getStorageLimit() const override {
|
||||
return dht_->getStorageLimit();
|
||||
}
|
||||
|
||||
std::vector<NodeExport> exportNodes() const override {
|
||||
return dht_->exportNodes();
|
||||
}
|
||||
|
||||
+27
-16
@@ -213,7 +213,7 @@ struct OPENDHT_PUBLIC Value
|
||||
}
|
||||
static Filter notFilter(Filter&& f) {
|
||||
if (not f) return [](const Value&) { return false; };
|
||||
return [f = std::move(f)](const Value& v) { return not f(v); };
|
||||
return [f](const Value& v) { return not f(v); };
|
||||
}
|
||||
std::vector<Sp<Value>> filter(const std::vector<Sp<Value>>& values) {
|
||||
if (not (*this))
|
||||
@@ -343,10 +343,10 @@ struct OPENDHT_PUBLIC Value
|
||||
return unpack<T>(*this);
|
||||
}
|
||||
|
||||
inline bool isEncrypted() const {
|
||||
bool isEncrypted() const {
|
||||
return not cypher.empty();
|
||||
}
|
||||
inline bool isSigned() const {
|
||||
bool isSigned() const {
|
||||
return owner and not signature.empty();
|
||||
}
|
||||
|
||||
@@ -355,24 +355,37 @@ struct OPENDHT_PUBLIC Value
|
||||
* Afterward, checkSignature() will return true and owner will
|
||||
* be set to the corresponding public key.
|
||||
*/
|
||||
void sign(const crypto::PrivateKey& key);
|
||||
void sign(const crypto::PrivateKey& key) {
|
||||
if (isEncrypted())
|
||||
throw DhtException("Can't sign encrypted data.");
|
||||
owner = key.getSharedPublicKey();
|
||||
signature = key.sign(getToSign());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the value is signed and that the signature matches.
|
||||
* If true, the owner field will contain the signer public key.
|
||||
*/
|
||||
inline bool checkSignature() const {
|
||||
bool checkSignature() const {
|
||||
return isSigned() and owner->checkSignature(getToSign(), signature);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<crypto::PublicKey> getOwner() const {
|
||||
return owner;
|
||||
std::shared_ptr<crypto::PublicKey> getOwner() const {
|
||||
return std::static_pointer_cast<crypto::PublicKey>(owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign the value with from and returns the encrypted version for to.
|
||||
*/
|
||||
Value encrypt(const crypto::PrivateKey& from, const crypto::PublicKey& to);
|
||||
Value encrypt(const crypto::PrivateKey& from, const crypto::PublicKey& to) {
|
||||
if (isEncrypted())
|
||||
throw DhtException("Data is already encrypted.");
|
||||
setRecipient(to.getId());
|
||||
sign(from);
|
||||
Value nv {id};
|
||||
nv.setCypher(to.encrypt(getToEncrypt()));
|
||||
return nv;
|
||||
}
|
||||
|
||||
Value() {}
|
||||
|
||||
@@ -440,18 +453,18 @@ struct OPENDHT_PUBLIC Value
|
||||
return id == o.id and contentEquals(o);
|
||||
}
|
||||
|
||||
inline void setRecipient(const InfoHash& r) {
|
||||
void setRecipient(const InfoHash& r) {
|
||||
recipient = r;
|
||||
}
|
||||
|
||||
inline void setCypher(Blob&& c) {
|
||||
void setCypher(Blob&& c) {
|
||||
cypher = std::move(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack part of the data to be signed (must always be done the same way)
|
||||
*/
|
||||
inline Blob getToSign() const {
|
||||
Blob getToSign() const {
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
msgpack_pack_to_sign(pk);
|
||||
@@ -461,7 +474,7 @@ struct OPENDHT_PUBLIC Value
|
||||
/**
|
||||
* Pack part of the data to be encrypted
|
||||
*/
|
||||
inline Blob getToEncrypt() const {
|
||||
Blob getToEncrypt() const {
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
msgpack_pack_to_encrypt(pk);
|
||||
@@ -471,7 +484,7 @@ struct OPENDHT_PUBLIC Value
|
||||
/** print value for debugging */
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const Value& v);
|
||||
|
||||
inline std::string toString() const {
|
||||
std::string toString() const {
|
||||
std::stringstream ss;
|
||||
ss << *this;
|
||||
return ss.str();
|
||||
@@ -625,10 +638,8 @@ struct OPENDHT_PUBLIC Value
|
||||
*/
|
||||
unsigned priority {0};
|
||||
|
||||
bool checkSignature();
|
||||
Sp<Value> decrypt(const crypto::PrivateKey& key);
|
||||
|
||||
private:
|
||||
friend class SecureDht;
|
||||
/* Cache for crypto ops */
|
||||
bool signatureChecked {false};
|
||||
bool signatureValid {false};
|
||||
|
||||
@@ -20,7 +20,7 @@ help).
|
||||
|
||||
## Python dependencies
|
||||
|
||||
- pyroute2 >=0.6.9
|
||||
- pyroute2 >=0.3.14
|
||||
- matplotlib
|
||||
- GeoIP (used by `scanner.py` for drawing map of the world)
|
||||
- ipaddress
|
||||
|
||||
@@ -30,7 +30,7 @@ import traceback
|
||||
import ipaddress
|
||||
import netifaces
|
||||
import numpy as np
|
||||
from pyroute2 import NSPopen
|
||||
from pyroute2.netns.process.proxy import NSPopen
|
||||
import msgpack
|
||||
|
||||
from opendht import *
|
||||
|
||||
@@ -15,253 +15,107 @@
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from pyroute2 import NDB, NSPopen
|
||||
import argparse, subprocess
|
||||
|
||||
from pyroute2 import IPDB, NetNS
|
||||
from pyroute2.netns.process.proxy import NSPopen
|
||||
|
||||
def int_range(mini, maxi):
|
||||
def check_ifnum(arg):
|
||||
try:
|
||||
ret = int(arg)
|
||||
except ValueError:
|
||||
raise argparse.ArgumentTypeError('must be an integer')
|
||||
if ret > maxi or ret < mini:
|
||||
raise argparse.ArgumentTypeError(
|
||||
f'must be {mini} <= int <= {maxi}'
|
||||
)
|
||||
return ret
|
||||
|
||||
return check_ifnum
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Creates a virtual network topology for testing'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-i', '--ifname', help='interface name', default='ethdht'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-n',
|
||||
'--ifnum',
|
||||
type=int_range(1, 245),
|
||||
help='number of isolated interfaces to create',
|
||||
default=1,
|
||||
)
|
||||
parser.add_argument(
|
||||
'-r',
|
||||
'--remove',
|
||||
help='remove instead of adding network interfaces',
|
||||
action='store_true',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-l',
|
||||
'--loss',
|
||||
help='simulated packet loss (percent)',
|
||||
type=int,
|
||||
default=0,
|
||||
)
|
||||
parser.add_argument(
|
||||
'-d', '--delay', help='simulated latency (ms)', type=int, default=0
|
||||
)
|
||||
parser.add_argument(
|
||||
'-4', '--ipv4', help='Enable IPv4', action='store_true'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-6', '--ipv6', help='Enable IPv6', action='store_true'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-b',
|
||||
'--debug',
|
||||
help='Turn on debug logging and dump topology databases',
|
||||
action='store_true',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-v',
|
||||
'--verbose',
|
||||
help='Turn on verbose output on netns and interfaces operations',
|
||||
action='store_true',
|
||||
)
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='Creates a virtual network topology for testing')
|
||||
parser.add_argument('-i', '--ifname', help='interface name', default='ethdht')
|
||||
parser.add_argument('-n', '--ifnum', type=int, help='number of isolated interfaces to create', default=1)
|
||||
parser.add_argument('-r', '--remove', help='remove instead of adding network interfaces', action="store_true")
|
||||
parser.add_argument('-l', '--loss', help='simulated packet loss (percent)', type=int, default=0)
|
||||
parser.add_argument('-d', '--delay', help='simulated latency (ms)', type=int, default=0)
|
||||
parser.add_argument('-4', '--ipv4', help='Enable IPv4', action="store_true")
|
||||
parser.add_argument('-6', '--ipv6', help='Enable IPv6', action="store_true")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
local_addr4 = '10.0.42.'
|
||||
local_addr6 = '2001:db9::'
|
||||
bripv4 = f'{local_addr4}1/24'
|
||||
bripv6 = f'{local_addr6}1/64'
|
||||
bridge_name = f'br{args.ifname}'
|
||||
tap_name = f'tap{args.ifname}'
|
||||
veth_names = []
|
||||
namespaces = []
|
||||
ipv4addrs = []
|
||||
ipv6addrs = []
|
||||
for ifn in range(args.ifnum):
|
||||
namespaces.append(f'node{ifn}')
|
||||
veth_names.append(f'{args.ifname}{ifn}')
|
||||
ipv4addrs.append(f'{local_addr4}{ifn+8}/24' if args.ipv4 else None)
|
||||
ipv6addrs.append(f'{local_addr6}{ifn+8}/64' if args.ipv6 else None)
|
||||
brige_name = 'br'+args.ifname
|
||||
|
||||
with NDB(log='debug' if args.debug else None) as ndb:
|
||||
ip = None
|
||||
try:
|
||||
ip = IPDB()
|
||||
if args.remove:
|
||||
# cleanup interfaces in the main namespace
|
||||
for iface in veth_names + [bridge_name] + [tap_name]:
|
||||
if iface in ndb.interfaces:
|
||||
ndb.interfaces[iface].remove().commit()
|
||||
if args.verbose:
|
||||
print(f'link: del main/{iface}')
|
||||
|
||||
# cleanup namespaces
|
||||
for nsname in namespaces:
|
||||
try:
|
||||
ndb.netns[nsname].remove().commit()
|
||||
if args.verbose:
|
||||
print(f'netns: del {nsname}')
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
# create ports
|
||||
for veth, nsname, ipv4addr, ipv6addr in zip(
|
||||
veth_names, namespaces, ipv4addrs, ipv6addrs
|
||||
):
|
||||
# create a network namespace and launch NDB for it
|
||||
#
|
||||
# another possible solution could be simply to attach
|
||||
# the namespace to the main NDB instance, but it can
|
||||
# take a lot of memory in case of many interfaces, thus
|
||||
# launch and discard netns NDB instances
|
||||
netns = NDB(
|
||||
log='debug' if args.debug else None,
|
||||
sources=[
|
||||
{
|
||||
'target': 'localhost',
|
||||
'netns': nsname,
|
||||
'kind': 'netns',
|
||||
}
|
||||
],
|
||||
)
|
||||
if args.verbose:
|
||||
print(f'netns: add {nsname}')
|
||||
# create the port and push the peer into the namespace
|
||||
(
|
||||
ndb.interfaces.create(
|
||||
**{
|
||||
'ifname': veth,
|
||||
'kind': 'veth',
|
||||
'state': 'up',
|
||||
'peer': {'ifname': veth, 'net_ns_fd': nsname},
|
||||
}
|
||||
).commit()
|
||||
)
|
||||
if args.verbose:
|
||||
print(f'link: add main/{veth} <-> {nsname}/{veth}')
|
||||
# bring up namespace's loopback
|
||||
(
|
||||
netns.interfaces.wait(ifname='lo', timeout=3)
|
||||
.set('state', 'up')
|
||||
.commit()
|
||||
)
|
||||
if args.verbose:
|
||||
print(f'link: set {nsname}/lo')
|
||||
# bring up the peer
|
||||
with netns.interfaces.wait(ifname=veth, timeout=3) as i:
|
||||
i.set('state', 'up')
|
||||
if args.ipv4:
|
||||
i.add_ip(ipv4addr)
|
||||
if args.ipv6:
|
||||
i.add_ip(ipv6addr)
|
||||
if args.verbose:
|
||||
print(f'link: set {nsname}/{veth}, {ipv4addr}, {ipv6addr}')
|
||||
# disconnect the namespace NDB agent, not removing the NS
|
||||
if args.debug:
|
||||
fname = f'{nsname}-ndb.db'
|
||||
print(f'dump: netns topology database {fname}')
|
||||
netns.schema.backup(fname)
|
||||
# cleanup interfaces
|
||||
for ifn in range(args.ifnum):
|
||||
iface = args.ifname+str(ifn)
|
||||
if iface in ip.interfaces:
|
||||
with ip.interfaces[iface] as i:
|
||||
i.remove()
|
||||
if 'tap'+args.ifname in ip.interfaces:
|
||||
with ip.interfaces['tap'+args.ifname] as i:
|
||||
i.remove()
|
||||
if brige_name in ip.interfaces:
|
||||
with ip.interfaces[brige_name] as i:
|
||||
i.remove()
|
||||
for ifn in range(args.ifnum):
|
||||
netns = NetNS('node'+str(ifn))
|
||||
netns.close()
|
||||
# set up the emulation QDisc
|
||||
nsp = NSPopen(
|
||||
nsname,
|
||||
[
|
||||
'tc',
|
||||
'qdisc',
|
||||
'add',
|
||||
'dev',
|
||||
veth,
|
||||
'root',
|
||||
'netem',
|
||||
'delay',
|
||||
f'{args.delay}ms',
|
||||
f'{int(args.delay)/2}ms',
|
||||
'loss',
|
||||
f'{args.loss}%',
|
||||
'25%',
|
||||
],
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
netns.remove()
|
||||
else:
|
||||
for ifn in range(args.ifnum):
|
||||
iface = args.ifname+str(ifn)
|
||||
if not iface in ip.interfaces:
|
||||
ip.create(kind='veth', ifname=iface, peer=iface+'.1').commit()
|
||||
|
||||
ip.create(kind='tuntap', ifname='tap'+args.ifname, mode='tap').commit()
|
||||
|
||||
with ip.create(kind='bridge', ifname=brige_name) as i:
|
||||
for ifn in range(args.ifnum):
|
||||
iface = args.ifname+str(ifn)
|
||||
i.add_port(ip.interfaces[iface])
|
||||
i.add_port(ip.interfaces['tap'+args.ifname])
|
||||
if args.ipv4:
|
||||
i.add_ip(local_addr4+'1/24')
|
||||
if args.ipv6:
|
||||
i.add_ip(local_addr6+'1/64')
|
||||
i.up()
|
||||
|
||||
with ip.interfaces['tap'+args.ifname] as tap:
|
||||
tap.up()
|
||||
|
||||
for ifn in range(args.ifnum):
|
||||
iface = args.ifname+str(ifn)
|
||||
|
||||
nns = NetNS('node'+str(ifn))
|
||||
iface1 = iface+'.1'
|
||||
with ip.interfaces[iface1] as i:
|
||||
i.net_ns_fd = nns.netns
|
||||
|
||||
with ip.interfaces[iface] as i:
|
||||
i.up()
|
||||
|
||||
ip_ns = IPDB(nl=nns)
|
||||
try:
|
||||
with ip_ns.interfaces.lo as lo:
|
||||
lo.up()
|
||||
with ip_ns.interfaces[iface1] as i:
|
||||
if args.ipv4:
|
||||
i.add_ip(local_addr4+str(ifn+8)+'/24')
|
||||
if args.ipv6:
|
||||
i.add_ip(local_addr6+str(ifn+8)+'/64')
|
||||
i.up()
|
||||
finally:
|
||||
ip_ns.release()
|
||||
|
||||
nsp = NSPopen(nns.netns, ["tc", "qdisc", "add", "dev", iface1, "root", "netem", "delay", str(args.delay)+"ms", str(int(args.delay/2))+"ms", "loss", str(args.loss)+"%", "25%"], stdout=subprocess.PIPE)
|
||||
#print(nsp.communicate()[0].decode())
|
||||
nsp.communicate()
|
||||
nsp.wait()
|
||||
nsp.release()
|
||||
if args.verbose:
|
||||
print(
|
||||
f'netem: add {nsname}/{veth}, '
|
||||
f'{args.delay}, {args.loss}'
|
||||
)
|
||||
|
||||
# create the tap
|
||||
#
|
||||
# for some reason we should create the tap inteface first,
|
||||
# and only then bring it up, thus two commit() calls
|
||||
(
|
||||
ndb.interfaces.create(
|
||||
kind='tuntap', ifname=tap_name, mode='tap'
|
||||
)
|
||||
.commit()
|
||||
.set('state', 'up')
|
||||
.commit()
|
||||
)
|
||||
if args.verbose:
|
||||
print(f'link: add main/{tap_name}')
|
||||
if args.ipv4:
|
||||
subprocess.call(["sysctl", "-w", "net.ipv4.conf."+brige_name+".forwarding=1"])
|
||||
if args.ipv6:
|
||||
subprocess.call(["sysctl", "-w", "net.ipv6.conf."+brige_name+".forwarding=1"])
|
||||
|
||||
# create the bridge and add all the ports
|
||||
with ndb.interfaces.create(
|
||||
ifname=bridge_name, kind='bridge', state='up'
|
||||
) as i:
|
||||
if args.ipv4:
|
||||
i.add_ip(bripv4)
|
||||
if args.ipv6:
|
||||
i.add_ip(bripv6)
|
||||
for iface in veth_names + [tap_name]:
|
||||
i.add_port(iface)
|
||||
if args.verbose:
|
||||
print(f'link: add main/{bridge_name}, {bripv4}, {bripv6}')
|
||||
|
||||
with open(os.devnull, 'w') as fnull:
|
||||
if args.ipv4:
|
||||
subprocess.call(
|
||||
[
|
||||
'sysctl',
|
||||
'-w',
|
||||
f'net.ipv4.conf.{bridge_name}.forwarding=1',
|
||||
],
|
||||
stdout=fnull,
|
||||
)
|
||||
if args.verbose:
|
||||
print(f'sysctl: set {bridge_name} ipv4 forwarding')
|
||||
if args.ipv6:
|
||||
subprocess.call(
|
||||
[
|
||||
'sysctl',
|
||||
'-w',
|
||||
f'net.ipv6.conf.{bridge_name}.forwarding=1',
|
||||
],
|
||||
stdout=fnull,
|
||||
)
|
||||
if args.verbose:
|
||||
print(f'sysctl: set {bridge_name} ipv4 forwarding')
|
||||
|
||||
if args.debug:
|
||||
fname = 'main-ndb.db'
|
||||
print('dump: the main netns topology database')
|
||||
ndb.schema.backup(fname)
|
||||
except Exception as e:
|
||||
print('Error',e)
|
||||
finally:
|
||||
if ip:
|
||||
ip.release()
|
||||
+66
-71
@@ -1194,20 +1194,15 @@ Dht::cancelPut(const InfoHash& id, const Value::Id& vid)
|
||||
};
|
||||
canceled |= sr_cancel_put(dht4.searches);
|
||||
canceled |= sr_cancel_put(dht6.searches);
|
||||
if (canceled) {
|
||||
auto st = store.find(id);
|
||||
if (st != store.end()) {
|
||||
if (auto value = st->second.remove(id, vid))
|
||||
storageRemoved(id, st->second, {value}, value->size());
|
||||
}
|
||||
}
|
||||
if (canceled)
|
||||
storageErase(id, vid);
|
||||
return canceled;
|
||||
}
|
||||
|
||||
// Storage
|
||||
|
||||
void
|
||||
Dht::storageChanged(const InfoHash& id, Storage& st, const Sp<Value>& v, bool newValue)
|
||||
Dht::storageChanged(const InfoHash& id, Storage& st, ValueStorage& v, bool newValue)
|
||||
{
|
||||
if (newValue) {
|
||||
if (not st.local_listeners.empty()) {
|
||||
@@ -1217,8 +1212,8 @@ Dht::storageChanged(const InfoHash& id, Storage& st, const Sp<Value>& v, bool ne
|
||||
cbs.reserve(st.local_listeners.size());
|
||||
for (const auto& l : st.local_listeners) {
|
||||
std::vector<Sp<Value>> vals;
|
||||
if (not l.second.filter or l.second.filter(*v))
|
||||
vals.push_back(v);
|
||||
if (not l.second.filter or l.second.filter(*v.data))
|
||||
vals.push_back(v.data);
|
||||
if (not vals.empty()) {
|
||||
if (logger_)
|
||||
logger_->d(id, "[store %s] sending update local listener with token %lu",
|
||||
@@ -1239,14 +1234,14 @@ Dht::storageChanged(const InfoHash& id, Storage& st, const Sp<Value>& v, bool ne
|
||||
for (const auto& node_listeners : st.listeners) {
|
||||
for (const auto& l : node_listeners.second) {
|
||||
auto f = l.second.query.where.getFilter();
|
||||
if (f and not f(*v))
|
||||
if (f and not f(*v.data))
|
||||
continue;
|
||||
if (logger_)
|
||||
logger_->w(id, node_listeners.first->id, "[store %s] [node %s] sending update",
|
||||
id.toString().c_str(),
|
||||
node_listeners.first->toString().c_str());
|
||||
std::vector<Sp<Value>> vals {};
|
||||
vals.push_back(v);
|
||||
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);
|
||||
@@ -1287,18 +1282,26 @@ Dht::storageStore(const InfoHash& id, const Sp<Value>& value, time_point created
|
||||
vs->expiration_job = scheduler.add(expiration, std::bind(&Dht::expireStorage, this, id));
|
||||
}
|
||||
if (total_store_size > max_store_size) {
|
||||
auto value = vs->data;
|
||||
auto value_diff = store.second.values_diff;
|
||||
expireStore();
|
||||
storageChanged(id, st->second, value, value_diff > 0);
|
||||
} else {
|
||||
storageChanged(id, st->second, vs->data, store.second.values_diff > 0);
|
||||
}
|
||||
storageChanged(id, st->second, *vs, store.second.values_diff > 0);
|
||||
}
|
||||
|
||||
return std::get<0>(store);
|
||||
}
|
||||
|
||||
bool
|
||||
Dht::storageErase(const InfoHash& id, Value::Id vid)
|
||||
{
|
||||
auto st = store.find(id);
|
||||
if (st == store.end())
|
||||
return false;
|
||||
auto ret = st->second.remove(id, vid);
|
||||
total_store_size += ret.size_diff;
|
||||
total_values += ret.values_diff;
|
||||
return ret.values_diff;
|
||||
}
|
||||
|
||||
void
|
||||
Dht::storageAddListener(const InfoHash& id, const Sp<Node>& node, size_t socket_id, Query&& query, int version)
|
||||
{
|
||||
@@ -1330,8 +1333,36 @@ Dht::expireStore(decltype(store)::iterator i)
|
||||
const auto& id = i->first;
|
||||
auto& st = i->second;
|
||||
auto stats = st.expire(id, scheduler.time());
|
||||
total_store_size += stats.first;
|
||||
total_values -= stats.second.size();
|
||||
if (not stats.second.empty()) {
|
||||
storageRemoved(id, st, stats.second, -stats.first);
|
||||
if (logger_)
|
||||
logger_->d(id, "[store %s] discarded %ld expired values (%ld bytes)",
|
||||
id.toString().c_str(), stats.second.size(), -stats.first);
|
||||
|
||||
if (not st.listeners.empty()) {
|
||||
if (logger_)
|
||||
logger_->d(id, "[store %s] %lu remote listeners", id.toString().c_str(), st.listeners.size());
|
||||
|
||||
std::vector<Value::Id> ids;
|
||||
ids.reserve(stats.second.size());
|
||||
for (const auto& v : stats.second)
|
||||
ids.emplace_back(v->id);
|
||||
|
||||
for (const auto& node_listeners : st.listeners) {
|
||||
for (const auto& l : node_listeners.second) {
|
||||
if (logger_)
|
||||
logger_->w(id, node_listeners.first->id, "[store %s] [node %s] sending expired",
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto& local_listeners : st.local_listeners) {
|
||||
local_listeners.second.get_cb(stats.second, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1343,41 +1374,6 @@ Dht::expireStorage(InfoHash h)
|
||||
expireStore(i);
|
||||
}
|
||||
|
||||
void
|
||||
Dht::storageRemoved(const InfoHash& id, Storage& st, const std::vector<Sp<Value>>& values, size_t totalSize)
|
||||
{
|
||||
if (logger_)
|
||||
logger_->d(id, "[store %s] discarded %ld values (%ld bytes)",
|
||||
id.toString().c_str(), values.size(), totalSize);
|
||||
|
||||
total_store_size -= totalSize;
|
||||
total_values -= values.size();
|
||||
|
||||
if (not st.listeners.empty()) {
|
||||
if (logger_)
|
||||
logger_->d(id, "[store %s] %lu remote listeners", id.toString().c_str(), st.listeners.size());
|
||||
|
||||
std::vector<Value::Id> ids;
|
||||
ids.reserve(values.size());
|
||||
for (const auto& v : values)
|
||||
ids.emplace_back(v->id);
|
||||
|
||||
for (const auto& node_listeners : st.listeners) {
|
||||
for (const auto& l : node_listeners.second) {
|
||||
if (logger_)
|
||||
logger_->w(id, node_listeners.first->id, "[store %s] [node %s] sending expired",
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto& local_listeners : st.local_listeners) {
|
||||
local_listeners.second.get_cb(values, true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Dht::expireStore()
|
||||
{
|
||||
@@ -1403,23 +1399,23 @@ Dht::expireStore()
|
||||
break;
|
||||
}
|
||||
auto largest = store_quota.begin();
|
||||
for (auto it = std::next(largest); it != store_quota.end(); ++it) {
|
||||
for (auto it = ++largest; it != store_quota.end(); ++it) {
|
||||
if (it->second.size() > largest->second.size())
|
||||
largest = it;
|
||||
}
|
||||
if (largest != store_quota.end()) {
|
||||
while (true) {
|
||||
auto exp_value = largest->second.getOldest();
|
||||
auto storage = store.find(exp_value.first);
|
||||
if (storage != store.end()) {
|
||||
if (logger_)
|
||||
logger_->w("Storage quota full: discarding value from %s at %s %016" PRIx64, largest->first.toString().c_str(), exp_value.first.to_c_str(), exp_value.second);
|
||||
|
||||
if (auto value = storage->second.remove(exp_value.first, exp_value.second)) {
|
||||
storageRemoved(storage->first, storage->second, {value}, value->size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (logger_)
|
||||
logger_->w("No space left: discarding value of largest consumer %s", largest->first.toString().c_str());
|
||||
while (true) {
|
||||
auto exp_value = largest->second.getOldest();
|
||||
auto storage = store.find(exp_value.first);
|
||||
if (storage != store.end()) {
|
||||
auto ret = storage->second.remove(exp_value.first, exp_value.second);
|
||||
total_store_size += ret.size_diff;
|
||||
total_values += ret.values_diff;
|
||||
if (logger_)
|
||||
logger_->w("Discarded %ld bytes, still %ld used", largest->first.toString().c_str(), total_store_size);
|
||||
if (ret.values_diff)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1668,7 +1664,7 @@ Dht::dumpTables() const
|
||||
std::string
|
||||
Dht::getStorageLog() const
|
||||
{
|
||||
std::ostringstream out;
|
||||
std::stringstream out;
|
||||
for (const auto& s : store)
|
||||
out << printStorageLog(s);
|
||||
out << std::endl << std::endl;
|
||||
@@ -1793,8 +1789,7 @@ Dht::Dht(std::unique_ptr<net::DatagramSocket>&& sock, const Config& config, cons
|
||||
myid(config.node_id ? config.node_id : InfoHash::getRandom(rd)),
|
||||
store(),
|
||||
store_quota(),
|
||||
max_store_keys(config.max_store_keys ? (int)config.max_store_keys : MAX_HASHES),
|
||||
max_store_size(config.max_store_size ? (int)config.max_store_size : DEFAULT_STORAGE_LIMIT),
|
||||
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),
|
||||
network_engine(myid, fromDhtConfig(config), std::move(sock), logger_, rd, scheduler,
|
||||
std::bind(&Dht::onError, this, _1, _2),
|
||||
@@ -2563,7 +2558,7 @@ Dht::storageRefresh(const InfoHash& id, Value::Id vid)
|
||||
}
|
||||
}
|
||||
|
||||
auto expiration = s->second.refresh(id, now, vid, types);
|
||||
auto expiration = s->second.refresh(now, vid, types);
|
||||
if (expiration.first) {
|
||||
scheduler.cancel(expiration.first->expiration_job);
|
||||
if (expiration.second != time_point::max()) {
|
||||
|
||||
+190
-66
@@ -38,6 +38,15 @@ namespace dht {
|
||||
|
||||
static const std::string PEER_DISCOVERY_DHT_SERVICE = "dht";
|
||||
|
||||
struct DhtRunner::Listener {
|
||||
size_t tokenClassicDht {0};
|
||||
size_t tokenProxyDht {0};
|
||||
ValueCallback gcb;
|
||||
InfoHash hash {};
|
||||
Value::Filter f;
|
||||
Where w;
|
||||
};
|
||||
|
||||
struct NodeInsertionPack {
|
||||
dht::InfoHash nodeId;
|
||||
in_port_t port;
|
||||
@@ -46,6 +55,9 @@ struct NodeInsertionPack {
|
||||
};
|
||||
|
||||
DhtRunner::DhtRunner() : dht_()
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
, dht_via_proxy_()
|
||||
#endif //OPENDHT_PROXY_CLIENT
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSADATA wsd;
|
||||
@@ -107,9 +119,8 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
local6.setFamily(AF_INET6);
|
||||
}
|
||||
auto state_path = config.dht_config.node_config.persist_path;
|
||||
if (not state_path.empty())
|
||||
state_path += "_port.txt";
|
||||
if (not state_path.empty() && (local4.getPort() == 0 || local6.getPort() == 0)) {
|
||||
state_path += "_port.txt";
|
||||
std::ifstream inConfig(state_path);
|
||||
if (inConfig.is_open()) {
|
||||
in_port_t port;
|
||||
@@ -138,7 +149,7 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
context.sock.reset(new net::UdpSocket(local4, local6, context.logger));
|
||||
}
|
||||
|
||||
if (not state_path.empty() and config.proxy_server.empty()) {
|
||||
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;
|
||||
@@ -152,15 +163,14 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
net::PacketList ret;
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(sock_mtx);
|
||||
rcv.splice(rcv.end(), std::move(pkts));
|
||||
size_t dropped = 0;
|
||||
while (rcv.size() > net::RX_QUEUE_MAX_SIZE) {
|
||||
auto maxSize = net::RX_QUEUE_MAX_SIZE - pkts.size();
|
||||
while (rcv.size() > maxSize) {
|
||||
if (logger_)
|
||||
logger_->e("Dropping packet: queue is full!");
|
||||
rcv.pop_front();
|
||||
dropped++;
|
||||
}
|
||||
if (dropped and logger_) {
|
||||
logger_->w("[runner %p] dropped %zu packets: queue is full!", this, dropped);
|
||||
}
|
||||
|
||||
rcv.splice(rcv.end(), std::move(pkts));
|
||||
ret = std::move(rcv_free);
|
||||
}
|
||||
cv.notify_all();
|
||||
@@ -171,25 +181,30 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
config_ = config;
|
||||
identityAnnouncedCb_ = context.identityAnnouncedCb;
|
||||
#endif
|
||||
if (config.proxy_server.empty()) {
|
||||
auto dht = std::make_unique<Dht>(std::move(context.sock), SecureDht::getConfig(config.dht_config), context.logger);
|
||||
dht_ = std::make_unique<SecureDht>(std::move(dht), config.dht_config, std::move(context.identityAnnouncedCb), context.logger);
|
||||
} else {
|
||||
enableProxy(true);
|
||||
}
|
||||
auto dht = std::make_unique<Dht>(std::move(context.sock), SecureDht::getConfig(config.dht_config), context.logger);
|
||||
dht_ = std::make_unique<SecureDht>(std::move(dht), config.dht_config, std::move(context.identityAnnouncedCb), context.logger);
|
||||
enableProxy(not config.proxy_server.empty());
|
||||
} catch(const std::exception& e) {
|
||||
config_ = {};
|
||||
identityAnnouncedCb_ = {};
|
||||
dht_.reset();
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
dht_via_proxy_.reset();
|
||||
#endif
|
||||
running = State::Idle;
|
||||
throw;
|
||||
}
|
||||
|
||||
if (context.logger and dht_via_proxy_) {
|
||||
dht_via_proxy_->setLogger(context.logger);
|
||||
}
|
||||
if (context.statusChangedCallback) {
|
||||
statusCb = std::move(context.statusChangedCallback);
|
||||
}
|
||||
if (context.certificateStore) {
|
||||
dht_->setLocalCertificateStore(std::move(context.certificateStore));
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->setLocalCertificateStore(std::move(context.certificateStore));
|
||||
}
|
||||
|
||||
if (not config.threaded)
|
||||
@@ -272,8 +287,7 @@ DhtRunner::shutdown(ShutdownCallback cb, bool stop) {
|
||||
auto expected = State::Running;
|
||||
if (not running.compare_exchange_strong(expected, State::Stopping)) {
|
||||
if (expected == State::Stopping and ongoing_ops) {
|
||||
if (cb)
|
||||
shutdownCallbacks_.emplace_back(std::move(cb));
|
||||
shutdownCallbacks_.emplace_back(std::move(cb));
|
||||
}
|
||||
else if (cb) {
|
||||
lck.unlock();
|
||||
@@ -287,10 +301,12 @@ DhtRunner::shutdown(ShutdownCallback cb, bool stop) {
|
||||
shutdownCallbacks_.emplace_back(std::move(cb));
|
||||
pending_ops.emplace([=](SecureDht&) mutable {
|
||||
auto onShutdown = [this]{ opEnded(); };
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->shutdown(onShutdown, stop);
|
||||
#endif
|
||||
if (dht_)
|
||||
dht_->shutdown(onShutdown, stop);
|
||||
else
|
||||
opEnded();
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
@@ -393,25 +409,31 @@ void
|
||||
DhtRunner::dumpTables() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
dht_->dumpTables();
|
||||
activeDht()->dumpTables();
|
||||
}
|
||||
|
||||
InfoHash
|
||||
DhtRunner::getId() const
|
||||
{
|
||||
return dht_ ? dht_->getId() : InfoHash{};
|
||||
if (auto dht = activeDht())
|
||||
return dht->getId();
|
||||
return {};
|
||||
}
|
||||
|
||||
std::shared_ptr<crypto::PublicKey>
|
||||
DhtRunner::getPublicKey() const
|
||||
{
|
||||
return dht_ ? dht_->getPublicKey() : std::shared_ptr<crypto::PublicKey>{};
|
||||
if (auto dht = activeDht())
|
||||
return dht->getPublicKey();
|
||||
return {};
|
||||
}
|
||||
|
||||
InfoHash
|
||||
DhtRunner::getNodeId() const
|
||||
{
|
||||
return dht_ ? dht_->getNodeId() : InfoHash{};
|
||||
if (auto dht = activeDht())
|
||||
return dht->getNodeId();
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -453,6 +475,10 @@ DhtRunner::setLogger(const Sp<Logger>& logger) {
|
||||
logger_ = logger;
|
||||
if (dht_)
|
||||
dht_->setLogger(logger);
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->setLogger(logger);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@@ -466,12 +492,16 @@ DhtRunner::setLogFilter(const InfoHash& f) {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
if (dht_)
|
||||
dht_->setLogFilter(f);
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->setLogFilter(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::registerType(const ValueType& type) {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
dht_->registerType(type);
|
||||
activeDht()->registerType(type);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -484,7 +514,7 @@ unsigned
|
||||
DhtRunner::getNodesStats(sa_family_t af, unsigned *good_return, unsigned *dubious_return, unsigned *cached_return, unsigned *incoming_return) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
const auto stats = dht_->getNodesStats(af);
|
||||
const auto stats = activeDht()->getNodesStats(af);
|
||||
if (good_return)
|
||||
*good_return = stats.good_nodes;
|
||||
if (dubious_return)
|
||||
@@ -500,19 +530,19 @@ NodeStats
|
||||
DhtRunner::getNodesStats(sa_family_t af) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
return dht_->getNodesStats(af);
|
||||
return activeDht()->getNodesStats(af);
|
||||
}
|
||||
|
||||
NodeInfo
|
||||
DhtRunner::getNodeInfo() const {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
NodeInfo info {};
|
||||
if (dht_) {
|
||||
info.id = dht_->getId();
|
||||
info.node_id = dht_->getNodeId();
|
||||
info.ipv4 = dht_->getNodesStats(AF_INET);
|
||||
info.ipv6 = dht_->getNodesStats(AF_INET6);
|
||||
if (auto sock = dht_->getSocket()) {
|
||||
if (auto dht = activeDht()) {
|
||||
info.id = dht->getId();
|
||||
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();
|
||||
}
|
||||
@@ -533,7 +563,6 @@ 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);
|
||||
std::tie(info.storage_size, info.storage_values) = dht.getStoreSize();
|
||||
if (auto sock = dht.getSocket()) {
|
||||
info.bound4 = sock->getBoundRef(AF_INET).getPort();
|
||||
info.bound6 = sock->getBoundRef(AF_INET6).getPort();
|
||||
@@ -549,45 +578,45 @@ std::vector<unsigned>
|
||||
DhtRunner::getNodeMessageStats(bool in) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
return dht_->getNodeMessageStats(in);
|
||||
return activeDht()->getNodeMessageStats(in);
|
||||
}
|
||||
|
||||
std::string
|
||||
DhtRunner::getStorageLog() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
return dht_->getStorageLog();
|
||||
return activeDht()->getStorageLog();
|
||||
}
|
||||
std::string
|
||||
DhtRunner::getStorageLog(const InfoHash& f) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
return dht_->getStorageLog(f);
|
||||
return activeDht()->getStorageLog(f);
|
||||
}
|
||||
std::string
|
||||
DhtRunner::getRoutingTablesLog(sa_family_t af) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
return dht_->getRoutingTablesLog(af);
|
||||
return activeDht()->getRoutingTablesLog(af);
|
||||
}
|
||||
std::string
|
||||
DhtRunner::getSearchesLog(sa_family_t af) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
return dht_->getSearchesLog(af);
|
||||
return activeDht()->getSearchesLog(af);
|
||||
}
|
||||
std::string
|
||||
DhtRunner::getSearchLog(const InfoHash& f, sa_family_t af) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
return dht_->getSearchLog(f, af);
|
||||
return activeDht()->getSearchLog(f, af);
|
||||
}
|
||||
std::vector<SockAddr>
|
||||
DhtRunner::getPublicAddress(sa_family_t af)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
if (dht_)
|
||||
return dht_->getPublicAddress(af);
|
||||
if (auto dht = activeDht())
|
||||
return dht->getPublicAddress(af);
|
||||
return {};
|
||||
}
|
||||
std::vector<std::string>
|
||||
@@ -614,12 +643,15 @@ DhtRunner::getPublicAddress(std::function<void(std::vector<SockAddr>&&)> cb, sa_
|
||||
void
|
||||
DhtRunner::registerCertificate(std::shared_ptr<crypto::Certificate> cert) {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
dht_->registerCertificate(cert);
|
||||
activeDht()->registerCertificate(cert);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::setLocalCertificateStore(CertificateStoreQuery&& query_method) {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->setLocalCertificateStore(std::forward<CertificateStoreQuery>(query_method));
|
||||
#endif
|
||||
if (dht_)
|
||||
dht_->setLocalCertificateStore(std::forward<CertificateStoreQuery>(query_method));
|
||||
}
|
||||
@@ -627,7 +659,8 @@ DhtRunner::setLocalCertificateStore(CertificateStoreQuery&& query_method) {
|
||||
time_point
|
||||
DhtRunner::loop_()
|
||||
{
|
||||
if (not dht_)
|
||||
auto dht = activeDht();
|
||||
if (not dht)
|
||||
return {};
|
||||
|
||||
decltype(pending_ops) ops {};
|
||||
@@ -638,7 +671,7 @@ DhtRunner::loop_()
|
||||
std::move(pending_ops) : std::move(pending_ops_prio);
|
||||
}
|
||||
while (not ops.empty()) {
|
||||
ops.front()(*dht_);
|
||||
ops.front()(*dht);
|
||||
ops.pop();
|
||||
}
|
||||
|
||||
@@ -671,13 +704,13 @@ DhtRunner::loop_()
|
||||
if (now - pkt.received > net::RX_QUEUE_MAX_DELAY)
|
||||
dropped++;
|
||||
else
|
||||
wakeup = dht_->periodic(pkt.data.data(), pkt.data.size(), std::move(pkt.from), now);
|
||||
wakeup = dht->periodic(pkt.data.data(), pkt.data.size(), std::move(pkt.from), now);
|
||||
pkt.data.clear();
|
||||
}
|
||||
received_treated.splice(received_treated.end(), std::move(received));
|
||||
} else {
|
||||
// Or just run the scheduler
|
||||
wakeup = dht_->periodic(nullptr, 0, nullptr, 0, clock::now());
|
||||
wakeup = dht->periodic(nullptr, 0, nullptr, 0, clock::now());
|
||||
}
|
||||
|
||||
if (not received_treated.empty()) {
|
||||
@@ -689,8 +722,8 @@ DhtRunner::loop_()
|
||||
if (dropped && logger_)
|
||||
logger_->e("[runner %p] Dropped %zu packets with high delay.", this, dropped);
|
||||
|
||||
NodeStatus nstatus4 = dht_->updateStatus(AF_INET);
|
||||
NodeStatus nstatus6 = dht_->updateStatus(AF_INET6);
|
||||
NodeStatus nstatus4 = dht->updateStatus(AF_INET);
|
||||
NodeStatus nstatus6 = dht->updateStatus(AF_INET6);
|
||||
if (nstatus4 != status4 || nstatus6 != status6) {
|
||||
status4 = nstatus4;
|
||||
status6 = nstatus6;
|
||||
@@ -748,7 +781,27 @@ DhtRunner::listen(InfoHash hash, ValueCallback vcb, Value::Filter f, Where w)
|
||||
return ret_token->get_future();
|
||||
}
|
||||
pending_ops.emplace([=](SecureDht& dht) mutable {
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
auto tokenbGlobal = listener_token_++;
|
||||
auto& listener = listeners_[tokenbGlobal];
|
||||
listener.hash = hash;
|
||||
listener.f = std::move(f);
|
||||
listener.w = std::move(w);
|
||||
listener.gcb = [hash,vcb,tokenbGlobal,this](const std::vector<Sp<Value>>& vals, bool expired) {
|
||||
if (not vcb(vals, expired)) {
|
||||
cancelListen(hash, tokenbGlobal);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
if (auto token = dht.listen(hash, listener.gcb, listener.f, listener.w)) {
|
||||
if (use_proxy) listener.tokenProxyDht = token;
|
||||
else listener.tokenClassicDht = token;
|
||||
}
|
||||
ret_token->set_value(tokenbGlobal);
|
||||
#else
|
||||
ret_token->set_value(dht.listen(hash, std::move(vcb), std::move(f), std::move(w)));
|
||||
#endif
|
||||
});
|
||||
cv.notify_all();
|
||||
return ret_token->get_future();
|
||||
@@ -767,10 +820,23 @@ DhtRunner::cancelListen(InfoHash h, size_t token)
|
||||
if (running != State::Running)
|
||||
return;
|
||||
ongoing_ops++;
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
pending_ops.emplace([=](SecureDht&) {
|
||||
auto it = listeners_.find(token);
|
||||
if (it == listeners_.end()) return;
|
||||
if (it->second.tokenClassicDht)
|
||||
dht_->cancelListen(h, it->second.tokenClassicDht);
|
||||
if (it->second.tokenProxyDht and dht_via_proxy_)
|
||||
dht_via_proxy_->cancelListen(h, it->second.tokenProxyDht);
|
||||
listeners_.erase(it);
|
||||
opEnded();
|
||||
});
|
||||
#else
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.cancelListen(h, token);
|
||||
opEnded();
|
||||
});
|
||||
#endif // OPENDHT_PROXY_CLIENT
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
@@ -781,10 +847,23 @@ DhtRunner::cancelListen(InfoHash h, std::shared_future<size_t> ftoken)
|
||||
if (running != State::Running)
|
||||
return;
|
||||
ongoing_ops++;
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
pending_ops.emplace([this, h, ftoken = std::move(ftoken)](SecureDht&) {
|
||||
auto it = listeners_.find(ftoken.get());
|
||||
if (it == listeners_.end()) return;
|
||||
if (it->second.tokenClassicDht)
|
||||
dht_->cancelListen(h, it->second.tokenClassicDht);
|
||||
if (it->second.tokenProxyDht and dht_via_proxy_)
|
||||
dht_via_proxy_->cancelListen(h, it->second.tokenProxyDht);
|
||||
listeners_.erase(it);
|
||||
opEnded();
|
||||
});
|
||||
#else
|
||||
pending_ops.emplace([this, h, ftoken = std::move(ftoken)](SecureDht& dht) {
|
||||
dht.cancelListen(h, ftoken.get());
|
||||
opEnded();
|
||||
});
|
||||
#endif // OPENDHT_PROXY_CLIENT
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
@@ -1073,9 +1152,23 @@ void
|
||||
DhtRunner::resetDht()
|
||||
{
|
||||
peerDiscovery_.reset();
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
listeners_.clear();
|
||||
dht_via_proxy_.reset();
|
||||
#endif // OPENDHT_PROXY_CLIENT
|
||||
dht_.reset();
|
||||
}
|
||||
|
||||
SecureDht*
|
||||
DhtRunner::activeDht() const
|
||||
{
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
return use_proxy? dht_via_proxy_.get() : dht_.get();
|
||||
#else
|
||||
return dht_.get();
|
||||
#endif // OPENDHT_PROXY_CLIENT
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::setProxyServer(const std::string& proxy, const std::string& pushNodeId)
|
||||
{
|
||||
@@ -1088,7 +1181,7 @@ DhtRunner::setProxyServer(const std::string& proxy, const std::string& pushNodeI
|
||||
enableProxy(use_proxy and not config_.proxy_server.empty());
|
||||
#else
|
||||
if (not proxy.empty())
|
||||
throw std::runtime_error("DHT proxy requested but OpenDHT built without proxy support.");
|
||||
std::cerr << "DHT proxy requested but OpenDHT built without proxy support." << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1096,33 +1189,54 @@ void
|
||||
DhtRunner::enableProxy(bool proxify)
|
||||
{
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
if (dht_) {
|
||||
dht_->shutdown({});
|
||||
if (dht_via_proxy_) {
|
||||
dht_via_proxy_->shutdown({});
|
||||
}
|
||||
if (proxify) {
|
||||
// Init the proxy client
|
||||
auto dht_via_proxy = std::make_unique<DhtProxyClient>(
|
||||
auto dht_via_proxy = std::unique_ptr<DhtInterface>(
|
||||
new DhtProxyClient(
|
||||
config_.server_ca,
|
||||
config_.client_identity,
|
||||
[this]{
|
||||
if (config_.threaded) {
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht&) mutable {});
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht&) mutable {});
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
},
|
||||
config_.proxy_server, config_.push_node_id, logger_);
|
||||
config_.proxy_server, config_.push_node_id, logger_)
|
||||
);
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
if (not config_.push_token.empty())
|
||||
dht_via_proxy->setPushNotificationToken(config_.push_token);
|
||||
dht_ = std::make_unique<SecureDht>(std::move(dht_via_proxy), config_.dht_config, identityAnnouncedCb_, logger_);
|
||||
#endif
|
||||
dht_via_proxy_ = std::unique_ptr<SecureDht>(new SecureDht(std::move(dht_via_proxy), config_.dht_config, identityAnnouncedCb_, logger_));
|
||||
// add current listeners
|
||||
for (auto& l: listeners_)
|
||||
l.second.tokenProxyDht = dht_via_proxy_->listen(l.second.hash, l.second.gcb, l.second.f, l.second.w);
|
||||
// and use it
|
||||
use_proxy = proxify;
|
||||
} else {
|
||||
use_proxy = proxify;
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
if (not listeners_.empty()) {
|
||||
pending_ops.emplace([this](SecureDht& /*dht*/) mutable {
|
||||
if (not dht_)
|
||||
return;
|
||||
for (auto& l : listeners_) {
|
||||
if (not l.second.tokenClassicDht) {
|
||||
l.second.tokenClassicDht = dht_->listen(l.second.hash, l.second.gcb, l.second.f, l.second.w);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (proxify)
|
||||
throw std::runtime_error("DHT proxy requested but OpenDHT built without proxy support.");
|
||||
std::cerr << "DHT proxy requested but OpenDHT built without proxy support." << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1130,8 +1244,16 @@ void
|
||||
DhtRunner::forwardAllMessages(bool forward)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
#ifdef OPENDHT_PROXY_SERVER
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->forwardAllMessages(forward);
|
||||
#endif // OPENDHT_PROXY_CLIENT
|
||||
if (dht_)
|
||||
dht_->forwardAllMessages(forward);
|
||||
#else
|
||||
(void) forward;
|
||||
#endif // OPENDHT_PROXY_SERVER
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1142,8 +1264,8 @@ DhtRunner::setPushNotificationToken(const std::string& token) {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
#if defined(OPENDHT_PROXY_CLIENT) && defined(OPENDHT_PUSH_NOTIFICATIONS)
|
||||
config_.push_token = token;
|
||||
if (dht_)
|
||||
dht_->setPushNotificationToken(token);
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->setPushNotificationToken(token);
|
||||
#else
|
||||
(void) token;
|
||||
#endif
|
||||
@@ -1153,11 +1275,13 @@ void
|
||||
DhtRunner::pushNotificationReceived(const std::map<std::string, std::string>& data)
|
||||
{
|
||||
#if defined(OPENDHT_PROXY_CLIENT) && defined(OPENDHT_PUSH_NOTIFICATIONS)
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht&) {
|
||||
if (dht_)
|
||||
dht_->pushNotificationReceived(data);
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht&) {
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->pushNotificationReceived(data);
|
||||
});
|
||||
}
|
||||
cv.notify_all();
|
||||
#else
|
||||
(void) data;
|
||||
|
||||
@@ -415,7 +415,7 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, SockAddr f)
|
||||
return;
|
||||
}
|
||||
|
||||
auto msg = std::make_unique<ParsedMessage>();
|
||||
std::unique_ptr<ParsedMessage> msg {new ParsedMessage};
|
||||
try {
|
||||
msgpack::unpacked msg_res = msgpack::unpack((const char*)buf, buflen);
|
||||
msg->msgpack_unpack(msg_res.get());
|
||||
@@ -1155,12 +1155,11 @@ NetworkEngine::sendAnnounceValue(const Sp<Node>& n,
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(config.network?1:0));
|
||||
|
||||
bool add_created = created < scheduler.time();
|
||||
pk.pack(KEY_A); pk.pack_map(add_created ? 5 : 4);
|
||||
pk.pack(KEY_A); pk.pack_map((created < scheduler.time() ? 5 : 4));
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
pk.pack(KEY_REQ_H); pk.pack(infohash);
|
||||
auto v = packValueHeader(buffer, {value});
|
||||
if (add_created) {
|
||||
if (created < scheduler.time()) {
|
||||
pk.pack(KEY_REQ_CREATION);
|
||||
pk.pack(to_time_t(created));
|
||||
}
|
||||
|
||||
+43
-14
@@ -71,21 +71,37 @@ SecureDht::~SecureDht() = default;
|
||||
ValueType
|
||||
SecureDht::secureType(ValueType&& type)
|
||||
{
|
||||
type.storePolicy = [type](InfoHash id, Sp<Value>& v, const InfoHash& nid, const SockAddr& a) {
|
||||
if (v->isSigned())
|
||||
return v->checkSignature();
|
||||
type.storePolicy = [this,type](InfoHash id, Sp<Value>& v, const InfoHash& nid, const SockAddr& a) {
|
||||
if (v->isSigned()) {
|
||||
if (!v->signatureChecked) {
|
||||
v->signatureChecked = true;
|
||||
v->signatureValid = v->owner and v->owner->checkSignature(v->getToSign(), v->signature);
|
||||
}
|
||||
if (!v->signatureValid) {
|
||||
if (logger_)
|
||||
logger_->w("Signature verification failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return type.storePolicy(id, v, nid, a);
|
||||
};
|
||||
type.editPolicy = [this,type](InfoHash id, const Sp<Value>& o, Sp<Value>& n, const InfoHash& nid, const SockAddr& a) {
|
||||
if (not o->isSigned())
|
||||
if (!o->isSigned())
|
||||
return type.editPolicy(id, o, n, nid, a);
|
||||
if (o->owner != n->owner or not n->isSigned()) {
|
||||
if (o->owner != n->owner) {
|
||||
if (logger_)
|
||||
logger_->w("Edition forbidden: owner changed.");
|
||||
return false;
|
||||
}
|
||||
if (!n->signatureChecked) {
|
||||
n->signatureChecked = true;
|
||||
n->signatureValid = o->owner and o->owner->checkSignature(n->getToSign(), n->signature);
|
||||
}
|
||||
if (!n->signatureValid) {
|
||||
if (logger_)
|
||||
logger_->w("Edition forbidden: signature verification failed.");
|
||||
return false;
|
||||
}
|
||||
if (not n->checkSignature())
|
||||
return false;
|
||||
if (o->seq == n->seq) {
|
||||
// If the data is exactly the same,
|
||||
// it can be reannounced, possibly by someone else.
|
||||
@@ -237,12 +253,19 @@ SecureDht::checkValue(const Sp<Value>& v)
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
if (v->decrypted) {
|
||||
return v->decryptedValue;
|
||||
}
|
||||
v->decrypted = true;
|
||||
try {
|
||||
if (auto decrypted_val = v->decrypt(*key_)) {
|
||||
if (decrypted_val->owner)
|
||||
nodesPubKeys_[decrypted_val->owner->getId()] = decrypted_val->owner;
|
||||
return decrypted_val;
|
||||
Value decrypted_val (decrypt(*v));
|
||||
if (decrypted_val.recipient == getId()) {
|
||||
if (decrypted_val.owner)
|
||||
nodesPubKeys_[decrypted_val.owner->getId()] = decrypted_val.owner;
|
||||
v->decryptedValue = std::make_shared<Value>(std::move(decrypted_val));
|
||||
return v->decryptedValue;
|
||||
}
|
||||
// Ignore values belonging to other people
|
||||
} catch (const std::exception& e) {
|
||||
if (logger_)
|
||||
logger_->w("Could not decrypt value %s : %s", v->toString().c_str(), e.what());
|
||||
@@ -250,11 +273,17 @@ SecureDht::checkValue(const Sp<Value>& v)
|
||||
}
|
||||
// Check signed values
|
||||
else if (v->isSigned()) {
|
||||
if (v->checkSignature()) {
|
||||
if (enableCache_ and v->owner)
|
||||
if (v->signatureChecked) {
|
||||
return v->signatureValid ? v : Sp<Value>{};
|
||||
}
|
||||
v->signatureChecked = true;
|
||||
if (v->owner and v->owner->checkSignature(v->getToSign(), v->signature)) {
|
||||
v->signatureValid = true;
|
||||
if (enableCache_)
|
||||
nodesPubKeys_[v->owner->getId()] = v->owner;
|
||||
return v;
|
||||
} else if (logger_)
|
||||
}
|
||||
else if (logger_)
|
||||
logger_->w("Signature verification failed for %s", v->toString().c_str());
|
||||
}
|
||||
// Forward normal values
|
||||
|
||||
+9
-27
@@ -37,32 +37,18 @@ public:
|
||||
storedValues_.emplace(expiration, std::pair<InfoHash, Value::Id>(id, value.id));
|
||||
}
|
||||
void erase(const InfoHash& id, const Value& value, time_point expiration) {
|
||||
totalSize_ -= value.size();
|
||||
auto range = storedValues_.equal_range(expiration);
|
||||
for (auto rit = range.first; rit != range.second;) {
|
||||
if (rit->second.first == id && rit->second.second == value.id) {
|
||||
totalSize_ -= value.size();
|
||||
storedValues_.erase(rit);
|
||||
return;
|
||||
break;
|
||||
} else
|
||||
++rit;
|
||||
}
|
||||
// printf("StorageBucket::erase can't find value %s %016" PRIx64 "\n", id.to_c_str(), value.id);
|
||||
}
|
||||
void refresh(const InfoHash& id, const Value& value, time_point old_expiration, time_point expiration) {
|
||||
auto range = storedValues_.equal_range(old_expiration);
|
||||
for (auto rit = range.first; rit != range.second;) {
|
||||
if (rit->second.first == id && rit->second.second == value.id) {
|
||||
storedValues_.erase(rit);
|
||||
storedValues_.emplace(expiration, std::pair<InfoHash, Value::Id>(id, value.id));
|
||||
return;
|
||||
} else
|
||||
++rit;
|
||||
}
|
||||
// printf("StorageBucket::refresh can't find value %s %016" PRIx64 "\n", id.to_c_str(), value.id);
|
||||
insert(id, value, expiration);
|
||||
}
|
||||
size_t size() const { return totalSize_; }
|
||||
std::pair<InfoHash, Value::Id> getOldest() const { return storedValues_.empty() ? std::pair<InfoHash, Value::Id>{} : storedValues_.begin()->second; }
|
||||
std::pair<InfoHash, Value::Id> getOldest() const { return storedValues_.begin()->second; }
|
||||
private:
|
||||
std::multimap<time_point, std::pair<InfoHash, Value::Id>> storedValues_;
|
||||
size_t totalSize_ {0};
|
||||
@@ -88,7 +74,7 @@ struct Storage {
|
||||
size_t listener_token {1};
|
||||
|
||||
/* The maximum number of values we store for a given hash. */
|
||||
static constexpr unsigned MAX_VALUES {64 * 1024};
|
||||
static constexpr unsigned MAX_VALUES {1024};
|
||||
|
||||
/**
|
||||
* Changes caused by an operation on the storage.
|
||||
@@ -158,14 +144,11 @@ struct Storage {
|
||||
* @return time of the next expiration, time_point::max() if no expiration
|
||||
*/
|
||||
std::pair<ValueStorage*, time_point>
|
||||
refresh(const InfoHash& id, const time_point& now, const Value::Id& vid, const TypeStore& types) {
|
||||
refresh(const time_point& now, const Value::Id& vid, const TypeStore& types) {
|
||||
for (auto& vs : values)
|
||||
if (vs.data->id == vid) {
|
||||
vs.created = now;
|
||||
auto oldExp = vs.expiration;
|
||||
vs.expiration = std::max(oldExp, now + types.getType(vs.data->type).expiration);
|
||||
if (vs.store_bucket)
|
||||
vs.store_bucket->refresh(id, *vs.data, oldExp, vs.expiration);
|
||||
vs.expiration = std::max(vs.expiration, now + types.getType(vs.data->type).expiration);
|
||||
return {&vs, vs.expiration};
|
||||
}
|
||||
return {nullptr, time_point::max()};
|
||||
@@ -177,7 +160,7 @@ struct Storage {
|
||||
local_listeners.erase(token);
|
||||
}
|
||||
|
||||
Sp<Value> remove(const InfoHash& id, Value::Id);
|
||||
StoreDiff remove(const InfoHash& id, Value::Id);
|
||||
|
||||
std::pair<ssize_t, std::vector<Sp<Value>>> expire(const InfoHash& id, time_point now);
|
||||
|
||||
@@ -246,7 +229,7 @@ Storage::store(const InfoHash& id, const Sp<Value>& value, time_point created, t
|
||||
return std::make_pair(nullptr, StoreDiff{});
|
||||
}
|
||||
|
||||
Sp<Value>
|
||||
Storage::StoreDiff
|
||||
Storage::remove(const InfoHash& id, Value::Id vid)
|
||||
{
|
||||
auto it = std::find_if (values.begin(), values.end(), [&](const ValueStorage& vr) {
|
||||
@@ -260,9 +243,8 @@ Storage::remove(const InfoHash& id, Value::Id vid)
|
||||
if (it->expiration_job)
|
||||
it->expiration_job->cancel();
|
||||
total_size -= size;
|
||||
auto value = it->data;
|
||||
values.erase(it);
|
||||
return value;
|
||||
return {-size, -1, 0};
|
||||
}
|
||||
|
||||
Storage::StoreDiff
|
||||
|
||||
+1
-1
@@ -106,7 +106,7 @@ print_addr(const sockaddr* sa, socklen_t slen)
|
||||
{
|
||||
char hbuf[NI_MAXHOST];
|
||||
char sbuf[NI_MAXSERV];
|
||||
std::ostringstream out;
|
||||
std::stringstream out;
|
||||
if (sa and slen and !getnameinfo(sa, slen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV)) {
|
||||
if (sa->sa_family == AF_INET6)
|
||||
out << "[" << hbuf << "]";
|
||||
|
||||
@@ -254,62 +254,6 @@ unpackId(const Json::Value& json, const std::string& key) {
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
Value::sign(const crypto::PrivateKey& key)
|
||||
{
|
||||
if (isEncrypted())
|
||||
throw DhtException("Can't sign encrypted data.");
|
||||
owner = key.getSharedPublicKey();
|
||||
signature = key.sign(getToSign());
|
||||
}
|
||||
|
||||
Value
|
||||
Value::encrypt(const crypto::PrivateKey& from, const crypto::PublicKey& to)
|
||||
{
|
||||
if (isEncrypted())
|
||||
throw DhtException("Data is already encrypted.");
|
||||
setRecipient(to.getId());
|
||||
sign(from);
|
||||
Value nv {id};
|
||||
nv.setCypher(to.encrypt(getToEncrypt()));
|
||||
return nv;
|
||||
}
|
||||
|
||||
bool
|
||||
Value::checkSignature()
|
||||
{
|
||||
if (!signatureChecked) {
|
||||
signatureChecked = true;
|
||||
if (isSigned()) {
|
||||
signatureValid = owner and owner->checkSignature(getToSign(), signature);
|
||||
} else {
|
||||
signatureValid = true;
|
||||
}
|
||||
}
|
||||
return signatureValid;
|
||||
}
|
||||
|
||||
Sp<Value>
|
||||
Value::decrypt(const crypto::PrivateKey& key)
|
||||
{
|
||||
if (not decrypted) {
|
||||
decrypted = true;
|
||||
if (isEncrypted()) {
|
||||
auto decryptedBlob = key.decrypt(cypher);
|
||||
auto msg = msgpack::unpack((const char*)decryptedBlob.data(), decryptedBlob.size());
|
||||
auto v = std::make_unique<Value>(id);
|
||||
v->msgpack_unpack_body(msg.get());
|
||||
if (v->recipient != key.getPublicKey().getId())
|
||||
throw crypto::DecryptError("Recipient mismatch");
|
||||
// Ignore values belonging to other people
|
||||
if (not v->owner or not v->owner->checkSignature(v->getToSign(), v->signature))
|
||||
throw crypto::DecryptError("Signature mismatch");
|
||||
decryptedValue = std::move(v);
|
||||
}
|
||||
}
|
||||
return decryptedValue;
|
||||
}
|
||||
|
||||
bool
|
||||
FieldValue::operator==(const FieldValue& vfd) const
|
||||
{
|
||||
|
||||
+20
-49
@@ -35,11 +35,9 @@ 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;
|
||||
config.dht_config.node_config.max_store_size = -1;
|
||||
config.dht_config.node_config.max_store_keys = -1;
|
||||
|
||||
node1.run(0, config);
|
||||
node2.run(0, config);
|
||||
node1.run(42222, config);
|
||||
node2.run(42232, config);
|
||||
node2.bootstrap(node1.getBound());
|
||||
}
|
||||
|
||||
@@ -56,17 +54,15 @@ DhtRunnerTester::tearDown() {
|
||||
node1.shutdown(shutdown);
|
||||
node2.shutdown(shutdown);
|
||||
std::unique_lock<std::mutex> lk(cv_m);
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]{ return done == 2u; }));
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 12s, [&]{ return done == 2; }));
|
||||
node1.join();
|
||||
node2.join();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunnerTester::testConstructors() {
|
||||
CPPUNIT_ASSERT(node1.getBoundPort());
|
||||
CPPUNIT_ASSERT_EQUAL(node1.getBoundPort(), node1.getBound().getPort());
|
||||
CPPUNIT_ASSERT(node2.getBoundPort());
|
||||
CPPUNIT_ASSERT_EQUAL(node2.getBoundPort(), node2.getBound().getPort());
|
||||
CPPUNIT_ASSERT(node1.getBoundPort() == 42222);
|
||||
CPPUNIT_ASSERT(node2.getBoundPort() == 42232);
|
||||
|
||||
dht::DhtRunner::Config config {};
|
||||
dht::DhtRunner::Context context {};
|
||||
@@ -95,53 +91,33 @@ DhtRunnerTester::testListen() {
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
std::atomic_uint valueCount(0);
|
||||
std::atomic_uint valueCounta(0);
|
||||
std::atomic_uint valueCountb(0);
|
||||
std::atomic_uint valueCountc(0);
|
||||
std::atomic_uint valueCountd(0);
|
||||
|
||||
unsigned putCount(0);
|
||||
unsigned putOkCount1(0);
|
||||
unsigned putOkCount2(0);
|
||||
unsigned putOkCount3(0);
|
||||
unsigned putOkCount(0);
|
||||
|
||||
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 = 2048;
|
||||
constexpr unsigned N = 256;
|
||||
constexpr unsigned SZ = 56 * 1024;
|
||||
|
||||
auto ftokena = node1.listen(a, [&](const std::vector<std::shared_ptr<dht::Value>>& values, bool expired) {
|
||||
if (expired)
|
||||
valueCounta -= values.size();
|
||||
else
|
||||
valueCounta += values.size();
|
||||
auto ftokena = node1.listen(a, [&](const std::shared_ptr<dht::Value>&) {
|
||||
valueCount++;
|
||||
return true;
|
||||
});
|
||||
|
||||
auto ftokenb = node1.listen(b, [&](const std::shared_ptr<dht::Value>&) {
|
||||
/*if (expired)
|
||||
valueCountb -= values.size();
|
||||
else
|
||||
valueCountb += values.size();*/
|
||||
valueCountb++;
|
||||
valueCount++;
|
||||
return false;
|
||||
});
|
||||
|
||||
auto ftokenc = node1.listen(c, [&](const std::vector<std::shared_ptr<dht::Value>>& values, bool expired) {
|
||||
if (expired)
|
||||
valueCountc -= values.size();
|
||||
else
|
||||
valueCountc += values.size();
|
||||
auto ftokenc = node1.listen(c, [&](const std::shared_ptr<dht::Value>&) {
|
||||
valueCount++;
|
||||
return true;
|
||||
});
|
||||
|
||||
auto ftokend = node1.listen(d, [&](const std::vector<std::shared_ptr<dht::Value>>& values, bool expired) {
|
||||
if (expired)
|
||||
valueCountd -= values.size();
|
||||
else
|
||||
valueCountd += values.size();
|
||||
auto ftokend = node1.listen(d, [&](const std::shared_ptr<dht::Value>&) {
|
||||
valueCount++;
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -154,21 +130,21 @@ DhtRunnerTester::testListen() {
|
||||
node2.put(a, dht::Value("v1"), [&](bool ok) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
putCount++;
|
||||
if (ok) putOkCount1++;
|
||||
if (ok) putOkCount++;
|
||||
cv.notify_all();
|
||||
});
|
||||
node2.put(b, dht::Value("v2"), [&](bool ok) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
putCount++;
|
||||
if (ok) putOkCount2++;
|
||||
if (ok) putOkCount++;
|
||||
cv.notify_all();
|
||||
});
|
||||
auto bigVal = std::make_shared<dht::Value>();
|
||||
bigVal->data = mtu;
|
||||
node2.put(c, std::move(bigVal), [&](bool ok) {
|
||||
node2.put(c, bigVal, [&](bool ok) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
putCount++;
|
||||
if (ok) putOkCount3++;
|
||||
if (ok) putOkCount++;
|
||||
cv.notify_all();
|
||||
});
|
||||
}
|
||||
@@ -176,9 +152,7 @@ DhtRunnerTester::testListen() {
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]{ return putCount == N * 3u; }));
|
||||
CPPUNIT_ASSERT_EQUAL(N, putOkCount1);
|
||||
CPPUNIT_ASSERT_EQUAL(N, putOkCount2);
|
||||
CPPUNIT_ASSERT_EQUAL(N, putOkCount3);
|
||||
CPPUNIT_ASSERT_EQUAL(N * 3u, putOkCount);
|
||||
}
|
||||
|
||||
CPPUNIT_ASSERT(ftokena.valid());
|
||||
@@ -194,10 +168,7 @@ DhtRunnerTester::testListen() {
|
||||
CPPUNIT_ASSERT(tokena);
|
||||
CPPUNIT_ASSERT(tokenc);
|
||||
CPPUNIT_ASSERT(tokend);
|
||||
CPPUNIT_ASSERT_EQUAL(N, valueCounta.load());
|
||||
CPPUNIT_ASSERT_EQUAL(1u, valueCountb.load());
|
||||
CPPUNIT_ASSERT_EQUAL(N, valueCountc.load());
|
||||
CPPUNIT_ASSERT_EQUAL(0u, valueCountd.load());
|
||||
CPPUNIT_ASSERT_EQUAL(N * 2u + 1u, valueCount.load());
|
||||
|
||||
node1.cancelListen(a, tokena);
|
||||
node1.cancelListen(b, std::move(ftokenb));
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
if (OPENDHT_SHARED)
|
||||
set (OPENDHT_LIBS opendht)
|
||||
set (OPENDHT_C_LIBS opendht-c)
|
||||
else ()
|
||||
set (OPENDHT_LIBS opendht-static)
|
||||
set (OPENDHT_C_LIBS opendht-c-static)
|
||||
if (MSVC)
|
||||
set (MSC_COMPAT_SOURCES ${MSC_COMPAT_DIR}/wingetopt.c)
|
||||
endif ()
|
||||
@@ -11,8 +9,8 @@ endif ()
|
||||
|
||||
function (configure_tool name extra_files)
|
||||
add_executable (${name} ${name}.cpp ${extra_files})
|
||||
add_dependencies(${name} ${OPENDHT_LIBS})
|
||||
target_link_libraries (${name} LINK_PUBLIC ${OPENDHT_LIBS} ${READLINE_LIBRARIES})
|
||||
target_link_libraries (${name} LINK_PUBLIC ${READLINE_LIBRARIES})
|
||||
target_link_libraries (${name} LINK_PUBLIC ${OPENDHT_LIBS})
|
||||
if (MSVC)
|
||||
target_sources(${name} PRIVATE ${MSC_COMPAT_SOURCES})
|
||||
target_include_directories (${name} PRIVATE ${MSC_COMPAT_DIR})
|
||||
@@ -31,8 +29,7 @@ endif ()
|
||||
|
||||
if (OPENDHT_C)
|
||||
add_executable (dhtcnode dhtcnode.c)
|
||||
add_dependencies(dhtcnode ${OPENDHT_C_LIBS})
|
||||
target_link_libraries (dhtcnode LINK_PUBLIC ${OPENDHT_C_LIBS} ${READLINE_LIBRARIES})
|
||||
target_link_libraries (dhtcnode LINK_PUBLIC opendht-c ${READLINE_LIBRARIES})
|
||||
target_include_directories (dhtcnode SYSTEM PRIVATE ${PROJECT_SOURCE_DIR}/c)
|
||||
endif ()
|
||||
|
||||
|
||||
+41
-94
@@ -1,34 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <opendht_c.h>
|
||||
|
||||
#include <getopt.h>
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
#include <getopt.h>
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
struct op_context {
|
||||
dht_runner* runner;
|
||||
atomic_bool stop;
|
||||
@@ -36,18 +18,12 @@ struct op_context {
|
||||
struct listen_context {
|
||||
dht_runner* runner;
|
||||
dht_op_token* token;
|
||||
size_t count;
|
||||
};
|
||||
|
||||
bool dht_value_callback(const dht_value* value, bool expired, void* user_data)
|
||||
{
|
||||
struct listen_context* ctx = (struct listen_context*) user_data;
|
||||
if (expired)
|
||||
ctx->count--;
|
||||
else
|
||||
ctx->count++;
|
||||
dht_data_view data = dht_value_get_data(value);
|
||||
printf("Listen: %s value: %.*s (total %zu).\n", expired ? "expired" : "new", (int)data.size, data.data, ctx->count);
|
||||
printf("Value callback %s: %.*s.\n", expired ? "expired" : "new", (int)data.size, data.data);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -120,26 +96,24 @@ struct dht_params {
|
||||
};
|
||||
|
||||
static const struct option long_options[] = {
|
||||
{"help", no_argument , NULL, 'h'},
|
||||
{"port", required_argument, NULL, 'p'},
|
||||
{"net", required_argument, NULL, 'n'},
|
||||
{"bootstrap", required_argument, NULL, 'b'},
|
||||
{"identity", no_argument , NULL, 'i'},
|
||||
{"verbose", no_argument , NULL, 'v'},
|
||||
{"service", no_argument , NULL, 's'},
|
||||
{"peer-discovery", no_argument , NULL, 'D'},
|
||||
{"no-rate-limit", no_argument , NULL, 'U'},
|
||||
{"persist", required_argument, NULL, 'f'},
|
||||
{"logfile", required_argument, NULL, 'l'},
|
||||
{"syslog", no_argument , NULL, 'L'},
|
||||
{"version", no_argument , NULL, 'V'},
|
||||
{NULL, 0 , NULL, 0}
|
||||
{"help", no_argument , NULL, 'h'},
|
||||
{"port", required_argument, NULL, 'p'},
|
||||
{"net", required_argument, NULL, 'n'},
|
||||
{"bootstrap", required_argument, NULL, 'b'},
|
||||
{"identity", no_argument , NULL, 'i'},
|
||||
{"verbose", no_argument , NULL, 'v'},
|
||||
{"service", no_argument , NULL, 's'},
|
||||
{"peer-discovery", no_argument , NULL, 'D'},
|
||||
{"no-rate-limit", no_argument , NULL, 'U'},
|
||||
{"persist", required_argument, NULL, 'f'},
|
||||
{"logfile", required_argument, NULL, 'l'},
|
||||
{"syslog", no_argument , NULL, 'L'},
|
||||
{NULL, 0 , NULL, 0}
|
||||
};
|
||||
|
||||
struct dht_params
|
||||
parse_args(int argc, char **argv) {
|
||||
struct dht_params params;
|
||||
memset(¶ms, 0, sizeof params);
|
||||
int opt;
|
||||
while ((opt = getopt_long(argc, argv, "hisvDp:n:b:f:l:", long_options, NULL)) != -1) {
|
||||
switch (opt) {
|
||||
@@ -170,9 +144,6 @@ parse_args(int argc, char **argv) {
|
||||
case 's':
|
||||
params.service = true;
|
||||
break;
|
||||
case 'V':
|
||||
params.version = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -180,30 +151,15 @@ parse_args(int argc, char **argv) {
|
||||
return params;
|
||||
}
|
||||
|
||||
dht_infohash parse_key(const char* key_str) {
|
||||
dht_infohash key;
|
||||
dht_infohash_from_hex_null(&key, key_str);
|
||||
if (dht_infohash_is_zero(&key)) {
|
||||
dht_infohash_get_from_string(&key, key_str);
|
||||
printf("Using h(%s) = %s\n", key_str, dht_infohash_print(&key));
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct dht_params params = parse_args(argc, argv);
|
||||
|
||||
if (params.version) {
|
||||
printf("OpenDHT version %s\n", dht_version());
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
dht_runner* runner = dht_runner_new();
|
||||
dht_runner_config dht_config;
|
||||
dht_runner_config_default(&dht_config);
|
||||
dht_config.peer_discovery = params.peer_discovery; // Look for other peers on the network
|
||||
dht_config.peer_publish = params.peer_discovery; // Publish our own peer info
|
||||
dht_runner_config_default(&dht_config);
|
||||
dht_config.peer_discovery = params.peer_discovery; // Look for other peers on the network
|
||||
dht_config.peer_publish = params.peer_discovery; // Publish our own peer info
|
||||
dht_config.dht_config.node_config.network = params.network;
|
||||
dht_config.log = params.log;
|
||||
dht_runner_run_config(runner, params.port, &dht_config);
|
||||
@@ -216,19 +172,20 @@ int main(int argc, char **argv)
|
||||
char cmd[64];
|
||||
char arg[64];
|
||||
char value[256];
|
||||
dht_infohash key;
|
||||
while (true) {
|
||||
const char* line_read = readline("> ");
|
||||
if (line_read && *line_read)
|
||||
add_history(line_read);
|
||||
if (!line_read)
|
||||
break;
|
||||
if (!*line_read)
|
||||
if (!strcmp(line_read, "\0"))
|
||||
continue;
|
||||
add_history(line_read);
|
||||
|
||||
memset(cmd, 0, sizeof cmd);
|
||||
memset(arg, 0, sizeof arg);
|
||||
memset(value, 0, sizeof value);
|
||||
bzero(cmd, sizeof cmd);
|
||||
bzero(arg, sizeof arg);
|
||||
bzero(value, sizeof value);
|
||||
sscanf(line_read, "%64s %64s %256s", cmd, arg, value);
|
||||
//printf("%s -> %s\n", cmd, arg);
|
||||
|
||||
if (!strcmp(cmd, "la")) {
|
||||
struct sockaddr** addrs = dht_runner_get_public_address(runner);
|
||||
@@ -244,44 +201,34 @@ int main(int argc, char **argv)
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (!strcmp(cmd, "ll")) {
|
||||
dht_infohash key = dht_runner_get_node_id(runner);
|
||||
printf("DHT node %s running on port %u\n", dht_infohash_print(&key), dht_runner_get_bound_port(runner, AF_INET));
|
||||
continue;
|
||||
|
||||
dht_infohash key;
|
||||
dht_infohash_from_hex(&key, arg);
|
||||
if (dht_infohash_is_zero(&key)) {
|
||||
dht_infohash_get_from_string(&key, arg);
|
||||
printf("Using h(%s) = %s\n", arg, dht_infohash_print(&key));
|
||||
}
|
||||
else if (!strcmp(cmd, "g")) {
|
||||
key = parse_key(arg);
|
||||
if (!strcmp(cmd, "g")) {
|
||||
dht_runner_get(runner, &key, dht_get_callback, dht_get_done_callback, runner);
|
||||
}
|
||||
else if (!strcmp(cmd, "l")) {
|
||||
key = parse_key(arg);
|
||||
} else if (!strcmp(cmd, "l")) {
|
||||
struct listen_context* ctx = malloc(sizeof(struct listen_context));
|
||||
ctx->runner = runner;
|
||||
ctx->count = 0;
|
||||
ctx->token = dht_runner_listen(runner, &key, dht_value_callback, listen_context_free, ctx);
|
||||
}
|
||||
else if (!strcmp(cmd, "p")) {
|
||||
key = parse_key(arg);
|
||||
} else if (!strcmp(cmd, "p")) {
|
||||
dht_value* val = dht_value_new_from_string(value);
|
||||
dht_runner_put(runner, &key, val, dht_put_done_callback, runner, true);
|
||||
dht_value_unref(val);
|
||||
}
|
||||
else {
|
||||
printf("Unkown command: %s\n", cmd);
|
||||
}
|
||||
}
|
||||
printf("Stopping..\n");
|
||||
|
||||
// Graceful shutdown
|
||||
printf("Stopping…\n");
|
||||
struct op_context ctx;
|
||||
ctx.runner = runner;
|
||||
atomic_init(&ctx.stop, false);
|
||||
dht_runner_shutdown(runner, dht_shutdown_callback, &ctx);
|
||||
|
||||
// Wait until shutdown callback is called
|
||||
while (!atomic_load(&ctx.stop)) {
|
||||
usleep(10000);
|
||||
usleep(250);
|
||||
}
|
||||
dht_runner_delete(runner);
|
||||
return EXIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
@@ -146,7 +146,6 @@ void cmd_loop(std::shared_ptr<DhtRunner>& node, dht_params& params
|
||||
node->getNodeInfo([&](const std::shared_ptr<dht::NodeInfo>& nodeInfo) {
|
||||
print_node_info(*nodeInfo);
|
||||
std::cout << nodeInfo->ongoing_ops << " ongoing operations" << std::endl;
|
||||
std::cout << "Storage has " << nodeInfo->storage_values << " values, using " << (nodeInfo->storage_size/1024) << " KB" << std::endl;
|
||||
std::cout << "IPv4 stats:" << std::endl;
|
||||
std::cout << nodeInfo->ipv4.toString() << std::endl;
|
||||
std::cout << "IPv6 stats:" << std::endl;
|
||||
|
||||
Reference in new issue
Block a user