mirror of
https://github.com/savoirfairelinux/opendht.git
synced 2026-11-08 04:21:51 +00:00
commit
e1b9256f19
23 files changed
+1421
-1402
No files matched your search
@@ -0,0 +1,83 @@
|
||||
cmake_minimum_required (VERSION 2.8.6)
|
||||
project (opendht)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
||||
|
||||
set (prefix ${CMAKE_INSTALL_PREFIX})
|
||||
set (exec_prefix "\${prefix}")
|
||||
set (libdir "\${exec_prefix}/lib")
|
||||
set (includedir "\${prefix}/include")
|
||||
|
||||
option (OPENDHT_PYTHON "Build Python bindings" OFF)
|
||||
option (OPENDHT_TOOLS "Build DHT tools" ON)
|
||||
option (OPENDHT_DEBUG "Build with debug flags" OFF)
|
||||
|
||||
set (CMAKE_CXX_FLAGS "-std=c++11 -Wno-return-type -Wall -Wextra -Wnon-virtual-dtor ${CMAKE_CXX_FLAGS}")
|
||||
|
||||
find_package (GnuTLS 3.1 REQUIRED)
|
||||
find_package (Msgpack 1.1 REQUIRED)
|
||||
|
||||
list (APPEND opendht_SOURCES
|
||||
src/infohash.cpp
|
||||
src/crypto.cpp
|
||||
src/default_types.cpp
|
||||
src/value.cpp
|
||||
src/dht.cpp
|
||||
src/securedht.cpp
|
||||
src/dhtrunner.cpp
|
||||
)
|
||||
|
||||
list (APPEND opendht_HEADERS
|
||||
include/opendht/rng.h
|
||||
include/opendht/crypto.h
|
||||
include/opendht/infohash.h
|
||||
include/opendht/default_types.h
|
||||
include/opendht/value.h
|
||||
include/opendht/dht.h
|
||||
include/opendht/securedht.h
|
||||
include/opendht.h
|
||||
)
|
||||
|
||||
configure_file (
|
||||
opendht.pc.in
|
||||
opendht.pc
|
||||
@ONLY
|
||||
)
|
||||
|
||||
include_directories (
|
||||
./
|
||||
include/
|
||||
include/opendht/
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/
|
||||
)
|
||||
|
||||
if (OPENDHT_DEBUG)
|
||||
set(CMAKE_BUILD_TYPE Debug)
|
||||
else ()
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
endif ()
|
||||
|
||||
add_library (opendht SHARED
|
||||
${opendht_SOURCES}
|
||||
${opendht_HEADERS}
|
||||
)
|
||||
set_target_properties (opendht PROPERTIES IMPORT_SUFFIX "_import.lib")
|
||||
#set_target_properties (opendht PROPERTIES SOVERSION 1 VERSION 1.0.0)
|
||||
|
||||
add_library (opendht-static STATIC
|
||||
${opendht_SOURCES}
|
||||
${opendht_HEADERS}
|
||||
)
|
||||
set_target_properties (opendht-static PROPERTIES OUTPUT_NAME "opendht")
|
||||
|
||||
if (NOT DEFINED CMAKE_INSTALL_LIBDIR)
|
||||
set(CMAKE_INSTALL_LIBDIR lib)
|
||||
endif ()
|
||||
|
||||
if (OPENDHT_TOOLS)
|
||||
add_subdirectory(tools)
|
||||
endif ()
|
||||
|
||||
install (TARGETS opendht opendht-static DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
install (DIRECTORY include DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/opendht.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
@@ -0,0 +1,48 @@
|
||||
# - Try to find msgpack
|
||||
# Once done this will define
|
||||
# MSGPACK_FOUND - System has msgpack
|
||||
# MSGPACK_INCLUDE_DIRS - The msgpack include directories
|
||||
# MSGPACK_LIBRARIES - The libraries needed to use msgpack
|
||||
|
||||
if(NOT MSGPACK_USE_BUNDLED)
|
||||
find_package(PkgConfig)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PC_MSGPACK QUIET msgpack)
|
||||
endif()
|
||||
else()
|
||||
set(PC_MSGPACK_INCLUDEDIR)
|
||||
set(PC_MSGPACK_INCLUDE_DIRS)
|
||||
set(PC_MSGPACK_LIBDIR)
|
||||
set(PC_MSGPACK_LIBRARY_DIRS)
|
||||
set(LIMIT_SEARCH NO_DEFAULT_PATH)
|
||||
endif()
|
||||
|
||||
set(MSGPACK_DEFINITIONS ${PC_MSGPACK_CFLAGS_OTHER})
|
||||
|
||||
find_path(MSGPACK_INCLUDE_DIR msgpack.h
|
||||
HINTS ${PC_MSGPACK_INCLUDEDIR} ${PC_MSGPACK_INCLUDE_DIRS}
|
||||
${LIMIT_SEARCH})
|
||||
|
||||
# If we're asked to use static linkage, add libmsgpack.a as a preferred library name.
|
||||
if(MSGPACK_USE_STATIC)
|
||||
list(APPEND MSGPACK_NAMES
|
||||
"${CMAKE_STATIC_LIBRARY_PREFIX}msgpack${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
||||
endif()
|
||||
|
||||
list(APPEND MSGPACK_NAMES msgpack)
|
||||
|
||||
find_library(MSGPACK_LIBRARY NAMES ${MSGPACK_NAMES}
|
||||
HINTS ${PC_MSGPACK_LIBDIR} ${PC_MSGPACK_LIBRARY_DIRS}
|
||||
${LIMIT_SEARCH})
|
||||
|
||||
mark_as_advanced(MSGPACK_INCLUDE_DIR MSGPACK_LIBRARY)
|
||||
|
||||
set(MSGPACK_LIBRARIES ${MSGPACK_LIBRARY})
|
||||
set(MSGPACK_INCLUDE_DIRS ${MSGPACK_INCLUDE_DIR})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
# handle the QUIETLY and REQUIRED arguments and set MSGPACK_FOUND to TRUE
|
||||
# if all listed variables are TRUE
|
||||
find_package_handle_standard_args(Msgpack DEFAULT_MSG
|
||||
MSGPACK_LIBRARY MSGPACK_INCLUDE_DIR)
|
||||
|
||||
@@ -72,7 +72,9 @@ LT_LANG(C++)
|
||||
AX_CXX_COMPILE_STDCXX_11([noext],[mandatory])
|
||||
|
||||
PKG_PROG_PKG_CONFIG()
|
||||
PKG_CHECK_MODULES([nettle], [nettle >= 2.4])
|
||||
PKG_CHECK_MODULES([GNUTLS], [gnutls >= 3.1])
|
||||
PKG_CHECK_MODULES([msgpack], [msgpack >= 1.1])
|
||||
|
||||
AC_ARG_ENABLE([tools], AS_HELP_STRING([--disable-tools],[Disable tools (CLI DHT node)]),,build_tools=yes)
|
||||
AM_CONDITIONAL(ENABLE_TOOLS, test x$build_tools == xyes)
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "infohash.h"
|
||||
#include "serialize.h"
|
||||
|
||||
extern "C" {
|
||||
#include <gnutls/gnutls.h>
|
||||
@@ -42,6 +41,8 @@ extern "C" {
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
typedef std::vector<uint8_t> Blob;
|
||||
|
||||
namespace dht {
|
||||
namespace crypto {
|
||||
|
||||
@@ -75,7 +76,7 @@ Identity generateIdentity(const std::string& name = "dhtnode", Identity ca = {},
|
||||
/**
|
||||
* A public key.
|
||||
*/
|
||||
struct PublicKey : public Serializable
|
||||
struct PublicKey
|
||||
{
|
||||
PublicKey() {}
|
||||
PublicKey(gnutls_pubkey_t k) : pk(k) {}
|
||||
@@ -91,14 +92,25 @@ struct PublicKey : public Serializable
|
||||
bool checkSignature(const Blob& data, const Blob& signature) const;
|
||||
Blob encrypt(const Blob&) const;
|
||||
|
||||
void pack(Blob& b) const override;
|
||||
void pack(Blob& b) const;
|
||||
void unpack(const uint8_t* dat, size_t dat_size);
|
||||
|
||||
void unpack(Blob::const_iterator& begin, Blob::const_iterator& end) override;
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& p) const
|
||||
{
|
||||
Blob b;
|
||||
pack(b);
|
||||
p.pack_bin(b.size());
|
||||
p.pack_bin_body((const char*)b.data(), b.size());
|
||||
}
|
||||
|
||||
void msgpack_unpack(msgpack::object o);
|
||||
|
||||
gnutls_pubkey_t pk {};
|
||||
private:
|
||||
PublicKey(const PublicKey&) = delete;
|
||||
PublicKey& operator=(const PublicKey&) = delete;
|
||||
void encryptBloc(const uint8_t* src, size_t src_size, uint8_t* dst, size_t dst_size) const;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -134,6 +146,7 @@ struct PrivateKey
|
||||
/**
|
||||
* Generate a new RSA key pair
|
||||
* @param key_length : size of the modulus in bits
|
||||
* Minimim value: 2048
|
||||
* Recommended values: 4096, 8192
|
||||
*/
|
||||
static PrivateKey generate(unsigned key_length = 4096);
|
||||
@@ -143,11 +156,12 @@ struct PrivateKey
|
||||
private:
|
||||
PrivateKey(const PrivateKey&) = delete;
|
||||
PrivateKey& operator=(const PrivateKey&) = delete;
|
||||
Blob decryptBloc(const uint8_t* src, size_t src_size) const;
|
||||
|
||||
friend dht::crypto::Identity dht::crypto::generateIdentity(const std::string&, dht::crypto::Identity, unsigned key_length);
|
||||
};
|
||||
|
||||
struct Certificate : public Serializable {
|
||||
struct Certificate {
|
||||
Certificate() {}
|
||||
|
||||
/**
|
||||
@@ -155,11 +169,16 @@ struct Certificate : public Serializable {
|
||||
*/
|
||||
Certificate(gnutls_x509_crt_t crt) : cert(crt) {}
|
||||
|
||||
Certificate(Certificate&& o) noexcept : cert(o.cert), issuer(std::move(o.issuer)) { o.cert = nullptr; };
|
||||
|
||||
/**
|
||||
* Import certificate (PEM or DER) or certificate chain (PEM),
|
||||
* ordered from subject to issuer
|
||||
*/
|
||||
Certificate(const Blob& crt);
|
||||
Certificate(const uint8_t* dat, size_t dat_size) {
|
||||
unpack(dat, dat_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import certificate chain (PEM or DER),
|
||||
@@ -179,12 +198,16 @@ struct Certificate : public Serializable {
|
||||
unpack(certs);
|
||||
}
|
||||
|
||||
Certificate(Certificate&& o) noexcept : cert(o.cert), issuer(std::move(o.issuer)) { o.cert = nullptr; };
|
||||
Certificate& operator=(Certificate&& o) noexcept;
|
||||
~Certificate();
|
||||
|
||||
void pack(Blob& b) const override;
|
||||
void unpack(Blob::const_iterator& begin, Blob::const_iterator& end) override;
|
||||
void pack(Blob& b) const;
|
||||
void unpack(const uint8_t* dat, size_t dat_size);
|
||||
Blob getPacked() const {
|
||||
Blob b;
|
||||
pack(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
template<typename Iterator>
|
||||
void unpack(const Iterator& begin, const Iterator& end)
|
||||
@@ -227,6 +250,17 @@ struct Certificate : public Serializable {
|
||||
*this = tmp_issuer ? std::move(*tmp_issuer) : Certificate();
|
||||
}
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& p) const
|
||||
{
|
||||
Blob b;
|
||||
pack(b);
|
||||
p.pack_bin(b.size());
|
||||
p.pack_bin_body((const char*)b.data(), b.size());
|
||||
}
|
||||
|
||||
void msgpack_unpack(msgpack::object o);
|
||||
|
||||
operator bool() const { return cert; }
|
||||
PublicKey getPublicKey() const;
|
||||
|
||||
@@ -270,6 +304,15 @@ private:
|
||||
friend dht::crypto::Identity dht::crypto::generateIdentity(const std::string&, dht::crypto::Identity, unsigned key_length);
|
||||
};
|
||||
|
||||
/**
|
||||
* AES-GCM encryption. Key must be 128, 192 or 126 bits long (16, 24 or 32 bytes).
|
||||
*/
|
||||
Blob aesEncrypt(const Blob& data, const Blob& key);
|
||||
|
||||
/**
|
||||
* AES-GCM decryption.
|
||||
*/
|
||||
Blob aesDecrypt(const Blob& data, const Blob& key);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -34,17 +34,14 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
struct DhtMessage : public ValueSerializable
|
||||
struct DhtMessage : public Value::Serializable<DhtMessage>
|
||||
{
|
||||
DhtMessage(std::string s = {}, Blob msg = {}) : service(s), data(msg) {}
|
||||
|
||||
|
||||
std::string getService() const {
|
||||
return service;
|
||||
}
|
||||
|
||||
virtual void pack(Blob& res) const;
|
||||
virtual void unpack(Blob::const_iterator& begin, Blob::const_iterator& end);
|
||||
|
||||
static const ValueType TYPE;
|
||||
virtual const ValueType& getType() const {
|
||||
return TYPE;
|
||||
@@ -61,14 +58,15 @@ struct DhtMessage : public ValueSerializable
|
||||
public:
|
||||
std::string service;
|
||||
Blob data;
|
||||
MSGPACK_DEFINE(service, data);
|
||||
};
|
||||
|
||||
|
||||
struct SignedValue : public ValueSerializable
|
||||
template <typename Type>
|
||||
struct SignedValue : public Value::Serializable<Type>
|
||||
{
|
||||
virtual void unpackValue(const Value& v) {
|
||||
from = v.owner.getId();
|
||||
ValueSerializable::unpackValue(v);
|
||||
Value::Serializable<Type>::unpackValue(v);
|
||||
}
|
||||
static Value::Filter getFilter() {
|
||||
return [](const Value& v){ return v.isSigned(); };
|
||||
@@ -77,15 +75,16 @@ public:
|
||||
dht::InfoHash from;
|
||||
};
|
||||
|
||||
struct EncryptedValue : public SignedValue
|
||||
template <typename Type>
|
||||
struct EncryptedValue : public SignedValue<Type>
|
||||
{
|
||||
virtual void unpackValue(const Value& v) {
|
||||
to = v.recipient;
|
||||
SignedValue::unpackValue(v);
|
||||
SignedValue<Type>::unpackValue(v);
|
||||
}
|
||||
static Value::Filter getFilter() {
|
||||
return Value::Filter::chain(
|
||||
SignedValue::getFilter(),
|
||||
SignedValue<Type>::getFilter(),
|
||||
[](const Value& v){ return v.recipient != InfoHash(); }
|
||||
);
|
||||
}
|
||||
@@ -94,7 +93,7 @@ public:
|
||||
dht::InfoHash to;
|
||||
};
|
||||
|
||||
struct ImMessage : public SignedValue
|
||||
struct ImMessage : public SignedValue<ImMessage>
|
||||
{
|
||||
ImMessage() {}
|
||||
ImMessage(std::string&& msg)
|
||||
@@ -107,14 +106,6 @@ struct ImMessage : public SignedValue
|
||||
static Value::Filter getFilter() {
|
||||
return SignedValue::getFilter();
|
||||
}
|
||||
virtual void pack(Blob& data) const {
|
||||
serialize<std::chrono::system_clock::time_point>(std::chrono::system_clock::now(), data);
|
||||
data.insert(data.end(), im_message.begin(), im_message.end());
|
||||
}
|
||||
virtual void unpack(Blob::const_iterator& b, Blob::const_iterator& e) {
|
||||
sent = deserialize<decltype(sent)>(b, e);
|
||||
im_message = std::string(b, e);
|
||||
}
|
||||
virtual void unpackValue(const Value& v) {
|
||||
to = v.recipient;
|
||||
SignedValue::unpackValue(v);
|
||||
@@ -123,9 +114,10 @@ struct ImMessage : public SignedValue
|
||||
dht::InfoHash to;
|
||||
std::chrono::system_clock::time_point sent;
|
||||
std::string im_message;
|
||||
MSGPACK_DEFINE(im_message);
|
||||
};
|
||||
|
||||
struct TrustRequest : public EncryptedValue
|
||||
struct TrustRequest : public EncryptedValue<TrustRequest>
|
||||
{
|
||||
TrustRequest() {}
|
||||
TrustRequest(std::string s) : service(s) {}
|
||||
@@ -138,22 +130,16 @@ struct TrustRequest : public EncryptedValue
|
||||
static Value::Filter getFilter() {
|
||||
return EncryptedValue::getFilter();
|
||||
}
|
||||
virtual void pack(Blob& data) const {
|
||||
serialize<std::string>(service, data);
|
||||
serialize<Blob>(payload, data);
|
||||
}
|
||||
virtual void unpack(Blob::const_iterator& b, Blob::const_iterator& e) {
|
||||
service = deserialize<std::string>(b, e);
|
||||
payload = deserialize<Blob>(b, e);
|
||||
}
|
||||
|
||||
std::string service;
|
||||
Blob payload;
|
||||
MSGPACK_DEFINE(service, payload);
|
||||
};
|
||||
|
||||
struct IceCandidates : public EncryptedValue
|
||||
struct IceCandidates : public EncryptedValue<IceCandidates>
|
||||
{
|
||||
IceCandidates() {}
|
||||
IceCandidates(Blob ice) : ice_data(ice) {}
|
||||
IceCandidates(Value::Id msg_id, Blob ice) : id(msg_id), ice_data(ice) {}
|
||||
|
||||
static const ValueType TYPE;
|
||||
virtual const ValueType& getType() const {
|
||||
@@ -162,25 +148,16 @@ struct IceCandidates : public EncryptedValue
|
||||
static Value::Filter getFilter() {
|
||||
return EncryptedValue::getFilter();
|
||||
}
|
||||
virtual void pack(Blob& data) const {
|
||||
serialize<Blob>(ice_data, data);
|
||||
}
|
||||
virtual void unpack(Blob::const_iterator& b, Blob::const_iterator& e) {
|
||||
ice_data = deserialize<Blob>(b, e);
|
||||
}
|
||||
virtual void unpackValue(const Value& v) {
|
||||
EncryptedValue::unpackValue(v);
|
||||
id = v.id;
|
||||
}
|
||||
|
||||
Value::Id id;
|
||||
Blob ice_data;
|
||||
MSGPACK_DEFINE(id, ice_data);
|
||||
};
|
||||
|
||||
|
||||
/* "Peer" announcement
|
||||
*/
|
||||
struct IpServiceAnnouncement : public ValueSerializable
|
||||
struct IpServiceAnnouncement : public Value::Serializable<IpServiceAnnouncement>
|
||||
{
|
||||
IpServiceAnnouncement(in_port_t p = 0) {
|
||||
ss.ss_family = 0;
|
||||
@@ -193,11 +170,36 @@ struct IpServiceAnnouncement : public ValueSerializable
|
||||
}
|
||||
|
||||
IpServiceAnnouncement(const Blob& b) {
|
||||
unpackBlob(b);
|
||||
msgpack_unpack(unpackMsg(b).get());
|
||||
}
|
||||
|
||||
virtual void pack(Blob& res) const;
|
||||
virtual void unpack(Blob::const_iterator& begin, Blob::const_iterator& end);
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& pk) const
|
||||
{
|
||||
pk.pack_array(2);
|
||||
pk.pack(getPort());
|
||||
if (ss.ss_family == AF_INET) {
|
||||
pk.pack_bin(sizeof(in_addr));
|
||||
pk.pack_bin_body((const char*)&reinterpret_cast<const sockaddr_in*>(&ss)->sin_addr, sizeof(in_addr));
|
||||
} else if (ss.ss_family == AF_INET6) {
|
||||
pk.pack_bin(sizeof(in6_addr));
|
||||
pk.pack_bin_body((const char*)&reinterpret_cast<const sockaddr_in6*>(&ss)->sin6_addr, sizeof(in6_addr));
|
||||
}
|
||||
}
|
||||
|
||||
void msgpack_unpack(msgpack::object o)
|
||||
{
|
||||
if (o.type != msgpack::type::ARRAY) throw msgpack::type_error();
|
||||
if (o.via.array.size < 2) throw msgpack::type_error();
|
||||
setPort(o.via.array.ptr[0].as<in_port_t>());
|
||||
auto ip_dat = o.via.array.ptr[1].as<Blob>();
|
||||
if (ip_dat.size() == sizeof(in_addr))
|
||||
std::copy(ip_dat.begin(), ip_dat.end(), (char*)&reinterpret_cast<sockaddr_in*>(&ss)->sin_addr);
|
||||
else if (ip_dat.size() == sizeof(in6_addr))
|
||||
std::copy(ip_dat.begin(), ip_dat.end(), (char*)&reinterpret_cast<sockaddr_in6*>(&ss)->sin6_addr);
|
||||
else
|
||||
throw msgpack::type_error();
|
||||
}
|
||||
|
||||
in_port_t getPort() const {
|
||||
return ntohs(reinterpret_cast<const sockaddr_in*>(&ss)->sin_port);
|
||||
|
||||
+29
-15
@@ -138,12 +138,14 @@ public:
|
||||
|
||||
static GetCallbackSimple
|
||||
bindGetCb(GetCallbackRaw raw_cb, void* user_data) {
|
||||
if (not raw_cb) return {};
|
||||
return [=](const std::shared_ptr<Value>& value) {
|
||||
return raw_cb(value, user_data);
|
||||
};
|
||||
}
|
||||
static GetCallback
|
||||
bindGetCb(GetCallbackSimple cb) {
|
||||
if (not cb) return {};
|
||||
return [=](const std::vector<std::shared_ptr<Value>>& values) {
|
||||
for (const auto& v : values)
|
||||
if (not cb(v))
|
||||
@@ -159,11 +161,13 @@ public:
|
||||
|
||||
static DoneCallback
|
||||
bindDoneCb(DoneCallbackSimple donecb) {
|
||||
if (not donecb) return {};
|
||||
using namespace std::placeholders;
|
||||
return std::bind(donecb, _1);
|
||||
}
|
||||
static DoneCallback
|
||||
bindDoneCb(DoneCallbackRaw raw_cb, void* user_data) {
|
||||
if (not raw_cb) return {};
|
||||
return [=](bool success, const std::vector<std::shared_ptr<Node>>& nodes) {
|
||||
raw_cb(success, (std::vector<std::shared_ptr<Node>>*)&nodes, user_data);
|
||||
};
|
||||
@@ -266,7 +270,7 @@ public:
|
||||
* reannounced on a regular basis.
|
||||
* User can call #cancelPut(InfoHash, Value::Id) to cancel a put operation.
|
||||
*/
|
||||
void put(const InfoHash& key, const std::shared_ptr<Value>&, DoneCallback cb=nullptr);
|
||||
void put(const InfoHash& key, std::shared_ptr<Value>, DoneCallback cb=nullptr);
|
||||
void put(const InfoHash& key, const std::shared_ptr<Value>& v, DoneCallbackSimple cb) {
|
||||
put(key, v, bindDoneCb(cb));
|
||||
}
|
||||
@@ -359,7 +363,7 @@ private:
|
||||
static constexpr unsigned MAX_HASHES {16384};
|
||||
|
||||
/* The maximum number of searches we keep data about. */
|
||||
static constexpr unsigned MAX_SEARCHES {1024};
|
||||
static constexpr unsigned MAX_SEARCHES {128};
|
||||
|
||||
/* The time after which we can send get requests for
|
||||
a search in case of no answers. */
|
||||
@@ -383,6 +387,7 @@ private:
|
||||
|
||||
static constexpr unsigned TOKEN_SIZE {64};
|
||||
|
||||
static const std::string my_v;
|
||||
|
||||
struct NodeCache {
|
||||
std::shared_ptr<Node> getNode(const InfoHash& id, sa_family_t family);
|
||||
@@ -673,6 +678,7 @@ private:
|
||||
*/
|
||||
struct TransId final : public std::array<uint8_t, 4> {
|
||||
TransId() {}
|
||||
TransId(const std::array<char, 4>& o) { std::copy(o.begin(), o.end(), begin()); }
|
||||
TransId(const TransPrefix prefix, uint16_t seqno = 0) {
|
||||
std::copy_n(prefix.begin(), prefix.size(), begin());
|
||||
*reinterpret_cast<uint16_t*>(data()+prefix.size()) = seqno;
|
||||
@@ -688,7 +694,7 @@ private:
|
||||
}
|
||||
|
||||
bool matches(const TransPrefix prefix, uint16_t *seqno_return = nullptr) const {
|
||||
if (std::equal(begin(), begin()+1, prefix.begin())) {
|
||||
if (std::equal(begin(), begin()+2, prefix.begin())) {
|
||||
if (seqno_return)
|
||||
*seqno_return = *reinterpret_cast<const uint16_t*>(&(*this)[2]);
|
||||
return true;
|
||||
@@ -708,7 +714,7 @@ private:
|
||||
int dht_socket6 {-1};
|
||||
|
||||
InfoHash myid {};
|
||||
static const uint8_t my_v[9];
|
||||
|
||||
std::array<uint8_t, 8> secret {{}};
|
||||
std::array<uint8_t, 8> oldsecret {{}};
|
||||
|
||||
@@ -785,16 +791,24 @@ private:
|
||||
int sendError(const sockaddr*, socklen_t, TransId tid, uint16_t code, const char *message, bool include_id=false);
|
||||
|
||||
void processMessage(const uint8_t *buf, size_t buflen, const sockaddr *from, socklen_t fromlen);
|
||||
MessageType parseMessage(const uint8_t *buf, size_t buflen,
|
||||
TransId& tid,
|
||||
InfoHash& id_return, InfoHash& info_hash_return,
|
||||
InfoHash& target_return, in_port_t& port_return,
|
||||
Blob& token, Value::Id& value_id,
|
||||
uint8_t *nodes_return, unsigned *nodes_len,
|
||||
uint8_t *nodes6_return, unsigned *nodes6_len,
|
||||
std::vector<std::shared_ptr<Value>>& values_return,
|
||||
want_t* want_return, uint16_t& error_code, bool& ring,
|
||||
sockaddr* addr_return, socklen_t& addr_length_return);
|
||||
|
||||
struct ParsedMessage {
|
||||
MessageType type;
|
||||
InfoHash id;
|
||||
InfoHash info_hash;
|
||||
InfoHash target;
|
||||
TransId tid;
|
||||
Blob token;
|
||||
Value::Id value_id;
|
||||
Blob nodes4;
|
||||
Blob nodes6;
|
||||
std::vector<std::shared_ptr<Value>> values;
|
||||
want_t want;
|
||||
uint16_t error_code;
|
||||
std::string ua;
|
||||
Address addr;
|
||||
void msgpack_unpack(msgpack::object o);
|
||||
};
|
||||
|
||||
void rotateSecrets();
|
||||
|
||||
@@ -859,7 +873,7 @@ private:
|
||||
* The values can be filtered by an arbitrary provided filter.
|
||||
*/
|
||||
Search* search(const InfoHash& id, sa_family_t af, GetCallback = nullptr, DoneCallback = nullptr, Value::Filter = Value::AllFilter());
|
||||
void announce(const InfoHash& id, sa_family_t af, const std::shared_ptr<Value>& value, DoneCallback callback);
|
||||
void announce(const InfoHash& id, sa_family_t af, std::shared_ptr<Value> value, DoneCallback callback);
|
||||
size_t listenTo(const InfoHash& id, sa_family_t af, GetCallback cb, Value::Filter f = Value::AllFilter());
|
||||
|
||||
std::list<Search>::iterator newSearch();
|
||||
|
||||
+23
-25
@@ -73,50 +73,49 @@ public:
|
||||
void get(const std::string& key, Dht::GetCallback vcb, Dht::DoneCallbackSimple dcb={}, Value::Filter f = Value::AllFilter());
|
||||
|
||||
template <class T>
|
||||
void get(InfoHash hash, std::function<bool(std::vector<T>&&)> cb)
|
||||
void get(InfoHash hash, std::function<bool(std::vector<T>&&)> cb, Dht::DoneCallbackSimple dcb={})
|
||||
{
|
||||
get(hash, [=](const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
return cb(unpackVector<T>(vals));
|
||||
},
|
||||
T::getFilter());
|
||||
dcb,
|
||||
getFilterSet<T>());
|
||||
}
|
||||
template <class T>
|
||||
void get(InfoHash hash, std::function<bool(T&&)> cb)
|
||||
void get(InfoHash hash, std::function<bool(T&&)> cb, Dht::DoneCallbackSimple dcb={})
|
||||
{
|
||||
get(hash, [=](const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
for (const auto& v : vals) {
|
||||
T msg;
|
||||
try {
|
||||
msg.unpackValue(*v);
|
||||
if (not cb(Value::unpack<T>(*v)))
|
||||
return false;
|
||||
} catch (const std::exception&) {
|
||||
continue;
|
||||
}
|
||||
if (not cb(std::move(msg)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
T::getFilter());
|
||||
dcb,
|
||||
getFilterSet<T>());
|
||||
}
|
||||
|
||||
std::future<std::vector<std::shared_ptr<dht::Value>>> get(InfoHash key, Value::Filter f = Value::AllFilter()) {
|
||||
auto p = std::make_shared<std::promise<std::vector<std::shared_ptr< dht::Value >>>>();
|
||||
auto values = std::make_shared<std::vector<std::shared_ptr< dht::Value >>>();
|
||||
|
||||
get(key, [=](const std::vector<std::shared_ptr<dht::Value>>& vlist) {
|
||||
values->insert(values->end(), vlist.begin(), vlist.end());
|
||||
return true;
|
||||
}, [=](bool) {
|
||||
p->set_value(std::move(*values));
|
||||
}, f);
|
||||
},
|
||||
f);
|
||||
return p->get_future();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::future<std::vector<T>> get(InfoHash key) {
|
||||
auto p = std::make_shared<std::promise<std::vector<std::shared_ptr<dht::Value>>>>();
|
||||
auto p = std::make_shared<std::promise<std::vector<T>>>();
|
||||
auto values = std::make_shared<std::vector<T>>();
|
||||
|
||||
get<T>(key, [=](T&& v) {
|
||||
values->emplace_back(std::move(v));
|
||||
return true;
|
||||
@@ -138,29 +137,23 @@ public:
|
||||
return listen(hash, [=](const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
return cb(unpackVector<T>(vals));
|
||||
},
|
||||
T::getFilter());
|
||||
getFilterSet<T>());
|
||||
}
|
||||
template <class T>
|
||||
template <typename T>
|
||||
std::future<size_t> listen(InfoHash hash, std::function<bool(T&&)> cb, Value::Filter f = Value::AllFilter())
|
||||
{
|
||||
return listen(hash, [=](const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
for (const auto& v : vals) {
|
||||
T msg;
|
||||
try {
|
||||
msg.unpackValue(*v);
|
||||
if (not cb(Value::unpack<T>(*v)))
|
||||
return false;
|
||||
} catch (const std::exception&) {
|
||||
continue;
|
||||
}
|
||||
if (not cb(std::move(msg)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
Value::Filter::chain({
|
||||
Value::TypeFilter(T::TYPE),
|
||||
T::getFilter(),
|
||||
f
|
||||
}));
|
||||
getFilterSet<T>(f));
|
||||
}
|
||||
|
||||
void cancelListen(InfoHash h, size_t token);
|
||||
@@ -171,12 +164,12 @@ public:
|
||||
void put(InfoHash hash, Value&& value, Dht::DoneCallbackSimple cb) {
|
||||
put(hash, std::forward<Value>(value), Dht::bindDoneCb(cb));
|
||||
}
|
||||
void put(const std::string& key, Value&& value, Dht::DoneCallback cb=nullptr);
|
||||
void put(const std::string& key, Value&& value, Dht::DoneCallbackSimple cb=nullptr);
|
||||
|
||||
void cancelPut(const InfoHash& h, const Value::Id& id);
|
||||
|
||||
void putSigned(InfoHash hash, Value&& value, Dht::DoneCallback cb=nullptr);
|
||||
void putSigned(const std::string& key, Value&& value, Dht::DoneCallback cb=nullptr);
|
||||
void putSigned(const std::string& key, Value&& value, Dht::DoneCallbackSimple cb=nullptr);
|
||||
void putSigned(InfoHash hash, Value&& value, Dht::DoneCallbackSimple cb) {
|
||||
putSigned(hash, std::forward<Value>(value), Dht::bindDoneCb(cb));
|
||||
}
|
||||
@@ -372,6 +365,10 @@ private:
|
||||
|
||||
static std::vector<std::pair<sockaddr_storage, socklen_t>> getAddrInfo(const char* host, const char* service);
|
||||
|
||||
Dht::Status getStatus() const {
|
||||
return std::max(status4, status6);
|
||||
}
|
||||
|
||||
std::unique_ptr<SecureDht> dht_ {};
|
||||
mutable std::mutex dht_mtx {};
|
||||
std::thread dht_thread {};
|
||||
@@ -381,6 +378,7 @@ private:
|
||||
std::mutex sock_mtx {};
|
||||
std::vector<std::pair<Blob, std::pair<sockaddr_storage, socklen_t>>> rcv {};
|
||||
|
||||
std::queue<std::function<void(SecureDht&)>> pending_ops_prio {};
|
||||
std::queue<std::function<void(SecureDht&)>> pending_ops {};
|
||||
std::mutex storage_mtx {};
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <msgpack.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <array>
|
||||
@@ -73,6 +75,10 @@ public:
|
||||
*/
|
||||
explicit InfoHash(const std::string& hex);
|
||||
|
||||
InfoHash(const msgpack::object& o) {
|
||||
msgpack_unpack(o);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the lowest 1 bit in an id.
|
||||
* Result will allways be lower than 8*HASH_LEN
|
||||
@@ -193,6 +199,20 @@ public:
|
||||
friend std::ostream& operator<< (std::ostream& s, const InfoHash& h);
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& pk) const
|
||||
{
|
||||
pk.pack_bin(HASH_LEN);
|
||||
pk.pack_bin_body((char*)data(), HASH_LEN);
|
||||
}
|
||||
|
||||
void msgpack_unpack(msgpack::object o) {
|
||||
if (o.type != msgpack::type::BIN or o.via.bin.size != HASH_LEN)
|
||||
throw msgpack::type_error();
|
||||
std::copy_n(o.via.bin.ptr, HASH_LEN, data());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -99,7 +99,7 @@ public:
|
||||
/**
|
||||
* Will take ownership of the value, sign it using our private key and put it in the DHT.
|
||||
*/
|
||||
void putSigned(const InfoHash& hash, const std::shared_ptr<Value>& val, DoneCallback callback);
|
||||
void putSigned(const InfoHash& hash, std::shared_ptr<Value> val, DoneCallback callback);
|
||||
void putSigned(const InfoHash& hash, Value&& v, DoneCallback callback) {
|
||||
putSigned(hash, std::make_shared<Value>(std::move(v)), callback);
|
||||
}
|
||||
|
||||
@@ -1,339 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2013, Simone Pellegrini All rights reserved.
|
||||
* Copyright (c) 2014 Savoir-Faire Linux. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <numeric>
|
||||
#include <limits>
|
||||
#include <chrono>
|
||||
|
||||
typedef std::vector<uint8_t> Blob;
|
||||
|
||||
template <class T>
|
||||
inline void serialize(const T&, Blob&);
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<std::size_t> struct int_{};
|
||||
|
||||
}
|
||||
|
||||
// get_size
|
||||
template <class T>
|
||||
size_t get_size(const T& obj);
|
||||
|
||||
namespace detail {
|
||||
|
||||
typedef uint16_t serialized_size_t;
|
||||
|
||||
template <class T>
|
||||
struct get_size_helper;
|
||||
|
||||
template <class T>
|
||||
struct get_size_helper<std::vector<T>> {
|
||||
static size_t value(const std::vector<T>& obj) {
|
||||
return std::accumulate(obj.begin(), obj.end(), sizeof(serialized_size_t),
|
||||
[](const size_t& acc, const T& cur) { return acc+get_size(cur); });
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct get_size_helper<std::string> {
|
||||
static size_t value(const std::string& obj) {
|
||||
return sizeof(serialized_size_t) + obj.length()*sizeof(uint8_t);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct get_size_helper<std::chrono::time_point<T>> {
|
||||
static constexpr size_t value(const std::chrono::time_point<T>&) {
|
||||
return sizeof(typename std::chrono::time_point<T>::rep);
|
||||
}
|
||||
};
|
||||
|
||||
template <class tuple_type>
|
||||
inline size_t get_tuple_size(const tuple_type& obj, int_<0>) {
|
||||
constexpr size_t idx = std::tuple_size<tuple_type>::value-1;
|
||||
return get_size(std::get<idx>(obj));
|
||||
}
|
||||
|
||||
template <class tuple_type, size_t pos>
|
||||
inline size_t get_tuple_size(const tuple_type& obj, int_<pos>) {
|
||||
constexpr size_t idx = std::tuple_size<tuple_type>::value-pos-1;
|
||||
size_t acc = get_size(std::get<idx>(obj));
|
||||
|
||||
// recur
|
||||
return acc+get_tuple_size(obj, int_<pos-1>());
|
||||
}
|
||||
|
||||
template <class ...T>
|
||||
struct get_size_helper<std::tuple<T...>> {
|
||||
static size_t value(const std::tuple<T...>& obj) {
|
||||
return get_tuple_size(obj, int_<sizeof...(T)-1>());
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct get_size_helper {
|
||||
static size_t value(const T&) { return sizeof(T); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline size_t get_size(const T& obj) {
|
||||
return detail::get_size_helper<T>::value(obj);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <class T>
|
||||
class serialize_helper;
|
||||
|
||||
template <class T>
|
||||
void serializer(const T& obj, Blob::iterator&);
|
||||
|
||||
template <class tuple_type>
|
||||
inline void serialize_tuple(const tuple_type& obj, Blob::iterator& res, int_<0>) {
|
||||
constexpr size_t idx = std::tuple_size<tuple_type>::value-1;
|
||||
serializer(std::get<idx>(obj), res);
|
||||
}
|
||||
|
||||
template <class tuple_type, size_t pos>
|
||||
inline void serialize_tuple(const tuple_type& obj, Blob::iterator& res, int_<pos>) {
|
||||
constexpr size_t idx = std::tuple_size<tuple_type>::value-pos-1;
|
||||
serializer(std::get<idx>(obj), res);
|
||||
|
||||
// recur
|
||||
serialize_tuple(obj, res, int_<pos-1>());
|
||||
}
|
||||
|
||||
template <class... T>
|
||||
struct serialize_helper<std::tuple<T...>> {
|
||||
static void apply(const std::tuple<T...>& obj, Blob::iterator& res) {
|
||||
detail::serialize_tuple(obj, res, detail::int_<sizeof...(T)-1>());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <>
|
||||
struct serialize_helper<std::string> {
|
||||
static void apply(const std::string& obj, Blob::iterator& res) {
|
||||
// store the number of elements of this vector at the beginning
|
||||
if (obj.length() > std::numeric_limits<serialized_size_t>::max())
|
||||
throw std::length_error("string is too long");
|
||||
serializer(static_cast<serialized_size_t>(obj.length()), res);
|
||||
for(const auto& cur : obj) { serializer(cur, res); }
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct serialize_helper<std::vector<T>> {
|
||||
static void apply(const std::vector<T>& obj, Blob::iterator& res) {
|
||||
// store the number of elements of this vector at the beginning
|
||||
if (obj.size() > std::numeric_limits<serialized_size_t>::max())
|
||||
throw std::length_error("vector is too large");
|
||||
serializer(static_cast<serialized_size_t>(obj.size()), res);
|
||||
for(const auto& cur : obj) { serializer(cur, res); }
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct serialize_helper<std::chrono::time_point<T>> {
|
||||
static void apply(const std::chrono::time_point<T>& obj, Blob::iterator& res) {
|
||||
serializer(obj.time_since_epoch().count(), res);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct serialize_helper {
|
||||
static void apply(const T& obj, Blob::iterator& res) {
|
||||
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(&obj);
|
||||
std::copy(ptr,ptr+sizeof(T),res);
|
||||
res+=sizeof(T);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <class T>
|
||||
inline void serializer(const T& obj, Blob::iterator& res) {
|
||||
serialize_helper<T>::apply(obj,res);
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
|
||||
template <class T>
|
||||
inline void serialize(const T& obj, Blob& res) {
|
||||
|
||||
size_t offset = res.size();
|
||||
size_t size = get_size(obj);
|
||||
res.resize(res.size() + size);
|
||||
|
||||
Blob::iterator it = res.begin()+offset;
|
||||
detail::serializer(obj,it);
|
||||
if (res.begin() + offset + size != it)
|
||||
throw std::logic_error("error serializing object");
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <class T>
|
||||
struct deserialize_helper;
|
||||
|
||||
template <class T>
|
||||
struct deserialize_helper {
|
||||
static T apply(Blob::const_iterator& begin,
|
||||
Blob::const_iterator end) {
|
||||
if (begin+sizeof(T)>end)
|
||||
throw std::length_error("error deserializing object");
|
||||
T val;
|
||||
std::copy(begin, begin+sizeof(T), reinterpret_cast<uint8_t*>(&val));
|
||||
begin+=sizeof(T);
|
||||
return val;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct deserialize_helper<std::chrono::time_point<T>> {
|
||||
static std::chrono::time_point<T> apply(Blob::const_iterator& begin,
|
||||
Blob::const_iterator end) {
|
||||
return std::chrono::time_point<T>(typename T::duration(deserialize_helper<typename T::rep>::apply(begin,end)));
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct deserialize_helper<std::vector<T>> {
|
||||
static std::vector<T> apply(Blob::const_iterator& begin,
|
||||
Blob::const_iterator end)
|
||||
{
|
||||
// retrieve the number of elements
|
||||
serialized_size_t size = deserialize_helper<serialized_size_t>::apply(begin,end);
|
||||
|
||||
std::vector<T> vect(size);
|
||||
for(size_t i=0; i<size; ++i) {
|
||||
vect[i] = std::move(deserialize_helper<T>::apply(begin,end));
|
||||
}
|
||||
return vect;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct deserialize_helper<std::string> {
|
||||
static std::string apply(Blob::const_iterator& begin,
|
||||
Blob::const_iterator end)
|
||||
{
|
||||
// retrieve the number of elements
|
||||
serialized_size_t size = deserialize_helper<serialized_size_t>::apply(begin,end);
|
||||
|
||||
if (size == 0u) return std::string();
|
||||
std::string str(size,'\0');
|
||||
for(size_t i=0; i<size; ++i) {
|
||||
str.at(i) = deserialize_helper<uint8_t>::apply(begin,end);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
template <class tuple_type>
|
||||
inline void deserialize_tuple(tuple_type& obj,
|
||||
Blob::const_iterator& begin,
|
||||
Blob::const_iterator end, int_<0>) {
|
||||
constexpr size_t idx = std::tuple_size<tuple_type>::value-1;
|
||||
typedef typename std::tuple_element<idx,tuple_type>::type T;
|
||||
|
||||
std::get<idx>(obj) = std::move(deserialize_helper<T>::apply(begin, end));
|
||||
}
|
||||
|
||||
template <class tuple_type, size_t pos>
|
||||
inline void deserialize_tuple(tuple_type& obj,
|
||||
Blob::const_iterator& begin,
|
||||
Blob::const_iterator end, int_<pos>) {
|
||||
constexpr size_t idx = std::tuple_size<tuple_type>::value-pos-1;
|
||||
typedef typename std::tuple_element<idx,tuple_type>::type T;
|
||||
std::get<idx>(obj) = std::move(deserialize_helper<T>::apply(begin, end));
|
||||
|
||||
// recur
|
||||
deserialize_tuple(obj, begin, end, int_<pos-1>());
|
||||
}
|
||||
|
||||
template <class... T>
|
||||
struct deserialize_helper<std::tuple<T...>> {
|
||||
static std::tuple<T...> apply(Blob::const_iterator& begin,
|
||||
Blob::const_iterator end)
|
||||
{
|
||||
//return std::make_tuple(deserialize(begin,begin+sizeof(T),T())...);
|
||||
std::tuple<T...> ret;
|
||||
deserialize_tuple(ret, begin, end, int_<sizeof...(T)-1>());
|
||||
return ret;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T deserialize(Blob::const_iterator& begin, const Blob::const_iterator& end) {
|
||||
return detail::deserialize_helper<T>::apply(begin, end);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T deserialize(const Blob& res) {
|
||||
Blob::const_iterator it = res.begin();
|
||||
return deserialize<T>(it, res.end());
|
||||
}
|
||||
|
||||
namespace dht {
|
||||
|
||||
struct Serializable {
|
||||
/**
|
||||
* Append serialized object to res.
|
||||
*/
|
||||
virtual void pack(Blob& res) const = 0;
|
||||
Blob getPacked() const {
|
||||
Blob ret;
|
||||
pack(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read serialized object from {begin, end}.
|
||||
*/
|
||||
virtual void unpack(Blob::const_iterator& begin, Blob::const_iterator& end) = 0;
|
||||
void unpackBlob(const Blob& data) {
|
||||
auto cib = data.cbegin(), cie = data.cend();
|
||||
unpack(cib, cie);
|
||||
}
|
||||
|
||||
virtual ~Serializable() = default;
|
||||
};
|
||||
|
||||
}
|
||||
+185
-53
@@ -32,7 +32,7 @@
|
||||
|
||||
#include "infohash.h"
|
||||
#include "crypto.h"
|
||||
#include "serialize.h"
|
||||
#include <msgpack.hpp>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <netinet/in.h>
|
||||
@@ -153,12 +153,23 @@ struct ValueType {
|
||||
EditPolicy editPolicy {DEFAULT_EDIT_POLICY};
|
||||
};
|
||||
|
||||
struct ValueSerializable : public Serializable
|
||||
{
|
||||
virtual const ValueType& getType() const = 0;
|
||||
virtual void unpackValue(const Value& v);
|
||||
virtual Value packValue() const;
|
||||
};
|
||||
template <typename Type>
|
||||
Blob
|
||||
packMsg(const Type& t) {
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack(t);
|
||||
return {buffer.data(), buffer.data()+buffer.size()};
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
Type
|
||||
unpackMsg(Blob b) {
|
||||
msgpack::unpacked msg_res = msgpack::unpack((const char*)b.data(), b.size());
|
||||
return msg_res.get().as<Type>();
|
||||
}
|
||||
|
||||
msgpack::unpacked unpackMsg(Blob b);
|
||||
|
||||
/**
|
||||
* A "value" is data potentially stored on the Dht, with some metadata.
|
||||
@@ -169,7 +180,7 @@ struct ValueSerializable : public Serializable
|
||||
* Values are stored at a given InfoHash in the Dht, but also have a
|
||||
* unique ID to distinguish between values stored at the same location.
|
||||
*/
|
||||
struct Value : public Serializable
|
||||
struct Value
|
||||
{
|
||||
typedef uint64_t Id;
|
||||
static const Id INVALID_ID {0};
|
||||
@@ -222,31 +233,57 @@ struct Value : public Serializable
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Hold information about how the data is signed/encrypted.
|
||||
* Class is final because bitset have no virtual destructor.
|
||||
*/
|
||||
class ValueFlags final : public std::bitset<3> {
|
||||
public:
|
||||
using std::bitset<3>::bitset;
|
||||
ValueFlags() {}
|
||||
ValueFlags(bool sign, bool encrypted, bool have_recipient = false) : bitset<3>((sign ? 1:0) | (encrypted ? 2:0) | (have_recipient ? 4:0)) {}
|
||||
bool isSigned() const {
|
||||
return (*this)[0];
|
||||
template <typename T>
|
||||
struct Serializable
|
||||
{
|
||||
virtual const ValueType& getType() const = 0;
|
||||
virtual void unpackValue(const Value& v) {
|
||||
auto msg = msgpack::unpack((const char*)v.data.data(), v.data.size());
|
||||
msgpack::object obj = msg.get();
|
||||
obj.convert(static_cast<T*>(this));
|
||||
}
|
||||
bool isEncrypted() const {
|
||||
return (*this)[1];
|
||||
}
|
||||
bool haveRecipient() const {
|
||||
return (*this)[2];
|
||||
|
||||
virtual Value packValue() const {
|
||||
return Value {getType(), static_cast<const T&>(*this)};
|
||||
}
|
||||
virtual ~Serializable() = default;
|
||||
};
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_base_of<Serializable<T>, T>::value, T>::type* = nullptr>
|
||||
static Value pack(const T& obj)
|
||||
{
|
||||
return obj.packValue();
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<!std::is_base_of<Serializable<T>, T>::value, T>::type* = nullptr>
|
||||
static Value pack(const T& obj)
|
||||
{
|
||||
return {ValueType::USER_DATA.id, packMsg<T>(obj)};
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_base_of<Serializable<T>, T>::value, T>::type* = nullptr>
|
||||
static T unpack(const Value& v)
|
||||
{
|
||||
T msg;
|
||||
msg.unpackValue(v);
|
||||
return msg;
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<!std::is_base_of<Serializable<T>, T>::value, T>::type* = nullptr>
|
||||
static T unpack(const Value& v)
|
||||
{
|
||||
return unpackMsg<T>(v.data);
|
||||
}
|
||||
|
||||
bool isEncrypted() const {
|
||||
return flags.isEncrypted();
|
||||
return not cypher.empty();
|
||||
}
|
||||
bool isSigned() const {
|
||||
return flags.isSigned();
|
||||
return isEncrypted() or not signature.empty();
|
||||
}
|
||||
|
||||
Value() {}
|
||||
@@ -260,12 +297,14 @@ struct Value : public Serializable
|
||||
: id(id), type(t), data(std::move(data)) {}
|
||||
Value(ValueType::Id t, const uint8_t* dat_ptr, size_t dat_len, Id id = INVALID_ID)
|
||||
: id(id), type(t), data(dat_ptr, dat_ptr+dat_len) {}
|
||||
Value(ValueType::Id t, const Serializable& d, Id id = INVALID_ID)
|
||||
: id(id), type(t), data(d.getPacked()) {}
|
||||
Value(const ValueType& t, const Serializable& d, Id id = INVALID_ID)
|
||||
: id(id), type(t.id), data(d.getPacked()) {}
|
||||
Value(const ValueSerializable& d, Id id = INVALID_ID)
|
||||
: id(id), type(d.getType().id), data(d.getPacked()) {}
|
||||
|
||||
template <typename Type>
|
||||
Value(ValueType::Id t, const Type& d, Id id = INVALID_ID)
|
||||
: id(id), type(t), data(packMsg(d)) {}
|
||||
|
||||
template <typename Type>
|
||||
Value(const ValueType& t, const Type& d, Id id = INVALID_ID)
|
||||
: id(id), type(t.id), data(packMsg(d)) {}
|
||||
|
||||
/** Custom user data constructor */
|
||||
Value(const Blob& userdata) : data(userdata) {}
|
||||
@@ -273,41 +312,50 @@ struct Value : public Serializable
|
||||
Value(const uint8_t* dat_ptr, size_t dat_len) : data(dat_ptr, dat_ptr+dat_len) {}
|
||||
|
||||
Value(Value&& o) noexcept
|
||||
: id(o.id), flags(o.flags), owner(std::move(o.owner)), recipient(o.recipient),
|
||||
: id(o.id), owner(std::move(o.owner)), recipient(o.recipient),
|
||||
type(o.type), data(std::move(o.data)), seq(o.seq), signature(std::move(o.signature)), cypher(std::move(o.cypher)) {}
|
||||
|
||||
template <typename Type>
|
||||
Value(const Type& vs)
|
||||
: Value(pack<Type>(vs)) {}
|
||||
|
||||
Value(const msgpack::object& o) {
|
||||
msgpack_unpack(o);
|
||||
}
|
||||
|
||||
inline bool operator== (const Value& o) {
|
||||
return id == o.id &&
|
||||
(flags.isEncrypted() ? cypher == o.cypher :
|
||||
(isEncrypted() ? cypher == o.cypher :
|
||||
(owner == o.owner && type == o.type && data == o.data && signature == o.signature));
|
||||
}
|
||||
|
||||
void setRecipient(const InfoHash& r) {
|
||||
recipient = r;
|
||||
flags[2] = true;
|
||||
}
|
||||
|
||||
void setCypher(Blob&& c) {
|
||||
cypher = std::move(c);
|
||||
flags = {true, true, true};
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack part of the data to be signed
|
||||
* Pack part of the data to be signed (must always be done the same way)
|
||||
*/
|
||||
void packToSign(Blob& res) const;
|
||||
Blob getToSign() const;
|
||||
Blob getToSign() const {
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
msgpack_pack_to_sign(pk);
|
||||
return {buffer.data(), buffer.data()+buffer.size()};
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack part of the data to be encrypted
|
||||
*/
|
||||
void packToEncrypt(Blob& res) const;
|
||||
Blob getToEncrypt() const;
|
||||
|
||||
void pack(Blob& res) const;
|
||||
|
||||
void unpackBody(Blob::const_iterator& begin, Blob::const_iterator& end);
|
||||
virtual void unpack(Blob::const_iterator& begin, Blob::const_iterator& end);
|
||||
Blob getToEncrypt() const {
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
msgpack_pack_to_encrypt(pk);
|
||||
return {buffer.data(), buffer.data()+buffer.size()};
|
||||
}
|
||||
|
||||
/** print value for debugging */
|
||||
friend std::ostream& operator<< (std::ostream& s, const Value& v);
|
||||
@@ -318,12 +366,54 @@ struct Value : public Serializable
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack_to_sign(Packer& pk) const
|
||||
{
|
||||
pk.pack_map((user_type.empty()?0:1) + (owner?(recipient == InfoHash() ? 4 : 5):2));
|
||||
if (owner) { // isSigned
|
||||
pk.pack(std::string("seq")); pk.pack(seq);
|
||||
pk.pack(std::string("owner")); owner.msgpack_pack(pk);
|
||||
if (recipient != InfoHash()) {
|
||||
pk.pack(std::string("to")); pk.pack(recipient);
|
||||
}
|
||||
}
|
||||
pk.pack(std::string("type")); pk.pack(type);
|
||||
pk.pack(std::string("data")); pk.pack_bin(data.size());
|
||||
pk.pack_bin_body((const char*)data.data(), data.size());
|
||||
if (not user_type.empty()) {
|
||||
pk.pack(std::string("utype")); pk.pack(user_type);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack_to_encrypt(Packer& pk) const
|
||||
{
|
||||
if (isEncrypted()) {
|
||||
pk.pack_bin(cypher.size());
|
||||
pk.pack_bin_body((const char*)cypher.data(), cypher.size());
|
||||
} else {
|
||||
pk.pack_map(isSigned() ? 2 : 1);
|
||||
pk.pack(std::string("body")); msgpack_pack_to_sign(pk);
|
||||
if (isSigned()) {
|
||||
pk.pack(std::string("sig")); pk.pack_bin(signature.size());
|
||||
pk.pack_bin_body((const char*)signature.data(), signature.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& pk) const
|
||||
{
|
||||
pk.pack_map(2);
|
||||
pk.pack(std::string("id")); pk.pack(id);
|
||||
pk.pack(std::string("dat")); msgpack_pack_to_encrypt(pk);
|
||||
}
|
||||
|
||||
void msgpack_unpack(msgpack::object o);
|
||||
void msgpack_unpack_body(const msgpack::object& o);
|
||||
|
||||
Id id {INVALID_ID};
|
||||
|
||||
// data (part that is signed / encrypted)
|
||||
|
||||
ValueFlags flags {};
|
||||
|
||||
/**
|
||||
* Public key of the signer.
|
||||
*/
|
||||
@@ -342,6 +432,11 @@ struct Value : public Serializable
|
||||
ValueType::Id type {ValueType::USER_DATA.id};
|
||||
Blob data {};
|
||||
|
||||
/**
|
||||
* Custom user-defined type
|
||||
*/
|
||||
std::string user_type {};
|
||||
|
||||
/**
|
||||
* Sequence number to avoid replay attacks
|
||||
*/
|
||||
@@ -358,6 +453,45 @@ struct Value : public Serializable
|
||||
Blob cypher {};
|
||||
};
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_base_of<Value::Serializable<T>, T>::value, T>::type* = nullptr>
|
||||
Value::Filter
|
||||
getFilterSet(Value::Filter f)
|
||||
{
|
||||
return Value::Filter::chain({
|
||||
Value::TypeFilter(T::TYPE),
|
||||
T::getFilter(),
|
||||
f
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<!std::is_base_of<Value::Serializable<T>, T>::value, T>::type* = nullptr>
|
||||
Value::Filter
|
||||
getFilterSet(Value::Filter f)
|
||||
{
|
||||
return f;
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_base_of<Value::Serializable<T>, T>::value, T>::type* = nullptr>
|
||||
Value::Filter
|
||||
getFilterSet()
|
||||
{
|
||||
return Value::Filter::chain({
|
||||
Value::TypeFilter(T::TYPE),
|
||||
T::getFilter()
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<!std::is_base_of<Value::Serializable<T>, T>::value, T>::type* = nullptr>
|
||||
Value::Filter
|
||||
getFilterSet()
|
||||
{
|
||||
return Value::AllFilter();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::vector<T>
|
||||
unpackVector(const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
@@ -365,9 +499,7 @@ unpackVector(const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
ret.reserve(vals.size());
|
||||
for (const auto& v : vals) {
|
||||
try {
|
||||
T msg;
|
||||
msg.unpackValue(*v);
|
||||
ret.emplace_back(std::move(msg));
|
||||
ret.emplace_back(Value::unpack<T>(*v));
|
||||
} catch (const std::exception&) {}
|
||||
}
|
||||
return ret;
|
||||
|
||||
+1
-1
@@ -2,6 +2,7 @@ lib_LTLIBRARIES = libopendht.la
|
||||
|
||||
AM_CPPFLAGS = -I../include/opendht
|
||||
libopendht_la_CXXFLAGS = @CXXFLAGS@
|
||||
libopendht_la_LDFLAGS = @LDFLAGS@ @GNUTLS_LIBS@ @nettle_LIBS@
|
||||
|
||||
libopendht_la_SOURCES = \
|
||||
dht.cpp \
|
||||
@@ -24,6 +25,5 @@ nobase_include_HEADERS = \
|
||||
../include/opendht/crypto.h \
|
||||
../include/opendht/securedht.h \
|
||||
../include/opendht/dhtrunner.h \
|
||||
../include/opendht/serialize.h \
|
||||
../include/opendht/default_types.h \
|
||||
../include/opendht/rng.h
|
||||
+162
-41
@@ -35,14 +35,17 @@ extern "C" {
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/abstract.h>
|
||||
#include <gnutls/x509.h>
|
||||
#include <nettle/gcm.h>
|
||||
#include <nettle/aes.h>
|
||||
}
|
||||
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <random>
|
||||
#include <stdexcept>
|
||||
#include <cassert>
|
||||
|
||||
static std::uniform_int_distribution<uint8_t> rand_byte;
|
||||
|
||||
static gnutls_digest_algorithm_t get_dig_for_pub(gnutls_pubkey_t pubkey)
|
||||
{
|
||||
gnutls_digest_algorithm_t dig;
|
||||
@@ -82,6 +85,87 @@ static gnutls_digest_algorithm_t get_dig(gnutls_x509_crt_t crt)
|
||||
namespace dht {
|
||||
namespace crypto {
|
||||
|
||||
static constexpr std::array<size_t, 3> AES_LENGTHS {128/8, 192/8, 256/8};
|
||||
|
||||
size_t aesKeySize(size_t max)
|
||||
{
|
||||
unsigned aes_key_len = 0;
|
||||
for (size_t s = 0; s < AES_LENGTHS.size(); s++) {
|
||||
if (AES_LENGTHS[s] <= max)
|
||||
aes_key_len = AES_LENGTHS[s];
|
||||
else break;
|
||||
}
|
||||
return aes_key_len;
|
||||
}
|
||||
|
||||
bool aesKeySizeGood(size_t key_size)
|
||||
{
|
||||
for (auto& i : AES_LENGTHS)
|
||||
if (key_size == i)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef GCM_DIGEST_SIZE
|
||||
#define GCM_DIGEST_SIZE GCM_BLOCK_SIZE
|
||||
#endif
|
||||
|
||||
Blob
|
||||
aesEncrypt(const Blob& data, const Blob& key)
|
||||
{
|
||||
std::array<uint8_t, GCM_IV_SIZE> iv;
|
||||
{
|
||||
crypto::random_device rdev;
|
||||
std::generate_n(iv.begin(), iv.size(), std::bind(rand_byte, std::ref(rdev)));
|
||||
}
|
||||
struct gcm_aes_ctx aes;
|
||||
gcm_aes_set_key(&aes, key.size(), key.data());
|
||||
gcm_aes_set_iv(&aes, iv.size(), iv.data());
|
||||
gcm_aes_update(&aes, data.size(), data.data());
|
||||
|
||||
Blob ret(data.size() + GCM_IV_SIZE + GCM_DIGEST_SIZE);
|
||||
std::copy(iv.begin(), iv.end(), ret.begin());
|
||||
gcm_aes_encrypt(&aes, data.size(), ret.data() + GCM_IV_SIZE, data.data());
|
||||
gcm_aes_digest(&aes, GCM_DIGEST_SIZE, ret.data() + GCM_IV_SIZE + data.size());
|
||||
return ret;
|
||||
}
|
||||
|
||||
Blob
|
||||
aesDecrypt(const Blob& data, const Blob& key)
|
||||
{
|
||||
if (not aesKeySizeGood(key.size()))
|
||||
throw DecryptError("Wrong key size");
|
||||
|
||||
if (data.size() <= GCM_IV_SIZE + GCM_DIGEST_SIZE)
|
||||
throw DecryptError("Wrong data size");
|
||||
|
||||
std::array<uint8_t, GCM_DIGEST_SIZE> digest;
|
||||
|
||||
struct gcm_aes_ctx aes;
|
||||
gcm_aes_set_key(&aes, key.size(), key.data());
|
||||
gcm_aes_set_iv(&aes, GCM_IV_SIZE, data.data());
|
||||
|
||||
size_t data_sz = data.size() - GCM_IV_SIZE - GCM_DIGEST_SIZE;
|
||||
Blob ret(data_sz);
|
||||
//gcm_aes_update(&aes, data_sz, data.data() + GCM_IV_SIZE);
|
||||
gcm_aes_decrypt(&aes, data_sz, ret.data(), data.data() + GCM_IV_SIZE);
|
||||
//gcm_aes_digest(aes, GCM_DIGEST_SIZE, digest.data());
|
||||
|
||||
// TODO compute the proper digest directly from the decryption pass
|
||||
Blob ret_tmp(data_sz);
|
||||
struct gcm_aes_ctx aes_d;
|
||||
gcm_aes_set_key(&aes_d, key.size(), key.data());
|
||||
gcm_aes_set_iv(&aes_d, GCM_IV_SIZE, data.data());
|
||||
gcm_aes_update(&aes_d, ret.size() , ret.data());
|
||||
gcm_aes_encrypt(&aes_d, ret.size(), ret_tmp.data(), ret.data());
|
||||
gcm_aes_digest(&aes_d, GCM_DIGEST_SIZE, digest.data());
|
||||
|
||||
if (not std::equal(digest.begin(), digest.end(), data.end() - GCM_DIGEST_SIZE))
|
||||
throw DecryptError("Can't decrypt data");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PrivateKey::PrivateKey()
|
||||
{
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030300
|
||||
@@ -194,6 +278,19 @@ PrivateKey::sign(const Blob& data) const
|
||||
return ret;
|
||||
}
|
||||
|
||||
Blob
|
||||
PrivateKey::decryptBloc(const uint8_t* src, size_t src_size) const
|
||||
{
|
||||
const gnutls_datum_t dat {(uint8_t*)src, (unsigned)src_size};
|
||||
gnutls_datum_t out;
|
||||
int err = gnutls_privkey_decrypt_data(key, 0, &dat, &out);
|
||||
if (err != GNUTLS_E_SUCCESS)
|
||||
throw DecryptError(std::string("Can't decrypt data: ") + gnutls_strerror(err));
|
||||
Blob ret {out.data, out.data+out.size};
|
||||
gnutls_free(out.data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Blob
|
||||
PrivateKey::decrypt(const Blob& cipher) const
|
||||
{
|
||||
@@ -208,20 +305,12 @@ PrivateKey::decrypt(const Blob& cipher) const
|
||||
throw CryptoException("Must be an RSA key");
|
||||
|
||||
unsigned cypher_block_sz = key_len / 8;
|
||||
if (cipher.size() % cypher_block_sz)
|
||||
throw CryptoException("Unexpected cipher length");
|
||||
if (cipher.size() < cypher_block_sz)
|
||||
throw DecryptError("Unexpected cipher length");
|
||||
else if (cipher.size() == cypher_block_sz)
|
||||
return decryptBloc(cipher.data(), cypher_block_sz);
|
||||
|
||||
Blob ret;
|
||||
for (auto cb = cipher.cbegin(), ce = cipher.cend(); cb < ce; cb += cypher_block_sz) {
|
||||
const gnutls_datum_t dat {(uint8_t*)(&(*cb)), cypher_block_sz};
|
||||
gnutls_datum_t out;
|
||||
int err = gnutls_privkey_decrypt_data(key, 0, &dat, &out);
|
||||
if (err != GNUTLS_E_SUCCESS)
|
||||
throw DecryptError(std::string("Can't decrypt data: ") + gnutls_strerror(err));
|
||||
ret.insert(ret.end(), out.data, out.data+out.size);
|
||||
gnutls_free(out.data);
|
||||
}
|
||||
return ret;
|
||||
return aesDecrypt(Blob {cipher.begin() + cypher_block_sz, cipher.end()}, decryptBloc(cipher.data(), cypher_block_sz));
|
||||
}
|
||||
|
||||
Blob
|
||||
@@ -256,7 +345,7 @@ PrivateKey::getPublicKey() const
|
||||
|
||||
PublicKey::PublicKey(const Blob& dat) : pk(nullptr)
|
||||
{
|
||||
unpackBlob(dat);
|
||||
unpack(dat.data(), dat.size());
|
||||
}
|
||||
|
||||
PublicKey::~PublicKey()
|
||||
@@ -286,17 +375,16 @@ PublicKey::pack(Blob& b) const
|
||||
if (err != GNUTLS_E_SUCCESS)
|
||||
throw CryptoException(std::string("Could not export public key: ") + gnutls_strerror(err));
|
||||
tmp.resize(sz);
|
||||
serialize<Blob>(tmp, b);
|
||||
b.insert(b.end(), tmp.begin(), tmp.end());
|
||||
}
|
||||
|
||||
void
|
||||
PublicKey::unpack(Blob::const_iterator& begin, Blob::const_iterator& end)
|
||||
PublicKey::unpack(const uint8_t* data, size_t data_size)
|
||||
{
|
||||
Blob tmp = deserialize<Blob>(begin, end);
|
||||
if (pk)
|
||||
gnutls_pubkey_deinit(pk);
|
||||
gnutls_pubkey_init(&pk);
|
||||
const gnutls_datum_t dat {(uint8_t*)tmp.data(), (unsigned)tmp.size()};
|
||||
const gnutls_datum_t dat {(uint8_t*)data, (unsigned)data_size};
|
||||
int err = gnutls_pubkey_import(pk, &dat, GNUTLS_X509_FMT_PEM);
|
||||
if (err != GNUTLS_E_SUCCESS)
|
||||
err = gnutls_pubkey_import(pk, &dat, GNUTLS_X509_FMT_DER);
|
||||
@@ -304,6 +392,14 @@ PublicKey::unpack(Blob::const_iterator& begin, Blob::const_iterator& end)
|
||||
throw CryptoException(std::string("Could not read public key: ") + gnutls_strerror(err));
|
||||
}
|
||||
|
||||
void
|
||||
PublicKey::msgpack_unpack(msgpack::object o)
|
||||
{
|
||||
if (o.type != msgpack::type::BIN)
|
||||
throw msgpack::type_error();
|
||||
unpack((const uint8_t*)o.via.bin.ptr, o.via.bin.size);
|
||||
}
|
||||
|
||||
bool
|
||||
PublicKey::checkSignature(const Blob& data, const Blob& signature) const {
|
||||
if (!pk)
|
||||
@@ -314,6 +410,20 @@ PublicKey::checkSignature(const Blob& data, const Blob& signature) const {
|
||||
return rc >= 0;
|
||||
}
|
||||
|
||||
void
|
||||
PublicKey::encryptBloc(const uint8_t* src, size_t src_size, uint8_t* dst, size_t dst_size) const
|
||||
{
|
||||
const gnutls_datum_t key_dat {(uint8_t*)src, (unsigned)src_size};
|
||||
gnutls_datum_t encrypted;
|
||||
auto err = gnutls_pubkey_encrypt_data(pk, 0, &key_dat, &encrypted);
|
||||
if (err != GNUTLS_E_SUCCESS)
|
||||
throw CryptoException(std::string("Can't encrypt data: ") + gnutls_strerror(err));
|
||||
if (encrypted.size != dst_size)
|
||||
throw CryptoException("Unexpected cypherblock size");
|
||||
std::copy_n(encrypted.data, encrypted.size, dst);
|
||||
gnutls_free(encrypted.data);
|
||||
}
|
||||
|
||||
Blob
|
||||
PublicKey::encrypt(const Blob& data) const
|
||||
{
|
||||
@@ -327,27 +437,30 @@ PublicKey::encrypt(const Blob& data) const
|
||||
if (err != GNUTLS_PK_RSA)
|
||||
throw CryptoException("Must be an RSA key");
|
||||
|
||||
unsigned max_block_sz = key_len / 8 - 11;
|
||||
unsigned cypher_block_sz = key_len / 8;
|
||||
unsigned block_num = data.empty() ? 1 : 1 + (data.size() - 1) / max_block_sz;
|
||||
|
||||
Blob ret;
|
||||
auto eb = data.cbegin();
|
||||
auto ee = data.cend();
|
||||
for (unsigned i=0; i<block_num; i++) {
|
||||
auto blk_sz = std::min<unsigned>(ee - eb, max_block_sz);
|
||||
const gnutls_datum_t dat {(uint8_t*)&(*eb), blk_sz};
|
||||
gnutls_datum_t encrypted;
|
||||
err = gnutls_pubkey_encrypt_data(pk, 0, &dat, &encrypted);
|
||||
if (err != GNUTLS_E_SUCCESS)
|
||||
throw CryptoException(std::string("Can't encrypt data: ") + gnutls_strerror(err));
|
||||
if (encrypted.size != cypher_block_sz)
|
||||
throw CryptoException("Unexpected cypherblock size");
|
||||
ret.insert(ret.end(), encrypted.data, encrypted.data+encrypted.size);
|
||||
eb += blk_sz;
|
||||
gnutls_free(encrypted.data);
|
||||
const unsigned max_block_sz = key_len / 8 - 11;
|
||||
const unsigned cypher_block_sz = key_len / 8;
|
||||
if (data.size() <= max_block_sz) {
|
||||
Blob ret(cypher_block_sz);
|
||||
encryptBloc(data.data(), data.size(), ret.data(), cypher_block_sz);
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned aes_key_sz = aesKeySize(max_block_sz);
|
||||
if (aes_key_sz == 0)
|
||||
throw CryptoException("Key is not long enough for AES128");
|
||||
Blob key(aes_key_sz);
|
||||
{
|
||||
crypto::random_device rdev;
|
||||
std::generate_n(key.begin(), key.size(), std::bind(rand_byte, std::ref(rdev)));
|
||||
}
|
||||
auto data_encrypted = aesEncrypt(data, key);
|
||||
|
||||
Blob ret;
|
||||
ret.reserve(cypher_block_sz + data_encrypted.size());
|
||||
|
||||
ret.resize(cypher_block_sz);
|
||||
encryptBloc(key.data(), key.size(), ret.data(), cypher_block_sz);
|
||||
ret.insert(ret.end(), data_encrypted.begin(), data_encrypted.end());
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -363,7 +476,7 @@ PublicKey::getId() const
|
||||
|
||||
Certificate::Certificate(const Blob& certData) : cert(nullptr)
|
||||
{
|
||||
unpackBlob(certData);
|
||||
unpack(certData.data(), certData.size());
|
||||
}
|
||||
|
||||
Certificate&
|
||||
@@ -378,7 +491,7 @@ Certificate::operator=(Certificate&& o) noexcept
|
||||
}
|
||||
|
||||
void
|
||||
Certificate::unpack(Blob::const_iterator& begin, Blob::const_iterator& end)
|
||||
Certificate::unpack(const uint8_t* dat, size_t dat_size)
|
||||
{
|
||||
if (cert) {
|
||||
gnutls_x509_crt_deinit(cert);
|
||||
@@ -386,7 +499,7 @@ Certificate::unpack(Blob::const_iterator& begin, Blob::const_iterator& end)
|
||||
}
|
||||
gnutls_x509_crt_t* cert_list;
|
||||
unsigned cert_num;
|
||||
const gnutls_datum_t crt_dt {(uint8_t*)&(*begin), (unsigned)(end-begin)};
|
||||
const gnutls_datum_t crt_dt {(uint8_t*)dat, (unsigned)dat_size};
|
||||
int err = gnutls_x509_crt_list_import2(&cert_list, &cert_num, &crt_dt, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED);
|
||||
if (err != GNUTLS_E_SUCCESS)
|
||||
err = gnutls_x509_crt_list_import2(&cert_list, &cert_num, &crt_dt, GNUTLS_X509_FMT_DER, GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED);
|
||||
@@ -405,6 +518,14 @@ Certificate::unpack(Blob::const_iterator& begin, Blob::const_iterator& end)
|
||||
gnutls_free(cert_list);
|
||||
}
|
||||
|
||||
void
|
||||
Certificate::msgpack_unpack(msgpack::object o)
|
||||
{
|
||||
if (o.type != msgpack::type::BIN)
|
||||
throw msgpack::type_error();
|
||||
unpack((const uint8_t*)o.via.bin.ptr, o.via.bin.size);
|
||||
}
|
||||
|
||||
void
|
||||
Certificate::pack(Blob& b) const
|
||||
{
|
||||
|
||||
+15
-63
@@ -38,29 +38,14 @@ std::ostream& operator<< (std::ostream& s, const DhtMessage& v)
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
DhtMessage::pack(Blob& res) const
|
||||
{
|
||||
serialize<std::string>(service, res);
|
||||
serialize<Blob>(data, res);
|
||||
}
|
||||
|
||||
void
|
||||
DhtMessage::unpack(Blob::const_iterator& begin, Blob::const_iterator& end)
|
||||
{
|
||||
service = deserialize<std::string>(begin, end);
|
||||
data = deserialize<Blob>(begin, end);
|
||||
}
|
||||
|
||||
bool
|
||||
DhtMessage::storePolicy(InfoHash, std::shared_ptr<Value>& v, InfoHash, const sockaddr*, socklen_t)
|
||||
{
|
||||
DhtMessage request;
|
||||
try {
|
||||
request.unpackBlob(v->data);
|
||||
auto msg = unpackMsg<DhtMessage>(v->data);
|
||||
if (msg.service.empty())
|
||||
return false;
|
||||
} catch (const std::exception& e) {}
|
||||
if (request.service.empty())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -71,9 +56,7 @@ DhtMessage::ServiceFilter(std::string s)
|
||||
Value::TypeFilter(TYPE),
|
||||
[s](const Value& v) {
|
||||
try {
|
||||
auto b = v.data.cbegin(), e = v.data.cend();
|
||||
auto service = deserialize<std::string>(b, e);
|
||||
return service == s;
|
||||
return unpackMsg<DhtMessage>(v.data).service == s;
|
||||
} catch (const std::exception& e) {
|
||||
return false;
|
||||
}
|
||||
@@ -95,51 +78,20 @@ std::ostream& operator<< (std::ostream& s, const IpServiceAnnouncement& v)
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
IpServiceAnnouncement::pack(Blob& res) const
|
||||
{
|
||||
serialize<in_port_t>(getPort(), res);
|
||||
if (ss.ss_family == AF_INET) {
|
||||
auto sa4 = reinterpret_cast<const sockaddr_in*>(&ss);
|
||||
serialize<in_addr>(sa4->sin_addr, res);
|
||||
} else if (ss.ss_family == AF_INET6) {
|
||||
auto sa6 = reinterpret_cast<const sockaddr_in6*>(&ss);
|
||||
serialize<in6_addr>(sa6->sin6_addr, res);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
IpServiceAnnouncement::unpack(Blob::const_iterator& begin, Blob::const_iterator& end)
|
||||
{
|
||||
setPort(deserialize<in_port_t>(begin, end));
|
||||
size_t addr_size = end - begin;
|
||||
if (addr_size < sizeof(in_addr)) {
|
||||
ss.ss_family = 0;
|
||||
} else if (addr_size == sizeof(in_addr)) {
|
||||
auto sa4 = reinterpret_cast<sockaddr_in*>(&ss);
|
||||
sa4->sin_family = AF_INET;
|
||||
sa4->sin_addr = deserialize<in_addr>(begin, end);
|
||||
} else if (addr_size == sizeof(in6_addr)) {
|
||||
auto sa6 = reinterpret_cast<sockaddr_in6*>(&ss);
|
||||
sa6->sin6_family = AF_INET6;
|
||||
sa6->sin6_addr = deserialize<in6_addr>(begin, end);
|
||||
} else {
|
||||
throw std::runtime_error("ServiceAnnouncement parse error.");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
IpServiceAnnouncement::storePolicy(InfoHash, std::shared_ptr<Value>& v, InfoHash, const sockaddr* from, socklen_t fromlen)
|
||||
{
|
||||
IpServiceAnnouncement request {};
|
||||
request.unpackBlob(v->data);
|
||||
if (request.getPort() == 0)
|
||||
return false;
|
||||
IpServiceAnnouncement sa_addr {from, fromlen};
|
||||
sa_addr.setPort(request.getPort());
|
||||
// argument v is modified (not the value).
|
||||
v = std::make_shared<Value>(IpServiceAnnouncement::TYPE, sa_addr, v->id);
|
||||
return true;
|
||||
try {
|
||||
auto msg = unpackMsg<IpServiceAnnouncement>(v->data);
|
||||
if (msg.getPort() == 0)
|
||||
return false;
|
||||
IpServiceAnnouncement sa_addr {from, fromlen};
|
||||
sa_addr.setPort(msg.getPort());
|
||||
// argument v is modified (not the value).
|
||||
v = std::make_shared<Value>(IpServiceAnnouncement::TYPE, sa_addr, v->id);
|
||||
return true;
|
||||
} catch (const std::exception& e) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
const ValueType DhtMessage::TYPE = {1, "DHT message", std::chrono::minutes(5), DhtMessage::storePolicy, ValueType::DEFAULT_EDIT_POLICY};
|
||||
|
||||
+406
-510
File diff suppressed because it is too large.
Load diff
+32
-17
@@ -107,7 +107,9 @@ DhtRunner::run(const sockaddr_in* local4, const sockaddr_in6* local6, DhtRunner:
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
if (not pending_ops.empty())
|
||||
if (not pending_ops_prio.empty())
|
||||
return true;
|
||||
if (not pending_ops.empty() and getStatus() >= Dht::Status::Connecting)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -125,6 +127,11 @@ DhtRunner::join()
|
||||
dht_thread.join();
|
||||
if (rcv_thread.joinable())
|
||||
rcv_thread.join();
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops = decltype(pending_ops)();
|
||||
pending_ops_prio = decltype(pending_ops_prio)();
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
dht_.reset();
|
||||
@@ -142,12 +149,22 @@ DhtRunner::loop_()
|
||||
decltype(pending_ops) ops {};
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
ops = std::move(pending_ops);
|
||||
ops = std::move(pending_ops_prio);
|
||||
}
|
||||
while (not ops.empty()) {
|
||||
ops.front()(*dht_);
|
||||
ops.pop();
|
||||
}
|
||||
if (getStatus() >= Dht::Status::Connecting) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
ops = std::move(pending_ops);
|
||||
}
|
||||
while (not ops.empty()) {
|
||||
ops.front()(*dht_);
|
||||
ops.pop();
|
||||
}
|
||||
}
|
||||
|
||||
time_point wakeup {};
|
||||
{
|
||||
@@ -164,14 +181,13 @@ DhtRunner::loop_()
|
||||
}
|
||||
}
|
||||
|
||||
if (statusCb) {
|
||||
Dht::Status nstatus4 = dht_->getStatus(AF_INET);
|
||||
Dht::Status nstatus6 = dht_->getStatus(AF_INET6);
|
||||
if (nstatus4 != status4 || nstatus6 != status6) {
|
||||
status4 = nstatus4;
|
||||
status6 = nstatus6;
|
||||
Dht::Status nstatus4 = dht_->getStatus(AF_INET);
|
||||
Dht::Status nstatus6 = dht_->getStatus(AF_INET6);
|
||||
if (nstatus4 != status4 || nstatus6 != status6) {
|
||||
status4 = nstatus4;
|
||||
status6 = nstatus6;
|
||||
if (statusCb)
|
||||
statusCb(status4, status6);
|
||||
}
|
||||
}
|
||||
|
||||
return wakeup;
|
||||
@@ -193,7 +209,7 @@ DhtRunner::doRun(const sockaddr_in* sin4, const sockaddr_in6* sin6, SecureDht::C
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
#if 1
|
||||
if (sin6) {
|
||||
s6 = socket(PF_INET6, SOCK_DGRAM, 0);
|
||||
if(s6 >= 0) {
|
||||
@@ -241,13 +257,12 @@ DhtRunner::doRun(const sockaddr_in* sin4, const sockaddr_in6* sin6, SecureDht::C
|
||||
if(rc > 0) {
|
||||
fromlen = sizeof(from);
|
||||
if(s4 >= 0 && FD_ISSET(s4, &readfds))
|
||||
rc = recvfrom(s4, (char*)buf, sizeof(buf) - 1, 0, (struct sockaddr*)&from, &fromlen);
|
||||
rc = recvfrom(s4, (char*)buf, sizeof(buf), 0, (struct sockaddr*)&from, &fromlen);
|
||||
else if(s6 >= 0 && FD_ISSET(s6, &readfds))
|
||||
rc = recvfrom(s6, (char*)buf, sizeof(buf) - 1, 0, (struct sockaddr*)&from, &fromlen);
|
||||
rc = recvfrom(s6, (char*)buf, sizeof(buf), 0, (struct sockaddr*)&from, &fromlen);
|
||||
else
|
||||
break;
|
||||
if (rc > 0) {
|
||||
buf[rc] = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(sock_mtx);
|
||||
rcv.emplace_back(Blob {buf, buf+rc+1}, std::make_pair(from, fromlen));
|
||||
@@ -343,7 +358,7 @@ DhtRunner::put(InfoHash hash, const std::shared_ptr<Value>& value, Dht::DoneCall
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::put(const std::string& key, Value&& value, Dht::DoneCallback cb)
|
||||
DhtRunner::put(const std::string& key, Value&& value, Dht::DoneCallbackSimple cb)
|
||||
{
|
||||
put(InfoHash::get(key), std::forward<Value>(value), cb);
|
||||
}
|
||||
@@ -370,7 +385,7 @@ DhtRunner::putSigned(InfoHash hash, Value&& value, Dht::DoneCallback cb)
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::putSigned(const std::string& key, Value&& value, Dht::DoneCallback cb)
|
||||
DhtRunner::putSigned(const std::string& key, Value&& value, Dht::DoneCallbackSimple cb)
|
||||
{
|
||||
putSigned(InfoHash::get(key), std::forward<Value>(value), cb);
|
||||
}
|
||||
@@ -427,7 +442,7 @@ void
|
||||
DhtRunner::bootstrap(const std::vector<std::pair<sockaddr_storage, socklen_t>>& nodes)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
pending_ops_prio.emplace([=](SecureDht& dht) {
|
||||
for (auto& node : nodes)
|
||||
dht.pingNode((sockaddr*)&node.first, node.second);
|
||||
});
|
||||
@@ -438,7 +453,7 @@ void
|
||||
DhtRunner::bootstrap(const std::vector<NodeExport>& nodes)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
pending_ops_prio.emplace([=](SecureDht& dht) {
|
||||
for (auto& node : nodes)
|
||||
dht.insertNode(node);
|
||||
});
|
||||
|
||||
+16
-13
@@ -238,12 +238,8 @@ SecureDht::getCallbackFilter(GetCallback cb, Value::Filter&& filter)
|
||||
try {
|
||||
Value decrypted_val (decrypt(*v));
|
||||
if (decrypted_val.recipient == getId()) {
|
||||
if (decrypted_val.owner.checkSignature(decrypted_val.getToSign(), decrypted_val.signature)) {
|
||||
if (not filter or filter(decrypted_val))
|
||||
tmpvals.push_back(std::make_shared<Value>(std::move(decrypted_val)));
|
||||
}
|
||||
else
|
||||
DHT_WARN("Signature verification failed for %s", v->toString().c_str());
|
||||
if (not filter or filter(decrypted_val))
|
||||
tmpvals.push_back(std::make_shared<Value>(std::move(decrypted_val)));
|
||||
}
|
||||
// Ignore values belonging to other people
|
||||
} catch (const std::exception& e) {
|
||||
@@ -284,7 +280,7 @@ SecureDht::listen(const InfoHash& id, GetCallback cb, Value::Filter&& f)
|
||||
}
|
||||
|
||||
void
|
||||
SecureDht::putSigned(const InfoHash& hash, const std::shared_ptr<Value>& val, DoneCallback callback)
|
||||
SecureDht::putSigned(const InfoHash& hash, std::shared_ptr<Value> val, DoneCallback callback)
|
||||
{
|
||||
if (val->id == Value::INVALID_ID) {
|
||||
crypto::random_device rdev;
|
||||
@@ -343,17 +339,16 @@ SecureDht::putEncrypted(const InfoHash& hash, const InfoHash& to, std::shared_pt
|
||||
void
|
||||
SecureDht::sign(Value& v) const
|
||||
{
|
||||
if (v.flags.isEncrypted())
|
||||
if (v.isEncrypted())
|
||||
throw DhtException("Can't sign encrypted data.");
|
||||
v.owner = key_->getPublicKey();
|
||||
v.flags = Value::ValueFlags(true, false, v.flags[2]);
|
||||
v.signature = key_->sign(v.getToSign());
|
||||
}
|
||||
|
||||
Value
|
||||
SecureDht::encrypt(Value& v, const crypto::PublicKey& to) const
|
||||
{
|
||||
if (v.flags.isEncrypted())
|
||||
if (v.isEncrypted())
|
||||
throw DhtException("Data is already encrypted.");
|
||||
v.setRecipient(to.getId());
|
||||
sign(v);
|
||||
@@ -365,12 +360,20 @@ SecureDht::encrypt(Value& v, const crypto::PublicKey& to) const
|
||||
Value
|
||||
SecureDht::decrypt(const Value& v)
|
||||
{
|
||||
if (not v.flags.isEncrypted())
|
||||
if (not v.isEncrypted())
|
||||
throw DhtException("Data is not encrypted.");
|
||||
|
||||
auto decrypted = key_->decrypt(v.cypher);
|
||||
|
||||
Value ret {v.id};
|
||||
auto pb = decrypted.cbegin(), pe = decrypted.cend();
|
||||
ret.unpackBody(pb, pe);
|
||||
auto msg = msgpack::unpack((const char*)decrypted.data(), decrypted.size());
|
||||
ret.msgpack_unpack_body(msg.get());
|
||||
|
||||
if (ret.recipient != getId())
|
||||
throw crypto::DecryptError("Recipient mismatch");
|
||||
if (not ret.owner.checkSignature(ret.getToSign(), ret.signature))
|
||||
throw crypto::DecryptError("Signature mismatch");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
+78
-80
@@ -38,11 +38,17 @@ namespace dht {
|
||||
std::ostream& operator<< (std::ostream& s, const Value& v)
|
||||
{
|
||||
s << "Value[id:" << std::hex << v.id << std::dec << " ";
|
||||
if (v.flags.isSigned())
|
||||
if (v.isSigned())
|
||||
s << "signed (v" << v.seq << ") ";
|
||||
if (v.flags.isEncrypted())
|
||||
if (v.isEncrypted())
|
||||
s << "encrypted ";
|
||||
else {
|
||||
else if (v.isSigned()) {
|
||||
if (v.recipient == InfoHash())
|
||||
s << "signed (v" << v.seq << ") ";
|
||||
else
|
||||
s << "decrypted ";
|
||||
}
|
||||
if (not v.isEncrypted()) {
|
||||
if (v.type == IpServiceAnnouncement::TYPE.id) {
|
||||
s << IpServiceAnnouncement(v.data);
|
||||
} else if (v.type == CERTIFICATE_TYPE.id) {
|
||||
@@ -57,7 +63,7 @@ std::ostream& operator<< (std::ostream& s, const Value& v)
|
||||
s << "Data (type: " << v.type << " ): ";
|
||||
s << std::hex;
|
||||
for (size_t i=0; i<v.data.size(); i++)
|
||||
s << std::setfill('0') << std::setw(2) << (unsigned)v.data[i];
|
||||
s << std::setfill('0') << std::setw(2) << (unsigned)v.data[i] << " ";
|
||||
s << std::dec;
|
||||
}
|
||||
}
|
||||
@@ -68,62 +74,45 @@ std::ostream& operator<< (std::ostream& s, const Value& v)
|
||||
const ValueType ValueType::USER_DATA = {0, "User Data"};
|
||||
|
||||
|
||||
void
|
||||
Value::packToSign(Blob& res) const
|
||||
{
|
||||
res.push_back(flags.to_ulong());
|
||||
if (flags.isEncrypted()) {
|
||||
res.insert(res.end(), cypher.begin(), cypher.end());
|
||||
} else {
|
||||
if (flags.isSigned()) {
|
||||
serialize<decltype(seq)>(seq, res);
|
||||
owner.pack(res);
|
||||
if (flags.haveRecipient())
|
||||
res.insert(res.end(), recipient.begin(), recipient.end());
|
||||
msgpack::unpacked
|
||||
unpackMsg(Blob b) {
|
||||
return msgpack::unpack((const char*)b.data(), b.size());
|
||||
}
|
||||
|
||||
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)
|
||||
continue;
|
||||
if (o.key.as<std::string>() == key) {
|
||||
return &o.val;
|
||||
}
|
||||
serialize<ValueType::Id>(type, res);
|
||||
serialize<Blob>(data, res);
|
||||
}
|
||||
}
|
||||
|
||||
Blob
|
||||
Value::getToSign() const
|
||||
{
|
||||
Blob ret;
|
||||
packToSign(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack part of the data to be encrypted
|
||||
*/
|
||||
void
|
||||
Value::packToEncrypt(Blob& res) const
|
||||
{
|
||||
packToSign(res);
|
||||
if (!flags.isEncrypted() && flags.isSigned())
|
||||
serialize<Blob>(signature, res);
|
||||
}
|
||||
|
||||
Blob
|
||||
Value::getToEncrypt() const
|
||||
{
|
||||
Blob ret;
|
||||
packToEncrypt(ret);
|
||||
return ret;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
Value::pack(Blob& res) const
|
||||
Value::msgpack_unpack(msgpack::object o)
|
||||
{
|
||||
serialize<Id>(id, res);
|
||||
packToEncrypt(res);
|
||||
if (o.type != msgpack::type::MAP) throw msgpack::type_error();
|
||||
if (o.via.map.size < 2) throw msgpack::type_error();
|
||||
|
||||
if (auto rid = findMapValue(o, "id")) {
|
||||
id = rid->as<Id>();
|
||||
} else
|
||||
throw msgpack::type_error();
|
||||
|
||||
if (auto rdat = findMapValue(o, "dat")) {
|
||||
msgpack_unpack_body(*rdat);
|
||||
} else
|
||||
throw msgpack::type_error();
|
||||
}
|
||||
|
||||
void
|
||||
Value::unpackBody(Blob::const_iterator& begin, Blob::const_iterator& end)
|
||||
Value::msgpack_unpack_body(const msgpack::object& o)
|
||||
{
|
||||
// clear optional fields
|
||||
owner = {};
|
||||
recipient = {};
|
||||
cypher.clear();
|
||||
@@ -131,39 +120,48 @@ Value::unpackBody(Blob::const_iterator& begin, Blob::const_iterator& end)
|
||||
data.clear();
|
||||
type = 0;
|
||||
|
||||
flags = {deserialize<uint8_t>(begin, end)};
|
||||
if (flags.isEncrypted()) {
|
||||
cypher = {begin, end};
|
||||
begin = end;
|
||||
if (o.type == msgpack::type::BIN) {
|
||||
auto dat = o.as<std::vector<char>>();
|
||||
cypher = {dat.begin(), dat.end()};
|
||||
} else {
|
||||
if(flags.isSigned()) {
|
||||
seq = deserialize<decltype(seq)>(begin, end);
|
||||
owner.unpack(begin, end);
|
||||
if (flags.haveRecipient())
|
||||
recipient = deserialize<InfoHash>(begin, end);
|
||||
if (o.type != msgpack::type::MAP)
|
||||
throw msgpack::type_error();
|
||||
auto rbody = findMapValue(o, "body");
|
||||
if (not rbody)
|
||||
throw msgpack::type_error();
|
||||
|
||||
if (auto rdata = findMapValue(*rbody, "data")) {
|
||||
auto dat = rdata->as<std::vector<char>>();
|
||||
data = {dat.begin(), dat.end()};
|
||||
} else
|
||||
throw msgpack::type_error();
|
||||
|
||||
if (auto rtype = findMapValue(*rbody, "type")) {
|
||||
type = rtype->as<ValueType::Id>();
|
||||
} else
|
||||
throw msgpack::type_error();
|
||||
|
||||
if (auto rutype = findMapValue(*rbody, "utype")) {
|
||||
user_type = rutype->as<std::string>();
|
||||
}
|
||||
|
||||
if (auto rowner = findMapValue(*rbody, "owner")) {
|
||||
if (auto rseq = findMapValue(*rbody, "seq"))
|
||||
seq = rseq->as<decltype(seq)>();
|
||||
else
|
||||
throw msgpack::type_error();
|
||||
owner.msgpack_unpack(*rowner);
|
||||
if (auto rrecipient = findMapValue(*rbody, "to")) {
|
||||
recipient = rrecipient->as<InfoHash>();
|
||||
}
|
||||
|
||||
if (auto rsig = findMapValue(o, "sig")) {
|
||||
auto dat = rsig->as<std::vector<char>>();
|
||||
signature = {dat.begin(), dat.end()};
|
||||
} else
|
||||
throw msgpack::type_error();
|
||||
}
|
||||
type = deserialize<ValueType::Id>(begin, end);
|
||||
data = deserialize<Blob>(begin, end);
|
||||
if (flags.isSigned())
|
||||
signature = deserialize<Blob>(begin, end);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Value::unpack(Blob::const_iterator& begin, Blob::const_iterator& end)
|
||||
{
|
||||
id = deserialize<Id>(begin, end);
|
||||
unpackBody(begin, end);
|
||||
}
|
||||
|
||||
void
|
||||
ValueSerializable::unpackValue(const Value& v) {
|
||||
unpackBlob(v.data);
|
||||
}
|
||||
|
||||
Value
|
||||
ValueSerializable::packValue() const {
|
||||
return Value {getType(), *this};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
add_executable (dhtnode dhtnode.cpp tools_common.h)
|
||||
add_executable (dhtscanner dhtscanner.cpp tools_common.h)
|
||||
add_executable (dhtchat dhtchat.cpp tools_common.h)
|
||||
|
||||
target_link_libraries (dhtnode LINK_PUBLIC opendht gnutls)
|
||||
target_link_libraries (dhtscanner LINK_PUBLIC opendht gnutls)
|
||||
target_link_libraries (dhtchat LINK_PUBLIC opendht gnutls)
|
||||
+2
-2
@@ -57,8 +57,8 @@ main(int argc, char **argv)
|
||||
DhtRunner dht;
|
||||
dht.run(params.port, dht::crypto::generateIdentity("DHT Chat Node"), true);
|
||||
|
||||
if (not params.bootstrap.empty())
|
||||
dht.bootstrap(params.bootstrap[0].c_str(), params.bootstrap[1].c_str());
|
||||
if (not params.bootstrap.first.empty())
|
||||
dht.bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str());
|
||||
|
||||
std::cout << "OpenDht node " << dht.getNodeId() << " running on port " << params.port << std::endl;
|
||||
std::cout << "Public key ID " << dht.getId() << std::endl;
|
||||
|
||||
+181
-173
@@ -59,194 +59,202 @@ void print_node_info(const DhtRunner& dht, const dht_params& params) {
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
auto params = parseArgs(argc, argv);
|
||||
if (params.help) {
|
||||
print_usage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: remove with GnuTLS >= 3.3
|
||||
int rc = gnutls_global_init();
|
||||
if (rc != GNUTLS_E_SUCCESS)
|
||||
throw std::runtime_error(std::string("Error initializing GnuTLS: ")+gnutls_strerror(rc));
|
||||
|
||||
dht::crypto::Identity crt {};
|
||||
if (params.generate_identity) {
|
||||
auto ca_tmp = dht::crypto::generateIdentity("DHT Node CA");
|
||||
crt = dht::crypto::generateIdentity("DHT Node", ca_tmp);
|
||||
}
|
||||
|
||||
DhtRunner dht;
|
||||
dht.run(params.port, crt, true, params.is_bootstrap_node);
|
||||
|
||||
if (not params.bootstrap.empty()) {
|
||||
std::cout << "Bootstrap: " << params.bootstrap[0] << ":" << params.bootstrap[1] << std::endl;
|
||||
dht.bootstrap(params.bootstrap[0].c_str(), params.bootstrap[1].c_str());
|
||||
}
|
||||
|
||||
print_node_info(dht, params);
|
||||
std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl << std::endl;
|
||||
|
||||
bool do_log {false};
|
||||
while (true)
|
||||
{
|
||||
std::cout << ">> ";
|
||||
std::string line;
|
||||
std::getline(std::cin, line);
|
||||
std::istringstream iss(line);
|
||||
std::string op, idstr, value;
|
||||
iss >> op >> idstr;
|
||||
|
||||
if (std::cin.eof() || op == "x" || op == "q" || op == "exit" || op == "quit") {
|
||||
break;
|
||||
} else if (op == "h" || op == "help") {
|
||||
std::cout << "OpenDht command line interface (CLI)" << std::endl;
|
||||
std::cout << "Possible commands:" << std::endl;
|
||||
std::cout << " h, help Print this help message." << std::endl;
|
||||
std::cout << " q, quit Quit the program." << std::endl;
|
||||
std::cout << " log Print the full DHT log." << std::endl;
|
||||
|
||||
std::cout << std::endl << "Node information:" << std::endl;
|
||||
std::cout << " ll Print basic information and stats about the current node." << std::endl;
|
||||
std::cout << " ls Print basic information about current searches." << std::endl;
|
||||
std::cout << " ld Print basic information about currenty stored values on this node." << std::endl;
|
||||
std::cout << " lr Print the full current routing table of this node" << std::endl;
|
||||
|
||||
std::cout << std::endl << "Operations on the DHT:" << std::endl;
|
||||
std::cout << " g [key] Get values at [key]." << std::endl;
|
||||
std::cout << " l [key] Listen for value changes at [key]." << std::endl;
|
||||
std::cout << " p [key] [str] Put string value at [key]." << std::endl;
|
||||
std::cout << " s [key] [str] Put string value at [key], signed with our generated private key." << std::endl;
|
||||
std::cout << " e [key] [dest] [str] Put string value at [key], encrypted for [dest] with its public key (if found)." << std::endl;
|
||||
std::cout << std::endl;
|
||||
continue;
|
||||
} else if (op == "ll") {
|
||||
print_node_info(dht, params);
|
||||
unsigned good4, dubious4, cached4, incoming4;
|
||||
unsigned good6, dubious6, cached6, incoming6;
|
||||
dht.getNodesStats(AF_INET, &good4, &dubious4, &cached4, &incoming4);
|
||||
dht.getNodesStats(AF_INET6, &good6, &dubious6, &cached6, &incoming6);
|
||||
std::cout << "IPv4 nodes : " << good4 << " good, " << dubious4 << " dubious, " << incoming4 << " incoming." << std::endl;
|
||||
std::cout << "IPv6 nodes : " << good6 << " good, " << dubious6 << " dubious, " << incoming6 << " incoming." << std::endl;
|
||||
continue;
|
||||
} else if (op == "lr") {
|
||||
std::cout << "IPv4 routing table:" << std::endl;
|
||||
std::cout << dht.getRoutingTablesLog(AF_INET) << std::endl;
|
||||
std::cout << "IPv6 routing table:" << std::endl;
|
||||
std::cout << dht.getRoutingTablesLog(AF_INET6) << std::endl;
|
||||
continue;
|
||||
} else if (op == "ld") {
|
||||
std::cout << dht.getStorageLog() << std::endl;
|
||||
continue;
|
||||
} else if (op == "ls") {
|
||||
std::cout << "Searches:" << std::endl;
|
||||
std::cout << dht.getSearchesLog() << std::endl;
|
||||
continue;
|
||||
} else if (op == "la") {
|
||||
std::cout << "Reported public addresses:" << std::endl;
|
||||
auto addrs = dht.getPublicAddressStr();
|
||||
for (const auto& addr : addrs)
|
||||
std::cout << addr << std::endl;
|
||||
continue;
|
||||
} else if (op == "log") {
|
||||
do_log = !do_log;
|
||||
if (do_log)
|
||||
enableLogging(dht);
|
||||
else
|
||||
disableLogging(dht);
|
||||
continue;
|
||||
try {
|
||||
auto params = parseArgs(argc, argv);
|
||||
if (params.help) {
|
||||
print_usage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (op.empty())
|
||||
continue;
|
||||
// TODO: remove with GnuTLS >= 3.3
|
||||
int rc = gnutls_global_init();
|
||||
if (rc != GNUTLS_E_SUCCESS)
|
||||
throw std::runtime_error(std::string("Error initializing GnuTLS: ")+gnutls_strerror(rc));
|
||||
|
||||
dht::InfoHash id {idstr};
|
||||
static const std::set<std::string> VALID_OPS {"g", "l", "p", "s", "e", "a"};
|
||||
if (VALID_OPS.find(op) == VALID_OPS.cend()) {
|
||||
std::cout << "Unknown command: " << op << std::endl;
|
||||
std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl;
|
||||
continue;
|
||||
dht::crypto::Identity crt {};
|
||||
if (params.generate_identity) {
|
||||
auto ca_tmp = dht::crypto::generateIdentity("DHT Node CA");
|
||||
crt = dht::crypto::generateIdentity("DHT Node", ca_tmp);
|
||||
}
|
||||
static constexpr dht::InfoHash INVALID_ID {};
|
||||
if (id == INVALID_ID) {
|
||||
std::cout << "Syntax error: invalid InfoHash." << std::endl;
|
||||
continue;
|
||||
|
||||
dht.run(params.port, crt, true, params.is_bootstrap_node);
|
||||
|
||||
if (params.log)
|
||||
enableLogging(dht);
|
||||
|
||||
if (not params.bootstrap.first.empty()) {
|
||||
std::cout << "Bootstrap: " << params.bootstrap.first << ":" << params.bootstrap.second << std::endl;
|
||||
dht.bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str());
|
||||
}
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
if (op == "g") {
|
||||
dht.get(id, [start](std::shared_ptr<Value> value) {
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
std::cout << "Get: found value (after " << print_dt(now-start) << "s)" << std::endl;
|
||||
std::cout << "\t" << *value << std::endl;
|
||||
return true;
|
||||
}, [start](bool ok) {
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
std::cout << "Get: " << (ok ? "completed" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
|
||||
});
|
||||
}
|
||||
else if (op == "l") {
|
||||
std::cout << id << std::endl;
|
||||
dht.listen(id, [](std::shared_ptr<Value> value) {
|
||||
std::cout << "Listen: found value:" << std::endl;
|
||||
std::cout << "\t" << *value << std::endl;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
else if (op == "p") {
|
||||
std::string v;
|
||||
iss >> v;
|
||||
dht.put(id, dht::Value {
|
||||
dht::ValueType::USER_DATA.id,
|
||||
std::vector<uint8_t> {v.begin(), v.end()}
|
||||
}, [start](bool ok) {
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
std::cout << "Put: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
|
||||
});
|
||||
}
|
||||
else if (op == "s") {
|
||||
if (not params.generate_identity) {
|
||||
print_id_req();
|
||||
print_node_info(dht, params);
|
||||
std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl << std::endl;
|
||||
|
||||
while (true)
|
||||
{
|
||||
std::cout << ">> ";
|
||||
std::string line;
|
||||
std::getline(std::cin, line);
|
||||
std::istringstream iss(line);
|
||||
std::string op, idstr, value;
|
||||
iss >> op >> idstr;
|
||||
|
||||
if (std::cin.eof() || op == "x" || op == "q" || op == "exit" || op == "quit") {
|
||||
break;
|
||||
} else if (op == "h" || op == "help") {
|
||||
std::cout << "OpenDht command line interface (CLI)" << std::endl;
|
||||
std::cout << "Possible commands:" << std::endl;
|
||||
std::cout << " h, help Print this help message." << std::endl;
|
||||
std::cout << " q, quit Quit the program." << std::endl;
|
||||
std::cout << " log Print the full DHT log." << std::endl;
|
||||
|
||||
std::cout << std::endl << "Node information:" << std::endl;
|
||||
std::cout << " ll Print basic information and stats about the current node." << std::endl;
|
||||
std::cout << " ls Print basic information about current searches." << std::endl;
|
||||
std::cout << " ld Print basic information about currenty stored values on this node." << std::endl;
|
||||
std::cout << " lr Print the full current routing table of this node" << std::endl;
|
||||
|
||||
std::cout << std::endl << "Operations on the DHT:" << std::endl;
|
||||
std::cout << " g [key] Get values at [key]." << std::endl;
|
||||
std::cout << " l [key] Listen for value changes at [key]." << std::endl;
|
||||
std::cout << " p [key] [str] Put string value at [key]." << std::endl;
|
||||
std::cout << " s [key] [str] Put string value at [key], signed with our generated private key." << std::endl;
|
||||
std::cout << " e [key] [dest] [str] Put string value at [key], encrypted for [dest] with its public key (if found)." << std::endl;
|
||||
std::cout << std::endl;
|
||||
continue;
|
||||
} else if (op == "ll") {
|
||||
print_node_info(dht, params);
|
||||
unsigned good4, dubious4, cached4, incoming4;
|
||||
unsigned good6, dubious6, cached6, incoming6;
|
||||
dht.getNodesStats(AF_INET, &good4, &dubious4, &cached4, &incoming4);
|
||||
dht.getNodesStats(AF_INET6, &good6, &dubious6, &cached6, &incoming6);
|
||||
std::cout << "IPv4 nodes : " << good4 << " good, " << dubious4 << " dubious, " << incoming4 << " incoming." << std::endl;
|
||||
std::cout << "IPv6 nodes : " << good6 << " good, " << dubious6 << " dubious, " << incoming6 << " incoming." << std::endl;
|
||||
continue;
|
||||
} else if (op == "lr") {
|
||||
std::cout << "IPv4 routing table:" << std::endl;
|
||||
std::cout << dht.getRoutingTablesLog(AF_INET) << std::endl;
|
||||
std::cout << "IPv6 routing table:" << std::endl;
|
||||
std::cout << dht.getRoutingTablesLog(AF_INET6) << std::endl;
|
||||
continue;
|
||||
} else if (op == "ld") {
|
||||
std::cout << dht.getStorageLog() << std::endl;
|
||||
continue;
|
||||
} else if (op == "ls") {
|
||||
std::cout << "Searches:" << std::endl;
|
||||
std::cout << dht.getSearchesLog() << std::endl;
|
||||
continue;
|
||||
} else if (op == "la") {
|
||||
std::cout << "Reported public addresses:" << std::endl;
|
||||
auto addrs = dht.getPublicAddressStr();
|
||||
for (const auto& addr : addrs)
|
||||
std::cout << addr << std::endl;
|
||||
continue;
|
||||
} else if (op == "log") {
|
||||
params.log = !params.log;
|
||||
if (params.log)
|
||||
enableLogging(dht);
|
||||
else
|
||||
disableLogging(dht);
|
||||
continue;
|
||||
}
|
||||
std::string v;
|
||||
iss >> v;
|
||||
dht.putSigned(id, dht::Value {
|
||||
dht::ValueType::USER_DATA.id,
|
||||
std::vector<uint8_t> {v.begin(), v.end()}
|
||||
}, [start](bool ok) {
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
std::cout << "Put signed: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
|
||||
});
|
||||
}
|
||||
else if (op == "e") {
|
||||
if (not params.generate_identity) {
|
||||
print_id_req();
|
||||
|
||||
if (op.empty())
|
||||
continue;
|
||||
|
||||
dht::InfoHash id {idstr};
|
||||
static const std::set<std::string> VALID_OPS {"g", "l", "p", "s", "e", "a"};
|
||||
if (VALID_OPS.find(op) == VALID_OPS.cend()) {
|
||||
std::cout << "Unknown command: " << op << std::endl;
|
||||
std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl;
|
||||
continue;
|
||||
}
|
||||
std::string tostr;
|
||||
std::string v;
|
||||
iss >> tostr >> v;
|
||||
dht.putEncrypted(id, InfoHash(tostr), dht::Value {
|
||||
dht::ValueType::USER_DATA.id,
|
||||
std::vector<uint8_t> {v.begin(), v.end()}
|
||||
}, [start](bool ok) {
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
std::cout << "Put encrypted: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
|
||||
});
|
||||
}
|
||||
else if (op == "a") {
|
||||
in_port_t port;
|
||||
iss >> port;
|
||||
dht.put(id, dht::Value {dht::IpServiceAnnouncement::TYPE.id, dht::IpServiceAnnouncement(port)}, [start](bool ok) {
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
std::cout << "Announce: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
|
||||
});
|
||||
static constexpr dht::InfoHash INVALID_ID {};
|
||||
if (id == INVALID_ID) {
|
||||
std::cout << "Syntax error: invalid InfoHash." << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
if (op == "g") {
|
||||
dht.get(id, [start](std::shared_ptr<Value> value) {
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
std::cout << "Get: found value (after " << print_dt(now-start) << "s)" << std::endl;
|
||||
std::cout << "\t" << *value << std::endl;
|
||||
return true;
|
||||
}, [start](bool ok) {
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
std::cout << "Get: " << (ok ? "completed" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
|
||||
});
|
||||
}
|
||||
else if (op == "l") {
|
||||
std::cout << id << std::endl;
|
||||
dht.listen(id, [](std::shared_ptr<Value> value) {
|
||||
std::cout << "Listen: found value:" << std::endl;
|
||||
std::cout << "\t" << *value << std::endl;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
else if (op == "p") {
|
||||
std::string v;
|
||||
iss >> v;
|
||||
dht.put(id, dht::Value {
|
||||
dht::ValueType::USER_DATA.id,
|
||||
std::vector<uint8_t> {v.begin(), v.end()}
|
||||
}, [start](bool ok) {
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
std::cout << "Put: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
|
||||
});
|
||||
}
|
||||
else if (op == "s") {
|
||||
if (not params.generate_identity) {
|
||||
print_id_req();
|
||||
continue;
|
||||
}
|
||||
std::string v;
|
||||
iss >> v;
|
||||
dht.putSigned(id, dht::Value {
|
||||
dht::ValueType::USER_DATA.id,
|
||||
std::vector<uint8_t> {v.begin(), v.end()}
|
||||
}, [start](bool ok) {
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
std::cout << "Put signed: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
|
||||
});
|
||||
}
|
||||
else if (op == "e") {
|
||||
if (not params.generate_identity) {
|
||||
print_id_req();
|
||||
continue;
|
||||
}
|
||||
std::string tostr;
|
||||
std::string v;
|
||||
iss >> tostr >> v;
|
||||
dht.putEncrypted(id, InfoHash(tostr), dht::Value {
|
||||
dht::ValueType::USER_DATA.id,
|
||||
std::vector<uint8_t> {v.begin(), v.end()}
|
||||
}, [start](bool ok) {
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
std::cout << "Put encrypted: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
|
||||
});
|
||||
}
|
||||
else if (op == "a") {
|
||||
in_port_t port;
|
||||
iss >> port;
|
||||
dht.put(id, dht::Value {dht::IpServiceAnnouncement::TYPE.id, dht::IpServiceAnnouncement(port)}, [start](bool ok) {
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
std::cout << "Announce: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << std::endl << "Stopping node..." << std::endl;
|
||||
} catch(const std::exception&e) {
|
||||
std::cout << std::endl << e.what() << std::endl;
|
||||
}
|
||||
|
||||
std::cout << std::endl << "Stopping node..." << std::endl;
|
||||
dht.join();
|
||||
gnutls_global_deinit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -90,8 +90,8 @@ main(int argc, char **argv)
|
||||
DhtRunner dht;
|
||||
dht.run(params.port, crt_tmp, true, [](dht::Dht::Status /* ipv4 */, dht::Dht::Status /* ipv6 */) {});
|
||||
|
||||
if (not params.bootstrap.empty())
|
||||
dht.bootstrap(params.bootstrap[0].c_str(), params.bootstrap[1].c_str());
|
||||
if (not params.bootstrap.first.empty())
|
||||
dht.bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str());
|
||||
|
||||
std::cout << "OpenDht node " << dht.getNodeId() << " running on port " << params.port << std::endl;
|
||||
std::cout << "Scanning network..." << std::endl;
|
||||
|
||||
+29
-14
@@ -112,26 +112,37 @@ print_dt(DT d) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Split string with delimiter
|
||||
* Split "[host]:port" or "host:port" to pair<"host", "port">.
|
||||
*/
|
||||
std::vector<std::string>
|
||||
split(const std::string& s, char delim) {
|
||||
std::vector<std::string> elems;
|
||||
std::stringstream ss(s);
|
||||
std::string item;
|
||||
while (std::getline(ss, item, delim))
|
||||
elems.emplace_back(std::move(item));
|
||||
return elems;
|
||||
std::pair<std::string, std::string>
|
||||
splitPort(const std::string& s) {
|
||||
if (s.empty())
|
||||
return {};
|
||||
if (s[0] == '[') {
|
||||
std::size_t closure = s.find_first_of(']');
|
||||
std::size_t found = s.find_last_of(':');
|
||||
if (closure == std::string::npos)
|
||||
return {s, ""};
|
||||
if (found == std::string::npos or found < closure)
|
||||
return {s.substr(1,closure-1), ""};
|
||||
return {s.substr(1,closure-1), s.substr(found+1)};
|
||||
}
|
||||
std::size_t found = s.find_last_of(':');
|
||||
std::size_t first = s.find_first_of(':');
|
||||
if (found == std::string::npos or found != first)
|
||||
return {s, ""};
|
||||
return {s.substr(0,found), s.substr(found+1)};
|
||||
}
|
||||
|
||||
static const constexpr in_port_t DHT_DEFAULT_PORT = 4222;
|
||||
|
||||
struct dht_params {
|
||||
bool help {false}; // print help and exit
|
||||
bool log {false};
|
||||
in_port_t port {DHT_DEFAULT_PORT};
|
||||
bool is_bootstrap_node {false};
|
||||
bool generate_identity {false};
|
||||
std::vector<std::string> bootstrap {};
|
||||
std::pair<std::string, std::string> bootstrap {};
|
||||
};
|
||||
|
||||
static const struct option long_options[] = {
|
||||
@@ -139,6 +150,7 @@ static const struct option long_options[] = {
|
||||
{"port", required_argument, nullptr, 'p'},
|
||||
{"bootstrap", optional_argument, nullptr, 'b'},
|
||||
{"identity", no_argument , nullptr, 'i'},
|
||||
{"verbose", no_argument , nullptr, 'v'},
|
||||
{nullptr, 0, nullptr, 0}
|
||||
};
|
||||
|
||||
@@ -146,7 +158,7 @@ dht_params
|
||||
parseArgs(int argc, char **argv) {
|
||||
dht_params params;
|
||||
int opt;
|
||||
while ((opt = getopt_long(argc, argv, ":hip:b:", long_options, nullptr)) != -1) {
|
||||
while ((opt = getopt_long(argc, argv, ":hivp:b:", long_options, nullptr)) != -1) {
|
||||
switch (opt) {
|
||||
case 'p': {
|
||||
int port_arg = atoi(optarg);
|
||||
@@ -158,9 +170,9 @@ parseArgs(int argc, char **argv) {
|
||||
break;
|
||||
case 'b':
|
||||
if (optarg) {
|
||||
params.bootstrap = split((optarg[0] == '=') ? optarg+1 : optarg, ':');
|
||||
if (params.bootstrap.size() == 1)
|
||||
params.bootstrap.emplace_back(std::to_string(DHT_DEFAULT_PORT));
|
||||
params.bootstrap = splitPort((optarg[0] == '=') ? optarg+1 : optarg);
|
||||
if (not params.bootstrap.first.empty() and params.bootstrap.second.empty())
|
||||
params.bootstrap.second = std::to_string(DHT_DEFAULT_PORT);
|
||||
}
|
||||
else
|
||||
params.is_bootstrap_node = true;
|
||||
@@ -168,6 +180,9 @@ parseArgs(int argc, char **argv) {
|
||||
case 'h':
|
||||
params.help = true;
|
||||
break;
|
||||
case 'v':
|
||||
params.log = true;
|
||||
break;
|
||||
case 'i':
|
||||
params.generate_identity = true;
|
||||
break;
|
||||
|
||||
Reference in new issue
Block a user