2006-08-01 17:27:08 +02:00
|
|
|
/*
|
|
|
|
|
2018-04-09 09:04:33 +02:00
|
|
|
Copyright (c) 2006-2018, Arvid Norberg
|
2006-08-01 17:27:08 +02:00
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in
|
|
|
|
the documentation and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of the author nor the names of its
|
|
|
|
contributors may be used to endorse or promote products derived
|
|
|
|
from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2015-05-10 20:38:10 +02:00
|
|
|
#include "libtorrent/config.hpp"
|
|
|
|
|
2006-08-01 17:27:08 +02:00
|
|
|
#include <utility>
|
2016-05-17 15:24:06 +02:00
|
|
|
#include <cinttypes> // for PRId64 et.al.
|
2016-05-25 06:31:52 +02:00
|
|
|
#include <functional>
|
2016-06-20 17:32:06 +02:00
|
|
|
#include <tuple>
|
2016-11-27 14:46:53 +01:00
|
|
|
#include <array>
|
2015-04-18 04:33:39 +02:00
|
|
|
|
2016-05-23 14:15:39 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-06-04 16:01:43 +02:00
|
|
|
#include "libtorrent/hex.hpp" // to_hex
|
2016-05-23 14:15:39 +02:00
|
|
|
#endif
|
|
|
|
|
2016-09-27 00:04:42 +02:00
|
|
|
#include <libtorrent/socket_io.hpp>
|
|
|
|
#include <libtorrent/session_status.hpp>
|
2011-05-23 07:07:52 +02:00
|
|
|
#include "libtorrent/bencode.hpp"
|
2006-08-01 17:27:08 +02:00
|
|
|
#include "libtorrent/hasher.hpp"
|
2011-02-28 01:35:58 +01:00
|
|
|
#include "libtorrent/random.hpp"
|
2016-09-27 00:04:42 +02:00
|
|
|
#include <libtorrent/assert.hpp>
|
|
|
|
#include <libtorrent/aux_/time.hpp>
|
2017-01-29 21:37:42 +01:00
|
|
|
#include "libtorrent/aux_/throw.hpp"
|
2015-04-03 22:15:48 +02:00
|
|
|
#include "libtorrent/alert_types.hpp" // for dht_lookup
|
2015-05-10 20:38:10 +02:00
|
|
|
#include "libtorrent/performance_counters.hpp" // for counters
|
2015-04-03 22:15:48 +02:00
|
|
|
|
2006-08-01 17:27:08 +02:00
|
|
|
#include "libtorrent/kademlia/node.hpp"
|
2013-12-27 05:28:25 +01:00
|
|
|
#include "libtorrent/kademlia/dht_observer.hpp"
|
2014-02-17 06:56:49 +01:00
|
|
|
#include "libtorrent/kademlia/direct_request.hpp"
|
2016-12-15 05:55:23 +01:00
|
|
|
#include "libtorrent/kademlia/io.hpp"
|
2006-08-01 17:27:08 +02:00
|
|
|
|
|
|
|
#include "libtorrent/kademlia/refresh.hpp"
|
2013-12-27 05:28:25 +01:00
|
|
|
#include "libtorrent/kademlia/get_peers.hpp"
|
|
|
|
#include "libtorrent/kademlia/get_item.hpp"
|
2016-09-22 08:04:05 +02:00
|
|
|
#include "libtorrent/kademlia/msg.hpp"
|
2016-09-27 00:04:42 +02:00
|
|
|
#include <libtorrent/kademlia/put_data.hpp>
|
2017-06-12 11:54:11 +02:00
|
|
|
#include <libtorrent/kademlia/sample_infohashes.hpp>
|
2013-10-14 09:43:18 +02:00
|
|
|
|
2016-05-25 06:31:52 +02:00
|
|
|
using namespace std::placeholders;
|
|
|
|
|
2017-04-12 19:00:57 +02:00
|
|
|
namespace libtorrent { namespace dht {
|
2006-08-01 17:27:08 +02:00
|
|
|
|
2015-04-20 06:52:49 +02:00
|
|
|
namespace {
|
|
|
|
|
2018-09-04 13:46:09 +02:00
|
|
|
// the write tokens we generate are 4 bytes
|
|
|
|
constexpr int write_token_size = 4;
|
|
|
|
|
2006-08-01 17:27:08 +02:00
|
|
|
void nop() {}
|
|
|
|
|
2017-07-21 05:19:28 +02:00
|
|
|
node_id calculate_node_id(node_id const& nid, aux::listen_socket_handle const& sock)
|
2015-05-09 20:06:02 +02:00
|
|
|
{
|
|
|
|
address external_address;
|
2017-07-21 05:19:28 +02:00
|
|
|
external_address = sock.get_external_address();
|
2016-01-01 15:21:07 +01:00
|
|
|
|
|
|
|
// if we don't have an observer, don't pretend that external_address is valid
|
|
|
|
// generating an ID based on 0.0.0.0 would be terrible. random is better
|
2017-04-21 06:45:43 +02:00
|
|
|
if (external_address.is_unspecified())
|
2016-01-01 15:21:07 +01:00
|
|
|
{
|
|
|
|
return generate_random_id();
|
|
|
|
}
|
|
|
|
|
2018-03-04 19:28:31 +01:00
|
|
|
if (nid.is_all_zeros() || !verify_id(nid, external_address))
|
2015-05-09 20:06:02 +02:00
|
|
|
return generate_id(external_address);
|
2016-01-01 15:21:07 +01:00
|
|
|
|
2015-05-09 20:06:02 +02:00
|
|
|
return nid;
|
|
|
|
}
|
|
|
|
|
2016-09-22 08:04:05 +02:00
|
|
|
// generate an error response message
|
|
|
|
void incoming_error(entry& e, char const* msg, int error_code = 203)
|
|
|
|
{
|
|
|
|
e["y"] = "e";
|
|
|
|
entry::list_type& l = e["e"].list();
|
2017-11-12 00:50:24 +01:00
|
|
|
l.emplace_back(error_code);
|
|
|
|
l.emplace_back(msg);
|
2016-09-22 08:04:05 +02:00
|
|
|
}
|
|
|
|
|
2015-04-20 06:52:49 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
2017-07-21 05:19:28 +02:00
|
|
|
node::node(aux::listen_socket_handle const& sock, socket_manager* sock_man
|
2019-01-11 01:43:45 +01:00
|
|
|
, dht::settings const& settings
|
2016-09-11 07:58:48 +02:00
|
|
|
, node_id const& nid
|
2014-07-06 21:18:00 +02:00
|
|
|
, dht_observer* observer
|
2016-09-09 21:02:20 +02:00
|
|
|
, counters& cnt
|
2017-04-21 06:45:43 +02:00
|
|
|
, get_foreign_node_t get_foreign_node
|
2016-06-04 01:44:16 +02:00
|
|
|
, dht_storage_interface& storage)
|
2006-08-01 17:27:08 +02:00
|
|
|
: m_settings(settings)
|
2017-04-21 06:45:43 +02:00
|
|
|
, m_id(calculate_node_id(nid, sock))
|
2018-04-01 13:48:17 +02:00
|
|
|
, m_table(m_id, is_v4(sock.get_local_endpoint()) ? udp::v4() : udp::v6(), 8, settings, observer)
|
2017-04-21 06:45:43 +02:00
|
|
|
, m_rpc(m_id, m_settings, m_table, sock, sock_man, observer)
|
2017-09-22 06:00:38 +02:00
|
|
|
, m_sock(sock)
|
|
|
|
, m_sock_man(sock_man)
|
2018-01-11 01:35:15 +01:00
|
|
|
, m_get_foreign_node(std::move(get_foreign_node))
|
2013-10-14 01:04:40 +02:00
|
|
|
, m_observer(observer)
|
2018-04-01 13:48:17 +02:00
|
|
|
, m_protocol(map_protocol_to_descriptor(is_v4(sock.get_local_endpoint()) ? udp::v4() : udp::v6()))
|
2015-03-12 05:34:54 +01:00
|
|
|
, m_last_tracker_tick(aux::time_now())
|
2014-11-01 23:47:56 +01:00
|
|
|
, m_last_self_refresh(min_time())
|
2014-07-06 21:18:00 +02:00
|
|
|
, m_counters(cnt)
|
2016-06-04 01:44:16 +02:00
|
|
|
, m_storage(storage)
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2016-08-06 19:18:48 +02:00
|
|
|
m_secret[0] = random(0xffffffff);
|
|
|
|
m_secret[1] = random(0xffffffff);
|
2015-09-08 22:12:54 +02:00
|
|
|
}
|
|
|
|
|
2016-07-10 13:34:45 +02:00
|
|
|
node::~node() = default;
|
2016-01-09 19:28:15 +01:00
|
|
|
|
|
|
|
void node::update_node_id()
|
2015-09-08 22:12:54 +02:00
|
|
|
{
|
2016-01-09 19:28:15 +01:00
|
|
|
// if we don't have an observer, we can't ask for the external IP (and our
|
|
|
|
// current node ID is likely not generated from an external address), so we
|
|
|
|
// can just stop here in that case.
|
2016-09-13 14:18:47 +02:00
|
|
|
if (m_observer == nullptr) return;
|
2016-01-09 19:28:15 +01:00
|
|
|
|
2017-07-21 05:19:28 +02:00
|
|
|
auto ext_address = m_sock.get_external_address();
|
|
|
|
|
2016-01-09 19:28:15 +01:00
|
|
|
// it's possible that our external address hasn't actually changed. If our
|
|
|
|
// current ID is still valid, don't do anything.
|
2017-07-21 05:19:28 +02:00
|
|
|
if (verify_id(m_id, ext_address))
|
2016-01-09 19:28:15 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-13 14:18:47 +02:00
|
|
|
if (m_observer != nullptr) m_observer->log(dht_logger::node
|
2016-01-09 19:28:15 +01:00
|
|
|
, "updating node ID (because external IP address changed)");
|
|
|
|
#endif
|
|
|
|
|
2017-07-21 05:19:28 +02:00
|
|
|
m_id = generate_id(ext_address);
|
2016-01-09 19:28:15 +01:00
|
|
|
|
|
|
|
m_table.update_node_id(m_id);
|
2016-07-25 03:30:36 +02:00
|
|
|
m_rpc.update_node_id(m_id);
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
|
|
|
|
2016-08-13 13:04:53 +02:00
|
|
|
bool node::verify_token(string_view token, sha1_hash const& info_hash
|
2015-08-06 08:30:06 +02:00
|
|
|
, udp::endpoint const& addr) const
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2018-09-04 13:46:09 +02:00
|
|
|
if (token.length() != write_token_size)
|
2007-05-14 19:49:36 +02:00
|
|
|
{
|
2015-05-16 21:29:49 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-09 21:02:20 +02:00
|
|
|
if (m_observer != nullptr)
|
2015-05-17 22:59:18 +02:00
|
|
|
{
|
|
|
|
m_observer->log(dht_logger::node, "token of incorrect length: %d"
|
|
|
|
, int(token.length()));
|
|
|
|
}
|
2007-05-14 19:49:36 +02:00
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
2006-08-01 17:27:08 +02:00
|
|
|
|
|
|
|
hasher h1;
|
2008-10-21 10:45:42 +02:00
|
|
|
error_code ec;
|
2016-07-22 16:29:39 +02:00
|
|
|
std::string const address = addr.address().to_string(ec);
|
2008-10-21 10:45:42 +02:00
|
|
|
if (ec) return false;
|
2016-07-22 16:29:39 +02:00
|
|
|
h1.update(address);
|
2015-08-06 08:30:06 +02:00
|
|
|
h1.update(reinterpret_cast<char const*>(&m_secret[0]), sizeof(m_secret[0]));
|
2016-07-24 00:57:04 +02:00
|
|
|
h1.update(info_hash);
|
2015-05-17 22:59:18 +02:00
|
|
|
|
2006-08-01 17:27:08 +02:00
|
|
|
sha1_hash h = h1.final();
|
2015-08-06 08:30:06 +02:00
|
|
|
if (std::equal(token.begin(), token.end(), reinterpret_cast<char*>(&h[0])))
|
2006-08-01 17:27:08 +02:00
|
|
|
return true;
|
2015-05-17 22:59:18 +02:00
|
|
|
|
2006-08-01 17:27:08 +02:00
|
|
|
hasher h2;
|
2016-07-22 16:29:39 +02:00
|
|
|
h2.update(address);
|
2015-08-06 08:30:06 +02:00
|
|
|
h2.update(reinterpret_cast<char const*>(&m_secret[1]), sizeof(m_secret[1]));
|
2016-07-24 00:57:04 +02:00
|
|
|
h2.update(info_hash);
|
2006-08-01 17:27:08 +02:00
|
|
|
h = h2.final();
|
2016-12-27 16:12:57 +01:00
|
|
|
return std::equal(token.begin(), token.end(), reinterpret_cast<char*>(&h[0]));
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
|
|
|
|
2016-07-22 16:29:39 +02:00
|
|
|
std::string node::generate_token(udp::endpoint const& addr
|
2016-07-24 00:57:04 +02:00
|
|
|
, sha1_hash const& info_hash)
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
|
|
|
std::string token;
|
2018-09-04 13:46:09 +02:00
|
|
|
token.resize(write_token_size);
|
2006-08-01 17:27:08 +02:00
|
|
|
hasher h;
|
2008-10-21 10:45:42 +02:00
|
|
|
error_code ec;
|
2016-07-22 16:29:39 +02:00
|
|
|
std::string const address = addr.address().to_string(ec);
|
2008-10-21 10:45:42 +02:00
|
|
|
TORRENT_ASSERT(!ec);
|
2016-07-22 16:29:39 +02:00
|
|
|
h.update(address);
|
2015-08-06 08:30:06 +02:00
|
|
|
h.update(reinterpret_cast<char*>(&m_secret[0]), sizeof(m_secret[0]));
|
2016-07-24 00:57:04 +02:00
|
|
|
h.update(info_hash);
|
2006-08-01 17:27:08 +02:00
|
|
|
|
2016-07-22 16:29:39 +02:00
|
|
|
sha1_hash const hash = h.final();
|
2018-09-04 13:46:09 +02:00
|
|
|
std::copy(hash.begin(), hash.begin() + write_token_size, token.begin());
|
2016-07-22 16:29:39 +02:00
|
|
|
TORRENT_ASSERT(std::equal(token.begin(), token.end(), hash.data()));
|
2008-11-11 18:51:02 +01:00
|
|
|
return token;
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
|
|
|
|
2015-05-09 21:00:22 +02:00
|
|
|
void node::bootstrap(std::vector<udp::endpoint> const& nodes
|
2009-09-20 02:23:36 +02:00
|
|
|
, find_data::nodes_callback const& f)
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2014-11-08 17:58:18 +01:00
|
|
|
node_id target = m_id;
|
|
|
|
make_id_secret(target);
|
|
|
|
|
2016-09-02 22:42:55 +02:00
|
|
|
auto r = std::make_shared<dht::bootstrap>(*this, target, f);
|
2015-03-12 05:34:54 +01:00
|
|
|
m_last_self_refresh = aux::time_now();
|
2009-09-20 02:23:36 +02:00
|
|
|
|
2015-05-16 21:29:49 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2010-02-14 08:46:57 +01:00
|
|
|
int count = 0;
|
|
|
|
#endif
|
|
|
|
|
2016-09-02 22:42:55 +02:00
|
|
|
for (auto const& n : nodes)
|
2009-09-20 02:23:36 +02:00
|
|
|
{
|
2015-05-16 21:29:49 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2010-02-14 08:46:57 +01:00
|
|
|
++count;
|
|
|
|
#endif
|
2016-09-09 01:13:47 +02:00
|
|
|
r->add_entry(node_id(), n, observer::flag_initial);
|
2009-09-20 02:23:36 +02:00
|
|
|
}
|
2015-05-17 22:59:18 +02:00
|
|
|
|
2015-05-16 21:29:49 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-09 21:02:20 +02:00
|
|
|
if (m_observer != nullptr)
|
2015-05-17 22:59:18 +02:00
|
|
|
m_observer->log(dht_logger::node, "bootstrapping with %d nodes", count);
|
2010-02-14 08:46:57 +01:00
|
|
|
#endif
|
2009-09-20 02:23:36 +02:00
|
|
|
r->start();
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
2010-12-12 04:17:08 +01:00
|
|
|
|
2015-05-09 21:00:22 +02:00
|
|
|
int node::bucket_size(int bucket)
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
|
|
|
return m_table.bucket_size(bucket);
|
|
|
|
}
|
|
|
|
|
2015-05-09 21:00:22 +02:00
|
|
|
void node::new_write_key()
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
|
|
|
m_secret[1] = m_secret[0];
|
2016-08-06 19:18:48 +02:00
|
|
|
m_secret[0] = random(0xffffffff);
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
|
|
|
|
2015-05-09 21:00:22 +02:00
|
|
|
void node::unreachable(udp::endpoint const& ep)
|
2008-05-08 02:22:17 +02:00
|
|
|
{
|
|
|
|
m_rpc.unreachable(ep);
|
|
|
|
}
|
|
|
|
|
2017-09-22 06:00:38 +02:00
|
|
|
void node::incoming(aux::listen_socket_handle const& s, msg const& m)
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2009-09-20 02:23:36 +02:00
|
|
|
// is this a reply?
|
2017-02-27 21:52:46 +01:00
|
|
|
bdecode_node const y_ent = m.message.dict_find_string("y");
|
2019-04-30 09:11:00 +02:00
|
|
|
if (!y_ent || y_ent.string_length() != 1)
|
2009-09-20 02:23:36 +02:00
|
|
|
{
|
2014-01-03 09:02:53 +01:00
|
|
|
// don't respond to this obviously broken messages. We don't
|
|
|
|
// want to open up a magnification opportunity
|
|
|
|
// entry e;
|
|
|
|
// incoming_error(e, "missing 'y' entry");
|
2016-01-18 06:07:21 +01:00
|
|
|
// m_sock.send_packet(e, m.addr);
|
2009-09-20 02:23:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-01-15 19:47:41 +01:00
|
|
|
char const y = *(y_ent.string_ptr());
|
2009-09-20 02:23:36 +02:00
|
|
|
|
2020-01-15 19:47:41 +01:00
|
|
|
// we can only ascribe the external IP this node is saying we have to the
|
|
|
|
// listen socket the packet was received on
|
|
|
|
if (s == m_sock)
|
2013-12-20 05:47:41 +01:00
|
|
|
{
|
2020-01-15 19:47:41 +01:00
|
|
|
bdecode_node ext_ip = m.message.dict_find_string("ip");
|
2013-12-20 05:47:41 +01:00
|
|
|
|
2020-01-15 19:47:41 +01:00
|
|
|
// backwards compatibility
|
|
|
|
if (!ext_ip)
|
|
|
|
{
|
|
|
|
bdecode_node const r = m.message.dict_find_dict("r");
|
|
|
|
if (r)
|
|
|
|
ext_ip = r.dict_find_string("ip");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ext_ip && ext_ip.string_length() >= int(detail::address_size(udp::v6())))
|
|
|
|
{
|
|
|
|
// this node claims we use the wrong node-ID!
|
|
|
|
char const* ptr = ext_ip.string_ptr();
|
|
|
|
if (m_observer != nullptr)
|
|
|
|
m_observer->set_external_address(m_sock, detail::read_v6_address(ptr)
|
|
|
|
, m.addr.address());
|
|
|
|
}
|
|
|
|
else if (ext_ip && ext_ip.string_length() >= int(detail::address_size(udp::v4())))
|
|
|
|
{
|
|
|
|
char const* ptr = ext_ip.string_ptr();
|
|
|
|
if (m_observer != nullptr)
|
|
|
|
m_observer->set_external_address(m_sock, detail::read_v4_address(ptr)
|
|
|
|
, m.addr.address());
|
|
|
|
}
|
2013-10-14 03:03:43 +02:00
|
|
|
}
|
2013-10-14 01:04:40 +02:00
|
|
|
|
2009-09-20 02:23:36 +02:00
|
|
|
switch (y)
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2009-09-20 02:23:36 +02:00
|
|
|
case 'r':
|
|
|
|
{
|
2010-01-03 12:08:39 +01:00
|
|
|
node_id id;
|
2015-09-03 00:09:49 +02:00
|
|
|
m_rpc.incoming(m, &id);
|
2009-09-20 02:23:36 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'q':
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m.message.dict_find_string_value("y") == "q");
|
2015-09-01 23:07:38 +02:00
|
|
|
// When a DHT node enters the read-only state, it no longer
|
|
|
|
// responds to 'query' messages that it receives.
|
|
|
|
if (m_settings.read_only) break;
|
|
|
|
|
2020-01-15 19:47:41 +01:00
|
|
|
// ignore packets arriving on a different interface than the one we're
|
|
|
|
// associated with
|
|
|
|
if (s != m_sock) return;
|
2015-11-14 06:08:57 +01:00
|
|
|
|
2017-04-21 06:45:43 +02:00
|
|
|
if (!m_sock_man->has_quota())
|
2016-01-18 08:21:27 +01:00
|
|
|
{
|
|
|
|
m_counters.inc_stats_counter(counters::dht_messages_in_dropped);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-09-20 02:23:36 +02:00
|
|
|
entry e;
|
|
|
|
incoming_request(m, e);
|
2017-04-21 06:45:43 +02:00
|
|
|
m_sock_man->send_packet(m_sock, e, m.addr);
|
2009-09-20 02:23:36 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'e':
|
|
|
|
{
|
2015-05-16 21:29:49 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-09 21:02:20 +02:00
|
|
|
if (m_observer != nullptr && m_observer->should_log(dht_logger::node))
|
2009-09-20 02:23:36 +02:00
|
|
|
{
|
2017-02-27 21:52:46 +01:00
|
|
|
bdecode_node const err = m.message.dict_find_list("e");
|
2015-12-01 00:02:00 +01:00
|
|
|
if (err && err.list_size() >= 2
|
|
|
|
&& err.list_at(0).type() == bdecode_node::int_t
|
2016-09-09 21:02:20 +02:00
|
|
|
&& err.list_at(1).type() == bdecode_node::string_t)
|
2015-12-01 00:02:00 +01:00
|
|
|
{
|
|
|
|
m_observer->log(dht_logger::node, "INCOMING ERROR: (%" PRId64 ") %s"
|
|
|
|
, err.list_int_value_at(0)
|
2016-08-13 13:04:53 +02:00
|
|
|
, err.list_string_value_at(1).to_string().c_str());
|
2015-12-01 00:02:00 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_observer->log(dht_logger::node, "INCOMING ERROR (malformed)");
|
|
|
|
}
|
2009-09-20 02:23:36 +02:00
|
|
|
}
|
|
|
|
#endif
|
2014-01-03 09:02:53 +01:00
|
|
|
node_id id;
|
2015-09-03 00:09:49 +02:00
|
|
|
m_rpc.incoming(m, &id);
|
2009-09-20 02:23:36 +02:00
|
|
|
break;
|
|
|
|
}
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-12 19:00:57 +02:00
|
|
|
namespace {
|
|
|
|
|
2016-09-02 22:42:55 +02:00
|
|
|
void announce_fun(std::vector<std::pair<node_entry, std::string>> const& v
|
2018-07-15 18:56:14 +02:00
|
|
|
, node& node, int const listen_port, sha1_hash const& ih, announce_flags_t const flags)
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2015-05-16 21:29:49 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-12 15:20:15 +02:00
|
|
|
auto logger = node.observer();
|
|
|
|
if (logger != nullptr && logger->should_log(dht_logger::node))
|
2015-05-17 22:59:18 +02:00
|
|
|
{
|
2016-09-12 15:20:15 +02:00
|
|
|
logger->log(dht_logger::node, "sending announce_peer [ ih: %s "
|
2016-12-22 16:44:36 +01:00
|
|
|
" p: %d nodes: %d ]", aux::to_hex(ih).c_str(), listen_port, int(v.size()));
|
2015-05-17 22:59:18 +02:00
|
|
|
}
|
2008-03-24 03:19:47 +01:00
|
|
|
#endif
|
2009-10-07 22:51:02 +02:00
|
|
|
|
2015-05-17 22:59:18 +02:00
|
|
|
// create a dummy traversal_algorithm
|
2016-09-02 22:42:55 +02:00
|
|
|
auto algo = std::make_shared<traversal_algorithm>(node, node_id());
|
2008-12-23 21:04:12 +01:00
|
|
|
// store on the first k nodes
|
2016-09-02 22:42:55 +02:00
|
|
|
for (auto const& p : v)
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2015-05-16 21:29:49 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-12 15:20:15 +02:00
|
|
|
if (logger != nullptr && logger->should_log(dht_logger::node))
|
2015-05-17 22:59:18 +02:00
|
|
|
{
|
2016-09-12 15:20:15 +02:00
|
|
|
logger->log(dht_logger::node, "announce-distance: %d"
|
2016-09-02 22:42:55 +02:00
|
|
|
, (160 - distance_exp(ih, p.first.id)));
|
2015-05-17 22:59:18 +02:00
|
|
|
}
|
2008-05-08 03:17:14 +02:00
|
|
|
#endif
|
2008-12-23 21:04:12 +01:00
|
|
|
|
2016-09-03 03:05:11 +02:00
|
|
|
auto o = node.m_rpc.allocate_observer<announce_observer>(algo
|
|
|
|
, p.first.ep(), p.first.id);
|
|
|
|
if (!o) return;
|
2016-06-18 14:31:07 +02:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2008-02-09 22:04:24 +01:00
|
|
|
o->m_in_constructor = false;
|
|
|
|
#endif
|
2009-09-20 02:23:36 +02:00
|
|
|
entry e;
|
|
|
|
e["y"] = "q";
|
|
|
|
e["q"] = "announce_peer";
|
|
|
|
entry& a = e["a"];
|
2016-07-24 00:57:04 +02:00
|
|
|
a["info_hash"] = ih;
|
2009-09-20 02:23:36 +02:00
|
|
|
a["port"] = listen_port;
|
2016-09-02 22:42:55 +02:00
|
|
|
a["token"] = p.second;
|
2018-07-15 18:56:14 +02:00
|
|
|
a["seed"] = (flags & announce::seed) ? 1 : 0;
|
|
|
|
if (flags & announce::implied_port) a["implied_port"] = 1;
|
2015-01-18 02:06:36 +01:00
|
|
|
node.stats_counters().inc_stats_counter(counters::dht_announce_peer_out);
|
2016-09-02 22:42:55 +02:00
|
|
|
node.m_rpc.invoke(e, p.first.ep(), o);
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-12 15:20:15 +02:00
|
|
|
void node::add_router_node(udp::endpoint const& router)
|
2006-09-27 19:20:18 +02:00
|
|
|
{
|
2015-05-16 21:29:49 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-09 21:02:20 +02:00
|
|
|
if (m_observer != nullptr && m_observer->should_log(dht_logger::node))
|
2015-05-17 22:59:18 +02:00
|
|
|
{
|
|
|
|
m_observer->log(dht_logger::node, "adding router node: %s"
|
|
|
|
, print_endpoint(router).c_str());
|
|
|
|
}
|
2008-03-24 03:19:47 +01:00
|
|
|
#endif
|
2006-09-27 19:20:18 +02:00
|
|
|
m_table.add_router_node(router);
|
|
|
|
}
|
|
|
|
|
2016-09-09 21:02:20 +02:00
|
|
|
void node::add_node(udp::endpoint const& node)
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2015-11-14 06:08:57 +01:00
|
|
|
if (!native_address(node)) return;
|
2006-08-01 17:27:08 +02:00
|
|
|
// ping the node, and if we get a reply, it
|
|
|
|
// will be added to the routing table
|
2014-11-02 10:41:29 +01:00
|
|
|
send_single_refresh(node, m_table.num_active_buckets());
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
|
|
|
|
2015-06-23 18:24:30 +02:00
|
|
|
void node::get_peers(sha1_hash const& info_hash
|
2016-08-13 03:31:55 +02:00
|
|
|
, std::function<void(std::vector<tcp::endpoint> const&)> dcallback
|
2016-09-12 15:20:15 +02:00
|
|
|
, std::function<void(std::vector<std::pair<node_entry, std::string>> const&)> ncallback
|
2018-07-15 18:56:14 +02:00
|
|
|
, announce_flags_t const flags)
|
2015-06-23 18:24:30 +02:00
|
|
|
{
|
|
|
|
// search for nodes with ids close to id or with peers
|
|
|
|
// for info-hash id. then send announce_peer to them.
|
2018-07-15 18:56:14 +02:00
|
|
|
bool const noseeds = bool(flags & announce::seed);
|
2015-06-23 18:24:30 +02:00
|
|
|
|
2016-11-17 20:34:49 +01:00
|
|
|
auto ta = m_settings.privacy_lookups
|
|
|
|
? std::make_shared<dht::obfuscated_get_peers>(*this, info_hash, dcallback, ncallback, noseeds)
|
|
|
|
: std::make_shared<dht::get_peers>(*this, info_hash, dcallback, ncallback, noseeds);
|
2015-06-23 18:24:30 +02:00
|
|
|
|
|
|
|
ta->start();
|
|
|
|
}
|
|
|
|
|
2018-07-15 18:56:14 +02:00
|
|
|
void node::announce(sha1_hash const& info_hash, int listen_port, announce_flags_t const flags
|
2016-08-13 03:31:55 +02:00
|
|
|
, std::function<void(std::vector<tcp::endpoint> const&)> f)
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2015-05-16 21:29:49 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-09 21:02:20 +02:00
|
|
|
if (m_observer != nullptr && m_observer->should_log(dht_logger::node))
|
2015-05-17 22:59:18 +02:00
|
|
|
{
|
|
|
|
m_observer->log(dht_logger::node, "announcing [ ih: %s p: %d ]"
|
2016-12-22 16:44:36 +01:00
|
|
|
, aux::to_hex(info_hash).c_str(), listen_port);
|
2015-05-17 22:59:18 +02:00
|
|
|
}
|
2008-03-24 03:19:47 +01:00
|
|
|
#endif
|
2013-09-09 09:08:02 +02:00
|
|
|
|
2018-07-19 14:27:12 +02:00
|
|
|
if (listen_port == 0 && m_observer != nullptr)
|
2018-07-08 23:45:29 +02:00
|
|
|
{
|
|
|
|
listen_port = m_observer->get_listen_port(
|
2018-11-26 18:42:20 +01:00
|
|
|
(flags & announce::ssl_torrent) ? aux::transport::ssl : aux::transport::plaintext
|
2018-07-08 23:45:29 +02:00
|
|
|
, m_sock);
|
|
|
|
}
|
|
|
|
|
2017-11-12 00:50:24 +01:00
|
|
|
get_peers(info_hash, std::move(f)
|
2016-05-25 06:31:52 +02:00
|
|
|
, std::bind(&announce_fun, _1, std::ref(*this)
|
2018-07-15 18:56:14 +02:00
|
|
|
, listen_port, info_hash, flags), flags);
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
|
|
|
|
2016-09-13 14:18:47 +02:00
|
|
|
void node::direct_request(udp::endpoint const& ep, entry& e
|
2016-08-13 03:31:55 +02:00
|
|
|
, std::function<void(msg const&)> f)
|
2014-02-17 06:56:49 +01:00
|
|
|
{
|
|
|
|
// not really a traversal
|
2016-09-02 22:42:55 +02:00
|
|
|
auto algo = std::make_shared<direct_traversal>(*this, node_id(), f);
|
2014-02-17 06:56:49 +01:00
|
|
|
|
2017-11-06 02:17:56 +01:00
|
|
|
auto o = m_rpc.allocate_observer<direct_observer>(std::move(algo), ep, node_id());
|
2016-09-03 03:05:11 +02:00
|
|
|
if (!o) return;
|
2016-06-18 14:31:07 +02:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2014-02-17 06:56:49 +01:00
|
|
|
o->m_in_constructor = false;
|
|
|
|
#endif
|
|
|
|
m_rpc.invoke(e, ep, o);
|
|
|
|
}
|
|
|
|
|
2015-05-09 21:00:22 +02:00
|
|
|
void node::get_item(sha1_hash const& target
|
2016-08-13 03:31:55 +02:00
|
|
|
, std::function<void(item const&)> f)
|
2013-12-27 05:28:25 +01:00
|
|
|
{
|
2015-05-16 21:29:49 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-09 21:02:20 +02:00
|
|
|
if (m_observer != nullptr && m_observer->should_log(dht_logger::node))
|
2015-05-17 22:59:18 +02:00
|
|
|
{
|
|
|
|
m_observer->log(dht_logger::node, "starting get for [ hash: %s ]"
|
2016-12-22 16:44:36 +01:00
|
|
|
, aux::to_hex(target).c_str());
|
2015-05-17 22:59:18 +02:00
|
|
|
}
|
2013-12-27 05:28:25 +01:00
|
|
|
#endif
|
|
|
|
|
2016-09-02 22:42:55 +02:00
|
|
|
auto ta = std::make_shared<dht::get_item>(*this, target
|
|
|
|
, std::bind(f, _1), find_data::nodes_callback());
|
2013-12-27 05:28:25 +01:00
|
|
|
ta->start();
|
|
|
|
}
|
|
|
|
|
2016-07-24 00:57:04 +02:00
|
|
|
void node::get_item(public_key const& pk, std::string const& salt
|
2016-08-13 03:31:55 +02:00
|
|
|
, std::function<void(item const&, bool)> f)
|
2014-03-03 00:35:35 +01:00
|
|
|
{
|
2015-05-16 21:29:49 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-09 21:02:20 +02:00
|
|
|
if (m_observer != nullptr && m_observer->should_log(dht_logger::node))
|
2015-05-17 22:59:18 +02:00
|
|
|
{
|
|
|
|
char hex_key[65];
|
2016-07-29 08:36:15 +02:00
|
|
|
aux::to_hex(pk.bytes, hex_key);
|
2015-05-17 22:59:18 +02:00
|
|
|
m_observer->log(dht_logger::node, "starting get for [ key: %s ]", hex_key);
|
|
|
|
}
|
2014-03-03 00:35:35 +01:00
|
|
|
#endif
|
|
|
|
|
2016-09-02 22:42:55 +02:00
|
|
|
auto ta = std::make_shared<dht::get_item>(*this, pk, salt, f
|
|
|
|
, find_data::nodes_callback());
|
2015-09-22 20:10:57 +02:00
|
|
|
ta->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2016-09-02 22:42:55 +02:00
|
|
|
void put(std::vector<std::pair<node_entry, std::string>> const& nodes
|
2018-01-11 01:35:15 +01:00
|
|
|
, std::shared_ptr<put_data> const& ta)
|
2015-09-22 20:10:57 +02:00
|
|
|
{
|
2015-11-22 18:31:10 +01:00
|
|
|
ta->set_targets(nodes);
|
2015-09-22 20:10:57 +02:00
|
|
|
ta->start();
|
|
|
|
}
|
|
|
|
|
2018-11-14 12:30:27 +01:00
|
|
|
void put_data_cb(item const& i, bool auth
|
2018-01-11 01:35:15 +01:00
|
|
|
, std::shared_ptr<put_data> const& ta
|
|
|
|
, std::function<void(item&)> const& f)
|
2015-09-22 20:10:57 +02:00
|
|
|
{
|
|
|
|
// call data_callback only when we got authoritative data.
|
|
|
|
if (auth)
|
|
|
|
{
|
2018-11-14 12:30:27 +01:00
|
|
|
item copy(i);
|
|
|
|
f(copy);
|
|
|
|
ta->set_data(std::move(copy));
|
2015-09-22 20:10:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2016-08-13 03:31:55 +02:00
|
|
|
void node::put_item(sha1_hash const& target, entry const& data, std::function<void(int)> f)
|
2015-09-22 20:10:57 +02:00
|
|
|
{
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-09 21:02:20 +02:00
|
|
|
if (m_observer != nullptr && m_observer->should_log(dht_logger::node))
|
2015-09-22 20:10:57 +02:00
|
|
|
{
|
2019-07-05 20:01:16 +02:00
|
|
|
m_observer->log(dht_logger::node, "starting put for [ hash: %s ]"
|
2016-12-22 16:44:36 +01:00
|
|
|
, aux::to_hex(target).c_str());
|
2015-09-22 20:10:57 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
item i;
|
|
|
|
i.assign(data);
|
2016-09-02 22:42:55 +02:00
|
|
|
auto put_ta = std::make_shared<dht::put_data>(*this, std::bind(f, _2));
|
2018-11-14 12:30:27 +01:00
|
|
|
put_ta->set_data(std::move(i));
|
2015-09-22 20:10:57 +02:00
|
|
|
|
2016-09-02 22:42:55 +02:00
|
|
|
auto ta = std::make_shared<dht::get_item>(*this, target
|
|
|
|
, get_item::data_callback(), std::bind(&put, _1, put_ta));
|
2015-09-22 20:10:57 +02:00
|
|
|
ta->start();
|
|
|
|
}
|
|
|
|
|
2016-07-24 00:57:04 +02:00
|
|
|
void node::put_item(public_key const& pk, std::string const& salt
|
2016-08-13 03:31:55 +02:00
|
|
|
, std::function<void(item const&, int)> f
|
|
|
|
, std::function<void(item&)> data_cb)
|
2015-09-22 20:10:57 +02:00
|
|
|
{
|
2016-09-09 21:02:20 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
if (m_observer != nullptr && m_observer->should_log(dht_logger::node))
|
2015-09-22 20:10:57 +02:00
|
|
|
{
|
|
|
|
char hex_key[65];
|
2016-07-29 08:36:15 +02:00
|
|
|
aux::to_hex(pk.bytes, hex_key);
|
2019-07-05 20:01:16 +02:00
|
|
|
m_observer->log(dht_logger::node, "starting put for [ key: %s ]", hex_key);
|
2015-09-22 20:10:57 +02:00
|
|
|
}
|
2016-09-09 21:02:20 +02:00
|
|
|
#endif
|
2015-09-22 20:10:57 +02:00
|
|
|
|
2016-09-02 22:42:55 +02:00
|
|
|
auto put_ta = std::make_shared<dht::put_data>(*this, f);
|
2015-09-22 20:10:57 +02:00
|
|
|
|
2016-09-02 22:42:55 +02:00
|
|
|
auto ta = std::make_shared<dht::get_item>(*this, pk, salt
|
2016-05-25 06:31:52 +02:00
|
|
|
, std::bind(&put_data_cb, _1, _2, put_ta, data_cb)
|
2016-09-02 22:42:55 +02:00
|
|
|
, std::bind(&put, _1, put_ta));
|
2014-03-03 00:35:35 +01:00
|
|
|
ta->start();
|
|
|
|
}
|
|
|
|
|
2017-06-12 11:54:11 +02:00
|
|
|
void node::sample_infohashes(udp::endpoint const& ep, sha1_hash const& target
|
|
|
|
, std::function<void(time_duration
|
|
|
|
, int, std::vector<sha1_hash>
|
|
|
|
, std::vector<std::pair<sha1_hash, udp::endpoint>>)> f)
|
|
|
|
{
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
if (m_observer != nullptr && m_observer->should_log(dht_logger::node))
|
|
|
|
{
|
|
|
|
m_observer->log(dht_logger::node, "starting sample_infohashes for [ node: %s, target: %s ]"
|
|
|
|
, print_endpoint(ep).c_str(), aux::to_hex(target).c_str());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// not an actual traversal
|
|
|
|
auto ta = std::make_shared<dht::sample_infohashes>(*this, node_id(), std::move(f));
|
|
|
|
|
|
|
|
auto o = m_rpc.allocate_observer<sample_infohashes_observer>(ta, ep, node_id());
|
|
|
|
if (!o) return;
|
|
|
|
#if TORRENT_USE_ASSERTS
|
|
|
|
o->m_in_constructor = false;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
entry e;
|
|
|
|
|
|
|
|
e["q"] = "sample_infohashes";
|
|
|
|
e["a"]["target"] = target;
|
|
|
|
|
|
|
|
stats_counters().inc_stats_counter(counters::dht_sample_infohashes_out);
|
|
|
|
|
|
|
|
m_rpc.invoke(e, ep, o);
|
|
|
|
}
|
|
|
|
|
2014-11-01 23:47:56 +01:00
|
|
|
struct ping_observer : observer
|
|
|
|
{
|
|
|
|
ping_observer(
|
2017-11-06 02:17:56 +01:00
|
|
|
std::shared_ptr<traversal_algorithm> algorithm
|
2014-11-01 23:47:56 +01:00
|
|
|
, udp::endpoint const& ep, node_id const& id)
|
2017-11-06 02:17:56 +01:00
|
|
|
: observer(std::move(algorithm), ep, id)
|
2014-11-01 23:47:56 +01:00
|
|
|
{}
|
|
|
|
|
|
|
|
// parses out "nodes"
|
2016-07-10 02:10:38 +02:00
|
|
|
void reply(msg const& m) override
|
2014-11-01 23:47:56 +01:00
|
|
|
{
|
|
|
|
flags |= flag_done;
|
|
|
|
|
2017-02-27 21:52:46 +01:00
|
|
|
bdecode_node const r = m.message.dict_find_dict("r");
|
2014-11-01 23:47:56 +01:00
|
|
|
if (!r)
|
|
|
|
{
|
2015-05-16 21:29:49 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2015-05-22 04:42:26 +02:00
|
|
|
if (get_observer())
|
2015-05-17 22:59:18 +02:00
|
|
|
{
|
2015-05-22 04:42:26 +02:00
|
|
|
get_observer()->log(dht_logger::node
|
2015-05-17 22:59:18 +02:00
|
|
|
, "[%p] missing response dict"
|
2015-08-20 01:33:20 +02:00
|
|
|
, static_cast<void*>(algorithm()));
|
2015-05-17 22:59:18 +02:00
|
|
|
}
|
2014-11-01 23:47:56 +01:00
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
2017-05-29 18:20:52 +02:00
|
|
|
look_for_nodes(algorithm()->get_node().protocol_nodes_key(), algorithm()->get_node().protocol(), r,
|
|
|
|
[this](node_endpoint const& nep) { algorithm()->get_node().m_table.heard_about(nep.id, nep.ep); });
|
2014-11-01 23:47:56 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-05-09 21:00:22 +02:00
|
|
|
void node::tick()
|
2006-08-06 18:36:00 +02:00
|
|
|
{
|
2014-11-01 23:47:56 +01:00
|
|
|
// every now and then we refresh our own ID, just to keep
|
|
|
|
// expanding the routing table buckets closer to us.
|
2015-09-16 17:40:05 +02:00
|
|
|
// if m_table.depth() < 4, means routing_table doesn't
|
|
|
|
// have enough nodes.
|
2016-09-09 21:02:20 +02:00
|
|
|
time_point const now = aux::time_now();
|
2015-09-16 17:40:05 +02:00
|
|
|
if (m_last_self_refresh + minutes(10) < now && m_table.depth() < 4)
|
2014-11-01 23:47:56 +01:00
|
|
|
{
|
2014-11-08 17:58:18 +01:00
|
|
|
node_id target = m_id;
|
|
|
|
make_id_secret(target);
|
2016-09-09 21:02:20 +02:00
|
|
|
auto const r = std::make_shared<dht::bootstrap>(*this, target, std::bind(&nop));
|
2014-11-01 23:47:56 +01:00
|
|
|
r->start();
|
|
|
|
m_last_self_refresh = now;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-02 10:41:29 +01:00
|
|
|
node_entry const* ne = m_table.next_refresh();
|
2016-06-20 17:32:06 +02:00
|
|
|
if (ne == nullptr) return;
|
2014-11-01 23:47:56 +01:00
|
|
|
|
2014-11-26 03:02:32 +01:00
|
|
|
// this shouldn't happen
|
|
|
|
TORRENT_ASSERT(m_id != ne->id);
|
|
|
|
if (ne->id == m_id) return;
|
|
|
|
|
2016-09-09 21:02:20 +02:00
|
|
|
int const bucket = 159 - distance_exp(m_id, ne->id);
|
2014-11-26 03:02:32 +01:00
|
|
|
TORRENT_ASSERT(bucket < 160);
|
2014-11-02 10:41:29 +01:00
|
|
|
send_single_refresh(ne->ep(), bucket, ne->id);
|
|
|
|
}
|
|
|
|
|
2016-09-09 21:02:20 +02:00
|
|
|
void node::send_single_refresh(udp::endpoint const& ep, int const bucket
|
2014-11-02 10:41:29 +01:00
|
|
|
, node_id const& id)
|
|
|
|
{
|
2014-11-26 03:02:32 +01:00
|
|
|
TORRENT_ASSERT(id != m_id);
|
|
|
|
TORRENT_ASSERT(bucket >= 0);
|
2014-11-26 04:58:55 +01:00
|
|
|
TORRENT_ASSERT(bucket <= 159);
|
2014-11-26 03:02:32 +01:00
|
|
|
|
2014-11-02 10:41:29 +01:00
|
|
|
// generate a random node_id within the given bucket
|
|
|
|
// TODO: 2 it would be nice to have a bias towards node-id prefixes that
|
|
|
|
// are missing in the bucket
|
|
|
|
node_id mask = generate_prefix_mask(bucket + 1);
|
2014-11-08 17:58:18 +01:00
|
|
|
node_id target = generate_secret_id() & ~mask;
|
2014-11-03 07:15:51 +01:00
|
|
|
target |= m_id & mask;
|
2014-11-02 10:41:29 +01:00
|
|
|
|
2015-05-18 01:32:13 +02:00
|
|
|
// create a dummy traversal_algorithm
|
2017-11-06 02:17:56 +01:00
|
|
|
auto algo = std::make_shared<traversal_algorithm>(*this, node_id());
|
|
|
|
auto o = m_rpc.allocate_observer<ping_observer>(std::move(algo), ep, id);
|
2016-09-03 03:05:11 +02:00
|
|
|
if (!o) return;
|
2016-06-18 14:31:07 +02:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2014-11-01 23:47:56 +01:00
|
|
|
o->m_in_constructor = false;
|
|
|
|
#endif
|
|
|
|
entry e;
|
|
|
|
e["y"] = "q";
|
2014-11-02 10:41:29 +01:00
|
|
|
|
2015-09-11 22:43:21 +02:00
|
|
|
if (m_table.is_full(bucket))
|
|
|
|
{
|
|
|
|
// current bucket is full, just ping it.
|
|
|
|
e["q"] = "ping";
|
|
|
|
m_counters.inc_stats_counter(counters::dht_ping_out);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// use get_peers instead of find_node. We'll get nodes in the response
|
|
|
|
// either way.
|
|
|
|
e["q"] = "get_peers";
|
2018-11-26 18:42:20 +01:00
|
|
|
e["a"]["info_hash"] = target.to_string();
|
2015-09-11 22:43:21 +02:00
|
|
|
m_counters.inc_stats_counter(counters::dht_get_peers_out);
|
|
|
|
}
|
|
|
|
|
2014-11-02 10:41:29 +01:00
|
|
|
m_rpc.invoke(e, ep, o);
|
2006-08-06 18:36:00 +02:00
|
|
|
}
|
|
|
|
|
2015-05-09 21:00:22 +02:00
|
|
|
time_duration node::connection_timeout()
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
|
|
|
time_duration d = m_rpc.tick();
|
2015-03-12 05:34:54 +01:00
|
|
|
time_point now(aux::time_now());
|
2014-07-06 21:18:00 +02:00
|
|
|
if (now - minutes(2) < m_last_tracker_tick) return d;
|
2008-10-21 19:10:11 +02:00
|
|
|
m_last_tracker_tick = now;
|
|
|
|
|
2016-06-04 01:44:16 +02:00
|
|
|
m_storage.tick();
|
2007-01-07 14:55:27 +01:00
|
|
|
|
2006-08-01 17:27:08 +02:00
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
2015-05-09 21:00:22 +02:00
|
|
|
void node::status(std::vector<dht_routing_bucket>& table
|
2015-01-17 18:02:58 +01:00
|
|
|
, std::vector<dht_lookup>& requests)
|
|
|
|
{
|
2016-05-01 00:54:23 +02:00
|
|
|
std::lock_guard<std::mutex> l(m_mutex);
|
2015-01-17 18:02:58 +01:00
|
|
|
|
|
|
|
m_table.status(table);
|
|
|
|
|
2016-09-09 21:02:20 +02:00
|
|
|
for (auto const& r : m_running_requests)
|
2015-01-17 18:02:58 +01:00
|
|
|
{
|
2018-01-11 01:35:15 +01:00
|
|
|
requests.emplace_back();
|
2015-08-18 13:55:50 +02:00
|
|
|
dht_lookup& lookup = requests.back();
|
2016-09-09 21:02:20 +02:00
|
|
|
r->status(lookup);
|
2015-01-17 18:02:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-20 17:32:06 +02:00
|
|
|
std::tuple<int, int, int> node::get_stats_counters() const
|
2015-09-17 17:11:46 +02:00
|
|
|
{
|
2016-05-13 03:27:11 +02:00
|
|
|
int nodes, replacements;
|
2016-06-20 17:32:06 +02:00
|
|
|
std::tie(nodes, replacements, std::ignore) = size();
|
|
|
|
return std::make_tuple(nodes, replacements, m_rpc.num_allocated_observers());
|
2015-09-17 17:11:46 +02:00
|
|
|
}
|
|
|
|
|
2018-04-26 09:01:14 +02:00
|
|
|
#if TORRENT_ABI_VERSION == 1
|
2015-01-17 18:02:58 +01:00
|
|
|
// TODO: 2 use the non deprecated function instead of this one
|
2015-05-09 21:00:22 +02:00
|
|
|
void node::status(session_status& s)
|
2008-09-20 19:42:25 +02:00
|
|
|
{
|
2016-05-01 00:54:23 +02:00
|
|
|
std::lock_guard<std::mutex> l(m_mutex);
|
2009-01-23 11:36:07 +01:00
|
|
|
|
|
|
|
m_table.status(s);
|
2016-06-04 20:04:29 +02:00
|
|
|
s.dht_total_allocations += m_rpc.num_allocated_observers();
|
2018-01-11 01:35:15 +01:00
|
|
|
for (auto& r : m_running_requests)
|
2008-09-20 19:42:25 +02:00
|
|
|
{
|
2018-01-11 01:35:15 +01:00
|
|
|
s.active_requests.emplace_back();
|
2015-08-18 13:55:50 +02:00
|
|
|
dht_lookup& lookup = s.active_requests.back();
|
2018-01-11 01:35:15 +01:00
|
|
|
r->status(lookup);
|
2008-09-20 19:42:25 +02:00
|
|
|
}
|
|
|
|
}
|
2015-01-04 22:31:02 +01:00
|
|
|
#endif
|
2008-09-20 19:42:25 +02:00
|
|
|
|
2016-09-20 05:47:17 +02:00
|
|
|
bool node::lookup_peers(sha1_hash const& info_hash, entry& reply
|
2016-09-20 19:45:13 +02:00
|
|
|
, bool noseed, bool scrape, address const& requester) const
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
if (m_observer)
|
|
|
|
m_observer->get_peers(info_hash);
|
2008-09-20 19:42:25 +02:00
|
|
|
|
2016-09-22 05:00:39 +02:00
|
|
|
return m_storage.get_peers(info_hash, noseed, scrape, requester, reply);
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
|
|
|
|
2016-09-19 02:08:15 +02:00
|
|
|
entry write_nodes_entry(std::vector<node_entry> const& nodes)
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2016-09-19 02:08:15 +02:00
|
|
|
entry r;
|
2016-09-09 21:02:20 +02:00
|
|
|
std::back_insert_iterator<std::string> out(r.string());
|
|
|
|
for (auto const& n : nodes)
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2016-09-09 21:02:20 +02:00
|
|
|
std::copy(n.id.begin(), n.id.end(), out);
|
2018-09-17 16:56:48 +02:00
|
|
|
detail::write_endpoint(n.ep(), out);
|
2009-09-20 02:23:36 +02:00
|
|
|
}
|
2016-09-19 02:08:15 +02:00
|
|
|
return r;
|
2009-09-27 19:41:51 +02:00
|
|
|
}
|
2014-10-01 18:21:29 +02:00
|
|
|
|
2009-09-20 02:23:36 +02:00
|
|
|
// build response
|
2015-05-09 21:00:22 +02:00
|
|
|
void node::incoming_request(msg const& m, entry& e)
|
2009-09-20 02:23:36 +02:00
|
|
|
{
|
|
|
|
e = entry(entry::dictionary_t);
|
|
|
|
e["y"] = "r";
|
2016-08-13 13:04:53 +02:00
|
|
|
e["t"] = m.message.dict_find_string_value("t").to_string();
|
2009-09-20 02:23:36 +02:00
|
|
|
|
2017-06-30 20:03:34 +02:00
|
|
|
static key_desc_t const top_desc[] = {
|
2015-03-12 06:20:12 +01:00
|
|
|
{"q", bdecode_node::string_t, 0, 0},
|
|
|
|
{"ro", bdecode_node::int_t, 0, key_desc_t::optional},
|
|
|
|
{"a", bdecode_node::dict_t, 0, key_desc_t::parse_children},
|
|
|
|
{"id", bdecode_node::string_t, 20, key_desc_t::last_child},
|
2009-09-27 07:27:43 +02:00
|
|
|
};
|
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node top_level[4];
|
2009-09-27 07:27:43 +02:00
|
|
|
char error_string[200];
|
2016-09-22 08:04:05 +02:00
|
|
|
if (!verify_message(m.message, top_desc, top_level, error_string))
|
2009-09-20 02:23:36 +02:00
|
|
|
{
|
2009-09-27 07:27:43 +02:00
|
|
|
incoming_error(e, error_string);
|
2009-09-20 02:23:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-14 01:04:40 +02:00
|
|
|
e["ip"] = endpoint_to_bytes(m.addr);
|
2013-10-14 03:03:43 +02:00
|
|
|
|
2017-06-30 20:03:34 +02:00
|
|
|
bdecode_node const arg_ent = top_level[2];
|
|
|
|
bool const read_only = top_level[1] && top_level[1].int_value() != 0;
|
|
|
|
node_id const id(top_level[3].string_ptr());
|
2013-10-14 03:03:43 +02:00
|
|
|
|
2013-10-14 01:04:40 +02:00
|
|
|
// if this nodes ID doesn't match its IP, tell it what
|
|
|
|
// its IP is with an error
|
|
|
|
// don't enforce this yet
|
2013-10-14 03:03:43 +02:00
|
|
|
if (m_settings.enforce_node_id && !verify_id(id, m.addr.address()))
|
2013-10-14 01:04:40 +02:00
|
|
|
{
|
|
|
|
incoming_error(e, "invalid node ID");
|
|
|
|
return;
|
|
|
|
}
|
2009-09-20 02:23:36 +02:00
|
|
|
|
2014-10-12 06:18:34 +02:00
|
|
|
if (!read_only)
|
|
|
|
m_table.heard_about(id, m.addr);
|
2009-09-20 02:23:36 +02:00
|
|
|
|
|
|
|
entry& reply = e["r"];
|
|
|
|
m_rpc.add_our_id(reply);
|
|
|
|
|
2013-09-03 02:45:48 +02:00
|
|
|
// mirror back the other node's external port
|
|
|
|
reply["p"] = m.addr.port();
|
|
|
|
|
2018-02-28 15:07:56 +01:00
|
|
|
string_view const query = top_level[0].string_value();
|
2014-02-17 06:56:49 +01:00
|
|
|
|
2016-08-15 22:17:13 +02:00
|
|
|
if (m_observer && m_observer->on_dht_request(query, m, e))
|
2014-02-17 06:56:49 +01:00
|
|
|
return;
|
|
|
|
|
2016-08-15 22:17:13 +02:00
|
|
|
if (query == "ping")
|
2009-09-20 02:23:36 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
m_counters.inc_stats_counter(counters::dht_ping_in);
|
2009-09-20 02:23:36 +02:00
|
|
|
// we already have 't' and 'id' in the response
|
|
|
|
// no more left to add
|
|
|
|
}
|
2016-08-15 22:17:13 +02:00
|
|
|
else if (query == "get_peers")
|
2009-09-20 02:23:36 +02:00
|
|
|
{
|
2017-06-30 20:03:34 +02:00
|
|
|
static key_desc_t const msg_desc[] = {
|
2015-03-12 06:20:12 +01:00
|
|
|
{"info_hash", bdecode_node::string_t, 20, 0},
|
|
|
|
{"noseed", bdecode_node::int_t, 0, key_desc_t::optional},
|
|
|
|
{"scrape", bdecode_node::int_t, 0, key_desc_t::optional},
|
2015-11-14 06:08:57 +01:00
|
|
|
{"want", bdecode_node::list_t, 0, key_desc_t::optional},
|
2009-09-27 07:27:43 +02:00
|
|
|
};
|
|
|
|
|
2015-11-14 06:08:57 +01:00
|
|
|
bdecode_node msg_keys[4];
|
2016-09-22 08:04:05 +02:00
|
|
|
if (!verify_message(arg_ent, msg_desc, msg_keys, error_string))
|
2009-09-20 02:23:36 +02:00
|
|
|
{
|
2015-01-18 02:06:36 +01:00
|
|
|
m_counters.inc_stats_counter(counters::dht_invalid_get_peers);
|
2009-09-27 07:27:43 +02:00
|
|
|
incoming_error(e, error_string);
|
2009-09-20 02:23:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-08-30 19:28:46 +02:00
|
|
|
sha1_hash const info_hash(msg_keys[0].string_ptr());
|
2015-12-01 00:02:00 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
m_counters.inc_stats_counter(counters::dht_get_peers_in);
|
|
|
|
|
2009-09-20 02:23:36 +02:00
|
|
|
// always return nodes as well as peers
|
2015-11-14 06:08:57 +01:00
|
|
|
write_nodes_entries(info_hash, msg_keys[3], reply);
|
2009-09-20 02:23:36 +02:00
|
|
|
|
2017-06-30 20:03:34 +02:00
|
|
|
bool const noseed = msg_keys[1] && msg_keys[1].int_value() != 0;
|
|
|
|
bool const scrape = msg_keys[2] && msg_keys[2].int_value() != 0;
|
2016-09-20 19:45:13 +02:00
|
|
|
// If our storage is full we want to withhold the write token so that
|
|
|
|
// announces will spill over to our neighbors. This widens the
|
|
|
|
// perimeter of nodes which store peers for this torrent
|
2017-06-30 20:03:34 +02:00
|
|
|
bool const full = lookup_peers(info_hash, reply, noseed, scrape, m.addr.address());
|
2016-09-20 05:47:17 +02:00
|
|
|
if (!full) reply["token"] = generate_token(m.addr, info_hash);
|
|
|
|
|
2015-05-16 21:29:49 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2015-05-17 22:59:18 +02:00
|
|
|
if (reply.find_key("values") && m_observer)
|
2013-01-20 08:54:54 +01:00
|
|
|
{
|
2015-05-10 06:54:02 +02:00
|
|
|
m_observer->log(dht_logger::node, "values: %d"
|
|
|
|
, int(reply["values"].list().size()));
|
2013-01-20 08:54:54 +01:00
|
|
|
}
|
2006-08-01 17:27:08 +02:00
|
|
|
#endif
|
2009-09-20 02:23:36 +02:00
|
|
|
}
|
2016-08-15 22:17:13 +02:00
|
|
|
else if (query == "find_node")
|
2009-09-20 02:23:36 +02:00
|
|
|
{
|
2017-06-30 20:03:34 +02:00
|
|
|
static key_desc_t const msg_desc[] = {
|
2015-03-12 06:20:12 +01:00
|
|
|
{"target", bdecode_node::string_t, 20, 0},
|
2015-11-14 06:08:57 +01:00
|
|
|
{"want", bdecode_node::list_t, 0, key_desc_t::optional},
|
2009-09-27 07:27:43 +02:00
|
|
|
};
|
|
|
|
|
2015-11-14 06:08:57 +01:00
|
|
|
bdecode_node msg_keys[2];
|
2016-09-22 08:04:05 +02:00
|
|
|
if (!verify_message(arg_ent, msg_desc, msg_keys, error_string))
|
2009-09-20 02:23:36 +02:00
|
|
|
{
|
2017-01-18 00:43:52 +01:00
|
|
|
m_counters.inc_stats_counter(counters::dht_invalid_find_node);
|
2009-09-27 07:27:43 +02:00
|
|
|
incoming_error(e, error_string);
|
2009-09-20 02:23:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
m_counters.inc_stats_counter(counters::dht_find_node_in);
|
2017-06-30 20:03:34 +02:00
|
|
|
sha1_hash const target(msg_keys[0].string_ptr());
|
2009-09-27 07:27:43 +02:00
|
|
|
|
2015-11-14 06:08:57 +01:00
|
|
|
write_nodes_entries(target, msg_keys[1], reply);
|
2009-09-20 02:23:36 +02:00
|
|
|
}
|
2016-08-15 22:17:13 +02:00
|
|
|
else if (query == "announce_peer")
|
2009-09-20 02:23:36 +02:00
|
|
|
{
|
2017-06-30 20:03:34 +02:00
|
|
|
static key_desc_t const msg_desc[] = {
|
2015-03-12 06:20:12 +01:00
|
|
|
{"info_hash", bdecode_node::string_t, 20, 0},
|
|
|
|
{"port", bdecode_node::int_t, 0, 0},
|
|
|
|
{"token", bdecode_node::string_t, 0, 0},
|
|
|
|
{"n", bdecode_node::string_t, 0, key_desc_t::optional},
|
|
|
|
{"seed", bdecode_node::int_t, 0, key_desc_t::optional},
|
|
|
|
{"implied_port", bdecode_node::int_t, 0, key_desc_t::optional},
|
2009-09-27 07:27:43 +02:00
|
|
|
};
|
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node msg_keys[6];
|
2016-09-22 08:04:05 +02:00
|
|
|
if (!verify_message(arg_ent, msg_desc, msg_keys, error_string))
|
2009-09-20 02:23:36 +02:00
|
|
|
{
|
2015-01-18 02:06:36 +01:00
|
|
|
m_counters.inc_stats_counter(counters::dht_invalid_announce);
|
2009-09-27 07:27:43 +02:00
|
|
|
incoming_error(e, error_string);
|
2009-09-20 02:23:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-01-11 01:35:15 +01:00
|
|
|
auto port = int(msg_keys[1].int_value());
|
2013-03-25 08:26:39 +01:00
|
|
|
|
|
|
|
// is the announcer asking to ignore the explicit
|
|
|
|
// listen port and instead use the source port of the packet?
|
2015-03-12 06:20:12 +01:00
|
|
|
if (msg_keys[5] && msg_keys[5].int_value() != 0)
|
2013-03-25 08:26:39 +01:00
|
|
|
port = m.addr.port();
|
|
|
|
|
2009-09-20 02:23:36 +02:00
|
|
|
if (port < 0 || port >= 65536)
|
|
|
|
{
|
2015-01-18 02:06:36 +01:00
|
|
|
m_counters.inc_stats_counter(counters::dht_invalid_announce);
|
2011-01-19 06:57:44 +01:00
|
|
|
incoming_error(e, "invalid port");
|
2009-09-20 02:23:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-06-30 20:03:34 +02:00
|
|
|
sha1_hash const info_hash(msg_keys[0].string_ptr());
|
2009-09-20 02:23:36 +02:00
|
|
|
|
2015-04-03 22:15:48 +02:00
|
|
|
if (m_observer)
|
|
|
|
m_observer->announce(info_hash, m.addr.address(), port);
|
2009-09-20 02:23:36 +02:00
|
|
|
|
2016-07-24 00:57:04 +02:00
|
|
|
if (!verify_token(msg_keys[2].string_value()
|
|
|
|
, sha1_hash(msg_keys[0].string_ptr()), m.addr))
|
2009-09-20 02:23:36 +02:00
|
|
|
{
|
2015-01-18 02:06:36 +01:00
|
|
|
m_counters.inc_stats_counter(counters::dht_invalid_announce);
|
2011-01-19 06:57:44 +01:00
|
|
|
incoming_error(e, "invalid token");
|
2009-09-20 02:23:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
m_counters.inc_stats_counter(counters::dht_announce_peer_in);
|
|
|
|
|
2009-09-20 02:23:36 +02:00
|
|
|
// the token was correct. That means this
|
|
|
|
// node is not spoofing its address. So, let
|
|
|
|
// the table get a chance to add it.
|
2012-09-22 20:15:29 +02:00
|
|
|
m_table.node_seen(id, m.addr, 0xffff);
|
2009-09-20 02:23:36 +02:00
|
|
|
|
2017-06-30 20:03:34 +02:00
|
|
|
tcp::endpoint const addr = tcp::endpoint(m.addr.address(), std::uint16_t(port));
|
|
|
|
string_view const name = msg_keys[3] ? msg_keys[3].string_value() : string_view();
|
|
|
|
bool const seed = msg_keys[4] && msg_keys[4].int_value();
|
2010-11-27 04:09:28 +01:00
|
|
|
|
2016-08-15 22:17:13 +02:00
|
|
|
m_storage.announce_peer(info_hash, addr, name, seed);
|
2009-09-20 02:23:36 +02:00
|
|
|
}
|
2016-08-15 22:17:13 +02:00
|
|
|
else if (query == "put")
|
2011-01-19 06:57:44 +01:00
|
|
|
{
|
2011-05-25 04:26:07 +02:00
|
|
|
// the first 2 entries are for both mutable and
|
|
|
|
// immutable puts
|
2017-06-30 20:03:34 +02:00
|
|
|
static key_desc_t const msg_desc[] = {
|
2015-03-12 06:20:12 +01:00
|
|
|
{"token", bdecode_node::string_t, 0, 0},
|
|
|
|
{"v", bdecode_node::none_t, 0, 0},
|
|
|
|
{"seq", bdecode_node::int_t, 0, key_desc_t::optional},
|
2011-05-25 04:26:07 +02:00
|
|
|
// public key
|
2016-07-24 00:57:04 +02:00
|
|
|
{"k", bdecode_node::string_t, public_key::len, key_desc_t::optional},
|
|
|
|
{"sig", bdecode_node::string_t, signature::len, key_desc_t::optional},
|
2015-03-12 06:20:12 +01:00
|
|
|
{"cas", bdecode_node::int_t, 0, key_desc_t::optional},
|
|
|
|
{"salt", bdecode_node::string_t, 0, key_desc_t::optional},
|
2011-01-19 06:57:44 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
// attempt to parse the message
|
2017-07-03 00:30:32 +02:00
|
|
|
// also reject the message if it has any non-fatal encoding errors
|
|
|
|
// because put messages contain a signed value they must have correct bencoding
|
|
|
|
// otherwise the value will not round-trip without breaking the signature
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node msg_keys[7];
|
2017-07-03 00:30:32 +02:00
|
|
|
if (!verify_message(arg_ent, msg_desc, msg_keys, error_string)
|
|
|
|
|| arg_ent.has_soft_error(error_string))
|
2011-01-19 06:57:44 +01:00
|
|
|
{
|
2015-01-18 02:06:36 +01:00
|
|
|
m_counters.inc_stats_counter(counters::dht_invalid_put);
|
2011-01-19 06:57:44 +01:00
|
|
|
incoming_error(e, error_string);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
m_counters.inc_stats_counter(counters::dht_put_in);
|
|
|
|
|
2011-05-25 04:26:07 +02:00
|
|
|
// is this a mutable put?
|
2017-06-30 20:03:34 +02:00
|
|
|
bool const mutable_put = (msg_keys[2] && msg_keys[3] && msg_keys[4]);
|
2011-05-25 04:26:07 +02:00
|
|
|
|
2014-01-03 05:18:46 +01:00
|
|
|
// public key (only set if it's a mutable put)
|
2016-07-24 00:57:04 +02:00
|
|
|
char const* pub_key = nullptr;
|
|
|
|
if (msg_keys[3]) pub_key = msg_keys[3].string_ptr();
|
2014-01-03 05:18:46 +01:00
|
|
|
|
|
|
|
// signature (only set if it's a mutable put)
|
2016-07-24 00:57:04 +02:00
|
|
|
char const* sign = nullptr;
|
|
|
|
if (msg_keys[4]) sign = msg_keys[4].string_ptr();
|
2014-01-03 05:18:46 +01:00
|
|
|
|
2011-05-23 07:07:52 +02:00
|
|
|
// pointer and length to the whole entry
|
2016-07-24 00:57:04 +02:00
|
|
|
span<char const> buf = msg_keys[1].data_section();
|
2017-11-12 00:50:24 +01:00
|
|
|
if (buf.size() > 1000 || buf.empty())
|
2011-01-19 06:57:44 +01:00
|
|
|
{
|
2015-01-18 02:06:36 +01:00
|
|
|
m_counters.inc_stats_counter(counters::dht_invalid_put);
|
2013-09-03 02:45:48 +02:00
|
|
|
incoming_error(e, "message too big", 205);
|
2011-01-19 06:57:44 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-07-24 00:57:04 +02:00
|
|
|
span<char const> salt;
|
2014-01-03 05:18:46 +01:00
|
|
|
if (msg_keys[6])
|
2018-11-01 23:05:30 +01:00
|
|
|
salt = {msg_keys[6].string_ptr(), msg_keys[6].string_length()};
|
2016-07-24 00:57:04 +02:00
|
|
|
if (salt.size() > 64)
|
2014-01-03 05:18:46 +01:00
|
|
|
{
|
2015-01-18 02:06:36 +01:00
|
|
|
m_counters.inc_stats_counter(counters::dht_invalid_put);
|
2014-01-03 05:18:46 +01:00
|
|
|
incoming_error(e, "salt too big", 207);
|
|
|
|
return;
|
|
|
|
}
|
2011-05-25 04:26:07 +02:00
|
|
|
|
2016-07-24 00:57:04 +02:00
|
|
|
sha1_hash const target = pub_key
|
|
|
|
? item_target_id(salt, public_key(pub_key))
|
|
|
|
: item_target_id(buf);
|
2014-01-03 05:18:46 +01:00
|
|
|
|
2016-05-17 15:24:06 +02:00
|
|
|
// std::fprintf(stderr, "%s PUT target: %s salt: %s key: %s\n"
|
2011-05-25 04:26:07 +02:00
|
|
|
// , mutable_put ? "mutable":"immutable"
|
2016-07-29 08:36:15 +02:00
|
|
|
// , aux::to_hex(target).c_str()
|
2014-01-03 05:18:46 +01:00
|
|
|
// , salt.second > 0 ? std::string(salt.first, salt.second).c_str() : ""
|
2016-07-29 08:36:15 +02:00
|
|
|
// , pk ? aux::to_hex(pk).c_str() : "");
|
2011-05-23 07:07:52 +02:00
|
|
|
|
|
|
|
// verify the write-token. tokens are only valid to write to
|
|
|
|
// specific target hashes. it must match the one we got a "get" for
|
2016-07-24 00:57:04 +02:00
|
|
|
if (!verify_token(msg_keys[0].string_value(), target, m.addr))
|
2011-01-19 06:57:44 +01:00
|
|
|
{
|
2015-01-18 02:06:36 +01:00
|
|
|
m_counters.inc_stats_counter(counters::dht_invalid_put);
|
2011-05-23 07:07:52 +02:00
|
|
|
incoming_error(e, "invalid token");
|
2011-01-19 06:57:44 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-05-25 04:26:07 +02:00
|
|
|
if (!mutable_put)
|
2011-01-19 06:57:44 +01:00
|
|
|
{
|
2016-07-24 00:57:04 +02:00
|
|
|
m_storage.put_immutable_item(target, buf, m.addr.address());
|
2011-01-19 06:57:44 +01:00
|
|
|
}
|
2011-05-25 04:26:07 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// mutable put, we must verify the signature
|
2016-07-24 00:57:04 +02:00
|
|
|
sequence_number const seq(msg_keys[2].int_value());
|
|
|
|
public_key const pk(pub_key);
|
|
|
|
signature const sig(sign);
|
2015-09-08 22:12:54 +02:00
|
|
|
|
2016-07-24 00:57:04 +02:00
|
|
|
if (seq < sequence_number(0))
|
2015-09-08 22:12:54 +02:00
|
|
|
{
|
|
|
|
m_counters.inc_stats_counter(counters::dht_invalid_put);
|
|
|
|
incoming_error(e, "invalid (negative) sequence number");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-18 18:01:20 +02:00
|
|
|
// msg_keys[4] is the signature, msg_keys[3] is the public key
|
2016-07-24 00:57:04 +02:00
|
|
|
if (!verify_mutable_item(buf, salt, seq, pk, sig))
|
2011-05-25 04:26:07 +02:00
|
|
|
{
|
2015-01-18 02:06:36 +01:00
|
|
|
m_counters.inc_stats_counter(counters::dht_invalid_put);
|
2013-09-03 02:45:48 +02:00
|
|
|
incoming_error(e, "invalid signature", 206);
|
2011-05-25 04:26:07 +02:00
|
|
|
return;
|
|
|
|
}
|
2011-05-23 07:07:52 +02:00
|
|
|
|
2016-07-24 00:57:04 +02:00
|
|
|
TORRENT_ASSERT(signature::len == msg_keys[4].string_length());
|
2011-05-25 04:26:07 +02:00
|
|
|
|
2016-07-24 00:57:04 +02:00
|
|
|
sequence_number item_seq;
|
2016-06-04 01:44:16 +02:00
|
|
|
if (!m_storage.get_mutable_item_seq(target, item_seq))
|
2015-09-08 22:12:54 +02:00
|
|
|
{
|
2016-07-24 00:57:04 +02:00
|
|
|
m_storage.put_mutable_item(target, buf, sig, seq, pk, salt
|
2015-09-08 22:12:54 +02:00
|
|
|
, m.addr.address());
|
2011-05-25 04:26:07 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-09-03 02:45:48 +02:00
|
|
|
// this is the "cas" field in the put message
|
2014-08-27 07:57:37 +02:00
|
|
|
// if it was specified, we MUST make sure the current sequence
|
|
|
|
// number matches the expected value before replacing it
|
|
|
|
// this is critical for avoiding race conditions when multiple
|
|
|
|
// writers are accessing the same slot
|
2016-11-27 14:46:53 +01:00
|
|
|
if (msg_keys[5] && item_seq.value != msg_keys[5].int_value())
|
2013-09-03 02:45:48 +02:00
|
|
|
{
|
2015-01-18 02:06:36 +01:00
|
|
|
m_counters.inc_stats_counter(counters::dht_invalid_put);
|
2014-08-27 07:57:37 +02:00
|
|
|
incoming_error(e, "CAS mismatch", 301);
|
|
|
|
return;
|
2013-09-03 02:45:48 +02:00
|
|
|
}
|
|
|
|
|
2015-09-08 22:12:54 +02:00
|
|
|
if (item_seq > seq)
|
2011-05-25 04:26:07 +02:00
|
|
|
{
|
2015-01-18 02:06:36 +01:00
|
|
|
m_counters.inc_stats_counter(counters::dht_invalid_put);
|
2013-09-03 02:45:48 +02:00
|
|
|
incoming_error(e, "old sequence number", 302);
|
2011-05-25 04:26:07 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-07-24 00:57:04 +02:00
|
|
|
m_storage.put_mutable_item(target, buf, sig, seq, pk, salt
|
2015-09-08 22:12:54 +02:00
|
|
|
, m.addr.address());
|
2011-05-25 04:26:07 +02:00
|
|
|
}
|
|
|
|
}
|
2011-05-23 07:07:52 +02:00
|
|
|
|
2012-09-22 20:15:29 +02:00
|
|
|
m_table.node_seen(id, m.addr, 0xffff);
|
2011-01-19 06:57:44 +01:00
|
|
|
}
|
2016-08-15 22:17:13 +02:00
|
|
|
else if (query == "get")
|
2011-01-19 06:57:44 +01:00
|
|
|
{
|
2018-02-28 15:07:56 +01:00
|
|
|
static key_desc_t const msg_desc[] = {
|
2015-03-12 06:20:12 +01:00
|
|
|
{"seq", bdecode_node::int_t, 0, key_desc_t::optional},
|
|
|
|
{"target", bdecode_node::string_t, 20, 0},
|
2015-11-14 06:08:57 +01:00
|
|
|
{"want", bdecode_node::list_t, 0, key_desc_t::optional},
|
2011-01-19 06:57:44 +01:00
|
|
|
};
|
|
|
|
|
2011-05-23 07:07:52 +02:00
|
|
|
// k is not used for now
|
|
|
|
|
2011-01-19 06:57:44 +01:00
|
|
|
// attempt to parse the message
|
2015-11-14 06:08:57 +01:00
|
|
|
bdecode_node msg_keys[3];
|
2016-09-22 08:04:05 +02:00
|
|
|
if (!verify_message(arg_ent, msg_desc, msg_keys, error_string))
|
2011-01-19 06:57:44 +01:00
|
|
|
{
|
2015-01-18 02:06:36 +01:00
|
|
|
m_counters.inc_stats_counter(counters::dht_invalid_get);
|
2011-01-19 06:57:44 +01:00
|
|
|
incoming_error(e, error_string);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
m_counters.inc_stats_counter(counters::dht_get_in);
|
2017-06-30 11:51:52 +02:00
|
|
|
sha1_hash const target(msg_keys[1].string_ptr());
|
2011-01-19 06:57:44 +01:00
|
|
|
|
2016-05-17 15:24:06 +02:00
|
|
|
// std::fprintf(stderr, "%s GET target: %s\n"
|
2011-05-25 04:26:07 +02:00
|
|
|
// , msg_keys[1] ? "mutable":"immutable"
|
2016-07-29 08:36:15 +02:00
|
|
|
// , aux::to_hex(target).c_str());
|
2011-05-25 04:26:07 +02:00
|
|
|
|
2017-06-30 11:51:52 +02:00
|
|
|
reply["token"] = generate_token(m.addr, target);
|
2015-08-18 13:55:50 +02:00
|
|
|
|
2011-01-19 06:57:44 +01:00
|
|
|
// always return nodes as well as peers
|
2015-11-14 06:08:57 +01:00
|
|
|
write_nodes_entries(target, msg_keys[2], reply);
|
2011-01-19 06:57:44 +01:00
|
|
|
|
2014-12-25 12:24:02 +01:00
|
|
|
// if the get has a sequence number it must be for a mutable item
|
|
|
|
// so don't bother searching the immutable table
|
|
|
|
if (!msg_keys[0])
|
2011-05-25 04:26:07 +02:00
|
|
|
{
|
2016-06-04 01:44:16 +02:00
|
|
|
if (!m_storage.get_immutable_item(target, reply)) // ok, check for a mutable one
|
2015-09-08 22:12:54 +02:00
|
|
|
{
|
2016-07-24 00:57:04 +02:00
|
|
|
m_storage.get_mutable_item(target, sequence_number(0)
|
|
|
|
, true, reply);
|
2015-09-08 22:12:54 +02:00
|
|
|
}
|
2012-11-16 23:25:39 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-06-04 01:44:16 +02:00
|
|
|
m_storage.get_mutable_item(target
|
2016-07-24 00:57:04 +02:00
|
|
|
, sequence_number(msg_keys[0].int_value()), false
|
2015-09-08 22:12:54 +02:00
|
|
|
, reply);
|
2009-09-27 05:38:41 +02:00
|
|
|
}
|
|
|
|
}
|
2017-06-12 11:54:11 +02:00
|
|
|
else if (query == "sample_infohashes")
|
|
|
|
{
|
2017-06-30 20:03:34 +02:00
|
|
|
static key_desc_t const msg_desc[] = {
|
2017-06-12 11:54:11 +02:00
|
|
|
{"target", bdecode_node::string_t, 20, 0},
|
|
|
|
{"want", bdecode_node::list_t, 0, key_desc_t::optional},
|
|
|
|
};
|
|
|
|
|
|
|
|
bdecode_node msg_keys[2];
|
|
|
|
if (!verify_message(arg_ent, msg_desc, msg_keys, error_string))
|
|
|
|
{
|
|
|
|
m_counters.inc_stats_counter(counters::dht_invalid_sample_infohashes);
|
|
|
|
incoming_error(e, error_string);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_counters.inc_stats_counter(counters::dht_sample_infohashes_in);
|
|
|
|
sha1_hash const target(msg_keys[0].string_ptr());
|
|
|
|
|
|
|
|
// TODO: keep the returned value to pass as a limit
|
|
|
|
// to write_nodes_entries when implemented
|
|
|
|
m_storage.get_infohashes_sample(reply);
|
|
|
|
|
|
|
|
write_nodes_entries(target, msg_keys[1], reply);
|
|
|
|
}
|
2009-09-20 02:23:36 +02:00
|
|
|
else
|
|
|
|
{
|
2009-09-27 02:40:05 +02:00
|
|
|
// if we don't recognize the message but there's a
|
|
|
|
// 'target' or 'info_hash' in the arguments, treat it
|
|
|
|
// as find_node to be future compatible
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node target_ent = arg_ent.dict_find_string("target");
|
|
|
|
if (!target_ent || target_ent.string_length() != 20)
|
2009-09-27 02:40:05 +02:00
|
|
|
{
|
2015-03-12 06:20:12 +01:00
|
|
|
target_ent = arg_ent.dict_find_string("info_hash");
|
|
|
|
if (!target_ent || target_ent.string_length() != 20)
|
2009-09-27 02:40:05 +02:00
|
|
|
{
|
|
|
|
incoming_error(e, "unknown message");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-30 20:03:34 +02:00
|
|
|
sha1_hash const target(target_ent.string_ptr());
|
2009-09-27 02:40:05 +02:00
|
|
|
// always return nodes as well as peers
|
2015-11-14 06:08:57 +01:00
|
|
|
write_nodes_entries(target, arg_ent.dict_find_list("want"), reply);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-27 00:04:42 +02:00
|
|
|
// TODO: limit number of entries in the result
|
2015-11-14 06:08:57 +01:00
|
|
|
void node::write_nodes_entries(sha1_hash const& info_hash
|
|
|
|
, bdecode_node const& want, entry& r)
|
|
|
|
{
|
|
|
|
// if no wants entry was specified, include a nodes
|
|
|
|
// entry based on the protocol the request came in with
|
|
|
|
if (want.type() != bdecode_node::list_t)
|
|
|
|
{
|
2016-09-19 02:08:15 +02:00
|
|
|
std::vector<node_entry> n;
|
2015-11-14 06:08:57 +01:00
|
|
|
m_table.find_node(info_hash, n, 0);
|
2016-09-19 02:08:15 +02:00
|
|
|
r[protocol_nodes_key()] = write_nodes_entry(n);
|
2009-09-20 02:23:36 +02:00
|
|
|
return;
|
|
|
|
}
|
2015-11-14 06:08:57 +01:00
|
|
|
|
|
|
|
// if there is a wants entry then we may need to reach into
|
|
|
|
// another node's routing table to get nodes of the requested type
|
|
|
|
// we use a map maintained by the owning dht_tracker to find the
|
|
|
|
// node associated with each string in the want list, which may
|
|
|
|
// include this node
|
|
|
|
for (int i = 0; i < want.list_size(); ++i)
|
|
|
|
{
|
|
|
|
bdecode_node wanted = want.list_at(i);
|
|
|
|
if (wanted.type() != bdecode_node::string_t)
|
|
|
|
continue;
|
2017-04-21 06:45:43 +02:00
|
|
|
node* wanted_node = m_get_foreign_node(info_hash, wanted.string_value().to_string());
|
|
|
|
if (!wanted_node) continue;
|
2016-09-19 02:08:15 +02:00
|
|
|
std::vector<node_entry> n;
|
2017-04-21 06:45:43 +02:00
|
|
|
wanted_node->m_table.find_node(info_hash, n, 0);
|
|
|
|
r[wanted_node->protocol_nodes_key()] = write_nodes_entry(n);
|
2016-02-12 04:56:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-26 15:16:39 +01:00
|
|
|
node::protocol_descriptor const& node::map_protocol_to_descriptor(udp const protocol)
|
2016-02-12 04:56:52 +01:00
|
|
|
{
|
2018-02-28 15:07:56 +01:00
|
|
|
static std::array<protocol_descriptor, 2> const descriptors =
|
2016-11-27 14:46:53 +01:00
|
|
|
{{
|
|
|
|
{udp::v4(), "n4", "nodes"},
|
|
|
|
{udp::v6(), "n6", "nodes6"}
|
|
|
|
}};
|
2016-02-12 04:56:52 +01:00
|
|
|
|
2018-11-26 15:16:39 +01:00
|
|
|
auto const iter = std::find_if(descriptors.begin(), descriptors.end()
|
|
|
|
, [&protocol](protocol_descriptor const& d) { return d.protocol == protocol; });
|
|
|
|
|
|
|
|
if (iter == descriptors.end())
|
2016-02-12 04:56:52 +01:00
|
|
|
{
|
2018-11-26 15:16:39 +01:00
|
|
|
TORRENT_ASSERT_FAIL();
|
|
|
|
aux::throw_ex<std::out_of_range>("unknown protocol");
|
2015-11-14 06:08:57 +01:00
|
|
|
}
|
2016-02-12 04:56:52 +01:00
|
|
|
|
2018-11-26 15:16:39 +01:00
|
|
|
return *iter;
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
2015-09-17 17:11:46 +02:00
|
|
|
|
2006-08-01 17:27:08 +02:00
|
|
|
} } // namespace libtorrent::dht
|