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