diff --git a/docs/dht_extensions.rst b/docs/dht_extensions.rst new file mode 100644 index 000000000..60fd4a1b3 --- /dev/null +++ b/docs/dht_extensions.rst @@ -0,0 +1,39 @@ +:Author: Arvid Norberg, arvid@rasterbar.com + +Mainline DHT extensions +======================= + +libtorrent implements a few extensions to the Mainline DHT protocol. + +client identification +--------------------- + +In each DHT packet, an extra key is inserted named "v". This is a string +describing the client and version used. This can help alot when debugging +and finding errors in client implementations. The string is encoded as four +characters, two characters describing the client and two characters interpreted +as a binary number describing the client version. + +Currently known clients: + ++---------------+--------+ +| uTorrent | ``UT`` | ++---------------+--------+ +| libtorrent | ``LT`` | ++---------------+--------+ +| MooPolice | ``MP`` | ++---------------+--------+ +| GetRight | ``GR`` | ++---------------+--------+ + +IPv6 support +------------ + +The only DHT messages that don't support IPv6 is the ``nodes`` reply. It +encodes all the contacts as 6 bytes sequences packet together in sequence in + string. The problem is that IPv6 endpoints cannot be encoded as 6 bytes, but +18 bytes. The extension libtorrent applies is to add another key, called +``nodes2`` which is encoded as a list of strings. Each string represents one +contact and is encoded as 20 bytes node-id and then a variable length encoded +IP address (6 bytes in IPv4 case and 18 bytes in IPv6 case). + diff --git a/src/kademlia/dht_tracker.cpp b/src/kademlia/dht_tracker.cpp index 44cf8272e..57d2f6212 100644 --- a/src/kademlia/dht_tracker.cpp +++ b/src/kademlia/dht_tracker.cpp @@ -103,6 +103,25 @@ namespace return boost::optional( boost::lexical_cast(nid->string())); } + + template + void read_endpoint_list(libtorrent::entry const* n, std::vector& epl) + { + using namespace libtorrent; + entry::list_type const& contacts = n->list(); + for (entry::list_type::const_iterator i = contacts.begin() + , end(contacts.end()); i != end; ++i) + { + std::string const& p = i->string(); + if (p.size() < 6) continue; + std::string::const_iterator in = p.begin(); + if (p.size() == 6) + epl.push_back(read_v4_endpoint(in)); + else if (p.size() == 18) + epl.push_back(read_v6_endpoint(in)); + } + } + } namespace libtorrent { namespace dht @@ -161,24 +180,11 @@ namespace libtorrent { namespace dht if (bootstrap.type() == entry::dictionary_t) { - if (entry const* nodes = bootstrap.find_key("nodes")) + try { - if (nodes->type() == entry::list_t) - { - entry::list_type const& node_list = nodes->list(); - for (entry::list_type::const_iterator i = node_list.begin() - , end(node_list.end()); i != end; ++i) - { - if (i->type() != entry::string_t) continue; - std::string const& str = i->string(); - std::string::const_iterator in(str.begin()); - if (str.length() == 6) - initial_nodes.push_back(read_v4_endpoint(in)); - else if (str.length() == 18) - initial_nodes.push_back(read_v6_endpoint(in)); - } - } - } + if (entry const* nodes = bootstrap.find_key("nodes")) + read_endpoint_list(nodes, initial_nodes); + } catch (std::exception&) {} } m_dht.bootstrap(initial_nodes, bind(&dht_tracker::on_bootstrap, this)); @@ -436,26 +442,15 @@ namespace libtorrent { namespace dht if (entry const* n = r.find_key("values")) { m.peers.clear(); - entry::list_type const& peers = n->list(); - for (entry::list_type::const_iterator i = peers.begin() - , end(peers.end()); i != end; ++i) - { - std::string const& p = i->string(); - if (p.size() < 6) continue; - std::string::const_iterator in = p.begin(); - if (p.size() == 6) - m.peers.push_back(read_v4_endpoint(in)); - else if (p.size() == 18) - m.peers.push_back(read_v6_endpoint(in)); - } + read_endpoint_list(n, m.peers); #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " peers: " << m.peers.size(); #endif } + m.nodes.clear(); if (entry const* n = r.find_key("nodes")) { - m.nodes.clear(); std::string const& nodes = n->string(); std::string::const_iterator i = nodes.begin(); std::string::const_iterator end = nodes.end(); @@ -473,6 +468,31 @@ namespace libtorrent { namespace dht #endif } + if (entry const* n = r.find_key("nodes2")) + { + entry::list_type const& contacts = n->list(); + for (entry::list_type::const_iterator i = contacts.begin() + , end(contacts.end()); i != end; ++i) + { + std::string const& p = i->string(); + if (p.size() < 6 + 20) continue; + std::string::const_iterator in = p.begin(); + + node_id id; + std::copy(in, in + 20, id.begin()); + in += 20; + if (p.size() == 6 + 20) + m.nodes.push_back(libtorrent::dht::node_entry( + id, read_v4_endpoint(in))); + else if (p.size() == 18 + 20) + m.nodes.push_back(libtorrent::dht::node_entry( + id, read_v6_endpoint(in))); + } +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " nodes2 + nodes: " << m.nodes.size(); +#endif + } + entry const* token = r.find_key("token"); if (token) m.write_token = *token; } @@ -697,17 +717,39 @@ namespace libtorrent { namespace dht break; case messages::find_node: { + bool ipv6_nodes = false; r["nodes"] = entry(entry::string_t); entry& n = r["nodes"]; std::back_insert_iterator out(n.string()); for (msg::nodes_t::const_iterator i = m.nodes.begin() , end(m.nodes.end()); i != end; ++i) { + if (!i->addr.address().is_v4()) + { + ipv6_nodes = true; + continue; + } std::copy(i->id.begin(), i->id.end(), out); write_endpoint(i->addr, out); } + + if (ipv6_nodes) + { + r["nodes2"] = entry(entry::list_t); + entry& p = r["nodes2"]; + std::string endpoint; + endpoint.resize(6); + for (msg::nodes_t::const_iterator i = m.nodes.begin() + , end(m.nodes.end()); i != end; ++i) + { + std::string::iterator out = endpoint.begin(); + std::copy(i->id.begin(), i->id.end(), out); + write_endpoint(i->addr, out); + p.list().push_back(entry(endpoint)); + } + } #ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size(); + TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size(); #endif break; } @@ -721,6 +763,7 @@ namespace libtorrent { namespace dht for (msg::nodes_t::const_iterator i = m.nodes.begin() , end(m.nodes.end()); i != end; ++i) { + if (!i->addr.address().is_v4()) continue; std::copy(i->id.begin(), i->id.end(), out); write_endpoint(i->addr, out); }