3 Copyright (c) 2006, Arvid Norberg
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
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"
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"
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
;
67 key_refresh
= 5 // generate a new write token key every 5 minutes
72 const int tick_period
= 1; // minutes
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");
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();
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);
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)
139 #ifdef TORRENT_DHT_VERBOSE_LOGGING
140 TORRENT_DEFINE_LOG(dht_tracker
)
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
))
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)
157 , m_host_resolver(sock
.get_io_service())
162 #ifdef TORRENT_DHT_VERBOSE_LOGGING
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);
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);
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
);
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
)
242 std::cerr
<< "exception-type: " << typeid(exc
).name() << std::endl
;
243 std::cerr
<< "what: " << exc
.what() << std::endl
;
244 TORRENT_ASSERT(false);
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";
283 #ifdef TORRENT_DHT_VERBOSE_LOGGING
284 static bool first
= true;
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
);
294 int torrents
= std::distance(m_dht
.begin_data(), m_dht
.end_data());
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
);
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";
325 boost::tie(active
, passive
) = m_dht
.size();
326 pc
<< (m_counter
* tick_period
)
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
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))
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);
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;
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
)
397 if (i
->count
< min
->count
) min
= i
;
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
<< " ]";
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);
421 // we got 50 messages from this peer, but it was in
422 // more than 5 seconds. Reset the counter and the timer
424 match
->limit
= now
+ seconds(5);
430 min
->limit
= now
+ seconds(5);
434 #ifdef TORRENT_DHT_VERBOSE_LOGGING
435 ++m_total_message_input
;
436 m_total_in_bytes
+= bytes_transferred
;
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";
457 #ifdef TORRENT_DHT_VERBOSE_LOGGING
458 std::stringstream log_line
;
459 log_line
<< time_now_string() << " RECEIVED ["
463 libtorrent::dht::msg m
;
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";
502 log_line
<< " c: " << client
;
505 catch (std::exception
&)
507 log_line
<< " c: generic";
511 std::string
const& msg_type
= e
["y"].string();
515 #ifdef TORRENT_DHT_VERBOSE_LOGGING
516 log_line
<< " r: " << messages::ids
[m
.message_id
]
517 << " t: " << to_hex(m
.transaction_id
);
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"))
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
));
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();
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)
559 std::copy(i
, i
+ 20, id
.begin());
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();
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();
580 std::copy(in
, in
+ 20, id
.begin());
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();
594 entry
const* token
= r
.find_key("token");
595 if (token
) m
.write_token
= *token
;
597 else if (msg_type
== "q")
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
;
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
);
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
);
635 else if (request_kind
== "announce_peer")
637 #ifdef TORRENT_DHT_VERBOSE_LOGGING
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
;
657 #ifdef TORRENT_DHT_VERBOSE_LOGGING
658 TORRENT_LOG(dht_tracker
) << " *** UNSUPPORTED REQUEST *** : "
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
;
674 throw std::runtime_error("DHT error message");
678 #ifdef TORRENT_DHT_VERBOSE_LOGGING
679 TORRENT_LOG(dht_tracker
) << " *** UNSUPPORTED MESSAGE TYPE *** : "
682 throw std::runtime_error("unsupported message type: " + msg_type
);
685 #ifdef TORRENT_DHT_VERBOSE_LOGGING
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() << " ]";
693 TORRENT_ASSERT(m
.message_id
!= messages::error
);
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";
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
)
719 std::back_insert_iterator
<std::string
> out(node
);
720 write_endpoint(i
->addr
, out
);
721 nodes
.list().push_back(entry(node
));
724 m_dht
.replacement_cache(cache
);
725 for (bucket_t::iterator
i(cache
.begin())
726 , end(cache
.end()); i
!= end
; ++i
)
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());
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()
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())
801 std::copy(i
->id
.begin(), i
->id
.end(), out
);
802 write_endpoint(i
->addr
, out
);
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
);
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
);
845 if (m
.message_id
== messages::error
)
847 TORRENT_ASSERT(m
.reply
);
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
));
854 #ifdef TORRENT_DHT_VERBOSE_LOGGING
855 log_line
<< " err: " << m
.error_code
856 << " msg: " << m
.error_msg
;
862 e
["r"] = entry(entry::dictionary_t
);
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
];
870 if (m
.write_token
.type() != entry::undefined_t
)
871 r
["token"] = m
.write_token
;
873 switch (m
.message_id
)
877 case messages::find_node
:
879 write_nodes_entry(r
, m
);
882 case messages::get_peers
:
886 write_nodes_entry(r
, m
);
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
)
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();
909 case messages::announce_peer
:
917 e
["a"] = entry(entry::dictionary_t
);
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
];
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
);
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
);
948 case messages::announce_peer
:
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
);
963 bencode(std::back_inserter(m_send_buf
), e
);
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();
972 ++m_replies_sent
[m
.message_id
];
973 m_replies_bytes_sent
[m
.message_id
] += int(m_send_buf
.size());
977 m_queries_out_bytes
+= m_send_buf
.size();
979 TORRENT_LOG(dht_tracker
) << log_line
.str() << " ]";
982 if (!m
.piggy_backed_ping
) return;
986 pm
.piggy_backed_ping
= false;
987 pm
.message_id
= messages::ping
;
988 pm
.transaction_id
= m
.ping_transaction_id
;
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);