forked from premiere/premiere-libtorrent
DHT refactoring and support for storing arbitrary data with put
This commit is contained in:
parent
c9bfa1279e
commit
d6b1aa4c36
|
@ -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
18
Jamfile
|
@ -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 =
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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()))
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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) ;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue