/src/libtorrent/src/kademlia/get_peers.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | |
3 | | Copyright (c) 2006, Daniel Wallin |
4 | | Copyright (c) 2013-2020, Arvid Norberg |
5 | | Copyright (c) 2015, Steven Siloti |
6 | | Copyright (c) 2015, Thomas Yuan |
7 | | Copyright (c) 2016-2017, Alden Torres |
8 | | Copyright (c) 2016-2017, Pavel Pimenov |
9 | | All rights reserved. |
10 | | |
11 | | Redistribution and use in source and binary forms, with or without |
12 | | modification, are permitted provided that the following conditions |
13 | | are met: |
14 | | |
15 | | * Redistributions of source code must retain the above copyright |
16 | | notice, this list of conditions and the following disclaimer. |
17 | | * Redistributions in binary form must reproduce the above copyright |
18 | | notice, this list of conditions and the following disclaimer in |
19 | | the documentation and/or other materials provided with the distribution. |
20 | | * Neither the name of the author nor the names of its |
21 | | contributors may be used to endorse or promote products derived |
22 | | from this software without specific prior written permission. |
23 | | |
24 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
25 | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
26 | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
27 | | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
28 | | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
29 | | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
30 | | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
31 | | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
32 | | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
33 | | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
34 | | POSSIBILITY OF SUCH DAMAGE. |
35 | | |
36 | | */ |
37 | | |
38 | | #include <libtorrent/kademlia/get_peers.hpp> |
39 | | #include <libtorrent/kademlia/node.hpp> |
40 | | #include <libtorrent/kademlia/dht_observer.hpp> |
41 | | #include <libtorrent/socket_io.hpp> |
42 | | #include <libtorrent/performance_counters.hpp> |
43 | | #include <libtorrent/aux_/ip_helpers.hpp> // for is_v4 |
44 | | |
45 | | #ifndef TORRENT_DISABLE_LOGGING |
46 | | #include <libtorrent/hex.hpp> // to_hex |
47 | | #endif |
48 | | |
49 | | namespace libtorrent { namespace dht { |
50 | | |
51 | | void get_peers_observer::reply(msg const& m) |
52 | 0 | { |
53 | 0 | bdecode_node const r = m.message.dict_find_dict("r"); |
54 | 0 | if (!r) |
55 | 0 | { |
56 | | #ifndef TORRENT_DISABLE_LOGGING |
57 | | get_observer()->log(dht_logger::traversal, "[%u] missing response dict" |
58 | | , algorithm()->id()); |
59 | | #endif |
60 | 0 | timeout(); |
61 | 0 | return; |
62 | 0 | } |
63 | | |
64 | | // look for peers |
65 | 0 | bdecode_node const n = r.dict_find_list("values"); |
66 | 0 | if (n) |
67 | 0 | { |
68 | 0 | std::vector<tcp::endpoint> peer_list; |
69 | 0 | if (n.list_size() == 1 && n.list_at(0).type() == bdecode_node::string_t |
70 | 0 | && aux::is_v4(m.addr)) |
71 | 0 | { |
72 | | // assume it's mainline format |
73 | 0 | char const* peers = n.list_at(0).string_ptr(); |
74 | 0 | char const* end = peers + n.list_at(0).string_length(); |
75 | |
|
76 | | #ifndef TORRENT_DISABLE_LOGGING |
77 | | log_peers(m, r, int((end - peers) / 6)); |
78 | | #endif |
79 | 0 | while (end - peers >= 6) |
80 | 0 | peer_list.push_back(aux::read_v4_endpoint<tcp::endpoint>(peers)); |
81 | 0 | } |
82 | 0 | else |
83 | 0 | { |
84 | | // assume it's uTorrent/libtorrent format |
85 | 0 | peer_list = aux::read_endpoint_list<tcp::endpoint>(n); |
86 | | #ifndef TORRENT_DISABLE_LOGGING |
87 | | log_peers(m, r, n.list_size()); |
88 | | #endif |
89 | 0 | } |
90 | 0 | static_cast<get_peers*>(algorithm())->got_peers(peer_list); |
91 | 0 | } |
92 | |
|
93 | 0 | find_data_observer::reply(m); |
94 | 0 | } |
95 | | #ifndef TORRENT_DISABLE_LOGGING |
96 | | void get_peers_observer::log_peers(msg const& m, bdecode_node const& r, int const size) const |
97 | | { |
98 | | auto logger = get_observer(); |
99 | | if (logger != nullptr && logger->should_log(dht_logger::traversal)) |
100 | | { |
101 | | bdecode_node const id = r.dict_find_string("id"); |
102 | | if (id && id.string_length() == 20) |
103 | | { |
104 | | logger->log(dht_logger::traversal, "[%u] PEERS " |
105 | | "invoke-count: %d branch-factor: %d addr: %s id: %s distance: %d p: %d" |
106 | | , algorithm()->id() |
107 | | , algorithm()->invoke_count() |
108 | | , algorithm()->branch_factor() |
109 | | , print_endpoint(m.addr).c_str() |
110 | | , aux::to_hex({id.string_ptr(), id.string_length()}).c_str() |
111 | | , distance_exp(algorithm()->target(), node_id(id.string_ptr())) |
112 | | , size); |
113 | | } |
114 | | } |
115 | | } |
116 | | #endif |
117 | | void get_peers::got_peers(std::vector<tcp::endpoint> const& peers) |
118 | 0 | { |
119 | 0 | if (m_data_callback) m_data_callback(peers); |
120 | 0 | } |
121 | | |
122 | | get_peers::get_peers( |
123 | | node& dht_node |
124 | | , node_id const& target |
125 | | , data_callback dcallback |
126 | | , nodes_callback ncallback |
127 | | , bool noseeds) |
128 | 0 | : find_data(dht_node, target, std::move(ncallback)) |
129 | 0 | , m_data_callback(std::move(dcallback)) |
130 | 0 | , m_noseeds(noseeds) |
131 | 0 | { |
132 | 0 | } |
133 | | |
134 | 0 | char const* get_peers::name() const { return "get_peers"; } |
135 | | |
136 | | bool get_peers::invoke(observer_ptr o) |
137 | 0 | { |
138 | 0 | if (m_done) return false; |
139 | | |
140 | 0 | entry e; |
141 | 0 | e["y"] = "q"; |
142 | 0 | entry& a = e["a"]; |
143 | |
|
144 | 0 | e["q"] = "get_peers"; |
145 | 0 | a["info_hash"] = target().to_string(); |
146 | 0 | if (m_noseeds) a["noseed"] = 1; |
147 | |
|
148 | 0 | if (m_node.observer() != nullptr) |
149 | 0 | { |
150 | 0 | m_node.observer()->outgoing_get_peers(target(), target(), o->target_ep()); |
151 | 0 | } |
152 | |
|
153 | 0 | m_node.stats_counters().inc_stats_counter(counters::dht_get_peers_out); |
154 | |
|
155 | 0 | return m_node.m_rpc.invoke(e, o->target_ep(), o); |
156 | 0 | } |
157 | | |
158 | | observer_ptr get_peers::new_observer(udp::endpoint const& ep |
159 | | , node_id const& id) |
160 | 0 | { |
161 | 0 | auto o = m_node.m_rpc.allocate_observer<get_peers_observer>(self(), ep, id); |
162 | 0 | #if TORRENT_USE_ASSERTS |
163 | 0 | if (o) o->m_in_constructor = false; |
164 | 0 | #endif |
165 | 0 | return o; |
166 | 0 | } |
167 | | |
168 | | obfuscated_get_peers::obfuscated_get_peers( |
169 | | node& dht_node |
170 | | , node_id const& target |
171 | | , data_callback dcallback |
172 | | , nodes_callback ncallback |
173 | | , bool noseeds) |
174 | 0 | : get_peers(dht_node, target, std::move(dcallback), std::move(ncallback), noseeds) |
175 | 0 | , m_obfuscated(true) |
176 | 0 | { |
177 | 0 | } |
178 | | |
179 | | char const* obfuscated_get_peers::name() const |
180 | 0 | { return !m_obfuscated ? get_peers::name() : "get_peers [obfuscated]"; } |
181 | | |
182 | | observer_ptr obfuscated_get_peers::new_observer(udp::endpoint const& ep |
183 | | , node_id const& id) |
184 | 0 | { |
185 | 0 | if (m_obfuscated) |
186 | 0 | { |
187 | 0 | auto o = m_node.m_rpc.allocate_observer<obfuscated_get_peers_observer>(self() |
188 | 0 | , ep, id); |
189 | 0 | #if TORRENT_USE_ASSERTS |
190 | 0 | if (o) o->m_in_constructor = false; |
191 | 0 | #endif |
192 | 0 | return o; |
193 | 0 | } |
194 | 0 | else |
195 | 0 | { |
196 | 0 | auto o = m_node.m_rpc.allocate_observer<get_peers_observer>(self() |
197 | 0 | , ep, id); |
198 | 0 | #if TORRENT_USE_ASSERTS |
199 | 0 | if (o) o->m_in_constructor = false; |
200 | 0 | #endif |
201 | 0 | return o; |
202 | 0 | } |
203 | 0 | } |
204 | | |
205 | | bool obfuscated_get_peers::invoke(observer_ptr o) |
206 | 0 | { |
207 | 0 | if (!m_obfuscated) return get_peers::invoke(o); |
208 | | |
209 | 0 | node_id const& id = o->id(); |
210 | 0 | int const shared_prefix = 160 - distance_exp(id, target()); |
211 | |
|
212 | 0 | entry e; |
213 | 0 | e["y"] = "q"; |
214 | 0 | e["q"] = "get_peers"; |
215 | 0 | entry& a = e["a"]; |
216 | | |
217 | | // This logic will obfuscate the target info-hash |
218 | | // we're looking up, in order to preserve more privacy |
219 | | // on the DHT. This is done by only including enough |
220 | | // bits in the info-hash for the node we're querying to |
221 | | // give a good answer, but not more. |
222 | | |
223 | | // now, obfuscate the bits past shared_prefix + 3 |
224 | 0 | node_id mask = generate_prefix_mask(shared_prefix + 3); |
225 | 0 | node_id obfuscated_target = generate_random_id() & ~mask; |
226 | 0 | obfuscated_target |= target() & mask; |
227 | 0 | a["info_hash"] = obfuscated_target.to_string(); |
228 | |
|
229 | 0 | if (m_node.observer() != nullptr) |
230 | 0 | { |
231 | 0 | m_node.observer()->outgoing_get_peers(target(), obfuscated_target |
232 | 0 | , o->target_ep()); |
233 | 0 | } |
234 | |
|
235 | 0 | m_node.stats_counters().inc_stats_counter(counters::dht_get_peers_out); |
236 | |
|
237 | 0 | return m_node.m_rpc.invoke(e, o->target_ep(), o); |
238 | 0 | } |
239 | | |
240 | | void obfuscated_get_peers::done() |
241 | 0 | { |
242 | 0 | if (!m_obfuscated) return get_peers::done(); |
243 | | |
244 | | // oops, we failed to switch over to the non-obfuscated |
245 | | // mode early enough. do it now |
246 | | |
247 | 0 | auto ta = std::make_shared<get_peers>(m_node, target() |
248 | 0 | , m_data_callback, m_nodes_callback, m_noseeds); |
249 | | |
250 | | // don't call these when the obfuscated_get_peers |
251 | | // is done, we're passing them on to be called when |
252 | | // ta completes. |
253 | 0 | m_data_callback = nullptr; |
254 | 0 | m_nodes_callback = nullptr; |
255 | |
|
256 | | #ifndef TORRENT_DISABLE_LOGGING |
257 | | get_node().observer()->log(dht_logger::traversal, "[%u] obfuscated get_peers " |
258 | | "phase 1 done, spawning get_peers [ %u ]" |
259 | | , id(), ta->id()); |
260 | | #endif |
261 | |
|
262 | 0 | int num_added = 0; |
263 | 0 | for (auto i = m_results.begin() |
264 | 0 | , end(m_results.end()); i != end && num_added < 16; ++i) |
265 | 0 | { |
266 | 0 | observer_ptr o = *i; |
267 | | |
268 | | // only add nodes whose node ID we know and that |
269 | | // we know are alive |
270 | 0 | if (o->flags & observer::flag_no_id) continue; |
271 | 0 | if (!(o->flags & observer::flag_alive)) continue; |
272 | | |
273 | 0 | ta->add_entry(o->id(), o->target_ep(), observer::flag_initial); |
274 | 0 | ++num_added; |
275 | 0 | } |
276 | |
|
277 | 0 | ta->start(); |
278 | |
|
279 | 0 | get_peers::done(); |
280 | 0 | } |
281 | | |
282 | | void obfuscated_get_peers_observer::reply(msg const& m) |
283 | 0 | { |
284 | 0 | bdecode_node const r = m.message.dict_find_dict("r"); |
285 | 0 | if (!r) |
286 | 0 | { |
287 | | #ifndef TORRENT_DISABLE_LOGGING |
288 | | get_observer()->log(dht_logger::traversal, "[%u] missing response dict" |
289 | | , algorithm()->id()); |
290 | | #endif |
291 | 0 | timeout(); |
292 | 0 | return; |
293 | 0 | } |
294 | | |
295 | 0 | bdecode_node const id = r.dict_find_string("id"); |
296 | 0 | if (!id || id.string_length() != 20) |
297 | 0 | { |
298 | | #ifndef TORRENT_DISABLE_LOGGING |
299 | | get_observer()->log(dht_logger::traversal, "[%u] invalid id in response" |
300 | | , algorithm()->id()); |
301 | | #endif |
302 | 0 | timeout(); |
303 | 0 | return; |
304 | 0 | } |
305 | | |
306 | 0 | traversal_observer::reply(m); |
307 | |
|
308 | 0 | done(); |
309 | 0 | } |
310 | | |
311 | | } } // namespace libtorrent::dht |