1
// libTorrent - BitTorrent library
2
// Copyright (C) 2005-2007, Jari Sundell
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.
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.
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
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
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.
32
// Contact: Jari Sundell <jaris@ifi.uio.no>
35
// 3185 Skoppum, NORWAY
37
#ifndef LIBTORRENT_DHT_TRANSACTION_H
38
#define LIBTORRENT_DHT_TRANSACTION_H
41
#include <rak/socket_address.h>
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"
56
class DhtTransactionSearch;
59
class DhtTransactionPing;
60
class DhtTransactionFindNode;
61
class DhtTransactionFindNodeAnnounce;
62
class DhtTransactionGetPeers;
63
class DhtTransactionAnnouncePeer;
65
// DhtSearch implements the DHT search algorithm and holds search data
66
// that needs to be persistent across multiple find_node transactions.
68
// DhtAnnounce is a derived class used for searches that will eventually
69
// lead to an announce to the closest nodes.
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) { }
76
bool operator () (const DhtNode* one, const DhtNode* two) const;
78
const HashString& target() const { return m_target; }
79
raw_string target_raw_string() const { return raw_string(m_target.data(), HashString::size_data); }
82
const HashString& m_target;
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;
93
typedef std::map<DhtNode*, DhtSearch*, dht_compare_closer> base_type;
95
// Number of closest potential contact nodes to keep.
96
static const unsigned int max_contacts = 18;
98
// Number of closest nodes we actually announce to.
99
static const unsigned int max_announce = 3;
101
DhtSearch(const HashString& target, const DhtBucket& contacts);
102
virtual ~DhtSearch();
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) { }
112
DhtNode* node() const { return (**this).first; }
113
DhtSearch* search() const { return (**this).second; }
116
typedef accessor_wrapper<base_type::const_iterator> const_accessor;
117
typedef accessor_wrapper<base_type::iterator> accessor;
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);
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();
127
// Search statistics.
128
int num_contacted() { return m_contacted; }
129
int num_replied() { return m_replied; }
131
bool start() { m_started = true; return m_pending; }
132
bool complete() const { return m_started && !m_pending; }
134
const HashString& target() const { return m_target; }
135
raw_string target_raw_string() const { return raw_string(m_target.data(), HashString::size_data); }
137
virtual bool is_announce() const { return false; }
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(); }
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);
147
void trim(bool final);
148
void node_status(const_accessor& n, bool success);
149
void set_node_active(const_accessor& n, bool active);
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;
157
bool m_restart; // If true, trim nodes and reset m_next on the following get_contact call.
160
// Next node to return in get_contact, is end() if we have no more contactable nodes.
161
const_accessor m_next;
164
DhtSearch(const DhtSearch& s);
166
bool node_uncontacted(const DhtNode* node) const;
171
class DhtAnnounce : public DhtSearch {
173
DhtAnnounce(const HashString& infoHash, TrackerDht* tracker, const DhtBucket& contacts)
174
: DhtSearch(infoHash, contacts),
175
m_tracker(tracker) { }
178
virtual bool is_announce() const { return true; }
180
const TrackerDht* tracker() const { return m_tracker; }
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();
187
void receive_peers(raw_list peers) { m_tracker->receive_peers(peers); }
188
void update_status() { m_tracker->receive_progress(m_replied, m_contacted); }
191
TrackerDht* m_tracker;
194
// Possible bencode keys in a DHT message.
219
class DhtMessage : public static_map_type<dht_keys, key_LAST> {
221
typedef static_map_type<dht_keys, key_LAST> base_type;
223
DhtMessage() : data_end(data) {};
225
// Must be big enough to hold one of the possible variable-sized reply data.
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];
237
// Class holding transaction data to be transmitted.
238
class DhtTransactionPacket {
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); };
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); };
248
~DhtTransactionPacket() { delete[] m_data; }
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; }
254
const rak::socket_address* address() const { return &m_sa; }
255
rak::socket_address* address() { return &m_sa; }
257
const char* c_str() const { return m_data; }
258
size_t length() const { return m_length; }
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; }
266
void build_buffer(const DhtMessage& data);
268
rak::socket_address m_sa;
272
DhtTransaction* m_transaction;
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 {
280
virtual ~DhtTransaction();
289
virtual transaction_type type() = 0;
290
virtual bool is_search() { return false; }
292
// Key to uniquely identify a transaction with given per-node transaction id.
293
typedef uint64_t key_type;
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);
299
// Node ID and address.
300
const HashString& id() { return m_id; }
301
const rak::socket_address* address() { return &m_sa; }
303
int timeout() { return m_timeout; }
304
int quick_timeout() { return m_quickTimeout; }
305
bool has_quick_timeout() { return m_hasQuickTimeout; }
307
DhtTransactionPacket* packet() { return m_packet; }
308
void set_packet(DhtTransactionPacket* p) { m_packet = p; }
310
DhtTransactionSearch* as_search();
312
DhtTransactionPing* as_ping();
313
DhtTransactionFindNode* as_find_node();
314
DhtTransactionGetPeers* as_get_peers();
315
DhtTransactionAnnouncePeer* as_announce_peer();
318
DhtTransaction(int quick_timeout, int timeout, const HashString& id, const rak::socket_address* sa);
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;
326
DhtTransaction(const DhtTransaction& t);
328
rak::socket_address m_sa;
331
DhtTransactionPacket* m_packet;
334
class DhtTransactionSearch : public DhtTransaction {
336
virtual ~DhtTransactionSearch();
338
virtual bool is_search() { return true; }
340
DhtSearch::const_accessor node() { return m_node; }
341
DhtSearch* search() { return m_search; }
345
void complete(bool success);
348
DhtTransactionSearch(int quick_timeout, int timeout, DhtSearch::const_accessor& node)
349
: DhtTransaction(quick_timeout, timeout, node.node()->id(), node.node()->address()),
351
m_search(node.search()) { if (!m_hasQuickTimeout) m_search->m_concurrency++; }
354
DhtSearch::const_accessor m_node;
358
// Actual transaction classes.
359
class DhtTransactionPing : public DhtTransaction {
361
DhtTransactionPing(const HashString& id, const rak::socket_address* sa)
362
: DhtTransaction(-1, 30, id, sa) { }
364
virtual transaction_type type() { return DHT_PING; }
367
class DhtTransactionFindNode : public DhtTransactionSearch {
369
DhtTransactionFindNode(DhtSearch::const_accessor& node)
370
: DhtTransactionSearch(4, 30, node) { }
372
virtual transaction_type type() { return DHT_FIND_NODE; }
375
class DhtTransactionGetPeers : public DhtTransactionSearch {
377
DhtTransactionGetPeers(DhtSearch::const_accessor& node)
378
: DhtTransactionSearch(-1, 30, node) { }
380
virtual transaction_type type() { return DHT_GET_PEERS; }
383
class DhtTransactionAnnouncePeer : public DhtTransaction {
385
DhtTransactionAnnouncePeer(const HashString& id,
386
const rak::socket_address* sa,
387
const HashString& infoHash,
389
: DhtTransaction(-1, 30, id, sa),
390
m_infoHash(infoHash),
393
virtual transaction_type type() { return DHT_ANNOUNCE_PEER; }
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; }
400
HashString m_infoHash;
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]);
414
DhtSearch::set_node_active(const_accessor& n, bool active) {
415
n.node()->m_lastSeen = active;
419
dht_compare_closer::operator () (const DhtNode* one, const DhtNode* two) const {
420
return DhtSearch::is_closer(*one, *two, m_target);
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;
429
DhtTransaction::key_match(key_type key, const rak::socket_address* sa) {
430
return (key >> 32) == sa->sa_inet()->address_n();
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);
439
inline DhtTransactionPing*
440
DhtTransaction::as_ping() {
441
return static_cast<DhtTransactionPing*>(this);
444
inline DhtTransactionFindNode*
445
DhtTransaction::as_find_node() {
446
return static_cast<DhtTransactionFindNode*>(this);
449
inline DhtTransactionGetPeers*
450
DhtTransaction::as_get_peers() {
451
return static_cast<DhtTransactionGetPeers*>(this);
454
inline DhtTransactionAnnouncePeer*
455
DhtTransaction::as_announce_peer() {
456
return static_cast<DhtTransactionAnnouncePeer*>(this);