~ubuntu-branches/ubuntu/wily/libtorrent/wily-proposed

« back to all changes in this revision

Viewing changes to src/dht/dht_transaction.h

  • Committer: Bazaar Package Importer
  • Author(s): Rogério Brito
  • Date: 2011-03-20 01:06:18 UTC
  • mfrom: (1.1.13 upstream) (4.1.9 sid)
  • Revision ID: james.westby@ubuntu.com-20110320010618-g3wyylccqzqko73c
Tags: 0.12.7-5
* Use Steinar's "real" patch for IPv6. Addresses #490277, #618275,
  and Closes: #617791.
* Adapt libtorrent-0.12.6-ipv6-07.patch. It FTBFS otherwise.
* Add proper attibution to the IPv6 patch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// libTorrent - BitTorrent library
 
2
// Copyright (C) 2005-2007, 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> {
 
74
  dht_compare_closer(const HashString& target) : m_target(target) { }
 
75
 
 
76
  bool operator () (const DhtNode* one, const DhtNode* two) const;
 
77
 
 
78
  const HashString&               target() const   { return m_target; }
 
79
  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 {
 
109
    accessor_wrapper() { }
 
110
    accessor_wrapper(const T& itr) : T(itr) { }
 
111
 
 
112
    DhtNode*                        node() const     { return (**this).first; }
 
113
    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.
 
128
  int                  num_contacted()                   { return m_contacted; }
 
129
  int                  num_replied()                     { return m_replied; }
 
130
 
 
131
  bool                 start()                           { m_started = true; return m_pending; }
 
132
  bool                 complete() const                  { return m_started && !m_pending; }
 
133
 
 
134
  const HashString&    target() const                    { return m_target; }
 
135
  raw_string           target_raw_string() const         { return raw_string(m_target.data(), HashString::size_data); }
 
136
 
 
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.
 
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:
 
173
  DhtAnnounce(const HashString& infoHash, TrackerDht* tracker, const DhtBucket& contacts)
 
174
    : DhtSearch(infoHash, contacts),
 
175
      m_tracker(tracker) { }
 
176
  ~DhtAnnounce();
 
177
 
 
178
  virtual bool         is_announce() const               { return true; }
 
179
 
 
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
 
 
187
  void                 receive_peers(raw_list peers) { m_tracker->receive_peers(peers); }
 
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
 
 
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
 
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
 
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
 
 
248
  ~DhtTransactionPacket()                               { delete[] m_data; }
 
249
 
 
250
  bool                        has_transaction() const   { return m_id >= -1; }
 
251
  bool                        has_failed() const        { return m_id == -1; }
 
252
  void                        set_failed()              { m_id = -1; }
 
253
 
 
254
  const rak::socket_address*  address() const           { return &m_sa; }
 
255
  rak::socket_address*        address()                 { return &m_sa; }
 
256
 
 
257
  const char*                 c_str() const             { return m_data; }
 
258
  size_t                      length() const            { return m_length; }
 
259
 
 
260
  int                         id() const                { return m_id; }
 
261
  int                         age() const               { return has_transaction() ? 0 : cachedTime.seconds() + m_id; }
 
262
  const DhtTransaction*       transaction() const       { return m_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;
 
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
 
 
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.
 
300
  const HashString&           id()                 { return m_id; }
 
301
  const rak::socket_address*  address()            { return &m_sa; }
 
302
 
 
303
  int                         timeout()            { return m_timeout; }
 
304
  int                         quick_timeout()      { return m_quickTimeout; }
 
305
  bool                        has_quick_timeout()  { return m_hasQuickTimeout; }
 
306
 
 
307
  DhtTransactionPacket*       packet()             { return m_packet; }
 
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
 
 
338
  virtual bool               is_search()                  { return true; }
 
339
 
 
340
  DhtSearch::const_accessor  node()                       { return m_node; }
 
341
  DhtSearch*                 search()                     { return m_search; }
 
342
 
 
343
  void                       set_stalled();
 
344
 
 
345
  void                       complete(bool success);
 
346
 
 
347
protected: 
 
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:
 
361
  DhtTransactionPing(const HashString& id, const rak::socket_address* sa) 
 
362
    : DhtTransaction(-1, 30, id, sa) { }
 
363
 
 
364
  virtual transaction_type    type()                     { return DHT_PING; }
 
365
};
 
366
 
 
367
class DhtTransactionFindNode : public DhtTransactionSearch {
 
368
public:
 
369
  DhtTransactionFindNode(DhtSearch::const_accessor& node)
 
370
    : DhtTransactionSearch(4, 30, node) { }
 
371
 
 
372
  virtual transaction_type    type()                     { return DHT_FIND_NODE; }
 
373
};
 
374
 
 
375
class DhtTransactionGetPeers : public DhtTransactionSearch {
 
376
public:
 
377
  DhtTransactionGetPeers(DhtSearch::const_accessor& node)
 
378
    : DhtTransactionSearch(-1, 30, node) { }
 
379
 
 
380
  virtual transaction_type    type()                     { return DHT_GET_PEERS; }
 
381
};
 
382
 
 
383
class DhtTransactionAnnouncePeer : public DhtTransaction {
 
384
public:
 
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
 
 
393
  virtual transaction_type type()      { return DHT_ANNOUNCE_PEER; }
 
394
 
 
395
  const HashString&        info_hash() { return m_infoHash; }
 
396
  raw_string               info_hash_raw_string() const { return raw_string(m_infoHash.data(), HashString::size_data); }
 
397
  raw_string               token()     { return m_token; }
 
398
 
 
399
private:
 
400
  HashString m_infoHash;
 
401
  raw_string m_token;
 
402
};
 
403
 
 
404
inline bool
 
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
 
414
DhtSearch::set_node_active(const_accessor& n, bool active) {
 
415
  n.node()->m_lastSeen = active;
 
416
}
 
417
 
 
418
inline bool
 
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
 
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
 
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*
 
435
DhtTransaction::as_search() {
 
436
  return static_cast<DhtTransactionSearch*>(this);
 
437
}
 
438
 
 
439
inline DhtTransactionPing*
 
440
DhtTransaction::as_ping() {
 
441
  return static_cast<DhtTransactionPing*>(this);
 
442
}
 
443
 
 
444
inline DhtTransactionFindNode*
 
445
DhtTransaction::as_find_node() {
 
446
  return static_cast<DhtTransactionFindNode*>(this);
 
447
}
 
448
 
 
449
inline DhtTransactionGetPeers*
 
450
DhtTransaction::as_get_peers() {
 
451
  return static_cast<DhtTransactionGetPeers*>(this);
 
452
}
 
453
 
 
454
inline DhtTransactionAnnouncePeer*
 
455
DhtTransaction::as_announce_peer() {
 
456
  return static_cast<DhtTransactionAnnouncePeer*>(this);
 
457
}
 
458
 
 
459
}
 
460
 
 
461
#endif