Merge pull request #3 from savoirfairelinux/feature/ValueSerializable

Merge new type system
This commit is contained in:
Adrien Béraud committed 2015-04-16 13:46:34 -04:00
commit 02e8638b7f
10 files changed
+562 -265

No files matched your search

+1
View File
@@ -35,3 +35,4 @@
#include "opendht/infohash.h"
#include "opendht/securedht.h"
#include "opendht/dhtrunner.h"
#include "opendht/default_types.h"
+240
View File
@@ -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
};
}
+59
View File
@@ -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);
+3
View File
@@ -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
View File
@@ -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
View File
@@ -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
+151
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}