Merge pull request #14 from savoirfairelinux/v2

Release version 2
This commit is contained in:
Adrien Béraud committed 2015-08-19 16:21:09 -04:00
commit e1b9256f19
23 files changed
+1421 -1402

No files matched your search

+83
View File
@@ -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)
+48
View File
@@ -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)
+2
View File
@@ -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)
+51 -8
View File
@@ -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);
}
}
+47 -45
View File
@@ -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
View File
@@ -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
View File
@@ -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 {};
+20
View File
@@ -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());
}
};
}
+1 -1
View File
@@ -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);
}
-339
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+32 -17
View File
@@ -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
View File
@@ -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
View File
@@ -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};
}
}
+8
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+2 -2
View File
@@ -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
View File
@@ -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;