Compare commits

...
5 Commits
Author SHA1 Message Date
Adrien Béraud 38bfe5ba5b sockaddr: use more precise exception 2018-12-11 14:12:13 -05:00
Adrien Béraud 0d2787867c securedht: move crypto ops to Value 2018-12-11 14:12:13 -05:00
Adrien Béraud cb9fe9d1fe Merge pull request #354 from atraczyk/master
msvc: replace write with _write when building on windows
2018-12-06 12:23:36 -05:00
Andreas Traczyk dcab32ef4f replace write with _write when building with msvc 2018-12-06 12:16:59 -05:00
Adrien Béraud 4948bd7ad3 node cache: remove expired weak pointers from cache
Remove expired Node weak pointers regularly,
in a way that ensures that:
* every expired pointer gets eventually cleaned up
* expired pointer ratio remains reasonably low
* no cleanup pass blocks the node for too long,
* impact on performance is negligible

Impact on performance:
Every N insertions, at most N nodes are tested by iterating the map
-> performance impact in O(1) (constant).
2018-11-29 19:27:32 -05:00
7 changed files with 97 additions and 54 deletions

No files matched your search

+5 -1
View File
@@ -40,12 +40,16 @@ struct NodeCache {
~NodeCache();
private:
class NodeMap : public std::map<InfoHash, std::weak_ptr<Node>> {
class NodeMap : private std::map<InfoHash, std::weak_ptr<Node>> {
public:
Sp<Node> getNode(const InfoHash& id);
Sp<Node> getNode(const InfoHash& id, const SockAddr&, time_point now, bool confirmed, bool client);
std::vector<Sp<Node>> getCachedNodes(const InfoHash& id, size_t count) const;
void clearBadNodes();
void setExpired();
void cleanup();
private:
size_t cleanup_counter {0};
};
const NodeMap& cache(sa_family_t af) const { return af == AF_INET ? cache_4 : cache_6; }
+2 -2
View File
@@ -67,7 +67,7 @@ public:
*/
SockAddr(const sockaddr* sa, socklen_t length) {
if (length > sizeof(sockaddr_storage))
throw std::runtime_error("Socket address length is too large");
throw std::invalid_argument("Socket address length is too large");
set(sa, length);
}
SockAddr(const sockaddr* sa) {
@@ -78,7 +78,7 @@ public:
else if(sa->sa_family == AF_INET6)
len = sizeof(sockaddr_in6);
else
throw std::runtime_error("Unknown address family");
throw std::invalid_argument("Unknown address family");
}
set(sa, len);
}
+3 -1
View File
@@ -591,8 +591,10 @@ struct OPENDHT_PUBLIC Value
*/
Blob cypher {};
bool checkSignature();
Sp<Value> decrypt(const crypto::PrivateKey& key);
private:
friend class SecureDht;
/* Cache for crypto ops */
bool signatureChecked {false};
bool signatureValid {false};
+1
View File
@@ -37,6 +37,7 @@
#include <winsock2.h>
#include <ws2tcpip.h>
#define close(x) closesocket(x)
#define write(...) _write(__VA_ARGS__)
#endif
namespace dht {
+38 -11
View File
@@ -20,6 +20,9 @@
namespace dht {
constexpr size_t CLEANUP_MAX_NODES {1024};
constexpr size_t CLEANUP_FREQ {1024};
NodeCache::~NodeCache()
{
cache_4.setExpired();
@@ -41,29 +44,33 @@ NodeCache::getNode(const InfoHash& id, const SockAddr& addr, time_point now, boo
std::vector<Sp<Node>>
NodeCache::getCachedNodes(const InfoHash& id, sa_family_t sa_f, size_t count) const
{
const auto& c = cache(sa_f);
return cache(sa_f).getCachedNodes(id, count);
}
std::vector<Sp<Node>>
NodeCache::NodeMap::getCachedNodes(const InfoHash& id, size_t count) const
{
std::vector<Sp<Node>> nodes;
nodes.reserve(std::min(c.size(), count));
NodeMap::const_iterator it;
auto dec_it = [&c](NodeMap::const_iterator& it) {
nodes.reserve(std::min(size(), count));
const_iterator it;
auto dec_it = [this](const_iterator& it) {
auto ret = it;
it = (it == c.cbegin()) ? c.cend() : std::prev(it);
it = (it == cbegin()) ? cend() : std::prev(it);
return ret;
};
auto it_p = c.lower_bound(id),
auto it_p = lower_bound(id),
it_n = it_p;
if (not c.empty())
if (not empty())
dec_it(it_p); /* Create 2 separate iterator if we could */
while (nodes.size() < count and (it_n != c.cend() or it_p != c.cend())) {
while (nodes.size() < count and (it_n != cend() or it_p != cend())) {
/* If one of the iterator is at the end, then take the other one
If they are both in middle of somewhere comapre both and take
the closest to the id. */
if (it_p == c.cend()) it = it_n++;
else if (it_n == c.cend()) it = dec_it(it_p);
else it = id.xorCmp(it_p->first, it_n->first) < 0 ? dec_it(it_p) : it_n++;
if (it_p == cend()) it = it_n++;
else if (it_n == cend()) it = dec_it(it_p);
else it = id.xorCmp(it_p->first, it_n->first) < 0 ? dec_it(it_p) : it_n++;
if (auto n = it->second.lock())
if ( not n->isExpired() and not n->isClient() )
@@ -104,6 +111,10 @@ NodeCache::NodeMap::getNode(const InfoHash& id, const SockAddr& addr, time_point
if (not node) {
node = std::make_shared<Node>(id, addr, client);
it.first->second = node;
if (cleanup_counter++ == CLEANUP_FREQ) {
cleanup();
cleanup_counter = 0;
}
} else if (confirm or node->isOld(now)) {
node->update(addr);
}
@@ -120,6 +131,7 @@ NodeCache::NodeMap::clearBadNodes() {
erase(it++);
}
}
cleanup_counter = 0;
}
void
@@ -128,6 +140,21 @@ NodeCache::NodeMap::setExpired() {
if (auto n = wn.second.lock())
n->setExpired();
clear();
cleanup_counter = 0;
}
void
NodeCache::NodeMap::cleanup()
{
auto it = lower_bound(InfoHash::getRandom());
for (size_t n = 0, maxNodes = std::min(size(), CLEANUP_MAX_NODES); n != maxNodes; n++) {
if (it == end())
it = begin();
if (it->second.expired())
erase(it++);
else
++it;
}
}
}
+13 -39
View File
@@ -68,33 +68,19 @@ ValueType
SecureDht::secureType(ValueType&& type)
{
type.storePolicy = [this,type](InfoHash id, Sp<Value>& v, const InfoHash& nid, const SockAddr& a) {
if (v->isSigned()) {
if (!v->signatureChecked) {
v->signatureChecked = true;
v->signatureValid = v->owner and v->owner->checkSignature(v->getToSign(), v->signature);
}
if (!v->signatureValid) {
DHT_LOG.WARN("Signature verification failed");
return false;
}
}
if (v->isSigned())
return v->checkSignature();
return type.storePolicy(id, v, nid, a);
};
type.editPolicy = [this,type](InfoHash id, const Sp<Value>& o, Sp<Value>& n, const InfoHash& nid, const SockAddr& a) {
if (!o->isSigned())
if (not o->isSigned())
return type.editPolicy(id, o, n, nid, a);
if (o->owner != n->owner) {
if (o->owner != n->owner or not n->isSigned()) {
DHT_LOG.WARN("Edition forbidden: owner changed.");
return false;
}
if (!n->signatureChecked) {
n->signatureChecked = true;
n->signatureValid = o->owner and o->owner->checkSignature(n->getToSign(), n->signature);
}
if (!n->signatureValid) {
DHT_LOG.WARN("Edition forbidden: signature verification failed.");
if (not n->checkSignature())
return false;
}
if (o->seq == n->seq) {
// If the data is exactly the same,
// it can be reannounced, possibly by someone else.
@@ -241,35 +227,23 @@ SecureDht::checkValue(const Sp<Value>& v)
#endif
return {};
}
if (v->decrypted) {
return v->decryptedValue;
}
v->decrypted = true;
try {
Value decrypted_val (decrypt(*v));
if (decrypted_val.recipient == getId()) {
if (decrypted_val.owner)
nodesPubKeys_[decrypted_val.owner->getId()] = decrypted_val.owner;
v->decryptedValue = std::make_shared<Value>(std::move(decrypted_val));
return v->decryptedValue;
if (auto decrypted_val = v->decrypt(*key_)) {
if (decrypted_val->owner)
nodesPubKeys_[decrypted_val->owner->getId()] = decrypted_val->owner;
return decrypted_val;
}
// Ignore values belonging to other people
} catch (const std::exception& e) {
DHT_LOG.WARN("Could not decrypt value %s : %s", v->toString().c_str(), e.what());
}
}
// Check signed values
else if (v->isSigned()) {
if (v->signatureChecked) {
return v->signatureValid ? v : Sp<Value>{};
}
v->signatureChecked = true;
if (v->owner and v->owner->checkSignature(v->getToSign(), v->signature)) {
v->signatureValid = true;
nodesPubKeys_[v->owner->getId()] = v->owner;
if (v->checkSignature()) {
if (v->owner)
nodesPubKeys_[v->owner->getId()] = v->owner;
return v;
}
else
} else
DHT_LOG.WARN("Signature verification failed for %s", v->toString().c_str());
}
// Forward normal values
+35
View File
@@ -233,6 +233,41 @@ Value::toJson() const
return val;
}
bool
Value::checkSignature()
{
if (!signatureChecked) {
signatureChecked = true;
if (isSigned()) {
signatureValid = owner and owner->checkSignature(getToSign(), signature);
} else {
signatureValid = true;
}
}
return signatureValid;
}
Sp<Value>
Value::decrypt(const crypto::PrivateKey& key)
{
if (not decrypted) {
decrypted = true;
if (isEncrypted()) {
auto decryptedBlob = key.decrypt(cypher);
std::unique_ptr<Value> v {new Value(id)};
auto msg = msgpack::unpack((const char*)decryptedBlob.data(), decryptedBlob.size());
v->msgpack_unpack_body(msg.get());
if (v->recipient != key.getPublicKey().getId())
throw crypto::DecryptError("Recipient mismatch");
// Ignore values belonging to other people
if (not v->owner or not v->owner->checkSignature(v->getToSign(), v->signature))
throw crypto::DecryptError("Signature mismatch");
decryptedValue = std::move(v);
}
}
return decryptedValue;
}
uint64_t
unpackId(const Json::Value& json, const std::string& key) {
uint64_t ret = 0;