2013-12-27 05:28:25 +01:00
|
|
|
/*
|
|
|
|
|
|
|
|
Copyright (c) 2013, Steven Siloti
|
|
|
|
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.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2014-01-19 20:45:50 +01:00
|
|
|
#include <libtorrent/config.hpp>
|
2013-12-27 05:28:25 +01:00
|
|
|
#include <libtorrent/hasher.hpp>
|
2015-05-10 06:54:02 +02:00
|
|
|
#include <libtorrent/bdecode.hpp>
|
2013-12-27 05:28:25 +01:00
|
|
|
#include <libtorrent/kademlia/get_item.hpp>
|
|
|
|
#include <libtorrent/kademlia/node.hpp>
|
2015-05-10 06:54:02 +02:00
|
|
|
#include <libtorrent/kademlia/dht_observer.hpp>
|
2013-12-27 05:28:25 +01:00
|
|
|
|
2014-01-19 20:45:50 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2013-12-27 05:28:25 +01:00
|
|
|
#include <libtorrent/bencode.hpp>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
namespace libtorrent { namespace dht
|
|
|
|
{
|
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
void get_item::got_data(bdecode_node const& v,
|
2013-12-27 05:28:25 +01:00
|
|
|
char const* pk,
|
|
|
|
boost::uint64_t seq,
|
|
|
|
char const* sig)
|
|
|
|
{
|
2014-02-24 01:31:13 +01:00
|
|
|
// we received data!
|
|
|
|
|
2015-05-06 03:11:54 +02:00
|
|
|
std::pair<char const*, int> salt(m_salt.c_str(), int(m_salt.size()));
|
2014-01-03 05:18:46 +01:00
|
|
|
|
2014-03-03 00:35:35 +01:00
|
|
|
sha1_hash incoming_target;
|
|
|
|
if (pk)
|
|
|
|
incoming_target = item_target_id(salt, pk);
|
|
|
|
else
|
2015-03-12 06:20:12 +01:00
|
|
|
incoming_target = item_target_id(v.data_section());
|
2014-03-03 00:35:35 +01:00
|
|
|
|
2014-01-03 05:18:46 +01:00
|
|
|
if (incoming_target != m_target) return;
|
|
|
|
|
2013-12-27 05:28:25 +01:00
|
|
|
if (pk && sig)
|
|
|
|
{
|
2014-02-24 01:31:13 +01:00
|
|
|
// this is mutable data. If it passes the signature
|
|
|
|
// check, remember it. Just keep the version with
|
|
|
|
// the highest sequence number.
|
2013-12-27 05:28:25 +01:00
|
|
|
if (m_data.empty() || m_data.seq() < seq)
|
|
|
|
{
|
2014-01-03 05:18:46 +01:00
|
|
|
if (!m_data.assign(v, salt, seq, pk, sig))
|
2013-12-27 05:28:25 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (m_data.empty())
|
|
|
|
{
|
2014-02-24 01:31:13 +01:00
|
|
|
// this is the first time we receive data,
|
|
|
|
// and it's immutable
|
|
|
|
|
2013-12-27 05:28:25 +01:00
|
|
|
m_data.assign(v);
|
|
|
|
bool put_requested = m_data_callback(m_data);
|
2014-02-24 01:31:13 +01:00
|
|
|
|
|
|
|
// if we intend to put, we need to keep going
|
|
|
|
// until we find the closest nodes, since those
|
|
|
|
// are the ones we're putting to
|
2013-12-27 05:28:25 +01:00
|
|
|
if (put_requested)
|
|
|
|
{
|
2014-01-19 20:45:50 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2013-12-27 05:28:25 +01:00
|
|
|
std::vector<char> buffer;
|
|
|
|
bencode(std::back_inserter(buffer), m_data.value());
|
|
|
|
TORRENT_ASSERT(m_target == hasher(&buffer[0], buffer.size()).final());
|
|
|
|
#endif
|
2014-02-24 01:31:13 +01:00
|
|
|
|
|
|
|
// this function is called when we're done, passing
|
|
|
|
// in all relevant nodes we received data from close
|
|
|
|
// to the target.
|
2013-12-27 05:28:25 +01:00
|
|
|
m_nodes_callback = boost::bind(&get_item::put, this, _1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// There can only be one true immutable item with a given id
|
|
|
|
// Now that we've got it and the user doesn't want to do a put
|
|
|
|
// there's no point in continuing to query other nodes
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
get_item::get_item(
|
2015-05-09 21:00:22 +02:00
|
|
|
node& dht_node
|
2013-12-27 05:28:25 +01:00
|
|
|
, node_id target
|
|
|
|
, data_callback const& dcallback)
|
2015-05-09 21:00:22 +02:00
|
|
|
: find_data(dht_node, target, nodes_callback())
|
2013-12-27 05:28:25 +01:00
|
|
|
, m_data_callback(dcallback)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-03-03 00:35:35 +01:00
|
|
|
get_item::get_item(
|
2015-05-09 21:00:22 +02:00
|
|
|
node& dht_node
|
2014-03-03 00:35:35 +01:00
|
|
|
, char const* pk
|
|
|
|
, std::string const& salt
|
|
|
|
, data_callback const& dcallback)
|
2015-05-09 21:00:22 +02:00
|
|
|
: find_data(dht_node, item_target_id(
|
2014-03-03 00:35:35 +01:00
|
|
|
std::make_pair(salt.c_str(), int(salt.size())), pk)
|
|
|
|
, nodes_callback())
|
|
|
|
, m_data_callback(dcallback)
|
|
|
|
, m_data(pk, salt)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2013-12-27 05:28:25 +01:00
|
|
|
char const* get_item::name() const { return "get"; }
|
|
|
|
|
|
|
|
observer_ptr get_item::new_observer(void* ptr
|
|
|
|
, udp::endpoint const& ep, node_id const& id)
|
|
|
|
{
|
|
|
|
observer_ptr o(new (ptr) get_item_observer(this, ep, id));
|
2014-01-19 20:45:50 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2013-12-27 05:28:25 +01:00
|
|
|
o->m_in_constructor = false;
|
|
|
|
#endif
|
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool get_item::invoke(observer_ptr o)
|
|
|
|
{
|
|
|
|
if (m_done)
|
|
|
|
{
|
|
|
|
m_invoke_count = -1;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry e;
|
|
|
|
e["y"] = "q";
|
|
|
|
entry& a = e["a"];
|
|
|
|
|
|
|
|
e["q"] = "get";
|
|
|
|
a["target"] = m_target.to_string();
|
|
|
|
|
|
|
|
return m_node.m_rpc.invoke(e, o->target_ep(), o);
|
|
|
|
}
|
|
|
|
|
|
|
|
void get_item::done()
|
|
|
|
{
|
|
|
|
if (m_data.is_mutable() || m_data.empty())
|
|
|
|
{
|
2014-02-24 01:31:13 +01:00
|
|
|
// for mutable data, we only call the callback at the end,
|
|
|
|
// when we've heard from everyone, to be sure we got the
|
|
|
|
// latest version of the data (i.e. highest sequence number)
|
2013-12-27 05:28:25 +01:00
|
|
|
bool put_requested = m_data_callback(m_data);
|
|
|
|
if (put_requested)
|
|
|
|
{
|
2014-01-19 20:45:50 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2013-12-27 05:28:25 +01:00
|
|
|
if (m_data.is_mutable())
|
|
|
|
{
|
2014-03-03 00:35:35 +01:00
|
|
|
TORRENT_ASSERT(m_target
|
|
|
|
== item_target_id(std::pair<char const*, int>(m_data.salt().c_str()
|
|
|
|
, m_data.salt().size())
|
|
|
|
, m_data.pk().data()));
|
2013-12-27 05:28:25 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::vector<char> buffer;
|
|
|
|
bencode(std::back_inserter(buffer), m_data.value());
|
|
|
|
TORRENT_ASSERT(m_target == hasher(&buffer[0], buffer.size()).final());
|
|
|
|
}
|
|
|
|
#endif
|
2014-02-24 01:31:13 +01:00
|
|
|
|
|
|
|
// this function is called when we're done, passing
|
|
|
|
// in all relevant nodes we received data from close
|
|
|
|
// to the target.
|
2013-12-27 05:28:25 +01:00
|
|
|
m_nodes_callback = boost::bind(&get_item::put, this, _1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
find_data::done();
|
|
|
|
}
|
|
|
|
|
2014-02-24 01:31:13 +01:00
|
|
|
// this function sends a put message to the nodes
|
|
|
|
// closest to the target. Those nodes are passed in
|
|
|
|
// as the v argument
|
2013-12-27 05:28:25 +01:00
|
|
|
void get_item::put(std::vector<std::pair<node_entry, std::string> > const& v)
|
|
|
|
{
|
2015-05-16 21:29:49 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2015-05-10 06:54:02 +02:00
|
|
|
// TODO: 3 it would be nice to not have to spend so much time rendering
|
|
|
|
// the bencoded dict if logging is disabled
|
2015-05-30 08:31:03 +02:00
|
|
|
get_node().observer()->log(dht_logger::traversal, "[%p] sending put "
|
|
|
|
"[ seq: %" PRId64 " nodes: %d ]"
|
|
|
|
, this, (m_data.is_mutable() ? m_data.seq() : -1)
|
2015-05-10 06:54:02 +02:00
|
|
|
, int(v.size()));
|
2013-12-27 05:28:25 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// create a dummy traversal_algorithm
|
|
|
|
boost::intrusive_ptr<traversal_algorithm> algo(
|
|
|
|
new traversal_algorithm(m_node, (node_id::min)()));
|
|
|
|
|
|
|
|
// store on the first k nodes
|
|
|
|
for (std::vector<std::pair<node_entry, std::string> >::const_iterator i = v.begin()
|
|
|
|
, end(v.end()); i != end; ++i)
|
|
|
|
{
|
2015-05-16 21:29:49 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2015-05-10 06:54:02 +02:00
|
|
|
get_node().observer()->log(dht_logger::traversal, "[%p] put-distance: %d"
|
|
|
|
, this, 160 - distance_exp(m_target, i->first.id));
|
2013-12-27 05:28:25 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
void* ptr = m_node.m_rpc.allocate_observer();
|
|
|
|
if (ptr == 0) return;
|
2014-11-03 07:15:51 +01:00
|
|
|
|
|
|
|
// TODO: 3 we don't support CAS errors here! we need a custom observer
|
2013-12-27 05:28:25 +01:00
|
|
|
observer_ptr o(new (ptr) announce_observer(algo, i->first.ep(), i->first.id));
|
2014-01-19 20:45:50 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2013-12-27 05:28:25 +01:00
|
|
|
o->m_in_constructor = false;
|
|
|
|
#endif
|
|
|
|
entry e;
|
|
|
|
e["y"] = "q";
|
|
|
|
e["q"] = "put";
|
|
|
|
entry& a = e["a"];
|
|
|
|
a["v"] = m_data.value();
|
|
|
|
a["token"] = i->second;
|
|
|
|
if (m_data.is_mutable())
|
|
|
|
{
|
2014-02-24 01:31:13 +01:00
|
|
|
a["k"] = std::string(m_data.pk().data(), item_pk_len);
|
2013-12-27 05:28:25 +01:00
|
|
|
a["seq"] = m_data.seq();
|
2014-02-24 01:31:13 +01:00
|
|
|
a["sig"] = std::string(m_data.sig().data(), item_sig_len);
|
2014-08-16 09:46:06 +02:00
|
|
|
if (!m_data.salt().empty())
|
|
|
|
{
|
|
|
|
a["salt"] = m_data.salt();
|
|
|
|
}
|
2013-12-27 05:28:25 +01:00
|
|
|
}
|
|
|
|
m_node.m_rpc.invoke(e, i->first.ep(), o);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void get_item_observer::reply(msg const& m)
|
|
|
|
{
|
|
|
|
char const* pk = NULL;
|
|
|
|
char const* sig = NULL;
|
|
|
|
boost::uint64_t seq = 0;
|
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node r = m.message.dict_find_dict("r");
|
2013-12-27 05:28:25 +01:00
|
|
|
if (!r)
|
|
|
|
{
|
2015-05-16 21:29:49 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2015-05-22 04:42:26 +02:00
|
|
|
get_observer()->log(dht_logger::traversal, "[%p] missing response dict"
|
|
|
|
, algorithm());
|
2013-12-27 05:28:25 +01:00
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node k = r.dict_find_string("k");
|
|
|
|
if (k && k.string_length() == item_pk_len)
|
|
|
|
pk = k.string_ptr();
|
2013-12-27 05:28:25 +01:00
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node s = r.dict_find_string("sig");
|
|
|
|
if (s && s.string_length() == item_sig_len)
|
|
|
|
sig = s.string_ptr();
|
2013-12-27 05:28:25 +01:00
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node q = r.dict_find_int("seq");
|
2013-12-27 05:28:25 +01:00
|
|
|
if (q)
|
2015-03-12 06:20:12 +01:00
|
|
|
seq = q.int_value();
|
2013-12-27 05:28:25 +01:00
|
|
|
else if (pk && sig)
|
|
|
|
return;
|
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node v = r.dict_find("v");
|
2013-12-27 05:28:25 +01:00
|
|
|
if (v)
|
|
|
|
{
|
2015-05-22 04:42:26 +02:00
|
|
|
static_cast<get_item*>(algorithm())->got_data(v, pk, seq, sig);
|
2013-12-27 05:28:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
find_data_observer::reply(m);
|
|
|
|
}
|
|
|
|
|
|
|
|
} } // namespace libtorrent::dht
|