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