DHT refactoring and support for storing arbitrary data with put

This commit is contained in:
Arvid Norberg 2013-12-27 04:28:25 +00:00
parent c9bfa1279e
commit d6b1aa4c36
22 changed files with 1756 additions and 394 deletions

View File

@ -1,3 +1,4 @@
* DHT refactoring and support for storing arbitrary data with put
* support building on android
* improved support for web seeds that don't support keep-alive
* improve DHT routing table to return better nodes (lower RTT and closer to target)

18
Jamfile
View File

@ -118,10 +118,21 @@ rule linking ( properties * )
if <test-coverage>on in $(properties)
&& ( <toolset>gcc in $(properties)
|| <toolset>darwin in $(properties) )
|| <toolset>darwin in $(properties)
|| <toolset>clang in $(properties) )
{
result += <cxxflags>-fprofile-arcs <cxxflags>-ftest-coverage
<linkflags>-lgcov <define>NDEBUG ;
<define>NDEBUG ;
if <toolset>gcc in $(properties)
|| <toolset>darwin in $(properties)
{
result += <linkflags>-lgcov ;
}
else
{
result += <linkflags>--coverage ;
}
}
# clock_gettime on linux requires librt
@ -579,6 +590,9 @@ KADEMLIA_SOURCES =
routing_table
traversal_algorithm
logging
get_peers
item
get_item
;
ED25519_SOURCES =

View File

@ -147,4 +147,8 @@ nobase_include_HEADERS = \
kademlia/refresh.hpp \
kademlia/routing_table.hpp \
kademlia/rpc_manager.hpp \
kademlia/traversal_algorithm.hpp
kademlia/traversal_algorithm.hpp \
kademlia/item.hpp \
kademlia/get_item.hpp \
kademlia/get_peers.hpp

View File

@ -137,6 +137,7 @@ namespace libtorrent
// hidden
bool operator==(entry const& e) const;
bool operator!=(entry const& e) const { return !(*this == e); }
// copies the structure of the right hand side into this
// entry.

View File

@ -57,22 +57,15 @@ class node_impl;
// -------- find data -----------
//TODO: 3 rename this class to get_peers, since that's what it does
// find_data is an unnecessarily generic name
struct find_data : traversal_algorithm
{
public:
typedef boost::function<void(std::vector<tcp::endpoint> const&)> data_callback;
typedef boost::function<void(std::vector<std::pair<node_entry, std::string> > const&, bool)> nodes_callback;
typedef boost::function<void(std::vector<std::pair<node_entry, std::string> > const&)> nodes_callback;
void got_peers(std::vector<tcp::endpoint> const& peers);
void got_write_token(node_id const& n, std::string const& write_token)
{ m_write_tokens[n] = write_token; }
find_data(node_impl& node, node_id target
, data_callback const& dcallback
, nodes_callback const& ncallback
, bool noseeds);
, nodes_callback const& ncallback);
virtual void start();
@ -83,40 +76,12 @@ public:
protected:
virtual void done();
observer_ptr new_observer(void* ptr, udp::endpoint const& ep
virtual observer_ptr new_observer(void* ptr, udp::endpoint const& ep
, node_id const& id);
virtual bool invoke(observer_ptr o);
data_callback m_data_callback;
nodes_callback m_nodes_callback;
std::map<node_id, std::string> m_write_tokens;
node_id const m_target;
bool m_done:1;
bool m_got_peers:1;
bool m_noseeds:1;
};
struct obfuscated_get_peers : find_data
{
typedef find_data::nodes_callback done_callback;
obfuscated_get_peers(node_impl& node, node_id target
, data_callback const& dcallback
, nodes_callback const& ncallback
, bool noseeds);
virtual char const* name() const;
protected:
observer_ptr new_observer(void* ptr, udp::endpoint const& ep
, node_id const& id);
virtual bool invoke(observer_ptr o);
virtual void done();
private:
// when set to false, we no longer obfuscate
// the target hash, and send regular get_peers
bool m_obfuscated;
bool m_done;
};
struct find_data_observer : traversal_observer
@ -126,17 +91,8 @@ struct find_data_observer : traversal_observer
, udp::endpoint const& ep, node_id const& id)
: traversal_observer(algorithm, ep, id)
{}
void reply(msg const&);
};
struct obfuscated_find_data_observer : traversal_observer
{
obfuscated_find_data_observer(
boost::intrusive_ptr<traversal_algorithm> const& algorithm
, udp::endpoint const& ep, node_id const& id)
: traversal_observer(algorithm, ep, id)
{}
void reply(msg const&);
virtual void reply(msg const&);
};
} } // namespace libtorrent::dht

View File

@ -0,0 +1,82 @@
/*
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.
*/
#ifndef LIBTORRENT_GET_ITEM_HPP
#define LIBTORRENT_GET_ITEM_HPP
#include <string>
#include <libtorrent/kademlia/find_data.hpp>
#include <libtorrent/kademlia/item.hpp>
namespace libtorrent { namespace dht
{
class get_item : public find_data
{
public:
typedef boost::function<bool(item&)> data_callback;
void got_data(lazy_entry const* v,
char const* pk,
boost::uint64_t seq,
char const* sig);
get_item(node_impl& node, node_id target, data_callback const& dcallback);
virtual char const* name() const;
protected:
virtual observer_ptr new_observer(void* ptr, udp::endpoint const& ep, node_id const& id);
virtual bool invoke(observer_ptr o);
virtual void done();
void put(std::vector<std::pair<node_entry, std::string> > const& v);
data_callback m_data_callback;
item m_data;
};
class get_item_observer : public find_data_observer
{
public:
get_item_observer(
boost::intrusive_ptr<traversal_algorithm> const& algorithm
, udp::endpoint const& ep, node_id const& id)
: find_data_observer(algorithm, ep, id)
{}
virtual void reply(msg const&);
};
} } // namespace libtorrent::dht
#endif // LIBTORRENT_GET_ITEM_HPP

View File

@ -0,0 +1,108 @@
/*
Copyright (c) 2006-2013, Arvid Norberg & Daniel Wallin
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.
*/
#ifndef LIBTORRENT_GET_PEERS_HPP
#define LIBTORRENT_GET_PEERS_HPP
#include <libtorrent/kademlia/find_data.hpp>
namespace libtorrent { namespace dht
{
struct get_peers : find_data
{
typedef boost::function<void(std::vector<tcp::endpoint> const&)> data_callback;
void got_peers(std::vector<tcp::endpoint> const& peers);
get_peers(node_impl& node, node_id target
, data_callback const& dcallback
, nodes_callback const& ncallback
, bool noseeds);
virtual char const* name() const;
protected:
virtual bool invoke(observer_ptr o);
virtual observer_ptr new_observer(void* ptr, udp::endpoint const& ep, node_id const& id);
data_callback m_data_callback;
bool m_noseeds;
};
struct obfuscated_get_peers : get_peers
{
typedef get_peers::nodes_callback done_callback;
obfuscated_get_peers(node_impl& node, node_id target
, data_callback const& dcallback
, nodes_callback const& ncallback
, bool noseeds);
virtual char const* name() const;
protected:
virtual observer_ptr new_observer(void* ptr, udp::endpoint const& ep,
node_id const& id);
virtual bool invoke(observer_ptr o);
virtual void done();
private:
// when set to false, we no longer obfuscate
// the target hash, and send regular get_peers
bool m_obfuscated;
};
struct get_peers_observer : find_data_observer
{
get_peers_observer(
boost::intrusive_ptr<traversal_algorithm> const& algorithm
, udp::endpoint const& ep, node_id const& id)
: find_data_observer(algorithm, ep, id)
{}
virtual void reply(msg const&);
};
struct obfuscated_get_peers_observer : traversal_observer
{
obfuscated_get_peers_observer(
boost::intrusive_ptr<traversal_algorithm> const& algorithm
, udp::endpoint const& ep, node_id const& id)
: traversal_observer(algorithm, ep, id)
{}
virtual void reply(msg const&);
};
} } // namespace libtorrent::dht
#endif // LIBTORRENT_GET_PEERS_HPP

View File

@ -0,0 +1,122 @@
/*
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.
*/
#ifndef LIBTORRENT_ITEM_HPP
#define LIBTORRENT_ITEM_HPP
#include <libtorrent/sha1_hash.hpp>
#include <libtorrent/lazy_entry.hpp>
#include <libtorrent/entry.hpp>
#include <vector>
#include <exception>
namespace libtorrent { namespace dht
{
bool TORRENT_EXTRA_EXPORT verify_mutable_item(std::pair<char const*, int> v,
boost::uint64_t seq,
char const* pk,
char const* sig);
void TORRENT_EXTRA_EXPORT sign_mutable_item(std::pair<char const*, int> v,
boost::uint64_t seq,
char const* pk,
char const* sk,
char* sig);
sha1_hash TORRENT_EXTRA_EXPORT mutable_item_cas(std::pair<char const*, int> v, boost::uint64_t seq);
struct TORRENT_EXTRA_EXPORT invalid_item : std::exception
{
virtual const char* what() const throw() { return "invalid DHT item"; }
};
enum
{
item_pk_len = 32,
item_sk_len = 64,
item_sig_len = 64
};
struct lazy_item;
class TORRENT_EXTRA_EXPORT item
{
public:
item() : m_mutable(false) {}
item(entry const& v) { assign(v); }
item(entry const& v, boost::uint64_t seq, char const* pk, char const* sk);
item(lazy_entry const* v) { assign(v); }
item(lazy_entry const* v, boost::uint64_t seq, char const* pk, char const* sig);
item(lazy_item const&);
void assign(entry const& v) { assign(v, 0, NULL, NULL); }
void assign(entry const& v, boost::uint64_t seq, char const* pk, char const* sk);
void assign(lazy_entry const* v) { assign(v, 0, NULL, NULL); }
bool assign(lazy_entry const* v, boost::uint64_t seq, char const* pk, char const* sig);
void clear() { m_value = entry(); }
bool empty() const { return m_value.type() == entry::undefined_t; }
bool is_mutable() const { return m_mutable; }
sha1_hash cas();
entry const& value() const { return m_value; }
char const* pk() { TORRENT_ASSERT(m_mutable); return m_pk; }
char const* sig() { TORRENT_ASSERT(m_mutable); return m_sig; }
boost::uint64_t seq() { TORRENT_ASSERT(m_mutable); return m_seq; }
private:
entry m_value;
char m_pk[item_pk_len];
char m_sig[item_sig_len];
boost::uint64_t m_seq;
bool m_mutable;
};
struct lazy_item
{
lazy_item(lazy_entry const* v) : value(v), pk(NULL), sig(NULL), seq(0) {}
lazy_item(lazy_entry const* v, char const* pk, char const* sig, boost::uint64_t seq);
bool is_mutable() const { return pk && sig; }
lazy_entry const* value;
char const* pk;
char const* sig;
boost::uint64_t const seq;
};
} } // namespace libtorrent::dht
#endif // LIBTORRENT_ITEM_HPP

View File

@ -43,6 +43,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include <libtorrent/kademlia/node_id.hpp>
#include <libtorrent/kademlia/msg.hpp>
#include <libtorrent/kademlia/find_data.hpp>
#include <libtorrent/kademlia/item.hpp>
#include <libtorrent/io.hpp>
#include <libtorrent/session_settings.hpp>
@ -132,11 +133,11 @@ struct dht_immutable_item
int size;
};
struct ed25519_public_key { char bytes[32]; };
struct ed25519_public_key { char bytes[item_pk_len]; };
struct dht_mutable_item : dht_immutable_item
{
char sig[64];
char sig[item_sig_len];
boost::uint64_t seq;
ed25519_public_key key;
};
@ -232,6 +233,8 @@ public:
void announce(sha1_hash const& info_hash, int listen_port, bool seed
, boost::function<void(std::vector<tcp::endpoint> const&)> f);
void get_item(sha1_hash const& target, boost::function<bool(item&)> f);
bool verify_token(std::string const& token, char const* info_hash
, udp::endpoint const& addr);

View File

@ -35,7 +35,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include <libtorrent/kademlia/traversal_algorithm.hpp>
#include <libtorrent/kademlia/node_id.hpp>
#include <libtorrent/kademlia/find_data.hpp>
#include <libtorrent/kademlia/get_peers.hpp>
namespace libtorrent { namespace dht
{
@ -43,10 +43,10 @@ namespace libtorrent { namespace dht
class routing_table;
class rpc_manager;
class refresh : public find_data
class refresh : public get_peers
{
public:
typedef find_data::nodes_callback done_callback;
typedef get_peers::nodes_callback done_callback;
refresh(node_impl& node, node_id target
, done_callback const& callback);

View File

@ -67,6 +67,7 @@ struct traversal_algorithm : boost::noncopyable
void failed(observer_ptr o, int flags = 0);
virtual ~traversal_algorithm();
void status(dht_lookup& l);
void abort();
void* allocate_observer();
void free_observer(void* ptr);
@ -112,7 +113,7 @@ protected:
int m_ref_count;
node_impl& m_node;
node_id m_target;
node_id const m_target;
std::vector<observer_ptr> m_results;
int m_invoke_count;
int m_branch_factor;
@ -130,7 +131,7 @@ struct traversal_observer : observer
{}
// parses out "nodes" and keeps traversing
void reply(msg const&);
virtual void reply(msg const&);
};
} } // namespace libtorrent::dht

View File

@ -11,6 +11,9 @@ KADEMLIA_SOURCES = \
kademlia/rpc_manager.cpp \
kademlia/logging.cpp \
kademlia/traversal_algorithm.cpp \
kademlia/get_peers.cpp \
kademlia/get_item.cpp \
kademlia/item.cpp \
../ed25519/src/add_scalar.c \
../ed25519/src/fe.c \
../ed25519/src/ge.c \

View File

@ -80,77 +80,7 @@ void find_data_observer::reply(msg const& m)
node_id(id->string_ptr()), token->string_value());
}
// look for peers
lazy_entry const* n = r->dict_find_list("values");
if (n)
{
std::vector<tcp::endpoint> peer_list;
if (n->list_size() == 1 && n->list_at(0)->type() == lazy_entry::string_t)
{
// assume it's mainline format
char const* peers = n->list_at(0)->string_ptr();
char const* end = peers + n->list_at(0)->string_length();
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(traversal)
<< "[" << m_algorithm.get() << "] PEERS"
<< " invoke-count: " << m_algorithm->invoke_count()
<< " branch-factor: " << m_algorithm->branch_factor()
<< " addr: " << m.addr
<< " id: " << node_id(id->string_ptr())
<< " distance: " << distance_exp(m_algorithm->target(), node_id(id->string_ptr()))
<< " p: " << ((end - peers) / 6);
#endif
while (end - peers >= 6)
peer_list.push_back(read_v4_endpoint<tcp::endpoint>(peers));
}
else
{
// assume it's uTorrent/libtorrent format
read_endpoint_list<tcp::endpoint>(n, peer_list);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(traversal)
<< "[" << m_algorithm.get() << "] PEERS"
<< " invoke-count: " << m_algorithm->invoke_count()
<< " branch-factor: " << m_algorithm->branch_factor()
<< " addr: " << m.addr
<< " id: " << node_id(id->string_ptr())
<< " distance: " << distance_exp(m_algorithm->target(), node_id(id->string_ptr()))
<< " p: " << n->list_size();
#endif
}
static_cast<find_data*>(m_algorithm.get())->got_peers(peer_list);
}
traversal_observer::reply(m);
done();
}
void obfuscated_find_data_observer::reply(msg const& m)
{
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;
}
lazy_entry const* id = r->dict_find_string("id");
if (!id || id->string_length() != 20)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(traversal) << "[" << m_algorithm.get()
<< "] invalid id in response";
#endif
return;
}
traversal_observer::reply(m);
done();
}
@ -163,16 +93,10 @@ void add_entry_fun(void* userdata, node_entry const& e)
find_data::find_data(
node_impl& node
, node_id target
, data_callback const& dcallback
, nodes_callback const& ncallback
, bool noseeds)
, nodes_callback const& ncallback)
: traversal_algorithm(node, target)
, m_data_callback(dcallback)
, m_nodes_callback(ncallback)
, m_target(target)
, m_done(false)
, m_got_peers(false)
, m_noseeds(noseeds)
{
}
@ -196,32 +120,7 @@ observer_ptr find_data::new_observer(void* ptr
return o;
}
char const* find_data::name() const { return "get_peers"; }
bool find_data::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_peers";
a["info_hash"] = m_target.to_string();
if (m_noseeds) a["noseed"] = 1;
return m_node.m_rpc.invoke(e, o->target_ep(), o);
}
void find_data::got_peers(std::vector<tcp::endpoint> const& peers)
{
if (!peers.empty()) m_got_peers = true;
if (m_data_callback) m_data_callback(peers);
}
char const* find_data::name() const { return "find_data"; }
void find_data::done()
{
@ -230,7 +129,7 @@ void find_data::done()
m_done = true;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(traversal) << "[" << this << "] get_peers DONE";
TORRENT_LOG(traversal) << "[" << this << "] find_data DONE";
#endif
std::vector<std::pair<node_entry, std::string> > results;
@ -246,138 +145,10 @@ void find_data::done()
results.push_back(std::make_pair(node_entry(o->id(), o->target_ep()), j->second));
--num_results;
}
if (m_nodes_callback) m_nodes_callback(results, m_got_peers);
if (m_nodes_callback) m_nodes_callback(results);
traversal_algorithm::done();
}
obfuscated_get_peers::obfuscated_get_peers(
node_impl& node
, node_id info_hash
, data_callback const& dcallback
, nodes_callback const& ncallback
, bool noseeds)
: find_data(node, info_hash, dcallback, ncallback, noseeds)
, m_obfuscated(true)
{
}
char const* obfuscated_get_peers::name() const
{ return !m_obfuscated ? find_data::name() : "get_peers [obfuscated]"; }
observer_ptr obfuscated_get_peers::new_observer(void* ptr
, udp::endpoint const& ep, node_id const& id)
{
if (m_obfuscated)
{
observer_ptr o(new (ptr) obfuscated_find_data_observer(this, ep, id));
#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS
o->m_in_constructor = false;
#endif
return o;
}
else
{
observer_ptr o(new (ptr) find_data_observer(this, ep, id));
#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS
o->m_in_constructor = false;
#endif
return o;
}
}
bool obfuscated_get_peers::invoke(observer_ptr o)
{
if (!m_obfuscated) return find_data::invoke(o);
node_id id = o->id();
int shared_prefix = 160 - distance_exp(id, m_target);
// when we get close to the target zone in the DHT
// start using the correct info-hash, in order to
// start receiving peers
if (shared_prefix > m_node.m_table.depth() - 10)
{
m_obfuscated = false;
// clear the queried bits on all successful nodes in
// our node-list for this traversal algorithm, to
// allow the get_peers traversal to regress in case
// nodes further down end up being dead
for (std::vector<observer_ptr>::iterator i = m_results.begin()
, end(m_results.end()); i != end; ++i)
{
observer* o = i->get();
// don't re-request from nodes that didn't respond
if (o->flags & observer::flag_failed) continue;
// don't interrupt with queries that are already in-flight
if ((o->flags & observer::flag_alive) == 0) continue;
o->flags &= ~(observer::flag_queried | observer::flag_alive);
}
return find_data::invoke(o);
}
entry e;
e["y"] = "q";
e["q"] = "find_node";
entry& a = e["a"];
// This logic will obfuscate the target info-hash
// we're looking up, in order to preserve more privacy
// on the DHT. This is done by only including enough
// bits in the info-hash for the node we're querying to
// give a good answer, but not more.
// now, obfuscate the bits past shared_prefix + 5
node_id obfuscated_target = generate_random_id();
obfuscated_target >>= shared_prefix + 3;
obfuscated_target^= m_target;
a["target"] = obfuscated_target.to_string();
return m_node.m_rpc.invoke(e, o->target_ep(), o);
}
void obfuscated_get_peers::done()
{
if (!m_obfuscated) return find_data::done();
// oops, we failed to switch over to the non-obfuscated
// mode early enough. do it now
boost::intrusive_ptr<find_data> ta(new find_data(m_node, m_target
, m_data_callback
, m_nodes_callback
, m_noseeds));
// don't call these when the obfuscated_get_peers
// is done, we're passing them on to be called when
// ta completes.
m_data_callback.clear();
m_nodes_callback.clear();
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(traversal) << " [" << this << "]"
<< " obfuscated get_peers phase 1 done, spawning get_peers [" << ta.get() << "]";
#endif
int num_added = 0;
for (std::vector<observer_ptr>::iterator i = m_results.begin()
, end(m_results.end()); i != end && num_added < 16; ++i)
{
observer_ptr o = *i;
// only add nodes whose node ID we know and that
// we know are alive
if (o->flags & observer::flag_no_id) continue;
if ((o->flags & observer::flag_alive) == 0) continue;
ta->add_entry(o->id(), o->target_ep(), observer::flag_initial);
++num_added;
}
ta->start();
find_data::done();
}
} } // namespace libtorrent::dht

231
src/kademlia/get_item.cpp Normal file
View File

@ -0,0 +1,231 @@
/*
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.
*/
#include <libtorrent/hasher.hpp>
#include <libtorrent/kademlia/get_item.hpp>
#include <libtorrent/kademlia/node.hpp>
#if (defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS) && !TORRENT_NO_ASSERTS
#include <libtorrent/bencode.hpp>
#endif
namespace libtorrent { namespace dht
{
void get_item::got_data(lazy_entry const* v,
char const* pk,
boost::uint64_t seq,
char const* sig)
{
if (pk && sig)
{
if (hasher(pk, item_pk_len).final() != m_target)
return;
if (m_data.empty() || m_data.seq() < seq)
{
if (!m_data.assign(v, seq, pk, sig))
return;
}
}
else if (m_data.empty())
{
std::pair<char const*, int> buf = v->data_section();
if (hasher(buf.first, buf.second).final() != m_target)
return;
m_data.assign(v);
bool put_requested = m_data_callback(m_data);
if (put_requested)
{
#if (defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS) && !TORRENT_NO_ASSERTS
std::vector<char> buffer;
bencode(std::back_inserter(buffer), m_data.value());
TORRENT_ASSERT(m_target == hasher(&buffer[0], buffer.size()).final());
#endif
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(
node_impl& node
, node_id target
, data_callback const& dcallback)
: find_data(node, target, nodes_callback())
, m_data_callback(dcallback)
{
}
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));
#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS
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())
{
bool put_requested = m_data_callback(m_data);
if (put_requested)
{
#if (defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS) && !TORRENT_NO_ASSERTS
if (m_data.is_mutable())
{
TORRENT_ASSERT(m_target == hasher(m_data.pk(), item_pk_len).final());
}
else
{
std::vector<char> buffer;
bencode(std::back_inserter(buffer), m_data.value());
TORRENT_ASSERT(m_target == hasher(&buffer[0], buffer.size()).final());
}
#endif
m_nodes_callback = boost::bind(&get_item::put, this, _1);
}
}
find_data::done();
}
void get_item::put(std::vector<std::pair<node_entry, std::string> > const& v)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(node) << "sending put [ v: " << m_data.value()
<< " seq: " << (m_data.is_mutable() ? m_data.seq() : -1)
<< " nodes: " << v.size() << " ]" ;
#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)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(node) << " put-distance: " << (160 - distance_exp(m_target, i->first.id));
#endif
void* ptr = m_node.m_rpc.allocate_observer();
if (ptr == 0) return;
observer_ptr o(new (ptr) announce_observer(algo, i->first.ep(), i->first.id));
#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS
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())
{
a["k"] = std::string(m_data.pk(), item_pk_len);
a["seq"] = m_data.seq();
a["sig"] = std::string(m_data.sig(), item_sig_len);
}
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;
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;
}
lazy_entry const* k = r->dict_find_string("k");
if (k && k->string_length() == item_pk_len)
pk = k->string_ptr();
lazy_entry const* s = r->dict_find_string("sig");
if (s && s->string_length() == item_sig_len)
sig = s->string_ptr();
lazy_entry const* q = r->dict_find_int("seq");
if (q)
seq = q->int_value();
else if (pk && sig)
return;
lazy_entry const* v = r->dict_find("v");
if (v)
{
static_cast<get_item*>(m_algorithm.get())->got_data(v, pk, seq, sig);
}
find_data_observer::reply(m);
}
} } // namespace libtorrent::dht

313
src/kademlia/get_peers.cpp Normal file
View File

@ -0,0 +1,313 @@
/*
Copyright (c) 2006-2013, Arvid Norberg & Daniel Wallin
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 <libtorrent/kademlia/get_peers.hpp>
#include <libtorrent/kademlia/node.hpp>
#include <libtorrent/socket_io.hpp>
namespace libtorrent { namespace dht
{
using detail::read_endpoint_list;
using detail::read_v4_endpoint;
#if TORRENT_USE_IPV6
using detail::read_v6_endpoint;
#endif
void get_peers_observer::reply(msg const& m)
{
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 peers
lazy_entry const* n = r->dict_find_list("values");
if (n)
{
std::vector<tcp::endpoint> peer_list;
if (n->list_size() == 1 && n->list_at(0)->type() == lazy_entry::string_t)
{
// assume it's mainline format
char const* peers = n->list_at(0)->string_ptr();
char const* end = peers + n->list_at(0)->string_length();
#ifdef TORRENT_DHT_VERBOSE_LOGGING
lazy_entry const* id = r->dict_find_string("id");
if (id && id->string_length() == 20)
{
TORRENT_LOG(traversal)
<< "[" << m_algorithm.get() << "] PEERS"
<< " invoke-count: " << m_algorithm->invoke_count()
<< " branch-factor: " << m_algorithm->branch_factor()
<< " addr: " << m.addr
<< " id: " << node_id(id->string_ptr())
<< " distance: " << distance_exp(m_algorithm->target(), node_id(id->string_ptr()))
<< " p: " << ((end - peers) / 6);
}
#endif
while (end - peers >= 6)
peer_list.push_back(read_v4_endpoint<tcp::endpoint>(peers));
}
else
{
// assume it's uTorrent/libtorrent format
read_endpoint_list<tcp::endpoint>(n, peer_list);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
lazy_entry const* id = r->dict_find_string("id");
if (id && id->string_length() == 20)
{
TORRENT_LOG(traversal)
<< "[" << m_algorithm.get() << "] PEERS"
<< " invoke-count: " << m_algorithm->invoke_count()
<< " branch-factor: " << m_algorithm->branch_factor()
<< " addr: " << m.addr
<< " id: " << node_id(id->string_ptr())
<< " distance: " << distance_exp(m_algorithm->target(), node_id(id->string_ptr()))
<< " p: " << n->list_size();
}
#endif
}
static_cast<get_peers*>(m_algorithm.get())->got_peers(peer_list);
}
find_data_observer::reply(m);
}
void get_peers::got_peers(std::vector<tcp::endpoint> const& peers)
{
if (m_data_callback) m_data_callback(peers);
}
get_peers::get_peers(
node_impl& node
, node_id target
, data_callback const& dcallback
, nodes_callback const& ncallback
, bool noseeds)
: find_data(node, target, ncallback)
, m_data_callback(dcallback)
, m_noseeds(noseeds)
{
}
char const* get_peers::name() const { return "get_peers"; }
bool get_peers::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_peers";
a["info_hash"] = m_target.to_string();
if (m_noseeds) a["noseed"] = 1;
return m_node.m_rpc.invoke(e, o->target_ep(), o);
}
observer_ptr get_peers::new_observer(void* ptr
, udp::endpoint const& ep, node_id const& id)
{
observer_ptr o(new (ptr) get_peers_observer(this, ep, id));
#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS
o->m_in_constructor = false;
#endif
return o;
}
obfuscated_get_peers::obfuscated_get_peers(
node_impl& node
, node_id info_hash
, data_callback const& dcallback
, nodes_callback const& ncallback
, bool noseeds)
: get_peers(node, info_hash, dcallback, ncallback, noseeds)
, m_obfuscated(true)
{
}
char const* obfuscated_get_peers::name() const
{ return !m_obfuscated ? get_peers::name() : "get_peers [obfuscated]"; }
observer_ptr obfuscated_get_peers::new_observer(void* ptr
, udp::endpoint const& ep, node_id const& id)
{
if (m_obfuscated)
{
observer_ptr o(new (ptr) obfuscated_get_peers_observer(this, ep, id));
#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS
o->m_in_constructor = false;
#endif
return o;
}
else
{
observer_ptr o(new (ptr) get_peers_observer(this, ep, id));
#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS
o->m_in_constructor = false;
#endif
return o;
}
}
bool obfuscated_get_peers::invoke(observer_ptr o)
{
if (!m_obfuscated) return get_peers::invoke(o);
node_id id = o->id();
int shared_prefix = 160 - distance_exp(id, m_target);
// when we get close to the target zone in the DHT
// start using the correct info-hash, in order to
// start receiving peers
if (shared_prefix > m_node.m_table.depth() - 10)
{
m_obfuscated = false;
// clear the queried bits on all successful nodes in
// our node-list for this traversal algorithm, to
// allow the get_peers traversal to regress in case
// nodes further down end up being dead
for (std::vector<observer_ptr>::iterator i = m_results.begin()
, end(m_results.end()); i != end; ++i)
{
observer* o = i->get();
// don't re-request from nodes that didn't respond
if (o->flags & observer::flag_failed) continue;
// don't interrupt with queries that are already in-flight
if ((o->flags & observer::flag_alive) == 0) continue;
o->flags &= ~(observer::flag_queried | observer::flag_alive);
}
return get_peers::invoke(o);
}
entry e;
e["y"] = "q";
e["q"] = "find_node";
entry& a = e["a"];
// This logic will obfuscate the target info-hash
// we're looking up, in order to preserve more privacy
// on the DHT. This is done by only including enough
// bits in the info-hash for the node we're querying to
// give a good answer, but not more.
// now, obfuscate the bits past shared_prefix + 5
node_id obfuscated_target = generate_random_id();
obfuscated_target >>= shared_prefix + 3;
obfuscated_target^= m_target;
a["target"] = obfuscated_target.to_string();
return m_node.m_rpc.invoke(e, o->target_ep(), o);
}
void obfuscated_get_peers::done()
{
if (!m_obfuscated) return get_peers::done();
// oops, we failed to switch over to the non-obfuscated
// mode early enough. do it now
boost::intrusive_ptr<get_peers> ta(new get_peers(m_node, m_target
, m_data_callback
, m_nodes_callback
, m_noseeds));
// don't call these when the obfuscated_get_peers
// is done, we're passing them on to be called when
// ta completes.
m_data_callback.clear();
m_nodes_callback.clear();
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(traversal) << " [" << this << "]"
<< " obfuscated get_peers phase 1 done, spawning get_peers [" << ta.get() << "]";
#endif
int num_added = 0;
for (std::vector<observer_ptr>::iterator i = m_results.begin()
, end(m_results.end()); i != end && num_added < 16; ++i)
{
observer_ptr o = *i;
// only add nodes whose node ID we know and that
// we know are alive
if (o->flags & observer::flag_no_id) continue;
if ((o->flags & observer::flag_alive) == 0) continue;
ta->add_entry(o->id(), o->target_ep(), observer::flag_initial);
++num_added;
}
ta->start();
get_peers::done();
}
void obfuscated_get_peers_observer::reply(msg const& m)
{
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;
}
lazy_entry const* id = r->dict_find_string("id");
if (!id || id->string_length() != 20)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(traversal) << "[" << m_algorithm.get()
<< "] invalid id in response";
#endif
return;
}
traversal_observer::reply(m);
done();
}
} } // namespace libtorrent::dht

177
src/kademlia/item.cpp Normal file
View File

@ -0,0 +1,177 @@
/*
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.
*/
#include <libtorrent/hasher.hpp>
#include <libtorrent/kademlia/item.hpp>
#include <libtorrent/bencode.hpp>
#include "ed25519.h"
namespace libtorrent { namespace dht
{
namespace
{
enum { canonical_length = 1100 };
int canonical_string(std::pair<char const*, int> v, boost::uint64_t seq, char out[canonical_length])
{
int len = snprintf(out, canonical_length, "3:seqi%" PRId64 "e1:v", seq);
memcpy(out + len, v.first, v.second);
len += v.second;
TORRENT_ASSERT(len <= canonical_length);
return len;
}
}
bool verify_mutable_item(std::pair<char const*, int> v,
boost::uint64_t seq,
char const* pk,
char const* sig)
{
#ifdef TORRENT_USE_VALGRIND
VALGRIND_CHECK_MEM_IS_DEFINED(v.first, v.second);
VALGRIND_CHECK_MEM_IS_DEFINED(pk, item_pk_len);
VALGRIND_CHECK_MEM_IS_DEFINED(sig, item_sig_len);
#endif
char str[canonical_length];
int len = canonical_string(v, seq, str);
return ed25519_verify((unsigned char const*)sig,
(unsigned char const*)str,
len,
(unsigned char const*)pk) == 1;
}
void sign_mutable_item(std::pair<char const*, int> v,
boost::uint64_t seq,
char const* pk,
char const* sk,
char* sig)
{
#ifdef TORRENT_USE_VALGRIND
VALGRIND_CHECK_MEM_IS_DEFINED(v.first, v.second);
VALGRIND_CHECK_MEM_IS_DEFINED(sk, item_sk_len);
VALGRIND_CHECK_MEM_IS_DEFINED(pk, item_pk_len);
#endif
char str[canonical_length];
int len = canonical_string(v, seq, str);
ed25519_sign((unsigned char*)sig,
(unsigned char const*)str,
len,
(unsigned char const*)pk,
(unsigned char const*)sk
);
}
sha1_hash mutable_item_cas(std::pair<char const*, int> v, boost::uint64_t seq)
{
char str[canonical_length];
int len = canonical_string(v, seq, str);
return hasher(str, len).final();
}
item::item(entry const& v, boost::uint64_t seq, char const* pk, char const* sk)
{
assign(v, seq, pk, sk);
}
item::item(lazy_entry const* v, boost::uint64_t seq, char const* pk, char const* sig)
{
if (!assign(v, seq, pk, sig))
throw invalid_item();
}
item::item(lazy_item const& i)
: m_seq(i.seq)
, m_mutable(i.is_mutable())
{
m_value = *i.value;
// if this is a mutable item lazy_item will have already verified it
memcpy(m_pk, i.pk, item_pk_len);
memcpy(m_sig, i.sig, item_sig_len);
}
void item::assign(entry const& v, boost::uint64_t seq, char const* pk, char const* sk)
{
m_value = v;
if (pk && sk)
{
char buffer[1000];
int bsize = bencode(buffer, v);
TORRENT_ASSERT(bsize <= 1000);
sign_mutable_item(std::make_pair(buffer, bsize), seq, pk, sk, m_sig);
memcpy(m_pk, pk, item_pk_len);
m_seq = seq;
m_mutable = true;
}
else
m_mutable = false;
}
bool item::assign(lazy_entry const* v, boost::uint64_t seq, char const* pk, char const* sig)
{
TORRENT_ASSERT(v->data_section().second <= 1000);
if (pk && sig)
{
if (!verify_mutable_item(v->data_section(), seq, pk, sig))
return false;
memcpy(m_pk, pk, item_pk_len);
memcpy(m_sig, sig, item_sig_len);
m_seq = seq;
m_mutable = true;
}
else
m_mutable = false;
m_value = *v;
return true;
}
sha1_hash item::cas()
{
TORRENT_ASSERT(m_mutable);
char buffer[1000];
int bsize = bencode(buffer, m_value);
return mutable_item_cas(std::make_pair(buffer, bsize), m_seq);
}
lazy_item::lazy_item(lazy_entry const* v, char const* pk, char const* sig, boost::uint64_t seq)
: value(v), pk(pk), sig(sig), seq(seq)
{
if (is_mutable() && !verify_mutable_item(v->data_section(), seq, pk, sig))
throw invalid_item();
}
} } // namespace libtorrent::dht

View File

@ -48,12 +48,11 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/kademlia/rpc_manager.hpp"
#include "libtorrent/kademlia/routing_table.hpp"
#include "libtorrent/kademlia/node.hpp"
#include <libtorrent/kademlia/dht_observer.hpp>
#include "libtorrent/kademlia/dht_observer.hpp"
#include "libtorrent/kademlia/refresh.hpp"
#include "libtorrent/kademlia/find_data.hpp"
#include "ed25519.h"
#include "libtorrent/kademlia/get_peers.hpp"
#include "libtorrent/kademlia/get_item.hpp"
#ifdef TORRENT_USE_VALGRIND
#include <valgrind/memcheck.h>
@ -370,7 +369,7 @@ void node_impl::announce(sha1_hash const& info_hash, int listen_port, bool seed
// search for nodes with ids close to id or with peers
// for info-hash id. then send announce_peer to them.
boost::intrusive_ptr<find_data> ta;
boost::intrusive_ptr<get_peers> ta;
if (m_settings.privacy_lookups)
{
ta.reset(new obfuscated_get_peers(*this, info_hash, f
@ -379,7 +378,7 @@ void node_impl::announce(sha1_hash const& info_hash, int listen_port, bool seed
}
else
{
ta.reset(new find_data(*this, info_hash, f
ta.reset(new get_peers(*this, info_hash, f
, boost::bind(&announce_fun, _1, boost::ref(*this)
, listen_port, info_hash, seed), seed));
}
@ -387,6 +386,17 @@ void node_impl::announce(sha1_hash const& info_hash, int listen_port, bool seed
ta->start();
}
void node_impl::get_item(sha1_hash const& target, boost::function<bool(item&)> f)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(node) << "starting get for [ " << target << " ]" ;
#endif
boost::intrusive_ptr<dht::get_item> ta;
ta.reset(new dht::get_item(*this, target, f));
ta->start();
}
void node_impl::tick()
{
node_id target;
@ -512,9 +522,9 @@ void node_impl::lookup_peers(sha1_hash const& info_hash, int prefix, entry& repl
return;
}
namespace
namespace detail
{
void write_nodes_entry(entry& r, nodes_t const& nodes)
void TORRENT_EXTRA_EXPORT write_nodes_entry(entry& r, nodes_t const& nodes)
{
bool ipv6_nodes = false;
entry& n = r["nodes"];
@ -550,6 +560,7 @@ namespace
}
}
}
using detail::write_nodes_entry;
// verifies that a message has all the required
// entries and returns them in ret
@ -890,8 +901,8 @@ void node_impl::incoming_request(msg const& m, entry& e)
{"v", lazy_entry::none_t, 0, 0},
{"seq", lazy_entry::int_t, 0, key_desc_t::optional},
// public key
{"k", lazy_entry::string_t, 32, key_desc_t::optional},
{"sig", lazy_entry::string_t, 64, key_desc_t::optional},
{"k", lazy_entry::string_t, item_pk_len, key_desc_t::optional},
{"sig", lazy_entry::string_t, item_sig_len, key_desc_t::optional},
{"cas", lazy_entry::string_t, 20, key_desc_t::optional},
};
@ -918,7 +929,7 @@ void node_impl::incoming_request(msg const& m, entry& e)
if (!mutable_put)
target = hasher(buf.first, buf.second).final();
else
target = hasher(msg_keys[3]->string_ptr(), 32).final();
target = hasher(msg_keys[3]->string_ptr(), item_pk_len).final();
// fprintf(stderr, "%s PUT target: %s\n"
// , mutable_put ? "mutable":"immutable"
@ -969,25 +980,16 @@ void node_impl::incoming_request(msg const& m, entry& e)
else
{
// mutable put, we must verify the signature
// generate the message digest by merging the sequence number and the
char seq[1100];
int len = snprintf(seq, sizeof(seq), "3:seqi%" PRId64 "e1:v", msg_keys[2]->int_value());
std::pair<char const*, int> buf = msg_keys[1]->data_section();
memcpy(seq + len, buf.first, buf.second);
len += buf.second;
TORRENT_ASSERT(len <= 1100);
#ifdef TORRENT_USE_VALGRIND
VALGRIND_CHECK_MEM_IS_DEFINED(buf.first, buf.second);
VALGRIND_CHECK_MEM_IS_DEFINED(msg_keys[4]->string_ptr(), 64);
VALGRIND_CHECK_MEM_IS_DEFINED(msg_keys[3]->string_ptr(), 32);
VALGRIND_CHECK_MEM_IS_DEFINED(seq, len);
VALGRIND_CHECK_MEM_IS_DEFINED(msg_keys[4]->string_ptr(), item_sig_len);
VALGRIND_CHECK_MEM_IS_DEFINED(msg_keys[3]->string_ptr(), item_pk_len);
#endif
// msg_keys[4] is the signature, msg_keys[3] is the public key
if (ed25519_verify((unsigned char const*)msg_keys[4]->string_ptr()
, (unsigned char const*)seq, len
, (unsigned char const*)msg_keys[3]->string_ptr()) != 1)
if (!verify_mutable_item(msg_keys[1]->data_section()
, msg_keys[2]->int_value()
, msg_keys[3]->string_ptr()
, msg_keys[4]->string_ptr()))
{
incoming_error(e, "invalid signature", 206);
return;
@ -1035,10 +1037,7 @@ void node_impl::incoming_request(msg const& m, entry& e)
// matches the expected value before replacing it
if (msg_keys[5])
{
int len = snprintf(seq, sizeof(seq), "3:seqi%" PRId64 "e1:v", item->seq);
memcpy(seq + len, item->value, item->size);
len += item->size;
sha1_hash h = hasher(seq, len).final();
sha1_hash h = mutable_item_cas(std::make_pair(item->value, item->size), item->seq);
if (h != sha1_hash(msg_keys[5]->string_ptr()))
{

View File

@ -49,7 +49,7 @@ refresh::refresh(
node_impl& node
, node_id target
, done_callback const& callback)
: find_data(node, target, find_data::data_callback(), callback, false)
: get_peers(node, target, get_peers::data_callback(), callback, false)
{
}
@ -61,7 +61,7 @@ char const* refresh::name() const
observer_ptr refresh::new_observer(void* ptr
, udp::endpoint const& ep, node_id const& id)
{
observer_ptr o(new (ptr) find_data_observer(this, ep, id));
observer_ptr o(new (ptr) get_peers_observer(this, ep, id));
#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS
o->m_in_constructor = false;
#endif

View File

@ -500,5 +500,18 @@ void traversal_observer::reply(msg const& m)
}
}
void traversal_algorithm::abort()
{
m_num_target_nodes = 0;
for (std::vector<observer_ptr>::iterator i = m_results.begin()
, end(m_results.end()); i != end; ++i)
{
observer& o = **i;
if (o.flags & observer::flag_queried)
o.flags |= observer::flag_done;
}
done();
}
} } // namespace libtorrent::dht

View File

@ -52,7 +52,7 @@ rule link_libtorrent ( properties * )
else
{
result +=
<library>/torrent//torrent/<link>static/<boost-link>static/<export-extra>on ;
<library>/torrent//torrent/<link>static/<boost-link>static/<export-extra>on/<test-coverage>on ;
}
return $(result) ;
}

View File

@ -55,7 +55,7 @@ extern "C"
int EXPORT ed25519_create_seed(unsigned char *seed);
void EXPORT ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed);
void EXPORT ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key);
int EXPORT ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *private_key);
int EXPORT ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key);
void EXPORT ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar);
void EXPORT ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key);
}

View File

@ -32,6 +32,7 @@ POSSIBILITY OF SUCH DAMAGE.
#ifndef TORRENT_DISABLE_DHT
#include "libtorrent/config.hpp"
#include "libtorrent/session.hpp"
#include "libtorrent/kademlia/node.hpp" // for verify_message
#include "libtorrent/bencode.hpp"
@ -41,6 +42,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/kademlia/node_id.hpp"
#include "libtorrent/kademlia/routing_table.hpp"
#include "libtorrent/kademlia/item.hpp"
#include <numeric>
#include "test.hpp"
@ -57,6 +59,8 @@ POSSIBILITY OF SUCH DAMAGE.
using namespace libtorrent;
using namespace libtorrent::dht;
void nop() {}
sha1_hash to_hash(char const* s)
{
sha1_hash ret;
@ -84,13 +88,13 @@ void node_push_back(void* userdata, libtorrent::dht::node_entry const& n)
void nop(void* userdata, libtorrent::dht::node_entry const& n) {}
std::list<std::pair<udp::endpoint, entry> > g_responses;
std::list<std::pair<udp::endpoint, entry> > g_sent_packets;
struct mock_socket : udp_socket_interface
{
bool send_packet(entry& msg, udp::endpoint const& ep, int flags)
{
g_responses.push_back(std::make_pair(ep, msg));
g_sent_packets.push_back(std::make_pair(ep, msg));
return true;
}
};
@ -121,7 +125,23 @@ boost::array<char, 64> generate_key()
static const std::string no;
void send_dht_msg(node_impl& node, char const* msg, udp::endpoint const& ep
std::list<std::pair<udp::endpoint, entry> >::iterator
find_packet(udp::endpoint ep)
{
return std::find_if(g_sent_packets.begin(), g_sent_packets.end()
, boost::bind(&std::pair<udp::endpoint, entry>::first, _1) == ep);
}
void lazy_from_entry(entry const& e, lazy_entry& l)
{
error_code ec;
static char inbuf[1500];
int len = bencode(inbuf, e);
int ret = lazy_bdecode(inbuf, inbuf + len, l, ec);
TEST_CHECK(ret == 0);
}
void send_dht_request(node_impl& node, char const* msg, udp::endpoint const& ep
, lazy_entry* reply, char const* t = "10", char const* info_hash = 0
, char const* name = 0, std::string const token = std::string(), int port = 0
, char const* target = 0, entry const* value = 0
@ -171,22 +191,81 @@ void send_dht_msg(node_impl& node, char const* msg, udp::endpoint const& ep
node.incoming(m);
// by now the node should have invoked the send function and put the
// response in g_responses
// response in g_sent_packets
std::list<std::pair<udp::endpoint, entry> >::iterator i
= std::find_if(g_responses.begin(), g_responses.end()
, boost::bind(&std::pair<udp::endpoint, entry>::first, _1) == ep);
if (i == g_responses.end())
= find_packet(ep);
if (i == g_sent_packets.end())
{
TEST_ERROR("not response from DHT node");
return;
}
static char inbuf[1500];
int len = bencode(inbuf, i->second);
g_responses.erase(i);
int ret = lazy_bdecode(inbuf, inbuf + len, *reply, ec);
TEST_CHECK(ret == 0);
lazy_from_entry(i->second, *reply);
g_sent_packets.erase(i);
}
namespace libtorrent { namespace dht { namespace detail
{
// defined in node.cpp
void write_nodes_entry(entry& r, nodes_t const& nodes);
} } }
void write_peers(entry::dictionary_type& r, std::set<tcp::endpoint> const& peers)
{
entry::list_type& pe = r["values"].list();
for (std::set<tcp::endpoint>::const_iterator it = peers.begin()
; it != peers.end(); ++it)
{
std::string endpoint(18, '\0');
std::string::iterator out = endpoint.begin();
libtorrent::detail::write_endpoint(*it, out);
endpoint.resize(out - endpoint.begin());
pe.push_back(entry(endpoint));
}
}
void send_dht_response(node_impl& node, lazy_entry const& request, udp::endpoint const& ep
, nodes_t const& nodes = nodes_t()
, std::string const token = std::string(), int port = 0
, std::set<tcp::endpoint> const& peers = std::set<tcp::endpoint>()
, char const* target = 0, entry const* value = 0
, std::string const key = std::string(), std::string const sig = std::string()
, int seq = -1, sha1_hash const* nid = NULL)
{
entry e;
e["y"] = "r";
e["t"] = request.dict_find_string_value("t");
// e["ip"] = endpoint_to_bytes(ep);
entry::dictionary_type& r = e["r"].dict();
if (nid == NULL) r["id"] = generate_next().to_string();
else r["id"] = nid->to_string();
if (!token.empty()) r["token"] = token;
if (port) r["p"] = port;
if (!nodes.empty()) dht::detail::write_nodes_entry(e["r"], nodes);
if (!peers.empty()) write_peers(r, peers);
if (value) r["v"] = *value;
if (!sig.empty()) r["sig"] = sig;
if (!key.empty()) r["k"] = key;
if (seq >= 0) r["seq"] = seq;
char msg_buf[1500];
int size = bencode(msg_buf, e);
#if defined TORRENT_DEBUG && TORRENT_USE_IOSTREAM
// this yields a lot of output. too much
// std::cerr << "sending: " << e << "\n";
#endif
#ifdef TORRENT_USE_VALGRIND
VALGRIND_CHECK_MEM_IS_DEFINED(msg_buf, size);
#endif
lazy_entry decoded;
error_code ec;
lazy_bdecode(msg_buf, msg_buf + size, decoded, ec);
if (ec) fprintf(stderr, "lazy_bdecode failed: %s\n", ec.message().c_str());
dht::msg m(decoded, ep);
node.incoming(m);
}
struct announce_item
@ -220,7 +299,7 @@ void announce_immutable_items(node_impl& node, udp::endpoint const* eps
{
if ((i % items[j].num_peers) == 0) continue;
lazy_entry response;
send_dht_msg(node, "get", eps[i], &response, "10", 0
send_dht_request(node, "get", eps[i], &response, "10", 0
, 0, no, 0, (char const*)&items[j].target[0]);
key_desc_t desc[] =
@ -258,7 +337,7 @@ void announce_immutable_items(node_impl& node, udp::endpoint const* eps
TEST_EQUAL(addr, eps[i].address());
}
send_dht_msg(node, "put", eps[i], &response, "10", 0
send_dht_request(node, "put", eps[i], &response, "10", 0
, 0, token, 0, (char const*)&items[j].target[0], &items[j].ent);
key_desc_t desc2[] =
@ -287,7 +366,7 @@ void announce_immutable_items(node_impl& node, udp::endpoint const* eps
for (int j = 0; j < num_items; ++j)
{
lazy_entry response;
send_dht_msg(node, "get", eps[j], &response, "10", 0
send_dht_request(node, "get", eps[j], &response, "10", 0
, 0, no, 0, (char const*)&items[j].target[0]);
key_desc_t desc[] =
@ -327,12 +406,37 @@ struct print_alert : alert_dispatcher
}
};
int sum_distance_exp(int s, node_entry const& e, node_id const& ref)
{
return s + distance_exp(e.id, ref);
}
// TODO: 3 test find_data, obfuscated_get_peers and bootstrap
std::vector<tcp::endpoint> g_got_peers;
void get_peers_cb(std::vector<tcp::endpoint> const& peers)
{
g_got_peers.insert(g_got_peers.end(), peers.begin(), peers.end());
}
std::vector<dht::item> g_got_items;
dht::item g_put_item;
int g_put_count;
bool get_item_cb(dht::item& i)
{
if (!i.empty())
g_got_items.push_back(i);
if (!g_put_item.empty())
{
i = g_put_item;
g_put_count++;
return true;
}
return false;
}
// TODO: 3 test obfuscated_get_peers
int test_main()
{
dht_settings sett;
@ -346,13 +450,13 @@ int test_main()
// DHT should be running on port 48199 now
lazy_entry response;
lazy_entry const* parsed[10];
lazy_entry const* parsed[11];
char error_string[200];
bool ret;
// ====== ping ======
udp::endpoint source(address::from_string("10.0.0.1"), 20);
send_dht_msg(node, "ping", source, &response, "10");
send_dht_request(node, "ping", source, &response, "10");
dht::key_desc_t pong_desc[] = {
{"y", lazy_entry::string_t, 1, 0},
@ -376,7 +480,7 @@ int test_main()
// ====== invalid message ======
send_dht_msg(node, "find_node", source, &response, "10");
send_dht_request(node, "find_node", source, &response, "10");
dht::key_desc_t err_desc[] = {
{"y", lazy_entry::string_t, 1, 0},
@ -406,7 +510,7 @@ int test_main()
// ====== get_peers ======
send_dht_msg(node, "get_peers", source, &response, "10", "01010101010101010101");
send_dht_request(node, "get_peers", source, &response, "10", "01010101010101010101");
dht::key_desc_t peer1_desc[] = {
{"y", lazy_entry::string_t, 1, 0},
@ -433,7 +537,7 @@ int test_main()
// ====== announce ======
send_dht_msg(node, "announce_peer", source, &response, "10", "01010101010101010101", "test", token, 8080);
send_dht_request(node, "announce_peer", source, &response, "10", "01010101010101010101", "test", token, 8080);
dht::key_desc_t ann_desc[] = {
{"y", lazy_entry::string_t, 1, 0},
@ -458,7 +562,7 @@ int test_main()
for (int i = 0; i < 100; ++i)
{
source = udp::endpoint(rand_v4(), 6000);
send_dht_msg(node, "get_peers", source, &response, "10", "01010101010101010101");
send_dht_request(node, "get_peers", source, &response, "10", "01010101010101010101");
ret = dht::verify_message(&response, peer1_desc, parsed, 4, error_string, sizeof(error_string));
if (ret)
@ -473,14 +577,14 @@ int test_main()
fprintf(stderr, " invalid get_peers response: %s\n", error_string);
}
response.clear();
send_dht_msg(node, "announce_peer", source, &response, "10", "01010101010101010101"
send_dht_request(node, "announce_peer", source, &response, "10", "01010101010101010101"
, "test", token, 8080, 0, 0, false, i >= 50);
response.clear();
}
// ====== get_peers ======
send_dht_msg(node, "get_peers", source, &response, "10", "01010101010101010101"
send_dht_request(node, "get_peers", source, &response, "10", "01010101010101010101"
, 0, no, 0, 0, 0, true);
dht::key_desc_t peer2_desc[] = {
@ -524,7 +628,7 @@ int test_main()
// http://libtorrent.org/dht_sec.html
source = udp::endpoint(address::from_string("124.31.75.21"), 20);
node_id nid = to_hash("1712f6c70c5d6a4ec8a88e4c6ab4c28b95eee401");
send_dht_msg(node, "find_node", source, &response, "10", 0, 0, std::string()
send_dht_request(node, "find_node", source, &response, "10", 0, 0, std::string()
, 0, "0101010101010101010101010101010101010101", 0, false, false, std::string(), std::string(), -1, 0, &nid);
dht::key_desc_t nodes_desc[] = {
@ -549,7 +653,7 @@ int test_main()
// verify that we reject invalid node IDs
// this is now an invalid node-id for 'source'
nid[0] = 0x18;
send_dht_msg(node, "find_node", source, &response, "10", 0, 0, std::string()
send_dht_request(node, "find_node", source, &response, "10", 0, 0, std::string()
, 0, "0101010101010101010101010101010101010101", 0, false, false, std::string(), std::string(), -1, 0, &nid);
ret = dht::verify_message(&response, err_desc, parsed, 2, error_string, sizeof(error_string));
@ -648,18 +752,18 @@ int test_main()
fprintf(stderr, "generating ed25519 keys\n");
unsigned char seed[32];
ed25519_create_seed(seed);
unsigned char private_key[64];
unsigned char public_key[32];
char private_key[item_sk_len];
char public_key[item_pk_len];
ed25519_create_keypair(public_key, private_key, seed);
ed25519_create_keypair((unsigned char*)public_key, (unsigned char*)private_key, seed);
fprintf(stderr, "pub: %s priv: %s\n"
, to_hex(std::string((char*)public_key, 32)).c_str()
, to_hex(std::string((char*)private_key, 64)).c_str());
, to_hex(std::string(public_key, item_pk_len)).c_str()
, to_hex(std::string(private_key, item_sk_len)).c_str());
TEST_CHECK(ret);
send_dht_msg(node, "get", source, &response, "10", 0
, 0, no, 0, (char*)&hasher((char*)public_key, 32).final()[0]
send_dht_request(node, "get", source, &response, "10", 0
, 0, no, 0, (char*)&hasher(public_key, item_pk_len).final()[0]
, 0, false, false, std::string(), std::string(), 64);
key_desc_t desc[] =
@ -686,22 +790,20 @@ int test_main()
TEST_ERROR(error_string);
}
unsigned char signature[64];
char signature[item_sig_len];
char buffer[1200];
int seq = 4;
int pos = snprintf(buffer, sizeof(buffer), "3:seqi%de1:v", seq);
char* ptr = buffer + pos;
pos += bencode(ptr, items[0].ent);
ed25519_sign(signature, (unsigned char*)buffer, pos, public_key, private_key);
TEST_EQUAL(ed25519_verify(signature, (unsigned char*)buffer, pos, public_key), 1);
std::pair<const char*, int> itemv(buffer, bencode(buffer, items[0].ent));
sign_mutable_item(itemv, seq, public_key, private_key, signature);
TEST_EQUAL(verify_mutable_item(itemv, seq, public_key, signature), true);
#ifdef TORRENT_USE_VALGRIND
VALGRIND_CHECK_MEM_IS_DEFINED(signature, 64);
VALGRIND_CHECK_MEM_IS_DEFINED(signature, item_sig_len);
#endif
send_dht_msg(node, "put", source, &response, "10", 0
send_dht_request(node, "put", source, &response, "10", 0
, 0, token, 0, 0, &items[0].ent, false, false
, std::string((char*)public_key, 32)
, std::string((char*)signature, 64), seq);
, std::string(public_key, item_pk_len)
, std::string(signature, item_sig_len), seq);
key_desc_t desc2[] =
{
@ -722,8 +824,8 @@ int test_main()
TEST_ERROR(error_string);
}
send_dht_msg(node, "get", source, &response, "10", 0
, 0, no, 0, (char*)&hasher((char*)public_key, 32).final()[0]
send_dht_request(node, "get", source, &response, "10", 0
, 0, no, 0, (char*)&hasher(public_key, item_pk_len).final()[0]
, 0, false, false, std::string(), std::string(), 64);
key_desc_t desc3[] =
@ -759,23 +861,21 @@ int test_main()
// also test that invalid signatures fail!
pos = snprintf(buffer, sizeof(buffer), "3:seqi%de1:v", seq);
ptr = buffer + pos;
pos += bencode(ptr, items[0].ent);
ed25519_sign(signature, (unsigned char*)buffer, pos, public_key, private_key);
TEST_EQUAL(ed25519_verify(signature, (unsigned char*)buffer, pos, public_key), 1);
itemv.second = bencode(buffer, items[0].ent);
sign_mutable_item(itemv, seq, public_key, private_key, signature);
TEST_EQUAL(verify_mutable_item(itemv, seq, public_key, signature), 1);
#ifdef TORRENT_USE_VALGRIND
VALGRIND_CHECK_MEM_IS_DEFINED(signature, 64);
VALGRIND_CHECK_MEM_IS_DEFINED(signature, item_sig_len);
#endif
// break the signature
signature[2] ^= 0xaa;
TEST_CHECK(ed25519_verify(signature, (unsigned char*)buffer, pos, public_key) != 1);
TEST_CHECK(verify_mutable_item(itemv, seq, public_key, signature) != 1);
send_dht_msg(node, "put", source, &response, "10", 0
send_dht_request(node, "put", source, &response, "10", 0
, 0, token, 0, 0, &items[0].ent, false, false
, std::string((char*)public_key, 32)
, std::string((char*)signature, 64), seq);
, std::string(public_key, item_pk_len)
, std::string(signature, item_sig_len), seq);
key_desc_t desc_error[] =
{
@ -801,23 +901,21 @@ int test_main()
// === test CAS put ===
// this is the hash that we expect to be there
sha1_hash cas = hasher(buffer, pos).final();
sha1_hash cas = mutable_item_cas(itemv, seq);
// increment sequence number
++seq;
pos = snprintf(buffer, sizeof(buffer), "3:seqi%de1:v", seq);
ptr = buffer + pos;
// put item 1
pos += bencode(ptr, items[1].ent);
ed25519_sign(signature, (unsigned char*)buffer, pos, public_key, private_key);
TEST_EQUAL(ed25519_verify(signature, (unsigned char*)buffer, pos, public_key), 1);
itemv.second = bencode(buffer, items[1].ent);
sign_mutable_item(itemv, seq, public_key, private_key, signature);
TEST_EQUAL(verify_mutable_item(itemv, seq, public_key, signature), 1);
#ifdef TORRENT_USE_VALGRIND
VALGRIND_CHECK_MEM_IS_DEFINED(signature, 64);
VALGRIND_CHECK_MEM_IS_DEFINED(signature, item_sig_len);
#endif
send_dht_msg(node, "put", source, &response, "10", 0
send_dht_request(node, "put", source, &response, "10", 0
, 0, token, 0, 0, &items[1].ent, false, false
, std::string((char*)public_key, 32)
, std::string((char*)signature, 64), seq
, std::string(public_key, item_pk_len)
, std::string(signature, item_sig_len), seq
, (char const*)&cas[0]);
ret = verify_message(&response, desc2, parsed, 1, error_string, sizeof(error_string));
@ -837,10 +935,10 @@ int test_main()
// put the same message again. This should fail because the
// CAS hash is outdated, it's not the hash of the value that's
// stored anymore
send_dht_msg(node, "put", source, &response, "10", 0
send_dht_request(node, "put", source, &response, "10", 0
, 0, token, 0, 0, &items[1].ent, false, false
, std::string((char*)public_key, 32)
, std::string((char*)signature, 64), seq
, std::string(public_key, item_pk_len)
, std::string(signature, item_sig_len), seq
, (char const*)&cas[0]);
ret = verify_message(&response, desc_error, parsed, 2, error_string, sizeof(error_string));
@ -1231,6 +1329,471 @@ int test_main()
, rs[i], to_hex(id.to_string()).c_str());
}
}
// test traversal algorithms
dht::key_desc_t ping_desc[] = {
{"y", lazy_entry::string_t, 1, 0},
{"t", lazy_entry::string_t, 2, 0},
{"q", lazy_entry::string_t, 4, 0},
{"a", lazy_entry::dict_t, 0, key_desc_t::parse_children},
{"id", lazy_entry::string_t, 20, key_desc_t::last_child},
};
dht::key_desc_t find_node_desc[] = {
{"y", lazy_entry::string_t, 1, 0},
{"t", lazy_entry::string_t, 2, 0},
{"q", lazy_entry::string_t, 9, 0},
{"a", lazy_entry::dict_t, 0, key_desc_t::parse_children},
{"id", lazy_entry::string_t, 20, 0},
{"target", lazy_entry::string_t, 20, key_desc_t::last_child},
};
dht::key_desc_t get_peers_desc[] = {
{"y", lazy_entry::string_t, 1, 0},
{"t", lazy_entry::string_t, 2, 0},
{"q", lazy_entry::string_t, 9, 0},
{"a", lazy_entry::dict_t, 0, key_desc_t::parse_children},
{"id", lazy_entry::string_t, 20, 0},
{"info_hash", lazy_entry::string_t, 20, key_desc_t::last_child},
};
dht::key_desc_t get_item_desc[] = {
{"y", lazy_entry::string_t, 1, 0},
{"t", lazy_entry::string_t, 2, 0},
{"q", lazy_entry::string_t, 3, 0},
{"a", lazy_entry::dict_t, 0, key_desc_t::parse_children},
{"id", lazy_entry::string_t, 20, 0},
{"target", lazy_entry::string_t, 20, key_desc_t::last_child},
};
// bootstrap
do
{
dht::node_impl node(&ad, &s, sett, node_id::min(), ext, 0);
udp::endpoint initial_node(address_v4::from_string("4.4.4.4"), 1234);
std::vector<udp::endpoint> nodesv;
nodesv.push_back(initial_node);
node.bootstrap(nodesv, boost::bind(&nop));
TEST_EQUAL(g_sent_packets.size(), 1);
if (g_sent_packets.empty()) break;
TEST_EQUAL(g_sent_packets.front().first, initial_node);
lazy_from_entry(g_sent_packets.front().second, response);
ret = verify_message(&response, find_node_desc, parsed, 6, error_string, sizeof(error_string));
if (ret)
{
TEST_EQUAL(parsed[0]->string_value(), "q");
TEST_EQUAL(parsed[2]->string_value(), "find_node");
if (parsed[0]->string_value() != "q" || parsed[2]->string_value() != "find_node") break;
}
else
{
fprintf(stderr, " invalid find_node request: %s\n", print_entry(response).c_str());
TEST_ERROR(error_string);
break;
}
udp::endpoint found_node(address_v4::from_string("5.5.5.5"), 2235);
nodes_t nodes;
nodes.push_back(found_node);
g_sent_packets.clear();
send_dht_response(node, response, initial_node, nodes);
TEST_EQUAL(g_sent_packets.size(), 1);
if (g_sent_packets.empty()) break;
TEST_EQUAL(g_sent_packets.front().first, found_node);
lazy_from_entry(g_sent_packets.front().second, response);
ret = verify_message(&response, find_node_desc, parsed, 6, error_string, sizeof(error_string));
if (ret)
{
TEST_EQUAL(parsed[0]->string_value(), "q");
TEST_EQUAL(parsed[2]->string_value(), "find_node");
if (parsed[0]->string_value() != "q" || parsed[2]->string_value() != "find_node") break;
}
else
{
fprintf(stderr, " invalid find_node request: %s\n", print_entry(response).c_str());
TEST_ERROR(error_string);
break;
}
g_sent_packets.clear();
send_dht_response(node, response, found_node);
TEST_CHECK(g_sent_packets.empty());
TEST_EQUAL(node.num_global_nodes(), 3);
} while (false);
// get_peers
do
{
dht::node_id target = to_hash("1234876923549721020394873245098347598635");
dht::node_impl node(&ad, &s, sett, node_id::min(), ext, 0);
udp::endpoint initial_node(address_v4::from_string("4.4.4.4"), 1234);
node.m_table.add_node(initial_node);
node.announce(target, 1234, false, get_peers_cb);
TEST_EQUAL(g_sent_packets.size(), 1);
if (g_sent_packets.empty()) break;
TEST_EQUAL(g_sent_packets.front().first, initial_node);
lazy_from_entry(g_sent_packets.front().second, response);
ret = verify_message(&response, get_peers_desc, parsed, 6, error_string, sizeof(error_string));
if (ret)
{
TEST_EQUAL(parsed[0]->string_value(), "q");
TEST_EQUAL(parsed[2]->string_value(), "get_peers");
TEST_EQUAL(parsed[5]->string_value(), target.to_string());
if (parsed[0]->string_value() != "q" || parsed[2]->string_value() != "get_peers") break;
}
else
{
fprintf(stderr, " invalid get_peers request: %s\n", print_entry(response).c_str());
TEST_ERROR(error_string);
break;
}
std::set<tcp::endpoint> peers[2];
peers[0].insert(tcp::endpoint(address_v4::from_string("4.1.1.1"), 4111));
peers[0].insert(tcp::endpoint(address_v4::from_string("4.1.1.2"), 4112));
peers[0].insert(tcp::endpoint(address_v4::from_string("4.1.1.3"), 4113));
udp::endpoint next_node(address_v4::from_string("5.5.5.5"), 2235);
nodes_t nodes;
nodes.push_back(next_node);
g_sent_packets.clear();
send_dht_response(node, response, initial_node, nodes, "10", 1234, peers[0]);
TEST_EQUAL(g_sent_packets.size(), 1);
if (g_sent_packets.empty()) break;
TEST_EQUAL(g_sent_packets.front().first, next_node);
lazy_from_entry(g_sent_packets.front().second, response);
ret = verify_message(&response, get_peers_desc, parsed, 6, error_string, sizeof(error_string));
if (ret)
{
TEST_EQUAL(parsed[0]->string_value(), "q");
TEST_EQUAL(parsed[2]->string_value(), "get_peers");
TEST_EQUAL(parsed[5]->string_value(), target.to_string());
if (parsed[0]->string_value() != "q" || parsed[2]->string_value() != "get_peers") break;
}
else
{
fprintf(stderr, " invalid get_peers request: %s\n", print_entry(response).c_str());
TEST_ERROR(error_string);
break;
}
peers[1].insert(tcp::endpoint(address_v4::from_string("4.1.1.4"), 4114));
peers[1].insert(tcp::endpoint(address_v4::from_string("4.1.1.5"), 4115));
peers[1].insert(tcp::endpoint(address_v4::from_string("4.1.1.6"), 4116));
g_sent_packets.clear();
send_dht_response(node, response, next_node, nodes_t(), "11", 1234, peers[1]);
TEST_CHECK(g_sent_packets.empty());
for (int i = 0; i < 2; ++i)
for (std::set<tcp::endpoint>::iterator peer = peers[i].begin(); peer != peers[i].end(); ++peer)
{
TEST_CHECK(std::find(g_got_peers.begin(), g_got_peers.end(), *peer) != g_got_peers.end());
}
g_got_peers.clear();
} while (false);
// immutable get
do
{
dht::node_impl node(&ad, &s, sett, node_id::min(), ext, 0);
udp::endpoint initial_node(address_v4::from_string("4.4.4.4"), 1234);
node.m_table.add_node(initial_node);
node.get_item(items[0].target, get_item_cb);
TEST_EQUAL(g_sent_packets.size(), 1);
if (g_sent_packets.empty()) break;
TEST_EQUAL(g_sent_packets.front().first, initial_node);
lazy_from_entry(g_sent_packets.front().second, response);
ret = verify_message(&response, get_item_desc, parsed, 6, error_string, sizeof(error_string));
if (ret)
{
TEST_EQUAL(parsed[0]->string_value(), "q");
TEST_EQUAL(parsed[2]->string_value(), "get");
TEST_EQUAL(parsed[5]->string_value(), items[0].target.to_string());
if (parsed[0]->string_value() != "q" || parsed[2]->string_value() != "get") break;
}
else
{
fprintf(stderr, " invalid get request: %s\n", print_entry(response).c_str());
TEST_ERROR(error_string);
break;
}
g_sent_packets.clear();
send_dht_response(node, response, initial_node, nodes_t(), "10", 1234, std::set<tcp::endpoint>()
, NULL, &items[0].ent);
TEST_CHECK(g_sent_packets.empty());
TEST_EQUAL(g_got_items.size(), 1);
if (g_got_items.empty()) break;
TEST_EQUAL(g_got_items.front().value(), items[0].ent);
g_got_items.clear();
} while (false);
// mutable get
do
{
dht::node_impl node(&ad, &s, sett, node_id::min(), ext, 0);
udp::endpoint initial_node(address_v4::from_string("4.4.4.4"), 1234);
node.m_table.add_node(initial_node);
sha1_hash target = hasher(public_key, item_pk_len).final();
node.get_item(target, get_item_cb);
TEST_EQUAL(g_sent_packets.size(), 1);
if (g_sent_packets.empty()) break;
TEST_EQUAL(g_sent_packets.front().first, initial_node);
lazy_from_entry(g_sent_packets.front().second, response);
ret = verify_message(&response, get_item_desc, parsed, 6, error_string, sizeof(error_string));
if (ret)
{
TEST_EQUAL(parsed[0]->string_value(), "q");
TEST_EQUAL(parsed[2]->string_value(), "get");
TEST_EQUAL(parsed[5]->string_value(), target.to_string());
if (parsed[0]->string_value() != "q" || parsed[2]->string_value() != "get") break;
}
else
{
fprintf(stderr, " invalid get request: %s\n", print_entry(response).c_str());
TEST_ERROR(error_string);
break;
}
g_sent_packets.clear();
itemv.second = bencode(buffer, items[0].ent);
sign_mutable_item(itemv, seq, public_key, private_key, signature);
send_dht_response(node, response, initial_node, nodes_t(), "10", 1234, std::set<tcp::endpoint>()
, NULL, &items[0].ent, std::string(public_key, item_pk_len), std::string(signature, item_sig_len), seq);
TEST_CHECK(g_sent_packets.empty());
TEST_EQUAL(g_got_items.size(), 1);
if (g_got_items.empty()) break;
TEST_EQUAL(g_got_items.front().value(), items[0].ent);
TEST_CHECK(memcmp(g_got_items.front().pk(), public_key, item_pk_len) == 0);
TEST_CHECK(memcmp(g_got_items.front().sig(), signature, item_sig_len) == 0);
TEST_EQUAL(g_got_items.front().seq(), seq);
g_got_items.clear();
} while (false);
dht::key_desc_t put_immutable_item_desc[] = {
{"y", lazy_entry::string_t, 1, 0},
{"t", lazy_entry::string_t, 2, 0},
{"q", lazy_entry::string_t, 3, 0},
{"a", lazy_entry::dict_t, 0, key_desc_t::parse_children},
{"id", lazy_entry::string_t, 20, 0},
{"token", lazy_entry::string_t, 2, 0},
{"v", lazy_entry::none_t, 0, key_desc_t::last_child},
};
dht::key_desc_t put_mutable_item_desc[] = {
{"y", lazy_entry::string_t, 1, 0},
{"t", lazy_entry::string_t, 2, 0},
{"q", lazy_entry::string_t, 3, 0},
{"a", lazy_entry::dict_t, 0, key_desc_t::parse_children},
{"id", lazy_entry::string_t, 20, 0},
{"cas", lazy_entry::string_t, 20, key_desc_t::optional},
{"k", lazy_entry::string_t, item_pk_len, 0},
{"seq", lazy_entry::int_t, 0, 0},
{"sig", lazy_entry::string_t, item_sig_len, 0},
{"token", lazy_entry::string_t, 2, 0},
{"v", lazy_entry::none_t, 0, key_desc_t::last_child},
};
// immutable put
do
{
dht::node_impl node(&ad, &s, sett, node_id::min(), ext, 0);
enum { num_test_nodes = 2 };
node_entry nodes[num_test_nodes] =
{ node_entry(generate_next(), udp::endpoint(address_v4::from_string("4.4.4.4"), 1234))
, node_entry(generate_next(), udp::endpoint(address_v4::from_string("5.5.5.5"), 1235)) };
for (int i = 0; i < num_test_nodes; ++i)
node.m_table.add_node(nodes[i]);
g_put_item.assign(items[0].ent);
node.get_item(items[0].target, get_item_cb);
TEST_EQUAL(g_sent_packets.size(), num_test_nodes);
if (g_sent_packets.size() != num_test_nodes) break;
for (int i = 0; i < num_test_nodes; ++i)
{
std::list<std::pair<udp::endpoint, entry> >::iterator packet = find_packet(nodes[i].ep());
TEST_CHECK(packet != g_sent_packets.end());
if (packet == g_sent_packets.end()) continue;
lazy_from_entry(packet->second, response);
ret = verify_message(&response, get_item_desc, parsed, 6, error_string, sizeof(error_string));
if (!ret)
{
fprintf(stderr, " invalid get request: %s\n", print_entry(response).c_str());
TEST_ERROR(error_string);
continue;
}
char t[10];
snprintf(t, sizeof(t), "%02d", i);
send_dht_response(node, response, nodes[i].ep(), nodes_t(), t, 1234,
std::set<tcp::endpoint>(), 0, 0, std::string(), std::string(), -1, &nodes[i].id);
g_sent_packets.erase(packet);
}
TEST_EQUAL(g_put_count, 1);
TEST_EQUAL(g_sent_packets.size(), num_test_nodes);
if (g_sent_packets.size() != num_test_nodes) break;
itemv.second = bencode(buffer, items[0].ent);
for (int i = 0; i < num_test_nodes; ++i)
{
std::list<std::pair<udp::endpoint, entry> >::iterator packet = find_packet(nodes[i].ep());
TEST_CHECK(packet != g_sent_packets.end());
if (packet == g_sent_packets.end()) continue;
lazy_from_entry(packet->second, response);
ret = verify_message(&response, put_immutable_item_desc, parsed, 7, error_string, sizeof(error_string));
if (ret)
{
TEST_EQUAL(parsed[0]->string_value(), "q");
TEST_EQUAL(parsed[2]->string_value(), "put");
std::pair<const char*, int> v = parsed[6]->data_section();
TEST_EQUAL(v.second, itemv.second);
TEST_CHECK(memcmp(v.first, itemv.first, itemv.second) == 0);
char t[10];
snprintf(t, sizeof(t), "%02d", i);
TEST_EQUAL(parsed[5]->string_value(), t);
if (parsed[0]->string_value() != "q" || parsed[2]->string_value() != "put") continue;
}
else
{
fprintf(stderr, " invalid immutable put request: %s\n", print_entry(response).c_str());
TEST_ERROR(error_string);
continue;
}
}
g_sent_packets.clear();
g_put_item.clear();
g_put_count = 0;
} while (false);
// mutable put
do
{
dht::node_impl node(&ad, &s, sett, node_id::min(), ext, 0);
enum { num_test_nodes = 2 };
node_entry nodes[num_test_nodes] =
{ node_entry(generate_next(), udp::endpoint(address_v4::from_string("4.4.4.4"), 1234))
, node_entry(generate_next(), udp::endpoint(address_v4::from_string("5.5.5.5"), 1235)) };
for (int i = 0; i < num_test_nodes; ++i)
node.m_table.add_node(nodes[i]);
sha1_hash target = hasher(public_key, item_pk_len).final();
g_put_item.assign(items[0].ent, seq, public_key, private_key);
std::string sig(g_put_item.sig(), item_sig_len);
node.get_item(target, get_item_cb);
TEST_EQUAL(g_sent_packets.size(), num_test_nodes);
if (g_sent_packets.size() != num_test_nodes) break;
for (int i = 0; i < num_test_nodes; ++i)
{
std::list<std::pair<udp::endpoint, entry> >::iterator packet = find_packet(nodes[i].ep());
TEST_CHECK(packet != g_sent_packets.end());
if (packet == g_sent_packets.end()) continue;
lazy_from_entry(packet->second, response);
ret = verify_message(&response, get_item_desc, parsed, 6, error_string, sizeof(error_string));
if (!ret)
{
fprintf(stderr, " invalid mutable put request: %s\n", print_entry(response).c_str());
TEST_ERROR(error_string);
continue;
}
char t[10];
snprintf(t, sizeof(t), "%02d", i);
send_dht_response(node, response, nodes[i].ep(), nodes_t(), t, 1234,
std::set<tcp::endpoint>(), 0, 0, std::string(), std::string(), -1, &nodes[i].id);
g_sent_packets.erase(packet);
}
TEST_EQUAL(g_put_count, 1);
TEST_EQUAL(g_sent_packets.size(), num_test_nodes);
if (g_sent_packets.size() != num_test_nodes) break;
itemv.second = bencode(buffer, items[0].ent);
for (int i = 0; i < num_test_nodes; ++i)
{
std::list<std::pair<udp::endpoint, entry> >::iterator packet = find_packet(nodes[i].ep());
TEST_CHECK(packet != g_sent_packets.end());
if (packet == g_sent_packets.end()) continue;
lazy_from_entry(packet->second, response);
ret = verify_message(&response, put_mutable_item_desc, parsed, 11, error_string, sizeof(error_string));
if (ret)
{
TEST_EQUAL(parsed[0]->string_value(), "q");
TEST_EQUAL(parsed[2]->string_value(), "put");
TEST_EQUAL(parsed[6]->string_value(), std::string(public_key, item_pk_len));
TEST_EQUAL(parsed[7]->int_value(), seq);
TEST_EQUAL(parsed[8]->string_value(), sig);
std::pair<const char*, int> v = parsed[10]->data_section();
TEST_EQUAL(v.second, itemv.second);
TEST_CHECK(memcmp(v.first, itemv.first, itemv.second) == 0);
char t[10];
snprintf(t, sizeof(t), "%02d", i);
TEST_EQUAL(parsed[9]->string_value(), t);
if (parsed[0]->string_value() != "q" || parsed[2]->string_value() != "put") continue;
}
else
{
fprintf(stderr, " invalid put request: %s\n", print_entry(response).c_str());
TEST_ERROR(error_string);
continue;
}
}
g_sent_packets.clear();
g_put_item.clear();
g_put_count = 0;
} while (false);
return 0;
}