From 8dc1d552c0bab7b72371c3a1529e365410c7548c Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Mon, 17 May 2010 12:03:46 +0200 Subject: add rtorrent-extended Signed-off-by: Florian Pritz --- libtorrent-extended/dht_pex_static_map.patch | 2353 ++++++++++++++++++++++++++ 1 file changed, 2353 insertions(+) create mode 100644 libtorrent-extended/dht_pex_static_map.patch (limited to 'libtorrent-extended/dht_pex_static_map.patch') diff --git a/libtorrent-extended/dht_pex_static_map.patch b/libtorrent-extended/dht_pex_static_map.patch new file mode 100644 index 0000000..1e767e0 --- /dev/null +++ b/libtorrent-extended/dht_pex_static_map.patch @@ -0,0 +1,2353 @@ +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 +-#include ++#include + + #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(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(), 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(), 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 + + #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 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 PeerList; + + PeerList m_peers; + std::vector 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 { ++public: ++ typedef StaticMap 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 + #include + #include + #include +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(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 + + #include ++#include + + namespace torrent { + +@@ -49,7 +50,8 @@ class AddressList : public std::list { + 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 +-#include ++#include + + #include + +@@ -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 { ++public: ++ typedef StaticMap base_type; ++ typedef StaticMapKeys::mapping_type mapping_type; ++}; ++ ++class ExtPEXMessage : public StaticMap { ++public: ++ typedef StaticMap 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 + #include + #include ++#include + + 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 + #include + #include ++#include + + 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 + #include ++#include + #include + + #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 ++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 ++inline const char* ++staticMap_read_bencode(const char* buffer, const char* bufferEnd, StaticMap& 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 ++inline object_buffer_t ++staticMap_write_bencode_c(object_write_t writeFunc, void* data, object_buffer_t buffer, const StaticMap& 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 ++// ++// 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 ++#include ++#include ++#include ++ ++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(-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 ++// ++// 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 ++// ++// Skomakerveien 33 ++// 3185 Skoppum, NORWAY ++ ++#ifndef LIBTORRENT_STATIC_MAP_H ++#define LIBTORRENT_STATIC_MAP_H ++ ++#include ++#include ++#include ++#include ++ ++// 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 { ++public: ++ typedef std::vector 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 ++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); -- cgit v1.2.3-24-g4f1b