~ubuntu-branches/ubuntu/oneiric/libtorrent/oneiric

« back to all changes in this revision

Viewing changes to src/torrent/peer/peer_list.cc

  • 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
#include "config.h"
 
38
 
 
39
#include <algorithm>
 
40
#include <functional>
 
41
#include <rak/functional.h>
 
42
#include <rak/socket_address.h>
 
43
 
 
44
#include "download/available_list.h"
 
45
#include "torrent/peer/client_list.h"
 
46
 
 
47
#include "exceptions.h"
 
48
#include "globals.h"
 
49
#include "manager.h"
 
50
#include "peer_info.h"
 
51
#include "peer_list.h"
 
52
 
 
53
namespace torrent {
 
54
 
 
55
bool
 
56
socket_address_less(const sockaddr* s1, const sockaddr* s2) {
 
57
  const rak::socket_address* sa1 = rak::socket_address::cast_from(s1);
 
58
  const rak::socket_address* sa2 = rak::socket_address::cast_from(s2);
 
59
 
 
60
  if (sa1->family() != sa2->family())
 
61
    return sa1->family() < sa2->family();
 
62
 
 
63
  else if (sa1->family() == rak::socket_address::af_inet)
 
64
    // Sort by hardware byte order to ensure proper ordering for
 
65
    // humans.
 
66
    return sa1->sa_inet()->address_h() < sa2->sa_inet()->address_h();
 
67
 
 
68
#ifdef RAK_USE_INET6
 
69
  else {
 
70
    const in6_addr addr1 = sa1->sa_inet6()->address();
 
71
    const in6_addr addr2 = sa2->sa_inet6()->address();
 
72
    return memcmp(&addr1, &addr2, sizeof(in6_addr)) < 0;
 
73
  }
 
74
#else
 
75
  else
 
76
    throw internal_error("socket_address_key(...) tried to compare an invalid family type.");
 
77
#endif
 
78
 
 
79
}
 
80
 
 
81
inline bool
 
82
socket_address_key::is_comparable(const sockaddr* sa) {
 
83
  return rak::socket_address::cast_from(sa)->family() == rak::socket_address::af_inet ||
 
84
    rak::socket_address::cast_from(sa)->family() == rak::socket_address::af_inet6;
 
85
}
 
86
 
 
87
struct peer_list_equal_port : public std::binary_function<PeerList::reference, uint16_t, bool> {
 
88
  bool operator () (PeerList::reference p, uint16_t port) {
 
89
    return rak::socket_address::cast_from(p.second->socket_address())->port() == port;
 
90
  }
 
91
};
 
92
 
 
93
PeerList::PeerList() :
 
94
  m_availableList(new AvailableList) {
 
95
}
 
96
 
 
97
PeerList::~PeerList() {
 
98
  std::for_each(begin(), end(), rak::on(rak::mem_ref(&value_type::second), rak::call_delete<PeerInfo>()));
 
99
  base_type::clear();
 
100
 
 
101
  delete m_availableList;
 
102
}
 
103
 
 
104
PeerInfo*
 
105
PeerList::insert_address(const sockaddr* sa, int flags) {
 
106
  if (!socket_address_key::is_comparable(sa))
 
107
    return NULL;
 
108
 
 
109
  range_type range = base_type::equal_range(sa);
 
110
 
 
111
  // Do some special handling if we got a new port number but the
 
112
  // address was present.
 
113
  //
 
114
  // What we do depends on the flags, but for now just allow one
 
115
  // PeerInfo per address key and do nothing.
 
116
  if (range.first != range.second)
 
117
    return NULL;
 
118
 
 
119
  const rak::socket_address* address = rak::socket_address::cast_from(sa);
 
120
 
 
121
  PeerInfo* peerInfo = new PeerInfo(sa);
 
122
  peerInfo->set_listen_port(address->port());
 
123
  
 
124
  manager->client_list()->retrieve_unknown(&peerInfo->mutable_client_info());
 
125
 
 
126
  base_type::insert(range.second, value_type(socket_address_key(peerInfo->socket_address()), peerInfo));
 
127
 
 
128
  if (flags & address_available && peerInfo->listen_port() != 0)
 
129
    m_availableList->push_back(address);
 
130
 
 
131
  return peerInfo;
 
132
}
 
133
 
 
134
inline bool
 
135
socket_address_less_rak(const rak::socket_address& s1, const rak::socket_address& s2) {
 
136
  return socket_address_less(s1.c_sockaddr(), s2.c_sockaddr());
 
137
}
 
138
 
 
139
uint32_t
 
140
PeerList::insert_available(const void* al) {
 
141
  uint32_t inserted = 0;
 
142
  const AddressList* addressList = static_cast<const AddressList*>(al);
 
143
 
 
144
  if (m_availableList->size() + addressList->size() > m_availableList->capacity())
 
145
    m_availableList->reserve(m_availableList->size() + addressList->size() + 128);
 
146
 
 
147
  // Optimize this so that we don't traverse the tree for every
 
148
  // insert, since we know 'al' is sorted.
 
149
 
 
150
  AddressList::const_iterator itr   = addressList->begin();
 
151
  AddressList::const_iterator last  = addressList->end();
 
152
  AvailableList::const_iterator availItr  = m_availableList->begin();
 
153
  AvailableList::const_iterator availLast = m_availableList->end();
 
154
 
 
155
  for (; itr != last; itr++) {
 
156
    if (!socket_address_key::is_comparable(itr->c_sockaddr()) || itr->port() == 0)
 
157
      continue;
 
158
 
 
159
    availItr = std::find_if(availItr, availLast, rak::bind2nd(std::ptr_fun(&socket_address_less_rak), *itr));
 
160
 
 
161
    if (availItr != availLast && !socket_address_less(availItr->c_sockaddr(), itr->c_sockaddr())) {
 
162
      // The address is already in m_availableList, so don't bother
 
163
      // going further.
 
164
      continue;
 
165
    }
 
166
 
 
167
    // Check if the peerinfo exists, if it does, check if we would
 
168
    // ever want to connect. Just update the timer for the last
 
169
    // availability notice if the peer isn't really ideal, but might
 
170
    // be used in an emergency.
 
171
    range_type range = base_type::equal_range(itr->c_sockaddr());
 
172
 
 
173
    if (range.first != range.second) {
 
174
      // Add some logic here to select the best PeerInfo, but for now
 
175
      // just assume the first one is the only one that exists.
 
176
      PeerInfo* peerInfo = range.first->second;
 
177
 
 
178
      if (peerInfo->listen_port() == 0)
 
179
        peerInfo->set_port(itr->port());
 
180
 
 
181
      if (peerInfo->connection() != NULL ||
 
182
          peerInfo->last_handshake() + 600 > (uint32_t)cachedTime.seconds())
 
183
        continue;
 
184
 
 
185
      // If the peer has sent us bad chunks or we just connected or
 
186
      // tried to do so a few minutes ago, only update its
 
187
      // availability timer.
 
188
    }
 
189
 
 
190
    // Should we perhaps add to available list even though we don't
 
191
    // want the peer, just to ensure we don't need to search for the
 
192
    // PeerInfo every time it gets reported. Though I'd assume it
 
193
    // won't happen often enough to be worth it.
 
194
 
 
195
    inserted++;
 
196
    m_availableList->push_back(&*itr);
 
197
  }
 
198
 
 
199
  return inserted;
 
200
}
 
201
 
 
202
uint32_t
 
203
PeerList::available_list_size() const {
 
204
  return m_availableList->size();
 
205
}
 
206
 
 
207
PeerInfo*
 
208
PeerList::connected(const sockaddr* sa, int flags) {
 
209
  if (!socket_address_key::is_comparable(sa))
 
210
    return NULL;
 
211
 
 
212
  PeerInfo* peerInfo;
 
213
  const rak::socket_address* address = rak::socket_address::cast_from(sa);
 
214
 
 
215
  range_type range = base_type::equal_range(sa);
 
216
 
 
217
  if (range.first == range.second) {
 
218
    // Create a new entry.
 
219
    peerInfo = new PeerInfo(sa);
 
220
 
 
221
    base_type::insert(range.second, value_type(socket_address_key(peerInfo->socket_address()), peerInfo));
 
222
 
 
223
  } else if (!range.first->second->is_connected()) {
 
224
    // Use an old entry.
 
225
    peerInfo = range.first->second;
 
226
    peerInfo->set_port(address->port());
 
227
 
 
228
  } else {
 
229
    // Make sure we don't end up throwing away the port the host is
 
230
    // actually listening on, when there may be several simultaneous
 
231
    // connection attempts to/from different ports.
 
232
    //
 
233
    // This also ensure we can connect to peers running on the same
 
234
    // host as the tracker.
 
235
    if (flags & connect_keep_handshakes &&
 
236
        range.first->second->is_handshake() &&
 
237
        rak::socket_address::cast_from(range.first->second->socket_address())->port() != address->port())
 
238
      m_availableList->buffer()->push_back(*address);
 
239
 
 
240
    return NULL;
 
241
  }
 
242
 
 
243
  if (flags & connect_filter_recent &&
 
244
      peerInfo->last_handshake() + 600 > (uint32_t)cachedTime.seconds())
 
245
    return NULL;
 
246
 
 
247
  if (!(flags & connect_incoming))
 
248
    peerInfo->set_listen_port(address->port());
 
249
 
 
250
  if (flags & connect_incoming)
 
251
    peerInfo->set_flags(PeerInfo::flag_incoming);
 
252
  else
 
253
    peerInfo->unset_flags(PeerInfo::flag_incoming);
 
254
 
 
255
  peerInfo->set_flags(PeerInfo::flag_connected);
 
256
  peerInfo->set_last_handshake(cachedTime.seconds());
 
257
 
 
258
  return peerInfo;
 
259
}
 
260
 
 
261
// Make sure we properly clear port when disconnecting.
 
262
 
 
263
void
 
264
PeerList::disconnected(PeerInfo* p, int flags) {
 
265
  range_type range = base_type::equal_range(p->socket_address());
 
266
  
 
267
  iterator itr = std::find_if(range.first, range.second, rak::equal(p, rak::mem_ref(&value_type::second)));
 
268
 
 
269
  if (itr == range.second) {
 
270
    if (std::find_if(base_type::begin(), base_type::end(), rak::equal(p, rak::mem_ref(&value_type::second))) == base_type::end())
 
271
      throw internal_error("PeerList::disconnected(...) itr == range.second, doesn't exist.");
 
272
    else
 
273
      throw internal_error("PeerList::disconnected(...) itr == range.second, not in the range.");
 
274
  }
 
275
  
 
276
  disconnected(itr, flags);
 
277
}
 
278
 
 
279
PeerList::iterator
 
280
PeerList::disconnected(iterator itr, int flags) {
 
281
  if (itr == base_type::end())
 
282
    throw internal_error("PeerList::disconnected(...) itr == end().");
 
283
 
 
284
  if (!itr->second->is_connected())
 
285
    throw internal_error("PeerList::disconnected(...) !itr->is_connected().");
 
286
 
 
287
  itr->second->unset_flags(PeerInfo::flag_connected);
 
288
 
 
289
  // Replace the socket address port with the listening port so that
 
290
  // future outgoing connections will connect to the right port.
 
291
  itr->second->set_port(0);
 
292
 
 
293
  if (flags & disconnect_set_time)
 
294
    itr->second->set_last_connection(cachedTime.seconds());
 
295
 
 
296
  if (flags & disconnect_available && itr->second->listen_port() != 0)
 
297
    m_availableList->push_back(rak::socket_address::cast_from(itr->second->socket_address()));
 
298
 
 
299
  // Do magic to get rid of unneeded entries.
 
300
  return ++itr;
 
301
}
 
302
 
 
303
uint32_t
 
304
PeerList::cull_peers(int flags) {
 
305
  uint32_t counter = 0;
 
306
  uint32_t timer;
 
307
 
 
308
  if (flags & cull_old)
 
309
    timer = cachedTime.seconds() - 24 * 60 * 60;
 
310
  else
 
311
    timer = 0;
 
312
 
 
313
  for (iterator itr = base_type::begin(); itr != base_type::end(); ) {
 
314
    if (itr->second->is_connected() ||
 
315
        itr->second->transfer_counter() != 0 ||
 
316
        itr->second->last_connection() >= timer ||
 
317
 
 
318
        (flags & cull_keep_interesting && 
 
319
         (itr->second->failed_counter() != 0 || itr->second->is_blocked()))) {
 
320
      itr++;
 
321
      continue;
 
322
    }
 
323
 
 
324
    // The key is a pointer to a member in the value, although the key
 
325
    // shouldn't actually be used in erase (I think), just ot be safe
 
326
    // we delete it after erase.
 
327
    iterator tmp = itr++;
 
328
    PeerInfo* peerInfo = tmp->second;
 
329
 
 
330
    base_type::erase(tmp);
 
331
    delete peerInfo;
 
332
 
 
333
    counter++;
 
334
  }
 
335
 
 
336
  return counter;
 
337
}
 
338
 
 
339
}