file_progress fix
[libtorrent.git] / src / kademlia / dht_tracker.cpp
blob4c781b9e70e4c57bd9fedef22cc833d1957eb5fb
1 /*
3 Copyright (c) 2006, Arvid Norberg
4 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
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.
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.
33 #include "libtorrent/pch.hpp"
35 #include <fstream>
36 #include <set>
37 #include <numeric>
38 #include <stdexcept>
39 #include <boost/bind.hpp>
40 #include <boost/ref.hpp>
41 #include <boost/optional.hpp>
42 #include <boost/lexical_cast.hpp>
43 #include <boost/filesystem/operations.hpp>
45 #include "libtorrent/kademlia/node.hpp"
46 #include "libtorrent/kademlia/node_id.hpp"
47 #include "libtorrent/kademlia/traversal_algorithm.hpp"
48 #include "libtorrent/kademlia/dht_tracker.hpp"
50 #include "libtorrent/socket.hpp"
51 #include "libtorrent/bencode.hpp"
52 #include "libtorrent/io.hpp"
53 #include "libtorrent/version.hpp"
54 #include "libtorrent/escape_string.hpp"
56 using boost::ref;
57 using boost::lexical_cast;
58 using libtorrent::dht::node_impl;
59 using libtorrent::dht::node_id;
60 using libtorrent::dht::packet_t;
61 using libtorrent::dht::msg;
62 namespace messages = libtorrent::dht::messages;
63 using namespace libtorrent::detail;
65 enum
67 key_refresh = 5 // generate a new write token key every 5 minutes
70 namespace
72 const int tick_period = 1; // minutes
74 struct count_peers
76 int& count;
77 count_peers(int& c): count(c) {}
78 void operator()(std::pair<libtorrent::dht::node_id
79 , libtorrent::dht::torrent_entry> const& t)
81 count += std::distance(t.second.peers.begin()
82 , t.second.peers.end());
86 boost::optional<node_id> read_id(libtorrent::entry const& d)
88 using namespace libtorrent;
89 using libtorrent::dht::node_id;
91 if (d.type() != entry::dictionary_t) return boost::optional<node_id>();
92 entry const* nid = d.find_key("node-id");
93 if (!nid
94 || nid->type() != entry::string_t
95 || nid->string().length() != 40)
96 return boost::optional<node_id>();
97 return boost::optional<node_id>(
98 boost::lexical_cast<node_id>(nid->string()));
101 template <class EndpointType>
102 void read_endpoint_list(libtorrent::entry const* n, std::vector<EndpointType>& epl)
104 using namespace libtorrent;
105 entry::list_type const& contacts = n->list();
106 for (entry::list_type::const_iterator i = contacts.begin()
107 , end(contacts.end()); i != end; ++i)
109 std::string const& p = i->string();
110 if (p.size() < 6) continue;
111 std::string::const_iterator in = p.begin();
112 if (p.size() == 6)
113 epl.push_back(read_v4_endpoint<EndpointType>(in));
114 else if (p.size() == 18)
115 epl.push_back(read_v6_endpoint<EndpointType>(in));
121 namespace libtorrent { namespace dht
124 void intrusive_ptr_add_ref(dht_tracker const* c)
126 TORRENT_ASSERT(c != 0);
127 TORRENT_ASSERT(c->m_refs >= 0);
128 ++c->m_refs;
131 void intrusive_ptr_release(dht_tracker const* c)
133 TORRENT_ASSERT(c != 0);
134 TORRENT_ASSERT(c->m_refs > 0);
135 if (--c->m_refs == 0)
136 delete c;
139 #ifdef TORRENT_DHT_VERBOSE_LOGGING
140 TORRENT_DEFINE_LOG(dht_tracker)
141 #endif
143 // class that puts the networking and the kademlia node in a single
144 // unit and connecting them together.
145 dht_tracker::dht_tracker(udp_socket& sock, dht_settings const& settings
146 , entry const& bootstrap)
147 : m_dht(bind(&dht_tracker::send_packet, this, _1), settings
148 , read_id(bootstrap))
149 , m_sock(sock)
150 , m_last_new_key(time_now() - minutes(key_refresh))
151 , m_timer(sock.get_io_service())
152 , m_connection_timer(sock.get_io_service())
153 , m_refresh_timer(sock.get_io_service())
154 , m_settings(settings)
155 , m_refresh_bucket(160)
156 , m_abort(false)
157 , m_host_resolver(sock.get_io_service())
158 , m_refs(0)
160 using boost::bind;
162 #ifdef TORRENT_DHT_VERBOSE_LOGGING
163 m_counter = 0;
164 std::fill_n(m_replies_bytes_sent, 5, 0);
165 std::fill_n(m_queries_bytes_received, 5, 0);
166 std::fill_n(m_replies_sent, 5, 0);
167 std::fill_n(m_queries_received, 5, 0);
168 m_announces = 0;
169 m_failed_announces = 0;
170 m_total_message_input = 0;
171 m_ut_message_input = 0;
172 m_lt_message_input = 0;
173 m_mp_message_input = 0;
174 m_gr_message_input = 0;
175 m_mo_message_input = 0;
176 m_total_in_bytes = 0;
177 m_total_out_bytes = 0;
178 m_queries_out_bytes = 0;
180 // turns on and off individual components' logging
182 // rpc_log().enable(false);
183 // node_log().enable(false);
184 // traversal_log().enable(false);
185 // dht_tracker_log.enable(false);
187 #endif
188 std::vector<udp::endpoint> initial_nodes;
190 if (bootstrap.type() == entry::dictionary_t)
194 if (entry const* nodes = bootstrap.find_key("nodes"))
195 read_endpoint_list<udp::endpoint>(nodes, initial_nodes);
196 } catch (std::exception&) {}
199 m_timer.expires_from_now(seconds(1));
200 m_timer.async_wait(bind(&dht_tracker::tick, self(), _1));
202 m_connection_timer.expires_from_now(seconds(10));
203 m_connection_timer.async_wait(
204 bind(&dht_tracker::connection_timeout, self(), _1));
206 m_refresh_timer.expires_from_now(seconds(5));
207 m_refresh_timer.async_wait(bind(&dht_tracker::refresh_timeout, self(), _1));
209 m_dht.bootstrap(initial_nodes, bind(&dht_tracker::on_bootstrap, self()));
212 void dht_tracker::stop()
214 mutex_t::scoped_lock l(m_mutex);
215 m_abort = true;
216 m_timer.cancel();
217 m_connection_timer.cancel();
218 m_refresh_timer.cancel();
219 m_host_resolver.cancel();
222 void dht_tracker::dht_status(session_status& s)
224 boost::tie(s.dht_nodes, s.dht_node_cache) = m_dht.size();
225 s.dht_torrents = m_dht.data_size();
226 s.dht_global_nodes = m_dht.num_global_nodes();
229 void dht_tracker::connection_timeout(error_code const& e)
232 mutex_t::scoped_lock l(m_mutex);
233 if (e || m_abort) return;
235 time_duration d = m_dht.connection_timeout();
236 m_connection_timer.expires_from_now(d);
237 m_connection_timer.async_wait(bind(&dht_tracker::connection_timeout, self(), _1));
239 catch (std::exception& exc)
241 #ifndef NDEBUG
242 std::cerr << "exception-type: " << typeid(exc).name() << std::endl;
243 std::cerr << "what: " << exc.what() << std::endl;
244 TORRENT_ASSERT(false);
245 #endif
248 void dht_tracker::refresh_timeout(error_code const& e)
251 mutex_t::scoped_lock l(m_mutex);
252 if (e || m_abort) return;
254 time_duration d = m_dht.refresh_timeout();
255 m_refresh_timer.expires_from_now(d);
256 m_refresh_timer.async_wait(
257 bind(&dht_tracker::refresh_timeout, self(), _1));
259 catch (std::exception&)
261 TORRENT_ASSERT(false);
264 void dht_tracker::tick(error_code const& e)
267 mutex_t::scoped_lock l(m_mutex);
268 if (e || m_abort) return;
270 m_timer.expires_from_now(minutes(tick_period));
271 m_timer.async_wait(bind(&dht_tracker::tick, self(), _1));
273 ptime now = time_now();
274 if (now - m_last_new_key > minutes(key_refresh))
276 m_last_new_key = now;
277 m_dht.new_write_key();
278 #ifdef TORRENT_DHT_VERBOSE_LOGGING
279 TORRENT_LOG(dht_tracker) << time_now_string() << " new write key";
280 #endif
283 #ifdef TORRENT_DHT_VERBOSE_LOGGING
284 static bool first = true;
285 if (first)
287 boost::filesystem::create_directory("libtorrent_logs");
290 std::ofstream st("libtorrent_logs/routing_table_state.txt", std::ios_base::trunc);
291 m_dht.print_state(st);
293 // count torrents
294 int torrents = std::distance(m_dht.begin_data(), m_dht.end_data());
296 // count peers
297 int peers = 0;
298 std::for_each(m_dht.begin_data(), m_dht.end_data(), count_peers(peers));
300 std::ofstream pc("libtorrent_logs/dht_stats.log", std::ios_base::app);
301 if (first)
303 first = false;
304 pc << "\n\n ***** starting log at " << time_now_string() << " *****\n\n"
305 << "minute:active nodes:passive nodes"
306 ":ping replies sent:ping queries recvd:ping"
307 ":ping replies sent:ping queries recvd:ping"
308 ":find_node replies bytes sent:find_node queries bytes recv"
309 ":find_node replies bytes sent:find_node queries bytes recv"
310 ":get_peers replies sent:get_peers queries recvd:get_peers"
311 ":get_peers replies bytes sent:get_peers queries bytes recv"
312 ":announce_peer replies sent:announce_peer queries recvd:announce_peer"
313 ":announce_peer replies bytes sent:announce_peer queries bytes recv"
314 ":error replies sent:error queries recvd:error"
315 ":error replies bytes sent:error queries bytes recv"
316 ":num torrents:num peers:announces per min"
317 ":failed announces per min:total msgs per min"
318 ":ut msgs per min:lt msgs per min:mp msgs per min"
319 ":gr msgs per min:bytes in per sec:bytes out per sec"
320 ":queries out bytes per sec\n\n";
323 int active;
324 int passive;
325 boost::tie(active, passive) = m_dht.size();
326 pc << (m_counter * tick_period)
327 << "\t" << active
328 << "\t" << passive;
329 for (int i = 0; i < 5; ++i)
330 pc << "\t" << (m_replies_sent[i] / float(tick_period))
331 << "\t" << (m_queries_received[i] / float(tick_period))
332 << "\t" << (m_replies_bytes_sent[i] / float(tick_period*60))
333 << "\t" << (m_queries_bytes_received[i] / float(tick_period*60));
335 pc << "\t" << torrents
336 << "\t" << peers
337 << "\t" << m_announces / float(tick_period)
338 << "\t" << m_failed_announces / float(tick_period)
339 << "\t" << (m_total_message_input / float(tick_period))
340 << "\t" << (m_ut_message_input / float(tick_period))
341 << "\t" << (m_lt_message_input / float(tick_period))
342 << "\t" << (m_mp_message_input / float(tick_period))
343 << "\t" << (m_gr_message_input / float(tick_period))
344 << "\t" << (m_mo_message_input / float(tick_period))
345 << "\t" << (m_total_in_bytes / float(tick_period*60))
346 << "\t" << (m_total_out_bytes / float(tick_period*60))
347 << "\t" << (m_queries_out_bytes / float(tick_period*60))
348 << std::endl;
349 ++m_counter;
350 std::fill_n(m_replies_bytes_sent, 5, 0);
351 std::fill_n(m_queries_bytes_received, 5, 0);
352 std::fill_n(m_replies_sent, 5, 0);
353 std::fill_n(m_queries_received, 5, 0);
354 m_announces = 0;
355 m_failed_announces = 0;
356 m_total_message_input = 0;
357 m_ut_message_input = 0;
358 m_lt_message_input = 0;
359 m_total_in_bytes = 0;
360 m_total_out_bytes = 0;
361 m_queries_out_bytes = 0;
362 #endif
364 catch (std::exception&)
366 TORRENT_ASSERT(false);
369 void dht_tracker::announce(sha1_hash const& ih, int listen_port
370 , boost::function<void(std::vector<tcp::endpoint> const&
371 , sha1_hash const&)> f)
373 m_dht.announce(ih, listen_port, f);
377 void dht_tracker::on_unreachable(udp::endpoint const& ep)
379 m_dht.unreachable(ep);
382 // translate bittorrent kademlia message into the generice kademlia message
383 // used by the library
384 void dht_tracker::on_receive(udp::endpoint const& ep, char const* buf, int bytes_transferred)
387 node_ban_entry* match = 0;
388 node_ban_entry* min = m_ban_nodes;
389 ptime now = time_now();
390 for (node_ban_entry* i = m_ban_nodes; i < m_ban_nodes + num_ban_nodes; ++i)
392 if (i->src == ep)
394 match = i;
395 break;
397 if (i->count < min->count) min = i;
400 if (match)
402 ++match->count;
403 if (match->count >= 20)
405 if (now < match->limit)
407 #ifdef TORRENT_DHT_VERBOSE_LOGGING
408 if (match->count == 20)
410 TORRENT_LOG(dht_tracker) << time_now_string() << " BANNING PEER [ ip: "
411 << ep << " time: " << total_milliseconds((now - match->limit) + seconds(5)) / 1000.f
412 << " count: " << match->count << " ]";
414 #endif
415 // we've received 20 messages in less than 5 seconds from
416 // this node. Ignore it until it's silent for 5 minutes
417 match->limit = now + minutes(5);
418 return;
421 // we got 50 messages from this peer, but it was in
422 // more than 5 seconds. Reset the counter and the timer
423 match->count = 0;
424 match->limit = now + seconds(5);
427 else
429 min->count = 1;
430 min->limit = now + seconds(5);
431 min->src = ep;
434 #ifdef TORRENT_DHT_VERBOSE_LOGGING
435 ++m_total_message_input;
436 m_total_in_bytes += bytes_transferred;
437 #endif
441 using libtorrent::entry;
442 using libtorrent::bdecode;
444 TORRENT_ASSERT(bytes_transferred > 0);
446 entry e = bdecode(buf, buf + bytes_transferred);
447 if (e.type() == entry::undefined_t)
449 #ifdef TORRENT_DHT_VERBOSE_LOGGING
450 std::string msg(buf, buf + bytes_transferred);
451 TORRENT_LOG(dht_tracker) << "invalid incoming packet: "
452 << e.what() << "\n" << msg << "\n";
453 #endif
454 return;
457 #ifdef TORRENT_DHT_VERBOSE_LOGGING
458 std::stringstream log_line;
459 log_line << time_now_string() << " RECEIVED ["
460 " ip: " << ep;
461 #endif
463 libtorrent::dht::msg m;
464 m.message_id = 0;
465 m.addr = ep;
466 m.transaction_id = e["t"].string();
468 #ifdef TORRENT_DHT_VERBOSE_LOGGING
471 entry const* ver = e.find_key("v");
472 if (!ver) throw std::exception();
474 std::string const& client = ver->string();
475 if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "UT"))
477 ++m_ut_message_input;
478 log_line << " c: uTorrent";
480 else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "LT"))
482 ++m_lt_message_input;
483 log_line << " c: libtorrent";
485 else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "MP"))
487 ++m_mp_message_input;
488 log_line << " c: MooPolice";
490 else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "GR"))
492 ++m_gr_message_input;
493 log_line << " c: GetRight";
495 else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "MO"))
497 ++m_mo_message_input;
498 log_line << " c: Mono Torrent";
500 else
502 log_line << " c: " << client;
505 catch (std::exception&)
507 log_line << " c: generic";
509 #endif
511 std::string const& msg_type = e["y"].string();
513 if (msg_type == "r")
515 #ifdef TORRENT_DHT_VERBOSE_LOGGING
516 log_line << " r: " << messages::ids[m.message_id]
517 << " t: " << to_hex(m.transaction_id);
518 #endif
520 m.reply = true;
521 entry const& r = e["r"];
522 std::string const& id = r["id"].string();
523 if (id.size() != 20) throw std::runtime_error("invalid size of id");
524 std::copy(id.begin(), id.end(), m.id.begin());
526 if (entry const* n = r.find_key("values"))
528 m.peers.clear();
529 if (n->list().size() == 1)
531 // assume it's mainline format
532 std::string const& peers = n->list().front().string();
533 std::string::const_iterator i = peers.begin();
534 std::string::const_iterator end = peers.end();
536 while (std::distance(i, end) >= 6)
537 m.peers.push_back(read_v4_endpoint<tcp::endpoint>(i));
539 else
541 // assume it's uTorrent/libtorrent format
542 read_endpoint_list<tcp::endpoint>(n, m.peers);
544 #ifdef TORRENT_DHT_VERBOSE_LOGGING
545 log_line << " p: " << m.peers.size();
546 #endif
549 m.nodes.clear();
550 if (entry const* n = r.find_key("nodes"))
552 std::string const& nodes = n->string();
553 std::string::const_iterator i = nodes.begin();
554 std::string::const_iterator end = nodes.end();
556 while (std::distance(i, end) >= 26)
558 node_id id;
559 std::copy(i, i + 20, id.begin());
560 i += 20;
561 m.nodes.push_back(libtorrent::dht::node_entry(
562 id, read_v4_endpoint<udp::endpoint>(i)));
564 #ifdef TORRENT_DHT_VERBOSE_LOGGING
565 log_line << " n: " << m.nodes.size();
566 #endif
569 if (entry const* n = r.find_key("nodes2"))
571 entry::list_type const& contacts = n->list();
572 for (entry::list_type::const_iterator i = contacts.begin()
573 , end(contacts.end()); i != end; ++i)
575 std::string const& p = i->string();
576 if (p.size() < 6 + 20) continue;
577 std::string::const_iterator in = p.begin();
579 node_id id;
580 std::copy(in, in + 20, id.begin());
581 in += 20;
582 if (p.size() == 6 + 20)
583 m.nodes.push_back(libtorrent::dht::node_entry(
584 id, read_v4_endpoint<udp::endpoint>(in)));
585 else if (p.size() == 18 + 20)
586 m.nodes.push_back(libtorrent::dht::node_entry(
587 id, read_v6_endpoint<udp::endpoint>(in)));
589 #ifdef TORRENT_DHT_VERBOSE_LOGGING
590 log_line << " n2: " << m.nodes.size();
591 #endif
594 entry const* token = r.find_key("token");
595 if (token) m.write_token = *token;
597 else if (msg_type == "q")
599 m.reply = false;
600 entry const& a = e["a"];
601 std::string const& id = a["id"].string();
602 if (id.size() != 20) throw std::runtime_error("invalid size of id");
603 std::copy(id.begin(), id.end(), m.id.begin());
605 std::string request_kind(e["q"].string());
606 #ifdef TORRENT_DHT_VERBOSE_LOGGING
607 log_line << " q: " << request_kind;
608 #endif
610 if (request_kind == "ping")
612 m.message_id = libtorrent::dht::messages::ping;
614 else if (request_kind == "find_node")
616 std::string const& target = a["target"].string();
617 if (target.size() != 20) throw std::runtime_error("invalid size of target id");
618 std::copy(target.begin(), target.end(), m.info_hash.begin());
619 #ifdef TORRENT_DHT_VERBOSE_LOGGING
620 log_line << " t: " << boost::lexical_cast<std::string>(m.info_hash);
621 #endif
623 m.message_id = libtorrent::dht::messages::find_node;
625 else if (request_kind == "get_peers")
627 std::string const& info_hash = a["info_hash"].string();
628 if (info_hash.size() != 20) throw std::runtime_error("invalid size of info-hash");
629 std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin());
630 m.message_id = libtorrent::dht::messages::get_peers;
631 #ifdef TORRENT_DHT_VERBOSE_LOGGING
632 log_line << " ih: " << boost::lexical_cast<std::string>(m.info_hash);
633 #endif
635 else if (request_kind == "announce_peer")
637 #ifdef TORRENT_DHT_VERBOSE_LOGGING
638 ++m_announces;
639 #endif
640 std::string const& info_hash = a["info_hash"].string();
641 if (info_hash.size() != 20)
642 throw std::runtime_error("invalid size of info-hash");
643 std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin());
644 m.port = a["port"].integer();
645 m.write_token = a["token"];
646 m.message_id = libtorrent::dht::messages::announce_peer;
647 #ifdef TORRENT_DHT_VERBOSE_LOGGING
648 log_line << " ih: " << boost::lexical_cast<std::string>(m.info_hash);
649 log_line << " p: " << m.port;
651 if (!m_dht.verify_token(m))
652 ++m_failed_announces;
653 #endif
655 else
657 #ifdef TORRENT_DHT_VERBOSE_LOGGING
658 TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED REQUEST *** : "
659 << request_kind;
660 #endif
661 throw std::runtime_error("unsupported request: " + request_kind);
664 else if (msg_type == "e")
666 entry::list_type const& list = e["e"].list();
667 m.message_id = messages::error;
668 m.error_msg = list.back().string();
669 m.error_code = list.front().integer();
670 #ifdef TORRENT_DHT_VERBOSE_LOGGING
671 log_line << " incoming error: " << m.error_code
672 << " " << m.error_msg;
673 #endif
674 throw std::runtime_error("DHT error message");
676 else
678 #ifdef TORRENT_DHT_VERBOSE_LOGGING
679 TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED MESSAGE TYPE *** : "
680 << msg_type;
681 #endif
682 throw std::runtime_error("unsupported message type: " + msg_type);
685 #ifdef TORRENT_DHT_VERBOSE_LOGGING
686 if (!m.reply)
688 ++m_queries_received[m.message_id];
689 m_queries_bytes_received[m.message_id] += int(bytes_transferred);
691 TORRENT_LOG(dht_tracker) << log_line.str() << " ]";
692 #endif
693 TORRENT_ASSERT(m.message_id != messages::error);
694 m_dht.incoming(m);
696 catch (std::exception& e)
698 #ifdef TORRENT_DHT_VERBOSE_LOGGING
699 std::string msg(buf, buf + bytes_transferred);
700 TORRENT_LOG(dht_tracker) << "invalid incoming packet: "
701 << e.what() << "\n" << msg << "\n";
702 #endif
705 catch (std::exception& e)
707 TORRENT_ASSERT(false);
710 entry dht_tracker::state() const
712 entry ret(entry::dictionary_t);
714 entry nodes(entry::list_t);
715 for (node_impl::iterator i(m_dht.begin())
716 , end(m_dht.end()); i != end; ++i)
718 std::string node;
719 std::back_insert_iterator<std::string> out(node);
720 write_endpoint(i->addr, out);
721 nodes.list().push_back(entry(node));
723 bucket_t cache;
724 m_dht.replacement_cache(cache);
725 for (bucket_t::iterator i(cache.begin())
726 , end(cache.end()); i != end; ++i)
728 std::string node;
729 std::back_insert_iterator<std::string> out(node);
730 write_endpoint(i->addr, out);
731 nodes.list().push_back(entry(node));
733 if (!nodes.list().empty())
734 ret["nodes"] = nodes;
737 ret["node-id"] = boost::lexical_cast<std::string>(m_dht.nid());
738 return ret;
741 void dht_tracker::add_node(udp::endpoint node)
743 m_dht.add_node(node);
746 void dht_tracker::add_node(std::pair<std::string, int> const& node)
748 udp::resolver::query q(node.first, lexical_cast<std::string>(node.second));
749 m_host_resolver.async_resolve(q,
750 bind(&dht_tracker::on_name_lookup, self(), _1, _2));
753 void dht_tracker::on_name_lookup(error_code const& e
754 , udp::resolver::iterator host) try
756 if (e || host == udp::resolver::iterator()) return;
757 add_node(host->endpoint());
759 catch (std::exception&)
761 TORRENT_ASSERT(false);
764 void dht_tracker::add_router_node(std::pair<std::string, int> const& node)
766 udp::resolver::query q(node.first, lexical_cast<std::string>(node.second));
767 m_host_resolver.async_resolve(q,
768 bind(&dht_tracker::on_router_name_lookup, self(), _1, _2));
771 void dht_tracker::on_router_name_lookup(error_code const& e
772 , udp::resolver::iterator host) try
774 if (e || host == udp::resolver::iterator()) return;
775 m_dht.add_router_node(host->endpoint());
777 catch (std::exception&)
779 TORRENT_ASSERT(false);
782 void dht_tracker::on_bootstrap()
785 namespace
787 void write_nodes_entry(entry& r, libtorrent::dht::msg const& m)
789 bool ipv6_nodes = false;
790 r["nodes"] = entry(entry::string_t);
791 entry& n = r["nodes"];
792 std::back_insert_iterator<std::string> out(n.string());
793 for (msg::nodes_t::const_iterator i = m.nodes.begin()
794 , end(m.nodes.end()); i != end; ++i)
796 if (!i->addr.address().is_v4())
798 ipv6_nodes = true;
799 continue;
801 std::copy(i->id.begin(), i->id.end(), out);
802 write_endpoint(i->addr, out);
805 if (ipv6_nodes)
807 r["nodes2"] = entry(entry::list_t);
808 entry& p = r["nodes2"];
809 std::string endpoint;
810 for (msg::nodes_t::const_iterator i = m.nodes.begin()
811 , end(m.nodes.end()); i != end; ++i)
813 if (!i->addr.address().is_v6()) continue;
814 endpoint.resize(18 + 20);
815 std::string::iterator out = endpoint.begin();
816 std::copy(i->id.begin(), i->id.end(), out);
817 out += 20;
818 write_endpoint(i->addr, out);
819 endpoint.resize(out - endpoint.begin());
820 p.list().push_back(entry(endpoint));
826 void dht_tracker::send_packet(msg const& m)
829 using libtorrent::bencode;
830 using libtorrent::entry;
831 entry e(entry::dictionary_t);
832 TORRENT_ASSERT(!m.transaction_id.empty() || m.message_id == messages::error);
833 e["t"] = m.transaction_id;
834 static char const version_str[] = {'L', 'T'
835 , LIBTORRENT_VERSION_MAJOR, LIBTORRENT_VERSION_MINOR};
836 e["v"] = std::string(version_str, version_str + 4);
838 #ifdef TORRENT_DHT_VERBOSE_LOGGING
839 std::stringstream log_line;
840 log_line << time_now_string()
841 << " SENDING [ ip: " << m.addr
842 << " t: " << to_hex(m.transaction_id);
843 #endif
845 if (m.message_id == messages::error)
847 TORRENT_ASSERT(m.reply);
848 e["y"] = "e";
849 entry error_list(entry::list_t);
850 TORRENT_ASSERT(m.error_code > 200 && m.error_code <= 204);
851 error_list.list().push_back(entry(m.error_code));
852 error_list.list().push_back(entry(m.error_msg));
853 e["e"] = error_list;
854 #ifdef TORRENT_DHT_VERBOSE_LOGGING
855 log_line << " err: " << m.error_code
856 << " msg: " << m.error_msg;
857 #endif
859 else if (m.reply)
861 e["y"] = "r";
862 e["r"] = entry(entry::dictionary_t);
863 entry& r = e["r"];
864 r["id"] = std::string(m.id.begin(), m.id.end());
866 #ifdef TORRENT_DHT_VERBOSE_LOGGING
867 log_line << " r: " << messages::ids[m.message_id];
868 #endif
870 if (m.write_token.type() != entry::undefined_t)
871 r["token"] = m.write_token;
873 switch (m.message_id)
875 case messages::ping:
876 break;
877 case messages::find_node:
879 write_nodes_entry(r, m);
880 break;
882 case messages::get_peers:
884 if (m.peers.empty())
886 write_nodes_entry(r, m);
888 else
890 r["values"] = entry(entry::list_t);
891 entry& p = r["values"];
892 std::string endpoint;
893 for (msg::peers_t::const_iterator i = m.peers.begin()
894 , end(m.peers.end()); i != end; ++i)
896 endpoint.resize(18);
897 std::string::iterator out = endpoint.begin();
898 write_endpoint(*i, out);
899 endpoint.resize(out - endpoint.begin());
900 p.list().push_back(entry(endpoint));
902 #ifdef TORRENT_DHT_VERBOSE_LOGGING
903 log_line << " p: " << m.peers.size();
904 #endif
906 break;
909 case messages::announce_peer:
910 break;
911 break;
914 else
916 e["y"] = "q";
917 e["a"] = entry(entry::dictionary_t);
918 entry& a = e["a"];
919 a["id"] = std::string(m.id.begin(), m.id.end());
921 if (m.write_token.type() != entry::undefined_t)
922 a["token"] = m.write_token;
923 TORRENT_ASSERT(m.message_id <= messages::error);
924 e["q"] = messages::ids[m.message_id];
926 #ifdef TORRENT_DHT_VERBOSE_LOGGING
927 log_line << " q: " << messages::ids[m.message_id];
928 #endif
930 switch (m.message_id)
932 case messages::find_node:
934 a["target"] = std::string(m.info_hash.begin(), m.info_hash.end());
935 #ifdef TORRENT_DHT_VERBOSE_LOGGING
936 log_line << " target: " << boost::lexical_cast<std::string>(m.info_hash);
937 #endif
938 break;
940 case messages::get_peers:
942 a["info_hash"] = std::string(m.info_hash.begin(), m.info_hash.end());
943 #ifdef TORRENT_DHT_VERBOSE_LOGGING
944 log_line << " ih: " << boost::lexical_cast<std::string>(m.info_hash);
945 #endif
946 break;
948 case messages::announce_peer:
949 a["port"] = m.port;
950 a["info_hash"] = std::string(m.info_hash.begin(), m.info_hash.end());
951 a["token"] = m.write_token;
952 #ifdef TORRENT_DHT_VERBOSE_LOGGING
953 log_line << " p: " << m.port
954 << " ih: " << boost::lexical_cast<std::string>(m.info_hash);
955 #endif
956 break;
957 default: break;
962 m_send_buf.clear();
963 bencode(std::back_inserter(m_send_buf), e);
964 error_code ec;
965 m_sock.send(m.addr, &m_send_buf[0], (int)m_send_buf.size(), ec);
967 #ifdef TORRENT_DHT_VERBOSE_LOGGING
968 m_total_out_bytes += m_send_buf.size();
970 if (m.reply)
972 ++m_replies_sent[m.message_id];
973 m_replies_bytes_sent[m.message_id] += int(m_send_buf.size());
975 else
977 m_queries_out_bytes += m_send_buf.size();
979 TORRENT_LOG(dht_tracker) << log_line.str() << " ]";
980 #endif
982 if (!m.piggy_backed_ping) return;
984 msg pm;
985 pm.reply = false;
986 pm.piggy_backed_ping = false;
987 pm.message_id = messages::ping;
988 pm.transaction_id = m.ping_transaction_id;
989 pm.id = m.id;
990 pm.addr = m.addr;
992 send_packet(pm);
994 catch (std::exception&)
996 // m_send may fail with "no route to host"
997 // but it shouldn't throw since an error code
998 // is passed in instead
999 TORRENT_ASSERT(false);