diff options
Diffstat (limited to 'libtorrent-extended')
-rw-r--r-- | libtorrent-extended/PKGBUILD | 54 | ||||
-rw-r--r-- | libtorrent-extended/bad_peer_handling.patch | 67 | ||||
-rw-r--r-- | libtorrent-extended/dht_pex_static_map.patch | 2353 | ||||
-rw-r--r-- | libtorrent-extended/ipv6.patch | 1235 | ||||
-rw-r--r-- | libtorrent-extended/magnet_uri.patch | 1652 | ||||
-rw-r--r-- | libtorrent-extended/object_sstr.patch | 233 |
6 files changed, 0 insertions, 5594 deletions
diff --git a/libtorrent-extended/PKGBUILD b/libtorrent-extended/PKGBUILD deleted file mode 100644 index eef3cf9..0000000 --- a/libtorrent-extended/PKGBUILD +++ /dev/null @@ -1,54 +0,0 @@ -# Maintainer: Lucky <aur.archlinux.org [at] lucky.take0ver [dot] net> -# Contributor: Daenyth <Daenyth+Arch [at] gmail [dot] com> -# Contributor: Jeff Mickey <jeff@archlinux.org> -# Contributor: sh__ - -pkgname=libtorrent-extended -_pkgname=libtorrent -pkgver=0.12.6 -pkgrel=3 -pkgdesc="BitTorrent library written in C++ with magnet link, IPv6 and bad peer patch" -url="http://libtorrent.rakshasa.no" -arch=('i686' 'x86_64') -license=('GPL') -depends=('libsigc++2.0' 'openssl') -conflicts=('libtorrent') -provides=('libtorrent') -options=('!libtool') -source=(http://libtorrent.rakshasa.no/downloads/${_pkgname}-${pkgver}.tar.gz - # support for magnet links - # http://libtorrent.rakshasa.no/ticket/955 (deleted) - # http://libtorrent.rakshasa.no/ticket/2100 - dht_pex_static_map.patch - magnet_uri.patch - object_sstr.patch - # support for IPv6 - # http://libtorrent.rakshasa.no/ticket/1111 - ipv6.patch - # support for bad peers (kick/ban) - # http://ovh.ttdpatch.net/~jdrexler/rt/experimental/bad_peer_handling.diff - # https://calomel.org/rtorrent_mods.html - bad_peer_handling.patch) -md5sums=('037499ed708aaf72988cee60e5a8d96b' - 'c8c538e26e77a0cf5951c47b86252922' - 'b1d9701ca0b8c26291c467e20d6e3eba' - '0eb6044530c1bb5bb21d5d539481c23d' - '90c5e5a374ee29737a0ad0a93c912e6a' - 'b01ebff539102c07a0192e179c3170b1') - -build() { - cd "${srcdir}/${_pkgname}-${pkgver}" - - patch -p1 < ${srcdir}/dht_pex_static_map.patch - patch -p1 < ${srcdir}/magnet_uri.patch - patch -p1 < ${srcdir}/object_sstr.patch - patch -p1 < ${srcdir}/ipv6.patch - patch -p1 < ${srcdir}/bad_peer_handling.patch - - ./autogen.sh - CXXFLAGS="${CXXFLAGS} -fno-strict-aliasing" \ - ./configure --prefix=/usr --enable-ipv6 --disable-debug || return 1 - make || return 1 - make DESTDIR="${pkgdir}" install -} -# vim:set ts=2 sw=2 et: diff --git a/libtorrent-extended/bad_peer_handling.patch b/libtorrent-extended/bad_peer_handling.patch deleted file mode 100644 index 26d9515..0000000 --- a/libtorrent-extended/bad_peer_handling.patch +++ /dev/null @@ -1,67 +0,0 @@ -diff --git a/src/download/choke_manager.cc b/src/download/choke_manager.cc -index 4915a96..efda46c 100644 ---- a/src/download/choke_manager.cc -+++ b/src/download/choke_manager.cc -@@ -193,7 +193,8 @@ ChokeManager::set_snubbed(PeerConnectionBase* pc, ChokeManagerNode* base) { - choke_manager_erase(&m_queued, pc); - } - -- base->set_queued(false); -+ //breaks unsnubbing, ticket #989: -+ //base->set_queued(false); - } - - void -diff --git a/src/torrent/peer/peer.cc b/src/torrent/peer/peer.cc -index 3f29f82..a15a7b2 100644 ---- a/src/torrent/peer/peer.cc -+++ b/src/torrent/peer/peer.cc -@@ -63,7 +63,8 @@ bool Peer::is_down_interested() const { return c_ptr()->is_up_interested(); - - bool Peer::is_snubbed() const { return c_ptr()->is_up_snubbed(); } - void Peer::set_snubbed(bool v) { m_ptr()->set_upload_snubbed(v); } --void Peer::set_banned() { m_peerInfo->set_failed_counter(64); } -+void Peer::set_banned() { m_peerInfo->set_banned(); } -+void Peer::set_unbanned() { m_peerInfo->set_unbanned(); } - - const Rate* Peer::down_rate() const { return c_ptr()->c_peer_chunks()->download_throttle()->rate(); } - const Rate* Peer::up_rate() const { return c_ptr()->c_peer_chunks()->upload_throttle()->rate(); } -diff --git a/src/torrent/peer/peer.h b/src/torrent/peer/peer.h -index 70169ad..6d9b4f9 100644 ---- a/src/torrent/peer/peer.h -+++ b/src/torrent/peer/peer.h -@@ -69,7 +69,10 @@ public: - - bool is_snubbed() const; - void set_snubbed(bool v); -+ -+ bool is_banned() const { return peer_info()->is_banned(); } - void set_banned(); -+ void set_unbanned(); - - const HashString& id() const { return peer_info()->id(); } - const char* options() const { return peer_info()->options(); } -diff --git a/src/torrent/peer/peer_info.h b/src/torrent/peer/peer_info.h -index fe80027..9b17e61 100644 ---- a/src/torrent/peer/peer_info.h -+++ b/src/torrent/peer/peer_info.h -@@ -51,6 +51,8 @@ public: - friend class PeerList; - friend class ProtocolExtension; - -+ static const unsigned int banned_mask = 4096; -+ - static const int flag_connected = (1 << 0); - static const int flag_incoming = (1 << 1); - static const int flag_handshake = (1 << 2); -@@ -79,6 +81,10 @@ public: - uint32_t failed_counter() const { return m_failedCounter; } - void set_failed_counter(uint32_t c) { m_failedCounter = c; } - -+ bool is_banned() const { return m_failedCounter & banned_mask; } -+ void set_banned() { m_failedCounter |= banned_mask; } -+ void set_unbanned() { m_failedCounter &= ~banned_mask; } -+ - uint32_t transfer_counter() const { return m_transferCounter; } - void set_transfer_counter(uint32_t c) { m_transferCounter = c; } - diff --git a/libtorrent-extended/dht_pex_static_map.patch b/libtorrent-extended/dht_pex_static_map.patch deleted file mode 100644 index 1e767e0..0000000 --- a/libtorrent-extended/dht_pex_static_map.patch +++ /dev/null @@ -1,2353 +0,0 @@ -diff --git a/src/dht/dht_bucket.cc b/src/dht/dht_bucket.cc -index 04aa475..819f4aa 100644 ---- a/src/dht/dht_bucket.cc -+++ b/src/dht/dht_bucket.cc -@@ -52,6 +52,8 @@ DhtBucket::DhtBucket(const HashString& begin, const HashString& end) : - m_good(0), - m_bad(0), - -+ m_fullCacheLength(0), -+ - m_begin(begin), - m_end(end) { - -@@ -67,6 +69,8 @@ DhtBucket::add_node(DhtNode* n) { - m_good++; - else if (n->is_bad()) - m_bad++; -+ -+ m_fullCacheLength = 0; - } - - void -@@ -81,6 +85,8 @@ DhtBucket::remove_node(DhtNode* n) { - m_good--; - else if (n->is_bad()) - m_bad--; -+ -+ m_fullCacheLength = 0; - } - - void -@@ -92,9 +98,11 @@ DhtBucket::count() { - // Called every 15 minutes for housekeeping. - void - DhtBucket::update() { -- // For now we only update the counts after some nodes have become bad -- // due to prolonged inactivity. - count(); -+ -+ // In case adjacent buckets whose nodes we borrowed have changed, -+ // we force an update of the cache. -+ m_fullCacheLength = 0; - } - - DhtBucket::iterator -@@ -188,4 +196,23 @@ DhtBucket::split(const HashString& id) { - return other; - } - -+void -+DhtBucket::build_full_cache() { -+ DhtBucketChain chain(this); -+ -+ char* pos = m_fullCache; -+ do { -+ for (const_iterator itr = chain.bucket()->begin(); itr != chain.bucket()->end() && pos < m_fullCache + sizeof(m_fullCache); ++itr) { -+ if (!(*itr)->is_bad()) { -+ pos = (*itr)->store_compact(pos); -+ -+ if (pos > m_fullCache + sizeof(m_fullCache)) -+ throw internal_error("DhtRouter::store_closest_nodes wrote past buffer end."); -+ } -+ } -+ } while (pos < m_fullCache + sizeof(m_fullCache) && chain.next() != NULL); -+ -+ m_fullCacheLength = pos - m_fullCache; -+} -+ - } -diff --git a/src/dht/dht_bucket.h b/src/dht/dht_bucket.h -index 97622a3..bcdfd67 100644 ---- a/src/dht/dht_bucket.h -+++ b/src/dht/dht_bucket.h -@@ -111,6 +111,10 @@ public: - DhtBucket* parent() const { return m_parent; } - DhtBucket* child() const { return m_child; } - -+ // Return a full bucket's worth of compact node data. If this bucket is not -+ // full, it uses nodes from the child/parent buckets until we have enough. -+ SimpleString full_bucket(); -+ - // Called by the DhtNode on its bucket to update good/bad node counts. - void node_now_good(bool was_bad); - void node_now_bad(bool was_good); -@@ -118,6 +122,8 @@ public: - private: - void count(); - -+ void build_full_cache(); -+ - DhtBucket* m_parent; - DhtBucket* m_child; - -@@ -126,11 +132,15 @@ private: - unsigned int m_good; - unsigned int m_bad; - -+ size_t m_fullCacheLength; -+ - // These are 40 bytes together, so might as well put them last. - // m_end is const because it is used as key for the DhtRouter routing table - // map, which would be inconsistent if m_end were changed carelessly. - HashString m_begin; - const HashString m_end; -+ -+ char m_fullCache[num_nodes * 26]; - }; - - // Helper class to recursively follow a chain of buckets. It first recurses -@@ -160,6 +170,13 @@ DhtBucket::node_now_bad(bool was_good) { - m_bad++; - } - -+inline SimpleString -+DhtBucket::full_bucket() { -+ if (!m_fullCacheLength) -+ build_full_cache(); -+ return SimpleString(m_fullCache, m_fullCacheLength); -+} -+ - inline const DhtBucket* - DhtBucketChain::next() { - // m_restart is clear when we're done recursing into the children and -diff --git a/src/dht/dht_router.cc b/src/dht/dht_router.cc -index b1c95c3..ff38b8c 100644 ---- a/src/dht/dht_router.cc -+++ b/src/dht/dht_router.cc -@@ -329,24 +329,6 @@ DhtRouter::node_invalid(const HashString& id) { - delete_node(m_nodes.find(&node->id())); - } - --char* --DhtRouter::store_closest_nodes(const HashString& id, char* buffer, char* bufferEnd) { -- DhtBucketChain chain(find_bucket(id)->second); -- -- do { -- for (DhtBucket::const_iterator itr = chain.bucket()->begin(); itr != chain.bucket()->end() && buffer != bufferEnd; ++itr) { -- if (!(*itr)->is_bad()) { -- buffer = (*itr)->store_compact(buffer); -- -- if (buffer > bufferEnd) -- throw internal_error("DhtRouter::store_closest_nodes wrote past buffer end."); -- } -- } -- } while (buffer != bufferEnd && chain.next() != NULL); -- -- return buffer; --} -- - Object* - DhtRouter::store_cache(Object* container) const { - container->insert_key("self_id", str()); -@@ -470,7 +452,7 @@ DhtRouter::receive_timeout() { - for (DhtBucketList::const_iterator itr = m_routingTable.begin(); itr != m_routingTable.end(); ++itr) { - itr->second->update(); - -- if (!itr->second->is_full() || itr->second->age() > timeout_bucket_bootstrap) -+ if (!itr->second->is_full() || itr->second == bucket() || itr->second->age() > timeout_bucket_bootstrap) - bootstrap_bucket(itr->second); - } - -@@ -505,15 +487,13 @@ DhtRouter::generate_token(const rak::socket_address* sa, int token, char buffer[ - return buffer; - } - --std::string --DhtRouter::make_token(const rak::socket_address* sa) { -- char token[20]; -- -- return std::string(generate_token(sa, m_curToken, token), size_token); -+SimpleString -+DhtRouter::make_token(const rak::socket_address* sa, char* buffer) { -+ return SimpleString(generate_token(sa, m_curToken, buffer), size_token); - } - - bool --DhtRouter::token_valid(const std::string& token, const rak::socket_address* sa) { -+DhtRouter::token_valid(SimpleString token, const rak::socket_address* sa) { - if (token.length() != size_token) - return false; - -@@ -521,12 +501,12 @@ DhtRouter::token_valid(const std::string& token, const rak::socket_address* sa) - char reference[20]; - - // First try current token. -- if (std::memcmp(generate_token(sa, m_curToken, reference), token.c_str(), size_token) == 0) -+ if (token == SimpleString(generate_token(sa, m_curToken, reference), size_token)) - return true; - - // If token recently changed, some clients may be using the older one. - // That way a token is valid for 15-30 minutes, instead of 0-15. -- return std::memcmp(generate_token(sa, m_prevToken, reference), token.c_str(), size_token) == 0; -+ return token == SimpleString(generate_token(sa, m_prevToken, reference), size_token); - } - - DhtNode* -diff --git a/src/dht/dht_router.h b/src/dht/dht_router.h -index f2b673f..816747f 100644 ---- a/src/dht/dht_router.h -+++ b/src/dht/dht_router.h -@@ -115,14 +115,14 @@ public: - - // Store compact node information (26 bytes) for nodes closest to the - // given ID in the given buffer, return new buffer end. -- char* store_closest_nodes(const HashString& id, char* buffer, char* bufferEnd); -+ SimpleString get_closest_nodes(const HashString& id) { return find_bucket(id)->second->full_bucket(); } - - // Store DHT cache in the given container. - Object* store_cache(Object* container) const; - - // Create and verify a token. Tokens are valid between 15-30 minutes from creation. -- std::string make_token(const rak::socket_address* sa); -- bool token_valid(const std::string& token, const rak::socket_address* sa); -+ SimpleString make_token(const rak::socket_address* sa, char* buffer); -+ bool token_valid(SimpleString token, const rak::socket_address* sa); - - DhtManager::statistics_type get_statistics() const; - void reset_statistics() { m_server.reset_statistics(); } -@@ -147,6 +147,8 @@ private: - bool add_node_to_bucket(DhtNode* node); - void delete_node(const DhtNodeList::accessor& itr); - -+ void store_closest_nodes(const HashString& id, DhtBucket* bucket); -+ - DhtBucketList::iterator split_bucket(const DhtBucketList::iterator& itr, DhtNode* node); - - void bootstrap(); -diff --git a/src/dht/dht_server.cc b/src/dht/dht_server.cc -index 1f2234b..256b92b 100644 ---- a/src/dht/dht_server.cc -+++ b/src/dht/dht_server.cc -@@ -38,13 +38,14 @@ - #include "globals.h" - - #include <algorithm> --#include <sstream> -+#include <cstdio> - - #include "torrent/exceptions.h" - #include "torrent/connection_manager.h" - #include "torrent/object.h" - #include "torrent/object_stream.h" - #include "torrent/poll.h" -+#include "torrent/static_map.h" - #include "torrent/throttle.h" - #include "tracker/tracker_dht.h" - -@@ -63,6 +64,34 @@ const char* DhtServer::queries[] = { - "announce_peer", - }; - -+// List of all possible keys we need/support in a DHT message. -+// Unsupported keys we receive are dropped (ignored) while decoding. -+// See torrent/static_map.h for how this works. -+DhtMessage::mapping_type dht_key_names[DhtMessage::length] = { -+ { key_a_id, "a::id" }, -+ { key_a_infoHash, "a::info_hash" }, -+ { key_a_port, "a::port", }, -+ { key_a_target, "a::target" }, -+ { key_a_token, "a::token" }, -+ -+ { key_e_0, "e[0]" }, -+ { key_e_1, "e[1]" }, -+ -+ { key_q, "q" }, -+ -+ { key_r_id, "r::id" }, -+ { key_r_nodes, "r::nodes" }, -+ { key_r_token, "r::token" }, -+ { key_r_values, "r::values[]" }, -+ -+ { key_t, "t::" }, -+ { key_v, "v" }, -+ { key_y, "y" }, -+}; -+ -+template<> -+const DhtMessage::key_map_init DhtMessage::base_type::keyMap(dht_key_names); -+ - // Error in DHT protocol, avoids std::string ctor from communication_error - class dht_error : public network_error { - public: -@@ -238,54 +267,51 @@ DhtServer::update() { - } - - void --DhtServer::process_query(const Object& transactionId, const HashString& id, const rak::socket_address* sa, Object& request) { -+DhtServer::process_query(const HashString& id, const rak::socket_address* sa, const DhtMessage& msg) { - m_queriesReceived++; - m_networkUp = true; - -- std::string& query = request.get_key_string("q"); -- -- Object& arg = request.get_key("a"); -+ SimpleString query = msg[key_q].as_sstring(); - - // Construct reply. -- Object reply = Object::create_map(); -+ DhtMessage reply; - - if (query == "find_node") -- create_find_node_response(arg, reply); -+ create_find_node_response(msg, reply); - - else if (query == "get_peers") -- create_get_peers_response(arg, sa, reply); -+ create_get_peers_response(msg, sa, reply); - - else if (query == "announce_peer") -- create_announce_peer_response(arg, sa, reply); -+ create_announce_peer_response(msg, sa, reply); - - else if (query != "ping") - throw dht_error(dht_error_bad_method, "Unknown query type."); - - m_router->node_queried(id, sa); -- create_response(transactionId, sa, reply); -+ create_response(msg, sa, reply); - } - - void --DhtServer::create_find_node_response(const Object& arg, Object& reply) { -- const std::string& target = arg.get_key_string("target"); -+DhtServer::create_find_node_response(const DhtMessage& req, DhtMessage& reply) { -+ SimpleString target = req[key_a_target].as_sstring(); - - if (target.length() < HashString::size_data) - throw dht_error(dht_error_protocol, "target string too short"); - -- char compact[sizeof(compact_node_info) * DhtBucket::num_nodes]; -- char* end = m_router->store_closest_nodes(*HashString::cast_from(target), compact, compact + sizeof(compact)); -- -- if (end == compact) -+ SimpleString nodes = m_router->get_closest_nodes(*HashString::cast_from(target)); -+ if (nodes.empty()) - throw dht_error(dht_error_generic, "No nodes"); - -- reply.insert_key("nodes", std::string(compact, end)); -+ reply[key_r_nodes] = nodes; - } - - void --DhtServer::create_get_peers_response(const Object& arg, const rak::socket_address* sa, Object& reply) { -- reply.insert_key("token", m_router->make_token(sa)); -+DhtServer::create_get_peers_response(const DhtMessage& req, const rak::socket_address* sa, DhtMessage& reply) { -+ reply[key_r_token] = m_router->make_token(sa, reply.data_end); -+ reply.data_end += reply[key_r_token].as_sstring().length(); - -- const std::string& info_hash_str = arg.get_key_string("info_hash"); -+ SimpleString info_hash_str = req[key_a_infoHash].as_sstring(); - - if (info_hash_str.length() < HashString::size_data) - throw dht_error(dht_error_protocol, "info hash too short"); -@@ -296,35 +322,34 @@ DhtServer::create_get_peers_response(const Object& arg, const rak::socket_addres - - // If we're not tracking or have no peers, send closest nodes. - if (!tracker || tracker->empty()) { -- char compact[sizeof(compact_node_info) * DhtBucket::num_nodes]; -- char* end = m_router->store_closest_nodes(*info_hash, compact, compact + sizeof(compact)); -- -- if (end == compact) -+ SimpleString nodes = m_router->get_closest_nodes(*info_hash); -+ if (nodes.empty()) - throw dht_error(dht_error_generic, "No peers nor nodes"); - -- reply.insert_key("nodes", std::string(compact, end)); -+ reply[key_r_nodes] = nodes; - - } else { -- reply.insert_key("values", Object::create_list()).as_list().swap(tracker->get_peers().as_list()); -+ reply[key_r_values] = tracker->get_peers(); - } - } - - void --DhtServer::create_announce_peer_response(const Object& arg, const rak::socket_address* sa, Object& reply) { -- const std::string& info_hash = arg.get_key_string("info_hash"); -+DhtServer::create_announce_peer_response(const DhtMessage& req, const rak::socket_address* sa, DhtMessage& reply) { -+ SimpleString info_hash = req[key_a_infoHash].as_sstring(); - - if (info_hash.length() < HashString::size_data) - throw dht_error(dht_error_protocol, "info hash too short"); - -- if (!m_router->token_valid(arg.get_key_string("token"), sa)) -+ if (!m_router->token_valid(req[key_a_token].as_sstring(), sa)) - throw dht_error(dht_error_protocol, "Token invalid."); - - DhtTracker* tracker = m_router->get_tracker(*HashString::cast_from(info_hash), true); -- tracker->add_peer(sa->sa_inet()->address_n(), arg.get_key_value("port")); -+ tracker->add_peer(sa->sa_inet()->address_n(), req[key_a_port].as_value()); - } - - void --DhtServer::process_response(int transactionId, const HashString& id, const rak::socket_address* sa, Object& request) { -+DhtServer::process_response(const HashString& id, const rak::socket_address* sa, const DhtMessage& response) { -+ int transactionId = (unsigned char)response[key_t].as_sstring()[2]; - transaction_itr itr = m_transactions.find(DhtTransaction::key(sa, transactionId)); - - // Response to a transaction we don't have in our table. At this point it's -@@ -351,11 +376,9 @@ DhtServer::process_response(int transactionId, const HashString& id, const rak:: - if ((id != transaction->id() && transaction->id() != m_router->zero_id)) - return; - -- const Object& response = request.get_key("r"); -- - switch (transaction->type()) { - case DhtTransaction::DHT_FIND_NODE: -- parse_find_node_reply(transaction->as_find_node(), response.get_key_string("nodes")); -+ parse_find_node_reply(transaction->as_find_node(), response[key_r_nodes].as_sstring()); - break; - - case DhtTransaction::DHT_GET_PEERS: -@@ -381,7 +404,8 @@ DhtServer::process_response(int transactionId, const HashString& id, const rak:: - } - - void --DhtServer::process_error(int transactionId, const rak::socket_address* sa, Object& request) { -+DhtServer::process_error(const rak::socket_address* sa, const DhtMessage& error) { -+ int transactionId = (unsigned char)error[key_t].as_sstring()[2]; - transaction_itr itr = m_transactions.find(DhtTransaction::key(sa, transactionId)); - - if (itr == m_transactions.end()) -@@ -399,7 +423,7 @@ DhtServer::process_error(int transactionId, const rak::socket_address* sa, Objec - } - - void --DhtServer::parse_find_node_reply(DhtTransactionSearch* transaction, const std::string& nodes) { -+DhtServer::parse_find_node_reply(DhtTransactionSearch* transaction, SimpleString nodes) { - transaction->complete(true); - - if (sizeof(const compact_node_info) != 26) -@@ -421,16 +445,16 @@ DhtServer::parse_find_node_reply(DhtTransactionSearch* transaction, const std::s - } - - void --DhtServer::parse_get_peers_reply(DhtTransactionGetPeers* transaction, const Object& response) { -+DhtServer::parse_get_peers_reply(DhtTransactionGetPeers* transaction, const DhtMessage& response) { - DhtAnnounce* announce = static_cast<DhtAnnounce*>(transaction->as_search()->search()); - - transaction->complete(true); - -- if (response.has_key_list("values")) -- announce->receive_peers(response.get_key("values")); -+ if (response[key_r_values].is_sstring()) -+ announce->receive_peers(response[key_r_values].as_sstring()); - -- if (response.has_key_string("token")) -- add_transaction(new DhtTransactionAnnouncePeer(transaction->id(), transaction->address(), announce->target(), response.get_key_string("token")), packet_prio_low); -+ if (response[key_r_token].is_sstring()) -+ add_transaction(new DhtTransactionAnnouncePeer(transaction->id(), transaction->address(), announce->target(), response[key_r_token].as_sstring()), packet_prio_low); - - announce->update_status(); - } -@@ -490,17 +514,19 @@ DhtServer::create_query(transaction_itr itr, int tID, const rak::socket_address* - if (itr->second->id() == m_router->id()) - throw internal_error("DhtServer::create_query trying to send to itself."); - -- Object query = Object::create_map(); -+ DhtMessage query; - -- DhtTransaction* transaction = itr->second; -- char trans_id = tID; -- query.insert_key("t", std::string(&trans_id, 1)); -- query.insert_key("y", "q"); -- query.insert_key("q", queries[transaction->type()]); -- query.insert_key("v", PEER_VERSION); -+ // Transaction ID is a bencode string. -+ query[key_t] = SimpleString(query.data_end, 3); -+ *query.data_end++ = '1'; -+ *query.data_end++ = ':'; -+ *query.data_end++ = tID; - -- Object& q = query.insert_key("a", Object::create_map()); -- q.insert_key("id", m_router->str()); -+ DhtTransaction* transaction = itr->second; -+ query[key_y] = SimpleString("q", 1); -+ query[key_q] = SimpleString(queries[transaction->type()]); -+ query[key_v] = SimpleString(PEER_VERSION, 4); -+ query[key_a_id] = m_router->s_str(); - - switch (transaction->type()) { - case DhtTransaction::DHT_PING: -@@ -508,17 +534,17 @@ DhtServer::create_query(transaction_itr itr, int tID, const rak::socket_address* - break; - - case DhtTransaction::DHT_FIND_NODE: -- q.insert_key("target", transaction->as_find_node()->search()->target().str()); -+ query[key_a_target] = transaction->as_find_node()->search()->target().s_str(); - break; - - case DhtTransaction::DHT_GET_PEERS: -- q.insert_key("info_hash", transaction->as_get_peers()->search()->target().str()); -+ query[key_a_infoHash] = transaction->as_get_peers()->search()->target().s_str(); - break; - - case DhtTransaction::DHT_ANNOUNCE_PEER: -- q.insert_key("info_hash", transaction->as_announce_peer()->info_hash().str()); -- q.insert_key("token", transaction->as_announce_peer()->token()); -- q.insert_key("port", manager->connection_manager()->listen_port()); -+ query[key_a_infoHash] = transaction->as_announce_peer()->info_hash().s_str(); -+ query[key_a_token] = transaction->as_announce_peer()->token(); -+ query[key_a_port] = manager->connection_manager()->listen_port(); - break; - } - -@@ -530,31 +556,26 @@ DhtServer::create_query(transaction_itr itr, int tID, const rak::socket_address* - } - - void --DhtServer::create_response(const Object& transactionId, const rak::socket_address* sa, Object& r) { -- Object reply = Object::create_map(); -- r.insert_key("id", m_router->str()); -- -- reply.insert_key("t", transactionId); -- reply.insert_key("y", "r"); -- reply.insert_key("r", r); -- reply.insert_key("v", PEER_VERSION); -+DhtServer::create_response(const DhtMessage& req, const rak::socket_address* sa, DhtMessage& reply) { -+ reply[key_r_id] = m_router->s_str(); -+ reply[key_t] = req[key_t]; -+ reply[key_y] = SimpleString("r", 1); -+ reply[key_v] = SimpleString(PEER_VERSION, 4); - - add_packet(new DhtTransactionPacket(sa, reply), packet_prio_reply); - } - - void --DhtServer::create_error(const Object* transactionId, const rak::socket_address* sa, int num, const std::string& msg) { -- Object error = Object::create_map(); -+DhtServer::create_error(const DhtMessage& req, const rak::socket_address* sa, int num, const char* msg) { -+ DhtMessage error; - -- if (transactionId != NULL) -- error.insert_key("t", *transactionId); -+ if (req[key_t].is_sstring()) -+ error[key_t] = req[key_t]; - -- error.insert_key("y", "e"); -- error.insert_key("v", PEER_VERSION); -- -- Object& e = error.insert_key("e", Object::create_list()); -- e.insert_back(num); -- e.insert_back(msg); -+ error[key_y] = SimpleString("e", 1); -+ error[key_v] = SimpleString(PEER_VERSION, 4); -+ error[key_e_0] = num; -+ error[key_e_1] = SimpleString(msg); - - add_packet(new DhtTransactionPacket(sa, error), packet_prio_reply); - } -@@ -656,15 +677,12 @@ DhtServer::clear_transactions() { - void - DhtServer::event_read() { - uint32_t total = 0; -- std::istringstream sstream; -- -- sstream.imbue(std::locale::classic()); - - while (true) { - Object request; - rak::socket_address sa; - int type = '?'; -- const Object* transactionId = NULL; -+ DhtMessage message; - const HashString* nodeId = NULL; - - try { -@@ -675,31 +693,32 @@ DhtServer::event_read() { - break; - - total += read; -- sstream.str(std::string(buffer, read)); -- -- sstream >> request; - - // If it's not a valid bencode dictionary at all, it's probably not a DHT - // packet at all, so we don't throw an error to prevent bounce loops. -- if (sstream.fail() || !request.is_map()) -+ try { -+ staticMap_read_bencode(buffer, buffer + read, message); -+ } catch (bencode_error& e) { - continue; -+ } - -- if (!request.has_key("t")) -+ if (!message[key_t].is_sstring()) - throw dht_error(dht_error_protocol, "No transaction ID"); - -- transactionId = &request.get_key("t"); -- -- if (!request.has_key_string("y")) -+ if (!message[key_y].is_sstring()) - throw dht_error(dht_error_protocol, "No message type"); - -- if (request.get_key_string("y").length() != 1) -+ if (message[key_y].as_sstring().length() != 1) - throw dht_error(dht_error_bad_method, "Unsupported message type"); - -- type = request.get_key_string("y")[0]; -+ type = message[key_y].as_sstring()[0]; - - // Queries and replies have node ID in different dictionaries. - if (type == 'r' || type == 'q') { -- const std::string& nodeIdStr = request.get_key(type == 'q' ? "a" : "r").get_key_string("id"); -+ if (!message[type == 'q' ? key_a_id : key_r_id].is_sstring()) -+ throw dht_error(dht_error_protocol, "Invalid `id' value"); -+ -+ SimpleString nodeIdStr = message[type == 'q' ? key_a_id : key_r_id].as_sstring(); - - if (nodeIdStr.length() < HashString::size_data) - throw dht_error(dht_error_protocol, "`id' value too short"); -@@ -709,7 +728,8 @@ DhtServer::event_read() { - - // Sanity check the returned transaction ID. - if ((type == 'r' || type == 'e') && -- (!transactionId->is_string() || transactionId->as_string().length() != 1)) -+ (!message[key_t].is_sstring() || message[key_t].as_sstring().length() != 3 -+ || message[key_t].as_sstring()[0] != '1' || message[key_t].as_sstring()[1] != ':')) - throw dht_error(dht_error_protocol, "Invalid transaction ID type/length."); - - // Stupid broken implementations. -@@ -718,15 +738,15 @@ DhtServer::event_read() { - - switch (type) { - case 'q': -- process_query(*transactionId, *nodeId, &sa, request); -+ process_query(*nodeId, &sa, message); - break; - - case 'r': -- process_response(((unsigned char*)transactionId->as_string().c_str())[0], *nodeId, &sa, request); -+ process_response(*nodeId, &sa, message); - break; - - case 'e': -- process_error(((unsigned char*)transactionId->as_string().c_str())[0], &sa, request); -+ process_error(&sa, message); - break; - - default: -@@ -737,16 +757,19 @@ DhtServer::event_read() { - // so that if it repeatedly sends malformed replies we will drop it instead of propagating it - // to other nodes. - } catch (bencode_error& e) { -- if ((type == 'r' || type == 'e') && nodeId != NULL) -+ if ((type == 'r' || type == 'e') && nodeId != NULL) { - m_router->node_inactive(*nodeId, &sa); -- else -- create_error(transactionId, &sa, dht_error_protocol, std::string("Malformed packet: ") + e.what()); -+ } else { -+ snprintf(message.data_end, message.data + message.data_size - message.data_end - 1, "Malformed packet: %s", e.what()); -+ message.data[message.data_size - 1] = 0; -+ create_error(message, &sa, dht_error_protocol, message.data_end); -+ } - - } catch (dht_error& e) { - if ((type == 'r' || type == 'e') && nodeId != NULL) - m_router->node_inactive(*nodeId, &sa); - else -- create_error(transactionId, &sa, e.code(), e.what()); -+ create_error(message, &sa, e.code(), e.what()); - - } catch (network_error& e) { - -diff --git a/src/dht/dht_server.h b/src/dht/dht_server.h -index 1855b73..1f55f15 100644 ---- a/src/dht/dht_server.h -+++ b/src/dht/dht_server.h -@@ -46,6 +46,7 @@ - #include "net/throttle_node.h" - #include "download/download_info.h" // for SocketAddressCompact - #include "torrent/hash_string.h" -+#include "torrent/simple_string.h" - - #include "dht_transaction.h" - -@@ -56,6 +57,7 @@ class DhtNode; - class DhtRouter; - - class DownloadInfo; -+class DhtMessage; - class TrackerDht; - - // UDP server that handles the DHT node communications. -@@ -134,23 +136,23 @@ private: - - void start_write(); - -- void process_query(const Object& transaction, const HashString& id, const rak::socket_address* sa, Object& req); -- void process_response(int transaction, const HashString& id, const rak::socket_address* sa, Object& req); -- void process_error(int transaction, const rak::socket_address* sa, Object& req); -+ void process_query(const HashString& id, const rak::socket_address* sa, const DhtMessage& req); -+ void process_response(const HashString& id, const rak::socket_address* sa, const DhtMessage& req); -+ void process_error(const rak::socket_address* sa, const DhtMessage& error); - -- void parse_find_node_reply(DhtTransactionSearch* t, const std::string& nodes); -- void parse_get_peers_reply(DhtTransactionGetPeers* t, const Object& res); -+ void parse_find_node_reply(DhtTransactionSearch* t, SimpleString res); -+ void parse_get_peers_reply(DhtTransactionGetPeers* t, const DhtMessage& res); - - void find_node_next(DhtTransactionSearch* t); - - void add_packet(DhtTransactionPacket* packet, int priority); - void create_query(transaction_itr itr, int tID, const rak::socket_address* sa, int priority); -- void create_response(const Object& transactionID, const rak::socket_address* sa, Object& r); -- void create_error(const Object* transactionID, const rak::socket_address* sa, int num, const std::string& msg); -+ void create_response(const DhtMessage& req, const rak::socket_address* sa, DhtMessage& reply); -+ void create_error(const DhtMessage& req, const rak::socket_address* sa, int num, const char* msg); - -- void create_find_node_response(const Object& arg, Object& reply); -- void create_get_peers_response(const Object& arg, const rak::socket_address* sa, Object& reply); -- void create_announce_peer_response(const Object& arg, const rak::socket_address* sa, Object& reply); -+ void create_find_node_response(const DhtMessage& arg, DhtMessage& reply); -+ void create_get_peers_response(const DhtMessage& arg, const rak::socket_address* sa, DhtMessage& reply); -+ void create_announce_peer_response(const DhtMessage& arg, const rak::socket_address* sa, DhtMessage& reply); - - int add_transaction(DhtTransaction* t, int priority); - -diff --git a/src/dht/dht_tracker.cc b/src/dht/dht_tracker.cc -index 416dbf3..6e1afe9 100644 ---- a/src/dht/dht_tracker.cc -+++ b/src/dht/dht_tracker.cc -@@ -54,8 +54,8 @@ DhtTracker::add_peer(uint32_t addr, uint16_t port) { - - // Check if peer exists. If not, find oldest peer. - for (unsigned int i = 0; i < size(); i++) { -- if (m_peers[i].addr == compact.addr) { -- m_peers[i].port = compact.port; -+ if (m_peers[i].peer.addr == compact.addr) { -+ m_peers[i].peer.port = compact.port; - m_lastSeen[i] = cachedTime.seconds(); - return; - -@@ -77,10 +77,13 @@ DhtTracker::add_peer(uint32_t addr, uint16_t port) { - } - } - --// Return compact info (6 bytes) for up to 30 peers, returning different --// peers for each call if there are more. --Object -+// Return compact info as bencoded string (8 bytes per peer) for up to 30 peers, -+// returning different peers for each call if there are more. -+SimpleString - DhtTracker::get_peers(unsigned int maxPeers) { -+ if (sizeof(BencodeAddress) != 8) -+ throw internal_error("DhtTracker::BencodeAddress is packed incorrectly."); -+ - PeerList::iterator first = m_peers.begin(); - PeerList::iterator last = m_peers.end(); - -@@ -94,11 +97,7 @@ DhtTracker::get_peers(unsigned int maxPeers) { - last = first + maxPeers; - } - -- Object peers = Object::create_list(); -- for (; first != last; ++first) -- peers.insert_back(std::string(first->c_str(), sizeof(*first))); -- -- return peers; -+ return SimpleString(first->bencode(), last->bencode() - first->bencode()); - } - - // Remove old announces. -@@ -107,9 +106,9 @@ DhtTracker::prune(uint32_t maxAge) { - uint32_t minSeen = cachedTime.seconds() - maxAge; - - for (unsigned int i = 0; i < m_lastSeen.size(); i++) -- if (m_lastSeen[i] < minSeen) m_peers[i].port = 0; -+ if (m_lastSeen[i] < minSeen) m_peers[i].peer.port = 0; - -- m_peers.erase(std::remove_if(m_peers.begin(), m_peers.end(), rak::on(rak::mem_ref(&SocketAddressCompact::port), std::bind2nd(std::equal_to<uint16_t>(), 0))), m_peers.end()); -+ m_peers.erase(std::remove_if(m_peers.begin(), m_peers.end(), std::mem_fun_ref(&BencodeAddress::empty)), m_peers.end()); - m_lastSeen.erase(std::remove_if(m_lastSeen.begin(), m_lastSeen.end(), std::bind2nd(std::less<uint32_t>(), minSeen)), m_lastSeen.end()); - - if (m_peers.size() != m_lastSeen.size()) -diff --git a/src/dht/dht_tracker.h b/src/dht/dht_tracker.h -index 8515dd0..53fd1e3 100644 ---- a/src/dht/dht_tracker.h -+++ b/src/dht/dht_tracker.h -@@ -43,6 +43,7 @@ - #include <rak/socket_address.h> - - #include "download/download_info.h" // for SocketAddressCompact -+#include "torrent/simple_string.h" - - namespace torrent { - -@@ -65,14 +66,26 @@ public: - size_t size() const { return m_peers.size(); } - - void add_peer(uint32_t addr, uint16_t port); -- Object get_peers(unsigned int maxPeers = max_peers); -+ SimpleString get_peers(unsigned int maxPeers = max_peers); - - // Remove old announces from the tracker that have not reannounced for - // more than the given number of seconds. - void prune(uint32_t maxAge); - - private: -- typedef std::vector<SocketAddressCompact> PeerList; -+ // We need to store the address as a bencoded string. -+ struct BencodeAddress { -+ char header[2]; -+ SocketAddressCompact peer; -+ -+ BencodeAddress(const SocketAddressCompact& p) : peer(p) { header[0] = '6'; header[1] = ':'; } -+ -+ const char* bencode() const { return header; } -+ -+ bool empty() const { return !peer.port; } -+ } __attribute__ ((packed)); -+ -+ typedef std::vector<BencodeAddress> PeerList; - - PeerList m_peers; - std::vector<uint32_t> m_lastSeen; -diff --git a/src/dht/dht_transaction.cc b/src/dht/dht_transaction.cc -index 2a6a8a6..0b3cfd0 100644 ---- a/src/dht/dht_transaction.cc -+++ b/src/dht/dht_transaction.cc -@@ -123,7 +123,7 @@ DhtSearch::trim(bool final) { - // We keep: - // - the max_contacts=18 closest good or unknown nodes and all nodes closer - // than them (to see if further searches find closer ones) -- // - for announces, also the 8 closest good nodes (i.e. nodes that have -+ // - for announces, also the 3 closest good nodes (i.e. nodes that have - // replied) to have at least that many for the actual announce - // - any node that currently has transactions pending - // -@@ -136,7 +136,7 @@ DhtSearch::trim(bool final) { - // node is new and unknown otherwise - - int needClosest = final ? 0 : max_contacts; -- int needGood = is_announce() ? DhtBucket::num_nodes : 0; -+ int needGood = is_announce() ? max_announce : 0; - - // We're done if we can't find any more nodes to contact. - m_next = end(); -@@ -252,7 +252,7 @@ DhtAnnounce::start_announce() { - } - - void --DhtAnnounce::receive_peers(const Object& peers) { -+DhtAnnounce::receive_peers(SimpleString peers) { - m_tracker->receive_peers(peers); - } - -@@ -262,9 +262,9 @@ DhtAnnounce::update_status() { - } - - void --DhtTransactionPacket::build_buffer(const Object& data) { -+DhtTransactionPacket::build_buffer(const DhtMessage& msg) { - char buffer[1500]; // If the message would exceed an Ethernet frame, something went very wrong. -- object_buffer_t result = object_write_bencode_c(object_write_to_buffer, NULL, std::make_pair(buffer, buffer + sizeof(buffer)), &data); -+ object_buffer_t result = staticMap_write_bencode_c(object_write_to_buffer, NULL, std::make_pair(buffer, buffer + sizeof(buffer)), msg); - - m_length = result.second - buffer; - m_data = new char[m_length]; -@@ -277,7 +277,6 @@ DhtTransaction::DhtTransaction(int quick_timeout, int timeout, const HashString& - m_sa(*sa), - m_timeout(cachedTime.seconds() + timeout), - m_quickTimeout(cachedTime.seconds() + quick_timeout), -- m_retry(3), - m_packet(NULL) { - - } -diff --git a/src/dht/dht_transaction.h b/src/dht/dht_transaction.h -index 194316d..43b42ab 100644 ---- a/src/dht/dht_transaction.h -+++ b/src/dht/dht_transaction.h -@@ -43,6 +43,7 @@ - - #include "dht/dht_node.h" - #include "torrent/hash_string.h" -+#include "torrent/static_map.h" - - namespace torrent { - -@@ -93,6 +94,9 @@ public: - // Number of closest potential contact nodes to keep. - static const unsigned int max_contacts = 18; - -+ // Number of closest nodes we actually announce to. -+ static const unsigned int max_announce = 3; -+ - DhtSearch(const HashString& target, const DhtBucket& contacts); - virtual ~DhtSearch(); - -@@ -178,22 +182,66 @@ public: - // counts announces instead. - const_accessor start_announce(); - -- void receive_peers(const Object& peer_list); -+ void receive_peers(SimpleString peers); - void update_status(); - - private: - TrackerDht* m_tracker; - }; - -+// Possible bencode keys in a DHT message. -+enum dht_keys { -+ key_a_id, -+ key_a_infoHash, -+ key_a_port, -+ key_a_target, -+ key_a_token, -+ -+ key_e_0, -+ key_e_1, -+ -+ key_q, -+ -+ key_r_id, -+ key_r_nodes, -+ key_r_token, -+ key_r_values, -+ -+ key_t, -+ key_v, -+ key_y, -+ -+ key_LAST, -+}; -+ -+class DhtMessage : public StaticMap<dht_keys, key_LAST> { -+public: -+ typedef StaticMap<dht_keys, key_LAST> base_type; -+ typedef StaticMapKeys::mapping_type mapping_type; -+ -+ DhtMessage() : data_end(data) {}; -+ -+ // Must be big enough to hold one of the possible variable-sized reply data. -+ // Currently either: -+ // - error message (size doesn't really matter, it'll be truncated at worst) -+ // - announce token (8 bytes, needs 20 bytes buffer to build) -+ // Never more than one of the above. -+ // And additionally for queries we send: -+ // - transaction ID (3 bytes) -+ static const size_t data_size = 64; -+ char data[data_size]; -+ char* data_end; -+}; -+ - // Class holding transaction data to be transmitted. - class DhtTransactionPacket { - public: - // transaction packet -- DhtTransactionPacket(const rak::socket_address* s, const Object& d, unsigned int id, DhtTransaction* t) -+ DhtTransactionPacket(const rak::socket_address* s, const DhtMessage& d, unsigned int id, DhtTransaction* t) - : m_sa(*s), m_id(id), m_transaction(t) { build_buffer(d); }; - - // non-transaction packet -- DhtTransactionPacket(const rak::socket_address* s, const Object& d) -+ DhtTransactionPacket(const rak::socket_address* s, const DhtMessage& d) - : m_sa(*s), m_id(-cachedTime.seconds()), m_transaction(NULL) { build_buffer(d); }; - - ~DhtTransactionPacket() { delete[] m_data; } -@@ -214,7 +262,7 @@ public: - DhtTransaction* transaction() { return m_transaction; } - - private: -- void build_buffer(const Object& data); -+ void build_buffer(const DhtMessage& data); - - rak::socket_address m_sa; - char* m_data; -@@ -255,9 +303,6 @@ public: - int quick_timeout() { return m_quickTimeout; } - bool has_quick_timeout() { return m_hasQuickTimeout; } - -- int dec_retry() { return m_retry--; } -- int retry() { return m_retry; } -- - DhtTransactionPacket* packet() { return m_packet; } - void set_packet(DhtTransactionPacket* p) { m_packet = p; } - -@@ -282,7 +327,6 @@ private: - rak::socket_address m_sa; - int m_timeout; - int m_quickTimeout; -- int m_retry; - DhtTransactionPacket* m_packet; - }; - -@@ -337,7 +381,7 @@ public: - - class DhtTransactionAnnouncePeer : public DhtTransaction { - public: -- DhtTransactionAnnouncePeer(const HashString& id, const rak::socket_address* sa, const HashString& infoHash, const std::string& token) -+ DhtTransactionAnnouncePeer(const HashString& id, const rak::socket_address* sa, const HashString& infoHash, SimpleString token) - : DhtTransaction(-1, 30, id, sa), - m_infoHash(infoHash), - m_token(token) { } -@@ -345,11 +389,11 @@ public: - virtual transaction_type type() { return DHT_ANNOUNCE_PEER; } - - const HashString& info_hash() { return m_infoHash; } -- const std::string& token() { return m_token; } -+ SimpleString token() { return m_token; } - - private: - HashString m_infoHash; -- std::string m_token; -+ SimpleString m_token; - }; - - inline bool -diff --git a/src/download/download_constructor.cc b/src/download/download_constructor.cc -index fc2a272..f37f848 100644 ---- a/src/download/download_constructor.cc -+++ b/src/download/download_constructor.cc -@@ -36,6 +36,7 @@ - - #include "config.h" - -+#include <cstdio> - #include <cstring> - #include <string.h> - #include <rak/functional.h> -diff --git a/src/net/address_list.cc b/src/net/address_list.cc -index 2fc3992..e5cf3cb 100644 ---- a/src/net/address_list.cc -+++ b/src/net/address_list.cc -@@ -70,7 +70,7 @@ AddressList::parse_address_normal(const Object::list_type& b) { - } - - void --AddressList::parse_address_compact(const std::string& s) { -+AddressList::parse_address_compact(SimpleString s) { - if (sizeof(const SocketAddressCompact) != 6) - throw internal_error("ConnectionList::AddressList::parse_address_compact(...) bad struct size."); - -@@ -79,4 +79,18 @@ AddressList::parse_address_compact(const std::string& s) { - std::back_inserter(*this)); - } - -+void -+AddressList::parse_address_bencode(SimpleString s) { -+ if (sizeof(const SocketAddressCompact) != 6) -+ throw internal_error("AddressList::parse_address_bencode(...) bad struct size."); -+ -+ while (s.length() >= 2 + sizeof(SocketAddressCompact)) { -+ if (s[0] != '6' || s[1] != ':') -+ break; -+ -+ insert(end(), *reinterpret_cast<const SocketAddressCompact*>(s.c_str() + 2)); -+ s = SimpleString(s.c_str() + 2 + sizeof(SocketAddressCompact), s.length() - 2 - sizeof(SocketAddressCompact)); -+ } -+} -+ - } -diff --git a/src/net/address_list.h b/src/net/address_list.h -index e4d2009..10dbac4 100644 ---- a/src/net/address_list.h -+++ b/src/net/address_list.h -@@ -42,6 +42,7 @@ - #include <rak/socket_address.h> - - #include <torrent/object.h> -+#include <torrent/simple_string.h> - - namespace torrent { - -@@ -49,7 +50,8 @@ class AddressList : public std::list<rak::socket_address> { - public: - // Parse normal or compact list of addresses and add to AddressList - void parse_address_normal(const Object::list_type& b); -- void parse_address_compact(const std::string& s); -+ void parse_address_compact(SimpleString s); -+ void parse_address_bencode(SimpleString s); - - private: - static rak::socket_address parse_address(const Object& b); -diff --git a/src/protocol/extensions.cc b/src/protocol/extensions.cc -index f3464af..7cbf6e3 100644 ---- a/src/protocol/extensions.cc -+++ b/src/protocol/extensions.cc -@@ -37,7 +37,7 @@ - #include "config.h" - - #include <limits> --#include <sstream> -+#include <stdarg.h> - - #include <cstdio> - -@@ -49,18 +49,62 @@ - #include "torrent/object_stream.h" - #include "torrent/peer/connection_list.h" - #include "torrent/peer/peer_info.h" --#include "tracker/tracker_http.h" -+#include "torrent/static_map.h" - #include "manager.h" - - #include "extensions.h" - - namespace torrent { - --const char* ProtocolExtension::message_keys[] = { -- "HANDSHAKE", -- "ut_pex", -+enum ext_handshake_keys { -+ key_e, -+ key_m_utPex, -+ key_p, -+ key_reqq, -+ key_v, -+ key_handshake_LAST - }; - -+enum ext_pex_keys { -+ key_pex_added, -+ key_pex_LAST -+}; -+ -+class ExtHandshakeMessage : public StaticMap<ext_handshake_keys, key_handshake_LAST> { -+public: -+ typedef StaticMap<ext_handshake_keys, key_handshake_LAST> base_type; -+ typedef StaticMapKeys::mapping_type mapping_type; -+}; -+ -+class ExtPEXMessage : public StaticMap<ext_pex_keys, key_pex_LAST> { -+public: -+ typedef StaticMap<ext_pex_keys, key_pex_LAST> base_type; -+ typedef StaticMapKeys::mapping_type mapping_type; -+}; -+ -+ExtHandshakeMessage::mapping_type ext_handshake_key_names[ExtHandshakeMessage::length] = { -+ { key_e, "e" }, -+ { key_m_utPex, "m::ut_pex" }, -+ { key_p, "p" }, -+ { key_reqq, "reqq" }, -+ { key_v, "v" }, -+}; -+ -+ExtPEXMessage::mapping_type ext_pex_key_names[ExtPEXMessage::length] = { -+ { key_pex_added, "added" }, -+}; -+ -+ext_handshake_keys message_keys[ProtocolExtension::FIRST_INVALID] = { -+ key_handshake_LAST, // Handshake, not actually used. -+ key_m_utPex, -+}; -+ -+template<> -+const ExtHandshakeMessage::key_map_init ExtHandshakeMessage::base_type::keyMap(ext_handshake_key_names); -+ -+template<> -+const ExtPEXMessage::key_map_init ExtPEXMessage::base_type::keyMap(ext_pex_key_names); -+ - void - ProtocolExtension::cleanup() { - // if (is_default()) -@@ -105,23 +149,21 @@ ProtocolExtension::unset_local_enabled(int t) { - - DataBuffer - ProtocolExtension::generate_handshake_message() { -- Object map = Object::create_map(); -- Object message = Object::create_map(); -- -- map.insert_key(message_keys[UT_PEX], is_local_enabled(UT_PEX) ? 1 : 0); -+ ExtHandshakeMessage message; - - // Add "e" key if encryption is enabled, set it to 1 if we require - // encryption for incoming connections, or 0 otherwise. - if ((manager->connection_manager()->encryption_options() & ConnectionManager::encryption_allow_incoming) != 0) -- message.insert_key("e", (manager->connection_manager()->encryption_options() & ConnectionManager::encryption_require) != 0); -+ message[key_e] = (manager->connection_manager()->encryption_options() & ConnectionManager::encryption_require) != 0; -+ -+ message[key_p] = manager->connection_manager()->listen_port(); -+ message[key_v] = SimpleString("libTorrent " VERSION); -+ message[key_reqq] = 2048; // maximum request queue size - -- message.insert_key("m", map); -- message.insert_key("p", manager->connection_manager()->listen_port()); -- message.insert_key("v", "libTorrent " VERSION); -- message.insert_key("reqq", 2048); // maximum request queue size -+ message[key_m_utPex] = is_local_enabled(UT_PEX) ? UT_PEX : 0; - - char buffer[1024]; -- object_buffer_t result = object_write_bencode_c(object_write_to_buffer, NULL, std::make_pair(buffer, buffer + sizeof(buffer)), &message); -+ object_buffer_t result = staticMap_write_bencode_c(object_write_to_buffer, NULL, std::make_pair(buffer, buffer + sizeof(buffer)), message); - - int length = result.second - buffer; - char* copy = new char[length]; -@@ -130,21 +172,30 @@ ProtocolExtension::generate_handshake_message() { - return DataBuffer(copy, copy + length); - } - --DataBuffer --ProtocolExtension::generate_toggle_message(ProtocolExtension::MessageType t, bool on) { -- // TODO: Check if we're accepting this message type? -+inline DataBuffer -+ProtocolExtension::build_bencode(size_t maxLength, const char* format, ...) { -+ char* b = new char[maxLength]; - -- // Manually create bencoded map { "m" => { message_keys[t] => on ? t : 0 } } -- char* b = new char[32]; -- unsigned int length = snprintf(b, 32, "d1:md%zu:%si%deee", strlen(message_keys[t]), message_keys[t], on ? t : 0); -+ va_list args; -+ va_start(args, format); -+ unsigned int length = vsnprintf(b, maxLength, format, args); -+ va_end(args); - -- if (length > 32) -- throw internal_error("ProtocolExtension::toggle_message wrote past buffer."); -+ if (length > maxLength) -+ throw internal_error("ProtocolExtension::build_bencode wrote past buffer."); - - return DataBuffer(b, b + length); - } - - DataBuffer -+ProtocolExtension::generate_toggle_message(MessageType t, bool on) { -+ // TODO: Check if we're accepting this message type? -+ -+ // Manually create bencoded map { "m" => { message_keys[t] => on ? t : 0 } } -+ return build_bencode(32, "d1:md%zu:%si%deee", strlen(ext_handshake_key_names[message_keys[t]].key) - 3, ext_handshake_key_names[message_keys[t]].key + 3, on ? t : 0); -+} -+ -+DataBuffer - ProtocolExtension::generate_ut_pex_message(const PEXList& added, const PEXList& removed) { - if (added.empty() && removed.empty()) - return DataBuffer(); -@@ -195,37 +246,30 @@ ProtocolExtension::read_start(int type, uint32_t length, bool skip) { - - void - ProtocolExtension::read_done() { -- if (m_readType == SKIP_EXTENSION) { -- delete [] m_read; -- m_read = NULL; -- return; -- } -+ try { -+ switch(m_readType) { -+ case SKIP_EXTENSION: -+ break; - -- std::stringstream s(std::string(m_read, m_readPos)); -- s.imbue(std::locale::classic()); -+ case HANDSHAKE: -+ parse_handshake(); -+ break; - -- delete [] m_read; -- m_read = NULL; -- -- Object message; -- s >> message; -+ case UT_PEX: -+ parse_ut_pex(); -+ break; - -- if (s.fail() || !message.is_map()) -- throw communication_error("Invalid extension message."); -- -- switch(m_readType) { -- case HANDSHAKE: -- parse_handshake(message); -- break; -- -- case UT_PEX: -- parse_ut_pex(message); -- break; -+ default: -+ throw internal_error("ProtocolExtension::read_done called with invalid extension type."); -+ } - -- default: -- throw internal_error("ProtocolExtension::down_extension_finished called with invalid extension type."); -+ } catch (bencode_error& e) { -+ // Ignore malformed messages. - } - -+ delete [] m_read; -+ m_read = NULL; -+ - m_readType = FIRST_INVALID; - m_flags |= flag_received_ext; - } -@@ -242,24 +286,22 @@ ProtocolExtension::peer_toggle_remote(int type, bool active) { - } - - void --ProtocolExtension::parse_handshake(const Object& message) { -- if (message.has_key_map("m")) { -- const Object& idMap = message.get_key("m"); -+ProtocolExtension::parse_handshake() { -+ ExtHandshakeMessage message; -+ staticMap_read_bencode(m_read, m_readPos, message); - -- for (int t = HANDSHAKE + 1; t < FIRST_INVALID; t++) { -- if (!idMap.has_key_value(message_keys[t])) -- continue; -+ for (int t = HANDSHAKE + 1; t < FIRST_INVALID; t++) { -+ if (!message[message_keys[t]].is_value()) -+ continue; - -- uint8_t id = idMap.get_key_value(message_keys[t]); -+ uint8_t id = message[message_keys[t]].as_value(); - -- set_remote_supported(t); -+ set_remote_supported(t); - -- if (id != m_idMap[t - 1]) { -- peer_toggle_remote(t, id != 0); -- -- m_idMap[t - 1] = id; -- } -+ if (id != m_idMap[t - 1]) { -+ peer_toggle_remote(t, id != 0); - -+ m_idMap[t - 1] = id; - } - } - -@@ -271,29 +313,32 @@ ProtocolExtension::parse_handshake(const Object& message) { - unset_local_enabled(t); - } - -- if (message.has_key_value("p")) { -- uint16_t port = message.get_key_value("p"); -+ if (message[key_p].is_value()) { -+ uint16_t port = message[key_p].as_value(); - - if (port > 0) - m_peerInfo->set_listen_port(port); - } - -- if (message.has_key_value("reqq")) -- m_maxQueueLength = message.get_key_value("reqq"); -+ if (message[key_reqq].is_value()) -+ m_maxQueueLength = message[key_reqq].as_value(); - - m_flags &= ~flag_initial_handshake; - } - - void --ProtocolExtension::parse_ut_pex(const Object& message) { -+ProtocolExtension::parse_ut_pex() { - // Ignore message if we're still in the handshake (no connection - // yet), or no peers are present. - -+ ExtPEXMessage message; -+ staticMap_read_bencode(m_read, m_readPos, message); -+ - // TODO: Check if pex is enabled? -- if (!message.has_key_string("added")) -+ if (!message[key_pex_added].is_sstring()) - return; - -- const std::string& peers = message.get_key_string("added"); -+ SimpleString peers = message[key_pex_added].as_sstring(); - if (peers.empty()) - return; - -diff --git a/src/protocol/extensions.h b/src/protocol/extensions.h -index 1c370fc..96ed652 100644 ---- a/src/protocol/extensions.h -+++ b/src/protocol/extensions.h -@@ -46,6 +46,13 @@ - #include "download/download_info.h" - #include "net/data_buffer.h" - -+// Not really important, so no need to make this a configure check. -+#ifdef __GNUC__ -+#define ATTRIBUTE_PRINTF(num) __attribute__ ((format (printf, num, num+1))) -+#else -+#define ATTRIBUTE_PRINTF(num) -+#endif -+ - namespace torrent { - - class ProtocolExtension { -@@ -71,8 +78,6 @@ public: - static const int flag_local_enabled_base = 1<<8; - static const int flag_remote_supported_base = 1<<16; - -- static const char* message_keys[FIRST_INVALID]; -- - // Number of extensions we support, not counting handshake. - static const int extension_count = FIRST_INVALID - HANDSHAKE - 1; - -@@ -128,8 +133,10 @@ public: - void reset() { std::memset(&m_idMap, 0, sizeof(m_idMap)); } - - private: -- void parse_handshake(const Object& message); -- void parse_ut_pex(const Object& message); -+ void parse_handshake(); -+ void parse_ut_pex(); -+ -+ static DataBuffer build_bencode(size_t maxLength, const char* format, ...) ATTRIBUTE_PRINTF(2); - - void peer_toggle_remote(int type, bool active); - -diff --git a/src/torrent/Makefile.am b/src/torrent/Makefile.am -index bec124d..820ce52 100644 ---- a/src/torrent/Makefile.am -+++ b/src/torrent/Makefile.am -@@ -41,6 +41,9 @@ libsub_torrent_la_SOURCES = \ - rate.h \ - resume.cc \ - resume.h \ -+ simple_string.h \ -+ static_map.cc \ -+ static_map.h \ - throttle.cc \ - throttle.h \ - torrent.cc \ -@@ -74,6 +77,8 @@ libtorrentinclude_HEADERS = \ - poll_select.h \ - rate.h \ - resume.h \ -+ simple_string.h \ -+ static_map.h \ - throttle.h \ - torrent.h \ - tracker.h \ -diff --git a/src/torrent/hash_string.h b/src/torrent/hash_string.h -index f62d450..14623f7 100644 ---- a/src/torrent/hash_string.h -+++ b/src/torrent/hash_string.h -@@ -44,6 +44,7 @@ - #include <string> - #include <iterator> - #include <torrent/common.h> -+#include <torrent/simple_string.h> - - namespace torrent { - -@@ -85,6 +86,8 @@ public: - - std::string str() const { return std::string(m_data, size_data); } - -+ SimpleString s_str() const { return SimpleString(m_data, size_data); } -+ - void clear(int v = 0) { std::memset(data(), v, size()); } - - void assign(const value_type* src) { std::memcpy(data(), src, size()); } -@@ -96,6 +99,7 @@ public: - // size_data. - static const HashString* cast_from(const char* src) { return (const HashString*)src; } - static const HashString* cast_from(const std::string& src) { return (const HashString*)src.c_str(); } -+ static const HashString* cast_from(const SimpleString& src){ return (const HashString*)src.c_str(); } - - static HashString* cast_from(char* src) { return (HashString*)src; } - -diff --git a/src/torrent/object.cc b/src/torrent/object.cc -index 2b1cf41..3a0bcae 100644 ---- a/src/torrent/object.cc -+++ b/src/torrent/object.cc -@@ -195,6 +195,7 @@ Object::operator = (const Object& src) { - case TYPE_STRING: m_string = new string_type(*src.m_string); break; - case TYPE_LIST: m_list = new list_type(*src.m_list); break; - case TYPE_MAP: m_map = new map_type(*src.m_map); break; -+ case TYPE_SSTRING:m_sstring = src.m_sstring; break; - } - - return *this; -diff --git a/src/torrent/object.h b/src/torrent/object.h -index 7ad040b..6cc4e4a 100644 ---- a/src/torrent/object.h -+++ b/src/torrent/object.h -@@ -42,6 +42,7 @@ - #include <list> - #include <torrent/common.h> - #include <torrent/exceptions.h> -+#include <torrent/simple_string.h> - - namespace torrent { - -@@ -82,13 +83,16 @@ public: - TYPE_VALUE, - TYPE_STRING, - TYPE_LIST, -- TYPE_MAP -+ TYPE_MAP, -+ TYPE_SSTRING, // Only used in StaticMap. - }; - - Object() : m_flags(TYPE_NONE) {} - Object(const value_type v) : m_flags(TYPE_VALUE), m_value(v) {} - Object(const char* s) : m_flags(TYPE_STRING), m_string(new string_type(s)) {} - Object(const string_type& s) : m_flags(TYPE_STRING), m_string(new string_type(s)) {} -+ Object(const char* s, size_t l) : m_flags(TYPE_SSTRING), m_sstring(SimpleString(s, l)) {} -+ Object(SimpleString s) : m_flags(TYPE_SSTRING), m_sstring(s) {} - Object(const Object& b); - - ~Object() { clear(); } -@@ -96,6 +100,7 @@ public: - // Move this out of the class namespace, call them create_object_. - static Object create_value() { return Object(value_type()); } - static Object create_string() { return Object(string_type()); } -+ static Object create_sstring(){ return Object(SimpleString()); } - static Object create_list() { Object tmp; tmp.m_flags = TYPE_LIST; tmp.m_list = new list_type(); return tmp; } - static Object create_map() { Object tmp; tmp.m_flags = TYPE_MAP; tmp.m_map = new map_type(); return tmp; } - -@@ -120,6 +125,7 @@ public: - bool is_string() const { return type() == TYPE_STRING; } - bool is_list() const { return type() == TYPE_LIST; } - bool is_map() const { return type() == TYPE_MAP; } -+ bool is_sstring() const { return type() == TYPE_SSTRING; } - - value_type& as_value() { check_throw(TYPE_VALUE); return m_value; } - const value_type& as_value() const { check_throw(TYPE_VALUE); return m_value; } -@@ -133,6 +139,9 @@ public: - map_type& as_map() { check_throw(TYPE_MAP); return *m_map; } - const map_type& as_map() const { check_throw(TYPE_MAP); return *m_map; } - -+ SimpleStringBase& as_sstring() { check_throw(TYPE_SSTRING); return m_sstring; } -+ SimpleString as_sstring() const { check_throw(TYPE_SSTRING); return m_sstring; } -+ - bool has_key(const key_type& k) const { check_throw(TYPE_MAP); return m_map->find(k) != m_map->end(); } - bool has_key_value(const key_type& k) const { check_throw(TYPE_MAP); return check(m_map->find(k), TYPE_VALUE); } - bool has_key_string(const key_type& k) const { check_throw(TYPE_MAP); return check(m_map->find(k), TYPE_STRING); } -@@ -200,6 +209,7 @@ public: - string_type* m_string; - list_type* m_list; - map_type* m_map; -+ SimpleStringBase m_sstring; - }; - }; - -@@ -211,6 +221,7 @@ Object::Object(const Object& b) : m_flags(b.type()) { - case TYPE_STRING: m_string = new string_type(*b.m_string); break; - case TYPE_LIST: m_list = new list_type(*b.m_list); break; - case TYPE_MAP: m_map = new map_type(*b.m_map); break; -+ case TYPE_SSTRING:m_sstring = b.m_sstring; break; - } - } - -@@ -222,6 +233,7 @@ Object::clear() { - case TYPE_STRING: delete m_string; break; - case TYPE_LIST: delete m_list; break; - case TYPE_MAP: delete m_map; break; -+ case TYPE_SSTRING:break; - } - - // Only clear type? -diff --git a/src/torrent/object_stream.cc b/src/torrent/object_stream.cc -index 18eb849..73c816b 100644 ---- a/src/torrent/object_stream.cc -+++ b/src/torrent/object_stream.cc -@@ -38,12 +38,14 @@ - - #include <iterator> - #include <iostream> -+#include <sstream> - #include <rak/functional.h> - - #include "utils/sha1.h" - - #include "object.h" - #include "object_stream.h" -+#include "static_map.h" - - namespace torrent { - -@@ -63,6 +65,18 @@ object_read_string(std::istream* input, std::string& str) { - return !input->fail(); - } - -+Object -+object_get_sstring(const char** buffer) { -+ /*const*/ char* next; -+ size_t length = strtoumax(*buffer, &next, 10); -+ -+ if (next == *buffer || *next != ':') -+ return Object(); -+ -+ *buffer = next + 1 + length; -+ return Object(next + 1, length); -+} -+ - // Could consider making this non-recursive, but they seldomly are - // deep enough to make that worth-while. - void -@@ -159,6 +173,133 @@ object_read_bencode(std::istream* input, Object* object, uint32_t depth) { - object->clear(); - } - -+const char* -+staticMap_read_bencode_c(const char* buffer, const char* bufferEnd, uint32_t depth, Object* values, const StaticMapKeys& keys, bool discard) { -+ if (buffer >= bufferEnd) -+ return bufferEnd; -+ -+ // Undecoded bencode object. -+ if (!discard && keys.type() == StaticMapKeys::TYPE_BENCODE) { -+ const char* begin = buffer; -+ buffer = staticMap_read_bencode_c(buffer, bufferEnd, ++depth, values, keys, true); -+ values[keys.index_begin()] = SimpleString(begin, buffer - begin); -+ return buffer; -+ } -+ -+ if (!discard && keys.type() == StaticMapKeys::TYPE_BENCODE_LIST && *buffer != 'l') -+ discard = true; -+ -+ switch (*buffer) { -+ case 'i': { -+ char* next; -+ intmax_t value = strtoimax(++buffer, &next, 10); -+ -+ if (next == buffer || next > bufferEnd || *next != 'e') -+ break; -+ -+ if (!discard && keys.type() == StaticMapKeys::TYPE_VALUE) -+ values[keys.index_begin()] = (int64_t)value; -+ -+ return next + 1; -+ } -+ -+ case 'l': { -+ ++buffer; -+ if (++depth >= 1024) -+ break; -+ -+ // Want undecoded bencode list: find end of list. -+ if (!discard && keys.type() == StaticMapKeys::TYPE_BENCODE_LIST) { -+ const char* end = buffer; -+ while (end < bufferEnd && *end != 'e') -+ end = staticMap_read_bencode_c(end, bufferEnd, depth, values, keys, true); -+ -+ values[keys.index_begin()] = SimpleString(buffer, end - buffer); -+ return ++end; -+ } -+ -+ StaticMapKeys::const_iterator itr = keys.begin(); -+ while (buffer != bufferEnd) { -+ if (*buffer == 'e') -+ return ++buffer; -+ -+ discard |= itr == keys.end(); -+ buffer = staticMap_read_bencode_c(buffer, bufferEnd, depth, values, discard ? keys : *itr, discard); -+ -+ if (itr != keys.end()) -+ ++itr; -+ } -+ -+ break; -+ } -+ -+ case 'd': { -+ ++buffer; -+ if (++depth >= 1024) -+ break; -+ -+ StaticMapKeys::const_iterator itr = keys.begin(); -+ SimpleString last; -+ bool discardThis = discard; -+ -+ while (buffer != bufferEnd) { -+ if (*buffer == 'e') -+ return ++buffer; -+ -+ Object keyObj = object_get_sstring(&buffer); -+ if (!keyObj.is_sstring()) -+ break; -+ -+ SimpleString key = keyObj.as_sstring(); -+ if (key.end() >= bufferEnd) -+ break; -+ -+ if (key < last) { -+ itr = keys.begin(); -+ discardThis = discard; -+ } -+ -+ discardThis |= itr == keys.end(); -+ int cmp = discardThis ? -1 : key.cmp(itr->key()); -+ while (cmp > 0) { -+ if (++itr == keys.end()) { -+ cmp = -1; -+ discardThis = true; -+ break; -+ } -+ -+ cmp = key.cmp(itr->key()); -+ } -+ -+ buffer = staticMap_read_bencode_c(buffer, bufferEnd, depth, values, cmp ? keys : *itr, cmp); -+ -+ last = key; -+ } -+ -+ break; -+ } -+ -+ default: -+ if (*buffer < '0' || *buffer > '9') -+ break; -+ -+ Object strObj = object_get_sstring(&buffer); -+ if (!strObj.is_sstring()) -+ break; -+ -+ SimpleString str = strObj.as_sstring(); -+ if (str.end() >= bufferEnd) -+ break; -+ -+ if (!discard && keys.type() == StaticMapKeys::TYPE_VALUE) -+ values[keys.index_begin()] = str; -+ -+ return str.end(); -+ } -+ -+ throw bencode_error("Invalid bencode data."); -+} -+ - void - object_write_bencode(std::ostream* output, const Object* object) { - char buffer[1024]; -@@ -267,6 +408,7 @@ void - object_write_bencode_c_object(object_write_data_t* output, const Object* object) { - switch (object->type()) { - case Object::TYPE_NONE: -+ case Object::TYPE_SSTRING: - break; - - case Object::TYPE_VALUE: -@@ -306,6 +448,86 @@ object_write_bencode_c_object(object_write_data_t* output, const Object* object) - } - } - -+void -+staticMap_write_bencode_c_values(object_write_data_t* output, const Object* values, const StaticMapKeys& keys) { -+ if (keys.type() == StaticMapKeys::TYPE_LIST) { -+ size_t indexEnd = keys.index_begin(); -+ while (indexEnd < keys.index_end() && values[indexEnd].type() != Object::TYPE_NONE) -+ indexEnd++; -+ -+ // Empty list? Drop it. Sparse lists are not possible so only check first element. -+ if (indexEnd == keys.index_begin()) -+ return; -+ -+ object_write_bencode_c_char(output, 'l'); -+ StaticMapKeys::const_iterator itr = keys.begin(); -+ size_t index = keys.index_begin(); -+ while (index < indexEnd) { -+ staticMap_write_bencode_c_values(output, values, *itr); -+ index = itr->index_end(); -+ if (++itr == keys.end() && index != indexEnd) -+ throw internal_error("staticMap_write_bencode_c_values reached end of list before end of index list."); -+ } -+ object_write_bencode_c_char(output, 'e'); -+ -+ } else if (keys.type() == StaticMapKeys::TYPE_DICT) { -+ // Find next non-empty entry. -+ size_t next = keys.index_begin(); -+ while (values[next].type() == Object::TYPE_NONE) -+ if (++next == keys.index_end()) -+ return; -+ -+ object_write_bencode_c_char(output, 'd'); -+ StaticMapKeys::const_iterator itr = keys.begin(); -+ while (next < keys.index_end()) { -+ while (itr->index_end() <= next) -+ if (++itr == keys.end()) -+ throw internal_error("staticMap_write_bencode_c_values reached end of keys before end of index list."); -+ -+ object_write_bencode_c_value(output, itr->key().size()); -+ object_write_bencode_c_char(output, ':'); -+ object_write_bencode_c_string(output, itr->key().c_str(), itr->key().size()); -+ -+ staticMap_write_bencode_c_values(output, values, *itr); -+ -+ next = itr->index_end(); -+ while (next < keys.index_end() && values[next].type() == Object::TYPE_NONE) -+ ++next; -+ } -+ object_write_bencode_c_char(output, 'e'); -+ -+ // Undecoded bencode value. -+ } else if (keys.type() == StaticMapKeys::TYPE_BENCODE) { -+ SimpleString value = values[keys.index_begin()].as_sstring(); -+ object_write_bencode_c_string(output, value.c_str(), value.size()); -+ -+ } else if (keys.type() == StaticMapKeys::TYPE_BENCODE_LIST) { -+ SimpleString value = values[keys.index_begin()].as_sstring(); -+ object_write_bencode_c_char(output, 'l'); -+ object_write_bencode_c_string(output, value.c_str(), value.size()); -+ object_write_bencode_c_char(output, 'e'); -+ -+ } else if (keys.type() != StaticMapKeys::TYPE_VALUE) { -+ throw internal_error("staticMap_write_bencode_c_values received key keys with invalid values type."); -+ -+ } else if (values[keys.index_begin()].type() == Object::TYPE_NONE) { -+ -+ } else if (values[keys.index_begin()].type() == Object::TYPE_VALUE) { -+ object_write_bencode_c_char(output, 'i'); -+ object_write_bencode_c_value(output, values[keys.index_begin()].as_value()); -+ object_write_bencode_c_char(output, 'e'); -+ -+ } else if (values[keys.index_begin()].type() == Object::TYPE_SSTRING) { -+ SimpleString value = values[keys.index_begin()].as_sstring(); -+ object_write_bencode_c_value(output, value.size()); -+ object_write_bencode_c_char(output, ':'); -+ object_write_bencode_c_string(output, value.c_str(), value.size()); -+ -+ } else { -+ throw internal_error("staticMap_write_bencode_c_values received key keys with invalid values type."); -+ } -+} -+ - object_buffer_t - object_write_bencode_c(object_write_t writeFunc, void* data, object_buffer_t buffer, const Object* object) { - object_write_data_t output; -@@ -327,6 +549,32 @@ object_write_bencode_c(object_write_t writeFunc, void* data, object_buffer_t buf - } - - object_buffer_t -+staticMap_write_bencode_c_wrap(object_write_t writeFunc, void* data, object_buffer_t buffer, const Object* values, const StaticMapKeys& map) { -+ object_write_data_t output; -+ output.writeFunc = writeFunc; -+ output.data = data; -+ output.buffer = buffer; -+ output.pos = buffer.first; -+ -+ staticMap_write_bencode_c_values(&output, values, map); -+#ifdef USE_EXTRA_DEBUG -+ std::istringstream sstream; -+ sstream.imbue(std::locale::classic()); -+ sstream.str(std::string(output.buffer.first, output.pos)); -+ Object request; -+ sstream >> request; -+ if (sstream.fail()) -+ throw internal_error("staticMap_write_bencode_c_wrap failed to create valid bencode format."); -+#endif -+ -+ // Don't flush the buffer. -+ if (output.pos == output.buffer.first) -+ return output.buffer; -+ -+ return output.writeFunc(output.data, object_buffer_t(output.buffer.first, output.pos)); -+} -+ -+object_buffer_t - object_write_to_buffer(void* data, object_buffer_t buffer) { - if (buffer.first == buffer.second) - throw internal_error("object_write_to_buffer(...) buffer overflow."); -diff --git a/src/torrent/object_stream.h b/src/torrent/object_stream.h -index 41cf82a..b399bf7 100644 ---- a/src/torrent/object_stream.h -+++ b/src/torrent/object_stream.h -@@ -43,6 +43,10 @@ - - namespace torrent { - -+template<typename tmpl_key_type, size_t tmpl_length> -+class StaticMap; -+class StaticMapKeys; -+ - std::string object_sha1(const Object* object) LIBTORRENT_EXPORT; - - // Assumes the stream's locale has been set to POSIX or C. Max depth -@@ -53,6 +57,18 @@ void object_read_bencode(std::istream* input, Object* object, uint32_t depth = 0 - // Assumes the stream's locale has been set to POSIX or C. - void object_write_bencode(std::ostream* output, const Object* object) LIBTORRENT_EXPORT; - -+// Convert buffer to static key map. Inlined because we don't want -+// a separate wrapper function for each template argument. -+template<typename tmpl_key_type, size_t tmpl_length> -+inline const char* -+staticMap_read_bencode(const char* buffer, const char* bufferEnd, StaticMap<tmpl_key_type, tmpl_length>& map) { -+ return staticMap_read_bencode_c(buffer, bufferEnd, 0, map.values(), map.map(), false); -+}; -+ -+// Internal use only. -+const char* -+staticMap_read_bencode_c(const char* buffer, const char* bufferEnd, uint32_t depth, Object* values, const StaticMapKeys& keys, bool discard); -+ - std::istream& operator >> (std::istream& input, Object& object) LIBTORRENT_EXPORT; - std::ostream& operator << (std::ostream& output, const Object& object) LIBTORRENT_EXPORT; - -@@ -62,6 +78,15 @@ typedef object_buffer_t (*object_write_t)(void* data, object_buffer_t buffer); - - object_buffer_t object_write_bencode_c(object_write_t writeFunc, void* data, object_buffer_t buffer, const Object* object) LIBTORRENT_EXPORT; - -+template<typename tmpl_key_type, size_t tmpl_length> -+inline object_buffer_t -+staticMap_write_bencode_c(object_write_t writeFunc, void* data, object_buffer_t buffer, const StaticMap<tmpl_key_type, tmpl_length>& object) { -+ return staticMap_write_bencode_c_wrap(writeFunc, data, buffer, object.values(), object.map()); -+} -+ -+// Internal use only. -+object_buffer_t staticMap_write_bencode_c_wrap(object_write_t writeFunc, void* data, object_buffer_t buffer, const Object* values, const StaticMapKeys& keys) LIBTORRENT_EXPORT; -+ - // To char buffer. 'data' is NULL. - object_buffer_t object_write_to_buffer(void* data, object_buffer_t buffer) LIBTORRENT_EXPORT; - object_buffer_t object_write_to_sha1(void* data, object_buffer_t buffer) LIBTORRENT_EXPORT; -diff --git a/src/torrent/simple_string.h b/src/torrent/simple_string.h -new file mode 100644 -index 0000000..8eaf3b7 ---- /dev/null -+++ b/src/torrent/simple_string.h -@@ -0,0 +1,129 @@ -+// libTorrent - BitTorrent library -+// Copyright (C) 2005-2008, Jari Sundell -+// -+// This program is free software; you can redistribute it and/or modify -+// it under the terms of the GNU General Public License as published by -+// the Free Software Foundation; either version 2 of the License, or -+// (at your option) any later version. -+// -+// This program is distributed in the hope that it will be useful, -+// but WITHOUT ANY WARRANTY; without even the implied warranty of -+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+// GNU General Public License for more details. -+// -+// You should have received a copy of the GNU General Public License -+// along with this program; if not, write to the Free Software -+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+// -+// In addition, as a special exception, the copyright holders give -+// permission to link the code of portions of this program with the -+// OpenSSL library under certain conditions as described in each -+// individual source file, and distribute linked combinations -+// including the two. -+// -+// You must obey the GNU General Public License in all respects for -+// all of the code used other than OpenSSL. If you modify file(s) -+// with this exception, you may extend this exception to your version -+// of the file(s), but you are not obligated to do so. If you do not -+// wish to do so, delete this exception statement from your version. -+// If you delete this exception statement from all source files in the -+// program, then also delete it here. -+// -+// Contact: Jari Sundell <jaris@ifi.uio.no> -+// -+// Skomakerveien 33 -+// 3185 Skoppum, NORWAY -+ -+// A simple string with no constructors (i.e. POD) plus a derived -+// class with constructors and conversion operators. In most cases, -+// SimpleString is the class to use, except in unions where it needs -+// to be SimpleStringBase. -+// -+// For efficient conversion from C string literals, depends on the -+// compiler optimizing strlen("literal") to an integer literal. -+// Then a comparison with either a C string literal or a SimpleString -+// literal is a memcmp call plus (if equal) a comparison of the lengths. -+ -+#ifndef LIBTORRENT_SIMPLE_STRING_H -+#define LIBTORRENT_SIMPLE_STRING_H -+ -+#include <cstring> -+#include <memory> -+#include <string> -+#include <torrent/common.h> -+ -+namespace torrent { -+ -+// Simple string base class (POD). -+struct LIBTORRENT_EXPORT SimpleStringBase { -+ int cmp(const SimpleStringBase& other) const; -+ -+ char operator [] (size_t index) const { return m_data[index]; } -+ -+ const char* begin() const { return m_data; } -+ const char* end() const { return m_data + m_length; } -+ -+ // NOTE: Unlike std::string, SimpleString's c_str() is NOT guaranteed to be zero-terminated! -+ const char* c_str() const { return m_data; } -+ const char* data() const { return m_data; } -+ -+ bool empty() const { return !m_length; } -+ size_t length() const { return m_length; } -+ size_t size() const { return m_length; } -+ -+ std::string str() const { return std::string(m_data, m_length); } -+ std::string substr(size_t pos = 0, size_t n = npos) const { return std::string(m_data + pos, std::min(m_length - pos, n)); } -+ -+ // Allocates a copy of the string and returns it. -+ SimpleStringBase copy() const; -+ -+ static const size_t npos = static_cast<size_t>(-1); -+ -+protected: -+ const char* m_data; -+ size_t m_length; -+}; -+ -+// Conversion helper class, we don't want constructors -+// in the base class to be able to put it in a union. -+struct LIBTORRENT_EXPORT SimpleString : public SimpleStringBase { -+ typedef SimpleStringBase base_type; -+ -+ SimpleString() { m_data = ""; m_length = 0; } -+ SimpleString(const base_type& s) { m_data = s.c_str(); m_length = s.length(); } -+ SimpleString(const std::string& s) { m_data = s.c_str(); m_length = s.length(); } -+ SimpleString(const char* s) { m_data = s; m_length = strlen(s); } -+ SimpleString(const char* s, size_t l) { m_data = s; m_length = l; } -+}; -+ -+inline int -+SimpleStringBase::cmp(const SimpleStringBase& other) const { -+ int cmp = memcmp(m_data, other.m_data, std::min(m_length, other.m_length)); -+ return cmp ? cmp : m_length - other.m_length; -+} -+ -+inline SimpleStringBase -+SimpleStringBase::copy() const { -+ char* data = new char[m_length + 1]; -+ memcpy(data, m_data, m_length); -+ data[m_length] = 0; -+ return SimpleString(data, m_length); -+} -+ -+inline bool operator == (const SimpleStringBase& one, const SimpleStringBase& other) { return one.cmp(other) == 0; } -+inline bool operator != (const SimpleStringBase& one, const SimpleStringBase& other) { return one.cmp(other) != 0; } -+inline bool operator <= (const SimpleStringBase& one, const SimpleStringBase& other) { return one.cmp(other) <= 0; } -+inline bool operator < (const SimpleStringBase& one, const SimpleStringBase& other) { return one.cmp(other) < 0; } -+inline bool operator >= (const SimpleStringBase& one, const SimpleStringBase& other) { return one.cmp(other) >= 0; } -+inline bool operator > (const SimpleStringBase& one, const SimpleStringBase& other) { return one.cmp(other) > 0; } -+ -+inline bool operator == (const SimpleStringBase& one, const char* other) { return one.cmp(SimpleString(other)) == 0; } -+inline bool operator != (const SimpleStringBase& one, const char* other) { return one.cmp(SimpleString(other)) != 0; } -+inline bool operator <= (const SimpleStringBase& one, const char* other) { return one.cmp(SimpleString(other)) <= 0; } -+inline bool operator < (const SimpleStringBase& one, const char* other) { return one.cmp(SimpleString(other)) < 0; } -+inline bool operator >= (const SimpleStringBase& one, const char* other) { return one.cmp(SimpleString(other)) >= 0; } -+inline bool operator > (const SimpleStringBase& one, const char* other) { return one.cmp(SimpleString(other)) > 0; } -+ -+} -+ -+#endif -diff --git a/src/torrent/static_map.cc b/src/torrent/static_map.cc -new file mode 100644 -index 0000000..b71f257 ---- /dev/null -+++ b/src/torrent/static_map.cc -@@ -0,0 +1,123 @@ -+// libTorrent - BitTorrent library -+// Copyright (C) 2005-2008, Jari Sundell -+// -+// This program is free software; you can redistribute it and/or modify -+// it under the terms of the GNU General Public License as published by -+// the Free Software Foundation; either version 2 of the License, or -+// (at your option) any later version. -+// -+// This program is distributed in the hope that it will be useful, -+// but WITHOUT ANY WARRANTY; without even the implied warranty of -+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+// GNU General Public License for more details. -+// -+// You should have received a copy of the GNU General Public License -+// along with this program; if not, write to the Free Software -+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+// -+// In addition, as a special exception, the copyright holders give -+// permission to link the code of portions of this program with the -+// OpenSSL library under certain conditions as described in each -+// individual source file, and distribute linked combinations -+// including the two. -+// -+// You must obey the GNU General Public License in all respects for -+// all of the code used other than OpenSSL. If you modify file(s) -+// with this exception, you may extend this exception to your version -+// of the file(s), but you are not obligated to do so. If you do not -+// wish to do so, delete this exception statement from your version. -+// If you delete this exception statement from all source files in the -+// program, then also delete it here. -+// -+// Contact: Jari Sundell <jaris@ifi.uio.no> -+// -+// Skomakerveien 33 -+// 3185 Skoppum, NORWAY -+ -+#include "config.h" -+ -+#include "static_map.h" -+ -+namespace torrent { -+ -+inline int -+StaticMapKeys::check_key_order(SimpleString key) { -+ int cmp = empty() ? -1 : back().key().cmp(key); -+ if (cmp > 0) { -+ if (type() == TYPE_LIST) -+ cmp = -1; // List order is given by indices, not alphabetically. -+ else -+ throw internal_error("StaticMapKeys::StaticMapKeys() called with unsorted keys."); -+ } -+ -+ return cmp; -+} -+ -+StaticMapKeys::StaticMapKeys(const mapping_type* key_list, size_t length) -+ : m_key(SimpleString("root", 4)), -+ m_indexBegin(0), -+ m_indexEnd(0), -+ m_type(key_list[0].key[0] == '[' ? TYPE_LIST : TYPE_DICT) { -+ -+ for (size_t index = 0; index < length; index++, key_list++) { -+ if (key_list->index != index) -+ throw internal_error("StaticMapKeys::StaticMapKeys() used with list not in index order."); -+ -+ StaticMapKeys* curMap = this; -+ const char* key = key_list->key; -+ while (key != NULL && *key) { -+ curMap->set_end(index + 1); -+ -+ const char* sep = key + 1 + strcspn(key + 1, ":["); -+ SimpleString keyStr(key, sep - key); -+ -+ // New key, in correct order? Or same key as before? -+ int cmp = curMap->check_key_order(keyStr); -+ -+ if (sep[0] == 0) { -+ curMap->insert(curMap->end(), StaticMapKeys(keyStr, TYPE_VALUE, index, index + 1)); -+ break; -+ -+ } else if (sep[0] == '[' && sep[1] == ']' && sep[2] == 0) { -+ curMap->insert(curMap->end(), StaticMapKeys(keyStr, TYPE_BENCODE_LIST, index, index + 1)); -+ break; -+ -+ } else if (sep[0] == ':' && sep[1] == ':' && sep[2] == 0) { -+ curMap->insert(curMap->end(), StaticMapKeys(keyStr, TYPE_BENCODE, index, index + 1)); -+ break; -+ } -+ -+ if (sep[0] == ':' && sep[1] == ':') { -+ if (cmp < 0) -+ curMap->insert(curMap->end(), StaticMapKeys(keyStr, TYPE_DICT, index, index + 1)); -+ else if (curMap->back().type() != TYPE_DICT) -+ throw internal_error("StaticMapKeys::StaticMapKeys() called with a mixed dictionary/list entry."); -+ -+ curMap = &curMap->back(); -+ key = sep + 2; -+ -+ } else if (sep[0] == '[' && sep[1] >= '0' && sep[1] <= '9') { -+ key = sep++; -+ while (*sep >= '0' && *sep <= '9') -+ ++sep; -+ if (*sep != ']') -+ throw internal_error("StaticMapKeys::StaticMapKeys() called with invalid list index."); -+ -+ if (cmp < 0) -+ curMap->insert(curMap->end(), StaticMapKeys(keyStr, TYPE_LIST, index, index + 1)); -+ else if (curMap->back().type() != TYPE_LIST) -+ throw internal_error("StaticMapKeys::StaticMapKeys() called with a mixed dictionary/list entry."); -+ -+ curMap = &curMap->back(); -+ -+ } else { -+ throw internal_error("StaticMapKeys::StaticMapKeys() called with unsupported key type."); -+ } -+ } -+ } -+ -+ if (index_end() != length) -+ throw internal_error("StaticMapKeys::StaticMapKeys() is missing values."); -+} -+ -+} -diff --git a/src/torrent/static_map.h b/src/torrent/static_map.h -new file mode 100644 -index 0000000..d862f16 ---- /dev/null -+++ b/src/torrent/static_map.h -@@ -0,0 +1,158 @@ -+// libTorrent - BitTorrent library -+// Copyright (C) 2005-2008, Jari Sundell -+// -+// This program is free software; you can redistribute it and/or modify -+// it under the terms of the GNU General Public License as published by -+// the Free Software Foundation; either version 2 of the License, or -+// (at your option) any later version. -+// -+// This program is distributed in the hope that it will be useful, -+// but WITHOUT ANY WARRANTY; without even the implied warranty of -+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+// GNU General Public License for more details. -+// -+// You should have received a copy of the GNU General Public License -+// along with this program; if not, write to the Free Software -+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+// -+// In addition, as a special exception, the copyright holders give -+// permission to link the code of portions of this program with the -+// OpenSSL library under certain conditions as described in each -+// individual source file, and distribute linked combinations -+// including the two. -+// -+// You must obey the GNU General Public License in all respects for -+// all of the code used other than OpenSSL. If you modify file(s) -+// with this exception, you may extend this exception to your version -+// of the file(s), but you are not obligated to do so. If you do not -+// wish to do so, delete this exception statement from your version. -+// If you delete this exception statement from all source files in the -+// program, then also delete it here. -+// -+// Contact: Jari Sundell <jaris@ifi.uio.no> -+// -+// Skomakerveien 33 -+// 3185 Skoppum, NORWAY -+ -+#ifndef LIBTORRENT_STATIC_MAP_H -+#define LIBTORRENT_STATIC_MAP_H -+ -+#include <vector> -+#include <torrent/common.h> -+#include <torrent/object.h> -+#include <torrent/simple_string.h> -+ -+// StaticMap: holds a pre-defined subset of possible bencode keys and stores -+// their values in a flat array for fast decoding, key access and encoding. -+// Makes no copies, so the underlying data buffer must outlive the map object. -+ -+// With this, the complexity for bencoding and bdecoding a StaticMap object -+// is O(n). The access to any of the pre-defined keys is O(1). Access to -+// other keys is not supported, they are dropped while bdecoding. Decoded -+// Object types are either VALUE, SSTRING or NONE (if key was not present). -+ -+// To use, define an enum of all required keys, and use this type along with -+// the number of possible keys in the StaticMap template arguments. Define -+// the enum -> key string as array of StaticMapKeys::mapping_type. Define -+// the static keyMap variable, most simply by defining base_type in your -+// derived map class, like this: -+// template<> const Derived::key_map_init Derived::base_type::keyMap(key_list); -+ -+// The argument of the constructor of this static keyMap object is a list -+// of mapping_type entries. For efficiency, they must be ordered in -+// increasing number of the index, and increasing alphabetical order -+// (or more specifically, the bencode order) at the same time. In other words, -+// the original enum must also be in alphabetical order of the keys the enum -+// values refer to. -+ -+// Format of the key specifications ("..." may contain any number of further keys): -+// "foo::..." makes foo a bencode dictionary -+// "foo[0]..." makes foo a bencode list -+// "foo::" makes foo an undecoded bencode value (may contain arbitrary bencode data) -+// "foo[]" makes foo an undecoded list of bencode values (like the above but adding the 'l' and 'e' indicators) -+// "foo" makes foo an integer or string value (automatic) -+// -+// Examples: -+// "baz" refers to a single value for key "baz" -+// "foo::a[0]::bar" refers to a single value for key "bar" in the dictionary at index 0 of the list for key "a" in dictionary "foo" -+// "foo::a[1]" refers to a single value at index 1 of the list for key "a" in the dictionary "foo" -+// "zoo::" refers to a bdecoded value for key "zoo" -+// -+// If the four values are 4, 5, "6" and 7, this would be bencoded as d3:bazi4e3:food1:ald3:bari5ee1:6ee3:zooi7ee -+// -+// Note that sparse lists are not possible, you must explicitly specify all needed entries starting from index 0, -+// and when bencoding, the first unset value terminates the list. -+ -+namespace torrent { -+ -+// Hierarchical structure mapping bencode keys to flat array indices. -+class LIBTORRENT_EXPORT StaticMapKeys : public std::vector<StaticMapKeys> { -+public: -+ typedef std::vector<StaticMapKeys> base_type; -+ -+ struct mapping_type { -+ size_t index; -+ const char* key; -+ }; -+ -+ enum value_type { -+ TYPE_VALUE, -+ TYPE_LIST, -+ TYPE_DICT, -+ TYPE_BENCODE, -+ TYPE_BENCODE_LIST, -+ }; -+ -+ StaticMapKeys(const mapping_type* key_list, size_t length); -+ -+ void set_end(size_t end) { m_indexEnd = end; } -+ -+ size_t index_begin() const { return m_indexBegin; } -+ size_t index_end() const { return m_indexEnd; } -+ -+ value_type type() const { return m_type; } -+ -+ SimpleString key() const { return m_key; } -+ -+private: -+ StaticMapKeys(SimpleString key, value_type type, size_t begin, size_t end) -+ : m_key(key), m_indexBegin(begin), m_indexEnd(end), m_type(type) {} -+ -+ int check_key_order(SimpleString key); -+ -+ SimpleString m_key; -+ size_t m_indexBegin; -+ size_t m_indexEnd; -+ value_type m_type; -+}; -+ -+template<typename tmpl_key_type, size_t tmpl_length> -+class LIBTORRENT_EXPORT StaticMap { -+public: -+ typedef Object& value_type; -+ typedef tmpl_key_type key_type; -+ typedef StaticMapKeys key_map_type; -+ typedef Object list_type[tmpl_length]; -+ -+ Object& operator [] (key_type key) { return m_values[key]; } -+ const Object& operator [] (key_type key) const { return m_values[key]; } -+ -+ const key_map_type& map() const { return keyMap; } -+ -+ list_type& values() { return m_values; } -+ const list_type& values() const { return m_values; } -+ -+ static const size_t length = tmpl_length; -+ -+private: -+ struct key_map_init : public key_map_type { -+ key_map_init(key_map_type::mapping_type* key_list) : key_map_type(key_list, tmpl_length) {}; -+ }; -+ static const key_map_init keyMap; -+ -+ list_type m_values; -+}; -+ -+} -+ -+#endif -diff --git a/src/tracker/tracker_dht.cc b/src/tracker/tracker_dht.cc -index c63ce58..309fcf2 100644 ---- a/src/tracker/tracker_dht.cc -+++ b/src/tracker/tracker_dht.cc -@@ -115,13 +115,11 @@ TrackerDht::type() const { - } - - void --TrackerDht::receive_peers(const Object& peer_list) { -+TrackerDht::receive_peers(SimpleString peers) { - if (!is_busy()) - throw internal_error("TrackerDht::receive_peers called while not busy."); - -- Object::list_type peers = peer_list.as_list(); -- for (Object::list_type::const_iterator itr = peers.begin(); itr != peers.end(); ++itr) -- m_peers.parse_address_compact(itr->as_string()); -+ m_peers.parse_address_bencode(peers); - } - - void -diff --git a/src/tracker/tracker_dht.h b/src/tracker/tracker_dht.h -index d197e61..d096b46 100644 ---- a/src/tracker/tracker_dht.h -+++ b/src/tracker/tracker_dht.h -@@ -71,7 +71,7 @@ public: - - bool has_peers() const { return !m_peers.empty(); } - -- void receive_peers(const Object& peer_list); -+ void receive_peers(SimpleString peers); - void receive_success(); - void receive_failed(const char* msg); - void receive_progress(int replied, int contacted); diff --git a/libtorrent-extended/ipv6.patch b/libtorrent-extended/ipv6.patch deleted file mode 100644 index 7da4ebd..0000000 --- a/libtorrent-extended/ipv6.patch +++ /dev/null @@ -1,1235 +0,0 @@ -diff --git a/rak/socket_address.h b/rak/socket_address.h -index 25fdb37..d38533e 100644 ---- a/rak/socket_address.h -+++ b/rak/socket_address.h -@@ -145,7 +145,7 @@ private: - }; - }; - --// Remeber to set the AF_INET. -+// Remember to set the AF_INET. - - class socket_address_inet { - public: -@@ -184,6 +184,10 @@ public: - - const sockaddr* c_sockaddr() const { return reinterpret_cast<const sockaddr*>(&m_sockaddr); } - const sockaddr_in* c_sockaddr_inet() const { return &m_sockaddr; } -+ -+#ifdef RAK_USE_INET6 -+ socket_address_inet6 to_mapped_address() const; -+#endif - - bool operator == (const socket_address_inet& rhs) const; - bool operator < (const socket_address_inet& rhs) const; -@@ -192,6 +196,52 @@ private: - struct sockaddr_in m_sockaddr; - }; - -+#ifdef RAK_USE_INET6 -+// Remember to set the AF_INET6. -+ -+class socket_address_inet6 { -+public: -+ bool is_any() const { return is_port_any() && is_address_any(); } -+ bool is_valid() const { return !is_port_any() && !is_address_any(); } -+ bool is_port_any() const { return port() == 0; } -+ bool is_address_any() const { return std::memcmp(&m_sockaddr.sin6_addr, &in6addr_any, sizeof(in6_addr)) == 0; } -+ -+ void clear() { std::memset(this, 0, sizeof(socket_address_inet6)); set_family(); } -+ -+ uint16_t port() const { return ntohs(m_sockaddr.sin6_port); } -+ uint16_t port_n() const { return m_sockaddr.sin6_port; } -+ void set_port(uint16_t p) { m_sockaddr.sin6_port = htons(p); } -+ void set_port_n(uint16_t p) { m_sockaddr.sin6_port = p; } -+ -+ in6_addr address() const { return m_sockaddr.sin6_addr; } -+ std::string address_str() const; -+ bool address_c_str(char* buf, socklen_t size) const; -+ -+ void set_address(in6_addr a) { m_sockaddr.sin6_addr = a; } -+ bool set_address_str(const std::string& a) { return set_address_c_str(a.c_str()); } -+ bool set_address_c_str(const char* a); -+ -+ void set_address_any() { set_port(0); set_address(in6addr_any); } -+ -+ sa_family_t family() const { return m_sockaddr.sin6_family; } -+ void set_family() { m_sockaddr.sin6_family = AF_INET6; } -+ -+ sockaddr* c_sockaddr() { return reinterpret_cast<sockaddr*>(&m_sockaddr); } -+ sockaddr_in6* c_sockaddr_inet6() { return &m_sockaddr; } -+ -+ const sockaddr* c_sockaddr() const { return reinterpret_cast<const sockaddr*>(&m_sockaddr); } -+ const sockaddr_in6* c_sockaddr_inet6() const { return &m_sockaddr; } -+ -+ socket_address normalize_address() const; -+ -+ bool operator == (const socket_address_inet6& rhs) const; -+ bool operator < (const socket_address_inet6& rhs) const; -+ -+private: -+ struct sockaddr_in6 m_sockaddr; -+}; -+#endif -+ - // Unique key for the address, excluding port numbers etc. - class socket_address_key { - public: -@@ -241,8 +291,10 @@ socket_address::is_valid() const { - switch (family()) { - case af_inet: - return sa_inet()->is_valid(); --// case af_inet6: --// return sa_inet6().is_valid(); -+#ifdef RAK_USE_INET6 -+ case af_inet6: -+ return sa_inet6()->is_valid(); -+#endif - default: - return false; - } -@@ -253,6 +305,10 @@ socket_address::is_bindable() const { - switch (family()) { - case af_inet: - return !sa_inet()->is_address_any(); -+#ifdef RAK_USE_INET6 -+ case af_inet6: -+ return !sa_inet6()->is_address_any(); -+#endif - default: - return false; - } -@@ -263,6 +319,10 @@ socket_address::is_address_any() const { - switch (family()) { - case af_inet: - return sa_inet()->is_address_any(); -+#ifdef RAK_USE_INET6 -+ case af_inet6: -+ return sa_inet6()->is_address_any(); -+#endif - default: - return true; - } -@@ -273,6 +333,10 @@ socket_address::port() const { - switch (family()) { - case af_inet: - return sa_inet()->port(); -+#ifdef RAK_USE_INET6 -+ case af_inet6: -+ return sa_inet6()->port(); -+#endif - default: - return 0; - } -@@ -283,6 +347,10 @@ socket_address::set_port(uint16_t p) { - switch (family()) { - case af_inet: - return sa_inet()->set_port(p); -+#ifdef RAK_USE_INET6 -+ case af_inet6: -+ return sa_inet6()->set_port(p); -+#endif - default: - break; - } -@@ -293,6 +361,10 @@ socket_address::address_str() const { - switch (family()) { - case af_inet: - return sa_inet()->address_str(); -+#ifdef RAK_USE_INET6 -+ case af_inet6: -+ return sa_inet6()->address_str(); -+#endif - default: - return std::string(); - } -@@ -303,6 +375,10 @@ socket_address::address_c_str(char* buf, socklen_t size) const { - switch (family()) { - case af_inet: - return sa_inet()->address_c_str(buf, size); -+#ifdef RAK_USE_INET6 -+ case af_inet6: -+ return sa_inet6()->address_c_str(buf, size); -+#endif - default: - return false; - } -@@ -314,6 +390,12 @@ socket_address::set_address_c_str(const char* a) { - sa_inet()->set_family(); - return true; - -+#ifdef RAK_USE_INET6 -+ } else if (sa_inet6()->set_address_c_str(a)) { -+ sa_inet6()->set_family(); -+ return true; -+#endif -+ - } else { - return false; - } -@@ -325,6 +407,10 @@ socket_address::length() const { - switch(family()) { - case af_inet: - return sizeof(sockaddr_in); -+#ifdef RAK_USE_INET6 -+ case af_inet6: -+ return sizeof(sockaddr_in6); -+#endif - default: - return 0; - } -@@ -349,8 +435,10 @@ socket_address::operator == (const socket_address& rhs) const { - switch (family()) { - case af_inet: - return *sa_inet() == *rhs.sa_inet(); --// case af_inet6: --// return *sa_inet6() == *rhs.sa_inet6(); -+#ifdef RAK_USE_INET6 -+ case af_inet6: -+ return *sa_inet6() == *rhs.sa_inet6(); -+#endif - default: - throw std::logic_error("socket_address::operator == (rhs) invalid type comparison."); - } -@@ -364,8 +452,10 @@ socket_address::operator < (const socket_address& rhs) const { - switch (family()) { - case af_inet: - return *sa_inet() < *rhs.sa_inet(); --// case af_inet6: --// return *sa_inet6() < *rhs.sa_inet6(); -+#ifdef RAK_USE_INET6 -+ case af_inet6: -+ return *sa_inet6() < *rhs.sa_inet6(); -+#endif - default: - throw std::logic_error("socket_address::operator < (rhs) invalid type comparison."); - } -@@ -391,6 +481,23 @@ socket_address_inet::set_address_c_str(const char* a) { - return inet_pton(AF_INET, a, &m_sockaddr.sin_addr); - } - -+#ifdef RAK_USE_INET6 -+inline socket_address_inet6 -+socket_address_inet::to_mapped_address() const { -+ uint32_t addr32[4]; -+ addr32[0] = 0; -+ addr32[1] = 0; -+ addr32[2] = htonl(0xffff); -+ addr32[3] = m_sockaddr.sin_addr.s_addr; -+ -+ socket_address_inet6 sa; -+ sa.clear(); -+ sa.set_address(*reinterpret_cast<in6_addr *>(addr32)); -+ sa.set_port_n(m_sockaddr.sin_port); -+ return sa; -+} -+#endif -+ - inline bool - socket_address_inet::operator == (const socket_address_inet& rhs) const { - return -@@ -406,6 +513,59 @@ socket_address_inet::operator < (const socket_address_inet& rhs) const { - m_sockaddr.sin_port < rhs.m_sockaddr.sin_port); - } - -+#ifdef RAK_USE_INET6 -+ -+inline std::string -+socket_address_inet6::address_str() const { -+ char buf[INET6_ADDRSTRLEN]; -+ -+ if (!address_c_str(buf, INET6_ADDRSTRLEN)) -+ return std::string(); -+ -+ return std::string(buf); -+} -+ -+inline bool -+socket_address_inet6::address_c_str(char* buf, socklen_t size) const { -+ return inet_ntop(family(), &m_sockaddr.sin6_addr, buf, size); -+} -+ -+inline bool -+socket_address_inet6::set_address_c_str(const char* a) { -+ return inet_pton(AF_INET6, a, &m_sockaddr.sin6_addr); -+} -+ -+inline socket_address -+socket_address_inet6::normalize_address() const { -+ const uint32_t *addr32 = reinterpret_cast<const uint32_t *>(m_sockaddr.sin6_addr.s6_addr); -+ if (addr32[0] == 0 && addr32[1] == 0 && addr32[2] == htonl(0xffff)) { -+ socket_address addr4; -+ addr4.sa_inet()->set_family(); -+ addr4.sa_inet()->set_address_n(addr32[3]); -+ addr4.sa_inet()->set_port_n(m_sockaddr.sin6_port); -+ return addr4; -+ } -+ return *reinterpret_cast<const socket_address*>(this); -+} -+ -+inline bool -+socket_address_inet6::operator == (const socket_address_inet6& rhs) const { -+ return -+ memcmp(&m_sockaddr.sin6_addr, &rhs.m_sockaddr.sin6_addr, sizeof(in6_addr)) == 0 && -+ m_sockaddr.sin6_port == rhs.m_sockaddr.sin6_port; -+} -+ -+inline bool -+socket_address_inet6::operator < (const socket_address_inet6& rhs) const { -+ int addr_comp = memcmp(&m_sockaddr.sin6_addr, &rhs.m_sockaddr.sin6_addr, sizeof(in6_addr)); -+ return -+ addr_comp < 0 || -+ (addr_comp == 0 || -+ m_sockaddr.sin6_port < rhs.m_sockaddr.sin6_port); -+} -+ -+#endif -+ - } - - #endif -diff --git a/src/dht/dht_node.cc b/src/dht/dht_node.cc -index 9d51a28..e0eb306 100644 ---- a/src/dht/dht_node.cc -+++ b/src/dht/dht_node.cc -@@ -55,8 +55,15 @@ DhtNode::DhtNode(const HashString& id, const rak::socket_address* sa) : - m_recentlyInactive(0), - m_bucket(NULL) { - -+#ifdef RAK_USE_INET6 -+ if (sa->family() != rak::socket_address::af_inet && -+ (sa->family() != rak::socket_address::af_inet6 || -+ !sa->sa_inet6()->is_any())) -+ throw resource_error("Address not af_inet or in6addr_any"); -+#else - if (sa->family() != rak::socket_address::af_inet) - throw resource_error("Address not af_inet"); -+#endif - } - - DhtNode::DhtNode(const SimpleString& id, const Object& cache) : -@@ -85,8 +92,20 @@ DhtNode::store_compact(char* buffer) const { - - Object* - DhtNode::store_cache(Object* container) const { -- container->insert_key("i", m_socketAddress.sa_inet()->address_h()); -- container->insert_key("p", m_socketAddress.sa_inet()->port()); -+#ifdef RAK_USE_INET6 -+ if (m_socketAddress.family() == rak::socket_address::af_inet6) { -+ // Currently, all we support is in6addr_any (checked in the constructor), -+ // which is effectively equivalent to this. Note that we need to specify -+ // int64_t explicitly here because a zero constant is special in C++ and -+ // thus we need an explicit match. -+ container->insert_key("i", int64_t(0)); -+ container->insert_key("p", m_socketAddress.sa_inet6()->port()); -+ } else -+#endif -+ { -+ container->insert_key("i", m_socketAddress.sa_inet()->address_h()); -+ container->insert_key("p", m_socketAddress.sa_inet()->port()); -+ } - container->insert_key("t", m_lastSeen); - return container; - } -diff --git a/src/dht/dht_server.cc b/src/dht/dht_server.cc -index 256b92b..8c8b380 100644 ---- a/src/dht/dht_server.cc -+++ b/src/dht/dht_server.cc -@@ -692,6 +692,16 @@ DhtServer::event_read() { - if (read < 0) - break; - -+#ifdef RAK_USE_INET6 -+ // We can currently only process mapped-IPv4 addresses, not real IPv6. -+ // Translate them to an af_inet socket_address. -+ if (sa.family() == rak::socket_address::af_inet6) -+ sa = sa.sa_inet6()->normalize_address(); -+#endif -+ -+ if (sa.family() != rak::socket_address::af_inet) -+ continue; -+ - total += read; - - // If it's not a valid bencode dictionary at all, it's probably not a DHT -diff --git a/src/download/download_info.h b/src/download/download_info.h -index 68fb178..2e27ba3 100644 ---- a/src/download/download_info.h -+++ b/src/download/download_info.h -@@ -216,6 +216,28 @@ struct SocketAddressCompact { - const char* c_str() const { return reinterpret_cast<const char*>(this); } - } __attribute__ ((packed)); - -+#ifdef RAK_USE_INET6 -+struct SocketAddressCompact6 { -+ SocketAddressCompact6() {} -+ SocketAddressCompact6(in6_addr a, uint16_t p) : addr(a), port(p) {} -+ SocketAddressCompact6(const rak::socket_address_inet6* sa) : addr(sa->address()), port(sa->port_n()) {} -+ -+ operator rak::socket_address () const { -+ rak::socket_address sa; -+ sa.sa_inet6()->clear(); -+ sa.sa_inet6()->set_port_n(port); -+ sa.sa_inet6()->set_address(addr); -+ -+ return sa; -+ } -+ -+ in6_addr addr; -+ uint16_t port; -+ -+ const char* c_str() const { return reinterpret_cast<const char*>(this); } -+} __attribute__ ((packed)); -+#endif -+ - } - - #endif -diff --git a/src/net/Makefile.am b/src/net/Makefile.am -index e07f382..3df0f6d 100644 ---- a/src/net/Makefile.am -+++ b/src/net/Makefile.am -@@ -4,6 +4,8 @@ libsub_net_la_SOURCES = \ - address_list.cc \ - address_list.h \ - data_buffer.h \ -+ local_addr.cc \ -+ local_addr.h \ - listen.cc \ - listen.h \ - protocol_buffer.h \ -diff --git a/src/net/Makefile.in b/src/net/Makefile.in -index 1920403..cdbe398 100644 ---- a/src/net/Makefile.in -+++ b/src/net/Makefile.in -@@ -54,9 +54,9 @@ CONFIG_CLEAN_FILES = - CONFIG_CLEAN_VPATH_FILES = - LTLIBRARIES = $(noinst_LTLIBRARIES) - libsub_net_la_LIBADD = --am_libsub_net_la_OBJECTS = address_list.lo listen.lo socket_base.lo \ -- socket_datagram.lo socket_fd.lo socket_set.lo socket_stream.lo \ -- throttle_internal.lo throttle_list.lo -+am_libsub_net_la_OBJECTS = address_list.lo local_addr.lo listen.lo \ -+ socket_base.lo socket_datagram.lo socket_fd.lo socket_set.lo \ -+ socket_stream.lo throttle_internal.lo throttle_list.lo - libsub_net_la_OBJECTS = $(am_libsub_net_la_OBJECTS) - DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) - depcomp = $(SHELL) $(top_srcdir)/depcomp -@@ -216,6 +216,8 @@ libsub_net_la_SOURCES = \ - address_list.cc \ - address_list.h \ - data_buffer.h \ -+ local_addr.cc \ -+ local_addr.h \ - listen.cc \ - listen.h \ - protocol_buffer.h \ -@@ -290,6 +292,7 @@ distclean-compile: - - @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/address_list.Plo@am__quote@ - @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/listen.Plo@am__quote@ -+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/local_addr.Plo@am__quote@ - @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket_base.Plo@am__quote@ - @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket_datagram.Plo@am__quote@ - @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket_fd.Plo@am__quote@ -diff --git a/src/net/address_list.cc b/src/net/address_list.cc -index e5cf3cb..a7dff2c 100644 ---- a/src/net/address_list.cc -+++ b/src/net/address_list.cc -@@ -92,5 +92,16 @@ AddressList::parse_address_bencode(SimpleString s) { - s = SimpleString(s.c_str() + 2 + sizeof(SocketAddressCompact), s.length() - 2 - sizeof(SocketAddressCompact)); - } - } -+#ifdef RAK_USE_INET6 -+void -+AddressList::parse_address_compact_ipv6(const std::string& s) { -+ if (sizeof(const SocketAddressCompact6) != 18) -+ throw internal_error("ConnectionList::AddressList::parse_address_compact_ipv6(...) bad struct size."); -+ -+ std::copy(reinterpret_cast<const SocketAddressCompact6*>(s.c_str()), -+ reinterpret_cast<const SocketAddressCompact6*>(s.c_str() + s.size() - s.size() % sizeof(SocketAddressCompact6)), -+ std::back_inserter(*this)); -+} -+#endif - - } -diff --git a/src/net/address_list.h b/src/net/address_list.h -index 10dbac4..3aec366 100644 ---- a/src/net/address_list.h -+++ b/src/net/address_list.h -@@ -51,6 +51,9 @@ public: - // Parse normal or compact list of addresses and add to AddressList - void parse_address_normal(const Object::list_type& b); - void parse_address_compact(SimpleString s); -+#ifdef RAK_USE_INET6 -+ void parse_address_compact_ipv6(const std::string& s); -+#endif - void parse_address_bencode(SimpleString s); - - private: -diff --git a/src/net/local_addr.cc b/src/net/local_addr.cc -new file mode 100644 -index 0000000..fae3f85 ---- /dev/null -+++ b/src/net/local_addr.cc -@@ -0,0 +1,336 @@ -+// libTorrent - BitTorrent library -+// Copyright (C) 2005-2007, Jari Sundell -+// -+// This program is free software; you can redistribute it and/or modify -+// it under the terms of the GNU General Public License as published by -+// the Free Software Foundation; either version 2 of the License, or -+// (at your option) any later version. -+// -+// This program is distributed in the hope that it will be useful, -+// but WITHOUT ANY WARRANTY; without even the implied warranty of -+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+// GNU General Public License for more details. -+// -+// You should have received a copy of the GNU General Public License -+// along with this program; if not, write to the Free Software -+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+// -+// In addition, as a special exception, the copyright holders give -+// permission to link the code of portions of this program with the -+// OpenSSL library under certain conditions as described in each -+// individual source file, and distribute linked combinations -+// including the two. -+// -+// You must obey the GNU General Public License in all respects for -+// all of the code used other than OpenSSL. If you modify file(s) -+// with this exception, you may extend this exception to your version -+// of the file(s), but you are not obligated to do so. If you do not -+// wish to do so, delete this exception statement from your version. -+// If you delete this exception statement from all source files in the -+// program, then also delete it here. -+// -+// Contact: Jari Sundell <jaris@ifi.uio.no> -+// -+// Skomakerveien 33 -+// 3185 Skoppum, NORWAY -+ -+#include "config.h" -+ -+#include <stdio.h> -+#include <ifaddrs.h> -+#include <rak/socket_address.h> -+#include <sys/types.h> -+#include <errno.h> -+ -+#ifdef __linux__ -+#include <linux/netlink.h> -+#include <linux/rtnetlink.h> -+#endif -+ -+#include "torrent/exceptions.h" -+#include "socket_fd.h" -+#include "local_addr.h" -+ -+namespace torrent { -+namespace { -+ -+// IPv4 priority, from highest to lowest: -+// -+// 1. Everything else (global address) -+// 2. Private address space (10.0.0.0/8, 172.16.0.0/16, 192.168.0.0/24) -+// 3. Empty/INADDR_ANY (0.0.0.0) -+// 4. Link-local address (169.254.0.0/16) -+// 5. Localhost (127.0.0.0/8) -+int get_priority_ipv4(const in_addr& addr) { -+ if ((addr.s_addr & htonl(0xff000000U)) == htonl(0x7f000000U)) { -+ return 5; -+ } -+ if (addr.s_addr == htonl(0)) { -+ return 4; -+ } -+ if ((addr.s_addr & htonl(0xffff0000U)) == htonl(0xa9fe0000U)) { -+ return 3; -+ } -+ if ((addr.s_addr & htonl(0xff000000U)) == htonl(0x0a000000U) || -+ (addr.s_addr & htonl(0xffff0000U)) == htonl(0xac100000U) || -+ (addr.s_addr & htonl(0xffff0000U)) == htonl(0xc0a80000U)) { -+ return 2; -+ } -+ return 1; -+} -+ -+#ifdef RAK_USE_INET6 -+// IPv6 priority, from highest to lowest: -+// -+// 1. Global address (2000::/16 not in 6to4 or Teredo) -+// 2. 6to4 (2002::/16) -+// 3. Teredo (2001::/32) -+// 4. Empty/INADDR_ANY (::) -+// 5. Everything else (link-local, ULA, etc.) -+int get_priority_ipv6(const in6_addr& addr) { -+ const uint32_t *addr32 = reinterpret_cast<const uint32_t *>(addr.s6_addr); -+ if (addr32[0] == htonl(0) && -+ addr32[1] == htonl(0) && -+ addr32[2] == htonl(0) && -+ addr32[3] == htonl(0)) { -+ return 4; -+ } -+ if (addr32[0] == htonl(0x20010000)) { -+ return 3; -+ } -+ if ((addr32[0] & htonl(0xffff0000)) == htonl(0x20020000)) { -+ return 2; -+ } -+ if ((addr32[0] & htonl(0xe0000000)) == htonl(0x20000000)) { -+ return 1; -+ } -+ return 5; -+} -+#endif -+ -+int get_priority(const rak::socket_address& addr) { -+ switch (addr.family()) { -+ case AF_INET: -+ return get_priority_ipv4(addr.c_sockaddr_inet()->sin_addr); -+#ifdef RAK_USE_INET6 -+ case AF_INET6: -+ return get_priority_ipv6(addr.c_sockaddr_inet6()->sin6_addr); -+#endif -+ default: -+ throw torrent::internal_error("Unknown address family given to compare"); -+ } -+} -+ -+} -+ -+#ifdef __linux__ -+ -+// Linux-specific implementation that understands how to filter away -+// understands how to filter away secondary addresses. -+bool get_local_address(sa_family_t family, rak::socket_address *address) { -+ ifaddrs *ifaddrs; -+ if (getifaddrs(&ifaddrs)) { -+ return false; -+ } -+ -+ rak::socket_address best_addr; -+ switch (family) { -+ case AF_INET: -+ best_addr.sa_inet()->clear(); -+ break; -+#ifdef RAK_USE_INET6 -+ case AF_INET6: -+ best_addr.sa_inet6()->clear(); -+ break; -+#endif -+ default: -+ throw torrent::internal_error("Unknown address family given to get_local_address"); -+ } -+ -+ // The bottom bit of the priority is used to hold if the address is -+ // a secondary address (e.g. with IPv6 privacy extensions) or not; -+ // secondary addresses have lower priority (higher number). -+ int best_addr_pri = get_priority(best_addr) * 2; -+ -+ // Get all the addresses via Linux' netlink interface. -+ int fd = ::socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); -+ if (fd == -1) { -+ return false; -+ } -+ -+ struct sockaddr_nl nladdr; -+ memset(&nladdr, 0, sizeof(nladdr)); -+ nladdr.nl_family = AF_NETLINK; -+ if (::bind(fd, (sockaddr *)&nladdr, sizeof(nladdr))) { -+ ::close(fd); -+ return false; -+ } -+ -+ const int seq_no = 1; -+ struct { -+ nlmsghdr nh; -+ rtgenmsg g; -+ } req; -+ memset(&req, 0, sizeof(req)); -+ -+ req.nh.nlmsg_len = sizeof(req); -+ req.nh.nlmsg_type = RTM_GETADDR; -+ req.nh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; -+ req.nh.nlmsg_pid = getpid(); -+ req.nh.nlmsg_seq = seq_no; -+ req.g.rtgen_family = AF_UNSPEC; -+ -+ int ret; -+ do { -+ ret = ::sendto(fd, &req, sizeof(req), 0, (sockaddr *)&nladdr, sizeof(nladdr)); -+ } while (ret == -1 && errno == EINTR); -+ -+ if (ret == -1) { -+ ::close(fd); -+ return false; -+ } -+ -+ bool done = false; -+ do { -+ char buf[4096]; -+ socklen_t len = sizeof(nladdr); -+ do { -+ ret = ::recvfrom(fd, buf, sizeof(buf), 0, (sockaddr *)&nladdr, &len); -+ } while (ret == -1 && errno == EINTR); -+ -+ if (ret < 0) { -+ ::close(fd); -+ return false; -+ } -+ -+ for (const nlmsghdr *nlmsg = (const nlmsghdr *)buf; -+ NLMSG_OK(nlmsg, ret); -+ nlmsg = NLMSG_NEXT(nlmsg, ret)) { -+ if (nlmsg->nlmsg_seq != seq_no) -+ continue; -+ if (nlmsg->nlmsg_type == NLMSG_DONE) { -+ done = true; -+ break; -+ } -+ if (nlmsg->nlmsg_type == NLMSG_ERROR) { -+ ::close(fd); -+ return false; -+ } -+ if (nlmsg->nlmsg_type != RTM_NEWADDR) -+ continue; -+ -+ const ifaddrmsg *ifa = (const ifaddrmsg *)NLMSG_DATA(nlmsg); -+ -+ if (ifa->ifa_family != family) -+ continue; -+ -+#ifdef IFA_F_OPTIMISTIC -+ if ((ifa->ifa_flags & IFA_F_OPTIMISTIC) != 0) -+ continue; -+#endif -+#ifdef IFA_F_DADFAILED -+ if ((ifa->ifa_flags & IFA_F_DADFAILED) != 0) -+ continue; -+#endif -+#ifdef IFA_F_DEPRECATED -+ if ((ifa->ifa_flags & IFA_F_DEPRECATED) != 0) -+ continue; -+#endif -+#ifdef IFA_F_TENTATIVE -+ if ((ifa->ifa_flags & IFA_F_TENTATIVE) != 0) -+ continue; -+#endif -+ -+ // Since there can be point-to-point links on the machine, we need to keep -+ // track of the addresses we've seen for this interface; if we see both -+ // IFA_LOCAL and IFA_ADDRESS for an interface, keep only the IFA_LOCAL. -+ rak::socket_address this_addr; -+ bool seen_addr = false; -+ int plen = IFA_PAYLOAD(nlmsg); -+ for (const rtattr *rta = IFA_RTA(ifa); -+ RTA_OK(rta, plen); -+ rta = RTA_NEXT(rta, plen)) { -+ if (rta->rta_type != IFA_LOCAL && -+ rta->rta_type != IFA_ADDRESS) { -+ continue; -+ } -+ if (rta->rta_type == IFA_ADDRESS && seen_addr) { -+ continue; -+ } -+ seen_addr = true; -+ switch (ifa->ifa_family) { -+ case AF_INET: -+ this_addr.sa_inet()->clear(); -+ this_addr.sa_inet()->set_address(*(const in_addr *)RTA_DATA(rta)); -+ break; -+#ifdef RAK_USE_INET6 -+ case AF_INET6: -+ this_addr.sa_inet6()->clear(); -+ this_addr.sa_inet6()->set_address(*(const in6_addr *)RTA_DATA(rta)); -+ break; -+#endif -+ } -+ } -+ if (!seen_addr) -+ continue; -+ -+ int this_addr_pri = get_priority(this_addr) * 2; -+ if ((ifa->ifa_flags & IFA_F_SECONDARY) == IFA_F_SECONDARY) { -+ ++this_addr_pri; -+ } -+ -+ if (this_addr_pri < best_addr_pri) { -+ best_addr = this_addr; -+ best_addr_pri = this_addr_pri; -+ } -+ } -+ } while (!done); -+ -+ ::close(fd); -+ if (!best_addr.is_address_any()) { -+ *address = best_addr; -+ return true; -+ } else { -+ return false; -+ } -+} -+ -+#else -+ -+// Generic POSIX variant. -+bool get_local_address(sa_family_t family, rak::socket_address *address) { -+ SocketFd sock; -+ if (!sock.open_datagram()) { -+ return false; -+ } -+ -+ rak::socket_address dummy_dest; -+ dummy_dest.clear(); -+ -+ switch (family) { -+ case rak::socket_address::af_inet: -+ dummy_dest.set_address_c_str("4.0.0.0"); -+ break; -+#ifdef RAK_USE_INET6 -+ case rak::socket_address::af_inet6: -+ dummy_dest.set_address_c_str("2001:700::"); -+ break; -+#endif -+ default: -+ throw internal_error("Unknown address family"); -+ } -+ dummy_dest.set_port(80); -+ -+ if (!sock.connect(dummy_dest)) { -+ sock.close(); -+ return false; -+ } -+ -+ bool ret = sock.getsockname(address); -+ sock.close(); -+ return ret; -+} -+ -+#endif -+ -+} -diff --git a/src/net/local_addr.h b/src/net/local_addr.h -new file mode 100644 -index 0000000..43bc820 ---- /dev/null -+++ b/src/net/local_addr.h -@@ -0,0 +1,64 @@ -+// libTorrent - BitTorrent library -+// Copyright (C) 2005-2007, Jari Sundell -+// -+// This program is free software; you can redistribute it and/or modify -+// it under the terms of the GNU General Public License as published by -+// the Free Software Foundation; either version 2 of the License, or -+// (at your option) any later version. -+// -+// This program is distributed in the hope that it will be useful, -+// but WITHOUT ANY WARRANTY; without even the implied warranty of -+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+// GNU General Public License for more details. -+// -+// You should have received a copy of the GNU General Public License -+// along with this program; if not, write to the Free Software -+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+// -+// In addition, as a special exception, the copyright holders give -+// permission to link the code of portions of this program with the -+// OpenSSL library under certain conditions as described in each -+// individual source file, and distribute linked combinations -+// including the two. -+// -+// You must obey the GNU General Public License in all respects for -+// all of the code used other than OpenSSL. If you modify file(s) -+// with this exception, you may extend this exception to your version -+// of the file(s), but you are not obligated to do so. If you do not -+// wish to do so, delete this exception statement from your version. -+// If you delete this exception statement from all source files in the -+// program, then also delete it here. -+// -+// Contact: Jari Sundell <jaris@ifi.uio.no> -+// -+// Skomakerveien 33 -+// 3185 Skoppum, NORWAY -+ -+// A routine to get a local IP address that can be presented to a tracker. -+// (Does not use UPnP etc., so will not understand NAT.) -+// On a machine with multiple network cards, address selection can be a -+// complex process, and in general what's selected is a source/destination -+// address pair. However, this routine will give an approximation that will -+// be good enough for most purposes and users. -+ -+#ifndef LIBTORRENT_NET_LOCAL_ADDR_H -+#define LIBTORRENT_NET_LOCAL_ADDR_H -+ -+#include <unistd.h> -+ -+namespace rak { -+ class socket_address; -+} -+ -+namespace torrent { -+ -+// Note: family must currently be rak::af_inet or rak::af_inet6 -+// (rak::af_unspec won't do); anything else will throw an exception. -+// Returns false if no address of the given family could be found, -+// either because there are none, or because something went wrong in -+// the process (e.g., no free file descriptors). -+bool get_local_address(sa_family_t family, rak::socket_address *address); -+ -+} -+ -+#endif /* LIBTORRENT_NET_LOCAL_ADDR_H */ -diff --git a/src/net/socket_datagram.cc b/src/net/socket_datagram.cc -index b983289..69992e3 100644 ---- a/src/net/socket_datagram.cc -+++ b/src/net/socket_datagram.cc -@@ -73,7 +73,13 @@ SocketDatagram::write_datagram(const void* buffer, unsigned int length, rak::soc - int r; - - if (sa != NULL) { -- r = ::sendto(m_fileDesc, buffer, length, 0, sa->sa_inet()->c_sockaddr(), sizeof(rak::socket_address_inet)); -+#ifdef RAK_USE_INET6 -+ if (m_ipv6_socket && sa->family() == rak::socket_address::pf_inet) { -+ rak::socket_address_inet6 sa_mapped = sa->sa_inet()->to_mapped_address(); -+ r = ::sendto(m_fileDesc, buffer, length, 0, sa_mapped.c_sockaddr(), sizeof(rak::socket_address_inet6)); -+ } else -+#endif -+ r = ::sendto(m_fileDesc, buffer, length, 0, sa->c_sockaddr(), sa->length()); - } else { - r = ::send(m_fileDesc, buffer, length, 0); - } -diff --git a/src/net/socket_fd.cc b/src/net/socket_fd.cc -index c349d21..8576628 100644 ---- a/src/net/socket_fd.cc -+++ b/src/net/socket_fd.cc -@@ -70,6 +70,11 @@ SocketFd::set_priority(priority_type p) { - check_valid(); - int opt = p; - -+#ifdef RAK_USE_INET6 -+ if (m_ipv6_socket) -+ return setsockopt(m_fd, IPPROTO_IPV6, IPV6_TCLASS, &opt, sizeof(opt)) == 0; -+ else -+#endif - return setsockopt(m_fd, IPPROTO_IP, IP_TOS, &opt, sizeof(opt)) == 0; - } - -@@ -112,12 +117,36 @@ SocketFd::get_error() const { - - bool - SocketFd::open_stream() { -+#ifdef RAK_USE_INET6 -+ m_fd = socket(rak::socket_address::pf_inet6, SOCK_STREAM, IPPROTO_TCP); -+ if (m_fd == -1) { -+ m_ipv6_socket = false; -+ return (m_fd = socket(rak::socket_address::pf_inet, SOCK_STREAM, IPPROTO_TCP)) != -1; -+ } -+ m_ipv6_socket = true; -+ -+ int zero = 0; -+ return setsockopt(m_fd, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) != -1; -+#else - return (m_fd = socket(rak::socket_address::pf_inet, SOCK_STREAM, IPPROTO_TCP)) != -1; -+#endif - } - - bool - SocketFd::open_datagram() { -+#ifdef RAK_USE_INET6 -+ m_fd = socket(rak::socket_address::pf_inet6, SOCK_DGRAM, 0); -+ if (m_fd == -1) { -+ m_ipv6_socket = false; -+ return (m_fd = socket(rak::socket_address::pf_inet, SOCK_DGRAM, 0)) != -1; -+ } -+ m_ipv6_socket = true; -+ -+ int zero = 0; -+ return setsockopt(m_fd, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) != -1; -+#else - return (m_fd = socket(rak::socket_address::pf_inet, SOCK_DGRAM, 0)) != -1; -+#endif - } - - bool -@@ -149,10 +178,34 @@ bool - SocketFd::connect(const rak::socket_address& sa) { - check_valid(); - -+#ifdef RAK_USE_INET6 -+ if (m_ipv6_socket && sa.family() == rak::socket_address::pf_inet) { -+ rak::socket_address_inet6 sa_mapped = sa.sa_inet()->to_mapped_address(); -+ return !::connect(m_fd, sa_mapped.c_sockaddr(), sizeof(sa_mapped)) || errno == EINPROGRESS; -+ } -+#endif - return !::connect(m_fd, sa.c_sockaddr(), sa.length()) || errno == EINPROGRESS; - } - - bool -+SocketFd::getsockname(rak::socket_address *sa) { -+ check_valid(); -+ -+ socklen_t len = sizeof(rak::socket_address); -+ if (::getsockname(m_fd, sa->c_sockaddr(), &len)) { -+ return false; -+ } -+ -+#ifdef RAK_USE_INET6 -+ if (m_ipv6_socket && sa->family() == rak::socket_address::af_inet6) { -+ *sa = sa->sa_inet6()->normalize_address(); -+ } -+#endif -+ -+ return true; -+} -+ -+bool - SocketFd::listen(int size) { - check_valid(); - -@@ -164,7 +217,18 @@ SocketFd::accept(rak::socket_address* sa) { - check_valid(); - socklen_t len = sizeof(rak::socket_address); - -+#ifdef RAK_USE_INET6 -+ if (sa == NULL) { -+ return SocketFd(::accept(m_fd, NULL, &len)); -+ } -+ int fd = ::accept(m_fd, sa->c_sockaddr(), &len); -+ if (fd != -1 && m_ipv6_socket && sa->family() == rak::socket_address::af_inet6) { -+ *sa = sa->sa_inet6()->normalize_address(); -+ } -+ return SocketFd(fd); -+#else - return SocketFd(::accept(m_fd, sa != NULL ? sa->c_sockaddr() : NULL, &len)); -+#endif - } - - // unsigned int -diff --git a/src/net/socket_fd.h b/src/net/socket_fd.h -index a208ad6..d39c413 100644 ---- a/src/net/socket_fd.h -+++ b/src/net/socket_fd.h -@@ -77,6 +77,7 @@ public: - bool bind(const rak::socket_address& sa); - bool bind(const rak::socket_address& sa, unsigned int length); - bool connect(const rak::socket_address& sa); -+ bool getsockname(rak::socket_address* sa); - - bool listen(int size); - SocketFd accept(rak::socket_address* sa); -@@ -88,6 +89,9 @@ private: - inline void check_valid() const; - - int m_fd; -+#ifdef RAK_USE_INET6 -+ bool m_ipv6_socket; -+#endif - }; - - } -diff --git a/src/torrent/connection_manager.cc b/src/torrent/connection_manager.cc -index 64c4197..152a0a1 100644 ---- a/src/torrent/connection_manager.cc -+++ b/src/torrent/connection_manager.cc -@@ -78,13 +78,18 @@ ConnectionManager::ConnectionManager() : - m_slotResolver(&resolve_host) { - - m_bindAddress = (new rak::socket_address())->c_sockaddr(); -- rak::socket_address::cast_from(m_bindAddress)->sa_inet()->clear(); -- - m_localAddress = (new rak::socket_address())->c_sockaddr(); -- rak::socket_address::cast_from(m_localAddress)->sa_inet()->clear(); -- - m_proxyAddress = (new rak::socket_address())->c_sockaddr(); -+ -+#ifdef RAK_USE_INET6 -+ rak::socket_address::cast_from(m_bindAddress)->sa_inet6()->clear(); -+ rak::socket_address::cast_from(m_localAddress)->sa_inet6()->clear(); -+ rak::socket_address::cast_from(m_proxyAddress)->sa_inet6()->clear(); -+#else -+ rak::socket_address::cast_from(m_bindAddress)->sa_inet()->clear(); -+ rak::socket_address::cast_from(m_localAddress)->sa_inet()->clear(); - rak::socket_address::cast_from(m_proxyAddress)->sa_inet()->clear(); -+#endif - } - - ConnectionManager::~ConnectionManager() { -@@ -123,8 +128,10 @@ void - ConnectionManager::set_bind_address(const sockaddr* sa) { - const rak::socket_address* rsa = rak::socket_address::cast_from(sa); - -+#ifndef RAK_USE_INET6 - if (rsa->family() != rak::socket_address::af_inet) - throw input_error("Tried to set a bind address that is not an af_inet address."); -+#endif - - rak::socket_address::cast_from(m_bindAddress)->copy(*rsa, rsa->length()); - } -@@ -133,8 +140,10 @@ void - ConnectionManager::set_local_address(const sockaddr* sa) { - const rak::socket_address* rsa = rak::socket_address::cast_from(sa); - -+#ifndef RAK_USE_INET6 - if (rsa->family() != rak::socket_address::af_inet) - throw input_error("Tried to set a local address that is not an af_inet address."); -+#endif - - rak::socket_address::cast_from(m_localAddress)->copy(*rsa, rsa->length()); - } -@@ -143,8 +152,10 @@ void - ConnectionManager::set_proxy_address(const sockaddr* sa) { - const rak::socket_address* rsa = rak::socket_address::cast_from(sa); - -+#ifndef RAK_USE_INET6 - if (rsa->family() != rak::socket_address::af_inet) - throw input_error("Tried to set a proxy address that is not an af_inet address."); -+#endif - - rak::socket_address::cast_from(m_proxyAddress)->copy(*rsa, rsa->length()); - } -diff --git a/src/torrent/event.h b/src/torrent/event.h -index 335e6d6..2d6e36c 100644 ---- a/src/torrent/event.h -+++ b/src/torrent/event.h -@@ -57,6 +57,10 @@ public: - - protected: - int m_fileDesc; -+ -+#ifdef RAK_USE_INET6 -+ bool m_ipv6_socket; -+#endif - }; - - } -diff --git a/src/torrent/peer/peer_list.cc b/src/torrent/peer/peer_list.cc -index 187ba5b..cef840b 100644 ---- a/src/torrent/peer/peer_list.cc -+++ b/src/torrent/peer/peer_list.cc -@@ -65,15 +65,23 @@ socket_address_less(const sockaddr* s1, const sockaddr* s2) { - // humans. - return sa1->sa_inet()->address_h() < sa2->sa_inet()->address_h(); - -+#ifdef RAK_USE_INET6 -+ else { -+ const in6_addr addr1 = sa1->sa_inet6()->address(); -+ const in6_addr addr2 = sa2->sa_inet6()->address(); -+ return memcmp(&addr1, &addr2, sizeof(in6_addr)) < 0; -+ } -+#else - else -- // When we implement INET6 handling, embed the ipv4 address in -- // the ipv6 address. - throw internal_error("socket_address_key(...) tried to compare an invalid family type."); -+#endif -+ - } - - inline bool - socket_address_key::is_comparable(const sockaddr* sa) { -- return rak::socket_address::cast_from(sa)->family() == rak::socket_address::af_inet; -+ return rak::socket_address::cast_from(sa)->family() == rak::socket_address::af_inet || -+ rak::socket_address::cast_from(sa)->family() == rak::socket_address::af_inet6; - } - - struct peer_list_equal_port : public std::binary_function<PeerList::reference, uint16_t, bool> { -diff --git a/src/tracker/tracker_http.cc b/src/tracker/tracker_http.cc -index d23cae6..493dd0d 100644 ---- a/src/tracker/tracker_http.cc -+++ b/src/tracker/tracker_http.cc -@@ -43,6 +43,7 @@ - - #include "download/download_info.h" - #include "net/address_list.h" -+#include "net/local_addr.h" - #include "torrent/connection_manager.h" - #include "torrent/exceptions.h" - #include "torrent/http.h" -@@ -114,9 +115,16 @@ TrackerHttp::send_state(int state) { - - const rak::socket_address* localAddress = rak::socket_address::cast_from(manager->connection_manager()->local_address()); - -- if (localAddress->family() == rak::socket_address::af_inet && -- !localAddress->sa_inet()->is_address_any()) -+ if (!localAddress->is_address_any()) - s << "&ip=" << localAddress->address_str(); -+ -+#ifdef RAK_USE_INET6 -+ if (localAddress->is_address_any() || localAddress->family() != rak::socket_address::pf_inet6) { -+ rak::socket_address local_v6; -+ if (get_local_address(rak::socket_address::af_inet6, &local_v6)) -+ s << "&ipv6=" << rak::copy_escape_html(local_v6.address_str()); -+ } -+#endif - - if (info->is_compact()) - s << "&compact=1"; -@@ -220,18 +228,34 @@ TrackerHttp::receive_done() { - - AddressList l; - -- try { -- // Due to some trackers sending the wrong type when no peers are -- // available, don't bork on it. -- if (b.get_key("peers").is_string()) -- l.parse_address_compact(b.get_key_string("peers")); -+ if (!b.has_key("peers") -+#ifdef RAK_USE_INET6 -+ && !b.has_key("peers6") -+#endif -+ ) { -+ return receive_failed("No peers returned"); -+ } -+ -+ if (b.has_key("peers")) { -+ try { -+ // Due to some trackers sending the wrong type when no peers are -+ // available, don't bork on it. -+ if (b.get_key("peers").is_string()) -+ l.parse_address_compact(b.get_key_string("peers")); -+ -+ else if (b.get_key("peers").is_list()) -+ l.parse_address_normal(b.get_key_list("peers")); - -- else if (b.get_key("peers").is_list()) -- l.parse_address_normal(b.get_key_list("peers")); -+ } catch (bencode_error& e) { -+ return receive_failed(e.what()); -+ } -+ } - -- } catch (bencode_error& e) { -- return receive_failed(e.what()); -+#ifdef RAK_USE_INET6 -+ if (b.has_key("peers6")) { -+ l.parse_address_compact_ipv6(b.get_key_string("peers6")); - } -+#endif - - close(); - m_parent->receive_success(this, &l); -diff --git a/src/tracker/tracker_udp.cc b/src/tracker/tracker_udp.cc -index dacf21b..63442a3 100644 ---- a/src/tracker/tracker_udp.cc -+++ b/src/tracker/tracker_udp.cc -@@ -271,11 +271,18 @@ TrackerUdp::prepare_announce_input() { - - const rak::socket_address* localAddress = rak::socket_address::cast_from(manager->connection_manager()->local_address()); - -- // This code assumes we're have a inet address. -+#ifdef RAK_USE_INET6 -+ uint32_t local_addr = 0; -+ if (localAddress->family() == rak::socket_address::af_inet) -+ local_addr = localAddress->sa_inet()->address_n(); -+#else - if (localAddress->family() != rak::socket_address::af_inet) - throw internal_error("TrackerUdp::prepare_announce_input() info->local_address() not of family AF_INET."); -+ -+ uint32_t local_addr = localAddress->sa_inet()->address_n(); -+#endif - -- m_writeBuffer->write_32_n(localAddress->sa_inet()->address_n()); -+ m_writeBuffer->write_32_n(local_addr); - m_writeBuffer->write_32(m_parent->key()); - m_writeBuffer->write_32(m_parent->numwant()); - m_writeBuffer->write_16(manager->connection_manager()->listen_port()); diff --git a/libtorrent-extended/magnet_uri.patch b/libtorrent-extended/magnet_uri.patch deleted file mode 100644 index 3d00540..0000000 --- a/libtorrent-extended/magnet_uri.patch +++ /dev/null @@ -1,1652 +0,0 @@ -diff --git a/src/download/download_constructor.cc b/src/download/download_constructor.cc -index f37f848..86e5351 100644 ---- a/src/download/download_constructor.cc -+++ b/src/download/download_constructor.cc -@@ -81,7 +81,10 @@ struct download_constructor_encoding_match : - }; - - void --DownloadConstructor::initialize(const Object& b) { -+DownloadConstructor::initialize(Object& b) { -+ if (!b.has_key_map("info") && b.has_key_string("magnet-uri")) -+ parse_magnet_uri(b, b.get_key_string("magnet-uri")); -+ - if (b.has_key_string("encoding")) - m_defaultEncoding = b.get_key_string("encoding"); - -@@ -136,10 +139,24 @@ DownloadConstructor::parse_info(const Object& b) { - if (b.flags() & Object::flag_unordered) - throw input_error("Download has unordered info dictionary."); - -- uint32_t chunkSize = b.get_key_value("piece length"); -+ uint32_t chunkSize; -+ -+ if (b.has_key_value("meta_download") && b.get_key_value("meta_download")) -+ m_download->info()->set_meta_download(true); -+ -+ if (m_download->info()->is_meta_download()) { -+ if (b.get_key_string("pieces").length() != HashString::size_data) -+ throw input_error("Meta-download has invalid piece data."); -+ -+ chunkSize = 1; -+ parse_single_file(b, chunkSize); -+ -+ } else { -+ chunkSize = b.get_key_value("piece length"); - -- if (chunkSize <= (1 << 10) || chunkSize > (128 << 20)) -- throw input_error("Torrent has an invalid \"piece length\"."); -+ if (chunkSize <= (1 << 10) || chunkSize > (128 << 20)) -+ throw input_error("Torrent has an invalid \"piece length\"."); -+ } - - if (b.has_key("length")) { - parse_single_file(b, chunkSize); -@@ -148,11 +165,11 @@ DownloadConstructor::parse_info(const Object& b) { - parse_multi_files(b.get_key("files"), chunkSize); - fileList->set_root_dir("./" + m_download->info()->name()); - -- } else { -+ } else if (!m_download->info()->is_meta_download()) { - throw input_error("Torrent must have either length or files entry."); - } - -- if (fileList->size_bytes() == 0) -+ if (fileList->size_bytes() == 0 && !m_download->info()->is_meta_download()) - throw input_error("Torrent has zero length."); - - // Set chunksize before adding files to make sure the index range is -@@ -239,7 +256,7 @@ DownloadConstructor::parse_single_file(const Object& b, uint32_t chunkSize) { - throw input_error("Bad torrent file, \"name\" is an invalid path name."); - - FileList* fileList = m_download->main()->file_list(); -- fileList->initialize(b.get_key_value("length"), chunkSize); -+ fileList->initialize(chunkSize == 1 ? 1 : b.get_key_value("length"), chunkSize); - fileList->set_multi_file(false); - - std::list<Path> pathList; -@@ -343,4 +360,132 @@ DownloadConstructor::choose_path(std::list<Path>* pathList) { - return pathList->front(); - } - -+static const char* -+parse_base32_sha1(const char* pos, HashString& hash) { -+ HashString::iterator hashItr = hash.begin(); -+ -+ static const int base_shift = 8+8-5; -+ int shift = base_shift; -+ uint16_t decoded = 0; -+ -+ while (*pos) { -+ char c = *pos++; -+ uint16_t value; -+ -+ if (c >= 'A' && c <= 'Z') -+ value = c - 'A'; -+ else if (c >= 'a' && c <= 'z') -+ value = c - 'a'; -+ else if (c >= '2' && c <= '7') -+ value = 26 + c - '2'; -+ else if (c == '&') -+ break; -+ else -+ return NULL; -+ -+ decoded |= (value << shift); -+ if (shift <= 8) { -+ // Too many characters for a base32 SHA1. -+ if (hashItr == hash.end()) -+ return NULL; -+ -+ *hashItr++ = (decoded >> 8); -+ decoded <<= 8; -+ shift += 3; -+ } else { -+ shift -= 5; -+ } -+ } -+ -+ return hashItr != hash.end() || shift != base_shift ? NULL : pos; -+} -+ -+void -+DownloadConstructor::parse_magnet_uri(Object& b, const std::string& uri) { -+ if (std::strncmp(uri.c_str(), "magnet:?", 8)) -+ throw input_error("Invalid magnet URI."); -+ -+ const char* pos = uri.c_str() + 8; -+ -+ Object trackers(Object::create_list()); -+ HashString hash; -+ bool hashValid = false; -+ -+ while (*pos) { -+ const char* tagStart = pos; -+ while (*pos != '=') -+ if (!*pos++) -+ break; -+ -+ SimpleString tag(tagStart, pos - tagStart); -+ pos++; -+ -+ // hash may be base32 encoded (optional in BEP 0009 and common practice) -+ if (tag == "xt") { -+ if (strncmp(pos, "urn:btih:", 9)) -+ throw input_error("Invalid magnet URI."); -+ -+ pos += 9; -+ -+ const char* nextPos = parse_base32_sha1(pos, hash); -+ if (nextPos != NULL) { -+ pos = nextPos; -+ hashValid = true; -+ continue; -+ } -+ } -+ -+ // everything else, including sometimes the hash, is url encoded. -+ std::string decoded; -+ while (*pos) { -+ char c = *pos++; -+ if (c == '%') { -+ if (sscanf(pos, "%02hhx", &c) != 1) -+ throw input_error("Invalid magnet URI."); -+ -+ pos += 2; -+ -+ } else if (c == '&') { -+ break; -+ } -+ -+ decoded.push_back(c); -+ } -+ -+ if (tag == "xt") { -+ // url-encoded hash as per magnet URN specs -+ if (decoded.length() == hash.size_data) { -+ hash = *HashString::cast_from(decoded); -+ hashValid = true; -+ -+ // hex-encoded hash as per BEP 0009 -+ } else if (decoded.length() == hash.size_data * 2) { -+ std::string::iterator hexItr = decoded.begin(); -+ for (HashString::iterator itr = hash.begin(), last = hash.end(); itr != last; itr++, hexItr += 2) -+ *itr = (rak::hexchar_to_value(*hexItr) << 4) + rak::hexchar_to_value(*(hexItr + 1)); -+ hashValid = true; -+ -+ } else { -+ throw input_error("Invalid magnet URI."); -+ } -+ } else if (tag == "tr") { -+ trackers.insert_back(Object::create_list()).insert_back(decoded); -+ } -+ // could also handle "dn" = display name (torrent name), but we can't really use that -+ } -+ -+ if (!hashValid) -+ throw input_error("Invalid magnet URI."); -+ -+ Object& info = b.insert_key("info", Object::create_map()); -+ info.insert_key("pieces", hash.str()); -+ info.insert_key("name", rak::transform_hex(hash.str()) + ".meta"); -+ info.insert_key("meta_download", (int64_t)1); -+ -+ if (!trackers.as_list().empty()) { -+ b.insert_preserve_copy("announce", trackers.as_list().begin()->as_list().begin()->as_string()); -+ b.insert_preserve_type("announce-list", trackers); -+ } -+} -+ - } -diff --git a/src/download/download_constructor.h b/src/download/download_constructor.h -index 7192f90..8af520f 100644 ---- a/src/download/download_constructor.h -+++ b/src/download/download_constructor.h -@@ -55,7 +55,7 @@ class DownloadConstructor { - public: - DownloadConstructor() : m_download(NULL), m_encodingList(NULL) {} - -- void initialize(const Object& b); -+ void initialize(Object& b); - - void set_download(DownloadWrapper* d) { m_download = d; } - void set_encoding_list(const EncodingList* e) { m_encodingList = e; } -@@ -64,6 +64,7 @@ private: - void parse_name(const Object& b); - void parse_tracker(const Object& b); - void parse_info(const Object& b); -+ void parse_magnet_uri(Object& b, const std::string& uri); - - void add_tracker_group(const Object& b); - void add_tracker_single(const Object& b, int group); -diff --git a/src/download/download_info.h b/src/download/download_info.h -index 0a3c0e8..68fb178 100644 ---- a/src/download/download_info.h -+++ b/src/download/download_info.h -@@ -76,6 +76,7 @@ public: - m_isCompact(true), - m_isAcceptingNewPeers(true), - m_isPrivate(false), -+ m_isMetaDownload(false), - m_pexEnabled(true), - m_pexActive(true), - -@@ -86,7 +87,8 @@ public: - m_uploadedBaseline(0), - m_completedBaseline(0), - m_sizePex(0), -- m_maxSizePex(8) { -+ m_maxSizePex(8), -+ m_metadataSize(0) { - } - - const std::string& name() const { return m_name; } -@@ -116,6 +118,9 @@ public: - bool is_private() const { return m_isPrivate; } - void set_private(bool p) { m_isPrivate = p; if (p) m_pexEnabled = false; } - -+ bool is_meta_download() const { return m_isMetaDownload; } -+ void set_meta_download(bool m) { m_isMetaDownload = m; } -+ - bool is_pex_enabled() const { return m_pexEnabled; } - void set_pex_enabled(bool enabled) { m_pexEnabled = enabled && !m_isPrivate; } - -@@ -134,6 +139,9 @@ public: - uint64_t completed_adjusted() const { return std::max<int64_t>(m_slotStatCompleted() - completed_baseline(), 0); } - void set_completed_baseline(uint64_t b) { m_completedBaseline = b; } - -+ size_t metadata_size() const { return m_metadataSize; } -+ void set_metadata_size(size_t size) { m_metadataSize = size; } -+ - uint32_t size_pex() const { return m_sizePex; } - void set_size_pex(uint32_t b) { m_sizePex = b; } - -@@ -165,6 +173,7 @@ private: - bool m_isCompact; - bool m_isAcceptingNewPeers; - bool m_isPrivate; -+ bool m_isMetaDownload; - bool m_pexEnabled; - bool m_pexActive; - -@@ -176,6 +185,7 @@ private: - uint64_t m_completedBaseline; - uint32_t m_sizePex; - uint32_t m_maxSizePex; -+ size_t m_metadataSize; - - slot_stat_type m_slotStatCompleted; - slot_stat_type m_slotStatLeft; -diff --git a/src/download/download_main.cc b/src/download/download_main.cc -index 1dd5f98..5691021 100644 ---- a/src/download/download_main.cc -+++ b/src/download/download_main.cc -@@ -455,4 +455,19 @@ DownloadMain::do_peer_exchange() { - } - } - -+void -+DownloadMain::set_metadata_size(size_t size) { -+ if (m_info->is_meta_download()) { -+ if (m_fileList.size_bytes() < 2) -+ file_list()->reset_filesize(size); -+ else if (size != m_fileList.size_bytes()) -+ throw communication_error("Peer-supplied metadata size mismatch."); -+ -+ } else if (m_info->metadata_size() && m_info->metadata_size() != size) { -+ throw communication_error("Peer-supplied metadata size mismatch."); -+ } -+ -+ m_info->set_metadata_size(size); -+} -+ - } -diff --git a/src/download/download_main.h b/src/download/download_main.h -index 5d0090b..700f41e 100644 ---- a/src/download/download_main.h -+++ b/src/download/download_main.h -@@ -116,6 +116,8 @@ public: - - bool want_pex_msg() { return m_info->is_pex_active() && m_peerList.available_list()->want_more(); }; - -+ void set_metadata_size(size_t s); -+ - // Carefull with these. - void setup_delegator(); - void setup_tracker(); -diff --git a/src/net/data_buffer.h b/src/net/data_buffer.h -index a26ca36..e3d9e38 100644 ---- a/src/net/data_buffer.h -+++ b/src/net/data_buffer.h -@@ -48,6 +48,7 @@ struct DataBuffer { - DataBuffer(char* data, char* end) : m_data(data), m_end(end), m_owned(true) {} - - DataBuffer clone() const { DataBuffer d = *this; d.m_owned = false; return d; } -+ DataBuffer release() { DataBuffer d = *this; set(NULL, NULL, false); return d; } - - char* data() const { return m_data; } - char* end() const { return m_end; } -@@ -70,7 +71,7 @@ private: - - inline void - DataBuffer::clear() { -- if (!empty()) -+ if (!empty() && m_owned) - delete[] m_data; - - m_data = m_end = NULL; -diff --git a/src/net/socket_base.cc b/src/net/socket_base.cc -index 90457dc..13a9c8b 100644 ---- a/src/net/socket_base.cc -+++ b/src/net/socket_base.cc -@@ -47,7 +47,7 @@ - - namespace torrent { - --char* SocketBase::m_nullBuffer = new char[1 << 17]; -+char* SocketBase::m_nullBuffer = new char[SocketBase::null_buffer_size]; - - SocketBase::~SocketBase() { - if (get_fd().is_valid()) -diff --git a/src/net/socket_base.h b/src/net/socket_base.h -index 9340a23..0f0f424 100644 ---- a/src/net/socket_base.h -+++ b/src/net/socket_base.h -@@ -68,6 +68,8 @@ protected: - SocketBase(const SocketBase&); - void operator = (const SocketBase&); - -+ static const size_t null_buffer_size = 1 << 17; -+ - static char* m_nullBuffer; - }; - -diff --git a/src/protocol/Makefile.am b/src/protocol/Makefile.am -index 6171d06..18f671d 100644 ---- a/src/protocol/Makefile.am -+++ b/src/protocol/Makefile.am -@@ -17,6 +17,8 @@ libsub_protocol_la_SOURCES = \ - peer_connection_base.h \ - peer_connection_leech.cc \ - peer_connection_leech.h \ -+ peer_connection_metadata.cc \ -+ peer_connection_metadata.h \ - peer_factory.cc \ - peer_factory.h \ - protocol_base.h \ -diff --git a/src/protocol/extensions.cc b/src/protocol/extensions.cc -index 7cbf6e3..3e0cf60 100644 ---- a/src/protocol/extensions.cc -+++ b/src/protocol/extensions.cc -@@ -43,6 +43,8 @@ - - #include "download/available_list.h" - #include "download/download_main.h" -+#include "download/download_manager.h" -+#include "download/download_wrapper.h" - #include "protocol/peer_connection_base.h" - #include "torrent/connection_manager.h" - #include "torrent/object.h" -@@ -58,7 +60,9 @@ namespace torrent { - - enum ext_handshake_keys { - key_e, -+ key_m_utMetadata, - key_m_utPex, -+ key_metadataSize, - key_p, - key_reqq, - key_v, -@@ -70,6 +74,13 @@ enum ext_pex_keys { - key_pex_LAST - }; - -+enum ext_metadata_keys { -+ key_msgType, -+ key_piece, -+ key_totalSize, -+ key_metadata_LAST -+}; -+ - class ExtHandshakeMessage : public StaticMap<ext_handshake_keys, key_handshake_LAST> { - public: - typedef StaticMap<ext_handshake_keys, key_handshake_LAST> base_type; -@@ -82,9 +93,17 @@ public: - typedef StaticMapKeys::mapping_type mapping_type; - }; - -+class ExtMetadataMessage : public StaticMap<ext_metadata_keys, key_metadata_LAST> { -+public: -+ typedef StaticMap<ext_metadata_keys, key_metadata_LAST> base_type; -+ typedef StaticMapKeys::mapping_type mapping_type; -+}; -+ - ExtHandshakeMessage::mapping_type ext_handshake_key_names[ExtHandshakeMessage::length] = { - { key_e, "e" }, -+ { key_m_utMetadata, "m::ut_metadata" }, - { key_m_utPex, "m::ut_pex" }, -+ { key_metadataSize, "metadata_size" }, - { key_p, "p" }, - { key_reqq, "reqq" }, - { key_v, "v" }, -@@ -94,9 +113,16 @@ ExtPEXMessage::mapping_type ext_pex_key_names[ExtPEXMessage::length] = { - { key_pex_added, "added" }, - }; - -+ExtMetadataMessage::mapping_type ext_metadata_key_names[ExtMetadataMessage::length] = { -+ { key_msgType, "msg_type" }, -+ { key_piece, "piece" }, -+ { key_totalSize, "total_size" }, -+}; -+ - ext_handshake_keys message_keys[ProtocolExtension::FIRST_INVALID] = { - key_handshake_LAST, // Handshake, not actually used. - key_m_utPex, -+ key_m_utMetadata, - }; - - template<> -@@ -105,6 +131,9 @@ const ExtHandshakeMessage::key_map_init ExtHandshakeMessage::base_type::keyMap(e - template<> - const ExtPEXMessage::key_map_init ExtPEXMessage::base_type::keyMap(ext_pex_key_names); - -+template<> -+const ExtMetadataMessage::key_map_init ExtMetadataMessage::base_type::keyMap(ext_metadata_key_names); -+ - void - ProtocolExtension::cleanup() { - // if (is_default()) -@@ -160,7 +189,11 @@ ProtocolExtension::generate_handshake_message() { - message[key_v] = SimpleString("libTorrent " VERSION); - message[key_reqq] = 2048; // maximum request queue size - -+ if (!m_download->info()->is_meta_download()) -+ message[key_metadataSize] = m_download->info()->metadata_size(); -+ - message[key_m_utPex] = is_local_enabled(UT_PEX) ? UT_PEX : 0; -+ message[key_m_utMetadata] = UT_METADATA; - - char buffer[1024]; - object_buffer_t result = staticMap_write_bencode_c(object_write_to_buffer, NULL, std::make_pair(buffer, buffer + sizeof(buffer)), message); -@@ -224,7 +257,7 @@ ProtocolExtension::generate_ut_pex_message(const PEXList& added, const PEXList& - - void - ProtocolExtension::read_start(int type, uint32_t length, bool skip) { -- if (is_default() || (type >= FIRST_INVALID) || length > (1 << 14)) -+ if (is_default() || (type >= FIRST_INVALID) || length > (1 << 15)) - throw communication_error("Received invalid extension message."); - - if (m_read != NULL || length < 0) -@@ -244,19 +277,25 @@ ProtocolExtension::read_start(int type, uint32_t length, bool skip) { - m_readPos = m_read = new char[length]; - } - --void -+bool - ProtocolExtension::read_done() { -+ bool blocked = false; -+ - try { - switch(m_readType) { - case SKIP_EXTENSION: - break; - - case HANDSHAKE: -- parse_handshake(); -+ blocked = parse_handshake(); - break; - - case UT_PEX: -- parse_ut_pex(); -+ blocked = parse_ut_pex(); -+ break; -+ -+ case UT_METADATA: -+ blocked = parse_ut_metadata(); - break; - - default: -@@ -272,6 +311,8 @@ ProtocolExtension::read_done() { - - m_readType = FIRST_INVALID; - m_flags |= flag_received_ext; -+ -+ return !blocked; - } - - // Called whenever peer enables or disables an extension. -@@ -285,7 +326,7 @@ ProtocolExtension::peer_toggle_remote(int type, bool active) { - } - } - --void -+bool - ProtocolExtension::parse_handshake() { - ExtHandshakeMessage message; - staticMap_read_bencode(m_read, m_readPos, message); -@@ -323,10 +364,15 @@ ProtocolExtension::parse_handshake() { - if (message[key_reqq].is_value()) - m_maxQueueLength = message[key_reqq].as_value(); - -+ if (message[key_metadataSize].is_value()) -+ m_download->set_metadata_size(message[key_metadataSize].as_value()); -+ - m_flags &= ~flag_initial_handshake; -+ -+ return false; - } - --void -+bool - ProtocolExtension::parse_ut_pex() { - // Ignore message if we're still in the handshake (no connection - // yet), or no peers are present. -@@ -336,11 +382,11 @@ ProtocolExtension::parse_ut_pex() { - - // TODO: Check if pex is enabled? - if (!message[key_pex_added].is_sstring()) -- return; -+ return false; - - SimpleString peers = message[key_pex_added].as_sstring(); - if (peers.empty()) -- return; -+ return false; - - AddressList l; - l.parse_address_compact(peers); -@@ -348,6 +394,82 @@ ProtocolExtension::parse_ut_pex() { - l.erase(std::unique(l.begin(), l.end()), l.end()); - - m_download->peer_list()->insert_available(&l); -+ -+ return false; -+} -+ -+bool -+ProtocolExtension::parse_ut_metadata() { -+ ExtMetadataMessage message; -+ -+ // Piece data comes after bencoded extension message. -+ const char* dataStart = staticMap_read_bencode(m_read, m_readPos, message); -+ -+ switch(message[key_msgType].as_value()) { -+ case 0: -+ // Can't process new request while still having data to send. -+ if (has_pending_message()) -+ return true; -+ -+ send_metadata_piece(message[key_piece].as_value()); -+ break; -+ -+ case 1: -+ if (m_connection == NULL) -+ break; -+ -+ m_connection->receive_metadata_piece(message[key_piece].as_value(), dataStart, m_readPos - dataStart); -+ break; -+ -+ case 2: -+ if (m_connection != NULL) -+ m_connection->receive_metadata_piece(message[key_piece].as_value(), NULL, 0); -+ break; -+ }; -+ -+ return false; -+} -+ -+void -+ProtocolExtension::send_metadata_piece(size_t piece) { -+ // Reject out-of-range piece, or if we don't have the complete metadata yet. -+ size_t metadataSize = m_download->info()->metadata_size(); -+ size_t pieceEnd = (metadataSize + metadata_piece_size - 1) >> metadata_piece_shift; -+ -+ if (m_download->info()->is_meta_download() || piece >= pieceEnd) { -+ // reject: { "msg_type" => 2, "piece" => ... } -+ m_pendingType = UT_METADATA; -+ m_pending = build_bencode(40, "d8:msg_typei2e5:piecei%zuee", piece); -+ return; -+ } -+ -+ // These messages will be rare, so we'll just build the -+ // metadata here instead of caching it uselessly. -+ char* buffer = new char[metadataSize]; -+ object_buffer_t result = object_write_bencode_c(object_write_to_buffer, NULL, object_buffer_t(buffer, buffer + metadataSize), -+ &(*manager->download_manager()->find(m_download->info()))->bencode()->get_key("info")); -+ -+ // data: { "msg_type" => 1, "piece" => ..., "total_size" => ... } followed by piece data (outside of dictionary) -+ size_t length = piece == pieceEnd - 1 ? m_download->info()->metadata_size() % metadata_piece_size : metadata_piece_size; -+ m_pendingType = UT_METADATA; -+ m_pending = build_bencode(length + 128, "d8:msg_typei1e5:piecei%zue10:total_sizei%zuee", piece, metadataSize); -+ -+ memcpy(m_pending.end(), buffer + (piece << metadata_piece_shift), length); -+ m_pending.set(m_pending.data(), m_pending.end() + length, m_pending.owned()); -+ delete [] buffer; -+} -+ -+bool -+ProtocolExtension::request_metadata_piece(const Piece* p) { -+ if (p->offset() % metadata_piece_size) -+ throw internal_error("ProtocolExtension::request_metadata_piece got misaligned piece offset."); -+ -+ if (has_pending_message()) -+ return false; -+ -+ m_pendingType = UT_METADATA; -+ m_pending = build_bencode(40, "d8:msg_typei0e5:piecei%uee", (unsigned)(p->offset() >> metadata_piece_shift)); -+ return true; - } - - } -diff --git a/src/protocol/extensions.h b/src/protocol/extensions.h -index 96ed652..485e7d7 100644 ---- a/src/protocol/extensions.h -+++ b/src/protocol/extensions.h -@@ -60,6 +60,7 @@ public: - typedef enum { - HANDSHAKE = 0, - UT_PEX, -+ UT_METADATA, - - FIRST_INVALID, // first invalid message ID - -@@ -81,6 +82,10 @@ public: - // Number of extensions we support, not counting handshake. - static const int extension_count = FIRST_INVALID - HANDSHAKE - 1; - -+ // Fixed size of a metadata piece (16 KB). -+ static const size_t metadata_piece_shift = 14; -+ static const size_t metadata_piece_size = 1 << metadata_piece_shift; -+ - ProtocolExtension(); - ~ProtocolExtension() { delete [] m_read; } - -@@ -91,6 +96,7 @@ public: - static ProtocolExtension make_default(); - - void set_info(PeerInfo* peerInfo, DownloadMain* download) { m_peerInfo = peerInfo; m_download = download; } -+ void set_connection(PeerConnectionBase* c) { m_connection = c; } - - DataBuffer generate_handshake_message(); - static DataBuffer generate_toggle_message(MessageType t, bool on); -@@ -112,7 +118,7 @@ public: - - // Handle reading extension data from peer. - void read_start(int type, uint32_t length, bool skip); -- void read_done(); -+ bool read_done(); - - char* read_position() { return m_readPos; } - bool read_move(uint32_t v) { m_readPos += v; return (m_readLeft -= v) == 0; } -@@ -132,13 +138,23 @@ public: - void clear_initial_pex() { m_flags &= ~flag_initial_pex; } - void reset() { std::memset(&m_idMap, 0, sizeof(m_idMap)); } - -+ bool request_metadata_piece(const Piece* p); -+ -+ // To handle cases where the extension protocol needs to send a reply. -+ bool has_pending_message() const { return m_pendingType != HANDSHAKE; } -+ MessageType pending_message_type() const { return m_pendingType; } -+ DataBuffer pending_message_data() { return m_pending.release(); } -+ void clear_pending_message() { if (m_pending.empty()) m_pendingType = HANDSHAKE; } -+ - private: -- void parse_handshake(); -- void parse_ut_pex(); -+ bool parse_handshake(); -+ bool parse_ut_pex(); -+ bool parse_ut_metadata(); - - static DataBuffer build_bencode(size_t maxLength, const char* format, ...) ATTRIBUTE_PRINTF(2); - - void peer_toggle_remote(int type, bool active); -+ void send_metadata_piece(size_t piece); - - // Map of IDs peer uses for each extension message type, excluding - // HANDSHAKE. -@@ -149,11 +165,15 @@ private: - int m_flags; - PeerInfo* m_peerInfo; - DownloadMain* m_download; -+ PeerConnectionBase* m_connection; - - uint8_t m_readType; - uint32_t m_readLeft; - char* m_read; - char* m_readPos; -+ -+ MessageType m_pendingType; -+ DataBuffer m_pending; - }; - - inline -@@ -163,10 +183,13 @@ ProtocolExtension::ProtocolExtension() : - m_flags(flag_local_enabled_base | flag_remote_supported_base | flag_initial_handshake), - m_peerInfo(NULL), - m_download(NULL), -+ m_connection(NULL), - m_readType(FIRST_INVALID), -- m_read(NULL) { -+ m_read(NULL), -+ m_pendingType(HANDSHAKE) { - - reset(); -+ set_local_enabled(UT_METADATA); - } - - inline ProtocolExtension -diff --git a/src/protocol/handshake.cc b/src/protocol/handshake.cc -index d863f7b..7fb389b 100644 ---- a/src/protocol/handshake.cc -+++ b/src/protocol/handshake.cc -@@ -723,6 +723,17 @@ restart: - - case READ_MESSAGE: - case POST_HANDSHAKE: -+ // For meta-downloads, we aren't interested in the bitfield or -+ // extension messages here, PCMetadata handles all that. The -+ // bitfield only refers to the single-chunk meta-data, so fake that. -+ if (m_download->info()->is_meta_download()) { -+ m_bitfield.set_size_bits(1); -+ m_bitfield.allocate(); -+ m_bitfield.set(0); -+ read_done(); -+ break; -+ } -+ - fill_read_buffer(5); - - // Received a keep-alive message which means we won't be -@@ -1022,6 +1033,10 @@ Handshake::prepare_peer_info() { - std::memcpy(m_peerInfo->set_options(), m_options, 8); - m_peerInfo->mutable_id().assign((const char*)m_readBuffer.position()); - m_readBuffer.consume(20); -+ -+ // For meta downloads, we require support of the extension protocol. -+ if (m_download->info()->is_meta_download() && !m_peerInfo->supports_extensions()) -+ throw handshake_error(ConnectionManager::handshake_dropped, e_handshake_unwanted_connection); - } - - void -diff --git a/src/protocol/peer_connection_base.cc b/src/protocol/peer_connection_base.cc -index ab043a6..815ea93 100644 ---- a/src/protocol/peer_connection_base.cc -+++ b/src/protocol/peer_connection_base.cc -@@ -93,8 +93,7 @@ PeerConnectionBase::~PeerConnectionBase() { - if (m_extensions != NULL && !m_extensions->is_default()) - delete m_extensions; - -- if (m_extensionMessage.owned()) -- m_extensionMessage.clear(); -+ m_extensionMessage.clear(); - } - - void -@@ -116,6 +115,8 @@ PeerConnectionBase::initialize(DownloadMain* download, PeerInfo* peerInfo, Socke - m_encryption = *encryptionInfo; - m_extensions = extensions; - -+ m_extensions->set_connection(this); -+ - m_peerChunks.set_peer_info(m_peerInfo); - m_peerChunks.bitfield()->swap(*bitfield); - -@@ -581,8 +582,12 @@ PeerConnectionBase::down_extension() { - m_extensions->read_move(bytes); - } - -- if (m_extensions->is_complete()) -- m_extensions->read_done(); -+ // If extension can't be processed yet (due to a pending write), -+ // disable reads until the pending message is completely sent. -+ if (m_extensions->is_complete() && !m_extensions->is_invalid() && !m_extensions->read_done()) { -+ manager->poll()->remove_read(this); -+ return false; -+ } - - return m_extensions->is_complete(); - } -@@ -693,12 +698,15 @@ PeerConnectionBase::up_extension() { - if (m_extensionOffset < m_extensionMessage.length()) - return false; - -- // clear() deletes the buffer, only do that if we made a copy, -- // otherwise the buffer is shared among all connections. -- if (m_extensionMessage.owned()) -- m_extensionMessage.clear(); -- else -- m_extensionMessage.set(NULL, NULL, false); -+ m_extensionMessage.clear(); -+ -+ // If we have an unprocessed message, process it now and enable reads again. -+ if (m_extensions->is_complete() && !m_extensions->is_invalid()) { -+ if (!m_extensions->read_done()) -+ throw internal_error("PeerConnectionBase::up_extension could not process complete extension message."); -+ -+ manager->poll()->insert_read(this); -+ } - - return true; - } -@@ -857,4 +865,16 @@ PeerConnectionBase::send_pex_message() { - return true; - } - -+// Extension protocol needs to send a reply. -+bool -+PeerConnectionBase::send_ext_message() { -+ write_prepare_extension(m_extensions->pending_message_type(), m_extensions->pending_message_data()); -+ m_extensions->clear_pending_message(); -+ return true; -+} -+ -+void -+PeerConnectionBase::receive_metadata_piece(uint32_t piece, const char* data, uint32_t length) { -+} -+ - } -diff --git a/src/protocol/peer_connection_base.h b/src/protocol/peer_connection_base.h -index 2994963..d131341 100644 ---- a/src/protocol/peer_connection_base.h -+++ b/src/protocol/peer_connection_base.h -@@ -140,6 +140,9 @@ public: - void read_insert_poll_safe(); - void write_insert_poll_safe(); - -+ // Communication with the protocol extensions -+ virtual void receive_metadata_piece(uint32_t piece, const char* data, uint32_t length); -+ - protected: - static const uint32_t extension_must_encrypt = ~uint32_t(); - -@@ -179,6 +182,7 @@ protected: - bool try_request_pieces(); - - bool send_pex_message(); -+ bool send_ext_message(); - - DownloadMain* m_download; - -diff --git a/src/protocol/peer_connection_leech.cc b/src/protocol/peer_connection_leech.cc -index a75d333..36c6d7a 100644 ---- a/src/protocol/peer_connection_leech.cc -+++ b/src/protocol/peer_connection_leech.cc -@@ -333,9 +333,13 @@ PeerConnection<type>::read_message() { - m_down->set_state(ProtocolRead::READ_EXTENSION); - } - -- if (down_extension()) -- m_down->set_state(ProtocolRead::IDLE); -+ if (!down_extension()) -+ return false; - -+ if (m_extensions->has_pending_message()) -+ write_insert_poll_safe(); -+ -+ m_down->set_state(ProtocolRead::IDLE); - return true; - - default: -@@ -433,6 +437,9 @@ PeerConnection<type>::event_read() { - if (!down_extension()) - return; - -+ if (m_extensions->has_pending_message()) -+ write_insert_poll_safe(); -+ - m_down->set_state(ProtocolRead::IDLE); - break; - -@@ -546,6 +553,10 @@ PeerConnection<type>::fill_write_buffer() { - send_pex_message()) { - // Don't do anything else if send_pex_message() succeeded. - -+ } else if (m_extensions->has_pending_message() && m_up->can_write_extension() && -+ send_ext_message()) { -+ // Same. -+ - } else if (!m_upChoke.choked() && - !m_peerChunks.upload_queue()->empty() && - m_up->can_write_piece() && -diff --git a/src/protocol/peer_connection_metadata.cc b/src/protocol/peer_connection_metadata.cc -new file mode 100644 -index 0000000..24f13ca ---- /dev/null -+++ b/src/protocol/peer_connection_metadata.cc -@@ -0,0 +1,461 @@ -+// libTorrent - BitTorrent library -+// Copyright (C) 2005-2007, Jari Sundell -+// -+// This program is free software; you can redistribute it and/or modify -+// it under the terms of the GNU General Public License as published by -+// the Free Software Foundation; either version 2 of the License, or -+// (at your option) any later version. -+// -+// This program is distributed in the hope that it will be useful, -+// but WITHOUT ANY WARRANTY; without even the implied warranty of -+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+// GNU General Public License for more details. -+// -+// You should have received a copy of the GNU General Public License -+// along with this program; if not, write to the Free Software -+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+// -+// In addition, as a special exception, the copyright holders give -+// permission to link the code of portions of this program with the -+// OpenSSL library under certain conditions as described in each -+// individual source file, and distribute linked combinations -+// including the two. -+// -+// You must obey the GNU General Public License in all respects for -+// all of the code used other than OpenSSL. If you modify file(s) -+// with this exception, you may extend this exception to your version -+// of the file(s), but you are not obligated to do so. If you do not -+// wish to do so, delete this exception statement from your version. -+// If you delete this exception statement from all source files in the -+// program, then also delete it here. -+// -+// Contact: Jari Sundell <jaris@ifi.uio.no> -+// -+// Skomakerveien 33 -+// 3185 Skoppum, NORWAY -+ -+#include "config.h" -+ -+#include <cstring> -+#include <sstream> -+ -+#include "data/chunk_list_node.h" -+#include "download/choke_manager.h" -+#include "download/chunk_selector.h" -+#include "download/chunk_statistics.h" -+#include "download/download_info.h" -+#include "download/download_main.h" -+#include "torrent/dht_manager.h" -+#include "torrent/peer/connection_list.h" -+#include "torrent/peer/peer_info.h" -+ -+#include "extensions.h" -+#include "peer_connection_metadata.h" -+ -+namespace torrent { -+ -+PeerConnectionMetadata::~PeerConnectionMetadata() { -+} -+ -+void -+PeerConnectionMetadata::initialize_custom() { -+} -+ -+void -+PeerConnectionMetadata::update_interested() { -+} -+ -+bool -+PeerConnectionMetadata::receive_keepalive() { -+ if (cachedTime - m_timeLastRead > rak::timer::from_seconds(240)) -+ return false; -+ -+ m_tryRequest = true; -+ -+ // There's no point in adding ourselves to the write poll if the -+ // buffer is full, as that will already have been taken care of. -+ if (m_up->get_state() == ProtocolWrite::IDLE && -+ m_up->can_write_keepalive()) { -+ -+ write_insert_poll_safe(); -+ -+ ProtocolBuffer<512>::iterator old_end = m_up->buffer()->end(); -+ m_up->write_keepalive(); -+ -+ if (is_encrypted()) -+ m_encryption.encrypt(old_end, m_up->buffer()->end() - old_end); -+ } -+ -+ return true; -+} -+ -+// We keep the message in the buffer if it is incomplete instead of -+// keeping the state and remembering the read information. This -+// shouldn't happen very often compared to full reads. -+inline bool -+PeerConnectionMetadata::read_message() { -+ ProtocolBuffer<512>* buf = m_down->buffer(); -+ -+ if (buf->remaining() < 4) -+ return false; -+ -+ // Remember the start of the message so we may reset it if we don't -+ // have the whole message. -+ ProtocolBuffer<512>::iterator beginning = buf->position(); -+ -+ uint32_t length = buf->read_32(); -+ -+ if (length == 0) { -+ // Keepalive message. -+ m_down->set_last_command(ProtocolBase::KEEP_ALIVE); -+ -+ return true; -+ -+ } else if (buf->remaining() < 1) { -+ buf->set_position_itr(beginning); -+ return false; -+ -+ } else if (length > (1 << 20)) { -+ throw communication_error("PeerConnection::read_message() got an invalid message length."); -+ } -+ -+ m_down->set_last_command((ProtocolBase::Protocol)buf->peek_8()); -+ -+ // Ignore most messages, they aren't relevant for a metadata download. -+ switch (buf->read_8()) { -+ case ProtocolBase::CHOKE: -+ case ProtocolBase::UNCHOKE: -+ case ProtocolBase::INTERESTED: -+ case ProtocolBase::NOT_INTERESTED: -+ return true; -+ -+ case ProtocolBase::HAVE: -+ if (!m_down->can_read_have_body()) -+ break; -+ -+ buf->read_32(); -+ return true; -+ -+ case ProtocolBase::REQUEST: -+ if (!m_down->can_read_request_body()) -+ break; -+ -+ m_down->read_request(); -+ return true; -+ -+ case ProtocolBase::PIECE: -+ throw communication_error("Received a piece but the connection is strictly for meta data."); -+ -+ case ProtocolBase::CANCEL: -+ if (!m_down->can_read_cancel_body()) -+ break; -+ -+ m_down->read_request(); -+ return true; -+ -+ case ProtocolBase::PORT: -+ if (!m_down->can_read_port_body()) -+ break; -+ -+ manager->dht_manager()->add_node(m_peerInfo->socket_address(), m_down->buffer()->read_16()); -+ return true; -+ -+ case ProtocolBase::EXTENSION_PROTOCOL: -+ if (!m_down->can_read_extension_body()) -+ break; -+ -+ if (m_extensions->is_default()) { -+ m_extensions = new ProtocolExtension(); -+ m_extensions->set_info(m_peerInfo, m_download); -+ } -+ -+ { -+ int extension = m_down->buffer()->read_8(); -+ m_extensions->read_start(extension, length - 2, (extension == ProtocolExtension::UT_PEX) && !m_download->want_pex_msg()); -+ m_down->set_state(ProtocolRead::READ_EXTENSION); -+ } -+ -+ if (!down_extension()) -+ return false; -+ -+ // Drop peer if it disabled the metadata extension. -+ if (!m_extensions->is_remote_supported(ProtocolExtension::UT_METADATA)) -+ throw close_connection(); -+ -+ m_down->set_state(ProtocolRead::IDLE); -+ m_tryRequest = true; -+ write_insert_poll_safe(); -+ -+ return true; -+ -+ case ProtocolBase::BITFIELD: -+ // Discard the bitfield sent by the peer. -+ m_skipLength = length - 1; -+ m_down->set_state(ProtocolRead::READ_SKIP_PIECE); -+ return false; -+ -+ default: -+ throw communication_error("Received unsupported message type."); -+ } -+ -+ // We were unsuccessfull in reading the message, need more data. -+ buf->set_position_itr(beginning); -+ return false; -+} -+ -+void -+PeerConnectionMetadata::event_read() { -+ m_timeLastRead = cachedTime; -+ -+ // Need to make sure ProtocolBuffer::end() is pointing to the end of -+ // the unread data, and that the unread data starts from the -+ // beginning of the buffer. Or do we use position? Propably best, -+ // therefor ProtocolBuffer::position() points to the beginning of -+ // the unused data. -+ -+ try { -+ -+ // Normal read. -+ // -+ // We rarely will read zero bytes as the read of 64 bytes will -+ // almost always either not fill up or it will require additional -+ // reads. -+ // -+ // Only loop when end hits 64. -+ -+ do { -+ switch (m_down->get_state()) { -+ case ProtocolRead::IDLE: -+ if (m_down->buffer()->size_end() < read_size) { -+ unsigned int length = read_stream_throws(m_down->buffer()->end(), read_size - m_down->buffer()->size_end()); -+ m_down->throttle()->node_used_unthrottled(length); -+ -+ if (is_encrypted()) -+ m_encryption.decrypt(m_down->buffer()->end(), length); -+ -+ m_down->buffer()->move_end(length); -+ } -+ -+ while (read_message()); -+ -+ if (m_down->buffer()->size_end() == read_size) { -+ m_down->buffer()->move_unused(); -+ break; -+ } else { -+ m_down->buffer()->move_unused(); -+ return; -+ } -+ -+ case ProtocolRead::READ_EXTENSION: -+ if (!down_extension()) -+ return; -+ -+ // Drop peer if it disabled the metadata extension. -+ if (!m_extensions->is_remote_supported(ProtocolExtension::UT_METADATA)) -+ throw close_connection(); -+ -+ m_down->set_state(ProtocolRead::IDLE); -+ m_tryRequest = true; -+ write_insert_poll_safe(); -+ break; -+ -+ // Actually skipping the bitfield. -+ // We never receive normal piece messages anyway. -+ case ProtocolRead::READ_SKIP_PIECE: -+ if (!read_skip_bitfield()) -+ return; -+ -+ m_down->set_state(ProtocolRead::IDLE); -+ break; -+ -+ default: -+ throw internal_error("PeerConnection::event_read() wrong state."); -+ } -+ -+ // Figure out how to get rid of the shouldLoop boolean. -+ } while (true); -+ -+ // Exception handlers: -+ -+ } catch (close_connection& e) { -+ m_download->connection_list()->erase(this, 0); -+ -+ } catch (blocked_connection& e) { -+ m_download->info()->signal_network_log().emit("Momentarily blocked read connection."); -+ m_download->connection_list()->erase(this, 0); -+ -+ } catch (network_error& e) { -+ m_download->connection_list()->erase(this, 0); -+ -+ } catch (storage_error& e) { -+ m_download->info()->signal_storage_error().emit(e.what()); -+ m_download->connection_list()->erase(this, 0); -+ -+ } catch (base_error& e) { -+ std::stringstream s; -+ s << "Connection read fd(" << get_fd().get_fd() << ',' << m_down->get_state() << ',' << m_down->last_command() << ") \"" << e.what() << '"'; -+ -+ throw internal_error(s.str()); -+ } -+} -+ -+inline void -+PeerConnectionMetadata::fill_write_buffer() { -+ ProtocolBuffer<512>::iterator old_end = m_up->buffer()->end(); -+ -+ if (m_tryRequest) -+ m_tryRequest = try_request_metadata_pieces(); -+ -+ if (m_sendPEXMask && m_up->can_write_extension() && -+ send_pex_message()) { -+ // Don't do anything else if send_pex_message() succeeded. -+ -+ } else if (m_extensions->has_pending_message() && m_up->can_write_extension() && -+ send_ext_message()) { -+ // Same. -+ } -+ -+ if (is_encrypted()) -+ m_encryption.encrypt(old_end, m_up->buffer()->end() - old_end); -+} -+ -+void -+PeerConnectionMetadata::event_write() { -+ try { -+ -+ do { -+ -+ switch (m_up->get_state()) { -+ case ProtocolWrite::IDLE: -+ -+ fill_write_buffer(); -+ -+ if (m_up->buffer()->remaining() == 0) { -+ manager->poll()->remove_write(this); -+ return; -+ } -+ -+ m_up->set_state(ProtocolWrite::MSG); -+ -+ case ProtocolWrite::MSG: -+ if (!m_up->buffer()->consume(m_up->throttle()->node_used_unthrottled(write_stream_throws(m_up->buffer()->position(), m_up->buffer()->remaining())))) -+ return; -+ -+ m_up->buffer()->reset(); -+ -+ if (m_up->last_command() != ProtocolBase::EXTENSION_PROTOCOL) { -+ m_up->set_state(ProtocolWrite::IDLE); -+ break; -+ } -+ -+ m_up->set_state(ProtocolWrite::WRITE_EXTENSION); -+ -+ case ProtocolWrite::WRITE_EXTENSION: -+ if (!up_extension()) -+ return; -+ -+ m_up->set_state(ProtocolWrite::IDLE); -+ break; -+ -+ default: -+ throw internal_error("PeerConnection::event_write() wrong state."); -+ } -+ -+ } while (true); -+ -+ } catch (close_connection& e) { -+ m_download->connection_list()->erase(this, 0); -+ -+ } catch (blocked_connection& e) { -+ m_download->info()->signal_network_log().emit("Momentarily blocked write connection."); -+ m_download->connection_list()->erase(this, 0); -+ -+ } catch (network_error& e) { -+ m_download->connection_list()->erase(this, 0); -+ -+ } catch (storage_error& e) { -+ m_download->info()->signal_storage_error().emit(e.what()); -+ m_download->connection_list()->erase(this, 0); -+ -+ } catch (base_error& e) { -+ std::stringstream s; -+ s << "Connection write fd(" << get_fd().get_fd() << ',' << m_up->get_state() << ',' << m_up->last_command() << ") \"" << e.what() << '"'; -+ -+ throw internal_error(s.str()); -+ } -+} -+ -+bool -+PeerConnectionMetadata::read_skip_bitfield() { -+ if (m_down->buffer()->remaining()) { -+ uint32_t length = std::min(m_skipLength, (uint32_t)m_down->buffer()->remaining()); -+ m_down->buffer()->consume(length); -+ m_skipLength -= length; -+ } -+ -+ if (m_skipLength) { -+ uint32_t length = std::min(m_skipLength, (uint32_t)null_buffer_size); -+ length = read_stream_throws(m_nullBuffer, length); -+ if (!length) -+ return false; -+ m_skipLength -= length; -+ } -+ -+ return !m_skipLength; -+} -+ -+// Same as the PCB code, but only one at a time and with the extension protocol. -+bool -+PeerConnectionMetadata::try_request_metadata_pieces() { -+ if (m_download->file_list()->chunk_size() == 1 || !m_extensions->is_remote_supported(ProtocolExtension::UT_METADATA)) -+ return false; -+ -+ if (download_queue()->queued_empty()) -+ m_downStall = 0; -+ -+ uint32_t pipeSize = download_queue()->calculate_pipe_size(m_peerChunks.download_throttle()->rate()->rate()); -+ -+ // Don't start requesting if we can't do it in large enough chunks. -+ if (download_queue()->queued_size() >= (pipeSize + 10) / 2) -+ return false; -+ -+ if (!download_queue()->queued_size() < pipeSize || !m_up->can_write_extension() || -+ m_extensions->has_pending_message()) -+ return false; -+ -+ const Piece* p = download_queue()->delegate(); -+ -+ if (p == NULL) -+ return false; -+ -+ if (!m_download->file_list()->is_valid_piece(*p) || !m_peerChunks.bitfield()->get(p->index())) -+ throw internal_error("PeerConnectionMetadata::try_request_metadata_pieces() tried to use an invalid piece."); -+ -+ return m_extensions->request_metadata_piece(p); -+} -+ -+void -+PeerConnectionMetadata::receive_metadata_piece(uint32_t piece, const char* data, uint32_t length) { -+ if (data == NULL) { -+ // Length is not set in a reject message. -+ length = ProtocolExtension::metadata_piece_size; -+ if ((piece << ProtocolExtension::metadata_piece_shift) + ProtocolExtension::metadata_piece_size >= m_download->file_list()->size_bytes()) -+ length = m_download->file_list()->chunk_size() % ProtocolExtension::metadata_piece_size; -+ m_tryRequest = false; -+ read_cancel_piece(Piece(0, piece << ProtocolExtension::metadata_piece_shift, length)); -+ return; -+ } -+ -+ if (!down_chunk_start(Piece(0, piece << ProtocolExtension::metadata_piece_shift, length))) -+ down_chunk_skip_process(data, length); -+ else -+ down_chunk_process(data, length); -+ -+ if (!m_downloadQueue.transfer()->is_finished()) -+ throw internal_error("PeerConnectionMetadata::receive_metadata_piece did not have complete piece."); -+ -+ m_tryRequest = true; -+ down_chunk_finished(); -+} -+ -+} -diff --git a/src/protocol/peer_connection_metadata.h b/src/protocol/peer_connection_metadata.h -new file mode 100644 -index 0000000..127700a ---- /dev/null -+++ b/src/protocol/peer_connection_metadata.h -@@ -0,0 +1,73 @@ -+// libTorrent - BitTorrent library -+// Copyright (C) 2005-2007, Jari Sundell -+// -+// This program is free software; you can redistribute it and/or modify -+// it under the terms of the GNU General Public License as published by -+// the Free Software Foundation; either version 2 of the License, or -+// (at your option) any later version. -+// -+// This program is distributed in the hope that it will be useful, -+// but WITHOUT ANY WARRANTY; without even the implied warranty of -+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+// GNU General Public License for more details. -+// -+// You should have received a copy of the GNU General Public License -+// along with this program; if not, write to the Free Software -+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+// -+// In addition, as a special exception, the copyright holders give -+// permission to link the code of portions of this program with the -+// OpenSSL library under certain conditions as described in each -+// individual source file, and distribute linked combinations -+// including the two. -+// -+// You must obey the GNU General Public License in all respects for -+// all of the code used other than OpenSSL. If you modify file(s) -+// with this exception, you may extend this exception to your version -+// of the file(s), but you are not obligated to do so. If you do not -+// wish to do so, delete this exception statement from your version. -+// If you delete this exception statement from all source files in the -+// program, then also delete it here. -+// -+// Contact: Jari Sundell <jaris@ifi.uio.no> -+// -+// Skomakerveien 33 -+// 3185 Skoppum, NORWAY -+ -+#ifndef LIBTORRENT_PROTOCOL_PEER_CONNECTION_METADATA_H -+#define LIBTORRENT_PROTOCOL_PEER_CONNECTION_METADATA_H -+ -+#include "peer_connection_base.h" -+ -+#include "torrent/download.h" -+ -+namespace torrent { -+ -+class PeerConnectionMetadata : public PeerConnectionBase { -+public: -+ ~PeerConnectionMetadata(); -+ -+ virtual void initialize_custom(); -+ virtual void update_interested(); -+ virtual bool receive_keepalive(); -+ -+ virtual void event_read(); -+ virtual void event_write(); -+ -+ virtual void receive_metadata_piece(uint32_t piece, const char* data, uint32_t length); -+ -+private: -+ inline bool read_message(); -+ -+ bool read_skip_bitfield(); -+ -+ bool try_request_metadata_pieces(); -+ -+ inline void fill_write_buffer(); -+ -+ uint32_t m_skipLength; -+}; -+ -+} -+ -+#endif -diff --git a/src/protocol/peer_factory.cc b/src/protocol/peer_factory.cc -index 7ab9fe8..cfe6a1e 100644 ---- a/src/protocol/peer_factory.cc -+++ b/src/protocol/peer_factory.cc -@@ -38,6 +38,7 @@ - - #include "peer_factory.h" - #include "peer_connection_leech.h" -+#include "peer_connection_metadata.h" - - namespace torrent { - -@@ -62,4 +63,11 @@ createPeerConnectionInitialSeed(bool encrypted) { - return pc; - } - -+PeerConnectionBase* -+createPeerConnectionMetadata(bool encrypted) { -+ PeerConnectionBase* pc = new PeerConnectionMetadata; -+ -+ return pc; -+} -+ - } -diff --git a/src/protocol/peer_factory.h b/src/protocol/peer_factory.h -index 363a5c3..f22d76f 100644 ---- a/src/protocol/peer_factory.h -+++ b/src/protocol/peer_factory.h -@@ -44,6 +44,7 @@ class PeerConnectionBase; - PeerConnectionBase* createPeerConnectionDefault(bool encrypted); - PeerConnectionBase* createPeerConnectionSeed(bool encrypted); - PeerConnectionBase* createPeerConnectionInitialSeed(bool encrypted); -+PeerConnectionBase* createPeerConnectionMetadata(bool encrypted); - - } - -diff --git a/src/torrent/data/file_list.cc b/src/torrent/data/file_list.cc -index 2f5d8d2..7208612 100644 ---- a/src/torrent/data/file_list.cc -+++ b/src/torrent/data/file_list.cc -@@ -466,6 +466,18 @@ FileList::open(int flags) { - - m_isOpen = true; - m_frozenRootDir = m_rootDir; -+ -+ // For meta-downloads, if the file exists, we have to assume that -+ // it is either 0 or 1 length or the correct size. If the size -+ // turns out wrong later, a communication_error will be thrown elsewhere -+ // to alert the user in this (unlikely) case. -+ if (size_bytes() < 2) { -+ rak::file_stat stat; -+ -+ // This probably recurses into open() once, but that is harmless. -+ if (stat.update((*begin())->frozen_path()) && stat.size() > 1) -+ return reset_filesize(stat.size()); -+ } - } - - void -@@ -661,4 +673,14 @@ FileList::update_completed() { - } - } - -+void -+FileList::reset_filesize(int64_t size) { -+ close(); -+ m_chunkSize = size; -+ m_torrentSize = size; -+ (*begin())->set_size_bytes(size); -+ (*begin())->set_range(m_chunkSize); -+ open(open_no_create); -+} -+ - } -diff --git a/src/torrent/data/file_list.h b/src/torrent/data/file_list.h -index bcc8939..60d418a 100644 ---- a/src/torrent/data/file_list.h -+++ b/src/torrent/data/file_list.h -@@ -167,6 +167,10 @@ protected: - iterator inc_completed(iterator firstItr, uint32_t index) LIBTORRENT_NO_EXPORT; - void update_completed() LIBTORRENT_NO_EXPORT; - -+ // Used for meta downloads; we only know the -+ // size after the first extension handshake. -+ void reset_filesize(int64_t) LIBTORRENT_NO_EXPORT; -+ - private: - bool open_file(File* node, const Path& lastPath, int flags) LIBTORRENT_NO_EXPORT; - void make_directory(Path::const_iterator pathBegin, Path::const_iterator pathEnd, Path::const_iterator startItr) LIBTORRENT_NO_EXPORT; -diff --git a/src/torrent/download.cc b/src/torrent/download.cc -index d6cc199..49daad9 100644 ---- a/src/torrent/download.cc -+++ b/src/torrent/download.cc -@@ -225,6 +225,11 @@ Download::set_pex_enabled(bool enabled) { - m_ptr->info()->set_pex_enabled(enabled); - } - -+bool -+Download::is_meta_download() const { -+ return m_ptr->info()->is_meta_download(); -+} -+ - const std::string& - Download::name() const { - if (m_ptr == NULL) -@@ -504,6 +509,11 @@ Download::connection_type() const { - - void - Download::set_connection_type(ConnectionType t) { -+ if (m_ptr->info()->is_meta_download()) { -+ m_ptr->main()->connection_list()->slot_new_connection(&createPeerConnectionMetadata); -+ return; -+ } -+ - switch (t) { - case CONNECTION_LEECH: - m_ptr->main()->connection_list()->slot_new_connection(&createPeerConnectionDefault); -diff --git a/src/torrent/download.h b/src/torrent/download.h -index 5e9700e..5d16d4e 100644 ---- a/src/torrent/download.h -+++ b/src/torrent/download.h -@@ -100,6 +100,8 @@ public: - bool is_pex_enabled() const; - void set_pex_enabled(bool enabled); - -+ bool is_meta_download() const; -+ - // Returns "" if the object is not valid. - const std::string& name() const; - -@@ -184,6 +186,7 @@ public: - CONNECTION_LEECH, - CONNECTION_SEED, - CONNECTION_INITIAL_SEED, -+ CONNECTION_METADATA, - } ConnectionType; - - ConnectionType connection_type() const; -diff --git a/src/torrent/object_stream.cc b/src/torrent/object_stream.cc -index 73c816b..9d9a962 100644 ---- a/src/torrent/object_stream.cc -+++ b/src/torrent/object_stream.cc -@@ -600,4 +600,11 @@ object_write_to_stream(void* data, object_buffer_t buffer) { - return buffer; - } - -+object_buffer_t -+object_write_to_size(void* data, object_buffer_t buffer) { -+ *reinterpret_cast<uint64_t*>(data) += std::distance(buffer.first, buffer.second); -+ -+ return buffer; -+} -+ - } -diff --git a/src/torrent/object_stream.h b/src/torrent/object_stream.h -index b399bf7..3de5d82 100644 ---- a/src/torrent/object_stream.h -+++ b/src/torrent/object_stream.h -@@ -91,6 +91,9 @@ object_buffer_t staticMap_write_bencode_c_wrap(object_write_t writeFunc, void* d - object_buffer_t object_write_to_buffer(void* data, object_buffer_t buffer) LIBTORRENT_EXPORT; - object_buffer_t object_write_to_sha1(void* data, object_buffer_t buffer) LIBTORRENT_EXPORT; - object_buffer_t object_write_to_stream(void* data, object_buffer_t buffer) LIBTORRENT_EXPORT; -+ -+// Measures bencode size, 'data' is uint64_t*. -+object_buffer_t object_write_to_size(void* data, object_buffer_t buffer) LIBTORRENT_EXPORT; - } - - #endif -diff --git a/src/torrent/torrent.cc b/src/torrent/torrent.cc -index e8ffbac..47027cc 100644 ---- a/src/torrent/torrent.cc -+++ b/src/torrent/torrent.cc -@@ -350,11 +350,22 @@ download_add(Object* object) { - - ctor.initialize(*object); - -- std::string infoHash = object_sha1(&object->get_key("info")); -+ std::string infoHash; -+ if (download->info()->is_meta_download()) -+ infoHash = object->get_key("info").get_key("pieces").as_string(); -+ else -+ infoHash = object_sha1(&object->get_key("info")); - - if (manager->download_manager()->find(infoHash) != manager->download_manager()->end()) - throw input_error("Info hash already used by another torrent."); - -+ if (!download->info()->is_meta_download()) { -+ char buffer[1024]; -+ uint64_t metadata_size = 0; -+ object_write_bencode_c(&object_write_to_size, &metadata_size, object_buffer_t(buffer, buffer + sizeof(buffer)), &object->get_key("info")); -+ download->main()->set_metadata_size(metadata_size); -+ } -+ - download->set_hash_queue(manager->hash_queue()); - download->initialize(infoHash, PEER_NAME + rak::generate_random<std::string>(20 - std::string(PEER_NAME).size())); - diff --git a/libtorrent-extended/object_sstr.patch b/libtorrent-extended/object_sstr.patch deleted file mode 100644 index f577080..0000000 --- a/libtorrent-extended/object_sstr.patch +++ /dev/null @@ -1,233 +0,0 @@ -diff --git a/src/dht/dht_node.cc b/src/dht/dht_node.cc -index 3574807..9d51a28 100644 ---- a/src/dht/dht_node.cc -+++ b/src/dht/dht_node.cc -@@ -59,8 +59,8 @@ DhtNode::DhtNode(const HashString& id, const rak::socket_address* sa) : - throw resource_error("Address not af_inet"); - } - --DhtNode::DhtNode(const std::string& id, const Object& cache) : -- HashString(*HashString::cast_from(id.c_str())), -+DhtNode::DhtNode(const SimpleString& id, const Object& cache) : -+ HashString(*HashString::cast_from(id)), - m_recentlyActive(false), - m_recentlyInactive(0), - m_bucket(NULL) { -diff --git a/src/dht/dht_node.h b/src/dht/dht_node.h -index 032c5cc..8234add 100644 ---- a/src/dht/dht_node.h -+++ b/src/dht/dht_node.h -@@ -57,7 +57,7 @@ public: - static const unsigned int max_failed_replies = 5; - - DhtNode(const HashString& id, const rak::socket_address* sa); -- DhtNode(const std::string& id, const Object& cache); -+ DhtNode(const SimpleString& id, const Object& cache); - - const HashString& id() const { return *this; } - const rak::socket_address* address() const { return &m_socketAddress; } -diff --git a/src/dht/dht_router.cc b/src/dht/dht_router.cc -index ff38b8c..e9abe5d 100644 ---- a/src/dht/dht_router.cc -+++ b/src/dht/dht_router.cc -@@ -337,7 +337,7 @@ DhtRouter::store_cache(Object* container) const { - Object& nodes = container->insert_key("nodes", Object::create_map()); - for (DhtNodeList::const_accessor itr = m_nodes.begin(); itr != m_nodes.end(); ++itr) { - if (!itr.node()->is_bad()) -- itr.node()->store_cache(&nodes.insert_key(itr.id().str(), Object::create_map())); -+ itr.node()->store_cache(&nodes.insert_key(itr.id().s_str(), Object::create_map())); - } - - // Insert contacts, if we have any. -diff --git a/src/torrent/object.cc b/src/torrent/object.cc -index 3a0bcae..b609f9c 100644 ---- a/src/torrent/object.cc -+++ b/src/torrent/object.cc -@@ -44,47 +44,59 @@ - - namespace torrent { - --Object& --Object::get_key(const std::string& k) { -- check_throw(TYPE_MAP); -- map_type::iterator itr = m_map->find(k); -+std::pair<Object::map_type::base_type::iterator, bool> -+Object::map_type::insert(const value_type& value) { -+ base_type::iterator itr = lower_bound(value.first); - -- if (itr == m_map->end()) -- throw bencode_error("Object operator [" + k + "] could not find element"); -+ if (itr != end() && !key_comp()(value.first, itr->first)) -+ return std::make_pair(itr, false); - -- return itr->second; --} -+ // Insert with an allocated copy of the key. -+ itr = base_type::insert(itr, value_type(value.first.copy(), value.second)); - -+ // This means the value was actually already present. -+ if (itr->second.get_string() != NULL) -+ throw internal_error("Object::map_type::insert failed to insert value."); - --const Object& --Object::get_key(const std::string& k) const { -- check_throw(TYPE_MAP); -- map_type::const_iterator itr = m_map->find(k); -+ // Make entry own the string and free it when erased. -+ itr->second.set_string(itr->first.c_str()); - -- if (itr == m_map->end()) -- throw bencode_error("Object operator [" + k + "] could not find element"); -+ return std::make_pair(itr, true); -+} - -- return itr->second; -+Object::map_type::base_type::iterator -+Object::map_type::insert(base_type::iterator itr, const value_type& value) { -+ SimpleString copy = value.first.copy(); -+ itr = base_type::insert(itr, value_type(copy, value.second)); -+ -+ // If the entry already owns its string, it wasn't really -+ // inserted and already existed, so discard the copy. -+ if (itr->second.get_string() != NULL) -+ delete [] copy.c_str(); -+ else -+ itr->second.set_string(itr->first.c_str()); -+ -+ return itr; - } - - Object& --Object::get_key(const char* k) { -+Object::get_key(const key_type& k) { - check_throw(TYPE_MAP); -- map_type::iterator itr = m_map->find(std::string(k)); -+ map_type::iterator itr = m_map->find(k); - - if (itr == m_map->end()) -- throw bencode_error("Object operator [" + std::string(k) + "] could not find element"); -+ throw bencode_error("Object operator [" + k.str() + "] could not find element"); - - return itr->second; - } - - const Object& --Object::get_key(const char* k) const { -+Object::get_key(const key_type& k) const { - check_throw(TYPE_MAP); -- map_type::iterator itr = m_map->find(std::string(k)); -+ map_type::iterator itr = m_map->find(k); - - if (itr == m_map->end()) -- throw bencode_error("Object operator [" + std::string(k) + "] could not find element"); -+ throw bencode_error("Object operator [" + k.str() + "] could not find element"); - - return itr->second; - } -@@ -143,7 +155,7 @@ Object::merge_copy(const Object& object, uint32_t maxDepth) { - while (srcItr != srcLast) { - destItr = std::find_if(destItr, dest.end(), rak::less_equal(srcItr->first, rak::mem_ref(&map_type::value_type::first))); - -- if (srcItr->first < destItr->first) -+ if (dest.key_comp()(srcItr->first, destItr->first)) - // destItr remains valid and pointing to the next possible - // position. - dest.insert(destItr, *srcItr); -diff --git a/src/torrent/object.h b/src/torrent/object.h -index 6cc4e4a..b7b4e8f 100644 ---- a/src/torrent/object.h -+++ b/src/torrent/object.h -@@ -46,18 +46,52 @@ - - namespace torrent { - --// TODO: Look into making a custom comp and allocator classes for the --// map_type which use a const char* for key_type. --// - // TODO: Use placement new/delete in order to avoid the extra level of - // indirection caused by the union. - - class LIBTORRENT_EXPORT Object { -+ template<typename T> -+ class string_wrapper : public T { -+ public: -+ string_wrapper() : T(), m_string(NULL) {} -+ string_wrapper(const T& value) : T(value), m_string(NULL) {} -+ string_wrapper(const string_wrapper& other) : T(other), m_string(NULL) {} -+ -+ ~string_wrapper() { delete [] m_string; m_string = NULL; } -+ -+ const char* get_string() const { return m_string; } -+ void set_string(const char* s) { m_string = s; } -+ -+ private: -+ string_wrapper& operator = (const string_wrapper& other); -+ -+ const char* m_string; -+ }; -+ - public: - typedef int64_t value_type; - typedef std::string string_type; - typedef std::list<Object> list_type; -- typedef std::map<std::string, Object> map_type; -+ class map_type : public std::map<SimpleString, string_wrapper<Object> > { -+ public: -+ typedef std::map<SimpleString, string_wrapper<Object> > base_type; -+ using base_type::value_type; -+ using base_type::key_type; -+ -+ map_type(const map_type& other) : base_type(other.key_comp()) { insert(other.begin(), other.end()); } -+ map_type() {} -+ -+ std::pair<base_type::iterator, bool> insert(const value_type& value); -+ base_type::iterator insert(base_type::iterator itr, const value_type& value); -+ -+ template<typename InputIterator> -+ void insert(InputIterator begin, InputIterator end); -+ -+ Object& operator[] (key_type key); -+ -+ private: -+ map_type& operator = (const map_type& other); -+ }; - typedef map_type::key_type key_type; - - typedef list_type::iterator list_iterator; -@@ -153,8 +187,6 @@ public: - - Object& get_key(const key_type& k); - const Object& get_key(const key_type& k) const; -- Object& get_key(const char* k); -- const Object& get_key(const char* k) const; - - template <typename T> value_type& get_key_value(const T& k) { return get_key(k).as_value(); } - template <typename T> const value_type& get_key_value(const T& k) const { return get_key(k).as_value(); } -@@ -213,6 +245,27 @@ public: - }; - }; - -+// We need to call our own insert function, so -+// we have to define this operator to use that. -+inline Object& -+Object::map_type::operator[] (key_type key) { -+ base_type::iterator itr = lower_bound(key); -+ -+ if (itr == end() || key_comp()(key, itr->first)) -+ itr = insert(itr, value_type(key, mapped_type())); -+ -+ return itr->second; -+} -+ -+template<typename InputIterator> -+inline void -+Object::map_type::insert(InputIterator itr, InputIterator itrEnd) { -+ while (itr != itrEnd) { -+ insert(end(), *itr); -+ ++itr; -+ } -+} -+ - inline - Object::Object(const Object& b) : m_flags(b.type()) { - switch (type()) { |