diff --git a/CMakeLists.txt b/CMakeLists.txt index b1c2ad3163bf8b7a066665160e51992a67b9a539..560d91d10b1eba0187c9637009147868b2e93891 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ list (APPEND opendht_SOURCES src/node.cpp src/value.cpp src/dht.cpp + src/routing_table.cpp src/network_engine.cpp src/securedht.cpp src/dhtrunner.cpp @@ -55,6 +56,7 @@ list (APPEND opendht_HEADERS include/opendht/node.h include/opendht/value.h include/opendht/dht.h + include/opendht/routing_table.h include/opendht/network_engine.h include/opendht/scheduler.h include/opendht/securedht.h diff --git a/include/opendht/dht.h b/include/opendht/dht.h index 4251aba96d05b2769c594483f79f8f07dee4005d..f193984a0103319f98225f5d871a88f5d4ae75a2 100644 --- a/include/opendht/dht.h +++ b/include/opendht/dht.h @@ -26,6 +26,7 @@ #include "utils.h" #include "network_engine.h" #include "scheduler.h" +#include "routing_table.h" #include <string> #include <array> @@ -368,60 +369,6 @@ private: std::list<std::weak_ptr<Node>> cache_6; }; - struct Bucket { - Bucket() : cached() {} - Bucket(sa_family_t af, const InfoHash& f = {}, time_point t = time_point::min()) - : af(af), first(f), time(t), cached() {} - sa_family_t af {0}; - InfoHash first {}; - time_point time {time_point::min()}; /* time of last reply in this bucket */ - std::list<std::shared_ptr<Node>> nodes {}; - sockaddr_storage cached; /* the address of a likely candidate */ - socklen_t cachedlen {0}; - - /** Return a random node in a bucket. */ - std::shared_ptr<Node> randomNode(); - }; - - class RoutingTable : public std::list<Bucket> { - public: - using std::list<Bucket>::list; - - InfoHash middle(const RoutingTable::const_iterator&) const; - - std::vector<std::shared_ptr<Node>> findClosestNodes(const InfoHash id, time_point now, size_t count = TARGET_NODES) const; - - RoutingTable::iterator findBucket(const InfoHash& id); - RoutingTable::const_iterator findBucket(const InfoHash& id) const; - - /** - * Return true if the id is in the bucket's range. - */ - inline bool contains(const RoutingTable::const_iterator& bucket, const InfoHash& id) const { - return InfoHash::cmp(bucket->first, id) <= 0 - && (std::next(bucket) == end() || InfoHash::cmp(id, std::next(bucket)->first) < 0); - } - - /** - * Return true if the table has no bucket ore one empty buket. - */ - inline bool isEmpty() const { - return empty() || (size() == 1 && front().nodes.empty()); - } - - /** - * Return a random id in the bucket's range. - */ - InfoHash randomId(const RoutingTable::const_iterator& bucket) const; - - unsigned depth(const RoutingTable::const_iterator& bucket) const; - - /** - * Split a bucket in two equal parts. - */ - bool split(const RoutingTable::iterator& b); - }; - struct SearchNode { SearchNode(std::shared_ptr<Node> node) : node(node) {} diff --git a/include/opendht/routing_table.h b/include/opendht/routing_table.h new file mode 100644 index 0000000000000000000000000000000000000000..f78babd6870293ceaec992ec44491c3a87367fc9 --- /dev/null +++ b/include/opendht/routing_table.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2014-2016 Savoir-faire Linux Inc. + * Author(s) : 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. + */ + +#pragma once + +#include "node.h" + +namespace dht { + +struct Bucket { + Bucket() : cached() {} + Bucket(sa_family_t af, const InfoHash& f = {}, time_point t = time_point::min()) + : af(af), first(f), time(t), cached() {} + sa_family_t af {0}; + InfoHash first {}; + time_point time {time_point::min()}; /* time of last reply in this bucket */ + std::list<std::shared_ptr<Node>> nodes {}; + sockaddr_storage cached; /* the address of a likely candidate */ + socklen_t cachedlen {0}; + + /** Return a random node in a bucket. */ + std::shared_ptr<Node> randomNode(); +}; + +class RoutingTable : public std::list<Bucket> { +public: + using std::list<Bucket>::list; + + InfoHash middle(const RoutingTable::const_iterator&) const; + + std::vector<std::shared_ptr<Node>> findClosestNodes(const InfoHash id, time_point now, size_t count = TARGET_NODES) const; + + RoutingTable::iterator findBucket(const InfoHash& id); + RoutingTable::const_iterator findBucket(const InfoHash& id) const; + + /** + * Return true if the id is in the bucket's range. + */ + inline bool contains(const RoutingTable::const_iterator& bucket, const InfoHash& id) const { + return InfoHash::cmp(bucket->first, id) <= 0 + && (std::next(bucket) == end() || InfoHash::cmp(id, std::next(bucket)->first) < 0); + } + + /** + * Return true if the table has no bucket ore one empty buket. + */ + inline bool isEmpty() const { + return empty() || (size() == 1 && front().nodes.empty()); + } + + /** + * Return a random id in the bucket's range. + */ + InfoHash randomId(const RoutingTable::const_iterator& bucket) const; + + unsigned depth(const RoutingTable::const_iterator& bucket) const; + + /** + * Split a bucket in two equal parts. + */ + bool split(const RoutingTable::iterator& b); +}; + +} diff --git a/src/dht.cpp b/src/dht.cpp index 60fe95440f91da4239c76c6b69e55063b83bdfcf..75bd32b203614dc6bb500bbec1844f2a93293856 100644 --- a/src/dht.cpp +++ b/src/dht.cpp @@ -173,128 +173,6 @@ Dht::isMartian(const sockaddr *sa, socklen_t len) } } -std::shared_ptr<Node> -Dht::Bucket::randomNode() -{ - if (nodes.empty()) - return nullptr; - std::uniform_int_distribution<unsigned> rand_node(0, nodes.size()-1); - unsigned nn = rand_node(rd); - for (auto& n : nodes) - if (not nn--) return n; - return nodes.back(); -} - -InfoHash -Dht::RoutingTable::randomId(const Dht::RoutingTable::const_iterator& it) const -{ - int bit1 = it->first.lowbit(); - int bit2 = std::next(it) != end() ? std::next(it)->first.lowbit() : -1; - int bit = std::max(bit1, bit2) + 1; - - if (bit >= 8*(int)HASH_LEN) - return it->first; - - int b = bit/8; - InfoHash id_return; - std::copy_n(it->first.begin(), b, id_return.begin()); - id_return[b] = it->first[b] & (0xFF00 >> (bit % 8)); - id_return[b] |= rand_byte(rd) >> (bit % 8); - for (unsigned i = b + 1; i < HASH_LEN; i++) - id_return[i] = rand_byte(rd); - return id_return; -} - -InfoHash -Dht::RoutingTable::middle(const RoutingTable::const_iterator& it) const -{ - unsigned bit = depth(it); - if (bit >= 8*HASH_LEN) - throw std::out_of_range("End of table"); - - InfoHash id = it->first; - id.setBit(bit, 1); - return id; -} - -unsigned -Dht::RoutingTable::depth(const RoutingTable::const_iterator& it) const -{ - int bit1 = it->first.lowbit(); - int bit2 = std::next(it) != end() ? std::next(it)->first.lowbit() : -1; - return std::max(bit1, bit2)+1; -} - -std::vector<std::shared_ptr<Node>> -Dht::RoutingTable::findClosestNodes(const InfoHash id, time_point now, size_t count) const -{ - std::vector<std::shared_ptr<Node>> nodes {}; - auto bucket = findBucket(id); - - if (bucket == end()) { return nodes; } - - auto sortedBucketInsert = [&](const Bucket &b) { - for (auto n : b.nodes) { - if (not n->isGood(now)) - continue; - - auto here = std::find_if(nodes.begin(), nodes.end(), - [&id,&n](std::shared_ptr<Node> &node) { - return id.xorCmp(n->id, node->id) < 0; - } - ); - nodes.insert(here, n); - } - }; - - auto itn = bucket; - auto itp = std::prev(bucket); - while (nodes.size() < count && (itn != end() || itp != end())) { - if (itn != end()) { - sortedBucketInsert(*itn); - itn = std::next(itn); - } - if (itp != end()) { - sortedBucketInsert(*itp); - if (itp == begin()) { - itp = end(); - continue; - } - itp = std::prev(itp); - } - } - - // shrink to the count closest nodes. - if (nodes.size() > count) { - nodes.resize(count); - } - return nodes; -} - -Dht::RoutingTable::iterator -Dht::RoutingTable::findBucket(const InfoHash& id) -{ - if (empty()) - return end(); - auto b = begin(); - while (true) { - auto next = std::next(b); - if (next == end()) - return b; - if (InfoHash::cmp(id, next->first) < 0) - return b; - b = next; - } -} - -Dht::RoutingTable::const_iterator -Dht::RoutingTable::findBucket(const InfoHash& id) const -{ - /* Avoid code duplication for the const version */ - const_iterator it = const_cast<RoutingTable*>(this)->findBucket(id); - return it; -} - /* Every bucket contains an unordered list of nodes. */ std::shared_ptr<Node> Dht::findNode(const InfoHash& id, sa_family_t af) @@ -444,34 +322,6 @@ Dht::getPublicAddress(sa_family_t family) return ret; } -/* Split a bucket into two equal parts. */ -bool -Dht::RoutingTable::split(const RoutingTable::iterator& b) -{ - InfoHash new_id; - try { - new_id = middle(b); - } catch (const std::out_of_range& e) { - return false; - } - - // Insert new bucket - insert(std::next(b), Bucket {b->af, new_id, b->time}); - - // Re-assign nodes - std::list<std::shared_ptr<Node>> nodes {}; - nodes.splice(nodes.begin(), b->nodes); - while (!nodes.empty()) { - auto n = nodes.begin(); - auto b = findBucket((*n)->id); - if (b == end()) - nodes.erase(n); - else - b->nodes.splice(b->nodes.begin(), nodes, n); - } - return true; -} - bool Dht::trySearchInsert(const std::shared_ptr<Node>& node) { diff --git a/src/routing_table.cpp b/src/routing_table.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e8873b682356441ec95f4b619347d000247e12ec --- /dev/null +++ b/src/routing_table.cpp @@ -0,0 +1,161 @@ +#include "routing_table.h" +#include "rng.h" + +#include <memory> + +namespace dht { + +static std::mt19937 rd {dht::crypto::random_device{}()}; +static std::uniform_int_distribution<uint8_t> rand_byte; + +std::shared_ptr<Node> +Bucket::randomNode() +{ + if (nodes.empty()) + return nullptr; + std::uniform_int_distribution<unsigned> rand_node(0, nodes.size()-1); + unsigned nn = rand_node(rd); + for (auto& n : nodes) + if (not nn--) return n; + return nodes.back(); +} + +InfoHash +RoutingTable::randomId(const RoutingTable::const_iterator& it) const +{ + int bit1 = it->first.lowbit(); + int bit2 = std::next(it) != end() ? std::next(it)->first.lowbit() : -1; + int bit = std::max(bit1, bit2) + 1; + + if (bit >= 8*(int)HASH_LEN) + return it->first; + + int b = bit/8; + InfoHash id_return; + std::copy_n(it->first.begin(), b, id_return.begin()); + id_return[b] = it->first[b] & (0xFF00 >> (bit % 8)); + id_return[b] |= rand_byte(rd) >> (bit % 8); + for (unsigned i = b + 1; i < HASH_LEN; i++) + id_return[i] = rand_byte(rd); + return id_return; +} + +InfoHash +RoutingTable::middle(const RoutingTable::const_iterator& it) const +{ + unsigned bit = depth(it); + if (bit >= 8*HASH_LEN) + throw std::out_of_range("End of table"); + + InfoHash id = it->first; + id.setBit(bit, 1); + return id; +} + +unsigned +RoutingTable::depth(const RoutingTable::const_iterator& it) const +{ + int bit1 = it->first.lowbit(); + int bit2 = std::next(it) != end() ? std::next(it)->first.lowbit() : -1; + return std::max(bit1, bit2)+1; +} + +std::vector<std::shared_ptr<Node>> +RoutingTable::findClosestNodes(const InfoHash id, time_point now, size_t count) const +{ + std::vector<std::shared_ptr<Node>> nodes {}; + auto bucket = findBucket(id); + + if (bucket == end()) { return nodes; } + + auto sortedBucketInsert = [&](const Bucket &b) { + for (auto n : b.nodes) { + if (not n->isGood(now)) + continue; + + auto here = std::find_if(nodes.begin(), nodes.end(), + [&id,&n](std::shared_ptr<Node> &node) { + return id.xorCmp(n->id, node->id) < 0; + } + ); + nodes.insert(here, n); + } + }; + + auto itn = bucket; + auto itp = std::prev(bucket); + while (nodes.size() < count && (itn != end() || itp != end())) { + if (itn != end()) { + sortedBucketInsert(*itn); + itn = std::next(itn); + } + if (itp != end()) { + sortedBucketInsert(*itp); + if (itp == begin()) { + itp = end(); + continue; + } + itp = std::prev(itp); + } + } + + // shrink to the count closest nodes. + if (nodes.size() > count) { + nodes.resize(count); + } + return nodes; +} + +RoutingTable::iterator +RoutingTable::findBucket(const InfoHash& id) +{ + if (empty()) + return end(); + auto b = begin(); + while (true) { + auto next = std::next(b); + if (next == end()) + return b; + if (InfoHash::cmp(id, next->first) < 0) + return b; + b = next; + } +} + +RoutingTable::const_iterator +RoutingTable::findBucket(const InfoHash& id) const +{ + /* Avoid code duplication for the const version */ + const_iterator it = const_cast<RoutingTable*>(this)->findBucket(id); + return it; +} + +/* Split a bucket into two equal parts. */ +bool +RoutingTable::split(const RoutingTable::iterator& b) +{ + InfoHash new_id; + try { + new_id = middle(b); + } catch (const std::out_of_range& e) { + return false; + } + + // Insert new bucket + insert(std::next(b), Bucket {b->af, new_id, b->time}); + + // Re-assign nodes + std::list<std::shared_ptr<Node>> nodes {}; + nodes.splice(nodes.begin(), b->nodes); + while (!nodes.empty()) { + auto n = nodes.begin(); + auto b = findBucket((*n)->id); + if (b == end()) + nodes.erase(n); + else + b->nodes.splice(b->nodes.begin(), nodes, n); + } + return true; +} + +}