mirror of
https://github.com/savoirfairelinux/opendht.git
synced 2026-11-08 04:21:51 +00:00
Merge pull request #3 from savoirfairelinux/feature/ValueSerializable
Merge new type system
This commit is contained in:
commit
02e8638b7f
10 files changed
+562
-265
No files matched your search
@@ -35,3 +35,4 @@
|
||||
#include "opendht/infohash.h"
|
||||
#include "opendht/securedht.h"
|
||||
#include "opendht/dhtrunner.h"
|
||||
#include "opendht/default_types.h"
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Savoir-Faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Additional permission under GNU GPL version 3 section 7:
|
||||
*
|
||||
* If you modify this program, or any covered work, by linking or
|
||||
* combining it with the OpenSSL project's OpenSSL library (or a
|
||||
* modified version of that library), containing parts covered by the
|
||||
* terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
|
||||
* grants you additional permission to convey the resulting work.
|
||||
* Corresponding Source for a non-source form of such a combination
|
||||
* shall include the source code for the parts of OpenSSL used as well
|
||||
* as that of the covered work.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "value.h"
|
||||
|
||||
namespace dht {
|
||||
|
||||
struct DhtMessage : public ValueSerializable
|
||||
{
|
||||
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;
|
||||
}
|
||||
static Value::Filter getFilter() { return {}; }
|
||||
|
||||
static bool storePolicy(InfoHash, std::shared_ptr<Value>&, InfoHash, const sockaddr*, socklen_t);
|
||||
|
||||
static Value::Filter ServiceFilter(std::string s);
|
||||
|
||||
/** print value for debugging */
|
||||
friend std::ostream& operator<< (std::ostream&, const DhtMessage&);
|
||||
|
||||
public:
|
||||
std::string service;
|
||||
Blob data;
|
||||
};
|
||||
|
||||
|
||||
struct SignedValue : public ValueSerializable
|
||||
{
|
||||
virtual void unpackValue(const Value& v) {
|
||||
from = v.owner.getId();
|
||||
ValueSerializable::unpackValue(v);
|
||||
}
|
||||
static Value::Filter getFilter() {
|
||||
return [](const Value& v){ return v.isSigned(); };
|
||||
}
|
||||
public:
|
||||
dht::InfoHash from;
|
||||
};
|
||||
|
||||
struct EncryptedValue : public SignedValue
|
||||
{
|
||||
virtual void unpackValue(const Value& v) {
|
||||
to = v.recipient;
|
||||
SignedValue::unpackValue(v);
|
||||
}
|
||||
static Value::Filter getFilter() {
|
||||
return Value::Filter::chain(
|
||||
SignedValue::getFilter(),
|
||||
[](const Value& v){ return v.recipient != InfoHash(); }
|
||||
);
|
||||
}
|
||||
|
||||
public:
|
||||
dht::InfoHash to;
|
||||
};
|
||||
|
||||
struct ImMessage : public SignedValue
|
||||
{
|
||||
ImMessage() {}
|
||||
ImMessage(std::string&& msg)
|
||||
: sent(std::chrono::system_clock::now()), im_message(std::move(msg)) {}
|
||||
|
||||
static const ValueType TYPE;
|
||||
virtual const ValueType& getType() const {
|
||||
return TYPE;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
dht::InfoHash to;
|
||||
std::chrono::system_clock::time_point sent;
|
||||
std::string im_message;
|
||||
};
|
||||
|
||||
struct ContactInvite : public EncryptedValue
|
||||
{
|
||||
ContactInvite() {}
|
||||
ContactInvite(std::string s) : service(s) {}
|
||||
|
||||
static const ValueType TYPE;
|
||||
virtual const ValueType& getType() const {
|
||||
return TYPE;
|
||||
}
|
||||
static Value::Filter getFilter() {
|
||||
return EncryptedValue::getFilter();
|
||||
}
|
||||
virtual void pack(Blob& data) const {
|
||||
serialize<std::string>(service, data);
|
||||
}
|
||||
virtual void unpack(Blob::const_iterator& b, Blob::const_iterator& e) {
|
||||
service = deserialize<std::string>(b, e);
|
||||
}
|
||||
std::string service;
|
||||
};
|
||||
|
||||
struct IceCandidates : public EncryptedValue
|
||||
{
|
||||
IceCandidates() {}
|
||||
IceCandidates(Blob ice) : ice_data(ice) {}
|
||||
|
||||
static const ValueType TYPE;
|
||||
virtual const ValueType& getType() const {
|
||||
return TYPE;
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
/* "Peer" announcement
|
||||
*/
|
||||
struct IpServiceAnnouncement : public ValueSerializable
|
||||
{
|
||||
IpServiceAnnouncement(in_port_t p = 0) {
|
||||
ss.ss_family = 0;
|
||||
setPort(p);
|
||||
}
|
||||
|
||||
IpServiceAnnouncement(const sockaddr* sa, socklen_t sa_len) {
|
||||
if (sa)
|
||||
std::copy_n((const uint8_t*)sa, sa_len, (uint8_t*)&ss);
|
||||
}
|
||||
|
||||
IpServiceAnnouncement(const Blob& b) {
|
||||
unpackBlob(b);
|
||||
}
|
||||
|
||||
virtual void pack(Blob& res) const;
|
||||
virtual void unpack(Blob::const_iterator& begin, Blob::const_iterator& end);
|
||||
|
||||
in_port_t getPort() const {
|
||||
return ntohs(reinterpret_cast<const sockaddr_in*>(&ss)->sin_port);
|
||||
}
|
||||
void setPort(in_port_t p) {
|
||||
reinterpret_cast<sockaddr_in*>(&ss)->sin_port = htons(p);
|
||||
}
|
||||
|
||||
sockaddr_storage getPeerAddr() const {
|
||||
return ss;
|
||||
}
|
||||
|
||||
static const ValueType TYPE;
|
||||
virtual const ValueType& getType() const {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
static bool storePolicy(InfoHash, std::shared_ptr<Value>&, InfoHash, const sockaddr*, socklen_t);
|
||||
|
||||
/** print value for debugging */
|
||||
friend std::ostream& operator<< (std::ostream&, const IpServiceAnnouncement&);
|
||||
|
||||
private:
|
||||
sockaddr_storage ss;
|
||||
};
|
||||
|
||||
|
||||
const std::array<std::reference_wrapper<const ValueType>, 5>
|
||||
DEFAULT_TYPES
|
||||
{
|
||||
ValueType::USER_DATA,
|
||||
DhtMessage::TYPE,
|
||||
ImMessage::TYPE,
|
||||
IceCandidates::TYPE,
|
||||
ContactInvite::TYPE
|
||||
};
|
||||
|
||||
const std::array<std::reference_wrapper<const ValueType>, 1>
|
||||
DEFAULT_INSECURE_TYPES
|
||||
{
|
||||
IpServiceAnnouncement::TYPE
|
||||
};
|
||||
|
||||
}
|
||||
@@ -60,8 +60,67 @@ public:
|
||||
void get(InfoHash hash, Dht::GetCallback vcb, Dht::DoneCallback dcb=nullptr, Value::Filter f = Value::AllFilter());
|
||||
void get(const std::string& key, Dht::GetCallback vcb, Dht::DoneCallback dcb=nullptr, Value::Filter f = Value::AllFilter());
|
||||
|
||||
template <class T>
|
||||
void get(InfoHash hash, std::function<bool(std::vector<T>&&)> cb)
|
||||
{
|
||||
get(hash, [=](const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
return cb(unpackVector<T>(vals));
|
||||
},
|
||||
T::getFilter());
|
||||
}
|
||||
template <class T>
|
||||
void get(InfoHash hash, std::function<bool(T&&)> cb)
|
||||
{
|
||||
get(hash, [=](const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
for (const auto& v : vals) {
|
||||
T msg;
|
||||
try {
|
||||
msg.unpackValue(*v);
|
||||
} catch (const std::exception&) {
|
||||
continue;
|
||||
}
|
||||
if (not cb(std::move(msg)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
T::getFilter());
|
||||
}
|
||||
|
||||
std::future<size_t> listen(InfoHash hash, Dht::GetCallback vcb, Value::Filter f = Value::AllFilter());
|
||||
std::future<size_t> listen(const std::string& key, Dht::GetCallback vcb, Value::Filter f = Value::AllFilter());
|
||||
|
||||
template <class T>
|
||||
std::future<size_t> listen(InfoHash hash, std::function<bool(std::vector<T>&&)> cb)
|
||||
{
|
||||
return listen(hash, [=](const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
return cb(unpackVector<T>(vals));
|
||||
},
|
||||
T::getFilter());
|
||||
}
|
||||
template <class 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);
|
||||
} 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
|
||||
}));
|
||||
}
|
||||
|
||||
void cancelListen(InfoHash h, size_t token);
|
||||
void cancelListen(InfoHash h, std::shared_future<size_t> token);
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
|
||||
namespace dht {
|
||||
|
||||
@@ -133,6 +134,8 @@ private:
|
||||
std::shared_ptr<crypto::Certificate> certificate_ {};
|
||||
|
||||
std::map<InfoHash, std::shared_ptr<crypto::Certificate>> nodesCertificates_ {};
|
||||
|
||||
std::uniform_int_distribution<Value::Id> rand_id {};
|
||||
};
|
||||
|
||||
const ValueType CERTIFICATE_TYPE = {
|
||||
|
||||
+49
-110
@@ -153,6 +153,13 @@ 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* A "value" is data potentially stored on the Dht, with some metadata.
|
||||
*
|
||||
@@ -167,7 +174,30 @@ struct Value : public Serializable
|
||||
typedef uint64_t Id;
|
||||
static const Id INVALID_ID {0};
|
||||
|
||||
typedef std::function<bool(const Value&)> Filter;
|
||||
class Filter : public std::function<bool(const Value&)> {
|
||||
using std::function<bool(const Value&)>::function;
|
||||
public:
|
||||
static Filter chain(Filter&& f1, Filter&& f2) {
|
||||
return [f1,f2](const Value& v){
|
||||
return f1(v) && f2(v);
|
||||
};
|
||||
}
|
||||
static Filter chain(std::initializer_list<Filter> l) {
|
||||
const std::vector<Filter> list(l.begin(), l.end());
|
||||
return [list](const Value& v){
|
||||
for (const auto& f : list)
|
||||
if (f and not f(v))
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
}
|
||||
Filter chain(Filter&& f2) {
|
||||
Filter f1 = std::move(*this);
|
||||
return [f1,f2](const Value& v){
|
||||
return f1(v) && f2(v);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
static const Filter AllFilter() {
|
||||
return [](const Value&){return true;};
|
||||
@@ -186,9 +216,9 @@ struct Value : public Serializable
|
||||
};
|
||||
}
|
||||
|
||||
static Filter chainFilters(Filter&& f1, Filter&& f2) {
|
||||
return [f1,f2](const Value& v){
|
||||
return f1(v) && f2(v);
|
||||
static Filter recipientFilter(const InfoHash& r) {
|
||||
return [r](const Value& v) {
|
||||
return v.recipient == r;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -234,6 +264,8 @@ struct Value : public Serializable
|
||||
: 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()) {}
|
||||
|
||||
/** Custom user data constructor */
|
||||
Value(const Blob& userdata) : data(userdata) {}
|
||||
@@ -326,112 +358,19 @@ struct Value : public Serializable
|
||||
Blob cypher {};
|
||||
};
|
||||
|
||||
|
||||
/* "Peer" announcement
|
||||
*/
|
||||
struct IpServiceAnnouncement : public Serializable
|
||||
{
|
||||
IpServiceAnnouncement(in_port_t p = 0) {
|
||||
ss.ss_family = 0;
|
||||
setPort(p);
|
||||
template <class T>
|
||||
std::vector<T>
|
||||
unpackVector(const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
std::vector<T> ret;
|
||||
ret.reserve(vals.size());
|
||||
for (const auto& v : vals) {
|
||||
try {
|
||||
T msg;
|
||||
msg.unpackValue(*v);
|
||||
ret.emplace_back(std::move(msg));
|
||||
} catch (const std::exception&) {}
|
||||
}
|
||||
|
||||
IpServiceAnnouncement(const sockaddr* sa, socklen_t sa_len) {
|
||||
if (sa)
|
||||
std::copy_n((const uint8_t*)sa, sa_len, (uint8_t*)&ss);
|
||||
}
|
||||
|
||||
IpServiceAnnouncement(const Blob& b) {
|
||||
unpackBlob(b);
|
||||
}
|
||||
|
||||
virtual void pack(Blob& res) const;
|
||||
virtual void unpack(Blob::const_iterator& begin, Blob::const_iterator& end);
|
||||
|
||||
in_port_t getPort() const {
|
||||
return ntohs(reinterpret_cast<const sockaddr_in*>(&ss)->sin_port);
|
||||
}
|
||||
void setPort(in_port_t p) {
|
||||
reinterpret_cast<sockaddr_in*>(&ss)->sin_port = htons(p);
|
||||
}
|
||||
|
||||
sockaddr_storage getPeerAddr() const {
|
||||
return ss;
|
||||
}
|
||||
|
||||
static const ValueType TYPE;
|
||||
static bool storePolicy(InfoHash, std::shared_ptr<Value>&, InfoHash, const sockaddr*, socklen_t);
|
||||
|
||||
/** print value for debugging */
|
||||
friend std::ostream& operator<< (std::ostream&, const IpServiceAnnouncement&);
|
||||
|
||||
private:
|
||||
sockaddr_storage ss;
|
||||
};
|
||||
|
||||
struct DhtMessage : public Serializable
|
||||
{
|
||||
enum class Service : uint16_t {
|
||||
UNDEFINED = 0,
|
||||
IM_MESSAGE,
|
||||
ICE_CANDIDATES
|
||||
};
|
||||
|
||||
DhtMessage(uint16_t s, Blob msg = {}) : service(static_cast<Service>(s)), message(msg) {}
|
||||
DhtMessage(Service s, Blob msg = {}) : service(s), message(msg) {}
|
||||
|
||||
DhtMessage(const Blob& b) {
|
||||
unpackBlob(b);
|
||||
}
|
||||
|
||||
Service getService() const {
|
||||
return service;
|
||||
}
|
||||
|
||||
const Blob& getMessage() const {
|
||||
return message;
|
||||
}
|
||||
|
||||
virtual void pack(Blob& res) const;
|
||||
virtual void unpack(Blob::const_iterator& begin, Blob::const_iterator& end);
|
||||
|
||||
static const ValueType TYPE;
|
||||
static bool storePolicy(InfoHash, std::shared_ptr<Value>&, InfoHash, const sockaddr*, socklen_t);
|
||||
|
||||
static Value::Filter ServiceFilter(Service s) {
|
||||
return Value::chainFilters(
|
||||
Value::TypeFilter(TYPE),
|
||||
[s](const Value& v) {
|
||||
try {
|
||||
auto b = v.data.cbegin(), e = v.data.cend();
|
||||
auto service = deserialize<Service>(b, e);
|
||||
return service == s;
|
||||
} catch (const std::exception& e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/** print value for debugging */
|
||||
friend std::ostream& operator<< (std::ostream&, const DhtMessage&);
|
||||
|
||||
private:
|
||||
Service service;
|
||||
Blob message;
|
||||
};
|
||||
|
||||
const std::array<std::reference_wrapper<const ValueType>, 2>
|
||||
DEFAULT_TYPES
|
||||
{
|
||||
ValueType::USER_DATA,
|
||||
DhtMessage::TYPE,
|
||||
};
|
||||
|
||||
const std::array<std::reference_wrapper<const ValueType>, 1>
|
||||
DEFAULT_INSECURE_TYPES
|
||||
{
|
||||
IpServiceAnnouncement::TYPE
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
+4
-2
@@ -9,7 +9,8 @@ libopendht_la_SOURCES = \
|
||||
value.cpp \
|
||||
crypto.cpp \
|
||||
securedht.cpp \
|
||||
dhtrunner.cpp
|
||||
dhtrunner.cpp \
|
||||
default_types.cpp
|
||||
|
||||
nobase_include_HEADERS = \
|
||||
../include/opendht.h \
|
||||
@@ -19,4 +20,5 @@ nobase_include_HEADERS = \
|
||||
../include/opendht/crypto.h \
|
||||
../include/opendht/securedht.h \
|
||||
../include/opendht/dhtrunner.h \
|
||||
../include/opendht/serialize.h
|
||||
../include/opendht/serialize.h \
|
||||
../include/opendht/default_types.h
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Savoir-Faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Additional permission under GNU GPL version 3 section 7:
|
||||
*
|
||||
* If you modify this program, or any covered work, by linking or
|
||||
* combining it with the OpenSSL project's OpenSSL library (or a
|
||||
* modified version of that library), containing parts covered by the
|
||||
* terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
|
||||
* grants you additional permission to convey the resulting work.
|
||||
* Corresponding Source for a non-source form of such a combination
|
||||
* shall include the source code for the parts of OpenSSL used as well
|
||||
* as that of the covered work.
|
||||
*/
|
||||
|
||||
#include "default_types.h"
|
||||
|
||||
namespace dht {
|
||||
|
||||
std::ostream& operator<< (std::ostream& s, const DhtMessage& v)
|
||||
{
|
||||
s << "DhtMessage: service " << v.service << std::endl;
|
||||
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);
|
||||
} catch (const std::exception& e) {}
|
||||
if (request.service.empty())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Value::Filter
|
||||
DhtMessage::ServiceFilter(std::string s)
|
||||
{
|
||||
return Value::Filter::chain(
|
||||
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;
|
||||
} catch (const std::exception& e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
std::ostream& operator<< (std::ostream& s, const IpServiceAnnouncement& v)
|
||||
{
|
||||
s << "Peer: ";
|
||||
s << "port " << v.getPort();
|
||||
|
||||
if (v.ss.ss_family == AF_INET || v.ss.ss_family == AF_INET6) {
|
||||
char hbuf[NI_MAXHOST];
|
||||
if (getnameinfo((sockaddr*)&v.ss, sizeof(v.ss), hbuf, sizeof(hbuf), nullptr, 0, NI_NUMERICHOST) == 0) {
|
||||
s << " addr " << std::string(hbuf, strlen(hbuf));
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
const ValueType DhtMessage::TYPE = {1, "DHT message", std::chrono::minutes(5), DhtMessage::storePolicy, ValueType::DEFAULT_EDIT_POLICY};
|
||||
const ValueType IpServiceAnnouncement::TYPE = {2, "Internet Service Announcement", std::chrono::minutes(15), IpServiceAnnouncement::storePolicy, ValueType::DEFAULT_EDIT_POLICY};
|
||||
const ValueType ImMessage::TYPE = {3, "IM message", std::chrono::minutes(5)};
|
||||
const ValueType ContactInvite::TYPE = {4, "Service contact invitation", std::chrono::hours(24*7)};
|
||||
const ValueType IceCandidates::TYPE = {5, "ICE candidates", std::chrono::minutes(5)};
|
||||
|
||||
}
|
||||
+5
-4
@@ -30,6 +30,8 @@
|
||||
|
||||
#include "securedht.h"
|
||||
|
||||
#include "default_types.h"
|
||||
|
||||
extern "C" {
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/abstract.h>
|
||||
@@ -208,7 +210,7 @@ SecureDht::getCallbackFilter(GetCallback cb, Value::Filter&& filter)
|
||||
// Check signed values
|
||||
else if (v->isSigned()) {
|
||||
if (v->owner.checkSignature(v->getToSign(), v->signature)) {
|
||||
if (not filter or filter(*v))
|
||||
if (not filter or filter(*v))
|
||||
tmpvals.push_back(v);
|
||||
}
|
||||
else
|
||||
@@ -242,9 +244,8 @@ void
|
||||
SecureDht::putSigned(const InfoHash& hash, const std::shared_ptr<Value>& val, DoneCallback callback)
|
||||
{
|
||||
if (val->id == Value::INVALID_ID) {
|
||||
auto id = getId();
|
||||
static_assert(sizeof(Value::Id) <= sizeof(InfoHash), "Value::Id can't be larger than InfoHash");
|
||||
val->id = *reinterpret_cast<Value::Id*>(id.data());
|
||||
std::random_device rdev;
|
||||
val->id = rand_id(rdev);
|
||||
}
|
||||
|
||||
// Check if we are already announcing a value
|
||||
|
||||
+7
-90
@@ -29,6 +29,8 @@
|
||||
*/
|
||||
|
||||
#include "value.h"
|
||||
|
||||
#include "default_types.h"
|
||||
#include "securedht.h" // print certificate ID
|
||||
|
||||
namespace dht {
|
||||
@@ -154,99 +156,14 @@ Value::unpack(Blob::const_iterator& begin, Blob::const_iterator& end)
|
||||
unpackBody(begin, end);
|
||||
}
|
||||
|
||||
std::ostream& operator<< (std::ostream& s, const IpServiceAnnouncement& v)
|
||||
{
|
||||
s << "Peer: ";
|
||||
s << "port " << v.getPort();
|
||||
|
||||
if (v.ss.ss_family == AF_INET || v.ss.ss_family == AF_INET6) {
|
||||
char hbuf[NI_MAXHOST];
|
||||
if (getnameinfo((sockaddr*)&v.ss, sizeof(v.ss), hbuf, sizeof(hbuf), nullptr, 0, NI_NUMERICHOST) == 0) {
|
||||
s << " addr " << std::string(hbuf, strlen(hbuf));
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
ValueSerializable::unpackValue(const Value& v) {
|
||||
unpackBlob(v.data);
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
Value
|
||||
ValueSerializable::packValue() const {
|
||||
return Value {getType(), *this};
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const ValueType IpServiceAnnouncement::TYPE = {2, "Internet Service Announcement", std::chrono::minutes(15), IpServiceAnnouncement::storePolicy, ValueType::DEFAULT_EDIT_POLICY};
|
||||
|
||||
std::ostream& operator<< (std::ostream& s, const DhtMessage& v)
|
||||
{
|
||||
s << "DhtMessage: service " << v.service << std::endl;
|
||||
s.write((const char*)v.message.data(), v.message.size());
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
DhtMessage::pack(Blob& res) const
|
||||
{
|
||||
serialize<Service>(service, res);
|
||||
serialize<Blob>(message, res);
|
||||
}
|
||||
|
||||
void
|
||||
DhtMessage::unpack(Blob::const_iterator& begin, Blob::const_iterator& end)
|
||||
{
|
||||
service = deserialize<Service>(begin, end);
|
||||
message = deserialize<Blob>(begin, end);
|
||||
}
|
||||
|
||||
bool
|
||||
DhtMessage::storePolicy(InfoHash, std::shared_ptr<Value>& v, InfoHash, const sockaddr*, socklen_t)
|
||||
{
|
||||
DhtMessage request {v->data};
|
||||
if (request.service == Service::UNDEFINED)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
const ValueType DhtMessage::TYPE = {1, "DHT message", std::chrono::minutes(5), DhtMessage::storePolicy, ValueType::DEFAULT_EDIT_POLICY};
|
||||
|
||||
}
|
||||
+43
-59
@@ -38,13 +38,17 @@ extern "C" {
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <random>
|
||||
|
||||
#include <ctime>
|
||||
|
||||
using namespace dht;
|
||||
|
||||
const std::string getDateTime(const std::chrono::system_clock::time_point& t) {
|
||||
template <class DT>
|
||||
static double dtToDouble(DT d) {
|
||||
return std::chrono::duration_cast<std::chrono::duration<double>>(d).count();
|
||||
}
|
||||
|
||||
const std::string printTime(const std::chrono::system_clock::time_point& t) {
|
||||
auto now = std::chrono::system_clock::to_time_t(t);
|
||||
struct tm tstruct = *localtime(&now);
|
||||
char buf[80];
|
||||
@@ -67,16 +71,11 @@ main(int argc, char **argv)
|
||||
if (!port)
|
||||
port = 4222;
|
||||
|
||||
int rc = gnutls_global_init();
|
||||
if (rc != GNUTLS_E_SUCCESS)
|
||||
if (int rc = gnutls_global_init())
|
||||
throw std::runtime_error(std::string("Error initializing GnuTLS: ")+gnutls_strerror(rc));
|
||||
|
||||
auto ca_tmp = dht::crypto::generateIdentity("DHT Node CA");
|
||||
auto crt_tmp = dht::crypto::generateIdentity("DHT Node", ca_tmp);
|
||||
|
||||
DhtRunner dht;
|
||||
dht.run(port, crt_tmp, true, [](dht::Dht::Status /* ipv4 */, dht::Dht::Status /* ipv6 */) {
|
||||
});
|
||||
dht.run(port, dht::crypto::generateIdentity("DHT Chat Node"), true);
|
||||
|
||||
while (i+1 < argc) {
|
||||
dht.bootstrap(argv[i], argv[i + 1]);
|
||||
@@ -91,30 +90,6 @@ main(int argc, char **argv)
|
||||
InfoHash room;
|
||||
const InfoHash myid = dht.getId();
|
||||
|
||||
std::random_device rdev;
|
||||
std::uniform_int_distribution<Value::Id> rand_id {};
|
||||
|
||||
auto rcv_msg = [&](const std::vector<std::shared_ptr<Value>>& values) {
|
||||
for (const auto& a : values) {
|
||||
try {
|
||||
if (a->owner.getId() == myid)
|
||||
continue;
|
||||
if (a->isEncrypted())
|
||||
std::cout << a->owner.getId().toString() << " : [encrypted]" << std::endl;
|
||||
else {
|
||||
Blob msg_raw = dht::DhtMessage(a->data).getMessage();
|
||||
auto b = msg_raw.cbegin();
|
||||
auto e = msg_raw.cend();
|
||||
auto date = deserialize<std::chrono::system_clock::time_point>(b, e);
|
||||
auto dt = std::chrono::system_clock::now() - date;
|
||||
std::string msg {b, e};
|
||||
std::cout << a->owner.getId().toString() << " at " << getDateTime(date) << " (took " << std::chrono::duration_cast<std::chrono::duration<double>>(dt).count() << "s) : " << msg << std::endl;
|
||||
}
|
||||
} catch (const std::exception& e) {}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
while (true)
|
||||
{
|
||||
std::cout << (connected ? ">> " : "> ");
|
||||
@@ -122,49 +97,58 @@ main(int argc, char **argv)
|
||||
std::getline(std::cin, line);
|
||||
static constexpr dht::InfoHash INVALID_ID {};
|
||||
|
||||
if (std::cin.eof()) {
|
||||
if (std::cin.eof())
|
||||
break;
|
||||
}
|
||||
if (line.empty())
|
||||
continue;
|
||||
|
||||
std::istringstream iss(line);
|
||||
std::string op, idstr;
|
||||
iss >> op >> idstr;
|
||||
if (not connected) {
|
||||
std::istringstream iss(line);
|
||||
std::string op, idstr, p;
|
||||
iss >> op >> idstr;
|
||||
if (op == "x" || op == "q" || op == "exit" || op == "quit")
|
||||
break;
|
||||
else if (op == "c") {
|
||||
room = InfoHash(idstr);
|
||||
if (room == INVALID_ID) {
|
||||
std::cout << "Syntax error: invalid InfoHash." << std::endl;
|
||||
continue;
|
||||
room = InfoHash::get(idstr);
|
||||
std::cout << "Joining h(" << idstr << ") = " << room << std::endl;
|
||||
}
|
||||
dht.listen(room, rcv_msg, dht::DhtMessage::ServiceFilter(dht::DhtMessage::Service::IM_MESSAGE));
|
||||
|
||||
dht.listen<dht::ImMessage>(room, [&](dht::ImMessage&& msg) {
|
||||
if (msg.from != myid)
|
||||
std::cout << msg.from.toString() << " at " << printTime(msg.sent)
|
||||
<< " (took " << dtToDouble(std::chrono::system_clock::now() - msg.sent)
|
||||
<< "s) " << (msg.to == myid ? "ENCRYPTED ":"") << ": " << msg.im_message << std::endl;
|
||||
return true;
|
||||
});
|
||||
connected = true;
|
||||
} else {
|
||||
std::cout << "Unknown command. Type 'c {hash}' to join a channel" << std::endl << std::endl;
|
||||
}
|
||||
} else {
|
||||
auto id = rand_id(rdev);
|
||||
std::vector<uint8_t> data;
|
||||
serialize<std::chrono::system_clock::time_point>(std::chrono::system_clock::now(), data);
|
||||
data.insert(data.end(), line.begin(), line.end());
|
||||
dht.putSigned(room, dht::Value {
|
||||
dht::DhtMessage::TYPE,
|
||||
dht::DhtMessage(
|
||||
dht::DhtMessage::Service::IM_MESSAGE,
|
||||
data
|
||||
),
|
||||
id
|
||||
}, [id](bool ok) {
|
||||
//dht.cancelPut(room, id);
|
||||
if (not ok)
|
||||
std::cout << "Message publishing failed !" << std::endl;
|
||||
});
|
||||
if (op == "d") {
|
||||
connected = false;
|
||||
continue;
|
||||
} else if (op == "e") {
|
||||
std::getline(iss, line);
|
||||
dht.putEncrypted(room, InfoHash(idstr), dht::ImMessage(std::move(line)), [](bool ok) {
|
||||
//dht.cancelPut(room, id);
|
||||
if (not ok)
|
||||
std::cout << "Message publishing failed !" << std::endl;
|
||||
});
|
||||
} else {
|
||||
dht.putSigned(room, dht::ImMessage(std::move(op)), [](bool ok) {
|
||||
//dht.cancelPut(room, id);
|
||||
if (not ok)
|
||||
std::cout << "Message publishing failed !" << std::endl;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << std::endl << "Stopping node..." << std::endl;
|
||||
dht.join();
|
||||
|
||||
gnutls_global_deinit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user