1 /*
2 * Copyright (C) 2014-2019 Savoir-faire Linux Inc.
3 * Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19 #include "node_cache.h"
20
21 namespace dht {
22
23 constexpr size_t CLEANUP_MAX_NODES {1024};
24 constexpr size_t CLEANUP_FREQ {1024};
25
~NodeCache()26 NodeCache::~NodeCache()
27 {
28 cache_4.setExpired();
29 cache_6.setExpired();
30 }
31
32 Sp<Node>
getNode(const InfoHash & id,sa_family_t family)33 NodeCache::getNode(const InfoHash& id, sa_family_t family) {
34 return cache(family).getNode(id);
35 }
36
37 Sp<Node>
getNode(const InfoHash & id,const SockAddr & addr,time_point now,bool confirm,bool client)38 NodeCache::getNode(const InfoHash& id, const SockAddr& addr, time_point now, bool confirm, bool client) {
39 if (not id)
40 return std::make_shared<Node>(id, addr);
41 return cache(addr.getFamily()).getNode(id, addr, now, confirm, client);
42 }
43
44 std::vector<Sp<Node>>
getCachedNodes(const InfoHash & id,sa_family_t sa_f,size_t count) const45 NodeCache::getCachedNodes(const InfoHash& id, sa_family_t sa_f, size_t count) const
46 {
47 return cache(sa_f).getCachedNodes(id, count);
48 }
49
50 std::vector<Sp<Node>>
getCachedNodes(const InfoHash & id,size_t count) const51 NodeCache::NodeMap::getCachedNodes(const InfoHash& id, size_t count) const
52 {
53 std::vector<Sp<Node>> nodes;
54 nodes.reserve(std::min(size(), count));
55 const_iterator it;
56 auto dec_it = [this](const_iterator& it) {
57 auto ret = it;
58 it = (it == cbegin()) ? cend() : std::prev(it);
59 return ret;
60 };
61
62 auto it_p = lower_bound(id),
63 it_n = it_p;
64 if (not empty())
65 dec_it(it_p); /* Create 2 separate iterator if we could */
66
67 while (nodes.size() < count and (it_n != cend() or it_p != cend())) {
68 /* If one of the iterator is at the end, then take the other one
69 If they are both in middle of somewhere comapre both and take
70 the closest to the id. */
71 if (it_p == cend()) it = it_n++;
72 else if (it_n == cend()) it = dec_it(it_p);
73 else it = id.xorCmp(it_p->first, it_n->first) < 0 ? dec_it(it_p) : it_n++;
74
75 if (auto n = it->second.lock())
76 if ( not n->isExpired() and not n->isClient() )
77 nodes.emplace_back(std::move(n));
78 }
79
80 return nodes;
81 }
82
83 void
clearBadNodes(sa_family_t family)84 NodeCache::clearBadNodes(sa_family_t family)
85 {
86 if (family == 0) {
87 clearBadNodes(AF_INET);
88 clearBadNodes(AF_INET6);
89 } else {
90 cache(family).clearBadNodes();
91 }
92 }
93
94 Sp<Node>
getNode(const InfoHash & id)95 NodeCache::NodeMap::getNode(const InfoHash& id)
96 {
97 auto wn = find(id);
98 if (wn == end())
99 return {};
100 if (auto n = wn->second.lock())
101 return n;
102 erase(wn);
103 return {};
104 }
105
106 Sp<Node>
getNode(const InfoHash & id,const SockAddr & addr,time_point now,bool confirm,bool client)107 NodeCache::NodeMap::getNode(const InfoHash& id, const SockAddr& addr, time_point now, bool confirm, bool client)
108 {
109 auto it = emplace(id, std::weak_ptr<Node>{});
110 auto node = it.first->second.lock();
111 if (not node) {
112 node = std::make_shared<Node>(id, addr, client);
113 it.first->second = node;
114 if (cleanup_counter++ == CLEANUP_FREQ) {
115 cleanup();
116 cleanup_counter = 0;
117 }
118 } else if (confirm or node->isOld(now)) {
119 node->update(addr);
120 }
121 return node;
122 }
123
124 void
clearBadNodes()125 NodeCache::NodeMap::clearBadNodes() {
126 for (auto it = cbegin(); it != cend();) {
127 if (auto n = it->second.lock()) {
128 n->reset();
129 ++it;
130 } else {
131 erase(it++);
132 }
133 }
134 cleanup_counter = 0;
135 }
136
137 void
setExpired()138 NodeCache::NodeMap::setExpired() {
139 for (auto& wn : *this)
140 if (auto n = wn.second.lock())
141 n->setExpired();
142 clear();
143 cleanup_counter = 0;
144 }
145
146 void
cleanup()147 NodeCache::NodeMap::cleanup()
148 {
149 auto it = lower_bound(InfoHash::getRandom());
150 for (size_t n = 0, maxNodes = std::min(size(), CLEANUP_MAX_NODES); n != maxNodes; n++) {
151 if (it == end())
152 it = begin();
153 if (it->second.expired())
154 erase(it++);
155 else
156 ++it;
157 }
158 }
159
160 }
161