updated on Thu Jan 26 16:09:46 UTC 2012
[aur-mirror.git] / libtorrent-extended / ex_magnet_uri.patch
blob8b3a81152c1674095a1c3b09183099d61210fbea
1 diff --git a/src/dht/dht_bucket.cc b/src/dht/dht_bucket.cc
2 index 04aa475..819f4aa 100644
3 --- a/src/dht/dht_bucket.cc
4 +++ b/src/dht/dht_bucket.cc
5 @@ -52,6 +52,8 @@ DhtBucket::DhtBucket(const HashString& begin, const HashString& end) :
6 m_good(0),
7 m_bad(0),
9 + m_fullCacheLength(0),
11 m_begin(begin),
12 m_end(end) {
14 @@ -67,6 +69,8 @@ DhtBucket::add_node(DhtNode* n) {
15 m_good++;
16 else if (n->is_bad())
17 m_bad++;
19 + m_fullCacheLength = 0;
22 void
23 @@ -81,6 +85,8 @@ DhtBucket::remove_node(DhtNode* n) {
24 m_good--;
25 else if (n->is_bad())
26 m_bad--;
28 + m_fullCacheLength = 0;
31 void
32 @@ -92,9 +98,11 @@ DhtBucket::count() {
33 // Called every 15 minutes for housekeeping.
34 void
35 DhtBucket::update() {
36 - // For now we only update the counts after some nodes have become bad
37 - // due to prolonged inactivity.
38 count();
40 + // In case adjacent buckets whose nodes we borrowed have changed,
41 + // we force an update of the cache.
42 + m_fullCacheLength = 0;
45 DhtBucket::iterator
46 @@ -188,4 +196,23 @@ DhtBucket::split(const HashString& id) {
47 return other;
50 +void
51 +DhtBucket::build_full_cache() {
52 + DhtBucketChain chain(this);
54 + char* pos = m_fullCache;
55 + do {
56 + for (const_iterator itr = chain.bucket()->begin(); itr != chain.bucket()->end() && pos < m_fullCache + sizeof(m_fullCache); ++itr) {
57 + if (!(*itr)->is_bad()) {
58 + pos = (*itr)->store_compact(pos);
60 + if (pos > m_fullCache + sizeof(m_fullCache))
61 + throw internal_error("DhtRouter::store_closest_nodes wrote past buffer end.");
62 + }
63 + }
64 + } while (pos < m_fullCache + sizeof(m_fullCache) && chain.next() != NULL);
66 + m_fullCacheLength = pos - m_fullCache;
70 diff --git a/src/dht/dht_bucket.h b/src/dht/dht_bucket.h
71 index 97622a3..bcdfd67 100644
72 --- a/src/dht/dht_bucket.h
73 +++ b/src/dht/dht_bucket.h
74 @@ -111,6 +111,10 @@ public:
75 DhtBucket* parent() const { return m_parent; }
76 DhtBucket* child() const { return m_child; }
78 + // Return a full bucket's worth of compact node data. If this bucket is not
79 + // full, it uses nodes from the child/parent buckets until we have enough.
80 + SimpleString full_bucket();
82 // Called by the DhtNode on its bucket to update good/bad node counts.
83 void node_now_good(bool was_bad);
84 void node_now_bad(bool was_good);
85 @@ -118,6 +122,8 @@ public:
86 private:
87 void count();
89 + void build_full_cache();
91 DhtBucket* m_parent;
92 DhtBucket* m_child;
94 @@ -126,11 +132,15 @@ private:
95 unsigned int m_good;
96 unsigned int m_bad;
98 + size_t m_fullCacheLength;
100 // These are 40 bytes together, so might as well put them last.
101 // m_end is const because it is used as key for the DhtRouter routing table
102 // map, which would be inconsistent if m_end were changed carelessly.
103 HashString m_begin;
104 const HashString m_end;
106 + char m_fullCache[num_nodes * 26];
109 // Helper class to recursively follow a chain of buckets. It first recurses
110 @@ -160,6 +170,13 @@ DhtBucket::node_now_bad(bool was_good) {
111 m_bad++;
114 +inline SimpleString
115 +DhtBucket::full_bucket() {
116 + if (!m_fullCacheLength)
117 + build_full_cache();
118 + return SimpleString(m_fullCache, m_fullCacheLength);
121 inline const DhtBucket*
122 DhtBucketChain::next() {
123 // m_restart is clear when we're done recursing into the children and
124 diff --git a/src/dht/dht_node.cc b/src/dht/dht_node.cc
125 index 3574807..9d51a28 100644
126 --- a/src/dht/dht_node.cc
127 +++ b/src/dht/dht_node.cc
128 @@ -59,8 +59,8 @@ DhtNode::DhtNode(const HashString& id, const rak::socket_address* sa) :
129 throw resource_error("Address not af_inet");
132 -DhtNode::DhtNode(const std::string& id, const Object& cache) :
133 - HashString(*HashString::cast_from(id.c_str())),
134 +DhtNode::DhtNode(const SimpleString& id, const Object& cache) :
135 + HashString(*HashString::cast_from(id)),
136 m_recentlyActive(false),
137 m_recentlyInactive(0),
138 m_bucket(NULL) {
139 diff --git a/src/dht/dht_node.h b/src/dht/dht_node.h
140 index 032c5cc..8234add 100644
141 --- a/src/dht/dht_node.h
142 +++ b/src/dht/dht_node.h
143 @@ -57,7 +57,7 @@ public:
144 static const unsigned int max_failed_replies = 5;
146 DhtNode(const HashString& id, const rak::socket_address* sa);
147 - DhtNode(const std::string& id, const Object& cache);
148 + DhtNode(const SimpleString& id, const Object& cache);
150 const HashString& id() const { return *this; }
151 const rak::socket_address* address() const { return &m_socketAddress; }
152 diff --git a/src/dht/dht_router.cc b/src/dht/dht_router.cc
153 index b1c95c3..e9abe5d 100644
154 --- a/src/dht/dht_router.cc
155 +++ b/src/dht/dht_router.cc
156 @@ -329,24 +329,6 @@ DhtRouter::node_invalid(const HashString& id) {
157 delete_node(m_nodes.find(&node->id()));
160 -char*
161 -DhtRouter::store_closest_nodes(const HashString& id, char* buffer, char* bufferEnd) {
162 - DhtBucketChain chain(find_bucket(id)->second);
164 - do {
165 - for (DhtBucket::const_iterator itr = chain.bucket()->begin(); itr != chain.bucket()->end() && buffer != bufferEnd; ++itr) {
166 - if (!(*itr)->is_bad()) {
167 - buffer = (*itr)->store_compact(buffer);
169 - if (buffer > bufferEnd)
170 - throw internal_error("DhtRouter::store_closest_nodes wrote past buffer end.");
173 - } while (buffer != bufferEnd && chain.next() != NULL);
175 - return buffer;
178 Object*
179 DhtRouter::store_cache(Object* container) const {
180 container->insert_key("self_id", str());
181 @@ -355,7 +337,7 @@ DhtRouter::store_cache(Object* container) const {
182 Object& nodes = container->insert_key("nodes", Object::create_map());
183 for (DhtNodeList::const_accessor itr = m_nodes.begin(); itr != m_nodes.end(); ++itr) {
184 if (!itr.node()->is_bad())
185 - itr.node()->store_cache(&nodes.insert_key(itr.id().str(), Object::create_map()));
186 + itr.node()->store_cache(&nodes.insert_key(itr.id().s_str(), Object::create_map()));
189 // Insert contacts, if we have any.
190 @@ -470,7 +452,7 @@ DhtRouter::receive_timeout() {
191 for (DhtBucketList::const_iterator itr = m_routingTable.begin(); itr != m_routingTable.end(); ++itr) {
192 itr->second->update();
194 - if (!itr->second->is_full() || itr->second->age() > timeout_bucket_bootstrap)
195 + if (!itr->second->is_full() || itr->second == bucket() || itr->second->age() > timeout_bucket_bootstrap)
196 bootstrap_bucket(itr->second);
199 @@ -505,15 +487,13 @@ DhtRouter::generate_token(const rak::socket_address* sa, int token, char buffer[
200 return buffer;
203 -std::string
204 -DhtRouter::make_token(const rak::socket_address* sa) {
205 - char token[20];
207 - return std::string(generate_token(sa, m_curToken, token), size_token);
208 +SimpleString
209 +DhtRouter::make_token(const rak::socket_address* sa, char* buffer) {
210 + return SimpleString(generate_token(sa, m_curToken, buffer), size_token);
213 bool
214 -DhtRouter::token_valid(const std::string& token, const rak::socket_address* sa) {
215 +DhtRouter::token_valid(SimpleString token, const rak::socket_address* sa) {
216 if (token.length() != size_token)
217 return false;
219 @@ -521,12 +501,12 @@ DhtRouter::token_valid(const std::string& token, const rak::socket_address* sa)
220 char reference[20];
222 // First try current token.
223 - if (std::memcmp(generate_token(sa, m_curToken, reference), token.c_str(), size_token) == 0)
224 + if (token == SimpleString(generate_token(sa, m_curToken, reference), size_token))
225 return true;
227 // If token recently changed, some clients may be using the older one.
228 // That way a token is valid for 15-30 minutes, instead of 0-15.
229 - return std::memcmp(generate_token(sa, m_prevToken, reference), token.c_str(), size_token) == 0;
230 + return token == SimpleString(generate_token(sa, m_prevToken, reference), size_token);
233 DhtNode*
234 diff --git a/src/dht/dht_router.h b/src/dht/dht_router.h
235 index f2b673f..816747f 100644
236 --- a/src/dht/dht_router.h
237 +++ b/src/dht/dht_router.h
238 @@ -115,14 +115,14 @@ public:
240 // Store compact node information (26 bytes) for nodes closest to the
241 // given ID in the given buffer, return new buffer end.
242 - char* store_closest_nodes(const HashString& id, char* buffer, char* bufferEnd);
243 + SimpleString get_closest_nodes(const HashString& id) { return find_bucket(id)->second->full_bucket(); }
245 // Store DHT cache in the given container.
246 Object* store_cache(Object* container) const;
248 // Create and verify a token. Tokens are valid between 15-30 minutes from creation.
249 - std::string make_token(const rak::socket_address* sa);
250 - bool token_valid(const std::string& token, const rak::socket_address* sa);
251 + SimpleString make_token(const rak::socket_address* sa, char* buffer);
252 + bool token_valid(SimpleString token, const rak::socket_address* sa);
254 DhtManager::statistics_type get_statistics() const;
255 void reset_statistics() { m_server.reset_statistics(); }
256 @@ -147,6 +147,8 @@ private:
257 bool add_node_to_bucket(DhtNode* node);
258 void delete_node(const DhtNodeList::accessor& itr);
260 + void store_closest_nodes(const HashString& id, DhtBucket* bucket);
262 DhtBucketList::iterator split_bucket(const DhtBucketList::iterator& itr, DhtNode* node);
264 void bootstrap();
265 diff --git a/src/dht/dht_server.cc b/src/dht/dht_server.cc
266 index 1f2234b..256b92b 100644
267 --- a/src/dht/dht_server.cc
268 +++ b/src/dht/dht_server.cc
269 @@ -38,13 +38,14 @@
270 #include "globals.h"
272 #include <algorithm>
273 -#include <sstream>
274 +#include <cstdio>
276 #include "torrent/exceptions.h"
277 #include "torrent/connection_manager.h"
278 #include "torrent/object.h"
279 #include "torrent/object_stream.h"
280 #include "torrent/poll.h"
281 +#include "torrent/static_map.h"
282 #include "torrent/throttle.h"
283 #include "tracker/tracker_dht.h"
285 @@ -63,6 +64,34 @@ const char* DhtServer::queries[] = {
286 "announce_peer",
289 +// List of all possible keys we need/support in a DHT message.
290 +// Unsupported keys we receive are dropped (ignored) while decoding.
291 +// See torrent/static_map.h for how this works.
292 +DhtMessage::mapping_type dht_key_names[DhtMessage::length] = {
293 + { key_a_id, "a::id" },
294 + { key_a_infoHash, "a::info_hash" },
295 + { key_a_port, "a::port", },
296 + { key_a_target, "a::target" },
297 + { key_a_token, "a::token" },
299 + { key_e_0, "e[0]" },
300 + { key_e_1, "e[1]" },
302 + { key_q, "q" },
304 + { key_r_id, "r::id" },
305 + { key_r_nodes, "r::nodes" },
306 + { key_r_token, "r::token" },
307 + { key_r_values, "r::values[]" },
309 + { key_t, "t::" },
310 + { key_v, "v" },
311 + { key_y, "y" },
314 +template<>
315 +const DhtMessage::key_map_init DhtMessage::base_type::keyMap(dht_key_names);
317 // Error in DHT protocol, avoids std::string ctor from communication_error
318 class dht_error : public network_error {
319 public:
320 @@ -238,54 +267,51 @@ DhtServer::update() {
323 void
324 -DhtServer::process_query(const Object& transactionId, const HashString& id, const rak::socket_address* sa, Object& request) {
325 +DhtServer::process_query(const HashString& id, const rak::socket_address* sa, const DhtMessage& msg) {
326 m_queriesReceived++;
327 m_networkUp = true;
329 - std::string& query = request.get_key_string("q");
331 - Object& arg = request.get_key("a");
332 + SimpleString query = msg[key_q].as_sstring();
334 // Construct reply.
335 - Object reply = Object::create_map();
336 + DhtMessage reply;
338 if (query == "find_node")
339 - create_find_node_response(arg, reply);
340 + create_find_node_response(msg, reply);
342 else if (query == "get_peers")
343 - create_get_peers_response(arg, sa, reply);
344 + create_get_peers_response(msg, sa, reply);
346 else if (query == "announce_peer")
347 - create_announce_peer_response(arg, sa, reply);
348 + create_announce_peer_response(msg, sa, reply);
350 else if (query != "ping")
351 throw dht_error(dht_error_bad_method, "Unknown query type.");
353 m_router->node_queried(id, sa);
354 - create_response(transactionId, sa, reply);
355 + create_response(msg, sa, reply);
358 void
359 -DhtServer::create_find_node_response(const Object& arg, Object& reply) {
360 - const std::string& target = arg.get_key_string("target");
361 +DhtServer::create_find_node_response(const DhtMessage& req, DhtMessage& reply) {
362 + SimpleString target = req[key_a_target].as_sstring();
364 if (target.length() < HashString::size_data)
365 throw dht_error(dht_error_protocol, "target string too short");
367 - char compact[sizeof(compact_node_info) * DhtBucket::num_nodes];
368 - char* end = m_router->store_closest_nodes(*HashString::cast_from(target), compact, compact + sizeof(compact));
370 - if (end == compact)
371 + SimpleString nodes = m_router->get_closest_nodes(*HashString::cast_from(target));
372 + if (nodes.empty())
373 throw dht_error(dht_error_generic, "No nodes");
375 - reply.insert_key("nodes", std::string(compact, end));
376 + reply[key_r_nodes] = nodes;
379 void
380 -DhtServer::create_get_peers_response(const Object& arg, const rak::socket_address* sa, Object& reply) {
381 - reply.insert_key("token", m_router->make_token(sa));
382 +DhtServer::create_get_peers_response(const DhtMessage& req, const rak::socket_address* sa, DhtMessage& reply) {
383 + reply[key_r_token] = m_router->make_token(sa, reply.data_end);
384 + reply.data_end += reply[key_r_token].as_sstring().length();
386 - const std::string& info_hash_str = arg.get_key_string("info_hash");
387 + SimpleString info_hash_str = req[key_a_infoHash].as_sstring();
389 if (info_hash_str.length() < HashString::size_data)
390 throw dht_error(dht_error_protocol, "info hash too short");
391 @@ -296,35 +322,34 @@ DhtServer::create_get_peers_response(const Object& arg, const rak::socket_addres
393 // If we're not tracking or have no peers, send closest nodes.
394 if (!tracker || tracker->empty()) {
395 - char compact[sizeof(compact_node_info) * DhtBucket::num_nodes];
396 - char* end = m_router->store_closest_nodes(*info_hash, compact, compact + sizeof(compact));
398 - if (end == compact)
399 + SimpleString nodes = m_router->get_closest_nodes(*info_hash);
400 + if (nodes.empty())
401 throw dht_error(dht_error_generic, "No peers nor nodes");
403 - reply.insert_key("nodes", std::string(compact, end));
404 + reply[key_r_nodes] = nodes;
406 } else {
407 - reply.insert_key("values", Object::create_list()).as_list().swap(tracker->get_peers().as_list());
408 + reply[key_r_values] = tracker->get_peers();
412 void
413 -DhtServer::create_announce_peer_response(const Object& arg, const rak::socket_address* sa, Object& reply) {
414 - const std::string& info_hash = arg.get_key_string("info_hash");
415 +DhtServer::create_announce_peer_response(const DhtMessage& req, const rak::socket_address* sa, DhtMessage& reply) {
416 + SimpleString info_hash = req[key_a_infoHash].as_sstring();
418 if (info_hash.length() < HashString::size_data)
419 throw dht_error(dht_error_protocol, "info hash too short");
421 - if (!m_router->token_valid(arg.get_key_string("token"), sa))
422 + if (!m_router->token_valid(req[key_a_token].as_sstring(), sa))
423 throw dht_error(dht_error_protocol, "Token invalid.");
425 DhtTracker* tracker = m_router->get_tracker(*HashString::cast_from(info_hash), true);
426 - tracker->add_peer(sa->sa_inet()->address_n(), arg.get_key_value("port"));
427 + tracker->add_peer(sa->sa_inet()->address_n(), req[key_a_port].as_value());
430 void
431 -DhtServer::process_response(int transactionId, const HashString& id, const rak::socket_address* sa, Object& request) {
432 +DhtServer::process_response(const HashString& id, const rak::socket_address* sa, const DhtMessage& response) {
433 + int transactionId = (unsigned char)response[key_t].as_sstring()[2];
434 transaction_itr itr = m_transactions.find(DhtTransaction::key(sa, transactionId));
436 // Response to a transaction we don't have in our table. At this point it's
437 @@ -351,11 +376,9 @@ DhtServer::process_response(int transactionId, const HashString& id, const rak::
438 if ((id != transaction->id() && transaction->id() != m_router->zero_id))
439 return;
441 - const Object& response = request.get_key("r");
443 switch (transaction->type()) {
444 case DhtTransaction::DHT_FIND_NODE:
445 - parse_find_node_reply(transaction->as_find_node(), response.get_key_string("nodes"));
446 + parse_find_node_reply(transaction->as_find_node(), response[key_r_nodes].as_sstring());
447 break;
449 case DhtTransaction::DHT_GET_PEERS:
450 @@ -381,7 +404,8 @@ DhtServer::process_response(int transactionId, const HashString& id, const rak::
453 void
454 -DhtServer::process_error(int transactionId, const rak::socket_address* sa, Object& request) {
455 +DhtServer::process_error(const rak::socket_address* sa, const DhtMessage& error) {
456 + int transactionId = (unsigned char)error[key_t].as_sstring()[2];
457 transaction_itr itr = m_transactions.find(DhtTransaction::key(sa, transactionId));
459 if (itr == m_transactions.end())
460 @@ -399,7 +423,7 @@ DhtServer::process_error(int transactionId, const rak::socket_address* sa, Objec
463 void
464 -DhtServer::parse_find_node_reply(DhtTransactionSearch* transaction, const std::string& nodes) {
465 +DhtServer::parse_find_node_reply(DhtTransactionSearch* transaction, SimpleString nodes) {
466 transaction->complete(true);
468 if (sizeof(const compact_node_info) != 26)
469 @@ -421,16 +445,16 @@ DhtServer::parse_find_node_reply(DhtTransactionSearch* transaction, const std::s
472 void
473 -DhtServer::parse_get_peers_reply(DhtTransactionGetPeers* transaction, const Object& response) {
474 +DhtServer::parse_get_peers_reply(DhtTransactionGetPeers* transaction, const DhtMessage& response) {
475 DhtAnnounce* announce = static_cast<DhtAnnounce*>(transaction->as_search()->search());
477 transaction->complete(true);
479 - if (response.has_key_list("values"))
480 - announce->receive_peers(response.get_key("values"));
481 + if (response[key_r_values].is_sstring())
482 + announce->receive_peers(response[key_r_values].as_sstring());
484 - if (response.has_key_string("token"))
485 - add_transaction(new DhtTransactionAnnouncePeer(transaction->id(), transaction->address(), announce->target(), response.get_key_string("token")), packet_prio_low);
486 + if (response[key_r_token].is_sstring())
487 + add_transaction(new DhtTransactionAnnouncePeer(transaction->id(), transaction->address(), announce->target(), response[key_r_token].as_sstring()), packet_prio_low);
489 announce->update_status();
491 @@ -490,17 +514,19 @@ DhtServer::create_query(transaction_itr itr, int tID, const rak::socket_address*
492 if (itr->second->id() == m_router->id())
493 throw internal_error("DhtServer::create_query trying to send to itself.");
495 - Object query = Object::create_map();
496 + DhtMessage query;
498 - DhtTransaction* transaction = itr->second;
499 - char trans_id = tID;
500 - query.insert_key("t", std::string(&trans_id, 1));
501 - query.insert_key("y", "q");
502 - query.insert_key("q", queries[transaction->type()]);
503 - query.insert_key("v", PEER_VERSION);
504 + // Transaction ID is a bencode string.
505 + query[key_t] = SimpleString(query.data_end, 3);
506 + *query.data_end++ = '1';
507 + *query.data_end++ = ':';
508 + *query.data_end++ = tID;
510 - Object& q = query.insert_key("a", Object::create_map());
511 - q.insert_key("id", m_router->str());
512 + DhtTransaction* transaction = itr->second;
513 + query[key_y] = SimpleString("q", 1);
514 + query[key_q] = SimpleString(queries[transaction->type()]);
515 + query[key_v] = SimpleString(PEER_VERSION, 4);
516 + query[key_a_id] = m_router->s_str();
518 switch (transaction->type()) {
519 case DhtTransaction::DHT_PING:
520 @@ -508,17 +534,17 @@ DhtServer::create_query(transaction_itr itr, int tID, const rak::socket_address*
521 break;
523 case DhtTransaction::DHT_FIND_NODE:
524 - q.insert_key("target", transaction->as_find_node()->search()->target().str());
525 + query[key_a_target] = transaction->as_find_node()->search()->target().s_str();
526 break;
528 case DhtTransaction::DHT_GET_PEERS:
529 - q.insert_key("info_hash", transaction->as_get_peers()->search()->target().str());
530 + query[key_a_infoHash] = transaction->as_get_peers()->search()->target().s_str();
531 break;
533 case DhtTransaction::DHT_ANNOUNCE_PEER:
534 - q.insert_key("info_hash", transaction->as_announce_peer()->info_hash().str());
535 - q.insert_key("token", transaction->as_announce_peer()->token());
536 - q.insert_key("port", manager->connection_manager()->listen_port());
537 + query[key_a_infoHash] = transaction->as_announce_peer()->info_hash().s_str();
538 + query[key_a_token] = transaction->as_announce_peer()->token();
539 + query[key_a_port] = manager->connection_manager()->listen_port();
540 break;
543 @@ -530,31 +556,26 @@ DhtServer::create_query(transaction_itr itr, int tID, const rak::socket_address*
546 void
547 -DhtServer::create_response(const Object& transactionId, const rak::socket_address* sa, Object& r) {
548 - Object reply = Object::create_map();
549 - r.insert_key("id", m_router->str());
551 - reply.insert_key("t", transactionId);
552 - reply.insert_key("y", "r");
553 - reply.insert_key("r", r);
554 - reply.insert_key("v", PEER_VERSION);
555 +DhtServer::create_response(const DhtMessage& req, const rak::socket_address* sa, DhtMessage& reply) {
556 + reply[key_r_id] = m_router->s_str();
557 + reply[key_t] = req[key_t];
558 + reply[key_y] = SimpleString("r", 1);
559 + reply[key_v] = SimpleString(PEER_VERSION, 4);
561 add_packet(new DhtTransactionPacket(sa, reply), packet_prio_reply);
564 void
565 -DhtServer::create_error(const Object* transactionId, const rak::socket_address* sa, int num, const std::string& msg) {
566 - Object error = Object::create_map();
567 +DhtServer::create_error(const DhtMessage& req, const rak::socket_address* sa, int num, const char* msg) {
568 + DhtMessage error;
570 - if (transactionId != NULL)
571 - error.insert_key("t", *transactionId);
572 + if (req[key_t].is_sstring())
573 + error[key_t] = req[key_t];
575 - error.insert_key("y", "e");
576 - error.insert_key("v", PEER_VERSION);
578 - Object& e = error.insert_key("e", Object::create_list());
579 - e.insert_back(num);
580 - e.insert_back(msg);
581 + error[key_y] = SimpleString("e", 1);
582 + error[key_v] = SimpleString(PEER_VERSION, 4);
583 + error[key_e_0] = num;
584 + error[key_e_1] = SimpleString(msg);
586 add_packet(new DhtTransactionPacket(sa, error), packet_prio_reply);
588 @@ -656,15 +677,12 @@ DhtServer::clear_transactions() {
589 void
590 DhtServer::event_read() {
591 uint32_t total = 0;
592 - std::istringstream sstream;
594 - sstream.imbue(std::locale::classic());
596 while (true) {
597 Object request;
598 rak::socket_address sa;
599 int type = '?';
600 - const Object* transactionId = NULL;
601 + DhtMessage message;
602 const HashString* nodeId = NULL;
604 try {
605 @@ -675,31 +693,32 @@ DhtServer::event_read() {
606 break;
608 total += read;
609 - sstream.str(std::string(buffer, read));
611 - sstream >> request;
613 // If it's not a valid bencode dictionary at all, it's probably not a DHT
614 // packet at all, so we don't throw an error to prevent bounce loops.
615 - if (sstream.fail() || !request.is_map())
616 + try {
617 + staticMap_read_bencode(buffer, buffer + read, message);
618 + } catch (bencode_error& e) {
619 continue;
622 - if (!request.has_key("t"))
623 + if (!message[key_t].is_sstring())
624 throw dht_error(dht_error_protocol, "No transaction ID");
626 - transactionId = &request.get_key("t");
628 - if (!request.has_key_string("y"))
629 + if (!message[key_y].is_sstring())
630 throw dht_error(dht_error_protocol, "No message type");
632 - if (request.get_key_string("y").length() != 1)
633 + if (message[key_y].as_sstring().length() != 1)
634 throw dht_error(dht_error_bad_method, "Unsupported message type");
636 - type = request.get_key_string("y")[0];
637 + type = message[key_y].as_sstring()[0];
639 // Queries and replies have node ID in different dictionaries.
640 if (type == 'r' || type == 'q') {
641 - const std::string& nodeIdStr = request.get_key(type == 'q' ? "a" : "r").get_key_string("id");
642 + if (!message[type == 'q' ? key_a_id : key_r_id].is_sstring())
643 + throw dht_error(dht_error_protocol, "Invalid `id' value");
645 + SimpleString nodeIdStr = message[type == 'q' ? key_a_id : key_r_id].as_sstring();
647 if (nodeIdStr.length() < HashString::size_data)
648 throw dht_error(dht_error_protocol, "`id' value too short");
649 @@ -709,7 +728,8 @@ DhtServer::event_read() {
651 // Sanity check the returned transaction ID.
652 if ((type == 'r' || type == 'e') &&
653 - (!transactionId->is_string() || transactionId->as_string().length() != 1))
654 + (!message[key_t].is_sstring() || message[key_t].as_sstring().length() != 3
655 + || message[key_t].as_sstring()[0] != '1' || message[key_t].as_sstring()[1] != ':'))
656 throw dht_error(dht_error_protocol, "Invalid transaction ID type/length.");
658 // Stupid broken implementations.
659 @@ -718,15 +738,15 @@ DhtServer::event_read() {
661 switch (type) {
662 case 'q':
663 - process_query(*transactionId, *nodeId, &sa, request);
664 + process_query(*nodeId, &sa, message);
665 break;
667 case 'r':
668 - process_response(((unsigned char*)transactionId->as_string().c_str())[0], *nodeId, &sa, request);
669 + process_response(*nodeId, &sa, message);
670 break;
672 case 'e':
673 - process_error(((unsigned char*)transactionId->as_string().c_str())[0], &sa, request);
674 + process_error(&sa, message);
675 break;
677 default:
678 @@ -737,16 +757,19 @@ DhtServer::event_read() {
679 // so that if it repeatedly sends malformed replies we will drop it instead of propagating it
680 // to other nodes.
681 } catch (bencode_error& e) {
682 - if ((type == 'r' || type == 'e') && nodeId != NULL)
683 + if ((type == 'r' || type == 'e') && nodeId != NULL) {
684 m_router->node_inactive(*nodeId, &sa);
685 - else
686 - create_error(transactionId, &sa, dht_error_protocol, std::string("Malformed packet: ") + e.what());
687 + } else {
688 + snprintf(message.data_end, message.data + message.data_size - message.data_end - 1, "Malformed packet: %s", e.what());
689 + message.data[message.data_size - 1] = 0;
690 + create_error(message, &sa, dht_error_protocol, message.data_end);
693 } catch (dht_error& e) {
694 if ((type == 'r' || type == 'e') && nodeId != NULL)
695 m_router->node_inactive(*nodeId, &sa);
696 else
697 - create_error(transactionId, &sa, e.code(), e.what());
698 + create_error(message, &sa, e.code(), e.what());
700 } catch (network_error& e) {
702 diff --git a/src/dht/dht_server.h b/src/dht/dht_server.h
703 index 1855b73..1f55f15 100644
704 --- a/src/dht/dht_server.h
705 +++ b/src/dht/dht_server.h
706 @@ -46,6 +46,7 @@
707 #include "net/throttle_node.h"
708 #include "download/download_info.h" // for SocketAddressCompact
709 #include "torrent/hash_string.h"
710 +#include "torrent/simple_string.h"
712 #include "dht_transaction.h"
714 @@ -56,6 +57,7 @@ class DhtNode;
715 class DhtRouter;
717 class DownloadInfo;
718 +class DhtMessage;
719 class TrackerDht;
721 // UDP server that handles the DHT node communications.
722 @@ -134,23 +136,23 @@ private:
724 void start_write();
726 - void process_query(const Object& transaction, const HashString& id, const rak::socket_address* sa, Object& req);
727 - void process_response(int transaction, const HashString& id, const rak::socket_address* sa, Object& req);
728 - void process_error(int transaction, const rak::socket_address* sa, Object& req);
729 + void process_query(const HashString& id, const rak::socket_address* sa, const DhtMessage& req);
730 + void process_response(const HashString& id, const rak::socket_address* sa, const DhtMessage& req);
731 + void process_error(const rak::socket_address* sa, const DhtMessage& error);
733 - void parse_find_node_reply(DhtTransactionSearch* t, const std::string& nodes);
734 - void parse_get_peers_reply(DhtTransactionGetPeers* t, const Object& res);
735 + void parse_find_node_reply(DhtTransactionSearch* t, SimpleString res);
736 + void parse_get_peers_reply(DhtTransactionGetPeers* t, const DhtMessage& res);
738 void find_node_next(DhtTransactionSearch* t);
740 void add_packet(DhtTransactionPacket* packet, int priority);
741 void create_query(transaction_itr itr, int tID, const rak::socket_address* sa, int priority);
742 - void create_response(const Object& transactionID, const rak::socket_address* sa, Object& r);
743 - void create_error(const Object* transactionID, const rak::socket_address* sa, int num, const std::string& msg);
744 + void create_response(const DhtMessage& req, const rak::socket_address* sa, DhtMessage& reply);
745 + void create_error(const DhtMessage& req, const rak::socket_address* sa, int num, const char* msg);
747 - void create_find_node_response(const Object& arg, Object& reply);
748 - void create_get_peers_response(const Object& arg, const rak::socket_address* sa, Object& reply);
749 - void create_announce_peer_response(const Object& arg, const rak::socket_address* sa, Object& reply);
750 + void create_find_node_response(const DhtMessage& arg, DhtMessage& reply);
751 + void create_get_peers_response(const DhtMessage& arg, const rak::socket_address* sa, DhtMessage& reply);
752 + void create_announce_peer_response(const DhtMessage& arg, const rak::socket_address* sa, DhtMessage& reply);
754 int add_transaction(DhtTransaction* t, int priority);
756 diff --git a/src/dht/dht_tracker.cc b/src/dht/dht_tracker.cc
757 index 416dbf3..6e1afe9 100644
758 --- a/src/dht/dht_tracker.cc
759 +++ b/src/dht/dht_tracker.cc
760 @@ -54,8 +54,8 @@ DhtTracker::add_peer(uint32_t addr, uint16_t port) {
762 // Check if peer exists. If not, find oldest peer.
763 for (unsigned int i = 0; i < size(); i++) {
764 - if (m_peers[i].addr == compact.addr) {
765 - m_peers[i].port = compact.port;
766 + if (m_peers[i].peer.addr == compact.addr) {
767 + m_peers[i].peer.port = compact.port;
768 m_lastSeen[i] = cachedTime.seconds();
769 return;
771 @@ -77,10 +77,13 @@ DhtTracker::add_peer(uint32_t addr, uint16_t port) {
775 -// Return compact info (6 bytes) for up to 30 peers, returning different
776 -// peers for each call if there are more.
777 -Object
778 +// Return compact info as bencoded string (8 bytes per peer) for up to 30 peers,
779 +// returning different peers for each call if there are more.
780 +SimpleString
781 DhtTracker::get_peers(unsigned int maxPeers) {
782 + if (sizeof(BencodeAddress) != 8)
783 + throw internal_error("DhtTracker::BencodeAddress is packed incorrectly.");
785 PeerList::iterator first = m_peers.begin();
786 PeerList::iterator last = m_peers.end();
788 @@ -94,11 +97,7 @@ DhtTracker::get_peers(unsigned int maxPeers) {
789 last = first + maxPeers;
792 - Object peers = Object::create_list();
793 - for (; first != last; ++first)
794 - peers.insert_back(std::string(first->c_str(), sizeof(*first)));
796 - return peers;
797 + return SimpleString(first->bencode(), last->bencode() - first->bencode());
800 // Remove old announces.
801 @@ -107,9 +106,9 @@ DhtTracker::prune(uint32_t maxAge) {
802 uint32_t minSeen = cachedTime.seconds() - maxAge;
804 for (unsigned int i = 0; i < m_lastSeen.size(); i++)
805 - if (m_lastSeen[i] < minSeen) m_peers[i].port = 0;
806 + if (m_lastSeen[i] < minSeen) m_peers[i].peer.port = 0;
808 - 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());
809 + m_peers.erase(std::remove_if(m_peers.begin(), m_peers.end(), std::mem_fun_ref(&BencodeAddress::empty)), m_peers.end());
810 m_lastSeen.erase(std::remove_if(m_lastSeen.begin(), m_lastSeen.end(), std::bind2nd(std::less<uint32_t>(), minSeen)), m_lastSeen.end());
812 if (m_peers.size() != m_lastSeen.size())
813 diff --git a/src/dht/dht_tracker.h b/src/dht/dht_tracker.h
814 index 8515dd0..53fd1e3 100644
815 --- a/src/dht/dht_tracker.h
816 +++ b/src/dht/dht_tracker.h
817 @@ -43,6 +43,7 @@
818 #include <rak/socket_address.h>
820 #include "download/download_info.h" // for SocketAddressCompact
821 +#include "torrent/simple_string.h"
823 namespace torrent {
825 @@ -65,14 +66,26 @@ public:
826 size_t size() const { return m_peers.size(); }
828 void add_peer(uint32_t addr, uint16_t port);
829 - Object get_peers(unsigned int maxPeers = max_peers);
830 + SimpleString get_peers(unsigned int maxPeers = max_peers);
832 // Remove old announces from the tracker that have not reannounced for
833 // more than the given number of seconds.
834 void prune(uint32_t maxAge);
836 private:
837 - typedef std::vector<SocketAddressCompact> PeerList;
838 + // We need to store the address as a bencoded string.
839 + struct BencodeAddress {
840 + char header[2];
841 + SocketAddressCompact peer;
843 + BencodeAddress(const SocketAddressCompact& p) : peer(p) { header[0] = '6'; header[1] = ':'; }
845 + const char* bencode() const { return header; }
847 + bool empty() const { return !peer.port; }
848 + } __attribute__ ((packed));
850 + typedef std::vector<BencodeAddress> PeerList;
852 PeerList m_peers;
853 std::vector<uint32_t> m_lastSeen;
854 diff --git a/src/dht/dht_transaction.cc b/src/dht/dht_transaction.cc
855 index 2a6a8a6..0b3cfd0 100644
856 --- a/src/dht/dht_transaction.cc
857 +++ b/src/dht/dht_transaction.cc
858 @@ -123,7 +123,7 @@ DhtSearch::trim(bool final) {
859 // We keep:
860 // - the max_contacts=18 closest good or unknown nodes and all nodes closer
861 // than them (to see if further searches find closer ones)
862 - // - for announces, also the 8 closest good nodes (i.e. nodes that have
863 + // - for announces, also the 3 closest good nodes (i.e. nodes that have
864 // replied) to have at least that many for the actual announce
865 // - any node that currently has transactions pending
867 @@ -136,7 +136,7 @@ DhtSearch::trim(bool final) {
868 // node is new and unknown otherwise
870 int needClosest = final ? 0 : max_contacts;
871 - int needGood = is_announce() ? DhtBucket::num_nodes : 0;
872 + int needGood = is_announce() ? max_announce : 0;
874 // We're done if we can't find any more nodes to contact.
875 m_next = end();
876 @@ -252,7 +252,7 @@ DhtAnnounce::start_announce() {
879 void
880 -DhtAnnounce::receive_peers(const Object& peers) {
881 +DhtAnnounce::receive_peers(SimpleString peers) {
882 m_tracker->receive_peers(peers);
885 @@ -262,9 +262,9 @@ DhtAnnounce::update_status() {
888 void
889 -DhtTransactionPacket::build_buffer(const Object& data) {
890 +DhtTransactionPacket::build_buffer(const DhtMessage& msg) {
891 char buffer[1500]; // If the message would exceed an Ethernet frame, something went very wrong.
892 - object_buffer_t result = object_write_bencode_c(object_write_to_buffer, NULL, std::make_pair(buffer, buffer + sizeof(buffer)), &data);
893 + object_buffer_t result = staticMap_write_bencode_c(object_write_to_buffer, NULL, std::make_pair(buffer, buffer + sizeof(buffer)), msg);
895 m_length = result.second - buffer;
896 m_data = new char[m_length];
897 @@ -277,7 +277,6 @@ DhtTransaction::DhtTransaction(int quick_timeout, int timeout, const HashString&
898 m_sa(*sa),
899 m_timeout(cachedTime.seconds() + timeout),
900 m_quickTimeout(cachedTime.seconds() + quick_timeout),
901 - m_retry(3),
902 m_packet(NULL) {
905 diff --git a/src/dht/dht_transaction.h b/src/dht/dht_transaction.h
906 index 194316d..43b42ab 100644
907 --- a/src/dht/dht_transaction.h
908 +++ b/src/dht/dht_transaction.h
909 @@ -43,6 +43,7 @@
911 #include "dht/dht_node.h"
912 #include "torrent/hash_string.h"
913 +#include "torrent/static_map.h"
915 namespace torrent {
917 @@ -93,6 +94,9 @@ public:
918 // Number of closest potential contact nodes to keep.
919 static const unsigned int max_contacts = 18;
921 + // Number of closest nodes we actually announce to.
922 + static const unsigned int max_announce = 3;
924 DhtSearch(const HashString& target, const DhtBucket& contacts);
925 virtual ~DhtSearch();
927 @@ -178,22 +182,66 @@ public:
928 // counts announces instead.
929 const_accessor start_announce();
931 - void receive_peers(const Object& peer_list);
932 + void receive_peers(SimpleString peers);
933 void update_status();
935 private:
936 TrackerDht* m_tracker;
939 +// Possible bencode keys in a DHT message.
940 +enum dht_keys {
941 + key_a_id,
942 + key_a_infoHash,
943 + key_a_port,
944 + key_a_target,
945 + key_a_token,
947 + key_e_0,
948 + key_e_1,
950 + key_q,
952 + key_r_id,
953 + key_r_nodes,
954 + key_r_token,
955 + key_r_values,
957 + key_t,
958 + key_v,
959 + key_y,
961 + key_LAST,
964 +class DhtMessage : public StaticMap<dht_keys, key_LAST> {
965 +public:
966 + typedef StaticMap<dht_keys, key_LAST> base_type;
967 + typedef StaticMapKeys::mapping_type mapping_type;
969 + DhtMessage() : data_end(data) {};
971 + // Must be big enough to hold one of the possible variable-sized reply data.
972 + // Currently either:
973 + // - error message (size doesn't really matter, it'll be truncated at worst)
974 + // - announce token (8 bytes, needs 20 bytes buffer to build)
975 + // Never more than one of the above.
976 + // And additionally for queries we send:
977 + // - transaction ID (3 bytes)
978 + static const size_t data_size = 64;
979 + char data[data_size];
980 + char* data_end;
983 // Class holding transaction data to be transmitted.
984 class DhtTransactionPacket {
985 public:
986 // transaction packet
987 - DhtTransactionPacket(const rak::socket_address* s, const Object& d, unsigned int id, DhtTransaction* t)
988 + DhtTransactionPacket(const rak::socket_address* s, const DhtMessage& d, unsigned int id, DhtTransaction* t)
989 : m_sa(*s), m_id(id), m_transaction(t) { build_buffer(d); };
991 // non-transaction packet
992 - DhtTransactionPacket(const rak::socket_address* s, const Object& d)
993 + DhtTransactionPacket(const rak::socket_address* s, const DhtMessage& d)
994 : m_sa(*s), m_id(-cachedTime.seconds()), m_transaction(NULL) { build_buffer(d); };
996 ~DhtTransactionPacket() { delete[] m_data; }
997 @@ -214,7 +262,7 @@ public:
998 DhtTransaction* transaction() { return m_transaction; }
1000 private:
1001 - void build_buffer(const Object& data);
1002 + void build_buffer(const DhtMessage& data);
1004 rak::socket_address m_sa;
1005 char* m_data;
1006 @@ -255,9 +303,6 @@ public:
1007 int quick_timeout() { return m_quickTimeout; }
1008 bool has_quick_timeout() { return m_hasQuickTimeout; }
1010 - int dec_retry() { return m_retry--; }
1011 - int retry() { return m_retry; }
1013 DhtTransactionPacket* packet() { return m_packet; }
1014 void set_packet(DhtTransactionPacket* p) { m_packet = p; }
1016 @@ -282,7 +327,6 @@ private:
1017 rak::socket_address m_sa;
1018 int m_timeout;
1019 int m_quickTimeout;
1020 - int m_retry;
1021 DhtTransactionPacket* m_packet;
1024 @@ -337,7 +381,7 @@ public:
1026 class DhtTransactionAnnouncePeer : public DhtTransaction {
1027 public:
1028 - DhtTransactionAnnouncePeer(const HashString& id, const rak::socket_address* sa, const HashString& infoHash, const std::string& token)
1029 + DhtTransactionAnnouncePeer(const HashString& id, const rak::socket_address* sa, const HashString& infoHash, SimpleString token)
1030 : DhtTransaction(-1, 30, id, sa),
1031 m_infoHash(infoHash),
1032 m_token(token) { }
1033 @@ -345,11 +389,11 @@ public:
1034 virtual transaction_type type() { return DHT_ANNOUNCE_PEER; }
1036 const HashString& info_hash() { return m_infoHash; }
1037 - const std::string& token() { return m_token; }
1038 + SimpleString token() { return m_token; }
1040 private:
1041 HashString m_infoHash;
1042 - std::string m_token;
1043 + SimpleString m_token;
1046 inline bool
1047 diff --git a/src/download/download_constructor.cc b/src/download/download_constructor.cc
1048 index fc2a272..86e5351 100644
1049 --- a/src/download/download_constructor.cc
1050 +++ b/src/download/download_constructor.cc
1051 @@ -36,6 +36,7 @@
1053 #include "config.h"
1055 +#include <cstdio>
1056 #include <cstring>
1057 #include <string.h>
1058 #include <rak/functional.h>
1059 @@ -80,7 +81,10 @@ struct download_constructor_encoding_match :
1062 void
1063 -DownloadConstructor::initialize(const Object& b) {
1064 +DownloadConstructor::initialize(Object& b) {
1065 + if (!b.has_key_map("info") && b.has_key_string("magnet-uri"))
1066 + parse_magnet_uri(b, b.get_key_string("magnet-uri"));
1068 if (b.has_key_string("encoding"))
1069 m_defaultEncoding = b.get_key_string("encoding");
1071 @@ -135,10 +139,24 @@ DownloadConstructor::parse_info(const Object& b) {
1072 if (b.flags() & Object::flag_unordered)
1073 throw input_error("Download has unordered info dictionary.");
1075 - uint32_t chunkSize = b.get_key_value("piece length");
1076 + uint32_t chunkSize;
1078 + if (b.has_key_value("meta_download") && b.get_key_value("meta_download"))
1079 + m_download->info()->set_meta_download(true);
1081 + if (m_download->info()->is_meta_download()) {
1082 + if (b.get_key_string("pieces").length() != HashString::size_data)
1083 + throw input_error("Meta-download has invalid piece data.");
1085 + chunkSize = 1;
1086 + parse_single_file(b, chunkSize);
1088 + } else {
1089 + chunkSize = b.get_key_value("piece length");
1091 - if (chunkSize <= (1 << 10) || chunkSize > (128 << 20))
1092 - throw input_error("Torrent has an invalid \"piece length\".");
1093 + if (chunkSize <= (1 << 10) || chunkSize > (128 << 20))
1094 + throw input_error("Torrent has an invalid \"piece length\".");
1097 if (b.has_key("length")) {
1098 parse_single_file(b, chunkSize);
1099 @@ -147,11 +165,11 @@ DownloadConstructor::parse_info(const Object& b) {
1100 parse_multi_files(b.get_key("files"), chunkSize);
1101 fileList->set_root_dir("./" + m_download->info()->name());
1103 - } else {
1104 + } else if (!m_download->info()->is_meta_download()) {
1105 throw input_error("Torrent must have either length or files entry.");
1108 - if (fileList->size_bytes() == 0)
1109 + if (fileList->size_bytes() == 0 && !m_download->info()->is_meta_download())
1110 throw input_error("Torrent has zero length.");
1112 // Set chunksize before adding files to make sure the index range is
1113 @@ -238,7 +256,7 @@ DownloadConstructor::parse_single_file(const Object& b, uint32_t chunkSize) {
1114 throw input_error("Bad torrent file, \"name\" is an invalid path name.");
1116 FileList* fileList = m_download->main()->file_list();
1117 - fileList->initialize(b.get_key_value("length"), chunkSize);
1118 + fileList->initialize(chunkSize == 1 ? 1 : b.get_key_value("length"), chunkSize);
1119 fileList->set_multi_file(false);
1121 std::list<Path> pathList;
1122 @@ -342,4 +360,132 @@ DownloadConstructor::choose_path(std::list<Path>* pathList) {
1123 return pathList->front();
1126 +static const char*
1127 +parse_base32_sha1(const char* pos, HashString& hash) {
1128 + HashString::iterator hashItr = hash.begin();
1130 + static const int base_shift = 8+8-5;
1131 + int shift = base_shift;
1132 + uint16_t decoded = 0;
1134 + while (*pos) {
1135 + char c = *pos++;
1136 + uint16_t value;
1138 + if (c >= 'A' && c <= 'Z')
1139 + value = c - 'A';
1140 + else if (c >= 'a' && c <= 'z')
1141 + value = c - 'a';
1142 + else if (c >= '2' && c <= '7')
1143 + value = 26 + c - '2';
1144 + else if (c == '&')
1145 + break;
1146 + else
1147 + return NULL;
1149 + decoded |= (value << shift);
1150 + if (shift <= 8) {
1151 + // Too many characters for a base32 SHA1.
1152 + if (hashItr == hash.end())
1153 + return NULL;
1155 + *hashItr++ = (decoded >> 8);
1156 + decoded <<= 8;
1157 + shift += 3;
1158 + } else {
1159 + shift -= 5;
1163 + return hashItr != hash.end() || shift != base_shift ? NULL : pos;
1166 +void
1167 +DownloadConstructor::parse_magnet_uri(Object& b, const std::string& uri) {
1168 + if (std::strncmp(uri.c_str(), "magnet:?", 8))
1169 + throw input_error("Invalid magnet URI.");
1171 + const char* pos = uri.c_str() + 8;
1173 + Object trackers(Object::create_list());
1174 + HashString hash;
1175 + bool hashValid = false;
1177 + while (*pos) {
1178 + const char* tagStart = pos;
1179 + while (*pos != '=')
1180 + if (!*pos++)
1181 + break;
1183 + SimpleString tag(tagStart, pos - tagStart);
1184 + pos++;
1186 + // hash may be base32 encoded (optional in BEP 0009 and common practice)
1187 + if (tag == "xt") {
1188 + if (strncmp(pos, "urn:btih:", 9))
1189 + throw input_error("Invalid magnet URI.");
1191 + pos += 9;
1193 + const char* nextPos = parse_base32_sha1(pos, hash);
1194 + if (nextPos != NULL) {
1195 + pos = nextPos;
1196 + hashValid = true;
1197 + continue;
1201 + // everything else, including sometimes the hash, is url encoded.
1202 + std::string decoded;
1203 + while (*pos) {
1204 + char c = *pos++;
1205 + if (c == '%') {
1206 + if (sscanf(pos, "%02hhx", &c) != 1)
1207 + throw input_error("Invalid magnet URI.");
1209 + pos += 2;
1211 + } else if (c == '&') {
1212 + break;
1215 + decoded.push_back(c);
1218 + if (tag == "xt") {
1219 + // url-encoded hash as per magnet URN specs
1220 + if (decoded.length() == hash.size_data) {
1221 + hash = *HashString::cast_from(decoded);
1222 + hashValid = true;
1224 + // hex-encoded hash as per BEP 0009
1225 + } else if (decoded.length() == hash.size_data * 2) {
1226 + std::string::iterator hexItr = decoded.begin();
1227 + for (HashString::iterator itr = hash.begin(), last = hash.end(); itr != last; itr++, hexItr += 2)
1228 + *itr = (rak::hexchar_to_value(*hexItr) << 4) + rak::hexchar_to_value(*(hexItr + 1));
1229 + hashValid = true;
1231 + } else {
1232 + throw input_error("Invalid magnet URI.");
1234 + } else if (tag == "tr") {
1235 + trackers.insert_back(Object::create_list()).insert_back(decoded);
1237 + // could also handle "dn" = display name (torrent name), but we can't really use that
1240 + if (!hashValid)
1241 + throw input_error("Invalid magnet URI.");
1243 + Object& info = b.insert_key("info", Object::create_map());
1244 + info.insert_key("pieces", hash.str());
1245 + info.insert_key("name", rak::transform_hex(hash.str()) + ".meta");
1246 + info.insert_key("meta_download", (int64_t)1);
1248 + if (!trackers.as_list().empty()) {
1249 + b.insert_preserve_copy("announce", trackers.as_list().begin()->as_list().begin()->as_string());
1250 + b.insert_preserve_type("announce-list", trackers);
1255 diff --git a/src/download/download_constructor.h b/src/download/download_constructor.h
1256 index 7192f90..8af520f 100644
1257 --- a/src/download/download_constructor.h
1258 +++ b/src/download/download_constructor.h
1259 @@ -55,7 +55,7 @@ class DownloadConstructor {
1260 public:
1261 DownloadConstructor() : m_download(NULL), m_encodingList(NULL) {}
1263 - void initialize(const Object& b);
1264 + void initialize(Object& b);
1266 void set_download(DownloadWrapper* d) { m_download = d; }
1267 void set_encoding_list(const EncodingList* e) { m_encodingList = e; }
1268 @@ -64,6 +64,7 @@ private:
1269 void parse_name(const Object& b);
1270 void parse_tracker(const Object& b);
1271 void parse_info(const Object& b);
1272 + void parse_magnet_uri(Object& b, const std::string& uri);
1274 void add_tracker_group(const Object& b);
1275 void add_tracker_single(const Object& b, int group);
1276 diff --git a/src/download/download_info.h b/src/download/download_info.h
1277 index 0a3c0e8..68fb178 100644
1278 --- a/src/download/download_info.h
1279 +++ b/src/download/download_info.h
1280 @@ -76,6 +76,7 @@ public:
1281 m_isCompact(true),
1282 m_isAcceptingNewPeers(true),
1283 m_isPrivate(false),
1284 + m_isMetaDownload(false),
1285 m_pexEnabled(true),
1286 m_pexActive(true),
1288 @@ -86,7 +87,8 @@ public:
1289 m_uploadedBaseline(0),
1290 m_completedBaseline(0),
1291 m_sizePex(0),
1292 - m_maxSizePex(8) {
1293 + m_maxSizePex(8),
1294 + m_metadataSize(0) {
1297 const std::string& name() const { return m_name; }
1298 @@ -116,6 +118,9 @@ public:
1299 bool is_private() const { return m_isPrivate; }
1300 void set_private(bool p) { m_isPrivate = p; if (p) m_pexEnabled = false; }
1302 + bool is_meta_download() const { return m_isMetaDownload; }
1303 + void set_meta_download(bool m) { m_isMetaDownload = m; }
1305 bool is_pex_enabled() const { return m_pexEnabled; }
1306 void set_pex_enabled(bool enabled) { m_pexEnabled = enabled && !m_isPrivate; }
1308 @@ -134,6 +139,9 @@ public:
1309 uint64_t completed_adjusted() const { return std::max<int64_t>(m_slotStatCompleted() - completed_baseline(), 0); }
1310 void set_completed_baseline(uint64_t b) { m_completedBaseline = b; }
1312 + size_t metadata_size() const { return m_metadataSize; }
1313 + void set_metadata_size(size_t size) { m_metadataSize = size; }
1315 uint32_t size_pex() const { return m_sizePex; }
1316 void set_size_pex(uint32_t b) { m_sizePex = b; }
1318 @@ -165,6 +173,7 @@ private:
1319 bool m_isCompact;
1320 bool m_isAcceptingNewPeers;
1321 bool m_isPrivate;
1322 + bool m_isMetaDownload;
1323 bool m_pexEnabled;
1324 bool m_pexActive;
1326 @@ -176,6 +185,7 @@ private:
1327 uint64_t m_completedBaseline;
1328 uint32_t m_sizePex;
1329 uint32_t m_maxSizePex;
1330 + size_t m_metadataSize;
1332 slot_stat_type m_slotStatCompleted;
1333 slot_stat_type m_slotStatLeft;
1334 diff --git a/src/download/download_main.cc b/src/download/download_main.cc
1335 index 1dd5f98..5691021 100644
1336 --- a/src/download/download_main.cc
1337 +++ b/src/download/download_main.cc
1338 @@ -455,4 +455,19 @@ DownloadMain::do_peer_exchange() {
1342 +void
1343 +DownloadMain::set_metadata_size(size_t size) {
1344 + if (m_info->is_meta_download()) {
1345 + if (m_fileList.size_bytes() < 2)
1346 + file_list()->reset_filesize(size);
1347 + else if (size != m_fileList.size_bytes())
1348 + throw communication_error("Peer-supplied metadata size mismatch.");
1350 + } else if (m_info->metadata_size() && m_info->metadata_size() != size) {
1351 + throw communication_error("Peer-supplied metadata size mismatch.");
1354 + m_info->set_metadata_size(size);
1358 diff --git a/src/download/download_main.h b/src/download/download_main.h
1359 index 5d0090b..700f41e 100644
1360 --- a/src/download/download_main.h
1361 +++ b/src/download/download_main.h
1362 @@ -116,6 +116,8 @@ public:
1364 bool want_pex_msg() { return m_info->is_pex_active() && m_peerList.available_list()->want_more(); };
1366 + void set_metadata_size(size_t s);
1368 // Carefull with these.
1369 void setup_delegator();
1370 void setup_tracker();
1371 diff --git a/src/net/address_list.cc b/src/net/address_list.cc
1372 index 2fc3992..e5cf3cb 100644
1373 --- a/src/net/address_list.cc
1374 +++ b/src/net/address_list.cc
1375 @@ -70,7 +70,7 @@ AddressList::parse_address_normal(const Object::list_type& b) {
1378 void
1379 -AddressList::parse_address_compact(const std::string& s) {
1380 +AddressList::parse_address_compact(SimpleString s) {
1381 if (sizeof(const SocketAddressCompact) != 6)
1382 throw internal_error("ConnectionList::AddressList::parse_address_compact(...) bad struct size.");
1384 @@ -79,4 +79,18 @@ AddressList::parse_address_compact(const std::string& s) {
1385 std::back_inserter(*this));
1388 +void
1389 +AddressList::parse_address_bencode(SimpleString s) {
1390 + if (sizeof(const SocketAddressCompact) != 6)
1391 + throw internal_error("AddressList::parse_address_bencode(...) bad struct size.");
1393 + while (s.length() >= 2 + sizeof(SocketAddressCompact)) {
1394 + if (s[0] != '6' || s[1] != ':')
1395 + break;
1397 + insert(end(), *reinterpret_cast<const SocketAddressCompact*>(s.c_str() + 2));
1398 + s = SimpleString(s.c_str() + 2 + sizeof(SocketAddressCompact), s.length() - 2 - sizeof(SocketAddressCompact));
1403 diff --git a/src/net/address_list.h b/src/net/address_list.h
1404 index e4d2009..10dbac4 100644
1405 --- a/src/net/address_list.h
1406 +++ b/src/net/address_list.h
1407 @@ -42,6 +42,7 @@
1408 #include <rak/socket_address.h>
1410 #include <torrent/object.h>
1411 +#include <torrent/simple_string.h>
1413 namespace torrent {
1415 @@ -49,7 +50,8 @@ class AddressList : public std::list<rak::socket_address> {
1416 public:
1417 // Parse normal or compact list of addresses and add to AddressList
1418 void parse_address_normal(const Object::list_type& b);
1419 - void parse_address_compact(const std::string& s);
1420 + void parse_address_compact(SimpleString s);
1421 + void parse_address_bencode(SimpleString s);
1423 private:
1424 static rak::socket_address parse_address(const Object& b);
1425 diff --git a/src/net/data_buffer.h b/src/net/data_buffer.h
1426 index a26ca36..e3d9e38 100644
1427 --- a/src/net/data_buffer.h
1428 +++ b/src/net/data_buffer.h
1429 @@ -48,6 +48,7 @@ struct DataBuffer {
1430 DataBuffer(char* data, char* end) : m_data(data), m_end(end), m_owned(true) {}
1432 DataBuffer clone() const { DataBuffer d = *this; d.m_owned = false; return d; }
1433 + DataBuffer release() { DataBuffer d = *this; set(NULL, NULL, false); return d; }
1435 char* data() const { return m_data; }
1436 char* end() const { return m_end; }
1437 @@ -70,7 +71,7 @@ private:
1439 inline void
1440 DataBuffer::clear() {
1441 - if (!empty())
1442 + if (!empty() && m_owned)
1443 delete[] m_data;
1445 m_data = m_end = NULL;
1446 diff --git a/src/net/socket_base.cc b/src/net/socket_base.cc
1447 index 90457dc..13a9c8b 100644
1448 --- a/src/net/socket_base.cc
1449 +++ b/src/net/socket_base.cc
1450 @@ -47,7 +47,7 @@
1452 namespace torrent {
1454 -char* SocketBase::m_nullBuffer = new char[1 << 17];
1455 +char* SocketBase::m_nullBuffer = new char[SocketBase::null_buffer_size];
1457 SocketBase::~SocketBase() {
1458 if (get_fd().is_valid())
1459 diff --git a/src/net/socket_base.h b/src/net/socket_base.h
1460 index 9340a23..0f0f424 100644
1461 --- a/src/net/socket_base.h
1462 +++ b/src/net/socket_base.h
1463 @@ -68,6 +68,8 @@ protected:
1464 SocketBase(const SocketBase&);
1465 void operator = (const SocketBase&);
1467 + static const size_t null_buffer_size = 1 << 17;
1469 static char* m_nullBuffer;
1472 diff --git a/src/protocol/Makefile.am b/src/protocol/Makefile.am
1473 index 6171d06..18f671d 100644
1474 --- a/src/protocol/Makefile.am
1475 +++ b/src/protocol/Makefile.am
1476 @@ -17,6 +17,8 @@ libsub_protocol_la_SOURCES = \
1477 peer_connection_base.h \
1478 peer_connection_leech.cc \
1479 peer_connection_leech.h \
1480 + peer_connection_metadata.cc \
1481 + peer_connection_metadata.h \
1482 peer_factory.cc \
1483 peer_factory.h \
1484 protocol_base.h \
1485 diff --git a/src/protocol/extensions.cc b/src/protocol/extensions.cc
1486 index f3464af..3e0cf60 100644
1487 --- a/src/protocol/extensions.cc
1488 +++ b/src/protocol/extensions.cc
1489 @@ -37,30 +37,103 @@
1490 #include "config.h"
1492 #include <limits>
1493 -#include <sstream>
1494 +#include <stdarg.h>
1496 #include <cstdio>
1498 #include "download/available_list.h"
1499 #include "download/download_main.h"
1500 +#include "download/download_manager.h"
1501 +#include "download/download_wrapper.h"
1502 #include "protocol/peer_connection_base.h"
1503 #include "torrent/connection_manager.h"
1504 #include "torrent/object.h"
1505 #include "torrent/object_stream.h"
1506 #include "torrent/peer/connection_list.h"
1507 #include "torrent/peer/peer_info.h"
1508 -#include "tracker/tracker_http.h"
1509 +#include "torrent/static_map.h"
1510 #include "manager.h"
1512 #include "extensions.h"
1514 namespace torrent {
1516 -const char* ProtocolExtension::message_keys[] = {
1517 - "HANDSHAKE",
1518 - "ut_pex",
1519 +enum ext_handshake_keys {
1520 + key_e,
1521 + key_m_utMetadata,
1522 + key_m_utPex,
1523 + key_metadataSize,
1524 + key_p,
1525 + key_reqq,
1526 + key_v,
1527 + key_handshake_LAST
1530 +enum ext_pex_keys {
1531 + key_pex_added,
1532 + key_pex_LAST
1535 +enum ext_metadata_keys {
1536 + key_msgType,
1537 + key_piece,
1538 + key_totalSize,
1539 + key_metadata_LAST
1542 +class ExtHandshakeMessage : public StaticMap<ext_handshake_keys, key_handshake_LAST> {
1543 +public:
1544 + typedef StaticMap<ext_handshake_keys, key_handshake_LAST> base_type;
1545 + typedef StaticMapKeys::mapping_type mapping_type;
1548 +class ExtPEXMessage : public StaticMap<ext_pex_keys, key_pex_LAST> {
1549 +public:
1550 + typedef StaticMap<ext_pex_keys, key_pex_LAST> base_type;
1551 + typedef StaticMapKeys::mapping_type mapping_type;
1554 +class ExtMetadataMessage : public StaticMap<ext_metadata_keys, key_metadata_LAST> {
1555 +public:
1556 + typedef StaticMap<ext_metadata_keys, key_metadata_LAST> base_type;
1557 + typedef StaticMapKeys::mapping_type mapping_type;
1560 +ExtHandshakeMessage::mapping_type ext_handshake_key_names[ExtHandshakeMessage::length] = {
1561 + { key_e, "e" },
1562 + { key_m_utMetadata, "m::ut_metadata" },
1563 + { key_m_utPex, "m::ut_pex" },
1564 + { key_metadataSize, "metadata_size" },
1565 + { key_p, "p" },
1566 + { key_reqq, "reqq" },
1567 + { key_v, "v" },
1570 +ExtPEXMessage::mapping_type ext_pex_key_names[ExtPEXMessage::length] = {
1571 + { key_pex_added, "added" },
1574 +ExtMetadataMessage::mapping_type ext_metadata_key_names[ExtMetadataMessage::length] = {
1575 + { key_msgType, "msg_type" },
1576 + { key_piece, "piece" },
1577 + { key_totalSize, "total_size" },
1580 +ext_handshake_keys message_keys[ProtocolExtension::FIRST_INVALID] = {
1581 + key_handshake_LAST, // Handshake, not actually used.
1582 + key_m_utPex,
1583 + key_m_utMetadata,
1586 +template<>
1587 +const ExtHandshakeMessage::key_map_init ExtHandshakeMessage::base_type::keyMap(ext_handshake_key_names);
1589 +template<>
1590 +const ExtPEXMessage::key_map_init ExtPEXMessage::base_type::keyMap(ext_pex_key_names);
1592 +template<>
1593 +const ExtMetadataMessage::key_map_init ExtMetadataMessage::base_type::keyMap(ext_metadata_key_names);
1595 void
1596 ProtocolExtension::cleanup() {
1597 // if (is_default())
1598 @@ -105,23 +178,25 @@ ProtocolExtension::unset_local_enabled(int t) {
1600 DataBuffer
1601 ProtocolExtension::generate_handshake_message() {
1602 - Object map = Object::create_map();
1603 - Object message = Object::create_map();
1605 - map.insert_key(message_keys[UT_PEX], is_local_enabled(UT_PEX) ? 1 : 0);
1606 + ExtHandshakeMessage message;
1608 // Add "e" key if encryption is enabled, set it to 1 if we require
1609 // encryption for incoming connections, or 0 otherwise.
1610 if ((manager->connection_manager()->encryption_options() & ConnectionManager::encryption_allow_incoming) != 0)
1611 - message.insert_key("e", (manager->connection_manager()->encryption_options() & ConnectionManager::encryption_require) != 0);
1612 + message[key_e] = (manager->connection_manager()->encryption_options() & ConnectionManager::encryption_require) != 0;
1614 + message[key_p] = manager->connection_manager()->listen_port();
1615 + message[key_v] = SimpleString("libTorrent " VERSION);
1616 + message[key_reqq] = 2048; // maximum request queue size
1618 + if (!m_download->info()->is_meta_download())
1619 + message[key_metadataSize] = m_download->info()->metadata_size();
1621 - message.insert_key("m", map);
1622 - message.insert_key("p", manager->connection_manager()->listen_port());
1623 - message.insert_key("v", "libTorrent " VERSION);
1624 - message.insert_key("reqq", 2048); // maximum request queue size
1625 + message[key_m_utPex] = is_local_enabled(UT_PEX) ? UT_PEX : 0;
1626 + message[key_m_utMetadata] = UT_METADATA;
1628 char buffer[1024];
1629 - object_buffer_t result = object_write_bencode_c(object_write_to_buffer, NULL, std::make_pair(buffer, buffer + sizeof(buffer)), &message);
1630 + object_buffer_t result = staticMap_write_bencode_c(object_write_to_buffer, NULL, std::make_pair(buffer, buffer + sizeof(buffer)), message);
1632 int length = result.second - buffer;
1633 char* copy = new char[length];
1634 @@ -130,21 +205,30 @@ ProtocolExtension::generate_handshake_message() {
1635 return DataBuffer(copy, copy + length);
1638 -DataBuffer
1639 -ProtocolExtension::generate_toggle_message(ProtocolExtension::MessageType t, bool on) {
1640 - // TODO: Check if we're accepting this message type?
1641 +inline DataBuffer
1642 +ProtocolExtension::build_bencode(size_t maxLength, const char* format, ...) {
1643 + char* b = new char[maxLength];
1645 - // Manually create bencoded map { "m" => { message_keys[t] => on ? t : 0 } }
1646 - char* b = new char[32];
1647 - unsigned int length = snprintf(b, 32, "d1:md%zu:%si%deee", strlen(message_keys[t]), message_keys[t], on ? t : 0);
1648 + va_list args;
1649 + va_start(args, format);
1650 + unsigned int length = vsnprintf(b, maxLength, format, args);
1651 + va_end(args);
1653 - if (length > 32)
1654 - throw internal_error("ProtocolExtension::toggle_message wrote past buffer.");
1655 + if (length > maxLength)
1656 + throw internal_error("ProtocolExtension::build_bencode wrote past buffer.");
1658 return DataBuffer(b, b + length);
1661 DataBuffer
1662 +ProtocolExtension::generate_toggle_message(MessageType t, bool on) {
1663 + // TODO: Check if we're accepting this message type?
1665 + // Manually create bencoded map { "m" => { message_keys[t] => on ? t : 0 } }
1666 + 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);
1669 +DataBuffer
1670 ProtocolExtension::generate_ut_pex_message(const PEXList& added, const PEXList& removed) {
1671 if (added.empty() && removed.empty())
1672 return DataBuffer();
1673 @@ -173,7 +257,7 @@ ProtocolExtension::generate_ut_pex_message(const PEXList& added, const PEXList&
1675 void
1676 ProtocolExtension::read_start(int type, uint32_t length, bool skip) {
1677 - if (is_default() || (type >= FIRST_INVALID) || length > (1 << 14))
1678 + if (is_default() || (type >= FIRST_INVALID) || length > (1 << 15))
1679 throw communication_error("Received invalid extension message.");
1681 if (m_read != NULL || length < 0)
1682 @@ -193,41 +277,42 @@ ProtocolExtension::read_start(int type, uint32_t length, bool skip) {
1683 m_readPos = m_read = new char[length];
1686 -void
1687 +bool
1688 ProtocolExtension::read_done() {
1689 - if (m_readType == SKIP_EXTENSION) {
1690 - delete [] m_read;
1691 - m_read = NULL;
1692 - return;
1694 + bool blocked = false;
1696 - std::stringstream s(std::string(m_read, m_readPos));
1697 - s.imbue(std::locale::classic());
1698 + try {
1699 + switch(m_readType) {
1700 + case SKIP_EXTENSION:
1701 + break;
1703 - delete [] m_read;
1704 - m_read = NULL;
1705 + case HANDSHAKE:
1706 + blocked = parse_handshake();
1707 + break;
1709 - Object message;
1710 - s >> message;
1711 + case UT_PEX:
1712 + blocked = parse_ut_pex();
1713 + break;
1715 - if (s.fail() || !message.is_map())
1716 - throw communication_error("Invalid extension message.");
1717 + case UT_METADATA:
1718 + blocked = parse_ut_metadata();
1719 + break;
1721 - switch(m_readType) {
1722 - case HANDSHAKE:
1723 - parse_handshake(message);
1724 - break;
1726 - case UT_PEX:
1727 - parse_ut_pex(message);
1728 - break;
1729 + default:
1730 + throw internal_error("ProtocolExtension::read_done called with invalid extension type.");
1733 - default:
1734 - throw internal_error("ProtocolExtension::down_extension_finished called with invalid extension type.");
1735 + } catch (bencode_error& e) {
1736 + // Ignore malformed messages.
1739 + delete [] m_read;
1740 + m_read = NULL;
1742 m_readType = FIRST_INVALID;
1743 m_flags |= flag_received_ext;
1745 + return !blocked;
1748 // Called whenever peer enables or disables an extension.
1749 @@ -241,25 +326,23 @@ ProtocolExtension::peer_toggle_remote(int type, bool active) {
1753 -void
1754 -ProtocolExtension::parse_handshake(const Object& message) {
1755 - if (message.has_key_map("m")) {
1756 - const Object& idMap = message.get_key("m");
1757 +bool
1758 +ProtocolExtension::parse_handshake() {
1759 + ExtHandshakeMessage message;
1760 + staticMap_read_bencode(m_read, m_readPos, message);
1762 - for (int t = HANDSHAKE + 1; t < FIRST_INVALID; t++) {
1763 - if (!idMap.has_key_value(message_keys[t]))
1764 - continue;
1765 + for (int t = HANDSHAKE + 1; t < FIRST_INVALID; t++) {
1766 + if (!message[message_keys[t]].is_value())
1767 + continue;
1769 - uint8_t id = idMap.get_key_value(message_keys[t]);
1770 + uint8_t id = message[message_keys[t]].as_value();
1772 - set_remote_supported(t);
1773 + set_remote_supported(t);
1775 - if (id != m_idMap[t - 1]) {
1776 - peer_toggle_remote(t, id != 0);
1778 - m_idMap[t - 1] = id;
1780 + if (id != m_idMap[t - 1]) {
1781 + peer_toggle_remote(t, id != 0);
1783 + m_idMap[t - 1] = id;
1787 @@ -271,31 +354,39 @@ ProtocolExtension::parse_handshake(const Object& message) {
1788 unset_local_enabled(t);
1791 - if (message.has_key_value("p")) {
1792 - uint16_t port = message.get_key_value("p");
1793 + if (message[key_p].is_value()) {
1794 + uint16_t port = message[key_p].as_value();
1796 if (port > 0)
1797 m_peerInfo->set_listen_port(port);
1800 - if (message.has_key_value("reqq"))
1801 - m_maxQueueLength = message.get_key_value("reqq");
1802 + if (message[key_reqq].is_value())
1803 + m_maxQueueLength = message[key_reqq].as_value();
1805 + if (message[key_metadataSize].is_value())
1806 + m_download->set_metadata_size(message[key_metadataSize].as_value());
1808 m_flags &= ~flag_initial_handshake;
1810 + return false;
1813 -void
1814 -ProtocolExtension::parse_ut_pex(const Object& message) {
1815 +bool
1816 +ProtocolExtension::parse_ut_pex() {
1817 // Ignore message if we're still in the handshake (no connection
1818 // yet), or no peers are present.
1820 + ExtPEXMessage message;
1821 + staticMap_read_bencode(m_read, m_readPos, message);
1823 // TODO: Check if pex is enabled?
1824 - if (!message.has_key_string("added"))
1825 - return;
1826 + if (!message[key_pex_added].is_sstring())
1827 + return false;
1829 - const std::string& peers = message.get_key_string("added");
1830 + SimpleString peers = message[key_pex_added].as_sstring();
1831 if (peers.empty())
1832 - return;
1833 + return false;
1835 AddressList l;
1836 l.parse_address_compact(peers);
1837 @@ -303,6 +394,82 @@ ProtocolExtension::parse_ut_pex(const Object& message) {
1838 l.erase(std::unique(l.begin(), l.end()), l.end());
1840 m_download->peer_list()->insert_available(&l);
1842 + return false;
1845 +bool
1846 +ProtocolExtension::parse_ut_metadata() {
1847 + ExtMetadataMessage message;
1849 + // Piece data comes after bencoded extension message.
1850 + const char* dataStart = staticMap_read_bencode(m_read, m_readPos, message);
1852 + switch(message[key_msgType].as_value()) {
1853 + case 0:
1854 + // Can't process new request while still having data to send.
1855 + if (has_pending_message())
1856 + return true;
1858 + send_metadata_piece(message[key_piece].as_value());
1859 + break;
1861 + case 1:
1862 + if (m_connection == NULL)
1863 + break;
1865 + m_connection->receive_metadata_piece(message[key_piece].as_value(), dataStart, m_readPos - dataStart);
1866 + break;
1868 + case 2:
1869 + if (m_connection != NULL)
1870 + m_connection->receive_metadata_piece(message[key_piece].as_value(), NULL, 0);
1871 + break;
1872 + };
1874 + return false;
1877 +void
1878 +ProtocolExtension::send_metadata_piece(size_t piece) {
1879 + // Reject out-of-range piece, or if we don't have the complete metadata yet.
1880 + size_t metadataSize = m_download->info()->metadata_size();
1881 + size_t pieceEnd = (metadataSize + metadata_piece_size - 1) >> metadata_piece_shift;
1883 + if (m_download->info()->is_meta_download() || piece >= pieceEnd) {
1884 + // reject: { "msg_type" => 2, "piece" => ... }
1885 + m_pendingType = UT_METADATA;
1886 + m_pending = build_bencode(40, "d8:msg_typei2e5:piecei%zuee", piece);
1887 + return;
1890 + // These messages will be rare, so we'll just build the
1891 + // metadata here instead of caching it uselessly.
1892 + char* buffer = new char[metadataSize];
1893 + object_buffer_t result = object_write_bencode_c(object_write_to_buffer, NULL, object_buffer_t(buffer, buffer + metadataSize),
1894 + &(*manager->download_manager()->find(m_download->info()))->bencode()->get_key("info"));
1896 + // data: { "msg_type" => 1, "piece" => ..., "total_size" => ... } followed by piece data (outside of dictionary)
1897 + size_t length = piece == pieceEnd - 1 ? m_download->info()->metadata_size() % metadata_piece_size : metadata_piece_size;
1898 + m_pendingType = UT_METADATA;
1899 + m_pending = build_bencode(length + 128, "d8:msg_typei1e5:piecei%zue10:total_sizei%zuee", piece, metadataSize);
1901 + memcpy(m_pending.end(), buffer + (piece << metadata_piece_shift), length);
1902 + m_pending.set(m_pending.data(), m_pending.end() + length, m_pending.owned());
1903 + delete [] buffer;
1906 +bool
1907 +ProtocolExtension::request_metadata_piece(const Piece* p) {
1908 + if (p->offset() % metadata_piece_size)
1909 + throw internal_error("ProtocolExtension::request_metadata_piece got misaligned piece offset.");
1911 + if (has_pending_message())
1912 + return false;
1914 + m_pendingType = UT_METADATA;
1915 + m_pending = build_bencode(40, "d8:msg_typei0e5:piecei%uee", (unsigned)(p->offset() >> metadata_piece_shift));
1916 + return true;
1920 diff --git a/src/protocol/extensions.h b/src/protocol/extensions.h
1921 index 1c370fc..485e7d7 100644
1922 --- a/src/protocol/extensions.h
1923 +++ b/src/protocol/extensions.h
1924 @@ -46,6 +46,13 @@
1925 #include "download/download_info.h"
1926 #include "net/data_buffer.h"
1928 +// Not really important, so no need to make this a configure check.
1929 +#ifdef __GNUC__
1930 +#define ATTRIBUTE_PRINTF(num) __attribute__ ((format (printf, num, num+1)))
1931 +#else
1932 +#define ATTRIBUTE_PRINTF(num)
1933 +#endif
1935 namespace torrent {
1937 class ProtocolExtension {
1938 @@ -53,6 +60,7 @@ public:
1939 typedef enum {
1940 HANDSHAKE = 0,
1941 UT_PEX,
1942 + UT_METADATA,
1944 FIRST_INVALID, // first invalid message ID
1946 @@ -71,11 +79,13 @@ public:
1947 static const int flag_local_enabled_base = 1<<8;
1948 static const int flag_remote_supported_base = 1<<16;
1950 - static const char* message_keys[FIRST_INVALID];
1952 // Number of extensions we support, not counting handshake.
1953 static const int extension_count = FIRST_INVALID - HANDSHAKE - 1;
1955 + // Fixed size of a metadata piece (16 KB).
1956 + static const size_t metadata_piece_shift = 14;
1957 + static const size_t metadata_piece_size = 1 << metadata_piece_shift;
1959 ProtocolExtension();
1960 ~ProtocolExtension() { delete [] m_read; }
1962 @@ -86,6 +96,7 @@ public:
1963 static ProtocolExtension make_default();
1965 void set_info(PeerInfo* peerInfo, DownloadMain* download) { m_peerInfo = peerInfo; m_download = download; }
1966 + void set_connection(PeerConnectionBase* c) { m_connection = c; }
1968 DataBuffer generate_handshake_message();
1969 static DataBuffer generate_toggle_message(MessageType t, bool on);
1970 @@ -107,7 +118,7 @@ public:
1972 // Handle reading extension data from peer.
1973 void read_start(int type, uint32_t length, bool skip);
1974 - void read_done();
1975 + bool read_done();
1977 char* read_position() { return m_readPos; }
1978 bool read_move(uint32_t v) { m_readPos += v; return (m_readLeft -= v) == 0; }
1979 @@ -127,11 +138,23 @@ public:
1980 void clear_initial_pex() { m_flags &= ~flag_initial_pex; }
1981 void reset() { std::memset(&m_idMap, 0, sizeof(m_idMap)); }
1983 + bool request_metadata_piece(const Piece* p);
1985 + // To handle cases where the extension protocol needs to send a reply.
1986 + bool has_pending_message() const { return m_pendingType != HANDSHAKE; }
1987 + MessageType pending_message_type() const { return m_pendingType; }
1988 + DataBuffer pending_message_data() { return m_pending.release(); }
1989 + void clear_pending_message() { if (m_pending.empty()) m_pendingType = HANDSHAKE; }
1991 private:
1992 - void parse_handshake(const Object& message);
1993 - void parse_ut_pex(const Object& message);
1994 + bool parse_handshake();
1995 + bool parse_ut_pex();
1996 + bool parse_ut_metadata();
1998 + static DataBuffer build_bencode(size_t maxLength, const char* format, ...) ATTRIBUTE_PRINTF(2);
2000 void peer_toggle_remote(int type, bool active);
2001 + void send_metadata_piece(size_t piece);
2003 // Map of IDs peer uses for each extension message type, excluding
2004 // HANDSHAKE.
2005 @@ -142,11 +165,15 @@ private:
2006 int m_flags;
2007 PeerInfo* m_peerInfo;
2008 DownloadMain* m_download;
2009 + PeerConnectionBase* m_connection;
2011 uint8_t m_readType;
2012 uint32_t m_readLeft;
2013 char* m_read;
2014 char* m_readPos;
2016 + MessageType m_pendingType;
2017 + DataBuffer m_pending;
2020 inline
2021 @@ -156,10 +183,13 @@ ProtocolExtension::ProtocolExtension() :
2022 m_flags(flag_local_enabled_base | flag_remote_supported_base | flag_initial_handshake),
2023 m_peerInfo(NULL),
2024 m_download(NULL),
2025 + m_connection(NULL),
2026 m_readType(FIRST_INVALID),
2027 - m_read(NULL) {
2028 + m_read(NULL),
2029 + m_pendingType(HANDSHAKE) {
2031 reset();
2032 + set_local_enabled(UT_METADATA);
2035 inline ProtocolExtension
2036 diff --git a/src/protocol/handshake.cc b/src/protocol/handshake.cc
2037 index d863f7b..7fb389b 100644
2038 --- a/src/protocol/handshake.cc
2039 +++ b/src/protocol/handshake.cc
2040 @@ -723,6 +723,17 @@ restart:
2042 case READ_MESSAGE:
2043 case POST_HANDSHAKE:
2044 + // For meta-downloads, we aren't interested in the bitfield or
2045 + // extension messages here, PCMetadata handles all that. The
2046 + // bitfield only refers to the single-chunk meta-data, so fake that.
2047 + if (m_download->info()->is_meta_download()) {
2048 + m_bitfield.set_size_bits(1);
2049 + m_bitfield.allocate();
2050 + m_bitfield.set(0);
2051 + read_done();
2052 + break;
2055 fill_read_buffer(5);
2057 // Received a keep-alive message which means we won't be
2058 @@ -1022,6 +1033,10 @@ Handshake::prepare_peer_info() {
2059 std::memcpy(m_peerInfo->set_options(), m_options, 8);
2060 m_peerInfo->mutable_id().assign((const char*)m_readBuffer.position());
2061 m_readBuffer.consume(20);
2063 + // For meta downloads, we require support of the extension protocol.
2064 + if (m_download->info()->is_meta_download() && !m_peerInfo->supports_extensions())
2065 + throw handshake_error(ConnectionManager::handshake_dropped, e_handshake_unwanted_connection);
2068 void
2069 diff --git a/src/protocol/peer_connection_base.cc b/src/protocol/peer_connection_base.cc
2070 index ab043a6..815ea93 100644
2071 --- a/src/protocol/peer_connection_base.cc
2072 +++ b/src/protocol/peer_connection_base.cc
2073 @@ -93,8 +93,7 @@ PeerConnectionBase::~PeerConnectionBase() {
2074 if (m_extensions != NULL && !m_extensions->is_default())
2075 delete m_extensions;
2077 - if (m_extensionMessage.owned())
2078 - m_extensionMessage.clear();
2079 + m_extensionMessage.clear();
2082 void
2083 @@ -116,6 +115,8 @@ PeerConnectionBase::initialize(DownloadMain* download, PeerInfo* peerInfo, Socke
2084 m_encryption = *encryptionInfo;
2085 m_extensions = extensions;
2087 + m_extensions->set_connection(this);
2089 m_peerChunks.set_peer_info(m_peerInfo);
2090 m_peerChunks.bitfield()->swap(*bitfield);
2092 @@ -581,8 +582,12 @@ PeerConnectionBase::down_extension() {
2093 m_extensions->read_move(bytes);
2096 - if (m_extensions->is_complete())
2097 - m_extensions->read_done();
2098 + // If extension can't be processed yet (due to a pending write),
2099 + // disable reads until the pending message is completely sent.
2100 + if (m_extensions->is_complete() && !m_extensions->is_invalid() && !m_extensions->read_done()) {
2101 + manager->poll()->remove_read(this);
2102 + return false;
2105 return m_extensions->is_complete();
2107 @@ -693,12 +698,15 @@ PeerConnectionBase::up_extension() {
2108 if (m_extensionOffset < m_extensionMessage.length())
2109 return false;
2111 - // clear() deletes the buffer, only do that if we made a copy,
2112 - // otherwise the buffer is shared among all connections.
2113 - if (m_extensionMessage.owned())
2114 - m_extensionMessage.clear();
2115 - else
2116 - m_extensionMessage.set(NULL, NULL, false);
2117 + m_extensionMessage.clear();
2119 + // If we have an unprocessed message, process it now and enable reads again.
2120 + if (m_extensions->is_complete() && !m_extensions->is_invalid()) {
2121 + if (!m_extensions->read_done())
2122 + throw internal_error("PeerConnectionBase::up_extension could not process complete extension message.");
2124 + manager->poll()->insert_read(this);
2127 return true;
2129 @@ -857,4 +865,16 @@ PeerConnectionBase::send_pex_message() {
2130 return true;
2133 +// Extension protocol needs to send a reply.
2134 +bool
2135 +PeerConnectionBase::send_ext_message() {
2136 + write_prepare_extension(m_extensions->pending_message_type(), m_extensions->pending_message_data());
2137 + m_extensions->clear_pending_message();
2138 + return true;
2141 +void
2142 +PeerConnectionBase::receive_metadata_piece(uint32_t piece, const char* data, uint32_t length) {
2146 diff --git a/src/protocol/peer_connection_base.h b/src/protocol/peer_connection_base.h
2147 index 2994963..d131341 100644
2148 --- a/src/protocol/peer_connection_base.h
2149 +++ b/src/protocol/peer_connection_base.h
2150 @@ -140,6 +140,9 @@ public:
2151 void read_insert_poll_safe();
2152 void write_insert_poll_safe();
2154 + // Communication with the protocol extensions
2155 + virtual void receive_metadata_piece(uint32_t piece, const char* data, uint32_t length);
2157 protected:
2158 static const uint32_t extension_must_encrypt = ~uint32_t();
2160 @@ -179,6 +182,7 @@ protected:
2161 bool try_request_pieces();
2163 bool send_pex_message();
2164 + bool send_ext_message();
2166 DownloadMain* m_download;
2168 diff --git a/src/protocol/peer_connection_leech.cc b/src/protocol/peer_connection_leech.cc
2169 index a75d333..36c6d7a 100644
2170 --- a/src/protocol/peer_connection_leech.cc
2171 +++ b/src/protocol/peer_connection_leech.cc
2172 @@ -333,9 +333,13 @@ PeerConnection<type>::read_message() {
2173 m_down->set_state(ProtocolRead::READ_EXTENSION);
2176 - if (down_extension())
2177 - m_down->set_state(ProtocolRead::IDLE);
2178 + if (!down_extension())
2179 + return false;
2181 + if (m_extensions->has_pending_message())
2182 + write_insert_poll_safe();
2184 + m_down->set_state(ProtocolRead::IDLE);
2185 return true;
2187 default:
2188 @@ -433,6 +437,9 @@ PeerConnection<type>::event_read() {
2189 if (!down_extension())
2190 return;
2192 + if (m_extensions->has_pending_message())
2193 + write_insert_poll_safe();
2195 m_down->set_state(ProtocolRead::IDLE);
2196 break;
2198 @@ -546,6 +553,10 @@ PeerConnection<type>::fill_write_buffer() {
2199 send_pex_message()) {
2200 // Don't do anything else if send_pex_message() succeeded.
2202 + } else if (m_extensions->has_pending_message() && m_up->can_write_extension() &&
2203 + send_ext_message()) {
2204 + // Same.
2206 } else if (!m_upChoke.choked() &&
2207 !m_peerChunks.upload_queue()->empty() &&
2208 m_up->can_write_piece() &&
2209 diff --git a/src/protocol/peer_connection_metadata.cc b/src/protocol/peer_connection_metadata.cc
2210 new file mode 100644
2211 index 0000000..24f13ca
2212 --- /dev/null
2213 +++ b/src/protocol/peer_connection_metadata.cc
2214 @@ -0,0 +1,461 @@
2215 +// libTorrent - BitTorrent library
2216 +// Copyright (C) 2005-2007, Jari Sundell
2218 +// This program is free software; you can redistribute it and/or modify
2219 +// it under the terms of the GNU General Public License as published by
2220 +// the Free Software Foundation; either version 2 of the License, or
2221 +// (at your option) any later version.
2222 +//
2223 +// This program is distributed in the hope that it will be useful,
2224 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
2225 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2226 +// GNU General Public License for more details.
2227 +//
2228 +// You should have received a copy of the GNU General Public License
2229 +// along with this program; if not, write to the Free Software
2230 +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2232 +// In addition, as a special exception, the copyright holders give
2233 +// permission to link the code of portions of this program with the
2234 +// OpenSSL library under certain conditions as described in each
2235 +// individual source file, and distribute linked combinations
2236 +// including the two.
2238 +// You must obey the GNU General Public License in all respects for
2239 +// all of the code used other than OpenSSL. If you modify file(s)
2240 +// with this exception, you may extend this exception to your version
2241 +// of the file(s), but you are not obligated to do so. If you do not
2242 +// wish to do so, delete this exception statement from your version.
2243 +// If you delete this exception statement from all source files in the
2244 +// program, then also delete it here.
2246 +// Contact: Jari Sundell <jaris@ifi.uio.no>
2248 +// Skomakerveien 33
2249 +// 3185 Skoppum, NORWAY
2251 +#include "config.h"
2253 +#include <cstring>
2254 +#include <sstream>
2256 +#include "data/chunk_list_node.h"
2257 +#include "download/choke_manager.h"
2258 +#include "download/chunk_selector.h"
2259 +#include "download/chunk_statistics.h"
2260 +#include "download/download_info.h"
2261 +#include "download/download_main.h"
2262 +#include "torrent/dht_manager.h"
2263 +#include "torrent/peer/connection_list.h"
2264 +#include "torrent/peer/peer_info.h"
2266 +#include "extensions.h"
2267 +#include "peer_connection_metadata.h"
2269 +namespace torrent {
2271 +PeerConnectionMetadata::~PeerConnectionMetadata() {
2274 +void
2275 +PeerConnectionMetadata::initialize_custom() {
2278 +void
2279 +PeerConnectionMetadata::update_interested() {
2282 +bool
2283 +PeerConnectionMetadata::receive_keepalive() {
2284 + if (cachedTime - m_timeLastRead > rak::timer::from_seconds(240))
2285 + return false;
2287 + m_tryRequest = true;
2289 + // There's no point in adding ourselves to the write poll if the
2290 + // buffer is full, as that will already have been taken care of.
2291 + if (m_up->get_state() == ProtocolWrite::IDLE &&
2292 + m_up->can_write_keepalive()) {
2294 + write_insert_poll_safe();
2296 + ProtocolBuffer<512>::iterator old_end = m_up->buffer()->end();
2297 + m_up->write_keepalive();
2299 + if (is_encrypted())
2300 + m_encryption.encrypt(old_end, m_up->buffer()->end() - old_end);
2303 + return true;
2306 +// We keep the message in the buffer if it is incomplete instead of
2307 +// keeping the state and remembering the read information. This
2308 +// shouldn't happen very often compared to full reads.
2309 +inline bool
2310 +PeerConnectionMetadata::read_message() {
2311 + ProtocolBuffer<512>* buf = m_down->buffer();
2313 + if (buf->remaining() < 4)
2314 + return false;
2316 + // Remember the start of the message so we may reset it if we don't
2317 + // have the whole message.
2318 + ProtocolBuffer<512>::iterator beginning = buf->position();
2320 + uint32_t length = buf->read_32();
2322 + if (length == 0) {
2323 + // Keepalive message.
2324 + m_down->set_last_command(ProtocolBase::KEEP_ALIVE);
2326 + return true;
2328 + } else if (buf->remaining() < 1) {
2329 + buf->set_position_itr(beginning);
2330 + return false;
2332 + } else if (length > (1 << 20)) {
2333 + throw communication_error("PeerConnection::read_message() got an invalid message length.");
2336 + m_down->set_last_command((ProtocolBase::Protocol)buf->peek_8());
2338 + // Ignore most messages, they aren't relevant for a metadata download.
2339 + switch (buf->read_8()) {
2340 + case ProtocolBase::CHOKE:
2341 + case ProtocolBase::UNCHOKE:
2342 + case ProtocolBase::INTERESTED:
2343 + case ProtocolBase::NOT_INTERESTED:
2344 + return true;
2346 + case ProtocolBase::HAVE:
2347 + if (!m_down->can_read_have_body())
2348 + break;
2350 + buf->read_32();
2351 + return true;
2353 + case ProtocolBase::REQUEST:
2354 + if (!m_down->can_read_request_body())
2355 + break;
2357 + m_down->read_request();
2358 + return true;
2360 + case ProtocolBase::PIECE:
2361 + throw communication_error("Received a piece but the connection is strictly for meta data.");
2363 + case ProtocolBase::CANCEL:
2364 + if (!m_down->can_read_cancel_body())
2365 + break;
2367 + m_down->read_request();
2368 + return true;
2370 + case ProtocolBase::PORT:
2371 + if (!m_down->can_read_port_body())
2372 + break;
2374 + manager->dht_manager()->add_node(m_peerInfo->socket_address(), m_down->buffer()->read_16());
2375 + return true;
2377 + case ProtocolBase::EXTENSION_PROTOCOL:
2378 + if (!m_down->can_read_extension_body())
2379 + break;
2381 + if (m_extensions->is_default()) {
2382 + m_extensions = new ProtocolExtension();
2383 + m_extensions->set_info(m_peerInfo, m_download);
2387 + int extension = m_down->buffer()->read_8();
2388 + m_extensions->read_start(extension, length - 2, (extension == ProtocolExtension::UT_PEX) && !m_download->want_pex_msg());
2389 + m_down->set_state(ProtocolRead::READ_EXTENSION);
2392 + if (!down_extension())
2393 + return false;
2395 + // Drop peer if it disabled the metadata extension.
2396 + if (!m_extensions->is_remote_supported(ProtocolExtension::UT_METADATA))
2397 + throw close_connection();
2399 + m_down->set_state(ProtocolRead::IDLE);
2400 + m_tryRequest = true;
2401 + write_insert_poll_safe();
2403 + return true;
2405 + case ProtocolBase::BITFIELD:
2406 + // Discard the bitfield sent by the peer.
2407 + m_skipLength = length - 1;
2408 + m_down->set_state(ProtocolRead::READ_SKIP_PIECE);
2409 + return false;
2411 + default:
2412 + throw communication_error("Received unsupported message type.");
2415 + // We were unsuccessfull in reading the message, need more data.
2416 + buf->set_position_itr(beginning);
2417 + return false;
2420 +void
2421 +PeerConnectionMetadata::event_read() {
2422 + m_timeLastRead = cachedTime;
2424 + // Need to make sure ProtocolBuffer::end() is pointing to the end of
2425 + // the unread data, and that the unread data starts from the
2426 + // beginning of the buffer. Or do we use position? Propably best,
2427 + // therefor ProtocolBuffer::position() points to the beginning of
2428 + // the unused data.
2430 + try {
2432 + // Normal read.
2433 + //
2434 + // We rarely will read zero bytes as the read of 64 bytes will
2435 + // almost always either not fill up or it will require additional
2436 + // reads.
2437 + //
2438 + // Only loop when end hits 64.
2440 + do {
2441 + switch (m_down->get_state()) {
2442 + case ProtocolRead::IDLE:
2443 + if (m_down->buffer()->size_end() < read_size) {
2444 + unsigned int length = read_stream_throws(m_down->buffer()->end(), read_size - m_down->buffer()->size_end());
2445 + m_down->throttle()->node_used_unthrottled(length);
2447 + if (is_encrypted())
2448 + m_encryption.decrypt(m_down->buffer()->end(), length);
2450 + m_down->buffer()->move_end(length);
2453 + while (read_message());
2455 + if (m_down->buffer()->size_end() == read_size) {
2456 + m_down->buffer()->move_unused();
2457 + break;
2458 + } else {
2459 + m_down->buffer()->move_unused();
2460 + return;
2463 + case ProtocolRead::READ_EXTENSION:
2464 + if (!down_extension())
2465 + return;
2467 + // Drop peer if it disabled the metadata extension.
2468 + if (!m_extensions->is_remote_supported(ProtocolExtension::UT_METADATA))
2469 + throw close_connection();
2471 + m_down->set_state(ProtocolRead::IDLE);
2472 + m_tryRequest = true;
2473 + write_insert_poll_safe();
2474 + break;
2476 + // Actually skipping the bitfield.
2477 + // We never receive normal piece messages anyway.
2478 + case ProtocolRead::READ_SKIP_PIECE:
2479 + if (!read_skip_bitfield())
2480 + return;
2482 + m_down->set_state(ProtocolRead::IDLE);
2483 + break;
2485 + default:
2486 + throw internal_error("PeerConnection::event_read() wrong state.");
2489 + // Figure out how to get rid of the shouldLoop boolean.
2490 + } while (true);
2492 + // Exception handlers:
2494 + } catch (close_connection& e) {
2495 + m_download->connection_list()->erase(this, 0);
2497 + } catch (blocked_connection& e) {
2498 + m_download->info()->signal_network_log().emit("Momentarily blocked read connection.");
2499 + m_download->connection_list()->erase(this, 0);
2501 + } catch (network_error& e) {
2502 + m_download->connection_list()->erase(this, 0);
2504 + } catch (storage_error& e) {
2505 + m_download->info()->signal_storage_error().emit(e.what());
2506 + m_download->connection_list()->erase(this, 0);
2508 + } catch (base_error& e) {
2509 + std::stringstream s;
2510 + s << "Connection read fd(" << get_fd().get_fd() << ',' << m_down->get_state() << ',' << m_down->last_command() << ") \"" << e.what() << '"';
2512 + throw internal_error(s.str());
2516 +inline void
2517 +PeerConnectionMetadata::fill_write_buffer() {
2518 + ProtocolBuffer<512>::iterator old_end = m_up->buffer()->end();
2520 + if (m_tryRequest)
2521 + m_tryRequest = try_request_metadata_pieces();
2523 + if (m_sendPEXMask && m_up->can_write_extension() &&
2524 + send_pex_message()) {
2525 + // Don't do anything else if send_pex_message() succeeded.
2527 + } else if (m_extensions->has_pending_message() && m_up->can_write_extension() &&
2528 + send_ext_message()) {
2529 + // Same.
2532 + if (is_encrypted())
2533 + m_encryption.encrypt(old_end, m_up->buffer()->end() - old_end);
2536 +void
2537 +PeerConnectionMetadata::event_write() {
2538 + try {
2540 + do {
2542 + switch (m_up->get_state()) {
2543 + case ProtocolWrite::IDLE:
2545 + fill_write_buffer();
2547 + if (m_up->buffer()->remaining() == 0) {
2548 + manager->poll()->remove_write(this);
2549 + return;
2552 + m_up->set_state(ProtocolWrite::MSG);
2554 + case ProtocolWrite::MSG:
2555 + if (!m_up->buffer()->consume(m_up->throttle()->node_used_unthrottled(write_stream_throws(m_up->buffer()->position(), m_up->buffer()->remaining()))))
2556 + return;
2558 + m_up->buffer()->reset();
2560 + if (m_up->last_command() != ProtocolBase::EXTENSION_PROTOCOL) {
2561 + m_up->set_state(ProtocolWrite::IDLE);
2562 + break;
2565 + m_up->set_state(ProtocolWrite::WRITE_EXTENSION);
2567 + case ProtocolWrite::WRITE_EXTENSION:
2568 + if (!up_extension())
2569 + return;
2571 + m_up->set_state(ProtocolWrite::IDLE);
2572 + break;
2574 + default:
2575 + throw internal_error("PeerConnection::event_write() wrong state.");
2578 + } while (true);
2580 + } catch (close_connection& e) {
2581 + m_download->connection_list()->erase(this, 0);
2583 + } catch (blocked_connection& e) {
2584 + m_download->info()->signal_network_log().emit("Momentarily blocked write connection.");
2585 + m_download->connection_list()->erase(this, 0);
2587 + } catch (network_error& e) {
2588 + m_download->connection_list()->erase(this, 0);
2590 + } catch (storage_error& e) {
2591 + m_download->info()->signal_storage_error().emit(e.what());
2592 + m_download->connection_list()->erase(this, 0);
2594 + } catch (base_error& e) {
2595 + std::stringstream s;
2596 + s << "Connection write fd(" << get_fd().get_fd() << ',' << m_up->get_state() << ',' << m_up->last_command() << ") \"" << e.what() << '"';
2598 + throw internal_error(s.str());
2602 +bool
2603 +PeerConnectionMetadata::read_skip_bitfield() {
2604 + if (m_down->buffer()->remaining()) {
2605 + uint32_t length = std::min(m_skipLength, (uint32_t)m_down->buffer()->remaining());
2606 + m_down->buffer()->consume(length);
2607 + m_skipLength -= length;
2610 + if (m_skipLength) {
2611 + uint32_t length = std::min(m_skipLength, (uint32_t)null_buffer_size);
2612 + length = read_stream_throws(m_nullBuffer, length);
2613 + if (!length)
2614 + return false;
2615 + m_skipLength -= length;
2618 + return !m_skipLength;
2621 +// Same as the PCB code, but only one at a time and with the extension protocol.
2622 +bool
2623 +PeerConnectionMetadata::try_request_metadata_pieces() {
2624 + if (m_download->file_list()->chunk_size() == 1 || !m_extensions->is_remote_supported(ProtocolExtension::UT_METADATA))
2625 + return false;
2627 + if (download_queue()->queued_empty())
2628 + m_downStall = 0;
2630 + uint32_t pipeSize = download_queue()->calculate_pipe_size(m_peerChunks.download_throttle()->rate()->rate());
2632 + // Don't start requesting if we can't do it in large enough chunks.
2633 + if (download_queue()->queued_size() >= (pipeSize + 10) / 2)
2634 + return false;
2636 + if (!download_queue()->queued_size() < pipeSize || !m_up->can_write_extension() ||
2637 + m_extensions->has_pending_message())
2638 + return false;
2640 + const Piece* p = download_queue()->delegate();
2642 + if (p == NULL)
2643 + return false;
2645 + if (!m_download->file_list()->is_valid_piece(*p) || !m_peerChunks.bitfield()->get(p->index()))
2646 + throw internal_error("PeerConnectionMetadata::try_request_metadata_pieces() tried to use an invalid piece.");
2648 + return m_extensions->request_metadata_piece(p);
2651 +void
2652 +PeerConnectionMetadata::receive_metadata_piece(uint32_t piece, const char* data, uint32_t length) {
2653 + if (data == NULL) {
2654 + // Length is not set in a reject message.
2655 + length = ProtocolExtension::metadata_piece_size;
2656 + if ((piece << ProtocolExtension::metadata_piece_shift) + ProtocolExtension::metadata_piece_size >= m_download->file_list()->size_bytes())
2657 + length = m_download->file_list()->chunk_size() % ProtocolExtension::metadata_piece_size;
2658 + m_tryRequest = false;
2659 + read_cancel_piece(Piece(0, piece << ProtocolExtension::metadata_piece_shift, length));
2660 + return;
2663 + if (!down_chunk_start(Piece(0, piece << ProtocolExtension::metadata_piece_shift, length)))
2664 + down_chunk_skip_process(data, length);
2665 + else
2666 + down_chunk_process(data, length);
2668 + if (!m_downloadQueue.transfer()->is_finished())
2669 + throw internal_error("PeerConnectionMetadata::receive_metadata_piece did not have complete piece.");
2671 + m_tryRequest = true;
2672 + down_chunk_finished();
2676 diff --git a/src/protocol/peer_connection_metadata.h b/src/protocol/peer_connection_metadata.h
2677 new file mode 100644
2678 index 0000000..127700a
2679 --- /dev/null
2680 +++ b/src/protocol/peer_connection_metadata.h
2681 @@ -0,0 +1,73 @@
2682 +// libTorrent - BitTorrent library
2683 +// Copyright (C) 2005-2007, Jari Sundell
2685 +// This program is free software; you can redistribute it and/or modify
2686 +// it under the terms of the GNU General Public License as published by
2687 +// the Free Software Foundation; either version 2 of the License, or
2688 +// (at your option) any later version.
2689 +//
2690 +// This program is distributed in the hope that it will be useful,
2691 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
2692 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2693 +// GNU General Public License for more details.
2694 +//
2695 +// You should have received a copy of the GNU General Public License
2696 +// along with this program; if not, write to the Free Software
2697 +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2699 +// In addition, as a special exception, the copyright holders give
2700 +// permission to link the code of portions of this program with the
2701 +// OpenSSL library under certain conditions as described in each
2702 +// individual source file, and distribute linked combinations
2703 +// including the two.
2705 +// You must obey the GNU General Public License in all respects for
2706 +// all of the code used other than OpenSSL. If you modify file(s)
2707 +// with this exception, you may extend this exception to your version
2708 +// of the file(s), but you are not obligated to do so. If you do not
2709 +// wish to do so, delete this exception statement from your version.
2710 +// If you delete this exception statement from all source files in the
2711 +// program, then also delete it here.
2713 +// Contact: Jari Sundell <jaris@ifi.uio.no>
2715 +// Skomakerveien 33
2716 +// 3185 Skoppum, NORWAY
2718 +#ifndef LIBTORRENT_PROTOCOL_PEER_CONNECTION_METADATA_H
2719 +#define LIBTORRENT_PROTOCOL_PEER_CONNECTION_METADATA_H
2721 +#include "peer_connection_base.h"
2723 +#include "torrent/download.h"
2725 +namespace torrent {
2727 +class PeerConnectionMetadata : public PeerConnectionBase {
2728 +public:
2729 + ~PeerConnectionMetadata();
2731 + virtual void initialize_custom();
2732 + virtual void update_interested();
2733 + virtual bool receive_keepalive();
2735 + virtual void event_read();
2736 + virtual void event_write();
2738 + virtual void receive_metadata_piece(uint32_t piece, const char* data, uint32_t length);
2740 +private:
2741 + inline bool read_message();
2743 + bool read_skip_bitfield();
2745 + bool try_request_metadata_pieces();
2747 + inline void fill_write_buffer();
2749 + uint32_t m_skipLength;
2754 +#endif
2755 diff --git a/src/protocol/peer_factory.cc b/src/protocol/peer_factory.cc
2756 index 7ab9fe8..cfe6a1e 100644
2757 --- a/src/protocol/peer_factory.cc
2758 +++ b/src/protocol/peer_factory.cc
2759 @@ -38,6 +38,7 @@
2761 #include "peer_factory.h"
2762 #include "peer_connection_leech.h"
2763 +#include "peer_connection_metadata.h"
2765 namespace torrent {
2767 @@ -62,4 +63,11 @@ createPeerConnectionInitialSeed(bool encrypted) {
2768 return pc;
2771 +PeerConnectionBase*
2772 +createPeerConnectionMetadata(bool encrypted) {
2773 + PeerConnectionBase* pc = new PeerConnectionMetadata;
2775 + return pc;
2779 diff --git a/src/protocol/peer_factory.h b/src/protocol/peer_factory.h
2780 index 363a5c3..f22d76f 100644
2781 --- a/src/protocol/peer_factory.h
2782 +++ b/src/protocol/peer_factory.h
2783 @@ -44,6 +44,7 @@ class PeerConnectionBase;
2784 PeerConnectionBase* createPeerConnectionDefault(bool encrypted);
2785 PeerConnectionBase* createPeerConnectionSeed(bool encrypted);
2786 PeerConnectionBase* createPeerConnectionInitialSeed(bool encrypted);
2787 +PeerConnectionBase* createPeerConnectionMetadata(bool encrypted);
2791 diff --git a/src/torrent/Makefile.am b/src/torrent/Makefile.am
2792 index bec124d..820ce52 100644
2793 --- a/src/torrent/Makefile.am
2794 +++ b/src/torrent/Makefile.am
2795 @@ -41,6 +41,9 @@ libsub_torrent_la_SOURCES = \
2796 rate.h \
2797 resume.cc \
2798 resume.h \
2799 + simple_string.h \
2800 + static_map.cc \
2801 + static_map.h \
2802 throttle.cc \
2803 throttle.h \
2804 torrent.cc \
2805 @@ -74,6 +77,8 @@ libtorrentinclude_HEADERS = \
2806 poll_select.h \
2807 rate.h \
2808 resume.h \
2809 + simple_string.h \
2810 + static_map.h \
2811 throttle.h \
2812 torrent.h \
2813 tracker.h \
2814 diff --git a/src/torrent/data/file_list.cc b/src/torrent/data/file_list.cc
2815 index 2f5d8d2..7208612 100644
2816 --- a/src/torrent/data/file_list.cc
2817 +++ b/src/torrent/data/file_list.cc
2818 @@ -466,6 +466,18 @@ FileList::open(int flags) {
2820 m_isOpen = true;
2821 m_frozenRootDir = m_rootDir;
2823 + // For meta-downloads, if the file exists, we have to assume that
2824 + // it is either 0 or 1 length or the correct size. If the size
2825 + // turns out wrong later, a communication_error will be thrown elsewhere
2826 + // to alert the user in this (unlikely) case.
2827 + if (size_bytes() < 2) {
2828 + rak::file_stat stat;
2830 + // This probably recurses into open() once, but that is harmless.
2831 + if (stat.update((*begin())->frozen_path()) && stat.size() > 1)
2832 + return reset_filesize(stat.size());
2836 void
2837 @@ -661,4 +673,14 @@ FileList::update_completed() {
2841 +void
2842 +FileList::reset_filesize(int64_t size) {
2843 + close();
2844 + m_chunkSize = size;
2845 + m_torrentSize = size;
2846 + (*begin())->set_size_bytes(size);
2847 + (*begin())->set_range(m_chunkSize);
2848 + open(open_no_create);
2852 diff --git a/src/torrent/data/file_list.h b/src/torrent/data/file_list.h
2853 index bcc8939..60d418a 100644
2854 --- a/src/torrent/data/file_list.h
2855 +++ b/src/torrent/data/file_list.h
2856 @@ -167,6 +167,10 @@ protected:
2857 iterator inc_completed(iterator firstItr, uint32_t index) LIBTORRENT_NO_EXPORT;
2858 void update_completed() LIBTORRENT_NO_EXPORT;
2860 + // Used for meta downloads; we only know the
2861 + // size after the first extension handshake.
2862 + void reset_filesize(int64_t) LIBTORRENT_NO_EXPORT;
2864 private:
2865 bool open_file(File* node, const Path& lastPath, int flags) LIBTORRENT_NO_EXPORT;
2866 void make_directory(Path::const_iterator pathBegin, Path::const_iterator pathEnd, Path::const_iterator startItr) LIBTORRENT_NO_EXPORT;
2867 diff --git a/src/torrent/download.cc b/src/torrent/download.cc
2868 index d6cc199..49daad9 100644
2869 --- a/src/torrent/download.cc
2870 +++ b/src/torrent/download.cc
2871 @@ -225,6 +225,11 @@ Download::set_pex_enabled(bool enabled) {
2872 m_ptr->info()->set_pex_enabled(enabled);
2875 +bool
2876 +Download::is_meta_download() const {
2877 + return m_ptr->info()->is_meta_download();
2880 const std::string&
2881 Download::name() const {
2882 if (m_ptr == NULL)
2883 @@ -504,6 +509,11 @@ Download::connection_type() const {
2885 void
2886 Download::set_connection_type(ConnectionType t) {
2887 + if (m_ptr->info()->is_meta_download()) {
2888 + m_ptr->main()->connection_list()->slot_new_connection(&createPeerConnectionMetadata);
2889 + return;
2892 switch (t) {
2893 case CONNECTION_LEECH:
2894 m_ptr->main()->connection_list()->slot_new_connection(&createPeerConnectionDefault);
2895 diff --git a/src/torrent/download.h b/src/torrent/download.h
2896 index 5e9700e..5d16d4e 100644
2897 --- a/src/torrent/download.h
2898 +++ b/src/torrent/download.h
2899 @@ -100,6 +100,8 @@ public:
2900 bool is_pex_enabled() const;
2901 void set_pex_enabled(bool enabled);
2903 + bool is_meta_download() const;
2905 // Returns "" if the object is not valid.
2906 const std::string& name() const;
2908 @@ -184,6 +186,7 @@ public:
2909 CONNECTION_LEECH,
2910 CONNECTION_SEED,
2911 CONNECTION_INITIAL_SEED,
2912 + CONNECTION_METADATA,
2913 } ConnectionType;
2915 ConnectionType connection_type() const;
2916 diff --git a/src/torrent/hash_string.h b/src/torrent/hash_string.h
2917 index f62d450..14623f7 100644
2918 --- a/src/torrent/hash_string.h
2919 +++ b/src/torrent/hash_string.h
2920 @@ -44,6 +44,7 @@
2921 #include <string>
2922 #include <iterator>
2923 #include <torrent/common.h>
2924 +#include <torrent/simple_string.h>
2926 namespace torrent {
2928 @@ -85,6 +86,8 @@ public:
2930 std::string str() const { return std::string(m_data, size_data); }
2932 + SimpleString s_str() const { return SimpleString(m_data, size_data); }
2934 void clear(int v = 0) { std::memset(data(), v, size()); }
2936 void assign(const value_type* src) { std::memcpy(data(), src, size()); }
2937 @@ -96,6 +99,7 @@ public:
2938 // size_data.
2939 static const HashString* cast_from(const char* src) { return (const HashString*)src; }
2940 static const HashString* cast_from(const std::string& src) { return (const HashString*)src.c_str(); }
2941 + static const HashString* cast_from(const SimpleString& src){ return (const HashString*)src.c_str(); }
2943 static HashString* cast_from(char* src) { return (HashString*)src; }
2945 diff --git a/src/torrent/object.cc b/src/torrent/object.cc
2946 index 2b1cf41..b609f9c 100644
2947 --- a/src/torrent/object.cc
2948 +++ b/src/torrent/object.cc
2949 @@ -44,47 +44,59 @@
2951 namespace torrent {
2953 -Object&
2954 -Object::get_key(const std::string& k) {
2955 - check_throw(TYPE_MAP);
2956 - map_type::iterator itr = m_map->find(k);
2957 +std::pair<Object::map_type::base_type::iterator, bool>
2958 +Object::map_type::insert(const value_type& value) {
2959 + base_type::iterator itr = lower_bound(value.first);
2961 - if (itr == m_map->end())
2962 - throw bencode_error("Object operator [" + k + "] could not find element");
2963 + if (itr != end() && !key_comp()(value.first, itr->first))
2964 + return std::make_pair(itr, false);
2966 - return itr->second;
2968 + // Insert with an allocated copy of the key.
2969 + itr = base_type::insert(itr, value_type(value.first.copy(), value.second));
2971 + // This means the value was actually already present.
2972 + if (itr->second.get_string() != NULL)
2973 + throw internal_error("Object::map_type::insert failed to insert value.");
2975 -const Object&
2976 -Object::get_key(const std::string& k) const {
2977 - check_throw(TYPE_MAP);
2978 - map_type::const_iterator itr = m_map->find(k);
2979 + // Make entry own the string and free it when erased.
2980 + itr->second.set_string(itr->first.c_str());
2982 - if (itr == m_map->end())
2983 - throw bencode_error("Object operator [" + k + "] could not find element");
2984 + return std::make_pair(itr, true);
2987 - return itr->second;
2988 +Object::map_type::base_type::iterator
2989 +Object::map_type::insert(base_type::iterator itr, const value_type& value) {
2990 + SimpleString copy = value.first.copy();
2991 + itr = base_type::insert(itr, value_type(copy, value.second));
2993 + // If the entry already owns its string, it wasn't really
2994 + // inserted and already existed, so discard the copy.
2995 + if (itr->second.get_string() != NULL)
2996 + delete [] copy.c_str();
2997 + else
2998 + itr->second.set_string(itr->first.c_str());
3000 + return itr;
3003 Object&
3004 -Object::get_key(const char* k) {
3005 +Object::get_key(const key_type& k) {
3006 check_throw(TYPE_MAP);
3007 - map_type::iterator itr = m_map->find(std::string(k));
3008 + map_type::iterator itr = m_map->find(k);
3010 if (itr == m_map->end())
3011 - throw bencode_error("Object operator [" + std::string(k) + "] could not find element");
3012 + throw bencode_error("Object operator [" + k.str() + "] could not find element");
3014 return itr->second;
3017 const Object&
3018 -Object::get_key(const char* k) const {
3019 +Object::get_key(const key_type& k) const {
3020 check_throw(TYPE_MAP);
3021 - map_type::iterator itr = m_map->find(std::string(k));
3022 + map_type::iterator itr = m_map->find(k);
3024 if (itr == m_map->end())
3025 - throw bencode_error("Object operator [" + std::string(k) + "] could not find element");
3026 + throw bencode_error("Object operator [" + k.str() + "] could not find element");
3028 return itr->second;
3030 @@ -143,7 +155,7 @@ Object::merge_copy(const Object& object, uint32_t maxDepth) {
3031 while (srcItr != srcLast) {
3032 destItr = std::find_if(destItr, dest.end(), rak::less_equal(srcItr->first, rak::mem_ref(&map_type::value_type::first)));
3034 - if (srcItr->first < destItr->first)
3035 + if (dest.key_comp()(srcItr->first, destItr->first))
3036 // destItr remains valid and pointing to the next possible
3037 // position.
3038 dest.insert(destItr, *srcItr);
3039 @@ -195,6 +207,7 @@ Object::operator = (const Object& src) {
3040 case TYPE_STRING: m_string = new string_type(*src.m_string); break;
3041 case TYPE_LIST: m_list = new list_type(*src.m_list); break;
3042 case TYPE_MAP: m_map = new map_type(*src.m_map); break;
3043 + case TYPE_SSTRING:m_sstring = src.m_sstring; break;
3046 return *this;
3047 diff --git a/src/torrent/object.h b/src/torrent/object.h
3048 index 7ad040b..b7b4e8f 100644
3049 --- a/src/torrent/object.h
3050 +++ b/src/torrent/object.h
3051 @@ -42,21 +42,56 @@
3052 #include <list>
3053 #include <torrent/common.h>
3054 #include <torrent/exceptions.h>
3055 +#include <torrent/simple_string.h>
3057 namespace torrent {
3059 -// TODO: Look into making a custom comp and allocator classes for the
3060 -// map_type which use a const char* for key_type.
3062 // TODO: Use placement new/delete in order to avoid the extra level of
3063 // indirection caused by the union.
3065 class LIBTORRENT_EXPORT Object {
3066 + template<typename T>
3067 + class string_wrapper : public T {
3068 + public:
3069 + string_wrapper() : T(), m_string(NULL) {}
3070 + string_wrapper(const T& value) : T(value), m_string(NULL) {}
3071 + string_wrapper(const string_wrapper& other) : T(other), m_string(NULL) {}
3073 + ~string_wrapper() { delete [] m_string; m_string = NULL; }
3075 + const char* get_string() const { return m_string; }
3076 + void set_string(const char* s) { m_string = s; }
3078 + private:
3079 + string_wrapper& operator = (const string_wrapper& other);
3081 + const char* m_string;
3082 + };
3084 public:
3085 typedef int64_t value_type;
3086 typedef std::string string_type;
3087 typedef std::list<Object> list_type;
3088 - typedef std::map<std::string, Object> map_type;
3089 + class map_type : public std::map<SimpleString, string_wrapper<Object> > {
3090 + public:
3091 + typedef std::map<SimpleString, string_wrapper<Object> > base_type;
3092 + using base_type::value_type;
3093 + using base_type::key_type;
3095 + map_type(const map_type& other) : base_type(other.key_comp()) { insert(other.begin(), other.end()); }
3096 + map_type() {}
3098 + std::pair<base_type::iterator, bool> insert(const value_type& value);
3099 + base_type::iterator insert(base_type::iterator itr, const value_type& value);
3101 + template<typename InputIterator>
3102 + void insert(InputIterator begin, InputIterator end);
3104 + Object& operator[] (key_type key);
3106 + private:
3107 + map_type& operator = (const map_type& other);
3108 + };
3109 typedef map_type::key_type key_type;
3111 typedef list_type::iterator list_iterator;
3112 @@ -82,13 +117,16 @@ public:
3113 TYPE_VALUE,
3114 TYPE_STRING,
3115 TYPE_LIST,
3116 - TYPE_MAP
3117 + TYPE_MAP,
3118 + TYPE_SSTRING, // Only used in StaticMap.
3121 Object() : m_flags(TYPE_NONE) {}
3122 Object(const value_type v) : m_flags(TYPE_VALUE), m_value(v) {}
3123 Object(const char* s) : m_flags(TYPE_STRING), m_string(new string_type(s)) {}
3124 Object(const string_type& s) : m_flags(TYPE_STRING), m_string(new string_type(s)) {}
3125 + Object(const char* s, size_t l) : m_flags(TYPE_SSTRING), m_sstring(SimpleString(s, l)) {}
3126 + Object(SimpleString s) : m_flags(TYPE_SSTRING), m_sstring(s) {}
3127 Object(const Object& b);
3129 ~Object() { clear(); }
3130 @@ -96,6 +134,7 @@ public:
3131 // Move this out of the class namespace, call them create_object_.
3132 static Object create_value() { return Object(value_type()); }
3133 static Object create_string() { return Object(string_type()); }
3134 + static Object create_sstring(){ return Object(SimpleString()); }
3135 static Object create_list() { Object tmp; tmp.m_flags = TYPE_LIST; tmp.m_list = new list_type(); return tmp; }
3136 static Object create_map() { Object tmp; tmp.m_flags = TYPE_MAP; tmp.m_map = new map_type(); return tmp; }
3138 @@ -120,6 +159,7 @@ public:
3139 bool is_string() const { return type() == TYPE_STRING; }
3140 bool is_list() const { return type() == TYPE_LIST; }
3141 bool is_map() const { return type() == TYPE_MAP; }
3142 + bool is_sstring() const { return type() == TYPE_SSTRING; }
3144 value_type& as_value() { check_throw(TYPE_VALUE); return m_value; }
3145 const value_type& as_value() const { check_throw(TYPE_VALUE); return m_value; }
3146 @@ -133,6 +173,9 @@ public:
3147 map_type& as_map() { check_throw(TYPE_MAP); return *m_map; }
3148 const map_type& as_map() const { check_throw(TYPE_MAP); return *m_map; }
3150 + SimpleStringBase& as_sstring() { check_throw(TYPE_SSTRING); return m_sstring; }
3151 + SimpleString as_sstring() const { check_throw(TYPE_SSTRING); return m_sstring; }
3153 bool has_key(const key_type& k) const { check_throw(TYPE_MAP); return m_map->find(k) != m_map->end(); }
3154 bool has_key_value(const key_type& k) const { check_throw(TYPE_MAP); return check(m_map->find(k), TYPE_VALUE); }
3155 bool has_key_string(const key_type& k) const { check_throw(TYPE_MAP); return check(m_map->find(k), TYPE_STRING); }
3156 @@ -144,8 +187,6 @@ public:
3158 Object& get_key(const key_type& k);
3159 const Object& get_key(const key_type& k) const;
3160 - Object& get_key(const char* k);
3161 - const Object& get_key(const char* k) const;
3163 template <typename T> value_type& get_key_value(const T& k) { return get_key(k).as_value(); }
3164 template <typename T> const value_type& get_key_value(const T& k) const { return get_key(k).as_value(); }
3165 @@ -200,9 +241,31 @@ public:
3166 string_type* m_string;
3167 list_type* m_list;
3168 map_type* m_map;
3169 + SimpleStringBase m_sstring;
3173 +// We need to call our own insert function, so
3174 +// we have to define this operator to use that.
3175 +inline Object&
3176 +Object::map_type::operator[] (key_type key) {
3177 + base_type::iterator itr = lower_bound(key);
3179 + if (itr == end() || key_comp()(key, itr->first))
3180 + itr = insert(itr, value_type(key, mapped_type()));
3182 + return itr->second;
3185 +template<typename InputIterator>
3186 +inline void
3187 +Object::map_type::insert(InputIterator itr, InputIterator itrEnd) {
3188 + while (itr != itrEnd) {
3189 + insert(end(), *itr);
3190 + ++itr;
3194 inline
3195 Object::Object(const Object& b) : m_flags(b.type()) {
3196 switch (type()) {
3197 @@ -211,6 +274,7 @@ Object::Object(const Object& b) : m_flags(b.type()) {
3198 case TYPE_STRING: m_string = new string_type(*b.m_string); break;
3199 case TYPE_LIST: m_list = new list_type(*b.m_list); break;
3200 case TYPE_MAP: m_map = new map_type(*b.m_map); break;
3201 + case TYPE_SSTRING:m_sstring = b.m_sstring; break;
3205 @@ -222,6 +286,7 @@ Object::clear() {
3206 case TYPE_STRING: delete m_string; break;
3207 case TYPE_LIST: delete m_list; break;
3208 case TYPE_MAP: delete m_map; break;
3209 + case TYPE_SSTRING:break;
3212 // Only clear type?
3213 diff --git a/src/torrent/object_stream.cc b/src/torrent/object_stream.cc
3214 index 18eb849..9d9a962 100644
3215 --- a/src/torrent/object_stream.cc
3216 +++ b/src/torrent/object_stream.cc
3217 @@ -38,12 +38,14 @@
3219 #include <iterator>
3220 #include <iostream>
3221 +#include <sstream>
3222 #include <rak/functional.h>
3224 #include "utils/sha1.h"
3226 #include "object.h"
3227 #include "object_stream.h"
3228 +#include "static_map.h"
3230 namespace torrent {
3232 @@ -63,6 +65,18 @@ object_read_string(std::istream* input, std::string& str) {
3233 return !input->fail();
3236 +Object
3237 +object_get_sstring(const char** buffer) {
3238 + /*const*/ char* next;
3239 + size_t length = strtoumax(*buffer, &next, 10);
3241 + if (next == *buffer || *next != ':')
3242 + return Object();
3244 + *buffer = next + 1 + length;
3245 + return Object(next + 1, length);
3248 // Could consider making this non-recursive, but they seldomly are
3249 // deep enough to make that worth-while.
3250 void
3251 @@ -159,6 +173,133 @@ object_read_bencode(std::istream* input, Object* object, uint32_t depth) {
3252 object->clear();
3255 +const char*
3256 +staticMap_read_bencode_c(const char* buffer, const char* bufferEnd, uint32_t depth, Object* values, const StaticMapKeys& keys, bool discard) {
3257 + if (buffer >= bufferEnd)
3258 + return bufferEnd;
3260 + // Undecoded bencode object.
3261 + if (!discard && keys.type() == StaticMapKeys::TYPE_BENCODE) {
3262 + const char* begin = buffer;
3263 + buffer = staticMap_read_bencode_c(buffer, bufferEnd, ++depth, values, keys, true);
3264 + values[keys.index_begin()] = SimpleString(begin, buffer - begin);
3265 + return buffer;
3268 + if (!discard && keys.type() == StaticMapKeys::TYPE_BENCODE_LIST && *buffer != 'l')
3269 + discard = true;
3271 + switch (*buffer) {
3272 + case 'i': {
3273 + char* next;
3274 + intmax_t value = strtoimax(++buffer, &next, 10);
3276 + if (next == buffer || next > bufferEnd || *next != 'e')
3277 + break;
3279 + if (!discard && keys.type() == StaticMapKeys::TYPE_VALUE)
3280 + values[keys.index_begin()] = (int64_t)value;
3282 + return next + 1;
3285 + case 'l': {
3286 + ++buffer;
3287 + if (++depth >= 1024)
3288 + break;
3290 + // Want undecoded bencode list: find end of list.
3291 + if (!discard && keys.type() == StaticMapKeys::TYPE_BENCODE_LIST) {
3292 + const char* end = buffer;
3293 + while (end < bufferEnd && *end != 'e')
3294 + end = staticMap_read_bencode_c(end, bufferEnd, depth, values, keys, true);
3296 + values[keys.index_begin()] = SimpleString(buffer, end - buffer);
3297 + return ++end;
3300 + StaticMapKeys::const_iterator itr = keys.begin();
3301 + while (buffer != bufferEnd) {
3302 + if (*buffer == 'e')
3303 + return ++buffer;
3305 + discard |= itr == keys.end();
3306 + buffer = staticMap_read_bencode_c(buffer, bufferEnd, depth, values, discard ? keys : *itr, discard);
3308 + if (itr != keys.end())
3309 + ++itr;
3312 + break;
3315 + case 'd': {
3316 + ++buffer;
3317 + if (++depth >= 1024)
3318 + break;
3320 + StaticMapKeys::const_iterator itr = keys.begin();
3321 + SimpleString last;
3322 + bool discardThis = discard;
3324 + while (buffer != bufferEnd) {
3325 + if (*buffer == 'e')
3326 + return ++buffer;
3328 + Object keyObj = object_get_sstring(&buffer);
3329 + if (!keyObj.is_sstring())
3330 + break;
3332 + SimpleString key = keyObj.as_sstring();
3333 + if (key.end() >= bufferEnd)
3334 + break;
3336 + if (key < last) {
3337 + itr = keys.begin();
3338 + discardThis = discard;
3341 + discardThis |= itr == keys.end();
3342 + int cmp = discardThis ? -1 : key.cmp(itr->key());
3343 + while (cmp > 0) {
3344 + if (++itr == keys.end()) {
3345 + cmp = -1;
3346 + discardThis = true;
3347 + break;
3350 + cmp = key.cmp(itr->key());
3353 + buffer = staticMap_read_bencode_c(buffer, bufferEnd, depth, values, cmp ? keys : *itr, cmp);
3355 + last = key;
3358 + break;
3361 + default:
3362 + if (*buffer < '0' || *buffer > '9')
3363 + break;
3365 + Object strObj = object_get_sstring(&buffer);
3366 + if (!strObj.is_sstring())
3367 + break;
3369 + SimpleString str = strObj.as_sstring();
3370 + if (str.end() >= bufferEnd)
3371 + break;
3373 + if (!discard && keys.type() == StaticMapKeys::TYPE_VALUE)
3374 + values[keys.index_begin()] = str;
3376 + return str.end();
3379 + throw bencode_error("Invalid bencode data.");
3382 void
3383 object_write_bencode(std::ostream* output, const Object* object) {
3384 char buffer[1024];
3385 @@ -267,6 +408,7 @@ void
3386 object_write_bencode_c_object(object_write_data_t* output, const Object* object) {
3387 switch (object->type()) {
3388 case Object::TYPE_NONE:
3389 + case Object::TYPE_SSTRING:
3390 break;
3392 case Object::TYPE_VALUE:
3393 @@ -306,6 +448,86 @@ object_write_bencode_c_object(object_write_data_t* output, const Object* object)
3397 +void
3398 +staticMap_write_bencode_c_values(object_write_data_t* output, const Object* values, const StaticMapKeys& keys) {
3399 + if (keys.type() == StaticMapKeys::TYPE_LIST) {
3400 + size_t indexEnd = keys.index_begin();
3401 + while (indexEnd < keys.index_end() && values[indexEnd].type() != Object::TYPE_NONE)
3402 + indexEnd++;
3404 + // Empty list? Drop it. Sparse lists are not possible so only check first element.
3405 + if (indexEnd == keys.index_begin())
3406 + return;
3408 + object_write_bencode_c_char(output, 'l');
3409 + StaticMapKeys::const_iterator itr = keys.begin();
3410 + size_t index = keys.index_begin();
3411 + while (index < indexEnd) {
3412 + staticMap_write_bencode_c_values(output, values, *itr);
3413 + index = itr->index_end();
3414 + if (++itr == keys.end() && index != indexEnd)
3415 + throw internal_error("staticMap_write_bencode_c_values reached end of list before end of index list.");
3417 + object_write_bencode_c_char(output, 'e');
3419 + } else if (keys.type() == StaticMapKeys::TYPE_DICT) {
3420 + // Find next non-empty entry.
3421 + size_t next = keys.index_begin();
3422 + while (values[next].type() == Object::TYPE_NONE)
3423 + if (++next == keys.index_end())
3424 + return;
3426 + object_write_bencode_c_char(output, 'd');
3427 + StaticMapKeys::const_iterator itr = keys.begin();
3428 + while (next < keys.index_end()) {
3429 + while (itr->index_end() <= next)
3430 + if (++itr == keys.end())
3431 + throw internal_error("staticMap_write_bencode_c_values reached end of keys before end of index list.");
3433 + object_write_bencode_c_value(output, itr->key().size());
3434 + object_write_bencode_c_char(output, ':');
3435 + object_write_bencode_c_string(output, itr->key().c_str(), itr->key().size());
3437 + staticMap_write_bencode_c_values(output, values, *itr);
3439 + next = itr->index_end();
3440 + while (next < keys.index_end() && values[next].type() == Object::TYPE_NONE)
3441 + ++next;
3443 + object_write_bencode_c_char(output, 'e');
3445 + // Undecoded bencode value.
3446 + } else if (keys.type() == StaticMapKeys::TYPE_BENCODE) {
3447 + SimpleString value = values[keys.index_begin()].as_sstring();
3448 + object_write_bencode_c_string(output, value.c_str(), value.size());
3450 + } else if (keys.type() == StaticMapKeys::TYPE_BENCODE_LIST) {
3451 + SimpleString value = values[keys.index_begin()].as_sstring();
3452 + object_write_bencode_c_char(output, 'l');
3453 + object_write_bencode_c_string(output, value.c_str(), value.size());
3454 + object_write_bencode_c_char(output, 'e');
3456 + } else if (keys.type() != StaticMapKeys::TYPE_VALUE) {
3457 + throw internal_error("staticMap_write_bencode_c_values received key keys with invalid values type.");
3459 + } else if (values[keys.index_begin()].type() == Object::TYPE_NONE) {
3461 + } else if (values[keys.index_begin()].type() == Object::TYPE_VALUE) {
3462 + object_write_bencode_c_char(output, 'i');
3463 + object_write_bencode_c_value(output, values[keys.index_begin()].as_value());
3464 + object_write_bencode_c_char(output, 'e');
3466 + } else if (values[keys.index_begin()].type() == Object::TYPE_SSTRING) {
3467 + SimpleString value = values[keys.index_begin()].as_sstring();
3468 + object_write_bencode_c_value(output, value.size());
3469 + object_write_bencode_c_char(output, ':');
3470 + object_write_bencode_c_string(output, value.c_str(), value.size());
3472 + } else {
3473 + throw internal_error("staticMap_write_bencode_c_values received key keys with invalid values type.");
3477 object_buffer_t
3478 object_write_bencode_c(object_write_t writeFunc, void* data, object_buffer_t buffer, const Object* object) {
3479 object_write_data_t output;
3480 @@ -327,6 +549,32 @@ object_write_bencode_c(object_write_t writeFunc, void* data, object_buffer_t buf
3483 object_buffer_t
3484 +staticMap_write_bencode_c_wrap(object_write_t writeFunc, void* data, object_buffer_t buffer, const Object* values, const StaticMapKeys& map) {
3485 + object_write_data_t output;
3486 + output.writeFunc = writeFunc;
3487 + output.data = data;
3488 + output.buffer = buffer;
3489 + output.pos = buffer.first;
3491 + staticMap_write_bencode_c_values(&output, values, map);
3492 +#ifdef USE_EXTRA_DEBUG
3493 + std::istringstream sstream;
3494 + sstream.imbue(std::locale::classic());
3495 + sstream.str(std::string(output.buffer.first, output.pos));
3496 + Object request;
3497 + sstream >> request;
3498 + if (sstream.fail())
3499 + throw internal_error("staticMap_write_bencode_c_wrap failed to create valid bencode format.");
3500 +#endif
3502 + // Don't flush the buffer.
3503 + if (output.pos == output.buffer.first)
3504 + return output.buffer;
3506 + return output.writeFunc(output.data, object_buffer_t(output.buffer.first, output.pos));
3509 +object_buffer_t
3510 object_write_to_buffer(void* data, object_buffer_t buffer) {
3511 if (buffer.first == buffer.second)
3512 throw internal_error("object_write_to_buffer(...) buffer overflow.");
3513 @@ -352,4 +600,11 @@ object_write_to_stream(void* data, object_buffer_t buffer) {
3514 return buffer;
3517 +object_buffer_t
3518 +object_write_to_size(void* data, object_buffer_t buffer) {
3519 + *reinterpret_cast<uint64_t*>(data) += std::distance(buffer.first, buffer.second);
3521 + return buffer;
3525 diff --git a/src/torrent/object_stream.h b/src/torrent/object_stream.h
3526 index 41cf82a..3de5d82 100644
3527 --- a/src/torrent/object_stream.h
3528 +++ b/src/torrent/object_stream.h
3529 @@ -43,6 +43,10 @@
3531 namespace torrent {
3533 +template<typename tmpl_key_type, size_t tmpl_length>
3534 +class StaticMap;
3535 +class StaticMapKeys;
3537 std::string object_sha1(const Object* object) LIBTORRENT_EXPORT;
3539 // Assumes the stream's locale has been set to POSIX or C. Max depth
3540 @@ -53,6 +57,18 @@ void object_read_bencode(std::istream* input, Object* object, uint32_t depth = 0
3541 // Assumes the stream's locale has been set to POSIX or C.
3542 void object_write_bencode(std::ostream* output, const Object* object) LIBTORRENT_EXPORT;
3544 +// Convert buffer to static key map. Inlined because we don't want
3545 +// a separate wrapper function for each template argument.
3546 +template<typename tmpl_key_type, size_t tmpl_length>
3547 +inline const char*
3548 +staticMap_read_bencode(const char* buffer, const char* bufferEnd, StaticMap<tmpl_key_type, tmpl_length>& map) {
3549 + return staticMap_read_bencode_c(buffer, bufferEnd, 0, map.values(), map.map(), false);
3552 +// Internal use only.
3553 +const char*
3554 +staticMap_read_bencode_c(const char* buffer, const char* bufferEnd, uint32_t depth, Object* values, const StaticMapKeys& keys, bool discard);
3556 std::istream& operator >> (std::istream& input, Object& object) LIBTORRENT_EXPORT;
3557 std::ostream& operator << (std::ostream& output, const Object& object) LIBTORRENT_EXPORT;
3559 @@ -62,10 +78,22 @@ typedef object_buffer_t (*object_write_t)(void* data, object_buffer_t buffer);
3561 object_buffer_t object_write_bencode_c(object_write_t writeFunc, void* data, object_buffer_t buffer, const Object* object) LIBTORRENT_EXPORT;
3563 +template<typename tmpl_key_type, size_t tmpl_length>
3564 +inline object_buffer_t
3565 +staticMap_write_bencode_c(object_write_t writeFunc, void* data, object_buffer_t buffer, const StaticMap<tmpl_key_type, tmpl_length>& object) {
3566 + return staticMap_write_bencode_c_wrap(writeFunc, data, buffer, object.values(), object.map());
3569 +// Internal use only.
3570 +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;
3572 // To char buffer. 'data' is NULL.
3573 object_buffer_t object_write_to_buffer(void* data, object_buffer_t buffer) LIBTORRENT_EXPORT;
3574 object_buffer_t object_write_to_sha1(void* data, object_buffer_t buffer) LIBTORRENT_EXPORT;
3575 object_buffer_t object_write_to_stream(void* data, object_buffer_t buffer) LIBTORRENT_EXPORT;
3577 +// Measures bencode size, 'data' is uint64_t*.
3578 +object_buffer_t object_write_to_size(void* data, object_buffer_t buffer) LIBTORRENT_EXPORT;
3581 #endif
3582 diff --git a/src/torrent/simple_string.h b/src/torrent/simple_string.h
3583 new file mode 100644
3584 index 0000000..8eaf3b7
3585 --- /dev/null
3586 +++ b/src/torrent/simple_string.h
3587 @@ -0,0 +1,129 @@
3588 +// libTorrent - BitTorrent library
3589 +// Copyright (C) 2005-2008, Jari Sundell
3591 +// This program is free software; you can redistribute it and/or modify
3592 +// it under the terms of the GNU General Public License as published by
3593 +// the Free Software Foundation; either version 2 of the License, or
3594 +// (at your option) any later version.
3595 +//
3596 +// This program is distributed in the hope that it will be useful,
3597 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
3598 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3599 +// GNU General Public License for more details.
3600 +//
3601 +// You should have received a copy of the GNU General Public License
3602 +// along with this program; if not, write to the Free Software
3603 +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
3605 +// In addition, as a special exception, the copyright holders give
3606 +// permission to link the code of portions of this program with the
3607 +// OpenSSL library under certain conditions as described in each
3608 +// individual source file, and distribute linked combinations
3609 +// including the two.
3611 +// You must obey the GNU General Public License in all respects for
3612 +// all of the code used other than OpenSSL. If you modify file(s)
3613 +// with this exception, you may extend this exception to your version
3614 +// of the file(s), but you are not obligated to do so. If you do not
3615 +// wish to do so, delete this exception statement from your version.
3616 +// If you delete this exception statement from all source files in the
3617 +// program, then also delete it here.
3619 +// Contact: Jari Sundell <jaris@ifi.uio.no>
3621 +// Skomakerveien 33
3622 +// 3185 Skoppum, NORWAY
3624 +// A simple string with no constructors (i.e. POD) plus a derived
3625 +// class with constructors and conversion operators. In most cases,
3626 +// SimpleString is the class to use, except in unions where it needs
3627 +// to be SimpleStringBase.
3629 +// For efficient conversion from C string literals, depends on the
3630 +// compiler optimizing strlen("literal") to an integer literal.
3631 +// Then a comparison with either a C string literal or a SimpleString
3632 +// literal is a memcmp call plus (if equal) a comparison of the lengths.
3634 +#ifndef LIBTORRENT_SIMPLE_STRING_H
3635 +#define LIBTORRENT_SIMPLE_STRING_H
3637 +#include <cstring>
3638 +#include <memory>
3639 +#include <string>
3640 +#include <torrent/common.h>
3642 +namespace torrent {
3644 +// Simple string base class (POD).
3645 +struct LIBTORRENT_EXPORT SimpleStringBase {
3646 + int cmp(const SimpleStringBase& other) const;
3648 + char operator [] (size_t index) const { return m_data[index]; }
3650 + const char* begin() const { return m_data; }
3651 + const char* end() const { return m_data + m_length; }
3653 + // NOTE: Unlike std::string, SimpleString's c_str() is NOT guaranteed to be zero-terminated!
3654 + const char* c_str() const { return m_data; }
3655 + const char* data() const { return m_data; }
3657 + bool empty() const { return !m_length; }
3658 + size_t length() const { return m_length; }
3659 + size_t size() const { return m_length; }
3661 + std::string str() const { return std::string(m_data, m_length); }
3662 + std::string substr(size_t pos = 0, size_t n = npos) const { return std::string(m_data + pos, std::min(m_length - pos, n)); }
3664 + // Allocates a copy of the string and returns it.
3665 + SimpleStringBase copy() const;
3667 + static const size_t npos = static_cast<size_t>(-1);
3669 +protected:
3670 + const char* m_data;
3671 + size_t m_length;
3674 +// Conversion helper class, we don't want constructors
3675 +// in the base class to be able to put it in a union.
3676 +struct LIBTORRENT_EXPORT SimpleString : public SimpleStringBase {
3677 + typedef SimpleStringBase base_type;
3679 + SimpleString() { m_data = ""; m_length = 0; }
3680 + SimpleString(const base_type& s) { m_data = s.c_str(); m_length = s.length(); }
3681 + SimpleString(const std::string& s) { m_data = s.c_str(); m_length = s.length(); }
3682 + SimpleString(const char* s) { m_data = s; m_length = strlen(s); }
3683 + SimpleString(const char* s, size_t l) { m_data = s; m_length = l; }
3686 +inline int
3687 +SimpleStringBase::cmp(const SimpleStringBase& other) const {
3688 + int cmp = memcmp(m_data, other.m_data, std::min(m_length, other.m_length));
3689 + return cmp ? cmp : m_length - other.m_length;
3692 +inline SimpleStringBase
3693 +SimpleStringBase::copy() const {
3694 + char* data = new char[m_length + 1];
3695 + memcpy(data, m_data, m_length);
3696 + data[m_length] = 0;
3697 + return SimpleString(data, m_length);
3700 +inline bool operator == (const SimpleStringBase& one, const SimpleStringBase& other) { return one.cmp(other) == 0; }
3701 +inline bool operator != (const SimpleStringBase& one, const SimpleStringBase& other) { return one.cmp(other) != 0; }
3702 +inline bool operator <= (const SimpleStringBase& one, const SimpleStringBase& other) { return one.cmp(other) <= 0; }
3703 +inline bool operator < (const SimpleStringBase& one, const SimpleStringBase& other) { return one.cmp(other) < 0; }
3704 +inline bool operator >= (const SimpleStringBase& one, const SimpleStringBase& other) { return one.cmp(other) >= 0; }
3705 +inline bool operator > (const SimpleStringBase& one, const SimpleStringBase& other) { return one.cmp(other) > 0; }
3707 +inline bool operator == (const SimpleStringBase& one, const char* other) { return one.cmp(SimpleString(other)) == 0; }
3708 +inline bool operator != (const SimpleStringBase& one, const char* other) { return one.cmp(SimpleString(other)) != 0; }
3709 +inline bool operator <= (const SimpleStringBase& one, const char* other) { return one.cmp(SimpleString(other)) <= 0; }
3710 +inline bool operator < (const SimpleStringBase& one, const char* other) { return one.cmp(SimpleString(other)) < 0; }
3711 +inline bool operator >= (const SimpleStringBase& one, const char* other) { return one.cmp(SimpleString(other)) >= 0; }
3712 +inline bool operator > (const SimpleStringBase& one, const char* other) { return one.cmp(SimpleString(other)) > 0; }
3716 +#endif
3717 diff --git a/src/torrent/static_map.cc b/src/torrent/static_map.cc
3718 new file mode 100644
3719 index 0000000..b71f257
3720 --- /dev/null
3721 +++ b/src/torrent/static_map.cc
3722 @@ -0,0 +1,123 @@
3723 +// libTorrent - BitTorrent library
3724 +// Copyright (C) 2005-2008, Jari Sundell
3726 +// This program is free software; you can redistribute it and/or modify
3727 +// it under the terms of the GNU General Public License as published by
3728 +// the Free Software Foundation; either version 2 of the License, or
3729 +// (at your option) any later version.
3730 +//
3731 +// This program is distributed in the hope that it will be useful,
3732 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
3733 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3734 +// GNU General Public License for more details.
3735 +//
3736 +// You should have received a copy of the GNU General Public License
3737 +// along with this program; if not, write to the Free Software
3738 +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
3740 +// In addition, as a special exception, the copyright holders give
3741 +// permission to link the code of portions of this program with the
3742 +// OpenSSL library under certain conditions as described in each
3743 +// individual source file, and distribute linked combinations
3744 +// including the two.
3746 +// You must obey the GNU General Public License in all respects for
3747 +// all of the code used other than OpenSSL. If you modify file(s)
3748 +// with this exception, you may extend this exception to your version
3749 +// of the file(s), but you are not obligated to do so. If you do not
3750 +// wish to do so, delete this exception statement from your version.
3751 +// If you delete this exception statement from all source files in the
3752 +// program, then also delete it here.
3754 +// Contact: Jari Sundell <jaris@ifi.uio.no>
3756 +// Skomakerveien 33
3757 +// 3185 Skoppum, NORWAY
3759 +#include "config.h"
3761 +#include "static_map.h"
3763 +namespace torrent {
3765 +inline int
3766 +StaticMapKeys::check_key_order(SimpleString key) {
3767 + int cmp = empty() ? -1 : back().key().cmp(key);
3768 + if (cmp > 0) {
3769 + if (type() == TYPE_LIST)
3770 + cmp = -1; // List order is given by indices, not alphabetically.
3771 + else
3772 + throw internal_error("StaticMapKeys::StaticMapKeys() called with unsorted keys.");
3775 + return cmp;
3778 +StaticMapKeys::StaticMapKeys(const mapping_type* key_list, size_t length)
3779 + : m_key(SimpleString("root", 4)),
3780 + m_indexBegin(0),
3781 + m_indexEnd(0),
3782 + m_type(key_list[0].key[0] == '[' ? TYPE_LIST : TYPE_DICT) {
3784 + for (size_t index = 0; index < length; index++, key_list++) {
3785 + if (key_list->index != index)
3786 + throw internal_error("StaticMapKeys::StaticMapKeys() used with list not in index order.");
3788 + StaticMapKeys* curMap = this;
3789 + const char* key = key_list->key;
3790 + while (key != NULL && *key) {
3791 + curMap->set_end(index + 1);
3793 + const char* sep = key + 1 + strcspn(key + 1, ":[");
3794 + SimpleString keyStr(key, sep - key);
3796 + // New key, in correct order? Or same key as before?
3797 + int cmp = curMap->check_key_order(keyStr);
3799 + if (sep[0] == 0) {
3800 + curMap->insert(curMap->end(), StaticMapKeys(keyStr, TYPE_VALUE, index, index + 1));
3801 + break;
3803 + } else if (sep[0] == '[' && sep[1] == ']' && sep[2] == 0) {
3804 + curMap->insert(curMap->end(), StaticMapKeys(keyStr, TYPE_BENCODE_LIST, index, index + 1));
3805 + break;
3807 + } else if (sep[0] == ':' && sep[1] == ':' && sep[2] == 0) {
3808 + curMap->insert(curMap->end(), StaticMapKeys(keyStr, TYPE_BENCODE, index, index + 1));
3809 + break;
3812 + if (sep[0] == ':' && sep[1] == ':') {
3813 + if (cmp < 0)
3814 + curMap->insert(curMap->end(), StaticMapKeys(keyStr, TYPE_DICT, index, index + 1));
3815 + else if (curMap->back().type() != TYPE_DICT)
3816 + throw internal_error("StaticMapKeys::StaticMapKeys() called with a mixed dictionary/list entry.");
3818 + curMap = &curMap->back();
3819 + key = sep + 2;
3821 + } else if (sep[0] == '[' && sep[1] >= '0' && sep[1] <= '9') {
3822 + key = sep++;
3823 + while (*sep >= '0' && *sep <= '9')
3824 + ++sep;
3825 + if (*sep != ']')
3826 + throw internal_error("StaticMapKeys::StaticMapKeys() called with invalid list index.");
3828 + if (cmp < 0)
3829 + curMap->insert(curMap->end(), StaticMapKeys(keyStr, TYPE_LIST, index, index + 1));
3830 + else if (curMap->back().type() != TYPE_LIST)
3831 + throw internal_error("StaticMapKeys::StaticMapKeys() called with a mixed dictionary/list entry.");
3833 + curMap = &curMap->back();
3835 + } else {
3836 + throw internal_error("StaticMapKeys::StaticMapKeys() called with unsupported key type.");
3841 + if (index_end() != length)
3842 + throw internal_error("StaticMapKeys::StaticMapKeys() is missing values.");
3846 diff --git a/src/torrent/static_map.h b/src/torrent/static_map.h
3847 new file mode 100644
3848 index 0000000..d862f16
3849 --- /dev/null
3850 +++ b/src/torrent/static_map.h
3851 @@ -0,0 +1,158 @@
3852 +// libTorrent - BitTorrent library
3853 +// Copyright (C) 2005-2008, Jari Sundell
3855 +// This program is free software; you can redistribute it and/or modify
3856 +// it under the terms of the GNU General Public License as published by
3857 +// the Free Software Foundation; either version 2 of the License, or
3858 +// (at your option) any later version.
3859 +//
3860 +// This program is distributed in the hope that it will be useful,
3861 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
3862 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3863 +// GNU General Public License for more details.
3864 +//
3865 +// You should have received a copy of the GNU General Public License
3866 +// along with this program; if not, write to the Free Software
3867 +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
3869 +// In addition, as a special exception, the copyright holders give
3870 +// permission to link the code of portions of this program with the
3871 +// OpenSSL library under certain conditions as described in each
3872 +// individual source file, and distribute linked combinations
3873 +// including the two.
3875 +// You must obey the GNU General Public License in all respects for
3876 +// all of the code used other than OpenSSL. If you modify file(s)
3877 +// with this exception, you may extend this exception to your version
3878 +// of the file(s), but you are not obligated to do so. If you do not
3879 +// wish to do so, delete this exception statement from your version.
3880 +// If you delete this exception statement from all source files in the
3881 +// program, then also delete it here.
3883 +// Contact: Jari Sundell <jaris@ifi.uio.no>
3885 +// Skomakerveien 33
3886 +// 3185 Skoppum, NORWAY
3888 +#ifndef LIBTORRENT_STATIC_MAP_H
3889 +#define LIBTORRENT_STATIC_MAP_H
3891 +#include <vector>
3892 +#include <torrent/common.h>
3893 +#include <torrent/object.h>
3894 +#include <torrent/simple_string.h>
3896 +// StaticMap: holds a pre-defined subset of possible bencode keys and stores
3897 +// their values in a flat array for fast decoding, key access and encoding.
3898 +// Makes no copies, so the underlying data buffer must outlive the map object.
3900 +// With this, the complexity for bencoding and bdecoding a StaticMap object
3901 +// is O(n). The access to any of the pre-defined keys is O(1). Access to
3902 +// other keys is not supported, they are dropped while bdecoding. Decoded
3903 +// Object types are either VALUE, SSTRING or NONE (if key was not present).
3905 +// To use, define an enum of all required keys, and use this type along with
3906 +// the number of possible keys in the StaticMap template arguments. Define
3907 +// the enum -> key string as array of StaticMapKeys::mapping_type. Define
3908 +// the static keyMap variable, most simply by defining base_type in your
3909 +// derived map class, like this:
3910 +// template<> const Derived::key_map_init Derived::base_type::keyMap(key_list);
3912 +// The argument of the constructor of this static keyMap object is a list
3913 +// of mapping_type entries. For efficiency, they must be ordered in
3914 +// increasing number of the index, and increasing alphabetical order
3915 +// (or more specifically, the bencode order) at the same time. In other words,
3916 +// the original enum must also be in alphabetical order of the keys the enum
3917 +// values refer to.
3919 +// Format of the key specifications ("..." may contain any number of further keys):
3920 +// "foo::..." makes foo a bencode dictionary
3921 +// "foo[0]..." makes foo a bencode list
3922 +// "foo::" makes foo an undecoded bencode value (may contain arbitrary bencode data)
3923 +// "foo[]" makes foo an undecoded list of bencode values (like the above but adding the 'l' and 'e' indicators)
3924 +// "foo" makes foo an integer or string value (automatic)
3926 +// Examples:
3927 +// "baz" refers to a single value for key "baz"
3928 +// "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"
3929 +// "foo::a[1]" refers to a single value at index 1 of the list for key "a" in the dictionary "foo"
3930 +// "zoo::" refers to a bdecoded value for key "zoo"
3932 +// If the four values are 4, 5, "6" and 7, this would be bencoded as d3:bazi4e3:food1:ald3:bari5ee1:6ee3:zooi7ee
3934 +// Note that sparse lists are not possible, you must explicitly specify all needed entries starting from index 0,
3935 +// and when bencoding, the first unset value terminates the list.
3937 +namespace torrent {
3939 +// Hierarchical structure mapping bencode keys to flat array indices.
3940 +class LIBTORRENT_EXPORT StaticMapKeys : public std::vector<StaticMapKeys> {
3941 +public:
3942 + typedef std::vector<StaticMapKeys> base_type;
3944 + struct mapping_type {
3945 + size_t index;
3946 + const char* key;
3947 + };
3949 + enum value_type {
3950 + TYPE_VALUE,
3951 + TYPE_LIST,
3952 + TYPE_DICT,
3953 + TYPE_BENCODE,
3954 + TYPE_BENCODE_LIST,
3955 + };
3957 + StaticMapKeys(const mapping_type* key_list, size_t length);
3959 + void set_end(size_t end) { m_indexEnd = end; }
3961 + size_t index_begin() const { return m_indexBegin; }
3962 + size_t index_end() const { return m_indexEnd; }
3964 + value_type type() const { return m_type; }
3966 + SimpleString key() const { return m_key; }
3968 +private:
3969 + StaticMapKeys(SimpleString key, value_type type, size_t begin, size_t end)
3970 + : m_key(key), m_indexBegin(begin), m_indexEnd(end), m_type(type) {}
3972 + int check_key_order(SimpleString key);
3974 + SimpleString m_key;
3975 + size_t m_indexBegin;
3976 + size_t m_indexEnd;
3977 + value_type m_type;
3980 +template<typename tmpl_key_type, size_t tmpl_length>
3981 +class LIBTORRENT_EXPORT StaticMap {
3982 +public:
3983 + typedef Object& value_type;
3984 + typedef tmpl_key_type key_type;
3985 + typedef StaticMapKeys key_map_type;
3986 + typedef Object list_type[tmpl_length];
3988 + Object& operator [] (key_type key) { return m_values[key]; }
3989 + const Object& operator [] (key_type key) const { return m_values[key]; }
3991 + const key_map_type& map() const { return keyMap; }
3993 + list_type& values() { return m_values; }
3994 + const list_type& values() const { return m_values; }
3996 + static const size_t length = tmpl_length;
3998 +private:
3999 + struct key_map_init : public key_map_type {
4000 + key_map_init(key_map_type::mapping_type* key_list) : key_map_type(key_list, tmpl_length) {};
4001 + };
4002 + static const key_map_init keyMap;
4004 + list_type m_values;
4009 +#endif
4010 diff --git a/src/torrent/torrent.cc b/src/torrent/torrent.cc
4011 index e8ffbac..47027cc 100644
4012 --- a/src/torrent/torrent.cc
4013 +++ b/src/torrent/torrent.cc
4014 @@ -350,11 +350,22 @@ download_add(Object* object) {
4016 ctor.initialize(*object);
4018 - std::string infoHash = object_sha1(&object->get_key("info"));
4019 + std::string infoHash;
4020 + if (download->info()->is_meta_download())
4021 + infoHash = object->get_key("info").get_key("pieces").as_string();
4022 + else
4023 + infoHash = object_sha1(&object->get_key("info"));
4025 if (manager->download_manager()->find(infoHash) != manager->download_manager()->end())
4026 throw input_error("Info hash already used by another torrent.");
4028 + if (!download->info()->is_meta_download()) {
4029 + char buffer[1024];
4030 + uint64_t metadata_size = 0;
4031 + object_write_bencode_c(&object_write_to_size, &metadata_size, object_buffer_t(buffer, buffer + sizeof(buffer)), &object->get_key("info"));
4032 + download->main()->set_metadata_size(metadata_size);
4035 download->set_hash_queue(manager->hash_queue());
4036 download->initialize(infoHash, PEER_NAME + rak::generate_random<std::string>(20 - std::string(PEER_NAME).size()));
4038 diff --git a/src/tracker/tracker_dht.cc b/src/tracker/tracker_dht.cc
4039 index c63ce58..309fcf2 100644
4040 --- a/src/tracker/tracker_dht.cc
4041 +++ b/src/tracker/tracker_dht.cc
4042 @@ -115,13 +115,11 @@ TrackerDht::type() const {
4045 void
4046 -TrackerDht::receive_peers(const Object& peer_list) {
4047 +TrackerDht::receive_peers(SimpleString peers) {
4048 if (!is_busy())
4049 throw internal_error("TrackerDht::receive_peers called while not busy.");
4051 - Object::list_type peers = peer_list.as_list();
4052 - for (Object::list_type::const_iterator itr = peers.begin(); itr != peers.end(); ++itr)
4053 - m_peers.parse_address_compact(itr->as_string());
4054 + m_peers.parse_address_bencode(peers);
4057 void
4058 diff --git a/src/tracker/tracker_dht.h b/src/tracker/tracker_dht.h
4059 index d197e61..d096b46 100644
4060 --- a/src/tracker/tracker_dht.h
4061 +++ b/src/tracker/tracker_dht.h
4062 @@ -71,7 +71,7 @@ public:
4064 bool has_peers() const { return !m_peers.empty(); }
4066 - void receive_peers(const Object& peer_list);
4067 + void receive_peers(SimpleString peers);
4068 void receive_success();
4069 void receive_failed(const char* msg);
4070 void receive_progress(int replied, int contacted);