From 86c704a6fffaa778e6c8a73183ead4134a6a621f Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Fri, 28 Feb 2014 04:02:48 +0000 Subject: [PATCH] fix dht_bootstrap_alert being posted. add additional alert for dht put completion. add utility to test immutable put/get. fix issue in DHT preventing stores on router nodes (even when they return write tokens). immutable put and get confirmed to be working --- ChangeLog | 3 +- include/libtorrent/alert_types.hpp | 76 ++++++-- include/libtorrent/aux_/session_impl.hpp | 2 +- include/libtorrent/entry.hpp | 66 +++---- include/libtorrent/kademlia/dht_tracker.hpp | 3 +- include/libtorrent/kademlia/find_data.hpp | 5 +- src/alert.cpp | 11 ++ src/entry.cpp | 56 +++++- src/kademlia/dht_tracker.cpp | 5 +- src/kademlia/find_data.cpp | 39 +++- src/kademlia/get_item.cpp | 4 +- src/kademlia/refresh.cpp | 2 +- src/kademlia/traversal_algorithm.cpp | 28 ++- src/session.cpp | 2 +- src/session_impl.cpp | 31 +++- tools/Jamfile | 1 + tools/dht_put.cpp | 189 ++++++++++++++++++++ 17 files changed, 438 insertions(+), 85 deletions(-) create mode 100644 tools/dht_put.cpp diff --git a/ChangeLog b/ChangeLog index 28436214e..8d526e1d3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,7 +7,7 @@ * allow force_announce to only affect a single tracker * add moving_storage field to torrent_status * expose UPnP and NAT-PMP mapping in session object - * DHT refactoring and support for storing arbitrary data with put + * DHT refactoring and support for storing arbitrary data with put and get * 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 @@ -41,6 +41,7 @@ * fix uTP edge case where udp socket buffer fills up * fix nagle implementation in uTP + * fix dht_bootstrap_alert being posted * SetFileValidData fix on windows (prevents zero-fill) * fix minor lock_files issue on unix diff --git a/include/libtorrent/alert_types.hpp b/include/libtorrent/alert_types.hpp index d8882d762..ed8adf60a 100644 --- a/include/libtorrent/alert_types.hpp +++ b/include/libtorrent/alert_types.hpp @@ -1520,8 +1520,8 @@ namespace libtorrent int reason; }; - // This alert is generated when a DHT node announces to an info-hash on our DHT node. It belongs - // to the ``dht_notification`` category. + // This alert is generated when a DHT node announces to an info-hash on our + // DHT node. It belongs to the ``dht_notification`` category. struct TORRENT_EXPORT dht_announce_alert: alert { // internal @@ -1542,8 +1542,8 @@ namespace libtorrent sha1_hash info_hash; }; - // This alert is generated when a DHT node sends a ``get_peers`` message to our DHT node. - // It belongs to the ``dht_notification`` category. + // This alert is generated when a DHT node sends a ``get_peers`` message to + // our DHT node. It belongs to the ``dht_notification`` category. struct TORRENT_EXPORT dht_get_peers_alert: alert { // internal @@ -1590,21 +1590,22 @@ namespace libtorrent num_channels }; - // an array of samples. The enum describes what each - // sample is a measurement of. All of these are raw, and not smoothing is performed. + // an array of samples. The enum describes what each sample is a + // measurement of. All of these are raw, and not smoothing is performed. int transferred[num_channels]; - // the number of milliseconds during which these stats - // were collected. This is typically just above 1000, but if CPU is - // limited, it may be higher than that. + // the number of milliseconds during which these stats were collected. + // This is typically just above 1000, but if CPU is limited, it may be + // higher than that. int interval; }; - // This alert is posted when the disk cache has been flushed for a specific torrent - // as a result of a call to torrent_handle::flush_cache(). This alert belongs to the - // ``storage_notification`` category, which must be enabled to let this alert through. - // The alert is also posted when removing a torrent from the session, once the outstanding - // cache flush is complete and the torrent does no longer have any files open. + // This alert is posted when the disk cache has been flushed for a specific + // torrent as a result of a call to torrent_handle::flush_cache(). This + // alert belongs to the ``storage_notification`` category, which must be + // enabled to let this alert through. The alert is also posted when removing + // a torrent from the session, once the outstanding cache flush is complete + // and the torrent does no longer have any files open. struct TORRENT_EXPORT cache_flushed_alert: torrent_alert { // internal @@ -1647,8 +1648,8 @@ namespace libtorrent std::string str; }; - // This alert is generated when we receive a local service discovery message from a peer - // for a torrent we're currently participating in. + // This alert is generated when we receive a local service discovery message + // from a peer for a torrent we're currently participating in. struct TORRENT_EXPORT lsd_peer_alert: peer_alert { // internal @@ -1663,9 +1664,9 @@ namespace libtorrent virtual std::string message() const; }; - // This alert is posted whenever a tracker responds with a ``trackerid``. The tracker ID - // is like a cookie. The libtorrent will store the tracker ID for this tracker and - // repeat it in subsequent announces. + // This alert is posted whenever a tracker responds with a ``trackerid``. + // The tracker ID is like a cookie. The libtorrent will store the tracker ID + // for this tracker and repeat it in subsequent announces. struct TORRENT_EXPORT trackerid_alert: tracker_alert { // internal @@ -2003,6 +2004,43 @@ namespace libtorrent // the data for this item entry item; }; + + // this is posted when a DHT put operation completes. This is useful if the + // client is waiting for a put to complete before shutting down for instance. + struct TORRENT_EXPORT dht_put_alert: alert + { + // internal + dht_put_alert(sha1_hash const& t) + : target(t) + , seq(0) + {} + dht_put_alert(boost::array key + , boost::array sig + , std::string s + , uint64_t sequence_number) + : target(0) + , public_key(key) + , signature(sig) + , salt(s) + , seq(sequence_number) + {} + + TORRENT_DEFINE_ALERT(dht_put_alert); + + const static int static_category = alert::dht_notification; + virtual std::string message() const; + + // the target hash the item was stored under if this was an *immutable* + // item. + sha1_hash target; + + // if a mutable item was stored, these are the public key, signature, + // salt and sequence number the item was stored under. + boost::array public_key; + boost::array signature; + std::string salt; + boost::uint64_t seq; + }; #undef TORRENT_DEFINE_ALERT } diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index 9f8eeab51..59bb06717 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -323,7 +323,7 @@ namespace libtorrent void dht_get_mutable_item(boost::array key , std::string salt = std::string()); - void dht_put_item(entry data); + void dht_put_item(entry data, sha1_hash target); void dht_put_mutable_item(boost::array key , boost::function& diff --git a/include/libtorrent/entry.hpp b/include/libtorrent/entry.hpp index 61fd0abc4..720f87927 100644 --- a/include/libtorrent/entry.hpp +++ b/include/libtorrent/entry.hpp @@ -149,16 +149,18 @@ namespace libtorrent void operator=(integer_type const&); // The ``integer()``, ``string()``, ``list()`` and ``dict()`` functions - // are accessors that return the respective type. If the ``entry`` object isn't of the - // type you request, the accessor will throw libtorrent_exception (which derives from - // ``std::runtime_error``). You can ask an ``entry`` for its type through the - // ``type()`` function. + // are accessors that return the respective type. If the ``entry`` object + // isn't of the type you request, the accessor will throw + // libtorrent_exception (which derives from ``std::runtime_error``). You + // can ask an ``entry`` for its type through the ``type()`` function. // - // If you want to create an ``entry`` you give it the type you want it to have in its - // constructor, and then use one of the non-const accessors to get a reference which you then - // can assign the value you want it to have. + // If you want to create an ``entry`` you give it the type you want it to + // have in its constructor, and then use one of the non-const accessors + // to get a reference which you then can assign the value you want it to + // have. // - // The typical code to get info from a torrent file will then look like this:: + // The typical code to get info from a torrent file will then look like + // this:: // // entry torrent_file; // // ... @@ -187,8 +189,8 @@ namespace libtorrent // } // // - // To make it easier to extract information from a torrent file, the class torrent_info - // exists. + // To make it easier to extract information from a torrent file, the + // class torrent_info exists. integer_type& integer(); const integer_type& integer() const; string_type& string(); @@ -201,16 +203,17 @@ namespace libtorrent // swaps the content of *this* with ``e``. void swap(entry& e); - // All of these functions requires the entry to be a dictionary, if it isn't they - // will throw ``libtorrent::type_error``. + // All of these functions requires the entry to be a dictionary, if it + // isn't they will throw ``libtorrent::type_error``. // - // The non-const versions of the ``operator[]`` will return a reference to either - // the existing element at the given key or, if there is no element with the - // given key, a reference to a newly inserted element at that key. + // The non-const versions of the ``operator[]`` will return a reference + // to either the existing element at the given key or, if there is no + // element with the given key, a reference to a newly inserted element at + // that key. // // The const version of ``operator[]`` will only return a reference to an - // existing element at the given key. If the key is not found, it will throw - // ``libtorrent::type_error``. + // existing element at the given key. If the key is not found, it will + // throw ``libtorrent::type_error``. entry& operator[](char const* key); entry& operator[](std::string const& key); #ifndef BOOST_NO_EXCEPTIONS @@ -218,12 +221,12 @@ namespace libtorrent const entry& operator[](std::string const& key) const; #endif - // These functions requires the entry to be a dictionary, if it isn't they - // will throw ``libtorrent::type_error``. + // These functions requires the entry to be a dictionary, if it isn't + // they will throw ``libtorrent::type_error``. // - // They will look for an element at the given key in the dictionary, if the - // element cannot be found, they will return 0. If an element with the given - // key is found, the return a pointer to it. + // They will look for an element at the given key in the dictionary, if + // the element cannot be found, they will return 0. If an element with + // the given key is found, the return a pointer to it. entry* find_key(char const* key); entry const* find_key(char const* key) const; entry* find_key(std::string const& key); @@ -264,20 +267,17 @@ namespace libtorrent integer_type data[(union_size + sizeof(integer_type) - 1) / sizeof(integer_type)]; - // the bitfield is used so that the m_type_queried - // field still fits, so that the ABI is the same for - // debug builds and release builds. It appears to be - // very hard to match debug builds with debug versions - // of libtorrent + // the bitfield is used so that the m_type_queried field still fits, so + // that the ABI is the same for debug builds and release builds. It + // appears to be very hard to match debug builds with debug versions of + // libtorrent boost::uint8_t m_type:7; public: - // in debug mode this is set to false by bdecode - // to indicate that the program has not yet queried - // the type of this entry, and sould not assume - // that it has a certain type. This is asserted in - // the accessor functions. This does not apply if - // exceptions are used. + // in debug mode this is set to false by bdecode to indicate that the + // program has not yet queried the type of this entry, and sould not + // assume that it has a certain type. This is asserted in the accessor + // functions. This does not apply if exceptions are used. mutable boost::uint8_t m_type_queried:1; }; diff --git a/include/libtorrent/kademlia/dht_tracker.hpp b/include/libtorrent/kademlia/dht_tracker.hpp index ca837e52e..691169b51 100644 --- a/include/libtorrent/kademlia/dht_tracker.hpp +++ b/include/libtorrent/kademlia/dht_tracker.hpp @@ -80,7 +80,8 @@ namespace libtorrent { namespace dht , dht_settings const& settings, entry const* state = 0); virtual ~dht_tracker(); - void start(entry const& bootstrap); + void start(entry const& bootstrap + , find_data::nodes_callback const& f); void stop(); void add_node(udp::endpoint node); diff --git a/include/libtorrent/kademlia/find_data.hpp b/include/libtorrent/kademlia/find_data.hpp index bc4c8c0e6..45a7ad307 100644 --- a/include/libtorrent/kademlia/find_data.hpp +++ b/include/libtorrent/kademlia/find_data.hpp @@ -61,12 +61,11 @@ struct find_data : traversal_algorithm { typedef boost::function > const&)> nodes_callback; - 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 , nodes_callback const& ncallback); + void got_write_token(node_id const& n, std::string const& write_token); + virtual void start(); virtual char const* name() const; diff --git a/src/alert.cpp b/src/alert.cpp index 841f456be..180166330 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -599,6 +599,17 @@ namespace libtorrent { return msg; } + std::string dht_put_alert::message() const + { + char msg[1050]; + snprintf(msg, sizeof(msg), "DHT put complete (key=%s sig=%s salt=%s seq=%" PRId64 ")" + , to_hex(std::string(&public_key[0], 32)).c_str() + , to_hex(std::string(&signature[0], 64)).c_str() + , salt.c_str() + , seq); + return msg; + } + } // namespace libtorrent diff --git a/src/entry.cpp b/src/entry.cpp index 8236c0cf6..5ea17347a 100644 --- a/src/entry.cpp +++ b/src/entry.cpp @@ -491,8 +491,60 @@ namespace libtorrent void entry::swap(entry& e) { - // not implemented - TORRENT_ASSERT(false); + bool clear_this = false; + bool clear_that = false; + + if (m_type == undefined_t && e.m_type == undefined_t) + return; + + if (m_type == undefined_t) + { + construct((data_type)e.m_type); + clear_that = true; + } + + if (e.m_type == undefined_t) + { + e.construct((data_type)m_type); + clear_this = true; + } + + if (m_type == e.m_type) + { + switch (m_type) + { + case int_t: + std::swap(*reinterpret_cast(data) + , *reinterpret_cast(e.data)); + break; + case string_t: + std::swap(*reinterpret_cast(data) + , *reinterpret_cast(e.data)); + break; + case list_t: + std::swap(*reinterpret_cast(data) + , *reinterpret_cast(e.data)); + break; + case dictionary_t: + std::swap(*reinterpret_cast(data) + , *reinterpret_cast(e.data)); + break; + default: + break; + } + + if (clear_this) + destruct(); + + if (clear_that) + e.destruct(); + } + else + { + // currently, only swapping entries of the same type or where one + // of the entries is uninitialized is supported. + TORRENT_ASSERT(false && "not implemented"); + } } std::string entry::to_string() const diff --git a/src/kademlia/dht_tracker.cpp b/src/kademlia/dht_tracker.cpp index 27e701a00..5ff8ae8c3 100644 --- a/src/kademlia/dht_tracker.cpp +++ b/src/kademlia/dht_tracker.cpp @@ -246,7 +246,8 @@ namespace libtorrent { namespace dht // defined in node.cpp extern void nop(); - void dht_tracker::start(entry const& bootstrap) + void dht_tracker::start(entry const& bootstrap + , find_data::nodes_callback const& f) { std::vector initial_nodes; @@ -268,7 +269,7 @@ namespace libtorrent { namespace dht m_refresh_timer.expires_from_now(seconds(5), ec); m_refresh_timer.async_wait(boost::bind(&dht_tracker::refresh_timeout, self(), _1)); - m_dht.bootstrap(initial_nodes, boost::bind(&libtorrent::dht::nop)); + m_dht.bootstrap(initial_nodes, f); } void dht_tracker::stop() diff --git a/src/kademlia/find_data.cpp b/src/kademlia/find_data.cpp index 9b2d00aa4..39814f55a 100644 --- a/src/kademlia/find_data.cpp +++ b/src/kademlia/find_data.cpp @@ -80,6 +80,10 @@ void find_data_observer::reply(msg const& m) node_id(id->string_ptr()), token->string_value()); } + // in case we didn't know the id of this peer when we sent the message to + // it. For instance if it's a bootstrap node. + set_id(node_id(id->string_ptr())); + traversal_observer::reply(m); done(); } @@ -110,6 +114,15 @@ void find_data::start() traversal_algorithm::start(); } +void find_data::got_write_token(node_id const& n, std::string const& write_token) +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] adding write " + "token '" << to_hex(write_token) << "' under id '" << to_hex(n.to_string()) << "'"; +#endif + m_write_tokens[n] = write_token; +} + observer_ptr find_data::new_observer(void* ptr , udp::endpoint const& ep, node_id const& id) { @@ -129,7 +142,7 @@ void find_data::done() m_done = true; #ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(traversal) << "[" << this << "] find_data DONE"; + TORRENT_LOG(traversal) << "[" << this << "] " << name() << " DONE"; #endif std::vector > results; @@ -138,13 +151,31 @@ void find_data::done() , end(m_results.end()); i != end && num_results > 0; ++i) { observer_ptr const& o = *i; - if (o->flags & observer::flag_no_id) continue; - if ((o->flags & observer::flag_queried) == 0) continue; + if ((o->flags & observer::flag_alive) == 0) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] not alive: " + << o->target_ep(); +#endif + continue; + } std::map::iterator j = m_write_tokens.find(o->id()); - if (j == m_write_tokens.end()) continue; + if (j == m_write_tokens.end()) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] no write token: " + << o->target_ep(); +#endif + continue; + } results.push_back(std::make_pair(node_entry(o->id(), o->target_ep()), j->second)); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] " + << o->target_ep(); +#endif --num_results; } + if (m_nodes_callback) m_nodes_callback(results); traversal_algorithm::done(); diff --git a/src/kademlia/get_item.cpp b/src/kademlia/get_item.cpp index 26db9af00..156de931b 100644 --- a/src/kademlia/get_item.cpp +++ b/src/kademlia/get_item.cpp @@ -177,8 +177,8 @@ void get_item::done() void get_item::put(std::vector > 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) + TORRENT_LOG(node) << "sending put [ v: \"" << m_data.value() + << "\" seq: " << (m_data.is_mutable() ? m_data.seq() : -1) << " nodes: " << v.size() << " ]" ; #endif diff --git a/src/kademlia/refresh.cpp b/src/kademlia/refresh.cpp index 65930e578..c036d3452 100644 --- a/src/kademlia/refresh.cpp +++ b/src/kademlia/refresh.cpp @@ -94,7 +94,7 @@ char const* bootstrap::name() const { return "bootstrap"; } void bootstrap::done() { #ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(traversal) << " [" << this << "]" + TORRENT_LOG(traversal) << "[" << this << "]" << " bootstrap done, pinging remaining nodes"; #endif diff --git a/src/kademlia/traversal_algorithm.cpp b/src/kademlia/traversal_algorithm.cpp index 9a783d0e4..b80364a5d 100644 --- a/src/kademlia/traversal_algorithm.cpp +++ b/src/kademlia/traversal_algorithm.cpp @@ -79,7 +79,9 @@ traversal_algorithm::traversal_algorithm( { #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(traversal) << "[" << this << "] NEW" - " target: " << target << " k: " << m_node.m_table.bucket_size(); + " target: " << target + << " k: " << m_node.m_table.bucket_size() + ; #endif } @@ -151,7 +153,9 @@ void traversal_algorithm::add_entry(node_id const& id, udp::endpoint addr, unsig << " address: " << o->target_addr() << " existing node: " << (*j)->id() << " " << (*j)->target_addr() - << " distance: " << distance_exp(m_target, o->id()); + << " distance: " << distance_exp(m_target, o->id()) + << " type: " << name() + ; #endif return; } @@ -163,7 +167,9 @@ void traversal_algorithm::add_entry(node_id const& id, udp::endpoint addr, unsig TORRENT_LOG(traversal) << "[" << this << "] ADD id: " << id << " address: " << addr << " distance: " << distance_exp(m_target, id) - << " invoke-count: " << m_invoke_count; + << " invoke-count: " << m_invoke_count + << " type: " << name() + ; #endif i = m_results.insert(i, o); } @@ -268,7 +274,9 @@ void traversal_algorithm::failed(observer_ptr o, int flags) << " distance: " << distance_exp(m_target, o->id()) << " addr: " << o->target_ep() << " branch-factor: " << m_branch_factor - << " invoke-count: " << m_invoke_count; + << " invoke-count: " << m_invoke_count + << " type: " << name() + ; #endif } else @@ -285,7 +293,9 @@ void traversal_algorithm::failed(observer_ptr o, int flags) << " distance: " << distance_exp(m_target, o->id()) << " addr: " << o->target_ep() << " branch-factor: " << m_branch_factor - << " invoke-count: " << m_invoke_count; + << " invoke-count: " << m_invoke_count + << " type: " << name() + ; #endif // don't tell the routing table about // node ids that we just generated ourself @@ -322,7 +332,8 @@ void traversal_algorithm::done() << results_target << " id: " << o->id() << " distance: " << distance_exp(m_target, o->id()) - << " address: " << o->target_ep(); + << " address: " << o->target_ep() + ; --results_target; int dist = distance_exp(m_target, o->id()); if (dist < closest_target) closest_target = dist; @@ -330,7 +341,9 @@ void traversal_algorithm::done() } TORRENT_LOG(traversal) << "[" << this << "] COMPLETED " - << "distance: " << closest_target; + << "distance: " << closest_target + << " type: " << name() + ; #endif // delete all our references to the observer objects so @@ -393,6 +406,7 @@ bool traversal_algorithm::add_requests() << " invoke-count: " << m_invoke_count << " branch-factor: " << m_branch_factor << " distance: " << distance_exp(m_target, (*i)->id()) + << " type: " << name() ; #endif diff --git a/src/session.cpp b/src/session.cpp index 737b2a875..94893891e 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -902,7 +902,7 @@ namespace libtorrent sha1_hash ret = hasher(&buf[0], buf.size()).final(); #ifndef TORRENT_DISABLE_DHT - TORRENT_ASYNC_CALL1(dht_put_item, data); + TORRENT_ASYNC_CALL2(dht_put_item, data, ret); #endif return ret; } diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 7aa5c8f2c..c7a3d3409 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -5693,6 +5693,12 @@ retry: void session_impl::start_dht() { start_dht(m_dht_state); } + void on_bootstrap(alert_manager& alerts) + { + if (alerts.should_post()) + alerts.post_alert(dht_bootstrap_alert()); + } + void session_impl::start_dht(entry const& startup_state) { INVARIANT_CHECK; @@ -5706,7 +5712,7 @@ retry: m_dht->add_router_node(*i); } - m_dht->start(startup_state); + m_dht->start(startup_state, boost::bind(&on_bootstrap, boost::ref(m_alerts))); m_udp_socket.subscribe(m_dht.get()); } @@ -5806,15 +5812,20 @@ retry: , this, _1), salt); } - void nop() {} - - void session_impl::dht_put_item(entry data) + void on_dht_put(alert_manager& alerts, sha1_hash target) { - if (!m_dht) return; - m_dht->put_item(data, boost::bind(&nop)); + if (alerts.should_post()) + alerts.post_alert(dht_put_alert(target)); } - void put_mutable_callback(dht::item& i + void session_impl::dht_put_item(entry data, sha1_hash target) + { + if (!m_dht) return; + m_dht->put_item(data, boost::bind(&on_dht_put, boost::ref(m_alerts) + , target)); + } + + void put_mutable_callback(alert_manager& alerts, dht::item& i , boost::function& , boost::uint64_t&, std::string const&)> cb) { @@ -5825,6 +5836,9 @@ retry: std::string salt = i.salt(); cb(value, sig, seq, salt); i.assign(value, salt, seq, pk.data(), sig.data()); + + if (alerts.should_post()) + alerts.post_alert(dht_put_alert(pk, sig, salt, seq)); } void session_impl::dht_put_mutable_item(boost::array key @@ -5833,7 +5847,8 @@ retry: , std::string salt) { if (!m_dht) return; - m_dht->put_item(key.data(), boost::bind(&put_mutable_callback, _1, cb), salt); + m_dht->put_item(key.data(), boost::bind(&put_mutable_callback + , boost::ref(m_alerts), _1, cb), salt); } #endif diff --git a/tools/Jamfile b/tools/Jamfile index 40ec9dbd2..bf9407f56 100644 --- a/tools/Jamfile +++ b/tools/Jamfile @@ -18,4 +18,5 @@ project tools exe parse_hash_fails : parse_hash_fails.cpp ; exe parse_request_log : parse_request_log.cpp ; +exe dht : dht_put.cpp ; diff --git a/tools/dht_put.cpp b/tools/dht_put.cpp new file mode 100644 index 000000000..fad8ebce6 --- /dev/null +++ b/tools/dht_put.cpp @@ -0,0 +1,189 @@ +/* + +Copyright (c) 2014, Arvid Norberg +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/session.hpp" +#include "libtorrent/escape_string.hpp" // for from_hex +#include "libtorrent/alert_types.hpp" +#include "libtorrent/bencode.hpp" // for bencode() + +#include + +using namespace libtorrent; + +void usage() +{ + fprintf(stderr, + "USAGE:\ndht \n\nCOMMANDS:\n" + "get - retrieves and prints out the immutable\n" + " item stored under hash.\n" + "put - puts the specified string as an immutable\n" + " item onto the DHT. The resulting target hash\n" + ); + exit(1); +} + +std::auto_ptr wait_for_alert(session& s, int alert_type) +{ + std::auto_ptr ret; + bool found = false; + while (!found) + { + s.wait_for_alert(seconds(5)); + + std::deque alerts; + s.pop_alerts(&alerts); + for (std::deque::iterator i = alerts.begin() + , end(alerts.end()); i != end; ++i) + { + if ((*i)->type() != alert_type) + { + static int spinner = 0; + static const char anim[] = {'-', '\\', '|', '/'}; + printf("\r%c", anim[spinner]); + fflush(stdout); + spinner = (spinner + 1) & 3; + //print some alerts? + delete *i; + continue; + } + ret = std::auto_ptr(*i); + found = true; + } + } + printf("\n"); + return ret; +} + +int main(int argc, char* argv[]) +{ + session s; + s.set_alert_mask(0xffffffff); + + s.add_dht_router(std::pair("router.utorrent.com", 6881)); + + FILE* f = fopen(".dht", "rb"); + if (f != NULL) + { + fseek(f, 0, SEEK_END); + int size = ftell(f); + fseek(f, 0, SEEK_SET); + if (size > 0) + { + std::vector state; + state.resize(size); + fread(&state[0], 1, state.size(), f); + + lazy_entry e; + error_code ec; + lazy_bdecode(&state[0], &state[0] + state.size(), e, ec); + if (ec) + fprintf(stderr, "failed to parse .dht file: (%d) %s\n" + , ec.value(), ec.message().c_str()); + else + s.load_state(e); + } + fclose(f); + } + + printf("bootstrapping\n"); + wait_for_alert(s, dht_bootstrap_alert::alert_type); + + // skip pointer to self + ++argv; + --argc; + + if (argc < 1) usage(); + + if (strcmp(argv[0], "get") == 0) + { + ++argv; + --argc; + + if (argc < 1) usage(); + + if (strlen(argv[0]) != 40) + { + fprintf(stderr, "the hash is expected to be 40 hex characters\n"); + usage(); + } + sha1_hash target; + from_hex(argv[0], 40, (char*)&target[0]); + + s.dht_get_item(target); + + printf("GET %s\n", to_hex(target.to_string()).c_str()); + + std::auto_ptr a = wait_for_alert(s, dht_immutable_item_alert::alert_type); + + dht_immutable_item_alert* item = alert_cast(a.get()); + entry data; + if (item) + data.swap(item->item); + + printf("%s", data.to_string().c_str()); + } + else if (strcmp(argv[0], "put") == 0) + { + ++argv; + --argc; + + if (argc < 1) usage(); + + entry data; + data = std::string(argv[0]); + + sha1_hash target = s.dht_put_item(data); + + printf("PUT %s\n", to_hex(target.to_string()).c_str()); + + wait_for_alert(s, dht_put_alert::alert_type); + } + else + { + usage(); + } + + entry e; + s.save_state(e, session::save_dht_state); + std::vector state; + bencode(std::back_inserter(state), e); + f = fopen(".dht", "wb+"); + if (f == NULL) + { + fprintf(stderr, "failed to open file .dht for writing"); + return 1; + } + fwrite(&state[0], 1, state.size(), f); + fclose(f); +} +