From 986117567531d586ebf24da410f11d08c265bc75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20B=C3=A9raud?= Date: Wed, 15 Apr 2015 15:51:02 -0400 Subject: [PATCH 1/6] types: new ValueSerializable API, separate default types --- include/opendht.h | 1 + include/opendht/default_types.h | 235 ++++++++++++++++++++++++++++++++ include/opendht/dhtrunner.h | 59 ++++++++ include/opendht/securedht.h | 3 + include/opendht/value.h | 159 +++++++-------------- src/Makefile.am | 6 +- src/default_types.cpp | 151 ++++++++++++++++++++ src/securedht.cpp | 9 +- src/value.cpp | 97 +------------ tools/dhtchat.cpp | 80 ++++------- 10 files changed, 542 insertions(+), 258 deletions(-) create mode 100644 include/opendht/default_types.h create mode 100644 src/default_types.cpp diff --git a/include/opendht.h b/include/opendht.h index fb49b559..61830def 100644 --- a/include/opendht.h +++ b/include/opendht.h @@ -35,3 +35,4 @@ #include "opendht/infohash.h" #include "opendht/securedht.h" #include "opendht/dhtrunner.h" +#include "opendht/default_types.h" diff --git a/include/opendht/default_types.h b/include/opendht/default_types.h new file mode 100644 index 00000000..22556208 --- /dev/null +++ b/include/opendht/default_types.h @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2014 Savoir-Faire Linux Inc. + * Author : Adrien Béraud + * + * 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&, 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.isEncrypted() and 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::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(b, e); + im_message = std::string(b, e); + } + + 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(service, data); + } + virtual void unpack(Blob::const_iterator& b, Blob::const_iterator& e) { + service = deserialize(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(ice_data, data); + } + virtual void unpack(Blob::const_iterator& b, Blob::const_iterator& e) { + ice_data = deserialize(b, e); + } + virtual void unpackValue(const Value& v) { + id = v.id; + ValueSerializable::unpackValue(v); + } + + 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(&ss)->sin_port); + } + void setPort(in_port_t p) { + reinterpret_cast(&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&, InfoHash, const sockaddr*, socklen_t); + + /** print value for debugging */ + friend std::ostream& operator<< (std::ostream&, const IpServiceAnnouncement&); + +private: + sockaddr_storage ss; +}; + + +const std::array, 5> +DEFAULT_TYPES +{ + ValueType::USER_DATA, + DhtMessage::TYPE, + ImMessage::TYPE, + IceCandidates::TYPE, + ContactInvite::TYPE +}; + +const std::array, 1> +DEFAULT_INSECURE_TYPES +{ + IpServiceAnnouncement::TYPE +}; + +} diff --git a/include/opendht/dhtrunner.h b/include/opendht/dhtrunner.h index 0cde00b1..9a78d75d 100644 --- a/include/opendht/dhtrunner.h +++ b/include/opendht/dhtrunner.h @@ -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 + void get(InfoHash hash, std::function&&)> cb) + { + get(hash, [=](const std::vector>& vals) { + return cb(unpackVector(vals)); + }, + T::getFilter()); + } + template + void get(InfoHash hash, std::function cb) + { + get(hash, [=](const std::vector>& 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 listen(InfoHash hash, Dht::GetCallback vcb, Value::Filter f = Value::AllFilter()); std::future listen(const std::string& key, Dht::GetCallback vcb, Value::Filter f = Value::AllFilter()); + + template + std::future listen(InfoHash hash, std::function&&)> cb) + { + return listen(hash, [=](const std::vector>& vals) { + return cb(unpackVector(vals)); + }, + T::getFilter()); + } + template + std::future listen(InfoHash hash, std::function cb, Value::Filter f = Value::AllFilter()) + { + return listen(hash, [=](const std::vector>& 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 token); diff --git a/include/opendht/securedht.h b/include/opendht/securedht.h index 3311200f..ea457115 100644 --- a/include/opendht/securedht.h +++ b/include/opendht/securedht.h @@ -36,6 +36,7 @@ #include #include #include +#include namespace dht { @@ -133,6 +134,8 @@ private: std::shared_ptr certificate_ {}; std::map> nodesCertificates_ {}; + + std::uniform_int_distribution rand_id {}; }; const ValueType CERTIFICATE_TYPE = { diff --git a/include/opendht/value.h b/include/opendht/value.h index 09e97fd3..5129da78 100644 --- a/include/opendht/value.h +++ b/include/opendht/value.h @@ -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 Filter; + class Filter : public std::function { + using std::function::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 l) { + const std::vector 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 +std::vector +unpackVector(const std::vector>& vals) { + std::vector ret; + ret.reserve(vals.size()); + for (const auto& v : vals) { + try { + T msg; + msg.unpackBlob(v->data); + 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(&ss)->sin_port); - } - void setPort(in_port_t p) { - reinterpret_cast(&ss)->sin_port = htons(p); - } - - sockaddr_storage getPeerAddr() const { - return ss; - } - - static const ValueType TYPE; - static bool storePolicy(InfoHash, std::shared_ptr&, 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(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&, 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(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, 2> -DEFAULT_TYPES -{ - ValueType::USER_DATA, - DhtMessage::TYPE, -}; - -const std::array, 1> -DEFAULT_INSECURE_TYPES -{ - IpServiceAnnouncement::TYPE -}; + return ret; +} } diff --git a/src/Makefile.am b/src/Makefile.am index f5c25162..b0ed802c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 diff --git a/src/default_types.cpp b/src/default_types.cpp new file mode 100644 index 00000000..b8163de3 --- /dev/null +++ b/src/default_types.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2014 Savoir-Faire Linux Inc. + * Author : Adrien Béraud + * + * 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(service, res); + serialize(data, res); +} + +void +DhtMessage::unpack(Blob::const_iterator& begin, Blob::const_iterator& end) +{ + service = deserialize(begin, end); + data = deserialize(begin, end); +} + +bool +DhtMessage::storePolicy(InfoHash, std::shared_ptr& 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(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(getPort(), res); + if (ss.ss_family == AF_INET) { + auto sa4 = reinterpret_cast(&ss); + serialize(sa4->sin_addr, res); + } else if (ss.ss_family == AF_INET6) { + auto sa6 = reinterpret_cast(&ss); + serialize(sa6->sin6_addr, res); + } +} + +void +IpServiceAnnouncement::unpack(Blob::const_iterator& begin, Blob::const_iterator& end) +{ + setPort(deserialize(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(&ss); + sa4->sin_family = AF_INET; + sa4->sin_addr = deserialize(begin, end); + } else if (addr_size == sizeof(in6_addr)) { + auto sa6 = reinterpret_cast(&ss); + sa6->sin6_family = AF_INET6; + sa6->sin6_addr = deserialize(begin, end); + } else { + throw std::runtime_error("ServiceAnnouncement parse error."); + } +} + +bool +IpServiceAnnouncement::storePolicy(InfoHash, std::shared_ptr& 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(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)}; + +} diff --git a/src/securedht.cpp b/src/securedht.cpp index a82355b6..331caeb6 100644 --- a/src/securedht.cpp +++ b/src/securedht.cpp @@ -30,6 +30,8 @@ #include "securedht.h" +#include "default_types.h" + extern "C" { #include #include @@ -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& 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(id.data()); + std::random_device rdev; + val->id = rand_id(rdev); } // Check if we are already announcing a value diff --git a/src/value.cpp b/src/value.cpp index 0be144b5..08d20666 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -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(getPort(), res); - if (ss.ss_family == AF_INET) { - auto sa4 = reinterpret_cast(&ss); - serialize(sa4->sin_addr, res); - } else if (ss.ss_family == AF_INET6) { - auto sa6 = reinterpret_cast(&ss); - serialize(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(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(&ss); - sa4->sin_family = AF_INET; - sa4->sin_addr = deserialize(begin, end); - } else if (addr_size == sizeof(in6_addr)) { - auto sa6 = reinterpret_cast(&ss); - sa6->sin6_family = AF_INET6; - sa6->sin6_addr = deserialize(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& 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(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, res); - serialize(message, res); -} - -void -DhtMessage::unpack(Blob::const_iterator& begin, Blob::const_iterator& end) -{ - service = deserialize(begin, end); - message = deserialize(begin, end); -} - -bool -DhtMessage::storePolicy(InfoHash, std::shared_ptr& 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}; - } diff --git a/tools/dhtchat.cpp b/tools/dhtchat.cpp index e1681157..b42d1d58 100644 --- a/tools/dhtchat.cpp +++ b/tools/dhtchat.cpp @@ -38,13 +38,17 @@ extern "C" { #include #include #include -#include #include using namespace dht; -const std::string getDateTime(const std::chrono::system_clock::time_point& t) { +template +static double dtToDouble(DT d) { + return std::chrono::duration_cast>(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 rand_id {}; - - auto rcv_msg = [&](const std::vector>& 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(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>(dt).count() << "s) : " << msg << std::endl; - } - } catch (const std::exception& e) {} - } - return true; - }; - while (true) { std::cout << (connected ? ">> " : "> "); @@ -122,9 +97,10 @@ 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; if (not connected) { std::istringstream iss(line); @@ -135,25 +111,27 @@ main(int argc, char **argv) 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(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.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 data; - serialize(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) { + if (line == "d") { + connected = false; + continue; + } + dht.putSigned(room, dht::ImMessage(std::move(line)), [](bool ok) { //dht.cancelPut(room, id); if (not ok) std::cout << "Message publishing failed !" << std::endl; @@ -163,8 +141,6 @@ main(int argc, char **argv) std::cout << std::endl << "Stopping node..." << std::endl; dht.join(); - gnutls_global_deinit(); - return 0; } From 29ca90927b002162c9d756d5e8f12df78ace1b57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20B=C3=A9raud?= Date: Thu, 16 Apr 2015 11:35:25 -0400 Subject: [PATCH 2/6] types: fix EncryptedValue filter --- include/opendht/default_types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/opendht/default_types.h b/include/opendht/default_types.h index 22556208..dc3e9a3d 100644 --- a/include/opendht/default_types.h +++ b/include/opendht/default_types.h @@ -86,7 +86,7 @@ struct EncryptedValue : public SignedValue static Value::Filter getFilter() { return Value::Filter::chain( SignedValue::getFilter(), - [](const Value& v){ return v.isEncrypted() and v.recipient != InfoHash(); } + [](const Value& v){ return v.recipient != InfoHash(); } ); } From 61e6f395681b95fcbbd19669cd935f4f9b351e65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20B=C3=A9raud?= Date: Thu, 16 Apr 2015 11:36:10 -0400 Subject: [PATCH 3/6] types: make ImMessage optionally encrypted --- include/opendht/default_types.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/opendht/default_types.h b/include/opendht/default_types.h index dc3e9a3d..77e9e061 100644 --- a/include/opendht/default_types.h +++ b/include/opendht/default_types.h @@ -115,7 +115,12 @@ struct ImMessage : public SignedValue sent = deserialize(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; }; From f68f03dd7b9aa104527618df73784c77d52b57d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20B=C3=A9raud?= Date: Thu, 16 Apr 2015 11:36:48 -0400 Subject: [PATCH 4/6] dhtchat: allow to send encrypted messages --- tools/dhtchat.cpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/tools/dhtchat.cpp b/tools/dhtchat.cpp index b42d1d58..a016be96 100644 --- a/tools/dhtchat.cpp +++ b/tools/dhtchat.cpp @@ -102,10 +102,10 @@ main(int argc, char **argv) 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") { @@ -119,7 +119,7 @@ main(int argc, char **argv) if (msg.from != myid) std::cout << msg.from.toString() << " at " << printTime(msg.sent) << " (took " << dtToDouble(std::chrono::system_clock::now() - msg.sent) - << "s) : " << msg.im_message << std::endl; + << "s) " << (msg.to == myid ? "ENCRYPTED ":"") << ": " << msg.im_message << std::endl; return true; }); connected = true; @@ -127,15 +127,23 @@ main(int argc, char **argv) std::cout << "Unknown command. Type 'c {hash}' to join a channel" << std::endl << std::endl; } } else { - if (line == "d") { + 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; + }); } - dht.putSigned(room, dht::ImMessage(std::move(line)), [](bool ok) { - //dht.cancelPut(room, id); - if (not ok) - std::cout << "Message publishing failed !" << std::endl; - }); } } From c99f4ea6536765747a3f64b50b639efcfdca19bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20B=C3=A9raud?= Date: Thu, 16 Apr 2015 13:15:59 -0400 Subject: [PATCH 5/6] types: read recipient for IceCandidates --- include/opendht/default_types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/opendht/default_types.h b/include/opendht/default_types.h index 77e9e061..b26b4574 100644 --- a/include/opendht/default_types.h +++ b/include/opendht/default_types.h @@ -165,8 +165,8 @@ struct IceCandidates : public EncryptedValue ice_data = deserialize(b, e); } virtual void unpackValue(const Value& v) { + EncryptedValue::unpackValue(v); id = v.id; - ValueSerializable::unpackValue(v); } Value::Id id; From ee40fa2bc74f7bf47dcd2712c2b6393aa3afaf50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20B=C3=A9raud?= Date: Thu, 16 Apr 2015 13:45:49 -0400 Subject: [PATCH 6/6] values: use unpackValue in unpackVector --- include/opendht/value.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/opendht/value.h b/include/opendht/value.h index 5129da78..cfba20a7 100644 --- a/include/opendht/value.h +++ b/include/opendht/value.h @@ -366,7 +366,7 @@ unpackVector(const std::vector>& vals) { for (const auto& v : vals) { try { T msg; - msg.unpackBlob(v->data); + msg.unpackValue(*v); ret.emplace_back(std::move(msg)); } catch (const std::exception&) {} }