1 // libTorrent - BitTorrent library
2 // Copyright (C) 2005-2011, Jari Sundell
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 //
18 // In addition, as a special exception, the copyright holders give
19 // permission to link the code of portions of this program with the
20 // OpenSSL library under certain conditions as described in each
21 // individual source file, and distribute linked combinations
22 // including the two.
23 //
24 // You must obey the GNU General Public License in all respects for
25 // all of the code used other than OpenSSL.  If you modify file(s)
26 // with this exception, you may extend this exception to your version
27 // of the file(s), but you are not obligated to do so.  If you do not
28 // wish to do so, delete this exception statement from your version.
29 // If you delete this exception statement from all source files in the
30 // program, then also delete it here.
31 //
32 // Contact:  Jari Sundell <jaris@ifi.uio.no>
33 //
34 //           Skomakerveien 33
35 //           3185 Skoppum, NORWAY
36 
37 #ifndef LIBTORRENT_DHT_TRANSACTION_H
38 #define LIBTORRENT_DHT_TRANSACTION_H
39 
40 #include <map>
41 #include <rak/socket_address.h>
42 
43 #include "dht/dht_node.h"
44 #include "torrent/hash_string.h"
45 #include "torrent/object_static_map.h"
46 #include "tracker/tracker_dht.h"
47 
48 namespace torrent {
49 
50 class TrackerDht;
51 
52 class DhtBucket;
53 
54 class DhtSearch;
55 class DhtAnnounce;
56 class DhtTransactionSearch;
57 
58 class DhtTransaction;
59 class DhtTransactionPing;
60 class DhtTransactionFindNode;
61 class DhtTransactionFindNodeAnnounce;
62 class DhtTransactionGetPeers;
63 class DhtTransactionAnnouncePeer;
64 
65 // DhtSearch implements the DHT search algorithm and holds search data
66 // that needs to be persistent across multiple find_node transactions.
67 //
68 // DhtAnnounce is a derived class used for searches that will eventually
69 // lead to an announce to the closest nodes.
70 
71 
72 // Compare predicate for ID closeness.
73 struct dht_compare_closer : public std::binary_function<const DhtNode*, const DhtNode*, bool> {
dht_compare_closerdht_compare_closer74   dht_compare_closer(const HashString& target) : m_target(target) { }
75 
76   bool operator () (const DhtNode* one, const DhtNode* two) const;
77 
targetdht_compare_closer78   const HashString&               target() const   { return m_target; }
target_raw_stringdht_compare_closer79   raw_string                      target_raw_string() const { return raw_string(m_target.data(), HashString::size_data); }
80 
81   private:
82   const HashString&    m_target;
83 };
84 
85 // DhtSearch contains a list of nodes sorted by closeness to the given target,
86 // and returns what nodes to contact with up to three concurrent transactions pending.
87 // The map element is the DhtSearch object itself to allow the returned accessors
88 // to know which search a given node belongs to.
89 class DhtSearch : protected std::map<DhtNode*, DhtSearch*, dht_compare_closer> {
90   friend class DhtTransactionSearch;
91 
92 public:
93   typedef std::map<DhtNode*, DhtSearch*, dht_compare_closer> base_type;
94 
95   // Number of closest potential contact nodes to keep.
96   static const unsigned int max_contacts = 18;
97 
98   // Number of closest nodes we actually announce to.
99   static const unsigned int max_announce = 3;
100 
101   DhtSearch(const HashString& target, const DhtBucket& contacts);
102   virtual ~DhtSearch();
103 
104   // Wrapper for iterators, allowing more convenient access to the key
105   // and element values, which also makes it easier to change the container
106   // without having to modify much code using iterators.
107   template <typename T>
108   struct accessor_wrapper : public T {
accessor_wrapperaccessor_wrapper109     accessor_wrapper() { }
accessor_wrapperaccessor_wrapper110     accessor_wrapper(const T& itr) : T(itr) { }
111 
nodeaccessor_wrapper112     DhtNode*                        node() const     { return (**this).first; }
searchaccessor_wrapper113     DhtSearch*                      search() const   { return (**this).second; }
114   };
115 
116   typedef accessor_wrapper<base_type::const_iterator>  const_accessor;
117   typedef accessor_wrapper<base_type::iterator>        accessor;
118 
119   // Add a potential node to contact for the search.
120   bool                 add_contact(const HashString& id, const rak::socket_address* sa);
121   void                 add_contacts(const DhtBucket& contacts);
122 
123   // Return next node to contact. Up to concurrent_searches nodes are returned,
124   // and end() after that. Don't advance the accessor to get further contacts!
125   const_accessor       get_contact();
126 
127   // Search statistics.
num_contacted()128   int                  num_contacted()                   { return m_contacted; }
num_replied()129   int                  num_replied()                     { return m_replied; }
130 
start()131   bool                 start()                           { m_started = true; return m_pending; }
complete()132   bool                 complete() const                  { return m_started && !m_pending; }
133 
target()134   const HashString&    target() const                    { return m_target; }
target_raw_string()135   raw_string           target_raw_string() const         { return raw_string(m_target.data(), HashString::size_data); }
136 
is_announce()137   virtual bool         is_announce() const               { return false; }
138 
139   // Expose the otherwise private end() function but return an accessor,
140   // to allow code checking whether get_contact returned a valid accessor.
end()141   const_accessor       end() const                       { return base_type::end(); }
142 
143   // Used by the sorting/comparison predicate to see which node is closer.
144   static bool          is_closer(const HashString& one, const HashString& two, const HashString& target);
145 
146 protected:
147   void                 trim(bool final);
148   void                 node_status(const_accessor& n, bool success);
149   void                 set_node_active(const_accessor& n, bool active);
150 
151   // Statistics about contacted nodes.
152   unsigned int         m_pending;
153   unsigned int         m_contacted;
154   unsigned int         m_replied;
155   unsigned int         m_concurrency;
156 
157   bool                 m_restart;  // If true, trim nodes and reset m_next on the following get_contact call.
158   bool                 m_started;
159 
160   // Next node to return in get_contact, is end() if we have no more contactable nodes.
161   const_accessor       m_next;
162 
163 private:
164   DhtSearch(const DhtSearch& s);
165 
166   bool                 node_uncontacted(const DhtNode* node) const;
167 
168   HashString           m_target;
169 };
170 
171 class DhtAnnounce : public DhtSearch {
172 public:
DhtAnnounce(const HashString & infoHash,TrackerDht * tracker,const DhtBucket & contacts)173   DhtAnnounce(const HashString& infoHash, TrackerDht* tracker, const DhtBucket& contacts)
174     : DhtSearch(infoHash, contacts),
175       m_tracker(tracker) { }
176   ~DhtAnnounce();
177 
is_announce()178   virtual bool         is_announce() const               { return true; }
179 
tracker()180   const TrackerDht*    tracker() const                   { return m_tracker; }
181 
182   // Start announce and return final set of nodes in get_contact() calls.
183   // This resets DhtSearch's completed() function, which now
184   // counts announces instead.
185   const_accessor       start_announce();
186 
receive_peers(raw_list peers)187   void                 receive_peers(raw_list peers) { m_tracker->receive_peers(peers); }
update_status()188   void                 update_status() { m_tracker->receive_progress(m_replied, m_contacted); }
189 
190 private:
191   TrackerDht*          m_tracker;
192 };
193 
194 // Possible bencode keys in a DHT message.
195 enum dht_keys {
196   key_a_id,
197   key_a_infoHash,
198   key_a_port,
199   key_a_target,
200   key_a_token,
201 
202   key_e_0,
203   key_e_1,
204 
205   key_q,
206 
207   key_r_id,
208   key_r_nodes,
209   key_r_token,
210   key_r_values,
211 
212   key_t,
213   key_v,
214   key_y,
215 
216   key_LAST,
217 };
218 
219 class DhtMessage : public static_map_type<dht_keys, key_LAST> {
220 public:
221   typedef static_map_type<dht_keys, key_LAST> base_type;
222 
DhtMessage()223   DhtMessage() : data_end(data) {};
224 
225   // Must be big enough to hold one of the possible variable-sized reply data.
226   // Currently either:
227   // - error message (size doesn't really matter, it'll be truncated at worst)
228   // - announce token (8 bytes, needs 20 bytes buffer to build)
229   // Never more than one of the above.
230   // And additionally for queries we send:
231   // - transaction ID (3 bytes)
232   static const size_t data_size = 64;
233   char data[data_size];
234   char* data_end;
235 };
236 
237 // Class holding transaction data to be transmitted.
238 class DhtTransactionPacket {
239 public:
240   // transaction packet
DhtTransactionPacket(const rak::socket_address * s,const DhtMessage & d,unsigned int id,DhtTransaction * t)241   DhtTransactionPacket(const rak::socket_address* s, const DhtMessage& d, unsigned int id, DhtTransaction* t)
242     : m_sa(*s), m_id(id), m_transaction(t) { build_buffer(d); };
243 
244   // non-transaction packet
DhtTransactionPacket(const rak::socket_address * s,const DhtMessage & d)245   DhtTransactionPacket(const rak::socket_address* s, const DhtMessage& d)
246     : m_sa(*s), m_id(-cachedTime.seconds()), m_transaction(NULL) { build_buffer(d); };
247 
~DhtTransactionPacket()248   ~DhtTransactionPacket()                               { delete[] m_data; }
249 
has_transaction()250   bool                        has_transaction() const   { return m_id >= -1; }
has_failed()251   bool                        has_failed() const        { return m_id == -1; }
set_failed()252   void                        set_failed()              { m_id = -1; }
253 
address()254   const rak::socket_address*  address() const           { return &m_sa; }
address()255   rak::socket_address*        address()                 { return &m_sa; }
256 
c_str()257   const char*                 c_str() const             { return m_data; }
length()258   size_t                      length() const            { return m_length; }
259 
id()260   int                         id() const                { return m_id; }
age()261   int                         age() const               { return has_transaction() ? 0 : cachedTime.seconds() + m_id; }
transaction()262   const DhtTransaction*       transaction() const       { return m_transaction; }
transaction()263   DhtTransaction*             transaction()             { return m_transaction; }
264 
265 private:
266   void                        build_buffer(const DhtMessage& data);
267 
268   rak::socket_address   m_sa;
269   char*                 m_data;
270   size_t                m_length;
271   int                   m_id;
272   DhtTransaction*       m_transaction;
273 };
274 
275 // DHT Transaction classes. DhtTransaction and DhtTransactionSearch
276 // are not directly usable with no public constructor, since type()
277 // is a pure virtual function.
278 class DhtTransaction {
279 public:
280   virtual ~DhtTransaction();
281 
282   typedef enum {
283     DHT_PING,
284     DHT_FIND_NODE,
285     DHT_GET_PEERS,
286     DHT_ANNOUNCE_PEER,
287   } transaction_type;
288 
289   virtual transaction_type    type() = 0;
is_search()290   virtual bool                is_search()          { return false; }
291 
292   // Key to uniquely identify a transaction with given per-node transaction id.
293   typedef uint64_t key_type;
294 
key(int id)295   key_type                    key(int id) const    { return key(&m_sa, id); }
296   static key_type             key(const rak::socket_address* sa, int id);
297   static bool                 key_match(key_type key, const rak::socket_address* sa);
298 
299   // Node ID and address.
id()300   const HashString&           id()                 { return m_id; }
address()301   const rak::socket_address*  address()            { return &m_sa; }
302 
timeout()303   int                         timeout()            { return m_timeout; }
quick_timeout()304   int                         quick_timeout()      { return m_quickTimeout; }
has_quick_timeout()305   bool                        has_quick_timeout()  { return m_hasQuickTimeout; }
306 
packet()307   DhtTransactionPacket*       packet()             { return m_packet; }
set_packet(DhtTransactionPacket * p)308   void                        set_packet(DhtTransactionPacket* p) { m_packet = p; }
309 
310   DhtTransactionSearch*       as_search();
311 
312   DhtTransactionPing*         as_ping();
313   DhtTransactionFindNode*     as_find_node();
314   DhtTransactionGetPeers*     as_get_peers();
315   DhtTransactionAnnouncePeer* as_announce_peer();
316 
317 protected:
318   DhtTransaction(int quick_timeout, int timeout, const HashString& id, const rak::socket_address* sa);
319 
320   // m_id must be the first element to ensure it is aligned properly,
321   // because we later read a size_t value from it.
322   const HashString       m_id;
323   bool                   m_hasQuickTimeout;
324 
325 private:
326   DhtTransaction(const DhtTransaction& t);
327 
328   rak::socket_address    m_sa;
329   int                    m_timeout;
330   int                    m_quickTimeout;
331   DhtTransactionPacket*  m_packet;
332 };
333 
334 class DhtTransactionSearch : public DhtTransaction {
335 public:
336   virtual ~DhtTransactionSearch();
337 
is_search()338   virtual bool               is_search()                  { return true; }
339 
node()340   DhtSearch::const_accessor  node()                       { return m_node; }
search()341   DhtSearch*                 search()                     { return m_search; }
342 
343   void                       set_stalled();
344 
345   void                       complete(bool success);
346 
347 protected:
DhtTransactionSearch(int quick_timeout,int timeout,DhtSearch::const_accessor & node)348   DhtTransactionSearch(int quick_timeout, int timeout, DhtSearch::const_accessor& node)
349     : DhtTransaction(quick_timeout, timeout, node.node()->id(), node.node()->address()),
350       m_node(node),
351       m_search(node.search()) { if (!m_hasQuickTimeout) m_search->m_concurrency++; }
352 
353 private:
354   DhtSearch::const_accessor  m_node;
355   DhtSearch*                 m_search;
356 };
357 
358 // Actual transaction classes.
359 class DhtTransactionPing : public DhtTransaction {
360 public:
DhtTransactionPing(const HashString & id,const rak::socket_address * sa)361   DhtTransactionPing(const HashString& id, const rak::socket_address* sa)
362     : DhtTransaction(-1, 30, id, sa) { }
363 
type()364   virtual transaction_type    type()                     { return DHT_PING; }
365 };
366 
367 class DhtTransactionFindNode : public DhtTransactionSearch {
368 public:
DhtTransactionFindNode(DhtSearch::const_accessor & node)369   DhtTransactionFindNode(DhtSearch::const_accessor& node)
370     : DhtTransactionSearch(4, 30, node) { }
371 
type()372   virtual transaction_type    type()                     { return DHT_FIND_NODE; }
373 };
374 
375 class DhtTransactionGetPeers : public DhtTransactionSearch {
376 public:
DhtTransactionGetPeers(DhtSearch::const_accessor & node)377   DhtTransactionGetPeers(DhtSearch::const_accessor& node)
378     : DhtTransactionSearch(-1, 30, node) { }
379 
type()380   virtual transaction_type    type()                     { return DHT_GET_PEERS; }
381 };
382 
383 class DhtTransactionAnnouncePeer : public DhtTransaction {
384 public:
DhtTransactionAnnouncePeer(const HashString & id,const rak::socket_address * sa,const HashString & infoHash,raw_string token)385   DhtTransactionAnnouncePeer(const HashString& id,
386                              const rak::socket_address* sa,
387                              const HashString& infoHash,
388                              raw_string token)
389     : DhtTransaction(-1, 30, id, sa),
390       m_infoHash(infoHash),
391       m_token(token) { }
392 
type()393   virtual transaction_type type()      { return DHT_ANNOUNCE_PEER; }
394 
info_hash()395   const HashString&        info_hash() { return m_infoHash; }
info_hash_raw_string()396   raw_string               info_hash_raw_string() const { return raw_string(m_infoHash.data(), HashString::size_data); }
token()397   raw_string               token()     { return m_token; }
398 
399 private:
400   HashString m_infoHash;
401   raw_string m_token;
402 };
403 
404 inline bool
is_closer(const HashString & one,const HashString & two,const HashString & target)405 DhtSearch::is_closer(const HashString& one, const HashString& two, const HashString& target) {
406   for (unsigned int i=0; i<one.size(); i++)
407     if (one[i] != two[i])
408       return (uint8_t)(one[i] ^ target[i]) < (uint8_t)(two[i] ^ target[i]);
409 
410   return false;
411 }
412 
413 inline void
set_node_active(const_accessor & n,bool active)414 DhtSearch::set_node_active(const_accessor& n, bool active) {
415   n.node()->m_lastSeen = active;
416 }
417 
418 inline bool
operator()419 dht_compare_closer::operator () (const DhtNode* one, const DhtNode* two) const {
420   return DhtSearch::is_closer(*one, *two, m_target);
421 }
422 
423 inline DhtTransaction::key_type
key(const rak::socket_address * sa,int id)424 DhtTransaction::key(const rak::socket_address* sa, int id) {
425   return ((uint64_t)sa->sa_inet()->address_n() << 32) + id;
426 }
427 
428 inline bool
key_match(key_type key,const rak::socket_address * sa)429 DhtTransaction::key_match(key_type key, const rak::socket_address* sa) {
430   return (key >> 32) == sa->sa_inet()->address_n();
431 }
432 
433 // These could (should?) check that the type matches, or use dynamic_cast if we have RTTI.
434 inline DhtTransactionSearch*
as_search()435 DhtTransaction::as_search() {
436   return static_cast<DhtTransactionSearch*>(this);
437 }
438 
439 inline DhtTransactionPing*
as_ping()440 DhtTransaction::as_ping() {
441   return static_cast<DhtTransactionPing*>(this);
442 }
443 
444 inline DhtTransactionFindNode*
as_find_node()445 DhtTransaction::as_find_node() {
446   return static_cast<DhtTransactionFindNode*>(this);
447 }
448 
449 inline DhtTransactionGetPeers*
as_get_peers()450 DhtTransaction::as_get_peers() {
451   return static_cast<DhtTransactionGetPeers*>(this);
452 }
453 
454 inline DhtTransactionAnnouncePeer*
as_announce_peer()455 DhtTransaction::as_announce_peer() {
456   return static_cast<DhtTransactionAnnouncePeer*>(this);
457 }
458 
459 }
460 
461 #endif
462