mirror of
https://github.com/savoirfairelinux/opendht.git
synced 2026-11-08 12:28:42 +00:00
329 lines
9.1 KiB
C++
329 lines
9.1 KiB
C++
/*
|
|
* 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 "infohash.h"
|
|
#include "crypto.h"
|
|
#include "serialize.h"
|
|
|
|
#include <sys/socket.h>
|
|
#include <netdb.h>
|
|
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <bitset>
|
|
#include <vector>
|
|
#include <iostream>
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#include <memory>
|
|
|
|
#include <cstdarg>
|
|
|
|
namespace dht {
|
|
|
|
/**
|
|
* Wrapper for logging methods
|
|
*/
|
|
struct LogMethod {
|
|
LogMethod() = default;
|
|
|
|
template<typename T>
|
|
LogMethod( T&& t) : func(std::forward<T>(t)) {}
|
|
|
|
void operator()(char const* format, ...) const {
|
|
va_list args;
|
|
va_start(args, format);
|
|
func(format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
void logPrintable(const uint8_t *buf, size_t buflen) const {
|
|
std::string buf_clean(buflen, '\0');
|
|
for (size_t i=0; i<buflen; i++)
|
|
buf_clean[i] = buf[i] >= 32 && buf[i] <= 126 ? buf[i] : '.';
|
|
(*this)("%s", buf_clean.c_str());
|
|
}
|
|
private:
|
|
std::function<void(char const*, va_list)> func;
|
|
};
|
|
|
|
/**
|
|
* Dummy function used to disable logging
|
|
*/
|
|
constexpr int NOLOG(char const*, va_list) { return 0; }
|
|
|
|
|
|
class Value;
|
|
|
|
typedef std::function<bool(InfoHash, std::shared_ptr<Value>&, InfoHash, const sockaddr*, socklen_t)> StorePolicy;
|
|
typedef std::function<bool(InfoHash, const std::shared_ptr<Value>&, std::shared_ptr<Value>&, InfoHash, const sockaddr*, socklen_t)> EditPolicy;
|
|
|
|
struct ValueType {
|
|
typedef uint16_t Id;
|
|
ValueType () {}
|
|
|
|
ValueType (Id id, std::string name, time_t e = 60 * 60)
|
|
: id(id), name(name), expiration(e) {}
|
|
|
|
ValueType (Id id, std::string name, time_t e, StorePolicy&& sp, EditPolicy&& ep)
|
|
: id(id), name(name), expiration(e), storePolicy(std::move(sp)), editPolicy(std::move(ep)) {}
|
|
|
|
virtual ~ValueType() {}
|
|
|
|
bool operator==(const ValueType& o) {
|
|
return id == o.id;
|
|
}
|
|
|
|
// Generic value type
|
|
static const ValueType USER_DATA;
|
|
|
|
static bool DEFAULT_STORE_POLICY(InfoHash, std::shared_ptr<Value>&, InfoHash, const sockaddr*, socklen_t) {
|
|
return true;
|
|
}
|
|
static bool DEFAULT_EDIT_POLICY(InfoHash, const std::shared_ptr<Value>&, std::shared_ptr<Value>&, InfoHash, const sockaddr*, socklen_t) {
|
|
return false;
|
|
}
|
|
|
|
Id id {0};
|
|
std::string name {};
|
|
time_t expiration {60 * 60};
|
|
StorePolicy storePolicy {DEFAULT_STORE_POLICY};
|
|
EditPolicy editPolicy {DEFAULT_EDIT_POLICY};
|
|
};
|
|
|
|
/**
|
|
* A "value" is data potentially stored on the Dht, with some metadata.
|
|
*
|
|
* It can be an IP:port announced for a service, a public key, or any kind of
|
|
* light user-defined data (recommended: less than 512 bytes).
|
|
*
|
|
* Values are stored at a given InfoHash in the Dht, but also have a
|
|
* unique ID to distinguish between values stored at the same location.
|
|
*/
|
|
struct Value : public Serializable
|
|
{
|
|
typedef uint64_t Id;
|
|
static const Id INVALID_ID {0};
|
|
|
|
typedef std::function<bool(const Value&)> Filter;
|
|
|
|
static const Filter AllFilter() {
|
|
return [](const Value&){return true;};
|
|
}
|
|
|
|
static Filter TypeFilter(const ValueType& t) {
|
|
const auto tid = t.id;
|
|
return [tid](const Value& v) {
|
|
return v.type == tid;
|
|
};
|
|
}
|
|
|
|
static Filter chainFilters(Filter& f1, Filter& f2) {
|
|
return [f1,f2](const Value& v){
|
|
return f1(v) && f2(v);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Hold information about how the data is signed/encrypted.
|
|
* Class is final because bitset have no virtual destructor.
|
|
*/
|
|
class ValueFlags final : public std::bitset<3> {
|
|
public:
|
|
using std::bitset<3>::bitset;
|
|
ValueFlags() {}
|
|
ValueFlags(bool sign, bool encrypted, bool have_recipient = false) : bitset<3>((sign ? 1:0) | (encrypted ? 2:0) | (have_recipient ? 4:0)) {}
|
|
bool isSigned() const {
|
|
return (*this)[0];
|
|
}
|
|
bool isEncrypted() const {
|
|
return (*this)[1];
|
|
}
|
|
bool haveRecipient() const {
|
|
return (*this)[2];
|
|
}
|
|
};
|
|
|
|
bool isEncrypted() const {
|
|
return flags.isEncrypted();
|
|
}
|
|
bool isSigned() const {
|
|
return flags.isSigned();
|
|
}
|
|
|
|
Value() {}
|
|
|
|
Value (Id id) : id(id) {}
|
|
|
|
/** Generic constructor */
|
|
Value(ValueType::Id t, const Blob& data, Id id = INVALID_ID)
|
|
: id(id), type(t), data(data) {}
|
|
Value(ValueType::Id t, const Serializable& d, Id id = INVALID_ID)
|
|
: id(id), type(t), data(d.getPacked()) {}
|
|
Value(const ValueType& t, const Serializable& d, Id id = INVALID_ID)
|
|
: id(id), type(t.id), data(d.getPacked()) {}
|
|
|
|
/** Custom user data constructor */
|
|
Value(const Blob& userdata) : data(userdata) {}
|
|
Value(Blob&& userdata) : data(std::move(userdata)) {}
|
|
|
|
Value(Value&& o) noexcept
|
|
: id(o.id), flags(o.flags), owner(std::move(o.owner)), recipient(o.recipient),
|
|
type(o.type), data(std::move(o.data)), seq(o.seq), signature(std::move(o.signature)), cypher(std::move(o.cypher)) {}
|
|
|
|
inline bool operator== (const Value& o) {
|
|
return id == o.id &&
|
|
(flags.isEncrypted() ? cypher == o.cypher :
|
|
(owner == o.owner && type == o.type && data == o.data && signature == o.signature));
|
|
}
|
|
|
|
void setRecipient(const InfoHash& r) {
|
|
recipient = r;
|
|
flags[2] = true;
|
|
}
|
|
|
|
void setCypher(Blob&& c) {
|
|
cypher = std::move(c);
|
|
flags = {true, true, true};
|
|
}
|
|
|
|
/**
|
|
* Pack part of the data to be signed
|
|
*/
|
|
void packToSign(Blob& res) const;
|
|
Blob getToSign() const;
|
|
|
|
/**
|
|
* Pack part of the data to be encrypted
|
|
*/
|
|
void packToEncrypt(Blob& res) const;
|
|
Blob getToEncrypt() const;
|
|
|
|
void pack(Blob& res) const;
|
|
|
|
void unpackBody(Blob::const_iterator& begin, Blob::const_iterator& end);
|
|
virtual void unpack(Blob::const_iterator& begin, Blob::const_iterator& end);
|
|
|
|
/** print value for debugging */
|
|
friend std::ostream& operator<< (std::ostream& s, const Value& v);
|
|
|
|
std::string toString() const {
|
|
std::stringstream ss;
|
|
ss << *this;
|
|
return ss.str();
|
|
}
|
|
|
|
Id id {INVALID_ID};
|
|
|
|
// data (part that is signed / encrypted)
|
|
|
|
ValueFlags flags {};
|
|
|
|
/**
|
|
* Public key of the signer.
|
|
*/
|
|
crypto::PublicKey owner {};
|
|
|
|
/**
|
|
* Hash of the recipient (optional).
|
|
* Should only be present for encrypted values.
|
|
* Can optionally be present for signed values.
|
|
*/
|
|
InfoHash recipient {};
|
|
|
|
/**
|
|
* Type of data.
|
|
*/
|
|
ValueType::Id type {ValueType::USER_DATA.id};
|
|
Blob data {};
|
|
|
|
/**
|
|
* Sequence number to avoid replay attacks
|
|
*/
|
|
uint16_t seq {0};
|
|
|
|
/**
|
|
* Optional signature.
|
|
*/
|
|
Blob signature {};
|
|
|
|
/**
|
|
* Hold encrypted version of the data.
|
|
*/
|
|
Blob cypher {};
|
|
};
|
|
|
|
|
|
/* "Peer" announcement
|
|
*/
|
|
struct ServiceAnnouncement : public Serializable
|
|
{
|
|
ServiceAnnouncement(in_port_t p = 0) {
|
|
ss.ss_family = 0;
|
|
setPort(p);
|
|
}
|
|
|
|
ServiceAnnouncement(const sockaddr* sa, socklen_t sa_len) {
|
|
if (sa)
|
|
std::copy_n((const uint8_t*)sa, sa_len, (uint8_t*)&ss);
|
|
}
|
|
|
|
ServiceAnnouncement(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 ServiceAnnouncement&);
|
|
|
|
private:
|
|
sockaddr_storage ss;
|
|
};
|
|
|
|
}
|