summaryrefslogtreecommitdiffstats
path: root/libtorrent-extended
diff options
context:
space:
mode:
Diffstat (limited to 'libtorrent-extended')
-rw-r--r--libtorrent-extended/PKGBUILD54
-rw-r--r--libtorrent-extended/bad_peer_handling.patch67
-rw-r--r--libtorrent-extended/dht_pex_static_map.patch2353
-rw-r--r--libtorrent-extended/ipv6.patch1235
-rw-r--r--libtorrent-extended/magnet_uri.patch1652
-rw-r--r--libtorrent-extended/object_sstr.patch233
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()) {