2006-08-01 17:27:08 +02:00
|
|
|
/*
|
|
|
|
|
2014-02-23 20:12:25 +01:00
|
|
|
Copyright (c) 2006-2014, 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.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <utility>
|
|
|
|
#include <boost/bind.hpp>
|
2009-11-23 09:38:50 +01:00
|
|
|
#include <boost/function/function1.hpp>
|
2006-08-01 17:27:08 +02:00
|
|
|
|
|
|
|
#include "libtorrent/io.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"
|
2008-09-20 19:42:25 +02:00
|
|
|
#include "libtorrent/alert_types.hpp"
|
2011-01-19 06:57:44 +01:00
|
|
|
#include "libtorrent/alert.hpp"
|
2009-09-20 02:23:36 +02:00
|
|
|
#include "libtorrent/socket.hpp"
|
2011-02-28 01:35:58 +01:00
|
|
|
#include "libtorrent/random.hpp"
|
2008-09-20 19:42:25 +02:00
|
|
|
#include "libtorrent/aux_/session_impl.hpp"
|
2006-08-01 17:27:08 +02:00
|
|
|
#include "libtorrent/kademlia/node_id.hpp"
|
|
|
|
#include "libtorrent/kademlia/rpc_manager.hpp"
|
|
|
|
#include "libtorrent/kademlia/routing_table.hpp"
|
|
|
|
#include "libtorrent/kademlia/node.hpp"
|
2013-12-27 05:28:25 +01:00
|
|
|
#include "libtorrent/kademlia/dht_observer.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"
|
2014-07-06 21:18:00 +02:00
|
|
|
#include "libtorrent/performance_counters.hpp" // for counters
|
2006-08-01 17:27:08 +02:00
|
|
|
|
2013-10-14 09:43:18 +02:00
|
|
|
#ifdef TORRENT_USE_VALGRIND
|
|
|
|
#include <valgrind/memcheck.h>
|
|
|
|
#endif
|
|
|
|
|
2006-08-01 17:27:08 +02:00
|
|
|
namespace libtorrent { namespace dht
|
|
|
|
{
|
|
|
|
|
2013-09-03 02:45:48 +02:00
|
|
|
void incoming_error(entry& e, char const* msg, int error_code = 203);
|
2009-09-25 18:32:02 +02:00
|
|
|
|
2009-09-20 02:23:36 +02:00
|
|
|
using detail::write_endpoint;
|
|
|
|
|
2013-01-21 02:40:59 +01:00
|
|
|
// TODO: 2 make this configurable in dht_settings
|
2006-08-01 17:27:08 +02:00
|
|
|
enum { announce_interval = 30 };
|
|
|
|
|
|
|
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
|
|
|
TORRENT_DEFINE_LOG(node)
|
2013-06-18 18:01:37 +02:00
|
|
|
|
|
|
|
extern int g_failed_announces;
|
|
|
|
extern int g_announces;
|
|
|
|
|
2006-08-01 17:27:08 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// remove peers that have timed out
|
|
|
|
void purge_peers(std::set<peer_entry>& peers)
|
|
|
|
{
|
|
|
|
for (std::set<peer_entry>::iterator i = peers.begin()
|
|
|
|
, end(peers.end()); i != end;)
|
|
|
|
{
|
|
|
|
// the peer has timed out
|
2007-04-05 00:27:36 +02:00
|
|
|
if (i->added + minutes(int(announce_interval * 1.5f)) < time_now())
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
|
|
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
2008-11-10 03:08:42 +01:00
|
|
|
TORRENT_LOG(node) << "peer timed out at: " << i->addr;
|
2006-08-01 17:27:08 +02:00
|
|
|
#endif
|
|
|
|
peers.erase(i++);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void nop() {}
|
|
|
|
|
2012-04-30 07:39:35 +02:00
|
|
|
node_impl::node_impl(alert_dispatcher* alert_disp
|
|
|
|
, udp_socket_interface* sock
|
2011-01-19 06:57:44 +01:00
|
|
|
, dht_settings const& settings, node_id nid, address const& external_address
|
2014-07-06 21:18:00 +02:00
|
|
|
, dht_observer* observer
|
|
|
|
, struct counters& cnt)
|
2006-08-01 17:27:08 +02:00
|
|
|
: m_settings(settings)
|
2011-01-19 06:57:44 +01:00
|
|
|
, m_id(nid == (node_id::min)() || !verify_id(nid, external_address) ? generate_id(external_address) : nid)
|
2006-08-01 17:27:08 +02:00
|
|
|
, m_table(m_id, 8, settings)
|
2013-10-14 01:04:40 +02:00
|
|
|
, m_rpc(m_id, m_table, sock)
|
|
|
|
, m_observer(observer)
|
2007-04-05 00:27:36 +02:00
|
|
|
, m_last_tracker_tick(time_now())
|
2014-11-01 23:47:56 +01:00
|
|
|
, m_last_self_refresh(min_time())
|
2012-04-30 07:39:35 +02:00
|
|
|
, m_post_alert(alert_disp)
|
|
|
|
, m_sock(sock)
|
2014-07-06 21:18:00 +02:00
|
|
|
, m_counters(cnt)
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2011-04-08 06:47:26 +02:00
|
|
|
m_secret[0] = random();
|
2014-08-16 22:26:00 +02:00
|
|
|
m_secret[1] = random();
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
|
|
|
|
2014-09-22 21:49:32 +02:00
|
|
|
void node_impl::post_alert(alert* a)
|
|
|
|
{
|
|
|
|
if (!m_post_alert)
|
|
|
|
delete a;
|
|
|
|
else
|
|
|
|
m_post_alert->post_alert(a);
|
|
|
|
}
|
|
|
|
|
2009-09-20 02:23:36 +02:00
|
|
|
bool node_impl::verify_token(std::string const& token, char const* info_hash
|
|
|
|
, udp::endpoint const& addr)
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2007-05-14 19:49:36 +02:00
|
|
|
if (token.length() != 4)
|
|
|
|
{
|
|
|
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
|
|
|
TORRENT_LOG(node) << "token of incorrect length: " << token.length();
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
2006-08-01 17:27:08 +02:00
|
|
|
|
|
|
|
hasher h1;
|
2008-10-21 10:45:42 +02:00
|
|
|
error_code ec;
|
2009-09-20 02:23:36 +02:00
|
|
|
std::string address = addr.address().to_string(ec);
|
2008-10-21 10:45:42 +02:00
|
|
|
if (ec) return false;
|
2006-08-01 17:27:08 +02:00
|
|
|
h1.update(&address[0], address.length());
|
|
|
|
h1.update((char*)&m_secret[0], sizeof(m_secret[0]));
|
2009-09-20 02:23:36 +02:00
|
|
|
h1.update((char*)info_hash, sha1_hash::size);
|
2006-08-01 17:27:08 +02:00
|
|
|
|
|
|
|
sha1_hash h = h1.final();
|
2013-06-23 10:07:39 +02:00
|
|
|
if (std::equal(token.begin(), token.end(), (char*)&h[0]))
|
2006-08-01 17:27:08 +02:00
|
|
|
return true;
|
2007-05-14 19:49:36 +02:00
|
|
|
|
2006-08-01 17:27:08 +02:00
|
|
|
hasher h2;
|
|
|
|
h2.update(&address[0], address.length());
|
|
|
|
h2.update((char*)&m_secret[1], sizeof(m_secret[1]));
|
2009-09-20 02:23:36 +02:00
|
|
|
h2.update((char*)info_hash, sha1_hash::size);
|
2006-08-01 17:27:08 +02:00
|
|
|
h = h2.final();
|
2013-06-23 10:07:39 +02:00
|
|
|
if (std::equal(token.begin(), token.end(), (char*)&h[0]))
|
2006-08-01 17:27:08 +02:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-09-20 02:23:36 +02:00
|
|
|
std::string node_impl::generate_token(udp::endpoint const& addr, char const* info_hash)
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
|
|
|
std::string token;
|
|
|
|
token.resize(4);
|
|
|
|
hasher h;
|
2008-10-21 10:45:42 +02:00
|
|
|
error_code ec;
|
2009-09-20 02:23:36 +02:00
|
|
|
std::string address = addr.address().to_string(ec);
|
2008-10-21 10:45:42 +02:00
|
|
|
TORRENT_ASSERT(!ec);
|
2006-08-01 17:27:08 +02:00
|
|
|
h.update(&address[0], address.length());
|
|
|
|
h.update((char*)&m_secret[0], sizeof(m_secret[0]));
|
2009-09-20 02:23:36 +02:00
|
|
|
h.update(info_hash, sha1_hash::size);
|
2006-08-01 17:27:08 +02:00
|
|
|
|
|
|
|
sha1_hash hash = h.final();
|
2013-06-23 10:07:39 +02:00
|
|
|
std::copy(hash.begin(), hash.begin() + 4, (char*)&token[0]);
|
|
|
|
TORRENT_ASSERT(std::equal(token.begin(), token.end(), (char*)&hash[0]));
|
2008-11-11 18:51:02 +01:00
|
|
|
return token;
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void node_impl::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-01 23:47:56 +01:00
|
|
|
boost::intrusive_ptr<dht::bootstrap> r(new dht::bootstrap(*this, m_id, f));
|
|
|
|
m_last_self_refresh = time_now();
|
2009-09-20 02:23:36 +02:00
|
|
|
|
2010-02-14 08:46:57 +01:00
|
|
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
|
|
|
int count = 0;
|
|
|
|
#endif
|
|
|
|
|
2008-03-24 03:19:47 +01:00
|
|
|
for (std::vector<udp::endpoint>::const_iterator i = nodes.begin()
|
|
|
|
, end(nodes.end()); i != end; ++i)
|
2009-09-20 02:23:36 +02:00
|
|
|
{
|
2010-02-14 08:46:57 +01:00
|
|
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
|
|
|
++count;
|
|
|
|
#endif
|
2010-11-05 20:06:50 +01:00
|
|
|
r->add_entry(node_id(0), *i, observer::flag_initial);
|
2009-09-20 02:23:36 +02:00
|
|
|
}
|
|
|
|
|
2010-02-14 08:46:57 +01:00
|
|
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
|
|
|
TORRENT_LOG(node) << "bootstrapping with " << count << " nodes";
|
|
|
|
#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
|
|
|
|
2006-08-01 17:27:08 +02:00
|
|
|
int node_impl::bucket_size(int bucket)
|
|
|
|
{
|
|
|
|
return m_table.bucket_size(bucket);
|
|
|
|
}
|
|
|
|
|
|
|
|
void node_impl::new_write_key()
|
|
|
|
{
|
|
|
|
m_secret[1] = m_secret[0];
|
2012-05-03 05:16:31 +02:00
|
|
|
m_secret[0] = random();
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
|
|
|
|
2008-05-08 02:22:17 +02:00
|
|
|
void node_impl::unreachable(udp::endpoint const& ep)
|
|
|
|
{
|
|
|
|
m_rpc.unreachable(ep);
|
|
|
|
}
|
|
|
|
|
2006-08-01 17:27:08 +02:00
|
|
|
void node_impl::incoming(msg const& m)
|
|
|
|
{
|
2009-09-20 02:23:36 +02:00
|
|
|
// is this a reply?
|
|
|
|
lazy_entry const* y_ent = m.message.dict_find_string("y");
|
|
|
|
if (!y_ent || y_ent->string_length() == 0)
|
|
|
|
{
|
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");
|
|
|
|
// m_sock->send_packet(e, m.addr, 0);
|
2009-09-20 02:23:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
char y = *(y_ent->string_ptr());
|
|
|
|
|
2013-10-14 01:04:40 +02:00
|
|
|
lazy_entry const* ext_ip = m.message.dict_find_string("ip");
|
2013-12-20 05:47:41 +01:00
|
|
|
|
|
|
|
// backwards compatibility
|
|
|
|
if (ext_ip == NULL)
|
|
|
|
{
|
|
|
|
lazy_entry const* r = m.message.dict_find_dict("r");
|
|
|
|
if (r)
|
|
|
|
ext_ip = r->dict_find_string("ip");
|
|
|
|
}
|
|
|
|
|
2013-10-14 01:04:40 +02:00
|
|
|
#if TORRENT_USE_IPV6
|
2013-10-14 03:03:43 +02:00
|
|
|
if (ext_ip && ext_ip->string_length() >= 16)
|
2013-10-14 01:04:40 +02:00
|
|
|
{
|
|
|
|
// this node claims we use the wrong node-ID!
|
|
|
|
address_v6::bytes_type b;
|
|
|
|
memcpy(&b[0], ext_ip->string_ptr(), 16);
|
|
|
|
if (m_observer)
|
|
|
|
m_observer->set_external_address(address_v6(b)
|
|
|
|
, m.addr.address());
|
2013-10-14 03:03:43 +02:00
|
|
|
} else
|
2013-10-14 01:04:40 +02:00
|
|
|
#endif
|
2013-10-14 03:03:43 +02:00
|
|
|
if (ext_ip && ext_ip->string_length() >= 4)
|
|
|
|
{
|
|
|
|
address_v4::bytes_type b;
|
|
|
|
memcpy(&b[0], ext_ip->string_ptr(), 4);
|
|
|
|
if (m_observer)
|
|
|
|
m_observer->set_external_address(address_v4(b)
|
|
|
|
, m.addr.address());
|
|
|
|
}
|
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;
|
2014-11-01 23:47:56 +01:00
|
|
|
m_rpc.incoming(m, &id, m_settings);
|
2009-09-20 02:23:36 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'q':
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m.message.dict_find_string_value("y") == "q");
|
|
|
|
entry e;
|
|
|
|
incoming_request(m, e);
|
2012-04-30 07:39:35 +02:00
|
|
|
m_sock->send_packet(e, m.addr, 0);
|
2009-09-20 02:23:36 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'e':
|
|
|
|
{
|
|
|
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
|
|
|
lazy_entry const* err = m.message.dict_find_list("e");
|
|
|
|
if (err && err->list_size() >= 2)
|
|
|
|
{
|
|
|
|
TORRENT_LOG(node) << "INCOMING ERROR: " << err->list_string_value_at(1);
|
|
|
|
}
|
|
|
|
#endif
|
2014-01-03 09:02:53 +01:00
|
|
|
node_id id;
|
|
|
|
m_rpc.incoming(m, &id, m_settings);
|
2009-09-20 02:23:36 +02:00
|
|
|
break;
|
|
|
|
}
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
2008-12-23 21:04:12 +01:00
|
|
|
void announce_fun(std::vector<std::pair<node_entry, std::string> > const& v
|
2014-01-20 07:35:06 +01:00
|
|
|
, node_impl& node, int listen_port, sha1_hash const& ih, int flags)
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2008-03-24 03:19:47 +01:00
|
|
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
2008-12-23 21:04:12 +01:00
|
|
|
TORRENT_LOG(node) << "sending announce_peer [ ih: " << ih
|
2008-03-24 03:19:47 +01:00
|
|
|
<< " p: " << listen_port
|
|
|
|
<< " nodes: " << v.size() << " ]" ;
|
|
|
|
#endif
|
2009-10-07 22:51:02 +02:00
|
|
|
|
|
|
|
// create a dummy traversal_algorithm
|
|
|
|
boost::intrusive_ptr<traversal_algorithm> algo(
|
2009-10-23 08:41:09 +02:00
|
|
|
new traversal_algorithm(node, (node_id::min)()));
|
2009-10-07 22:51:02 +02:00
|
|
|
|
2008-12-23 21:04:12 +01:00
|
|
|
// store on the first k nodes
|
|
|
|
for (std::vector<std::pair<node_entry, std::string> >::const_iterator i = v.begin()
|
2006-08-01 17:27:08 +02:00
|
|
|
, end(v.end()); i != end; ++i)
|
|
|
|
{
|
2008-05-08 03:17:14 +02:00
|
|
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
2013-01-20 08:54:54 +01:00
|
|
|
TORRENT_LOG(node) << " announce-distance: " << (160 - distance_exp(ih, i->first.id));
|
2008-05-08 03:17:14 +02:00
|
|
|
#endif
|
2008-12-23 21:04:12 +01:00
|
|
|
|
2010-11-06 08:12:57 +01:00
|
|
|
void* ptr = node.m_rpc.allocate_observer();
|
2009-05-14 00:18:41 +02:00
|
|
|
if (ptr == 0) return;
|
2010-11-05 20:06:50 +01:00
|
|
|
observer_ptr o(new (ptr) announce_observer(algo, i->first.ep(), i->first.id));
|
2012-01-16 23:48:43 +01:00
|
|
|
#if defined TORRENT_DEBUG || TORRENT_RELEASE_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"];
|
2009-10-07 22:51:02 +02:00
|
|
|
a["info_hash"] = ih.to_string();
|
2009-09-20 02:23:36 +02:00
|
|
|
a["port"] = listen_port;
|
|
|
|
a["token"] = i->second;
|
2014-01-20 07:35:06 +01:00
|
|
|
a["seed"] = (flags & node_impl::flag_seed) ? 1 : 0;
|
|
|
|
if (flags & node_impl::flag_implied_port) a["implied_port"] = 1;
|
2014-07-06 21:18:00 +02:00
|
|
|
node.stats_counters().inc_stats_counter(counters::dht_announce_peer_in);
|
2009-10-07 22:51:02 +02:00
|
|
|
node.m_rpc.invoke(e, i->first.ep(), o);
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-27 19:20:18 +02:00
|
|
|
void node_impl::add_router_node(udp::endpoint router)
|
|
|
|
{
|
2008-03-24 03:19:47 +01:00
|
|
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
|
|
|
TORRENT_LOG(node) << "adding router node: " << router;
|
|
|
|
#endif
|
2006-09-27 19:20:18 +02:00
|
|
|
m_table.add_router_node(router);
|
|
|
|
}
|
|
|
|
|
2006-08-01 17:27:08 +02:00
|
|
|
void node_impl::add_node(udp::endpoint node)
|
|
|
|
{
|
|
|
|
// 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
|
|
|
}
|
|
|
|
|
2014-01-20 07:35:06 +01:00
|
|
|
void node_impl::announce(sha1_hash const& info_hash, int listen_port, int flags
|
2008-12-23 21:04:12 +01:00
|
|
|
, boost::function<void(std::vector<tcp::endpoint> const&)> f)
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2008-03-24 03:19:47 +01:00
|
|
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
|
|
|
TORRENT_LOG(node) << "announcing [ ih: " << info_hash << " p: " << listen_port << " ]" ;
|
|
|
|
#endif
|
2008-12-23 21:04:12 +01:00
|
|
|
// search for nodes with ids close to id or with peers
|
|
|
|
// for info-hash id. then send announce_peer to them.
|
2013-09-09 09:08:02 +02:00
|
|
|
|
2013-12-27 05:28:25 +01:00
|
|
|
boost::intrusive_ptr<get_peers> ta;
|
2013-09-09 09:08:02 +02:00
|
|
|
if (m_settings.privacy_lookups)
|
|
|
|
{
|
|
|
|
ta.reset(new obfuscated_get_peers(*this, info_hash, f
|
|
|
|
, boost::bind(&announce_fun, _1, boost::ref(*this)
|
2014-01-20 07:35:06 +01:00
|
|
|
, listen_port, info_hash, flags), flags & node_impl::flag_seed));
|
2013-09-09 09:08:02 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-12-27 05:28:25 +01:00
|
|
|
ta.reset(new get_peers(*this, info_hash, f
|
2013-09-09 09:08:02 +02:00
|
|
|
, boost::bind(&announce_fun, _1, boost::ref(*this)
|
2014-01-20 07:35:06 +01:00
|
|
|
, listen_port, info_hash, flags), flags & node_impl::flag_seed));
|
2013-09-09 09:08:02 +02:00
|
|
|
}
|
|
|
|
|
2009-09-20 02:23:36 +02:00
|
|
|
ta->start();
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
|
|
|
|
2014-03-03 00:35:35 +01:00
|
|
|
void node_impl::get_item(sha1_hash const& target
|
|
|
|
, boost::function<bool(item&)> f)
|
2013-12-27 05:28:25 +01:00
|
|
|
{
|
|
|
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
2014-03-03 00:35:35 +01:00
|
|
|
TORRENT_LOG(node) << "starting get for [ hash: " << target << " ]" ;
|
2013-12-27 05:28:25 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
boost::intrusive_ptr<dht::get_item> ta;
|
|
|
|
ta.reset(new dht::get_item(*this, target, f));
|
|
|
|
ta->start();
|
|
|
|
}
|
|
|
|
|
2014-03-03 00:35:35 +01:00
|
|
|
void node_impl::get_item(char const* pk, std::string const& salt
|
|
|
|
, boost::function<bool(item&)> f)
|
|
|
|
{
|
|
|
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
|
|
|
TORRENT_LOG(node) << "starting get for [ key: "
|
|
|
|
<< to_hex(std::string(pk, 32)) << " ]" ;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
boost::intrusive_ptr<dht::get_item> ta;
|
|
|
|
ta.reset(new dht::get_item(*this, pk, salt, f));
|
|
|
|
ta->start();
|
|
|
|
}
|
|
|
|
|
2014-11-01 23:47:56 +01:00
|
|
|
struct ping_observer : observer
|
|
|
|
{
|
|
|
|
ping_observer(
|
|
|
|
boost::intrusive_ptr<traversal_algorithm> const& algorithm
|
|
|
|
, udp::endpoint const& ep, node_id const& id)
|
|
|
|
: observer(algorithm, ep, id)
|
|
|
|
{}
|
|
|
|
|
|
|
|
// parses out "nodes"
|
|
|
|
virtual void reply(msg const& m)
|
|
|
|
{
|
|
|
|
flags |= flag_done;
|
|
|
|
|
|
|
|
lazy_entry const* r = m.message.dict_find_dict("r");
|
|
|
|
if (!r)
|
|
|
|
{
|
|
|
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
|
|
|
TORRENT_LOG(traversal) << "[" << m_algorithm.get()
|
|
|
|
<< "] missing response dict";
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// look for nodes
|
|
|
|
lazy_entry const* n = r->dict_find_string("nodes");
|
|
|
|
if (n)
|
|
|
|
{
|
|
|
|
char const* nodes = n->string_ptr();
|
|
|
|
char const* end = nodes + n->string_length();
|
|
|
|
|
|
|
|
while (end - nodes >= 26)
|
|
|
|
{
|
|
|
|
node_id id;
|
|
|
|
std::copy(nodes, nodes + 20, id.begin());
|
|
|
|
nodes += 20;
|
|
|
|
m_algorithm.get()->node().m_table.heard_about(id
|
|
|
|
, detail::read_v4_endpoint<udp::endpoint>(nodes));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-03-03 00:35:35 +01:00
|
|
|
|
2010-01-03 12:08:39 +01:00
|
|
|
void node_impl::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.
|
|
|
|
ptime now = time_now();
|
|
|
|
if (m_last_self_refresh + minutes(10) < now)
|
|
|
|
{
|
|
|
|
boost::intrusive_ptr<dht::refresh> r(new dht::refresh(*this, m_id
|
|
|
|
, boost::bind(&nop)));
|
|
|
|
r->start();
|
|
|
|
m_last_self_refresh = now;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-02 10:41:29 +01:00
|
|
|
node_entry const* ne = m_table.next_refresh();
|
2014-11-01 23:47:56 +01:00
|
|
|
if (ne == NULL) return;
|
|
|
|
|
2014-11-02 10:41:29 +01:00
|
|
|
int bucket = 159 - distance_exp(m_id, ne->id);
|
|
|
|
send_single_refresh(ne->ep(), bucket, ne->id);
|
|
|
|
}
|
|
|
|
|
|
|
|
void node_impl::send_single_refresh(udp::endpoint const& ep, int bucket
|
|
|
|
, node_id const& id)
|
|
|
|
{
|
2014-11-01 23:47:56 +01:00
|
|
|
void* ptr = m_rpc.allocate_observer();
|
|
|
|
if (ptr == 0) return;
|
|
|
|
|
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 target = generate_random_id();
|
|
|
|
node_id mask = generate_prefix_mask(bucket + 1);
|
|
|
|
|
|
|
|
// target = (target & ~mask) | (root & mask)
|
|
|
|
node_id root = m_id;
|
|
|
|
root &= mask;
|
|
|
|
target &= ~mask;
|
|
|
|
target |= root;
|
|
|
|
|
2014-11-01 23:47:56 +01:00
|
|
|
// create a dummy traversal_algorithm
|
|
|
|
// this is unfortunately necessary for the observer
|
|
|
|
// to free itself from the pool when it's being released
|
|
|
|
boost::intrusive_ptr<traversal_algorithm> algo(
|
|
|
|
new traversal_algorithm(*this, (node_id::min)()));
|
2014-11-02 10:41:29 +01:00
|
|
|
observer_ptr o(new (ptr) ping_observer(algo, ep, id));
|
2014-11-01 23:47:56 +01:00
|
|
|
#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS
|
|
|
|
o->m_in_constructor = false;
|
|
|
|
#endif
|
|
|
|
entry e;
|
|
|
|
e["y"] = "q";
|
|
|
|
entry& a = e["a"];
|
2014-11-02 10:41:29 +01:00
|
|
|
|
|
|
|
// use get_peers instead of find_node. We'll get nodes in the response
|
|
|
|
// either way.
|
|
|
|
e["q"] = "get_peers";
|
|
|
|
a["info_hash"] = target.to_string();
|
|
|
|
m_counters.inc_stats_counter(counters::dht_get_peers_out);
|
|
|
|
|
|
|
|
// e["q"] = "find_node";
|
|
|
|
// a["target"] = target.to_string();
|
|
|
|
m_rpc.invoke(e, ep, o);
|
2006-08-06 18:36:00 +02:00
|
|
|
}
|
|
|
|
|
2007-01-07 14:55:27 +01:00
|
|
|
time_duration node_impl::connection_timeout()
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
|
|
|
time_duration d = m_rpc.tick();
|
2008-10-21 19:10:11 +02:00
|
|
|
ptime now(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;
|
|
|
|
|
2011-05-23 07:07:52 +02:00
|
|
|
for (dht_immutable_table_t::iterator i = m_immutable_table.begin();
|
|
|
|
i != m_immutable_table.end();)
|
2011-01-19 06:57:44 +01:00
|
|
|
{
|
|
|
|
if (i->second.last_seen + minutes(60) > now)
|
|
|
|
{
|
|
|
|
++i;
|
|
|
|
continue;
|
|
|
|
}
|
2011-05-23 07:07:52 +02:00
|
|
|
free(i->second.value);
|
|
|
|
m_immutable_table.erase(i++);
|
2014-07-06 21:18:00 +02:00
|
|
|
m_counters.inc_stats_counter(counters::dht_immutable_data, -1);
|
2011-01-19 06:57:44 +01:00
|
|
|
}
|
|
|
|
|
2008-10-21 19:10:11 +02:00
|
|
|
// look through all peers and see if any have timed out
|
2009-09-27 05:38:41 +02:00
|
|
|
for (table_t::iterator i = m_map.begin(), end(m_map.end()); i != end;)
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2008-10-21 19:10:11 +02:00
|
|
|
torrent_entry& t = i->second;
|
|
|
|
node_id const& key = i->first;
|
|
|
|
++i;
|
|
|
|
purge_peers(t.peers);
|
|
|
|
|
|
|
|
// if there are no more peers, remove the entry altogether
|
|
|
|
if (t.peers.empty())
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2008-10-21 19:10:11 +02:00
|
|
|
table_t::iterator i = m_map.find(key);
|
2014-07-06 21:18:00 +02:00
|
|
|
if (i != m_map.end())
|
|
|
|
{
|
|
|
|
m_map.erase(i);
|
|
|
|
m_counters.inc_stats_counter(counters::dht_torrents, -1);
|
|
|
|
}
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
|
|
|
}
|
2007-01-07 14:55:27 +01:00
|
|
|
|
2006-08-01 17:27:08 +02:00
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
2008-09-20 19:42:25 +02:00
|
|
|
void node_impl::status(session_status& s)
|
|
|
|
{
|
2008-10-09 05:33:14 +02:00
|
|
|
mutex_t::scoped_lock l(m_mutex);
|
2009-01-23 11:36:07 +01:00
|
|
|
|
|
|
|
m_table.status(s);
|
|
|
|
s.dht_torrents = int(m_map.size());
|
2008-09-20 19:42:25 +02:00
|
|
|
s.active_requests.clear();
|
2010-11-06 08:12:57 +01:00
|
|
|
s.dht_total_allocations = m_rpc.num_allocated_observers();
|
2008-09-20 19:42:25 +02:00
|
|
|
for (std::set<traversal_algorithm*>::iterator i = m_running_requests.begin()
|
|
|
|
, end(m_running_requests.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
s.active_requests.push_back(dht_lookup());
|
|
|
|
dht_lookup& l = s.active_requests.back();
|
|
|
|
(*i)->status(l);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-12 06:18:34 +02:00
|
|
|
void node_impl::lookup_peers(sha1_hash const& info_hash, entry& reply
|
2011-05-23 02:45:36 +02:00
|
|
|
, bool noseed, bool scrape) const
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2012-04-30 07:39:35 +02:00
|
|
|
if (m_post_alert)
|
|
|
|
{
|
|
|
|
alert* a = new dht_get_peers_alert(info_hash);
|
|
|
|
if (!m_post_alert->post_alert(a)) delete a;
|
|
|
|
}
|
2008-09-20 19:42:25 +02:00
|
|
|
|
2010-12-06 07:39:16 +01:00
|
|
|
table_t::const_iterator i = m_map.lower_bound(info_hash);
|
2011-05-23 02:45:36 +02:00
|
|
|
if (i == m_map.end()) return;
|
2014-10-12 06:18:34 +02:00
|
|
|
if (i->first != info_hash) return;
|
2006-08-01 17:27:08 +02:00
|
|
|
|
|
|
|
torrent_entry const& v = i->second;
|
|
|
|
|
2010-11-27 04:09:28 +01:00
|
|
|
if (!v.name.empty()) reply["n"] = v.name;
|
|
|
|
|
2011-05-23 02:45:36 +02:00
|
|
|
if (scrape)
|
2009-09-27 05:38:41 +02:00
|
|
|
{
|
2011-05-23 02:45:36 +02:00
|
|
|
bloom_filter<256> downloaders;
|
|
|
|
bloom_filter<256> seeds;
|
|
|
|
|
|
|
|
for (std::set<peer_entry>::const_iterator i = v.peers.begin()
|
|
|
|
, end(v.peers.end()); i != end; ++i)
|
2009-09-27 05:38:41 +02:00
|
|
|
{
|
2011-05-23 02:45:36 +02:00
|
|
|
sha1_hash iphash;
|
|
|
|
hash_address(i->addr.address(), iphash);
|
|
|
|
if (i->seed) seeds.set(iphash);
|
|
|
|
else downloaders.set(iphash);
|
2009-09-27 05:38:41 +02:00
|
|
|
}
|
2011-05-23 02:45:36 +02:00
|
|
|
|
|
|
|
reply["BFpe"] = downloaders.to_string();
|
|
|
|
reply["BFse"] = seeds.to_string();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int num = (std::min)((int)v.peers.size(), m_settings.max_peers_reply);
|
|
|
|
std::set<peer_entry>::const_iterator iter = v.peers.begin();
|
|
|
|
entry::list_type& pe = reply["values"].list();
|
|
|
|
std::string endpoint;
|
|
|
|
|
|
|
|
for (int t = 0, m = 0; m < num && iter != v.peers.end(); ++iter, ++t)
|
2009-09-27 05:38:41 +02:00
|
|
|
{
|
2011-05-23 02:45:36 +02:00
|
|
|
if ((random() / float(UINT_MAX + 1.f)) * (num - t) >= num - m) continue;
|
|
|
|
if (noseed && iter->seed) continue;
|
2009-09-27 05:38:41 +02:00
|
|
|
endpoint.resize(18);
|
|
|
|
std::string::iterator out = endpoint.begin();
|
|
|
|
write_endpoint(iter->addr, out);
|
|
|
|
endpoint.resize(out - endpoint.begin());
|
|
|
|
pe.push_back(entry(endpoint));
|
|
|
|
|
|
|
|
++m;
|
|
|
|
}
|
|
|
|
}
|
2011-05-23 02:45:36 +02:00
|
|
|
return;
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
|
|
|
|
2013-12-27 05:28:25 +01:00
|
|
|
namespace detail
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2013-12-27 05:28:25 +01:00
|
|
|
void TORRENT_EXTRA_EXPORT write_nodes_entry(entry& r, nodes_t const& nodes)
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2009-09-20 02:23:36 +02:00
|
|
|
entry& n = r["nodes"];
|
|
|
|
std::back_insert_iterator<std::string> out(n.string());
|
|
|
|
for (nodes_t::const_iterator i = nodes.begin()
|
|
|
|
, end(nodes.end()); i != end; ++i)
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2014-10-01 18:21:29 +02:00
|
|
|
if (!i->addr().is_v4()) continue;
|
2009-09-20 02:23:36 +02:00
|
|
|
std::copy(i->id.begin(), i->id.end(), out);
|
2013-01-28 05:00:23 +01:00
|
|
|
write_endpoint(udp::endpoint(i->addr(), i->port()), out);
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
2009-09-20 02:23:36 +02:00
|
|
|
}
|
2009-09-27 19:41:51 +02:00
|
|
|
}
|
2014-10-01 18:21:29 +02:00
|
|
|
|
2013-12-27 05:28:25 +01:00
|
|
|
using detail::write_nodes_entry;
|
2009-09-27 07:27:43 +02:00
|
|
|
|
2009-09-27 19:41:51 +02:00
|
|
|
// verifies that a message has all the required
|
|
|
|
// entries and returns them in ret
|
|
|
|
bool verify_message(lazy_entry const* msg, key_desc_t const desc[], lazy_entry const* ret[]
|
|
|
|
, int size , char* error, int error_size)
|
|
|
|
{
|
|
|
|
// clear the return buffer
|
|
|
|
memset(ret, 0, sizeof(ret[0]) * size);
|
2009-09-27 07:27:43 +02:00
|
|
|
|
2011-01-19 06:57:44 +01:00
|
|
|
// when parsing child nodes, this is the stack
|
|
|
|
// of lazy_entry pointers to return to
|
|
|
|
lazy_entry const* stack[5];
|
|
|
|
int stack_ptr = -1;
|
|
|
|
|
2009-09-27 19:41:51 +02:00
|
|
|
if (msg->type() != lazy_entry::dict_t)
|
2009-09-27 07:27:43 +02:00
|
|
|
{
|
2009-09-27 19:41:51 +02:00
|
|
|
snprintf(error, error_size, "not a dictionary");
|
|
|
|
return false;
|
|
|
|
}
|
2011-01-19 06:57:44 +01:00
|
|
|
++stack_ptr;
|
|
|
|
stack[stack_ptr] = msg;
|
2009-09-27 19:41:51 +02:00
|
|
|
for (int i = 0; i < size; ++i)
|
|
|
|
{
|
|
|
|
key_desc_t const& k = desc[i];
|
2011-01-19 06:57:44 +01:00
|
|
|
|
|
|
|
// fprintf(stderr, "looking for %s in %s\n", k.name, print_entry(*msg).c_str());
|
|
|
|
|
2009-09-27 19:41:51 +02:00
|
|
|
ret[i] = msg->dict_find(k.name);
|
2011-05-23 07:07:52 +02:00
|
|
|
// none_t means any type
|
|
|
|
if (ret[i] && ret[i]->type() != k.type && k.type != lazy_entry::none_t) ret[i] = 0;
|
2009-09-27 19:41:51 +02:00
|
|
|
if (ret[i] == 0 && (k.flags & key_desc_t::optional) == 0)
|
2009-09-27 07:27:43 +02:00
|
|
|
{
|
2011-05-23 07:07:52 +02:00
|
|
|
// the key was not found, and it's not an optional key
|
2009-09-27 19:41:51 +02:00
|
|
|
snprintf(error, error_size, "missing '%s' key", k.name);
|
2009-09-27 07:27:43 +02:00
|
|
|
return false;
|
|
|
|
}
|
2009-09-27 19:41:51 +02:00
|
|
|
|
|
|
|
if (k.size > 0
|
|
|
|
&& ret[i]
|
2011-01-19 06:57:44 +01:00
|
|
|
&& k.type == lazy_entry::string_t)
|
|
|
|
{
|
|
|
|
bool invalid = false;
|
|
|
|
if (k.flags & key_desc_t::size_divisible)
|
|
|
|
invalid = (ret[i]->string_length() % k.size) != 0;
|
|
|
|
else
|
|
|
|
invalid = ret[i]->string_length() != k.size;
|
|
|
|
|
|
|
|
if (invalid)
|
|
|
|
{
|
|
|
|
// the string was not of the required size
|
|
|
|
ret[i] = 0;
|
|
|
|
if ((k.flags & key_desc_t::optional) == 0)
|
|
|
|
{
|
|
|
|
snprintf(error, error_size, "invalid value for '%s'", k.name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (k.flags & key_desc_t::parse_children)
|
2009-09-27 07:27:43 +02:00
|
|
|
{
|
2011-01-19 06:57:44 +01:00
|
|
|
TORRENT_ASSERT(k.type == lazy_entry::dict_t);
|
|
|
|
|
|
|
|
if (ret[i])
|
|
|
|
{
|
|
|
|
++stack_ptr;
|
2011-02-21 06:24:41 +01:00
|
|
|
TORRENT_ASSERT(stack_ptr < int(sizeof(stack)/sizeof(stack[0])));
|
2011-01-19 06:57:44 +01:00
|
|
|
msg = ret[i];
|
|
|
|
stack[stack_ptr] = msg;
|
|
|
|
}
|
|
|
|
else
|
2009-09-27 07:27:43 +02:00
|
|
|
{
|
2011-01-19 06:57:44 +01:00
|
|
|
// skip all children
|
|
|
|
while (i < size && (desc[i].flags & key_desc_t::last_child) == 0) ++i;
|
|
|
|
// if this assert is hit, desc is incorrect
|
|
|
|
TORRENT_ASSERT(i < size);
|
2009-09-27 07:27:43 +02:00
|
|
|
}
|
|
|
|
}
|
2011-01-19 06:57:44 +01:00
|
|
|
else if (k.flags & key_desc_t::last_child)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(stack_ptr > 0);
|
2013-06-14 18:41:47 +02:00
|
|
|
// this can happen if the specification passed
|
|
|
|
// in is unbalanced. i.e. contain more last_child
|
|
|
|
// nodes than parse_children
|
|
|
|
if (stack_ptr == 0) return false;
|
2011-01-19 06:57:44 +01:00
|
|
|
--stack_ptr;
|
|
|
|
msg = stack[stack_ptr];
|
|
|
|
}
|
2009-09-27 07:27:43 +02:00
|
|
|
}
|
2009-09-27 19:41:51 +02:00
|
|
|
return true;
|
2009-09-20 02:23:36 +02:00
|
|
|
}
|
2006-08-01 17:27:08 +02:00
|
|
|
|
2013-09-03 02:45:48 +02:00
|
|
|
void incoming_error(entry& e, char const* msg, int error_code)
|
2009-09-20 02:23:36 +02:00
|
|
|
{
|
|
|
|
e["y"] = "e";
|
|
|
|
entry::list_type& l = e["e"].list();
|
2013-09-03 02:45:48 +02:00
|
|
|
l.push_back(entry(error_code));
|
2009-09-20 02:23:36 +02:00
|
|
|
l.push_back(entry(msg));
|
|
|
|
}
|
|
|
|
|
2013-09-14 12:08:31 +02:00
|
|
|
// return true of the first argument is a better canidate for removal, i.e.
|
|
|
|
// less important to keep
|
|
|
|
struct immutable_item_comparator
|
|
|
|
{
|
|
|
|
immutable_item_comparator(node_id const& our_id) : m_our_id(our_id) {}
|
|
|
|
|
|
|
|
bool operator() (std::pair<node_id, dht_immutable_item> const& lhs
|
|
|
|
, std::pair<node_id, dht_immutable_item> const& rhs) const
|
|
|
|
{
|
|
|
|
int l_distance = distance_exp(lhs.first, m_our_id);
|
|
|
|
int r_distance = distance_exp(rhs.first, m_our_id);
|
|
|
|
|
|
|
|
// this is a score taking the popularity (number of announcers) and the
|
|
|
|
// fit, in terms of distance from ideal storing node, into account.
|
|
|
|
// each additional 5 announcers is worth one extra bit in the distance.
|
|
|
|
// that is, an item with 10 announcers is allowed to be twice as far
|
|
|
|
// from another item with 5 announcers, from our node ID. Twice as far
|
|
|
|
// because it gets one more bit.
|
|
|
|
return lhs.second.num_announcers / 5 - l_distance < rhs.second.num_announcers / 5 - r_distance;
|
|
|
|
}
|
|
|
|
|
|
|
|
node_id const& m_our_id;
|
|
|
|
};
|
|
|
|
|
2009-09-20 02:23:36 +02:00
|
|
|
// build response
|
|
|
|
void node_impl::incoming_request(msg const& m, entry& e)
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!m_sock->has_quota())
|
|
|
|
return;
|
|
|
|
|
2009-09-20 02:23:36 +02:00
|
|
|
e = entry(entry::dictionary_t);
|
|
|
|
e["y"] = "r";
|
|
|
|
e["t"] = m.message.dict_find_string_value("t");
|
|
|
|
|
2009-09-27 07:27:43 +02:00
|
|
|
key_desc_t top_desc[] = {
|
|
|
|
{"q", lazy_entry::string_t, 0, 0},
|
2014-10-12 06:18:34 +02:00
|
|
|
{"ro", lazy_entry::int_t, 0, key_desc_t::optional},
|
2011-12-01 05:01:36 +01:00
|
|
|
{"a", lazy_entry::dict_t, 0, key_desc_t::parse_children},
|
|
|
|
{"id", lazy_entry::string_t, 20, key_desc_t::last_child},
|
2009-09-27 07:27:43 +02:00
|
|
|
};
|
|
|
|
|
2014-10-12 06:18:34 +02:00
|
|
|
lazy_entry const* top_level[4];
|
2009-09-27 07:27:43 +02:00
|
|
|
char error_string[200];
|
2014-10-12 06:18:34 +02:00
|
|
|
if (!verify_message(&m.message, top_desc, top_level, 4, error_string, sizeof(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
|
|
|
|
|
|
|
char const* query = top_level[0]->string_cstr();
|
|
|
|
|
2014-10-12 06:18:34 +02:00
|
|
|
lazy_entry const* arg_ent = top_level[2];
|
|
|
|
bool read_only = top_level[1] && top_level[1]->int_value() != 0;
|
|
|
|
node_id 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();
|
|
|
|
|
2009-09-20 02:23:36 +02:00
|
|
|
if (strcmp(query, "ping") == 0)
|
|
|
|
{
|
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
|
|
|
|
}
|
|
|
|
else if (strcmp(query, "get_peers") == 0)
|
|
|
|
{
|
2009-09-27 07:27:43 +02:00
|
|
|
key_desc_t msg_desc[] = {
|
|
|
|
{"info_hash", lazy_entry::string_t, 20, 0},
|
2011-05-23 02:45:36 +02:00
|
|
|
{"noseed", lazy_entry::int_t, 0, key_desc_t::optional},
|
|
|
|
{"scrape", lazy_entry::int_t, 0, key_desc_t::optional},
|
2009-09-27 07:27:43 +02:00
|
|
|
};
|
|
|
|
|
2014-10-12 06:18:34 +02:00
|
|
|
lazy_entry const* msg_keys[3];
|
|
|
|
if (!verify_message(arg_ent, msg_desc, msg_keys, 3, error_string, sizeof(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;
|
|
|
|
}
|
|
|
|
|
2009-09-27 07:27:43 +02:00
|
|
|
reply["token"] = generate_token(m.addr, msg_keys[0]->string_ptr());
|
2009-09-20 02:23:36 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
m_counters.inc_stats_counter(counters::dht_get_peers_in);
|
|
|
|
|
2009-09-27 07:27:43 +02:00
|
|
|
sha1_hash info_hash(msg_keys[0]->string_ptr());
|
2009-09-20 02:23:36 +02:00
|
|
|
nodes_t n;
|
|
|
|
// always return nodes as well as peers
|
|
|
|
m_table.find_node(info_hash, n, 0);
|
|
|
|
write_nodes_entry(reply, n);
|
|
|
|
|
2011-05-23 02:45:36 +02:00
|
|
|
bool noseed = false;
|
|
|
|
bool scrape = false;
|
2014-10-12 06:18:34 +02:00
|
|
|
if (msg_keys[1] && msg_keys[1]->int_value() != 0) noseed = true;
|
|
|
|
if (msg_keys[2] && msg_keys[2]->int_value() != 0) scrape = true;
|
|
|
|
lookup_peers(info_hash, reply, noseed, scrape);
|
2009-09-20 02:23:36 +02:00
|
|
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
2013-01-20 08:54:54 +01:00
|
|
|
if (reply.find_key("values"))
|
|
|
|
{
|
|
|
|
TORRENT_LOG(node) << " values: " << reply["values"].list().size();
|
|
|
|
}
|
2006-08-01 17:27:08 +02:00
|
|
|
#endif
|
2009-09-20 02:23:36 +02:00
|
|
|
}
|
|
|
|
else if (strcmp(query, "find_node") == 0)
|
|
|
|
{
|
2009-09-27 07:27:43 +02:00
|
|
|
key_desc_t msg_desc[] = {
|
|
|
|
{"target", lazy_entry::string_t, 20, 0},
|
|
|
|
};
|
|
|
|
|
|
|
|
lazy_entry const* msg_keys[1];
|
|
|
|
if (!verify_message(arg_ent, msg_desc, msg_keys, 1, error_string, sizeof(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;
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
m_counters.inc_stats_counter(counters::dht_find_node_in);
|
2009-09-27 07:27:43 +02:00
|
|
|
sha1_hash target(msg_keys[0]->string_ptr());
|
|
|
|
|
2014-10-12 06:18:34 +02:00
|
|
|
// TODO: 2 find_node should write directly to the response entry
|
2009-09-20 02:23:36 +02:00
|
|
|
nodes_t n;
|
|
|
|
m_table.find_node(target, n, 0);
|
|
|
|
write_nodes_entry(reply, n);
|
|
|
|
}
|
|
|
|
else if (strcmp(query, "announce_peer") == 0)
|
|
|
|
{
|
2009-09-27 07:27:43 +02:00
|
|
|
key_desc_t msg_desc[] = {
|
|
|
|
{"info_hash", lazy_entry::string_t, 20, 0},
|
|
|
|
{"port", lazy_entry::int_t, 0, 0},
|
|
|
|
{"token", lazy_entry::string_t, 0, 0},
|
2010-11-27 04:09:28 +01:00
|
|
|
{"n", lazy_entry::string_t, 0, key_desc_t::optional},
|
2011-05-23 02:45:36 +02:00
|
|
|
{"seed", lazy_entry::int_t, 0, key_desc_t::optional},
|
2013-03-25 08:26:39 +01:00
|
|
|
{"implied_port", lazy_entry::int_t, 0, key_desc_t::optional},
|
2009-09-27 07:27:43 +02:00
|
|
|
};
|
|
|
|
|
2013-03-25 08:26:39 +01:00
|
|
|
lazy_entry const* msg_keys[6];
|
|
|
|
if (!verify_message(arg_ent, msg_desc, msg_keys, 6, error_string, sizeof(error_string)))
|
2009-09-20 02:23:36 +02:00
|
|
|
{
|
2009-10-03 20:29:47 +02:00
|
|
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
2009-09-29 19:06:08 +02:00
|
|
|
++g_failed_announces;
|
2009-10-03 20:29:47 +02:00
|
|
|
#endif
|
2009-09-27 07:27:43 +02:00
|
|
|
incoming_error(e, error_string);
|
2009-09-20 02:23:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-02-21 06:24:41 +01:00
|
|
|
int 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?
|
|
|
|
if (msg_keys[5] && msg_keys[5]->int_value() != 0)
|
|
|
|
port = m.addr.port();
|
|
|
|
|
2009-09-20 02:23:36 +02:00
|
|
|
if (port < 0 || port >= 65536)
|
|
|
|
{
|
2009-10-03 20:29:47 +02:00
|
|
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
2009-09-29 19:06:08 +02:00
|
|
|
++g_failed_announces;
|
2009-10-03 20:29:47 +02:00
|
|
|
#endif
|
2011-01-19 06:57:44 +01:00
|
|
|
incoming_error(e, "invalid port");
|
2009-09-20 02:23:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-09-27 07:27:43 +02:00
|
|
|
sha1_hash info_hash(msg_keys[0]->string_ptr());
|
2009-09-20 02:23:36 +02:00
|
|
|
|
2012-04-30 07:39:35 +02:00
|
|
|
if (m_post_alert)
|
|
|
|
{
|
|
|
|
alert* a = new dht_announce_alert(m.addr.address(), port, info_hash);
|
|
|
|
if (!m_post_alert->post_alert(a)) delete a;
|
|
|
|
}
|
2009-09-20 02:23:36 +02:00
|
|
|
|
2009-09-27 07:27:43 +02:00
|
|
|
if (!verify_token(msg_keys[2]->string_value(), msg_keys[0]->string_ptr(), m.addr))
|
2009-09-20 02:23:36 +02:00
|
|
|
{
|
2009-10-03 20:29:47 +02:00
|
|
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
2009-09-29 19:06:08 +02:00
|
|
|
++g_failed_announces;
|
2009-10-03 20:29:47 +02:00
|
|
|
#endif
|
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
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
table_t::iterator ti = m_map.find(info_hash);
|
|
|
|
torrent_entry* v;
|
|
|
|
if (ti == m_map.end())
|
2010-11-27 04:09:28 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
// we don't have this torrent, add it
|
|
|
|
// do we need to remove another one first?
|
|
|
|
if (!m_map.empty() && int(m_map.size()) >= m_settings.max_torrents)
|
2010-11-27 04:09:28 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
// we need to remove some. Remove the ones with the
|
|
|
|
// fewest peers
|
|
|
|
int num_peers = m_map.begin()->second.peers.size();
|
|
|
|
table_t::iterator candidate = m_map.begin();
|
|
|
|
for (table_t::iterator i = m_map.begin()
|
|
|
|
, end(m_map.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
if (int(i->second.peers.size()) > num_peers) continue;
|
|
|
|
if (i->first == info_hash) continue;
|
|
|
|
num_peers = i->second.peers.size();
|
|
|
|
candidate = i;
|
|
|
|
}
|
|
|
|
m_map.erase(candidate);
|
|
|
|
m_counters.inc_stats_counter(counters::dht_torrents, -1);
|
2010-11-27 04:09:28 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
m_counters.inc_stats_counter(counters::dht_torrents);
|
|
|
|
v = &m_map[info_hash];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
v = &ti->second;
|
2010-11-27 04:09:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// the peer announces a torrent name, and we don't have a name
|
|
|
|
// for this torrent. Store it.
|
2014-07-06 21:18:00 +02:00
|
|
|
if (msg_keys[3] && v->name.empty())
|
2010-11-27 04:09:28 +01:00
|
|
|
{
|
|
|
|
std::string name = msg_keys[3]->string_value();
|
|
|
|
if (name.size() > 50) name.resize(50);
|
2014-07-06 21:18:00 +02:00
|
|
|
v->name = name;
|
2010-11-27 04:09:28 +01:00
|
|
|
}
|
|
|
|
|
2011-04-08 06:47:26 +02:00
|
|
|
peer_entry peer;
|
|
|
|
peer.addr = tcp::endpoint(m.addr.address(), port);
|
|
|
|
peer.added = time_now();
|
2011-05-23 02:45:36 +02:00
|
|
|
peer.seed = msg_keys[4] && msg_keys[4]->int_value();
|
2014-07-06 21:18:00 +02:00
|
|
|
std::set<peer_entry>::iterator i = v->peers.find(peer);
|
|
|
|
if (i != v->peers.end()) v->peers.erase(i++);
|
|
|
|
v->peers.insert(i, peer);
|
2009-10-03 20:29:47 +02:00
|
|
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
2009-09-29 19:06:08 +02:00
|
|
|
++g_announces;
|
2009-10-03 20:29:47 +02:00
|
|
|
#endif
|
2009-09-20 02:23:36 +02:00
|
|
|
}
|
2011-05-23 07:07:52 +02:00
|
|
|
else if (strcmp(query, "put") == 0)
|
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
|
2011-01-19 06:57:44 +01:00
|
|
|
const static key_desc_t msg_desc[] = {
|
|
|
|
{"token", lazy_entry::string_t, 0, 0},
|
2011-05-23 07:07:52 +02:00
|
|
|
{"v", lazy_entry::none_t, 0, 0},
|
2011-05-25 04:41:48 +02:00
|
|
|
{"seq", lazy_entry::int_t, 0, key_desc_t::optional},
|
2011-05-25 04:26:07 +02:00
|
|
|
// public key
|
2013-12-27 05:28:25 +01:00
|
|
|
{"k", lazy_entry::string_t, item_pk_len, key_desc_t::optional},
|
|
|
|
{"sig", lazy_entry::string_t, item_sig_len, key_desc_t::optional},
|
2014-08-27 07:57:37 +02:00
|
|
|
{"cas", lazy_entry::int_t, 0, key_desc_t::optional},
|
2014-01-03 05:18:46 +01:00
|
|
|
{"salt", lazy_entry::string_t, 0, key_desc_t::optional},
|
2011-01-19 06:57:44 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
// attempt to parse the message
|
2014-01-03 05:18:46 +01:00
|
|
|
lazy_entry const* msg_keys[7];
|
|
|
|
if (!verify_message(arg_ent, msg_desc, msg_keys, 7, error_string, sizeof(error_string)))
|
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?
|
2011-05-25 04:41:48 +02:00
|
|
|
bool 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)
|
|
|
|
char const* pk = NULL;
|
|
|
|
if (msg_keys[3]) pk = msg_keys[3]->string_ptr();
|
|
|
|
|
|
|
|
// signature (only set if it's a mutable put)
|
|
|
|
char const* sig = NULL;
|
|
|
|
if (msg_keys[4]) sig = msg_keys[4]->string_ptr();
|
|
|
|
|
2011-05-23 07:07:52 +02:00
|
|
|
// pointer and length to the whole entry
|
|
|
|
std::pair<char const*, int> buf = msg_keys[1]->data_section();
|
2013-09-03 02:45:48 +02:00
|
|
|
if (buf.second > 1000 || buf.second <= 0)
|
2011-01-19 06:57:44 +01:00
|
|
|
{
|
2013-09-03 02:45:48 +02:00
|
|
|
incoming_error(e, "message too big", 205);
|
2011-01-19 06:57:44 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-02-17 08:24:43 +01:00
|
|
|
std::pair<char const*, int> salt(static_cast<char const*>(NULL), 0);
|
2014-01-03 05:18:46 +01:00
|
|
|
if (msg_keys[6])
|
|
|
|
salt = std::pair<char const*, int>(
|
|
|
|
msg_keys[6]->string_ptr(), msg_keys[6]->string_length());
|
|
|
|
if (salt.second > 64)
|
|
|
|
{
|
|
|
|
incoming_error(e, "salt too big", 207);
|
|
|
|
return;
|
|
|
|
}
|
2011-05-25 04:26:07 +02:00
|
|
|
|
2014-03-03 00:35:35 +01:00
|
|
|
sha1_hash target;
|
|
|
|
if (pk)
|
|
|
|
target = item_target_id(salt, pk);
|
|
|
|
else
|
|
|
|
target = item_target_id(buf);
|
2014-01-03 05:18:46 +01:00
|
|
|
|
|
|
|
// fprintf(stderr, "%s PUT target: %s salt: %s key: %s\n"
|
2011-05-25 04:26:07 +02:00
|
|
|
// , mutable_put ? "mutable":"immutable"
|
2014-01-03 05:18:46 +01:00
|
|
|
// , to_hex(target.to_string()).c_str()
|
|
|
|
// , salt.second > 0 ? std::string(salt.first, salt.second).c_str() : ""
|
|
|
|
// , pk ? to_hex(std::string(pk, 32)).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
|
|
|
|
if (!verify_token(msg_keys[0]->string_value(), (char const*)&target[0], m.addr))
|
2011-01-19 06:57:44 +01:00
|
|
|
{
|
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
|
|
|
dht_immutable_item* f = 0;
|
|
|
|
|
|
|
|
if (!mutable_put)
|
2011-01-19 06:57:44 +01:00
|
|
|
{
|
2011-05-25 04:26:07 +02:00
|
|
|
dht_immutable_table_t::iterator i = m_immutable_table.find(target);
|
|
|
|
if (i == m_immutable_table.end())
|
2011-01-19 06:57:44 +01:00
|
|
|
{
|
2011-05-25 04:26:07 +02:00
|
|
|
// make sure we don't add too many items
|
|
|
|
if (int(m_immutable_table.size()) >= m_settings.max_dht_items)
|
|
|
|
{
|
|
|
|
// delete the least important one (i.e. the one
|
2013-09-14 12:08:31 +02:00
|
|
|
// the fewest peers are announcing, and farthest
|
|
|
|
// from our node ID)
|
2011-05-25 04:26:07 +02:00
|
|
|
dht_immutable_table_t::iterator j = std::min_element(m_immutable_table.begin()
|
|
|
|
, m_immutable_table.end()
|
2013-09-14 12:08:31 +02:00
|
|
|
, immutable_item_comparator(m_id));
|
|
|
|
|
2011-05-25 04:26:07 +02:00
|
|
|
TORRENT_ASSERT(j != m_immutable_table.end());
|
|
|
|
free(j->second.value);
|
|
|
|
m_immutable_table.erase(j);
|
2014-07-06 21:18:00 +02:00
|
|
|
m_counters.inc_stats_counter(counters::dht_immutable_data, -1);
|
2011-05-25 04:26:07 +02:00
|
|
|
}
|
|
|
|
dht_immutable_item to_add;
|
|
|
|
to_add.value = (char*)malloc(buf.second);
|
|
|
|
to_add.size = buf.second;
|
|
|
|
memcpy(to_add.value, buf.first, buf.second);
|
2011-05-23 07:07:52 +02:00
|
|
|
|
2011-05-25 04:26:07 +02:00
|
|
|
boost::tie(i, boost::tuples::ignore) = m_immutable_table.insert(
|
|
|
|
std::make_pair(target, to_add));
|
2014-07-06 21:18:00 +02:00
|
|
|
m_counters.inc_stats_counter(counters::dht_immutable_data);
|
2011-05-25 04:26:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// fprintf(stderr, "added immutable item (%d)\n", int(m_immutable_table.size()));
|
|
|
|
|
|
|
|
f = &i->second;
|
2011-01-19 06:57:44 +01:00
|
|
|
}
|
2011-05-25 04:26:07 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// mutable put, we must verify the signature
|
|
|
|
|
2013-10-14 09:43:18 +02:00
|
|
|
#ifdef TORRENT_USE_VALGRIND
|
2013-12-27 05:28:25 +01:00
|
|
|
VALGRIND_CHECK_MEM_IS_DEFINED(msg_keys[4]->string_ptr(), item_sig_len);
|
2014-01-03 05:18:46 +01:00
|
|
|
VALGRIND_CHECK_MEM_IS_DEFINED(pk, item_pk_len);
|
2013-10-14 09:43:18 +02:00
|
|
|
#endif
|
2013-08-18 18:01:20 +02:00
|
|
|
// msg_keys[4] is the signature, msg_keys[3] is the public key
|
2014-01-03 05:18:46 +01:00
|
|
|
if (!verify_mutable_item(buf, salt
|
|
|
|
, msg_keys[2]->int_value(), pk, sig))
|
2011-05-25 04:26:07 +02:00
|
|
|
{
|
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
|
|
|
|
2011-05-25 04:26:07 +02:00
|
|
|
dht_mutable_table_t::iterator i = m_mutable_table.find(target);
|
|
|
|
if (i == m_mutable_table.end())
|
|
|
|
{
|
2013-09-03 02:45:48 +02:00
|
|
|
// this is the case where we don't have an item in this slot
|
2011-05-25 04:26:07 +02:00
|
|
|
// make sure we don't add too many items
|
|
|
|
if (int(m_mutable_table.size()) >= m_settings.max_dht_items)
|
|
|
|
{
|
|
|
|
// delete the least important one (i.e. the one
|
|
|
|
// the fewest peers are announcing)
|
|
|
|
dht_mutable_table_t::iterator j = std::min_element(m_mutable_table.begin()
|
|
|
|
, m_mutable_table.end()
|
|
|
|
, boost::bind(&dht_immutable_item::num_announcers
|
|
|
|
, boost::bind(&dht_mutable_table_t::value_type::second, _1)));
|
|
|
|
TORRENT_ASSERT(j != m_mutable_table.end());
|
|
|
|
free(j->second.value);
|
2014-01-03 05:18:46 +01:00
|
|
|
free(j->second.salt);
|
2011-05-25 04:26:07 +02:00
|
|
|
m_mutable_table.erase(j);
|
2014-07-06 21:18:00 +02:00
|
|
|
m_counters.inc_stats_counter(counters::dht_mutable_data, -1);
|
2011-05-25 04:26:07 +02:00
|
|
|
}
|
|
|
|
dht_mutable_item to_add;
|
|
|
|
to_add.value = (char*)malloc(buf.second);
|
|
|
|
to_add.size = buf.second;
|
|
|
|
to_add.seq = msg_keys[2]->int_value();
|
2014-01-03 05:18:46 +01:00
|
|
|
to_add.salt = NULL;
|
|
|
|
to_add.salt_size = 0;
|
|
|
|
if (salt.second > 0)
|
|
|
|
{
|
|
|
|
to_add.salt = (char*)malloc(salt.second);
|
|
|
|
to_add.salt_size = salt.second;
|
|
|
|
memcpy(to_add.salt, salt.first, salt.second);
|
|
|
|
}
|
|
|
|
memcpy(to_add.sig, sig, sizeof(to_add.sig));
|
2011-05-25 04:26:07 +02:00
|
|
|
TORRENT_ASSERT(sizeof(to_add.sig) == msg_keys[4]->string_length());
|
|
|
|
memcpy(to_add.value, buf.first, buf.second);
|
2014-01-03 05:18:46 +01:00
|
|
|
memcpy(&to_add.key, pk, sizeof(to_add.key));
|
2011-05-25 04:26:07 +02:00
|
|
|
|
|
|
|
boost::tie(i, boost::tuples::ignore) = m_mutable_table.insert(
|
|
|
|
std::make_pair(target, to_add));
|
2014-07-06 21:18:00 +02:00
|
|
|
m_counters.inc_stats_counter(counters::dht_mutable_data);
|
2011-05-25 04:26:07 +02:00
|
|
|
|
|
|
|
// fprintf(stderr, "added mutable item (%d)\n", int(m_mutable_table.size()));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-09-03 02:45:48 +02:00
|
|
|
// this is the case where we already
|
2011-05-25 04:26:07 +02:00
|
|
|
dht_mutable_item* item = &i->second;
|
|
|
|
|
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
|
|
|
|
if (msg_keys[5] && item->seq != msg_keys[5]->int_value())
|
2013-09-03 02:45:48 +02:00
|
|
|
{
|
2014-08-27 07:57:37 +02:00
|
|
|
incoming_error(e, "CAS mismatch", 301);
|
|
|
|
return;
|
2013-09-03 02:45:48 +02:00
|
|
|
}
|
|
|
|
|
2014-05-10 05:23:05 +02:00
|
|
|
if (item->seq > boost::uint64_t(msg_keys[2]->int_value()))
|
2011-05-25 04:26:07 +02:00
|
|
|
{
|
2013-09-03 02:45:48 +02:00
|
|
|
incoming_error(e, "old sequence number", 302);
|
2011-05-25 04:26:07 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-05-10 05:23:05 +02:00
|
|
|
if (item->seq < boost::uint64_t(msg_keys[2]->int_value()))
|
2011-05-25 04:26:07 +02:00
|
|
|
{
|
|
|
|
if (item->size != buf.second)
|
|
|
|
{
|
|
|
|
free(item->value);
|
|
|
|
item->value = (char*)malloc(buf.second);
|
|
|
|
item->size = buf.second;
|
|
|
|
}
|
|
|
|
item->seq = msg_keys[2]->int_value();
|
|
|
|
memcpy(item->sig, msg_keys[4]->string_ptr(), sizeof(item->sig));
|
|
|
|
TORRENT_ASSERT(sizeof(item->sig) == msg_keys[4]->string_length());
|
|
|
|
memcpy(item->value, buf.first, buf.second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
f = &i->second;
|
|
|
|
}
|
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
|
|
|
|
2011-05-25 04:26:07 +02:00
|
|
|
f->last_seen = time_now();
|
2011-01-19 06:57:44 +01:00
|
|
|
|
|
|
|
// maybe increase num_announcers if we haven't seen this IP before
|
|
|
|
sha1_hash iphash;
|
|
|
|
hash_address(m.addr.address(), iphash);
|
2011-05-25 04:26:07 +02:00
|
|
|
if (!f->ips.find(iphash))
|
2011-01-19 06:57:44 +01:00
|
|
|
{
|
2011-05-25 04:26:07 +02:00
|
|
|
f->ips.set(iphash);
|
|
|
|
++f->num_announcers;
|
2011-01-19 06:57:44 +01:00
|
|
|
}
|
|
|
|
}
|
2011-05-23 07:07:52 +02:00
|
|
|
else if (strcmp(query, "get") == 0)
|
2011-01-19 06:57:44 +01:00
|
|
|
{
|
|
|
|
key_desc_t msg_desc[] = {
|
|
|
|
{"target", lazy_entry::string_t, 20, 0},
|
|
|
|
};
|
|
|
|
|
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
|
2012-11-16 23:25:39 +01:00
|
|
|
lazy_entry const* msg_keys[1];
|
|
|
|
if (!verify_message(arg_ent, msg_desc, msg_keys, 1, error_string, sizeof(error_string)))
|
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);
|
2011-01-19 06:57:44 +01:00
|
|
|
sha1_hash target(msg_keys[0]->string_ptr());
|
|
|
|
|
2011-05-25 04:26:07 +02:00
|
|
|
// fprintf(stderr, "%s GET target: %s\n"
|
|
|
|
// , msg_keys[1] ? "mutable":"immutable"
|
|
|
|
// , to_hex(target.to_string()).c_str());
|
|
|
|
|
2011-01-19 06:57:44 +01:00
|
|
|
reply["token"] = generate_token(m.addr, msg_keys[0]->string_ptr());
|
|
|
|
|
|
|
|
nodes_t n;
|
|
|
|
// always return nodes as well as peers
|
|
|
|
m_table.find_node(target, n, 0);
|
|
|
|
write_nodes_entry(reply, n);
|
|
|
|
|
2012-11-16 23:25:39 +01:00
|
|
|
dht_immutable_table_t::iterator i = m_immutable_table.find(target);
|
|
|
|
if (i != m_immutable_table.end())
|
2011-05-25 04:26:07 +02:00
|
|
|
{
|
2012-11-16 23:25:39 +01:00
|
|
|
dht_immutable_item const& f = i->second;
|
|
|
|
reply["v"] = bdecode(f.value, f.value + f.size);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dht_mutable_table_t::iterator i = m_mutable_table.find(target);
|
2011-05-25 04:26:07 +02:00
|
|
|
if (i != m_mutable_table.end())
|
|
|
|
{
|
|
|
|
dht_mutable_item const& f = i->second;
|
|
|
|
reply["v"] = bdecode(f.value, f.value + f.size);
|
|
|
|
reply["seq"] = f.seq;
|
2013-10-22 09:12:31 +02:00
|
|
|
reply["sig"] = std::string(f.sig, f.sig + sizeof(f.sig));
|
2012-11-16 23:25:39 +01:00
|
|
|
reply["k"] = std::string(f.key.bytes, f.key.bytes + sizeof(f.key.bytes));
|
2011-05-25 04:26:07 +02:00
|
|
|
}
|
2009-09-27 05:38:41 +02:00
|
|
|
}
|
|
|
|
}
|
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
|
|
|
|
lazy_entry const* target_ent = arg_ent->dict_find_string("target");
|
|
|
|
if (target_ent == 0 || target_ent->string_length() != 20)
|
|
|
|
{
|
|
|
|
target_ent = arg_ent->dict_find_string("info_hash");
|
|
|
|
if (target_ent == 0 || target_ent->string_length() != 20)
|
|
|
|
{
|
|
|
|
incoming_error(e, "unknown message");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sha1_hash target(target_ent->string_ptr());
|
|
|
|
nodes_t n;
|
|
|
|
// always return nodes as well as peers
|
|
|
|
m_table.find_node(target, n, 0);
|
|
|
|
write_nodes_entry(reply, n);
|
2009-09-20 02:23:36 +02:00
|
|
|
return;
|
|
|
|
}
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} } // namespace libtorrent::dht
|
2009-09-20 02:23:36 +02:00
|
|
|
|