summaryrefslogtreecommitdiffstats
path: root/libtorrent-extended/dht_pex_static_map.patch
diff options
context:
space:
mode:
Diffstat (limited to 'libtorrent-extended/dht_pex_static_map.patch')
-rw-r--r--libtorrent-extended/dht_pex_static_map.patch2353
1 files changed, 0 insertions, 2353 deletions
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);